@shopify/cli-hydrogen 8.0.4 → 8.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/dist/{lib/setups/i18n/templates → assets/hydrogen/i18n}/domains.ts +1 -1
  2. package/dist/assets/hydrogen/i18n/mock-i18n-types.ts +3 -0
  3. package/dist/{lib/setups/i18n/templates → assets/hydrogen/i18n}/subdomains.ts +1 -1
  4. package/dist/{lib/setups/i18n/templates → assets/hydrogen/i18n}/subfolders.ts +1 -1
  5. package/dist/{generator-templates → assets/hydrogen}/starter/.eslintrc.cjs +1 -0
  6. package/dist/{generator-templates → assets/hydrogen}/starter/CHANGELOG.md +31 -0
  7. package/dist/assets/hydrogen/starter/app/components/Aside.tsx +76 -0
  8. package/dist/{generator-templates → assets/hydrogen}/starter/app/components/Cart.tsx +38 -14
  9. package/dist/{generator-templates → assets/hydrogen}/starter/app/components/Footer.tsx +30 -13
  10. package/dist/{generator-templates → assets/hydrogen}/starter/app/components/Header.tsx +52 -12
  11. package/dist/{generator-templates/starter/app/components/Layout.tsx → assets/hydrogen/starter/app/components/PageLayout.tsx} +38 -28
  12. package/dist/{generator-templates → assets/hydrogen}/starter/app/components/Search.tsx +5 -3
  13. package/dist/{generator-templates → assets/hydrogen}/starter/app/entry.server.tsx +8 -2
  14. package/dist/{generator-templates → assets/hydrogen}/starter/app/lib/fragments.ts +70 -0
  15. package/dist/assets/hydrogen/starter/app/root.tsx +204 -0
  16. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/_index.tsx +62 -25
  17. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.addresses.tsx +0 -1
  18. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.orders.$id.tsx +2 -2
  19. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.profile.tsx +0 -1
  20. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/api.predictive-search.tsx +6 -1
  21. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +34 -8
  22. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/blogs.$blogHandle._index.tsx +36 -10
  23. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/blogs._index.tsx +35 -12
  24. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/cart.tsx +7 -7
  25. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/collections.$handle.tsx +49 -7
  26. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/collections._index.tsx +32 -6
  27. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/collections.all.tsx +31 -6
  28. package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +84 -0
  29. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/products.$handle.tsx +82 -17
  30. package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/search.tsx +4 -1
  31. package/dist/{generator-templates → assets/hydrogen}/starter/app/styles/app.css +22 -4
  32. package/dist/{generator-templates → assets/hydrogen}/starter/env.d.ts +1 -0
  33. package/dist/{generator-templates → assets/hydrogen}/starter/package.json +10 -10
  34. package/dist/{generator-templates → assets/hydrogen}/starter/storefrontapi.generated.d.ts +3 -1
  35. package/dist/{generator-templates → assets/hydrogen}/starter/vite.config.ts +15 -0
  36. package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/routes/index.jsx +0 -10
  37. package/dist/{generator-templates/assets → assets/hydrogen}/vite/vite.config.js +15 -0
  38. package/dist/commands/hydrogen/build.js +119 -36
  39. package/dist/commands/hydrogen/debug/cpu.js +74 -64
  40. package/dist/commands/hydrogen/deploy.js +4 -4
  41. package/dist/commands/hydrogen/dev.js +133 -51
  42. package/dist/{init.d.ts → commands/hydrogen/init.d.ts} +5 -5
  43. package/dist/commands/hydrogen/init.js +17 -8
  44. package/dist/commands/hydrogen/preview.js +101 -13
  45. package/dist/commands/hydrogen/setup/vite.js +2 -2
  46. package/dist/commands/hydrogen/setup.js +1 -1
  47. package/dist/commands/hydrogen/upgrade.js +53 -50
  48. package/dist/index.d.ts +403 -0
  49. package/dist/index.js +61 -0
  50. package/dist/lib/build.js +48 -37
  51. package/dist/lib/bundle/analyzer.js +2 -4
  52. package/dist/lib/check-version.js +38 -18
  53. package/dist/lib/classic-compiler/build.js +11 -4
  54. package/dist/lib/classic-compiler/debug-cpu.js +52 -0
  55. package/dist/lib/classic-compiler/dev.js +12 -5
  56. package/dist/lib/codegen.js +34 -9
  57. package/dist/lib/cpu-profiler.js +29 -12
  58. package/dist/lib/defer.js +13 -7
  59. package/dist/lib/deps-optimizer.js +146 -0
  60. package/dist/lib/flags.js +2 -7
  61. package/dist/lib/format-code.js +1 -2
  62. package/dist/lib/import-utils.js +22 -0
  63. package/dist/lib/live-reload.js +15 -5
  64. package/dist/lib/log.js +24 -2
  65. package/dist/lib/mini-oxygen/index.js +6 -2
  66. package/dist/lib/mini-oxygen/node.js +18 -4
  67. package/dist/lib/mini-oxygen/workerd.js +18 -5
  68. package/dist/lib/onboarding/local.js +1 -1
  69. package/dist/lib/onboarding/setup-template.mocks.js +7 -11
  70. package/dist/lib/remix-config.js +11 -9
  71. package/dist/lib/remix-version-check.js +8 -13
  72. package/dist/lib/resource-cleanup.js +13 -0
  73. package/dist/lib/setups/css/assets.js +3 -3
  74. package/dist/lib/setups/css/css-modules.js +2 -2
  75. package/dist/lib/setups/css/postcss.js +2 -2
  76. package/dist/lib/setups/css/tailwind.js +2 -2
  77. package/dist/lib/setups/css/vanilla-extract.js +2 -2
  78. package/dist/lib/setups/i18n/index.js +3 -5
  79. package/dist/lib/setups/routes/generate.js +17 -16
  80. package/dist/lib/template-diff.js +120 -42
  81. package/dist/lib/template-downloader.js +6 -11
  82. package/dist/lib/transpile/morph/typedefs.js +17 -0
  83. package/dist/lib/virtual-routes.js +2 -2
  84. package/dist/lib/vite-config.js +2 -1
  85. package/oclif.manifest.json +89 -1
  86. package/package.json +13 -9
  87. package/dist/generator-templates/starter/app/components/Aside.tsx +0 -47
  88. package/dist/generator-templates/starter/app/lib/root-data.ts +0 -11
  89. package/dist/generator-templates/starter/app/root.tsx +0 -227
  90. package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +0 -56
  91. package/dist/lib/setups/i18n/mock-i18n-types.js +0 -1
  92. package/dist/lib/setups/i18n/templates/domains.js +0 -14
  93. package/dist/lib/setups/i18n/templates/subdomains.js +0 -14
  94. package/dist/lib/setups/i18n/templates/subfolders.js +0 -13
  95. package/dist/lib/setups/routes/templates/locale-check.js +0 -9
  96. package/dist/virtual-routes/components/IconDiscord.jsx +0 -21
  97. /package/dist/{lib/bundle/bundle-analyzer.html → assets/hydrogen/bundle/analyzer.html} +0 -0
  98. /package/dist/{generator-templates/assets → assets/hydrogen}/css-modules/package.json +0 -0
  99. /package/dist/{generator-templates/assets → assets/hydrogen}/postcss/package.json +0 -0
  100. /package/dist/{generator-templates/assets → assets/hydrogen}/postcss/postcss.config.js +0 -0
  101. /package/dist/{lib/setups/routes/templates → assets/hydrogen/routes}/locale-check.ts +0 -0
  102. /package/dist/{generator-templates → assets/hydrogen}/starter/.eslintignore +0 -0
  103. /package/dist/{generator-templates → assets/hydrogen}/starter/.graphqlrc.yml +0 -0
  104. /package/dist/{generator-templates → assets/hydrogen}/starter/README.md +0 -0
  105. /package/dist/{generator-templates → assets/hydrogen}/starter/app/assets/favicon.svg +0 -0
  106. /package/dist/{generator-templates → assets/hydrogen}/starter/app/entry.client.tsx +0 -0
  107. /package/dist/{generator-templates → assets/hydrogen}/starter/app/graphql/customer-account/CustomerAddressMutations.ts +0 -0
  108. /package/dist/{generator-templates → assets/hydrogen}/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +0 -0
  109. /package/dist/{generator-templates → assets/hydrogen}/starter/app/graphql/customer-account/CustomerOrderQuery.ts +0 -0
  110. /package/dist/{generator-templates → assets/hydrogen}/starter/app/graphql/customer-account/CustomerOrdersQuery.ts +0 -0
  111. /package/dist/{generator-templates → assets/hydrogen}/starter/app/graphql/customer-account/CustomerUpdateMutation.ts +0 -0
  112. /package/dist/{generator-templates → assets/hydrogen}/starter/app/lib/search.ts +0 -0
  113. /package/dist/{generator-templates → assets/hydrogen}/starter/app/lib/session.ts +0 -0
  114. /package/dist/{generator-templates → assets/hydrogen}/starter/app/lib/variants.ts +0 -0
  115. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/$.tsx +0 -0
  116. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/[robots.txt].tsx +0 -0
  117. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/[sitemap.xml].tsx +0 -0
  118. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.$.tsx +0 -0
  119. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account._index.tsx +0 -0
  120. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.orders._index.tsx +0 -0
  121. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account.tsx +0 -0
  122. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account_.authorize.tsx +0 -0
  123. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account_.login.tsx +0 -0
  124. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/account_.logout.tsx +0 -0
  125. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/cart.$lines.tsx +0 -0
  126. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/discount.$code.tsx +0 -0
  127. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/policies.$handle.tsx +0 -0
  128. /package/dist/{generator-templates → assets/hydrogen}/starter/app/routes/policies._index.tsx +0 -0
  129. /package/dist/{generator-templates → assets/hydrogen}/starter/app/styles/reset.css +0 -0
  130. /package/dist/{generator-templates → assets/hydrogen}/starter/customer-accountapi.generated.d.ts +0 -0
  131. /package/dist/{generator-templates → assets/hydrogen}/starter/public/.gitkeep +0 -0
  132. /package/dist/{generator-templates → assets/hydrogen}/starter/server.ts +0 -0
  133. /package/dist/{generator-templates → assets/hydrogen}/starter/tsconfig.json +0 -0
  134. /package/dist/{generator-templates/assets → assets/hydrogen}/tailwind/package.json +0 -0
  135. /package/dist/{generator-templates/assets → assets/hydrogen}/tailwind/postcss.config.js +0 -0
  136. /package/dist/{generator-templates/assets → assets/hydrogen}/tailwind/tailwind.config.js +0 -0
  137. /package/dist/{generator-templates/assets → assets/hydrogen}/tailwind/tailwind.css +0 -0
  138. /package/dist/{generator-templates/assets → assets/hydrogen}/vanilla-extract/package.json +0 -0
  139. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/debug-network.css +0 -0
  140. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/favicon-dark.svg +0 -0
  141. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/favicon.svg +0 -0
  142. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/inter-variable-font.woff2 +0 -0
  143. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/jetbrainsmono-variable-font.woff2 +0 -0
  144. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/assets/styles.css +0 -0
  145. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/FlameChartWrapper.jsx +0 -0
  146. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/HydrogenLogoBaseBW.jsx +0 -0
  147. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/HydrogenLogoBaseColor.jsx +0 -0
  148. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconBanner.jsx +0 -0
  149. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconClose.jsx +0 -0
  150. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconDiscard.jsx +0 -0
  151. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconError.jsx +0 -0
  152. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconGithub.jsx +0 -0
  153. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/IconTwitter.jsx +0 -0
  154. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/Layout.jsx +0 -0
  155. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/RequestDetails.jsx +0 -0
  156. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/RequestTable.jsx +0 -0
  157. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/components/RequestWaterfall.jsx +0 -0
  158. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/lib/useDebugNetworkServer.jsx +0 -0
  159. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/routes/graphiql.jsx +0 -0
  160. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/routes/subrequest-profiler.jsx +0 -0
  161. /package/dist/{virtual-routes → assets/hydrogen/virtual-routes}/virtual-root.jsx +0 -0
  162. /package/dist/{generator-templates/assets → assets/hydrogen}/vite/package.json +0 -0
