@shopify/cli-hydrogen 11.1.3 → 11.1.5

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.
Files changed (95) hide show
  1. package/dist/assets/hydrogen/bundle/analyzer.html +155 -148
  2. package/dist/assets/hydrogen/starter/CHANGELOG.md +125 -49
  3. package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +1 -1
  4. package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +1 -1
  5. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +1 -1
  6. package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +62 -29
  7. package/dist/assets/hydrogen/starter/app/components/Header.tsx +1 -1
  8. package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +1 -1
  9. package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +2 -2
  10. package/dist/assets/hydrogen/starter/app/components/SearchForm.tsx +1 -1
  11. package/dist/assets/hydrogen/starter/app/components/SearchFormPredictive.tsx +8 -3
  12. package/dist/assets/hydrogen/starter/app/components/SearchResults.tsx +3 -11
  13. package/dist/assets/hydrogen/starter/app/components/SearchResultsPredictive.tsx +2 -6
  14. package/dist/assets/hydrogen/starter/app/entry.client.tsx +10 -2
  15. package/dist/assets/hydrogen/starter/app/entry.server.tsx +5 -3
  16. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerAddressMutations.ts +7 -4
  17. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -1
  18. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrderQuery.ts +4 -1
  19. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +10 -5
  20. package/dist/assets/hydrogen/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +3 -2
  21. package/dist/assets/hydrogen/starter/app/lib/context.ts +34 -17
  22. package/dist/assets/hydrogen/starter/app/lib/fragments.ts +1 -0
  23. package/dist/assets/hydrogen/starter/app/lib/orderFilters.ts +90 -0
  24. package/dist/assets/hydrogen/starter/app/lib/redirect.ts +1 -1
  25. package/dist/assets/hydrogen/starter/app/lib/session.ts +1 -1
  26. package/dist/assets/hydrogen/starter/app/lib/variants.ts +1 -1
  27. package/dist/assets/hydrogen/starter/app/root.tsx +23 -18
  28. package/dist/assets/hydrogen/starter/app/routes/$.tsx +2 -2
  29. package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +2 -2
  30. package/dist/assets/hydrogen/starter/app/routes/[sitemap.xml].tsx +2 -3
  31. package/dist/assets/hydrogen/starter/app/routes/_index.tsx +12 -8
  32. package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +4 -3
  33. package/dist/assets/hydrogen/starter/app/routes/account._index.tsx +1 -1
  34. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +15 -11
  35. package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +47 -22
  36. package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +152 -23
  37. package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +11 -8
  38. package/dist/assets/hydrogen/starter/app/routes/account.tsx +16 -4
  39. package/dist/assets/hydrogen/starter/app/routes/account_.authorize.tsx +2 -2
  40. package/dist/assets/hydrogen/starter/app/routes/account_.login.tsx +5 -3
  41. package/dist/assets/hydrogen/starter/app/routes/account_.logout.tsx +3 -2
  42. package/dist/assets/hydrogen/starter/app/routes/api.$version.[graphql.json].tsx +2 -2
  43. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +6 -10
  44. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +10 -7
  45. package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +13 -7
  46. package/dist/assets/hydrogen/starter/app/routes/cart.$lines.tsx +3 -2
  47. package/dist/assets/hydrogen/starter/app/routes/cart.tsx +13 -9
  48. package/dist/assets/hydrogen/starter/app/routes/collections.$handle.tsx +8 -11
  49. package/dist/assets/hydrogen/starter/app/routes/collections._index.tsx +6 -6
  50. package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +10 -7
  51. package/dist/assets/hydrogen/starter/app/routes/discount.$code.tsx +3 -2
  52. package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +8 -6
  53. package/dist/assets/hydrogen/starter/app/routes/policies.$handle.tsx +7 -4
  54. package/dist/assets/hydrogen/starter/app/routes/policies._index.tsx +19 -13
  55. package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +9 -6
  56. package/dist/assets/hydrogen/starter/app/routes/search.tsx +14 -14
  57. package/dist/assets/hydrogen/starter/app/routes/sitemap.$type.$page[.xml].tsx +2 -3
  58. package/dist/assets/hydrogen/starter/app/routes.ts +1 -1
  59. package/dist/assets/hydrogen/starter/app/styles/app.css +53 -1
  60. package/dist/assets/hydrogen/starter/customer-accountapi.generated.d.ts +47 -13
  61. package/dist/assets/hydrogen/starter/env.d.ts +1 -39
  62. package/dist/assets/hydrogen/starter/eslint.config.js +35 -52
  63. package/dist/assets/hydrogen/starter/package.json +14 -15
  64. package/dist/assets/hydrogen/starter/react-router.config.ts +9 -3
  65. package/dist/assets/hydrogen/starter/server.ts +7 -7
  66. package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +1 -1
  67. package/dist/assets/hydrogen/starter/tsconfig.json +17 -13
  68. package/dist/assets/hydrogen/starter/vite.config.ts +3 -0
  69. package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +13 -20
  70. package/dist/assets/hydrogen/virtual-routes/routes/[.]well-known.appspecific.com[.]chrome[.]devtools[.]json.jsx +37 -0
  71. package/dist/commands/hydrogen/build.js +2 -16
  72. package/dist/commands/hydrogen/codegen.js +2 -10
  73. package/dist/commands/hydrogen/debug/cpu.js +3 -7
  74. package/dist/commands/hydrogen/deploy.js +14 -9
  75. package/dist/commands/hydrogen/dev.js +6 -10
  76. package/dist/commands/hydrogen/env/pull.js +9 -1
  77. package/dist/commands/hydrogen/init.d.ts +1 -1
  78. package/dist/commands/hydrogen/preview.js +1 -16
  79. package/dist/commands/hydrogen/upgrade.js +145 -20
  80. package/dist/index.d.ts +1 -6
  81. package/dist/lib/build.js +17 -4
  82. package/dist/lib/flags.js +2 -10
  83. package/dist/lib/import-utils.js +4 -1
  84. package/dist/lib/live-reload.js +4 -4
  85. package/dist/lib/log.js +1 -1
  86. package/dist/lib/mini-oxygen/common.js +4 -1
  87. package/dist/lib/onboarding/local.js +25 -1
  88. package/dist/lib/onboarding/remote.js +4 -13
  89. package/dist/lib/onboarding/setup-template.mocks.js +4 -6
  90. package/dist/lib/react-router-version-check.js +82 -0
  91. package/dist/lib/setups/routes/generate.js +3 -0
  92. package/dist/lib/transpile/project.js +15 -7
  93. package/oclif.manifest.json +8 -53
  94. package/package.json +5 -5
  95. package/dist/lib/template-diff.js +0 -202
