@shopify/cli-hydrogen 8.1.0 → 8.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 (101) hide show
  1. package/dist/assets/hydrogen/starter/CHANGELOG.md +166 -0
  2. package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +37 -0
  3. package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +150 -0
  4. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +68 -0
  5. package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +101 -0
  6. package/dist/assets/hydrogen/starter/app/components/Header.tsx +3 -3
  7. package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +2 -2
  8. package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +80 -0
  9. package/dist/assets/hydrogen/starter/app/components/ProductImage.tsx +23 -0
  10. package/dist/assets/hydrogen/starter/app/components/ProductPrice.tsx +27 -0
  11. package/dist/assets/hydrogen/starter/app/lib/session.ts +5 -0
  12. package/dist/assets/hydrogen/starter/app/root.tsx +23 -36
  13. package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +1 -5
  14. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +12 -70
  15. package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +7 -14
  16. package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +1 -8
  17. package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +5 -22
  18. package/dist/assets/hydrogen/starter/app/routes/account.tsx +0 -1
  19. package/dist/assets/hydrogen/starter/app/routes/cart.tsx +1 -3
  20. package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +51 -232
  21. package/dist/assets/hydrogen/starter/package.json +10 -11
  22. package/dist/assets/hydrogen/starter/server.ts +4 -0
  23. package/dist/assets/hydrogen/tailwind/package.json +1 -6
  24. package/dist/assets/hydrogen/tailwind/tailwind.css +6 -3
  25. package/dist/assets/hydrogen/vanilla-extract/package.json +2 -3
  26. package/dist/assets/hydrogen/virtual-routes/components/{Layout.jsx → PageLayout.jsx} +2 -2
  27. package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +1 -2
  28. package/dist/assets/hydrogen/virtual-routes/components/RequestTable.jsx +1 -2
  29. package/dist/assets/hydrogen/virtual-routes/routes/index.jsx +1 -2
  30. package/dist/assets/hydrogen/virtual-routes/virtual-root.jsx +8 -30
  31. package/dist/commands/hydrogen/build.js +33 -10
  32. package/dist/commands/hydrogen/customer-account/push.js +3 -6
  33. package/dist/commands/hydrogen/debug/cpu.js +3 -3
  34. package/dist/commands/hydrogen/deploy.js +14 -3
  35. package/dist/commands/hydrogen/dev.js +3 -6
  36. package/dist/commands/hydrogen/env/list.js +1 -2
  37. package/dist/commands/hydrogen/env/pull.js +2 -4
  38. package/dist/commands/hydrogen/env/push.js +6 -12
  39. package/dist/commands/hydrogen/init.d.ts +18 -15
  40. package/dist/commands/hydrogen/init.js +12 -24
  41. package/dist/commands/hydrogen/link.js +1 -2
  42. package/dist/commands/hydrogen/preview.js +4 -6
  43. package/dist/commands/hydrogen/setup/css.js +29 -12
  44. package/dist/commands/hydrogen/setup/vite.js +3 -6
  45. package/dist/commands/hydrogen/setup.js +8 -7
  46. package/dist/commands/hydrogen/upgrade.js +16 -32
  47. package/dist/hooks/init.js +50 -6
  48. package/dist/index.d.ts +46 -46
  49. package/dist/lib/auth.js +1 -2
  50. package/dist/lib/build.js +1 -2
  51. package/dist/lib/bundle/analyzer.js +39 -24
  52. package/dist/lib/bundle/vite-plugin.js +161 -0
  53. package/dist/lib/check-cli-version.js +61 -0
  54. package/dist/lib/check-lockfile.js +2 -2
  55. package/dist/lib/classic-compiler/build.js +3 -3
  56. package/dist/lib/classic-compiler/dev.js +5 -10
  57. package/dist/lib/codegen.js +8 -16
  58. package/dist/lib/defer.js +2 -4
  59. package/dist/lib/environment-variables.js +2 -4
  60. package/dist/lib/file.js +15 -7
  61. package/dist/lib/flags.js +10 -0
  62. package/dist/lib/get-oxygen-deployment-data.js +1 -2
  63. package/dist/lib/graphiql-url.js +1 -2
  64. package/dist/lib/import-utils.js +3 -2
  65. package/dist/lib/log.js +11 -22
  66. package/dist/lib/mini-oxygen/common.js +1 -2
  67. package/dist/lib/mini-oxygen/node.js +1 -2
  68. package/dist/lib/missing-routes.js +1 -2
  69. package/dist/lib/onboarding/common.js +60 -15
  70. package/dist/lib/onboarding/local.js +14 -13
  71. package/dist/lib/onboarding/remote.js +16 -9
  72. package/dist/lib/onboarding/setup-template.mocks.js +6 -3
  73. package/dist/lib/remix-config.js +2 -4
  74. package/dist/lib/remix-version-check.js +1 -2
  75. package/dist/lib/request-events.js +3 -6
  76. package/dist/lib/setups/css/assets.js +1 -1
  77. package/dist/lib/setups/css/index.js +17 -10
  78. package/dist/lib/setups/css/replacers.js +74 -76
  79. package/dist/lib/setups/css/tailwind.js +16 -20
  80. package/dist/lib/setups/css/vanilla-extract.js +8 -5
  81. package/dist/lib/setups/i18n/replacers.js +1 -2
  82. package/dist/lib/setups/routes/generate.js +18 -19
  83. package/dist/lib/shell.js +5 -10
  84. package/dist/lib/template-diff.js +83 -104
  85. package/dist/lib/template-downloader.js +2 -2
  86. package/dist/lib/transpile/morph/functions.js +3 -6
  87. package/dist/lib/transpile/morph/index.js +2 -4
  88. package/dist/lib/transpile/morph/typedefs.js +3 -6
  89. package/dist/lib/transpile/morph/utils.js +2 -4
  90. package/dist/lib/transpile/project.js +4 -3
  91. package/oclif.manifest.json +51 -4
  92. package/package.json +8 -12
  93. package/dist/assets/hydrogen/css-modules/package.json +0 -6
  94. package/dist/assets/hydrogen/postcss/package.json +0 -10
  95. package/dist/assets/hydrogen/postcss/postcss.config.js +0 -8
  96. package/dist/assets/hydrogen/starter/app/components/Cart.tsx +0 -364
  97. package/dist/assets/hydrogen/tailwind/postcss.config.js +0 -10
  98. package/dist/assets/hydrogen/tailwind/tailwind.config.js +0 -8
  99. package/dist/lib/check-version.js +0 -75
  100. package/dist/lib/setups/css/css-modules.js +0 -23
  101. package/dist/lib/setups/css/postcss.js +0 -31
