项目初始化规范配置:(Prettier、EsLint、StyleLint、CommitLint)

摘要:

包含js格式检查,css样式检查,自动根据规则格式化,提交检查。(Prettier、EsLint、StyleLint、CommitLint、husky、lint-staged、@commitlint/cz-commitlint、commitizen、cz-git、czg)

常见问题

配置过程中可能出现的问题,可以先跳过此部分,出现问题时再回来查看。

配置文件不生效

  1. 先检查重启编辑器后是否生效。
  2. 如果生效,可以使用下面三种方法:
  • ctrl+shift+p打开控制台,输入“Reload Window”重新加载窗口。
  • 重启vscode。
  • 配置文件使用json格式。
  1. 不生效,检查配置文件名是否正确,检查配置文件是否有语法错误。

换行符问题

  • 不同平台创建文件,默认换行符可能不同:lf、crlf。当使用window开发时,默认换行符为crlf。而如果在prettier配置中设置了换行符为lf,stylelint会报错。

解决方法

  1. 安装vscode插件:EditorConfig for VS Code
  2. 在项目根目录创建.editorconfig文件,内容如下:(根据实际情况修改)
  3. 这样保存文件时,vscode会根据.editorconfig文件的配置,自动将换行符转换为配置内容。
bash 复制代码
root = true

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

[*.{yml,yaml,json}]
indent_style = space
indent_size = 2

[*.md]
trim_trailing_whitespace = false

[Makefile]
indent_style = tab

Prettier 配置

安装vscode插件

  1. 安装Prettier - Code formatter插件
  2. 修改vscode设置:
json 复制代码
{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.fixAll.tslint": "explicit",
    "source.fixAll.stylelint": "explicit"
  },
}

安装依赖

复制代码
npm install prettier --save-dev

配置文件

  • .prettierrc.json,根据实际情况修改
  • .prettierignore,忽略配置,根据实际情况修改
js 复制代码
{
  "semi": false,
  "singleQuote": true,
  "trailingComma": "all",
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf",
  "jsxSingleQuote": false,
  "singleAttributePerLine": false,
  "htmlWhitespaceSensitivity": "css",
  "proseWrap": "never",
  "overrides": [
    {
      "files": ["*.json", "*.yml", "*.yaml"],
      "options": { "printWidth": 80 }
    }
  ]
}

EsLint 配置

EsLint版本:^9

安装vscode插件

  1. 安装EsLint插件
  2. 修改vscode设置:
json 复制代码
{
  "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact", "vue"],
}

依赖说明

注意:prettier放到最后,用于覆盖与EsLint冲突的配置。

JS简单配置

js 复制代码
import js from '@eslint/js'
import globals from 'globals'
import { defineConfig, globalIgnores } from 'eslint/config'
import prettier from 'eslint-config-prettier'

export default defineConfig([
  globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),

  {
    files: ['**/*.{js,mjs,cjs}'],
    plugins: { js },
    extends: ['js/recommended'],
    languageOptions: { globals: globals.browser },
  },

  {
    rules: {
      // 自己的规则
    }
  },

  prettier,
])

TS简单配置

js 复制代码
import js from '@eslint/js'
import globals from 'globals'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
import prettier from 'eslint-config-prettier'

export default defineConfig([
  globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),

  {
    files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
    plugins: { js },
    extends: ['js/recommended'],
    languageOptions: { globals: globals.browser },
  },
  tseslint.configs.recommended,

  {
    rules: {
      // 自己的规则
    }
  },

  prettier,
])

Vue+JS简单配置

js 复制代码
import { defineConfig, globalIgnores } from 'eslint/config'
import globals from 'globals'
import js from '@eslint/js'
import pluginVue from 'eslint-plugin-vue'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'

