@wular/pnext 0.0.21 → 0.0.23
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/package.json +1 -1
- package/src/cli/create.ts +25 -40
- package/src/cli/serve/entry.ts +14 -4
- package/src/cli/serve/pipeline.ts +4 -0
- package/src/cli/serve/ui.ts +1 -1
- package/src/cli/start.ts +9 -0
- package/src/cli/template/README.md +10 -0
- package/src/cli/template/app/client/page.tsx +30 -0
- package/src/cli/template/app/globals.css +322 -0
- package/src/cli/template/app/layout.tsx +33 -0
- package/src/cli/template/app/page.tsx +29 -0
- package/src/cli/template/app/server/page.tsx +27 -0
- package/src/cli/template/gitignore +2 -0
- package/src/cli/template/package.json +18 -0
- package/src/cli/template/pnext.config.ts +2 -0
- package/src/cli/template/tsconfig.json +10 -0
- package/src/cli/typegen.ts +4 -1
- package/src/client/router/runtime.ts +20 -6
- package/src/compat/metadata.ts +44 -2
- package/src/compat/next/config-loader.ts +7 -1
- package/src/compat/register/typed-routes.ts +4 -0
- package/src/compat/typecheck/check.ts +5 -1
- package/src/config.ts +7 -0
- package/src/dev/server.ts +16 -4
- package/src/extensions.ts +7 -1
- package/src/render/metadata.ts +53 -31
- package/src/render/renderer.ts +33 -10
- package/src/resolve/imports.ts +7 -1
- package/src/resolve/scan-facts.ts +7 -1
- package/src/runtime/fetch-host.ts +46 -2
- package/src/types.ts +84 -67
- package/tsconfig.json +1 -1
package/src/resolve/imports.ts
CHANGED
|
@@ -11,6 +11,12 @@ import {
|
|
|
11
11
|
} from './engine'
|
|
12
12
|
import { onExtensionHostReset } from '../extensions'
|
|
13
13
|
|
|
14
|
+
// `Bun` through globalThis: pnext ships TypeScript source, so app compilers without
|
|
15
|
+
// bun-types typecheck this file and a bare `Bun` identifier would fail them.
|
|
16
|
+
const { Bun: bun } = globalThis as unknown as {
|
|
17
|
+
Bun: { resolveSync(specifier: string, parent: string): string }
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
// Conditions for "is this a local source file" lookups. Ordered to mirror the
|
|
15
21
|
// preference a bundler applies, though `exports` key order ultimately decides.
|
|
16
22
|
const localConditions = ['source', 'import', 'browser', 'node', 'default', 'require']
|
|
@@ -668,7 +674,7 @@ export function resolveRuntimeSpecifier(dir: string, specifier: string): string
|
|
|
668
674
|
const cached = runtimeResolveCache.get(key)
|
|
669
675
|
if (cached !== undefined) return cached
|
|
670
676
|
try {
|
|
671
|
-
const resolved =
|
|
677
|
+
const resolved = bun.resolveSync(specifier, dir)
|
|
672
678
|
runtimeResolveCache.set(key, resolved)
|
|
673
679
|
return resolved
|
|
674
680
|
} catch {
|
|
@@ -17,6 +17,12 @@ const parseSync: typeof import('oxc-parser').parseSync = (...args) =>
|
|
|
17
17
|
import { loadNative } from '../utils/native-require'
|
|
18
18
|
import { spliceSource } from '../runtime/module-transform'
|
|
19
19
|
|
|
20
|
+
// `Bun` through globalThis: pnext ships TypeScript source, so app compilers without
|
|
21
|
+
// bun-types typecheck this file and a bare `Bun` identifier would fail them.
|
|
22
|
+
const { Bun: bun } = globalThis as unknown as {
|
|
23
|
+
Bun: { hash(input: string | ArrayBufferView): { toString(radix?: number): string } }
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
export interface ScanEdge {
|
|
21
27
|
specifier: string
|
|
22
28
|
/** Bound names: `default`, `*` (namespace object), or the imported names. */
|
|
@@ -187,7 +193,7 @@ export function rewriteFacts(file: string, source: string): RewriteFacts {
|
|
|
187
193
|
}
|
|
188
194
|
|
|
189
195
|
function parsed(file: string, source: string, lang = langForFile(file)) {
|
|
190
|
-
const key = `${lang}\0${source.length}\0${
|
|
196
|
+
const key = `${lang}\0${source.length}\0${bun.hash(source).toString()}`
|
|
191
197
|
const cached = memo.get(key)
|
|
192
198
|
if (cached) {
|
|
193
199
|
hits += 1
|
|
@@ -3,10 +3,18 @@
|
|
|
3
3
|
* always derives Host from the request URL and drops any supplied one. An app that forwards its
|
|
4
4
|
* inbound request headers to an upstream - every proxy/BFF route handler - would otherwise send
|
|
5
5
|
* the local Host upstream, an RFC 9110 7.2 violation that real upstreams reject. Match undici.
|
|
6
|
+
*
|
|
7
|
+
* Bun also ignores response-body cancellation: cancelling a fetch Response's stream leaves the
|
|
8
|
+
* upstream request in flight and its keep-alive socket checked out for good, so a passthrough proxy
|
|
9
|
+
* route whose client disconnects mid-stream leaks a connection per abort until later fetches to that
|
|
10
|
+
* host queue behind them forever. Only the fetch's own AbortSignal tears the request down, so each
|
|
11
|
+
* response body is tracked against its controller here and the serving layer fires it on disconnect.
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
14
|
let installed = false
|
|
9
15
|
|
|
16
|
+
const upstreamAborts = new WeakMap<ReadableStream<Uint8Array>, AbortController>()
|
|
17
|
+
|
|
10
18
|
function isHostKey(key: unknown): boolean {
|
|
11
19
|
return typeof key === 'string' && key.length === 4 && key.toLowerCase() === 'host'
|
|
12
20
|
}
|
|
@@ -36,7 +44,43 @@ export function installFetchHostNormalization(): void {
|
|
|
36
44
|
const original = globalThis.fetch.bind(globalThis)
|
|
37
45
|
globalThis.fetch = function fetch(input: RequestInfo | URL, init?: RequestInit) {
|
|
38
46
|
const source = init?.headers ?? (input instanceof Request ? input.headers : undefined)
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
const normalized =
|
|
48
|
+
source !== undefined && hasHost(source) ? { ...init, headers: withoutHost(source) } : init
|
|
49
|
+
return trackUpstream(original, input, normalized)
|
|
41
50
|
} as typeof globalThis.fetch
|
|
42
51
|
}
|
|
52
|
+
|
|
53
|
+
/** Give every fetch an abort handle of its own, keyed on the body it streams back. */
|
|
54
|
+
function trackUpstream(
|
|
55
|
+
original: typeof globalThis.fetch,
|
|
56
|
+
input: RequestInfo | URL,
|
|
57
|
+
init: RequestInit | undefined,
|
|
58
|
+
): Promise<Response> {
|
|
59
|
+
const controller = new AbortController()
|
|
60
|
+
const caller = init?.signal ?? (input instanceof Request ? input.signal : undefined)
|
|
61
|
+
const signal = caller ? AbortSignal.any([caller, controller.signal]) : controller.signal
|
|
62
|
+
return original(input, { ...init, signal }).then(response => {
|
|
63
|
+
if (response.body) upstreamAborts.set(response.body, controller)
|
|
64
|
+
return response
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Tear down the upstream fetch `body` is streaming from once the client goes away. Bun.serve never
|
|
70
|
+
* cancels the body it is writing, so this disconnect is the only chance to release that socket.
|
|
71
|
+
* Zero-cost for any response that is not a fetch passthrough.
|
|
72
|
+
*/
|
|
73
|
+
export function abortUpstreamFetchOnDisconnect(
|
|
74
|
+
body: ReadableStream<Uint8Array> | null,
|
|
75
|
+
signal: AbortSignal | undefined,
|
|
76
|
+
): void {
|
|
77
|
+
if (!body || !signal || !upstreamAborts.has(body)) return
|
|
78
|
+
const abort = () => {
|
|
79
|
+
const controller = upstreamAborts.get(body)
|
|
80
|
+
if (!controller) return
|
|
81
|
+
upstreamAborts.delete(body)
|
|
82
|
+
controller.abort()
|
|
83
|
+
}
|
|
84
|
+
if (signal.aborted) abort()
|
|
85
|
+
else signal.addEventListener('abort', abort, { once: true })
|
|
86
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -95,7 +95,8 @@ export interface ResourceHint extends Omit<MetadataLink, 'url'> {
|
|
|
95
95
|
export type MetadataTitle =
|
|
96
96
|
string | number | { absolute?: string; default?: string; template?: string | null }
|
|
97
97
|
|
|
98
|
-
export type MetadataIcon =
|
|
98
|
+
export type MetadataIcon =
|
|
99
|
+
string | URL | (Omit<Partial<MetadataLink>, 'url'> & { url: string | URL })
|
|
99
100
|
|
|
100
101
|
export interface MetadataIconGroups {
|
|
101
102
|
icon?: MetadataIcon | MetadataIcon[]
|
|
@@ -116,7 +117,7 @@ export interface MetadataRobots {
|
|
|
116
117
|
nosnippet?: boolean
|
|
117
118
|
noimageindex?: boolean
|
|
118
119
|
nocache?: boolean
|
|
119
|
-
googleBot?: MetadataRobots & Record<string, string | number | boolean | undefined>
|
|
120
|
+
googleBot?: string | (MetadataRobots & Record<string, string | number | boolean | undefined>)
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
export type MetadataImage =
|
|
@@ -134,7 +135,7 @@ export type MetadataImage =
|
|
|
134
135
|
export interface MetadataOpenGraph {
|
|
135
136
|
title?: MetadataTitle
|
|
136
137
|
description?: string
|
|
137
|
-
url?: string | URL
|
|
138
|
+
url?: string | URL | null
|
|
138
139
|
siteName?: string
|
|
139
140
|
locale?: string
|
|
140
141
|
type?: string
|
|
@@ -142,109 +143,125 @@ export interface MetadataOpenGraph {
|
|
|
142
143
|
videos?: MetadataImage | MetadataImage[]
|
|
143
144
|
audio?: MetadataImage | MetadataImage[]
|
|
144
145
|
publishedTime?: string
|
|
145
|
-
authors?: string | string[]
|
|
146
|
+
authors?: string | URL | (string | URL)[] | null
|
|
146
147
|
emails?: string | string[]
|
|
147
148
|
phoneNumbers?: string | string[]
|
|
148
149
|
faxNumbers?: string | string[]
|
|
149
150
|
}
|
|
150
151
|
|
|
152
|
+
export interface MetadataTwitterPlayer {
|
|
153
|
+
playerUrl?: string | URL
|
|
154
|
+
streamUrl?: string | URL
|
|
155
|
+
width?: string | number
|
|
156
|
+
height?: string | number
|
|
157
|
+
}
|
|
158
|
+
|
|
151
159
|
export interface MetadataTwitter {
|
|
152
160
|
card?: string
|
|
153
|
-
site?: string
|
|
154
|
-
siteId?: string
|
|
155
|
-
creator?: string
|
|
156
|
-
creatorId?: string
|
|
161
|
+
site?: string | null
|
|
162
|
+
siteId?: string | null
|
|
163
|
+
creator?: string | null
|
|
164
|
+
creatorId?: string | null
|
|
157
165
|
title?: MetadataTitle
|
|
158
|
-
description?: string
|
|
166
|
+
description?: string | null
|
|
159
167
|
images?: MetadataImage | MetadataImage[]
|
|
160
|
-
players?:
|
|
161
|
-
playerUrl?: string | URL
|
|
162
|
-
streamUrl?: string | URL
|
|
163
|
-
width?: string | number
|
|
164
|
-
height?: string | number
|
|
165
|
-
}
|
|
168
|
+
players?: MetadataTwitterPlayer | MetadataTwitterPlayer[]
|
|
166
169
|
app?: {
|
|
167
170
|
name?: string
|
|
168
|
-
id?: Record<string,
|
|
171
|
+
id?: Record<string, MetadataValue | undefined>
|
|
169
172
|
url?: Record<string, string | URL | undefined>
|
|
170
173
|
}
|
|
171
174
|
}
|
|
172
175
|
|
|
176
|
+
export type MetadataAlternate =
|
|
177
|
+
| string
|
|
178
|
+
| URL
|
|
179
|
+
| { url: string | URL; title?: string }
|
|
180
|
+
| (string | URL | { url: string | URL; title?: string })[]
|
|
181
|
+
|
|
173
182
|
export interface MetadataAlternates {
|
|
174
|
-
canonical?: string | URL | { url: string | URL }
|
|
175
|
-
languages?: Record<string,
|
|
176
|
-
media?: Record<string,
|
|
177
|
-
types?: Record<
|
|
178
|
-
string,
|
|
179
|
-
| string
|
|
180
|
-
| URL
|
|
181
|
-
| { url: string | URL; title?: string }
|
|
182
|
-
| (string | URL | { url: string | URL; title?: string })[]
|
|
183
|
-
>
|
|
183
|
+
canonical?: string | URL | { url: string | URL; title?: string } | null
|
|
184
|
+
languages?: Record<string, MetadataAlternate | null | undefined>
|
|
185
|
+
media?: Record<string, MetadataAlternate | null | undefined>
|
|
186
|
+
types?: Record<string, MetadataAlternate | null | undefined>
|
|
184
187
|
}
|
|
185
188
|
|
|
189
|
+
// Every field is nullable: a metadata object may explicitly null a field out to
|
|
190
|
+
// suppress an inherited value, and null reads the same as absent here.
|
|
186
191
|
export interface Metadata {
|
|
187
|
-
metadataBase?: string | URL
|
|
188
|
-
title?: MetadataTitle
|
|
189
|
-
description?: string
|
|
190
|
-
lang?: string
|
|
191
|
-
keywords?: string | number | (string | number)[]
|
|
192
|
-
authors?: string | MetadataAuthor | (string | MetadataAuthor)[]
|
|
193
|
-
generator?: string
|
|
194
|
-
applicationName?: string
|
|
195
|
-
referrer?: string
|
|
196
|
-
robots?: string | MetadataRobots
|
|
197
|
-
openGraph?: MetadataOpenGraph
|
|
198
|
-
twitter?: MetadataTwitter
|
|
199
|
-
alternates?: MetadataAlternates
|
|
192
|
+
metadataBase?: string | URL | null
|
|
193
|
+
title?: MetadataTitle | null
|
|
194
|
+
description?: string | null
|
|
195
|
+
lang?: string | null
|
|
196
|
+
keywords?: string | number | (string | number)[] | null
|
|
197
|
+
authors?: string | MetadataAuthor | (string | MetadataAuthor)[] | null
|
|
198
|
+
generator?: string | null
|
|
199
|
+
applicationName?: string | null
|
|
200
|
+
referrer?: string | null
|
|
201
|
+
robots?: string | MetadataRobots | null
|
|
202
|
+
openGraph?: MetadataOpenGraph | null
|
|
203
|
+
twitter?: MetadataTwitter | null
|
|
204
|
+
alternates?: MetadataAlternates | null
|
|
200
205
|
pagination?: {
|
|
201
|
-
previous?: string | URL
|
|
202
|
-
next?: string | URL
|
|
203
|
-
}
|
|
204
|
-
icons?: MetadataIcon | MetadataIcon[] | MetadataIconGroups
|
|
205
|
-
manifest?: string | URL
|
|
206
|
-
creator?: string
|
|
207
|
-
publisher?: string
|
|
206
|
+
previous?: string | URL | null
|
|
207
|
+
next?: string | URL | null
|
|
208
|
+
} | null
|
|
209
|
+
icons?: MetadataIcon | MetadataIcon[] | MetadataIconGroups | null
|
|
210
|
+
manifest?: string | URL | null
|
|
211
|
+
creator?: string | null
|
|
212
|
+
publisher?: string | null
|
|
208
213
|
formatDetection?: {
|
|
209
|
-
email?: boolean
|
|
210
|
-
address?: boolean
|
|
211
|
-
telephone?: boolean
|
|
212
|
-
url?: boolean
|
|
213
|
-
}
|
|
214
|
+
email?: boolean | null
|
|
215
|
+
address?: boolean | null
|
|
216
|
+
telephone?: boolean | null
|
|
217
|
+
url?: boolean | null
|
|
218
|
+
} | null
|
|
214
219
|
verification?: {
|
|
215
|
-
google?:
|
|
216
|
-
yahoo?:
|
|
217
|
-
yandex?:
|
|
218
|
-
other?: Record<string,
|
|
219
|
-
}
|
|
220
|
+
google?: MetadataValue | MetadataValue[] | null
|
|
221
|
+
yahoo?: MetadataValue | MetadataValue[] | null
|
|
222
|
+
yandex?: MetadataValue | MetadataValue[] | null
|
|
223
|
+
other?: Record<string, MetadataValue | MetadataValue[]>
|
|
224
|
+
} | null
|
|
220
225
|
itunes?: {
|
|
221
226
|
appId?: string
|
|
222
|
-
appArgument?: string
|
|
223
|
-
}
|
|
227
|
+
appArgument?: string | null
|
|
228
|
+
} | null
|
|
224
229
|
appleWebApp?:
|
|
225
230
|
| boolean
|
|
226
231
|
| {
|
|
227
232
|
capable?: boolean
|
|
228
|
-
title?: string
|
|
233
|
+
title?: string | null
|
|
229
234
|
statusBarStyle?: string
|
|
230
235
|
startupImage?:
|
|
231
236
|
| string
|
|
232
237
|
| URL
|
|
233
238
|
| { url: string | URL; media?: string }
|
|
234
239
|
| (string | URL | { url: string | URL; media?: string })[]
|
|
240
|
+
| null
|
|
235
241
|
}
|
|
242
|
+
| null
|
|
236
243
|
facebook?: {
|
|
237
244
|
appId?: string
|
|
238
|
-
admins?:
|
|
239
|
-
}
|
|
245
|
+
admins?: MetadataValue | MetadataValue[]
|
|
246
|
+
} | null
|
|
240
247
|
pinterest?: {
|
|
241
|
-
richPin?: boolean
|
|
242
|
-
}
|
|
243
|
-
appLinks?:
|
|
244
|
-
other?: Record<string,
|
|
248
|
+
richPin?: boolean | string | null
|
|
249
|
+
} | null
|
|
250
|
+
appLinks?: MetadataAppLinks | null
|
|
251
|
+
other?: Record<string, MetadataValue | MetadataValue[] | null>
|
|
245
252
|
links?: MetadataLink[]
|
|
246
253
|
}
|
|
247
254
|
|
|
255
|
+
/** A metadata value that renders to text; numbers are stringified. */
|
|
256
|
+
export type MetadataValue = string | number
|
|
257
|
+
|
|
258
|
+
export type MetadataAppLinkTarget = Record<string, MetadataValue | boolean | URL | undefined>
|
|
259
|
+
|
|
260
|
+
export type MetadataAppLinks = Record<
|
|
261
|
+
string,
|
|
262
|
+
MetadataAppLinkTarget | MetadataAppLinkTarget[] | undefined
|
|
263
|
+
>
|
|
264
|
+
|
|
248
265
|
export type ThemeColor = string | { color: string; media?: string }
|
|
249
266
|
|
|
250
267
|
export interface Viewport {
|
|
@@ -255,8 +272,8 @@ export interface Viewport {
|
|
|
255
272
|
userScalable?: boolean
|
|
256
273
|
viewportFit?: 'auto' | 'contain' | 'cover'
|
|
257
274
|
interactiveWidget?: 'resizes-visual' | 'resizes-content' | 'overlays-content'
|
|
258
|
-
themeColor?: ThemeColor | ThemeColor[]
|
|
259
|
-
colorScheme?: string
|
|
275
|
+
themeColor?: ThemeColor | ThemeColor[] | null
|
|
276
|
+
colorScheme?: string | null
|
|
260
277
|
}
|
|
261
278
|
|
|
262
279
|
export interface NextRequestCookie {
|
package/tsconfig.json
CHANGED