pnpm workspaceのモノレポをマルチステージビルドするにはturbo pruneがよい

課題

pnpm workspaceのモノレポをDockerでマルチステージビルドして動かすには、公式ドキュメントpnpm deployが紹介されています。
このpnpm deployですが、injectWorkspacePackages: trueが必須になっており、これが開発体験に悪い影響を与えると感じます。

injectWorkspacePackages

これが有効になっていると、pnpm install時にworkspace内のパッケージがnode_modulesにコピーされ、VSCodeでのコードジャンプもnode_modules内に向いてしまいます。
しかし、開発時はパッケージのsrcディレクトリのtsファイルを参照して欲しく、パッケージのコードの編集は即座に反映されて欲しいものです。

ですから、開発体験を落とさないためにinjectWorkspacePackagesfalseを保ったまま、うまくマルチステージビルドを解決する必要があります。

turbo prune

Turborepoのpruneを使うことで上記の課題は解決できます。pruneは依存関係にあるファイルを抜き出し、部分的なモノレポを構築してくれます。Dockerの場合は、ドキュメントにある通り、turbo pruneで部分的なモノレポを作ったあと、作っておいたturbo.jsonに従ってturbo buildすることでpnpm deployを避けることができます。

pnpmのドキュメントのDockerfileの例をturbo pruneに直すとこんな感じになります。
ディレクトリ構造の想定は以下です。

.
├── apps
│   ├── app1
│   └── app2
├── packages
│   └── utils
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.json

Dockerfile

FROM node:20-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable

FROM base AS base-builder
WORKDIR /app
COPY . .
RUN pnpm install -g turbo

FROM base-builder AS app1-builder
RUN turbo prune app1 --docker

FROM base-builder AS app2-builder
RUN turbo prune app2 --docker

FROM base AS app1-installer
WORKDIR /app
COPY --from=app1-builder /app/out/json/ .
COPY --from=app1-builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY --from=app1-builder /app/out/full/ .

COPY --from=app1-builder /app/tsconfig.json ./tsconfig.json

RUN pnpm build

FROM base AS app2-installer
WORKDIR /app
COPY --from=app2-builder /app/out/json/ .
COPY --from=app2-builder /app/out/pnpm-lock.yaml ./pnpm-lock.yaml
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
COPY --from=app2-builder /app/out/full/ .
COPY --from=app2-builder /app/tsconfig.json ./tsconfig.json
RUN pnpm build

FROM base AS app1
WORKDIR /app
COPY --from=app1-installer /app/ .
WORKDIR /app/apps/app1
EXPOSE 8000
CMD ["pnpm", "start"]

FROM base AS app2
WORKDIR /app
COPY --from=app2-installer /app/ .
WORKDIR /app/apps/app2
EXPOSE 8001
CMD ["pnpm", "start"]

結論

injectWorkspacePackagesを無効にして開発体験をキープしたまま、適切なマルチステージビルドが可能になりました。pnpm workspaceを使う場合はぜひTurborepoの併用を検討するといいでしょう。


元の記事を確認する

関連記事