export default defineConfig([
  {
    name: 'app/files-to-lint',
    files: ['**/*.{js,mjs,jsx,vue}'],
  },

  globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),

  {
    languageOptions: {
      globals: {
        ...globals.browser,
      },
    },
  },

  js.configs.recommended,
  ...pluginVue.configs['flat/essential'],

  {
    rules: {
      // 自己的规则
    }
  },

  skipFormatting,
])

Vue+TS简单配置

js 复制代码
import { globalIgnores } from 'eslint/config'
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
import pluginVue from 'eslint-plugin-vue'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'

export default defineConfigWithVueTs(
  {
    name: 'app/files-to-lint',
    files: ['**/*.{ts,mts,tsx,vue}'],
  },

  globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),

  pluginVue.configs['flat/essential'],
  vueTsConfigs.recommended,

  {
    rules: {
      // 自己的规则
    }
  },

  skipFormatting,
)

Nuxtjs简单配置

js 复制代码
// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxt/eslint'
  ],
  eslint: {
    // options here
  }
})

// eslint.config.mjs
import withNuxt from './.nuxt/eslint.config.mjs'

export default withNuxt(
  {
    rules: {
      // 自己的规则
    }
  }
)

React+JS简单配置

js 复制代码
import js from '@eslint/js'
import globals from 'globals'
import pluginReact from 'eslint-plugin-react'
import { defineConfig, globalIgnores } from 'eslint/config'
import prettier from 'eslint-config-prettier'

export default defineConfig([
  globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
  {
    files: ['**/*.{js,mjs,cjs,jsx}'],
    plugins: { js },
    extends: ['js/recommended'],
    languageOptions: { globals: globals.browser },
  },
  pluginReact.configs.flat.recommended,

  {
    rules: {
      // 自己的规则
    }
  },

  prettier,
])

React+TS简单配置

js 复制代码
import js from '@eslint/js'
import globals from 'globals'
import tseslint from 'typescript-eslint'
import pluginReact from 'eslint-plugin-react'
import { defineConfig, globalIgnores } from 'eslint/config'
import prettier from 'eslint-config-prettier'

