@catmint/cli 0.0.0-prealpha.2 → 0.0.0-prealpha.20
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/build.d.ts.map +1 -1
- package/dist/commands/build.js +247 -109
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +104 -23
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +374 -7
- package/dist/commands/start.js.map +1 -1
- package/dist/route-analysis.d.ts +41 -0
- package/dist/route-analysis.d.ts.map +1 -0
- package/dist/route-analysis.js +177 -0
- package/dist/route-analysis.js.map +1 -0
- package/package.json +5 -4
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// catmint/cli/route-analysis — Shared route rendering mode detection
|
|
2
|
+
//
|
|
3
|
+
// Detects whether a page uses cachedRoute(), staticRoute(), or neither
|
|
4
|
+
// (dynamic) by parsing the source file with SWC AST analysis.
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
/**
|
|
8
|
+
* Detect the rendering mode of a page file using SWC AST analysis.
|
|
9
|
+
*
|
|
10
|
+
* Parses the file and inspects the default export:
|
|
11
|
+
* - `export default staticRoute(...)` → `"static"`
|
|
12
|
+
* - `export default cachedRoute(...)` → `"cached"`
|
|
13
|
+
* - anything else → `"dynamic"`
|
|
14
|
+
*
|
|
15
|
+
* Endpoints are always `"dynamic"`.
|
|
16
|
+
*/
|
|
17
|
+
export function detectRouteMode(filePath, routeType) {
|
|
18
|
+
// Endpoints are always dynamic
|
|
19
|
+
if (routeType === "endpoint")
|
|
20
|
+
return "dynamic";
|
|
21
|
+
if (!existsSync(filePath))
|
|
22
|
+
return "dynamic";
|
|
23
|
+
// Only analyze TypeScript/JavaScript files, not MDX
|
|
24
|
+
if (!/\.(tsx?|jsx?)$/.test(filePath))
|
|
25
|
+
return "dynamic";
|
|
26
|
+
const code = readFileSync(filePath, "utf-8");
|
|
27
|
+
// Quick bail-out: if neither wrapper is mentioned, it's dynamic
|
|
28
|
+
const hasCached = code.includes("cachedRoute");
|
|
29
|
+
const hasStatic = code.includes("staticRoute");
|
|
30
|
+
if (!hasCached && !hasStatic)
|
|
31
|
+
return "dynamic";
|
|
32
|
+
const calleeName = findDefaultExportCallee(code);
|
|
33
|
+
if (calleeName === "staticRoute")
|
|
34
|
+
return "static";
|
|
35
|
+
if (calleeName === "cachedRoute")
|
|
36
|
+
return "cached";
|
|
37
|
+
return "dynamic";
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Extract `cachedRoute()` options from a page source file using SWC AST analysis.
|
|
41
|
+
*
|
|
42
|
+
* Parses the file with SWC and walks the AST to find the default export.
|
|
43
|
+
* If it's a `cachedRoute(handler, options)` call, extracts the options object
|
|
44
|
+
* (revalidate, tag, staleWhileRevalidate) from the second argument.
|
|
45
|
+
*
|
|
46
|
+
* This approach is robust against code examples in string literals, JSX content,
|
|
47
|
+
* and unusual formatting — unlike regex-based extraction.
|
|
48
|
+
*
|
|
49
|
+
* Returns null if the file doesn't use cachedRoute as its default export.
|
|
50
|
+
*/
|
|
51
|
+
export function extractCacheOptions(filePath) {
|
|
52
|
+
if (!existsSync(filePath))
|
|
53
|
+
return null;
|
|
54
|
+
// Only analyze TypeScript/JavaScript page files, not MDX documentation
|
|
55
|
+
if (!/\.(tsx?|jsx?)$/.test(filePath))
|
|
56
|
+
return null;
|
|
57
|
+
const code = readFileSync(filePath, "utf-8");
|
|
58
|
+
// Quick bail-out: if the file doesn't mention cachedRoute at all, skip parsing
|
|
59
|
+
if (!code.includes("cachedRoute"))
|
|
60
|
+
return null;
|
|
61
|
+
const ast = parseWithSwc(code);
|
|
62
|
+
if (!ast)
|
|
63
|
+
return null;
|
|
64
|
+
// Find the default export statement
|
|
65
|
+
for (const node of ast.body) {
|
|
66
|
+
// Handle: export default cachedRoute(...)
|
|
67
|
+
if (node.type === "ExportDefaultExpression" &&
|
|
68
|
+
node.expression?.type === "CallExpression" &&
|
|
69
|
+
node.expression.callee?.type === "Identifier" &&
|
|
70
|
+
node.expression.callee.value === "cachedRoute") {
|
|
71
|
+
return extractOptionsFromCallArgs(node.expression.arguments);
|
|
72
|
+
}
|
|
73
|
+
// Handle: export default cachedRoute(...) where the expression is wrapped
|
|
74
|
+
if (node.type === "ExportDefaultDeclaration" &&
|
|
75
|
+
node.decl?.type === "CallExpression" &&
|
|
76
|
+
node.decl.callee?.type === "Identifier" &&
|
|
77
|
+
node.decl.callee.value === "cachedRoute") {
|
|
78
|
+
return extractOptionsFromCallArgs(node.decl.arguments);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Internal helpers
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
/**
|
|
87
|
+
* Parse a TypeScript/TSX source file with SWC.
|
|
88
|
+
* Returns null if parsing fails.
|
|
89
|
+
*/
|
|
90
|
+
function parseWithSwc(code) {
|
|
91
|
+
try {
|
|
92
|
+
const esmRequire = createRequire(import.meta.url);
|
|
93
|
+
const { parseSync } = esmRequire("@swc/core");
|
|
94
|
+
return parseSync(code, {
|
|
95
|
+
syntax: "typescript",
|
|
96
|
+
tsx: true,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Find the callee name of the default export if it's a call expression.
|
|
105
|
+
*
|
|
106
|
+
* e.g. `export default cachedRoute(...)` → `"cachedRoute"`
|
|
107
|
+
* e.g. `export default staticRoute(...)` → `"staticRoute"`
|
|
108
|
+
* e.g. `export default function Page()` → `null`
|
|
109
|
+
*/
|
|
110
|
+
function findDefaultExportCallee(code) {
|
|
111
|
+
const ast = parseWithSwc(code);
|
|
112
|
+
if (!ast)
|
|
113
|
+
return null;
|
|
114
|
+
for (const node of ast.body) {
|
|
115
|
+
// export default someCall(...)
|
|
116
|
+
if (node.type === "ExportDefaultExpression" &&
|
|
117
|
+
node.expression?.type === "CallExpression" &&
|
|
118
|
+
node.expression.callee?.type === "Identifier") {
|
|
119
|
+
return node.expression.callee.value;
|
|
120
|
+
}
|
|
121
|
+
// export default declaration form
|
|
122
|
+
if (node.type === "ExportDefaultDeclaration" &&
|
|
123
|
+
node.decl?.type === "CallExpression" &&
|
|
124
|
+
node.decl.callee?.type === "Identifier") {
|
|
125
|
+
return node.decl.callee.value;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Extract cache options from the arguments array of a cachedRoute() call AST node.
|
|
132
|
+
*
|
|
133
|
+
* cachedRoute(handler, { tag: [...], revalidate: N, staleWhileRevalidate: bool })
|
|
134
|
+
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
135
|
+
* This is args[1] — an ObjectExpression
|
|
136
|
+
*/
|
|
137
|
+
function extractOptionsFromCallArgs(args) {
|
|
138
|
+
if (!args || args.length < 2) {
|
|
139
|
+
// cachedRoute(handler) without options — mark as cached with empty options
|
|
140
|
+
return args?.length === 1 ? {} : null;
|
|
141
|
+
}
|
|
142
|
+
const optionsArg = args[1]?.expression;
|
|
143
|
+
if (!optionsArg || optionsArg.type !== "ObjectExpression") {
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
const result = {};
|
|
147
|
+
for (const prop of optionsArg.properties) {
|
|
148
|
+
if (prop.type !== "KeyValueProperty")
|
|
149
|
+
continue;
|
|
150
|
+
const key = prop.key?.type === "Identifier"
|
|
151
|
+
? prop.key.value
|
|
152
|
+
: prop.key?.type === "StringLiteral"
|
|
153
|
+
? prop.key.value
|
|
154
|
+
: null;
|
|
155
|
+
if (key === "revalidate" && prop.value?.type === "NumericLiteral") {
|
|
156
|
+
result.revalidate = prop.value.value;
|
|
157
|
+
}
|
|
158
|
+
if (key === "tag" && prop.value?.type === "ArrayExpression") {
|
|
159
|
+
const tags = [];
|
|
160
|
+
for (const elem of prop.value.elements) {
|
|
161
|
+
const expr = elem?.expression ?? elem;
|
|
162
|
+
if (expr?.type === "StringLiteral") {
|
|
163
|
+
tags.push(expr.value);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (tags.length > 0) {
|
|
167
|
+
result.tags = tags;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (key === "staleWhileRevalidate" &&
|
|
171
|
+
prop.value?.type === "BooleanLiteral") {
|
|
172
|
+
result.staleWhileRevalidate = prop.value.value;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=route-analysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-analysis.js","sourceRoot":"","sources":["../src/route-analysis.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@catmint/cli",
|
|
3
|
-
"version": "0.0.0-prealpha.
|
|
3
|
+
"version": "0.0.0-prealpha.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
|
-
"license": "
|
|
6
|
+
"license": "GPL-2.0",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
9
|
"bin"
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"picocolors": "^1.1.1",
|
|
21
22
|
"vite": "^6.0.0",
|
|
22
|
-
"catmint": "0.0.0-prealpha.
|
|
23
|
-
"@catmint/vite": "0.0.0-prealpha.
|
|
23
|
+
"catmint": "0.0.0-prealpha.20",
|
|
24
|
+
"@catmint/vite": "0.0.0-prealpha.20"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
27
|
"@swc/core": "^1.15.11",
|