@shopify/cli-hydrogen 4.0.9 → 4.1.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.
Files changed (74) hide show
  1. package/dist/commands/hydrogen/build.d.ts +4 -4
  2. package/dist/commands/hydrogen/build.js +17 -16
  3. package/dist/commands/hydrogen/check.d.ts +3 -6
  4. package/dist/commands/hydrogen/check.js +10 -9
  5. package/dist/commands/hydrogen/dev.d.ts +3 -3
  6. package/dist/commands/hydrogen/dev.js +22 -21
  7. package/dist/commands/hydrogen/g.d.ts +10 -0
  8. package/dist/commands/hydrogen/g.js +17 -0
  9. package/dist/commands/hydrogen/generate/route.d.ts +7 -9
  10. package/dist/commands/hydrogen/generate/route.js +49 -47
  11. package/dist/commands/hydrogen/generate/route.test.js +48 -40
  12. package/dist/commands/hydrogen/generate/routes.d.ts +2 -2
  13. package/dist/commands/hydrogen/init.d.ts +3 -3
  14. package/dist/commands/hydrogen/init.js +74 -93
  15. package/dist/commands/hydrogen/init.test.js +71 -24
  16. package/dist/commands/hydrogen/preview.d.ts +2 -2
  17. package/dist/commands/hydrogen/preview.js +4 -4
  18. package/dist/commands/hydrogen/shortcut.d.ts +9 -0
  19. package/dist/commands/hydrogen/shortcut.js +74 -0
  20. package/dist/commands/hydrogen/shortcut.test.js +58 -0
  21. package/dist/generator-templates/routes/[robots.txt].tsx +35 -1
  22. package/dist/generator-templates/routes/[sitemap.xml].tsx +33 -2
  23. package/dist/generator-templates/routes/account/login.tsx +42 -13
  24. package/dist/generator-templates/routes/account/register.tsx +42 -13
  25. package/dist/generator-templates/routes/cart.tsx +42 -2
  26. package/dist/generator-templates/routes/collections/$collectionHandle.tsx +44 -5
  27. package/dist/generator-templates/routes/index.tsx +33 -0
  28. package/dist/generator-templates/routes/pages/$pageHandle.tsx +48 -10
  29. package/dist/generator-templates/routes/policies/$policyHandle.tsx +67 -14
  30. package/dist/generator-templates/routes/policies/index.tsx +54 -4
  31. package/dist/generator-templates/routes/products/$productHandle.tsx +44 -9
  32. package/dist/hooks/init.js +2 -2
  33. package/dist/{utils → lib}/check-lockfile.js +7 -4
  34. package/dist/{utils → lib}/check-lockfile.test.js +19 -28
  35. package/dist/{utils → lib}/check-version.test.js +3 -2
  36. package/dist/lib/colors.d.ts +8 -0
  37. package/dist/lib/colors.js +8 -0
  38. package/dist/{utils → lib}/config.js +9 -18
  39. package/dist/{utils → lib}/flags.d.ts +3 -3
  40. package/dist/{utils → lib}/flags.js +4 -4
  41. package/dist/{utils → lib}/mini-oxygen.js +14 -12
  42. package/dist/lib/remix-version-interop.d.ts +11 -0
  43. package/dist/lib/remix-version-interop.js +54 -0
  44. package/dist/lib/remix-version-interop.test.d.ts +1 -0
  45. package/dist/lib/remix-version-interop.test.js +93 -0
  46. package/dist/lib/shell.d.ts +12 -0
  47. package/dist/lib/shell.js +73 -0
  48. package/dist/lib/template-downloader.d.ts +6 -0
  49. package/dist/{utils → lib}/template-downloader.js +21 -16
  50. package/dist/{utils → lib}/transpile-ts.js +5 -5
  51. package/dist/lib/virtual-routes.test.d.ts +1 -0
  52. package/dist/virtual-routes/routes/index.jsx +2 -15
  53. package/dist/virtual-routes/virtual-root.jsx +5 -6
  54. package/oclif.manifest.json +1 -1
  55. package/package.json +11 -10
  56. package/dist/utils/template-downloader.d.ts +0 -11
  57. /package/dist/{utils/check-lockfile.test.d.ts → commands/hydrogen/shortcut.test.d.ts} +0 -0
  58. /package/dist/{utils → lib}/check-lockfile.d.ts +0 -0
  59. /package/dist/{utils/check-version.test.d.ts → lib/check-lockfile.test.d.ts} +0 -0
  60. /package/dist/{utils → lib}/check-version.d.ts +0 -0
  61. /package/dist/{utils → lib}/check-version.js +0 -0
  62. /package/dist/{utils/flags.test.d.ts → lib/check-version.test.d.ts} +0 -0
  63. /package/dist/{utils → lib}/config.d.ts +0 -0
  64. /package/dist/{utils/virtual-routes.test.d.ts → lib/flags.test.d.ts} +0 -0
  65. /package/dist/{utils → lib}/flags.test.js +0 -0
  66. /package/dist/{utils → lib}/log.d.ts +0 -0
  67. /package/dist/{utils → lib}/log.js +0 -0
  68. /package/dist/{utils → lib}/mini-oxygen.d.ts +0 -0
  69. /package/dist/{utils → lib}/missing-routes.d.ts +0 -0
  70. /package/dist/{utils → lib}/missing-routes.js +0 -0
  71. /package/dist/{utils → lib}/transpile-ts.d.ts +0 -0
  72. /package/dist/{utils → lib}/virtual-routes.d.ts +0 -0
  73. /package/dist/{utils → lib}/virtual-routes.js +0 -0
  74. /package/dist/{utils → lib}/virtual-routes.test.js +0 -0