@@ -1,23 +1,28 @@
1
- import { fileURLToPath } from 'node:url';
2
- import { setH2OVerbose, isH2Verbose, muteDevLogs, enhanceH2Logs } from '../../lib/log.js';
3
- import { commonFlags, overrideFlag, deprecated, flagsToCamelObject, DEFAULT_INSPECTOR_PORT, DEFAULT_APP_PORT } from '../../lib/flags.js';
1
+ import { Flags } from '@oclif/core';
4
2
  import Command from '@shopify/cli-kit/node/base-command';
5
3
  import colors from '@shopify/cli-kit/node/colors';
6
- import { resolvePath } from '@shopify/cli-kit/node/path';
4
+ import { resolvePath, joinPath } from '@shopify/cli-kit/node/path';
7
5
  import { collectLog } from '@shopify/cli-kit/node/output';
8
- import { renderSuccess } from '@shopify/cli-kit/node/ui';
6
+ import { renderSuccess, renderInfo } from '@shopify/cli-kit/node/ui';
9
7
  import { AbortError } from '@shopify/cli-kit/node/error';
10
- import { Flags } from '@oclif/core';
8
+ import { removeFile } from '@shopify/cli-kit/node/fs';
9
+ import { setH2OVerbose, isH2Verbose, muteDevLogs, enhanceH2Logs } from '../../lib/log.js';
10
+ import { commonFlags, overrideFlag, deprecated, flagsToCamelObject, DEFAULT_INSPECTOR_PORT, DEFAULT_APP_PORT } from '../../lib/flags.js';
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, copyShopifyConfig } from '../../lib/template-diff.js';
15
- import { getDevConfigInBackground, TUNNEL_DOMAIN, startTunnelAndPushConfig, getUtilityBannerlines, getDebugBannerLine, isMockShop, notifyIssueWithTunnelAndMockShop } from '../../lib/dev-shared.js';
14
+ import { prepareDiffDirectory } from '../../lib/template-diff.js';
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';
18
18
  import { logRequestLine } from '../../lib/mini-oxygen/common.js';
