@meng-xi/vite-plugin 0.1.1 → 0.1.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/README-en.md +522 -610
- package/README.md +496 -584
- package/dist/common/compress/index.cjs +1 -0
- package/dist/common/compress/index.d.cts +23 -0
- package/dist/common/compress/index.d.mts +23 -0
- package/dist/common/compress/index.d.ts +23 -0
- package/dist/common/compress/index.mjs +1 -0
- package/dist/common/format/index.cjs +1 -1
- package/dist/common/format/index.d.cts +33 -1
- package/dist/common/format/index.d.mts +33 -1
- package/dist/common/format/index.d.ts +33 -1
- package/dist/common/format/index.mjs +1 -1
- package/dist/common/fs/index.cjs +1 -1
- package/dist/common/fs/index.d.cts +70 -2
- package/dist/common/fs/index.d.mts +70 -2
- package/dist/common/fs/index.d.ts +70 -2
- package/dist/common/fs/index.mjs +1 -1
- package/dist/common/index.cjs +1 -1
- package/dist/common/index.d.cts +4 -2
- package/dist/common/index.d.mts +4 -2
- package/dist/common/index.d.ts +4 -2
- package/dist/common/index.mjs +1 -1
- package/dist/common/path/index.cjs +1 -0
- package/dist/common/path/index.d.cts +22 -0
- package/dist/common/path/index.d.mts +22 -0
- package/dist/common/path/index.d.ts +22 -0
- package/dist/common/path/index.mjs +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.mts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.mjs +1 -1
- package/dist/plugins/bundleAnalyzer/index.cjs +235 -0
- package/dist/plugins/bundleAnalyzer/index.d.cts +215 -0
- package/dist/plugins/bundleAnalyzer/index.d.mts +215 -0
- package/dist/plugins/bundleAnalyzer/index.d.ts +215 -0
- package/dist/plugins/bundleAnalyzer/index.mjs +235 -0
- package/dist/plugins/compressAssets/index.cjs +1 -0
- package/dist/plugins/compressAssets/index.d.cts +132 -0
- package/dist/plugins/compressAssets/index.d.mts +132 -0
- package/dist/plugins/compressAssets/index.d.ts +132 -0
- package/dist/plugins/compressAssets/index.mjs +1 -0
- package/dist/plugins/generateRouter/index.cjs +4 -4
- package/dist/plugins/generateRouter/index.mjs +1 -1
- package/dist/plugins/generateVersion/index.cjs +1 -1
- package/dist/plugins/generateVersion/index.mjs +1 -1
- package/dist/plugins/htmlInject/index.cjs +7 -7
- package/dist/plugins/index.cjs +1 -1
- package/dist/plugins/index.d.cts +2 -0
- package/dist/plugins/index.d.mts +2 -0
- package/dist/plugins/index.d.ts +2 -0
- package/dist/plugins/index.mjs +1 -1
- package/dist/plugins/loadingManager/index.cjs +1 -1
- package/dist/plugins/loadingManager/index.mjs +1 -1
- package/package.json +24 -2
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 判断模块 ID 是否来自 node_modules
|
|
3
|
+
*
|
|
4
|
+
* @param {string} moduleId - 模块标识符
|
|
5
|
+
* @returns {boolean} 是否来自 node_modules
|
|
6
|
+
*
|
|
7
|
+
* @description 检测规则:
|
|
8
|
+
* - 路径中包含 `node_modules` 的模块
|
|
9
|
+
* - 以 `\0` 开头的虚拟模块(Rollup 内部模块)
|
|
10
|
+
* - 以 `virtual:` 开头的虚拟模块
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* isNodeModule('node_modules/lodash/index.js') // true
|
|
15
|
+
* isNodeModule('src/utils/helper.ts') // false
|
|
16
|
+
* isNodeModule('\0some-virtual-module') // true
|
|
17
|
+
* isNodeModule('virtual:import-meta-env') // true
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function isNodeModule(moduleId: string): boolean;
|
|
21
|
+
|
|
22
|
+
export { isNodeModule };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 判断模块 ID 是否来自 node_modules
|
|
3
|
+
*
|
|
4
|
+
* @param {string} moduleId - 模块标识符
|
|
5
|
+
* @returns {boolean} 是否来自 node_modules
|
|
6
|
+
*
|
|
7
|
+
* @description 检测规则:
|
|
8
|
+
* - 路径中包含 `node_modules` 的模块
|
|
9
|
+
* - 以 `\0` 开头的虚拟模块(Rollup 内部模块)
|
|
10
|
+
* - 以 `virtual:` 开头的虚拟模块
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* isNodeModule('node_modules/lodash/index.js') // true
|
|
15
|
+
* isNodeModule('src/utils/helper.ts') // false
|
|
16
|
+
* isNodeModule('\0some-virtual-module') // true
|
|
17
|
+
* isNodeModule('virtual:import-meta-env') // true
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function isNodeModule(moduleId: string): boolean;
|
|
21
|
+
|
|
22
|
+
export { isNodeModule };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(t){return t.includes("node_modules")||t.startsWith("\0")||t.startsWith("virtual:")}export{e as isNodeModule};
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";const
|
|
1
|
+
"use strict";const common_compress_index=require("./common/compress/index.cjs"),common_format_index=require("./common/format/index.cjs"),common_fs_index=require("./common/fs/index.cjs"),common_html_index=require("./common/html/index.cjs"),common_object_index=require("./common/object/index.cjs"),common_path_index=require("./common/path/index.cjs"),common_script_index=require("./common/script/index.cjs"),validator=require("./shared/vite-plugin.Bcg6RW2N.cjs"),common_validation_index=require("./common/validation/index.cjs"),factory_index=require("./factory/index.cjs"),logger_index=require("./logger/index.cjs"),plugins_buildProgress_index=require("./plugins/buildProgress/index.cjs"),plugins_bundleAnalyzer_index=require("./plugins/bundleAnalyzer/index.cjs"),plugins_compressAssets_index=require("./plugins/compressAssets/index.cjs"),plugins_copyFile_index=require("./plugins/copyFile/index.cjs"),plugins_faviconManager_index=require("./plugins/faviconManager/index.cjs"),plugins_generateRouter_index=require("./plugins/generateRouter/index.cjs"),plugins_generateVersion_index=require("./plugins/generateVersion/index.cjs"),plugins_htmlInject_index=require("./plugins/htmlInject/index.cjs"),plugins_loadingManager_index=require("./plugins/loadingManager/index.cjs"),plugins_versionUpdateChecker_index=require("./plugins/versionUpdateChecker/index.cjs");require("node:zlib"),require("node:stream/promises"),require("node:stream"),require("crypto"),require("node:path"),require("fs"),require("path"),require("node:fs"),exports.calculateGzipSize=common_compress_index.calculateGzipSize,exports.escapeHtmlAttr=common_format_index.escapeHtmlAttr,exports.formatDate=common_format_index.formatDate,exports.formatFileSize=common_format_index.formatFileSize,exports.generateRandomHash=common_format_index.generateRandomHash,exports.getDateFormatParams=common_format_index.getDateFormatParams,exports.getExtension=common_format_index.getExtension,exports.padNumber=common_format_index.padNumber,exports.parseTemplate=common_format_index.parseTemplate,exports.stripJsonComments=common_format_index.stripJsonComments,exports.toCamelCase=common_format_index.toCamelCase,exports.toPascalCase=common_format_index.toPascalCase,exports.checkSourceExists=common_fs_index.checkSourceExists,exports.copySourceToTarget=common_fs_index.copySourceToTarget,exports.ensureTargetDir=common_fs_index.ensureTargetDir,exports.fileExists=common_fs_index.fileExists,exports.readDirRecursive=common_fs_index.readDirRecursive,exports.readFileContent=common_fs_index.readFileContent,exports.readFileSync=common_fs_index.readFileSync,exports.runWithConcurrency=common_fs_index.runWithConcurrency,exports.scanDirectory=common_fs_index.scanDirectory,exports.shouldUpdateFile=common_fs_index.shouldUpdateFile,exports.writeFileContent=common_fs_index.writeFileContent,exports.writeJsonReport=common_fs_index.writeJsonReport,exports.injectBeforeTag=common_html_index.injectBeforeTag,exports.injectBeforeTagWithFallback=common_html_index.injectBeforeTagWithFallback,exports.injectHeadAndBody=common_html_index.injectHeadAndBody,exports.injectHtmlByPriority=common_html_index.injectHtmlByPriority,exports.deepMerge=common_object_index.deepMerge,exports.isNodeModule=common_path_index.isNodeModule,exports.containsScriptTag=common_script_index.containsScriptTag,exports.makeCallback=common_script_index.makeCallback,exports.validateIdentifierName=common_script_index.validateIdentifierName,exports.Validator=validator.Validator,exports.validateCallbackFields=common_validation_index.validateCallbackFields,exports.validateEnumValue=common_validation_index.validateEnumValue,exports.validateGlobalName=common_validation_index.validateGlobalName,exports.validateNestedDuration=common_validation_index.validateNestedDuration,exports.validateNoScriptInTemplate=common_validation_index.validateNoScriptInTemplate,exports.validateNonNegativeNumber=common_validation_index.validateNonNegativeNumber,exports.BasePlugin=factory_index.BasePlugin,exports.createPluginFactory=factory_index.createPluginFactory,exports.Logger=logger_index.Logger,exports.buildProgress=plugins_buildProgress_index.buildProgress,exports.bundleAnalyzer=plugins_bundleAnalyzer_index.bundleAnalyzer,exports.compressAssets=plugins_compressAssets_index.compressAssets,exports.copyFile=plugins_copyFile_index.copyFile,exports.faviconManager=plugins_faviconManager_index.faviconManager,exports.generateRouter=plugins_generateRouter_index.generateRouter,exports.generateVersion=plugins_generateVersion_index.generateVersion,exports.htmlInject=plugins_htmlInject_index.htmlInject,exports.loadingManager=plugins_loadingManager_index.loadingManager,exports.versionUpdateChecker=plugins_versionUpdateChecker_index.versionUpdateChecker;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { DateFormatOptions, escapeHtmlAttr, formatDate, generateRandomHash, getDateFormatParams, padNumber, parseTemplate, stripJsonComments, toCamelCase, toPascalCase } from './common/format/index.cjs';
|
|
1
|
+
export { calculateGzipSize } from './common/compress/index.cjs';
|
|
2
|
+
export { DateFormatOptions, escapeHtmlAttr, formatDate, formatFileSize, generateRandomHash, getDateFormatParams, getExtension, padNumber, parseTemplate, stripJsonComments, toCamelCase, toPascalCase } from './common/format/index.cjs';
|
|
3
|
+
export { CopyOptions, CopyResult, ScanDirectoryOptions, ScannedFile, checkSourceExists, copySourceToTarget, ensureTargetDir, fileExists, readDirRecursive, readFileContent, readFileSync, runWithConcurrency, scanDirectory, shouldUpdateFile, writeFileContent, writeJsonReport } from './common/fs/index.cjs';
|
|
3
4
|
export { DualInjectResult, HtmlInjectResult, injectBeforeTag, injectBeforeTagWithFallback, injectHeadAndBody, injectHtmlByPriority } from './common/html/index.cjs';
|
|
4
5
|
export { deepMerge } from './common/object/index.cjs';
|
|
6
|
+
export { isNodeModule } from './common/path/index.cjs';
|
|
5
7
|
export { containsScriptTag, makeCallback, validateIdentifierName } from './common/script/index.cjs';
|
|
6
8
|
export { V as Validator } from './shared/vite-plugin.DRRlWY8P.cjs';
|
|
7
9
|
export { validateCallbackFields, validateEnumValue, validateGlobalName, validateNestedDuration, validateNoScriptInTemplate, validateNonNegativeNumber } from './common/validation/index.cjs';
|
|
8
10
|
export { BasePlugin, BasePluginOptions, OptionsNormalizer, PluginFactory, PluginWithInstance, createPluginFactory } from './factory/index.cjs';
|
|
9
11
|
export { L as Logger, P as PluginLogger } from './shared/vite-plugin.CLr0ttuO.cjs';
|
|
10
12
|
export { BuildPhase, BuildProgressOptions, ProgressFormat, ProgressTheme, buildProgress } from './plugins/buildProgress/index.cjs';
|
|
13
|
+
export { BundleAnalysisResult, BundleAnalyzerOptions, BundleOutputFormat, ChunkStats, ComparisonDiff, FileTypeDistribution, ModuleStats, SizeWarning, bundleAnalyzer } from './plugins/bundleAnalyzer/index.cjs';
|
|
14
|
+
export { CompressAlgorithm, CompressAssetsOptions, CompressStats, CompressSummary, compressAssets } from './plugins/compressAssets/index.cjs';
|
|
11
15
|
export { CopyFileOptions, copyFile } from './plugins/copyFile/index.cjs';
|
|
12
16
|
export { FaviconManagerOptions, Icon, faviconManager } from './plugins/faviconManager/index.cjs';
|
|
13
17
|
export { GenerateRouterOptions, NameStrategy, OutputFormat, RouteConfig, RouteMeta, UniAppPageConfig, UniAppPagesJson, UniAppTabBarConfig, generateRouter } from './plugins/generateRouter/index.cjs';
|
package/dist/index.d.mts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { DateFormatOptions, escapeHtmlAttr, formatDate, generateRandomHash, getDateFormatParams, padNumber, parseTemplate, stripJsonComments, toCamelCase, toPascalCase } from './common/format/index.mjs';
|
|
1
|
+
export { calculateGzipSize } from './common/compress/index.mjs';
|
|
2
|
+
export { DateFormatOptions, escapeHtmlAttr, formatDate, formatFileSize, generateRandomHash, getDateFormatParams, getExtension, padNumber, parseTemplate, stripJsonComments, toCamelCase, toPascalCase } from './common/format/index.mjs';
|
|
3
|
+
export { CopyOptions, CopyResult, ScanDirectoryOptions, ScannedFile, checkSourceExists, copySourceToTarget, ensureTargetDir, fileExists, readDirRecursive, readFileContent, readFileSync, runWithConcurrency, scanDirectory, shouldUpdateFile, writeFileContent, writeJsonReport } from './common/fs/index.mjs';
|
|
3
4
|
export { DualInjectResult, HtmlInjectResult, injectBeforeTag, injectBeforeTagWithFallback, injectHeadAndBody, injectHtmlByPriority } from './common/html/index.mjs';
|
|
4
5
|
export { deepMerge } from './common/object/index.mjs';
|
|
6
|
+
export { isNodeModule } from './common/path/index.mjs';
|
|
5
7
|
export { containsScriptTag, makeCallback, validateIdentifierName } from './common/script/index.mjs';
|
|
6
8
|
export { V as Validator } from './shared/vite-plugin.DRRlWY8P.mjs';
|
|
7
9
|
export { validateCallbackFields, validateEnumValue, validateGlobalName, validateNestedDuration, validateNoScriptInTemplate, validateNonNegativeNumber } from './common/validation/index.mjs';
|
|
8
10
|
export { BasePlugin, BasePluginOptions, OptionsNormalizer, PluginFactory, PluginWithInstance, createPluginFactory } from './factory/index.mjs';
|
|
9
11
|
export { L as Logger, P as PluginLogger } from './shared/vite-plugin.CLr0ttuO.mjs';
|
|
10
12
|
export { BuildPhase, BuildProgressOptions, ProgressFormat, ProgressTheme, buildProgress } from './plugins/buildProgress/index.mjs';
|
|
13
|
+
export { BundleAnalysisResult, BundleAnalyzerOptions, BundleOutputFormat, ChunkStats, ComparisonDiff, FileTypeDistribution, ModuleStats, SizeWarning, bundleAnalyzer } from './plugins/bundleAnalyzer/index.mjs';
|
|
14
|
+
export { CompressAlgorithm, CompressAssetsOptions, CompressStats, CompressSummary, compressAssets } from './plugins/compressAssets/index.mjs';
|
|
11
15
|
export { CopyFileOptions, copyFile } from './plugins/copyFile/index.mjs';
|
|
12
16
|
export { FaviconManagerOptions, Icon, faviconManager } from './plugins/faviconManager/index.mjs';
|
|
13
17
|
export { GenerateRouterOptions, NameStrategy, OutputFormat, RouteConfig, RouteMeta, UniAppPageConfig, UniAppPagesJson, UniAppTabBarConfig, generateRouter } from './plugins/generateRouter/index.mjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { DateFormatOptions, escapeHtmlAttr, formatDate, generateRandomHash, getDateFormatParams, padNumber, parseTemplate, stripJsonComments, toCamelCase, toPascalCase } from './common/format/index.js';
|
|
1
|
+
export { calculateGzipSize } from './common/compress/index.js';
|
|
2
|
+
export { DateFormatOptions, escapeHtmlAttr, formatDate, formatFileSize, generateRandomHash, getDateFormatParams, getExtension, padNumber, parseTemplate, stripJsonComments, toCamelCase, toPascalCase } from './common/format/index.js';
|
|
3
|
+
export { CopyOptions, CopyResult, ScanDirectoryOptions, ScannedFile, checkSourceExists, copySourceToTarget, ensureTargetDir, fileExists, readDirRecursive, readFileContent, readFileSync, runWithConcurrency, scanDirectory, shouldUpdateFile, writeFileContent, writeJsonReport } from './common/fs/index.js';
|
|
3
4
|
export { DualInjectResult, HtmlInjectResult, injectBeforeTag, injectBeforeTagWithFallback, injectHeadAndBody, injectHtmlByPriority } from './common/html/index.js';
|
|
4
5
|
export { deepMerge } from './common/object/index.js';
|
|
6
|
+
export { isNodeModule } from './common/path/index.js';
|
|
5
7
|
export { containsScriptTag, makeCallback, validateIdentifierName } from './common/script/index.js';
|
|
6
8
|
export { V as Validator } from './shared/vite-plugin.DRRlWY8P.js';
|
|
7
9
|
export { validateCallbackFields, validateEnumValue, validateGlobalName, validateNestedDuration, validateNoScriptInTemplate, validateNonNegativeNumber } from './common/validation/index.js';
|
|
8
10
|
export { BasePlugin, BasePluginOptions, OptionsNormalizer, PluginFactory, PluginWithInstance, createPluginFactory } from './factory/index.js';
|
|
9
11
|
export { L as Logger, P as PluginLogger } from './shared/vite-plugin.CLr0ttuO.js';
|
|
10
12
|
export { BuildPhase, BuildProgressOptions, ProgressFormat, ProgressTheme, buildProgress } from './plugins/buildProgress/index.js';
|
|
13
|
+
export { BundleAnalysisResult, BundleAnalyzerOptions, BundleOutputFormat, ChunkStats, ComparisonDiff, FileTypeDistribution, ModuleStats, SizeWarning, bundleAnalyzer } from './plugins/bundleAnalyzer/index.js';
|
|
14
|
+
export { CompressAlgorithm, CompressAssetsOptions, CompressStats, CompressSummary, compressAssets } from './plugins/compressAssets/index.js';
|
|
11
15
|
export { CopyFileOptions, copyFile } from './plugins/copyFile/index.js';
|
|
12
16
|
export { FaviconManagerOptions, Icon, faviconManager } from './plugins/faviconManager/index.js';
|
|
13
17
|
export { GenerateRouterOptions, NameStrategy, OutputFormat, RouteConfig, RouteMeta, UniAppPageConfig, UniAppPagesJson, UniAppTabBarConfig, generateRouter } from './plugins/generateRouter/index.js';
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{
|
|
1
|
+
export{calculateGzipSize}from"./common/compress/index.mjs";export{escapeHtmlAttr,formatDate,formatFileSize,generateRandomHash,getDateFormatParams,getExtension,padNumber,parseTemplate,stripJsonComments,toCamelCase,toPascalCase}from"./common/format/index.mjs";export{checkSourceExists,copySourceToTarget,ensureTargetDir,fileExists,readDirRecursive,readFileContent,readFileSync,runWithConcurrency,scanDirectory,shouldUpdateFile,writeFileContent,writeJsonReport}from"./common/fs/index.mjs";export{injectBeforeTag,injectBeforeTagWithFallback,injectHeadAndBody,injectHtmlByPriority}from"./common/html/index.mjs";export{deepMerge}from"./common/object/index.mjs";export{isNodeModule}from"./common/path/index.mjs";export{containsScriptTag,makeCallback,validateIdentifierName}from"./common/script/index.mjs";export{V as Validator}from"./shared/vite-plugin.DcExl6jd.mjs";export{validateCallbackFields,validateEnumValue,validateGlobalName,validateNestedDuration,validateNoScriptInTemplate,validateNonNegativeNumber}from"./common/validation/index.mjs";export{BasePlugin,createPluginFactory}from"./factory/index.mjs";export{Logger}from"./logger/index.mjs";export{buildProgress}from"./plugins/buildProgress/index.mjs";export{bundleAnalyzer}from"./plugins/bundleAnalyzer/index.mjs";export{compressAssets}from"./plugins/compressAssets/index.mjs";export{copyFile}from"./plugins/copyFile/index.mjs";export{faviconManager}from"./plugins/faviconManager/index.mjs";export{generateRouter}from"./plugins/generateRouter/index.mjs";export{generateVersion}from"./plugins/generateVersion/index.mjs";export{htmlInject}from"./plugins/htmlInject/index.mjs";export{loadingManager}from"./plugins/loadingManager/index.mjs";export{versionUpdateChecker}from"./plugins/versionUpdateChecker/index.mjs";import"node:zlib";import"node:stream/promises";import"node:stream";import"crypto";import"node:path";import"fs";import"path";import"node:fs";
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";const factory_index=require("../../factory/index.cjs"),a$1=require("node:fs"),a=require("node:path"),common_compress_index=require("../../common/compress/index.cjs"),common_path_index=require("../../common/path/index.cjs"),common_fs_index=require("../../common/fs/index.cjs"),common_format_index=require("../../common/format/index.cjs");require("../../logger/index.cjs"),require("../../common/object/index.cjs"),require("../../shared/vite-plugin.Bcg6RW2N.cjs"),require("node:zlib"),require("node:stream/promises"),require("node:stream"),require("fs"),require("path"),require("crypto");function _interopDefaultCompat(o){return o&&typeof o=="object"&&"default"in o?o.default:o}const a__default$1=_interopDefaultCompat(a$1),a__default=_interopDefaultCompat(a);async function scanOutputDirectory(o,s={}){return common_fs_index.scanDirectory(o,{includeExtensions:s.includeExtensions,excludePatterns:s.excludePatterns})}function analyzeFileTypeDistribution(o){const s=o.reduce((r,n)=>r+n.size,0),e=new Map;for(const r of o){const n=r.extension||"(no ext)",t=e.get(n)||{count:0,totalSize:0};t.count++,t.totalSize+=r.size,e.set(n,t)}return Array.from(e.entries()).map(([r,{count:n,totalSize:t}])=>({extension:r,count:n,totalSize:t,percentage:s>0?Number((t/s*100).toFixed(1)):0})).sort((r,n)=>n.totalSize-r.totalSize)}function checkSizeThresholds(o,s){const e=[],r=s*1024;for(const n of o){n.size>r&&e.push({level:"chunk",name:n.name,sizeKB:Number((n.size/1024).toFixed(1)),thresholdKB:s,message:`Chunk "${n.name}" \u8D85\u8FC7\u9608\u503C: ${(n.size/1024).toFixed(1)}KB > ${s}KB`});for(const t of n.modules)t.size>r&&e.push({level:"module",name:t.id,sizeKB:Number((t.size/1024).toFixed(1)),thresholdKB:s,message:`\u6A21\u5757 "${t.id}" \u8D85\u8FC7\u9608\u503C: ${(t.size/1024).toFixed(1)}KB > ${s}KB`})}return e}function getTopModules(o,s,e){const r=[];for(const n of o)for(const t of n.modules)e&&t.isNodeModule||r.push(t);return r.sort((n,t)=>t.size-n.size).slice(0,s)}async function buildChunkStats(o,s,e={}){const{gzipSize:r=!0,excludeNodeModules:n=!1}=e,t=[];for(const d of s){const l=a__default.relative(o,d.filePath),c=l.replace(/\\/g,"/"),i=d.extension;let u="chunk";i===".html"?u="entry":[".js",".mjs",".cjs",".css",".html"].includes(i)||(u="asset");let h=0;if(r)try{const m=await a__default$1.promises.readFile(d.filePath);h=await common_compress_index.calculateGzipSize(m)}catch{h=0}const p=common_path_index.isNodeModule(l),f={id:c,size:d.size,gzipSize:h,chunks:[c],imports:[],isEntry:u==="entry",isNodeModule:p};n&&p||t.push({name:c,size:d.size,gzipSize:h,modules:[f],type:u,fileCount:1})}return t.sort((d,l)=>l.size-d.size)}async function analyzeBundle(o,s){const e=Date.now(),r=await scanOutputDirectory(o,{includeExtensions:s.includeExtensions,excludePatterns:s.excludePatterns}),n=await buildChunkStats(o,r,{gzipSize:s.gzipSize,excludeNodeModules:s.excludeNodeModules}),t=s.excludeNodeModules?r.filter(p=>!common_path_index.isNodeModule(a__default.relative(o,p.filePath))):r,d=analyzeFileTypeDistribution(t),l=getTopModules(n,s.topModules,s.excludeNodeModules),c=checkSizeThresholds(n,s.sizeThreshold),i=n.reduce((p,f)=>p+f.size,0),u=n.reduce((p,f)=>p+f.gzipSize,0),h=Date.now()-e;return{timestamp:new Date().toISOString(),totalSize:i,totalGzipSize:u,chunks:n,topModules:l,fileTypeDistribution:d,warnings:c,comparisonDiffs:[],analysisTime:h}}async function loadPreviousReport(o){try{const s=a__default.isAbsolute(o)?o:a__default.resolve(process.cwd(),o);if(!await a__default$1.promises.access(s,a__default$1.constants.F_OK).then(()=>!0).catch(()=>!1))return null;const e=await a__default$1.promises.readFile(s,"utf-8");return JSON.parse(e)}catch{return null}}function compareWithPrevious(o,s){const e=[],r=new Map;for(const t of s.chunks)r.set(t.name,t.size);const n=new Map;for(const t of o.chunks)n.set(t.name,t.size);for(const[t,d]of n){const l=r.get(t);if(l===void 0)e.push({name:t,previousSize:-1,currentSize:d,diff:d,diffPercentage:100,trend:"added"});else{const c=d-l,i=l>0?Number((c/l*100).toFixed(1)):0;e.push({name:t,previousSize:l,currentSize:d,diff:c,diffPercentage:i,trend:c>0?"increased":c<0?"decreased":"unchanged"})}}for(const[t,d]of r)n.has(t)||e.push({name:t,previousSize:d,currentSize:-1,diff:-d,diffPercentage:-100,trend:"removed"});return e.sort((t,d)=>Math.abs(d.diff)-Math.abs(t.diff))}async function generateJsonReport(o,s,e){const r=a__default.join(o,`${s}.json`),n={timestamp:e.timestamp,summary:{totalSize:e.totalSize,totalGzipSize:e.totalGzipSize,totalSizeFormatted:common_format_index.formatFileSize(e.totalSize),totalGzipSizeFormatted:common_format_index.formatFileSize(e.totalGzipSize),chunkCount:e.chunks.length,warningCount:e.warnings.length,analysisTime:e.analysisTime},chunks:e.chunks.map(t=>({name:t.name,size:t.size,sizeFormatted:common_format_index.formatFileSize(t.size),gzipSize:t.gzipSize,gzipSizeFormatted:common_format_index.formatFileSize(t.gzipSize),type:t.type,fileCount:t.fileCount,modules:t.modules.map(d=>({id:d.id,size:d.size,sizeFormatted:common_format_index.formatFileSize(d.size),gzipSize:d.gzipSize,gzipSizeFormatted:common_format_index.formatFileSize(d.gzipSize),isEntry:d.isEntry,isNodeModule:d.isNodeModule,imports:d.imports}))})),topModules:e.topModules.map(t=>({id:t.id,size:t.size,sizeFormatted:common_format_index.formatFileSize(t.size),gzipSize:t.gzipSize,gzipSizeFormatted:common_format_index.formatFileSize(t.gzipSize),isNodeModule:t.isNodeModule})),fileTypeDistribution:e.fileTypeDistribution,warnings:e.warnings,comparisonDiffs:e.comparisonDiffs};return await common_fs_index.writeJsonReport(r,n),r}async function generateHtmlReport(o,s,e,r={}){const n=a__default.join(o,`${s}.html`),t=r.defaultChartType||"treemap",d=e.chunks.map(p=>({name:p.name,size:p.size,gzipSize:p.gzipSize,type:p.type})),l=e.fileTypeDistribution,c=e.warnings,i=e.comparisonDiffs,u=e.topModules.slice(0,10),h=x(e,d,l,c,i,u,t);return await common_fs_index.writeFileContent(n,h),n}function x(o,s,e,r,n,t,d){const l=common_format_index.formatFileSize(o.totalSize),c=common_format_index.formatFileSize(o.totalGzipSize);return`<!DOCTYPE html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Bundle Analysis Report</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f7fa; color: #333; line-height: 1.6; }
|
|
10
|
+
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
|
|
11
|
+
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; border-radius: 12px; margin-bottom: 24px; }
|
|
12
|
+
.header h1 { font-size: 24px; margin-bottom: 8px; }
|
|
13
|
+
.header .meta { font-size: 14px; opacity: 0.9; }
|
|
14
|
+
.summary-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px; }
|
|
15
|
+
.card { background: white; border-radius: 10px; padding: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
|
|
16
|
+
.card .label { font-size: 13px; color: #888; margin-bottom: 4px; }
|
|
17
|
+
.card .value { font-size: 24px; font-weight: 700; color: #333; }
|
|
18
|
+
.card .sub { font-size: 12px; color: #aaa; margin-top: 2px; }
|
|
19
|
+
.section { background: white; border-radius: 10px; padding: 24px; margin-bottom: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
|
|
20
|
+
.section h2 { font-size: 18px; margin-bottom: 16px; padding-bottom: 8px; border-bottom: 2px solid #f0f0f0; }
|
|
21
|
+
.chart-tabs { display: flex; gap: 8px; margin-bottom: 16px; }
|
|
22
|
+
.chart-tab { padding: 6px 16px; border: 1px solid #ddd; border-radius: 6px; background: white; cursor: pointer; font-size: 13px; transition: all 0.2s; }
|
|
23
|
+
.chart-tab.active { background: #667eea; color: white; border-color: #667eea; }
|
|
24
|
+
.chart-container { min-height: 400px; position: relative; }
|
|
25
|
+
.treemap { display: flex; flex-wrap: wrap; gap: 2px; }
|
|
26
|
+
.treemap-item { border-radius: 4px; padding: 8px; color: white; font-size: 11px; overflow: hidden; display: flex; flex-direction: column; justify-content: center; align-items: center; cursor: pointer; transition: opacity 0.2s; min-width: 40px; min-height: 40px; }
|
|
27
|
+
.treemap-item:hover { opacity: 0.85; }
|
|
28
|
+
.treemap-item .name { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
|
|
29
|
+
.treemap-item .size { font-size: 10px; opacity: 0.9; }
|
|
30
|
+
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
31
|
+
th, td { padding: 10px 12px; text-align: left; border-bottom: 1px solid #f0f0f0; }
|
|
32
|
+
th { background: #fafafa; font-weight: 600; color: #555; position: sticky; top: 0; }
|
|
33
|
+
tr:hover { background: #f8f9ff; }
|
|
34
|
+
.bar { height: 8px; border-radius: 4px; background: #667eea; transition: width 0.3s; }
|
|
35
|
+
.bar-bg { width: 100%; height: 8px; border-radius: 4px; background: #f0f0f0; }
|
|
36
|
+
.warning { background: #fff8e1; border-left: 4px solid #ffc107; padding: 12px 16px; border-radius: 0 6px 6px 0; margin-bottom: 8px; font-size: 13px; }
|
|
37
|
+
.warning.critical { background: #ffebee; border-left-color: #f44336; }
|
|
38
|
+
.diff-positive { color: #f44336; }
|
|
39
|
+
.diff-negative { color: #4caf50; }
|
|
40
|
+
.diff-added { color: #2196f3; }
|
|
41
|
+
.diff-removed { color: #9e9e9e; }
|
|
42
|
+
.diff-unchanged { color: #bbb; }
|
|
43
|
+
.pie-chart { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
|
|
44
|
+
.pie-svg { flex-shrink: 0; }
|
|
45
|
+
.pie-legend { flex: 1; min-width: 200px; }
|
|
46
|
+
.legend-item { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; font-size: 13px; }
|
|
47
|
+
.legend-color { width: 12px; height: 12px; border-radius: 3px; flex-shrink: 0; }
|
|
48
|
+
.empty-state { text-align: center; padding: 40px; color: #aaa; }
|
|
49
|
+
@media (max-width: 768px) { .container { padding: 12px; } .summary-cards { grid-template-columns: repeat(2, 1fr); } }
|
|
50
|
+
</style>
|
|
51
|
+
</head>
|
|
52
|
+
<body>
|
|
53
|
+
<div class="container">
|
|
54
|
+
<div class="header">
|
|
55
|
+
<h1>Bundle Analysis Report</h1>
|
|
56
|
+
<div class="meta">Generated: ${o.timestamp} | Analysis time: ${o.analysisTime}ms</div>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div class="summary-cards">
|
|
60
|
+
<div class="card">
|
|
61
|
+
<div class="label">Total Size</div>
|
|
62
|
+
<div class="value">${l}</div>
|
|
63
|
+
<div class="sub">gzip: ${c}</div>
|
|
64
|
+
</div>
|
|
65
|
+
<div class="card">
|
|
66
|
+
<div class="label">Chunks</div>
|
|
67
|
+
<div class="value">${o.chunks.length}</div>
|
|
68
|
+
<div class="sub">entry: ${o.chunks.filter(i=>i.type==="entry").length} | chunk: ${o.chunks.filter(i=>i.type==="chunk").length} | asset: ${o.chunks.filter(i=>i.type==="asset").length}</div>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="card">
|
|
71
|
+
<div class="label">Warnings</div>
|
|
72
|
+
<div class="value">${r.length}</div>
|
|
73
|
+
<div class="sub">${r.length>0?"threshold exceeded":"all within limits"}</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="card">
|
|
76
|
+
<div class="label">File Types</div>
|
|
77
|
+
<div class="value">${e.length}</div>
|
|
78
|
+
<div class="sub">extensions detected</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
${r.length>0?`
|
|
83
|
+
<div class="section">
|
|
84
|
+
<h2>Warnings</h2>
|
|
85
|
+
${r.map(i=>`<div class="warning ${i.sizeKB>i.thresholdKB*2?"critical":""}"><strong>${i.level.toUpperCase()}</strong>: ${i.message}</div>`).join("")}
|
|
86
|
+
</div>
|
|
87
|
+
`:""}
|
|
88
|
+
|
|
89
|
+
<div class="section">
|
|
90
|
+
<h2>Size Distribution</h2>
|
|
91
|
+
<div class="chart-tabs">
|
|
92
|
+
<button class="chart-tab ${d==="treemap"?"active":""}" onclick="switchChart('treemap')">Treemap</button>
|
|
93
|
+
<button class="chart-tab ${d==="sunburst"?"active":""}" onclick="switchChart('sunburst')">Sunburst</button>
|
|
94
|
+
<button class="chart-tab ${d==="list"?"active":""}" onclick="switchChart('list')">List</button>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="chart-container" id="chartContainer"></div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div class="section">
|
|
100
|
+
<h2>File Type Distribution</h2>
|
|
101
|
+
<div class="pie-chart">
|
|
102
|
+
<div class="pie-svg" id="pieChart"></div>
|
|
103
|
+
<div class="pie-legend" id="pieLegend"></div>
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div class="section">
|
|
108
|
+
<h2>Top Modules</h2>
|
|
109
|
+
<table>
|
|
110
|
+
<thead><tr><th>#</th><th>Module</th><th>Size</th><th>gzip</th><th>Type</th></tr></thead>
|
|
111
|
+
<tbody>
|
|
112
|
+
${t.map((i,u)=>`<tr><td>${u+1}</td><td title="${i.id}">${i.id.length>60?i.id.slice(0,57)+"...":i.id}</td><td>${common_format_index.formatFileSize(i.size)}</td><td>${common_format_index.formatFileSize(i.gzipSize)}</td><td>${i.isNodeModule?"node_modules":"source"}</td></tr>`).join("")}
|
|
113
|
+
</tbody>
|
|
114
|
+
</table>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
${n.length>0?`
|
|
118
|
+
<div class="section">
|
|
119
|
+
<h2>Comparison with Previous Build</h2>
|
|
120
|
+
<table>
|
|
121
|
+
<thead><tr><th>Name</th><th>Previous</th><th>Current</th><th>Diff</th><th>Trend</th></tr></thead>
|
|
122
|
+
<tbody>
|
|
123
|
+
${n.slice(0,20).map(i=>{const u=i.trend==="increased"?"diff-positive":i.trend==="decreased"?"diff-negative":i.trend==="added"?"diff-added":i.trend==="removed"?"diff-removed":"diff-unchanged",h=i.trend==="increased"?"▲":i.trend==="decreased"?"▼":i.trend==="added"?"✚":i.trend==="removed"?"✖":"▬";return`<tr><td>${i.name}</td><td>${i.previousSize>=0?common_format_index.formatFileSize(i.previousSize):"-"}</td><td>${i.currentSize>=0?common_format_index.formatFileSize(i.currentSize):"-"}</td><td class="${u}">${i.diff>=0?"+":""}${common_format_index.formatFileSize(Math.abs(i.diff))} (${i.diffPercentage}%)</td><td class="${u}">${h} ${i.trend}</td></tr>`}).join("")}
|
|
124
|
+
</tbody>
|
|
125
|
+
</table>
|
|
126
|
+
</div>
|
|
127
|
+
`:""}
|
|
128
|
+
|
|
129
|
+
<div class="section">
|
|
130
|
+
<h2>All Chunks</h2>
|
|
131
|
+
<table>
|
|
132
|
+
<thead><tr><th>Name</th><th>Size</th><th>gzip</th><th>Type</th><th>Proportion</th></tr></thead>
|
|
133
|
+
<tbody>
|
|
134
|
+
${o.chunks.map(i=>{const u=o.totalSize>0?i.size/o.totalSize*100:0;return`<tr><td title="${i.name}">${i.name.length>50?i.name.slice(0,47)+"...":i.name}</td><td>${common_format_index.formatFileSize(i.size)}</td><td>${common_format_index.formatFileSize(i.gzipSize)}</td><td>${i.type}</td><td><div class="bar-bg"><div class="bar" style="width:${u}%"></div></div><span style="font-size:11px;color:#888;">${u.toFixed(1)}%</span></td></tr>`}).join("")}
|
|
135
|
+
</tbody>
|
|
136
|
+
</table>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
<script>
|
|
141
|
+
const chartData = ${JSON.stringify(s)};
|
|
142
|
+
const totalSize = ${o.totalSize};
|
|
143
|
+
const distributionData = ${JSON.stringify(e)};
|
|
144
|
+
const COLORS = ['#667eea','#764ba2','#f093fb','#f5576c','#4facfe','#00f2fe','#43e97b','#fa709a','#fee140','#a18cd1','#fbc2eb','#a6c1ee','#ffecd2','#fcb69f','#ff9a9e','#fad0c4'];
|
|
145
|
+
|
|
146
|
+
function switchChart(type) {
|
|
147
|
+
document.querySelectorAll('.chart-tab').forEach(t => t.classList.remove('active'));
|
|
148
|
+
event.target.classList.add('active');
|
|
149
|
+
renderChart(type);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function renderChart(type) {
|
|
153
|
+
const container = document.getElementById('chartContainer');
|
|
154
|
+
if (type === 'treemap') renderTreemap(container);
|
|
155
|
+
else if (type === 'sunburst') renderSunburst(container);
|
|
156
|
+
else renderList(container);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function renderTreemap(container) {
|
|
160
|
+
const items = chartData.map((d, i) => {
|
|
161
|
+
const pct = totalSize > 0 ? (d.size / totalSize * 100) : 0;
|
|
162
|
+
const flex = Math.max(pct * 3, 2);
|
|
163
|
+
return '<div class="treemap-item" style="flex:' + flex + ' ' + flex + ' 0;background:' + COLORS[i % COLORS.length] + ';" title="' + d.name + ': ${common_format_index.formatFileSize(0)}'.replace('0B', formatBytes(d.size)) + '"><span class="name">' + d.name.split('/').pop() + '</span><span class="size">' + formatBytes(d.size) + '</span></div>';
|
|
164
|
+
});
|
|
165
|
+
container.innerHTML = '<div class="treemap">' + items.join('') + '</div>';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function renderSunburst(container) {
|
|
169
|
+
const cx = 200, cy = 200, r1 = 80, r2 = 160;
|
|
170
|
+
let paths = '';
|
|
171
|
+
let startAngle = 0;
|
|
172
|
+
const sorted = [...chartData].sort((a, b) => b.size - a.size);
|
|
173
|
+
sorted.forEach((d, i) => {
|
|
174
|
+
const angle = totalSize > 0 ? (d.size / totalSize) * Math.PI * 2 : 0;
|
|
175
|
+
const endAngle = startAngle + angle;
|
|
176
|
+
const x1 = cx + r1 * Math.cos(startAngle);
|
|
177
|
+
const y1 = cy + r1 * Math.sin(startAngle);
|
|
178
|
+
const x2 = cx + r2 * Math.cos(startAngle);
|
|
179
|
+
const y2 = cy + r2 * Math.sin(startAngle);
|
|
180
|
+
const x3 = cx + r2 * Math.cos(endAngle);
|
|
181
|
+
const y3 = cy + r2 * Math.sin(endAngle);
|
|
182
|
+
const x4 = cx + r1 * Math.cos(endAngle);
|
|
183
|
+
const y4 = cy + r1 * Math.sin(endAngle);
|
|
184
|
+
const largeArc = angle > Math.PI ? 1 : 0;
|
|
185
|
+
paths += '<path d="M' + x1 + ',' + y1 + 'L' + x2 + ',' + y2 + 'A' + r2 + ',' + r2 + ' 0 ' + largeArc + ',1 ' + x3 + ',' + y3 + 'L' + x4 + ',' + y4 + 'A' + r1 + ',' + r1 + ' 0 ' + largeArc + ',0 ' + x1 + ',' + y1 + '" fill="' + COLORS[i % COLORS.length] + '" opacity="0.85" style="cursor:pointer" title="' + d.name + ': ' + formatBytes(d.size) + '"/>';
|
|
186
|
+
startAngle = endAngle;
|
|
187
|
+
});
|
|
188
|
+
container.innerHTML = '<div style="text-align:center"><svg width="400" height="400" viewBox="0 0 400 400">' + paths + '<circle cx="' + cx + '" cy="' + cy + '" r="' + (r1 - 1) + '" fill="white"/><text x="' + cx + '" y="' + cy + '" text-anchor="middle" dy="-6" font-size="14" font-weight="600">${l}</text><text x="' + cx + '" y="' + cy + '" text-anchor="middle" dy="14" font-size="11" fill="#888">total</text></svg></div>';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function renderList(container) {
|
|
192
|
+
const rows = [...chartData].sort((a, b) => b.size - a.size).map((d, i) => {
|
|
193
|
+
const pct = totalSize > 0 ? (d.size / totalSize * 100).toFixed(1) : '0.0';
|
|
194
|
+
return '<tr><td>' + (i+1) + '</td><td title="' + d.name + '">' + d.name + '</td><td>' + formatBytes(d.size) + '</td><td>' + formatBytes(d.gzipSize) + '</td><td>' + d.type + '</td><td><div class="bar-bg"><div class="bar" style="width:' + pct + '%"></div></div>' + pct + '%</td></tr>';
|
|
195
|
+
});
|
|
196
|
+
container.innerHTML = '<table><thead><tr><th>#</th><th>Name</th><th>Size</th><th>gzip</th><th>Type</th><th>Proportion</th></tr></thead><tbody>' + rows.join('') + '</tbody></table>';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function formatBytes(bytes) {
|
|
200
|
+
if (bytes < 1024) return bytes + 'B';
|
|
201
|
+
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + 'KB';
|
|
202
|
+
return (bytes / 1048576).toFixed(2) + 'MB';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function renderPieChart() {
|
|
206
|
+
const svg = document.getElementById('pieChart');
|
|
207
|
+
const legend = document.getElementById('pieLegend');
|
|
208
|
+
const cx = 100, cy = 100, r = 90;
|
|
209
|
+
let paths = '';
|
|
210
|
+
let startAngle = -Math.PI / 2;
|
|
211
|
+
const items = distributionData.slice(0, 8);
|
|
212
|
+
const otherPct = distributionData.slice(8).reduce((s, d) => s + d.percentage, 0);
|
|
213
|
+
if (otherPct > 0) items.push({ extension: '(other)', count: 0, totalSize: 0, percentage: otherPct });
|
|
214
|
+
|
|
215
|
+
items.forEach((d, i) => {
|
|
216
|
+
const angle = (d.percentage / 100) * Math.PI * 2;
|
|
217
|
+
const endAngle = startAngle + angle;
|
|
218
|
+
const x1 = cx + r * Math.cos(startAngle);
|
|
219
|
+
const y1 = cy + r * Math.sin(startAngle);
|
|
220
|
+
const x2 = cx + r * Math.cos(endAngle);
|
|
221
|
+
const y2 = cy + r * Math.sin(endAngle);
|
|
222
|
+
const largeArc = angle > Math.PI ? 1 : 0;
|
|
223
|
+
paths += '<path d="M' + cx + ',' + cy + 'L' + x1 + ',' + y1 + 'A' + r + ',' + r + ' 0 ' + largeArc + ',1 ' + x2 + ',' + y2 + 'Z" fill="' + COLORS[i % COLORS.length] + '" opacity="0.85"/>';
|
|
224
|
+
startAngle = endAngle;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
svg.innerHTML = '<svg width="200" height="200" viewBox="0 0 200 200">' + paths + '</svg>';
|
|
228
|
+
legend.innerHTML = items.map((d, i) => '<div class="legend-item"><div class="legend-color" style="background:' + COLORS[i % COLORS.length] + '"></div><span>' + d.extension + ' (' + d.percentage.toFixed(1) + '%)</span></div>').join('');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
renderChart('${d}');
|
|
232
|
+
renderPieChart();
|
|
233
|
+
<\/script>
|
|
234
|
+
</body>
|
|
235
|
+
</html>`}class $ extends factory_index.BasePlugin{analysisResult=null;getDefaultOptions(){return{outputFormat:"json",outputFile:"bundle-analysis",openAnalyzer:!1,sizeThreshold:100,topModules:20,compareWith:null,gzipSize:!0,excludeNodeModules:!1,excludePatterns:[],includeExtensions:[],defaultChartType:"treemap"}}validateOptions(){this.validator.field("outputFormat").enum(["json","html","both"]).field("openAnalyzer").boolean().field("sizeThreshold").number().minValue(0).field("topModules").number().minValue(1).field("gzipSize").boolean().field("excludeNodeModules").boolean().field("defaultChartType").enum(["treemap","sunburst","list"]).validate()}getPluginName(){return"bundle-analyzer"}getEnforce(){return"post"}addPluginHooks(s){s.writeBundle={order:"post",handler:async()=>{await this.safeExecute(()=>this.runAnalysis(),"\u5206\u6790\u6784\u5EFA\u4EA7\u7269")}}}async runAnalysis(){if(!this.viteConfig)return;const s=this.viteConfig.build.outDir;this.analysisResult=await analyzeBundle(s,this.options),this.options.compareWith&&await this.performComparison(),this.logSummary(),this.logWarnings(),await this.generateReports(s),this.options.openAnalyzer&&(this.options.outputFormat==="html"||this.options.outputFormat==="both")&&await this.openHtmlReport(s)}async performComparison(){if(!this.analysisResult||!this.options.compareWith)return;const s=await loadPreviousReport(this.options.compareWith);if(!s){this.logger.info(`\u672A\u627E\u5230\u5BF9\u6BD4\u62A5\u544A: ${this.options.compareWith}\uFF0C\u8DF3\u8FC7\u5BF9\u6BD4\u5206\u6790`);return}const e=compareWithPrevious(this.analysisResult,s);if(this.analysisResult.comparisonDiffs=e,e.length>0){const r=e.filter(l=>l.trend==="increased").length,n=e.filter(l=>l.trend==="decreased").length,t=e.filter(l=>l.trend==="added").length,d=e.filter(l=>l.trend==="removed").length;this.logger.info(`\u6784\u5EFA\u5BF9\u6BD4: ${r} \u4E2A\u589E\u5927, ${n} \u4E2A\u51CF\u5C0F, ${t} \u4E2A\u65B0\u589E, ${d} \u4E2A\u79FB\u9664`)}}async generateReports(s){if(!this.analysisResult)return;const{outputFormat:e,outputFile:r}=this.options;if(e==="json"||e==="both"){const n=await generateJsonReport(s,r,this.analysisResult);this.logger.info(`JSON \u62A5\u544A\u5DF2\u751F\u6210: ${n}`)}if(e==="html"||e==="both"){const n=await generateHtmlReport(s,r,this.analysisResult,{defaultChartType:this.options.defaultChartType});this.logger.info(`HTML \u62A5\u544A\u5DF2\u751F\u6210: ${n}`)}}async openHtmlReport(s){const e=`${s}/${this.options.outputFile}.html`;try{const{exec:r}=await import("node:child_process"),n=process.platform;r(n==="win32"?`start "" "${e}"`:n==="darwin"?`open "${e}"`:`xdg-open "${e}"`),this.logger.info(`\u5DF2\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u62A5\u544A: ${e}`)}catch{this.logger.warn(`\u65E0\u6CD5\u81EA\u52A8\u6253\u5F00\u6D4F\u89C8\u5668\uFF0C\u8BF7\u624B\u52A8\u6253\u5F00: ${e}`)}}logSummary(){if(!this.analysisResult)return;const{chunks:s,totalSize:e,totalGzipSize:r,analysisTime:n}=this.analysisResult;this.logger.success(`\u4EA7\u7269\u5206\u6790\u5B8C\u6210: ${s.length} \u4E2A chunk, \u603B\u4F53\u79EF: ${common_format_index.formatFileSize(e)} (gzip: ${common_format_index.formatFileSize(r)}), \u5206\u6790\u8017\u65F6: ${n}ms`);const t=s.filter(c=>c.type==="entry").length,d=s.filter(c=>c.type==="chunk").length,l=s.filter(c=>c.type==="asset").length;if((t>0||d>0||l>0)&&this.logger.info(` \u5165\u53E3: ${t} | \u4EE3\u7801\u5757: ${d} | \u8D44\u6E90: ${l}`),this.analysisResult.topModules.length>0){this.logger.info("\u4F53\u79EF Top 5 \u6A21\u5757:");const c=this.analysisResult.topModules.slice(0,5);for(let i=0;i<c.length;i++){const u=c[i],h=u.isNodeModule?"node_modules":"source";this.logger.info(` ${i+1}. ${common_format_index.formatFileSize(u.size)} (${h}) ${u.id}`)}}}logWarnings(){if(!(!this.analysisResult||this.analysisResult.warnings.length===0)){this.logger.warn(`\u53D1\u73B0 ${this.analysisResult.warnings.length} \u4E2A\u4F53\u79EF\u544A\u8B66:`);for(const s of this.analysisResult.warnings){const e=s.sizeKB>s.thresholdKB*2?"\u{1F534}":"\u{1F7E1}";this.logger.warn(` ${e} ${s.message}`)}}}getResult(){return this.analysisResult}}const bundleAnalyzer=factory_index.createPluginFactory($);exports.bundleAnalyzer=bundleAnalyzer;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { BasePluginOptions, PluginFactory } from '../../factory/index.cjs';
|
|
2
|
+
import 'vite';
|
|
3
|
+
import '../../shared/vite-plugin.CLr0ttuO.cjs';
|
|
4
|
+
import '../../shared/vite-plugin.DRRlWY8P.cjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 输出报告格式类型
|
|
8
|
+
*
|
|
9
|
+
* @typedef {('json' | 'html' | 'both')} BundleOutputFormat
|
|
10
|
+
* @description 支持的报告输出格式:
|
|
11
|
+
* - `json`: 生成 JSON 格式的分析报告
|
|
12
|
+
* - `html`: 生成包含可视化图表的 HTML 报告
|
|
13
|
+
* - `both`: 同时生成 JSON 和 HTML 报告
|
|
14
|
+
*/
|
|
15
|
+
type BundleOutputFormat = 'json' | 'html' | 'both';
|
|
16
|
+
/**
|
|
17
|
+
* 单个模块的统计信息
|
|
18
|
+
*
|
|
19
|
+
* @interface ModuleStats
|
|
20
|
+
* @description 记录构建产物中单个模块的详细统计数据
|
|
21
|
+
*/
|
|
22
|
+
interface ModuleStats {
|
|
23
|
+
/** 模块标识符(通常是模块路径或 ID) */
|
|
24
|
+
id: string;
|
|
25
|
+
/** 模块原始大小(字节) */
|
|
26
|
+
size: number;
|
|
27
|
+
/** 模块 gzip 压缩后大小(字节),仅在 gzipSize 开启时可用 */
|
|
28
|
+
gzipSize: number;
|
|
29
|
+
/** 所属 chunk 名称列表 */
|
|
30
|
+
chunks: string[];
|
|
31
|
+
/** 该模块的依赖模块 ID 列表 */
|
|
32
|
+
imports: string[];
|
|
33
|
+
/** 是否为入口模块 */
|
|
34
|
+
isEntry: boolean;
|
|
35
|
+
/** 是否来自 node_modules */
|
|
36
|
+
isNodeModule: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 单个 chunk 的统计信息
|
|
40
|
+
*
|
|
41
|
+
* @interface ChunkStats
|
|
42
|
+
* @description 记录构建产物中单个 chunk 的详细统计数据
|
|
43
|
+
*/
|
|
44
|
+
interface ChunkStats {
|
|
45
|
+
/** chunk 名称 */
|
|
46
|
+
name: string;
|
|
47
|
+
/** chunk 原始大小(字节) */
|
|
48
|
+
size: number;
|
|
49
|
+
/** chunk gzip 压缩后大小(字节),仅在 gzipSize 开启时可用 */
|
|
50
|
+
gzipSize: number;
|
|
51
|
+
/** chunk 包含的模块列表 */
|
|
52
|
+
modules: ModuleStats[];
|
|
53
|
+
/** chunk 类型标识(如 'entry'、'chunk'、'asset') */
|
|
54
|
+
type: 'entry' | 'chunk' | 'asset';
|
|
55
|
+
/** chunk 包含的文件数量 */
|
|
56
|
+
fileCount: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 文件类型分布统计项
|
|
60
|
+
*
|
|
61
|
+
* @interface FileTypeDistribution
|
|
62
|
+
* @description 按文件扩展名分类的体积分布统计
|
|
63
|
+
*/
|
|
64
|
+
interface FileTypeDistribution {
|
|
65
|
+
/** 文件扩展名(如 '.js', '.css') */
|
|
66
|
+
extension: string;
|
|
67
|
+
/** 该类型的文件数量 */
|
|
68
|
+
count: number;
|
|
69
|
+
/** 该类型的总大小(字节) */
|
|
70
|
+
totalSize: number;
|
|
71
|
+
/** 该类型的总体积占比(0-100) */
|
|
72
|
+
percentage: number;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 体积阈值告警信息
|
|
76
|
+
*
|
|
77
|
+
* @interface SizeWarning
|
|
78
|
+
* @description 当模块或 chunk 超过配置的体积阈值时生成的告警
|
|
79
|
+
*/
|
|
80
|
+
interface SizeWarning {
|
|
81
|
+
/** 告警类型:模块级别或 chunk 级别 */
|
|
82
|
+
level: 'module' | 'chunk';
|
|
83
|
+
/** 告警目标名称 */
|
|
84
|
+
name: string;
|
|
85
|
+
/** 实际大小(KB) */
|
|
86
|
+
sizeKB: number;
|
|
87
|
+
/** 阈值大小(KB) */
|
|
88
|
+
thresholdKB: number;
|
|
89
|
+
/** 告警消息 */
|
|
90
|
+
message: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 构建对比差异项
|
|
94
|
+
*
|
|
95
|
+
* @interface ComparisonDiff
|
|
96
|
+
* @description 两次构建之间同一模块/chunk 的体积变化
|
|
97
|
+
*/
|
|
98
|
+
interface ComparisonDiff {
|
|
99
|
+
/** 模块或 chunk 名称 */
|
|
100
|
+
name: string;
|
|
101
|
+
/** 上次构建大小(字节),不存在则为 -1 */
|
|
102
|
+
previousSize: number;
|
|
103
|
+
/** 本次构建大小(字节) */
|
|
104
|
+
currentSize: number;
|
|
105
|
+
/** 体积变化量(字节),正数表示增大 */
|
|
106
|
+
diff: number;
|
|
107
|
+
/** 变化百分比 */
|
|
108
|
+
diffPercentage: number;
|
|
109
|
+
/** 变化趋势:增大、减小、不变、新增、移除 */
|
|
110
|
+
trend: 'increased' | 'decreased' | 'unchanged' | 'added' | 'removed';
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 构建产物分析结果
|
|
114
|
+
*
|
|
115
|
+
* @interface BundleAnalysisResult
|
|
116
|
+
* @description 包含完整的构建产物分析数据,用于报告生成和日志输出
|
|
117
|
+
*/
|
|
118
|
+
interface BundleAnalysisResult {
|
|
119
|
+
/** 分析时间戳(ISO 格式) */
|
|
120
|
+
timestamp: string;
|
|
121
|
+
/** 构建产物总大小(字节) */
|
|
122
|
+
totalSize: number;
|
|
123
|
+
/** 构建产物 gzip 总大小(字节) */
|
|
124
|
+
totalGzipSize: number;
|
|
125
|
+
/** chunk 统计列表 */
|
|
126
|
+
chunks: ChunkStats[];
|
|
127
|
+
/** Top N 大模块排行 */
|
|
128
|
+
topModules: ModuleStats[];
|
|
129
|
+
/** 文件类型分布统计 */
|
|
130
|
+
fileTypeDistribution: FileTypeDistribution[];
|
|
131
|
+
/** 体积阈值告警列表 */
|
|
132
|
+
warnings: SizeWarning[];
|
|
133
|
+
/** 构建对比差异列表(仅在配置 compareWith 时可用) */
|
|
134
|
+
comparisonDiffs: ComparisonDiff[];
|
|
135
|
+
/** 分析耗时(毫秒) */
|
|
136
|
+
analysisTime: number;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 构建产物分析插件的配置选项
|
|
140
|
+
*
|
|
141
|
+
* @interface BundleAnalyzerOptions
|
|
142
|
+
* @extends {BasePluginOptions}
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* bundleAnalyzer({
|
|
147
|
+
* outputFormat: 'both',
|
|
148
|
+
* outputFile: 'bundle-analysis',
|
|
149
|
+
* openAnalyzer: true,
|
|
150
|
+
* sizeThreshold: 200,
|
|
151
|
+
* topModules: 30,
|
|
152
|
+
* gzipSize: true,
|
|
153
|
+
* compareWith: 'bundle-analysis-prev.json'
|
|
154
|
+
* })
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
interface BundleAnalyzerOptions extends BasePluginOptions {
|
|
158
|
+
/** 报告输出格式:json、html 或同时输出两者 */
|
|
159
|
+
outputFormat?: BundleOutputFormat;
|
|
160
|
+
/** 报告输出文件名(不含扩展名),默认 'bundle-analysis' */
|
|
161
|
+
outputFile?: string;
|
|
162
|
+
/** 是否在生成 HTML 报告后自动打开浏览器 */
|
|
163
|
+
openAnalyzer?: boolean;
|
|
164
|
+
/** 体积告警阈值(KB),超过此大小的 chunk 将产生告警 */
|
|
165
|
+
sizeThreshold?: number;
|
|
166
|
+
/** Top N 大模块排行数量 */
|
|
167
|
+
topModules?: number;
|
|
168
|
+
/** 用于对比的之前分析报告路径,设为 null 则不进行对比 */
|
|
169
|
+
compareWith?: string | null;
|
|
170
|
+
/** 是否计算 gzip 大小 */
|
|
171
|
+
gzipSize?: boolean;
|
|
172
|
+
/** 是否在分析中排除 node_modules 中的模块 */
|
|
173
|
+
excludeNodeModules?: boolean;
|
|
174
|
+
/** 需要排除的文件路径模式列表(支持 glob 模式) */
|
|
175
|
+
excludePatterns?: string[];
|
|
176
|
+
/** 需要包含的文件扩展名列表,为空则包含所有 */
|
|
177
|
+
includeExtensions?: string[];
|
|
178
|
+
/** HTML 报告中图表的默认展示形式 */
|
|
179
|
+
defaultChartType?: 'treemap' | 'sunburst' | 'list';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* 创建构建产物分析插件
|
|
184
|
+
*
|
|
185
|
+
* @function bundleAnalyzer
|
|
186
|
+
* @param {Partial<BundleAnalyzerOptions>} [options] - 插件配置选项
|
|
187
|
+
* @returns {Plugin} Vite 插件实例
|
|
188
|
+
*
|
|
189
|
+
* @description 在 Vite 构建完成后自动分析输出目录中的构建产物,
|
|
190
|
+
* 生成体积统计、模块排行、文件类型分布等关键指标,
|
|
191
|
+
* 支持 JSON 报告和 HTML 可视化图表,支持体积阈值告警和构建对比。
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```typescript
|
|
195
|
+
* // vite.config.ts
|
|
196
|
+
* import { bundleAnalyzer } from '@meng-xi/vite-plugin'
|
|
197
|
+
*
|
|
198
|
+
* export default defineConfig({
|
|
199
|
+
* plugins: [
|
|
200
|
+
* bundleAnalyzer({
|
|
201
|
+
* outputFormat: 'both',
|
|
202
|
+
* sizeThreshold: 200,
|
|
203
|
+
* topModules: 30,
|
|
204
|
+
* gzipSize: true,
|
|
205
|
+
* compareWith: 'bundle-analysis-prev.json',
|
|
206
|
+
* defaultChartType: 'treemap'
|
|
207
|
+
* })
|
|
208
|
+
* ]
|
|
209
|
+
* })
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
declare const bundleAnalyzer: PluginFactory<BundleAnalyzerOptions, BundleAnalyzerOptions>;
|
|
213
|
+
|
|
214
|
+
export { bundleAnalyzer };
|
|
215
|
+
export type { BundleAnalysisResult, BundleAnalyzerOptions, BundleOutputFormat, ChunkStats, ComparisonDiff, FileTypeDistribution, ModuleStats, SizeWarning };
|