@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.
Files changed (35) hide show
  1. package/dist/bin/build-rsc.js +18 -0
  2. package/dist/bin/build.js +10 -0
  3. package/dist/bin/webpack.config.d.ts +3 -1
  4. package/dist/bin/webpack.config.js +3 -1
  5. package/dist/build/rsc/compiler.d.ts +3 -1
  6. package/dist/build/rsc/compiler.js +7 -3
  7. package/dist/build/rsc/react-client-reference-manifest.d.ts +7 -0
  8. package/dist/build/rsc/react-client-reference-manifest.js +132 -6
  9. package/dist/client/link.js +1 -1
  10. package/dist/client/navigation.js +2 -2
  11. package/dist/client/rsc-router.d.ts +1 -3
  12. package/dist/client/rsc-router.js +98 -45
  13. package/dist/deploy/adapters/cloudflare.js +2 -25
  14. package/dist/deploy/adapters/netlify.js +2 -14
  15. package/dist/deploy/utils.d.ts +6 -0
  16. package/dist/deploy/utils.js +64 -1
  17. package/dist/image/get-img-props.js +21 -11
  18. package/dist/image/image-config.d.ts +2 -0
  19. package/dist/image/image-config.js +17 -0
  20. package/dist/image/index.d.ts +18 -3
  21. package/dist/image/index.js +1 -1
  22. package/dist/image/react-server.d.ts +1 -2
  23. package/dist/image/react-server.js +1 -1
  24. package/dist/server/app-router-runtime.d.ts +1 -0
  25. package/dist/server/app-router-runtime.js +34 -9
  26. package/dist/server/hydration-chunks.d.ts +7 -0
  27. package/dist/server/hydration-chunks.js +49 -0
  28. package/dist/server/module-compile-hook.js +11 -0
  29. package/dist/server/rsc-engine.js +14 -61
  30. package/dist/server/rsc-upstream.js +10 -3
  31. package/dist/server/ssr-webpack-shim.d.ts +6 -0
  32. package/dist/server/ssr-webpack-shim.js +29 -0
  33. package/dist/server/static-generator.d.ts +2 -0
  34. package/dist/server/static-generator.js +29 -29
  35. 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
- const redirectsPath = writeStaticRedirects(outputDir);
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, redirectsPath, netlifyTomlPath],
79
+ artifactPaths: [outputDir, netlifyTomlPath],
92
80
  instructions: ['Static mode: Netlify serves pre-rendered pages only.'],
93
81
  };
94
82
  }
@@ -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;
@@ -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, width, loader, config, unoptimized) {
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
- let widthInt = width ? Number(width) : undefined;
33
- let heightInt = height ? Number(height) : undefined;
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
- // Disable optimizer when explicitly requested, configured globally,
37
- // or when building for Vercel static output (no /_vista/image endpoint).
38
- const disableOptimization = !!unoptimized || !!config.unoptimized || vercelStaticBuild;
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'];
@@ -1,6 +1,21 @@
1
1
  import React from 'react';
2
2
  import { ImageProps } from './get-img-props';
3
- export interface EnhancedImageProps extends ImageProps {
4
- }
5
- export declare const Image: React.ForwardRefExoticComponent<EnhancedImageProps & React.RefAttributes<HTMLImageElement>>;
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;
@@ -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.imageConfigDefault, image_loader_1.defaultLoader);
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;
@@ -1,7 +1,6 @@
1
1
  import * as React from 'react';
2
2
  import { type ImageProps } from './get-img-props';
3
- export interface EnhancedImageProps extends ImageProps {
4
- }
3
+ export type EnhancedImageProps = ImageProps;
5
4
  /**
6
5
  * React-server safe Image component.
7
6
  *
@@ -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.imageConfigDefault, image_loader_1.defaultLoader);
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 = path_1.default.resolve(rootDir);
289
- const resolvedEntryDir = path_1.default.resolve(path_1.default.dirname(entryFilePath));
290
- if (!resolvedEntryDir.startsWith(resolvedRootDir)) {
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
- const relativeSegments = path_1.default
294
- .relative(resolvedRootDir, resolvedEntryDir)
295
- .replace(/\\/g, '/')
296
- .split('/')
297
- .filter(Boolean);
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 (currentDir.startsWith(resolvedAppDir)) {
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 url_1 = require("url");
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
- const chunksDir = path_1.default.join(cwd, constants_1.BUILD_DIR, 'static', 'chunks');
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
- const dir = directoryChain[i];
620
- element = applySegmentBoundaries(dir, element);
621
- const layoutPath = (0, app_router_runtime_1.resolveConventionModule)(dir, 'root') ?? (0, app_router_runtime_1.resolveConventionModule)(dir, 'layout');
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
- const dir = directoryChain[i];
394
- element = applySegmentBoundaries(dir, element);
395
- const layoutPath = (0, app_router_runtime_1.resolveConventionModule)(dir, 'root') ?? (0, app_router_runtime_1.resolveConventionModule)(dir, 'layout');
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
  */