@shopify/cli-hydrogen 11.1.16 → 12.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,8 +7,8 @@ export async function loader({params, context}: LoaderFunctionArgs) {
7
7
  params.locale &&
8
8
  params.locale.toLowerCase() !== `${language}-${country}`.toLowerCase()
9
9
  ) {
10
- // If the locale URL param is defined, yet we still are still at the default locale
11
- // then the the locale param must be invalid, send to the 404 page
10
+ // If the locale URL param is defined, yet we are still at the default locale
11
+ // then the locale param must be invalid, send to the 404 page
12
12
  throw new Response(null, {status: 404});
13
13
  }
14
14
 
@@ -1,5 +1,31 @@
1
1
  # skeleton
2
2
 
3
+ ## 2026.4.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Include line item children recursively in cart. ([#3753](https://github.com/Shopify/hydrogen/pull/3753)) by [@martin-pettersson](https://github.com/martin-pettersson)
8
+
9
+ - Add generic cart result typing to `createCartHandler` so custom cart fragments can use their generated fragment types. ([#3767](https://github.com/Shopify/hydrogen/pull/3767)) by [@andguy95](https://github.com/andguy95)
10
+
11
+ - Widen React Router peer dependency ranges so Hydrogen packages accept compatible React Router 7.16 patch versions without npm peer dependency conflicts. New Hydrogen projects now default to React Router 7.16.0. ([#3771](https://github.com/Shopify/hydrogen/pull/3771)) by [@fredericoo](https://github.com/fredericoo)
12
+
13
+ - Updated dependencies [[`3ccc22e0a3407298e835104e29adfb036f261474`](https://github.com/Shopify/hydrogen/commit/3ccc22e0a3407298e835104e29adfb036f261474), [`a810db483c108ac8bbeaac45595b130ed95a2ec7`](https://github.com/Shopify/hydrogen/commit/a810db483c108ac8bbeaac45595b130ed95a2ec7), [`d17057eb4630710743e81b83573973100ec56d23`](https://github.com/Shopify/hydrogen/commit/d17057eb4630710743e81b83573973100ec56d23), [`995190036d99e02ba6fb559b8e3c406c90043599`](https://github.com/Shopify/hydrogen/commit/995190036d99e02ba6fb559b8e3c406c90043599), [`39f2b326e9fddda36b1d12149794bf4ffcc0d4b8`](https://github.com/Shopify/hydrogen/commit/39f2b326e9fddda36b1d12149794bf4ffcc0d4b8)]:
14
+ - @shopify/hydrogen@2026.4.3
15
+
16
+ ## 2026.4.2
17
+
18
+ ### Patch Changes
19
+
20
+ - Add support for Vite 7 and Vite 8. Hydrogen remains backwards-compatible with Vite 5+. ([#3617](https://github.com/Shopify/hydrogen/pull/3617)) by [@frandiox](https://github.com/frandiox)
21
+
22
+ Mini Oxygen's dev server has been refactored to use the [Vite Environment API](https://vite.dev/guide/api-environment), which is the standard way to run non-browser runtimes in Vite. This replaces the previous custom middleware approach with a first-class `FetchableDevEnvironment`, improving compatibility with Vite's built-in HMR and module invalidation.
23
+
24
+ New Hydrogen projects created with `npm create @shopify/hydrogen` will default to Vite 8. The `vite-tsconfig-paths` plugin is no longer needed in the skeleton template since Vite 8 supports `resolve.tsconfigPaths` natively.
25
+
26
+ - Updated dependencies [[`dc49699c799997d5893bc06e444f888e86a3bc29`](https://github.com/Shopify/hydrogen/commit/dc49699c799997d5893bc06e444f888e86a3bc29), [`50df825c57159757529f5f9f62c258d4de2a4b97`](https://github.com/Shopify/hydrogen/commit/50df825c57159757529f5f9f62c258d4de2a4b97), [`51f1e77fe63be5e5ded4ef0c91942bc304f1abc4`](https://github.com/Shopify/hydrogen/commit/51f1e77fe63be5e5ded4ef0c91942bc304f1abc4)]:
27
+ - @shopify/hydrogen@2026.4.2
28
+
3
29
  ## 2026.4.1
4
30
 
5
31
  ### Patch Changes
@@ -23,8 +23,8 @@ function getLineItemChildrenMap(lines: CartLine[]): LineItemChildrenMap {
23
23
  children[parentId].push(line);
24
24
  }
25
25
  if ('lineComponents' in line) {
26
- const children = getLineItemChildrenMap(line.lineComponents);
27
- for (const [parentId, childIds] of Object.entries(children)) {
26
+ const lineChildren = getLineItemChildrenMap(line.lineComponents);
27
+ for (const [parentId, childIds] of Object.entries(lineChildren)) {
28
28
  if (!children[parentId]) children[parentId] = [];
29
29
  children[parentId].push(...childIds);
30
30
  }
@@ -1,6 +1,7 @@
1
1
  import {createHydrogenContext} from '@shopify/hydrogen';
2
2
  import {AppSession} from '~/lib/session';
3
3
  import {CART_QUERY_FRAGMENT} from '~/lib/fragments';
4
+ import type {CartApiQueryFragment} from 'storefrontapi.generated';
4
5
 
5
6
  // Define the additional context object
6
7
  const additionalContext = {
@@ -16,6 +17,10 @@ type AdditionalContextType = typeof additionalContext;
16
17
 
17
18
  declare global {
18
19
  interface HydrogenAdditionalContext extends AdditionalContextType {}
20
+
21
+ // Augment HydrogenCustomCartFragment with the codegen'd cart fragment type so
22
+ // that context.cart.get() and all cart mutations return the extended cart type.
23
+ interface HydrogenCustomCartFragment extends CartApiQueryFragment {}
19
24
  }
20
25
 
21
26
  /**
@@ -2,7 +2,7 @@
2
2
  "name": "skeleton",
3
3
  "private": true,
4
4
  "sideEffects": false,
5
- "version": "2026.4.1",
5
+ "version": "2026.4.3",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "build": "shopify hydrogen build --codegen",
@@ -14,25 +14,25 @@
14
14
  },
15
15
  "prettier": "@shopify/prettier-config",
16
16
  "dependencies": {
17
- "@shopify/hydrogen": "2026.4.1",
17
+ "@shopify/hydrogen": "2026.4.3",
18
18
  "graphql": "^16.10.0",
19
19
  "graphql-tag": "^2.12.6",
20
20
  "isbot": "^5.1.22",
21
21
  "react": "^18.3.1",
22
22
  "react-dom": "^18.3.1",
23
- "react-router": "7.12.0",
24
- "react-router-dom": "7.12.0"
23
+ "react-router": "7.16.0",
24
+ "react-router-dom": "7.16.0"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@eslint/compat": "^1.2.5",
28
28
  "@eslint/eslintrc": "^3.2.0",
29
29
  "@eslint/js": "^9.18.0",
30
30
  "@graphql-codegen/cli": "5.0.2",
31
- "@react-router/dev": "7.12.0",
32
- "@react-router/fs-routes": "7.12.0",
31
+ "@react-router/dev": "7.16.0",
32
+ "@react-router/fs-routes": "7.16.0",
33
33
  "@shopify/cli": "3.93.2",
34
34
  "@shopify/hydrogen-codegen": "0.3.3",
35
- "@shopify/mini-oxygen": "4.0.2",
35
+ "@shopify/mini-oxygen": "4.1.0",
36
36
  "@shopify/oxygen-workers-types": "^4.1.6",
37
37
  "@shopify/prettier-config": "^1.1.2",
38
38
  "@total-typescript/ts-reset": "^0.6.1",
@@ -54,8 +54,7 @@
54
54
  "graphql-config": "^5.0.3",
55
55
  "prettier": "^3.4.2",
56
56
  "typescript": "^5.9.2",
57
- "vite": "^6.4.2",
58
- "vite-tsconfig-paths": "^4.3.1"
57
+ "vite": "^8.0.1"
59
58
  },
60
59
  "engines": {
61
60
  "node": "^22 || ^24"
@@ -2,10 +2,12 @@ import {defineConfig} from 'vite';
2
2
  import {hydrogen} from '@shopify/hydrogen/vite';
3
3
  import {oxygen} from '@shopify/mini-oxygen/vite';
4
4
  import {reactRouter} from '@react-router/dev/vite';
5
- import tsconfigPaths from 'vite-tsconfig-paths';
6
5
 
7
6
  export default defineConfig({
8
- plugins: [hydrogen(), oxygen(), reactRouter(), tsconfigPaths()],
7
+ plugins: [hydrogen(), oxygen(), reactRouter()],
8
+ resolve: {
9
+ tsconfigPaths: true,
10
+ },
9
11
  build: {
10
12
  // Allow a strict Content-Security-Policy
11
13
  // without inlining assets as base64:
@@ -8,7 +8,6 @@
8
8
  "isbot": "^5.1.21"
9
9
  },
10
10
  "devDependencies": {
11
- "vite": "^6.2.0",
12
- "vite-tsconfig-paths": "^4.3.1"
11
+ "vite": "^8.0.1"
13
12
  }
14
13
  }
@@ -2,9 +2,11 @@ import {defineConfig} from 'vite';
2
2
  import {hydrogen} from '@shopify/hydrogen/vite';
3
3
  import {oxygen} from '@shopify/mini-oxygen/vite';
4
4
  import {vitePlugin as remix} from '@remix-run/dev';
5
- import tsconfigPaths from 'vite-tsconfig-paths';
6
5
 
7
6
  export default defineConfig({
7
+ resolve: {
8
+ tsconfigPaths: true,
9
+ },
8
10
  plugins: [
9
11
  hydrogen(),
10
12
  oxygen(),
@@ -17,7 +19,6 @@ export default defineConfig({
17
19
  v3_routeConfig: false,
18
20
  },
19
21
  }),
20
- tsconfigPaths(),
21
22
  ],
22
23
  build: {
23
24
  // Allow a strict Content-Security-Policy
@@ -46,7 +46,7 @@ class Deploy extends Command {
46
46
  }),
47
47
  force: Flags.boolean({
48
48
  char: "f",
49
- description: "Forces a deployment to proceed if there are uncommited changes in its Git repository.",
49
+ description: "Forces a deployment to proceed if there are uncommitted changes in its Git repository.",
50
50
  default: false,
51
51
  env: "SHOPIFY_HYDROGEN_FLAG_FORCE",
52
52
  required: false
@@ -88,7 +88,7 @@ class Deploy extends Command {
88
88
  required: false
89
89
  }),
90
90
  "metadata-description": Flags.string({
91
- description: "Description of the changes in the deployment. Defaults to the commit message of the latest commit if there are no uncommited changes.",
91
+ description: "Description of the changes in the deployment. Defaults to the commit message of the latest commit if there are no uncommitted changes.",
92
92
  required: false,
93
93
  env: "SHOPIFY_HYDROGEN_FLAG_METADATA_DESCRIPTION"
94
94
  }),
@@ -155,7 +155,7 @@ async function runDeploy(options) {
155
155
  env: envHandle,
156
156
  envBranch,
157
157
  environmentFile,
158
- force: forceOnUncommitedChanges,
158
+ force: forceOnUncommittedChanges,
159
159
  forceClientSourcemap = false,
160
160
  noVerify,
161
161
  lockfileCheck,
@@ -175,7 +175,7 @@ async function runDeploy(options) {
175
175
  if (error instanceof GitDirectoryNotCleanError) {
176
176
  isCleanGit = false;
177
177
  }
178
- if (!forceOnUncommitedChanges && !isCleanGit) {
178
+ if (!forceOnUncommittedChanges && !isCleanGit) {
179
179
  let errorMessage = "Uncommitted changes detected";
180
180
  let changedFiles = void 0;
181
181
  const nextSteps = [
@@ -231,7 +231,7 @@ ${changedFiles.trimEnd()}`;
231
231
  renderWarning({
232
232
  headline: "No deployment description provided",
233
233
  body: [
234
- "Deploying uncommited changes, but no description has been provided. Use the ",
234
+ "Deploying uncommitted changes, but no description has been provided. Use the ",
235
235
  { command: "--metadata-description" },
236
236
  "flag to provide a description. If no description is provided, the description defaults to ",
237
237
  { userInput: "<sha> with additional changes" },
@@ -201,7 +201,6 @@ async function runDev({
201
201
  "Add `hydrogen()` plugin to your Vite config."
202
202
  );
203
203
  }
204
- h2Plugin.api?.getPluginOptions?.();
205
204
  const remixConfig = (await getViteConfig(root)).remixConfig;
206
205
  let codegenProcess;
207
206
  const setupCodegen = useCodegen ? () => {
@@ -17,6 +17,9 @@ import { isHydrogenMonorepo, hydrogenPackagesPath } from '../../lib/build.js';
17
17
  import { isCI } from '../../lib/is-ci.js';
18
18
  import { fetch } from '@shopify/cli-kit/node/http';
19
19
 
20
+ function getAllRemovedPackages(release) {
21
+ return [...release.removeDependencies, ...release.removeDevDependencies];
22
+ }
20
23
  const INSTRUCTIONS_FOLDER = ".hydrogen";
21
24
  class Upgrade extends Command {
22
25
  static descriptionWithMarkdown = "Upgrade Hydrogen project dependencies, preview features, fixes and breaking changes. The command also generates an instruction file for each upgrade.";
@@ -440,7 +443,8 @@ function displayConfirmation({
440
443
  targetVersion
441
444
  }) {
442
445
  const { features, fixes } = cumulativeRelease;
443
- if (features.length || fixes.length) {
446
+ const allRemovedPackages = getAllRemovedPackages(cumulativeRelease);
447
+ if (features.length || fixes.length || allRemovedPackages.length) {
444
448
  renderInfo({
445
449
  headline: `Included in this upgrade:`,
446
450
  // @ts-expect-error - filter(Boolean) removes falsy values, leaving only objects
@@ -464,6 +468,16 @@ function displayConfirmation({
464
468
  }
465
469
  }
466
470
  ]
471
+ },
472
+ allRemovedPackages.length && {
473
+ title: "Removed packages",
474
+ body: [
475
+ {
476
+ list: {
477
+ items: allRemovedPackages
478
+ }
479
+ }
480
+ ]
467
481
  }
468
482
  ].filter(Boolean)
469
483
  });
@@ -881,7 +895,8 @@ async function generateUpgradeInstructionsFile({
881
895
  { featuresMd: [], breakingChangesMd: [] }
882
896
  );
883
897
  const fixesMd = cumulativeRelease.fixes.filter((fixes) => fixes.steps).map(generateStepMd);
884
- if (!featuresMd.length && !fixesMd.length && !breakingChangesMd.length) {
898
+ const allRemovedPackages = getAllRemovedPackages(cumulativeRelease);
899
+ if (!featuresMd.length && !fixesMd.length && !breakingChangesMd.length && !allRemovedPackages.length) {
885
900
  renderInfo({
886
901
  headline: `No upgrade instructions generated`,
887
902
  body: `There are no additional upgrade instructions for this version.`
@@ -921,6 +936,16 @@ ${fixesMd.join(
921
936
  "\n"
922
937
  )}`;
923
938
  }
939
+ if (allRemovedPackages.length) {
940
+ md += fixesMd.length ? "\n\n----\n\n" : "\n";
941
+ md += `## Removed packages
942
+
943
+ The following packages have been removed as part of this upgrade:
944
+
945
+ `;
946
+ md += allRemovedPackages.map((dep) => `- \`${dep}\``).join("\n");
947
+ md += "\n";
948
+ }
924
949
  const filePath = joinPath(instructionsFolderPath, filename);
925
950
  try {
926
951
  await isDirectory(instructionsFolderPath);
@@ -1048,4 +1073,4 @@ async function displayDevUpgradeNotice({
1048
1073
  }
1049
1074
  }
1050
1075
 
1051
- export { buildUpgradeCommandArgs, Upgrade as default, displayConfirmation, displayDevUpgradeNotice, getAbsoluteVersion, getAvailableUpgrades, getChangelog, getCumulativeRelease, getHydrogenVersion, getPackageVersion, getSelectedRelease, isRunningFromHydrogenMonorepo, runUpgrade, upgradeNodeModules, validateUpgrade };
1076
+ export { buildUpgradeCommandArgs, Upgrade as default, displayConfirmation, displayDevUpgradeNotice, generateUpgradeInstructionsFile, getAbsoluteVersion, getAvailableUpgrades, getChangelog, getCumulativeRelease, getHydrogenVersion, getPackageVersion, getSelectedRelease, isRunningFromHydrogenMonorepo, runUpgrade, upgradeNodeModules, validateUpgrade };
@@ -44,7 +44,7 @@ function hydrogenBundleAnalyzer(pluginOptions) {
44
44
  modsToAnalyze.map(async (mod) => {
45
45
  const relativeModId = relativePath(root, mod.id);
46
46
  const modBundleInfo = workerFile.modules[mod.id];
47
- const originalCodeBytes = modBundleInfo?.originalLength ?? mod.code?.length ?? 0;
47
+ const originalCodeBytes = typeof modBundleInfo?.originalLength === "number" ? modBundleInfo.originalLength : mod.code?.length ?? 0;
48
48
  let resultingCodeBytes = modBundleInfo?.renderedLength ?? 0;
49
49
  if (pluginOptions?.minify && modBundleInfo?.code) {
50
50
  const minifiedCode = await pluginOptions.minify(modBundleInfo.code, mod.id).catch(() => null);
@@ -115,33 +115,19 @@ function hydrogenBundleAnalyzer(pluginOptions) {
115
115
  }
116
116
  }
117
117
  };
118
- bundle[BUNDLE_ANALYZER_JSON_FILE] = {
118
+ this.emitFile({
119
119
  type: "asset",
120
120
  fileName: BUNDLE_ANALYZER_JSON_FILE,
121
- needsCodeReference: false,
122
- source: JSON.stringify(metafile, null, 2),
123
- names: [BUNDLE_ANALYZER_JSON_FILE],
124
- originalFileNames: [BUNDLE_ANALYZER_JSON_FILE],
125
- // name and originalFileName should be deprecated .. but
126
- // for some reason, removing them breaks typescript check
127
- name: BUNDLE_ANALYZER_JSON_FILE,
128
- originalFileName: BUNDLE_ANALYZER_JSON_FILE
129
- };
130
- bundle[BUNDLE_ANALYZER_HTML_FILE] = {
121
+ source: JSON.stringify(metafile, null, 2)
122
+ });
123
+ this.emitFile({
131
124
  type: "asset",
132
125
  fileName: BUNDLE_ANALYZER_HTML_FILE,
133
- needsCodeReference: false,
134
126
  source: injectAnalyzerTemplateData(
135
127
  analysisTemplate,
136
128
  JSON.stringify(metafile)
137
- ),
138
- names: [BUNDLE_ANALYZER_HTML_FILE],
139
- originalFileNames: [BUNDLE_ANALYZER_HTML_FILE],
140
- // name and originalFileName should be deprecated .. but
141
- // for some reason, removing them breaks typescript check
142
- name: BUNDLE_ANALYZER_HTML_FILE,
143
- originalFileName: BUNDLE_ANALYZER_HTML_FILE
144
- };
129
+ )
130
+ });
145
131
  }
146
132
  };
147
133
  }
@@ -353,7 +353,7 @@
353
353
  },
354
354
  "force": {
355
355
  "char": "f",
356
- "description": "Forces a deployment to proceed if there are uncommited changes in its Git repository.",
356
+ "description": "Forces a deployment to proceed if there are uncommitted changes in its Git repository.",
357
357
  "env": "SHOPIFY_HYDROGEN_FLAG_FORCE",
358
358
  "name": "force",
359
359
  "required": false,
@@ -437,7 +437,7 @@
437
437
  "type": "option"
438
438
  },
439
439
  "metadata-description": {
440
- "description": "Description of the changes in the deployment. Defaults to the commit message of the latest commit if there are no uncommited changes.",
440
+ "description": "Description of the changes in the deployment. Defaults to the commit message of the latest commit if there are no uncommitted changes.",
441
441
  "env": "SHOPIFY_HYDROGEN_FLAG_METADATA_DESCRIPTION",
442
442
  "name": "metadata-description",
443
443
  "required": false,
@@ -1675,5 +1675,5 @@
1675
1675
  ]
1676
1676
  }
1677
1677
  },
1678
- "version": "11.1.16"
1678
+ "version": "12.0.1"
1679
1679
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "@shopify:registry": "https://registry.npmjs.org"
6
6
  },
7
- "version": "11.1.16",
7
+ "version": "12.0.1",
8
8
  "license": "MIT",
9
9
  "type": "module",
10
10
  "repository": {
@@ -13,22 +13,22 @@
13
13
  "directory": "packages/cli-hydrogen"
14
14
  },
15
15
  "devDependencies": {
16
- "@react-router/dev": "7.12.0",
16
+ "@react-router/dev": "7.16.0",
17
17
  "@types/diff": "^5.0.2",
18
18
  "@types/gunzip-maybe": "^1.4.0",
19
19
  "@types/node": "22.19.15",
20
- "esbuild": "^0.25.0",
21
20
  "@types/prettier": "^2.7.2",
22
21
  "@types/source-map-support": "^0.5.10",
23
22
  "@types/tar-fs": "^2.0.1",
24
23
  "@vitest/coverage-v8": "^1.0.4",
25
24
  "devtools-protocol": "^0.0.1177611",
25
+ "esbuild": "^0.27.0",
26
26
  "fast-glob": "^3.2.12",
27
27
  "flame-chart-js": "2.3.2",
28
28
  "get-port": "^7.0.0",
29
29
  "type-fest": "^4.33.0",
30
- "vite": "^6.4.2",
31
- "vitest": "^1.0.4"
30
+ "vite": "^8.0.1",
31
+ "vitest": "^3.2.4"
32
32
  },
33
33
  "dependencies": {
34
34
  "@ast-grep/napi": "0.34.1",
@@ -40,7 +40,7 @@
40
40
  "chokidar": "3.5.3",
41
41
  "cli-truncate": "^4.0.0",
42
42
  "diff": "^5.1.0",
43
- "esbuild": "^0.25.12",
43
+ "esbuild": "^0.27.0",
44
44
  "get-east-asian-width": "^1.3.0",
45
45
  "get-port": "^7.0.0",
46
46
  "gunzip-maybe": "^1.4.2",
@@ -55,11 +55,11 @@
55
55
  },
56
56
  "peerDependencies": {
57
57
  "@graphql-codegen/cli": "^5.0.2",
58
- "@react-router/dev": "^7.12.0",
58
+ "@react-router/dev": "~7.16.0",
59
59
  "graphql-config": "^5.0.3",
60
- "vite": "^5.1.0 || ^6.2.0",
60
+ "vite": "^5.1.0 || ^6.2.0 || ^7.0.0 || ^8.0.0",
61
61
  "@shopify/hydrogen-codegen": "0.3.3",
62
- "@shopify/mini-oxygen": "4.0.2"
62
+ "@shopify/mini-oxygen": "4.1.0"
63
63
  },
64
64
  "peerDependenciesMeta": {
65
65
  "@graphql-codegen/cli": {