babel源码阅读 ~ 准备工作
前言
学习一个库的源码,我认为不能直接打开 src 或者 lib 目录去直接找入口文件着去阅读,而应该去了解下库的作者是如何梳理、debug 源码的
本着这个原则,我们需要在阅读源码之前,理清楚整个项目是如何 debug 的,一些 pkg.json 里的命令是如何执行的
以下代码全部出自于 babel-7.9.6 这个版本,源码地址:https://github.com/babel/babel/tree/v7.9.6
package.json
拿到一个 nodejs 的库,首先应该看他根目录下的 package.json 文件。
作为阅读者和学习者,我们应该关注它的 scripts 和 bin 两个字段下的内容。
由于 babel 的 bin 命令都散落在 packages 中的各个包中,所以这里我们只需要关注 scripts 字段:
"scripts": {
"bootstrap": "make bootstrap",
"codesandbox": "make bootstrap-only; make build-no-bundle",
"build": "make build",
"fix": "make fix",
"lint": "make lint",
"test": "make test"
}
我们发现所有的 script,全部使用 make 命令触发。
此时会在根目录下寻找名为 makefile 或者 Makefile 的文件,找到后则执行相应的内容。
Makefile
以 lint: make lint 为例:
找到 Makefile 中的 lint 命令的注册位置:
lint: lint-js lint-ts
注:Makefile 的主要语法为:
target : Array<prerequisites>
List<command>
具体为:
target: 命令label,或者target文件 => 这里只做一个命令的名称prerequisites: 依赖的命令或目标文件 => 可以理解成执行该命令前需要执行的命令, 可以没有,也可以很多很多command: 具体的指令,纯bash
好的回过头来,我们发现他有两处 prerequisites:
lint-jslint-ts
所以此时去找到两个依赖的命令:
# YARN 这个变量赋值为 `yarn --silent`
YARN := yarn --silent
# SOURCE 变量赋值为: `packages codemods eslint`
SOURCES = packages codemods eslint
# lint-js 不包含依赖,只有一条命令
lint-js:
# 带入 YARN 和 SOURCE 变量后,执行的命令就是:
# BABEL_ENV=test yarn --silent eslint scripts packages codemods eslint '*.js' --format=codeframe
BABEL_ENV=test $(YARN) eslint scripts $(SOURCES) '*.js' --format=codeframe
lint-ts:
# 直接执行 scripts 目录下的 lint-ts-typigs.sh 这个脚本
scripts/lint-ts-typings.sh
我们可以看到,lint-js 命令就相当于我们在控制台中输入:
BABEL_ENV=test yarn --silent eslint scripts packages codemods eslint '*.js' --format=codeframe
可以看到执行的是 eslint 命令,我们可以将 --silent 去掉后看到 eslint 命令的位置:
/Users/anning/Desktop/babel-7.9.6/node_modules/.bin/eslint scripts packages codemods eslint '*.js' --format=codeframe
注:其实一般都在 /node_modules/bin 里找...
说完这个例子之后,我们折回来说我们学习时 debug 的方式。
在 scripts 里我们是没能找到类似 vue-next 之类的 "dev": "node scripts/dev.js", 之类的命令的,所以也不能够像 vue-next 那样直接 yarn dev [packageName] 的去 debug 某个具体的包
这时候我们有注意到 CONTRIBUTING 里有一段:
Fork the
babelrepository to your GitHub Account.Then, run:
$ git clone https://github.com/<your-github-username>/babel $ cd babel $ make bootstrapThen you can either run:
$ make buildto build Babel once or:
$ make watchto have Babel build itself and incrementally build files on change.
所以我们尝试去寻找 Makefile 中的 watch 指令:
watch: build-no-bundle
BABEL_ENV=development $(YARN) gulp watch
这里也给出 build-no-bundle 的各个依赖的配置和注释:
# 先执行 clean, clean-lib
build-no-bundle: clean clean-lib
# 控制权交到 gulp 和 Gulpfile. 执行 Gulpfile 里的 build-no-bundle task
BABEL_ENV=development $(YARN) gulp build-no-bundle
# Ensure that build artifacts for types are created during local
# development too.
// 然后执行到 makefile 里的 generate-type-helpers 和 build-typings 命令
$(MAKE) generate-type-helpers
$(MAKE) build-typings
# 执行 `node packages/babel-types/scripts/generateTypeHelpers.js` 这条命令
generate-type-helpers:
$(NODE) packages/babel-types/scripts/generateTypeHelpers.js
# 执行 build-flow-typings build-typescript-typings 两条前置依赖的指令
build-typings: build-flow-typings build-typescript-typings
# 编译 flow 的类型文件
build-flow-typings:
$(NODE) packages/babel-types/scripts/generators/flow.js > packages/babel-types/lib/index.js.flow
# 编译 ts 的类型文件
build-typescript-typings:
$(NODE) packages/babel-types/scripts/generators/typescript.js > packages/babel-types/lib/index.d.ts
# 先执行 test-clean 命令
# 而后删除一些文件夹 - -。。
clean: test-clean
rm -f .npmrc
rm -rf packages/babel-polyfill/browser*
rm -rf packages/babel-polyfill/dist
rm -rf coverage
rm -rf packages/*/npm-debug*
# 执行 clean-source-test 这个变量下的命令
test-clean:
$(foreach source, $(SOURCES), \
$(call clean-source-test, $(source)))
# 执行 clean-source-lib 这个变量下的命令
clean-lib:
$(foreach source, $(SOURCES), \
$(call clean-source-lib, $(source)))
# clean-source-lib定义
define clean-source-lib
rm -rf $(1)/*/lib
endef
# clean-source-test 定义
define clean-source-test
rm -rf $(1)/*/test/tmp
rm -rf $(1)/*/test-fixtures.json
endef
Gulpfile
我们可以看到前置执行了 build-no-bundle 之后,执行的是:
BABEL_ENV=development yarn --silent gulp watch
所以控制权交回到 gulp 和 Gulpfile 中的各个 task 中,我们看到 gulp watch 对应的脚本是:
const defaultSourcesGlob = "./@(codemods|packages|eslint)/*/src/**/*.js";
gulp.task("build-no-bundle", () => buildBabel());
gulp.task(
"watch",
gulp.series("build-no-bundle", function watch() {
gulp.watch(defaultSourcesGlob, gulp.task("build-no-bundle"));
})
);
比较明显的就是会监听 defaultSourcesGlob 下的文件,监听到修改时就会重新执行 build-no-bundle 的 task,而后重新执行 buildBabel()
我们尝试执行 make watch 之后,控制台打印如下信息:
[11:50:26] Using gulpfile ~/Desktop/babel-7.9.6/gulpfile.js
[11:50:26] Starting 'watch'...
[11:50:26] Starting 'build-no-bundle'...
[11:50:26] Compiling 'packages/babel-helper-builder-react-jsx-experimental/src/index.js'...
[11:50:27] Compiling 'packages/babel-helper-compilation-targets/src/debug.js'...
@babel/preset-env: `DEBUG` option
Using targets:
{
"node": "10.15"
}
Using modules transform: false
Using plugins:
proposal-numeric-separator { "node":"10.15" }
proposal-nullish-coalescing-operator { "node":"10.15" }
proposal-optional-chaining { "node":"10.15" }
syntax-json-strings { "node":"10.15" }
syntax-optional-catch-binding { "node":"10.15" }
syntax-async-generators { "node":"10.15" }
syntax-object-rest-spread { "node":"10.15" }
syntax-dynamic-import { "node":"10.15" }
.......
在经历了很多很多文件的输出之后,我们看到最后两句:
[11:50:44] Finished 'build-no-bundle' after 18 s
[11:50:44] Starting 'watch'...
一切如我们所见。
我们修改之后,被修改的文件会重新被编译:
[11:50:44] Finished 'build-no-bundle' after 18 s
[11:50:44] Starting 'watch'...
[11:55:17] Starting 'build-no-bundle'...
[11:55:18] Compiling 'packages/babel-core/src/index.js'...
[11:55:18] Finished 'build-no-bundle' after 700 ms
[11:55:27] Starting 'build-no-bundle'...
[11:55:27] Compiling 'packages/babel-core/src/index.js'...
[11:55:27] Finished 'build-no-bundle' after 422 ms
[11:55:47] Starting 'build-no-bundle'...
[11:55:47] Compiling 'packages/babel-core/src/index.js'...
[11:55:48] Finished 'build-no-bundle' after 376 ms
[11:55:50] Starting 'build-no-bundle'...
[11:55:50] Compiling 'packages/babel-core/src/index.js'...
[11:55:50] Finished 'build-no-bundle' after 362 ms
至此,我们就可以边调试边学习了~

浙公网安备 33010602011771号