@mui/internal-bundle-size-checker 1.0.4 → 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/src/worker.js CHANGED
@@ -1,25 +1,15 @@
1
- import { promisify } from 'util';
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
- // Type declarations are now in types.d.ts
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
- * Creates webpack configuration for bundle size checking
70
- * @param {ObjectEntry} entry - Entry point (string or object)
71
- * @param {CommandLineArgs} args
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 createWebpackConfig(entry, args) {
75
- const analyzerMode = args.analyze ? 'static' : 'disabled';
76
- const concatenateModules = !args.accurateBundles;
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
- if (entry.code && (entry.import || entry.importedNames)) {
92
- console.warn(
93
- chalk.yellow(
94
- `Warning: Both code and import/importedNames are defined for entry "${chalk.bold(entry.id)}". Using code property.`,
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
- // Default to import * as if importedNames is not defined
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
- const assets = new Map(stats.assets.map((asset) => [asset.name, asset]));
296
-
297
- if (stats.entrypoints) {
298
- Object.values(stats.entrypoints).forEach((entrypoint) => {
299
- let parsedSize = 0;
300
- let gzipSize = 0;
301
-
302
- if (entrypoint.assets) {
303
- entrypoint.assets.forEach(({ name, size }) => {
304
- const asset = assets.get(name);
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
- // eslint-disable-next-line no-console -- process monitoring
347
- console.log(
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:')} ${externalsArray.join(', ')}
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
- // Convert the Map to an array of entries for the return value
358
- return Array.from(sizeMap.entries());
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
  }
@@ -0,0 +1,5 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {},
5
+ });