mp-lens
Version:
微信小程序分析工具 (Unused Code, Dependencies, Visualization)
176 lines • 7.22 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.WXMLParser = void 0;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Could not find a declaration file for module '@wxml/parser'
const parser_1 = require("@wxml/parser");
const debug_logger_1 = require("../utils/debug-logger");
const wxml_path_1 = require("../utils/wxml-path");
/**
* Parser for WXML files that finds dependencies to other files using AST parsing.
*
* Path resolution rules for WeChat Mini Program WXML files:
* 1. Paths starting with '/' are relative to the mini program root
* Example: <import src="/templates/header.wxml" />
*
* 2. Paths starting with './' or '../' are relative to the current file's directory
* Example: <import src="../templates/header.wxml" />
*
* 3. Paths with no prefix (like "templates/header.wxml") should be treated as relative
* to the current file's directory, equivalent to adding a './' prefix.
* This parser automatically adds the './' prefix to follow Mini Program conventions.
*/
class WXMLParser {
constructor() {
// No dependencies needed for pure text analysis
}
async parse(content, filePath) {
try {
const dependencies = new Set();
// Parse WXML content to AST
const ast = (0, parser_1.parse)(content);
this.processImportIncludeTags(ast, dependencies);
this.processWxsTags(ast, dependencies);
this.processImageSources(ast, dependencies);
// NOTE: processCustomComponents is intentionally omitted as component
// dependencies are defined in JSON files.
return Array.from(dependencies);
}
catch (e) {
debug_logger_1.logger.warn(`Error parsing WXML file ${filePath}: ${e.message}`);
throw e; // Re-throw
}
}
/**
* Processes import and include tags to extract template dependencies
*/
processImportIncludeTags(ast, dependencies) {
this.findImportIncludeTags(ast, (path) => {
const normalizedPath = (0, wxml_path_1.normalizeWxmlImportPath)(path);
debug_logger_1.logger.debug(`Found import/include: ${path} -> normalized: ${normalizedPath}`);
dependencies.add(normalizedPath);
});
}
/**
* Processes wxs tags to extract WXS script dependencies
*/
processWxsTags(ast, dependencies) {
this.findWxsTags(ast, (path) => {
const normalizedPath = (0, wxml_path_1.normalizeWxmlImportPath)(path);
debug_logger_1.logger.debug(`Found wxs: ${path} -> normalized: ${normalizedPath}`);
dependencies.add(normalizedPath);
});
}
/**
* Processes image tags to extract image dependencies
*/
processImageSources(ast, dependencies) {
this.findImageTags(ast, (src) => {
// Skip data URIs, remote URLs, and template expressions
if (src.startsWith('data:') || /^(http|https):\/\//.test(src) || /{{.*?}}/.test(src)) {
return;
}
const normalizedPath = (0, wxml_path_1.normalizeWxmlImportPath)(src);
debug_logger_1.logger.debug(`Found image: ${src} -> normalized: ${normalizedPath}`);
dependencies.add(normalizedPath);
});
}
/**
* Recursively finds import and include tags in the AST
*/
findImportIncludeTags(ast, callback) {
var _a;
if (ast.type === 'WXElement' && (ast.name === 'import' || ast.name === 'include')) {
// Find src attribute from startTag.attributes
const attrs = (_a = ast.startTag) === null || _a === void 0 ? void 0 : _a.attributes;
if (attrs && Array.isArray(attrs)) {
const srcAttr = attrs.find((attr) => attr.key === 'src');
if (srcAttr && srcAttr.value) {
callback(srcAttr.value);
}
}
}
// Recursively process children
if (ast.type === 'WXElement' && Array.isArray(ast.children)) {
for (const child of ast.children) {
this.findImportIncludeTags(child, callback);
}
}
// Handle Program/body
if (ast.type === 'Program' && Array.isArray(ast.body)) {
for (const node of ast.body) {
this.findImportIncludeTags(node, callback);
}
}
}
/**
* Recursively finds wxs tags in the AST
*/
findWxsTags(ast, callback) {
var _a, _b;
// Handle WXScript (wxs tags)
if (ast.type === 'WXScript' && ast.name === 'wxs') {
// Find src attribute from startTag.attributes
const attrs = (_a = ast.startTag) === null || _a === void 0 ? void 0 : _a.attributes;
if (attrs && Array.isArray(attrs)) {
const srcAttr = attrs.find((attr) => attr.key === 'src');
if (srcAttr && srcAttr.value) {
callback(srcAttr.value);
}
}
}
// Also handle WXElement in case wxs is parsed as a regular element
if (ast.type === 'WXElement' && ast.name === 'wxs') {
// Find src attribute from startTag.attributes
const attrs = (_b = ast.startTag) === null || _b === void 0 ? void 0 : _b.attributes;
if (attrs && Array.isArray(attrs)) {
const srcAttr = attrs.find((attr) => attr.key === 'src');
if (srcAttr && srcAttr.value) {
callback(srcAttr.value);
}
}
}
// Recursively process children
if (ast.type === 'WXElement' && Array.isArray(ast.children)) {
for (const child of ast.children) {
this.findWxsTags(child, callback);
}
}
// Handle Program/body
if (ast.type === 'Program' && Array.isArray(ast.body)) {
for (const node of ast.body) {
this.findWxsTags(node, callback);
}
}
}
/**
* Recursively finds image tags in the AST
*/
findImageTags(ast, callback) {
var _a;
if (ast.type === 'WXElement' && ast.name === 'image') {
// Find src attribute from startTag.attributes
const attrs = (_a = ast.startTag) === null || _a === void 0 ? void 0 : _a.attributes;
if (attrs && Array.isArray(attrs)) {
const srcAttr = attrs.find((attr) => attr.key === 'src');
if (srcAttr && srcAttr.value) {
callback(srcAttr.value);
}
}
}
// Recursively process children
if (ast.type === 'WXElement' && Array.isArray(ast.children)) {
for (const child of ast.children) {
this.findImageTags(child, callback);
}
}
// Handle Program/body
if (ast.type === 'Program' && Array.isArray(ast.body)) {
for (const node of ast.body) {
this.findImageTags(node, callback);
}
}
}
}
exports.WXMLParser = WXMLParser;
//# sourceMappingURL=wxml-parser.js.map