webpack構建原理(實現一個簡易的webpack構建器)
webpack的構建原理:
- webpack的構建原理所在的核心文件:./lib/webpack-structure.js
- webpack配置文件:./webpack.config.js
- webpack的執行文件:./bundle.js
- 源碼所在的文件:./src/index.js
- 源碼依賴的文件:./src/expo.js
實現代碼:
- 工程結構:
- webpack-structure.js
const fs = require("fs")
const parser = require("@babel/parser")
const traverse = require("@babel/traverse").default
const path = require("path")
const {transformFromAst} = require("@babel/core")
module.exports = class Webpack {
constructor(options) {
const {entry, output} = options;
this.entry = entry;
this.output = output;
this.modules = []
}
run() {
const info = this.parse(this.entry)
this.modules.push(info);
for (let i = 0; i < this.modules.length; i++) {
const item = this.modules[i]
const {dependencies} = item
if (dependencies) {
for (let key in dependencies) {
this.modules.push(this.parse(dependencies[key]))
}
}
}
const obj = {}
this.modules.forEach(item => {
obj[item.entryFile] = {
dependencies: item.dependencies,
code: item.code
}
})
this.generateFile(obj)
}
parse(entryFile) {
const content = fs.readFileSync(entryFile, "utf-8")
const ast = parser.parse(content, {
sourceType: "module"
})
const dependencies = {}
traverse(ast, {
ImportDeclaration({node}) {
const newPathName = `${path.dirname(entryFile)}/${node.source.value.split("/")[1]}`;
dependencies[node.source.value] = newPathName
}
})
const {code} = transformFromAst(ast, null, {
presets: ["@babel/preset-env"]
})
return {
entryFile,
dependencies,
code
}
}
generateFile(code) {
const filePath = path.join(this.output.path, this.output.filename)
const newCode = JSON.stringify(code)
const bundle = `(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('${this.entry}')
})(${newCode})`
this.hasDir(this.output.path).then(msg => {
fs.writeFileSync(filePath, bundle, "utf-8")
}).catch(error => {
console.log(error);
return this.createDir(this.output.path)
}).then(msg => {
console.log(msg)
fs.writeFileSync(filePath, bundle, "utf-8")
console.log(`webpack編譯文件成功,內容輸出到文件${filePath}`)
}).catch(error => {
console.log(error);
})
}
}
hasDir(path) {
return new Promise((resolve, reject) => {
fs.stat(path, (err, msg) => {
if (err) {
reject(`目錄${path}不存在,系統開始自動創建目錄...`)
} else {
resolve()
}
})
})
}
createDir(path) {
return new Promise((resolve, reject) => {
let err = fs.mkdirSync(path, {})
if (err) {
reject(`創建目錄${path}失敗...`)
} else {
resolve(`系統創建目錄${path}成功,webpack開始編譯文件...`)
}
})
}
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'main.js'
}
};
const options = require("./webpack.config.js")
const Webpack = require("./lib/webpack-structure.js")
new Webpack(options).run()
import {add} from "./expo.js"
add(1, 2)
console.log("hello webpack");
export const add = function (a, b) {
return a + b
}
執行node bundle.js
的結果
(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('./src/index.js')
})({"./src/index.js":{"dependencies":{"./expo.js":"./src/expo.js"},"code":"\"use strict\";\n\nvar _expo = require(\"./expo.js\");\n\n(0, _expo.add)(1, 2);\nconsole.log(\"hello webpack\");"},"./src/expo.js":{"dependencies":{},"code":"\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.mini = exports.add = void 0;\n\nvar add = function add(a, b) {\n return a + b;\n};\n\nexports.add = add;\n\nvar mini = function mini(a, b) {\n return a - b;\n};\n\nexports.mini = mini;"}})
- 將main.js文件中的代碼拷貝到瀏覽器的控制檯執行,成功輸出“hello webpack”