前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >创建公司内部使用的eslint-config-package

创建公司内部使用的eslint-config-package

原创
作者头像
写bug的高哈哈
发布2025-02-01 19:20:18
发布2025-02-01 19:20:18
6700
代码可运行
举报
运行总次数:0
代码可运行

在现今的 JavaScript 项目中,为了确保代码的品质和编写风格,ESLint 的设置和使用几乎可以算是标配。

通过 ESLint 除了可以避免掉许多不必要的程序语法错误,还能确保开发者彼此之间有一致的程序编写风格,才不会 A 开发者写出来的代码和 B 开发者写出来的,在编写风格上有太大的落差。

为什么要创建自己的 eslint-config package

eslint-config package 指的就是把 ESLint 配置文件,打包成一个 npm 包,这里面包含了要使用那些规则、使用这些规则的逻辑等等,目的则是让不同项目能够直接使用。

几个大家可能听过的像是 eslint-config-standard、eslint-config-airbnb、eslint-config-prettier、eslint-config-google、…等等,都是包好的 ESLint 配置文件,让想要使用的开发者可以直接下载使用。

一般来说,每个项目都会有独立的一个 ESLint 配置文件,可以针对不同项目进行设置就好,不需要额外抽成一个包。然而,在公司中有许多不同的项目同时在进行,包含 B2B 和多个 B2C 项目,如果每次都要在各个项目中复制贴上相同的设置,会是相当麻烦的一件事;另外,如果在项目中有针对部分规则进行个别的微调,久了之后可能会忘了原本使用的设置是什么,甚至发生不同的项目使用的设置差异过大的情况。

这时候如果可以把 ESLint 中的设置打包成一个包,未来新开发项目时只需要使用 npm 安装这个包后,就可以使用到公司内部一致的设置,将会省下非常多不必要的麻烦。

认识 ESLint 中的常见配置项

实际上要创建 eslint-config 的包并没有太大的难度,因为本质上就是把写好的 ESLint 设置进行汇总而已。下面是 ESLint 配置文件常见的配置项:

代码语言:javascript
代码运行次数:0
复制
// .eslintrc.js
module.exports = {
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    'plugin:react/recommended',
    'airbnb',
  ],
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 12,
    sourceType: 'module',
  },
  plugins: [
    'react',
  ],
  rules: {
  },
};

其中包含了:

  • env :让 ESLint 知道那些是原本就存在项目中的全局变量,例如, alertwindow 等等,否则当你在项目中直接使用了这些全局变量却又没有定义的话,ESLint 就会认为这个变量不存在而报错。
  • parserOptions :告诉 ESLint 项目中所使用的 JavaScript 语法版本(例如,ECMAScript6、ECMAScript7)。
  • rules :通过这个配置项的设置,可以让 ESLint 知道当不同的规则触发时,ESLint 要用什么类型的方式给予提示,是要当成是 error、warning、或是不用理会。例如,在这里可以设置,假如开发者在程序中定义了变量却没有使用到这个变量时,要当成是严重的错误(error),出现警告的提示(warning)就好,或是可以完全不用理会这个情况(off)。

除了这几个配置项之外,比较容易混淆的则是 plugins 和 extends 这两个配置项,因此会花多一些的篇幅来描述。

plugins

plugins 是一系列由其他开发者编写好的规则,让使用的人可以把这些规则加载到 ESLint 中使用,但要留意的是:“ 它只是定义好的规则,并没有说明要如何使用这些规则 ”,也就是说,plugin 只会加载规则,但不会说明这些规则发生时到底要被 ESLint 判断成是 error、warning、还是可以不用理会,这个部分一样是要在 rules 配置项中设置。

常见 eslint plugin 的包像是 eslint-plugin-react、eslint-plugin-import、eslint-plugin-vue、eslint-plugin-prettier、eslint-plugin-jest、…等等。

eslint-plugin-react 为例,可以看到这里定义好了一系列可以被 ESLint 使用的规则:

