@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,21 +1,22 @@
1
1
  import path from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
- import { muteDevLogs, enhanceH2Logs } from '../../lib/log.js';
4
- import { commonFlags, overrideFlag, flagsToCamelObject } from '../../lib/flags.js';
3
+ import { setH2OVerbose, isH2Verbose, muteDevLogs, enhanceH2Logs } from '../../lib/log.js';
4
+ import { commonFlags, overrideFlag, flagsToCamelObject, DEFAULT_INSPECTOR_PORT, DEFAULT_APP_PORT } from '../../lib/flags.js';
5
5
  import Command from '@shopify/cli-kit/node/base-command';
6
6
  import colors from '@shopify/cli-kit/node/colors';
7
- import { renderInfo } from '@shopify/cli-kit/node/ui';
7
+ import { collectLog } from '@shopify/cli-kit/node/output';
8
+ import { renderSuccess } from '@shopify/cli-kit/node/ui';
8
9
  import { AbortError } from '@shopify/cli-kit/node/error';
9
10
  import { Flags } from '@oclif/core';
10
11
  import { spawnCodegenProcess } from '../../lib/codegen.js';
11
12
  import { getAllEnvironmentVariables } from '../../lib/environment-variables.js';
12
- import { getConfig } from '../../lib/shopify-config.js';
13
- import { checkRemixVersions } from '../../lib/remix-version-check.js';
14
13
  import { displayDevUpgradeNotice } from './upgrade.js';
15
14
  import { prepareDiffDirectory } from '../../lib/template-diff.js';
16
- import { setH2OPluginContext } from '../../lib/vite/shared.js';
17
- import { getGraphiQLUrl } from '../../lib/graphiql-url.js';
18
- import { getDebugBannerLine } from '../../lib/mini-oxygen/workerd.js';
15
+ import { getDevConfigInBackground, TUNNEL_DOMAIN, startTunnelAndPushConfig, getUtilityBannerlines, getDebugBannerLine, isMockShop, notifyIssueWithTunnelAndMockShop } from '../../lib/dev-shared.js';
16
+ import { getCliCommand } from '../../lib/shell.js';
17
+ import { findPort } from '../../lib/find-port.js';
18
+ import { logRequestLine } from '../../lib/mini-oxygen/common.js';
19
+ import { findHydrogenPlugin, findOxygenPlugin } from '../../lib/vite-config.js';
19
20
 
20
21
  class DevVite extends Command {
21
22
  static description = "Runs Hydrogen storefront in an Oxygen worker for development.";
@@ -28,8 +29,7 @@ class DevVite extends Command {
28
29
  ...commonFlags.codegen,
29
30
  "disable-virtual-routes": Flags.boolean({
30
31
  description: "Disable rendering fallback routes when a route file doesn't exist.",
31
- env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_VIRTUAL_ROUTES",
32
- default: false
32
+ env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_VIRTUAL_ROUTES"
33
33
  }),
34
34
  ...commonFlags.debug,
35
35
  ...commonFlags.inspectorPort,
@@ -38,28 +38,34 @@ class DevVite extends Command {
38
38
  default: false,
39
39
  required: false
40
40
  }),
41
+ ...commonFlags.env,
41
42
  ...commonFlags.envBranch,
42
43
  "disable-version-check": Flags.boolean({
43
44
  description: "Skip the version check when running `hydrogen dev`",
44
45
  default: false,
45
46
  required: false
46
47
  }),
47
- ...commonFlags.diff
48
+ ...commonFlags.diff,
49
+ ...commonFlags.customerAccountPush,
50
+ ...commonFlags.verbose
48
51
  };
