@shopify/cli-hydrogen 7.1.2 → 8.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-vite.js +19 -10
- package/dist/commands/hydrogen/build.js +10 -2
- package/dist/commands/hydrogen/check.js +1 -0
- package/dist/commands/hydrogen/codegen.js +1 -0
- package/dist/commands/hydrogen/customer-account/push.js +170 -0
- package/dist/commands/hydrogen/debug/cpu.js +3 -0
- package/dist/commands/hydrogen/deploy.js +121 -36
- package/dist/commands/hydrogen/dev-vite.js +128 -59
- package/dist/commands/hydrogen/dev.js +108 -51
- package/dist/commands/hydrogen/env/list.js +7 -8
- package/dist/commands/hydrogen/env/pull.js +17 -1
- package/dist/commands/hydrogen/env/{push__unstable.js → push.js} +23 -50
- package/dist/commands/hydrogen/generate/route.js +1 -0
- package/dist/commands/hydrogen/init.js +45 -17
- package/dist/commands/hydrogen/link.js +20 -4
- package/dist/commands/hydrogen/list.js +1 -0
- package/dist/commands/hydrogen/login.js +1 -0
- package/dist/commands/hydrogen/logout.js +1 -0
- package/dist/commands/hydrogen/preview.js +31 -16
- package/dist/commands/hydrogen/setup/css.js +8 -1
- package/dist/commands/hydrogen/setup/markets.js +1 -0
- package/dist/commands/hydrogen/setup/vite.js +244 -138
- package/dist/commands/hydrogen/setup.js +21 -22
- package/dist/commands/hydrogen/shortcut.js +10 -0
- package/dist/commands/hydrogen/unlink.js +1 -0
- package/dist/commands/hydrogen/upgrade.js +2 -1
- package/dist/generator-templates/assets/vite/package.json +3 -4
- package/dist/generator-templates/assets/vite/vite.config.js +10 -2
- package/dist/generator-templates/starter/CHANGELOG.md +89 -0
- package/dist/generator-templates/starter/README.md +3 -44
- package/dist/generator-templates/starter/app/graphql/customer-account/CustomerDetailsQuery.ts +1 -0
- package/dist/generator-templates/starter/app/lib/fragments.ts +2 -0
- package/dist/generator-templates/starter/app/root.tsx +2 -5
- package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +1 -1
- package/dist/generator-templates/starter/app/routes/account.tsx +1 -1
- package/dist/generator-templates/starter/app/routes/collections.all.tsx +160 -0
- package/dist/generator-templates/starter/app/routes/products.$handle.tsx +1 -2
- package/dist/generator-templates/starter/customer-accountapi.generated.d.ts +6 -3
- package/dist/generator-templates/starter/{remix.env.d.ts → env.d.ts} +8 -2
- package/dist/generator-templates/starter/package.json +14 -9
- package/dist/generator-templates/starter/server.ts +2 -1
- package/dist/generator-templates/starter/storefrontapi.generated.d.ts +59 -3
- package/dist/generator-templates/starter/vite.config.ts +21 -0
- package/dist/{commands/hydrogen/init.d.ts → init.d.ts} +11 -3
- package/dist/lib/check-lockfile.js +12 -18
- package/dist/lib/codegen.js +37 -13
- package/dist/lib/common.js +50 -0
- package/dist/lib/cpu-profiler.js +4 -1
- package/dist/lib/dev-shared.js +97 -0
- package/dist/lib/environment-variables.js +51 -30
- package/dist/lib/file.js +8 -1
- package/dist/lib/flags.js +37 -16
- package/dist/lib/graphql/admin/customer-application-update.js +29 -0
- package/dist/lib/graphql/admin/get-oxygen-data.js +1 -0
- package/dist/lib/graphql/admin/list-environments.js +1 -0
- package/dist/lib/graphql/admin/pull-variables.js +4 -4
- package/dist/lib/graphql/admin/test-helper.js +37 -0
- package/dist/lib/log.js +86 -13
- package/dist/lib/mini-oxygen/common.js +19 -33
- package/dist/lib/mini-oxygen/index.js +6 -2
- package/dist/lib/mini-oxygen/node.js +43 -31
- package/dist/lib/mini-oxygen/workerd.js +72 -165
- package/dist/lib/missing-routes.js +1 -1
- package/dist/lib/onboarding/common.js +82 -70
- package/dist/lib/onboarding/local.js +19 -9
- package/dist/lib/onboarding/remote.js +35 -30
- package/dist/lib/package-managers.js +24 -0
- package/dist/lib/remix-config.js +17 -1
- package/dist/lib/request-events.js +6 -1
- package/dist/lib/setups/i18n/replacers.js +9 -6
- package/dist/lib/setups/routes/generate.js +1 -0
- package/dist/lib/shell.js +2 -1
- package/dist/lib/shopify-config.js +19 -1
- package/dist/lib/template-diff.js +36 -15
- package/dist/lib/template-downloader.js +35 -5
- package/dist/lib/transpile/morph/typedefs.js +5 -2
- package/dist/lib/transpile/project.js +8 -4
- package/dist/lib/tunneling.js +44 -0
- package/dist/lib/virtual-routes.js +1 -1
- package/dist/lib/vite-config.js +39 -9
- package/oclif.manifest.json +711 -498
- package/package.json +32 -24
- package/dist/commands/hydrogen/deploy.test.js +0 -553
- package/dist/commands/hydrogen/env/list.test.js +0 -148
- package/dist/commands/hydrogen/env/pull.test.js +0 -207
- package/dist/commands/hydrogen/env/push__unstable.test.js +0 -383
- package/dist/commands/hydrogen/generate/route.test.js +0 -43
- package/dist/commands/hydrogen/init.test.js +0 -641
- package/dist/commands/hydrogen/link.test.js +0 -187
- package/dist/commands/hydrogen/list.test.js +0 -111
- package/dist/commands/hydrogen/setup.test.js +0 -61
- package/dist/commands/hydrogen/shortcut.test.js +0 -30
- package/dist/commands/hydrogen/unlink.test.js +0 -36
- package/dist/commands/hydrogen/upgrade.test.js +0 -786
- package/dist/generator-templates/starter/remix.config.js +0 -24
- package/dist/lib/auth.test.js +0 -157
- package/dist/lib/check-lockfile.test.js +0 -81
- package/dist/lib/check-version.test.js +0 -86
- package/dist/lib/environment-variables.test.js +0 -149
- package/dist/lib/file.test.js +0 -68
- package/dist/lib/flags.test.js +0 -43
- package/dist/lib/get-oxygen-deployment-data.test.js +0 -120
- package/dist/lib/gid.test.js +0 -15
- package/dist/lib/graphql/admin/client.test.js +0 -76
- package/dist/lib/graphql/admin/create-storefront.test.js +0 -64
- package/dist/lib/graphql/admin/link-storefront.test.js +0 -38
- package/dist/lib/graphql/admin/list-environments.test.js +0 -44
- package/dist/lib/graphql/admin/list-storefronts.test.js +0 -44
- package/dist/lib/graphql/admin/pull-variables.test.js +0 -43
- package/dist/lib/graphql/business-platform/user-account.test.js +0 -80
- package/dist/lib/log.test.js +0 -92
- package/dist/lib/mini-oxygen/assets.js +0 -134
- package/dist/lib/mini-oxygen/mini-oxygen.test.js +0 -214
- package/dist/lib/mini-oxygen/workerd-inspector-logs.js +0 -227
- package/dist/lib/mini-oxygen/workerd-inspector-proxy.js +0 -200
- package/dist/lib/mini-oxygen/workerd-inspector.js +0 -219
- package/dist/lib/missing-routes.test.js +0 -45
- package/dist/lib/remix-version-check.test.js +0 -39
- package/dist/lib/remix-version-interop.test.js +0 -13
- package/dist/lib/setups/i18n/domains.test.js +0 -39
- package/dist/lib/setups/i18n/replacers.test.js +0 -261
- package/dist/lib/setups/i18n/subdomains.test.js +0 -39
- package/dist/lib/setups/i18n/subfolders.test.js +0 -39
- package/dist/lib/setups/routes/generate.test.js +0 -296
- package/dist/lib/shell.test.js +0 -111
- package/dist/lib/shopify-config.test.js +0 -199
- package/dist/lib/string.test.js +0 -16
- package/dist/lib/virtual-routes.test.js +0 -49
- package/dist/lib/vite/hydrogen-middleware.js +0 -82
- package/dist/lib/vite/mini-oxygen.js +0 -152
- package/dist/lib/vite/plugins.d.ts +0 -27
- package/dist/lib/vite/plugins.js +0 -139
- package/dist/lib/vite/shared.js +0 -10
- package/dist/lib/vite/utils.js +0 -55
- package/dist/lib/vite/worker-entry.js +0 -1518
- /package/dist/generator-templates/starter/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/dist/lib/shell.test.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
|
|
2
|
-
import { platform, userInfo } from 'node:os';
|
|
3
|
-
import { fileExists } from '@shopify/cli-kit/node/fs';
|
|
4
|
-
import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
|
|
5
|
-
import { shellWriteAlias, createPlatformShortcut, getCliCommand } from './shell.js';
|
|
6
|
-
import { execAsync } from './process.js';
|
|
7
|
-
|
|
8
|
-
vi.mock("node:os");
|
|
9
|
-
vi.mock("node:child_process");
|
|
10
|
-
vi.mock("@shopify/cli-kit/node/fs");
|
|
11
|
-
vi.mock("@shopify/cli-kit/node/node-package-manager");
|
|
12
|
-
vi.mock("./process.js", async () => {
|
|
13
|
-
const original = await vi.importActual(
|
|
14
|
-
"./process.js"
|
|
15
|
-
);
|
|
16
|
-
return {
|
|
17
|
-
...original,
|
|
18
|
-
execAsync: vi.fn()
|
|
19
|
-
};
|
|
20
|
-
});
|
|
21
|
-
vi.mocked(fileExists).mockResolvedValue(false);
|
|
22
|
-
vi.mocked(getPackageManager).mockResolvedValue("npm");
|
|
23
|
-
describe("shell", () => {
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
vi.resetAllMocks();
|
|
26
|
-
});
|
|
27
|
-
afterEach(() => {
|
|
28
|
-
delete process.env.MINGW_PREFIX;
|
|
29
|
-
});
|
|
30
|
-
describe("shellWriteAlias", () => {
|
|
31
|
-
["bash", "zsh", "fish"].forEach((shell) => {
|
|
32
|
-
const alias = "h2";
|
|
33
|
-
const command = "command";
|
|
34
|
-
it(`writes ${shell} alias to file`, async () => {
|
|
35
|
-
await expect(
|
|
36
|
-
shellWriteAlias(shell, alias, command)
|
|
37
|
-
).resolves.toBeTruthy();
|
|
38
|
-
expect(execAsync).toHaveBeenLastCalledWith(
|
|
39
|
-
expect.stringMatching(
|
|
40
|
-
new RegExp(
|
|
41
|
-
`printf "${command}" ${shell === "fish" ? ">" : ">>"} .*.${shell}`
|
|
42
|
-
)
|
|
43
|
-
)
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
it(`skips writing ${shell} alias when not supported`, async () => {
|
|
47
|
-
vi.mocked(execAsync).mockImplementation(
|
|
48
|
-
(shellCommand) => shellCommand.startsWith("which") ? Promise.reject(null) : Promise.resolve({ stdout: "stuff", stderr: "" })
|
|
49
|
-
);
|
|
50
|
-
await expect(
|
|
51
|
-
shellWriteAlias(shell, alias, command)
|
|
52
|
-
).resolves.toBeFalsy();
|
|
53
|
-
expect(execAsync).not.toHaveBeenLastCalledWith(
|
|
54
|
-
expect.stringMatching(/^printf/)
|
|
55
|
-
);
|
|
56
|
-
});
|
|
57
|
-
it(`skips writing ${shell} alias when already aliased`, async () => {
|
|
58
|
-
vi.mocked(fileExists).mockResolvedValue(true);
|
|
59
|
-
vi.mocked(execAsync).mockImplementation(
|
|
60
|
-
(shellCommand) => shellCommand.startsWith("which") || shellCommand.startsWith("grep") ? Promise.resolve({ stdout: "stuff", stderr: "" }) : Promise.reject(null)
|
|
61
|
-
);
|
|
62
|
-
await expect(
|
|
63
|
-
shellWriteAlias(shell, alias, command)
|
|
64
|
-
).resolves.toBeTruthy();
|
|
65
|
-
expect(execAsync).not.toHaveBeenLastCalledWith(
|
|
66
|
-
expect.stringMatching(/^printf/)
|
|
67
|
-
);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
describe("createPlatformShortcut", () => {
|
|
72
|
-
it("creates aliases for Unix", async () => {
|
|
73
|
-
vi.mocked(platform).mockReturnValue("darwin");
|
|
74
|
-
const result = await createPlatformShortcut();
|
|
75
|
-
expect(result).toEqual(expect.arrayContaining(["zsh", "bash", "fish"]));
|
|
76
|
-
});
|
|
77
|
-
it("creates aliases for Windows", async () => {
|
|
78
|
-
vi.mocked(platform).mockReturnValue("win32");
|
|
79
|
-
const result = await createPlatformShortcut();
|
|
80
|
-
expect(result).toEqual(
|
|
81
|
-
expect.arrayContaining(["PowerShell", "PowerShell 7+"])
|
|
82
|
-
);
|
|
83
|
-
});
|
|
84
|
-
it("creates aliases for Windows in Git Bash", async () => {
|
|
85
|
-
process.env.MINGW_PREFIX = "something";
|
|
86
|
-
vi.mocked(platform).mockReturnValue("win32");
|
|
87
|
-
const result = await createPlatformShortcut();
|
|
88
|
-
expect(result).toEqual(expect.arrayContaining(["bash"]));
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
describe("getCliCommand", () => {
|
|
92
|
-
it("returns the shortcut alias if available", async () => {
|
|
93
|
-
vi.mocked(userInfo).mockReturnValue({ shell: "/bin/bash" });
|
|
94
|
-
vi.mocked(execAsync).mockImplementation(
|
|
95
|
-
(shellCommand) => shellCommand.startsWith("grep") ? Promise.resolve({ stdout: "stuff", stderr: "" }) : Promise.reject(null)
|
|
96
|
-
);
|
|
97
|
-
await expect(getCliCommand()).resolves.toEqual("h2");
|
|
98
|
-
});
|
|
99
|
-
it("returns the used package manager command", async () => {
|
|
100
|
-
vi.mocked(execAsync).mockImplementation(() => Promise.reject(null));
|
|
101
|
-
vi.mocked(getPackageManager).mockRejectedValueOnce(null);
|
|
102
|
-
await expect(getCliCommand()).resolves.toEqual("npx shopify hydrogen");
|
|
103
|
-
vi.mocked(getPackageManager).mockResolvedValue("npm");
|
|
104
|
-
await expect(getCliCommand()).resolves.toEqual("npx shopify hydrogen");
|
|
105
|
-
vi.mocked(getPackageManager).mockResolvedValue("yarn");
|
|
106
|
-
await expect(getCliCommand()).resolves.toEqual("yarn shopify hydrogen");
|
|
107
|
-
vi.mocked(getPackageManager).mockResolvedValue("pnpm");
|
|
108
|
-
await expect(getCliCommand()).resolves.toEqual("pnpm shopify hydrogen");
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
});
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { inTemporaryDirectory, mkdir, writeFile, fileExists, readFile } from '@shopify/cli-kit/node/fs';
|
|
3
|
-
import { joinPath, dirname } from '@shopify/cli-kit/node/path';
|
|
4
|
-
import { getConfig, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT, resetConfig, setUserAccount, setStorefront, unsetStorefront, ensureShopifyGitIgnore } from './shopify-config.js';
|
|
5
|
-
|
|
6
|
-
async function writeExistingConfig(dir, config) {
|
|
7
|
-
const existingConfig = config ?? {
|
|
8
|
-
shop: "previous-shop",
|
|
9
|
-
shopName: "Previous Shop",
|
|
10
|
-
email: "email",
|
|
11
|
-
storefront: {
|
|
12
|
-
id: "gid://shopify/HydrogenStorefront/1",
|
|
13
|
-
title: "Hydrogen"
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
const filePath = joinPath(dir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
17
|
-
await mkdir(dirname(filePath));
|
|
18
|
-
await writeFile(filePath, JSON.stringify(existingConfig));
|
|
19
|
-
expect(JSON.parse(await readFile(filePath))).toStrictEqual(existingConfig);
|
|
20
|
-
return { existingConfig, filePath };
|
|
21
|
-
}
|
|
22
|
-
describe("getConfig()", () => {
|
|
23
|
-
describe("when no config exists", () => {
|
|
24
|
-
it("returns an empty object", async () => {
|
|
25
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
26
|
-
const config = await getConfig(tmpDir);
|
|
27
|
-
expect(config).toStrictEqual({});
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe("when a config exists", () => {
|
|
32
|
-
it("returns the config", async () => {
|
|
33
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
34
|
-
const existingConfig = {
|
|
35
|
-
shop: "my-shop",
|
|
36
|
-
shopName: "My Shop",
|
|
37
|
-
email: "email"
|
|
38
|
-
};
|
|
39
|
-
const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
40
|
-
await mkdir(dirname(filePath));
|
|
41
|
-
await writeFile(filePath, JSON.stringify(existingConfig));
|
|
42
|
-
const config = await getConfig(tmpDir);
|
|
43
|
-
expect(config).toStrictEqual(existingConfig);
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
describe("resetConfig()", () => {
|
|
49
|
-
it("writes an empty object", async () => {
|
|
50
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
51
|
-
await writeExistingConfig(tmpDir);
|
|
52
|
-
await resetConfig(tmpDir);
|
|
53
|
-
const config = await getConfig(tmpDir);
|
|
54
|
-
expect(config).toStrictEqual({});
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
describe("setUserAccount()", () => {
|
|
59
|
-
describe("when no config exists", () => {
|
|
60
|
-
it("creates a new config file", async () => {
|
|
61
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
62
|
-
const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
63
|
-
expect(await fileExists(filePath)).toBeFalsy();
|
|
64
|
-
await setUserAccount(tmpDir, {
|
|
65
|
-
shop: "new-shop",
|
|
66
|
-
shopName: "New Shop",
|
|
67
|
-
email: "email"
|
|
68
|
-
});
|
|
69
|
-
expect(await fileExists(filePath)).toBeTruthy();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
it("returns the new config", async () => {
|
|
73
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
74
|
-
const newConfig = {
|
|
75
|
-
shop: "new-shop",
|
|
76
|
-
email: "email",
|
|
77
|
-
shopName: "New Shop"
|
|
78
|
-
};
|
|
79
|
-
const config = await setUserAccount(tmpDir, newConfig);
|
|
80
|
-
expect(config).toStrictEqual(newConfig);
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
describe("when a config exists", () => {
|
|
85
|
-
it("updates the config file", async () => {
|
|
86
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
87
|
-
const { existingConfig, filePath } = await writeExistingConfig(tmpDir);
|
|
88
|
-
const newConfig = {
|
|
89
|
-
shop: "new-shop",
|
|
90
|
-
shopName: "New Shop",
|
|
91
|
-
email: "email"
|
|
92
|
-
};
|
|
93
|
-
await setUserAccount(tmpDir, newConfig);
|
|
94
|
-
expect(JSON.parse(await readFile(filePath))).toStrictEqual({
|
|
95
|
-
...existingConfig,
|
|
96
|
-
...newConfig
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
it("returns the new config", async () => {
|
|
101
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
102
|
-
const { existingConfig } = await writeExistingConfig(tmpDir);
|
|
103
|
-
const newConfig = {
|
|
104
|
-
shop: "new-shop",
|
|
105
|
-
email: "email",
|
|
106
|
-
shopName: "New Shop"
|
|
107
|
-
};
|
|
108
|
-
const config = await setUserAccount(tmpDir, newConfig);
|
|
109
|
-
expect(config).toStrictEqual({
|
|
110
|
-
...existingConfig,
|
|
111
|
-
...newConfig
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
describe("setStorefront()", () => {
|
|
118
|
-
it("updates the config file", async () => {
|
|
119
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
120
|
-
const { existingConfig, filePath } = await writeExistingConfig(tmpDir);
|
|
121
|
-
const newStorefront = {
|
|
122
|
-
id: "gid://shopify/HydrogenStorefront/2",
|
|
123
|
-
title: "Remix"
|
|
124
|
-
};
|
|
125
|
-
await setStorefront(tmpDir, newStorefront);
|
|
126
|
-
expect(JSON.parse(await readFile(filePath))).toStrictEqual({
|
|
127
|
-
...existingConfig,
|
|
128
|
-
storefront: newStorefront
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
it("returns the new config", async () => {
|
|
133
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
134
|
-
const { existingConfig } = await writeExistingConfig(tmpDir);
|
|
135
|
-
const newStorefront = {
|
|
136
|
-
id: "gid://shopify/HydrogenStorefront/2",
|
|
137
|
-
title: "Remix"
|
|
138
|
-
};
|
|
139
|
-
const config = await setStorefront(tmpDir, newStorefront);
|
|
140
|
-
expect(config).toStrictEqual({
|
|
141
|
-
...existingConfig,
|
|
142
|
-
storefront: newStorefront
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
describe("unsetStorefront()", () => {
|
|
148
|
-
it("removes the storefront configuration and returns the config", async () => {
|
|
149
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
150
|
-
const { filePath, existingConfig } = await writeExistingConfig(tmpDir);
|
|
151
|
-
const config = await unsetStorefront(tmpDir);
|
|
152
|
-
expect(config).toStrictEqual({
|
|
153
|
-
...existingConfig,
|
|
154
|
-
storefront: void 0
|
|
155
|
-
});
|
|
156
|
-
const { storefront, ...actualConfig } = existingConfig;
|
|
157
|
-
expect(JSON.parse(await readFile(filePath))).toStrictEqual(actualConfig);
|
|
158
|
-
});
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
describe("ensureShopifyGitIgnore()", () => {
|
|
162
|
-
describe("when a .gitignore file already exists", () => {
|
|
163
|
-
it("updates the .gitignore file and returns true", async () => {
|
|
164
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
165
|
-
const existingFileContents = "node_modules\r\n";
|
|
166
|
-
const filePath = joinPath(tmpDir, ".gitignore");
|
|
167
|
-
await writeFile(filePath, JSON.stringify(existingFileContents));
|
|
168
|
-
expect(await readFile(filePath)).not.toContain(".shopify");
|
|
169
|
-
const result = await ensureShopifyGitIgnore(tmpDir);
|
|
170
|
-
expect(await readFile(filePath)).toContain(".shopify");
|
|
171
|
-
expect(result).toBeTruthy();
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
describe("and the file is already ignoring .shopify", () => {
|
|
175
|
-
it("does not update the file and returns false", async () => {
|
|
176
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
177
|
-
const existingFileContents = "node_modules\n.shopify\r\n";
|
|
178
|
-
const filePath = joinPath(tmpDir, ".gitignore");
|
|
179
|
-
await writeFile(filePath, JSON.stringify(existingFileContents));
|
|
180
|
-
const originalFile = await readFile(filePath);
|
|
181
|
-
const result = await ensureShopifyGitIgnore(tmpDir);
|
|
182
|
-
expect(await readFile(filePath)).toStrictEqual(originalFile);
|
|
183
|
-
expect(result).toBeFalsy();
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
describe("when a .gitignore does not exist", () => {
|
|
189
|
-
it("creates the .gitignore file and returns true", async () => {
|
|
190
|
-
await inTemporaryDirectory(async (tmpDir) => {
|
|
191
|
-
const filePath = joinPath(tmpDir, ".gitignore");
|
|
192
|
-
expect(await fileExists(filePath)).toBeFalsy();
|
|
193
|
-
const result = await ensureShopifyGitIgnore(tmpDir);
|
|
194
|
-
expect(await readFile(filePath)).toContain(".shopify");
|
|
195
|
-
expect(result).toBeTruthy();
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
});
|
package/dist/lib/string.test.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { titleize } from './string.js';
|
|
3
|
-
|
|
4
|
-
describe("titleize", () => {
|
|
5
|
-
const TEST_DIRECTORY_NAMES = {
|
|
6
|
-
"demo-storefront": "Demo Storefront",
|
|
7
|
-
"nifty \u{1F602} project ": "Nifty Project",
|
|
8
|
-
"Hello \u{1F602}": "Hello",
|
|
9
|
-
_____: ""
|
|
10
|
-
};
|
|
11
|
-
it("replaces non-alpha-numeric characters with spaces and capitalizes the first letter of every word", () => {
|
|
12
|
-
for (const [input, expected] of Object.entries(TEST_DIRECTORY_NAMES)) {
|
|
13
|
-
expect(titleize(input)).toBe(expected);
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { fileURLToPath } from 'url';
|
|
3
|
-
import { addVirtualRoutes, VIRTUAL_ROOT, VIRTUAL_ROUTES_DIR } from './virtual-routes.js';
|
|
4
|
-
|
|
5
|
-
describe("virtual routes", () => {
|
|
6
|
-
it("adds virtual routes", async () => {
|
|
7
|
-
const config = {
|
|
8
|
-
appDirectory: fileURLToPath(new URL("../virtual-test", import.meta.url)),
|
|
9
|
-
routes: {}
|
|
10
|
-
};
|
|
11
|
-
await addVirtualRoutes(config);
|
|
12
|
-
expect(config.routes[VIRTUAL_ROOT]).toMatchObject({
|
|
13
|
-
path: "",
|
|
14
|
-
id: VIRTUAL_ROOT,
|
|
15
|
-
file: "../virtual-routes/virtual-root.jsx"
|
|
16
|
-
});
|
|
17
|
-
expect(config.routes[VIRTUAL_ROUTES_DIR + "/index"]).toMatchObject({
|
|
18
|
-
parentId: VIRTUAL_ROOT,
|
|
19
|
-
path: void 0,
|
|
20
|
-
file: "../" + VIRTUAL_ROUTES_DIR + "/index.tsx"
|
|
21
|
-
});
|
|
22
|
-
expect(config.routes[VIRTUAL_ROUTES_DIR + "/graphiql"]).toMatchObject({
|
|
23
|
-
parentId: VIRTUAL_ROOT,
|
|
24
|
-
path: "graphiql",
|
|
25
|
-
file: "../" + VIRTUAL_ROUTES_DIR + "/graphiql.tsx"
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
it("skips existing routes", async () => {
|
|
29
|
-
const existingIndexRoute = {
|
|
30
|
-
id: "routes/index",
|
|
31
|
-
index: true,
|
|
32
|
-
parentId: "root",
|
|
33
|
-
path: void 0,
|
|
34
|
-
file: "user-app/routes/index.tsx"
|
|
35
|
-
};
|
|
36
|
-
const config = {
|
|
37
|
-
appDirectory: fileURLToPath(new URL("../virtual-test", import.meta.url)),
|
|
38
|
-
routes: {
|
|
39
|
-
[existingIndexRoute.id]: existingIndexRoute
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
await addVirtualRoutes(config);
|
|
43
|
-
expect(config.routes[existingIndexRoute.id]).toMatchObject(
|
|
44
|
-
existingIndexRoute
|
|
45
|
-
);
|
|
46
|
-
expect(config.routes[VIRTUAL_ROUTES_DIR + "/index"]).toBeFalsy();
|
|
47
|
-
expect(Object.values(config.routes).length).toBeGreaterThan(2);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { normalizePath } from 'vite';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { createRequire } from 'node:module';
|
|
4
|
-
import { createFileReadStream } from '@shopify/cli-kit/node/fs';
|
|
5
|
-
import { setConstructors, handleDebugNetworkRequest } from '../request-events.js';
|
|
6
|
-
import { SUBREQUEST_PROFILER_ENDPOINT } from '../mini-oxygen/common.js';
|
|
7
|
-
import { toWeb, pipeFromWeb } from './utils.js';
|
|
8
|
-
import { addVirtualRoutes } from '../virtual-routes.js';
|
|
9
|
-
|
|
10
|
-
function setupRemixDevServerHooks(viteUrl) {
|
|
11
|
-
globalThis["__remix_devServerHooks"] = {
|
|
12
|
-
getCriticalCss: (...args) => fetch(new URL("/__vite_critical_css", viteUrl), {
|
|
13
|
-
method: "POST",
|
|
14
|
-
body: JSON.stringify(args)
|
|
15
|
-
}).then((res) => res.json())
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
function setupHydrogenMiddleware(viteDevServer, options) {
|
|
19
|
-
viteDevServer.middlewares.use(
|
|
20
|
-
"/__vite_critical_css",
|
|
21
|
-
function h2HandleCriticalCss(req, res) {
|
|
22
|
-
toWeb(req).json().then(async (args) => {
|
|
23
|
-
const result = await globalThis["__remix_devServerHooks"]?.getCriticalCss?.(...args);
|
|
24
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
25
|
-
res.end(JSON.stringify(result ?? ""));
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
);
|
|
29
|
-
if (options.disableVirtualRoutes)
|
|
30
|
-
return;
|
|
31
|
-
addVirtualRoutesToRemix(viteDevServer);
|
|
32
|
-
setConstructors({ Response: globalThis.Response });
|
|
33
|
-
viteDevServer.middlewares.use(
|
|
34
|
-
SUBREQUEST_PROFILER_ENDPOINT,
|
|
35
|
-
function h2HandleSubrequestProfilerEvent(req, res) {
|
|
36
|
-
const webResponse = handleDebugNetworkRequest(toWeb(req));
|
|
37
|
-
pipeFromWeb(webResponse, res);
|
|
38
|
-
}
|
|
39
|
-
);
|
|
40
|
-
viteDevServer.middlewares.use(
|
|
41
|
-
"/graphiql/customer-account.schema.json",
|
|
42
|
-
function h2HandleGraphiQLCustomerSchema(req, res) {
|
|
43
|
-
const require2 = createRequire(import.meta.url);
|
|
44
|
-
const filePath = require2.resolve(
|
|
45
|
-
"@shopify/hydrogen/customer-account.schema.json"
|
|
46
|
-
);
|
|
47
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
48
|
-
createFileReadStream(filePath).pipe(res);
|
|
49
|
-
}
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
let virtualRoutesAdded = false;
|
|
53
|
-
async function addVirtualRoutesToRemix(viteDevServer) {
|
|
54
|
-
if (virtualRoutesAdded)
|
|
55
|
-
return;
|
|
56
|
-
const appDirectory = await reloadRemixVirtualRoutes(viteDevServer.config);
|
|
57
|
-
viteDevServer.watcher.on("all", (eventName, filepath) => {
|
|
58
|
-
const appFileAddedOrRemoved = (eventName === "add" || eventName === "unlink") && normalizePath(filepath).startsWith(normalizePath(appDirectory));
|
|
59
|
-
const viteConfigChanged = eventName === "change" && normalizePath(filepath) === normalizePath(viteDevServer.config.configFile ?? "");
|
|
60
|
-
if (appFileAddedOrRemoved || viteConfigChanged) {
|
|
61
|
-
setTimeout(() => reloadRemixVirtualRoutes(viteDevServer.config), 100);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
virtualRoutesAdded = true;
|
|
65
|
-
}
|
|
66
|
-
async function reloadRemixVirtualRoutes(config) {
|
|
67
|
-
const remixPluginContext = config.__remixPluginContext;
|
|
68
|
-
remixPluginContext.remixConfig = { ...remixPluginContext.remixConfig };
|
|
69
|
-
remixPluginContext.remixConfig.routes = {
|
|
70
|
-
...remixPluginContext.remixConfig.routes
|
|
71
|
-
};
|
|
72
|
-
await addVirtualRoutes(remixPluginContext.remixConfig).catch((error) => {
|
|
73
|
-
console.debug(
|
|
74
|
-
"Could not add virtual routes: " + (error?.stack ?? error?.message ?? error)
|
|
75
|
-
);
|
|
76
|
-
});
|
|
77
|
-
Object.freeze(remixPluginContext.remixConfig.routes);
|
|
78
|
-
Object.freeze(remixPluginContext.remixConfig);
|
|
79
|
-
return remixPluginContext?.remixConfig?.appDirectory ?? path.join(config.root, "app");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export { setupHydrogenMiddleware, setupRemixDevServerHooks };
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { fetchModule } from 'vite';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
import crypto from 'node:crypto';
|
|
4
|
-
import { Miniflare, NoOpLog } from 'miniflare';
|
|
5
|
-
import { OXYGEN_HEADERS_MAP, logRequestLine } from '../mini-oxygen/common.js';
|
|
6
|
-
import { PRIVATE_WORKERD_INSPECTOR_PORT, OXYGEN_WORKERD_COMPAT_PARAMS } from '../mini-oxygen/workerd.js';
|
|
7
|
-
import { findPort } from '../find-port.js';
|
|
8
|
-
import { createInspectorConnector } from '../mini-oxygen/workerd-inspector.js';
|
|
9
|
-
import { getHmrUrl, toURL, toWeb, pipeFromWeb } from './utils.js';
|
|
10
|
-
|
|
11
|
-
const scriptPath = fileURLToPath(new URL("./worker-entry.js", import.meta.url));
|
|
12
|
-
const FETCH_MODULE_PATHNAME = "/__vite_fetch_module";
|
|
13
|
-
const WARMUP_PATHNAME = "/__vite_warmup";
|
|
14
|
-
const oxygenHeadersMap = Object.values(OXYGEN_HEADERS_MAP).reduce(
|
|
15
|
-
(acc, item) => {
|
|
16
|
-
acc[item.name] = item.defaultValue;
|
|
17
|
-
return acc;
|
|
18
|
-
},
|
|
19
|
-
{}
|
|
20
|
-
);
|
|
21
|
-
async function startMiniOxygenRuntime({
|
|
22
|
-
viteDevServer,
|
|
23
|
-
env,
|
|
24
|
-
services,
|
|
25
|
-
debug = false,
|
|
26
|
-
inspectorPort,
|
|
27
|
-
workerEntryFile,
|
|
28
|
-
setupScripts
|
|
29
|
-
}) {
|
|
30
|
-
const [publicInspectorPort, privateInspectorPort] = await Promise.all([
|
|
31
|
-
findPort(inspectorPort),
|
|
32
|
-
findPort(PRIVATE_WORKERD_INSPECTOR_PORT)
|
|
33
|
-
]);
|
|
34
|
-
const mf = new Miniflare({
|
|
35
|
-
cf: false,
|
|
36
|
-
verbose: false,
|
|
37
|
-
log: new NoOpLog(),
|
|
38
|
-
inspectorPort: privateInspectorPort,
|
|
39
|
-
handleRuntimeStdio(stdout, stderr) {
|
|
40
|
-
stdout.destroy();
|
|
41
|
-
stderr.destroy();
|
|
42
|
-
},
|
|
43
|
-
workers: [
|
|
44
|
-
{
|
|
45
|
-
name: "oxygen",
|
|
46
|
-
modulesRoot: "/",
|
|
47
|
-
modules: [{ type: "ESModule", path: scriptPath }],
|
|
48
|
-
...OXYGEN_WORKERD_COMPAT_PARAMS,
|
|
49
|
-
serviceBindings: { ...services },
|
|
50
|
-
bindings: {
|
|
51
|
-
...env,
|
|
52
|
-
__VITE_ROOT: viteDevServer.config.root,
|
|
53
|
-
__VITE_RUNTIME_EXECUTE_URL: workerEntryFile,
|
|
54
|
-
__VITE_FETCH_MODULE_PATHNAME: FETCH_MODULE_PATHNAME,
|
|
55
|
-
__VITE_HMR_URL: getHmrUrl(viteDevServer),
|
|
56
|
-
__VITE_WARMUP_PATHNAME: WARMUP_PATHNAME
|
|
57
|
-
},
|
|
58
|
-
unsafeEvalBinding: "__VITE_UNSAFE_EVAL",
|
|
59
|
-
wrappedBindings: {
|
|
60
|
-
__VITE_SETUP_ENV: "setup-environment"
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
name: "setup-environment",
|
|
65
|
-
modules: true,
|
|
66
|
-
scriptPath,
|
|
67
|
-
script: `
|
|
68
|
-
const setupScripts = [${setupScripts ?? ""}];
|
|
69
|
-
export default (env) => (request) => {
|
|
70
|
-
const viteUrl = new URL(request.url).origin;
|
|
71
|
-
setupScripts.forEach((setup) => setup?.(viteUrl));
|
|
72
|
-
setupScripts.length = 0;
|
|
73
|
-
}`
|
|
74
|
-
}
|
|
75
|
-
]
|
|
76
|
-
});
|
|
77
|
-
const warmupWorkerdCache = () => {
|
|
78
|
-
let viteUrl = viteDevServer.resolvedUrls?.local[0] ?? viteDevServer.resolvedUrls?.network[0];
|
|
79
|
-
if (!viteUrl) {
|
|
80
|
-
const address = viteDevServer.httpServer?.address?.();
|
|
81
|
-
viteUrl = address && typeof address !== "string" ? `http://localhost:${address.port}` : address ?? void 0;
|
|
82
|
-
}
|
|
83
|
-
if (viteUrl) {
|
|
84
|
-
mf.dispatchFetch(new URL(WARMUP_PATHNAME, viteUrl)).catch(() => {
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
viteDevServer.httpServer?.listening ? warmupWorkerdCache() : viteDevServer.httpServer?.once("listening", warmupWorkerdCache);
|
|
89
|
-
mf.ready.then(() => {
|
|
90
|
-
const reconnect = createInspectorConnector({
|
|
91
|
-
debug,
|
|
92
|
-
sourceMapPath: "",
|
|
93
|
-
absoluteBundlePath: "",
|
|
94
|
-
privateInspectorPort,
|
|
95
|
-
publicInspectorPort
|
|
96
|
-
});
|
|
97
|
-
return reconnect();
|
|
98
|
-
});
|
|
99
|
-
return {
|
|
100
|
-
ready: mf.ready,
|
|
101
|
-
publicInspectorPort,
|
|
102
|
-
dispatch: (webRequest) => mf.dispatchFetch(webRequest),
|
|
103
|
-
async dispose() {
|
|
104
|
-
await mf.dispose();
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
function setupOxygenMiddleware(viteDevServer, dispatchFetch) {
|
|
109
|
-
viteDevServer.middlewares.use(
|
|
110
|
-
FETCH_MODULE_PATHNAME,
|
|
111
|
-
function o2HandleModuleFetch(req, res) {
|
|
112
|
-
const url = toURL(req);
|
|
113
|
-
const id = url.searchParams.get("id");
|
|
114
|
-
const importer = url.searchParams.get("importer") ?? void 0;
|
|
115
|
-
if (id) {
|
|
116
|
-
res.setHeader("cache-control", "no-store");
|
|
117
|
-
res.setHeader("content-type", "application/json");
|
|
118
|
-
fetchModule(viteDevServer, id, importer).then((ssrModule) => res.end(JSON.stringify(ssrModule))).catch((error) => {
|
|
119
|
-
console.error("Error during module fetch:", error);
|
|
120
|
-
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
121
|
-
res.end("Internal server error");
|
|
122
|
-
});
|
|
123
|
-
} else {
|
|
124
|
-
res.statusCode = 400;
|
|
125
|
-
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
126
|
-
res.end("Invalid request");
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
);
|
|
130
|
-
viteDevServer.middlewares.use(function o2HandleWorkerRequest(req, res) {
|
|
131
|
-
if (!req.headers.host)
|
|
132
|
-
throw new Error("Missing host header");
|
|
133
|
-
const webRequest = toWeb(req, {
|
|
134
|
-
"request-id": crypto.randomUUID(),
|
|
135
|
-
...oxygenHeadersMap
|
|
136
|
-
});
|
|
137
|
-
const startTimeMs = Date.now();
|
|
138
|
-
dispatchFetch(webRequest).then((webResponse) => {
|
|
139
|
-
pipeFromWeb(webResponse, res);
|
|
140
|
-
logRequestLine(webRequest, {
|
|
141
|
-
responseStatus: webResponse.status,
|
|
142
|
-
durationMs: Date.now() - startTimeMs
|
|
143
|
-
});
|
|
144
|
-
}).catch((error) => {
|
|
145
|
-
console.error("Error during evaluation:", error);
|
|
146
|
-
res.writeHead(500);
|
|
147
|
-
res.end();
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export { setupOxygenMiddleware, startMiniOxygenRuntime };
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Plugin } from 'vite';
|
|
2
|
-
|
|
3
|
-
type HydrogenPluginOptions = {
|
|
4
|
-
disableVirtualRoutes?: boolean;
|
|
5
|
-
};
|
|
6
|
-
type OxygenPluginOptions = {
|
|
7
|
-
ssrEntry?: string;
|
|
8
|
-
debug?: boolean;
|
|
9
|
-
inspectorPort?: number;
|
|
10
|
-
env?: Record<string, any>;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Enables Hydrogen utilities for local development
|
|
15
|
-
* such as GraphiQL, Subrequest Profiler, etc.
|
|
16
|
-
* It must be used in combination with the `oxygen` plugin and Hydrogen CLI.
|
|
17
|
-
* @experimental
|
|
18
|
-
*/
|
|
19
|
-
declare function hydrogen(pluginOptions?: HydrogenPluginOptions): Plugin[];
|
|
20
|
-
/**
|
|
21
|
-
* Runs backend code in an Oxygen worker instead of Node.js during development.
|
|
22
|
-
* It must be placed after `hydrogen` but before `remix` in the Vite plugins list.
|
|
23
|
-
* @experimental
|
|
24
|
-
*/
|
|
25
|
-
declare function oxygen(pluginOptions?: OxygenPluginOptions): Plugin[];
|
|
26
|
-
|
|
27
|
-
export { hydrogen, oxygen };
|