@shopify/cli-hydrogen 7.1.2 → 8.0.0

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 (136) hide show
  1. package/dist/commands/hydrogen/build-vite.js +19 -10
  2. package/dist/commands/hydrogen/build.js +10 -2
  3. package/dist/commands/hydrogen/check.js +1 -0
  4. package/dist/commands/hydrogen/codegen.js +1 -0
  5. package/dist/commands/hydrogen/customer-account/push.js +170 -0
  6. package/dist/commands/hydrogen/debug/cpu.js +3 -0
  7. package/dist/commands/hydrogen/deploy.js +121 -36
  8. package/dist/commands/hydrogen/dev-vite.js +128 -59
  9. package/dist/commands/hydrogen/dev.js +108 -51
  10. package/dist/commands/hydrogen/env/list.js +7 -8
  11. package/dist/commands/hydrogen/env/pull.js +17 -1
  12. package/dist/commands/hydrogen/env/{push__unstable.js → push.js} +23 -50
  13. package/dist/commands/hydrogen/generate/route.js +1 -0
  14. package/dist/commands/hydrogen/init.js +45 -17
  15. package/dist/commands/hydrogen/link.js +20 -4
  16. package/dist/commands/hydrogen/list.js +1 -0
  17. package/dist/commands/hydrogen/login.js +1 -0
  18. package/dist/commands/hydrogen/logout.js +1 -0
  19. package/dist/commands/hydrogen/preview.js +31 -16
  20. package/dist/commands/hydrogen/setup/css.js +8 -1
  21. package/dist/commands/hydrogen/setup/markets.js +1 -0
  22. package/dist/commands/hydrogen/setup/vite.js +244 -138
  23. package/dist/commands/hydrogen/setup.js +21 -22
  24. package/dist/commands/hydrogen/shortcut.js +10 -0
  25. package/dist/commands/hydrogen/unlink.js +1 -0
  26. package/dist/commands/hydrogen/upgrade.js +2 -1
  27. package/dist/generator-templates/assets/vite/package.json +3 -4
  28. package/dist/generator-templates/assets/vite/vite.config.js +10 -2
  29. package/dist/generator-templates/starter/CHANGELOG.md +89 -0
  30. package/dist/generator-templates/starter/README.md +3 -44
  31. package/dist/generator-templates/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -0
  32. package/dist/generator-templates/starter/app/lib/fragments.ts +2 -0
  33. package/dist/generator-templates/starter/app/root.tsx +2 -5
  34. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +1 -1
  35. package/dist/generator-templates/starter/app/routes/account.tsx +1 -1
  36. package/dist/generator-templates/starter/app/routes/collections.all.tsx +160 -0
  37. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +1 -2
  38. package/dist/generator-templates/starter/customer-accountapi.generated.d.ts +6 -3
  39. package/dist/generator-templates/starter/{remix.env.d.ts → env.d.ts} +8 -2
  40. package/dist/generator-templates/starter/package.json +14 -9
  41. package/dist/generator-templates/starter/server.ts +2 -1
  42. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +59 -3
  43. package/dist/generator-templates/starter/vite.config.ts +21 -0
  44. package/dist/{commands/hydrogen/init.d.ts → init.d.ts} +11 -3
  45. package/dist/lib/check-lockfile.js +12 -18
  46. package/dist/lib/codegen.js +37 -13
  47. package/dist/lib/common.js +50 -0
  48. package/dist/lib/cpu-profiler.js +4 -1
  49. package/dist/lib/dev-shared.js +97 -0
  50. package/dist/lib/environment-variables.js +51 -30
  51. package/dist/lib/file.js +8 -1
  52. package/dist/lib/flags.js +37 -16
  53. package/dist/lib/graphql/admin/customer-application-update.js +29 -0
  54. package/dist/lib/graphql/admin/get-oxygen-data.js +1 -0
  55. package/dist/lib/graphql/admin/list-environments.js +1 -0
  56. package/dist/lib/graphql/admin/pull-variables.js +4 -4
  57. package/dist/lib/graphql/admin/test-helper.js +37 -0
  58. package/dist/lib/log.js +86 -13
  59. package/dist/lib/mini-oxygen/common.js +19 -33
  60. package/dist/lib/mini-oxygen/index.js +6 -2
  61. package/dist/lib/mini-oxygen/node.js +43 -31
  62. package/dist/lib/mini-oxygen/workerd.js +72 -165
  63. package/dist/lib/missing-routes.js +1 -1
  64. package/dist/lib/onboarding/common.js +82 -70
  65. package/dist/lib/onboarding/local.js +19 -9
  66. package/dist/lib/onboarding/remote.js +35 -30
  67. package/dist/lib/package-managers.js +24 -0
  68. package/dist/lib/remix-config.js +17 -1
  69. package/dist/lib/request-events.js +6 -1
  70. package/dist/lib/setups/i18n/replacers.js +9 -6
  71. package/dist/lib/setups/routes/generate.js +1 -0
  72. package/dist/lib/shell.js +2 -1
  73. package/dist/lib/shopify-config.js +19 -1
  74. package/dist/lib/template-diff.js +36 -15
  75. package/dist/lib/template-downloader.js +35 -5
  76. package/dist/lib/transpile/morph/typedefs.js +5 -2
  77. package/dist/lib/transpile/project.js +8 -4
  78. package/dist/lib/tunneling.js +44 -0
  79. package/dist/lib/virtual-routes.js +1 -1
  80. package/dist/lib/vite-config.js +39 -9
  81. package/oclif.manifest.json +711 -498
  82. package/package.json +32 -24
  83. package/dist/commands/hydrogen/deploy.test.js +0 -553
  84. package/dist/commands/hydrogen/env/list.test.js +0 -148
  85. package/dist/commands/hydrogen/env/pull.test.js +0 -207
  86. package/dist/commands/hydrogen/env/push__unstable.test.js +0 -383
  87. package/dist/commands/hydrogen/generate/route.test.js +0 -43
  88. package/dist/commands/hydrogen/init.test.js +0 -641
  89. package/dist/commands/hydrogen/link.test.js +0 -187
  90. package/dist/commands/hydrogen/list.test.js +0 -111
  91. package/dist/commands/hydrogen/setup.test.js +0 -61
  92. package/dist/commands/hydrogen/shortcut.test.js +0 -30
  93. package/dist/commands/hydrogen/unlink.test.js +0 -36
  94. package/dist/commands/hydrogen/upgrade.test.js +0 -786
  95. package/dist/generator-templates/starter/remix.config.js +0 -24
  96. package/dist/lib/auth.test.js +0 -157
  97. package/dist/lib/check-lockfile.test.js +0 -81
  98. package/dist/lib/check-version.test.js +0 -86
  99. package/dist/lib/environment-variables.test.js +0 -149
  100. package/dist/lib/file.test.js +0 -68
  101. package/dist/lib/flags.test.js +0 -43
  102. package/dist/lib/get-oxygen-deployment-data.test.js +0 -120
  103. package/dist/lib/gid.test.js +0 -15
  104. package/dist/lib/graphql/admin/client.test.js +0 -76
  105. package/dist/lib/graphql/admin/create-storefront.test.js +0 -64
  106. package/dist/lib/graphql/admin/link-storefront.test.js +0 -38
  107. package/dist/lib/graphql/admin/list-environments.test.js +0 -44
  108. package/dist/lib/graphql/admin/list-storefronts.test.js +0 -44
  109. package/dist/lib/graphql/admin/pull-variables.test.js +0 -43
  110. package/dist/lib/graphql/business-platform/user-account.test.js +0 -80
  111. package/dist/lib/log.test.js +0 -92
  112. package/dist/lib/mini-oxygen/assets.js +0 -134
  113. package/dist/lib/mini-oxygen/mini-oxygen.test.js +0 -214
  114. package/dist/lib/mini-oxygen/workerd-inspector-logs.js +0 -227
  115. package/dist/lib/mini-oxygen/workerd-inspector-proxy.js +0 -200
  116. package/dist/lib/mini-oxygen/workerd-inspector.js +0 -219
  117. package/dist/lib/missing-routes.test.js +0 -45
  118. package/dist/lib/remix-version-check.test.js +0 -39
  119. package/dist/lib/remix-version-interop.test.js +0 -13
  120. package/dist/lib/setups/i18n/domains.test.js +0 -39
  121. package/dist/lib/setups/i18n/replacers.test.js +0 -261
  122. package/dist/lib/setups/i18n/subdomains.test.js +0 -39
  123. package/dist/lib/setups/i18n/subfolders.test.js +0 -39
  124. package/dist/lib/setups/routes/generate.test.js +0 -296
  125. package/dist/lib/shell.test.js +0 -111
  126. package/dist/lib/shopify-config.test.js +0 -199
  127. package/dist/lib/string.test.js +0 -16
  128. package/dist/lib/virtual-routes.test.js +0 -49
  129. package/dist/lib/vite/hydrogen-middleware.js +0 -82
  130. package/dist/lib/vite/mini-oxygen.js +0 -152
  131. package/dist/lib/vite/plugins.d.ts +0 -27
  132. package/dist/lib/vite/plugins.js +0 -139
  133. package/dist/lib/vite/shared.js +0 -10
  134. package/dist/lib/vite/utils.js +0 -55
  135. package/dist/lib/vite/worker-entry.js +0 -1518
  136. /package/dist/generator-templates/starter/{.eslintrc.js → .eslintrc.cjs} +0 -0
