@vistagenic/vista 0.2.12 → 0.2.13
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/bin/vista.js +171 -35
- package/dist/bin/build-rsc-flashpack.d.ts +4 -0
- package/dist/bin/build-rsc-flashpack.js +29 -0
- package/dist/bin/build-rsc.js +47 -16
- package/dist/bin/build.js +36 -13
- package/dist/bin/devtools-indicator-snippet.js +30 -0
- package/dist/bin/file-scanner.d.ts +1 -1
- package/dist/bin/file-scanner.js +8 -0
- package/dist/bin/flashpack-runner.d.ts +1 -0
- package/dist/bin/flashpack-runner.js +61 -0
- package/dist/bin/server-component-plugin.d.ts +6 -4
- package/dist/bin/server-component-plugin.js +22 -69
- package/dist/bin/webpack.config.d.ts +3 -0
- package/dist/bin/webpack.config.js +12 -3
- package/dist/build/manifest.d.ts +17 -3
- package/dist/build/manifest.js +99 -23
- package/dist/build/rsc/compiler.d.ts +2 -0
- package/dist/build/rsc/compiler.js +25 -5
- package/dist/build/rsc/react-client-reference-manifest.d.ts +22 -0
- package/dist/build/rsc/react-client-reference-manifest.js +219 -0
- package/dist/build/rsc/server-manifest.d.ts +23 -2
- package/dist/build/rsc/server-manifest.js +162 -24
- package/dist/build/standalone.d.ts +31 -0
- package/dist/build/standalone.js +334 -0
- package/dist/client/rsc-router.d.ts +31 -0
- package/dist/client/rsc-router.js +89 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.js +106 -5
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +3 -1
- package/dist/flashpack/command.d.ts +8 -0
- package/dist/flashpack/command.js +134 -0
- package/dist/flashpack/runtime.d.ts +39 -0
- package/dist/flashpack/runtime.js +249 -0
- package/dist/server/app-router-runtime.d.ts +26 -0
- package/dist/server/app-router-runtime.js +321 -0
- package/dist/server/artifact-validator.js +21 -1
- package/dist/server/cache.d.ts +10 -0
- package/dist/server/cache.js +270 -0
- package/dist/server/client-boundary.js +20 -2
- package/dist/server/engine.js +236 -159
- package/dist/server/fetch-policy.d.ts +2 -0
- package/dist/server/fetch-policy.js +123 -0
- package/dist/server/index.d.ts +7 -0
- package/dist/server/index.js +131 -22
- package/dist/server/module-boundary-validator.d.ts +15 -0
- package/dist/server/module-boundary-validator.js +262 -0
- package/dist/server/module-compile-hook.d.ts +7 -0
- package/dist/server/module-compile-hook.js +662 -0
- package/dist/server/ppr.d.ts +18 -0
- package/dist/server/ppr.js +59 -0
- package/dist/server/request-context.d.ts +31 -0
- package/dist/server/request-context.js +95 -0
- package/dist/server/rsc-engine-flashpack.d.ts +4 -0
- package/dist/server/rsc-engine-flashpack.js +27 -0
- package/dist/server/rsc-engine.d.ts +2 -0
- package/dist/server/rsc-engine.js +589 -317
- package/dist/server/rsc-upstream.js +539 -233
- package/dist/server/runtime-actions.d.ts +5 -0
- package/dist/server/runtime-actions.js +80 -0
- package/dist/server/runtime-artifacts.d.ts +11 -0
- package/dist/server/runtime-artifacts.js +35 -0
- package/dist/server/segment-config.d.ts +37 -0
- package/dist/server/segment-config.js +205 -0
- package/dist/server/spawn-permissions.d.ts +2 -0
- package/dist/server/spawn-permissions.js +21 -0
- package/dist/server/static-cache.d.ts +15 -1
- package/dist/server/static-cache.js +83 -3
- package/dist/server/static-generator.js +254 -100
- package/dist/server/structure-validator.d.ts +1 -1
- package/dist/server/structure-validator.js +26 -5
- package/dist/server/structure-watch.js +1 -1
- package/dist/server/typed-api-runtime.d.ts +1 -0
- package/dist/server/typed-api-runtime.js +145 -25
- package/dist/server/vista-import-map.d.ts +1 -0
- package/dist/server/vista-import-map.js +123 -0
- package/package.json +13 -1
|
@@ -19,10 +19,40 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
19
19
|
const child_process_1 = require("child_process");
|
|
20
20
|
const constants_1 = require("../constants");
|
|
21
21
|
const static_cache_1 = require("./static-cache");
|
|
22
|
+
const module_compile_hook_1 = require("./module-compile-hook");
|
|
23
|
+
const request_context_1 = require("./request-context");
|
|
24
|
+
const app_router_runtime_1 = require("./app-router-runtime");
|
|
25
|
+
const fetch_policy_1 = require("./fetch-policy");
|
|
26
|
+
const spawn_permissions_1 = require("./spawn-permissions");
|
|
27
|
+
const config_1 = require("../config");
|
|
28
|
+
const ppr_1 = require("./ppr");
|
|
29
|
+
const vista_import_map_1 = require("./vista-import-map");
|
|
22
30
|
const CjsModule = require('module');
|
|
23
31
|
let staticRuntimeReady = false;
|
|
24
32
|
let reactResolutionInstalled = false;
|
|
25
33
|
let originalResolveFilename = null;
|
|
34
|
+
function resolveFromWorkspace(specifier, cwd) {
|
|
35
|
+
const searchRoots = [
|
|
36
|
+
cwd,
|
|
37
|
+
path_1.default.resolve(cwd, '..'),
|
|
38
|
+
path_1.default.resolve(cwd, '..', '..'),
|
|
39
|
+
path_1.default.resolve(cwd, '..', '..', 'rsc'),
|
|
40
|
+
path_1.default.resolve(cwd, '..', '..', '..'),
|
|
41
|
+
path_1.default.resolve(cwd, '..', '..', '..', 'rsc'),
|
|
42
|
+
];
|
|
43
|
+
for (const root of searchRoots) {
|
|
44
|
+
try {
|
|
45
|
+
return require.resolve(specifier, { paths: [root] });
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// continue
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return require.resolve(specifier);
|
|
52
|
+
}
|
|
53
|
+
function resolveVistaInternalRequest(request) {
|
|
54
|
+
return (0, vista_import_map_1.resolveVistaSourceRequest)(request, path_1.default.resolve(__dirname, '..'));
|
|
55
|
+
}
|
|
26
56
|
function installSingleReactResolution(cwd) {
|
|
27
57
|
if (reactResolutionInstalled)
|
|
28
58
|
return;
|
|
@@ -43,6 +73,9 @@ function installSingleReactResolution(cwd) {
|
|
|
43
73
|
}
|
|
44
74
|
originalResolveFilename = CjsModule._resolveFilename;
|
|
45
75
|
CjsModule._resolveFilename = function (request, parent, isMain, options) {
|
|
76
|
+
const vistaResolvedPath = resolveVistaInternalRequest(request);
|
|
77
|
+
if (vistaResolvedPath)
|
|
78
|
+
return vistaResolvedPath;
|
|
46
79
|
if (request === 'react')
|
|
47
80
|
return reactPath;
|
|
48
81
|
if (request === 'react-dom')
|
|
@@ -71,15 +104,24 @@ function installSingleReactResolution(cwd) {
|
|
|
71
104
|
}
|
|
72
105
|
function setupTypeScriptRuntime(cwd) {
|
|
73
106
|
try {
|
|
74
|
-
const
|
|
75
|
-
|
|
107
|
+
const swcRegisterPath = resolveFromWorkspace('@swc-node/register/register', cwd);
|
|
108
|
+
const typescriptPath = resolveFromWorkspace('typescript', cwd);
|
|
109
|
+
const { register } = require(swcRegisterPath);
|
|
110
|
+
const ts = require(typescriptPath);
|
|
111
|
+
register({
|
|
112
|
+
module: ts.ModuleKind.CommonJS,
|
|
113
|
+
jsx: ts.JsxEmit.ReactJSX,
|
|
114
|
+
moduleResolution: ts.ModuleResolutionKind.Node16,
|
|
115
|
+
esModuleInterop: true,
|
|
116
|
+
allowJs: true,
|
|
117
|
+
});
|
|
76
118
|
return;
|
|
77
119
|
}
|
|
78
120
|
catch {
|
|
79
121
|
// fallback
|
|
80
122
|
}
|
|
81
123
|
try {
|
|
82
|
-
const tsNodePath =
|
|
124
|
+
const tsNodePath = resolveFromWorkspace('ts-node', cwd);
|
|
83
125
|
require(tsNodePath).register({
|
|
84
126
|
transpileOnly: true,
|
|
85
127
|
compilerOptions: {
|
|
@@ -87,6 +129,7 @@ function setupTypeScriptRuntime(cwd) {
|
|
|
87
129
|
jsx: 'react-jsx',
|
|
88
130
|
moduleResolution: 'node16',
|
|
89
131
|
esModuleInterop: true,
|
|
132
|
+
allowJs: true,
|
|
90
133
|
},
|
|
91
134
|
});
|
|
92
135
|
return;
|
|
@@ -95,8 +138,8 @@ function setupTypeScriptRuntime(cwd) {
|
|
|
95
138
|
// fallback
|
|
96
139
|
}
|
|
97
140
|
try {
|
|
98
|
-
|
|
99
|
-
require(
|
|
141
|
+
const tsxPath = resolveFromWorkspace('tsx/cjs', cwd);
|
|
142
|
+
require(tsxPath);
|
|
100
143
|
}
|
|
101
144
|
catch {
|
|
102
145
|
// no transpiler available
|
|
@@ -111,8 +154,14 @@ function setupStaticGenerationRuntime(cwd) {
|
|
|
111
154
|
m.exports = {};
|
|
112
155
|
}
|
|
113
156
|
};
|
|
157
|
+
const cacheComponentsConfig = (0, config_1.resolveCacheComponentsConfig)((0, config_1.loadConfig)(cwd));
|
|
114
158
|
installSingleReactResolution(cwd);
|
|
115
159
|
setupTypeScriptRuntime(cwd);
|
|
160
|
+
(0, module_compile_hook_1.installModuleCompileHook)({
|
|
161
|
+
cwd,
|
|
162
|
+
cacheComponentsEnabled: cacheComponentsConfig.enabled,
|
|
163
|
+
});
|
|
164
|
+
(0, fetch_policy_1.installSegmentFetchPolicyShim)();
|
|
116
165
|
staticRuntimeReady = true;
|
|
117
166
|
}
|
|
118
167
|
// ---------------------------------------------------------------------------
|
|
@@ -178,97 +227,170 @@ function expandPattern(pattern, params) {
|
|
|
178
227
|
* server bundle. For RSC mode, the actual Flight prerendering is
|
|
179
228
|
* handled by the upstream process.
|
|
180
229
|
*/
|
|
181
|
-
async function prerenderPage(urlPath, route, params, cwd) {
|
|
230
|
+
async function prerenderPage(urlPath, route, params, cwd, vistaDirRoot, appPprEnabled) {
|
|
182
231
|
setupStaticGenerationRuntime(cwd);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
232
|
+
return (0, request_context_1.runWithRequestContext)({
|
|
233
|
+
cwd,
|
|
234
|
+
vistaDirRoot,
|
|
235
|
+
urlPath,
|
|
236
|
+
segmentConfig: route.segmentConfig,
|
|
237
|
+
}, async () => {
|
|
238
|
+
try {
|
|
239
|
+
const React = require('react');
|
|
240
|
+
const { renderToString } = require('react-dom/server');
|
|
241
|
+
const isAsyncComponent = (component) => {
|
|
242
|
+
return (typeof component === 'function' &&
|
|
243
|
+
component.constructor &&
|
|
244
|
+
component.constructor.name === 'AsyncFunction');
|
|
245
|
+
};
|
|
246
|
+
const renderComponent = async (component, props, child) => {
|
|
247
|
+
if (isAsyncComponent(component)) {
|
|
248
|
+
const asyncProps = child === undefined ? props : { ...props, children: child };
|
|
249
|
+
return component(asyncProps);
|
|
250
|
+
}
|
|
251
|
+
if (child === undefined) {
|
|
252
|
+
return React.createElement(component, props);
|
|
253
|
+
}
|
|
254
|
+
return React.createElement(component, props, child);
|
|
255
|
+
};
|
|
256
|
+
const renderStaticSubtree = async (input) => {
|
|
257
|
+
const appDir = path_1.default.join(cwd, 'app');
|
|
258
|
+
const RouteModule = require(input.entryFilePath);
|
|
259
|
+
const RouteComponent = RouteModule.default;
|
|
260
|
+
if (!RouteComponent) {
|
|
261
|
+
throw new Error(`Route module does not export default component: ${input.entryFilePath}`);
|
|
262
|
+
}
|
|
263
|
+
let subtree = await renderComponent(RouteComponent, {
|
|
264
|
+
params: input.params,
|
|
265
|
+
searchParams: input.searchParams,
|
|
266
|
+
});
|
|
267
|
+
const directoryChain = (0, app_router_runtime_1.resolveDirectoryChain)(input.subtreeRootDir, input.entryFilePath);
|
|
268
|
+
for (let i = directoryChain.length - 1; i >= 0; i--) {
|
|
269
|
+
const dir = directoryChain[i];
|
|
270
|
+
const layoutPath = (0, app_router_runtime_1.resolveConventionModule)(dir, 'root') ?? (0, app_router_runtime_1.resolveConventionModule)(dir, 'layout');
|
|
271
|
+
if (!layoutPath || path_1.default.resolve(layoutPath) === path_1.default.resolve(input.entryFilePath)) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const LayoutModule = require(layoutPath);
|
|
275
|
+
const LayoutComponent = LayoutModule.default;
|
|
276
|
+
if (!LayoutComponent) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const slotProps = {};
|
|
280
|
+
if (!input.disableParallelSlots) {
|
|
281
|
+
const slotMatches = (0, app_router_runtime_1.resolveParallelSlotMatches)({
|
|
282
|
+
appDir,
|
|
283
|
+
layoutPath,
|
|
284
|
+
pathname: input.pathname,
|
|
285
|
+
});
|
|
286
|
+
for (const slotMatch of slotMatches) {
|
|
287
|
+
slotProps[slotMatch.slotName] = await renderStaticSubtree({
|
|
288
|
+
subtreeRootDir: slotMatch.slotRootDir,
|
|
289
|
+
entryFilePath: slotMatch.filePath,
|
|
290
|
+
pathname: input.pathname,
|
|
291
|
+
params: {
|
|
292
|
+
...input.params,
|
|
293
|
+
...slotMatch.params,
|
|
294
|
+
},
|
|
295
|
+
searchParams: input.searchParams,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
subtree = await renderComponent(LayoutComponent, {
|
|
300
|
+
params: input.params,
|
|
301
|
+
searchParams: input.searchParams,
|
|
302
|
+
...slotProps,
|
|
303
|
+
}, subtree);
|
|
304
|
+
}
|
|
305
|
+
return subtree;
|
|
306
|
+
};
|
|
307
|
+
// Load page component from webpack-built server bundle
|
|
308
|
+
const pageModule = require(route.pagePath);
|
|
309
|
+
const PageComponent = pageModule.default;
|
|
310
|
+
if (!PageComponent) {
|
|
311
|
+
console.warn(`[vista:ssg] No default export in ${route.pagePath}`);
|
|
312
|
+
return null;
|
|
198
313
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
for (const layoutPath of route.layoutPaths) {
|
|
211
|
-
try {
|
|
212
|
-
const layoutModule = require(layoutPath);
|
|
213
|
-
if (layoutModule?.metadata && typeof layoutModule.metadata === 'object') {
|
|
214
|
-
metadata = { ...metadata, ...layoutModule.metadata };
|
|
314
|
+
let metadata = {};
|
|
315
|
+
const searchParams = {};
|
|
316
|
+
for (const layoutPath of route.layoutPaths) {
|
|
317
|
+
try {
|
|
318
|
+
const layoutModule = require(layoutPath);
|
|
319
|
+
if (layoutModule?.metadata && typeof layoutModule.metadata === 'object') {
|
|
320
|
+
metadata = { ...metadata, ...layoutModule.metadata };
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// Ignore layout metadata failures for static generation.
|
|
215
325
|
}
|
|
216
326
|
}
|
|
217
|
-
|
|
218
|
-
|
|
327
|
+
if (pageModule.metadata && typeof pageModule.metadata === 'object') {
|
|
328
|
+
metadata = { ...metadata, ...pageModule.metadata };
|
|
219
329
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
330
|
+
if (typeof pageModule.generateMetadata === 'function') {
|
|
331
|
+
try {
|
|
332
|
+
const dynamicMeta = await pageModule.generateMetadata({ params: params || {}, searchParams }, metadata);
|
|
333
|
+
if (dynamicMeta && typeof dynamicMeta === 'object') {
|
|
334
|
+
metadata = { ...metadata, ...dynamicMeta };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch (metadataError) {
|
|
338
|
+
console.warn(`[vista:ssg] generateMetadata failed for ${urlPath}:`, metadataError?.message || String(metadataError));
|
|
229
339
|
}
|
|
230
340
|
}
|
|
231
|
-
|
|
232
|
-
console.warn(`[vista:ssg] generateMetadata failed for ${urlPath}:`, metadataError?.message || String(metadataError));
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
let metadataHtml = '';
|
|
236
|
-
try {
|
|
237
|
-
const { generateMetadataHtml } = require('../metadata/generate');
|
|
238
|
-
metadataHtml = generateMetadataHtml(metadata);
|
|
239
|
-
}
|
|
240
|
-
catch {
|
|
241
|
-
metadataHtml = '';
|
|
242
|
-
}
|
|
243
|
-
// Build the element, passing params as props
|
|
244
|
-
let element = await renderComponent(PageComponent, { params: params || {} });
|
|
245
|
-
// Wrap in layouts (outside-in)
|
|
246
|
-
for (let i = route.layoutPaths.length - 1; i >= 0; i--) {
|
|
341
|
+
let metadataHtml = '';
|
|
247
342
|
try {
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
if (LayoutComponent) {
|
|
251
|
-
element = await renderComponent(LayoutComponent, { params: params || {}, searchParams: {} }, element);
|
|
252
|
-
}
|
|
343
|
+
const { generateMetadataHtml } = require('../metadata/generate');
|
|
344
|
+
metadataHtml = generateMetadataHtml(metadata);
|
|
253
345
|
}
|
|
254
346
|
catch {
|
|
255
|
-
|
|
347
|
+
metadataHtml = '';
|
|
348
|
+
}
|
|
349
|
+
const element = await renderStaticSubtree({
|
|
350
|
+
subtreeRootDir: path_1.default.join(cwd, 'app'),
|
|
351
|
+
entryFilePath: route.pagePath,
|
|
352
|
+
pathname: urlPath,
|
|
353
|
+
params: params || {},
|
|
354
|
+
searchParams,
|
|
355
|
+
});
|
|
356
|
+
const pprEnabled = (0, ppr_1.isRoutePPREligible)(route, appPprEnabled);
|
|
357
|
+
let shellHtml;
|
|
358
|
+
let pprInfo = undefined;
|
|
359
|
+
if (pprEnabled && route.loadingPath) {
|
|
360
|
+
try {
|
|
361
|
+
const shellElement = await renderStaticSubtree({
|
|
362
|
+
subtreeRootDir: path_1.default.join(cwd, 'app'),
|
|
363
|
+
entryFilePath: route.loadingPath,
|
|
364
|
+
pathname: urlPath,
|
|
365
|
+
params: params || {},
|
|
366
|
+
searchParams,
|
|
367
|
+
});
|
|
368
|
+
const renderedShellHtml = renderToString(shellElement);
|
|
369
|
+
shellHtml = (0, ppr_1.injectPprResumeBootstrap)(wrapInDocument(`${renderedShellHtml}\n<!--vista:ppr-shell-->`, urlPath, metadataHtml, cwd), urlPath);
|
|
370
|
+
pprInfo = (0, ppr_1.createPartialPrerenderInfo)(urlPath);
|
|
371
|
+
}
|
|
372
|
+
catch (shellError) {
|
|
373
|
+
console.warn(`[vista:ppr] Failed to generate shell for ${urlPath}:`, shellError?.message || String(shellError));
|
|
374
|
+
}
|
|
256
375
|
}
|
|
376
|
+
// Render to HTML string
|
|
377
|
+
const html = renderToString(element);
|
|
378
|
+
return {
|
|
379
|
+
html: wrapInDocument(html, urlPath, metadataHtml, cwd),
|
|
380
|
+
shellHtml,
|
|
381
|
+
generatedAt: Date.now(),
|
|
382
|
+
revalidate: route.revalidate || 0,
|
|
383
|
+
routePattern: route.pattern,
|
|
384
|
+
params,
|
|
385
|
+
tags: (0, request_context_1.consumeTrackedTags)(),
|
|
386
|
+
ppr: pprInfo,
|
|
387
|
+
};
|
|
257
388
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
revalidate: route.revalidate || 0,
|
|
264
|
-
routePattern: route.pattern,
|
|
265
|
-
params,
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
catch (err) {
|
|
269
|
-
console.error(`[vista:ssg] Error pre-rendering ${urlPath}:`, err?.message || String(err));
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
389
|
+
catch (err) {
|
|
390
|
+
console.error(`[vista:ssg] Error pre-rendering ${urlPath}:`, err?.message || String(err));
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
});
|
|
272
394
|
}
|
|
273
395
|
/**
|
|
274
396
|
* Wrap rendered HTML in a basic document shell.
|
|
@@ -381,16 +503,41 @@ async function startStaticFlightUpstream(cwd) {
|
|
|
381
503
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
382
504
|
return null;
|
|
383
505
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
506
|
+
let child;
|
|
507
|
+
try {
|
|
508
|
+
child = (0, child_process_1.spawn)(process.execPath, ['--conditions', 'react-server', upstreamScript, '--port', String(port)], {
|
|
509
|
+
cwd,
|
|
510
|
+
env: {
|
|
511
|
+
...process.env,
|
|
512
|
+
NODE_ENV: process.env.NODE_ENV || 'production',
|
|
513
|
+
RSC_UPSTREAM_PORT: String(port),
|
|
514
|
+
},
|
|
515
|
+
stdio: 'pipe',
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
catch (spawnError) {
|
|
519
|
+
if ((0, spawn_permissions_1.isPermissionDeniedSpawnError)(spawnError)) {
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
throw spawnError;
|
|
523
|
+
}
|
|
524
|
+
try {
|
|
525
|
+
await waitForUpstreamReady(child, 12000);
|
|
526
|
+
}
|
|
527
|
+
catch (startupError) {
|
|
528
|
+
if ((0, spawn_permissions_1.isPermissionDeniedSpawnError)(startupError)) {
|
|
529
|
+
try {
|
|
530
|
+
if (!child.killed) {
|
|
531
|
+
child.kill();
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
// ignore cleanup failures
|
|
536
|
+
}
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
throw startupError;
|
|
540
|
+
}
|
|
394
541
|
const close = async () => {
|
|
395
542
|
if (child.killed)
|
|
396
543
|
return;
|
|
@@ -471,6 +618,8 @@ function wrapInDocument(bodyHtml, _urlPath, metadataHtml, cwd) {
|
|
|
471
618
|
*/
|
|
472
619
|
async function generateStaticPages(options) {
|
|
473
620
|
const { cwd, vistaDirRoot, manifest, isDev, buildId } = options;
|
|
621
|
+
const vistaConfig = (0, config_1.loadConfig)(cwd);
|
|
622
|
+
const appPprEnabled = (0, ppr_1.isAppPPREnabled)(vistaConfig);
|
|
474
623
|
const result = {
|
|
475
624
|
pagesGenerated: 0,
|
|
476
625
|
generatedPaths: [],
|
|
@@ -479,7 +628,7 @@ async function generateStaticPages(options) {
|
|
|
479
628
|
};
|
|
480
629
|
// In dev mode, skip prerendering (pages are rendered on demand)
|
|
481
630
|
if (isDev) {
|
|
482
|
-
result.manifest = (0, static_cache_1.generatePrerenderManifest)(manifest.routes);
|
|
631
|
+
result.manifest = (0, static_cache_1.generatePrerenderManifest)(manifest.routes, undefined, { appPprEnabled });
|
|
483
632
|
return result;
|
|
484
633
|
}
|
|
485
634
|
const staticRoutes = manifest.routes.filter((r) => r.renderMode === 'static' || r.renderMode === 'isr');
|
|
@@ -489,14 +638,19 @@ async function generateStaticPages(options) {
|
|
|
489
638
|
flightUpstream = await startStaticFlightUpstream(cwd);
|
|
490
639
|
}
|
|
491
640
|
catch (flightError) {
|
|
492
|
-
|
|
641
|
+
if ((0, spawn_permissions_1.isPermissionDeniedSpawnError)(flightError)) {
|
|
642
|
+
console.log('[vista:ssg] Flight payload pre-generation skipped (spawn blocked by environment permissions)');
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
console.warn(`[vista:ssg] Flight payload pre-generation disabled: ${(0, spawn_permissions_1.getErrorMessage)(flightError)}`);
|
|
646
|
+
}
|
|
493
647
|
}
|
|
494
648
|
try {
|
|
495
649
|
for (const route of staticRoutes) {
|
|
496
650
|
if (route.type === 'static') {
|
|
497
651
|
// Simple static route — single URL
|
|
498
652
|
const urlPath = route.pattern;
|
|
499
|
-
const page = await prerenderPage(urlPath, route, undefined, cwd);
|
|
653
|
+
const page = await prerenderPage(urlPath, route, undefined, cwd, vistaDirRoot, appPprEnabled);
|
|
500
654
|
if (page) {
|
|
501
655
|
if (flightUpstream) {
|
|
502
656
|
const flightData = await flightUpstream.fetchFlight(urlPath);
|
|
@@ -522,7 +676,7 @@ async function generateStaticPages(options) {
|
|
|
522
676
|
}
|
|
523
677
|
for (const params of paramSets) {
|
|
524
678
|
const urlPath = expandPattern(route.pattern, params);
|
|
525
|
-
const page = await prerenderPage(urlPath, route, params, cwd);
|
|
679
|
+
const page = await prerenderPage(urlPath, route, params, cwd, vistaDirRoot, appPprEnabled);
|
|
526
680
|
if (page) {
|
|
527
681
|
if (flightUpstream) {
|
|
528
682
|
const flightData = await flightUpstream.fetchFlight(urlPath);
|
|
@@ -548,7 +702,7 @@ async function generateStaticPages(options) {
|
|
|
548
702
|
}
|
|
549
703
|
}
|
|
550
704
|
// Generate prerender manifest
|
|
551
|
-
result.manifest = (0, static_cache_1.generatePrerenderManifest)(manifest.routes);
|
|
705
|
+
result.manifest = (0, static_cache_1.generatePrerenderManifest)(manifest.routes, undefined, { appPprEnabled });
|
|
552
706
|
// Write manifest to disk
|
|
553
707
|
const manifestPath = path_1.default.join(vistaDirRoot, 'prerender-manifest.json');
|
|
554
708
|
fs_1.default.writeFileSync(manifestPath, JSON.stringify(result.manifest, null, 2));
|
|
@@ -566,7 +720,7 @@ async function revalidatePath(urlPath, route, params, cwd, vistaDirRoot) {
|
|
|
566
720
|
}
|
|
567
721
|
(0, static_cache_1.markRevalidating)(urlPath);
|
|
568
722
|
try {
|
|
569
|
-
const page = await prerenderPage(urlPath, route, params, cwd);
|
|
723
|
+
const page = await prerenderPage(urlPath, route, params, cwd, vistaDirRoot, (0, ppr_1.isAppPPREnabled)((0, config_1.loadConfig)(cwd)));
|
|
570
724
|
if (page) {
|
|
571
725
|
(0, static_cache_1.setCachedPage)(urlPath, page);
|
|
572
726
|
(0, static_cache_1.writeStaticPageToDisk)(vistaDirRoot, urlPath, page);
|
|
@@ -25,7 +25,7 @@ export interface StructureValidationResult {
|
|
|
25
25
|
export interface RouteGraphNode {
|
|
26
26
|
segment: string;
|
|
27
27
|
pattern: string;
|
|
28
|
-
kind: 'static' | 'dynamic' | 'catch-all' | 'optional-catch-all' | 'group' | 'reserved-internal';
|
|
28
|
+
kind: 'static' | 'dynamic' | 'catch-all' | 'optional-catch-all' | 'group' | 'parallel' | 'interception' | 'reserved-internal';
|
|
29
29
|
hasPage: boolean;
|
|
30
30
|
hasLayout: boolean;
|
|
31
31
|
children: RouteGraphNode[];
|
|
@@ -20,7 +20,6 @@ const path_1 = __importDefault(require("path"));
|
|
|
20
20
|
// ============================================================================
|
|
21
21
|
const FILE_EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js'];
|
|
22
22
|
const RESERVED_INTERNAL_SEGMENTS = new Set(['[not-found]']);
|
|
23
|
-
const VALID_SEGMENT_PATTERN = /^[a-zA-Z0-9_\-]+$|^\[\[\.\.\.[\w\-]+\]\]$|^\[[\w\-]+\]$|^\[\.\.\.[\w\-]+\]$|^\([\w\-]+\)$/;
|
|
24
23
|
const CONVENTION_FILES = new Set([
|
|
25
24
|
'page',
|
|
26
25
|
'layout',
|
|
@@ -41,9 +40,28 @@ function fileExistsWithExtensions(dir, stem) {
|
|
|
41
40
|
}
|
|
42
41
|
return null;
|
|
43
42
|
}
|
|
43
|
+
function isParallelSegment(name) {
|
|
44
|
+
return /^@[\w-]+$/.test(name);
|
|
45
|
+
}
|
|
46
|
+
function isInterceptionSegment(name) {
|
|
47
|
+
return /^(?:\(\.\)|\(\.\.\)|\(\.\.\)\(\.\.\)|\(\.\.\.\))(?:[\w-]+|\[[^\]]+\]|\[\[\.\.\.[^\]]+\]\])$/.test(name);
|
|
48
|
+
}
|
|
49
|
+
function isValidSegmentName(name) {
|
|
50
|
+
return (/^[a-zA-Z0-9_\-]+$/.test(name) ||
|
|
51
|
+
/^\[\[\.\.\.[\w\-]+\]\]$/.test(name) ||
|
|
52
|
+
/^\[[\w\-]+\]$/.test(name) ||
|
|
53
|
+
/^\[\.\.\.[\w\-]+\]$/.test(name) ||
|
|
54
|
+
/^\([\w\-]+\)$/.test(name) ||
|
|
55
|
+
isParallelSegment(name) ||
|
|
56
|
+
isInterceptionSegment(name));
|
|
57
|
+
}
|
|
44
58
|
function classifySegment(name) {
|
|
45
59
|
if (RESERVED_INTERNAL_SEGMENTS.has(name))
|
|
46
60
|
return 'reserved-internal';
|
|
61
|
+
if (isParallelSegment(name))
|
|
62
|
+
return 'parallel';
|
|
63
|
+
if (isInterceptionSegment(name))
|
|
64
|
+
return 'interception';
|
|
47
65
|
if (name.startsWith('(') && name.endsWith(')'))
|
|
48
66
|
return 'group';
|
|
49
67
|
if (name.startsWith('[[...') && name.endsWith(']]'))
|
|
@@ -67,7 +85,7 @@ function segmentToPattern(segment, kind) {
|
|
|
67
85
|
const param = segment.slice(5, -2); // [[...slug]] -> slug
|
|
68
86
|
return `:${param}*?`;
|
|
69
87
|
}
|
|
70
|
-
if (kind === 'group')
|
|
88
|
+
if (kind === 'group' || kind === 'parallel' || kind === 'interception')
|
|
71
89
|
return '';
|
|
72
90
|
return segment;
|
|
73
91
|
}
|
|
@@ -105,6 +123,9 @@ function buildRouteGraph(dir, parentPattern = '') {
|
|
|
105
123
|
}
|
|
106
124
|
function collectPatterns(nodes, acc = new Map()) {
|
|
107
125
|
for (const node of nodes) {
|
|
126
|
+
if (node.kind === 'parallel' || node.kind === 'interception') {
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
108
129
|
if (node.hasPage && node.kind !== 'reserved-internal') {
|
|
109
130
|
// Normalize dynamic params so /blog/:id and /blog/:slug collide
|
|
110
131
|
const normalizedPattern = node.pattern.replace(/:[^/]+/g, ':_dynamic_');
|
|
@@ -198,13 +219,13 @@ function checkInvalidSegmentNames(dir, issues) {
|
|
|
198
219
|
// Skip hidden directories and reserved
|
|
199
220
|
if (name.startsWith('.') || name === 'node_modules')
|
|
200
221
|
continue;
|
|
201
|
-
if (!
|
|
222
|
+
if (!isValidSegmentName(name) && !RESERVED_INTERNAL_SEGMENTS.has(name)) {
|
|
202
223
|
issues.push({
|
|
203
224
|
code: 'INVALID_SEGMENT_NAME',
|
|
204
225
|
severity: 'error',
|
|
205
|
-
message: `Invalid route segment name: "${name}". Segments must be alphanumeric/dashes, [param], [...param],
|
|
226
|
+
message: `Invalid route segment name: "${name}". Segments must be alphanumeric/dashes, [param], [...param], (group), @slot, or interception syntax.`,
|
|
206
227
|
filePath: path_1.default.join(dir, name),
|
|
207
|
-
fix: `Rename "${name}" to a valid segment pattern (e.g., lowercase-dashed, [dynamic], (group)).`,
|
|
228
|
+
fix: `Rename "${name}" to a valid segment pattern (e.g., lowercase-dashed, [dynamic], (group), @modal, or (.)target).`,
|
|
208
229
|
});
|
|
209
230
|
}
|
|
210
231
|
// Recurse into children
|
|
@@ -32,7 +32,7 @@ class StructureWatcher extends events_1.EventEmitter {
|
|
|
32
32
|
constructor(options) {
|
|
33
33
|
super();
|
|
34
34
|
this.cwd = options.cwd;
|
|
35
|
-
this.debounceMs = options.debounceMs ??
|
|
35
|
+
this.debounceMs = options.debounceMs ?? 60;
|
|
36
36
|
this.notFoundRoute = options.notFoundRoute;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type express from 'express';
|
|
2
2
|
import type { ResolvedTypedApiConfig } from '../config';
|
|
3
3
|
export declare function resolveLegacyApiRoutePath(cwd: string, requestPath: string): string | null;
|
|
4
|
+
export declare function resolveLegacyRouteHandlerPath(cwd: string, requestPath: string): string | null;
|
|
4
5
|
export declare function runLegacyApiRoute(options: {
|
|
5
6
|
req: express.Request;
|
|
6
7
|
res: express.Response;
|