自分用のESLint Shareable Configをつくった

自分用のESLint Shareable Configをつくり、npmパッケージとして公開しました。

## なぜつくったのか

自分が管理しているJavaScript/TypeScriptのリポジトリにおいて、中身がほとんど同じESLintの設定ファイルがいくつか存在していました。
これではメンテナビリティ的によろしくないので、共通化できそうな部分をShareable Configとして公開することにしたのです。

## 事前調査

パッケージをつくるにあたり、JavaScript用パッケージとTypeScript用パッケージの依存関係をどのようにすべきか、事前調査をしました。

https://qiita.com/munieru_jp/items/c9b26cf9984a09f73d6f

そのうえで、まずはJavaScript用パッケージをつくり、その後TypeScript用パッケージをつくる際にJavaScript用パッケージを依存に含めるかどうか考えることにしました。
結果的に、TypeScript用パッケージはJavaScript用パッケージに依存しないような構成になりました。

## つくったパッケージ

JavaScript用とTypeScript用の2つのパッケージをつくりました。

https://www.npmjs.com/package/@munierujp/eslint-config

https://www.npmjs.com/package/@munierujp/eslint-config-typescript

また、それらのパッケージの依存となる以下の2つのパッケージもつくりました。

https://www.npmjs.com/package/@eslint-recommended/eslint-config

https://www.npmjs.com/package/@eslint-recommended/eslint-config-typescript

各パッケージの内容について説明していきます。

### @eslint-recommended/eslint-config

https://www.npmjs.com/package/@eslint-recommended/eslint-config

`@eslint-recommended/eslint-config`は、JavaScript用のESLint Shareable Configです。
内部的には以下のパッケージを使用しています。

