@shopify/cli-hydrogen 5.0.2 → 5.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/hydrogen/build.js +21 -6
- package/dist/commands/hydrogen/check.js +2 -2
- package/dist/commands/hydrogen/codegen-unstable.js +14 -25
- package/dist/commands/hydrogen/dev.js +55 -43
- 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/preview.js +1 -1
- 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 +264 -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 +216 -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-config.js +135 -0
- package/dist/lib/remix-version-check.js +51 -0
- package/dist/lib/remix-version-check.test.js +38 -0
- package/dist/lib/remix-version-interop.js +6 -6
- package/dist/lib/remix-version-interop.test.js +12 -2
- 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 +20 -21
- 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/config.js +0 -141
- 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
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { describe, beforeEach, vi, it, expect } from 'vitest';
|
|
2
|
+
import { temporaryDirectoryTask } from 'tempy';
|
|
3
|
+
import { getResolvedRoutes, generateRoutes, generateProjectFile } from './generate.js';
|
|
4
|
+
import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
|
|
5
|
+
import { fileExists, readFile, mkdir, writeFile } from '@shopify/cli-kit/node/fs';
|
|
6
|
+
import { joinPath, dirname } from '@shopify/cli-kit/node/path';
|
|
7
|
+
import { getTemplateAppFile } from '../../../lib/build.js';
|
|
8
|
+
import { getRemixConfig } from '../../remix-config.js';
|
|
9
|
+
|
|
10
|
+
const readProjectFile = (dirs, fileBasename, ext = "tsx") => readFile(joinPath(dirs.appDirectory, `${fileBasename}.${ext}`));
|
|
11
|
+
describe("generate/route", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.resetAllMocks();
|
|
14
|
+
vi.mock("@shopify/cli-kit/node/output");
|
|
15
|
+
vi.mock("@shopify/cli-kit/node/ui");
|
|
16
|
+
vi.mock("../../remix-config.js", async () => ({ getRemixConfig: vi.fn() }));
|
|
17
|
+
});
|
|
18
|
+
describe("generateRoutes", () => {
|
|
19
|
+
it("generates all routes with correct configuration", async () => {
|
|
20
|
+
const { resolvedRouteFiles } = await getResolvedRoutes();
|
|
21
|
+
expect(
|
|
22
|
+
resolvedRouteFiles.find((item) => /account_?\.login/.test(item))
|
|
23
|
+
).toBeTruthy();
|
|
24
|
+
await temporaryDirectoryTask(async (tmpDir) => {
|
|
25
|
+
const directories = await createHydrogenFixture(tmpDir, {
|
|
26
|
+
files: [
|
|
27
|
+
["jsconfig.json", JSON.stringify({ compilerOptions: { test: "js" } })],
|
|
28
|
+
[".prettierrc.json", JSON.stringify({ singleQuote: false })]
|
|
29
|
+
],
|
|
30
|
+
templates: resolvedRouteFiles.map(
|
|
31
|
+
(filepath) => ["routes/" + filepath + ".tsx", ""]
|
|
32
|
+
)
|
|
33
|
+
});
|
|
34
|
+
vi.mocked(getRemixConfig).mockResolvedValue(directories);
|
|
35
|
+
const result = await generateRoutes({
|
|
36
|
+
routeName: "all",
|
|
37
|
+
directory: directories.rootDirectory,
|
|
38
|
+
templatesRoot: directories.templatesRoot
|
|
39
|
+
});
|
|
40
|
+
expect(result).toMatchObject(
|
|
41
|
+
expect.objectContaining({
|
|
42
|
+
isTypescript: false,
|
|
43
|
+
transpilerOptions: { test: "js" },
|
|
44
|
+
formatOptions: { singleQuote: false },
|
|
45
|
+
routes: expect.any(Array)
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
expect(result.routes).toHaveLength(
|
|
49
|
+
Object.values(resolvedRouteFiles).length
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
it("figures out the locale if a home route already exists", async () => {
|
|
54
|
+
await temporaryDirectoryTask(async (tmpDir) => {
|
|
55
|
+
const route = "routes/pages.$handle";
|
|
56
|
+
const directories = await createHydrogenFixture(tmpDir, {
|
|
57
|
+
files: [
|
|
58
|
+
["tsconfig.json", JSON.stringify({ compilerOptions: { test: "ts" } })],
|
|
59
|
+
["app/routes/($locale)._index.tsx", "export const test = true;"]
|
|
60
|
+
],
|
|
61
|
+
templates: [[route + ".tsx", `const str = "hello world"`]]
|
|
62
|
+
});
|
|
63
|
+
vi.mocked(getRemixConfig).mockResolvedValue({
|
|
64
|
+
...directories,
|
|
65
|
+
tsconfigPath: "somewhere",
|
|
66
|
+
future: {
|
|
67
|
+
v2_routeConvention: true
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const result = await generateRoutes({
|
|
71
|
+
routeName: ["page"],
|
|
72
|
+
directory: directories.rootDirectory,
|
|
73
|
+
templatesRoot: directories.templatesRoot
|
|
74
|
+
});
|
|
75
|
+
expect(result).toMatchObject(
|
|
76
|
+
expect.objectContaining({
|
|
77
|
+
isTypescript: true,
|
|
78
|
+
transpilerOptions: void 0,
|
|
79
|
+
routes: expect.any(Array),
|
|
80
|
+
formatOptions: expect.any(Object)
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
expect(result.routes).toHaveLength(1);
|
|
84
|
+
expect(result.routes[0]).toMatchObject({
|
|
85
|
+
destinationRoute: expect.stringContaining("($locale).pages.$handle")
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
describe("generateProjectFile", () => {
|
|
91
|
+
it("generates a route file for Remix v1", async () => {
|
|
92
|
+
await temporaryDirectoryTask(async (tmpDir) => {
|
|
93
|
+
const route = "routes/pages.$handle";
|
|
94
|
+
const directories = await createHydrogenFixture(tmpDir, {
|
|
95
|
+
files: [],
|
|
96
|
+
templates: [[route + ".tsx", `const str = "hello world"`]]
|
|
97
|
+
});
|
|
98
|
+
await generateProjectFile(route, {
|
|
99
|
+
...directories,
|
|
100
|
+
v2Flags: {
|
|
101
|
+
isV2RouteConvention: false
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
expect(
|
|
105
|
+
await readProjectFile(directories, route.replace(".", "/"), "jsx")
|
|
106
|
+
).toContain(`const str = 'hello world'`);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
it("generates a route file for Remix v2", async () => {
|
|
110
|
+
await temporaryDirectoryTask(async (tmpDir) => {
|
|
111
|
+
const route = "routes/custom.path.$handle._index";
|
|
112
|
+
const directories = await createHydrogenFixture(tmpDir, {
|
|
113
|
+
files: [],
|
|
114
|
+
templates: [[route + ".tsx", `const str = "hello world"`]]
|
|
115
|
+
});
|
|
116
|
+
await generateProjectFile(route, {
|
|
117
|
+
...directories,
|
|
118
|
+
v2Flags: { isV2RouteConvention: true }
|
|
119
|
+
});
|
|
120
|
+
expect(await readProjectFile(directories, route, "jsx")).toContain(
|
|
121
|
+
`const str = 'hello world'`
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
it("generates route files with locale prefix", async () => {
|
|
126
|
+
await temporaryDirectoryTask(async (tmpDir) => {
|
|
127
|
+
const routeCode = `const str = 'hello world'`;
|
|
128
|
+
const directories = await createHydrogenFixture(tmpDir, {
|
|
129
|
+
files: [],
|
|
130
|
+
templates: [
|
|
131
|
+
["routes/_index.tsx", routeCode],
|
|
132
|
+
["routes/pages.$handle.tsx", routeCode],
|
|
133
|
+
["routes/[robots.txt].tsx", routeCode],
|
|
134
|
+
["routes/[sitemap.xml].tsx", routeCode]
|
|
135
|
+
]
|
|
136
|
+
});
|
|
137
|
+
const localePrefix = "locale";
|
|
138
|
+
await generateProjectFile("routes/_index", {
|
|
139
|
+
...directories,
|
|
140
|
+
v2Flags: { isV2RouteConvention: true },
|
|
141
|
+
localePrefix,
|
|
142
|
+
typescript: true
|
|
143
|
+
});
|
|
144
|
+
await generateProjectFile("routes/pages.$handle", {
|
|
145
|
+
...directories,
|
|
146
|
+
v2Flags: { isV2RouteConvention: false },
|
|
147
|
+
localePrefix,
|
|
148
|
+
typescript: true
|
|
149
|
+
});
|
|
150
|
+
await generateProjectFile("routes/[sitemap.xml]", {
|
|
151
|
+
...directories,
|
|
152
|
+
v2Flags: { isV2RouteConvention: true },
|
|
153
|
+
localePrefix,
|
|
154
|
+
typescript: true
|
|
155
|
+
});
|
|
156
|
+
await generateProjectFile("routes/[robots.txt]", {
|
|
157
|
+
...directories,
|
|
158
|
+
v2Flags: { isV2RouteConvention: true },
|
|
159
|
+
localePrefix,
|
|
160
|
+
typescript: true
|
|
161
|
+
});
|
|
162
|
+
await expect(
|
|
163
|
+
readProjectFile(directories, `routes/($locale)._index`)
|
|
164
|
+
).resolves.toContain(routeCode);
|
|
165
|
+
await expect(
|
|
166
|
+
readProjectFile(directories, `routes/($locale).[sitemap.xml]`)
|
|
167
|
+
).resolves.toContain(routeCode);
|
|
168
|
+
await expect(
|
|
169
|
+
readProjectFile(directories, `routes/[robots.txt]`)
|
|
170
|
+
).resolves.toContain(routeCode);
|
|
171
|
+
await expect(
|
|
172
|
+
readProjectFile(directories, `routes/($locale)/pages/$handle`)
|
|
173
|
+
).resolves.toContain(routeCode);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
it("produces a typescript file when typescript argument is true", async () => {
|
|
177
|
+
await temporaryDirectoryTask(async (tmpDir) => {
|
|
178
|
+
const route = "routes/pages.$handle";
|
|
179
|
+
const directories = await createHydrogenFixture(tmpDir, {
|
|
180
|
+
files: [],
|
|
181
|
+
templates: [[route + ".tsx", 'const str = "hello typescript"']]
|
|
182
|
+
});
|
|
183
|
+
await generateProjectFile(route, {
|
|
184
|
+
...directories,
|
|
185
|
+
typescript: true,
|
|
186
|
+
v2Flags: { isV2RouteConvention: true }
|
|
187
|
+
});
|
|
188
|
+
expect(await readProjectFile(directories, route)).toContain(
|
|
189
|
+
`const str = 'hello typescript'`
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
it("prompts the user if there the file already exists", async () => {
|
|
194
|
+
await temporaryDirectoryTask(async (tmpDir) => {
|
|
195
|
+
vi.mocked(renderConfirmationPrompt).mockImplementationOnce(
|
|
196
|
+
async () => true
|
|
197
|
+
);
|
|
198
|
+
const route = "routes/page.$handle";
|
|
199
|
+
const directories = await createHydrogenFixture(tmpDir, {
|
|
200
|
+
files: [[`app/${route}.jsx`, 'const str = "I exist"']],
|
|
201
|
+
templates: [[route + ".tsx", 'const str = "hello world"']]
|
|
202
|
+
});
|
|
203
|
+
await generateProjectFile(route, {
|
|
204
|
+
...directories,
|
|
205
|
+
v2Flags: { isV2RouteConvention: true }
|
|
206
|
+
});
|
|
207
|
+
expect(renderConfirmationPrompt).toHaveBeenCalledWith(
|
|
208
|
+
expect.objectContaining({
|
|
209
|
+
message: expect.stringContaining("already exists")
|
|
210
|
+
})
|
|
211
|
+
);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
it("does not prompt the user if the force property is true", async () => {
|
|
215
|
+
await temporaryDirectoryTask(async (tmpDir) => {
|
|
216
|
+
vi.mocked(renderConfirmationPrompt).mockImplementationOnce(
|
|
217
|
+
async () => true
|
|
218
|
+
);
|
|
219
|
+
const route = "routes/page.$pageHandle";
|
|
220
|
+
const directories = await createHydrogenFixture(tmpDir, {
|
|
221
|
+
files: [[`app/${route}.jsx`, 'const str = "I exist"']],
|
|
222
|
+
templates: [[route + ".tsx", 'const str = "hello world"']]
|
|
223
|
+
});
|
|
224
|
+
await generateProjectFile(route, {
|
|
225
|
+
...directories,
|
|
226
|
+
force: true
|
|
227
|
+
});
|
|
228
|
+
expect(renderConfirmationPrompt).not.toHaveBeenCalled();
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
it("generates all the route dependencies", async () => {
|
|
232
|
+
await temporaryDirectoryTask(async (tmpDir) => {
|
|
233
|
+
const templates = [
|
|
234
|
+
[
|
|
235
|
+
"routes/pages.$pageHandle.tsx",
|
|
236
|
+
`import Dep from 'some-node-dep';
|
|
237
|
+
import AnotherRoute from './AnotherRoute';
|
|
238
|
+
import Form from '~/components/Form';
|
|
239
|
+
import {
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
Button} from '../components/Button';
|
|
243
|
+
import {stuff} from '../utils';
|
|
244
|
+
import {serverOnly} from '../something.server';
|
|
245
|
+
import styles from '../styles/app.css';
|
|
246
|
+
export {Dep, AnotherRoute, Form, Button, stuff, serverOnly, styles};
|
|
247
|
+
`
|
|
248
|
+
],
|
|
249
|
+
[
|
|
250
|
+
"components/Form.tsx",
|
|
251
|
+
`import {Button} from './Button';
|
|
252
|
+
import {Text} from './Text';
|
|
253
|
+
export {Button, Text};
|
|
254
|
+
`
|
|
255
|
+
],
|
|
256
|
+
["components/Button.tsx", `export const Button = '';
|
|
257
|
+
`],
|
|
258
|
+
["components/Text.tsx", `export const Text = '';
|
|
259
|
+
`],
|
|
260
|
+
["utils/index.ts", `export {stuff} from './stuff';
|
|
261
|
+
`],
|
|
262
|
+
["utils/stuff.ts", `export const stuff = '';
|
|
263
|
+
`],
|
|
264
|
+
["something.server.ts", `export const serverOnly = '';
|
|
265
|
+
`],
|
|
266
|
+
["styles/app.css", `.red{color:red;}`]
|
|
267
|
+
];
|
|
268
|
+
const directories = await createHydrogenFixture(tmpDir, { templates });
|
|
269
|
+
vi.mocked(getRemixConfig).mockResolvedValue(directories);
|
|
270
|
+
await generateProjectFile("routes/pages.$pageHandle", {
|
|
271
|
+
...directories,
|
|
272
|
+
v2Flags: { isV2RouteConvention: true },
|
|
273
|
+
force: true
|
|
274
|
+
});
|
|
275
|
+
await Promise.all(
|
|
276
|
+
templates.map(async ([file, content]) => {
|
|
277
|
+
const actualFile = joinPath(
|
|
278
|
+
directories.appDirectory,
|
|
279
|
+
file.replace(".ts", ".js")
|
|
280
|
+
);
|
|
281
|
+
await expect(fileExists(actualFile)).resolves.toBeTruthy();
|
|
282
|
+
await expect(readFile(actualFile)).resolves.toEqual(
|
|
283
|
+
content.replace(/\{\n+/, "{")
|
|
284
|
+
);
|
|
285
|
+
})
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
async function createHydrogenFixture(directory, {
|
|
292
|
+
files = [],
|
|
293
|
+
templates = []
|
|
294
|
+
}) {
|
|
295
|
+
const projectDir = "project";
|
|
296
|
+
for (const item of files) {
|
|
297
|
+
const [filePath, fileContent] = item;
|
|
298
|
+
const fullFilePath = joinPath(directory, projectDir, filePath);
|
|
299
|
+
await mkdir(dirname(fullFilePath));
|
|
300
|
+
await writeFile(fullFilePath, fileContent);
|
|
301
|
+
}
|
|
302
|
+
for (const item of templates) {
|
|
303
|
+
const [filePath, fileContent] = item;
|
|
304
|
+
const fullFilePath = getTemplateAppFile(filePath, directory);
|
|
305
|
+
await mkdir(dirname(fullFilePath));
|
|
306
|
+
await writeFile(fullFilePath, fileContent);
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
rootDirectory: joinPath(directory, projectDir),
|
|
310
|
+
appDirectory: joinPath(directory, projectDir, "app"),
|
|
311
|
+
templatesRoot: directory
|
|
312
|
+
};
|
|
313
|
+
}
|
package/dist/lib/shell.js
CHANGED
|
@@ -6,7 +6,7 @@ import { getPackageManager } from '@shopify/cli-kit/node/node-package-manager';
|
|
|
6
6
|
import { execAsync } from './process.js';
|
|
7
7
|
|
|
8
8
|
const ALIAS_NAME = "h2";
|
|
9
|
-
const isWindows = () =>
|
|
9
|
+
const isWindows = () => os.platform() === "win32";
|
|
10
10
|
const isGitBash = () => !!process.env.MINGW_PREFIX;
|
|
11
11
|
function resolveFromHome(filepath) {
|
|
12
12
|
if (filepath[0] === "~") {
|
|
@@ -107,15 +107,62 @@ async function hasCliAlias() {
|
|
|
107
107
|
return false;
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
async function
|
|
111
|
-
|
|
110
|
+
async function createPlatformShortcut() {
|
|
111
|
+
const shortcuts = isWindows() && !isGitBash() ? await createShortcutsForWindows() : await createShortcutsForUnix();
|
|
112
|
+
return shortcuts;
|
|
113
|
+
}
|
|
114
|
+
const BASH_ZSH_COMMAND = `
|
|
115
|
+
# Shopify Hydrogen alias to local projects
|
|
116
|
+
alias ${ALIAS_NAME}='$(npm prefix -s)/node_modules/.bin/shopify hydrogen'`;
|
|
117
|
+
const FISH_FUNCTION = `
|
|
118
|
+
function ${ALIAS_NAME} --wraps='shopify hydrogen' --description 'Shortcut for the Hydrogen CLI'
|
|
119
|
+
set npmPrefix (npm prefix -s)
|
|
120
|
+
$npmPrefix/node_modules/.bin/shopify hydrogen $argv
|
|
121
|
+
end
|
|
122
|
+
`;
|
|
123
|
+
async function createShortcutsForUnix() {
|
|
124
|
+
const shells = [];
|
|
125
|
+
if (await shellWriteAlias("zsh", ALIAS_NAME, BASH_ZSH_COMMAND)) {
|
|
126
|
+
shells.push("zsh");
|
|
127
|
+
}
|
|
128
|
+
if (await shellWriteAlias("bash", ALIAS_NAME, BASH_ZSH_COMMAND)) {
|
|
129
|
+
shells.push("bash");
|
|
130
|
+
}
|
|
131
|
+
if (await shellWriteAlias("fish", ALIAS_NAME, FISH_FUNCTION)) {
|
|
132
|
+
shells.push("fish");
|
|
133
|
+
}
|
|
134
|
+
return shells;
|
|
135
|
+
}
|
|
136
|
+
const PS_FUNCTION = `function Invoke-Local-H2 {$npmPrefix = npm prefix -s; Invoke-Expression "$npmPrefix\\node_modules\\.bin\\shopify.ps1 hydrogen $Args"}; Set-Alias -Name ${ALIAS_NAME} -Value Invoke-Local-H2`;
|
|
137
|
+
const PS_APPEND_PROFILE_COMMAND = `
|
|
138
|
+
if (!(Test-Path -Path $PROFILE)) {
|
|
139
|
+
New-Item -ItemType File -Path $PROFILE -Force
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
$profileContent = Get-Content -Path $PROFILE
|
|
143
|
+
if (!$profileContent -or $profileContent -NotLike '*Invoke-Local-H2*') {
|
|
144
|
+
Add-Content -Path $PROFILE -Value '${PS_FUNCTION}'
|
|
145
|
+
}
|
|
146
|
+
`;
|
|
147
|
+
async function createShortcutsForWindows() {
|
|
148
|
+
const shells = [];
|
|
149
|
+
if (await shellRunScript(PS_APPEND_PROFILE_COMMAND, "powershell.exe")) {
|
|
150
|
+
shells.push("PowerShell");
|
|
151
|
+
}
|
|
152
|
+
if (await shellRunScript(PS_APPEND_PROFILE_COMMAND, "pwsh.exe")) {
|
|
153
|
+
shells.push("PowerShell 7+");
|
|
154
|
+
}
|
|
155
|
+
return shells;
|
|
156
|
+
}
|
|
157
|
+
async function getCliCommand(directory = process.cwd(), forcePkgManager) {
|
|
158
|
+
if (!forcePkgManager && await hasCliAlias()) {
|
|
112
159
|
return ALIAS_NAME;
|
|
113
160
|
}
|
|
114
161
|
let cli = "npx";
|
|
115
|
-
const pkgManager = await getPackageManager(
|
|
162
|
+
const pkgManager = forcePkgManager ?? await getPackageManager(directory).catch(() => null);
|
|
116
163
|
if (pkgManager === "pnpm" || pkgManager === "yarn")
|
|
117
164
|
cli = pkgManager;
|
|
118
165
|
return `${cli} shopify hydrogen`;
|
|
119
166
|
}
|
|
120
167
|
|
|
121
|
-
export { ALIAS_NAME, getCliCommand, isGitBash, isWindows, shellRunScript, shellWriteAlias };
|
|
168
|
+
export { ALIAS_NAME, createPlatformShortcut, getCliCommand, isGitBash, isWindows, shellRunScript, shellWriteAlias };
|
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 };
|