@shopify/cli-hydrogen 4.0.5 → 4.0.6
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/init.d.ts +4 -2
- package/dist/commands/hydrogen/init.js +18 -17
- package/dist/generator-templates/routes/[sitemap.xml].tsx +2 -2
- package/dist/generator-templates/routes/cart.tsx +4 -6
- package/dist/generator-templates/routes/collections/$collectionHandle.tsx +1 -1
- package/dist/generator-templates/routes/pages/$pageHandle.tsx +1 -1
- package/dist/generator-templates/routes/policies/$policyHandle.tsx +1 -1
- package/dist/generator-templates/routes/policies/index.tsx +4 -2
- package/dist/generator-templates/routes/products/$productHandle.tsx +1 -1
- package/dist/utils/check-lockfile.test.d.ts +1 -0
- package/dist/utils/check-lockfile.test.js +74 -0
- package/dist/utils/flags.d.ts +9 -1
- package/dist/utils/flags.js +19 -1
- package/dist/utils/flags.test.js +26 -1
- package/dist/utils/template-downloader.js +1 -1
- package/dist/utils/virtual-routes.js +4 -7
- package/dist/utils/virtual-routes.test.js +2 -0
- package/dist/virtual-routes/assets/inter-variable-font.ttf +0 -0
- package/dist/virtual-routes/assets/jetbrainsmono-variable-font.ttf +0 -0
- package/dist/virtual-routes/assets/styles.css +42 -11
- package/dist/virtual-routes/routes/index.jsx +33 -11
- package/oclif.manifest.json +1 -1
- package/package.json +5 -5
|
@@ -4,10 +4,11 @@ import Command from '@shopify/cli-kit/node/base-command';
|
|
|
4
4
|
declare class Init extends Command {
|
|
5
5
|
static description: string;
|
|
6
6
|
static flags: {
|
|
7
|
-
path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined>;
|
|
8
7
|
force: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
|
|
9
|
-
|
|
8
|
+
path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined>;
|
|
9
|
+
language: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined>;
|
|
10
10
|
template: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined>;
|
|
11
|
+
'install-deps': _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
|
|
11
12
|
};
|
|
12
13
|
run(): Promise<void>;
|
|
13
14
|
}
|
|
@@ -17,6 +18,7 @@ declare function runInit(options?: {
|
|
|
17
18
|
language?: string;
|
|
18
19
|
token?: string;
|
|
19
20
|
force?: boolean;
|
|
21
|
+
installDeps?: boolean;
|
|
20
22
|
}): Promise<void>;
|
|
21
23
|
|
|
22
24
|
export { Init as default, runInit };
|
|
@@ -3,25 +3,35 @@ import { packageManagerUsedForCreating, installNodeModules } from '@shopify/cli-
|
|
|
3
3
|
import { renderFatalError } from '@shopify/cli-kit/node/ui';
|
|
4
4
|
import Flags from '@oclif/core/lib/flags.js';
|
|
5
5
|
import { output, path } from '@shopify/cli-kit';
|
|
6
|
-
import { commonFlags } from '../../utils/flags.js';
|
|
6
|
+
import { commonFlags, parseProcessFlags } from '../../utils/flags.js';
|
|
7
7
|
import { transpileProject } from '../../utils/transpile-ts.js';
|
|
8
8
|
import { getLatestTemplates } from '../../utils/template-downloader.js';
|
|
9
9
|
import { readdir } from 'fs/promises';
|
|
10
10
|
|
|
11
|
+
const STARTER_TEMPLATES = ["hello-world", "demo-store"];
|
|
12
|
+
const FLAG_MAP = { f: "force" };
|
|
11
13
|
class Init extends Command {
|
|
12
14
|
static description = "Creates a new Hydrogen storefront.";
|
|
13
15
|
static flags = {
|
|
14
|
-
path: commonFlags.path,
|
|
15
16
|
force: commonFlags.force,
|
|
17
|
+
path: Flags.string({
|
|
18
|
+
description: "The path to the directory of the new Hydrogen storefront.",
|
|
19
|
+
env: "SHOPIFY_HYDROGEN_FLAG_PATH"
|
|
20
|
+
}),
|
|
16
21
|
language: Flags.string({
|
|
17
22
|
description: "Sets the template language to use. One of `js` or `ts`.",
|
|
18
23
|
choices: ["js", "ts"],
|
|
19
|
-
default: "js",
|
|
20
24
|
env: "SHOPIFY_HYDROGEN_FLAG_LANGUAGE"
|
|
21
25
|
}),
|
|
22
26
|
template: Flags.string({
|
|
23
27
|
description: "Sets the template to use. One of `demo-store` or `hello-world`.",
|
|
28
|
+
choices: STARTER_TEMPLATES,
|
|
24
29
|
env: "SHOPIFY_HYDROGEN_FLAG_TEMPLATE"
|
|
30
|
+
}),
|
|
31
|
+
"install-deps": Flags.boolean({
|
|
32
|
+
description: "Auto install dependencies using the active package manager",
|
|
33
|
+
env: "SHOPIFY_HYDROGEN_INSTALL_DEPS",
|
|
34
|
+
allowNo: true
|
|
25
35
|
})
|
|
26
36
|
};
|
|
27
37
|
async run() {
|
|
@@ -29,7 +39,7 @@ class Init extends Command {
|
|
|
29
39
|
await runInit({ ...flags });
|
|
30
40
|
}
|
|
31
41
|
}
|
|
32
|
-
async function runInit(options =
|
|
42
|
+
async function runInit(options = parseProcessFlags(process.argv, FLAG_MAP)) {
|
|
33
43
|
supressNodeExperimentalWarnings();
|
|
34
44
|
const templatesPromise = getLatestTemplates().catch((error) => {
|
|
35
45
|
output.info("\n\n\n");
|
|
@@ -44,7 +54,7 @@ async function runInit(options = getProcessFlags()) {
|
|
|
44
54
|
type: "select",
|
|
45
55
|
name: "template",
|
|
46
56
|
message: "Choose a template",
|
|
47
|
-
choices:
|
|
57
|
+
choices: STARTER_TEMPLATES.map((value) => ({
|
|
48
58
|
name: value.replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()),
|
|
49
59
|
value
|
|
50
60
|
}))
|
|
@@ -119,7 +129,7 @@ async function runInit(options = getProcessFlags()) {
|
|
|
119
129
|
let depsInstalled = false;
|
|
120
130
|
let packageManager = await packageManagerUsedForCreating();
|
|
121
131
|
if (packageManager !== "unknown") {
|
|
122
|
-
const
|
|
132
|
+
const installDeps = options.installDeps ?? (await ui.prompt([
|
|
123
133
|
{
|
|
124
134
|
type: "select",
|
|
125
135
|
name: "installDeps",
|
|
@@ -130,8 +140,8 @@ async function runInit(options = getProcessFlags()) {
|
|
|
130
140
|
],
|
|
131
141
|
default: "true"
|
|
132
142
|
}
|
|
133
|
-
]);
|
|
134
|
-
if (installDeps
|
|
143
|
+
])).installDeps === "true";
|
|
144
|
+
if (installDeps) {
|
|
135
145
|
await installNodeModules({
|
|
136
146
|
directory: projectDir,
|
|
137
147
|
packageManager,
|
|
@@ -162,15 +172,6 @@ async function projectExists(projectDir) {
|
|
|
162
172
|
const { file } = await import('@shopify/cli-kit');
|
|
163
173
|
return await file.exists(projectDir) && await file.isDirectory(projectDir) && (await readdir(projectDir)).length > 0;
|
|
164
174
|
}
|
|
165
|
-
function getProcessFlags() {
|
|
166
|
-
const flagMap = { f: "force" };
|
|
167
|
-
const [, , ...flags] = process.argv;
|
|
168
|
-
return flags.reduce((acc, flag) => {
|
|
169
|
-
const [key, value = true] = flag.replace(/^\-+/, "").split("=");
|
|
170
|
-
const mappedKey = flagMap[key] || key;
|
|
171
|
-
return { ...acc, [mappedKey]: value };
|
|
172
|
-
}, {});
|
|
173
|
-
}
|
|
174
175
|
function supressNodeExperimentalWarnings() {
|
|
175
176
|
const warningListener = process.listeners("warning")[0];
|
|
176
177
|
if (warningListener) {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {flattenConnection} from '@shopify/hydrogen
|
|
1
|
+
import {flattenConnection} from '@shopify/hydrogen';
|
|
2
2
|
import type {LoaderArgs} from '@shopify/remix-oxygen';
|
|
3
3
|
import {
|
|
4
4
|
CollectionConnection,
|
|
5
5
|
PageConnection,
|
|
6
6
|
ProductConnection,
|
|
7
|
-
} from '@shopify/hydrogen
|
|
7
|
+
} from '@shopify/hydrogen/storefront-api-types';
|
|
8
8
|
|
|
9
9
|
const MAX_URLS = 250; // the google limit is 50K, however, SF API only allow querying for 250 resources each time
|
|
10
10
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {Await, useMatches} from '@remix-run/react';
|
|
2
2
|
import {Suspense} from 'react';
|
|
3
|
-
import {flattenConnection} from '@shopify/hydrogen
|
|
4
|
-
import type {
|
|
3
|
+
import {flattenConnection} from '@shopify/hydrogen';
|
|
4
|
+
import type {Cart as CartType} from '@shopify/hydrogen/storefront-api-types';
|
|
5
5
|
|
|
6
6
|
export async function action() {
|
|
7
7
|
// @TODO implement cart action
|
|
@@ -11,7 +11,7 @@ export default function CartRoute() {
|
|
|
11
11
|
const [root] = useMatches();
|
|
12
12
|
return (
|
|
13
13
|
<Suspense fallback="loading">
|
|
14
|
-
<Await resolve={root.data?.cart}>
|
|
14
|
+
<Await resolve={root.data?.cart as CartType}>
|
|
15
15
|
{(cart) => {
|
|
16
16
|
const linesCount = Boolean(cart?.lines?.edges?.length || 0);
|
|
17
17
|
if (!linesCount) {
|
|
@@ -20,9 +20,7 @@ export default function CartRoute() {
|
|
|
20
20
|
);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
const cartLines = cart?.lines
|
|
24
|
-
? flattenConnection<CartLine>(cart?.lines)
|
|
25
|
-
: [];
|
|
23
|
+
const cartLines = cart?.lines ? flattenConnection(cart?.lines) : [];
|
|
26
24
|
|
|
27
25
|
return (
|
|
28
26
|
<>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {json, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
2
2
|
import {useLoaderData} from '@remix-run/react';
|
|
3
|
-
import type {Collection as CollectionType} from '@shopify/hydrogen
|
|
3
|
+
import type {Collection as CollectionType} from '@shopify/hydrogen/storefront-api-types';
|
|
4
4
|
import {Link} from '@remix-run/react';
|
|
5
5
|
|
|
6
6
|
export async function loader({params, request, context}: LoaderArgs) {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from '@shopify/remix-oxygen';
|
|
7
7
|
import {useLoaderData} from '@remix-run/react';
|
|
8
8
|
import invariant from 'tiny-invariant';
|
|
9
|
-
import type {Page as PageType} from '@shopify/hydrogen
|
|
9
|
+
import type {Page as PageType} from '@shopify/hydrogen/storefront-api-types';
|
|
10
10
|
import type {SeoHandleFunction} from '@shopify/hydrogen';
|
|
11
11
|
|
|
12
12
|
export async function loader({params, context}: LoaderArgs) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {json, type MetaFunction, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
2
2
|
import {useLoaderData} from '@remix-run/react';
|
|
3
3
|
|
|
4
|
-
import {ShopPolicy} from '@shopify/hydrogen
|
|
4
|
+
import {ShopPolicy} from '@shopify/hydrogen/storefront-api-types';
|
|
5
5
|
|
|
6
6
|
export async function loader({request, params, context}: LoaderArgs) {
|
|
7
7
|
const handle = params.policyHandle;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {json, type LoaderArgs} from '@shopify/remix-oxygen';
|
|
2
2
|
import {useLoaderData, Link} from '@remix-run/react';
|
|
3
|
-
import type {ShopPolicy} from '@shopify/hydrogen
|
|
3
|
+
import type {ShopPolicy} from '@shopify/hydrogen/storefront-api-types';
|
|
4
4
|
|
|
5
5
|
export async function loader({context: {storefront}}: LoaderArgs) {
|
|
6
6
|
const data = await storefront.query<{
|
|
@@ -26,7 +26,9 @@ export default function Policies() {
|
|
|
26
26
|
{policies.map((policy) => {
|
|
27
27
|
return (
|
|
28
28
|
policy && (
|
|
29
|
-
<Link to={`/policies/${policy.handle}`}>
|
|
29
|
+
<Link key={policy.id} to={`/policies/${policy.handle}`}>
|
|
30
|
+
{policy.title}
|
|
31
|
+
</Link>
|
|
30
32
|
)
|
|
31
33
|
);
|
|
32
34
|
})}
|
|
@@ -3,7 +3,7 @@ import {useLoaderData} from '@remix-run/react';
|
|
|
3
3
|
import type {
|
|
4
4
|
ProductVariant,
|
|
5
5
|
Product as ProductType,
|
|
6
|
-
} from '@shopify/hydrogen
|
|
6
|
+
} from '@shopify/hydrogen/storefront-api-types';
|
|
7
7
|
|
|
8
8
|
export async function loader({params, context}: LoaderArgs) {
|
|
9
9
|
const {productHandle} = params;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { checkLockfileStatus } from './check-lockfile.js';
|
|
2
|
+
import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
|
|
3
|
+
import { git, outputMocker, file, path } from '@shopify/cli-kit';
|
|
4
|
+
|
|
5
|
+
vi.mock("@shopify/cli-kit", async () => {
|
|
6
|
+
const cliKit = await vi.importActual("@shopify/cli-kit");
|
|
7
|
+
return {
|
|
8
|
+
...cliKit,
|
|
9
|
+
git: {
|
|
10
|
+
factory: vi.fn()
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
describe("checkLockfileStatus()", () => {
|
|
15
|
+
const checkIgnoreMock = vi.fn();
|
|
16
|
+
const gitFactoryMock = {
|
|
17
|
+
checkIgnore: checkIgnoreMock
|
|
18
|
+
};
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.mocked(git.factory).mockReturnValue(gitFactoryMock);
|
|
21
|
+
vi.mocked(checkIgnoreMock).mockResolvedValue([]);
|
|
22
|
+
});
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
vi.restoreAllMocks();
|
|
25
|
+
outputMocker.mockAndCaptureOutput().clear();
|
|
26
|
+
});
|
|
27
|
+
describe("when a lockfile is present", () => {
|
|
28
|
+
it("does not call displayLockfileWarning", async () => {
|
|
29
|
+
await file.inTemporaryDirectory(async (tmpDir) => {
|
|
30
|
+
await file.write(path.join(tmpDir, "package-lock.json"), "");
|
|
31
|
+
const outputMock = outputMocker.mockAndCaptureOutput();
|
|
32
|
+
await checkLockfileStatus(tmpDir);
|
|
33
|
+
expect(outputMock.warn()).toBe("");
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe("and it is being ignored by Git", () => {
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
vi.mocked(checkIgnoreMock).mockResolvedValue(["package-lock.json"]);
|
|
39
|
+
});
|
|
40
|
+
it("renders a warning", async () => {
|
|
41
|
+
await file.inTemporaryDirectory(async (tmpDir) => {
|
|
42
|
+
await file.write(path.join(tmpDir, "package-lock.json"), "");
|
|
43
|
+
const outputMock = outputMocker.mockAndCaptureOutput();
|
|
44
|
+
await checkLockfileStatus(tmpDir);
|
|
45
|
+
expect(outputMock.warn()).toMatch(
|
|
46
|
+
/ warning .+ Lockfile ignored by Git .+/is
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
describe("when there are multiple lockfiles", () => {
|
|
53
|
+
it("renders a warning", async () => {
|
|
54
|
+
await file.inTemporaryDirectory(async (tmpDir) => {
|
|
55
|
+
await file.write(path.join(tmpDir, "package-lock.json"), "");
|
|
56
|
+
await file.write(path.join(tmpDir, "pnpm-lock.yaml"), "");
|
|
57
|
+
const outputMock = outputMocker.mockAndCaptureOutput();
|
|
58
|
+
await checkLockfileStatus(tmpDir);
|
|
59
|
+
expect(outputMock.warn()).toMatch(
|
|
60
|
+
/ warning .+ Multiple lockfiles found .+/is
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe("when a lockfile is missing", () => {
|
|
66
|
+
it("renders a warning", async () => {
|
|
67
|
+
await file.inTemporaryDirectory(async (tmpDir) => {
|
|
68
|
+
const outputMock = outputMocker.mockAndCaptureOutput();
|
|
69
|
+
await checkLockfileStatus(tmpDir);
|
|
70
|
+
expect(outputMock.warn()).toMatch(/ warning .+ No lockfile found .+/is);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
package/dist/utils/flags.d.ts
CHANGED
|
@@ -6,5 +6,13 @@ declare const commonFlags: {
|
|
|
6
6
|
force: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
|
|
7
7
|
};
|
|
8
8
|
declare function flagsToCamelObject(obj: Record<string, any>): any;
|
|
9
|
+
/**
|
|
10
|
+
* Parse process arguments into an object for use in the cli as flags.
|
|
11
|
+
* This is used when starting the init command from create-hydrogen without Oclif.
|
|
12
|
+
* @example
|
|
13
|
+
* input: `node ./bin --force --no-install-deps --language js`
|
|
14
|
+
* output: { force: true, installDeps: false, language: 'js' }
|
|
15
|
+
*/
|
|
16
|
+
declare function parseProcessFlags(processArgv: string[], flagMap?: Record<string, string>): any;
|
|
9
17
|
|
|
10
|
-
export { commonFlags, flagsToCamelObject };
|
|
18
|
+
export { commonFlags, flagsToCamelObject, parseProcessFlags };
|
package/dist/utils/flags.js
CHANGED
|
@@ -23,5 +23,23 @@ function flagsToCamelObject(obj) {
|
|
|
23
23
|
return acc;
|
|
24
24
|
}, {});
|
|
25
25
|
}
|
|
26
|
+
function parseProcessFlags(processArgv, flagMap = {}) {
|
|
27
|
+
const [, , ...args] = processArgv;
|
|
28
|
+
const options = {};
|
|
29
|
+
for (let i = 0; i < args.length; i++) {
|
|
30
|
+
const arg = args[i];
|
|
31
|
+
const nextArg = args[i + 1];
|
|
32
|
+
if (arg?.startsWith("-")) {
|
|
33
|
+
let key = arg.replace(/^\-{1,2}/, "");
|
|
34
|
+
let value = !nextArg || nextArg.startsWith("-") ? true : nextArg;
|
|
35
|
+
if (value === true && key.startsWith("no-")) {
|
|
36
|
+
value = false;
|
|
37
|
+
key = key.replace("no-", "");
|
|
38
|
+
}
|
|
39
|
+
options[flagMap[key] || key] = value;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return flagsToCamelObject(options);
|
|
43
|
+
}
|
|
26
44
|
|
|
27
|
-
export { commonFlags, flagsToCamelObject };
|
|
45
|
+
export { commonFlags, flagsToCamelObject, parseProcessFlags };
|
package/dist/utils/flags.test.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { flagsToCamelObject } from './flags.js';
|
|
2
|
+
import { flagsToCamelObject, parseProcessFlags } from './flags.js';
|
|
3
3
|
|
|
4
4
|
describe("CLI flag utils", () => {
|
|
5
5
|
it("turns kebab-case flags to camelCase", async () => {
|
|
@@ -15,4 +15,29 @@ describe("CLI flag utils", () => {
|
|
|
15
15
|
flag: "value"
|
|
16
16
|
});
|
|
17
17
|
});
|
|
18
|
+
it("parses flags from process.argv", async () => {
|
|
19
|
+
expect(
|
|
20
|
+
parseProcessFlags(
|
|
21
|
+
"node ./bin --force --install-deps --template hello-world --path test --language ts".split(
|
|
22
|
+
" "
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
).toMatchObject({
|
|
26
|
+
force: true,
|
|
27
|
+
installDeps: true,
|
|
28
|
+
template: "hello-world",
|
|
29
|
+
path: "test",
|
|
30
|
+
language: "ts"
|
|
31
|
+
});
|
|
32
|
+
expect(
|
|
33
|
+
parseProcessFlags(
|
|
34
|
+
"node ./bin -f --no-install-deps --language js".split(" "),
|
|
35
|
+
{ f: "force" }
|
|
36
|
+
)
|
|
37
|
+
).toMatchObject({
|
|
38
|
+
force: true,
|
|
39
|
+
installDeps: false,
|
|
40
|
+
language: "js"
|
|
41
|
+
});
|
|
42
|
+
});
|
|
18
43
|
});
|
|
@@ -33,7 +33,7 @@ async function downloadTarball(url, storageDir) {
|
|
|
33
33
|
strip: 1,
|
|
34
34
|
filter: (name) => {
|
|
35
35
|
name = name.replace(storageDir, "");
|
|
36
|
-
return !name.startsWith("/templates/") || name.startsWith("/templates/skeleton/");
|
|
36
|
+
return !name.startsWith(path.normalize("/templates/")) || name.startsWith(path.normalize("/templates/skeleton/"));
|
|
37
37
|
}
|
|
38
38
|
})
|
|
39
39
|
);
|
|
@@ -4,23 +4,20 @@ import recursiveReaddir from 'recursive-readdir';
|
|
|
4
4
|
|
|
5
5
|
const VIRTUAL_ROUTES_DIR = "virtual-routes/routes";
|
|
6
6
|
const VIRTUAL_ROOT = "virtual-routes/virtual-root";
|
|
7
|
-
const INDEX_SUFFIX = "/index";
|
|
8
7
|
async function addVirtualRoutes(config) {
|
|
9
8
|
const userRouteList = Object.values(config.routes);
|
|
10
9
|
const distPath = fileURLToPath(new URL("..", import.meta.url));
|
|
11
10
|
const virtualRoutesPath = path.join(distPath, VIRTUAL_ROUTES_DIR);
|
|
12
11
|
for (const absoluteFilePath of await recursiveReaddir(virtualRoutesPath)) {
|
|
13
12
|
const relativeFilePath = path.relative(virtualRoutesPath, absoluteFilePath);
|
|
14
|
-
const routePath =
|
|
15
|
-
|
|
16
|
-
).replace(
|
|
17
|
-
const isIndex = routePath.endsWith(INDEX_SUFFIX);
|
|
18
|
-
const normalizedVirtualRoutePath = isIndex ? routePath.slice(0, -INDEX_SUFFIX.length) || void 0 : routePath.slice(1).replace(/\$/g, ":").replace(/[\[\]]/g, "");
|
|
13
|
+
const routePath = relativeFilePath.replace(/\.[jt]sx?$/, "").replaceAll("\\", "/");
|
|
14
|
+
const isIndex = /(^|\/)index$/.test(routePath);
|
|
15
|
+
const normalizedVirtualRoutePath = isIndex ? routePath.slice(0, -"index".length).replace(/\/$/, "") || void 0 : routePath.replace(/\$/g, ":").replace(/[\[\]]/g, "");
|
|
19
16
|
const hasUserRoute = userRouteList.some(
|
|
20
17
|
(r) => r.parentId === "root" && r.path === normalizedVirtualRoutePath
|
|
21
18
|
);
|
|
22
19
|
if (!hasUserRoute) {
|
|
23
|
-
const id = VIRTUAL_ROUTES_DIR + routePath;
|
|
20
|
+
const id = VIRTUAL_ROUTES_DIR + "/" + routePath;
|
|
24
21
|
config.routes[id] = {
|
|
25
22
|
id,
|
|
26
23
|
parentId: VIRTUAL_ROOT,
|
|
@@ -16,10 +16,12 @@ describe("virtual routes", () => {
|
|
|
16
16
|
});
|
|
17
17
|
expect(config.routes[VIRTUAL_ROUTES_DIR + "/index"]).toMatchObject({
|
|
18
18
|
parentId: VIRTUAL_ROOT,
|
|
19
|
+
path: void 0,
|
|
19
20
|
file: "../" + VIRTUAL_ROUTES_DIR + "/index.tsx"
|
|
20
21
|
});
|
|
21
22
|
expect(config.routes[VIRTUAL_ROUTES_DIR + "/graphiql"]).toMatchObject({
|
|
22
23
|
parentId: VIRTUAL_ROOT,
|
|
24
|
+
path: "graphiql",
|
|
23
25
|
file: "../" + VIRTUAL_ROUTES_DIR + "/graphiql.tsx"
|
|
24
26
|
});
|
|
25
27
|
});
|
|
Binary file
|
|
Binary file
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
@font-face {
|
|
2
|
+
font-family: 'Inter';
|
|
3
|
+
src: url('../assets/inter-variable-font.ttf') format('truetype-variations');
|
|
4
|
+
}
|
|
5
|
+
@font-face {
|
|
6
|
+
font-family: 'JetBrains Mono';
|
|
7
|
+
src: url('../assets/jetbrainsmono-variable-font.ttf')
|
|
8
|
+
format('truetype-variations');
|
|
9
|
+
}
|
|
10
|
+
|
|
1
11
|
* {
|
|
2
12
|
box-sizing: border-box;
|
|
3
13
|
}
|
|
@@ -11,8 +21,7 @@ p {
|
|
|
11
21
|
|
|
12
22
|
h1 {
|
|
13
23
|
font-size: 3rem;
|
|
14
|
-
|
|
15
|
-
line-height: 1.4;
|
|
24
|
+
line-height: 1.25;
|
|
16
25
|
}
|
|
17
26
|
|
|
18
27
|
h2 {
|
|
@@ -24,23 +33,31 @@ h2 {
|
|
|
24
33
|
p {
|
|
25
34
|
font-size: 1rem;
|
|
26
35
|
line-height: 1.4;
|
|
36
|
+
font-weight: 500;
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
body {
|
|
30
40
|
padding: 0;
|
|
31
41
|
margin: 0;
|
|
32
42
|
background: #ffffff;
|
|
33
|
-
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
34
|
-
Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
35
|
-
|
|
43
|
+
font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
44
|
+
Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
45
|
+
-webkit-font-smoothing: antialiased;
|
|
36
46
|
min-height: 100vh;
|
|
37
47
|
display: flex;
|
|
38
48
|
flex-direction: column;
|
|
39
49
|
}
|
|
40
50
|
|
|
51
|
+
.hydrogen-virtual-route {
|
|
52
|
+
display: grid;
|
|
53
|
+
grid-template-rows: auto 1fr auto;
|
|
54
|
+
min-height: 100vh;
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
header {
|
|
42
58
|
display: flex;
|
|
43
59
|
flex-direction: row;
|
|
60
|
+
gap: 20px;
|
|
44
61
|
padding: 14px 20px;
|
|
45
62
|
width: 100%;
|
|
46
63
|
height: 48px;
|
|
@@ -59,14 +76,16 @@ header nav {
|
|
|
59
76
|
|
|
60
77
|
header h1 {
|
|
61
78
|
font-size: 0.75rem;
|
|
62
|
-
margin-right: 20px;
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
header p {
|
|
82
|
+
font-family: 'JetBrains Mono';
|
|
83
|
+
font-weight: 700;
|
|
66
84
|
font-size: 0.875rem;
|
|
67
85
|
border: 1.5px solid #d2d5d8;
|
|
68
86
|
border-radius: 4px;
|
|
69
87
|
padding: 1px;
|
|
88
|
+
white-space: nowrap;
|
|
70
89
|
}
|
|
71
90
|
|
|
72
91
|
main {
|
|
@@ -91,14 +110,25 @@ footer a {
|
|
|
91
110
|
text-decoration: none;
|
|
92
111
|
}
|
|
93
112
|
|
|
94
|
-
main h1 {
|
|
113
|
+
main > h1 {
|
|
95
114
|
color: #202223;
|
|
115
|
+
font-weight: 900;
|
|
116
|
+
margin-bottom: 24px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
main > p {
|
|
120
|
+
margin-bottom: 16px;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
main > section {
|
|
124
|
+
margin-bottom: 40px;
|
|
96
125
|
}
|
|
97
126
|
|
|
98
127
|
main a {
|
|
99
128
|
color: #475f91;
|
|
100
|
-
font-size:
|
|
129
|
+
font-size: 1rem;
|
|
101
130
|
position: relative;
|
|
131
|
+
font-weight: 500;
|
|
102
132
|
}
|
|
103
133
|
|
|
104
134
|
main a::after {
|
|
@@ -109,7 +139,6 @@ main a::after {
|
|
|
109
139
|
main {
|
|
110
140
|
display: flex;
|
|
111
141
|
flex-direction: column;
|
|
112
|
-
gap: 1rem;
|
|
113
142
|
}
|
|
114
143
|
|
|
115
144
|
.Links ul {
|
|
@@ -135,6 +164,7 @@ main {
|
|
|
135
164
|
margin-bottom: 0.5rem;
|
|
136
165
|
font-size: 1.5rem;
|
|
137
166
|
color: #44474a;
|
|
167
|
+
font-weight: 700;
|
|
138
168
|
}
|
|
139
169
|
|
|
140
170
|
.Banner {
|
|
@@ -147,7 +177,7 @@ main {
|
|
|
147
177
|
box-shadow: 0px 2px 0px rgba(0, 0, 0, 0.1);
|
|
148
178
|
border-radius: 8px;
|
|
149
179
|
font-style: normal;
|
|
150
|
-
font-size:
|
|
180
|
+
font-size: 1rem;
|
|
151
181
|
line-height: 24px;
|
|
152
182
|
color: #5c5f62;
|
|
153
183
|
border: 1px solid #d2d5d8;
|
|
@@ -173,8 +203,9 @@ main {
|
|
|
173
203
|
padding: 2px;
|
|
174
204
|
}
|
|
175
205
|
|
|
176
|
-
.Banner
|
|
206
|
+
.Banner h2 {
|
|
177
207
|
font-size: 1rem;
|
|
208
|
+
color: #202223;
|
|
178
209
|
}
|
|
179
210
|
|
|
180
211
|
.Banner.ErrorBanner {
|
|
@@ -7,6 +7,8 @@ import { IconTwitter } from "../components/IconTwitter.jsx";
|
|
|
7
7
|
import { IconBanner } from "../components/IconBanner.jsx";
|
|
8
8
|
import { IconError } from "../components/IconError.jsx";
|
|
9
9
|
import favicon from "../assets/favicon.svg";
|
|
10
|
+
import interVariableFont from "../assets/inter-variable-font.ttf";
|
|
11
|
+
import jetbrainsmonoVariableFont from "../assets/jetbrainsmono-variable-font.ttf";
|
|
10
12
|
const meta = () => {
|
|
11
13
|
return {
|
|
12
14
|
title: "Hydrogen",
|
|
@@ -20,6 +22,20 @@ const links = () => [
|
|
|
20
22
|
rel: "icon",
|
|
21
23
|
type: "image/svg+xml",
|
|
22
24
|
href: favicon
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
rel: "preload",
|
|
28
|
+
href: interVariableFont,
|
|
29
|
+
as: "font",
|
|
30
|
+
type: "font/ttf",
|
|
31
|
+
crossOrigin: "anonymous"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
rel: "preload",
|
|
35
|
+
href: jetbrainsmonoVariableFont,
|
|
36
|
+
as: "font",
|
|
37
|
+
type: "font/ttf",
|
|
38
|
+
crossOrigin: "anonymous"
|
|
23
39
|
}
|
|
24
40
|
];
|
|
25
41
|
async function loader({ context }) {
|
|
@@ -44,24 +60,31 @@ function Index() {
|
|
|
44
60
|
{shopName || "Hydrogen"}
|
|
45
61
|
</h1>
|
|
46
62
|
<p>Welcome to your new custom storefront</p>
|
|
47
|
-
<section className="Banner">
|
|
63
|
+
{configDone ? null : <section className="Banner">
|
|
48
64
|
<div>
|
|
49
65
|
<IconBanner />
|
|
50
66
|
<h2>Configure storefront token</h2>
|
|
51
67
|
</div>
|
|
52
68
|
<p>
|
|
53
|
-
{"You\u2019re seeing this because you have not yet configured your storefront token.
|
|
69
|
+
{"You\u2019re seeing this because you have not yet configured your storefront token. "}
|
|
70
|
+
<br />
|
|
71
|
+
<br />
|
|
72
|
+
{" To get started, edit "}
|
|
54
73
|
{` `}
|
|
55
74
|
<code>.env</code>
|
|
56
|
-
{". Then, create your first route with the file
|
|
75
|
+
{". Then, create your first route with the file"}
|
|
57
76
|
{` `}
|
|
58
77
|
<code>/app/routes/index.jsx</code>
|
|
59
78
|
{". Learn more about"}
|
|
60
79
|
{` `}
|
|
61
|
-
<a href="https://shopify.dev/custom-storefronts/hydrogen/
|
|
80
|
+
<a target="_blank" rel="norefferer noopener" href="https://shopify.dev/docs/custom-storefronts/hydrogen/environment-variables">editing environment variables</a>
|
|
81
|
+
{` `}
|
|
82
|
+
{"and"}
|
|
83
|
+
{` `}
|
|
84
|
+
<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>
|
|
62
85
|
{"."}
|
|
63
86
|
</p>
|
|
64
|
-
</section>
|
|
87
|
+
</section>}
|
|
65
88
|
<ResourcesLinks />
|
|
66
89
|
</Layout></>;
|
|
67
90
|
}
|
|
@@ -78,9 +101,8 @@ function ErrorPage() {
|
|
|
78
101
|
<p>
|
|
79
102
|
{"Check your domain and API token in your "}
|
|
80
103
|
<code>.env</code>
|
|
81
|
-
{" file.
|
|
82
|
-
|
|
83
|
-
<a target="_blank" rel="norefferer noopener" href="https://shopify.dev/custom-storefronts/hydrogen/getting-started/quickstart">{"how to configure your\xA0storefront"}</a>
|
|
104
|
+
{" file. Learn more about"}
|
|
105
|
+
<a target="_blank" rel="norefferer noopener" href="https://shopify.dev/docs/custom-storefronts/hydrogen/environment-variables">editing environment variables</a>
|
|
84
106
|
{"."}
|
|
85
107
|
</p>
|
|
86
108
|
</section>
|
|
@@ -112,9 +134,9 @@ function Layout({
|
|
|
112
134
|
<h1>{shopName?.toUpperCase()}</h1>
|
|
113
135
|
<p>{"\xA0Dev Mode\xA0"}</p>
|
|
114
136
|
<nav>
|
|
115
|
-
<a href="https://discord.com/invite/shopifydevs"><IconDiscord /></a>
|
|
116
|
-
<a href="https://github.com/Shopify/hydrogen"><IconGithub /></a>
|
|
117
|
-
<a href="https://twitter.com/shopifydevs?lang=en"><IconTwitter /></a>
|
|
137
|
+
<a target="_blank" rel="norefferer noopener" href="https://discord.com/invite/shopifydevs"><IconDiscord /></a>
|
|
138
|
+
<a target="_blank" rel="norefferer noopener" href="https://github.com/Shopify/hydrogen"><IconGithub /></a>
|
|
139
|
+
<a target="_blank" rel="norefferer noopener" href="https://twitter.com/shopifydevs?lang=en"><IconTwitter /></a>
|
|
118
140
|
</nav>
|
|
119
141
|
</header>
|
|
120
142
|
<main>{children}</main>
|
package/oclif.manifest.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"4.0.
|
|
1
|
+
{"version":"4.0.6","commands":{"hydrogen:build":{"id":"hydrogen:build","description":"Builds a Hydrogen storefront for production.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false},"sourcemap":{"name":"sourcemap","type":"boolean","description":"Generate sourcemaps for the build.","allowNo":false},"disable-route-warning":{"name":"disable-route-warning","type":"boolean","description":"Disable warning about missing standard routes.","allowNo":false}},"args":[]},"hydrogen:check":{"id":"hydrogen:check","description":"Returns diagnostic information about a Hydrogen storefront.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false}},"args":[{"name":"resource","description":"The resource to check. Currently only 'routes' is supported.","required":true,"options":["routes"]}]},"hydrogen:dev":{"id":"hydrogen:dev","description":"Runs Hydrogen storefront in an Oxygen worker for development.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false},"port":{"name":"port","type":"option","description":"Port to run the server on.","multiple":false,"default":3000},"disable-virtual-routes":{"name":"disable-virtual-routes","type":"boolean","description":"Disable rendering fallback routes when a route file doesn't exist","allowNo":false}},"args":[]},"hydrogen:init":{"id":"hydrogen:init","description":"Creates a new Hydrogen storefront.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"force":{"name":"force","type":"boolean","char":"f","description":"Overwrite the destination directory and files if they already exist.","allowNo":false},"path":{"name":"path","type":"option","description":"The path to the directory of the new Hydrogen storefront.","multiple":false},"language":{"name":"language","type":"option","description":"Sets the template language to use. One of `js` or `ts`.","multiple":false},"template":{"name":"template","type":"option","description":"Sets the template to use. One of `demo-store` or `hello-world`.","multiple":false},"install-deps":{"name":"install-deps","type":"boolean","description":"Auto install dependencies using the active package manager","allowNo":true}},"args":[]},"hydrogen:preview":{"id":"hydrogen:preview","description":"Runs a Hydrogen storefront in an Oxygen worker for production.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false},"port":{"name":"port","type":"option","description":"Port to run the server on.","multiple":false,"default":3000}},"args":[]},"hydrogen:generate:route":{"id":"hydrogen:generate:route","description":"Generates a standard Shopify route.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"adapter":{"name":"adapter","type":"option","description":"Remix adapter used in the route. The default is `@shopify/remix-oxygen`.","multiple":false},"typescript":{"name":"typescript","type":"boolean","description":"Generate TypeScript files","allowNo":false},"force":{"name":"force","type":"boolean","char":"f","description":"Overwrite the destination directory and files if they already exist.","allowNo":false},"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false}},"args":[{"name":"route","description":"The route to generate. One of home,page,cart,products,collections,policies,robots,sitemap,account,all.","required":true,"options":["home","page","cart","products","collections","policies","robots","sitemap","account","all"]}]},"hydrogen:generate:routes":{"id":"hydrogen:generate:routes","description":"Generates all supported standard shopify routes.","strict":true,"pluginName":"@shopify/cli-hydrogen","pluginAlias":"@shopify/cli-hydrogen","pluginType":"core","aliases":[],"flags":{"adapter":{"name":"adapter","type":"option","description":"Remix adapter used in the route. The default is `@shopify/remix-oxygen`.","multiple":false},"typescript":{"name":"typescript","type":"boolean","description":"Generate TypeScript files","allowNo":false},"force":{"name":"force","type":"boolean","char":"f","description":"Overwrite the destination directory and files if they already exist.","allowNo":false},"path":{"name":"path","type":"option","description":"The path to the directory of the Hydrogen storefront. The default is the current directory.","multiple":false}},"args":[]}}}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"@shopify:registry": "https://registry.npmjs.org"
|
|
6
6
|
},
|
|
7
|
-
"version": "4.0.
|
|
7
|
+
"version": "4.0.6",
|
|
8
8
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"typecheck": "tsc --noEmit",
|
|
14
14
|
"generate:manifest": "oclif manifest",
|
|
15
15
|
"prepack": "npm run build",
|
|
16
|
-
"test": "vitest run",
|
|
17
|
-
"test:watch": "vitest"
|
|
16
|
+
"test": "cross-env SHOPIFY_UNIT_TEST=1 vitest run",
|
|
17
|
+
"test:watch": "cross-env SHOPIFY_UNIT_TEST=1 vitest"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@types/fs-extra": "^9.0.13",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"@remix-run/react": "^1.12.0",
|
|
31
|
-
"@shopify/hydrogen-react": "^2023.1.
|
|
32
|
-
"@shopify/remix-oxygen": "^1.0.
|
|
31
|
+
"@shopify/hydrogen-react": "^2023.1.5",
|
|
32
|
+
"@shopify/remix-oxygen": "^1.0.3"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@oclif/core": "^1.20.4",
|