- [eslint-config-standard](https://www.npmjs.com/package/eslint-config-standard)
- [eslint-plugin-unicorn](https://www.npmjs.com/package/eslint-plugin-unicorn)
- [eslint-plugin-eslint-comments](https://www.npmjs.com/package/eslint-plugin-eslint-comments)
- [eslint-plugin-jsdoc](https://www.npmjs.com/package/eslint-plugin-jsdoc)

このパッケージでは自分の好みはなるべく排除して、各パッケージの推奨設定をそのまま採用しています。

現在の設定：

```js:index.js
module.exports = {
  extends: [
    'standard',
    'plugin:unicorn/recommended',
    'plugin:eslint-comments/recommended',
    'plugin:jsdoc/recommended'
  ]
}
```

出典：https://github.com/eslint-recommended/eslint-config/blob/66dbdbaa75f4cd672855b489291732273fb6bd7f/index.js

いまのところ`@munierujp/eslint-config`でしか使っていませんが、今後他のOrganization用のShareable Configをつくる際、こちらを共通の親として使うことを想定しています。

### @munierujp/eslint-config

https://www.npmjs.com/package/@munierujp/eslint-config

`@munierujp/eslint-config`は、JavaScript用のESLint Shareable Configです。
内部的には以下のパッケージを使用しています。

- [@eslint-recommended/eslint-config](https://www.npmjs.com/package/@eslint-recommended/eslint-config)
- [eslint-plugin-import-newlines](https://www.npmjs.com/package/eslint-plugin-import-newlines)

`@eslint-recommended/eslint-config`では自分の好みはなるべく排除していますが、こちらは自分の好みを反映した設定にしてあります。

現在の設定：

```js:index.js
const unicornRules = {
  // ファイル名のケースはものによって異なる（関数はローワーキャメルケース、クラスはアッパーキャメルケースなど）ので無効化
  'unicorn/filename-case': 'off',
  // Array.prototype.forEach() を使うのは理にかなっているので無効化
  'unicorn/no-array-for-each': 'off',
  // undefinedを明示的に書くのは理にかなっているので無効化
  'unicorn/no-useless-undefined': 'off',
  // 数値を 1_000 のように書くと読みやすいケースもあるもののそうではないケースもあるので無効化
  'unicorn/numeric-separators-style': 'off',
  // 完全にESMにはできないので無効化
  'unicorn/prefer-module': 'off',
  // セレクタによって要素の取得メソッドを使いわけるのは理にかなっているので無効化
  'unicorn/prefer-query-selector': 'off',
  // Array.from() を使うのは理にかなっているので無効化
  'unicorn/prefer-spread': 'off',
  // TODO [engine:node@>=18]: Node.js v18以上になったら有効化
  'unicorn/prefer-top-level-await': 'off',
  // 一般的に定着しているような略語を省略せずに書くのは冗長なので無効化
  'unicorn/prevent-abbreviations': 'off',
  // switch文のcase節を常にブロックにするのは冗長なので必要なときのみブロックにする
  'unicorn/switch-case-braces': ['error', 'avoid']
}

const importRules = {
  // 標準のソートを無効化
  'sort-imports': 'off',
  // アルファベット順にソート
  'import/order': ['error', {
    alphabetize: {
      order: 'asc',
      caseInsensitive: true
    }
  }],
  // 項目ごとに改行を強制
  'import-newlines/enforce': ['error', {
    items: 1,
    semi: false
  }]
}

module.exports = {
  extends: [
    '@eslint-recommended'
  ],
  plugins: [
    'import-newlines'
  ],
  rules: {
    ...unicornRules,
    ...importRules
  }
}
```

出典：https://github.com/munierujp/eslint-config/blob/0f91d7022ef481db7e0c615e5963b0c129bd8ad7/index.js

### @eslint-recommended/eslint-config-typescript

https://www.npmjs.com/package/@eslint-recommended/eslint-config-typescript

`@eslint-recommended/eslint-config-typescript`は、TypeScript用のESLint Shareable Configです。
内部的には以下のパッケージを使用しています。

- [eslint-config-standard-with-typescript](https://www.npmjs.com/package/eslint-config-standard-with-typescript)
- [eslint-plugin-unicorn](https://www.npmjs.com/package/eslint-plugin-unicorn)
- [eslint-plugin-eslint-comments](https://www.npmjs.com/package/eslint-plugin-eslint-comments)
- [eslint-plugin-jsdoc](https://www.npmjs.com/package/eslint-plugin-jsdoc)
- [eslint-plugin-tsdoc](https://www.npmjs.com/package/eslint-plugin-tsdoc)

このパッケージでは自分の好みはなるべく排除して、各パッケージの推奨設定をそのまま採用しています。

現在の設定：

```js:index.js
module.exports = {
  extends: [
    'standard-with-typescript',
    'plugin:unicorn/recommended',
    'plugin:eslint-comments/recommended'
  ],
  overrides: [
    {
      files: [
        '*.js'
      ],
      extends: [
        'plugin:jsdoc/recommended'
      ]
    },
    {
      files: [
        '*.ts'
      ],
      plugins: [
        'tsdoc'
      ],
      rules: {
        'tsdoc/syntax': 'warn'
      }
    }
  ]
}
```

出典：https://github.com/eslint-recommended/eslint-config-typescript/blob/04eb6c665e3b0db4947ce445cbe76770e18f3d0e/index.js

`@eslint-recommended/eslint-config`との違いは、`eslint-config-standard`のかわりに`eslint-config-standard-with-typescript`を使っていることと、`eslint-plugin-jsdoc`にくわえて`eslint-plugin-tsdoc`も使っていることです。
いまのところ`@munierujp/eslint-config-typescript`でしか使っていませんが、今後他のOrganization用のShareable Configをつくる際、こちらを共通の親として使うことを想定しています。

### @munierujp/eslint-config-typescript

https://www.npmjs.com/package/@munierujp/eslint-config-typescript

`@munierujp/eslint-config-typescript`は、TypeScript用のESLint Shareable Configです。
内部的には以下のパッケージを使用しています。

- [@eslint-recommended/eslint-config-typescript](https://www.npmjs.com/package/@eslint-recommended/eslint-config-typescript)
- [eslint-plugin-import-newlines](https://www.npmjs.com/package/eslint-plugin-import-newlines)

`@eslint-recommended/eslint-config-typescript`では自分の好みはなるべく排除していますが、こちらは自分の好みを反映した設定にしてあります。

現在の設定：

```js:index.js
const unicornRules = {
  // ファイル名のケースはものによって異なる（関数はローワーキャメルケース、クラスはアッパーキャメルケースなど）ので無効化
  'unicorn/filename-case': 'off',
  // Array.prototype.forEach() を使うのは理にかなっているので無効化
  'unicorn/no-array-for-each': 'off',
  // undefinedを明示的に書くのは理にかなっているので無効化
  'unicorn/no-useless-undefined': 'off',
  // 数値を 1_000 のように書くと読みやすいケースもあるもののそうではないケースもあるので無効化
  'unicorn/numeric-separators-style': 'off',
  // 完全にESMにはできないので無効化
  'unicorn/prefer-module': 'off',
  // セレクタによって要素の取得メソッドを使いわけるのは理にかなっているので無効化
  'unicorn/prefer-query-selector': 'off',
  // Array.from() を使うのは理にかなっているので無効化
  'unicorn/prefer-spread': 'off',
  // TODO [engine:node@>=18]: Node.js v18以上になったら有効化
  'unicorn/prefer-top-level-await': 'off',
  // 一般的に定着しているような略語を省略せずに書くのは冗長なので無効化
  'unicorn/prevent-abbreviations': 'off',
  // switch文のcase節を常にブロックにするのは冗長なので必要なときのみブロックにする
  'unicorn/switch-case-braces': ['error', 'avoid']
}

const importRules = {
  // 標準のソートを無効化
  'sort-imports': 'off',
  // アルファベット順にソート
  'import/order': ['error', {
    alphabetize: {
      order: 'asc',
      caseInsensitive: true
    }
  }],
  // 項目ごとに改行を強制
  'import-newlines/enforce': ['error', {
    items: 1,
    semi: false
  }]
}

module.exports = {
  extends: [
    '@eslint-recommended/eslint-config-typescript'
  ],
  plugins: [
    'import-newlines'
  ],
  rules: {
    ...unicornRules,
    ...importRules
  }
}
```

出典：https://github.com/munierujp/eslint-config-typescript/blob/2bc85d9ea0a83bbe65b29b1ddd59427daf3db69e/index.js

`@munierujp/eslint-config`との違いは、`@eslint-recommended/eslint-config`のかわりに`@eslint-recommended/eslint-config-typescript`を使っていることです。