mp-lens
Version:
微信小程序分析工具 (Unused Code, Dependencies, Visualization)
243 lines • 11.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.diffBundle = diffBundle;
const chalk_1 = __importDefault(require("chalk"));
const fs = __importStar(require("fs"));
const glob_1 = require("glob");
const path = __importStar(require("path"));
const analyzer_1 = require("../analyzer/analyzer");
const command_init_1 = require("../utils/command-init");
const debug_logger_1 = require("../utils/debug-logger");
const errors_1 = require("../utils/errors");
const git_helper_1 = require("../utils/git-helper");
const imageExtensions = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp'];
function formatBytes(bytes, includeSign = false) {
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (Math.abs(bytes) < 1e-9) {
// Treat as zero
return includeSign ? '+0 Bytes' : '0 Bytes';
}
const i = Math.floor(Math.log(Math.abs(bytes)) / Math.log(k));
const value = parseFloat((bytes / Math.pow(k, i)).toFixed(2));
let prefix = '';
if (includeSign) {
if (value > 0) {
prefix = '+';
}
// Negative sign is part of 'value' if bytes is negative
}
return `${prefix}${value} ${sizes[i]}`;
}
async function getProjectPackageSizes(cliOptions, projectRoot) {
const analysisSpecificCliOptions = { ...cliOptions, project: projectRoot };
const context = await (0, command_init_1.initializeCommandContext)(analysisSpecificCliOptions);
const files = new Map();
let totalSize = 0;
// 1. Analyze assets (images) using glob
if (context.miniappRoot) {
const imagePattern = `**/*.{${imageExtensions.join(',')}}`;
debug_logger_1.logger.debug(`开始使用glob模式扫描图片资源: ${imagePattern} 于目录: ${context.miniappRoot}`);
try {
const assetFiles = (0, glob_1.sync)(imagePattern, {
cwd: context.miniappRoot,
nodir: true,
absolute: false, // Get paths relative to cwd
});
debug_logger_1.logger.debug(`Glob扫描到 ${assetFiles.length} 个潜在图片资源文件。`);
for (const relativeAssetPath of assetFiles) {
const absoluteAssetPath = path.join(context.miniappRoot, relativeAssetPath);
try {
const stats = fs.statSync(absoluteAssetPath);
if (stats.isFile()) {
files.set(relativeAssetPath, stats.size);
}
}
catch (err) {
debug_logger_1.logger.warn(`无法获取资源文件 ${absoluteAssetPath} 的状态: ${err instanceof Error ? err.message : String(err)}`);
}
}
}
catch (globError) {
debug_logger_1.logger.error(`扫描图片资源时发生错误: ${globError instanceof Error ? globError.message : String(globError)}`);
}
}
else {
debug_logger_1.logger.warn('miniappRoot 未定义,跳过图片资源扫描。');
}
// 2. Analyze project structure (non-assets and non-globbed assets)
const { projectStructure, reachableNodeIds } = await (0, analyzer_1.analyzeProject)(projectRoot, {
fileTypes: context.fileTypes,
excludePatterns: context.exclude,
essentialFiles: context.essentialFilesList,
verbose: context.verbose,
verboseLevel: context.verboseLevel,
miniappRoot: context.miniappRoot,
appJsonPath: context.appJsonPath,
appJsonContent: context.appJsonContent,
includeAssets: false, // Key change: analyzeProject will not primarily look for assets
});
projectStructure.nodes.forEach((node) => {
var _a, _b;
if (reachableNodeIds.has(node.id) &&
((_a = node.properties) === null || _a === void 0 ? void 0 : _a.absolutePath) && // Ensure it's a file node
((_b = node.properties) === null || _b === void 0 ? void 0 : _b.fileSize) !== undefined) {
const nodePath = node.id; // node.id is relative path for file nodes
// If this path is NOT already in our 'files' map (i.e., it wasn't added by the asset glob scan),
// then add it from analyzeProject. This prioritizes glob-scanned assets.
if (!files.has(nodePath)) {
files.set(nodePath, node.properties.fileSize);
}
}
});
// 3. Calculate total size and file count from the 'files' map
for (const size of files.values()) {
totalSize += size;
}
return {
totalSize,
totalFiles: files.size,
files,
};
}
async function diffBundle(cliOptions, cmdOptions) {
debug_logger_1.logger.info('开始分析包大小变化...');
const projectRoot = cliOptions.project;
if (!(0, git_helper_1.isGitRepository)(projectRoot)) {
throw new errors_1.HandledError('当前目录不是一个 Git 仓库。请在 Git 仓库内运行此命令。');
}
const baseRef = cmdOptions.base || 'master';
const targetRef = cmdOptions.target || 'HEAD';
debug_logger_1.logger.info(`对比基准 (Base): ${baseRef}`);
debug_logger_1.logger.info(`对比目标 (Target): ${targetRef}`);
let baseSizes;
let targetSizes;
const gitManager = new git_helper_1.GitSwitchManager(projectRoot);
try {
// --- Analyze Target ---
if (targetRef !== 'HEAD' && targetRef !== gitManager.getOriginalBranch()) {
if (!(0, git_helper_1.isWorkingDirectoryClean)(projectRoot)) {
throw new errors_1.HandledError('工作区不干净,请提交或暂存更改后再切换到其他提交进行对比。');
}
if (!(0, git_helper_1.branchOrCommitExists)(projectRoot, targetRef)) {
throw new errors_1.HandledError(`目标提交或分支 '${targetRef}' 不存在。`);
}
debug_logger_1.logger.info(`准备分析目标: ${targetRef}`);
gitManager.switchTo(targetRef);
}
else {
debug_logger_1.logger.info(`直接分析当前状态作为目标 (${targetRef})`);
}
targetSizes = await getProjectPackageSizes(cliOptions, projectRoot);
// --- Analyze Base ---
if (baseRef !== gitManager.getCurrentBranch()) {
if (!(0, git_helper_1.isWorkingDirectoryClean)(projectRoot)) {
debug_logger_1.logger.warn('切换到基准前检测到工作区不干净,这可能导致分析不准确。');
}
if (!(0, git_helper_1.branchOrCommitExists)(projectRoot, baseRef)) {
throw new errors_1.HandledError(`基准提交或分支 '${baseRef}' 不存在。`);
}
debug_logger_1.logger.info(`准备分析基准: ${baseRef}`);
gitManager.switchTo(baseRef);
}
baseSizes = await getProjectPackageSizes(cliOptions, projectRoot);
}
finally {
gitManager.restore();
}
// --- Compare and Display Results ---
console.log(chalk_1.default.bold('\n📊 包大小差异对比结果:'));
console.log(chalk_1.default.dim(` 基准 (Base): ${baseRef}`));
console.log(chalk_1.default.dim(` 目标 (Target): ${targetRef}\n`));
const sizeDiff = targetSizes.totalSize - baseSizes.totalSize;
const filesDiff = targetSizes.totalFiles - baseSizes.totalFiles;
console.log(` 总包大小: ${formatBytes(baseSizes.totalSize)} -> ${formatBytes(targetSizes.totalSize)} (${formatBytes(sizeDiff, true)})`);
console.log(` 总文件数: ${baseSizes.totalFiles} -> ${targetSizes.totalFiles} (${filesDiff > 0 ? '+' : ''}${filesDiff})`);
console.log(chalk_1.default.bold('\n📄 文件级别变化明细:'));
const changes = [];
// Find added and modified files
for (const [file, newSize] of targetSizes.files.entries()) {
if (baseSizes.files.has(file)) {
const oldSize = baseSizes.files.get(file);
if (oldSize !== newSize) {
changes.push({
type: 'modified',
file,
oldSize,
newSize,
impact: newSize - oldSize, // Actual difference
});
}
}
else {
changes.push({ type: 'added', file, size: newSize, impact: newSize });
}
}
// Find deleted files
for (const [file, oldSize] of baseSizes.files.entries()) {
if (!targetSizes.files.has(file)) {
changes.push({ type: 'deleted', file, size: oldSize, impact: -oldSize }); // Negative impact
}
}
// Sort changes by impact, descending (largest positive first, largest negative last)
changes.sort((a, b) => b.impact - a.impact);
if (changes.length === 0) {
console.log(' 所有文件大小均未发生变化。');
}
else {
changes.forEach((change) => {
switch (change.type) {
case 'added': {
console.log(chalk_1.default.green(` + ${change.file} (${formatBytes(change.size)})`));
break;
}
case 'deleted': {
console.log(chalk_1.default.red(` - ${change.file} (-${formatBytes(change.size)})`));
break;
}
case 'modified': {
const diff = change.newSize - change.oldSize;
console.log(chalk_1.default.yellow(` ~ ${change.file} (${formatBytes(change.oldSize)} -> ${formatBytes(change.newSize)}, ${formatBytes(diff, true)})`));
break;
}
}
});
}
}
//# sourceMappingURL=diffBundle.js.map