Skip to content

Conversation

@juanpprieto
Copy link
Contributor

@juanpprieto juanpprieto commented Sep 5, 2025

WHY are these changes introduced?

Part of the React Router 7.8.x migration effort. This PR migrates the skeleton template to use React Router 7.8.x, showcasing the power of the new framework features and Hydrogen's enhanced support for React Router best practices.

WHAT is this pull request doing?

This PR migrates the skeleton template to React Router 7.8.x, leveraging the new Hydrogen exports and React Router's advanced type safety features.

🚀 Key Highlights

1. Hydrogen Preset for Zero-Config Setup

The new hydrogenPreset from @shopify/hydrogen provides automatic configuration for Hydrogen apps:

// react-router.config.ts
import {hydrogenPreset} from '@shopify/hydrogen/react-router-preset';
import type {Config} from '@react-router/dev/config';

export default {
  presets: [hydrogenPreset()],
} satisfies Config;

2. Enhanced Type Safety with Automatic Type Generation

React Router 7.8.x provides automatic type generation for every route, eliminating type errors and providing perfect IntelliSense:

Before (Manual typing with potential errors):
// app/routes/products.$handle.tsx
import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
import {useLoaderData, type MetaFunction} from '@remix-run/react';

// Manual typing - prone to errors and drift
export const meta: MetaFunction<typeof loader> = ({data}) => {
  // TypeScript can't verify this matches actual loader return
  return [{title: data?.product?.title}];
};

export async function loader({params, context}: LoaderFunctionArgs) {
  // params types are generic, not route-specific
  const handle = params.handle; // string | undefined
  // ...
}
After (Automatic type safety with generated Route types):
// app/routes/products.$handle.tsx
import {defer} from '@shopify/remix-oxygen';
import {useLoaderData} from 'react-router';
import type {Route} from './+types/products.$handle';  // ← Auto-generated types!

// Type-safe meta with auto-completion for loader data
export const meta: Route.MetaFunction = ({data}) => {
  // TypeScript knows exactly what data contains
  return [{title: data?.product?.title}];
};

export async function loader({params, context}: Route.LoaderArgs) {
  // params.handle is typed as string (required param)
  const handle = params.handle; // string - never undefined!
  // Full type safety for context from HydrogenContext
  const {storefront, cart, env} = context;
  // ...
}

export default function Component() {
  // Automatically typed from loader return
  const {product, variants} = useLoaderData<typeof loader>();
  // Full IntelliSense for all properties
  return <div>{product.title}</div>;
}

Key Pattern: Every route imports its auto-generated types:

// Standard import pattern for all routes
import type {Route} from './+types/[route-name]';

// Then use Route.* for all exports
export const meta: Route.MetaFunction = () => {};
export const links: Route.LinksFunction = () => [];
export const headers: Route.HeadersFunction = () => {};
export async function loader(args: Route.LoaderArgs) {}
export async function action(args: Route.ActionArgs) {}

3. New Server Entry with Hydrogen's createRequestHandler

The server.ts now imports createRequestHandler from @shopify/hydrogen/oxygen and uses React Router's virtual build import:

Before (Remix-specific imports):
// server.ts
import {createRequestHandler} from '@shopify/remix-oxygen';
import {createAppLoadContext} from '~/lib/context';

export default {
  async fetch(request, env, executionContext) {
    const appLoadContext = await createAppLoadContext(
      request,
      env,
      executionContext,
    );

    const handleRequest = createRequestHandler({
      build: await import(/* @vite-ignore */ './build/server/index.js'),
      mode: process.env.NODE_ENV,
      getLoadContext: () => appLoadContext,
    });

    const response = await handleRequest(request);
    // ... handle session and redirects
    return response;
  },
};
After (Hydrogen/React Router 7.8.x imports):
// server.ts
import {createRequestHandler} from '@shopify/hydrogen/oxygen';  // ← New import!
import {createHydrogenRouterContext} from '~/lib/context';

