@wular/pnext 0.0.19 → 0.0.21

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 (83) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/reference/compat.md +2 -2
  4. package/reference/getting-started.md +2 -2
  5. package/src/cli/adapters/vercel.ts +27 -0
  6. package/src/cli/analyze.ts +8 -7
  7. package/src/cli/boot/trace.ts +9 -3
  8. package/src/cli/build/cache.ts +324 -0
  9. package/src/cli/build.ts +42 -18
  10. package/src/cli/create.ts +4 -4
  11. package/src/cli/dev.ts +23 -0
  12. package/src/cli/migrate/package-json.ts +3 -2
  13. package/src/cli/migrate/report.ts +1 -1
  14. package/src/cli/migrate/run.ts +2 -2
  15. package/src/cli/migrate/scan.ts +128 -1
  16. package/src/cli/serve/pipeline.ts +42 -42
  17. package/src/cli/start.ts +20 -20
  18. package/src/client/build.ts +64 -35
  19. package/src/client/entry.ts +228 -16
  20. package/src/client/router/page-slot.ts +81 -0
  21. package/src/client/router/runtime.ts +107 -112
  22. package/src/compat/bundler/cjs-exports.ts +26 -20
  23. package/src/compat/client/css-order.ts +1 -1
  24. package/src/compat/client/navigation-scroll.ts +36 -40
  25. package/src/compat/css/modules.ts +6 -2
  26. package/src/compat/metadata.ts +0 -9
  27. package/src/compat/next/font/cache.ts +10 -22
  28. package/src/compat/next/font/runtime.ts +20 -0
  29. package/src/compat/next/legacy-request-apis.ts +41 -5
  30. package/src/compat/next/script.tsx +16 -24
  31. package/src/compat/react/client-lite.ts +1 -0
  32. package/src/compat/react/client.ts +11 -0
  33. package/src/compat/react/dom-client.ts +11 -2
  34. package/src/compat/react/dom-react-server.ts +4 -0
  35. package/src/compat/react/dom.ts +11 -2
  36. package/src/compat/react/internals.ts +65 -0
  37. package/src/compat/react/parity.ts +47 -0
  38. package/src/compat/react/preact.ts +31 -1
  39. package/src/compat/react/react-server.ts +7 -0
  40. package/src/compat/react/server.ts +16 -2
  41. package/src/compat/react/style-values.ts +68 -0
  42. package/src/compat/register/proxy.ts +3 -1
  43. package/src/compat/register/render.ts +14 -3
  44. package/src/compat/register/routing.ts +57 -7
  45. package/src/compat/register/typed-routes.ts +0 -2
  46. package/src/compat/tsconfig-defaults.ts +4 -0
  47. package/src/compat/typecheck/check.ts +26 -71
  48. package/src/compat/typecheck/worker.ts +3 -2
  49. package/src/compat/validation/validate.ts +2 -0
  50. package/src/compat-bootstrap.ts +4 -0
  51. package/src/css/build.ts +153 -14
  52. package/src/css/postcss.ts +32 -3
  53. package/src/dev/restart/client-key.ts +1 -1
  54. package/src/dev/restart/global-css.ts +12 -94
  55. package/src/dev/restart/record.ts +106 -0
  56. package/src/dev/restart/route-facts.ts +11 -90
  57. package/src/dev/server.ts +208 -41
  58. package/src/extensions.ts +69 -3
  59. package/src/render/hooks.ts +1 -1
  60. package/src/render/metadata.ts +89 -17
  61. package/src/render/renderer.ts +325 -138
  62. package/src/render/resource-hints.ts +62 -9
  63. package/src/render/slots.tsx +2 -1
  64. package/src/resolve/scan-facts.ts +13 -5
  65. package/src/routing/forwarded.ts +20 -7
  66. package/src/routing/handler.ts +6 -2
  67. package/src/routing/href.ts +11 -1
  68. package/src/routing/metadata-files.ts +16 -3
  69. package/src/routing/proxy.ts +6 -3
  70. package/src/routing/routes.ts +253 -34
  71. package/src/runtime/fetch-host.ts +42 -0
  72. package/src/runtime/fingerprint.ts +132 -0
  73. package/src/runtime/loader.ts +7 -5
  74. package/src/runtime/module-cache.ts +3 -36
  75. package/src/runtime/modules.ts +333 -77
  76. package/src/runtime/vendor-build.ts +34 -5
  77. package/src/runtime/vendor.ts +1 -18
  78. package/src/types.ts +6 -0
  79. package/src/utils/asset-hash.ts +22 -0
  80. package/src/utils/code.ts +41 -0
  81. package/src/utils/html.ts +4 -1
  82. package/src/utils/registry.ts +70 -0
  83. package/src/utils/serialize.ts +66 -7
