@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.
- package/dist/commands/hydrogen/build.d.ts +1 -1
- package/dist/commands/hydrogen/build.js +24 -22
- 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/dev.d.ts +2 -1
- package/dist/commands/hydrogen/dev.js +85 -65
- 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 +3 -1
- package/dist/lib/config.js +67 -63
- package/dist/lib/flags.d.ts +1 -0
- package/dist/lib/flags.js +8 -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/log.d.ts +2 -1
- package/dist/lib/log.js +8 -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 +9 -8
- package/dist/lib/colors.d.ts +0 -11
- package/dist/lib/colors.js +0 -11
|
@@ -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
|
|
|
@@ -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 {
|
|
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-
|
|
24
|
+
vi.mock("../../lib/graphql/admin/link-storefront.js");
|
|
21
25
|
vi.mock("../../lib/shop.js", () => ({
|
|
22
|
-
getHydrogenShop: () =>
|
|
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(
|
|
32
|
-
|
|
33
|
-
|
|
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(
|
|
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(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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(
|
|
68
|
-
|
|
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(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
|
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 {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
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 {
|
|
8
|
-
import {
|
|
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
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
id: parseGid(id),
|
|
38
|
-
title,
|
|
39
|
-
productionUrl,
|
|
40
|
-
currentDeployment: formatDeployment(currentProductionDeployment)
|
|
41
|
-
})
|
|
28
|
+
pluralizedStorefronts({
|
|
29
|
+
storefronts,
|
|
30
|
+
shop
|
|
31
|
+
}).toString()
|
|
42
32
|
);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
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: "
|
|
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 };
|