@shopify/cli-hydrogen 4.2.1 → 5.0.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 (54) hide show
  1. package/dist/commands/hydrogen/build.js +6 -5
  2. package/dist/commands/hydrogen/check.js +1 -1
  3. package/dist/commands/hydrogen/codegen-unstable.d.ts +1 -0
  4. package/dist/commands/hydrogen/codegen-unstable.js +6 -0
  5. package/dist/commands/hydrogen/env/list.d.ts +2 -3
  6. package/dist/commands/hydrogen/env/list.js +42 -44
  7. package/dist/commands/hydrogen/env/list.test.js +18 -24
  8. package/dist/commands/hydrogen/env/pull.d.ts +2 -3
  9. package/dist/commands/hydrogen/env/pull.js +42 -23
  10. package/dist/commands/hydrogen/env/pull.test.js +16 -4
  11. package/dist/commands/hydrogen/init.js +3 -13
  12. package/dist/commands/hydrogen/link.d.ts +0 -1
  13. package/dist/commands/hydrogen/link.js +34 -36
  14. package/dist/commands/hydrogen/link.test.js +43 -27
  15. package/dist/commands/hydrogen/list.d.ts +2 -2
  16. package/dist/commands/hydrogen/list.js +43 -39
  17. package/dist/commands/hydrogen/list.test.js +24 -32
  18. package/dist/commands/hydrogen/shortcut.js +6 -7
  19. package/dist/commands/hydrogen/shortcut.test.js +8 -9
  20. package/dist/commands/hydrogen/unlink.d.ts +0 -1
  21. package/dist/commands/hydrogen/unlink.js +5 -3
  22. package/dist/lib/admin-session.d.ts +1 -0
  23. package/dist/lib/codegen.d.ts +3 -2
  24. package/dist/lib/codegen.js +20 -7
  25. package/dist/lib/combined-environment-variables.js +19 -36
  26. package/dist/lib/combined-environment-variables.test.js +7 -7
  27. package/dist/lib/config.d.ts +1 -1
  28. package/dist/lib/config.js +67 -63
  29. package/dist/lib/flags.js +2 -3
  30. package/dist/lib/graphql/admin/link-storefront.d.ts +12 -9
  31. package/dist/lib/graphql/admin/link-storefront.js +18 -1
  32. package/dist/lib/graphql/admin/list-environments.d.ts +6 -5
  33. package/dist/lib/graphql/admin/list-environments.js +11 -1
  34. package/dist/lib/graphql/admin/list-storefronts.d.ts +13 -5
  35. package/dist/lib/graphql/admin/list-storefronts.js +18 -1
  36. package/dist/lib/graphql/admin/pull-variables.d.ts +6 -1
  37. package/dist/lib/graphql/admin/pull-variables.js +14 -1
  38. package/dist/lib/mini-oxygen.js +1 -1
  39. package/dist/lib/process.d.ts +6 -0
  40. package/dist/lib/process.js +17 -0
  41. package/dist/lib/pull-environment-variables.d.ts +1 -0
  42. package/dist/lib/pull-environment-variables.js +4 -14
  43. package/dist/lib/remix-version-interop.js +1 -1
  44. package/dist/lib/shell.d.ts +5 -6
  45. package/dist/lib/shell.js +65 -17
  46. package/dist/lib/shell.test.d.ts +1 -0
  47. package/dist/lib/shell.test.js +85 -0
  48. package/dist/lib/shopify-config.d.ts +1 -1
  49. package/dist/lib/shopify-config.js +2 -2
  50. package/dist/lib/transpile-ts.js +6 -0
  51. package/oclif.manifest.json +1 -1
  52. package/package.json +7 -6
  53. package/dist/lib/colors.d.ts +0 -11
  54. package/dist/lib/colors.js +0 -11
@@ -49,7 +49,7 @@ describe("combinedEnvironmentVariables()", () => {
49
49
  const outputMock = mockAndCaptureOutput();
50
50
  await combinedEnvironmentVariables({ root: tmpDir, shop: "my-shop" });
51
51
  expect(outputMock.info()).toMatch(
52
- /Injecting environment variables into MiniOxygen/
52
+ /Environment variables injected into MiniOxygen:/
53
53
  );
54
54
  });
55
55
  });