@@ -0,0 +1,68 @@
1
+ // React's dangerousStyleValue, which preact does not implement: a numeric 0 is unitless for EVERY
2
+ // property, booleans render nothing at all, and React's unitless set is wider than preact's (aspect
3
+ // ratio, columns, grid placement, scale, font weight). preact appends `px` in each of those cases, so
4
+ // the same style object serializes differently from React's - `width:0px` where React writes `width:0`.
5
+ // Numbers are resolved to React's exact string in the vnode pass, before preact (DOM setStyle or
6
+ // render-to-string) ever formats them, so both the served markup and the hydrated DOM match React.
7
+ //
8
+ // Not gated on the react-compat flag like ./parity's passes: this changes how a value the app already
9
+ // wrote is spelled, never what it means, and pnext's own render API is React-shaped either way.
10
+ import { options, type VNode } from 'preact'
11
+
12
+ // A space-delimited string rather than a Set: same lookup for a list this size, no construction at
13
+ // module init, and fewer bytes in every client bundle that ships the shim.
14
+ const UNITLESS =
15
+ ' animationIterationCount aspectRatio borderImageOutset borderImageSlice borderImageWidth boxFlex' +
16
+ ' boxFlexGroup boxOrdinalGroup columnCount columns flex flexGrow flexPositive flexShrink flexNegative' +
17
+ ' flexOrder fontWeight gridArea gridColumn gridColumnEnd gridColumnSpan gridColumnStart gridRow' +
18
+ ' gridRowEnd gridRowSpan gridRowStart lineClamp lineHeight opacity order orphans scale tabSize widows' +
19
+ ' zIndex zoom fillOpacity floodOpacity stopOpacity strokeDasharray strokeDashoffset strokeMiterlimit' +
20
+ ' strokeOpacity strokeWidth '
21
+
22
+ // React registers a vendor-prefixed twin of every unitless property; unprefixing the name keeps the
23
+ // list at one entry per property instead of five.
24
+ const VENDOR_PREFIX = /^(?:Webkit|Moz|ms|O)(?=[A-Z])/
25
+
26
+ function unitless(name: string) {
27
+ if (UNITLESS.includes(` ${name} `)) return true
28
+ const base = name.replace(VENDOR_PREFIX, '')
29
+ return base !== name && UNITLESS.includes(` ${base[0]!.toLowerCase()}${base.slice(1)} `)
30
+ }
31
+
32
+ /** React's spelling of one style value, or undefined when preact already agrees. */
33
+ export function reactStyleValue(name: string, value: unknown): string | undefined {
34
+ // React drops boolean declarations entirely; an empty string is what drops them in preact.
35
+ if (typeof value === 'boolean') return ''
36
+ if (typeof value !== 'number') return undefined
37
+ if (value !== 0 && !name.startsWith('--') && !unitless(name)) return undefined
38
+ return String(value)
39
+ }
40
+
41
+ export function applyReactStyleValues(vnode: VNode): void {
42
+ if (typeof vnode.type !== 'string') return
43
+ const props = vnode.props as Record<string, unknown> | null
44
+ const style = props?.style
45
+ if (!style || typeof style !== 'object' || Array.isArray(style)) return
46
+
47
+ // The app's object is never mutated - it may be a module-level constant reused across renders.
48
+ let normalized: Record<string, unknown> | undefined
49
+ for (const name in style) {
50
+ const resolved = reactStyleValue(name, (style as Record<string, unknown>)[name])
51
+ if (resolved === undefined) continue
52
+ normalized ??= { ...style }
53
+ normalized[name] = resolved
54
+ }
55
+ if (normalized) props.style = normalized
56
+ }
57
+
58
+ const installed = Symbol.for('pnext.react-style-values-installed')
59
+ const styleOptions = options as typeof options & { [installed]?: true }
60
+
61
+ if (!styleOptions[installed]) {
62
+ const previousVNode = options.vnode?.bind(options)
63
+ options.vnode = vnode => {
64
+ previousVNode?.(vnode)
65
+ applyReactStyleValues(vnode)
66
+ }
67
+ styleOptions[installed] = true
68
+ }
@@ -8,6 +8,7 @@ import { recordRewrite } from '../protocol'
8
8
  import { recordCanonicalUrl } from '../next/canonical-url'
