@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.
@@ -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.2",
3
+ "version": "0.0.0-prealpha.20",
4
4
  "type": "module",
5
5
  "private": false,
6
- "license": "MIT",
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.2",
23
- "@catmint/vite": "0.0.0-prealpha.2"
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",