@shopify/cli-hydrogen 4.2.1 → 5.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 (60) hide show
  1. package/dist/commands/hydrogen/build.d.ts +1 -1
  2. package/dist/commands/hydrogen/build.js +24 -22
  3. package/dist/commands/hydrogen/check.js +1 -1
  4. package/dist/commands/hydrogen/codegen-unstable.d.ts +1 -0
  5. package/dist/commands/hydrogen/codegen-unstable.js +6 -0
  6. package/dist/commands/hydrogen/dev.d.ts +2 -1
  7. package/dist/commands/hydrogen/dev.js +85 -65
  8. package/dist/commands/hydrogen/env/list.d.ts +2 -3
  9. package/dist/commands/hydrogen/env/list.js +42 -44
  10. package/dist/commands/hydrogen/env/list.test.js +18 -24
  11. package/dist/commands/hydrogen/env/pull.d.ts +2 -3
  12. package/dist/commands/hydrogen/env/pull.js +42 -23
  13. package/dist/commands/hydrogen/env/pull.test.js +16 -4
  14. package/dist/commands/hydrogen/init.js +3 -13
  15. package/dist/commands/hydrogen/link.d.ts +0 -1
  16. package/dist/commands/hydrogen/link.js +34 -36
  17. package/dist/commands/hydrogen/link.test.js +43 -27
  18. package/dist/commands/hydrogen/list.d.ts +2 -2
  19. package/dist/commands/hydrogen/list.js +43 -39
  20. package/dist/commands/hydrogen/list.test.js +24 -32
  21. package/dist/commands/hydrogen/shortcut.js +6 -7
  22. package/dist/commands/hydrogen/shortcut.test.js +8 -9
  23. package/dist/commands/hydrogen/unlink.d.ts +0 -1
  24. package/dist/commands/hydrogen/unlink.js +5 -3
  25. package/dist/lib/admin-session.d.ts +1 -0
  26. package/dist/lib/codegen.d.ts +3 -2
  27. package/dist/lib/codegen.js +20 -7
  28. package/dist/lib/combined-environment-variables.js +19 -36
  29. package/dist/lib/combined-environment-variables.test.js +7 -7
  30. package/dist/lib/config.d.ts +3 -1
  31. package/dist/lib/config.js +67 -63
  32. package/dist/lib/flags.d.ts +1 -0
  33. package/dist/lib/flags.js +8 -3
  34. package/dist/lib/graphql/admin/link-storefront.d.ts +12 -9
  35. package/dist/lib/graphql/admin/link-storefront.js +18 -1
  36. package/dist/lib/graphql/admin/list-environments.d.ts +6 -5
  37. package/dist/lib/graphql/admin/list-environments.js +11 -1
  38. package/dist/lib/graphql/admin/list-storefronts.d.ts +13 -5
  39. package/dist/lib/graphql/admin/list-storefronts.js +18 -1
  40. package/dist/lib/graphql/admin/pull-variables.d.ts +6 -1
  41. package/dist/lib/graphql/admin/pull-variables.js +14 -1
  42. package/dist/lib/log.d.ts +2 -1
  43. package/dist/lib/log.js +8 -1
  44. package/dist/lib/mini-oxygen.js +1 -1
  45. package/dist/lib/process.d.ts +6 -0
  46. package/dist/lib/process.js +17 -0
  47. package/dist/lib/pull-environment-variables.d.ts +1 -0
  48. package/dist/lib/pull-environment-variables.js +4 -14
  49. package/dist/lib/remix-version-interop.js +1 -1
  50. package/dist/lib/shell.d.ts +5 -6
  51. package/dist/lib/shell.js +65 -17
  52. package/dist/lib/shell.test.d.ts +1 -0
  53. package/dist/lib/shell.test.js +85 -0
  54. package/dist/lib/shopify-config.d.ts +1 -1
  55. package/dist/lib/shopify-config.js +2 -2
  56. package/dist/lib/transpile-ts.js +6 -0
  57. package/oclif.manifest.json +1 -1
  58. package/package.json +9 -8
  59. package/dist/lib/colors.d.ts +0 -11
  60. package/dist/lib/colors.js +0 -11