19
19
  import { hasViteConfig, findHydrogenPlugin, findOxygenPlugin } from '../../lib/vite-config.js';
20
20
  import { runClassicCompilerDev } from '../../lib/classic-compiler/dev.js';
21
+ import { importVite } from '../../lib/import-utils.js';
22
+ import { createEntryPointErrorHandler } from '../../lib/deps-optimizer.js';
23
+ import { getCodeFormatOptions } from '../../lib/format-code.js';
24
+ import { setupResourceCleanup } from '../../lib/resource-cleanup.js';
25
+ import { hydrogenPackagesPath } from '../../lib/build.js';
21
26
 
22
27
  class Dev extends Command {
23
28
  static descriptionWithMarkdown = `Runs a Hydrogen storefront in a local runtime that emulates an Oxygen worker for development.
@@ -53,6 +58,11 @@ class Dev extends Command {
53
58
  default: false,
54
59
  required: false
55
60
  }),
61
+ "disable-deps-optimizer": Flags.boolean({
62
+ description: "Disable adding dependencies to Vite's `ssr.optimizeDeps.include` automatically",
63
+ env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_DEPS_OPTIMIZER",
64
+ default: false
65
+ }),
56
66
  // For the classic compiler:
57
67
  worker: deprecated("--worker", { isBoolean: true }),
58
68
  ...overrideFlag(commonFlags.legacyRuntime, {
@@ -69,10 +79,8 @@ class Dev extends Command {
69
79
  async run() {
70
80
  const { flags } = await this.parse(Dev);
71
81
  const originalDirectory = flags.path ? resolvePath(flags.path) : process.cwd();
72
- let directory = originalDirectory;
73
- if (flags.diff) {
74
- directory = await prepareDiffDirectory(directory, true);
75
- }
82
+ const diff = flags.diff ? await prepareDiffDirectory(originalDirectory, true) : void 0;
83
+ const directory = diff?.targetDirectory ?? originalDirectory;
76
84
  const devParams = {
77
85
  ...flagsToCamelObject(flags),
78
86
  customerAccountPush: flags["customer-account-push__unstable"],
@@ -80,16 +88,13 @@ class Dev extends Command {
80
88
  cliConfig: this.config
81
89
  };
82
90
  const { close } = await hasViteConfig(directory) ? await runDev(devParams) : await runClassicCompilerDev(devParams);
83
- let closingPromise;
84
- const processExit = process.exit;
85
- process.exit = async (code) => {
86
- closingPromise ??= close();
87
- await closingPromise;
88
- return processExit(code);
89
- };
90
- if (flags.diff) {
91
- await copyShopifyConfig(directory, originalDirectory);
92
- }
91
+ setupResourceCleanup(async () => {
92
+ await close();
93
+ if (diff) {
94
+ await diff.copyShopifyConfig();
95
+ await diff.cleanup();
96
+ }
97
+ });
93
98
  }
94
99
  }
95
100
  async function runDev({
@@ -100,12 +105,12 @@ async function runDev({
100
105
  codegen: useCodegen = false,
101
106
  codegenConfigPath,
102
107
  disableVirtualRoutes,
108
+ disableDepsOptimizer = false,
103
109
  envBranch,
104
110
  env: envHandle,
105
111
  debug = false,
106
112
  disableVersionCheck = false,
107
113
  inspectorPort,
108
- isLocalDev = false,
109
114
  customerAccountPush: customerAccountPushFlag = false,
110
115
  cliConfig,
111
116
  verbose
@@ -134,19 +139,28 @@ async function runDev({
134
139
  if (debug && !inspectorPort) {
135
140
  inspectorPort = await findPort(DEFAULT_INSPECTOR_PORT);
136
141
  }
137
- const vite = await import('vite');
138
- const fs = isLocalDev ? { allow: [root, fileURLToPath(new URL("../../../../", import.meta.url))] } : void 0;
142
+ const vite = await importVite(root);
143
+ if (hydrogenPackagesPath) {
144
+ await removeFile(joinPath(root, "node_modules/.vite"));
145
+ }
139
146
  const customLogger = vite.createLogger();
140
147
  if (process.env.SHOPIFY_UNIT_TEST) {
141
148
  customLogger.info = (msg) => collectLog("info", msg);
142
149
  customLogger.warn = (msg) => collectLog("warn", msg);
143
150
  customLogger.error = (msg) => collectLog("error", msg);
144
151
  }
152
+ const formatOptionsPromise = Promise.resolve().then(
153
+ () => getCodeFormatOptions(root)
154
+ );
145
155
  const viteServer = await vite.createServer({
146
156
  root,
147
157
  customLogger,
148
158
  clearScreen: false,
149
- server: { fs, host: host ? true : void 0 },
159
+ server: {
160
+ host: host ? true : void 0,
161
+ // Allow Vite to read files from the Hydrogen packages in local development.
162
+ fs: hydrogenPackagesPath ? { allow: [root, hydrogenPackagesPath] } : void 0
163
+ },
150
164
  plugins: [
151
165
  {
152
166
  name: "hydrogen:cli",
@@ -159,7 +173,19 @@ async function runDev({
159
173
  entry: ssrEntry,
160
174
  envPromise: envPromise.then(({ allVariables: allVariables2 }) => allVariables2),
161
175
  inspectorPort,
162
- logRequestLine
176
+ logRequestLine,
177
+ entryPointErrorHandler: createEntryPointErrorHandler({
178
+ disableDepsOptimizer,
179
+ configFile: config.configFile,
180
+ formatOptionsPromise,
181
+ showSuccessBanner: () => showSuccessBanner({
182
+ disableVirtualRoutes,
183
+ debug,
184
+ inspectorPort,
185
+ finalHost,
186
+ storefrontTitle
187
+ })
188
+ })
163
189
  });
164
190
  },
165
191
  configureServer: (viteDevServer) => {
@@ -188,14 +214,19 @@ async function runDev({
188
214
  );
189
215
  }
190
216
  const h2PluginOptions = h2Plugin.api?.getPluginOptions?.();
191
- const codegenProcess = useCodegen ? spawnCodegenProcess({
192
- rootDirectory: root,
193
- configFilePath: codegenConfigPath,
194
- appDirectory: h2PluginOptions?.remixConfig?.appDirectory
195
- }) : void 0;
196
- process.on("unhandledRejection", (err) => {
197
- console.log("Unhandled Rejection: ", err);
198
- });
217
+ let codegenProcess;
218
+ const setupCodegen = useCodegen ? () => {
219
+ codegenProcess?.kill(0);
220
+ codegenProcess = spawnCodegenProcess({
221
+ rootDirectory: root,
222
+ configFilePath: codegenConfigPath,
223
+ appDirectory: h2PluginOptions?.remixConfig?.appDirectory
224
+ });
225
+ } : void 0;
226
+ setupCodegen?.();
227
+ if (hydrogenPackagesPath) {
228
+ setupMonorepoReload(viteServer, hydrogenPackagesPath, setupCodegen);
229
+ }
199
230
  const publicPort = appPort ?? viteServer.config.server.port ?? DEFAULT_APP_PORT;
200
231
  const [tunnel, cliCommand] = await Promise.all([
201
232
  backgroundPromise.then(
@@ -219,22 +250,13 @@ async function runDev({
219
250
  viteServer.printUrls();
220
251
  viteServer.bindCLIShortcuts({ print: true });
221
252
  console.log("\n");
222
- const customSections = [];
223
- if (!h2PluginOptions?.disableVirtualRoutes) {
224
- customSections.push({ body: getUtilityBannerlines(finalHost) });
225
- }
226
- if (debug && inspectorPort) {
227
- customSections.push({
228
- body: { warn: getDebugBannerLine(inspectorPort) }
229
- });
230
- }
231
- const { storefrontTitle } = await backgroundPromise;
232
- renderSuccess({
233
- body: [
234
- `View ${storefrontTitle ? colors.cyan(storefrontTitle) : "Hydrogen"} app:`,
235
- { link: { url: finalHost } }
236
- ],
237
- customSections
253
+ const storefrontTitle = (await backgroundPromise).storefrontTitle;
254
+ showSuccessBanner({
255
+ disableVirtualRoutes,
256
+ debug,
257
+ inspectorPort,
258
+ finalHost,
259
+ storefrontTitle
238
260
  });
239
261
  if (!disableVersionCheck) {
240
262
  displayDevUpgradeNotice({ targetPath: root });
@@ -251,5 +273,65 @@ async function runDev({
251
273
  }
252
274
  };
253
275
  }
276
+ function showSuccessBanner({
277
+ disableVirtualRoutes,
278
+ debug,
279
+ inspectorPort,
280
+ finalHost,
281
+ storefrontTitle
282
+ }) {
283
+ const customSections = [];
284
+ if (!disableVirtualRoutes) {
285
+ customSections.push({ body: getUtilityBannerlines(finalHost) });
286
+ }
287
+ if (debug && inspectorPort) {
288
+ customSections.push({
289
+ body: { warn: getDebugBannerLine(inspectorPort) }
290
+ });
291
+ }
292
+ renderSuccess({
293
+ body: [
294
+ `View ${storefrontTitle ? colors.cyan(storefrontTitle) : "Hydrogen"} app:`,
295
+ { link: { url: finalHost } }
296
+ ],
297
+ customSections
298
+ });
299
+ }
300
+ function setupMonorepoReload(viteServer, monorepoPackagesPath, setupCodegen) {
301
+ viteServer.httpServer?.once("listening", () => {
302
+ viteServer.watcher.add(
303
+ monorepoPackagesPath + "hydrogen/dist/vite/plugin.js"
304
+ );
305
+ viteServer.watcher.add(
306
+ monorepoPackagesPath + "mini-oxygen/dist/vite/plugin.js"
307
+ );
308
+ viteServer.watcher.add(
309
+ monorepoPackagesPath + "mini-oxygen/dist/vite/worker-entry.js"
310
+ );
311
+ viteServer.watcher.add(
312
+ monorepoPackagesPath + "hydrogen-codegen/dist/esm/index.js"
313
+ );
314
+ viteServer.watcher.on("change", async (file) => {
315
+ if (file.includes(monorepoPackagesPath)) {
316
+ if (file.includes("/packages/hydrogen-codegen/")) {
317
+ if (setupCodegen) {
318
+ setupCodegen();
319
+ renderInfo({
320
+ headline: "The Hydrogen Codegen source has been modified.",
321
+ body: "The codegen process has been restarted."
322
+ });
323
+ }
324
+ } else {
325
+ await viteServer.restart(true);
326
+ console.log("");
327
+ renderInfo({
328
+ headline: "The H2O Vite plugins have been modified.",
329
+ body: "The Vite server has been restarted to reflect the changes."
330
+ });
331
+ }
332
+ }
333
+ });
334
+ });
335
+ }
254
336
 
255
337
  export { Dev as default, runDev };
@@ -2,10 +2,10 @@ import * as _oclif_core_lib_interfaces_parser_js from '@oclif/core/lib/interface
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
3
  import { PackageManager } from '@shopify/cli-kit/node/node-package-manager';
4
4
 
5
- declare const GENERATOR_SETUP_ASSETS_SUB_DIRS: readonly ["tailwind", "css-modules", "vanilla-extract", "postcss", "vite"];
6
- type AssetDir = (typeof GENERATOR_SETUP_ASSETS_SUB_DIRS)[number];
5
+ declare const ASSETS_STARTER_DIR = "starter";
6
+ type AssetsDir = 'tailwind' | 'css-modules' | 'vanilla-extract' | 'postcss' | 'vite' | 'i18n' | 'routes' | 'bundle' | 'virtual-routes' | 'internal-templates' | 'external-templates' | typeof ASSETS_STARTER_DIR;
7
7
 
8
- type CssStrategy = Exclude<AssetDir, 'vite'>;
8
+ type CssStrategy = Extract<AssetsDir, 'tailwind' | 'css-modules' | 'vanilla-extract' | 'postcss'>;
9
9
 
10
10
  declare const I18N_CHOICES: readonly ["subfolders", "domains", "subdomains", "none"];
11
11
  type I18nChoice = (typeof I18N_CHOICES)[number];
@@ -57,8 +57,8 @@ declare class Init extends Command {
57
57
  declare function runInit({ markets, ...options }?: InitOptions & {
58
58
  markets?: InitOptions['i18n'];
59
59
  }): Promise<{
60
- language?: "js" | "ts" | undefined;
61
- packageManager: "npm" | "yarn" | "pnpm" | "unknown" | "bun";
60
+ language?: "ts" | "js" | undefined;
61
+ packageManager: "unknown" | "yarn" | "npm" | "pnpm" | "bun";
62
62
  cssStrategy?: CssStrategy | undefined;
63
63
  cliCommand: "h2" | "yarn shopify hydrogen" | "pnpm shopify hydrogen" | "bun shopify hydrogen" | "npx shopify hydrogen";
64
64
  depsInstalled: boolean;
@@ -1,14 +1,15 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
- import { fileURLToPath } from 'node:url';
3
2
  import { packageManagerFromUserAgent } from '@shopify/cli-kit/node/node-package-manager';
4
3
  import { Flags } from '@oclif/core';
5
4
  import { AbortError } from '@shopify/cli-kit/node/error';
6
5
  import { commonFlags, flagsToCamelObject, parseProcessFlags } from '../../lib/flags.js';
7
6
  import { checkHydrogenVersion } from '../../lib/check-version.js';
8
7
  import { I18N_CHOICES } from '../../lib/setups/i18n/index.js';
9
- import { supressNodeExperimentalWarnings } from '../../lib/process.js';
8
+ import { supressNodeExperimentalWarnings, execAsync } from '../../lib/process.js';
10
9
  import { setupTemplate } from '../../lib/onboarding/index.js';
11
10
  import { LANGUAGES } from '../../lib/onboarding/common.js';
11
+ import { currentProcessIsGlobal, inferPackageManagerForGlobalCLI } from '@shopify/cli-kit/node/is-global';
12
+ import { getPkgJsonPath } from '../../lib/build.js';
12
13
 
13
14
  const FLAG_MAP = { f: "force" };
14
15
  class Init extends Command {
@@ -89,18 +90,26 @@ async function runInit({
89
90
  options.routes ??= true;
90
91
  options.shortcut ??= true;
91
92
  }
93
+ const npmPrefix = (await execAsync("npm prefix -s")).stdout.trim();
94
+ const isH2 = process.argv[1]?.startsWith(npmPrefix);
95
+ const isGlobal = currentProcessIsGlobal() && !isH2;
92
96
  const showUpgrade = await checkHydrogenVersion(
93
97
  // Resolving the CLI package from a local directory might fail because
94
98
  // this code could be run from a global dependency (e.g. on `npm create`).
95
99
  // Therefore, pass the known path to the package.json directly from here:
96
- fileURLToPath(new URL("../../../package.json", import.meta.url)),
97
- "cli"
100
+ await getPkgJsonPath(),
101
+ isGlobal ? "cli" : "cliHydrogen"
98
102
  );
99
103
  if (showUpgrade) {
100
- const packageManager = options.packageManager ?? packageManagerFromUserAgent();
101
- showUpgrade(
102
- packageManager === "unknown" ? "" : `Please use the latest version with \`${packageManager} create @shopify/hydrogen@latest\``
103
- );
104
+ let packageManager2 = options.packageManager ?? packageManagerFromUserAgent();
105
+ if (packageManager2 === "unknown" || !packageManager2) {
106
+ packageManager2 = inferPackageManagerForGlobalCLI();
107
+ }
108
+ const globalInstallCommand = packageManager2 === "yarn" ? `yarn global add @shopify/cli` : `${packageManager2} install -g @shopify/cli`;
109
+ const globalMessage = `Please install the latest Shopify CLI version with \`${globalInstallCommand}\` and try again.`;
110
+ const localMessage = `Please use the latest version with \`${packageManager2} create @shopify/hydrogen@latest\``;
111
+ const message = isGlobal ? globalMessage : localMessage;
112
+ showUpgrade(message);
104
113
  }