@@ -1,5 +1,6 @@
1
1
  import * as _oclif_core_lib_interfaces_parser_js from '@oclif/core/lib/interfaces/parser.js';
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
+ import { PackageManager } from '@shopify/cli-kit/node/node-package-manager';
3
4
 
4
5
  declare const GENERATOR_SETUP_ASSETS_SUB_DIRS: readonly ["tailwind", "css-modules", "vanilla-extract", "postcss", "vite"];
5
6
  type AssetDir = (typeof GENERATOR_SETUP_ASSETS_SUB_DIRS)[number];
@@ -25,6 +26,8 @@ type InitOptions = {
25
26
  shortcut?: boolean;
26
27
  installDeps?: boolean;
27
28
  git?: boolean;
29
+ quickstart?: boolean;
30
+ packageManager?: PackageManager;
28
31
  };
29
32
  declare const LANGUAGES: {
30
33
  readonly js: "JavaScript";
@@ -33,10 +36,13 @@ declare const LANGUAGES: {
33
36
  type Language = keyof typeof LANGUAGES;
34
37
 
35
38
  declare class Init extends Command {
39
+ static descriptionWithMarkdown: string;
36
40
  static description: string;
37
41
  static flags: {
38
42
  routes: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
39
43
  git: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
44
+ quickstart: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
45
+ 'package-manager': _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
40
46
  shortcut: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
41
47
  markets: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
42
48
  styling: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -49,11 +55,13 @@ declare class Init extends Command {
49
55
  };
50
56
  run(): Promise<void>;
51
57
  }
52
- declare function runInit(options?: InitOptions): Promise<{
58
+ declare function runInit({ markets, ...options }?: InitOptions & {
59
+ markets?: InitOptions['i18n'];
60
+ }): Promise<{
53
61
  language?: "js" | "ts" | undefined;
54
- packageManager: "npm" | "pnpm" | "yarn" | "bun" | "unknown";
62
+ packageManager: "npm" | "yarn" | "pnpm" | "unknown" | "bun";
55
63
  cssStrategy?: CssStrategy | undefined;
56
- cliCommand: "h2" | "pnpm shopify hydrogen" | "yarn shopify hydrogen" | "bun shopify hydrogen" | "npx shopify hydrogen";
64
+ cliCommand: "h2" | "yarn shopify hydrogen" | "pnpm shopify hydrogen" | "bun shopify hydrogen" | "npx shopify hydrogen";
57
65
  depsInstalled: boolean;
58
66
  depsError?: Error | undefined;
59
67
  i18n?: "subfolders" | "domains" | "subdomains" | undefined;
@@ -3,7 +3,7 @@ import { resolvePath } from '@shopify/cli-kit/node/path';
3
3
  import { checkIfIgnoredInGitRepository } from '@shopify/cli-kit/node/git';
4
4
  import { renderWarning } from '@shopify/cli-kit/node/ui';
5
5
  import { AbortError } from '@shopify/cli-kit/node/error';
6
- import { lockfiles } from '@shopify/cli-kit/node/node-package-manager';
6
+ import { packageManagers } from './package-managers.js';
7
7
 
8
8
  function missingLockfileWarning(shouldExit) {
9
9
  const headline = "No lockfile found";
@@ -23,15 +23,9 @@ function missingLockfileWarning(shouldExit) {
23
23
  renderWarning({ headline, body, nextSteps });
24
24
  }
25
25
  }
26
- function multipleLockfilesWarning(lockfiles2, shouldExit) {
27
- const packageManagers = {
28
- "bun.lockb": "bun",
29
- "yarn.lock": "yarn",
30
- "package-lock.json": "npm",
31
- "pnpm-lock.yaml": "pnpm"
32
- };
33
- const lockfileList = lockfiles2.map((lockfile) => {
34
- return `${lockfile} (created by ${packageManagers[lockfile]})`;
26
+ function multipleLockfilesWarning(packageManagers2, shouldExit) {
27
+ const lockfileList = packageManagers2.map(({ name, lockfile }) => {
28
+ return `${lockfile} (created by ${name})`;
35
29
  });
36
30
  const headline = "Multiple lockfiles found";
37
31
  const body = [
@@ -61,19 +55,19 @@ function lockfileIgnoredWarning(lockfile) {
61
55
  async function checkLockfileStatus(directory, shouldExit = false) {
62
56
  if (process.env.LOCAL_DEV)
63
57
  return;
64
- const availableLockfiles = [];
65
- for (const lockFileName of lockfiles) {
66
- if (await fileExists(resolvePath(directory, lockFileName))) {
67
- availableLockfiles.push(lockFileName);
58
+ const foundPackageManagers = [];
59
+ for (const packageManager of packageManagers) {
60
+ if (await fileExists(resolvePath(directory, packageManager.lockfile))) {
61
+ foundPackageManagers.push(packageManager);
68
62
  }
69
63
  }
70
- if (availableLockfiles.length === 0) {
64
+ if (foundPackageManagers.length === 0) {
71
65
  return missingLockfileWarning(shouldExit);
72
66
  }
73
- if (availableLockfiles.length > 1) {
74
- return multipleLockfilesWarning(availableLockfiles, shouldExit);
67
+ if (foundPackageManagers.length > 1) {
68
+ return multipleLockfilesWarning(foundPackageManagers, shouldExit);
75
69
  }
76
- const lockfile = availableLockfiles[0];
70
+ const lockfile = foundPackageManagers[0].lockfile;
77
71
  const ignoredLockfile = await checkIfIgnoredInGitRepository(directory, [
78
72
  lockfile
79
73
  ]).catch(() => {
@@ -1,7 +1,7 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { getCodeFormatOptions, formatCode } from './format-code.js';
4
- import { renderWarning, renderFatalError } from '@shopify/cli-kit/node/ui';
4
+ import { renderWarning } from '@shopify/cli-kit/node/ui';
5
5
  import { relativePath, joinPath, resolvePath, basename } from '@shopify/cli-kit/node/path';
6
6
  import { AbortError } from '@shopify/cli-kit/node/error';
7
7
 
@@ -17,6 +17,13 @@ if (isStandaloneProcess) {
17
17
  });
18
18
  }
19
19
  function normalizeCodegenError(errorMessage, rootDirectory) {
20
+ if (errorMessage.includes("AbortError: ")) {
21
+ const parsedError = errorMessage.split("AbortError: ")[1] ?? "";
22
+ const message2 = parsedError.split("\n")[0];
23
+ const details2 = parsedError.match(/tryMessage: '(.*)',$/m)?.[1];
24
+ if (message2)
25
+ return { message: message2, details: details2 };
26
+ }
20
27
  const [first = "", ...rest] = errorMessage.replaceAll("[FAILED]", "").replace(/\s{2,}/g, "\n").replace(/\n,\n/, "\n").trim().split("\n");
21
28
  const message = "[Codegen] " + first;
22
29
  let details = rest.join("\n");
@@ -48,25 +55,27 @@ function spawnCodegenProcess({
48
55
  const { message, details } = normalizeCodegenError(dataString, rootDirectory);
49
56
  if (/`punycode`/.test(message))
50
57
  return;
58
+ if (/\.body\[\d\]/.test(message))
59
+ return;
60
+ if (/console\.time(End)?\(\)/.test(message))
61
+ return;
51
62
  console.log("");
52
63
  renderWarning({ headline: message, body: details });
53
64
  });
54
65
  child.on("close", (code) => {
55
66
  if (code && code > 0) {
56
- renderFatalError({
57
- type: 0,
58
- name: "CodegenError",
59
- message: `Codegen process exited with code ${code}`,
60
- skipOclifErrorHandling: true,
61
- tryMessage: "Try restarting the dev server."
67
+ renderWarning({
68
+ headline: "Codegen process exited with code " + code,
69
+ body: "There should be more logs above."
62
70
  });
63
- process.exit(code);
64
71
  }
65
72
  });
66
73
  return child;
67
74
  }
68
75
  function codegen(options) {
69
76
  return generateTypes(options).catch((error) => {
77
+ if (error instanceof AbortError)
78
+ throw error;
70
79
  const { message, details } = normalizeCodegenError(
71
80
  error.message,
72
81
  options.rootDirectory
@@ -80,7 +89,12 @@ async function generateTypes({
80
89
  forceSfapiVersion,
81
90
  ...dirs
82
91
  }) {
83
- const { generate, loadCodegenConfig, CodegenContext } = await import('@graphql-codegen/cli');
92
+ const { generate, loadCodegenConfig, CodegenContext } = await import('@graphql-codegen/cli').catch(() => {
93
+ throw new AbortError(
94
+ "Could not load GraphQL Codegen CLI.",
95
+ "Please make sure you have `@graphql-codegen/cli` installed as a dev dependency."
96
+ );
97
+ });
84
98
  const { config: codegenConfig } = (
85
99
  // Load <root>/codegen.ts if available
86
100
  await loadCodegenConfig({
@@ -116,8 +130,18 @@ async function generateDefaultConfig({
116
130
  rootDirectory,
117
131
  appDirectory = resolvePath(rootDirectory, "app")
118
132
  }, forceSfapiVersion) {
119
- const { getSchema, preset, pluckConfig } = await import('@shopify/hydrogen-codegen');
120
- const { loadConfig } = await import('graphql-config');
133
+ const { getSchema, preset, pluckConfig } = await import('@shopify/hydrogen-codegen').catch(() => {
134
+ throw new AbortError(
135
+ "Could not load Hydrogen Codegen.",
136
+ "Please make sure you have `@shopify/hydrogen-codegen` installed as a dev dependency."
137
+ );
138
+ });
139
+ const { loadConfig } = await import('graphql-config').catch(() => {
140
+ throw new AbortError(
141
+ "Could not load GraphQL Config.",
142
+ "Please make sure you have `graphql-config` installed as a dev dependency."
143
+ );
144
+ });
121
145
  const gqlConfig = await loadConfig({
122
146
  rootDir: rootDirectory,
123
147
  throwOnEmpty: false,
@@ -128,8 +152,8 @@ async function generateDefaultConfig({
128
152
  const sfapiProject = findGqlProject(sfapiSchema, gqlConfig);
129
153
  const defaultGlob = "*!(*.d).{ts,tsx,js,jsx}";
130
154
  const appDirRelative = relativePath(rootDirectory, appDirectory);
131
- const caapiSchema = getSchema("customer-account");
132
- const caapiProject = findGqlProject(caapiSchema, gqlConfig);
155
+ const caapiSchema = getSchema("customer-account", { throwIfMissing: false });
156
+ const caapiProject = caapiSchema ? findGqlProject(caapiSchema, gqlConfig) : void 0;
133
157
  const customerAccountAPIConfig = caapiProject?.documents ? {
134
158
  ["customer-accountapi.generated.d.ts"]: {
135
159
  preset,
@@ -0,0 +1,50 @@
1
+ import { AbortError } from '@shopify/cli-kit/node/error';
2
+ import colors from '@shopify/cli-kit/node/colors';
3
+
4
+ function orderEnvironmentsBySafety(environments) {
5
+ return [
6
+ ...environments.filter((environment) => environment.type === "PREVIEW"),
7
+ ...environments.filter((environment) => environment.type === "CUSTOM"),
8
+ ...environments.filter((environment) => environment.type === "PRODUCTION")
9
+ ];
10
+ }
11
+ function createEnvironmentCliChoiceLabel(name, handle, branch) {
12
+ const metadataStringified = Object.entries({ handle, branch }).reduce((acc, [key, val]) => {
13
+ if (val) {
14
+ acc.push(`${key}: ${val}`);
15
+ }
16
+ return acc;
17
+ }, []).join(", ");
18
+ return `${name} ${colors.dim(`(${metadataStringified})`)}`;
19
+ }
20
+ function findEnvironmentOrThrow(environments, envHandle) {
21
+ const matchedEnvironment = environments.find(
22
+ ({ handle }) => handle === envHandle
23
+ );
24
+ if (!matchedEnvironment) {
25
+ throw environmentNotFound("handle", envHandle);
26
+ }
27
+ return matchedEnvironment;
28
+ }
29
+ function findEnvironmentByBranchOrThrow(environments, branch) {
30
+ const matchedEnvironment = environments.find(({ branch: b }) => b === branch);
31
+ if (!matchedEnvironment) {
32
+ throw environmentNotFound("branch", branch);
33
+ }
34
+ return matchedEnvironment;
35
+ }
36
+ function environmentNotFound(criterion, value) {
37
+ return new AbortError(
38
+ "Environment not found",
39
+ `We could not find an environment matching the ${criterion} '${value}'.`,
40
+ [
41
+ [
42
+ "Run",
43
+ { command: "env list" },
44
+ "to view a list of available environments."
45
+ ]
46
+ ]
47
+ );
48
+ }
49
+
50
+ export { createEnvironmentCliChoiceLabel, findEnvironmentByBranchOrThrow, findEnvironmentOrThrow, orderEnvironmentsBySafety };
@@ -1,8 +1,11 @@
1
1
  import { readFile } from '@shopify/cli-kit/node/fs';
2
2
  import { Session } from 'node:inspector';
3
+ import { handleMiniOxygenImportFail } from './mini-oxygen/common.js';
3
4
 
4
5
  async function createCpuStartupProfiler() {
5
- const { createMiniOxygen } = await import('@shopify/mini-oxygen');
6
+ const { createMiniOxygen } = await import('@shopify/mini-oxygen/node').catch(
7
+ handleMiniOxygenImportFail
8
+ );
6
9
  const miniOxygen = createMiniOxygen({
7
10
  script: "export default {}",
8
11
  modules: true,
@@ -0,0 +1,97 @@
1
+ import colors from '@shopify/cli-kit/node/colors';
2
+ import { outputInfo, outputContent, outputToken } from '@shopify/cli-kit/node/output';
3
+ import { renderInfo } from '@shopify/cli-kit/node/ui';
4
+ import { AbortError } from '@shopify/cli-kit/node/error';
5
+ import { getStorefrontId, runCustomerAccountPush } from '../commands/hydrogen/customer-account/push.js';
6
+ import { getLocalVariables } from '../lib/environment-variables.js';
7
+ import { startTunnelPlugin, pollTunnelURL } from './tunneling.js';
8
+ import { getConfig } from './shopify-config.js';
9
+ import { getGraphiQLUrl } from './graphiql-url.js';
10
+
11
+ function isMockShop(envVariables) {
12
+ return envVariables.PUBLIC_STORE_DOMAIN && envVariables.PUBLIC_STORE_DOMAIN.includes("mock.shop");
13
+ }
14
+ function notifyIssueWithTunnelAndMockShop(cliCommand) {
15
+ renderInfo({
16
+ headline: "Using mock.shop with `--customer-account-push` flag is not supported",
17
+ body: "The functionalities of this flag are disabled.",
18
+ nextSteps: [
19
+ "You may continue knowing Customer Account API (/account) interactions will fail.",
20
+ [
21
+ "Or run",
22
+ { command: `${cliCommand} env pull` },
23
+ "to link to your store credentials."
24
+ ]
25
+ ]
26
+ });
27
+ }
28
+ function getDevConfigInBackground(root, customerAccountPushFlag) {
29
+ return getLocalVariables(root).then(async ({ variables: localVariables }) => {
30
+ const customerAccountPush = customerAccountPushFlag && !isMockShop(localVariables);
31
+ if (customerAccountPush) {
32
+ await getStorefrontId(root);
33
+ }
34
+ const { shop, storefront } = await getConfig(root);
35
+ const storefrontId = storefront?.id;
36
+ return {
37
+ storefrontId,
38
+ customerAccountPush,
39
+ fetchRemote: !!shop && !!storefrontId,
40
+ localVariables,
41
+ storefrontTitle: storefront?.title
42
+ };
43
+ });
44
+ }
45
+ const TUNNEL_DOMAIN = Object.freeze({
46
+ ORIGINAL: ".trycloudflare.com",
47
+ REBRANDED: ".tryhydrogen.dev"
48
+ });
49
+ async function startTunnelAndPushConfig(root, cliConfig, port, storefrontId) {
50
+ outputInfo("\nStarting tunnel...\n");
51
+ const tunnel = await startTunnelPlugin(cliConfig, port, "cloudflare");
52
+ const host = await pollTunnelURL(tunnel).then(
53
+ (host2) => (
54
+ // Replace branded tunnel domain:
55
+ host2.replace(TUNNEL_DOMAIN.ORIGINAL, TUNNEL_DOMAIN.REBRANDED)
56
+ )
57
+ );
58
+ const cleanup = await runCustomerAccountPush({
59
+ path: root,
60
+ devOrigin: host,
61
+ storefrontId
62
+ }).catch((error) => {
63
+ if (error instanceof AbortError) {
64
+ renderInfo({
65
+ headline: "Customer Account Application setup update fail.",
66
+ body: error.tryMessage || void 0,
67
+ nextSteps: error.nextSteps
68
+ });
69
+ }
70
+ });
71
+ return { host, cleanup };
72
+ }
73
+ function getDebugBannerLine(publicInspectorPort) {
74
+ const isVSCode = process.env.TERM_PROGRAM === "vscode";
75
+ const debuggingDocsLink = "https://h2o.fyi/debugging/server-code" + (isVSCode ? "#visual-studio-code" : "#step-2-attach-a-debugger");
76
+ return outputContent`Debugging enabled on port ${String(
77
+ publicInspectorPort
78
+ )}.\nAttach a ${outputToken.link(
79
+ colors.yellow(isVSCode ? "VSCode debugger" : "debugger"),
80
+ debuggingDocsLink
81
+ )} or open DevTools in http://localhost:${String(publicInspectorPort)}.`.value;
82
+ }
83
+ function getUtilityBannerlines(host) {
84
+ host = host.endsWith("/") ? host.slice(0, -1) : host;
85
+ return [
86
+ `View GraphiQL API browser:
87
+ ${getGraphiQLUrl({
88
+ host
89
+ })}`,
90
+ `View server network requests:
91
+ ${host}/subrequest-profiler`
92
+ ].map((value, index) => ({
93
+ subdued: `${index === 0 ? "" : "\n\n"}${value}`
94
+ }));
95
+ }
96
+
97
+ export { TUNNEL_DOMAIN, getDebugBannerLine, getDevConfigInBackground, getUtilityBannerlines, isMockShop, notifyIssueWithTunnelAndMockShop, startTunnelAndPushConfig };
@@ -6,6 +6,8 @@ import { readAndParseDotEnv } from '@shopify/cli-kit/node/dot-env';
6
6
  import { renderWarning } from '@shopify/cli-kit/node/ui';
7
7
  import colors from '@shopify/cli-kit/node/colors';
8
8
  import { getStorefrontEnvVariables } from './graphql/admin/pull-variables.js';
9
+ import { getStorefrontEnvironments } from './graphql/admin/list-environments.js';
10
+ import { findEnvironmentByBranchOrThrow } from './common.js';
9
11
  import { login } from './auth.js';
10
12
 
11
13
  const createEmptyRemoteVars = () => ({
@@ -14,50 +16,63 @@ const createEmptyRemoteVars = () => ({
14
16
  });
15
17
  async function getAllEnvironmentVariables({
16
18
  root,
19
+ envHandle,
17
20
  envBranch,
18
- fetchRemote = true
21
+ fetchRemote = true,
22
+ localVariables: inlineLocalVariables
19
23
  }) {
20
- const dotEnvPath = resolvePath(root, ".env");
21
24
  const [{ remoteVariables, remoteSecrets }, { variables: localVariables }] = await Promise.all([
22
25
  // Get remote vars
23
- fetchRemote ? getRemoteVariables(root, envBranch).catch((error) => {
24
- renderWarning({
25
- headline: "Failed to load environment variables from Shopify. The development server will still start, but the following error occurred:",
26
- body: [error.message, error.tryMessage, error.nextSteps].filter(Boolean).join("\n\n")
27
- });
28
- return createEmptyRemoteVars();
29
- }) : createEmptyRemoteVars(),
26
+ fetchRemote ? getRemoteVariables(root, envHandle, envBranch).catch(
27
+ (error) => {
28
+ renderWarning({
29
+ headline: "Failed to load environment variables from Shopify. The development server will still start, but the following error occurred:",
30
+ body: [error.message, error.tryMessage, error.nextSteps].filter(Boolean).join("\n\n")
31
+ });
32
+ return createEmptyRemoteVars();
33
+ }
34
+ ) : createEmptyRemoteVars(),
30
35
  // Get local vars
31
- fileExists(dotEnvPath).then(
32
- (exists) => exists ? readAndParseDotEnv(dotEnvPath) : { variables: {} }
33
- )
36
+ inlineLocalVariables ? { variables: inlineLocalVariables } : getLocalVariables(root)
34
37
  ]);
35
38
  const remoteSecretKeys = Object.keys(remoteSecrets);
36
39
  const remotePublicKeys = Object.keys(remoteVariables);
37
40
  const localKeys = Object.keys(localVariables);
38
- if (localKeys.length > 0 || remotePublicKeys.length + remoteSecretKeys.length > 0) {
39
- outputInfo("\nEnvironment variables injected into MiniOxygen:\n");
40
- outputInfo(
41
- linesToColumns([
42
- ...remotePublicKeys.filter((key) => !localKeys.includes(key)).map((key) => [key, "from Oxygen"]),
43
- ...localKeys.map((key) => [key, "from local .env"]),
44
- // Ensure secret variables always get added to the bottom of the list
45
- ...remoteSecretKeys.filter((key) => !localKeys.includes(key)).map((key) => [
46
- colors.dim(key),
47
- colors.dim("from Oxygen (Marked as secret)")
41
+ function logInjectedVariables() {
42
+ if (localKeys.length > 0 || remotePublicKeys.length + remoteSecretKeys.length > 0) {
43
+ outputInfo("\nEnvironment variables injected into MiniOxygen:\n");
44
+ outputInfo(
45
+ linesToColumns([
46
+ ...remotePublicKeys.filter((key) => !localKeys.includes(key)).map((key) => [key, "from Oxygen"]),
47
+ ...localKeys.map((key) => [key, "from local .env"]),
48
+ // Ensure secret variables always get added to the bottom of the list
49
+ ...remoteSecretKeys.filter((key) => !localKeys.includes(key)).map((key) => [
50
+ colors.dim(key),
51
+ colors.dim("from Oxygen (Marked as secret)")
52
+ ])
48
53
  ])
49
- ])
50
- );
54
+ );
55
+ }
51
56
  }
52
57
  return {
53
- ...remoteSecrets,
54
- ...remoteVariables,
55
- ...localVariables
58
+ logInjectedVariables,
59
+ remoteVariables,
60
+ remoteSecrets,
61
+ localVariables,
62
+ allVariables: {
63
+ ...remoteSecrets,
64
+ ...remoteVariables,
65
+ ...localVariables
66
+ }
56
67
  };
57
68
  }
58
- async function getRemoteVariables(root, envBranch) {
69
+ async function getRemoteVariables(root, envHandle, envBranch) {
59
70
  const { session, config } = await login(root);
60
- const envVariables = (await getStorefrontEnvVariables(session, config.storefront.id, envBranch))?.environmentVariables || [];
71
+ if (envBranch) {
72
+ const environments = (await getStorefrontEnvironments(session, config.storefront.id))?.environments || [];
73
+ envHandle = findEnvironmentByBranchOrThrow(environments, envBranch).handle;
74
+ }
75
+ const envVariables = (await getStorefrontEnvVariables(session, config.storefront.id, envHandle))?.environmentVariables || [];
61
76
  const remoteVariables = {};
62
77
  const remoteSecrets = {};
63
78
  for (const { key, value, isSecret } of envVariables) {
@@ -68,5 +83,11 @@ async function getRemoteVariables(root, envBranch) {
68
83
  }
69
84
  return { remoteVariables, remoteSecrets };
70
85
  }
86
+ async function getLocalVariables(root) {
87
+ const dotEnvPath = resolvePath(root, ".env");
88
+ return await fileExists(dotEnvPath).then(
89
+ (exists) => exists ? readAndParseDotEnv(dotEnvPath) : { variables: {} }
90
+ );
91
+ }
71
92
 
72
- export { getAllEnvironmentVariables };
93
+ export { getAllEnvironmentVariables, getLocalVariables };
package/dist/lib/file.js CHANGED
@@ -13,7 +13,14 @@ async function replaceFileContent(filepath, formatConfig, replacer) {
13
13
  }
14
14
  return writeFile(filepath, content);
15
15
  }
16
- const DEFAULT_EXTENSIONS = ["tsx", "ts", "jsx", "js", "mjs", "cjs"];
16
+ const DEFAULT_EXTENSIONS = [
17
+ "tsx",
18
+ "ts",
19
+ "jsx",
20
+ "js",
21
+ "mjs",
22
+ "cjs"
23
+ ];
17
24
  async function findFileWithExtension(directory, fileBase, extensions = DEFAULT_EXTENSIONS) {
18
25
  const dirFiles = await readdir(directory);
19
26
  if (dirFiles.includes(fileBase)) {
package/dist/lib/flags.js CHANGED
@@ -5,9 +5,9 @@ import { normalizeStoreFqdn } from '@shopify/cli-kit/node/context/fqdn';
5
5
  import colors from '@shopify/cli-kit/node/colors';
6
6
  import { STYLING_CHOICES } from './setups/css/index.js';
7
7
  import { I18N_CHOICES } from './setups/i18n/index.js';
8
- import { DEFAULT_INSPECTOR_PORT } from './mini-oxygen/common.js';
9
8
 
10
- const DEFAULT_PORT = 3e3;
9
+ const DEFAULT_APP_PORT = 3e3;
10
+ const DEFAULT_INSPECTOR_PORT = 9229;
11
11
  const commonFlags = {
12
12
  path: {
13
13
  path: Flags.string({
@@ -17,9 +17,8 @@ const commonFlags = {
17
17
  },
18
18
  port: {
19
19
  port: Flags.integer({
20
- description: `The port to run the server on. Defaults to ${DEFAULT_PORT}.`,
21
- env: "SHOPIFY_HYDROGEN_FLAG_PORT",
22
- default: DEFAULT_PORT
20
+ description: `The port to run the server on. Defaults to ${DEFAULT_APP_PORT}.`,
21
+ env: "SHOPIFY_HYDROGEN_FLAG_PORT"
23
22
  })
24
23
  },
25
24
  legacyRuntime: {
@@ -50,17 +49,23 @@ const commonFlags = {
50
49
  allowNo: true
51
50
  })
52
51
  },
52
+ env: {
53
+ env: Flags.string({
54
+ description: "Specifies the environment to perform the operation using its handle. Fetch the handle using the `env list` command.",
55
+ exclusive: ["env-branch"]
56
+ })
57
+ },
58
+ /**
59
+ * @deprecated use `env` instead.
60
+ */
53
61
  envBranch: {
54
62
  "env-branch": Flags.string({
55
- description: "Specifies the environment to pull variables from using its Git branch name.",
63
+ description: "Specifies the environment to perform the operation using its Git branch name.",
56
64
  env: "SHOPIFY_HYDROGEN_ENVIRONMENT_BRANCH",
57
- char: "e"
58
- })
59
- },
60
- env: {
61
- env: Flags.string({
62
- description: "Specifies an environment's name when using remote environment variables.",
63
- env: "SHOPIFY_HYDROGEN_ENVIRONMENT_NAME"
65
+ deprecated: {
66
+ to: "env",
67
+ message: "--env-branch is deprecated. Use --env instead."
68
+ }
64
69
  })
65
70
  },
66
71
  sourcemap: {
@@ -118,8 +123,7 @@ const commonFlags = {
118
123
  inspectorPort: {
119
124
  "inspector-port": Flags.integer({
120
125
  description: `The port where the inspector is available. Defaults to ${DEFAULT_INSPECTOR_PORT}.`,
121
- env: "SHOPIFY_HYDROGEN_FLAG_INSPECTOR_PORT",
122
- default: DEFAULT_INSPECTOR_PORT
126
+ env: "SHOPIFY_HYDROGEN_FLAG_INSPECTOR_PORT"
123
127
  })
124
128
  },
125
129
  diff: {
@@ -149,6 +153,23 @@ const commonFlags = {
149
153
  description: "Disables any warnings about missing standard routes.",
150
154
  env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_ROUTE_WARNING"
151
155
  })
156
+ },
157
+ customerAccountPush: {
158
+ "customer-account-push__unstable": Flags.boolean({
159
+ hidden: true,
160
+ description: "Use tunneling for local development and push the tunneling domain to admin. Required to use Customer Account API's Oauth flow",
161
+ required: false,
162
+ default: false,
163
+ env: "SHOPIFY_HYDROGEN_FLAG_CUSTOMER_ACCOUNT_PUSH"
164
+ })
165
+ },
166
+ verbose: {
167
+ verbose: Flags.boolean({
168
+ description: "Outputs more information about the command's execution.",
169
+ required: false,
170
+ default: false,
171
+ env: "SHOPIFY_HYDROGEN_FLAG_VERBOSE"
172
+ })
152
173
  }
153
174
  };
154
175
  function flagsToCamelObject(obj) {
@@ -202,4 +223,4 @@ function overrideFlag(flags, extra) {
202
223
  );
203
224
  }
204
225
 
205
- export { DEFAULT_PORT, commonFlags, deprecated, flagsToCamelObject, overrideFlag, parseProcessFlags };
226
+ export { DEFAULT_APP_PORT, DEFAULT_INSPECTOR_PORT, commonFlags, deprecated, flagsToCamelObject, overrideFlag, parseProcessFlags };
@@ -0,0 +1,29 @@
1
+ import { adminRequest } from './client.js';
2
+
3
+ const CUSTOMER_APPLICATION_URLS_REPLACE = `#graphql
4
+ mutation CustomerApplicationUrlsReplace($storefrontId: ID!, $urlsReplaceInput: HydrogenStorefrontCustomerApplicationUrlsReplaceInput!) {
5
+ hydrogenStorefrontCustomerApplicationUrlsReplace(
6
+ storefrontId: $storefrontId,
7
+ urlsReplaceInput: $urlsReplaceInput,
8
+ ) {
9
+ success
10
+ userErrors {
11
+ code
12
+ field
13
+ message
14
+ }
15
+ }
16
+ }
17
+ `;
18
+ async function replaceCustomerApplicationUrls(adminSession, storefrontId, urlsReplaceInput) {
19
+ const {
20
+ hydrogenStorefrontCustomerApplicationUrlsReplace: { success, userErrors }
21
+ } = await adminRequest(
22
+ CUSTOMER_APPLICATION_URLS_REPLACE,
23
+ adminSession,
24
+ { storefrontId, urlsReplaceInput }
25
+ );
26
+ return { success, userErrors };
27
+ }
28
+
29
+ export { CUSTOMER_APPLICATION_URLS_REPLACE, replaceCustomerApplicationUrls };
@@ -6,6 +6,7 @@ const GetDeploymentDataQuery = `#graphql
6
6
  oxygenDeploymentToken
7
7
  environments {
8
8
  name
9
+ handle
9
10
  branch
10
11
  type
11
12
  }
@@ -10,6 +10,7 @@ const ListEnvironmentsQuery = `#graphql
10
10
  createdAt
11
11
  id
12
12
  name
13
+ handle
13
14
  type
14
15
  url
15
16
  }