@@ -1,12 +1,15 @@
1
1
  import {
2
- type MetaFunction,
3
2
  type ActionFunction,
4
3
  type LoaderArgs,
4
+ type ErrorBoundaryComponent,
5
5
  redirect,
6
- json,
7
6
  } from '@shopify/remix-oxygen';
8
- import {Form, Link, useActionData, useLoaderData} from '@remix-run/react';
9
- import {useState} from 'react';
7
+ import {
8
+ Form,
9
+ useCatch,
10
+ useRouteError,
11
+ isRouteErrorResponse,
12
+ } from '@remix-run/react';
10
13
 
11
14
  export async function loader({context, params}: LoaderArgs) {
12
15
  const customerAccessToken = await context.session.get('customerAccessToken');
@@ -14,15 +17,11 @@ export async function loader({context, params}: LoaderArgs) {
14
17
  if (customerAccessToken) {
15
18
  return redirect(params.lang ? `${params.lang}/account` : '/account');
16
19
  }
17
- }
18
20
 
19
- type ActionData = {
20
- formError?: string;
21
- };
22
-
23
- const badRequest = (data: ActionData) => json(data, {status: 400});
21
+ return new Response(null);
22
+ }
24
23
 
25
- export const action: ActionFunction = async ({request, context, params}) => {
24
+ export const action: ActionFunction = async ({request}) => {
26
25
  const formData = await request.formData();
27
26
 
28
27
  const email = formData.get('email');
@@ -34,8 +33,8 @@ export const action: ActionFunction = async ({request, context, params}) => {
34
33
  typeof email !== 'string' ||
35
34
  typeof password !== 'string'
36
35
  ) {
37
- return badRequest({
38
- formError: 'Please provide both an email and a password.',
36
+ throw new Response('Please provide both an email and a password.', {
37
+ status: 400,
39
38
  });
40
39
  }
41
40
 
@@ -72,3 +71,33 @@ export default function Login() {
72
71
  </Form>
73
72
  );
74
73
  }
74
+
75
+ export const ErrorBoundaryV1: ErrorBoundaryComponent = ({error}) => {
76
+ console.error(error);
77
+
78
+ return <div>There was an error.</div>;
79
+ };
80
+
81
+ export function CatchBoundary() {
82
+ const caught = useCatch();
83
+ console.error(caught);
84
+
85
+ return (
86
+ <div>
87
+ There was an error. Status: {caught.status}. Message:{' '}
88
+ {caught.data?.message}
89
+ </div>
90
+ );
91
+ }
92
+
93
+ export function ErrorBoundary() {
94
+ const error = useRouteError();
95
+
96
+ if (isRouteErrorResponse(error)) {
97
+ console.error(error.status, error.statusText, error.data);
98
+ return <div>Route Error</div>;
99
+ } else {
100
+ console.error((error as Error).message);
101
+ return <div>Thrown Error</div>;
102
+ }
103
+ }
@@ -1,12 +1,15 @@
1
1
  import {
2
- type MetaFunction,
3
2
  type ActionFunction,
4
3
  type LoaderArgs,
4
+ type ErrorBoundaryComponent,
5
5
  redirect,
6
- json,
7
6
  } from '@shopify/remix-oxygen';
8
- import {Form, Link, useActionData, useLoaderData} from '@remix-run/react';
9
- import {useState} from 'react';
7
+ import {
8
+ Form,
9
+ useCatch,
10
+ useRouteError,
11
+ isRouteErrorResponse,
12
+ } from '@remix-run/react';
10
13
 
11
14
  export async function loader({context, params}: LoaderArgs) {
12
15
  const customerAccessToken = await context.session.get('customerAccessToken');
@@ -14,15 +17,11 @@ export async function loader({context, params}: LoaderArgs) {
14
17
  if (customerAccessToken) {
15
18
  return redirect(params.lang ? `${params.lang}/account` : '/account');
16
19
  }
17
- }
18
20
 
19
- type ActionData = {
20
- formError?: string;
21
- };
22
-
23
- const badRequest = (data: ActionData) => json(data, {status: 400});
21
+ return new Response(null);
22
+ }
24
23
 
25
- export const action: ActionFunction = async ({request, context, params}) => {
24
+ export const action: ActionFunction = async ({request}) => {
26
25
  const formData = await request.formData();
27
26
 
28
27
  const email = formData.get('email');
@@ -34,8 +33,8 @@ export const action: ActionFunction = async ({request, context, params}) => {
34
33
  typeof email !== 'string' ||
35
34
  typeof password !== 'string'
36
35
  ) {
37
- return badRequest({
38
- formError: 'Please provide both an email and a password.',
36
+ throw new Response('Please provide both an email and a password.', {
37
+ status: 404,
39
38
  });
40
39
  }
41
40
 
@@ -72,3 +71,33 @@ export default function Register() {
72
71
  </Form>
73
72
  );
74
73
  }
74
+
75
+ export const ErrorBoundaryV1: ErrorBoundaryComponent = ({error}) => {
76
+ console.error(error);
77
+
78
+ return <div>There was an error.</div>;
79
+ };
80
+
81
+ export function CatchBoundary() {
82
+ const caught = useCatch();
83
+ console.error(caught);
84
+
85
+ return (
86
+ <div>
87
+ There was an error. Status: {caught.status}. Message:{' '}
88
+ {caught.data?.message}
89
+ </div>
90
+ );
91
+ }
92
+
93
+ export function ErrorBoundary() {
94
+ const error = useRouteError();
95
+
96
+ if (isRouteErrorResponse(error)) {
97
+ console.error(error.status, error.statusText, error.data);
98
+ return <div>Route Error</div>;
99
+ } else {
100
+ console.error((error as Error).message);
101
+ return <div>Thrown Error</div>;
102
+ }
103
+ }
@@ -1,7 +1,14 @@
1
- import {Await, useMatches} from '@remix-run/react';
1
+ import {
2
+ Await,
3
+ useMatches,
4
+ useCatch,
5
+ useRouteError,
6
+ isRouteErrorResponse,
7
+ } from '@remix-run/react';
2
8
  import {Suspense} from 'react';
3
9
  import {flattenConnection} from '@shopify/hydrogen';
4
10
  import type {Cart as CartType} from '@shopify/hydrogen/storefront-api-types';
11
+ import {type ErrorBoundaryComponent} from '@shopify/remix-oxygen';
5
12
 
6
13
  export async function action() {
7
14
  // @TODO implement cart action
@@ -11,7 +18,10 @@ export default function CartRoute() {
11
18
  const [root] = useMatches();
12
19
  return (
13
20
  <Suspense fallback="loading">
14
- <Await resolve={root.data?.cart as CartType}>
21
+ <Await
22
+ resolve={root.data?.cart as CartType}
23
+ errorElement={<div>An error occurred</div>}
24
+ >
15
25
  {(cart) => {
16
26
  const linesCount = Boolean(cart?.lines?.edges?.length || 0);
17
27
  if (!linesCount) {
@@ -39,3 +49,33 @@ export default function CartRoute() {
39
49
  </Suspense>
40
50
  );
41
51
  }
52
+
53
+ export const ErrorBoundaryV1: ErrorBoundaryComponent = ({error}) => {
54
+ console.error(error);
55
+
56
+ return <div>There was an error.</div>;
57
+ };
58
+
59
+ export function CatchBoundary() {
60
+ const caught = useCatch();
61
+ console.error(caught);
62
+
63
+ return (
64
+ <div>
65
+ There was an error. Status: {caught.status}. Message:{' '}
66
+ {caught.data?.message}
67
+ </div>
68
+ );
69
+ }
70
+
71
+ export function ErrorBoundary() {
72
+ const error = useRouteError();
73
+
74
+ if (isRouteErrorResponse(error)) {
75
+ console.error(error.status, error.statusText, error.data);
76
+ return <div>Route Error</div>;
77
+ } else {
78
+ console.error((error as Error).message);
79
+ return <div>Thrown Error</div>;
80
+ }
81
+ }
@@ -1,9 +1,18 @@
1
- import {json, type LoaderArgs} from '@shopify/remix-oxygen';
2
- import {useLoaderData} from '@remix-run/react';
1
+ import {
2
+ json,
3
+ type LoaderArgs,
4
+ type ErrorBoundaryComponent,
5
+ } from '@shopify/remix-oxygen';
6
+ import {
7
+ useLoaderData,
8
+ Link,
9
+ useCatch,
10
+ useRouteError,
11
+ isRouteErrorResponse,
12
+ } from '@remix-run/react';
3
13
  import type {Collection as CollectionType} from '@shopify/hydrogen/storefront-api-types';
4
- import {Link} from '@remix-run/react';
5
14
 
6
- export async function loader({params, request, context}: LoaderArgs) {
15
+ export async function loader({params, context}: LoaderArgs) {
7
16
  const {collectionHandle} = params;
8
17
 
9
18
  const {collection} = await context.storefront.query<{
@@ -38,8 +47,38 @@ export default function Collection() {
38
47
  );
39
48
  }
40
49
 
50
+ export const ErrorBoundaryV1: ErrorBoundaryComponent = ({error}) => {
51
+ console.error(error);
52
+
53
+ return <div>There was an error.</div>;
54
+ };
55
+
56
+ export function CatchBoundary() {
57
+ const caught = useCatch();
58
+ console.error(caught);
59
+
60
+ return (
61
+ <div>
62
+ There was an error. Status: {caught.status}. Message:{' '}
63
+ {caught.data?.message}
64
+ </div>
65
+ );
66
+ }
67
+
68
+ export function ErrorBoundary() {
69
+ const error = useRouteError();
70
+
71
+ if (isRouteErrorResponse(error)) {
72
+ console.error(error.status, error.statusText, error.data);
73
+ return <div>Route Error</div>;
74
+ } else {
75
+ console.error((error as Error).message);
76
+ return <div>Thrown Error</div>;
77
+ }
78
+ }
79
+
41
80
  const COLLECTION_QUERY = `#graphql
42
- query CollectionDetails(
81
+ query collection_details(
43
82
  $handle: String!
44
83
  $country: CountryCode
45
84
  $language: LanguageCode
@@ -1,3 +1,6 @@
1
+ import {type ErrorBoundaryComponent} from '@shopify/remix-oxygen';
2
+ import {useCatch, useRouteError, isRouteErrorResponse} from '@remix-run/react';
3
+
1
4
  export default function Index() {
2
5
  return (
3
6
  <p>
@@ -5,3 +8,33 @@ export default function Index() {
5
8
  </p>
6
9
  );
7
10
  }
11
+
12
+ export const ErrorBoundaryV1: ErrorBoundaryComponent = ({error}) => {
13
+ console.error(error);
14
+
15
+ return <div>There was an error.</div>;
16
+ };
17
+
18
+ export function CatchBoundary() {
19
+ const caught = useCatch();
20
+ console.error(caught);
21
+
22
+ return (
23
+ <div>
24
+ There was an error. Status: {caught.status}. Message:{' '}
25
+ {caught.data?.message}
26
+ </div>
27
+ );
28
+ }
29
+
30
+ export function ErrorBoundary() {
31
+ const error = useRouteError();
32
+
33
+ if (isRouteErrorResponse(error)) {
34
+ console.error(error.status, error.statusText, error.data);
35
+ return <div>Route Error</div>;
36
+ } else {
37
+ console.error((error as Error).message);
38
+ return <div>Thrown Error</div>;
39
+ }
40
+ }
@@ -2,15 +2,22 @@ import {
2
2
  json,
3
3
  type MetaFunction,
4
4
  type LoaderArgs,
5
- SerializeFrom,
5
+ type ErrorBoundaryComponent,
6
6
  } from '@shopify/remix-oxygen';
7
- import {useLoaderData} from '@remix-run/react';
8
- import invariant from 'tiny-invariant';
7
+ import {
8
+ useLoaderData,
9
+ type V2_MetaFunction,
10
+ useCatch,
11
+ useRouteError,
12
+ isRouteErrorResponse,
13
+ } from '@remix-run/react';
9
14
  import type {Page as PageType} from '@shopify/hydrogen/storefront-api-types';
10
15
  import type {SeoHandleFunction} from '@shopify/hydrogen';
11
16
 
12
17
  export async function loader({params, context}: LoaderArgs) {
13
- invariant(params.pageHandle, 'Missing page handle');
18
+ if (!params.pageHandle) {
19
+ throw new Error('Missing page handle');
20
+ }
14
21
 
15
22
  const {page} = await context.storefront.query<{page: PageType}>(PAGE_QUERY, {
16
23
  variables: {
@@ -36,13 +43,14 @@ export const handle = {
36
43
  seo,
37
44
  };
38
45
 
39
- export const meta: MetaFunction = ({data}) => {
46
+ export const metaV1: MetaFunction = ({data}) => {
40
47
  const {title, description} = data?.page.seo ?? {};
48
+ return {title, description};
49
+ };
41
50
 
42
- return {
43
- title,
44
- description,
45
- };
51
+ export const meta: V2_MetaFunction = ({data}) => {
52
+ const {title, description} = data?.page.seo ?? {};
53
+ return [{title}, {name: 'description', content: description}];
46
54
  };
47
55
 
48
56
  export default function Page() {
@@ -58,8 +66,38 @@ export default function Page() {
58
66
  );
59
67
  }
60
68
 
69
+ export const ErrorBoundaryV1: ErrorBoundaryComponent = ({error}) => {
70
+ console.error(error);
71
+
72
+ return <div>There was an error.</div>;
73
+ };
74
+
75
+ export function CatchBoundary() {
76
+ const caught = useCatch();
77
+ console.error(caught);
78
+
79
+ return (
80
+ <div>
81
+ There was an error. Status: {caught.status}. Message:{' '}
82
+ {caught.data?.message}
83
+ </div>
84
+ );
85
+ }
86
+
87
+ export function ErrorBoundary() {
88
+ const error = useRouteError();
89
+
90
+ if (isRouteErrorResponse(error)) {
91
+ console.error(error.status, error.statusText, error.data);
92
+ return <div>Route Error</div>;
93
+ } else {
94
+ console.error((error as Error).message);
95
+ return <div>Thrown Error</div>;
96
+ }
97
+ }
98
+
61
99
  const PAGE_QUERY = `#graphql
62
- query PageDetails($language: LanguageCode, $handle: String!)
100
+ query page_details($language: LanguageCode, $handle: String!)
63
101
  @inContext(language: $language) {
64
102
  page(handle: $handle) {
65
103
  id
@@ -1,21 +1,31 @@
1
- import {json, type MetaFunction, type LoaderArgs} from '@shopify/remix-oxygen';
2
- import {useLoaderData} from '@remix-run/react';
1
+ import {
2
+ json,
3
+ type MetaFunction,
4
+ type LoaderArgs,
5
+ type ErrorBoundaryComponent,
6
+ } from '@shopify/remix-oxygen';
7
+ import {
8
+ useLoaderData,
9
+ type V2_MetaFunction,
10
+ useCatch,
11
+ useRouteError,
12
+ isRouteErrorResponse,
13
+ } from '@remix-run/react';
14
+ import {Shop} from '@shopify/hydrogen/storefront-api-types';
3
15
 
4
- import {ShopPolicy} from '@shopify/hydrogen/storefront-api-types';
5
-
6
- export async function loader({request, params, context}: LoaderArgs) {
16
+ export async function loader({params, context}: LoaderArgs) {
7
17
  const handle = params.policyHandle;
8
18
 
9
19
  if (!handle) {
10
- throw new Response(null, {status: 404});
20
+ throw new Response('No handle was passed in', {status: 404});
11
21
  }
12
22
 
13
23
  const policyName = handle.replace(/-([a-z])/g, (_: unknown, m1: string) =>
14
24
  m1.toUpperCase(),
15
- );
25
+ ) as SelectedPolicies;
16
26
 
17
27
  const data = await context.storefront.query<{
18
- shop: Record<string, ShopPolicy>;
28
+ shop: Pick<Shop, SelectedPolicies>;
19
29
  }>(POLICY_CONTENT_QUERY, {
20
30
  variables: {
21
31
  privacyPolicy: false,
@@ -30,16 +40,20 @@ export async function loader({request, params, context}: LoaderArgs) {
30
40
  const policy = data.shop?.[policyName];
31
41
 
32
42
  if (!policy) {
33
- throw new Response(null, {status: 404});
43
+ throw new Response('Could not find the policy', {status: 404});
34
44
  }
35
45
 
36
46
  return json({policy});
37
47
  }
38
48
 
39
- export const meta: MetaFunction<typeof loader> = ({data}) => {
40
- return {
41
- title: data?.policy?.title ?? 'Policies',
42
- };
49
+ export const metaV1: MetaFunction<typeof loader> = ({data}) => {
50
+ const title = data?.policy?.title ?? 'Policies';
51
+ return {title};
52
+ };
53
+
54
+ export const meta: V2_MetaFunction<typeof loader> = ({data}) => {
55
+ const title = data?.policy?.title ?? 'Policies';
56
+ return [{title}];
43
57
  };
44
58
 
45
59
  export default function Policies() {
@@ -53,6 +67,36 @@ export default function Policies() {
53
67
  );
54
68
  }
55
69
 
70
+ export const ErrorBoundaryV1: ErrorBoundaryComponent = ({error}) => {
71
+ console.error(error);
72
+
73
+ return <div>There was an error.</div>;
74
+ };
75
+
76
+ export function CatchBoundary() {
77
+ const caught = useCatch();
78
+ console.error(caught);
79
+
80
+ return (
81
+ <div>
82
+ There was an error. Status: {caught.status}. Message:{' '}
83
+ {caught.data?.message}
84
+ </div>
85
+ );
86
+ }
87
+
88
+ export function ErrorBoundary() {
89
+ const error = useRouteError();
90
+
91
+ if (isRouteErrorResponse(error)) {
92
+ console.error(error.status, error.statusText, error.data);
93
+ return <div>Route Error</div>;
94
+ } else {
95
+ console.error((error as Error).message);
96
+ return <div>Thrown Error</div>;
97
+ }
98
+ }
99
+
56
100
  const POLICY_CONTENT_QUERY = `#graphql
57
101
  fragment Policy on ShopPolicy {
58
102
  body
@@ -62,7 +106,7 @@ const POLICY_CONTENT_QUERY = `#graphql
62
106
  url
63
107
  }
64
108
 
65
- query PoliciesQuery(
109
+ query policy_query(
66
110
  $language: LanguageCode
67
111
  $privacyPolicy: Boolean!
68
112
  $shippingPolicy: Boolean!
@@ -85,3 +129,12 @@ const POLICY_CONTENT_QUERY = `#graphql
85
129
  }
86
130
  }
87
131
  `;
132
+
133
+ const policies = [
134
+ 'privacyPolicy',
135
+ 'shippingPolicy',
136
+ 'refundPolicy',
137
+ 'termsOfService',
138
+ ] as const;
139
+
140
+ type SelectedPolicies = (typeof policies)[number];
@@ -1,10 +1,20 @@
1
- import {json, type LoaderArgs} from '@shopify/remix-oxygen';
2
- import {useLoaderData, Link} from '@remix-run/react';
3
- import type {ShopPolicy} from '@shopify/hydrogen/storefront-api-types';
1
+ import {
2
+ json,
3
+ type LoaderArgs,
4
+ type ErrorBoundaryComponent,
5
+ } from '@shopify/remix-oxygen';
6
+ import {
7
+ useLoaderData,
8
+ Link,
9
+ useCatch,
10
+ useRouteError,
11
+ isRouteErrorResponse,
12
+ } from '@remix-run/react';
13
+ import type {Shop} from '@shopify/hydrogen/storefront-api-types';
4
14
 
5
15
  export async function loader({context: {storefront}}: LoaderArgs) {
6
16
  const data = await storefront.query<{
7
- shop: Record<string, ShopPolicy>;
17
+ shop: Pick<Shop, SelectedPolicies>;
8
18
  }>(POLICIES_QUERY);
9
19
 
10
20
  const policies = Object.values(data.shop || {});
@@ -36,6 +46,36 @@ export default function Policies() {
36
46
  );
37
47
  }
38
48
 
49
+ export const ErrorBoundaryV1: ErrorBoundaryComponent = ({error}) => {
50
+ console.error(error);
51
+
52
+ return <div>There was an error.</div>;
53
+ };
54
+
55
+ export function CatchBoundary() {
56
+ const caught = useCatch();
57
+ console.error(caught);
58
+
59
+ return (
60
+ <div>
61
+ There was an error. Status: {caught.status}. Message:{' '}
62
+ {caught.data?.message}
63
+ </div>
64
+ );
65
+ }
66
+
67
+ export function ErrorBoundary() {
68
+ const error = useRouteError();
69
+
70
+ if (isRouteErrorResponse(error)) {
71
+ console.error(error.status, error.statusText, error.data);
72
+ return <div>Route Error</div>;
73
+ } else {
74
+ console.error((error as Error).message);
75
+ return <div>Thrown Error</div>;
76
+ }
77
+ }
78
+
39
79
  const POLICIES_QUERY = `#graphql
40
80
  fragment Policy on ShopPolicy {
41
81
  id
@@ -65,3 +105,13 @@ const POLICIES_QUERY = `#graphql
65
105
  }
66
106
  }
67
107
  `;
108
+
109
+ const policies = [
110
+ 'privacyPolicy',
111
+ 'shippingPolicy',
112
+ 'refundPolicy',
113
+ 'termsOfService',
114
+ 'subscriptionPolicy',
115
+ ] as const;
116
+
117
+ type SelectedPolicies = (typeof policies)[number];
@@ -1,15 +1,21 @@
1
- import {defer, type LoaderArgs} from '@shopify/remix-oxygen';
2
- import {useLoaderData} from '@remix-run/react';
3
- import type {
4
- ProductVariant,
5
- Product as ProductType,
6
- } from '@shopify/hydrogen/storefront-api-types';
1
+ import {
2
+ defer,
3
+ type LoaderArgs,
4
+ type ErrorBoundaryComponent,
5
+ } from '@shopify/remix-oxygen';
6
+ import {
7
+ useLoaderData,
8
+ useCatch,
9
+ useRouteError,
10
+ isRouteErrorResponse,
11
+ } from '@remix-run/react';
12
+ import type {Product as ProductType} from '@shopify/hydrogen/storefront-api-types';
7
13
 
8
14
  export async function loader({params, context}: LoaderArgs) {
9
15
  const {productHandle} = params;
10
16
 
11
17
  const {product} = await context.storefront.query<{
12
- product: ProductType & {selectedVariant?: ProductVariant};
18
+ product: Pick<ProductType, 'id' | 'title' | 'descriptionHtml' | 'vendor'>;
13
19
  }>(PRODUCT_QUERY, {
14
20
  variables: {
15
21
  handle: productHandle,
@@ -40,9 +46,38 @@ export default function Product() {
40
46
  );
41
47
  }
42
48
 
43
- const PRODUCT_QUERY = `#graphql
49
+ export const ErrorBoundaryV1: ErrorBoundaryComponent = ({error}) => {
50
+ console.error(error);
51
+
52
+ return <div>There was an error.</div>;
53
+ };
54
+
55
+ export function CatchBoundary() {
56
+ const caught = useCatch();
57
+ console.error(caught);
58
+
59
+ return (
60
+ <div>
61
+ There was an error. Status: {caught.status}. Message:{' '}
62
+ {caught.data?.message}
63
+ </div>
64
+ );
65
+ }
66
+
67
+ export function ErrorBoundary() {
68
+ const error = useRouteError();
69
+
70
+ if (isRouteErrorResponse(error)) {
71
+ console.error(error.status, error.statusText, error.data);
72
+ return <div>Route Error</div>;
73
+ } else {
74
+ console.error((error as Error).message);
75
+ return <div>Thrown Error</div>;
76
+ }
77
+ }
44
78
 
45
- query Product(
79
+ const PRODUCT_QUERY = `#graphql
80
+ query product_query(
46
81
  $country: CountryCode
47
82
  $language: LanguageCode
48
83
  $handle: String!
@@ -1,10 +1,10 @@
1
1
  import { spawnSync } from 'child_process';
2
- import { output } from '@shopify/cli-kit';
2
+ import { outputDebug } from '@shopify/cli-kit/node/output';
3
3
 
4
4
  const EXPERIMENTAL_VM_MODULES_FLAG = "--experimental-vm-modules";
5
5
  const hook = async function(options) {
6
6
  if (options.id && ["hydrogen:dev", "hydrogen:preview"].includes(options.id) && !process.execArgv.includes(EXPERIMENTAL_VM_MODULES_FLAG) && !(process.env.NODE_OPTIONS ?? "").includes(EXPERIMENTAL_VM_MODULES_FLAG)) {
7
- output.debug(
7
+ outputDebug(
8
8
  `Restarting CLI process with ${EXPERIMENTAL_VM_MODULES_FLAG} flag.`
9
9
  );
10
10
  const [command, ...args] = process.argv;