@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,227 @@
|
|
|
1
|
+
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
2
|
+
import { joinPath, relativePath } from '@shopify/cli-kit/node/path';
|
|
3
|
+
import { fileExists } from '@shopify/cli-kit/node/fs';
|
|
4
|
+
import { ts, tsx, js, jsx } from '@ast-grep/napi';
|
|
5
|
+
import { replaceFileContent, findFileWithExtension } from '../../file.js';
|
|
6
|
+
|
|
7
|
+
const astGrep = { ts, tsx, js, jsx };
|
|
8
|
+
async function replaceServerI18n({ rootDirectory, serverEntryPoint = "server" }, formatConfig, localeExtractImplementation) {
|
|
9
|
+
const { filepath, astType } = await findEntryFile({
|
|
10
|
+
rootDirectory,
|
|
11
|
+
serverEntryPoint
|
|
12
|
+
});
|
|
13
|
+
await replaceFileContent(filepath, formatConfig, async (content) => {
|
|
14
|
+
const root = astGrep[astType].parse(content).root();
|
|
15
|
+
const requestIdentifier = root.find({
|
|
16
|
+
rule: {
|
|
17
|
+
kind: "identifier",
|
|
18
|
+
inside: {
|
|
19
|
+
kind: "formal_parameters",
|
|
20
|
+
stopBy: "end",
|
|
21
|
+
inside: {
|
|
22
|
+
kind: "method_definition",
|
|
23
|
+
stopBy: "end",
|
|
24
|
+
has: {
|
|
25
|
+
kind: "property_identifier",
|
|
26
|
+
regex: "^fetch$"
|
|
27
|
+
},
|
|
28
|
+
inside: {
|
|
29
|
+
kind: "export_statement",
|
|
30
|
+
stopBy: "end"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
const requestIdentifierName = requestIdentifier?.text() ?? "request";
|
|
37
|
+
const i18nFunctionName = localeExtractImplementation.match(
|
|
38
|
+
/^(export )?function (\w+)/m
|
|
39
|
+
)?.[2];
|
|
40
|
+
if (!i18nFunctionName) {
|
|
41
|
+
throw new Error("Could not find the i18n function name");
|
|
42
|
+
}
|
|
43
|
+
const i18nFunctionCall = `${i18nFunctionName}(${requestIdentifierName})`;
|
|
44
|
+
const hydrogenImportPath = "@shopify/hydrogen";
|
|
45
|
+
const hydrogenImportName = "createStorefrontClient";
|
|
46
|
+
const importSpecifier = root.find({
|
|
47
|
+
rule: {
|
|
48
|
+
kind: "import_specifier",
|
|
49
|
+
inside: {
|
|
50
|
+
kind: "import_statement",
|
|
51
|
+
stopBy: "end",
|
|
52
|
+
has: {
|
|
53
|
+
kind: "string_fragment",
|
|
54
|
+
stopBy: "end",
|
|
55
|
+
regex: `^${hydrogenImportPath}$`
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
has: {
|
|
59
|
+
kind: "identifier",
|
|
60
|
+
regex: `^${hydrogenImportName}`
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
let [importName, importAlias] = importSpecifier?.text().split(/\s+as\s+/) || [];
|
|
65
|
+
importName = importAlias ?? importName;
|
|
66
|
+
if (!importName) {
|
|
67
|
+
throw new AbortError(
|
|
68
|
+
`Could not find a Hydrogen import in ${serverEntryPoint}`,
|
|
69
|
+
`Please import "${hydrogenImportName}" from "${hydrogenImportPath}"`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
const argumentObject = root.find({
|
|
73
|
+
rule: {
|
|
74
|
+
kind: "object",
|
|
75
|
+
inside: {
|
|
76
|
+
kind: "arguments",
|
|
77
|
+
inside: {
|
|
78
|
+
kind: "call_expression",
|
|
79
|
+
has: {
|
|
80
|
+
kind: "identifier",
|
|
81
|
+
regex: importName
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
if (!argumentObject) {
|
|
88
|
+
throw new AbortError(
|
|
89
|
+
`Could not find a Hydrogen client instantiation with an inline object as argument in ${serverEntryPoint}`,
|
|
90
|
+
`Please add a call to ${importName}({...})`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
const i18nProperty = argumentObject.find({
|
|
94
|
+
rule: {
|
|
95
|
+
kind: "property_identifier",
|
|
96
|
+
regex: "^i18n$"
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const i18nValue = i18nProperty?.next()?.next();
|
|
100
|
+
if (i18nValue) {
|
|
101
|
+
if (i18nValue.text().includes(i18nFunctionName)) {
|
|
102
|
+
throw new AbortError(
|
|
103
|
+
"An i18n strategy is already set up.",
|
|
104
|
+
`Remove the existing i18n property in the ${importName}({...}) call to set up a new one.`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
const { start, end } = i18nValue.range();
|
|
108
|
+
content = content.slice(0, start.index) + i18nFunctionCall + content.slice(end.index);
|
|
109
|
+
} else {
|
|
110
|
+
const { end } = argumentObject.range();
|
|
111
|
+
const firstPart = content.slice(0, end.index - 1);
|
|
112
|
+
content = firstPart + ((/,\s*$/.test(firstPart) ? "" : ",") + `i18n: ${i18nFunctionCall}`) + content.slice(end.index - 1);
|
|
113
|
+
}
|
|
114
|
+
const importTypes = localeExtractImplementation.match(
|
|
115
|
+
/import\s+type\s+[^;]+?;/
|
|
116
|
+
)?.[0];
|
|
117
|
+
if (importTypes) {
|
|
118
|
+
localeExtractImplementation = localeExtractImplementation.replace(
|
|
119
|
+
importTypes,
|
|
120
|
+
""
|
|
121
|
+
);
|
|
122
|
+
const lastImportNode = root.findAll({ rule: { kind: "import_statement" } }).pop();
|
|
123
|
+
if (lastImportNode) {
|
|
124
|
+
const lastImportContent = lastImportNode.text();
|
|
125
|
+
content = content.replace(
|
|
126
|
+
lastImportContent,
|
|
127
|
+
lastImportContent + "\n" + importTypes.replace(
|
|
128
|
+
/'[^']+'/,
|
|
129
|
+
`'@shopify/hydrogen/storefront-api-types'`
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return content + `
|
|
135
|
+
|
|
136
|
+
${localeExtractImplementation.replace(/^export function/m, "function").replace(/^export {.*?;/m, "")}
|
|
137
|
+
`;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
async function replaceRemixEnv({ rootDirectory, serverEntryPoint }, formatConfig, localeExtractImplementation) {
|
|
141
|
+
const remixEnvPath = joinPath(rootDirectory, "remix.env.d.ts");
|
|
142
|
+
if (!await fileExists(remixEnvPath)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const i18nTypeName = localeExtractImplementation.match(/export type (\w+)/)?.[1];
|
|
146
|
+
if (!i18nTypeName) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const { filepath: entryFilepath } = await findEntryFile({
|
|
150
|
+
rootDirectory,
|
|
151
|
+
serverEntryPoint
|
|
152
|
+
});
|
|
153
|
+
const relativePathToEntry = relativePath(
|
|
154
|
+
rootDirectory,
|
|
155
|
+
entryFilepath
|
|
156
|
+
).replace(/.[tj]sx?$/, "");
|
|
157
|
+
await replaceFileContent(remixEnvPath, formatConfig, (content) => {
|
|
158
|
+
if (content.includes(`Storefront<`))
|
|
159
|
+
return;
|
|
160
|
+
const root = astGrep.ts.parse(content).root();
|
|
161
|
+
const storefrontTypeNode = root.find({
|
|
162
|
+
rule: {
|
|
163
|
+
kind: "property_signature",
|
|
164
|
+
has: {
|
|
165
|
+
kind: "type_annotation",
|
|
166
|
+
has: {
|
|
167
|
+
regex: "^Storefront$"
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
inside: {
|
|
171
|
+
kind: "interface_declaration",
|
|
172
|
+
stopBy: "end",
|
|
173
|
+
regex: "AppLoadContext"
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
if (!storefrontTypeNode) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const { end } = storefrontTypeNode.range();
|
|
181
|
+
content = content.slice(0, end.index) + `<${i18nTypeName}>` + content.slice(end.index);
|
|
182
|
+
const serverImportNode = root.findAll({
|
|
183
|
+
rule: {
|
|
184
|
+
kind: "import_statement",
|
|
185
|
+
has: {
|
|
186
|
+
kind: "string_fragment",
|
|
187
|
+
stopBy: "end",
|
|
188
|
+
regex: `^(\\./)?${relativePathToEntry.replaceAll(
|
|
189
|
+
".",
|
|
190
|
+
"\\."
|
|
191
|
+
)}(\\.[jt]sx?)?$`
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}).pop();
|
|
195
|
+
if (serverImportNode) {
|
|
196
|
+
content = content.replace(
|
|
197
|
+
serverImportNode.text(),
|
|
198
|
+
serverImportNode.text().replace("{", `{${i18nTypeName},`)
|
|
199
|
+
);
|
|
200
|
+
} else {
|
|
201
|
+
const lastImportNode = root.findAll({ rule: { kind: "import_statement" } }).pop() ?? root.findAll({ rule: { kind: "comment", regex: "^/// <reference" } }).pop();
|
|
202
|
+
const { end: end2 } = lastImportNode?.range() ?? { end: { index: 0 } };
|
|
203
|
+
const typeImport = `
|
|
204
|
+
import type {${i18nTypeName}} from './${serverEntryPoint.replace(
|
|
205
|
+
/\.[jt]s$/,
|
|
206
|
+
""
|
|
207
|
+
)}';`;
|
|
208
|
+
content = content.slice(0, end2.index) + typeImport + content.slice(end2.index);
|
|
209
|
+
}
|
|
210
|
+
return content;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
async function findEntryFile({
|
|
214
|
+
rootDirectory,
|
|
215
|
+
serverEntryPoint = "server"
|
|
216
|
+
}) {
|
|
217
|
+
const match = serverEntryPoint.match(/\.([jt]sx?)$/)?.[1];
|
|
218
|
+
const { filepath, astType } = match ? { filepath: joinPath(rootDirectory, serverEntryPoint), astType: match } : await findFileWithExtension(rootDirectory, serverEntryPoint);
|
|
219
|
+
if (!filepath || !astType) {
|
|
220
|
+
throw new AbortError(
|
|
221
|
+
`Could not find a server entry point at ${serverEntryPoint}`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
return { filepath, astType };
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export { replaceRemixEnv, replaceServerI18n };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getLocaleFromRequest } from './templates/subdomains.js';
|
|
3
|
+
|
|
4
|
+
describe("Setup i18n with subdomains", () => {
|
|
5
|
+
it("extracts the locale from the subdomain", () => {
|
|
6
|
+
expect(
|
|
7
|
+
getLocaleFromRequest(new Request("https://example.com"))
|
|
8
|
+
).toMatchObject({
|
|
9
|
+
language: "EN",
|
|
10
|
+
country: "US"
|
|
11
|
+
});
|
|
12
|
+
expect(
|
|
13
|
+
getLocaleFromRequest(new Request("https://jp.example.com"))
|
|
14
|
+
).toMatchObject({
|
|
15
|
+
language: "JA",
|
|
16
|
+
country: "JP"
|
|
17
|
+
});
|
|
18
|
+
expect(
|
|
19
|
+
getLocaleFromRequest(new Request("https://es.sub.example.com"))
|
|
20
|
+
).toMatchObject({
|
|
21
|
+
language: "ES",
|
|
22
|
+
country: "ES"
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { getLocaleFromRequest } from './templates/subfolders.js';
|
|
3
|
+
|
|
4
|
+
describe("Setup i18n with subfolders", () => {
|
|
5
|
+
it("extracts the locale from the pathname", () => {
|
|
6
|
+
expect(
|
|
7
|
+
getLocaleFromRequest(new Request("https://example.com"))
|
|
8
|
+
).toMatchObject({
|
|
9
|
+
language: "EN",
|
|
10
|
+
country: "US"
|
|
11
|
+
});
|
|
12
|
+
expect(
|
|
13
|
+
getLocaleFromRequest(new Request("https://example.com/ja-jp"))
|
|
14
|
+
).toMatchObject({
|
|
15
|
+
language: "JA",
|
|
16
|
+
country: "JP"
|
|
17
|
+
});
|
|
18
|
+
expect(
|
|
19
|
+
getLocaleFromRequest(new Request("https://example.com/es-es/path"))
|
|
20
|
+
).toMatchObject({
|
|
21
|
+
language: "ES",
|
|
22
|
+
country: "ES"
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function getLocaleFromRequest(request) {
|
|
2
|
+
const defaultLocale = { language: "EN", country: "US" };
|
|
3
|
+
const supportedLocales = {
|
|
4
|
+
ES: "ES",
|
|
5
|
+
FR: "FR",
|
|
6
|
+
DE: "DE",
|
|
7
|
+
JP: "JA"
|
|
8
|
+
};
|
|
9
|
+
const url = new URL(request.url);
|
|
10
|
+
const domain = url.hostname.split(".").pop()?.toUpperCase();
|
|
11
|
+
return supportedLocales[domain] ? { language: supportedLocales[domain], country: domain } : defaultLocale;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { getLocaleFromRequest };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type {LanguageCode, CountryCode} from '../mock-i18n-types.js';
|
|
2
|
+
|
|
3
|
+
export type I18nLocale = {language: LanguageCode; country: CountryCode};
|
|
4
|
+
|
|
5
|
+
function getLocaleFromRequest(request: Request): I18nLocale {
|
|
6
|
+
const defaultLocale: I18nLocale = {language: 'EN', country: 'US'};
|
|
7
|
+
const supportedLocales = {
|
|
8
|
+
ES: 'ES',
|
|
9
|
+
FR: 'FR',
|
|
10
|
+
DE: 'DE',
|
|
11
|
+
JP: 'JA',
|
|
12
|
+
} as Record<CountryCode, LanguageCode>;
|
|
13
|
+
|
|
14
|
+
const url = new URL(request.url);
|
|
15
|
+
const domain = url.hostname
|
|
16
|
+
.split('.')
|
|
17
|
+
.pop()
|
|
18
|
+
?.toUpperCase() as keyof typeof supportedLocales;
|
|
19
|
+
|
|
20
|
+
return supportedLocales[domain]
|
|
21
|
+
? {language: supportedLocales[domain], country: domain}
|
|
22
|
+
: defaultLocale;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {getLocaleFromRequest};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function getLocaleFromRequest(request) {
|
|
2
|
+
const defaultLocale = { language: "EN", country: "US" };
|
|
3
|
+
const supportedLocales = {
|
|
4
|
+
ES: "ES",
|
|
5
|
+
FR: "FR",
|
|
6
|
+
DE: "DE",
|
|
7
|
+
JP: "JA"
|
|
8
|
+
};
|
|
9
|
+
const url = new URL(request.url);
|
|
10
|
+
const firstSubdomain = url.hostname.split(".")[0]?.toUpperCase();
|
|
11
|
+
return supportedLocales[firstSubdomain] ? { language: supportedLocales[firstSubdomain], country: firstSubdomain } : defaultLocale;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { getLocaleFromRequest };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type {LanguageCode, CountryCode} from '../mock-i18n-types.js';
|
|
2
|
+
|
|
3
|
+
export type I18nLocale = {language: LanguageCode; country: CountryCode};
|
|
4
|
+
|
|
5
|
+
function getLocaleFromRequest(request: Request): I18nLocale {
|
|
6
|
+
const defaultLocale: I18nLocale = {language: 'EN', country: 'US'};
|
|
7
|
+
const supportedLocales = {
|
|
8
|
+
ES: 'ES',
|
|
9
|
+
FR: 'FR',
|
|
10
|
+
DE: 'DE',
|
|
11
|
+
JP: 'JA',
|
|
12
|
+
} as Record<CountryCode, LanguageCode>;
|
|
13
|
+
|
|
14
|
+
const url = new URL(request.url);
|
|
15
|
+
const firstSubdomain = url.hostname
|
|
16
|
+
.split('.')[0]
|
|
17
|
+
?.toUpperCase() as keyof typeof supportedLocales;
|
|
18
|
+
|
|
19
|
+
return supportedLocales[firstSubdomain]
|
|
20
|
+
? {language: supportedLocales[firstSubdomain], country: firstSubdomain}
|
|
21
|
+
: defaultLocale;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export {getLocaleFromRequest};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function getLocaleFromRequest(request) {
|
|
2
|
+
const url = new URL(request.url);
|
|
3
|
+
const firstPathPart = url.pathname.split("/")[1]?.toUpperCase() ?? "";
|
|
4
|
+
let pathPrefix = "";
|
|
5
|
+
let language = "EN";
|
|
6
|
+
let country = "US";
|
|
7
|
+
if (/^[A-Z]{2}-[A-Z]{2}$/i.test(firstPathPart)) {
|
|
8
|
+
pathPrefix = "/" + firstPathPart;
|
|
9
|
+
[language, country] = firstPathPart.split("-");
|
|
10
|
+
}
|
|
11
|
+
return { language, country, pathPrefix };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { getLocaleFromRequest };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type {LanguageCode, CountryCode} from '../mock-i18n-types.js';
|
|
2
|
+
|
|
3
|
+
export type I18nLocale = {
|
|
4
|
+
language: LanguageCode;
|
|
5
|
+
country: CountryCode;
|
|
6
|
+
pathPrefix: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
function getLocaleFromRequest(request: Request): I18nLocale {
|
|
10
|
+
const url = new URL(request.url);
|
|
11
|
+
const firstPathPart = url.pathname.split('/')[1]?.toUpperCase() ?? '';
|
|
12
|
+
|
|
13
|
+
let pathPrefix = '';
|
|
14
|
+
let language: LanguageCode = 'EN';
|
|
15
|
+
let country: CountryCode = 'US';
|
|
16
|
+
|
|
17
|
+
if (/^[A-Z]{2}-[A-Z]{2}$/i.test(firstPathPart)) {
|
|
18
|
+
pathPrefix = '/' + firstPathPart;
|
|
19
|
+
[language, country] = firstPathPart.split('-') as [
|
|
20
|
+
LanguageCode,
|
|
21
|
+
CountryCode,
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return {language, country, pathPrefix};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export {getLocaleFromRequest};
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { readdir } from 'fs/promises';
|
|
2
|
+
import { fileExists, mkdir, copyFile, readFile, writeFile } from '@shopify/cli-kit/node/fs';
|
|
3
|
+
import { joinPath, relativizePath, dirname, relativePath, resolvePath, basename } from '@shopify/cli-kit/node/path';
|
|
4
|
+
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
5
|
+
import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
|
|
6
|
+
import { transpileFile } from '../../../lib/transpile-ts.js';
|
|
7
|
+
import { getCodeFormatOptions, formatCode } from '../../../lib/format-code.js';
|
|
8
|
+
import { getTemplateAppFile, GENERATOR_ROUTE_DIR, getStarterDir } from '../../../lib/build.js';
|
|
9
|
+
import { getV2Flags, convertTemplateToRemixVersion, convertRouteToV1 } from '../../../lib/remix-version-interop.js';
|
|
10
|
+
import { getRemixConfig } from '../../remix-config.js';
|
|
11
|
+
import { findFileWithExtension } from '../../file.js';
|
|
12
|
+
|
|
13
|
+
const NO_LOCALE_PATTERNS = [/robots\.txt/];
|
|
14
|
+
const ROUTE_MAP = {
|
|
15
|
+
home: ["_index", "$"],
|
|
16
|
+
page: "pages*",
|
|
17
|
+
cart: "cart",
|
|
18
|
+
products: "products*",
|
|
19
|
+
collections: "collections*",
|
|
20
|
+
policies: "policies*",
|
|
21
|
+
blogs: "blogs*",
|
|
22
|
+
account: "account*",
|
|
23
|
+
search: ["search", "api.predictive-search"],
|
|
24
|
+
robots: "[robots.txt]",
|
|
25
|
+
sitemap: "[sitemap.xml]"
|
|
26
|
+
};
|
|
27
|
+
let allRouteTemplateFiles = [];
|
|
28
|
+
async function getResolvedRoutes(routeKeys = Object.keys(ROUTE_MAP)) {
|
|
29
|
+
if (allRouteTemplateFiles.length === 0) {
|
|
30
|
+
allRouteTemplateFiles = (await readdir(getTemplateAppFile(GENERATOR_ROUTE_DIR))).map((item) => item.replace(/\.tsx?$/, ""));
|
|
31
|
+
}
|
|
32
|
+
const routeGroups = {};
|
|
33
|
+
const resolvedRouteFiles = [];
|
|
34
|
+
for (const key of routeKeys) {
|
|
35
|
+
routeGroups[key] = [];
|
|
36
|
+
const value = ROUTE_MAP[key];
|
|
37
|
+
if (!value) {
|
|
38
|
+
throw new AbortError(
|
|
39
|
+
`No route found for ${key}. Try one of ${ALL_ROUTE_CHOICES.join()}.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
const routes = Array.isArray(value) ? value : [value];
|
|
43
|
+
for (const route of routes) {
|
|
44
|
+
const routePrefix = route.replace("*", "");
|
|
45
|
+
routeGroups[key].push(
|
|
46
|
+
...allRouteTemplateFiles.filter((file) => file.startsWith(routePrefix))
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
resolvedRouteFiles.push(...routeGroups[key]);
|
|
50
|
+
}
|
|
51
|
+
return { routeGroups, resolvedRouteFiles };
|
|
52
|
+
}
|
|
53
|
+
const ALL_ROUTE_CHOICES = [...Object.keys(ROUTE_MAP), "all"];
|
|
54
|
+
async function generateRoutes(options) {
|
|
55
|
+
const { routeGroups, resolvedRouteFiles } = options.routeName === "all" ? await getResolvedRoutes() : await getResolvedRoutes([options.routeName]);
|
|
56
|
+
const { rootDirectory, appDirectory, future, tsconfigPath } = await getRemixConfig(options.directory);
|
|
57
|
+
const routesArray = resolvedRouteFiles.flatMap(
|
|
58
|
+
(item) => GENERATOR_ROUTE_DIR + "/" + item
|
|
59
|
+
);
|
|
60
|
+
const v2Flags = await getV2Flags(rootDirectory, future);
|
|
61
|
+
const formatOptions = await getCodeFormatOptions(rootDirectory);
|
|
62
|
+
const localePrefix = await getLocalePrefix(
|
|
63
|
+
appDirectory,
|
|
64
|
+
options,
|
|
65
|
+
v2Flags.isV2RouteConvention
|
|
66
|
+
);
|
|
67
|
+
const typescript = options.typescript ?? !!tsconfigPath;
|
|
68
|
+
const transpilerOptions = typescript ? void 0 : await getJsTranspilerOptions(rootDirectory);
|
|
69
|
+
const routes = [];
|
|
70
|
+
for (const route of routesArray) {
|
|
71
|
+
routes.push(
|
|
72
|
+
await generateProjectFile(route, {
|
|
73
|
+
...options,
|
|
74
|
+
typescript,
|
|
75
|
+
localePrefix,
|
|
76
|
+
rootDirectory,
|
|
77
|
+
appDirectory,
|
|
78
|
+
formatOptions,
|
|
79
|
+
transpilerOptions,
|
|
80
|
+
v2Flags
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
routes,
|
|
86
|
+
routeGroups,
|
|
87
|
+
isTypescript: typescript,
|
|
88
|
+
transpilerOptions,
|
|
89
|
+
v2Flags,
|
|
90
|
+
formatOptions
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async function getLocalePrefix(appDirectory, { localePrefix, routeName }, isV2RouteConvention = true) {
|
|
94
|
+
if (localePrefix)
|
|
95
|
+
return localePrefix;
|
|
96
|
+
if (localePrefix !== void 0 || routeName === "all")
|
|
97
|
+
return;
|
|
98
|
+
const existingFiles = await readdir(joinPath(appDirectory, "routes")).catch(
|
|
99
|
+
() => []
|
|
100
|
+
);
|
|
101
|
+
const homeRouteWithLocaleRE = isV2RouteConvention ? /^\(\$(\w+)\)\._index.[jt]sx?$/ : /^\(\$(\w+)\)$/;
|
|
102
|
+
const homeRouteWithLocale = existingFiles.find(
|
|
103
|
+
(file) => homeRouteWithLocaleRE.test(file)
|
|
104
|
+
);
|
|
105
|
+
if (homeRouteWithLocale) {
|
|
106
|
+
return homeRouteWithLocale.match(homeRouteWithLocaleRE)?.[1];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function generateProjectFile(routeFrom, {
|
|
110
|
+
rootDirectory,
|
|
111
|
+
appDirectory,
|
|
112
|
+
typescript,
|
|
113
|
+
force,
|
|
114
|
+
adapter,
|
|
115
|
+
templatesRoot = getStarterDir(),
|
|
116
|
+
transpilerOptions,
|
|
117
|
+
formatOptions,
|
|
118
|
+
localePrefix,
|
|
119
|
+
v2Flags = {},
|
|
120
|
+
signal
|
|
121
|
+
}) {
|
|
122
|
+
const extension = (routeFrom.match(/(\.[jt]sx?)$/) ?? [])[1] ?? ".tsx";
|
|
123
|
+
routeFrom = routeFrom.replace(extension, "");
|
|
124
|
+
const routeTemplatePath = getTemplateAppFile(
|
|
125
|
+
routeFrom + extension,
|
|
126
|
+
templatesRoot
|
|
127
|
+
);
|
|
128
|
+
const allFilesToGenerate = await findRouteDependencies(
|
|
129
|
+
routeTemplatePath,
|
|
130
|
+
getTemplateAppFile("", templatesRoot)
|
|
131
|
+
);
|
|
132
|
+
const routeDestinationPath = joinPath(
|
|
133
|
+
appDirectory,
|
|
134
|
+
getDestinationRoute(routeFrom, localePrefix, v2Flags) + (typescript ? extension : extension.replace(".ts", ".js"))
|
|
135
|
+
);
|
|
136
|
+
const result = {
|
|
137
|
+
operation: "created",
|
|
138
|
+
sourceRoute: routeFrom,
|
|
139
|
+
destinationRoute: relativizePath(routeDestinationPath, rootDirectory)
|
|
140
|
+
};
|
|
141
|
+
if (!force && await fileExists(routeDestinationPath)) {
|
|
142
|
+
const shouldOverwrite = await renderConfirmationPrompt({
|
|
143
|
+
message: `The file ${result.destinationRoute} already exists. Do you want to replace it?`,
|
|
144
|
+
defaultValue: false,
|
|
145
|
+
confirmationMessage: "Yes",
|
|
146
|
+
cancellationMessage: "No",
|
|
147
|
+
abortSignal: signal
|
|
148
|
+
});
|
|
149
|
+
if (!shouldOverwrite)
|
|
150
|
+
return { ...result, operation: "skipped" };
|
|
151
|
+
result.operation = "replaced";
|
|
152
|
+
}
|
|
153
|
+
for (const filePath of allFilesToGenerate) {
|
|
154
|
+
const isRoute = filePath.startsWith(GENERATOR_ROUTE_DIR + "/");
|
|
155
|
+
const destinationPath = isRoute ? routeDestinationPath : joinPath(
|
|
156
|
+
appDirectory,
|
|
157
|
+
filePath.replace(/\.ts(x?)$/, `.${typescript ? "ts$1" : "js$1"}`)
|
|
158
|
+
);
|
|
159
|
+
if (!await fileExists(dirname(destinationPath))) {
|
|
160
|
+
await mkdir(dirname(destinationPath));
|
|
161
|
+
}
|
|
162
|
+
if (!/\.[jt]sx?$/.test(filePath)) {
|
|
163
|
+
await copyFile(
|
|
164
|
+
getTemplateAppFile(filePath, templatesRoot),
|
|
165
|
+
destinationPath
|
|
166
|
+
);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
let templateContent = convertTemplateToRemixVersion(
|
|
170
|
+
await readFile(getTemplateAppFile(filePath, templatesRoot)),
|
|
171
|
+
v2Flags
|
|
172
|
+
);
|
|
173
|
+
if (!typescript) {
|
|
174
|
+
templateContent = transpileFile(templateContent, transpilerOptions);
|
|
175
|
+
}
|
|
176
|
+
if (adapter) {
|
|
177
|
+
templateContent = templateContent.replace(
|
|
178
|
+
/@shopify\/remix-oxygen/g,
|
|
179
|
+
adapter
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
templateContent = formatCode(
|
|
183
|
+
templateContent,
|
|
184
|
+
formatOptions,
|
|
185
|
+
destinationPath
|
|
186
|
+
);
|
|
187
|
+
await writeFile(destinationPath, templateContent);
|
|
188
|
+
}
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
function getDestinationRoute(routeFrom, localePrefix, v2Flags) {
|
|
192
|
+
const routePath = routeFrom.replace(GENERATOR_ROUTE_DIR + "/", "");
|
|
193
|
+
const filePrefix = localePrefix && !NO_LOCALE_PATTERNS.some((pattern) => pattern.test(routePath)) ? `($${localePrefix})` + (v2Flags.isV2RouteConvention ? "." : "/") : "";
|
|
194
|
+
return GENERATOR_ROUTE_DIR + "/" + filePrefix + (v2Flags.isV2RouteConvention ? routePath : convertRouteToV1(routePath));
|
|
195
|
+
}
|
|
196
|
+
async function findRouteDependencies(routeFilePath, appDirectory) {
|
|
197
|
+
const filesToCheck = /* @__PURE__ */ new Set([routeFilePath]);
|
|
198
|
+
const fileDependencies = /* @__PURE__ */ new Set([relativePath(appDirectory, routeFilePath)]);
|
|
199
|
+
for (const filePath of filesToCheck) {
|
|
200
|
+
const importMatches = (await readFile(filePath, { encoding: "utf8" })).matchAll(/^(import|export)\s+.*?\s+from\s+['"](.*?)['"];?$/gims);
|
|
201
|
+
for (let [, , match] of importMatches) {
|
|
202
|
+
if (!match || !/^(\.|~)/.test(match))
|
|
203
|
+
continue;
|
|
204
|
+
match = match.replace(
|
|
205
|
+
"~",
|
|
206
|
+
relativePath(dirname(filePath), appDirectory) || "."
|
|
207
|
+
);
|
|
208
|
+
const resolvedMatchPath = resolvePath(dirname(filePath), match);
|
|
209
|
+
const absoluteFilepath = (await findFileWithExtension(
|
|
210
|
+
dirname(resolvedMatchPath),
|
|
211
|
+
basename(resolvedMatchPath)
|
|
212
|
+
)).filepath || resolvedMatchPath;
|
|
213
|
+
if (!absoluteFilepath.includes(`/${GENERATOR_ROUTE_DIR}/`)) {
|
|
214
|
+
fileDependencies.add(relativePath(appDirectory, absoluteFilepath));
|
|
215
|
+
if (/\.[jt]sx?$/.test(absoluteFilepath)) {
|
|
216
|
+
filesToCheck.add(absoluteFilepath);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return [...fileDependencies];
|
|
222
|
+
}
|
|
223
|
+
async function getJsTranspilerOptions(rootDirectory) {
|
|
224
|
+
const jsConfigPath = joinPath(rootDirectory, "jsconfig.json");
|
|
225
|
+
if (!await fileExists(jsConfigPath))
|
|
226
|
+
return;
|
|
227
|
+
return JSON.parse(
|
|
228
|
+
(await readFile(jsConfigPath, { encoding: "utf8" })).replace(
|
|
229
|
+
/^\s*\/\/.*$/gm,
|
|
230
|
+
""
|
|
231
|
+
)
|
|
232
|
+
)?.compilerOptions;
|
|
233
|
+
}
|
|
234
|
+
async function renderRoutePrompt(options) {
|
|
235
|
+
const generateAll = await renderConfirmationPrompt({
|
|
236
|
+
message: "Scaffold all standard route files? " + Object.keys(ROUTE_MAP).join(", "),
|
|
237
|
+
confirmationMessage: "Yes",
|
|
238
|
+
cancellationMessage: "No",
|
|
239
|
+
...options
|
|
240
|
+
});
|
|
241
|
+
return generateAll ? "all" : [];
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export { ALL_ROUTE_CHOICES, generateProjectFile, generateRoutes, getResolvedRoutes, renderRoutePrompt };
|