@shopify/cli-hydrogen 5.5.1 → 6.0.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 (62) hide show
  1. package/dist/commands/hydrogen/build.js +2 -6
  2. package/dist/commands/hydrogen/{codegen-unstable.js → codegen.js} +2 -0
  3. package/dist/commands/hydrogen/deploy.js +1 -0
  4. package/dist/commands/hydrogen/deploy.test.js +1 -0
  5. package/dist/commands/hydrogen/dev.js +6 -8
  6. package/dist/commands/hydrogen/generate/route.js +5 -1
  7. package/dist/commands/hydrogen/generate/route.test.js +4 -2
  8. package/dist/commands/hydrogen/init.test.js +1 -4
  9. package/dist/commands/hydrogen/setup.js +1 -6
  10. package/dist/generator-templates/starter/CHANGELOG.md +143 -0
  11. package/dist/generator-templates/starter/app/components/Footer.tsx +19 -8
  12. package/dist/generator-templates/starter/app/components/Header.tsx +13 -5
  13. package/dist/generator-templates/starter/app/components/Layout.tsx +14 -4
  14. package/dist/generator-templates/starter/app/root.tsx +14 -27
  15. package/dist/generator-templates/starter/app/routes/$.tsx +2 -2
  16. package/dist/generator-templates/starter/app/routes/[robots.txt].tsx +2 -2
  17. package/dist/generator-templates/starter/app/routes/[sitemap.xml].tsx +5 -2
  18. package/dist/generator-templates/starter/app/routes/_index.tsx +4 -9
  19. package/dist/generator-templates/starter/app/routes/account.$.tsx +2 -2
  20. package/dist/generator-templates/starter/app/routes/account.addresses.tsx +6 -6
  21. package/dist/generator-templates/starter/app/routes/account.orders.$id.tsx +4 -4
  22. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +4 -4
  23. package/dist/generator-templates/starter/app/routes/account.profile.tsx +6 -6
  24. package/dist/generator-templates/starter/app/routes/account.tsx +2 -2
  25. package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +6 -6
  26. package/dist/generator-templates/starter/app/routes/account_.login.tsx +6 -11
  27. package/dist/generator-templates/starter/app/routes/account_.logout.tsx +4 -4
  28. package/dist/generator-templates/starter/app/routes/account_.recover.tsx +4 -4
  29. package/dist/generator-templates/starter/app/routes/account_.register.tsx +4 -4
  30. package/dist/generator-templates/starter/app/routes/account_.reset.$id.$resetToken.tsx +4 -4
  31. package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +4 -4
  32. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +5 -5
  33. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +5 -5
  34. package/dist/generator-templates/starter/app/routes/blogs._index.tsx +7 -4
  35. package/dist/generator-templates/starter/app/routes/cart.$lines.tsx +2 -2
  36. package/dist/generator-templates/starter/app/routes/cart.tsx +11 -8
  37. package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +5 -5
  38. package/dist/generator-templates/starter/app/routes/collections._index.tsx +2 -2
  39. package/dist/generator-templates/starter/app/routes/discount.$code.tsx +2 -2
  40. package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +5 -5
  41. package/dist/generator-templates/starter/app/routes/policies.$handle.tsx +5 -5
  42. package/dist/generator-templates/starter/app/routes/policies._index.tsx +2 -2
  43. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +7 -7
  44. package/dist/generator-templates/starter/app/routes/search.tsx +5 -4
  45. package/dist/generator-templates/starter/app/styles/app.css +16 -16
  46. package/dist/generator-templates/starter/package.json +10 -10
  47. package/dist/generator-templates/starter/remix.config.js +0 -8
  48. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +79 -55
  49. package/dist/lib/auth.js +3 -0
  50. package/dist/lib/codegen.js +31 -22
  51. package/dist/lib/flags.js +8 -1
  52. package/dist/lib/onboarding/common.js +2 -14
  53. package/dist/lib/remix-version-check.js +2 -1
  54. package/dist/lib/remix-version-interop.js +1 -39
  55. package/dist/lib/remix-version-interop.test.js +8 -98
  56. package/dist/lib/setups/css/postcss.js +1 -6
  57. package/dist/lib/setups/css/tailwind.js +6 -12
  58. package/dist/lib/setups/routes/generate.js +13 -23
  59. package/dist/lib/setups/routes/generate.test.js +6 -20
  60. package/dist/lib/transpile/morph/typedefs.js +1 -1
  61. package/oclif.manifest.json +29 -16
  62. package/package.json +13 -9
