@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
@@ -0,0 +1,383 @@
1
+ import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
2
+ import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
3
+ import { inTemporaryDirectory, writeFile } from '@shopify/cli-kit/node/fs';
4
+ import { joinPath } from '@shopify/cli-kit/node/path';
5
+ import { renderConfirmationPrompt, renderSelectPrompt } from '@shopify/cli-kit/node/ui';
6
+ import { login } from '../../../lib/auth.js';
7
+ import { getStorefrontEnvVariables } from '../../../lib/graphql/admin/pull-variables.js';
8
+ import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-environments.js';
9
+ import { pushStorefrontEnvVariables } from '../../../lib/graphql/admin/push-variables.js';
10
+ import { runEnvPush } from './push__unstable.js';
11
+
12
+ vi.mock("@shopify/cli-kit/node/ui", async () => {
13
+ const original = await vi.importActual("@shopify/cli-kit/node/ui");
14
+ return {
15
+ ...original,
16
+ renderConfirmationPrompt: vi.fn(),
17
+ renderSelectPrompt: vi.fn()
18
+ };
19
+ });
20
+ vi.mock("../link.js");
21
+ vi.mock("../../../lib/auth.js");
22
+ vi.mock("../../../lib/render-errors.js");
23
+ vi.mock("../../../lib/graphql/admin/pull-variables.js");
24
+ vi.mock("../../../lib/graphql/admin/list-environments.js");
25
+ vi.mock("../../../lib/graphql/admin/push-variables.js");
26
+ const ADMIN_SESSION = {
27
+ token: "abc123",
28
+ storeFqdn: "my-shop"
29
+ };
30
+ const SHOPIFY_CONFIG = {
31
+ shop: "my-shop",
32
+ shopName: "My Shop",
33
+ email: "email",
34
+ storefront: {
35
+ id: "gid://shopify/HydrogenStorefront/2",
36
+ title: "Existing Link"
37
+ }
38
+ };
39
+ const PRODUCTION_ENV = {
40
+ id: "gid://shopify/HydrogenStorefrontEnvironment/1",
41
+ createdAt: "2024-01-01",
42
+ branch: "main",
43
+ name: "Production",
44
+ type: "PRODUCTION",
45
+ url: "production.com"
46
+ };
47
+ const PREVIEW_ENV = {
48
+ id: "gid://shopify/HydrogenStorefrontEnvironment/2",
49
+ createdAt: "2024-01-01",
50
+ branch: null,
51
+ name: "Preview",
52
+ type: "PREVIEW",
53
+ url: null
54
+ };
55
+ const CUSTOM_ENV = {
56
+ id: "gid://shopify/HydrogenStorefrontEnvironment/3",
57
+ createdAt: "2024-01-01",
58
+ branch: "staging",
59
+ name: "Staging",
60
+ type: "CUSTOM",
61
+ url: "custom.com"
62
+ };
63
+ const outputMock = mockAndCaptureOutput();
64
+ const processExit = vi.spyOn(process, "exit");
65
+ describe("pushVariables", () => {
66
+ beforeEach(async () => {
67
+ processExit.mockImplementation(() => {
68
+ throw "mockExit";
69
+ });
70
+ vi.mocked(login).mockResolvedValue({
71
+ session: ADMIN_SESSION,
72
+ config: SHOPIFY_CONFIG
73
+ });
74
+ vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
75
+ id: SHOPIFY_CONFIG.storefront.id,
76
+ environmentVariables: [
77
+ {
78
+ id: "gid://shopify/HydrogenStorefrontEnvironmentVariable/1",
79
+ key: "PUBLIC_API_TOKEN",
80
+ value: "abc123",
81
+ readOnly: true,
82
+ isSecret: false
83
+ },
84
+ {
85
+ id: "gid://shopify/HydrogenStorefrontEnvironmentVariable/2",
86
+ key: "PRIVATE_API_TOKEN",
87
+ value: "",
88
+ readOnly: true,
89
+ isSecret: true
90
+ }
91
+ ]
92
+ });
93
+ vi.mocked(getStorefrontEnvironments).mockResolvedValue({
94
+ id: SHOPIFY_CONFIG.storefront.id,
95
+ productionUrl: "prod.com",
96
+ environments: [PRODUCTION_ENV, PREVIEW_ENV, CUSTOM_ENV]
97
+ });
98
+ vi.mocked(pushStorefrontEnvVariables).mockResolvedValue({
99
+ userErrors: []
100
+ });
101
+ });
102
+ afterEach(() => {
103
+ outputMock.clear();
104
+ vi.clearAllMocks();
105
+ vi.resetAllMocks();
106
+ });
107
+ it("calls getStorefrontEnvironments", async () => {
108
+ vi.mocked(renderConfirmationPrompt).mockResolvedValue(true);
109
+ await inTemporaryDirectory(async (tmpDir) => {
110
+ const filePath = joinPath(tmpDir, ".env");
111
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=2");
112
+ await expect(
113
+ runEnvPush({ path: tmpDir, env: "Preview" })
114
+ ).resolves.not.toThrow();
115
+ expect(getStorefrontEnvironments).toHaveBeenCalledWith(
116
+ ADMIN_SESSION,
117
+ SHOPIFY_CONFIG.storefront.id
118
+ );
119
+ });
120
+ });
121
+ it("errors if no environments data", async () => {
122
+ vi.mocked(getStorefrontEnvironments).mockResolvedValue({
123
+ id: SHOPIFY_CONFIG.storefront.id,
124
+ productionUrl: "prod.com",
125
+ environments: []
126
+ });
127
+ await inTemporaryDirectory(async (tmpDir) => {
128
+ const filePath = joinPath(tmpDir, ".env");
129
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=2");
130
+ await expect(
131
+ runEnvPush({ path: tmpDir, env: "Preview" })
132
+ ).rejects.toThrowError("No environments found");
133
+ });
134
+ });
135
+ it("prompts the user to select an environment", async () => {
136
+ vi.mocked(renderSelectPrompt).mockResolvedValue(PREVIEW_ENV.id);
137
+ await inTemporaryDirectory(async (tmpDir) => {
138
+ const filePath = joinPath(tmpDir, ".env");
139
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=2");
140
+ await expect(runEnvPush({ path: tmpDir })).resolves.not.toThrow();
141
+ expect(renderSelectPrompt).toHaveBeenCalledWith({
142
+ message: "Select a set of environment variables to overwrite:",
143
+ choices: [
144
+ expect.objectContaining({ label: expect.stringContaining("Preview") }),
145
+ expect.objectContaining({ label: expect.stringContaining("Staging") }),
146
+ expect.objectContaining({
147
+ label: expect.stringContaining("Production")
148
+ })
149
+ ]
150
+ });
151
+ });
152
+ });
153
+ describe("when an environment is passed", () => {
154
+ it("errors when the environment does not match graphql", async () => {
155
+ await inTemporaryDirectory(async (tmpDir) => {
156
+ const filePath = joinPath(tmpDir, ".env");
157
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=2");
158
+ await expect(
159
+ runEnvPush({ path: tmpDir, env: "Something random" })
160
+ ).rejects.toThrowError("Environment not found");
161
+ });
162
+ });
163
+ it("prompts the user if there are multiple matches", async () => {
164
+ vi.mocked(renderSelectPrompt).mockResolvedValue(PREVIEW_ENV.id);
165
+ vi.mocked(getStorefrontEnvironments).mockResolvedValue({
166
+ id: SHOPIFY_CONFIG.storefront.id,
167
+ productionUrl: "prod.com",
168
+ environments: [
169
+ PRODUCTION_ENV,
170
+ PREVIEW_ENV,
171
+ { ...CUSTOM_ENV, name: "Preview" }
172
+ ]
173
+ });
174
+ await inTemporaryDirectory(async (tmpDir) => {
175
+ const filePath = joinPath(tmpDir, ".env");
176
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=2");
177
+ await expect(
178
+ runEnvPush({ path: tmpDir, env: "Preview" })
179
+ ).resolves.not.toThrow();
180
+ expect(renderSelectPrompt).toHaveBeenCalledWith({
181
+ message: "There were multiple environments found with the name Preview:",
182
+ choices: expect.any(Array)
183
+ });
184
+ });
185
+ });
186
+ });
187
+ it("exits if variables are identical", async () => {
188
+ vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
189
+ id: SHOPIFY_CONFIG.storefront.id,
190
+ environmentVariables: [
191
+ {
192
+ id: "1",
193
+ key: "EXISTING_TOKEN",
194
+ value: "1",
195
+ isSecret: false,
196
+ readOnly: false
197
+ },
198
+ {
199
+ id: "2",
200
+ key: "SECOND_TOKEN",
201
+ value: "2",
202
+ isSecret: false,
203
+ readOnly: false
204
+ }
205
+ ]
206
+ });
207
+ await inTemporaryDirectory(async (tmpDir) => {
208
+ const filePath = joinPath(tmpDir, ".env");
209
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=2");
210
+ await expect(
211
+ runEnvPush({ path: tmpDir, env: "Preview" })
212
+ ).resolves.not.toThrow();
213
+ expect(outputMock.info()).toMatch(
214
+ /No changes to your environment variables/
215
+ );
216
+ });
217
+ });
218
+ it("renders a diff when a variable is updated", async () => {
219
+ vi.mocked(renderConfirmationPrompt).mockResolvedValue(true);
220
+ vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
221
+ id: SHOPIFY_CONFIG.storefront.id,
222
+ environmentVariables: [
223
+ {
224
+ id: "1",
225
+ key: "EXISTING_TOKEN",
226
+ value: "1",
227
+ isSecret: false,
228
+ readOnly: false
229
+ },
230
+ {
231
+ id: "2",
232
+ key: "SECOND_TOKEN",
233
+ value: "updated value",
234
+ isSecret: false,
235
+ readOnly: false
236
+ }
237
+ ]
238
+ });
239
+ await inTemporaryDirectory(async (tmpDir) => {
240
+ const filePath = joinPath(tmpDir, ".env");
241
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=2");
242
+ await expect(
243
+ runEnvPush({ path: tmpDir, env: "Preview" })
244
+ ).resolves.not.toThrow();
245
+ expect(renderConfirmationPrompt).toHaveBeenCalled();
246
+ });
247
+ });
248
+ it("ignores comparison against secrets", async () => {
249
+ vi.mocked(renderConfirmationPrompt).mockResolvedValue(true);
250
+ vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
251
+ id: SHOPIFY_CONFIG.storefront.id,
252
+ environmentVariables: [
253
+ {
254
+ id: "1",
255
+ key: "EXISTING_TOKEN",
256
+ value: "1",
257
+ isSecret: false,
258
+ readOnly: false
259
+ },
260
+ {
261
+ id: "2",
262
+ key: "SECOND_TOKEN",
263
+ value: "updated value",
264
+ isSecret: true,
265
+ readOnly: false
266
+ }
267
+ ]
268
+ });
269
+ await inTemporaryDirectory(async (tmpDir) => {
270
+ const filePath = joinPath(tmpDir, ".env");
271
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=2");
272
+ await expect(
273
+ runEnvPush({ path: tmpDir, env: "Preview" })
274
+ ).resolves.not.toThrow();
275
+ });
276
+ expect(outputMock.info()).toMatch(
277
+ /No changes to your environment variables/
278
+ );
279
+ });
280
+ it("ignores comparison against read only variables", async () => {
281
+ vi.mocked(renderConfirmationPrompt).mockResolvedValue(true);
282
+ vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
283
+ id: SHOPIFY_CONFIG.storefront.id,
284
+ environmentVariables: [
285
+ {
286
+ id: "1",
287
+ key: "EXISTING_TOKEN",
288
+ value: "1",
289
+ isSecret: false,
290
+ readOnly: false
291
+ },
292
+ {
293
+ id: "2",
294
+ key: "SECOND_TOKEN",
295
+ value: "updated value",
296
+ isSecret: false,
297
+ readOnly: true
298
+ }
299
+ ]
300
+ });
301
+ await inTemporaryDirectory(async (tmpDir) => {
302
+ const filePath = joinPath(tmpDir, ".env");
303
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=2");
304
+ await expect(
305
+ runEnvPush({ path: tmpDir, env: "Preview" })
306
+ ).resolves.not.toThrow();
307
+ expect(outputMock.info()).toMatch(
308
+ /No changes to your environment variables/
309
+ );
310
+ });
311
+ });
312
+ it("exits when diff is not confirmed", async () => {
313
+ vi.mocked(renderConfirmationPrompt).mockResolvedValue(false);
314
+ vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
315
+ id: SHOPIFY_CONFIG.storefront.id,
316
+ environmentVariables: [
317
+ {
318
+ id: "1",
319
+ key: "EXISTING_TOKEN",
320
+ value: "1",
321
+ isSecret: false,
322
+ readOnly: false
323
+ },
324
+ {
325
+ id: "2",
326
+ key: "SECOND_TOKEN",
327
+ value: "updated value",
328
+ isSecret: false,
329
+ readOnly: true
330
+ }
331
+ ]
332
+ });
333
+ await inTemporaryDirectory(async (tmpDir) => {
334
+ const filePath = joinPath(tmpDir, ".env");
335
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=2");
336
+ await expect(
337
+ runEnvPush({ path: tmpDir, env: "Preview" })
338
+ ).resolves.not.toThrow();
339
+ expect(pushStorefrontEnvVariables).not.toHaveBeenCalled();
340
+ });
341
+ });
342
+ it("calls pushStorefrontEnvVariables when diff is confirmed", async () => {
343
+ vi.mocked(renderConfirmationPrompt).mockResolvedValue(true);
344
+ vi.mocked(getStorefrontEnvVariables).mockResolvedValue({
345
+ id: SHOPIFY_CONFIG.storefront.id,
346
+ environmentVariables: [
347
+ {
348
+ id: "1",
349
+ key: "EXISTING_TOKEN",
350
+ value: "1",
351
+ isSecret: false,
352
+ readOnly: false
353
+ },
354
+ {
355
+ id: "2",
356
+ key: "SECOND_TOKEN",
357
+ value: "2",
358
+ isSecret: false,
359
+ readOnly: false
360
+ }
361
+ ]
362
+ });
363
+ await inTemporaryDirectory(async (tmpDir) => {
364
+ const filePath = joinPath(tmpDir, ".env");
365
+ await writeFile(filePath, "EXISTING_TOKEN=1\nSECOND_TOKEN=NEW_VALUE");
366
+ await expect(
367
+ runEnvPush({ path: tmpDir, env: "Preview" })
368
+ ).resolves.not.toThrow();
369
+ expect(pushStorefrontEnvVariables).toHaveBeenCalledWith(
370
+ { storeFqdn: "my-shop", token: "abc123" },
371
+ "gid://shopify/HydrogenStorefront/2",
372
+ "gid://shopify/HydrogenStorefrontEnvironment/2",
373
+ [
374
+ { key: "EXISTING_TOKEN", value: "1" },
375
+ { key: "SECOND_TOKEN", value: "NEW_VALUE" }
376
+ ]
377
+ );
378
+ expect(outputMock.info()).toMatch(
379
+ /Environment variables push to Preview was successful/
380
+ );
381
+ });
382
+ });
383
+ });
@@ -22,8 +22,8 @@ class GenerateRoute extends Command {
22
22
  description: "The param name in Remix routes for the i18n locale, if any. Example: `locale` becomes ($locale).",
23
23
  env: "SHOPIFY_HYDROGEN_FLAG_ADAPTER"
24
24
  }),
