Skip to content

Commit 6d47050

Browse files
committed
fix(pages): resolve staged entries when activated
1 parent f479528 commit 6d47050

3 files changed

Lines changed: 79 additions & 27 deletions

File tree

packages/vinext/src/index.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ import {
1919
invalidateRouteCache,
2020
matchRoute,
2121
} from "./routing/pages-router.js";
22-
import { generateServerEntry as _generateServerEntry } from "./entries/pages-server-entry.js";
22+
import {
23+
generatePagesRequestEntry as _generatePagesRequestEntry,
24+
generatePagesResponseEntry as _generatePagesResponseEntry,
25+
generateServerEntry as _generateServerEntry,
26+
} from "./entries/pages-server-entry.js";
2327
import { generateClientEntry as _generateClientEntry } from "./entries/pages-client-entry.js";
2428
import {
2529
appRouteGraph,
@@ -50,7 +54,9 @@ import { createDirectRunner } from "./server/dev-module-runner.js";
5054
import { generateRscEntry } from "./entries/app-rsc-entry.js";
5155
import { generateSsrEntry } from "./entries/app-ssr-entry.js";
5256
import {
57+
VIRTUAL_CDN_CACHE_ADAPTER,
5358
VIRTUAL_CACHE_ADAPTERS,
59+
generateCdnCacheAdapterModule,
5460
generateCacheAdaptersModule,
5561
hasVerbatimResponseVary,
5662
VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY,
@@ -1093,6 +1099,10 @@ const VIRTUAL_WORKER_ENTRY = "virtual:vinext-worker-entry";
10931099
const RESOLVED_WORKER_ENTRY = VIRTUAL_PREFIX + VIRTUAL_WORKER_ENTRY;
10941100
const VIRTUAL_SERVER_ENTRY = "virtual:vinext-server-entry";
10951101
const RESOLVED_SERVER_ENTRY = VIRTUAL_PREFIX + VIRTUAL_SERVER_ENTRY;
1102+
const VIRTUAL_PAGES_REQUEST_ENTRY = "virtual:vinext-pages-request-entry";
1103+
const RESOLVED_PAGES_REQUEST_ENTRY = VIRTUAL_PREFIX + VIRTUAL_PAGES_REQUEST_ENTRY;
1104+
const VIRTUAL_PAGES_RESPONSE_ENTRY = "virtual:vinext-pages-response-entry";
1105+
const RESOLVED_PAGES_RESPONSE_ENTRY = VIRTUAL_PREFIX + VIRTUAL_PAGES_RESPONSE_ENTRY;
10961106
const VIRTUAL_CLIENT_ENTRY = "virtual:vinext-client-entry";
10971107
const RESOLVED_CLIENT_ENTRY = VIRTUAL_PREFIX + VIRTUAL_CLIENT_ENTRY;
10981108
const VIRTUAL_PAGES_CLIENT_ASSETS = "virtual:vinext-pages-client-assets";
@@ -1113,6 +1123,8 @@ const VIRTUAL_ROOT_PARAMS = "virtual:vinext-root-params";
11131123
const RESOLVED_ROOT_PARAMS = VIRTUAL_PREFIX + VIRTUAL_ROOT_PARAMS;
11141124
/** Virtual module that registers config-driven cache adapters (see VinextOptions.cache). */
11151125
const RESOLVED_CACHE_ADAPTERS = VIRTUAL_PREFIX + VIRTUAL_CACHE_ADAPTERS;
1126+
/** CDN-only registrar kept out of the data-cache response graph. */
1127+
const RESOLVED_CDN_CACHE_ADAPTER = VIRTUAL_PREFIX + VIRTUAL_CDN_CACHE_ADAPTER;
11161128
/** Virtual module that registers the config-driven image optimizer (see VinextOptions.images). */
11171129
const RESOLVED_IMAGE_ADAPTERS = VIRTUAL_PREFIX + VIRTUAL_IMAGE_ADAPTERS;
11181130
/** Virtual module for composed instrumentation-client bootstrap. */
@@ -1601,6 +1613,33 @@ export default function vinext(options: VinextOptions = {}): PluginOption[] {
16011613
);
16021614
}
16031615

1616+
async function generatePagesRequestEntry(configuredPublicDir: string | false): Promise<string> {
1617+
const publicFiles =
1618+
isServeCommand && devPublicFileRoutes
1619+
? [...devPublicFileRoutes].sort()
1620+
: scanPublicFileRoutes(root, configuredPublicDir === "" ? false : configuredPublicDir);
1621+
return _generatePagesRequestEntry(
1622+
pagesDir,
1623+
nextConfig,
1624+
fileMatcher,
1625+
middlewarePath,
1626+
instrumentationPath,
1627+
publicFiles,
1628+
prerenderSecret,
1629+
);
1630+
}
1631+
1632+
function generatePagesResponseEntry(): Promise<string> {
1633+
return _generatePagesResponseEntry(
1634+
pagesDir,
1635+
nextConfig,
1636+
fileMatcher,
1637+
middlewarePath,
1638+
instrumentationPath,
1639+
prerenderSecret,
1640+
);
1641+
}
1642+
16041643
/**
16051644
* Generate the virtual client hydration entry module.
16061645
* This is the entry point for `vite build` (client bundle).
@@ -3962,10 +4001,18 @@ export default function vinext(options: VinextOptions = {}): PluginOption[] {
39624001

39634002
// Pages Router virtual modules
39644003
if (cleanId === VIRTUAL_SERVER_ENTRY) return RESOLVED_SERVER_ENTRY;
4004+
if (cleanId === VIRTUAL_PAGES_REQUEST_ENTRY) return RESOLVED_PAGES_REQUEST_ENTRY;
4005+
if (cleanId === VIRTUAL_PAGES_RESPONSE_ENTRY) return RESOLVED_PAGES_RESPONSE_ENTRY;
39654006
if (cleanId === VIRTUAL_CLIENT_ENTRY) return RESOLVED_CLIENT_ENTRY;
39664007
if (cleanId.endsWith("/" + VIRTUAL_SERVER_ENTRY)) {
39674008
return RESOLVED_SERVER_ENTRY;
39684009
}
4010+
if (cleanId.endsWith("/" + VIRTUAL_PAGES_REQUEST_ENTRY)) {
4011+
return RESOLVED_PAGES_REQUEST_ENTRY;
4012+
}
4013+
if (cleanId.endsWith("/" + VIRTUAL_PAGES_RESPONSE_ENTRY)) {
4014+
return RESOLVED_PAGES_RESPONSE_ENTRY;
4015+
}
39694016
if (cleanId.endsWith("/" + VIRTUAL_CLIENT_ENTRY)) {
39704017
return RESOLVED_CLIENT_ENTRY;
39714018
}
@@ -3992,6 +4039,12 @@ export default function vinext(options: VinextOptions = {}): PluginOption[] {
39924039
) {
39934040
return RESOLVED_CACHE_ADAPTERS;
39944041
}
4042+
if (
4043+
cleanId === VIRTUAL_CDN_CACHE_ADAPTER ||
4044+
cleanId.endsWith("/" + VIRTUAL_CDN_CACHE_ADAPTER)
4045+
) {
4046+
return RESOLVED_CDN_CACHE_ADAPTER;
4047+
}
39954048
if (
39964049
cleanId === VIRTUAL_IMAGE_ADAPTERS ||
39974050
cleanId.endsWith("/" + VIRTUAL_IMAGE_ADAPTERS)
@@ -4049,6 +4102,16 @@ export default function vinext(options: VinextOptions = {}): PluginOption[] {
40494102
this.environment.config.publicDir === "" ? false : this.environment.config.publicDir,
40504103
);
40514104
}
4105+
if (id === RESOLVED_PAGES_REQUEST_ENTRY) {
4106+
recordServerEntryLoad(this.environment?.name, id);
4107+
return await generatePagesRequestEntry(
4108+
this.environment.config.publicDir === "" ? false : this.environment.config.publicDir,
4109+
);
4110+
}
4111+
if (id === RESOLVED_PAGES_RESPONSE_ENTRY) {
4112+
recordServerEntryLoad(this.environment?.name, id);
4113+
return await generatePagesResponseEntry();
4114+
}
40524115
if (id === RESOLVED_CLIENT_ENTRY) {
40534116
return await generateClientEntry();
40544117
}
@@ -4180,6 +4243,9 @@ export default function vinext(options: VinextOptions = {}): PluginOption[] {
41804243
if (id === RESOLVED_CACHE_ADAPTERS) {
41814244
return generateCacheAdaptersModule(options.cache);
41824245
}
4246+
if (id === RESOLVED_CDN_CACHE_ADAPTER) {
4247+
return generateCdnCacheAdapterModule(options.cache);
4248+
}
41834249
if (id === RESOLVED_IMAGE_ADAPTERS) {
41844250
return generateImageAdaptersModule(options.images);
41854251
}

tests/after-deploy.test.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import { describe, expect, it } from "vite-plus/test";
1414
import fs from "node:fs";
1515
import path from "node:path";
16-
import { readPagesRouterEntrySource } from "./worker-entry-source.js";
16+
import { readPagesResponseStageEntrySource } from "./worker-entry-source.js";
1717

1818
type ExecutionContextLike = {
1919
waitUntil(promise: Promise<unknown>): void;
@@ -141,19 +141,20 @@ describe("after() in deploy mode — Pages Router worker entry", () => {
141141
it("forwards ctx to handleApiRoute so api routes can call after()", () => {
142142
// Regression for #1365: handleApiRoute previously ignored ctx, leaving
143143
// after() inside Pages Router api routes without a way to call
144-
// ctx.waitUntil(). The generated worker entry must thread ctx through.
145-
//
146-
// After #1336 item 3 the dispatch URL is `apiLookupUrl` (the locale-
147-
// stripped form of `resolvedUrl`), but `ctx` is still threaded through.
148-
const content = readPagesRouterEntrySource();
149-
expect(content).toContain(
150-
'handleApiRoute(req, apiUrl, ctx, new URL(req.url).origin, "worker")',
144+
// ctx.waitUntil(). Rendering now lives in the response stage, which must
145+
// preserve that context in the cacheability wrapper it passes to the
146+
// generated Pages entry.
147+
const content = readPagesResponseStageEntrySource();
148+
expect(content).toMatch(
149+
/pagesEntry\.handleApiRoute\(\s*request,\s*props\.apiUrl,\s*cacheabilityContext,\s*new URL\(request\.url\)\.origin,/,
151150
);
152151
});
153152

154153
it("forwards ctx and staged middleware headers to renderPage so page renders can call after() and apply CSP nonces", () => {
155-
const content = readPagesRouterEntrySource();
156-
expect(content).toContain("renderPage(req, resolvedUrl, null, ctx, stagedHeaders, options)");
154+
const content = readPagesResponseStageEntrySource();
155+
expect(content).toMatch(
156+
/pagesEntry\.renderPage\(\s*request,\s*props\.resolvedUrl,\s*null,\s*cacheabilityContext,\s*renderHeaders,\s*props\.renderOptions/,
157+
);
157158
});
158159
});
159160

tests/worker-entry-source.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,6 @@ export function readAppRouterEntrySource(): string {
99
);
1010
}
1111

12-
export function readAppRequestStageEntrySource(): string {
13-
const sourceUrl = new URL(
14-
"../packages/vinext/src/server/app-request-stage-independent-entry.ts",
15-
import.meta.url,
16-
);
17-
if (fs.existsSync(sourceUrl)) return fs.readFileSync(sourceUrl, "utf-8");
18-
return fs.readFileSync(
19-
new URL(
20-
"../packages/vinext/src/server/app-request-stage-independent-entry.js",
21-
import.meta.url,
22-
),
23-
"utf-8",
24-
);
25-
}
26-
2712
export function readPagesRouterEntrySource(): string {
2813
return readPagesRequestStageEntrySource();
2914
}
@@ -37,7 +22,7 @@ export function readPagesSingleEntrySource(): string {
3722
);
3823
}
3924

40-
export function readPagesRequestStageEntrySource(): string {
25+
function readPagesRequestStageEntrySource(): string {
4126
const sourceUrl = new URL(
4227
"../packages/vinext/src/server/pages-request-stage-entry.ts",
4328
import.meta.url,

0 commit comments

Comments
 (0)