NestJS 代码质量工具配置

学习目标

  • 了解代码质量工具的重要性和作用
  • 掌握 ESLint 的配置和使用方法
  • 学习 Prettier 的配置和代码格式化技巧
  • 掌握 Commitlint 的配置和提交规范
  • 了解如何将代码质量工具集成到开发流程中
  • 学习代码质量工具的最佳实践

代码质量工具概述

代码质量工具是一组用于检查、分析和改进代码质量的软件工具。它们可以帮助开发者识别潜在的问题、强制执行代码风格规范、提高代码可读性和可维护性。对于 NestJS 项目来说,合理配置和使用代码质量工具可以显著提高项目质量和开发效率。

代码质量工具的重要性

  • 提高代码质量:识别潜在的错误、漏洞和不良实践
  • 保持代码一致性:强制执行统一的代码风格和规范
  • 减少代码审查时间:自动化检查减少人工审查的工作量
  • 提高可维护性:清晰、一致的代码更容易理解和维护
  • 促进团队协作:统一的代码规范有助于团队成员之间的协作
  • 减少技术债务:及时发现和修复问题,避免问题积累

常用的代码质量工具

  • ESLint:JavaScript/TypeScript 代码检查工具,用于识别和报告代码中的问题
  • Prettier:代码格式化工具,用于自动格式化代码,保持代码风格一致
  • Commitlint:Git 提交消息检查工具,用于强制执行规范的提交消息格式
  • Husky:Git 钩子工具,用于在 Git 操作前执行自定义脚本
  • Lint-Staged:用于在 Git 暂存文件上运行脚本,只对修改的文件进行检查

ESLint 配置

ESLint 是一个强大的 JavaScript/TypeScript 代码检查工具,它可以帮助开发者识别和修复代码中的问题,强制执行代码风格规范。

1. 安装 ESLint

# 安装 ESLint 核心包
npm install --save-dev eslint

# 安装 TypeScript 相关插件
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin

# 安装其他必要插件
npm install --save-dev eslint-plugin-nestjs eslint-plugin-import

2. 基础配置

创建 .eslintrc.js 文件:

// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: 'tsconfig.json',
    tsconfigRootDir: __dirname,
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint/eslint-plugin', 'nestjs', 'import'],
  extends: [
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking',
    'plugin:nestjs/recommended',
    'plugin:import/recommended',
    'plugin:import/typescript',
  ],
  root: true,
  env: {
    node: true,
    jest: true,
  },
  ignorePatterns: ['.eslintrc.js'],
  rules: {
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-unused-vars': ['error', {
      argsIgnorePattern: '^_',
    }],
    'nestjs/use-validation-pipe': 'error',
    'nestjs/use-guards': 'error',
    'nestjs/use-filters': 'error',
    'import/order': ['error', {
      'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
      'newlines-between': 'always',
    }],
  },
};

3. 自定义规则

ESLint 允许开发者根据项目需求自定义规则。以下是一些常用的自定义规则:

// .eslintrc.js 中的 rules 部分
rules: {
  // 代码风格规则
  'semi': ['error', 'always'],
  'quotes': ['error', 'single'],
  'comma-dangle': ['error', 'always-multiline'],
  'object-curly-spacing': ['error', 'always'],
  'array-bracket-spacing': ['error', 'never'],
  'space-before-function-paren': ['error', 'never'],
  
  // TypeScript 规则
  '@typescript-eslint/naming-convention': [
    'error',
    {
      'selector': 'interface',
      'format': ['PascalCase'],
    },
    {
      'selector': 'class',
      'format': ['PascalCase'],
    },
    {
      'selector': 'enum',
      'format': ['PascalCase'],
    },
    {
      'selector': 'typeAlias',
      'format': ['PascalCase'],
    },
    {
      'selector': 'variable',
      'format': ['camelCase', 'UPPER_CASE'],
    },
    {
      'selector': 'function',
      'format': ['camelCase'],
    },
  ],
  
  // 导入规则
  'import/no-cycle': 'error',
  'import/no-unresolved': 'error',
  'import/no-duplicates': 'error',
  
  // NestJS 特定规则
  'nestjs/no-async-module-exports': 'error',
  'nestjs/no-empty-lifecycle-method': 'error',
  'nestjs/no-explicit-any-generics': 'error',
  'nestjs/no-invalid-provide-injection-token': 'error',
  'nestjs/no-invalid-exports-in-modules': 'error',
  'nestjs/no-standalone-injectable': 'error',
  'nestjs/no-unused-providers': 'error',
  'nestjs/no-unused-modules': 'error',
  'nestjs/no-unused-imports': 'error',
  'nestjs/valid-provide-injection-token': 'error',
},

