@shopify/cli-hydrogen 5.2.3 → 5.3.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 (37) hide show
  1. package/dist/commands/hydrogen/build.js +42 -20
  2. package/dist/commands/hydrogen/deploy.js +171 -0
  3. package/dist/commands/hydrogen/deploy.test.js +185 -0
  4. package/dist/commands/hydrogen/dev.js +21 -13
  5. package/dist/commands/hydrogen/init.js +10 -6
  6. package/dist/commands/hydrogen/init.test.js +16 -1
  7. package/dist/commands/hydrogen/preview.js +27 -11
  8. package/dist/generator-templates/starter/app/root.tsx +6 -4
  9. package/dist/generator-templates/starter/app/routes/account.tsx +1 -1
  10. package/dist/generator-templates/starter/app/routes/cart.$lines.tsx +70 -0
  11. package/dist/generator-templates/starter/app/routes/cart.tsx +1 -1
  12. package/dist/generator-templates/starter/app/routes/discount.$code.tsx +43 -0
  13. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +3 -1
  14. package/dist/generator-templates/starter/package.json +3 -3
  15. package/dist/generator-templates/starter/remix.env.d.ts +11 -3
  16. package/dist/generator-templates/starter/server.ts +21 -18
  17. package/dist/generator-templates/starter/tsconfig.json +1 -1
  18. package/dist/lib/bundle/analyzer.js +56 -0
  19. package/dist/lib/bundle/bundle-analyzer.html +2045 -0
  20. package/dist/lib/flags.js +4 -0
  21. package/dist/lib/get-oxygen-token.js +47 -0
  22. package/dist/lib/get-oxygen-token.test.js +104 -0
  23. package/dist/lib/graphql/admin/oxygen-token.js +21 -0
  24. package/dist/lib/log.js +56 -13
  25. package/dist/lib/mini-oxygen/common.js +58 -0
  26. package/dist/lib/mini-oxygen/index.js +12 -0
  27. package/dist/lib/{mini-oxygen.js → mini-oxygen/node.js} +27 -52
  28. package/dist/lib/mini-oxygen/types.js +1 -0
  29. package/dist/lib/mini-oxygen/workerd-inspector.js +392 -0
  30. package/dist/lib/mini-oxygen/workerd.js +182 -0
  31. package/dist/lib/onboarding/common.js +4 -4
  32. package/dist/lib/onboarding/local.js +1 -1
  33. package/dist/lib/render-errors.js +1 -1
  34. package/dist/lib/setups/routes/generate.js +1 -1
  35. package/dist/virtual-routes/routes/index.jsx +4 -4
  36. package/oclif.manifest.json +81 -3
  37. package/package.json +12 -4
