Files

173 lines
6.5 KiB
JavaScript
Raw Permalink Normal View History

2025-11-14 11:39:33 +08:00
const path = require('path');
const fs = require('fs-extra');
const htmlparser2 = require('htmlparser2');
const { MpComponentFileExtension } = require('./constant');
const { transformScript, resolveToContext } = require('./utils.js');
const { parse } = require('@babel/parser');
const { default: traverse } = require('@babel/traverse');
class CollectDependency {
constructor (context, readFileSync, existsSync, addExtension) {
this.context = context;
this.existsSync = existsSync || fs.existsSync.bind(fs);
this.readFileSync = readFileSync || fs.readFileSync.bind(fs);
this.addExtension = addExtension;
}
getWxCssDeps (file) {
const deps = [];
const dirName = path.dirname(file);
let content = this.readFileSync(file, 'utf-8');
if (content instanceof Buffer) {
content = content.toString('utf-8');
}
const importRegExp = /@import\s*['"](.+)['"];*/g;
let matched;
while ((matched = importRegExp.exec(content)) !== null) {
if (!matched[1]) {
continue;
}
const wxssFile = resolveToContext(dirName, matched[1], this.context);
if (this.existsSync(wxssFile)) {
deps.push(wxssFile);
}
}
return deps;
};
getWxHtmlDeps (file) {
const deps = [];
const dirName = path.dirname(file);
let content = this.readFileSync(file, 'utf-8');
if (content instanceof Buffer) {
content = content.toString('utf-8');
}
// https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/import.html
// WXML 提供两种文件引用方式import和include。
const targetTags = ['import', 'include', 'wxs'];
const existsSync = this.existsSync;
const htmlParser = new htmlparser2.Parser({
onopentag (name, attribs = {}) {
if (!targetTags.includes(name)) {
return;
}
const { src } = attribs;
if (!src) {
return;
}
const wxmlFile = resolveToContext(dirName, src, this.context);
console.log(wxmlFile);
if (existsSync(wxmlFile)) {
deps.push(wxmlFile);
}
},
});
htmlParser.write(content);
htmlParser.end();
return deps;
}
getJsonDeps (file) {
const deps = [];
const dirName = path.dirname(file);
let fileContent = this.readFileSync(file);
if (fileContent && fileContent instanceof Buffer) {
fileContent = fileContent.toString('utf-8');
}
if (!fileContent || !(fileContent.trim())) {
return [];
}
fileContent = JSON.parse(fileContent);
const usingComponents = fileContent.usingComponents;
if (usingComponents && typeof usingComponents === 'object') {
Object.values(usingComponents).forEach((component) => {
component = resolveToContext(dirName, component, this.context);
// 每个组件都需要判断 js/json/wxml/wxss 文件是否存在
MpComponentFileExtension.forEach((ext) => {
const file = this.addExtension(component, ext);
if (this.existsSync(file)) {
deps.push(file);
}
});
});
}
return deps;
}
getJsDeps (file) {
const deps = [];
const dirName = path.dirname(file);
// 读取 js 文件内容
let content = this.readFileSync(file, 'utf-8');
if (content instanceof Buffer) {
content = content.toString('utf-8');
}
// 将代码转化为 AST
const ast = parse(content, { sourceType: 'module', plugins: ['exportDefaultFrom'] });
// 遍历 AST
traverse(ast, {
ImportDeclaration: ({ node }) => {
// 获取 import from 地址
const { value } = node.source;
const jsFile = transformScript(dirName, value, this.existsSync);
if (jsFile) {
deps.push(jsFile);
}
},
Property: (nodePath) => {
const parentPath = nodePath.parentPath;
const callee = parentPath && parentPath.parent && parentPath.parent.callee;
if (!callee || callee.name !== 'Component') {
return;
}
const relationsNodes = nodePath.container.filter(item => item.key.name === 'relations');
if (!relationsNodes.length) {
return;
}
const relationsNode = relationsNodes[0];
const propertyNodes = relationsNode.value.properties || [];
let allRelationsComponents = propertyNodes.map(filePathNode => {
if (!filePathNode.key.value) {
return '';
}
return path.resolve(dirName, filePathNode.key.value);
});
allRelationsComponents = allRelationsComponents.filter(item => item);
allRelationsComponents.forEach(component => {
MpComponentFileExtension.forEach((ext) => {
const file = this.addExtension(component, ext);
if (this.existsSync(file)) {
deps.push(file);
}
});
});
},
ExportNamedDeclaration: ({ node }) => {
if (!node.source) {
return;
}
// 获取 export from 地址
const { value } = node.source;
const jsFile = transformScript(dirName, value, this.existsSync);
if (jsFile) {
deps.push(jsFile);
}
},
CallExpression: ({ node }) => {
if ((node.callee.name && node.callee.name === 'require') && node.arguments.length >= 1) {
// 获取 require 地址
const [{ value }] = node.arguments;
const jsFile = transformScript(dirName, value, this.existsSync);
if (jsFile) {
deps.push(jsFile);
}
}
},
});
return deps;
}
}
module.exports = CollectDependency;