@shopify/cli-hydrogen 6.0.2 → 6.1.1

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 (30) hide show
  1. package/dist/commands/hydrogen/deploy.js +25 -179
  2. package/dist/commands/hydrogen/dev.js +33 -23
  3. package/dist/commands/hydrogen/preview.js +20 -10
  4. package/dist/commands/hydrogen/shortcut.js +1 -0
  5. package/dist/commands/hydrogen/upgrade.js +705 -0
  6. package/dist/commands/hydrogen/upgrade.test.js +786 -0
  7. package/dist/generator-templates/starter/CHANGELOG.md +70 -0
  8. package/dist/generator-templates/starter/app/components/Footer.tsx +3 -1
  9. package/dist/generator-templates/starter/app/components/Layout.tsx +13 -10
  10. package/dist/generator-templates/starter/app/routes/[robots.txt].tsx +0 -27
  11. package/dist/generator-templates/starter/package.json +10 -9
  12. package/dist/generator-templates/starter/remix.env.d.ts +2 -0
  13. package/dist/lib/check-lockfile.js +1 -0
  14. package/dist/lib/codegen.js +1 -0
  15. package/dist/lib/flags.js +13 -2
  16. package/dist/lib/log.js +1 -0
  17. package/dist/lib/mini-oxygen/assets.js +118 -0
  18. package/dist/lib/mini-oxygen/common.js +2 -1
  19. package/dist/lib/mini-oxygen/index.js +3 -0
  20. package/dist/lib/mini-oxygen/node.js +15 -3
  21. package/dist/lib/mini-oxygen/workerd-inspector-logs.js +227 -0
  22. package/dist/lib/mini-oxygen/workerd-inspector-proxy.js +200 -0
  23. package/dist/lib/mini-oxygen/workerd-inspector.js +62 -235
  24. package/dist/lib/mini-oxygen/workerd.js +54 -47
  25. package/dist/lib/render-errors.js +2 -0
  26. package/dist/lib/setups/i18n/replacers.test.js +2 -0
  27. package/dist/lib/shell.js +1 -1
  28. package/oclif.manifest.json +90 -8
  29. package/package.json +10 -21
  30. package/dist/commands/hydrogen/deploy.test.js +0 -238
@@ -1,16 +1,8 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
- import colors from '@shopify/cli-kit/node/colors';
4
- import { outputWarn, outputInfo, outputContent } from '@shopify/cli-kit/node/output';
5
- import { AbortError } from '@shopify/cli-kit/node/error';
6
- import { getLatestGitCommit } from '@shopify/cli-kit/node/git';
7
- import { resolvePath } from '@shopify/cli-kit/node/path';
8
- import { renderFatalError, renderSelectPrompt, renderSuccess, renderTasks } from '@shopify/cli-kit/node/ui';
9
- import { ciPlatform } from '@shopify/cli-kit/node/context/local';
10
- import { parseToken, createDeploy } from '@shopify/oxygen-cli/deploy';
11
- import { commonFlags, flagsToCamelObject } from '../../lib/flags.js';
12
- import { getOxygenDeploymentData } from '../../lib/get-oxygen-deployment-data.js';
13
- import { runBuild } from './build.js';
3
+ import { outputWarn } from '@shopify/cli-kit/node/output';
4
+ import { renderWarning } from '@shopify/cli-kit/node/ui';
5
+ import { commonFlags } from '../../lib/flags.js';
14
6
 
