@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.
- package/dist/commands/hydrogen/build.js +6 -5
- package/dist/commands/hydrogen/check.js +1 -1
- package/dist/commands/hydrogen/codegen-unstable.d.ts +1 -0
- package/dist/commands/hydrogen/codegen-unstable.js +6 -0
- package/dist/commands/hydrogen/env/list.d.ts +2 -3
- package/dist/commands/hydrogen/env/list.js +42 -44
- package/dist/commands/hydrogen/env/list.test.js +18 -24
- package/dist/commands/hydrogen/env/pull.d.ts +2 -3
- package/dist/commands/hydrogen/env/pull.js +42 -23
- package/dist/commands/hydrogen/env/pull.test.js +16 -4
- package/dist/commands/hydrogen/init.js +3 -13
- package/dist/commands/hydrogen/link.d.ts +0 -1
- package/dist/commands/hydrogen/link.js +34 -36
- package/dist/commands/hydrogen/link.test.js +43 -27
- package/dist/commands/hydrogen/list.d.ts +2 -2
- package/dist/commands/hydrogen/list.js +43 -39
- package/dist/commands/hydrogen/list.test.js +24 -32
- package/dist/commands/hydrogen/shortcut.js +6 -7
- package/dist/commands/hydrogen/shortcut.test.js +8 -9
- package/dist/commands/hydrogen/unlink.d.ts +0 -1
- package/dist/commands/hydrogen/unlink.js +5 -3
- package/dist/lib/admin-session.d.ts +1 -0
- package/dist/lib/codegen.d.ts +3 -2
- package/dist/lib/codegen.js +20 -7
- package/dist/lib/combined-environment-variables.js +19 -36
- package/dist/lib/combined-environment-variables.test.js +7 -7
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/config.js +67 -63
- package/dist/lib/flags.js +2 -3
- package/dist/lib/graphql/admin/link-storefront.d.ts +12 -9
- package/dist/lib/graphql/admin/link-storefront.js +18 -1
- package/dist/lib/graphql/admin/list-environments.d.ts +6 -5
- package/dist/lib/graphql/admin/list-environments.js +11 -1
- package/dist/lib/graphql/admin/list-storefronts.d.ts +13 -5
- package/dist/lib/graphql/admin/list-storefronts.js +18 -1
- package/dist/lib/graphql/admin/pull-variables.d.ts +6 -1
- package/dist/lib/graphql/admin/pull-variables.js +14 -1
- package/dist/lib/mini-oxygen.js +1 -1
- package/dist/lib/process.d.ts +6 -0
- package/dist/lib/process.js +17 -0
- package/dist/lib/pull-environment-variables.d.ts +1 -0
- package/dist/lib/pull-environment-variables.js +4 -14
- package/dist/lib/remix-version-interop.js +1 -1
- package/dist/lib/shell.d.ts +5 -6
- package/dist/lib/shell.js +65 -17
- package/dist/lib/shell.test.d.ts +1 -0
- package/dist/lib/shell.test.js +85 -0
- package/dist/lib/shopify-config.d.ts +1 -1
- package/dist/lib/shopify-config.js +2 -2
- package/dist/lib/transpile-ts.js +6 -0
- package/oclif.manifest.json +1 -1
- package/package.json +7 -6
- package/dist/lib/colors.d.ts +0 -11
- package/dist/lib/colors.js +0 -11
|
@@ -1,14 +1,14 @@
|
|
|
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
12
|
|
|
13
13
|
const LOG_WORKER_BUILT = "\u{1F4E6} Worker built";
|
|
14
14
|
class Build extends Command {
|
|
@@ -18,7 +18,8 @@ class Build extends Command {
|
|
|
18
18
|
sourcemap: Flags.boolean({
|
|
19
19
|
description: "Generate sourcemaps for the build.",
|
|
20
20
|
env: "SHOPIFY_HYDROGEN_FLAG_SOURCEMAP",
|
|
21
|
-
default: true
|
|
21
|
+
default: true,
|
|
22
|
+
allowNo: true
|
|
22
23
|
}),
|
|
23
24
|
["disable-route-warning"]: Flags.boolean({
|
|
24
25
|
description: "Disable warning about missing standard routes.",
|
|
@@ -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) {
|
|
@@ -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
|
|
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 {
|
|
18
|
+
export { EnvList as default, listEnvironments };
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
2
|
-
import { renderConfirmationPrompt
|
|
3
|
-
import {
|
|
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 {
|
|
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
|
|
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(
|
|
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
|
|
47
|
-
ListEnvironmentsQuery,
|
|
46
|
+
const { storefront } = await getStorefrontEnvironments(
|
|
48
47
|
adminSession,
|
|
49
|
-
|
|
50
|
-
id: configStorefront.id
|
|
51
|
-
}
|
|
48
|
+
configStorefront.id
|
|
52
49
|
);
|
|
53
|
-
|
|
54
|
-
if (!hydrogenStorefront) {
|
|
50
|
+
if (!storefront) {
|
|
55
51
|
renderMissingStorefront({ adminSession, storefront: configStorefront });
|
|
56
52
|
return;
|
|
57
53
|
}
|
|
58
|
-
const previewEnvironmentIndex =
|
|
54
|
+
const previewEnvironmentIndex = storefront.environments.findIndex(
|
|
59
55
|
(env) => env.type === "PREVIEW"
|
|
60
56
|
);
|
|
61
|
-
const previewEnvironment =
|
|
57
|
+
const previewEnvironment = storefront.environments.splice(
|
|
62
58
|
previewEnvironmentIndex,
|
|
63
59
|
1
|
|
64
60
|
);
|
|
65
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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 {
|
|
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 {
|
|
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",
|
|
25
|
-
|
|
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: () =>
|
|
28
|
+
getHydrogenShop: () => SHOP
|
|
33
29
|
}));
|
|
34
30
|
describe("listEnvironments", () => {
|
|
35
31
|
const ADMIN_SESSION = {
|
|
36
32
|
token: "abc123",
|
|
37
|
-
storeFqdn:
|
|
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(
|
|
72
|
-
|
|
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(
|
|
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
|
-
/
|
|
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(/
|
|
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(
|
|
141
|
-
|
|
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
|
|
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 {
|
|
22
|
+
export { EnvPull as default, pullVariables };
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
import { diffLines } from 'diff';
|
|
1
2
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
2
|
-
import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
|
|
3
|
-
import {
|
|
4
|
-
import { fileExists, writeFile } from '@shopify/cli-kit/node/fs';
|
|
3
|
+
import { renderInfo, renderConfirmationPrompt, renderWarning, renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
4
|
+
import { outputContent, outputToken } from '@shopify/cli-kit/node/output';
|
|
5
|
+
import { fileExists, readFile, writeFile } from '@shopify/cli-kit/node/fs';
|
|
5
6
|
import { resolvePath } from '@shopify/cli-kit/node/path';
|
|
7
|
+
import { patchEnvFile } from '@shopify/cli-kit/node/dot-env';
|
|
8
|
+
import colors from '@shopify/cli-kit/node/colors';
|
|
6
9
|
import { commonFlags, flagsToCamelObject } from '../../../lib/flags.js';
|
|
7
10
|
import { pullRemoteEnvironmentVariables } from '../../../lib/pull-environment-variables.js';
|
|
8
11
|
import { getConfig } from '../../../lib/shopify-config.js';
|
|
9
12
|
|
|
10
|
-
class
|
|
13
|
+
class EnvPull extends Command {
|
|
11
14
|
static description = "Populate your .env with variables from your Hydrogen storefront.";
|
|
12
|
-
static hidden = true;
|
|
13
15
|
static flags = {
|
|
14
16
|
["env-branch"]: commonFlags["env-branch"],
|
|
15
17
|
path: commonFlags.path,
|
|
@@ -17,7 +19,7 @@ class Pull extends Command {
|
|
|
17
19
|
force: commonFlags.force
|
|
18
20
|
};
|
|
19
21
|
async run() {
|
|
20
|
-
const { flags } = await this.parse(
|
|
22
|
+
const { flags } = await this.parse(EnvPull);
|
|
21
23
|
await pullVariables({ ...flagsToCamelObject(flags) });
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -36,33 +38,50 @@ async function pullVariables({
|
|
|
36
38
|
if (!environmentVariables.length) {
|
|
37
39
|
return;
|
|
38
40
|
}
|
|
41
|
+
const fileName = colors.whiteBright(`.env`);
|
|
39
42
|
const dotEnvPath = resolvePath(actualPath, ".env");
|
|
43
|
+
const fetchedEnv = {};
|
|
44
|
+
environmentVariables.forEach(({ isSecret, key, value }) => {
|
|
45
|
+
fetchedEnv[key] = isSecret ? `""` : value;
|
|
46
|
+
});
|
|
40
47
|
if (await fileExists(dotEnvPath) && !force) {
|
|
48
|
+
const existingEnv = await readFile(dotEnvPath);
|
|
49
|
+
const patchedEnv = patchEnvFile(existingEnv, fetchedEnv);
|
|
50
|
+
if (existingEnv === patchedEnv) {
|
|
51
|
+
renderInfo({
|
|
52
|
+
body: `No changes to your ${fileName} file`
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const diff = diffLines(existingEnv, patchedEnv);
|
|
41
57
|
const overwrite = await renderConfirmationPrompt({
|
|
42
|
-
|
|
58
|
+
confirmationMessage: `Yes, confirm changes`,
|
|
59
|
+
cancellationMessage: `No, make changes later`,
|
|
60
|
+
message: outputContent`We'll make the following changes to your .env file:
|
|
61
|
+
|
|
62
|
+
${outputToken.linesDiff(diff)}
|
|
63
|
+
Continue?`.value
|
|
43
64
|
});
|
|
44
65
|
if (!overwrite) {
|
|
45
66
|
return;
|
|
46
67
|
}
|
|
68
|
+
await writeFile(dotEnvPath, patchedEnv);
|
|
69
|
+
} else {
|
|
70
|
+
const newEnv = patchEnvFile(null, fetchedEnv);
|
|
71
|
+
await writeFile(dotEnvPath, newEnv);
|
|
47
72
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (isSecret) {
|
|
52
|
-
hasSecretVariables = true;
|
|
53
|
-
line = `# ${key} is marked as secret and its value is hidden
|
|
54
|
-
` + line;
|
|
55
|
-
}
|
|
56
|
-
return line;
|
|
57
|
-
}).join("\n") + "\n";
|
|
73
|
+
const hasSecretVariables = environmentVariables.some(
|
|
74
|
+
({ isSecret }) => isSecret
|
|
75
|
+
);
|
|
58
76
|
if (hasSecretVariables) {
|
|
59
77
|
const { storefront: configStorefront } = await getConfig(actualPath);
|
|
60
|
-
|
|
61
|
-
`${configStorefront.title} contains environment variables marked as secret, so their values weren\u2019t pulled.`
|
|
62
|
-
);
|
|
78
|
+
renderWarning({
|
|
79
|
+
body: `${configStorefront.title} contains environment variables marked as secret, so their values weren\u2019t pulled.`
|
|
80
|
+
});
|
|
63
81
|
}
|
|
64
|
-
|
|
65
|
-
|
|
82
|
+
renderSuccess({
|
|
83
|
+
body: ["Changes have been made to your", { filePath: fileName }, "file"]
|
|
84
|
+
});
|
|
66
85
|
}
|
|
67
86
|
|
|
68
|
-
export {
|
|
87
|
+
export { EnvPull as default, pullVariables };
|
|
@@ -69,7 +69,7 @@ describe("pullVariables", () => {
|
|
|
69
69
|
expect(await fileExists(filePath)).toBeFalsy();
|
|
70
70
|
await pullVariables({ path: tmpDir });
|
|
71
71
|
expect(await readFile(filePath)).toStrictEqual(
|
|
72
|
-
'PUBLIC_API_TOKEN=
|
|
72
|
+
'PUBLIC_API_TOKEN=abc123\nPRIVATE_API_TOKEN=""'
|
|
73
73
|
);
|
|
74
74
|
});
|
|
75
75
|
});
|
|
@@ -77,8 +77,18 @@ describe("pullVariables", () => {
|
|
|
77
77
|
await inTemporaryDirectory(async (tmpDir) => {
|
|
78
78
|
const outputMock = mockAndCaptureOutput();
|
|
79
79
|
await pullVariables({ path: tmpDir });
|
|
80
|
-
expect(outputMock.warn()).
|
|
81
|
-
|
|
80
|
+
expect(outputMock.warn()).toMatch(
|
|
81
|
+
/Existing Link contains environment variables marked as secret, so their/
|
|
82
|
+
);
|
|
83
|
+
expect(outputMock.warn()).toMatch(/values weren’t pulled./);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
it("renders a success message", async () => {
|
|
87
|
+
await inTemporaryDirectory(async (tmpDir) => {
|
|
88
|
+
const outputMock = mockAndCaptureOutput();
|
|
89
|
+
await pullVariables({ path: tmpDir });
|
|
90
|
+
expect(outputMock.info()).toMatch(
|
|
91
|
+
/Changes have been made to your \.env file/
|
|
82
92
|
);
|
|
83
93
|
});
|
|
84
94
|
});
|
|
@@ -92,8 +102,10 @@ describe("pullVariables", () => {
|
|
|
92
102
|
await writeFile(filePath, "EXISTING_TOKEN=1");
|
|
93
103
|
await pullVariables({ path: tmpDir });
|
|
94
104
|
expect(renderConfirmationPrompt).toHaveBeenCalledWith({
|
|
105
|
+
confirmationMessage: `Yes, confirm changes`,
|
|
106
|
+
cancellationMessage: `No, make changes later`,
|
|
95
107
|
message: expect.stringMatching(
|
|
96
|
-
/
|
|
108
|
+
/We'll make the following changes to your \.env file:/
|
|
97
109
|
)
|
|
98
110
|
});
|
|
99
111
|
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
2
|
+
import { readdir } from 'node:fs/promises';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
2
4
|
import { packageManagerUsedForCreating, installNodeModules } from '@shopify/cli-kit/node/node-package-manager';
|
|
3
5
|
import { renderFatalError, renderSelectPrompt, renderTextPrompt, renderConfirmationPrompt, renderInfo, renderTasks, renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
4
6
|
import { Flags } from '@oclif/core';
|
|
@@ -9,8 +11,7 @@ import { commonFlags, flagsToCamelObject, parseProcessFlags } from '../../lib/fl
|
|
|
9
11
|
import { transpileProject } from '../../lib/transpile-ts.js';
|
|
10
12
|
import { getLatestTemplates } from '../../lib/template-downloader.js';
|
|
11
13
|
import { checkHydrogenVersion } from '../../lib/check-version.js';
|
|
12
|
-
import {
|
|
13
|
-
import { fileURLToPath } from 'url';
|
|
14
|
+
import { supressNodeExperimentalWarnings } from '../../lib/process.js';
|
|
14
15
|
|
|
15
16
|
const STARTER_TEMPLATES = ["hello-world", "demo-store"];
|
|
16
17
|
const FLAG_MAP = { f: "force" };
|
|
@@ -167,16 +168,5 @@ async function runInit(options = parseProcessFlags(process.argv, FLAG_MAP)) {
|
|
|
167
168
|
async function projectExists(projectDir) {
|
|
168
169
|
return await fileExists(projectDir) && await isDirectory(projectDir) && (await readdir(projectDir)).length > 0;
|
|
169
170
|
}
|
|
170
|
-
function supressNodeExperimentalWarnings() {
|
|
171
|
-
const warningListener = process.listeners("warning")[0];
|
|
172
|
-
if (warningListener) {
|
|
173
|
-
process.removeAllListeners("warning");
|
|
174
|
-
process.prependListener("warning", (warning) => {
|
|
175
|
-
if (warning.name != "ExperimentalWarning") {
|
|
176
|
-
warningListener(warning);
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
171
|
|
|
182
172
|
export { Init as default, runInit };
|
|
@@ -3,7 +3,6 @@ import Command from '@shopify/cli-kit/node/base-command';
|
|
|
3
3
|
|
|
4
4
|
declare class Link extends Command {
|
|
5
5
|
static description: string;
|
|
6
|
-
static hidden: boolean;
|
|
7
6
|
static flags: {
|
|
8
7
|
force: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
|
|
9
8
|
path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import Command from '@shopify/cli-kit/node/base-command';
|
|
3
|
-
import { renderConfirmationPrompt, renderWarning, renderSelectPrompt } from '@shopify/cli-kit/node/ui';
|
|
4
|
-
import { outputContent, outputToken, outputSuccess, outputInfo } from '@shopify/cli-kit/node/output';
|
|
5
|
-
import { adminRequest, parseGid } from '../../lib/graphql.js';
|
|
3
|
+
import { renderConfirmationPrompt, renderWarning, renderSelectPrompt, renderSuccess } from '@shopify/cli-kit/node/ui';
|
|
6
4
|
import { commonFlags } from '../../lib/flags.js';
|
|
7
5
|
import { getHydrogenShop } from '../../lib/shop.js';
|
|
8
|
-
import {
|
|
9
|
-
import { hydrogenStorefrontUrl } from '../../lib/admin-urls.js';
|
|
10
|
-
import { LinkStorefrontQuery } from '../../lib/graphql/admin/link-storefront.js';
|
|
6
|
+
import { getStorefronts } from '../../lib/graphql/admin/link-storefront.js';
|
|
11
7
|
import { getConfig, setStorefront } from '../../lib/shopify-config.js';
|
|
12
8
|
import { logMissingStorefronts } from '../../lib/missing-storefronts.js';
|
|
9
|
+
import { getCliCommand } from '../../lib/shell.js';
|
|
13
10
|
|
|
14
11
|
class Link extends Command {
|
|
15
12
|
static description = "Link a local project to one of your shop's Hydrogen storefronts.";
|
|
16
|
-
static hidden = true;
|
|
17
13
|
static flags = {
|
|
18
14
|
force: commonFlags.force,
|
|
19
15
|
path: commonFlags.path,
|
|
20
16
|
shop: commonFlags.shop,
|
|
21
17
|
storefront: Flags.string({
|
|
22
|
-
char: "h",
|
|
23
18
|
description: `The name of a Hydrogen Storefront (e.g. "Jane's Apparel")`,
|
|
24
19
|
env: "SHOPIFY_HYDROGEN_STOREFRONT"
|
|
25
20
|
})
|
|
@@ -46,56 +41,59 @@ async function linkStorefront({
|
|
|
46
41
|
return;
|
|
47
42
|
}
|
|
48
43
|
}
|
|
49
|
-
const adminSession = await
|
|
50
|
-
|
|
51
|
-
LinkStorefrontQuery,
|
|
52
|
-
adminSession
|
|
53
|
-
);
|
|
54
|
-
if (!result.hydrogenStorefronts.length) {
|
|
44
|
+
const { storefronts, adminSession } = await getStorefronts(shop);
|
|
45
|
+
if (storefronts.length === 0) {
|
|
55
46
|
logMissingStorefronts(adminSession);
|
|
56
47
|
return;
|
|
57
48
|
}
|
|
58
49
|
let selectedStorefront;
|
|
50
|
+
const cliCommand = await getCliCommand();
|
|
59
51
|
if (flagStorefront) {
|
|
60
|
-
selectedStorefront =
|
|
61
|
-
(
|
|
52
|
+
selectedStorefront = storefronts.find(
|
|
53
|
+
({ title }) => title === flagStorefront
|
|
62
54
|
);
|
|
63
55
|
if (!selectedStorefront) {
|
|
64
56
|
renderWarning({
|
|
65
57
|
headline: `Couldn't find ${flagStorefront}`,
|
|
66
|
-
body:
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
body: [
|
|
59
|
+
"There's no storefront matching",
|
|
60
|
+
{ userInput: flagStorefront },
|
|
61
|
+
"on your",
|
|
62
|
+
{ userInput: shop },
|
|
63
|
+
"shop. To see all available Hydrogen storefronts, run",
|
|
64
|
+
{
|
|
65
|
+
command: `${cliCommand} list`
|
|
66
|
+
}
|
|
67
|
+
]
|
|
69
68
|
});
|
|
70
69
|
return;
|
|
71
70
|
}
|
|
72
71
|
} else {
|
|
73
|
-
const choices =
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
const choices = storefronts.map(({ id, title, productionUrl }) => ({
|
|
73
|
+
value: id,
|
|
74
|
+
label: `${title} (${productionUrl})`
|
|
76
75
|
}));
|
|
77
76
|
const storefrontId = await renderSelectPrompt({
|
|
78
|
-
message: "Choose a Hydrogen storefront to link
|
|
79
|
-
choices
|
|
80
|
-
defaultValue: "true"
|
|
77
|
+
message: "Choose a Hydrogen storefront to link",
|
|
78
|
+
choices
|
|
81
79
|
});
|
|
82
|
-
selectedStorefront =
|
|
83
|
-
(storefront) => storefront.id === storefrontId
|
|
84
|
-
);
|
|
80
|
+
selectedStorefront = storefronts.find(({ id }) => id === storefrontId);
|
|
85
81
|
}
|
|
86
82
|
if (!selectedStorefront) {
|
|
87
83
|
return;
|
|
88
84
|
}
|
|
89
85
|
await setStorefront(path ?? process.cwd(), selectedStorefront);
|
|
90
|
-
outputSuccess(`Linked to ${selectedStorefront.title}`);
|
|
91
86
|
if (!silent) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
87
|
+
renderSuccess({
|
|
88
|
+
body: [{ userInput: selectedStorefront.title }, "is now linked"],
|
|
89
|
+
nextSteps: [
|
|
90
|
+
[
|
|
91
|
+
"Run",
|
|
92
|
+
{ command: `${cliCommand} dev` },
|
|
93
|
+
"to start your local development server and start building"
|
|
94
|
+
]
|
|
95
|
+
]
|
|
96
|
+
});
|
|
99
97
|
}
|
|
100
98
|
}
|
|
101
99
|
|