@shopify/cli-hydrogen 4.0.8 → 4.1.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 (76) hide show
  1. package/README.md +9 -0
  2. package/dist/commands/hydrogen/build.d.ts +4 -1
  3. package/dist/commands/hydrogen/build.js +21 -17
  4. package/dist/commands/hydrogen/check.d.ts +3 -6
  5. package/dist/commands/hydrogen/check.js +10 -9
  6. package/dist/commands/hydrogen/dev.d.ts +3 -2
  7. package/dist/commands/hydrogen/dev.js +24 -22
  8. package/dist/commands/hydrogen/g.d.ts +10 -0
  9. package/dist/commands/hydrogen/g.js +17 -0
  10. package/dist/commands/hydrogen/generate/route.d.ts +7 -9
  11. package/dist/commands/hydrogen/generate/route.js +49 -47
  12. package/dist/commands/hydrogen/generate/route.test.js +48 -40
  13. package/dist/commands/hydrogen/generate/routes.d.ts +2 -2
  14. package/dist/commands/hydrogen/init.d.ts +3 -3
  15. package/dist/commands/hydrogen/init.js +76 -95
  16. package/dist/commands/hydrogen/init.test.js +126 -0
  17. package/dist/commands/hydrogen/preview.d.ts +2 -2
  18. package/dist/commands/hydrogen/preview.js +4 -4
  19. package/dist/commands/hydrogen/shortcut.d.ts +9 -0
  20. package/dist/commands/hydrogen/shortcut.js +74 -0
  21. package/dist/commands/hydrogen/shortcut.test.js +58 -0
  22. package/dist/generator-templates/routes/[robots.txt].tsx +35 -1
  23. package/dist/generator-templates/routes/[sitemap.xml].tsx +45 -10
  24. package/dist/generator-templates/routes/account/login.tsx +42 -13
  25. package/dist/generator-templates/routes/account/register.tsx +42 -13
  26. package/dist/generator-templates/routes/cart.tsx +42 -2
  27. package/dist/generator-templates/routes/collections/$collectionHandle.tsx +44 -5
  28. package/dist/generator-templates/routes/index.tsx +33 -0
  29. package/dist/generator-templates/routes/pages/$pageHandle.tsx +48 -10
  30. package/dist/generator-templates/routes/policies/$policyHandle.tsx +67 -14
  31. package/dist/generator-templates/routes/policies/index.tsx +54 -4
  32. package/dist/generator-templates/routes/products/$productHandle.tsx +44 -9
  33. package/dist/hooks/init.js +2 -2
  34. package/dist/{utils → lib}/check-lockfile.js +7 -4
  35. package/dist/{utils → lib}/check-lockfile.test.js +19 -28
  36. package/dist/{utils → lib}/check-version.test.js +3 -2
  37. package/dist/lib/colors.d.ts +8 -0
  38. package/dist/lib/colors.js +8 -0
  39. package/dist/{utils → lib}/config.js +10 -19
  40. package/dist/{utils → lib}/flags.d.ts +9 -3
  41. package/dist/{utils → lib}/flags.js +19 -4
  42. package/dist/lib/flags.test.d.ts +1 -0
  43. package/dist/{utils → lib}/mini-oxygen.js +14 -12
  44. package/dist/{utils → lib}/missing-routes.js +1 -1
  45. package/dist/lib/remix-version-interop.d.ts +11 -0
  46. package/dist/lib/remix-version-interop.js +54 -0
  47. package/dist/lib/remix-version-interop.test.d.ts +1 -0
  48. package/dist/lib/remix-version-interop.test.js +93 -0
  49. package/dist/lib/shell.d.ts +12 -0
  50. package/dist/lib/shell.js +73 -0
  51. package/dist/lib/template-downloader.d.ts +6 -0
  52. package/dist/{utils → lib}/template-downloader.js +21 -16
  53. package/dist/{utils → lib}/transpile-ts.js +5 -5
  54. package/dist/lib/virtual-routes.test.d.ts +1 -0
  55. package/dist/virtual-routes/routes/index.jsx +2 -15
  56. package/dist/virtual-routes/virtual-root.jsx +5 -6
  57. package/oclif.manifest.json +1 -1
  58. package/package.json +11 -10
  59. package/dist/utils/template-downloader.d.ts +0 -11
  60. /package/dist/{utils/check-lockfile.test.d.ts → commands/hydrogen/init.test.d.ts} +0 -0
  61. /package/dist/{utils/check-version.test.d.ts → commands/hydrogen/shortcut.test.d.ts} +0 -0
  62. /package/dist/{utils → lib}/check-lockfile.d.ts +0 -0
  63. /package/dist/{utils/flags.test.d.ts → lib/check-lockfile.test.d.ts} +0 -0
  64. /package/dist/{utils → lib}/check-version.d.ts +0 -0
  65. /package/dist/{utils → lib}/check-version.js +0 -0
  66. /package/dist/{utils/virtual-routes.test.d.ts → lib/check-version.test.d.ts} +0 -0
  67. /package/dist/{utils → lib}/config.d.ts +0 -0
  68. /package/dist/{utils → lib}/flags.test.js +0 -0
  69. /package/dist/{utils → lib}/log.d.ts +0 -0
  70. /package/dist/{utils → lib}/log.js +0 -0
  71. /package/dist/{utils → lib}/mini-oxygen.d.ts +0 -0
  72. /package/dist/{utils → lib}/missing-routes.d.ts +0 -0
  73. /package/dist/{utils → lib}/transpile-ts.d.ts +0 -0
  74. /package/dist/{utils → lib}/virtual-routes.d.ts +0 -0
  75. /package/dist/{utils → lib}/virtual-routes.js +0 -0
  76. /package/dist/{utils → lib}/virtual-routes.test.js +0 -0
