Skip to content

Commit

Permalink
feat(ToyWebpack|config|readme): ✅ complete v1.0.0
Browse files Browse the repository at this point in the history
1. add _getBundle and _fileOutput function;
2. add build and test script
3. update readme.txt
  • Loading branch information
classmatewu committed Oct 2, 2021
1 parent 00fc592 commit 5b6dc24
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 7 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,20 @@
* graph:options -> entryDeps -> graph:利用参数,生成入口文件依赖,递归这一步可得到依赖图谱
* chunk:graph -> chunk:利用依赖图谱,结合babel,生成module集合,即chunk
* bundle:chunk -> bundle:利用chunk,合并与加工module集合,最后生成浏览器能够运行的产物,即bundle

### 运行
`./modules/`文件夹下存放要打包的文件
`./config.js`文件进行打包配置
执行`npm run build`,生成打包产物
执行`npm run test`,验证打包结果

### 版本
v1.0.0: 完成简陋的第一版,支持仅支持esm的模块方式,且引用路径不能重复,即使在不同文件夹下(即所有文件名不能一样)

### 展望
v1.0.0: 完成简陋第一版
v1.0.1: 修复引用路径不能重复,即使在不同文件夹下
v1.0.2: 支持cjs、amd模块

### 联系我
如果疑问或建议,欢迎提issues或邮件联系我`[email protected]`
49 changes: 46 additions & 3 deletions ToyWebpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ class ToyWebpack {
run() {
console.log('run ToyWebpack')
const chunk = this._getChunk(this.entry)
console.log('---chunk---', chunk);
const bundle = this._getBundle(this.entry, chunk)
this._fileOutput(this.output, bundle)
console.log('success!');
}

/**
Expand All @@ -40,7 +42,6 @@ class ToyWebpack {
ImportDeclaration({node}) {
const depOriginPath = node?.source?.value // 可能是绝对路径,也可能是相对路径
const depAbsPath = path.resolve(moduleAbsPath, `../${depOriginPath}`) // 将depOriginPath转为绝对路径
console.log('---depAbsPath---', depOriginPath, depAbsPath);
dep[depOriginPath] = depAbsPath
},
// TODO 添加 cjs、amd 模块的打包的方式
Expand All @@ -66,7 +67,6 @@ class ToyWebpack {
_getChunk(entryOriginPath) {
// 先解析入口模块
const entryAbsPath = path.resolve(__dirname, entryOriginPath)
console.log('---entryAbsPath---', entryOriginPath, entryAbsPath);
const {dep: entryDep, code: entryCode} = this._parseModule(entryAbsPath)
const depChain = [entryDep]

Expand All @@ -85,6 +85,49 @@ class ToyWebpack {

return chunk
}

/**
* @description 由chunk生成浏览器可以直接执行的代码,即bundle
*/
_getBundle(entry, chunk) {
/**
* @description 解释一下几个重要的逻辑点
* 1. 每个module外面都需要包裹一层自执行函数,原因是避免变量的污染,做到隔离效果
* 2. entry、chunk这两个变量都进行序列化的原因不同:
* - entry是由于它的值是入口文件的路径,如果不再转为字符串,那就变成了这个路径是变量名,从而导致错误(也可以写成'${entry}',即将JSON.stringify()替换为'')
* - chunk除了有上面一点原因外,它是个对象,由于包裹在es6的模版字符串中,而对象转为字符串会变成[object, Object](不可以将JSON.stringify()替换为'')
* 3. 在浏览器/node中,可以利用eval / new Function这两种方法来将字符串转换为js代码并执行
* 4. 直接执行eval不行么,为什么需要定义一个require,在require里执行eval,然后再执行require,这不是多此一举么?
* - 因为es6的import导入语句转为es5就会变成require,而浏览器并没有require方法,所以就需要我们自己来实现一个,参数是import时的originPath,也就是chunk的key
* 5. 为什么需要在require函数里定义一个export对象,执行完了eval后再导出出去呢?
* - 因为es6的export导出语句,转为es5实际上就是定义了一个exports,把要导出的变量放进去,最后renturn出去。
* - 所以,在导入的时候,执行es5的require,就会拿到这个exports变量,从里面拿到引用模块导出的变量
*/
return `
;(function(entry, chunk) {
function require(moduleOriginPath) {
var exports = {}
;(function() {
eval(chunk[moduleOriginPath])
})()
return exports
}
require(entry)
})(${JSON.stringify(entry)}, ${JSON.stringify(chunk)})
`
}

/**
* @description 生成打包产物文件
*/
_fileOutput(output, bundle) {
const { path, filename } = output
// 若文件夹不存在,则先创建文件夹
if (!fs.existsSync(path)) {
fs.mkdirSync(path)
}
fs.writeFileSync(`${path}/${filename}`, bundle, 'utf-8')
}
}

module.exports = ToyWebpack
2 changes: 1 addition & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = {
*/
output : {
filename : 'bundle.js',
path : path.join(__dirname, '../dist')
path : path.join(__dirname, './dist')
},

/**
Expand Down
12 changes: 12 additions & 0 deletions dist/bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

;(function(entry, chunk) {
function require(moduleOriginPath) {
var exports = {}
;(function() {
eval(chunk[moduleOriginPath])
})()
return exports
}
require(entry)
})("./modules/index.js", {"./modules/index.js":"\"use strict\";\n\nvar _a = require(\"./a.js\");\n\n// const c = require('./c')\n// import d from './d.js';\n// const b = require('./b')\nconsole.log(\"Hello \".concat(_a.a));","./a.js":"\"use strict\";\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.a = void 0;\n\n/**\n * this is a es6 module\n */\nvar a = 'World';\nexports.a = a;"})

4 changes: 2 additions & 2 deletions modules/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import a from './a.js';
import {a} from './a.js';
// const c = require('./c')
// import d from './d.js';
// const b = require('./b')

console.log(`Hello ${a} ${b} ${d}`);
console.log(`Hello ${a}`);
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"description": "手写实现一个迷你的webpack",
"main": "ToyWebpack.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"build": "node ./app.js",
"test": "node ./dist/bundle.js"
},
"repository": {
"type": "git",
Expand Down

0 comments on commit 5b6dc24

Please sign in to comment.