export default defineConfig([
  globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
  {
    files: ['**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
    plugins: { js },
    extends: ['js/recommended'],
    languageOptions: { globals: globals.browser },
  },
  tseslint.configs.recommended,
  pluginReact.configs.flat.recommended,

  {
    rules: {
      // 自己的规则
    }
  },

  prettier,
])

Next简单配置

js 复制代码
import { defineConfig, globalIgnores } from 'eslint/config'
import nextVitals from 'eslint-config-next/core-web-vitals'
import nextTs from 'eslint-config-next/typescript'
import prettier from 'eslint-config-prettier'

export default defineConfig([
  ...nextVitals,
  ...nextTs,
  prettier,
  globalIgnores([
    '.next/**',
    'out/**',
    'build/**',
    'next-env.d.ts',
  ]),
  {
    rules: {
      // 自己的规则
    },
  },
])

import 检查、排序

js 复制代码
import { defineConfig } from 'eslint/config'
import importPlugin from 'eslint-plugin-import'

export default defineConfig([
  // ...
  importPlugin.flatConfigs.recommended,

  {
    rules: {
      'import/order': [
        'error',
        {
          // 组排序:builtin > external > parent > sibling > index
          // Node.js内置模块 > 第三方模块 > 项目内部模块 > 父级目录 > 同级目录 > 当前目录的index文件
          groups: ['builtin', 'external', 'parent', 'sibling', 'index'],
          // 首字母排序,忽略大小写
          alphabetize: { order: 'asc', caseInsensitive: true },
        },
      ],
    },
  },
])

StyleLint配置

安装vscode插件

  1. 安装StyleLint插件
  2. 修改vscode设置:
json 复制代码
{
  "stylelint.validate": ["css", "scss", "less", "vue", "html"],
}

依赖说明

安装依赖

根据上方说明和自己需要进行安装,下方为全部安装命令。

bash 复制代码
npm install stylelint stylelint-config-standard-scss stylelint-config-html stylelint-config-standard-vue stylelint-config-recess-order stylelint-prettier --save-dev

配置文件

  • stylelint.config.mjs,根据实际情况修改
  • .stylelintignore,忽略配置,根据实际情况修改
js 复制代码
import propertyGroups from 'stylelint-config-recess-order/groups'

/** @type {import('stylelint').Config} */
export default {
  extends: [
    // 标准配置(包含scss配置)
    'stylelint-config-standard-scss',
    // html配置(支持很多类型文件,具体看源码)
    'stylelint-config-html',
    // vue配置
    'stylelint-config-standard-vue',
    // 属性排序配置
    'stylelint-config-recess-order',
    // prettier配置
    'stylelint-prettier/recommended',
  ],
  rules: {
    'order/properties-order': propertyGroups.map((group) => ({
      ...group,
      // 此处覆盖默认排序
    })),
  },
}

更多插件及扩展

查看官方文档:Awesome Stylelint

CommitLint配置

这些规范在git中使用,配置前请先确认项目是否已经初始化git仓库。

依赖说明

  • husky:提供git钩子的工具
  • lint-staged:在git暂存文件上运行格式化或检查。(虽然eslint、stylelint等工具也能对文件进行检查,但lint-staged只在git暂存文件上运行,避免对未更改的文件进行检查,提高效率。)
  • @commitlint/cli:对提交信息进行检查
  • @commitlint/config-conventional:符合conventional commit规范的默认配置,可在此基础上扩展。

三者配合下,在git要提交时,husky提供钩子,lint-staged在husky提供的pre-commit钩子上运行代码格式化或检查,@commitlint/cli在husky提供的commit-msg钩子上运行提交信息是否符合规范检查。

安装依赖

bash 复制代码
npm install husky lint-staged @commitlint/cli @commitlint/config-conventional --save-dev

配置文件

  1. 初始化husky
bash 复制代码
npx husky init

此命令会创建.husky目录,并在其中创建pre-commit脚本,并更新 package.json 中的 prepare 脚本。

  1. lint-staged.config.mjs,根据实际情况修改
js 复制代码
/**
 * @filename: lint-staged.config.mjs
 * @type {import('lint-staged').Configuration}
 */
export default {
  '*.{js,jsx,ts,tsx}': ['prettier --write', 'eslint --fix'],
  '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': ['prettier --write--parser json'],
  'package.json': ['prettier --write'],
  '*.vue': ['prettier --write', 'eslint --fix', 'stylelint --fix'],
  '*.{css,scss,less,styl,html}': ['prettier --write', 'stylelint --fix'],
  '*.md': ['prettier --write'],
}
  1. 更新.husky/commit-msg文件,根据实际情况修改
bash 复制代码
# 选择任何一种方式都行

# 指定配置文件(当文件名不符合默认规则时)
npx lint-staged --config lint-staged.config.mjs

# 简单配置
npx lint-staged

# 使用pnpm
pnpm exec lint-staged

# 当然也可以把这个放在`package.json`的`scripts`中,然后在`pre-commit`钩子中运行
npm run staged
  1. commitlint.config.mjs,根据实际情况修改
js 复制代码
/**
 * @filename: commitlint.config.mjs
 * @type {import('@commitlint/types').UserConfig}
 */
export default {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'body-leading-blank': [2, 'always'],
    'footer-leading-blank': [1, 'always'],
    'header-max-length': [2, 'always', 108],
    'subject-empty': [2, 'never'],
    'type-empty': [2, 'never'],
    'subject-case': [0],
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'perf',
        'style',
        'docs',
        'test',
        'refactor',
        'build',
        'ci',
        'chore',
        'revert',
        'wip',
        'workflow',
        'types',
        'release',
      ],
    ],
  },
}
  1. 更新.husky/commit-msg文件,根据实际情况修改
bash 复制代码
npx --no -- commitlint --edit "$1"

添加commit交互式提示

两种推荐方案,都差不多。

