Slide 1

Slide 1 text

0 ESLintをもっと有効活⽤しよう 2024-12-13 第110回NearMe技術勉強会 Ryo Omori

Slide 2

Slide 2 text

1 レガシーコードでよく⾒る例(NearMeに限らない話だと思います) ● コーディング規約が守られていない ● 型がない変数や関数が多くある ● 過剰なOptional ● ⼀⽬⾒てcomplexityが⾼そうなコード ● 重複コードが⼀定数存在する

Slide 3

Slide 3 text

2 技術的負債解決のアプローチは様々 ● アーキテクチャの導⼊ ● ライブラリの導⼊ ● コーディング規約を作る ● リファクタ⽂化の醸成(20%ルール、Boy Scout Rule、振り返り) ● ⾃動テストの強化 などなど

Slide 4

Slide 4 text

3 今⽇はESLintのお話し 3

Slide 5

Slide 5 text

4 NearMeのeslint ESLintとTypeScriptの推奨ルールセットである 「eslint:recommended」「plugin:@typescript-eslint/recommended」 を適⽤しているものの、レガシーコードに関してはType-Unsafeなコードがwarningで残っ ているのが特徴。

Slide 6

Slide 6 text

5 ESLintを含む静的解析がうまく機能しない理由 ● Warningだと無視されてしまうこともしばしば ● Errorにするには全コードを改修する必要がある ● もしくはlintを無効化する必要があるが、これだとは無効化されたコードはリファクタ の機会を失ってしまう

Slide 7

Slide 7 text

6 そこでreviewdog 6

Slide 8

Slide 8 text

7 reviewdogとは ● GitHubのようなコードホスティングサービスにレビューコメントを⾃動投稿すること が可能になるツール ● コードの変更箇所にのみlint結果をコメントとして投稿 ● lintの設定はローカル開発時とCI上で変えることもできる Github Repo:reviewdog - A code review dog who keeps your codebase healthy.

Slide 9

Slide 9 text

8 例えば以下のように.eslintrc.jsonをローカルとreviewdogで分けて .eslintrc.json(ローカル開発⽤) { "extends": ["next/core-web-vitals", "next/typescript"], "rules": { "@typescript-eslint/explicit-module-boundary-types": "warning" } } .eslintrc.reviewdog.json { "extends": ["next/core-web-vitals", "next/typescript"], "rules": { "@typescript-eslint/explicit-module-boundary-types": "error" } }

Slide 10

Slide 10 text

9 GitHub Actionのworkflowを作る github/workflows/reviewdog.yml name: Reviewdog on: [pull_request] jobs: reviewdog: name: Reviewdog runs-on: ubuntu-latest steps: (中略) - name: Run ESLint with reviewdog uses: reviewdog/action-eslint@v1 with: github_token: ${{ secrets.YOUR_TOKEN }} reporter: github-pr-review eslint_flags: '-c .eslintrc.reviewdog.json src/' fail_level: "error"

Slide 11

Slide 11 text

10 このような変更を加えてPRを出すと

Slide 12

Slide 12 text

11 変更箇所にのみエラーのコメントをつけることが可能

Slide 13

Slide 13 text

12 レガシーコードに修正が⼊った時もCI上はエラーになる

Slide 14

Slide 14 text

13 reviewdogで差分に対してlintができると ● 新しいコードの品質を担保できる ● 修正に巻き込まれるレガシーコードの修正を促せる ● 新しいコーディング規約を追加しやすくなります (レガシーコードを修正しなくても良いため)

Slide 15

Slide 15 text

14 差分に対してlintができるため ● 新しいコードの品質を担保できる ● 修正に巻き込まれるレガシーコードの修正を促せる ● 新しいコーディング規約を追加しやすくなります (レガシーコードを修正しなくても良いため)    ↓ 基本的な規約(“より’を推奨する等)は増減するものではありませんが プロジェクト固有の問題に対して制約を設けたくなることはある。

Slide 16

Slide 16 text

15 ESLintのカスタマイズ 15

Slide 17

Slide 17 text

