hacomono フロントエンド テックリードのみゅーとん(@_mew_ton)です
npm を使うプロジェクトを立ち上げる際、フレームワークやら、テストやらをどんどん導入していくと、一つの package.json に大量の依存パッケージが書き込まれて、管理が煩雑になるケースがしばしばあります
特にありがちなのが、 “このパッケージ、なんの目的で導入してるんだっけ?” となるなど
今回、いい感じに管理する方法を見つけたので、ご紹介します
TL; DR
- monorepo 構造にして、
dependencies-eslint
のような、特定の目的で利用する依存パッケージのみをな管理するだけのサブプロジェクトを作成する - 内容に package.json のみを配置し、利用するパッケージを dependencies に記載する
- ルートの package.json 及び、利用先のプロジェクトで、 dependencies, devDependencies など適切なタイプの依存先として
dependencies-eslint
を設定する
太りすぎた dependencies
下はうちのプロジェクトで使っている package.json です (※開発時に使用するパッケージのみ記載しています)
{ "name": "package-sample", "version": "0.0.1", "private": true, "workspaces": [ "packages/*" ], "scripts": { "dev": "turbo run dev", "lint": "turbo run lint", "test": "turbo run test", "format": "prettier --write . --ignore-path .eslintignore", "dedup": "yarn-deduplicate --strategy=fewer", "postdedup": "yarn install" }, "devDependencies": { "@hacomono/eslint-config-vue3": "0.0.11", "@hacomono/prettier-config": "0.0.11", "@storybook/addon-a11y": "6.5.13", "@storybook/addon-actions": "6.5.13", "@storybook/addon-essentials": "6.5.13", "@storybook/addon-interactions": "6.5.13", "@storybook/addon-links": "6.5.13", "@storybook/addon-postcss": "2.0.0", "@storybook/jest": "0.0.10", "@storybook/test-runner": "0.9.0", "@storybook/testing-library": "0.0.13", "@storybook/testing-vue3": "0.0.2", "@storybook/vue3": "6.5.13", "@testing-library/vue": "6.6.1", "@typescript-eslint/eslint-plugin": "5.42.0", "@vitejs/plugin-vue": "3.2.0", "@vue/eslint-config-typescript": "11.0.2", "axe-core": "4.5.1", "chromatic": "6.7.4", "eslint-config-prettier": "8.5.0", "eslint-config-turbo": "latest", "eslint-plugin-import": "2.26.0", "eslint-plugin-storybook": "0.6.7", "eslint-plugin-vue": "9.7.0", "eslint": "8.27.0", "jest-environment-node": "29.2.2", "jest": "29.2.2", "playwright": "1.27.1", "prettier": "2.7.1", "storybook-addon-designs": "6.3.1", "storybook-builder-vite": "0.1.23", "turbo": "latest", "typescript": "4.8.4", "vite": "3.2.2", "yarn-deduplicate": "latest" }, "resolutions": { "postcss": "^8.0.0" }, "engines": { "node": ">=16.18.0" } }
見て分かる通り、カオスです。
このプロジェクトでは、以下の理由で devDependencies を構成しています
- monorepo 構成を管理してのために turborepo を導入している
- フロントエンドの基本構成として、 typescript, vue, vite を採用している
- フォーマッターとして eslint 及びそのプラグインを大量導入している
- テスト・ドキュメンテーションのために storybook, jest とそのプラグインを導入している
しかし、この構成では例えば axe-core
のような名前に storybook, jest などの共通のパッケージ名が含まれていないと、どういう用途で導入したパッケージがわかりづらくなるなど、管理が非常に辛くなります
dependencies の整理方法
1. パッケージを用途別に整理する
devDependencies に記載されているパッケージ群を用途別に分類してみます
今回は eslint と storybook に関する依存パッケージが多いため、これらを分類しました
eslint の依存パッケージ
- @hacomono/eslint-config-vue3
- @typescript-eslint/eslint-plugin
- @vue/eslint-config-typescript
- eslint
- eslint-config-prettier
- eslint-plugin-import
- eslint-plugin-storybook
- eslint-plugin-vue
- eslint-config-turbo
storybook の依存パッケージ
- @storybook/addon-a11y
- @storybook/addon-actions
- @storybook/addon-essentials
- @storybook/addon-interactions
- @storybook/addon-links
- @storybook/addon-postcss
- @storybook/jest
- @storybook/test-runner
- @storybook/testing-library
- @storybook/testing-vue3
- @storybook/vue3
- @testing-library/vue
- @vitejs/plugin-vue
- axe-core
- chromatic
- playwright
- jest-environment-node
- jest
- storybook-addon-designs
- storybook-builder-vite
一見、jest (Unitテスト用ライブラリ), playwright (E2Eテスト用ライブラリ) は Storybook の分類に含めるのは変では?と思われるかもしれませんが、
これらは、Storybook の integration テストを実施するために導入しています
2. 依存管理用サブプロジェクトを作成する
eslint のみにターゲットを絞って説明します。
./packages/dependencies-eslint
というサブプロジェクトを作り、そのルートに 以下の内容で package.json
を作成します
{ "name": "@hacomono/dependencies-eslint", "version": "0.0.1", "private": true, "dependencies": { "@hacomono/eslint-config-vue3": "0.0.11", "@typescript-eslint/eslint-plugin": "5.42.0", "@vue/eslint-config-typescript": "11.0.2", "eslint": "8.27.0", "eslint-config-prettier": "8.5.0", "eslint-plugin-import": "2.26.0", "eslint-plugin-storybook": "0.6.7", "eslint-plugin-vue": "9.7.0", "eslint-config-turbo": "latest" } }
作成後、ルートの package.json
側で、eslint のための依存パッケージを消し、 @hacomono/dependencies-eslint
に置き換えます。
{ "name": "package-sample", "version": "0.0.1", "private": true, "workspaces": [ "packages/*" ], "scripts": { "dev": "turbo run dev", "lint": "turbo run lint", "test": "turbo run test", "format": "prettier --write . --ignore-path .eslintignore", "dedup": "yarn-deduplicate --strategy=fewer", "postdedup": "yarn install" }, "devDependencies": { "@hacomono/dependencies-eslint": "*", // ← eslint の依存パッケージのまとめ "@hacomono/dependencies-storybook": "*", // ← storybook の依存パッケージのまとめ "@hacomono/prettier-config": "0.0.11", "prettier": "2.7.1", "turbo": "latest", "typescript": "4.8.4", "yarn-deduplicate": "latest" }, "resolutions": { "postcss": "^8.0.0" }, "engines": { "node": ">=16.18.0" } }
超すっきりしました
Pros, Cons
目的ごとに依存パッケージを整理できるので、dependencies の管理が圧倒的にしやすくなったと思います。
反面、パッケージの構造上うまく動かない可能性も無きにしもあらずのため、対応は慎重に行うべきかなと思います。
まとめ
依存パッケージだけを管理するサブプロジェクトを作る という方法で、
package.json の肥大化を防ぐ方法を今回紹介しました。
依存パッケージを整理して、きれいな monorepo 構成を維持していきたいですね。