mp-lens
Version:
微信小程序分析工具 (Unused Code, Dependencies, Visualization)
328 lines • 17.4 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;
};
})();
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