@vistagenic/vista 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/build-rsc.js +18 -0
- package/dist/bin/build.js +10 -0
- package/dist/bin/webpack.config.d.ts +3 -1
- package/dist/bin/webpack.config.js +3 -1
- package/dist/build/rsc/compiler.d.ts +3 -1
- package/dist/build/rsc/compiler.js +7 -3
- package/dist/build/rsc/react-client-reference-manifest.d.ts +7 -0
- package/dist/build/rsc/react-client-reference-manifest.js +132 -6
- package/dist/client/link.js +1 -1
- package/dist/client/navigation.js +2 -2
- package/dist/client/rsc-router.d.ts +1 -3
- package/dist/client/rsc-router.js +98 -45
- package/dist/deploy/adapters/cloudflare.js +2 -25
- package/dist/deploy/adapters/netlify.js +2 -14
- package/dist/deploy/utils.d.ts +6 -0
- package/dist/deploy/utils.js +64 -1
- package/dist/image/get-img-props.js +21 -11
- package/dist/image/image-config.d.ts +2 -0
- package/dist/image/image-config.js +17 -0
- package/dist/image/index.d.ts +18 -3
- package/dist/image/index.js +1 -1
- package/dist/image/react-server.d.ts +1 -2
- package/dist/image/react-server.js +1 -1
- package/dist/server/app-router-runtime.d.ts +1 -0
- package/dist/server/app-router-runtime.js +34 -9
- package/dist/server/hydration-chunks.d.ts +7 -0
- package/dist/server/hydration-chunks.js +49 -0
- package/dist/server/module-compile-hook.js +11 -0
- package/dist/server/rsc-engine.js +14 -61
- package/dist/server/rsc-upstream.js +10 -3
- package/dist/server/ssr-webpack-shim.d.ts +6 -0
- package/dist/server/ssr-webpack-shim.js +29 -0
- package/dist/server/static-generator.d.ts +2 -0
- package/dist/server/static-generator.js +29 -29
- package/package.json +1 -1
|
@@ -55,18 +55,6 @@ function writeFullRuntimeNetlifyToml(ctx) {
|
|
|
55
55
|
(0, utils_1.writeFileIfAllowed)(targetFile, content, ctx.force);
|
|
56
56
|
return targetFile;
|
|
57
57
|
}
|
|
58
|
-
function writeStaticRedirects(outputDir) {
|
|
59
|
-
const redirectsPath = path_1.default.join(outputDir, '_redirects');
|
|
60
|
-
const lines = [
|
|
61
|
-
'/_vista/* /:splat 200',
|
|
62
|
-
'/ /static/pages/index.html 200',
|
|
63
|
-
'/rsc /static/pages/index.rsc 200',
|
|
64
|
-
'/_rsc/* /static/pages/:splat.rsc 200',
|
|
65
|
-
'/* /static/pages/:splat.html 200',
|
|
66
|
-
];
|
|
67
|
-
fs_1.default.writeFileSync(redirectsPath, `${lines.join('\n')}\n`, 'utf8');
|
|
68
|
-
return redirectsPath;
|
|
69
|
-
}
|
|
70
58
|
exports.netlifyAdapter = {
|
|
71
59
|
id: 'netlify',
|
|
72
60
|
requiredOutput: 'standalone',
|
|
@@ -83,12 +71,12 @@ exports.netlifyAdapter = {
|
|
|
83
71
|
(0, utils_1.ensureDir)(outputDir);
|
|
84
72
|
if ((0, runtime_pack_1.isStaticOnlyDeploy)(ctx)) {
|
|
85
73
|
(0, utils_1.copyStaticHostAssets)(ctx.cwd, ctx.vistaDir, outputDir);
|
|
86
|
-
|
|
74
|
+
(0, utils_1.prepareStaticCdnOutput)(outputDir);
|
|
87
75
|
const netlifyTomlPath = writeStaticNetlifyToml(ctx);
|
|
88
76
|
return {
|
|
89
77
|
status: 'emitted',
|
|
90
78
|
target: 'netlify',
|
|
91
|
-
artifactPaths: [outputDir,
|
|
79
|
+
artifactPaths: [outputDir, netlifyTomlPath],
|
|
92
80
|
instructions: ['Static mode: Netlify serves pre-rendered pages only.'],
|
|
93
81
|
};
|
|
94
82
|
}
|
package/dist/deploy/utils.d.ts
CHANGED
|
@@ -15,4 +15,10 @@ export declare const STATIC_HOST_ROUTE_RULES: ({
|
|
|
15
15
|
dest: string;
|
|
16
16
|
handle?: undefined;
|
|
17
17
|
})[];
|
|
18
|
+
/** Lift `.vista/static/pages/*.html` to pretty CDN paths (`docs/foo/index.html`). */
|
|
19
|
+
export declare function flattenPrerenderedPages(pagesDir: string, targetDir: string): void;
|
|
20
|
+
/** Serve Flight files at `/rsc/*.rsc` (extension avoids file/directory collisions). */
|
|
21
|
+
export declare function flattenPrerenderedFlight(pagesDir: string, targetDir: string): void;
|
|
22
|
+
/** Copy webpack assets to `/_vista/static` and flatten HTML + Flight for file-based CDNs. */
|
|
23
|
+
export declare function prepareStaticCdnOutput(targetDir: string): void;
|
|
18
24
|
export declare function copyStaticHostAssets(cwd: string, vistaDir: string, targetDir: string): void;
|
package/dist/deploy/utils.js
CHANGED
|
@@ -9,6 +9,9 @@ exports.copyDirectoryRecursive = copyDirectoryRecursive;
|
|
|
9
9
|
exports.copyFileIfPresent = copyFileIfPresent;
|
|
10
10
|
exports.writeFileIfAllowed = writeFileIfAllowed;
|
|
11
11
|
exports.readJsonSafe = readJsonSafe;
|
|
12
|
+
exports.flattenPrerenderedPages = flattenPrerenderedPages;
|
|
13
|
+
exports.flattenPrerenderedFlight = flattenPrerenderedFlight;
|
|
14
|
+
exports.prepareStaticCdnOutput = prepareStaticCdnOutput;
|
|
12
15
|
exports.copyStaticHostAssets = copyStaticHostAssets;
|
|
13
16
|
const fs_1 = __importDefault(require("fs"));
|
|
14
17
|
const path_1 = __importDefault(require("path"));
|
|
@@ -84,15 +87,75 @@ function readJsonSafe(absolutePath) {
|
|
|
84
87
|
}
|
|
85
88
|
exports.STATIC_HOST_ROUTE_RULES = [
|
|
86
89
|
{ handle: 'filesystem' },
|
|
87
|
-
{ src: '^/_vista/(.*)$', dest: '/$1' },
|
|
90
|
+
{ src: '^/_vista/static/(.*)$', dest: '/static/$1' },
|
|
88
91
|
{ src: '^/(?:rsc|_rsc)/?$', dest: '/static/pages/index.rsc' },
|
|
89
92
|
{ src: '^/(?:rsc|_rsc)/(.+)$', dest: '/static/pages/$1.rsc' },
|
|
90
93
|
{ src: '^/$', dest: '/static/pages/index.html' },
|
|
91
94
|
{ src: '^/(.+)$', dest: '/static/pages/$1.html' },
|
|
92
95
|
];
|
|
96
|
+
function walkFiles(dir, visitor, prefix = '') {
|
|
97
|
+
if (!fs_1.default.existsSync(dir))
|
|
98
|
+
return;
|
|
99
|
+
const entries = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
102
|
+
const absolutePath = path_1.default.join(dir, entry.name);
|
|
103
|
+
if (entry.isDirectory()) {
|
|
104
|
+
walkFiles(absolutePath, visitor, relativePath);
|
|
105
|
+
}
|
|
106
|
+
else if (entry.isFile()) {
|
|
107
|
+
visitor(absolutePath, relativePath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Lift `.vista/static/pages/*.html` to pretty CDN paths (`docs/foo/index.html`). */
|
|
112
|
+
function flattenPrerenderedPages(pagesDir, targetDir) {
|
|
113
|
+
walkFiles(pagesDir, (absolutePath, relativePath) => {
|
|
114
|
+
const posix = relativePath.replace(/\\/g, '/');
|
|
115
|
+
if (!posix.endsWith('.html') || posix.endsWith('.shell.html'))
|
|
116
|
+
return;
|
|
117
|
+
const destRel = posix === 'index.html' || posix.endsWith('/index.html')
|
|
118
|
+
? posix
|
|
119
|
+
: posix.replace(/\.html$/, '/index.html');
|
|
120
|
+
const dest = path_1.default.join(targetDir, destRel.split('/').join(path_1.default.sep));
|
|
121
|
+
ensureDir(path_1.default.dirname(dest));
|
|
122
|
+
fs_1.default.copyFileSync(absolutePath, dest);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/** Serve Flight files at `/rsc/*.rsc` (extension avoids file/directory collisions). */
|
|
126
|
+
function flattenPrerenderedFlight(pagesDir, targetDir) {
|
|
127
|
+
walkFiles(pagesDir, (absolutePath, relativePath) => {
|
|
128
|
+
const posix = relativePath.replace(/\\/g, '/');
|
|
129
|
+
if (!posix.endsWith('.rsc'))
|
|
130
|
+
return;
|
|
131
|
+
const destRel = `rsc/${posix}`;
|
|
132
|
+
const dest = path_1.default.join(targetDir, destRel.split('/').join(path_1.default.sep));
|
|
133
|
+
ensureDir(path_1.default.dirname(dest));
|
|
134
|
+
fs_1.default.copyFileSync(absolutePath, dest);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function writeStaticRscRedirects(targetDir) {
|
|
138
|
+
const redirectsPath = path_1.default.join(targetDir, '_redirects');
|
|
139
|
+
const lines = [
|
|
140
|
+
'/rsc /rsc/index.rsc 200',
|
|
141
|
+
'/rsc/ /rsc/index.rsc 200',
|
|
142
|
+
'/rsc/*.rsc /rsc/:splat.rsc 200',
|
|
143
|
+
'/rsc/* /rsc/:splat.rsc 200',
|
|
144
|
+
];
|
|
145
|
+
fs_1.default.writeFileSync(redirectsPath, `${lines.join('\n')}\n`, 'utf8');
|
|
146
|
+
}
|
|
147
|
+
/** Copy webpack assets to `/_vista/static` and flatten HTML + Flight for file-based CDNs. */
|
|
148
|
+
function prepareStaticCdnOutput(targetDir) {
|
|
149
|
+
const staticDir = path_1.default.join(targetDir, 'static');
|
|
150
|
+
copyDirectoryRecursive(staticDir, path_1.default.join(targetDir, '_vista', 'static'));
|
|
151
|
+
flattenPrerenderedPages(path_1.default.join(staticDir, 'pages'), targetDir);
|
|
152
|
+
flattenPrerenderedFlight(path_1.default.join(staticDir, 'pages'), targetDir);
|
|
153
|
+
writeStaticRscRedirects(targetDir);
|
|
154
|
+
}
|
|
93
155
|
function copyStaticHostAssets(cwd, vistaDir, targetDir) {
|
|
94
156
|
copyDirectoryRecursive(path_1.default.join(cwd, 'public'), targetDir);
|
|
95
157
|
copyDirectoryRecursive(path_1.default.join(vistaDir, 'static'), path_1.default.join(targetDir, 'static'));
|
|
158
|
+
copyDirectoryRecursive(path_1.default.join(vistaDir, 'static'), path_1.default.join(targetDir, '_vista', 'static'));
|
|
96
159
|
const clientCssPath = path_1.default.join(vistaDir, 'client.css');
|
|
97
160
|
copyFileIfPresent(clientCssPath, path_1.default.join(targetDir, 'client.css'));
|
|
98
161
|
copyFileIfPresent(clientCssPath, path_1.default.join(targetDir, 'styles.css'));
|
|
@@ -3,22 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getImgProps = getImgProps;
|
|
4
4
|
const image_config_1 = require("./image-config");
|
|
5
5
|
// Helper: Generate srcSet
|
|
6
|
-
function generateSrcSet(src,
|
|
6
|
+
function generateSrcSet(src, _width, loader, config, unoptimized, quality) {
|
|
7
7
|
if (unoptimized)
|
|
8
8
|
return undefined;
|
|
9
9
|
const { deviceSizes, imageSizes } = config;
|
|
10
10
|
const sizes = [...deviceSizes, ...imageSizes].sort((a, b) => a - b);
|
|
11
|
-
// If width is known, only generate sizes up to that width (plus maybe 2x/3x for density)
|
|
12
|
-
// For simplicity MVP, we'll generate device sizes.
|
|
13
11
|
return sizes
|
|
14
12
|
.map((size) => {
|
|
15
|
-
const url = loader({ src, width: size });
|
|
13
|
+
const url = loader({ src, width: size, quality });
|
|
16
14
|
return `${url} ${size}w`;
|
|
17
15
|
})
|
|
18
16
|
.join(', ');
|
|
19
17
|
}
|
|
20
18
|
function getImgProps(props, config = image_config_1.imageConfigDefault, defaultLoader) {
|
|
21
|
-
const { src, alt, width, height, fill, loader = defaultLoader, quality, priority, unoptimized, style, sizes, className, loading, placeholder, blurDataURL, onLoadingComplete, ...rest } = props;
|
|
19
|
+
const { src, alt, width, height, fill, loader = defaultLoader, quality, priority, unoptimized, style, sizes, className, loading, placeholder: _placeholder, blurDataURL: _blurDataURL, onLoadingComplete: _onLoadingComplete, ...rest } = props;
|
|
22
20
|
const imgStyle = { ...style };
|
|
23
21
|
// Handle Fill Mode
|
|
24
22
|
if (fill) {
|
|
@@ -29,15 +27,27 @@ function getImgProps(props, config = image_config_1.imageConfigDefault, defaultL
|
|
|
29
27
|
imgStyle.objectFit = 'cover'; // Default to cover for bg images
|
|
30
28
|
}
|
|
31
29
|
// Handle Dimensions
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const widthInt = width ? Number(width) : undefined;
|
|
31
|
+
const heightInt = height ? Number(height) : undefined;
|
|
34
32
|
const vercelStaticBuild = typeof window === 'undefined' &&
|
|
35
33
|
(process.env.VERCEL === '1' || typeof process.env.VERCEL_URL === 'string');
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
const srcPath = String(src || '')
|
|
35
|
+
.split('?')[0]
|
|
36
|
+
.toLowerCase();
|
|
37
|
+
const passthroughSrc = srcPath.startsWith('data:') ||
|
|
38
|
+
srcPath.startsWith('blob:') ||
|
|
39
|
+
srcPath.endsWith('.svg') ||
|
|
40
|
+
srcPath.endsWith('.gif') ||
|
|
41
|
+
srcPath.endsWith('.ico');
|
|
42
|
+
const staticHost = process.env.VISTA_DEPLOY_OUTPUT === 'static' ||
|
|
43
|
+
process.env.VISTA_IMAGES_UNOPTIMIZED === '1' ||
|
|
44
|
+
process.env.CF_PAGES === '1' ||
|
|
45
|
+
process.env.NETLIFY === 'true';
|
|
46
|
+
// Skip /_vista/image when the optimizer is unavailable (static CDN hosts)
|
|
47
|
+
// or the format cannot be resized (SVG/GIF/ICO).
|
|
48
|
+
const disableOptimization = !!unoptimized || !!config.unoptimized || passthroughSrc || staticHost || vercelStaticBuild;
|
|
39
49
|
// Generate SrcSet
|
|
40
|
-
const srcSet = generateSrcSet(src, widthInt, loader, config, disableOptimization);
|
|
50
|
+
const srcSet = generateSrcSet(src, widthInt, loader, config, disableOptimization, quality ? Number(quality) : undefined);
|
|
41
51
|
return {
|
|
42
52
|
...rest,
|
|
43
53
|
src,
|
|
@@ -14,6 +14,8 @@ export declare const imageConfigDefault: {
|
|
|
14
14
|
remotePatterns: any[];
|
|
15
15
|
unoptimized: boolean;
|
|
16
16
|
};
|
|
17
|
+
/** Merge defaults with build-time env (`VISTA_IMAGES_UNOPTIMIZED`, static deploy). */
|
|
18
|
+
export declare function resolveRuntimeImageConfig(overrides?: ImageConfig): ImageConfigComplete;
|
|
17
19
|
export type ImageConfigComplete = typeof imageConfigDefault;
|
|
18
20
|
export type ImageConfig = Partial<ImageConfigComplete>;
|
|
19
21
|
export declare const VALID_LOADERS: readonly ["default", "imgix", "cloudinary", "akamai", "custom"];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.VALID_LOADERS = exports.imageConfigDefault = void 0;
|
|
4
|
+
exports.resolveRuntimeImageConfig = resolveRuntimeImageConfig;
|
|
4
5
|
const constants_1 = require("../constants");
|
|
5
6
|
exports.imageConfigDefault = {
|
|
6
7
|
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
|
@@ -18,4 +19,20 @@ exports.imageConfigDefault = {
|
|
|
18
19
|
remotePatterns: [],
|
|
19
20
|
unoptimized: false,
|
|
20
21
|
};
|
|
22
|
+
function envDisablesImageOptimization() {
|
|
23
|
+
return (process.env.VISTA_IMAGES_UNOPTIMIZED === '1' ||
|
|
24
|
+
process.env.VISTA_DEPLOY_OUTPUT === 'static' ||
|
|
25
|
+
process.env.CF_PAGES === '1' ||
|
|
26
|
+
process.env.NETLIFY === 'true');
|
|
27
|
+
}
|
|
28
|
+
/** Merge defaults with build-time env (`VISTA_IMAGES_UNOPTIMIZED`, static deploy). */
|
|
29
|
+
function resolveRuntimeImageConfig(overrides) {
|
|
30
|
+
return {
|
|
31
|
+
...exports.imageConfigDefault,
|
|
32
|
+
...overrides,
|
|
33
|
+
unoptimized: overrides?.unoptimized === true ||
|
|
34
|
+
exports.imageConfigDefault.unoptimized ||
|
|
35
|
+
envDisablesImageOptimization(),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
21
38
|
exports.VALID_LOADERS = ['default', 'imgix', 'cloudinary', 'akamai', 'custom'];
|
package/dist/image/index.d.ts
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ImageProps } from './get-img-props';
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export type EnhancedImageProps = ImageProps;
|
|
4
|
+
export declare const Image: React.ForwardRefExoticComponent<React.ImgHTMLAttributes<HTMLImageElement> & {
|
|
5
|
+
src: string;
|
|
6
|
+
alt: string;
|
|
7
|
+
width?: number | string;
|
|
8
|
+
height?: number | string;
|
|
9
|
+
fill?: boolean;
|
|
10
|
+
loader?: import("./image-loader").ImageLoader;
|
|
11
|
+
quality?: number | string;
|
|
12
|
+
priority?: boolean;
|
|
13
|
+
unoptimized?: boolean;
|
|
14
|
+
placeholder?: import("./get-img-props").PlaceholderValue;
|
|
15
|
+
blurDataURL?: string;
|
|
16
|
+
onLoadingComplete?: (result: {
|
|
17
|
+
naturalWidth: number;
|
|
18
|
+
naturalHeight: number;
|
|
19
|
+
}) => void;
|
|
20
|
+
} & React.RefAttributes<HTMLImageElement>>;
|
|
6
21
|
export default Image;
|
package/dist/image/index.js
CHANGED
|
@@ -61,7 +61,7 @@ exports.Image = (0, react_1.forwardRef)((props, ref) => {
|
|
|
61
61
|
}
|
|
62
62
|
}, [onError, useDirectSrc]);
|
|
63
63
|
// Get processed img props
|
|
64
|
-
const imgProps = (0, get_img_props_1.getImgProps)({ ...restProps, priority, unoptimized: useDirectSrc || restProps.unoptimized }, image_config_1.
|
|
64
|
+
const imgProps = (0, get_img_props_1.getImgProps)({ ...restProps, priority, unoptimized: useDirectSrc || restProps.unoptimized }, (0, image_config_1.resolveRuntimeImageConfig)(), image_loader_1.defaultLoader);
|
|
65
65
|
const fallbackSrc = String(restProps.src || '');
|
|
66
66
|
const effectiveSrc = useDirectSrc ? fallbackSrc : imgProps.src;
|
|
67
67
|
const effectiveSrcSet = useDirectSrc ? undefined : imgProps.srcSet;
|
|
@@ -12,7 +12,7 @@ const image_loader_1 = require("./image-loader");
|
|
|
12
12
|
* react-server condition uses a plain SSR-friendly <img> wrapper.
|
|
13
13
|
*/
|
|
14
14
|
function Image(props) {
|
|
15
|
-
const imgProps = (0, get_img_props_1.getImgProps)(props, image_config_1.
|
|
15
|
+
const imgProps = (0, get_img_props_1.getImgProps)(props, (0, image_config_1.resolveRuntimeImageConfig)(), image_loader_1.defaultLoader);
|
|
16
16
|
return ((0, jsx_runtime_1.jsx)("img", { ...imgProps, decoding: props.priority ? 'sync' : 'async', fetchPriority: props.priority ? 'high' : undefined }));
|
|
17
17
|
}
|
|
18
18
|
exports.default = Image;
|
|
@@ -21,6 +21,7 @@ export declare function resolveParallelSlotMatches(input: {
|
|
|
21
21
|
layoutPath: string;
|
|
22
22
|
pathname: string;
|
|
23
23
|
}): ParallelSlotMatch[];
|
|
24
|
+
export declare function isPathInside(parent: string, child: string): boolean;
|
|
24
25
|
export declare function resolveDirectoryChain(rootDir: string, entryFilePath: string): string[];
|
|
25
26
|
export declare function resolveNearestSegmentNotFoundPath(appDir: string, startDir: string): string | null;
|
|
26
27
|
export {};
|
|
@@ -9,6 +9,7 @@ exports.isInterceptionRouteSegment = isInterceptionRouteSegment;
|
|
|
9
9
|
exports.isRouteGroupSegment = isRouteGroupSegment;
|
|
10
10
|
exports.isParallelRouteSegment = isParallelRouteSegment;
|
|
11
11
|
exports.resolveParallelSlotMatches = resolveParallelSlotMatches;
|
|
12
|
+
exports.isPathInside = isPathInside;
|
|
12
13
|
exports.resolveDirectoryChain = resolveDirectoryChain;
|
|
13
14
|
exports.resolveNearestSegmentNotFoundPath = resolveNearestSegmentNotFoundPath;
|
|
14
15
|
const fs_1 = __importDefault(require("fs"));
|
|
@@ -284,17 +285,41 @@ function resolveParallelSlotMatches(input) {
|
|
|
284
285
|
}
|
|
285
286
|
return matches;
|
|
286
287
|
}
|
|
288
|
+
function canonicalizeExistingPath(input) {
|
|
289
|
+
const resolved = path_1.default.resolve(input);
|
|
290
|
+
try {
|
|
291
|
+
return fs_1.default.realpathSync.native(resolved);
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
return resolved;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function posixPath(input) {
|
|
298
|
+
return input.replace(/\\/g, '/');
|
|
299
|
+
}
|
|
300
|
+
function isPathInside(parent, child) {
|
|
301
|
+
const root = posixPath(canonicalizeExistingPath(parent));
|
|
302
|
+
const target = posixPath(canonicalizeExistingPath(child));
|
|
303
|
+
const normalize = (value) => process.platform === 'win32' ? value.toLowerCase() : value;
|
|
304
|
+
const a = normalize(root);
|
|
305
|
+
const b = normalize(target);
|
|
306
|
+
return b === a || b.startsWith(a.endsWith('/') ? a : `${a}/`);
|
|
307
|
+
}
|
|
287
308
|
function resolveDirectoryChain(rootDir, entryFilePath) {
|
|
288
|
-
const resolvedRootDir =
|
|
289
|
-
const resolvedEntryDir =
|
|
290
|
-
if (!
|
|
309
|
+
const resolvedRootDir = canonicalizeExistingPath(rootDir);
|
|
310
|
+
const resolvedEntryDir = canonicalizeExistingPath(path_1.default.dirname(entryFilePath));
|
|
311
|
+
if (!isPathInside(resolvedRootDir, resolvedEntryDir)) {
|
|
291
312
|
return [resolvedEntryDir];
|
|
292
313
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
.
|
|
314
|
+
let relativePath = path_1.default.relative(resolvedRootDir, resolvedEntryDir).replace(/\\/g, '/');
|
|
315
|
+
if (relativePath.startsWith('..')) {
|
|
316
|
+
const rootPosix = posixPath(resolvedRootDir);
|
|
317
|
+
const entryPosix = posixPath(resolvedEntryDir);
|
|
318
|
+
const rootKey = process.platform === 'win32' ? rootPosix.toLowerCase() : rootPosix;
|
|
319
|
+
const entryKey = process.platform === 'win32' ? entryPosix.toLowerCase() : entryPosix;
|
|
320
|
+
relativePath = entryKey.slice(rootKey.length).replace(/^\//, '');
|
|
321
|
+
}
|
|
322
|
+
const relativeSegments = relativePath.split('/').filter((segment) => segment && segment !== '.');
|
|
298
323
|
const chain = [resolvedRootDir];
|
|
299
324
|
let currentDir = resolvedRootDir;
|
|
300
325
|
for (const segment of relativeSegments) {
|
|
@@ -306,7 +331,7 @@ function resolveDirectoryChain(rootDir, entryFilePath) {
|
|
|
306
331
|
function resolveNearestSegmentNotFoundPath(appDir, startDir) {
|
|
307
332
|
let currentDir = path_1.default.resolve(startDir);
|
|
308
333
|
const resolvedAppDir = path_1.default.resolve(appDir);
|
|
309
|
-
while (
|
|
334
|
+
while (isPathInside(resolvedAppDir, currentDir)) {
|
|
310
335
|
const notFoundPath = resolveConventionModule(currentDir, 'not-found');
|
|
311
336
|
if (notFoundPath) {
|
|
312
337
|
return notFoundPath;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function isBootstrapChunkFile(filename: string): boolean;
|
|
2
|
+
export declare function sortHydrationChunkFiles(files: string[]): string[];
|
|
3
|
+
/**
|
|
4
|
+
* Script tags that must be in the HTML document. Async `clientN-*.js`
|
|
5
|
+
* chunks are loaded by the webpack runtime, matching Next.js.
|
|
6
|
+
*/
|
|
7
|
+
export declare function listHydrationChunkFiles(cwd: string, isDev?: boolean): string[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isBootstrapChunkFile = isBootstrapChunkFile;
|
|
7
|
+
exports.sortHydrationChunkFiles = sortHydrationChunkFiles;
|
|
8
|
+
exports.listHydrationChunkFiles = listHydrationChunkFiles;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const constants_1 = require("../constants");
|
|
12
|
+
const BOOTSTRAP_CHUNK_RE = /^(webpack|framework|vendor|main|runtime)(?:-[a-z0-9]+)?\.js$/i;
|
|
13
|
+
const BOOTSTRAP_PRIORITY = ['webpack', 'framework', 'vendor', 'runtime', 'main'];
|
|
14
|
+
function isBootstrapChunkFile(filename) {
|
|
15
|
+
const base = filename.split(/[/\\]/).pop() || filename;
|
|
16
|
+
return BOOTSTRAP_CHUNK_RE.test(base);
|
|
17
|
+
}
|
|
18
|
+
function bootstrapRank(filename) {
|
|
19
|
+
const base = (filename.split(/[/\\]/).pop() || filename).replace(/(?:-[a-z0-9]+)?\.js$/i, '');
|
|
20
|
+
const index = BOOTSTRAP_PRIORITY.indexOf(base.toLowerCase());
|
|
21
|
+
return index === -1 ? BOOTSTRAP_PRIORITY.length : index;
|
|
22
|
+
}
|
|
23
|
+
function sortHydrationChunkFiles(files) {
|
|
24
|
+
return [...files].sort((a, b) => {
|
|
25
|
+
const rankDelta = bootstrapRank(a) - bootstrapRank(b);
|
|
26
|
+
if (rankDelta !== 0)
|
|
27
|
+
return rankDelta;
|
|
28
|
+
return a.localeCompare(b);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Script tags that must be in the HTML document. Async `clientN-*.js`
|
|
33
|
+
* chunks are loaded by the webpack runtime, matching Next.js.
|
|
34
|
+
*/
|
|
35
|
+
function listHydrationChunkFiles(cwd, isDev = false) {
|
|
36
|
+
const chunksDir = path_1.default.join(cwd, constants_1.BUILD_DIR, 'static', 'chunks');
|
|
37
|
+
if (!fs_1.default.existsSync(chunksDir))
|
|
38
|
+
return [];
|
|
39
|
+
const files = fs_1.default
|
|
40
|
+
.readdirSync(chunksDir)
|
|
41
|
+
.filter((name) => name.endsWith('.js') &&
|
|
42
|
+
!name.endsWith('.map') &&
|
|
43
|
+
!name.includes('.hot-update.') &&
|
|
44
|
+
isBootstrapChunkFile(name));
|
|
45
|
+
const normalizedFiles = isDev
|
|
46
|
+
? files.filter((name) => !/-[0-9a-f]{8,}\.js$/i.test(name))
|
|
47
|
+
: files;
|
|
48
|
+
return sortHydrationChunkFiles(normalizedFiles);
|
|
49
|
+
}
|
|
@@ -641,11 +641,22 @@ function transpileProjectSource(source, filename, fallback) {
|
|
|
641
641
|
return fallback;
|
|
642
642
|
}
|
|
643
643
|
}
|
|
644
|
+
function extraCompileRootsFromStandalone(cwd) {
|
|
645
|
+
const resolved = path_1.default.resolve(cwd);
|
|
646
|
+
const normalized = resolved.replace(/\\/g, '/');
|
|
647
|
+
const marker = '/.vista/standalone/project';
|
|
648
|
+
const idx = normalized.toLowerCase().lastIndexOf(marker);
|
|
649
|
+
if (idx === -1) {
|
|
650
|
+
return [];
|
|
651
|
+
}
|
|
652
|
+
return [path_1.default.resolve(resolved.slice(0, idx))];
|
|
653
|
+
}
|
|
644
654
|
function installModuleCompileHook(options) {
|
|
645
655
|
const vistaRuntimeDir = path_1.default.resolve(__dirname, '..');
|
|
646
656
|
const vistaPackageRoot = path_1.default.resolve(__dirname, '..', '..');
|
|
647
657
|
activeCompileRoots = Array.from(new Set([
|
|
648
658
|
options.cwd,
|
|
659
|
+
...extraCompileRootsFromStandalone(options.cwd),
|
|
649
660
|
vistaRuntimeDir,
|
|
650
661
|
vistaPackageRoot,
|
|
651
662
|
path_1.default.join(vistaPackageRoot, 'src'),
|
|
@@ -24,7 +24,7 @@ const server_1 = require("react-dom/server");
|
|
|
24
24
|
const webpack_dev_middleware_1 = __importDefault(require("webpack-dev-middleware"));
|
|
25
25
|
const stream_1 = require("stream");
|
|
26
26
|
const child_process_1 = require("child_process");
|
|
27
|
-
const
|
|
27
|
+
const ssr_webpack_shim_1 = require("./ssr-webpack-shim");
|
|
28
28
|
const middleware_runner_1 = require("./middleware-runner");
|
|
29
29
|
const image_optimizer_1 = require("./image-optimizer");
|
|
30
30
|
const ppr_1 = require("./ppr");
|
|
@@ -33,39 +33,10 @@ const logger_1 = require("./logger");
|
|
|
33
33
|
const not_found_page_1 = require("./not-found-page");
|
|
34
34
|
const static_cache_1 = require("./static-cache");
|
|
35
35
|
const static_generator_1 = require("./static-generator");
|
|
36
|
+
const hydration_chunks_1 = require("./hydration-chunks");
|
|
36
37
|
const react_client_reference_manifest_1 = require("../build/rsc/react-client-reference-manifest");
|
|
37
38
|
const constants_1 = require("../constants");
|
|
38
39
|
const CjsModule = require('module');
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
// SSR Webpack Shim
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
// The Flight SSR decoder (react-server-dom-webpack/client.node) calls
|
|
43
|
-
// __webpack_require__(specifier) to load client components during SSR.
|
|
44
|
-
// In Vista the SSR process runs in plain Node.js (not through webpack),
|
|
45
|
-
// so we provide a shim that converts file:// URLs to absolute paths and
|
|
46
|
-
// delegates to Node's require().
|
|
47
|
-
// ---------------------------------------------------------------------------
|
|
48
|
-
function installSSRWebpackShim() {
|
|
49
|
-
if (typeof globalThis.__webpack_require__ === 'function')
|
|
50
|
-
return;
|
|
51
|
-
globalThis.__webpack_require__ = function ssrWebpackRequire(specifier) {
|
|
52
|
-
let modulePath = specifier;
|
|
53
|
-
// Convert file:// URLs to absolute paths
|
|
54
|
-
if (specifier.startsWith('file://')) {
|
|
55
|
-
try {
|
|
56
|
-
modulePath = (0, url_1.fileURLToPath)(specifier);
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
modulePath = specifier;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return require(modulePath);
|
|
63
|
-
};
|
|
64
|
-
// Chunk loading is a no-op on the server — all code is already local.
|
|
65
|
-
globalThis.__webpack_chunk_load__ = function ssrChunkLoad(_chunkId) {
|
|
66
|
-
return Promise.resolve();
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
40
|
const config_1 = require("../config");
|
|
70
41
|
const dev_error_1 = require("../dev-error");
|
|
71
42
|
const artifact_validator_1 = require("./artifact-validator");
|
|
@@ -338,32 +309,7 @@ function cleanHotUpdateFiles(cwd) {
|
|
|
338
309
|
}
|
|
339
310
|
}
|
|
340
311
|
function findChunkFiles(cwd, isDev) {
|
|
341
|
-
|
|
342
|
-
if (!fs_1.default.existsSync(chunksDir))
|
|
343
|
-
return [];
|
|
344
|
-
const files = fs_1.default
|
|
345
|
-
.readdirSync(chunksDir)
|
|
346
|
-
.filter((name) => name.endsWith('.js') && !name.endsWith('.map') && !name.includes('.hot-update.'));
|
|
347
|
-
// In dev, avoid loading stale production artifacts left from a previous build.
|
|
348
|
-
// Production chunks end with a hash suffix like `main-1a2b3c4d.js`.
|
|
349
|
-
const normalizedFiles = isDev
|
|
350
|
-
? files.filter((name) => !/-[0-9a-f]{8,}\.js$/i.test(name))
|
|
351
|
-
: files;
|
|
352
|
-
// Load webpack runtime first, then framework, then the rest alphabetically.
|
|
353
|
-
// This ensures the chunk registry (__webpack_require__) is available before
|
|
354
|
-
// any deferred chunk tries to self-register.
|
|
355
|
-
const priority = ['webpack.js', 'framework.js', 'vendor.js'];
|
|
356
|
-
return normalizedFiles.sort((a, b) => {
|
|
357
|
-
const ai = priority.indexOf(a);
|
|
358
|
-
const bi = priority.indexOf(b);
|
|
359
|
-
if (ai !== -1 && bi !== -1)
|
|
360
|
-
return ai - bi;
|
|
361
|
-
if (ai !== -1)
|
|
362
|
-
return -1;
|
|
363
|
-
if (bi !== -1)
|
|
364
|
-
return 1;
|
|
365
|
-
return a.localeCompare(b);
|
|
366
|
-
});
|
|
312
|
+
return (0, hydration_chunks_1.listHydrationChunkFiles)(cwd, isDev);
|
|
367
313
|
}
|
|
368
314
|
function normalizeSSRManifest(manifest) {
|
|
369
315
|
if (!manifest || !manifest.moduleMap) {
|
|
@@ -616,9 +562,15 @@ async function renderAppSubtreeElement(input) {
|
|
|
616
562
|
});
|
|
617
563
|
const directoryChain = (0, app_router_runtime_1.resolveDirectoryChain)(input.subtreeRootDir, input.entryFilePath);
|
|
618
564
|
for (let i = directoryChain.length - 1; i >= 0; i--) {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
565
|
+
element = applySegmentBoundaries(directoryChain[i], element);
|
|
566
|
+
}
|
|
567
|
+
const layoutPaths = input.layoutPaths && input.layoutPaths.length > 0
|
|
568
|
+
? input.layoutPaths
|
|
569
|
+
: directoryChain
|
|
570
|
+
.map((dir) => (0, app_router_runtime_1.resolveConventionModule)(dir, 'root') ?? (0, app_router_runtime_1.resolveConventionModule)(dir, 'layout'))
|
|
571
|
+
.filter((layoutPath) => Boolean(layoutPath));
|
|
572
|
+
for (let i = layoutPaths.length - 1; i >= 0; i--) {
|
|
573
|
+
const layoutPath = layoutPaths[i];
|
|
622
574
|
if (!layoutPath || path_1.default.resolve(layoutPath) === path_1.default.resolve(input.entryFilePath)) {
|
|
623
575
|
continue;
|
|
624
576
|
}
|
|
@@ -682,6 +634,7 @@ async function createRouteElement(route, context, isDev, rootLayout, runtimeRoot
|
|
|
682
634
|
cwd: runtimeRoot,
|
|
683
635
|
evaluateLeafMetadata: false,
|
|
684
636
|
disableParallelSlots: options.disableParallelSlots,
|
|
637
|
+
layoutPaths: route.layoutPaths,
|
|
685
638
|
});
|
|
686
639
|
return { element, metadata, rootMode: rootLayout.mode };
|
|
687
640
|
}
|
|
@@ -1017,7 +970,7 @@ function startRSCServer(options = {}) {
|
|
|
1017
970
|
cacheComponentsEnabled: cacheComponentsConfig.enabled,
|
|
1018
971
|
});
|
|
1019
972
|
(0, fetch_policy_1.installSegmentFetchPolicyShim)();
|
|
1020
|
-
installSSRWebpackShim();
|
|
973
|
+
(0, ssr_webpack_shim_1.installSSRWebpackShim)();
|
|
1021
974
|
const serverManifestPath = path_1.default.join(cwd, constants_1.BUILD_DIR, 'server', 'server-manifest.json');
|
|
1022
975
|
if (!fs_1.default.existsSync(serverManifestPath)) {
|
|
1023
976
|
failRSCStartup(`[vista:rsc] Missing server manifest at ${serverManifestPath}. Run "vista build --rsc" first.`, shouldListen);
|
|
@@ -390,9 +390,15 @@ async function renderAppSubtreeElement(input) {
|
|
|
390
390
|
});
|
|
391
391
|
const directoryChain = (0, app_router_runtime_1.resolveDirectoryChain)(input.subtreeRootDir, input.entryFilePath);
|
|
392
392
|
for (let i = directoryChain.length - 1; i >= 0; i--) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
393
|
+
element = applySegmentBoundaries(directoryChain[i], element);
|
|
394
|
+
}
|
|
395
|
+
const layoutPaths = input.layoutPaths && input.layoutPaths.length > 0
|
|
396
|
+
? input.layoutPaths
|
|
397
|
+
: directoryChain
|
|
398
|
+
.map((dir) => (0, app_router_runtime_1.resolveConventionModule)(dir, 'root') ?? (0, app_router_runtime_1.resolveConventionModule)(dir, 'layout'))
|
|
399
|
+
.filter((layoutPath) => Boolean(layoutPath));
|
|
400
|
+
for (let i = layoutPaths.length - 1; i >= 0; i--) {
|
|
401
|
+
const layoutPath = layoutPaths[i];
|
|
396
402
|
if (!layoutPath || path_1.default.resolve(layoutPath) === path_1.default.resolve(input.entryFilePath)) {
|
|
397
403
|
continue;
|
|
398
404
|
}
|
|
@@ -447,6 +453,7 @@ async function createRouteElement(route, context, isDev, runtimeRoot, options =
|
|
|
447
453
|
cwd: runtimeRoot,
|
|
448
454
|
evaluateLeafMetadata: true,
|
|
449
455
|
disableParallelSlots: options.disableParallelSlots,
|
|
456
|
+
layoutPaths: route.layoutPaths,
|
|
450
457
|
});
|
|
451
458
|
}
|
|
452
459
|
async function readRawRequestBody(req) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-server-dom-webpack/client.node calls __webpack_require__ while decoding
|
|
3
|
+
* Flight into a React tree. Vista SSR/SSG run in plain Node, so this shim maps
|
|
4
|
+
* webpack specifiers (including file:// URLs) onto require().
|
|
5
|
+
*/
|
|
6
|
+
export declare function installSSRWebpackShim(): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installSSRWebpackShim = installSSRWebpackShim;
|
|
4
|
+
const url_1 = require("url");
|
|
5
|
+
/**
|
|
6
|
+
* react-server-dom-webpack/client.node calls __webpack_require__ while decoding
|
|
7
|
+
* Flight into a React tree. Vista SSR/SSG run in plain Node, so this shim maps
|
|
8
|
+
* webpack specifiers (including file:// URLs) onto require().
|
|
9
|
+
*/
|
|
10
|
+
function installSSRWebpackShim() {
|
|
11
|
+
if (typeof globalThis.__webpack_require__ === 'function') {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
globalThis.__webpack_require__ = function ssrWebpackRequire(specifier) {
|
|
15
|
+
let modulePath = specifier;
|
|
16
|
+
if (specifier.startsWith('file://')) {
|
|
17
|
+
try {
|
|
18
|
+
modulePath = (0, url_1.fileURLToPath)(specifier);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
modulePath = specifier;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return require(modulePath);
|
|
25
|
+
};
|
|
26
|
+
globalThis.__webpack_chunk_load__ = function ssrChunkLoad(_chunkId) {
|
|
27
|
+
return Promise.resolve();
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -34,6 +34,8 @@ export interface StaticGeneratorResult {
|
|
|
34
34
|
/** The prerender manifest */
|
|
35
35
|
manifest: PrerenderManifest;
|
|
36
36
|
}
|
|
37
|
+
/** Embed Flight on SSG HTML so the client can hydrate without a live /rsc server. */
|
|
38
|
+
export declare function injectInlineFlightBootstrap(html: string, flightText: string): string;
|
|
37
39
|
/**
|
|
38
40
|
* Run static generation for all eligible routes.
|
|
39
41
|
*/
|