@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.
Files changed (71) hide show
  1. package/dist/commands/hydrogen/build.js +10 -5
  2. package/dist/commands/hydrogen/check.js +1 -1
  3. package/dist/commands/hydrogen/codegen-unstable.js +3 -3
  4. package/dist/commands/hydrogen/dev.js +26 -17
  5. package/dist/commands/hydrogen/init.js +3 -0
  6. package/dist/commands/hydrogen/init.test.js +199 -51
  7. package/dist/commands/hydrogen/preview.js +4 -3
  8. package/dist/commands/hydrogen/setup.js +5 -2
  9. package/dist/commands/hydrogen/setup.test.js +62 -0
  10. package/dist/generator-templates/starter/app/components/Footer.tsx +1 -1
  11. package/dist/generator-templates/starter/app/components/Header.tsx +1 -1
  12. package/dist/generator-templates/starter/app/components/Search.tsx +3 -3
  13. package/dist/generator-templates/starter/app/root.tsx +24 -1
  14. package/dist/generator-templates/starter/app/routes/$.tsx +4 -0
  15. package/dist/generator-templates/starter/app/routes/_index.tsx +6 -2
  16. package/dist/generator-templates/starter/app/routes/account.$.tsx +1 -2
  17. package/dist/generator-templates/starter/app/routes/account.addresses.tsx +1 -1
  18. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +2 -7
  19. package/dist/generator-templates/starter/app/routes/account.profile.tsx +7 -2
  20. package/dist/generator-templates/starter/app/routes/account.tsx +4 -3
  21. package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +6 -2
  22. package/dist/generator-templates/starter/app/routes/account_.login.tsx +6 -2
  23. package/dist/generator-templates/starter/app/routes/account_.logout.tsx +2 -6
  24. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +1 -2
  25. package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +1 -2
  26. package/dist/generator-templates/starter/app/routes/blogs._index.tsx +1 -2
  27. package/dist/generator-templates/starter/app/routes/cart.tsx +1 -2
  28. package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +1 -2
  29. package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +1 -2
  30. package/dist/generator-templates/starter/app/routes/policies.$handle.tsx +2 -3
  31. package/dist/generator-templates/starter/app/routes/products.$handle.tsx +23 -15
  32. package/dist/generator-templates/starter/app/routes/search.tsx +1 -2
  33. package/dist/generator-templates/starter/package.json +4 -4
  34. package/dist/generator-templates/starter/remix.config.js +1 -0
  35. package/dist/generator-templates/starter/storefrontapi.generated.d.ts +9 -9
  36. package/dist/lib/ast.js +9 -0
  37. package/dist/lib/check-version.test.js +1 -0
  38. package/dist/lib/codegen.js +17 -7
  39. package/dist/lib/environment-variables.js +15 -11
  40. package/dist/lib/file.test.js +4 -5
  41. package/dist/lib/find-port.js +9 -0
  42. package/dist/lib/flags.js +3 -2
  43. package/dist/lib/format-code.js +3 -0
  44. package/dist/lib/live-reload.js +62 -0
  45. package/dist/lib/log.js +6 -1
  46. package/dist/lib/mini-oxygen.js +28 -18
  47. package/dist/lib/missing-routes.js +17 -1
  48. package/dist/lib/onboarding/common.js +5 -0
  49. package/dist/lib/onboarding/local.js +21 -8
  50. package/dist/lib/onboarding/remote.js +8 -3
  51. package/dist/lib/remix-config.js +2 -0
  52. package/dist/lib/remix-version-check.test.js +1 -0
  53. package/dist/lib/setups/css/replacers.js +7 -4
  54. package/dist/lib/setups/i18n/replacers.js +7 -5
  55. package/dist/lib/setups/routes/generate.js +4 -1
  56. package/dist/lib/setups/routes/generate.test.js +10 -11
  57. package/dist/lib/template-downloader.js +4 -0
  58. package/dist/lib/transpile-ts.js +1 -0
  59. package/dist/lib/virtual-routes.js +4 -1
  60. package/dist/virtual-routes/components/HydrogenLogoBaseBW.jsx +26 -4
  61. package/dist/virtual-routes/components/HydrogenLogoBaseColor.jsx +40 -10
  62. package/dist/virtual-routes/components/IconBanner.jsx +286 -44
  63. package/dist/virtual-routes/components/IconDiscord.jsx +17 -1
  64. package/dist/virtual-routes/components/IconError.jsx +55 -17
  65. package/dist/virtual-routes/components/IconGithub.jsx +19 -1
  66. package/dist/virtual-routes/components/IconTwitter.jsx +17 -1
  67. package/dist/virtual-routes/components/Layout.jsx +1 -1
  68. package/dist/virtual-routes/routes/index.jsx +110 -94
  69. package/dist/virtual-routes/virtual-root.jsx +7 -15
  70. package/oclif.manifest.json +1 -1
  71. 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
