-
-
Notifications
You must be signed in to change notification settings - Fork 489
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Brotli compression support #432
base: master
Are you sure you want to change the base?
feat: Brotli compression support #432
Conversation
e9d87bf
to
32dc3e7
Compare
Thanks for taking a stab at this! I'm checking out the PR description and the API changes now, and are not focusing on the internal code changes yet as I write this comment, so consider this comment a high-level review I'm a bit concerned how this API separetes the I can see that we could then later, after this pull request, consider opening up the API more and allow some sort of a For now, to ease reviewing, I'd favor a less open API change where we could focus only on choosing between This "brotli" or "gzip" choice could very well be implemented only at the CLI and Plugin options levels — the internal wiring could keep on using the What do you think? Am I making any sense? 😅 Oh, and as you can see, the tests have become unfortunately quite flaky indeed. I haven't had the time to look into why the tests are timing out or getting aborted. Any help in getting the GitHub actions to run properly would be very much appreciated and would also help reviewing this PR once we're happy with the way the feature has been designed 🙏 We haven't really done anything about the CI since #402 where the checks were running successfully. So something has likely changed since then, and it likely isn't anything we've written as there isn't quite much git history since removing Travis CI: https://github.com/webpack-contrib/webpack-bundle-analyzer/commits/master |
|
The CI is now hopefully fixed, thanks to #435. Feel free to merge or rebase newest changes in to this PR to hopefully get a green CI |
To be in line with the new `compressedSize*` options. Supports `gzip` as a synonym for backwards compatibility. Internal naming is also still `gzip*`.
32dc3e7
to
85a6376
Compare
85a6376
to
70d8cf1
Compare
@valscion ready for the next review! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the direction of this PR 👍. It was a good idea to get rid of the gzip-size
package and rely on the native zlib
module also for the gzip case — it makes the code paths for brotli and gzip look a bit more similar, and all the previous usages of gzip-size
were easy to find 👍
test/plugin.js
Outdated
await webpackCompile(config, '4.44.2'); | ||
await expectValidReport({ | ||
parsedSize: 1311, | ||
gzipSize: 302 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that, even though it increases the size of this PR, it might be better to aim to use brotliSize
in the data when Brotli is used instead of gzipSize
. It would be quite confusing for direct users of the report JSON file (e.g. those who supply --analyzerMode json
value to CLI) if the key would remain as gzipSize
even though Brotli was selected.
What do you think? Does the confusion risk outweigh changing the code more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just compressedSize
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@th0r I thought about this too, as I'm not a friend of key explosion.
On the other hand I'm OK with gzipSize
, brotliSize
:
- backwards compatible
- unambiguous for (human/bot) readers of the raw data
- theoretically allows for dual-compression stats
src/viewer.js
Outdated
@@ -126,21 +136,23 @@ async function generateReport(bundleStats, opts) { | |||
openBrowser = true, | |||
reportFilename, | |||
reportTitle, | |||
compressionAlgorithm = 'gzip', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah, I see you've found that we have multiple places where we're defining default values for options, and haven't really made up our mind where they'd belong 😅
How do you feel about moving all the compressionAlgorithm = 'gzip
default value definitions to the top-level usages? That is, moving the compressionAlgorithm = 'gzip'
code to the plugin starting point in src/BundleAnalyzerPlugin.js
. The CLI starting point in src/bin/analyzer.js
already seems to define the default value for the --compression-algorithm
to be gzip
so we should be covered there.
Then we could get rid of all the compressionAlgorithm = 'gzip'
defaulting in other code paths and assume that we have the value filled in in those code paths.
README.md
Outdated
### `gzip` | ||
### `compressed` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make it so that the added option for defaultSizes
would be brotli
instead of compressed
? That way the current, default gzip usage would not have to be changed.
That way we could keep the gzip-related documentation intact and only worry about adding new documentation related to the brotli options.
I could imagine that the fourth size definition documentation here could look something like this:
brotli
This is the size of running the parsed bundles/modules through Brotli compression. Note: This needs the compression algorithm to be configured to use Brotli.
client/components/ModulesTreemap.jsx
Outdated
return [ | ||
{label: 'Stat', prop: 'statSize'}, | ||
{label: 'Parsed', prop: 'parsedSize'}, | ||
{label: window.compressedSizeLabel, prop: 'gzipSize'} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we change gzipSize
to compressedSize
?
src/analyzer.js
Outdated
@@ -102,7 +111,7 @@ function getViewerData(bundleStats, bundleDir, opts) { | |||
|
|||
if (assetSources) { | |||
asset.parsedSize = Buffer.byteLength(assetSources.src); | |||
asset.gzipSize = gzipSize.sync(assetSources.src); | |||
asset.gzipSize = compressedSize(assetSources.src); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
asset.gzipSize = compressedSize(assetSources.src); | |
asset.compressedSize = compressedSize(assetSources.src); |
src/analyzer.js
Outdated
} = opts || {}; | ||
|
||
const isAssetIncluded = createAssetsFilter(excludeAssets); | ||
|
||
const compressedSize = COMPRESSED_SIZE[compressionAlgorithm]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's better call it getCompressedSize
please
src/analyzer.js
Outdated
@@ -143,7 +152,7 @@ function getViewerData(bundleStats, bundleDir, opts) { | |||
} | |||
|
|||
asset.modules = assetModules; | |||
asset.tree = createModulesTree(asset.modules); | |||
asset.tree = createModulesTree(asset.modules, {compressedSize}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
asset.tree = createModulesTree(asset.modules, {compressedSize}); | |
asset.tree = createModulesTree(asset.modules, {getCompressedSize}); |
src/viewer.js
Outdated
@@ -22,6 +22,14 @@ function resolveTitle(reportTitle) { | |||
} | |||
} | |||
|
|||
function resolveDefaultSizes(defaultSizes) { | |||
return defaultSizes === 'compressed' ? 'gzip' : defaultSizes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we inverse it please? I mean, deprecate gzip
default size and use compressed
instead so it will look like this:
return defaultSizes === 'compressed' ? 'gzip' : defaultSizes; | |
return defaultSizes === 'gzip' ? 'compressed' : defaultSizes; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My current approach, based on #432 (comment), is:
function resolveDefaultSizes(defaultSizes, compressionAlgorithm) {
if (['gzip', 'brotli'].includes(defaultSizes)) return compressionAlgorithm;
return defaultSizes;
}
That is, we drop compressed
. The config is "forgiving" if the user specifies the wrong defaultSizes
.
WDYT?
but be forgiving with a gzip/brotli mismatch
@@ -61,7 +61,8 @@ new BundleAnalyzerPlugin(options?: object) | |||
|**`analyzerPort`**|`{Number}` or `auto`|Default: `8888`. Port that will be used in `server` mode to start HTTP server.| | |||
|**`reportFilename`**|`{String}`|Default: `report.html`. Path to bundle report file that will be generated in `static` mode. It can be either an absolute path or a path relative to a bundle output directory (which is output.path in webpack config).| | |||
|**`reportTitle`**|`{String\|function}`|Default: function that returns pretty printed current date and time. Content of the HTML `title` element; or a function of the form `() => string` that provides the content.| | |||
|**`defaultSizes`**|One of: `stat`, `parsed`, `gzip`|Default: `parsed`. Module sizes to show in report by default. [Size definitions](#size-definitions) section describes what these values mean.| | |||
|**`defaultSizes`**|One of: `stat`, `parsed`, `gzip`, `brotli`|Default: `parsed`. Module sizes to show in report by default. [Size definitions](#size-definitions) section describes what these values mean.| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case user will have to provide compression algorithm twice: for compressionAlgorithm
and defaultSizes
and it opens possibilities for invalid option combinations like defaultSizes: 'gzip', compressionAlgorithm: 'brotli'
which don't make sense. I personally would change defaultSizes: 'gzip'
to defaultSizes: 'compressed'
and deprecate the former, but support it (convert to compressed
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation handles the mismatch gracefully, so it's more of a cosmetic issue on the user side.
Reintroducing compressed
would help here. If used, the config always looks "good", even when switching the algorithm. If we support compressed
, fail on mismatches between compressionAlgorithm
and defaultSizes
?
{label: 'Parsed', prop: 'parsedSize'} | ||
]; | ||
|
||
if (window.compressionAlgorithm === 'gzip') items.push({label: 'Gzipped', prop: 'gzipSize'}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would change gzipSize/brotliSize
to compressedSize
but looks like it will be a breaking change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice if we could first do this as a new feature, release in a minor version and then do a major release later to consolidate the API on on plain compressedSize
. That has been my opinion during this PR review at least
What do you think of this plan?
I'll let you @th0r review this |
Hi, Is there any update on this functionality? Thanks for your work! |
Amazing feature not going to production just by personal opinions on how to name it brotli or general word "compression". |
Not sure, maybe a misunderstanding about who's turn it is? @th0r could you clarify the to-dos here? |
@valscion in #432 (comment) you handed the review to @th0r. |
Yeah looks like I can take over reviewing this. Now that brotli is used more and more, having this show in Let's first get this PR up-to-date and then I can start reviewing again. I've lost context of this PR so I'll basically have to start the review process from scratch as well |
Fantastic to see this PR still is active. I would love to see this in production. Keep up the great work! |
This is my go at #113
Instead of adding Brotli support, I added low-level options to replace Gzip support. The overall approach is inspired by me trying to avoid the hassle of conditional code based on the Node.js version, my own requirements (no need for high-level options), and #113 (comment):Updated with high-level option
compressionAlgorithm: gzip | brotli
Example usage:
Regarding the requirements...
...stated in #113 (comment)
For me, the POC was trying out https://www.npmjs.com/package/webpack-bundle-analyzer-brotli, which works great, but is outdated.
The new option is opt-in. Either gzip or Brotli. Users who want Brotli support will be willing to accept the longer wait.
The user must specify the compression as an option. They know their environment (e.g. the Node.js version) and resources best.
I tried to keep the changes at a minimum.
In this PR I even left most of the internals (including a lot of
gzip*
naming) untouched. A cleanup of the internal naming could be done independently, but I'm not sure if this is required.