Thus, programs must be written for people to read, and only incidentally for machines to execute
代码质量不言而喻,好代码辑清晰,阅读流畅,团队合作更容易。
前置知识 AST 抽象语法树
抽象语法树 AST 是远离业务逻辑,迈向编程底层,提升个人编程能力的重要知识点。虽然 AST 的工具很多可能不会用在生产环境上,但在诸如开发体验上有很大的帮助。
在 vscode AST 插件体验
如果经常使用 vscode, 可以使用 vscode-ast 插件,来直观感受 JS/TS ast 分析与编辑器中代码的映射关系。
在一个 .js
文件尝试 AST 分析:
ast 作为底层知识,这里只是介绍现在 nodejs 中常用的 JS/TS 工具:
JS解释器名称 | 说明 |
---|---|
esprima | 高性能、符合标准的 ECMAScript 解析器 |
Babylon | babel 的 js 解释器 |
espree | 在acorn基础上衍生而来,eslint中使用 |
acorn | webpack使用js解释器 |
astexplorer | 支持多语言的 AST 在线工具(使用 codemirror 编写) |
swc | 基于 rust 的工具链 |
typescript | ts 语言词法分析 |
@typescript-eslint/parser | 在 eslint 中使用 esLint |
flow | js 静态类型检查 |
uglifyjs | 压缩工具 |
css ast
css 解释器名称 | 说明 |
---|---|
cssom | CSS解析器(不在维护状态) |
csstree | CSS 工具集,包括基于 W3C 规范和浏览器实现的快速详细解析器、助行器、生成器和词法分析器 |
postcss | 用 js 编写的 css 转换工具 |
rework/css | css 解释器 |
AST 基本工作流内容
- parser 转换 language -> AST
- walker 遍历
- generator 遍历器
- lexer 词法分析器
使用 postcss 为例
读取 css 为 ast
// plugin.js
function plugin(opts = {}) {
return {
postcssPlugin: "transform-px-to-em",
Declaration(decl) {
decl.value = decl.value.replace(/px/g, "rem");
},
};
}
plugin.postcss = true;
export default plugin;
// transform.js
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import postcss from "postcss";
import plugin from "./plugin.js";
const __filename = fileURLToPath(import.meta.url); // file 协议转转成 绝对路径
const __dirname = path.dirname(__filename); // 获取当前文件路径
const resolvePath = (p) => path.resolve(__dirname, p);
async function init() {
const cssCode = fs.readFileSync(resolvePath("./index.css"));
const cssAst = await postcss([plugin])
.process(cssCode)
.catch((e) => {
console.log(e);
});
console.log(cssAst);
fs.writeFileSync(resolvePath("./_index.css"), cssAst.css);
}
init();
// before
div {
padding: 0px 20px 10px 10px;
margin: 0px; /*reset margin*/
}
// after
div {
padding: 0rem 20rem 10rem 10rem;
margin: 0rem; /*reset margin*/
}
Node 应用应用层面 AST 相关工具
lint | 说明 |
---|---|
eslint | js 相关 linter 工具 |
prettier | 多文件格式化工具(主打格式功能,可以集成到 eslint 中) |
editorconfig | 编辑器行为约束 |
stylelint | 样式文件 lint 约束,修复工具 |
commitlint | 提交代码时 lint |
lint 与 git 配合
git 功能 | 说明 |
---|---|
lint-staged | 仅处理 lint-staged 的能力 |
git hook pre-commit | 提交之前需要执行的hook |
git hook commit-msg | 提交信息 |
非 AST 工具
非 AST 解析 | 说明 |
---|---|
esbuild | esbuild 不是基于 AST 代码解析 |
VScode 插件
- eslint
- stylelint
- Error Lens
- prettier
- editorconfig for VSCode
ESLint
以下示例使用 vscode 编辑器(确保 eslint 插件):
Vite + React + TS + ESLint
// choices react-ts
pnpm create vite
// new eslint init eslint config and choice typescript
npm init @eslint/config
自动生成.eslintrc.cjs 文件,因为是 esm 然后将其改为 .eslintrc
ESLint 配置文件示例
{
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"overrides": [
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
}
}
在 npm 脚本中添加 eslint 命令
"scripts": {
"lint": "eslint --ext .ts,.tsx, ./src",
"lint:fix": "eslint --ext .ts,.tsx, ./src --fix"
}
在 tsx 文件添加 const 常量修改代码会报错:
const val = 1;
val = 2;
Prettier
全局安装 prettier 或者 项目级别 prettier, 在 ESLint 中使用 prettier 注意使用发生冲突()
Vite + React + TS + ESLint + Prettier
pnpm install prettier -D
pnpm install eslint-config-prettier eslint-plugin-prettier -D
prettier 配置文件: .prettier/.prettierignore
{
"semi": false,
"trailingComma": "none",
"singleQuote": true,
"jsxSingleQuote": true,
"printWidth": 80,
"tabWidth": 2
}
配置 .eslintrc
从 eslint 中启动 prettier 进行格式化,因为上面已经安装了:
- eslint-config-prettier
- eslint-plugin-prettier
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
}
这样就可以在 eslint npm 脚本中启动 prettier。
StyleLint
Vite + React + TS + ESLint + Prettier + StyleLint
对样式进行 lint 和 格式化:
pnpm install stylelint -D
pnpm install stylelint-config-prettier stylelint-prettier
npm 中添加脚本:
"scripts": {
"stylelint": "stylelint ./src/**/*.{css,less,scss,sass}",
"stylelint:fix": "pnpm run stylelint --fix",
},
stylelint 配置文件示例
// .stylelintrc
{
"extends": ["stylelint-prettier/recommended"]
}
当我们有一些 lint 的时候,stylelint 就会提示正在编码的你,此时有一个很好的提示,在初期编码的你,这个更多不是约束,而是很好的提示。
Commitlint
Vite + React + TS + ESLint + Prettier + StyleLint + Commitlint
全局使用、局部使用
// 全局
npm install -g @commitlint/cli @commitlint/config-conventional
// 局部
npm install --save-dev @commitlint/config-conventional @commitlint/cli
初始化 git
git init
touch .gitignore
# 加入
node_modules
接下来就需要配合 husky(或者其他的 git 钩子管理)配合 lint-staged。
commitlint 配置文件
echo "{ \"extends\": ['@commitlint/config-conventional'] }" > .commitlintrc
lint-staged 和 husky
lint-staged 仅仅 lint 出来 git 暂存库中得文件
pnpm install lint-staged -D
配置 lint-staged
在 package.json 中配置
{ "lint-staged": { "*": "your-cmd" } }
使用配置文件 .lintstagedrc
{
"**/*.{ts, tsx, js, jsx}": ["npm run lint"],
"src/**/*.{css,less,sass,scss}": ["npm run stylelint"],
"*.md": ["prettier --write"]
}
npm 添加 lint-stated 脚本
{
"lint-staged": "lint-staged",
}
husky 安装 + 配合 lint-staged
自动化生成 husky 相关文件
pnpm dlx husky-init && pnpm install # pnpm
添加 pre-commit/commit-msg 钩子
- pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint-staged
- commit-msg
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no -- commitlint --edit ""
提交一个“非法”的 message
git add .
git commit -m 'abc'
错误: subject-empty/type-empty 的错误提示:
test 工具
测试也是在自动化过程中,重要的代码质量保证:
pnpm install jest ts-jest -D
- 添加 npm 脚本(如果需要也可以添加到 lint-staged 中)
"scripts": {
"test": "jest"
},
- 配置 jest
export default {
coverageProvider: 'v8',
preset: 'ts-jest',
roots: ['<rootDir>/src'],
testEnvironment: 'node',
transform: {
'^.+\\.tsx?$': 'ts-jest'
}
}
提交规范工具 commitizen
两种安装方式:全局安装-项目安装
如何提交代码?
提交行为项目不应该直接业务代码相关,推荐全局安装,写入配置文件,方便在其他的电脑切换时方便使用。
npm install -g commitizen
commitizen 使用 首先适配器(如何修改,这里不在探讨)
commitizen init cz-conventional-changelog --save-dev --save-exact
在全局配置文件中指定适配器
echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc
指定 git cz 命令取代提交 git commit
npm install commitizen cz-conventional-changelog -D
局部安装
局部安装配置 commitizen 配置 package.json 中
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
基本完成提交一次
git add .
git commit -m 'feat: init'
提交成功。 下面是基本简单的配置.
修改业务代码,使用 git cz 提交就不需要自己手写符合要求的 commit message:
抽象为一个命令
为了能快速的初始化 lint 工具,进一步提升效率,封装一个命令工具,一个命令自动配置这些:
https://github.com/yyong008/lints-compose.git
npm link # 本地使用
pnpm lints-compose create # 创建 link 配置文件 + 安装 npm 包
pnpm lints-compose reset # 移除配置文件
小结
- 要了 lint 解工具解决了哪些功能化的问题
- 尝试自己一点适用于自己的工程化工具
- 熟悉 AST, 了解底层知识服务于工程化
参考
- Esprima 官方网站 esprima.org/
- babel/packages/babel-parser at master github.com/babel/babel…
- acornjs/acorn github.com/acornjs/aco…
- eslint/espree github.com/eslint/espr…