@shopify/cli-hydrogen 8.1.0 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/assets/hydrogen/starter/CHANGELOG.md +166 -0
  2. package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +37 -0
  3. package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +150 -0
  4. package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +68 -0
  5. package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +101 -0
  6. package/dist/assets/hydrogen/starter/app/components/Header.tsx +3 -3
  7. package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +2 -2
  8. package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +80 -0
  9. package/dist/assets/hydrogen/starter/app/components/ProductImage.tsx +23 -0
  10. package/dist/assets/hydrogen/starter/app/components/ProductPrice.tsx +27 -0
  11. package/dist/assets/hydrogen/starter/app/lib/session.ts +5 -0
  12. package/dist/assets/hydrogen/starter/app/root.tsx +23 -36
  13. package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +1 -5
  14. package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +12 -70
  15. package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +7 -14
  16. package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +1 -8
  17. package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +5 -22
  18. package/dist/assets/hydrogen/starter/app/routes/account.tsx +0 -1
  19. package/dist/assets/hydrogen/starter/app/routes/cart.tsx +1 -3
  20. package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +51 -232
  21. package/dist/assets/hydrogen/starter/package.json +10 -11
  22. package/dist/assets/hydrogen/starter/server.ts +4 -0
  23. package/dist/assets/hydrogen/tailwind/package.json +1 -6
  24. package/dist/assets/hydrogen/tailwind/tailwind.css +6 -3
  25. package/dist/assets/hydrogen/vanilla-extract/package.json +2 -3
  26. package/dist/assets/hydrogen/virtual-routes/components/{Layout.jsx → PageLayout.jsx} +2 -2
  27. package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +1 -2
  28. package/dist/assets/hydrogen/virtual-routes/components/RequestTable.jsx +1 -2
  29. package/dist/assets/hydrogen/virtual-routes/routes/index.jsx +1 -2
  30. package/dist/assets/hydrogen/virtual-routes/virtual-root.jsx +8 -30
  31. package/dist/commands/hydrogen/build.js +33 -10
  32. package/dist/commands/hydrogen/customer-account/push.js +3 -6
  33. package/dist/commands/hydrogen/debug/cpu.js +3 -3
  34. package/dist/commands/hydrogen/deploy.js +14 -3
  35. package/dist/commands/hydrogen/dev.js +3 -6
  36. package/dist/commands/hydrogen/env/list.js +1 -2
  37. package/dist/commands/hydrogen/env/pull.js +2 -4
  38. package/dist/commands/hydrogen/env/push.js +6 -12
  39. package/dist/commands/hydrogen/init.d.ts +18 -15
  40. package/dist/commands/hydrogen/init.js +12 -24
  41. package/dist/commands/hydrogen/link.js +1 -2
  42. package/dist/commands/hydrogen/preview.js +4 -6
  43. package/dist/commands/hydrogen/setup/css.js +29 -12
  44. package/dist/commands/hydrogen/setup/vite.js +3 -6
  45. package/dist/commands/hydrogen/setup.js +8 -7
  46. package/dist/commands/hydrogen/upgrade.js +16 -32
  47. package/dist/hooks/init.js +50 -6
  48. package/dist/index.d.ts +46 -46
  49. package/dist/lib/auth.js +1 -2
  50. package/dist/lib/build.js +1 -2
  51. package/dist/lib/bundle/analyzer.js +39 -24
  52. package/dist/lib/bundle/vite-plugin.js +161 -0
  53. package/dist/lib/check-cli-version.js +61 -0
  54. package/dist/lib/check-lockfile.js +2 -2
  55. package/dist/lib/classic-compiler/build.js +3 -3
  56. package/dist/lib/classic-compiler/dev.js +5 -10
  57. package/dist/lib/codegen.js +8 -16
  58. package/dist/lib/defer.js +2 -4
  59. package/dist/lib/environment-variables.js +2 -4
  60. package/dist/lib/file.js +15 -7
  61. package/dist/lib/flags.js +10 -0
  62. package/dist/lib/get-oxygen-deployment-data.js +1 -2
  63. package/dist/lib/graphiql-url.js +1 -2
  64. package/dist/lib/import-utils.js +3 -2
  65. package/dist/lib/log.js +11 -22
  66. package/dist/lib/mini-oxygen/common.js +1 -2
  67. package/dist/lib/mini-oxygen/node.js +1 -2
  68. package/dist/lib/missing-routes.js +1 -2
  69. package/dist/lib/onboarding/common.js +60 -15
  70. package/dist/lib/onboarding/local.js +14 -13
  71. package/dist/lib/onboarding/remote.js +16 -9
  72. package/dist/lib/onboarding/setup-template.mocks.js +6 -3
  73. package/dist/lib/remix-config.js +2 -4
  74. package/dist/lib/remix-version-check.js +1 -2
  75. package/dist/lib/request-events.js +3 -6
  76. package/dist/lib/setups/css/assets.js +1 -1
  77. package/dist/lib/setups/css/index.js +17 -10
  78. package/dist/lib/setups/css/replacers.js +74 -76
  79. package/dist/lib/setups/css/tailwind.js +16 -20
  80. package/dist/lib/setups/css/vanilla-extract.js +8 -5
  81. package/dist/lib/setups/i18n/replacers.js +1 -2
  82. package/dist/lib/setups/routes/generate.js +18 -19
  83. package/dist/lib/shell.js +5 -10
  84. package/dist/lib/template-diff.js +83 -104
  85. package/dist/lib/template-downloader.js +2 -2
  86. package/dist/lib/transpile/morph/functions.js +3 -6
  87. package/dist/lib/transpile/morph/index.js +2 -4
  88. package/dist/lib/transpile/morph/typedefs.js +3 -6
  89. package/dist/lib/transpile/morph/utils.js +2 -4
  90. package/dist/lib/transpile/project.js +4 -3
  91. package/oclif.manifest.json +51 -4
  92. package/package.json +8 -12
  93. package/dist/assets/hydrogen/css-modules/package.json +0 -6
  94. package/dist/assets/hydrogen/postcss/package.json +0 -10
  95. package/dist/assets/hydrogen/postcss/postcss.config.js +0 -8
  96. package/dist/assets/hydrogen/starter/app/components/Cart.tsx +0 -364
  97. package/dist/assets/hydrogen/tailwind/postcss.config.js +0 -10
  98. package/dist/assets/hydrogen/tailwind/tailwind.config.js +0 -8
  99. package/dist/lib/check-version.js +0 -75
  100. package/dist/lib/setups/css/css-modules.js +0 -23
  101. package/dist/lib/setups/css/postcss.js +0 -31
