@shopify/cli-hydrogen 6.0.2 → 6.1.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.
- package/dist/commands/hydrogen/deploy.js +72 -8
- package/dist/commands/hydrogen/deploy.test.js +111 -9
- package/dist/commands/hydrogen/dev.js +33 -23
- package/dist/commands/hydrogen/preview.js +20 -10
- package/dist/commands/hydrogen/shortcut.js +1 -0
- package/dist/commands/hydrogen/upgrade.js +705 -0
- package/dist/commands/hydrogen/upgrade.test.js +786 -0
- package/dist/generator-templates/starter/CHANGELOG.md +70 -0
- package/dist/generator-templates/starter/app/components/Footer.tsx +3 -1
- package/dist/generator-templates/starter/app/components/Layout.tsx +13 -10
- package/dist/generator-templates/starter/app/routes/[robots.txt].tsx +0 -27
- package/dist/generator-templates/starter/package.json +10 -9
- package/dist/generator-templates/starter/remix.env.d.ts +2 -0
- package/dist/lib/check-lockfile.js +1 -0
- package/dist/lib/codegen.js +1 -0
- package/dist/lib/flags.js +13 -2
- package/dist/lib/log.js +1 -0
- package/dist/lib/mini-oxygen/assets.js +118 -0
- package/dist/lib/mini-oxygen/common.js +2 -1
- package/dist/lib/mini-oxygen/index.js +3 -0
- package/dist/lib/mini-oxygen/node.js +15 -3
- package/dist/lib/mini-oxygen/workerd-inspector-logs.js +227 -0
- package/dist/lib/mini-oxygen/workerd-inspector-proxy.js +200 -0
- package/dist/lib/mini-oxygen/workerd-inspector.js +62 -235
- package/dist/lib/mini-oxygen/workerd.js +54 -47
- package/dist/lib/render-errors.js +2 -0
- package/dist/lib/setups/i18n/replacers.test.js +2 -0
- package/dist/lib/shell.js +1 -1
- package/oclif.manifest.json +90 -8
- package/package.json +10 -21
|
@@ -3,9 +3,10 @@ import Command from '@shopify/cli-kit/node/base-command';
|
|
|
3
3
|
import colors from '@shopify/cli-kit/node/colors';
|
|
4
4
|
import { outputWarn, outputInfo, outputContent } from '@shopify/cli-kit/node/output';
|
|
5
5
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
6
|
-
import {
|
|
6
|
+
import { writeFile } from '@shopify/cli-kit/node/fs';
|
|
7
|
+
import { ensureIsClean, getLatestGitCommit, GitDirectoryNotCleanError } from '@shopify/cli-kit/node/git';
|
|
7
8
|
import { resolvePath } from '@shopify/cli-kit/node/path';
|
|
8
|
-
import { renderFatalError, renderSelectPrompt, renderSuccess, renderTasks } from '@shopify/cli-kit/node/ui';
|
|
9
|
+
import { renderFatalError, renderWarning, renderSelectPrompt, renderSuccess, renderTasks } from '@shopify/cli-kit/node/ui';
|
|
9
10
|
import { ciPlatform } from '@shopify/cli-kit/node/context/local';
|
|
10
11
|
import { parseToken, createDeploy } from '@shopify/oxygen-cli/deploy';
|
|
11
12
|
import { commonFlags, flagsToCamelObject } from '../../lib/flags.js';
|
|
@@ -24,6 +25,13 @@ class Deploy extends Command {
|
|
|
24
25
|
description: "Environment branch (tag) for environment to deploy to",
|
|
25
26
|
required: false
|
|
26
27
|
}),
|
|
28
|
+
force: Flags.boolean({
|
|
29
|
+
char: "f",
|
|
30
|
+
description: "Forces a deployment to proceed if there are uncommited changes in its Git repository.",
|
|
31
|
+
default: false,
|
|
32
|
+
env: "SHOPIFY_HYDROGEN_FLAG_FORCE",
|
|
33
|
+
required: false
|
|
34
|
+
}),
|
|
27
35
|
path: commonFlags.path,
|
|
28
36
|
shop: commonFlags.shop,
|
|
29
37
|
"public-deployment": Flags.boolean({
|
|
@@ -32,12 +40,22 @@ class Deploy extends Command {
|
|
|
32
40
|
required: false,
|
|
33
41
|
default: false
|
|
34
42
|
}),
|
|
43
|
+
"no-json-output": Flags.boolean({
|
|
44
|
+
description: "Prevents the command from creating a JSON file containing the deployment URL (in CI environments).",
|
|
45
|
+
required: false,
|
|
46
|
+
default: false
|
|
47
|
+
}),
|
|
35
48
|
token: Flags.string({
|
|
36
49
|
char: "t",
|
|
37
50
|
description: "Oxygen deployment token",
|
|
38
51
|
env: "SHOPIFY_HYDROGEN_DEPLOYMENT_TOKEN",
|
|
39
52
|
required: false
|
|
40
53
|
}),
|
|
54
|
+
"metadata-description": Flags.string({
|
|
55
|
+
description: "Description of the changes in the deployment. Defaults to the commit message of the latest commit if there are no uncommited changes.",
|
|
56
|
+
required: false,
|
|
57
|
+
env: "SHOPIFY_HYDROGEN_FLAG_METADATA_DESCRIPTION"
|
|
58
|
+
}),
|
|
41
59
|
"metadata-url": Flags.string({
|
|
42
60
|
description: "URL that links to the deployment. Will be saved and displayed in the Shopify admin",
|
|
43
61
|
required: false,
|
|
@@ -77,6 +95,8 @@ class Deploy extends Command {
|
|
|
77
95
|
async function oxygenDeploy(options) {
|
|
78
96
|
const {
|
|
79
97
|
environmentTag,
|
|
98
|
+
force: forceOnUncommitedChanges,
|
|
99
|
+
noJsonOutput,
|
|
80
100
|
path,
|
|
81
101
|
shop,
|
|
82
102
|
publicDeployment,
|
|
@@ -84,20 +104,53 @@ async function oxygenDeploy(options) {
|
|
|
84
104
|
metadataUser,
|
|
85
105
|
metadataVersion
|
|
86
106
|
} = options;
|
|
87
|
-
|
|
107
|
+
let { metadataDescription } = options;
|
|
108
|
+
let isCleanGit = true;
|
|
109
|
+
try {
|
|
110
|
+
await ensureIsClean(path);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (error instanceof GitDirectoryNotCleanError) {
|
|
113
|
+
isCleanGit = false;
|
|
114
|
+
}
|
|
115
|
+
if (!forceOnUncommitedChanges && !isCleanGit) {
|
|
116
|
+
throw new AbortError("Uncommitted changes detected.", null, [
|
|
117
|
+
[
|
|
118
|
+
"Commit your changes before deploying or use the ",
|
|
119
|
+
{ command: "--force" },
|
|
120
|
+
" flag to deploy with uncommitted changes."
|
|
121
|
+
]
|
|
122
|
+
]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const isCI = ciPlatform().isCI;
|
|
88
126
|
let token = options.token;
|
|
89
127
|
let branch;
|
|
128
|
+
let commitHash;
|
|
90
129
|
let deploymentData;
|
|
91
130
|
let deploymentEnvironmentTag = void 0;
|
|
92
131
|
let gitCommit;
|
|
93
132
|
try {
|
|
94
133
|
gitCommit = await getLatestGitCommit(path);
|
|
95
134
|
branch = (/HEAD -> ([^,]*)/.exec(gitCommit.refs) || [])[1];
|
|
135
|
+
commitHash = gitCommit.hash;
|
|
96
136
|
} catch (error) {
|
|
97
137
|
outputWarn("Could not retrieve Git history.");
|
|
98
138
|
branch = void 0;
|
|
99
139
|
}
|
|
100
|
-
if (!
|
|
140
|
+
if (!metadataDescription && !isCleanGit) {
|
|
141
|
+
renderWarning({
|
|
142
|
+
headline: "No deployment description provided",
|
|
143
|
+
body: [
|
|
144
|
+
"Deploying uncommited changes, but no description has been provided. Use the ",
|
|
145
|
+
{ command: "--metadata-description" },
|
|
146
|
+
"flag to provide a description. If no description is provided, the description defaults to ",
|
|
147
|
+
{ userInput: "<sha> with additional changes" },
|
|
148
|
+
" using the SHA of the last commit."
|
|
149
|
+
]
|
|
150
|
+
});
|
|
151
|
+
metadataDescription = `${commitHash} with additional changes`;
|
|
152
|
+
}
|
|
153
|
+
if (!isCI) {
|
|
101
154
|
deploymentData = await getOxygenDeploymentData({
|
|
102
155
|
root: path,
|
|
103
156
|
flagShop: shop
|
|
@@ -108,14 +161,14 @@ async function oxygenDeploy(options) {
|
|
|
108
161
|
token = token || deploymentData.oxygenDeploymentToken;
|
|
109
162
|
}
|
|
110
163
|
if (!token) {
|
|
111
|
-
const errMessage =
|
|
164
|
+
const errMessage = isCI ? [
|
|
112
165
|
"No deployment token provided. Use the ",
|
|
113
166
|
{ command: "--token" },
|
|
114
167
|
" flag to provide a token."
|
|
115
168
|
] : `Could not obtain an Oxygen deployment token, please try again or contact Shopify support.`;
|
|
116
169
|
throw new AbortError(errMessage);
|
|
117
170
|
}
|
|
118
|
-
if (!
|
|
171
|
+
if (!isCI && !environmentTag && deploymentData?.environments) {
|
|
119
172
|
if (deploymentData.environments.length > 1) {
|
|
120
173
|
const choices = [
|
|
121
174
|
...deploymentData.environments.map(({ name, branch: branch2 }) => ({
|
|
@@ -134,14 +187,22 @@ async function oxygenDeploy(options) {
|
|
|
134
187
|
);
|
|
135
188
|
}
|
|
136
189
|
}
|
|
190
|
+
let deploymentUrl = "https://oxygen.shopifyapps.com";
|
|
191
|
+
if (process.env.UNSAFE_SHOPIFY_HYDROGEN_DEPLOYMENT_URL) {
|
|
192
|
+
deploymentUrl = process.env.UNSAFE_SHOPIFY_HYDROGEN_DEPLOYMENT_URL;
|
|
193
|
+
outputWarn(
|
|
194
|
+
"Using a custom deployment service. Don't do this in production!"
|
|
195
|
+
);
|
|
196
|
+
}
|
|
137
197
|
const config = {
|
|
138
198
|
assetsDir: "dist/client",
|
|
139
199
|
bugsnag: true,
|
|
140
|
-
deploymentUrl
|
|
200
|
+
deploymentUrl,
|
|
141
201
|
deploymentToken: parseToken(token),
|
|
142
202
|
environmentTag: environmentTag || deploymentEnvironmentTag || branch,
|
|
143
203
|
verificationMaxDuration: 180,
|
|
144
204
|
metadata: {
|
|
205
|
+
...metadataDescription ? { description: metadataDescription } : {},
|
|
145
206
|
...metadataUrl ? { url: metadataUrl } : {},
|
|
146
207
|
...metadataUser ? { user: metadataUser } : {},
|
|
147
208
|
...metadataVersion ? { version: metadataVersion } : {}
|
|
@@ -211,7 +272,7 @@ async function oxygenDeploy(options) {
|
|
|
211
272
|
}
|
|
212
273
|
]);
|
|
213
274
|
};
|
|
214
|
-
await createDeploy({ config, hooks, logger: deploymentLogger }).then((url) => {
|
|
275
|
+
await createDeploy({ config, hooks, logger: deploymentLogger }).then(async (url) => {
|
|
215
276
|
const deploymentType = config.publicDeployment ? "public" : "private";
|
|
216
277
|
renderSuccess({
|
|
217
278
|
body: ["Successfully deployed to Oxygen"],
|
|
@@ -221,6 +282,9 @@ async function oxygenDeploy(options) {
|
|
|
221
282
|
]
|
|
222
283
|
]
|
|
223
284
|
});
|
|
285
|
+
if (isCI && !noJsonOutput) {
|
|
286
|
+
await writeFile("h2_deploy_log.json", JSON.stringify({ url }));
|
|
287
|
+
}
|
|
224
288
|
resolveDeploy();
|
|
225
289
|
}).catch((error) => {
|
|
226
290
|
rejectDeploy(deployError || error);
|
|
@@ -2,14 +2,18 @@ import { vi, describe, expect, beforeEach, afterEach, it } from 'vitest';
|
|
|
2
2
|
import { login } from '../../lib/auth.js';
|
|
3
3
|
import { getStorefronts } from '../../lib/graphql/admin/link-storefront.js';
|
|
4
4
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { writeFile } from '@shopify/cli-kit/node/fs';
|
|
6
|
+
import { renderSelectPrompt, renderSuccess, renderWarning, renderFatalError } from '@shopify/cli-kit/node/ui';
|
|
7
|
+
import { ensureIsClean, GitDirectoryNotCleanError, getLatestGitCommit } from '@shopify/cli-kit/node/git';
|
|
7
8
|
import { oxygenDeploy, deploymentLogger } from './deploy.js';
|
|
8
9
|
import { getOxygenDeploymentData } from '../../lib/get-oxygen-deployment-data.js';
|
|
9
10
|
import { createDeploy, parseToken } from '@shopify/oxygen-cli/deploy';
|
|
11
|
+
import { ciPlatform } from '@shopify/cli-kit/node/context/local';
|
|
10
12
|
|
|
11
13
|
vi.mock("../../lib/get-oxygen-deployment-data.js");
|
|
12
14
|
vi.mock("@shopify/oxygen-cli/deploy");
|
|
15
|
+
vi.mock("@shopify/cli-kit/node/fs");
|
|
16
|
+
vi.mock("@shopify/cli-kit/node/context/local");
|
|
13
17
|
vi.mock("../../lib/auth.js");
|
|
14
18
|
vi.mock("../../lib/shopify-config.js");
|
|
15
19
|
vi.mock("../../lib/graphql/admin/link-storefront.js");
|
|
@@ -30,17 +34,16 @@ vi.mock("@shopify/cli-kit/node/ui", async () => {
|
|
|
30
34
|
renderFatalError: vi.fn(),
|
|
31
35
|
renderSelectPrompt: vi.fn(),
|
|
32
36
|
renderSuccess: vi.fn(),
|
|
33
|
-
renderTasks: vi.fn()
|
|
37
|
+
renderTasks: vi.fn(),
|
|
38
|
+
renderWarning: vi.fn()
|
|
34
39
|
};
|
|
35
40
|
});
|
|
36
41
|
vi.mock("@shopify/cli-kit/node/git", async () => {
|
|
42
|
+
const actual = await vi.importActual("@shopify/cli-kit/node/git");
|
|
37
43
|
return {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
vi.mock("@shopify/cli-kit/node/context/local", async () => {
|
|
42
|
-
return {
|
|
43
|
-
ciPlatform: () => ({ isCI: false })
|
|
44
|
+
...actual,
|
|
45
|
+
getLatestGitCommit: vi.fn(),
|
|
46
|
+
ensureIsClean: vi.fn()
|
|
44
47
|
};
|
|
45
48
|
});
|
|
46
49
|
describe("deploy", () => {
|
|
@@ -63,6 +66,8 @@ describe("deploy", () => {
|
|
|
63
66
|
};
|
|
64
67
|
const originalExit = process.exit;
|
|
65
68
|
const deployParams = {
|
|
69
|
+
force: false,
|
|
70
|
+
noJsonOutput: false,
|
|
66
71
|
path: "./",
|
|
67
72
|
shop: "snowdevil.myshopify.com",
|
|
68
73
|
publicDeployment: false,
|
|
@@ -111,6 +116,7 @@ describe("deploy", () => {
|
|
|
111
116
|
session: ADMIN_SESSION,
|
|
112
117
|
config: UNLINKED_SHOPIFY_CONFIG
|
|
113
118
|
});
|
|
119
|
+
vi.mocked(ciPlatform).mockReturnValue({ isCI: false });
|
|
114
120
|
vi.mocked(getStorefronts).mockResolvedValue([
|
|
115
121
|
{
|
|
116
122
|
...FULL_SHOPIFY_CONFIG.storefront,
|
|
@@ -149,6 +155,81 @@ describe("deploy", () => {
|
|
|
149
155
|
});
|
|
150
156
|
expect(vi.mocked(renderSuccess)).toHaveBeenCalled;
|
|
151
157
|
});
|
|
158
|
+
it("errors when there are uncommited changes", async () => {
|
|
159
|
+
vi.mocked(ensureIsClean).mockRejectedValue(
|
|
160
|
+
new GitDirectoryNotCleanError("Uncommitted changes")
|
|
161
|
+
);
|
|
162
|
+
await expect(oxygenDeploy(deployParams)).rejects.toThrowError(
|
|
163
|
+
"Uncommitted changes detected"
|
|
164
|
+
);
|
|
165
|
+
expect(vi.mocked(createDeploy)).not.toHaveBeenCalled;
|
|
166
|
+
});
|
|
167
|
+
it("proceeds with warning and modified description when there are uncommited changes and the force flag is used", async () => {
|
|
168
|
+
vi.mocked(ensureIsClean).mockRejectedValue(
|
|
169
|
+
new GitDirectoryNotCleanError("Uncommitted changes")
|
|
170
|
+
);
|
|
171
|
+
vi.mocked(getLatestGitCommit).mockResolvedValue({
|
|
172
|
+
hash: "123",
|
|
173
|
+
message: "test commit",
|
|
174
|
+
date: "2021-01-01",
|
|
175
|
+
author_name: "test author",
|
|
176
|
+
author_email: "test@author.com",
|
|
177
|
+
body: "test body",
|
|
178
|
+
refs: "HEAD -> main"
|
|
179
|
+
});
|
|
180
|
+
await oxygenDeploy({
|
|
181
|
+
...deployParams,
|
|
182
|
+
force: true
|
|
183
|
+
});
|
|
184
|
+
expect(vi.mocked(renderWarning)).toHaveBeenCalledWith({
|
|
185
|
+
headline: "No deployment description provided",
|
|
186
|
+
body: expect.anything()
|
|
187
|
+
});
|
|
188
|
+
expect(vi.mocked(createDeploy)).toHaveBeenCalledWith({
|
|
189
|
+
config: {
|
|
190
|
+
...expectedConfig,
|
|
191
|
+
environmentTag: "main",
|
|
192
|
+
metadata: {
|
|
193
|
+
...expectedConfig.metadata,
|
|
194
|
+
description: "123 with additional changes"
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
hooks: expectedHooks,
|
|
198
|
+
logger: deploymentLogger
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
it("proceeds with provided description without warning when there are uncommited changes and the force flag is used", async () => {
|
|
202
|
+
vi.mocked(ensureIsClean).mockRejectedValue(
|
|
203
|
+
new GitDirectoryNotCleanError("Uncommitted changes")
|
|
204
|
+
);
|
|
205
|
+
vi.mocked(getLatestGitCommit).mockResolvedValue({
|
|
206
|
+
hash: "123",
|
|
207
|
+
message: "test commit",
|
|
208
|
+
date: "2021-01-01",
|
|
209
|
+
author_name: "test author",
|
|
210
|
+
author_email: "test@author.com",
|
|
211
|
+
body: "test body",
|
|
212
|
+
refs: "HEAD -> main"
|
|
213
|
+
});
|
|
214
|
+
await oxygenDeploy({
|
|
215
|
+
...deployParams,
|
|
216
|
+
force: true,
|
|
217
|
+
metadataDescription: "cool new stuff"
|
|
218
|
+
});
|
|
219
|
+
expect(vi.mocked(renderWarning)).not.toHaveBeenCalled;
|
|
220
|
+
expect(vi.mocked(createDeploy)).toHaveBeenCalledWith({
|
|
221
|
+
config: {
|
|
222
|
+
...expectedConfig,
|
|
223
|
+
environmentTag: "main",
|
|
224
|
+
metadata: {
|
|
225
|
+
...expectedConfig.metadata,
|
|
226
|
+
description: "cool new stuff"
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
hooks: expectedHooks,
|
|
230
|
+
logger: deploymentLogger
|
|
231
|
+
});
|
|
232
|
+
});
|
|
152
233
|
it("calls createDeploy with the checked out branch name", async () => {
|
|
153
234
|
vi.mocked(getLatestGitCommit).mockResolvedValue({
|
|
154
235
|
hash: "123",
|
|
@@ -184,6 +265,27 @@ describe("deploy", () => {
|
|
|
184
265
|
]
|
|
185
266
|
});
|
|
186
267
|
});
|
|
268
|
+
it("writes a file with JSON content in CI environments", async () => {
|
|
269
|
+
vi.mocked(ciPlatform).mockReturnValue({
|
|
270
|
+
isCI: true,
|
|
271
|
+
name: "github",
|
|
272
|
+
metadata: {}
|
|
273
|
+
});
|
|
274
|
+
const ciDeployParams = {
|
|
275
|
+
...deployParams,
|
|
276
|
+
token: "some-token",
|
|
277
|
+
metadataDescription: "cool new stuff"
|
|
278
|
+
};
|
|
279
|
+
await oxygenDeploy(ciDeployParams);
|
|
280
|
+
expect(vi.mocked(writeFile)).toHaveBeenCalledWith(
|
|
281
|
+
"h2_deploy_log.json",
|
|
282
|
+
JSON.stringify({ url: "https://a-lovely-deployment.com" })
|
|
283
|
+
);
|
|
284
|
+
vi.mocked(writeFile).mockClear();
|
|
285
|
+
ciDeployParams.noJsonOutput = true;
|
|
286
|
+
await oxygenDeploy(ciDeployParams);
|
|
287
|
+
expect(vi.mocked(writeFile)).not.toHaveBeenCalled();
|
|
288
|
+
});
|
|
187
289
|
it("handles error during uploadFiles", async () => {
|
|
188
290
|
const mockRenderFatalError = vi.fn();
|
|
189
291
|
vi.mocked(renderFatalError).mockImplementation(mockRenderFatalError);
|
|
@@ -7,11 +7,10 @@ import colors from '@shopify/cli-kit/node/colors';
|
|
|
7
7
|
import { copyPublicFiles } from './build.js';
|
|
8
8
|
import { getProjectPaths, assertOxygenChecks, handleRemixImportFail, getRemixConfig } from '../../lib/remix-config.js';
|
|
9
9
|
import { muteDevLogs, createRemixLogger, enhanceH2Logs } from '../../lib/log.js';
|
|
10
|
-
import { commonFlags, overrideFlag, deprecated, flagsToCamelObject
|
|
10
|
+
import { commonFlags, overrideFlag, deprecated, flagsToCamelObject } from '../../lib/flags.js';
|
|
11
11
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
12
12
|
import { Flags } from '@oclif/core';
|
|
13
|
-
import { startMiniOxygen } from '../../lib/mini-oxygen/index.js';
|
|
14
|
-
import { checkHydrogenVersion } from '../../lib/check-version.js';
|
|
13
|
+
import { buildAssetsUrl, startMiniOxygen } from '../../lib/mini-oxygen/index.js';
|
|
15
14
|
import { addVirtualRoutes } from '../../lib/virtual-routes.js';
|
|
16
15
|
import { spawnCodegenProcess } from '../../lib/codegen.js';
|
|
17
16
|
import { getAllEnvironmentVariables } from '../../lib/environment-variables.js';
|
|
@@ -19,6 +18,8 @@ import { getConfig } from '../../lib/shopify-config.js';
|
|
|
19
18
|
import { setupLiveReload } from '../../lib/live-reload.js';
|
|
20
19
|
import { checkRemixVersions } from '../../lib/remix-version-check.js';
|
|
21
20
|
import { getGraphiQLUrl } from '../../lib/graphiql-url.js';
|
|
21
|
+
import { displayDevUpgradeNotice } from './upgrade.js';
|
|
22
|
+
import { findPort } from '../../lib/find-port.js';
|
|
22
23
|
|
|
23
24
|
const LOG_REBUILDING = "\u{1F9F1} Rebuilding...";
|
|
24
25
|
const LOG_REBUILT = "\u{1F680} Rebuilt";
|
|
@@ -27,7 +28,7 @@ class Dev extends Command {
|
|
|
27
28
|
static flags = {
|
|
28
29
|
path: commonFlags.path,
|
|
29
30
|
port: commonFlags.port,
|
|
30
|
-
|
|
31
|
+
worker: commonFlags.workerRuntime,
|
|
31
32
|
codegen: overrideFlag(commonFlags.codegen, {
|
|
32
33
|
description: commonFlags.codegen.description + " It updates the types on file save."
|
|
33
34
|
}),
|
|
@@ -38,43 +39,42 @@ class Dev extends Command {
|
|
|
38
39
|
env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_VIRTUAL_ROUTES",
|
|
39
40
|
default: false
|
|
40
41
|
}),
|
|
41
|
-
debug:
|
|
42
|
-
|
|
43
|
-
env: "SHOPIFY_HYDROGEN_FLAG_DEBUG",
|
|
44
|
-
default: false
|
|
45
|
-
}),
|
|
42
|
+
debug: commonFlags.debug,
|
|
43
|
+
"inspector-port": commonFlags.inspectorPort,
|
|
46
44
|
host: deprecated("--host")(),
|
|
47
|
-
["env-branch"]: commonFlags.envBranch
|
|
45
|
+
["env-branch"]: commonFlags.envBranch,
|
|
46
|
+
["disable-version-check"]: Flags.boolean({
|
|
47
|
+
description: "Skip the version check when running `hydrogen dev`",
|
|
48
|
+
default: false,
|
|
49
|
+
required: false
|
|
50
|
+
})
|
|
48
51
|
};
|
|
49
52
|
async run() {
|
|
50
53
|
const { flags } = await this.parse(Dev);
|
|
51
54
|
const directory = flags.path ? path.resolve(flags.path) : process.cwd();
|
|
52
55
|
await runDev({
|
|
53
56
|
...flagsToCamelObject(flags),
|
|
54
|
-
useCodegen: flags.codegen,
|
|
55
|
-
workerRuntime: flags["worker-unstable"],
|
|
56
57
|
path: directory
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
async function runDev({
|
|
61
|
-
port:
|
|
62
|
+
port: appPort,
|
|
62
63
|
path: appPath,
|
|
63
|
-
useCodegen = false,
|
|
64
|
-
workerRuntime = false,
|
|
64
|
+
codegen: useCodegen = false,
|
|
65
|
+
worker: workerRuntime = false,
|
|
65
66
|
codegenConfigPath,
|
|
66
67
|
disableVirtualRoutes,
|
|
67
68
|
envBranch,
|
|
68
69
|
debug = false,
|
|
69
|
-
sourcemap = true
|
|
70
|
+
sourcemap = true,
|
|
71
|
+
disableVersionCheck = false,
|
|
72
|
+
inspectorPort
|
|
70
73
|
}) {
|
|
71
74
|
if (!process.env.NODE_ENV)
|
|
72
75
|
process.env.NODE_ENV = "development";
|
|
73
76
|
muteDevLogs();
|
|
74
|
-
if (debug)
|
|
75
|
-
(await import('node:inspector')).open();
|
|
76
77
|
const { root, publicPath, buildPathClient, buildPathWorkerFile } = getProjectPaths(appPath);
|
|
77
|
-
const checkingHydrogenVersion = checkHydrogenVersion(root);
|
|
78
78
|
const copyingFiles = copyPublicFiles(publicPath, buildPathClient);
|
|
79
79
|
const reloadConfig = async () => {
|
|
80
80
|
const config = await getRemixConfig(root);
|
|
@@ -90,6 +90,12 @@ async function runDev({
|
|
|
90
90
|
return [fileRelative, path.resolve(root, fileRelative)];
|
|
91
91
|
};
|
|
92
92
|
const serverBundleExists = () => fileExists(buildPathWorkerFile);
|
|
93
|
+
inspectorPort = debug ? await findPort(inspectorPort) : inspectorPort;
|
|
94
|
+
appPort = workerRuntime ? await findPort(appPort) : appPort;
|
|
95
|
+
const assetsPort = workerRuntime ? await findPort(appPort + 100) : 0;
|
|
96
|
+
if (assetsPort) {
|
|
97
|
+
process.env.HYDROGEN_ASSET_BASE_URL = buildAssetsUrl(assetsPort);
|
|
98
|
+
}
|
|
93
99
|
const [remixConfig, { shop, storefront }] = await Promise.all([
|
|
94
100
|
reloadConfig(),
|
|
95
101
|
getConfig(root)
|
|
@@ -112,7 +118,10 @@ async function runDev({
|
|
|
112
118
|
miniOxygen = await startMiniOxygen(
|
|
113
119
|
{
|
|
114
120
|
root,
|
|
115
|
-
|
|
121
|
+
debug,
|
|
122
|
+
assetsPort,
|
|
123
|
+
inspectorPort,
|
|
124
|
+
port: appPort,
|
|
116
125
|
watch: !liveReload,
|
|
117
126
|
buildPathWorkerFile,
|
|
118
127
|
buildPathClient,
|
|
@@ -142,9 +151,9 @@ View server-side network requests: ${miniOxygen.listeningAt}/debug-network`
|
|
|
142
151
|
spawnCodegenProcess({ ...remixConfig, configFilePath: codegenConfigPath });
|
|
143
152
|
}
|
|
144
153
|
checkRemixVersions();
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
154
|
+
if (!disableVersionCheck) {
|
|
155
|
+
displayDevUpgradeNotice({ targetPath: appPath });
|
|
156
|
+
}
|
|
148
157
|
}
|
|
149
158
|
const fileWatchCache = createFileWatchCache();
|
|
150
159
|
let skipRebuildLogs = false;
|
|
@@ -184,6 +193,7 @@ View server-side network requests: ${miniOxygen.listeningAt}/debug-network`
|
|
|
184
193
|
name: "BuildError",
|
|
185
194
|
type: 0,
|
|
186
195
|
message: "MiniOxygen cannot start because the server bundle has not been generated.",
|
|
196
|
+
skipOclifErrorHandling: true,
|
|
187
197
|
tryMessage: "This is likely due to an error in your app and Remix is unable to compile. Try fixing the app and MiniOxygen will start."
|
|
188
198
|
});
|
|
189
199
|
}
|
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
2
2
|
import { muteDevLogs } from '../../lib/log.js';
|
|
3
3
|
import { getProjectPaths } from '../../lib/remix-config.js';
|
|
4
|
-
import { commonFlags, flagsToCamelObject
|
|
4
|
+
import { commonFlags, flagsToCamelObject } from '../../lib/flags.js';
|
|
5
5
|
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
|
+
import { findPort } from '../../lib/find-port.js';
|
|
8
9
|
|
|
9
10
|
class Preview extends Command {
|
|
10
11
|
static description = "Runs a Hydrogen storefront in an Oxygen worker for production.";
|
|
11
12
|
static flags = {
|
|
12
13
|
path: commonFlags.path,
|
|
13
14
|
port: commonFlags.port,
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
worker: commonFlags.workerRuntime,
|
|
16
|
+
"env-branch": commonFlags.envBranch,
|
|
17
|
+
"inspector-port": commonFlags.inspectorPort,
|
|
18
|
+
debug: commonFlags.debug
|
|
16
19
|
};
|
|
17
20
|
async run() {
|
|
18
21
|
const { flags } = await this.parse(Preview);
|
|
19
22
|
await runPreview({
|
|
20
|
-
...flagsToCamelObject(flags)
|
|
21
|
-
workerRuntime: flags["worker-unstable"]
|
|
23
|
+
...flagsToCamelObject(flags)
|
|
22
24
|
});
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
async function runPreview({
|
|
26
|
-
port
|
|
28
|
+
port: appPort,
|
|
27
29
|
path: appPath,
|
|
28
|
-
workerRuntime = false,
|
|
29
|
-
envBranch
|
|
30
|
+
worker: workerRuntime = false,
|
|
31
|
+
envBranch,
|
|
32
|
+
inspectorPort,
|
|
33
|
+
debug
|
|
30
34
|
}) {
|
|
31
35
|
if (!process.env.NODE_ENV)
|
|
32
36
|
process.env.NODE_ENV = "production";
|
|
@@ -35,13 +39,19 @@ async function runPreview({
|
|
|
35
39
|
const { shop, storefront } = await getConfig(root);
|
|
36
40
|
const fetchRemote = !!shop && !!storefront?.id;
|
|
37
41
|
const env = await getAllEnvironmentVariables({ root, fetchRemote, envBranch });
|
|
42
|
+
appPort = workerRuntime ? await findPort(appPort) : appPort;
|
|
43
|
+
inspectorPort = debug ? await findPort(inspectorPort) : inspectorPort;
|
|
44
|
+
const assetsPort = workerRuntime ? await findPort(appPort + 100) : 0;
|
|
38
45
|
const miniOxygen = await startMiniOxygen(
|
|
39
46
|
{
|
|
40
47
|
root,
|
|
41
|
-
port,
|
|
48
|
+
port: appPort,
|
|
49
|
+
assetsPort,
|
|
50
|
+
env,
|
|
42
51
|
buildPathClient,
|
|
43
52
|
buildPathWorkerFile,
|
|
44
|
-
|
|
53
|
+
inspectorPort,
|
|
54
|
+
debug
|
|
45
55
|
},
|
|
46
56
|
workerRuntime
|
|
47
57
|
);
|