vinext 0.0.31 → 0.0.33
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/README.md +10 -7
- package/dist/build/report.d.ts +1 -1
- package/dist/build/report.js +334 -0
- package/dist/build/report.js.map +1 -1
- package/dist/build/run-prerender.js +2 -2
- package/dist/build/run-prerender.js.map +1 -1
- package/dist/check.js +93 -3
- package/dist/check.js.map +1 -1
- package/dist/cli.js +3 -1
- package/dist/cli.js.map +1 -1
- package/dist/config/next-config.d.ts +2 -0
- package/dist/config/next-config.js +9 -3
- package/dist/config/next-config.js.map +1 -1
- package/dist/entries/app-browser-entry.js +3 -330
- package/dist/entries/app-browser-entry.js.map +1 -1
- package/dist/entries/app-rsc-entry.js +286 -644
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/app-ssr-entry.js +4 -460
- package/dist/entries/app-ssr-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +2 -1
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/entries/runtime-entry-module.d.ts +13 -0
- package/dist/entries/runtime-entry-module.js +27 -0
- package/dist/entries/runtime-entry-module.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +16 -35
- package/dist/index.js.map +1 -1
- package/dist/plugins/optimize-imports.d.ts +38 -0
- package/dist/plugins/optimize-imports.js +557 -0
- package/dist/plugins/optimize-imports.js.map +1 -0
- package/dist/routing/pages-router.js +1 -1
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/server/api-handler.d.ts +2 -2
- package/dist/server/api-handler.js +3 -4
- package/dist/server/api-handler.js.map +1 -1
- package/dist/server/app-browser-entry.d.ts +1 -0
- package/dist/server/app-browser-entry.js +161 -0
- package/dist/server/app-browser-entry.js.map +1 -0
- package/dist/server/app-browser-stream.d.ts +33 -0
- package/dist/server/app-browser-stream.js +54 -0
- package/dist/server/app-browser-stream.js.map +1 -0
- package/dist/server/app-page-cache.d.ts +61 -0
- package/dist/server/app-page-cache.js +133 -0
- package/dist/server/app-page-cache.js.map +1 -0
- package/dist/server/app-page-response.d.ts +51 -0
- package/dist/server/app-page-response.js +90 -0
- package/dist/server/app-page-response.js.map +1 -0
- package/dist/server/app-route-handler-cache.d.ts +42 -0
- package/dist/server/app-route-handler-cache.js +69 -0
- package/dist/server/app-route-handler-cache.js.map +1 -0
- package/dist/server/app-route-handler-execution.d.ts +64 -0
- package/dist/server/app-route-handler-execution.js +100 -0
- package/dist/server/app-route-handler-execution.js.map +1 -0
- package/dist/server/app-route-handler-policy.d.ts +51 -0
- package/dist/server/app-route-handler-policy.js +57 -0
- package/dist/server/app-route-handler-policy.js.map +1 -0
- package/dist/server/app-route-handler-response.d.ts +26 -0
- package/dist/server/app-route-handler-response.js +61 -0
- package/dist/server/app-route-handler-response.js.map +1 -0
- package/dist/server/app-route-handler-runtime.d.ts +27 -0
- package/dist/server/app-route-handler-runtime.js +99 -0
- package/dist/server/app-route-handler-runtime.js.map +1 -0
- package/dist/server/app-ssr-entry.d.ts +19 -0
- package/dist/server/app-ssr-entry.js +105 -0
- package/dist/server/app-ssr-entry.js.map +1 -0
- package/dist/server/app-ssr-stream.d.ts +30 -0
- package/dist/server/app-ssr-stream.js +116 -0
- package/dist/server/app-ssr-stream.js.map +1 -0
- package/dist/server/dev-server.d.ts +3 -2
- package/dist/server/dev-server.js +29 -30
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/instrumentation.d.ts +11 -39
- package/dist/server/instrumentation.js +14 -15
- package/dist/server/instrumentation.js.map +1 -1
- package/dist/server/middleware.d.ts +3 -2
- package/dist/server/middleware.js +12 -29
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/prod-server.js +3 -2
- package/dist/server/prod-server.js.map +1 -1
- package/dist/shims/compat-router.d.ts +3 -1
- package/dist/shims/compat-router.js.map +1 -1
- package/dist/shims/error-boundary.d.ts +1 -1
- package/dist/shims/fetch-cache.js +2 -0
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/head.d.ts +2 -1
- package/dist/shims/head.js +27 -5
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/internal/router-context.d.ts +2 -1
- package/dist/shims/internal/router-context.js.map +1 -1
- package/dist/shims/metadata.js +3 -3
- package/dist/shims/metadata.js.map +1 -1
- package/dist/shims/request-state-types.d.ts +2 -2
- package/dist/shims/router.d.ts +1 -1
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/server.d.ts +41 -3
- package/dist/shims/server.js +90 -7
- package/dist/shims/server.js.map +1 -1
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,6 +99,8 @@ This will:
|
|
|
99
99
|
|
|
100
100
|
The migration is non-destructive -- your existing Next.js setup continues to work alongside vinext. It does not modify `next.config`, `tsconfig.json`, or any source files, and it does not remove Next.js dependencies.
|
|
101
101
|
|
|
102
|
+
vinext supports both Vite 7 and Vite 8. If you bring custom Vite config or plugins from an older setup, note that Vite 8 now defaults to Rolldown, Oxc, Lightning CSS, and a newer browser baseline. Prefer `oxc`, `optimizeDeps.rolldownOptions`, and `build.rolldownOptions` over older `esbuild` and `build.rollupOptions` knobs, and override `build.target` if you still need older browsers. If a dependency only breaks on Vite 8 because of stricter CommonJS default import handling, fix the import or use `legacy.inconsistentCjsInterop: true` as a temporary escape hatch. See the [Vite 8 migration guide](https://vite.dev/guide/migration).
|
|
103
|
+
|
|
102
104
|
```bash
|
|
103
105
|
npm run dev:vinext # Start the vinext dev server (port 3001)
|
|
104
106
|
npm run dev # Still runs Next.js as before
|
|
@@ -478,12 +480,13 @@ Every `next/*` import is shimmed to a Vite-compatible implementation.
|
|
|
478
480
|
|
|
479
481
|
### Configuration
|
|
480
482
|
|
|
481
|
-
| Feature | | Notes
|
|
482
|
-
| ------------------------------------------------ | --- |
|
|
483
|
-
| `next.config.js` / `.ts` / `.mjs` | ✅ | Function configs, phase argument
|
|
484
|
-
| `rewrites` / `redirects` / `headers` | ✅ | All phases, param interpolation
|
|
485
|
-
| Environment variables (`.env*`, `NEXT_PUBLIC_*`) | ✅ | Auto-loads Next.js-style dotenv files; only public vars are inlined
|
|
486
|
-
| `images` config | 🟡 | Parsed but not used for optimization
|
|
483
|
+
| Feature | | Notes |
|
|
484
|
+
| ------------------------------------------------ | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
485
|
+
| `next.config.js` / `.ts` / `.mjs` | ✅ | Function configs, phase argument |
|
|
486
|
+
| `rewrites` / `redirects` / `headers` | ✅ | All phases, param interpolation |
|
|
487
|
+
| Environment variables (`.env*`, `NEXT_PUBLIC_*`) | ✅ | Auto-loads Next.js-style dotenv files; only public vars are inlined |
|
|
488
|
+
| `images` config | 🟡 | Parsed but not used for optimization |
|
|
489
|
+
| `experimental.optimizePackageImports` | ✅ | Rewrites barrel imports to direct sub-module imports in RSC/SSR environments. A default set (lucide-react, date-fns, radix-ui, antd, MUI, and others) are always optimized. Add package names here to extend the list. |
|
|
487
490
|
|
|
488
491
|
### Environment variable loading (`.env*`)
|
|
489
492
|
|
|
@@ -696,7 +699,7 @@ Or add it to your `package.json` as a file dependency:
|
|
|
696
699
|
}
|
|
697
700
|
```
|
|
698
701
|
|
|
699
|
-
vinext has peer dependencies on `react ^19.2.4`, `react-dom ^19.2.4`, and `vite ^7.0.0`. Then replace `next` with `vinext` in your scripts and run as normal.
|
|
702
|
+
vinext has peer dependencies on `react ^19.2.4`, `react-dom ^19.2.4`, and `vite ^7.0.0 || ^8.0.0`. Then replace `next` with `vinext` in your scripts and run as normal.
|
|
700
703
|
|
|
701
704
|
## Contributing
|
|
702
705
|
|
package/dist/build/report.d.ts
CHANGED
|
@@ -108,7 +108,7 @@ declare function findDir(root: string, ...candidates: string[]): string | null;
|
|
|
108
108
|
*/
|
|
109
109
|
declare function printBuildReport(options: {
|
|
110
110
|
root: string;
|
|
111
|
-
pageExtensions
|
|
111
|
+
pageExtensions: string[];
|
|
112
112
|
prerenderResult?: PrerenderResult;
|
|
113
113
|
}): Promise<void>;
|
|
114
114
|
//#endregion
|
package/dist/build/report.js
CHANGED
|
@@ -67,12 +67,346 @@ function extractExportConstNumber(code, name) {
|
|
|
67
67
|
* null — no `revalidate` key found (fully static)
|
|
68
68
|
*/
|
|
69
69
|
function extractGetStaticPropsRevalidate(code) {
|
|
70
|
+
const returnObjects = extractGetStaticPropsReturnObjects(code);
|
|
71
|
+
if (returnObjects) {
|
|
72
|
+
for (const searchSpace of returnObjects) {
|
|
73
|
+
const revalidate = extractTopLevelRevalidateValue(searchSpace);
|
|
74
|
+
if (revalidate !== null) return revalidate;
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
70
78
|
const m = /\brevalidate\s*:\s*(-?\d+(?:\.\d+)?|Infinity|false)\b/.exec(code);
|
|
71
79
|
if (!m) return null;
|
|
72
80
|
if (m[1] === "false") return false;
|
|
73
81
|
if (m[1] === "Infinity") return Infinity;
|
|
74
82
|
return parseFloat(m[1]);
|
|
75
83
|
}
|
|
84
|
+
function extractTopLevelRevalidateValue(code) {
|
|
85
|
+
let braceDepth = 0;
|
|
86
|
+
let parenDepth = 0;
|
|
87
|
+
let bracketDepth = 0;
|
|
88
|
+
let quote = null;
|
|
89
|
+
let inLineComment = false;
|
|
90
|
+
let inBlockComment = false;
|
|
91
|
+
for (let i = 0; i < code.length; i++) {
|
|
92
|
+
const char = code[i];
|
|
93
|
+
const next = code[i + 1];
|
|
94
|
+
if (inLineComment) {
|
|
95
|
+
if (char === "\n") inLineComment = false;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (inBlockComment) {
|
|
99
|
+
if (char === "*" && next === "/") {
|
|
100
|
+
inBlockComment = false;
|
|
101
|
+
i++;
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (quote) {
|
|
106
|
+
if (char === "\\") {
|
|
107
|
+
i++;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (char === quote) quote = null;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (char === "/" && next === "/") {
|
|
114
|
+
inLineComment = true;
|
|
115
|
+
i++;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (char === "/" && next === "*") {
|
|
119
|
+
inBlockComment = true;
|
|
120
|
+
i++;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
124
|
+
quote = char;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (char === "{") {
|
|
128
|
+
braceDepth++;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (char === "}") {
|
|
132
|
+
braceDepth--;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (char === "(") {
|
|
136
|
+
parenDepth++;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (char === ")") {
|
|
140
|
+
parenDepth--;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (char === "[") {
|
|
144
|
+
bracketDepth++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (char === "]") {
|
|
148
|
+
bracketDepth--;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (braceDepth === 1 && parenDepth === 0 && bracketDepth === 0 && matchesKeywordAt(code, i, "revalidate")) {
|
|
152
|
+
const colonIndex = findNextNonWhitespaceIndex(code, i + 10);
|
|
153
|
+
if (colonIndex === -1 || code[colonIndex] !== ":") continue;
|
|
154
|
+
const valueStart = findNextNonWhitespaceIndex(code, colonIndex + 1);
|
|
155
|
+
if (valueStart === -1) return null;
|
|
156
|
+
const valueMatch = /^(-?\d+(?:\.\d+)?|Infinity|false)\b/.exec(code.slice(valueStart));
|
|
157
|
+
if (!valueMatch) return null;
|
|
158
|
+
if (valueMatch[1] === "false") return false;
|
|
159
|
+
if (valueMatch[1] === "Infinity") return Infinity;
|
|
160
|
+
return parseFloat(valueMatch[1]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
function extractGetStaticPropsReturnObjects(code) {
|
|
166
|
+
const declarationMatch = /(?:^|\n)\s*(?:export\s+)?(?:async\s+)?function\s+getStaticProps\b|(?:^|\n)\s*(?:export\s+)?(?:const|let|var)\s+getStaticProps\b/.exec(code);
|
|
167
|
+
if (!declarationMatch) {
|
|
168
|
+
if (/(?:^|\n)\s*export\s*\{[^}]*\bgetStaticProps\b[^}]*\}\s*from\b/.test(code)) return [];
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
const declaration = extractGetStaticPropsDeclaration(code, declarationMatch);
|
|
172
|
+
if (declaration === null) return [];
|
|
173
|
+
const returnObjects = declaration.trimStart().startsWith("{") ? collectReturnObjectsFromFunctionBody(declaration) : [];
|
|
174
|
+
if (returnObjects.length > 0) return returnObjects;
|
|
175
|
+
const arrowMatch = declaration.search(/=>\s*\(\s*\{/);
|
|
176
|
+
if (arrowMatch === -1) return [];
|
|
177
|
+
const braceStart = declaration.indexOf("{", arrowMatch);
|
|
178
|
+
if (braceStart === -1) return [];
|
|
179
|
+
const braceEnd = findMatchingBrace(declaration, braceStart);
|
|
180
|
+
if (braceEnd === -1) return [];
|
|
181
|
+
return [declaration.slice(braceStart, braceEnd + 1)];
|
|
182
|
+
}
|
|
183
|
+
function extractGetStaticPropsDeclaration(code, declarationMatch) {
|
|
184
|
+
const declarationStart = declarationMatch.index;
|
|
185
|
+
const declarationText = declarationMatch[0];
|
|
186
|
+
const declarationTail = code.slice(declarationStart);
|
|
187
|
+
if (declarationText.includes("function getStaticProps")) return extractFunctionBody(code, declarationStart + declarationText.length);
|
|
188
|
+
const functionExpressionMatch = /(?:async\s+)?function\b/.exec(declarationTail);
|
|
189
|
+
if (functionExpressionMatch) return extractFunctionBody(declarationTail, functionExpressionMatch.index);
|
|
190
|
+
const blockBodyMatch = /=>\s*\{/.exec(declarationTail);
|
|
191
|
+
if (blockBodyMatch) {
|
|
192
|
+
const braceStart = declarationTail.indexOf("{", blockBodyMatch.index);
|
|
193
|
+
if (braceStart === -1) return null;
|
|
194
|
+
const braceEnd = findMatchingBrace(declarationTail, braceStart);
|
|
195
|
+
if (braceEnd === -1) return null;
|
|
196
|
+
return declarationTail.slice(braceStart, braceEnd + 1);
|
|
197
|
+
}
|
|
198
|
+
const implicitArrowMatch = declarationTail.search(/=>\s*\(\s*\{/);
|
|
199
|
+
if (implicitArrowMatch === -1) return null;
|
|
200
|
+
const implicitBraceStart = declarationTail.indexOf("{", implicitArrowMatch);
|
|
201
|
+
if (implicitBraceStart === -1) return null;
|
|
202
|
+
const implicitBraceEnd = findMatchingBrace(declarationTail, implicitBraceStart);
|
|
203
|
+
if (implicitBraceEnd === -1) return null;
|
|
204
|
+
return declarationTail.slice(0, implicitBraceEnd + 1);
|
|
205
|
+
}
|
|
206
|
+
function extractFunctionBody(code, functionStart) {
|
|
207
|
+
const bodyEnd = findFunctionBodyEnd(code, functionStart);
|
|
208
|
+
if (bodyEnd === -1) return null;
|
|
209
|
+
const paramsStart = code.indexOf("(", functionStart);
|
|
210
|
+
if (paramsStart === -1) return null;
|
|
211
|
+
const paramsEnd = findMatchingParen(code, paramsStart);
|
|
212
|
+
if (paramsEnd === -1) return null;
|
|
213
|
+
const bodyStart = code.indexOf("{", paramsEnd + 1);
|
|
214
|
+
if (bodyStart === -1) return null;
|
|
215
|
+
return code.slice(bodyStart, bodyEnd + 1);
|
|
216
|
+
}
|
|
217
|
+
function collectReturnObjectsFromFunctionBody(code) {
|
|
218
|
+
const returnObjects = [];
|
|
219
|
+
let quote = null;
|
|
220
|
+
let inLineComment = false;
|
|
221
|
+
let inBlockComment = false;
|
|
222
|
+
for (let i = 0; i < code.length; i++) {
|
|
223
|
+
const char = code[i];
|
|
224
|
+
const next = code[i + 1];
|
|
225
|
+
if (inLineComment) {
|
|
226
|
+
if (char === "\n") inLineComment = false;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (inBlockComment) {
|
|
230
|
+
if (char === "*" && next === "/") {
|
|
231
|
+
inBlockComment = false;
|
|
232
|
+
i++;
|
|
233
|
+
}
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (quote) {
|
|
237
|
+
if (char === "\\") {
|
|
238
|
+
i++;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (char === quote) quote = null;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (char === "/" && next === "/") {
|
|
245
|
+
inLineComment = true;
|
|
246
|
+
i++;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (char === "/" && next === "*") {
|
|
250
|
+
inBlockComment = true;
|
|
251
|
+
i++;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
255
|
+
quote = char;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (matchesKeywordAt(code, i, "function")) {
|
|
259
|
+
const nestedBodyEnd = findFunctionBodyEnd(code, i);
|
|
260
|
+
if (nestedBodyEnd !== -1) i = nestedBodyEnd;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (matchesKeywordAt(code, i, "class")) {
|
|
264
|
+
const classBodyEnd = findClassBodyEnd(code, i);
|
|
265
|
+
if (classBodyEnd !== -1) i = classBodyEnd;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (char === "=" && next === ">") {
|
|
269
|
+
const nestedBodyEnd = findArrowFunctionBodyEnd(code, i);
|
|
270
|
+
if (nestedBodyEnd !== -1) i = nestedBodyEnd;
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (char >= "A" && char <= "Z" || char >= "a" && char <= "z" || char === "_" || char === "$" || char === "*") {
|
|
274
|
+
const methodBodyEnd = findObjectMethodBodyEnd(code, i);
|
|
275
|
+
if (methodBodyEnd !== -1) {
|
|
276
|
+
i = methodBodyEnd;
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (matchesKeywordAt(code, i, "return")) {
|
|
281
|
+
const braceStart = findNextNonWhitespaceIndex(code, i + 6);
|
|
282
|
+
if (braceStart === -1 || code[braceStart] !== "{") continue;
|
|
283
|
+
const braceEnd = findMatchingBrace(code, braceStart);
|
|
284
|
+
if (braceEnd === -1) continue;
|
|
285
|
+
returnObjects.push(code.slice(braceStart, braceEnd + 1));
|
|
286
|
+
i = braceEnd;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return returnObjects;
|
|
290
|
+
}
|
|
291
|
+
function findFunctionBodyEnd(code, functionStart) {
|
|
292
|
+
const paramsStart = code.indexOf("(", functionStart);
|
|
293
|
+
if (paramsStart === -1) return -1;
|
|
294
|
+
const paramsEnd = findMatchingParen(code, paramsStart);
|
|
295
|
+
if (paramsEnd === -1) return -1;
|
|
296
|
+
const bodyStart = code.indexOf("{", paramsEnd + 1);
|
|
297
|
+
if (bodyStart === -1) return -1;
|
|
298
|
+
return findMatchingBrace(code, bodyStart);
|
|
299
|
+
}
|
|
300
|
+
function findClassBodyEnd(code, classStart) {
|
|
301
|
+
const bodyStart = code.indexOf("{", classStart + 5);
|
|
302
|
+
if (bodyStart === -1) return -1;
|
|
303
|
+
return findMatchingBrace(code, bodyStart);
|
|
304
|
+
}
|
|
305
|
+
function findArrowFunctionBodyEnd(code, arrowIndex) {
|
|
306
|
+
const bodyStart = findNextNonWhitespaceIndex(code, arrowIndex + 2);
|
|
307
|
+
if (bodyStart === -1 || code[bodyStart] !== "{") return -1;
|
|
308
|
+
return findMatchingBrace(code, bodyStart);
|
|
309
|
+
}
|
|
310
|
+
function findObjectMethodBodyEnd(code, start) {
|
|
311
|
+
let i = start;
|
|
312
|
+
if (matchesKeywordAt(code, i, "async")) {
|
|
313
|
+
const afterAsync = findNextNonWhitespaceIndex(code, i + 5);
|
|
314
|
+
if (afterAsync === -1) return -1;
|
|
315
|
+
if (code[afterAsync] !== "(") i = afterAsync;
|
|
316
|
+
}
|
|
317
|
+
if (code[i] === "*") {
|
|
318
|
+
i = findNextNonWhitespaceIndex(code, i + 1);
|
|
319
|
+
if (i === -1) return -1;
|
|
320
|
+
}
|
|
321
|
+
if (!/[A-Za-z_$]/.test(code[i] ?? "")) return -1;
|
|
322
|
+
const nameStart = i;
|
|
323
|
+
while (/[A-Za-z0-9_$]/.test(code[i] ?? "")) i++;
|
|
324
|
+
const name = code.slice(nameStart, i);
|
|
325
|
+
if (name === "if" || name === "for" || name === "while" || name === "switch" || name === "catch" || name === "function" || name === "return" || name === "const" || name === "let" || name === "var" || name === "new") return -1;
|
|
326
|
+
if (name === "get" || name === "set") {
|
|
327
|
+
const afterAccessor = findNextNonWhitespaceIndex(code, i);
|
|
328
|
+
if (afterAccessor === -1) return -1;
|
|
329
|
+
if (code[afterAccessor] !== "(") {
|
|
330
|
+
i = afterAccessor;
|
|
331
|
+
if (!/[A-Za-z_$]/.test(code[i] ?? "")) return -1;
|
|
332
|
+
while (/[A-Za-z0-9_$]/.test(code[i] ?? "")) i++;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const paramsStart = findNextNonWhitespaceIndex(code, i);
|
|
336
|
+
if (paramsStart === -1 || code[paramsStart] !== "(") return -1;
|
|
337
|
+
const paramsEnd = findMatchingParen(code, paramsStart);
|
|
338
|
+
if (paramsEnd === -1) return -1;
|
|
339
|
+
const bodyStart = findNextNonWhitespaceIndex(code, paramsEnd + 1);
|
|
340
|
+
if (bodyStart === -1 || code[bodyStart] !== "{") return -1;
|
|
341
|
+
return findMatchingBrace(code, bodyStart);
|
|
342
|
+
}
|
|
343
|
+
function findNextNonWhitespaceIndex(code, start) {
|
|
344
|
+
for (let i = start; i < code.length; i++) if (!/\s/.test(code[i])) return i;
|
|
345
|
+
return -1;
|
|
346
|
+
}
|
|
347
|
+
function matchesKeywordAt(code, index, keyword) {
|
|
348
|
+
const before = index === 0 ? "" : code[index - 1];
|
|
349
|
+
const after = code[index + keyword.length] ?? "";
|
|
350
|
+
return code.startsWith(keyword, index) && (before === "" || !/[A-Za-z0-9_$]/.test(before)) && (after === "" || !/[A-Za-z0-9_$]/.test(after));
|
|
351
|
+
}
|
|
352
|
+
function findMatchingBrace(code, start) {
|
|
353
|
+
return findMatchingToken(code, start, "{", "}");
|
|
354
|
+
}
|
|
355
|
+
function findMatchingParen(code, start) {
|
|
356
|
+
return findMatchingToken(code, start, "(", ")");
|
|
357
|
+
}
|
|
358
|
+
function findMatchingToken(code, start, openToken, closeToken) {
|
|
359
|
+
let depth = 0;
|
|
360
|
+
let quote = null;
|
|
361
|
+
let inLineComment = false;
|
|
362
|
+
let inBlockComment = false;
|
|
363
|
+
for (let i = start; i < code.length; i++) {
|
|
364
|
+
const char = code[i];
|
|
365
|
+
const next = code[i + 1];
|
|
366
|
+
if (inLineComment) {
|
|
367
|
+
if (char === "\n") inLineComment = false;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (inBlockComment) {
|
|
371
|
+
if (char === "*" && next === "/") {
|
|
372
|
+
inBlockComment = false;
|
|
373
|
+
i++;
|
|
374
|
+
}
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (quote) {
|
|
378
|
+
if (char === "\\") {
|
|
379
|
+
i++;
|
|
380
|
+
continue;
|
|
381
|
+
}
|
|
382
|
+
if (char === quote) quote = null;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (char === "/" && next === "/") {
|
|
386
|
+
inLineComment = true;
|
|
387
|
+
i++;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
if (char === "/" && next === "*") {
|
|
391
|
+
inBlockComment = true;
|
|
392
|
+
i++;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (char === "\"" || char === "'" || char === "`") {
|
|
396
|
+
quote = char;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (char === openToken) {
|
|
400
|
+
depth++;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (char === closeToken) {
|
|
404
|
+
depth--;
|
|
405
|
+
if (depth === 0) return i;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return -1;
|
|
409
|
+
}
|
|
76
410
|
/**
|
|
77
411
|
* Classifies a Pages Router page file by reading its source and examining
|
|
78
412
|
* which data-fetching exports it contains.
|
package/dist/build/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","names":[],"sources":["../../src/build/report.ts"],"sourcesContent":["/**\n * Build report — prints a Next.js-style route table after `vinext build`.\n *\n * Classifies every discovered route as:\n * ○ Static — confirmed static: force-static or revalidate=Infinity\n * ◐ ISR — statically rendered, revalidated on a timer (revalidate=N)\n * ƒ Dynamic — confirmed dynamic: force-dynamic, revalidate=0, or getServerSideProps\n * ? Unknown — no explicit config; likely dynamic but not confirmed\n * λ API — API route handler\n *\n * Classification uses regex-based static source analysis (no module\n * execution). Vite's parseAst() is NOT used because it doesn't handle\n * TypeScript syntax.\n *\n * Limitation: without running the build, we cannot detect dynamic API usage\n * (headers(), cookies(), connection(), etc.) that implicitly forces a route\n * dynamic. Routes without explicit `export const dynamic` or\n * `export const revalidate` are classified as \"unknown\" rather than \"static\"\n * to avoid false confidence.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Route } from \"../routing/pages-router.js\";\nimport type { AppRoute } from \"../routing/app-router.js\";\nimport type { PrerenderResult } from \"./prerender.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type RouteType = \"static\" | \"isr\" | \"ssr\" | \"unknown\" | \"api\";\n\nexport interface RouteRow {\n pattern: string;\n type: RouteType;\n /** Only set for `isr` routes. */\n revalidate?: number;\n /**\n * True when the route was classified as `static` by speculative prerender\n * (i.e. was `unknown` from static analysis but rendered successfully).\n * Used by `formatBuildReport` to add a note in the legend.\n */\n prerendered?: boolean;\n}\n\n// ─── Regex-based export detection ────────────────────────────────────────────\n\n/**\n * Returns true if the source code contains a named export with the given name.\n * Handles all three common export forms:\n * export function foo() {}\n * export const foo = ...\n * export { foo }\n */\nexport function hasNamedExport(code: string, name: string): boolean {\n // Function / generator / async function declaration\n const fnRe = new RegExp(`(?:^|\\\\n)\\\\s*export\\\\s+(?:async\\\\s+)?function\\\\s+${name}\\\\b`);\n if (fnRe.test(code)) return true;\n\n // Variable declaration (const / let / var)\n const varRe = new RegExp(`(?:^|\\\\n)\\\\s*export\\\\s+(?:const|let|var)\\\\s+${name}\\\\s*[=:]`);\n if (varRe.test(code)) return true;\n\n // Re-export specifier: export { foo } or export { foo as bar }\n const reRe = new RegExp(`export\\\\s*\\\\{[^}]*\\\\b${name}\\\\b[^}]*\\\\}`);\n if (reRe.test(code)) return true;\n\n return false;\n}\n\n/**\n * Extracts the string value of `export const <name> = \"value\"`.\n * Handles optional TypeScript type annotations:\n * export const dynamic: string = \"force-dynamic\"\n * Returns null if the export is absent or not a string literal.\n */\nexport function extractExportConstString(code: string, name: string): string | null {\n const re = new RegExp(\n `^\\\\s*export\\\\s+const\\\\s+${name}\\\\s*(?::[^=]+)?\\\\s*=\\\\s*['\"]([^'\"]+)['\"]`,\n \"m\",\n );\n const m = re.exec(code);\n return m ? m[1] : null;\n}\n\n/**\n * Extracts the numeric value of `export const <name> = <number>`.\n * Supports integers, decimals, negative values, and `Infinity`.\n * Handles optional TypeScript type annotations.\n * Returns null if the export is absent or not a number.\n */\nexport function extractExportConstNumber(code: string, name: string): number | null {\n const re = new RegExp(\n `^\\\\s*export\\\\s+const\\\\s+${name}\\\\s*(?::[^=]+)?\\\\s*=\\\\s*(-?\\\\d+(?:\\\\.\\\\d+)?|Infinity)`,\n \"m\",\n );\n const m = re.exec(code);\n if (!m) return null;\n return m[1] === \"Infinity\" ? Infinity : parseFloat(m[1]);\n}\n\n/**\n * Extracts the `revalidate` value from inside a `getStaticProps` return object.\n * Looks for: revalidate: <number> or revalidate: false or revalidate: Infinity\n *\n * Returns:\n * number — a positive revalidation interval (enables ISR)\n * 0 — treat as SSR (revalidate every request)\n * false — fully static (no revalidation)\n * Infinity — fully static (treated same as false by Next.js)\n * null — no `revalidate` key found (fully static)\n */\nexport function extractGetStaticPropsRevalidate(code: string): number | false | null {\n // TODO: This regex matches `revalidate:` anywhere in the file, not scoped to\n // the getStaticProps return object. A config object or comment elsewhere in\n // the file (e.g. `const defaults = { revalidate: 30 }`) could produce a false\n // positive. Rare in practice, but a proper AST-based approach would be more\n // accurate.\n const re = /\\brevalidate\\s*:\\s*(-?\\d+(?:\\.\\d+)?|Infinity|false)\\b/;\n const m = re.exec(code);\n if (!m) return null;\n if (m[1] === \"false\") return false;\n if (m[1] === \"Infinity\") return Infinity;\n return parseFloat(m[1]);\n}\n\n// ─── Route classification ─────────────────────────────────────────────────────\n\n/**\n * Classifies a Pages Router page file by reading its source and examining\n * which data-fetching exports it contains.\n *\n * API routes (files under pages/api/) are always `api`.\n */\nexport function classifyPagesRoute(filePath: string): {\n type: RouteType;\n revalidate?: number;\n} {\n // API routes are identified by their path\n const normalized = filePath.replace(/\\\\/g, \"/\");\n if (normalized.includes(\"/pages/api/\")) {\n return { type: \"api\" };\n }\n\n let code: string;\n try {\n code = fs.readFileSync(filePath, \"utf8\");\n } catch {\n return { type: \"unknown\" };\n }\n\n if (hasNamedExport(code, \"getServerSideProps\")) {\n return { type: \"ssr\" };\n }\n\n if (hasNamedExport(code, \"getStaticProps\")) {\n const revalidate = extractGetStaticPropsRevalidate(code);\n\n if (revalidate === null || revalidate === false || revalidate === Infinity) {\n return { type: \"static\" };\n }\n if (revalidate === 0) {\n return { type: \"ssr\" };\n }\n // Positive number → ISR\n return { type: \"isr\", revalidate };\n }\n\n return { type: \"static\" };\n}\n\n/**\n * Classifies an App Router route.\n *\n * @param pagePath Absolute path to the page.tsx (null for API-only routes)\n * @param routePath Absolute path to the route.ts handler (null for page routes)\n * @param isDynamic Whether the URL pattern contains dynamic segments\n */\nexport function classifyAppRoute(\n pagePath: string | null,\n routePath: string | null,\n isDynamic: boolean,\n): { type: RouteType; revalidate?: number } {\n // Route handlers with no page component → API\n if (routePath !== null && pagePath === null) {\n return { type: \"api\" };\n }\n\n const filePath = pagePath ?? routePath;\n if (!filePath) return { type: \"unknown\" };\n\n let code: string;\n try {\n code = fs.readFileSync(filePath, \"utf8\");\n } catch {\n return { type: \"unknown\" };\n }\n\n // Check `export const dynamic`\n const dynamicValue = extractExportConstString(code, \"dynamic\");\n if (dynamicValue === \"force-dynamic\") {\n return { type: \"ssr\" };\n }\n if (dynamicValue === \"force-static\" || dynamicValue === \"error\") {\n // \"error\" enforces static rendering — it throws if dynamic APIs are used,\n // so the page is statically rendered (same as force-static for classification).\n return { type: \"static\" };\n }\n\n // Check `export const revalidate`\n const revalidateValue = extractExportConstNumber(code, \"revalidate\");\n if (revalidateValue !== null) {\n if (revalidateValue === Infinity) return { type: \"static\" };\n if (revalidateValue === 0) return { type: \"ssr\" };\n if (revalidateValue > 0) return { type: \"isr\", revalidate: revalidateValue };\n }\n\n // Fall back to isDynamic flag (dynamic URL segments without explicit config)\n if (isDynamic) return { type: \"ssr\" };\n\n // No explicit config and no dynamic URL segments — we can't confirm static\n // without running the build (dynamic API calls like headers() are invisible\n // to static analysis). Report as unknown rather than falsely claiming static.\n return { type: \"unknown\" };\n}\n\n// ─── Row building ─────────────────────────────────────────────────────────────\n\n/**\n * Builds a sorted list of RouteRow objects from the discovered routes.\n * Routes are sorted alphabetically by path, matching filesystem order.\n *\n * When `prerenderResult` is provided, routes that were classified as `unknown`\n * by static analysis but were successfully rendered speculatively are upgraded\n * to `static` (confirmed by execution). The `prerendered` flag is set on those\n * rows so the formatter can add a legend note.\n */\nexport function buildReportRows(options: {\n pageRoutes?: Route[];\n apiRoutes?: Route[];\n appRoutes?: AppRoute[];\n prerenderResult?: PrerenderResult;\n}): RouteRow[] {\n const rows: RouteRow[] = [];\n\n // Build a set of routes that were confirmed rendered by speculative prerender.\n const renderedRoutes = new Set<string>();\n if (options.prerenderResult) {\n for (const r of options.prerenderResult.routes) {\n if (r.status === \"rendered\") renderedRoutes.add(r.route);\n }\n }\n\n for (const route of options.pageRoutes ?? []) {\n const { type, revalidate } = classifyPagesRoute(route.filePath);\n rows.push({ pattern: route.pattern, type, revalidate });\n }\n\n for (const route of options.apiRoutes ?? []) {\n rows.push({ pattern: route.pattern, type: \"api\" });\n }\n\n for (const route of options.appRoutes ?? []) {\n const { type, revalidate } = classifyAppRoute(route.pagePath, route.routePath, route.isDynamic);\n if (type === \"unknown\" && renderedRoutes.has(route.pattern)) {\n // Speculative prerender confirmed this route is static.\n rows.push({ pattern: route.pattern, type: \"static\", prerendered: true });\n } else {\n rows.push({ pattern: route.pattern, type, revalidate });\n }\n }\n\n // Sort purely by path — mirrors filesystem order, matching Next.js output style\n rows.sort((a, b) => a.pattern.localeCompare(b.pattern));\n\n return rows;\n}\n\n// ─── Formatting ───────────────────────────────────────────────────────────────\n\nconst SYMBOLS: Record<RouteType, string> = {\n static: \"○\",\n isr: \"◐\",\n ssr: \"ƒ\",\n unknown: \"?\",\n api: \"λ\",\n};\n\nconst LABELS: Record<RouteType, string> = {\n static: \"Static\",\n isr: \"ISR\",\n ssr: \"Dynamic\",\n unknown: \"Unknown\",\n api: \"API\",\n};\n\n/**\n * Formats a list of RouteRows into a Next.js-style build report string.\n *\n * Example output:\n * Route (pages)\n * ┌ ○ /\n * ├ ◐ /blog/:slug (60s)\n * ├ ƒ /dashboard\n * └ λ /api/posts\n *\n * ○ Static ◐ ISR ƒ Dynamic λ API\n */\nexport function formatBuildReport(rows: RouteRow[], routerLabel = \"app\"): string {\n if (rows.length === 0) return \"\";\n\n const lines: string[] = [];\n lines.push(` Route (${routerLabel})`);\n\n // Determine padding width from the longest pattern\n const maxPatternLen = Math.max(...rows.map((r) => r.pattern.length));\n\n rows.forEach((row, i) => {\n const isLast = i === rows.length - 1;\n const corner = rows.length === 1 ? \"─\" : i === 0 ? \"┌\" : isLast ? \"└\" : \"├\";\n const sym = SYMBOLS[row.type];\n const suffix =\n row.type === \"isr\" && row.revalidate !== undefined ? ` (${row.revalidate}s)` : \"\";\n const padding = \" \".repeat(maxPatternLen - row.pattern.length);\n lines.push(` ${corner} ${sym} ${row.pattern}${padding}${suffix}`);\n });\n\n lines.push(\"\");\n\n // Legend — only include types that appear in this report, sorted alphabetically by label\n const usedTypes = [...new Set(rows.map((r) => r.type))].sort((a, b) =>\n LABELS[a].localeCompare(LABELS[b]),\n );\n lines.push(\" \" + usedTypes.map((t) => `${SYMBOLS[t]} ${LABELS[t]}`).join(\" \"));\n\n // Explanatory note — only shown when unknown routes are present\n if (usedTypes.includes(\"unknown\")) {\n lines.push(\"\");\n lines.push(\" ? Some routes could not be classified. vinext currently uses static analysis\");\n lines.push(\n \" and cannot detect dynamic API usage (headers(), cookies(), etc.) at build time.\",\n );\n lines.push(\" Automatic classification will be improved in a future release.\");\n }\n\n // Speculative-render note — shown when any routes were confirmed static by prerender\n const hasPrerendered = rows.some((r) => r.prerendered);\n if (hasPrerendered) {\n lines.push(\"\");\n lines.push(\n \" ○ Routes marked static were confirmed by speculative prerender (attempted render\",\n );\n lines.push(\" succeeded without dynamic API usage).\");\n }\n\n return lines.join(\"\\n\");\n}\n\n// ─── Directory detection ──────────────────────────────────────────────────────\n\nexport function findDir(root: string, ...candidates: string[]): string | null {\n for (const candidate of candidates) {\n const full = path.join(root, candidate);\n try {\n if (fs.statSync(full).isDirectory()) return full;\n } catch {\n // not found or not a directory — try next candidate\n }\n }\n return null;\n}\n\n// ─── Main entry point ─────────────────────────────────────────────────────────\n\n/**\n * Scans the project at `root`, classifies all routes, and prints the\n * Next.js-style build report to stdout.\n *\n * Called at the end of `vinext build` in cli.ts.\n */\nexport async function printBuildReport(options: {\n root: string;\n pageExtensions?: string[];\n prerenderResult?: PrerenderResult;\n}): Promise<void> {\n const { root } = options;\n\n const appDir = findDir(root, \"app\", \"src/app\");\n const pagesDir = findDir(root, \"pages\", \"src/pages\");\n\n if (!appDir && !pagesDir) return;\n\n if (appDir) {\n // Dynamic import to avoid loading routing code unless needed\n const { appRouter } = await import(\"../routing/app-router.js\");\n const routes = await appRouter(appDir, options.pageExtensions);\n const rows = buildReportRows({ appRoutes: routes, prerenderResult: options.prerenderResult });\n if (rows.length > 0) {\n console.log(\"\\n\" + formatBuildReport(rows, \"app\"));\n }\n }\n\n if (pagesDir) {\n const { pagesRouter, apiRouter } = await import(\"../routing/pages-router.js\");\n const [pageRoutes, apiRoutes] = await Promise.all([\n pagesRouter(pagesDir, options.pageExtensions),\n apiRouter(pagesDir, options.pageExtensions),\n ]);\n const rows = buildReportRows({\n pageRoutes,\n apiRoutes,\n prerenderResult: options.prerenderResult,\n });\n if (rows.length > 0) {\n console.log(\"\\n\" + formatBuildReport(rows, \"pages\"));\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"report.js","names":[],"sources":["../../src/build/report.ts"],"sourcesContent":["/**\n * Build report — prints a Next.js-style route table after `vinext build`.\n *\n * Classifies every discovered route as:\n * ○ Static — confirmed static: force-static or revalidate=Infinity\n * ◐ ISR — statically rendered, revalidated on a timer (revalidate=N)\n * ƒ Dynamic — confirmed dynamic: force-dynamic, revalidate=0, or getServerSideProps\n * ? Unknown — no explicit config; likely dynamic but not confirmed\n * λ API — API route handler\n *\n * Classification uses regex-based static source analysis (no module\n * execution). Vite's parseAst() is NOT used because it doesn't handle\n * TypeScript syntax.\n *\n * Limitation: without running the build, we cannot detect dynamic API usage\n * (headers(), cookies(), connection(), etc.) that implicitly forces a route\n * dynamic. Routes without explicit `export const dynamic` or\n * `export const revalidate` are classified as \"unknown\" rather than \"static\"\n * to avoid false confidence.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Route } from \"../routing/pages-router.js\";\nimport type { AppRoute } from \"../routing/app-router.js\";\nimport type { PrerenderResult } from \"./prerender.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type RouteType = \"static\" | \"isr\" | \"ssr\" | \"unknown\" | \"api\";\n\nexport interface RouteRow {\n pattern: string;\n type: RouteType;\n /** Only set for `isr` routes. */\n revalidate?: number;\n /**\n * True when the route was classified as `static` by speculative prerender\n * (i.e. was `unknown` from static analysis but rendered successfully).\n * Used by `formatBuildReport` to add a note in the legend.\n */\n prerendered?: boolean;\n}\n\n// ─── Regex-based export detection ────────────────────────────────────────────\n\n/**\n * Returns true if the source code contains a named export with the given name.\n * Handles all three common export forms:\n * export function foo() {}\n * export const foo = ...\n * export { foo }\n */\nexport function hasNamedExport(code: string, name: string): boolean {\n // Function / generator / async function declaration\n const fnRe = new RegExp(`(?:^|\\\\n)\\\\s*export\\\\s+(?:async\\\\s+)?function\\\\s+${name}\\\\b`);\n if (fnRe.test(code)) return true;\n\n // Variable declaration (const / let / var)\n const varRe = new RegExp(`(?:^|\\\\n)\\\\s*export\\\\s+(?:const|let|var)\\\\s+${name}\\\\s*[=:]`);\n if (varRe.test(code)) return true;\n\n // Re-export specifier: export { foo } or export { foo as bar }\n const reRe = new RegExp(`export\\\\s*\\\\{[^}]*\\\\b${name}\\\\b[^}]*\\\\}`);\n if (reRe.test(code)) return true;\n\n return false;\n}\n\n/**\n * Extracts the string value of `export const <name> = \"value\"`.\n * Handles optional TypeScript type annotations:\n * export const dynamic: string = \"force-dynamic\"\n * Returns null if the export is absent or not a string literal.\n */\nexport function extractExportConstString(code: string, name: string): string | null {\n const re = new RegExp(\n `^\\\\s*export\\\\s+const\\\\s+${name}\\\\s*(?::[^=]+)?\\\\s*=\\\\s*['\"]([^'\"]+)['\"]`,\n \"m\",\n );\n const m = re.exec(code);\n return m ? m[1] : null;\n}\n\n/**\n * Extracts the numeric value of `export const <name> = <number>`.\n * Supports integers, decimals, negative values, and `Infinity`.\n * Handles optional TypeScript type annotations.\n * Returns null if the export is absent or not a number.\n */\nexport function extractExportConstNumber(code: string, name: string): number | null {\n const re = new RegExp(\n `^\\\\s*export\\\\s+const\\\\s+${name}\\\\s*(?::[^=]+)?\\\\s*=\\\\s*(-?\\\\d+(?:\\\\.\\\\d+)?|Infinity)`,\n \"m\",\n );\n const m = re.exec(code);\n if (!m) return null;\n return m[1] === \"Infinity\" ? Infinity : parseFloat(m[1]);\n}\n\n/**\n * Extracts the `revalidate` value from inside a `getStaticProps` return object.\n * Looks for: revalidate: <number> or revalidate: false or revalidate: Infinity\n *\n * Returns:\n * number — a positive revalidation interval (enables ISR)\n * 0 — treat as SSR (revalidate every request)\n * false — fully static (no revalidation)\n * Infinity — fully static (treated same as false by Next.js)\n * null — no `revalidate` key found (fully static)\n */\nexport function extractGetStaticPropsRevalidate(code: string): number | false | null {\n const returnObjects = extractGetStaticPropsReturnObjects(code);\n\n if (returnObjects) {\n for (const searchSpace of returnObjects) {\n const revalidate = extractTopLevelRevalidateValue(searchSpace);\n if (revalidate !== null) return revalidate;\n }\n return null;\n }\n\n const m = /\\brevalidate\\s*:\\s*(-?\\d+(?:\\.\\d+)?|Infinity|false)\\b/.exec(code);\n if (!m) return null;\n if (m[1] === \"false\") return false;\n if (m[1] === \"Infinity\") return Infinity;\n return parseFloat(m[1]);\n}\n\nfunction extractTopLevelRevalidateValue(code: string): number | false | null {\n let braceDepth = 0;\n let parenDepth = 0;\n let bracketDepth = 0;\n let quote: '\"' | \"'\" | \"`\" | null = null;\n let inLineComment = false;\n let inBlockComment = false;\n\n for (let i = 0; i < code.length; i++) {\n const char = code[i];\n const next = code[i + 1];\n\n if (inLineComment) {\n if (char === \"\\n\") inLineComment = false;\n continue;\n }\n\n if (inBlockComment) {\n if (char === \"*\" && next === \"/\") {\n inBlockComment = false;\n i++;\n }\n continue;\n }\n\n if (quote) {\n if (char === \"\\\\\") {\n i++;\n continue;\n }\n if (char === quote) quote = null;\n continue;\n }\n\n if (char === \"/\" && next === \"/\") {\n inLineComment = true;\n i++;\n continue;\n }\n\n if (char === \"/\" && next === \"*\") {\n inBlockComment = true;\n i++;\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n quote = char;\n continue;\n }\n\n if (char === \"{\") {\n braceDepth++;\n continue;\n }\n\n if (char === \"}\") {\n braceDepth--;\n continue;\n }\n\n if (char === \"(\") {\n parenDepth++;\n continue;\n }\n\n if (char === \")\") {\n parenDepth--;\n continue;\n }\n\n if (char === \"[\") {\n bracketDepth++;\n continue;\n }\n\n if (char === \"]\") {\n bracketDepth--;\n continue;\n }\n\n if (\n braceDepth === 1 &&\n parenDepth === 0 &&\n bracketDepth === 0 &&\n matchesKeywordAt(code, i, \"revalidate\")\n ) {\n const colonIndex = findNextNonWhitespaceIndex(code, i + \"revalidate\".length);\n if (colonIndex === -1 || code[colonIndex] !== \":\") continue;\n\n const valueStart = findNextNonWhitespaceIndex(code, colonIndex + 1);\n if (valueStart === -1) return null;\n\n const valueMatch = /^(-?\\d+(?:\\.\\d+)?|Infinity|false)\\b/.exec(code.slice(valueStart));\n if (!valueMatch) return null;\n if (valueMatch[1] === \"false\") return false;\n if (valueMatch[1] === \"Infinity\") return Infinity;\n return parseFloat(valueMatch[1]);\n }\n }\n\n return null;\n}\n\nfunction extractGetStaticPropsReturnObjects(code: string): string[] | null {\n const declarationMatch =\n /(?:^|\\n)\\s*(?:export\\s+)?(?:async\\s+)?function\\s+getStaticProps\\b|(?:^|\\n)\\s*(?:export\\s+)?(?:const|let|var)\\s+getStaticProps\\b/.exec(\n code,\n );\n if (!declarationMatch) {\n // A file can re-export getStaticProps from another module without defining\n // it locally. In that case we can't safely infer revalidate from this file,\n // so skip the whole-file fallback to avoid unrelated false positives.\n if (/(?:^|\\n)\\s*export\\s*\\{[^}]*\\bgetStaticProps\\b[^}]*\\}\\s*from\\b/.test(code)) {\n return [];\n }\n return null;\n }\n\n const declaration = extractGetStaticPropsDeclaration(code, declarationMatch);\n if (declaration === null) return [];\n\n const returnObjects = declaration.trimStart().startsWith(\"{\")\n ? collectReturnObjectsFromFunctionBody(declaration)\n : [];\n\n if (returnObjects.length > 0) return returnObjects;\n\n const arrowMatch = declaration.search(/=>\\s*\\(\\s*\\{/);\n // getStaticProps was found but contains no return objects — return empty\n // (non-null signals the caller to skip the whole-file fallback).\n if (arrowMatch === -1) return [];\n\n const braceStart = declaration.indexOf(\"{\", arrowMatch);\n if (braceStart === -1) return [];\n\n const braceEnd = findMatchingBrace(declaration, braceStart);\n if (braceEnd === -1) return [];\n\n return [declaration.slice(braceStart, braceEnd + 1)];\n}\n\nfunction extractGetStaticPropsDeclaration(\n code: string,\n declarationMatch: RegExpExecArray,\n): string | null {\n const declarationStart = declarationMatch.index;\n const declarationText = declarationMatch[0];\n const declarationTail = code.slice(declarationStart);\n\n if (declarationText.includes(\"function getStaticProps\")) {\n return extractFunctionBody(code, declarationStart + declarationText.length);\n }\n\n const functionExpressionMatch = /(?:async\\s+)?function\\b/.exec(declarationTail);\n if (functionExpressionMatch) {\n return extractFunctionBody(declarationTail, functionExpressionMatch.index);\n }\n\n const blockBodyMatch = /=>\\s*\\{/.exec(declarationTail);\n if (blockBodyMatch) {\n const braceStart = declarationTail.indexOf(\"{\", blockBodyMatch.index);\n if (braceStart === -1) return null;\n\n const braceEnd = findMatchingBrace(declarationTail, braceStart);\n if (braceEnd === -1) return null;\n\n return declarationTail.slice(braceStart, braceEnd + 1);\n }\n\n const implicitArrowMatch = declarationTail.search(/=>\\s*\\(\\s*\\{/);\n if (implicitArrowMatch === -1) return null;\n\n const implicitBraceStart = declarationTail.indexOf(\"{\", implicitArrowMatch);\n if (implicitBraceStart === -1) return null;\n\n const implicitBraceEnd = findMatchingBrace(declarationTail, implicitBraceStart);\n if (implicitBraceEnd === -1) return null;\n\n return declarationTail.slice(0, implicitBraceEnd + 1);\n}\n\nfunction extractFunctionBody(code: string, functionStart: number): string | null {\n const bodyEnd = findFunctionBodyEnd(code, functionStart);\n if (bodyEnd === -1) return null;\n\n const paramsStart = code.indexOf(\"(\", functionStart);\n if (paramsStart === -1) return null;\n\n const paramsEnd = findMatchingParen(code, paramsStart);\n if (paramsEnd === -1) return null;\n\n const bodyStart = code.indexOf(\"{\", paramsEnd + 1);\n if (bodyStart === -1) return null;\n\n return code.slice(bodyStart, bodyEnd + 1);\n}\n\nfunction collectReturnObjectsFromFunctionBody(code: string): string[] {\n const returnObjects: string[] = [];\n let quote: '\"' | \"'\" | \"`\" | null = null;\n let inLineComment = false;\n let inBlockComment = false;\n\n for (let i = 0; i < code.length; i++) {\n const char = code[i];\n const next = code[i + 1];\n\n if (inLineComment) {\n if (char === \"\\n\") inLineComment = false;\n continue;\n }\n\n if (inBlockComment) {\n if (char === \"*\" && next === \"/\") {\n inBlockComment = false;\n i++;\n }\n continue;\n }\n\n if (quote) {\n if (char === \"\\\\\") {\n i++;\n continue;\n }\n if (char === quote) quote = null;\n continue;\n }\n\n if (char === \"/\" && next === \"/\") {\n inLineComment = true;\n i++;\n continue;\n }\n\n if (char === \"/\" && next === \"*\") {\n inBlockComment = true;\n i++;\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n quote = char;\n continue;\n }\n\n if (matchesKeywordAt(code, i, \"function\")) {\n const nestedBodyEnd = findFunctionBodyEnd(code, i);\n if (nestedBodyEnd !== -1) {\n i = nestedBodyEnd;\n }\n continue;\n }\n\n if (matchesKeywordAt(code, i, \"class\")) {\n const classBodyEnd = findClassBodyEnd(code, i);\n if (classBodyEnd !== -1) {\n i = classBodyEnd;\n }\n continue;\n }\n\n if (char === \"=\" && next === \">\") {\n const nestedBodyEnd = findArrowFunctionBodyEnd(code, i);\n if (nestedBodyEnd !== -1) {\n i = nestedBodyEnd;\n }\n continue;\n }\n\n if (\n (char >= \"A\" && char <= \"Z\") ||\n (char >= \"a\" && char <= \"z\") ||\n char === \"_\" ||\n char === \"$\" ||\n char === \"*\"\n ) {\n const methodBodyEnd = findObjectMethodBodyEnd(code, i);\n if (methodBodyEnd !== -1) {\n i = methodBodyEnd;\n continue;\n }\n }\n\n if (matchesKeywordAt(code, i, \"return\")) {\n const braceStart = findNextNonWhitespaceIndex(code, i + \"return\".length);\n if (braceStart === -1 || code[braceStart] !== \"{\") continue;\n\n const braceEnd = findMatchingBrace(code, braceStart);\n if (braceEnd === -1) continue;\n\n returnObjects.push(code.slice(braceStart, braceEnd + 1));\n i = braceEnd;\n }\n }\n\n return returnObjects;\n}\n\nfunction findFunctionBodyEnd(code: string, functionStart: number): number {\n const paramsStart = code.indexOf(\"(\", functionStart);\n if (paramsStart === -1) return -1;\n\n const paramsEnd = findMatchingParen(code, paramsStart);\n if (paramsEnd === -1) return -1;\n\n const bodyStart = code.indexOf(\"{\", paramsEnd + 1);\n if (bodyStart === -1) return -1;\n\n return findMatchingBrace(code, bodyStart);\n}\n\nfunction findClassBodyEnd(code: string, classStart: number): number {\n const bodyStart = code.indexOf(\"{\", classStart + \"class\".length);\n if (bodyStart === -1) return -1;\n\n return findMatchingBrace(code, bodyStart);\n}\n\nfunction findArrowFunctionBodyEnd(code: string, arrowIndex: number): number {\n const bodyStart = findNextNonWhitespaceIndex(code, arrowIndex + 2);\n if (bodyStart === -1 || code[bodyStart] !== \"{\") return -1;\n\n return findMatchingBrace(code, bodyStart);\n}\n\nfunction findObjectMethodBodyEnd(code: string, start: number): number {\n let i = start;\n\n if (matchesKeywordAt(code, i, \"async\")) {\n const afterAsync = findNextNonWhitespaceIndex(code, i + \"async\".length);\n if (afterAsync === -1) return -1;\n if (code[afterAsync] !== \"(\") {\n i = afterAsync;\n }\n }\n\n if (code[i] === \"*\") {\n i = findNextNonWhitespaceIndex(code, i + 1);\n if (i === -1) return -1;\n }\n\n if (!/[A-Za-z_$]/.test(code[i] ?? \"\")) return -1;\n\n const nameStart = i;\n while (/[A-Za-z0-9_$]/.test(code[i] ?? \"\")) i++;\n const name = code.slice(nameStart, i);\n\n if (\n name === \"if\" ||\n name === \"for\" ||\n name === \"while\" ||\n name === \"switch\" ||\n name === \"catch\" ||\n name === \"function\" ||\n name === \"return\" ||\n name === \"const\" ||\n name === \"let\" ||\n name === \"var\" ||\n name === \"new\"\n ) {\n return -1;\n }\n\n if (name === \"get\" || name === \"set\") {\n const afterAccessor = findNextNonWhitespaceIndex(code, i);\n if (afterAccessor === -1) return -1;\n if (code[afterAccessor] !== \"(\") {\n i = afterAccessor;\n if (!/[A-Za-z_$]/.test(code[i] ?? \"\")) return -1;\n while (/[A-Za-z0-9_$]/.test(code[i] ?? \"\")) i++;\n }\n }\n\n const paramsStart = findNextNonWhitespaceIndex(code, i);\n if (paramsStart === -1 || code[paramsStart] !== \"(\") return -1;\n\n const paramsEnd = findMatchingParen(code, paramsStart);\n if (paramsEnd === -1) return -1;\n\n const bodyStart = findNextNonWhitespaceIndex(code, paramsEnd + 1);\n if (bodyStart === -1 || code[bodyStart] !== \"{\") return -1;\n\n return findMatchingBrace(code, bodyStart);\n}\n\nfunction findNextNonWhitespaceIndex(code: string, start: number): number {\n for (let i = start; i < code.length; i++) {\n if (!/\\s/.test(code[i])) return i;\n }\n return -1;\n}\n\nfunction matchesKeywordAt(code: string, index: number, keyword: string): boolean {\n const before = index === 0 ? \"\" : code[index - 1];\n const after = code[index + keyword.length] ?? \"\";\n return (\n code.startsWith(keyword, index) &&\n (before === \"\" || !/[A-Za-z0-9_$]/.test(before)) &&\n (after === \"\" || !/[A-Za-z0-9_$]/.test(after))\n );\n}\n\nfunction findMatchingBrace(code: string, start: number): number {\n return findMatchingToken(code, start, \"{\", \"}\");\n}\n\nfunction findMatchingParen(code: string, start: number): number {\n return findMatchingToken(code, start, \"(\", \")\");\n}\n\nfunction findMatchingToken(\n code: string,\n start: number,\n openToken: string,\n closeToken: string,\n): number {\n let depth = 0;\n let quote: '\"' | \"'\" | \"`\" | null = null;\n let inLineComment = false;\n let inBlockComment = false;\n\n for (let i = start; i < code.length; i++) {\n const char = code[i];\n const next = code[i + 1];\n\n if (inLineComment) {\n if (char === \"\\n\") inLineComment = false;\n continue;\n }\n\n if (inBlockComment) {\n if (char === \"*\" && next === \"/\") {\n inBlockComment = false;\n i++;\n }\n continue;\n }\n\n if (quote) {\n if (char === \"\\\\\") {\n i++;\n continue;\n }\n if (char === quote) quote = null;\n continue;\n }\n\n if (char === \"/\" && next === \"/\") {\n inLineComment = true;\n i++;\n continue;\n }\n\n if (char === \"/\" && next === \"*\") {\n inBlockComment = true;\n i++;\n continue;\n }\n\n if (char === '\"' || char === \"'\" || char === \"`\") {\n quote = char;\n continue;\n }\n\n if (char === openToken) {\n depth++;\n continue;\n }\n\n if (char === closeToken) {\n depth--;\n if (depth === 0) return i;\n }\n }\n\n return -1;\n}\n\n// ─── Route classification ─────────────────────────────────────────────────────\n\n/**\n * Classifies a Pages Router page file by reading its source and examining\n * which data-fetching exports it contains.\n *\n * API routes (files under pages/api/) are always `api`.\n */\nexport function classifyPagesRoute(filePath: string): {\n type: RouteType;\n revalidate?: number;\n} {\n // API routes are identified by their path\n const normalized = filePath.replace(/\\\\/g, \"/\");\n if (normalized.includes(\"/pages/api/\")) {\n return { type: \"api\" };\n }\n\n let code: string;\n try {\n code = fs.readFileSync(filePath, \"utf8\");\n } catch {\n return { type: \"unknown\" };\n }\n\n if (hasNamedExport(code, \"getServerSideProps\")) {\n return { type: \"ssr\" };\n }\n\n if (hasNamedExport(code, \"getStaticProps\")) {\n const revalidate = extractGetStaticPropsRevalidate(code);\n\n if (revalidate === null || revalidate === false || revalidate === Infinity) {\n return { type: \"static\" };\n }\n if (revalidate === 0) {\n return { type: \"ssr\" };\n }\n // Positive number → ISR\n return { type: \"isr\", revalidate };\n }\n\n return { type: \"static\" };\n}\n\n/**\n * Classifies an App Router route.\n *\n * @param pagePath Absolute path to the page.tsx (null for API-only routes)\n * @param routePath Absolute path to the route.ts handler (null for page routes)\n * @param isDynamic Whether the URL pattern contains dynamic segments\n */\nexport function classifyAppRoute(\n pagePath: string | null,\n routePath: string | null,\n isDynamic: boolean,\n): { type: RouteType; revalidate?: number } {\n // Route handlers with no page component → API\n if (routePath !== null && pagePath === null) {\n return { type: \"api\" };\n }\n\n const filePath = pagePath ?? routePath;\n if (!filePath) return { type: \"unknown\" };\n\n let code: string;\n try {\n code = fs.readFileSync(filePath, \"utf8\");\n } catch {\n return { type: \"unknown\" };\n }\n\n // Check `export const dynamic`\n const dynamicValue = extractExportConstString(code, \"dynamic\");\n if (dynamicValue === \"force-dynamic\") {\n return { type: \"ssr\" };\n }\n if (dynamicValue === \"force-static\" || dynamicValue === \"error\") {\n // \"error\" enforces static rendering — it throws if dynamic APIs are used,\n // so the page is statically rendered (same as force-static for classification).\n return { type: \"static\" };\n }\n\n // Check `export const revalidate`\n const revalidateValue = extractExportConstNumber(code, \"revalidate\");\n if (revalidateValue !== null) {\n if (revalidateValue === Infinity) return { type: \"static\" };\n if (revalidateValue === 0) return { type: \"ssr\" };\n if (revalidateValue > 0) return { type: \"isr\", revalidate: revalidateValue };\n }\n\n // Fall back to isDynamic flag (dynamic URL segments without explicit config)\n if (isDynamic) return { type: \"ssr\" };\n\n // No explicit config and no dynamic URL segments — we can't confirm static\n // without running the build (dynamic API calls like headers() are invisible\n // to static analysis). Report as unknown rather than falsely claiming static.\n return { type: \"unknown\" };\n}\n\n// ─── Row building ─────────────────────────────────────────────────────────────\n\n/**\n * Builds a sorted list of RouteRow objects from the discovered routes.\n * Routes are sorted alphabetically by path, matching filesystem order.\n *\n * When `prerenderResult` is provided, routes that were classified as `unknown`\n * by static analysis but were successfully rendered speculatively are upgraded\n * to `static` (confirmed by execution). The `prerendered` flag is set on those\n * rows so the formatter can add a legend note.\n */\nexport function buildReportRows(options: {\n pageRoutes?: Route[];\n apiRoutes?: Route[];\n appRoutes?: AppRoute[];\n prerenderResult?: PrerenderResult;\n}): RouteRow[] {\n const rows: RouteRow[] = [];\n\n // Build a set of routes that were confirmed rendered by speculative prerender.\n const renderedRoutes = new Set<string>();\n if (options.prerenderResult) {\n for (const r of options.prerenderResult.routes) {\n if (r.status === \"rendered\") renderedRoutes.add(r.route);\n }\n }\n\n for (const route of options.pageRoutes ?? []) {\n const { type, revalidate } = classifyPagesRoute(route.filePath);\n rows.push({ pattern: route.pattern, type, revalidate });\n }\n\n for (const route of options.apiRoutes ?? []) {\n rows.push({ pattern: route.pattern, type: \"api\" });\n }\n\n for (const route of options.appRoutes ?? []) {\n const { type, revalidate } = classifyAppRoute(route.pagePath, route.routePath, route.isDynamic);\n if (type === \"unknown\" && renderedRoutes.has(route.pattern)) {\n // Speculative prerender confirmed this route is static.\n rows.push({ pattern: route.pattern, type: \"static\", prerendered: true });\n } else {\n rows.push({ pattern: route.pattern, type, revalidate });\n }\n }\n\n // Sort purely by path — mirrors filesystem order, matching Next.js output style\n rows.sort((a, b) => a.pattern.localeCompare(b.pattern));\n\n return rows;\n}\n\n// ─── Formatting ───────────────────────────────────────────────────────────────\n\nconst SYMBOLS: Record<RouteType, string> = {\n static: \"○\",\n isr: \"◐\",\n ssr: \"ƒ\",\n unknown: \"?\",\n api: \"λ\",\n};\n\nconst LABELS: Record<RouteType, string> = {\n static: \"Static\",\n isr: \"ISR\",\n ssr: \"Dynamic\",\n unknown: \"Unknown\",\n api: \"API\",\n};\n\n/**\n * Formats a list of RouteRows into a Next.js-style build report string.\n *\n * Example output:\n * Route (pages)\n * ┌ ○ /\n * ├ ◐ /blog/:slug (60s)\n * ├ ƒ /dashboard\n * └ λ /api/posts\n *\n * ○ Static ◐ ISR ƒ Dynamic λ API\n */\nexport function formatBuildReport(rows: RouteRow[], routerLabel = \"app\"): string {\n if (rows.length === 0) return \"\";\n\n const lines: string[] = [];\n lines.push(` Route (${routerLabel})`);\n\n // Determine padding width from the longest pattern\n const maxPatternLen = Math.max(...rows.map((r) => r.pattern.length));\n\n rows.forEach((row, i) => {\n const isLast = i === rows.length - 1;\n const corner = rows.length === 1 ? \"─\" : i === 0 ? \"┌\" : isLast ? \"└\" : \"├\";\n const sym = SYMBOLS[row.type];\n const suffix =\n row.type === \"isr\" && row.revalidate !== undefined ? ` (${row.revalidate}s)` : \"\";\n const padding = \" \".repeat(maxPatternLen - row.pattern.length);\n lines.push(` ${corner} ${sym} ${row.pattern}${padding}${suffix}`);\n });\n\n lines.push(\"\");\n\n // Legend — only include types that appear in this report, sorted alphabetically by label\n const usedTypes = [...new Set(rows.map((r) => r.type))].sort((a, b) =>\n LABELS[a].localeCompare(LABELS[b]),\n );\n lines.push(\" \" + usedTypes.map((t) => `${SYMBOLS[t]} ${LABELS[t]}`).join(\" \"));\n\n // Explanatory note — only shown when unknown routes are present\n if (usedTypes.includes(\"unknown\")) {\n lines.push(\"\");\n lines.push(\" ? Some routes could not be classified. vinext currently uses static analysis\");\n lines.push(\n \" and cannot detect dynamic API usage (headers(), cookies(), etc.) at build time.\",\n );\n lines.push(\" Automatic classification will be improved in a future release.\");\n }\n\n // Speculative-render note — shown when any routes were confirmed static by prerender\n const hasPrerendered = rows.some((r) => r.prerendered);\n if (hasPrerendered) {\n lines.push(\"\");\n lines.push(\n \" ○ Routes marked static were confirmed by speculative prerender (attempted render\",\n );\n lines.push(\" succeeded without dynamic API usage).\");\n }\n\n return lines.join(\"\\n\");\n}\n\n// ─── Directory detection ──────────────────────────────────────────────────────\n\nexport function findDir(root: string, ...candidates: string[]): string | null {\n for (const candidate of candidates) {\n const full = path.join(root, candidate);\n try {\n if (fs.statSync(full).isDirectory()) return full;\n } catch {\n // not found or not a directory — try next candidate\n }\n }\n return null;\n}\n\n// ─── Main entry point ─────────────────────────────────────────────────────────\n\n/**\n * Scans the project at `root`, classifies all routes, and prints the\n * Next.js-style build report to stdout.\n *\n * Called at the end of `vinext build` in cli.ts.\n */\nexport async function printBuildReport(options: {\n root: string;\n pageExtensions: string[];\n prerenderResult?: PrerenderResult;\n}): Promise<void> {\n const { root } = options;\n\n const appDir = findDir(root, \"app\", \"src/app\");\n const pagesDir = findDir(root, \"pages\", \"src/pages\");\n\n if (!appDir && !pagesDir) return;\n\n if (appDir) {\n // Dynamic import to avoid loading routing code unless needed\n const { appRouter } = await import(\"../routing/app-router.js\");\n const routes = await appRouter(appDir, options.pageExtensions);\n const rows = buildReportRows({ appRoutes: routes, prerenderResult: options.prerenderResult });\n if (rows.length > 0) {\n console.log(\"\\n\" + formatBuildReport(rows, \"app\"));\n }\n }\n\n if (pagesDir) {\n const { pagesRouter, apiRouter } = await import(\"../routing/pages-router.js\");\n const [pageRoutes, apiRoutes] = await Promise.all([\n pagesRouter(pagesDir, options.pageExtensions),\n apiRouter(pagesDir, options.pageExtensions),\n ]);\n const rows = buildReportRows({\n pageRoutes,\n apiRoutes,\n prerenderResult: options.prerenderResult,\n });\n if (rows.length > 0) {\n console.log(\"\\n\" + formatBuildReport(rows, \"pages\"));\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -105,7 +105,7 @@ async function runPrerender(options) {
|
|
|
105
105
|
sharedPrerenderSecret = readPrerenderSecret(serverDir);
|
|
106
106
|
}
|
|
107
107
|
if (appDir) {
|
|
108
|
-
const routes = await appRouter(appDir);
|
|
108
|
+
const routes = await appRouter(appDir, config.pageExtensions);
|
|
109
109
|
let appTotal = 0;
|
|
110
110
|
const result = await prerenderApp({
|
|
111
111
|
mode,
|
|
@@ -127,7 +127,7 @@ async function runPrerender(options) {
|
|
|
127
127
|
allRoutes.push(...result.routes);
|
|
128
128
|
}
|
|
129
129
|
if (pagesDir) {
|
|
130
|
-
const [pageRoutes, apiRoutes] = await Promise.all([pagesRouter(pagesDir), apiRouter(pagesDir)]);
|
|
130
|
+
const [pageRoutes, apiRoutes] = await Promise.all([pagesRouter(pagesDir, config.pageExtensions), apiRouter(pagesDir, config.pageExtensions)]);
|
|
131
131
|
let pagesTotal = 0;
|
|
132
132
|
const result = await prerenderPages({
|
|
133
133
|
mode,
|