@shopify/cli-hydrogen 8.0.0 → 8.0.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 (32) hide show
  1. package/dist/commands/hydrogen/debug/cpu.js +7 -1
  2. package/dist/commands/hydrogen/deploy.js +10 -21
  3. package/dist/commands/hydrogen/dev-vite.js +5 -5
  4. package/dist/commands/hydrogen/dev.js +2 -2
  5. package/dist/commands/hydrogen/env/list.js +13 -20
  6. package/dist/commands/hydrogen/env/pull.js +12 -18
  7. package/dist/commands/hydrogen/env/push.js +11 -18
  8. package/dist/commands/hydrogen/init.js +8 -18
  9. package/dist/commands/hydrogen/link.js +5 -2
  10. package/dist/commands/hydrogen/setup.js +0 -1
  11. package/dist/generator-templates/assets/vite/vite.config.js +5 -0
  12. package/dist/generator-templates/starter/CHANGELOG.md +49 -0
  13. package/dist/generator-templates/starter/app/components/Footer.tsx +1 -1
  14. package/dist/generator-templates/starter/app/components/Header.tsx +1 -1
  15. package/dist/generator-templates/starter/app/lib/root-data.ts +11 -0
  16. package/dist/generator-templates/starter/app/root.tsx +2 -15
  17. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +3 -3
  18. package/dist/generator-templates/starter/app/routes/cart.tsx +1 -1
  19. package/dist/generator-templates/starter/app/routes/collections.all.tsx +1 -1
  20. package/dist/generator-templates/starter/package.json +6 -6
  21. package/dist/generator-templates/starter/vite.config.ts +5 -0
  22. package/dist/init.d.ts +0 -1
  23. package/dist/lib/dev-shared.js +3 -1
  24. package/dist/lib/flags.js +0 -10
  25. package/dist/lib/get-oxygen-deployment-data.js +10 -17
  26. package/dist/lib/onboarding/common.js +3 -0
  27. package/dist/lib/render-errors.js +17 -10
  28. package/dist/lib/transpile/morph/functions.js +26 -8
  29. package/dist/lib/transpile/morph/typedefs.js +2 -3
  30. package/dist/lib/verify-linked-storefront.js +24 -0
  31. package/oclif.manifest.json +2 -19
  32. package/package.json +2 -2
@@ -4,8 +4,9 @@ import Command from '@shopify/cli-kit/node/base-command';
4
4
  import { outputInfo, outputWarn } from '@shopify/cli-kit/node/output';
5
5
  import colors from '@shopify/cli-kit/node/colors';
6
6
  import { writeFile } from '@shopify/cli-kit/node/fs';
7
+ import { AbortError } from '@shopify/cli-kit/node/error';
7
8
  import ansiEscapes from 'ansi-escapes';
8
- import { getProjectPaths, handleRemixImportFail, getRemixConfig } from '../../../lib/remix-config.js';
9
+ import { getProjectPaths, hasRemixConfigFile, handleRemixImportFail, getRemixConfig } from '../../../lib/remix-config.js';
9
10
  import { muteDevLogs, createRemixLogger } from '../../../lib/log.js';
10
11
  import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
11
12
  import { createCpuStartupProfiler } from '../../../lib/cpu-profiler.js';
