// 这里导入webpack配置,我用对象的形式表示,当然entry属性上定义的文件要有
// const config = require("./webpack.config");
const config = { entry: './src/index.js' };
const { join, dirname } = require("path");
const { readFileSync, writeFileSync } = require("fs");
const reg = /(?<=from\s+['"])[\.\/\w]+(?=['"])/g;
class Complier {
constructor({ entry }) {
this.entry = entry;
}
run() {
writeFileSync('generateFile.js', generateCode(this.entry));
}
}
const parser = (entry, prePath = '.') => {
let temp = null;
const filePath = `./${join(prePath, entry)}`;
const code = Buffer.from(readFileSync(filePath)).toString();
const dependencies = {};
// 通过正则将当前文件es导入的模块识别并存到dependencies里,注释也会存起来,请不要写注释
while ((temp = reg.exec(code)) !== null) {
temp.forEach(filePath => {
dependencies[filePath] = `./${join(prePath, dirname(entry), filePath)}`;
})
}
return {
filePath,
dependencies,
code,
};
}
const makeDependenciesGraph = (entry) => {
const entryModule = parser(entry);
const graphArray = [entryModule];
// 利用广度遍历的思想遍历一遍
for (let i = 0; i < graphArray.length; i++) {
const { dependencies } = graphArray[i];
if (dependencies) {
for (let path in dependencies) {
graphArray.push(parser(path, dirname(entry)));
}
}
}
const graph = {};
graphArray.forEach(({ filePath, dependencies, code }) => {
graph[filePath] = {
dependencies,
code
};
});
return graph;
};
const generateCode = (entry) => {
const graph = JSON.stringify(makeDependenciesGraph(entry));
return `
(function (graph) {
function require(module) {
function localRequire(relativePath) {
return require(graph[module].dependencies[relativePath]);
}
var exports = {};
(function (require, exports, code) {
eval(code);
})(localRequire, exports, graph[module].code);
return exports;
}
require("${`./${join('.', entry).replace(/\\/g, '\\\\')}`}");
})(${graph});
`;
};
new Complier(config).run();