UNPKG

mp-lens

Version:

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

328 lines 17.4 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.findMiniProgramEntryPoints = findMiniProgramEntryPoints; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const debug_logger_1 = require("../utils/debug-logger"); /** * Finds potential entry points for a Mini Program project. * Includes app, pages, subPackages, and components found via usingComponents. * * @param projectRoot The absolute path to the root of the entire project. * @param miniappRoot The absolute path to the root of the miniapp source code (where app.json resides). * @returns A promise resolving to an array of entry file paths relative to the projectRoot. */ async function findMiniProgramEntryPoints(projectRoot, miniappRoot) { debug_logger_1.logger.debug('[EntryFinder] Starting Mini Program entry point analysis (incl. components recursively)...'); debug_logger_1.logger.debug(`[EntryFinder] Project Root: ${projectRoot}`); debug_logger_1.logger.debug(`[EntryFinder] MiniApp Root: ${miniappRoot}`); const entryPoints = new Set(); const processedComponentJsonPaths = new Set(); // Initialize cycle tracker // 1. Find and add global app files addImplicitGlobalFiles(projectRoot, miniappRoot, entryPoints); // 2. Process app.json globals (Pass tracker) const appJsonContent = processAppJsonGlobals(projectRoot, miniappRoot, entryPoints, processedComponentJsonPaths); // 3. Process Pages and SubPackages (requires appJsonContent) let pagesFound = []; if (appJsonContent) { pagesFound = processPagesAndSubPackages(projectRoot, miniappRoot, appJsonContent, entryPoints); } else { debug_logger_1.logger.warn('[EntryFinder] Skipping page/subpackage processing due to missing app.json content.'); } // 4. Process page-specific components (Pass tracker) if (pagesFound.length > 0) { processPageSpecificComponents(projectRoot, miniappRoot, pagesFound, entryPoints, processedComponentJsonPaths); } else { debug_logger_1.logger.debug('[EntryFinder] No pages found, skipping page-specific component processing.'); } debug_logger_1.logger.info(`[EntryFinder] Total potential entry points found (recursive components): ${entryPoints.size}`); return Array.from(entryPoints); } /** * Add standard Mini Program global files as entry points */ function addImplicitGlobalFiles(projectRoot, miniappRoot, entryPoints) { const implicitFiles = [ // App instance files 'app.js', 'app.ts', // Global styles 'app.wxss', // Configuration files 'project.config.json', 'sitemap.json', ]; for (const fileName of implicitFiles) { const filePath = path.resolve(miniappRoot, fileName); if (fs.existsSync(filePath)) { const relativePath = path.relative(projectRoot, filePath); entryPoints.add(relativePath); debug_logger_1.logger.debug(`[EntryFinder] Found global file: ${relativePath}`); } } // Special warning only for app.js/ts since it's essential const hasAppJs = fs.existsSync(path.resolve(miniappRoot, 'app.js')); const hasAppTs = fs.existsSync(path.resolve(miniappRoot, 'app.ts')); if (!hasAppJs && !hasAppTs) { debug_logger_1.logger.warn('[EntryFinder] Could not find app.js or app.ts in miniapp root.'); } } /** * Process app.json and extract global components */ function processAppJsonGlobals(projectRoot, miniappRoot, entryPoints, processedComponentJsonPaths) { let appJsonContent = null; try { // Directly resolve app.json instead of using resolveAppJson const appJsonPath = path.resolve(miniappRoot, 'app.json'); if (fs.existsSync(appJsonPath)) { // Read and parse the file directly appJsonContent = JSON.parse(fs.readFileSync(appJsonPath, 'utf8')); // Add app.json to entry points entryPoints.add(path.relative(projectRoot, appJsonPath)); debug_logger_1.logger.debug(`[EntryFinder] Added app.json: ${path.relative(projectRoot, appJsonPath)}`); // Process global components from app.json if ((appJsonContent === null || appJsonContent === void 0 ? void 0 : appJsonContent.usingComponents) && typeof appJsonContent.usingComponents === 'object') { debug_logger_1.logger.debug('[EntryFinder] Processing global usingComponents...'); for (const [alias, compPath] of Object.entries(appJsonContent.usingComponents)) { if (typeof compPath === 'string') { // Use recursive helper and pass tracker findComponentEntriesRecursively(projectRoot, alias, compPath, miniappRoot, miniappRoot, entryPoints, processedComponentJsonPaths); } } } return appJsonContent; } else { debug_logger_1.logger.warn('[EntryFinder] Could not find app.json in miniapp root.'); return null; } } catch (error) { debug_logger_1.logger.error(`[EntryFinder] Error processing app.json: ${error.message}`); return null; } } /** * Process pages and subpackages from app.json */ function processPagesAndSubPackages(projectRoot, miniappRoot, appJsonContent, entryPoints) { const pagesFound = []; const findPageEntriesAndStore = (pagePath, rootDir) => { if (typeof pagePath === 'string') { pagesFound.push({ pagePath, rootDir }); // Store page path and its root directory const pageJsEntry = path.resolve(rootDir, `${pagePath}.js`); const pageTsEntry = path.resolve(rootDir, `${pagePath}.ts`); if (fs.existsSync(pageJsEntry)) { entryPoints.add(path.relative(projectRoot, pageJsEntry)); debug_logger_1.logger.debug(`[EntryFinder] Found page entry: ${path.relative(projectRoot, pageJsEntry)}`); } else if (fs.existsSync(pageTsEntry)) { entryPoints.add(path.relative(projectRoot, pageTsEntry)); debug_logger_1.logger.debug(`[EntryFinder] Found page entry: ${path.relative(projectRoot, pageTsEntry)}`); } // Add WXML file check const pageWxml = path.resolve(rootDir, `${pagePath}.wxml`); if (fs.existsSync(pageWxml)) { entryPoints.add(path.relative(projectRoot, pageWxml)); debug_logger_1.logger.debug(`[EntryFinder] Found page template: ${path.relative(projectRoot, pageWxml)}`); } // Add WXSS file check const pageWxss = path.resolve(rootDir, `${pagePath}.wxss`); if (fs.existsSync(pageWxss)) { entryPoints.add(path.relative(projectRoot, pageWxss)); debug_logger_1.logger.debug(`[EntryFinder] Found page style: ${path.relative(projectRoot, pageWxss)}`); } const pageJson = path.resolve(rootDir, `${pagePath}.json`); if (fs.existsSync(pageJson)) { entryPoints.add(path.relative(projectRoot, pageJson)); debug_logger_1.logger.debug(`[EntryFinder] Found page config: ${path.relative(projectRoot, pageJson)}`); } } }; // Process main pages if ((appJsonContent === null || appJsonContent === void 0 ? void 0 : appJsonContent.pages) && Array.isArray(appJsonContent.pages)) { debug_logger_1.logger.debug(`[EntryFinder] Found ${appJsonContent.pages.length} main pages.`); appJsonContent.pages.forEach((pagePath) => findPageEntriesAndStore(pagePath, miniappRoot)); } else { debug_logger_1.logger.warn('[EntryFinder] No "pages" array found in app.json content.'); } // Process subPackages const subPackages = (appJsonContent === null || appJsonContent === void 0 ? void 0 : appJsonContent.subPackages) || (appJsonContent === null || appJsonContent === void 0 ? void 0 : appJsonContent.subpackages) || []; if (Array.isArray(subPackages) && subPackages.length > 0) { debug_logger_1.logger.debug(`[EntryFinder] Found ${subPackages.length} subPackages.`); subPackages.forEach((pkg) => { if (pkg && typeof pkg.root === 'string' && Array.isArray(pkg.pages)) { const subPkgRoot = path.resolve(miniappRoot, pkg.root); debug_logger_1.logger.debug(`[EntryFinder] Processing subPackage root: ${pkg.root}`); pkg.pages.forEach((pagePath) => findPageEntriesAndStore(pagePath, subPkgRoot)); } }); } return pagesFound; } /** * Process components used in page JSON files */ function processPageSpecificComponents(projectRoot, miniappRoot, pagesFound, entryPoints, processedComponentJsonPaths) { debug_logger_1.logger.debug(`[EntryFinder] Processing components for ${pagesFound.length} found pages...`); for (const { pagePath, rootDir } of pagesFound) { const pageJsonPath = path.resolve(rootDir, `${pagePath}.json`); if (fs.existsSync(pageJsonPath)) { try { const pageJsonContent = JSON.parse(fs.readFileSync(pageJsonPath, 'utf-8')); if ((pageJsonContent === null || pageJsonContent === void 0 ? void 0 : pageJsonContent.usingComponents) && typeof pageJsonContent.usingComponents === 'object') { const pageDir = path.dirname(pageJsonPath); debug_logger_1.logger.debug(`[EntryFinder] Processing page components for: ${pagePath}`); for (const [alias, compPath] of Object.entries(pageJsonContent.usingComponents)) { if (typeof compPath === 'string') { // Use recursive helper and pass tracker findComponentEntriesRecursively(projectRoot, alias, compPath, pageDir, miniappRoot, entryPoints, processedComponentJsonPaths); } } } } catch (e) { debug_logger_1.logger.warn(`[EntryFinder] Failed to read or parse page JSON: ${pageJsonPath}, Error: ${e.message}`); } } } } /** * Helper to find and add component entry files (.js, .ts, .json) recursively. * * @param projectRoot Absolute path to the project root. * @param componentAlias The alias used in usingComponents. * @param componentPathValue The path value from usingComponents. * @param definingJsonDir Absolute path to the directory containing the JSON file where the component was defined. * @param miniappRoot Absolute path to the miniapp root (for resolving absolute paths starting with '/'). * @param entryPoints The Set to add found entry points to. * @param processedComponentJsonPaths A Set to track processed component JSON paths for cycle detection. */ function findComponentEntriesRecursively(projectRoot, componentAlias, componentPathValue, definingJsonDir, miniappRoot, entryPoints, processedComponentJsonPaths) { // Ignore plugin protocols or npm packages for now if (componentPathValue.startsWith('plugin://') || componentPathValue.startsWith('plugin-private://') || componentPathValue.startsWith('npm:')) { debug_logger_1.logger.debug(`[EntryFinder] Skipping non-local component: ${componentAlias} -> ${componentPathValue}`); return; } let componentBasePath; if (componentPathValue.startsWith('/')) { // Absolute path within miniapp root componentBasePath = path.resolve(miniappRoot, componentPathValue.substring(1)); } else { // Relative path from the defining JSON's directory componentBasePath = path.resolve(definingJsonDir, componentPathValue); } // Always try to add .js and .ts files, considering both direct file and index file patterns const componentFilesToCheck = [ `${componentBasePath}.js`, // e.g., components/comp.js `${componentBasePath}.ts`, // e.g., components/comp.ts path.join(componentBasePath, 'index.js'), // e.g., components/comp/index.js path.join(componentBasePath, 'index.ts'), // e.g., components/comp/index.ts // Add WXML and WXSS file patterns `${componentBasePath}.wxml`, // e.g., components/comp.wxml `${componentBasePath}.wxss`, // e.g., components/comp.wxss path.join(componentBasePath, 'index.wxml'), // e.g., components/comp/index.wxml path.join(componentBasePath, 'index.wxss'), // e.g., components/comp/index.wxss ]; for (const compFile of componentFilesToCheck) { if (fs.existsSync(compFile)) { const relativePath = path.relative(projectRoot, compFile); // Avoid adding duplicates if both comp.js and comp/index.js exist (though unlikely) if (!entryPoints.has(relativePath)) { entryPoints.add(relativePath); debug_logger_1.logger.debug(`[EntryFinder] Found component script entry: ${componentAlias} -> ${relativePath}`); } } } // --- Recursive part: Process the component's JSON file --- // // Check both direct json and index.json patterns const potentialJsonPaths = [ `${componentBasePath}.json`, path.join(componentBasePath, 'index.json'), ]; let actualComponentJsonPath = null; for (const p of potentialJsonPaths) { if (fs.existsSync(p)) { actualComponentJsonPath = p; debug_logger_1.logger.debug(`[EntryFinder] Found component JSON config at: ${actualComponentJsonPath}`); break; // Found the primary JSON config } } if (!actualComponentJsonPath) { // No JSON file found using either pattern, cannot find nested components debug_logger_1.logger.debug(`[EntryFinder] No JSON config found for component base path: ${componentBasePath}`); return; } // Cycle detection using the actual found path if (processedComponentJsonPaths.has(actualComponentJsonPath)) { debug_logger_1.logger.debug(`[EntryFinder] Already processed component JSON (cycle detected?): ${actualComponentJsonPath}`); return; } // Mark this component JSON as processed *before* recursive calls processedComponentJsonPaths.add(actualComponentJsonPath); debug_logger_1.logger.debug(`[EntryFinder] Processing component JSON: ${actualComponentJsonPath}`); // Add the actual JSON file itself as an entry point entryPoints.add(path.relative(projectRoot, actualComponentJsonPath)); // Read and parse the component's JSON for its own usingComponents try { const componentJsonContent = JSON.parse(fs.readFileSync(actualComponentJsonPath, 'utf-8')); if ((componentJsonContent === null || componentJsonContent === void 0 ? void 0 : componentJsonContent.usingComponents) && typeof componentJsonContent.usingComponents === 'object') { // Use the directory of the actual JSON file found const componentDir = path.dirname(actualComponentJsonPath); debug_logger_1.logger.debug(`[EntryFinder] Found nested usingComponents in: ${componentAlias} (${actualComponentJsonPath})`); for (const [nestedAlias, nestedCompPath] of Object.entries(componentJsonContent.usingComponents)) { if (typeof nestedCompPath === 'string') { // Recursive call findComponentEntriesRecursively(projectRoot, nestedAlias, nestedCompPath, componentDir, // Resolve nested components relative to *this* component's dir miniappRoot, entryPoints, processedComponentJsonPaths); } } } } catch (e) { debug_logger_1.logger.warn(`[EntryFinder] Failed to read or parse component JSON: ${actualComponentJsonPath}, Error: ${e.message}`); } } //# sourceMappingURL=entry-finder.js.map