4. 忽略文件

创建 .eslintignore 文件,指定 ESLint 应该忽略的文件和目录:

# .eslintignore
node_modules
 dist
 coverage
 .env
 .env.*
 !.env.example

5. 运行 ESLint

package.json 中添加 ESLint 脚本:

// package.json
{
  "scripts": {
    "lint": "eslint "{src,apps,libs,test}/**/*.ts" --fix",
    "lint:check": "eslint "{src,apps,libs,test}/**/*.ts""
  }
}

运行 ESLint 检查:

# 运行 ESLint 检查并自动修复问题
npm run lint

# 仅运行 ESLint 检查,不自动修复
npm run lint:check

Prettier 配置

Prettier 是一个代码格式化工具,它可以自动格式化代码,保持代码风格一致。Prettier 与 ESLint 配合使用可以达到最佳效果。

1. 安装 Prettier

# 安装 Prettier 核心包
npm install --save-dev prettier

# 安装 ESLint 集成插件
npm install --save-dev eslint-config-prettier eslint-plugin-prettier

2. 基础配置

创建 .prettierrc 文件:

// .prettierrc
{
  "singleQuote": true,
  "trailingComma": "all",
  "printWidth": 80,
  "tabWidth": 2,
  "semi": true,
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf"
}

3. 配置选项说明

  • singleQuote:使用单引号代替双引号
  • trailingComma:在多行结构的末尾添加逗号
  • printWidth:每行代码的最大长度
  • tabWidth:缩进的空格数
  • semi:在语句末尾添加分号
  • bracketSpacing:在对象字面量的括号之间添加空格
  • arrowParens:在箭头函数的参数周围添加括号
  • endOfLine:行尾换行符类型(lf, crlf, cr, auto)

4. 忽略文件

创建 .prettierignore 文件,指定 Prettier 应该忽略的文件和目录:

# .prettierignore
node_modules
 dist
 coverage
 .env
 .env.*
 !.env.example

5. 与 ESLint 集成

修改 .eslintrc.js 文件,集成 Prettier:

// .eslintrc.js
module.exports = {
  // 其他配置...
  extends: [
    // 其他扩展...
    'prettier', // 确保 prettier 在最后,以覆盖其他规则
  ],
  plugins: [
    // 其他插件...
    'prettier',
  ],
  rules: {
    // 其他规则...
    'prettier/prettier': 'error',
  },
};

6. 运行 Prettier

package.json 中添加 Prettier 脚本:

// package.json
{
  "scripts": {
    "format": "prettier --write \"{src,apps,libs,test}/**/*.{ts,js,json,md}\"",
    "format:check": "prettier --check \"{src,apps,libs,test}/**/*.{ts,js,json,md}\""
  }
}

运行 Prettier 格式化:

# 运行 Prettier 格式化代码
npm run format

# 仅检查代码格式,不自动格式化
npm run format:check

Commitlint 配置

Commitlint 是一个 Git 提交消息检查工具,它可以帮助开发者强制执行规范的提交消息格式。规范的提交消息有助于团队协作和版本历史管理。

1. 安装 Commitlint

# 安装 Commitlint 核心包
npm install --save-dev @commitlint/cli @commitlint/config-conventional

# 安装 Husky(Git 钩子工具)
npm install --save-dev husky

# 安装 Lint-Staged(暂存文件检查工具)
npm install --save-dev lint-staged

2. 配置 Commitlint

创建 commitlint.config.js 文件:

// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'build',     // 构建相关
        'chore',     // 其他修改
        'ci',        // CI 配置修改
        'docs',      // 文档修改
        'feat',      // 新功能
        'fix',       // 修复 bug
        'perf',      // 性能优化
        'refactor',  // 代码重构
        'revert',    // 回滚提交
        'style',     // 代码风格修改
        'test',      // 测试相关
      ],
    ],
    'type-case': [2, 'always', 'lowerCase'],
    'type-empty': [2, 'never'],
    'scope-empty': [0], // 作用域可选
    'scope-case': [2, 'always', 'lowerCase'],
    'subject-empty': [2, 'never'],
    'subject-full-stop': [2, 'never', '.'],
    'subject-case': [2, 'never', ['upper-case']],
    'body-leading-blank': [1, 'always'],
    'body-max-line-length': [1, 'always', 72],
    'footer-leading-blank': [1, 'always'],
    'footer-max-line-length': [1, 'always', 72],
  },
};

3. 配置 Husky

初始化 Husky:

# 初始化 Husky
npx husky install

# 在 package.json 中添加 prepare 脚本
npm pkg set scripts.prepare="husky install"

创建 Git 钩子:

# 创建 commit-msg 钩子
npx husky add .husky/commit-msg "npx --no -- commitlint --edit $1"

# 创建 pre-commit 钩子
npx husky add .husky/pre-commit "npx lint-staged"

4. 配置 Lint-Staged

创建 .lintstagedrc.js 文件:

// .lintstagedrc.js
module.exports = {
  '*.ts': [
    'eslint --fix',
    'prettier --write',
  ],
  '*.{js,json,md}': [
    'prettier --write',
  ],
};

5. 提交消息格式

规范的提交消息格式如下:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

示例:

  • 新功能feat(auth): add login functionality
  • 修复 bugfix(user): resolve profile update issue
  • 文档修改docs(readme): update installation instructions
  • 代码重构refactor(user): simplify user service logic
  • 性能优化perf(database): optimize query performance
  • 测试相关test(auth): add login test cases
  • 构建相关build(deps): update dependencies
  • CI 配置ci(cd): update deployment pipeline

6. 运行 Commitlint

当你执行 git commit 命令时,Husky 会自动运行 Commitlint 检查提交消息格式。如果提交消息不符合规范,提交会被拒绝。

你也可以手动运行 Commitlint 检查:

# 检查最后一次提交消息
npx commitlint --from HEAD~1 --to HEAD

# 检查指定范围的提交消息
npx commitlint --from <commit> --to <commit>

其他代码质量工具

除了 ESLint、Prettier 和 Commitlint 之外,还有一些其他有用的代码质量工具:

1. TypeScript 类型检查

TypeScript 本身提供了类型检查功能,可以帮助开发者发现类型相关的问题。

package.json 中添加 TypeScript 类型检查脚本:

// package.json
{
  "scripts": {
    "typecheck": "tsc --noEmit"
  }
}

运行类型检查:

# 运行 TypeScript 类型检查
npm run typecheck

2. SonarQube

SonarQube 是一个代码质量和安全分析平台,它可以提供更全面的代码分析。

安装 SonarScanner

# 安装 SonarScanner
npm install --save-dev sonarqube-scanner

配置 SonarQube

创建 sonar-project.properties 文件:

# sonar-project.properties
sonar.projectKey=nestjs-project
sonar.projectName=NestJS Project
sonar.projectVersion=1.0.0