+ });
@@ -93,7 +93,7 @@ function activeLinkStyle({
93
93
  isPending: boolean;
94
94
  }) {
95
95
  return {
96
- fontWeight: isActive ? 'bold' : '',
96
+ fontWeight: isActive ? 'bold' : undefined,
97
97
  color: isPending ? 'grey' : 'white',
98
98
  };
99
99
  }
@@ -172,7 +172,7 @@ function activeLinkStyle({
172
172
  isPending: boolean;
173
173
  }) {
174
174
  return {
175
- fontWeight: isActive ? 'bold' : '',
175
+ fontWeight: isActive ? 'bold' : undefined,
176
176
  color: isPending ? 'grey' : 'black',
177
177
  };
178
178
  }
@@ -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-react/storefront-api-types';
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
  );
@@ -5,3 +5,7 @@ export async function loader({request}: LoaderArgs) {
5
5
  status: 404,
6
6
  });
7
7
  }
8
+
9
+ export default function CatchAllPage() {
10
+ return null;
11
+ }
@@ -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 {Await, useLoaderData, Link} from '@remix-run/react';
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 {LoaderArgs} from '@shopify/remix-oxygen';
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 type {ActionArgs, LoaderArgs} from '@shopify/remix-oxygen';
4
- import {json, redirect, type V2_MetaFunction} from '@shopify/remix-oxygen';
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
- <AcccountMenu />
112
+ <AccountMenu />
113
113
  {children}
114
114
  </div>
115
115
  );
116
116
  }
117
117
 
118
- function AcccountMenu() {
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}>
@@ -1,5 +1,9 @@
1
- import type {ActionArgs, LoaderArgs} from '@shopify/remix-oxygen';
2
- import {json, redirect} from '@shopify/remix-oxygen';
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 {Form, Link, useActionData} from '@remix-run/react';
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
- json,
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-react/storefront-api-types';
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 type {FetcherWithComponents} from '@remix-run/react';
5
- import {Await, Link, useLoaderData} from '@remix-run/react';
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 {CartLineInput} from '@shopify/hydrogen/storefront-api-types';
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) => option.name === 'Title' && option.value === 'Default Title',
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.1.1",
19
- "@shopify/hydrogen": "^2023.7.1",
20
- "@shopify/remix-oxygen": "^1.1.2",
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.2",
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",
@@ -17,6 +17,7 @@ module.exports = {
17
17
  serverPlatform: 'neutral',
18
18
  serverMinify: process.env.NODE_ENV === 'production',
19
19
  future: {
20
+ v2_dev: true,
20
21
  v2_meta: true,
21
22
  v2_headers: true,
22
23
  v2_errorBoundary: true,
@@ -1350,7 +1350,7 @@ export type PoliciesQuery = {
1350
1350
 
1351
1351
  export type ProductVariantFragment = Pick<
1352
1352
  StorefrontAPI.ProductVariant,
1353
- 'availableForSale' | 'id' | 'quantityAvailable' | 'sku' | 'title'
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' | 'quantityAvailable' | 'sku' | 'title'
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' | 'quantityAvailable' | 'sku' | 'title'
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' | 'quantityAvailable' | 'sku' | 'title'
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' | 'quantityAvailable' | 'sku' | 'title'
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' | 'quantityAvailable' | 'sku' | 'title'
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' | 'quantityAvailable' | 'sku' | 'title'
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 quantityAvailable\n selectedOptions {\n name\n value\n }\n sku\n title\n unitPrice {\n amount\n currencyCode\n }\n }\n\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 quantityAvailable\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': {
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
  };
@@ -0,0 +1,9 @@
1
+ async function importLangAstGrep(lang) {
2
+ const astGrep = await import('@ast-grep/napi');
3
+ if (!(lang in astGrep)) {
4
+ throw new Error(`Wrong language for AST: ${lang}`);
5
+ }
6
+ return astGrep[lang];
7
+ }
8
+
9
+ export { importLangAstGrep };
@@ -34,6 +34,7 @@ describe("checkHydrogenVersion()", () => {
34
34
  await checkHydrogenVersion("dir");
35
35
  expect(checkForNewVersion).toHaveBeenCalledWith(
36
36
  "@shopify/hydrogen",
37
+ // Calver
37
38
  expect.stringMatching(/20\d{2}\.\d{1,2}\.\d{1,3}/)
38
39
  );
39
40
  });