15
7
  const deploymentLogger = (message, level = "info") => {
16
8
  if (level === "error" || level === "warn") {
@@ -24,6 +16,13 @@ class Deploy extends Command {
24
16
  description: "Environment branch (tag) for environment to deploy to",
25
17
  required: false
26
18
  }),
19
+ force: Flags.boolean({
20
+ char: "f",
21
+ description: "Forces a deployment to proceed if there are uncommited changes in its Git repository.",
22
+ default: false,
23
+ env: "SHOPIFY_HYDROGEN_FLAG_FORCE",
24
+ required: false
25
+ }),
27
26
  path: commonFlags.path,
28
27
  shop: commonFlags.shop,
29
28
  "public-deployment": Flags.boolean({
@@ -32,12 +31,22 @@ class Deploy extends Command {
32
31
  required: false,
33
32
  default: false
34
33
  }),
34
+ "no-json-output": Flags.boolean({
35
+ description: "Prevents the command from creating a JSON file containing the deployment URL (in CI environments).",
36
+ required: false,
37
+ default: false
38
+ }),
35
39
  token: Flags.string({
36
40
  char: "t",
37
41
  description: "Oxygen deployment token",
38
42
  env: "SHOPIFY_HYDROGEN_DEPLOYMENT_TOKEN",
39
43
  required: false
40
44
  }),
45
+ "metadata-description": Flags.string({
46
+ description: "Description of the changes in the deployment. Defaults to the commit message of the latest commit if there are no uncommited changes.",
47
+ required: false,
48
+ env: "SHOPIFY_HYDROGEN_FLAG_METADATA_DESCRIPTION"
49
+ }),
41
50
  "metadata-url": Flags.string({
42
51
  description: "URL that links to the deployment. Will be saved and displayed in the Shopify admin",
43
52
  required: false,
@@ -56,176 +65,13 @@ class Deploy extends Command {
56
65
  };
57
66
  static hidden = true;
58
67
  async run() {
59
- const { flags } = await this.parse(Deploy);
60
- const deploymentOptions = this.flagsToOxygenDeploymentOptions(flags);
61
- await oxygenDeploy(deploymentOptions).catch((error) => {
62
- renderFatalError(error);
63
- process.exit(1);
64
- }).finally(() => {
65
- process.exit(0);
66
- });
67
- }
68
- flagsToOxygenDeploymentOptions(flags) {
69
- const camelFlags = flagsToCamelObject(flags);
70
- return {
71
- ...camelFlags,
72
- environmentTag: flags["env-branch"],
73
- path: flags.path ? resolvePath(flags.path) : process.cwd()
74
- };
75
- }
76
- }
77
- async function oxygenDeploy(options) {
78
- const {
79
- environmentTag,
80
- path,
81
- shop,
82
- publicDeployment,
83
- metadataUrl,
84
- metadataUser,
85
- metadataVersion
86
- } = options;
87
- const ci = ciPlatform();
88
- let token = options.token;
89
- let branch;
90
- let deploymentData;
91
- let deploymentEnvironmentTag = void 0;
92
- let gitCommit;
93
- try {
94
- gitCommit = await getLatestGitCommit(path);
95
- branch = (/HEAD -> ([^,]*)/.exec(gitCommit.refs) || [])[1];
96
- } catch (error) {
97
- outputWarn("Could not retrieve Git history.");
98
- branch = void 0;
99
- }
100
- if (!ci.isCI) {
101
- deploymentData = await getOxygenDeploymentData({
102
- root: path,
103
- flagShop: shop
104
- });
105
- if (!deploymentData) {
106
- return;
107
- }
108
- token = token || deploymentData.oxygenDeploymentToken;
109
- }
110
- if (!token) {
111
- const errMessage = ci.isCI ? [
112
- "No deployment token provided. Use the ",
113
- { command: "--token" },
114
- " flag to provide a token."
115
- ] : `Could not obtain an Oxygen deployment token, please try again or contact Shopify support.`;
116
- throw new AbortError(errMessage);
117
- }
118
- if (!ci.isCI && !environmentTag && deploymentData?.environments) {
119
- if (deploymentData.environments.length > 1) {
120
- const choices = [
121
- ...deploymentData.environments.map(({ name, branch: branch2 }) => ({
122
- label: name,
123
- value: branch2
124
- }))
125
- ];
126
- deploymentEnvironmentTag = await renderSelectPrompt({
127
- message: "Select an environment to deploy to",
128
- choices,
129
- defaultValue: branch
130
- });
131
- } else {
132
- outputInfo(
133
- `Using current checked out branch ${branch} as environment tag`
134
- );
135
- }
136
- }
137
- const config = {
138
- assetsDir: "dist/client",
139
- bugsnag: true,
140
- deploymentUrl: "https://oxygen.shopifyapps.com",
141
- deploymentToken: parseToken(token),
142
- environmentTag: environmentTag || deploymentEnvironmentTag || branch,
143
- verificationMaxDuration: 180,
144
- metadata: {
145
- ...metadataUrl ? { url: metadataUrl } : {},
146
- ...metadataUser ? { user: metadataUser } : {},
147
- ...metadataVersion ? { version: metadataVersion } : {}
148
- },
149
- publicDeployment,
150
- skipVerification: false,
151
- rootPath: path,
152
- skipBuild: false,
153
- workerOnly: false,
154
- workerDir: "dist/worker"
155
- };
156
- let resolveUpload;
157
- const uploadPromise = new Promise((resolve) => {
158
- resolveUpload = resolve;
159
- });
160
- let resolveHealthCheck;
161
- const healthCheckPromise = new Promise((resolve) => {
162
- resolveHealthCheck = resolve;
163
- });
164
- let deployError = null;
165
- let resolveDeploy;
166
- let rejectDeploy;
167
- const deployPromise = new Promise((resolve, reject) => {
168
- resolveDeploy = resolve;
169
- rejectDeploy = reject;
170
- });
171
- const hooks = {
172
- buildFunction: async (assetPath) => {
173
- outputInfo(
174
- outputContent`${colors.whiteBright("Building project...")}`.value
175
- );
176
- await runBuild({
177
- directory: path,
178
- assetPath,
179
- sourcemap: false,
180
- useCodegen: false
181
- });
182
- },
183
- onVerificationComplete: () => resolveHealthCheck(),
184
- onUploadFilesStart: () => uploadStart(),
185
- onUploadFilesComplete: () => resolveUpload(),
186
- onVerificationError: (error) => {
187
- deployError = new AbortError(
188
- error.message,
189
- "Please verify the deployment status in the Shopify Admin and retry deploying if necessary."
190
- );
191
- },
192
- onUploadFilesError: (error) => {
193
- deployError = new AbortError(
194
- error.message,
195
- "Check your connection and try again. If the problem persists, try again later or contact support."
196
- );
197
- }
198
- };
199
- const uploadStart = async () => {
200
- outputInfo(
201
- outputContent`${colors.whiteBright("Deploying to Oxygen..\n")}`.value
202
- );
203
- await renderTasks([
204
- {
205
- title: "Uploading files",
206
- task: async () => await uploadPromise
207
- },
208
- {
209
- title: "Verifying deployment",
210
- task: async () => await healthCheckPromise
211
- }
212
- ]);
213
- };
214
- await createDeploy({ config, hooks, logger: deploymentLogger }).then((url) => {
215
- const deploymentType = config.publicDeployment ? "public" : "private";
216
- renderSuccess({
217
- body: ["Successfully deployed to Oxygen"],
68
+ renderWarning({
69
+ body: "Deploy command unavailable.",
218
70
  nextSteps: [
219
- [
220
- `Open ${url} in your browser to view your ${deploymentType} deployment`
221
- ]
71
+ "To use the deploy command, upgrade cli-hydrogen to v7.0.0+."
222
72
  ]
223
73
  });
224
- resolveDeploy();
225
- }).catch((error) => {
226
- rejectDeploy(deployError || error);
227
- });
228
- return deployPromise;
74
+ }
229
75
  }
230
76
 
231
- export { Deploy as default, deploymentLogger, oxygenDeploy };
77
+ export { Deploy as default, deploymentLogger };
@@ -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, DEFAULT_PORT } from '../../lib/flags.js';
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
- ["worker-unstable"]: commonFlags.workerRuntime,
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: Flags.boolean({
42
- description: "Attaches a Node inspector",
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: portFlag = DEFAULT_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
- port: portFlag,
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
- const showUpgrade = await checkingHydrogenVersion;
146
- if (showUpgrade)
147
- showUpgrade();
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, DEFAULT_PORT } from '../../lib/flags.js';
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
- ["worker-unstable"]: commonFlags.workerRuntime,
15
- ["env-branch"]: commonFlags.envBranch
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 = DEFAULT_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
- env
53
+ inspectorPort,
54
+ debug
45
55
  },
46
56
  workerRuntime
47
57
  );
@@ -22,6 +22,7 @@ Restart your terminal session and run \`${ALIAS_NAME}\` from your local project.
22
22
  name: "error",
23
23
  type: 0,
24
24
  message: "No supported shell found.",
25
+ skipOclifErrorHandling: true,
25
26
  tryMessage: "Please create a shortcut manually."
26
27
  });
27
28
  }