25
- force: commonFlags.force,
26
- path: commonFlags.path
25
+ ...commonFlags.force,
26
+ ...commonFlags.path
27
27
  };
28
28
  static hidden;
29
29
  static args = {
@@ -0,0 +1,69 @@
1
+ import * as _oclif_core_lib_interfaces_parser_js from '@oclif/core/lib/interfaces/parser.js';
2
+ import Command from '@shopify/cli-kit/node/base-command';
3
+
4
+ declare const GENERATOR_SETUP_ASSETS_SUB_DIRS: readonly ["tailwind", "css-modules", "vanilla-extract", "postcss", "vite"];
5
+ type AssetDir = (typeof GENERATOR_SETUP_ASSETS_SUB_DIRS)[number];
6
+
7
+ type CssStrategy = Exclude<AssetDir, 'vite'>;
8
+
9
+ declare const I18N_CHOICES: readonly ["subfolders", "domains", "subdomains", "none"];
10
+ type I18nChoice = (typeof I18N_CHOICES)[number];
11
+
12
+ declare const STYLING_CHOICES: readonly [...CssStrategy[], "none"];
13
+ type StylingChoice = (typeof STYLING_CHOICES)[number];
14
+
15
+ type InitOptions = {
16
+ path?: string;
17
+ template?: string;
18
+ language?: Language;
19
+ mockShop?: boolean;
20
+ styling?: StylingChoice;
21
+ i18n?: I18nChoice;
22
+ token?: string;
23
+ force?: boolean;
24
+ routes?: boolean;
25
+ shortcut?: boolean;
26
+ installDeps?: boolean;
27
+ git?: boolean;
28
+ };
29
+ declare const LANGUAGES: {
30
+ readonly js: "JavaScript";
31
+ readonly ts: "TypeScript";
32
+ };
33
+ type Language = keyof typeof LANGUAGES;
34
+
35
+ declare class Init extends Command {
36
+ static description: string;
37
+ static flags: {
38
+ routes: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
39
+ git: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
40
+ shortcut: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
41
+ markets: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
42
+ styling: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
43
+ 'mock-shop': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
44
+ 'install-deps': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
45
+ path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
46
+ language: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
47
+ template: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
48
+ force: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
49
+ };
50
+ run(): Promise<void>;
51
+ }
52
+ declare function runInit(options?: InitOptions): Promise<{
53
+ language?: "js" | "ts" | undefined;
54
+ packageManager: "npm" | "pnpm" | "yarn" | "bun" | "unknown";
55
+ cssStrategy?: CssStrategy | undefined;
56
+ cliCommand: "h2" | "pnpm shopify hydrogen" | "yarn shopify hydrogen" | "bun shopify hydrogen" | "npx shopify hydrogen";
57
+ depsInstalled: boolean;
58
+ depsError?: Error | undefined;
59
+ i18n?: "subfolders" | "domains" | "subdomains" | undefined;
60
+ i18nError?: Error | undefined;
61
+ routes?: Record<string, string | string[]> | undefined;
62
+ routesError?: Error | undefined;
63
+ location: string;
64
+ name: string;
65
+ directory: string;
66
+ storefrontTitle?: string | undefined;
67
+ } | undefined>;
68
+
69
+ export { Init as default, runInit };
@@ -16,7 +16,7 @@ const FLAG_MAP = { f: "force" };
16
16
  class Init extends Command {
17
17
  static description = "Creates a new Hydrogen storefront.";
18
18
  static flags = {
19
- force: commonFlags.force,
19
+ ...commonFlags.force,
20
20
  path: Flags.string({
21
21
  description: "The path to the directory of the new Hydrogen storefront.",
22
22
  env: "SHOPIFY_HYDROGEN_FLAG_PATH"
@@ -30,15 +30,15 @@ class Init extends Command {
30
30
  description: "Scaffolds project based on an existing template or example from the Hydrogen repository.",
31
31
  env: "SHOPIFY_HYDROGEN_FLAG_TEMPLATE"
32
32
  }),
33
- "install-deps": commonFlags.installDeps,
33
+ ...commonFlags.installDeps,
34
34
  "mock-shop": Flags.boolean({
35
35
  description: "Use mock.shop as the data source for the storefront.",
36
36
  default: false,
37
37
  env: "SHOPIFY_HYDROGEN_FLAG_MOCK_DATA"
38
38
  }),
39
- styling: commonFlags.styling,
40
- markets: commonFlags.markets,
41
- shortcut: commonFlags.shortcut,
39
+ ...commonFlags.styling,
40
+ ...commonFlags.markets,
41
+ ...commonFlags.shortcut,
42
42
  routes: Flags.boolean({
43
43
  description: "Generate routes for all pages.",
44
44
  env: "SHOPIFY_HYDROGEN_FLAG_ROUTES",
@@ -114,9 +114,9 @@ describe("init", () => {
114
114
  language: "ts",
115
115
  template: "https://github.com/some/repo"
116
116
  })
117
- ).resolves;
117
+ ).resolves.ok;
118
118
  });
119
- expect(outputMock.error()).toMatch("--template");
119
+ await vi.waitFor(() => expect(outputMock.error()).toMatch("--template"));
120
120
  expect(processExit).toHaveBeenCalledWith(1);
121
121
  processExit.mockRestore();
122
122
  });
