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;
|