@@ -0,0 +1,392 @@
1
+ import { dirname } from 'node:path';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { SourceMapConsumer } from 'source-map';
4
+ import { parse } from 'stack-trace';
5
+ import WebSocket from 'ws';
6
+
7
+ async function findInspectorUrl(inspectorPort) {
8
+ try {
9
+ const jsonUrl = `http://127.0.0.1:${inspectorPort}/json`;
10
+ const body = await (await fetch(jsonUrl)).json();
11
+ return body?.find(({ id }) => id === "core:user:hydrogen")?.webSocketDebuggerUrl;
12
+ } catch (error) {
13
+ console.error("Error attempting to retrieve debugger URL:", error);
14
+ }
15
+ }
16
+ function connectToInspector({
17
+ inspectorUrl,
18
+ sourceMapPath
19
+ }) {
20
+ const messageCounterRef = { value: -1 };
21
+ const getMessageId = () => messageCounterRef.value--;
22
+ const pendingMessages = /* @__PURE__ */ new Map();
23
+ const ws = new WebSocket(inspectorUrl);
24
+ let keepAliveInterval;
25
+ const isClosed = () => ws.readyState === WebSocket.CLOSED || ws.readyState === WebSocket.CLOSING;
26
+ const send = (method, params) => {
27
+ if (!isClosed()) {
28
+ const id = getMessageId();
29
+ let promiseResolve = void 0;
30
+ const promise = new Promise(
31
+ (resolve) => promiseResolve = resolve
32
+ );
33
+ pendingMessages.set(id, promiseResolve);
34
+ ws.send(JSON.stringify({ id, method, params }));
35
+ return promise;
36
+ }
37
+ return Promise.resolve(void 0);
38
+ };
39
+ const cleanupMessageQueue = (data) => {
40
+ try {
41
+ if (data?.id < 0) {
42
+ const resolve = pendingMessages.get(data.id);
43
+ if (resolve !== void 0) {
44
+ pendingMessages.delete(data.id);
45
+ resolve(data.result);
46
+ }
47
+ return true;
48
+ }
49
+ } catch (error) {
50
+ console.error(error);
51
+ }
52
+ return false;
53
+ };
54
+ function getPropertyValue(name, response) {
55
+ return response?.result.find((prop) => prop.name === name)?.value;
56
+ }
57
+ async function reconstructError(initialProperties, ro) {
58
+ let errorProperties = { ...initialProperties };
59
+ const objectId = ro?.objectId;
60
+ if (objectId) {
61
+ const [sourceMapConsumer, getPropertiesResponse] = await Promise.all([
62
+ getSourceMapConsumer(),
63
+ send("Runtime.getProperties", {
64
+ objectId,
65
+ ownProperties: false,
66
+ accessorPropertiesOnly: false,
67
+ generatePreview: false,
68
+ nonIndexedPropertiesOnly: false
69
+ })
70
+ ]);
71
+ const message = getPropertyValue("message", getPropertiesResponse);
72
+ if (message?.value) {
73
+ errorProperties.message = message.value;
74
+ }
75
+ const stack = getPropertyValue("stack", getPropertiesResponse);
76
+ if (stack?.value) {
77
+ errorProperties.stack = sourceMapConsumer ? formatStack(sourceMapConsumer, stack.value) : stack.value;
78
+ }
79
+ const cause = getPropertyValue("cause", getPropertiesResponse);
80
+ if (cause) {
81
+ errorProperties.cause = cause.description ?? cause.value;
82
+ if (cause.subtype === "error" && sourceMapConsumer && cause.description !== void 0) {
83
+ errorProperties.stack = formatStack(
84
+ sourceMapConsumer,
85
+ cause.description
86
+ );
87
+ }
88
+ }
89
+ const isDomException = ro?.className === "DOMException";
90
+ if (isDomException) {
91
+ const stackDescriptor = getPropertiesResponse?.result.find(
92
+ (prop) => prop.name === "stack"
93
+ );
94
+ const getObjectId = stackDescriptor?.get?.objectId;
95
+ if (getObjectId !== void 0) {
96
+ const callFunctionResponse = await send("Runtime.callFunctionOn", {
97
+ objectId,
98
+ functionDeclaration: "function invokeGetter(getter) { return Reflect.apply(getter, this, []); }",
99
+ arguments: [{ objectId: getObjectId }],
100
+ silent: true
101
+ });
102
+ if (callFunctionResponse !== void 0) {
103
+ const stack2 = callFunctionResponse.result.value;
104
+ if (typeof stack2 === "string" && sourceMapConsumer !== void 0) {
105
+ errorProperties.stack = formatStack(sourceMapConsumer, stack2);
106
+ } else {
107
+ try {
108
+ errorProperties.stack = JSON.stringify(stack2);
109
+ } catch {
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ }
116
+ const error = new Error(errorProperties.message);
117
+ error.stack = errorProperties.stack;
118
+ if (errorProperties.cause) {
119
+ error.cause = errorProperties.cause;
120
+ }
121
+ return error;
122
+ }
123
+ const sourceMapAbortController = new AbortController();
124
+ let sourceMapConsumerPromise;
125
+ const getSourceMapConsumer = () => {
126
+ return sourceMapConsumerPromise ??= (async () => {
127
+ if (!sourceMapPath || sourceMapAbortController.signal.aborted) {
128
+ return;
129
+ }
130
+ try {
131
+ const mapContent = await readFile(sourceMapPath, "utf-8");
132
+ if (sourceMapAbortController.signal.aborted)
133
+ return;
134
+ const map = JSON.parse(mapContent);
135
+ map.sourceRoot = dirname(sourceMapPath);
136
+ const sourceMapConsumer = await new SourceMapConsumer(map);
137
+ if (sourceMapAbortController.signal.aborted) {
138
+ sourceMapConsumer.destroy();
139
+ return;
140
+ }
141
+ sourceMapAbortController.signal.addEventListener("abort", () => {
142
+ sourceMapConsumerPromise = Promise.resolve(void 0);
143
+ sourceMapConsumer.destroy();
144
+ });
145
+ return sourceMapConsumer;
146
+ } catch {
147
+ }
148
+ })();
149
+ };
150
+ ws.addEventListener("message", async (event) => {
151
+ if (typeof event.data === "string") {
152
+ const evt = JSON.parse(event.data);
153
+ cleanupMessageQueue(evt);
154
+ if (evt.method === "Runtime.exceptionThrown") {
155
+ const params = evt.params;
156
+ const errorProperties = {};
157
+ const sourceMapConsumer = await getSourceMapConsumer();
158
+ if (sourceMapConsumer !== void 0) {
159
+ const message = params.exceptionDetails.exception?.description?.split("\n")[0];
160
+ const stack = params.exceptionDetails.stackTrace?.callFrames;
161
+ const formatted = formatStructuredError(
162
+ sourceMapConsumer,
163
+ message,
164
+ stack
165
+ );
166
+ errorProperties.message = params.exceptionDetails.text;
167
+ errorProperties.stack = formatted;
168
+ } else {
169
+ errorProperties.message = params.exceptionDetails.text + " " + (params.exceptionDetails.exception?.description ?? "");
170
+ }
171
+ console.error(
172
+ await reconstructError(
173
+ errorProperties,
174
+ params.exceptionDetails.exception
175
+ )
176
+ );
177
+ }
178
+ if (evt.method === "Runtime.consoleAPICalled") {
179
+ const params = evt.params;
180
+ await logConsoleMessage(params, reconstructError);
181
+ }
182
+ } else {
183
+ console.error("Unrecognised devtools event:", event);
184
+ }
185
+ });
186
+ ws.once("open", () => {
187
+ send("Runtime.enable");
188
+ keepAliveInterval = setInterval(() => send("Runtime.getIsolateId"), 1e4);
189
+ });
190
+ ws.on("unexpected-response", () => {
191
+ console.log("Waiting for connection...");
192
+ });
193
+ ws.once("close", () => {
194
+ clearInterval(keepAliveInterval);
195
+ sourceMapAbortController.abort();
196
+ });
197
+ return () => {
198
+ clearInterval(keepAliveInterval);
199
+ if (!isClosed()) {
200
+ try {
201
+ ws.close();
202
+ } catch (err) {
203
+ }
204
+ }
205
+ sourceMapAbortController.abort();
206
+ };
207
+ }
208
+ const mapConsoleAPIMessageTypeToConsoleMethod = {
209
+ log: "log",
210
+ debug: "debug",
211
+ info: "info",
212
+ warning: "warn",
213
+ error: "error",
214
+ dir: "dir",
215
+ dirxml: "dirxml",
216
+ table: "table",
217
+ trace: "trace",
218
+ clear: "clear",
219
+ count: "count",
220
+ assert: "assert",
221
+ profile: "profile",
222
+ profileEnd: "profileEnd",
223
+ timeEnd: "timeEnd",
224
+ startGroup: "group",
225
+ startGroupCollapsed: "groupCollapsed",
226
+ endGroup: "groupEnd"
227
+ };
228
+ async function logConsoleMessage(evt, reconstructError) {
229
+ const args = [];
230
+ for (const ro of evt.args) {
231
+ switch (ro.type) {
232
+ case "string":
233
+ case "number":
234
+ case "boolean":
235
+ case "undefined":
236
+ case "symbol":
237
+ case "bigint":
238
+ args.push(ro.value);
239
+ break;
240
+ case "function":
241
+ args.push(`[Function: ${ro.description ?? "<no-description>"}]`);
242
+ break;
243
+ case "object":
244
+ if (!ro.preview) {
245
+ args.push(
246
+ ro.subtype === "null" ? "null" : ro.description ?? "<no-description>"
247
+ );
248
+ } else {
249
+ if (ro.preview.description)
250
+ args.push(ro.preview.description);
251
+ switch (ro.preview.subtype) {
252
+ case "array":
253
+ args.push(
254
+ "[ " + ro.preview.properties.map(({ value }) => {
255
+ return value;
256
+ }).join(", ") + (ro.preview.overflow ? "..." : "") + " ]"
257
+ );
258
+ break;
259
+ case "weakmap":
260
+ case "map":
261
+ ro.preview.entries === void 0 ? args.push("{}") : args.push(
262
+ "{\n" + ro.preview.entries.map(({ key, value }) => {
263
+ return ` ${key?.description ?? "<unknown>"} => ${value.description}`;
264
+ }).join(",\n") + (ro.preview.overflow ? "\n ..." : "") + "\n}"
265
+ );
266
+ break;
267
+ case "weakset":
268
+ case "set":
269
+ ro.preview.entries === void 0 ? args.push("{}") : args.push(
270
+ "{ " + ro.preview.entries.map(({ value }) => {
271
+ return `${value.description}`;
272
+ }).join(", ") + (ro.preview.overflow ? ", ..." : "") + " }"
273
+ );
274
+ break;
275
+ case "regexp":
276
+ break;
277
+ case "date":
278
+ break;
279
+ case "generator":
280
+ args.push(ro.preview?.properties[0]?.value || "");
281
+ break;
282
+ case "promise":
283
+ if (ro.preview?.properties[0]?.value === "pending") {
284
+ args.push(`{<${ro.preview.properties[0].value}>}`);
285
+ } else {
286
+ args.push(
287
+ `{<${ro.preview?.properties[0]?.value}>: ${ro.preview?.properties[1]?.value}}`
288
+ );
289
+ }
290
+ break;
291
+ case "node":
292
+ case "iterator":
293
+ case "proxy":
294
+ case "typedarray":
295
+ case "arraybuffer":
296
+ case "dataview":
297
+ case "webassemblymemory":
298
+ case "wasmvalue":
299
+ break;
300
+ case "error":
301
+ const errorProperties = {
302
+ message: ro.preview.description?.split("\n").filter((line) => !/^\s+at\s/.test(line)).join("\n") ?? ro.preview.properties.find(({ name }) => name === "message")?.value ?? "",
303
+ stack: ro.preview.description ?? ro.description ?? ro.preview.properties.find(({ name }) => name === "stack")?.value,
304
+ cause: ro.preview.properties.find(({ name }) => name === "cause")?.value
305
+ };
306
+ const error = await reconstructError(errorProperties, ro);
307
+ args.splice(-1, 1, error);
308
+ break;
309
+ default:
310
+ args.push(
311
+ "{\n" + ro.preview.properties.map(({ name, value }) => {
312
+ return ` ${name}: ${value}`;
313
+ }).join(",\n") + (ro.preview.overflow ? "\n ..." : "") + "\n}"
314
+ );
315
+ }
316
+ }
317
+ break;
318
+ default:
319
+ args.push(ro.description || ro.unserializableValue || "\u{1F98B}");
320
+ break;
321
+ }
322
+ }
323
+ const method = mapConsoleAPIMessageTypeToConsoleMethod[evt.type];
324
+ if (method in console) {
325
+ switch (method) {
326
+ case "dir":
327
+ console.dir(args);
328
+ break;
329
+ case "table":
330
+ console.table(args);
331
+ break;
332
+ default:
333
+ console[method].apply(console, args);
334
+ break;
335
+ }
336
+ } else {
337
+ console.warn(`Unsupported console method: ${method}`);
338
+ console.warn("console event:", evt);
339
+ }
340
+ }
341
+ function formatStructuredError(sourceMapConsumer, message, frames) {
342
+ const lines = [];
343
+ if (message !== void 0)
344
+ lines.push(message);
345
+ frames?.forEach(({ functionName, lineNumber, columnNumber }, i) => {
346
+ try {
347
+ if (lineNumber) {
348
+ const pos = sourceMapConsumer.originalPositionFor({
349
+ line: lineNumber + 1,
350
+ column: columnNumber
351
+ });
352
+ if (i === 0 && pos.source && pos.line) {
353
+ const fileSource = sourceMapConsumer.sourceContentFor(pos.source);
354
+ const fileSourceLine = fileSource?.split("\n")[pos.line - 1] || "";
355
+ lines.push(fileSourceLine.trim());
356
+ if (pos.column) {
357
+ lines.push(
358
+ `${" ".repeat(pos.column - fileSourceLine.search(/\S/))}^`
359
+ );
360
+ }
361
+ }
362
+ if (pos && pos.line !== null && pos.column !== null) {
363
+ const convertedFnName = pos.name || functionName || "";
364
+ let convertedLocation = `${pos.source}:${pos.line}:${pos.column + 1}`;
365
+ if (convertedFnName === "") {
366
+ lines.push(` at ${convertedLocation}`);
367
+ } else {
368
+ lines.push(` at ${convertedFnName} (${convertedLocation})`);
369
+ }
370
+ }
371
+ }
372
+ } catch {
373
+ }
374
+ });
375
+ return lines.join("\n");
376
+ }
377
+ function formatStack(sourceMapConsumer, stack) {
378
+ const message = stack.split("\n")[0];
379
+ const callSites = parse({ stack });
380
+ const frames = callSites.map((site) => ({
381
+ functionName: site.getFunctionName() ?? "",
382
+ // `Protocol.Runtime.CallFrame`s line numbers are 0-indexed, hence `- 1`
383
+ lineNumber: (site.getLineNumber() ?? 1) - 1,
384
+ columnNumber: site.getColumnNumber() ?? 1,
385
+ // Unused by `formattedError`
386
+ scriptId: "",
387
+ url: ""
388
+ }));
389
+ return formatStructuredError(sourceMapConsumer, message, frames);
390
+ }
391
+
392
+ export { connectToInspector, findInspectorUrl, mapConsoleAPIMessageTypeToConsoleMethod };
@@ -0,0 +1,182 @@
1
+ import { Miniflare, NoOpLog, Response, Request } from 'miniflare';
2
+ import { resolvePath } from '@shopify/cli-kit/node/path';
3
+ import { glob, readFile, createFileReadStream, fileSize } from '@shopify/cli-kit/node/fs';
4
+ import { renderSuccess } from '@shopify/cli-kit/node/ui';
5
+ import { lookupMimeType } from '@shopify/cli-kit/node/mimes';
6
+ import { findInspectorUrl, connectToInspector } from './workerd-inspector.js';
7
+ import { DEFAULT_PORT } from '../flags.js';
8
+ import { findPort } from '../find-port.js';
9
+ import { OXYGEN_HEADERS_MAP, logRequestLine } from './common.js';
10
+
11
+ async function startWorkerdServer({
12
+ root,
13
+ port = DEFAULT_PORT,
14
+ watch = false,
15
+ buildPathWorkerFile,
16
+ buildPathClient,
17
+ env
18
+ }) {
19
+ const inspectorPort = await findPort(8787);
20
+ const oxygenHeadersMap = Object.values(OXYGEN_HEADERS_MAP).reduce(
21
+ (acc, item) => {
22
+ acc[item.name] = item.defaultValue;
23
+ return acc;
24
+ },
25
+ {}
26
+ );
27
+ const buildMiniOxygenOptions = async () => ({
28
+ cf: false,
29
+ verbose: false,
30
+ port,
31
+ log: new NoOpLog(),
32
+ liveReload: watch,
33
+ inspectorPort,
34
+ host: "localhost",
35
+ workers: [
36
+ {
37
+ name: "mini-oxygen",
38
+ modules: true,
39
+ script: `export default { fetch: ${miniOxygenHandler.toString()} }`,
40
+ bindings: {
41
+ initialAssets: await glob("**/*", { cwd: buildPathClient }),
42
+ oxygenHeadersMap
43
+ },
44
+ serviceBindings: {
45
+ hydrogen: "hydrogen",
46
+ assets: createAssetHandler(buildPathClient),
47
+ logRequest
48
+ }
49
+ },
50
+ {
51
+ name: "hydrogen",
52
+ modules: [
53
+ {
54
+ type: "ESModule",
55
+ path: resolvePath(root, buildPathWorkerFile),
56
+ contents: await readFile(resolvePath(root, buildPathWorkerFile))
57
+ }
58
+ ],
59
+ bindings: { ...env },
60
+ compatibilityFlags: ["streams_enable_constructors"],
61
+ compatibilityDate: "2022-10-31"
62
+ }
63
+ ]
64
+ });
65
+ let miniOxygenOptions = await buildMiniOxygenOptions();
66
+ const miniOxygen = new Miniflare(miniOxygenOptions);
67
+ const listeningAt = (await miniOxygen.ready).origin;
68
+ const sourceMapPath = buildPathWorkerFile + ".map";
69
+ let inspectorUrl = await findInspectorUrl(inspectorPort);
70
+ let cleanupInspector = inspectorUrl ? connectToInspector({ inspectorUrl, sourceMapPath }) : void 0;
71
+ return {
72
+ port,
73
+ listeningAt,
74
+ async reload(nextOptions) {
75
+ miniOxygenOptions = await buildMiniOxygenOptions();
76
+ if (nextOptions) {
77
+ const hydrogen = miniOxygenOptions.workers.find(
78
+ (worker) => worker.name === "hydrogen"
79
+ );
80
+ if (hydrogen) {
81
+ hydrogen.bindings = { ...nextOptions?.env ?? env };
82
+ }
83
+ }
84
+ cleanupInspector?.();
85
+ await miniOxygen.setOptions(miniOxygenOptions);
86
+ inspectorUrl ??= await findInspectorUrl(inspectorPort);
87
+ if (inspectorUrl) {
88
+ cleanupInspector = connectToInspector({ inspectorUrl, sourceMapPath });
89
+ }
90
+ },
91
+ showBanner(options) {
92
+ console.log("");
93
+ renderSuccess({
94
+ headline: `${options?.headlinePrefix ?? ""}MiniOxygen (Unstable Worker Runtime) ${options?.mode ?? "development"} server running.`,
95
+ body: [
96
+ `View ${options?.appName ?? "Hydrogen"} app: ${listeningAt}`,
97
+ ...options?.extraLines ?? []
98
+ ]
99
+ });
100
+ console.log("");
101
+ },
102
+ async close() {
103
+ await miniOxygen.dispose();
104
+ }
105
+ };
106
+ }
107
+ async function miniOxygenHandler(request, env, context) {
108
+ if (request.method === "GET") {
109
+ const pathname = new URL(request.url).pathname;
110
+ if (pathname.startsWith("/debug-network")) {
111
+ return new Response(
112
+ "The Network Debugger is currently not supported in the Worker Runtime."
113
+ );
114
+ }
115
+ if (new Set(env.initialAssets).has(pathname.slice(1))) {
116
+ const response2 = await env.assets.fetch(
117
+ new Request(request.url, {
118
+ signal: request.signal,
119
+ headers: request.headers
120
+ })
121
+ );
122
+ if (response2.status !== 404)
123
+ return response2;
124
+ }
125
+ }
126
+ const requestInit = {
127
+ headers: {
128
+ ...env.oxygenHeadersMap,
129
+ ...Object.fromEntries(request.headers.entries())
130
+ }
131
+ };
132
+ const startTimeMs = Date.now();
133
+ const response = await env.hydrogen.fetch(request, requestInit);
134
+ const durationMs = Date.now() - startTimeMs;
135
+ context.waitUntil(
136
+ env.logRequest.fetch(
137
+ new Request(request.url, {
138
+ method: request.method,
139
+ signal: request.signal,
140
+ headers: {
141
+ ...Object.fromEntries(request.headers.entries()),
142
+ "h2-duration-ms": String(durationMs),
143
+ "h2-response-status": String(response.status)
144
+ }
145
+ })
146
+ )
147
+ );
148
+ return response;
149
+ }
150
+ function createAssetHandler(buildPathClient) {
151
+ return async (request) => {
152
+ const relativeAssetPath = new URL(request.url).pathname.replace("/", "");
153
+ if (relativeAssetPath) {
154
+ try {
155
+ const absoluteAssetPath = resolvePath(
156
+ buildPathClient,
157
+ relativeAssetPath
158
+ );
159
+ return new Response(createFileReadStream(absoluteAssetPath), {
160
+ headers: {
161
+ "Content-Type": lookupMimeType(relativeAssetPath) || "text/plain",
162
+ "Content-Length": String(await fileSize(absoluteAssetPath))
163
+ }
164
+ });
165
+ } catch (error) {
166
+ if (error.code !== "ENOENT") {
167
+ throw error;
168
+ }
169
+ }
170
+ }
171
+ return new Response("Not Found", { status: 404 });
172
+ };
173
+ }
174
+ async function logRequest(request) {
175
+ logRequestLine(request, {
176
+ responseStatus: Number(request.headers.get("h2-response-status") || 200),
177
+ durationMs: Number(request.headers.get("h2-duration-ms") || 0)
178
+ });
179
+ return new Response("ok");
180
+ }
181
+
182
+ export { startWorkerdServer };
@@ -1,5 +1,5 @@
1
1
  import { readdir } from 'node:fs/promises';
2
- import { packageManagerUsedForCreating, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
2
+ import { packageManagerFromUserAgent, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
3
3
  import { renderConfirmationPrompt, renderInfo, renderTextPrompt, renderSelectPrompt, renderFatalError, renderWarning, renderSuccess } from '@shopify/cli-kit/node/ui';
4
4
  import { hyphenate, capitalize } from '@shopify/cli-kit/common/string';
5
5
  import { joinPath, resolvePath, basename } from '@shopify/cli-kit/node/path';
@@ -39,7 +39,7 @@ async function handleI18n(controller, cliCommand, flagI18n) {
39
39
  };
40
40
  }
41
41
  async function handleRouteGeneration(controller, flagRoutes) {
42
- const routesToScaffold = flagRoutes ? "all" : await renderRoutePrompt({
42
+ const routesToScaffold = flagRoutes === true ? "all" : flagRoutes === false ? [] : await renderRoutePrompt({
43
43
  abortSignal: controller.signal
44
44
  });
45
45
  const needsRouteGeneration = routesToScaffold === "all" || routesToScaffold.length > 0;
@@ -200,7 +200,7 @@ async function handleLanguage(projectDir, controller, flagLanguage) {
200
200
  };
201
201
  }
202
202
  async function handleCssStrategy(projectDir, controller, flagStyling) {
203
- const selection = flagStyling ? flagStyling : await renderCssPrompt({
203
+ const selection = flagStyling ?? await renderCssPrompt({
204
204
  abortSignal: controller.signal,
205
205
  extraChoices: { none: "Skip and set up later" }
206
206
  });
@@ -226,7 +226,7 @@ async function handleCssStrategy(projectDir, controller, flagStyling) {
226
226
  };
227
227
  }
228
228
  async function handleDependencies(projectDir, controller, shouldInstallDeps) {
229
- const detectedPackageManager = await packageManagerUsedForCreating();
229
+ const detectedPackageManager = packageManagerFromUserAgent();
230
230
  let actualPackageManager = "npm";
231
231
  if (shouldInstallDeps !== false) {
232
232
  if (detectedPackageManager === "unknown") {
@@ -199,7 +199,7 @@ async function setupLocalStarterTemplate(options, controller) {
199
199
  );
200
200
  const { setupRoutes } = await handleRouteGeneration(
201
201
  controller,
202
- options.routes || true
202
+ options.routes ?? true
203
203
  // TODO: Remove default value when multi-select UI component is available
204
204
  );
205
205
  setupSummary.i18n = i18nStrategy;
@@ -30,7 +30,7 @@ function renderMissingLink({ session, cliCommand }) {
30
30
  type: 0,
31
31
  message: `No linked Hydrogen storefront on ${session.storeFqdn}`,
32
32
  tryMessage: [
33
- "To pull environment variables, link this project to a Hydrogen storefront. To select a storefront to link, run",
33
+ "To pull environment variables or to deploy to Oxygen, link this project to a Hydrogen storefront. To select a storefront to link, run",
34
34
  { command: `${cliCommand} link` }
35
35
  ]
36
36
  });
@@ -14,7 +14,7 @@ const NO_LOCALE_PATTERNS = [/robots\.txt/];
14
14
  const ROUTE_MAP = {
15
15
  home: ["_index", "$"],
16
16
  page: "pages*",
17
- cart: "cart",
17
+ cart: ["cart", "cart.$lines", "discount.$code"],
18
18
  products: "products*",
19
19
  collections: "collections*",
20
20
  policies: "policies*",
@@ -66,8 +66,8 @@ function Index() {
66
66
  "You\u2019re seeing this because you don\u2019t have a home route in your project yet. ",
67
67
  /* @__PURE__ */ jsx("br", {}),
68
68
  "Run ",
69
- /* @__PURE__ */ jsx("code", { children: "h2 generate route home" }),
70
- " to create your home route. Learn more about",
69
+ /* @__PURE__ */ jsx("code", { children: "h2 setup" }),
70
+ " to scaffold standard Shopify routes. Learn more about",
71
71
  ` `,
72
72
  /* @__PURE__ */ jsx(CreateRoutesLink, {})
73
73
  ] }) : /* @__PURE__ */ jsxs("p", { children: [
@@ -80,8 +80,8 @@ function Index() {
80
80
  /* @__PURE__ */ jsx("code", { children: "h2 link && h2 env pull" }),
81
81
  ". Then, run",
82
82
  " ",
83
- /* @__PURE__ */ jsx("code", { children: "h2 generate route home" }),
84
- " to create your first route.",
83
+ /* @__PURE__ */ jsx("code", { children: "h2 setup" }),
84
+ " to scaffold standard Shopify routes.",
85
85
  /* @__PURE__ */ jsx("br", {}),
86
86
  "Learn more about",
87
87
  ` `,