@shopify/cli-hydrogen 5.1.1 → 5.2.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 +10 -5
- package/dist/commands/hydrogen/check.js +1 -1
- package/dist/commands/hydrogen/codegen-unstable.js +3 -3
- package/dist/commands/hydrogen/dev.js +26 -17
- package/dist/commands/hydrogen/init.js +3 -0
- package/dist/commands/hydrogen/init.test.js +199 -51
- package/dist/commands/hydrogen/preview.js +4 -3
- package/dist/commands/hydrogen/setup.js +5 -2
- package/dist/commands/hydrogen/setup.test.js +62 -0
- package/dist/generator-templates/starter/app/components/Footer.tsx +1 -1
- package/dist/generator-templates/starter/app/components/Header.tsx +1 -1
- package/dist/generator-templates/starter/app/components/Search.tsx +3 -3
- package/dist/generator-templates/starter/app/root.tsx +24 -1
- package/dist/generator-templates/starter/app/routes/$.tsx +4 -0
- package/dist/generator-templates/starter/app/routes/_index.tsx +6 -2
- package/dist/generator-templates/starter/app/routes/account.$.tsx +1 -2
- package/dist/generator-templates/starter/app/routes/account.addresses.tsx +1 -1
- package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +2 -7
- package/dist/generator-templates/starter/app/routes/account.profile.tsx +7 -2
- package/dist/generator-templates/starter/app/routes/account.tsx +4 -3
- package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +6 -2
- package/dist/generator-templates/starter/app/routes/account_.login.tsx +6 -2
- package/dist/generator-templates/starter/app/routes/account_.logout.tsx +2 -6
- package/dist/generator-templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +1 -2
- package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +1 -2
- package/dist/generator-templates/starter/app/routes/blogs._index.tsx +1 -2
- package/dist/generator-templates/starter/app/routes/cart.tsx +1 -2
- package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +1 -2
- package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +1 -2
- package/dist/generator-templates/starter/app/routes/policies.$handle.tsx +2 -3
- package/dist/generator-templates/starter/app/routes/products.$handle.tsx +23 -15
- package/dist/generator-templates/starter/app/routes/search.tsx +1 -2
- package/dist/generator-templates/starter/package.json +4 -4
- package/dist/generator-templates/starter/remix.config.js +1 -0
- package/dist/generator-templates/starter/storefrontapi.generated.d.ts +9 -9
- package/dist/lib/ast.js +9 -0
- package/dist/lib/check-version.test.js +1 -0
- package/dist/lib/codegen.js +17 -7
- package/dist/lib/environment-variables.js +15 -11
- package/dist/lib/file.test.js +4 -5
- package/dist/lib/find-port.js +9 -0
- package/dist/lib/flags.js +3 -2
- package/dist/lib/format-code.js +3 -0
- package/dist/lib/live-reload.js +62 -0
- package/dist/lib/log.js +6 -1
- package/dist/lib/mini-oxygen.js +28 -18
- package/dist/lib/missing-routes.js +17 -1
- package/dist/lib/onboarding/common.js +5 -0
- package/dist/lib/onboarding/local.js +21 -8
- package/dist/lib/onboarding/remote.js +8 -3
- package/dist/lib/remix-config.js +2 -0
- package/dist/lib/remix-version-check.test.js +1 -0
- package/dist/lib/setups/css/replacers.js +7 -4
- package/dist/lib/setups/i18n/replacers.js +7 -5
- package/dist/lib/setups/routes/generate.js +4 -1
- package/dist/lib/setups/routes/generate.test.js +10 -11
- package/dist/lib/template-downloader.js +4 -0
- package/dist/lib/transpile-ts.js +1 -0
- package/dist/lib/virtual-routes.js +4 -1
- package/dist/virtual-routes/components/HydrogenLogoBaseBW.jsx +26 -4
- package/dist/virtual-routes/components/HydrogenLogoBaseColor.jsx +40 -10
- package/dist/virtual-routes/components/IconBanner.jsx +286 -44
- package/dist/virtual-routes/components/IconDiscord.jsx +17 -1
- package/dist/virtual-routes/components/IconError.jsx +55 -17
- package/dist/virtual-routes/components/IconGithub.jsx +19 -1
- package/dist/virtual-routes/components/IconTwitter.jsx +17 -1
- package/dist/virtual-routes/components/Layout.jsx +1 -1
- package/dist/virtual-routes/routes/index.jsx +110 -94
- package/dist/virtual-routes/virtual-root.jsx +7 -15
- package/oclif.manifest.json +1 -1
- package/package.json +9 -8
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { vi, describe, beforeEach, it, expect } from 'vitest';
|
|
3
|
+
import { inTemporaryDirectory, copyFile, fileExists, readFile } from '@shopify/cli-kit/node/fs';
|
|
4
|
+
import { joinPath } from '@shopify/cli-kit/node/path';
|
|
5
|
+
import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
|
|
6
|
+
import { runSetup } from './setup.js';
|
|
7
|
+
import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
|
|
8
|
+
|
|
9
|
+
vi.mock("../../lib/shell.js");
|
|
10
|
+
vi.mock("@shopify/cli-kit/node/ui", async () => {
|
|
11
|
+
const original = await vi.importActual("@shopify/cli-kit/node/ui");
|
|
12
|
+
return {
|
|
13
|
+
...original,
|
|
14
|
+
renderConfirmationPrompt: vi.fn(),
|
|
15
|
+
renderSelectPrompt: vi.fn(),
|
|
16
|
+
renderTextPrompt: vi.fn(),
|
|
17
|
+
renderInfo: vi.fn()
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
describe("setup", () => {
|
|
21
|
+
const outputMock = mockAndCaptureOutput();
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
vi.resetAllMocks();
|
|
24
|
+
});
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
outputMock.clear();
|
|
27
|
+
});
|
|
28
|
+
it("sets up an i18n strategy and generates routes", async () => {
|
|
29
|
+
await inTemporaryDirectory(async (tmpDir) => {
|
|
30
|
+
await copyFile(
|
|
31
|
+
fileURLToPath(
|
|
32
|
+
new URL("../../../../../templates/hello-world", import.meta.url)
|
|
33
|
+
),
|
|
34
|
+
tmpDir
|
|
35
|
+
);
|
|
36
|
+
await expect(
|
|
37
|
+
fileExists(joinPath(tmpDir, "app/routes/_index.tsx"))
|
|
38
|
+
).resolves.toBeFalsy();
|
|
39
|
+
vi.mocked(renderConfirmationPrompt).mockResolvedValueOnce(true);
|
|
40
|
+
await expect(
|
|
41
|
+
runSetup({
|
|
42
|
+
directory: tmpDir,
|
|
43
|
+
markets: "subfolders",
|
|
44
|
+
installDeps: false
|
|
45
|
+
})
|
|
46
|
+
).resolves.not.toThrow();
|
|
47
|
+
await expect(
|
|
48
|
+
fileExists(joinPath(tmpDir, "app/routes/($locale)._index.tsx"))
|
|
49
|
+
).resolves.toBeTruthy();
|
|
50
|
+
const serverFile = await readFile(`${tmpDir}/server.ts`);
|
|
51
|
+
expect(serverFile).toMatch(/i18n: getLocaleFromRequest\(request\),/);
|
|
52
|
+
expect(serverFile).toMatch(/url.pathname/);
|
|
53
|
+
const output = outputMock.info();
|
|
54
|
+
expect(output).toMatch("success");
|
|
55
|
+
expect(output).not.toMatch("warning");
|
|
56
|
+
expect(output).toMatch(/Markets:\s*Subfolders/);
|
|
57
|
+
expect(output).toMatch("Routes");
|
|
58
|
+
expect(output).toMatch("Home (/ & /:catchAll)");
|
|
59
|
+
expect(output).toMatch("Account (/account/*)");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
useParams,
|
|
3
|
-
useFetcher,
|
|
4
2
|
Link,
|
|
5
3
|
Form,
|
|
4
|
+
useParams,
|
|
5
|
+
useFetcher,
|
|
6
|
+
useFetchers,
|
|
6
7
|
type FormProps,
|
|
7
8
|
} from '@remix-run/react';
|
|
8
9
|
import {Image, Money, Pagination} from '@shopify/hydrogen';
|
|
9
10
|
import React, {useRef, useEffect} from 'react';
|
|
10
|
-
import {useFetchers} from '@remix-run/react';
|
|
11
11
|
|
|
12
12
|
import type {
|
|
13
13
|
PredictiveProductFragment,
|
|
@@ -5,19 +5,40 @@ import {
|
|
|
5
5
|
Outlet,
|
|
6
6
|
Scripts,
|
|
7
7
|
useCatch,
|
|
8
|
+
LiveReload,
|
|
8
9
|
useMatches,
|
|
9
10
|
useRouteError,
|
|
10
11
|
useLoaderData,
|
|
11
12
|
ScrollRestoration,
|
|
12
13
|
isRouteErrorResponse,
|
|
14
|
+
type ShouldRevalidateFunction,
|
|
13
15
|
} from '@remix-run/react';
|
|
14
|
-
import type {CustomerAccessToken} from '@shopify/hydrogen
|
|
16
|
+
import type {CustomerAccessToken} from '@shopify/hydrogen/storefront-api-types';
|
|
15
17
|
import type {HydrogenSession} from '../server';
|
|
16
18
|
import favicon from '../public/favicon.svg';
|
|
17
19
|
import resetStyles from './styles/reset.css';
|
|
18
20
|
import appStyles from './styles/app.css';
|
|
19
21
|
import {Layout} from '~/components/Layout';
|
|
20
22
|
|
|
23
|
+
// This is important to avoid re-fetching root queries on sub-navigations
|
|
24
|
+
export const shouldRevalidate: ShouldRevalidateFunction = ({
|
|
25
|
+
formMethod,
|
|
26
|
+
currentUrl,
|
|
27
|
+
nextUrl,
|
|
28
|
+
}) => {
|
|
29
|
+
// revalidate when a mutation is performed e.g add to cart, login...
|
|
30
|
+
if (formMethod && formMethod !== 'GET') {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// revalidate when manually revalidating via useRevalidator
|
|
35
|
+
if (currentUrl.toString() === nextUrl.toString()) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return false;
|
|
40
|
+
};
|
|
41
|
+
|
|
21
42
|
export function links() {
|
|
22
43
|
return [
|
|
23
44
|
{rel: 'stylesheet', href: resetStyles},
|
|
@@ -93,6 +114,7 @@ export default function App() {
|
|
|
93
114
|
</Layout>
|
|
94
115
|
<ScrollRestoration />
|
|
95
116
|
<Scripts />
|
|
117
|
+
<LiveReload />
|
|
96
118
|
</body>
|
|
97
119
|
</html>
|
|
98
120
|
);
|
|
@@ -133,6 +155,7 @@ export function ErrorBoundary() {
|
|
|
133
155
|
</Layout>
|
|
134
156
|
<ScrollRestoration />
|
|
135
157
|
<Scripts />
|
|
158
|
+
<LiveReload />
|
|
136
159
|
</body>
|
|
137
160
|
</html>
|
|
138
161
|
);
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import type {V2_MetaFunction} from '@shopify/remix-oxygen';
|
|
2
1
|
import {defer, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
3
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Await,
|
|
4
|
+
useLoaderData,
|
|
5
|
+
Link,
|
|
6
|
+
type V2_MetaFunction,
|
|
7
|
+
} from '@remix-run/react';
|
|
4
8
|
import {Suspense} from 'react';
|
|
5
9
|
import {Image, Money} from '@shopify/hydrogen';
|
|
6
10
|
import type {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {redirect} from '@shopify/remix-oxygen';
|
|
1
|
+
import {redirect, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
3
2
|
|
|
4
3
|
export async function loader({context}: LoaderArgs) {
|
|
5
4
|
if (await context.session.get('customerAccessToken')) {
|
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
redirect,
|
|
6
6
|
type ActionArgs,
|
|
7
7
|
type LoaderArgs,
|
|
8
|
-
type V2_MetaFunction,
|
|
9
8
|
} from '@shopify/remix-oxygen';
|
|
10
9
|
import {
|
|
11
10
|
Form,
|
|
12
11
|
useActionData,
|
|
13
12
|
useNavigation,
|
|
14
13
|
useOutletContext,
|
|
14
|
+
type V2_MetaFunction,
|
|
15
15
|
} from '@remix-run/react';
|
|
16
16
|
|
|
17
17
|
export type ActionResponse = {
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import {Link, useLoaderData} from '@remix-run/react';
|
|
1
|
+
import {Link, useLoaderData, type V2_MetaFunction} from '@remix-run/react';
|
|
2
2
|
import {Money, Pagination, getPaginationVariables} from '@shopify/hydrogen';
|
|
3
|
-
import {
|
|
4
|
-
json,
|
|
5
|
-
redirect,
|
|
6
|
-
type LoaderArgs,
|
|
7
|
-
type V2_MetaFunction,
|
|
8
|
-
} from '@shopify/remix-oxygen';
|
|
3
|
+
import {json, redirect, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
9
4
|
import type {
|
|
10
5
|
CustomerOrdersFragment,
|
|
11
6
|
OrderItemFragment,
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import type {CustomerFragment} from 'storefrontapi.generated';
|
|
2
2
|
import type {CustomerUpdateInput} from '@shopify/hydrogen/storefront-api-types';
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
json,
|
|
5
|
+
redirect,
|
|
6
|
+
type ActionArgs,
|
|
7
|
+
type LoaderArgs,
|
|
8
|
+
} from '@shopify/remix-oxygen';
|
|
5
9
|
import {
|
|
6
10
|
Form,
|
|
7
11
|
useActionData,
|
|
8
12
|
useNavigation,
|
|
9
13
|
useOutletContext,
|
|
14
|
+
type V2_MetaFunction,
|
|
10
15
|
} from '@remix-run/react';
|
|
11
16
|
|
|
12
17
|
export type ActionResponse = {
|
|
@@ -109,13 +109,13 @@ function AccountLayout({
|
|
|
109
109
|
<div className="account">
|
|
110
110
|
<h1>{heading}</h1>
|
|
111
111
|
<br />
|
|
112
|
-
<
|
|
112
|
+
<AccountMenu />
|
|
113
113
|
{children}
|
|
114
114
|
</div>
|
|
115
115
|
);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
function
|
|
118
|
+
function AccountMenu() {
|
|
119
119
|
function isActiveStyle({
|
|
120
120
|
isActive,
|
|
121
121
|
isPending,
|
|
@@ -124,10 +124,11 @@ function AcccountMenu() {
|
|
|
124
124
|
isPending: boolean;
|
|
125
125
|
}) {
|
|
126
126
|
return {
|
|
127
|
-
fontWeight: isActive ? 'bold' :
|
|
127
|
+
fontWeight: isActive ? 'bold' : undefined,
|
|
128
128
|
color: isPending ? 'grey' : 'black',
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
|
+
|
|
131
132
|
return (
|
|
132
133
|
<nav role="navigation">
|
|
133
134
|
<NavLink to="/account/orders" style={isActiveStyle}>
|
package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
json,
|
|
3
|
+
redirect,
|
|
4
|
+
type ActionArgs,
|
|
5
|
+
type LoaderArgs,
|
|
6
|
+
} from '@shopify/remix-oxygen';
|
|
3
7
|
import {Form, useActionData, type V2_MetaFunction} from '@remix-run/react';
|
|
4
8
|
|
|
5
9
|
type ActionResponse = {
|
|
@@ -3,9 +3,13 @@ import {
|
|
|
3
3
|
redirect,
|
|
4
4
|
type ActionArgs,
|
|
5
5
|
type LoaderArgs,
|
|
6
|
-
type V2_MetaFunction,
|
|
7
6
|
} from '@shopify/remix-oxygen';
|
|
8
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Form,
|
|
9
|
+
Link,
|
|
10
|
+
useActionData,
|
|
11
|
+
type V2_MetaFunction,
|
|
12
|
+
} from '@remix-run/react';
|
|
9
13
|
|
|
10
14
|
type ActionResponse = {
|
|
11
15
|
error: string | null;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
redirect,
|
|
4
|
-
type ActionArgs,
|
|
5
|
-
type V2_MetaFunction,
|
|
6
|
-
} from '@shopify/remix-oxygen';
|
|
1
|
+
import {json, redirect, type ActionArgs} from '@shopify/remix-oxygen';
|
|
2
|
+
import {type V2_MetaFunction} from '@remix-run/react';
|
|
7
3
|
|
|
8
4
|
export const meta: V2_MetaFunction = () => {
|
|
9
5
|
return [{title: 'Logout'}];
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {V2_MetaFunction} from '@shopify/remix-oxygen';
|
|
2
1
|
import {json, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
3
|
-
import {useLoaderData} from '@remix-run/react';
|
|
2
|
+
import {useLoaderData, type V2_MetaFunction} from '@remix-run/react';
|
|
4
3
|
import {Image} from '@shopify/hydrogen';
|
|
5
4
|
|
|
6
5
|
export const meta: V2_MetaFunction = ({data}) => {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {V2_MetaFunction} from '@shopify/remix-oxygen';
|
|
2
1
|
import {json, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
3
|
-
import {Link, useLoaderData} from '@remix-run/react';
|
|
2
|
+
import {Link, useLoaderData, type V2_MetaFunction} from '@remix-run/react';
|
|
4
3
|
import {Image, Pagination, getPaginationVariables} from '@shopify/hydrogen';
|
|
5
4
|
import type {ArticleItemFragment} from 'storefrontapi.generated';
|
|
6
5
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {V2_MetaFunction} from '@shopify/remix-oxygen';
|
|
2
1
|
import {json, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
3
|
-
import {Link, useLoaderData} from '@remix-run/react';
|
|
2
|
+
import {Link, useLoaderData, type V2_MetaFunction} from '@remix-run/react';
|
|
4
3
|
import {Pagination, getPaginationVariables} from '@shopify/hydrogen';
|
|
5
4
|
|
|
6
5
|
export const meta: V2_MetaFunction = () => {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {Await, useMatches} from '@remix-run/react';
|
|
1
|
+
import {Await, useMatches, type V2_MetaFunction} from '@remix-run/react';
|
|
2
2
|
import {Suspense} from 'react';
|
|
3
3
|
import type {CartQueryData} from '@shopify/hydrogen';
|
|
4
4
|
import {CartForm} from '@shopify/hydrogen';
|
|
5
|
-
import type {V2_MetaFunction} from '@shopify/remix-oxygen';
|
|
6
5
|
import {type ActionArgs, json} from '@shopify/remix-oxygen';
|
|
7
6
|
import type {CartApiQueryFragment} from 'storefrontapi.generated';
|
|
8
7
|
import {CartMain} from '~/components/Cart';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {V2_MetaFunction} from '@shopify/remix-oxygen';
|
|
2
1
|
import {json, redirect, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
3
|
-
import {useLoaderData, Link} from '@remix-run/react';
|
|
2
|
+
import {useLoaderData, Link, type V2_MetaFunction} from '@remix-run/react';
|
|
4
3
|
import {
|
|
5
4
|
Pagination,
|
|
6
5
|
getPaginationVariables,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {V2_MetaFunction} from '@shopify/remix-oxygen';
|
|
2
1
|
import {json, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
3
|
-
import {useLoaderData} from '@remix-run/react';
|
|
2
|
+
import {useLoaderData, type V2_MetaFunction} from '@remix-run/react';
|
|
4
3
|
|
|
5
4
|
export const meta: V2_MetaFunction = ({data}) => {
|
|
6
5
|
return [{title: `Hydrogen | ${data.page.title}`}];
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type {V2_MetaFunction} from '@shopify/remix-oxygen';
|
|
2
1
|
import {json, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
3
|
-
import {Link, useLoaderData} from '@remix-run/react';
|
|
4
|
-
import {type Shop} from '@shopify/hydrogen
|
|
2
|
+
import {Link, useLoaderData, type V2_MetaFunction} from '@remix-run/react';
|
|
3
|
+
import {type Shop} from '@shopify/hydrogen/storefront-api-types';
|
|
5
4
|
|
|
6
5
|
type SelectedPolicies = keyof Pick<
|
|
7
6
|
Shop,
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import {Suspense} from 'react';
|
|
2
|
-
import type {V2_MetaFunction} from '@shopify/remix-oxygen';
|
|
3
2
|
import {defer, redirect, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
4
|
-
import
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
Await,
|
|
5
|
+
Link,
|
|
6
|
+
useLoaderData,
|
|
7
|
+
type V2_MetaFunction,
|
|
8
|
+
type FetcherWithComponents,
|
|
9
|
+
} from '@remix-run/react';
|
|
6
10
|
import type {
|
|
7
11
|
ProductFragment,
|
|
8
12
|
ProductVariantsQuery,
|
|
@@ -17,7 +21,10 @@ import {
|
|
|
17
21
|
getSelectedProductOptions,
|
|
18
22
|
CartForm,
|
|
19
23
|
} from '@shopify/hydrogen';
|
|
20
|
-
import type {
|
|
24
|
+
import type {
|
|
25
|
+
CartLineInput,
|
|
26
|
+
SelectedOption,
|
|
27
|
+
} from '@shopify/hydrogen/storefront-api-types';
|
|
21
28
|
import {getVariantUrl} from '~/utils';
|
|
22
29
|
|
|
23
30
|
export const meta: V2_MetaFunction = ({data}) => {
|
|
@@ -47,15 +54,6 @@ export async function loader({params, request, context}: LoaderArgs) {
|
|
|
47
54
|
variables: {handle, selectedOptions},
|
|
48
55
|
});
|
|
49
56
|
|
|
50
|
-
// In order to show which variants are available in the UI, we need to query
|
|
51
|
-
// all of them. But there might be a *lot*, so instead separate the variants
|
|
52
|
-
// into it's own separate query that is deferred. So there's a brief moment
|
|
53
|
-
// where variant options might show as available when they're not, but after
|
|
54
|
-
// this deffered query resolves, the UI will update.
|
|
55
|
-
const variants = storefront.query(VARIANTS_QUERY, {
|
|
56
|
-
variables: {handle},
|
|
57
|
-
});
|
|
58
|
-
|
|
59
57
|
if (!product?.id) {
|
|
60
58
|
throw new Response(null, {status: 404});
|
|
61
59
|
}
|
|
@@ -63,7 +61,8 @@ export async function loader({params, request, context}: LoaderArgs) {
|
|
|
63
61
|
const firstVariant = product.variants.nodes[0];
|
|
64
62
|
const firstVariantIsDefault = Boolean(
|
|
65
63
|
firstVariant.selectedOptions.find(
|
|
66
|
-
(option) =>
|
|
64
|
+
(option: SelectedOption) =>
|
|
65
|
+
option.name === 'Title' && option.value === 'Default Title',
|
|
67
66
|
),
|
|
68
67
|
);
|
|
69
68
|
|
|
@@ -76,6 +75,16 @@ export async function loader({params, request, context}: LoaderArgs) {
|
|
|
76
75
|
return redirectToFirstVariant({product, request});
|
|
77
76
|
}
|
|
78
77
|
}
|
|
78
|
+
|
|
79
|
+
// In order to show which variants are available in the UI, we need to query
|
|
80
|
+
// all of them. But there might be a *lot*, so instead separate the variants
|
|
81
|
+
// into it's own separate query that is deferred. So there's a brief moment
|
|
82
|
+
// where variant options might show as available when they're not, but after
|
|
83
|
+
// this deffered query resolves, the UI will update.
|
|
84
|
+
const variants = storefront.query(VARIANTS_QUERY, {
|
|
85
|
+
variables: {handle},
|
|
86
|
+
});
|
|
87
|
+
|
|
79
88
|
return defer({product, variants});
|
|
80
89
|
}
|
|
81
90
|
|
|
@@ -337,7 +346,6 @@ const PRODUCT_VARIANT_FRAGMENT = `#graphql
|
|
|
337
346
|
title
|
|
338
347
|
handle
|
|
339
348
|
}
|
|
340
|
-
quantityAvailable
|
|
341
349
|
selectedOptions {
|
|
342
350
|
name
|
|
343
351
|
value
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {V2_MetaFunction} from '@shopify/remix-oxygen';
|
|
2
1
|
import {defer, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
3
|
-
import {useLoaderData} from '@remix-run/react';
|
|
2
|
+
import {useLoaderData, type V2_MetaFunction} from '@remix-run/react';
|
|
4
3
|
import {getPaginationVariables} from '@shopify/hydrogen';
|
|
5
4
|
|
|
6
5
|
import {SearchForm, SearchResults, NoSearchResults} from '~/components/Search';
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@remix-run/react": "1.19.1",
|
|
17
17
|
"@shopify/cli": "3.48.0",
|
|
18
|
-
"@shopify/cli-hydrogen": "^5.
|
|
19
|
-
"@shopify/hydrogen": "^2023.7.
|
|
20
|
-
"@shopify/remix-oxygen": "^1.1.
|
|
18
|
+
"@shopify/cli-hydrogen": "^5.2.0",
|
|
19
|
+
"@shopify/hydrogen": "^2023.7.3",
|
|
20
|
+
"@shopify/remix-oxygen": "^1.1.3",
|
|
21
21
|
"graphql": "^16.6.0",
|
|
22
22
|
"graphql-tag": "^2.12.6",
|
|
23
23
|
"isbot": "^3.6.6",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@remix-run/dev": "1.19.1",
|
|
29
|
-
"@shopify/oxygen-workers-types": "^3.17.
|
|
29
|
+
"@shopify/oxygen-workers-types": "^3.17.3",
|
|
30
30
|
"@shopify/prettier-config": "^1.1.2",
|
|
31
31
|
"@total-typescript/ts-reset": "^0.4.2",
|
|
32
32
|
"@types/eslint": "^8.4.10",
|
|
@@ -1350,7 +1350,7 @@ export type PoliciesQuery = {
|
|
|
1350
1350
|
|
|
1351
1351
|
export type ProductVariantFragment = Pick<
|
|
1352
1352
|
StorefrontAPI.ProductVariant,
|
|
1353
|
-
'availableForSale' | 'id' | '
|
|
1353
|
+
'availableForSale' | 'id' | 'sku' | 'title'
|
|
1354
1354
|
> & {
|
|
1355
1355
|
compareAtPrice?: StorefrontAPI.Maybe<
|
|
1356
1356
|
Pick<StorefrontAPI.MoneyV2, 'amount' | 'currencyCode'>
|
|
@@ -1377,7 +1377,7 @@ export type ProductFragment = Pick<
|
|
|
1377
1377
|
selectedVariant?: StorefrontAPI.Maybe<
|
|
1378
1378
|
Pick<
|
|
1379
1379
|
StorefrontAPI.ProductVariant,
|
|
1380
|
-
'availableForSale' | 'id' | '
|
|
1380
|
+
'availableForSale' | 'id' | 'sku' | 'title'
|
|
1381
1381
|
> & {
|
|
1382
1382
|
compareAtPrice?: StorefrontAPI.Maybe<
|
|
1383
1383
|
Pick<StorefrontAPI.MoneyV2, 'amount' | 'currencyCode'>
|
|
@@ -1402,7 +1402,7 @@ export type ProductFragment = Pick<
|
|
|
1402
1402
|
nodes: Array<
|
|
1403
1403
|
Pick<
|
|
1404
1404
|
StorefrontAPI.ProductVariant,
|
|
1405
|
-
'availableForSale' | 'id' | '
|
|
1405
|
+
'availableForSale' | 'id' | 'sku' | 'title'
|
|
1406
1406
|
> & {
|
|
1407
1407
|
compareAtPrice?: StorefrontAPI.Maybe<
|
|
1408
1408
|
Pick<StorefrontAPI.MoneyV2, 'amount' | 'currencyCode'>
|
|
@@ -1446,7 +1446,7 @@ export type ProductQuery = {
|
|
|
1446
1446
|
selectedVariant?: StorefrontAPI.Maybe<
|
|
1447
1447
|
Pick<
|
|
1448
1448
|
StorefrontAPI.ProductVariant,
|
|
1449
|
-
'availableForSale' | 'id' | '
|
|
1449
|
+
'availableForSale' | 'id' | 'sku' | 'title'
|
|
1450
1450
|
> & {
|
|
1451
1451
|
compareAtPrice?: StorefrontAPI.Maybe<
|
|
1452
1452
|
Pick<StorefrontAPI.MoneyV2, 'amount' | 'currencyCode'>
|
|
@@ -1471,7 +1471,7 @@ export type ProductQuery = {
|
|
|
1471
1471
|
nodes: Array<
|
|
1472
1472
|
Pick<
|
|
1473
1473
|
StorefrontAPI.ProductVariant,
|
|
1474
|
-
'availableForSale' | 'id' | '
|
|
1474
|
+
'availableForSale' | 'id' | 'sku' | 'title'
|
|
1475
1475
|
> & {
|
|
1476
1476
|
compareAtPrice?: StorefrontAPI.Maybe<
|
|
1477
1477
|
Pick<StorefrontAPI.MoneyV2, 'amount' | 'currencyCode'>
|
|
@@ -1503,7 +1503,7 @@ export type ProductVariantsFragment = {
|
|
|
1503
1503
|
nodes: Array<
|
|
1504
1504
|
Pick<
|
|
1505
1505
|
StorefrontAPI.ProductVariant,
|
|
1506
|
-
'availableForSale' | 'id' | '
|
|
1506
|
+
'availableForSale' | 'id' | 'sku' | 'title'
|
|
1507
1507
|
> & {
|
|
1508
1508
|
compareAtPrice?: StorefrontAPI.Maybe<
|
|
1509
1509
|
Pick<StorefrontAPI.MoneyV2, 'amount' | 'currencyCode'>
|
|
@@ -1539,7 +1539,7 @@ export type ProductVariantsQuery = {
|
|
|
1539
1539
|
nodes: Array<
|
|
1540
1540
|
Pick<
|
|
1541
1541
|
StorefrontAPI.ProductVariant,
|
|
1542
|
-
'availableForSale' | 'id' | '
|
|
1542
|
+
'availableForSale' | 'id' | 'sku' | 'title'
|
|
1543
1543
|
> & {
|
|
1544
1544
|
compareAtPrice?: StorefrontAPI.Maybe<
|
|
1545
1545
|
Pick<StorefrontAPI.MoneyV2, 'amount' | 'currencyCode'>
|
|
@@ -1839,11 +1839,11 @@ interface GeneratedQueryTypes {
|
|
|
1839
1839
|
return: PoliciesQuery;
|
|
1840
1840
|
variables: PoliciesQueryVariables;
|
|
1841
1841
|
};
|
|
1842
|
-
'#graphql\n query Product(\n $country: CountryCode\n $handle: String!\n $language: LanguageCode\n $selectedOptions: [SelectedOptionInput!]!\n ) @inContext(country: $country, language: $language) {\n product(handle: $handle) {\n ...Product\n }\n }\n #graphql\n fragment Product on Product {\n id\n title\n vendor\n handle\n descriptionHtml\n description\n options {\n name\n values\n }\n selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {\n ...ProductVariant\n }\n variants(first: 1) {\n nodes {\n ...ProductVariant\n }\n }\n seo {\n description\n title\n }\n }\n #graphql\n fragment ProductVariant on ProductVariant {\n availableForSale\n compareAtPrice {\n amount\n currencyCode\n }\n id\n image {\n __typename\n id\n url\n altText\n width\n height\n }\n price {\n amount\n currencyCode\n }\n product {\n title\n handle\n }\n
|
|
1842
|
+
'#graphql\n query Product(\n $country: CountryCode\n $handle: String!\n $language: LanguageCode\n $selectedOptions: [SelectedOptionInput!]!\n ) @inContext(country: $country, language: $language) {\n product(handle: $handle) {\n ...Product\n }\n }\n #graphql\n fragment Product on Product {\n id\n title\n vendor\n handle\n descriptionHtml\n description\n options {\n name\n values\n }\n selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {\n ...ProductVariant\n }\n variants(first: 1) {\n nodes {\n ...ProductVariant\n }\n }\n seo {\n description\n title\n }\n }\n #graphql\n fragment ProductVariant on ProductVariant {\n availableForSale\n compareAtPrice {\n amount\n currencyCode\n }\n id\n image {\n __typename\n id\n url\n altText\n width\n height\n }\n price {\n amount\n currencyCode\n }\n product {\n title\n handle\n }\n selectedOptions {\n name\n value\n }\n sku\n title\n unitPrice {\n amount\n currencyCode\n }\n }\n\n\n': {
|
|
1843
1843
|
return: ProductQuery;
|
|
1844
1844
|
variables: ProductQueryVariables;
|
|
1845
1845
|
};
|
|
1846
|
-
'#graphql\n #graphql\n fragment ProductVariants on Product {\n variants(first: 250) {\n nodes {\n ...ProductVariant\n }\n }\n }\n #graphql\n fragment ProductVariant on ProductVariant {\n availableForSale\n compareAtPrice {\n amount\n currencyCode\n }\n id\n image {\n __typename\n id\n url\n altText\n width\n height\n }\n price {\n amount\n currencyCode\n }\n product {\n title\n handle\n }\n
|
|
1846
|
+
'#graphql\n #graphql\n fragment ProductVariants on Product {\n variants(first: 250) {\n nodes {\n ...ProductVariant\n }\n }\n }\n #graphql\n fragment ProductVariant on ProductVariant {\n availableForSale\n compareAtPrice {\n amount\n currencyCode\n }\n id\n image {\n __typename\n id\n url\n altText\n width\n height\n }\n price {\n amount\n currencyCode\n }\n product {\n title\n handle\n }\n selectedOptions {\n name\n value\n }\n sku\n title\n unitPrice {\n amount\n currencyCode\n }\n }\n\n\n query ProductVariants(\n $country: CountryCode\n $language: LanguageCode\n $handle: String!\n ) @inContext(country: $country, language: $language) {\n product(handle: $handle) {\n ...ProductVariants\n }\n }\n': {
|
|
1847
1847
|
return: ProductVariantsQuery;
|
|
1848
1848
|
variables: ProductVariantsQueryVariables;
|
|
1849
1849
|
};
|
package/dist/lib/ast.js
ADDED