@@ -57,7 +57,7 @@ describe("combinedEnvironmentVariables()", () => {
57
57
  await inTemporaryDirectory(async (tmpDir) => {
58
58
  const outputMock = mockAndCaptureOutput();
59
59
  await combinedEnvironmentVariables({ root: tmpDir, shop: "my-shop" });
60
- expect(outputMock.info()).toMatch(/Using PUBLIC_API_TOKEN from Hydrogen/);
60
+ expect(outputMock.info()).toMatch(/PUBLIC_API_TOKEN\s+from Oxygen/);
61
61
  });
62
62
  });
63
63
  describe("when one of the variables is a secret", () => {
@@ -76,7 +76,7 @@ describe("combinedEnvironmentVariables()", () => {
76
76
  const outputMock = mockAndCaptureOutput();
77
77
  await combinedEnvironmentVariables({ root: tmpDir, shop: "my-shop" });
78
78
  expect(outputMock.info()).toMatch(
79
- /Ignoring PUBLIC_API_TOKEN \(value is marked as secret\)/
79
+ /PUBLIC_API_TOKEN\s+from Oxygen \(Marked as secret\)/
80
80
  );
81
81
  });
82
82
  });
@@ -88,7 +88,7 @@ describe("combinedEnvironmentVariables()", () => {
88
88
  await writeFile(filePath, "LOCAL_TOKEN=1");
89
89
  const outputMock = mockAndCaptureOutput();
90
90
  await combinedEnvironmentVariables({ root: tmpDir });
91
- expect(outputMock.info()).toMatch(/Using LOCAL_TOKEN from \.env/);
91
+ expect(outputMock.info()).toMatch(/LOCAL_TOKEN\s+from local \.env/);
92
92
  });
93
93
  });
