@mui/internal-bundle-size-checker 1.0.3 → 1.0.5
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/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -4
- package/src/cli.js +10 -272
- package/src/fetchSnapshot.js +58 -0
- package/src/index.js +2 -9
- package/src/renderMarkdownReport.js +72 -74
- package/src/renderMarkdownReport.test.js +559 -0
- package/src/types.d.ts +1 -58
- package/src/viteBuilder.js +229 -0
- package/src/webpackBuilder.js +267 -0
- package/src/worker.js +51 -283
- package/tsconfig.json +3 -2
- package/vite.config.mts +5 -0
package/src/worker.js
CHANGED
|
@@ -1,25 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { pathToFileURL } from 'url';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fs from 'fs/promises';
|
|
4
|
-
import { pathToFileURL } from 'url';
|
|
5
|
-
import webpackCallbackBased from 'webpack';
|
|
6
|
-
import CompressionPlugin from 'compression-webpack-plugin';
|
|
7
|
-
import TerserPlugin from 'terser-webpack-plugin';
|
|
8
|
-
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
|
9
|
-
import { createRequire } from 'node:module';
|
|
10
4
|
import chalk from 'chalk';
|
|
11
5
|
import * as module from 'module';
|
|
12
6
|
import { byteSizeFormatter } from './formatUtils.js';
|
|
7
|
+
import { getWebpackSizes } from './webpackBuilder.js';
|
|
8
|
+
import { getViteSizes } from './viteBuilder.js';
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
* @type {(options: webpackCallbackBased.Configuration) => Promise<webpackCallbackBased.Stats>}
|
|
16
|
-
*/
|
|
17
|
-
// @ts-expect-error Can't select the right overload
|
|
18
|
-
const webpack = promisify(webpackCallbackBased);
|
|
19
|
-
const rootDir = process.cwd();
|
|
20
|
-
const require = createRequire(import.meta.url);
|
|
10
|
+
const require = module.createRequire(import.meta.url);
|
|
21
11
|
|
|
22
|
-
|
|
12
|
+
const rootDir = process.cwd();
|
|
23
13
|
|
|
24
14
|
/**
|
|
25
15
|
* Attempts to extract peer dependencies from a package's package.json
|
|
@@ -66,18 +56,13 @@ async function getPeerDependencies(packageName) {
|
|
|
66
56
|
}
|
|
67
57
|
|
|
68
58
|
/**
|
|
69
|
-
*
|
|
70
|
-
* @param {
|
|
71
|
-
* @
|
|
72
|
-
* @returns {Promise<{configuration: import('webpack').Configuration, externalsArray: string[]}>}
|
|
59
|
+
* Get sizes for a bundle
|
|
60
|
+
* @param {{ entry: ObjectEntry, args: CommandLineArgs, index: number, total: number }} options
|
|
61
|
+
* @returns {Promise<Array<[string, { parsed: number, gzip: number }]>>}
|
|
73
62
|
*/
|
|
74
|
-
async function
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const entryName = entry.id;
|
|
79
|
-
let entryContent;
|
|
80
|
-
let packageExternals = null;
|
|
63
|
+
export default async function getSizes({ entry, args, index, total }) {
|
|
64
|
+
// eslint-disable-next-line no-console -- process monitoring
|
|
65
|
+
console.log(chalk.blue(`Compiling ${index + 1}/${total}: ${chalk.bold(`[${entry.id}]`)}`));
|
|
81
66
|
|
|
82
67
|
// Process peer dependencies if externals aren't specified but import is
|
|
83
68
|
if (entry.import && !entry.externals) {
|
|
@@ -85,275 +70,58 @@ async function createWebpackConfig(entry, args) {
|
|
|
85
70
|
.split('/')
|
|
86
71
|
.slice(0, entry.import.startsWith('@') ? 2 : 1)
|
|
87
72
|
.join('/');
|
|
88
|
-
packageExternals = await getPeerDependencies(packageRoot);
|
|
73
|
+
const packageExternals = await getPeerDependencies(packageRoot);
|
|
74
|
+
|
|
75
|
+
// Generate externals based on priorities:
|
|
76
|
+
// 1. Explicitly defined externals in the entry object
|
|
77
|
+
// 2. Peer dependencies from package.json if available
|
|
78
|
+
// 3. Default externals (react, react-dom)
|
|
79
|
+
entry.externals = packageExternals ?? ['react', 'react-dom'];
|
|
80
|
+
} else if (!entry.externals) {
|
|
81
|
+
// Set default externals if not specified
|
|
82
|
+
entry.externals = ['react', 'react-dom'];
|
|
89
83
|
}
|
|
90
84
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
),
|
|
96
|
-
);
|
|
97
|
-
entryContent = entry.code;
|
|
98
|
-
} else if (entry.code) {
|
|
99
|
-
entryContent = entry.code;
|
|
100
|
-
} else if (entry.import) {
|
|
101
|
-
if (entry.importedNames && entry.importedNames.length > 0) {
|
|
102
|
-
// Generate named imports for each name in the importedNames array
|
|
103
|
-
const imports = entry.importedNames
|
|
104
|
-
.map((name) => `import { ${name} } from '${entry.import}';`)
|
|
105
|
-
.join('\n');
|
|
106
|
-
const logs = entry.importedNames.map((name) => `console.log(${name});`).join('\n');
|
|
107
|
-
entryContent = `${imports}\n${logs}`;
|
|
85
|
+
try {
|
|
86
|
+
let sizeMap;
|
|
87
|
+
if (args.vite) {
|
|
88
|
+
sizeMap = await getViteSizes(entry, args);
|
|
108
89
|
} else {
|
|
109
|
-
|
|
110
|
-
entryContent = `import * as _ from '${entry.import}';\nconsole.log(_);`;
|
|
90
|
+
sizeMap = await getWebpackSizes(entry, args);
|
|
111
91
|
}
|
|
112
|
-
} else {
|
|
113
|
-
throw new Error(`Entry "${entry.id}" must have either code or import property defined`);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Generate externals function from an array of package names
|
|
118
|
-
* @param {string[]} packages - Array of package names to exclude (defaults to react and react-dom)
|
|
119
|
-
* @returns {function} - Function to determine if a request should be treated as external
|
|
120
|
-
*/
|
|
121
|
-
function createExternalsFunction(packages = ['react', 'react-dom']) {
|
|
122
|
-
/**
|
|
123
|
-
* Check if a request should be treated as external
|
|
124
|
-
* Uses the new recommended format to avoid deprecation warnings
|
|
125
|
-
* @param {{ context: string, request: string }} params - Object containing context and request
|
|
126
|
-
* @param {Function} callback - Callback to handle the result
|
|
127
|
-
*/
|
|
128
|
-
return ({ request }, callback) => {
|
|
129
|
-
// Iterate through all packages and check if request is equal to or starts with package + '/'
|
|
130
|
-
for (const pkg of packages) {
|
|
131
|
-
if (request === pkg || request.startsWith(`${pkg}/`)) {
|
|
132
|
-
return callback(null, `commonjs ${request}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return callback();
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Generate externals based on priorities:
|
|
141
|
-
// 1. Explicitly defined externals in the entry object
|
|
142
|
-
// 2. Peer dependencies from package.json if available
|
|
143
|
-
// 3. Default externals (react, react-dom)
|
|
144
|
-
const externalsArray =
|
|
145
|
-
typeof entry === 'object' && entry.externals
|
|
146
|
-
? entry.externals
|
|
147
|
-
: (packageExternals ?? ['react', 'react-dom']);
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* @type {import('webpack').Configuration}
|
|
151
|
-
*/
|
|
152
|
-
const configuration = {
|
|
153
|
-
externals: [
|
|
154
|
-
// @ts-expect-error -- webpack types are not compatible with the current version
|
|
155
|
-
createExternalsFunction(externalsArray),
|
|
156
|
-
],
|
|
157
|
-
mode: 'production',
|
|
158
|
-
optimization: {
|
|
159
|
-
concatenateModules,
|
|
160
|
-
minimizer: [
|
|
161
|
-
new TerserPlugin({
|
|
162
|
-
test: /\.m?js(\?.*)?$/i,
|
|
163
|
-
// Avoid creating LICENSE.txt files for each module
|
|
164
|
-
// See https://github.com/webpack-contrib/terser-webpack-plugin#remove-comments
|
|
165
|
-
terserOptions: {
|
|
166
|
-
format: {
|
|
167
|
-
comments: false,
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
extractComments: false,
|
|
171
|
-
}),
|
|
172
|
-
],
|
|
173
|
-
},
|
|
174
|
-
module: {
|
|
175
|
-
rules: [
|
|
176
|
-
{
|
|
177
|
-
test: /\.[jt]sx?$/,
|
|
178
|
-
include: rootDir,
|
|
179
|
-
exclude: /node_modules/,
|
|
180
|
-
use: {
|
|
181
|
-
loader: require.resolve('babel-loader'),
|
|
182
|
-
options: {
|
|
183
|
-
presets: [
|
|
184
|
-
require.resolve('@babel/preset-react'),
|
|
185
|
-
require.resolve('@babel/preset-typescript'),
|
|
186
|
-
],
|
|
187
|
-
},
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
test: /\.css$/,
|
|
192
|
-
use: [require.resolve('css-loader')],
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
test: /\.(png|svg|jpg|gif)$/,
|
|
196
|
-
use: [require.resolve('file-loader')],
|
|
197
|
-
},
|
|
198
|
-
],
|
|
199
|
-
},
|
|
200
|
-
output: {
|
|
201
|
-
filename: '[name].js',
|
|
202
|
-
library: {
|
|
203
|
-
// TODO: Use `type: 'module'` once it is supported (currently incompatible with `externals`)
|
|
204
|
-
name: 'M',
|
|
205
|
-
type: 'var',
|
|
206
|
-
// type: 'module',
|
|
207
|
-
},
|
|
208
|
-
path: path.join(rootDir, 'build'),
|
|
209
|
-
},
|
|
210
|
-
plugins: [
|
|
211
|
-
new CompressionPlugin({
|
|
212
|
-
filename: '[path][base][fragment].gz',
|
|
213
|
-
}),
|
|
214
|
-
new BundleAnalyzerPlugin({
|
|
215
|
-
analyzerMode,
|
|
216
|
-
// We create a report for each bundle so around 120 reports.
|
|
217
|
-
// Opening them all is spam.
|
|
218
|
-
// If opened with `webpack --config . --analyze` it'll still open one new tab though.
|
|
219
|
-
openAnalyzer: false,
|
|
220
|
-
// '[name].html' not supported: https://github.com/webpack-contrib/webpack-bundle-analyzer/issues/12
|
|
221
|
-
reportFilename: `${entryName}.html`,
|
|
222
|
-
logLevel: 'warn',
|
|
223
|
-
}),
|
|
224
|
-
],
|
|
225
|
-
// A context to the current dir, which has a node_modules folder with workspace dependencies
|
|
226
|
-
context: rootDir,
|
|
227
|
-
entry: {
|
|
228
|
-
// This format is a data: url combined with inline matchResource to obtain a virtual entry.
|
|
229
|
-
// See https://github.com/webpack/webpack/issues/6437#issuecomment-874466638
|
|
230
|
-
// See https://webpack.js.org/api/module-methods/#import
|
|
231
|
-
// See https://webpack.js.org/api/loaders/#inline-matchresource
|
|
232
|
-
[entryName]: `./index.js!=!data:text/javascript;charset=utf-8;base64,${Buffer.from(entryContent.trim()).toString('base64')}`,
|
|
233
|
-
},
|
|
234
|
-
// TODO: 'browserslist:modern'
|
|
235
|
-
// See https://github.com/webpack/webpack/issues/14203
|
|
236
|
-
target: 'web',
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
// Return both the configuration and the externals array
|
|
240
|
-
return { configuration, externalsArray };
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Get sizes for a bundle
|
|
245
|
-
* @param {{ entry: ObjectEntry, args: CommandLineArgs, index: number, total: number }} options
|
|
246
|
-
* @returns {Promise<Array<[string, { parsed: number, gzip: number }]>>}
|
|
247
|
-
*/
|
|
248
|
-
export default async function getSizes({ entry, args, index, total }) {
|
|
249
|
-
/** @type {Map<string, { parsed: number, gzip: number }>} */
|
|
250
|
-
const sizeMap = new Map();
|
|
251
|
-
|
|
252
|
-
// Create webpack configuration (now async to handle peer dependency resolution)
|
|
253
|
-
const { configuration, externalsArray } = await createWebpackConfig(entry, args);
|
|
254
|
-
|
|
255
|
-
// eslint-disable-next-line no-console -- process monitoring
|
|
256
|
-
console.log(chalk.blue(`Compiling ${index + 1}/${total}: ${chalk.bold(`[${entry.id}]`)}`));
|
|
257
|
-
|
|
258
|
-
const webpackStats = await webpack(configuration);
|
|
259
|
-
|
|
260
|
-
if (!webpackStats) {
|
|
261
|
-
throw new Error('No webpack stats were returned');
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (webpackStats.hasErrors()) {
|
|
265
|
-
const statsJson = webpackStats.toJson({
|
|
266
|
-
all: false,
|
|
267
|
-
entrypoints: true,
|
|
268
|
-
errors: true,
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
const entrypointKeys = statsJson.entrypoints ? Object.keys(statsJson.entrypoints) : [];
|
|
272
|
-
|
|
273
|
-
throw new Error(
|
|
274
|
-
`${chalk.red.bold('ERROR:')} The following errors occurred during bundling of ${chalk.yellow(entrypointKeys.join(', '))} with webpack: \n${(
|
|
275
|
-
statsJson.errors || []
|
|
276
|
-
)
|
|
277
|
-
.map((error) => {
|
|
278
|
-
return `${JSON.stringify(error, null, 2)}`;
|
|
279
|
-
})
|
|
280
|
-
.join('\n')}`,
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const stats = webpackStats.toJson({
|
|
285
|
-
all: false,
|
|
286
|
-
assets: true,
|
|
287
|
-
entrypoints: true,
|
|
288
|
-
relatedAssets: true,
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
if (!stats.assets) {
|
|
292
|
-
return Array.from(sizeMap.entries());
|
|
293
|
-
}
|
|
294
92
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (asset && asset.related) {
|
|
306
|
-
const gzippedAsset = asset.related.find((relatedAsset) => {
|
|
307
|
-
return relatedAsset.type === 'gzipped';
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
if (size !== undefined) {
|
|
311
|
-
parsedSize += size;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (gzippedAsset && gzippedAsset.size !== undefined) {
|
|
315
|
-
gzipSize += gzippedAsset.size;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
});
|
|
93
|
+
// Create a concise log message showing import details
|
|
94
|
+
let entryDetails = '';
|
|
95
|
+
if (entry.code) {
|
|
96
|
+
entryDetails = 'code import';
|
|
97
|
+
} else if (entry.import) {
|
|
98
|
+
entryDetails = `${entry.import}`;
|
|
99
|
+
if (entry.importedNames && entry.importedNames.length > 0) {
|
|
100
|
+
entryDetails += ` [${entry.importedNames.join(', ')}]`;
|
|
101
|
+
} else {
|
|
102
|
+
entryDetails += ' [*]';
|
|
319
103
|
}
|
|
320
|
-
|
|
321
|
-
if (!entrypoint.name) {
|
|
322
|
-
throw new Error('Entrypoint name is undefined');
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
sizeMap.set(entrypoint.name, { parsed: parsedSize, gzip: gzipSize });
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Create a concise log message showing import details
|
|
330
|
-
let entryDetails = '';
|
|
331
|
-
if (entry.code) {
|
|
332
|
-
entryDetails = 'code import';
|
|
333
|
-
} else if (entry.import) {
|
|
334
|
-
entryDetails = `${entry.import}`;
|
|
335
|
-
if (entry.importedNames && entry.importedNames.length > 0) {
|
|
336
|
-
entryDetails += ` [${entry.importedNames.join(', ')}]`;
|
|
337
|
-
} else {
|
|
338
|
-
entryDetails += ' [*]';
|
|
339
104
|
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Print a summary with all the requested information
|
|
343
|
-
// Get the size entry for this entry.id from the map
|
|
344
|
-
const entrySize = sizeMap.get(entry.id) || { parsed: 0, gzip: 0 };
|
|
345
105
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
106
|
+
// Print a summary with all the requested information
|
|
107
|
+
// Get the size entry for this entry.id from the map
|
|
108
|
+
const entrySize = sizeMap.get(entry.id) || { parsed: 0, gzip: 0 };
|
|
109
|
+
// eslint-disable-next-line no-console -- process monitoring
|
|
110
|
+
console.log(
|
|
111
|
+
`
|
|
349
112
|
${chalk.green('✓')} ${chalk.green.bold(`Completed ${index + 1}/${total}: [${entry.id}]`)}
|
|
350
113
|
${chalk.cyan('Import:')} ${entryDetails}
|
|
351
|
-
${chalk.cyan('Externals:')} ${
|
|
114
|
+
${chalk.cyan('Externals:')} ${entry.externals.join(', ')}
|
|
115
|
+
${chalk.cyan('Bundler:')} ${args.vite ? 'vite' : 'webpack'}
|
|
352
116
|
${chalk.cyan('Sizes:')} ${chalk.yellow(byteSizeFormatter.format(entrySize.parsed))} (${chalk.yellow(byteSizeFormatter.format(entrySize.gzip))} gzipped)
|
|
353
117
|
${args.analyze ? ` ${chalk.cyan('Analysis:')} ${chalk.underline(pathToFileURL(path.join(rootDir, 'build', `${entry.id}.html`)).href)}` : ''}
|
|
354
118
|
`.trim(),
|
|
355
|
-
|
|
119
|
+
);
|
|
356
120
|
|
|
357
|
-
|
|
358
|
-
|
|
121
|
+
// Convert the Map to an array of entries for the return value
|
|
122
|
+
return Array.from(sizeMap.entries());
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error(chalk.red(`Error processing bundle for ${entry.id}:`), error);
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
359
127
|
}
|
package/tsconfig.json
CHANGED
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
"resolveJsonModule": true,
|
|
12
12
|
"isolatedModules": true,
|
|
13
13
|
"outDir": "./build",
|
|
14
|
-
"noEmit": true
|
|
14
|
+
"noEmit": true,
|
|
15
|
+
"composite": true
|
|
15
16
|
},
|
|
16
|
-
"include": ["
|
|
17
|
+
"include": ["src"],
|
|
17
18
|
"exclude": ["node_modules", "build"]
|
|
18
19
|
}
|