@taro-minify-pack/plugin-bundle-stats 0.1.4-alpha.3

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/.eslintrc.js ADDED
@@ -0,0 +1,21 @@
1
+ module.exports = {
2
+ env: {
3
+ node: true,
4
+ es6: true
5
+ },
6
+ extends: [
7
+ 'standard',
8
+ 'plugin:@typescript-eslint/recommended'
9
+ ],
10
+ parser: '@typescript-eslint/parser',
11
+ plugins: ['@typescript-eslint'],
12
+ rules: {
13
+ '@typescript-eslint/no-empty-function': 'off',
14
+ '@typescript-eslint/no-explicit-any': 'off',
15
+ '@typescript-eslint/no-var-requires': 'off',
16
+ '@typescript-eslint/no-non-null-assertion': 'off',
17
+ '@typescript-eslint/ban-ts-comment': 'off',
18
+ '@typescript-eslint/no-unused-vars': 'off',
19
+ 'no-use-before-define': 'off'
20
+ }
21
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 yu.pan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @taro-minify-pack/plugin-bundle-stats
2
+
3
+ > 集成 bundle-stats-webpack-plugin 到 Taro 项目,可视化分析打包结果,帮助优化代码体积和依赖关系。
@@ -0,0 +1,5 @@
1
+ import type { IPluginContext } from '@tarojs/service';
2
+ import { BundleStatsOpt } from './types';
3
+ export type { BundleStatsOpt } from './types';
4
+ declare const _default: (ctx: IPluginContext, pluginOpts?: Partial<BundleStatsOpt>) => void;
5
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ var __importDefault = (this && this.__importDefault) || function (mod) {
23
+ return (mod && mod.__esModule) ? mod : { "default": mod };
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ const bundle_stats_webpack_plugin_1 = require("bundle-stats-webpack-plugin");
27
+ const got_1 = __importDefault(require("got"));
28
+ const path_1 = __importDefault(require("path"));
29
+ const fs_1 = __importDefault(require("fs"));
30
+ const promises_1 = require("stream/promises");
31
+ const summarize_report_1 = require("./summarize-report");
32
+ const helper_1 = require("@taro-minify-pack/helper");
33
+ const bundleStatsDefaultOpt = {
34
+ reportPath: 'bundleStatsReport',
35
+ baselinePath: 'baseline.json'
36
+ };
37
+ exports.default = (ctx, pluginOpts = {}) => {
38
+ const finalOpts = Object.assign(Object.assign({}, bundleStatsDefaultOpt), pluginOpts);
39
+ const { reportPath, baselinePath, upload } = finalOpts, rest = __rest(finalOpts, ["reportPath", "baselinePath", "upload"]);
40
+ ctx.modifyWebpackChain(({ chain }) => {
41
+ const { appPath, outputPath } = ctx.paths;
42
+ const outDir = path_1.default.relative(outputPath, path_1.default.resolve(appPath, reportPath));
43
+ const baselineFilepath = path_1.default.resolve(appPath, reportPath, 'baseline.json');
44
+ const config = Object.assign(Object.assign({}, rest), { baselineFilepath, outDir });
45
+ chain.plugin('bundle-stats').use(bundle_stats_webpack_plugin_1.BundleStatsWebpackPlugin, [config]);
46
+ });
47
+ ctx.onBuildStart(() => __awaiter(void 0, void 0, void 0, function* () {
48
+ const { appPath } = ctx.paths;
49
+ const reportDirPath = path_1.default.resolve(appPath, reportPath);
50
+ const reportBaselineFilepath = path_1.default.resolve(reportDirPath, 'baseline.json');
51
+ if (!baselinePath || baselinePath === reportBaselineFilepath)
52
+ return;
53
+ if (!fs_1.default.existsSync(reportDirPath))
54
+ fs_1.default.mkdirSync(reportDirPath, { recursive: true });
55
+ if (!/https?:\/\//.test(baselinePath))
56
+ return fs_1.default.copyFileSync(baselinePath, reportBaselineFilepath);
57
+ const stream = got_1.default.stream(baselinePath);
58
+ yield (0, promises_1.pipeline)(stream, fs_1.default.createWriteStream(reportBaselineFilepath));
59
+ console.log(`✅ load baseline.json success. ${reportBaselineFilepath}`);
60
+ }));
61
+ ctx.onBuildComplete(() => {
62
+ const { appPath, outputPath } = ctx.paths;
63
+ const appConfigPath = path_1.default.join(outputPath, 'app.json');
64
+ const appConfig = JSON.parse(fs_1.default.readFileSync(appConfigPath, 'utf8'));
65
+ const bundleStatsReportPath = path_1.default.resolve(appPath, reportPath, 'bundle-stats.json');
66
+ const bundleStatsReport = JSON.parse(fs_1.default.readFileSync(bundleStatsReportPath, 'utf8'));
67
+ const reportStr = (0, summarize_report_1.summarizeReport)({ appConfig, bundleStatsReport });
68
+ const summaryReportPath = path_1.default.resolve(appPath, reportPath, 'summary-report.md');
69
+ fs_1.default.writeFileSync(summaryReportPath, reportStr);
70
+ console.log('✅ generate summary-report.md success.');
71
+ });
72
+ ctx.registerCommand({
73
+ name: 'upload-bundle-stats-report',
74
+ fn() {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ const { appPath } = ctx.paths;
77
+ const assetsDirPath = path_1.default.resolve(appPath, reportPath);
78
+ yield (0, helper_1.uploadAssets)({ assetsDirPath, upload });
79
+ console.log('✅ upload bundle-stats report success.');
80
+ });
81
+ }
82
+ });
83
+ };
84
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { SummarizeReportOpt } from './types';
2
+ export declare const summarizeReport: (opt: SummarizeReportOpt) => string;
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.summarizeReport = void 0;
7
+ const handlebars_1 = __importDefault(require("handlebars"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const dayjs_1 = __importDefault(require("dayjs"));
11
+ /**
12
+ * 格式化路径
13
+ */
14
+ const normalizePath = (filePath) => filePath.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/$/, '');
15
+ /**
16
+ * 获取这次构建的指标
17
+ * @param metric
18
+ */
19
+ const getCurMetricRunInfo = (metric) => {
20
+ const [currentMetricRun] = (metric === null || metric === void 0 ? void 0 : metric.runs) || [];
21
+ return currentMetricRun || {};
22
+ };
23
+ /**
24
+ * 获取这次构建的指标
25
+ * @param metric
26
+ */
27
+ const getBaselineMetricRunInfo = (metric) => {
28
+ const [_, baselineMetricRun] = (metric === null || metric === void 0 ? void 0 : metric.runs) || [];
29
+ return baselineMetricRun || {};
30
+ };
31
+ /**
32
+ * 格式化
33
+ */
34
+ const formatMetric = (metricList) => {
35
+ return metricList.map(metric => {
36
+ const [current, baseline] = (metric === null || metric === void 0 ? void 0 : metric.runs) || [];
37
+ const { displayDelta, displayDeltaPercentage } = current || {};
38
+ const displayLabel = normalizePath(metric.label);
39
+ return Object.assign(Object.assign({}, metric), { displayLabel, displayDelta, displayDeltaPercentage, current, baseline });
40
+ });
41
+ };
42
+ const formatSize = (size) => {
43
+ if (Math.abs(size) < 1024)
44
+ return `${size}B`;
45
+ if (Math.abs(size) < 1024 * 1024)
46
+ return `${(size / 1024).toFixed(2)}KB`;
47
+ return `${(size / 1024 / 1024).toFixed(2)}MB`;
48
+ };
49
+ const formatSymbol = (val) => {
50
+ if (val > 0)
51
+ return '+';
52
+ if (val < 0)
53
+ return '-';
54
+ return '';
55
+ };
56
+ /**
57
+ * 统计
58
+ */
59
+ const summaryMetric = (metric) => {
60
+ const currentTotalSize = metric.reduce((result, item) => getCurMetricRunInfo(item).value + result, 0);
61
+ const baselineTotalSize = metric.reduce((result, item) => getBaselineMetricRunInfo(item).value + result, 0);
62
+ const sizeDelta = currentTotalSize - baselineTotalSize;
63
+ const displaySizeDelta = `${formatSymbol(sizeDelta)}${formatSize(sizeDelta)}`;
64
+ const displayCurrentTotalSize = formatSize(currentTotalSize);
65
+ const displayBaselineTotalSize = formatSize(baselineTotalSize);
66
+ return { currentTotalSize, baselineTotalSize, sizeDelta, displayCurrentTotalSize, displayBaselineTotalSize, displaySizeDelta };
67
+ };
68
+ const statisticMetric = (metric) => {
69
+ const initVal = { currentCount: 0, baselineCount: 0 };
70
+ const { currentCount, baselineCount } = metric.reduce((result, item) => {
71
+ const curMetricRun = getCurMetricRunInfo(item);
72
+ const baselineMetricRun = getBaselineMetricRunInfo(item);
73
+ const currentCount = curMetricRun.value ? result.currentCount + 1 : result.currentCount;
74
+ const baselineCount = baselineMetricRun.value ? result.baselineCount + 1 : result.baselineCount;
75
+ return { currentCount, baselineCount };
76
+ }, initVal);
77
+ const countDelta = currentCount - baselineCount;
78
+ return { currentCount, baselineCount, countDelta };
79
+ };
80
+ const summarizeReport = (opt) => {
81
+ const { appConfig, bundleStatsReport, focusMetricRun, displayLimit = 20 } = opt;
82
+ const { subpackages, subPackages } = appConfig;
83
+ const subPackageRoots = (subPackages || subpackages || []).map((item) => item.root);
84
+ // 主包资产
85
+ const mainPackageAssets = bundleStatsReport.assets.filter(asset => {
86
+ const { name } = getCurMetricRunInfo(asset);
87
+ const assetName = normalizePath(name || asset.key);
88
+ return !subPackageRoots.some(subPackageRoots => assetName.startsWith(subPackageRoots));
89
+ });
90
+ // 主包资产的 chunkId 列表
91
+ const mainPackageAssetChunkIds = mainPackageAssets.map(asset => getCurMetricRunInfo(asset).chunkId);
92
+ // 主包模块
93
+ const mainPackageModules = bundleStatsReport.modules.filter(module => {
94
+ const { chunkIds } = getCurMetricRunInfo(module);
95
+ return mainPackageAssetChunkIds.some((chunkId) => chunkIds === null || chunkIds === void 0 ? void 0 : chunkIds.includes(chunkId));
96
+ });
97
+ // 额外关注的资产
98
+ const focusAssets = bundleStatsReport.assets.filter(asset => {
99
+ var _a;
100
+ const { name } = getCurMetricRunInfo(asset);
101
+ const assetName = normalizePath(name || asset.key);
102
+ return (_a = focusMetricRun === null || focusMetricRun === void 0 ? void 0 : focusMetricRun.assets) === null || _a === void 0 ? void 0 : _a.includes(assetName);
103
+ });
104
+ // 额外关注的资产的 chunkId 列表
105
+ const focusAssetChunkIds = focusAssets.map(asset => getCurMetricRunInfo(asset).chunkId);
106
+ // 额外关注的模块
107
+ const focusModules = bundleStatsReport.modules.filter(module => {
108
+ var _a;
109
+ const { name, chunkIds } = getCurMetricRunInfo(module);
110
+ const isInFocusAssets = focusAssetChunkIds.some((chunkId) => chunkIds === null || chunkIds === void 0 ? void 0 : chunkIds.includes(chunkId));
111
+ return isInFocusAssets || ((_a = focusMetricRun === null || focusMetricRun === void 0 ? void 0 : focusMetricRun.modules) === null || _a === void 0 ? void 0 : _a.includes(name || module.key));
112
+ });
113
+ // 额外关注的包
114
+ const focusPackages = bundleStatsReport.packages.filter(dependPackage => {
115
+ var _a;
116
+ const { name } = getCurMetricRunInfo(dependPackage);
117
+ return (_a = focusMetricRun === null || focusMetricRun === void 0 ? void 0 : focusMetricRun.packages) === null || _a === void 0 ? void 0 : _a.includes(name || dependPackage.key);
118
+ });
119
+ // 概览
120
+ const mainPackageSummary = (() => {
121
+ const summary = summaryMetric(mainPackageAssets);
122
+ const { currentCount: currentAssetCount, baselineCount: baselineAssetCount, countDelta: assetCountDelta } = statisticMetric(mainPackageAssets);
123
+ const { currentCount: currentModuleCount, baselineCount: baselineModuleCount, countDelta: moduleCountDelta } = statisticMetric(mainPackageModules);
124
+ return Object.assign(Object.assign({}, summary), { currentAssetCount, currentModuleCount, baselineAssetCount, baselineModuleCount, assetCountDelta, moduleCountDelta });
125
+ })();
126
+ const mainPackageChangedModules = (() => {
127
+ const tempArr = mainPackageModules.filter(item => item.changed);
128
+ return tempArr.sort((a, b) => { var _a, _b; return ((_a = getCurMetricRunInfo(b).delta) !== null && _a !== void 0 ? _a : 0) - ((_b = getCurMetricRunInfo(a).delta) !== null && _b !== void 0 ? _b : 0); });
129
+ })();
130
+ const mainPackageBusinessModules = (() => {
131
+ const tempArr = mainPackageModules.filter(item => !/node_modules/.test(item.key));
132
+ return tempArr.sort((a, b) => { var _a, _b; return ((_a = getCurMetricRunInfo(b).value) !== null && _a !== void 0 ? _a : 0) - ((_b = getCurMetricRunInfo(a).value) !== null && _b !== void 0 ? _b : 0); });
133
+ })();
134
+ const mainPackageDependModules = (() => {
135
+ const tempArr = mainPackageModules.filter(item => /node_modules/.test(item.key));
136
+ return tempArr.sort((a, b) => { var _a, _b; return ((_a = getCurMetricRunInfo(b).value) !== null && _a !== void 0 ? _a : 0) - ((_b = getCurMetricRunInfo(a).value) !== null && _b !== void 0 ? _b : 0); });
137
+ })();
138
+ const template = fs_1.default.readFileSync(path_1.default.join(__dirname, './summary-report-template.hbs'), 'utf-8');
139
+ return handlebars_1.default.compile(template)({
140
+ mainPackageSummary,
141
+ buildTime: (0, dayjs_1.default)().format('YYYY-MM-DD HH:mm:ss'),
142
+ mainPackageAssets: formatMetric(mainPackageAssets),
143
+ mainPackageChangedModules: formatMetric(mainPackageChangedModules),
144
+ mainPackageBusinessModules: formatMetric(mainPackageBusinessModules).slice(0, displayLimit),
145
+ mainPackageDependModules: formatMetric(mainPackageDependModules).slice(0, displayLimit),
146
+ focusAssets: formatMetric(focusAssets),
147
+ focusModules: formatMetric(focusModules),
148
+ focusPackages: formatMetric(focusPackages)
149
+ });
150
+ };
151
+ exports.summarizeReport = summarizeReport;
152
+ //# sourceMappingURL=summarize-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summarize-report.js","sourceRoot":"","sources":["../src/summarize-report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ # 📦 Bundle Stats Summary
2
+ > Build Time: {{buildTime}}
3
+
4
+ ## 主包整体变化
5
+ | 指标 | Current | Baseline | Delta |
6
+ |------|--------:|--------:|------:|
7
+ | 总体体积 | {{mainPackageSummary.displayCurrentTotalSize}} | {{mainPackageSummary.displayBaselineTotalSize}} | {{mainPackageSummary.displaySizeDelta}} |
8
+ | 模块数量 | {{mainPackageSummary.currentModuleCount}} | {{mainPackageSummary.baselineModuleCount}} | {{mainPackageSummary.moduleCountDelta}} |
9
+ | 资源数量 | {{mainPackageSummary.currentAssetCount}} | {{mainPackageSummary.baselineAssetCount}} | {{mainPackageSummary.assetCountDelta}} |
10
+
11
+ ## 主包资源分析
12
+ | Asset | Current | Baseline | Delta | Ratio|
13
+ |----------|------:|------:|------:|------:|
14
+ {{#each mainPackageAssets}}
15
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
16
+ {{/each}}
17
+
18
+ ## 主包模块分析
19
+
20
+ {{#if mainPackageChangedModules.length}}
21
+ ### 主包变化模块分析
22
+ | Module | Current | Baseline | Delta | Ratio|
23
+ |----------|------:|------:|------:|------:|
24
+ {{#each mainPackageChangedModules}}
25
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
26
+ {{/each}}
27
+ {{/if}}
28
+
29
+ ### 主包业务模块分析
30
+ | Module | Current | Baseline | Delta | Ratio|
31
+ |----------|------:|------:|------:|------:|
32
+ {{#each mainPackageBusinessModules}}
33
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
34
+ {{/each}}
35
+
36
+ ### 主包依赖模块分析
37
+ | Module | Current | Baseline | Delta | Ratio|
38
+ |----------|------:|------:|------:|------:|
39
+ {{#each mainPackageDependModules}}
40
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
41
+ {{/each}}
42
+
43
+ {{#if focusAssets.length}}
44
+ ## 重点资源分析
45
+ | Asset | Current | Baseline | Delta | Ratio|
46
+ |----------|------:|------:|------:|------:|
47
+ {{#each focusAssets}}
48
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
49
+ {{/each}}
50
+ {{/if}}
51
+
52
+ {{#if focusModules.length}}
53
+ ## 重点模块分析
54
+ | Module | Current | Baseline | Delta | Ratio|
55
+ |----------|------:|------:|------:|------:|
56
+ {{#each focusModules}}
57
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
58
+ {{/each}}
59
+ {{/if}}
60
+
61
+ {{#if focusPackages.length}}
62
+ ## 重点依赖分析
63
+ | Package | Current | Baseline | Delta | Ratio|
64
+ |----------|------:|------:|------:|------:|
65
+ {{#each focusPackages}}
66
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
67
+ {{/each}}
68
+ {{/if}}
@@ -0,0 +1,88 @@
1
+ import { Uploader } from '@taro-minify-pack/helper';
2
+ import { AppConfig } from '@tarojs/taro';
3
+ interface WebpackStatsOptions {
4
+ /**
5
+ * Output webpack assets information
6
+ * Default: `true`.
7
+ */
8
+ assets?: boolean;
9
+ /**
10
+ * Output webpack chunks information
11
+ * Default: `true`.
12
+ */
13
+ chunks?: boolean;
14
+ /**
15
+ * Output webpack modules information
16
+ * Default: `true`.
17
+ */
18
+ modules?: boolean;
19
+ /**
20
+ * Output webpack hash information
21
+ * Default: `true`.
22
+ */
23
+ hash?: boolean;
24
+ /**
25
+ * Output webpack builtAt information
26
+ * Default: `true`.
27
+ */
28
+ builtAt?: boolean;
29
+ }
30
+ export interface BundleStatsOpt {
31
+ html?: boolean;
32
+ json?: boolean;
33
+ silent?: boolean;
34
+ compare?: boolean;
35
+ baseline?: boolean;
36
+ baselinePath?: string;
37
+ reportPath: string;
38
+ upload?: Uploader;
39
+ stats?: Partial<WebpackStatsOptions>;
40
+ reportConfig?: Omit<SummarizeReportOpt, 'appConfig' | 'bundleStatsReport'>;
41
+ }
42
+ export interface BaseMetricRun {
43
+ value: number;
44
+ displayValue: string;
45
+ delta?: number;
46
+ deltaPercentage?: number;
47
+ displayDelta?: string;
48
+ displayDeltaPercentage?: string;
49
+ deltaType?: string;
50
+ regression?: boolean;
51
+ }
52
+ export interface AssetMetricRun extends BaseMetricRun {
53
+ name: string;
54
+ isChunk: boolean;
55
+ chunkId: string;
56
+ }
57
+ export interface ModuleMetricRun extends BaseMetricRun {
58
+ name: string;
59
+ chunkIds: string[];
60
+ }
61
+ export interface PackageMetricRun extends BaseMetricRun {
62
+ name: string;
63
+ }
64
+ export interface Metric<T extends BaseMetricRun> {
65
+ key: string;
66
+ label: string;
67
+ biggerIsBetter: boolean;
68
+ changed: boolean;
69
+ runs: T[];
70
+ }
71
+ export interface BundleStatsReport {
72
+ stats: Metric<BaseMetricRun>[];
73
+ assets: Metric<AssetMetricRun>[];
74
+ modules: Metric<ModuleMetricRun>[];
75
+ packages: Metric<PackageMetricRun>[];
76
+ }
77
+ export interface FocusMetricRun {
78
+ assets: string[];
79
+ modules: string;
80
+ packages: string[];
81
+ }
82
+ export interface SummarizeReportOpt {
83
+ appConfig: AppConfig;
84
+ bundleStatsReport: BundleStatsReport;
85
+ focusMetricRun?: Partial<FocusMetricRun>;
86
+ displayLimit?: number;
87
+ }
88
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@taro-minify-pack/plugin-bundle-stats",
3
+ "version": "0.1.4-alpha.3",
4
+ "description": "",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "homepage": "https://github.com/panyu97py/taro-minify-pack",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/panyu97py/taro-minify-pack.git"
14
+ },
15
+ "author": "yu.pan",
16
+ "license": "MIT",
17
+ "dependencies": {
18
+ "bundle-stats-webpack-plugin": "^4.22.1",
19
+ "dayjs": "^1.11.21",
20
+ "got": "^11.8.6",
21
+ "handlebars": "^4.7.9",
22
+ "@taro-minify-pack/helper": "0.1.4-alpha.3"
23
+ },
24
+ "devDependencies": {
25
+ "@tarojs/service": "^4.0.8",
26
+ "@tarojs/taro": "^4.0.8",
27
+ "@types/webpack-bundle-analyzer": "^4.7.0",
28
+ "@typescript-eslint/eslint-plugin": "^8.26.1",
29
+ "@typescript-eslint/parser": "^8.26.1",
30
+ "cpx": "^1.5.0",
31
+ "eslint": "^8.19.0",
32
+ "eslint-config-standard": "^17.0.0",
33
+ "eslint-plugin-import": "^2.26.0",
34
+ "eslint-plugin-n": "^15.2.3",
35
+ "eslint-plugin-promise": "^6.0.0",
36
+ "typescript": "^5.8.2"
37
+ },
38
+ "scripts": {
39
+ "build": "tsc && cpx \"src/**/*.hbs\" dist"
40
+ }
41
+ }
package/src/index.ts ADDED
@@ -0,0 +1,63 @@
1
+ import type { IPluginContext } from '@tarojs/service'
2
+ import { BundleStatsWebpackPlugin } from 'bundle-stats-webpack-plugin'
3
+ import { BundleStatsOpt, BundleStatsReport } from './types'
4
+ import got from 'got'
5
+ import path from 'path'
6
+ import fs from 'fs'
7
+ import { pipeline } from 'stream/promises'
8
+ import { summarizeReport } from './summarize-report'
9
+ import { uploadAssets } from '@taro-minify-pack/helper'
10
+ import { AppConfig } from '@tarojs/taro'
11
+
12
+ export type { BundleStatsOpt } from './types'
13
+
14
+ const bundleStatsDefaultOpt: BundleStatsOpt = {
15
+ reportPath: 'bundleStatsReport',
16
+ baselinePath: 'baseline.json'
17
+ }
18
+ export default (ctx: IPluginContext, pluginOpts: Partial<BundleStatsOpt> = {}) => {
19
+ const finalOpts: BundleStatsOpt = { ...bundleStatsDefaultOpt, ...pluginOpts }
20
+ const { reportPath, baselinePath, upload, ...rest } = finalOpts
21
+
22
+ ctx.modifyWebpackChain(({ chain }) => {
23
+ const { appPath, outputPath } = ctx.paths
24
+ const outDir = path.relative(outputPath, path.resolve(appPath, reportPath))
25
+ const baselineFilepath = path.resolve(appPath, reportPath, 'baseline.json')
26
+ const config: BundleStatsWebpackPlugin.Options = { ...rest, baselineFilepath, outDir }
27
+ chain.plugin('bundle-stats').use(BundleStatsWebpackPlugin, [config])
28
+ })
29
+
30
+ ctx.onBuildStart(async () => {
31
+ const { appPath } = ctx.paths
32
+ const reportDirPath = path.resolve(appPath, reportPath)
33
+ const reportBaselineFilepath = path.resolve(reportDirPath, 'baseline.json')
34
+ if (!baselinePath || baselinePath === reportBaselineFilepath) return
35
+ if (!fs.existsSync(reportDirPath)) fs.mkdirSync(reportDirPath, { recursive: true })
36
+ if (!/https?:\/\//.test(baselinePath)) return fs.copyFileSync(baselinePath, reportBaselineFilepath)
37
+ const stream = got.stream(baselinePath)
38
+ await pipeline(stream, fs.createWriteStream(reportBaselineFilepath))
39
+ console.log(`✅ load baseline.json success. ${reportBaselineFilepath}`)
40
+ })
41
+
42
+ ctx.onBuildComplete(() => {
43
+ const { appPath, outputPath } = ctx.paths
44
+ const appConfigPath = path.join(outputPath, 'app.json')
45
+ const appConfig: AppConfig = JSON.parse(fs.readFileSync(appConfigPath, 'utf8'))
46
+ const bundleStatsReportPath = path.resolve(appPath, reportPath, 'bundle-stats.json')
47
+ const bundleStatsReport: BundleStatsReport = JSON.parse(fs.readFileSync(bundleStatsReportPath, 'utf8'))
48
+ const reportStr = summarizeReport({ appConfig, bundleStatsReport })
49
+ const summaryReportPath = path.resolve(appPath, reportPath, 'summary-report.md')
50
+ fs.writeFileSync(summaryReportPath, reportStr)
51
+ console.log('✅ generate summary-report.md success.')
52
+ })
53
+
54
+ ctx.registerCommand({
55
+ name: 'upload-bundle-stats-report',
56
+ async fn () {
57
+ const { appPath } = ctx.paths
58
+ const assetsDirPath = path.resolve(appPath, reportPath)
59
+ await uploadAssets({ assetsDirPath, upload })
60
+ console.log('✅ upload bundle-stats report success.')
61
+ }
62
+ })
63
+ }
@@ -0,0 +1,161 @@
1
+ import Handlebars from 'handlebars'
2
+ import { BaseMetricRun, Metric, SummarizeReportOpt } from './types'
3
+ import fs from 'fs'
4
+ import path from 'path'
5
+ import dayjs from 'dayjs'
6
+
7
+ /**
8
+ * 格式化路径
9
+ */
10
+ const normalizePath = (filePath: string) => filePath.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/$/, '')
11
+
12
+ /**
13
+ * 获取这次构建的指标
14
+ * @param metric
15
+ */
16
+ const getCurMetricRunInfo = <T extends BaseMetricRun> (metric?: Metric<T>) => {
17
+ const [currentMetricRun] = metric?.runs || []
18
+ return currentMetricRun || {}
19
+ }
20
+
21
+ /**
22
+ * 获取这次构建的指标
23
+ * @param metric
24
+ */
25
+ const getBaselineMetricRunInfo = <T extends BaseMetricRun> (metric?: Metric<T>) => {
26
+ const [_, baselineMetricRun] = metric?.runs || []
27
+ return baselineMetricRun || {}
28
+ }
29
+
30
+ /**
31
+ * 格式化
32
+ */
33
+ const formatMetric = <T extends BaseMetricRun> (metricList: Metric<T>[]) => {
34
+ return metricList.map(metric => {
35
+ const [current, baseline] = metric?.runs || []
36
+ const { displayDelta, displayDeltaPercentage } = current || {}
37
+ const displayLabel = normalizePath(metric.label)
38
+ return { ...metric, displayLabel, displayDelta, displayDeltaPercentage, current, baseline }
39
+ })
40
+ }
41
+
42
+ const formatSize = (size: number) => {
43
+ if (Math.abs(size) < 1024) return `${size}B`
44
+ if (Math.abs(size) < 1024 * 1024) return `${(size / 1024).toFixed(2)}KB`
45
+ return `${(size / 1024 / 1024).toFixed(2)}MB`
46
+ }
47
+
48
+ const formatSymbol = (val: number) => {
49
+ if (val > 0) return '+'
50
+ if (val < 0) return '-'
51
+ return ''
52
+ }
53
+
54
+ /**
55
+ * 统计
56
+ */
57
+ const summaryMetric = <T extends BaseMetricRun> (metric: Metric<T>[]) => {
58
+ const currentTotalSize = metric.reduce((result, item) => getCurMetricRunInfo(item).value + result, 0)
59
+ const baselineTotalSize = metric.reduce((result, item) => getBaselineMetricRunInfo(item).value + result, 0)
60
+ const sizeDelta = currentTotalSize - baselineTotalSize
61
+ const displaySizeDelta = `${formatSymbol(sizeDelta)}${formatSize(sizeDelta)}`
62
+ const displayCurrentTotalSize = formatSize(currentTotalSize)
63
+ const displayBaselineTotalSize = formatSize(baselineTotalSize)
64
+ return { currentTotalSize, baselineTotalSize, sizeDelta, displayCurrentTotalSize, displayBaselineTotalSize, displaySizeDelta }
65
+ }
66
+
67
+ const statisticMetric = <T extends BaseMetricRun> (metric: Metric<T>[]) => {
68
+ const initVal = { currentCount: 0, baselineCount: 0 }
69
+ const { currentCount, baselineCount } = metric.reduce((result, item) => {
70
+ const curMetricRun = getCurMetricRunInfo(item)
71
+ const baselineMetricRun = getBaselineMetricRunInfo(item)
72
+ const currentCount = curMetricRun.value ? result.currentCount + 1 : result.currentCount
73
+ const baselineCount = baselineMetricRun.value ? result.baselineCount + 1 : result.baselineCount
74
+ return { currentCount, baselineCount }
75
+ }, initVal)
76
+ const countDelta = currentCount - baselineCount
77
+ return { currentCount, baselineCount, countDelta }
78
+ }
79
+
80
+ export const summarizeReport = (opt: SummarizeReportOpt) => {
81
+ const { appConfig, bundleStatsReport, focusMetricRun, displayLimit = 20 } = opt
82
+
83
+ const { subpackages, subPackages } = appConfig
84
+ const subPackageRoots = (subPackages || subpackages || []).map((item) => item.root)
85
+
86
+ // 主包资产
87
+ const mainPackageAssets = bundleStatsReport.assets.filter(asset => {
88
+ const { name } = getCurMetricRunInfo(asset)
89
+ const assetName = normalizePath(name || asset.key)
90
+ return !subPackageRoots.some(subPackageRoots => assetName.startsWith(subPackageRoots))
91
+ })
92
+
93
+ // 主包资产的 chunkId 列表
94
+ const mainPackageAssetChunkIds = mainPackageAssets.map(asset => getCurMetricRunInfo(asset).chunkId)
95
+
96
+ // 主包模块
97
+ const mainPackageModules = bundleStatsReport.modules.filter(module => {
98
+ const { chunkIds } = getCurMetricRunInfo(module)
99
+ return mainPackageAssetChunkIds.some((chunkId) => chunkIds?.includes(chunkId))
100
+ })
101
+
102
+ // 额外关注的资产
103
+ const focusAssets = bundleStatsReport.assets.filter(asset => {
104
+ const { name } = getCurMetricRunInfo(asset)
105
+ const assetName = normalizePath(name || asset.key)
106
+ return focusMetricRun?.assets?.includes(assetName)
107
+ })
108
+
109
+ // 额外关注的资产的 chunkId 列表
110
+ const focusAssetChunkIds = focusAssets.map(asset => getCurMetricRunInfo(asset).chunkId)
111
+
112
+ // 额外关注的模块
113
+ const focusModules = bundleStatsReport.modules.filter(module => {
114
+ const { name, chunkIds } = getCurMetricRunInfo(module)
115
+ const isInFocusAssets = focusAssetChunkIds.some((chunkId) => chunkIds?.includes(chunkId))
116
+ return isInFocusAssets || focusMetricRun?.modules?.includes(name || module.key)
117
+ })
118
+
119
+ // 额外关注的包
120
+ const focusPackages = bundleStatsReport.packages.filter(dependPackage => {
121
+ const { name } = getCurMetricRunInfo(dependPackage)
122
+ return focusMetricRun?.packages?.includes(name || dependPackage.key)
123
+ })
124
+
125
+ // 概览
126
+ const mainPackageSummary = (() => {
127
+ const summary = summaryMetric(mainPackageAssets)
128
+ const { currentCount: currentAssetCount, baselineCount: baselineAssetCount, countDelta: assetCountDelta } = statisticMetric(mainPackageAssets)
129
+ const { currentCount: currentModuleCount, baselineCount: baselineModuleCount, countDelta: moduleCountDelta } = statisticMetric(mainPackageModules)
130
+ return { ...summary, currentAssetCount, currentModuleCount, baselineAssetCount, baselineModuleCount, assetCountDelta, moduleCountDelta }
131
+ })()
132
+
133
+ const mainPackageChangedModules = (() => {
134
+ const tempArr = mainPackageModules.filter(item => item.changed)
135
+ return tempArr.sort((a, b) => (getCurMetricRunInfo(b).delta ?? 0) - (getCurMetricRunInfo(a).delta ?? 0))
136
+ })()
137
+
138
+ const mainPackageBusinessModules = (() => {
139
+ const tempArr = mainPackageModules.filter(item => !/node_modules/.test(item.key))
140
+ return tempArr.sort((a, b) => (getCurMetricRunInfo(b).value ?? 0) - (getCurMetricRunInfo(a).value ?? 0))
141
+ })()
142
+
143
+ const mainPackageDependModules = (() => {
144
+ const tempArr = mainPackageModules.filter(item => /node_modules/.test(item.key))
145
+ return tempArr.sort((a, b) => (getCurMetricRunInfo(b).value ?? 0) - (getCurMetricRunInfo(a).value ?? 0))
146
+ })()
147
+
148
+ const template = fs.readFileSync(path.join(__dirname, './summary-report-template.hbs'), 'utf-8')
149
+
150
+ return Handlebars.compile(template)({
151
+ mainPackageSummary,
152
+ buildTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
153
+ mainPackageAssets: formatMetric(mainPackageAssets),
154
+ mainPackageChangedModules: formatMetric(mainPackageChangedModules),
155
+ mainPackageBusinessModules: formatMetric(mainPackageBusinessModules).slice(0, displayLimit),
156
+ mainPackageDependModules: formatMetric(mainPackageDependModules).slice(0, displayLimit),
157
+ focusAssets: formatMetric(focusAssets),
158
+ focusModules: formatMetric(focusModules),
159
+ focusPackages: formatMetric(focusPackages)
160
+ })
161
+ }
@@ -0,0 +1,68 @@
1
+ # 📦 Bundle Stats Summary
2
+ > Build Time: {{buildTime}}
3
+
4
+ ## 主包整体变化
5
+ | 指标 | Current | Baseline | Delta |
6
+ |------|--------:|--------:|------:|
7
+ | 总体体积 | {{mainPackageSummary.displayCurrentTotalSize}} | {{mainPackageSummary.displayBaselineTotalSize}} | {{mainPackageSummary.displaySizeDelta}} |
8
+ | 模块数量 | {{mainPackageSummary.currentModuleCount}} | {{mainPackageSummary.baselineModuleCount}} | {{mainPackageSummary.moduleCountDelta}} |
9
+ | 资源数量 | {{mainPackageSummary.currentAssetCount}} | {{mainPackageSummary.baselineAssetCount}} | {{mainPackageSummary.assetCountDelta}} |
10
+
11
+ ## 主包资源分析
12
+ | Asset | Current | Baseline | Delta | Ratio|
13
+ |----------|------:|------:|------:|------:|
14
+ {{#each mainPackageAssets}}
15
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
16
+ {{/each}}
17
+
18
+ ## 主包模块分析
19
+
20
+ {{#if mainPackageChangedModules.length}}
21
+ ### 主包变化模块分析
22
+ | Module | Current | Baseline | Delta | Ratio|
23
+ |----------|------:|------:|------:|------:|
24
+ {{#each mainPackageChangedModules}}
25
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
26
+ {{/each}}
27
+ {{/if}}
28
+
29
+ ### 主包业务模块分析
30
+ | Module | Current | Baseline | Delta | Ratio|
31
+ |----------|------:|------:|------:|------:|
32
+ {{#each mainPackageBusinessModules}}
33
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
34
+ {{/each}}
35
+
36
+ ### 主包依赖模块分析
37
+ | Module | Current | Baseline | Delta | Ratio|
38
+ |----------|------:|------:|------:|------:|
39
+ {{#each mainPackageDependModules}}
40
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
41
+ {{/each}}
42
+
43
+ {{#if focusAssets.length}}
44
+ ## 重点资源分析
45
+ | Asset | Current | Baseline | Delta | Ratio|
46
+ |----------|------:|------:|------:|------:|
47
+ {{#each focusAssets}}
48
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
49
+ {{/each}}
50
+ {{/if}}
51
+
52
+ {{#if focusModules.length}}
53
+ ## 重点模块分析
54
+ | Module | Current | Baseline | Delta | Ratio|
55
+ |----------|------:|------:|------:|------:|
56
+ {{#each focusModules}}
57
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
58
+ {{/each}}
59
+ {{/if}}
60
+
61
+ {{#if focusPackages.length}}
62
+ ## 重点依赖分析
63
+ | Package | Current | Baseline | Delta | Ratio|
64
+ |----------|------:|------:|------:|------:|
65
+ {{#each focusPackages}}
66
+ | {{displayLabel}} | {{current.displayValue}} | {{baseline.displayValue}} | {{displayDelta}} | {{displayDeltaPercentage}}|
67
+ {{/each}}
68
+ {{/if}}
package/src/types.ts ADDED
@@ -0,0 +1,100 @@
1
+ import { Uploader } from '@taro-minify-pack/helper'
2
+ import { AppConfig } from '@tarojs/taro'
3
+
4
+ interface WebpackStatsOptions {
5
+ /**
6
+ * Output webpack assets information
7
+ * Default: `true`.
8
+ */
9
+ assets?: boolean;
10
+
11
+ /**
12
+ * Output webpack chunks information
13
+ * Default: `true`.
14
+ */
15
+ chunks?: boolean;
16
+
17
+ /**
18
+ * Output webpack modules information
19
+ * Default: `true`.
20
+ */
21
+ modules?: boolean;
22
+
23
+ /**
24
+ * Output webpack hash information
25
+ * Default: `true`.
26
+ */
27
+ hash?: boolean;
28
+
29
+ /**
30
+ * Output webpack builtAt information
31
+ * Default: `true`.
32
+ */
33
+ builtAt?: boolean;
34
+ }
35
+
36
+ export interface BundleStatsOpt {
37
+ html?: boolean;
38
+ json?: boolean;
39
+ silent?: boolean;
40
+ compare?: boolean;
41
+ baseline?: boolean;
42
+ baselinePath?: string;
43
+ reportPath: string;
44
+ upload?: Uploader,
45
+ stats?: Partial<WebpackStatsOptions>;
46
+ reportConfig?: Omit<SummarizeReportOpt, 'appConfig'|'bundleStatsReport'>
47
+ }
48
+
49
+ export interface BaseMetricRun {
50
+ value: number;
51
+ displayValue: string;
52
+ delta?: number;
53
+ deltaPercentage?: number;
54
+ displayDelta?: string;
55
+ displayDeltaPercentage?: string;
56
+ deltaType?: string;
57
+ regression?: boolean;
58
+ }
59
+
60
+ export interface AssetMetricRun extends BaseMetricRun {
61
+ name: string
62
+ isChunk: boolean,
63
+ chunkId: string,
64
+ }
65
+
66
+ export interface ModuleMetricRun extends BaseMetricRun {
67
+ name: string
68
+ chunkIds: string[],
69
+ }
70
+ export interface PackageMetricRun extends BaseMetricRun {
71
+ name: string
72
+ }
73
+
74
+ export interface Metric<T extends BaseMetricRun> {
75
+ key: string;
76
+ label: string;
77
+ biggerIsBetter: boolean;
78
+ changed: boolean;
79
+ runs: T[];
80
+ }
81
+
82
+ export interface BundleStatsReport {
83
+ stats: Metric<BaseMetricRun>[];
84
+ assets: Metric<AssetMetricRun>[];
85
+ modules: Metric<ModuleMetricRun>[];
86
+ packages: Metric<PackageMetricRun>[];
87
+ }
88
+
89
+ export interface FocusMetricRun {
90
+ assets: string[],
91
+ modules: string,
92
+ packages: string[],
93
+ }
94
+
95
+ export interface SummarizeReportOpt {
96
+ appConfig: AppConfig
97
+ bundleStatsReport: BundleStatsReport
98
+ focusMetricRun?: Partial<FocusMetricRun>,
99
+ displayLimit?: number
100
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2015",
4
+ "module": "commonjs",
5
+ "strict": true,
6
+ "sourceMap": true,
7
+ "declaration": true,
8
+ "esModuleInterop": true,
9
+ "moduleResolution": "node",
10
+ // 声明文件生成到的文件目录
11
+ "outDir": "./dist/",
12
+ "skipLibCheck": true,
13
+ /* Skip type checking of declaration files. */
14
+ "forceConsistentCasingInFileNames": true,
15
+ "jsx": "react",
16
+ "lib": ["esnext", "dom"],
17
+ "types": ["@types/webpack-bundle-analyzer"]
18
+ },
19
+ "include": ["src"]
20
+ }