@shopify/cli-hydrogen 7.1.0 → 7.1.2

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 (72) hide show
  1. package/dist/commands/hydrogen/build-vite.js +131 -0
  2. package/dist/commands/hydrogen/build.js +6 -15
  3. package/dist/commands/hydrogen/check.js +1 -1
  4. package/dist/commands/hydrogen/codegen.js +3 -3
  5. package/dist/commands/hydrogen/debug/cpu.js +1 -1
  6. package/dist/commands/hydrogen/deploy.js +46 -31
  7. package/dist/commands/hydrogen/deploy.test.js +35 -49
  8. package/dist/commands/hydrogen/dev-vite.js +159 -0
  9. package/dist/commands/hydrogen/dev.js +11 -14
  10. package/dist/commands/hydrogen/env/list.js +1 -1
  11. package/dist/commands/hydrogen/env/pull.js +3 -3
  12. package/dist/commands/hydrogen/env/pull.test.js +2 -0
  13. package/dist/commands/hydrogen/env/push__unstable.js +190 -0
  14. package/dist/commands/hydrogen/env/push__unstable.test.js +383 -0
  15. package/dist/commands/hydrogen/generate/route.js +2 -2
  16. package/dist/commands/hydrogen/init.d.ts +69 -0
  17. package/dist/commands/hydrogen/init.js +5 -5
  18. package/dist/commands/hydrogen/init.test.js +2 -2
  19. package/dist/commands/hydrogen/link.js +2 -2
  20. package/dist/commands/hydrogen/list.js +1 -1
  21. package/dist/commands/hydrogen/login.js +2 -9
  22. package/dist/commands/hydrogen/logout.js +1 -1
  23. package/dist/commands/hydrogen/preview.js +15 -7
  24. package/dist/commands/hydrogen/setup/css.js +3 -3
  25. package/dist/commands/hydrogen/setup/markets.js +4 -4
  26. package/dist/commands/hydrogen/setup/vite.js +209 -0
  27. package/dist/commands/hydrogen/setup.js +8 -6
  28. package/dist/commands/hydrogen/unlink.js +1 -1
  29. package/dist/commands/hydrogen/upgrade.js +5 -3
  30. package/dist/generator-templates/assets/vite/package.json +15 -0
  31. package/dist/generator-templates/assets/vite/vite.config.js +13 -0
  32. package/dist/generator-templates/starter/CHANGELOG.md +49 -0
  33. package/dist/generator-templates/starter/app/components/Search.tsx +12 -7
  34. package/dist/generator-templates/starter/app/root.tsx +1 -2
  35. package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +8 -15
  36. package/dist/generator-templates/starter/package.json +9 -8
  37. package/dist/generator-templates/starter/public/.gitkeep +0 -0
  38. package/dist/lib/build.js +2 -1
  39. package/dist/lib/codegen.js +8 -3
  40. package/dist/lib/environment-variables.test.js +4 -2
  41. package/dist/lib/flags.js +149 -95
  42. package/dist/lib/graphql/admin/pull-variables.js +1 -0
  43. package/dist/lib/graphql/admin/pull-variables.test.js +7 -1
  44. package/dist/lib/graphql/admin/push-variables.js +35 -0
  45. package/dist/lib/log.js +1 -0
  46. package/dist/lib/mini-oxygen/common.js +2 -1
  47. package/dist/lib/mini-oxygen/node.js +2 -2
  48. package/dist/lib/mini-oxygen/workerd-inspector.js +1 -1
  49. package/dist/lib/mini-oxygen/workerd.js +29 -17
  50. package/dist/lib/onboarding/common.js +0 -3
  51. package/dist/lib/onboarding/local.js +4 -1
  52. package/dist/lib/onboarding/remote.js +16 -11
  53. package/dist/lib/remix-config.js +1 -1
  54. package/dist/lib/request-events.js +3 -3
  55. package/dist/lib/setups/css/assets.js +7 -2
  56. package/dist/lib/template-diff.js +26 -11
  57. package/dist/lib/template-downloader.js +11 -2
  58. package/dist/lib/vite/hydrogen-middleware.js +82 -0
  59. package/dist/lib/vite/mini-oxygen.js +152 -0
  60. package/dist/lib/vite/plugins.d.ts +27 -0
  61. package/dist/lib/vite/plugins.js +139 -0
  62. package/dist/lib/vite/shared.js +10 -0
  63. package/dist/lib/vite/utils.js +55 -0
  64. package/dist/lib/vite/worker-entry.js +1518 -0
  65. package/dist/lib/vite-config.js +45 -0
  66. package/dist/virtual-routes/lib/useDebugNetworkServer.jsx +4 -2
  67. package/dist/virtual-routes/routes/index.jsx +5 -5
  68. package/dist/virtual-routes/routes/subrequest-profiler.jsx +1 -1
  69. package/dist/virtual-routes/virtual-root.jsx +1 -1
  70. package/oclif.manifest.json +1127 -494
  71. package/package.json +36 -11
  72. /package/dist/generator-templates/starter/{public → app/assets}/favicon.svg +0 -0
package/dist/lib/flags.js CHANGED
@@ -9,96 +9,147 @@ import { DEFAULT_INSPECTOR_PORT } from './mini-oxygen/common.js';
9
9
 
10
10
  const DEFAULT_PORT = 3e3;
11
11
  const commonFlags = {
12
- path: Flags.string({
13
- description: "The path to the directory of the Hydrogen storefront. The default is the current directory.",
14
- env: "SHOPIFY_HYDROGEN_FLAG_PATH"
15
- }),
16
- port: Flags.integer({
17
- description: "Port to run the server on.",
18
- env: "SHOPIFY_HYDROGEN_FLAG_PORT",
19
- default: DEFAULT_PORT
20
- }),
21
- legacyRuntime: Flags.boolean({
22
- description: "Run the app in a Node.js sandbox instead of an Oxygen worker.",
23
- env: "SHOPIFY_HYDROGEN_FLAG_WORKER"
24
- }),
25
- force: Flags.boolean({
26
- description: "Overwrite the destination directory and files if they already exist.",
27
- env: "SHOPIFY_HYDROGEN_FLAG_FORCE",
28
- char: "f"
29
- }),
30
- shop: Flags.string({
31
- char: "s",
32
- description: "Shop URL. It can be the shop prefix (janes-apparel) or the full myshopify.com URL (janes-apparel.myshopify.com, https://janes-apparel.myshopify.com).",
33
- env: "SHOPIFY_SHOP",
34
- parse: async (input) => normalizeStoreFqdn(input)
35
- }),
36
- installDeps: Flags.boolean({
37
- description: "Auto install dependencies using the active package manager",
38
- env: "SHOPIFY_HYDROGEN_FLAG_INSTALL_DEPS",
39
- allowNo: true
40
- }),
41
- envBranch: Flags.string({
42
- description: "Specify an environment's branch name when using remote environment variables.",
43
- env: "SHOPIFY_HYDROGEN_ENVIRONMENT_BRANCH",
44
- char: "e"
45
- }),
46
- sourcemap: Flags.boolean({
47
- description: "Generate sourcemaps for the build.",
48
- env: "SHOPIFY_HYDROGEN_FLAG_SOURCEMAP",
49
- default: true,
50
- allowNo: true
51
- }),
52
- codegen: Flags.boolean({
53
- description: "Generate types for the Storefront API queries found in your project.",
54
- required: false,
55
- default: false
56
- }),
57
- codegenConfigPath: Flags.string({
58
- description: "Specify a path to a codegen configuration file. Defaults to `<root>/codegen.ts` if it exists.",
59
- required: false,
60
- dependsOn: ["codegen"]
61
- }),
62
- styling: Flags.string({
63
- description: `Sets the styling strategy to use. One of ${STYLING_CHOICES.map(
64
- (item) => `\`${item}\``
65
- ).join(", ")}.`,
66
- choices: STYLING_CHOICES,
67
- env: "SHOPIFY_HYDROGEN_FLAG_STYLING"
68
- }),
69
- markets: Flags.string({
70
- description: `Sets the URL structure to support multiple markets. One of ${I18N_CHOICES.map(
71
- (item) => `\`${item}\``
72
- ).join(", ")}.`,
73
- choices: I18N_CHOICES,
74
- env: "SHOPIFY_HYDROGEN_FLAG_I18N"
75
- }),
76
- shortcut: Flags.boolean({
77
- description: "Create a shortcut to the Shopify Hydrogen CLI.",
78
- env: "SHOPIFY_HYDROGEN_FLAG_SHORTCUT",
79
- allowNo: true
80
- }),
81
- debug: Flags.boolean({
82
- description: "Enables inspector connections with a debugger.",
83
- env: "SHOPIFY_HYDROGEN_FLAG_DEBUG",
84
- default: false
85
- }),
86
- inspectorPort: Flags.integer({
87
- description: "Port where the inspector will be available.",
88
- env: "SHOPIFY_HYDROGEN_FLAG_INSPECTOR_PORT",
89
- default: DEFAULT_INSPECTOR_PORT
90
- }),
91
- diff: Flags.boolean({
92
- description: "Applies the current files on top of Hydrogen's starter template in a temporary directory.",
93
- default: false,
94
- required: false
95
- }),
96
- lockfileCheck: Flags.boolean({
97
- allowNo: true,
98
- default: true,
99
- description: "Checks that there is exactly 1 valid lockfile in the project. Defaults to true, use `--no-lockfile-check` to disable.",
100
- env: "SHOPIFY_HYDROGEN_FLAG_LOCKFILE_CHECK"
101
- })
12
+ path: {
13
+ path: Flags.string({
14
+ description: "The path to the directory of the Hydrogen storefront. Defaults to the current directory where the command is run.",
15
+ env: "SHOPIFY_HYDROGEN_FLAG_PATH"
16
+ })
17
+ },
18
+ port: {
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
23
+ })
24
+ },
25
+ legacyRuntime: {
26
+ "legacy-runtime": Flags.boolean({
27
+ description: "Runs the app in a Node.js sandbox instead of an Oxygen worker.",
28
+ env: "SHOPIFY_HYDROGEN_FLAG_WORKER"
29
+ })
30
+ },
31
+ force: {
32
+ force: Flags.boolean({
33
+ description: "Overwrites the destination directory and files if they already exist.",
34
+ env: "SHOPIFY_HYDROGEN_FLAG_FORCE",
35
+ char: "f"
36
+ })
37
+ },
38
+ shop: {
39
+ shop: Flags.string({
40
+ char: "s",
41
+ description: "Shop URL. It can be the shop prefix (janes-apparel) or the full myshopify.com URL (janes-apparel.myshopify.com, https://janes-apparel.myshopify.com).",
42
+ env: "SHOPIFY_SHOP",
43
+ parse: async (input) => normalizeStoreFqdn(input)
44
+ })
45
+ },
46
+ installDeps: {
47
+ "install-deps": Flags.boolean({
48
+ description: "Auto installs dependencies using the active package manager.",
49
+ env: "SHOPIFY_HYDROGEN_FLAG_INSTALL_DEPS",
50
+ allowNo: true
51
+ })
52
+ },
53
+ envBranch: {
54
+ "env-branch": Flags.string({
55
+ description: "Specifies the environment to pull variables from using its Git branch name.",
56
+ 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"
64
+ })
65
+ },
66
+ sourcemap: {
67
+ sourcemap: Flags.boolean({
68
+ description: "Controls whether sourcemaps are generated. Default to `true`. Deactivate `--no-sourcemaps`.",
69
+ env: "SHOPIFY_HYDROGEN_FLAG_SOURCEMAP",
70
+ default: true,
71
+ allowNo: true
72
+ })
73
+ },
74
+ codegen: {
75
+ codegen: Flags.boolean({
76
+ description: "Automatically generates GraphQL types for your project\u2019s Storefront API queries.",
77
+ required: false,
78
+ default: false
79
+ }),
80
+ "codegen-config-path": Flags.string({
81
+ description: "Specifies a path to a codegen configuration file. Defaults to `<root>/codegen.ts` if this file exists.",
82
+ required: false,
83
+ dependsOn: ["codegen"]
84
+ })
85
+ },
86
+ styling: {
87
+ styling: Flags.string({
88
+ description: `Sets the styling strategy to use. One of ${STYLING_CHOICES.map(
89
+ (item) => `\`${item}\``
90
+ ).join(", ")}.`,
91
+ choices: STYLING_CHOICES,
92
+ env: "SHOPIFY_HYDROGEN_FLAG_STYLING"
93
+ })
94
+ },
95
+ markets: {
96
+ markets: Flags.string({
97
+ description: `Sets the URL structure to support multiple markets. Must be one of: ${I18N_CHOICES.map(
98
+ (item) => `\`${item}\``
99
+ ).join(", ")}. Example: \`--markets subfolders\`.`,
100
+ choices: I18N_CHOICES,
101
+ env: "SHOPIFY_HYDROGEN_FLAG_I18N"
102
+ })
103
+ },
104
+ shortcut: {
105
+ shortcut: Flags.boolean({
106
+ description: "Creates a global h2 shortcut for Shopify CLI using shell aliases. Deactivate with `--no-shortcut`.",
107
+ env: "SHOPIFY_HYDROGEN_FLAG_SHORTCUT",
108
+ allowNo: true
109
+ })
110
+ },
111
+ debug: {
112
+ debug: Flags.boolean({
113
+ description: "Enables inspector connections to the server with a debugger such as Visual Studio Code or Chrome DevTools.",
114
+ env: "SHOPIFY_HYDROGEN_FLAG_DEBUG",
115
+ default: false
116
+ })
117
+ },
118
+ inspectorPort: {
119
+ "inspector-port": Flags.integer({
120
+ 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
123
+ })
124
+ },
125
+ diff: {
126
+ diff: Flags.boolean({
127
+ description: "Applies the current files on top of Hydrogen's starter template in a temporary directory.",
128
+ default: false,
129
+ required: false,
130
+ hidden: true
131
+ })
132
+ },
133
+ entry: {
134
+ entry: Flags.string({
135
+ description: "Entry file for the worker. Defaults to `./server`.",
136
+ env: "SHOPIFY_HYDROGEN_FLAG_ENTRY"
137
+ })
138
+ },
139
+ lockfileCheck: {
140
+ "lockfile-check": Flags.boolean({
141
+ allowNo: true,
142
+ default: true,
143
+ description: "Checks that there is exactly one valid lockfile in the project. Defaults to `true`. Deactivate with `--no-lockfile-check`.",
144
+ env: "SHOPIFY_HYDROGEN_FLAG_LOCKFILE_CHECK"
145
+ })
146
+ },
147
+ disableRouteWarning: {
148
+ "disable-route-warning": Flags.boolean({
149
+ description: "Disables any warnings about missing standard routes.",
150
+ env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_ROUTE_WARNING"
151
+ })
152
+ }
102
153
  };
