-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
140 lines (122 loc) · 3.36 KB
/
index.js
File metadata and controls
140 lines (122 loc) · 3.36 KB
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
138
139
140
import fs from 'fs'
import { parse } from '@babel/parser'
import traverse from '@babel/traverse'
import path from 'path'
import ejs from 'ejs'
import { transformFromAst } from '@babel/core'
import { SyncHook } from 'tapable'
import * as minWebpackConfig from './minWebpack.config.js'
let id = 0
let { entry, outPutPath } = minWebpackConfig.default
const loader = minWebpackConfig.default.module.rules
const plugins = minWebpackConfig.default.plugins
// 定义hooks
const hooks = {
emitFile: new SyncHook(["context"])
}
const assetCache = new Map();
/**
* 获取文件的依赖等信息
* @param {*} filePath
* @returns
*/
function createAsset(filePath) {
// 检查缓存中是否已经有该模块
if (assetCache.has(filePath)) {
return assetCache.get(filePath);
}
let source = fs.readFileSync(filePath, {
encoding: 'utf-8'
});
const loaderContext = {
addDeps(dep) {
console.log(dep);
}
};
// loader
loader.forEach(({ test, use }) => {
if (test.test(filePath)) {
if (Array.isArray(use)) {
use.reverse().forEach(loaderItem => {
source = loaderItem.call(loaderContext, source);
});
}
}
});
// 转换为ast
const ast = parse(source, { sourceType: 'module' });
const deps = [];
// 获取文件的import语法部分
traverse.default(ast, {
ImportDeclaration({ node }) {
const { value } = node.source;
deps.push(value);
},
});
const { code } = transformFromAst(ast, null, {
presets: [['@babel/preset-env', { modules: 'commonjs' }]]
});
// 创建 asset 对象
const asset = {
id: id++,
deps,
filePath,
mapping: {},
code,
};
// 将创建的 asset 存入缓存
assetCache.set(filePath, asset);
return asset;
}
function createGraph() {
const minMain = createAsset(entry);
const queue = [minMain];
for (const asset of queue) {
asset.deps.forEach(relativePath => {
const { mapping } = asset;
const fullPath = path.resolve('min-pack-example', relativePath);
// 检查缓存,避免重复处理
if (!assetCache.has(fullPath)) {
const source = createAsset(fullPath);
mapping[relativePath] = source.id;
// 这里会造成循环引用问题
queue.push(source);
} else {
// 如果已经在缓存中,直接使用缓存中的 ID
mapping[relativePath] = assetCache.get(fullPath).id;
}
});
}
return queue;
}
const graph = createGraph()
console.log(graph)
function initPlugins() {
plugins.forEach(plugin => {
plugin.apply(hooks)
})
}
function build(graph) {
const template = fs.readFileSync('./bundle.ejs', { encoding: 'utf-8' })
const data = graph.map(item => {
const { code, mapping, id } = item
return {
id,
code,
mapping
}
})
const code = ejs.render(template, {
data: data
})
const context = {
_ChangeOutPutPath(newPath) {
outPutPath = newPath
}
}
// 同步执行
hooks.emitFile.call(context)
fs.writeFileSync(outPutPath, code)
}
initPlugins()
build(graph)