代码语言:javascript复制
todolist
- 将ES6转换成ES5语法
- 通过babylon生成AST
- 通过babel-core将AST重新生成源码
- 分析模块之间的依赖关系
- 通过 babel-traverse 的 ImportDeclaration方法获取依赖属性
- 生成js文件可以在浏览器中运行
基础准备
给自定义webpack 起个名字 x-webpack
安装基础插件
npm install --dev-save @babel/core @babel/preset-env @babel/traverse babylon magic-string
package.json
代码语言:javascript复制{
"name": "x-webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"bin": {
"x-webpack": "./lib/index.js"
},
"keywords": [],
"author": "",
"license": "ISC"
}
本地安装软链
- x-webpack目录下,执行 npm link
指定lib/index.js 文件类型(用于bin的执行)
- #!/usr/bin/env node
x-webpack 开整
- index.js,执行文件
const { resolve } = require('path')
const Compiler = require('./compiler')
const defaultConfig = require('./default_config') // 默认配置
const customConfig = require(resolve('./x.config.js')) // 加载个人项目配置,这里约定配置文件为 x.config.js
const config = { ...defaultConfig, ... customConfig }
// 开启 xwebpack 打包
new Compiler(config).run()
- default_config.js 默认配置
module.exports = {
entry: './src/index.js',
output: {
filename: 'boundle.js'
}
}
- compiler.js 编译器
const path = require('path')
const fs = require('fs')
const { getAST, getDependencis, transform } = require('./parser')
module.exports = class Compiler{
constructor(options) {
const { entry, output } = options
this.entry = entry
this.output = output
this.modules = [] // 收集的所有模块
this.root = process.cwd() // 就是x-webpack-demo目录
}
run() {
// 搜集依赖
const entryModule = this.buildModule(this.entry, true)
this.modules.push(entryModule)
this.modules.map((_module) => {
_module.dependencies.map((dependency) => {
this.modules.push(this.buildModule(dependency))
})
})
// 生成文件
this.emit()
}
buildModule(filename, isEntry) {
let ast;
if (isEntry) {
ast = getAST(filename)
} else {
// process.cwd() 表示根目录
let absolutePath = path.join(this.root, './src', filename)
ast = getAST(absolutePath)
}
return {
filename,
dependencies: getDependencis(ast),
transformCode: transform(ast)
}
}
emit() {
const outputPath = path.join(this.output.path, this.output.filename);
let modules = ''
this.modules.map((_module) => {
modules = `'${ _module.filename }': function (require, module, exports) { ${ _module.transformCode } },`
});
const bundle = `
(function(modules) {
function require(fileName) {
const fn = modules[fileName];
const module = { exports : {} };
fn(require, module, module.exports);
return module.exports;
}
require('${this.entry}');
})({${modules}})
`
try {
// 检查目录是否存在, 没有则创建
if(!fs.existsSync(this.output.path)) {
fs.mkdirSync(this.output.path)
}
// 输出
fs.writeFileSync(outputPath, bundle, 'utf-8')
} catch (e) {
throw new Error(e)
}
}
}
- parse.js 解析babel
const fs = require('fs')
const babylone = require('babylon')
const traverse = require('@babel/traverse').default
const { transformFromAst } = require('@babel/core')
// const MagicString = require('magic-string')
module.exports = {
// 生成AST
getAST: filename => {
// 读取入口文件内容
const content = fs.readFileSync(filename, 'utf-8')
// 生成AST语法分析树
return babylone.parse(content, {
sourceType: 'module'
})
},
// 分析依赖
getDependencis: ast => {
const dependencies = []
// 遍历AST树
traverse(ast, {
ImportDeclaration({ node }) {
dependencies.push(node.source.value);
// 魔法字符串处理
// const code = new MagicString(content)
// const { start, end, specifiers , source } = node
// 替换内容
// code.overwrite(start, end, `var ${specifiers[0].local.name} = __webpack_require__("${source.value}")`)
}
})
return dependencies
},
transform: ast => {
const { code } = transformFromAst(ast, null, {
presets: ['@babel/env']
})
return code
}
}
使用x-webpack
- 创建 x-webpack-demo
- package.json
{
"name": "x-webpack-demo",
"version": "1.0.0",
"description": "",
"main": "x.config.js",
"scripts": {
"build": "x-webpack" // x-webpack已经通过npm link 软链到本地了
},
"keywords": [],
"author": "",
"license": "ISC"
}
- x.config.js
module.exports = {
entry: './src/index.js',
output: {
filename: 'main.js'
}
}
- src/index.js
const sayHi = require('./a.js')
sayHi('xfz')
- src/a.js
const getYear = require('./common/util.js')
module.exports = (name) => {
console.log(`hello:${name}`)
getYear()
}
- src/common/util.js
module.exports = age => {
console.log(`I am ${age} years old`)
}
- index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="./main.js"></script>
</body>
</html>
他来啦,他来啦,他带着绿帽走来啦...
打包:npm run build
打包产出物
代码语言:javascript复制(function(modules) {
const installedModules = {}
function __webpack_require__(id) {
if (installedModules[id]) {
return installedModules[id].exports
}
let module = installedModules[id] = {
exports: {}
}
modules[id].call(modules.exports, module, module.exports, __webpack_require__) return module.exports
}
return __webpack_require__("./src/index.js")
})("./src/index.js": function(module, exports, __webpack_require__) {
eval('const sayHi = __webpack_require__("./src/a.js")
sayHi('xfz ')')
},
"./src/a.js": function(module, exports, __webpack_require__) {
eval('const getYear = __webpack_require__("./src/common/util.js")
module.exports = (name) => {
console.log(`hello:${name}`)
getYear()
}')
},
"./src/common/util.js": function(module, exports, __webpack_require__) {
eval('module.exports = age => {
console.log(`I am ${age} years old`)
}')
})