@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
package/README.md CHANGED
@@ -2,152 +2,117 @@
2
2
 
3
3
  # pnext
4
4
 
5
- **A fast little framework for server-first React apps, fully compatible with Next.js.**
5
+ **A fast little framework for server-first React apps, fully compatible with Next.js.**[<sup>1</sup>](./reference/compat.md)
6
6
 
7
7
  </div>
8
8
 
9
- ## Measured against Next.js
9
+ ## Getting started
10
10
 
11
- The same fixture source runs under both frameworks; `bun bench` measures them side by side and rewrites [`reference/performance.md`](./reference/performance.md) with your machine's absolute numbers. The ranges below span the fixtures — hello-world, an SSR site, and a mid-size admin dashboard (30 routes, 18 client islands):
12
-
13
- | | pnext vs Next.js |
14
- |---|--:|
15
- | Dev server ready | **2.5–3× faster** |
16
- | First page, cold | **6.5–13× faster** |
17
- | Warm request | **4–7.5× faster** |
18
- | HMR save → visible | **1–2.5× faster** |
19
- | Production build | **4.5–10× faster** |
20
- | First-page client JS (gzip) | **6–65× less** |
21
-
22
- Nothing is prebundled — the dev server compiles what a request needs and caches it content-addressed, so it's ready in ~100 ms at any app size.
23
-
24
- Core pnext (no Next compat) is its own story, and it's smaller still:
25
-
26
- - **Server-only pages ship 0 KB of JavaScript.**
27
- - A server-rendered page with links ships **~1 KB gzip** of framework, total; the router + prefetch runtime alone is **348 bytes**.
28
- - A fully hydrated route pays **~7.5 KB gzip** of framework — **12.5 KB** with React compat, **15 KB** with the full `next/*` surface. Next.js ships 130+ KB before your first component.
29
-
30
- Compatibility is proven, not promised: pnext runs Next.js's own test suite — 4,400+ tracked assertions passing — so `next/link`, `next/navigation`, `next/headers`, metadata, middleware, and friends behave the way you already know.
31
-
32
- ## How
33
-
34
- One pipeline, three fast tools: [Bun](https://bun.sh) as runtime, esbuild as the only bundler (dev and prod are the same artifacts — no dev/prod drift), and oxc for parsing, resolving, and transforms. Preact renders and hydrates; React APIs work through compat. The dev server compiles exactly what a request needs, when it needs it.
35
-
36
- ## Install
11
+ A new app:
37
12
 
38
13
  ```sh
39
- bun add -d @wular/pnext
14
+ bunx @wular/pnext create my-app
40
15
  ```
41
16
 
42
- ```json
43
- {
44
- "scripts": {
45
- "dev": "pnext dev --port 3000",
46
- "build": "pnext build",
47
- "build:vercel": "pnext build --adapter vercel",
48
- "start": "pnext start --port 3000",
49
- "analyze": "pnext analyze"
50
- }
51
- }
52
- ```
17
+ Migrating a Next.js app? This rewrites scripts and config in place, scans your source, and reports anything that needs a look (it never edits your code):
53
18
 
54
- ## App shape
55
-
56
- pnext looks for `app/` or `src/app/`:
57
-
58
- ```txt
59
- src/app/
60
- globals.css
61
- page.tsx
62
- layout.tsx
63
- users/
64
- [id]/
65
- page.tsx
66
- api/
67
- hello/
68
- route.ts
69
- public/
70
- logo.svg
19
+ ```sh
20
+ bunx @wular/pnext migrate
71
21
  ```
72
22
 
73
- `public/` is served from `/`. Global CSS imported from the root layout is compiled and linked in the head, with your PostCSS/Tailwind setup. CSS from pages and components becomes route CSS; `.module.css` is scoped. `layout.tsx` can export `metadata`, a default wrapper component, or both — a metadata-only root layout lets pnext create the document shell.
74
-
75
- ## Pages
23
+ Or by hand: `bun add -d @wular/pnext`, then `pnext dev`.
76
24
 
77
- Pages and layouts are Server Components by default:
25
+ ## Incremental by design
78
26
 
79
- ```tsx
80
- export const metadata = {
81
- title: 'My app',
82
- };
27
+ Server-rendered pages ship **0 KB** of JavaScript, or **~1 KB gzip** if you want client-side navigation and prefetching. Interactive pages hydrate on Preact for **~7.5 KB** of framework, **~12.5 KB** with React compatibility. And everything is instant: the first page in dev renders **11–13.5× faster** than Next's on **3.4–3.8× less memory**, and production builds run **7–10.5× faster**. See [Performance](./reference/performance.md).
83
28
 
84
- export default async function Page() {
85
- const post = await getPost();
86
- return <article>{post.title}</article>;
87
- }
88
- ```
29
+ Core pnext is pure Preact. `compat.react` runs React components and libraries on it, and `compat.next` runs a whole Next.js App Router app unchanged. Start anywhere on that ladder and move when it suits you. The App Router compatibility is validated against Next's own test suite (4,400+ assertions passing). The `pages/` folder is not supported, and neither are private internal utilities of Next.js or React. See [Compatibility](./reference/compat.md).
89
30
 
90
- Add a Client Component only where the browser matters:
31
+ ## A quick tour
91
32
 
92
- ```tsx
93
- 'use client';
33
+ ### Your first page
94
34
 
95
- import { useState } from 'preact/hooks';
35
+ Routes live in `app/`. A `page.tsx` is a Server Component by default. It runs only on the server, so it can be async and talk to your database, filesystem, or internal services directly. None of that code reaches the browser:
96
36
 
97
- export function Counter() {
98
- const [count, setCount] = useState(0);
99
- return <button onClick={() => setCount(count + 1)}>{count}</button>;
37
+ ```tsx
38
+ // app/posts/[id]/page.tsx
39
+ import type { PageProps } from "#gen/app/posts/[id]/page";
40
+
41
+ export default async function Page({ params }: PageProps) {
42
+ const { id } = await params;
43
+ const post = await db.post.findUnique({ where: { id } });
44
+ return (
45
+ <article>
46
+ <h1>{post.title}</h1>
47
+ <p>{post.body}</p>
48
+ </article>
49
+ );
100
50
  }
101
51
  ```
102
52
 
103
- Server Components render Client Components with serializable props; Client Components server-render and hydrate. Server-rendered children passed into a Client Component stay on the server and ship no code.
53
+ This page ships **0 KB** of JavaScript. Layouts work the same way: the root `layout.tsx` owns `<html>` and `<body>` and can export `metadata`.
54
+
55
+ ### Adding interactivity
104
56
 
105
- Dedupe repeated reads with server-only `cache()`:
57
+ Mark a component with `"use client"` where you need state, effects, or event handlers. Server Components render it into the page, and only that island hydrates in the browser:
106
58
 
107
59
  ```tsx
108
- import { cache } from '@wular/pnext/cache';
60
+ // app/counter.tsx
61
+ "use client";
62
+ import { useState } from "preact/hooks";
109
63
 
110
- export const getPost = cache(async (id: string) => {
111
- return db.post.findUnique({ where: { id } });
112
- });
64
+ export function Counter({ initial }: { initial: number }) {
65
+ const [count, setCount] = useState(initial);
66
+ return <button onClick={() => setCount(count + 1)}>Count {count}</button>;
67
+ }
113
68
  ```
114
69
 
115
- ## Routes
70
+ Client Components hydrate on Preact for ~7.5 KB of framework, or ~12.5 KB with [`compat.react`](./reference/compat.md) if you want to run React components and libraries unchanged. Offscreen islands can stay out of the initial bundle entirely with `dynamic({ load: 'visible' })`.
116
71
 
117
- `route.ts` files are HTTP handlers:
72
+ ### Streaming
118
73
 
119
- ```tsx
120
- import type { NextRequest } from '@wular/pnext/server';
74
+ Wrap slow server work in `<Suspense>`. The shell streams immediately and the content follows when it's ready:
121
75
 
122
- export function GET(request: NextRequest) {
123
- const name = request.nextUrl.searchParams.get('name');
124
- return Response.json({ hello: name ?? 'world' });
76
+ ```tsx
77
+ import { Suspense } from "@wular/pnext";
78
+
79
+ export default function Page() {
80
+ return (
81
+ <Suspense fallback={<p>Loading comments…</p>}>
82
+ <Comments />
83
+ </Suspense>
84
+ );
125
85
  }
126
86
  ```
127
87
 
128
- Static `GET` handlers and `[param]` pages with a `params()` export are written as static files at build time; everything else stays runtime. `pnext build --adapter vercel` emits `.vercel/output` directly.
129
-
130
- ## Types
88
+ ### APIs and everything else
131
89
 
132
- `pnext dev`, `build`, and `typegen` generate route types in `.pnext/types`; import typed route props from `#gen/app/...`.
90
+ A `route.ts` file is an HTTP handler:
133
91
 
134
- ## Benchmarks
135
-
136
- ```sh
137
- bun bench
92
+ ```ts
93
+ // app/api/users/route.ts
94
+ export async function GET(request: NextRequest) {
95
+ return Response.json(await listUsers());
96
+ }
138
97
  ```
139
98
 
140
- Runs pnext and Next.js side by side on the shared fixtures in [`bench/`](./bench/) — dev cold start, warm requests, HMR, production build, shipped bytes — and rewrites [`reference/performance.md`](./reference/performance.md) with your machine's numbers. Don't take our word for it.
99
+ - `proxy.ts` runs before route matching.
100
+ - `loading.tsx`, `error.tsx`, and `not-found.tsx` define per-segment fallbacks.
101
+ - `pnext build` emits static HTML for routes that never read the request, and a server for the ones that do. `pnext start` serves it.
102
+
103
+ ## Learn more
141
104
 
142
- ## Reference
105
+ Apps are file-routed from `app/`: `page.tsx` and `layout.tsx` are Server Components, `route.ts` files are HTTP handlers, `public/` is served from `/`. The reference covers the rest:
143
106
 
144
107
  - [Overview](./reference/overview.md)
108
+ - [Dev server](./reference/dev.md)
145
109
  - [Routing](./reference/routing.md)
146
- - [Metadata](./reference/metadata.md)
110
+ - [Navigation](./reference/navigation.md)
147
111
  - [Rendering](./reference/rendering.md)
112
+ - [Metadata](./reference/metadata.md)
148
113
  - [CSS](./reference/css.md)
149
114
  - [Environment Variables](./reference/env.md)
150
- - [Compatibility](./reference/compat.md)
151
- - [Typegen](./reference/typegen.md)
152
115
  - [Config](./reference/config.md)
116
+ - [Typegen](./reference/typegen.md)
117
+ - [Compatibility](./reference/compat.md)
153
118
  - [Performance](./reference/performance.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wular/pnext",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "pnext": "bin/pnext"
@@ -23,7 +23,8 @@
23
23
  "./link": "./src/api/link.tsx",
24
24
  "./navigation": "./src/api/navigation.ts",
25
25
  "./navigation/client": "./src/api/client-navigation.ts",
26
- "./server": "./src/api/server.ts"
26
+ "./server": "./src/api/server.ts",
27
+ "./config/*": "./config/*"
27
28
  },
28
29
  "scripts": {
29
30
  "lint": "bun scripts/check-compat-boundary.ts && eslint . --max-warnings 0 && tsc --noEmit",