sonar.sources=src
sonar.exclusions=**/node_modules/**,**/dist/**,**/coverage/**

sonar.language=ts
sonar.typescript.tsconfigPath=tsconfig.json

sonar.test.exclusions=**/*.spec.ts,**/*.test.ts
sonar.test.inclusions=**/*.spec.ts,**/*.test.ts

sonar.coverage.exclusions=**/*.spec.ts,**/*.test.ts
sonar.javascript.lcov.reportPaths=coverage/lcov.info

package.json 中添加 SonarQube 脚本:

// package.json
{
  "scripts": {
    "sonar": "sonar-scanner"
  }
}

运行 SonarQube 分析:

# 运行 SonarQube 分析
npm run sonar

3. Dependency-Check

Dependency-Check 是一个用于检查项目依赖中已知漏洞的工具。

安装 Dependency-Check

# 安装 Dependency-Check
npm install --save-dev dependency-check

package.json 中添加 Dependency-Check 脚本:

// package.json
{
  "scripts": {
    "depcheck": "dependency-check package.json"
  }
}

运行 Dependency-Check:

# 运行 Dependency-Check
npm run depcheck

集成到开发流程中

将代码质量工具集成到开发流程中可以确保代码质量检查的持续性和自动化。以下是一些集成方法:

1. 开发环境集成

  • IDE 集成:在 VS Code、WebStorm 等 IDE 中安装相应的插件,实现实时检查和自动格式化
  • Git 钩子:使用 Husky 配置 Git 钩子,在 Git 操作前执行检查
  • 编辑器配置:配置编辑器自动保存时运行格式化和检查

2. CI/CD 集成

在 CI/CD 流程中集成代码质量检查,可以确保只有符合质量标准的代码才能被合并和部署。

GitHub Actions 配置

创建 .github/workflows/code-quality.yml 文件:

# .github/workflows/code-quality.yml
name: Code Quality

on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 16
          cache: 'npm'
      - run: npm ci
      - run: npm run lint:check
      - run: npm run format:check
      - run: npm run typecheck
      - run: npm run test

GitLab CI 配置

创建 .gitlab-ci.yml 文件:

# .gitlab-ci.yml
stages:
  - test

code_quality:
  stage: test
  image: node:16
  script:
    - npm ci
    - npm run lint:check
    - npm run format:check
    - npm run typecheck
    - npm run test
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

3. 开发工作流

推荐的开发工作流:

  1. 编写代码:在本地编写代码,IDE 实时进行代码检查和格式化
  2. 提交代码:执行 git commit,Husky 自动运行检查
  3. 推送代码:执行 git push,远程仓库 CI/CD 流程运行完整检查
  4. 代码审查:团队成员进行代码审查,关注逻辑和架构
  5. 合并代码:代码审查通过后,合并到主分支

实践案例:配置完整的代码质量工具链

1. 项目初始化

# 创建新项目
nest new nestjs-code-quality

# 进入项目目录
cd nestjs-code-quality

2. 安装依赖

# 安装 ESLint 相关依赖
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-nestjs eslint-plugin-import

# 安装 Prettier 相关依赖
npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier

# 安装 Commitlint 相关依赖
npm install --save-dev @commitlint/cli @commitlint/config-conventional husky lint-staged

3. 配置文件

创建 .eslintrc.js

// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: 'tsconfig.json',
    tsconfigRootDir: __dirname,
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint/eslint-plugin', 'nestjs', 'import', 'prettier'],
  extends: [
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking',
    'plugin:nestjs/recommended',
    'plugin:import/recommended',
    'plugin:import/typescript',
    'prettier',
  ],
  root: true,
  env: {
    node: true,
    jest: true,
  },
  ignorePatterns: ['.eslintrc.js'],
  rules: {
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-unused-vars': ['error', {
      argsIgnorePattern: '^_',
    }],
    'nestjs/use-validation-pipe': 'error',
    'nestjs/use-guards': 'error',
    'nestjs/use-filters': 'error',
    'import/order': ['error', {
      'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
      'newlines-between': 'always',
    }],
    'prettier/prettier': 'error',
  },
};

创建 .prettierrc

// .prettierrc
{
  "singleQuote": true,
  "trailingComma": "all",
  "printWidth": 80,
  "tabWidth": 2,
  "semi": true,
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf"
}

创建 commitlint.config.js

// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'build',
        'chore',
        'ci',
        'docs',
        'feat',
        'fix',
        'perf',
        'refactor',
        'revert',
        'style',
        'test',
      ],
    ],
  },
};

创建 .lintstagedrc.js

// .lintstagedrc.js
module.exports = {
  '*.ts': [
    'eslint --fix',
    'prettier --write',
  ],
  '*.{js,json,md}': [
    'prettier --write',
  ],
};

创建 .eslintignore 和 .prettierignore

# .eslintignore 和 .prettierignore
node_modules
 dist
 coverage
 .env
 .env.*
 !.env.example

4. 配置 package.json

// package.json
{
  "scripts": {
    "build": "nest build",
    "format": "prettier --write \"{src,apps,libs,test}/**/*.{ts,js,json,md}\"",
    "format:check": "prettier --check \"{src,apps,libs,test}/**/*.{ts,js,json,md}\"",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "lint:check": "eslint \"{src,apps,libs,test}/**/*.ts\"",
    "typecheck": "tsc --noEmit",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "prepare": "husky install"
  }
}

5. 初始化 Husky

# 初始化 Husky
npx husky install

# 创建 commit-msg 钩子
npx husky add .husky/commit-msg "npx --no -- commitlint --edit $1"

