# 六.plugins
# bin/mypack.js
#! /usr/bin/env node
//1)需要找到当前执行名的路径 拿到webpack.config.js
let path = require("path")
//config配置文件
let config = require(path.resolve("webpack.config.js"))
let Complier = require("../lib/Complier.js")
let complier = new Complier(config)
complier.hooks.entryOption.call()
//标识运行编译
complier.run()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# lib/Complier.js
let fs = require("fs")
let path = require("path")
let babylon = require("babylon")
let traverse = require("@babel/traverse").default
let t = require("@babel/types")
let generator = require("@babel/generator").default
let ejs = require("ejs")
let { SyncHook } = require("tapable")
//babylon 主要把源码转换成ast
//@babel/traverse
//@babel/types
//@babel/generator
class Complier {
constructor(config) {
//entry output
this.config = config
//需要保存入口文件的路径
this.entryId
//需要保存所有的模块依赖
this.modules = {}
this.entry = config.entry
this.root = process.cwd()
this.hooks = {
entryOption: new SyncHook(),
complie: new SyncHook(),
afterComplie: new SyncHook(),
afterPlugins: new SyncHook(),
run: new SyncHook(),
emit: new SyncHook(),
done: new SyncHook(),
}
//如果传递了plugins参数
let plugins = this.config.plugins
if (Array.isArray(plugins)) {
plugins.forEach((plugin) => {
plugin.apply(this)
})
}
this.hooks.afterPlugins.call()
}
getSource(modulePath) {
let rules = this.config.module.rules
let content = fs.readFileSync(modulePath, "utf8")
for (let i = 0; i < rules.length; i++) {
let rule = rules[i]
let { test, use } = rule
let len = use.length - 1
if (test.test(modulePath)) {
function normalLoader() {
let loader = require(use[len--])
console.log(loader, content, "888888888888888888")
content = loader(content)
if (len >= 0) {
normalLoader()
}
}
normalLoader()
}
}
return content
}
//解析源码 AST解析语法树
parse(source, parentPath) {
let ast = babylon.parse(source)
let dependencies = []
traverse(ast, {
CallExpression(p) {
let node = p.node
if (node.callee.name === "require") {
node.callee.name = "__webpack_require__"
let moduleName = node.arguments[0].value
moduleName = moduleName + (path.extname(moduleName) ? "" : ".js")
moduleName = "./" + path.join(parentPath, moduleName)
;("src/a.js")
dependencies.push(moduleName)
node.arguments = [t.stringLiteral(moduleName)]
}
},
})
let sourceCode = generator(ast).code
return { sourceCode, dependencies }
}
buildModule(modulePath, isEntry) {
let source = this.getSource(modulePath)
//模块id modulePath = modulePath-this.root
let moduleName = "./" + path.relative(this.root, modulePath)
if (isEntry) {
this.entryId = moduleName
}
//解析需要把source源码进行改造 返回一个依赖列表
let { sourceCode, dependencies } = this.parse(
source,
path.dirname(moduleName)
)
//把相对路径和模块中的内容 对应起来
this.modules[moduleName] = sourceCode
dependencies.forEach((dep) => {
this.buildModule(path.join(this.root, dep), false)
})
}
emitFile() {
//发射文件
//用数据 渲染我们的
// 拿到输出到那个目录下 输出路径
let main = path.join(this.config.output.path, this.config.output.filename)
//模板路径
let templateStr = this.getSource(path.join(__dirname, "main.ejs"))
let code = ejs.render(templateStr, {
entryId: this.entryId,
modules: this.modules,
})
this.assets = {}
//资源中 路径对应的代码
this.assets[main] = code
fs.writeFileSync(main, this.assets[main])
}
run() {
//执行
this.hooks.run.call()
this.hooks.complie.call()
this.buildModule(path.resolve(this.root, this.entry), true)
this.hooks.afterComplie.call()
console.log(this.modules, this.entryId)
this.emitFile()
this.hooks.emit.call()
this.hooks.done.call()
}
}
module.exports = Complier
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# 运行
npx mypack
1
得到:afterPlugins emit
← 五.loader