@shopify/cli-hydrogen 12.0.0 → 12.0.1

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 (72) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/hydrogen/routes/locale-check.ts +3 -3
  3. package/dist/assets/hydrogen/starter/.graphqlrc.ts +4 -2
  4. package/dist/assets/hydrogen/starter/CHANGELOG.md +271 -0
  5. package/dist/assets/hydrogen/starter/app/components/Aside.tsx +4 -2
  6. package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +67 -38
  7. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +49 -8
  8. package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +133 -57
  9. package/dist/assets/hydrogen/starter/app/components/Header.tsx +3 -3
  10. package/dist/assets/hydrogen/starter/app/components/MockShopNotice.tsx +20 -0
  11. package/dist/assets/hydrogen/starter/app/components/PaginatedResourceSection.tsx +24 -4
  12. package/dist/assets/hydrogen/starter/app/components/ProductPrice.tsx +1 -1
  13. package/dist/assets/hydrogen/starter/app/entry.client.tsx +2 -3
  14. package/dist/assets/hydrogen/starter/app/lib/context.ts +5 -0
  15. package/dist/assets/hydrogen/starter/app/lib/fragments.ts +8 -0
  16. package/dist/assets/hydrogen/starter/app/routes/[robots.txt].tsx +14 -53
  17. package/dist/assets/hydrogen/starter/app/routes/_index.tsx +15 -9
  18. package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +1 -1
  19. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +14 -14
  20. package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +1 -1
  21. package/dist/assets/hydrogen/starter/app/routes/account.tsx +4 -7
  22. package/dist/assets/hydrogen/starter/app/routes/account_.login.tsx +10 -0
  23. package/dist/assets/hydrogen/starter/app/routes/blogs.$blogHandle._index.tsx +2 -9
  24. package/dist/assets/hydrogen/starter/app/routes/blogs._index.tsx +1 -4
  25. package/dist/assets/hydrogen/starter/app/routes/cart.tsx +3 -11
  26. package/dist/assets/hydrogen/starter/app/routes/collections.all.tsx +1 -3
  27. package/dist/assets/hydrogen/starter/app/routes/pages.$handle.tsx +2 -8
  28. package/dist/assets/hydrogen/starter/app/routes/policies.$handle.tsx +1 -4
  29. package/dist/assets/hydrogen/starter/app/routes/policies._index.tsx +1 -1
  30. package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +2 -9
  31. package/dist/assets/hydrogen/starter/app/routes/search.tsx +18 -12
  32. package/dist/assets/hydrogen/starter/app/styles/app.css +72 -1
  33. package/dist/assets/hydrogen/starter/eslint.config.js +1 -0
  34. package/dist/assets/hydrogen/starter/guides/predictiveSearch/predictiveSearch.md +3 -3
  35. package/dist/assets/hydrogen/starter/guides/search/search.md +1 -1
  36. package/dist/assets/hydrogen/starter/package.json +16 -16
  37. package/dist/assets/hydrogen/starter/server.ts +5 -7
  38. package/dist/assets/hydrogen/starter/storefrontapi.generated.d.ts +89 -11
  39. package/dist/assets/hydrogen/starter/vite.config.ts +10 -4
  40. package/dist/assets/hydrogen/vite/package.json +1 -2
  41. package/dist/assets/hydrogen/vite/vite.config.js +4 -3
  42. package/dist/commands/hydrogen/deploy.js +19 -13
  43. package/dist/commands/hydrogen/dev.js +6 -6
  44. package/dist/commands/hydrogen/generate/route.js +1 -1
  45. package/dist/commands/hydrogen/init.d.ts +2 -4
  46. package/dist/commands/hydrogen/init.js +2 -8
  47. package/dist/commands/hydrogen/preview.js +1 -1
  48. package/dist/commands/hydrogen/setup/css.js +1 -1
  49. package/dist/commands/hydrogen/setup/markets.js +1 -1
  50. package/dist/commands/hydrogen/setup/vite.js +1 -1
  51. package/dist/commands/hydrogen/setup.js +31 -33
  52. package/dist/commands/hydrogen/upgrade.js +173 -39
  53. package/dist/lib/build.js +4 -5
  54. package/dist/lib/bundle/vite-plugin.js +7 -21
  55. package/dist/lib/check-lockfile.js +16 -7
  56. package/dist/lib/codegen.js +2 -2
  57. package/dist/lib/format-code.js +3 -3
  58. package/dist/lib/graphql/admin/client.js +48 -23
  59. package/dist/lib/mini-oxygen/workerd.js +3 -5
  60. package/dist/lib/onboarding/common.js +21 -26
  61. package/dist/lib/onboarding/local.js +46 -59
  62. package/dist/lib/package-managers.js +2 -0
  63. package/dist/lib/patch-cli.js +198 -0
  64. package/dist/lib/setups/routes/generate.js +5 -3
  65. package/dist/lib/template-pack.js +93 -0
  66. package/dist/lib/transpile/morph/functions.js +1 -1
  67. package/dist/lib/transpile/morph/typedefs.js +2 -3
  68. package/dist/lib/upgrade-e2e-utils.js +50 -0
  69. package/oclif.manifest.json +7 -15
  70. package/package.json +22 -19
  71. package/dist/assets/hydrogen/starter/app/routes/api.$version.[graphql.json].tsx +0 -14
  72. package/dist/lib/react-router-version-check.js +0 -82
