Skip to content

fix(dev): stop @shopify/hydrogen watch from racing example bundlers - #3826

Closed
div-cowboy wants to merge 1 commit into
Shopify:previewfrom
div-cowboy:fix/dev-hydrogen-build-race
Closed

fix(dev): stop @shopify/hydrogen watch from racing example bundlers#3826
div-cowboy wants to merge 1 commit into
Shopify:previewfrom
div-cowboy:fix/dev-hydrogen-build-race

Conversation

@div-cowboy

Copy link
Copy Markdown

Problem

Running pnpm dev (and pnpm dev:next / pnpm dev:rr) prints a wall of transient errors at startup:

Module not found: Can't resolve '@shopify/hydrogen'
  ./examples/nextjs/proxy.ts
  ./examples/nextjs/lib/cart-handlers.ts
  ...

The app still serves once startup settles, but it looks broken and is alarming.

Cause

Those scripts schedule both @shopify/hydrogen#build (one-shot) and @shopify/hydrogen#dev (tsdown --watch). The watch rewrites dist/ on startup while the example bundlers (Turbopack, Vite) are reading it, so they momentarily resolve partial/empty module files → "Module not found".

I confirmed:

  • It's transient — dist/ files never disappear (high-frequency polling shows 0 missing); it's mid-write partial reads.
  • The watch alone causes it even when @shopify/hydrogen#build is a turbo cache hit, so serializing build→dev wouldn't fix it. The concurrent watch has to be removed from the examples' dev path.

Fix

Scope the dev scripts to the examples. Turbo still builds @shopify/hydrogen once via dependsOn: ["^build"], but no longer runs its watch alongside the examples:

- "dev": "turbo run dev",
+ "dev": "turbo run dev --filter='./examples/*'",
- "dev:rr": "turbo run dev --filter=@shopify/hydrogen-example-react-router...",
- "dev:next": "turbo run dev --filter=@shopify/hydrogen-example-nextjs...",
+ "dev:rr": "turbo run dev --filter=@shopify/hydrogen-example-react-router",
+ "dev:next": "turbo run dev --filter=@shopify/hydrogen-example-nextjs",

dev:pkgs still watches ./packages/* for SDK development — run it alongside an example only when actively editing the SDK. This also makes pnpm dev match its README description ("every example's dev server in parallel").

Trade-off

Editing @shopify/hydrogen source no longer hot-rebuilds into a running example under pnpm dev (use pnpm dev:pkgs for that). A future improvement could restore concurrent SDK-watch + examples by making the SDK build write dist/ atomically (temp + rename) so bundlers never observe a partial file.

Verification

Cold (packages/hydrogen/dist removed, .next and turbo cache cleared):

pnpm dev:next
→ 0 module-not-found errors (was 22)
→ @shopify/hydrogen#build runs; @shopify/hydrogen#dev (watch) does not

Turbo dry-run confirms @shopify/hydrogen#build is still scheduled and @shopify/hydrogen#dev is dropped.

🤖 Generated with Claude Code

Running `pnpm dev` (or `dev:next`/`dev:rr`) spewed a wall of transient
"Module not found: Can't resolve '@shopify/hydrogen'" errors at startup.

Cause: those scripts scheduled both `@shopify/hydrogen#build` (one-shot)
and `@shopify/hydrogen#dev` (`tsdown --watch`). The watch rewrites dist/
on startup while the example bundlers (Turbopack, Vite) are reading it,
so they momentarily see partial/empty module files. The errors are
transient (the app still serves once the watch settles), but they look
like a broken setup. Confirmed the watch alone causes it even when
`#build` is a cache hit, so serializing the two doesn't help — the
concurrent watch has to go.

Scope the dev scripts to the examples so turbo still builds
@shopify/hydrogen once (via `dependsOn: ["^build"]`) but does not run its
watch alongside them:

- `dev`     -> `turbo run dev --filter='./examples/*'`
- `dev:next`/`dev:rr` -> drop the trailing `...` (dependency) selector

`dev:pkgs` still watches the packages for SDK development; run it
alongside an example only when you're actively editing the SDK.

Verified cold (dist removed, caches cleared): `pnpm dev:next` now logs 0
module-not-found errors (was 22), with @shopify/hydrogen#build running
and #dev (watch) not.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@div-cowboy
div-cowboy requested a review from a team as a code owner June 30, 2026 21:27
@div-cowboy

Copy link
Copy Markdown
Author

I have signed the CLA!

div-cowboy added a commit to div-cowboy/hydrogen that referenced this pull request Jun 30, 2026
`pnpm dev` runs `@shopify/hydrogen#build` (one-shot, via turbo `^build`)
and then `@shopify/hydrogen#dev` (`tsdown --watch`). The watch's default
startup clean wipes the just-built `dist/` while the example bundlers are
reading it, spewing transient "Module not found: Can't resolve
'@shopify/hydrogen'" at every example.

Disable clean in `--watch` mode only: `dist/` stays populated from the
build, rolldown writes each output atomically, so example bundlers always
resolve a complete module. Production builds still clean; SDK hot reload
is preserved (verified: edit src -> tsdown rebuilds -> example reloads).

This replaces the dev-script workaround from Shopify#3826 (which removed the
watch and so lost SDK hot reload).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@div-cowboy

Copy link
Copy Markdown
Author

Superseded by #3825. That PR fixes the same pnpm dev "Module not found" race at its root — @shopify/hydrogen's tsdown --watch was cleaning dist/ on startup while example bundlers read it — by skipping the clean in watch mode. That keeps the SDK watch (and hot reload) running, whereas this PR removed the watch from the dev scripts and lost hot reload. Folding both the render fix and the race fix into #3825 so a fresh clone works in one PR.

@div-cowboy div-cowboy closed this Jun 30, 2026
div-cowboy added a commit to div-cowboy/hydrogen that referenced this pull request Jul 31, 2026
`pnpm dev` runs `@shopify/hydrogen#build` (one-shot, via turbo `^build`)
and then `@shopify/hydrogen#dev` (`tsdown --watch`). The watch's default
startup clean wipes the just-built `dist/` while the example bundlers are
reading it, spewing transient "Module not found: Can't resolve
'@shopify/hydrogen'" at every example.

Disable clean in `--watch` mode only: `dist/` stays populated from the
build, rolldown writes each output atomically, so example bundlers always
resolve a complete module. Production builds still clean; SDK hot reload
is preserved (verified: edit src -> tsdown rebuilds -> example reloads).

This replaces the dev-script workaround from Shopify#3826 (which removed the
watch and so lost SDK hot reload).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant