npm i -D eslint @typescript-eslint/parser eslint-plugin-react
npm i -D prettier eslint-config-prettier eslint-plugin-prettier
eslint: ESLint的核心代码
@typescript-eslint/parser:ESLint的解析器,用于解析typescript,从而检查和规范Typescript代码
@typescript-eslint/eslint-plugin:这是一个ESLint插件,包含了各类定义好的检测Typescript代码的规范,eslint-plugin-prettier已覆盖,无需安装
eslint-plugin-react:支持React语法的ESlint插件
prettier:prettier 插件的核心代码
eslint-config-prettier:解决ESLint中的样式规范和prettier中样式规范的冲突,以prettier的样式规范为准,使ESLint中的样式规范自动失效。
eslint-plugin-prettier:将prettier的规则作为ESLint规则来使用,因此需要安装 prettier。
该插件先使用prettier对你的代码进行格式化,然后与格式化之前的代码进行对比,如果过出现了不一致,这个地方就会被prettier进行标记。
接下来,我们需要在rules中添加,"prettier/prettier": "error"
,表示被prettier标记的地方抛出错误信息。然后借助eslint --fix
将抛出error的地方进行fix。
// 如果已在extends添加了插件的配置方案,则不需要重复添加 "prettier/prettier": "error"
extends: [
'plugin:prettier/recommended',
],
rules:{
// "prettier/prettier": "error"
}
Changed: All configs have been merged into one!
To upgrade, change:
{ "extends": [ "some-other-config-you-use", "prettier", "prettier/@typescript-eslint", "prettier/babel", "prettier/flowtype", "prettier/react", "prettier/standard", "prettier/unicorn", "prettier/vue" ] }Into:
{ "extends": [ "some-other-config-you-use", "prettier" ] }If you use eslint-plugin-prettier, all you need is plugin:prettier/recommended:
{ "extends": [ "some-other-config-you-use", "plugin:prettier/recommended" ] }
module.exports = {
parser: '@typescript-eslint/parser', //定义ESLint的解析器
// extends可省略前缀 eslint-config-
// plugins可省略前缀 eslint-plugin-
// extends使用插件导出的config,删掉插件前缀后加上前缀plugin:
extends: [ // 继承的规则,后面覆盖前面
'plugin:react/recommended',
'plugin:prettier/recommended',
],
plugins: [
// plugin:prettier/recommended 的 config 已包含以下plugins
// "react",
// '@typescript-eslint',
],
parserOptions: { // 解析器选项
"ecmaVersion": 2019, // 支持 ES6 语法并不意味着同时支持新的 ES6 全局变量或类型(比如 Set 等新类型),需{ "env":{ "es6": true } }
"sourceType": "module", // "script" (默认),如果代码是 ECMAScript 模块,设置为"module"
"ecmaFeatures": {
"jsx": true
}
},
env:{ // 指定代码的运行环境
browser: true,
es6:true,
node: true,
},
// 全局变量
globals:{
"var1": "writable",
"var2": "readonly",
"var3": "off", // 禁用
},
settings: {
// eslint-plugin-react要求:https://www.npmjs.com/package/eslint-plugin-react
"react": {
"pragma": "React",
"version": "detect"
}
},
// 扩展(或覆盖)规则
rules: {},
// 覆盖指定文件类型的rules
overrides: [
{
"files": ["./src/**/*.html"],
"rules": {
}
}
]
}
https://prettier.io/docs/en/options.html
可以查看vscode 设置->扩展-> prettier,中文解释
module.exports = {
printWidth: 120, // 行长
tabWidth: 2, // 缩进长度
useTabs: false, // 使用空格代替tab缩进
semi: true, // 结尾分号
singleQuote: true, // 单引号,jsx忽略此选项,请使用 jsxSingleQuote
quoteProps: 'preserve', // <as-needed|consistent|preserve> 是否给对象属性加引号 preserve:保留编程时的状态
jsxSingleQuote: false, // jsx中使用单引号
trailingComma: 'all', // <es5|none|all> 在对象或数组最后一个元素后面是否加逗号。all 尽可能尾随逗号
bracketSpacing: true, // 在对象前后添加空格-eg: { foo: bar }
bracketSameLine: false, // 多属性html、jsx标签的‘>’折行放置
arrowParens: 'avoid', // <always|avoid> avoid:单参数箭头函数参数不使用圆括号
endOfLine: 'lf', // 结束行形式
embeddedLanguageFormatting: 'auto', // 对引用代码进行格式化
overrides: [
// {
// files: '*.test.js',
// options: {
// semi: true,
// },
// },
// {
// files: ['*.html', 'legacy/**/*.js'],
// options: {
// tabWidth: 4,
// },
// },
],
};
.eslintignore
glob模式
ESLint总是忽略 /node_modules/*
和 /bower_components/*
中的文件。
dist
!build/index.js
如果相比于当前工作目录下 .eslintignore
文件,你更想使用一个不同的文件,你可以在命令行使用 --ignore-path
选项指定它。例如,你可以使用 .jshintignore
文件,因为它有相同的格式:
eslint --ignore-path .jshintignore file.js
你也可以使用你的 .gitignore
文件:
eslint --ignore-path .gitignore file.js
任何文件只要满足标准忽略文件格式都可以用。记住,指定 --ignore-path
意味着任何现有的 .eslintignore
文件将不被使用。请注意,.eslintignore
中的匹配规则比 .gitignore
中的更严格。****
.prettierignore
如果没有用 prettier 或 插件去修改,可以不用该文件
dist
!build/index.js
node_moduls
plugin插件主要是为eslint新增一些检查规则,引入 plugin 可以理解为只是加载了插件,引入了额外的 自定义的rules。需要在 rules、extends 中定义后才会生效,如果没有则不生效。
plugin类似配置好的.eslintrc.js,因此这部分可以在extends里配置:plugin:config名称。
extends可以看做是集成一个个配置方案的最佳实践。它允许配置多份别人配置好的.eslintrc.js。
允许 extends 配置多个模块,如果规则冲突,位置靠后的包将覆盖前面的。rules 中的规则相同,并且优先级恒定高于 extends,类似class的extends,支持多继承。
命名规范
extends 的模块名称以 eslint-config- 开头,例如 eslint-config-myconfig。也支持 npm 作用域模块,例如 : @scope/eslint-config 或 @scope/eslint-config-myconfig) 。使用的时候可以用全称(slint-config-myconfig),也可以用缩写(myconfig)。
如果 extends 引用了 plugin 的config,plugins 中会自动引入该plugin,即使 plugins 没有显式声明。
For your example, there are 2 ways to do add typescript-eslint
...
{
parser: "@typescript-eslint/parser",
parserOptions: { sourceType: "module" },
plugins: ["@typescript-eslint"],
extends: [],
rules: {
"@typescript-eslint/explicit-function-return-type": [
"error",
{
allowExpressions: true
}
]
}
}
{
plugins: [],
extends: ["plugin:@typescript-eslint/recommended"],
rules: {
"@typescript-eslint/explicit-function-return-type": [
"error",
{
allowExpressions: true
}
]
}
}
The difference is...
parser
, parserOptions
and plugins
are manually added,@typescript-eslint/explicit-function-return-type
is enforced.plugin:@typescript-eslint/recommended
has automatically added parser
, parserOptions
and plugins
. extends引用了plugin的config,即使plugins中没有显式引入,也会自动引入@typescript-eslint/explicit-function-return-type
is augmented and enforced.优先级最高
"off"
或 0
- 关闭规则"warn"
或 1
- 开启规则,使用警告级别的错误:warn
(不会导致程序退出)"error"
或 2
- 开启规则,使用错误级别的错误:error
(当被触发的时候,程序会退出)rules
属性可以做下面的任何事情以扩展(或覆盖)规则:
"eqeqeq": ["error", "allow-null"]
三等于号"eqeqeq": "warn"
"eqeqeq": ["warn", "allow-null"]
"quotes": ["error", "single", "avoid-escape"]
"quotes": ["error", "single"]
"quotes": ["error", "single"]
基础配置:数组类型,派生配置:字符串类型,最后类型:覆盖数组第一项,"error"和"warn"是同一类型,都是放在数组第一位。如果派生配置是"eqeqeq": "always"
,则最后类型:"eqeqeq": ["warn", "always"]
基础配置:数组类型,派生配置:数组类型,最后类型:覆盖数组。
https://eslint.org/docs/user-guide/configuring/rules#using-configuration-comments-1
/* eslint-disable */
alert('foo');
将需要忽略的代码块用注释包裹起来
/* eslint-disable */
alert('foo');
/* eslint-enable */
将需要忽略的代码块用注释包裹起来
/* eslint-disable no-alert, no-console */
alert('foo');
console.log('bar');
/* eslint-enable no-alert, no-console */
此方法,有两种形式
alert('foo'); // eslint-disable-line
// or
// eslint-disable-next-line
alert('foo');
alert('foo'); // eslint-disable-line no-alert
// eslint-disable-next-line no-alert
alert('foo');
alert('foo'); // eslint-disable-line no-alert, quotes, semi
// eslint-disable-next-line no-alert, quotes, semi
alert('foo');
https://prettier.io/docs/en/ignore.html
A JavaScript comment(注释) of prettier-ignore
will exclude the next node in the abstract syntax tree from formatting.
{
"script":{
"lint": "eslint src --fix --ext .js,.ts,.jsx,.tsx"
}
}
下载 vscode的ESlint、prettier 插件
添加 eslint、prettier 规则
vscode的ESlint 插件会优先执行.eslintrc.js
文件的配置。prettier 同理。
vscode下载EditorConfig for VS Code插件
通配符
* 匹配除/之外的任意字符串
** 匹配任意字符串
? 匹配任意单个字符
[name] 匹配name中的任意一个单一字符
[!name] 匹配不存在name中的任意一个单一字符
{s1,s2,s3} 匹配给定的字符串中的任意一个(用逗号分隔)
{num1..num2} 匹配num1到num2之间的任意一个整数, 这里的num1和num2可以为正整数也可以为负整数
配置选项
root = true # 根目录的配置文件,编辑器会由当前目录向上查找,如果找到 `roor = true` 的文件,则不再查找
[*] # 匹配所有的文件
indent_style = space # 空格缩进
indent_size = 4 # 缩进空格为4个
end_of_line = lf # 文件换行符是 linux 的 `\n`
charset = utf-8 # 文件编码是 utf-8
trim_trailing_whitespace = true # 不保留行末的空格
insert_final_newline = true # 文件末尾添加一个空行
curly_bracket_next_line = false # 大括号不另起一行
spaces_around_operators = true # 运算符两遍都有空格
indent_brace_style = 1tbs # 条件语句格式是 1tbs
spaces_around_brackets # 表示括号和括号之间应有空格:无空格,仅在括号内,仅在括号外或在括号的两侧 (none,inside,outside,both)
max_line_length # 在指定的字符数后强制换行。off关闭此功能
[*.js] # 对所有的 js 文件生效
quote_type = single # 字符串使用单引号
[*.{html,less,css,json}] # 对所有 html, less, css, json 文件生效
quote_type = double # 字符串使用双引号
[package.json] # 对 package.json 生效
indent_size = 2 # 使用2个空格缩进
常用配置
# http://editorconfig.org
# root: 表明是最顶层的配置文件,发现设为true时,才会停止查找.editorconfig文件。
root = true
[*]
# 文件编码
charset = utf-8
# 使用单引号
quote_type = single
# 换行符格式
end_of_line = lf
# 缩进数量
indent_size = 2
# 缩进类型
indent_style = tab
# 行最大长度,要和.prettierrc.js保持一致
max_line_length = 120
# 删除末尾空格
trim_trailing_whitespace = true
# 结尾插入新行
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.{html,css,less,scss,json}] # 对所有 html,css,less,scss,json 文件生效
quote_type = double # 字符串使用双引号
# Makefile文件中要用tab缩进,这个确实是历史遗留问题,用空格缩进会出错。
[Makefile]
indent_style = tab
CSS语法规则和风格检测工具
支持 glob 模式
例如 .gitignore
*.zip
!xxx.zip
xxx.zip不会被忽略
!xxx.zip
*.zip
xxx.zip会被忽略
https://www.yuque.com/arvinxx-fe/workflow
https://juejin.cn/post/6934292467160514567
husky 或 yorkie 可以让我们向项目中方便添加git hooks。
lint-staged 是一个在git暂存文件上运行linters的工具,支持glob模式,只针对 add 的文件,不是所有文件。
Lint 这个单词的本意就是衣服上的棉球,通过 Linting 这个单词的意思就是去除代码中可能存在问题的意思。
整体的hooks非常多,但是我们用的比较多的其实只有两个
commit-msg
git commit
和 git merge
调用git commit --no-verify
绕过pre-commit
git commit
调用git commit --no-verify
绕过hook
脚本置于目录 ~/.git/hooks
中,以可执行文件的形式存在。
$ ls -lah .git/hooks
applypatch-msg.sample pre-merge-commit.sample
commit-msg.sample pre-push.sample
fsmonitor-watchman.sample pre-rebase.sample
post-update.sample pre-receive.sample
pre-applypatch.sample prepare-commit-msg.sample
pre-commit.sample update.sample
另外 git hooks 可使用 core.hooksPath
自定义脚本位置。
# 可通过命令行配置 core.hooksPath
$ git config 'core.hooksPath' .husky
# 也可通过写入文件配置 core.hooksPath
$ cat .git/config
[core]
ignorecase = true
precomposeunicode = true
hooksPath = .husky
在前端工程化中,husky
即通过自定义 core.hooksPath
并将 npm scripts
写入其中的方式来实现此功能。
安装依赖 npm install husky --save-dev
npm set-script prepare "husky install"
npm run prepare
// package.json 生成 "prepare":"husky install"
{
"scripts":{
"prepare":"husky install"
}
}
husky install
运行时需要在同一目录下存在 .git
,即有一个git仓库,运行成功后根目录会新建一个 .husky 目录
注意:set-script 需要 npm > 7.0 版本 可以使用 npm install -g npm
升版本。
npx husky add .husky/pre-commit "npm test"
卸载uninstall
npm uninstall husky && git config --unset core.hooksPath
lint-staged
可以让你当前的代码检查只检查本次修改更新的代码,并在出现错误的时候,自动修复并推送。
很多人不用 lint-staged,直接在 pre-commit 钩子里执行 npm run lint,这样有个问题,如果项目大了,你只修改一个文件,但它仍然会校验src下所有文件,就会导致提个代码慢的要死等半天。而 lint-staged 就能解决这个问题,它只会校验你修改的那部分文件。
每次它在你本地 commit 之前,校验你所提的内容是否符合你本地配置的 eslint 规则
eslint --fix
尝试帮你自动修复 2.1 修复成功,则会自动帮你把修复好的代码提交; 2.2 修复失败,提示你错误,让你修复好才可以提交代码;npm install lint-staged --save-dev
npx husky add .husky/pre-commit "npx lint-staged"
--ext 指定JavaScript文件扩展名-默认值:.js
--fix 自动修复问题
package.json
"scripts":{
"lint": "eslint src --fix --ext .js,.jsx,.ts,.tsx",
},
"lint-staged": {
"src/**":[
"npm run lint"
]
}
或
"scripts":{
"lint": "lint-staged",
}
"lint-staged": {
"**/*.js": "eslint --ext .js",
"**/*.vue": "eslint --ext .vue", // 可能需要安装eslint-plugin-vue
"src/**/*.{js,vue}": [
"prettier --write",
"eslint --fix",
// "git add" // 从v10.0.0开始,对最初暂存文件的任何新修改都将自动添加到提交中。如果您的任务以前包含git add步骤,请删除此步骤。自动行为可确保竞争条件较少,因为尝试同时运行多个git操作通常会导致错误。
]
},
或使用yorkie,而不使用husky
npm install yorkie --save-dev
"scripts":{
"lint": "eslint src --fix --ext .js,.jsx,.ts,.tsx",
},
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"src/**":[
"npm run lint"
]
}
<type>(<scope>): <subject> #header部分
// 空一行
<body>
// 空一行
<footer>
Header是必需,Body 和 Footer 可以省略
Header部分只有一行,包括三个字段:type(必需)、scope(可选)和subject(必需)。
用于说明本次commit的类型
允许使用下面个标识
Type | Description |
---|---|
feat | A new feature |
fix | A bug fix |
docs | Documentation only changes |
style: | Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) |
refactor | A code change that neither fixes a bug nor adds a feature |
perf | A code change that improves performance |
test | Adding missing or correcting existing tests |
chore | Changes to the build process or auxiliary tools and libraries such as documentation generation |
如果type为feat、fix、perf、revert,则该 commit 将肯定出现在 Change log 之中。其他情况(docs、chore、style、test)由你决定,要不要放入 Change log。
Revert
还有一种特殊情况,如果当前 commit 用于撤销以前的 commit,则必须以revert:开头,后面跟着被撤销 Commit 的 Header。
Body部分的格式是固定的,必须写成This reverts commit . 其中的hash是被撤销 commit 的 SHA 标识符。
revert: feat(pencil): add 'graphiteWidth' option
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
其中的hash是被撤销 commit 的 SHA 标识符。
用于说明本次commit影响的范围,比如首页、详情页等。当更改影响多个范围时,可以使用*。
用于说明本次commit的简短描述
ticket
是 commit 相关联的 issue 编号。每一次 commit 都可以选择性的与某个 issue 关联。
比如在 message 中添加 #n,就可以与第 n 个 issue 进行关联。这个提交会作为一个 comment ,出现在编号为1的 issue 记录中。
:gitmoji: type(scope): subject(#1)
如果添加:
close #n
closes #n
closed #n
fix #n
fixes #n
fixed #n
resolve #n
resolves #n
resolved #n
比如以下 Commit 可以自动关闭第 4 个 issue 。
:bug: fix: Fix bug(#4)
用于本次commit的详细描述,可以分成多行
使用祈使句,现在时态:"change" not "changed" nor "changes"
<type>(<scope>): <subject>
- first changes... #body部分
- second changes... #body部分
只用于下面两种情况
如果当前代码与上一个版本出现重大变动或不兼容,如版本升级、接口参数减少、接口删除、迁移等就是破坏性修改。则 Footer 部分以BREAKING CHANGE开头,后面空一格或两个换行符,然后描述变动、以及变动理由和迁移方法。
BREAKING CHANGE: 变动的描述\理由\迁移方法
如果当前 commit 针对某个 issue,那么可以在 Footer 部分关闭这个 issue,也可以关闭多个 issue。填写格式如下:
Close #ISSUE_ID
Closes #ISSUE_ID, #ISSUE_ID
使用Commitizen提交时,系统会提示您在提交时填写所有必需的提交字段。
adapter(适配器) : 更换 commitizen 命令行工具的交互方式插件。
npm install -D commitizen cz-customizable
安装成功以后,这意味着你可以不再使用 git commit
的方式提交你的代码。
在你执行完 git add
后,执行 npx cz
或 npm run commit
替代 git commit
,commitizen 就可以一步步帮助你完成提交了。
// package.json
{
"scripts":{
"commit":"cz"
}
}
// package.json
{
"config": {
"commitizen": {
"path": "./node_modules/cz-customizable"
},
}
}
根目录新建 .cz-config.js
module.exports = {
types: [
{ value: 'feat', name: '✨ feat: A new feature' },
{ value: 'fix', name: '🔧 fix: A bug fix' },
{ value: 'docs', name: '📝 docs: Documentation only changes' },
{ value: 'style', name: '🎨 style: Changes that do not affect the meaning of the code' },
{ value: 'refactor', name: '♻️ refactor: A code change that neither fixes a bug nor adds a feature' },
{ value: 'perf', name: '🚀 perf: A code change that improves performance' },
{ value: 'test', name: '🧪 test: Adding missing or correcting existing tests' },
{ value: 'ci', name: '👷 ci: CI configuration related e.g. changes to k8s,docker configuration files' },
{ value: 'build', name: '📦 build: Changes to the build process or auxiliary tools' },
{ value: 'chore', name: '🍻 chore: chroe' },
{ value: 'revert', name: '💊 revert: Reverts a previous commit' }
],
scopes: [
{ name: 'docs' },
{ name: 'ci' },
{ name: 'style' },
],
// allowTicketNumber: false,
// isTicketNumberRequired: false,
// ticketNumberPrefix: 'TICKET-',
// ticketNumberRegExp: '\\d{1,5}',
// it needs to match the value for field type. Eg.: 'fix'
// 当 type 为 fix 时,重写 scopes 的值
/* scopeOverrides: {
fix: [
{name: 'merge'},
{name: 'style'},
{name: 'e2eTest'},
{name: 'unitTest'}
]
}, */
// override the messages, defaults are as follows
messages: {
type: "Select the type of change that you're committing:",
scope: 'Denote the SCOPE of this change (optional):',
// used if allowCustomScopes is true
customScope: 'Denote the SCOPE of this change:',
subject: 'Write a SHORT, IMPERATIVE tense description of the change:\n',
body: 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n',
breaking: 'List any BREAKING CHANGES (optional):\n',
footer: 'List any ISSUES CLOSED by this change (optional). E.g.: #31, #34:\n',
confirmCommit: 'Are you sure you want to proceed with the commit above?',
},
allowCustomScopes: true,
allowBreakingChanges: ['feat', 'fix'],
subjectLimit: 100,
// skip any questions you want
// skipQuestions: ['body'],
// limit subject length
// breaklineChar: '|', // It is supported for fields body and footer.
// footerPrefix : 'ISSUES CLOSED:'
// askForBreakingChangeFirst : true, // default is false
}
// package.json
{
"config": {
"commitizen": {
"path": "./node_modules/cz-emoji"
},
"cz-emoji": {
"types": [
{
"emoji": "✨",
"code": "✨ feat",
"description": "A new feature",
"name": "feature"
},
{
"emoji": "🔧",
"code": "🔧 fix",
"description": " A bug fix",
"name": "fix"
},
{
"emoji": "📝",
"code": "📝 docs",
"description": "Documentation only changes",
"name": "docs"
},
{
"emoji": "🎨",
"code": "🎨 style",
"description": "Changes that do not affect the meaning of the code",
"name": "style"
},
{
"emoji": "♻️",
"code": "♻️ refactor",
"description": " A code change that neither fixes a bug nor adds a feature",
"name": "refactor"
},
{
"emoji": "🚀",
"code": "🚀 perf",
"description": "A code change that improves performance",
"name": "perf"
},
{
"emoji": "🧪",
"code": "🧪 test",
"description": "**Adding missing or correcting existing tests",
"name": "test"
},
{
"emoji": "👷",
"code": "👷 ci",
"description": "CI configuration related e.g. changes to k8s,docker configuration files",
"name": "ci"
},
{
"emoji": "📦",
"code": "📦 build",
"description": "Changes to the build process or auxiliary tools",
"name": "build"
},
{
"emoji": "🍻",
"code": "🍻 chore",
"description": "chore",
"name": "chore"
},
{
"emoji": "💊",
"code": "💊 revert",
"description": "Reverts a previous commit",
"name": "revert"
}
],
"scopes": [
"docs",
"ci",
"style"
],
"subjectMaxLength": 50
}
}
}
我们也可以使用 vscode 插件 git-commit-plugin
实现上述的功能。
Lint commit messages.
npm i -D @commitlint/cli @commitlint/config-conventional
module.exports = {
extends: ['@commitlint/config-conventional'],
/*
* Any rules defined here will override rules from extends-config
*/
// 0 disables the rule. For 1 it will be considered a warning for 2 an error.
// always|never: never inverts(取反) the rule.
rules: { // 要和 .cz-config.js 的 type 保持完全一致
"type-enum": [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'ci', 'build', 'chore', 'revert']]
},
}
npm i -D @commitlint/cli commitlint-config-git-commit-emoji
commitlint.config.js
数组里的type类型(包括emoji)要和 .cz-config.js 的 value 保持一致
module.exports = {
extends: ['git-commit-emoji'], // commitlint-config-git-commit-emoji 可省略前缀 commitlint-config-
/*
* Any rules defined here will override rules from extends-config
*/
rules: { // 要和 .cz-config.js 的 type 保持完全一致
"type-enum": [2, 'always', ['✨ feat', '🔧 fix', '📝 docs', '🎨 style', '♻️ refactor', '🚀 perf', '🧪 test', '👷 ci', '📦 build', '🍻 chore', '⏪️ revert']]
},
}
添加 commitlint 的 hook 到 husky 中,commit-msg
时进行校验
# Add hook
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
# Sometimes above command doesn't work in some command interpreters
# You can try other commands below to write npx --no -- commitlint --edit $1
# in the commit-msg file.
npx husky add .husky/commit-msg \"npx --no -- commitlint --edit '$1'\"
# or
npx husky add .husky/commit-msg "npx --no -- commitlint --edit $1"
# or
yarn husky add .husky/commit-msg 'yarn commitlint --edit $1'
semantic-release自动化了整个软件包发布工作流,包括:确定下一个版本号、生成release说明和发布软件包。(打tag、release、修改package.json version、生成CHANGLOG.md,npm publish)
npm i -D semantic-release @semantic-release/changelog @semantic-release/git conventional-changelog-conventionalcommits
semantic-release 的依赖包,无须再下载:
"@semantic-release/commit-analyzer" "@semantic-release/release-notes-generator" "@semantic-release/npm" "@semantic-release/github"
文件夹根目录创建 .releaserc.js
// semantic-release 配置
module.exports = {
branches: [ // 指定在哪个分支下要执行发布操作
'master',
{
name: 'beta', prerelease: true // beta 分支为 预发布 分支 2.0.0-beta.1, 2.0.0-beta.2, etc...
}
],
plugins: [ // 插件有顺序要求
[
'@semantic-release/commit-analyzer', // semantic-release plugin to analyze commits with conventional-changelog
{
preset: "conventionalcommits", // 需安装 conventional-changelog-conventionalcommits
// 没列在这里或者不在 default release rules(https://github.com/semantic-release/commit-analyzer/blob/master/lib/default-release-rules.js) 里的规则,不会进行 release
"releaseRules": [
{ type: 'feat', release: 'minor' },
{ type: 'fix', release: 'patch' },
{ type: 'perf', release: 'patch' },
{ type: 'style', release: 'patch' },
{ type: 'revert', release: 'patch' },
{ breaking: true, release: 'major' }, // https://www.conventionalcommits.org/en a commit that has a footer BREAKING CHANGE:
],
"parserOpts": {
"noteKeywords": ["BREAKING CHANGE", "BREAKING CHANGES"] // the commits that contains BREAKING CHANGE, BREAKING CHANGES or BREAKING in their body will be considered breaking changes(by default the angular preset checks only for BREAKING CHANGE and BREAKING CHANGES)
}
}
],
['@semantic-release/release-notes-generator', // semantic-release plugin to generate changelog content(非文件) with conventional-changelog
{
preset: "conventionalcommits",
presetConfig: { // Conventional Changelog Configuration Spec, https://github.com/conventional-changelog/conventional-changelog-config-spec
"types": [
{ "type": "feat", "section": "✨ Features | 新功能" },
{ "type": "fix", "section": "🛠️ Bug Fixes | Bug 修复" },
{ "type": "docs", "section": "📝 Documentation | 文档" },
{ "type": "style", "section": "🎨 Styles | 风格" },
{ "type": "refactor", "section": "♻️ Code Refactoring | 代码重构" },
{ "type": "perf", "section": "🚀 Performance Improvements | 性能优化" },
{ "type": "test", "section": "📸 Tests | 测试" },
{ "type": "build", "section": "📦 Build System | 打包构建" },
{ "type": "ci", "section": "🐳 Continuous Integration | CI 配置" },
{ "type": "chore", "section": "🍮 Chore | 构建/工程依赖/工具" },
{ "type": "revert", "section": "💊 Revert | 回退" }
]
}
}
],
[
'@semantic-release/changelog', // semantic-release plugin to create or update a changelog file
{
changelogFile: 'CHANGELOG.md'
}
],
// 需要NPM_TOKEN,命令行cross-env、或 .npmrc 文件 或在 github actions中定义
// 根据commit type,Update the package.json version
'@semantic-release/npm', // semantic-release plugin to publish a npm package.
// 需要GITHUB_TOKEN
[
'@semantic-release/github', // 创建 github-release
{
labels: false // The labels to add to the issue created when a release fails. Set to false to not add any label.
}
],
[
'@semantic-release/git', // semantic-release plugin to commit release assets to the project's git repository.
{
assets: ['CHANGELOG.md', 'package.json'], // release成功后,远程仓库的CHANGELOG.md、package.json 会更新,本地仓库需要 git pull 拉取
message: "🍺 chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
conventional-changelog-emoji-config 能够将 :emoji: type: …
或 表情 type: …
格式进行 type 分类。
但不支持 @semantic-release/release-notes-generator
中的presetConfig
,因为conventional-changelog-emoji-config的配置不符合conventional-changelog-config-spec 规范
根目录新建 .changelogrc.js
// conventional-changelog-emoji-config 配置
module.exports = {
displayTypes: ['build', 'ci', 'feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'revert',]
}
使用 semantic-release 就可以满足我们发布和changlog的需求。
standard-version 是一款遵循语义化版本( semver)和 commit message 标准规范 的版本和 changlog 自动化工具。通常情况下,我们会在 master 分支进行如下的版本发布操作:
git pull origin master
根据 pacakage.json 中的 version 更新版本号,更新 changelog
git add -A, 然后 git commit
git tag 打版本操作
push 版本 tag 和 master 分支到仓库
其中2,3,4则是 standard-version 工具会自动完成的工作,配合本地的 shell 脚本,则可以自动完成一系列版本发布的工作了。
npm i -D standard-version conventional-changelog-emoji-config
conventional-changelog-emoji-config
或 conventional-changelog-gitmoji-config
—— emoji 风格的 conventional-changelog 规则,后面会讲到package.json
{
"scripts": {
"release": "standard-version",
"release-major": "standard-version --release-as major",
"release-minor": "standard-version --release-as minor",
"release-patch": "standard-version --release-as patch"
}
}
版本构成
https://semver.org/lang/zh-CN/
版本号 major.minor.patch
major
: 当你发了一个含有 Breaking Change 的 APIminor
: 当你新增了一个向后兼容的功能时patch
: 当你修复了一个向后兼容的 Bug命令说明
release
: 按照standard-version
默认规则升级版本号release-major
: 升级major版本号release-minor
: 升级minor版本号release-patch
: 升级patch版本号standard-version 默认版本升级规则
执行以下命令,就会根据你的commit信息自动生成 CHANGELOG.md 文件。
npm run release
当你的commit type是 feat和fix的时候执行这个命令,它会自增 package.json 版本号。
根目录新建 versionrc.js
module.exports = {
"types": [
{ "type": "feat", "section": "✨ Features | 新功能" },
{ "type": "fix", "section": "🔧 Bug Fixes | Bug 修复" },
{ "type": "docs", "section": "📝 Documentation | 文档" },
{ "type": "style", "section": "🎨 Styles | 风格" },
{ "type": "refactor", "section": "♻️ Code Refactoring | 代码重构" },
{ "type": "perf", "section": "🚀 Performance Improvements | 性能优化" },
{ "type": "test", "section": "📸 Tests | 测试" },
{ "type": "build", "section": "📦 Build System | 打包构建" },
{ "type": "ci", "section": "🐳 Continuous Integration | CI 配置" },
{ "type": "chore", "section": "🍮 Chore | 构建/工程依赖/工具" },
{ "type": "revert", "section": "💊 Revert | 回退" }
]
}
"types": [
{"type": "feat", "section": "Features"},
{"type": "fix", "section": "Bug Fixes"},
{"type": "chore", "hidden": true},
{"type": "docs", "hidden": true},
{"type": "style", "hidden": true},
{"type": "refactor", "hidden": true},
{"type": "perf", "hidden": true},
{"type": "test", "hidden": true}
]
types
name | type | required | default | description |
---|---|---|---|---|
type | string |
✔️ | N/A |
A string used to match <type> s used in the Conventional Commits convention. |
section | string |
✖️ | N/A |
The section where the matched commit type will display in the CHANGELOG. |
hidden | boolean |
✖️ | N/A |
Set to true to hide matched commit type s in the CHANGELOG. |
standard-version 默认的预设是 angular ,像之前自定义表情那种commmit,规范和angular不一样,它读不到对应的type生成的更改日志是没法分类的。根据 :emoji: type: … 格式生成 CHANGELOG 的工具:
conventional-changelog-emoji-config
conventional-changelog-gitmoji-config
npm i -D conventional-changelog-emoji-config
npx husky add .husky/pre-push "npm run release"
{
"scripts": {
"release": "standard-version --preset emoji-config",
"release-major": "standard-version --preset emoji-config --release-as major",
"release-minor": "standard-version --preset emoji-config --release-as minor",
"release-patch": "standard-version --preset emoji-config --release-as patch"
}
}
可以省略前缀 conventional-changelog-
根目录新建 .changelogrc.js
module.exports = {
// changlog 可以显示的type
displayTypes: ['build', 'ci', 'feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'revert',]
}
如何确保所有 npm install
的依赖都是安全的?
当有一个库偷偷在你的笔记本后台挖矿怎么办?
比如,不久前一个周下载量超过八百万的库被侵入,它在你的笔记本运行时会偷偷挖矿。
Audit
,审计,检测你的所有依赖是否安全。npm audit
/yarn audit
均有效。
通过审计,可看出有风险的 package
、依赖库的依赖链、风险原因及其解决方案。
https://docs.npmjs.com/cli/v6/commands/npm-audit
$ npm audit
┌───────────────┬──────────────────────────────────────────────────────────────┐
│ high │ Regular Expression Denial of Service in trim │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Package │ trim │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Patched in │ >=0.0.3 │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Dependency of │ @mdx-js/loader │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ Path │ @mdx-js/loader > @mdx-js/mdx > remark-mdx > remark-parse > │
│ │ trim │
├───────────────┼──────────────────────────────────────────────────────────────┤
│ More info │ https://www.npmjs.com/advisories/1002775 │
└───────────────┴──────────────────────────────────────────────────────────────┘
76 vulnerabilities found - Packages audited: 1076
Severity: 49 Moderate | 27 High
✨ Done in 4.60s.
仅针对生产依赖审计
$ npm audit production
$ yarn audit dependencies
通过 npm audit fix
可以自动修复该库的风险,原理就是升级依赖库,升级至已修复了风险的版本号。
$ npm audit fix
yarn audit
无法自动修复,需要使用 yarn upgrade
手动更新版本号,不够智能。
synk是一个高级版的 npm audit
,可自动修复,且支持 CICD 集成与多种语言。
$ npx snyk
$ npx wizard
当dependabot检测到有问题时,会自动向该仓库提交 PR。
开启方式比较简单,仅需将 dependabot.yml
配置文件放入仓库的 .github
目录中即可开启。之后 Dependabot 就会自动提交 PR 来更新您项目中的依赖项了。
您也可以在 GitHub 页面上进行操作,在仓库页面通过 Insights
-> Dependency graph
-> Dependabot
-> Enable Dependabot
路径即可开启,之后就可以点击 Create config file
来创建配置文件了。
例子
# Basic dependabot.yml file with
# minimum configuration for two package managers
version: 2
updates:
# Enable version updates for npm
- package-ecosystem: "npm"
# Look for `package.json` and `lock` files in the `root` directory
directory: "/"
# Check the npm registry for updates every day (weekdays)
schedule:
interval: "daily"
# Enable version updates for Docker
- package-ecosystem: "docker"
# Look for a `Dockerfile` in the `root` directory
directory: "/"
# Check for updates once a week
schedule:
interval: "weekly"
使用 npm outdated
虽能发现需要升级版本号的 package,但仍然需要手动在 package.json 更改版本号进行升级。
此时推荐一个功能更强大的工具 npm-check-updates
,比 npm outdated
强大百倍。
npm-check-updates -u
,可自动将 package.json 中待更新版本号进行重写。
升级 [minor] 小版本号,有可能引起 Break Change
,可仅仅升级到最新的 patch 版本。
$ npx npm-check-updates --target patch
给npm包在本地环境打补丁
假设 lodash
有一个 Bug,影响线上开发,应该怎么办?
答: 三步走。
很合理很规范的一个流程,但是它一个最大的问题就是,太慢了,三步走完黄花菜都凉了。
此时可直接上手修改 node_modules
中 lodash 代码,并修复问题!
新问题:node_modules
未纳入版本管理,在生产环境并没有用。请看流程
node_modules/lodash
,本地正常运行 ✅npm i lodash
,lodash 未被修改,线上运行失败 ❌此时有一个简单的方案,临时将修复文件纳入工作目录,可以解决这个问题
node_modules/lodash
,本地正常运行 ✅${work_dir}/patchs/lodash
中,纳入版本管理npm i lodash
,并将修改文件再度复制到 node_modules/lodash
中,线上正常运行 ✅但此时并不是很智能,且略有小问题,演示如下:
node_modules/lodash
,本地正常运行 ✅${work_dir}/patchs/lodash
中,纳入版本管理 ✅npm i lodash
,并将修改文件再度复制到 node_modules/lodash
中,线上正常运行 ✅lodash
,该问题得以解决,而我们代码引用了 lodash 的新特性npm i lodash
,并将修改文件再度复制到 node_modules/lodash
中,由于已更新了 lodash,并且依赖于新特性,线上运行失败 ❌此时有一个万能之策,那就是 patch-package
想要知道 patch-package
如何解决上述问题,请先了解下它的用法,流程如下
$ npm i -D patch-package
# 在 package.json scripts 增加命令 "postinstall":"patch-package"
# 修改 lodash 的一个小问题
$ vim node_modules/lodash/index.js
# 对 lodash 的修复,生成一个 patch 文件,位于 {workRoot}/patches/lodash+4.17.21.patch
$ npx patch-package lodash
# 将修复文件提交到版本管理之中
$ git add patches/lodash+4.17.21.patch
$ git commit -m "fix 一点儿小事 in lodash"
# 当其他同事拉到代码如何应用补丁呢?让同事执行 npm i
# 此后的命令在生产环境或 CI 中执行
# 此后的命令在生产环境或 CI 中执行
# 此后的命令在生产环境或 CI 中执行
# 在生产环境装包
$ npm i
# 大功告成!
patch-package
只会当前版本生效
如果你装的包版本和你之前生成的补丁中记录的版本不一样,npx patch-package
会直接报错**ERROR** Failed to apply patch for package xxxx at path
web应用的生产环境构建需要 lock-file 锁定依赖版本
当我们在 npm i
某个依赖时,默认的版本号是最新版本号 ^1.2.3
,以 ^
开头可最大限度地使用新特性,但是某些库不遵循该依赖可能出现问题。
^1.2.3
指 >=1.2.3 <2.0.0
一个问题: 当项目中没有 lock 文件时,生产环境的风险是如何产生的?
演示风险过程如下:
pkg 1.2.3
: 首次在开发环境安装 pkg 库,为此时最新版本 1.2.3
,dependencies
依赖中显示 ^1.2.3
,实际安装版本为 1.2.3
pkg 1.19.0
: 在生产环境中上线项目,安装 pkg 库,此时最新版本为 1.19.0
,满足 dependencies
中依赖 ^1.2.3
范围,实际安装版本为 1.19.0
,但是 pkg
未遵从 semver 规范,在此过程中引入了 Breaking Change,如何此时 1.19.0
有问题的话,那生产环境中的 1.19.0
将会导致 bug,且难以调试
而当有了 lock 文件时,每一个依赖的版本号都被锁死在了 lock 文件,每次依赖安装的版本号都从 lock 文件中进行获取,避免了不可测的依赖风险。
https://stackoverflow.com/questions/11030552/what-does-rc-mean-in-dot-files
“rc”后缀可以追溯到Unix的祖父母CTSS。它有一个名为“runcom”的命令脚本功能。早期的Unix使用“rc”作为操作系统引导脚本的名称,以此向CTSS runcom致敬。
TypeDoc
帮助我们自动生成文档并部署到 GitHub pages。此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。