94
94
  describe("and they overwrite remote variables", () => {
@@ -98,11 +98,11 @@ describe("combinedEnvironmentVariables()", () => {
98
98
  await writeFile(filePath, "PUBLIC_API_TOKEN=abc");
99
99
  const outputMock = mockAndCaptureOutput();
100
100
  await combinedEnvironmentVariables({ root: tmpDir, shop: "my-shop" });
101
- expect(outputMock.info()).toMatch(
102
- /Ignoring PUBLIC_API_TOKEN \(overwritten via \.env\)/
101
+ expect(outputMock.info()).not.toMatch(
102
+ /PUBLIC_API_TOKEN\s+from Oxygen/
103
103
  );
104
104
  expect(outputMock.info()).toMatch(
105
- /Using PUBLIC_API_TOKEN from \.env/
105
+ /PUBLIC_API_TOKEN\s+from local \.env/
106
106
  );
107
107
  });
108
108
  });
@@ -8,7 +8,7 @@ declare function getProjectPaths(appPath?: string, entry?: string): {
8
8
  buildPathWorkerFile: string;
9
9
  publicPath: string;
10
10
  };
11
- declare function getRemixConfig(root: string, mode?: ServerMode): Promise<RemixConfig & {
11
+ declare function getRemixConfig(root: string, skipOxygenChecks?: boolean, mode?: ServerMode): Promise<RemixConfig & {
12
12
  serverConditions?: string[] | undefined;
13
13
  serverMainFields?: string[] | undefined;
14
14
  serverDependenciesToBundle?: string | undefined;
@@ -23,72 +23,76 @@ function getProjectPaths(appPath, entry) {
23
23
  publicPath
24
24
  };
25
25
  }
26
- async function getRemixConfig(root, mode = process.env.NODE_ENV) {
26
+ async function getRemixConfig(root, skipOxygenChecks = false, mode = process.env.NODE_ENV) {
27
27
  const { readConfig } = await import('@remix-run/dev/dist/config.js');
28
28
  const config = await readConfig(root, mode);
29
- if (!config.serverEntryPoint) {
30
- throwConfigError(
31
- "Could not find a server entry point.",
32
- "Please add a server option to your remix.config.js pointing to an Oxygen worker entry file."
33
- );
34
- } else {
35
- assertEntryFileExists(config.rootDirectory, config.serverEntryPoint);
36
- }
37
- if (config.serverPlatform !== "neutral") {
38
- throwConfigError(
39
- 'The serverPlatform in remix.config.js must be "neutral".'
40
- );
41
- }
42
- if (config.serverModuleFormat !== "esm") {
43
- throwConfigError(
44
- 'The serverModuleFormat in remix.config.js must be "esm".'
45
- );
46
- }
47
- if (config.serverDependenciesToBundle !== "all") {
48
- throwConfigError(
49
- 'The serverDependenciesToBundle in remix.config.js must be "all".'
50
- );
51
- }
52
- if (!config.serverConditions?.includes("worker")) {
53
- throwConfigError(
54
- 'The serverConditions in remix.config.js must include "worker".'
55
- );
56
- }
57
- if (process.env.NODE_ENV === "development" && !config.serverConditions?.includes("development")) {
58
- outputWarn(
59
- "Add `process.env.NODE_ENV` value to serverConditions in remix.config.js to enable debugging features in development."
60
- );
61
- }
62
- if (!config.serverMainFields || !oxygenServerMainFields.every((v, i) => config.serverMainFields?.[i] === v)) {
63
- throwConfigError(
64
- `The serverMainFields in remix.config.js must be ${JSON.stringify(
65
- oxygenServerMainFields
66
- )}.`
67
- );
68
- }
69
- const cdnUrl = process.env.HYDROGEN_ASSET_BASE_URL;
70
- if (cdnUrl && !config.publicPath.startsWith(cdnUrl)) {
71
- throwConfigError(
72
- "The publicPath in remix.config.js must be prepended with the value of `process.env.HYDROGEN_ASSET_BASE_URL`."
73
- );
74
- }
75
- const expectedServerBuildPath = path.join(
76
- BUILD_DIR,
77
- WORKER_SUBDIR,
78
- "index.js"
79
- );
80
- if (config.serverBuildPath !== path.resolve(root, expectedServerBuildPath)) {
81
- throwConfigError(
82
- `The serverBuildPath in remix.config.js must be "${expectedServerBuildPath}".`
83
- );
84
- }
85
- const expectedAssetsBuildDirectory = path.join(BUILD_DIR, CLIENT_SUBDIR);
86
- if (!config.assetsBuildDirectory.startsWith(
87
- path.resolve(root, expectedAssetsBuildDirectory)
88
- )) {
89
- throwConfigError(
90
- `The assetsBuildDirectory in remix.config.js must be in "${expectedAssetsBuildDirectory}".`
29
+ if (!skipOxygenChecks) {
30
+ if (!config.serverEntryPoint) {
31
+ throwConfigError(
32
+ "Could not find a server entry point.",
33
+ "Please add a server option to your remix.config.js pointing to an Oxygen worker entry file."
34
+ );
35
+ } else {
36
+ assertEntryFileExists(config.rootDirectory, config.serverEntryPoint);
37
+ }
38
+ if (config.serverPlatform !== "neutral") {
39
+ throwConfigError(
40
+ 'The serverPlatform in remix.config.js must be "neutral".'
41
+ );
42
+ }
43
+ if (config.serverModuleFormat !== "esm") {
44
+ throwConfigError(
45
+ 'The serverModuleFormat in remix.config.js must be "esm".'
46
+ );
47
+ }
48
+ if (config.serverDependenciesToBundle !== "all") {
49
+ throwConfigError(
50
+ 'The serverDependenciesToBundle in remix.config.js must be "all".'
51
+ );
52
+ }
53
+ if (!config.serverConditions?.includes("worker")) {
54
+ throwConfigError(
55
+ 'The serverConditions in remix.config.js must include "worker".'
56
+ );
57
+ }
58
+ if (process.env.NODE_ENV === "development" && !config.serverConditions?.includes("development")) {
59
+ outputWarn(
60
+ "Add `process.env.NODE_ENV` value to serverConditions in remix.config.js to enable debugging features in development."
61
+ );
62
+ }
63
+ if (!config.serverMainFields || !oxygenServerMainFields.every(
64
+ (v, i) => config.serverMainFields?.[i] === v
65
+ )) {
66
+ throwConfigError(
67
+ `The serverMainFields in remix.config.js must be ${JSON.stringify(
68
+ oxygenServerMainFields
69
+ )}.`
70
+ );
71
+ }
72
+ const cdnUrl = process.env.HYDROGEN_ASSET_BASE_URL;
73
+ if (cdnUrl && !config.publicPath.startsWith(cdnUrl)) {
74
+ throwConfigError(
75
+ "The publicPath in remix.config.js must be prepended with the value of `process.env.HYDROGEN_ASSET_BASE_URL`."
76
+ );
77
+ }
78
+ const expectedServerBuildPath = path.join(
79
+ BUILD_DIR,
80
+ WORKER_SUBDIR,
81
+ "index.js"
91
82
  );
83
+ if (config.serverBuildPath !== path.resolve(root, expectedServerBuildPath)) {
84
+ throwConfigError(
85
+ `The serverBuildPath in remix.config.js must be "${expectedServerBuildPath}".`
86
+ );
87
+ }
88
+ const expectedAssetsBuildDirectory = path.join(BUILD_DIR, CLIENT_SUBDIR);
89
+ if (!config.assetsBuildDirectory.startsWith(
90
+ path.resolve(root, expectedAssetsBuildDirectory)
91
+ )) {
92
+ throwConfigError(
93
+ `The assetsBuildDirectory in remix.config.js must be in "${expectedAssetsBuildDirectory}".`
94
+ );
95
+ }
92
96
  }
93
97
  if (process.env.LOCAL_DEV) {
94
98
  const packagesPath = fileURLToPath(new URL("../../..", import.meta.url));
package/dist/lib/flags.js CHANGED
@@ -2,7 +2,7 @@ import { Flags } from '@oclif/core';
2
2
  import { camelize } from '@shopify/cli-kit/common/string';
3
3
  import { renderInfo } from '@shopify/cli-kit/node/ui';
4
4
  import { normalizeStoreFqdn } from '@shopify/cli-kit/node/context/fqdn';
5
- import { colors } from './colors.js';
5
+ import colors from '@shopify/cli-kit/node/colors';
6
6
 
7
7
  const commonFlags = {
8
8
  path: Flags.string({
@@ -28,8 +28,7 @@ const commonFlags = {
28
28
  ["env-branch"]: Flags.string({
29
29
  description: "Specify an environment's branch name when using remote environment variables.",
30
30
  env: "SHOPIFY_HYDROGEN_ENVIRONMENT_BRANCH",
31
- char: "e",
32
- hidden: true
31
+ char: "e"
33
32
  })
34
33
  };
35
34
  function flagsToCamelObject(obj) {
@@ -1,11 +1,14 @@
1
+ import { AdminSession } from '@shopify/cli-kit/node/session';
2
+
1
3
  declare const LinkStorefrontQuery = "#graphql\n query LinkStorefront {\n hydrogenStorefronts {\n id\n title\n productionUrl\n }\n }\n";
2
- interface HydrogenStorefront {
3
- id: string;
4
- title: string;
5
- productionUrl: string;
6
- }
7
- interface LinkStorefrontSchema {
8
- hydrogenStorefronts: HydrogenStorefront[];
9
- }
4
+ declare function getStorefronts(shop: string): Promise<{
5
+ adminSession: AdminSession;
6
+ storefronts: {
7
+ parsedId: string;
8
+ id: string;
9
+ title: string;
10
+ productionUrl: string;
11
+ }[];
12
+ }>;
10
13
 
11
- export { LinkStorefrontQuery, LinkStorefrontSchema };
14
+ export { LinkStorefrontQuery, getStorefronts };
@@ -1,3 +1,6 @@
1
+ import { adminRequest, parseGid } from '../../graphql.js';
2
+ import { getAdminSession } from '../../admin-session.js';
3
+
1
4
  const LinkStorefrontQuery = `#graphql
2
5
  query LinkStorefront {
3
6
  hydrogenStorefronts {
@@ -7,5 +10,19 @@ const LinkStorefrontQuery = `#graphql
7
10
  }
8
11
  }
9
12
  `;
13
+ async function getStorefronts(shop) {
14
+ const adminSession = await getAdminSession(shop);
15
+ const { hydrogenStorefronts } = await adminRequest(
16
+ LinkStorefrontQuery,
17
+ adminSession
18
+ );
19
+ return {
20
+ adminSession,
21
+ storefronts: hydrogenStorefronts.map((storefront) => ({
22
+ ...storefront,
23
+ parsedId: parseGid(storefront.id)
24
+ }))
25
+ };
26
+ }
10
27
 
11
- export { LinkStorefrontQuery };
28
+ export { LinkStorefrontQuery, getStorefronts };
@@ -1,4 +1,5 @@
1
- declare const ListEnvironmentsQuery = "#graphql\n query ListStorefronts($id: ID!) {\n hydrogenStorefront(id: $id) {\n id\n productionUrl\n environments {\n branch\n createdAt\n id\n name\n type\n url\n }\n }\n }\n";
1
+ import { AdminSession } from '@shopify/cli-kit/node/session';
2
+
2
3
  type EnvironmentType = 'PREVIEW' | 'PRODUCTION' | 'CUSTOM';
3
4
  interface Environment {
4
5
  branch: string | null;
@@ -13,8 +14,8 @@ interface HydrogenStorefront {
13
14
  environments: Environment[];
14
15
  productionUrl: string;
15
16
  }
16
- interface ListEnvironmentsSchema {
17
- hydrogenStorefront: HydrogenStorefront | null;
18
- }
17
+ declare function getStorefrontEnvironments(adminSession: AdminSession, storefrontId: string): Promise<{
18
+ storefront: HydrogenStorefront | null;
19
+ }>;
19
20
 
20
- export { Environment, EnvironmentType, ListEnvironmentsQuery, ListEnvironmentsSchema };
21
+ export { Environment, getStorefrontEnvironments };
@@ -1,3 +1,5 @@
1
+ import { adminRequest } from '../../graphql.js';
2
+
1
3
  const ListEnvironmentsQuery = `#graphql
2
4
  query ListStorefronts($id: ID!) {
3
5
  hydrogenStorefront(id: $id) {
@@ -14,5 +16,13 @@ const ListEnvironmentsQuery = `#graphql
14
16
  }
15
17
  }
16
18
  `;
19
+ async function getStorefrontEnvironments(adminSession, storefrontId) {
20
+ const { hydrogenStorefront } = await adminRequest(
21
+ ListEnvironmentsQuery,
22
+ adminSession,
23
+ { id: storefrontId }
24
+ );
25
+ return { storefront: hydrogenStorefront };
26
+ }
17
27
 
18
- export { ListEnvironmentsQuery };
28
+ export { getStorefrontEnvironments };
@@ -1,4 +1,5 @@
1
- declare const ListStorefrontsQuery = "#graphql\n query ListStorefronts {\n hydrogenStorefronts {\n id\n title\n productionUrl\n currentProductionDeployment {\n id\n createdAt\n commitMessage\n }\n }\n }\n";
1
+ import { AdminSession } from '@shopify/cli-kit/node/session';
2
+
2
3
  interface Deployment {
3
4
  id: string;
4
5
  createdAt: string;
@@ -10,8 +11,15 @@ interface HydrogenStorefront {
10
11
  productionUrl?: string;
11
12
  currentProductionDeployment: Deployment | null;
12
13
  }
13
- interface ListStorefrontsSchema {
14
- hydrogenStorefronts: HydrogenStorefront[];
15
- }
14
+ declare function getStorefrontsWithDeployment(shop: string): Promise<{
15
+ adminSession: AdminSession;
16
+ storefronts: {
17
+ parsedId: string;
18
+ id: string;
19
+ title: string;
20
+ productionUrl?: string | undefined;
21
+ currentProductionDeployment: Deployment | null;
22
+ }[];
23
+ }>;
16
24
 
17
- export { Deployment, ListStorefrontsQuery, ListStorefrontsSchema };
25
+ export { Deployment, HydrogenStorefront, getStorefrontsWithDeployment };
@@ -1,3 +1,6 @@
1
+ import { adminRequest, parseGid } from '../../graphql.js';
2
+ import { getAdminSession } from '../../admin-session.js';
3
+
1
4
  const ListStorefrontsQuery = `#graphql
2
5
  query ListStorefronts {
3
6
  hydrogenStorefronts {
@@ -12,5 +15,19 @@ const ListStorefrontsQuery = `#graphql
12
15
  }
13
16
  }
14
17
  `;
18
+ async function getStorefrontsWithDeployment(shop) {
19
+ const adminSession = await getAdminSession(shop);
20
+ const { hydrogenStorefronts } = await adminRequest(
21
+ ListStorefrontsQuery,
22
+ adminSession
23
+ );
24
+ return {
25
+ adminSession,
26
+ storefronts: hydrogenStorefronts.map((storefront) => ({
27
+ ...storefront,
28
+ parsedId: parseGid(storefront.id)
29
+ }))
30
+ };
31
+ }
15
32
 
16
- export { ListStorefrontsQuery };
33
+ export { getStorefrontsWithDeployment };
@@ -1,3 +1,5 @@
1
+ import { AdminSession } from '@shopify/cli-kit/node/session';
2
+
1
3
  declare const PullVariablesQuery = "#graphql\n query PullVariables($id: ID!, $branch: String) {\n hydrogenStorefront(id: $id) {\n id\n environmentVariables(branchName: $branch) {\n id\n isSecret\n key\n value\n }\n }\n }\n";
2
4
  interface EnvironmentVariable {
3
5
  id: string;
@@ -12,5 +14,8 @@ interface HydrogenStorefront {
12
14
  interface PullVariablesSchema {
13
15
  hydrogenStorefront: HydrogenStorefront | null;
14
16
  }
17
+ declare function getStorefrontEnvVariables(adminSession: AdminSession, storefrontId: string, envBranch?: string): Promise<{
18
+ storefront: HydrogenStorefront | null;
19
+ }>;
15
20
 
16
- export { EnvironmentVariable, PullVariablesQuery, PullVariablesSchema };
21
+ export { EnvironmentVariable, PullVariablesQuery, PullVariablesSchema, getStorefrontEnvVariables };
@@ -1,3 +1,5 @@
1
+ import { adminRequest } from '../../graphql.js';
2
+
1
3
  const PullVariablesQuery = `#graphql
2
4
  query PullVariables($id: ID!, $branch: String) {
3
5
  hydrogenStorefront(id: $id) {
@@ -11,5 +13,16 @@ const PullVariablesQuery = `#graphql
11
13
  }
12
14
  }
13
15
  `;
16
+ async function getStorefrontEnvVariables(adminSession, storefrontId, envBranch) {
17
+ const { hydrogenStorefront } = await adminRequest(
18
+ PullVariablesQuery,
19
+ adminSession,
20
+ {
21
+ id: storefrontId,
22
+ branch: envBranch
23
+ }
24
+ );
25
+ return { storefront: hydrogenStorefront };
26
+ }
14
27
 
15
- export { PullVariablesQuery };
28
+ export { PullVariablesQuery, getStorefrontEnvVariables };
@@ -1,7 +1,7 @@
1
1
  import { outputInfo, outputContent, outputToken } from '@shopify/cli-kit/node/output';
2
2
  import { resolvePath } from '@shopify/cli-kit/node/path';
3
3
  import { fileExists } from '@shopify/cli-kit/node/fs';
4
- import { colors } from './colors.js';
4
+ import colors from '@shopify/cli-kit/node/colors';
5
5
 
6
6
  async function startMiniOxygen({
7
7
  root,
@@ -0,0 +1,6 @@
1
+ import { exec } from 'node:child_process';
2
+
3
+ declare const execAsync: typeof exec.__promisify__;
4
+ declare function supressNodeExperimentalWarnings(): void;
5
+
6
+ export { execAsync, supressNodeExperimentalWarnings };
@@ -0,0 +1,17 @@
1
+ import { exec } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+
4
+ const execAsync = promisify(exec);
5
+ function supressNodeExperimentalWarnings() {
6
+ const warningListener = process.listeners("warning")[0];
7
+ if (warningListener) {
8
+ process.removeAllListeners("warning");
9
+ process.prependListener("warning", (warning) => {
10
+ if (warning.name != "ExperimentalWarning") {
11
+ warningListener(warning);
12
+ }
13
+ });
14
+ }
15
+ }
16
+
17
+ export { execAsync, supressNodeExperimentalWarnings };
@@ -1,4 +1,5 @@
1
1
  import { EnvironmentVariable } from './graphql/admin/pull-variables.js';
2
+ import '@shopify/cli-kit/node/session';
2
3
 
3
4
  interface Arguments {
4
5
  envBranch?: string;
@@ -1,12 +1,11 @@
1
1
  import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
2
2
  import { outputContent, outputToken, outputInfo } from '@shopify/cli-kit/node/output';
3
3
  import { linkStorefront } from '../commands/hydrogen/link.js';
4
- import { adminRequest } from './graphql.js';
5
4
  import { getHydrogenShop } from './shop.js';
6
5
  import { getAdminSession } from './admin-session.js';
7
6
  import { getConfig } from './shopify-config.js';
8
7
  import { renderMissingLink, renderMissingStorefront } from './render-errors.js';
9
- import { PullVariablesQuery } from './graphql/admin/pull-variables.js';
8
+ import { getStorefrontEnvVariables } from './graphql/admin/pull-variables.js';
10
9
 
11
10
  async function pullRemoteEnvironmentVariables({
12
11
  envBranch,
@@ -35,20 +34,11 @@ async function pullRemoteEnvironmentVariables({
35
34
  if (!configStorefront) {
36
35
  return [];
37
36
  }
38
- if (!silent) {
39
- outputInfo(
40
- `Fetching environment variables from ${configStorefront.title}...`
41
- );
42
- }
43
- const result = await adminRequest(
44
- PullVariablesQuery,
37
+ const { storefront } = await getStorefrontEnvVariables(
45
38
  adminSession,
46
- {
47
- id: configStorefront.id,
48
- branch: envBranch
49
- }
39
+ configStorefront.id,
40
+ envBranch
50
41
  );
51
- const storefront = result.hydrogenStorefront;
52
42
  if (!storefront) {
53
43
  if (!silent) {
54
44
  renderMissingStorefront({ adminSession, storefront: configStorefront });
@@ -13,7 +13,7 @@ function isRemixV2() {
13
13
  async function getV2Flags(root) {
14
14
  const isV2 = isRemixV2();
15
15
  const futureFlags = {
16
- ...!isV2 && (await getRemixConfig(root)).future
16
+ ...!isV2 && (await getRemixConfig(root, true)).future
17
17
  };
18
18
  return {
19
19
  isV2Meta: isV2 || !!futureFlags.v2_meta,
@@ -1,12 +1,11 @@
1
1
  type UnixShell = 'zsh' | 'bash' | 'fish';
2
2
  type WindowsShell = 'PowerShell' | 'PowerShell 7+' | 'CMD';
3
3
  type Shell = UnixShell | WindowsShell;
4
+ declare const ALIAS_NAME = "h2";
4
5
  declare const isWindows: () => boolean;
5
6
  declare const isGitBash: () => boolean;
6
- declare function homeFileExists(filepath: string): false | Promise<boolean>;
7
- declare function supportsShell(shell: UnixShell): boolean;
8
- declare function hasAlias(aliasName: string, filepath: string): boolean;
9
- declare function shellWriteFile(filepath: string, content: string, append?: boolean): boolean;
10
- declare function shellRunScript(script: string, shellBin: string): boolean;
7
+ declare function shellWriteAlias(shell: UnixShell, aliasName: string, content: string): Promise<boolean>;
8
+ declare function shellRunScript(script: string, shellBin: string): Promise<boolean>;
9
+ declare function getCliCommand(): Promise<"h2" | "yarn shopify hydrogen" | "pnpm shopify hydrogen" | "npx shopify hydrogen">;
11
10
 
12
- export { Shell, UnixShell, WindowsShell, hasAlias, homeFileExists, isGitBash, isWindows, shellRunScript, shellWriteFile, supportsShell };
11
+ export { ALIAS_NAME, Shell, UnixShell, WindowsShell, getCliCommand, isGitBash, isWindows, shellRunScript, shellWriteAlias };
package/dist/lib/shell.js CHANGED
@@ -1,14 +1,16 @@
1
- import os from 'os';
2
- import path from 'path';
3
- import { execSync } from 'child_process';
1
+ import os from 'node:os';
4
2
  import { fileExists } from '@shopify/cli-kit/node/fs';
3
+ import { joinPath } from '@shopify/cli-kit/node/path';
5
4
  import { outputDebug } from '@shopify/cli-kit/node/output';
5
+ import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
6
+ import { execAsync } from './process.js';
6
7
 
8
+ const ALIAS_NAME = "h2";
7
9
  const isWindows = () => process.platform === "win32";
8
10
  const isGitBash = () => !!process.env.MINGW_PREFIX;
9
11
  function resolveFromHome(filepath) {
10
12
  if (filepath[0] === "~") {
11
- return path.join(os.homedir(), filepath.slice(1));
13
+ return joinPath(os.homedir(), filepath.slice(1));
12
14
  }
13
15
  return filepath;
14
16
  }
@@ -19,33 +21,44 @@ function homeFileExists(filepath) {
19
21
  return false;
20
22
  }
21
23
  }
22
- function supportsShell(shell) {
24
+ async function supportsShell(shell) {
23
25
  try {
24
- execSync(`which ${shell}`, { stdio: "ignore" });
26
+ await execAsync(`which ${shell}`);
25
27
  return true;
26
28
  } catch {
27
29
  return false;
28
30
  }
29
31
  }
30
- function hasAlias(aliasName, filepath) {
32
+ function getShellAliasDefinitionFile(shell) {
33
+ if (shell === "bash")
34
+ return "~/.bashrc";
35
+ if (shell === "zsh")
36
+ return "~/.zshrc";
37
+ return `~/.config/fish/functions/${ALIAS_NAME}.fish`;
38
+ }
39
+ async function hasAliasDefinition(aliasName, shell) {
40
+ const filepath = getShellAliasDefinitionFile(shell);
31
41
  try {
32
- const result = execSync(
33
- `grep 'alias ${aliasName}' ${resolveFromHome(filepath)}`,
34
- { stdio: "pipe" }
35
- ).toString();
36
- return !!result;
42
+ if (shell === "fish") {
43
+ return await homeFileExists(filepath);
44
+ }
45
+ const result = await execAsync(
46
+ `grep 'alias ${aliasName}' ${resolveFromHome(filepath)}`
47
+ );
48
+ return !!result.stdout;
37
49
  } catch {
38
50
  return false;
39
51
  }
40
52
  }
41
- function shellWriteFile(filepath, content, append = false) {
53
+ async function shellWriteFile(shell, content, append = false) {
54
+ const filepath = getShellAliasDefinitionFile(shell);
42
55
  content = `"${content}"`;
43
56
  content = content.replaceAll("\n", "\\n");
44
57
  if (!isWindows()) {
45
58
  content = content.replaceAll("$", "\\$");
46
59
  }
47
60
  try {
48
- execSync(
61
+ await execAsync(
49
62
  `printf ${content} ${append ? ">>" : ">"} ${resolveFromHome(filepath)}`
50
63
  );
51
64
  return true;
@@ -57,9 +70,16 @@ function shellWriteFile(filepath, content, append = false) {
57
70
  return false;
58
71
  }
59
72
  }
60
- function shellRunScript(script, shellBin) {
73
+ async function shellWriteAlias(shell, aliasName, content) {
74
+ if (!await supportsShell(shell))
75
+ return false;
76
+ if (await hasAliasDefinition(aliasName, shell))
77
+ return true;
78
+ return await shellWriteFile(shell, content, shell !== "fish");
79
+ }
80
+ async function shellRunScript(script, shellBin) {
61
81
  try {
62
- execSync(script, { shell: shellBin, stdio: "ignore" });
82
+ await execAsync(script, { shell: shellBin });
63
83
  return true;
64
84
  } catch (error) {
65
85
  outputDebug(
@@ -69,5 +89,33 @@ function shellRunScript(script, shellBin) {
69
89
  return false;
70
90
  }
71
91
  }
92
+ function isKnownUnixShell(shell) {
93
+ return ["zsh", "bash", "fish"].includes(shell);
94
+ }
95
+ async function hasCliAlias() {
96
+ try {
97
+ if (isWindows() && !isGitBash()) {
98
+ await execAsync(`Get-Alias -Name ${ALIAS_NAME}`);
99
+ } else {
100
+ const shell = os.userInfo().shell?.split("/").pop() ?? "bash";
101
+ if (!isKnownUnixShell(shell))
102
+ return false;
103
+ return await hasAliasDefinition(ALIAS_NAME, shell);
104
+ }
105
+ return true;
106
+ } catch {
107
+ return false;
108
+ }
109
+ }
110
+ async function getCliCommand() {
111
+ if (await hasCliAlias()) {
112
+ return ALIAS_NAME;
113
+ }
114
+ let cli = "npx";
115
+ const pkgManager = await getPackageManager(process.cwd()).catch(() => null);
116
+ if (pkgManager === "pnpm" || pkgManager === "yarn")
117
+ cli = pkgManager;
118
+ return `${cli} shopify hydrogen`;
119
+ }
72
120
 
73
- export { hasAlias, homeFileExists, isGitBash, isWindows, shellRunScript, shellWriteFile, supportsShell };
121
+ export { ALIAS_NAME, getCliCommand, isGitBash, isWindows, shellRunScript, shellWriteAlias };
@@ -0,0 +1 @@
1
+