在这些 plugin 中都有定义了许多不同的规则可以加载到 ESLint 中,再由开发者自行针对这些规则,通过 rules 配置项来决定严重程度。

从这些 eslint plugin 的包中可以看到,每个包的前缀都是一样的,都是以 eslint-plugin-* 当作开头,因此当我们在 ESLint 的配置文件中,想要启用这个 plugin 的规则时,可以省略掉这个前缀。

例如,在上面示例的 .eslintrc.js 中,可以看到 plugins 配置项只写了 react ,而实际上它完整的包名称是 eslint-plugin-reacteslint-plugin-* 的部分可以省略:

代码语言:javascript
代码运行次数:0
复制
{
  plugins: [
    // eslint-plugin-react 的缩写
    'react',
  ],
}

当我们挂入 plugins 时,其实就只是把许多定义好的规则加载到 ESLint 中,但这些规则要怎么被使用并不包括在 plugins 的范畴内,需要通过 rules 配置项,设置每个规则要如何被使用后才算真的有效。

extends

最后来说明 extends 这个配置项,extends 的功能其实很单纯,就是把其他开发者写好的 eslint-config(ESLint 配置文件)给加载进来。

举例来说,上面示例的 .eslintrc.js 就是一个 ESLint 的配置文件,如果其他开发者想要使用同样的设置,除了用复制贴上的方式,把同样的设置贴到自己的 ESLint 配置文件之外,也可以通过用 extends 这个配置项,直接使用别人写好的设置,ESLint 就会把对应的设置也都加载到你项目的 ESlint 配置文件中。

也就是说,我们可以先创建了公司内部要共用的 ESLint 配置文件后,在不同项目的 extends 配置项都去加载这个共用的 ESLint 配置文件后,这些设置以及创建好的规则判断(哪些规则要显示为严重、哪些是警告、哪些可以不用理会)就会自动使用到项目当中,如此就可以避免需要重复复制贴上的问题。

常见的 ESLint 配置文件像是文章开头提到的 eslint-config-standard、eslint-config-airbnb、eslint-config-prettier、eslint-config-google,同样的,你会发现它们也都有一样的前缀,都是以 eslint-config-* 开头,因此在使用时可以省略掉 eslint-config- 的前缀,像是这样:

代码语言:javascript
代码运行次数:0
复制
{
  extends: [
    'plugin:react/recommended',
    'airbnb',
  ],
}

这里 extends 了 airbnb ,其实指的就是加载 eslint-config-airbnb 的 ESLint 配置文件。

在这里我们还看到另外也加载了一个名为 plugin:react/recommend 的 ESLint 配置文件,之所以前面是以 plugin:react 开头,是因为这只配置文件实际上是放在 eslint-plugin-react 中(注意,是 eslint-plugin-react 而不是 eslint-config-react 喔!)。

为什么要把 React 的 ESLint 配置文件放在 eslint-plugin-react 中,而不是独立成 eslint-config-react 呢?

这呼应到前面有提到的,plugins 本质上只是用来定义一系列的规则,但这些规则怎么被使用并不在 plugin 的范畴内。可是,既然都把规则定义好了,何不干脆帮大家把怎么使用这些规则的建议设置也一并提供在 plugin 的项目中,方便大家下载 plugin 后就可以直接使用呢?这也就是为什么在 eslint-plugin-react 中,还有 recommended 这个 eslint-config 可以使用。

eslint-plugin-react 本身是 ESLint 的 plugin,但包里同时提供了 plugin:react/recommended 这个 ESLint 配置文件(eslint-config)让开发者使用。

当你在 extends 配置项中加载了 plugin:react/recommended 后,就会连带的把使用这些规则的建议设置也加载到你的项目当中:

extends 和 plugin 的作用不同,但很容易搞混的原因也在这里。简单来说,plugin 定义的是一系列可以被使用的规则,虽然不是必须,但它经常也会顺便提供许多建议可以怎么使用这些规则的设置,让开发者可以直接通过 extends 来使用。像是大家常听到的 prettier 也是通过这样的方式,它先定义好一系列的规则后放在 eslint-plugin-prettier 中,而在这个 plugin 的包里也顺便提供了建议的 ESLint 设置让开发者能够使用,所以你可能经常会看到这样的 ESLint 配置文件:

代码语言:javascript
代码运行次数:0
复制
// .eslintrc.js
module.exports = {
  plugins: ['react', 'jest', 'prettier'],
  extends: [
    'plugin:react/recommended',
    'plugin:jest/recommended',
    'plugin:prettier/recommended',
  ],
};

都是类似的道理。

plugins 提供许多的规则,让开发者可以自行设置要如何使用这些规则,而 plugin 的作者也可能一并提供了他认为合理的配置文件让开发者可以放在 extends 中直接使用,但这并不是强制的。

有些时候你可能会看到下面这种只有 extends 却没有加载 plugins 的情况而感到困惑:

代码语言:javascript
代码运行次数:0
复制
// .eslintrc.js
module.exports = {
  plugins: [],
  extends: [
    'plugin:react/recommended',
    'plugin:jest/recommended',
    'plugin:prettier/recommended',
  ],
};

之所以可以这样写,是因为通常在这些 extends 的 ESLint 配置文件中,都已经帮你把使用到的 plugin 给定义好,所以你只要确保有安装了这些 plugin,即使没有在项目的 ESLint 配置文件中明确通过 plugins 定义这些 plugin,它们还是会被加载。

例如,你可以在 eslint-plugin-react 的 recommended config 中看到,它已经帮你把 plugins: ['react'] 写好了:

因此一旦你有使用 extends: ['plugin:react/recommended'] 加载这个配置文件,它就已经帮你在 plugins 的地方加载 eslint-plugin-react。

创建公司内部的 ESLint config

在了解 ESLint 中 plugin、extends 和 rules 的概念后,就可以知道,我们只需要先创建好一个可以被共用的 ESLint 配置文件,在这里面定义好各项目都希望遵循的规则及使用规则的逻辑后,接着只要在各个项目中,各自通过 extends 的方式,加载这个共用的配置文件,就可以达到预期的效果。

以代码的概念会像是这样子。

先创建一支准备被共用的 ESLint 配置文件,例如这里取名叫 base.eslint.config.js

代码语言:javascript
代码运行次数:0
复制
// base.eslint.config.js
module.exports = {
  env: {
    es6: true,
  },
  extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'],
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 12,
    sourceType: 'module',
  },
  plugins: [],
  rules: {
    // new JSX transform
    'react/jsx-uses-react': 'off',
    'react/react-in-jsx-scope': 'off',

    // customized rules
    'no-console': 'off',
  },
};

接着在项目本身的 ESLint 配置文件中,通过 extends 的方式去读取到这支共用的配置文件就可以了。

假设这支共用的配置文件和项目自身的 ESLint 配置文件放在相同的路径时,可以直接这样加载:

代码语言:javascript
代码运行次数:0
复制
// .eslintrc.js
// 项目本身的 ESLint 配置文件
module.exports = {
  extends: ['./base.eslint.config'],
};

如此这个项目就会加载并使用这支共用的设置。

如果要确定项目有没有实际吃到 extends 的内容,可以通过下列指令,复制项目中 ESLint 最终实际会使用到的设置来瞧瞧:

代码语言:shell
复制
# 检视并复制当前使用到的 eslint config
$ npx eslint --print-config path::String | pbcopy

可以看到,虽然项目本身的 ESLint config 只有短短一行 extends: ['./base.eslint.config'] ,但通过上述的指令,可以看到实际使用的设置非常多:

除了可以看到有使用了几个 plugins 之外,我们写在 base.eslint.config.js 中的设置(例如: no-console 的规则逻辑)也有出现在最终实际会被 ESLint 使用到的设置中。

根据需要创建不同类型的配置文件

由于共用的配置文件本身也只是 JavaScript,因此不需要把所有的设置都放在同一支 base.eslint.config.js 中,而是可以通过 JavaScript 模块的方式来进行管理。

例如,可以有一支 base 的配置文件:

代码语言:javascript
代码运行次数:0
复制
// base.eslint.config.js
module.exports = {
  env: {
    es6: true,
  },
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: 12,
    sourceType: 'module',
  },
  rules: {
    // ...
  },
};

接着在创建一个专门给 React 项目使用的,在这支 react.eslint.config.js 中,会先去 extends base 的设置,把刚刚写的配置文件加载后,再加上针对 React 项目要使用的规则:

代码语言:javascript
代码运行次数:0
复制
// react.eslint.config.js
module.exports = {
  extends: [
    // 加载 base 的设置
    './base.eslint.config',

    // 使用针对 react 想要使用的设置
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
  ],
  rules: {
    // new JSX transform
    'react/jsx-uses-react': 'off',
    'react/react-in-jsx-scope': 'off',
  },
};

针对 TypeScript 的项目,可以同样 extends base 的设置后,针对 TypeScript 的部分进行设置:

代码语言:typescript
复制
// typescript.eslint.config.js
module.exports = {
  extends: ['./base.eslint.config'],
  plugins: ['@typescript-eslint'],
  parser: '@typescript-eslint/parser',
  rules: {
    'no-param-reassign': [
      'error',
      {
        props: true,
        ignorePropertyModificationsFor: ['state'],
      },
    ],
  },
};

如此,未来如果新开发的是 react 的项目,该项目可以直接 extends react.eslint.config.js ;如果是 TypeScript 的项目,则可以 extends typescript.eslint.config.js 即可。

包成 NPM 包

实际上,我们不只是会创建共用的 ESLint 配置文件,还会把这个共用的 eslint 配置文件发布到 npm 上,就像是前面提到的 eslint-config-standard 或 eslint-config-airbnb 一样,让想要使用这个配置文件的项目只需要通过 npm 就可以安装对应的配置文件,并且通过 extends 的方式来加载。

以 upup 为例,我们把共用的 eslint 配置文件包成一个名为 eslint-config-upup 的包,未来开新项目的时候,只需通过 npm install eslint-config-upup 下载配置文件后,接着在项目本身的 ESLint 配置文件中去 extends 它就可以了:

代码语言:javascript
代码运行次数:0
复制
// .eslintrc.js
module.exports = {
  extends: ['upup'], // 包名称开头的 "eslint-config-*"" 可以省略
};

如果是针对特定类型的项目一样可以像这样进行 extends:

代码语言:javascript
代码运行次数:0
复制
// .eslintrc.js
module.exports = {
  extends: ['upup/react'], // 如果是针对 react 的项目
};

或是针对 TypeScript 的项目:

代码语言:javascript
代码运行次数:0
复制
module.exports = {
  extends: ['upup/typescript'], // 如果是针对 typescript 的项目
}; 

规则使用的逻辑是可以被覆盖的

另外因为 extends 的概念是加载另一个配置文件进来,如果有需要根据项目客制化调整的话,还是可以在项目本身的 eslint 配置文件通过 rules 进行覆盖和调整。

举例来说,虽然在 eslint-config-upup 默认针对 no-console 这个规则是警告(warning),但根据项目个别的需要,还是可以覆盖和调整:

代码语言:javascript
代码运行次数:0
复制
// .eslintrc.js
module.exports = {
  extends: ['upup'],
  
  // 虽然在 eslint-config-upup 默认的规则是 warning,但可以通过覆盖的方式调整
  rules: {
    'no-console': 'error',
  },
};

一样通过先前提供的指令,复制最终执行的 ESLint 配置文件后,可以看到调整后最终出来的设置会是这样:

公司内部使用的一些规则设置

