Upgrade to Pro — share decks privately, control downloads, hide ads and more …

pnpm monorepo で ビルドいらずの パッケージ参照を実現する

pnpm monorepo で ビルドいらずの パッケージ参照を実現する

Avatar for offich

offich

May 12, 2026

More Decks by offich

Other Decks in Programming

Transcript

  1. 背景:monorepo でよくある問題 コンポーネントカタログサイトがコンポーネントライブラリに依存している packages/ component-lib/ ← React コンポーネント群 catalog-site/ ←

    Astro コンポーネントカタログサイト カタログサイトの package.json : { "dependencies": { "@myorg/component-lib": "workspace:*" } } 4
  2. 問題:開発のたびにビルドが必要 コンポーネントライブラリが dist/ を publish ターゲットにしているため component-lib/ src/ ← TypeScript

    ソース dist/ ← ビルド成果物(.mjs / .cjs / .d.mts) カタログサイトの Vite が解決するのは dist/ の成果物 コンポーネントライブラリを変更 ↓ pnpm --filter component-lib build ← 毎回これが必要 ↓ カタログサイトで変更が反映される 開発体験が悪い 5
  3. 解決策:Custom Condition Node.js / Vite の 条件付きエクスポート(Conditional Exports) の仕組みを使う package.json

    の exports フィールドにカスタムの条件を追加し、 開発時だけ TypeScript ソースを直接参照 させる コンポーネントライブラリを変更 ↓ 即座に反映される (ビルド不要) 6
  4. コンポーネントライブラリ側の設定 @lib/source という条件が有効なときだけ ./src/index.ts を返す packages/component-lib/package.json { "exports": { ".":

    { "@lib/source": "./src/index.ts", // ← カスタムコンディション "types": { "import": "./dist/index.d.mts", "require": "./dist/index.d.cts" }, "import": "./dist/index.mjs", // 通常はビルド成果物 "require": "./dist/index.cjs" } } } 7
  5. 注意:条件の順番が重要 カスタムコンディションは exports 内で一番上に配置すること Node.js / Vite は条件を上から順に評価し、最初にマッチしたものを使う { "exports":

    { ".": { "@lib/source": "./src/index.ts", // 一番上に置く "import": "./dist/index.mjs", // 下にあるので @lib/source が優先される "default": "./dist/index.mjs" } } } import れる や types より下に置くと先にマッチしてしまい、カスタムコンディションが無視さ 8
  6. カタログサイト側の設定 packages/catalog-site/astro.config.mjs const resolve = process.env.NODE_ENV === "development" ? {

    conditions: ["@lib/source"] } // ← 開発時のみ有効化 : undefined; export default defineConfig({ vite: { resolve: resolve, ssr: { noExternal: ["@myorg/component-lib"], resolve: resolve, }, }, }); 9
  7. 動作の仕組み import { Button } from "@myorg/component-lib"; 開発時( NODE_ENV=development )

    Vite が exports を解決 → conditions に "@lib/source" が含まれる → "./src/index.ts" を返す → ビルド不要でソース直参照 本番ビルド時( NODE_ENV=production ) Vite が exports を解決 → conditions に "@lib/source" が含まれない → "./dist/index.mjs" を返す → 通常のビルド成果物を参照 10
  8. SSR(Astro)での注意点 Astro は SSR を持つため、Client / Server 両側 で条件を設定する必要がある vite:

    { // クライアントサイドバンドル向け resolve: { conditions: ["@lib/source"] }, ssr: { // サーバーサイドバンドル向け noExternal: ["@myorg/component-lib"], // ← これも必要 resolve: { conditions: ["@lib/source"] }, }, } を指定しないと SSR バンドル時にコンポーネントライブラリが外部扱いされ、 カスタムコンディションが無視される noExternal 11
  9. Custom Condition のメリット・デメリット 内容 ビルド不要で即座にソース変更が反映 HMR が正しく動作する 本番ビルドは従来と変わらない Vite /

    バンドラ側での設定が必要 TS ソースをそのまま食わせるため、消費側が TS に対応している必要がある カスタム条件名の命名を慣習として統一する必要がある 12
  10. 代替手法との比較 手法 Custom Condition exports["."]=src/ に直接向け る vite-plugin-turbo-resolve 等 tsconfig

    paths 毎回 build ビルド不 設定コス 備考 要 ト 低 今日の話 本番も src 参照になってしま 低 う 中 追加依存が増える 中 バンドラによっては動かない なし 開発体験が辛い 13
  11. まとめ 1. package.json の exports に カスタムコンディション を追加 "@lib/source": "./src/index.ts"

    2. 開発時のみ Vite に条件を渡す conditions: ["@lib/source"]; 3. Astro(SSR あり)は ssr.noExternal + ssr.resolve.conditions も設定 ビルドいらずで TypeScript ソースを直参照、開発体験が大きく改善 14