Skip to content

Commit c1143b0

Browse files
committed
feat: add extensions to options
1 parent 8badc73 commit c1143b0

15 files changed

+337
-209
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ with the following properties:
5656
- `open` (boolean, optional): Automatically open the analyzer in the browser. Defaults to `false`.
5757
- `getStartResponse` (function, optional): Callback function that receives the response object
5858
of the analyzer server.
59+
- `extensions` (Array<string>, optional): Which extensions should be included. By default
60+
`['.js', '.cjs', '.mjs', '.ts', '.tsx']`. It's not possible to analyze `.css` because
61+
`webpack-bundle-analyzer` filters output files by `/\.(js|mjs|cjs)$/iu`. But you may include smth.
62+
like `.json` if it is imported in some file.
5963

6064
### Example
6165

assets/cjs.svg

Lines changed: 1 addition & 1 deletion
Loading

assets/coverage.svg

Lines changed: 1 addition & 1 deletion
Loading

assets/esm.svg

Lines changed: 1 addition & 1 deletion
Loading

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"gen-size-badge": "node --import tsx ./scripts/genSizeBadges.ts"
3131
},
3232
"dependencies": {
33-
"webpack-bundle-analyzer": "4.9.1"
33+
"webpack-bundle-analyzer": "4.10.2"
3434
},
3535
"devDependencies": {
3636
"@types/node": "22.5.1",

pnpm-lock.yaml

Lines changed: 10 additions & 41 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/declarations.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ declare module 'webpack-bundle-analyzer' {
88
port: number;
99
host: string;
1010
openBrowser: boolean;
11+
reportTitle: string;
12+
entrypoints?: Array<string>;
1113
}
1214
) => Promise<TypeStartResponse>;
1315
}

src/getModules.ts

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/getStats.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import * as path from 'node:path';
2+
3+
import { Metafile } from 'esbuild';
4+
5+
import { TypeStats } from './types.js';
6+
7+
type TypeInputFilePath = string;
8+
type TypeOutputFilePath = string;
9+
10+
export const getStats = ({ inputs, outputs }: Metafile, extensionsSet: Set<string>): TypeStats => {
11+
const stats: TypeStats = {
12+
assets: [],
13+
modules: [],
14+
};
15+
16+
/**
17+
* Filter outputs so they have valid extensions and valid children
18+
*
19+
*/
20+
21+
const outputsFilteredEntries = Object.entries(outputs).filter(
22+
([outputFilePath, outputChunkData]) => {
23+
const outputFileExt = path.parse(outputFilePath).ext;
24+
25+
const hasValidChildren =
26+
Object.keys(outputChunkData.inputs).length > 0 &&
27+
Object.keys(outputChunkData.inputs).some((inputFilePath) => {
28+
const inputFileExt = path.parse(inputFilePath).ext;
29+
30+
return extensionsSet.has(inputFileExt);
31+
});
32+
33+
return extensionsSet.has(outputFileExt) && hasValidChildren;
34+
}
35+
);
36+
37+
/**
38+
* Visualization should be grouped by output files,
39+
* so first we need to connect each input file to the corresponding output files
40+
*
41+
* it's just an optimization of On3 complexity to On2
42+
*
43+
*/
44+
45+
const chunksIndexed: Record<TypeInputFilePath, Array<TypeOutputFilePath>> = {};
46+
const chunksSizesInOutput: Record<TypeInputFilePath, number> = {};
47+
48+
outputsFilteredEntries.forEach(([outputFilePath, outputChunkData]) => {
49+
Object.entries(outputChunkData.inputs).forEach(([inputFilePath, { bytesInOutput }]) => {
50+
chunksIndexed[inputFilePath] ??= [];
51+
chunksIndexed[inputFilePath].push(outputFilePath);
52+
53+
chunksSizesInOutput[inputFilePath] = bytesInOutput;
54+
});
55+
});
56+
57+
/**
58+
* Filter inputs so they have valid extensions and outputted in some file
59+
*
60+
*/
61+
62+
const inputsFilteredEntries = Object.entries(inputs).filter(([inputFilePath]) => {
63+
const inputFileExt = path.parse(inputFilePath).ext;
64+
65+
return extensionsSet.has(inputFileExt) && chunksIndexed[inputFilePath]?.length > 0;
66+
});
67+
68+
/**
69+
* Some package managers like to build a huge tree structure of folders like pnpm
70+
* ex. node_modules/.pnpm/[email protected][email protected]/node_modules/dk-mobx-restore-state
71+
*
72+
* so let's take the final part only
73+
*
74+
*/
75+
76+
stats.modules = inputsFilteredEntries.map(([inputFilePath, { bytes }]) => {
77+
const name = `./${inputFilePath.replace(/(.*)?node_modules/, 'node_modules')}`;
78+
79+
return {
80+
id: name,
81+
name,
82+
size: chunksSizesInOutput[inputFilePath] || bytes,
83+
chunks: chunksIndexed[inputFilePath],
84+
};
85+
});
86+
87+
stats.assets = outputsFilteredEntries.map(([outputFilePath]) => ({
88+
name: outputFilePath,
89+
chunks: [outputFilePath],
90+
}));
91+
92+
return stats;
93+
};

