WangShuXian6 / blog

FE-BLOG
https://wangshuxian6.github.io/blog/
MIT License
46 stars 10 forks source link

Lint #132

Open WangShuXian6 opened 2 years ago

WangShuXian6 commented 2 years ago

Lint

WangShuXian6 commented 2 years ago

React 项目代码格式化示例

install

yarn add @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-airbnb eslint-config-prettier eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks husky lint-staged  prettier stylelint stylelint-config-prettier stylelint-config-rational-order stylelint-config-standard stylelint-prettier  -D

更新配置文件后需重启编辑器并等待载入配置

package.json 旧版,已废弃

"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.10.0",
"@typescript-eslint/parser": "^5.10.0",
"eslint": "^8.7.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"husky": "^7.0.4",
"lint-staged": "^12.2.1",
"prettier": "^2.5.1",
"stylelint": "^14.2.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-standard": "^24.0.0",
"stylelint-prettier": "^2.0.0"
}

husky https://typicode.github.io/husky/

生成 husky pre-commit hook

npx husky init

执行该命令后,会看到.husky/目录下新增了一个名为pre-commit的shell脚本。 这样,在之后执行git commit命令时会先触发pre-commit这个脚本。 pre-commit脚本内容如下:

npm run lint

在 package 根结点添加脚本字段

"scripts": {
"lint": "lint-staged"
},
"lint-staged": {
"src/**/*.{html,css,scss,less}": [
"stylelint 'src/**/*.{html,css,scss,less}' --fix",
"prettier --write"
],
"src/**/*.{ts,tsx}": [
"eslint 'src/**/*.{ts,tsx}'  --fix",
"prettier --parser=typescript --write"
],
"src/**/*.{js,jsx}": [
"eslint 'src/**/*.{js,jsx}'  --fix",
"prettier --write"
]
},

.editorconfig

root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

eslint.config.js 9.0版本必须

import js from '@eslint/js'

export default [
  js.configs.recommended,
...]

typescript-eslint

npm install --save-dev eslint @eslint/js @types/eslint__js typescript typescript-eslint
export default [
  //...tseslint.configs.recommended,
  ...tseslint.configs.strict,
  ...tseslint.configs.stylistic,
...]

eslint-config-prettier https://github.com/prettier/eslint-config-prettier

npm install --save-dev eslint-config-prettier
import eslintConfigPrettier from 'eslint-config-prettier'

export default [
...
  eslintConfigPrettier//放在配置末尾
]

eslint-plugin-react https://www.npmjs.com/package/eslint-plugin-react

npm install eslint eslint-plugin-react --save-dev
import reactPlugin from 'eslint-plugin-react'

export default [
  reactPlugin.configs.flat.recommended,
...]

完整配置

import eslintConfigPrettier from 'eslint-config-prettier'
import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import reactPlugin from 'eslint-plugin-react'

export default [
  js.configs.recommended,
  //...tseslint.configs.recommended,
  ...tseslint.configs.strict,
  ...tseslint.configs.stylistic,
  reactPlugin.configs.flat.recommended,
  {
    files: ['**/*.{ts,tsx}'],
    ignores: [
      'node_modules',
      'src/lib/*',
      'typings',
      '.vscode',
      'config',
      'dist',
      'doc',
      'environments',
      'mock',
      'projects/*',
      'test',
      '*.less',
      '*.css',
      '*.md',
      '*.ico',
      '*.svg',
      '*.png',
      '*.jpg',
      '*.jpeg'
    ],
    languageOptions: {
      ecmaVersion: 13,
      sourceType: 'module',
      globals: {
        browser: true,
        node: true,
        commonjs: true,
        es2021: true
      },
      parserOptions: {
        ecmaFeatures: {
          jsx: true
        }
      }
    },
    plugins: {},
    rules: {
      'no-unused-vars': 'off',
      '@typescript-eslint/no-unused-vars': 'off',
      'no-useless-escape': 'off',
      '@typescript-eslint/no-explicit-any': 'off',
      '@typescript-eslint/ban-ts-comment': 'off',
      'react/react-in-jsx-scope': 'off',
      'react-hooks/exhaustive-deps': 'off'
    },
    settings: {
      'import/resolver': {
        node: {
          extensions: ['.js', '.jsx', '.ts', '.tsx']
        }
      },
      react: {
        version: 'detect'
      }
    }
  },
  eslintConfigPrettier
]

.eslintignore 已废弃,移动到 eslint.config.jsignores属性

node_modules
src/lib/*
typings
.vscode
config
dist
doc
environments
mock
projects/*
test
.less
.css
.md
.ico
.svg
.png
.jpg
.jpeg

.eslintrc.json 已废弃

{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true,
    "commonjs": true
  },
  //"extends": ["plugin:react/recommended", "airbnb"],
  "extends": [
    "plugin:@typescript-eslint/recommended", //让 ESLint 兼容 TypeScript
    "plugin:prettier/recommended", //箭头主体样式和首选箭头回调
    "plugin:react/jsx-runtime", //禁用 React 17的新 JSX 转换相关规则
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended", //强制执行Hooks 规则。
    //扩展"prettier"关闭了一堆核心 ESLint 规则,以及来自这些插件的一些规则
    "prettier" // Prettier 的配置将覆盖 extends 数组中先前任何 代码格式化 相关的 ESLint 配置,二者就能并行不悖地工作了
    //"prettier/@typescript-eslint" //禁用相关插件中所有关乎 代码格式化 的规则 "prettier/@typescript-eslint" has been merged into "prettier"
  ], // 排后的规则会覆盖前面
  "parser": "@typescript-eslint/parser", //让 ESLint 兼容 TypeScript
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 13,
    "sourceType": "module"
  },
  "plugins": ["react", "@typescript-eslint", "prettier"],
  "rules": {
    // "no-use-before-define": "off",
    // "@typescript-eslint/no-use-before-define": ["error"],
    // "no-param-reassign": "off",
    // "react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx", ".svg"] }],
    "no-unused-vars": "off",
    "@typescript-eslint/no-unused-vars": "off",
    "prettier/prettier": "error", //任何格式化错误就也被认为是 ESLint 错误了
    "no-useless-escape": "off", //禁用不必要的转义
    "@typescript-eslint/no-explicit-any": "off", // any 类型
    "@typescript-eslint/ban-ts-comment": "off", // "@ts-ignore"
    "react/react-in-jsx-scope": "off", // 'React' must be in scope when using JSX
    "react-hooks/exhaustive-deps": "off"
  },
  //rules 数组中自定义的规则会覆盖 prettier/@typescript-eslint 配置
  //代码格式化 规则应该在 .prettierrc 中配置
  "settings": {
    "import/resolver": {
      "node": {
        "extensions": [".js", ".jsx", ".ts", ".tsx"]
      }
    },
    "react": {
      //"createClass": "createReactClass", // Regex for Component Factory to use,
      // default to "createReactClass"
      //"pragma": "React",  // Pragma to use, default to "React"
      //"fragment": "Fragment",  // Fragment to use (may be a property of <pragma>), default to "Fragment"
      "version": "detect" // React version. "detect" automatically picks the version you have installed.
      // You can also use `16.0`, `16.3`, etc, if you want to override the detected value.
      // default to latest and warns if missing
      // It will default to "detect" in the future
      //"flowVersion": "0.53" // Flow version
    }
  }
}

.gitignore

# dependencies
node_modules/
examples/*/node_modules/
packages/*/node_modules/
packages/*/package-lock.json
package-lock.json

# build and test
build/
packages/*/dist/
packages/*/esm/
coverage/
scratch/
*.pyc
*.tsbuildinfo
scenarios/*/dist/

# logs
yarn-error.log
npm-debug.log
lerna-debug.log
local.log

# ide
.idea
*.sublime-*

# misc
.DS_Store
._*
.Spotlight-V100
.Trashes

.rpt2_cache

docs
lint-results.json

# legacy
tmp.js

# eslint
.eslintcache
eslintcache/*

# Parcel
.cache

# dist
dist/**/*
app/**/*/dist
backend/**/*/dist
doc/**/*/dist
examples/**/*/dist
packages/**/*/dist

# yarn
yarn.lock

.prettierignore