105
114
  return setupTemplate(options);
106
115
  }
@@ -1,13 +1,21 @@
1
+ import { Flags } from '@oclif/core';
1
2
  import Command from '@shopify/cli-kit/node/base-command';
3
+ import { AbortError } from '@shopify/cli-kit/node/error';
4
+ import { outputInfo } from '@shopify/cli-kit/node/output';
5
+ import { resolvePath, joinPath } from '@shopify/cli-kit/node/path';
2
6
  import { setH2OVerbose, isH2Verbose, muteDevLogs } from '../../lib/log.js';
3
7
  import { getProjectPaths, hasRemixConfigFile } from '../../lib/remix-config.js';
4
- import { commonFlags, deprecated, flagsToCamelObject, DEFAULT_APP_PORT } from '../../lib/flags.js';
8
+ import { commonFlags, deprecated, overrideFlag, flagsToCamelObject, DEFAULT_APP_PORT } from '../../lib/flags.js';
5
9
  import { startMiniOxygen } from '../../lib/mini-oxygen/index.js';
6
10
  import { getAllEnvironmentVariables } from '../../lib/environment-variables.js';
7
11
  import { getConfig } from '../../lib/shopify-config.js';
8
12
  import { findPort } from '../../lib/find-port.js';
9
- import { joinPath } from '@shopify/cli-kit/node/path';
10
13
  import { getViteConfig } from '../../lib/vite-config.js';
