From 8643c05341b6a8727cd0e81b3092209d6f492d4a Mon Sep 17 00:00:00 2001 From: Vianney MORAIN Date: Wed, 17 Jun 2026 14:48:57 +0200 Subject: [PATCH 1/4] fix(docker): hoist react-router-dom in pnpm deploy to fix builder startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pnpm v9 with autoInstallPeers=true resolves react-router-dom as a peer dependency of @remix-run/react rather than as a direct dep, so pnpm deploy --prod omits the top-level node_modules symlink. Node cannot find the package at runtime → ERR_MODULE_NOT_FOUND crash on startup. Adding public-hoist-pattern in apps/builder/.npmrc forces pnpm deploy to create the top-level symlink regardless of peer resolution. Co-Authored-By: Claude Sonnet 4.6 --- apps/builder/.npmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/builder/.npmrc diff --git a/apps/builder/.npmrc b/apps/builder/.npmrc new file mode 100644 index 000000000000..eac4d1995892 --- /dev/null +++ b/apps/builder/.npmrc @@ -0,0 +1 @@ +public-hoist-pattern[]=react-router-dom From 3b6705e1a4e56d80629aeea3223fd6c2591b3e18 Mon Sep 17 00:00:00 2001 From: Vianney MORAIN Date: Wed, 17 Jun 2026 14:50:06 +0200 Subject: [PATCH 2/4] docs(claude): document apps/builder/.npmrc pnpm hoisting config Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ebdc6ead4f30..256b2edcbf24 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -136,12 +136,13 @@ pnpm -r --filter='./packages/**' build ### Environment files -| File | Purpose | -| ------------------------------- | -------------------------------------------------------- | -| `apps/builder/.env` | Default config (DB URL, PostgREST URL, `DEV_LOGIN=true`) | -| `apps/builder/.env.development` | Local overrides (`AUTH_SECRET=1234`, `DOCKER_DEV=true`) | +| File | Purpose | +| ------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| `apps/builder/.env` | Default config (DB URL, PostgREST URL, `DEV_LOGIN=true`) | +| `apps/builder/.env.development` | Local overrides (`AUTH_SECRET=1234`, `DOCKER_DEV=true`) | +| `apps/builder/.npmrc` | pnpm hoisting config — forces `react-router-dom` top-level symlink in `pnpm deploy --prod` (Docker image fix) | -Both files are committed. Edit `.env.development` for machine-specific overrides. +Both env files are committed. Edit `.env.development` for machine-specific overrides. --- From a91d95d07fc3b02cc4df2c6ac303a0809a72e9fb Mon Sep 17 00:00:00 2001 From: Vianney MORAIN Date: Wed, 17 Jun 2026 15:13:21 +0200 Subject: [PATCH 3/4] fix(docker): create react-router-dom symlink after pnpm deploy pnpm v9 with autoInstallPeers=true does not create a top-level symlink for react-router-dom in pnpm deploy --prod: it treats it as provided via @remix-run/react peer resolution, so Node fails at runtime with ERR_MODULE_NOT_FOUND. Replace the .npmrc public-hoist-pattern approach (which only affects workspace installs, not pnpm deploy) with an explicit ln -sf from the virtual store in the Dockerfile. Co-Authored-By: Claude Sonnet 4.6 --- apps/builder/.npmrc | 1 - apps/builder/Dockerfile | 9 +++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) delete mode 100644 apps/builder/.npmrc diff --git a/apps/builder/.npmrc b/apps/builder/.npmrc deleted file mode 100644 index eac4d1995892..000000000000 --- a/apps/builder/.npmrc +++ /dev/null @@ -1 +0,0 @@ -public-hoist-pattern[]=react-router-dom diff --git a/apps/builder/Dockerfile b/apps/builder/Dockerfile index 2076ef6db87c..f781f85a79a8 100644 --- a/apps/builder/Dockerfile +++ b/apps/builder/Dockerfile @@ -57,6 +57,15 @@ RUN pnpm --filter=@webstudio-is/http-client build && \ # (not in the package's own .gitignore) specifically so pnpm deploy includes it. RUN pnpm --filter "@webstudio-is/builder" --prod deploy /standalone +# pnpm v9 with autoInstallPeers=true does not create a top-level symlink for +# react-router-dom: it treats it as already provided via @remix-run/react's peer +# resolution, so Node cannot find it at runtime (ERR_MODULE_NOT_FOUND). +# Create the symlink explicitly from the virtual store. +RUN store=/standalone/node_modules/.pnpm && \ + entry=$(ls "$store" | grep '^react-router-dom@' | head -1) && \ + [ -n "$entry" ] && [ ! -e /standalone/node_modules/react-router-dom ] && \ + ln -sf "$store/$entry/node_modules/react-router-dom" /standalone/node_modules/react-router-dom || true + # ─── Stage 2: prisma-cli ───────────────────────────────────────────────────── # Install the Prisma CLI in isolation so it carries Alpine-compatible engine binaries # without polluting the app's node_modules. From 80b98c4a2011d86900d1bc78de153972742dc15b Mon Sep 17 00:00:00 2001 From: Vianney MORAIN Date: Wed, 17 Jun 2026 15:30:27 +0200 Subject: [PATCH 4/4] fix(docker): use relative symlink for react-router-dom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix used an absolute path (/standalone/node_modules/.pnpm/...) which breaks when the runner stage copies node_modules from /standalone to /app — the symlink still pointed to /standalone which no longer exists. pnpm creates all its symlinks as relative paths; match that convention. Co-Authored-By: Claude Sonnet 4.6 --- apps/builder/Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/builder/Dockerfile b/apps/builder/Dockerfile index f781f85a79a8..5d4401521e48 100644 --- a/apps/builder/Dockerfile +++ b/apps/builder/Dockerfile @@ -61,10 +61,9 @@ RUN pnpm --filter "@webstudio-is/builder" --prod deploy /standalone # react-router-dom: it treats it as already provided via @remix-run/react's peer # resolution, so Node cannot find it at runtime (ERR_MODULE_NOT_FOUND). # Create the symlink explicitly from the virtual store. -RUN store=/standalone/node_modules/.pnpm && \ - entry=$(ls "$store" | grep '^react-router-dom@' | head -1) && \ +RUN entry=$(ls /standalone/node_modules/.pnpm | grep '^react-router-dom@' | head -1) && \ [ -n "$entry" ] && [ ! -e /standalone/node_modules/react-router-dom ] && \ - ln -sf "$store/$entry/node_modules/react-router-dom" /standalone/node_modules/react-router-dom || true + ln -sf ".pnpm/$entry/node_modules/react-router-dom" /standalone/node_modules/react-router-dom || true # ─── Stage 2: prisma-cli ───────────────────────────────────────────────────── # Install the Prisma CLI in isolation so it carries Alpine-compatible engine binaries