src/pluginWebpackAnalyzer.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
1-
import * as path from 'node:path';
2-
31
import { start } from 'webpack-bundle-analyzer';
42
import { Plugin } from 'esbuild';
53

6-
import { TypeOptions, TypeStats, TypeStartResponse } from './types.js';
4+
import { TypeOptions, TypeStartResponse } from './types.js';
75
import { pluginName } from './constants.js';
8-
import { getModules } from './getModules.js';
6+
import { getStats } from './getStats.js';
97
import { validateResult } from './validators/validateResult.js';
108
import { validateOptions } from './validators/validateOptions.js';
119
import { validateSetup } from './validators/validateSetup.js';
1210

1311
export const pluginWebpackAnalyzer = (options?: TypeOptions): Plugin => {
1412
validateOptions(options);
1513

14+
const finalOptions: TypeOptions = {
15+
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
16+
port: options?.port ?? 8888,
17+
host: options?.host ?? '127.0.0.1',
18+
open: options?.open || false,
19+
extensions: options?.extensions || ['.js', '.cjs', '.mjs', '.ts', '.tsx'],
20+
getStartResponse: options?.getStartResponse,
21+
};
22+
23+
const extensionsSet = new Set(finalOptions.extensions);
24+
1625
return {
1726
name: pluginName,
1827
setup(build) {
@@ -23,13 +32,7 @@ export const pluginWebpackAnalyzer = (options?: TypeOptions): Plugin => {
2332
build.onEnd((resultRaw) => {
2433
const result = validateResult(resultRaw);
2534

26-
const stats: TypeStats = {
27-
assets: Object.keys(result.metafile.outputs).map((chunkName) => ({
28-
name: chunkName,
29-
chunks: [path.parse(chunkName).name.split('.').shift()!],
30-
})),
31-
modules: getModules(result.metafile),
32-
};
35+
const stats = getStats(result.metafile, extensionsSet);
3336

3437
if (response?.updateChartData) {
3538
response.updateChartData(stats);
@@ -40,14 +43,14 @@ export const pluginWebpackAnalyzer = (options?: TypeOptions): Plugin => {
4043
// https://github.com/webpack-contrib/webpack-bundle-analyzer
4144
return start(stats, {
4245
analyzerUrl: (params) => `http://${params.listenHost}:${params.boundAddress.port}`,
43-
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
44-
port: options?.port ?? 8888,
45-
host: options?.host ?? '127.0.0.1',
46-
openBrowser: options?.open || false,
46+
port: finalOptions.port!,
47+
host: finalOptions.host!,
48+
openBrowser: finalOptions.open!,
49+
reportTitle: 'Analyzer',
4750
}).then((res) => {
4851
response = res;
4952

50-
options?.getStartResponse?.(res);
53+
finalOptions?.getStartResponse?.(res);
5154
});
5255
});
5356

0 commit comments

Comments
 (0)