1. 使用@commitlint/cz-commitlint插件

安装依赖

bash 复制代码
npm i -D @commitlint/cz-commitlint commitizen inquirer@9

修改package.json,添加commitizen配置

json 复制代码
{
  "scripts": {
    "commit": "git-cz"
  },
  "config": {
    "commitizen": {
      "path": "@commitlint/cz-commitlint"
    }
  }
}

修改commitlint.config.mjs,添加prompt配置(根据实际情况修改)

js 复制代码
/**
 * @filename: commitlint.config.mjs
 * @type {import('@commitlint/types').UserConfig}
 */
export default {
  extends: ['@commitlint/config-conventional'],
  rules: {
    // 自定义规则
  },
  prompt: {
    settings: {},
    messages: {
      skip: '回车跳过',
      max: '最多 %d 个字符',
      min: '至少 %d 个字符',
      emptyWarning: '不能为空',
      upperLimitWarning: '超过上限',
      lowerLimitWarning: '低于下限',
    },
    questions: {
      type: {
        description: '选择你要提交的变更类型:',
        enum: {
          feat: {
            description: '新增功能',
            title: '新增功能',
            emoji: '✨',
          },
          fix: {
            description: '修复 bug',
            title: '修复 bug',
            emoji: '🐛',
          },
          docs: {
            description: '文档变更',
            title: '文档变更',
            emoji: '📚',
          },
          style: {
            description: '代码格式变更,不影响代码含义(空格、格式化、缺少分号等)',
            title: '代码格式变更',
            emoji: '💎',
          },
          refactor: {
            description: '代码变更,既不修复 bug 也不新增功能',
            title: '代码重构',
            emoji: '📦',
          },
          perf: {
            description: '性能优化',
            title: '性能优化',
            emoji: '🚀',
          },
          test: {
            description: '添加缺失的测试或修正现有的测试',
            title: '测试',
            emoji: '🚨',
          },
          build: {
            description: '构建系统或外部依赖项的变更(例如 scopes: gulp, broccoli, npm)',
            title: '构建系统变更',
            emoji: '🛠',
          },
          ci: {
            description:
              'CI 配置文件或脚本的变更(例如 scopes: Travis, Circle, BrowserStack, SauceLabs)',
            title: 'CI 配置变更',
            emoji: '⚙️',
          },
          chore: {
            description: '其他不修改 src 或 test 文件的变更',
            title: '其他变更',
            emoji: '♻️',
          },
          revert: {
            description: '回滚之前的提交',
            title: '回滚提交',
            emoji: '🗑',
          },
        },
      },
      scope: {
        description: '变更的范围(例如组件或文件名)',
      },
      subject: {
        description: '变更的简短描述( imperative tense)',
      },
      body: {
        description: '变更的详细描述',
      },
      isBreaking: {
        description: '是否有重大变更?',
      },
      breakingBody: {
        description: '重大变更提交需要包含变更描述。请输入变更描述的详细信息',
      },
      breaking: {
        description: '重大变更的描述',
      },
      isIssueAffected: {
        description: '是否有影响已打开问题的变更?',
      },
      issuesBody: {
        description: '如果有影响已打开问题的变更,提交需要包含变更描述。请输入变更描述的详细信息',
      },
      issues: {
        description: '添加影响已打开问题的变更引用(例如 "fix #123", "re #123".)',
      },
    },
  },
}

使用

bash 复制代码
git add .
npm run commit

2. 使用cz-git插件(推荐)

安装依赖

bash 复制代码
npm i -D commitizen cz-git czg

修改package.json,添加commitizen配置

json 复制代码
{
  "scripts": {
    "commit": "git-cz",
    "czg": "czg"
  },
  "config": {
    "commitizen": {
      "path": "node_modules/cz-git"
    }
  }
}

修改commitlint.config.mjs,添加prompt配置(根据实际情况修改)

