Skip to content

Incorporate the v3_routeConfig future flag for Remix.#2722

Merged
seanparsons merged 35 commits into
mainfrom
refactor/v3_routeconfig_flag
Mar 24, 2025
Merged

Incorporate the v3_routeConfig future flag for Remix.#2722
seanparsons merged 35 commits into
mainfrom
refactor/v3_routeconfig_flag

Conversation

@seanparsons

@seanparsons seanparsons commented Jan 23, 2025

Copy link
Copy Markdown
Contributor

Upgrade instructions

Please refer to the Remix documentation for more details on v3_routeConfig future flag: https://remix.run/docs/en/main/start/future-flags#v3_routeconfig

  1. Update your vite.config.ts.

    export default defineConfig({
      plugins: [
        hydrogen(),
        oxygen(),
        remix({
    -      presets: [hydrogen.preset()],
    +      presets: [hydrogen.v3preset()],
        future: {
          v3_fetcherPersist: true,
          v3_relativeSplatPath: true,
          v3_throwAbortReason: true,
          v3_lazyRouteDiscovery: true,
          v3_singleFetch: true,
    +      v3_routeConfig: true,
        },
      }),
      tsconfigPaths(),
    ],
  2. Update your package.json and install the new packages. Make sure to match the Remix version along with other Remix npm packages and ensure the versions are 2.16.1 or above:

      "devDependencies": {
        "@remix-run/dev": "^2.16.1",
    +    "@remix-run/fs-routes": "^2.16.1",
    +    "@remix-run/route-config": "^2.16.1",
  3. Add a routes.ts file. This is your new Remix route configuration file.

    import {flatRoutes} from '@remix-run/fs-routes';
    import {type RouteConfig} from '@remix-run/route-config';
    import {hydrogenRoutes} from '@shopify/hydrogen';
    
    export default hydrogenRoutes([
      ...(await flatRoutes()),
      // Manual route definitions can be added to this array, in addition to or instead of using the `flatRoutes` file-based routing convention.
      // See https://remix.run/docs/en/main/guides/routing for more details
    ]) satisfies RouteConfig;

WHY are these changes introduced?

These changes are to support an upgrade to the next version of Remix, by using the future flag and supporting libraries to get the code ready for that upgrade in place before the upgrade itself happens.

WHAT is this pull request doing?

  • Added the v3_routeConfig flag set to true.
  • Created a hydrogen.v3preset function which excludes the routes attribute that the hydrogen.preset function includes because that is incompatible with the flag being turned on.
  • Added a hydrogenRoutes function which transforms the route specified in routes.ts to include the virtual routes.
  • Rewrote getVirtualRoutes to work without using any node libraries so that it can run in MiniOxygen.
  • Updated the example projects to have them use the flag and the changes necessary to use the flag.
  • Set the LANG environment variable in the cli package so that the date formatting uses the US formatting.
  • build.test.ts now closes resources so that the test doesn't fail due to the hanging file watchers.
  • build.ts sets the server.watch configuration option as that may be necessary to disable the file watching.
  • hydrogen/dev.ts shuts down the watcher on the viteServer when finishing up.
  • setup.test.ts makes sure to include the routes.ts file.
  • classic-compiler/dev.ts makes sure to filter out the virtual routes when disableVirtualRoutes is true.
  • remix-config.ts constructs a value routesViteNodeContext which is necessary when resolving the config if v3_routeConfig is enabled.
  • route-validator.ts is now null safe when assigning currentRoute.path.
  • get-virtual-routes.test.ts now checks for the jsx files instead of the tsx files of the virtual routes.
  • Patched @remix-run/dev to not watch in remixVitePlugin when in a test because the file watchers cannot be reached to switch them off which causes tests relying on that to fail.
  • Patched vite to handle the watch properties of the config to set it if the value is null as that has meaning in that particular case.

HOW to test your changes?

  • Follow the steps as described in https://remix.run/docs/en/main/start/future-flags#v3_routeconfig to bring your project in line with the expectations therein.
  • Wrap the routes in routes.ts with the hydrogenRoutes function, to add the supporting routes.
  • Change the hydrogen preset from hydrogen.preset() to hydrogen.v3preset().

Post-merge steps

Checklist

  • I've read the Contributing Guidelines
  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've added a changeset if this PR contains user-facing or noteworthy changes
  • I've added tests to cover my changes
  • I've added or updated the documentation
Amended changes

The following changes were reverted/amended in #2829 .

  1. Move the Layout component export from root.tsx into its own file. Make sure to supply an <Outlet> so Remix knows where to inject your route content.

    // /app/layout.tsx
    import {Outlet} from '@remix-run/react';
    
    export default function Layout() {
      const nonce = useNonce();
      const data = useRouteLoaderData<RootLoader>('root');
    
      return (
        <html lang="en">
          ...
          <Outlet />
          ...
        </html>
      );
    }
    
    // Remember to remove the Layout export from your root.tsx
  2. Add a routes.ts file. This is your new Remix route configuration file.

    import { flatRoutes } from "@remix-run/fs-routes";
    import { layout, type RouteConfig } from "@remix-run/route-config";
    import { hydrogenRoutes } from "@shopify/hydrogen";
    
    export default hydrogenRoutes([
      // Your entire app reading from routes folder using Layout from layout.tsx
      layout("./layout.tsx", await flatRoutes()),
    ]) satisfies RouteConfig;
  • common.ts now makes sure to include routes.ts and layout.tsx.
  • local.test.ts slightly tweaked to make it easier to find out what files are missing and also now correctly takes account of the content that was shifted from root.tsx to layout.tsx.
  • replacers.ts looks for layout instead of root.

@shopify

shopify Bot commented Jan 23, 2025

Copy link
Copy Markdown
Contributor

Oxygen deployed a preview of your refactor/v3_routeconfig_flag branch. Details:

Storefront Status Preview link Deployment details Last update (UTC)
third-party-queries-caching ✅ Successful (Logs) Preview deployment Inspect deployment March 21, 2025 9:10 PM
Skeleton (skeleton.hydrogen.shop) ✅ Successful (Logs) Preview deployment Inspect deployment March 21, 2025 9:10 PM
metaobjects ✅ Successful (Logs) Preview deployment Inspect deployment March 21, 2025 9:10 PM
classic-remix ✅ Successful (Logs) Preview deployment Inspect deployment March 21, 2025 9:10 PM
custom-cart-method ✅ Successful (Logs) Preview deployment Inspect deployment March 21, 2025 9:10 PM

Learn more about Hydrogen's GitHub integration.

@seanparsons
seanparsons marked this pull request as ready for review February 11, 2025 17:58
Comment thread .changeset/spicy-drinks-hang.md Outdated

2. In the Vite config (`vite.config.ts` usually) the `remix` plugin needs to have it's configuration slightly altered.

From this:

@juanpprieto juanpprieto Feb 18, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would wrap this instruction on a diff -/+ block instead

@juanpprieto juanpprieto left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great Sean!

Tophat found no issues

Comment thread templates/skeleton/app/layout.tsx
Comment thread templates/skeleton/package.json Outdated
"dependencies": {
"@remix-run/react": "^2.15.3",
"@remix-run/server-runtime": "^2.15.3",
"@remix-run/fs-routes": "^2.15.3",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A reminder that we will need to add these new dependencies to our changelog.json entry when the time comes to release this

@wizardlyhel

Copy link
Copy Markdown

Updated this PR so it's just a single patch on @remix-run/dev
Related issue: remix-run/remix#10324

@wizardlyhel wizardlyhel mentioned this pull request Mar 17, 2025
5 tasks
Helen Lin and others added 2 commits March 20, 2025 16:59
@wizardlyhel

Copy link
Copy Markdown

/snapit

@github-actions

Copy link
Copy Markdown
Contributor

🫰✨ Thanks @wizardlyhel! Your snapshots have been published to npm.

Test the snapshots by updating your package.json with the newly published versions:

"@shopify/cli-hydrogen": "0.0.0-snapshot-20250321172553",
"@shopify/hydrogen": "0.0.0-snapshot-20250321172553",
"@shopify/hydrogen-codegen": "0.0.0-snapshot-20250321172553",
"@shopify/mini-oxygen": "0.0.0-snapshot-20250321172553",
"@shopify/remix-oxygen": "0.0.0-snapshot-20250321172553"

Create a new project with all the released packages running npm create @shopify/hydrogen@<snapshot_version>
To try a new CLI plugin version, add @shopify/cli-hydrogen as a dependency to your project using the snapshot version.

@wizardlyhel

Copy link
Copy Markdown

/snapit

@github-actions

Copy link
Copy Markdown
Contributor

🫰✨ Thanks @wizardlyhel! Your snapshots have been published to npm.

Test the snapshots by updating your package.json with the newly published versions:

"@shopify/cli-hydrogen": "0.0.0-snapshot-20250321203919",
"@shopify/hydrogen": "0.0.0-snapshot-20250321203919",
"@shopify/hydrogen-codegen": "0.0.0-snapshot-20250321203919",
"@shopify/mini-oxygen": "0.0.0-snapshot-20250321203919",
"@shopify/remix-oxygen": "0.0.0-snapshot-20250321203919"

Create a new project with all the released packages running npm create @shopify/hydrogen@<snapshot_version>
To try a new CLI plugin version, add @shopify/cli-hydrogen as a dependency to your project using the snapshot version.

@wizardlyhel

Copy link
Copy Markdown

/snapit

@github-actions

Copy link
Copy Markdown
Contributor

🫰✨ Thanks @wizardlyhel! Your snapshots have been published to npm.

Test the snapshots by updating your package.json with the newly published versions:

"@shopify/cli-hydrogen": "0.0.0-snapshot-20250321210338",
"@shopify/hydrogen": "0.0.0-snapshot-20250321210338",
"@shopify/hydrogen-codegen": "0.0.0-snapshot-20250321210338",
"@shopify/mini-oxygen": "0.0.0-snapshot-20250321210338",
"@shopify/remix-oxygen": "0.0.0-snapshot-20250321210338"

Create a new project with all the released packages running npm create @shopify/hydrogen@<snapshot_version>
To try a new CLI plugin version, add @shopify/cli-hydrogen as a dependency to your project using the snapshot version.

@seanparsons
seanparsons merged commit 74ef1ba into main Mar 24, 2025
@seanparsons
seanparsons deleted the refactor/v3_routeconfig_flag branch March 24, 2025 16:39
@jasonloeve

Copy link
Copy Markdown

@wizardlyhel , I may have missed it but moving the layout out of root along with imports we loose the ability to have styled error pages / error messages wrapped in layout ?

@rbshop

rbshop commented Apr 1, 2025

Copy link
Copy Markdown
Contributor

@jasonloeve Looks like this was done in error whilst attempting to clean up the code. We'll revert this and cut a new release ASAP. Thanks for the heads up - edit: I was wrong to say this was to clean up code, it was actually done to support some virtual routes that are exposed during local dev. Either way, we'll get this fixed ASAP

@ruggishop ruggishop mentioned this pull request Apr 1, 2025
5 tasks
juanpprieto pushed a commit that referenced this pull request Sep 17, 2025
* WIP adding the v3_routeConfig flag.

* WIP testing the example projects.

* WIP Trying to diagnose the skeleton project startup failure.

* Reimplemented virtual routes to build paths without node libraries.

* Removed change to multipass project.

* Multipass example now uses v3preset.

* Fix virtual route layout

* fix the ordering of virtual routes

* CLI test fixes.

* Working unit tests.

* Additional patch to get the tests working correctly.

* Fixing eslint errors that arose after the merge.

* Applied prettier fixes to virtual-root.tsx.

* Added changeset for v3_routeConfig.

* Removed change to docs page because it's unnecessary.

* Update so that there is only 1 patch on remix-run/dev

* fix package lock

* remove patch

* fix vite import

* format

* clean up changeset

* Fix test

* Fix Prettier failure.

* fix virtual route without routeConfig flag

* format

* Fix test

* fix test again

* fix dev mode

* fix test again

---------

Co-authored-by: Helen Lin <helen.lin@shopify.com>
frandiox added a commit that referenced this pull request Mar 24, 2026
Three bugs were preventing HMR from working after the migration to
Vite's Environment API:

1. `server.watch: null` (accidentally introduced in #2722) caused Vite
   to create a NoopWatcher, so no file-change events were ever emitted.
   Removed it to restore chokidar.

2. The SSR environment was passed `transport: context.ws` (the same
   WebSocket server the browser uses). Because `isWebSocketServer` is
   true on that object, Vite used it directly as the SSR environment's
   hot channel. When the SSR module graph found no React Fast Refresh
   boundaries (SSR code has none), it sent `full-reload` over that
   shared WebSocket — hard-refreshing the browser on every file change.
   Dropping the transport gives the SSR environment a noop hot channel
   so those events are silently discarded.

3. `@vite/client` was never injected into HTML responses from workerd,
   so the browser never established a WebSocket connection to Vite's
   HMR server. React Router's <Scripts /> already injects the
   inject-hmr-runtime preamble; the only missing piece was @vite/client.
   Now injected via a simple string replace on HTML responses.

Server-side invalidation (route handlers, server.ts) is handled by a
new watcher listener in environment.ts: when a file in the SSR module
graph changes, the mini-oxygen instance is disposed so the next request
starts a fresh workerd with a clean module-runner cache.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
fredericoo pushed a commit that referenced this pull request Apr 8, 2026
Three bugs were preventing HMR from working after the migration to
Vite's Environment API:

1. `server.watch: null` (accidentally introduced in #2722) caused Vite
   to create a NoopWatcher, so no file-change events were ever emitted.
   Removed it to restore chokidar.

2. The SSR environment was passed `transport: context.ws` (the same
   WebSocket server the browser uses). Because `isWebSocketServer` is
   true on that object, Vite used it directly as the SSR environment's
   hot channel. When the SSR module graph found no React Fast Refresh
   boundaries (SSR code has none), it sent `full-reload` over that
   shared WebSocket — hard-refreshing the browser on every file change.
   Dropping the transport gives the SSR environment a noop hot channel
   so those events are silently discarded.

3. `@vite/client` was never injected into HTML responses from workerd,
   so the browser never established a WebSocket connection to Vite's
   HMR server. React Router's <Scripts /> already injects the
   inject-hmr-runtime preamble; the only missing piece was @vite/client.
   Now injected via a simple string replace on HTML responses.

Server-side invalidation (route handlers, server.ts) is handled by a
new watcher listener in environment.ts: when a file in the SSR module
graph changes, the mini-oxygen instance is disposed so the next request
starts a fresh workerd with a clean module-runner cache.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
fredericoo pushed a commit that referenced this pull request Apr 15, 2026
Three bugs were preventing HMR from working after the migration to
Vite's Environment API:

1. `server.watch: null` (accidentally introduced in #2722) caused Vite
   to create a NoopWatcher, so no file-change events were ever emitted.
   Removed it to restore chokidar.

2. The SSR environment was passed `transport: context.ws` (the same
   WebSocket server the browser uses). Because `isWebSocketServer` is
   true on that object, Vite used it directly as the SSR environment's
   hot channel. When the SSR module graph found no React Fast Refresh
   boundaries (SSR code has none), it sent `full-reload` over that
   shared WebSocket — hard-refreshing the browser on every file change.
   Dropping the transport gives the SSR environment a noop hot channel
   so those events are silently discarded.

3. `@vite/client` was never injected into HTML responses from workerd,
   so the browser never established a WebSocket connection to Vite's
   HMR server. React Router's <Scripts /> already injects the
   inject-hmr-runtime preamble; the only missing piece was @vite/client.
   Now injected via a simple string replace on HTML responses.

Server-side invalidation (route handlers, server.ts) is handled by a
new watcher listener in environment.ts: when a file in the SSR module
graph changes, the mini-oxygen instance is disposed so the next request
starts a fresh workerd with a clean module-runner cache.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
fredericoo added a commit that referenced this pull request Apr 24, 2026
* refactor: move mini-oxygen dev runtime to Vite 8 environments

Create a fetchable mini-oxygen SSR environment using Vite 8's environment APIs and move runtime lifecycle management into a dedicated environment module.

Keep Hydrogen and CLI option passing compatible via registerPluginOptions while routing dev requests through server.environments.ssr and preserving build-time compatibility_date handling.

Update the custom workerd module runner bridge for Vite 8's invoke contract, including getBuiltins support and fetchModule options forwarding, so local SSR still runs inside MiniOxygen.

Upgrade workspace Vite dependencies to v8, switch example/template configs to native resolve.tsconfigPaths, and add the environment API refactor plan document.

Co-authored-by: Codex <codex@openai.com>

* refactor: delegate mini-oxygen runner invokes to Vite

Replace mini-oxygen's custom /__vite_fetch_module bridge with a service binding that forwards Vite runner invoke payloads to the SSR environment's built-in handleInvoke() path.

This removes bespoke request parsing and builtins serialization while keeping the current fetchable SSR environment and Hydrogen/CLI runtime option flow intact.

Co-authored-by: Codex <codex@openai.com>

* Simplify Vite 8 cleanup around MiniOxygen

Drop the leftover unused Hydrogen plugin lookup in dev, and switch MiniOxygen and bundle analyzer asset emission to emitFile() so Vite 8/Rolldown no longer warns about mutating the bundle object.

Co-authored-by: OpenAI Codex <codex@openai.com>

* Fix Vite 8 CLI typecheck fallout

Align CLI internal MiniOxygen typing with monorepo source types, make the bundle analyzer tolerate Vite 8 and Rolldown RenderedModule typing, and fix MiniOxygen proxy port parsing strictness.

Co-authored-by: OpenAI Codex <codex@openai.com>

* Remove unused MiniOxygen Vite root binding

Drop the leftover __VITE_ROOT runtime binding from the MiniOxygen Vite worker bridge now that the Vite 8 Environment API path no longer uses it.

Co-authored-by: OpenAI Codex <codex@openai.com>

* fix: harden mini-oxygen environment lifecycle

Throw on late runtime reconfiguration, remove the refactor plan doc, and add coverage for MiniOxygen's Vite environment lifecycle.

Co-authored-by: OpenAI Codex <codex@openai.com>

* Fix CLI tests for Vite 8 compatibility

- Upgrade vitest from ^1.0.4 to ^3.2.4 in packages/cli (only package still on v1, which only supports Vite ^5)
- Update build test assertions to match Vite 8 renamed log messages

Co-Authored-By: Claude <noreply@anthropic.com>

* Support Vite 6

* fix: restore HMR in Vite Environment API setup

Three bugs were preventing HMR from working after the migration to
Vite's Environment API:

1. `server.watch: null` (accidentally introduced in #2722) caused Vite
   to create a NoopWatcher, so no file-change events were ever emitted.
   Removed it to restore chokidar.

2. The SSR environment was passed `transport: context.ws` (the same
   WebSocket server the browser uses). Because `isWebSocketServer` is
   true on that object, Vite used it directly as the SSR environment's
   hot channel. When the SSR module graph found no React Fast Refresh
   boundaries (SSR code has none), it sent `full-reload` over that
   shared WebSocket — hard-refreshing the browser on every file change.
   Dropping the transport gives the SSR environment a noop hot channel
   so those events are silently discarded.

3. `@vite/client` was never injected into HTML responses from workerd,
   so the browser never established a WebSocket connection to Vite's
   HMR server. React Router's <Scripts /> already injects the
   inject-hmr-runtime preamble; the only missing piece was @vite/client.
   Now injected via a simple string replace on HTML responses.

Server-side invalidation (route handlers, server.ts) is handled by a
new watcher listener in environment.ts: when a file in the SSR module
graph changes, the mini-oxygen instance is disposed so the next request
starts a fresh workerd with a clean module-runner cache.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* refactor: simplify mini-oxygen Vite HMR wiring

Remove redundant MiniOxygen watcher and HTML injection cleanup while keeping the SSR environment request-driven.

Co-authored-by: Codex <codex@openai.com>

* refactor: trim mini-oxygen environment plumbing

Remove the custom MiniOxygen environment marker/getter and read the entrypoint error handler from plugin state instead.

Co-authored-by: Codex <codex@openai.com>

* docs: clarify mini-oxygen Vite runtime behavior

Update MiniOxygen Vite runtime comments to reflect the current request-driven server refresh model and inspector behavior.

Co-authored-by: Codex <codex@openai.com>

* docs: clarify mini-oxygen environment lifecycle

Document the warmup timing and SSR environment replacement behavior in MiniOxygen's Vite environment integration.

Co-authored-by: Codex <codex@openai.com>

* fix: use accurate type cast for response body

Replace the double-cast `as unknown as BodyInit` with `as ReadableStream | null`,
which accurately describes the Miniflare Response.body type and avoids the
lossy intermediate cast through `unknown`.

* fix: extract magic number to WARMUP_SETTLE_DELAY_MS

Replace the bare `200` timeout with a named constant that communicates intent
at a glance. The delay lets Vite settle after listen/config reload before the
synthetic warmup request fires.

* chore: add TODO for Vite 6 getBuiltins shim removal

Mark the getBuiltins workaround so it's easy to find and remove once
Vite 6 support is dropped. Vite 7+ handles this internally.

* chore: changeset for mini-oxygen review feedback

Patch changeset covering the type cast improvement, magic number
extraction, and Vite 6 shim TODO marker.

* fix: correct vite compat range in changeset

Mini Oxygen requires Vite 6+ (Environment API didn't exist in Vite 5).
Only Hydrogen itself supports Vite 5+.

* fix: bump react-router from 7.12.0 to 7.14.0

React Router 7.14.0 adds Vite 8 support by switching from the
deprecated `esbuild` config API to `oxc`. This eliminates the
deprecation warning:

  `esbuild` option was specified by "react-router" plugin.
  This option is deprecated, please use `oxc` instead.

Also adds `unstable_mask` to mock Location objects in test files
to satisfy the new property added to react-router's Location type
in 7.14.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): regenerate recipes for Vite 8 skeleton changes

The Vite 8 migration changed the skeleton template (react-router 7.14.0,
vite ^8.0.1, removed vite-tsconfig-paths, updated vite.config.ts structure).
All recipe patches referencing old skeleton state needed regeneration.

Regenerated 13 recipes: b2b, bundles, combined-listings, custom-cart-method,
gtm, infinite-scroll, legacy-customer-account-flow, markets, metaobjects,
multipass, partytown, subscriptions, third-party-api.

The express recipe was NOT regenerated — its vite.config.ts patch references
removed imports (oxygen(), tsconfigPaths) and requires a full rewrite.
No E2E test covers it, so it's not blocking CI.

For multipass and partytown, the standard `regenerate` command fails because
withResolvedCatalog() resolves workspace:* before patch application, breaking
context matching. Worked around by calling applyRecipe() directly (bypassing
catalog resolution), then running generate and render separately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): update multipass and partytown vite.config.ts patches for optimizeDeps format

The merge from main introduced the nested dependency syntax in
ssr.optimizeDeps.include (e.g., 'react-router > set-cookie-parser').
The multipass and partytown recipe patches still referenced the old
single-line format, causing E2E test failures when applying patches
to the updated skeleton.

Manually rewrote both patches to match the current skeleton's
multi-line include array format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): correct hunk headers in multipass and partytown patches

The previous patch fix had an off-by-one in the @@ hunk header — the
first context line started at line 29 but the header claimed line 28.
This caused `patch` to apply with an offset, creating .orig backup
files, which the apply code treats as a conflict.

Fixed by including the `include: [` line (line 28) as leading context
so the hunk header accurately matches the file content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): regenerate multipass/partytown vite.config.ts patches via git diff

The previous patches were hand-edited across three commits (e396456,
862f005, 6b79693) to keep up with skeleton refactors. Hand-edits
dropped the git `index` header line, producing patches that BSD patch
accepted but GNU patch on Ubuntu CI applied with fuzz — creating `.orig`
files that cookbook's apply logic (`apply.ts:148`) treats as fatal. The
three failing e2e multipass specs all cascaded from this single fixture
generation failure.

Regenerated both patches via `pnpm cookbook generate --filePath
templates/skeleton/vite.config.ts --onlyFiles`, which uses `git diff`
under the hood and always produces a well-formed unified diff with the
`index` header restored. Verified locally under GNU patch 2.7.6 on
Ubuntu 24.04 (podman): both patches apply with zero offset, no fuzz, no
`.orig`/`.rej`.

README and LLM-prompt files re-rendered via `pnpm cookbook render` so
the diff shown in docs matches the committed patch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(cli): regenerate oclif manifest after main merge

Picks up the `uncommited → uncommitted` typo fix from main's
packages/cli/src/commands/hydrogen/deploy.ts. The manifest is a
derived artifact that must stay in sync with the source; CI's
manifest-drift guard flagged the mismatch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(changelog): add missing devDependencies field to 2026.4.1 entry

Main's packages/cli/src/commands/hydrogen/changelog-schema.test.ts
(added in #3630) asserts every release has a .devDependencies field.
The 2026.4.1 entry (added in #3731) was missing it, causing the unit
test to fail post-merge.

PR #3721 didn't actually change any skeleton devDependencies between
2026.4.0 and 2026.4.1 — only version strings were bumped. An empty
object accurately reflects that and satisfies the schema with zero
impact on merchant upgrade behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove not needed statement from changeset

we previously mentioned vite 6 as a requirement for CLI and hydrogen-react but they dont actually bundle vite at all

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: freddie <freddie.batista@shopify.com>
Co-authored-by: ✦ freddie <45042736+fredericoo@users.noreply.github.com>
frandiox pushed a commit that referenced this pull request Jun 16, 2026
* WIP adding the v3_routeConfig flag.

* WIP testing the example projects.

* WIP Trying to diagnose the skeleton project startup failure.

* Reimplemented virtual routes to build paths without node libraries.

* Removed change to multipass project.

* Multipass example now uses v3preset.

* Fix virtual route layout

* fix the ordering of virtual routes

* CLI test fixes.

* Working unit tests.

* Additional patch to get the tests working correctly.

* Fixing eslint errors that arose after the merge.

* Applied prettier fixes to virtual-root.tsx.

* Added changeset for v3_routeConfig.

* Removed change to docs page because it's unnecessary.

* Update so that there is only 1 patch on remix-run/dev

* fix package lock

* remove patch

* fix vite import

* format

* clean up changeset

* Fix test

* Fix Prettier failure.

* fix virtual route without routeConfig flag

* format

* Fix test

* fix test again

* fix dev mode

* fix test again

---------

Co-authored-by: Helen Lin <helen.lin@shopify.com>
frandiox added a commit that referenced this pull request Jun 16, 2026
* refactor: move mini-oxygen dev runtime to Vite 8 environments

Create a fetchable mini-oxygen SSR environment using Vite 8's environment APIs and move runtime lifecycle management into a dedicated environment module.

Keep Hydrogen and CLI option passing compatible via registerPluginOptions while routing dev requests through server.environments.ssr and preserving build-time compatibility_date handling.

Update the custom workerd module runner bridge for Vite 8's invoke contract, including getBuiltins support and fetchModule options forwarding, so local SSR still runs inside MiniOxygen.

Upgrade workspace Vite dependencies to v8, switch example/template configs to native resolve.tsconfigPaths, and add the environment API refactor plan document.

Co-authored-by: Codex <codex@openai.com>

* refactor: delegate mini-oxygen runner invokes to Vite

Replace mini-oxygen's custom /__vite_fetch_module bridge with a service binding that forwards Vite runner invoke payloads to the SSR environment's built-in handleInvoke() path.

This removes bespoke request parsing and builtins serialization while keeping the current fetchable SSR environment and Hydrogen/CLI runtime option flow intact.

Co-authored-by: Codex <codex@openai.com>

* Simplify Vite 8 cleanup around MiniOxygen

Drop the leftover unused Hydrogen plugin lookup in dev, and switch MiniOxygen and bundle analyzer asset emission to emitFile() so Vite 8/Rolldown no longer warns about mutating the bundle object.

Co-authored-by: OpenAI Codex <codex@openai.com>

* Fix Vite 8 CLI typecheck fallout

Align CLI internal MiniOxygen typing with monorepo source types, make the bundle analyzer tolerate Vite 8 and Rolldown RenderedModule typing, and fix MiniOxygen proxy port parsing strictness.

Co-authored-by: OpenAI Codex <codex@openai.com>

* Remove unused MiniOxygen Vite root binding

Drop the leftover __VITE_ROOT runtime binding from the MiniOxygen Vite worker bridge now that the Vite 8 Environment API path no longer uses it.

Co-authored-by: OpenAI Codex <codex@openai.com>

* fix: harden mini-oxygen environment lifecycle

Throw on late runtime reconfiguration, remove the refactor plan doc, and add coverage for MiniOxygen's Vite environment lifecycle.

Co-authored-by: OpenAI Codex <codex@openai.com>

* Fix CLI tests for Vite 8 compatibility

- Upgrade vitest from ^1.0.4 to ^3.2.4 in packages/cli (only package still on v1, which only supports Vite ^5)
- Update build test assertions to match Vite 8 renamed log messages

Co-Authored-By: Claude <noreply@anthropic.com>

* Support Vite 6

* fix: restore HMR in Vite Environment API setup

Three bugs were preventing HMR from working after the migration to
Vite's Environment API:

1. `server.watch: null` (accidentally introduced in #2722) caused Vite
   to create a NoopWatcher, so no file-change events were ever emitted.
   Removed it to restore chokidar.

2. The SSR environment was passed `transport: context.ws` (the same
   WebSocket server the browser uses). Because `isWebSocketServer` is
   true on that object, Vite used it directly as the SSR environment's
   hot channel. When the SSR module graph found no React Fast Refresh
   boundaries (SSR code has none), it sent `full-reload` over that
   shared WebSocket — hard-refreshing the browser on every file change.
   Dropping the transport gives the SSR environment a noop hot channel
   so those events are silently discarded.

3. `@vite/client` was never injected into HTML responses from workerd,
   so the browser never established a WebSocket connection to Vite's
   HMR server. React Router's <Scripts /> already injects the
   inject-hmr-runtime preamble; the only missing piece was @vite/client.
   Now injected via a simple string replace on HTML responses.

Server-side invalidation (route handlers, server.ts) is handled by a
new watcher listener in environment.ts: when a file in the SSR module
graph changes, the mini-oxygen instance is disposed so the next request
starts a fresh workerd with a clean module-runner cache.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* refactor: simplify mini-oxygen Vite HMR wiring

Remove redundant MiniOxygen watcher and HTML injection cleanup while keeping the SSR environment request-driven.

Co-authored-by: Codex <codex@openai.com>

* refactor: trim mini-oxygen environment plumbing

Remove the custom MiniOxygen environment marker/getter and read the entrypoint error handler from plugin state instead.

Co-authored-by: Codex <codex@openai.com>

* docs: clarify mini-oxygen Vite runtime behavior

Update MiniOxygen Vite runtime comments to reflect the current request-driven server refresh model and inspector behavior.

Co-authored-by: Codex <codex@openai.com>

* docs: clarify mini-oxygen environment lifecycle

Document the warmup timing and SSR environment replacement behavior in MiniOxygen's Vite environment integration.

Co-authored-by: Codex <codex@openai.com>

* fix: use accurate type cast for response body

Replace the double-cast `as unknown as BodyInit` with `as ReadableStream | null`,
which accurately describes the Miniflare Response.body type and avoids the
lossy intermediate cast through `unknown`.

* fix: extract magic number to WARMUP_SETTLE_DELAY_MS

Replace the bare `200` timeout with a named constant that communicates intent
at a glance. The delay lets Vite settle after listen/config reload before the
synthetic warmup request fires.

* chore: add TODO for Vite 6 getBuiltins shim removal

Mark the getBuiltins workaround so it's easy to find and remove once
Vite 6 support is dropped. Vite 7+ handles this internally.

* chore: changeset for mini-oxygen review feedback

Patch changeset covering the type cast improvement, magic number
extraction, and Vite 6 shim TODO marker.

* fix: correct vite compat range in changeset

Mini Oxygen requires Vite 6+ (Environment API didn't exist in Vite 5).
Only Hydrogen itself supports Vite 5+.

* fix: bump react-router from 7.12.0 to 7.14.0

React Router 7.14.0 adds Vite 8 support by switching from the
deprecated `esbuild` config API to `oxc`. This eliminates the
deprecation warning:

  `esbuild` option was specified by "react-router" plugin.
  This option is deprecated, please use `oxc` instead.

Also adds `unstable_mask` to mock Location objects in test files
to satisfy the new property added to react-router's Location type
in 7.14.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): regenerate recipes for Vite 8 skeleton changes

The Vite 8 migration changed the skeleton template (react-router 7.14.0,
vite ^8.0.1, removed vite-tsconfig-paths, updated vite.config.ts structure).
All recipe patches referencing old skeleton state needed regeneration.

Regenerated 13 recipes: b2b, bundles, combined-listings, custom-cart-method,
gtm, infinite-scroll, legacy-customer-account-flow, markets, metaobjects,
multipass, partytown, subscriptions, third-party-api.

The express recipe was NOT regenerated — its vite.config.ts patch references
removed imports (oxygen(), tsconfigPaths) and requires a full rewrite.
No E2E test covers it, so it's not blocking CI.

For multipass and partytown, the standard `regenerate` command fails because
withResolvedCatalog() resolves workspace:* before patch application, breaking
context matching. Worked around by calling applyRecipe() directly (bypassing
catalog resolution), then running generate and render separately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): update multipass and partytown vite.config.ts patches for optimizeDeps format

The merge from main introduced the nested dependency syntax in
ssr.optimizeDeps.include (e.g., 'react-router > set-cookie-parser').
The multipass and partytown recipe patches still referenced the old
single-line format, causing E2E test failures when applying patches
to the updated skeleton.

Manually rewrote both patches to match the current skeleton's
multi-line include array format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): correct hunk headers in multipass and partytown patches

The previous patch fix had an off-by-one in the @@ hunk header — the
first context line started at line 29 but the header claimed line 28.
This caused `patch` to apply with an offset, creating .orig backup
files, which the apply code treats as a conflict.

Fixed by including the `include: [` line (line 28) as leading context
so the hunk header accurately matches the file content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): regenerate multipass/partytown vite.config.ts patches via git diff

The previous patches were hand-edited across three commits (e396456,
862f005, 6b79693) to keep up with skeleton refactors. Hand-edits
dropped the git `index` header line, producing patches that BSD patch
accepted but GNU patch on Ubuntu CI applied with fuzz — creating `.orig`
files that cookbook's apply logic (`apply.ts:148`) treats as fatal. The
three failing e2e multipass specs all cascaded from this single fixture
generation failure.

Regenerated both patches via `pnpm cookbook generate --filePath
templates/skeleton/vite.config.ts --onlyFiles`, which uses `git diff`
under the hood and always produces a well-formed unified diff with the
`index` header restored. Verified locally under GNU patch 2.7.6 on
Ubuntu 24.04 (podman): both patches apply with zero offset, no fuzz, no
`.orig`/`.rej`.

README and LLM-prompt files re-rendered via `pnpm cookbook render` so
the diff shown in docs matches the committed patch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(cli): regenerate oclif manifest after main merge

Picks up the `uncommited → uncommitted` typo fix from main's
packages/cli/src/commands/hydrogen/deploy.ts. The manifest is a
derived artifact that must stay in sync with the source; CI's
manifest-drift guard flagged the mismatch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(changelog): add missing devDependencies field to 2026.4.1 entry

Main's packages/cli/src/commands/hydrogen/changelog-schema.test.ts
(added in #3630) asserts every release has a .devDependencies field.
The 2026.4.1 entry (added in #3731) was missing it, causing the unit
test to fail post-merge.

PR #3721 didn't actually change any skeleton devDependencies between
2026.4.0 and 2026.4.1 — only version strings were bumped. An empty
object accurately reflects that and satisfies the schema with zero
impact on merchant upgrade behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove not needed statement from changeset

we previously mentioned vite 6 as a requirement for CLI and hydrogen-react but they dont actually bundle vite at all

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: freddie <freddie.batista@shopify.com>
Co-authored-by: ✦ freddie <45042736+fredericoo@users.noreply.github.com>
marcospassos pushed a commit to croct-tech/croct-hydrogen-project that referenced this pull request Jul 16, 2026
* WIP adding the v3_routeConfig flag.

* WIP testing the example projects.

* WIP Trying to diagnose the skeleton project startup failure.

* Reimplemented virtual routes to build paths without node libraries.

* Removed change to multipass project.

* Multipass example now uses v3preset.

* Fix virtual route layout

* fix the ordering of virtual routes

* CLI test fixes.

* Working unit tests.

* Additional patch to get the tests working correctly.

* Fixing eslint errors that arose after the merge.

* Applied prettier fixes to virtual-root.tsx.

* Added changeset for v3_routeConfig.

* Removed change to docs page because it's unnecessary.

* Update so that there is only 1 patch on remix-run/dev

* fix package lock

* remove patch

* fix vite import

* format

* clean up changeset

* Fix test

* Fix Prettier failure.

* fix virtual route without routeConfig flag

* format

* Fix test

* fix test again

* fix dev mode

* fix test again

---------

Co-authored-by: Helen Lin <helen.lin@shopify.com>
marcospassos pushed a commit to croct-tech/croct-hydrogen-project that referenced this pull request Jul 16, 2026
* refactor: move mini-oxygen dev runtime to Vite 8 environments

Create a fetchable mini-oxygen SSR environment using Vite 8's environment APIs and move runtime lifecycle management into a dedicated environment module.

Keep Hydrogen and CLI option passing compatible via registerPluginOptions while routing dev requests through server.environments.ssr and preserving build-time compatibility_date handling.

Update the custom workerd module runner bridge for Vite 8's invoke contract, including getBuiltins support and fetchModule options forwarding, so local SSR still runs inside MiniOxygen.

Upgrade workspace Vite dependencies to v8, switch example/template configs to native resolve.tsconfigPaths, and add the environment API refactor plan document.

Co-authored-by: Codex <codex@openai.com>

* refactor: delegate mini-oxygen runner invokes to Vite

Replace mini-oxygen's custom /__vite_fetch_module bridge with a service binding that forwards Vite runner invoke payloads to the SSR environment's built-in handleInvoke() path.

This removes bespoke request parsing and builtins serialization while keeping the current fetchable SSR environment and Hydrogen/CLI runtime option flow intact.

Co-authored-by: Codex <codex@openai.com>

* Simplify Vite 8 cleanup around MiniOxygen

Drop the leftover unused Hydrogen plugin lookup in dev, and switch MiniOxygen and bundle analyzer asset emission to emitFile() so Vite 8/Rolldown no longer warns about mutating the bundle object.

Co-authored-by: OpenAI Codex <codex@openai.com>

* Fix Vite 8 CLI typecheck fallout

Align CLI internal MiniOxygen typing with monorepo source types, make the bundle analyzer tolerate Vite 8 and Rolldown RenderedModule typing, and fix MiniOxygen proxy port parsing strictness.

Co-authored-by: OpenAI Codex <codex@openai.com>

* Remove unused MiniOxygen Vite root binding

Drop the leftover __VITE_ROOT runtime binding from the MiniOxygen Vite worker bridge now that the Vite 8 Environment API path no longer uses it.

Co-authored-by: OpenAI Codex <codex@openai.com>

* fix: harden mini-oxygen environment lifecycle

Throw on late runtime reconfiguration, remove the refactor plan doc, and add coverage for MiniOxygen's Vite environment lifecycle.

Co-authored-by: OpenAI Codex <codex@openai.com>

* Fix CLI tests for Vite 8 compatibility

- Upgrade vitest from ^1.0.4 to ^3.2.4 in packages/cli (only package still on v1, which only supports Vite ^5)
- Update build test assertions to match Vite 8 renamed log messages

Co-Authored-By: Claude <noreply@anthropic.com>

* Support Vite 6

* fix: restore HMR in Vite Environment API setup

Three bugs were preventing HMR from working after the migration to
Vite's Environment API:

1. `server.watch: null` (accidentally introduced in Shopify#2722) caused Vite
   to create a NoopWatcher, so no file-change events were ever emitted.
   Removed it to restore chokidar.

2. The SSR environment was passed `transport: context.ws` (the same
   WebSocket server the browser uses). Because `isWebSocketServer` is
   true on that object, Vite used it directly as the SSR environment's
   hot channel. When the SSR module graph found no React Fast Refresh
   boundaries (SSR code has none), it sent `full-reload` over that
   shared WebSocket — hard-refreshing the browser on every file change.
   Dropping the transport gives the SSR environment a noop hot channel
   so those events are silently discarded.

3. `@vite/client` was never injected into HTML responses from workerd,
   so the browser never established a WebSocket connection to Vite's
   HMR server. React Router's <Scripts /> already injects the
   inject-hmr-runtime preamble; the only missing piece was @vite/client.
   Now injected via a simple string replace on HTML responses.

Server-side invalidation (route handlers, server.ts) is handled by a
new watcher listener in environment.ts: when a file in the SSR module
graph changes, the mini-oxygen instance is disposed so the next request
starts a fresh workerd with a clean module-runner cache.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>

* refactor: simplify mini-oxygen Vite HMR wiring

Remove redundant MiniOxygen watcher and HTML injection cleanup while keeping the SSR environment request-driven.

Co-authored-by: Codex <codex@openai.com>

* refactor: trim mini-oxygen environment plumbing

Remove the custom MiniOxygen environment marker/getter and read the entrypoint error handler from plugin state instead.

Co-authored-by: Codex <codex@openai.com>

* docs: clarify mini-oxygen Vite runtime behavior

Update MiniOxygen Vite runtime comments to reflect the current request-driven server refresh model and inspector behavior.

Co-authored-by: Codex <codex@openai.com>

* docs: clarify mini-oxygen environment lifecycle

Document the warmup timing and SSR environment replacement behavior in MiniOxygen's Vite environment integration.

Co-authored-by: Codex <codex@openai.com>

* fix: use accurate type cast for response body

Replace the double-cast `as unknown as BodyInit` with `as ReadableStream | null`,
which accurately describes the Miniflare Response.body type and avoids the
lossy intermediate cast through `unknown`.

* fix: extract magic number to WARMUP_SETTLE_DELAY_MS

Replace the bare `200` timeout with a named constant that communicates intent
at a glance. The delay lets Vite settle after listen/config reload before the
synthetic warmup request fires.

* chore: add TODO for Vite 6 getBuiltins shim removal

Mark the getBuiltins workaround so it's easy to find and remove once
Vite 6 support is dropped. Vite 7+ handles this internally.

* chore: changeset for mini-oxygen review feedback

Patch changeset covering the type cast improvement, magic number
extraction, and Vite 6 shim TODO marker.

* fix: correct vite compat range in changeset

Mini Oxygen requires Vite 6+ (Environment API didn't exist in Vite 5).
Only Hydrogen itself supports Vite 5+.

* fix: bump react-router from 7.12.0 to 7.14.0

React Router 7.14.0 adds Vite 8 support by switching from the
deprecated `esbuild` config API to `oxc`. This eliminates the
deprecation warning:

  `esbuild` option was specified by "react-router" plugin.
  This option is deprecated, please use `oxc` instead.

Also adds `unstable_mask` to mock Location objects in test files
to satisfy the new property added to react-router's Location type
in 7.14.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): regenerate recipes for Vite 8 skeleton changes

The Vite 8 migration changed the skeleton template (react-router 7.14.0,
vite ^8.0.1, removed vite-tsconfig-paths, updated vite.config.ts structure).
All recipe patches referencing old skeleton state needed regeneration.

Regenerated 13 recipes: b2b, bundles, combined-listings, custom-cart-method,
gtm, infinite-scroll, legacy-customer-account-flow, markets, metaobjects,
multipass, partytown, subscriptions, third-party-api.

The express recipe was NOT regenerated — its vite.config.ts patch references
removed imports (oxygen(), tsconfigPaths) and requires a full rewrite.
No E2E test covers it, so it's not blocking CI.

For multipass and partytown, the standard `regenerate` command fails because
withResolvedCatalog() resolves workspace:* before patch application, breaking
context matching. Worked around by calling applyRecipe() directly (bypassing
catalog resolution), then running generate and render separately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): update multipass and partytown vite.config.ts patches for optimizeDeps format

The merge from main introduced the nested dependency syntax in
ssr.optimizeDeps.include (e.g., 'react-router > set-cookie-parser').
The multipass and partytown recipe patches still referenced the old
single-line format, causing E2E test failures when applying patches
to the updated skeleton.

Manually rewrote both patches to match the current skeleton's
multi-line include array format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): correct hunk headers in multipass and partytown patches

The previous patch fix had an off-by-one in the @@ hunk header — the
first context line started at line 29 but the header claimed line 28.
This caused `patch` to apply with an offset, creating .orig backup
files, which the apply code treats as a conflict.

Fixed by including the `include: [` line (line 28) as leading context
so the hunk header accurately matches the file content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(cookbook): regenerate multipass/partytown vite.config.ts patches via git diff

The previous patches were hand-edited across three commits (e396456,
862f005, 6b79693) to keep up with skeleton refactors. Hand-edits
dropped the git `index` header line, producing patches that BSD patch
accepted but GNU patch on Ubuntu CI applied with fuzz — creating `.orig`
files that cookbook's apply logic (`apply.ts:148`) treats as fatal. The
three failing e2e multipass specs all cascaded from this single fixture
generation failure.

Regenerated both patches via `pnpm cookbook generate --filePath
templates/skeleton/vite.config.ts --onlyFiles`, which uses `git diff`
under the hood and always produces a well-formed unified diff with the
`index` header restored. Verified locally under GNU patch 2.7.6 on
Ubuntu 24.04 (podman): both patches apply with zero offset, no fuzz, no
`.orig`/`.rej`.

README and LLM-prompt files re-rendered via `pnpm cookbook render` so
the diff shown in docs matches the committed patch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore(cli): regenerate oclif manifest after main merge

Picks up the `uncommited → uncommitted` typo fix from main's
packages/cli/src/commands/hydrogen/deploy.ts. The manifest is a
derived artifact that must stay in sync with the source; CI's
manifest-drift guard flagged the mismatch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(changelog): add missing devDependencies field to 2026.4.1 entry

Main's packages/cli/src/commands/hydrogen/changelog-schema.test.ts
(added in Shopify#3630) asserts every release has a .devDependencies field.
The 2026.4.1 entry (added in Shopify#3731) was missing it, causing the unit
test to fail post-merge.

PR Shopify#3721 didn't actually change any skeleton devDependencies between
2026.4.0 and 2026.4.1 — only version strings were bumped. An empty
object accurately reflects that and satisfies the schema with zero
impact on merchant upgrade behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove not needed statement from changeset

we previously mentioned vite 6 as a requirement for CLI and hydrogen-react but they dont actually bundle vite at all

---------

Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: freddie <freddie.batista@shopify.com>
Co-authored-by: ✦ freddie <45042736+fredericoo@users.noreply.github.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.

5 participants