@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
package/dist/bin/build-rsc.js
CHANGED
|
@@ -244,6 +244,14 @@ waitForInlineFlightPayload(4000).then(function (inlineFlight) {
|
|
|
244
244
|
return;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
if (/(?:^|\\n)\\d+:E\\{/.test(inlineFlight)) {
|
|
248
|
+
reportDevRuntimeError(
|
|
249
|
+
'Hydration Error',
|
|
250
|
+
new Error('Inline Flight payload contains error rows. Keeping server HTML instead of hydrating the document.')
|
|
251
|
+
);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
247
255
|
const initialResponse = createFromReadableStream(flightTextToReadableStream(inlineFlight), {
|
|
248
256
|
callServer,
|
|
249
257
|
}) as Promise<React.ReactNode>;
|
|
@@ -365,6 +373,14 @@ async function buildRSC(watch = false) {
|
|
|
365
373
|
const engineVariant = (0, config_1.resolveAndApplyEngineVariant)(vistaConfig);
|
|
366
374
|
const structureConfig = (0, config_1.resolveStructureValidationConfig)(vistaConfig);
|
|
367
375
|
const cacheComponentsConfig = (0, config_1.resolveCacheComponentsConfig)(vistaConfig);
|
|
376
|
+
const deployConfig = (0, config_1.resolveDeployConfig)(vistaConfig);
|
|
377
|
+
const imagesUnoptimized = vistaConfig.images?.unoptimized === true || deployConfig.output === 'static';
|
|
378
|
+
if (imagesUnoptimized) {
|
|
379
|
+
process.env.VISTA_IMAGES_UNOPTIMIZED = '1';
|
|
380
|
+
}
|
|
381
|
+
if (deployConfig.output) {
|
|
382
|
+
process.env.VISTA_DEPLOY_OUTPUT = deployConfig.output;
|
|
383
|
+
}
|
|
368
384
|
if (_debug)
|
|
369
385
|
console.log(`[vista:build] Engine variant: ${engineVariant}`);
|
|
370
386
|
if (structureConfig.enabled) {
|
|
@@ -487,6 +503,8 @@ async function buildRSC(watch = false) {
|
|
|
487
503
|
buildId,
|
|
488
504
|
engineVariant,
|
|
489
505
|
clientReferenceFiles,
|
|
506
|
+
imagesUnoptimized,
|
|
507
|
+
deployOutput: deployConfig.output,
|
|
490
508
|
};
|
|
491
509
|
// Build CSS
|
|
492
510
|
runPostCSS(cwd, vistaDirs.root);
|
package/dist/bin/build.js
CHANGED
|
@@ -279,6 +279,14 @@ async function buildClient(watch = false, onRebuild) {
|
|
|
279
279
|
const engineVariant = (0, config_1.resolveAndApplyEngineVariant)(vistaConfig);
|
|
280
280
|
const structureConfig = (0, config_1.resolveStructureValidationConfig)(vistaConfig);
|
|
281
281
|
const cacheComponentsConfig = (0, config_1.resolveCacheComponentsConfig)(vistaConfig);
|
|
282
|
+
const deployConfig = (0, config_1.resolveDeployConfig)(vistaConfig);
|
|
283
|
+
const imagesUnoptimized = vistaConfig.images?.unoptimized === true || deployConfig.output === 'static';
|
|
284
|
+
if (imagesUnoptimized) {
|
|
285
|
+
process.env.VISTA_IMAGES_UNOPTIMIZED = '1';
|
|
286
|
+
}
|
|
287
|
+
if (deployConfig.output) {
|
|
288
|
+
process.env.VISTA_DEPLOY_OUTPUT = deployConfig.output;
|
|
289
|
+
}
|
|
282
290
|
if (_debug)
|
|
283
291
|
console.log(`[vista:build] Engine variant: ${engineVariant}`);
|
|
284
292
|
if (structureConfig.enabled) {
|
|
@@ -383,6 +391,8 @@ async function buildClient(watch = false, onRebuild) {
|
|
|
383
391
|
isDev: watch,
|
|
384
392
|
engineVariant,
|
|
385
393
|
cacheComponentsEnabled: cacheComponentsConfig.enabled,
|
|
394
|
+
imagesUnoptimized,
|
|
395
|
+
deployOutput: deployConfig.output,
|
|
386
396
|
});
|
|
387
397
|
// Create Webpack compiler
|
|
388
398
|
exports.compiler = compiler = (0, webpack_1.default)(config);
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import webpack from 'webpack';
|
|
2
|
-
import type { VistaEngineVariant } from '../config';
|
|
2
|
+
import type { DeployOutput, VistaEngineVariant } from '../config';
|
|
3
3
|
export interface WebpackConfigOptions {
|
|
4
4
|
cwd: string;
|
|
5
5
|
isDev: boolean;
|
|
6
6
|
engineVariant?: VistaEngineVariant;
|
|
7
7
|
cacheComponentsEnabled?: boolean;
|
|
8
|
+
imagesUnoptimized?: boolean;
|
|
9
|
+
deployOutput?: DeployOutput;
|
|
8
10
|
}
|
|
9
11
|
export declare function createWebpackConfig(options: WebpackConfigOptions): webpack.Configuration;
|
|
@@ -13,7 +13,7 @@ const vista_flight_plugin_1 = require("../build/webpack/plugins/vista-flight-plu
|
|
|
13
13
|
const constants_1 = require("../constants");
|
|
14
14
|
const app_dir_1 = require("../server/app-dir");
|
|
15
15
|
function createWebpackConfig(options) {
|
|
16
|
-
const { cwd, isDev, engineVariant = 'default', cacheComponentsEnabled = false } = options;
|
|
16
|
+
const { cwd, isDev, engineVariant = 'default', cacheComponentsEnabled = false, imagesUnoptimized = false, deployOutput, } = options;
|
|
17
17
|
const vistaDir = path_1.default.join(cwd, constants_1.BUILD_DIR);
|
|
18
18
|
const flashDir = path_1.default.join(cwd, constants_1.FLASH_DIR);
|
|
19
19
|
const entryPoint = path_1.default.join(vistaDir, 'client.tsx');
|
|
@@ -166,6 +166,8 @@ function createWebpackConfig(options) {
|
|
|
166
166
|
'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
|
|
167
167
|
'process.env.VISTA_ENGINE': JSON.stringify(engineVariant),
|
|
168
168
|
'process.env.VISTA_ENGINE_VARIANT': JSON.stringify(engineVariant),
|
|
169
|
+
'process.env.VISTA_IMAGES_UNOPTIMIZED': JSON.stringify(imagesUnoptimized ? '1' : ''),
|
|
170
|
+
'process.env.VISTA_DEPLOY_OUTPUT': JSON.stringify(deployOutput || ''),
|
|
169
171
|
}),
|
|
170
172
|
new mini_css_extract_plugin_1.default({
|
|
171
173
|
filename: isDev ? 'modules.css' : 'modules-[contenthash:8].css',
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import webpack from 'webpack';
|
|
9
9
|
import { VistaDirs } from '../manifest';
|
|
10
|
-
import type { VistaEngineVariant } from '../../config';
|
|
10
|
+
import type { DeployOutput, VistaEngineVariant } from '../../config';
|
|
11
11
|
export interface RSCCompilerOptions {
|
|
12
12
|
cwd: string;
|
|
13
13
|
isDev: boolean;
|
|
@@ -15,6 +15,8 @@ export interface RSCCompilerOptions {
|
|
|
15
15
|
buildId: string;
|
|
16
16
|
engineVariant?: VistaEngineVariant;
|
|
17
17
|
clientReferenceFiles?: string[];
|
|
18
|
+
imagesUnoptimized?: boolean;
|
|
19
|
+
deployOutput?: DeployOutput;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Create Server-Side Webpack Configuration
|
|
@@ -61,7 +61,7 @@ function resolveFromWorkspace(specifier, cwd) {
|
|
|
61
61
|
* Output goes to .vista/server/ and is NEVER sent to the client.
|
|
62
62
|
*/
|
|
63
63
|
function createServerWebpackConfig(options) {
|
|
64
|
-
const { cwd, isDev, vistaDirs, buildId, engineVariant = 'default' } = options;
|
|
64
|
+
const { cwd, isDev, vistaDirs, buildId, engineVariant = 'default', imagesUnoptimized = false, deployOutput, } = options;
|
|
65
65
|
const swcLoaderPath = resolveFromWorkspace('swc-loader', cwd);
|
|
66
66
|
const nullLoaderPath = resolveFromWorkspace('null-loader', cwd);
|
|
67
67
|
const cssLoaderPath = resolveFromWorkspace('css-loader', cwd);
|
|
@@ -181,6 +181,8 @@ function createServerWebpackConfig(options) {
|
|
|
181
181
|
'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
|
|
182
182
|
'process.env.VISTA_ENGINE': JSON.stringify(engineVariant),
|
|
183
183
|
'process.env.VISTA_ENGINE_VARIANT': JSON.stringify(engineVariant),
|
|
184
|
+
'process.env.VISTA_IMAGES_UNOPTIMIZED': JSON.stringify(imagesUnoptimized ? '1' : ''),
|
|
185
|
+
'process.env.VISTA_DEPLOY_OUTPUT': JSON.stringify(deployOutput || ''),
|
|
184
186
|
[constants_1.BUILD_ID_DEFINE]: JSON.stringify(buildId),
|
|
185
187
|
[constants_1.SERVER_DEFINE]: 'true',
|
|
186
188
|
}),
|
|
@@ -196,7 +198,7 @@ function createServerWebpackConfig(options) {
|
|
|
196
198
|
* Server components are replaced with client references.
|
|
197
199
|
*/
|
|
198
200
|
function createClientWebpackConfig(options) {
|
|
199
|
-
const { cwd, isDev, vistaDirs, buildId, engineVariant = 'default', clientReferenceFiles = [] } = options;
|
|
201
|
+
const { cwd, isDev, vistaDirs, buildId, engineVariant = 'default', clientReferenceFiles = [], imagesUnoptimized = false, deployOutput, } = options;
|
|
200
202
|
const swcLoaderPath = resolveFromWorkspace('swc-loader', cwd);
|
|
201
203
|
const nullLoaderPath = resolveFromWorkspace('null-loader', cwd);
|
|
202
204
|
const cssLoaderPath = resolveFromWorkspace('css-loader', cwd);
|
|
@@ -222,7 +224,7 @@ function createClientWebpackConfig(options) {
|
|
|
222
224
|
entry: clientEntry,
|
|
223
225
|
output: {
|
|
224
226
|
path: vistaDirs.chunks,
|
|
225
|
-
filename: isDev ? '[name].js' : '
|
|
227
|
+
filename: isDev ? '[name].js' : '[name]-[contenthash:8].js',
|
|
226
228
|
chunkFilename: isDev ? '[name].js' : '[name]-[contenthash:8].js',
|
|
227
229
|
publicPath: constants_1.STATIC_CHUNKS_PATH,
|
|
228
230
|
clean: !isDev,
|
|
@@ -405,6 +407,8 @@ function createClientWebpackConfig(options) {
|
|
|
405
407
|
'process.env.NODE_ENV': JSON.stringify(isDev ? 'development' : 'production'),
|
|
406
408
|
'process.env.VISTA_ENGINE': JSON.stringify(engineVariant),
|
|
407
409
|
'process.env.VISTA_ENGINE_VARIANT': JSON.stringify(engineVariant),
|
|
410
|
+
'process.env.VISTA_IMAGES_UNOPTIMIZED': JSON.stringify(imagesUnoptimized ? '1' : ''),
|
|
411
|
+
'process.env.VISTA_DEPLOY_OUTPUT': JSON.stringify(deployOutput || ''),
|
|
408
412
|
[constants_1.BUILD_ID_DEFINE]: JSON.stringify(buildId),
|
|
409
413
|
[constants_1.SERVER_DEFINE]: 'false',
|
|
410
414
|
}),
|
|
@@ -18,6 +18,13 @@ export interface ReactServerConsumerManifest {
|
|
|
18
18
|
moduleMap?: Record<string, Record<string, ReactServerConsumerManifestEntry>>;
|
|
19
19
|
serverModuleMap?: Record<string, unknown>;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Flight encode looks up `file://...#ExportName`. Webpack may have keyed the
|
|
23
|
+
* same module under a standalone copy, a different drive-letter case, or a
|
|
24
|
+
* slightly different absolute prefix. Resolve those aliases at lookup time so
|
|
25
|
+
* missing keys do not serialize as Flight `E{"digest":""}` rows.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveReactClientManifestEntry(manifest: ReactClientReferenceManifest, key: string): ReactClientReferenceManifestEntry | undefined;
|
|
21
28
|
export declare function normalizeReactClientReferenceManifest(input: ReactClientReferenceManifest): ReactClientReferenceManifest;
|
|
22
29
|
export declare function normalizeReactServerConsumerManifest(input: ReactServerConsumerManifest): ReactServerConsumerManifest;
|
|
23
30
|
/**
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveReactClientManifestEntry = resolveReactClientManifestEntry;
|
|
6
7
|
exports.normalizeReactClientReferenceManifest = normalizeReactClientReferenceManifest;
|
|
7
8
|
exports.normalizeReactServerConsumerManifest = normalizeReactServerConsumerManifest;
|
|
8
9
|
exports.createGuardedReactClientManifest = createGuardedReactClientManifest;
|
|
@@ -73,19 +74,36 @@ function addDriveLetterVariants(specifier, variants) {
|
|
|
73
74
|
variants.add(specifier.replace(/^file:\/\/\/([A-Za-z]):/, `file:///${lowerDrive}:`));
|
|
74
75
|
variants.add(specifier.replace(/^file:\/\/\/([A-Za-z]):/, `file:///${upperDrive}:`));
|
|
75
76
|
}
|
|
77
|
+
function addStandalonePathVariants(specifier, variants) {
|
|
78
|
+
addDriveLetterVariants(specifier, variants);
|
|
79
|
+
const standaloneProject = '/.vista/standalone/project';
|
|
80
|
+
const standaloneRuntime = '/.vista/standalone/runtime';
|
|
81
|
+
if (specifier.includes(standaloneProject)) {
|
|
82
|
+
addDriveLetterVariants(specifier.split(standaloneProject).join(''), variants);
|
|
83
|
+
}
|
|
84
|
+
if (specifier.includes(standaloneRuntime)) {
|
|
85
|
+
addDriveLetterVariants(specifier.split(standaloneRuntime).join(''), variants);
|
|
86
|
+
}
|
|
87
|
+
if (!specifier.includes('/.vista/standalone/') && specifier.startsWith('file://')) {
|
|
88
|
+
const inserted = specifier.replace(/^(file:\/\/\/(?:[A-Za-z]:)?(?:\/[^/]+)*)(\/(?:app|components|utils|lib|src|content|hooks)\/)/i, `$1${standaloneProject}$2`);
|
|
89
|
+
if (inserted !== specifier) {
|
|
90
|
+
addDriveLetterVariants(inserted, variants);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
76
94
|
function buildSpecifierVariants(specifier) {
|
|
77
95
|
const baseSpecifier = specifier.split('#', 1)[0];
|
|
78
96
|
const variants = new Set();
|
|
79
|
-
|
|
97
|
+
addStandalonePathVariants(baseSpecifier, variants);
|
|
80
98
|
if (baseSpecifier.startsWith('file://')) {
|
|
81
99
|
try {
|
|
82
|
-
|
|
100
|
+
addStandalonePathVariants(decodeURI(baseSpecifier), variants);
|
|
83
101
|
}
|
|
84
102
|
catch {
|
|
85
103
|
// ignore decode failures
|
|
86
104
|
}
|
|
87
105
|
try {
|
|
88
|
-
|
|
106
|
+
addStandalonePathVariants((0, url_1.pathToFileURL)((0, url_1.fileURLToPath)(baseSpecifier)).toString(), variants);
|
|
89
107
|
}
|
|
90
108
|
catch {
|
|
91
109
|
// ignore encode failures
|
|
@@ -93,6 +111,106 @@ function buildSpecifierVariants(specifier) {
|
|
|
93
111
|
}
|
|
94
112
|
return Array.from(variants);
|
|
95
113
|
}
|
|
114
|
+
function splitManifestKey(key) {
|
|
115
|
+
const hashIdx = key.lastIndexOf('#');
|
|
116
|
+
if (hashIdx <= 0) {
|
|
117
|
+
return { specifier: key, exportName: null };
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
specifier: key.slice(0, hashIdx),
|
|
121
|
+
exportName: key.slice(hashIdx + 1),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function normalizeManifestSpecifier(specifier) {
|
|
125
|
+
return specifier
|
|
126
|
+
.replace(/\\/g, '/')
|
|
127
|
+
.replace(/^file:\/\//i, '')
|
|
128
|
+
.replace(/^\/+/, '')
|
|
129
|
+
.replace(/\/\.vista\/standalone\/(?:project|runtime)/gi, '')
|
|
130
|
+
.toLowerCase();
|
|
131
|
+
}
|
|
132
|
+
function relativeClientPath(normalized) {
|
|
133
|
+
const markers = [
|
|
134
|
+
'/app/',
|
|
135
|
+
'/components/',
|
|
136
|
+
'/utils/',
|
|
137
|
+
'/lib/',
|
|
138
|
+
'/src/',
|
|
139
|
+
'/content/',
|
|
140
|
+
'/hooks/',
|
|
141
|
+
'/packages/vista/',
|
|
142
|
+
];
|
|
143
|
+
let best = normalized;
|
|
144
|
+
for (const marker of markers) {
|
|
145
|
+
const idx = normalized.lastIndexOf(marker);
|
|
146
|
+
if (idx !== -1) {
|
|
147
|
+
const sliced = normalized.slice(idx);
|
|
148
|
+
if (sliced.length <= best.length) {
|
|
149
|
+
best = sliced;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return best.replace(/\.(tsx|ts|jsx|js|mjs|cjs)$/i, '');
|
|
154
|
+
}
|
|
155
|
+
function exportNamesCompatible(requested, existing) {
|
|
156
|
+
if (requested === null || existing === null)
|
|
157
|
+
return true;
|
|
158
|
+
if (requested === existing)
|
|
159
|
+
return true;
|
|
160
|
+
const aliases = new Set(['', '*', 'default']);
|
|
161
|
+
return aliases.has(requested) && aliases.has(existing);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Flight encode looks up `file://...#ExportName`. Webpack may have keyed the
|
|
165
|
+
* same module under a standalone copy, a different drive-letter case, or a
|
|
166
|
+
* slightly different absolute prefix. Resolve those aliases at lookup time so
|
|
167
|
+
* missing keys do not serialize as Flight `E{"digest":""}` rows.
|
|
168
|
+
*/
|
|
169
|
+
function resolveReactClientManifestEntry(manifest, key) {
|
|
170
|
+
if (Object.prototype.hasOwnProperty.call(manifest, key)) {
|
|
171
|
+
return manifest[key];
|
|
172
|
+
}
|
|
173
|
+
const { specifier, exportName } = splitManifestKey(key);
|
|
174
|
+
const candidates = [];
|
|
175
|
+
for (const variant of buildSpecifierVariants(specifier)) {
|
|
176
|
+
candidates.push(variant);
|
|
177
|
+
if (exportName !== null) {
|
|
178
|
+
candidates.push(`${variant}#${exportName}`);
|
|
179
|
+
candidates.push(`${variant}#`);
|
|
180
|
+
candidates.push(`${variant}#default`);
|
|
181
|
+
candidates.push(`${variant}#*`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
for (const candidate of candidates) {
|
|
185
|
+
if (Object.prototype.hasOwnProperty.call(manifest, candidate)) {
|
|
186
|
+
return manifest[candidate];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const needle = normalizeManifestSpecifier(specifier);
|
|
190
|
+
const needleFile = needle.split('/').pop() || needle;
|
|
191
|
+
let fuzzy;
|
|
192
|
+
for (const [existingKey, entry] of Object.entries(manifest)) {
|
|
193
|
+
if (!entry || typeof entry !== 'object')
|
|
194
|
+
continue;
|
|
195
|
+
const existing = splitManifestKey(existingKey);
|
|
196
|
+
const existingNorm = normalizeManifestSpecifier(existing.specifier);
|
|
197
|
+
const existingFile = existingNorm.split('/').pop() || existingNorm;
|
|
198
|
+
const existingStem = existingFile.replace(/\.(tsx|ts|jsx|js|mjs|cjs)$/i, '');
|
|
199
|
+
const needleStem = needleFile.replace(/\.(tsx|ts|jsx|js|mjs|cjs)$/i, '');
|
|
200
|
+
const genericStem = existingStem === 'index' || existingStem === 'page' || existingStem === 'layout';
|
|
201
|
+
const pathMatches = existingNorm === needle ||
|
|
202
|
+
relativeClientPath(existingNorm) === relativeClientPath(needle) ||
|
|
203
|
+
(Boolean(needleFile) && !genericStem && existingStem === needleStem);
|
|
204
|
+
if (!pathMatches || !exportNamesCompatible(exportName, existing.exportName)) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (existing.exportName === exportName) {
|
|
208
|
+
return entry;
|
|
209
|
+
}
|
|
210
|
+
fuzzy = entry;
|
|
211
|
+
}
|
|
212
|
+
return fuzzy;
|
|
213
|
+
}
|
|
96
214
|
function createAliasedEntry(entry, exportName) {
|
|
97
215
|
if (exportName === '') {
|
|
98
216
|
return {
|
|
@@ -226,9 +344,11 @@ function normalizeReactServerConsumerManifest(input) {
|
|
|
226
344
|
function createGuardedReactClientManifest(manifest) {
|
|
227
345
|
return new Proxy(manifest, {
|
|
228
346
|
get(target, prop, receiver) {
|
|
229
|
-
if (typeof prop === 'string' &&
|
|
230
|
-
(
|
|
231
|
-
|
|
347
|
+
if (typeof prop === 'string' && (prop.startsWith('file:') || prop.includes('#'))) {
|
|
348
|
+
const resolved = resolveReactClientManifestEntry(target, prop);
|
|
349
|
+
if (resolved) {
|
|
350
|
+
return resolved;
|
|
351
|
+
}
|
|
232
352
|
const fileHint = prop.split('#')[0];
|
|
233
353
|
throw new Error(`Could not find the module "${prop}" in the React Client Manifest. ` +
|
|
234
354
|
`Client Components need a 'use client' directive and must live under a scanned project directory ` +
|
|
@@ -236,5 +356,11 @@ function createGuardedReactClientManifest(manifest) {
|
|
|
236
356
|
}
|
|
237
357
|
return Reflect.get(target, prop, receiver);
|
|
238
358
|
},
|
|
359
|
+
has(target, prop) {
|
|
360
|
+
if (typeof prop === 'string' && (prop.startsWith('file:') || prop.includes('#'))) {
|
|
361
|
+
return Boolean(resolveReactClientManifestEntry(target, prop));
|
|
362
|
+
}
|
|
363
|
+
return Reflect.has(target, prop);
|
|
364
|
+
},
|
|
239
365
|
});
|
|
240
366
|
}
|
package/dist/client/link.js
CHANGED
|
@@ -118,7 +118,7 @@ function resolvePrefetchBehavior(prefetch) {
|
|
|
118
118
|
exports.Link = react_1.default.forwardRef(({ href, as, replace, scroll = true, shallow, passHref, prefetch = 'auto', legacyBehavior, children, onClick, onMouseEnter, onTouchStart, onNavigate, target, ...props }, ref) => {
|
|
119
119
|
// Try the RSC router first — if we're inside an RSCRouter, use
|
|
120
120
|
// Flight-based navigation. Otherwise fall back to the legacy router.
|
|
121
|
-
const rscRouter = (0,
|
|
121
|
+
const rscRouter = (0, rsc_router_1.useRSCRouter)();
|
|
122
122
|
const legacyRouter = (0, react_1.useContext)(router_1.RouterContext);
|
|
123
123
|
const fallbackPathname = (0, router_1.usePathname)();
|
|
124
124
|
const pathname = rscRouter?.pathname ?? legacyRouter?.pathname ?? fallbackPathname;
|
|
@@ -59,7 +59,7 @@ const rsc_router_1 = require("./rsc-router");
|
|
|
59
59
|
* to popstate events.
|
|
60
60
|
*/
|
|
61
61
|
function usePathname() {
|
|
62
|
-
const rscCtx =
|
|
62
|
+
const rscCtx = (0, rsc_router_1.useRSCRouter)();
|
|
63
63
|
if (rscCtx)
|
|
64
64
|
return rscCtx.pathname;
|
|
65
65
|
const [pathname, setPathname] = React.useState(() => typeof window !== 'undefined' ? window.location.pathname : '/');
|
|
@@ -77,7 +77,7 @@ function usePathname() {
|
|
|
77
77
|
* Uses the RSC router context when available.
|
|
78
78
|
*/
|
|
79
79
|
function useSearchParams() {
|
|
80
|
-
const rscCtx =
|
|
80
|
+
const rscCtx = (0, rsc_router_1.useRSCRouter)();
|
|
81
81
|
if (rscCtx)
|
|
82
82
|
return rscCtx.searchParams;
|
|
83
83
|
const [searchParams, setSearchParams] = React.useState(() => typeof window !== 'undefined'
|
|
@@ -57,9 +57,7 @@ declare global {
|
|
|
57
57
|
at: number;
|
|
58
58
|
};
|
|
59
59
|
};
|
|
60
|
-
__VISTA_RSC_ROUTER__?: {
|
|
61
|
-
refresh: () => void;
|
|
62
|
-
prefetch: (url: string) => void;
|
|
60
|
+
__VISTA_RSC_ROUTER__?: RSCNavigationState & {
|
|
63
61
|
resume: (url: string) => void;
|
|
64
62
|
getState: () => {
|
|
65
63
|
pathname: string;
|
|
@@ -121,14 +121,36 @@ function getCallServer() {
|
|
|
121
121
|
}
|
|
122
122
|
return _callServer;
|
|
123
123
|
}
|
|
124
|
-
function
|
|
124
|
+
function flightRequestUrl(pathname, search) {
|
|
125
|
+
const normalized = pathname === '/' ? '' : pathname.replace(/\/$/, '');
|
|
126
|
+
// Static CDNs rewrite `/rsc/docs` → `/rsc/docs.rsc`. Request the extensionless
|
|
127
|
+
// URL so that rewrite is not applied twice (`/rsc/docs.rsc` → `/rsc/docs.rsc.rsc`).
|
|
128
|
+
return `/rsc${normalized}${search}`;
|
|
129
|
+
}
|
|
130
|
+
function hardNavigate(url) {
|
|
131
|
+
if (typeof window === 'undefined')
|
|
132
|
+
return;
|
|
133
|
+
window.location.assign(url);
|
|
134
|
+
}
|
|
135
|
+
function fetchFlight(pathname, search, options = {}) {
|
|
125
136
|
const key = cacheKey(pathname, search);
|
|
126
137
|
const cached = flightCache.get(key);
|
|
127
138
|
if (cached)
|
|
128
139
|
return cached;
|
|
129
140
|
const create = getCreateFromFetch();
|
|
130
|
-
const
|
|
141
|
+
const requestUrl = flightRequestUrl(pathname, search);
|
|
142
|
+
const hardFallback = options.hardFallback !== false;
|
|
143
|
+
const thenable = create(fetch(requestUrl, {
|
|
131
144
|
headers: { Accept: 'text/x-component' },
|
|
145
|
+
}).then((response) => {
|
|
146
|
+
const contentType = response.headers.get('content-type') || '';
|
|
147
|
+
if (!response.ok || contentType.includes('text/html')) {
|
|
148
|
+
if (hardFallback) {
|
|
149
|
+
hardNavigate(`${pathname}${search}`);
|
|
150
|
+
}
|
|
151
|
+
throw new Error(`Flight request failed (${response.status}): ${requestUrl}`);
|
|
152
|
+
}
|
|
153
|
+
return response;
|
|
132
154
|
}), { callServer: getCallServer() });
|
|
133
155
|
// Evict oldest if over limit
|
|
134
156
|
if (flightCache.size >= CACHE_MAX) {
|
|
@@ -148,8 +170,7 @@ function prefetchFlight(pathname, search) {
|
|
|
148
170
|
const key = cacheKey(pathname, search);
|
|
149
171
|
if (flightCache.has(key))
|
|
150
172
|
return;
|
|
151
|
-
|
|
152
|
-
fetchFlight(pathname, search);
|
|
173
|
+
fetchFlight(pathname, search, { hardFallback: false });
|
|
153
174
|
}
|
|
154
175
|
// ---------------------------------------------------------------------------
|
|
155
176
|
// RSCRoot — reads the current Flight thenable
|
|
@@ -157,6 +178,26 @@ function prefetchFlight(pathname, search) {
|
|
|
157
178
|
function RSCRoot({ response }) {
|
|
158
179
|
return React.use(response);
|
|
159
180
|
}
|
|
181
|
+
class FlightNavigationErrorBoundary extends React.Component {
|
|
182
|
+
state = { failed: false };
|
|
183
|
+
static getDerivedStateFromError() {
|
|
184
|
+
return { failed: true };
|
|
185
|
+
}
|
|
186
|
+
componentDidCatch() {
|
|
187
|
+
hardNavigate(this.props.href);
|
|
188
|
+
}
|
|
189
|
+
componentDidUpdate(prevProps) {
|
|
190
|
+
if (prevProps.href !== this.props.href && this.state.failed) {
|
|
191
|
+
this.setState({ failed: false });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
render() {
|
|
195
|
+
if (this.state.failed) {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
return this.props.children;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
160
201
|
function RSCRouter({ initialResponse, initialPathname }) {
|
|
161
202
|
const [flightResponse, setFlightResponse] = React.useState(initialResponse);
|
|
162
203
|
const [pathname, setPathname] = React.useState(initialPathname || (typeof window !== 'undefined' ? window.location.pathname : '/'));
|
|
@@ -164,6 +205,13 @@ function RSCRouter({ initialResponse, initialPathname }) {
|
|
|
164
205
|
? new URLSearchParams(window.location.search)
|
|
165
206
|
: new URLSearchParams());
|
|
166
207
|
const [isPending, startTransition] = React.useTransition();
|
|
208
|
+
const href = `${pathname}${searchParams.toString() ? `?${searchParams.toString()}` : ''}`;
|
|
209
|
+
React.useEffect(() => {
|
|
210
|
+
const initialKey = cacheKey(initialPathname || (typeof window !== 'undefined' ? window.location.pathname : '/'), typeof window !== 'undefined' ? window.location.search : '');
|
|
211
|
+
if (!flightCache.has(initialKey)) {
|
|
212
|
+
flightCache.set(initialKey, initialResponse);
|
|
213
|
+
}
|
|
214
|
+
}, [initialPathname, initialResponse]);
|
|
167
215
|
// Handle browser back/forward
|
|
168
216
|
React.useEffect(() => {
|
|
169
217
|
const onPopState = () => {
|
|
@@ -214,6 +262,33 @@ function RSCRouter({ initialResponse, initialPathname }) {
|
|
|
214
262
|
setFlightResponse(fetchFlight(curPath, curSearch));
|
|
215
263
|
});
|
|
216
264
|
}, []);
|
|
265
|
+
const resume = React.useCallback((url) => {
|
|
266
|
+
const parsed = new URL(url, window.location.origin);
|
|
267
|
+
const nextPath = parsed.pathname;
|
|
268
|
+
const nextSearch = parsed.search;
|
|
269
|
+
const nextUrl = `${nextPath}${nextSearch}`;
|
|
270
|
+
const nextResponse = fetchFlight(nextPath, nextSearch);
|
|
271
|
+
document.documentElement.setAttribute('data-vista-ppr', 'flight-resuming');
|
|
272
|
+
recordRuntimeTrace('rsc-resume-start', { url: nextUrl });
|
|
273
|
+
dispatchRuntimeEvent('vista:rsc-resume-start', { url: nextUrl });
|
|
274
|
+
startTransition(() => {
|
|
275
|
+
setPathname(nextPath);
|
|
276
|
+
setSearchParams(new URLSearchParams(nextSearch));
|
|
277
|
+
setFlightResponse(nextResponse);
|
|
278
|
+
});
|
|
279
|
+
Promise.resolve(nextResponse)
|
|
280
|
+
.then(() => {
|
|
281
|
+
recordRuntimeTrace('rsc-resume-complete', { url: nextUrl });
|
|
282
|
+
dispatchRuntimeEvent('vista:rsc-resume-complete', { url: nextUrl });
|
|
283
|
+
})
|
|
284
|
+
.catch((error) => {
|
|
285
|
+
const message = error && typeof error === 'object' && 'message' in error
|
|
286
|
+
? String(error.message || error)
|
|
287
|
+
: String(error || 'Unknown RSC resume error');
|
|
288
|
+
recordRuntimeTrace('rsc-resume-error', { url: nextUrl, message });
|
|
289
|
+
dispatchRuntimeEvent('vista:rsc-resume-error', { url: nextUrl, message });
|
|
290
|
+
});
|
|
291
|
+
}, []);
|
|
217
292
|
const contextValue = React.useMemo(() => ({
|
|
218
293
|
pathname,
|
|
219
294
|
searchParams,
|
|
@@ -225,59 +300,31 @@ function RSCRouter({ initialResponse, initialPathname }) {
|
|
|
225
300
|
refresh,
|
|
226
301
|
isPending,
|
|
227
302
|
}), [pathname, searchParams, push, replace, back, forward, prefetch, refresh, isPending]);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const bridge = {
|
|
233
|
-
refresh,
|
|
234
|
-
prefetch,
|
|
235
|
-
resume: (url) => {
|
|
236
|
-
const parsed = new URL(url, window.location.origin);
|
|
237
|
-
const nextPath = parsed.pathname;
|
|
238
|
-
const nextSearch = parsed.search;
|
|
239
|
-
const nextUrl = `${nextPath}${nextSearch}`;
|
|
240
|
-
const nextResponse = fetchFlight(nextPath, nextSearch);
|
|
241
|
-
document.documentElement.setAttribute('data-vista-ppr', 'flight-resuming');
|
|
242
|
-
recordRuntimeTrace('rsc-resume-start', { url: nextUrl });
|
|
243
|
-
dispatchRuntimeEvent('vista:rsc-resume-start', { url: nextUrl });
|
|
244
|
-
startTransition(() => {
|
|
245
|
-
setPathname(nextPath);
|
|
246
|
-
setSearchParams(new URLSearchParams(nextSearch));
|
|
247
|
-
setFlightResponse(nextResponse);
|
|
248
|
-
});
|
|
249
|
-
Promise.resolve(nextResponse)
|
|
250
|
-
.then(() => {
|
|
251
|
-
recordRuntimeTrace('rsc-resume-complete', { url: nextUrl });
|
|
252
|
-
dispatchRuntimeEvent('vista:rsc-resume-complete', { url: nextUrl });
|
|
253
|
-
})
|
|
254
|
-
.catch((error) => {
|
|
255
|
-
const message = error && typeof error === 'object' && 'message' in error
|
|
256
|
-
? String(error.message || error)
|
|
257
|
-
: String(error || 'Unknown RSC resume error');
|
|
258
|
-
recordRuntimeTrace('rsc-resume-error', { url: nextUrl, message });
|
|
259
|
-
dispatchRuntimeEvent('vista:rsc-resume-error', { url: nextUrl, message });
|
|
260
|
-
});
|
|
261
|
-
},
|
|
303
|
+
if (typeof window !== 'undefined') {
|
|
304
|
+
window.__VISTA_RSC_ROUTER__ = {
|
|
305
|
+
...contextValue,
|
|
306
|
+
resume,
|
|
262
307
|
getState: () => ({
|
|
263
308
|
pathname,
|
|
264
309
|
search: searchParams.toString() ? `?${searchParams.toString()}` : '',
|
|
265
310
|
isPending,
|
|
266
311
|
}),
|
|
267
312
|
};
|
|
268
|
-
|
|
313
|
+
}
|
|
314
|
+
React.useEffect(() => {
|
|
315
|
+
if (typeof window === 'undefined') {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
269
318
|
recordRuntimeTrace('rsc-router-ready', {
|
|
270
319
|
pathname,
|
|
271
320
|
search: searchParams.toString() ? `?${searchParams.toString()}` : '',
|
|
272
321
|
});
|
|
273
322
|
document.dispatchEvent(new CustomEvent('vista:rsc-router-ready'));
|
|
274
323
|
return () => {
|
|
275
|
-
|
|
276
|
-
delete window.__VISTA_RSC_ROUTER__;
|
|
277
|
-
}
|
|
324
|
+
delete window.__VISTA_RSC_ROUTER__;
|
|
278
325
|
};
|
|
279
|
-
}, [
|
|
280
|
-
return ((0, jsx_runtime_1.jsx)(exports.RSCRouterContext.Provider, { value: contextValue, children: (0, jsx_runtime_1.jsx)(RSCRoot, { response: flightResponse }) }));
|
|
326
|
+
}, []);
|
|
327
|
+
return ((0, jsx_runtime_1.jsx)(exports.RSCRouterContext.Provider, { value: contextValue, children: (0, jsx_runtime_1.jsx)(FlightNavigationErrorBoundary, { href: href, children: (0, jsx_runtime_1.jsx)(RSCRoot, { response: flightResponse }) }) }));
|
|
281
328
|
}
|
|
282
329
|
// ---------------------------------------------------------------------------
|
|
283
330
|
// Hooks — unified versions that work in both RSC and legacy modes
|
|
@@ -287,5 +334,11 @@ function RSCRouter({ initialResponse, initialPathname }) {
|
|
|
287
334
|
* otherwise falls back to null.
|
|
288
335
|
*/
|
|
289
336
|
function useRSCRouter() {
|
|
290
|
-
|
|
337
|
+
const ctx = React.useContext(exports.RSCRouterContext);
|
|
338
|
+
if (ctx)
|
|
339
|
+
return ctx;
|
|
340
|
+
if (typeof window !== 'undefined' && typeof window.__VISTA_RSC_ROUTER__?.push === 'function') {
|
|
341
|
+
return window.__VISTA_RSC_ROUTER__;
|
|
342
|
+
}
|
|
343
|
+
return null;
|
|
291
344
|
}
|
|
@@ -25,28 +25,6 @@ pages_build_output_dir = "${relativeOutput}"
|
|
|
25
25
|
(0, utils_1.writeFileIfAllowed)(targetFile, content, ctx.force);
|
|
26
26
|
return targetFile;
|
|
27
27
|
}
|
|
28
|
-
function writeRoutesJson(outputDir) {
|
|
29
|
-
const routesPath = path_1.default.join(outputDir, '_routes.json');
|
|
30
|
-
const routes = {
|
|
31
|
-
version: 1,
|
|
32
|
-
include: ['/*'],
|
|
33
|
-
exclude: ['/static/*'],
|
|
34
|
-
};
|
|
35
|
-
fs_1.default.writeFileSync(routesPath, `${JSON.stringify(routes, null, 2)}\n`, 'utf8');
|
|
36
|
-
return routesPath;
|
|
37
|
-
}
|
|
38
|
-
function writeRedirects(outputDir) {
|
|
39
|
-
const redirectsPath = path_1.default.join(outputDir, '_redirects');
|
|
40
|
-
const lines = [
|
|
41
|
-
'/_vista/* /:splat 200',
|
|
42
|
-
'/ /static/pages/index.html 200',
|
|
43
|
-
'/rsc /static/pages/index.rsc 200',
|
|
44
|
-
'/_rsc/* /static/pages/:splat.rsc 200',
|
|
45
|
-
'/* /static/pages/:splat.html 200',
|
|
46
|
-
];
|
|
47
|
-
fs_1.default.writeFileSync(redirectsPath, `${lines.join('\n')}\n`, 'utf8');
|
|
48
|
-
return redirectsPath;
|
|
49
|
-
}
|
|
50
28
|
exports.cloudflareAdapter = {
|
|
51
29
|
id: 'cloudflare',
|
|
52
30
|
requiredOutput: 'standalone',
|
|
@@ -63,13 +41,12 @@ exports.cloudflareAdapter = {
|
|
|
63
41
|
(0, utils_1.ensureDir)(outputDir);
|
|
64
42
|
if ((0, runtime_pack_1.isStaticOnlyDeploy)(ctx)) {
|
|
65
43
|
(0, utils_1.copyStaticHostAssets)(ctx.cwd, ctx.vistaDir, outputDir);
|
|
66
|
-
|
|
67
|
-
const redirectsPath = writeRedirects(outputDir);
|
|
44
|
+
(0, utils_1.prepareStaticCdnOutput)(outputDir);
|
|
68
45
|
const wranglerPath = writeStaticWranglerToml(ctx, outputDir);
|
|
69
46
|
return {
|
|
70
47
|
status: 'emitted',
|
|
71
48
|
target: 'cloudflare',
|
|
72
|
-
artifactPaths: [outputDir,
|
|
49
|
+
artifactPaths: [outputDir, wranglerPath],
|
|
73
50
|
instructions: [
|
|
74
51
|
'Static mode: Cloudflare Pages serves pre-rendered output.',
|
|
75
52
|
'For Flight SSR, omit deploy.output "static" and use Cloudflare Containers.',
|