@@ -6,7 +6,7 @@ declare class Build extends Command {
6
6
  static flags: {
7
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
- "disable-route-warning": _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
9
+ 'disable-route-warning': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
10
10
  base: _oclif_core_lib_interfaces_parser_js.OptionFlag<unknown, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
11
11
  entry: _oclif_core_lib_interfaces_parser_js.OptionFlag<unknown, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
12
12
  target: _oclif_core_lib_interfaces_parser_js.OptionFlag<unknown, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -1,26 +1,23 @@
1
1
  import path from 'path';
2
+ import { Flags } from '@oclif/core';
3
+ import Command from '@shopify/cli-kit/node/base-command';
2
4
  import { outputInfo, outputContent, outputToken, outputWarn } from '@shopify/cli-kit/node/output';
3
5
  import { rmdir, fileSize, copyFile } from '@shopify/cli-kit/node/fs';
6
+ import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
7
+ import colors from '@shopify/cli-kit/node/colors';
4
8
  import { getProjectPaths, getRemixConfig } from '../../lib/config.js';
5
9
  import { commonFlags, deprecated, flagsToCamelObject } from '../../lib/flags.js';
6
- import Command from '@shopify/cli-kit/node/base-command';
7
- import { Flags } from '@oclif/core';
8
10
  import { checkLockfileStatus } from '../../lib/check-lockfile.js';
9
11
  import { findMissingRoutes } from '../../lib/missing-routes.js';
10
- import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
11
- import { colors } from '../../lib/colors.js';
12
+ import { warnOnce } from '../../lib/log.js';
12
13
 
13
14
  const LOG_WORKER_BUILT = "\u{1F4E6} Worker built";
14
15
  class Build extends Command {
15
16
  static description = "Builds a Hydrogen storefront for production.";
16
17
  static flags = {
17
18
  path: commonFlags.path,
18
- sourcemap: Flags.boolean({
19
- description: "Generate sourcemaps for the build.",
20
- env: "SHOPIFY_HYDROGEN_FLAG_SOURCEMAP",
21
- default: true
22
- }),
23
- ["disable-route-warning"]: Flags.boolean({
19
+ sourcemap: commonFlags.sourcemap,
20
+ "disable-route-warning": Flags.boolean({
24
21
  description: "Disable warning about missing standard routes.",
25
22
  env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_ROUTE_WARNING"
26
23
  }),
@@ -45,23 +42,28 @@ async function runBuild({
45
42
  const { root, buildPath, buildPathClient, buildPathWorkerFile, publicPath } = getProjectPaths(appPath);
46
43
  await checkLockfileStatus(root);
47
44
  console.time(LOG_WORKER_BUILT);
48
- const [remixConfig] = await Promise.all([
45
+ outputInfo(`
46
+ \u{1F3D7}\uFE0F Building in ${process.env.NODE_ENV} mode...`);
47
+ const [remixConfig, { build }, { logThrown }, { createFileWatchCache }] = await Promise.all([
49
48
  getRemixConfig(root),
49
+ import('@remix-run/dev/dist/compiler/build.js'),
50
+ import('@remix-run/dev/dist/compiler/utils/log.js'),
51
+ import('@remix-run/dev/dist/compiler/fileWatchCache.js'),
50
52
  rmdir(buildPath, { force: true })
51
53
  ]);
52
- outputInfo(`
53
- \u{1F3D7}\uFE0F Building in ${process.env.NODE_ENV} mode...`);
54
- const { build } = await import('@remix-run/dev/dist/compiler/build.js');
55
- const { logCompileFailure } = await import('@remix-run/dev/dist/compiler/onCompileFailure.js');
56
54
  await Promise.all([
57
55
  copyPublicFiles(publicPath, buildPathClient),
58
- build(remixConfig, {
59
- mode: process.env.NODE_ENV,
60
- sourcemap,
61
- onCompileFailure: (failure) => {
62
- logCompileFailure(failure);
63
- throw Error();
64
- }
56
+ build({
57
+ config: remixConfig,
58
+ options: {
59
+ mode: process.env.NODE_ENV,
60
+ onWarning: warnOnce,
61
+ sourcemap
62
+ },
63
+ fileWatchCache: createFileWatchCache()
64
+ }).catch((thrown) => {
65
+ logThrown(thrown);
66
+ process.exit(1);
65
67
  })
66
68
  ]);
67
69
  if (process.env.NODE_ENV !== "development") {
@@ -29,7 +29,7 @@ class GenerateRoute extends Command {
29
29
  }
30
30
  }
31
31
  async function runCheckRoutes({ directory }) {
32
- const remixConfig = await getRemixConfig(directory);
32
+ const remixConfig = await getRemixConfig(directory, true);
33
33
  logMissingRoutes(findMissingRoutes(remixConfig));
34
34
  }
35
35
 
@@ -6,6 +6,7 @@ declare class Codegen extends Command {
6
6
  static flags: {
7
7
  path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
8
8
  "codegen-config-path": _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
9
+ "force-sfapi-version": _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
9
10
  watch: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
10
11
  };
11
12
  run(): Promise<void>;
@@ -15,6 +15,10 @@ class Codegen extends Command {
15
15
  description: "Specify a path to a codegen configuration file. Defaults to `<root>/codegen.ts` if it exists.",
16
16
  required: false
17
17
  }),
18
+ ["force-sfapi-version"]: Flags.string({
19
+ description: "Force generating Storefront API types for a specific version instead of using the one provided in Hydrogen. A token can also be provided with this format: `<version>:<token>`.",
20
+ hidden: true
21
+ }),
18
22
  watch: Flags.boolean({
19
23
  description: "Watch the project for changes to update types on file save.",
20
24
  required: false,
@@ -33,6 +37,7 @@ class Codegen extends Command {
33
37
  async function runCodegen({
34
38
  path: appPath,
35
39
  codegenConfigPath,
40
+ forceSfapiVersion,
36
41
  watch
37
42
  }) {
38
43
  const { root } = getProjectPaths(appPath);
@@ -42,6 +47,7 @@ async function runCodegen({
42
47
  const generatedFiles = await generateTypes({
43
48
  ...remixConfig,
44
49
  configFilePath: codegenConfigPath,
50
+ forceSfapiVersion,
45
51
  watch
46
52
  });
47
53
  if (!watch) {
@@ -8,7 +8,8 @@ declare class Dev extends Command {
8
8
  port: _oclif_core_lib_interfaces_parser_js.OptionFlag<number, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
9
9
  "codegen-unstable": _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
10
10
  "codegen-config-path": _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
11
- "disable-virtual-routes": _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
11
+ sourcemap: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
12
+ 'disable-virtual-routes': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
12
13
  shop: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
13
14
  debug: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
14
15
  host: _oclif_core_lib_interfaces_parser_js.OptionFlag<unknown, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -2,16 +2,16 @@ import path from 'path';
2
2
  import fs from 'fs/promises';
3
3
  import { outputInfo } from '@shopify/cli-kit/node/output';
4
4
  import { fileExists } from '@shopify/cli-kit/node/fs';
5
+ import { renderFatalError } from '@shopify/cli-kit/node/ui';
5
6
  import { copyPublicFiles } from './build.js';
6
7
  import { getProjectPaths, getRemixConfig } from '../../lib/config.js';
7
- import { muteDevLogs } from '../../lib/log.js';
8
+ import { muteDevLogs, warnOnce } from '../../lib/log.js';
8
9
  import { commonFlags, deprecated, flagsToCamelObject } from '../../lib/flags.js';
9
10
  import Command from '@shopify/cli-kit/node/base-command';
10
11
  import { Flags } from '@oclif/core';
11
12
  import { startMiniOxygen } from '../../lib/mini-oxygen.js';
12
13
  import { checkHydrogenVersion } from '../../lib/check-version.js';
13
14
  import { addVirtualRoutes } from '../../lib/virtual-routes.js';
14
- import { spawnCodegenProcess } from '../../lib/codegen.js';
15
15
  import { combinedEnvironmentVariables } from '../../lib/combined-environment-variables.js';
16
16
  import { getConfig } from '../../lib/shopify-config.js';
17
17
 
@@ -33,7 +33,8 @@ class Dev extends Command {
33
33
  required: false,
34
34
  dependsOn: ["codegen-unstable"]
35
35
  }),
36
- ["disable-virtual-routes"]: Flags.boolean({
36
+ sourcemap: commonFlags.sourcemap,
37
+ "disable-virtual-routes": Flags.boolean({
37
38
  description: "Disable rendering fallback routes when a route file doesn't exist.",
38
39
  env: "SHOPIFY_HYDROGEN_FLAG_DISABLE_VIRTUAL_ROUTES",
39
40
  default: false
@@ -65,7 +66,8 @@ async function runDev({
65
66
  disableVirtualRoutes,
66
67
  shop,
67
68
  envBranch,
68
- debug = false
69
+ debug = false,
70
+ sourcemap = true
69
71
  }) {
70
72
  if (!process.env.NODE_ENV)
71
73
  process.env.NODE_ENV = "development";
@@ -91,9 +93,9 @@ async function runDev({
91
93
  shop,
92
94
  envBranch
93
95
  }) : void 0;
94
- let miniOxygenStarted = false;
96
+ let isMiniOxygenStarted = false;
95
97
  async function safeStartMiniOxygen() {
96
- if (miniOxygenStarted)
98
+ if (isMiniOxygenStarted)
97
99
  return;
98
100
  await startMiniOxygen({
99
101
  root,
@@ -103,75 +105,93 @@ async function runDev({
103
105
  buildPathClient,
104
106
  environmentVariables
105
107
  });
106
- miniOxygenStarted = true;
108
+ isMiniOxygenStarted = true;
107
109
  const showUpgrade = await checkingHydrogenVersion;
108
110
  if (showUpgrade)
109
111
  showUpgrade();
110
112
  }
111
- const { watch } = await import('@remix-run/dev/dist/compiler/watch.js');
112
- const remixConfig = await reloadConfig();
113
- if (codegen) {
114
- spawnCodegenProcess({ ...remixConfig, configFilePath: codegenConfigPath });
115
- }
116
- await watch(remixConfig, {
117
- reloadConfig,
118
- mode: process.env.NODE_ENV,
119
- async onInitialBuild() {
120
- await copyingFiles;
121
- if (!await serverBundleExists()) {
122
- const { renderFatalError } = await import('@shopify/cli-kit/node/ui');
123
- return renderFatalError({
124
- name: "BuildError",
125
- type: 0,
126
- message: "MiniOxygen cannot start because the server bundle has not been generated.",
127
- tryMessage: "This is likely due to an error in your app and Remix is unable to compile. Try fixing the app and MiniOxygen will start."
128
- });
129
- }
130
- console.timeEnd(LOG_INITIAL_BUILD);
131
- await safeStartMiniOxygen();
113
+ let isInitialBuild = true;
114
+ const [{ watch }, { createFileWatchCache }] = await Promise.all([
115
+ import('@remix-run/dev/dist/compiler/watch.js'),
116
+ import('@remix-run/dev/dist/compiler/fileWatchCache.js')
117
+ ]);
118
+ const fileWatchCache = createFileWatchCache();
119
+ await watch(
120
+ {
121
+ config: await reloadConfig(),
122
+ options: {
123
+ mode: process.env.NODE_ENV,
124
+ onWarning: warnOnce,
125
+ sourcemap
126
+ },
127
+ fileWatchCache
132
128
  },
133
- async onFileCreated(file) {
134
- const [relative, absolute] = getFilePaths(file);
135
- outputInfo(`
129
+ {
130
+ reloadConfig,
131
+ onBuildStart() {
132
+ if (isInitialBuild) {
133
+ console.time(LOG_INITIAL_BUILD);
134
+ } else {
135
+ console.time(LOG_REBUILT);
136
+ outputInfo(LOG_REBUILDING);
137
+ }
138
+ },
139
+ async onBuildFinish() {
140
+ if (isInitialBuild) {
141
+ await copyingFiles;
142
+ console.timeEnd(LOG_INITIAL_BUILD);
143
+ isInitialBuild = false;
144
+ } else {
145
+ console.timeEnd(LOG_REBUILT);
146
+ if (!isMiniOxygenStarted)
147
+ console.log("");
148
+ }
149
+ if (!isMiniOxygenStarted) {
150
+ if (!await serverBundleExists()) {
151
+ return renderFatalError({
152
+ name: "BuildError",
153
+ type: 0,
154
+ message: "MiniOxygen cannot start because the server bundle has not been generated.",
155
+ tryMessage: "This is likely due to an error in your app and Remix is unable to compile. Try fixing the app and MiniOxygen will start."
156
+ });
157
+ }
158
+ await safeStartMiniOxygen();
159
+ }
160
+ },
161
+ async onFileCreated(file) {
162
+ const [relative, absolute] = getFilePaths(file);
163
+ outputInfo(`
136
164
  \u{1F4C4} File created: ${relative}`);
137
- if (absolute.startsWith(publicPath)) {
138
- await copyPublicFiles(
139
- absolute,
140
- absolute.replace(publicPath, buildPathClient)
141
- );
142
- }
143
- },
144
- async onFileChanged(file) {
145
- const [relative, absolute] = getFilePaths(file);
146
- outputInfo(`
165
+ if (absolute.startsWith(publicPath)) {
166
+ await copyPublicFiles(
167
+ absolute,
168
+ absolute.replace(publicPath, buildPathClient)
169
+ );
170
+ }
171
+ },
172
+ async onFileChanged(file) {
173
+ fileWatchCache.invalidateFile(file);
174
+ const [relative, absolute] = getFilePaths(file);
175
+ outputInfo(`
147
176
  \u{1F4C4} File changed: ${relative}`);
148
- if (absolute.startsWith(publicPath)) {
149
- await copyPublicFiles(
150
- absolute,
151
- absolute.replace(publicPath, buildPathClient)
152
- );
153
- }
154
- },
155
- async onFileDeleted(file) {
156
- const [relative, absolute] = getFilePaths(file);
157
- outputInfo(`
177
+ if (absolute.startsWith(publicPath)) {
178
+ await copyPublicFiles(
179
+ absolute,
180
+ absolute.replace(publicPath, buildPathClient)
181
+ );
182
+ }
183
+ },
184
+ async onFileDeleted(file) {
185
+ fileWatchCache.invalidateFile(file);
186
+ const [relative, absolute] = getFilePaths(file);
187
+ outputInfo(`
158
188
  \u{1F4C4} File deleted: ${relative}`);
159
- if (absolute.startsWith(publicPath)) {
160
- await fs.unlink(absolute.replace(publicPath, buildPathClient));
161
- }
162
- },
163
- onRebuildStart() {
164
- outputInfo(LOG_REBUILDING);
165
- console.time(LOG_REBUILT);
166
- },
167
- async onRebuildFinish() {
168
- console.timeEnd(LOG_REBUILT);
169
- if (!miniOxygenStarted && await serverBundleExists()) {
170
- console.log("");
171
- await safeStartMiniOxygen();
189
+ if (absolute.startsWith(publicPath)) {
190
+ await fs.unlink(absolute.replace(publicPath, buildPathClient));
191
+ }
172
192
  }
173
193
  }
174
- });
194
+ );
175
195
  }
176
196
 
177
197
  export { Dev as default };
@@ -1,9 +1,8 @@
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
3
 
4
- declare class List extends Command {
4
+ declare class EnvList extends Command {
5
5
  static description: string;
6
- static hidden: boolean;
7
6
  static flags: {
8
7
  path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
9
8
  shop: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -16,4 +15,4 @@ interface Flags {
16
15
  }
17
16
  declare function listEnvironments({ path, shop: flagShop }: Flags): Promise<void>;
18
17
 
19
- export { List as default, listEnvironments };
18
+ export { EnvList as default, listEnvironments };
@@ -1,24 +1,24 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
- import { renderConfirmationPrompt, renderTable } from '@shopify/cli-kit/node/ui';
3
- import { outputContent, outputToken, outputNewline } from '@shopify/cli-kit/node/output';
2
+ import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
3
+ import { pluralize } from '@shopify/cli-kit/common/string';
4
+ import colors from '@shopify/cli-kit/node/colors';
5
+ import { outputContent, outputToken, outputNewline, outputInfo } from '@shopify/cli-kit/node/output';
4
6
  import { linkStorefront } from '../link.js';
5
- import { adminRequest } from '../../../lib/graphql.js';
6
7
  import { commonFlags } from '../../../lib/flags.js';
7
8
  import { getHydrogenShop } from '../../../lib/shop.js';
8
9
  import { getAdminSession } from '../../../lib/admin-session.js';
9
- import { ListEnvironmentsQuery } from '../../../lib/graphql/admin/list-environments.js';
10
+ import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-environments.js';
10
11
  import { getConfig } from '../../../lib/shopify-config.js';
11
12
  import { renderMissingLink, renderMissingStorefront } from '../../../lib/render-errors.js';
12
13
 
13
- class List extends Command {
14
- static description = "List the environments on your Hydrogen storefront.";
15
- static hidden = true;
14
+ class EnvList extends Command {
15
+ static description = "List the environments on your linked Hydrogen storefront.";
16
16
  static flags = {
17
17
  path: commonFlags.path,
18
18
  shop: commonFlags.shop
19
19
  };
20
20
  async run() {
21
- const { flags } = await this.parse(List);
21
+ const { flags } = await this.parse(EnvList);
22
22
  await listEnvironments(flags);
23
23
  }
24
24
  }
@@ -43,54 +43,52 @@ async function listEnvironments({ path, shop: flagShop }) {
43
43
  if (!configStorefront) {
44
44
  return;
45
45
  }
46
- const result = await adminRequest(
47
- ListEnvironmentsQuery,
46
+ const { storefront } = await getStorefrontEnvironments(
48
47
  adminSession,
49
- {
50
- id: configStorefront.id
51
- }
48
+ configStorefront.id
52
49
  );
53
- const hydrogenStorefront = result.hydrogenStorefront;
54
- if (!hydrogenStorefront) {
50
+ if (!storefront) {
55
51
  renderMissingStorefront({ adminSession, storefront: configStorefront });
56
52
  return;
57
53
  }
58
- const previewEnvironmentIndex = hydrogenStorefront.environments.findIndex(
54
+ const previewEnvironmentIndex = storefront.environments.findIndex(
59
55
  (env) => env.type === "PREVIEW"
60
56
  );
61
- const previewEnvironment = hydrogenStorefront.environments.splice(
57
+ const previewEnvironment = storefront.environments.splice(
62
58
  previewEnvironmentIndex,
63
59
  1
64
60
  );
65
- hydrogenStorefront.environments.push(previewEnvironment[0]);
66
- const rows = hydrogenStorefront.environments.map(
67
- ({ branch, name, url, type }) => {
68
- const environmentUrl = type === "PRODUCTION" ? hydrogenStorefront.productionUrl : url;
69
- return {
70
- name,
71
- branch: branch ? branch : "-",
72
- url: environmentUrl ? environmentUrl : "-"
73
- };
74
- }
75
- );
61
+ storefront.environments.push(previewEnvironment[0]);
76
62
  outputNewline();
77
- renderTable({
78
- rows,
79
- columns: {
80
- name: {
81
- header: "Name",
82
- color: "whiteBright"
83
- },
84
- branch: {
85
- header: "Branch",
86
- color: "yellow"
87
- },
88
- url: {
89
- header: "URL",
90
- color: "green"
91
- }
63
+ outputInfo(
64
+ pluralizedEnvironments({
65
+ environments: storefront.environments,
66
+ storefrontTitle: configStorefront.title
67
+ }).toString()
68
+ );
69
+ storefront.environments.forEach(({ name, branch, type, url }) => {
70
+ outputNewline();
71
+ const environmentUrl = type === "PRODUCTION" ? storefront.productionUrl : url;
72
+ outputInfo(
73
+ outputContent`${colors.whiteBright(name)}${branch ? ` ${colors.dim(`(Branch: ${branch})`)}` : ""}`.value
74
+ );
75
+ if (environmentUrl) {
76
+ outputInfo(
77
+ outputContent` ${colors.whiteBright(environmentUrl)}`.value
78
+ );
92
79
  }
93
80
  });
94
81
  }
82
+ const pluralizedEnvironments = ({
83
+ environments,
84
+ storefrontTitle
85
+ }) => {
86
+ return pluralize(
87
+ environments,
88
+ (environments2) => `Showing ${environments2.length} environments for the Hydrogen storefront ${storefrontTitle}`,
89
+ (_environment) => `Showing 1 environment for the Hydrogen storefront ${storefrontTitle}`,
90
+ () => `There are no environments for the Hydrogen storefront ${storefrontTitle}`
91
+ );
92
+ };
95
93
 
96
- export { List as default, listEnvironments };
94
+ export { EnvList as default, listEnvironments };
@@ -2,14 +2,14 @@ import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
2
2
  import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
3
3
  import { inTemporaryDirectory } from '@shopify/cli-kit/node/fs';
4
4
  import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
5
- import { ListEnvironmentsQuery } from '../../../lib/graphql/admin/list-environments.js';
5
+ import { getStorefrontEnvironments } from '../../../lib/graphql/admin/list-environments.js';
6
6
  import { getAdminSession } from '../../../lib/admin-session.js';
7
- import { adminRequest } from '../../../lib/graphql.js';
8
7
  import { getConfig } from '../../../lib/shopify-config.js';
9
8
  import { renderMissingLink, renderMissingStorefront } from '../../../lib/render-errors.js';
10
9
  import { linkStorefront } from '../link.js';
11
10
  import { listEnvironments } from './list.js';
12
11
 
12
+ const SHOP = "my-shop";
13
13
  vi.mock("@shopify/cli-kit/node/ui", async () => {
14
14
  const original = await vi.importActual("@shopify/cli-kit/node/ui");
15
15
  return {
@@ -21,20 +21,16 @@ vi.mock("../link.js");
21
21
  vi.mock("../../../lib/admin-session.js");
22
22
  vi.mock("../../../lib/shopify-config.js");
23
23
  vi.mock("../../../lib/render-errors.js");
24
- vi.mock("../../../lib/graphql.js", async () => {
25
- const original = await vi.importActual("../../../lib/graphql.js");
26
- return {
27
- ...original,
28
- adminRequest: vi.fn()
29
- };
24
+ vi.mock("../../../lib/graphql/admin/list-environments.js", () => {
25
+ return { getStorefrontEnvironments: vi.fn() };
30
26
  });
31
27
  vi.mock("../../../lib/shop.js", () => ({
32
- getHydrogenShop: () => "my-shop"
28
+ getHydrogenShop: () => SHOP
33
29
  }));
34
30
  describe("listEnvironments", () => {
35
31
  const ADMIN_SESSION = {
36
32
  token: "abc123",
37
- storeFqdn: "my-shop"
33
+ storeFqdn: SHOP
38
34
  };
39
35
  const PRODUCTION_ENVIRONMENT = {
40
36
  id: "gid://shopify/HydrogenStorefrontEnvironment/1",
@@ -68,8 +64,8 @@ describe("listEnvironments", () => {
68
64
  title: "Existing Link"
69
65
  }
70
66
  });
71
- vi.mocked(adminRequest).mockResolvedValue({
72
- hydrogenStorefront: {
67
+ vi.mocked(getStorefrontEnvironments).mockResolvedValue({
68
+ storefront: {
73
69
  id: "gid://shopify/HydrogenStorefront/1",
74
70
  productionUrl: "https://example.com",
75
71
  environments: [
@@ -87,12 +83,9 @@ describe("listEnvironments", () => {
87
83
  it("makes a GraphQL call to fetch environment variables", async () => {
88
84
  await inTemporaryDirectory(async (tmpDir) => {
89
85
  await listEnvironments({ path: tmpDir });
90
- expect(adminRequest).toHaveBeenCalledWith(
91
- ListEnvironmentsQuery,
86
+ expect(getStorefrontEnvironments).toHaveBeenCalledWith(
92
87
  ADMIN_SESSION,
93
- {
94
- id: "gid://shopify/HydrogenStorefront/1"
95
- }
88
+ "gid://shopify/HydrogenStorefront/1"
96
89
  );
97
90
  });
98
91
  });
@@ -101,12 +94,13 @@ describe("listEnvironments", () => {
101
94
  const output = mockAndCaptureOutput();
102
95
  await listEnvironments({ path: tmpDir });
103
96
  expect(output.info()).toMatch(
104
- /Production\s*main\s*https:\/\/example\.com/
105
- );
106
- expect(output.info()).toMatch(
107
- /Staging\s*staging\s*https:\/\/oxygen-456\.example\.com/
97
+ /Showing 3 environments for the Hydrogen storefront Existing Link/
108
98
  );
109
- expect(output.info()).toMatch(/Preview\s*-\s*-/);
99
+ expect(output.info()).toMatch(/Production \(Branch: main\)/);
100
+ expect(output.info()).toMatch(/https:\/\/example\.com/);
101
+ expect(output.info()).toMatch(/Staging \(Branch: staging\)/);
102
+ expect(output.info()).toMatch(/https:\/\/oxygen-456\.example\.com/);
103
+ expect(output.info()).toMatch(/Preview/);
110
104
  });
111
105
  });
112
106
  describe("when there is no linked storefront", () => {
@@ -137,8 +131,8 @@ describe("listEnvironments", () => {
137
131
  });
138
132
  describe("when there is no matching storefront in the shop", () => {
139
133
  beforeEach(() => {
140
- vi.mocked(adminRequest).mockResolvedValue({
141
- hydrogenStorefront: null
134
+ vi.mocked(getStorefrontEnvironments).mockResolvedValue({
135
+ storefront: null
142
136
  });
143
137
  });
144
138
  it("calls renderMissingStorefront", async () => {
@@ -1,9 +1,8 @@
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
3
 
4
- declare class Pull extends Command {
4
+ declare class EnvPull extends Command {
5
5
  static description: string;
6
- static hidden: boolean;
7
6
  static flags: {
8
7
  "env-branch": _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
9
8
  path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -20,4 +19,4 @@ interface Flags {
20
19
  }
21
20
  declare function pullVariables({ envBranch, force, path, shop: flagShop, }: Flags): Promise<void>;
22
21
 
23
- export { Pull as default, pullVariables };
22
+ export { EnvPull as default, pullVariables };