export default {
  async fetch(request, env, executionContext) {
    const hydrogenContext = await createHydrogenRouterContext(
      request,
      env,
      executionContext,
    );

    const handleRequest = createRequestHandler({
      // React Router 7.8.x uses virtual imports for the server build
      build: await import('virtual:react-router/server-build'),
      mode: process.env.NODE_ENV,
      getLoadContext: () => hydrogenContext,
    });

    const response = await handleRequest(request);
    
    // Handle session commits
    if (hydrogenContext.session.isPending) {
      response.headers.set(
        'Set-Cookie',
        await hydrogenContext.session.commit(),
      );
    }
    
    // Handle storefront redirects
    return storefrontRedirect({
      request,
      response,
      storefront: hydrogenContext.storefront,
    });
  },
};

Key changes:

  • Import changed from @shopify/remix-oxygen to @shopify/hydrogen/oxygen
  • Build import changed from './build/server/index.js' to 'virtual:react-router/server-build'
  • Context renamed from createAppLoadContext to createHydrogenRouterContext

4. New Hydrogen Context Exports

Implement the new @shopify/hydrogen exports for React Router integration:

import {
  createHydrogenContext,     // Create typed context for routes
  hydrogenPreset,            // Zero-config React Router preset
  //...
} from '@shopify/hydrogen';

import {
  createRequestHandler,       // Server request handler for Oxygen
} from '@shopify/hydrogen/oxygen';

5. Simplified Context Creation

The new createHydrogenContext provides a streamlined way to set up the app context:

// app/lib/context.ts
import {createHydrogenContext} from '@shopify/hydrogen';
import {AppSession} from '~/lib/session';

// Define the additional context object
const additionalContext = {
  // Additional context for custom properties, CMS clients, 3P SDKs, etc.
  // These will be available as both context.propertyName and context.get(propertyContext)
  // Example of complex objects that could be added:
  // cms: await createCMSClient(env),
} as const;

// Automatically augment HydrogenAdditionalContext with the additional context type
type AdditionalContextType = typeof additionalContext;

declare global {
  interface HydrogenAdditionalContext extends AdditionalContextType {}
}

/**
 * Creates Hydrogen context for React Router 7.8.x
 * Returns HydrogenRouterContextProvider with hybrid access patterns
 * */
export async function createHydrogenRouterContext(request,env,executionContext) {
  // ..existing code
  
  const hydrogenContext = createHydrogenContext(
    {
      env,
      request,
      cache,
      waitUntil,
      session,
      // Or detect from URL path based on locale subpath, cookies, or any other strategy
      i18n: {language: 'EN', country: 'US'},
      cart: {
        queryFragment: CART_QUERY_FRAGMENT,
      },
    },
    additionalContext,
  );

  return hydrogenContext;
}

6. Type-Safe Route Patterns

Every route now follows a consistent, type-safe pattern with auto-generated types:

// Example: app/routes/collections.$handle.tsx
import {useLoaderData} from 'react-router';
import type {Route} from './+types/collections.$handle';  // ← Auto-generated!

export const meta: Route.MetaFunction = ({data}) => {
  // data is fully typed based on loader return
  return [{title: `Collection: ${data?.collection.title ?? ''}`}];
};

export async function loader({params, context}: Route.LoaderArgs) {
  // params.handle is string (not string | undefined)
  const {handle} = params;
  const {storefront} = context;
  // ...
}

export default function Collection() {
  const {collection} = useLoaderData<typeof loader>();
  // Full type safety and IntelliSense
  return <h1>{collection.title}</h1>;
}

HOW to test your changes?

  1. Setup:

    git checkout feat/skeleton-rr-7.8
    npm install && npm run build:pkg
    cd templates/skeleton && npm install
  2. Verify all scripts pass:

    npm run build      # ✅ Builds with React Router 7.8.x
    npm run typecheck  # ✅ Types are generated and valid
    npm run lint       # ✅ Code follows best practices
    npm run dev        # ✅ Dev server with all Hydrogen features
  3. Test type generation:

    # Check generated types
    ls app/routes/+types/
    # Modify a route and see types auto-update
    npm run typecheck
  4. Verify Hydrogen features:

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

