package-build-stats 8.0.3 → 8.1.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.
@@ -21,9 +21,15 @@ function makeRspackConfig({ packageName: _packageName, entry, externals, debug:
21
21
  mode: 'production',
22
22
  devtool: _debug ? 'source-map' : false,
23
23
  optimization: {
24
- runtimeChunk: { name: 'runtime' },
24
+ // Use 'multiple' to create a separate runtime chunk per entry point
25
+ // 'single' or { name: 'runtime' } shares one runtime across all entries,
26
+ // which breaks tree-shaking when building many exports simultaneously
27
+ runtimeChunk: 'multiple',
25
28
  realContentHash: false,
26
29
  minimize: minify,
30
+ // Enable tree-shaking optimizations
31
+ usedExports: true, // Mark unused exports for removal
32
+ sideEffects: true, // Respect package.json sideEffects field
27
33
  // Rspack automatically uses its built-in default minifiers:
28
34
  // - SwcJsMinimizerRspackPlugin for JS (SWC-based, very fast)
29
35
  // - LightningCssMinimizerRspackPlugin for CSS (Lightning CSS-based)
@@ -199,7 +199,11 @@ const BuildUtils = {
199
199
  gzip,
200
200
  };
201
201
  };
202
- const assetStatsPromises = ((_a = jsonStats === null || jsonStats === void 0 ? void 0 : jsonStats.assets) === null || _a === void 0 ? void 0 : _a.filter(asset => { var _a; return !((_a = asset.chunkNames) === null || _a === void 0 ? void 0 : _a.includes('runtime')); }).filter(asset => !asset.name.endsWith('LICENSE.txt')).map(getAssetStats)) || [];
202
+ const assetStatsPromises = ((_a = jsonStats === null || jsonStats === void 0 ? void 0 : jsonStats.assets) === null || _a === void 0 ? void 0 : _a.filter(asset => {
203
+ var _a;
204
+ return !((_a = asset.chunkNames) === null || _a === void 0 ? void 0 : _a.some(name => name === 'runtime' ||
205
+ (typeof name === 'string' && name.startsWith('runtime~'))));
206
+ }).filter(asset => !asset.name.endsWith('LICENSE.txt')).map(getAssetStats)) || [];
203
207
  const assetStats = await Promise.all(assetStatsPromises);
204
208
  telemetry_utils_1.default.assetsGZIPParseTime(name, perf_hooks_1.performance.now());
205
209
  let dependencySizeResults = {};
@@ -17,6 +17,7 @@ export declare function getAllExports(packageString: string, context: string, lo
17
17
  * Get exports details from code (compatibility function)
18
18
  *
19
19
  * This provides the same API as the existing getExportsDetails for backward compatibility
20
+ * Returns simple string arrays for exports (without moduleRequest info)
20
21
  */
21
22
  export declare function getExportsDetails(code: string, filename?: string): {
22
23
  exports: string[];
@@ -51,6 +51,7 @@ function getExportsFromStaticExports(staticExports) {
51
51
  const exportAllLocations = [];
52
52
  staticExports.forEach(staticExport => {
53
53
  staticExport.entries.forEach((entry) => {
54
+ var _a, _b;
54
55
  // Skip type-only exports (TypeScript type exports)
55
56
  if (entry.isType) {
56
57
  return;
@@ -67,10 +68,16 @@ function getExportsFromStaticExports(staticExports) {
67
68
  case 'None': // export const foo = 1
68
69
  // Get the export name
69
70
  if (entry.exportName.kind === 'Name' && entry.exportName.name) {
70
- exports.push(entry.exportName.name);
71
+ exports.push({
72
+ name: entry.exportName.name,
73
+ moduleRequest: (_a = entry.moduleRequest) === null || _a === void 0 ? void 0 : _a.value, // Track the source module for re-exports
74
+ });
71
75
  }
72
76
  else if (entry.exportName.kind === 'Default') {
73
- exports.push('default');
77
+ exports.push({
78
+ name: 'default',
79
+ moduleRequest: (_b = entry.moduleRequest) === null || _b === void 0 ? void 0 : _b.value,
80
+ });
74
81
  }
75
82
  break;
76
83
  }
@@ -113,15 +120,26 @@ async function walkExportsRecursive(context, lookupPath, visited, rootContext, i
113
120
  }
114
121
  const { exports, exportAllLocations } = getExportsFromStaticExports(parseResult.module.staticExports);
115
122
  const resolvedExports = {};
116
- // Add direct exports from this module
117
- exports.forEach(exp => {
118
- // Use path.relative() to calculate the relative path from root to resolvedPath
123
+ // Add direct exports from this module, resolving re-exports to their source files
124
+ for (const exp of exports) {
125
+ let sourcePath = resolvedPath;
126
+ // If this is a re-export (export { foo } from './module'), resolve to the source file
127
+ if (exp.moduleRequest) {
128
+ try {
129
+ sourcePath = await resolveModule(path_1.default.dirname(resolvedPath), exp.moduleRequest);
130
+ }
131
+ catch {
132
+ // If resolution fails, fall back to current file
133
+ sourcePath = resolvedPath;
134
+ }
135
+ }
136
+ // Use path.relative() to calculate the relative path from root to sourcePath
119
137
  // This works correctly since symlinks are not resolved (symlinks: false in resolver config)
120
138
  const relativePath = root
121
- ? path_1.default.relative(root, resolvedPath)
122
- : path_1.default.basename(resolvedPath);
123
- resolvedExports[exp] = relativePath;
124
- });
139
+ ? path_1.default.relative(root, sourcePath)
140
+ : path_1.default.basename(sourcePath);
141
+ resolvedExports[exp.name] = relativePath;
142
+ }
125
143
  // Recursively process export * statements
126
144
  const promises = exportAllLocations.map(async (location) => {
127
145
  const starExports = await walkExportsRecursive(path_1.default.dirname(resolvedPath), location, visited, root);
@@ -166,10 +184,16 @@ async function getAllExports(packageString, context, lookupPath, installPath) {
166
184
  * Get exports details from code (compatibility function)
167
185
  *
168
186
  * This provides the same API as the existing getExportsDetails for backward compatibility
187
+ * Returns simple string arrays for exports (without moduleRequest info)
169
188
  */
170
189
  function getExportsDetails(code, filename = 'module.js') {
171
190
  const parseResult = (0, oxc_parser_1.parseSync)(filename, code, {
172
191
  sourceType: 'module',
173
192
  });
174
- return getExportsFromStaticExports(parseResult.module.staticExports);
193
+ const result = getExportsFromStaticExports(parseResult.module.staticExports);
194
+ // Return simple string array for backward compatibility
195
+ return {
196
+ exports: result.exports.map(exp => exp.name),
197
+ exportAllLocations: result.exportAllLocations,
198
+ };
175
199
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "package-build-stats",
3
- "version": "8.0.3",
3
+ "version": "8.1.0",
4
4
  "author": "Shubham Kanodia <shubham.kanodia10@gmail.com>",
5
5
  "repository": "https://github.com/pastelsky/package-build-stats",
6
6
  "publishConfig": {
@@ -35,9 +35,15 @@ export default function makeRspackConfig({
35
35
  mode: 'production',
36
36
  devtool: _debug ? 'source-map' : false,
37
37
  optimization: {
38
- runtimeChunk: { name: 'runtime' },
38
+ // Use 'multiple' to create a separate runtime chunk per entry point
39
+ // 'single' or { name: 'runtime' } shares one runtime across all entries,
40
+ // which breaks tree-shaking when building many exports simultaneously
41
+ runtimeChunk: 'multiple',
39
42
  realContentHash: false,
40
43
  minimize: minify,
44
+ // Enable tree-shaking optimizations
45
+ usedExports: true, // Mark unused exports for removal
46
+ sideEffects: true, // Respect package.json sideEffects field
41
47
  // Rspack automatically uses its built-in default minifiers:
42
48
  // - SwcJsMinimizerRspackPlugin for JS (SWC-based, very fast)
43
49
  // - LightningCssMinimizerRspackPlugin for CSS (Lightning CSS-based)
@@ -306,7 +306,14 @@ const BuildUtils = {
306
306
 
307
307
  const assetStatsPromises =
308
308
  jsonStats?.assets
309
- ?.filter(asset => !asset.chunkNames?.includes('runtime'))
309
+ ?.filter(
310
+ asset =>
311
+ !asset.chunkNames?.some(
312
+ name =>
313
+ name === 'runtime' ||
314
+ (typeof name === 'string' && name.startsWith('runtime~')),
315
+ ),
316
+ )
310
317
  .filter(asset => !asset.name.endsWith('LICENSE.txt'))
311
318
  .map(getAssetStats) || []
312
319
  const assetStats = await Promise.all(assetStatsPromises)
@@ -39,14 +39,22 @@ const resolver = new ResolverFactory({
39
39
  symlinks: false, // Don't resolve symlinks to match enhanced-resolve behavior
40
40
  })
41
41
 
42
+ /**
43
+ * Represents a named export with its optional source module
44
+ */
45
+ type NamedExport = {
46
+ name: string
47
+ moduleRequest?: string // The source module for re-exports like `export { foo } from './module'`
48
+ }
49
+
42
50
  /**
43
51
  * Extract export information from parsed oxc module
44
52
  */
45
53
  function getExportsFromStaticExports(staticExports: StaticExport[]): {
46
- exports: string[]
54
+ exports: NamedExport[]
47
55
  exportAllLocations: string[]
48
56
  } {
49
- const exports: string[] = []
57
+ const exports: NamedExport[] = []
50
58
  const exportAllLocations: string[] = []
51
59
 
52
60
  staticExports.forEach(staticExport => {
@@ -69,9 +77,15 @@ function getExportsFromStaticExports(staticExports: StaticExport[]): {
69
77
  case 'None': // export const foo = 1
70
78
  // Get the export name
71
79
  if (entry.exportName.kind === 'Name' && entry.exportName.name) {
72
- exports.push(entry.exportName.name)
80
+ exports.push({
81
+ name: entry.exportName.name,
82
+ moduleRequest: entry.moduleRequest?.value, // Track the source module for re-exports
83
+ })
73
84
  } else if (entry.exportName.kind === 'Default') {
74
- exports.push('default')
85
+ exports.push({
86
+ name: 'default',
87
+ moduleRequest: entry.moduleRequest?.value,
88
+ })
75
89
  }
76
90
  break
77
91
  }
@@ -138,16 +152,31 @@ async function walkExportsRecursive(
138
152
 
139
153
  const resolvedExports: ResolvedExports = {}
140
154
 
141
- // Add direct exports from this module
142
- exports.forEach(exp => {
143
- // Use path.relative() to calculate the relative path from root to resolvedPath
155
+ // Add direct exports from this module, resolving re-exports to their source files
156
+ for (const exp of exports) {
157
+ let sourcePath = resolvedPath
158
+
159
+ // If this is a re-export (export { foo } from './module'), resolve to the source file
160
+ if (exp.moduleRequest) {
161
+ try {
162
+ sourcePath = await resolveModule(
163
+ path.dirname(resolvedPath),
164
+ exp.moduleRequest,
165
+ )
166
+ } catch {
167
+ // If resolution fails, fall back to current file
168
+ sourcePath = resolvedPath
169
+ }
170
+ }
171
+
172
+ // Use path.relative() to calculate the relative path from root to sourcePath
144
173
  // This works correctly since symlinks are not resolved (symlinks: false in resolver config)
145
174
  const relativePath = root
146
- ? path.relative(root, resolvedPath)
147
- : path.basename(resolvedPath)
175
+ ? path.relative(root, sourcePath)
176
+ : path.basename(sourcePath)
148
177
 
149
- resolvedExports[exp] = relativePath
150
- })
178
+ resolvedExports[exp.name] = relativePath
179
+ }
151
180
 
152
181
  // Recursively process export * statements
153
182
  const promises = exportAllLocations.map(async location => {
@@ -214,11 +243,18 @@ export async function getAllExports(
214
243
  * Get exports details from code (compatibility function)
215
244
  *
216
245
  * This provides the same API as the existing getExportsDetails for backward compatibility
246
+ * Returns simple string arrays for exports (without moduleRequest info)
217
247
  */
218
248
  export function getExportsDetails(code: string, filename = 'module.js') {
219
249
  const parseResult = parseSync(filename, code, {
220
250
  sourceType: 'module',
221
251
  })
222
252
 
223
- return getExportsFromStaticExports(parseResult.module.staticExports)
253
+ const result = getExportsFromStaticExports(parseResult.module.staticExports)
254
+
255
+ // Return simple string array for backward compatibility
256
+ return {
257
+ exports: result.exports.map(exp => exp.name),
258
+ exportAllLocations: result.exportAllLocations,
259
+ }
224
260
  }