UNPKG

mp-lens

Version:

微信小程序分析工具 (Unused Code, Dependencies, Visualization)

243 lines 11.2 kB
"use strict"; 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