# 创建 pre-commit 钩子
npx husky add .husky/pre-commit "npx lint-staged"

6. 测试配置

运行代码检查

# 运行 ESLint 检查
npm run lint:check

# 运行 Prettier 检查
npm run format:check

# 运行 TypeScript 类型检查
npm run typecheck

# 运行所有检查
npm run lint:check && npm run format:check && npm run typecheck

测试提交消息

# 尝试提交不符合规范的消息
git add .
git commit -m "add new feature"
# 应该会被拒绝

# 提交符合规范的消息
git add .
git commit -m "feat(auth): add login functionality"
# 应该会成功

代码质量工具最佳实践

1. 配置原则

  • 逐步引入:对于现有项目,逐步引入代码质量工具,避免一次性修改过多代码
  • 团队共识:与团队成员达成共识,确保所有人理解和支持代码质量工具的使用
  • 合理配置:根据项目需求和团队习惯,合理配置工具规则,避免过于严格或宽松
  • 持续优化:根据实际使用情况,持续优化工具配置,提高工具的实用性

2. 使用技巧

  • IDE 集成:充分利用 IDE 的集成功能,实现实时检查和自动格式化
  • 批量修复:对于现有项目,可以先运行批量修复命令,然后再进行手动调整
  • 忽略规则:对于特殊情况,可以使用注释暂时忽略某些规则检查
  • 自定义规则:根据项目特点,自定义适合的规则,提高工具的针对性

3. 常见问题与解决方案

ESLint 与 Prettier 冲突

问题:ESLint 的代码风格规则与 Prettier 的格式化规则冲突

解决方案

  • 确保 prettier 扩展在 ESLint 配置的最后,以覆盖其他规则
  • 使用 eslint-config-prettier 禁用与 Prettier 冲突的 ESLint 规则
  • 统一配置 ESLint 和 Prettier 的代码风格规则

提交消息不符合规范

问题:提交消息不符合 Commitlint 规范,导致提交被拒绝

解决方案

  • 学习并遵循规范的提交消息格式
  • 使用提交消息模板
  • 考虑使用提交消息生成工具,如 commitizen

检查速度慢

问题:代码质量检查速度慢,影响开发效率

解决方案

  • 使用 Lint-Staged 只检查修改的文件
  • 配置 ESLint 缓存,避免重复检查
  • 合理配置检查规则,避免过于复杂的规则
  • 考虑使用并行检查工具,如 eslint-parallel

规则过于严格

问题:某些规则过于严格,导致不必要的错误

解决方案

  • 根据项目需求调整规则严格程度
  • 使用注释暂时忽略某些规则检查
  • 为特殊文件或目录创建单独的配置

总结

代码质量工具是 NestJS 项目开发中的重要组成部分,它们可以帮助开发者提高代码质量、保持代码一致性、减少错误和技术债务。通过本教程的学习,我们了解了:

  • ESLint 的配置和使用方法,用于检查和修复代码中的问题
  • Prettier 的配置和使用方法,用于自动格式化代码,保持代码风格一致
  • Commitlint 的配置和使用方法,用于强制执行规范的提交消息格式
  • 如何将代码质量工具集成到开发流程中
  • 代码质量工具的最佳实践和常见问题解决方案

一个良好的代码质量工具链应该是:

  • 配置合理:根据项目需求和团队习惯,合理配置工具规则
  • 集成顺畅:无缝集成到开发流程中,不影响开发效率
  • 持续运行:在开发、提交和 CI/CD 过程中持续运行检查
  • 团队共识:团队成员共同理解和支持工具的使用
  • 持续优化:根据实际使用情况,持续优化工具配置

通过合理配置和使用代码质量工具,我们可以构建更高质量、更可维护的 NestJS 项目,提高开发效率和团队协作效果。

互动问题

  1. 你在项目中使用过哪些代码质量工具?它们为你的项目带来了哪些好处?

  2. 在配置 ESLint 时,你遇到过哪些常见问题?是如何解决的?

  3. 你认为规范的提交消息格式有哪些好处?在你的团队中是如何强制执行的?

  4. 如何平衡代码质量检查的严格程度和开发效率?

  5. 你认为代码质量工具链中最不可或缺的工具是什么?为什么?

« 上一篇 NestJS 开发工具链优化 下一篇 » NestJS 生产环境最佳实践