9
9
  import { getNextConfig } from '../next/config-loader'
10
10
  import { activateTestProxy } from '../lifecycle/testmode'
11
+ import { legacyRequestAPIs } from '../../request/context'
11
12
 
12
13
  export function registerProxyExtensions(_config: ResolvedConfig): void {
13
14
  setProxyResponseProtocol({
@@ -72,7 +73,8 @@ async function validateNextProxyFiles(config: ResolvedConfig) {
72
73
  `Both middleware file "${relativeProxyPath(config, middleware)}" and proxy file "${relativeProxyPath(config, proxy)}" are detected. Please use "${relativeProxyPath(config, proxy)}" only.`,
73
74
  )
74
75
  }
75
- if (middleware && !proxy) {
76
+ // Proxy is a Next 16 migration; legacy request APIs explicitly target the middleware era.
77
+ if (middleware && !proxy && !legacyRequestAPIs()) {
76
78
  console.warn('The "middleware" file convention is deprecated. Please use "proxy" instead.')
77
79
  }
78
80
  await Promise.all(files.map(file => validateNextProxyFile(config, file)))
@@ -41,6 +41,7 @@ import { invokeServerComponentWithUse } from '../react/server-component-use'
41
41
  import {
42
42
  fontAssets,
43
43
  fontPreloadDomScript,
44
+ fontSizeAdjustMeta,
44
45
  prewarmFonts,
45
46
  runWithFontScope,
46
47
  } from '../next/font/runtime'
@@ -52,10 +53,11 @@ import {
52
53
  } from '../metadata'
53
54
  import { aliasGenerateStaticParams } from '../static-params'
54
55
  import { getNextConfig } from '../next/config-loader'
56
+ import { withSyncProps } from '../next/legacy-request-apis'
55
57
  import { renderServerInsertedHTML, setServerInsertedHTMLScope } from '../react/server-inserted-html'
56
58
  import { nextClientRouteState } from '../next/route-state'
57
59
  import { earlySubmitCaptureScript } from '../actions/early-submit'
58
- import { getWorkUnit } from '../../request/context'
60
+ import { getWorkUnit, legacyRequestAPIs } from '../../request/context'
59
61
  import {
60
62
  createVaryParamsAccumulator,
61
63
  createVaryingParams,
@@ -102,6 +104,7 @@ export function registerRenderExtensions(config: ResolvedConfig): void {
102
104
  setRenderExtensions({
103
105
  clientRouteState: nextClientRouteState,
104
106
  trackVaryParams: trackRenderVaryParams,
107
+ legacySyncProps: withSyncProps,
105
108
  // Next asserts structural selectors (`body > p`) against the live DOM, so
106
109
  // island hosts and the page slot travel as comment pairs and the inline
107
110
  // bootstrap materializes them. Core ships the elements as final.
@@ -117,8 +120,9 @@ export function registerRenderExtensions(config: ResolvedConfig): void {
117
120
  })
118
121
  setDocumentScriptExtensions({
119
122
  documentBodyScripts: cfg =>
120
- `${flightDataBootstrapScript()}${renderPartytownHeadScripts()}${basePathScript(cfg.basePath)}${trailingSlashScript(cfg.trailingSlash)}${skipTrailingSlashScript(cfg.skipTrailingSlashRedirect)}${staleTimesScript()}${segmentSchedulerScript()}${appShellsScript()}${optimisticRoutingScript()}${nextRouterShimScript()}<script>${earlySubmitCaptureScript()}</script>${fontPreloadDomScript()}`,
121
- documentHeadTags: () => `${polyfillsScript(config)}${renderServerInsertedHTML()}`,
123
+ `${flightDataBootstrapScript()}${renderPartytownHeadScripts()}${basePathScript(cfg.basePath)}${trailingSlashScript(cfg.trailingSlash)}${skipTrailingSlashScript(cfg.skipTrailingSlashRedirect)}${staleTimesScript()}${segmentSchedulerScript()}${legacySyncPropsScript()}${appShellsScript()}${optimisticRoutingScript()}${nextRouterShimScript()}<script>${earlySubmitCaptureScript()}</script>${fontPreloadDomScript()}`,
124
+ documentHeadTags: () =>
125
+ `${polyfillsScript(config)}${fontSizeAdjustMeta()}${renderServerInsertedHTML()}`,
122
126
  streamInsertedHTML: renderServerInsertedHTML,
123
127
  })
124
128
  }
@@ -227,6 +231,13 @@ function segmentSchedulerScript(): string {
227
231
  // per params-reading route to prime that route's shared App Shell (compat/
228
232
  // client/segment-prefetch.ts). Strictly opt-in — without the flag the extra
229
233
  // request would break the request-count contracts of every other suite.
234
+ // Under compat.next.legacyRequestAPIs the server's params/searchParams promises also carry the
235
+ // resolved object's own keys (withSyncProps). Those keys cannot travel on the wire, so the client
236
+ // rebuilds them - but only if it knows the surface existed, or a strict app would grow one.
237
+ function legacySyncPropsScript(): string {
238
+ return legacyRequestAPIs() ? `<script>window.__PNEXT_SYNC_PROPS__=1;</script>` : ''
239
+ }
240
+
230
241
  function appShellsScript(): string {
231
242
  const experimental = getNextConfig().experimental as { appShells?: unknown } | undefined
232
243
  return experimental?.appShells === true
@@ -235,11 +235,14 @@ function maskSource(source: string): string {
235
235
 
236
236
  // A source that declares a server action needs the client action runtime, even
237
237
  // when the <form> itself lives in a client component the page hands the action
238
- // to. Matched on the RAW source: stripComments also blanks string literals,
239
- // which would erase the directive being looked for.
238
+ // to. Preserve string literals so module and inline directives remain visible,
239
+ // but discard comments: framework/user documentation mentioning `'use server'`
240
+ // is not executable and must not pull the action runtime into every importing route.
240
241
  function usesServerActionForm(source: string): boolean {
241
- if (source.includes('use server') && /(['"])use server\1/.test(source)) return true
242
- return usesClientFunctionFormAction(source)
242
+ return (
243
+ (source.includes('use server') && /(['"])use server\1/.test(stripComments(source))) ||
244
+ usesClientFunctionFormAction(source)
245
+ )
243
246
  }
244
247
 
245
248
  // React runs a FUNCTION passed to `<form action>` / `<button formAction>` on the client with the form's
@@ -408,6 +411,53 @@ const stripCommentsAndStrings = memoizeBySource(source =>
408
411
  stripComments(source).replace(/(['"`])(?:\\[\s\S]|(?!\1)[^\\])*\1/g, ''),
409
412
  )
410
413
 
411
- const stripComments = memoizeBySource(source =>
412
- source.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^:])\/\/.*$/gm, '$1'),
413
- )
414
+ // Keep literal contents intact: consumers use this to find directive literals
415
+ // and import specifiers. A regex-only stripper mistakes the second slash in
416
+ // `"https://..."` for a line comment, erasing every real directive later on
417
+ // that line or below it. Scan the lexical states instead, so every source
418
+ // shape follows the same rule.
419
+ const stripComments = memoizeBySource(source => {
420
+ const chars = [...source]
421
+ let quote: string | undefined
422
+ let lineComment = false
423
+ let blockComment = false
424
+ for (let index = 0; index < chars.length; index++) {
425
+ const char = chars[index]!
426
+ const next = chars[index + 1]
427
+ if (lineComment) {
428
+ if (char === '\n') lineComment = false
429
+ else chars[index] = ' '
430
+ continue
431
+ }
432
+ if (blockComment) {
433
+ chars[index] = char === '\n' ? '\n' : ' '
434
+ if (char === '*' && next === '/') {
435
+ chars[index + 1] = ' '
436
+ blockComment = false
437
+ index++
438
+ }
439
+ continue
440
+ }
441
+ if (quote) {
442
+ if (char === '\\') index++
443
+ else if (char === quote) quote = undefined
444
+ continue
445
+ }
446
+ if (char === '"' || char === "'" || char === '`') {
447
+ quote = char
448
+ continue
449
+ }
450
+ if (char === '/' && next === '/') {
451
+ chars[index] = chars[index + 1] = ' '
452
+ lineComment = true
453
+ index++
454
+ continue
455
+ }
456
+ if (char === '/' && next === '*') {
457
+ chars[index] = chars[index + 1] = ' '
458
+ blockComment = true
459
+ index++
460
+ }
461
+ }
462
+ return chars.join('')
463
+ })
@@ -11,7 +11,6 @@ import {
11
11
  } from '../../extensions'
12
12
  import type { RouteManifestEntry } from '../../types'
13
13
  import { nextCompatEnabled } from '../aliases'
14
- import { writeTsconfigDefaults } from '../tsconfig-defaults'
15
14
  import { typecheckDisabled, validateTypesOffThread } from '../typecheck/check'
16
15
  import { generateTypedRoutes } from '../typed-routes/generate'
17
16
 
@@ -21,7 +20,6 @@ export function registerTypedRoutesExtensions(_config: ResolvedConfig): void {
21
20
 
22
21
  async function runTypedRoutesBuildStep(ctx: BuildStepContext): Promise<void> {
23
22
  if (!nextCompatEnabled(ctx.config)) return
24
- await writeTsconfigDefaults(ctx.config)
25
23
  await generateTypedRoutes(ctx.config, ctx.routes as RouteManifestEntry[])
26
24
  // The typecheck reads only files that already exist on disk, so it runs on a worker thread
27
25
  // alongside client bundling and prerendering rather than ahead of them; the CLI awaits it before
@@ -144,7 +144,11 @@ function getDesiredCompilerOptions(
144
144
  ? {}
145
145
  : { isolatedModules: { value: true, reason: 'requirement for SWC / Babel' } }),
146
146
  jsx: {
147
+ // A missing value follows Next's automatic-runtime default, while an app
148
+ // that explicitly owns JSX emission keeps preserve. PNext's private
149
+ // typecheck project can analyze either without rewriting user intent.
147
150
  parsedValue: 'react-jsx',
151
+ parsedValues: ['preserve', 'react-jsx'],
148
152
  value: 'react-jsx',
149
153
  reason: 'next.js uses the React automatic runtime',
150
154
  },
@@ -42,6 +42,7 @@ export function typecheckDisabled(): boolean {
42
42
  export interface TypecheckWorkerInput {
43
43
  root: string
44
44
  outPath: string
45
+ compat: ResolvedConfig['compat']
45
46
  nextConfig: NextConfigObject
46
47
  }
47
48
 
@@ -63,6 +64,7 @@ export async function validateTypesOffThread(config: ResolvedConfig): Promise<vo
63
64
  const input: TypecheckWorkerInput = {
64
65
  root: config.root,
65
66
  outPath: config.outPath,
67
+ compat: config.compat,
66
68
  nextConfig: cloneable({
67
69
  typescript: next.typescript,
68
70
  typedRoutes: next.typedRoutes,
@@ -300,10 +302,13 @@ async function writeTypecheckProject(
300
302
  // `next/*` shims would shadow those and mask the typed-route errors the suite asserts. When the real next
301
303
  // package is resolvable from the app, drop the next-module shims and let real next plus the generated
302
304
  // files drive the check - exactly what `next build` does.
303
- const omitNextModuleShims = typedRoutesEnabled() && realNextResolvable(config.root)
305
+ const hasRealNext = realNextResolvable(config.root)
306
+ const omitNextModuleShims = typedRoutesEnabled() && hasRealNext
307
+ const legacyRequestTypes =
308
+ typeof config.compat?.next === 'object' && config.compat.next.legacyRequestAPIs === true
304
309
  await writeFile(
305
310
  path.join(dir, 'next-shims.d.ts'),
306
- nextShims(hasReactTypes, rootParams, omitNextModuleShims),
311
+ nextShims(hasReactTypes, rootParams, omitNextModuleShims, legacyRequestTypes, hasRealNext),
307
312
  )
308
313
  const preact = resolvePackageDir('preact', config.root)
309
314
  if (reactTypes && preact) {
@@ -349,12 +354,10 @@ async function writeTypecheckProject(
349
354
  allowJs: true,
350
355
  resolveJsonModule: true,
351
356
  isolatedModules: true,
352
- jsx: 'react-jsx',
353
- // Runtime JSX targets Preact, but compat typechecking validates the
354
- // React/Next contract: an inherited `jsxImportSource: 'preact'` would
355
- // make app components return Preact VNodes, which the React validator
356
- // rejects. 'react' is TS's own default for react-jsx.
357
- jsxImportSource: 'react',
357
+ // Next owns the JSX transform and typechecks the source form. Besides
358
+ // matching its required tsconfig option, `preserve` keeps a legacy
359
+ // `import React` semantically used when noUnusedLocals is enabled.
360
+ jsx: 'preserve',
358
361
  // `paths` only, never `baseUrl` — TS 7 removed it (TS5102). The
359
362
  // mappings are absolutized above so they resolve the same from here.
360
363
  ...(paths ? { paths } : {}),
@@ -375,7 +378,7 @@ async function writeTypecheckProject(
375
378
  // before this typecheck. Next includes it in the program so invalid exports fail the build with a
376
379
  // constraint type error; include it here too. It lives outside the `.pnext` outPath, so its
377
380
  // diagnostics survive appDiagnostic's scoping.
378
- ...(existsSync(path.join(config.root, '.next/types/validator.ts'))
381
+ ...(!legacyRequestTypes && existsSync(path.join(config.root, '.next/types/validator.ts'))
379
382
  ? [path.join(config.root, '.next/types/validator.ts')]
380
383
  : []),
381
384
  path.join(config.root, 'middleware.*'),
@@ -619,6 +622,8 @@ function nextShims(
619
622
  hasReactTypes: boolean,
620
623
  rootParams: Map<string, Set<RootParamValueType>>,
621
624
  omitNextModuleShims = false,
625
+ legacyRequestTypes = false,
626
+ hasRealNext = false,
622
627
  ): string {
623
628
  // Ambient module declarations override real module resolution, so the react
624
629
  // fallbacks below must only exist when no real `@types/react` was found —
@@ -653,6 +658,16 @@ declare module 'react/jsx-runtime' {
653
658
  export const jsxs: any
654
659
  export const Fragment: any
655
660
  }
661
+ `
662
+ const requestType = (type: string) =>
663
+ legacyRequestTypes ? `Promise<${type}> & ${type}` : `Promise<${type}>`
664
+ const requestShims = `declare module 'next/headers'{interface CookieSetOptions{name?:string;value?:string;path?:string;maxAge?:number;expires?:Date|number;httpOnly?:boolean;secure?:boolean;sameSite?:'strict'|'lax'|'none'|boolean;domain?:string}interface CookieStore{get(name:string):{name:string;value:string}|undefined;get(options:{name:string;value?:string}):{name:string;value:string}|undefined;getAll(name?:string):{name:string;value:string}[];getAll(options:{name:string;value?:string}):{name:string;value:string}[];has(name:string):boolean;readonly size:number;set(name:string,value:string,options?:CookieSetOptions):CookieStore;set(options:CookieSetOptions):CookieStore;delete(name:string):boolean;clear():CookieStore;[Symbol.iterator]():IterableIterator<readonly[string,{name?:string;value:string}]>}export function cookies():${requestType('CookieStore')};export function headers():${requestType('Headers')};export function draftMode():${requestType(`{isEnabled:boolean;enable():void;disable():void}`)}}
665
+ `
666
+ const fontShims = hasRealNext
667
+ ? ''
668
+ : `declare module 'next/font'{export interface FontModule{className:string;style:{fontFamily:string;fontWeight?:number;fontStyle?:string};variable:string}}
669
+ declare module 'next/font/local'{import type{FontModule}from'next/font';export default function localFont(options:any):FontModule}
670
+ declare module 'next/font/google'{import type{FontModule}from'next/font';type FontLoader=(options?:any)=>FontModule;const font:FontLoader;export default font;export const Inter:FontLoader,Geist:FontLoader,Geist_Mono:FontLoader,Roboto:FontLoader,Roboto_Mono:FontLoader,Open_Sans:FontLoader,Lato:FontLoader,Montserrat:FontLoader,Poppins:FontLoader,Noto_Sans:FontLoader}
656
671
  `
657
672
  const source = `${reactShims}
658
673
  declare const process: {
@@ -831,39 +846,7 @@ declare module 'next/cache' {
831
846
  export const unstable_cacheLife: (...args: any[]) => void
832
847
  }
833
848
 
834
- declare module 'next/headers' {
835
- interface CookieSetOptions {
836
- name?: string
837
- value?: string
838
- path?: string
839
- maxAge?: number
840
- expires?: Date | number
841
- httpOnly?: boolean
842
- secure?: boolean
843
- sameSite?: 'strict' | 'lax' | 'none' | boolean
844
- domain?: string
845
- }
846
- interface CookieStore {
847
- get(name: string): { name: string; value: string } | undefined
848
- get(options: { name: string; value?: string }): { name: string; value: string } | undefined
849
- getAll(name?: string): { name: string; value: string }[]
850
- getAll(options: { name: string; value?: string }): { name: string; value: string }[]
851
- has(name: string): boolean
852
- readonly size: number
853
- set(name: string, value: string, options?: CookieSetOptions): CookieStore
854
- set(options: CookieSetOptions): CookieStore
855
- delete(name: string): boolean
856
- clear(): CookieStore
857
- [Symbol.iterator](): IterableIterator<readonly [string, { name?: string; value: string }]>
858
- }
859
- export function cookies(): Promise<CookieStore>
860
- export function headers(): Promise<Headers>
861
- export function draftMode(): Promise<{
862
- isEnabled: boolean
863
- enable(): void
864
- disable(): void
865
- }>
866
- }
849
+ ${requestShims}
867
850
 
868
851
  declare module 'next/server' {
869
852
  interface NextURL extends URL {
@@ -1096,35 +1079,7 @@ declare module 'next/error' {
1096
1079
  ): ComponentType<P & { children?: ReactNode; clearError?: () => void }>
1097
1080
  }
1098
1081
 
1099
- declare module 'next/font' {
1100
- export interface FontModule {
1101
- className: string
1102
- style: { fontFamily: string; fontWeight?: number; fontStyle?: string }
1103
- variable: string
1104
- }
1105
- }
1106
-
1107
- declare module 'next/font/local' {
1108
- import type { FontModule } from 'next/font'
1109
- export default function localFont(options: any): FontModule
1110
- }
1111
-
1112
- declare module 'next/font/google' {
1113
- import type { FontModule } from 'next/font'
1114
- type FontLoader = (options?: any) => FontModule
1115
- const font: FontLoader
1116
- export default font
1117
- export const Inter: FontLoader
1118
- export const Geist: FontLoader
1119
- export const Geist_Mono: FontLoader
1120
- export const Roboto: FontLoader
1121
- export const Roboto_Mono: FontLoader
1122
- export const Open_Sans: FontLoader
1123
- export const Lato: FontLoader
1124
- export const Montserrat: FontLoader
1125
- export const Poppins: FontLoader
1126
- export const Noto_Sans: FontLoader
1127
- }
1082
+ ${fontShims}
1128
1083
 
1129
1084
  declare module 'next/script' {
1130
1085
  import type { ComponentType } from 'react'
@@ -11,11 +11,12 @@ import { PnextTypecheckError, validateTypes } from './check'
11
11
  import type { TypecheckWorkerInput, TypecheckWorkerResult } from './check'
12
12
 
13
13
  const input = workerData as TypecheckWorkerInput
14
- setNextConfig(input.nextConfig)
14
+ const { nextConfig, ...config } = input
15
+ setNextConfig(nextConfig)
15
16
 
16
17
  let result: TypecheckWorkerResult
17
18
  try {
18
- await validateTypes({ root: input.root, outPath: input.outPath } as ResolvedConfig)
19
+ await validateTypes(config as ResolvedConfig)
19
20
  result = { ok: true }
20
21
  } catch (error) {
21
22
  result =
@@ -15,6 +15,7 @@ import path from 'node:path'
15
15
  import type { ResolvedConfig } from '../../config'
16
16
  import { extraPageExtensions } from '../../extensions'
17
17
  import type { RouteManifestEntry } from '../../types'
18
+ import { legacyRequestAPIs } from '../../request/context'
18
19
  import { getNextConfig } from '../next/config-loader'
19
20
  import { stripComments } from '../../utils/code'
20
21
  import {
@@ -271,6 +272,7 @@ function warnBuildDiagnostics(config: ResolvedConfig, routes: RouteManifestEntry
271
272
  console.warn('Disabled SWC as replacement for Babel because of custom Babel configuration')
272
273
  }
273
274
  if (
275
+ !legacyRequestAPIs() &&
274
276
  typeof nextConfig.outputFileTracingRoot !== 'string' &&
275
277
  !(
276
278
  nextConfig.turbopack &&
@@ -20,6 +20,7 @@
20
20
 
21
21
  import type { ResolvedConfig } from './config'
22
22
  import { onExtensionHostReset } from './extensions'
23
+ import { installFetchHostNormalization } from './runtime/fetch-host'
23
24
 
24
25
  /** True when either Next or React compatibility (or the React compiler) is on. */
25
26
  function compatEnabled(config: ResolvedConfig): boolean {
@@ -43,6 +44,8 @@ onExtensionHostReset(() => {
43
44
  * compile. No-op (and no compat code loaded) for a pure-core app.
44
45
  */
45
46
  export function bootstrapCompatBoot(config: ResolvedConfig): Promise<void> {
47
+ // Core Bun-vs-undici runtime shim, not compat: every app needs it, gated or not.
48
+ installFetchHostNormalization()
46
49
  if (!compatEnabled(config)) return Promise.resolve()
47
50
  bootRegistration ??= import('./compat/register/boot').then(module =>
48
51
  module.registerCompatBootExtensions(config),
@@ -59,6 +62,7 @@ export function bootstrapCompat(
59
62
  config: ResolvedConfig,
60
63
  options: { serve?: boolean } = {},
61
64
  ): Promise<void> {
65
+ installFetchHostNormalization()
62
66
  if (!compatEnabled(config)) return Promise.resolve()
63
67
  registration ??= bootstrapCompatBoot(config)
64
68
  .then(() => import('./compat/register/index'))