@shopify/cli-hydrogen 4.2.0 → 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
@@ -2,11 +2,15 @@ import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
2
2
  import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
3
3
  import { renderSelectPrompt, renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
4
4
  import { adminRequest } from '../../lib/graphql.js';
5
- import { LinkStorefrontQuery } from '../../lib/graphql/admin/link-storefront.js';
6
- import { getAdminSession } from '../../lib/admin-session.js';
5
+ import { getStorefronts } from '../../lib/graphql/admin/link-storefront.js';
7
6
  import { getConfig, setStorefront } from '../../lib/shopify-config.js';
8
7
  import { linkStorefront } from './link.js';
9
8
 
9
+ const SHOP = "my-shop";
10
+ const ADMIN_SESSION = {
11
+ token: "abc123",
12
+ storeFqdn: SHOP
13
+ };
10
14
  vi.mock("@shopify/cli-kit/node/ui", async () => {
11
15
  const original = await vi.importActual("@shopify/cli-kit/node/ui");
12
16
  return {
@@ -17,22 +21,22 @@ vi.mock("@shopify/cli-kit/node/ui", async () => {
17
21
  });
18
22
  vi.mock("../../lib/graphql.js");
19
23
  vi.mock("../../lib/shopify-config.js");
20
- vi.mock("../../lib/admin-session.js");
24
+ vi.mock("../../lib/graphql/admin/link-storefront.js");
21
25
  vi.mock("../../lib/shop.js", () => ({
22
- getHydrogenShop: () => "my-shop"
26
+ getHydrogenShop: () => SHOP
27
+ }));
28
+ vi.mock("../../lib/shell.js", () => ({
29
+ getCliCommand: () => "h2"
23
30
  }));
24
- const ADMIN_SESSION = {
25
- token: "abc123",
26
- storeFqdn: "my-shop"
27
- };
28
31
  describe("link", () => {
29
32
  const outputMock = mockAndCaptureOutput();
30
33
  beforeEach(async () => {
31
- vi.mocked(getAdminSession).mockResolvedValue(ADMIN_SESSION);
32
- vi.mocked(adminRequest).mockResolvedValue({
33
- hydrogenStorefronts: [
34
+ vi.mocked(getStorefronts).mockResolvedValue({
35
+ adminSession: ADMIN_SESSION,
36
+ storefronts: [
34
37
  {
35
38
  id: "gid://shopify/HydrogenStorefront/1",
39
+ parsedId: "1",
36
40
  title: "Hydrogen",
37
41
  productionUrl: "https://example.com"
38
42
  }
@@ -46,26 +50,36 @@ describe("link", () => {
46
50
  });
47
51
  it("makes a GraphQL call to fetch the storefronts", async () => {
48
52
  await linkStorefront({});
49
- expect(adminRequest).toHaveBeenCalledWith(
50
- LinkStorefrontQuery,
51
- ADMIN_SESSION
52
- );
53
+ expect(getStorefronts).toHaveBeenCalledWith(SHOP);
53
54
  });
54
55
  it("renders a list of choices and forwards the selection to setStorefront", async () => {
55
56
  vi.mocked(renderSelectPrompt).mockResolvedValue(
56
57
  "gid://shopify/HydrogenStorefront/1"
57
58
  );
58
59
  await linkStorefront({ path: "my-path" });
59
- expect(setStorefront).toHaveBeenCalledWith("my-path", {
60
- id: "gid://shopify/HydrogenStorefront/1",
61
- title: "Hydrogen",
62
- productionUrl: "https://example.com"
63
- });
60
+ expect(setStorefront).toHaveBeenCalledWith(
61
+ "my-path",
62
+ expect.objectContaining({
63
+ id: "gid://shopify/HydrogenStorefront/1",
64
+ title: "Hydrogen"
65
+ })
66
+ );
67
+ });
68
+ it("renders a success message", async () => {
69
+ vi.mocked(renderSelectPrompt).mockResolvedValue(
70
+ "gid://shopify/HydrogenStorefront/1"
71
+ );
72
+ await linkStorefront({ path: "my-path" });
73
+ expect(outputMock.info()).toMatch(/Hydrogen is now linked/g);
74
+ expect(outputMock.info()).toMatch(
75
+ /Run `h2 dev` to start your local development server and start building/g
76
+ );
64
77
  });
65
78
  describe("when there are no Hydrogen storefronts", () => {
66
79
  it("renders a message and returns early", async () => {
67
- vi.mocked(adminRequest).mockResolvedValue({
68
- hydrogenStorefronts: []
80
+ vi.mocked(getStorefronts).mockResolvedValue({
81
+ adminSession: ADMIN_SESSION,
82
+ storefronts: []
69
83
  });
70
84
  await linkStorefront({});
71
85
  expect(outputMock.info()).toMatch(
@@ -119,11 +133,13 @@ describe("link", () => {
119
133
  it("does not prompt the user to make a selection", async () => {
120
134
  await linkStorefront({ path: "my-path", storefront: "Hydrogen" });
121
135
  expect(renderSelectPrompt).not.toHaveBeenCalled();
122
- expect(setStorefront).toHaveBeenCalledWith("my-path", {
123
- id: "gid://shopify/HydrogenStorefront/1",
124
- title: "Hydrogen",
125
- productionUrl: "https://example.com"
126
- });
136
+ expect(setStorefront).toHaveBeenCalledWith(
137
+ "my-path",
138
+ expect.objectContaining({
139
+ id: "gid://shopify/HydrogenStorefront/1",
140
+ title: "Hydrogen"
141
+ })
142
+ );
127
143
  });
128
144
  describe("and there is no matching storefront", () => {
129
145
  it("renders a warning message and returns early", async () => {
@@ -1,10 +1,10 @@
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
  import { Deployment } from '../../lib/graphql/admin/list-storefronts.js';
4
+ import '@shopify/cli-kit/node/session';
4
5
 
5
6
  declare class List extends Command {
6
7
  static description: string;
7
- static hidden: boolean;
8
8
  static flags: {
9
9
  path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
10
10
  shop: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -16,6 +16,6 @@ interface Flags {
16
16
  shop?: string;
17
17
  }
18
18
  declare function listStorefronts({ path, shop: flagShop }: Flags): Promise<void>;
19
- declare function formatDeployment(deployment: Deployment | null): string;
19
+ declare function formatDeployment(deployment: Deployment): string;
20
20
 
21
21
  export { List as default, formatDeployment, listStorefronts };
@@ -1,16 +1,15 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
- import { renderTable } from '@shopify/cli-kit/node/ui';
3
- import { outputInfo, outputContent } from '@shopify/cli-kit/node/output';
4
- import { adminRequest, parseGid } from '../../lib/graphql.js';
2
+ import { pluralize } from '@shopify/cli-kit/common/string';
3
+ import colors from '@shopify/cli-kit/node/colors';
4
+ import { outputNewline, outputInfo, outputContent } from '@shopify/cli-kit/node/output';
5
5
  import { commonFlags } from '../../lib/flags.js';
6
6
  import { getHydrogenShop } from '../../lib/shop.js';
7
- import { getAdminSession } from '../../lib/admin-session.js';
8
- import { ListStorefrontsQuery } from '../../lib/graphql/admin/list-storefronts.js';
7
+ import { parseGid } from '../../lib/graphql.js';
8
+ import { getStorefrontsWithDeployment } from '../../lib/graphql/admin/list-storefronts.js';
9
9
  import { logMissingStorefronts } from '../../lib/missing-storefronts.js';
10
10
 
11
11
  class List extends Command {
12
12
  static description = "Returns a list of Hydrogen storefronts available on a given shop.";
13
- static hidden = true;
14
13
  static flags = {
15
14
  path: commonFlags.path,
16
15
  shop: commonFlags.shop
@@ -22,49 +21,44 @@ class List extends Command {
22
21
  }
23
22
  async function listStorefronts({ path, shop: flagShop }) {
24
23
  const shop = await getHydrogenShop({ path, shop: flagShop });
25
- const adminSession = await getAdminSession(shop);
26
- const result = await adminRequest(
27
- ListStorefrontsQuery,
28
- adminSession
29
- );
30
- const storefrontsCount = result.hydrogenStorefronts.length;
31
- if (storefrontsCount > 0) {
24
+ const { storefronts, adminSession } = await getStorefrontsWithDeployment(shop);
25
+ if (storefronts.length > 0) {
26
+ outputNewline();
32
27
  outputInfo(
33
- outputContent`Found ${storefrontsCount.toString()} Hydrogen storefronts on ${shop}:\n`.value
34
- );
35
- const rows = result.hydrogenStorefronts.map(
36
- ({ id, title, productionUrl, currentProductionDeployment }) => ({
37
- id: parseGid(id),
38
- title,
39
- productionUrl,
40
- currentDeployment: formatDeployment(currentProductionDeployment)
41
- })
28
+ pluralizedStorefronts({
29
+ storefronts,
30
+ shop
31
+ }).toString()
42
32
  );
43
- renderTable({
44
- rows,
45
- columns: {
46
- id: {
47
- header: "ID"
48
- },
49
- title: {
50
- header: "Name",
51
- color: "whiteBright"
52
- },
53
- productionUrl: {
54
- header: "Production URL"
55
- },
56
- currentDeployment: {
57
- header: "Current deployment"
33
+ storefronts.forEach(
34
+ ({ currentProductionDeployment, id, productionUrl, title }) => {
35
+ outputNewline();
36
+ outputInfo(
37
+ outputContent`${colors.whiteBright(title)} ${colors.dim(
38
+ `(id: ${parseGid(id)})`
39
+ )}`.value
40
+ );
41
+ if (productionUrl) {
42
+ outputInfo(
43
+ outputContent` ${colors.whiteBright(productionUrl)}`.value
44
+ );
45
+ }
46
+ if (currentProductionDeployment) {
47
+ outputInfo(
48
+ outputContent` ${colors.dim(
49
+ formatDeployment(currentProductionDeployment)
50
+ )}`.value
51
+ );
58
52
  }
59
53
  }
60
- });
54
+ );
61
55
  } else {
62
56
  logMissingStorefronts(adminSession);
63
57
  }
64
58
  }
65
59
  const dateFormat = new Intl.DateTimeFormat("default", {
66
60
  year: "numeric",
67
- month: "long",
61
+ month: "numeric",
68
62
  day: "numeric"
69
63
  });
70
64
  function formatDeployment(deployment) {
@@ -79,5 +73,15 @@ function formatDeployment(deployment) {
79
73
  }
80
74
  return message;
81
75
  }
76
+ const pluralizedStorefronts = ({
77
+ storefronts,
78
+ shop
79
+ }) => {
80
+ return pluralize(
81
+ storefronts,
82
+ (storefronts2) => `Showing ${storefronts2.length} Hydrogen storefronts for the store ${shop}`,
83
+ (_storefront) => `Showing 1 Hydrogen storefront for the store ${shop}`
84
+ );
85
+ };
82
86
 
83
87
  export { List as default, formatDeployment, listStorefronts };
@@ -1,32 +1,24 @@
1
1
  import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
2
2
  import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
3
- import { ListStorefrontsQuery } from '../../lib/graphql/admin/list-storefronts.js';
4
- import { getAdminSession } from '../../lib/admin-session.js';
5
- import { adminRequest } from '../../lib/graphql.js';
3
+ import { getStorefrontsWithDeployment } from '../../lib/graphql/admin/list-storefronts.js';
6
4
  import { listStorefronts, formatDeployment } from './list.js';
7
5
 
8
- vi.mock("../../lib/admin-session.js");
9
- vi.mock("../../lib/graphql.js", async () => {
10
- const original = await vi.importActual(
11
- "../../lib/graphql.js"
12
- );
13
- return {
14
- ...original,
15
- adminRequest: vi.fn()
16
- };
6
+ const SHOP_NAME = "my-shop";
7
+ vi.mock("../../lib/graphql/admin/list-storefronts.js", async () => {
8
+ return { getStorefrontsWithDeployment: vi.fn() };
17
9
  });
18
10
  vi.mock("../../lib/shop.js", () => ({
19
- getHydrogenShop: () => "my-shop"
11
+ getHydrogenShop: () => SHOP_NAME
20
12
  }));
21
13
  describe("list", () => {
22
14
  const ADMIN_SESSION = {
23
15
  token: "abc123",
24
- storeFqdn: "my-shop"
16
+ storeFqdn: SHOP_NAME
25
17
  };
26
18
  beforeEach(async () => {
27
- vi.mocked(getAdminSession).mockResolvedValue(ADMIN_SESSION);
28
- vi.mocked(adminRequest).mockResolvedValue({
29
- hydrogenStorefronts: []
19
+ vi.mocked(getStorefrontsWithDeployment).mockResolvedValue({
20
+ adminSession: ADMIN_SESSION,
21
+ storefronts: []
30
22
  });
31
23
  });
32
24
  afterEach(() => {
@@ -35,23 +27,23 @@ describe("list", () => {
35
27
  });
36
28
  it("makes a GraphQL call to fetch the storefronts", async () => {
37
29
  await listStorefronts({});
38
- expect(adminRequest).toHaveBeenCalledWith(
39
- ListStorefrontsQuery,
40
- ADMIN_SESSION
41
- );
30
+ expect(getStorefrontsWithDeployment).toHaveBeenCalledWith(SHOP_NAME);
42
31
  });
43
32
  describe("and there are storefronts", () => {
44
33
  beforeEach(() => {
45
- vi.mocked(adminRequest).mockResolvedValue({
46
- hydrogenStorefronts: [
34
+ vi.mocked(getStorefrontsWithDeployment).mockResolvedValue({
35
+ adminSession: ADMIN_SESSION,
36
+ storefronts: [
47
37
  {
48
38
  id: "gid://shopify/HydrogenStorefront/1",
39
+ parsedId: "1",
49
40
  title: "Hydrogen",
50
41
  productionUrl: "https://example.com",
51
42
  currentProductionDeployment: null
52
43
  },
53
44
  {
54
45
  id: "gid://shopify/HydrogenStorefront/2",
46
+ parsedId: "2",
55
47
  title: "Demo Store",
56
48
  productionUrl: "https://demo.example.com",
57
49
  currentProductionDeployment: {
@@ -67,14 +59,13 @@ describe("list", () => {
67
59
  const outputMock = mockAndCaptureOutput();
68
60
  await listStorefronts({});
69
61
  expect(outputMock.info()).toMatch(
70
- /Found 2 Hydrogen storefronts on my-shop/g
71
- );
72
- expect(outputMock.info()).toMatch(
73
- /1 Hydrogen https:\/\/example.com/g
74
- );
75
- expect(outputMock.info()).toMatch(
76
- /2 Demo Store https:\/\/demo.example.com March 22, 2023, Update README.md/g
62
+ /Showing 2 Hydrogen storefronts for the store my-shop/g
77
63
  );
64
+ expect(outputMock.info()).toMatch(/Hydrogen \(id: 1\)/g);
65
+ expect(outputMock.info()).toMatch(/https:\/\/example.com/g);
66
+ expect(outputMock.info()).toMatch(/Demo Store \(id: 2\)/g);
67
+ expect(outputMock.info()).toMatch(/https:\/\/demo.example.com/g);
68
+ expect(outputMock.info()).toMatch(/3\/22\/2023, Update README.md/g);
78
69
  });
79
70
  });
80
71
  describe("and there are no storefronts", () => {
@@ -100,17 +91,18 @@ describe("formatDeployment", () => {
100
91
  commitMessage: "Update README.md\n\nThis is a description of why the change was made."
101
92
  };
102
93
  expect(formatDeployment(deployment)).toStrictEqual(
103
- "March 22, 2023, Update README.md"
94
+ "3/22/2023, Update README.md"
104
95
  );
105
96
  });
106
97
  describe("when there is no commit message", () => {
107
98
  it("only returns the date", () => {
108
99
  const deployment = {
109
100
  id: "gid://shopify/HydrogenStorefrontDeployment/1",
101
+ parsedId: "1",
110
102
  createdAt,
111
103
  commitMessage: null
112
104
  };
113
- expect(formatDeployment(deployment)).toStrictEqual("March 22, 2023");
105
+ expect(formatDeployment(deployment)).toStrictEqual("3/22/2023");
114
106
  });
115
107
  });
116
108
  });
@@ -1,8 +1,7 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
2
  import { renderSuccess, renderFatalError } from '@shopify/cli-kit/node/ui';
3
- import { isWindows, isGitBash, supportsShell, hasAlias, shellWriteFile, homeFileExists, shellRunScript } from '../../lib/shell.js';
3
+ import { ALIAS_NAME, isWindows, isGitBash, shellWriteAlias, shellRunScript } from '../../lib/shell.js';
4
4
 
5
- const ALIAS_NAME = "h2";
6
5
  class Shortcut extends Command {
7
6
  static description = `Creates a global \`${ALIAS_NAME}\` shortcut for the Hydrogen CLI`;
8
7
  async run() {
@@ -38,13 +37,13 @@ end
38
37
  `;
39
38
  async function createShortcutsForUnix() {
40
39
  const shells = [];
41
- if (supportsShell("zsh") && (hasAlias(ALIAS_NAME, "~/.zshrc") || shellWriteFile("~/.zshrc", BASH_ZSH_COMMAND, true))) {
40
+ if (await shellWriteAlias("zsh", ALIAS_NAME, BASH_ZSH_COMMAND)) {
42
41
  shells.push("zsh");
43
42
  }
44
- if (supportsShell("bash") && (hasAlias(ALIAS_NAME, "~/.bashrc") || shellWriteFile("~/.bashrc", BASH_ZSH_COMMAND, true))) {
43
+ if (await shellWriteAlias("bash", ALIAS_NAME, BASH_ZSH_COMMAND)) {
45
44
  shells.push("bash");
46
45
  }
47
- if (supportsShell("fish") && await homeFileExists("~/.config/fish/functions") && shellWriteFile(`~/.config/fish/functions/${ALIAS_NAME}.fish`, FISH_FUNCTION)) {
46
+ if (await shellWriteAlias("fish", ALIAS_NAME, FISH_FUNCTION)) {
48
47
  shells.push("fish");
49
48
  }
50
49
  return shells;
@@ -62,10 +61,10 @@ if (!$profileContent -or $profileContent -NotLike '*Invoke-Local-H2*') {
62
61
  `;
63
62
  async function createShortcutsForWindows() {
64
63
  const shells = [];
65
- if (shellRunScript(PS_APPEND_PROFILE_COMMAND, "powershell.exe")) {
64
+ if (await shellRunScript(PS_APPEND_PROFILE_COMMAND, "powershell.exe")) {
66
65
  shells.push("PowerShell");
67
66
  }
68
- if (shellRunScript(PS_APPEND_PROFILE_COMMAND, "pwsh.exe")) {
67
+ if (await shellRunScript(PS_APPEND_PROFILE_COMMAND, "pwsh.exe")) {
69
68
  shells.push("PowerShell 7+");
70
69
  }
71
70
  return shells;
@@ -1,7 +1,7 @@
1
1
  import { describe, beforeEach, vi, afterEach, expect, it } from 'vitest';
2
2
  import { runCreateShortcut } from './shortcut.js';
3
3
  import { mockAndCaptureOutput } from '@shopify/cli-kit/node/testing/output';
4
- import { supportsShell, isWindows, isGitBash } from '../../lib/shell.js';
4
+ import { shellWriteAlias, isWindows, isGitBash } from '../../lib/shell.js';
5
5
  import { execSync, exec } from 'child_process';
6
6
 
7
7
  describe("shortcut", () => {
@@ -10,18 +10,17 @@ describe("shortcut", () => {
10
10
  vi.resetAllMocks();
11
11
  vi.mock("child_process");
12
12
  vi.mock("../../lib/shell.js", async () => {
13
+ const original = await vi.importActual("../../lib/shell.js");
13
14
  return {
15
+ ...original,
14
16
  isWindows: vi.fn(),
15
17
  isGitBash: vi.fn(),
16
- supportsShell: vi.fn(),
17
- shellWriteFile: () => true,
18
- shellRunScript: () => true,
19
- hasAlias: () => false,
20
- homeFileExists: () => Promise.resolve(true)
18
+ shellWriteAlias: vi.fn(),
19
+ shellRunScript: async () => true
21
20
  };
22
21
  });
23
- vi.mocked(supportsShell).mockImplementation(
24
- (shell) => !isWindows() || shell === "bash"
22
+ vi.mocked(shellWriteAlias).mockImplementation(
23
+ async (shell) => !isWindows() || shell === "bash"
25
24
  );
26
25
  });
27
26
  afterEach(() => {
@@ -50,7 +49,7 @@ describe("shortcut", () => {
50
49
  });
51
50
  it("warns when not finding shells", async () => {
52
51
  vi.mocked(isWindows).mockReturnValue(false);
53
- vi.mocked(supportsShell).mockReturnValue(false);
52
+ vi.mocked(shellWriteAlias).mockResolvedValue(false);
54
53
  await runCreateShortcut();
55
54
  expect(outputMock.info()).toBeFalsy();
56
55
  expect(outputMock.error()).toBeTruthy();
@@ -3,7 +3,6 @@ import Command from '@shopify/cli-kit/node/base-command';
3
3
 
4
4
  declare class Unlink 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
  };
@@ -1,11 +1,11 @@
1
1
  import Command from '@shopify/cli-kit/node/base-command';
2
- import { outputWarn, outputSuccess } from '@shopify/cli-kit/node/output';
2
+ import { renderSuccess } from '@shopify/cli-kit/node/ui';
3
+ import { outputWarn } from '@shopify/cli-kit/node/output';
3
4
  import { commonFlags } from '../../lib/flags.js';
4
5
  import { getConfig, unsetStorefront } from '../../lib/shopify-config.js';
5
6
 
6
7
  class Unlink extends Command {
7
8
  static description = "Unlink a local project from a Hydrogen storefront.";
8
- static hidden = true;
9
9
  static flags = {
10
10
  path: commonFlags.path
11
11
  };
@@ -23,7 +23,9 @@ async function unlinkStorefront({ path }) {
23
23
  }
24
24
  const storefrontTitle = configStorefront.title;
25
25
  await unsetStorefront(actualPath);
26
- outputSuccess(`You are no longer linked to ${storefrontTitle}`);
26
+ renderSuccess({
27
+ body: ["You are no longer linked to", { bold: storefrontTitle }]
28
+ });
27
29
  }
28
30
 
29
31
  export { Unlink as default, unlinkStorefront };
@@ -1,4 +1,5 @@
1
1
  import { AdminSession } from '@shopify/cli-kit/node/session';
2
+ export { AdminSession } from '@shopify/cli-kit/node/session';
2
3
 
3
4
  declare function getAdminSession(shop: string): Promise<AdminSession>;
4
5
 
@@ -17,9 +17,10 @@ type ProjectDirs = {
17
17
  appDirectory: string;
18
18
  };
19
19
  type CodegenOptions = ProjectDirs & {
20
- configFilePath?: string;
21
20
  watch?: boolean;
21
+ configFilePath?: string;
22
+ forceSfapiVersion?: string;
22
23
  };
23
- declare function generateTypes({ configFilePath, watch, ...dirs }: CodegenOptions): Promise<string[]>;
24
+ declare function generateTypes({ watch, configFilePath, forceSfapiVersion, ...dirs }: CodegenOptions): Promise<string[]>;
24
25
 
25
26
  export { generateTypes, normalizeCodegenError, spawnCodegenProcess };
@@ -67,14 +67,15 @@ function spawnCodegenProcess({
67
67
  return child;
68
68
  }
69
69
  async function generateTypes({
70
- configFilePath,
71
70
  watch,
71
+ configFilePath,
72
+ forceSfapiVersion,
72
73
  ...dirs
73
74
  }) {
74
75
  const { config: codegenConfig } = await loadCodegenConfig({
75
76
  configFilePath,
76
77
  searchPlaces: [dirs.rootDirectory]
77
- }) || generateDefaultConfig(dirs);
78
+ }) || generateDefaultConfig(dirs, forceSfapiVersion);
78
79
  await addHooksToHydrogenOptions(codegenConfig, dirs);
79
80
  await generate(
80
81
  {
@@ -87,10 +88,7 @@ async function generateTypes({
87
88
  );
88
89
  return Object.keys(codegenConfig.generates);
89
90
  }
90
- function generateDefaultConfig({
91
- rootDirectory,
92
- appDirectory
93
- }) {
91
+ function generateDefaultConfig({ rootDirectory, appDirectory }, forceSfapiVersion) {
94
92
  const tsDefaultGlob = "*!(*.d).{ts,tsx}";
95
93
  const appDirRelative = relativePath(rootDirectory, appDirectory);
96
94
  return {
@@ -105,7 +103,22 @@ function generateDefaultConfig({
105
103
  documents: [
106
104
  tsDefaultGlob,
107
105
  joinPath(appDirRelative, "**", tsDefaultGlob)
108
- ]
106
+ ],
107
+ ...!!forceSfapiVersion && {
108
+ presetConfig: { importTypes: false },
109
+ schema: {
110
+ [`https://hydrogen-preview.myshopify.com/api/${forceSfapiVersion.split(":")[0]}/graphql.json`]: {
111
+ headers: {
112
+ "content-type": "application/json",
113
+ "X-Shopify-Storefront-Access-Token": forceSfapiVersion.split(":")[1] ?? "3b580e70970c4528da70c98e097c2fa0"
114
+ }
115
+ }
116
+ },
117
+ config: {
118
+ defaultScalarType: "string",
119
+ scalars: { JSON: "unknown" }
120
+ }
121
+ }
109
122
  }
110
123
  }
111
124
  }
@@ -1,10 +1,10 @@
1
1
  import { fileExists } from '@shopify/cli-kit/node/fs';
2
2
  import { resolvePath } from '@shopify/cli-kit/node/path';
3
- import { outputInfo, outputContent, outputToken } from '@shopify/cli-kit/node/output';
3
+ import { linesToColumns } from '@shopify/cli-kit/common/string';
4
+ import { outputInfo } from '@shopify/cli-kit/node/output';
4
5
  import { readAndParseDotEnv } from '@shopify/cli-kit/node/dot-env';
5
- import { colors } from './colors.js';
6
+ import colors from '@shopify/cli-kit/node/colors';
6
7
  import { pullRemoteEnvironmentVariables } from './pull-environment-variables.js';
7
- import { getConfig } from './shopify-config.js';
8
8
 
9
9
  async function combinedEnvironmentVariables({
10
10
  envBranch,
@@ -28,47 +28,30 @@ async function combinedEnvironmentVariables({
28
28
  );
29
29
  const localKeys = new Set(Object.keys(localEnvironmentVariables));
30
30
  if ([...remoteKeys, ...localKeys].length) {
31
- outputInfo(
32
- `${colors.bold("Injecting environment variables into MiniOxygen...")}`
33
- );
31
+ outputInfo("\nEnvironment variables injected into MiniOxygen:\n");
34
32
  }
35
- let storefrontTitle = "";
36
- if (remoteEnvironmentVariables.length) {
37
- const { storefront } = await getConfig(root);
38
- if (storefront) {
39
- storefrontTitle = storefront.title;
40
- }
41
- }
42
- remoteEnvironmentVariables.forEach(({ key, isSecret }) => {
43
- if (localKeys.has(key)) {
44
- outputIgnoringKey(key, `overwritten via ${colors.yellow(".env")}`);
45
- } else if (isSecret) {
46
- outputIgnoringKey(key, "value is marked as secret");
47
- } else {
48
- outputUsingKey(key, storefrontTitle);
33
+ let rows = [];
34
+ remoteEnvironmentVariables.filter(({ isSecret }) => !isSecret).forEach(({ key }) => {
35
+ if (!localKeys.has(key)) {
36
+ rows.push([key, "from Oxygen"]);
49
37
  }
50
38
  });
51
- [...localKeys].forEach((keyName) => {
52
- outputUsingKey(keyName, ".env");
39
+ localKeys.forEach((key) => {
40
+ rows.push([key, "from local .env"]);
41
+ });
42
+ remoteEnvironmentVariables.filter(({ isSecret }) => isSecret).forEach(({ key }) => {
43
+ if (!localKeys.has(key)) {
44
+ rows.push([
45
+ colors.dim(key),
46
+ colors.dim(`from Oxygen (Marked as secret)`)
47
+ ]);
48
+ }
53
49
  });
50
+ outputInfo(linesToColumns(rows));
54
51
  return {
55
52
  ...formattedRemoteVariables,
56
53
  ...localEnvironmentVariables
57
54
  };
58
55
  }
59
- function outputUsingKey(keyName, source) {
60
- outputInfo(
61
- outputContent` Using ${outputToken.green(
62
- keyName
63
- )} from ${outputToken.yellow(source)}`.value
64
- );
65
- }
66
- function outputIgnoringKey(keyName, reason) {
67
- outputInfo(
68
- outputContent`${colors.dim(
69
- ` Ignoring ${colors.green(keyName)} (${reason})`
70
- )}`.value
71
- );
72
- }
73
56
 
74
57
  export { combinedEnvironmentVariables };