@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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/reference/compat.md +2 -2
- package/reference/getting-started.md +2 -2
- package/src/cli/adapters/vercel.ts +27 -0
- package/src/cli/analyze.ts +8 -7
- package/src/cli/boot/trace.ts +9 -3
- package/src/cli/build/cache.ts +324 -0
- package/src/cli/build.ts +42 -18
- package/src/cli/create.ts +4 -4
- package/src/cli/dev.ts +23 -0
- package/src/cli/migrate/package-json.ts +3 -2
- package/src/cli/migrate/report.ts +1 -1
- package/src/cli/migrate/run.ts +2 -2
- package/src/cli/migrate/scan.ts +128 -1
- package/src/cli/serve/pipeline.ts +42 -42
- package/src/cli/start.ts +20 -20
- package/src/client/build.ts +64 -35
- package/src/client/entry.ts +228 -16
- package/src/client/router/page-slot.ts +81 -0
- package/src/client/router/runtime.ts +107 -112
- package/src/compat/bundler/cjs-exports.ts +26 -20
- package/src/compat/client/css-order.ts +1 -1
- package/src/compat/client/navigation-scroll.ts +36 -40
- package/src/compat/css/modules.ts +6 -2
- package/src/compat/metadata.ts +0 -9
- package/src/compat/next/font/cache.ts +10 -22
- package/src/compat/next/font/runtime.ts +20 -0
- package/src/compat/next/legacy-request-apis.ts +41 -5
- package/src/compat/next/script.tsx +16 -24
- package/src/compat/react/client-lite.ts +1 -0
- package/src/compat/react/client.ts +11 -0
- package/src/compat/react/dom-client.ts +11 -2
- package/src/compat/react/dom-react-server.ts +4 -0
- package/src/compat/react/dom.ts +11 -2
- package/src/compat/react/internals.ts +65 -0
- package/src/compat/react/parity.ts +47 -0
- package/src/compat/react/preact.ts +31 -1
- package/src/compat/react/react-server.ts +7 -0
- package/src/compat/react/server.ts +16 -2
- package/src/compat/react/style-values.ts +68 -0
- package/src/compat/register/proxy.ts +3 -1
- package/src/compat/register/render.ts +14 -3
- package/src/compat/register/routing.ts +57 -7
- package/src/compat/register/typed-routes.ts +0 -2
- package/src/compat/tsconfig-defaults.ts +4 -0
- package/src/compat/typecheck/check.ts +26 -71
- package/src/compat/typecheck/worker.ts +3 -2
- package/src/compat/validation/validate.ts +2 -0
- package/src/compat-bootstrap.ts +4 -0
- package/src/css/build.ts +153 -14
- package/src/css/postcss.ts +32 -3
- package/src/dev/restart/client-key.ts +1 -1
- package/src/dev/restart/global-css.ts +12 -94
- package/src/dev/restart/record.ts +106 -0
- package/src/dev/restart/route-facts.ts +11 -90
- package/src/dev/server.ts +208 -41
- package/src/extensions.ts +69 -3
- package/src/render/hooks.ts +1 -1
- package/src/render/metadata.ts +89 -17
- package/src/render/renderer.ts +325 -138
- package/src/render/resource-hints.ts +62 -9
- package/src/render/slots.tsx +2 -1
- package/src/resolve/scan-facts.ts +13 -5
- package/src/routing/forwarded.ts +20 -7
- package/src/routing/handler.ts +6 -2
- package/src/routing/href.ts +11 -1
- package/src/routing/metadata-files.ts +16 -3
- package/src/routing/proxy.ts +6 -3
- package/src/routing/routes.ts +253 -34
- package/src/runtime/fetch-host.ts +42 -0
- package/src/runtime/fingerprint.ts +132 -0
- package/src/runtime/loader.ts +7 -5
- package/src/runtime/module-cache.ts +3 -36
- package/src/runtime/modules.ts +333 -77
- package/src/runtime/vendor-build.ts +34 -5
- package/src/runtime/vendor.ts +1 -18
- package/src/types.ts +6 -0
- package/src/utils/asset-hash.ts +22 -0
- package/src/utils/code.ts +41 -0
- package/src/utils/html.ts +4 -1
- package/src/utils/registry.ts +70 -0
- 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
|
-
|
|
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: () =>
|
|
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.
|
|
239
|
-
//
|
|
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
|
-
|
|
242
|
-
|
|
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
|
-
|
|
412
|
-
|
|
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
|
|
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
|
-
|
|
353
|
-
//
|
|
354
|
-
// React
|
|
355
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14
|
+
const { nextConfig, ...config } = input
|
|
15
|
+
setNextConfig(nextConfig)
|
|
15
16
|
|
|
16
17
|
let result: TypecheckWorkerResult
|
|
17
18
|
try {
|
|
18
|
-
await validateTypes(
|
|
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 &&
|
package/src/compat-bootstrap.ts
CHANGED
|
@@ -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'))
|