@@ -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, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
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';
@@ -17,6 +17,9 @@ import { isHydrogenMonorepo, hydrogenPackagesPath } from '../../lib/build.js';
17
17
  import { isCI } from '../../lib/is-ci.js';
18
18
  import { fetch } from '@shopify/cli-kit/node/http';
19
19
 
20
+ function getAllRemovedPackages(release) {
21
+ return [...release.removeDependencies, ...release.removeDevDependencies];
22
+ }
20
23
  const INSTRUCTIONS_FOLDER = ".hydrogen";
21
24
  class Upgrade extends Command {
22
25
  static descriptionWithMarkdown = "Upgrade Hydrogen project dependencies, preview features, fixes and breaking changes. The command also generates an instruction file for each upgrade.";
@@ -106,7 +109,7 @@ async function runUpgrade({
106
109
  availableUpgrades,
107
110
  currentDependencies
108
111
  });
109
- cumulativeRelease = getCummulativeRelease({
112
+ cumulativeRelease = getCumulativeRelease({
110
113
  availableUpgrades,
111
114
  currentVersion,
112
115
  currentDependencies,
@@ -128,7 +131,11 @@ async function runUpgrade({
128
131
  appPath,
129
132
  selectedRelease,
130
133
  currentDependencies,
131
- targetVersion
134
+ targetVersion,
135
+ cumulativeRemoveDependencies: cumulativeRelease.removeDependencies,
136
+ cumulativeRemoveDevDependencies: cumulativeRelease.removeDevDependencies,
137
+ cumulativeDependencies: cumulativeRelease.dependencies,
138
+ cumulativeDevDependencies: cumulativeRelease.devDependencies
132
139
  });
133
140
  await validateUpgrade({
134
141
  appPath,
@@ -171,7 +178,7 @@ async function isRunningFromHydrogenMonorepo() {
171
178
  }
172
179
  function createNextRelease(latestRelease) {
173
180
  const dependencies = { ...latestRelease.dependencies };
174
- const devDependencies = { ...latestRelease.devDependencies };
181
+ const devDependencies = { ...latestRelease.devDependencies ?? {} };
175
182
  if (dependencies["@shopify/hydrogen"]) {
176
183
  dependencies["@shopify/hydrogen"] = "next";
177
184
  }
@@ -272,7 +279,7 @@ function hasOutdatedDependencies({
272
279
  }) {
273
280
  return Object.entries({
274
281
  ...release.dependencies,
275
- ...release.devDependencies
282
+ ...release.devDependencies ?? {}
276
283
  }).some(([name, version]) => {
277
284
  if (name === "@shopify/cli") return false;
278
285
  const currentDependencyVersion = currentDependencies?.[name];
@@ -336,20 +343,31 @@ async function getSelectedRelease({
336
343
  ) : void 0;
337
344
  return targetRelease ?? promptUpgradeOptions(currentVersion, availableUpgrades);
338
345
  }
339
- function getCummulativeRelease({
346
+ function getCumulativeRelease({
340
347
  availableUpgrades,
341
348
  selectedRelease,
342
349
  currentVersion,
343
350
  currentDependencies
344
351
  }) {
345
352
  const currentPinnedVersion = getAbsoluteVersion(currentVersion);
346
- if (!availableUpgrades?.length) {
347
- return { features: [], fixes: [] };
348
- }
353
+ const empty = {
354
+ features: [],
355
+ fixes: [],
356
+ removeDependencies: [],
357
+ removeDevDependencies: [],
358
+ dependencies: {},
359
+ devDependencies: {}
360
+ };
361
+ if (!availableUpgrades?.length) return empty;
349
362
  if (selectedRelease.dependencies?.["@shopify/hydrogen"] === "next") {
350
363
  return {
364
+ ...empty,
351
365
  features: selectedRelease.features || [],
352
- fixes: selectedRelease.fixes || []
366
+ fixes: selectedRelease.fixes || [],
367
+ removeDependencies: selectedRelease.removeDependencies ?? [],
368
+ removeDevDependencies: selectedRelease.removeDevDependencies ?? [],
369
+ dependencies: selectedRelease.dependencies ?? {},
370
+ devDependencies: selectedRelease.devDependencies ?? {}
353
371
  };
354
372
  }
355
373
  const upgradingReleases = availableUpgrades.filter((release) => {
@@ -359,14 +377,65 @@ function getCummulativeRelease({
359
377
  if (!isSameHydrogenVersion || !currentDependencies) return false;
360
378
  return hasOutdatedDependencies({ release, currentDependencies });
361
379
  });
362
- return upgradingReleases.reduce(
363
- (acc, release) => {
364
- acc.features = [...acc.features, ...release.features];
365
- acc.fixes = [...acc.fixes, ...release.fixes];
366
- return acc;
367
- },
368
- { features: [], fixes: [] }
380
+ const features = upgradingReleases.flatMap((r) => r.features);
381
+ const fixes = upgradingReleases.flatMap((r) => r.fixes);
382
+ const releasesByVersion = [...upgradingReleases].sort(
383
+ (a, b) => semver.compare(a.version, b.version)
369
384
  );
385
+ const removedDepsAt = /* @__PURE__ */ new Map();
386
+ const removedDevDepsAt = /* @__PURE__ */ new Map();
387
+ releasesByVersion.forEach((release, i) => {
388
+ release.removeDependencies?.forEach((dep) => {
389
+ removedDepsAt.set(dep, i);
390
+ });
391
+ release.removeDevDependencies?.forEach((dep) => {
392
+ removedDevDepsAt.set(dep, i);
393
+ });
394
+ });
395
+ const reinstalledDeps = /* @__PURE__ */ new Set();
396
+ const reinstalledDevDeps = /* @__PURE__ */ new Set();
397
+ for (let i = 0; i < releasesByVersion.length; i++) {
398
+ const release = releasesByVersion[i];
399
+ if (!release) continue;
400
+ const dependencies2 = release.dependencies ?? {};
401
+ const devDependencies2 = release.devDependencies ?? {};
402
+ Object.keys(dependencies2).forEach((dep) => {
403
+ const removalI = removedDepsAt.get(dep);
404
+ if (removalI !== void 0 && i >= removalI) {
405
+ reinstalledDeps.add(dep);
406
+ }
407
+ });
408
+ Object.keys(devDependencies2).forEach((dep) => {
409
+ const removalI = removedDevDepsAt.get(dep);
410
+ if (removalI !== void 0 && i >= removalI) {
411
+ reinstalledDevDeps.add(dep);
412
+ }
413
+ });
414
+ }
415
+ const removeDependencies = [
416
+ ...new Set(
417
+ releasesByVersion.flatMap((r) => r.removeDependencies ?? []).filter((dep) => !reinstalledDeps.has(dep))
418
+ )
419
+ ];
420
+ const removeDevDependencies = [
421
+ ...new Set(
422
+ releasesByVersion.flatMap((r) => r.removeDevDependencies ?? []).filter((dep) => !reinstalledDevDeps.has(dep))
423
+ )
424
+ ];
425
+ const dependencies = {};
426
+ const devDependencies = {};
427
+ for (const release of releasesByVersion) {
428
+ Object.assign(dependencies, release.dependencies ?? {});
429
+ Object.assign(devDependencies, release.devDependencies ?? {});
430
+ }
431
+ return {
432
+ features,
433
+ fixes,
434
+ removeDependencies,
435
+ removeDevDependencies,
436
+ dependencies,
437
+ devDependencies
438
+ };
370
439
  }
371
440
  function displayConfirmation({
372
441
  cumulativeRelease,
@@ -374,10 +443,11 @@ function displayConfirmation({
374
443
  targetVersion
375
444
  }) {
376
445
  const { features, fixes } = cumulativeRelease;
377
- if (features.length || fixes.length) {
446
+ const allRemovedPackages = getAllRemovedPackages(cumulativeRelease);
447
+ if (features.length || fixes.length || allRemovedPackages.length) {
378
448
  renderInfo({
379
449
  headline: `Included in this upgrade:`,
380
- //@ts-ignore we know that filter(Boolean) will always return an array
450
+ // @ts-expect-error - filter(Boolean) removes falsy values, leaving only objects
381
451
  customSections: [
382
452
  features.length && {
383
453
  title: "Features",
@@ -398,6 +468,16 @@ function displayConfirmation({
398
468
  }
399
469
  }
400
470
  ]
471
+ },
472
+ allRemovedPackages.length && {
473
+ title: "Removed packages",
474
+ body: [
475
+ {
476
+ list: {
477
+ items: allRemovedPackages
478
+ }
479
+ }
480
+ ]
401
481
  }
402
482
  ].filter(Boolean)
403
483
  });
@@ -461,10 +541,20 @@ function maybeIncludeDependency({
461
541
  function buildUpgradeCommandArgs({
462
542
  selectedRelease,
463
543
  currentDependencies,
464
- targetVersion
544
+ targetVersion,
545
+ cumulativeDependencies,
546
+ cumulativeDevDependencies
465
547
  }) {
466
548
  const args = [];
467
- for (const dependency of Object.entries(selectedRelease.dependencies)) {
549
+ const effectiveDependencies = {
550
+ ...cumulativeDependencies ?? {},
551
+ ...selectedRelease.dependencies
552
+ };
553
+ const effectiveDevDependencies = {
554
+ ...cumulativeDevDependencies ?? {},
555
+ ...selectedRelease.devDependencies ?? {}
556
+ };
557
+ for (const dependency of Object.entries(effectiveDependencies)) {
468
558
  const shouldUpgradeDep = maybeIncludeDependency({
469
559
  currentDependencies,
470
560
  dependency,
@@ -480,7 +570,7 @@ function buildUpgradeCommandArgs({
480
570
  )}`
481
571
  );
482
572
  }
483
- for (const dependency of Object.entries(selectedRelease.devDependencies)) {
573
+ for (const dependency of Object.entries(effectiveDevDependencies)) {
484
574
  const shouldUpgradeDep = maybeIncludeDependency({
485
575
  currentDependencies,
486
576
  dependency,
@@ -497,7 +587,7 @@ function buildUpgradeCommandArgs({
497
587
  );
498
588
  }
499
589
  const currentRemix = Object.entries(currentDependencies).find(isRemixDependency);
500
- const selectedRemix = Object.entries(selectedRelease.dependencies).find(
590
+ const selectedRemix = Object.entries(effectiveDependencies).find(
501
591
  isRemixDependency
502
592
  );
503
593
  if (currentRemix && selectedRemix) {
@@ -514,7 +604,7 @@ function buildUpgradeCommandArgs({
514
604
  const currentReactRouter = Object.entries(currentDependencies).find(
515
605
  isReactRouterDependency
516
606
  );
517
- const selectedReactRouter = Object.entries(selectedRelease.dependencies).find(
607
+ const selectedReactRouter = Object.entries(effectiveDependencies).find(
518
608
  isReactRouterDependency
519
609
  );
520
610
  if (selectedReactRouter) {
@@ -537,12 +627,16 @@ async function upgradeNodeModules({
537
627
  appPath,
538
628
  selectedRelease,
539
629
  currentDependencies,
540
- targetVersion
630
+ targetVersion,
631
+ cumulativeRemoveDependencies,
632
+ cumulativeRemoveDevDependencies,
633
+ cumulativeDependencies,
634
+ cumulativeDevDependencies
541
635
  }) {
542
636
  const tasks = [];
543
637
  const depsToRemove = [
544
- ...selectedRelease.removeDependencies || [],
545
- ...selectedRelease.removeDevDependencies || []
638
+ ...cumulativeRemoveDependencies,
639
+ ...cumulativeRemoveDevDependencies
546
640
  ].filter((dep) => dep in currentDependencies);
547
641
  if (depsToRemove.length > 0) {
548
642
  tasks.push({
@@ -559,17 +653,24 @@ async function upgradeNodeModules({
559
653
  const upgradeArgs = buildUpgradeCommandArgs({
560
654
  selectedRelease,
561
655
  currentDependencies,
562
- targetVersion
656
+ targetVersion,
657
+ cumulativeDependencies,
658
+ cumulativeDevDependencies
563
659
  });
564
660
  if (upgradeArgs.length > 0) {
565
661
  tasks.push({
566
662
  title: `Upgrading dependencies`,
567
663
  task: async () => {
568
- await installNodeModules({
569
- directory: appPath,
570
- packageManager: await getPackageManager(appPath),
571
- args: upgradeArgs
572
- });
664
+ const packageManager = await getPackageManager(appPath);
665
+ const command = packageManager === "npm" ? "install" : packageManager === "yarn" ? "add" : packageManager === "pnpm" ? "add" : packageManager === "bun" ? "install" : "install";
666
+ const extraArgs = packageManager === "npm" || packageManager === "unknown" ? ["--legacy-peer-deps"] : [];
667
+ await exec(
668
+ resolvePackageManagerName(packageManager),
669
+ [command, ...extraArgs, ...upgradeArgs],
670
+ {
671
+ cwd: appPath
672
+ }
673
+ );
573
674
  }
574
675
  });
575
676
  }
@@ -577,6 +678,9 @@ async function upgradeNodeModules({
577
678
  await renderTasks(tasks, {});
578
679
  }
579
680
  }
681
+ function resolvePackageManagerName(packageManager) {
682
+ return packageManager === "unknown" ? "npm" : packageManager;
683
+ }
580
684
  async function uninstallNodeModules({
581
685
  directory,
582
686
  packageManager,
@@ -584,8 +688,12 @@ async function uninstallNodeModules({
584
688
  }) {
585
689
  if (args.length === 0) return;
586
690
  const command = packageManager === "npm" ? "uninstall" : packageManager === "yarn" ? "remove" : packageManager === "pnpm" ? "remove" : packageManager === "bun" ? "remove" : "uninstall";
587
- const actualPackageManager = packageManager === "unknown" ? "npm" : packageManager;
588
- await exec(actualPackageManager, [command, ...args], { cwd: directory });
691
+ const extraArgs = packageManager === "npm" || packageManager === "unknown" ? ["--legacy-peer-deps"] : [];
692
+ await exec(
693
+ resolvePackageManagerName(packageManager),
694
+ [command, ...extraArgs, ...args],
695
+ { cwd: directory }
696
+ );
589
697
  }
590
698
  function appendRemixDependencies({
591
699
  currentDependencies,
@@ -787,7 +895,8 @@ async function generateUpgradeInstructionsFile({
787
895
  { featuresMd: [], breakingChangesMd: [] }
788
896
  );
789
897
  const fixesMd = cumulativeRelease.fixes.filter((fixes) => fixes.steps).map(generateStepMd);
790
- if (!featuresMd.length && !fixesMd.length && !breakingChangesMd.length) {
898
+ const allRemovedPackages = getAllRemovedPackages(cumulativeRelease);
899
+ if (!featuresMd.length && !fixesMd.length && !breakingChangesMd.length && !allRemovedPackages.length) {
791
900
  renderInfo({
792
901
  headline: `No upgrade instructions generated`,
793
902
  body: `There are no additional upgrade instructions for this version.`
@@ -827,6 +936,16 @@ ${fixesMd.join(
827
936
  "\n"
828
937
  )}`;
829
938
  }
939
+ if (allRemovedPackages.length) {
940
+ md += fixesMd.length ? "\n\n----\n\n" : "\n";
941
+ md += `## Removed packages
942
+
943
+ The following packages have been removed as part of this upgrade:
944
+
945
+ `;
946
+ md += allRemovedPackages.map((dep) => `- \`${dep}\``).join("\n");
947
+ md += "\n";
948
+ }
830
949
  const filePath = joinPath(instructionsFolderPath, filename);
831
950
  try {
832
951
  await isDirectory(instructionsFolderPath);
@@ -859,6 +978,13 @@ async function displayDevUpgradeNotice({
859
978
  const { currentVersion, currentDependencies } = await getHydrogenVersion({
860
979
  appPath
861
980
  });
981
+ if (currentVersion.startsWith("workspace:")) {
982
+ renderInfo({
983
+ headline: "Using monorepo @shopify/hydrogen dependency",
984
+ body: "This project uses the workspace protocol for @shopify/hydrogen, so upgrade notices are skipped during local monorepo development."
985
+ });
986
+ return;
987
+ }
862
988
  const isPrerelease = semver.prerelease(currentVersion);
863
989
  if (isPrerelease || /^[a-z]+$/i.test(currentVersion)) {
864
990
  return;
@@ -882,7 +1008,15 @@ async function displayDevUpgradeNotice({
882
1008
  ) : changelog.releases.findIndex(
883
1009
  (release) => getAbsoluteVersion(release.version) === pinnedCurrentVersion
884
1010
  );
885
- const relevantReleases = changelog.releases.slice(0, currentReleaseIndex);
1011
+ let relevantReleases;
1012
+ if (currentReleaseIndex === -1) {
1013
+ const insertionIndex = changelog.releases.findIndex(
1014
+ (release) => semver.lte(getAbsoluteVersion(release.version), pinnedCurrentVersion)
1015
+ );
1016
+ relevantReleases = insertionIndex === -1 ? changelog.releases : changelog.releases.slice(0, insertionIndex);
1017
+ } else {
1018
+ relevantReleases = changelog.releases.slice(0, currentReleaseIndex);
1019
+ }
886
1020
  const nextReleases = Object.values(
887
1021
  [...relevantReleases].reverse().reduce(
888
1022
  (acc, release) => {
@@ -891,7 +1025,7 @@ async function displayDevUpgradeNotice({
891
1025
  },
892
1026
  {}
893
1027
  )
894
- ).slice(0, 5);
1028
+ ).reverse().slice(0, 5);
895
1029
  let headline = Object.keys(uniqueAvailableUpgrades).length > 1 ? `There are ${Object.keys(uniqueAvailableUpgrades).length} new @shopify/hydrogen versions available.` : `There's a new @shopify/hydrogen version available.`;
896
1030
  const cliCommand = await getCliCommand();
897
1031
  renderInfo({
@@ -939,4 +1073,4 @@ async function displayDevUpgradeNotice({
939
1073
  }
940
1074
  }
941
1075
 
942
- export { buildUpgradeCommandArgs, Upgrade as default, displayConfirmation, displayDevUpgradeNotice, getAbsoluteVersion, getAvailableUpgrades, getChangelog, getCummulativeRelease, getHydrogenVersion, getPackageVersion, getSelectedRelease, isRunningFromHydrogenMonorepo, runUpgrade, upgradeNodeModules, validateUpgrade };
1076
+ export { buildUpgradeCommandArgs, Upgrade as default, displayConfirmation, displayDevUpgradeNotice, generateUpgradeInstructionsFile, getAbsoluteVersion, getAvailableUpgrades, getChangelog, getCumulativeRelease, getHydrogenVersion, getPackageVersion, getSelectedRelease, isRunningFromHydrogenMonorepo, runUpgrade, upgradeNodeModules, validateUpgrade };
package/dist/lib/build.js CHANGED
@@ -69,12 +69,11 @@ function getSkeletonNodeModules() {
69
69
  return joinPath(getSkeletonSourceDir(), "node_modules");
70
70
  }
71
71
  async function getRepoNodeModules() {
72
- const { stdout } = await execAsync("npm root");
73
- let nodeModulesPath = stdout.trim();
74
- if (!nodeModulesPath && isHydrogenMonorepo) {
75
- nodeModulesPath = joinPath(dirname(monorepoPackagesPath), "node_modules");
72
+ if (isHydrogenMonorepo) {
73
+ return getSkeletonNodeModules();
76
74
  }
77
- return nodeModulesPath;
75
+ const { stdout } = await execAsync("npm root");
76
+ return stdout.trim();
78
77
  }
79
78
 
80
79
  export { ASSETS_DIR_PREFIX, ASSETS_STARTER_DIR, ASSETS_STARTER_DIR_ROUTES, getAssetsDir, getPkgJsonPath, getRepoNodeModules, getSkeletonNodeModules, getSkeletonSourceDir, getStarterDir, getTemplateAppFile, hydrogenPackagesPath, isHydrogenMonorepo };
@@ -44,7 +44,7 @@ function hydrogenBundleAnalyzer(pluginOptions) {
44
44
  modsToAnalyze.map(async (mod) => {
45
45
  const relativeModId = relativePath(root, mod.id);
46
46
  const modBundleInfo = workerFile.modules[mod.id];
47
- const originalCodeBytes = modBundleInfo?.originalLength ?? mod.code?.length ?? 0;
47
+ const originalCodeBytes = typeof modBundleInfo?.originalLength === "number" ? modBundleInfo.originalLength : mod.code?.length ?? 0;
48
48
  let resultingCodeBytes = modBundleInfo?.renderedLength ?? 0;
49
49
  if (pluginOptions?.minify && modBundleInfo?.code) {
50
50
  const minifiedCode = await pluginOptions.minify(modBundleInfo.code, mod.id).catch(() => null);
@@ -115,33 +115,19 @@ function hydrogenBundleAnalyzer(pluginOptions) {
115
115
  }
116
116
  }
117
117
  };
118
- bundle[BUNDLE_ANALYZER_JSON_FILE] = {
118
+ this.emitFile({
119
119
  type: "asset",
120
120
  fileName: BUNDLE_ANALYZER_JSON_FILE,
121
- needsCodeReference: false,
122
- source: JSON.stringify(metafile, null, 2),
123
- names: [BUNDLE_ANALYZER_JSON_FILE],
124
- originalFileNames: [BUNDLE_ANALYZER_JSON_FILE],
125
- // name and originalFileName should be deprecated .. but
126
- // for some reason, removing them breaks typescript check
127
- name: BUNDLE_ANALYZER_JSON_FILE,
128
- originalFileName: BUNDLE_ANALYZER_JSON_FILE
129
- };
130
- bundle[BUNDLE_ANALYZER_HTML_FILE] = {
121
+ source: JSON.stringify(metafile, null, 2)
122
+ });
123
+ this.emitFile({
131
124
  type: "asset",
132
125
  fileName: BUNDLE_ANALYZER_HTML_FILE,
133
- needsCodeReference: false,
134
126
  source: injectAnalyzerTemplateData(
135
127
  analysisTemplate,
136
128
  JSON.stringify(metafile)
137
- ),
138
- names: [BUNDLE_ANALYZER_HTML_FILE],
139
- originalFileNames: [BUNDLE_ANALYZER_HTML_FILE],
140
- // name and originalFileName should be deprecated .. but
141
- // for some reason, removing them breaks typescript check
142
- name: BUNDLE_ANALYZER_HTML_FILE,
143
- originalFileName: BUNDLE_ANALYZER_HTML_FILE
144
- };
129
+ )
130
+ });
145
131
  }
146
132
  };
147
133
  }
@@ -24,10 +24,12 @@ function missingLockfileWarning(shouldExit) {
24
24
  renderWarning({ headline, body, nextSteps });
25
25
  }
26
26
  }
27
- function multipleLockfilesWarning(packageManagers2, shouldExit) {
28
- const lockfileList = packageManagers2.map(({ name, lockfile }) => {
29
- return `${lockfile} (created by ${name})`;
30
- });
27
+ function multipleLockfilesWarning(foundPackageManagers, shouldExit) {
28
+ const lockfileList = foundPackageManagers.map(
29
+ ({ packageManager, foundLockfile }) => {
30
+ return `${foundLockfile} (created by ${packageManager.name})`;
31
+ }
32
+ );
31
33
  const headline = "Multiple lockfiles found";
32
34
  const body = [
33
35
  `Your project contains more than one lockfile. This can cause version conflicts when installing and deploying your app. The following lockfiles were detected:
@@ -57,8 +59,15 @@ async function checkLockfileStatus(directory, shouldExit = false) {
57
59
  if (isHydrogenMonorepo && !process.env.SHOPIFY_UNIT_TEST) return;
58
60
  const foundPackageManagers = [];
59
61
  for (const packageManager of packageManagers) {
60
- if (await fileExists(resolvePath(directory, packageManager.lockfile))) {
61
- foundPackageManagers.push(packageManager);
62
+ const allLockfiles = [
63
+ packageManager.lockfile,
64
+ ...packageManager.alternativeLockfiles || []
65
+ ];
66
+ for (const lockfile2 of allLockfiles) {
67
+ if (await fileExists(resolvePath(directory, lockfile2))) {
68
+ foundPackageManagers.push({ packageManager, foundLockfile: lockfile2 });
69
+ break;
70
+ }
62
71
  }
63
72
  }
64
73
  if (foundPackageManagers.length === 0) {
@@ -67,7 +76,7 @@ async function checkLockfileStatus(directory, shouldExit = false) {
67
76
  if (foundPackageManagers.length > 1) {
68
77
  return multipleLockfilesWarning(foundPackageManagers, shouldExit);
69
78
  }
70
- const lockfile = foundPackageManagers[0].lockfile;
79
+ const lockfile = foundPackageManagers[0].foundLockfile;
71
80
  const ignoredLockfile = await checkIfIgnoredInGitRepository(directory, [
72
81
  lockfile
73
82
  ]).catch(() => {
@@ -2,7 +2,7 @@ import { spawn } from 'node:child_process';
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { getCodeFormatOptions, formatCode } from './format-code.js';
4
4
  import { renderInfo, renderWarning } from '@shopify/cli-kit/node/ui';
5
- import { relativePath, resolvePath, joinPath, basename } from '@shopify/cli-kit/node/path';
5
+ import { relativePath, joinPath, resolvePath, basename } from '@shopify/cli-kit/node/path';
6
6
  import { AbortError } from '@shopify/cli-kit/node/error';
7
7
  import { importLocal } from './import-utils.js';
8
8
 
@@ -283,7 +283,7 @@ function findGqlProject(schemaFilepath, gqlConfig) {
283
283
  );
284
284
  }
285
285
  async function addHooksToHydrogenOptions(codegenConfig, { rootDirectory }) {
286
- const name = Symbol.for("name");
286
+ const name = /* @__PURE__ */ Symbol.for("name");
287
287
  const hydrogenProjectsOptions = Object.values(codegenConfig.generates).filter(
288
288
  (value) => {
289
289
  const foundPreset = (Array.isArray(value) ? value[0] : value)?.preset;
@@ -1,5 +1,5 @@
1
1
  import { extname } from '@shopify/cli-kit/node/path';
2
- import * as prettier from 'prettier';
2
+ import { resolveConfig, format } from 'prettier';
3
3
  import * as FS from 'fs/promises';
4
4
  import * as Path from 'path';
5
5
 
@@ -12,14 +12,14 @@ const DEFAULT_PRETTIER_CONFIG = {
12
12
  async function getCodeFormatOptions(filePath = process.cwd()) {
13
13
  const pathToUse = (await FS.lstat(filePath)).isFile() ? filePath : Path.resolve(filePath, "prettier.file");
14
14
  try {
15
- return await prettier.resolveConfig(pathToUse) || DEFAULT_PRETTIER_CONFIG;
15
+ return await resolveConfig(pathToUse) || DEFAULT_PRETTIER_CONFIG;
16
16
  } catch {
17
17
  return DEFAULT_PRETTIER_CONFIG;
18
18
  }
19
19
  }
20
20
  async function formatCode(content, config = DEFAULT_PRETTIER_CONFIG, filePath = "") {
21
21
  const ext = extname(filePath);
22
- return prettier.format(content, {
22
+ return format(content, {
23
23
  // Specify the TypeScript parser for ts/tsx files. Otherwise
24
24
  // we need to use the babel parser instead of the default parser,
25
25
  // because prettier will print a warning.
@@ -1,6 +1,50 @@
1
1
  import { AbortError } from '@shopify/cli-kit/node/error';
2
2
  import { graphqlRequest } from '@shopify/cli-kit/node/api/graphql';
3
3
 
4
+ const KNOWN_USER_ERRORS = [
5
+ {
6
+ pattern: "app is not installed",
7
+ abort: [
8
+ "Hydrogen sales channel isn't installed",
9
+ "Install the Hydrogen sales channel on your store to start creating and linking Hydrogen storefronts: https://apps.shopify.com/hydrogen"
10
+ ]
11
+ },
12
+ {
13
+ pattern: "Access denied for hydrogenStorefrontCreate field",
14
+ abort: [
15
+ "Couldn't connect storefront to Shopify",
16
+ [
17
+ "Common reasons for this error include:",
18
+ {
19
+ list: {
20
+ items: [
21
+ "The Hydrogen sales channel isn't installed on the store.",
22
+ "You don't have the required account permission to manage apps or channels.",
23
+ "You're trying to connect to an ineligible store type (Trial, Development store)"
24
+ ]
25
+ }
26
+ }
27
+ ]
28
+ ]
29
+ },
30
+ {
31
+ pattern: "Access denied for hydrogenStorefronts field",
32
+ abort: [
33
+ "Couldn't access Hydrogen storefronts",
34
+ [
35
+ "Common reasons for this error include:",
36
+ {
37
+ list: {
38
+ items: [
39
+ "You don't have full access to apps or access to the Hydrogen channel.",
40
+ "The Hydrogen sales channel isn't installed on the store."
41
+ ]
42
+ }
43
+ }
44
+ ]
45
+ ]
46
+ }
47
+ ];
4
48
  async function adminRequest(query, session, variables) {
5
49
  const api = "Admin";
6
50
  const url = `https://${session.storeFqdn}/admin/api/unstable/graphql.json`;
@@ -14,29 +58,10 @@ async function adminRequest(query, session, variables) {
14
58
  });
15
59
  } catch (error) {
16
60
  const errors = error.errors;
17
- if (errors?.some?.((error2) => error2.message.includes("app is not installed"))) {
18
- throw new AbortError(
19
- "Hydrogen sales channel isn't installed",
20
- "Install the Hydrogen sales channel on your store to start creating and linking Hydrogen storefronts: https://apps.shopify.com/hydrogen"
21
- );
22
- }
23
- if (errors?.some?.(
24
- (error2) => error2.message.includes(
25
- "Access denied for hydrogenStorefrontCreate field"
26
- )
27
- )) {
28
- throw new AbortError("Couldn't connect storefront to Shopify", [
29
- "Common reasons for this error include:",
30
- {
31
- list: {
32
- items: [
33
- "The Hydrogen sales channel isn't installed on the store.",
34
- "You don't have the required account permission to manage apps or channels.",
35
- "You're trying to connect to an ineligible store type (Trial, Development store)"
36
- ]
37
- }
38
- }
39
- ]);
61
+ for (const { pattern, abort } of KNOWN_USER_ERRORS) {
62
+ if (errors?.some?.((e) => e.message.includes(pattern))) {
63
+ throw new AbortError(...abort);
64
+ }
40
65
  }
41
66
  throw error;
42
67
  }
@@ -1,13 +1,13 @@
1
1
  import { createRequire } from 'node:module';
2
2
  import { resolvePath, dirname } from '@shopify/cli-kit/node/path';
3
- import { readFile, createFileReadStream } from '@shopify/cli-kit/node/fs';
3
+ import { createFileReadStream, readFile } from '@shopify/cli-kit/node/fs';
4
4
  import { renderSuccess } from '@shopify/cli-kit/node/ui';
5
5
  import { outputNewline } from '@shopify/cli-kit/node/output';
6
6
  import { AbortError } from '@shopify/cli-kit/node/error';
7
7
  import colors from '@shopify/cli-kit/node/colors';
8
8
  import { handleMiniOxygenImportFail, SUBREQUEST_PROFILER_ENDPOINT, logRequestLine } from './common.js';
9
9
  import { TUNNEL_DOMAIN, getUtilityBannerlines, getDebugBannerLine } from '../dev-shared.js';
10
- import { setConstructors, handleDebugNetworkRequest, createLogRequestEvent, H2O_BINDING_NAME } from '../request-events.js';
10
+ import { setConstructors, handleDebugNetworkRequest, H2O_BINDING_NAME, createLogRequestEvent } from '../request-events.js';
11
11
  import { importLocal } from '../import-utils.js';
12
12
 
13
13
  async function startWorkerdServer({
@@ -28,9 +28,7 @@ async function startWorkerdServer({
28
28
  setConstructors({ Response });
29
29
  async function handleCustomerAccountSchema() {
30
30
  const require2 = createRequire(import.meta.url);
31
- const filePath = require2.resolve(
32
- "@shopify/hydrogen/customer-account.schema.json"
33
- );
31
+ const filePath = require2.resolve("@shopify/hydrogen/customer-account.schema.json");
34
32
  return new Response(createFileReadStream(filePath), {
35
33
  headers: { "Content-Type": "application/json" }
36
34
  });