juanpprieto and others added 15 commits September 3, 2025 21:39
- Pin react-router and react-router-dom to exact 7.8.2
- Pin @react-router/* packages to exact 7.8.2
- Remove version ranges (~, ^) to ensure consistency
- Update all packages, templates, and examples
- Update package-lock.json to lock exact versions
- Add @react-router/dev and @react-router/fs-routes to root devDependencies
- Ensures packages are available in root node_modules for test symlinks
- Fixes CLI setup test that creates temporary directories
…2 compatibility

- Add .react-router to .gitignore (generated by React Router 7.8.2)
- Remove deprecated --ext flag from example lint scripts for ESLint 9 compatibility
- React Router 7.8.2 requires loaderData to be non-optional in UIMatch type
- Add loaderData property to fillMatch helper in seo.test.ts
- Upgrade TypeScript to 5.9.2 across all packages
- Fix enum compatibility between Storefront and Customer Account APIs
- Fix Navigation type mocks in hydrogen package tests
- Fix BodyInit type compatibility in cache implementation
… USDC currency (#3090)

* Fix Money component compatibility with Customer Account API USDC currency

The 2025-07 API update introduced USDC currency code to Customer Account API but not Storefront API, causing TypeScript errors and runtime failures.

Changes:
- Update Money component to accept MoneyV2 from both Storefront and Customer Account APIs via union types
- Enhance useMoney hook to detect unsupported currency codes (like USDC) and gracefully fall back to decimal formatting
- Add currency code suffix for unsupported currencies (e.g., "100.00 USDC") to maintain clarity
- Default to 2 decimal places for USDC to reinforce its 1:1 USD peg based on industry standards

Technical details:
- Handle Intl.NumberFormat RangeError for cryptocurrency codes not in ISO 4217
- Add comprehensive test coverage for both API types and USDC formatting
- Update TypeScript types to support both CurrencyCode enums

Fixes #3089

* Add changeset for Money component USDC compatibility fix

* Add build script to copy customer-account-api-types to dist

Since our Money component now imports from customer-account-api-types, we need to ensure this file is copied to the dist folder during the build process. Previously, only storefront-api-types was being copied.

- Replace copy-storefront-types with copy-api-types script that copies both API type files
- Update build and dev:demo scripts to use the new copy-api-types script
- This fixes the CI build failure where customer-account-api-types.d.ts was not found in dist

* Trigger CI rebuild
Add missing dependencies to useMemo hook to satisfy react-hooks/exhaustive-deps rule
Move params initialization inside useMemo to avoid recreating on every render
…te dependencies

This commit addresses critical TypeScript 5.9 compatibility issues that were blocking
the React Router 7.8.x migration. The changes ensure all CI checks pass with zero errors.

## Key Changes:

### ESLint TypeScript Compatibility
- Removed unused `eslint-plugin-hydrogen` package that was causing TypeScript version conflicts
- Updated `@typescript-eslint/eslint-plugin` and `@typescript-eslint/parser` to v8.42.0
- Pinned all ESLint-related package versions for consistency and reproducibility

### Fixed Lint Errors Across Examples
- GTM example: Added missing `ready` and `subscribe` dependencies to useEffect hooks
- Partytown example: Added missing `location.pathname` dependency, fixed iframe accessibility
- Subscriptions example: Replaced deprecated @ts-ignore with @ts-expect-error directives
- Added missing iframe title attributes for accessibility compliance

### Build Configuration
- Added `dist/` to eslint ignores in skeleton template to prevent linting of built files
- Standardized ESLint configuration across all examples using flat config format
- Created eslint.config.js files for all examples to ensure consistent linting

### Dependencies
- Added `@total-typescript/ts-reset@0.6.1` at monorepo level (pinned version)
- Fixed duplicate import issue in hydrogen/src/seo/seo.ts

## Technical Details:

The root cause was eslint-plugin-hydrogen depending on an older version of
@typescript-eslint packages that don't support TypeScript 5.9. Since the plugin
wasn't actively used in the codebase, removing it was the cleanest solution.

All ESLint packages are now pinned to specific versions to prevent future
compatibility issues during dependency updates.

## Testing:
- ✅ All lint checks pass
- ✅ All typecheck passes
- ✅ No ESLint warnings about TypeScript version compatibility
- ✅ All examples build successfully
These files were added during development but should not be part of the final PR:
- Removed .github/pr-plans/ directory (planning documentation)
- Removed .changeset/ files added during development

These are internal development artifacts not needed for the React Router 7.8.x migration.
@shopify
Copy link
Contributor

shopify bot commented Sep 5, 2025

Oxygen deployed a preview of your feat/skeleton-rr-7.8 branch. Details:

Storefront Status Preview link Deployment details Last update (UTC)
metaobjects ✅ Successful (Logs) Preview deployment Inspect deployment September 5, 2025 5:31 AM
Skeleton (skeleton.hydrogen.shop) ✅ Successful (Logs) Preview deployment Inspect deployment September 13, 2025 3:11 AM
custom-cart-method - (Logs) - Inspect deployment September 5, 2025 5:31 AM
third-party-queries-caching - (Logs) - Inspect deployment September 5, 2025 5:31 AM

Learn more about Hydrogen's GitHub integration.

…r Customer Account enums

- Configure Customer Account API codegen to reference Storefront API types for LanguageCode and CurrencyCode enums
- Ensures type compatibility between APIs when passing i18n.language from Storefront to Customer Account API
- Regenerate Customer Account API types with proper enum references
- Resolves TypeScript errors in skeleton template account routes
@juanpprieto juanpprieto changed the title Migrate skeleton template to React Router 7.8.x PR3 - Migrate skeleton template to React Router 7.8.x Sep 5, 2025
@juanpprieto juanpprieto marked this pull request as draft September 5, 2025 05:56
@juanpprieto juanpprieto force-pushed the feat/hydrogen-core-rr-7.8 branch from f8ae9d6 to eba204a Compare September 5, 2025 06:06
- Remove CI_ISSUES_REPORT.md and PR1_STATUS.md files
- Restore deleted changeset file dry-swans-relate.md
- Improve comment for SellingPlanGroup type assertion
- Remove .claude/commands/shopify-cli-update.md file
- Regenerate GraphQL types to ensure correctness
- Add changeset for enum compatibility fix
- Resolved conflicts in codegen.ts by merging both approaches
- Used typescript-lint version of customer-account-api-types.d.ts
- Added CountryCode to enumValues configuration
- Restored .claude/commands/shopify-cli-update.md file
@juanpprieto juanpprieto changed the title PR3 - Migrate skeleton template to React Router 7.8.x PR4 - Migrate skeleton template to React Router 7.8.x Sep 9, 2025
@juanpprieto juanpprieto force-pushed the feat/hydrogen-core-rr-7.8 branch from ce66dc2 to 85fd779 Compare September 10, 2025 05:18
@juanpprieto juanpprieto force-pushed the feat/skeleton-rr-7.8 branch 2 times, most recently from e8488b5 to 430468a Compare September 10, 2025 15:41
@juanpprieto juanpprieto changed the base branch from feat/hydrogen-core-rr-7.8 to feat/cli-diff-removal September 10, 2025 16:35
… API

- Remove enumValues override that forced Customer Account API to use Storefront API types
- Allow Customer Account API to generate its own enum types (including USDC currency)
- Update Money component and useMoney hook to handle union types properly
- Fix test helper to use USDC as example of Customer Account API-specific currency
- This properly supports runtime values like USDC that Customer Account API can return
@juanpprieto juanpprieto changed the base branch from feat/hydrogen-core-rr-7.8 to feat/cli-diff-removal September 10, 2025 20:33
- Update all routes to use React Router 7.8.x imports and patterns
- Replace LoaderArgs/ActionArgs with Route.LoaderArgs/Route.ActionArgs
- Update type imports to use ./+types/[route] pattern
- Replace @shopify/remix-oxygen imports with react-router
- Update context.ts to use createHydrogenRouterContext pattern
- Configure react-router.config.ts with hydrogenPreset
- Update package.json dependencies for React Router 7.8.x
- All npm scripts now pass with zero issues
… API operations

- Updated all account routes to consistently use context.customerAccount.i18n.language
- This ensures Customer Account API mutations receive the correct LanguageCode type
- Storefront API operations continue to use context.storefront.i18n
@juanpprieto juanpprieto marked this pull request as ready for review September 10, 2025 20:45
@github-actions

This comment has been minimized.

- Remove @shopify/remix-oxygen dependency from package.json
- Update server.ts to import createRequestHandler from @shopify/hydrogen/oxygen
- Add comprehensive changeset documenting all migration changes
- Update CI to temporarily disable recipe validation
Base automatically changed from feat/cli-diff-removal to main September 13, 2025 00:32
@juanpprieto juanpprieto requested a review from a team as a code owner September 13, 2025 00:32
- Refactor: destructure customerAccount from context in account routes
- Change context.customerAccount.i18n.language to customerAccount.i18n.language
- Remove caret from typescript version in skeleton package.json (5.9.2 instead of ^5.9.2)
…ntext error

The Layout export in React Router 7.8 is called outside the NonceProvider context,
causing 'Cannot read properties of null (reading useContext)' error. The nonce is
still properly applied through ScrollRestoration and Scripts components' internal
implementation.
The merge incorrectly introduced isHydrogenMonorepo logic that doesn't exist
in the main branch. This simplifies the optimizeDeps configuration to match
what's in origin/main, fixing the TypeScript compilation error.
@juanpprieto juanpprieto merged commit 6681f92 into main Sep 13, 2025
11 checks passed
@juanpprieto juanpprieto deleted the feat/skeleton-rr-7.8 branch September 13, 2025 03:14
juanpprieto added a commit that referenced this pull request Sep 17, 2025
* feat: pin React Router to exact version 7.8.2 across monorepo

- Pin react-router and react-router-dom to exact 7.8.2
- Pin @react-router/* packages to exact 7.8.2
- Remove version ranges (~, ^) to ensure consistency
- Update all packages, templates, and examples
- Update package-lock.json to lock exact versions

* fix: hoist @react-router/dev and fs-routes to fix CLI tests

- Add @react-router/dev and @react-router/fs-routes to root devDependencies
- Ensures packages are available in root node_modules for test symlinks
- Fixes CLI setup test that creates temporary directories

* fix: update .gitignore and example lint scripts for React Router 7.8.2 compatibility

- Add .react-router to .gitignore (generated by React Router 7.8.2)
- Remove deprecated --ext flag from example lint scripts for ESLint 9 compatibility

* fix: update UIMatch test helper for React Router 7.8.2 compatibility

- React Router 7.8.2 requires loaderData to be non-optional in UIMatch type
- Add loaderData property to fillMatch helper in seo.test.ts

* Lint and format

* Lint and format

* Add changeset

* fix: TypeScript 5.9 compatibility updates

- Upgrade TypeScript to 5.9.2 across all packages
- Fix enum compatibility between Storefront and Customer Account APIs
- Fix Navigation type mocks in hydrogen package tests
- Fix BodyInit type compatibility in cache implementation

* [2025-07] Fix Money component compatibility with Customer Account API USDC currency (#3090)

* Fix Money component compatibility with Customer Account API USDC currency

The 2025-07 API update introduced USDC currency code to Customer Account API but not Storefront API, causing TypeScript errors and runtime failures.

Changes:
- Update Money component to accept MoneyV2 from both Storefront and Customer Account APIs via union types
- Enhance useMoney hook to detect unsupported currency codes (like USDC) and gracefully fall back to decimal formatting
- Add currency code suffix for unsupported currencies (e.g., "100.00 USDC") to maintain clarity
- Default to 2 decimal places for USDC to reinforce its 1:1 USD peg based on industry standards

Technical details:
- Handle Intl.NumberFormat RangeError for cryptocurrency codes not in ISO 4217
- Add comprehensive test coverage for both API types and USDC formatting
- Update TypeScript types to support both CurrencyCode enums

Fixes #3089

* Add changeset for Money component USDC compatibility fix

* Add build script to copy customer-account-api-types to dist

Since our Money component now imports from customer-account-api-types, we need to ensure this file is copied to the dist folder during the build process. Previously, only storefront-api-types was being copied.

- Replace copy-storefront-types with copy-api-types script that copies both API type files
- Update build and dev:demo scripts to use the new copy-api-types script
- This fixes the CI build failure where customer-account-api-types.d.ts was not found in dist

* Trigger CI rebuild

* Suggested money changes for PR #3090 (#3097)

* fix: resolve eslint dependency issue in subscriptions example

Add missing dependencies to useMemo hook to satisfy react-hooks/exhaustive-deps rule

* fix: properly fix useMemo dependencies in subscriptions example

Move params initialization inside useMemo to avoid recreating on every render

* fix: resolve TypeScript 5.9 compatibility issues with ESLint and update dependencies

This commit addresses critical TypeScript 5.9 compatibility issues that were blocking
the React Router 7.8.x migration. The changes ensure all CI checks pass with zero errors.

## Key Changes:

### ESLint TypeScript Compatibility
- Removed unused `eslint-plugin-hydrogen` package that was causing TypeScript version conflicts
- Updated `@typescript-eslint/eslint-plugin` and `@typescript-eslint/parser` to v8.42.0
- Pinned all ESLint-related package versions for consistency and reproducibility

### Fixed Lint Errors Across Examples
- GTM example: Added missing `ready` and `subscribe` dependencies to useEffect hooks
- Partytown example: Added missing `location.pathname` dependency, fixed iframe accessibility
- Subscriptions example: Replaced deprecated @ts-ignore with @ts-expect-error directives
- Added missing iframe title attributes for accessibility compliance

### Build Configuration
- Added `dist/` to eslint ignores in skeleton template to prevent linting of built files
- Standardized ESLint configuration across all examples using flat config format
- Created eslint.config.js files for all examples to ensure consistent linting

### Dependencies
- Added `@total-typescript/ts-reset@0.6.1` at monorepo level (pinned version)
- Fixed duplicate import issue in hydrogen/src/seo/seo.ts

## Technical Details:

The root cause was eslint-plugin-hydrogen depending on an older version of
@typescript-eslint packages that don't support TypeScript 5.9. Since the plugin
wasn't actively used in the codebase, removing it was the cleanest solution.

All ESLint packages are now pinned to specific versions to prevent future
compatibility issues during dependency updates.

## Testing:
- ✅ All lint checks pass
- ✅ All typecheck passes
- ✅ No ESLint warnings about TypeScript version compatibility
- ✅ All examples build successfully

* ci: trigger CI workflows

* chore: remove pr-plans and changeset files from migration branch

These files were added during development but should not be part of the final PR:
- Removed .github/pr-plans/ directory (planning documentation)
- Removed .changeset/ files added during development

These are internal development artifacts not needed for the React Router 7.8.x migration.

* fix(hydrogen-react): update codegen.ts to use Storefront API types for Customer Account enums

- Configure Customer Account API codegen to reference Storefront API types for LanguageCode and CurrencyCode enums
- Ensures type compatibility between APIs when passing i18n.language from Storefront to Customer Account API
- Regenerate Customer Account API types with proper enum references
- Resolves TypeScript errors in skeleton template account routes

* fix: address PR review feedback

- Remove CI_ISSUES_REPORT.md and PR1_STATUS.md files
- Restore deleted changeset file dry-swans-relate.md
- Improve comment for SellingPlanGroup type assertion

* fix: address PR review feedback

- Remove .claude/commands/shopify-cli-update.md file
- Regenerate GraphQL types to ensure correctness
- Add changeset for enum compatibility fix

* fix(hydrogen-react): restore separate enum types for Customer Account API

- Remove enumValues override that forced Customer Account API to use Storefront API types
- Allow Customer Account API to generate its own enum types (including USDC currency)
- Update Money component and useMoney hook to handle union types properly
- Fix test helper to use USDC as example of Customer Account API-specific currency
- This properly supports runtime values like USDC that Customer Account API can return

* chore: remove unrelated shopify-cli-update.md changes

This file was inadvertently modified in commit 054c2b3 and should match the base branch version

* fix: pin React and React-DOM to 18.3.1 across monorepo

- Pin React to exactly 18.3.1 (no ^ or ~) in all packages
- Pin React-DOM to exactly 18.3.1 where used
- This resolves React version mismatch issues in monorepo
- Fixes create-hydrogen test failures due to multiple React instances

* fix: pin React to 18.3.1 across monorepo to resolve version conflicts

- Pin React and React-DOM to exact version 18.3.1 in all packages
- Add npm overrides to force React 18.3.1 for transitive dependencies
- Fixes React hook errors caused by multiple React versions
- All tests now passing (381 tests across 45 files)

* fix: add missing ast-grep Linux x64 optional dependency for CI

- Add @ast-grep/napi-linux-x64-gnu as optional dependency
- Fixes CLI tests failing in Linux CI environment
- Module was missing causing MODULE_NOT_FOUND errors

* feat(hydrogen): add React Router 7.8.x support infrastructure

Implement comprehensive React Router 7.8.x support in @shopify/hydrogen package
matching the hydrogen-react-router-7.8.2-clean branch implementation.

Core Infrastructure:
- Add proxy-based hybrid context in createHydrogenContext for React Router compatibility
- Implement unstable_RouterContextProvider with Hydrogen services
- Enable both context.get() and direct property access patterns

New Files:
- Add context-keys.ts with React Router context keys for Hydrogen services
- Add react-router-preset.ts for React Router configuration
- Add react-router.d.ts for module augmentation

Type System Updates:
- Add HydrogenRouterContextProvider interface to types.d.ts
- Update dev.env.d.ts with React Router type augmentation
- Export context keys and NonceProvider from index.ts

Build Configuration:
- Add react-router-preset build targets in tsup.config.ts
- Add react-router-types and react-router-preset exports in package.json
- Include React Router in Vite SSR no-external list

Documentation and Examples:
- Update customer.auth-handler.example.tsx with React Router imports
- Update virtual routes to use react-router imports

All tests pass and skeleton template builds successfully with these changes.

* feat(hydrogen): export hydrogenContext for React Router context.get() pattern

Export a single hydrogenContext object containing all Hydrogen service context keys.
This enables users to access Hydrogen services using React Router's context.get() pattern
while maintaining compatibility with the existing direct property access pattern.

Changes:
- Add hydrogenContext grouped export with all service context keys
- Remove individual context key exports from package (internal use only)
- Add comprehensive tests verifying both access patterns work
- Add documentation and examples for hydrogenContext usage
- Verify proxy-based context supports both get/set methods and direct access

The proxy implementation in createHydrogenContext ensures both patterns work:
1. Direct access: const {storefront, cart} = context
2. Context.get(): const storefront = context.get(hydrogenContext.storefront)

This provides a cleaner API with better discoverability and type safety.

* Add changeset for hydrogen React Router 7.8.x support

* test(hydrogen): add comprehensive tests for React Router 7.8.x integration

- Add tests for HydrogenRouterContextProvider type augmentations
- Add tests for React Router preset configuration and validation
- Add tests for createHydrogenContext proxy behavior
- Add minimal export tests for critical API surface validation
- Test context.get() and context.set() patterns from React Router
- Verify hydrogenContext export structure and API isolation

* fix: apply linting and formatting

* Add @shopify/hydrogen/oxygen export path

Creates new oxygen export with:
- createRequestHandler: Oxygen-specific wrapper around React Router's handler
  - Request validation (GET/HEAD with body check)
  - URL normalization (double slash handling)
  - Powered-by header injection
- getStorefrontHeaders: Extract Oxygen headers from requests

This replaces the functionality from @shopify/remix-oxygen

* Move stack trace improvements to Vite plugin for Oxygen builds

Adds Error.prototype.toString override to Vite's crossBoundarySetup
so it's automatically injected into Oxygen worker environments.
This replaces the runtime override that was in remix-oxygen.

* Update TypeScript references from remix-oxygen to oxygen-workers-types

Updates /// <reference types> directives in env.d.ts files to use
@shopify/oxygen-workers-types instead of @shopify/remix-oxygen

* docs: improve changeset with comprehensive examples for React Router 7.8.x features

* docs: add createRequestHandler export details to changeset

* docs: update documentation URL to use 2025-07 API version

Address PR #3142 feedback - update outdated 2025-01 documentation URL

* fix: remove unnecessary comments in createHydrogenContext

- Remove comment explaining HydrogenContextOverloads interface
- Remove commented-out function signature that duplicated the actual implementation

* fix: update UnitPriceMeasurement documentation URL to 2025-07

* Regen docs

* feat: add customerAccount.i18n support for Customer Account API

- Added i18n property to CustomerAccount type with language field
- Made I18nBase.language a union of StorefrontLanguageCode | CustomerLanguageCode
- Added cast in createHydrogenContext with explanatory comment
- Updated test snapshots to reflect new context structure

This enables skeleton templates to use context.customerAccount.i18n.language
for Customer Account API operations while maintaining type safety

* test: update i18n replacers snapshot for React Router 7.8.x context structure

* refactor(cli): remove diff-based development system

Remove the deprecated --diff flag and associated functionality from all
CLI commands. The diff system is no longer needed with React Router 7.8.x
standalone applications.

Changes:
- Remove --diff flag from build, dev, preview, codegen commands
- Delete template-diff.ts and related diff utilities
- Clean up onboarding and live-reload code
- Update CLI manifest and documentation

This simplifies the CLI and removes complexity that's no longer needed.

* ci: temporarily disable examples and recipes from CI for React Router 7.8.x migration

- Remove all example folders from npm workspaces array
- Rename build:examples and typecheck:examples scripts to disabled variants
- Update CI workflow step names to reflect only packages and templates are built
- Examples will fail due to skeleton template React Router 7.8.x changes
- This is temporary until examples are updated to match new skeleton patterns

* test: fix failing tests after diff removal

- Fix plugin-autolinker test by clearing CI environment variables in beforeEach
  The console.log for auto-linking is only shown when not in CI, so tests
  need to ensure CI and GITHUB_ACTIONS env vars are cleared

- Update remote.test.ts to reflect that examples are now copied directly
  Since diff functionality was removed, examples are copied as-is rather
  than having diffs applied. Updated test expectations accordingly.

- Exclude env.d.ts from JS transpilation test since it's a TypeScript-only file

* feat: add React Router version checking to dev command

* fix: make transpile handle missing app/routes.ts for examples

* test: skip example template tests until examples are standalone

- Skip 'creates basic projects from example templates' test
- Skip 'transpiles projects to JS' test
- Both tests fail because --diff flag was removed but examples are still diff-based
- Will be re-enabled in Branch 4 when examples are converted to standalone
- Added TODO comments explaining the situation

* format

* delete .md

* fix: remove incorrectly added React dependencies from CLI package

The CLI package doesn't need React as a runtime dependency. These were incorrectly added during the diff removal refactor.

* fix(ci): disable examples deployment and fix build:all script

- Disable examples deployment workflow during React Router 7.8.x migration
- Fix build:all to use build:examples:disabled instead of build:examples
- Examples are not in workspaces and need separate migration

* feat(skeleton): use hydrogenPreset in react-router config

* feat(skeleton): migrate template to React Router 7.8.x

- Update all routes to use React Router 7.8.x imports and patterns
- Replace LoaderArgs/ActionArgs with Route.LoaderArgs/Route.ActionArgs
- Update type imports to use ./+types/[route] pattern
- Replace @shopify/remix-oxygen imports with react-router
- Update context.ts to use createHydrogenRouterContext pattern
- Configure react-router.config.ts with hydrogenPreset
- Update package.json dependencies for React Router 7.8.x
- All npm scripts now pass with zero issues

* chore: trigger CI after rebase

* fix(skeleton): use customerAccount.i18n.language for Customer Account API operations

- Updated all account routes to consistently use context.customerAccount.i18n.language
- This ensures Customer Account API mutations receive the correct LanguageCode type
- Storefront API operations continue to use context.storefront.i18n

* test: update i18n replacers snapshot for React Router 7.8.x context structure

* ci: temporarily disable recipe validation for React Router 7.8.x migration

* feat(skeleton): migrate to @shopify/hydrogen/oxygen imports

- Remove @shopify/remix-oxygen dependency from package.json
- Update server.ts to import createRequestHandler from @shopify/hydrogen/oxygen
- Add comprehensive changeset documenting all migration changes
- Update CI to temporarily disable recipe validation

* fix: remove markdown header from changeset to pass lint check

* Address PR #3141 review comments

- Refactor: destructure customerAccount from context in account routes
- Change context.customerAccount.i18n.language to customerAccount.i18n.language
- Remove caret from typescript version in skeleton package.json (5.9.2 instead of ^5.9.2)

* format

* fix: remove isHydrogenMonorepo logic from vite plugin to match main

The merge incorrectly introduced isHydrogenMonorepo logic that doesn't exist
in the main branch. This simplifies the optimizeDeps configuration to match
what's in origin/main, fixing the TypeScript compilation error.

---------

Co-authored-by: Kara Daviduik <105449131+kdaviduik@users.noreply.github.com>
Co-authored-by: Kara Daviduik <kara.daviduik@shopify.com>
This was referenced Sep 30, 2025
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.

3 participants