@@ -1,32 +1,17 @@
1
1
  import {Suspense} from 'react';
2
2
  import {defer, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen';
3
+ import {Await, useLoaderData, type MetaFunction} from '@remix-run/react';
4
+ import type {ProductFragment} from 'storefrontapi.generated';
3
5
  import {
4
- Await,
5
- Link,
6
- useLoaderData,
7
- type MetaFunction,
8
- type FetcherWithComponents,
9
- } from '@remix-run/react';
10
- import type {
11
- ProductFragment,
12
- ProductVariantsQuery,
13
- ProductVariantFragment,
14
- } from 'storefrontapi.generated';
15
- import {
16
- Image,
17
- Money,
18
- VariantSelector,
19
- type VariantOption,
20
6
  getSelectedProductOptions,
21
- CartForm,
22
- type OptimisticCartLine,
23
7
  Analytics,
24
- type CartViewPayload,
25
- useAnalytics,
8
+ useOptimisticVariant,
26
9
  } from '@shopify/hydrogen';
27
10
  import type {SelectedOption} from '@shopify/hydrogen/storefront-api-types';
28
11
  import {getVariantUrl} from '~/lib/variants';
29
- import {useAside} from '~/components/Aside';
12
+ import {ProductPrice} from '~/components/ProductPrice';
13
+ import {ProductImage} from '~/components/ProductImage';
14
+ import {ProductForm} from '~/components/ProductForm';
30
15
 
31
16
  export const meta: MetaFunction<typeof loader> = ({data}) => {
32
17
  return [{title: `Hydrogen | ${data?.product.title ?? ''}`}];
@@ -143,15 +128,54 @@ function redirectToFirstVariant({
143
128
 
144
129
  export default function Product() {
145
130
  const {product, variants} = useLoaderData<typeof loader>();
146
- const {selectedVariant} = product;
131
+ const selectedVariant = useOptimisticVariant(
132
+ product.selectedVariant,
133
+ variants,
134
+ );
135
+
136
+ const {title, descriptionHtml} = product;
137
+
147
138
  return (
148
139
  <div className="product">
149
140
  <ProductImage image={selectedVariant?.image} />
150
- <ProductMain
151
- selectedVariant={selectedVariant}
152
- product={product}
153
- variants={variants}
154
- />
141
+ <div className="product-main">
142
+ <h1>{title}</h1>
143
+ <ProductPrice
144
+ price={selectedVariant?.price}
145
+ compareAtPrice={selectedVariant?.compareAtPrice}
146
+ />
147
+ <br />
148
+ <Suspense
149
+ fallback={
150
+ <ProductForm
151
+ product={product}
152
+ selectedVariant={selectedVariant}
153
+ variants={[]}
154
+ />
155
+ }
156
+ >
157
+ <Await
158
+ errorElement="There was a problem loading product variants"
159
+ resolve={variants}
160
+ >
161
+ {(data) => (
162
+ <ProductForm
163
+ product={product}
164
+ selectedVariant={selectedVariant}
165
+ variants={data?.product?.variants.nodes || []}
166
+ />
167
+ )}
168
+ </Await>
169
+ </Suspense>
170
+ <br />
171
+ <br />
172
+ <p>
173
+ <strong>Description</strong>
174
+ </p>
175
+ <br />
176
+ <div dangerouslySetInnerHTML={{__html: descriptionHtml}} />
177
+ <br />
178
+ </div>
155
179
  <Analytics.ProductView
156
180
  data={{
157
181
  products: [
@@ -171,211 +195,6 @@ export default function Product() {
171
195
  );
172
196
  }
173
197
 
174
- function ProductImage({image}: {image: ProductVariantFragment['image']}) {
175
- if (!image) {
176
- return <div className="product-image" />;
177
- }
178
- return (
179
- <div className="product-image">
180
- <Image
181
- alt={image.altText || 'Product Image'}
182
- aspectRatio="1/1"
183
- data={image}
184
- key={image.id}
185
- sizes="(min-width: 45em) 50vw, 100vw"
186
- />
187
- </div>
188
- );
189
- }
190
-
191
- function ProductMain({
192
- selectedVariant,
193
- product,
194
- variants,
195
- }: {
196
- product: ProductFragment;
197
- selectedVariant: ProductFragment['selectedVariant'];
198
- variants: Promise<ProductVariantsQuery | null>;
199
- }) {
200
- const {title, descriptionHtml} = product;
201
- return (
202
- <div className="product-main">
203
- <h1>{title}</h1>
204
- <ProductPrice selectedVariant={selectedVariant} />
205
- <br />
206
- <Suspense
207
- fallback={
208
- <ProductForm
209
- product={product}
210
- selectedVariant={selectedVariant}
211
- variants={[]}
212
- />
213
- }
214
- >
215
- <Await
216
- errorElement="There was a problem loading product variants"
217
- resolve={variants}
218
- >
219
- {(data) => (
220
- <ProductForm
221
- product={product}
222
- selectedVariant={selectedVariant}
223
- variants={data?.product?.variants.nodes || []}
224
- />
225
- )}
226
- </Await>
227
- </Suspense>
228
- <br />
229
- <br />
230
- <p>
231
- <strong>Description</strong>
232
- </p>
233
- <br />
234
- <div dangerouslySetInnerHTML={{__html: descriptionHtml}} />
235
- <br />
236
- </div>
237
- );
238
- }
239
-
240
- function ProductPrice({
241
- selectedVariant,
242
- }: {
243
- selectedVariant: ProductFragment['selectedVariant'];
244
- }) {
245
- return (
246
- <div className="product-price">
247
- {selectedVariant?.compareAtPrice ? (
248
- <>
249
- <p>Sale</p>
250
- <br />
251
- <div className="product-price-on-sale">
252
- {selectedVariant ? <Money data={selectedVariant.price} /> : null}
253
- <s>
254
- <Money data={selectedVariant.compareAtPrice} />
255
- </s>
256
- </div>
257
- </>
258
- ) : (
259
- selectedVariant?.price && <Money data={selectedVariant?.price} />
260
- )}
261
- </div>
262
- );
263
- }
264
-
265
- function ProductForm({
266
- product,
267
- selectedVariant,
268
- variants,
269
- }: {
270
- product: ProductFragment;
271
- selectedVariant: ProductFragment['selectedVariant'];
272
- variants: Array<ProductVariantFragment>;
273
- }) {
274
- const {open} = useAside();
275
- const {publish, shop, cart, prevCart} = useAnalytics();
276
- return (
277
- <div className="product-form">
278
- <VariantSelector
279
- handle={product.handle}
280
- options={product.options}
281
- variants={variants}
282
- >
283
- {({option}) => <ProductOptions key={option.name} option={option} />}
284
- </VariantSelector>
285
- <br />
286
- <AddToCartButton
287
- disabled={!selectedVariant || !selectedVariant.availableForSale}
288
- onClick={() => {
289
- open('cart');
290
- publish('cart_viewed', {
291
- cart,
292
- prevCart,
293
- shop,
294
- url: window.location.href || '',
295
- } as CartViewPayload);
296
- }}
297
- lines={
298
- selectedVariant
299
- ? [
300
- {
301
- merchandiseId: selectedVariant.id,
302
- quantity: 1,
303
- selectedVariant,
304
- },
305
- ]
306
- : []
307
- }
308
- >
309
- {selectedVariant?.availableForSale ? 'Add to cart' : 'Sold out'}
310
- </AddToCartButton>
311
- </div>
312
- );
313
- }
314
-
315
- function ProductOptions({option}: {option: VariantOption}) {
316
- return (
317
- <div className="product-options" key={option.name}>
318
- <h5>{option.name}</h5>
319
- <div className="product-options-grid">
320
- {option.values.map(({value, isAvailable, isActive, to}) => {
321
- return (
322
- <Link
323
- className="product-options-item"
324
- key={option.name + value}
325
- prefetch="intent"
326
- preventScrollReset
327
- replace
328
- to={to}
329
- style={{
330
- border: isActive ? '1px solid black' : '1px solid transparent',
331
- opacity: isAvailable ? 1 : 0.3,
332
- }}
333
- >
334
- {value}
335
- </Link>
336
- );
337
- })}
338
- </div>
339
- <br />
340
- </div>
341
- );
342
- }
343
-
344
- function AddToCartButton({
345
- analytics,
346
- children,
347
- disabled,
348
- lines,
349
- onClick,
350
- }: {
351
- analytics?: unknown;
352
- children: React.ReactNode;
353
- disabled?: boolean;
354
- lines: Array<OptimisticCartLine>;
355
- onClick?: () => void;
356
- }) {
357
- return (
358
- <CartForm route="/cart" inputs={{lines}} action={CartForm.ACTIONS.LinesAdd}>
359
- {(fetcher: FetcherWithComponents<any>) => (
360
- <>
361
- <input
362
- name="analytics"
363
- type="hidden"
364
- value={JSON.stringify(analytics)}
365
- />
366
- <button
367
- type="submit"
368
- onClick={onClick}
369
- disabled={disabled ?? fetcher.state !== 'idle'}
370
- >
371
- {children}
372
- </button>
373
- </>
374
- )}
375
- </CartForm>
376
- );
377
- }
378
-
379
198
  const PRODUCT_VARIANT_FRAGMENT = `#graphql
380
199
  fragment ProductVariant on ProductVariant {
381
200
  availableForSale
@@ -2,7 +2,7 @@
2
2
  "name": "skeleton",
3
3
  "private": true,
4
4
  "sideEffects": false,
5
- "version": "2024.4.5",
5
+ "version": "2024.7.1",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "build": "shopify hydrogen build --codegen",
@@ -14,12 +14,10 @@
14
14
  },
15
15
  "prettier": "@shopify/prettier-config",
16
16
  "dependencies": {
17
- "@remix-run/react": "^2.9.2",
18
- "@remix-run/server-runtime": "^2.9.2",
19
- "@shopify/cli": "3.60.0",
20
- "@shopify/cli-hydrogen": "^8.1.0",
21
- "@shopify/hydrogen": "2024.4.3",
22
- "@shopify/remix-oxygen": "^2.0.4",
17
+ "@remix-run/react": "^2.10.1",
18
+ "@remix-run/server-runtime": "^2.10.1",
19
+ "@shopify/hydrogen": "2024.7.1",
20
+ "@shopify/remix-oxygen": "^2.0.5",
23
21
  "graphql": "^16.6.0",
24
22
  "graphql-tag": "^2.12.6",
25
23
  "isbot": "^3.8.0",
@@ -28,11 +26,12 @@
28
26
  },
29
27
  "devDependencies": {
30
28
  "@graphql-codegen/cli": "5.0.2",
31
- "@remix-run/dev": "^2.9.2",
32
- "@remix-run/eslint-config": "^2.9.2",
29
+ "@remix-run/dev": "^2.10.1",
30
+ "@remix-run/eslint-config": "^2.10.1",
31
+ "@shopify/cli": "^3.63.2",
33
32
  "@shopify/hydrogen-codegen": "^0.3.1",
34
- "@shopify/mini-oxygen": "^3.0.3",
35
- "@shopify/oxygen-workers-types": "^4.0.0",
33
+ "@shopify/mini-oxygen": "^3.0.4",
34
+ "@shopify/oxygen-workers-types": "^4.1.2",
36
35
  "@shopify/prettier-config": "^1.1.2",
37
36
  "@total-typescript/ts-reset": "^0.4.2",
38
37
  "@types/eslint": "^8.4.10",
@@ -96,6 +96,10 @@ export default {
96
96
 
97
97
  const response = await handleRequest(request);
98
98
 
99
+ if (session.isPending) {
100
+ response.headers.set('Set-Cookie', await session.commit());
101
+ }
102
+
99
103
  if (response.status === 404) {
100
104
  /**
101
105
  * Check for redirects only when there's a 404 from the app.
@@ -3,11 +3,6 @@
3
3
  "defaults"
4
4
  ],
5
5
  "devDependencies": {
6
- "@tailwindcss/forms": "^0.5.3",
7
- "@tailwindcss/typography": "^0.5.9",
8
- "postcss": "^8.4.21",
9
- "postcss-import": "^15.1.0",
10
- "postcss-preset-env": "^8.2.0",
11
- "tailwindcss": "^3.3.0"
6
+ "@tailwindcss/vite": "4.0.0-alpha.17"
12
7
  }
13
8
  }
@@ -1,3 +1,6 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
1
+ /**
2
+ * Configure Tailwind v4 in this file using CSS variables and directives:
3
+ * https://tailwindcss.com/blog/tailwindcss-v4-alpha#css-first-configuration
4
+ */
5
+
6
+ @import 'tailwindcss';
@@ -1,9 +1,8 @@
1
1
  {
2
- "comment": "Remix version is automatically updated by the CLI",
3
2
  "dependencies": {
4
- "@remix-run/css-bundle": "^1"
3
+ "@vanilla-extract/css": "^1.15.2"
5
4
  },
6
5
  "devDependencies": {
7
- "@vanilla-extract/css": "^1.11.0"
6
+ "@vanilla-extract/vite-plugin": "^4.0.10"
8
7
  }
9
8
  }
@@ -1,7 +1,7 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- function Layout(props) {
2
+ function PageLayout(props) {
3
3
  return /* @__PURE__ */ jsx("div", { className: "hydrogen-virtual-route", children: props.children });
4
4
  }
5
5
  export {
6
- Layout
6
+ PageLayout
7
7
  };
@@ -34,8 +34,7 @@ function RequestDetails({
34
34
  className: `tab${activeTabClass(key)}`,
35
35
  onClick: () => setActiveTab(key),
36
36
  onKeyUp: (event) => {
37
- if (event.code === "Space")
38
- setActiveTab(key);
37
+ if (event.code === "Space") setActiveTab(key);
39
38
  },
40
39
  children: TABS[key]
41
40
  }
@@ -61,8 +61,7 @@ function RequestTable({
61
61
  className: `grid-row${activeEventId === row.id ? " active" : ""}${row.status >= 400 ? " error" : ""}`,
62
62
  onClick: () => setActiveEventId(row.id),
63
63
  onKeyUp: (event) => {
64
- if (event.code === "Space")
65
- setActiveEventId(row.id);
64
+ if (event.code === "Space") setActiveEventId(row.id);
66
65
  },
67
66
  children: [
68
67
  /* @__PURE__ */ jsx("div", { className: "grid-cell", children: row.url }),
@@ -47,8 +47,7 @@ function Index() {
47
47
  } = useLoaderData();
48
48
  let { name: shopName, id: shopId } = shop;
49
49
  const configDone = shopId !== HYDROGEN_SHOP_ID && !isMockShop;
50
- if (isMockShop || !shopName)
51
- shopName = "Hydrogen";
50
+ if (isMockShop || !shopName) shopName = "Hydrogen";
52
51
  return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Layout, { shopName, children: [
53
52
  configDone ? /* @__PURE__ */ jsx(HydrogenLogoBaseColor, {}) : /* @__PURE__ */ jsx(HydrogenLogoBaseBW, {}),
54
53
  /* @__PURE__ */ jsxs("h1", { children: [
@@ -2,14 +2,13 @@ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import {
3
3
  Links,
4
4
  Meta,
5
- Outlet,
6
5
  Scripts,
7
6
  ScrollRestoration,
8
7
  isRouteErrorResponse,
9
8
  useRouteError
10
9
  } from "@remix-run/react";
11
10
  import favicon from "./assets/favicon.svg";
12
- import { Layout } from "./components/Layout.jsx";
11
+ import { PageLayout } from "./components/PageLayout.jsx";
13
12
  import { useNonce } from "@shopify/hydrogen";
14
13
  import styles from "./assets/styles.css?url";
15
14
  const links = () => {
@@ -18,7 +17,7 @@ const links = () => {
18
17
  { rel: "icon", type: "image/svg+xml", href: favicon }
19
18
  ];
20
19
  };
21
- function App() {
20
+ function Layout({ children }) {
22
21
  const nonce = useNonce();
23
22
  return /* @__PURE__ */ jsxs("html", { lang: "en", children: [
24
23
  /* @__PURE__ */ jsxs("head", { children: [
@@ -36,14 +35,13 @@ function App() {
36
35
  /* @__PURE__ */ jsx(Links, {})
37
36
  ] }),
38
37
  /* @__PURE__ */ jsxs("body", { children: [
39
- /* @__PURE__ */ jsx(Layout, { children: /* @__PURE__ */ jsx(Outlet, {}) }),
38
+ /* @__PURE__ */ jsx(PageLayout, { children }),
40
39
  /* @__PURE__ */ jsx(ScrollRestoration, { nonce }),
41
40
  /* @__PURE__ */ jsx(Scripts, { nonce })
42
41
  ] })
43
42
  ] });
44
43
  }
45
44
  function ErrorBoundary() {
46
- const nonce = useNonce();
47
45
  const error = useRouteError();
48
46
  let errorMessage = "Unknown error";
49
47
  let errorStatus = 500;
@@ -53,34 +51,14 @@ function ErrorBoundary() {
53
51
  } else if (error instanceof Error) {
54
52
  errorMessage = error.message;
55
53
  }
56
- return /* @__PURE__ */ jsxs("html", { lang: "en", children: [
57
- /* @__PURE__ */ jsxs("head", { children: [
58
- /* @__PURE__ */ jsx("meta", { charSet: "utf-8" }),
59
- /* @__PURE__ */ jsx("meta", { name: "viewport", content: "width=device-width,initial-scale=1" }),
60
- /* @__PURE__ */ jsx("title", { children: "Hydrogen" }),
61
- /* @__PURE__ */ jsx(
62
- "meta",
63
- {
64
- name: "description",
65
- content: "A custom storefront powered by Hydrogen"
66
- }
67
- ),
68
- /* @__PURE__ */ jsx(Meta, {}),
69
- /* @__PURE__ */ jsx(Links, {})
70
- ] }),
71
- /* @__PURE__ */ jsxs("body", { children: [
72
- /* @__PURE__ */ jsx(Layout, { children: /* @__PURE__ */ jsxs("div", { className: "route-error", children: [
73
- /* @__PURE__ */ jsx("h1", { children: "Please report this error" }),
74
- /* @__PURE__ */ jsx("h2", { children: errorStatus }),
75
- errorMessage && /* @__PURE__ */ jsx("fieldset", { children: /* @__PURE__ */ jsx("pre", { children: errorMessage }) })
76
- ] }) }),
77
- /* @__PURE__ */ jsx(ScrollRestoration, { nonce }),
78
- /* @__PURE__ */ jsx(Scripts, { nonce })
79
- ] })
54
+ return /* @__PURE__ */ jsxs("div", { className: "route-error", children: [
55
+ /* @__PURE__ */ jsx("h1", { children: "Please report this error" }),
56
+ /* @__PURE__ */ jsx("h2", { children: errorStatus }),
57
+ errorMessage && /* @__PURE__ */ jsx("fieldset", { children: /* @__PURE__ */ jsx("pre", { children: errorMessage }) })
80
58
  ] });
81
59
  }
82
60
  export {
83
61
  ErrorBoundary,
84
- App as default,
62
+ Layout,
85
63
  links
86
64
  };
@@ -1,7 +1,7 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
3
  import { resolvePath, joinPath } from '@shopify/cli-kit/node/path';
4
- import { collectLog, outputWarn } from '@shopify/cli-kit/node/output';
4
+ import { collectLog, outputInfo, outputContent, outputToken, outputWarn } from '@shopify/cli-kit/node/output';
5
5
  import { removeFile, fileSize } from '@shopify/cli-kit/node/fs';
6
6
  import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
7
7
  import { commonFlags, flagsToCamelObject } from '../../lib/flags.js';
@@ -10,6 +10,8 @@ import { hasViteConfig, getViteConfig } from '../../lib/vite-config.js';
10
10
  import { checkLockfileStatus } from '../../lib/check-lockfile.js';
11
11
  import { findMissingRoutes } from '../../lib/missing-routes.js';
12
12
  import { runClassicCompilerBuild } from '../../lib/classic-compiler/build.js';
13
+ import { hydrogenBundleAnalyzer } from '../../lib/bundle/vite-plugin.js';
14
+ import { BUNDLE_ANALYZER_HTML_FILE, getBundleAnalysisSummary } from '../../lib/bundle/analyzer.js';
13
15
  import { spawnCodegenProcess, codegen } from '../../lib/codegen.js';
14
16
  import { isCI } from '../../lib/is-ci.js';
15
17
  import { importVite } from '../../lib/import-utils.js';
@@ -33,8 +35,7 @@ class Build extends Command {
33
35
  }),
34
36
  // For the classic compiler:
35
37
  "bundle-stats": Flags.boolean({
36
- description: "[Classic Remix Compiler] Show a bundle size summary after building. Defaults to true, use `--no-bundle-stats` to disable.",
37
- default: true,
38
+ description: "Show a bundle size summary after building. Defaults to true, use `--no-bundle-stats` to disable.",
38
39
  allowNo: true
39
40
  })
40
41
  };
@@ -78,6 +79,7 @@ async function runBuild({
78
79
  disableRouteWarning = false,
79
80
  lockfileCheck = true,
80
81
  assetPath = "/",
82
+ bundleStats = !isCI(),
81
83
  watch = false,
82
84
  onServerBuildStart,
83
85
  onServerBuildFinish
@@ -129,8 +131,7 @@ async function runBuild({
129
131
  clientBuildStatus = deferPromise();
130
132
  },
131
133
  buildEnd(error) {
132
- if (error)
133
- clientBuildStatus.reject(error);
134
+ if (error) clientBuildStatus.reject(error);
134
135
  },
135
136
  writeBundle() {
136
137
  clientBuildStatus.resolve();
@@ -172,7 +173,22 @@ async function runBuild({
172
173
  closeWatcher() {
173
174
  this.error(new Error("Process exited before server build finished."));
174
175
  }
175
- }
176
+ },
177
+ ...bundleStats ? [
178
+ hydrogenBundleAnalyzer({
179
+ minify: serverMinify ? (code, filepath) => vite.transformWithEsbuild(code, filepath, {
180
+ minify: true,
181
+ minifyWhitespace: true,
182
+ minifySyntax: true,
183
+ minifyIdentifiers: true,
184
+ sourcemap: false,
185
+ treeShaking: false,
186
+ // Tree-shaking would drop most exports in routes
187
+ legalComments: "none",
188
+ target: "esnext"
189
+ }).then((result) => result.code) : void 0
190
+ })
191
+ ] : []
176
192
  ]
177
193
  });
178
194
  if (!watch) {
@@ -189,6 +205,15 @@ async function runBuild({
189
205
  };
190
206
  const codegenProcess = useCodegen ? watch ? spawnCodegenProcess(codegenOptions) : await codegen(codegenOptions).then(() => void 0) : void 0;
191
207
  if (!watch && process.env.NODE_ENV !== "development") {
208
+ if (bundleStats) {
209
+ const bundleAnalysisPath = "file://" + joinPath(serverOutDir, BUNDLE_ANALYZER_HTML_FILE);
210
+ outputInfo(
211
+ outputContent`${await getBundleAnalysisSummary(serverOutDir) || "\n"}\n │\n └─── ${outputToken.link(
212
+ "Complete analysis: " + bundleAnalysisPath,
213
+ bundleAnalysisPath
214
+ )}\n\n`
215
+ );
216
+ }
192
217
  const sizeMB = await fileSize(serverOutFile) / (1024 * 1024);
193
218
  if (sizeMB >= WORKER_BUILD_SIZE_LIMIT) {
194
219
  outputWarn(
@@ -216,10 +241,8 @@ This build is missing ${missingRoutes.length} route${missingRoutes.length > 1 ?
216
241
  codegenProcess?.removeAllListeners("close");
217
242
  codegenProcess?.kill("SIGINT");
218
243
  const promises = [];
219
- if ("close" in clientBuild)
220
- promises.push(clientBuild.close());
221
- if ("close" in serverBuild)
222
- promises.push(serverBuild.close());
244
+ if ("close" in clientBuild) promises.push(clientBuild.close());
245
+ if ("close" in serverBuild) promises.push(serverBuild.close());
223
246
  await Promise.allSettled(promises);
224
247
  if (clientBuildStatus?.state === "pending" || serverBuildStatus?.state === "pending") {
225
248
  clientBuildStatus?.promise.catch(() => {
@@ -134,8 +134,7 @@ async function runCustomerAccountPush({
134
134
  }
135
135
  }
136
136
  async function cleanupCustomerApplicationUrls(session, storefrontId, customerAccountConfig = {}) {
137
- if (!Object.values(customerAccountConfig).some(Boolean))
138
- return;
137
+ if (!Object.values(customerAccountConfig).some(Boolean)) return;
139
138
  outputDebug(
140
139
  `Cleaning up Customer Application url "${customerAccountConfig.redirectUri}"`
141
140
  );
@@ -151,11 +150,9 @@ ${error?.message}`
151
150
  });
152
151
  }
153
152
  async function getStorefrontId(root, storefrontIdFromFlag) {
154
- if (storefrontIdFromFlag)
155
- return storefrontIdFromFlag;
153
+ if (storefrontIdFromFlag) return storefrontIdFromFlag;
156
154
  const { session, config } = await login(root);
157
- if (config.storefront?.id)
158
- return config.storefront.id;
155
+ if (config.storefront?.id) return config.storefront.id;
159
156
  const cliCommand = await getCliCommand();
160
157
  const linkedStore = await linkStorefront(root, session, config, {
161
158
  cliCommand
@@ -34,7 +34,7 @@ class DebugCpu extends Command {
34
34
  async run() {
35
35
  const { flags } = await this.parse(DebugCpu);
36
36
  const originalDirectory = flags.path ? resolvePath(flags.path) : process.cwd();
37
- const diff = flags.build && flags.diff ? await prepareDiffDirectory(originalDirectory, true) : void 0;
37
+ const diff = flags.diff ? await prepareDiffDirectory(originalDirectory, true) : void 0;
38
38
  const { close } = await runDebugCpu({
39
39
  ...flagsToCamelObject(flags),
40
40
  directory: diff?.targetDirectory ?? originalDirectory,
@@ -47,8 +47,7 @@ class DebugCpu extends Command {
47
47
  }
48
48
  }
49
49
  async function runDebugCpu({ directory, entry, output }) {
50
- if (!process.env.NODE_ENV)
51
- process.env.NODE_ENV = "production";
50
+ if (!process.env.NODE_ENV) process.env.NODE_ENV = "production";
52
51
  muteDevLogs({ workerReload: false });
53
52
  let { buildPath, buildPathWorkerFile } = getProjectPaths(directory);
54
53
  const isClassicProject = await hasRemixConfigFile(directory);
@@ -99,6 +98,7 @@ Waiting for changes...`);
99
98
  sourcemap: true,
100
99
  disableRouteWarning: true,
101
100
  lockfileCheck: false,
101
+ bundleStats: false,
102
102
  ...hooks,
103
103
  onServerBuildStart() {
104
104
  if (times === 0) {