@@ -42,11 +42,7 @@ class Build extends Command {
42
42
  description: "Disable warning about missing standard routes.",
43
43
  env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_ROUTE_WARNING"
44
44
  }),
45
- "codegen-unstable": Flags.boolean({
46
- description: "Generate types for the Storefront API queries found in your project.",
47
- required: false,
48
- default: false
49
- }),
45
+ codegen: commonFlags.codegen,
50
46
  "codegen-config-path": commonFlags.codegenConfigPath,
51
47
  base: deprecated("--base")(),
52
48
  entry: deprecated("--entry")(),
@@ -57,7 +53,7 @@ class Build extends Command {
57
53
  const directory = flags.path ? resolvePath(flags.path) : process.cwd();
58
54
  await runBuild({
59
55
  ...flagsToCamelObject(flags),
60
- useCodegen: flags["codegen-unstable"],
56
+ useCodegen: flags.codegen,
61
57
  directory
62
58
  });
63
59
  }
@@ -24,6 +24,8 @@ class Codegen extends Command {
24
24
  default: false
25
25
  })
26
26
  };
27
+ static aliases = ["codegen-unstable"];
28
+ static deprecateAliases = true;
27
29
  async run() {
28
30
  const { flags } = await this.parse(Codegen);
29
31
  const directory = flags.path ? path.resolve(flags.path) : process.cwd();
@@ -136,6 +136,7 @@ async function oxygenDeploy(options) {
136
136
  }
137
137
  const config = {
138
138
  assetsDir: "dist/client",
139
+ bugsnag: true,
139
140
  deploymentUrl: "https://oxygen.shopifyapps.com",
140
141
  deploymentToken: parseToken(token),
141
142
  environmentTag: environmentTag || deploymentEnvironmentTag || branch,
@@ -81,6 +81,7 @@ describe("deploy", () => {
81
81
  };
82
82
  const expectedConfig = {
83
83
  assetsDir: "dist/client",
84
+ bugsnag: true,
84
85
  deploymentUrl: "https://oxygen.shopifyapps.com",
85
86
  deploymentToken: mockToken,
86
87
  verificationMaxDuration: 180,
@@ -7,7 +7,7 @@ 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, deprecated, flagsToCamelObject, DEFAULT_PORT } from '../../lib/flags.js';
10
+ import { commonFlags, overrideFlag, deprecated, flagsToCamelObject, DEFAULT_PORT } from '../../lib/flags.js';
11
11
  import Command from '@shopify/cli-kit/node/base-command';
12
12
  import { Flags } from '@oclif/core';
13
13
  import { startMiniOxygen } from '../../lib/mini-oxygen/index.js';
@@ -28,12 +28,10 @@ class Dev extends Command {
28
28
  path: commonFlags.path,
29
29
  port: commonFlags.port,
30
30
  ["worker-unstable"]: commonFlags.workerRuntime,
31
- ["codegen-unstable"]: Flags.boolean({
32
- description: "Generate types for the Storefront API queries found in your project. It updates the types on file save.",
33
- required: false,
34
- default: false
31
+ codegen: overrideFlag(commonFlags.codegen, {
32
+ description: commonFlags.codegen.description + " It updates the types on file save."
35
33
  }),
36
- ["codegen-config-path"]: commonFlags.codegenConfigPath,
34
+ "codegen-config-path": commonFlags.codegenConfigPath,
37
35
  sourcemap: commonFlags.sourcemap,
38
36
  "disable-virtual-routes": Flags.boolean({
39
37
  description: "Disable rendering fallback routes when a route file doesn't exist.",
@@ -53,7 +51,7 @@ class Dev extends Command {
53
51
  const directory = flags.path ? path.resolve(flags.path) : process.cwd();
54
52
  await runDev({
55
53
  ...flagsToCamelObject(flags),
56
- useCodegen: flags["codegen-unstable"],
54
+ useCodegen: flags.codegen,
57
55
  workerRuntime: flags["worker-unstable"],
58
56
  path: directory
59
57
  });
@@ -106,7 +104,7 @@ async function runDev({
106
104
  let isInitialBuild = true;
107
105
  let initialBuildDurationMs = 0;
108
106
  let initialBuildStartTimeMs = Date.now();
109
- const liveReload = remixConfig.future.v2_dev ? await setupLiveReload(remixConfig.devServerPort) : void 0;
107
+ const liveReload = await setupLiveReload(remixConfig.dev?.port ?? 8002) ;
110
108
  let miniOxygen;
111
109
  async function safeStartMiniOxygen() {
112
110
  if (miniOxygen)
@@ -5,6 +5,7 @@ import colors from '@shopify/cli-kit/node/colors';
5
5
  import { commonFlags } from '../../../lib/flags.js';
6
6
  import { Flags, Args } from '@oclif/core';
7
7
  import { ALL_ROUTE_CHOICES, generateRoutes } from '../../../lib/setups/routes/generate.js';
8
+ import { isV1RouteConventionInstalled } from '../../../lib/remix-version-interop.js';
8
9
 
9
10
  class GenerateRoute extends Command {
10
11
  static description = "Generates a standard Shopify route.";
@@ -44,7 +45,10 @@ class GenerateRoute extends Command {
44
45
  }
45
46
  }
46
47
  async function runGenerate(options) {
47
- const { routes } = await generateRoutes(options);
48
+ const { routes } = await generateRoutes({
49
+ ...options,
50
+ v1RouteConvention: isV1RouteConventionInstalled()
51
+ });
48
52
  const padEnd = 3 + routes.reduce(
49
53
  (acc, route) => Math.max(acc, route.destinationRoute.length),
50
54
  0
@@ -17,7 +17,6 @@ describe("runGenerate", () => {
17
17
  vi.mocked(generateRoutes).mockResolvedValue({
18
18
  isTypescript: true,
19
19
  formatOptions: {},
20
- v2Flags: {},
21
20
  routeGroups: {},
22
21
  routes: [
23
22
  { sourceRoute: "", destinationRoute: "/cart", operation: "created" },
@@ -35,7 +34,10 @@ describe("runGenerate", () => {
35
34
  typescript: true
36
35
  };
37
36
  await runGenerate(options);
38
- expect(generateRoutes).toHaveBeenCalledWith(options);
37
+ expect(generateRoutes).toHaveBeenCalledWith({
38
+ ...options,
39
+ v1RouteConvention: false
40
+ });
39
41
  expect(outputMock.info()).toMatch(/2 of 3 routes/i);
40
42
  });
41
43
  });
@@ -12,7 +12,7 @@ import { getSkeletonSourceDir } from '../../lib/build.js';
12
12
  import { execAsync } from '../../lib/process.js';
13
13
  import { rmdir, symlink } from 'fs-extra';
14
14
  import { runCheckRoutes } from './check.js';
15
- import { runCodegen } from './codegen-unstable.js';
15
+ import { runCodegen } from './codegen.js';
16
16
  import { runBuild } from './build.js';
17
17
 
18
18
  const { renderTasksHook } = vi.hoisted(() => ({ renderTasksHook: vi.fn() }));
@@ -287,9 +287,6 @@ describe("init", () => {
287
287
  language: "ts",
288
288
  styling: "tailwind"
289
289
  });
290
- await expect(readFile(`${tmpDir}/remix.config.js`)).resolves.toMatch(
291
- /tailwind: true,\n\s*postcss: true,\n\s*future:/
292
- );
293
290
  await expect(
294
291
  readFile(`${tmpDir}/app/styles/tailwind.css`)
295
292
  ).resolves.toMatch(/@tailwind base;/);
@@ -77,12 +77,7 @@ async function runSetup(options) {
77
77
  generateProjectEntries({
78
78
  rootDirectory: remixConfig.rootDirectory,
79
79
  appDirectory: remixConfig.appDirectory,
80
- typescript,
81
- v2Flags: {
82
- isV2RouteConvention: remixConfig.future?.v2_routeConvention,
83
- isV2ErrorBoundary: remixConfig.future?.v2_errorBoundary,
84
- isV2Meta: remixConfig.future?.v2_meta
85
- }
80
+ typescript
86
81
  })
87
82
  ])
88
83
  ).then(async () => {
@@ -0,0 +1,143 @@
1
+ # skeleton
2
+
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - The Storefront API 2023-10 now returns menu item URLs that include the `primaryDomainUrl`, instead of defaulting to the Shopify store ID URL (example.myshopify.com). The skeleton template requires changes to check for the `primaryDomainUrl`: by [@blittle](https://github.com/blittle)
8
+
9
+ 1. Update the `HeaderMenu` component to accept a `primaryDomainUrl` and include
10
+ it in the internal url check
11
+
12
+ ```diff
13
+ // app/components/Header.tsx
14
+
15
+ + import type {HeaderQuery} from 'storefrontapi.generated';
16
+
17
+ export function HeaderMenu({
18
+ menu,
19
+ + primaryDomainUrl,
20
+ viewport,
21
+ }: {
22
+ menu: HeaderProps['header']['menu'];
23
+ + primaryDomainUrl: HeaderQuery['shop']['primaryDomain']['url'];
24
+ viewport: Viewport;
25
+ }) {
26
+
27
+ // ...code
28
+
29
+ // if the url is internal, we strip the domain
30
+ const url =
31
+ item.url.includes('myshopify.com') ||
32
+ item.url.includes(publicStoreDomain) ||
33
+ + item.url.includes(primaryDomainUrl)
34
+ ? new URL(item.url).pathname
35
+ : item.url;
36
+
37
+ // ...code
38
+
39
+ }
40
+ ```
41
+
42
+ 2. Update the `FooterMenu` component to accept a `primaryDomainUrl` prop and include
43
+ it in the internal url check
44
+
45
+ ```diff
46
+ // app/components/Footer.tsx
47
+
48
+ - import type {FooterQuery} from 'storefrontapi.generated';
49
+ + import type {FooterQuery, HeaderQuery} from 'storefrontapi.generated';
50
+
51
+ function FooterMenu({
52
+ menu,
53
+ + primaryDomainUrl,
54
+ }: {
55
+ menu: FooterQuery['menu'];
56
+ + primaryDomainUrl: HeaderQuery['shop']['primaryDomain']['url'];
57
+ }) {
58
+ // code...
59
+
60
+ // if the url is internal, we strip the domain
61
+ const url =
62
+ item.url.includes('myshopify.com') ||
63
+ item.url.includes(publicStoreDomain) ||
64
+ + item.url.includes(primaryDomainUrl)
65
+ ? new URL(item.url).pathname
66
+ : item.url;
67
+
68
+ // ...code
69
+
70
+ );
71
+ }
72
+ ```
73
+
74
+ 3. Update the `Footer` component to accept a `shop` prop
75
+
76
+ ```diff
77
+ export function Footer({
78
+ menu,
79
+ + shop,
80
+ }: FooterQuery & {shop: HeaderQuery['shop']}) {
81
+ return (
82
+ <footer className="footer">
83
+ - <FooterMenu menu={menu} />
84
+ + <FooterMenu menu={menu} primaryDomainUrl={shop.primaryDomain.url} />
85
+ </footer>
86
+ );
87
+ }
88
+ ```
89
+
90
+ 4. Update `Layout.tsx` to pass the `shop` prop
91
+
92
+ ```diff
93
+ export function Layout({
94
+ cart,
95
+ children = null,
96
+ footer,
97
+ header,
98
+ isLoggedIn,
99
+ }: LayoutProps) {
100
+ return (
101
+ <>
102
+ <CartAside cart={cart} />
103
+ <SearchAside />
104
+ <MobileMenuAside menu={header.menu} shop={header.shop} />
105
+ <Header header={header} cart={cart} isLoggedIn={isLoggedIn} />
106
+ <main>{children}</main>
107
+ <Suspense>
108
+ <Await resolve={footer}>
109
+ - {(footer) => <Footer menu={footer.menu} />}
110
+ + {(footer) => <Footer menu={footer.menu} shop={header.shop} />}
111
+ </Await>
112
+ </Suspense>
113
+ </>
114
+ );
115
+ }
116
+ ```
117
+
118
+ ### Patch Changes
119
+
120
+ - If you are calling `useMatches()` in different places of your app to access the data returned by the root loader, you may want to update it to the following pattern to enhance types: ([#1289](https://github.com/Shopify/hydrogen/pull/1289)) by [@frandiox](https://github.com/frandiox)
121
+
122
+ ```ts
123
+ // root.tsx
124
+
125
+ import {useMatches} from '@remix-run/react';
126
+ import {type SerializeFrom} from '@shopify/remix-oxygen';
127
+
128
+ export const useRootLoaderData = () => {
129
+ const [root] = useMatches();
130
+ return root?.data as SerializeFrom<typeof loader>;
131
+ };
132
+
133
+ export function loader(context) {
134
+ // ...
135
+ }
136
+ ```
137
+
138
+ This way, you can import `useRootLoaderData()` anywhere in your app and get the correct type for the data returned by the root loader.
139
+
140
+ - Updated dependencies [[`81400439`](https://github.com/Shopify/hydrogen/commit/814004397c1d17ef0a53a425ed28a42cf67765cf), [`a6f397b6`](https://github.com/Shopify/hydrogen/commit/a6f397b64dc6a0d856cb7961731ee1f86bf80292), [`3464ec04`](https://github.com/Shopify/hydrogen/commit/3464ec04a084e1ceb30ee19874dc1b9171ce2b34), [`7fc088e2`](https://github.com/Shopify/hydrogen/commit/7fc088e21bea47840788cb7c60f873ce1f253128), [`867e0b03`](https://github.com/Shopify/hydrogen/commit/867e0b033fc9eb04b7250baea97d8fd49d26ccca), [`ad45656c`](https://github.com/Shopify/hydrogen/commit/ad45656c5f663cc1a60eab5daab4da1dfd0e6cc3), [`f24e3424`](https://github.com/Shopify/hydrogen/commit/f24e3424c8e2b363b181b71fcbd3e45f696fdd3f), [`66a48573`](https://github.com/Shopify/hydrogen/commit/66a4857387148b6a104df5783314c74aca8aada0), [`0ae7cbe2`](https://github.com/Shopify/hydrogen/commit/0ae7cbe280d8351126e11dc13f35d7277d9b2d86), [`8198c1be`](https://github.com/Shopify/hydrogen/commit/8198c1befdfafb39fbcc88d71f91d21eae252973), [`ad45656c`](https://github.com/Shopify/hydrogen/commit/ad45656c5f663cc1a60eab5daab4da1dfd0e6cc3)]:
141
+ - @shopify/hydrogen@2023.10.0
142
+ - @shopify/remix-oxygen@2.0.0
143
+ - @shopify/cli-hydrogen@6.0.0
@@ -1,17 +1,27 @@
1
- import {useMatches, NavLink} from '@remix-run/react';
2
- import type {FooterQuery} from 'storefrontapi.generated';
1
+ import {NavLink} from '@remix-run/react';
2
+ import type {FooterQuery, HeaderQuery} from 'storefrontapi.generated';
3
+ import {useRootLoaderData} from '~/root';
3
4
 
4
- export function Footer({menu}: FooterQuery) {
5
+ export function Footer({
6
+ menu,
7
+ shop,
8
+ }: FooterQuery & {shop: HeaderQuery['shop']}) {
5
9
  return (
6
10
  <footer className="footer">
7
- <FooterMenu menu={menu} />
11
+ <FooterMenu menu={menu} primaryDomainUrl={shop.primaryDomain.url} />
8
12
  </footer>
9
13
  );
10
14
  }
11
15
 
12
- function FooterMenu({menu}: Pick<FooterQuery, 'menu'>) {
13
- const [root] = useMatches();
14
- const publicStoreDomain = root?.data?.publicStoreDomain;
16
+ function FooterMenu({
17
+ menu,
18
+ primaryDomainUrl,
19
+ }: {
20
+ menu: FooterQuery['menu'];
21
+ primaryDomainUrl: HeaderQuery['shop']['primaryDomain']['url'];
22
+ }) {
23
+ const {publicStoreDomain} = useRootLoaderData();
24
+
15
25
  return (
16
26
  <nav className="footer-menu" role="navigation">
17
27
  {(menu || FALLBACK_FOOTER_MENU).items.map((item) => {
@@ -19,7 +29,8 @@ function FooterMenu({menu}: Pick<FooterQuery, 'menu'>) {
19
29
  // if the url is internal, we strip the domain
20
30
  const url =
21
31
  item.url.includes('myshopify.com') ||
22
- item.url.includes(publicStoreDomain)
32
+ item.url.includes(publicStoreDomain) ||
33
+ item.url.includes(primaryDomainUrl)
23
34
  ? new URL(item.url).pathname
24
35
  : item.url;
25
36
  const isExternal = !url.startsWith('/');
@@ -1,6 +1,8 @@
1
- import {Await, NavLink, useMatches} from '@remix-run/react';
1
+ import {Await, NavLink} from '@remix-run/react';
2
2
  import {Suspense} from 'react';
3
+ import type {HeaderQuery} from 'storefrontapi.generated';
3
4
  import type {LayoutProps} from './Layout';
5
+ import {useRootLoaderData} from '~/root';
4
6
 
5
7
  type HeaderProps = Pick<LayoutProps, 'header' | 'cart' | 'isLoggedIn'>;
6
8
 
@@ -13,7 +15,11 @@ export function Header({header, isLoggedIn, cart}: HeaderProps) {
13
15
  <NavLink prefetch="intent" to="/" style={activeLinkStyle} end>
14
16
  <strong>{shop.name}</strong>
15
17
  </NavLink>
16
- <HeaderMenu menu={menu} viewport="desktop" />
18
+ <HeaderMenu
19
+ menu={menu}
20
+ viewport="desktop"
21
+ primaryDomainUrl={header.shop.primaryDomain.url}
22
+ />
17
23
  <HeaderCtas isLoggedIn={isLoggedIn} cart={cart} />
18
24
  </header>
19
25
  );
@@ -21,13 +27,14 @@ export function Header({header, isLoggedIn, cart}: HeaderProps) {
21
27
 
22
28
  export function HeaderMenu({
23
29
  menu,
30
+ primaryDomainUrl,
24
31
  viewport,
25
32
  }: {
26
33
  menu: HeaderProps['header']['menu'];
34
+ primaryDomainUrl: HeaderQuery['shop']['primaryDomain']['url'];
27
35
  viewport: Viewport;
28
36
  }) {
29
- const [root] = useMatches();
30
- const publicStoreDomain = root?.data?.publicStoreDomain;
37
+ const {publicStoreDomain} = useRootLoaderData();
31
38
  const className = `header-menu-${viewport}`;
32
39
 
33
40
  function closeAside(event: React.MouseEvent<HTMLAnchorElement>) {
@@ -56,7 +63,8 @@ export function HeaderMenu({
56
63
  // if the url is internal, we strip the domain
57
64
  const url =
58
65
  item.url.includes('myshopify.com') ||
59
- item.url.includes(publicStoreDomain)
66
+ item.url.includes(publicStoreDomain) ||
67
+ item.url.includes(primaryDomainUrl)
60
68
  ? new URL(item.url).pathname
61
69
  : item.url;
62
70
  return (
@@ -33,12 +33,12 @@ export function Layout({
33
33
  <>
34
34
  <CartAside cart={cart} />
35
35
  <SearchAside />
36
- <MobileMenuAside menu={header.menu} />
36
+ <MobileMenuAside menu={header.menu} shop={header.shop} />
37
37
  <Header header={header} cart={cart} isLoggedIn={isLoggedIn} />
38
38
  <main>{children}</main>
39
39
  <Suspense>
40
40
  <Await resolve={footer}>
41
- {(footer) => <Footer menu={footer.menu} />}
41
+ {(footer) => <Footer menu={footer.menu} shop={header.shop} />}
42
42
  </Await>
43
43
  </Suspense>
44
44
  </>
@@ -86,10 +86,20 @@ function SearchAside() {
86
86
  );
87
87
  }
88
88
 
89
- function MobileMenuAside({menu}: {menu: HeaderQuery['menu']}) {
89
+ function MobileMenuAside({
90
+ menu,
91
+ shop,
92
+ }: {
93
+ menu: HeaderQuery['menu'];
94
+ shop: HeaderQuery['shop'];
95
+ }) {
90
96
  return (
91
97
  <Aside id="mobile-menu-aside" heading="MENU">
92
- <HeaderMenu menu={menu} viewport="mobile" />
98
+ <HeaderMenu
99
+ menu={menu}
100
+ viewport="mobile"
101
+ primaryDomainUrl={shop.primaryDomain.url}
102
+ />
93
103
  </Aside>
94
104
  );
95
105
  }
@@ -1,11 +1,14 @@
1
1
  import {useNonce} from '@shopify/hydrogen';
2
- import {defer, type LoaderArgs} from '@shopify/remix-oxygen';
2
+ import {
3
+ defer,
4
+ type SerializeFrom,
5
+ type LoaderFunctionArgs,
6
+ } from '@shopify/remix-oxygen';
3
7
  import {
4
8
  Links,
5
9
  Meta,
6
10
  Outlet,
7
11
  Scripts,
8
- useCatch,
9
12
  LiveReload,
10
13
  useMatches,
11
14
  useRouteError,
@@ -15,7 +18,6 @@ import {
15
18
  type ShouldRevalidateFunction,
16
19
  } from '@remix-run/react';
17
20
  import type {CustomerAccessToken} from '@shopify/hydrogen/storefront-api-types';
18
- import type {HydrogenSession} from '../server';
19
21
  import favicon from '../public/favicon.svg';
20
22
  import resetStyles from './styles/reset.css';
21
23
  import appStyles from './styles/app.css';
@@ -58,7 +60,12 @@ export function links() {
58
60
  ];
59
61
  }
60
62
 
61
- export async function loader({context}: LoaderArgs) {
63
+ export const useRootLoaderData = () => {
64
+ const [root] = useMatches();
65
+ return root?.data as SerializeFrom<typeof loader>;
66
+ };
67
+
68
+ export async function loader({context}: LoaderFunctionArgs) {
62
69
  const {storefront, session, cart} = context;
63
70
  const customerAccessToken = await session.get('customerAccessToken');
64
71
  const publicStoreDomain = context.env.PUBLIC_STORE_DOMAIN;
@@ -126,7 +133,7 @@ export default function App() {
126
133
 
127
134
  export function ErrorBoundary() {
128
135
  const error = useRouteError();
129
- const [root] = useMatches();
136
+ const rootData = useRootLoaderData();
130
137
  const nonce = useNonce();
131
138
  let errorMessage = 'Unknown error';
132
139
  let errorStatus = 500;
@@ -147,7 +154,7 @@ export function ErrorBoundary() {
147
154
  <Links />
148
155
  </head>
149
156
  <body>
150
- <Layout {...root.data}>
157
+ <Layout {...rootData}>
151
158
  <div className="route-error">
152
159
  <h1>Oops</h1>
153
160
  <h2>{errorStatus}</h2>
@@ -166,26 +173,6 @@ export function ErrorBoundary() {
166
173
  );
167
174
  }
168
175
 
169
- export const ErrorBoundaryV1 = ({error}: {error: Error}) => {
170
- // eslint-disable-next-line no-console
171
- console.error(error);
172
-
173
- return <div>There was an error.</div>;
174
- };
175
-
176
- export function CatchBoundary() {
177
- const caught = useCatch();
178
- // eslint-disable-next-line no-console
179
- console.error(caught);
180
-
181
- return (
182
- <div>
183
- There was an error. Status: {caught.status}. Message:{' '}
184
- {caught.data?.message}
185
- </div>
186
- );
187
- }
188
-
189
176
  /**
190
177
  * Validates the customer access token and returns a boolean and headers
191
178
  * @see https://shopify.dev/docs/api/storefront/latest/objects/CustomerAccessToken
@@ -199,7 +186,7 @@ export function CatchBoundary() {
199
186
  * ```
200
187
  */
201
188
  async function validateCustomerAccessToken(
202
- session: HydrogenSession,
189
+ session: LoaderFunctionArgs['context']['session'],
203
190
  customerAccessToken?: CustomerAccessToken,
204
191
  ) {
205
192
  let isLoggedIn = false;
@@ -1,6 +1,6 @@
1
- import type {LoaderArgs} from '@shopify/remix-oxygen';
1
+ import type {LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
 
3
- export async function loader({request}: LoaderArgs) {
3
+ export async function loader({request}: LoaderFunctionArgs) {
4
4
  throw new Response(`${new URL(request.url).pathname} not found`, {
5
5
  status: 404,
6
6
  });
@@ -1,8 +1,8 @@
1
- import {type LoaderArgs} from '@shopify/remix-oxygen';
1
+ import {type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
  import {useRouteError, isRouteErrorResponse} from '@remix-run/react';
3
3
  import {parseGid} from '@shopify/hydrogen';
4
4
 
5
- export async function loader({request, context}: LoaderArgs) {
5
+ export async function loader({request, context}: LoaderFunctionArgs) {
6
6
  const url = new URL(request.url);
7
7
 
8
8
  const {shop} = await context.storefront.query(ROBOTS_QUERY);
@@ -1,5 +1,5 @@
1
1
  import {flattenConnection} from '@shopify/hydrogen';
2
- import type {LoaderArgs} from '@shopify/remix-oxygen';
2
+ import type {LoaderFunctionArgs} from '@shopify/remix-oxygen';
3
3
  import type {SitemapQuery} from 'storefrontapi.generated';
4
4
 
5
5
  /**
@@ -19,7 +19,10 @@ type Entry = {
19
19
  };
20
20
  };
21
21
 
22
- export async function loader({request, context: {storefront}}: LoaderArgs) {
22
+ export async function loader({
23
+ request,
24
+ context: {storefront},
25
+ }: LoaderFunctionArgs) {
23
26
  const data = await storefront.query(SITEMAP_QUERY, {
24
27
  variables: {
25
28
  urlLimits: MAX_URLS,
@@ -1,10 +1,5 @@
1
- import {defer, type LoaderArgs} from '@shopify/remix-oxygen';
2
- import {
3
- Await,
4
- useLoaderData,
5
- Link,
6
- type V2_MetaFunction,
7
- } from '@remix-run/react';
1
+ import {defer, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
+ import {Await, useLoaderData, Link, type MetaFunction} from '@remix-run/react';
8
3
  import {Suspense} from 'react';
9
4
  import {Image, Money} from '@shopify/hydrogen';
10
5
  import type {
@@ -12,11 +7,11 @@ import type {
12
7
  RecommendedProductsQuery,
13
8
  } from 'storefrontapi.generated';
14
9
 
15
- export const meta: V2_MetaFunction = () => {
10
+ export const meta: MetaFunction = () => {
16
11
  return [{title: 'Hydrogen | Home'}];
17
12
  };
18
13
 
19
- export async function loader({context}: LoaderArgs) {
14
+ export async function loader({context}: LoaderFunctionArgs) {
20
15
  const {storefront} = context;
21
16
  const {collections} = await storefront.query(FEATURED_COLLECTION_QUERY);
22
17
  const featuredCollection = collections.nodes[0];
@@ -1,6 +1,6 @@
1
- import {redirect, type LoaderArgs} from '@shopify/remix-oxygen';
1
+ import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
2
2
 
3
- export async function loader({context}: LoaderArgs) {
3
+ export async function loader({context}: LoaderFunctionArgs) {
4
4
  if (await context.session.get('customerAccessToken')) {
5
5
  return redirect('/account');
6
6
  }
@@ -3,15 +3,15 @@ import type {AddressFragment, CustomerFragment} from 'storefrontapi.generated';
3
3
  import {
4
4
  json,
5
5
  redirect,
6
- type ActionArgs,
7
- type LoaderArgs,
6
+ type ActionFunctionArgs,
7
+ type LoaderFunctionArgs,
8
8
  } from '@shopify/remix-oxygen';
9
9
  import {
10
10
  Form,
11
11
  useActionData,
12
12
  useNavigation,
13
13
  useOutletContext,
14
- type V2_MetaFunction,
14
+ type MetaFunction,
15
15
  } from '@remix-run/react';
16
16
 
17
17
  export type ActionResponse = {
@@ -23,11 +23,11 @@ export type ActionResponse = {
23
23
  updatedAddress?: AddressFragment;
24
24
  };
25
25
 
26
- export const meta: V2_MetaFunction = () => {
26
+ export const meta: MetaFunction = () => {
27
27
  return [{title: 'Addresses'}];
28
28
  };
29
29
 
30
- export async function loader({context}: LoaderArgs) {
30
+ export async function loader({context}: LoaderFunctionArgs) {
31
31
  const {session} = context;
32
32
  const customerAccessToken = await session.get('customerAccessToken');
33
33
  if (!customerAccessToken) {
@@ -36,7 +36,7 @@ export async function loader({context}: LoaderArgs) {
36
36
  return json({});
37
37
  }
38
38
 
39
- export async function action({request, context}: ActionArgs) {
39
+ export async function action({request, context}: ActionFunctionArgs) {
40
40
  const {storefront, session} = context;
41
41
 
42
42
  try {