@@ -43,6 +44,11 @@ async function runDebugCpu({
43
44
  process.env.NODE_ENV = "production";
44
45
  muteDevLogs({ workerReload: false });
45
46
  const { root, buildPathWorkerFile } = getProjectPaths(appPath);
47
+ if (!await hasRemixConfigFile(root)) {
48
+ throw new AbortError(
49
+ "No remix.config.js file found. This command is not supported in Vite projects."
50
+ );
51
+ }
46
52
  outputInfo(
47
53
  "\u23F3\uFE0F Starting profiler for CPU startup... Profile will be written to:\n" + colors.dim(output)
48
54
  );
@@ -350,27 +350,16 @@ ${changedFiles.trimEnd()}`;
350
350
  workerDir,
351
351
  overriddenEnvironmentVariables
352
352
  };
353
- if (!isCI && (userProvidedEnvironmentTag || userChosenEnvironmentTag || config.defaultEnvironment)) {
354
- let chosenEnvironment = null;
355
- if (config.defaultEnvironment) {
356
- chosenEnvironment = findEnvironmentOrThrow(
357
- deploymentData.environments,
358
- "preview"
359
- );
360
- } else if (config.environmentTag) {
361
- chosenEnvironment = findEnvironmentByBranchOrThrow(
362
- deploymentData.environments,
363
- config.environmentTag
364
- );
365
- }
366
- let confirmationMessage = "Creating a deployment";
367
- if (chosenEnvironment) {
368
- confirmationMessage += ` against ${createEnvironmentCliChoiceLabel(
369
- chosenEnvironment.name,
370
- chosenEnvironment.handle,
371
- chosenEnvironment.branch
372
- )}`;
373
- }
353
+ if (!isCI && !config.defaultEnvironment && (userProvidedEnvironmentTag || userChosenEnvironmentTag)) {
354
+ let chosenEnvironment = findEnvironmentByBranchOrThrow(
355
+ deploymentData.environments,
356
+ config.environmentTag
357
+ );
358
+ let confirmationMessage = `Creating a deployment against ${createEnvironmentCliChoiceLabel(
359
+ chosenEnvironment.name,
360
+ chosenEnvironment.handle,
361
+ chosenEnvironment.branch
362
+ )}`;
374
363
  const confirmPush = await renderConfirmationPrompt({
375
364
  confirmationMessage: "Yes, confirm deploy",
376
365
  cancellationMessage: "No, cancel deploy",
@@ -96,12 +96,12 @@ async function runViteDev({
96
96
  customerAccountPushFlag
97
97
  );
98
98
  const envPromise = backgroundPromise.then(
99
- ({ fetchRemote, localVariables: localVariables2 }) => getAllEnvironmentVariables({
99
+ ({ fetchRemote, localVariables }) => getAllEnvironmentVariables({
100
100
  root,
101
101
  envBranch,
102
102
  envHandle,
103
103
  fetchRemote,
104
- localVariables: localVariables2
104
+ localVariables
105
105
  })
106
106
  );
107
107
  if (debug && !inspectorPort) {
@@ -130,7 +130,7 @@ async function runViteDev({
130
130
  findOxygenPlugin(config)?.api?.registerPluginOptions({
131
131
  debug,
132
132
  entry: ssrEntry,
133
- envPromise: envPromise.then(({ allVariables }) => allVariables),
133
+ envPromise: envPromise.then(({ allVariables: allVariables2 }) => allVariables2),
134
134
  inspectorPort,
135
135
  logRequestLine
136
136
  });
@@ -186,7 +186,7 @@ async function runViteDev({
186
186
  host: finalHost,
187
187
  cliCommand
188
188
  });
189
- const { logInjectedVariables, localVariables } = await envPromise;
189
+ const { logInjectedVariables, allVariables } = await envPromise;
190
190
  logInjectedVariables();
191
191
  console.log("");
192
192
  viteServer.printUrls();
@@ -212,7 +212,7 @@ async function runViteDev({
212
212
  if (!disableVersionCheck) {
213
213
  displayDevUpgradeNotice({ targetPath: root });
214
214
  }
215
- if (customerAccountPushFlag && isMockShop(localVariables)) {
215
+ if (customerAccountPushFlag && isMockShop(allVariables)) {
216
216
  notifyIssueWithTunnelAndMockShop(cliCommand);
217
217
  }
218
218
  return {
@@ -168,7 +168,7 @@ async function runDev({
168
168
  async function safeStartMiniOxygen() {
169
169
  if (miniOxygen)
170
170
  return;
171
- const { allVariables, localVariables, logInjectedVariables } = await envPromise;
171
+ const { allVariables, logInjectedVariables } = await envPromise;
172
172
  miniOxygen = await startMiniOxygen(
173
173
  {
174
174
  root,
@@ -204,7 +204,7 @@ async function runDev({
204
204
  if (!disableVersionCheck) {
205
205
  displayDevUpgradeNotice({ targetPath: appPath });
206
206
  }
207
- if (customerAccountPushFlag && isMockShop(localVariables)) {
207
+ if (customerAccountPushFlag && isMockShop(allVariables)) {
208
208
  notifyIssueWithTunnelAndMockShop(cliCommand);
209
209
  }
210
210
  }
@@ -1,14 +1,13 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
- import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
3
2
  import { pluralize } from '@shopify/cli-kit/common/string';
4
3
  import { outputInfo, outputNewline, outputContent } from '@shopify/cli-kit/node/output';
5
- import { linkStorefront } from '../link.js';
6
4
  import { commonFlags } from '../../../lib/flags.js';
7
5
  import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-environments.js';
8
6
  import { createEnvironmentCliChoiceLabel } from '../../../lib/common.js';
9
- import { renderMissingLink, renderMissingStorefront } from '../../../lib/render-errors.js';
7
+ import { renderMissingStorefront } from '../../../lib/render-errors.js';
10
8
  import { login } from '../../../lib/auth.js';
11
9
  import { getCliCommand } from '../../../lib/shell.js';
10
+ import { verifyLinkedStorefront } from '../../../lib/verify-linked-storefront.js';
12
11
 
13
12
  class EnvList extends Command {
14
13
  static descriptionWithMarkdown = "Lists all environments available on the linked Hydrogen storefront.";
@@ -26,29 +25,23 @@ async function runEnvList({ path: root = process.cwd() }) {
26
25
  login(root),
27
26
  getCliCommand()
28
27
  ]);
29
- let configStorefront = config.storefront;
30
- if (!configStorefront?.id) {
31
- renderMissingLink({ session, cliCommand });
32
- const runLink = await renderConfirmationPrompt({
33
- message: ["Run", { command: `${cliCommand} link` }, "?"]
34
- });
35
- if (!runLink) {
36
- return;
37
- }
38
- configStorefront = await linkStorefront(root, session, config, {
39
- cliCommand
40
- });
41
- }
42
- if (!configStorefront)
28
+ const linkedStorefront = await verifyLinkedStorefront({
29
+ root,
30
+ session,
31
+ config,
32
+ cliCommand
33
+ });
34
+ if (!linkedStorefront)
43
35
  return;
36
+ config.storefront = linkedStorefront;
44
37
  const storefront = await getStorefrontEnvironments(
45
38
  session,
46
- configStorefront.id
39
+ config.storefront.id
47
40
  );
48
41
  if (!storefront) {
49
42
  renderMissingStorefront({
50
43
  session,
51
- storefront: configStorefront,
44
+ storefront: config.storefront,
52
45
  cliCommand
53
46
  });
54
47
  return;
@@ -64,7 +57,7 @@ async function runEnvList({ path: root = process.cwd() }) {
64
57
  outputInfo(
65
58
  pluralizedEnvironments({
66
59
  environments: storefront.environments,
67
- storefrontTitle: configStorefront.title
60
+ storefrontTitle: config.storefront.title
68
61
  }).toString()
69
62
  );
70
63
  storefront.environments.forEach(({ name, handle, branch, type, url }) => {
@@ -1,7 +1,7 @@
1
1
  import { diffLines } from 'diff';
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
- import { renderConfirmationPrompt, renderInfo, renderWarning, renderSuccess } from '@shopify/cli-kit/node/ui';
4
- import { outputContent, outputToken, outputInfo } from '@shopify/cli-kit/node/output';
3
+ import { renderInfo, renderConfirmationPrompt, renderWarning, renderSuccess } from '@shopify/cli-kit/node/ui';
4
+ import { outputInfo, outputContent, outputToken } from '@shopify/cli-kit/node/output';
5
5
  import { fileExists, readFile, writeFile } from '@shopify/cli-kit/node/fs';
6
6
  import { resolvePath } from '@shopify/cli-kit/node/path';
7
7
  import { patchEnvFile } from '@shopify/cli-kit/node/dot-env';
@@ -10,10 +10,10 @@ import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
10
10
  import { login } from '../../../lib/auth.js';
11
11
  import { getCliCommand } from '../../../lib/shell.js';
12
12
  import { findEnvironmentOrThrow, findEnvironmentByBranchOrThrow } from '../../../lib/common.js';
13
- import { renderMissingLink, renderMissingStorefront } from '../../../lib/render-errors.js';
14
- import { linkStorefront } from '../link.js';
13
+ import { renderMissingStorefront } from '../../../lib/render-errors.js';
15
14
  import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-environments.js';
16
15
  import { getStorefrontEnvVariables } from '../../../lib/graphql/admin/pull-variables.js';
16
+ import { verifyLinkedStorefront } from '../../../lib/verify-linked-storefront.js';
17
17
 
18
18
  class EnvPull extends Command {
19
19
  static descriptionWithMarkdown = "Pulls environment variables from the linked Hydrogen storefront and writes them to an `.env` file.";
@@ -39,21 +39,15 @@ async function runEnvPull({
39
39
  login(root),
40
40
  getCliCommand()
41
41
  ]);
42
- if (!config.storefront?.id) {
43
- renderMissingLink({ session, cliCommand });
44
- const runLink = await renderConfirmationPrompt({
45
- message: outputContent`Run ${outputToken.genericShellCommand(
46
- `${cliCommand} link`
47
- )}?`.value
48
- });
49
- if (!runLink)
50
- return;
51
- config.storefront = await linkStorefront(root, session, config, {
52
- cliCommand
53
- });
54
- }
55
- if (!config.storefront?.id)
42
+ const linkedStorefront = await verifyLinkedStorefront({
43
+ root,
44
+ session,
45
+ config,
46
+ cliCommand
47
+ });
48
+ if (!linkedStorefront)
56
49
  return;
50
+ config.storefront = linkedStorefront;
57
51
  if (envHandle || envBranch) {
58
52
  const environments = (await getStorefrontEnvironments(session, config.storefront.id))?.environments || [];
59
53
  if (envHandle) {
@@ -5,16 +5,15 @@ import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
5
5
  import { login } from '../../../lib/auth.js';
6
6
  import { getCliCommand } from '../../../lib/shell.js';
7
7
  import { resolvePath } from '@shopify/cli-kit/node/path';
8
- import { renderConfirmationPrompt, renderSelectPrompt, renderInfo, renderSuccess } from '@shopify/cli-kit/node/ui';
9
- import { outputContent, outputToken, outputWarn } from '@shopify/cli-kit/node/output';
8
+ import { renderSelectPrompt, renderInfo, renderConfirmationPrompt, renderSuccess } from '@shopify/cli-kit/node/ui';
9
+ import { outputWarn, outputContent, outputToken } from '@shopify/cli-kit/node/output';
10
10
  import { orderEnvironmentsBySafety, findEnvironmentOrThrow, createEnvironmentCliChoiceLabel } from '../../../lib/common.js';
11
- import { renderMissingLink } from '../../../lib/render-errors.js';
12
11
  import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-environments.js';
13
- import { linkStorefront } from '../link.js';
14
12
  import { getStorefrontEnvVariables } from '../../../lib/graphql/admin/pull-variables.js';
15
13
  import { pushStorefrontEnvVariables } from '../../../lib/graphql/admin/push-variables.js';
16
14
  import { AbortError } from '@shopify/cli-kit/node/error';
17
15
  import { readAndParseDotEnv, createDotEnvFileLine } from '@shopify/cli-kit/node/dot-env';
16
+ import { verifyLinkedStorefront } from '../../../lib/verify-linked-storefront.js';
18
17
 
19
18
  class EnvPush extends Command {
20
19
  static description = "Push environment variables from the local .env file to your linked Hydrogen storefront.";
@@ -42,21 +41,15 @@ async function runEnvPush({
42
41
  login(path),
43
42
  getCliCommand()
44
43
  ]);
45
- if (!config.storefront?.id) {
46
- renderMissingLink({ session, cliCommand });
47
- const runLink = await renderConfirmationPrompt({
48
- message: outputContent`Run ${outputToken.genericShellCommand(
49
- `${cliCommand} link`
50
- )}?`.value
51
- });
52
- if (!runLink)
53
- return;
54
- config.storefront = await linkStorefront(path, session, config, {
55
- cliCommand
56
- });
57
- }
58
- if (!config.storefront?.id)
44
+ const linkedStorefront = await verifyLinkedStorefront({
45
+ root: path,
46
+ session,
47
+ config,
48
+ cliCommand
49
+ });
50
+ if (!linkedStorefront)
59
51
  return;
52
+ config.storefront = linkedStorefront;
60
53
  const { environments: environmentsData } = await getStorefrontEnvironments(session, config.storefront.id) ?? {};
61
54
  if (!environmentsData) {
62
55
  throw new AbortError("Failed to fetch environments");
@@ -6,7 +6,6 @@ import { AbortError } from '@shopify/cli-kit/node/error';
6
6
  import { AbortController } from '@shopify/cli-kit/node/abort';
7
7
  import { commonFlags, flagsToCamelObject, parseProcessFlags } from '../../lib/flags.js';
8
8
  import { checkHydrogenVersion } from '../../lib/check-version.js';
9
- import { STYLING_CHOICES } from './../../lib/setups/css/index.js';
10
9
  import { I18N_CHOICES } from '../../lib/setups/i18n/index.js';
11
10
  import { supressNodeExperimentalWarnings } from '../../lib/process.js';
12
11
  import { setupRemoteTemplate, setupLocalStarterTemplate } from '../../lib/onboarding/index.js';
@@ -34,16 +33,13 @@ class Init extends Command {
34
33
  ...commonFlags.installDeps,
35
34
  "mock-shop": Flags.boolean({
36
35
  description: "Use mock.shop as the data source for the storefront.",
37
- default: false,
38
36
  env: "SHOPIFY_HYDROGEN_FLAG_MOCK_DATA"
39
37
  }),
40
- ...commonFlags.styling,
41
38
  ...commonFlags.markets,
42
39
  ...commonFlags.shortcut,
43
40
  routes: Flags.boolean({
44
41
  description: "Generate routes for all pages.",
45
42
  env: "SHOPIFY_HYDROGEN_FLAG_ROUTES",
46
- hidden: true,
47
43
  allowNo: true
48
44
  }),
49
45
  git: Flags.boolean({
@@ -53,7 +49,7 @@ class Init extends Command {
53
49
  allowNo: true
54
50
  }),
55
51
  quickstart: Flags.boolean({
56
- description: "Scaffolds a new Hydrogen project with a set of sensible defaults.",
52
+ description: "Scaffolds a new Hydrogen project with a set of sensible defaults. Equivalent to `shopify hydrogen init --path hydrogen-quickstart --mock-shop --language js --shortcut --routes --markets none`",
57
53
  env: "SHOPIFY_HYDROGEN_FLAG_QUICKSTART",
58
54
  default: false
59
55
  }),
@@ -84,21 +80,15 @@ async function runInit({
84
80
  `Invalid URL structure strategy: ${options.i18n}. Must be one of ${I18N_CHOICES.join(", ")}`
85
81
  );
86
82
  }
87
- if (options.styling && !STYLING_CHOICES.includes(options.styling)) {
88
- throw new AbortError(
89
- `Invalid styling strategy: ${options.styling}. Must be one of ${STYLING_CHOICES.join(", ")}`
90
- );
91
- }
92
83
  options.git ??= true;
93
84
  if (options.quickstart) {
94
- options.i18n ||= "none";
95
- options.installDeps ||= true;
96
- options.language ||= "js";
97
- options.mockShop ||= true;
98
- options.path ||= "./hydrogen-quickstart";
99
- options.routes ||= true;
100
- options.shortcut ||= true;
101
- options.styling ||= "tailwind";
85
+ options.i18n ??= "none";
86
+ options.installDeps ??= true;
87
+ options.language ??= "js";
88
+ options.mockShop ??= true;
89
+ options.path ??= "./hydrogen-quickstart";
90
+ options.routes ??= true;
91
+ options.shortcut ??= true;
102
92
  }
103
93
  const showUpgrade = await checkHydrogenVersion(
104
94
  // Resolving the CLI package from a local directory might fail because
@@ -63,7 +63,8 @@ async function runLink({
63
63
  async function linkStorefront(root, session, config, {
64
64
  force = false,
65
65
  flagStorefront,
66
- cliCommand
66
+ cliCommand,
67
+ storefronts
67
68
  }) {
68
69
  if (!config.shop) {
69
70
  throw new AbortError("No shop found in local config, login first.");
@@ -76,7 +77,9 @@ async function linkStorefront(root, session, config, {
76
77
  return;
77
78
  }
78
79
  }
79
- const storefronts = await getStorefronts(session);
80
+ if (!storefronts) {
81
+ storefronts = await getStorefronts(session);
82
+ }
80
83
  let selectedStorefront;
81
84
  if (flagStorefront) {
82
85
  selectedStorefront = storefronts.find(
@@ -15,7 +15,6 @@ class Setup extends Command {
15
15
  static flags = {
16
16
  ...commonFlags.path,
17
17
  ...commonFlags.force,
18
- ...commonFlags.styling,
19
18
  ...commonFlags.markets,
20
19
  ...commonFlags.shortcut,
21
20
  ...overrideFlag(commonFlags.installDeps, {
@@ -18,4 +18,9 @@ export default defineConfig({
18
18
  }),
19
19
  tsconfigPaths(),
20
20
  ],
21
+ build: {
22
+ // Allow a strict Content-Security-Policy
23
+ // withtout inlining assets as base64:
24
+ assetsInlineLimit: 0,
25
+ },
21
26
  });
@@ -1,5 +1,54 @@
1
1
  # skeleton
2
2
 
3
+ ## 1.0.9
4
+
5
+ ### Patch Changes
6
+
7
+ - Pin React dependency to 18.2.0 to avoid mismatches. ([#2051](https://github.com/Shopify/hydrogen/pull/2051)) by [@frandiox](https://github.com/frandiox)
8
+
9
+ - Updated dependencies [[`9c36c8a5`](https://github.com/Shopify/hydrogen/commit/9c36c8a566b1ae2ceac4846c4c9fe4f63f6f4ab3)]:
10
+ - @shopify/cli-hydrogen@8.0.2
11
+
12
+ ## 1.0.8
13
+
14
+ ### Patch Changes
15
+
16
+ - Stop inlining the favicon in base64 to avoid issues with the Content-Security-Policy. In `vite.config.js`: ([#2006](https://github.com/Shopify/hydrogen/pull/2006)) by [@frandiox](https://github.com/frandiox)
17
+
18
+ ```diff
19
+ export default defineConfig({
20
+ plugins: [
21
+ ...
22
+ ],
23
+ + build: {
24
+ + assetsInlineLimit: 0,
25
+ + },
26
+ });
27
+ ```
28
+
29
+ - To improve HMR in Vite, move the `useRootLoaderData` function from `app/root.tsx` to a separate file like `app/lib/root-data.ts`. This change avoids circular imports: ([#2014](https://github.com/Shopify/hydrogen/pull/2014)) by [@frandiox](https://github.com/frandiox)
30
+
31
+ ```tsx
32
+ // app/lib/root-data.ts
33
+ import {useMatches} from '@remix-run/react';
34
+ import type {SerializeFrom} from '@shopify/remix-oxygen';
35
+ import type {loader} from '~/root';
36
+
37
+ /**
38
+ * Access the result of the root loader from a React component.
39
+ */
40
+ export const useRootLoaderData = () => {
41
+ const [root] = useMatches();
42
+ return root?.data as SerializeFrom<typeof loader>;
43
+ };
44
+ ```
45
+
46
+ Import this hook from `~/lib/root-data` instead of `~/root` in your components.
47
+
48
+ - Updated dependencies [[`b4dfda32`](https://github.com/Shopify/hydrogen/commit/b4dfda320ca52855b2d4493a4306d15a883ca843), [`ffa57bdb`](https://github.com/Shopify/hydrogen/commit/ffa57bdbcdf51e03d565736f9388b5bb4f46292c), [`ac4e1670`](https://github.com/Shopify/hydrogen/commit/ac4e1670f0361a2cd2c6827e4162bbbee0ca37f3), [`0af624d5`](https://github.com/Shopify/hydrogen/commit/0af624d51afc7250db889ba5e736c85a6070c8b2), [`9723eaf3`](https://github.com/Shopify/hydrogen/commit/9723eaf3e5a42c30e657d1cadb123ed775d620e4), [`e842f68c`](https://github.com/Shopify/hydrogen/commit/e842f68c8e879d4c54e0730f3cb55214a760d7f5)]:
49
+ - @shopify/cli-hydrogen@8.0.1
50
+ - @shopify/hydrogen@2024.4.1
51
+
3
52
  ## 1.0.7
4
53
 
5
54
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  import {NavLink} from '@remix-run/react';
2
2
  import type {FooterQuery, HeaderQuery} from 'storefrontapi.generated';
3
- import {useRootLoaderData} from '~/root';
3
+ import {useRootLoaderData} from '~/lib/root-data';
4
4
 
5
5
  export function Footer({
6
6
  menu,
@@ -2,7 +2,7 @@ import {Await, NavLink} from '@remix-run/react';
2
2
  import {Suspense} from 'react';
3
3
  import type {HeaderQuery} from 'storefrontapi.generated';
4
4
  import type {LayoutProps} from './Layout';
5
- import {useRootLoaderData} from '~/root';
5
+ import {useRootLoaderData} from '~/lib/root-data';
6
6
 
7
7
  type HeaderProps = Pick<LayoutProps, 'header' | 'cart' | 'isLoggedIn'>;
8
8
 
@@ -0,0 +1,11 @@
1
+ import {useMatches} from '@remix-run/react';
2
+ import type {SerializeFrom} from '@shopify/remix-oxygen';
3
+ import type {loader} from '~/root';
4
+
5
+ /**
6
+ * Access the result of the root loader from a React component.
7
+ */
8
+ export function useRootLoaderData() {
9
+ const [root] = useMatches();
10
+ return root?.data as SerializeFrom<typeof loader>;
11
+ }
@@ -1,15 +1,10 @@
1
1
  import {useNonce} from '@shopify/hydrogen';
2
- import {
3
- defer,
4
- type SerializeFrom,
5
- type LoaderFunctionArgs,
6
- } from '@shopify/remix-oxygen';
2
+ import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
7
3
  import {
8
4
  Links,
9
5
  Meta,
10
6
  Outlet,
11
7
  Scripts,
12
- useMatches,
13
8
  useRouteError,
14
9
  useLoaderData,
15
10
  ScrollRestoration,
@@ -58,14 +53,6 @@ export function links() {
58
53
  ];
59
54
  }
60
55
 
61
- /**
62
- * Access the result of the root loader from a React component.
63
- */
64
- export const useRootLoaderData = () => {
65
- const [root] = useMatches();
66
- return root?.data as SerializeFrom<typeof loader>;
67
- };
68
-
69
56
  export async function loader({context}: LoaderFunctionArgs) {
70
57
  const {storefront, customerAccount, cart} = context;
71
58
  const publicStoreDomain = context.env.PUBLIC_STORE_DOMAIN;
@@ -130,7 +117,7 @@ export default function App() {
130
117
 
131
118
  export function ErrorBoundary() {
132
119
  const error = useRouteError();
133
- const rootData = useRootLoaderData();
120
+ const rootData = useLoaderData<typeof loader>();
134
121
  const nonce = useNonce();
135
122
  let errorMessage = 'Unknown error';
136
123
  let errorStatus = 500;
@@ -7,11 +7,11 @@ export const meta: MetaFunction<typeof loader> = ({data}) => {
7
7
  return [{title: `Hydrogen | ${data?.blog.title ?? ''} blog`}];
8
8
  };
9
9
 
10
- export const loader = async ({
10
+ export async function loader({
11
11
  request,
12
12
  params,
13
13
  context: {storefront},
14
- }: LoaderFunctionArgs) => {
14
+ }: LoaderFunctionArgs) {
15
15
  const paginationVariables = getPaginationVariables(request, {
16
16
  pageBy: 4,
17
17
  });
@@ -32,7 +32,7 @@ export const loader = async ({
32
32
  }
33
33
 
34
34
  return json({blog});
35
- };
35
+ }
36
36
 
37
37
  export default function Blog() {
38
38
  const {blog} = useLoaderData<typeof loader>();
@@ -4,7 +4,7 @@ import type {CartQueryDataReturn} from '@shopify/hydrogen';
4
4
  import {CartForm} from '@shopify/hydrogen';
5
5
  import {json, type ActionFunctionArgs} from '@shopify/remix-oxygen';
6
6
  import {CartMain} from '~/components/Cart';
7
- import {useRootLoaderData} from '~/root';
7
+ import {useRootLoaderData} from '~/lib/root-data';
8
8
 
9
9
  export const meta: MetaFunction = () => {
10
10
  return [{title: `Hydrogen | Cart`}];
@@ -10,7 +10,7 @@ import type {ProductItemFragment} from 'storefrontapi.generated';
10
10
  import {useVariantUrl} from '~/lib/variants';
11
11
 
12
12
  export const meta: MetaFunction<typeof loader> = () => {
13
- return [{title: `Hydrogen | Procuts`}];
13
+ return [{title: `Hydrogen | Products`}];
14
14
  };
15
15
 
16
16
  export async function loader({request, context}: LoaderFunctionArgs) {
@@ -2,7 +2,7 @@
2
2
  "name": "skeleton",
3
3
  "private": true,
4
4
  "sideEffects": false,
5
- "version": "1.0.7",
5
+ "version": "1.0.9",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "build": "shopify hydrogen build --codegen",
@@ -17,21 +17,21 @@
17
17
  "@remix-run/react": "^2.8.0",
18
18
  "@remix-run/server-runtime": "^2.8.0",
19
19
  "@shopify/cli": "3.58.0",
20
- "@shopify/cli-hydrogen": "^8.0.0",
21
- "@shopify/hydrogen": "2024.4.0",
20
+ "@shopify/cli-hydrogen": "^8.0.2",
21
+ "@shopify/hydrogen": "2024.4.1",
22
22
  "@shopify/remix-oxygen": "^2.0.4",
23
23
  "graphql": "^16.6.0",
24
24
  "graphql-tag": "^2.12.6",
25
25
  "isbot": "^3.8.0",
26
- "react": "^18.2.0",
27
- "react-dom": "^18.2.0"
26
+ "react": "18.2.0",
27
+ "react-dom": "18.2.0"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@graphql-codegen/cli": "5.0.2",
31
31
  "@remix-run/dev": "^2.8.0",
32
32
  "@remix-run/eslint-config": "^2.8.0",
33
33
  "@shopify/hydrogen-codegen": "^0.3.0",
34
- "@shopify/mini-oxygen": "^3.0.0",
34
+ "@shopify/mini-oxygen": "^3.0.1",
35
35
  "@shopify/oxygen-workers-types": "^4.0.0",
36
36
  "@shopify/prettier-config": "^1.1.2",
37
37
  "@total-typescript/ts-reset": "^0.4.2",
@@ -18,4 +18,9 @@ export default defineConfig({
18
18
  }),
19
19
  tsconfigPaths(),
20
20
  ],
21
+ build: {
22
+ // Allow a strict Content-Security-Policy
23
+ // withtout inlining assets as base64:
24
+ assetsInlineLimit: 0,
25
+ },
21
26
  });
package/dist/init.d.ts CHANGED
@@ -45,7 +45,6 @@ declare class Init extends Command {
45
45
  'package-manager': _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
46
46
  shortcut: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
47
47
  markets: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
48
- styling: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
49
48
  'mock-shop': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
50
49
  'install-deps': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
51
50
  path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -9,7 +9,9 @@ import { getConfig } from './shopify-config.js';
9
9
  import { getGraphiQLUrl } from './graphiql-url.js';
10
10
 
11
11
  function isMockShop(envVariables) {
12
- return envVariables.PUBLIC_STORE_DOMAIN && envVariables.PUBLIC_STORE_DOMAIN.includes("mock.shop");
12
+ return envVariables.PUBLIC_STORE_DOMAIN === "mock.shop" || // We fallback to mock.shop if the env var is falsy.
13
+ // When it's undefined, it might be overwritten by remote variables.
14
+ envVariables.PUBLIC_STORE_DOMAIN === "";
13
15
  }
14
16
  function notifyIssueWithTunnelAndMockShop(cliCommand) {
15
17
  renderInfo({
package/dist/lib/flags.js CHANGED
@@ -3,7 +3,6 @@ import { camelize } from '@shopify/cli-kit/common/string';
3
3
  import { renderInfo } from '@shopify/cli-kit/node/ui';
4
4
  import { normalizeStoreFqdn } from '@shopify/cli-kit/node/context/fqdn';
5
5
  import colors from '@shopify/cli-kit/node/colors';
6
- import { STYLING_CHOICES } from './setups/css/index.js';
7
6
  import { I18N_CHOICES } from './setups/i18n/index.js';
8
7
 
9
8
  const DEFAULT_APP_PORT = 3e3;
@@ -88,15 +87,6 @@ const commonFlags = {
88
87
  dependsOn: ["codegen"]
89
88
  })
90
89
  },
91
- styling: {
92
- styling: Flags.string({
93
- description: `Sets the styling strategy to use. One of ${STYLING_CHOICES.map(
94
- (item) => `\`${item}\``
95
- ).join(", ")}.`,
96
- choices: STYLING_CHOICES,
97
- env: "SHOPIFY_HYDROGEN_FLAG_STYLING"
98
- })
99
- },
100
90
  markets: {
101
91
  markets: Flags.string({
102
92
  description: `Sets the URL structure to support multiple markets. Must be one of: ${I18N_CHOICES.map(
@@ -1,10 +1,9 @@
1
- import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
2
1
  import { outputWarn } from '@shopify/cli-kit/node/output';
3
- import { linkStorefront } from '../commands/hydrogen/link.js';
4
2
  import { login } from './auth.js';
5
3
  import { getCliCommand } from './shell.js';
6
- import { renderMissingLink, renderMissingStorefront } from './render-errors.js';
4
+ import { renderMissingStorefront } from './render-errors.js';
7
5
  import { getOxygenData } from './graphql/admin/get-oxygen-data.js';
6
+ import { verifyLinkedStorefront } from './verify-linked-storefront.js';
8
7
 
9
8
  async function getOxygenDeploymentData({
10
9
  root
@@ -13,21 +12,15 @@ async function getOxygenDeploymentData({
13
12
  login(root),
14
13
  getCliCommand()
15
14
  ]);
16
- if (!config.storefront?.id) {
17
- renderMissingLink({ session, cliCommand });
18
- const runLink = await renderConfirmationPrompt({
19
- message: ["Run", { command: `${cliCommand} link` }]
20
- });
21
- if (!runLink) {
22
- return;
23
- }
24
- config.storefront = await linkStorefront(root, session, config, {
25
- cliCommand
26
- });
27
- }
28
- if (!config.storefront) {
15
+ const linkedStorefront = await verifyLinkedStorefront({
16
+ root,
17
+ session,
18
+ config,
19
+ cliCommand
20
+ });
21
+ if (!linkedStorefront)
29
22
  return;
30
- }
23
+ config.storefront = linkedStorefront;
31
24
  const { storefront } = await getOxygenData(session, config.storefront.id);
32
25
  if (!storefront) {
33
26
  renderMissingStorefront({
@@ -141,6 +141,9 @@ async function handleStorefrontSelection(storefronts) {
141
141
  value: id
142
142
  }))
143
143
  ];
144
+ if (choices.length === 1) {
145
+ return;
146
+ }
144
147
  const storefrontId = await renderSelectPrompt({
145
148
  message: "Select a Hydrogen storefront to link",
146
149
  choices
@@ -1,4 +1,4 @@
1
- import { renderFatalError } from '@shopify/cli-kit/node/ui';
1
+ import { renderFatalError, renderInfo } from '@shopify/cli-kit/node/ui';
2
2
  import { outputContent, outputToken } from '@shopify/cli-kit/node/output';
3
3
  import { hydrogenStorefrontsUrl } from './admin-urls.js';
4
4
  import { parseGid } from './gid.js';
@@ -25,15 +25,22 @@ function renderMissingStorefront({
25
25
  )}`.value
26
26
  });
27
27
  }
28
- function renderMissingLink({ session, cliCommand }) {
29
- renderFatalError({
30
- name: "NoLinkedStorefrontError",
31
- type: 0,
32
- message: `No linked Hydrogen storefront on ${session.storeFqdn}`,
33
- skipOclifErrorHandling: true,
34
- tryMessage: [
35
- "To pull environment variables or to deploy to Oxygen, link this project to a Hydrogen storefront. To select a storefront to link, run",
36
- { command: `${cliCommand} link` }
28
+ function renderMissingLink({ noStorefronts = false }) {
29
+ const headline = noStorefronts ? "You don't have a Hydrogen storefront to link to" : "You haven't linked your project to a storefront yet";
30
+ renderInfo({
31
+ headline,
32
+ body: [
33
+ "Link your local environment to a Hydrogen storefront. Enable automatic environment variable injection and access to",
34
+ { command: "env list" },
35
+ ",",
36
+ { command: "env pull" },
37
+ ",",
38
+ { command: "env push" },
39
+ ", and",
40
+ { command: "deploy" },
41
+ "commands. Use",
42
+ { command: `unlink` },
43
+ "to disconnect from the storefront."
37
44
  ]
38
45
  });
39
46
  }
@@ -6,18 +6,36 @@ function generateFunctionDocumentation(functionNode, variableStatement) {
6
6
  variableStatement
7
7
  );
8
8
  generateParameterDocumentation(functionNode, outputDocNode);
9
- const jsDocs = variableStatement?.getJsDocs()[0];
10
- if (jsDocs?.getTags().length === 0) {
9
+ if (variableStatement) {
11
10
  const declaration = variableStatement?.getDeclarations()[0];
12
- const type = declaration?.getType().getText();
13
- if (type && type !== "any" && type !== "unknown") {
14
- const tagName = type.startsWith("() =>") ? "return" : "type";
15
- const normalizedType = type.replace(/^\(\)\s+=>\s+/, "").replace(/typeof import\("[^"]+"\)\./, "typeof ");
16
- const text = normalizedType === "SerializeFrom<typeof loader>" ? `{LoaderReturnData}` : `{${normalizedType}}`;
17
- jsDocs.addTag({ tagName, text });
11
+ const jsDocs = variableStatement?.getJsDocs()[0];
12
+ if (jsDocs?.getTags().length === 0) {
13
+ const type = declaration?.getType().getText();
14
+ if (isAnnotableType(type)) {
15
+ jsDocs.addTag({
16
+ tagName: type.startsWith("() =>") ? "return" : "type",
17
+ text: normalizeType(type)
18
+ });
19
+ }
20
+ }
21
+ } else {
22
+ const declaration = functionNode;
23
+ const jsDocs = declaration.getJsDocs()[0];
24
+ if (jsDocs?.getTags().length === 0) {
25
+ const returnType = declaration.getReturnType().getText();
26
+ if (isAnnotableType(returnType)) {
27
+ jsDocs.addTag({ tagName: "return", text: normalizeType(returnType) });
28
+ }
18
29
  }
19
30
  }
20
31
  }
32
+ function isAnnotableType(type) {
33
+ return !!(type && type !== "any" && type !== "unknown" && type !== "{}" && type !== "boolean");
34
+ }
35
+ function normalizeType(type) {
36
+ const normalizedType = type.replace(/^\(\)\s+=>\s+/, "").replace(/typeof import\("[^"]+"\)\./, "typeof ");
37
+ return normalizedType === "SerializeFrom<any>" ? `{SerializeFrom<loader>}` : `{${normalizedType}}`;
38
+ }
21
39
  function generateParameterDocumentation(functionNode, docNode) {
22
40
  const params = functionNode.getParameters();
23
41
  const jsDoc = getJsDocOrCreate(docNode);
@@ -27,12 +27,11 @@ function generateTypeDefs(sourceFile, code) {
27
27
  }
28
28
  typedefs.push("");
29
29
  const knownGenerics = {
30
- MetaFunction: "T"
30
+ MetaFunction: "T",
31
+ SerializeFrom: "T"
31
32
  };
32
33
  typedefsFromImports.forEach((typeElements, moduleSpecifier) => {
33
34
  for (const typeElement of typeElements) {
34
- if (typeElement === "SerializeFrom")
35
- continue;
36
35
  const hasGeneric = !!knownGenerics[typeElement];
37
36
  typedefs.push(
38
37
  `/** ${hasGeneric ? `@template ${knownGenerics[typeElement]} ` : ""}@typedef {import('${moduleSpecifier}').${typeElement}${hasGeneric ? `<${knownGenerics[typeElement]}>` : ""}} ${typeElement} */`
@@ -0,0 +1,24 @@
1
+ import { getStorefronts } from './graphql/admin/link-storefront.js';
2
+ import { linkStorefront } from '../commands/hydrogen/link.js';
3
+ import { renderMissingLink } from './render-errors.js';
4
+
5
+ async function verifyLinkedStorefront({
6
+ root,
7
+ session,
8
+ config,
9
+ cliCommand
10
+ }) {
11
+ const storefronts = await getStorefronts(session);
12
+ let configuredStorefront = config.storefront?.id ? storefronts.find(({ id }) => id === config.storefront.id) : void 0;
13
+ if (configuredStorefront) {
14
+ return configuredStorefront;
15
+ }
16
+ renderMissingLink({ noStorefronts: !storefronts.length });
17
+ return await linkStorefront(root, session, config, {
18
+ force: true,
19
+ cliCommand,
20
+ storefronts
21
+ });
22
+ }
23
+
24
+ export { verifyLinkedStorefront };
@@ -1195,14 +1195,6 @@
1195
1195
  "allowNo": false,
1196
1196
  "type": "boolean"
1197
1197
  },
1198
- "styling": {
1199
- "description": "Sets the styling strategy to use. One of `tailwind`, `css-modules`, `vanilla-extract`, `postcss`, `none`.",
1200
- "env": "SHOPIFY_HYDROGEN_FLAG_STYLING",
1201
- "name": "styling",
1202
- "hasDynamicHelp": false,
1203
- "multiple": false,
1204
- "type": "option"
1205
- },
1206
1198
  "markets": {
1207
1199
  "description": "Sets the URL structure to support multiple markets. Must be one of: `subfolders`, `domains`, `subdomains`, `none`. Example: `--markets subfolders`.",
1208
1200
  "env": "SHOPIFY_HYDROGEN_FLAG_I18N",
@@ -1221,7 +1213,6 @@
1221
1213
  "routes": {
1222
1214
  "description": "Generate routes for all pages.",
1223
1215
  "env": "SHOPIFY_HYDROGEN_FLAG_ROUTES",
1224
- "hidden": true,
1225
1216
  "name": "routes",
1226
1217
  "allowNo": true,
1227
1218
  "type": "boolean"
@@ -1234,7 +1225,7 @@
1234
1225
  "type": "boolean"
1235
1226
  },
1236
1227
  "quickstart": {
1237
- "description": "Scaffolds a new Hydrogen project with a set of sensible defaults.",
1228
+ "description": "Scaffolds a new Hydrogen project with a set of sensible defaults. Equivalent to `shopify hydrogen init --path hydrogen-quickstart --mock-shop --language js --shortcut --routes --markets none`",
1238
1229
  "env": "SHOPIFY_HYDROGEN_FLAG_QUICKSTART",
1239
1230
  "name": "quickstart",
1240
1231
  "allowNo": false,
@@ -1532,14 +1523,6 @@
1532
1523
  "allowNo": false,
1533
1524
  "type": "boolean"
1534
1525
  },
1535
- "styling": {
1536
- "description": "Sets the styling strategy to use. One of `tailwind`, `css-modules`, `vanilla-extract`, `postcss`, `none`.",
1537
- "env": "SHOPIFY_HYDROGEN_FLAG_STYLING",
1538
- "name": "styling",
1539
- "hasDynamicHelp": false,
1540
- "multiple": false,
1541
- "type": "option"
1542
- },
1543
1526
  "markets": {
1544
1527
  "description": "Sets the URL structure to support multiple markets. Must be one of: `subfolders`, `domains`, `subdomains`, `none`. Example: `--markets subfolders`.",
1545
1528
  "env": "SHOPIFY_HYDROGEN_FLAG_I18N",
@@ -1805,5 +1788,5 @@
1805
1788
  ]
1806
1789
  }
1807
1790
  },
1808
- "version": "8.0.0"
1791
+ "version": "8.0.2"
1809
1792
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public",
5
5
  "@shopify:registry": "https://registry.npmjs.org"
6
6
  },
7
- "version": "8.0.0",
7
+ "version": "8.0.2",
8
8
  "license": "MIT",
9
9
  "type": "module",
10
10
  "scripts": {
@@ -60,7 +60,7 @@
60
60
  "@graphql-codegen/cli": "^5.0.2",
61
61
  "@remix-run/dev": "^2.1.0",
62
62
  "@shopify/hydrogen-codegen": "^0.3.0",
63
- "@shopify/mini-oxygen": "^3.0.0",
63
+ "@shopify/mini-oxygen": "^3.0.1",
64
64
  "graphql-config": "^5.0.3",
65
65
  "vite": "^5.1.0"
66
66
  },