@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.
- package/dist/commands/hydrogen/build-vite.js +131 -0
- package/dist/commands/hydrogen/build.js +6 -15
- package/dist/commands/hydrogen/check.js +1 -1
- package/dist/commands/hydrogen/codegen.js +3 -3
- package/dist/commands/hydrogen/debug/cpu.js +1 -1
- package/dist/commands/hydrogen/deploy.js +46 -31
- package/dist/commands/hydrogen/deploy.test.js +35 -49
- package/dist/commands/hydrogen/dev-vite.js +159 -0
- package/dist/commands/hydrogen/dev.js +11 -14
- package/dist/commands/hydrogen/env/list.js +1 -1
- package/dist/commands/hydrogen/env/pull.js +3 -3
- package/dist/commands/hydrogen/env/pull.test.js +2 -0
- package/dist/commands/hydrogen/env/push__unstable.js +190 -0
- package/dist/commands/hydrogen/env/push__unstable.test.js +383 -0
- package/dist/commands/hydrogen/generate/route.js +2 -2
- package/dist/commands/hydrogen/init.d.ts +69 -0
- package/dist/commands/hydrogen/init.js +5 -5
- package/dist/commands/hydrogen/init.test.js +2 -2
- package/dist/commands/hydrogen/link.js +2 -2
- package/dist/commands/hydrogen/list.js +1 -1
- package/dist/commands/hydrogen/login.js +2 -9
- package/dist/commands/hydrogen/logout.js +1 -1
- package/dist/commands/hydrogen/preview.js +15 -7
- package/dist/commands/hydrogen/setup/css.js +3 -3
- package/dist/commands/hydrogen/setup/markets.js +4 -4
- package/dist/commands/hydrogen/setup/vite.js +209 -0
- package/dist/commands/hydrogen/setup.js +8 -6
- package/dist/commands/hydrogen/unlink.js +1 -1
- package/dist/commands/hydrogen/upgrade.js +5 -3
- package/dist/generator-templates/assets/vite/package.json +15 -0
- package/dist/generator-templates/assets/vite/vite.config.js +13 -0
- package/dist/generator-templates/starter/CHANGELOG.md +49 -0
- package/dist/generator-templates/starter/app/components/Search.tsx +12 -7
- package/dist/generator-templates/starter/app/root.tsx +1 -2
- package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +8 -15
- package/dist/generator-templates/starter/package.json +9 -8
- package/dist/generator-templates/starter/public/.gitkeep +0 -0
- package/dist/lib/build.js +2 -1
- package/dist/lib/codegen.js +8 -3
- package/dist/lib/environment-variables.test.js +4 -2
- package/dist/lib/flags.js +149 -95
- package/dist/lib/graphql/admin/pull-variables.js +1 -0
- package/dist/lib/graphql/admin/pull-variables.test.js +7 -1
- package/dist/lib/graphql/admin/push-variables.js +35 -0
- package/dist/lib/log.js +1 -0
- package/dist/lib/mini-oxygen/common.js +2 -1
- package/dist/lib/mini-oxygen/node.js +2 -2
- package/dist/lib/mini-oxygen/workerd-inspector.js +1 -1
- package/dist/lib/mini-oxygen/workerd.js +29 -17
- package/dist/lib/onboarding/common.js +0 -3
- package/dist/lib/onboarding/local.js +4 -1
- package/dist/lib/onboarding/remote.js +16 -11
- package/dist/lib/remix-config.js +1 -1
- package/dist/lib/request-events.js +3 -3
- package/dist/lib/setups/css/assets.js +7 -2
- package/dist/lib/template-diff.js +26 -11
- package/dist/lib/template-downloader.js +11 -2
- package/dist/lib/vite/hydrogen-middleware.js +82 -0
- package/dist/lib/vite/mini-oxygen.js +152 -0
- package/dist/lib/vite/plugins.d.ts +27 -0
- package/dist/lib/vite/plugins.js +139 -0
- package/dist/lib/vite/shared.js +10 -0
- package/dist/lib/vite/utils.js +55 -0
- package/dist/lib/vite/worker-entry.js +1518 -0
- package/dist/lib/vite-config.js +45 -0
- package/dist/virtual-routes/lib/useDebugNetworkServer.jsx +4 -2
- package/dist/virtual-routes/routes/index.jsx +5 -5
- package/dist/virtual-routes/routes/subrequest-profiler.jsx +1 -1
- package/dist/virtual-routes/virtual-root.jsx +1 -1
- package/oclif.manifest.json +1127 -494
- package/package.json +36 -11
- /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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
shop
|
|
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
|
-
|
|
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
|
-
|
|
14
|
-
|
|
16
|
+
...commonFlags.path,
|
|
17
|
+
...commonFlags.port,
|
|
15
18
|
worker: deprecated("--worker", { isBoolean: true }),
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
|
24
|
+
await runSetupMarkets({
|
|
25
25
|
...flagsToCamelObject(flags),
|
|
26
26
|
strategy: args.strategy,
|
|
27
27
|
directory
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
async function
|
|
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,
|
|
53
|
+
export { SetupMarkets as default, runSetupMarkets };
|