首先,因为公司中有部分项目是从 JavaScript 导入成 TypeScript 的,因此针对 TS 的文件我们是使用 ESLint 提供的 overrides 配置项来进行规则覆盖,也就是 TS 的设置只会使用在以 .ts.tsx 为后缀的文件,而不会使用到 JS 的文件,如此确保在项目中 JS 和 TS 的文件可以共存:

代码语言:javascript
代码运行次数:0
复制
// typescript.eslint.config.js
module.exports = {
  extends: ['./base'].map(require.resolve),

  // 使用 overrides 配置项,overrides 中的 ESLint 的设置只会使用到 .ts 或 .tsx 的文件
  overrides: [
    {
      files: ['**/*.ts?(x)'],
      extends: [
        'plugin:@typescript-eslint/recommended',
        'plugin:@typescript-eslint/recommended-requiring-type-checking',
      ],
      parserOptions: {
        // typescript-eslint specific options
        warnOnUnsupportedTypeScriptVersion: true,
      },
      rules: {
        // ...
      },
    },
  ],
};

另外针对 TypeScript 的 enum 类型,会限制只能用大写字母搭配底线来进行定义:

代码语言:javascript
代码运行次数:0
复制
// typescript.eslint.config.js
module.exports = {
  // ...
  rules: {
    '@typescript-eslint/naming-convention': [
      'error',
      {
        selector: 'enum',
        format: null,
        custom: {
          // enum should be uppercase and snakecase and allow double underscore
          regex: '^[A-Z][A-Z0-9]*(__?[A-Z0-9]+)*$',
          match: true,
        },
      },
    ],
  },
};

以及内部一些开发上的惯例,也会建在 ESLint 中。像是针对类型没有一定要在文件中的上方先定义后,才能在下方被使用:

代码语言:javascript
代码运行次数:0
复制
// typescript.eslint.config.js
module.exports = {
  // ...
  rules: {
    '@typescript-eslint/no-use-before-define': [
      'error',
      { ignoreTypeReferences: true },
    ],
    // ...
  },
};

针对 TypeScript 的 React 组件,因为已经有通过 TypeScript 进行 props 的定义,就可以把原本的 react/prop-types 的规则给关掉:

代码语言:javascript
代码运行次数:0
复制
// typescript.eslint.config.js
module.exports = {
  // ...
  rules: {
    'react/prop-types': 'off',
  },
};

另外,为了避免在 React 项目中,有人使用 named import,有人使用 default import,导致代码中会同时出现这两种不同的导入方法:

代码语言:javascript
代码运行次数:0
复制
// named import pattern
import { useState } from 'react';
const [foo, setFoo] = useSate();

// default import pattern
import React from 'react';
const [bar, setBar] = React.useState();

一样可以通过 ESLint 的设置来达到一致的编写规范:

代码语言:javascript
代码运行次数:0
复制
module.exports = {
  // ...
  rules: {
    'no-restricted-imports': [
      'error',
      {
        name: 'react',
        importNames: ['default'],
        message: "use import { xxx } from 'react'; instead",
      },
    ],
  },
};

总结

通过把公司内部共用的 ESLint 配置文件包成 npm 包,后续新开发的项目都可以直接通过 ESLint 的 extends 配置项来共用设置,就可以有效解决新项目每次都要先复制重复的 ESLint 设置,也可以避免各项目间彼此 ESLint 设置差异过大的麻烦。此外,如果公司有什么新规则希望可以使用到各个项目中时,也只需要去修改这个在 npm 上共用的 eslint-config package 就可以了,而不需要每个项目都去改动。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么要创建自己的 eslint-config package
  • 认识 ESLint 中的常见配置项
    • plugins
    • extends
  • 创建公司内部的 ESLint config
    • 根据需要创建不同类型的配置文件
    • 包成 NPM 包
    • 规则使用的逻辑是可以被覆盖的
  • 公司内部使用的一些规则设置
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档