52
+ static hidden = true;
49
53
  async run() {
50
54
  const { flags } = await this.parse(DevVite);
51
55
  let directory = flags.path ? path.resolve(flags.path) : process.cwd();
52
56
  if (flags.diff) {
53
57
  directory = await prepareDiffDirectory(directory, true);
54
58
  }
55
- await runDev({
59
+ await runViteDev({
56
60
  ...flagsToCamelObject(flags),
61
+ customerAccountPush: flags["customer-account-push__unstable"],
57
62
  path: directory,
58
- isLocalDev: flags.diff
63
+ isLocalDev: flags.diff,
64
+ cliConfig: this.config
59
65
  });
60
66
  }
61
67
  }
62
- async function runDev({
68
+ async function runViteDev({
63
69
  entry: ssrEntry,
64
70
  port: appPort,
65
71
  path: appPath,
@@ -68,92 +74,155 @@ async function runDev({
68
74
  codegenConfigPath,
69
75
  disableVirtualRoutes,
70
76
  envBranch,
77
+ env: envHandle,
71
78
  debug = false,
72
79
  disableVersionCheck = false,
73
80
  inspectorPort,
74
- isLocalDev = false
81
+ isLocalDev = false,
82
+ customerAccountPush: customerAccountPushFlag = false,
83
+ cliConfig,
84
+ verbose
75
85
  }) {
76
86
  if (!process.env.NODE_ENV)
77
87
  process.env.NODE_ENV = "development";
78
- muteDevLogs();
88
+ if (verbose)
89
+ setH2OVerbose();
90
+ if (!isH2Verbose())
91
+ muteDevLogs();
79
92
  const root = appPath ?? process.cwd();
80
- const envPromise = getConfig(root).then(({ shop, storefront }) => {
81
- const fetchRemote = !!shop && !!storefront?.id;
82
- return getAllEnvironmentVariables({ root, fetchRemote, envBranch });
83
- });
93
+ const cliCommandPromise = getCliCommand(root);
94
+ const backgroundPromise = getDevConfigInBackground(
95
+ root,
96
+ customerAccountPushFlag
97
+ );
98
+ const envPromise = backgroundPromise.then(
99
+ ({ fetchRemote, localVariables: localVariables2 }) => getAllEnvironmentVariables({
100
+ root,
101
+ envBranch,
102
+ envHandle,
103
+ fetchRemote,
104
+ localVariables: localVariables2
105
+ })
106
+ );
107
+ if (debug && !inspectorPort) {
108
+ inspectorPort = await findPort(DEFAULT_INSPECTOR_PORT);
109
+ }
84
110
  const vite = await import('vite');
85
111
  const fs = isLocalDev ? { allow: [root, fileURLToPath(new URL("../../../../", import.meta.url))] } : void 0;
112
+ const customLogger = vite.createLogger();
113
+ if (process.env.SHOPIFY_UNIT_TEST) {
114
+ customLogger.info = (msg) => collectLog("info", msg);
115
+ customLogger.warn = (msg) => collectLog("warn", msg);
116
+ customLogger.error = (msg) => collectLog("error", msg);
117
+ }
86
118
  const viteServer = await vite.createServer({
87
119
  root,
120
+ customLogger,
121
+ clearScreen: false,
88
122
  server: { fs, host: host ? true : void 0 },
89
- ...setH2OPluginContext({
90
- cliOptions: {
91
- debug,
92
- ssrEntry,
93
- envPromise,
94
- inspectorPort,
95
- disableVirtualRoutes
123
+ plugins: [
124
+ {
125
+ name: "hydrogen:cli",
126
+ configResolved(config) {
127
+ findHydrogenPlugin(config)?.api?.registerPluginOptions({
128
+ disableVirtualRoutes
129
+ });
130
+ findOxygenPlugin(config)?.api?.registerPluginOptions({
131
+ debug,
132
+ entry: ssrEntry,
133
+ envPromise: envPromise.then(({ allVariables }) => allVariables),
134
+ inspectorPort,
135
+ logRequestLine
136
+ });
137
+ },
138
+ configureServer: (viteDevServer) => {
139
+ if (customerAccountPushFlag) {
140
+ viteDevServer.middlewares.use((req, res, next) => {
141
+ const host2 = req.headers.host;
142
+ if (host2?.includes(TUNNEL_DOMAIN.ORIGINAL)) {
143
+ req.headers.host = host2.replace(
144
+ TUNNEL_DOMAIN.ORIGINAL,
145
+ TUNNEL_DOMAIN.REBRANDED
146
+ );
147
+ }
148
+ next();
149
+ });
150
+ }
151
+ }
96
152
  }
97
- })
153
+ ]
98
154
  });
99
- process.once("SIGTERM", async () => {
100
- try {
101
- await viteServer.close();
102
- } finally {
103
- process.exit();
104
- }
105
- });
106
- if (!viteServer.config.plugins.find((plugin) => plugin.name === "hydrogen:main")) {
155
+ const h2Plugin = findHydrogenPlugin(viteServer.config);
156
+ if (!h2Plugin) {
107
157
  await viteServer.close();
108
158
  throw new AbortError(
109
159
  "Hydrogen plugin not found.",
110
160
  "Add `hydrogen()` plugin to your Vite config."
111
161
  );
112
162
  }
163
+ const h2PluginOptions = h2Plugin.api?.getPluginOptions?.();
113
164
  const codegenProcess = useCodegen ? spawnCodegenProcess({
114
165
  rootDirectory: root,
115
- configFilePath: codegenConfigPath
166
+ configFilePath: codegenConfigPath,
167
+ appDirectory: h2PluginOptions?.remixConfig?.appDirectory
116
168
  }) : void 0;
117
169
  process.on("unhandledRejection", (err) => {
118
170
  console.log("Unhandled Rejection: ", err);
119
171
  });
120
- const publicPort = appPort ?? viteServer.config.server.port ?? 3e3;
121
- await viteServer.listen(publicPort);
172
+ const publicPort = appPort ?? viteServer.config.server.port ?? DEFAULT_APP_PORT;
173
+ const [tunnel, cliCommand] = await Promise.all([
174
+ backgroundPromise.then(
175
+ ({ customerAccountPush, storefrontId }) => customerAccountPush ? startTunnelAndPushConfig(root, cliConfig, publicPort, storefrontId) : void 0
176
+ ),
177
+ cliCommandPromise,
178
+ viteServer.listen(publicPort)
179
+ ]);
122
180
  const publicUrl = new URL(
123
181
  viteServer.resolvedUrls.local[0] ?? viteServer.resolvedUrls.network[0]
124
182
  );
125
- enhanceH2Logs({ rootDirectory: root, host: publicUrl.toString() });
126
- await envPromise;
183
+ const finalHost = tunnel?.host || publicUrl.toString() || publicUrl.origin;
184
+ enhanceH2Logs({
185
+ rootDirectory: root,
186
+ host: finalHost,
187
+ cliCommand
188
+ });
189
+ const { logInjectedVariables, localVariables } = await envPromise;
190
+ logInjectedVariables();
127
191
  console.log("");
128
192
  viteServer.printUrls();
129
193
  viteServer.bindCLIShortcuts({ print: true });
130
194
  console.log("\n");
131
- const infoLines = [];
132
- if (!disableVirtualRoutes) {
133
- infoLines.push(
134
- `${colors.dim("View GraphiQL API browser:")} ${getGraphiQLUrl({
135
- host: publicUrl.origin
136
- })}`,
137
- `
138
- ${colors.dim("View server network requests:")} ${publicUrl.origin}/subrequest-profiler`
139
- );
195
+ const customSections = [];
196
+ if (!h2PluginOptions?.disableVirtualRoutes) {
197
+ customSections.push({ body: getUtilityBannerlines(finalHost) });
140
198
  }
141
- if (debug) {
142
- infoLines.push({ warn: getDebugBannerLine(inspectorPort) });
143
- }
144
- if (infoLines.length > 0) {
145
- renderInfo({ body: infoLines });
199
+ if (debug && inspectorPort) {
200
+ customSections.push({
201
+ body: { warn: getDebugBannerLine(inspectorPort) }
202
+ });
146
203
  }
147
- checkRemixVersions();
204
+ const { storefrontTitle } = await backgroundPromise;
205
+ renderSuccess({
206
+ body: [
207
+ `View ${storefrontTitle ? colors.cyan(storefrontTitle) : "Hydrogen"} app:`,
208
+ { link: { url: finalHost } }
209
+ ],
210
+ customSections
211
+ });
148
212
  if (!disableVersionCheck) {
149
213
  displayDevUpgradeNotice({ targetPath: root });
150
214
  }
215
+ if (customerAccountPushFlag && isMockShop(localVariables)) {
216
+ notifyIssueWithTunnelAndMockShop(cliCommand);
217
+ }
151
218
  return {
219
+ getUrl: () => finalHost,
152
220
  async close() {
153
- codegenProcess?.kill(0);
154
- await viteServer.close();
221
+ codegenProcess?.removeAllListeners("close");
222
+ codegenProcess?.kill("SIGINT");
223
+ await Promise.allSettled([viteServer.close(), tunnel?.cleanup?.()]);
155
224
  }
156
225
  };
157
226
  }
158
227
 
159
- export { DevVite as default, runDev };
228
+ export { DevVite as default, runViteDev };
@@ -1,30 +1,33 @@
1
- import path from 'node:path';
2
1
  import fs from 'node:fs/promises';
3
2
  import { outputInfo, outputDebug } from '@shopify/cli-kit/node/output';
4
3
  import { fileExists } from '@shopify/cli-kit/node/fs';
5
4
  import { renderFatalError } from '@shopify/cli-kit/node/ui';
6
- import colors from '@shopify/cli-kit/node/colors';
5
+ import { resolvePath, relativePath } from '@shopify/cli-kit/node/path';
7
6
  import { copyPublicFiles } from './build.js';
8
7
  import { getProjectPaths, assertOxygenChecks, handleRemixImportFail, getRemixConfig } from '../../lib/remix-config.js';
9
- import { muteDevLogs, createRemixLogger, enhanceH2Logs } from '../../lib/log.js';
10
- import { commonFlags, deprecated, flagsToCamelObject } from '../../lib/flags.js';
8
+ import { setH2OVerbose, isH2Verbose, muteDevLogs, createRemixLogger, enhanceH2Logs } from '../../lib/log.js';
9
+ import { commonFlags, deprecated, flagsToCamelObject, DEFAULT_APP_PORT } from '../../lib/flags.js';
11
10
  import Command from '@shopify/cli-kit/node/base-command';
12
11
  import { Flags } from '@oclif/core';
13
12
  import { buildAssetsUrl, startMiniOxygen } from '../../lib/mini-oxygen/index.js';
14
13
  import { addVirtualRoutes } from '../../lib/virtual-routes.js';
15
14
  import { spawnCodegenProcess } from '../../lib/codegen.js';
16
15
  import { getAllEnvironmentVariables } from '../../lib/environment-variables.js';
17
- import { getConfig } from '../../lib/shopify-config.js';
18
16
  import { setupLiveReload } from '../../lib/live-reload.js';
19
17
  import { checkRemixVersions } from '../../lib/remix-version-check.js';
20
- import { getGraphiQLUrl } from '../../lib/graphiql-url.js';
21
18
  import { displayDevUpgradeNotice } from './upgrade.js';
22
19
  import { findPort } from '../../lib/find-port.js';
23
- import { prepareDiffDirectory } from '../../lib/template-diff.js';
20
+ import { prepareDiffDirectory, copyShopifyConfig } from '../../lib/template-diff.js';
21
+ import { getDevConfigInBackground, startTunnelAndPushConfig, isMockShop, notifyIssueWithTunnelAndMockShop } from '../../lib/dev-shared.js';
22
+ import { getCliCommand } from '../../lib/shell.js';
23
+ import { hasViteConfig } from '../../lib/vite-config.js';
24
24
 
25
25
  const LOG_REBUILDING = "\u{1F9F1} Rebuilding...";
26
26
  const LOG_REBUILT = "\u{1F680} Rebuilt";
27
27
  class Dev extends Command {
28
+ static descriptionWithMarkdown = `Runs a Hydrogen storefront in a local runtime that emulates an Oxygen worker for development.
29
+
30
+ If your project is [linked](https://shopify.dev/docs/api/shopify-cli/hydrogen/hydrogen-link) to a Hydrogen storefront, then its environment variables will be loaded with the runtime.`;
28
31
  static description = "Runs Hydrogen storefront in an Oxygen worker for development.";
29
32
  static flags = {
30
33
  ...commonFlags.path,
@@ -40,24 +43,43 @@ class Dev extends Command {
40
43
  }),
41
44
  ...commonFlags.debug,
42
45
  ...commonFlags.inspectorPort,
46
+ ...commonFlags.env,
43
47
  ...commonFlags.envBranch,
44
48
  "disable-version-check": Flags.boolean({
45
49
  description: "Skip the version check when running `hydrogen dev`",
46
50
  default: false,
47
51
  required: false
48
52
  }),
49
- ...commonFlags.diff
53
+ ...commonFlags.diff,
54
+ ...commonFlags.customerAccountPush,
55
+ ...commonFlags.verbose
50
56
  };
51
57
  async run() {
52
58
  const { flags } = await this.parse(Dev);
53
- let directory = flags.path ? path.resolve(flags.path) : process.cwd();
59
+ const originalDirectory = flags.path ? resolvePath(flags.path) : process.cwd();
60
+ let directory = originalDirectory;
54
61
  if (flags.diff) {
55
62
  directory = await prepareDiffDirectory(directory, true);
56
63
  }
57
- await runDev({
64
+ const devParams = {
58
65
  ...flagsToCamelObject(flags),
59
- path: directory
60
- });
66
+ customerAccountPush: flags["customer-account-push__unstable"],
67
+ path: directory,
68
+ cliConfig: this.config
69
+ };
70
+ const { close } = await hasViteConfig(directory ?? process.cwd()) ? await import('./dev-vite.js').then(
71
+ ({ runViteDev }) => runViteDev(devParams)
72
+ ) : await runDev(devParams);
73
+ let closingPromise;
74
+ const processExit = process.exit;
75
+ process.exit = async (code) => {
76
+ closingPromise ??= close();
77
+ await closingPromise;
78
+ return processExit(code);
79
+ };
80
+ if (flags.diff) {
81
+ await copyShopifyConfig(directory, originalDirectory);
82
+ }
61
83
  }
62
84
  }
63
85
  async function runDev({
@@ -67,17 +89,26 @@ async function runDev({
67
89
  legacyRuntime = false,
68
90
  codegenConfigPath,
69
91
  disableVirtualRoutes,
92
+ env: envHandle,
70
93
  envBranch,
71
94
  debug = false,
72
95
  sourcemap = true,
73
96
  disableVersionCheck = false,
74
- inspectorPort
97
+ inspectorPort,
98
+ customerAccountPush: customerAccountPushFlag = false,
99
+ shouldLiveReload = true,
100
+ cliConfig,
101
+ verbose
75
102
  }) {
76
103
  if (!process.env.NODE_ENV)
77
104
  process.env.NODE_ENV = "development";
78
- muteDevLogs();
105
+ if (verbose)
106
+ setH2OVerbose();
107
+ if (!isH2Verbose())
108
+ muteDevLogs();
79
109
  const { root, publicPath, buildPathClient, buildPathWorkerFile } = getProjectPaths(appPath);
80
- const copyingFiles = copyPublicFiles(publicPath, buildPathClient);
110
+ const copyFilesPromise = copyPublicFiles(publicPath, buildPathClient);
111
+ const cliCommandPromise = getCliCommand(root);
81
112
  const reloadConfig = async () => {
82
113
  const config = await getRemixConfig(root);
83
114
  return disableVirtualRoutes ? config : addVirtualRoutes(config).catch((error) => {
@@ -88,23 +119,42 @@ async function runDev({
88
119
  });
89
120
  };
90
121
  const getFilePaths = (file) => {
91
- const fileRelative = path.relative(root, file);
92
- return [fileRelative, path.resolve(root, fileRelative)];
122
+ const fileRelative = relativePath(root, file);
123
+ return [fileRelative, resolvePath(root, fileRelative)];
93
124
  };
94
125
  const serverBundleExists = () => fileExists(buildPathWorkerFile);
95
- inspectorPort = debug ? await findPort(inspectorPort) : inspectorPort;
96
- appPort = legacyRuntime ? appPort : await findPort(appPort);
126
+ if (!appPort) {
127
+ appPort = await findPort(DEFAULT_APP_PORT);
128
+ }
97
129
  const assetsPort = legacyRuntime ? 0 : await findPort(appPort + 100);
98
130
  if (assetsPort) {
99
- process.env.HYDROGEN_ASSET_BASE_URL = buildAssetsUrl(assetsPort);
131
+ process.env.HYDROGEN_ASSET_BASE_URL = await buildAssetsUrl(assetsPort);
100
132
  }
101
- const [remixConfig, { shop, storefront }] = await Promise.all([
102
- reloadConfig(),
103
- getConfig(root)
104
- ]);
133
+ const backgroundPromise = getDevConfigInBackground(
134
+ root,
135
+ customerAccountPushFlag
136
+ );
137
+ const tunnelPromise = cliConfig && backgroundPromise.then(({ customerAccountPush, storefrontId }) => {
138
+ if (customerAccountPush) {
139
+ return startTunnelAndPushConfig(
140
+ root,
141
+ cliConfig,
142
+ appPort,
143
+ storefrontId
144
+ );
145
+ }
146
+ });
147
+ const remixConfig = await reloadConfig();
105
148
  assertOxygenChecks(remixConfig);
106
- const fetchRemote = !!shop && !!storefront?.id;
107
- const envPromise = getAllEnvironmentVariables({ root, fetchRemote, envBranch });
149
+ const envPromise = backgroundPromise.then(
150
+ ({ fetchRemote, localVariables }) => getAllEnvironmentVariables({
151
+ root,
152
+ fetchRemote,
153
+ envBranch,
154
+ envHandle,
155
+ localVariables
156
+ })
157
+ );
108
158
  const [{ watch }, { createFileWatchCache }] = await Promise.all([
109
159
  import('@remix-run/dev/dist/compiler/watch.js'),
110
160
  import('@remix-run/dev/dist/compiler/fileWatchCache.js')
@@ -112,43 +162,37 @@ async function runDev({
112
162
  let isInitialBuild = true;
113
163
  let initialBuildDurationMs = 0;
114
164
  let initialBuildStartTimeMs = Date.now();
115
- const liveReload = await setupLiveReload(remixConfig.dev?.port ?? 8002) ;
165
+ const liveReload = shouldLiveReload ? await setupLiveReload(remixConfig.dev?.port ?? 8002) : void 0;
116
166
  let miniOxygen;
117
167
  let codegenProcess;
118
168
  async function safeStartMiniOxygen() {
119
169
  if (miniOxygen)
120
170
  return;
171
+ const { allVariables, localVariables, logInjectedVariables } = await envPromise;
121
172
  miniOxygen = await startMiniOxygen(
122
173
  {
123
174
  root,
124
175
  debug,
176
+ appPort,
125
177
  assetsPort,
126
178
  inspectorPort,
127
- port: appPort,
128
179
  watch: !liveReload,
129
180
  buildPathWorkerFile,
130
181
  buildPathClient,
131
- env: await envPromise
182
+ env: allVariables
132
183
  },
133
184
  legacyRuntime
134
185
  );
135
- enhanceH2Logs({ host: miniOxygen.listeningAt, ...remixConfig });
186
+ logInjectedVariables();
187
+ const host = (await tunnelPromise)?.host ?? miniOxygen.listeningAt;
188
+ const cliCommand = await cliCommandPromise;
189
+ enhanceH2Logs({ host, cliCommand, ...remixConfig });
190
+ const { storefrontTitle } = await backgroundPromise;
136
191
  miniOxygen.showBanner({
137
- appName: storefront ? colors.cyan(storefront?.title) : void 0,
192
+ appName: storefrontTitle,
138
193
  headlinePrefix: initialBuildDurationMs > 0 ? `Initial build: ${initialBuildDurationMs}ms
139
194
  ` : "",
140
- extraLines: [
141
- colors.dim(
142
- `
143
- View GraphiQL API browser: ${getGraphiQLUrl({
144
- host: miniOxygen.listeningAt
145
- })}`
146
- ),
147
- colors.dim(
148
- `
149
- View server network requests: ${miniOxygen.listeningAt}/subrequest-profiler`
150
- )
151
- ]
195
+ host
152
196
  });
153
197
  if (useCodegen) {
154
198
  codegenProcess = spawnCodegenProcess({
@@ -160,6 +204,9 @@ View server network requests: ${miniOxygen.listeningAt}/subrequest-profiler`
160
204
  if (!disableVersionCheck) {
161
205
  displayDevUpgradeNotice({ targetPath: appPath });
162
206
  }
207
+ if (customerAccountPushFlag && isMockShop(localVariables)) {
208
+ notifyIssueWithTunnelAndMockShop(cliCommand);
209
+ }
163
210
  }
164
211
  const fileWatchCache = createFileWatchCache();
165
212
  let skipRebuildLogs = false;
@@ -185,7 +232,7 @@ View server network requests: ${miniOxygen.listeningAt}/subrequest-profiler`
185
232
  onBuildManifest: liveReload?.onBuildManifest,
186
233
  async onBuildFinish(context, duration, succeeded) {
187
234
  if (isInitialBuild) {
188
- await copyingFiles;
235
+ await copyFilesPromise;
189
236
  initialBuildDurationMs = Date.now() - initialBuildStartTimeMs;
190
237
  isInitialBuild = false;
191
238
  } else if (!skipRebuildLogs) {
@@ -230,12 +277,16 @@ View server network requests: ${miniOxygen.listeningAt}/subrequest-profiler`
230
277
  \u{1F4C4} File changed: ${relative}`);
231
278
  if (relative.endsWith(".env")) {
232
279
  skipRebuildLogs = true;
280
+ const { fetchRemote } = await backgroundPromise;
281
+ const { allVariables, logInjectedVariables } = await getAllEnvironmentVariables({
282
+ root,
283
+ fetchRemote,
284
+ envBranch,
285
+ envHandle
286
+ });
287
+ logInjectedVariables();
233
288
  await miniOxygen.reload({
234
- env: await getAllEnvironmentVariables({
235
- root,
236
- fetchRemote,
237
- envBranch
238
- })
289
+ env: allVariables
239
290
  });
240
291
  }
241
292
  if (absolute.startsWith(publicPath)) {
@@ -257,9 +308,15 @@ View server network requests: ${miniOxygen.listeningAt}/subrequest-profiler`
257
308
  }
258
309
  );
259
310
  return {
311
+ getUrl: () => miniOxygen.listeningAt,
260
312
  async close() {
261
- codegenProcess?.kill(0);
262
- await Promise.all([closeWatcher(), miniOxygen?.close()]);
313
+ codegenProcess?.removeAllListeners("close");
314
+ codegenProcess?.kill("SIGINT");
315
+ await Promise.allSettled([
316
+ closeWatcher(),
317
+ miniOxygen?.close(),
318
+ Promise.resolve(tunnelPromise).then((tunnel) => tunnel?.cleanup?.())
319
+ ]);
263
320
  }
264
321
  };
265
322
  }
@@ -1,16 +1,17 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
2
  import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
3
3
  import { pluralize } from '@shopify/cli-kit/common/string';
4
- import colors from '@shopify/cli-kit/node/colors';
5
- import { outputNewline, outputInfo, outputContent } from '@shopify/cli-kit/node/output';
4
+ import { outputInfo, outputNewline, outputContent } from '@shopify/cli-kit/node/output';
6
5
  import { linkStorefront } from '../link.js';
7
6
  import { commonFlags } from '../../../lib/flags.js';
8
7
  import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-environments.js';
8
+ import { createEnvironmentCliChoiceLabel } from '../../../lib/common.js';
9
9
  import { renderMissingLink, renderMissingStorefront } from '../../../lib/render-errors.js';
10
10
  import { login } from '../../../lib/auth.js';
11
11
  import { getCliCommand } from '../../../lib/shell.js';
12
12
 
13
13
  class EnvList extends Command {
14
+ static descriptionWithMarkdown = "Lists all environments available on the linked Hydrogen storefront.";
14
15
  static description = "List the environments on your linked Hydrogen storefront.";
15
16
  static flags = {
16
17
  ...commonFlags.path
@@ -60,25 +61,23 @@ async function runEnvList({ path: root = process.cwd() }) {
60
61
  1
61
62
  );
62
63
  storefront.environments.push(previewEnvironment[0]);
63
- outputNewline();
64
64
  outputInfo(
65
65
  pluralizedEnvironments({
66
66
  environments: storefront.environments,
67
67
  storefrontTitle: configStorefront.title
68
68
  }).toString()
69
69
  );
70
- storefront.environments.forEach(({ name, branch, type, url }) => {
70
+ storefront.environments.forEach(({ name, handle, branch, type, url }) => {
71
71
  outputNewline();
72
72
  const environmentUrl = type === "PRODUCTION" ? storefront.productionUrl : url;
73
73
  outputInfo(
74
- outputContent`${colors.whiteBright(name)}${branch ? ` ${colors.dim(`(Branch: ${branch})`)}` : ""}`.value
74
+ outputContent`${createEnvironmentCliChoiceLabel(name, handle, branch)}`.value
75
75
  );
76
76
  if (environmentUrl) {
77
- outputInfo(
78
- outputContent` ${colors.whiteBright(environmentUrl)}`.value
79
- );
77
+ outputInfo(outputContent` ${environmentUrl}`.value);
80
78
  }
81
79
  });
80
+ outputNewline();
82
81
  }
83
82
  const pluralizedEnvironments = ({
84
83
  environments,
@@ -9,13 +9,17 @@ import colors from '@shopify/cli-kit/node/colors';
9
9
  import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
10
10
  import { login } from '../../../lib/auth.js';
11
11
  import { getCliCommand } from '../../../lib/shell.js';
12
+ import { findEnvironmentOrThrow, findEnvironmentByBranchOrThrow } from '../../../lib/common.js';
12
13
  import { renderMissingLink, renderMissingStorefront } from '../../../lib/render-errors.js';
13
14
  import { linkStorefront } from '../link.js';
15
+ import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-environments.js';
14
16
  import { getStorefrontEnvVariables } from '../../../lib/graphql/admin/pull-variables.js';
15
17
 
16
18
  class EnvPull extends Command {
19
+ static descriptionWithMarkdown = "Pulls environment variables from the linked Hydrogen storefront and writes them to an `.env` file.";
17
20
  static description = "Populate your .env with variables from your Hydrogen storefront.";
18
21
  static flags = {
22
+ ...commonFlags.env,
19
23
  ...commonFlags.envBranch,
20
24
  ...commonFlags.path,
21
25
  ...commonFlags.force
@@ -26,6 +30,7 @@ class EnvPull extends Command {
26
30
  }
27
31
  }
28
32
  async function runEnvPull({
33
+ env: envHandle,
29
34
  envBranch,
30
35
  path: root = process.cwd(),
31
36
  force
@@ -49,10 +54,21 @@ async function runEnvPull({
49
54
  }
50
55
  if (!config.storefront?.id)
51
56
  return;
57
+ if (envHandle || envBranch) {
58
+ const environments = (await getStorefrontEnvironments(session, config.storefront.id))?.environments || [];
59
+ if (envHandle) {
60
+ findEnvironmentOrThrow(environments, envHandle);
61
+ } else if (envBranch) {
62
+ envHandle = findEnvironmentByBranchOrThrow(
63
+ environments,
64
+ envBranch
65
+ ).handle;
66
+ }
67
+ }
52
68
  const storefront = await getStorefrontEnvVariables(
53
69
  session,
54
70
  config.storefront.id,
55
- envBranch
71
+ envHandle
56
72
  );
57
73
  if (!storefront) {
58
74
  renderMissingStorefront({