mini webpack实现
Favori,
图:Amrit Pal Singh
实现
废话补多说,直接上代码
const fs = require("fs");
const path = require("path");
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const babel = require("@babel/core");
const options = require("./mini-webpack.config");
class MiniWebpack {
constructor(options) {
this.options = options;
}
parse = (filename) => {
// 读取文件
const fileBuffer = fs.readFileSync(filename, "utf-8");
// 转换成抽象语法树
const ast = parser.parse(fileBuffer, { sourceType: "module" });
const dependencies = {};
// 遍历抽象语法树
traverse(ast, {
// 处理ImportDeclaration节点
ImportDeclaration({ node }) {
const dirname = path.dirname(filename);
const newDirname = "./" + path.join(dirname, node.source.value).replace("\\", "/");
dependencies[node.source.value] = newDirname;
},
});
// 将抽象语法树转换成代码
const { code } = babel.transformFromAst(ast, null, {
presets: ["@babel/preset-env"],
});
return {
filename,
dependencies,
code,
};
};
analyse = (entry) => {
// 解析入口文件
const entryModule = this.parse(entry);
const graphArray = [entryModule];
// 循环解析模块,保存信息
for (let i = 0; i < graphArray.length; ++i) {
const { dependencies } = graphArray[i];
Object.keys(dependencies).forEach((filename) => {
graphArray.push(this.parse(dependencies[filename]));
});
}
const graph = {};
// 生成依赖图谱对象
graphArray.forEach(({ filename, dependencies, code }) => {
graph[filename] = {
dependencies,
code,
};
});
return graph;
};
generate = (graph, entry) => {
return `
(function(graph){
function require(filename){
function localRequire(relativePath){
return require(graph[filename].dependencies[relativePath]);
}
const exports = {};
(function(require, exports, code){
eval(code);
})(localRequire, exports, graph[filename].code)
return exports;
}
require('${entry}');
})(${graph})
`;
};
fileOutput = (output, code) => {
const { path: dirPath, filename } = output;
const outputPath = path.join(dirPath, filename);
// 如果没有文件夹的话,生成文件夹
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath);
}
// 写入文件中
fs.writeFileSync(outputPath, code, "utf-8");
};
run = () => {
const { entry, output } = this.options;
const graph = this.analyse(entry);
// stringify一下依赖图谱对象,防止在模板字符串中调用toString()返回[object Object]
const graphStr = JSON.stringify(graph);
const code = this.generate(graphStr, entry);
this.fileOutput(output, code);
};
}
const miniWebpack = new MiniWebpack(options);
miniWebpack.run();
可以在 codesadebox 里测试
Terminal 里执行 node main.js