@@ -59,7 +59,14 @@ class Deploy extends Command {
59
59
  "auth-bypass-token": Flags.boolean({
60
60
  description: "Generate an authentication bypass token, which can be used to perform end-to-end tests against the deployment.",
61
61
  required: false,
62
- default: false
62
+ default: false,
63
+ env: "AUTH_BYPASS_TOKEN"
64
+ }),
65
+ "auth-bypass-token-duration": Flags.string({
66
+ description: "Specify the duration (in hours) up to 12 hours for the authentication bypass token. Defaults to `2`",
67
+ required: false,
68
+ env: "AUTH_BYPASS_TOKEN_DURATION",
69
+ dependsOn: ["auth-bypass-token"]
63
70
  }),
64
71
  "build-command": Flags.string({
65
72
  description: "Specify a build command to run before deploying. If not specified, `shopify hydrogen build` will be used.",
@@ -143,6 +150,7 @@ function createUnexpectedAbortError(message) {
143
150
  }
144
151
  async function runDeploy(options) {
145
152
  const {
153
+ authBypassTokenDuration,
146
154
  authBypassToken: generateAuthBypassToken,
147
155
  buildCommand,
148
156
  defaultEnvironment,
@@ -246,6 +254,9 @@ ${changedFiles.trimEnd()}`;
246
254
  "Environments are automatically picked up by the current Git branch."
247
255
  );
248
256
  }
257
+ if (isCI && envBranch) {
258
+ userProvidedEnvironmentTag = envBranch;
259
+ }
249
260
  if (!isCI) {
250
261
  deploymentData = await getOxygenDeploymentData({
251
262
  root,
@@ -336,6 +347,7 @@ ${changedFiles.trimEnd()}`;
336
347
  deploymentToken: parseToken(token),
337
348
  environmentTag: userProvidedEnvironmentTag || userChosenEnvironmentTag || fallbackEnvironmentTag,
338
349
  generateAuthBypassToken,
350
+ authBypassTokenDuration,
339
351
  verificationMaxDuration: 180,
340
352
  metadata: {
341
353
  ...metadataDescription ? { description: metadataDescription } : {},
@@ -367,8 +379,7 @@ ${changedFiles.trimEnd()}`;
367
379
 
368
380
  Continue?`.value
369
381
  });
370
- if (!confirmPush)
371
- return;
382
+ if (!confirmPush) return;
372
383
  }
373
384
  let resolveUpload;
374
385
  const uploadPromise = new Promise((resolve) => {
@@ -115,12 +115,9 @@ async function runDev({
115
115
  cliConfig,
116
116
  verbose
117
117
  }) {
118
- if (!process.env.NODE_ENV)
119
- process.env.NODE_ENV = "development";
120
- if (verbose)
121
- setH2OVerbose();
122
- if (!isH2Verbose())
123
- muteDevLogs();
118
+ if (!process.env.NODE_ENV) process.env.NODE_ENV = "development";
119
+ if (verbose) setH2OVerbose();
120
+ if (!isH2Verbose()) muteDevLogs();
124
121
  const root = appPath ?? process.cwd();
125
122
  const cliCommandPromise = getCliCommand(root);
126
123
  const backgroundPromise = getDevConfigInBackground(
@@ -31,8 +31,7 @@ async function runEnvList({ path: root = process.cwd() }) {
31
31
  config,
32
32
  cliCommand
33
33
  });
34
- if (!linkedStorefront)
35
- return;
34
+ if (!linkedStorefront) return;
36
35
  config.storefront = linkedStorefront;
37
36
  const storefront = await getStorefrontEnvironments(
38
37
  session,
@@ -45,8 +45,7 @@ async function runEnvPull({
45
45
  config,
46
46
  cliCommand
47
47
  });
48
- if (!linkedStorefront)
49
- return;
48
+ if (!linkedStorefront) return;
50
49
  config.storefront = linkedStorefront;
51
50
  if (envHandle || envBranch) {
52
51
  const environments = (await getStorefrontEnvironments(session, config.storefront.id))?.environments || [];
@@ -77,8 +76,7 @@ async function runEnvPull({
77
76
  return;
78
77
  }
79
78
  const variables = storefront.environmentVariables;
80
- if (!variables.length)
81
- return;
79
+ if (!variables.length) return;
82
80
  const fileName = colors.whiteBright(`.env`);
83
81
  const dotEnvPath = resolvePath(root, ".env");
84
82
  const fetchedEnv = {};
@@ -47,8 +47,7 @@ async function runEnvPush({
47
47
  config,
48
48
  cliCommand
49
49
  });
50
- if (!linkedStorefront)
51
- return;
50
+ if (!linkedStorefront) return;
52
51
  config.storefront = linkedStorefront;
53
52
  const { environments: environmentsData } = await getStorefrontEnvironments(session, config.storefront.id) ?? {};
54
53
  if (!environmentsData) {
@@ -88,22 +87,19 @@ async function runEnvPush({
88
87
  const comparableRemoteVars = remoteVars.sort((a, b) => a.key.localeCompare(b.key)).map(({ key, value }) => createDotEnvFileLine(key, value)).join("\n") + "\n";
89
88
  const compareableLocalVars = Object.keys(localVariables).sort((a, b) => a.localeCompare(b)).reduce((acc, key) => {
90
89
  const { isSecret, readOnly } = environmentVariables.find((variable) => variable.key === key) ?? {};
91
- if (isSecret || readOnly)
92
- return acc;
90
+ if (isSecret || readOnly) return acc;
93
91
  return [...acc, createDotEnvFileLine(key, localVariables[key])];
94
92
  }, []).join("\n") + "\n";
95
93
  if (!validatedEnvironment.name)
96
94
  throw new AbortError("Missing environment name");
97
95
  const remoteReadOnlyOrSecrets = environmentVariables.reduce(
98
96
  (acc, { isSecret, readOnly, key }) => {
99
- if (!isSecret && !readOnly)
100
- return acc;
97
+ if (!isSecret && !readOnly) return acc;
101
98
  const localVar = localVariables[key];
102
99
  const remoteVar = environmentVariables.find(
103
100
  (variable) => variable.key === key
104
101
  );
105
- if (localVar === remoteVar?.value)
106
- return acc;
102
+ if (localVar === remoteVar?.value) return acc;
107
103
  return [...acc, key];
108
104
  },
109
105
  []
@@ -131,11 +127,9 @@ async function runEnvPush({
131
127
  ${outputToken.linesDiff(diff)}
132
128
  Continue?`.value
133
129
  });
134
- if (!confirmPush)
135
- return;
130
+ if (!confirmPush) return;
136
131
  }
137
- if (!validatedEnvironment.id)
138
- throw new AbortError("Missing environment ID");
132
+ if (!validatedEnvironment.id) throw new AbortError("Missing environment ID");
139
133
  const { userErrors } = await pushStorefrontEnvVariables(
140
134
  session,
141
135
  config.storefront.id,
@@ -2,14 +2,16 @@ import * as _oclif_core_lib_interfaces_parser_js from '@oclif/core/lib/interface
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
3
  import { PackageManager } from '@shopify/cli-kit/node/node-package-manager';
4
4
 
5
- declare const ASSETS_STARTER_DIR = "starter";
6
- type AssetsDir = 'tailwind' | 'css-modules' | 'vanilla-extract' | 'postcss' | 'vite' | 'i18n' | 'routes' | 'bundle' | 'virtual-routes' | 'internal-templates' | 'external-templates' | typeof ASSETS_STARTER_DIR;
7
-
8
- type CssStrategy = Extract<AssetsDir, 'tailwind' | 'css-modules' | 'vanilla-extract' | 'postcss'>;
9
-
5
+ declare const SETUP_I18N_STRATEGIES: readonly ["subfolders", "domains", "subdomains"];
6
+ type I18nStrategy = (typeof SETUP_I18N_STRATEGIES)[number];
10
7
  declare const I18N_CHOICES: readonly ["subfolders", "domains", "subdomains", "none"];
11
8
  type I18nChoice = (typeof I18N_CHOICES)[number];
12
9
 
10
+ declare function getCliCommand(directory?: string, forcePkgManager?: 'npm' | 'pnpm' | 'yarn' | 'bun' | 'unknown'): Promise<"h2" | "yarn shopify hydrogen" | "pnpm shopify hydrogen" | "bun shopify hydrogen" | "npx shopify hydrogen">;
11
+ type CliCommand = Awaited<ReturnType<typeof getCliCommand>>;
12
+
13
+ type CssStrategy = 'tailwind' | 'css-modules' | 'vanilla-extract' | 'postcss';
14
+
13
15
  declare const STYLING_CHOICES: readonly [...CssStrategy[], "none"];
14
16
  type StylingChoice = (typeof STYLING_CHOICES)[number];
15
17
 
@@ -45,6 +47,7 @@ declare class Init extends Command {
45
47
  'package-manager': _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
46
48
  shortcut: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
47
49
  markets: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
50
+ styling: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
48
51
  'mock-shop': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
49
52
  'install-deps': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
50
53
  path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -57,20 +60,20 @@ declare class Init extends Command {
57
60
  declare function runInit({ markets, ...options }?: InitOptions & {
58
61
  markets?: InitOptions['i18n'];
59
62
  }): Promise<{
60
- language?: "ts" | "js" | undefined;
61
- packageManager: "unknown" | "yarn" | "npm" | "pnpm" | "bun";
62
- cssStrategy?: CssStrategy | undefined;
63
- cliCommand: "h2" | "yarn shopify hydrogen" | "pnpm shopify hydrogen" | "bun shopify hydrogen" | "npx shopify hydrogen";
63
+ language?: "ts" | "js";
64
+ packageManager: "npm" | "pnpm" | "yarn" | "bun" | "unknown";
65
+ cssStrategy?: CssStrategy;
66
+ cliCommand: CliCommand;
64
67
  depsInstalled: boolean;
65
- depsError?: Error | undefined;
66
- i18n?: "subfolders" | "domains" | "subdomains" | undefined;
67
- i18nError?: Error | undefined;
68
- routes?: Record<string, string | string[]> | undefined;
69
- routesError?: Error | undefined;
68
+ depsError?: Error;
69
+ i18n?: I18nStrategy;
70
+ i18nError?: Error;
71
+ routes?: Record<string, string | string[]>;
72
+ routesError?: Error;
70
73
  location: string;
71
74
  name: string;
72
75
  directory: string;
73
- storefrontTitle?: string | undefined;
76
+ storefrontTitle?: string;
74
77
  } | undefined>;
75
78
 
76
79
  export { Init as default, runInit };
@@ -1,15 +1,13 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
- import { packageManagerFromUserAgent } from '@shopify/cli-kit/node/node-package-manager';
3
2
  import { Flags } from '@oclif/core';
4
3
  import { AbortError } from '@shopify/cli-kit/node/error';
5
4
  import { commonFlags, flagsToCamelObject, parseProcessFlags } from '../../lib/flags.js';
6
- import { checkHydrogenVersion } from '../../lib/check-version.js';
5
+ import { checkCurrentCLIVersion } from '../../lib/check-cli-version.js';
6
+ import { STYLING_CHOICES } from '../../lib/setups/css/index.js';
7
7
  import { I18N_CHOICES } from '../../lib/setups/i18n/index.js';
8
- import { supressNodeExperimentalWarnings, execAsync } from '../../lib/process.js';
8
+ import { supressNodeExperimentalWarnings } from '../../lib/process.js';
9
9
  import { setupTemplate } from '../../lib/onboarding/index.js';
10
10
  import { LANGUAGES } from '../../lib/onboarding/common.js';
11
- import { currentProcessIsGlobal, inferPackageManagerForGlobalCLI } from '@shopify/cli-kit/node/is-global';
12
- import { getPkgJsonPath } from '../../lib/build.js';
13
11
 
14
12
  const FLAG_MAP = { f: "force" };
15
13
  class Init extends Command {
@@ -35,6 +33,7 @@ class Init extends Command {
35
33
  description: "Use mock.shop as the data source for the storefront.",
36
34
  env: "SHOPIFY_HYDROGEN_FLAG_MOCK_DATA"
37
35
  }),
36
+ ...commonFlags.styling,
38
37
  ...commonFlags.markets,
39
38
  ...commonFlags.shortcut,
40
39
  routes: Flags.boolean({
@@ -80,6 +79,11 @@ async function runInit({
80
79
  `Invalid URL structure strategy: ${options.i18n}. Must be one of ${I18N_CHOICES.join(", ")}`
81
80
  );
82
81
  }
82
+ if (options.styling && !STYLING_CHOICES.includes(options.styling)) {
83
+ throw new AbortError(
84
+ `Invalid styling strategy: ${options.styling}. Must be one of ${STYLING_CHOICES.join(", ")}`
85
+ );
86
+ }
83
87
  options.git ??= true;
84
88
  if (options.quickstart) {
85
89
  options.i18n ??= "none";
@@ -89,27 +93,11 @@ async function runInit({
89
93
  options.path ??= "./hydrogen-quickstart";
90
94
  options.routes ??= true;
91
95
  options.shortcut ??= true;
96
+ options.styling ??= "none";
92
97
  }
93
- const npmPrefix = (await execAsync("npm prefix -s")).stdout.trim();
94
- const isH2 = process.argv[1]?.startsWith(npmPrefix);
95
- const isGlobal = currentProcessIsGlobal() && !isH2;
96
- const showUpgrade = await checkHydrogenVersion(
97
- // Resolving the CLI package from a local directory might fail because
98
- // this code could be run from a global dependency (e.g. on `npm create`).
99
- // Therefore, pass the known path to the package.json directly from here:
100
- await getPkgJsonPath(),
101
- isGlobal ? "cli" : "cliHydrogen"
102
- );
98
+ const showUpgrade = await checkCurrentCLIVersion();
103
99
  if (showUpgrade) {
104
- let packageManager2 = options.packageManager ?? packageManagerFromUserAgent();
105
- if (packageManager2 === "unknown" || !packageManager2) {
106
- packageManager2 = inferPackageManagerForGlobalCLI();
107
- }
108
- const globalInstallCommand = packageManager2 === "yarn" ? `yarn global add @shopify/cli` : `${packageManager2} install -g @shopify/cli`;
109
- const globalMessage = `Please install the latest Shopify CLI version with \`${globalInstallCommand}\` and try again.`;
110
- const localMessage = `Please use the latest version with \`${packageManager2} create @shopify/hydrogen@latest\``;
111
- const message = isGlobal ? globalMessage : localMessage;
112
- showUpgrade(message);
100
+ showUpgrade(options.packageManager);
113
101
  }
114
102
  return setupTemplate(options);
115
103
  }
@@ -47,8 +47,7 @@ async function runLink({
47
47
  flagStorefront,
48
48
  cliCommand
49
49
  });
50
- if (!linkedStore)
51
- return;
50
+ if (!linkedStore) return;
52
51
  renderSuccess({
53
52
  body: [{ userInput: linkedStore.title }, "is now linked"],
54
53
  nextSteps: [
@@ -87,10 +87,8 @@ async function runPreview({
87
87
  }) {
88
88
  if (!process.env.NODE_ENV)
89
89
  process.env.NODE_ENV = watch ? "development" : "production";
90
- if (verbose)
91
- setH2OVerbose();
92
- if (!isH2Verbose())
93
- muteDevLogs();
90
+ if (verbose) setH2OVerbose();
91
+ if (!isH2Verbose()) muteDevLogs();
94
92
  let { root, buildPath, buildPathWorkerFile, buildPathClient } = getProjectPaths(directory);
95
93
  const isClassicProject = await hasRemixConfigFile(root);
96
94
  if (watch && isClassicProject) {
@@ -107,12 +105,12 @@ async function runPreview({
107
105
  disableRouteWarning: false,
108
106
  lockfileCheck: false,
109
107
  sourcemap: true,
108
+ bundleStats: false,
110
109
  useCodegen,
111
110
  codegenConfigPath
112
111
  };
113
112
  const buildProcess = shouldBuild ? isClassicProject ? await runClassicCompilerBuild({
114
- ...buildOptions,
115
- bundleStats: false
113
+ ...buildOptions
116
114
  }).then(projectBuild.resolve) : await runBuild({
117
115
  ...buildOptions,
118
116
  watch,
@@ -1,11 +1,11 @@
1
1
  import { resolvePath } from '@shopify/cli-kit/node/path';
2
2
  import { commonFlags, overrideFlag, flagsToCamelObject } from '../../../lib/flags.js';
3
3
  import Command from '@shopify/cli-kit/node/base-command';
4
- import { renderTasks, renderSuccess } from '@shopify/cli-kit/node/ui';
4
+ import { renderSuccess, renderTasks, renderWarning } from '@shopify/cli-kit/node/ui';
5
5
  import { getPackageManager, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
6
6
  import { Args } from '@oclif/core';
7
- import { hasRemixConfigFile, getRemixConfig } from '../../../lib/remix-config.js';
8
- import { SETUP_CSS_STRATEGIES, renderCssPrompt, setupCssStrategy, CSS_STRATEGY_NAME_MAP } from '../../../lib/setups/css/index.js';
7
+ import { SETUP_CSS_STRATEGIES, renderCssPrompt, CSS_STRATEGY_NAME_MAP, CSS_STRATEGY_HELP_URL_MAP, setupCssStrategy } from '../../../lib/setups/css/index.js';
8
+ import { getViteConfig } from '../../../lib/vite-config.js';
9
9
  import { AbortError } from '@shopify/cli-kit/node/error';
10
10
 
11
11
  class SetupCSS extends Command {
@@ -39,18 +39,25 @@ async function runSetupCSS({
39
39
  force = false,
40
40
  installDeps = true
41
41
  }) {
42
- if (!await hasRemixConfigFile(directory)) {
42
+ const viteConfig = await getViteConfig(directory).catch(() => null);
43
+ if (!viteConfig) {
43
44
  throw new AbortError(
44
- "No remix.config.js file found. This command is not supported in Vite projects."
45
+ "No Vite config found. This command is only supported in Vite projects."
45
46
  );
46
47
  }
47
- const remixConfigPromise = getRemixConfig(directory);
48
+ const { remixConfig } = viteConfig;
48
49
  const strategy = flagStrategy ? flagStrategy : await renderCssPrompt();
49
- const remixConfig = await remixConfigPromise;
50
- const setupOutput = await setupCssStrategy(strategy, remixConfig, force);
51
- if (!setupOutput)
50
+ if (strategy === "css-modules" || strategy === "postcss") {
51
+ renderSuccess({
52
+ headline: `Vite works out of the box with ${CSS_STRATEGY_NAME_MAP[strategy]}.`,
53
+ body: `See the Vite documentation for more information:
54
+ ${CSS_STRATEGY_HELP_URL_MAP[strategy]}`
55
+ });
52
56
  return;
53
- const { workPromise, generatedAssets, helpUrl } = setupOutput;
57
+ }
58
+ const setupOutput = await setupCssStrategy(strategy, remixConfig, force);
59
+ if (!setupOutput) return;
60
+ const { workPromise, generatedAssets, needsInstallDeps } = setupOutput;
54
61
  const tasks = [
55
62
  {
56
63
  title: "Updating files",
@@ -59,7 +66,8 @@ async function runSetupCSS({
59
66
  }
60
67
  }
61
68
  ];
62
- if (installDeps) {
69
+ let isNpm = false;
70
+ if (installDeps && needsInstallDeps) {
63
71
  const gettingPkgManagerPromise = getPackageManager(
64
72
  remixConfig.rootDirectory
65
73
  );
@@ -67,6 +75,7 @@ async function runSetupCSS({
67
75
  title: "Installing new dependencies",
68
76
  task: async () => {
69
77
  const packageManager = await gettingPkgManagerPromise;
78
+ isNpm = packageManager === "npm" || packageManager === "unknown";
70
79
  await installNodeModules({
71
80
  directory: remixConfig.rootDirectory,
72
81
  packageManager,
@@ -79,8 +88,16 @@ async function runSetupCSS({
79
88
  renderSuccess({
80
89
  headline: `${CSS_STRATEGY_NAME_MAP[strategy]} setup complete.`,
81
90
  body: (generatedAssets.length > 0 ? "You can now modify CSS configuration in the following files:\n" + generatedAssets.map((file) => ` - ${file}`).join("\n") + "\n" : "") + `
82
- For more information, visit ${helpUrl}.`
91
+ For more information, visit ${CSS_STRATEGY_HELP_URL_MAP[strategy]}`
83
92
  });
93
+ if (needsInstallDeps && isNpm && strategy === "tailwind") {
94
+ renderWarning({
95
+ body: [
96
+ "Due to a bug in NPM, you might need to reinstall dependencies again.\nRun",
97
+ { command: "npm install" }
98
+ ]
99
+ });
100
+ }
84
101
  }
85
102
 
86
103
  export { SetupCSS as default, runSetupCSS };
@@ -201,8 +201,7 @@ $1$2`
201
201
  }
202
202
  },
203
203
  async (err, nodes) => {
204
- if (err)
205
- reject(err);
204
+ if (err) reject(err);
206
205
  const nodeMap = {};
207
206
  nodes.forEach((node) => {
208
207
  if (node.text().endsWith(".module.css")) {
@@ -229,8 +228,7 @@ $1$2`
229
228
  resolve(null);
230
229
  }
231
230
  );
232
- if (fileNumber === 0)
233
- resolve(null);
231
+ if (fileNumber === 0) resolve(null);
234
232
  });
235
233
  await replaceFileContent(rootFilepath, formatOptions, (content) => {
236
234
  const root = astGrep.parse(content).root();
@@ -271,8 +269,7 @@ $1$2`
271
269
  ...liveReloadElements.reverse(),
272
270
  liveReloadImport
273
271
  ]) {
274
- if (!node)
275
- continue;
272
+ if (!node) continue;
276
273
  const { start, end } = node.range();
277
274
  content = content.slice(0, start.index) + content.slice(end.index);
278
275
  }
@@ -77,11 +77,13 @@ async function runSetup(options) {
77
77
  })
78
78
  ])
79
79
  ).then(async () => {
80
- routes = await setupRoutes(
81
- rootDirectory,
82
- typescript ? "ts" : "js",
83
- i18n
84
- );
80
+ routes = await setupRoutes(rootDirectory, typescript ? "ts" : "js", {
81
+ i18nStrategy: i18n,
82
+ // User might have added files before running this command.
83
+ // We should overwrite them to ensure the routes are set up correctly.
84
+ // Relies on Git to restore the files if needed.
85
+ overwriteFileDeps: true
86
+ });
85
87
  });
86
88
  }
87
89
  if (i18n) {
@@ -95,8 +97,7 @@ async function runSetup(options) {
95
97
  cliCommand,
96
98
  options.shortcut
97
99
  );
98
- if (!i18n && !needsRouteGeneration && !createShortcut)
99
- return;
100
+ if (!i18n && !needsRouteGeneration && !createShortcut) return;
100
101
  if (createShortcut) {
101
102
  backgroundWorkPromise = backgroundWorkPromise.then(async () => {
102
103
  if (await createShortcut()) {
@@ -153,8 +153,7 @@ async function getHydrogenVersion({ appPath }) {
153
153
  return { currentVersion, currentDependencies };
154
154
  }
155
155
  async function getChangelog() {
156
- if (CACHED_CHANGELOG)
157
- return CACHED_CHANGELOG;
156
+ if (CACHED_CHANGELOG) return CACHED_CHANGELOG;
158
157
  if (isHydrogenMonorepo && hydrogenPackagesPath && process.env.FORCE_CHANGELOG_SOURCE !== "remote") {
159
158
  const require2 = createRequire(import.meta.url);
160
159
  return require2(joinPath(
@@ -189,8 +188,7 @@ function hasOutdatedDependencies({
189
188
  ...release.devDependencies
190
189
  }).some(([name, version]) => {
191
190
  const currentDependencyVersion = currentDependencies?.[name];
192
- if (!currentDependencyVersion)
193
- return false;
191
+ if (!currentDependencyVersion) return false;
194
192
  const isDependencyOutdated = semver.gt(
195
193
  getAbsoluteVersion(version),
196
194
  getAbsoluteVersion(currentDependencyVersion)
@@ -204,11 +202,9 @@ function isUpgradeableRelease({
204
202
  release
205
203
  }) {
206
204
  const isHydrogenOutdated = semver.gt(release.version, currentPinnedVersion);
207
- if (isHydrogenOutdated)
208
- return true;
205
+ if (isHydrogenOutdated) return true;
209
206
  const isCurrentHydrogen = getAbsoluteVersion(release.version) === currentPinnedVersion;
210
- if (!isCurrentHydrogen)
211
- return false;
207
+ if (!isCurrentHydrogen) return false;
212
208
  return hasOutdatedDependencies({ release, currentDependencies });
213
209
  }
214
210
  function getAvailableUpgrades({
@@ -224,8 +220,7 @@ function getAvailableUpgrades({
224
220
  currentPinnedVersion,
225
221
  currentDependencies
226
222
  });
227
- if (!isUpgradeable)
228
- return false;
223
+ if (!isUpgradeable) return false;
229
224
  if (currentMajorVersion !== release.version) {
230
225
  currentMajorVersion = release.version;
231
226
  return true;
@@ -233,8 +228,7 @@ function getAvailableUpgrades({
233
228
  return false;
234
229
  });
235
230
  const uniqueAvailableUpgrades = availableUpgrades.reduce((acc, release) => {
236
- if (acc[release.version])
237
- return acc;
231
+ if (acc[release.version]) return acc;
238
232
  acc[release.version] = release;
239
233
  return acc;
240
234
  }, {});
@@ -262,11 +256,9 @@ function getCummulativeRelease({
262
256
  }
263
257
  const upgradingReleases = availableUpgrades.filter((release) => {
264
258
  const isHydrogenUpgrade = semver.gt(release.version, currentPinnedVersion) && semver.lte(release.version, selectedRelease.version);
265
- if (isHydrogenUpgrade)
266
- return true;
259
+ if (isHydrogenUpgrade) return true;
267
260
  const isSameHydrogenVersion = getAbsoluteVersion(release.version) === currentPinnedVersion;
268
- if (!isSameHydrogenVersion || !currentDependencies)
269
- return false;
261
+ if (!isSameHydrogenVersion || !currentDependencies) return false;
270
262
  return hasOutdatedDependencies({ release, currentDependencies });
271
263
  });
272
264
  return upgradingReleases.reduce(
@@ -330,27 +322,21 @@ function maybeIncludeDependency({
330
322
  }) {
331
323
  const existingDependencyVersion = currentDependencies[name];
332
324
  const isRemixPackage = isRemixDependency([name, version]);
333
- if (isRemixPackage)
334
- return false;
325
+ if (isRemixPackage) return false;
335
326
  const isNextVersion = existingDependencyVersion === "next";
336
- if (isNextVersion)
337
- return false;
327
+ if (isNextVersion) return false;
338
328
  const depMeta = selectedRelease.dependenciesMeta?.[name];
339
- if (!depMeta)
340
- return true;
329
+ if (!depMeta) return true;
341
330
  const isRequired = Boolean(
342
331
  selectedRelease.dependenciesMeta?.[name]?.required
343
332
  );
344
- if (!isRequired)
345
- return false;
346
- if (!existingDependencyVersion)
347
- return true;
333
+ if (!isRequired) return false;
334
+ if (!existingDependencyVersion) return true;
348
335
  const isOlderVersion = semver.lt(
349
336
  getAbsoluteVersion(existingDependencyVersion),
350
337
  getAbsoluteVersion(version)
351
338
  );
352
- if (isOlderVersion)
353
- return true;
339
+ if (isOlderVersion) return true;
354
340
  return false;
355
341
  }
356
342
  function buildUpgradeCommandArgs({
@@ -364,8 +350,7 @@ function buildUpgradeCommandArgs({
364
350
  dependency,
365
351
  selectedRelease
366
352
  });
367
- if (!shouldUpgradeDep)
368
- continue;
353
+ if (!shouldUpgradeDep) continue;
369
354
  args.push(`${dependency[0]}@${getAbsoluteVersion(dependency[1])}`);
370
355
  }
371
356
  for (const dependency of Object.entries(selectedRelease.devDependencies)) {
@@ -374,8 +359,7 @@ function buildUpgradeCommandArgs({
374
359
  dependency,
375
360
  selectedRelease
376
361
  });
377
- if (!shouldUpgradeDep)
378
- continue;
362
+ if (!shouldUpgradeDep) continue;
379
363
  args.push(`${dependency[0]}@${getAbsoluteVersion(dependency[1])}`);
380
364
  }
381
365
  const currentRemix = Object.entries(currentDependencies).find(isRemixDependency);
@@ -1,11 +1,41 @@
1
- import { spawnSync } from 'child_process';
2
- import { outputDebug } from '@shopify/cli-kit/node/output';
1
+ import { createRequire } from 'node:module';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { outputNewline, outputDebug } from '@shopify/cli-kit/node/output';
4
+ import { cwd, resolvePath, joinPath } from '@shopify/cli-kit/node/path';
5
+ import { renderWarning } from '@shopify/cli-kit/node/ui';
3
6
 
4
- const EXPERIMENTAL_VM_MODULES_FLAG = "--experimental-vm-modules";
5
- function commandNeedsVM(id = "", argv = []) {
6
- return id === "hydrogen:debug:cpu" || ["hydrogen:dev", "hydrogen:preview"].includes(id) && argv.includes("--legacy-runtime");
7
- }
8
7
  const hook = async function(options) {
8
+ if (!options.id?.startsWith("hydrogen:") || options.id === "hydrogen:init") {
9
+ return;
10
+ }
11
+ let projectPath = cwd();
12
+ const pathFlagRE = /^--path($|=)/;
13
+ const pathFlagIndex = options.argv.findIndex((arg) => pathFlagRE.test(arg));
14
+ if (pathFlagIndex !== -1) {
15
+ const pathFlagValue = options.argv[pathFlagIndex]?.split("=")[1] ?? options.argv[pathFlagIndex + 1];
16
+ if (pathFlagValue && !pathFlagValue.startsWith("--")) {
17
+ projectPath = resolvePath(projectPath, pathFlagValue);
18
+ }
19
+ }
20
+ if (!isHydrogenProject(projectPath)) {
21
+ outputNewline();
22
+ renderWarning({
23
+ headline: `Looks like you're trying to run a Hydrogen command outside of a Hydrogen project.`,
24
+ body: [
25
+ "Run",
26
+ { command: "shopify hydrogen init" },
27
+ "to create a new Hydrogen project or use the",
28
+ { command: "--path" },
29
+ "flag to specify an existing Hydrogen project.\n\n",
30
+ { subdued: projectPath }
31
+ ],
32
+ reference: [
33
+ "Getting started: https://shopify.dev/docs/storefronts/headless/hydrogen",
34
+ "CLI commands: https://shopify.dev/docs/api/shopify-cli/hydrogen"
35
+ ]
36
+ });
37
+ process.exit(1);
38
+ }
9
39
  if (commandNeedsVM(options.id, options.argv) && !process.execArgv.includes(EXPERIMENTAL_VM_MODULES_FLAG) && !(process.env.NODE_OPTIONS ?? "").includes(EXPERIMENTAL_VM_MODULES_FLAG)) {
10
40
  outputDebug(
11
41
  `Restarting CLI process with ${EXPERIMENTAL_VM_MODULES_FLAG} flag.`
@@ -16,6 +46,20 @@ const hook = async function(options) {
16
46
  process.exit(result.status ?? 1);
17
47
  }
18
48
  };
49
+ const EXPERIMENTAL_VM_MODULES_FLAG = "--experimental-vm-modules";
50
+ function commandNeedsVM(id = "", argv = []) {
51
+ return id === "hydrogen:debug:cpu" || ["hydrogen:dev", "hydrogen:preview"].includes(id) && argv.includes("--legacy-runtime");
52
+ }
53
+ function isHydrogenProject(projectPath) {
54
+ try {
55
+ const require2 = createRequire(import.meta.url);
56
+ const { dependencies } = require2(joinPath(projectPath, "package.json"));
57
+ return !!dependencies["@shopify/hydrogen"] || // Diff examples only have this package as a dependency
58
+ !!dependencies["@shopify/cli-hydrogen"];
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
19
63
  var init_default = hook;
20
64
 
21
65
  export { init_default as default };