@@ -6,7 +6,6 @@ import { Flags } from '@oclif/core';
6
6
  import { getProjectPaths, getRemixConfig } from '../../lib/remix-config.js';
7
7
  import { commonFlags, flagsToCamelObject } from '../../lib/flags.js';
8
8
  import { codegen } from '../../lib/codegen.js';
9
- import { prepareDiffDirectory } from '../../lib/template-diff.js';
10
9
 
11
10
  class Codegen extends Command {
12
11
  static descriptionWithMarkdown = "Automatically generates GraphQL types for your project\u2019s Storefront API queries.";
@@ -25,22 +24,15 @@ class Codegen extends Command {
25
24
  description: "Watch the project for changes to update types on file save.",
26
25
  required: false,
27
26
  default: false
28
- }),
29
- ...commonFlags.diff
27
+ })
30
28
  };
31
29
  async run() {
32
30
  const { flags } = await this.parse(Codegen);
33
- const originalDirectory = flags.path ? resolvePath(flags.path) : process.cwd();
34
- const diff = flags.diff ? await prepareDiffDirectory(originalDirectory, flags.watch) : void 0;
35
- const directory = diff?.targetDirectory ?? originalDirectory;
31
+ const directory = flags.path ? resolvePath(flags.path) : process.cwd();
36
32
  await runCodegen({
37
33
  ...flagsToCamelObject(flags),
38
34
  directory
39
35
  });
40
- if (diff) {
41
- await diff.copyDiffCodegen();
42
- await diff.cleanup();
43
- }
44
36
  }
45
37
  }
46
38
  async function runCodegen({
@@ -8,7 +8,6 @@ import ansiEscapes from 'ansi-escapes';
8
8
  import { getProjectPaths, isClassicProject } from '../../../lib/remix-config.js';
9
9
  import { muteDevLogs } from '../../../lib/log.js';
10
10
  import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
11
- import { prepareDiffDirectory } from '../../../lib/template-diff.js';
12
11
  import { setupResourceCleanup } from '../../../lib/resource-cleanup.js';
13
12
  import { createCpuStartupProfiler } from '../../../lib/cpu-profiler.js';
14
13
  import { runBuild } from '../build.js';
@@ -23,7 +22,6 @@ class DebugCpu extends Command {
23
22
  static description = "Builds and profiles the server startup time the app.";
24
23
  static flags = {
25
24
  ...commonFlags.path,
26
- ...commonFlags.diff,
27
25
  ...commonFlags.entry,
28
26
  output: Flags.string({
29
27
  description: `Specify a path to generate the profile file. Defaults to "${DEFAULT_OUTPUT_PATH}".`,
@@ -33,16 +31,14 @@ class DebugCpu extends Command {
33
31
  };
34
32
  async run() {
35
33
  const { flags } = await this.parse(DebugCpu);
36
- const originalDirectory = flags.path ? resolvePath(flags.path) : process.cwd();
37
- const diff = flags.diff ? await prepareDiffDirectory(originalDirectory, true) : void 0;
34
+ const directory = flags.path ? resolvePath(flags.path) : process.cwd();
38
35
  const { close } = await runDebugCpu({
39
36
  ...flagsToCamelObject(flags),
40
- directory: diff?.targetDirectory ?? originalDirectory,
41
- output: resolvePath(originalDirectory, flags.output)
37
+ directory,
38
+ output: resolvePath(directory, flags.output)
42
39
  });
43
40
  setupResourceCleanup(async () => {
44
41
  await close();
45
- await diff?.cleanup();
46
42
  });
47
43
  }
48
44
  }
@@ -7,7 +7,7 @@ import { AbortError } from '@shopify/cli-kit/node/error';
7
7
  import { writeFile } from '@shopify/cli-kit/node/fs';
8
8
  import { ensureIsClean, getLatestGitCommit, GitDirectoryNotCleanError } from '@shopify/cli-kit/node/git';
9
9
  import { resolvePath, relativePath } from '@shopify/cli-kit/node/path';
10
- import { renderWarning, renderSelectPrompt, renderConfirmationPrompt, renderSuccess, renderTasks } from '@shopify/cli-kit/node/ui';
10
+ import { renderWarning, renderSelectPrompt, renderConfirmationPrompt, renderInfo, renderSuccess, renderTasks } from '@shopify/cli-kit/node/ui';
11
11
  import { ciPlatform } from '@shopify/cli-kit/node/context/local';
12
12
  import { parseToken, createDeploy } from '@shopify/oxygen-cli/deploy';
13
13
  import { createRequire } from 'node:module';
@@ -17,10 +17,8 @@ import { commonFlags, overrideFlag, flagsToCamelObject } from '../../lib/flags.j
17
17
  import { getOxygenDeploymentData } from '../../lib/get-oxygen-deployment-data.js';
18
18
  import { runBuild } from './build.js';
19
19
  import { getViteConfig, REMIX_COMPILER_ERROR_MESSAGE } from '../../lib/vite-config.js';
20
- import { prepareDiffDirectory } from '../../lib/template-diff.js';
21
20
  import { isClassicProject, getProjectPaths } from '../../lib/remix-config.js';
22
21
  import { packageManagers } from '../../lib/package-managers.js';
23
- import { setupResourceCleanup } from '../../lib/resource-cleanup.js';
24
22
 
25
23
  const DEPLOY_OUTPUT_FILE_HANDLE = "h2_deploy_log.json";
26
24
  const deploymentLogger = (message, level = "info") => {
@@ -109,16 +107,14 @@ class Deploy extends Command {
109
107
  env: "SHOPIFY_HYDROGEN_FLAG_METADATA_VERSION",
110
108
  hidden: true
111
109
  }),
112
- ...commonFlags.diff
110
+ "force-client-sourcemap": Flags.boolean({
111
+ description: "Client sourcemapping is avoided by default because it makes backend code visible in the browser. Use this flag to force enabling it.",
112
+ env: "SHOPIFY_HYDROGEN_FLAG_FORCE_CLIENT_SOURCEMAP"
113
+ })
113
114
  };
114
115
  async run() {
115
116
  const { flags } = await this.parse(Deploy);
116
117
  const deploymentOptions = this.flagsToOxygenDeploymentOptions(flags);
117
- if (flags.diff) {
118
- const diff = await prepareDiffDirectory(deploymentOptions.path, false);
119
- deploymentOptions.path = diff.targetDirectory;
120
- setupResourceCleanup(diff.cleanup);
121
- }
122
118
  await runDeploy(deploymentOptions);
123
119
  process.exit(0);
124
120
  }
@@ -160,6 +156,7 @@ async function runDeploy(options) {
160
156
  envBranch,
161
157
  environmentFile,
162
158
  force: forceOnUncommitedChanges,
159
+ forceClientSourcemap = false,
163
160
  noVerify,
164
161
  lockfileCheck,
165
162
  jsonOutput,
@@ -434,6 +431,13 @@ Continue?`.value
434
431
  }
435
432
  };
436
433
  if (buildCommand) {
434
+ if (forceClientSourcemap) {
435
+ console.log("");
436
+ renderInfo({
437
+ headline: "The `--force-client-sourcemap` flag is not supported with a custom build command",
438
+ body: "Client sourcemaps will not be generated."
439
+ });
440
+ }
437
441
  config.buildCommand = buildCommand;
438
442
  } else {
439
443
  hooks.buildFunction = async (assetPath) => {
@@ -448,6 +452,7 @@ Continue?`.value
448
452
  assetPath,
449
453
  lockfileCheck,
450
454
  sourcemap: true,
455
+ forceClientSourcemap,
451
456
  useCodegen: false,
452
457
  entry: ssrEntry
453
458
  });
@@ -11,7 +11,7 @@ import { commonFlags, overrideFlag, flagsToCamelObject, DEFAULT_INSPECTOR_PORT,
11
11
  import { spawnCodegenProcess } from '../../lib/codegen.js';
12
12
  import { getAllEnvironmentVariables } from '../../lib/environment-variables.js';
13
13
  import { displayDevUpgradeNotice } from './upgrade.js';
14
- import { prepareDiffDirectory } from '../../lib/template-diff.js';
14
+ import { checkReactRouterVersions } from '../../lib/react-router-version-check.js';
15
15
  import { getDevConfigInBackground, TUNNEL_DOMAIN, startTunnelAndPushConfig, isMockShop, notifyIssueWithTunnelAndMockShop, getUtilityBannerlines, getDebugBannerLine } from '../../lib/dev-shared.js';
16
16
  import { getCliCommand } from '../../lib/shell.js';
17
17
  import { findPort } from '../../lib/find-port.js';
@@ -50,7 +50,6 @@ class Dev extends Command {
50
50
  default: false,
51
51
  required: false
52
52
  }),
53
- ...commonFlags.diff,
54
53
  ...commonFlags.customerAccountPush,
55
54
  ...commonFlags.verbose,
56
55
  host: Flags.boolean({
@@ -66,12 +65,10 @@ class Dev extends Command {
66
65
  };
67
66
  async run() {
68
67
  const { flags } = await this.parse(Dev);
69
- const originalDirectory = flags.path ? resolvePath(flags.path) : process.cwd();
70
- const diff = flags.diff ? await prepareDiffDirectory(originalDirectory, true) : void 0;
71
- const directory = diff?.targetDirectory ?? originalDirectory;
68
+ const directory = flags.path ? resolvePath(flags.path) : process.cwd();
72
69
  const devParams = {
73
70
  ...flagsToCamelObject(flags),
74
- customerAccountPush: flags["customer-account-push__unstable"],
71
+ customerAccountPush: flags["customer-account-push"],
75
72
  path: directory,
76
73
  cliConfig: this.config
77
74
  };
@@ -82,10 +79,6 @@ class Dev extends Command {
82
79
  const { close } = await runDev(devParams);
83
80
  setupResourceCleanup(async () => {
84
81
  await close();
85
- if (diff) {
86
- await diff.copyShopifyConfig();
87
- await diff.cleanup();
88
- }
89
82
  });
90
83
  }
91
84
  }
@@ -243,6 +236,9 @@ async function runDev({
243
236
  viteServer.bindCLIShortcuts({ print: true });
244
237
  console.log("\n");
245
238
  const storefrontTitle = (await backgroundPromise).storefrontTitle;
239
+ if (!disableVersionCheck) {
240
+ await checkReactRouterVersions(root);
241
+ }
246
242
  showSuccessBanner({
247
243
  disableVirtualRoutes,
248
244
  debug,
@@ -15,6 +15,14 @@ import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-envir
15
15
  import { getStorefrontEnvVariables } from '../../../lib/graphql/admin/pull-variables.js';
16
16
  import { verifyLinkedStorefront } from '../../../lib/verify-linked-storefront.js';
17
17
 
18
+ function needsQuoting(value) {
19
+ return /[{}@#\s"'\\$`|;&<>()!?*\[\]]/.test(value) || /[\x00-\x1F\x7F]/.test(value);
20
+ }
21
+ function quoteEnvValue(value) {
22
+ if (!needsQuoting(value)) return value;
23
+ const escaped = value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("\n", "\\n").replaceAll("\r", "\\r").replaceAll(" ", "\\t");
24
+ return `"${escaped}"`;
25
+ }
18
26
  class EnvPull extends Command {
19
27
  static descriptionWithMarkdown = "Pulls environment variables from the linked Hydrogen storefront and writes them to an `.env` file.";
20
28
  static description = "Populate your .env with variables from your Hydrogen storefront.";
@@ -83,7 +91,7 @@ async function runEnvPull({
83
91
  const dotEnvPath = resolvePath(root, envFile);
84
92
  const fetchedEnv = {};
85
93
  variables.forEach(({ isSecret, key, value }) => {
86
- fetchedEnv[key] = isSecret ? `""` : value;
94
+ fetchedEnv[key] = isSecret ? `""` : quoteEnvValue(value);
87
95
  });
88
96
  if (await fileExists(dotEnvPath) && !force) {
89
97
  const existingEnv = await readFile(dotEnvPath);
@@ -60,7 +60,7 @@ declare class Init extends Command {
60
60
  declare function runInit({ markets, ...options }?: InitOptions & {
61
61
  markets?: InitOptions['i18n'];
62
62
  }): Promise<{
63
- language?: "js" | "ts";
63
+ language?: "ts" | "js";
64
64
  packageManager: "npm" | "pnpm" | "yarn" | "bun" | "unknown";
65
65
  cssStrategy?: CssStrategy;
66
66
  cliCommand: CliCommand;
@@ -14,7 +14,6 @@ import { REMIX_COMPILER_ERROR_MESSAGE, getViteConfig } from '../../lib/vite-conf
14
14
  import { runBuild } from './build.js';
15
15
  import { setupResourceCleanup } from '../../lib/resource-cleanup.js';
16
16
  import { deferPromise } from '../../lib/defer.js';
17
- import { prepareDiffDirectory } from '../../lib/template-diff.js';
18
17
 
19
18
  class Preview extends Command {
20
19
  static descriptionWithMarkdown = "Runs a server in your local development environment that serves your Hydrogen app's production build. Requires running the [build](https://shopify.dev/docs/api/shopify-cli/hydrogen/hydrogen-build) command first.";
@@ -41,31 +40,17 @@ class Preview extends Command {
41
40
  }),
42
41
  ...overrideFlag(commonFlags.codegen, {
43
42
  codegen: { dependsOn: ["build"] }
44
- }),
45
- // Diff in preview only makes sense when combined with --build.
46
- // Without the build flag, preview only needs access to the existing
47
- // `dist` directory in the project, so there's no need to merge the
48
- // project with the skeleton template in a temporary directory.
49
- ...overrideFlag(commonFlags.diff, {
50
- diff: { dependsOn: ["build"] }
51
43
  })
52
44
  };
53
45
  async run() {
54
46
  const { flags } = await this.parse(Preview);
55
- const originalDirectory = flags.path ? resolvePath(flags.path) : process.cwd();
56
- const diff = flags.build && flags.diff ? await prepareDiffDirectory(originalDirectory, flags.watch) : void 0;
57
- const directory = diff?.targetDirectory ?? originalDirectory;
47
+ const directory = flags.path ? resolvePath(flags.path) : process.cwd();
58
48
  const { close } = await runPreview({
59
49
  ...flagsToCamelObject(flags),
60
50
  directory
61
51
  });
62
52
  setupResourceCleanup(async () => {
63
53
  await close();
64
- if (diff) {
65
- await diff.copyDiffBuild();
66
- if (flags.codegen) await diff.copyDiffCodegen();
67
- await diff.cleanup();
68
- }
69
54
  });
70
55
  }
71
56
  }
@@ -5,7 +5,7 @@ import { Flags } from '@oclif/core';
5
5
  import { ensureInsideGitDirectory, isClean } from '@shopify/cli-kit/node/git';
6
6
  import Command from '@shopify/cli-kit/node/base-command';
7
7
  import { renderSuccess, renderInfo, renderConfirmationPrompt, renderTasks, renderSelectPrompt, renderWarning } from '@shopify/cli-kit/node/ui';
8
- import { readFile, isDirectory, mkdir, fileExists, touchFile, removeFile, writeFile } from '@shopify/cli-kit/node/fs';
8
+ import { isDirectory, readFile, mkdir, fileExists, touchFile, removeFile, writeFile } from '@shopify/cli-kit/node/fs';
9
9
  import { getPackageManager, getDependencies, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
10
10
  import { exec } from '@shopify/cli-kit/node/system';
11
11
  import { AbortError } from '@shopify/cli-kit/node/error';
@@ -14,6 +14,7 @@ import { getCliCommand } from '../../lib/shell.js';
14
14
  import { commonFlags, flagsToCamelObject } from '../../lib/flags.js';
15
15
  import { getProjectPaths } from '../../lib/remix-config.js';
16
16
  import { isHydrogenMonorepo, hydrogenPackagesPath } from '../../lib/build.js';
17
+ import { isCI } from '../../lib/is-ci.js';
17
18
  import { fetch } from '@shopify/cli-kit/node/http';
18
19
 
19
20
  const INSTRUCTIONS_FOLDER = ".hydrogen";
@@ -47,6 +48,17 @@ async function runUpgrade({
47
48
  version: targetVersion,
48
49
  force
49
50
  }) {
51
+ if (targetVersion === "next") {
52
+ const isInTests = process.env.SHOPIFY_UNIT_TEST === "1";
53
+ const isInCIEnvironment = isCI();
54
+ const runningFromMonorepo = await isRunningFromHydrogenMonorepo();
55
+ if (!runningFromMonorepo && !isInTests && !isInCIEnvironment) {
56
+ throw new AbortError(
57
+ "--version=next is only available when running from the Hydrogen monorepo",
58
+ "This feature is designed for internal development and testing of unreleased versions"
59
+ );
60
+ }
61
+ }
50
62
  if (!force) {
51
63
  await checkIsGitRepo(appPath);
52
64
  await checkDirtyGitBranch(appPath);
@@ -54,6 +66,16 @@ async function runUpgrade({
54
66
  const { currentVersion, currentDependencies } = await getHydrogenVersion({
55
67
  appPath
56
68
  });
69
+ if (targetVersion === "next") {
70
+ const hasNextHydrogen = currentDependencies["@shopify/hydrogen"] === "next";
71
+ const hasNextMiniOxygen = currentDependencies["@shopify/mini-oxygen"] === "next";
72
+ if (hasNextHydrogen || hasNextMiniOxygen) {
73
+ throw new AbortError(
74
+ "Project already uses next versions",
75
+ 'Cannot upgrade to --version=next when @shopify/hydrogen or @shopify/mini-oxygen are already using "next" versions'
76
+ );
77
+ }
78
+ }
57
79
  const isPrerelease = semver.prerelease(currentVersion);
58
80
  if (isPrerelease) {
59
81
  throw new AbortError(
@@ -81,7 +103,8 @@ async function runUpgrade({
81
103
  selectedRelease = await getSelectedRelease({
82
104
  currentVersion,
83
105
  targetVersion,
84
- availableUpgrades
106
+ availableUpgrades,
107
+ currentDependencies
85
108
  });
86
109
  cumulativeRelease = getCummulativeRelease({
87
110
  availableUpgrades,
@@ -91,7 +114,8 @@ async function runUpgrade({
91
114
  });
92
115
  confirmed = await displayConfirmation({
93
116
  cumulativeRelease,
94
- selectedRelease
117
+ selectedRelease,
118
+ targetVersion
95
119
  });
96
120
  } while (!confirmed);
97
121
  const instrunctionsFilePathPromise = generateUpgradeInstructionsFile({
@@ -100,10 +124,16 @@ async function runUpgrade({
100
124
  currentVersion,
101
125
  selectedRelease
102
126
  });
103
- await upgradeNodeModules({ appPath, selectedRelease, currentDependencies });
127
+ await upgradeNodeModules({
128
+ appPath,
129
+ selectedRelease,
130
+ currentDependencies,
131
+ targetVersion
132
+ });
104
133
  await validateUpgrade({
105
134
  appPath,
106
- selectedRelease
135
+ selectedRelease,
136
+ targetVersion
107
137
  });
108
138
  const instrunctionsFilePath = await instrunctionsFilePathPromise;
109
139
  await displayUpgradeSummary({
@@ -113,6 +143,49 @@ async function runUpgrade({
113
143
  selectedRelease
114
144
  });
115
145
  }
146
+ async function isRunningFromHydrogenMonorepo() {
147
+ try {
148
+ const cwd = process.cwd();
149
+ const potentialMonorepoRoots = [
150
+ cwd,
151
+ joinPath(cwd, "../"),
152
+ joinPath(cwd, "../../")
153
+ ];
154
+ for (const root of potentialMonorepoRoots) {
155
+ try {
156
+ const templatesPath = joinPath(root, "templates/skeleton");
157
+ const packagesPath = joinPath(root, "packages/cli");
158
+ const hasTemplates = await isDirectory(templatesPath);
159
+ const hasPackages = await isDirectory(packagesPath);
160
+ if (hasTemplates && hasPackages) {
161
+ return true;
162
+ }
163
+ } catch {
164
+ continue;
165
+ }
166
+ }
167
+ return false;
168
+ } catch {
169
+ return false;
170
+ }
171
+ }
172
+ function createNextRelease(latestRelease) {
173
+ const dependencies = { ...latestRelease.dependencies };
174
+ const devDependencies = { ...latestRelease.devDependencies };
175
+ if (dependencies["@shopify/hydrogen"]) {
176
+ dependencies["@shopify/hydrogen"] = "next";
177
+ }
178
+ if (devDependencies["@shopify/mini-oxygen"]) {
179
+ devDependencies["@shopify/mini-oxygen"] = "next";
180
+ }
181
+ const nextRelease = {
182
+ ...latestRelease,
183
+ title: `${latestRelease.title} (next versions)`,
184
+ dependencies,
185
+ devDependencies
186
+ };
187
+ return nextRelease;
188
+ }
116
189
  function checkIsGitRepo(appPath) {
117
190
  return ensureInsideGitDirectory(appPath).catch(() => {
118
191
  throw new AbortError(
@@ -255,9 +328,10 @@ function getAvailableUpgrades({
255
328
  async function getSelectedRelease({
256
329
  targetVersion,
257
330
  availableUpgrades,
258
- currentVersion
331
+ currentVersion,
332
+ currentDependencies
259
333
  }) {
260
- const targetRelease = targetVersion ? availableUpgrades.find(
334
+ const targetRelease = targetVersion ? targetVersion === "next" ? createNextRelease(availableUpgrades[0]) : availableUpgrades.find(
261
335
  (release) => getAbsoluteVersion(release.version) === getAbsoluteVersion(targetVersion)
262
336
  ) : void 0;
263
337
  return targetRelease ?? promptUpgradeOptions(currentVersion, availableUpgrades);
@@ -272,6 +346,12 @@ function getCummulativeRelease({
272
346
  if (!availableUpgrades?.length) {
273
347
  return { features: [], fixes: [] };
274
348
  }
349
+ if (selectedRelease.dependencies?.["@shopify/hydrogen"] === "next") {
350
+ return {
351
+ features: selectedRelease.features || [],
352
+ fixes: selectedRelease.fixes || []
353
+ };
354
+ }
275
355
  const upgradingReleases = availableUpgrades.filter((release) => {
276
356
  const isHydrogenUpgrade = semver.gt(release.version, currentPinnedVersion) && semver.lte(release.version, selectedRelease.version);
277
357
  if (isHydrogenUpgrade) return true;
@@ -290,7 +370,8 @@ function getCummulativeRelease({
290
370
  }
291
371
  function displayConfirmation({
292
372
  cumulativeRelease,
293
- selectedRelease
373
+ selectedRelease,
374
+ targetVersion
294
375
  }) {
295
376
  const { features, fixes } = cumulativeRelease;
296
377
  if (features.length || fixes.length) {
@@ -321,6 +402,9 @@ function displayConfirmation({
321
402
  ].filter(Boolean)
322
403
  });
323
404
  }
405
+ if (targetVersion === "next") {
406
+ return true;
407
+ }
324
408
  return renderConfirmationPrompt({
325
409
  message: `Are you sure you want to upgrade to ${selectedRelease.version}?`,
326
410
  cancellationMessage: `No, choose another version`,
@@ -339,10 +423,15 @@ function isReactRouterDependency([name]) {
339
423
  }
340
424
  return false;
341
425
  }
426
+ function getPackageVersion(packageName, version, targetVersion) {
427
+ const shouldUseNext = targetVersion === "next" && (packageName === "@shopify/hydrogen" || packageName === "@shopify/mini-oxygen");
428
+ return shouldUseNext ? "next" : getAbsoluteVersion(version);
429
+ }
342
430
  function maybeIncludeDependency({
343
431
  currentDependencies,
344
432
  dependency: [name, version],
345
- selectedRelease
433
+ selectedRelease,
434
+ targetVersion
346
435
  }) {
347
436
  const existingDependencyVersion = currentDependencies[name];
348
437
  const isRemixPackage = isRemixDependency([name, version]);
@@ -350,7 +439,8 @@ function maybeIncludeDependency({
350
439
  const isReactRouterPackage = isReactRouterDependency([name, version]);
351
440
  if (isReactRouterPackage) return false;
352
441
  const isNextVersion = existingDependencyVersion === "next";
353
- if (isNextVersion) return false;
442
+ const allowNextVersions = targetVersion === "next";
443
+ if (isNextVersion && !allowNextVersions) return false;
354
444
  const depMeta = selectedRelease.dependenciesMeta?.[name];
355
445
  if (!depMeta) return true;
356
446
  const isRequired = Boolean(
@@ -358,6 +448,9 @@ function maybeIncludeDependency({
358
448
  );
359
449
  if (!isRequired) return false;
360
450
  if (!existingDependencyVersion) return true;
451
+ if (targetVersion === "next" && (name === "@shopify/hydrogen" || name === "@shopify/mini-oxygen")) {
452
+ return true;
453
+ }
361
454
  const isOlderVersion = semver.lt(
362
455
  getAbsoluteVersion(existingDependencyVersion),
363
456
  getAbsoluteVersion(version)
@@ -367,26 +460,41 @@ function maybeIncludeDependency({
367
460
  }
368
461
  function buildUpgradeCommandArgs({
369
462
  selectedRelease,
370
- currentDependencies
463
+ currentDependencies,
464
+ targetVersion
371
465
  }) {
372
466
  const args = [];
373
467
  for (const dependency of Object.entries(selectedRelease.dependencies)) {
374
468
  const shouldUpgradeDep = maybeIncludeDependency({
375
469
  currentDependencies,
376
470
  dependency,
377
- selectedRelease
471
+ selectedRelease,
472
+ targetVersion
378
473
  });
379
474
  if (!shouldUpgradeDep) continue;
380
- args.push(`${dependency[0]}@${getAbsoluteVersion(dependency[1])}`);
475
+ args.push(
476
+ `${dependency[0]}@${getPackageVersion(
477
+ dependency[0],
478
+ dependency[1],
479
+ targetVersion
480
+ )}`
481
+ );
381
482
  }
382
483
  for (const dependency of Object.entries(selectedRelease.devDependencies)) {
383
484
  const shouldUpgradeDep = maybeIncludeDependency({
384
485
  currentDependencies,
385
486
  dependency,
386
- selectedRelease
487
+ selectedRelease,
488
+ targetVersion
387
489
  });
388
490
  if (!shouldUpgradeDep) continue;
389
- args.push(`${dependency[0]}@${getAbsoluteVersion(dependency[1])}`);
491
+ args.push(
492
+ `${dependency[0]}@${getPackageVersion(
493
+ dependency[0],
494
+ dependency[1],
495
+ targetVersion
496
+ )}`
497
+ );
390
498
  }
391
499
  const currentRemix = Object.entries(currentDependencies).find(isRemixDependency);
392
500
  const selectedRemix = Object.entries(selectedRelease.dependencies).find(
@@ -428,7 +536,8 @@ function buildUpgradeCommandArgs({
428
536
  async function upgradeNodeModules({
429
537
  appPath,
430
538
  selectedRelease,
431
- currentDependencies
539
+ currentDependencies,
540
+ targetVersion
432
541
  }) {
433
542
  const tasks = [];
434
543
  const depsToRemove = [
@@ -449,7 +558,8 @@ async function upgradeNodeModules({
449
558
  }
450
559
  const upgradeArgs = buildUpgradeCommandArgs({
451
560
  selectedRelease,
452
- currentDependencies
561
+ currentDependencies,
562
+ targetVersion
453
563
  });
454
564
  if (upgradeArgs.length > 0) {
455
565
  tasks.push({
@@ -509,6 +619,13 @@ function appendReactRouterDependencies({
509
619
  return command;
510
620
  }
511
621
  function getAbsoluteVersion(version) {
622
+ if (version === "next") {
623
+ return "next";
624
+ }
625
+ const snapshotMatch = version.match(/^[\^~]?0\.0\.0-next-([a-f0-9]+)-(\d+)$/);
626
+ if (snapshotMatch) {
627
+ return version.replace(/^[\^~]?/, "");
628
+ }
512
629
  const result = semver.minVersion(version);
513
630
  if (!result) {
514
631
  throw new AbortError(`Invalid version: ${version}`);
@@ -606,7 +723,8 @@ ${releaseNotesUrl}`);
606
723
  }
607
724
  async function validateUpgrade({
608
725
  appPath,
609
- selectedRelease
726
+ selectedRelease,
727
+ targetVersion
610
728
  }) {
611
729
  const dependencies = await getDependencies(joinPath(appPath, "package.json"));
612
730
  const updatedVersion = dependencies["@shopify/hydrogen"];
@@ -614,6 +732,13 @@ async function validateUpgrade({
614
732
  throw new AbortError("Hydrogen version not found in package.json");
615
733
  }
616
734
  const updatedPinnedVersion = getAbsoluteVersion(updatedVersion);
735
+ updatedPinnedVersion.match(
736
+ /^0\.0\.0-next-[a-f0-9]+-\d+$/
737
+ );
738
+ const targetIsNext = selectedRelease.dependencies?.["@shopify/hydrogen"] === "next";
739
+ if (targetVersion === "next" && targetIsNext) {
740
+ return;
741
+ }
617
742
  if (updatedPinnedVersion !== selectedRelease.version) {
618
743
  throw new AbortError(
619
744
  `Failed to upgrade to Hydrogen version ${selectedRelease.version}`,
@@ -662,7 +787,7 @@ async function generateUpgradeInstructionsFile({
662
787
  { featuresMd: [], breakingChangesMd: [] }
663
788
  );
664
789
  const fixesMd = cumulativeRelease.fixes.filter((fixes) => fixes.steps).map(generateStepMd);
665
- if (!featuresMd.length && !fixesMd.length) {
790
+ if (!featuresMd.length && !fixesMd.length && !breakingChangesMd.length) {
666
791
  renderInfo({
667
792
  headline: `No upgrade instructions generated`,
668
793
  body: `There are no additional upgrade instructions for this version.`
@@ -814,4 +939,4 @@ async function displayDevUpgradeNotice({
814
939
  }
815
940
  }
816
941
 
817
- export { buildUpgradeCommandArgs, Upgrade as default, displayConfirmation, displayDevUpgradeNotice, getAbsoluteVersion, getAvailableUpgrades, getChangelog, getCummulativeRelease, getHydrogenVersion, getSelectedRelease, runUpgrade, upgradeNodeModules };
942
+ export { buildUpgradeCommandArgs, Upgrade as default, displayConfirmation, displayDevUpgradeNotice, getAbsoluteVersion, getAvailableUpgrades, getChangelog, getCummulativeRelease, getHydrogenVersion, getPackageVersion, getSelectedRelease, isRunningFromHydrogenMonorepo, runUpgrade, upgradeNodeModules, validateUpgrade };
package/dist/index.d.ts CHANGED
@@ -11,7 +11,6 @@ declare class Build extends Command {
11
11
  watch: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
12
12
  'bundle-stats': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
13
13
  'force-client-sourcemap': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
14
- diff: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
15
14
  codegen: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
16
15
  'codegen-config-path': _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
17
16
  'disable-route-warning': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
@@ -39,7 +38,6 @@ declare class Codegen extends Command {
39
38
  static descriptionWithMarkdown: string;
40
39
  static description: string;
41
40
  static flags: {
42
- diff: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
43
41
  'codegen-config-path': _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
44
42
  'force-sfapi-version': _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
45
43
  watch: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
@@ -66,7 +64,6 @@ declare class DebugCpu extends Command {
66
64
  static flags: {
67
65
  output: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
68
66
  entry: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
69
- diff: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
70
67
  path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
71
68
  };
72
69
  run(): Promise<void>;
@@ -87,8 +84,7 @@ declare class Dev extends Command {
87
84
  host: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
88
85
  'disable-deps-optimizer': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
89
86
  verbose: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
90
- 'customer-account-push__unstable': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
91
- diff: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
87
+ 'customer-account-push': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
92
88
  'disable-version-check': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
93
89
  'env-file': _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
94
90
  'env-branch': _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -217,7 +213,6 @@ declare class Preview extends Command {
217
213
  static descriptionWithMarkdown: string;
218
214
  static description: string;
219
215
  static flags: {
220
- diff: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
221
216
  codegen: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
222
217
  'codegen-config-path': _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
223
218
  entry: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;