@shopify/cli-hydrogen 5.0.2 → 5.1.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 +16 -2
- package/dist/commands/hydrogen/codegen-unstable.js +13 -24
- package/dist/commands/hydrogen/dev.js +45 -39
- package/dist/commands/hydrogen/env/list.js +25 -24
- package/dist/commands/hydrogen/env/list.test.js +46 -43
- package/dist/commands/hydrogen/env/pull.js +53 -25
- package/dist/commands/hydrogen/env/pull.test.js +123 -42
- package/dist/commands/hydrogen/generate/route.js +31 -132
- package/dist/commands/hydrogen/generate/route.test.js +34 -126
- package/dist/commands/hydrogen/init.js +46 -127
- package/dist/commands/hydrogen/init.test.js +352 -100
- package/dist/commands/hydrogen/link.js +70 -69
- package/dist/commands/hydrogen/link.test.js +72 -107
- package/dist/commands/hydrogen/list.js +22 -12
- package/dist/commands/hydrogen/list.test.js +51 -48
- package/dist/commands/hydrogen/login.js +31 -0
- package/dist/commands/hydrogen/logout.js +21 -0
- package/dist/commands/hydrogen/setup/css.js +79 -0
- package/dist/commands/hydrogen/setup/markets.js +53 -0
- package/dist/commands/hydrogen/setup.js +133 -0
- package/dist/commands/hydrogen/shortcut.js +2 -45
- package/dist/commands/hydrogen/shortcut.test.js +10 -37
- package/dist/generator-templates/assets/css-modules/package.json +6 -0
- package/dist/generator-templates/assets/postcss/package.json +10 -0
- package/dist/generator-templates/assets/postcss/postcss.config.js +8 -0
- package/dist/generator-templates/assets/tailwind/package.json +13 -0
- package/dist/generator-templates/assets/tailwind/postcss.config.js +10 -0
- package/dist/generator-templates/assets/tailwind/tailwind.config.js +8 -0
- package/dist/generator-templates/assets/tailwind/tailwind.css +3 -0
- package/dist/generator-templates/assets/vanilla-extract/package.json +9 -0
- package/dist/generator-templates/starter/.eslintignore +5 -0
- package/dist/generator-templates/starter/.eslintrc.js +18 -0
- package/dist/generator-templates/starter/.graphqlrc.yml +1 -0
- package/dist/generator-templates/starter/README.md +40 -0
- package/dist/generator-templates/starter/app/components/Aside.tsx +47 -0
- package/dist/generator-templates/starter/app/components/Cart.tsx +340 -0
- package/dist/generator-templates/starter/app/components/Footer.tsx +99 -0
- package/dist/generator-templates/starter/app/components/Header.tsx +178 -0
- package/dist/generator-templates/starter/app/components/Layout.tsx +95 -0
- package/dist/generator-templates/starter/app/components/Search.tsx +480 -0
- package/dist/generator-templates/starter/app/entry.client.tsx +12 -0
- package/dist/generator-templates/starter/app/entry.server.tsx +33 -0
- package/dist/generator-templates/starter/app/root.tsx +270 -0
- package/dist/generator-templates/starter/app/routes/$.tsx +7 -0
- package/dist/generator-templates/{routes → starter/app/routes}/[robots.txt].tsx +47 -69
- package/dist/generator-templates/starter/app/routes/[sitemap.xml].tsx +174 -0
- package/dist/generator-templates/starter/app/routes/_index.tsx +145 -0
- package/dist/generator-templates/starter/app/routes/account.$.tsx +9 -0
- package/dist/generator-templates/starter/app/routes/account.addresses.tsx +563 -0
- package/dist/generator-templates/starter/app/routes/account.orders.$id.tsx +309 -0
- package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +196 -0
- package/dist/generator-templates/starter/app/routes/account.profile.tsx +289 -0
- package/dist/generator-templates/starter/app/routes/account.tsx +203 -0
- package/dist/generator-templates/starter/app/routes/account_.activate.$id.$activationToken.tsx +157 -0
- package/dist/generator-templates/starter/app/routes/account_.login.tsx +143 -0
- package/dist/generator-templates/starter/app/routes/account_.logout.tsx +33 -0
- package/dist/generator-templates/starter/app/routes/account_.recover.tsx +124 -0
- package/dist/generator-templates/starter/app/routes/account_.register.tsx +207 -0
- package/dist/generator-templates/starter/app/routes/account_.reset.$id.$resetToken.tsx +136 -0
- package/dist/generator-templates/starter/app/routes/api.predictive-search.tsx +342 -0
- package/dist/generator-templates/starter/app/routes/blogs.$blogHandle.$articleHandle.tsx +88 -0
- package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +162 -0
- package/dist/generator-templates/starter/app/routes/blogs._index.tsx +94 -0
- package/dist/generator-templates/starter/app/routes/cart.tsx +104 -0
- package/dist/generator-templates/starter/app/routes/collections.$handle.tsx +184 -0
- package/dist/generator-templates/starter/app/routes/collections._index.tsx +120 -0
- package/dist/generator-templates/starter/app/routes/pages.$handle.tsx +57 -0
- package/dist/generator-templates/starter/app/routes/policies.$handle.tsx +94 -0
- package/dist/generator-templates/starter/app/routes/policies._index.tsx +63 -0
- package/dist/generator-templates/starter/app/routes/products.$handle.tsx +418 -0
- package/dist/generator-templates/starter/app/routes/search.tsx +168 -0
- package/dist/generator-templates/starter/app/styles/app.css +473 -0
- package/dist/generator-templates/starter/app/styles/reset.css +129 -0
- package/dist/generator-templates/starter/app/utils.ts +46 -0
- package/dist/generator-templates/starter/package.json +43 -0
- package/dist/generator-templates/starter/public/favicon.svg +28 -0
- package/dist/generator-templates/starter/remix.config.js +26 -0
- package/dist/generator-templates/starter/remix.env.d.ts +39 -0
- package/dist/generator-templates/starter/server.ts +253 -0
- package/dist/generator-templates/starter/storefrontapi.generated.d.ts +1906 -0
- package/dist/generator-templates/starter/tsconfig.json +22 -0
- package/dist/lib/auth.js +123 -0
- package/dist/lib/auth.test.js +157 -0
- package/dist/lib/build.js +51 -0
- package/dist/lib/check-version.js +3 -3
- package/dist/lib/check-version.test.js +24 -0
- package/dist/lib/codegen.js +26 -17
- package/dist/lib/environment-variables.js +68 -0
- package/dist/lib/environment-variables.test.js +147 -0
- package/dist/lib/file.js +41 -0
- package/dist/lib/file.test.js +69 -0
- package/dist/lib/flags.js +39 -2
- package/dist/lib/format-code.js +26 -0
- package/dist/lib/gid.js +12 -0
- package/dist/lib/{graphql.test.js → gid.test.js} +1 -1
- package/dist/lib/graphql/admin/client.js +27 -0
- package/dist/lib/graphql/admin/client.test.js +51 -0
- package/dist/lib/graphql/admin/create-storefront.js +13 -15
- package/dist/lib/graphql/admin/create-storefront.test.js +64 -0
- package/dist/lib/graphql/admin/fetch-job.js +6 -15
- package/dist/lib/graphql/admin/link-storefront.js +7 -11
- package/dist/lib/graphql/admin/link-storefront.test.js +38 -0
- package/dist/lib/graphql/admin/list-environments.js +2 -2
- package/dist/lib/graphql/admin/list-environments.test.js +44 -0
- package/dist/lib/graphql/admin/list-storefronts.js +7 -11
- package/dist/lib/graphql/admin/list-storefronts.test.js +44 -0
- package/dist/lib/graphql/admin/pull-variables.js +3 -3
- package/dist/lib/graphql/admin/pull-variables.test.js +37 -0
- package/dist/lib/graphql/business-platform/user-account.js +83 -0
- package/dist/lib/graphql/business-platform/user-account.test.js +80 -0
- package/dist/lib/log.js +185 -9
- package/dist/lib/log.test.js +92 -0
- package/dist/lib/mini-oxygen.js +19 -9
- package/dist/lib/missing-routes.js +0 -2
- package/dist/lib/onboarding/common.js +456 -0
- package/dist/lib/onboarding/index.js +2 -0
- package/dist/lib/onboarding/local.js +229 -0
- package/dist/lib/onboarding/remote.js +89 -0
- package/dist/lib/remix-version-interop.js +5 -5
- package/dist/lib/remix-version-interop.test.js +11 -1
- package/dist/lib/render-errors.js +13 -11
- package/dist/lib/setups/css/assets.js +89 -0
- package/dist/lib/setups/css/css-modules.js +22 -0
- package/dist/lib/setups/css/index.js +44 -0
- package/dist/lib/setups/css/postcss.js +34 -0
- package/dist/lib/setups/css/replacers.js +137 -0
- package/dist/lib/setups/css/tailwind.js +54 -0
- package/dist/lib/setups/css/vanilla-extract.js +22 -0
- package/dist/lib/setups/i18n/domains.test.js +25 -0
- package/dist/lib/setups/i18n/index.js +46 -0
- package/dist/lib/setups/i18n/replacers.js +227 -0
- package/dist/lib/setups/i18n/subdomains.test.js +25 -0
- package/dist/lib/setups/i18n/subfolders.test.js +25 -0
- package/dist/lib/setups/i18n/templates/domains.js +14 -0
- package/dist/lib/setups/i18n/templates/domains.ts +25 -0
- package/dist/lib/setups/i18n/templates/subdomains.js +14 -0
- package/dist/lib/setups/i18n/templates/subdomains.ts +24 -0
- package/dist/lib/setups/i18n/templates/subfolders.js +14 -0
- package/dist/lib/setups/i18n/templates/subfolders.ts +28 -0
- package/dist/lib/setups/routes/generate.js +244 -0
- package/dist/lib/setups/routes/generate.test.js +313 -0
- package/dist/lib/shell.js +52 -5
- package/dist/lib/shell.test.js +42 -16
- package/dist/lib/shopify-config.js +23 -18
- package/dist/lib/shopify-config.test.js +63 -73
- package/dist/lib/template-downloader.js +9 -7
- package/dist/lib/transpile-ts.js +9 -29
- package/dist/virtual-routes/routes/index.jsx +40 -19
- package/oclif.manifest.json +710 -1
- package/package.json +16 -16
- package/dist/commands/hydrogen/build.d.ts +0 -23
- package/dist/commands/hydrogen/check.d.ts +0 -15
- package/dist/commands/hydrogen/codegen-unstable.d.ts +0 -15
- package/dist/commands/hydrogen/dev.d.ts +0 -21
- package/dist/commands/hydrogen/env/list.d.ts +0 -18
- package/dist/commands/hydrogen/env/pull.d.ts +0 -22
- package/dist/commands/hydrogen/g.d.ts +0 -10
- package/dist/commands/hydrogen/generate/route.d.ts +0 -32
- package/dist/commands/hydrogen/generate/route.test.d.ts +0 -1
- package/dist/commands/hydrogen/generate/routes.d.ts +0 -16
- package/dist/commands/hydrogen/init.d.ts +0 -24
- package/dist/commands/hydrogen/init.test.d.ts +0 -1
- package/dist/commands/hydrogen/link.d.ts +0 -23
- package/dist/commands/hydrogen/link.test.d.ts +0 -1
- package/dist/commands/hydrogen/list.d.ts +0 -21
- package/dist/commands/hydrogen/list.test.d.ts +0 -1
- package/dist/commands/hydrogen/preview.d.ts +0 -17
- package/dist/commands/hydrogen/shortcut.d.ts +0 -9
- package/dist/commands/hydrogen/shortcut.test.d.ts +0 -1
- package/dist/commands/hydrogen/unlink.d.ts +0 -16
- package/dist/commands/hydrogen/unlink.test.d.ts +0 -1
- package/dist/create-app.d.ts +0 -1
- package/dist/generator-templates/routes/[sitemap.xml].tsx +0 -235
- package/dist/generator-templates/routes/account/login.tsx +0 -103
- package/dist/generator-templates/routes/account/register.tsx +0 -103
- package/dist/generator-templates/routes/cart.tsx +0 -81
- package/dist/generator-templates/routes/collections/$collectionHandle.tsx +0 -104
- package/dist/generator-templates/routes/collections/index.tsx +0 -102
- package/dist/generator-templates/routes/graphiql.tsx +0 -10
- package/dist/generator-templates/routes/index.tsx +0 -40
- package/dist/generator-templates/routes/pages/$pageHandle.tsx +0 -112
- package/dist/generator-templates/routes/policies/$policyHandle.tsx +0 -140
- package/dist/generator-templates/routes/policies/index.tsx +0 -117
- package/dist/generator-templates/routes/products/$productHandle.tsx +0 -92
- package/dist/hooks/init.d.ts +0 -5
- package/dist/lib/admin-session.d.ts +0 -6
- package/dist/lib/admin-session.js +0 -16
- package/dist/lib/admin-session.test.d.ts +0 -1
- package/dist/lib/admin-session.test.js +0 -27
- package/dist/lib/admin-urls.d.ts +0 -8
- package/dist/lib/check-lockfile.d.ts +0 -3
- package/dist/lib/check-lockfile.test.d.ts +0 -1
- package/dist/lib/check-version.d.ts +0 -16
- package/dist/lib/check-version.test.d.ts +0 -1
- package/dist/lib/codegen.d.ts +0 -26
- package/dist/lib/combined-environment-variables.d.ts +0 -8
- package/dist/lib/combined-environment-variables.js +0 -57
- package/dist/lib/combined-environment-variables.test.d.ts +0 -1
- package/dist/lib/combined-environment-variables.test.js +0 -111
- package/dist/lib/config.d.ts +0 -20
- package/dist/lib/flags.d.ts +0 -27
- package/dist/lib/flags.test.d.ts +0 -1
- package/dist/lib/graphql/admin/create-storefront.d.ts +0 -17
- package/dist/lib/graphql/admin/fetch-job.d.ts +0 -23
- package/dist/lib/graphql/admin/link-storefront.d.ts +0 -14
- package/dist/lib/graphql/admin/list-environments.d.ts +0 -21
- package/dist/lib/graphql/admin/list-storefronts.d.ts +0 -25
- package/dist/lib/graphql/admin/pull-variables.d.ts +0 -21
- package/dist/lib/graphql.d.ts +0 -21
- package/dist/lib/graphql.js +0 -18
- package/dist/lib/graphql.test.d.ts +0 -1
- package/dist/lib/log.d.ts +0 -6
- package/dist/lib/mini-oxygen.d.ts +0 -22
- package/dist/lib/missing-routes.d.ts +0 -8
- package/dist/lib/missing-routes.test.d.ts +0 -1
- package/dist/lib/missing-storefronts.d.ts +0 -5
- package/dist/lib/missing-storefronts.js +0 -18
- package/dist/lib/process.d.ts +0 -6
- package/dist/lib/pull-environment-variables.d.ts +0 -20
- package/dist/lib/pull-environment-variables.js +0 -57
- package/dist/lib/pull-environment-variables.test.d.ts +0 -1
- package/dist/lib/pull-environment-variables.test.js +0 -174
- package/dist/lib/remix-version-interop.d.ts +0 -11
- package/dist/lib/remix-version-interop.test.d.ts +0 -1
- package/dist/lib/render-errors.d.ts +0 -16
- package/dist/lib/shell.d.ts +0 -11
- package/dist/lib/shell.test.d.ts +0 -1
- package/dist/lib/shop.d.ts +0 -7
- package/dist/lib/shop.js +0 -32
- package/dist/lib/shop.test.d.ts +0 -1
- package/dist/lib/shop.test.js +0 -78
- package/dist/lib/shopify-config.d.ts +0 -35
- package/dist/lib/shopify-config.test.d.ts +0 -1
- package/dist/lib/string.d.ts +0 -3
- package/dist/lib/string.test.d.ts +0 -1
- package/dist/lib/template-downloader.d.ts +0 -6
- package/dist/lib/transpile-ts.d.ts +0 -16
- package/dist/lib/user-errors.d.ts +0 -9
- package/dist/lib/user-errors.js +0 -11
- package/dist/lib/virtual-routes.d.ts +0 -7
- package/dist/lib/virtual-routes.test.d.ts +0 -1
- /package/dist/{commands/hydrogen/env/list.test.d.ts → lib/setups/css/common.js} +0 -0
- /package/dist/{commands/hydrogen/env/pull.test.d.ts → lib/setups/i18n/mock-i18n-types.js} +0 -0
package/dist/lib/shell.test.js
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
|
-
import { describe, beforeEach,
|
|
1
|
+
import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
|
|
2
|
+
import { platform, userInfo } from 'node:os';
|
|
2
3
|
import { fileExists } from '@shopify/cli-kit/node/fs';
|
|
3
4
|
import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
|
|
4
|
-
import { shellWriteAlias, getCliCommand } from './shell.js';
|
|
5
|
+
import { shellWriteAlias, createPlatformShortcut, getCliCommand } from './shell.js';
|
|
5
6
|
import { execAsync } from './process.js';
|
|
6
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");
|
|
7
23
|
describe("shell", () => {
|
|
8
24
|
beforeEach(() => {
|
|
9
25
|
vi.resetAllMocks();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
vi.mock("./process.js", async () => {
|
|
14
|
-
const original = await vi.importActual(
|
|
15
|
-
"./process.js"
|
|
16
|
-
);
|
|
17
|
-
return {
|
|
18
|
-
...original,
|
|
19
|
-
execAsync: vi.fn()
|
|
20
|
-
};
|
|
21
|
-
});
|
|
22
|
-
vi.mocked(fileExists).mockResolvedValue(false);
|
|
23
|
-
vi.mocked(getPackageManager).mockResolvedValue("npm");
|
|
26
|
+
});
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
delete process.env.MINGW_PREFIX;
|
|
24
29
|
});
|
|
25
30
|
describe("shellWriteAlias", () => {
|
|
26
31
|
["bash", "zsh", "fish"].forEach((shell) => {
|
|
@@ -63,8 +68,29 @@ describe("shell", () => {
|
|
|
63
68
|
});
|
|
64
69
|
});
|
|
65
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
|
+
});
|
|
66
91
|
describe("getCliCommand", () => {
|
|
67
92
|
it("returns the shortcut alias if available", async () => {
|
|
93
|
+
vi.mocked(userInfo).mockReturnValue({ shell: "/bin/bash" });
|
|
68
94
|
vi.mocked(execAsync).mockImplementation(
|
|
69
95
|
(shellCommand) => shellCommand.startsWith("grep") ? Promise.resolve({ stdout: "stuff", stderr: "" }) : Promise.reject(null)
|
|
70
96
|
);
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { resolvePath, dirname } from '@shopify/cli-kit/node/path';
|
|
2
|
-
import { fileExists, readFile, mkdir
|
|
2
|
+
import { fileExists, writeFile, readFile, mkdir } from '@shopify/cli-kit/node/fs';
|
|
3
3
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
4
|
-
import { outputInfo } from '@shopify/cli-kit/node/output';
|
|
5
4
|
|
|
6
5
|
const SHOPIFY_DIR = ".shopify";
|
|
7
6
|
const SHOPIFY_DIR_PROJECT = "project.json";
|
|
7
|
+
async function resetConfig(root) {
|
|
8
|
+
const filePath = resolvePath(root, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
9
|
+
if (!await fileExists(filePath)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
await writeFile(filePath, JSON.stringify({}));
|
|
13
|
+
}
|
|
8
14
|
async function getConfig(root) {
|
|
9
15
|
const filePath = resolvePath(root, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
10
16
|
if (!await fileExists(filePath)) {
|
|
@@ -12,25 +18,23 @@ async function getConfig(root) {
|
|
|
12
18
|
}
|
|
13
19
|
return JSON.parse(await readFile(filePath));
|
|
14
20
|
}
|
|
15
|
-
async function
|
|
21
|
+
async function setUserAccount(root, { shop, shopName, email }) {
|
|
16
22
|
const filePath = resolvePath(root, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
17
|
-
|
|
23
|
+
let existingConfig = {};
|
|
24
|
+
if (await fileExists(filePath)) {
|
|
25
|
+
existingConfig = JSON.parse(await readFile(filePath));
|
|
26
|
+
} else {
|
|
18
27
|
await mkdir(dirname(filePath));
|
|
19
|
-
const newConfig = {
|
|
20
|
-
shop
|
|
21
|
-
};
|
|
22
|
-
await writeFile(filePath, JSON.stringify(newConfig));
|
|
23
|
-
await ensureShopifyGitIgnore(root);
|
|
24
|
-
return newConfig;
|
|
25
28
|
}
|
|
26
|
-
const
|
|
27
|
-
const config = {
|
|
29
|
+
const newConfig = {
|
|
28
30
|
...existingConfig,
|
|
29
|
-
shop
|
|
31
|
+
shop,
|
|
32
|
+
shopName,
|
|
33
|
+
email
|
|
30
34
|
};
|
|
31
|
-
await writeFile(filePath, JSON.stringify(
|
|
35
|
+
await writeFile(filePath, JSON.stringify(newConfig));
|
|
32
36
|
await ensureShopifyGitIgnore(root);
|
|
33
|
-
return
|
|
37
|
+
return newConfig;
|
|
34
38
|
}
|
|
35
39
|
async function setStorefront(root, { id, title }) {
|
|
36
40
|
try {
|
|
@@ -50,7 +54,9 @@ async function setStorefront(root, { id, title }) {
|
|
|
50
54
|
async function unsetStorefront(root) {
|
|
51
55
|
try {
|
|
52
56
|
const filePath = resolvePath(root, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
53
|
-
const existingConfig = JSON.parse(
|
|
57
|
+
const existingConfig = JSON.parse(
|
|
58
|
+
await readFile(filePath)
|
|
59
|
+
);
|
|
54
60
|
const config = {
|
|
55
61
|
...existingConfig,
|
|
56
62
|
storefront: void 0
|
|
@@ -75,7 +81,6 @@ async function ensureShopifyGitIgnore(root) {
|
|
|
75
81
|
}
|
|
76
82
|
gitIgnoreContents += `${SHOPIFY_DIR}\r
|
|
77
83
|
`;
|
|
78
|
-
outputInfo("Adding .shopify to .gitignore...");
|
|
79
84
|
await writeFile(gitIgnoreFilePath, gitIgnoreContents);
|
|
80
85
|
return true;
|
|
81
86
|
} catch {
|
|
@@ -83,4 +88,4 @@ async function ensureShopifyGitIgnore(root) {
|
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
export { SHOPIFY_DIR, SHOPIFY_DIR_PROJECT, ensureShopifyGitIgnore, getConfig,
|
|
91
|
+
export { SHOPIFY_DIR, SHOPIFY_DIR_PROJECT, ensureShopifyGitIgnore, getConfig, resetConfig, setStorefront, setUserAccount, unsetStorefront };
|
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import { inTemporaryDirectory, mkdir, writeFile, fileExists, readFile } from '@shopify/cli-kit/node/fs';
|
|
3
3
|
import { joinPath, dirname } from '@shopify/cli-kit/node/path';
|
|
4
|
-
import { getConfig, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT,
|
|
4
|
+
import { getConfig, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT, resetConfig, setUserAccount, setStorefront, unsetStorefront, ensureShopifyGitIgnore } from './shopify-config.js';
|
|
5
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
|
+
}
|
|
6
22
|
describe("getConfig()", () => {
|
|
7
23
|
describe("when no config exists", () => {
|
|
8
24
|
it("returns an empty object", async () => {
|
|
@@ -16,7 +32,9 @@ describe("getConfig()", () => {
|
|
|
16
32
|
it("returns the config", async () => {
|
|
17
33
|
await inTemporaryDirectory(async (tmpDir) => {
|
|
18
34
|
const existingConfig = {
|
|
19
|
-
shop: "my-shop"
|
|
35
|
+
shop: "my-shop",
|
|
36
|
+
shopName: "My Shop",
|
|
37
|
+
email: "email"
|
|
20
38
|
};
|
|
21
39
|
const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
22
40
|
await mkdir(dirname(filePath));
|
|
@@ -27,64 +45,70 @@ describe("getConfig()", () => {
|
|
|
27
45
|
});
|
|
28
46
|
});
|
|
29
47
|
});
|
|
30
|
-
describe("
|
|
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()", () => {
|
|
31
59
|
describe("when no config exists", () => {
|
|
32
60
|
it("creates a new config file", async () => {
|
|
33
61
|
await inTemporaryDirectory(async (tmpDir) => {
|
|
34
62
|
const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
35
63
|
expect(await fileExists(filePath)).toBeFalsy();
|
|
36
|
-
await
|
|
64
|
+
await setUserAccount(tmpDir, {
|
|
65
|
+
shop: "new-shop",
|
|
66
|
+
shopName: "New Shop",
|
|
67
|
+
email: "email"
|
|
68
|
+
});
|
|
37
69
|
expect(await fileExists(filePath)).toBeTruthy();
|
|
38
70
|
});
|
|
39
71
|
});
|
|
40
72
|
it("returns the new config", async () => {
|
|
41
73
|
await inTemporaryDirectory(async (tmpDir) => {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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);
|
|
46
81
|
});
|
|
47
82
|
});
|
|
48
83
|
});
|
|
49
84
|
describe("when a config exists", () => {
|
|
50
85
|
it("updates the config file", async () => {
|
|
51
86
|
await inTemporaryDirectory(async (tmpDir) => {
|
|
52
|
-
const existingConfig =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
87
|
+
const { existingConfig, filePath } = await writeExistingConfig(tmpDir);
|
|
88
|
+
const newConfig = {
|
|
89
|
+
shop: "new-shop",
|
|
90
|
+
shopName: "New Shop",
|
|
91
|
+
email: "email"
|
|
58
92
|
};
|
|
59
|
-
|
|
60
|
-
await mkdir(dirname(filePath));
|
|
61
|
-
await writeFile(filePath, JSON.stringify(existingConfig));
|
|
62
|
-
expect(JSON.parse(await readFile(filePath))).toStrictEqual(
|
|
63
|
-
existingConfig
|
|
64
|
-
);
|
|
65
|
-
await setShop(tmpDir, "new-shop");
|
|
93
|
+
await setUserAccount(tmpDir, newConfig);
|
|
66
94
|
expect(JSON.parse(await readFile(filePath))).toStrictEqual({
|
|
67
95
|
...existingConfig,
|
|
68
|
-
|
|
96
|
+
...newConfig
|
|
69
97
|
});
|
|
70
98
|
});
|
|
71
99
|
});
|
|
72
100
|
it("returns the new config", async () => {
|
|
73
101
|
await inTemporaryDirectory(async (tmpDir) => {
|
|
74
|
-
const existingConfig =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
102
|
+
const { existingConfig } = await writeExistingConfig(tmpDir);
|
|
103
|
+
const newConfig = {
|
|
104
|
+
shop: "new-shop",
|
|
105
|
+
email: "email",
|
|
106
|
+
shopName: "New Shop"
|
|
80
107
|
};
|
|
81
|
-
const
|
|
82
|
-
await mkdir(dirname(filePath));
|
|
83
|
-
await writeFile(filePath, JSON.stringify(existingConfig));
|
|
84
|
-
const config = await setShop(tmpDir, "new-shop");
|
|
108
|
+
const config = await setUserAccount(tmpDir, newConfig);
|
|
85
109
|
expect(config).toStrictEqual({
|
|
86
110
|
...existingConfig,
|
|
87
|
-
|
|
111
|
+
...newConfig
|
|
88
112
|
});
|
|
89
113
|
});
|
|
90
114
|
});
|
|
@@ -93,19 +117,7 @@ describe("setShop()", () => {
|
|
|
93
117
|
describe("setStorefront()", () => {
|
|
94
118
|
it("updates the config file", async () => {
|
|
95
119
|
await inTemporaryDirectory(async (tmpDir) => {
|
|
96
|
-
const existingConfig =
|
|
97
|
-
shop: "previous-shop",
|
|
98
|
-
storefront: {
|
|
99
|
-
id: "gid://shopify/HydrogenStorefront/1",
|
|
100
|
-
title: "Hydrogen"
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
104
|
-
await mkdir(dirname(filePath));
|
|
105
|
-
await writeFile(filePath, JSON.stringify(existingConfig));
|
|
106
|
-
expect(JSON.parse(await readFile(filePath))).toStrictEqual(
|
|
107
|
-
existingConfig
|
|
108
|
-
);
|
|
120
|
+
const { existingConfig, filePath } = await writeExistingConfig(tmpDir);
|
|
109
121
|
const newStorefront = {
|
|
110
122
|
id: "gid://shopify/HydrogenStorefront/2",
|
|
111
123
|
title: "Remix"
|
|
@@ -119,16 +131,7 @@ describe("setStorefront()", () => {
|
|
|
119
131
|
});
|
|
120
132
|
it("returns the new config", async () => {
|
|
121
133
|
await inTemporaryDirectory(async (tmpDir) => {
|
|
122
|
-
const existingConfig =
|
|
123
|
-
shop: "previous-shop",
|
|
124
|
-
storefront: {
|
|
125
|
-
id: "gid://shopify/HydrogenStorefront/1",
|
|
126
|
-
title: "Hydrogen"
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
130
|
-
await mkdir(dirname(filePath));
|
|
131
|
-
await writeFile(filePath, JSON.stringify(existingConfig));
|
|
134
|
+
const { existingConfig } = await writeExistingConfig(tmpDir);
|
|
132
135
|
const newStorefront = {
|
|
133
136
|
id: "gid://shopify/HydrogenStorefront/2",
|
|
134
137
|
title: "Remix"
|
|
@@ -144,27 +147,14 @@ describe("setStorefront()", () => {
|
|
|
144
147
|
describe("unsetStorefront()", () => {
|
|
145
148
|
it("removes the storefront configuration and returns the config", async () => {
|
|
146
149
|
await inTemporaryDirectory(async (tmpDir) => {
|
|
147
|
-
const existingConfig =
|
|
148
|
-
shop: "previous-shop",
|
|
149
|
-
storefront: {
|
|
150
|
-
id: "gid://shopify/HydrogenStorefront/1",
|
|
151
|
-
title: "Hydrogen"
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
const filePath = joinPath(tmpDir, SHOPIFY_DIR, SHOPIFY_DIR_PROJECT);
|
|
155
|
-
await mkdir(dirname(filePath));
|
|
156
|
-
await writeFile(filePath, JSON.stringify(existingConfig));
|
|
157
|
-
expect(JSON.parse(await readFile(filePath))).toStrictEqual(
|
|
158
|
-
existingConfig
|
|
159
|
-
);
|
|
150
|
+
const { filePath, existingConfig } = await writeExistingConfig(tmpDir);
|
|
160
151
|
const config = await unsetStorefront(tmpDir);
|
|
161
152
|
expect(config).toStrictEqual({
|
|
162
|
-
|
|
153
|
+
...existingConfig,
|
|
163
154
|
storefront: void 0
|
|
164
155
|
});
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
});
|
|
156
|
+
const { storefront, ...actualConfig } = existingConfig;
|
|
157
|
+
expect(JSON.parse(await readFile(filePath))).toStrictEqual(actualConfig);
|
|
168
158
|
});
|
|
169
159
|
});
|
|
170
160
|
});
|
|
@@ -9,8 +9,8 @@ import { fileURLToPath } from 'url';
|
|
|
9
9
|
|
|
10
10
|
const REPO_RELEASES_URL = `https://api.github.com/repos/shopify/hydrogen/releases/latest`;
|
|
11
11
|
const getTryMessage = (status) => status === 403 ? `If you are using a VPN, WARP, or similar service, consider disabling it momentarily.` : void 0;
|
|
12
|
-
async function getLatestReleaseDownloadUrl() {
|
|
13
|
-
const response = await fetch(REPO_RELEASES_URL);
|
|
12
|
+
async function getLatestReleaseDownloadUrl(signal) {
|
|
13
|
+
const response = await fetch(REPO_RELEASES_URL, { signal });
|
|
14
14
|
if (!response.ok || response.status >= 400) {
|
|
15
15
|
throw new AbortError(
|
|
16
16
|
`Failed to fetch the latest release information. Status ${response.status} ${response.statusText.replace(/\.$/, "")}.`,
|
|
@@ -23,8 +23,8 @@ async function getLatestReleaseDownloadUrl() {
|
|
|
23
23
|
url: release.tarball_url
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
-
async function downloadTarball(url, storageDir) {
|
|
27
|
-
const response = await fetch(url);
|
|
26
|
+
async function downloadTarball(url, storageDir, signal) {
|
|
27
|
+
const response = await fetch(url, { signal });
|
|
28
28
|
if (!response.ok || response.status >= 400) {
|
|
29
29
|
throw new AbortError(
|
|
30
30
|
`Failed to download the latest release files. Status ${response.status} ${response.statusText}}`,
|
|
@@ -43,9 +43,11 @@ async function downloadTarball(url, storageDir) {
|
|
|
43
43
|
})
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
|
-
async function getLatestTemplates(
|
|
46
|
+
async function getLatestTemplates({
|
|
47
|
+
signal
|
|
48
|
+
} = {}) {
|
|
47
49
|
try {
|
|
48
|
-
const { version, url } = await getLatestReleaseDownloadUrl();
|
|
50
|
+
const { version, url } = await getLatestReleaseDownloadUrl(signal);
|
|
49
51
|
const templateStoragePath = fileURLToPath(
|
|
50
52
|
new URL("../starter-templates", import.meta.url)
|
|
51
53
|
);
|
|
@@ -54,7 +56,7 @@ async function getLatestTemplates() {
|
|
|
54
56
|
}
|
|
55
57
|
const templateStorageVersionPath = path.join(templateStoragePath, version);
|
|
56
58
|
if (!await fileExists(templateStorageVersionPath)) {
|
|
57
|
-
await downloadTarball(url, templateStorageVersionPath);
|
|
59
|
+
await downloadTarball(url, templateStorageVersionPath, signal);
|
|
58
60
|
}
|
|
59
61
|
return {
|
|
60
62
|
version,
|
package/dist/lib/transpile-ts.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
|
-
import prettier from 'prettier';
|
|
4
3
|
import ts from 'typescript';
|
|
5
4
|
import glob from 'fast-glob';
|
|
6
5
|
import { outputDebug } from '@shopify/cli-kit/node/output';
|
|
6
|
+
import { getCodeFormatOptions, formatCode } from './format-code.js';
|
|
7
7
|
|
|
8
8
|
const escapeNewLines = (code) => code.replace(/\n\n/g, "\n/* :newline: */");
|
|
9
9
|
const restoreNewLines = (code) => code.replace(/\/\* :newline: \*\//g, "\n");
|
|
@@ -30,27 +30,6 @@ function transpileFile(code, config = DEFAULT_TS_CONFIG) {
|
|
|
30
30
|
});
|
|
31
31
|
return restoreNewLines(compiled.outputText);
|
|
32
32
|
}
|
|
33
|
-
const DEFAULT_PRETTIER_CONFIG = {
|
|
34
|
-
arrowParens: "always",
|
|
35
|
-
singleQuote: true,
|
|
36
|
-
bracketSpacing: false,
|
|
37
|
-
trailingComma: "all"
|
|
38
|
-
};
|
|
39
|
-
async function resolveFormatConfig(filePath = process.cwd()) {
|
|
40
|
-
try {
|
|
41
|
-
return await prettier.resolveConfig(filePath) || DEFAULT_PRETTIER_CONFIG;
|
|
42
|
-
} catch {
|
|
43
|
-
return DEFAULT_PRETTIER_CONFIG;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function format(content, config, filePath = "") {
|
|
47
|
-
const ext = path.extname(filePath);
|
|
48
|
-
const formattedContent = prettier.format(content, {
|
|
49
|
-
parser: ext === ".tsx" || ext === ".ts" ? "typescript" : "babel",
|
|
50
|
-
...config
|
|
51
|
-
});
|
|
52
|
-
return formattedContent;
|
|
53
|
-
}
|
|
54
33
|
const DEFAULT_JS_CONFIG = {
|
|
55
34
|
allowJs: true,
|
|
56
35
|
forceConsistentCasingInFileNames: true,
|
|
@@ -95,14 +74,14 @@ async function transpileProject(projectDir) {
|
|
|
95
74
|
absolute: true,
|
|
96
75
|
cwd: projectDir
|
|
97
76
|
});
|
|
98
|
-
const formatConfig = await
|
|
77
|
+
const formatConfig = await getCodeFormatOptions();
|
|
99
78
|
for (const entry of entries) {
|
|
100
79
|
if (entry.endsWith(".d.ts")) {
|
|
101
80
|
await fs.rm(entry);
|
|
102
81
|
continue;
|
|
103
82
|
}
|
|
104
83
|
const tsx = await fs.readFile(entry, "utf8");
|
|
105
|
-
const mjs =
|
|
84
|
+
const mjs = formatCode(transpileFile(tsx), formatConfig);
|
|
106
85
|
await fs.rm(entry);
|
|
107
86
|
await fs.writeFile(entry.replace(/\.ts(x?)$/, ".js$1"), mjs, "utf8");
|
|
108
87
|
}
|
|
@@ -145,11 +124,12 @@ async function transpileProject(projectDir) {
|
|
|
145
124
|
delete pkgJson.devDependencies[key];
|
|
146
125
|
}
|
|
147
126
|
}
|
|
127
|
+
const codegenFlag = /\s*--codegen(-unstable)?/;
|
|
148
128
|
if (pkgJson.scripts?.dev) {
|
|
149
|
-
pkgJson.scripts.dev = pkgJson.scripts.dev.replace(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
);
|
|
129
|
+
pkgJson.scripts.dev = pkgJson.scripts.dev.replace(codegenFlag, "");
|
|
130
|
+
}
|
|
131
|
+
if (pkgJson.scripts?.build) {
|
|
132
|
+
pkgJson.scripts.build = pkgJson.scripts.build.replace(codegenFlag, "");
|
|
153
133
|
}
|
|
154
134
|
await fs.writeFile(
|
|
155
135
|
path.join(projectDir, "package.json"),
|
|
@@ -172,4 +152,4 @@ async function transpileProject(projectDir) {
|
|
|
172
152
|
}
|
|
173
153
|
}
|
|
174
154
|
|
|
175
|
-
export { convertConfigToJS,
|
|
155
|
+
export { convertConfigToJS, transpileFile, transpileProject };
|
|
@@ -30,53 +30,74 @@ const links = () => [
|
|
|
30
30
|
crossOrigin: "anonymous"
|
|
31
31
|
}
|
|
32
32
|
];
|
|
33
|
-
async function loader({
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
async function loader({
|
|
34
|
+
context: { storefront }
|
|
35
|
+
}) {
|
|
36
|
+
const layout = await storefront.query(LAYOUT_QUERY);
|
|
37
|
+
return { layout, isMockShop: storefront.getApiUrl().includes("mock.shop") };
|
|
36
38
|
}
|
|
37
39
|
const HYDROGEN_SHOP_ID = "gid://shopify/Shop/55145660472";
|
|
38
40
|
function ErrorBoundary() {
|
|
39
41
|
return <ErrorPage />;
|
|
40
42
|
}
|
|
41
43
|
function Index() {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
const {
|
|
45
|
+
isMockShop,
|
|
46
|
+
layout: { shop }
|
|
47
|
+
} = useLoaderData();
|
|
48
|
+
let { name: shopName, id: shopId } = shop;
|
|
49
|
+
const configDone = shopId !== HYDROGEN_SHOP_ID && !isMockShop;
|
|
50
|
+
if (isMockShop || !shopName)
|
|
51
|
+
shopName = "Hydrogen";
|
|
45
52
|
return <><Layout shopName={shopName}>
|
|
46
53
|
{configDone ? <HydrogenLogoBaseColor /> : <HydrogenLogoBaseBW />}
|
|
47
54
|
<h1>
|
|
48
55
|
{"Hello, "}
|
|
49
|
-
{shopName
|
|
56
|
+
{shopName}
|
|
50
57
|
</h1>
|
|
51
58
|
<p>Welcome to your new custom storefront</p>
|
|
52
|
-
|
|
59
|
+
<section className="Banner">
|
|
53
60
|
<div>
|
|
54
61
|
<IconBanner />
|
|
55
|
-
<h2>Configure storefront token</h2>
|
|
62
|
+
<h2>{configDone ? "Create your first route" : "Configure storefront token"}</h2>
|
|
56
63
|
</div>
|
|
57
|
-
<p>
|
|
64
|
+
{configDone ? <p>
|
|
65
|
+
{"You\u2019re seeing this because you don\u2019t have a home route in your project yet. "}
|
|
66
|
+
<br />
|
|
67
|
+
{"Run "}
|
|
68
|
+
<code>h2 generate route home</code>
|
|
69
|
+
{" to create your home route. Learn more about"}
|
|
70
|
+
{` `}
|
|
71
|
+
<CreateRoutesLink />
|
|
72
|
+
</p> : <p>
|
|
58
73
|
{"You\u2019re seeing this because you have not yet configured your storefront token. "}
|
|
59
74
|
<br />
|
|
60
75
|
<br />
|
|
61
|
-
{" To
|
|
62
|
-
{` `}
|
|
63
|
-
<code>.env</code>
|
|
64
|
-
{". Then, create your first route with the file"}
|
|
76
|
+
{" To link your store,"}
|
|
65
77
|
{` `}
|
|
66
|
-
|
|
67
|
-
{"
|
|
78
|
+
{"run "}
|
|
79
|
+
<code>{"h2 link && h2 env pull"}</code>
|
|
80
|
+
{". Then, run"}
|
|
81
|
+
{" "}
|
|
82
|
+
<code>h2 generate route home</code>
|
|
83
|
+
{" to create your first route."}
|
|
84
|
+
<br />
|
|
85
|
+
{"Learn more about"}
|
|
68
86
|
{` `}
|
|
69
87
|
<a target="_blank" rel="norefferer noopener" href="https://shopify.dev/docs/custom-storefronts/hydrogen/environment-variables">editing environment variables</a>
|
|
70
88
|
{` `}
|
|
71
89
|
{"and"}
|
|
72
90
|
{` `}
|
|
73
|
-
<
|
|
91
|
+
<CreateRoutesLink />
|
|
74
92
|
{"."}
|
|
75
|
-
</p>
|
|
76
|
-
</section>
|
|
93
|
+
</p>}
|
|
94
|
+
</section>
|
|
77
95
|
<ResourcesLinks />
|
|
78
96
|
</Layout></>;
|
|
79
97
|
}
|
|
98
|
+
function CreateRoutesLink() {
|
|
99
|
+
return <a target="_blank" rel="norefferer noopener" href="https://shopify.dev/docs/custom-storefronts/hydrogen/building/begin-development#step-4-create-a-route">creating routes</a>;
|
|
100
|
+
}
|
|
80
101
|
function ErrorPage() {
|
|
81
102
|
return <><Layout shopName="Hydrogen">
|
|
82
103
|
<HydrogenLogoBaseBW />
|