@@ -16,8 +16,8 @@ import { handleStorefrontSelection } from '../../lib/onboarding/common.js';
16
16
  class Link extends Command {
17
17
  static description = "Link a local project to one of your shop's Hydrogen storefronts.";
18
18
  static flags = {
19
- force: commonFlags.force,
20
- path: commonFlags.path,
19
+ ...commonFlags.force,
20
+ ...commonFlags.path,
21
21
  storefront: Flags.string({
22
22
  description: `The name of a Hydrogen Storefront (e.g. "Jane's Apparel")`,
23
23
  env: "SHOPIFY_HYDROGEN_STOREFRONT"
@@ -13,7 +13,7 @@ import { getCliCommand } from '../../lib/shell.js';
13
13
  class List extends Command {
14
14
  static description = "Returns a list of Hydrogen storefronts available on a given shop.";
15
15
  static flags = {
16
- path: commonFlags.path
16
+ ...commonFlags.path
17
17
  };
18
18
  async run() {
19
19
  const { flags } = await this.parse(List);
@@ -1,19 +1,12 @@
1
- import { Flags } from '@oclif/core';
2
1
  import Command from '@shopify/cli-kit/node/base-command';
3
- import { normalizeStoreFqdn } from '@shopify/cli-kit/node/context/fqdn';
4
2
  import { commonFlags } from '../../lib/flags.js';
5
3
  import { login, renderLoginSuccess } from '../../lib/auth.js';
6
4
 
7
5
  class Login extends Command {
8
6
  static description = "Login to your Shopify account.";
9
7
  static flags = {
10
- path: commonFlags.path,
11
- shop: Flags.string({
12
- char: "s",
13
- 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).",
14
- env: "SHOPIFY_SHOP",
15
- parse: async (input) => normalizeStoreFqdn(input)
16
- })
8
+ ...commonFlags.path,
9
+ ...commonFlags.shop
17
10
  };
18
11
  async run() {
19
12
  const { flags } = await this.parse(Login);
@@ -6,7 +6,7 @@ import { logout } from '../../lib/auth.js';
6
6
  class Logout extends Command {
7
7
  static description = "Logout of your local session.";
8
8
  static flags = {
9
- path: commonFlags.path
9
+ ...commonFlags.path
10
10
  };
11
11
  async run() {
12
12
  const { flags } = await this.parse(Logout);
@@ -6,17 +6,20 @@ import { startMiniOxygen } from '../../lib/mini-oxygen/index.js';
6
6
  import { getAllEnvironmentVariables } from '../../lib/environment-variables.js';
7
7
  import { getConfig } from '../../lib/shopify-config.js';
8
8
  import { findPort } from '../../lib/find-port.js';
9
+ import { fileExists } from '@shopify/cli-kit/node/fs';
10
+ import { joinPath } from '@shopify/cli-kit/node/path';
11
+ import { getViteConfig } from '../../lib/vite-config.js';
9
12
 
10
13
  class Preview extends Command {
11
14
  static description = "Runs a Hydrogen storefront in an Oxygen worker for production.";
12
15
  static flags = {
13
- path: commonFlags.path,
14
- port: commonFlags.port,
16
+ ...commonFlags.path,
17
+ ...commonFlags.port,
15
18
  worker: deprecated("--worker", { isBoolean: true }),
16
- "legacy-runtime": commonFlags.legacyRuntime,
17
- "env-branch": commonFlags.envBranch,
18
- "inspector-port": commonFlags.inspectorPort,
19
- debug: commonFlags.debug
19
+ ...commonFlags.legacyRuntime,
20
+ ...commonFlags.envBranch,
21
+ ...commonFlags.inspectorPort,
22
+ ...commonFlags.debug
20
23
  };
21
24
  async run() {
22
25
  const { flags } = await this.parse(Preview);
@@ -36,7 +39,12 @@ async function runPreview({
36
39
  if (!process.env.NODE_ENV)
37
40
  process.env.NODE_ENV = "production";
38
41
  muteDevLogs({ workerReload: false });
39
- const { root, buildPathWorkerFile, buildPathClient } = getProjectPaths(appPath);
42
+ let { root, buildPathWorkerFile, buildPathClient } = getProjectPaths(appPath);
43
+ if (!await fileExists(joinPath(root, buildPathWorkerFile))) {
44
+ const maybeResult = await getViteConfig(root).catch(() => null);
45
+ if (maybeResult)
46
+ buildPathWorkerFile = maybeResult.serverOutFile;
47
+ }
40
48
  const { shop, storefront } = await getConfig(root);
41
49
  const fetchRemote = !!shop && !!storefront?.id;
42
50
  const env = await getAllEnvironmentVariables({ root, fetchRemote, envBranch });
@@ -10,9 +10,9 @@ import { SETUP_CSS_STRATEGIES, renderCssPrompt, setupCssStrategy, CSS_STRATEGY_N
10
10
  class SetupCSS extends Command {
11
11
  static description = "Setup CSS strategies for your project.";
12
12
  static flags = {
13
- path: commonFlags.path,
14
- force: commonFlags.force,
15
- "install-deps": overrideFlag(commonFlags.installDeps, { default: true })
13
+ ...commonFlags.path,
14
+ ...commonFlags.force,
15
+ ...overrideFlag(commonFlags.installDeps, { "install-deps": { default: true } })
16
16
  };
17
17
  static args = {
18
18
  strategy: Args.string({
@@ -9,7 +9,7 @@ import { SETUP_I18N_STRATEGIES, renderI18nPrompt, setupI18nStrategy, I18N_STRATE
9
9
  class SetupMarkets extends Command {
10
10
  static description = "Setup support for multiple markets in your project.";
11
11
  static flags = {
12
- path: commonFlags.path
12
+ ...commonFlags.path
13
13
  };
14
14
  static args = {
15
15
  strategy: Args.string({
@@ -21,14 +21,14 @@ class SetupMarkets extends Command {
21
21
  async run() {
22
22
  const { flags, args } = await this.parse(SetupMarkets);
23
23
  const directory = flags.path ? resolvePath(flags.path) : process.cwd();
24
- await runSetupI18n({
24
+ await runSetupMarkets({
25
25
  ...flagsToCamelObject(flags),
26
26
  strategy: args.strategy,
27
27
  directory
28
28
  });
29
29
  }
30
30
  }
31
- async function runSetupI18n({
31
+ async function runSetupMarkets({
32
32
  strategy: flagStrategy,
33
33
  directory
34
34
  }) {
@@ -50,4 +50,4 @@ async function runSetupI18n({
50
50
  });
51
51
  }
52
52
 
53
- export { SetupMarkets as default, runSetupI18n };
53
+ export { SetupMarkets as default, runSetupMarkets };