**/*.md
**/*.svg
**/*.ejs
**/*.html
src/lib/*
node_modules

.prettierrc

{
  "singleQuote": true,
  "tabWidth": 2,
  "bracketSpacing": true,
  "trailingComma": "none",
  "printWidth": 100,
  "semi": false,
  "overrides": [
    {
      "files": ".prettierrc",
      "options": { "parser": "typescript" }
    }
  ]
}

.stylelintignore

*.min.css

# 其他类型文件
*.js
*.jpg
*.woff

# 测试和打包目录
/test/
/dist/
.md
.ico
.svg
.png
.jpg
.jpeg

.stylelintrc.json

{
  "extends": [
    "stylelint-config-standard",
    "stylelint-config-rational-order",
    "stylelint-prettier/recommended"
  ],
  "rules": {
    "at-rule-no-unknown": null,
    "rule-empty-line-before": [
      "always-multi-line",
      {
        "except": ["first-nested"],
        "ignore": ["after-comment"]
      }
    ],
    "at-rule-empty-line-before": [
      "always",
      {
        "except": ["blockless-after-same-name-blockless", "first-nested"],
        "ignore": ["after-comment"]
      }
    ],
    "comment-empty-line-before": [
      "always",
      {
        "except": ["first-nested"],
        "ignore": ["stylelint-commands"]
      }
    ],
    "block-no-empty": true,
    "declaration-empty-line-before": "never",
    "declaration-block-no-duplicate-properties": true,
    "declaration-block-no-redundant-longhand-properties": true,
    "shorthand-property-no-redundant-values": true,
    "function-url-quotes": "always",
    "color-hex-length": "short",
    "color-named": "never",
    "comment-no-empty": true,
    "font-family-name-quotes": null,
    "font-weight-notation": "numeric",
    "property-no-vendor-prefix": true,
    "value-no-vendor-prefix": true,
    "selector-no-vendor-prefix": true,
    "no-descending-specificity": null,
    "no-invalid-double-slash-comments": null,
    "no-empty-source": null,
    "selector-class-pattern": null,
    "font-family-no-missing-generic-family-keyword": null
  }
}

tsconfig.json

{
  "compilerOptions": {
    "alwaysStrict": false,
    "declaration": true,
    "declarationMap": true,
    "downlevelIteration": true,
    "importHelpers": true,
    "inlineSources": true,
    "lib": ["es6", "dom"],
    // "module": "commonjs", // implied by "target" : "es5"
    "moduleResolution": "node",
    "noEmitHelpers": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitUseStrict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "plugins": [
      {
        "name": "typescript-tslint-plugin",
        "configFile": "./tslint.json",
        "alwaysShowRuleFailuresAsWarnings": false,
        "ignoreDefinitionFiles": true,
        "mockTypeScriptVersion": false,
        "suppressWhileTypeErrorsPresent": false
      }
    ],
    "pretty": true,
    "sourceMap": true,
    "strict": true,
    "strictBindCallApply": false,
    "target": "es5",
    "allowSyntheticDefaultImports": true,
    "types": ["node"],
    "noErrorTruncation": true, // move me up to @sentry/typescript
  }
}
WangShuXian6 commented 2 years ago

Stylelint

Stylelint 支持 scss

安装 postcss-scss

yarn add postcss-scss -D

更新 配置文件 追加 "customSyntax" : "postcss-scss" .stylelintrc.json

"rules":{}
"customSyntax" : "postcss-scss"
WangShuXian6 commented 2 years ago

monorepo 项目中子包package.json必须有 lint-staged配置

"lint-staged": {
    "src/**/*.{html,css,scss,less}": [
      "stylelint 'src/**/*.{html,css,scss,less}' --fix",
      "prettier --write"
    ],
    "src/**/*.{ts,tsx}": [
      "eslint 'src/**/*.{ts,tsx}'  --fix",
      "prettier --parser=typescript --write"
    ],
    "src/**/*.{js,jsx}": [
      "eslint 'src/**/*.{js,jsx}'  --fix",
      "prettier --write"
    ]
  }
WangShuXian6 commented 2 years ago

Vue3 项目代码格式化示例

依赖

pnpm install -D @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser @vitejs/plugin-vue eslint eslint-config-prettier eslint-plugin-vue husky lint-staged postcss-html prettier stylelint stylelint-config-prettier stylelint-config-rational-order stylelint-config-recommended-vue stylelint-config-standard stylelint-prettier postcss-less

package.json

{
  "name": "vue3-vite-lint",
  "private": true,
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc --noEmit && vite build",
    "preview": "vite preview",
    "lint":"lint-staged",
    "-prepare": "husky install"
  },
  "dependencies": {
    "vue": "^3.2.25",
    "vue-router": "4"
  },
  "devDependencies": {
    "@types/node": "^18.0.0",
    "@typescript-eslint/eslint-plugin": "^5.30.0",
    "@typescript-eslint/parser": "^5.30.0",
    "@vitejs/plugin-vue": "^2.3.3",
    "eslint": "^8.18.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-plugin-vue": "^9.1.1",
    "husky": "^8.0.1",
    "lint-staged": "^13.0.3",
    "postcss-html": "^1.4.1",
    "postcss-less": "^6.0.0",
    "prettier": "2.7.1",
    "stylelint": "^14.9.1",
    "stylelint-config-prettier": "^9.0.3",
    "stylelint-config-rational-order": "^0.1.2",
    "stylelint-config-recommended-vue": "^1.4.0",
    "stylelint-config-standard": "^26.0.0",
    "stylelint-prettier": "^2.0.0",
    "typescript": "^4.5.4",
    "vite": "^2.9.9",
    "vue-tsc": "^0.34.7"
  },
  "lint-staged": {
    "src/**/*.{html,css,scss,less,vue}": [
      "stylelint 'src/**/*.{html,css,scss,less,vue}' --fix",
      "prettier --write"
    ],
    "src/**/*.{ts,tsx,vue}": [
      "eslint 'src/**/*.{ts,tsx,vue}'  --fix",
      "prettier --write"
    ],
    "src/**/*.{js,jsx,vue}": [
      "eslint 'src/**/*.{js,jsx,vue}'  --fix",
      "prettier --write"
    ]
  }
}