103
154
  function flagsToCamelObject(obj) {
104
155
  return Object.entries(obj).reduce((acc, [key, value]) => {
@@ -141,11 +192,14 @@ function deprecated(name, { isBoolean = false } = {}) {
141
192
  type: isBoolean ? "boolean" : "option"
142
193
  };
143
194
  }
144
- function overrideFlag(flag, extra) {
145
- return {
146
- ...flag,
147
- ...extra
148
- };
195
+ function overrideFlag(flags, extra) {
196
+ return Object.entries(extra).reduce(
197
+ (acc, [key, value]) => {
198
+ acc[key] = { ...flags[key], ...value };
199
+ return acc;
200
+ },
201
+ { ...flags }
202
+ );
149
203
  }
150
204
 
151
205
  export { DEFAULT_PORT, commonFlags, deprecated, flagsToCamelObject, overrideFlag, parseProcessFlags };
@@ -7,6 +7,7 @@ const PullVariablesQuery = `#graphql
7
7
  environmentVariables(branchName: $branch) {
8
8
  id
9
9
  isSecret
10
+ readOnly
10
11
  key
11
12
  value
12
13
  }
@@ -16,7 +16,13 @@ describe("getStorefrontEnvVariables", () => {
16
16
  hydrogenStorefront: {
17
17
  id: "123",
18
18
  environmentVariables: [
19
- { id: "123", isSecret: false, key: "key", value: "value" }
19
+ {
20
+ id: "123",
21
+ isSecret: false,
22
+ readOnly: false,
23
+ key: "key",
24
+ value: "value"
25
+ }
20
26
  ]
21
27
  }
22
28
  };
@@ -0,0 +1,35 @@
1
+ import { adminRequest } from './client.js';
2
+
3
+ const PushVariablesMutation = `#graphql
4
+ mutation PushVariables(
5
+ $storefrontId: ID!,
6
+ $environmentId: ID!,
7
+ $environmentVariablesInput: [HydrogenStorefrontEnvironmentVariableInput!]!,
8
+ ) {
9
+ hydrogenStorefrontEnvironmentVariableBulkReplace(
10
+ storefrontId: $storefrontId,
11
+ environmentId: $environmentId,
12
+ environmentVariablesInput: $environmentVariablesInput,
13
+ ) {
14
+ userErrors {
15
+ code
16
+ message
17
+ }
18
+ }
19
+ }
20
+ `;
21
+ async function pushStorefrontEnvVariables(adminSession, storefrontId, environmentId, variables) {
22
+ const { hydrogenStorefrontEnvironmentVariableBulkReplace } = await adminRequest(
23
+ PushVariablesMutation,
24
+ adminSession,
25
+ {
26
+ storefrontId,
27
+ environmentId,
28
+ environmentVariablesInput: variables
29
+ }
30
+ );
31
+ const { userErrors } = hydrogenStorefrontEnvironmentVariableBulkReplace;
32
+ return { userErrors };
33
+ }
34
+
35
+ export { pushStorefrontEnvVariables };
package/dist/lib/log.js CHANGED
@@ -174,6 +174,7 @@ function muteAuthLogs({
174
174
  function enhanceH2Logs(options) {
175
175
  injectLogReplacer("error");
176
176
  injectLogReplacer("warn", warningDebouncer);
177
+ injectLogReplacer("log", warningDebouncer);
177
178
  addMessageReplacers("h2-warn", [
178
179
  ([first]) => {
179
180
  const message = first?.message ?? first;
@@ -3,6 +3,7 @@ import colors from '@shopify/cli-kit/node/colors';
3
3
  import { DEV_ROUTES } from '../request-events.js';
4
4
 
5
5
  const DEFAULT_INSPECTOR_PORT = 9229;
6
+ const SUBREQUEST_PROFILER_ENDPOINT = "/debug-network-server";
6
7
  function logRequestLine(request, {
7
8
  responseStatus = 200,
8
9
  durationMs = 0
@@ -56,4 +57,4 @@ const OXYGEN_HEADERS_MAP = {
56
57
  }
57
58
  };
58
59
 
59
- export { DEFAULT_INSPECTOR_PORT, OXYGEN_HEADERS_MAP, logRequestLine };
60
+ export { DEFAULT_INSPECTOR_PORT, OXYGEN_HEADERS_MAP, SUBREQUEST_PROFILER_ENDPOINT, logRequestLine };
@@ -4,7 +4,7 @@ import { readFile } from '@shopify/cli-kit/node/fs';
4
4
  import { renderSuccess } from '@shopify/cli-kit/node/ui';
5
5
  import { Response, startServer, Request } from '@shopify/mini-oxygen';
6
6
  import { DEFAULT_PORT } from '../flags.js';
7
- import { OXYGEN_HEADERS_MAP, logRequestLine } from './common.js';
7
+ import { OXYGEN_HEADERS_MAP, SUBREQUEST_PROFILER_ENDPOINT, logRequestLine } from './common.js';
8
8
  import { setConstructors, createLogRequestEvent, handleDebugNetworkRequest, H2O_BINDING_NAME } from '../request-events.js';
9
9
 
10
10
  async function startNodeServer({
@@ -59,7 +59,7 @@ async function startNodeServer({
59
59
  oxygenHeaders,
60
60
  async onRequest(request, defaultDispatcher) {
61
61
  const url = new URL(request.url);
62
- if (url.pathname === "/debug-network-server") {
62
+ if (url.pathname === SUBREQUEST_PROFILER_ENDPOINT) {
63
63
  return handleDebugNetworkRequest(request);
64
64
  }
65
65
  let requestId = request.headers.get("request-id");
@@ -38,7 +38,7 @@ async function findInspectorUrl(inspectorPort) {
38
38
  const jsonUrl = `http://127.0.0.1:${inspectorPort}/json`;
39
39
  const body = await (await fetch(jsonUrl)).json();
40
40
  const url = body?.find(
41
- ({ id }) => id === "core:user:hydrogen"
41
+ ({ id }) => id === "core:user:hydrogen" || id === "core:user:oxygen"
42
42
  )?.webSocketDebuggerUrl;
43
43
  if (!url) {
44
44
  throw new Error("Unable to find inspector URL");
@@ -6,11 +6,15 @@ import { outputContent, outputToken } from '@shopify/cli-kit/node/output';
6
6
  import colors from '@shopify/cli-kit/node/colors';
7
7
  import { createInspectorConnector } from './workerd-inspector.js';
8
8
  import { findPort } from '../find-port.js';
9
- import { OXYGEN_HEADERS_MAP, logRequestLine } from './common.js';
9
+ import { OXYGEN_HEADERS_MAP, SUBREQUEST_PROFILER_ENDPOINT, logRequestLine } from './common.js';
10
10
  import { setConstructors, handleDebugNetworkRequest, H2O_BINDING_NAME, createLogRequestEvent } from '../request-events.js';
11
11
  import { STATIC_ASSET_EXTENSIONS, createAssetsServer, buildAssetsUrl } from './assets.js';
12
12
 
13
13
  const PRIVATE_WORKERD_INSPECTOR_PORT = 9222;
14
+ const OXYGEN_WORKERD_COMPAT_PARAMS = {
15
+ compatibilityFlags: ["streams_enable_constructors"],
16
+ compatibilityDate: "2022-10-31"
17
+ };
14
18
  async function startWorkerdServer({
15
19
  root,
16
20
  port: appPort,
@@ -34,7 +38,10 @@ async function startWorkerdServer({
34
38
  const absoluteBundlePath = resolvePath(root, buildPathWorkerFile);
35
39
  const handleAssets = createAssetHandler(assetsPort);
36
40
  const staticAssetExtensions = STATIC_ASSET_EXTENSIONS.slice();
37
- let stringifiedOxygenHandler = miniOxygenHandler.toString();
41
+ let stringifiedOxygenHandler = miniOxygenHandler.toString().replace(
42
+ "SUBREQUEST_PROFILER_ENDPOINT",
43
+ JSON.stringify(SUBREQUEST_PROFILER_ENDPOINT)
44
+ );
38
45
  if (process.env.NODE_ENV === "test") {
39
46
  stringifiedOxygenHandler = stringifiedOxygenHandler.replace(
40
47
  /\w*vite_ssr_import[\w\d]*\./g,
@@ -49,7 +56,9 @@ async function startWorkerdServer({
49
56
  log: new NoOpLog(),
50
57
  liveReload: watch,
51
58
  host: "localhost",
52
- handleRuntimeStdio() {
59
+ handleRuntimeStdio(stdout, stderr) {
60
+ stdout.destroy();
61
+ stderr.destroy();
53
62
  },
54
63
  workers: [
55
64
  {
@@ -77,11 +86,12 @@ async function startWorkerdServer({
77
86
  contents: await readFile(absoluteBundlePath)
78
87
  }
79
88
  ],
80
- compatibilityFlags: ["streams_enable_constructors"],
81
- compatibilityDate: "2022-10-31",
89
+ ...OXYGEN_WORKERD_COMPAT_PARAMS,
82
90
  bindings: { ...env },
83
91
  serviceBindings: {
84
- [H2O_BINDING_NAME]: createLogRequestEvent({ absoluteBundlePath })
92
+ [H2O_BINDING_NAME]: createLogRequestEvent({
93
+ transformLocation: () => absoluteBundlePath
94
+ })
85
95
  }
86
96
  }
87
97
  ]
@@ -117,20 +127,12 @@ async function startWorkerdServer({
117
127
  },
118
128
  showBanner(options) {
119
129
  console.log("");
120
- const isVSCode = process.env.TERM_PROGRAM === "vscode";
121
- const debuggingDocsLink = "https://h2o.fyi/debugging/server-code" + (isVSCode ? "#visual-studio-code" : "#step-2-attach-a-debugger");
122
- const debuggerMessage = outputContent`\n\nDebugging enabled on port ${String(
123
- publicInspectorPort
124
- )}.\nAttach a ${outputToken.link(
125
- colors.yellow(isVSCode ? "VSCode debugger" : "debugger"),
126
- debuggingDocsLink
127
- )} or open DevTools in http://localhost:${String(publicInspectorPort)}.`.value;
128
130
  renderSuccess({
129
131
  headline: `${options?.headlinePrefix ?? ""}MiniOxygen (Worker Runtime) ${options?.mode ?? "development"} server running.`,
130
132
  body: [
131
133
  `View ${options?.appName ?? "Hydrogen"} app: ${listeningAt}`,
132
134
  ...options?.extraLines ?? [],
133
- ...debug ? [{ warn: debuggerMessage }] : []
135
+ ...debug ? [{ warn: getDebugBannerLine(publicInspectorPort) }] : []
134
136
  ]
135
137
  });
136
138
  console.log("");
@@ -144,7 +146,7 @@ async function startWorkerdServer({
144
146
  }
145
147
  async function miniOxygenHandler(request, env, context) {
146
148
  const { pathname } = new URL(request.url);
147
- if (pathname === "/debug-network-server") {
149
+ if (pathname === SUBREQUEST_PROFILER_ENDPOINT) {
148
150
  return env.debugNetwork.fetch(request);
149
151
  }
150
152
  if (request.method === "GET") {
@@ -209,5 +211,15 @@ async function logRequest(request) {
209
211
  });
210
212
  return new Response("ok");
211
213
  }
214
+ function getDebugBannerLine(publicInspectorPort) {
215
+ const isVSCode = process.env.TERM_PROGRAM === "vscode";
216
+ const debuggingDocsLink = "https://h2o.fyi/debugging/server-code" + (isVSCode ? "#visual-studio-code" : "#step-2-attach-a-debugger");
217
+ return outputContent`\n\nDebugging enabled on port ${String(
218
+ publicInspectorPort
219
+ )}.\nAttach a ${outputToken.link(
220
+ colors.yellow(isVSCode ? "VSCode debugger" : "debugger"),
221
+ debuggingDocsLink
222
+ )} or open DevTools in http://localhost:${String(publicInspectorPort)}.`.value;
223
+ }
212
224
 
213
- export { startWorkerdServer };
225
+ export { OXYGEN_WORKERD_COMPAT_PARAMS, PRIVATE_WORKERD_INSPECTOR_PORT, getDebugBannerLine, startWorkerdServer };
@@ -476,9 +476,6 @@ function createAbortHandler(controller, project) {
476
476
  error?.tryMessage ?? error?.stack
477
477
  )
478
478
  );
479
- if (process.env.NODE_ENV === "test") {
480
- console.error(error);
481
- }
482
479
  process.exit(1);
483
480
  };
484
481
  }
@@ -77,6 +77,10 @@ async function setupLocalStarterTemplate(options, controller) {
77
77
  }
78
78
  }
79
79
  ];
80
+ const cliCommand = await getCliCommand();
81
+ const envLeadingComment = `# The variables added in this file are only available locally in MiniOxygen.
82
+ # Run \`${cliCommand} link\` to also inject environment variables from your storefront,
83
+ # or \`${cliCommand} env pull\` to populate this file.`;
80
84
  backgroundWorkPromise = backgroundWorkPromise.then(() => {
81
85
  const promises = [
82
86
  // Add project name to package.json
@@ -89,7 +93,6 @@ async function setupLocalStarterTemplate(options, controller) {
89
93
  )
90
94
  )
91
95
  ];
92
- const envLeadingComment = "# The variables added in this file are only available locally in MiniOxygen.\n# Run `h2 link` to also inject environment variables from your storefront,\n# or `h2 env pull` to populate this file.";
93
96
  let storefrontToLink;
94
97
  if (storefrontInfo) {
95
98
  promises.push(
@@ -1,3 +1,4 @@
1
+ import { readdir } from 'node:fs/promises';
1
2
  import { AbortError } from '@shopify/cli-kit/node/error';
2
3
  import { fileExists, copyFile } from '@shopify/cli-kit/node/fs';
3
4
  import { readAndParsePackageJson } from '@shopify/cli-kit/node/node-package-manager';
@@ -22,36 +23,40 @@ async function setupRemoteTemplate(options, controller) {
22
23
  if (await fileExists(examplePath)) {
23
24
  return { templatesDir, sourcePath: examplePath };
24
25
  }
26
+ const availableTemplates = (await Promise.all([readdir(examplesDir), readdir(templatesDir)]).catch(
27
+ () => []
28
+ )).flat().filter((name) => name !== "skeleton" && !name.endsWith(".md")).sort();
25
29
  throw new AbortError(
26
- "Unknown value in --template flag.",
27
- "Skip the --template flag or provide the name of a template or example in the Hydrogen repository."
30
+ `Unknown value in \`--template\` flag "${appTemplate}".
31
+ Skip the flag or provide the name of a template or example in the Hydrogen repository.`,
32
+ availableTemplates.length === 0 ? "" : { list: { title: "Available templates:", items: availableTemplates } }
28
33
  );
29
34
  }).catch(abort);
30
35
  const project = await handleProjectLocation({ ...options, controller });
31
36
  if (!project)
32
37
  return;
33
38
  abort = createAbortHandler(controller, project);
34
- let backgroundWorkPromise = backgroundDownloadPromise.then(async (result) => {
39
+ const downloaded = await backgroundDownloadPromise;
40
+ if (controller.signal.aborted)
41
+ return;
42
+ let backgroundWorkPromise = Promise.resolve().then(async () => {
35
43
  if (controller.signal.aborted)
36
44
  return;
37
- const { sourcePath: sourcePath2, templatesDir } = result;
45
+ const { sourcePath, templatesDir } = downloaded;
38
46
  const pkgJson = await readAndParsePackageJson(
39
- joinPath(sourcePath2, "package.json")
47
+ joinPath(sourcePath, "package.json")
40
48
  );
41
49
  if (pkgJson.scripts?.dev?.includes("--diff")) {
42
50
  return applyTemplateDiff(
43
51
  project.directory,
44
- sourcePath2,
52
+ sourcePath,
45
53
  joinPath(templatesDir, "skeleton")
46
54
  );
47
55
  }
48
- return copyFile(sourcePath2, project.directory);
56
+ return copyFile(sourcePath, project.directory);
49
57
  }).catch(abort);
50
- if (controller.signal.aborted)
51
- return;
52
- const { sourcePath } = await backgroundDownloadPromise;
53
58
  const supportsTranspilation = await fileExists(
54
- joinPath(sourcePath, "tsconfig.json")
59
+ joinPath(downloaded.sourcePath, "tsconfig.json")
55
60
  );
56
61
  const { language, transpileProject } = supportsTranspilation ? await handleLanguage(project.directory, controller, options.language) : { language: "js", transpileProject: () => Promise.resolve() };
57
62
  backgroundWorkPromise = backgroundWorkPromise.then(() => transpileProject().catch(abort)).then(
@@ -12,7 +12,7 @@ const BUILD_DIR = "dist";
12
12
  const CLIENT_SUBDIR = "client";
13
13
  const WORKER_SUBDIR = "worker";
14
14
  const oxygenServerMainFields = ["browser", "module", "main"];
15
- function getProjectPaths(appPath, entry) {
15
+ function getProjectPaths(appPath) {
16
16
  const root = appPath ?? process.cwd();
17
17
  const publicPath = path.join(root, "public");
18
18
  const buildPath = path.join(root, BUILD_DIR);
@@ -31,7 +31,7 @@ const eventHistory = [];
31
31
  function createResponse(main = "ok", init) {
32
32
  return new ResponseConstructor(main, init);
33
33
  }
34
- async function clearHistory(request) {
34
+ function clearHistory(request) {
35
35
  eventHistory.length = 0;
36
36
  return createResponse();
37
37
  }
@@ -63,8 +63,8 @@ function createLogRequestEvent(options) {
63
63
  let stackLine = null;
64
64
  let stackLink = null;
65
65
  if (stackInfo?.file) {
66
- if (!path.isAbsolute(stackInfo.file) && options?.absoluteBundlePath) {
67
- stackInfo.file = options.absoluteBundlePath;
66
+ if (options?.transformLocation) {
67
+ stackInfo.file = options.transformLocation(stackInfo.file);
68
68
  }
69
69
  const { source, line, column } = mapSourcePosition({
70
70
  source: stackInfo.file,
@@ -1,9 +1,14 @@
1
1
  import { readFile, writeFile, fileExists } from '@shopify/cli-kit/node/fs';
2
2
  import { joinPath } from '@shopify/cli-kit/node/path';
3
3
  import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
4
- import { GENERATOR_SETUP_ASSETS_SUB_DIRS, getAssetDir } from '../../build.js';
4
+ import { getAssetDir } from '../../build.js';
5
5
 
6
- const SETUP_CSS_STRATEGIES = GENERATOR_SETUP_ASSETS_SUB_DIRS;
6
+ const SETUP_CSS_STRATEGIES = [
7
+ "tailwind",
8
+ "css-modules",
9
+ "vanilla-extract",
10
+ "postcss"
11
+ ];
7
12
  function copyAssets(feature, assets, rootDirectory, replacer = (content, filename) => content) {
8
13
  const setupAssetsPath = getAssetDir(feature);
9
14
  return Promise.all(