From 4988357ae4c4267f5a154197839e473479d248c2 Mon Sep 17 00:00:00 2001 From: Vijay Jain Date: Fri, 28 Aug 2020 16:44:13 +0530 Subject: [PATCH 1/5] Add support to analyze gzipped bundles --- src/BundleAnalyzerPlugin.js | 10 +++++-- src/analyzer.js | 11 ++++++-- src/parseUtils.js | 13 +++++++-- src/viewer.js | 11 +++++--- .../validBundleWithArrowFunction.js.br | Bin 0 -> 70 bytes .../validBundleWithArrowFunction.js.gz | Bin 0 -> 115 bytes test/parseUtils.js | 26 ++++++++++++++++-- 7 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 test/bundles/validBundleWithArrowFunction.js.br create mode 100644 test/bundles/validBundleWithArrowFunction.js.gz diff --git a/src/BundleAnalyzerPlugin.js b/src/BundleAnalyzerPlugin.js index 6d294730..97478758 100644 --- a/src/BundleAnalyzerPlugin.js +++ b/src/BundleAnalyzerPlugin.js @@ -21,6 +21,7 @@ class BundleAnalyzerPlugin { statsOptions: null, excludeAssets: null, logLevel: 'info', + decompressExtenstion: {}, // deprecated startAnalyzer: true, ...opts, @@ -114,7 +115,8 @@ class BundleAnalyzerPlugin { bundleDir: this.getBundleDirFromCompiler(), logger: this.logger, defaultSizes: this.opts.defaultSizes, - excludeAssets: this.opts.excludeAssets + excludeAssets: this.opts.excludeAssets, + decompressExtenstion: this.opts.decompressExtenstion }); } } @@ -124,7 +126,8 @@ class BundleAnalyzerPlugin { reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.json'), bundleDir: this.getBundleDirFromCompiler(), logger: this.logger, - excludeAssets: this.opts.excludeAssets + excludeAssets: this.opts.excludeAssets, + decompressExtenstion: this.opts.decompressExtenstion }); } @@ -136,7 +139,8 @@ class BundleAnalyzerPlugin { bundleDir: this.getBundleDirFromCompiler(), logger: this.logger, defaultSizes: this.opts.defaultSizes, - excludeAssets: this.opts.excludeAssets + excludeAssets: this.opts.excludeAssets, + decompressExtenstion: this.opts.decompressExtenstion }); } diff --git a/src/analyzer.js b/src/analyzer.js index 0a93b34d..d21cc42d 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -17,6 +17,13 @@ module.exports = { readStatsFromFile }; +function isExtensionAllowed(filename, {decompressExtenstion = {}}) { + if (FILENAME_EXTENSIONS.test(filename)) { + return true; + } + return !!decompressExtenstion[filename.split('.').pop()]; +} + function getViewerData(bundleStats, bundleDir, opts) { const { logger = new Logger(), @@ -53,7 +60,7 @@ function getViewerData(bundleStats, bundleDir, opts) { // See #22 asset.name = asset.name.replace(FILENAME_QUERY_REGEXP, ''); - return FILENAME_EXTENSIONS.test(asset.name) && !_.isEmpty(asset.chunks) && isAssetIncluded(asset.name); + return isExtensionAllowed(asset.name, opts) && !_.isEmpty(asset.chunks) && isAssetIncluded(asset.name); }); // Trying to parse bundle assets and get real module sizes if `bundleDir` is provided @@ -69,7 +76,7 @@ function getViewerData(bundleStats, bundleDir, opts) { let bundleInfo; try { - bundleInfo = parseBundle(assetFile); + bundleInfo = parseBundle(assetFile, opts); } catch (err) { const msg = (err.code === 'ENOENT') ? 'no such file' : err.message; logger.warn(`Error parsing bundle asset "${assetFile}": ${msg}`); diff --git a/src/parseUtils.js b/src/parseUtils.js index f5989105..6d16e1aa 100644 --- a/src/parseUtils.js +++ b/src/parseUtils.js @@ -2,13 +2,22 @@ const fs = require('fs'); const _ = require('lodash'); const acorn = require('acorn'); const walk = require('acorn-walk'); +const zlib = require('zlib'); module.exports = { parseBundle }; -function parseBundle(bundlePath) { - const content = fs.readFileSync(bundlePath, 'utf8'); +function parseBundle(bundlePath, {decompressExtenstion = {}}) { + let content; + const decompressAlgorithm = (decompressExtenstion[bundlePath.split('.').pop()] || {}).algorithm; + if (decompressAlgorithm && zlib[decompressAlgorithm]) { + const compressedBuffer = fs.readFileSync(bundlePath); + const decompressedBuffer = zlib[decompressAlgorithm](compressedBuffer); + content = decompressedBuffer.toString(); + } else { + content = fs.readFileSync(bundlePath, 'utf8'); + } const ast = acorn.parse(content, { sourceType: 'script', // I believe in a bright future of ECMAScript! diff --git a/src/viewer.js b/src/viewer.js index 4d4d0d27..3de0e12c 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -41,10 +41,11 @@ async function startServer(bundleStats, opts) { logger = new Logger(), defaultSizes = 'parsed', excludeAssets = null, - reportTitle + reportTitle, + decompressExtenstion } = opts || {}; - const analyzerOpts = {logger, excludeAssets}; + const analyzerOpts = {logger, excludeAssets, decompressExtenstion}; let chartData = getChartData(analyzerOpts, bundleStats, bundleDir); @@ -181,9 +182,11 @@ async function generateReport(bundleStats, opts) { } async function generateJSONReport(bundleStats, opts) { - const {reportFilename, bundleDir = null, logger = new Logger(), excludeAssets = null} = opts || {}; + const { + reportFilename, bundleDir = null, logger = new Logger(), excludeAssets = null, decompressExtenstion + } = opts || {}; - const chartData = getChartData({logger, excludeAssets}, bundleStats, bundleDir); + const chartData = getChartData({logger, excludeAssets, decompressExtenstion}, bundleStats, bundleDir); if (!chartData) return; diff --git a/test/bundles/validBundleWithArrowFunction.js.br b/test/bundles/validBundleWithArrowFunction.js.br new file mode 100644 index 0000000000000000000000000000000000000000..524587b62519be2a037cba866ec4fdf6d3304a8f GIT binary patch literal 70 zcmY#nU}PyzO)5xC&h{$K&nwV~Hi*@U)+o_Q)hW`nwX5b*P)N?tE6&eJ)yv6G*HH3E X&B@7ED9=+cj literal 0 HcmV?d00001 diff --git a/test/bundles/validBundleWithArrowFunction.js.gz b/test/bundles/validBundleWithArrowFunction.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..06c76d287891a143d91c3804cf81a2e8f3cf28cc GIT binary patch literal 115 zcmV-(0F3`1iwFq&i$`Ao19o9-X=FlmZe(m_S7~%;L2`0$cSdz?V{~b6ZZ2wb04q;T zDo9Mu_A1WLE6|8Gh}DVKDA7sPDblpHtL9QrNY2kI&d*8J%gImIQ1VF4$;nqJ&o9bJ VQB=~j=BkYa5&)!4`X@mE001?JG%o-E literal 0 HcmV?d00001 diff --git a/test/parseUtils.js b/test/parseUtils.js index ec08aa46..5fc14291 100644 --- a/test/parseUtils.js +++ b/test/parseUtils.js @@ -4,6 +4,10 @@ const _ = require('lodash'); const {parseBundle} = require('../lib/parseUtils'); const BUNDLES_DIR = `${__dirname}/bundles`; +const COMPRESSIONS = { + brotli: {extension: 'br', algorithm: 'brotliDecompressSync'}, + gzip: {extension: 'gz', algorithm: 'unzipSync'} +}; describe('parseBundle', function () { const bundles = fs @@ -16,7 +20,7 @@ describe('parseBundle', function () { .forEach(bundleName => { it(`should parse ${_.lowerCase(bundleName)}`, function () { const bundleFile = `${BUNDLES_DIR}/${bundleName}.js`; - const bundle = parseBundle(bundleFile); + const bundle = parseBundle(bundleFile, {}); const expectedModules = JSON.parse(fs.readFileSync(`${BUNDLES_DIR}/${bundleName}.modules.json`)); expect(bundle.src).to.equal(fs.readFileSync(bundleFile, 'utf8')); @@ -26,8 +30,26 @@ describe('parseBundle', function () { it("should parse invalid bundle and return it's content and empty modules hash", function () { const bundleFile = `${BUNDLES_DIR}/invalidBundle.js`; - const bundle = parseBundle(bundleFile); + const bundle = parseBundle(bundleFile, {}); expect(bundle.src).to.equal(fs.readFileSync(bundleFile, 'utf8')); expect(bundle.modules).to.deep.equal({}); }); + + Object.keys(COMPRESSIONS) + .forEach(compressionType => { + it(`should parse compressed ${compressionType} bundle`, function () { + const {extension, algorithm} = COMPRESSIONS[compressionType]; + const bundleFile = `${BUNDLES_DIR}/validBundleWithArrowFunction.js`; + const compressedBundleFile = `${bundleFile}.${extension}`; + const expectedModules = JSON.parse(fs.readFileSync(`${BUNDLES_DIR}/validBundleWithArrowFunction.modules.json`)); + const bundle = parseBundle(compressedBundleFile, { + decompressExtenstion: { + [extension]: {algorithm} + } + }); + expect(bundle.src).to.equal(fs.readFileSync(bundleFile, 'utf8')); + expect(bundle.modules).to.deep.equal(expectedModules.modules); + }); + }); + }); From 881a1ec5bdcf01b072585d24f66a553726aa5f8a Mon Sep 17 00:00:00 2001 From: Vijay Jain Date: Mon, 31 Aug 2020 20:41:06 +0530 Subject: [PATCH 2/5] Fix tests and update readme --- README.md | 1 + src/analyzer.js | 5 +++-- src/parseUtils.js | 15 ++++++++++----- test/parseUtils.js | 4 ++++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 43b8e677..14d2d24f 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ new BundleAnalyzerPlugin(options?: object) |**`statsOptions`**|`null` or `{Object}`|Default: `null`. Options for `stats.toJson()` method. For example you can exclude sources of your modules from stats file with `source: false` option. [See more options here](https://webpack.js.org/configuration/stats/). | |**`excludeAssets`**|`{null\|pattern\|pattern[]}` where `pattern` equals to `{String\|RegExp\|function}`|Default: `null`. Patterns that will be used to match against asset names to exclude them from the report. If pattern is a string it will be converted to RegExp via `new RegExp(str)`. If pattern is a function it should have the following signature `(assetName: string) => boolean` and should return `true` to *exclude* matching asset. If multiple patterns are provided asset should match at least one of them to be excluded. | |**`logLevel`**|One of: `info`, `warn`, `error`, `silent`|Default: `info`. Used to control how much details the plugin outputs.| +|**`decompressExtenstion`**|`null` or `{Object}`|Default: `null`. Used to show stats for a compressed file, object has compressed file's extension as key and it's value is decompression algorithm of [zlib](https://nodejs.org/api/zlib.html) to use, like: `{ gz: { algorithm: 'unzipSync' } }`|

Usage (as a CLI utility)

diff --git a/src/analyzer.js b/src/analyzer.js index d21cc42d..0c6bad86 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -27,7 +27,8 @@ function isExtensionAllowed(filename, {decompressExtenstion = {}}) { function getViewerData(bundleStats, bundleDir, opts) { const { logger = new Logger(), - excludeAssets = null + excludeAssets = null, + decompressExtenstion } = opts || {}; const isAssetIncluded = createAssetsFilter(excludeAssets); @@ -76,7 +77,7 @@ function getViewerData(bundleStats, bundleDir, opts) { let bundleInfo; try { - bundleInfo = parseBundle(assetFile, opts); + bundleInfo = parseBundle(assetFile, {decompressExtenstion, logger}); } catch (err) { const msg = (err.code === 'ENOENT') ? 'no such file' : err.message; logger.warn(`Error parsing bundle asset "${assetFile}": ${msg}`); diff --git a/src/parseUtils.js b/src/parseUtils.js index 6d16e1aa..4dda2eaa 100644 --- a/src/parseUtils.js +++ b/src/parseUtils.js @@ -3,18 +3,23 @@ const _ = require('lodash'); const acorn = require('acorn'); const walk = require('acorn-walk'); const zlib = require('zlib'); +const Logger = require('./Logger'); module.exports = { parseBundle }; -function parseBundle(bundlePath, {decompressExtenstion = {}}) { +function parseBundle(bundlePath, {decompressExtenstion = {}, logger = new Logger()}) { let content; const decompressAlgorithm = (decompressExtenstion[bundlePath.split('.').pop()] || {}).algorithm; - if (decompressAlgorithm && zlib[decompressAlgorithm]) { - const compressedBuffer = fs.readFileSync(bundlePath); - const decompressedBuffer = zlib[decompressAlgorithm](compressedBuffer); - content = decompressedBuffer.toString(); + if (decompressAlgorithm) { + if (zlib[decompressAlgorithm]) { + const compressedBuffer = fs.readFileSync(bundlePath); + const decompressedBuffer = zlib[decompressAlgorithm](compressedBuffer); + content = decompressedBuffer.toString(); + } else { + logger.error(`Algorithm "${decompressAlgorithm}" not available in zlib, consider upgrading node version`); + } } else { content = fs.readFileSync(bundlePath, 'utf8'); } diff --git a/test/parseUtils.js b/test/parseUtils.js index 5fc14291..746509ae 100644 --- a/test/parseUtils.js +++ b/test/parseUtils.js @@ -1,4 +1,5 @@ const fs = require('fs'); +const zlib = require('zlib'); const _ = require('lodash'); const {parseBundle} = require('../lib/parseUtils'); @@ -42,6 +43,9 @@ describe('parseBundle', function () { const bundleFile = `${BUNDLES_DIR}/validBundleWithArrowFunction.js`; const compressedBundleFile = `${bundleFile}.${extension}`; const expectedModules = JSON.parse(fs.readFileSync(`${BUNDLES_DIR}/validBundleWithArrowFunction.modules.json`)); + if (!zlib[algorithm]) { + return; + } const bundle = parseBundle(compressedBundleFile, { decompressExtenstion: { [extension]: {algorithm} From f2ad35adf1bb8a9b4831c4c20a794eefda40c4a6 Mon Sep 17 00:00:00 2001 From: Vijay Jain Date: Fri, 4 Sep 2020 14:51:17 +0530 Subject: [PATCH 3/5] Review comments icorporated --- README.md | 4 +++- src/BundleAnalyzerPlugin.js | 10 +++------- src/analyzer.js | 18 +++++------------- src/parseUtils.js | 31 +++++++++++++++++++++---------- src/viewer.js | 9 ++++----- test/.eslintrc.json | 3 ++- test/helpers.js | 6 ++++++ test/parseUtils.js | 27 ++++++++++++--------------- 8 files changed, 56 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 14d2d24f..dbd0ce7d 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,6 @@ new BundleAnalyzerPlugin(options?: object) |**`statsOptions`**|`null` or `{Object}`|Default: `null`. Options for `stats.toJson()` method. For example you can exclude sources of your modules from stats file with `source: false` option. [See more options here](https://webpack.js.org/configuration/stats/). | |**`excludeAssets`**|`{null\|pattern\|pattern[]}` where `pattern` equals to `{String\|RegExp\|function}`|Default: `null`. Patterns that will be used to match against asset names to exclude them from the report. If pattern is a string it will be converted to RegExp via `new RegExp(str)`. If pattern is a function it should have the following signature `(assetName: string) => boolean` and should return `true` to *exclude* matching asset. If multiple patterns are provided asset should match at least one of them to be excluded. | |**`logLevel`**|One of: `info`, `warn`, `error`, `silent`|Default: `info`. Used to control how much details the plugin outputs.| -|**`decompressExtenstion`**|`null` or `{Object}`|Default: `null`. Used to show stats for a compressed file, object has compressed file's extension as key and it's value is decompression algorithm of [zlib](https://nodejs.org/api/zlib.html) to use, like: `{ gz: { algorithm: 'unzipSync' } }`|

Usage (as a CLI utility)

@@ -179,6 +178,9 @@ Analyzer will use module sizes from stats file. ``` To get more information about it you can read [issue #147](https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/147). +### I don't see any stats for compressed files + +`webpack-bundle-analyzer` automatically detects files compressed with gzip(.gz) or brotli(.br), and decompresses them to generate stats. If using `compression-webpack-plugin` with `deleteOriginalAssets: true`, make sure to provide same name to output and compressed files so that correct chunk can be referred, see [issue #377](https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/377#issuecomment-682347389). For brotli assets, make sure your node version is `>=11.7.0`.

Maintainers

diff --git a/src/BundleAnalyzerPlugin.js b/src/BundleAnalyzerPlugin.js index 97478758..6d294730 100644 --- a/src/BundleAnalyzerPlugin.js +++ b/src/BundleAnalyzerPlugin.js @@ -21,7 +21,6 @@ class BundleAnalyzerPlugin { statsOptions: null, excludeAssets: null, logLevel: 'info', - decompressExtenstion: {}, // deprecated startAnalyzer: true, ...opts, @@ -115,8 +114,7 @@ class BundleAnalyzerPlugin { bundleDir: this.getBundleDirFromCompiler(), logger: this.logger, defaultSizes: this.opts.defaultSizes, - excludeAssets: this.opts.excludeAssets, - decompressExtenstion: this.opts.decompressExtenstion + excludeAssets: this.opts.excludeAssets }); } } @@ -126,8 +124,7 @@ class BundleAnalyzerPlugin { reportFilename: path.resolve(this.compiler.outputPath, this.opts.reportFilename || 'report.json'), bundleDir: this.getBundleDirFromCompiler(), logger: this.logger, - excludeAssets: this.opts.excludeAssets, - decompressExtenstion: this.opts.decompressExtenstion + excludeAssets: this.opts.excludeAssets }); } @@ -139,8 +136,7 @@ class BundleAnalyzerPlugin { bundleDir: this.getBundleDirFromCompiler(), logger: this.logger, defaultSizes: this.opts.defaultSizes, - excludeAssets: this.opts.excludeAssets, - decompressExtenstion: this.opts.decompressExtenstion + excludeAssets: this.opts.excludeAssets }); } diff --git a/src/analyzer.js b/src/analyzer.js index 0c6bad86..ac63b0db 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -10,25 +10,17 @@ const {parseBundle} = require('./parseUtils'); const {createAssetsFilter} = require('./utils'); const FILENAME_QUERY_REGEXP = /\?.*$/u; -const FILENAME_EXTENSIONS = /\.(js|mjs)$/iu; +const FILENAME_EXTENSIONS = /\.(js|mjs|gz|br)$/iu; module.exports = { getViewerData, readStatsFromFile }; -function isExtensionAllowed(filename, {decompressExtenstion = {}}) { - if (FILENAME_EXTENSIONS.test(filename)) { - return true; - } - return !!decompressExtenstion[filename.split('.').pop()]; -} - function getViewerData(bundleStats, bundleDir, opts) { const { logger = new Logger(), - excludeAssets = null, - decompressExtenstion + excludeAssets = null } = opts || {}; const isAssetIncluded = createAssetsFilter(excludeAssets); @@ -55,13 +47,13 @@ function getViewerData(bundleStats, bundleDir, opts) { }); } - // Picking only `*.js or *.mjs` assets from bundle that has non-empty `chunks` array + // Picking only `*.js or *.mjs or *.gz or *.br` assets from bundle that has non-empty `chunks` array bundleStats.assets = _.filter(bundleStats.assets, asset => { // Removing query part from filename (yes, somebody uses it for some reason and Webpack supports it) // See #22 asset.name = asset.name.replace(FILENAME_QUERY_REGEXP, ''); - return isExtensionAllowed(asset.name, opts) && !_.isEmpty(asset.chunks) && isAssetIncluded(asset.name); + return FILENAME_EXTENSIONS.test(asset.name) && !_.isEmpty(asset.chunks) && isAssetIncluded(asset.name); }); // Trying to parse bundle assets and get real module sizes if `bundleDir` is provided @@ -77,7 +69,7 @@ function getViewerData(bundleStats, bundleDir, opts) { let bundleInfo; try { - bundleInfo = parseBundle(assetFile, {decompressExtenstion, logger}); + bundleInfo = parseBundle(assetFile, {logger}); } catch (err) { const msg = (err.code === 'ENOENT') ? 'no such file' : err.message; logger.warn(`Error parsing bundle asset "${assetFile}": ${msg}`); diff --git a/src/parseUtils.js b/src/parseUtils.js index 4dda2eaa..1a34d133 100644 --- a/src/parseUtils.js +++ b/src/parseUtils.js @@ -9,17 +9,28 @@ module.exports = { parseBundle }; -function parseBundle(bundlePath, {decompressExtenstion = {}, logger = new Logger()}) { +const COMPRESSED_EXTENSIONS = /\.(gz|br)$/iu; +const DECOMPRESSION_ALGORITHMS = { + gz: 'unzipSync', + br: 'brotliDecompressSync' +}; + +function decompressBundle(bundlePath, {logger = new Logger()}) { + const decompressAlgorithm = DECOMPRESSION_ALGORITHMS[bundlePath.split('.').pop()]; + if (zlib[decompressAlgorithm]) { + const compressedBuffer = fs.readFileSync(bundlePath); + const decompressedBuffer = zlib[decompressAlgorithm](compressedBuffer); + return decompressedBuffer.toString(); + } else { + logger.warn(`Bundle "${bundlePath}" could be compressed, consider upgrading node version`); + return ''; + } +} + +function parseBundle(bundlePath, opts = {}) { let content; - const decompressAlgorithm = (decompressExtenstion[bundlePath.split('.').pop()] || {}).algorithm; - if (decompressAlgorithm) { - if (zlib[decompressAlgorithm]) { - const compressedBuffer = fs.readFileSync(bundlePath); - const decompressedBuffer = zlib[decompressAlgorithm](compressedBuffer); - content = decompressedBuffer.toString(); - } else { - logger.error(`Algorithm "${decompressAlgorithm}" not available in zlib, consider upgrading node version`); - } + if (COMPRESSED_EXTENSIONS.test(bundlePath)) { + content = decompressBundle(bundlePath, opts); } else { content = fs.readFileSync(bundlePath, 'utf8'); } diff --git a/src/viewer.js b/src/viewer.js index 3de0e12c..72e9a79e 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -41,11 +41,10 @@ async function startServer(bundleStats, opts) { logger = new Logger(), defaultSizes = 'parsed', excludeAssets = null, - reportTitle, - decompressExtenstion + reportTitle } = opts || {}; - const analyzerOpts = {logger, excludeAssets, decompressExtenstion}; + const analyzerOpts = {logger, excludeAssets}; let chartData = getChartData(analyzerOpts, bundleStats, bundleDir); @@ -183,10 +182,10 @@ async function generateReport(bundleStats, opts) { async function generateJSONReport(bundleStats, opts) { const { - reportFilename, bundleDir = null, logger = new Logger(), excludeAssets = null, decompressExtenstion + reportFilename, bundleDir = null, logger = new Logger(), excludeAssets = null } = opts || {}; - const chartData = getChartData({logger, excludeAssets, decompressExtenstion}, bundleStats, bundleDir); + const chartData = getChartData({logger, excludeAssets}, bundleStats, bundleDir); if (!chartData) return; diff --git a/test/.eslintrc.json b/test/.eslintrc.json index b2b1cbb0..dd51f4db 100644 --- a/test/.eslintrc.json +++ b/test/.eslintrc.json @@ -7,6 +7,7 @@ "globals": { "expect": true, "makeWebpackConfig": true, - "webpackCompile": true + "webpackCompile": true, + "hasNodeVersion": true } } diff --git a/test/helpers.js b/test/helpers.js index c8a9258e..4f972d58 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -7,6 +7,7 @@ chai.use(require('chai-subset')); global.expect = chai.expect; global.webpackCompile = webpackCompile; global.makeWebpackConfig = makeWebpackConfig; +global.hasNodeVersion = hasNodeVersion; const BundleAnalyzerPlugin = require('../lib/BundleAnalyzerPlugin'); @@ -80,3 +81,8 @@ function makeWebpackConfig(opts) { function wait(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } + +function hasNodeVersion(version) { + const currentVersion = process.version.split('v')[1].split('.'); + return version.split('.').every((v, i) => Number(v) <= Number(currentVersion[i])); +} diff --git a/test/parseUtils.js b/test/parseUtils.js index 746509ae..bfe61e93 100644 --- a/test/parseUtils.js +++ b/test/parseUtils.js @@ -1,13 +1,12 @@ const fs = require('fs'); -const zlib = require('zlib'); const _ = require('lodash'); const {parseBundle} = require('../lib/parseUtils'); const BUNDLES_DIR = `${__dirname}/bundles`; const COMPRESSIONS = { - brotli: {extension: 'br', algorithm: 'brotliDecompressSync'}, - gzip: {extension: 'gz', algorithm: 'unzipSync'} + brotli: {extension: 'br', minVersion: '11.7.0'}, + gzip: {extension: 'gz'} }; describe('parseBundle', function () { @@ -21,7 +20,7 @@ describe('parseBundle', function () { .forEach(bundleName => { it(`should parse ${_.lowerCase(bundleName)}`, function () { const bundleFile = `${BUNDLES_DIR}/${bundleName}.js`; - const bundle = parseBundle(bundleFile, {}); + const bundle = parseBundle(bundleFile); const expectedModules = JSON.parse(fs.readFileSync(`${BUNDLES_DIR}/${bundleName}.modules.json`)); expect(bundle.src).to.equal(fs.readFileSync(bundleFile, 'utf8')); @@ -31,7 +30,7 @@ describe('parseBundle', function () { it("should parse invalid bundle and return it's content and empty modules hash", function () { const bundleFile = `${BUNDLES_DIR}/invalidBundle.js`; - const bundle = parseBundle(bundleFile, {}); + const bundle = parseBundle(bundleFile); expect(bundle.src).to.equal(fs.readFileSync(bundleFile, 'utf8')); expect(bundle.modules).to.deep.equal({}); }); @@ -39,20 +38,18 @@ describe('parseBundle', function () { Object.keys(COMPRESSIONS) .forEach(compressionType => { it(`should parse compressed ${compressionType} bundle`, function () { - const {extension, algorithm} = COMPRESSIONS[compressionType]; + const {extension, minVersion} = COMPRESSIONS[compressionType]; const bundleFile = `${BUNDLES_DIR}/validBundleWithArrowFunction.js`; const compressedBundleFile = `${bundleFile}.${extension}`; const expectedModules = JSON.parse(fs.readFileSync(`${BUNDLES_DIR}/validBundleWithArrowFunction.modules.json`)); - if (!zlib[algorithm]) { - return; + const bundle = parseBundle(compressedBundleFile); + if (minVersion && !hasNodeVersion(minVersion)) { + expect(bundle.src).to.not.equal(fs.readFileSync(bundleFile, 'utf8')); + expect(bundle.modules).to.deep.not.equal(expectedModules.modules); + } else { + expect(bundle.src).to.equal(fs.readFileSync(bundleFile, 'utf8')); + expect(bundle.modules).to.deep.equal(expectedModules.modules); } - const bundle = parseBundle(compressedBundleFile, { - decompressExtenstion: { - [extension]: {algorithm} - } - }); - expect(bundle.src).to.equal(fs.readFileSync(bundleFile, 'utf8')); - expect(bundle.modules).to.deep.equal(expectedModules.modules); }); }); From 9359ec1b811db1f6c2e3ef6699d0c34a2bee7dc9 Mon Sep 17 00:00:00 2001 From: Vijay Jain Date: Fri, 4 Sep 2020 15:10:47 +0530 Subject: [PATCH 4/5] Update node supported version to 10.22.0(10 lts) --- README.md | 2 +- test/parseUtils.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dbd0ce7d..85dd2c01 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ To get more information about it you can read [issue #147](https://github.com/we ### I don't see any stats for compressed files -`webpack-bundle-analyzer` automatically detects files compressed with gzip(.gz) or brotli(.br), and decompresses them to generate stats. If using `compression-webpack-plugin` with `deleteOriginalAssets: true`, make sure to provide same name to output and compressed files so that correct chunk can be referred, see [issue #377](https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/377#issuecomment-682347389). For brotli assets, make sure your node version is `>=11.7.0`. +`webpack-bundle-analyzer` automatically detects files compressed with gzip(.gz) or brotli(.br), and decompresses them to generate stats. If using `compression-webpack-plugin` with `deleteOriginalAssets: true`, make sure to provide same name to output and compressed files so that correct chunk can be referred, see [issue #377](https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/377#issuecomment-682347389). For brotli assets, make sure your node version is `>=10.22.0`.

Maintainers

diff --git a/test/parseUtils.js b/test/parseUtils.js index bfe61e93..5bee9759 100644 --- a/test/parseUtils.js +++ b/test/parseUtils.js @@ -5,7 +5,7 @@ const {parseBundle} = require('../lib/parseUtils'); const BUNDLES_DIR = `${__dirname}/bundles`; const COMPRESSIONS = { - brotli: {extension: 'br', minVersion: '11.7.0'}, + brotli: {extension: 'br', minVersion: '10.22.0'}, gzip: {extension: 'gz'} }; From 5e0fbea6038f3ace9b83f31f2a739268c5ad06c0 Mon Sep 17 00:00:00 2001 From: Vijay Jain Date: Fri, 4 Sep 2020 15:28:43 +0530 Subject: [PATCH 5/5] Fix in node detection --- test/helpers.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/helpers.js b/test/helpers.js index 4f972d58..5d79b07f 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -84,5 +84,10 @@ function wait(ms) { function hasNodeVersion(version) { const currentVersion = process.version.split('v')[1].split('.'); - return version.split('.').every((v, i) => Number(v) <= Number(currentVersion[i])); + const versions = version.split('.'); + for (let i = 0; i < versions.length; i++) { + if (Number(currentVersion[i]) > Number(versions[i])) return true; + else if (Number(currentVersion[i]) < Number(versions[i])) return false; + } + return true; }