@shopify/cli-hydrogen 11.1.11 → 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 +13 -0
- 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/account.addresses.tsx +1 -1
- package/dist/assets/hydrogen/starter/package.json +3 -3
- package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +0 -11
- package/dist/commands/hydrogen/dev.js +4 -1
- package/dist/commands/hydrogen/upgrade.js +127 -33
- 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,18 @@
|
|
|
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
|
+
|
|
3
16
|
## 2026.1.2
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -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}
|
|
@@ -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": "2026.1.
|
|
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",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@react-router/fs-routes": "7.12.0",
|
|
33
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;
|
|
@@ -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 };
|
|
@@ -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": {
|