Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/integration_tests_reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ jobs:
export NEXT_TEST_MODE=${{
inputs.test_type == 'development' && 'dev' || 'start'
}}
export NEXT_TEST_EMIT_ALL_OUTPUT=1
${{ inputs.run_before_test }}
Expand Down Expand Up @@ -129,6 +130,7 @@ jobs:
'TURBOPACK_DEV=1' ||
'TURBOPACK_BUILD=1'
}}
export NEXT_TEST_EMIT_ALL_OUTPUT=1
${{ inputs.run_before_test }}
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
"registry": "https://registry.npmjs.org/"
}
},
"version": "16.1.1-canary.36"
"version": "16.2.0-canary.0"
}
2 changes: 1 addition & 1 deletion packages/create-next-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-next-app",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"keywords": [
"react",
"next",
Expand Down
4 changes: 2 additions & 2 deletions packages/eslint-config-next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eslint-config-next",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"description": "ESLint configuration used by Next.js.",
"license": "MIT",
"repository": {
Expand All @@ -12,7 +12,7 @@
"dist"
],
"dependencies": {
"@next/eslint-plugin-next": "16.1.1-canary.36",
"@next/eslint-plugin-next": "16.2.0-canary.0",
"eslint-import-resolver-node": "^0.3.6",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.32.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-internal/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@next/eslint-plugin-internal",
"private": true,
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"description": "ESLint plugin for working on Next.js.",
"exports": {
".": "./src/eslint-plugin-internal.js"
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-plugin-next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/eslint-plugin-next",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"description": "ESLint plugin for Next.js.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion packages/font/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@next/font",
"private": true,
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"repository": {
"url": "vercel/next.js",
"directory": "packages/font"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-bundle-analyzer/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/bundle-analyzer",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"main": "index.js",
"types": "index.d.ts",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-codemod/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/codemod",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"license": "MIT",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-env/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/env",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"keywords": [
"react",
"next",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-mdx/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/mdx",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"main": "index.js",
"license": "MIT",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/next-plugin-storybook/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/plugin-storybook",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-plugin-storybook"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-polyfill-module/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-module",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)",
"main": "dist/polyfill-module.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-polyfill-nomodule/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/polyfill-nomodule",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"description": "A polyfill for non-dead, nomodule browsers.",
"main": "dist/polyfill-nomodule.js",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-routing/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/routing",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"keywords": [
"react",
"next",
Expand Down
2 changes: 1 addition & 1 deletion packages/next-rspack/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "next-rspack",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"repository": {
"url": "vercel/next.js",
"directory": "packages/next-rspack"
Expand Down
2 changes: 1 addition & 1 deletion packages/next-swc/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@next/swc",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"private": true,
"files": [
"native/"
Expand Down
3 changes: 2 additions & 1 deletion packages/next/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -979,5 +979,6 @@
"978": "Next.js has blocked a javascript: URL as a security precaution.",
"979": "invariant: expected %s bytes of postponed state but only received %s bytes",
"980": "Failed to load client middleware manifest",
"981": "resolvedPathname must be set in request metadata"
"981": "resolvedPathname must be set in request metadata",
"982": "`serializeResumeDataCache` should not be called in edge runtime."
}
14 changes: 7 additions & 7 deletions packages/next/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "next",
"version": "16.1.1-canary.36",
"version": "16.2.0-canary.0",
"description": "The React Framework",
"main": "./dist/server/next.js",
"license": "MIT",
Expand Down Expand Up @@ -97,7 +97,7 @@
]
},
"dependencies": {
"@next/env": "16.1.1-canary.36",
"@next/env": "16.2.0-canary.0",
"@swc/helpers": "0.5.15",
"baseline-browser-mapping": "^2.8.3",
"caniuse-lite": "^1.0.30001579",
Expand Down Expand Up @@ -162,11 +162,11 @@
"@modelcontextprotocol/sdk": "1.18.1",
"@mswjs/interceptors": "0.23.0",
"@napi-rs/triples": "1.2.0",
"@next/font": "16.1.1-canary.36",
"@next/polyfill-module": "16.1.1-canary.36",
"@next/polyfill-nomodule": "16.1.1-canary.36",
"@next/react-refresh-utils": "16.1.1-canary.36",
"@next/swc": "16.1.1-canary.36",
"@next/font": "16.2.0-canary.0",
"@next/polyfill-module": "16.2.0-canary.0",
"@next/polyfill-nomodule": "16.2.0-canary.0",
"@next/react-refresh-utils": "16.2.0-canary.0",
"@next/swc": "16.2.0-canary.0",
"@opentelemetry/api": "1.6.0",
"@playwright/test": "1.51.1",
"@rspack/core": "1.6.7",
Expand Down
135 changes: 118 additions & 17 deletions packages/next/src/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,125 @@ import type { ActionManifest } from '../build/webpack/plugins/flight-client-entr
import { extractInfoFromServerReferenceId } from '../shared/lib/server-reference-info'
import { convertSegmentPathToStaticExportFilename } from '../shared/lib/segment-cache/segment-value-encoding'
import { getNextBuildDebuggerPortOffset } from '../lib/worker'
import { getParams } from './helpers/get-params'
import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic'
import { normalizeAppPath } from '../shared/lib/router/utils/app-paths'
import type { Params } from '../server/request/params'

export class ExportError extends Error {
code = 'NEXT_EXPORT_ERROR'
}

/**
* Picks an RDC seed by matching on the params that are
* already known, so fallback shells use a seed that has already
* computed those known params.
*/
function buildRDCCacheByPage(
results: ExportPagesResult,
finalPhaseExportPaths: ExportPathEntry[]
): Record<string, string> {
const renderResumeDataCachesByPage: Record<string, string> = {}
const seedCandidatesByPage = new Map<
string,
Array<{ path: string; renderResumeDataCache: string }>
>()

for (const { page, path, result } of results) {
if (!result) {
continue
}

if ('renderResumeDataCache' in result && result.renderResumeDataCache) {
// Collect all RDC seeds for this page so we can pick the best match
// for each fallback shell later (e.g. locale-specific variants).
const candidates = seedCandidatesByPage.get(page) ?? []
candidates.push({
path,
renderResumeDataCache: result.renderResumeDataCache,
})
seedCandidatesByPage.set(page, candidates)
// Remove the RDC string from the result so that it can be garbage
// collected, when there are more results for the same page.
result.renderResumeDataCache = undefined
}
}

const getKnownParamsKey = (
normalizedPage: string,
path: string,
fallbackParamNames: Set<string>
): string | null => {
let params: Params
try {
params = getParams(normalizedPage, path)
} catch {
return null
}

// Only keep params that are known, then sort
// for a stable key so we can match a compatible seed.
const entries = Object.entries(params).filter(
([key]) => !fallbackParamNames.has(key)
)

entries.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
return JSON.stringify(entries)
}

for (const exportPath of finalPhaseExportPaths) {
const { page, path, _fallbackRouteParams = [] } = exportPath
if (!isDynamicRoute(page)) {
continue
}

// Normalize app pages before param matching.
const normalizedPage = normalizeAppPath(page)
const pageKey = page !== path ? `${page}: ${path}` : path
const fallbackParamNames = new Set(
_fallbackRouteParams.map((param) => param.paramName)
)
// Build a key from the known params for this fallback shell so we can
// select a seed from a compatible prerendered route.
const targetKey = getKnownParamsKey(
normalizedPage,
path,
fallbackParamNames
)

if (!targetKey) {
continue
}

const candidates = seedCandidatesByPage.get(page)

// No suitable candidates, so there's no RDC seed to select
if (!candidates || candidates.length === 0) {
continue
}

let selected: string | null = null
for (const candidate of candidates) {
// Pick the seed whose known params match this fallback shell.
const candidateKey = getKnownParamsKey(
normalizedPage,
candidate.path,
fallbackParamNames
)
if (candidateKey === targetKey) {
selected = candidate.renderResumeDataCache
break
}
}

if (selected) {
renderResumeDataCachesByPage[pageKey] = selected
}
}

return renderResumeDataCachesByPage
}

async function exportAppImpl(
dir: string,
options: Readonly<ExportAppOptions>,
Expand Down Expand Up @@ -664,23 +778,10 @@ async function exportAppImpl(
results = await exportPagesInBatches(worker, initialPhaseExportPaths)

if (finalPhaseExportPaths.length > 0) {
const renderResumeDataCachesByPage: Record<string, string> = {}

for (const { page, result } of results) {
if (!result) {
continue
}

if ('renderResumeDataCache' in result && result.renderResumeDataCache) {
// The last RDC for each page is used. We only need one. It should have
// all the entries that the fallback shell also needs. We don't need to
// merge them per page.
renderResumeDataCachesByPage[page] = result.renderResumeDataCache
// Remove the RDC string from the result so that it can be garbage
// collected, when there are more results for the same page.
result.renderResumeDataCache = undefined
}
}
const renderResumeDataCachesByPage = buildRDCCacheByPage(
results,
finalPhaseExportPaths
)

const finalPhaseResults = await exportPagesInBatches(
worker,
Expand Down
4 changes: 2 additions & 2 deletions packages/next/src/export/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,9 @@ export async function exportPages(
// Also tests for `inspect-brk`
process.env.NODE_OPTIONS?.includes('--inspect')

const renderResumeDataCache = renderResumeDataCachesByPage[page]
const renderResumeDataCache = renderResumeDataCachesByPage[pageKey]
? createRenderResumeDataCache(
renderResumeDataCachesByPage[page],
renderResumeDataCachesByPage[pageKey],
renderOpts.experimental.maxPostponedStateSizeBytes
)
: undefined
Expand Down
Loading
Loading