14
+ import { runBuild } from './build.js';
15
+ import { runClassicCompilerBuild } from '../../lib/classic-compiler/build.js';
16
+ import { setupResourceCleanup } from '../../lib/resource-cleanup.js';
17
+ import { deferPromise } from '../../lib/defer.js';
18
+ import { prepareDiffDirectory } from '../../lib/template-diff.js';
11
19
 
12
20
  class Preview extends Command {
13
21
  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.";
@@ -21,33 +29,103 @@ class Preview extends Command {
21
29
  ...commonFlags.envBranch,
22
30
  ...commonFlags.inspectorPort,
23
31
  ...commonFlags.debug,
24
- ...commonFlags.verbose
32
+ ...commonFlags.verbose,
33
+ // For building the app:
34
+ build: Flags.boolean({
35
+ description: "Builds the app before starting the preview server."
36
+ }),
37
+ watch: Flags.boolean({
38
+ description: "Watches for changes and rebuilds the project.",
39
+ dependsOn: ["build"]
40
+ }),
41
+ ...overrideFlag(commonFlags.entry, {
42
+ entry: { dependsOn: ["build"] }
43
+ }),
44
+ ...overrideFlag(commonFlags.codegen, {
45
+ codegen: { dependsOn: ["build"] }
46
+ }),
47
+ // Diff in preview only makes sense when combined with --build.
48
+ // Without the build flag, preview only needs access to the existing
49
+ // `dist` directory in the project, so there's no need to merge the
50
+ // project with the skeleton template in a temporary directory.
51
+ ...overrideFlag(commonFlags.diff, {
52
+ diff: { dependsOn: ["build"] }
53
+ })
25
54
  };
26
55
  async run() {
27
56
  const { flags } = await this.parse(Preview);
28
- await runPreview({
29
- ...flagsToCamelObject(flags)
57
+ const originalDirectory = flags.path ? resolvePath(flags.path) : process.cwd();
58
+ const diff = flags.build && flags.diff ? await prepareDiffDirectory(originalDirectory, flags.watch) : void 0;
59
+ const directory = diff?.targetDirectory ?? originalDirectory;
60
+ const { close } = await runPreview({
61
+ ...flagsToCamelObject(flags),
62
+ directory
63
+ });
64
+ setupResourceCleanup(async () => {
65
+ await close();
66
+ if (diff) {
67
+ await diff.copyDiffBuild();
68
+ await diff.cleanup();
69
+ }
30
70
  });
31
71
  }
32
72
  }
33
73
  async function runPreview({
34
74
  port: appPort,
35
- path: appPath,
75
+ directory,
36
76
  legacyRuntime = false,
37
77
  env: envHandle,
38
78
  envBranch,
39
79
  inspectorPort,
40
80
  debug,
41
- verbose
81
+ verbose,
82
+ build: shouldBuild = false,
83
+ watch = false,
84
+ codegen: useCodegen = false,
85
+ codegenConfigPath,
86
+ entry
42
87
  }) {
43
88
  if (!process.env.NODE_ENV)
44
- process.env.NODE_ENV = "production";
89
+ process.env.NODE_ENV = watch ? "development" : "production";
45
90
  if (verbose)
46
91
  setH2OVerbose();
47
92
  if (!isH2Verbose())
48
93
  muteDevLogs();
49
- let { root, buildPath, buildPathWorkerFile, buildPathClient } = getProjectPaths(appPath);
50
- if (!await hasRemixConfigFile(root)) {
94
+ let { root, buildPath, buildPathWorkerFile, buildPathClient } = getProjectPaths(directory);
95
+ const isClassicProject = await hasRemixConfigFile(root);
96
+ if (watch && isClassicProject) {
97
+ throw new AbortError(
98
+ "Preview in watch mode is not supported for classic Remix projects.",
99
+ "Please use the dev command instead, which is the equivalent for classic projects."
100
+ );
101
+ }
102
+ let miniOxygen;
103
+ const projectBuild = deferPromise();
104
+ const buildOptions = {
105
+ directory: root,
106
+ entry,
107
+ disableRouteWarning: false,
108
+ lockfileCheck: false,
109
+ sourcemap: true,
110
+ useCodegen,
111
+ codegenConfigPath
112
+ };
113
+ const buildProcess = shouldBuild ? isClassicProject ? await runClassicCompilerBuild({
114
+ ...buildOptions,
115
+ bundleStats: false
116
+ }).then(projectBuild.resolve) : await runBuild({
117
+ ...buildOptions,
118
+ watch,
119
+ async onServerBuildFinish() {
120
+ if (projectBuild.state === "pending") {
121
+ projectBuild.resolve();
122
+ } else {
123
+ outputInfo("\u{1F3D7}\uFE0F Project rebuilt. Reloading server...");
124
+ }
125
+ await miniOxygen?.reload();
126
+ }
127
+ }) : projectBuild.resolve();
128
+ if (!isClassicProject) {
51
129
  const maybeResult = await getViteConfig(root).catch(() => null);
52
130
  buildPathWorkerFile = maybeResult?.serverOutFile ?? joinPath(buildPath, "server", "index.js");
53
131
  }
@@ -65,8 +143,9 @@ async function runPreview({
65
143
  appPort = await findPort(DEFAULT_APP_PORT);
66
144
  }
67
145
  const assetsPort = legacyRuntime ? 0 : await findPort(appPort + 100);
146
+ await projectBuild.promise;
68
147
  logInjectedVariables();
69
- const miniOxygen = await startMiniOxygen(
148
+ miniOxygen = await startMiniOxygen(
70
149
  {
71
150
  root,
72
151
  appPort,
@@ -75,11 +154,20 @@ async function runPreview({
75
154
  buildPathClient,
76
155
  buildPathWorkerFile,
77
156
  inspectorPort,
78
- debug
157
+ debug,
158
+ watch
79
159
  },
80
160
  legacyRuntime
81
161
  );
82
- miniOxygen.showBanner({ mode: "preview" });
162
+ miniOxygen.showBanner({
163
+ mode: "preview",
164
+ headlinePrefix: watch ? "Watching for changes. " : ""
165
+ });
166
+ return {
167
+ async close() {
168
+ await Promise.allSettled([miniOxygen.close(), buildProcess?.close()]);
169
+ }
170
+ };
83
171
  }
84
172
 
85
173
  export { Preview as default, runPreview };
@@ -7,7 +7,7 @@ import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
7
7
  import { getRawRemixConfig } from '../../../lib/remix-config.js';
8
8
  import { replaceFileContent, mergePackageJson } from '../../../lib/file.js';
9
9
  import { importLangAstGrep } from '../../../lib/ast.js';
10
- import { getAssetDir } from '../../../lib/build.js';
10
+ import { getAssetsDir } from '../../../lib/build.js';
11
11
  import { getCodeFormatOptions, formatCode } from '../../../lib/format-code.js';
12
12
  import { hasViteConfig } from '../../../lib/vite-config.js';
13
13
  import { AbortError } from '@shopify/cli-kit/node/error';
@@ -47,7 +47,7 @@ async function runSetupVite({ directory }) {
47
47
  const serverEntry = rawRemixConfig.server || "server.js";
48
48
  const isTS = serverEntry.endsWith(".ts");
49
49
  const fileExt = isTS ? "tsx" : "jsx";
50
- const viteAssets = getAssetDir("vite");
50
+ const viteAssets = await getAssetsDir("vite");
51
51
  const usesTailwind = !!rawRemixConfig.tailwind;
52
52
  const postCssConfigPath = resolvePath(directory, "postcss.config.js");
53
53
  const handlePartialIssue = () => {
@@ -56,7 +56,7 @@ async function runSetup(options) {
56
56
  );
57
57
  let routes;
58
58
  if (needsRouteGeneration) {
59
- const templateRoot = getTemplateAppFile("..");
59
+ const templateRoot = await getTemplateAppFile("..");
60
60
  const [typescript, dtsFiles] = await Promise.all([
61
61
  fileExists(joinPath(rootDirectory, "tsconfig.json")),
62
62
  glob("*.d.ts", { cwd: templateRoot })