@shopify/cli-hydrogen 11.1.10 → 11.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/hydrogen/starter/CHANGELOG.md +30 -0
- package/dist/assets/hydrogen/starter/app/components/Aside.tsx +4 -2
- package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +6 -3
- package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +117 -23
- package/dist/assets/hydrogen/starter/app/components/Header.tsx +3 -3
- package/dist/assets/hydrogen/starter/app/components/PaginatedResourceSection.tsx +24 -4
- package/dist/assets/hydrogen/starter/app/components/ProductPrice.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +14 -53
- package/dist/assets/hydrogen/starter/app/routes/_index.tsx +11 -4
- package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +1 -1
- package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +2 -2
- package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +1 -1
- package/dist/assets/hydrogen/starter/package.json +4 -4
- package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +0 -11
- package/dist/assets/hydrogen/starter/vite.config.ts +1 -1
- package/dist/assets/hydrogen/vite/vite.config.js +1 -1
- package/dist/commands/hydrogen/dev.js +4 -1
- package/dist/commands/hydrogen/upgrade.js +127 -33
- package/dist/lib/onboarding/common.js +2 -0
- package/dist/lib/patch-cli.js +198 -0
- package/dist/lib/template-pack.js +20 -11
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# skeleton
|
|
2
2
|
|
|
3
|
+
## 2026.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Improve screen reader experience for paginated product grids by hiding decorative arrow characters from assistive technology. ([#3557](https://github.com/Shopify/hydrogen/pull/3557)) by [@itsjustriley](https://github.com/itsjustriley)
|
|
8
|
+
|
|
9
|
+
- Fix broken `aria-label` on territory code input in address form. The label was the raw developer string `"territoryCode"` instead of a human-readable `"Country code"`. ([#3607](https://github.com/Shopify/hydrogen/pull/3607)) by [@itsjustriley](https://github.com/itsjustriley)
|
|
10
|
+
|
|
11
|
+
- Add aria-label to ProductPrice for improved screen reader accessibility ([#3558](https://github.com/Shopify/hydrogen/pull/3558)) by [@itsjustriley](https://github.com/itsjustriley)
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`108243003a7f36349a446478f4e8ab0cade3e13a`](https://github.com/Shopify/hydrogen/commit/108243003a7f36349a446478f4e8ab0cade3e13a)]:
|
|
14
|
+
- @shopify/hydrogen@2026.1.3
|
|
15
|
+
|
|
16
|
+
## 2026.1.2
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Improve gift card accessibility in Skeleton template ([#3518](https://github.com/Shopify/hydrogen/pull/3518)) by [@itsjustriley](https://github.com/itsjustriley)
|
|
21
|
+
|
|
22
|
+
- Updated shopify/cli dependencies for cli-hydrogen ([#3553](https://github.com/Shopify/hydrogen/pull/3553)) by [@andguy95](https://github.com/andguy95)
|
|
23
|
+
|
|
24
|
+
- Updated loaders that used `customerAccount.handleAuthStatus()` to now await it. ([#3523](https://github.com/Shopify/hydrogen/pull/3523)) by [@fredericoo](https://github.com/fredericoo)
|
|
25
|
+
|
|
26
|
+
### Migration
|
|
27
|
+
|
|
28
|
+
If you call `handleAuthStatus()` in your own loaders, update those callsites to use `await`.
|
|
29
|
+
|
|
30
|
+
- Updated dependencies [[`16b0e7baca0dfd1fb330d12dac924c7593d169a8`](https://github.com/Shopify/hydrogen/commit/16b0e7baca0dfd1fb330d12dac924c7593d169a8), [`1c19b87782818dbdb4252754d2d44eb9a44fe50f`](https://github.com/Shopify/hydrogen/commit/1c19b87782818dbdb4252754d2d44eb9a44fe50f), [`029fa2d0e2297f67b37c650ba8e875ee5dee81b3`](https://github.com/Shopify/hydrogen/commit/029fa2d0e2297f67b37c650ba8e875ee5dee81b3)]:
|
|
31
|
+
- @shopify/hydrogen@2026.1.2
|
|
32
|
+
|
|
3
33
|
## 2026.1.1
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
useEffect,
|
|
6
6
|
useState,
|
|
7
7
|
} from 'react';
|
|
8
|
+
import {useId} from 'react';
|
|
8
9
|
|
|
9
10
|
type AsideType = 'search' | 'cart' | 'mobile' | 'closed';
|
|
10
11
|
type AsideContextValue = {
|
|
@@ -34,7 +35,7 @@ export function Aside({
|
|
|
34
35
|
}) {
|
|
35
36
|
const {type: activeType, close} = useAside();
|
|
36
37
|
const expanded = type === activeType;
|
|
37
|
-
|
|
38
|
+
const id = useId();
|
|
38
39
|
useEffect(() => {
|
|
39
40
|
const abortController = new AbortController();
|
|
40
41
|
|
|
@@ -57,11 +58,12 @@ export function Aside({
|
|
|
57
58
|
aria-modal
|
|
58
59
|
className={`overlay ${expanded ? 'expanded' : ''}`}
|
|
59
60
|
role="dialog"
|
|
61
|
+
aria-labelledby={id}
|
|
60
62
|
>
|
|
61
63
|
<button className="close-outside" onClick={close} />
|
|
62
64
|
<aside>
|
|
63
65
|
<header>
|
|
64
|
-
<h3>{heading}</h3>
|
|
66
|
+
<h3 id={id}>{heading}</h3>
|
|
65
67
|
<button className="close reset" onClick={close} aria-label="Close">
|
|
66
68
|
×
|
|
67
69
|
</button>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useOptimisticCart
|
|
1
|
+
import {useOptimisticCart} from '@shopify/hydrogen';
|
|
2
2
|
import {Link} from 'react-router';
|
|
3
3
|
import type {CartApiQueryFragment} from 'storefrontapi.generated';
|
|
4
4
|
import {useAside} from '~/components/Aside';
|
|
@@ -50,7 +50,10 @@ export function CartMain({layout, cart: originalCart}: CartMainProps) {
|
|
|
50
50
|
const childrenMap = getLineItemChildrenMap(cart?.lines?.nodes ?? []);
|
|
51
51
|
|
|
52
52
|
return (
|
|
53
|
-
<
|
|
53
|
+
<section
|
|
54
|
+
className={className}
|
|
55
|
+
aria-label={layout === 'page' ? 'Cart page' : 'Cart drawer'}
|
|
56
|
+
>
|
|
54
57
|
<CartEmpty hidden={linesCount} layout={layout} />
|
|
55
58
|
<div className="cart-details">
|
|
56
59
|
<p id="cart-lines" className="sr-only">
|
|
@@ -79,7 +82,7 @@ export function CartMain({layout, cart: originalCart}: CartMainProps) {
|
|
|
79
82
|
</div>
|
|
80
83
|
{cartHasItems && <CartSummary cart={cart} layout={layout} />}
|
|
81
84
|
</div>
|
|
82
|
-
</
|
|
85
|
+
</section>
|
|
83
86
|
);
|
|
84
87
|
}
|
|
85
88
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {CartApiQueryFragment} from 'storefrontapi.generated';
|
|
2
2
|
import type {CartLayout} from '~/components/CartMain';
|
|
3
3
|
import {CartForm, Money, type OptimisticCart} from '@shopify/hydrogen';
|
|
4
|
-
import {useEffect, useRef} from 'react';
|
|
4
|
+
import {useEffect, useId, useRef, useState} from 'react';
|
|
5
5
|
import {useFetcher} from 'react-router';
|
|
6
6
|
|
|
7
7
|
type CartSummaryProps = {
|
|
@@ -12,11 +12,16 @@ type CartSummaryProps = {
|
|
|
12
12
|
export function CartSummary({cart, layout}: CartSummaryProps) {
|
|
13
13
|
const className =
|
|
14
14
|
layout === 'page' ? 'cart-summary-page' : 'cart-summary-aside';
|
|
15
|
+
const summaryId = useId();
|
|
16
|
+
const discountsHeadingId = useId();
|
|
17
|
+
const discountCodeInputId = useId();
|
|
18
|
+
const giftCardHeadingId = useId();
|
|
19
|
+
const giftCardInputId = useId();
|
|
15
20
|
|
|
16
21
|
return (
|
|
17
|
-
<div aria-labelledby=
|
|
18
|
-
<h4>Totals</h4>
|
|
19
|
-
<dl className="cart-subtotal">
|
|
22
|
+
<div aria-labelledby={summaryId} className={className}>
|
|
23
|
+
<h4 id={summaryId}>Totals</h4>
|
|
24
|
+
<dl role="group" className="cart-subtotal">
|
|
20
25
|
<dt>Subtotal</dt>
|
|
21
26
|
<dd>
|
|
22
27
|
{cart?.cost?.subtotalAmount?.amount ? (
|
|
@@ -26,8 +31,16 @@ export function CartSummary({cart, layout}: CartSummaryProps) {
|
|
|
26
31
|
)}
|
|
27
32
|
</dd>
|
|
28
33
|
</dl>
|
|
29
|
-
<CartDiscounts
|
|
30
|
-
|
|
34
|
+
<CartDiscounts
|
|
35
|
+
discountCodes={cart?.discountCodes}
|
|
36
|
+
discountsHeadingId={discountsHeadingId}
|
|
37
|
+
discountCodeInputId={discountCodeInputId}
|
|
38
|
+
/>
|
|
39
|
+
<CartGiftCard
|
|
40
|
+
giftCardCodes={cart?.appliedGiftCards}
|
|
41
|
+
giftCardHeadingId={giftCardHeadingId}
|
|
42
|
+
giftCardInputId={giftCardInputId}
|
|
43
|
+
/>
|
|
31
44
|
<CartCheckoutActions checkoutUrl={cart?.checkoutUrl} />
|
|
32
45
|
</div>
|
|
33
46
|
);
|
|
@@ -48,8 +61,12 @@ function CartCheckoutActions({checkoutUrl}: {checkoutUrl?: string}) {
|
|
|
48
61
|
|
|
49
62
|
function CartDiscounts({
|
|
50
63
|
discountCodes,
|
|
64
|
+
discountsHeadingId,
|
|
65
|
+
discountCodeInputId,
|
|
51
66
|
}: {
|
|
52
67
|
discountCodes?: CartApiQueryFragment['discountCodes'];
|
|
68
|
+
discountsHeadingId: string;
|
|
69
|
+
discountCodeInputId: string;
|
|
53
70
|
}) {
|
|
54
71
|
const codes: string[] =
|
|
55
72
|
discountCodes
|
|
@@ -57,13 +74,17 @@ function CartDiscounts({
|
|
|
57
74
|
?.map(({code}) => code) || [];
|
|
58
75
|
|
|
59
76
|
return (
|
|
60
|
-
<
|
|
77
|
+
<section aria-label="Discounts">
|
|
61
78
|
{/* Have existing discount, display it with a remove option */}
|
|
62
79
|
<dl hidden={!codes.length}>
|
|
63
80
|
<div>
|
|
64
|
-
<dt>
|
|
81
|
+
<dt id={discountsHeadingId}>Discounts</dt>
|
|
65
82
|
<UpdateDiscountForm>
|
|
66
|
-
<div
|
|
83
|
+
<div
|
|
84
|
+
className="cart-discount"
|
|
85
|
+
role="group"
|
|
86
|
+
aria-labelledby={discountsHeadingId}
|
|
87
|
+
>
|
|
67
88
|
<code>{codes?.join(', ')}</code>
|
|
68
89
|
|
|
69
90
|
<button type="submit" aria-label="Remove discount">
|
|
@@ -77,11 +98,11 @@ function CartDiscounts({
|
|
|
77
98
|
{/* Show an input to apply a discount */}
|
|
78
99
|
<UpdateDiscountForm discountCodes={codes}>
|
|
79
100
|
<div>
|
|
80
|
-
<label htmlFor=
|
|
101
|
+
<label htmlFor={discountCodeInputId} className="sr-only">
|
|
81
102
|
Discount code
|
|
82
103
|
</label>
|
|
83
104
|
<input
|
|
84
|
-
id=
|
|
105
|
+
id={discountCodeInputId}
|
|
85
106
|
type="text"
|
|
86
107
|
name="discountCode"
|
|
87
108
|
placeholder="Discount code"
|
|
@@ -92,7 +113,7 @@ function CartDiscounts({
|
|
|
92
113
|
</button>
|
|
93
114
|
</div>
|
|
94
115
|
</UpdateDiscountForm>
|
|
95
|
-
</
|
|
116
|
+
</section>
|
|
96
117
|
);
|
|
97
118
|
}
|
|
98
119
|
|
|
@@ -118,52 +139,110 @@ function UpdateDiscountForm({
|
|
|
118
139
|
|
|
119
140
|
function CartGiftCard({
|
|
120
141
|
giftCardCodes,
|
|
142
|
+
giftCardHeadingId,
|
|
143
|
+
giftCardInputId,
|
|
121
144
|
}: {
|
|
122
145
|
giftCardCodes: CartApiQueryFragment['appliedGiftCards'] | undefined;
|
|
146
|
+
giftCardHeadingId: string;
|
|
147
|
+
giftCardInputId: string;
|
|
123
148
|
}) {
|
|
124
149
|
const giftCardCodeInput = useRef<HTMLInputElement>(null);
|
|
150
|
+
const removeButtonRefs = useRef<Map<string, HTMLButtonElement>>(new Map());
|
|
151
|
+
const previousCardIdsRef = useRef<string[]>([]);
|
|
125
152
|
const giftCardAddFetcher = useFetcher({key: 'gift-card-add'});
|
|
153
|
+
const [removedCardIndex, setRemovedCardIndex] = useState<number | null>(null);
|
|
126
154
|
|
|
127
155
|
useEffect(() => {
|
|
128
156
|
if (giftCardAddFetcher.data) {
|
|
129
|
-
giftCardCodeInput.current
|
|
157
|
+
if (giftCardCodeInput.current !== null) {
|
|
158
|
+
giftCardCodeInput.current.value = '';
|
|
159
|
+
}
|
|
130
160
|
}
|
|
131
161
|
}, [giftCardAddFetcher.data]);
|
|
132
162
|
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
const currentCardIds = giftCardCodes?.map((card) => card.id) || [];
|
|
165
|
+
|
|
166
|
+
if (removedCardIndex !== null && giftCardCodes) {
|
|
167
|
+
const focusTargetIndex = Math.min(
|
|
168
|
+
removedCardIndex,
|
|
169
|
+
giftCardCodes.length - 1,
|
|
170
|
+
);
|
|
171
|
+
const focusTargetCard = giftCardCodes[focusTargetIndex];
|
|
172
|
+
const focusButton = focusTargetCard
|
|
173
|
+
? removeButtonRefs.current.get(focusTargetCard.id)
|
|
174
|
+
: null;
|
|
175
|
+
|
|
176
|
+
if (focusButton) {
|
|
177
|
+
focusButton.focus();
|
|
178
|
+
} else if (giftCardCodeInput.current) {
|
|
179
|
+
giftCardCodeInput.current.focus();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
setRemovedCardIndex(null);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
previousCardIdsRef.current = currentCardIds;
|
|
186
|
+
}, [giftCardCodes, removedCardIndex]);
|
|
187
|
+
|
|
188
|
+
const handleRemoveClick = (cardId: string) => {
|
|
189
|
+
const index = previousCardIdsRef.current.indexOf(cardId);
|
|
190
|
+
if (index !== -1) {
|
|
191
|
+
setRemovedCardIndex(index);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
133
195
|
return (
|
|
134
|
-
<
|
|
196
|
+
<section aria-label="Gift cards">
|
|
135
197
|
{giftCardCodes && giftCardCodes.length > 0 && (
|
|
136
198
|
<dl>
|
|
137
|
-
<dt>Applied Gift Card(s)</dt>
|
|
199
|
+
<dt id={giftCardHeadingId}>Applied Gift Card(s)</dt>
|
|
138
200
|
{giftCardCodes.map((giftCard) => (
|
|
139
|
-
<
|
|
140
|
-
<
|
|
201
|
+
<dd key={giftCard.id} className="cart-discount">
|
|
202
|
+
<RemoveGiftCardForm
|
|
203
|
+
giftCardId={giftCard.id}
|
|
204
|
+
lastCharacters={giftCard.lastCharacters}
|
|
205
|
+
onRemoveClick={() => handleRemoveClick(giftCard.id)}
|
|
206
|
+
buttonRef={(el: HTMLButtonElement | null) => {
|
|
207
|
+
if (el) {
|
|
208
|
+
removeButtonRefs.current.set(giftCard.id, el);
|
|
209
|
+
} else {
|
|
210
|
+
removeButtonRefs.current.delete(giftCard.id);
|
|
211
|
+
}
|
|
212
|
+
}}
|
|
213
|
+
>
|
|
141
214
|
<code>***{giftCard.lastCharacters}</code>
|
|
142
215
|
|
|
143
216
|
<Money data={giftCard.amountUsed} />
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
</div>
|
|
147
|
-
</RemoveGiftCardForm>
|
|
217
|
+
</RemoveGiftCardForm>
|
|
218
|
+
</dd>
|
|
148
219
|
))}
|
|
149
220
|
</dl>
|
|
150
221
|
)}
|
|
151
222
|
|
|
152
223
|
<AddGiftCardForm fetcherKey="gift-card-add">
|
|
153
224
|
<div>
|
|
225
|
+
<label htmlFor={giftCardInputId} className="sr-only">
|
|
226
|
+
Gift card code
|
|
227
|
+
</label>
|
|
154
228
|
<input
|
|
229
|
+
id={giftCardInputId}
|
|
155
230
|
type="text"
|
|
156
231
|
name="giftCardCode"
|
|
157
232
|
placeholder="Gift card code"
|
|
158
233
|
ref={giftCardCodeInput}
|
|
159
234
|
/>
|
|
160
235
|
|
|
161
|
-
<button
|
|
236
|
+
<button
|
|
237
|
+
type="submit"
|
|
238
|
+
disabled={giftCardAddFetcher.state !== 'idle'}
|
|
239
|
+
aria-label="Apply gift card code"
|
|
240
|
+
>
|
|
162
241
|
Apply
|
|
163
242
|
</button>
|
|
164
243
|
</div>
|
|
165
244
|
</AddGiftCardForm>
|
|
166
|
-
</
|
|
245
|
+
</section>
|
|
167
246
|
);
|
|
168
247
|
}
|
|
169
248
|
|
|
@@ -187,10 +266,16 @@ function AddGiftCardForm({
|
|
|
187
266
|
|
|
188
267
|
function RemoveGiftCardForm({
|
|
189
268
|
giftCardId,
|
|
269
|
+
lastCharacters,
|
|
190
270
|
children,
|
|
271
|
+
onRemoveClick,
|
|
272
|
+
buttonRef,
|
|
191
273
|
}: {
|
|
192
274
|
giftCardId: string;
|
|
275
|
+
lastCharacters: string;
|
|
193
276
|
children: React.ReactNode;
|
|
277
|
+
onRemoveClick?: () => void;
|
|
278
|
+
buttonRef?: (el: HTMLButtonElement | null) => void;
|
|
194
279
|
}) {
|
|
195
280
|
return (
|
|
196
281
|
<CartForm
|
|
@@ -201,6 +286,15 @@ function RemoveGiftCardForm({
|
|
|
201
286
|
}}
|
|
202
287
|
>
|
|
203
288
|
{children}
|
|
289
|
+
|
|
290
|
+
<button
|
|
291
|
+
type="submit"
|
|
292
|
+
aria-label={`Remove gift card ending in ${lastCharacters}`}
|
|
293
|
+
onClick={onRemoveClick}
|
|
294
|
+
ref={buttonRef}
|
|
295
|
+
>
|
|
296
|
+
Remove
|
|
297
|
+
</button>
|
|
204
298
|
</CartForm>
|
|
205
299
|
);
|
|
206
300
|
}
|
|
@@ -136,7 +136,7 @@ function SearchToggle() {
|
|
|
136
136
|
);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
function CartBadge({count}: {count: number
|
|
139
|
+
function CartBadge({count}: {count: number}) {
|
|
140
140
|
const {open} = useAside();
|
|
141
141
|
const {publish, shop, cart, prevCart} = useAnalytics();
|
|
142
142
|
|
|
@@ -154,14 +154,14 @@ function CartBadge({count}: {count: number | null}) {
|
|
|
154
154
|
} as CartViewPayload);
|
|
155
155
|
}}
|
|
156
156
|
>
|
|
157
|
-
Cart
|
|
157
|
+
Cart <span aria-label={`(items: ${count})`}>{count}</span>
|
|
158
158
|
</a>
|
|
159
159
|
);
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
function CartToggle({cart}: Pick<HeaderProps, 'cart'>) {
|
|
163
163
|
return (
|
|
164
|
-
<Suspense fallback={<CartBadge count={
|
|
164
|
+
<Suspense fallback={<CartBadge count={0} />}>
|
|
165
165
|
<Await resolve={cart}>
|
|
166
166
|
<CartBanner />
|
|
167
167
|
</Await>
|
|
@@ -2,15 +2,17 @@ import * as React from 'react';
|
|
|
2
2
|
import {Pagination} from '@shopify/hydrogen';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* <PaginatedResourceSection
|
|
5
|
+
* <PaginatedResourceSection> encapsulates the previous and next pagination behaviors throughout your application.
|
|
6
6
|
*/
|
|
7
7
|
export function PaginatedResourceSection<NodesType>({
|
|
8
8
|
connection,
|
|
9
9
|
children,
|
|
10
|
+
ariaLabel,
|
|
10
11
|
resourcesClassName,
|
|
11
12
|
}: {
|
|
12
13
|
connection: React.ComponentProps<typeof Pagination<NodesType>>['connection'];
|
|
13
14
|
children: React.FunctionComponent<{node: NodesType; index: number}>;
|
|
15
|
+
ariaLabel?: string;
|
|
14
16
|
resourcesClassName?: string;
|
|
15
17
|
}) {
|
|
16
18
|
return (
|
|
@@ -23,15 +25,33 @@ export function PaginatedResourceSection<NodesType>({
|
|
|
23
25
|
return (
|
|
24
26
|
<div>
|
|
25
27
|
<PreviousLink>
|
|
26
|
-
{isLoading ?
|
|
28
|
+
{isLoading ? (
|
|
29
|
+
'Loading...'
|
|
30
|
+
) : (
|
|
31
|
+
<span>
|
|
32
|
+
<span aria-hidden="true">↑</span> Load previous
|
|
33
|
+
</span>
|
|
34
|
+
)}
|
|
27
35
|
</PreviousLink>
|
|
28
36
|
{resourcesClassName ? (
|
|
29
|
-
<div
|
|
37
|
+
<div
|
|
38
|
+
aria-label={ariaLabel}
|
|
39
|
+
className={resourcesClassName}
|
|
40
|
+
role={ariaLabel ? 'region' : undefined}
|
|
41
|
+
>
|
|
42
|
+
{resourcesMarkup}
|
|
43
|
+
</div>
|
|
30
44
|
) : (
|
|
31
45
|
resourcesMarkup
|
|
32
46
|
)}
|
|
33
47
|
<NextLink>
|
|
34
|
-
{isLoading ?
|
|
48
|
+
{isLoading ? (
|
|
49
|
+
'Loading...'
|
|
50
|
+
) : (
|
|
51
|
+
<span>
|
|
52
|
+
Load more <span aria-hidden="true">↓</span>
|
|
53
|
+
</span>
|
|
54
|
+
)}
|
|
35
55
|
</NextLink>
|
|
36
56
|
</div>
|
|
37
57
|
);
|
|
@@ -9,7 +9,7 @@ export function ProductPrice({
|
|
|
9
9
|
compareAtPrice?: MoneyV2 | null;
|
|
10
10
|
}) {
|
|
11
11
|
return (
|
|
12
|
-
<div className="product-price">
|
|
12
|
+
<div aria-label="Price" className="product-price" role="group">
|
|
13
13
|
{compareAtPrice ? (
|
|
14
14
|
<div className="product-price-on-sale">
|
|
15
15
|
{price ? <Money data={price} /> : null}
|
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
import type {Route} from './+types/[robots.txt]';
|
|
2
|
-
import {parseGid} from '@shopify/hydrogen';
|
|
3
2
|
|
|
4
|
-
export
|
|
3
|
+
export function loader({request}: Route.LoaderArgs) {
|
|
5
4
|
const url = new URL(request.url);
|
|
6
|
-
|
|
7
|
-
const {shop} = await context.storefront.query(ROBOTS_QUERY);
|
|
8
|
-
|
|
9
|
-
const shopId = parseGid(shop.id).id;
|
|
10
|
-
const body = robotsTxtData({url: url.origin, shopId});
|
|
5
|
+
const body = robotsTxtData({url: url.origin});
|
|
11
6
|
|
|
12
7
|
return new Response(body, {
|
|
13
8
|
status: 200,
|
|
@@ -19,35 +14,31 @@ export async function loader({request, context}: Route.LoaderArgs) {
|
|
|
19
14
|
});
|
|
20
15
|
}
|
|
21
16
|
|
|
22
|
-
function robotsTxtData({url
|
|
17
|
+
function robotsTxtData({url}: {url?: string}) {
|
|
23
18
|
const sitemapUrl = url ? `${url}/sitemap.xml` : undefined;
|
|
24
19
|
|
|
25
20
|
return `
|
|
26
21
|
User-agent: *
|
|
27
|
-
${generalDisallowRules({sitemapUrl
|
|
22
|
+
${generalDisallowRules({sitemapUrl})}
|
|
28
23
|
|
|
29
24
|
# Google adsbot ignores robots.txt unless specifically named!
|
|
30
25
|
User-agent: adsbot-google
|
|
31
|
-
Disallow: /
|
|
32
|
-
Disallow: /
|
|
33
|
-
Disallow: /
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
${shopId ? `Disallow: /${shopId}/orders` : ''}
|
|
37
|
-
Disallow: /*?*oseid=*
|
|
38
|
-
Disallow: /*preview_theme_id*
|
|
39
|
-
Disallow: /*preview_script_id*
|
|
26
|
+
Disallow: /cart
|
|
27
|
+
Disallow: /account
|
|
28
|
+
Disallow: /search
|
|
29
|
+
Allow: /search/
|
|
30
|
+
Disallow: /search/?*
|
|
40
31
|
|
|
41
32
|
User-agent: Nutch
|
|
42
33
|
Disallow: /
|
|
43
34
|
|
|
44
35
|
User-agent: AhrefsBot
|
|
45
36
|
Crawl-delay: 10
|
|
46
|
-
${generalDisallowRules({sitemapUrl
|
|
37
|
+
${generalDisallowRules({sitemapUrl})}
|
|
47
38
|
|
|
48
39
|
User-agent: AhrefsSiteAudit
|
|
49
40
|
Crawl-delay: 10
|
|
50
|
-
${generalDisallowRules({sitemapUrl
|
|
41
|
+
${generalDisallowRules({sitemapUrl})}
|
|
51
42
|
|
|
52
43
|
User-agent: MJ12bot
|
|
53
44
|
Crawl-Delay: 10
|
|
@@ -61,21 +52,8 @@ Crawl-delay: 1
|
|
|
61
52
|
* This function generates disallow rules that generally follow what Shopify's
|
|
62
53
|
* Online Store has as defaults for their robots.txt
|
|
63
54
|
*/
|
|
64
|
-
function generalDisallowRules({
|
|
65
|
-
|
|
66
|
-
sitemapUrl,
|
|
67
|
-
}: {
|
|
68
|
-
shopId?: string;
|
|
69
|
-
sitemapUrl?: string;
|
|
70
|
-
}) {
|
|
71
|
-
return `Disallow: /admin
|
|
72
|
-
Disallow: /cart
|
|
73
|
-
Disallow: /orders
|
|
74
|
-
Disallow: /checkouts/
|
|
75
|
-
Disallow: /checkout
|
|
76
|
-
${shopId ? `Disallow: /${shopId}/checkouts` : ''}
|
|
77
|
-
${shopId ? `Disallow: /${shopId}/orders` : ''}
|
|
78
|
-
Disallow: /carts
|
|
55
|
+
function generalDisallowRules({sitemapUrl}: {sitemapUrl?: string}) {
|
|
56
|
+
return `Disallow: /cart
|
|
79
57
|
Disallow: /account
|
|
80
58
|
Disallow: /collections/*sort_by*
|
|
81
59
|
Disallow: /*/collections/*sort_by*
|
|
@@ -85,33 +63,16 @@ Disallow: /collections/*%2b*
|
|
|
85
63
|
Disallow: /*/collections/*+*
|
|
86
64
|
Disallow: /*/collections/*%2B*
|
|
87
65
|
Disallow: /*/collections/*%2b*
|
|
88
|
-
Disallow:
|
|
66
|
+
Disallow: /*/collections/*filter*&*filter*
|
|
89
67
|
Disallow: /blogs/*+*
|
|
90
68
|
Disallow: /blogs/*%2B*
|
|
91
69
|
Disallow: /blogs/*%2b*
|
|
92
70
|
Disallow: /*/blogs/*+*
|
|
93
71
|
Disallow: /*/blogs/*%2B*
|
|
94
72
|
Disallow: /*/blogs/*%2b*
|
|
95
|
-
Disallow: /*?*oseid=*
|
|
96
|
-
Disallow: /*preview_theme_id*
|
|
97
|
-
Disallow: /*preview_script_id*
|
|
98
73
|
Disallow: /policies/
|
|
99
|
-
Disallow: /*/*?*ls=*&ls=*
|
|
100
|
-
Disallow: /*/*?*ls%3D*%3Fls%3D*
|
|
101
|
-
Disallow: /*/*?*ls%3d*%3fls%3d*
|
|
102
74
|
Disallow: /search
|
|
103
75
|
Allow: /search/
|
|
104
76
|
Disallow: /search/?*
|
|
105
|
-
Disallow: /apple-app-site-association
|
|
106
|
-
Disallow: /.well-known/shopify/monorail
|
|
107
77
|
${sitemapUrl ? `Sitemap: ${sitemapUrl}` : ''}`;
|
|
108
78
|
}
|
|
109
|
-
|
|
110
|
-
const ROBOTS_QUERY = `#graphql
|
|
111
|
-
query StoreRobots($country: CountryCode, $language: LanguageCode)
|
|
112
|
-
@inContext(country: $country, language: $language) {
|
|
113
|
-
shop {
|
|
114
|
-
id
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
` as const;
|
|
@@ -83,7 +83,11 @@ function FeaturedCollection({
|
|
|
83
83
|
>
|
|
84
84
|
{image && (
|
|
85
85
|
<div className="featured-collection-image">
|
|
86
|
-
<Image
|
|
86
|
+
<Image
|
|
87
|
+
data={image}
|
|
88
|
+
sizes="100vw"
|
|
89
|
+
alt={image.altText || collection.title}
|
|
90
|
+
/>
|
|
87
91
|
</div>
|
|
88
92
|
)}
|
|
89
93
|
<h1>{collection.title}</h1>
|
|
@@ -97,8 +101,11 @@ function RecommendedProducts({
|
|
|
97
101
|
products: Promise<RecommendedProductsQuery | null>;
|
|
98
102
|
}) {
|
|
99
103
|
return (
|
|
100
|
-
<
|
|
101
|
-
|
|
104
|
+
<section
|
|
105
|
+
className="recommended-products"
|
|
106
|
+
aria-labelledby="recommended-products"
|
|
107
|
+
>
|
|
108
|
+
<h2 id="recommended-products">Recommended Products</h2>
|
|
102
109
|
<Suspense fallback={<div>Loading...</div>}>
|
|
103
110
|
<Await resolve={products}>
|
|
104
111
|
{(response) => (
|
|
@@ -113,7 +120,7 @@ function RecommendedProducts({
|
|
|
113
120
|
</Await>
|
|
114
121
|
</Suspense>
|
|
115
122
|
<br />
|
|
116
|
-
</
|
|
123
|
+
</section>
|
|
117
124
|
);
|
|
118
125
|
}
|
|
119
126
|
|
|
@@ -3,7 +3,7 @@ import type {Route} from './+types/account.$';
|
|
|
3
3
|
|
|
4
4
|
// fallback wild card for all unauthenticated routes in account section
|
|
5
5
|
export async function loader({context}: Route.LoaderArgs) {
|
|
6
|
-
context.customerAccount.handleAuthStatus();
|
|
6
|
+
await context.customerAccount.handleAuthStatus();
|
|
7
7
|
|
|
8
8
|
return redirect('/account');
|
|
9
9
|
}
|
|
@@ -32,7 +32,7 @@ export const meta: Route.MetaFunction = () => {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
export async function loader({context}: Route.LoaderArgs) {
|
|
35
|
-
context.customerAccount.handleAuthStatus();
|
|
35
|
+
await context.customerAccount.handleAuthStatus();
|
|
36
36
|
|
|
37
37
|
return {};
|
|
38
38
|
}
|
|
@@ -468,7 +468,7 @@ export function AddressForm({
|
|
|
468
468
|
/>
|
|
469
469
|
<label htmlFor="territoryCode">Country Code*</label>
|
|
470
470
|
<input
|
|
471
|
-
aria-label="
|
|
471
|
+
aria-label="Country code"
|
|
472
472
|
autoComplete="country"
|
|
473
473
|
defaultValue={address?.territoryCode ?? ''}
|
|
474
474
|
id="territoryCode"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "skeleton",
|
|
3
3
|
"private": true,
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "2026.1.
|
|
5
|
+
"version": "2026.1.3",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"build": "shopify hydrogen build --codegen",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"prettier": "@shopify/prettier-config",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@shopify/hydrogen": "
|
|
17
|
+
"@shopify/hydrogen": "2026.1.3",
|
|
18
18
|
"graphql": "^16.10.0",
|
|
19
19
|
"graphql-tag": "^2.12.6",
|
|
20
20
|
"isbot": "^5.1.22",
|
|
@@ -30,9 +30,9 @@
|
|
|
30
30
|
"@graphql-codegen/cli": "5.0.2",
|
|
31
31
|
"@react-router/dev": "7.12.0",
|
|
32
32
|
"@react-router/fs-routes": "7.12.0",
|
|
33
|
-
"@shopify/cli": "3.
|
|
33
|
+
"@shopify/cli": "3.91.1",
|
|
34
34
|
"@shopify/hydrogen-codegen": "0.3.3",
|
|
35
|
-
"@shopify/mini-oxygen": "4.0.
|
|
35
|
+
"@shopify/mini-oxygen": "4.0.2",
|
|
36
36
|
"@shopify/oxygen-workers-types": "^4.1.6",
|
|
37
37
|
"@shopify/prettier-config": "^1.1.2",
|
|
38
38
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
@@ -377,13 +377,6 @@ export type FooterQuery = {
|
|
|
377
377
|
>;
|
|
378
378
|
};
|
|
379
379
|
|
|
380
|
-
export type StoreRobotsQueryVariables = StorefrontAPI.Exact<{
|
|
381
|
-
country?: StorefrontAPI.InputMaybe<StorefrontAPI.CountryCode>;
|
|
382
|
-
language?: StorefrontAPI.InputMaybe<StorefrontAPI.LanguageCode>;
|
|
383
|
-
}>;
|
|
384
|
-
|
|
385
|
-
export type StoreRobotsQuery = {shop: Pick<StorefrontAPI.Shop, 'id'>};
|
|
386
|
-
|
|
387
380
|
export type FeaturedCollectionFragment = Pick<
|
|
388
381
|
StorefrontAPI.Collection,
|
|
389
382
|
'id' | 'title' | 'handle'
|
|
@@ -1283,10 +1276,6 @@ interface GeneratedQueryTypes {
|
|
|
1283
1276
|
return: FooterQuery;
|
|
1284
1277
|
variables: FooterQueryVariables;
|
|
1285
1278
|
};
|
|
1286
|
-
'#graphql\n query StoreRobots($country: CountryCode, $language: LanguageCode)\n @inContext(country: $country, language: $language) {\n shop {\n id\n }\n }\n': {
|
|
1287
|
-
return: StoreRobotsQuery;
|
|
1288
|
-
variables: StoreRobotsQueryVariables;
|
|
1289
|
-
};
|
|
1290
1279
|
'#graphql\n fragment FeaturedCollection on Collection {\n id\n title\n image {\n id\n url\n altText\n width\n height\n }\n handle\n }\n query FeaturedCollection($country: CountryCode, $language: LanguageCode)\n @inContext(country: $country, language: $language) {\n collections(first: 1, sortKey: UPDATED_AT, reverse: true) {\n nodes {\n ...FeaturedCollection\n }\n }\n }\n': {
|
|
1291
1280
|
return: FeaturedCollectionQuery;
|
|
1292
1281
|
variables: FeaturedCollectionQueryVariables;
|
|
@@ -8,7 +8,7 @@ export default defineConfig({
|
|
|
8
8
|
plugins: [hydrogen(), oxygen(), reactRouter(), tsconfigPaths()],
|
|
9
9
|
build: {
|
|
10
10
|
// Allow a strict Content-Security-Policy
|
|
11
|
-
//
|
|
11
|
+
// without inlining assets as base64:
|
|
12
12
|
assetsInlineLimit: 0,
|
|
13
13
|
},
|
|
14
14
|
ssr: {
|
|
@@ -7,7 +7,7 @@ import { renderSuccess, renderInfo } from '@shopify/cli-kit/node/ui';
|
|
|
7
7
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
8
8
|
import { removeFile } from '@shopify/cli-kit/node/fs';
|
|
9
9
|
import { setH2OVerbose, isH2Verbose, muteDevLogs, enhanceH2Logs } from '../../lib/log.js';
|
|
10
|
-
import { commonFlags, overrideFlag, flagsToCamelObject,
|
|
10
|
+
import { commonFlags, overrideFlag, flagsToCamelObject, DEFAULT_APP_PORT, DEFAULT_INSPECTOR_PORT } from '../../lib/flags.js';
|
|
11
11
|
import { spawnCodegenProcess } from '../../lib/codegen.js';
|
|
12
12
|
import { getAllEnvironmentVariables } from '../../lib/environment-variables.js';
|
|
13
13
|
import { displayDevUpgradeNotice } from './upgrade.js';
|
|
@@ -103,6 +103,9 @@ async function runDev({
|
|
|
103
103
|
if (!process.env.NODE_ENV) process.env.NODE_ENV = "development";
|
|
104
104
|
if (verbose) setH2OVerbose();
|
|
105
105
|
if (!isH2Verbose()) muteDevLogs();
|
|
106
|
+
if (appPort === 0) {
|
|
107
|
+
appPort = await findPort(DEFAULT_APP_PORT);
|
|
108
|
+
}
|
|
106
109
|
const root = appPath ?? process.cwd();
|
|
107
110
|
const cliCommandPromise = getCliCommand(root);
|
|
108
111
|
const backgroundPromise = getDevConfigInBackground(
|
|
@@ -6,7 +6,7 @@ import { ensureInsideGitDirectory, isClean } from '@shopify/cli-kit/node/git';
|
|
|
6
6
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
7
7
|
import { renderSuccess, renderInfo, renderConfirmationPrompt, renderTasks, renderSelectPrompt, renderWarning } from '@shopify/cli-kit/node/ui';
|
|
8
8
|
import { isDirectory, readFile, mkdir, fileExists, touchFile, removeFile, writeFile } from '@shopify/cli-kit/node/fs';
|
|
9
|
-
import { getPackageManager, getDependencies
|
|
9
|
+
import { getPackageManager, getDependencies } from '@shopify/cli-kit/node/node-package-manager';
|
|
10
10
|
import { exec } from '@shopify/cli-kit/node/system';
|
|
11
11
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
12
12
|
import { resolvePath, joinPath, dirname } from '@shopify/cli-kit/node/path';
|
|
@@ -106,7 +106,7 @@ async function runUpgrade({
|
|
|
106
106
|
availableUpgrades,
|
|
107
107
|
currentDependencies
|
|
108
108
|
});
|
|
109
|
-
cumulativeRelease =
|
|
109
|
+
cumulativeRelease = getCumulativeRelease({
|
|
110
110
|
availableUpgrades,
|
|
111
111
|
currentVersion,
|
|
112
112
|
currentDependencies,
|
|
@@ -128,7 +128,11 @@ async function runUpgrade({
|
|
|
128
128
|
appPath,
|
|
129
129
|
selectedRelease,
|
|
130
130
|
currentDependencies,
|
|
131
|
-
targetVersion
|
|
131
|
+
targetVersion,
|
|
132
|
+
cumulativeRemoveDependencies: cumulativeRelease.removeDependencies,
|
|
133
|
+
cumulativeRemoveDevDependencies: cumulativeRelease.removeDevDependencies,
|
|
134
|
+
cumulativeDependencies: cumulativeRelease.dependencies,
|
|
135
|
+
cumulativeDevDependencies: cumulativeRelease.devDependencies
|
|
132
136
|
});
|
|
133
137
|
await validateUpgrade({
|
|
134
138
|
appPath,
|
|
@@ -336,20 +340,31 @@ async function getSelectedRelease({
|
|
|
336
340
|
) : void 0;
|
|
337
341
|
return targetRelease ?? promptUpgradeOptions(currentVersion, availableUpgrades);
|
|
338
342
|
}
|
|
339
|
-
function
|
|
343
|
+
function getCumulativeRelease({
|
|
340
344
|
availableUpgrades,
|
|
341
345
|
selectedRelease,
|
|
342
346
|
currentVersion,
|
|
343
347
|
currentDependencies
|
|
344
348
|
}) {
|
|
345
349
|
const currentPinnedVersion = getAbsoluteVersion(currentVersion);
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
350
|
+
const empty = {
|
|
351
|
+
features: [],
|
|
352
|
+
fixes: [],
|
|
353
|
+
removeDependencies: [],
|
|
354
|
+
removeDevDependencies: [],
|
|
355
|
+
dependencies: {},
|
|
356
|
+
devDependencies: {}
|
|
357
|
+
};
|
|
358
|
+
if (!availableUpgrades?.length) return empty;
|
|
349
359
|
if (selectedRelease.dependencies?.["@shopify/hydrogen"] === "next") {
|
|
350
360
|
return {
|
|
361
|
+
...empty,
|
|
351
362
|
features: selectedRelease.features || [],
|
|
352
|
-
fixes: selectedRelease.fixes || []
|
|
363
|
+
fixes: selectedRelease.fixes || [],
|
|
364
|
+
removeDependencies: selectedRelease.removeDependencies ?? [],
|
|
365
|
+
removeDevDependencies: selectedRelease.removeDevDependencies ?? [],
|
|
366
|
+
dependencies: selectedRelease.dependencies ?? {},
|
|
367
|
+
devDependencies: selectedRelease.devDependencies ?? {}
|
|
353
368
|
};
|
|
354
369
|
}
|
|
355
370
|
const upgradingReleases = availableUpgrades.filter((release) => {
|
|
@@ -359,14 +374,65 @@ function getCummulativeRelease({
|
|
|
359
374
|
if (!isSameHydrogenVersion || !currentDependencies) return false;
|
|
360
375
|
return hasOutdatedDependencies({ release, currentDependencies });
|
|
361
376
|
});
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return acc;
|
|
367
|
-
},
|
|
368
|
-
{ features: [], fixes: [] }
|
|
377
|
+
const features = upgradingReleases.flatMap((r) => r.features);
|
|
378
|
+
const fixes = upgradingReleases.flatMap((r) => r.fixes);
|
|
379
|
+
const releasesByVersion = [...upgradingReleases].sort(
|
|
380
|
+
(a, b) => semver.compare(a.version, b.version)
|
|
369
381
|
);
|
|
382
|
+
const removedDepsAt = /* @__PURE__ */ new Map();
|
|
383
|
+
const removedDevDepsAt = /* @__PURE__ */ new Map();
|
|
384
|
+
releasesByVersion.forEach((release, i) => {
|
|
385
|
+
release.removeDependencies?.forEach((dep) => {
|
|
386
|
+
removedDepsAt.set(dep, i);
|
|
387
|
+
});
|
|
388
|
+
release.removeDevDependencies?.forEach((dep) => {
|
|
389
|
+
removedDevDepsAt.set(dep, i);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
const reinstalledDeps = /* @__PURE__ */ new Set();
|
|
393
|
+
const reinstalledDevDeps = /* @__PURE__ */ new Set();
|
|
394
|
+
for (let i = 0; i < releasesByVersion.length; i++) {
|
|
395
|
+
const release = releasesByVersion[i];
|
|
396
|
+
if (!release) continue;
|
|
397
|
+
const dependencies2 = release.dependencies ?? {};
|
|
398
|
+
const devDependencies2 = release.devDependencies ?? {};
|
|
399
|
+
Object.keys(dependencies2).forEach((dep) => {
|
|
400
|
+
const removalI = removedDepsAt.get(dep);
|
|
401
|
+
if (removalI !== void 0 && i >= removalI) {
|
|
402
|
+
reinstalledDeps.add(dep);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
Object.keys(devDependencies2).forEach((dep) => {
|
|
406
|
+
const removalI = removedDevDepsAt.get(dep);
|
|
407
|
+
if (removalI !== void 0 && i >= removalI) {
|
|
408
|
+
reinstalledDevDeps.add(dep);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
const removeDependencies = [
|
|
413
|
+
...new Set(
|
|
414
|
+
releasesByVersion.flatMap((r) => r.removeDependencies ?? []).filter((dep) => !reinstalledDeps.has(dep))
|
|
415
|
+
)
|
|
416
|
+
];
|
|
417
|
+
const removeDevDependencies = [
|
|
418
|
+
...new Set(
|
|
419
|
+
releasesByVersion.flatMap((r) => r.removeDevDependencies ?? []).filter((dep) => !reinstalledDevDeps.has(dep))
|
|
420
|
+
)
|
|
421
|
+
];
|
|
422
|
+
const dependencies = {};
|
|
423
|
+
const devDependencies = {};
|
|
424
|
+
for (const release of releasesByVersion) {
|
|
425
|
+
Object.assign(dependencies, release.dependencies ?? {});
|
|
426
|
+
Object.assign(devDependencies, release.devDependencies ?? {});
|
|
427
|
+
}
|
|
428
|
+
return {
|
|
429
|
+
features,
|
|
430
|
+
fixes,
|
|
431
|
+
removeDependencies,
|
|
432
|
+
removeDevDependencies,
|
|
433
|
+
dependencies,
|
|
434
|
+
devDependencies
|
|
435
|
+
};
|
|
370
436
|
}
|
|
371
437
|
function displayConfirmation({
|
|
372
438
|
cumulativeRelease,
|
|
@@ -377,7 +443,7 @@ function displayConfirmation({
|
|
|
377
443
|
if (features.length || fixes.length) {
|
|
378
444
|
renderInfo({
|
|
379
445
|
headline: `Included in this upgrade:`,
|
|
380
|
-
|
|
446
|
+
// @ts-expect-error - filter(Boolean) removes falsy values, leaving only objects
|
|
381
447
|
customSections: [
|
|
382
448
|
features.length && {
|
|
383
449
|
title: "Features",
|
|
@@ -461,10 +527,20 @@ function maybeIncludeDependency({
|
|
|
461
527
|
function buildUpgradeCommandArgs({
|
|
462
528
|
selectedRelease,
|
|
463
529
|
currentDependencies,
|
|
464
|
-
targetVersion
|
|
530
|
+
targetVersion,
|
|
531
|
+
cumulativeDependencies,
|
|
532
|
+
cumulativeDevDependencies
|
|
465
533
|
}) {
|
|
466
534
|
const args = [];
|
|
467
|
-
|
|
535
|
+
const effectiveDependencies = {
|
|
536
|
+
...cumulativeDependencies ?? {},
|
|
537
|
+
...selectedRelease.dependencies
|
|
538
|
+
};
|
|
539
|
+
const effectiveDevDependencies = {
|
|
540
|
+
...cumulativeDevDependencies ?? {},
|
|
541
|
+
...selectedRelease.devDependencies
|
|
542
|
+
};
|
|
543
|
+
for (const dependency of Object.entries(effectiveDependencies)) {
|
|
468
544
|
const shouldUpgradeDep = maybeIncludeDependency({
|
|
469
545
|
currentDependencies,
|
|
470
546
|
dependency,
|
|
@@ -480,7 +556,7 @@ function buildUpgradeCommandArgs({
|
|
|
480
556
|
)}`
|
|
481
557
|
);
|
|
482
558
|
}
|
|
483
|
-
for (const dependency of Object.entries(
|
|
559
|
+
for (const dependency of Object.entries(effectiveDevDependencies)) {
|
|
484
560
|
const shouldUpgradeDep = maybeIncludeDependency({
|
|
485
561
|
currentDependencies,
|
|
486
562
|
dependency,
|
|
@@ -497,7 +573,7 @@ function buildUpgradeCommandArgs({
|
|
|
497
573
|
);
|
|
498
574
|
}
|
|
499
575
|
const currentRemix = Object.entries(currentDependencies).find(isRemixDependency);
|
|
500
|
-
const selectedRemix = Object.entries(
|
|
576
|
+
const selectedRemix = Object.entries(effectiveDependencies).find(
|
|
501
577
|
isRemixDependency
|
|
502
578
|
);
|
|
503
579
|
if (currentRemix && selectedRemix) {
|
|
@@ -514,7 +590,7 @@ function buildUpgradeCommandArgs({
|
|
|
514
590
|
const currentReactRouter = Object.entries(currentDependencies).find(
|
|
515
591
|
isReactRouterDependency
|
|
516
592
|
);
|
|
517
|
-
const selectedReactRouter = Object.entries(
|
|
593
|
+
const selectedReactRouter = Object.entries(effectiveDependencies).find(
|
|
518
594
|
isReactRouterDependency
|
|
519
595
|
);
|
|
520
596
|
if (selectedReactRouter) {
|
|
@@ -537,12 +613,16 @@ async function upgradeNodeModules({
|
|
|
537
613
|
appPath,
|
|
538
614
|
selectedRelease,
|
|
539
615
|
currentDependencies,
|
|
540
|
-
targetVersion
|
|
616
|
+
targetVersion,
|
|
617
|
+
cumulativeRemoveDependencies,
|
|
618
|
+
cumulativeRemoveDevDependencies,
|
|
619
|
+
cumulativeDependencies,
|
|
620
|
+
cumulativeDevDependencies
|
|
541
621
|
}) {
|
|
542
622
|
const tasks = [];
|
|
543
623
|
const depsToRemove = [
|
|
544
|
-
...
|
|
545
|
-
...
|
|
624
|
+
...cumulativeRemoveDependencies,
|
|
625
|
+
...cumulativeRemoveDevDependencies
|
|
546
626
|
].filter((dep) => dep in currentDependencies);
|
|
547
627
|
if (depsToRemove.length > 0) {
|
|
548
628
|
tasks.push({
|
|
@@ -559,17 +639,24 @@ async function upgradeNodeModules({
|
|
|
559
639
|
const upgradeArgs = buildUpgradeCommandArgs({
|
|
560
640
|
selectedRelease,
|
|
561
641
|
currentDependencies,
|
|
562
|
-
targetVersion
|
|
642
|
+
targetVersion,
|
|
643
|
+
cumulativeDependencies,
|
|
644
|
+
cumulativeDevDependencies
|
|
563
645
|
});
|
|
564
646
|
if (upgradeArgs.length > 0) {
|
|
565
647
|
tasks.push({
|
|
566
648
|
title: `Upgrading dependencies`,
|
|
567
649
|
task: async () => {
|
|
568
|
-
await
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
650
|
+
const packageManager = await getPackageManager(appPath);
|
|
651
|
+
const command = packageManager === "npm" ? "install" : packageManager === "yarn" ? "add" : packageManager === "pnpm" ? "add" : packageManager === "bun" ? "install" : "install";
|
|
652
|
+
const extraArgs = packageManager === "npm" || packageManager === "unknown" ? ["--legacy-peer-deps"] : [];
|
|
653
|
+
await exec(
|
|
654
|
+
resolvePackageManagerName(packageManager),
|
|
655
|
+
[command, ...extraArgs, ...upgradeArgs],
|
|
656
|
+
{
|
|
657
|
+
cwd: appPath
|
|
658
|
+
}
|
|
659
|
+
);
|
|
573
660
|
}
|
|
574
661
|
});
|
|
575
662
|
}
|
|
@@ -577,6 +664,9 @@ async function upgradeNodeModules({
|
|
|
577
664
|
await renderTasks(tasks, {});
|
|
578
665
|
}
|
|
579
666
|
}
|
|
667
|
+
function resolvePackageManagerName(packageManager) {
|
|
668
|
+
return packageManager === "unknown" ? "npm" : packageManager;
|
|
669
|
+
}
|
|
580
670
|
async function uninstallNodeModules({
|
|
581
671
|
directory,
|
|
582
672
|
packageManager,
|
|
@@ -584,8 +674,12 @@ async function uninstallNodeModules({
|
|
|
584
674
|
}) {
|
|
585
675
|
if (args.length === 0) return;
|
|
586
676
|
const command = packageManager === "npm" ? "uninstall" : packageManager === "yarn" ? "remove" : packageManager === "pnpm" ? "remove" : packageManager === "bun" ? "remove" : "uninstall";
|
|
587
|
-
const
|
|
588
|
-
await exec(
|
|
677
|
+
const extraArgs = packageManager === "npm" || packageManager === "unknown" ? ["--legacy-peer-deps"] : [];
|
|
678
|
+
await exec(
|
|
679
|
+
resolvePackageManagerName(packageManager),
|
|
680
|
+
[command, ...extraArgs, ...args],
|
|
681
|
+
{ cwd: directory }
|
|
682
|
+
);
|
|
589
683
|
}
|
|
590
684
|
function appendRemixDependencies({
|
|
591
685
|
currentDependencies,
|
|
@@ -954,4 +1048,4 @@ async function displayDevUpgradeNotice({
|
|
|
954
1048
|
}
|
|
955
1049
|
}
|
|
956
1050
|
|
|
957
|
-
export { buildUpgradeCommandArgs, Upgrade as default, displayConfirmation, displayDevUpgradeNotice, getAbsoluteVersion, getAvailableUpgrades, getChangelog,
|
|
1051
|
+
export { buildUpgradeCommandArgs, Upgrade as default, displayConfirmation, displayDevUpgradeNotice, getAbsoluteVersion, getAvailableUpgrades, getChangelog, getCumulativeRelease, getHydrogenVersion, getPackageVersion, getSelectedRelease, isRunningFromHydrogenMonorepo, runUpgrade, upgradeNodeModules, validateUpgrade };
|
|
@@ -277,6 +277,8 @@ async function handleDependencies(projectDir, controller, packageManagerFromFlag
|
|
|
277
277
|
cancellationMessage: "No",
|
|
278
278
|
abortSignal: controller.signal
|
|
279
279
|
});
|
|
280
|
+
} else {
|
|
281
|
+
actualPackageManager = detectedPackageManager;
|
|
280
282
|
}
|
|
281
283
|
}
|
|
282
284
|
if (isHydrogenMonorepo) {
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { readFileSync, existsSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
function isErrnoException(err) {
|
|
5
|
+
return err instanceof Error && "code" in err;
|
|
6
|
+
}
|
|
7
|
+
const MARKER = "// [hydrogen-monorepo-patch]";
|
|
8
|
+
function getRunJsPath(root) {
|
|
9
|
+
return resolve(root, "node_modules", "@shopify", "cli", "bin", "run.js");
|
|
10
|
+
}
|
|
11
|
+
function isPatchApplied(content) {
|
|
12
|
+
return content.includes(MARKER);
|
|
13
|
+
}
|
|
14
|
+
function generatePatchedContent() {
|
|
15
|
+
return `#!/usr/bin/env node
|
|
16
|
+
${MARKER}
|
|
17
|
+
|
|
18
|
+
// Mirrors upstream @shopify/cli/bin/run.js behavior.
|
|
19
|
+
// This is not introduced by the patch \u2014 @shopify/cli ships with this line.
|
|
20
|
+
// Changing it here would create a behavioral divergence between dev and production.
|
|
21
|
+
process.removeAllListeners('warning')
|
|
22
|
+
|
|
23
|
+
// --- Monorepo detection ---
|
|
24
|
+
// Walk up from cwd to find the hydrogen monorepo root.
|
|
25
|
+
// We look for packages/cli/package.json with the correct package name \u2014
|
|
26
|
+
// if it matches, we know we're inside the monorepo and should load the
|
|
27
|
+
// local @shopify/cli-hydrogen
|
|
28
|
+
const {existsSync, readFileSync} = await import('node:fs');
|
|
29
|
+
const {resolve, dirname} = await import('node:path');
|
|
30
|
+
|
|
31
|
+
let monorepoRoot = null;
|
|
32
|
+
let dir = process.cwd();
|
|
33
|
+
while (true) {
|
|
34
|
+
const candidate = resolve(dir, 'packages', 'cli', 'package.json');
|
|
35
|
+
if (existsSync(candidate)) {
|
|
36
|
+
try {
|
|
37
|
+
const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
|
|
38
|
+
if (pkg.name === '@shopify/cli-hydrogen') {
|
|
39
|
+
monorepoRoot = dir;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
// Ignore malformed package.json and keep walking
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const parent = dirname(dir);
|
|
47
|
+
if (parent === dir) break; // reached filesystem root
|
|
48
|
+
dir = parent;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (monorepoRoot) {
|
|
52
|
+
// We're in the hydrogen monorepo. Start @shopify/cli normally but
|
|
53
|
+
// inject pluginAdditions so oclif loads @shopify/cli-hydrogen from
|
|
54
|
+
// the monorepo's workspace (packages/cli) instead of the version
|
|
55
|
+
// bundled inside @shopify/cli/dist.
|
|
56
|
+
//
|
|
57
|
+
// This uses the same pluginAdditions mechanism that ShopifyConfig
|
|
58
|
+
// uses internally
|
|
59
|
+
const {fileURLToPath} = await import('node:url');
|
|
60
|
+
const {Config, run, flush} = await import('@oclif/core');
|
|
61
|
+
|
|
62
|
+
// root must point to @shopify/cli's installed location so oclif
|
|
63
|
+
// can find its package.json, oclif config, and bundled commands.
|
|
64
|
+
const cliRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
65
|
+
|
|
66
|
+
const c = '\\x1b[38;5;209m';
|
|
67
|
+
const d = '\\x1b[2m';
|
|
68
|
+
const r = '\\x1b[0m';
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log(c + ' \u250C\u2500\u2500 hydrogen-monorepo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510' + r);
|
|
71
|
+
console.log(c + ' \u2502' + r + ' ' + c + '\u2502' + r);
|
|
72
|
+
console.log(c + ' \u2502' + r + ' Using local cli-hydrogen plugin from packages/cli ' + c + '\u2502' + r);
|
|
73
|
+
console.log(c + ' \u2502' + r + d + ' Bundled commands replaced with local source ' + r + c + '\u2502' + r);
|
|
74
|
+
console.log(c + ' \u2502' + r + ' ' + c + '\u2502' + r);
|
|
75
|
+
console.log(c + ' \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518' + r);
|
|
76
|
+
console.log('');
|
|
77
|
+
|
|
78
|
+
// Tell ShopifyConfig to skip its own monorepo detection since we
|
|
79
|
+
// are handling pluginAdditions ourselves.
|
|
80
|
+
process.env.IGNORE_HYDROGEN_MONOREPO = '1';
|
|
81
|
+
|
|
82
|
+
const config = new Config({
|
|
83
|
+
root: cliRoot,
|
|
84
|
+
// pluginAdditions tells oclif's plugin loader to read the
|
|
85
|
+
// monorepo root's package.json, find @shopify/cli-hydrogen in
|
|
86
|
+
// its dependencies (workspace:*), and load that as a core plugin.
|
|
87
|
+
// Because workspace:* symlinks to packages/cli, oclif loads the
|
|
88
|
+
// local source code \u2014 exactly what we want for development.
|
|
89
|
+
pluginAdditions: {
|
|
90
|
+
core: ['@shopify/cli-hydrogen'],
|
|
91
|
+
path: monorepoRoot,
|
|
92
|
+
},
|
|
93
|
+
// Skip the oclif manifest cache so commands are loaded fresh from
|
|
94
|
+
// disk rather than from a potentially stale oclif.manifest.json.
|
|
95
|
+
ignoreManifest: true,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
await config.load();
|
|
99
|
+
|
|
100
|
+
// --- Post-load command replacement ---
|
|
101
|
+
// After loading, both the bundled hydrogen commands (from @shopify/cli's
|
|
102
|
+
// root plugin) and the local ones (from pluginAdditions) are registered.
|
|
103
|
+
// Since @shopify/cli@3.83.0 (which upgraded to oclif v4),
|
|
104
|
+
// determinePriority is no longer an overridable instance method, so we
|
|
105
|
+
// manually replace the bundled commands with the external plugin's
|
|
106
|
+
// versions using oclif's private _commands Map.
|
|
107
|
+
const externalPlugin = Array.from(config.plugins.values()).find(
|
|
108
|
+
(p) => p.name === '@shopify/cli-hydrogen' && !p.isRoot,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
if (!externalPlugin) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
'[hydrogen-monorepo] Could not find local @shopify/cli-hydrogen plugin. ' +
|
|
114
|
+
'The patch may need updating for the current @shopify/cli version.'
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const cmds = config._commands;
|
|
119
|
+
if (!cmds || !(cmds instanceof Map)) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
'[hydrogen-monorepo] Cannot replace bundled commands \u2014 oclif internals changed. ' +
|
|
122
|
+
'The patch may need updating for the current oclif version.'
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Delete bundled hydrogen commands (canonical IDs + aliases + hidden aliases)
|
|
127
|
+
for (const command of externalPlugin.commands) {
|
|
128
|
+
if (!command.id.startsWith('hydrogen')) continue;
|
|
129
|
+
cmds.delete(command.id);
|
|
130
|
+
for (const alias of [...(command.aliases ?? []), ...(command.hiddenAliases ?? [])]) {
|
|
131
|
+
cmds.delete(alias);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Re-insert commands from the local plugin. loadCommands handles
|
|
135
|
+
// alias registration and command permutations correctly.
|
|
136
|
+
config.loadCommands(externalPlugin);
|
|
137
|
+
|
|
138
|
+
await run(process.argv.slice(2), config);
|
|
139
|
+
await flush();
|
|
140
|
+
} else {
|
|
141
|
+
// Not in the monorepo \u2014 run the standard @shopify/cli entrypoint.
|
|
142
|
+
const {default: runCLI} = await import('../dist/index.js');
|
|
143
|
+
runCLI({development: false});
|
|
144
|
+
}
|
|
145
|
+
`;
|
|
146
|
+
}
|
|
147
|
+
function generateOriginalContent() {
|
|
148
|
+
return `#!/usr/bin/env node
|
|
149
|
+
|
|
150
|
+
process.removeAllListeners('warning')
|
|
151
|
+
|
|
152
|
+
import runCLI from '../dist/index.js'
|
|
153
|
+
|
|
154
|
+
runCLI({development: false})
|
|
155
|
+
`;
|
|
156
|
+
}
|
|
157
|
+
function applyPatch(runJsPath) {
|
|
158
|
+
let current;
|
|
159
|
+
try {
|
|
160
|
+
current = readFileSync(runJsPath, "utf8");
|
|
161
|
+
} catch (err) {
|
|
162
|
+
if (isErrnoException(err) && err.code === "ENOENT") {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`@shopify/cli is not installed (${runJsPath} not found). Run 'pnpm install' first.`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
if (isPatchApplied(current)) return false;
|
|
170
|
+
const backupPath = runJsPath + ".backup";
|
|
171
|
+
if (!existsSync(backupPath)) {
|
|
172
|
+
writeFileSync(backupPath, current);
|
|
173
|
+
}
|
|
174
|
+
writeFileSync(runJsPath, generatePatchedContent());
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
function removePatch(runJsPath) {
|
|
178
|
+
let current;
|
|
179
|
+
try {
|
|
180
|
+
current = readFileSync(runJsPath, "utf8");
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if (isErrnoException(err) && err.code === "ENOENT") {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
if (!isPatchApplied(current)) return false;
|
|
188
|
+
const backupPath = runJsPath + ".backup";
|
|
189
|
+
const hasBackup = existsSync(backupPath);
|
|
190
|
+
const original = hasBackup ? readFileSync(backupPath, "utf8") : generateOriginalContent();
|
|
191
|
+
writeFileSync(runJsPath, original);
|
|
192
|
+
if (hasBackup) {
|
|
193
|
+
unlinkSync(backupPath);
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { MARKER, applyPatch, generateOriginalContent, generatePatchedContent, getRunJsPath, isPatchApplied, removePatch };
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import { createReadStream } from 'node:fs';
|
|
1
2
|
import { execFileSync } from 'node:child_process';
|
|
2
3
|
import { readFile, writeFile, mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
+
import { join, isAbsolute } from 'node:path';
|
|
5
|
+
import { pipeline } from 'node:stream/promises';
|
|
4
6
|
import { tmpdir } from 'node:os';
|
|
7
|
+
import gunzipMaybe from 'gunzip-maybe';
|
|
8
|
+
import { extract } from 'tar-fs';
|
|
5
9
|
|
|
10
|
+
const WINDOWS_SHELL_OPTS = process.platform === "win32" ? { shell: true } : {};
|
|
11
|
+
const PNPM_PACK_TIMEOUT_IN_MS = 6e4;
|
|
6
12
|
const DEPENDENCY_SECTIONS = [
|
|
7
13
|
"dependencies",
|
|
8
14
|
"devDependencies",
|
|
@@ -20,7 +26,9 @@ async function getPackedTemplatePackageJson(sourceTemplateDir) {
|
|
|
20
26
|
["pack", "--pack-destination", tempDir, "--json"],
|
|
21
27
|
{
|
|
22
28
|
cwd: sourceTemplateDir,
|
|
23
|
-
encoding: "utf8"
|
|
29
|
+
encoding: "utf8",
|
|
30
|
+
timeout: PNPM_PACK_TIMEOUT_IN_MS,
|
|
31
|
+
...WINDOWS_SHELL_OPTS
|
|
24
32
|
}
|
|
25
33
|
);
|
|
26
34
|
const parsedResult = JSON.parse(rawPackResult.trim());
|
|
@@ -28,14 +36,15 @@ async function getPackedTemplatePackageJson(sourceTemplateDir) {
|
|
|
28
36
|
if (!packedTarball) {
|
|
29
37
|
throw new Error("pnpm pack did not return a tarball filename.");
|
|
30
38
|
}
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
const tarballPath = isAbsolute(packedTarball) ? packedTarball : join(tempDir, packedTarball);
|
|
40
|
+
await pipeline(
|
|
41
|
+
createReadStream(tarballPath),
|
|
42
|
+
gunzipMaybe(),
|
|
43
|
+
extract(tempDir)
|
|
44
|
+
);
|
|
45
|
+
const packedManifestRaw = await readFile(
|
|
46
|
+
join(tempDir, "package", "package.json"),
|
|
47
|
+
"utf8"
|
|
39
48
|
);
|
|
40
49
|
return JSON.parse(packedManifestRaw);
|
|
41
50
|
} finally {
|
|
@@ -81,4 +90,4 @@ async function replaceWorkspaceProtocolVersions({
|
|
|
81
90
|
);
|
|
82
91
|
}
|
|
83
92
|
|
|
84
|
-
export { replaceWorkspaceProtocolVersions };
|
|
93
|
+
export { WINDOWS_SHELL_OPTS, replaceWorkspaceProtocolVersions };
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"@shopify:registry": "https://registry.npmjs.org"
|
|
6
6
|
},
|
|
7
|
-
"version": "11.1.
|
|
7
|
+
"version": "11.1.12",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"repository": {
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"@react-router/dev": "7.12.0",
|
|
59
59
|
"graphql-config": "^5.0.3",
|
|
60
60
|
"vite": "^5.1.0 || ^6.2.0",
|
|
61
|
-
"@shopify/
|
|
62
|
-
"@shopify/
|
|
61
|
+
"@shopify/mini-oxygen": "4.0.2",
|
|
62
|
+
"@shopify/hydrogen-codegen": "0.3.3"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"@graphql-codegen/cli": {
|