js 复制代码
// 所有范围(可写死,也可动态计算,比如pnpm的workspace,通过读取pnpm-workspace.yaml文件获取)
const scopes = []
// 有改变的范围(动态计算,比如git status --porcelain || true)
const scopesChange = []

/**
 * @filename: commitlint.config.mjs
 * @type {import('cz-git').UserConfig}
 */
export default {
  extends: ['@commitlint/config-conventional'],
  rules: {
    // 自定义规则
  },
  prompt: {
    // 常用别名:通过 `npm run czg :别名` 使用,直接提交,不再有后续交互
    // 如 `npm run czg :f`、`npm run czg :r` 等
    alias: {
      f: 'docs: 修复拼写错误',
      r: 'docs: 更新README.MD',
      s: 'style: 更新代码格式',
      b: 'build: 新的生产版本',
      c: 'chore: 更新配置文件',
      w: 'wip: 功能开发中',
    },

    scopes: [...scopes],
    defaultScope: scopesChange,
    customScopesAlign: scopesChange.length ? 'bottom' : 'top',
    enableMultipleScopes: true,
    scopeEnumSeparator: ',',
    customScopesAlias: '以上都不是?我要自定义',
    emptyScopesAlias: '跳过,不填写',

    allowCustomIssuePrefix: false,
    allowEmptyIssuePrefix: false,

    typesAppend: [
      { value: 'wip', name: 'wip:      正在开发中' },
      { value: 'workflow', name: 'workflow: 工作流程改进' },
      { value: 'types', name: 'types:    类型定义文件修改' },
    ],

    messages: {
      type: '选择你要提交的类型 :',
      scope: '选择一个提交范围 (可选):',
      customScope: '请输入自定义的提交范围 :',
      subject: '填写简短精炼的变更描述 :\n',
      body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
      breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
      footerPrefixsSelect: '选择关联issue前缀 (可选):',
      customFooterPrefixs: '输入自定义issue前缀 :',
      footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
      generatingByAI: '生成你的AI提交主题……',
      generatedSelectByAI: '由人工智能选择生成合适的主题:',
      confirmCommit: '是否提交或修改commit ?',
    },
    types: [
      { value: 'feat', name: 'feat:     新增功能', emoji: ':sparkles:' },
      { value: 'fix', name: 'fix:      修复缺陷(bug 修复)', emoji: ':bug:' },
      { value: 'docs', name: 'docs:     文档变更', emoji: ':memo:' },
      {
        value: 'style',
        name: 'style:    代码格式(修改空白字符,补全缺失的分号等)',
        emoji: ':lipstick:',
      },
      {
        value: 'refactor',
        name: 'refactor: 代码重构(既没有新增功能,也没有修复 bug)',
        emoji: ':recycle:',
      },
      { value: 'perf', name: 'perf:     性能优化', emoji: ':zap:' },
      {
        value: 'test',
        name: 'test:     添加疏漏测试或已有测试改动',
        emoji: ':white_check_mark:',
      },
      {
        value: 'build',
        name: 'build:    构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)',
        emoji: ':package:',
      },
      { value: 'ci', name: 'ci:       修改 CI 配置、脚本', emoji: ':ferris_wheel:' },
      { value: 'revert', name: 'revert:   回滚 commit', emoji: ':rewind:' },
      {
        value: 'chore',
        name: 'chore:    对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)',
        emoji: ':hammer:',
      },
      { value: 'wip', name: 'wip:      正在开发中' },
      { value: 'workflow', name: 'workflow: 工作流程改进' },
      { value: 'types', name: 'types:    类型定义文件修改' },
    ],
    useEmoji: true,
    emojiAlign: 'center',
    emptyIssuePrefixAlias: '跳过',
    customIssuePrefixAlias: '自定义前缀',
  },
}

使用

bash 复制代码
git add .
npm run commit # 交互式提交
npm run czg :f # 使用别名快速提交

评论

0条评论

logo

暂无内容,去看看其他的吧~