@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
|
@@ -1,15 +1,62 @@
|
|
|
1
1
|
import type { ResourceHint } from '../types'
|
|
2
2
|
|
|
3
|
-
declare global {
|
|
4
|
-
var __PNEXT_RESOURCE_HINTS__: ResourceHint[] | undefined
|
|
5
|
-
}
|
|
6
|
-
|
|
7
3
|
// Optional server-side observer for every registered hint (compat's Link response-header emission).
|
|
8
4
|
// Anchored on globalThis because react-dom's preload()/preinit() shims are compiled into app bundles as
|
|
9
5
|
// separate module instances - only a global slot lets the runtime-registered listener see the
|
|
10
6
|
// bundle-registered hints. No-op for pure-core apps.
|
|
11
7
|
const HINT_LISTENER = Symbol.for('pnext.resourceHintListener')
|
|
12
8
|
|
|
9
|
+
// The buffers a render fills and the renderer drains at document assembly: resource hints, the
|
|
10
|
+
// beforeInteractive head scripts and the deferred script runtime. One global array per kind is not
|
|
11
|
+
// enough - two renders alive at once in one process (a build prerendering a route beside the
|
|
12
|
+
// not-found document, or two concurrent requests) then share it, and whichever assembles first
|
|
13
|
+
// drains the other's preloads and head scripts. So a render gets a FRAME, and the buffers live in
|
|
14
|
+
// it; outside a render they stay on globalThis, where the next render start drops them.
|
|
15
|
+
//
|
|
16
|
+
// The frame store is created by the renderer (server-only, it owns node:async_hooks) and read here
|
|
17
|
+
// through the same globalThis anchor as the work unit: this module is compiled into client bundles
|
|
18
|
+
// and into every compat bundle's own copy, and only a shared slot lets those copies write into the
|
|
19
|
+
// frame the renderer reads.
|
|
20
|
+
export const RENDER_BUFFER_SCOPE = Symbol.for('pnext.renderBufferScope')
|
|
21
|
+
const RENDER_BUFFER_STRAYS = Symbol.for('pnext.renderBufferStrays')
|
|
22
|
+
|
|
23
|
+
export type RenderBufferKind = 'hints' | 'head' | 'deferred'
|
|
24
|
+
export type RenderBufferFrame = Record<RenderBufferKind, unknown[]>
|
|
25
|
+
|
|
26
|
+
export const newRenderBufferFrame = (): RenderBufferFrame => ({ hints: [], head: [], deferred: [] })
|
|
27
|
+
|
|
28
|
+
const globals = globalThis as Record<PropertyKey, unknown>
|
|
29
|
+
|
|
30
|
+
/** Duck-typed store read, so this module never pulls node:async_hooks into a client bundle. */
|
|
31
|
+
function frame(): RenderBufferFrame {
|
|
32
|
+
const scope = globals[RENDER_BUFFER_SCOPE] as
|
|
33
|
+
{ getStore(): RenderBufferFrame | undefined } | undefined
|
|
34
|
+
return (
|
|
35
|
+
scope?.getStore() ??
|
|
36
|
+
((globals[RENDER_BUFFER_STRAYS] ??= newRenderBufferFrame()) as RenderBufferFrame)
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The current render's buffer, or the process-wide stray one outside a render. */
|
|
41
|
+
export function renderBuffer<T>(kind: RenderBufferKind): T[] {
|
|
42
|
+
return frame()[kind] as T[]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Hand the buffer to the document being assembled and leave a fresh one behind. */
|
|
46
|
+
export function takeRenderBuffer<T>(kind: RenderBufferKind): T[] {
|
|
47
|
+
const active = frame()
|
|
48
|
+
const buffer = active[kind]
|
|
49
|
+
active[kind] = []
|
|
50
|
+
return buffer as T[]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Drop the frame's buffer AND the stray one, so out-of-render writes never accumulate. */
|
|
54
|
+
export function clearRenderBuffer(kind: RenderBufferKind): void {
|
|
55
|
+
frame()[kind] = []
|
|
56
|
+
const strays = globals[RENDER_BUFFER_STRAYS] as RenderBufferFrame | undefined
|
|
57
|
+
if (strays) strays[kind] = []
|
|
58
|
+
}
|
|
59
|
+
|
|
13
60
|
export function setResourceHintListener(listener: ((hint: ResourceHint) => void) | undefined) {
|
|
14
61
|
;(globalThis as Record<PropertyKey, unknown>)[HINT_LISTENER] = listener
|
|
15
62
|
}
|
|
@@ -22,22 +69,28 @@ export function registerResourceHint(hint: ResourceHint): void {
|
|
|
22
69
|
const listener = (globalThis as Record<PropertyKey, unknown>)[HINT_LISTENER] as
|
|
23
70
|
((hint: ResourceHint) => void) | undefined
|
|
24
71
|
listener?.(hint)
|
|
25
|
-
const hints = (
|
|
72
|
+
const hints = renderBuffer<ResourceHint>('hints')
|
|
26
73
|
if (hints.some(existing => resourceHintKey(existing) === resourceHintKey(hint))) return
|
|
27
74
|
hints.push(hint)
|
|
28
75
|
}
|
|
29
76
|
|
|
30
77
|
export function clearResourceHints(): void {
|
|
31
|
-
|
|
78
|
+
clearRenderBuffer('hints')
|
|
32
79
|
}
|
|
33
80
|
|
|
34
81
|
export function takeResourceHints(): ResourceHint[] {
|
|
35
|
-
|
|
36
|
-
globalThis.__PNEXT_RESOURCE_HINTS__ = []
|
|
37
|
-
return hints
|
|
82
|
+
return takeRenderBuffer<ResourceHint>('hints')
|
|
38
83
|
}
|
|
39
84
|
|
|
40
85
|
function resourceHintKey(hint: ResourceHint): string {
|
|
86
|
+
// React Float dedupes image preloads by the image URL, not the full attribute
|
|
87
|
+
// set. next/image may register the same optimized candidate more than once
|
|
88
|
+
// while a shared layout renders, and the first registration owns its shape.
|
|
89
|
+
if (hint.rel === 'preload' && hint.as === 'image') {
|
|
90
|
+
const srcSet = hint.attributes?.imagesrcset
|
|
91
|
+
const first = typeof srcSet === 'string' ? srcSet.split(/[\s,]+/)[0] : undefined
|
|
92
|
+
return `image:${first || hint.url || ''}`
|
|
93
|
+
}
|
|
41
94
|
const attributes = Object.entries(hint.attributes ?? {}).sort(([a], [b]) => a.localeCompare(b))
|
|
42
95
|
return JSON.stringify({ ...hint, attributes })
|
|
43
96
|
}
|
package/src/render/slots.tsx
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from '../routing/slots'
|
|
19
19
|
import { toPosixPath } from '../utils/fs'
|
|
20
20
|
import { PROMISE_MARKER_KEY, revivePromiseMarkers } from '../utils/serialize'
|
|
21
|
+
import { getRenderExtensions } from '../extensions'
|
|
21
22
|
import type { PageProps, RouteManifestEntry, RouteParamValue, ServerComponent } from '../types'
|
|
22
23
|
|
|
23
24
|
interface SlotRenderOptions {
|
|
@@ -423,7 +424,7 @@ export function promisePlaceholderJson(kind: 'params' | 'searchParams'): string
|
|
|
423
424
|
* entry runs, so a nested marker is a promise on both sides rather than a bare object on one.
|
|
424
425
|
*/
|
|
425
426
|
export function revivePromiseProps(props: Record<string, unknown>) {
|
|
426
|
-
return revivePromiseMarkers({ ...props })
|
|
427
|
+
return revivePromiseMarkers({ ...props }, getRenderExtensions().legacySyncProps)
|
|
427
428
|
}
|
|
428
429
|
|
|
429
430
|
function slotPageProps<Options extends SlotRenderOptions>(
|
|
@@ -115,6 +115,8 @@ export interface ScanFacts {
|
|
|
115
115
|
exportStars: string[]
|
|
116
116
|
/** A real `'use client'` directive — prologue position only, never a comment. */
|
|
117
117
|
useClient: boolean
|
|
118
|
+
/** A real module-level `'use server'` directive, on the same terms. */
|
|
119
|
+
useServer: boolean
|
|
118
120
|
/** Loader-shaped calls (see `DynamicCallFact`); only sources that can bind `dynamic` are walked. */
|
|
119
121
|
dynamicCalls: DynamicCallFact[]
|
|
120
122
|
}
|
|
@@ -128,6 +130,7 @@ const emptyFacts: ScanFacts = {
|
|
|
128
130
|
exportNames: [],
|
|
129
131
|
exportStars: [],
|
|
130
132
|
useClient: false,
|
|
133
|
+
useServer: false,
|
|
131
134
|
dynamicCalls: [],
|
|
132
135
|
}
|
|
133
136
|
|
|
@@ -378,7 +381,8 @@ function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
|
378
381
|
requires,
|
|
379
382
|
exportNames,
|
|
380
383
|
exportStars,
|
|
381
|
-
useClient:
|
|
384
|
+
useClient: moduleDirective(source, result, 'use client'),
|
|
385
|
+
useServer: moduleDirective(source, result, 'use server'),
|
|
382
386
|
// `result.program` deserializes the whole AST: walk it only for sources
|
|
383
387
|
// that can bind `dynamic()` at all (same cheap gate as resolve/dynamic.ts).
|
|
384
388
|
dynamicCalls:
|
|
@@ -400,12 +404,16 @@ function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
|
400
404
|
* was its only consumer. The raw-source scanner answers it without the AST; the AST is still
|
|
401
405
|
* consulted for the prologue shapes the scanner will not commit to.
|
|
402
406
|
*/
|
|
403
|
-
function
|
|
404
|
-
|
|
407
|
+
function moduleDirective(
|
|
408
|
+
source: string,
|
|
409
|
+
result: { program: { body: readonly Directive[] } },
|
|
410
|
+
directive: string,
|
|
411
|
+
) {
|
|
412
|
+
const sniffed = sniffDirective(source, directive)
|
|
405
413
|
if (sniffed !== undefined) return sniffed
|
|
406
414
|
// Uncertain, but a directive is its raw text: no occurrence, no directive.
|
|
407
|
-
if (!source.includes(
|
|
408
|
-
return hasDirective(result.program.body,
|
|
415
|
+
if (!source.includes(directive)) return false
|
|
416
|
+
return hasDirective(result.program.body, directive)
|
|
409
417
|
}
|
|
410
418
|
|
|
411
419
|
interface Directive {
|
package/src/routing/forwarded.ts
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
// x-forwarded-* request header injection (CORE HTTP correctness).
|
|
2
2
|
//
|
|
3
3
|
// Before route matching / render, the server records the observed host, port,
|
|
4
|
-
// and
|
|
5
|
-
// any request-header reader (middleware, route handlers) see them. Client-sent
|
|
4
|
+
// protocol, and peer address on the incoming request headers so headers() during
|
|
5
|
+
// render and any request-header reader (middleware, route handlers) see them. Client-sent
|
|
6
6
|
// x-forwarded-* values are never overridden (a trusted proxy already set them),
|
|
7
7
|
// including multi-value forms like "https, https".
|
|
8
8
|
|
|
9
9
|
const FORWARDED_HOST = 'x-forwarded-host'
|
|
10
10
|
const FORWARDED_PORT = 'x-forwarded-port'
|
|
11
11
|
const FORWARDED_PROTO = 'x-forwarded-proto'
|
|
12
|
+
const FORWARDED_FOR = 'x-forwarded-for'
|
|
13
|
+
|
|
14
|
+
/** The serving runtime, narrowed to the peer-address lookup (Bun's `Server`). */
|
|
15
|
+
export interface PeerAddressSource {
|
|
16
|
+
requestIP(request: Request): { address: string } | null
|
|
17
|
+
}
|
|
12
18
|
|
|
13
19
|
/**
|
|
14
|
-
* Return the request with x-forwarded-host/port/proto injected from the
|
|
15
|
-
* URL + Host header, preserving any client-sent values.
|
|
16
|
-
* request when every value was already present (no allocation
|
|
20
|
+
* Return the request with x-forwarded-host/port/proto/for injected from the
|
|
21
|
+
* request URL + Host header + peer address, preserving any client-sent values.
|
|
22
|
+
* Returns the same request when every value was already present (no allocation
|
|
23
|
+
* on the hot path).
|
|
17
24
|
*/
|
|
18
|
-
export function withForwardedHeaders(request: Request): Request {
|
|
25
|
+
export function withForwardedHeaders(request: Request, peer?: PeerAddressSource): Request {
|
|
19
26
|
const headers = request.headers
|
|
20
27
|
const hasHost = headers.has(FORWARDED_HOST)
|
|
21
28
|
const hasPort = headers.has(FORWARDED_PORT)
|
|
22
29
|
const hasProto = headers.has(FORWARDED_PROTO)
|
|
23
|
-
|
|
30
|
+
const hasFor = headers.has(FORWARDED_FOR)
|
|
31
|
+
if (hasHost && hasPort && hasProto && (hasFor || !peer)) return request
|
|
24
32
|
|
|
25
33
|
let url: URL
|
|
26
34
|
try {
|
|
@@ -37,5 +45,10 @@ export function withForwardedHeaders(request: Request): Request {
|
|
|
37
45
|
if (!hasHost) next.set(FORWARDED_HOST, host)
|
|
38
46
|
if (!hasPort) next.set(FORWARDED_PORT, port)
|
|
39
47
|
if (!hasProto) next.set(FORWARDED_PROTO, proto)
|
|
48
|
+
// Next sets it from the socket's remoteAddress and never appends to a client-sent value.
|
|
49
|
+
if (!hasFor && peer) {
|
|
50
|
+
const address = peer.requestIP(request)?.address
|
|
51
|
+
if (address) next.set(FORWARDED_FOR, address)
|
|
52
|
+
}
|
|
40
53
|
return new Request(request, { headers: next })
|
|
41
54
|
}
|
package/src/routing/handler.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isNotFoundError, isRedirectError } from '../api/navigation'
|
|
2
2
|
import { getWorkUnit, runWithRequest } from '../request/context'
|
|
3
3
|
import { nextResponseMeta, toNextRequest } from '../api/server'
|
|
4
|
-
import { reportRequestError } from '../extensions'
|
|
4
|
+
import { getRenderExtensions, reportRequestError } from '../extensions'
|
|
5
5
|
import type { NextRequest, RouteManifestEntry, RouteParamValue } from '../types'
|
|
6
6
|
import { safeDecode } from '../utils/decode'
|
|
7
7
|
|
|
@@ -156,7 +156,11 @@ function callHandler(
|
|
|
156
156
|
// Static routes (no dynamic segments) get no `params` in context — Next
|
|
157
157
|
// omits it, so `context.params` is `undefined` rather than a promise of `{}`.
|
|
158
158
|
const context: RouteHandlerContext =
|
|
159
|
-
Object.keys(params).length > 0
|
|
159
|
+
Object.keys(params).length > 0
|
|
160
|
+
? {
|
|
161
|
+
params: getRenderExtensions().legacySyncProps(Promise.resolve(params), 'params', params),
|
|
162
|
+
}
|
|
163
|
+
: {}
|
|
160
164
|
return runWithRequest(nextRequest, () => handler(nextRequest, context), params, {
|
|
161
165
|
responseHeaders,
|
|
162
166
|
})
|
package/src/routing/href.ts
CHANGED
|
@@ -9,6 +9,16 @@ export interface HrefParts {
|
|
|
9
9
|
hash?: string
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* A request for pnext's own build output: `/assets/*`, and next-compat's `/_next/static/*` - the
|
|
14
|
+
* same output under the path Next serves it from (see assetPathname). Every bypass that must not
|
|
15
|
+
* treat build output as a page (proxy, trailing-slash canonicalization) asks HERE, so moving the
|
|
16
|
+
* emitted prefix can never leave a bypass knowing only the old one.
|
|
17
|
+
*/
|
|
18
|
+
export function isBuildAssetPathname(pathname: string) {
|
|
19
|
+
return pathname.startsWith('/assets/') || pathname.startsWith('/_next/static/')
|
|
20
|
+
}
|
|
21
|
+
|
|
12
22
|
export function routeHref(route: string, options: HrefParts = {}) {
|
|
13
23
|
const path = applyTrailingSlash(fillParams(route, options.params ?? {}))
|
|
14
24
|
const search = searchString(options.search)
|
|
@@ -209,7 +219,7 @@ export function trailingSlashRedirect(
|
|
|
209
219
|
if (method !== 'GET' && method !== 'HEAD') return null
|
|
210
220
|
const pathname = url.pathname
|
|
211
221
|
if (pathname === '/' || pathname === '') return null
|
|
212
|
-
if (pathname.startsWith('/__pnext') || pathname
|
|
222
|
+
if (pathname.startsWith('/__pnext') || isBuildAssetPathname(pathname)) return null
|
|
213
223
|
const target = canonicalTrailingSlashPath(pathname, Boolean(config.trailingSlash))
|
|
214
224
|
if (target === pathname) return null
|
|
215
225
|
return new Response(null, {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
1
2
|
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
2
3
|
import path from 'node:path'
|
|
3
4
|
import type {
|
|
@@ -144,8 +145,12 @@ export async function withDynamicMetadataRoutes(
|
|
|
144
145
|
pathname: string,
|
|
145
146
|
loadModule: MetadataModuleLoader,
|
|
146
147
|
): Promise<StaticMetadataForPath> {
|
|
148
|
+
// Every render with no prebuilt metadata reaches here, so an app with no
|
|
149
|
+
// manifest/icon/sitemap modules pays one memoized lookup and no copy.
|
|
150
|
+
const discovered = discoverDynamicMetadataFiles(appPath)
|
|
151
|
+
if (discovered.length === 0) return metadata
|
|
147
152
|
const routeDir = toPosixPath(path.relative(appPath, path.dirname(routeFile)))
|
|
148
|
-
const files =
|
|
153
|
+
const files = discovered
|
|
149
154
|
.map(file => matchDynamicMetadataFile(file, pathname, routeDir))
|
|
150
155
|
.filter((file): file is MatchedDynamicMetadataFile => file !== undefined)
|
|
151
156
|
|
|
@@ -241,6 +246,7 @@ function staticMetadataFile(appPath: string, file: string): StaticMetadataFile |
|
|
|
241
246
|
outputPath: toPosixPath(path.join(outputDir, outputName)),
|
|
242
247
|
routeSegments,
|
|
243
248
|
contentType: contentTypeForFile(name),
|
|
249
|
+
...(kind === 'manifest' ? {} : { cacheIdentity: metadataFileIdentity(file) }),
|
|
244
250
|
...(info?.width ? { width: info.width } : {}),
|
|
245
251
|
...(info?.height ? { height: info.height } : {}),
|
|
246
252
|
...(info?.sizes ? { sizes: info.sizes } : {}),
|
|
@@ -296,8 +302,15 @@ function metadataImage(file: StaticMetadataFile): MetadataImage {
|
|
|
296
302
|
}
|
|
297
303
|
|
|
298
304
|
function staticMetadataUrl(file: StaticMetadataFile) {
|
|
299
|
-
|
|
300
|
-
|
|
305
|
+
// File-convention images carry their content identity in the query so an
|
|
306
|
+
// image replacement cannot be served from an old browser cache. Manifests
|
|
307
|
+
// are the one non-image convention represented by this type.
|
|
308
|
+
if (file.kind === 'manifest') return `/${file.outputPath}`
|
|
309
|
+
return `/${file.outputPath}?${file.kind}.${file.cacheIdentity}.${path.extname(file.outputPath).slice(1)}`
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function metadataFileIdentity(file: string) {
|
|
313
|
+
return createHash('sha256').update(readFileSync(file)).digest('base64url').slice(0, 16)
|
|
301
314
|
}
|
|
302
315
|
|
|
303
316
|
function nearestFile(files: StaticMetadataFile[], kind: StaticMetadataKind) {
|
package/src/routing/proxy.ts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
} from '../api/server'
|
|
22
22
|
import type { ExternalLoadTarget } from '../resolve/imports'
|
|
23
23
|
import { getProxyExtensions, withRouteRuntime } from '../extensions'
|
|
24
|
-
import { canonicalTrailingSlashPath } from './href'
|
|
24
|
+
import { canonicalTrailingSlashPath, isBuildAssetPathname } from './href'
|
|
25
25
|
|
|
26
26
|
export interface ProxyModule {
|
|
27
27
|
default?: ProxyHandler
|
|
@@ -126,7 +126,7 @@ export async function runProxy(
|
|
|
126
126
|
options: ProxyRunOptions = {},
|
|
127
127
|
): Promise<ProxyNextResult | Response | undefined> {
|
|
128
128
|
const url = new URL(request.url)
|
|
129
|
-
if (url.pathname.startsWith('/__pnext/') || url.pathname
|
|
129
|
+
if (url.pathname.startsWith('/__pnext/') || isBuildAssetPathname(url.pathname)) {
|
|
130
130
|
return undefined
|
|
131
131
|
}
|
|
132
132
|
|
|
@@ -165,7 +165,10 @@ export function createProxyRunner(
|
|
|
165
165
|
|
|
166
166
|
const runner: ProxyRunner = async (request, options = {}) => {
|
|
167
167
|
const url = new URL(request.url)
|
|
168
|
-
if (
|
|
168
|
+
if (
|
|
169
|
+
url.pathname.startsWith('/__pnext/') ||
|
|
170
|
+
(!config.compat?.next && isBuildAssetPathname(url.pathname))
|
|
171
|
+
) {
|
|
169
172
|
return undefined
|
|
170
173
|
}
|
|
171
174
|
if (!file) return undefined
|