
一些linter&formatter最佳实践
在创建新项目的时候,往往会经历一些痛苦,最知名的无非就是配置 linter 和 formatter 了。在这里记录一些最佳实践。
181字
花了好长时间调研的一些最佳实践,感觉基本上应该是够用了(?
这里的纯 eslint 部分是参考了 @antfu/eslint-config 的配置,rules 里面塞了一些格式化规则。
建议如果用的话,最好也加一下 .vscode/settings.json,能够确保正确开启这些扩展,以及保证不会被其他的格式化扩展影响修复行为。
纯 TypeScript 的 Node.js 项目
eslint
安装依赖
bash
pnpm add -D eslint @eslint/js @typescript-eslint/eslint-plugin @typescript-eslint/parser typescript-eslint eslint-plugin-n eslint-plugin-unicorn eslint-plugin-simple-import-sort eslint-plugin-jsonc jsonc-eslint-parser eslint-plugin-yml yaml-eslint-parser eslint-plugin-markdown
eslint.config.mjs
javascript
// @ts-check
import eslint from '@eslint/js'
import markdown from '@eslint/markdown'
import eslintPluginJsonc from 'eslint-plugin-jsonc'
import pluginN from 'eslint-plugin-n'
import simpleImportSort from 'eslint-plugin-simple-import-sort'
import eslintPluginUnicorn from 'eslint-plugin-unicorn'
import eslintPluginYml from 'eslint-plugin-yml'
import tseslint from 'typescript-eslint'
export default tseslint.config(
eslint.configs.recommended,
markdown.configs.processor,
tseslint.configs.strict,
tseslint.configs.stylistic,
eslintPluginUnicorn.configs.recommended,
pluginN.configs['flat/recommended-module'],
...eslintPluginJsonc.configs['flat/recommended-with-jsonc'],
...eslintPluginYml.configs['flat/recommended'],
{
languageOptions: {
sourceType: 'module',
},
plugins: {
'simple-import-sort': simpleImportSort,
},
rules: {
/* plugin rules */
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
/* stylistic rules */
'quotes': ['error', 'single', { avoidEscape: true }],
'indent': ['error', 2, { SwitchCase: 1 }],
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
'object-curly-spacing': ['error', 'always'],
'array-bracket-spacing': ['error', 'never'],
'computed-property-spacing': ['error', 'never'],
'array-element-newline': ['error', 'consistent'],
'comma-spacing': ['error', { before: false, after: true }],
'arrow-parens': ['error', 'always'],
'comma-dangle': ['error', 'always-multiline'],
'space-before-function-paren': [
'error',
{
anonymous: 'always',
named: 'never',
asyncArrow: 'always',
},
],
'padding-line-between-statements': [
'error',
{ blankLine: 'always', prev: 'multiline-block-like', next: '*' },
{ blankLine: 'always', prev: '*', next: 'return' },
{ blankLine: 'always', prev: 'const', next: 'function' },
{ blankLine: 'always', prev: 'let', next: 'function' },
{ blankLine: 'any', prev: 'const', next: 'const' },
{ blankLine: 'any', prev: 'let', next: 'let' },
],
'no-multi-spaces': 'error',
'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0 }],
'dot-location': ['error', 'property'],
'no-empty': ['error', { allowEmptyCatch: true }],
},
},
)
.vscode/settings.json
json
{
"prettier.enable": false,
"biome.enabled": false,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"[typescript]": {
"editor.defaultFormatter": null
},
"[javascript]": {
"editor.defaultFormatter": null
},
"eslint.rules.customizations": [
{ "rule": "@stylistic/*", "severity": "off" },
{ "rule": "*-indent", "severity": "off" },
{ "rule": "*-spacing", "severity": "off" },
{ "rule": "*-spaces", "severity": "off" },
{ "rule": "*-order", "severity": "off" },
{ "rule": "*-dangle", "severity": "off" },
{ "rule": "*-newline", "severity": "off" },
{ "rule": "*-multiline", "severity": "off" },
{ "rule": "*quotes", "severity": "off" },
{ "rule": "*semi", "severity": "off" }
],
"eslint.validate": [
"javascript",
"typescript",
"javascriptreact",
"typescriptreact",
"json",
"jsonc",
"yaml",
"markdown"
]
}
eslint + prettier
安装依赖
bash
pnpm add -D eslint @eslint/js eslint-plugin-n eslint-plugin-unicorn @eslint/markdown eslint-plugin-jsonc eslint-plugin-yml eslint-config-prettier typescript-eslint prettier @trivago/prettier-plugin-sort-imports
eslint.config.mjs
javascript
// @ts-check
import eslint from '@eslint/js'
import markdown from '@eslint/markdown'
import eslintConfigPrettier from 'eslint-config-prettier'
import eslintPluginJsonc from 'eslint-plugin-jsonc'
import pluginN from 'eslint-plugin-n'
import eslintPluginUnicorn from 'eslint-plugin-unicorn'
import eslintPluginYml from 'eslint-plugin-yml'
import tseslint from 'typescript-eslint'
export default tseslint.config(
eslint.configs.recommended,
tseslint.configs.strict,
eslintPluginUnicorn.configs.recommended,
pluginN.configs['flat/recommended-module'],
markdown.configs.processor,
...eslintPluginJsonc.configs['flat/recommended-with-jsonc'],
...eslintPluginYml.configs['flat/recommended'],
{
rules: {
'no-empty': ['error', { allowEmptyCatch: true }],
},
},
eslintConfigPrettier,
)
prettier.config.mjs
javascript
/** @type {import("prettier").Config} */
export default {
plugins: ['@trivago/prettier-plugin-sort-imports'],
singleQuote: true,
tabWidth: 2,
trailingComma: 'all',
arrowParens: 'always',
bracketSpacing: true,
semi: false,
importOrder: ['^node:(.*)$', '<THIRD_PARTY_MODULES>', '^@/(.*)$', '^[./]'],
importOrderSeparation: true,
importOrderSortSpecifiers: true,
}
.prettierignore
text
# Dependencies
node_modules/
# Build output
dist/
build/
# Cache directories
.cache/
# Log files
*.log
# Environment files
.env*
# Coverage output
coverage/
# Lock files
package-lock.json
yarn.lock
pnpm-lock.yaml
.vscode/settings.json
json
{
"biome.enabled": false,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"eslint.validate": ["javascript", "typescript", "javascriptreact", "typescriptreact"]
}
biome
安装依赖
bash
pnpm add -D @biomejs/biome
biome.json
json
{
"$schema": "https://biomejs.dev/schemas/latest/schema.json",
"organizeImports": {
"enabled": true
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noExplicitAny": "warn",
"noShadowRestrictedNames": "error"
},
"style": {
"noNonNullAssertion": "warn",
"useFilenamingConvention": "error"
},
"correctness": {
"noSwitchDeclarations": "error"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"semicolons": "asNeeded",
"trailingCommas": "es5",
"quoteProperties": "asNeeded"
}
},
"json": {
"parser": {
"allowComments": true
},
"formatter": {
"enabled": true,
"trailingCommas": "none"
}
}
}
.vscode/settings.json
json
{
// Disable Prettier & ESLint
"prettier.enable": false,
"editor.codeActionsOnSave": {},
"eslint.enable": false,
// Enable Biome plugin formatting functionality
"editor.defaultFormatter": "biomejs.biome",
// Formatting related settings
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.formatOnType": false,
// Enable Biome formatter for specific languages
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
纯 JavaScript 的 Node.js 项目
eslint
安装依赖
bash
pnpm add -D eslint @eslint/js eslint-plugin-n eslint-plugin-simple-import-sort eslint-plugin-unicorn eslint-plugin-jsonc eslint-plugin-yml @eslint/markdown globals typescript-eslint
eslint.config.mjs
javascript
// @ts-check
import eslint from '@eslint/js'
import markdown from '@eslint/markdown'
import eslintPluginJsonc from 'eslint-plugin-jsonc'
import pluginN from 'eslint-plugin-n'
import simpleImportSort from 'eslint-plugin-simple-import-sort'
import eslintPluginUnicorn from 'eslint-plugin-unicorn'
import eslintPluginYml from 'eslint-plugin-yml'
import globals from 'globals'
import tseslint from 'typescript-eslint'
export default tseslint.config(
eslint.configs.recommended,
markdown.configs.processor,
pluginN.configs['flat/recommended-module'],
eslintPluginUnicorn.configs.recommended,
...eslintPluginJsonc.configs['flat/recommended-with-jsonc'],
...eslintPluginYml.configs['flat/recommended'],
{
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
...globals.node,
},
},
plugins: {
'simple-import-sort': simpleImportSort,
},
rules: {
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'quotes': ['error', 'single', { avoidEscape: true }],
'indent': ['error', 2, { SwitchCase: 1 }],
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
'object-curly-spacing': ['error', 'always'],
'array-bracket-spacing': ['error', 'never'],
'computed-property-spacing': ['error', 'never'],
'array-element-newline': ['error', 'consistent'],
'comma-spacing': ['error', { before: false, after: true }],
'arrow-parens': ['error', 'always'],
'comma-dangle': ['error', 'always-multiline'],
'space-before-function-paren': [
'error',
{
anonymous: 'always',
named: 'never',
asyncArrow: 'always',
},
],
'padding-line-between-statements': [
'error',
{ blankLine: 'always', prev: 'multiline-block-like', next: '*' },
{ blankLine: 'always', prev: '*', next: 'return' },
{ blankLine: 'always', prev: 'const', next: 'function' },
{ blankLine: 'always', prev: 'let', next: 'function' },
{ blankLine: 'any', prev: 'const', next: 'const' },
{ blankLine: 'any', prev: 'let', next: 'let' },
],
'no-multi-spaces': 'error',
'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 0 }],
'dot-location': ['error', 'property'],
'no-empty': ['error', { allowEmptyCatch: true }],
},
}
)
.vscode/settings.json
json
{
"prettier.enable": false,
"biome.enabled": false,
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"[typescript]": {
"editor.defaultFormatter": null
},
"[javascript]": {
"editor.defaultFormatter": null
},
"eslint.rules.customizations": [
{ "rule": "@stylistic/*", "severity": "off" },
{ "rule": "*-indent", "severity": "off" },
{ "rule": "*-spacing", "severity": "off" },
{ "rule": "*-spaces", "severity": "off" },
{ "rule": "*-order", "severity": "off" },
{ "rule": "*-dangle", "severity": "off" },
{ "rule": "*-newline", "severity": "off" },
{ "rule": "*-multiline", "severity": "off" },
{ "rule": "*quotes", "severity": "off" },
{ "rule": "*semi", "severity": "off" }
],
"eslint.validate": [
"javascript",
"typescript",
"javascriptreact",
"typescriptreact",
"json",
"jsonc",
"yaml",
"markdown"
]
}
eslint + prettier
安装依赖
bash
pnpm add -D eslint @eslint/js eslint-plugin-n eslint-plugin-unicorn @eslint/markdown eslint-plugin-jsonc eslint-plugin-yml eslint-config-prettier globals prettier @trivago/prettier-plugin-sort-imports
eslint.config.mjs
javascript
// @ts-check
import eslint from '@eslint/js'
import markdown from '@eslint/markdown'
import eslintConfigPrettier from 'eslint-config-prettier'
import eslintPluginJsonc from 'eslint-plugin-jsonc'
import pluginN from 'eslint-plugin-n'
import eslintPluginUnicorn from 'eslint-plugin-unicorn'
import eslintPluginYml from 'eslint-plugin-yml'
import globals from 'globals'
export default [
eslint.configs.recommended,
pluginN.configs['flat/recommended-module'],
eslintPluginUnicorn.configs.recommended,
markdown.configs.processor,
...eslintPluginJsonc.configs['flat/recommended-with-jsonc'],
...eslintPluginYml.configs['flat/recommended'],
{
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
...globals.node,
},
},
rules: {
'no-empty': ['error', { allowEmptyCatch: true }],
},
},
eslintConfigPrettier,
]
prettier.config.mjs
javascript
/** @type {import("prettier").Config} */
export default {
plugins: ['@trivago/prettier-plugin-sort-imports'],
singleQuote: true,
tabWidth: 2,
trailingComma: 'all',
arrowParens: 'always',
bracketSpacing: true,
semi: false,
importOrder: ['^node:(.*)$', '<THIRD_PARTY_MODULES>', '^@/(.*)$', '^[./]'],
importOrderSeparation: true,
importOrderSortSpecifiers: true,
}
.prettierignore
text
# Dependencies
node_modules/
# Build output
dist/
build/
# Cache directories
.cache/
# Log files
*.log
# Environment files
.env*
# Coverage output
coverage/
# Lock files
package-lock.json
yarn.lock
pnpm-lock.yaml
.vscode/settings.json
json
{
"biome.enabled": false,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"eslint.validate": ["javascript", "typescript", "javascriptreact", "typescriptreact"]
}
biome
安装依赖
bash
pnpm add -D @biomejs/biome
biome.json
json
{
"$schema": "https://biomejs.dev/schemas/latest/schema.json",
"organizeImports": {
"enabled": true
},
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noShadowRestrictedNames": "error"
},
"style": {
"useFilenamingConvention": "error"
},
"correctness": {
"noSwitchDeclarations": "error"
},
"nursery": {
"useValidTypeof": "error"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"semicolons": "asNeeded",
"trailingCommas": "es5",
"quoteProperties": "asNeeded"
}
},
"json": {
"parser": {
"allowComments": true
},
"formatter": {
"enabled": true,
"trailingCommas": "none"
}
}
}
.vscode/settings.json
json
{
// Disable Prettier & ESLint
"prettier.enable": false,
"editor.codeActionsOnSave": {},
"eslint.enable": false,
// Enable Biome plugin formatting functionality
"editor.defaultFormatter": "biomejs.biome",
// Formatting related settings
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.formatOnType": false,
// Enable Biome formatter for specific languages
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
}
}
评论0