.stylelintrc.json

{
    "extends": [
      "stylelint-config-standard",
      "stylelint-config-rational-order",
      "stylelint-prettier/recommended",
      "stylelint-config-recommended-vue"
    ],
    "rules": {
      "at-rule-no-unknown": null,
      "rule-empty-line-before": [
        "always-multi-line",
        {
          "except": ["first-nested"],
          "ignore": ["after-comment"]
        }
      ],
      "at-rule-empty-line-before": [
        "always",
        {
          "except": ["blockless-after-same-name-blockless", "first-nested"],
          "ignore": ["after-comment"]
        }
      ],
      "comment-empty-line-before": [
        "always",
        {
          "except": ["first-nested"],
          "ignore": ["stylelint-commands"]
        }
      ],
      "block-no-empty": true,
      "declaration-empty-line-before": "never",
      "declaration-block-no-duplicate-properties": true,
      "declaration-block-no-redundant-longhand-properties": true,
      "shorthand-property-no-redundant-values": true,
      "function-url-quotes": "always",
      "color-hex-length": "short",
      "color-named": "never",
      "comment-no-empty": true,
      "font-family-name-quotes": null,
      "font-weight-notation": "numeric",
      "property-no-vendor-prefix": true,
      "value-no-vendor-prefix": true,
      "selector-no-vendor-prefix": true,
      "no-descending-specificity": null,
      "no-invalid-double-slash-comments": null,
      "no-empty-source": null,
      "selector-class-pattern": null,
      "font-family-no-missing-generic-family-keyword": null
    }
  }

.stylelintignore

*.min.css

# 其他类型文件
*.js
*.jpg
*.woff

# 测试和打包目录
/test/
/dist/
.md
.ico
.svg
.png
.jpg
.jpeg

.prettierrc.json

{
    "singleQuote": true,
    "tabWidth": 2,
    "bracketSpacing": true,
    "trailingComma": "none",
    "printWidth": 100,
    "semi": false,
    "overrides": [
      {
        "files": ".prettierrc",
        "options": { "parser": "typescript" }
      }
    ]
  }

.prettierignore

**/*.md
**/*.svg
**/*.ejs
**/*.html
src/lib/*
node_modules

.eslintrc.js

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true
  },
  // prettier 必须在最后以解决格式化冲突
  extends: ['eslint:recommended', 'plugin:vue/vue3-recommended', 'prettier'],
  parserOptions: {
    ecmaVersion: 13,
    parser: '@typescript-eslint/parser',
    sourceType: 'module'
  },
  plugins: ['vue', '@typescript-eslint'],
  rules: {
    // override/add rules settings here, such as:
    // 'vue/no-unused-vars': 'error'
    'vue/multi-word-component-names': 'off', //建议开启 App用户组件名称应始终为多字,根组件除外。这可以防止与现有和未来的 HTML 元素发生冲突,因为所有 HTML 元素都是一个单词。
  }
}

.eslintignore

node_modules
src/lib/*
typings
.vscode
config
dist
doc
environments
mock
projects/*
test
.less
.css
.md
.ico
.svg
.png
.jpg
.jpeg
src/env.d.ts
**/*.d.ts

.editorconfig

root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

.husky/pre-commit

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no-install lint-staged
WangShuXian6 commented 2 years ago

error

base.less: When linting something other than CSS, you should install an appropriate syntax, e.g. "postcss-less", and use the "customSyntax" option

安装 postcss-less

WangShuXian6 commented 3 months ago

Lint 方式2 - React

husky

安装

npm i husky -D

初始化

这将在项目根目录的 .husky 目录中添加一个 pre-commit hook 文件

npx husky init

默认情况下,这个文件将运行 test 命令,但我们可以扩展它以运行我们想要的任何其他命令。