ESLint 解析extends路径规则

903 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天,点击查看活动详情

前言

今天忙里偷闲,准备把团队常用的ESLint配置汇总起来,形成自己的规则包。

规则包使用lerna开发,内计划有三个模块

  • base 不涉及框架的基础规则 包名@eslint-config-guide/base
  • react jsx和react-hook的规则 包名@eslint-config-guide/react
  • vue vue-jsx和vue相关的规则 包名@eslint-config-guide/vue

先随便写了两条规则,放在base中,部署到npm后,打算先趟趟坑。

@eslint-config-guide/base内容如下

module.exports = {
'rules': { // 制定具体规则
    'no-console': 'warn',  // console 报警
    'quotes': ['error', 'single'] // 引号必须单引号
  }
}

demo中调用方式如下:

module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    '@guide/base',
  ],
};

看着貌似没毛病,eslint-config-可以被省略,@eslint-config-guide/base省略成@guide/base

万万没想到啊,直接报错

[Error - 16:43:09] Error: Failed to load config "@guide/base" to extend from.

啥玩意? 找不到这个包,我打开node_modules查看路径,没毛病啊。

可能是我自己package.json没写main吧,报着这种心态的我,核对了自己的配置文件,并没有发现问题。

算了,不偷懒,写全称吧@eslint-config-guide/base

[Error - 16:43:09] Error: Failed to load config "@eslint-config-guide/base" to extend from.

好嘛,换了个错误提示就换了个包名。

此处忽略大量的百度、google查找过程。

问题不大,遇事不决抄作业,我安装了常见的eslint-config-airbnb和`eslint-plugin-vue。

核对了package.json和入口文件后,心态有点崩,啥问题都没。

此时的我突然灵光一闪,是不是因为我是二级路径导致的问题呢,我又安装了一个二级路径的包@vue\eslint-config-typescript

看到这个包名,我还寻思人家大佬就是不一样,非得按照规则走,二级目录也不愿意省略eslint-config-

对比完成后,没毛病啊。

诶,此时陷入深深的自我怀疑中。

要不我拿本地路径试试,将demo代码修改如下

module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    './node_modules/@eslint-config-guide/base',
  ],
};

没毛病,正常运行,那就说明我代码没问题啊。

细细想来,那就是extends规则我有疏漏之处,看看官网的介绍吧

并没找到具体的规则解析。

逼自己看源码吧,有兴趣的可以一起看源码

规则

假设我们的配置文件如下

module.exports = {
  extends: [
    'eslint:recommended',
    'eslint-config-react',
    'react',
    './myRule.js',
    'prettier/@typescript-eslint',
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/eslint-plugin/recommended',
    'plugin:@typescript-eslint/test/recommended', // 为了测试,杜撰的一个包
    'plugin:@typescript-eslint/eslint-plugin-irene/recommended', // 为了测试,杜撰的一个包
    'plugin:prettier/recommended',
  ]
}

由此可以看出extends 有几种写法

eslint 开头

如果是 eslint:recommended,加载 ESLint 推荐的规则 如果是 eslint:all,加载 ESLint 所有的规则

plugin: 开头

首先分割pluginName,也就是plugin: 和最后一个 / 的之间部分;有如下几种情况

  • plugin:@typescript-eslint/recommended 的 pluginName 是 @typescript-eslint
  • plugin:@typescript-eslint/eslint-plugin/recommended 的 pluginName 是 @typescript-eslint/eslint-plugin
  • plugin:@typescript-eslint/test/recommended 的 pluginName 是 @typescript-eslint/test
  • plugin:@typescript-eslint/eslint-plugin-irene/recommended 的 pluginName 是 @typescript-eslint/eslint-plugin-irene
  • plugin:prettier/recommended 的 pluginName 是 prettier

获取到pluginName后,就可以得到标准化的包名

如果 pluginName 以 @ 开头,说明使用的是 scoped modules;有如下几种情况:

  • pluginName 是 @scopeName 或 @scopeName/eslint-plugin,对应的包名是 @scopeName/eslint-plugin;
    • @typescript-eslint 对应的是 @typescript-eslint/eslint-plugin
    • @typescript-eslint/eslint-plugin 对应的是 @typescript-eslint/eslint-plugin
  • pluginName 是 @scopeName/xxx,且 xxx 不以 eslint-plugin 开头,对应的包名是 @scopeName/eslint-plugin-xxx;
    • @typescript-eslint/test 对应的是 @typescript-eslint/eslint-plugin-test
  • pluginName 是 @scopeName/eslint-plugin-xxx,对应的包名是 @scopeName/eslint-plugin-xxx;
    • @typescript-eslint/eslint-plugin-irene 对应的是 @typescript-eslint/eslint-plugin-irene

如果 pluginName 不以 eslint-plugin- 开头,对应的包名是 eslint-plugin-xxx;例如:prettier 对应的是 eslint-plugin-prettier

本地路径

一个本地路径,指向本地的 ESLint 配置,例如:./myRule.js; 以 . 开头,这是为了兼容之前的版本;

其余

根据 extendName 得到标准化的包名,这一步与 plugin 相同

如果 extendName 以 @ 开头,说明使用的是 scoped modules;有如下几种情况:

  • extendName 是 @scopeName@scopeName/eslint-config,对应的包名是 @scopeName/eslint-config
  • extendName 是 @scopeName/xxx,且 xxx 不以 eslint-config 开头,对应的包名是 @scopeName/eslint-config-xxx
  • extendName 是 @scopeName/eslint-config-xxx,对应的包名是 @scopeName/eslint-config-xxx
  • extendName 不以 eslint-config- 开头,对应的包名是 eslint-config-xxx;例如:react 对应的是 eslint-config-react

结论

将包名改为@eslint-config-guide/eslint-config-base后,demo中的调用

module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
  },
  extends: [
    '@guide/base',
  ],
};

成功,但这个包名看着有点难受,先撤销发包,后续调整调整再发布

总结

一直以来,都以为是前面的eslint-config-eslint-plugin-可以省略,却没想到第二层依然需要对应的前缀标识,这次真的涨知识了

纸上得来终觉浅,绝知此事要躬行。