@shopify/cli-hydrogen 7.0.0 → 7.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 (40) hide show
  1. package/dist/commands/hydrogen/build.js +3 -8
  2. package/dist/commands/hydrogen/deploy.js +79 -32
  3. package/dist/commands/hydrogen/deploy.test.js +162 -5
  4. package/dist/commands/hydrogen/generate/route.js +6 -1
  5. package/dist/commands/hydrogen/init.test.js +2 -1
  6. package/dist/commands/hydrogen/setup.js +17 -19
  7. package/dist/generator-templates/starter/CHANGELOG.md +45 -0
  8. package/dist/generator-templates/starter/README.md +25 -2
  9. package/dist/generator-templates/starter/app/components/Cart.tsx +2 -2
  10. package/dist/generator-templates/starter/app/components/Layout.tsx +9 -1
  11. package/dist/generator-templates/starter/app/components/Search.tsx +44 -15
  12. package/dist/generator-templates/starter/app/lib/search.ts +29 -0
  13. package/dist/generator-templates/starter/app/root.tsx +0 -2
  14. package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +2 -2
  15. package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +1 -21
  16. package/dist/generator-templates/starter/app/routes/cart.tsx +1 -5
  17. package/dist/generator-templates/starter/app/routes/search.tsx +8 -2
  18. package/dist/generator-templates/starter/app/styles/app.css +10 -15
  19. package/dist/generator-templates/starter/package.json +7 -7
  20. package/dist/generator-templates/starter/remix.config.js +1 -0
  21. package/dist/generator-templates/starter/remix.env.d.ts +6 -2
  22. package/dist/generator-templates/starter/server.ts +1 -0
  23. package/dist/hooks/init.js +3 -3
  24. package/dist/lib/codegen.js +1 -8
  25. package/dist/lib/file.js +4 -1
  26. package/dist/lib/flags.js +6 -0
  27. package/dist/lib/graphiql-url.js +3 -0
  28. package/dist/lib/mini-oxygen/assets.js +17 -1
  29. package/dist/lib/onboarding/common.js +4 -3
  30. package/dist/lib/onboarding/local.js +7 -7
  31. package/dist/lib/onboarding/remote.js +2 -1
  32. package/dist/lib/setups/i18n/replacers.test.js +3 -2
  33. package/dist/lib/setups/routes/generate.js +58 -10
  34. package/dist/lib/setups/routes/templates/locale-check.js +9 -0
  35. package/dist/lib/setups/routes/templates/locale-check.ts +16 -0
  36. package/dist/lib/shell.js +1 -1
  37. package/dist/lib/template-diff.js +13 -3
  38. package/dist/virtual-routes/components/RequestDetails.jsx +2 -2
  39. package/oclif.manifest.json +47 -8
  40. package/package.json +5 -5
@@ -15,7 +15,7 @@ export function CartMain({layout, cart}: CartMainProps) {
15
15
  const linesCount = Boolean(cart?.lines?.nodes?.length || 0);
16
16
  const withDiscount =
17
17
  cart &&
18
- Boolean(cart.discountCodes.filter((code) => code.applicable).length);
18
+ Boolean(cart?.discountCodes?.filter((code) => code.applicable)?.length);
19
19
  const className = `cart-main ${withDiscount ? 'with-discount' : ''}`;
20
20
 
21
21
  return (
@@ -179,7 +179,7 @@ function CartLineQuantity({line}: {line: CartLine}) {
179
179
  const nextQuantity = Number((quantity + 1).toFixed(0));
180
180
 
181
181
  return (
182
- <div className="cart-line-quantiy">
182
+ <div className="cart-line-quantity">
183
183
  <small>Quantity: {quantity} &nbsp;&nbsp;</small>
184
184
  <CartLineUpdateButton lines={[{id: lineId, quantity: prevQuantity}]}>
185
185
  <button
@@ -76,7 +76,15 @@ function SearchAside() {
76
76
  type="search"
77
77
  />
78
78
  &nbsp;
79
- <button type="submit">Search</button>
79
+ <button
80
+ onClick={() => {
81
+ window.location.href = inputRef?.current?.value
82
+ ? `/search?q=${inputRef.current.value}`
83
+ : `/search`;
84
+ }}
85
+ >
86
+ Search
87
+ </button>
80
88
  </div>
81
89
  )}
82
90
  </PredictiveSearchForm>
@@ -3,11 +3,11 @@ import {
3
3
  Form,
4
4
  useParams,
5
5
  useFetcher,
6
- useFetchers,
7
6
  type FormProps,
8
7
  } from '@remix-run/react';
9
8
  import {Image, Money, Pagination} from '@shopify/hydrogen';
10
9
  import React, {useRef, useEffect} from 'react';
10
+ import {applyTrackingParams} from '~/lib/search';
11
11
 
12
12
  import type {
13
13
  PredictiveProductFragment,
@@ -104,7 +104,10 @@ export function SearchForm({searchTerm}: {searchTerm: string}) {
104
104
 
105
105
  export function SearchResults({
106
106
  results,
107
- }: Pick<FetchSearchResultsReturn['searchResults'], 'results'>) {
107
+ searchTerm,
108
+ }: Pick<FetchSearchResultsReturn['searchResults'], 'results'> & {
109
+ searchTerm: string;
110
+ }) {
108
111
  if (!results) {
109
112
  return null;
110
113
  }
@@ -128,6 +131,7 @@ export function SearchResults({
128
131
  <SearchResultsProductsGrid
129
132
  key="products"
130
133
  products={productResults}
134
+ searchTerm={searchTerm}
131
135
  />
132
136
  ) : null;
133
137
  }
@@ -148,19 +152,44 @@ export function SearchResults({
148
152
  );
149
153
  }
150
154
 
151
- function SearchResultsProductsGrid({products}: Pick<SearchQuery, 'products'>) {
155
+ function SearchResultsProductsGrid({
156
+ products,
157
+ searchTerm,
158
+ }: Pick<SearchQuery, 'products'> & {searchTerm: string}) {
152
159
  return (
153
160
  <div className="search-result">
154
161
  <h2>Products</h2>
155
162
  <Pagination connection={products}>
156
163
  {({nodes, isLoading, NextLink, PreviousLink}) => {
157
- const itemsMarkup = nodes.map((product) => (
158
- <div className="search-results-item" key={product.id}>
159
- <Link prefetch="intent" to={`/products/${product.handle}`}>
160
- <span>{product.title}</span>
161
- </Link>
162
- </div>
163
- ));
164
+ const ItemsMarkup = nodes.map((product) => {
165
+ const trackingParams = applyTrackingParams(
166
+ product,
167
+ `q=${encodeURIComponent(searchTerm)}`,
168
+ );
169
+
170
+ return (
171
+ <div className="search-results-item" key={product.id}>
172
+ <Link
173
+ prefetch="intent"
174
+ to={`/products/${product.handle}${trackingParams}`}
175
+ >
176
+ {product.variants.nodes[0].image && (
177
+ <Image
178
+ data={product.variants.nodes[0].image}
179
+ alt={product.title}
180
+ width={50}
181
+ />
182
+ )}
183
+ <div>
184
+ <p>{product.title}</p>
185
+ <small>
186
+ <Money data={product.variants.nodes[0].price} />
187
+ </small>
188
+ </div>
189
+ </Link>
190
+ </div>
191
+ );
192
+ });
164
193
  return (
165
194
  <div>
166
195
  <div>
@@ -169,7 +198,7 @@ function SearchResultsProductsGrid({products}: Pick<SearchQuery, 'products'>) {
169
198
  </PreviousLink>
170
199
  </div>
171
200
  <div>
172
- {itemsMarkup}
201
+ {ItemsMarkup}
173
202
  <br />
174
203
  </div>
175
204
  <div>
@@ -251,7 +280,9 @@ export function PredictiveSearchForm({
251
280
  ...props
252
281
  }: SearchFromProps) {
253
282
  const params = useParams();
254
- const fetcher = useFetcher<NormalizedPredictiveSearchResults>();
283
+ const fetcher = useFetcher<NormalizedPredictiveSearchResults>({
284
+ key: 'search',
285
+ });
255
286
  const inputRef = useRef<HTMLInputElement | null>(null);
256
287
 
257
288
  function fetchResults(event: React.ChangeEvent<HTMLInputElement>) {
@@ -318,7 +349,6 @@ export function PredictiveSearchResults() {
318
349
  />
319
350
  ))}
320
351
  </div>
321
- {/* view all results /search?q=term */}
322
352
  {searchTerm.current && (
323
353
  <Link onClick={goToSearchResult} to={`/search?q=${searchTerm.current}`}>
324
354
  <p>
@@ -425,10 +455,9 @@ type UseSearchReturn = NormalizedPredictiveSearch & {
425
455
  };
426
456
 
427
457
  function usePredictiveSearch(): UseSearchReturn {
428
- const fetchers = useFetchers();
458
+ const searchFetcher = useFetcher<FetchSearchResultsReturn>({key: 'search'});
429
459
  const searchTerm = useRef<string>('');
430
460
  const searchInputRef = useRef<HTMLInputElement | null>(null);
431
- const searchFetcher = fetchers.find((fetcher) => fetcher.data?.searchResults);
432
461
 
433
462
  if (searchFetcher?.state === 'loading') {
434
463
  searchTerm.current = (searchFetcher.formData?.get('q') || '') as string;
@@ -0,0 +1,29 @@
1
+ import type {
2
+ PredictiveQueryFragment,
3
+ SearchProductFragment,
4
+ PredictiveProductFragment,
5
+ PredictiveCollectionFragment,
6
+ PredictivePageFragment,
7
+ PredictiveArticleFragment,
8
+ } from 'storefrontapi.generated';
9
+
10
+ export function applyTrackingParams(
11
+ resource:
12
+ | PredictiveQueryFragment
13
+ | SearchProductFragment
14
+ | PredictiveProductFragment
15
+ | PredictiveCollectionFragment
16
+ | PredictiveArticleFragment
17
+ | PredictivePageFragment,
18
+ params?: string,
19
+ ) {
20
+ if (params) {
21
+ return resource?.trackingParameters
22
+ ? `?${params}&${resource.trackingParameters}`
23
+ : `?${params}`;
24
+ } else {
25
+ return resource?.trackingParameters
26
+ ? `?${resource.trackingParameters}`
27
+ : '';
28
+ }
29
+ }
@@ -73,8 +73,6 @@ export async function loader({context}: LoaderFunctionArgs) {
73
73
  const publicStoreDomain = context.env.PUBLIC_STORE_DOMAIN;
74
74
 
75
75
  const isLoggedInPromise = customerAccount.isLoggedIn();
76
-
77
- // defer the cart query by not awaiting it
78
76
  const cartPromise = cart.get();
79
77
 
80
78
  // defer the footer query (below the fold)
@@ -95,7 +95,7 @@ function EmptyOrders() {
95
95
  }
96
96
 
97
97
  function OrderItem({order}: {order: OrderItemFragment}) {
98
- const fulfillmentStatus = flattenConnection(order.fulfillments)[0].status;
98
+ const fulfillmentStatus = flattenConnection(order.fulfillments)[0]?.status;
99
99
  return (
100
100
  <>
101
101
  <fieldset>
@@ -104,7 +104,7 @@ function OrderItem({order}: {order: OrderItemFragment}) {
104
104
  </Link>
105
105
  <p>{new Date(order.processedAt).toDateString()}</p>
106
106
  <p>{order.financialStatus}</p>
107
- <p>{fulfillmentStatus}</p>
107
+ {fulfillmentStatus && <p>{fulfillmentStatus}</p>}
108
108
  <Money data={order.totalPrice} />
109
109
  <Link to={`/account/orders/${btoa(order.id)}`}>View Order →</Link>
110
110
  </fieldset>
@@ -4,6 +4,7 @@ import type {
4
4
  NormalizedPredictiveSearchResults,
5
5
  } from '~/components/Search';
6
6
  import {NO_PREDICTIVE_SEARCH_RESULTS} from '~/components/Search';
7
+ import {applyTrackingParams} from '~/lib/search';
7
8
 
8
9
  import type {
9
10
  PredictiveArticleFragment,
@@ -14,12 +15,6 @@ import type {
14
15
  PredictiveSearchQuery,
15
16
  } from 'storefrontapi.generated';
16
17
 
17
- type PredictiveSearchResultItem =
18
- | PredictiveArticleFragment
19
- | PredictiveCollectionFragment
20
- | PredictivePageFragment
21
- | PredictiveProductFragment;
22
-
23
18
  type PredictiveSearchTypes =
24
19
  | 'ARTICLE'
25
20
  | 'COLLECTION'
@@ -121,21 +116,6 @@ export function normalizePredictiveSearchResults(
121
116
  };
122
117
  }
123
118
 
124
- function applyTrackingParams(
125
- resource: PredictiveSearchResultItem | PredictiveQueryFragment,
126
- params?: string,
127
- ) {
128
- if (params) {
129
- return resource.trackingParameters
130
- ? `?${params}&${resource.trackingParameters}`
131
- : `?${params}`;
132
- } else {
133
- return resource.trackingParameters
134
- ? `?${resource.trackingParameters}`
135
- : '';
136
- }
137
- }
138
-
139
119
  const localePrefix = locale ? `/${locale}` : '';
140
120
  const results: NormalizedPredictiveSearchResults = [];
141
121
 
@@ -13,10 +13,7 @@ export const meta: MetaFunction = () => {
13
13
  export async function action({request, context}: ActionFunctionArgs) {
14
14
  const {cart} = context;
15
15
 
16
- const [formData, customerAccessToken] = await Promise.all([
17
- request.formData(),
18
- await context.customerAccount.getAccessToken(),
19
- ]);
16
+ const formData = await request.formData();
20
17
 
21
18
  const {action, inputs} = CartForm.getFormInput(formData);
22
19
 
@@ -54,7 +51,6 @@ export async function action({request, context}: ActionFunctionArgs) {
54
51
  case CartForm.ACTIONS.BuyerIdentityUpdate: {
55
52
  result = await cart.updateBuyerIdentity({
56
53
  ...inputs.buyerIdentity,
57
- customerAccessToken,
58
54
  });
59
55
  break;
60
56
  }
@@ -41,7 +41,10 @@ export async function loader({request, context}: LoaderFunctionArgs) {
41
41
  totalResults,
42
42
  };
43
43
 
44
- return defer({searchTerm, searchResults});
44
+ return defer({
45
+ searchTerm,
46
+ searchResults,
47
+ });
45
48
  }
46
49
 
47
50
  export default function SearchPage() {
@@ -54,7 +57,10 @@ export default function SearchPage() {
54
57
  {!searchTerm || !searchResults.totalResults ? (
55
58
  <NoSearchResults />
56
59
  ) : (
57
- <SearchResults results={searchResults.results} />
60
+ <SearchResults
61
+ results={searchResults.results}
62
+ searchTerm={searchTerm}
63
+ />
58
64
  )}
59
65
  </div>
60
66
  );
@@ -181,11 +181,6 @@ aside li {
181
181
  margin-top: auto;
182
182
  }
183
183
 
184
- .footer-menu-missing {
185
- display: inline-block;
186
- margin: 1rem;
187
- }
188
-
189
184
  .footer-menu {
190
185
  align-items: center;
191
186
  display: flex;
@@ -237,7 +232,7 @@ aside li {
237
232
  width: calc(var(--aside-width) - 40px);
238
233
  }
239
234
 
240
- .cart-line-quantiy {
235
+ .cart-line-quantity {
241
236
  display: flex;
242
237
  }
243
238
 
@@ -297,6 +292,15 @@ aside li {
297
292
  margin-bottom: 0.5rem;
298
293
  }
299
294
 
295
+ .search-results-item a {
296
+ display: flex;
297
+ flex: row;
298
+ align-items: center;
299
+ gap: 1rem;
300
+ }
301
+
302
+
303
+
300
304
  /*
301
305
  * --------------------------------------------------
302
306
  * routes/__index
@@ -392,11 +396,6 @@ aside li {
392
396
  margin-top: 0;
393
397
  }
394
398
 
395
- .product-images {
396
- display: grid;
397
- grid-gap: 1rem;
398
- }
399
-
400
399
  .product-image img {
401
400
  height: auto;
402
401
  width: 100%;
@@ -463,10 +462,6 @@ aside li {
463
462
  * routes/account
464
463
  * --------------------------------------------------
465
464
  */
466
- .account-profile-marketing {
467
- display: flex;
468
- align-items: center;
469
- }
470
465
 
471
466
  .account-logout {
472
467
  display: inline-block;
@@ -2,7 +2,7 @@
2
2
  "name": "skeleton",
3
3
  "private": true,
4
4
  "sideEffects": false,
5
- "version": "1.0.2",
5
+ "version": "1.0.4",
6
6
  "scripts": {
7
7
  "build": "shopify hydrogen build",
8
8
  "dev": "shopify hydrogen dev --codegen",
@@ -13,11 +13,11 @@
13
13
  },
14
14
  "prettier": "@shopify/prettier-config",
15
15
  "dependencies": {
16
- "@remix-run/react": "^2.5.1",
17
- "@remix-run/server-runtime": "^2.5.1",
16
+ "@remix-run/react": "^2.6.0",
17
+ "@remix-run/server-runtime": "^2.6.0",
18
18
  "@shopify/cli": "3.52.0",
19
- "@shopify/cli-hydrogen": "^7.0.0",
20
- "@shopify/hydrogen": "~2024.1.0",
19
+ "@shopify/cli-hydrogen": "^7.1.0",
20
+ "@shopify/hydrogen": "~2024.1.2",
21
21
  "@shopify/remix-oxygen": "^2.0.3",
22
22
  "graphql": "^16.6.0",
23
23
  "graphql-tag": "^2.12.6",
@@ -26,8 +26,8 @@
26
26
  "react-dom": "^18.2.0"
27
27
  },
28
28
  "devDependencies": {
29
- "@remix-run/dev": "^2.5.1",
30
- "@remix-run/eslint-config": "^2.5.1",
29
+ "@remix-run/dev": "^2.6.0",
30
+ "@remix-run/eslint-config": "^2.6.0",
31
31
  "@shopify/oxygen-workers-types": "^4.0.0",
32
32
  "@shopify/prettier-config": "^1.1.2",
33
33
  "@total-typescript/ts-reset": "^0.4.2",
@@ -19,5 +19,6 @@ module.exports = {
19
19
  future: {
20
20
  v3_fetcherPersist: true,
21
21
  v3_relativeSplatpath: true,
22
+ v3_throwAbortReason: true,
22
23
  },
23
24
  };
@@ -5,7 +5,11 @@
5
5
  // Enhance TypeScript's built-in typings.
6
6
  import '@total-typescript/ts-reset';
7
7
 
8
- import type {Storefront, CustomerClient, HydrogenCart} from '@shopify/hydrogen';
8
+ import type {
9
+ Storefront,
10
+ CustomerAccount,
11
+ HydrogenCart,
12
+ } from '@shopify/hydrogen';
9
13
  import type {AppSession} from '~/lib/session';
10
14
 
11
15
  declare global {
@@ -36,7 +40,7 @@ declare module '@shopify/remix-oxygen' {
36
40
  env: Env;
37
41
  cart: HydrogenCart;
38
42
  storefront: Storefront;
39
- customerAccount: CustomerClient;
43
+ customerAccount: CustomerAccount;
40
44
  session: AppSession;
41
45
  waitUntil: ExecutionContext['waitUntil'];
42
46
  }
@@ -70,6 +70,7 @@ export default {
70
70
  */
71
71
  const cart = createCartHandler({
72
72
  storefront,
73
+ customerAccount,
73
74
  getCartId: cartGetIdDefault(request.headers),
74
75
  setCartId: cartSetIdDefault(),
75
76
  cartQueryFragment: CART_QUERY_FRAGMENT,
@@ -3,7 +3,7 @@ import { outputDebug } from '@shopify/cli-kit/node/output';
3
3
 
4
4
  const EXPERIMENTAL_VM_MODULES_FLAG = "--experimental-vm-modules";
5
5
  function commandNeedsVM(id = "", argv = []) {
6
- return "hydrogen:debug:cpu";
6
+ return id === "hydrogen:debug:cpu" || ["hydrogen:dev", "hydrogen:preview"].includes(id) && argv.includes("--legacy-runtime");
7
7
  }
8
8
  const hook = async function(options) {
9
9
  if (commandNeedsVM(options.id, options.argv) && !process.execArgv.includes(EXPERIMENTAL_VM_MODULES_FLAG) && !(process.env.NODE_OPTIONS ?? "").includes(EXPERIMENTAL_VM_MODULES_FLAG)) {
@@ -12,8 +12,8 @@ const hook = async function(options) {
12
12
  );
13
13
  const [command, ...args] = process.argv;
14
14
  args.unshift(EXPERIMENTAL_VM_MODULES_FLAG);
15
- spawnSync(command, args, { stdio: "inherit" });
16
- process.exit(0);
15
+ const result = spawnSync(command, args, { stdio: "inherit" });
16
+ process.exit(result.status ?? 1);
17
17
  }
18
18
  };
19
19
  var init_default = hook;
@@ -63,14 +63,7 @@ function spawnCodegenProcess({
63
63
  });
64
64
  return child;
65
65
  }
66
- async function codegen(options) {
67
- await import('@shopify/hydrogen-codegen/patch').catch((error) => {
68
- throw new AbortError(
69
- `Failed to patch dependencies for codegen.
70
- ${error.stack}`,
71
- "Please report this issue."
72
- );
73
- });
66
+ function codegen(options) {
74
67
  return generateTypes(options).catch((error) => {
75
68
  const { message, details } = normalizeCodegenError(
76
69
  error.message,
package/dist/lib/file.js CHANGED
@@ -84,7 +84,10 @@ async function mergePackageJson(sourceDir, targetDir, options) {
84
84
  }, {});
85
85
  }
86
86
  }
87
- await writePackageJSON(targetDir, targetPkgJson);
87
+ await writePackageJSON(
88
+ targetDir,
89
+ options?.onResult?.(targetPkgJson) ?? targetPkgJson
90
+ );
88
91
  }
89
92
 
90
93
  export { findFileWithExtension, mergePackageJson, replaceFileContent };
package/dist/lib/flags.js CHANGED
@@ -92,6 +92,12 @@ const commonFlags = {
92
92
  description: "Applies the current files on top of Hydrogen's starter template in a temporary directory.",
93
93
  default: false,
94
94
  required: false
95
+ }),
96
+ lockfileCheck: Flags.boolean({
97
+ allowNo: true,
98
+ default: true,
99
+ description: "Checks that there is exactly 1 valid lockfile in the project. Defaults to true, use `--no-lockfile-check` to disable.",
100
+ env: "SHOPIFY_HYDROGEN_FLAG_LOCKFILE_CHECK"
95
101
  })
96
102
  };
97
103
  function flagsToCamelObject(obj) {
@@ -8,6 +8,9 @@ function getGraphiQLUrl({
8
8
  if (typeof variables !== "string")
9
9
  variables = JSON.stringify(variables);
10
10
  url += `?query=${encodeURIComponent(query)}${variables ? `&variables=${encodeURIComponent(variables)}` : ""}`;
11
+ if (graphql.schema) {
12
+ url += `&schema=${graphql.schema}`;
13
+ }
11
14
  }
12
15
  return url;
13
16
  }
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
+ import { createRequire } from 'node:module';
3
4
  import { createServer } from 'node:http';
4
5
  import { lookupMimeType } from '@shopify/cli-kit/node/mimes';
5
6
 
@@ -10,13 +11,28 @@ function buildAssetsUrl(assetsPort) {
10
11
  }
11
12
  function createAssetsServer(buildPathClient) {
12
13
  return createServer(async (req, res) => {
14
+ if (req.method === "OPTIONS") {
15
+ res.setHeader("Access-Control-Allow-Origin", req.headers.origin || "*");
16
+ res.setHeader("Access-Control-Allow-Credentials", "true");
17
+ res.setHeader("Access-Control-Allow-Private-Network", "true");
18
+ res.setHeader("Access-Control-Max-Age", "86400");
19
+ res.writeHead(204);
20
+ res.end();
21
+ return;
22
+ }
13
23
  res.setHeader("Access-Control-Allow-Origin", "*");
14
24
  res.setHeader("X-Content-Type-Options", "nosniff");
15
25
  const pathname = req.url?.split("?")[0] || "";
16
26
  const isValidAssetPath = pathname.startsWith(`/${artificialAssetPrefix}/`) && !pathname.includes("..");
17
27
  const relativeAssetPath = isValidAssetPath ? pathname.replace(`/${artificialAssetPrefix}`, "") : pathname;
18
28
  if (isValidAssetPath) {
19
- const filePath = path.join(buildPathClient, relativeAssetPath);
29
+ let filePath = path.join(buildPathClient, relativeAssetPath);
30
+ if (relativeAssetPath === "/graphiql/customer-account.schema.json") {
31
+ const require2 = createRequire(import.meta.url);
32
+ filePath = require2.resolve(
33
+ "@shopify/hydrogen/customer-account.schema.json"
34
+ );
35
+ }
20
36
  const file = await fs.open(filePath).catch(() => {
21
37
  });
22
38
  const stat = await file?.stat().catch(() => {
@@ -11,7 +11,7 @@ import colors from '@shopify/cli-kit/node/colors';
11
11
  import { login, renderLoginSuccess } from '../auth.js';
12
12
  import { renderI18nPrompt, setupI18nStrategy, I18N_STRATEGY_NAME_MAP } from '../setups/i18n/index.js';
13
13
  import { titleize } from '../string.js';
14
- import { ALIAS_NAME, createPlatformShortcut, getCliCommand } from '../shell.js';
14
+ import { ALIAS_NAME, createPlatformShortcut } from '../shell.js';
15
15
  import { transpileProject } from '../transpile/index.js';
16
16
  import { renderCssPrompt, setupCssStrategy, CSS_STRATEGY_NAME_MAP } from '../setups/css/index.js';
17
17
  import { renderRoutePrompt, generateRoutes, generateProjectFile } from '../setups/routes/generate.js';
@@ -75,6 +75,8 @@ function generateProjectEntries(options) {
75
75
  );
76
76
  }
77
77
  async function handleCliShortcut(controller, cliCommand, flagShortcut) {
78
+ if (cliCommand === ALIAS_NAME)
79
+ return {};
78
80
  const shouldCreateShortcut = flagShortcut ?? await renderConfirmationPrompt({
79
81
  confirmationMessage: "Yes",
80
82
  cancellationMessage: "No",
@@ -329,7 +331,7 @@ async function renderProjectReady(project, {
329
331
  packageManager,
330
332
  depsInstalled,
331
333
  cssStrategy,
332
- hasCreatedShortcut,
334
+ cliCommand,
333
335
  routes,
334
336
  i18n,
335
337
  depsError,
@@ -369,7 +371,6 @@ async function renderProjectReady(project, {
369
371
  }
370
372
  }
371
373
  const padMin = 1 + bodyLines.reduce((max, [label]) => Math.max(max, label.length), 0);
372
- const cliCommand = hasCreatedShortcut ? ALIAS_NAME : await getCliCommand(project.directory, packageManager);
373
374
  const render = hasErrors ? renderWarning : renderSuccess;
374
375
  render({
375
376
  headline: `Storefront setup complete` + (hasErrors ? " with errors (see warnings below)." : "!"),
@@ -156,7 +156,7 @@ async function setupLocalStarterTemplate(options, controller) {
156
156
  packageManager,
157
157
  cssStrategy,
158
158
  depsInstalled: false,
159
- hasCreatedShortcut: false
159
+ cliCommand: await getCliCommand("", packageManager)
160
160
  };
161
161
  if (shouldInstallDeps) {
162
162
  const installingDepsPromise = backgroundWorkPromise.then(async () => {
@@ -174,19 +174,19 @@ async function setupLocalStarterTemplate(options, controller) {
174
174
  }
175
175
  });
176
176
  }
177
- const pkgManagerCommand = await getCliCommand("", packageManager);
178
177
  const { createShortcut, showShortcutBanner } = await handleCliShortcut(
179
178
  controller,
180
- pkgManagerCommand,
179
+ setupSummary.cliCommand,
181
180
  options.shortcut
182
181
  );
183
182
  if (createShortcut) {
184
183
  backgroundWorkPromise = backgroundWorkPromise.then(async () => {
185
- setupSummary.hasCreatedShortcut = await createShortcut();
184
+ if (await createShortcut()) {
185
+ setupSummary.cliCommand = ALIAS_NAME;
186
+ }
186
187
  });
187
188
  showShortcutBanner();
188
189
  }
189
- const cliCommand = createShortcut ? ALIAS_NAME : pkgManagerCommand;
190
190
  renderSuccess({
191
191
  headline: [
192
192
  { userInput: storefrontInfo?.title ?? project.name },
@@ -196,13 +196,13 @@ async function setupLocalStarterTemplate(options, controller) {
196
196
  const continueWithSetup = (options.i18n ?? options.routes) !== void 0 || await renderConfirmationPrompt({
197
197
  message: "Do you want to scaffold routes and core functionality?",
198
198
  confirmationMessage: "Yes, set up now",
199
- cancellationMessage: "No, set up later " + colors.dim(`(run \`${cliCommand} setup\`)`),
199
+ cancellationMessage: "No, set up later " + colors.dim(`(run \`${setupSummary.cliCommand} setup\`)`),
200
200
  abortSignal: controller.signal
201
201
  });
202
202
  if (continueWithSetup) {
203
203
  const { i18nStrategy, setupI18n } = await handleI18n(
204
204
  controller,
205
- cliCommand,
205
+ setupSummary.cliCommand,
206
206
  options.i18n
207
207
  );
208
208
  const { setupRoutes } = await handleRouteGeneration(
@@ -5,6 +5,7 @@ import { joinPath } from '@shopify/cli-kit/node/path';
5
5
  import { renderTasks, renderInfo } from '@shopify/cli-kit/node/ui';
6
6
  import { getLatestTemplates } from '../template-downloader.js';
7
7
  import { applyTemplateDiff } from '../template-diff.js';
8
+ import { getCliCommand } from '../shell.js';
8
9
  import { createAbortHandler, handleProjectLocation, handleLanguage, createInitialCommit, handleDependencies, commitAll, renderProjectReady } from './common.js';
9
10
 
10
11
  async function setupRemoteTemplate(options, controller) {
@@ -65,7 +66,7 @@ async function setupRemoteTemplate(options, controller) {
65
66
  language,
66
67
  packageManager,
67
68
  depsInstalled: false,
68
- hasCreatedShortcut: false
69
+ cliCommand: await getCliCommand("", packageManager)
69
70
  };
70
71
  const tasks = [
71
72
  {