@shopify/cli-hydrogen 8.1.0 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/hydrogen/starter/CHANGELOG.md +166 -0
- package/dist/assets/hydrogen/starter/app/components/AddToCartButton.tsx +37 -0
- package/dist/assets/hydrogen/starter/app/components/CartLineItem.tsx +150 -0
- package/dist/assets/hydrogen/starter/app/components/CartMain.tsx +68 -0
- package/dist/assets/hydrogen/starter/app/components/CartSummary.tsx +101 -0
- package/dist/assets/hydrogen/starter/app/components/Header.tsx +3 -3
- package/dist/assets/hydrogen/starter/app/components/PageLayout.tsx +2 -2
- package/dist/assets/hydrogen/starter/app/components/ProductForm.tsx +80 -0
- package/dist/assets/hydrogen/starter/app/components/ProductImage.tsx +23 -0
- package/dist/assets/hydrogen/starter/app/components/ProductPrice.tsx +27 -0
- package/dist/assets/hydrogen/starter/app/lib/session.ts +5 -0
- package/dist/assets/hydrogen/starter/app/root.tsx +23 -36
- package/dist/assets/hydrogen/starter/app/routes/account.$.tsx +1 -5
- package/dist/assets/hydrogen/starter/app/routes/account.addresses.tsx +12 -70
- package/dist/assets/hydrogen/starter/app/routes/account.orders.$id.tsx +7 -14
- package/dist/assets/hydrogen/starter/app/routes/account.orders._index.tsx +1 -8
- package/dist/assets/hydrogen/starter/app/routes/account.profile.tsx +5 -22
- package/dist/assets/hydrogen/starter/app/routes/account.tsx +0 -1
- package/dist/assets/hydrogen/starter/app/routes/cart.tsx +1 -3
- package/dist/assets/hydrogen/starter/app/routes/products.$handle.tsx +51 -232
- package/dist/assets/hydrogen/starter/package.json +10 -11
- package/dist/assets/hydrogen/starter/server.ts +4 -0
- package/dist/assets/hydrogen/tailwind/package.json +1 -6
- package/dist/assets/hydrogen/tailwind/tailwind.css +6 -3
- package/dist/assets/hydrogen/vanilla-extract/package.json +2 -3
- package/dist/assets/hydrogen/virtual-routes/components/{Layout.jsx → PageLayout.jsx} +2 -2
- package/dist/assets/hydrogen/virtual-routes/components/RequestDetails.jsx +1 -2
- package/dist/assets/hydrogen/virtual-routes/components/RequestTable.jsx +1 -2
- package/dist/assets/hydrogen/virtual-routes/routes/index.jsx +1 -2
- package/dist/assets/hydrogen/virtual-routes/virtual-root.jsx +8 -30
- package/dist/commands/hydrogen/build.js +33 -10
- package/dist/commands/hydrogen/customer-account/push.js +3 -6
- package/dist/commands/hydrogen/debug/cpu.js +3 -3
- package/dist/commands/hydrogen/deploy.js +14 -3
- package/dist/commands/hydrogen/dev.js +3 -6
- package/dist/commands/hydrogen/env/list.js +1 -2
- package/dist/commands/hydrogen/env/pull.js +2 -4
- package/dist/commands/hydrogen/env/push.js +6 -12
- package/dist/commands/hydrogen/init.d.ts +18 -15
- package/dist/commands/hydrogen/init.js +12 -24
- package/dist/commands/hydrogen/link.js +1 -2
- package/dist/commands/hydrogen/preview.js +4 -6
- package/dist/commands/hydrogen/setup/css.js +29 -12
- package/dist/commands/hydrogen/setup/vite.js +3 -6
- package/dist/commands/hydrogen/setup.js +8 -7
- package/dist/commands/hydrogen/upgrade.js +16 -32
- package/dist/hooks/init.js +50 -6
- package/dist/index.d.ts +46 -46
- package/dist/lib/auth.js +1 -2
- package/dist/lib/build.js +1 -2
- package/dist/lib/bundle/analyzer.js +39 -24
- package/dist/lib/bundle/vite-plugin.js +161 -0
- package/dist/lib/check-cli-version.js +61 -0
- package/dist/lib/check-lockfile.js +2 -2
- package/dist/lib/classic-compiler/build.js +3 -3
- package/dist/lib/classic-compiler/dev.js +5 -10
- package/dist/lib/codegen.js +8 -16
- package/dist/lib/defer.js +2 -4
- package/dist/lib/environment-variables.js +2 -4
- package/dist/lib/file.js +15 -7
- package/dist/lib/flags.js +10 -0
- package/dist/lib/get-oxygen-deployment-data.js +1 -2
- package/dist/lib/graphiql-url.js +1 -2
- package/dist/lib/import-utils.js +3 -2
- package/dist/lib/log.js +11 -22
- package/dist/lib/mini-oxygen/common.js +1 -2
- package/dist/lib/mini-oxygen/node.js +1 -2
- package/dist/lib/missing-routes.js +1 -2
- package/dist/lib/onboarding/common.js +60 -15
- package/dist/lib/onboarding/local.js +14 -13
- package/dist/lib/onboarding/remote.js +16 -9
- package/dist/lib/onboarding/setup-template.mocks.js +6 -3
- package/dist/lib/remix-config.js +2 -4
- package/dist/lib/remix-version-check.js +1 -2
- package/dist/lib/request-events.js +3 -6
- package/dist/lib/setups/css/assets.js +1 -1
- package/dist/lib/setups/css/index.js +17 -10
- package/dist/lib/setups/css/replacers.js +74 -76
- package/dist/lib/setups/css/tailwind.js +16 -20
- package/dist/lib/setups/css/vanilla-extract.js +8 -5
- package/dist/lib/setups/i18n/replacers.js +1 -2
- package/dist/lib/setups/routes/generate.js +18 -19
- package/dist/lib/shell.js +5 -10
- package/dist/lib/template-diff.js +83 -104
- package/dist/lib/template-downloader.js +2 -2
- package/dist/lib/transpile/morph/functions.js +3 -6
- package/dist/lib/transpile/morph/index.js +2 -4
- package/dist/lib/transpile/morph/typedefs.js +3 -6
- package/dist/lib/transpile/morph/utils.js +2 -4
- package/dist/lib/transpile/project.js +4 -3
- package/oclif.manifest.json +51 -4
- package/package.json +8 -12
- package/dist/assets/hydrogen/css-modules/package.json +0 -6
- package/dist/assets/hydrogen/postcss/package.json +0 -10
- package/dist/assets/hydrogen/postcss/postcss.config.js +0 -8
- package/dist/assets/hydrogen/starter/app/components/Cart.tsx +0 -364
- package/dist/assets/hydrogen/tailwind/postcss.config.js +0 -10
- package/dist/assets/hydrogen/tailwind/tailwind.config.js +0 -8
- package/dist/lib/check-version.js +0 -75
- package/dist/lib/setups/css/css-modules.js +0 -23
- package/dist/lib/setups/css/postcss.js +0 -31
|
@@ -2,73 +2,6 @@ import { AbortError } from '@shopify/cli-kit/node/error';
|
|
|
2
2
|
import { findFileWithExtension, replaceFileContent } from '../../file.js';
|
|
3
3
|
import { importLangAstGrep } from '../../ast.js';
|
|
4
4
|
|
|
5
|
-
async function replaceRemixConfig(rootDirectory, formatConfig, newProperties) {
|
|
6
|
-
const { filepath, astType } = await findFileWithExtension(
|
|
7
|
-
rootDirectory,
|
|
8
|
-
"remix.config"
|
|
9
|
-
);
|
|
10
|
-
if (!filepath || !astType) {
|
|
11
|
-
throw new AbortError(
|
|
12
|
-
`Could not find remix.config.js file in ${rootDirectory}`
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
await replaceFileContent(filepath, formatConfig, async (content) => {
|
|
16
|
-
const astGrep = await importLangAstGrep(astType);
|
|
17
|
-
const root = astGrep.parse(content).root();
|
|
18
|
-
const remixConfigNode = root.find({
|
|
19
|
-
rule: {
|
|
20
|
-
kind: "object",
|
|
21
|
-
inside: {
|
|
22
|
-
any: [
|
|
23
|
-
{
|
|
24
|
-
kind: "export_statement"
|
|
25
|
-
// ESM
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
kind: "assignment_expression",
|
|
29
|
-
// CJS
|
|
30
|
-
has: {
|
|
31
|
-
kind: "member_expression",
|
|
32
|
-
field: "left",
|
|
33
|
-
pattern: "module.exports"
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
]
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
if (!remixConfigNode) {
|
|
41
|
-
throw new AbortError(
|
|
42
|
-
"Could not find a default export in remix.config.js"
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
newProperties = { ...newProperties };
|
|
46
|
-
for (const key of Object.keys(newProperties)) {
|
|
47
|
-
const propertyNode = remixConfigNode.find({
|
|
48
|
-
rule: {
|
|
49
|
-
kind: "pair",
|
|
50
|
-
has: {
|
|
51
|
-
field: "key",
|
|
52
|
-
regex: `^${key}$`
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
if (propertyNode?.text().endsWith(" " + JSON.stringify(newProperties[key]))) {
|
|
57
|
-
delete newProperties[key];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
if (Object.keys(newProperties).length === 0) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const childrenNodes = remixConfigNode.children();
|
|
64
|
-
const lastNode = childrenNodes.find((node) => node.text().startsWith("future:")) ?? childrenNodes.pop();
|
|
65
|
-
if (!lastNode) {
|
|
66
|
-
throw new AbortError("Could not add properties to Remix config");
|
|
67
|
-
}
|
|
68
|
-
const { start } = lastNode.range();
|
|
69
|
-
return content.slice(0, start.index) + JSON.stringify(newProperties).slice(1, -1) + "," + content.slice(start.index);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
5
|
async function replaceRootLinks(appDirectory, formatConfig, importer) {
|
|
73
6
|
const { filepath, astType } = await findFileWithExtension(appDirectory, "root");
|
|
74
7
|
if (!filepath || !astType) {
|
|
@@ -81,7 +14,8 @@ async function replaceRootLinks(appDirectory, formatConfig, importer) {
|
|
|
81
14
|
}
|
|
82
15
|
const astGrep = await importLangAstGrep(astType);
|
|
83
16
|
const root = astGrep.parse(content).root();
|
|
84
|
-
const
|
|
17
|
+
const importNodes = root.findAll({ rule: { kind: "import_statement" } });
|
|
18
|
+
const lastImportNode = importNodes.findLast((node) => node.text().includes(".css")) || importNodes.pop();
|
|
85
19
|
const linksReturnNode = root.find({
|
|
86
20
|
utils: {
|
|
87
21
|
"has-links-id": {
|
|
@@ -127,14 +61,78 @@ async function replaceRootLinks(appDirectory, formatConfig, importer) {
|
|
|
127
61
|
);
|
|
128
62
|
});
|
|
129
63
|
}
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
64
|
+
async function injectVitePlugin(rootDirectory, formatConfig, importer, pluginOptions) {
|
|
65
|
+
const { filepath, astType } = await findFileWithExtension(
|
|
66
|
+
rootDirectory,
|
|
67
|
+
"vite.config"
|
|
68
|
+
);
|
|
69
|
+
if (!filepath || !astType) {
|
|
70
|
+
throw new AbortError(`Could not find vite.config file in ${rootDirectory}`);
|
|
71
|
+
}
|
|
72
|
+
await replaceFileContent(filepath, formatConfig, async (content) => {
|
|
73
|
+
const importStatement = `import ${importer.isDefault ? importer.name : `{${importer.name}}`} from '${importer.path}';`;
|
|
74
|
+
if (new RegExp(`['"]${importer.path.replace("/", "\\/")}['"]`).test(content)) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const astGrep = await importLangAstGrep(astType);
|
|
78
|
+
const root = astGrep.parse(content).root();
|
|
79
|
+
const lastImportNode = root.findAll({ rule: { kind: "import_statement" } }).pop();
|
|
80
|
+
const vitePluginListNode = root.find(vitePluginListRule);
|
|
81
|
+
if (!lastImportNode || !vitePluginListNode) {
|
|
82
|
+
throw new AbortError(
|
|
83
|
+
'Could not find a "plugins" key in Vite config file. Please add one and try again.'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
const lastImportContent = lastImportNode.text();
|
|
87
|
+
const linksExportReturnContent = vitePluginListNode.text();
|
|
88
|
+
const newVitePluginItem = `${importer.name}(${pluginOptions ? JSON.stringify(pluginOptions) : ""})`;
|
|
89
|
+
return content.replace(lastImportContent, lastImportContent + "\n" + importStatement).replace(
|
|
90
|
+
linksExportReturnContent,
|
|
91
|
+
linksExportReturnContent.replace("[", `[${newVitePluginItem},`)
|
|
92
|
+
);
|
|
137
93
|
});
|
|
138
94
|
}
|
|
95
|
+
const vitePluginListRule = {
|
|
96
|
+
rule: {
|
|
97
|
+
// An array
|
|
98
|
+
pattern: "[$$$]",
|
|
99
|
+
inside: {
|
|
100
|
+
// directly in the value part of a `plugins` key
|
|
101
|
+
kind: "pair",
|
|
102
|
+
stopBy: "neighbor",
|
|
103
|
+
has: {
|
|
104
|
+
field: "key",
|
|
105
|
+
regex: "^plugins$",
|
|
106
|
+
stopBy: "neighbor"
|
|
107
|
+
},
|
|
108
|
+
inside: {
|
|
109
|
+
// directly inside an object (the Vite config object)
|
|
110
|
+
kind: "object",
|
|
111
|
+
stopBy: "neighbor",
|
|
112
|
+
// that is exported but is not inside another object
|
|
113
|
+
// e.g. `export default {something:{plugins:[]}}`
|
|
114
|
+
// doesn't match, but `export default {plugins:[]}` does.
|
|
115
|
+
// And `export default defineConfig({plugins:[]})` matches too.
|
|
116
|
+
all: [
|
|
117
|
+
{
|
|
118
|
+
inside: {
|
|
119
|
+
kind: "export_statement",
|
|
120
|
+
regex: "export default",
|
|
121
|
+
stopBy: "end"
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
not: {
|
|
126
|
+
inside: {
|
|
127
|
+
kind: "object",
|
|
128
|
+
stopBy: "end"
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
139
137
|
|
|
140
|
-
export {
|
|
138
|
+
export { injectVitePlugin, replaceRootLinks };
|
|
@@ -3,21 +3,15 @@ import { relativePath, joinPath } from '@shopify/cli-kit/node/path';
|
|
|
3
3
|
import { mergePackageJson } from '../../file.js';
|
|
4
4
|
import { canWriteFiles, copyAssets } from './assets.js';
|
|
5
5
|
import { getCodeFormatOptions } from '../../format-code.js';
|
|
6
|
-
import { replaceRootLinks } from './replacers.js';
|
|
6
|
+
import { replaceRootLinks, injectVitePlugin } from './replacers.js';
|
|
7
7
|
import { getAssetsDir } from '../../build.js';
|
|
8
8
|
|
|
9
9
|
const tailwindCssPath = "styles/tailwind.css";
|
|
10
|
-
async function setupTailwind({ rootDirectory, appDirectory
|
|
10
|
+
async function setupTailwind({ rootDirectory, appDirectory }, force = false) {
|
|
11
11
|
const relativeAppDirectory = relativePath(rootDirectory, appDirectory);
|
|
12
12
|
const assetMap = {
|
|
13
|
-
"tailwind.config.js": "tailwind.config.js",
|
|
14
|
-
"postcss.config.js": "postcss.config.js",
|
|
15
13
|
"tailwind.css": joinPath(relativeAppDirectory, tailwindCssPath)
|
|
16
14
|
};
|
|
17
|
-
if (futureOptions.tailwind && futureOptions.postcss) {
|
|
18
|
-
outputInfo(`Tailwind and PostCSS are already setup in ${rootDirectory}.`);
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
15
|
if (!await canWriteFiles(assetMap, appDirectory, force)) {
|
|
22
16
|
outputInfo(
|
|
23
17
|
`Skipping CSS setup as some files already exist. You may use \`--force\` or \`-f\` to override it.`
|
|
@@ -26,24 +20,26 @@ async function setupTailwind({ rootDirectory, appDirectory, ...futureOptions },
|
|
|
26
20
|
}
|
|
27
21
|
const workPromise = Promise.all([
|
|
28
22
|
mergePackageJson(await getAssetsDir("tailwind"), rootDirectory),
|
|
29
|
-
copyAssets(
|
|
30
|
-
"tailwind",
|
|
31
|
-
assetMap,
|
|
32
|
-
rootDirectory,
|
|
33
|
-
(content, filepath) => filepath === "tailwind.config.js" ? content.replace("{src-dir}", relativeAppDirectory) : content
|
|
34
|
-
),
|
|
23
|
+
copyAssets("tailwind", assetMap, rootDirectory),
|
|
35
24
|
getCodeFormatOptions(rootDirectory).then(
|
|
36
|
-
(formatConfig) =>
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
(formatConfig) => Promise.all([
|
|
26
|
+
replaceRootLinks(appDirectory, formatConfig, {
|
|
27
|
+
name: "tailwindCss",
|
|
28
|
+
path: `${tailwindCssPath}?url`,
|
|
29
|
+
isDefault: true
|
|
30
|
+
}),
|
|
31
|
+
injectVitePlugin(rootDirectory, formatConfig, {
|
|
32
|
+
name: "tailwindcss",
|
|
33
|
+
path: "@tailwindcss/vite",
|
|
34
|
+
isDefault: true
|
|
35
|
+
})
|
|
36
|
+
])
|
|
41
37
|
)
|
|
42
38
|
]);
|
|
43
39
|
return {
|
|
44
40
|
workPromise,
|
|
45
41
|
generatedAssets: Object.values(assetMap),
|
|
46
|
-
|
|
42
|
+
needsInstallDeps: true
|
|
47
43
|
};
|
|
48
44
|
}
|
|
49
45
|
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { mergePackageJson } from '../../file.js';
|
|
2
2
|
import { getCodeFormatOptions } from '../../format-code.js';
|
|
3
|
-
import {
|
|
3
|
+
import { injectVitePlugin } from './replacers.js';
|
|
4
4
|
import { getAssetsDir } from '../../build.js';
|
|
5
5
|
|
|
6
6
|
async function setupVanillaExtract({
|
|
7
|
-
rootDirectory
|
|
8
|
-
appDirectory
|
|
7
|
+
rootDirectory
|
|
9
8
|
}) {
|
|
10
9
|
const workPromise = Promise.all([
|
|
11
10
|
mergePackageJson(await getAssetsDir("vanilla-extract"), rootDirectory),
|
|
12
11
|
getCodeFormatOptions(rootDirectory).then(
|
|
13
|
-
(formatConfig) =>
|
|
12
|
+
(formatConfig) => injectVitePlugin(rootDirectory, formatConfig, {
|
|
13
|
+
path: "@vanilla-extract/vite-plugin",
|
|
14
|
+
name: "vanillaExtractPlugin",
|
|
15
|
+
isDefault: false
|
|
16
|
+
})
|
|
14
17
|
)
|
|
15
18
|
]);
|
|
16
19
|
return {
|
|
17
20
|
workPromise,
|
|
18
21
|
generatedAssets: [],
|
|
19
|
-
|
|
22
|
+
needsInstallDeps: true
|
|
20
23
|
};
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -162,8 +162,7 @@ async function replaceRemixEnv({ rootDirectory }, formatConfig, localeExtractImp
|
|
|
162
162
|
return;
|
|
163
163
|
}
|
|
164
164
|
await replaceFileContent(envPath, formatConfig, async (content) => {
|
|
165
|
-
if (content.includes(`Storefront<`))
|
|
166
|
-
return;
|
|
165
|
+
if (content.includes(`Storefront<`)) return;
|
|
167
166
|
const astGrep = await importLangAstGrep("ts");
|
|
168
167
|
const root = astGrep.parse(content).root();
|
|
169
168
|
const storefrontTypeNode = root.find({
|
|
@@ -91,10 +91,8 @@ async function generateRoutes(options, remixConfig) {
|
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
93
|
async function getLocalePrefix(routesDirectory, { localePrefix, routeName, v1RouteConvention }) {
|
|
94
|
-
if (localePrefix)
|
|
95
|
-
|
|
96
|
-
if (localePrefix !== void 0 || routeName === "all")
|
|
97
|
-
return;
|
|
94
|
+
if (localePrefix) return localePrefix;
|
|
95
|
+
if (localePrefix !== void 0 || routeName === "all") return;
|
|
98
96
|
const existingFiles = await readdir(routesDirectory).catch(() => []);
|
|
99
97
|
const coreRouteWithLocaleRE = v1RouteConvention ? /^\(\$(\w+)\)$/ : /^\(\$(\w+)\)\.(_index|\$|cart).[jt]sx?$/;
|
|
100
98
|
const coreRouteWithLocale = existingFiles.find(
|
|
@@ -114,19 +112,12 @@ async function generateProjectFile(routeFrom, {
|
|
|
114
112
|
formatOptions,
|
|
115
113
|
localePrefix,
|
|
116
114
|
v1RouteConvention = false,
|
|
117
|
-
signal
|
|
115
|
+
signal,
|
|
116
|
+
overwriteFileDeps = true
|
|
118
117
|
}) {
|
|
119
118
|
templatesRoot ??= await getStarterDir();
|
|
120
119
|
const extension = (routeFrom.match(/(\.[jt]sx?)$/) ?? [])[1] ?? ".tsx";
|
|
121
120
|
routeFrom = routeFrom.replace(extension, "");
|
|
122
|
-
const routeTemplatePath = await getTemplateAppFile(
|
|
123
|
-
routeFrom + extension,
|
|
124
|
-
templatesRoot
|
|
125
|
-
);
|
|
126
|
-
const allFilesToGenerate = await findRouteDependencies(
|
|
127
|
-
routeTemplatePath,
|
|
128
|
-
await getTemplateAppFile("", templatesRoot)
|
|
129
|
-
);
|
|
130
121
|
const routeDestinationPath = joinPath(
|
|
131
122
|
appDirectory,
|
|
132
123
|
getDestinationRoute(routeFrom, localePrefix, { v1RouteConvention }) + (typescript ? extension : extension.replace(".ts", ".js"))
|
|
@@ -144,16 +135,26 @@ async function generateProjectFile(routeFrom, {
|
|
|
144
135
|
cancellationMessage: "No",
|
|
145
136
|
abortSignal: signal
|
|
146
137
|
});
|
|
147
|
-
if (!shouldOverwrite)
|
|
148
|
-
return { ...result, operation: "skipped" };
|
|
138
|
+
if (!shouldOverwrite) return { ...result, operation: "skipped" };
|
|
149
139
|
result.operation = "replaced";
|
|
150
140
|
}
|
|
141
|
+
const routeTemplatePath = await getTemplateAppFile(
|
|
142
|
+
routeFrom + extension,
|
|
143
|
+
templatesRoot
|
|
144
|
+
);
|
|
145
|
+
const allFilesToGenerate = await findRouteDependencies(
|
|
146
|
+
routeTemplatePath,
|
|
147
|
+
await getTemplateAppFile("", templatesRoot)
|
|
148
|
+
);
|
|
151
149
|
for (const filePath of allFilesToGenerate) {
|
|
152
150
|
const isRoute = filePath.startsWith(ASSETS_STARTER_DIR_ROUTES + "/");
|
|
153
151
|
const destinationPath = isRoute ? routeDestinationPath : joinPath(
|
|
154
152
|
appDirectory,
|
|
155
153
|
filePath.replace(/\.ts(x?)$/, `.${typescript ? "ts$1" : "js$1"}`)
|
|
156
154
|
);
|
|
155
|
+
if (!overwriteFileDeps && await fileExists(destinationPath)) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
157
158
|
if (!await fileExists(dirname(destinationPath))) {
|
|
158
159
|
await mkdir(dirname(destinationPath));
|
|
159
160
|
}
|
|
@@ -200,8 +201,7 @@ async function findRouteDependencies(routeFilePath, appDirectory) {
|
|
|
200
201
|
for (const filePath of filesToCheck) {
|
|
201
202
|
const importMatches = (await readFile(filePath, { encoding: "utf8" })).matchAll(/^(import|export)\s+.*?\s+from\s+['"](.*?)['"];?$/gims);
|
|
202
203
|
for (let [, , match] of importMatches) {
|
|
203
|
-
if (!match || !/^(\.|~)/.test(match))
|
|
204
|
-
continue;
|
|
204
|
+
if (!match || !/^(\.|~)/.test(match)) continue;
|
|
205
205
|
match = match.replace(/\?[a-z.]+$/, "");
|
|
206
206
|
match = match.replace(
|
|
207
207
|
"~",
|
|
@@ -253,8 +253,7 @@ async function copyRouteTemplate({
|
|
|
253
253
|
adapter
|
|
254
254
|
}) {
|
|
255
255
|
const routePath = joinPath(routesDirectory, routeName);
|
|
256
|
-
if (await fileExists(routePath))
|
|
257
|
-
return;
|
|
256
|
+
if (await fileExists(routePath)) return;
|
|
258
257
|
const templatePath = await getAssetsDir("routes", templateName);
|
|
259
258
|
if (!await fileExists(templatePath)) {
|
|
260
259
|
throw new Error("Unknown strategy");
|
package/dist/lib/shell.js
CHANGED
|
@@ -30,10 +30,8 @@ async function supportsShell(shell) {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
function getShellAliasDefinitionFile(shell) {
|
|
33
|
-
if (shell === "bash")
|
|
34
|
-
|
|
35
|
-
if (shell === "zsh")
|
|
36
|
-
return "~/.zshrc";
|
|
33
|
+
if (shell === "bash") return "~/.bashrc";
|
|
34
|
+
if (shell === "zsh") return "~/.zshrc";
|
|
37
35
|
return `~/.config/fish/functions/${ALIAS_NAME}.fish`;
|
|
38
36
|
}
|
|
39
37
|
async function hasAliasDefinition(aliasName, shell) {
|
|
@@ -71,10 +69,8 @@ async function shellWriteFile(shell, content, append = false) {
|
|
|
71
69
|
}
|
|
72
70
|
}
|
|
73
71
|
async function shellWriteAlias(shell, aliasName, content) {
|
|
74
|
-
if (!await supportsShell(shell))
|
|
75
|
-
|
|
76
|
-
if (await hasAliasDefinition(aliasName, shell))
|
|
77
|
-
return true;
|
|
72
|
+
if (!await supportsShell(shell)) return false;
|
|
73
|
+
if (await hasAliasDefinition(aliasName, shell)) return true;
|
|
78
74
|
return await shellWriteFile(shell, content, shell !== "fish");
|
|
79
75
|
}
|
|
80
76
|
async function shellRunScript(script, shellBin) {
|
|
@@ -98,8 +94,7 @@ async function hasCliAlias() {
|
|
|
98
94
|
await execAsync(`Get-Alias -Name ${ALIAS_NAME}`);
|
|
99
95
|
} else {
|
|
100
96
|
const shell = os.userInfo().shell?.split("/").pop() ?? "bash";
|
|
101
|
-
if (!isKnownUnixShell(shell))
|
|
102
|
-
return false;
|
|
97
|
+
if (!isKnownUnixShell(shell)) return false;
|
|
103
98
|
return await hasAliasDefinition(ALIAS_NAME, shell);
|
|
104
99
|
}
|
|
105
100
|
return true;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { symlink, cp } from 'node:fs/promises';
|
|
1
2
|
import { temporaryDirectory } from 'tempy';
|
|
2
|
-
import { createSymlink, copy } from 'fs-extra/esm';
|
|
3
3
|
import { copyFile, fileExists, removeFile } from '@shopify/cli-kit/node/fs';
|
|
4
4
|
import { joinPath, relativePath } from '@shopify/cli-kit/node/path';
|
|
5
5
|
import { readAndParsePackageJson } from '@shopify/cli-kit/node/node-package-manager';
|
|
6
6
|
import { outputInfo } from '@shopify/cli-kit/node/output';
|
|
7
7
|
import colors from '@shopify/cli-kit/node/colors';
|
|
8
8
|
import { getStarterDir, isHydrogenMonorepo, getRepoNodeModules } from './build.js';
|
|
9
|
-
import { mergePackageJson } from './file.js';
|
|
9
|
+
import { mergePackageJson, mergeTsConfig } from './file.js';
|
|
10
10
|
|
|
11
11
|
async function prepareDiffDirectory(diffDirectory, watch) {
|
|
12
12
|
const targetDirectory = temporaryDirectory({ prefix: "tmp-hydrogen-diff-" });
|
|
@@ -20,106 +20,81 @@ ${colors.dim(
|
|
|
20
20
|
);
|
|
21
21
|
const templateDirectory = await getStarterDir(isHydrogenMonorepo);
|
|
22
22
|
await applyTemplateDiff(targetDirectory, diffDirectory, templateDirectory);
|
|
23
|
-
await
|
|
23
|
+
await symlink(
|
|
24
24
|
await getRepoNodeModules(),
|
|
25
25
|
joinPath(targetDirectory, "node_modules")
|
|
26
26
|
);
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
}) : void 0;
|
|
30
|
-
const subscriptions = await Promise.all([
|
|
27
|
+
const { default: chokidar } = await import('chokidar');
|
|
28
|
+
const subscriptions = watch ? [
|
|
31
29
|
// Copy back the changes in generated d.ts from the
|
|
32
30
|
// temporary directory to the original diff directory.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
event.path,
|
|
43
|
-
joinPath(diffDirectory, relativePath(targetDirectory, event.path))
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
},
|
|
47
|
-
{ ignore: ["!*.generated.d.ts"] }
|
|
48
|
-
),
|
|
31
|
+
chokidar.watch(joinPath(targetDirectory, "*.generated.d.ts"), {
|
|
32
|
+
ignoreInitial: true
|
|
33
|
+
}).on("all", async (eventName, eventFilePath) => {
|
|
34
|
+
const targetFile = joinPath(
|
|
35
|
+
diffDirectory,
|
|
36
|
+
relativePath(targetDirectory, eventFilePath)
|
|
37
|
+
);
|
|
38
|
+
await copyFile(eventFilePath, targetFile);
|
|
39
|
+
}),
|
|
49
40
|
// Copy new changes in the original diff directory to
|
|
50
41
|
// the temporary directory.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
42
|
+
chokidar.watch(diffDirectory, {
|
|
43
|
+
ignoreInitial: true,
|
|
44
|
+
ignored: [
|
|
45
|
+
"**/*.generated.d.ts",
|
|
46
|
+
"**/package.json",
|
|
47
|
+
"**/tsconfig.json",
|
|
48
|
+
"**/.shopify"
|
|
49
|
+
]
|
|
50
|
+
}).on("all", async (eventName, eventFilePath) => {
|
|
51
|
+
const targetFile = joinPath(
|
|
52
|
+
targetDirectory,
|
|
53
|
+
relativePath(diffDirectory, eventFilePath)
|
|
54
|
+
);
|
|
55
|
+
const fileInTemplate = eventFilePath.replace(
|
|
56
|
+
diffDirectory,
|
|
57
|
+
templateDirectory
|
|
58
|
+
);
|
|
59
|
+
if (eventName === "unlink") {
|
|
60
|
+
return fileExists(fileInTemplate).then(
|
|
61
|
+
(exists) => exists ? (
|
|
62
|
+
// Replace it with original file from the starter template.
|
|
63
|
+
copyFile(fileInTemplate, targetFile)
|
|
64
|
+
) : (
|
|
65
|
+
// Remove the file otherwise.
|
|
66
|
+
removeFile(targetFile)
|
|
67
|
+
)
|
|
68
|
+
).catch(() => {
|
|
77
69
|
});
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
ignore: [
|
|
81
|
-
"*.generated.d.ts",
|
|
82
|
-
"package.json",
|
|
83
|
-
"tsconfig.json",
|
|
84
|
-
".shopify"
|
|
85
|
-
]
|
|
86
70
|
}
|
|
87
|
-
|
|
71
|
+
return copyFile(eventFilePath, targetFile);
|
|
72
|
+
}),
|
|
88
73
|
// Copy new changes in the starter template to the temporary
|
|
89
74
|
// directory only if they don't overwrite the files in the
|
|
90
75
|
// original diff directory, which have higher priority.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
{
|
|
114
|
-
ignore: [
|
|
115
|
-
"*.generated.d.ts",
|
|
116
|
-
"package.json",
|
|
117
|
-
"tsconfig.json",
|
|
118
|
-
".shopify"
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
)
|
|
122
|
-
]);
|
|
76
|
+
chokidar.watch(templateDirectory, {
|
|
77
|
+
ignoreInitial: true,
|
|
78
|
+
ignored: [
|
|
79
|
+
"**/*.generated.d.ts",
|
|
80
|
+
"**/package.json",
|
|
81
|
+
"**/tsconfig.json",
|
|
82
|
+
"**/.shopify"
|
|
83
|
+
]
|
|
84
|
+
}).on("all", async (eventName, eventFilePath) => {
|
|
85
|
+
const fileInDiff = eventFilePath.replace(
|
|
86
|
+
templateDirectory,
|
|
87
|
+
diffDirectory
|
|
88
|
+
);
|
|
89
|
+
if (await fileExists(fileInDiff)) return;
|
|
90
|
+
const targetFile = joinPath(
|
|
91
|
+
targetDirectory,
|
|
92
|
+
relativePath(templateDirectory, eventFilePath)
|
|
93
|
+
);
|
|
94
|
+
return eventName === "unlink" ? removeFile(targetFile).catch(() => {
|
|
95
|
+
}) : copyFile(eventFilePath, targetFile);
|
|
96
|
+
})
|
|
97
|
+
] : [];
|
|
123
98
|
return {
|
|
124
99
|
/**
|
|
125
100
|
* The temporary directory with the starter template and diff applied.
|
|
@@ -129,7 +104,7 @@ ${colors.dim(
|
|
|
129
104
|
* Removes the temporary directory and stops the file watchers.
|
|
130
105
|
*/
|
|
131
106
|
cleanup: async () => {
|
|
132
|
-
await Promise.all(subscriptions.map((sub) => sub
|
|
107
|
+
await Promise.all(subscriptions.map((sub) => sub.close()));
|
|
133
108
|
await removeFile(targetDirectory);
|
|
134
109
|
},
|
|
135
110
|
/**
|
|
@@ -139,11 +114,10 @@ ${colors.dim(
|
|
|
139
114
|
*/
|
|
140
115
|
async copyShopifyConfig() {
|
|
141
116
|
const source = joinPath(targetDirectory, ".shopify");
|
|
142
|
-
if (!await fileExists(source))
|
|
143
|
-
return;
|
|
117
|
+
if (!await fileExists(source)) return;
|
|
144
118
|
const target = joinPath(diffDirectory, ".shopify");
|
|
145
119
|
await removeFile(target);
|
|
146
|
-
await
|
|
120
|
+
await cp(source, target, { recursive: true, force: true });
|
|
147
121
|
},
|
|
148
122
|
/**
|
|
149
123
|
* Brings the `dist` directory back to the original project.
|
|
@@ -153,8 +127,9 @@ ${colors.dim(
|
|
|
153
127
|
const target = joinPath(diffDirectory, "dist");
|
|
154
128
|
await removeFile(target);
|
|
155
129
|
await Promise.all([
|
|
156
|
-
|
|
157
|
-
|
|
130
|
+
cp(joinPath(targetDirectory, "dist"), target, {
|
|
131
|
+
force: true,
|
|
132
|
+
recursive: true
|
|
158
133
|
}),
|
|
159
134
|
copyFile(
|
|
160
135
|
joinPath(targetDirectory, ".env"),
|
|
@@ -166,23 +141,26 @@ ${colors.dim(
|
|
|
166
141
|
}
|
|
167
142
|
async function applyTemplateDiff(targetDirectory, diffDirectory, templateDir) {
|
|
168
143
|
templateDir ??= await getStarterDir();
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
]);
|
|
144
|
+
const diffPkgJson = await readAndParsePackageJson(
|
|
145
|
+
joinPath(diffDirectory, "package.json")
|
|
146
|
+
);
|
|
173
147
|
const diffOptions = diffPkgJson["h2:diff"] ?? {};
|
|
174
148
|
const createFilter = (re, skipFiles) => (filepath) => {
|
|
175
149
|
const filename = relativePath(templateDir, filepath);
|
|
176
150
|
return !re.test(filename) && !skipFiles?.includes(filename);
|
|
177
151
|
};
|
|
178
|
-
await
|
|
152
|
+
await cp(templateDir, targetDirectory, {
|
|
153
|
+
force: true,
|
|
154
|
+
recursive: true,
|
|
179
155
|
filter: createFilter(
|
|
180
156
|
// Do not copy .shopify from skeleton to avoid linking in examples inadvertedly
|
|
181
157
|
/(^|\/|\\)(dist|node_modules|\.cache|\.turbo|\.shopify|CHANGELOG\.md)(\/|\\|$)/i,
|
|
182
158
|
diffOptions.skipFiles || []
|
|
183
159
|
)
|
|
184
160
|
});
|
|
185
|
-
await
|
|
161
|
+
await cp(diffDirectory, targetDirectory, {
|
|
162
|
+
force: true,
|
|
163
|
+
recursive: true,
|
|
186
164
|
filter: createFilter(
|
|
187
165
|
/(^|\/|\\)(dist|node_modules|\.cache|.turbo|package\.json|tsconfig\.json)(\/|\\|$)/i
|
|
188
166
|
)
|
|
@@ -190,8 +168,8 @@ async function applyTemplateDiff(targetDirectory, diffDirectory, templateDir) {
|
|
|
190
168
|
await mergePackageJson(diffDirectory, targetDirectory, {
|
|
191
169
|
ignoredKeys: ["h2:diff"],
|
|
192
170
|
onResult: (pkgJson) => {
|
|
193
|
-
if (pkgJson.dependencies
|
|
194
|
-
pkgJson.dependencies["@shopify/cli-hydrogen"]
|
|
171
|
+
if (pkgJson.dependencies) {
|
|
172
|
+
delete pkgJson.dependencies["@shopify/cli-hydrogen"];
|
|
195
173
|
}
|
|
196
174
|
for (const key of ["build", "dev", "preview"]) {
|
|
197
175
|
const scriptLine = pkgJson.scripts?.[key];
|
|
@@ -212,6 +190,7 @@ async function applyTemplateDiff(targetDirectory, diffDirectory, templateDir) {
|
|
|
212
190
|
return pkgJson;
|
|
213
191
|
}
|
|
214
192
|
});
|
|
193
|
+
await mergeTsConfig(diffDirectory, targetDirectory);
|
|
215
194
|
}
|
|
216
195
|
|
|
217
196
|
export { applyTemplateDiff, prepareDiffDirectory };
|