package/README.md CHANGED
@@ -3,3 +3,12 @@
3
3
  The Hydrogen extension for the [Shopify CLI](https://shopify.dev/apps/tools/cli). Hydrogen is a set of tools, utilities, and best-in-class examples for building a commerce application with [Remix](https://wwww.remix.run).
4
4
 
5
5
  [Check out the docs](https://shopify.dev/custom-storefronts/hydrogen)
6
+
7
+ ## Contributing
8
+
9
+ The most common way to test the cli changes locally is to do the following:
10
+
11
+ - Run `npm run build` in this directory (`packages/cli` from the root of the repo).
12
+ - Run `npx shopify hydrogen` anywhere else in the monorepo, for example `npx shopify hydrogen init`.
13
+ - If you want to test a command inside of a template, run the command from within that template or use the `--path` flag to point to another template or any Hydrogen app.
14
+ - If you want to make changes to a file that is generated when running `npx shopify hydrogen generate`, make changes to that file from inside of the `templates/skeleton` directory.
@@ -4,9 +4,12 @@ import Command from '@shopify/cli-kit/node/base-command';
4
4
  declare class Build extends Command {
5
5
  static description: string;
6
6
  static flags: {
7
- path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined>;
7
+ path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
8
8
  sourcemap: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
9
9
  "disable-route-warning": _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
10
+ base: _oclif_core_lib_interfaces_parser_js.OptionFlag<unknown, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
11
+ entry: _oclif_core_lib_interfaces_parser_js.OptionFlag<unknown, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
12
+ target: _oclif_core_lib_interfaces_parser_js.OptionFlag<unknown, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
10
13
  };
11
14
  run(): Promise<void>;
12
15
  }
@@ -1,13 +1,14 @@
1
1
  import path from 'path';
2
- import { file, output } from '@shopify/cli-kit';
3
- import colors from '@shopify/cli-kit/node/colors';
4
- import { getProjectPaths, getRemixConfig } from '../../utils/config.js';
5
- import { commonFlags, flagsToCamelObject } from '../../utils/flags.js';
2
+ import { outputInfo, outputContent, outputToken, outputWarn } from '@shopify/cli-kit/node/output';
3
+ import { rmdir, fileSize, copyFile } from '@shopify/cli-kit/node/fs';
4
+ import { getProjectPaths, getRemixConfig } from '../../lib/config.js';
5
+ import { commonFlags, deprecated, flagsToCamelObject } from '../../lib/flags.js';
6
6
  import Command from '@shopify/cli-kit/node/base-command';
7
- import Flags from '@oclif/core/lib/flags.js';
8
- import { checkLockfileStatus } from '../../utils/check-lockfile.js';
9
- import { findMissingRoutes } from '../../utils/missing-routes.js';
7
+ import { Flags } from '@oclif/core';
8
+ import { checkLockfileStatus } from '../../lib/check-lockfile.js';
9
+ import { findMissingRoutes } from '../../lib/missing-routes.js';
10
10
  import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
11
+ import { colors } from '../../lib/colors.js';
11
12
 
12
13
  const LOG_WORKER_BUILT = "\u{1F4E6} Worker built";
13
14
  class Build extends Command {
@@ -22,7 +23,10 @@ class Build extends Command {
22
23
  ["disable-route-warning"]: Flags.boolean({
23
24
  description: "Disable warning about missing standard routes.",
24
25
  env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_ROUTE_WARNING"
25
- })
26
+ }),
27
+ base: deprecated("--base")(),
28
+ entry: deprecated("--entry")(),
29
+ target: deprecated("--target")()
26
30
  };
27
31
  async run() {
28
32
  const { flags } = await this.parse(Build);
@@ -43,9 +47,9 @@ async function runBuild({
43
47
  console.time(LOG_WORKER_BUILT);
44
48
  const [remixConfig] = await Promise.all([
45
49
  getRemixConfig(root),
46
- file.rmdir(buildPath, { force: true })
50
+ rmdir(buildPath, { force: true })
47
51
  ]);
48
- output.info(`
52
+ outputInfo(`
49
53
  \u{1F3D7}\uFE0F Building in ${process.env.NODE_ENV} mode...`);
50
54
  const { build } = await import('@remix-run/dev/dist/compiler/build.js');
51
55
  const { logCompileFailure } = await import('@remix-run/dev/dist/compiler/onCompileFailure.js');
@@ -62,14 +66,14 @@ async function runBuild({
62
66
  ]);
63
67
  if (process.env.NODE_ENV !== "development") {
64
68
  console.timeEnd(LOG_WORKER_BUILT);
65
- const sizeMB = await file.size(buildPathWorkerFile) / (1024 * 1024);
66
- output.info(
67
- output.content` ${colors.dim(
69
+ const sizeMB = await fileSize(buildPathWorkerFile) / (1024 * 1024);
70
+ outputInfo(
71
+ outputContent` ${colors.dim(
68
72
  path.relative(root, buildPathWorkerFile)
69
- )} ${output.token.yellow(sizeMB.toFixed(2))} MB\n`
73
+ )} ${outputToken.yellow(sizeMB.toFixed(2))} MB\n`
70
74
  );
71
75
  if (sizeMB >= 1) {
72
- output.warn(
76
+ outputWarn(
73
77
  `\u{1F6A8} Worker bundle exceeds 1 MB! This can delay your worker response.${remixConfig.serverMinify ? "" : " Minify your bundle by adding `serverMinify: true` to remix.config.js."}
74
78
  `
75
79
  );
@@ -80,7 +84,7 @@ async function runBuild({
80
84
  if (missingRoutes.length) {
81
85
  const packageManager = await getPackageManager(root);
82
86
  const exec = packageManager === "npm" ? "npx" : packageManager;
83
- output.warn(
87
+ outputWarn(
84
88
  `Heads up: Shopify stores have a number of standard routes that aren\u2019t set up yet.
85
89
  Some functionality and backlinks might not work as expected until these are created or redirects are set up.
86
90
  This build is missing ${missingRoutes.length} route${missingRoutes.length > 1 ? "s" : ""}. For more details, run \`${exec} shopify hydrogen check routes\`.
@@ -91,7 +95,7 @@ This build is missing ${missingRoutes.length} route${missingRoutes.length > 1 ?
91
95
  process.exit(0);
92
96
  }
93
97
  async function copyPublicFiles(publicPath, buildPathClient) {
94
- return file.copy(publicPath, buildPathClient);
98
+ return copyFile(publicPath, buildPathClient);
95
99
  }
96
100
 
97
101
  export { copyPublicFiles, Build as default, runBuild };
@@ -4,14 +4,11 @@ import Command from '@shopify/cli-kit/node/base-command';
4
4
  declare class GenerateRoute extends Command {
5
5
  static description: string;
6
6
  static flags: {
7
- path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined>;
7
+ path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
8
8
  };
9
9
  static args: {
10
- name: string;
11
- description: string;
12
- required: boolean;
13
- options: string[];
14
- }[];
10
+ resource: _oclif_core_lib_interfaces_parser_js.Arg<string, Record<string, unknown>>;
11
+ };
15
12
  run(): Promise<void>;
16
13
  }
17
14
 
@@ -1,25 +1,26 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
- import { path } from '@shopify/cli-kit';
3
- import { commonFlags } from '../../utils/flags.js';
4
- import { getRemixConfig } from '../../utils/config.js';
5
- import { logMissingRoutes, findMissingRoutes } from '../../utils/missing-routes.js';
2
+ import { resolvePath } from '@shopify/cli-kit/node/path';
3
+ import { commonFlags } from '../../lib/flags.js';
4
+ import { getRemixConfig } from '../../lib/config.js';
5
+ import { logMissingRoutes, findMissingRoutes } from '../../lib/missing-routes.js';
6
+ import { Args } from '@oclif/core';
6
7
 
7
8
  class GenerateRoute extends Command {
8
9
  static description = "Returns diagnostic information about a Hydrogen storefront.";
9
10
  static flags = {
10
11
  path: commonFlags.path
11
12
  };
12
- static args = [
13
- {
13
+ static args = {
14
+ resource: Args.string({
14
15
  name: "resource",
15
16
  description: `The resource to check. Currently only 'routes' is supported.`,
16
17
  required: true,
17
18
  options: ["routes"]
18
- }
19
- ];
19
+ })
20
+ };
20
21
  async run() {
21
22
  const { flags, args } = await this.parse(GenerateRoute);
22
- const directory = flags.path ? path.resolve(flags.path) : process.cwd();
23
+ const directory = flags.path ? resolvePath(flags.path) : process.cwd();
23
24
  if (args.resource === "routes") {
24
25
  await runCheckRoutes({ directory });
25
26
  } else {
@@ -4,9 +4,10 @@ import Command from '@shopify/cli-kit/node/base-command';
4
4
  declare class Dev extends Command {
5
5
  static description: string;
6
6
  static flags: {
7
- path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined>;
8
- port: _oclif_core_lib_interfaces_parser_js.OptionFlag<number>;
7
+ path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
8
+ port: _oclif_core_lib_interfaces_parser_js.OptionFlag<number, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
9
9
  "disable-virtual-routes": _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
10
+ host: _oclif_core_lib_interfaces_parser_js.OptionFlag<unknown, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
10
11
  };
11
12
  run(): Promise<void>;
12
13
  }
@@ -1,15 +1,16 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs/promises';
3
- import { output, file } from '@shopify/cli-kit';
3
+ import { outputInfo } from '@shopify/cli-kit/node/output';
4
+ import { fileExists } from '@shopify/cli-kit/node/fs';
4
5
  import { copyPublicFiles } from './build.js';
5
- import { getProjectPaths, getRemixConfig } from '../../utils/config.js';
6
- import { muteDevLogs } from '../../utils/log.js';
7
- import { commonFlags, flagsToCamelObject } from '../../utils/flags.js';
6
+ import { getProjectPaths, getRemixConfig } from '../../lib/config.js';
7
+ import { muteDevLogs } from '../../lib/log.js';
8
+ import { commonFlags, deprecated, flagsToCamelObject } from '../../lib/flags.js';
8
9
  import Command from '@shopify/cli-kit/node/base-command';
9
- import Flags from '@oclif/core/lib/flags.js';
10
- import { startMiniOxygen } from '../../utils/mini-oxygen.js';
11
- import { checkHydrogenVersion } from '../../utils/check-version.js';
12
- import { addVirtualRoutes } from '../../utils/virtual-routes.js';
10
+ import { Flags } from '@oclif/core';
11
+ import { startMiniOxygen } from '../../lib/mini-oxygen.js';
12
+ import { checkHydrogenVersion } from '../../lib/check-version.js';
13
+ import { addVirtualRoutes } from '../../lib/virtual-routes.js';
13
14
 
14
15
  const LOG_INITIAL_BUILD = "\n\u{1F3C1} Initial build";
15
16
  const LOG_REBUILDING = "\u{1F9F1} Rebuilding...";
@@ -23,7 +24,8 @@ class Dev extends Command {
23
24
  description: "Disable rendering fallback routes when a route file doesn't exist",
24
25
  env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_VIRTUAL_ROUTES",
25
26
  default: false
26
- })
27
+ }),
28
+ host: deprecated("--host")()
27
29
  };
28
30
  async run() {
29
31
  const { flags } = await this.parse(Dev);
@@ -47,11 +49,11 @@ async function runDev({
47
49
  const config = await getRemixConfig(root);
48
50
  return disableVirtualRoutes ? config : addVirtualRoutes(config);
49
51
  };
50
- const getFilePaths = (file2) => {
51
- const fileRelative = path.relative(root, file2);
52
+ const getFilePaths = (file) => {
53
+ const fileRelative = path.relative(root, file);
52
54
  return [fileRelative, path.resolve(root, fileRelative)];
53
55
  };
54
- const serverBundleExists = () => file.exists(buildPathWorkerFile);
56
+ const serverBundleExists = () => fileExists(buildPathWorkerFile);
55
57
  let miniOxygenStarted = false;
56
58
  async function safeStartMiniOxygen() {
57
59
  if (miniOxygenStarted)
@@ -86,9 +88,9 @@ async function runDev({
86
88
  console.timeEnd(LOG_INITIAL_BUILD);
87
89
  await safeStartMiniOxygen();
88
90
  },
89
- async onFileCreated(file2) {
90
- const [relative, absolute] = getFilePaths(file2);
91
- output.info(`
91
+ async onFileCreated(file) {
92
+ const [relative, absolute] = getFilePaths(file);
93
+ outputInfo(`
92
94
  \u{1F4C4} File created: ${relative}`);
93
95
  if (absolute.startsWith(publicPath)) {
94
96
  await copyPublicFiles(
@@ -97,9 +99,9 @@ async function runDev({
97
99
  );
98
100
  }
99
101
  },
100
- async onFileChanged(file2) {
101
- const [relative, absolute] = getFilePaths(file2);
102
- output.info(`
102
+ async onFileChanged(file) {
103
+ const [relative, absolute] = getFilePaths(file);
104
+ outputInfo(`
103
105
  \u{1F4C4} File changed: ${relative}`);
104
106
  if (absolute.startsWith(publicPath)) {
105
107
  await copyPublicFiles(
@@ -108,16 +110,16 @@ async function runDev({
108
110
  );
109
111
  }
110
112
  },
111
- async onFileDeleted(file2) {
112
- const [relative, absolute] = getFilePaths(file2);
113
- output.info(`
113
+ async onFileDeleted(file) {
114
+ const [relative, absolute] = getFilePaths(file);
115
+ outputInfo(`
114
116
  \u{1F4C4} File deleted: ${relative}`);
115
117
  if (absolute.startsWith(publicPath)) {
116
118
  await fs.unlink(absolute.replace(publicPath, buildPathClient));
117
119
  }
118
120
  },
119
121
  onRebuildStart() {
120
- output.info(LOG_REBUILDING);
122
+ outputInfo(LOG_REBUILDING);
121
123
  console.time(LOG_REBUILT);
122
124
  },
123
125
  async onRebuildFinish() {
@@ -0,0 +1,10 @@
1
+ import Command from '@shopify/cli-kit/node/base-command';
2
+
3
+ declare class GenerateRouteShortcut extends Command {
4
+ static description: string;
5
+ static strict: boolean;
6
+ static hidden: boolean;
7
+ run(): Promise<void>;
8
+ }
9
+
10
+ export { GenerateRouteShortcut as default };
@@ -0,0 +1,17 @@
1
+ import Command from '@shopify/cli-kit/node/base-command';
2
+ import GenerateRoute from './generate/route.js';
3
+
4
+ class GenerateRouteShortcut extends Command {
5
+ static description = "Shortcut for `hydrogen generate`. See `hydrogen generate --help` for more information.";
6
+ static strict = false;
7
+ static hidden = true;
8
+ async run() {
9
+ const [command, ...args] = this.argv;
10
+ if (command === "r" || command === "route") {
11
+ return new GenerateRoute(args, this.config).run();
12
+ }
13
+ throw new Error(`Invalid command argument "${command}".`);
14
+ }
15
+ }
16
+
17
+ export { GenerateRouteShortcut as default };
@@ -1,5 +1,6 @@
1
1
  import * as _oclif_core_lib_interfaces_parser_js from '@oclif/core/lib/interfaces/parser.js';
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
+ import { RemixV2Flags } from '../../../lib/remix-version-interop.js';
3
4
 
4
5
  declare const GENERATOR_TEMPLATES_DIR = "generator-templates";
5
6
  interface Result {
@@ -8,27 +9,24 @@ interface Result {
8
9
  declare class GenerateRoute extends Command {
9
10
  static description: string;
10
11
  static flags: {
11
- adapter: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined>;
12
+ adapter: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
12
13
  typescript: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
13
14
  force: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
14
- path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined>;
15
+ path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
15
16
  };
16
17
  static hidden: true;
17
18
  static args: {
18
- name: string;
19
- description: string;
20
- required: boolean;
21
- options: string[];
22
- env: string;
23
- }[];
19
+ route: _oclif_core_lib_interfaces_parser_js.Arg<string, Record<string, unknown>>;
20
+ };
24
21
  run(): Promise<void>;
25
22
  }
26
- declare function runGenerate(route: string, { directory, typescript, force, adapter, templatesRoot, }: {
23
+ declare function runGenerate(routeFrom: string, routeTo: string, { directory, typescript, force, adapter, templatesRoot, v2Flags, }: {
27
24
  directory: string;
28
25
  typescript?: boolean;
29
26
  force?: boolean;
30
27
  adapter?: string;
31
28
  templatesRoot?: string;
29
+ v2Flags?: RemixV2Flags;
32
30
  }): Promise<Result>;
33
31
 
34
32
  export { GENERATOR_TEMPLATES_DIR, GenerateRoute as default, runGenerate };
@@ -1,11 +1,13 @@
1
1
  import { fileURLToPath } from 'url';
2
2
  import Command from '@shopify/cli-kit/node/base-command';
3
- import { path, file, ui } from '@shopify/cli-kit';
3
+ import { fileExists, readFile, mkdir, writeFile } from '@shopify/cli-kit/node/fs';
4
+ import { resolvePath, joinPath, relativePath, relativizePath, dirname } from '@shopify/cli-kit/node/path';
4
5
  import { AbortError } from '@shopify/cli-kit/node/error';
5
- import { renderSuccess } from '@shopify/cli-kit/node/ui';
6
- import { commonFlags } from '../../../utils/flags.js';
7
- import Flags from '@oclif/core/lib/flags.js';
8
- import { transpileFile, format, resolveFormatConfig } from '../../../utils/transpile-ts.js';
6
+ import { renderSuccess, renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
7
+ import { commonFlags } from '../../../lib/flags.js';
8
+ import { Flags, Args } from '@oclif/core';
9
+ import { transpileFile, format, resolveFormatConfig } from '../../../lib/transpile-ts.js';
10
+ import { getV2Flags, convertRouteToV2, convertTemplateToRemixVersion } from '../../../lib/remix-version-interop.js';
9
11
 
10
12
  const GENERATOR_TEMPLATES_DIR = "generator-templates";
11
13
  const ROUTE_MAP = {
@@ -35,37 +37,43 @@ class GenerateRoute extends Command {
35
37
  path: commonFlags.path
36
38
  };
37
39
  static hidden;
38
- static args = [
39
- {
40
+ static args = {
41
+ route: Args.string({
40
42
  name: "route",
41
43
  description: `The route to generate. One of ${ROUTES.join()}.`,
42
44
  required: true,
43
45
  options: ROUTES,
44
46
  env: "SHOPIFY_HYDROGEN_ARG_ROUTE"
45
- }
46
- ];
47
+ })
48
+ };
47
49
  async run() {
48
50
  const result = /* @__PURE__ */ new Map();
49
- const { flags, args } = await this.parse(GenerateRoute);
50
- const directory = flags.path ? path.resolve(flags.path) : process.cwd();
51
- const { route } = args;
51
+ const {
52
+ flags,
53
+ args: { route }
54
+ } = await this.parse(GenerateRoute);
52
55
  const routePath = route === "all" ? Object.values(ROUTE_MAP).flat() : ROUTE_MAP[route];
53
56
  if (!routePath) {
54
57
  throw new AbortError(
55
58
  `No route found for ${route}. Try one of ${ROUTES.join()}.`
56
59
  );
57
60
  }
58
- const isTypescript = flags.typescript || await file.exists(path.join(directory, "tsconfig.json"));
61
+ const directory = flags.path ? resolvePath(flags.path) : process.cwd();
62
+ const isTypescript = flags.typescript || await fileExists(joinPath(directory, "tsconfig.json"));
59
63
  const routesArray = Array.isArray(routePath) ? routePath : [routePath];
60
64
  try {
65
+ const { isV2RouteConvention, ...v2Flags } = await getV2Flags(directory);
61
66
  for (const item of routesArray) {
67
+ const routeFrom = item;
68
+ const routeTo = isV2RouteConvention ? convertRouteToV2(item) : item;
62
69
  result.set(
63
- item,
64
- await runGenerate(item, {
70
+ routeTo,
71
+ await runGenerate(routeFrom, routeTo, {
65
72
  directory,
66
73
  typescript: isTypescript,
67
74
  force: flags.force,
68
- adapter: flags.adapter
75
+ adapter: flags.adapter,
76
+ v2Flags
69
77
  })
70
78
  );
71
79
  }
@@ -81,62 +89,56 @@ class GenerateRoute extends Command {
81
89
  body: {
82
90
  list: {
83
91
  items: Array.from(result.entries()).map(
84
- ([path2, { operation }]) => `[${operation}] app/routes${path2}${extension}`
92
+ ([path, { operation }]) => `[${operation}] app/routes${path}${extension}`
85
93
  )
86
94
  }
87
95
  }
88
96
  });
89
97
  }
90
98
  }
91
- async function runGenerate(route, {
99
+ async function runGenerate(routeFrom, routeTo, {
92
100
  directory,
93
101
  typescript,
94
102
  force,
95
103
  adapter,
96
- templatesRoot = fileURLToPath(new URL("../../../", import.meta.url))
104
+ templatesRoot = fileURLToPath(new URL("../../../", import.meta.url)),
105
+ v2Flags = {}
97
106
  }) {
98
107
  let operation;
99
108
  const extension = typescript ? ".tsx" : ".jsx";
100
- const templatePath = path.join(
109
+ const templatePath = joinPath(
101
110
  templatesRoot,
102
111
  GENERATOR_TEMPLATES_DIR,
103
112
  "routes",
104
- `${route}.tsx`
113
+ `${routeFrom}.tsx`
105
114
  );
106
- const destinationPath = path.join(
115
+ const destinationPath = joinPath(
107
116
  directory,
108
117
  "app",
109
118
  "routes",
110
- `${route}${extension}`
119
+ `${routeTo}${extension}`
111
120
  );
112
- const relativeDestinationPath = path.relative(directory, destinationPath);
113
- if (!force && await file.exists(destinationPath)) {
114
- const options = [
115
- { name: "No", value: "skip" },
116
- { name: `Yes`, value: "overwrite" }
117
- ];
118
- const choice = await ui.prompt([
119
- {
120
- type: "select",
121
- name: "value",
122
- message: `The file ${path.relativize(
123
- relativeDestinationPath
124
- )} already exists. Do you want to overwrite it?`,
125
- choices: options
126
- }
127
- ]);
128
- operation = choice.value === "skip" ? "skipped" : "overwritten";
121
+ const relativeDestinationPath = relativePath(directory, destinationPath);
122
+ if (!force && await fileExists(destinationPath)) {
123
+ const shouldOverwrite = await renderConfirmationPrompt({
124
+ message: `The file ${relativizePath(
125
+ relativeDestinationPath
126
+ )} already exists. Do you want to overwrite it?`,
127
+ defaultValue: false
128
+ });
129
+ operation = shouldOverwrite ? "overwritten" : "skipped";
129
130
  if (operation === "skipped") {
130
131
  return { operation };
131
132
  }
132
133
  } else {
133
134
  operation = "generated";
134
135
  }
135
- let templateContent = await file.read(templatePath);
136
+ let templateContent = await readFile(templatePath);
137
+ templateContent = convertTemplateToRemixVersion(templateContent, v2Flags);
136
138
  if (!typescript) {
137
- const jsConfigPath = path.join(directory, "jsconfig.json");
138
- const config = await file.exists(jsConfigPath) ? JSON.parse(
139
- (await file.read(jsConfigPath, { encoding: "utf8" })).replace(
139
+ const jsConfigPath = joinPath(directory, "jsconfig.json");
140
+ const config = await fileExists(jsConfigPath) ? JSON.parse(
141
+ (await readFile(jsConfigPath, { encoding: "utf8" })).replace(
140
142
  /^\s*\/\/.*$/gm,
141
143
  ""
142
144
  )
@@ -154,10 +156,10 @@ async function runGenerate(route, {
154
156
  await resolveFormatConfig(destinationPath),
155
157
  destinationPath
156
158
  );
157
- if (!await file.exists(path.dirname(destinationPath))) {
158
- await file.mkdir(path.dirname(destinationPath));
159
+ if (!await fileExists(dirname(destinationPath))) {
160
+ await mkdir(dirname(destinationPath));
159
161
  }
160
- await file.write(destinationPath, templateContent);
162
+ await writeFile(destinationPath, templateContent);
161
163
  return {
162
164
  operation
163
165
  };