@shopify/cli-hydrogen 5.4.2 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/hydrogen/debug/cpu.js +98 -0
- package/dist/commands/hydrogen/dev.js +3 -4
- package/dist/commands/hydrogen/init.test.js +2 -2
- package/dist/generator-templates/starter/app/root.tsx +3 -1
- package/dist/generator-templates/starter/app/routes/account.orders._index.tsx +1 -1
- package/dist/generator-templates/starter/app/routes/account_.recover.tsx +7 -2
- package/dist/generator-templates/starter/app/routes/account_.register.tsx +3 -3
- package/dist/generator-templates/starter/app/routes/blogs.$blogHandle._index.tsx +1 -0
- package/dist/generator-templates/starter/package.json +4 -3
- package/dist/generator-templates/starter/storefrontapi.generated.d.ts +4 -4
- package/dist/hooks/init.js +4 -1
- package/dist/lib/cpu-profiler.js +92 -0
- package/dist/lib/mini-oxygen/node.js +5 -5
- package/dist/lib/mini-oxygen/workerd.js +21 -14
- package/dist/lib/onboarding/common.js +2 -2
- package/dist/lib/request-events.js +20 -16
- package/dist/lib/setups/i18n/domains.test.js +14 -0
- package/dist/lib/setups/i18n/index.js +3 -3
- package/dist/lib/setups/i18n/replacers.js +84 -64
- package/dist/lib/setups/i18n/replacers.test.js +242 -0
- package/dist/lib/setups/i18n/subdomains.test.js +14 -0
- package/dist/lib/setups/i18n/subfolders.test.js +14 -0
- package/dist/lib/setups/i18n/templates/domains.js +1 -1
- package/dist/lib/setups/i18n/templates/domains.ts +5 -2
- package/dist/lib/setups/i18n/templates/subdomains.ts +4 -1
- package/dist/lib/setups/i18n/templates/subfolders.js +1 -2
- package/dist/lib/setups/i18n/templates/subfolders.ts +7 -6
- package/dist/lib/setups/routes/generate.js +5 -2
- package/dist/lib/transpile/file.js +6 -0
- package/dist/lib/transpile/index.js +2 -0
- package/dist/lib/transpile/morph/classes.js +48 -0
- package/dist/lib/transpile/morph/functions.js +76 -0
- package/dist/lib/transpile/morph/index.js +67 -0
- package/dist/lib/transpile/morph/typedefs.js +133 -0
- package/dist/lib/transpile/morph/utils.js +15 -0
- package/dist/lib/{transpile-ts.js → transpile/project.js} +38 -59
- package/oclif.manifest.json +27 -1
- package/package.json +7 -7
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Node } from 'ts-morph';
|
|
2
|
+
import { generateFunctionDocumentation } from './functions.js';
|
|
3
|
+
import { getJsDocOrCreate } from './utils.js';
|
|
4
|
+
|
|
5
|
+
function generateClassDocumentation(classNode) {
|
|
6
|
+
generateClassBaseDocumentation(classNode);
|
|
7
|
+
classNode.getMembers().forEach(generateClassMemberDocumentation);
|
|
8
|
+
}
|
|
9
|
+
function generateClassMemberDocumentation(classMember) {
|
|
10
|
+
generateModifierDocumentation(classMember);
|
|
11
|
+
if (Node.isPropertyDeclaration(classMember) || Node.isPropertyAssignment(classMember) || Node.isPropertySignature(classMember)) {
|
|
12
|
+
generateClassPropertyDocumentation(classMember);
|
|
13
|
+
}
|
|
14
|
+
if (Node.isMethodDeclaration(classMember) || Node.isConstructorDeclaration(classMember)) {
|
|
15
|
+
generateFunctionDocumentation(classMember);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function generateClassBaseDocumentation(classNode) {
|
|
19
|
+
const jsDoc = getJsDocOrCreate(classNode);
|
|
20
|
+
const extendedClass = classNode.getExtends();
|
|
21
|
+
if (extendedClass) {
|
|
22
|
+
jsDoc.addTag({ tagName: "extends", text: extendedClass.getText() });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function generateClassPropertyDocumentation(classMember) {
|
|
26
|
+
const structure = classMember.getStructure();
|
|
27
|
+
if (structure && "initializer" in structure) {
|
|
28
|
+
const initializer = structure.initializer;
|
|
29
|
+
if (initializer && initializer !== "undefined") {
|
|
30
|
+
const jsDoc = getJsDocOrCreate(classMember);
|
|
31
|
+
jsDoc.addTag({ tagName: "default", text: initializer });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function generateModifierDocumentation(classMember) {
|
|
36
|
+
if ("getModifiers" in classMember) {
|
|
37
|
+
const modifiers = classMember.getModifiers() || [];
|
|
38
|
+
for (const modifier of modifiers) {
|
|
39
|
+
const text = modifier?.getText();
|
|
40
|
+
if (["public", "private", "protected", "readonly", "static"].includes(text)) {
|
|
41
|
+
const jsDoc = getJsDocOrCreate(classMember);
|
|
42
|
+
jsDoc.addTag({ tagName: text });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { generateClassDocumentation };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { getJsDocOrCreate, sanitizeType } from './utils.js';
|
|
2
|
+
|
|
3
|
+
function generateFunctionDocumentation(functionNode, variableStatement) {
|
|
4
|
+
const outputDocNode = getOutputJsDocNodeOrCreate(
|
|
5
|
+
functionNode,
|
|
6
|
+
variableStatement
|
|
7
|
+
);
|
|
8
|
+
generateParameterDocumentation(functionNode, outputDocNode);
|
|
9
|
+
const jsDocs = variableStatement?.getJsDocs()[0];
|
|
10
|
+
if (jsDocs?.getTags().length === 0) {
|
|
11
|
+
const declaration = variableStatement?.getDeclarations()[0];
|
|
12
|
+
const type = declaration?.getType().getText();
|
|
13
|
+
if (type && type !== "any" && type !== "unknown") {
|
|
14
|
+
const tagName = type.startsWith("() =>") ? "return" : "type";
|
|
15
|
+
const normalizedType = type.replace(/^\(\)\s+=>\s+/, "").replace(/typeof import\("[^"]+"\)\./, "typeof ");
|
|
16
|
+
const text = normalizedType === "SerializeFrom<typeof loader>" ? `{LoaderReturnData}` : `{${normalizedType}}`;
|
|
17
|
+
jsDocs.addTag({ tagName, text });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function generateParameterDocumentation(functionNode, docNode) {
|
|
22
|
+
const params = functionNode.getParameters();
|
|
23
|
+
const jsDoc = getJsDocOrCreate(docNode);
|
|
24
|
+
const paramTags = (jsDoc.getTags() || []).filter(
|
|
25
|
+
(tag) => ["param", "parameter"].includes(tag.getTagName())
|
|
26
|
+
);
|
|
27
|
+
const commentLookup = Object.fromEntries(
|
|
28
|
+
paramTags.map((tag) => [
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
tag.compilerNode.name?.getText().replace(/\[|\]|(=.*)/g, "").trim(),
|
|
31
|
+
(tag.getComment() || "").toString().trim()
|
|
32
|
+
])
|
|
33
|
+
);
|
|
34
|
+
const preferredTagName = paramTags[0]?.getTagName();
|
|
35
|
+
paramTags.forEach((tag) => tag.remove());
|
|
36
|
+
for (const param of params) {
|
|
37
|
+
const paramType = sanitizeType(param.getTypeNode()?.getText());
|
|
38
|
+
if (!paramType)
|
|
39
|
+
continue;
|
|
40
|
+
const paramName = param.compilerNode.name?.getText();
|
|
41
|
+
const isOptional = param.isOptional();
|
|
42
|
+
const isRest = param.isRestParameter();
|
|
43
|
+
const paramTypeOut = isRest ? `...${paramType.replace(/\[\]\s*$/, "")}` : paramType;
|
|
44
|
+
let defaultValue;
|
|
45
|
+
if (isOptional) {
|
|
46
|
+
const paramInitializer = param.getInitializer();
|
|
47
|
+
defaultValue = paramInitializer?.getText().replaceAll(/(\s|\t)*\n(\s|\t)*/g, " ");
|
|
48
|
+
}
|
|
49
|
+
let paramNameOut = paramName;
|
|
50
|
+
if (paramNameOut.match(/[{},]/))
|
|
51
|
+
paramNameOut = "";
|
|
52
|
+
if (paramNameOut && isOptional) {
|
|
53
|
+
const defaultValueOut = defaultValue !== void 0 ? `=${defaultValue}` : "";
|
|
54
|
+
paramNameOut = `[${paramNameOut}${defaultValueOut}]`;
|
|
55
|
+
}
|
|
56
|
+
paramNameOut = paramNameOut ? ` ${paramNameOut}` : "";
|
|
57
|
+
const comment = commentLookup[paramName.trim()];
|
|
58
|
+
jsDoc.addTag({
|
|
59
|
+
tagName: preferredTagName || "param",
|
|
60
|
+
text: `{${paramTypeOut}}${paramNameOut}${comment ? ` ${comment}` : ""}`
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function getOutputJsDocNodeOrCreate(functionNode, docNode) {
|
|
65
|
+
if (docNode) {
|
|
66
|
+
const funcNodeDocs = functionNode.getJsDocs();
|
|
67
|
+
if (funcNodeDocs.length)
|
|
68
|
+
return functionNode;
|
|
69
|
+
getJsDocOrCreate(docNode);
|
|
70
|
+
return docNode;
|
|
71
|
+
}
|
|
72
|
+
getJsDocOrCreate(functionNode);
|
|
73
|
+
return functionNode;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export { generateFunctionDocumentation };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { ScriptTarget, ts, Project, SyntaxKind, Node } from 'ts-morph';
|
|
2
|
+
import { generateClassDocumentation } from './classes.js';
|
|
3
|
+
import { generateFunctionDocumentation } from './functions.js';
|
|
4
|
+
import { generateTypeDefs } from './typedefs.js';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_COMPILER_OPTIONS = {
|
|
7
|
+
target: ScriptTarget.ESNext,
|
|
8
|
+
esModuleInterop: true,
|
|
9
|
+
jsx: ts.JsxEmit.Preserve,
|
|
10
|
+
removeComments: false,
|
|
11
|
+
lib: ["DOM", "DOM.Iterable", "ES2022"]
|
|
12
|
+
};
|
|
13
|
+
function transpileTs(src, filepath, addJsDoc) {
|
|
14
|
+
return addJsDoc ? transpileTsWithJSDocs(src, filepath, DEFAULT_COMPILER_OPTIONS) : restoreCode(
|
|
15
|
+
ts.transpileModule(escapeCode(src), {
|
|
16
|
+
reportDiagnostics: false,
|
|
17
|
+
compilerOptions: DEFAULT_COMPILER_OPTIONS
|
|
18
|
+
}).outputText
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
const BLANK_LINE_MARKER = "// BLANK_LINE_MARKER //";
|
|
22
|
+
const COMMENT_PROTECTOR_HEADER = "const __COMMENT_PROTECTOR_HEADER = 1;\n";
|
|
23
|
+
function escapeCode(code) {
|
|
24
|
+
code = COMMENT_PROTECTOR_HEADER + code;
|
|
25
|
+
return code.split("\n").map((line) => line.match(/^[\s\t]*$/) ? BLANK_LINE_MARKER + line : line).join("\n");
|
|
26
|
+
}
|
|
27
|
+
function restoreCode(code) {
|
|
28
|
+
return code.replace(COMMENT_PROTECTOR_HEADER, "").split("\n").map((rawLine) => {
|
|
29
|
+
const line = rawLine.trim();
|
|
30
|
+
return line.startsWith(BLANK_LINE_MARKER) ? line.slice(BLANK_LINE_MARKER.length) : rawLine;
|
|
31
|
+
}).join("\n").trim();
|
|
32
|
+
}
|
|
33
|
+
function transpileTsWithJSDocs(src, filepath, compilerOptions = {}) {
|
|
34
|
+
const project = new Project({
|
|
35
|
+
useInMemoryFileSystem: true,
|
|
36
|
+
skipAddingFilesFromTsConfig: true,
|
|
37
|
+
skipFileDependencyResolution: false,
|
|
38
|
+
compilerOptions
|
|
39
|
+
});
|
|
40
|
+
const sourceFile = project.createSourceFile(filepath, escapeCode(src));
|
|
41
|
+
sourceFile.getClasses().forEach(generateClassDocumentation);
|
|
42
|
+
sourceFile.getFunctions().forEach((node) => generateFunctionDocumentation(node));
|
|
43
|
+
sourceFile.getVariableDeclarations().forEach((varDeclaration) => {
|
|
44
|
+
const initializer = varDeclaration.getInitializerIfKind(SyntaxKind.ArrowFunction) || varDeclaration.getInitializerIfKind(SyntaxKind.FunctionExpression);
|
|
45
|
+
if (!initializer)
|
|
46
|
+
return void 0;
|
|
47
|
+
generateFunctionDocumentation(
|
|
48
|
+
initializer,
|
|
49
|
+
varDeclaration.getVariableStatement()
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
sourceFile.getDefaultExportSymbol()?.getValueDeclaration()?.getChildren()?.find(Node.isObjectLiteralExpression)?.forEachChild((child) => {
|
|
53
|
+
if (Node.isFunctionLikeDeclaration(child)) {
|
|
54
|
+
generateFunctionDocumentation(child);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
let result = project.emitToMemory()?.getFiles()?.find(
|
|
58
|
+
(file) => file.filePath.slice(0, -3) === sourceFile.getFilePath().slice(0, -3)
|
|
59
|
+
)?.text;
|
|
60
|
+
if (!result)
|
|
61
|
+
throw new Error("Could not emit output to memory.");
|
|
62
|
+
result = restoreCode(result);
|
|
63
|
+
result = generateTypeDefs(sourceFile, result);
|
|
64
|
+
return result.replace(/^\s*\/\*[*\s]+\*\/$\n/gm, "\n").trim() + "\n";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { DEFAULT_COMPILER_OPTIONS, transpileTs };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { getJsDocOrCreate, sanitizeType } from './utils.js';
|
|
2
|
+
|
|
3
|
+
function generateTypeDefs(sourceFile, code) {
|
|
4
|
+
const typedefs = sourceFile.getTypeAliases().map(
|
|
5
|
+
(typeAlias) => generateTypedefDocumentation(typeAlias, sourceFile).trim()
|
|
6
|
+
).concat(
|
|
7
|
+
sourceFile.getInterfaces().map(
|
|
8
|
+
(interfaceNode) => generateInterfaceDocumentation(interfaceNode).trim()
|
|
9
|
+
)
|
|
10
|
+
);
|
|
11
|
+
const typeImports = sourceFile.getImportDeclarations().filter((imp) => /({|\s|\n)type\s/.test(imp.getText()));
|
|
12
|
+
const typedefsFromImports = /* @__PURE__ */ new Map();
|
|
13
|
+
for (const typeImport of typeImports) {
|
|
14
|
+
const typeElements = [];
|
|
15
|
+
const isFullType = typeImport.isTypeOnly();
|
|
16
|
+
for (const namedImport of typeImport.getNamedImports()) {
|
|
17
|
+
if (isFullType || namedImport.isTypeOnly()) {
|
|
18
|
+
typeElements.push(namedImport.getName());
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (typeElements.length > 0) {
|
|
22
|
+
typedefsFromImports.set(
|
|
23
|
+
typeImport.getModuleSpecifierValue(),
|
|
24
|
+
typeElements
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
typedefs.push("");
|
|
29
|
+
typedefsFromImports.forEach((typeElements, moduleSpecifier) => {
|
|
30
|
+
for (const typeElement of typeElements) {
|
|
31
|
+
if (typeElement === "SerializeFrom")
|
|
32
|
+
continue;
|
|
33
|
+
const hasGeneric = typeElement === "V2_MetaFunction";
|
|
34
|
+
typedefs.push(
|
|
35
|
+
`/** ${hasGeneric ? "@template T " : ""}@typedef {import('${moduleSpecifier}').${typeElement}${hasGeneric ? "<T>" : ""}} ${typeElement} */`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const filepath = sourceFile.getFilePath();
|
|
40
|
+
if (filepath.includes("routes") || filepath.includes("root.")) {
|
|
41
|
+
const source = sourceFile.getText();
|
|
42
|
+
if (/(function loader\(|const loader =)/.test(source)) {
|
|
43
|
+
typedefs.push(
|
|
44
|
+
`/** @typedef {import('@shopify/remix-oxygen').SerializeFrom<typeof loader>} LoaderReturnData */`
|
|
45
|
+
);
|
|
46
|
+
code = code.replace(
|
|
47
|
+
/^\s+?const\s+([\w\d]+|\{[\w\d\s,.]+\})\s+?=\s+?useLoaderData\(\)/gms,
|
|
48
|
+
"/** @type {LoaderReturnData} */\n$&"
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (/(function action\(|const action =)/.test(source)) {
|
|
52
|
+
typedefs.push(
|
|
53
|
+
`/** @typedef {import('@shopify/remix-oxygen').SerializeFrom<typeof action>} ActionReturnData */`
|
|
54
|
+
);
|
|
55
|
+
code = code.replace(
|
|
56
|
+
/^\s+?const\s+([\w\d]+|\{[\w\d\s,.]+\})\s+?=\s+?useActionData\(\)/gms,
|
|
57
|
+
"/** @type {ActionReturnData} */\n$&"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return code + `
|
|
62
|
+
|
|
63
|
+
${typedefs.join("\n")}
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
function generateInterfaceDocumentation(interfaceNode) {
|
|
67
|
+
const name = interfaceNode.getName();
|
|
68
|
+
const jsDoc = getJsDocOrCreate(interfaceNode);
|
|
69
|
+
jsDoc.addTag({ tagName: "typedef", text: `{Object} ${name}` });
|
|
70
|
+
interfaceNode.getProperties().forEach((prop) => {
|
|
71
|
+
generateObjectPropertyDocumentation(prop, jsDoc);
|
|
72
|
+
});
|
|
73
|
+
return jsDoc.getFullText();
|
|
74
|
+
}
|
|
75
|
+
function generateTypedefDocumentation(typeNode, sourceFile) {
|
|
76
|
+
const name = typeNode.getName();
|
|
77
|
+
let { type } = typeNode.getStructure();
|
|
78
|
+
if (typeof type !== "string")
|
|
79
|
+
return "";
|
|
80
|
+
type = sanitizeType(type);
|
|
81
|
+
const dummyNode = sourceFile.addVariableStatement({
|
|
82
|
+
declarations: [{ name: `__dummy${name}`, initializer: "null" }]
|
|
83
|
+
});
|
|
84
|
+
const typeParams = typeNode.getTypeParameters();
|
|
85
|
+
const jsDoc = dummyNode.addJsDoc({
|
|
86
|
+
tags: [
|
|
87
|
+
{
|
|
88
|
+
tagName: "typedef",
|
|
89
|
+
text: `{${type}} ${name}`
|
|
90
|
+
},
|
|
91
|
+
...typeParams.map((param) => {
|
|
92
|
+
const constraint = param.getConstraint();
|
|
93
|
+
const defaultType = param.getDefault();
|
|
94
|
+
const paramName = param.getName();
|
|
95
|
+
const nameWithDefault = defaultType ? `[${paramName}=${defaultType.getText()}]` : paramName;
|
|
96
|
+
return {
|
|
97
|
+
tagName: "template",
|
|
98
|
+
text: `${constraint ? `{${constraint.getText()}} ` : ""}${nameWithDefault}`
|
|
99
|
+
};
|
|
100
|
+
})
|
|
101
|
+
]
|
|
102
|
+
}).getText();
|
|
103
|
+
dummyNode.remove();
|
|
104
|
+
return jsDoc;
|
|
105
|
+
}
|
|
106
|
+
function getChildProperties(node) {
|
|
107
|
+
const properties = node.getType().getProperties();
|
|
108
|
+
const valueDeclarations = properties.map((child) => child.getValueDeclaration()).filter((child) => node.getFullText().includes(child?.getFullText()));
|
|
109
|
+
return valueDeclarations ?? [];
|
|
110
|
+
}
|
|
111
|
+
function generateObjectPropertyDocumentation(node, jsDoc, name = "", topLevelCall = true) {
|
|
112
|
+
name = name || node.getName();
|
|
113
|
+
if (!topLevelCall)
|
|
114
|
+
name = `${name}.${node.getName()}`;
|
|
115
|
+
let propType = node.getTypeNode()?.getText()?.replace(/\n/g, "")?.replace(/\s/g, "");
|
|
116
|
+
propType = sanitizeType(propType);
|
|
117
|
+
const isOptional = node.hasQuestionToken() || node.getJsDocs()?.[0]?.getTags()?.some((tag) => tag.getTagName() === "optional");
|
|
118
|
+
const existingPropDocs = node.getJsDocs()?.[0]?.getDescription() || "";
|
|
119
|
+
const children = getChildProperties(node);
|
|
120
|
+
if (children.length)
|
|
121
|
+
propType = "Object";
|
|
122
|
+
jsDoc.addTag({
|
|
123
|
+
tagName: "property",
|
|
124
|
+
text: `{${propType}} ${isOptional ? `[${name}]` : name} ${existingPropDocs}`
|
|
125
|
+
});
|
|
126
|
+
if (children.length) {
|
|
127
|
+
children.forEach(
|
|
128
|
+
(child) => generateObjectPropertyDocumentation(child, jsDoc, name, false)
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export { generateTypeDefs };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function getJsDocOrCreate(node) {
|
|
2
|
+
return node.getJsDocs().at(-1) || node.addJsDoc({ description: "\n" });
|
|
3
|
+
}
|
|
4
|
+
function sanitizeType(str) {
|
|
5
|
+
if (!str)
|
|
6
|
+
return str;
|
|
7
|
+
const extractedClassFromTypeof = /{*typeof\s+([^(?:}|\s);]*)/gm.exec(
|
|
8
|
+
str
|
|
9
|
+
)?.[1];
|
|
10
|
+
if (!extractedClassFromTypeof)
|
|
11
|
+
return str;
|
|
12
|
+
return `Class<${extractedClassFromTypeof}>`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { getJsDocOrCreate, sanitizeType };
|
|
@@ -1,48 +1,20 @@
|
|
|
1
1
|
import { glob, removeFile, readFile, writeFile } from '@shopify/cli-kit/node/fs';
|
|
2
2
|
import { outputDebug } from '@shopify/cli-kit/node/output';
|
|
3
3
|
import { joinPath } from '@shopify/cli-kit/node/path';
|
|
4
|
-
import { getCodeFormatOptions, formatCode } from '
|
|
4
|
+
import { getCodeFormatOptions, formatCode } from '../format-code.js';
|
|
5
|
+
import { transpileFile } from './file.js';
|
|
5
6
|
|
|
6
|
-
const escapeNewLines = (code) => code.replace(/\n\n/g, "\n/* :newline: */");
|
|
7
|
-
const restoreNewLines = (code) => code.replace(/\/\* :newline: \*\//g, "\n");
|
|
8
|
-
const DEFAULT_TS_CONFIG = {
|
|
9
|
-
lib: ["DOM", "DOM.Iterable", "ES2022"],
|
|
10
|
-
isolatedModules: true,
|
|
11
|
-
esModuleInterop: true,
|
|
12
|
-
resolveJsonModule: true,
|
|
13
|
-
target: "ES2022",
|
|
14
|
-
strict: true,
|
|
15
|
-
allowJs: true,
|
|
16
|
-
forceConsistentCasingInFileNames: true,
|
|
17
|
-
skipLibCheck: true
|
|
18
|
-
};
|
|
19
|
-
async function transpileFile(code, config = DEFAULT_TS_CONFIG) {
|
|
20
|
-
const tsImport = await import('typescript');
|
|
21
|
-
const ts = tsImport.default ?? tsImport;
|
|
22
|
-
const withArtificialNewLines = escapeNewLines(code);
|
|
23
|
-
const compiled = ts.transpileModule(withArtificialNewLines, {
|
|
24
|
-
reportDiagnostics: false,
|
|
25
|
-
compilerOptions: {
|
|
26
|
-
...config,
|
|
27
|
-
// '1' tells TypeScript to preserve the JSX syntax.
|
|
28
|
-
jsx: 1,
|
|
29
|
-
removeComments: false
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
return restoreNewLines(compiled.outputText);
|
|
33
|
-
}
|
|
34
7
|
const DEFAULT_JS_CONFIG = {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
resolveJsonModule: true
|
|
8
|
+
checkJs: false,
|
|
9
|
+
target: "ES2022",
|
|
10
|
+
module: "ES2022",
|
|
11
|
+
moduleResolution: "bundler",
|
|
12
|
+
baseUrl: ".",
|
|
13
|
+
paths: {
|
|
14
|
+
"~/*": ["app/*"]
|
|
15
|
+
}
|
|
44
16
|
};
|
|
45
|
-
const JS_CONFIG_KEYS = [
|
|
17
|
+
const JS_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
46
18
|
"noLib",
|
|
47
19
|
"target",
|
|
48
20
|
"module",
|
|
@@ -53,13 +25,13 @@ const JS_CONFIG_KEYS = [
|
|
|
53
25
|
"baseUrl",
|
|
54
26
|
"paths",
|
|
55
27
|
...Object.keys(DEFAULT_JS_CONFIG)
|
|
56
|
-
];
|
|
57
|
-
function convertConfigToJS(tsConfig) {
|
|
28
|
+
]);
|
|
29
|
+
function convertConfigToJS(tsConfig, keepTypes = false) {
|
|
58
30
|
const jsConfig = {
|
|
59
31
|
compilerOptions: { ...DEFAULT_JS_CONFIG }
|
|
60
32
|
};
|
|
61
33
|
if (tsConfig.include) {
|
|
62
|
-
jsConfig.include = tsConfig.include.filter((s) => !s.endsWith(".d.ts")).map((s) => s.replace(
|
|
34
|
+
jsConfig.include = tsConfig.include.filter((s) => keepTypes || !s.endsWith(".d.ts")).map((s) => s.replace(/(?<!\.d)\.ts(x?)$/, ".js$1"));
|
|
63
35
|
}
|
|
64
36
|
if (tsConfig.compilerOptions) {
|
|
65
37
|
for (const key of JS_CONFIG_KEYS) {
|
|
@@ -70,7 +42,7 @@ function convertConfigToJS(tsConfig) {
|
|
|
70
42
|
}
|
|
71
43
|
return jsConfig;
|
|
72
44
|
}
|
|
73
|
-
async function transpileProject(projectDir) {
|
|
45
|
+
async function transpileProject(projectDir, keepTypes = true) {
|
|
74
46
|
const entries = await glob("**/*.+(ts|tsx)", {
|
|
75
47
|
absolute: true,
|
|
76
48
|
cwd: projectDir
|
|
@@ -78,11 +50,15 @@ async function transpileProject(projectDir) {
|
|
|
78
50
|
const formatConfig = await getCodeFormatOptions();
|
|
79
51
|
for (const entry of entries) {
|
|
80
52
|
if (entry.endsWith(".d.ts")) {
|
|
81
|
-
|
|
53
|
+
if (!keepTypes)
|
|
54
|
+
await removeFile(entry);
|
|
82
55
|
continue;
|
|
83
56
|
}
|
|
84
57
|
const tsx = await readFile(entry);
|
|
85
|
-
const mjs = await formatCode(
|
|
58
|
+
const mjs = await formatCode(
|
|
59
|
+
await transpileFile(tsx, entry, keepTypes),
|
|
60
|
+
formatConfig
|
|
61
|
+
);
|
|
86
62
|
await removeFile(entry);
|
|
87
63
|
await writeFile(entry.replace(/\.ts(x?)$/, ".js$1"), mjs);
|
|
88
64
|
}
|
|
@@ -100,7 +76,8 @@ async function transpileProject(projectDir) {
|
|
|
100
76
|
const tsConfigPath = joinPath(projectDir, "tsconfig.json");
|
|
101
77
|
const tsConfigWithComments = await readFile(tsConfigPath);
|
|
102
78
|
const jsConfig = convertConfigToJS(
|
|
103
|
-
JSON.parse(tsConfigWithComments.replace(/^\s*\/\/.*$/gm, ""))
|
|
79
|
+
JSON.parse(tsConfigWithComments.replace(/^\s*\/\/.*$/gm, "")),
|
|
80
|
+
keepTypes
|
|
104
81
|
);
|
|
105
82
|
await removeFile(tsConfigPath);
|
|
106
83
|
await writeFile(
|
|
@@ -117,19 +94,21 @@ async function transpileProject(projectDir) {
|
|
|
117
94
|
await readFile(joinPath(projectDir, "package.json"))
|
|
118
95
|
);
|
|
119
96
|
delete pkgJson.scripts["typecheck"];
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
97
|
+
if (!keepTypes) {
|
|
98
|
+
delete pkgJson.devDependencies["typescript"];
|
|
99
|
+
delete pkgJson.devDependencies["@shopify/oxygen-workers-types"];
|
|
100
|
+
for (const key of Object.keys(pkgJson.devDependencies)) {
|
|
101
|
+
if (key.startsWith("@types/")) {
|
|
102
|
+
delete pkgJson.devDependencies[key];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const codegenFlag = /\s*--codegen(-unstable)?/;
|
|
106
|
+
if (pkgJson.scripts?.dev) {
|
|
107
|
+
pkgJson.scripts.dev = pkgJson.scripts.dev.replace(codegenFlag, "");
|
|
108
|
+
}
|
|
109
|
+
if (pkgJson.scripts?.build) {
|
|
110
|
+
pkgJson.scripts.build = pkgJson.scripts.build.replace(codegenFlag, "");
|
|
125
111
|
}
|
|
126
|
-
}
|
|
127
|
-
const codegenFlag = /\s*--codegen(-unstable)?/;
|
|
128
|
-
if (pkgJson.scripts?.dev) {
|
|
129
|
-
pkgJson.scripts.dev = pkgJson.scripts.dev.replace(codegenFlag, "");
|
|
130
|
-
}
|
|
131
|
-
if (pkgJson.scripts?.build) {
|
|
132
|
-
pkgJson.scripts.build = pkgJson.scripts.build.replace(codegenFlag, "");
|
|
133
112
|
}
|
|
134
113
|
await writeFile(
|
|
135
114
|
joinPath(projectDir, "package.json"),
|
|
@@ -152,4 +131,4 @@ async function transpileProject(projectDir) {
|
|
|
152
131
|
}
|
|
153
132
|
}
|
|
154
133
|
|
|
155
|
-
export {
|
|
134
|
+
export { transpileProject };
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "5.
|
|
2
|
+
"version": "5.5.0",
|
|
3
3
|
"commands": {
|
|
4
4
|
"hydrogen:build": {
|
|
5
5
|
"id": "hydrogen:build",
|
|
@@ -592,6 +592,32 @@
|
|
|
592
592
|
},
|
|
593
593
|
"args": {}
|
|
594
594
|
},
|
|
595
|
+
"hydrogen:debug:cpu": {
|
|
596
|
+
"id": "hydrogen:debug:cpu",
|
|
597
|
+
"description": "Builds and profiles the server startup time the app.",
|
|
598
|
+
"strict": true,
|
|
599
|
+
"pluginName": "@shopify/cli-hydrogen",
|
|
600
|
+
"pluginAlias": "@shopify/cli-hydrogen",
|
|
601
|
+
"pluginType": "core",
|
|
602
|
+
"aliases": [],
|
|
603
|
+
"flags": {
|
|
604
|
+
"path": {
|
|
605
|
+
"name": "path",
|
|
606
|
+
"type": "option",
|
|
607
|
+
"description": "The path to the directory of the Hydrogen storefront. The default is the current directory.",
|
|
608
|
+
"multiple": false
|
|
609
|
+
},
|
|
610
|
+
"output": {
|
|
611
|
+
"name": "output",
|
|
612
|
+
"type": "option",
|
|
613
|
+
"description": "Specify a path to generate the profile file. Defaults to \"startup.cpuprofile\".",
|
|
614
|
+
"required": false,
|
|
615
|
+
"multiple": false,
|
|
616
|
+
"default": "startup.cpuprofile"
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
"args": {}
|
|
620
|
+
},
|
|
595
621
|
"hydrogen:env:list": {
|
|
596
622
|
"id": "hydrogen:env:list",
|
|
597
623
|
"description": "List the environments on your linked Hydrogen storefront.",
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"@shopify:registry": "https://registry.npmjs.org"
|
|
6
6
|
},
|
|
7
|
-
"version": "5.
|
|
7
|
+
"version": "5.5.0",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"dev": "tsup --watch",
|
|
13
13
|
"typecheck": "tsc --noEmit",
|
|
14
14
|
"generate:manifest": "node scripts/generate-manifest.mjs",
|
|
15
|
-
"test": "cross-env SHOPIFY_UNIT_TEST=1 vitest run --test-timeout=
|
|
16
|
-
"test:watch": "cross-env SHOPIFY_UNIT_TEST=1 vitest --test-timeout=
|
|
15
|
+
"test": "cross-env SHOPIFY_UNIT_TEST=1 vitest run --test-timeout=20000",
|
|
16
|
+
"test:watch": "cross-env SHOPIFY_UNIT_TEST=1 vitest --test-timeout=20000"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@types/diff": "^5.0.2",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@oclif/core": "2.11.7",
|
|
38
38
|
"@shopify/cli-kit": "3.49.2",
|
|
39
39
|
"@shopify/hydrogen-codegen": "^0.0.2",
|
|
40
|
-
"@shopify/mini-oxygen": "^2.2.
|
|
40
|
+
"@shopify/mini-oxygen": "^2.2.3",
|
|
41
41
|
"@shopify/oxygen-cli": "^2.0.0",
|
|
42
42
|
"ansi-escapes": "^6.2.0",
|
|
43
43
|
"diff": "^5.1.0",
|
|
@@ -49,15 +49,15 @@
|
|
|
49
49
|
"source-map": "^0.7.4",
|
|
50
50
|
"stack-trace": "^1.0.0-pre2",
|
|
51
51
|
"tar-fs": "^2.1.1",
|
|
52
|
-
"
|
|
52
|
+
"ts-morph": "20.0.0",
|
|
53
53
|
"use-resize-observer": "^9.1.0",
|
|
54
54
|
"ws": "^8.13.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
57
|
"@remix-run/dev": "1.19.1",
|
|
58
58
|
"@remix-run/react": "1.19.1",
|
|
59
|
-
"@shopify/hydrogen-react": "^2023.7.
|
|
60
|
-
"@shopify/remix-oxygen": "^1.1.
|
|
59
|
+
"@shopify/hydrogen-react": "^2023.7.5",
|
|
60
|
+
"@shopify/remix-oxygen": "^1.1.8"
|
|
61
61
|
},
|
|
62
62
|
"peerDependenciesMeta": {
|
|
63
63
|
"@remix-run/dev": {
|