我给eslint-plugin-package-json添加了新的rule

285 阅读3分钟

前言

大概一周前,我在翻 github 的时候,发现了一个 大佬 - JoshuaKGoldbergeslint 插件 eslint-plugin-package-json,之前也没有正式搞过 eslint rule,这里记录下我给这个插件添加新的 rule 的整个过程。

起因

我自己在github有个 组织,里面有两个repo,

image.png

仓库分别包含了 eslintprettier 的 配置,所以关注有关插件多些。

无意间翻到了 eslint-plugin-package-json,这个仓库有一些针对 package.json 的规则。

image.png

简单看了 readmeissue,发现这个仓库真的不错,而且还在积极维护,作者也是一个顶级大佬,还是 typescript-eslint 的作者

image.png

issue 列表里面不少都是接收pr的状态,于是,我想自己能不能动手去解决一些issue呢?几经翻阅,我选了一个看起来比较简单的 github.com/JoshuaKGold…(其实真的不简单)

image.png

image.png

准备

这个 rule 要做的很简单,去除一些值为空数组或者空对象的字段,比如

{
   "main": "lib/index.js",
   "scripts": {},
   "files": []
}

转化为下面这样

{
   "main": "lib/index.js"
}

为了确保自己的思路没问题,我就直接在issue下面留言问了下,

image.png

得到了肯定的答复,我就开始准备处理这个问题

过程

在开始写代码之前,我考虑了一些case,比如

{
  "simple-git-hooks": {
    "pre-commit": "pnpm exec nano-staged --allow-empty",
    "preserveUnused": []
  }
}

这里的 preserveUnused 该不该被移除呢?理论上是应该和根部分的保持一致,我最开始写的时候,就先写了一版基础的,只能处理根部分的内容,大概花了一两天的时间,我写出来了代码,加了测试用例,

思路是定义好要处理的 field 数组,比如

const arrayFields = ['files', 'keywords', ...]
const objectFields = ['scripts', 'dependencies', ...]

package.json 文本转化为对象,然后删除指定的 field,再拼成一个对象,这样做的好处在于不用考虑 , 的问题了,

{
   "main": "lib/index.js",
   "scripts": {},
   "files": []
}

这个例子,要移除 scripts,也要把它后面的 , 移掉,然后移除 files,要把 main 后面的 , 也移掉,

不合理之处嘛,也多得很

image.png

作者告诉了我 This is a solid start,指出了挺多代码不合理的地方,我看了下 review comment,方向上出了一点问题,代码也不怎么合理,也没有考虑嵌套的情况。

image.png

于是在另一个 大佬 - michael faith 的指点下,我换了另外一种思路,从ast入手,处理 JSONArrayExpressionJSONObjectExpression

又折腾了一通之后,我又提交了一版代码,录了一个本地的测试视频

image.png

这次没啥大的问题,我也确实是走在了正确的方向,不过,JoshuaKGoldberg 提出了另外一个问题,

// package.json
{
  "some-custom-array": [ [] ],
  "some-custom-object": [ {} ],
}

image.png

这种case其实我第二次改的时候考虑到了,不过我在写的时候不是很清楚该怎么处理这种case,就直接没处理。我的想法是,这种case应该先不考虑支持,后面如果有人需要再考虑支持,因为添加特性简单,更改或移除难。

不过大佬说了自己的想法,支持起来不是很困难,我就在下面回复了我的思路,于是就有了昨天晚上的第三版代码

image.png

又更新了一波测试用例和文档,最后被合并并发布了🎉

image.png

这个pr前前后后搞了一个周,经过了反复讨论,代码也经过了反复修改,收获颇丰

image.png

结语

如有错误,欢迎指正。如有建议或想法,欢迎留言。