1. 编写的目的
自己写一个mini版本的webpck ,从而感受webpack内部的原理
2. 需要的工具
1. @babel/parser 把文件编译成抽象语法树ast
2. @babel/traverse 获取依赖
3. @babel/core transformFromAst 方法解析ast的es6 为es5
4. @babel/preset-env 设置babel环境
3. 代码
1. webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename:'main.js'
}
}
2. package.json 的script 部分
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"debug": "node --inspect-brk ./myWebpack/index.js",
"build": "node ./myWebpack/index.js"
},
3. 主代码
// index.js
const Compiler = require('./Compile.js');
const config = require('../webpack.config.js');
function myWebpack(config){
return new Compiler(config);
};
myWebpack(config).run();
// compile.js
const fs = require('fs');
const parser = require('./paser.js');
const {getEntrtFile, getAst, getDeps,getCode} = parser;
const path = require('path');
const { file } = require('@babel/types');
class Compiler{
constructor(config){
this.options = config;
this.modules = {}; // 存储各个模块
}
run(){
const filePath = path.resolve(process.cwd(),this.options.entry);
// 拿到文件
this.build(filePath, this.modules);
this.generate(this.modules, filePath); // 打包到一个bundle中去
}
build(filePath,container){
const entryFile = getEntrtFile(filePath);
// 获得ast
const ast = getAst(entryFile);
// 收集依赖
const deps = getDeps(ast, filePath);
// 收集代码
const code = getCode(ast);
const fileInfo = {
deps,
code,
filePath
}
container[filePath] = fileInfo;
if(Object.keys(deps).length){
Object.keys(deps).forEach(item=>{
!container[deps[item]] && this.build(deps[item],container)
});
};
}
generate(modules){
const distPath = path.resolve(this.options.output.path, this.options.output.filename);
let str = '';
// 伪代码 可能需要把ast的解析 想办法解析相对路径才行
Object.keys(modules).forEach(item=>{
str += modules[item].code;
});
// 最后插入代码
fs.writeFileSync(distPath, str, 'utf-8');
}
}
module.exports = Compiler;
// paser.js
const fs = require('fs');
const path = require('path');
const babelParser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const {transformFromAst} = require('@babel/core');
const parser = {
// 拿到文件
getEntrtFile(filePath){
return fs.readFileSync(filePath, 'utf-8');
},
// 获得ast
getAst(entryFile){
// 在ast的program的body属性中有四个node 代表有几行代码
return babelParser.parse(entryFile,{sourceType: 'module'})
},
//收集依赖
getDeps(ast,filePath){
// 获得文件目录相对路径
const dirName = path.dirname(filePath);
// 收集依赖 type: "ImportDeclaration" 就是引入的依赖
const deps = {}
traverse(ast, {
ImportDeclaration({node}){
const raletivePath = node.source.value;
const absolutePath = path.resolve(dirName,raletivePath);
deps[raletivePath] = absolutePath;
}
});
return deps;
},
//获取astes6编译后的es5代码
getCode(ast){
const {code} = transformFromAst(ast,null,{
presets: ['@babel/preset-env']
})
return code
}
}
module.exports = parser;