16 例えば以下のようなコード argsとcontextの値がごっちゃになるのでcontextのdestructuringをやめたい graphql/resolvers.ts export const resolvers = { Query: { getSetting: async ( _: unknown, { id, organizationId }: Args, { organizationId: contextOrganizationId }: Context ): Promise => { if (!contextOrganizationId) { throw new Error("Unauthorized"); } return { id, name: "⽻⽥空港送迎", organizationId: organizationId }; }, }, };

Slide 18

Slide 18 text

17 ESLintのカスタマイズ⽅法 1. ルールのディレクトリを⽤意 2. ルールのコードを書く 3. ESLint設定にルールを追加

Slide 19

Slide 19 text

18 1. ルールのディレクトリを⽤意 project-root/ │-- .eslintrc.json └-- eslint-plugin-custom-rules/ └-- no-destructuring-context.js

Slide 20

Slide 20 text

19 ESLintのルールを⾃作するには、AST(抽象構⽂⽊, Abstract Syntax Tree)を利⽤します。 ASTはコードをツリー構造として解析します。(https://astexplorer.net/) 2. ルールのコードを書く

Slide 21

Slide 21 text

20 2.ルールのコードを書く no-destructuring-context.js module.exports = { meta: { (中略...) }, create(context) { if (!/src\/schema\/.*\.ts$/.test(context.getFilename())) { return {}; } return { ArrowFunctionExpression(node) { // アロー関数の中の const param = node.params[2]; // 3つ⽬の引数が if ( param && (param.type === "ObjectPattern") // オブジェクトのdestructuring ) { context.report({ node: param, messageId: "noDestructuringContextArg", }); } }, }; }, };

Slide 22

Slide 22 text

21 2. ルールのコードを書く 1. metaオブジェクト: ○ ルールの種類、説明、推奨設定、メッセージIDなどを定義。 2. create関数: ○ ルールのメイン処理。ASTの特定のノードに対して検査し、違反を検出したらレポートを出 します。 3. ASTノードの検出: ○ ArrowFunctionExpression: アロー関数呼び出しを表すノード。 ○ node.params: アロー関数の引数 ○ params.type: 引数の型 ○ context.report: 違反が⾒つかった場合にエラーメッセージを報告。 公式:https://eslint.org/docs/latest/extend/custom-rules

Slide 23

Slide 23 text

22 ESLint設定にルールを追加 .eslintrc.json { "extends": [ "next/core-web-vitals", "next/typescript" ], "plugins": [ "custom-rules" ], "rules": { "@typescript-eslint/explicit-module-boundary-types": "error", "custom-rules/no-destructuring-context": "error" } } terminal $ yarn add -D file:eslint-plugin-custom-rules

Slide 24

Slide 24 text

23 以下のようにlintがかかるようになる

Slide 25

Slide 25 text

24 その他のCustom Lintの利⽤例 ● ORGANIZATION_ROLEなどのenumの直接参照をwarningにする ● graphql/schema 内、ObjectのFieldをBoolean, Int, ArrayについてOptionalをwarningにする ● Reactコンポーネント内でのインラインスタイルの使⽤を禁⽌する ● 複雑な関数や変数宣⾔に対して useCallback や useMemo の使⽤を促す ● next-i18next と react-i18nextの誤⽤防⽌ など ちょっと過剰な例もあるかもしれませんが ● プロジェクト固有のルールを強制し、品質を向上 ● コードレビューの効率化と⾃動化 ● バグやアンチパターンの早期発⾒ ● 保守性の向上と技術的負債の軽減 が可能になります reviewdogと合わせ導⼊を検討していきたい

Slide 26

Slide 26 text

25 Appendix 25

Slide 27

Slide 27 text

26 課題解決にはまず問題の可視化‧監視から Code Climate ● ESLintを含む静的解析(認知的複雑性やコードの重複なども計測可能)が可能 ● 技術的負債解析の結果を推移で確認できる ● Churn vs. maintainability というダッシュボードがあり、利⽤頻度が⾼くメンテナンス性も悪い ファイルが可視化でき、修正の優先順位付けをサポート ● privateリポジトリでも4⼈までは無料とのこと

Slide 28

Slide 28 text

27 Thank you