webpack-bundle-analyzer 5.2.0 → 5.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/viewer.js CHANGED
@@ -1,49 +1,130 @@
1
1
  "use strict";
2
2
 
3
- const path = require('path');
4
- const fs = require('fs');
5
- const http = require('http');
6
- const WebSocket = require('ws');
7
- const sirv = require('sirv');
3
+ const fs = require("node:fs");
4
+ const http = require("node:http");
5
+ const path = require("node:path");
8
6
  const {
9
7
  bold
10
- } = require('picocolors');
11
- const Logger = require('./Logger');
12
- const analyzer = require('./analyzer');
13
- const {
14
- open
15
- } = require('./utils');
8
+ } = require("picocolors");
9
+ const sirv = require("sirv");
10
+ const WebSocket = require("ws");
11
+ const Logger = require("./Logger");
12
+ const analyzer = require("./analyzer");
16
13
  const {
17
14
  renderViewer
18
- } = require('./template');
19
- const projectRoot = path.resolve(__dirname, '..');
15
+ } = require("./template");
16
+ const {
17
+ open
18
+ } = require("./utils");
19
+
20
+ /** @typedef {import("http").Server} Server */
21
+ /** @typedef {import("ws").WebSocketServer} WebSocketServer */
22
+ /** @typedef {import("webpack").StatsCompilation} StatsCompilation */
23
+ /** @typedef {import("./BundleAnalyzerPlugin").Sizes} Sizes */
24
+ /** @typedef {import("./BundleAnalyzerPlugin").CompressionAlgorithm} CompressionAlgorithm */
25
+ /** @typedef {import("./BundleAnalyzerPlugin").ReportTitle} ReportTitle */
26
+ /** @typedef {import("./BundleAnalyzerPlugin").AnalyzerUrl} AnalyzerUrl */
27
+ /** @typedef {import("./BundleAnalyzerPlugin").ExcludeAssets} ExcludeAssets */
28
+ /** @typedef {import("./analyzer").ViewerDataOptions} ViewerDataOptions */
29
+ /** @typedef {import("./analyzer").ChartData} ChartData */
30
+
31
+ const projectRoot = path.resolve(__dirname, "..");
32
+
33
+ /**
34
+ * @param {string | (() => string)} reportTitle report title
35
+ * @returns {string} resolved title
36
+ */
20
37
  function resolveTitle(reportTitle) {
21
- if (typeof reportTitle === 'function') {
38
+ if (typeof reportTitle === "function") {
22
39
  return reportTitle();
23
- } else {
24
- return reportTitle;
25
40
  }
41
+ return reportTitle;
26
42
  }
43
+
44
+ /**
45
+ * @param {Sizes} defaultSizes default sizes
46
+ * @param {CompressionAlgorithm} compressionAlgorithm compression algorithm
47
+ * @returns {Sizes} default sizes
48
+ */
27
49
  function resolveDefaultSizes(defaultSizes, compressionAlgorithm) {
28
- if (['gzip', 'brotli', 'zstd'].includes(defaultSizes)) return compressionAlgorithm;
50
+ if (["gzip", "brotli", "zstd"].includes(defaultSizes)) {
51
+ return compressionAlgorithm;
52
+ }
29
53
  return defaultSizes;
30
54
  }
31
- module.exports = {
32
- startServer,
33
- generateReport,
34
- generateJSONReport,
35
- getEntrypoints,
36
- // deprecated
37
- start: startServer
38
- };
55
+
56
+ /** @typedef {(string | undefined | null)[]} Entrypoints */
57
+
58
+ /**
59
+ * @param {StatsCompilation} bundleStats bundle stats
60
+ * @returns {Entrypoints} entrypoints
61
+ */
62
+ function getEntrypoints(bundleStats) {
63
+ if (bundleStats === null || bundleStats === undefined || !bundleStats.entrypoints) {
64
+ return [];
65
+ }
66
+ return Object.values(bundleStats.entrypoints).map(entrypoint => entrypoint.name);
67
+ }
68
+
69
+ /**
70
+ * @param {ViewerDataOptions} analyzerOpts analyzer options
71
+ * @param {StatsCompilation} bundleStats bundle stats
72
+ * @param {string | null} bundleDir bundle dir
73
+ * @returns {ChartData | null} chart data
74
+ */
75
+ function getChartData(analyzerOpts, bundleStats, bundleDir) {
76
+ /** @type {ChartData | undefined | null} */
77
+ let chartData;
78
+ const {
79
+ logger
80
+ } = analyzerOpts;
81
+ try {
82
+ chartData = analyzer.getViewerData(bundleStats, bundleDir, analyzerOpts);
83
+ } catch (err) {
84
+ logger.error(`Couldn't analyze webpack bundle:\n${err}`);
85
+ logger.debug(/** @type {Error} */err.stack);
86
+ chartData = null;
87
+ }
88
+
89
+ // chartData can either be an array (bundleInfo[]) or null. It can't be an plain object anyway
90
+ if (
91
+ // analyzer.getViewerData() doesn't failed in the previous step
92
+ chartData && !Array.isArray(chartData)) {
93
+ logger.error("Couldn't find any javascript bundles in provided stats file");
94
+ chartData = null;
95
+ }
96
+ return chartData;
97
+ }
98
+
99
+ /**
100
+ * @typedef {object} ServerOptions
101
+ * @property {number} port port
102
+ * @property {string} host host
103
+ * @property {boolean} openBrowser true when need to open browser, otherwise false
104
+ * @property {string | null} bundleDir bundle dir
105
+ * @property {Logger} logger logger
106
+ * @property {Sizes} defaultSizes default sizes
107
+ * @property {CompressionAlgorithm} compressionAlgorithm compression algorithm
108
+ * @property {ExcludeAssets | null} excludeAssets exclude assets
109
+ * @property {ReportTitle} reportTitle report title
110
+ * @property {AnalyzerUrl} analyzerUrl analyzer url
111
+ */
112
+
113
+ /** @typedef {{ ws: WebSocketServer, http: Server, updateChartData: (bundleStats: StatsCompilation) => void }} ViewerServerObj */
114
+
115
+ /**
116
+ * @param {StatsCompilation} bundleStats bundle stats
117
+ * @param {ServerOptions} opts options
118
+ * @returns {Promise<ViewerServerObj>} server
119
+ */
39
120
  async function startServer(bundleStats, opts) {
40
121
  const {
41
122
  port = 8888,
42
- host = '127.0.0.1',
123
+ host = "127.0.0.1",
43
124
  openBrowser = true,
44
125
  bundleDir = null,
45
126
  logger = new Logger(),
46
- defaultSizes = 'parsed',
127
+ defaultSizes = "parsed",
47
128
  compressionAlgorithm,
48
129
  excludeAssets = null,
49
130
  reportTitle,
@@ -55,32 +136,38 @@ async function startServer(bundleStats, opts) {
55
136
  compressionAlgorithm
56
137
  };
57
138
  let chartData = getChartData(analyzerOpts, bundleStats, bundleDir);
58
- const entrypoints = getEntrypoints(bundleStats);
59
- if (!chartData) return;
139
+ if (!chartData) {
140
+ throw new Error("Can't get chart data");
141
+ }
60
142
  const sirvMiddleware = sirv(`${projectRoot}/public`, {
61
143
  // disables caching and traverse the file system on every request
62
144
  dev: true
63
145
  });
146
+ const entrypoints = getEntrypoints(bundleStats);
64
147
  const server = http.createServer((req, res) => {
65
- if (req.method === 'GET' && req.url === '/') {
148
+ if (req.method === "GET" && req.url === "/") {
66
149
  const html = renderViewer({
67
- mode: 'server',
150
+ mode: "server",
68
151
  title: resolveTitle(reportTitle),
69
- chartData,
152
+ chartData: (/** @type {ChartData} */chartData),
70
153
  entrypoints,
71
154
  defaultSizes: resolveDefaultSizes(defaultSizes, compressionAlgorithm),
72
155
  compressionAlgorithm,
73
156
  enableWebSocket: true
74
157
  });
75
158
  res.writeHead(200, {
76
- 'Content-Type': 'text/html'
159
+ "Content-Type": "text/html"
77
160
  });
78
161
  res.end(html);
79
162
  } else {
80
163
  sirvMiddleware(req, res);
81
164
  }
82
165
  });
83
- await new Promise(resolve => {
166
+ await new Promise(
167
+ /**
168
+ * @param {(value: void) => void} resolve resolve
169
+ */
170
+ resolve => {
84
171
  server.listen(port, host, () => {
85
172
  resolve();
86
173
  const url = analyzerUrl({
@@ -88,7 +175,7 @@ async function startServer(bundleStats, opts) {
88
175
  listenHost: host,
89
176
  boundAddress: server.address()
90
177
  });
91
- logger.info(`${bold('Webpack Bundle Analyzer')} is started at ${bold(url)}\n` + `Use ${bold('Ctrl+C')} to close it`);
178
+ logger.info(`${bold("Webpack Bundle Analyzer")} is started at ${bold(url)}\n` + `Use ${bold("Ctrl+C")} to close it`);
92
179
  if (openBrowser) {
93
180
  open(url, logger);
94
181
  }
@@ -97,32 +184,54 @@ async function startServer(bundleStats, opts) {
97
184
  const wss = new WebSocket.Server({
98
185
  server
99
186
  });
100
- wss.on('connection', ws => {
101
- ws.on('error', err => {
187
+ wss.on("connection", ws => {
188
+ ws.on("error", err => {
102
189
  // Ignore network errors like `ECONNRESET`, `EPIPE`, etc.
103
- if (err.errno) return;
190
+ if (/** @type {NodeJS.ErrnoException} */err.errno) return;
104
191
  logger.info(err.message);
105
192
  });
106
193
  });
107
- return {
108
- ws: wss,
109
- http: server,
110
- updateChartData
111
- };
194
+
195
+ /**
196
+ * @param {StatsCompilation} bundleStats bundle stats
197
+ */
112
198
  function updateChartData(bundleStats) {
113
199
  const newChartData = getChartData(analyzerOpts, bundleStats, bundleDir);
114
200
  if (!newChartData) return;
115
201
  chartData = newChartData;
116
- wss.clients.forEach(client => {
202
+ for (const client of wss.clients) {
117
203
  if (client.readyState === WebSocket.OPEN) {
118
204
  client.send(JSON.stringify({
119
- event: 'chartDataUpdated',
205
+ event: "chartDataUpdated",
120
206
  data: newChartData
121
207
  }));
122
208
  }
123
- });
209
+ }
124
210
  }
211
+ return {
212
+ ws: wss,
213
+ http: server,
214
+ updateChartData
215
+ };
125
216
  }
217
+
218
+ /**
219
+ * @typedef {object} GenerateReportOptions
220
+ * @property {boolean} openBrowser true when need to open browser, otherwise false
221
+ * @property {string} reportFilename report filename
222
+ * @property {ReportTitle} reportTitle report title
223
+ * @property {string | null} bundleDir bundle dir
224
+ * @property {Logger} logger logger
225
+ * @property {Sizes} defaultSizes default sizes
226
+ * @property {CompressionAlgorithm} compressionAlgorithm compression algorithm
227
+ * @property {ExcludeAssets} excludeAssets exclude assets
228
+ */
229
+
230
+ /**
231
+ * @param {StatsCompilation} bundleStats bundle stats
232
+ * @param {GenerateReportOptions} opts opts
233
+ * @returns {Promise<void>}
234
+ */
126
235
  async function generateReport(bundleStats, opts) {
127
236
  const {
128
237
  openBrowser = true,
@@ -130,7 +239,7 @@ async function generateReport(bundleStats, opts) {
130
239
  reportTitle,
131
240
  bundleDir = null,
132
241
  logger = new Logger(),
133
- defaultSizes = 'parsed',
242
+ defaultSizes = "parsed",
134
243
  compressionAlgorithm,
135
244
  excludeAssets = null
136
245
  } = opts || {};
@@ -142,7 +251,7 @@ async function generateReport(bundleStats, opts) {
142
251
  const entrypoints = getEntrypoints(bundleStats);
143
252
  if (!chartData) return;
144
253
  const reportHtml = renderViewer({
145
- mode: 'static',
254
+ mode: "static",
146
255
  title: resolveTitle(reportTitle),
147
256
  chartData,
148
257
  entrypoints,
@@ -155,11 +264,26 @@ async function generateReport(bundleStats, opts) {
155
264
  recursive: true
156
265
  });
157
266
  fs.writeFileSync(reportFilepath, reportHtml);
158
- logger.info(`${bold('Webpack Bundle Analyzer')} saved report to ${bold(reportFilepath)}`);
267
+ logger.info(`${bold("Webpack Bundle Analyzer")} saved report to ${bold(reportFilepath)}`);
159
268
  if (openBrowser) {
160
269
  open(`file://${reportFilepath}`, logger);
161
270
  }
162
271
  }
272
+
273
+ /**
274
+ * @typedef {object} GenerateJSONReportOptions
275
+ * @property {string} reportFilename report filename
276
+ * @property {string | null} bundleDir bundle dir
277
+ * @property {Logger} logger logger
278
+ * @property {ExcludeAssets} excludeAssets exclude assets
279
+ * @property {CompressionAlgorithm} compressionAlgorithm compression algorithm
280
+ */
281
+
282
+ /**
283
+ * @param {StatsCompilation} bundleStats bundle stats
284
+ * @param {GenerateJSONReportOptions} opts options
285
+ * @returns {Promise<void>}
286
+ */
163
287
  async function generateJSONReport(bundleStats, opts) {
164
288
  const {
165
289
  reportFilename,
@@ -178,33 +302,13 @@ async function generateJSONReport(bundleStats, opts) {
178
302
  recursive: true
179
303
  });
180
304
  await fs.promises.writeFile(reportFilename, JSON.stringify(chartData));
181
- logger.info(`${bold('Webpack Bundle Analyzer')} saved JSON report to ${bold(reportFilename)}`);
305
+ logger.info(`${bold("Webpack Bundle Analyzer")} saved JSON report to ${bold(reportFilename)}`);
182
306
  }
183
- function getChartData(analyzerOpts, ...args) {
184
- let chartData;
185
- const {
186
- logger
187
- } = analyzerOpts;
188
- try {
189
- chartData = analyzer.getViewerData(...args, analyzerOpts);
190
- } catch (err) {
191
- logger.error(`Couldn't analyze webpack bundle:\n${err}`);
192
- logger.debug(err.stack);
193
- chartData = null;
194
- }
195
-
196
- // chartData can either be an array (bundleInfo[]) or null. It can't be an plain object anyway
197
- if (
198
- // analyzer.getViewerData() doesn't failed in the previous step
199
- chartData && !Array.isArray(chartData)) {
200
- logger.error("Couldn't find any javascript bundles in provided stats file");
201
- chartData = null;
202
- }
203
- return chartData;
204
- }
205
- function getEntrypoints(bundleStats) {
206
- if (bundleStats === null || bundleStats === undefined || !bundleStats.entrypoints) {
207
- return [];
208
- }
209
- return Object.values(bundleStats.entrypoints).map(entrypoint => entrypoint.name);
210
- }
307
+ module.exports = {
308
+ generateJSONReport,
309
+ generateReport,
310
+ getEntrypoints,
311
+ // deprecated
312
+ start: startServer,
313
+ startServer
314
+ };
package/package.json CHANGED
@@ -1,11 +1,20 @@
1
1
  {
2
2
  "name": "webpack-bundle-analyzer",
3
- "version": "5.2.0",
3
+ "version": "5.3.0",
4
4
  "description": "Webpack plugin and CLI utility that represents bundle content as convenient interactive zoomable treemap",
5
- "author": "Yury Grunin <grunin.ya@ya.ru>",
6
- "license": "MIT",
5
+ "keywords": [
6
+ "webpack",
7
+ "bundle",
8
+ "analyzer",
9
+ "modules",
10
+ "size",
11
+ "interactive",
12
+ "chart",
13
+ "treemap",
14
+ "zoomable",
15
+ "zoom"
16
+ ],
7
17
  "homepage": "https://github.com/webpack/webpack-bundle-analyzer",
8
- "changelog": "https://github.com/webpack/webpack-bundle-analyzer/blob/main/CHANGELOG.md",
9
18
  "bugs": {
10
19
  "url": "https://github.com/webpack/webpack-bundle-analyzer/issues"
11
20
  },
@@ -13,95 +22,95 @@
13
22
  "type": "git",
14
23
  "url": "git+https://github.com/webpack/webpack-bundle-analyzer.git"
15
24
  },
25
+ "license": "MIT",
26
+ "author": "Yury Grunin <grunin.ya@ya.ru>",
16
27
  "main": "lib/index.js",
17
28
  "bin": "lib/bin/analyzer.js",
18
- "engines": {
19
- "node": ">= 20.9.0"
20
- },
21
- "packageManager": "npm@6.14.8",
22
- "scripts": {
23
- "start": "gulp watch",
24
- "build": "gulp build",
25
- "npm-publish": "npm run lint && npm run build && npm test && npm publish",
26
- "lint": "eslint --ext js,jsx .",
27
- "install-test-webpack-versions": "./bin/install-test-webpack-versions.sh",
28
- "test": "npm run install-test-webpack-versions && NODE_OPTIONS=--openssl-legacy-provider jest --runInBand",
29
- "test-dev": "npm run install-test-webpack-versions && NODE_OPTIONS=--openssl-legacy-provider jest --watch --runInBand"
30
- },
31
29
  "files": [
32
30
  "public",
33
31
  "lib"
34
32
  ],
33
+ "scripts": {
34
+ "clean:analyzer": "del-cli lib",
35
+ "clean:viewer": "del-cli public",
36
+ "clean": "npm run clean:analyzer && npm run clean:viewer",
37
+ "build:analyzer": "npm run clean:analyzer && babel src -d lib --copy-files",
38
+ "build:viewer": "npm run clean:viewer && webpack-cli --node-env=production",
39
+ "build": "npm run build:analyzer && npm run build:viewer",
40
+ "watch:analyzer": "npm run build:analyzer -- --watch",
41
+ "watch:viewer": "npm run build:viewer -- --node-env=development --watch",
42
+ "npm-publish": "npm run lint && npm run build && npm test && npm publish",
43
+ "lint": "npm run lint:code && npm run lint:types && npm run fmt:check",
44
+ "lint:code": "eslint --cache .",
45
+ "lint:types": "tsc --pretty --noEmit",
46
+ "fmt": "npm run fmt:base -- --log-level warn --write",
47
+ "fmt:check": "npm run fmt:base -- --check",
48
+ "fmt:base": "prettier --cache --ignore-unknown .",
49
+ "fix": "npm run fix:code && npm run fmt",
50
+ "test": "NODE_OPTIONS=--openssl-legacy-provider jest --runInBand",
51
+ "test:coverage": "npm run test -- --coverage",
52
+ "test-dev": "NODE_OPTIONS=--openssl-legacy-provider jest --watch --runInBand",
53
+ "version": "changeset version",
54
+ "release": "npm run build && changeset publish"
55
+ },
35
56
  "dependencies": {
36
- "@discoveryjs/json-ext": "0.5.7",
57
+ "@discoveryjs/json-ext": "^0.6.3",
37
58
  "acorn": "^8.0.4",
38
59
  "acorn-walk": "^8.0.0",
39
- "commander": "^7.2.0",
40
- "debounce": "^1.2.1",
41
- "escape-string-regexp": "^4.0.0",
42
- "html-escaper": "^2.0.2",
60
+ "commander": "^14.0.2",
61
+ "escape-string-regexp": "^5.0.0",
62
+ "html-escaper": "^3.0.3",
43
63
  "opener": "^1.5.2",
44
64
  "picocolors": "^1.0.0",
45
65
  "sirv": "^3.0.2",
46
66
  "ws": "^8.19.0"
47
67
  },
48
68
  "devDependencies": {
49
- "@babel/core": "7.26.9",
50
- "@babel/plugin-proposal-decorators": "7.25.9",
69
+ "@babel/cli": "^7.28.6",
70
+ "@babel/core": "^7.26.9",
51
71
  "@babel/plugin-transform-class-properties": "^7.27.1",
52
- "@babel/plugin-transform-runtime": "7.26.9",
53
- "@babel/preset-env": "7.26.9",
54
- "@babel/preset-react": "7.26.3",
55
- "@babel/runtime": "7.26.9",
56
- "@carrotsearch/foamtree": "3.5.0",
57
- "autoprefixer": "10.2.5",
58
- "babel-eslint": "10.1.0",
59
- "babel-loader": "9.2.1",
60
- "babel-plugin-lodash": "3.3.4",
61
- "chai": "4.3.4",
62
- "chai-subset": "1.6.0",
63
- "classnames": "2.3.1",
64
- "core-js": "3.12.1",
65
- "css-loader": "5.2.5",
66
- "cssnano": "5.0.4",
67
- "del": "6.0.0",
68
- "eslint": "5.16.0",
69
- "eslint-config-th0r": "2.0.0",
70
- "eslint-config-th0r-react": "2.0.1",
71
- "eslint-plugin-react": "7.23.2",
72
- "filesize": "^6.3.0",
73
- "globby": "11.0.3",
74
- "gulp": "4.0.2",
75
- "gulp-babel": "8.0.0",
72
+ "@babel/plugin-transform-runtime": "^7.26.9",
73
+ "@babel/preset-env": "^7.26.9",
74
+ "@babel/preset-react": "^7.26.3",
75
+ "@babel/runtime": "^7.26.9",
76
+ "@carrotsearch/foamtree": "^3.5.0",
77
+ "@changesets/cli": "^2.30.0",
78
+ "@changesets/get-github-info": "^0.8.0",
79
+ "@types/html-escaper": "^3.0.4",
80
+ "@types/opener": "^1.4.3",
81
+ "autoprefixer": "^10.2.5",
82
+ "babel-eslint": "^10.1.0",
83
+ "babel-loader": "^10.0.0",
84
+ "classnames": "^2.3.1",
85
+ "core-js": "^3.12.1",
86
+ "css-loader": "^7.1.3",
87
+ "cssnano": "^7.1.2",
88
+ "debounce": "^3.0.0",
89
+ "del-cli": "^7.0.0",
90
+ "eslint": "^9.39.2",
91
+ "eslint-config-webpack": "^4.9.3",
92
+ "filesize": "^11.0.13",
76
93
  "jest": "^30.2.0",
77
- "lodash.memoize": "^4.1.2",
78
- "lodash.merge": "^4.6.2",
79
- "lodash.partial": "^4.2.1",
80
- "mobx": "5.15.7",
81
- "mobx-react": "6.3.1",
82
- "postcss": "8.3.0",
83
- "postcss-icss-values": "2.0.2",
84
- "postcss-loader": "5.3.0",
85
- "preact": "10.5.13",
94
+ "mobx": "^6.15.0",
95
+ "mobx-react": "^9.2.1",
96
+ "postcss": "^8.3.0",
97
+ "postcss-loader": "^8.2.0",
98
+ "preact": "^10.5.13",
99
+ "prettier": "^3.8.0",
100
+ "prop-types": "^15.8.1",
86
101
  "puppeteer": "^24.30.0",
87
- "stream-combiner2": "1.1.1",
88
- "style-loader": "2.0.0",
89
- "terser-webpack-plugin": "5.1.2",
90
- "url-loader": "4.1.1",
91
- "webpack": "5.98.0",
92
- "webpack-cli": "6.0.1",
93
- "webpack-dev-server": "5.2.0"
102
+ "style-loader": "^4.0.0",
103
+ "terser-webpack-plugin": "^5.1.2",
104
+ "tinyglobby": "^0.2.15",
105
+ "typescript": "^5.9.3",
106
+ "webpack": "^5.105.2",
107
+ "webpack-4": "npm:webpack@^4",
108
+ "webpack-cli": "^6.0.1",
109
+ "webpack-dev-server": "^5.2.0"
94
110
  },
95
- "keywords": [
96
- "webpack",
97
- "bundle",
98
- "analyzer",
99
- "modules",
100
- "size",
101
- "interactive",
102
- "chart",
103
- "treemap",
104
- "zoomable",
105
- "zoom"
106
- ]
111
+ "packageManager": "npm@10.1.0",
112
+ "engines": {
113
+ "node": ">= 20.9.0"
114
+ },
115
+ "changelog": "https://github.com/webpack/webpack-bundle-analyzer/blob/main/CHANGELOG.md"
107
116
  }