@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.
Files changed (71) hide show
  1. package/README.md +68 -103
  2. package/package.json +3 -2
  3. package/reference/data/bench.json +513 -0
  4. package/reference/performance.md +87 -29
  5. package/src/api/router/runtime.ts +10 -2
  6. package/src/cache/context.ts +4 -1
  7. package/src/cli/build.ts +39 -5
  8. package/src/cli/create.ts +146 -0
  9. package/src/cli/dev.ts +6 -0
  10. package/src/cli/index.ts +28 -4
  11. package/src/cli/migrate/index.ts +96 -0
  12. package/src/cli/migrate/package-json.ts +183 -0
  13. package/src/cli/migrate/report.ts +80 -0
  14. package/src/cli/migrate/scan.ts +135 -0
  15. package/src/cli/migrate/spinner.ts +20 -0
  16. package/src/cli/migrate/tsconfig.ts +77 -0
  17. package/src/cli/request-pipeline.ts +1340 -0
  18. package/src/cli/server-entry.ts +180 -0
  19. package/src/cli/start.ts +39 -1311
  20. package/src/client/build.ts +59 -20
  21. package/src/client/chunk-fold.ts +40 -0
  22. package/src/client/compat-surface.ts +175 -0
  23. package/src/client/entry.ts +67 -52
  24. package/src/compat/actions/action-client.ts +8 -1
  25. package/src/compat/actions/action-dispatch.ts +11 -1
  26. package/src/compat/actions/discovery.ts +23 -6
  27. package/src/compat/bundler/optimize-package-imports.ts +5 -1
  28. package/src/compat/bundler/worker.ts +2 -1
  29. package/src/compat/client/errors/bare-boundary.ts +32 -0
  30. package/src/compat/client/errors/error-boundary.ts +1 -15
  31. package/src/compat/client/errors/primitive-throw.ts +16 -0
  32. package/src/compat/client/link-status.ts +1 -1
  33. package/src/compat/css/lightningcss.ts +2 -1
  34. package/src/compat/css/modules.ts +4 -3
  35. package/src/compat/index.ts +1 -1
  36. package/src/compat/lifecycle/instrumentation-client.ts +1 -1
  37. package/src/compat/lifecycle/instrumentation.ts +5 -2
  38. package/src/compat/next/config-loader.ts +33 -5
  39. package/src/compat/next/dynamic.tsx +9 -5
  40. package/src/compat/next/link-validation-transform.ts +5 -1
  41. package/src/compat/next/link.tsx +51 -58
  42. package/src/compat/pages/client-plugin.ts +2 -1
  43. package/src/compat/react/action-state.ts +159 -0
  44. package/src/compat/react/client-lite.ts +74 -0
  45. package/src/compat/react/hooks-extra.ts +92 -0
  46. package/src/compat/react/parity.ts +128 -0
  47. package/src/compat/react/preact.ts +33 -420
  48. package/src/compat/react/server-inserted-html.ts +14 -7
  49. package/src/compat/react/use.ts +72 -0
  50. package/src/compat/register/actions.ts +27 -7
  51. package/src/compat/tsconfig-defaults.ts +6 -3
  52. package/src/config.ts +15 -1
  53. package/src/css/build.ts +13 -2
  54. package/src/dev/imports.ts +34 -5
  55. package/src/dev/module-cache.ts +35 -2
  56. package/src/dev/module-transform.ts +7 -1
  57. package/src/dev/server.ts +91 -22
  58. package/src/ppr.ts +5 -4
  59. package/src/proxy.ts +5 -1
  60. package/src/render/island-context.ts +21 -3
  61. package/src/render/renderer.ts +102 -26
  62. package/src/resolve/engine.ts +12 -2
  63. package/src/resolve/scan-facts.ts +5 -1
  64. package/src/routing/href.ts +4 -5
  65. package/src/routing/routes.ts +14 -2
  66. package/src/runtime/server.ts +8 -4
  67. package/src/runtime/vendor.ts +1 -1
  68. package/src/typegen.ts +3 -3
  69. package/src/utils/esbuild.ts +58 -0
  70. package/src/utils/fs.ts +14 -2
  71. 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
+ }
@@ -1,12 +1,35 @@
1
1
  # Performance
2
2
 
3
- Auto-generated by `bun bench` — do not edit by hand.
4
-
5
- - Date: 2026-08-17
6
- - Machine: Darwin 25.6.0 (arm64), Apple M1 x8
7
- - Bun: 1.3.6
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: 3 (first discarded), medians reported
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) | 101.8 ms | 268.2 ms | 2.63x |
19
- | Dev first page HTML | 82.0 ms | 1047.1 ms | 12.77x |
20
- | Dev warm request (p50 of 7) | 2.3 ms | 13.3 ms | 5.80x |
21
- | HMR save visible | 19.7 ms | 41.7 ms | 2.11x |
22
- | Prod build (wall) | 302.0 ms | 3000.2 ms | 9.93x |
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.73x |
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) | 93.8 ms | 288.1 ms | 3.07x |
35
- | Dev first page HTML | 103.4 ms | 1129.8 ms | 10.93x |
36
- | Dev warm request (p50 of 7) | 3.6 ms | 25.9 ms | 7.26x |
37
- | HMR save visible | 24.6 ms | 67.1 ms | 2.73x |
38
- | Prod build (wall) | 372.1 ms | 3145.8 ms | 8.45x |
39
- | First-page client JS (raw) | 40.37 KB | 502.78 KB | 12.45x |
40
- | First-page client JS (gzip) | 16.13 KB | 142.03 KB | 8.80x |
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) | 103.3 ms | 300.4 ms | 2.91x |
51
- | Dev first page HTML | 189.0 ms | 1278.9 ms | 6.77x |
52
- | Dev warm request (p50 of 7) | 6.7 ms | 28.4 ms | 4.22x |
53
- | HMR save visible | 67.8 ms | 73.8 ms | 1.09x |
54
- | Prod build (wall) | 941.5 ms | 4160.1 ms | 4.42x |
55
- | First-page client JS (raw) | 63.26 KB | 507.42 KB | 8.02x |
56
- | First-page client JS (gzip) | 23.63 KB | 144.85 KB | 6.13x |
57
- | First-page JS files | 5 | 9 | 1.80x |
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) | <= 150 ms | 93.8 ms | PASS |
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),