@shopify/cli-hydrogen 5.5.0 → 6.0.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/build.js +2 -6
- package/dist/commands/hydrogen/{codegen-unstable.js → codegen.js} +2 -0
- package/dist/commands/hydrogen/deploy.js +1 -0
- package/dist/commands/hydrogen/deploy.test.js +1 -0
- package/dist/commands/hydrogen/dev.js +6 -8
- package/dist/commands/hydrogen/generate/route.js +5 -1
- package/dist/commands/hydrogen/generate/route.test.js +4 -2
- package/dist/commands/hydrogen/init.test.js +1 -4
- package/dist/commands/hydrogen/setup.js +1 -6
- package/dist/generator-templates/starter/CHANGELOG.md +143 -0
- package/dist/generator-templates/starter/app/components/Footer.tsx +19 -8
- package/dist/generator-templates/starter/app/components/Header.tsx +13 -5
- package/dist/generator-templates/starter/app/components/Layout.tsx +14 -4
- package/dist/generator-templates/starter/app/root.tsx +14 -27
- package/dist/generator-templates/starter/app/routes/$.tsx +2 -2
- package/dist/generator-templates/starter/app/routes/[robots.txt].tsx +2 -2
- package/dist/generator-templates/starter/app/routes/[sitemap.xml].tsx +5 -2
- package/dist/generator-templates/starter/app/routes/_index.tsx +4 -9
- package/dist/generator-templates/starter/app/routes/account.$.tsx +2 -2
- package/dist/generator-templates/starter/app/routes/account.addresses.tsx +6 -6
- package/dist/generator-templates/starter/app/routes/account.orders.$id.tsx +4 -4
- package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +4 -4
- package/dist/generator-templates/starter/app/routes/account.profile.tsx +6 -6
- package/dist/generator-templates/starter/app/routes/account.tsx +2 -2
- package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +6 -6
- package/dist/generator-templates/starter/app/routes/account_.login.tsx +6 -11
- package/dist/generator-templates/starter/app/routes/account_.logout.tsx +4 -4
- package/dist/generator-templates/starter/app/routes/account_.recover.tsx +4 -4
- package/dist/generator-templates/starter/app/routes/account_.register.tsx +4 -4
- package/dist/generator-templates/starter/app/routes/account_.reset.$id.$resetToken.tsx +4 -4
- package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +4 -4
- package/dist/generator-templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +5 -5
- package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +5 -5
- package/dist/generator-templates/starter/app/routes/blogs._index.tsx +7 -4
- package/dist/generator-templates/starter/app/routes/cart.$lines.tsx +2 -2
- package/dist/generator-templates/starter/app/routes/cart.tsx +11 -8
- package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +5 -5
- package/dist/generator-templates/starter/app/routes/collections._index.tsx +2 -2
- package/dist/generator-templates/starter/app/routes/discount.$code.tsx +2 -2
- package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +5 -5
- package/dist/generator-templates/starter/app/routes/policies.$handle.tsx +5 -5
- package/dist/generator-templates/starter/app/routes/policies._index.tsx +2 -2
- package/dist/generator-templates/starter/app/routes/products.$handle.tsx +7 -7
- package/dist/generator-templates/starter/app/routes/search.tsx +5 -4
- package/dist/generator-templates/starter/app/styles/app.css +16 -16
- package/dist/generator-templates/starter/package.json +10 -10
- package/dist/generator-templates/starter/remix.config.js +0 -8
- package/dist/generator-templates/starter/storefrontapi.generated.d.ts +79 -55
- package/dist/lib/codegen.js +31 -22
- package/dist/lib/flags.js +8 -1
- package/dist/lib/onboarding/common.js +2 -14
- package/dist/lib/remix-version-check.js +2 -1
- package/dist/lib/remix-version-interop.js +1 -39
- package/dist/lib/remix-version-interop.test.js +8 -98
- package/dist/lib/setups/css/postcss.js +1 -6
- package/dist/lib/setups/css/tailwind.js +6 -12
- package/dist/lib/setups/routes/generate.js +13 -23
- package/dist/lib/setups/routes/generate.test.js +6 -20
- package/dist/lib/transpile/morph/typedefs.js +1 -1
- package/oclif.manifest.json +29 -16
- 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
|
-
|
|
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
|
|
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,
|
|
@@ -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
|
-
|
|
32
|
-
description:
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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(
|
|
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(
|
|
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
|
|
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 {
|
|
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({
|
|
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({
|
|
13
|
-
|
|
14
|
-
|
|
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
|
|
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
|
|
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
|
|
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({
|
|
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
|
|
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 {
|
|
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
|
|
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
|
|
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 {...
|
|
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:
|
|
189
|
+
session: LoaderFunctionArgs['context']['session'],
|
|
203
190
|
customerAccessToken?: CustomerAccessToken,
|
|
204
191
|
) {
|
|
205
192
|
let isLoggedIn = false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {LoaderFunctionArgs} from '@shopify/remix-oxygen';
|
|
2
2
|
|
|
3
|
-
export async function loader({request}:
|
|
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
|
|
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}:
|
|
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 {
|
|
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({
|
|
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
|
|
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:
|
|
10
|
+
export const meta: MetaFunction = () => {
|
|
16
11
|
return [{title: 'Hydrogen | Home'}];
|
|
17
12
|
};
|
|
18
13
|
|
|
19
|
-
export async function loader({context}:
|
|
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
|
|
1
|
+
import {redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
|
|
2
2
|
|
|
3
|
-
export async function loader({context}:
|
|
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
|
|
7
|
-
type
|
|
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
|
|
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:
|
|
26
|
+
export const meta: MetaFunction = () => {
|
|
27
27
|
return [{title: 'Addresses'}];
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
export async function loader({context}:
|
|
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}:
|
|
39
|
+
export async function action({request, context}: ActionFunctionArgs) {
|
|
40
40
|
const {storefront, session} = context;
|
|
41
41
|
|
|
42
42
|
try {
|