@wular/pnext 0.0.1 → 0.0.3
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 +68 -103
- package/package.json +3 -2
- package/reference/data/bench.json +513 -0
- package/reference/performance.md +87 -29
- package/src/api/router/runtime.ts +10 -2
- package/src/cache/context.ts +4 -1
- package/src/cli/build.ts +39 -5
- package/src/cli/create.ts +146 -0
- package/src/cli/dev.ts +6 -0
- package/src/cli/index.ts +28 -4
- package/src/cli/migrate/index.ts +96 -0
- package/src/cli/migrate/package-json.ts +183 -0
- package/src/cli/migrate/report.ts +80 -0
- package/src/cli/migrate/scan.ts +135 -0
- package/src/cli/migrate/spinner.ts +20 -0
- package/src/cli/migrate/tsconfig.ts +77 -0
- package/src/cli/request-pipeline.ts +1340 -0
- package/src/cli/server-entry.ts +180 -0
- package/src/cli/start.ts +39 -1311
- package/src/client/build.ts +59 -20
- package/src/client/chunk-fold.ts +40 -0
- package/src/client/compat-surface.ts +175 -0
- package/src/client/entry.ts +67 -52
- package/src/compat/actions/action-client.ts +8 -1
- package/src/compat/actions/action-dispatch.ts +11 -1
- package/src/compat/actions/discovery.ts +23 -6
- package/src/compat/bundler/optimize-package-imports.ts +5 -1
- package/src/compat/bundler/worker.ts +2 -1
- package/src/compat/client/errors/bare-boundary.ts +32 -0
- package/src/compat/client/errors/error-boundary.ts +1 -15
- package/src/compat/client/errors/primitive-throw.ts +16 -0
- package/src/compat/client/link-status.ts +1 -1
- package/src/compat/css/lightningcss.ts +2 -1
- package/src/compat/css/modules.ts +4 -3
- package/src/compat/index.ts +1 -1
- package/src/compat/lifecycle/instrumentation-client.ts +1 -1
- package/src/compat/lifecycle/instrumentation.ts +5 -2
- package/src/compat/next/config-loader.ts +33 -5
- package/src/compat/next/dynamic.tsx +9 -5
- package/src/compat/next/link-validation-transform.ts +5 -1
- package/src/compat/next/link.tsx +51 -58
- package/src/compat/pages/client-plugin.ts +2 -1
- package/src/compat/react/action-state.ts +159 -0
- package/src/compat/react/client-lite.ts +74 -0
- package/src/compat/react/hooks-extra.ts +92 -0
- package/src/compat/react/parity.ts +128 -0
- package/src/compat/react/preact.ts +33 -420
- package/src/compat/react/server-inserted-html.ts +14 -7
- package/src/compat/react/use.ts +72 -0
- package/src/compat/register/actions.ts +27 -7
- package/src/compat/tsconfig-defaults.ts +6 -3
- package/src/config.ts +15 -1
- package/src/css/build.ts +13 -2
- package/src/dev/imports.ts +34 -5
- package/src/dev/module-cache.ts +35 -2
- package/src/dev/module-transform.ts +7 -1
- package/src/dev/server.ts +91 -22
- package/src/ppr.ts +5 -4
- package/src/proxy.ts +5 -1
- package/src/render/island-context.ts +21 -3
- package/src/render/renderer.ts +102 -26
- package/src/resolve/engine.ts +12 -2
- package/src/resolve/scan-facts.ts +5 -1
- package/src/routing/href.ts +4 -5
- package/src/routing/routes.ts +14 -2
- package/src/runtime/server.ts +8 -4
- package/src/runtime/vendor.ts +1 -1
- package/src/typegen.ts +3 -3
- package/src/utils/esbuild.ts +58 -0
- package/src/utils/fs.ts +14 -2
- package/src/utils/native-require.ts +28 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
{
|
|
2
|
+
"run": {
|
|
3
|
+
"date": "2026-08-20",
|
|
4
|
+
"machine": "CI — Blacksmith 4 vCPU (Intel Xeon), 16 GB RAM, Ubuntu 22.04 x64",
|
|
5
|
+
"bun": "1.3.10",
|
|
6
|
+
"next": "16.2.12",
|
|
7
|
+
"runs": "5 per metric (first discarded), medians reported"
|
|
8
|
+
},
|
|
9
|
+
"fixtures": [
|
|
10
|
+
{
|
|
11
|
+
"id": "hello",
|
|
12
|
+
"label": "hello-world",
|
|
13
|
+
"blurb": "A single page."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "ssr",
|
|
17
|
+
"label": "ssr",
|
|
18
|
+
"blurb": "A server-rendered site."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "dashboard",
|
|
22
|
+
"label": "dashboard",
|
|
23
|
+
"blurb": "A mid-size admin app — 30 routes, 18 client islands."
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"metrics": [
|
|
27
|
+
{
|
|
28
|
+
"id": "dev-ready",
|
|
29
|
+
"label": "Dev server ready",
|
|
30
|
+
"category": "dev",
|
|
31
|
+
"phase": "cold",
|
|
32
|
+
"rows": {
|
|
33
|
+
"hello": [
|
|
34
|
+
56.4,
|
|
35
|
+
"56.4 ms",
|
|
36
|
+
212.3,
|
|
37
|
+
"212.3 ms",
|
|
38
|
+
3.76
|
|
39
|
+
],
|
|
40
|
+
"ssr": [
|
|
41
|
+
56,
|
|
42
|
+
"56.0 ms",
|
|
43
|
+
212.8,
|
|
44
|
+
"212.8 ms",
|
|
45
|
+
3.8
|
|
46
|
+
],
|
|
47
|
+
"dashboard": [
|
|
48
|
+
57.1,
|
|
49
|
+
"57.1 ms",
|
|
50
|
+
246.4,
|
|
51
|
+
"246.4 ms",
|
|
52
|
+
4.31
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": "dev-first-page",
|
|
58
|
+
"label": "Dev first page HTML",
|
|
59
|
+
"category": "dev",
|
|
60
|
+
"phase": "cold",
|
|
61
|
+
"rows": {
|
|
62
|
+
"hello": [
|
|
63
|
+
79.9,
|
|
64
|
+
"79.9 ms",
|
|
65
|
+
993.5,
|
|
66
|
+
"993.5 ms",
|
|
67
|
+
12.44
|
|
68
|
+
],
|
|
69
|
+
"ssr": [
|
|
70
|
+
93.5,
|
|
71
|
+
"93.5 ms",
|
|
72
|
+
1043.3,
|
|
73
|
+
"1043.3 ms",
|
|
74
|
+
11.16
|
|
75
|
+
],
|
|
76
|
+
"dashboard": [
|
|
77
|
+
127,
|
|
78
|
+
"127.0 ms",
|
|
79
|
+
1285.8,
|
|
80
|
+
"1285.8 ms",
|
|
81
|
+
10.12
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"id": "dev-warm",
|
|
87
|
+
"label": "Dev warm request (p50 of 7)",
|
|
88
|
+
"category": "dev",
|
|
89
|
+
"phase": "warm",
|
|
90
|
+
"rows": {
|
|
91
|
+
"hello": [
|
|
92
|
+
3,
|
|
93
|
+
"3.0 ms",
|
|
94
|
+
14,
|
|
95
|
+
"14.0 ms",
|
|
96
|
+
4.69
|
|
97
|
+
],
|
|
98
|
+
"ssr": [
|
|
99
|
+
16.5,
|
|
100
|
+
"16.5 ms",
|
|
101
|
+
26.4,
|
|
102
|
+
"26.4 ms",
|
|
103
|
+
1.6
|
|
104
|
+
],
|
|
105
|
+
"dashboard": [
|
|
106
|
+
21.6,
|
|
107
|
+
"21.6 ms",
|
|
108
|
+
34.1,
|
|
109
|
+
"34.1 ms",
|
|
110
|
+
1.58
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "hmr",
|
|
116
|
+
"label": "HMR save → visible",
|
|
117
|
+
"category": "dev",
|
|
118
|
+
"phase": "warm",
|
|
119
|
+
"rows": {
|
|
120
|
+
"hello": [
|
|
121
|
+
21.9,
|
|
122
|
+
"21.9 ms",
|
|
123
|
+
44.8,
|
|
124
|
+
"44.8 ms",
|
|
125
|
+
2.04
|
|
126
|
+
],
|
|
127
|
+
"ssr": [
|
|
128
|
+
264.3,
|
|
129
|
+
"264.3 ms",
|
|
130
|
+
68.3,
|
|
131
|
+
"68.3 ms",
|
|
132
|
+
0.26
|
|
133
|
+
],
|
|
134
|
+
"dashboard": [
|
|
135
|
+
300.4,
|
|
136
|
+
"300.4 ms",
|
|
137
|
+
97.6,
|
|
138
|
+
"97.6 ms",
|
|
139
|
+
0.32
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"id": "build-wall",
|
|
145
|
+
"label": "Production build (wall)",
|
|
146
|
+
"category": "build",
|
|
147
|
+
"phase": "cold",
|
|
148
|
+
"rows": {
|
|
149
|
+
"hello": [
|
|
150
|
+
291.3,
|
|
151
|
+
"291.3 ms",
|
|
152
|
+
2569.4,
|
|
153
|
+
"2569.4 ms",
|
|
154
|
+
8.82
|
|
155
|
+
],
|
|
156
|
+
"ssr": [
|
|
157
|
+
310.5,
|
|
158
|
+
"310.5 ms",
|
|
159
|
+
2738.8,
|
|
160
|
+
"2738.8 ms",
|
|
161
|
+
8.82
|
|
162
|
+
],
|
|
163
|
+
"dashboard": [
|
|
164
|
+
637.5,
|
|
165
|
+
"637.5 ms",
|
|
166
|
+
4566.3,
|
|
167
|
+
"4566.3 ms",
|
|
168
|
+
7.16
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"id": "prod-start",
|
|
174
|
+
"label": "Prod start (ready)",
|
|
175
|
+
"category": "build",
|
|
176
|
+
"phase": "cold",
|
|
177
|
+
"rows": {
|
|
178
|
+
"hello": [
|
|
179
|
+
60.7,
|
|
180
|
+
"60.7 ms",
|
|
181
|
+
110.9,
|
|
182
|
+
"110.9 ms",
|
|
183
|
+
1.83
|
|
184
|
+
],
|
|
185
|
+
"ssr": [
|
|
186
|
+
57.1,
|
|
187
|
+
"57.1 ms",
|
|
188
|
+
105.5,
|
|
189
|
+
"105.5 ms",
|
|
190
|
+
1.85
|
|
191
|
+
],
|
|
192
|
+
"dashboard": [
|
|
193
|
+
55.6,
|
|
194
|
+
"55.6 ms",
|
|
195
|
+
128.8,
|
|
196
|
+
"128.8 ms",
|
|
197
|
+
2.32
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"id": "prod-warm",
|
|
203
|
+
"label": "Prod warm request (p50 of 7)",
|
|
204
|
+
"category": "build",
|
|
205
|
+
"phase": "warm",
|
|
206
|
+
"rows": {
|
|
207
|
+
"hello": [
|
|
208
|
+
0.6,
|
|
209
|
+
"0.6 ms",
|
|
210
|
+
1.2,
|
|
211
|
+
"1.2 ms",
|
|
212
|
+
2.05
|
|
213
|
+
],
|
|
214
|
+
"ssr": [
|
|
215
|
+
0.5,
|
|
216
|
+
"0.5 ms",
|
|
217
|
+
1.3,
|
|
218
|
+
"1.3 ms",
|
|
219
|
+
2.5
|
|
220
|
+
],
|
|
221
|
+
"dashboard": [
|
|
222
|
+
0.5,
|
|
223
|
+
"0.5 ms",
|
|
224
|
+
1.7,
|
|
225
|
+
"1.7 ms",
|
|
226
|
+
3.41
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
"id": "dev-rss",
|
|
232
|
+
"label": "Dev server RSS (ready + 7 warm)",
|
|
233
|
+
"category": "memory",
|
|
234
|
+
"phase": "warm",
|
|
235
|
+
"rows": {
|
|
236
|
+
"hello": [
|
|
237
|
+
130.8,
|
|
238
|
+
"130.8 MB",
|
|
239
|
+
536.4,
|
|
240
|
+
"536.4 MB",
|
|
241
|
+
4.1
|
|
242
|
+
],
|
|
243
|
+
"ssr": [
|
|
244
|
+
151.3,
|
|
245
|
+
"151.3 MB",
|
|
246
|
+
553.6,
|
|
247
|
+
"553.6 MB",
|
|
248
|
+
3.66
|
|
249
|
+
],
|
|
250
|
+
"dashboard": [
|
|
251
|
+
168.6,
|
|
252
|
+
"168.6 MB",
|
|
253
|
+
577.1,
|
|
254
|
+
"577.1 MB",
|
|
255
|
+
3.42
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"id": "build-rss",
|
|
261
|
+
"label": "Prod build peak RSS",
|
|
262
|
+
"category": "memory",
|
|
263
|
+
"phase": "cold",
|
|
264
|
+
"rows": {
|
|
265
|
+
"hello": [
|
|
266
|
+
124.2,
|
|
267
|
+
"124.2 MB",
|
|
268
|
+
455.8,
|
|
269
|
+
"455.8 MB",
|
|
270
|
+
3.67
|
|
271
|
+
],
|
|
272
|
+
"ssr": [
|
|
273
|
+
136.8,
|
|
274
|
+
"136.8 MB",
|
|
275
|
+
456.6,
|
|
276
|
+
"456.6 MB",
|
|
277
|
+
3.34
|
|
278
|
+
],
|
|
279
|
+
"dashboard": [
|
|
280
|
+
188,
|
|
281
|
+
"188.0 MB",
|
|
282
|
+
569,
|
|
283
|
+
"569.0 MB",
|
|
284
|
+
3.03
|
|
285
|
+
]
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"id": "prod-rss",
|
|
290
|
+
"label": "Prod server RSS (ready + 7 warm)",
|
|
291
|
+
"category": "memory",
|
|
292
|
+
"phase": "warm",
|
|
293
|
+
"rows": {
|
|
294
|
+
"hello": [
|
|
295
|
+
85.2,
|
|
296
|
+
"85.2 MB",
|
|
297
|
+
85.7,
|
|
298
|
+
"85.7 MB",
|
|
299
|
+
1.01
|
|
300
|
+
],
|
|
301
|
+
"ssr": [
|
|
302
|
+
86,
|
|
303
|
+
"86.0 MB",
|
|
304
|
+
85.9,
|
|
305
|
+
"85.9 MB",
|
|
306
|
+
1
|
|
307
|
+
],
|
|
308
|
+
"dashboard": [
|
|
309
|
+
87.3,
|
|
310
|
+
"87.3 MB",
|
|
311
|
+
90.6,
|
|
312
|
+
"90.6 MB",
|
|
313
|
+
1.04
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"id": "install",
|
|
319
|
+
"label": "Framework install size",
|
|
320
|
+
"category": "payload",
|
|
321
|
+
"phase": "static",
|
|
322
|
+
"rows": {
|
|
323
|
+
"hello": [
|
|
324
|
+
7.1,
|
|
325
|
+
"7.1 MB",
|
|
326
|
+
420.7,
|
|
327
|
+
"420.7 MB",
|
|
328
|
+
59.14
|
|
329
|
+
],
|
|
330
|
+
"ssr": [
|
|
331
|
+
7.1,
|
|
332
|
+
"7.1 MB",
|
|
333
|
+
420.7,
|
|
334
|
+
"420.7 MB",
|
|
335
|
+
59.14
|
|
336
|
+
],
|
|
337
|
+
"dashboard": [
|
|
338
|
+
7.1,
|
|
339
|
+
"7.1 MB",
|
|
340
|
+
420.7,
|
|
341
|
+
"420.7 MB",
|
|
342
|
+
59.14
|
|
343
|
+
]
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"id": "js-gzip",
|
|
348
|
+
"label": "First-page client JS (gzip)",
|
|
349
|
+
"category": "payload",
|
|
350
|
+
"phase": "static",
|
|
351
|
+
"rows": {
|
|
352
|
+
"hello": [
|
|
353
|
+
2.19,
|
|
354
|
+
"2.19 KB",
|
|
355
|
+
141.78,
|
|
356
|
+
"141.78 KB",
|
|
357
|
+
64.67
|
|
358
|
+
],
|
|
359
|
+
"ssr": [
|
|
360
|
+
11.39,
|
|
361
|
+
"11.39 KB",
|
|
362
|
+
142.03,
|
|
363
|
+
"142.03 KB",
|
|
364
|
+
12.47
|
|
365
|
+
],
|
|
366
|
+
"dashboard": [
|
|
367
|
+
15.26,
|
|
368
|
+
"15.26 KB",
|
|
369
|
+
144.85,
|
|
370
|
+
"144.85 KB",
|
|
371
|
+
9.5
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"id": "js-raw",
|
|
377
|
+
"label": "First-page client JS (raw)",
|
|
378
|
+
"category": "payload",
|
|
379
|
+
"phase": "static",
|
|
380
|
+
"rows": {
|
|
381
|
+
"hello": [
|
|
382
|
+
4.43,
|
|
383
|
+
"4.43 KB",
|
|
384
|
+
502.47,
|
|
385
|
+
"502.47 KB",
|
|
386
|
+
113.36
|
|
387
|
+
],
|
|
388
|
+
"ssr": [
|
|
389
|
+
27.36,
|
|
390
|
+
"27.36 KB",
|
|
391
|
+
502.78,
|
|
392
|
+
"502.78 KB",
|
|
393
|
+
18.37
|
|
394
|
+
],
|
|
395
|
+
"dashboard": [
|
|
396
|
+
38.51,
|
|
397
|
+
"38.51 KB",
|
|
398
|
+
507.42,
|
|
399
|
+
"507.42 KB",
|
|
400
|
+
13.18
|
|
401
|
+
]
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"id": "js-files",
|
|
406
|
+
"label": "First-page JS files",
|
|
407
|
+
"category": "payload",
|
|
408
|
+
"phase": "static",
|
|
409
|
+
"rows": {
|
|
410
|
+
"hello": [
|
|
411
|
+
3,
|
|
412
|
+
"3",
|
|
413
|
+
5,
|
|
414
|
+
"5",
|
|
415
|
+
1.67
|
|
416
|
+
],
|
|
417
|
+
"ssr": [
|
|
418
|
+
4,
|
|
419
|
+
"4",
|
|
420
|
+
6,
|
|
421
|
+
"6",
|
|
422
|
+
1.5
|
|
423
|
+
],
|
|
424
|
+
"dashboard": [
|
|
425
|
+
6,
|
|
426
|
+
"6",
|
|
427
|
+
9,
|
|
428
|
+
"9",
|
|
429
|
+
1.5
|
|
430
|
+
]
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
"id": "zero-island",
|
|
435
|
+
"label": "Zero-island route client JS",
|
|
436
|
+
"category": "payload",
|
|
437
|
+
"phase": "static",
|
|
438
|
+
"rows": {
|
|
439
|
+
"hello": [
|
|
440
|
+
4.43,
|
|
441
|
+
"4.43 KB",
|
|
442
|
+
502.47,
|
|
443
|
+
"502.47 KB",
|
|
444
|
+
113.36
|
|
445
|
+
],
|
|
446
|
+
"ssr": [
|
|
447
|
+
4.46,
|
|
448
|
+
"4.46 KB",
|
|
449
|
+
502.47,
|
|
450
|
+
"502.47 KB",
|
|
451
|
+
112.56
|
|
452
|
+
]
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
],
|
|
456
|
+
"headlines": [
|
|
457
|
+
{
|
|
458
|
+
"label": "Dev server ready",
|
|
459
|
+
"direction": "faster",
|
|
460
|
+
"value": "3.8–4.3×"
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
"label": "First page, cold",
|
|
464
|
+
"direction": "faster",
|
|
465
|
+
"value": "10–12×"
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
"label": "Warm request (dev)",
|
|
469
|
+
"direction": "faster",
|
|
470
|
+
"value": "1.6–4.7×"
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"label": "Production build",
|
|
474
|
+
"direction": "faster",
|
|
475
|
+
"value": "7.2–8.8×"
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
"label": "Warm request (prod)",
|
|
479
|
+
"direction": "faster",
|
|
480
|
+
"value": "2–3.4×"
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
"label": "Dev server memory",
|
|
484
|
+
"direction": "less",
|
|
485
|
+
"value": "3.4–4.1×"
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
"label": "First-page client JS (gzip)",
|
|
489
|
+
"direction": "less",
|
|
490
|
+
"value": "9.5–65×"
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
"label": "Framework install size",
|
|
494
|
+
"direction": "smaller",
|
|
495
|
+
"value": "59×",
|
|
496
|
+
"detail": "7.1 MB vs 420.7 MB"
|
|
497
|
+
}
|
|
498
|
+
],
|
|
499
|
+
"clientRuntimes": [
|
|
500
|
+
{
|
|
501
|
+
"name": "router-prefetch-only",
|
|
502
|
+
"raw": "473 B",
|
|
503
|
+
"gzip": "348 B",
|
|
504
|
+
"brotli": "283 B"
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
"name": "combined-router-hydrator",
|
|
508
|
+
"raw": "10.47 KB",
|
|
509
|
+
"gzip": "4.47 KB",
|
|
510
|
+
"brotli": "4.07 KB"
|
|
511
|
+
}
|
|
512
|
+
]
|
|
513
|
+
}
|
package/reference/performance.md
CHANGED
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
# Performance
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Measured against Next.js: the same fixture source runs under both frameworks and `bun bench` measures them side by side, on a Blacksmith 4-vCPU CI runner (full machine details below). The ranges below span the fixtures — hello-world, an SSR site, and a mid-size admin dashboard (30 routes, 18 client islands):
|
|
4
|
+
|
|
5
|
+
| | pnext vs Next.js |
|
|
6
|
+
| --------------------------- | ---------------------------------: |
|
|
7
|
+
| Dev server ready | **3.8–4.3× faster** |
|
|
8
|
+
| First page, cold | **10–12× faster** |
|
|
9
|
+
| Warm request (dev) | **1.6–4.7× faster** |
|
|
10
|
+
| HMR save → visible | **0.3–2×** ¹ |
|
|
11
|
+
| Production build | **7.2–8.8× faster** |
|
|
12
|
+
| Warm request (prod) | **2–3.4× faster** |
|
|
13
|
+
| Prod server ready | **1.8–2.3× faster** |
|
|
14
|
+
| Dev server memory | **3.4–4.1× less** |
|
|
15
|
+
| Build peak memory | **3–3.7× less** |
|
|
16
|
+
| Framework install size | **59× smaller** (7.1 MB vs 421 MB) |
|
|
17
|
+
| First-page client JS (gzip) | **9.5–65× less** |
|
|
18
|
+
|
|
19
|
+
¹ pnext live-reloads (no client HMR runtime; state doesn't survive a save) — the metric
|
|
20
|
+
is save → fresh HTML, which both models support. Scoped invalidation fixed the small
|
|
21
|
+
fixture (21.9 ms, ahead of Next); the larger fixtures still trail on this runner
|
|
22
|
+
(264/300 ms vs 68/98 ms) and the remaining cost is under investigation.
|
|
23
|
+
|
|
24
|
+
Nothing is prebundled — the dev server compiles what a request needs and caches it content-addressed, which is why readiness doesn't scale with app size. The production server idles at parity with Next (~85–87 MB) while answering warm requests 2–3.4× faster.
|
|
25
|
+
|
|
26
|
+
The tables below are one full run's absolute numbers, pasted from `bun bench` output (the CI run's `bench-output` artifact carries the same data as `bench.json`).
|
|
27
|
+
|
|
28
|
+
- Date: 2026-08-20 (actions run 32364720143)
|
|
29
|
+
- Machine: CI — Blacksmith 4 vCPU (Intel Xeon), 16 GB RAM, Ubuntu 22.04 x64
|
|
30
|
+
- Bun: 1.3.10
|
|
8
31
|
- Next.js: 16.2.12
|
|
9
|
-
- Runs per metric:
|
|
32
|
+
- Runs per metric: 5 (first discarded), medians reported
|
|
10
33
|
|
|
11
34
|
Every fixture under `bench/fixtures/` runs unmodified on both frameworks, so the
|
|
12
35
|
two columns render the same app from the same source.
|
|
@@ -15,13 +38,19 @@ two columns render the same app from the same source.
|
|
|
15
38
|
|
|
16
39
|
| Metric | pnext | Next.js | Ratio |
|
|
17
40
|
| --- | --- | --- | --- |
|
|
18
|
-
| Dev cold start (ready) |
|
|
19
|
-
| Dev first page HTML |
|
|
20
|
-
| Dev warm request (p50 of 7) |
|
|
21
|
-
|
|
|
22
|
-
|
|
|
41
|
+
| Dev cold start (ready) | 56.4 ms | 212.3 ms | 3.76x |
|
|
42
|
+
| Dev first page HTML | 79.9 ms | 993.5 ms | 12.44x |
|
|
43
|
+
| Dev warm request (p50 of 7) | 3.0 ms | 14.0 ms | 4.69x |
|
|
44
|
+
| Dev server RSS (ready + 7 warm) | 130.8 MB | 536.4 MB | 4.10x |
|
|
45
|
+
| HMR save → visible | 21.9 ms | 44.8 ms | 2.04x |
|
|
46
|
+
| Prod build (wall) | 291.3 ms | 2569.4 ms | 8.82x |
|
|
47
|
+
| Prod build peak RSS | 124.2 MB | 455.8 MB | 3.67x |
|
|
48
|
+
| Prod start (ready) | 60.7 ms | 110.9 ms | 1.83x |
|
|
49
|
+
| Prod warm request (p50 of 7) | 0.6 ms | 1.2 ms | 2.05x |
|
|
50
|
+
| Prod server RSS (ready + 7 warm) | 85.2 MB | 85.7 MB | 1.01x |
|
|
51
|
+
| Framework install size | 7.1 MB | 420.7 MB | 59.14x |
|
|
23
52
|
| First-page client JS (raw) | 4.43 KB | 502.47 KB | 113.36x |
|
|
24
|
-
| First-page client JS (gzip) | 2.19 KB | 141.78 KB | 64.
|
|
53
|
+
| First-page client JS (gzip) | 2.19 KB | 141.78 KB | 64.67x |
|
|
25
54
|
| First-page JS files | 3 | 5 | 1.67x |
|
|
26
55
|
| Zero-island route client JS | 4.43 KB | 502.47 KB | 113.36x |
|
|
27
56
|
|
|
@@ -31,13 +60,19 @@ Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
|
|
|
31
60
|
|
|
32
61
|
| Metric | pnext | Next.js | Ratio |
|
|
33
62
|
| --- | --- | --- | --- |
|
|
34
|
-
| Dev cold start (ready) |
|
|
35
|
-
| Dev first page HTML |
|
|
36
|
-
| Dev warm request (p50 of 7) |
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
63
|
+
| Dev cold start (ready) | 56.0 ms | 212.8 ms | 3.80x |
|
|
64
|
+
| Dev first page HTML | 93.5 ms | 1043.3 ms | 11.16x |
|
|
65
|
+
| Dev warm request (p50 of 7) | 16.5 ms | 26.4 ms | 1.60x |
|
|
66
|
+
| Dev server RSS (ready + 7 warm) | 151.3 MB | 553.6 MB | 3.66x |
|
|
67
|
+
| HMR save → visible | 264.3 ms | 68.3 ms | 0.26x |
|
|
68
|
+
| Prod build (wall) | 310.5 ms | 2738.8 ms | 8.82x |
|
|
69
|
+
| Prod build peak RSS | 136.8 MB | 456.6 MB | 3.34x |
|
|
70
|
+
| Prod start (ready) | 57.1 ms | 105.5 ms | 1.85x |
|
|
71
|
+
| Prod warm request (p50 of 7) | 0.5 ms | 1.3 ms | 2.50x |
|
|
72
|
+
| Prod server RSS (ready + 7 warm) | 86.0 MB | 85.9 MB | 1.00x |
|
|
73
|
+
| Framework install size | 7.1 MB | 420.7 MB | 59.14x |
|
|
74
|
+
| First-page client JS (raw) | 27.36 KB | 502.78 KB | 18.37x |
|
|
75
|
+
| First-page client JS (gzip) | 11.39 KB | 142.03 KB | 12.47x |
|
|
41
76
|
| First-page JS files | 4 | 6 | 1.50x |
|
|
42
77
|
| Zero-island route client JS | 4.46 KB | 502.47 KB | 112.56x |
|
|
43
78
|
|
|
@@ -47,14 +82,20 @@ Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
|
|
|
47
82
|
|
|
48
83
|
| Metric | pnext | Next.js | Ratio |
|
|
49
84
|
| --- | --- | --- | --- |
|
|
50
|
-
| Dev cold start (ready) |
|
|
51
|
-
| Dev first page HTML |
|
|
52
|
-
| Dev warm request (p50 of 7) | 6
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
85
|
+
| Dev cold start (ready) | 57.1 ms | 246.4 ms | 4.31x |
|
|
86
|
+
| Dev first page HTML | 127.0 ms | 1285.8 ms | 10.12x |
|
|
87
|
+
| Dev warm request (p50 of 7) | 21.6 ms | 34.1 ms | 1.58x |
|
|
88
|
+
| Dev server RSS (ready + 7 warm) | 168.6 MB | 577.1 MB | 3.42x |
|
|
89
|
+
| HMR save → visible | 300.4 ms | 97.6 ms | 0.32x |
|
|
90
|
+
| Prod build (wall) | 637.5 ms | 4566.3 ms | 7.16x |
|
|
91
|
+
| Prod build peak RSS | 188.0 MB | 569.0 MB | 3.03x |
|
|
92
|
+
| Prod start (ready) | 55.6 ms | 128.8 ms | 2.32x |
|
|
93
|
+
| Prod warm request (p50 of 7) | 0.5 ms | 1.7 ms | 3.41x |
|
|
94
|
+
| Prod server RSS (ready + 7 warm) | 87.3 MB | 90.6 MB | 1.04x |
|
|
95
|
+
| Framework install size | 7.1 MB | 420.7 MB | 59.14x |
|
|
96
|
+
| First-page client JS (raw) | 38.51 KB | 507.42 KB | 13.18x |
|
|
97
|
+
| First-page client JS (gzip) | 15.26 KB | 144.85 KB | 9.50x |
|
|
98
|
+
| First-page JS files | 6 | 9 | 1.50x |
|
|
58
99
|
|
|
59
100
|
Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
|
|
60
101
|
|
|
@@ -69,7 +110,7 @@ Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
|
|
|
69
110
|
|
|
70
111
|
| Target | Limit | Measured | Status |
|
|
71
112
|
| --- | --- | --- | --- |
|
|
72
|
-
| Dev cold start, ssr fixture (pnext) | <=
|
|
113
|
+
| Dev cold start, ssr fixture (pnext) | <= 100 ms | 56.0 ms | PASS |
|
|
73
114
|
| Router runtime | <= 1.00 KB gzip | 348 B gzip | PASS |
|
|
74
115
|
| Hydrated-route framework tax | <= 5.00 KB gzip | 4.47 KB gzip | PASS |
|
|
75
116
|
| Zero-island route client JS (ssr `/about`) | 0 B (core pnext) | 4.46 KB (compat.next) | not exercised |
|
|
@@ -83,11 +124,28 @@ Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
|
|
|
83
124
|
stops at the readiness banner; first page HTML is the GET that follows it, so it
|
|
84
125
|
includes on-demand compilation.
|
|
85
126
|
- HMR appends a marker to a rendered string in a page component and polls until the
|
|
86
|
-
page serves it back. A 200 with stale HTML does not count.
|
|
127
|
+
page serves it back. A 200 with stale HTML does not count. pnext applies the save via
|
|
128
|
+
live-reload (full document refresh, no client HMR runtime); Next applies it via Fast
|
|
129
|
+
Refresh. The metric is model-agnostic: time until fresh content is served.
|
|
87
130
|
- Fixtures enable `compat.next` so one source tree runs on both frameworks. That ships
|
|
88
131
|
pnext's Next-compat navigation client, which a core pnext app does not carry — the
|
|
89
132
|
0 B zero-island budget is a core-pnext invariant this suite does not exercise.
|
|
90
133
|
|
|
134
|
+
## Memory
|
|
135
|
+
|
|
136
|
+
- RSS is summed across the whole process tree (parent + spawned workers), read once at a
|
|
137
|
+
fixed point: right after the ready signal and the 7 warm requests, dev and prod alike.
|
|
138
|
+
It is never sampled at an arbitrary time, since RSS is pressure-sensitive.
|
|
139
|
+
- Build peak RSS comes from `/usr/bin/time` wrapping the build process directly (`-l` on
|
|
140
|
+
macOS, `-v` on Linux), not the tree-sum helper — it is the OS-reported peak over the
|
|
141
|
+
whole build, not a single snapshot. The framework server-entry prebundle is emitted by
|
|
142
|
+
a short-lived child whose transient RSS (~15–25 MB, returned at exit) this number
|
|
143
|
+
excludes.
|
|
144
|
+
- Framework install size is each framework's own package cost, not the fixture's total
|
|
145
|
+
`node_modules`, which both frameworks share: `next` + its platform `@next/swc-*` binary,
|
|
146
|
+
or `@wular/pnext`'s npm-publish footprint (its `package.json` "files" list, since this
|
|
147
|
+
workspace resolves it to source rather than an installed build) + `preact`.
|
|
148
|
+
|
|
91
149
|
## Commands
|
|
92
150
|
|
|
93
151
|
```sh
|
|
@@ -437,11 +437,18 @@ async function warmPageAssets(html: string) {
|
|
|
437
437
|
for (const link of doc.querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"][href]')) {
|
|
438
438
|
preloadStylesheet(link.getAttribute('href') ?? '');
|
|
439
439
|
}
|
|
440
|
+
warmEntryChunks(doc);
|
|
441
|
+
const entrySrc = entryScriptSrc(doc);
|
|
442
|
+
if (entrySrc) await importEntry(entrySrc);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// The destination entry's static chunks, kicked off before the entry itself is imported: without
|
|
446
|
+
// this the browser only discovers them after the entry parses, so every chunk the split put behind
|
|
447
|
+
// the entry costs another serial round trip on the navigation's critical path.
|
|
448
|
+
function warmEntryChunks(doc: Document) {
|
|
440
449
|
for (const link of doc.querySelectorAll<HTMLLinkElement>('link[rel="modulepreload"][href]')) {
|
|
441
450
|
preloadModule(link.getAttribute('href') ?? '');
|
|
442
451
|
}
|
|
443
|
-
const entrySrc = entryScriptSrc(doc);
|
|
444
|
-
if (entrySrc) await importEntry(entrySrc);
|
|
445
452
|
}
|
|
446
453
|
|
|
447
454
|
function preloadStylesheet(href: string) {
|
|
@@ -4676,6 +4683,7 @@ export async function softNavigate(href: string, options: SoftNavigateOptions =
|
|
|
4676
4683
|
// Warm the new document's assets before touching the current one: the swap
|
|
4677
4684
|
// then paints styled content immediately instead of flashing unstyled HTML.
|
|
4678
4685
|
const entrySrc = entryScriptSrc(doc);
|
|
4686
|
+
warmEntryChunks(doc);
|
|
4679
4687
|
const [entryModule] = await Promise.all([
|
|
4680
4688
|
entrySrc ? importEntry(entrySrc) : Promise.resolve(null),
|
|
4681
4689
|
addStylesheets(doc),
|