@wular/pnext 0.0.1
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 +153 -0
- package/bin/pnext +67 -0
- package/config/lint/base.js +48 -0
- package/config/ts/base.json +26 -0
- package/config/ts/react.json +13 -0
- package/package.json +70 -0
- package/reference/compat.md +63 -0
- package/reference/config.md +120 -0
- package/reference/css.md +58 -0
- package/reference/dev.md +69 -0
- package/reference/env.md +40 -0
- package/reference/metadata.md +86 -0
- package/reference/navigation.md +149 -0
- package/reference/overview.md +35 -0
- package/reference/performance.md +97 -0
- package/reference/rendering.md +127 -0
- package/reference/routing.md +167 -0
- package/reference/typegen.md +64 -0
- package/src/api/cache.ts +80 -0
- package/src/api/client-cache.ts +9 -0
- package/src/api/client-navigation.ts +279 -0
- package/src/api/dynamic.tsx +102 -0
- package/src/api/link.tsx +119 -0
- package/src/api/navigation.ts +198 -0
- package/src/api/router/events.ts +53 -0
- package/src/api/router/history.ts +70 -0
- package/src/api/router/hub.ts +194 -0
- package/src/api/router/policies.ts +107 -0
- package/src/api/router/runtime.ts +5238 -0
- package/src/api/router/types.ts +299 -0
- package/src/api/router.ts +167 -0
- package/src/api/server.ts +323 -0
- package/src/api/suspense.ts +16 -0
- package/src/cache/context.ts +61 -0
- package/src/cli/adapters/vercel-warm.ts +375 -0
- package/src/cli/adapters/vercel.ts +1310 -0
- package/src/cli/analyze-print.ts +181 -0
- package/src/cli/analyze.ts +328 -0
- package/src/cli/boot-trace.ts +29 -0
- package/src/cli/build.ts +3114 -0
- package/src/cli/dev.ts +276 -0
- package/src/cli/index.ts +196 -0
- package/src/cli/named-bin.ts +119 -0
- package/src/cli/serve-ui.ts +160 -0
- package/src/cli/start.ts +1425 -0
- package/src/client/build.ts +2136 -0
- package/src/client/chunk-fold.ts +526 -0
- package/src/client/entry.ts +1525 -0
- package/src/client/paths.ts +22 -0
- package/src/client/prebuilt.ts +621 -0
- package/src/client/profile.ts +75 -0
- package/src/client/react-compiler.ts +94 -0
- package/src/client/reference-stub.ts +145 -0
- package/src/client/reference.ts +47 -0
- package/src/compat/actions/action-client.ts +531 -0
- package/src/compat/actions/action-dispatch.ts +676 -0
- package/src/compat/actions/action-router.ts +40 -0
- package/src/compat/actions/action-shared.ts +95 -0
- package/src/compat/actions/client-plugin.ts +135 -0
- package/src/compat/actions/client-stub.ts +65 -0
- package/src/compat/actions/config.ts +164 -0
- package/src/compat/actions/detect.ts +208 -0
- package/src/compat/actions/discovery.ts +244 -0
- package/src/compat/actions/early-submit.ts +40 -0
- package/src/compat/actions/endpoint.ts +602 -0
- package/src/compat/actions/flight.ts +52 -0
- package/src/compat/actions/form-state.ts +73 -0
- package/src/compat/actions/hoist.ts +485 -0
- package/src/compat/actions/ids.ts +39 -0
- package/src/compat/actions/index.ts +41 -0
- package/src/compat/actions/instances.ts +144 -0
- package/src/compat/actions/origin.ts +109 -0
- package/src/compat/actions/protocol.ts +125 -0
- package/src/compat/actions/registry.ts +74 -0
- package/src/compat/actions/rewrite.ts +282 -0
- package/src/compat/actions/serve.ts +414 -0
- package/src/compat/actions/server-tag.ts +21 -0
- package/src/compat/actions/unrecognized-error.ts +30 -0
- package/src/compat/adapter/build-complete.ts +257 -0
- package/src/compat/bundler/bun-externals.ts +53 -0
- package/src/compat/bundler/cjs-exports.ts +542 -0
- package/src/compat/bundler/config.ts +363 -0
- package/src/compat/bundler/externals.ts +34 -0
- package/src/compat/bundler/import-meta-url.ts +60 -0
- package/src/compat/bundler/modularize-imports.ts +119 -0
- package/src/compat/bundler/new-url-asset.ts +87 -0
- package/src/compat/bundler/optimize-package-imports.ts +273 -0
- package/src/compat/bundler/polyfill.ts +88 -0
- package/src/compat/bundler/react-compiler.ts +61 -0
- package/src/compat/bundler/react-profiler.tsx +25 -0
- package/src/compat/bundler/relay-transform.ts +116 -0
- package/src/compat/bundler/require-context.ts +281 -0
- package/src/compat/bundler/resolve-extensions.ts +75 -0
- package/src/compat/bundler/source-cache.ts +61 -0
- package/src/compat/bundler/static-imports.ts +25 -0
- package/src/compat/bundler/symlink-imports.ts +119 -0
- package/src/compat/bundler/tsconfig-paths.ts +50 -0
- package/src/compat/bundler/wasm.ts +153 -0
- package/src/compat/bundler/webpack-loaders.ts +685 -0
- package/src/compat/bundler/worker.ts +278 -0
- package/src/compat/cache/build-flags.ts +81 -0
- package/src/compat/cache/build-prerender-errors.ts +163 -0
- package/src/compat/cache/custom-handler.ts +159 -0
- package/src/compat/cache/fetch-patch.ts +745 -0
- package/src/compat/cache/handler.ts +100 -0
- package/src/compat/cache/modern-handler.ts +275 -0
- package/src/compat/cache/resume-data-cache.ts +143 -0
- package/src/compat/cache/revalidate.ts +759 -0
- package/src/compat/cache/runtime-error.ts +124 -0
- package/src/compat/cache/use-cache-transform.ts +961 -0
- package/src/compat/cache/use-cache.ts +1695 -0
- package/src/compat/cache-control.ts +269 -0
- package/src/compat/client/base-path.ts +64 -0
- package/src/compat/client/css-order.ts +36 -0
- package/src/compat/client/errors/control-flow.ts +92 -0
- package/src/compat/client/errors/error-boundary.ts +222 -0
- package/src/compat/client/errors/global-error.ts +238 -0
- package/src/compat/client/errors/install.ts +217 -0
- package/src/compat/client/errors/lazy.ts +53 -0
- package/src/compat/client/errors/primitive-throw.ts +126 -0
- package/src/compat/client/errors/soft-refresh.ts +14 -0
- package/src/compat/client/link-status.ts +86 -0
- package/src/compat/client/nav-compat-runtime.ts +57 -0
- package/src/compat/client/nav-compat.ts +42 -0
- package/src/compat/client/navigation-scroll.ts +154 -0
- package/src/compat/client/optimistic-routing.ts +206 -0
- package/src/compat/client/prefetch-cache.ts +111 -0
- package/src/compat/client/route-announcer.ts +72 -0
- package/src/compat/client/segment-cache-policy.ts +159 -0
- package/src/compat/client/segment-cache.ts +1077 -0
- package/src/compat/client/segment-prefetch.ts +375 -0
- package/src/compat/client/trailing-slash.ts +24 -0
- package/src/compat/css/chunking.ts +254 -0
- package/src/compat/css/inline-css.ts +73 -0
- package/src/compat/css/lightningcss.ts +90 -0
- package/src/compat/css/modules.ts +373 -0
- package/src/compat/css/nonce.ts +30 -0
- package/src/compat/css/sass-plugin.ts +65 -0
- package/src/compat/css/sass.ts +392 -0
- package/src/compat/css/styled-jsx-runtime.ts +80 -0
- package/src/compat/css/styled-jsx.ts +49 -0
- package/src/compat/edge-runtime.ts +71 -0
- package/src/compat/export/client.ts +112 -0
- package/src/compat/export/index.ts +272 -0
- package/src/compat/export/standalone.ts +207 -0
- package/src/compat/image-optimizer/cache.ts +119 -0
- package/src/compat/image-optimizer/detect.ts +143 -0
- package/src/compat/image-optimizer/index.ts +601 -0
- package/src/compat/image-optimizer/source.ts +243 -0
- package/src/compat/index.ts +458 -0
- package/src/compat/lifecycle/after-scope.ts +86 -0
- package/src/compat/lifecycle/after.ts +173 -0
- package/src/compat/lifecycle/error-funnel.ts +306 -0
- package/src/compat/lifecycle/error-serialize.ts +87 -0
- package/src/compat/lifecycle/error-ui.ts +167 -0
- package/src/compat/lifecycle/instrumentation-client.ts +138 -0
- package/src/compat/lifecycle/instrumentation.ts +277 -0
- package/src/compat/lifecycle/node-console.ts +19 -0
- package/src/compat/lifecycle/testmode.ts +263 -0
- package/src/compat/mdx/compile.ts +219 -0
- package/src/compat/mdx/next-mdx-stub.ts +46 -0
- package/src/compat/mdx/plugin.ts +37 -0
- package/src/compat/metadata-route-artifacts.ts +458 -0
- package/src/compat/metadata.ts +295 -0
- package/src/compat/middleware/manifest.ts +210 -0
- package/src/compat/misc/action-return.ts +173 -0
- package/src/compat/next/cache.ts +211 -0
- package/src/compat/next/canonical-url.ts +35 -0
- package/src/compat/next/client-cache.ts +57 -0
- package/src/compat/next/client-navigation.ts +313 -0
- package/src/compat/next/client-only.ts +3 -0
- package/src/compat/next/client-script.tsx +215 -0
- package/src/compat/next/client-server.ts +39 -0
- package/src/compat/next/config-loader.ts +569 -0
- package/src/compat/next/config.ts +29 -0
- package/src/compat/next/constants.cjs +6 -0
- package/src/compat/next/constants.ts +6 -0
- package/src/compat/next/custom-server.ts +236 -0
- package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
- package/src/compat/next/dynamic.tsx +46 -0
- package/src/compat/next/error.tsx +148 -0
- package/src/compat/next/font/cache.ts +171 -0
- package/src/compat/next/font/google.ts +2 -0
- package/src/compat/next/font/index.ts +8 -0
- package/src/compat/next/font/local.ts +5 -0
- package/src/compat/next/font/runtime-client.ts +71 -0
- package/src/compat/next/font/runtime.ts +974 -0
- package/src/compat/next/font/shared.ts +281 -0
- package/src/compat/next/form.tsx +156 -0
- package/src/compat/next/head.tsx +10 -0
- package/src/compat/next/headers.ts +247 -0
- package/src/compat/next/image/config.ts +196 -0
- package/src/compat/next/image/optimizer.ts +96 -0
- package/src/compat/next/image/patterns.ts +103 -0
- package/src/compat/next/image/shared.ts +141 -0
- package/src/compat/next/image/static-metadata.ts +283 -0
- package/src/compat/next/image/validate.ts +269 -0
- package/src/compat/next/image-client.tsx +215 -0
- package/src/compat/next/image-props.ts +575 -0
- package/src/compat/next/image-usage.ts +102 -0
- package/src/compat/next/image.tsx +56 -0
- package/src/compat/next/index.ts +1 -0
- package/src/compat/next/legacy-image.tsx +97 -0
- package/src/compat/next/link-usage.ts +29 -0
- package/src/compat/next/link-validation-transform.ts +200 -0
- package/src/compat/next/link.tsx +466 -0
- package/src/compat/next/navigation.cjs +21 -0
- package/src/compat/next/navigation.ts +188 -0
- package/src/compat/next/offline.ts +51 -0
- package/src/compat/next/og.ts +324 -0
- package/src/compat/next/optimistic-route-state.ts +188 -0
- package/src/compat/next/preferred-region.ts +39 -0
- package/src/compat/next/redirects.ts +131 -0
- package/src/compat/next/resource-hints.ts +136 -0
- package/src/compat/next/rewrites.ts +350 -0
- package/src/compat/next/root-params.ts +142 -0
- package/src/compat/next/router.cjs +49 -0
- package/src/compat/next/router.ts +143 -0
- package/src/compat/next/script.tsx +355 -0
- package/src/compat/next/server-only.ts +3 -0
- package/src/compat/next/server.ts +28 -0
- package/src/compat/next/svgr.ts +58 -0
- package/src/compat/next/telemetry.ts +77 -0
- package/src/compat/next/user-agent.ts +100 -0
- package/src/compat/next/web-vitals.ts +56 -0
- package/src/compat/otel/api.ts +95 -0
- package/src/compat/otel/client-trace-metadata.ts +71 -0
- package/src/compat/otel/fetch-span.ts +77 -0
- package/src/compat/otel/tracer.ts +944 -0
- package/src/compat/pages/client-plugin.ts +108 -0
- package/src/compat/pages/index.ts +527 -0
- package/src/compat/pages/router-state.ts +94 -0
- package/src/compat/ppr/io.ts +38 -0
- package/src/compat/ppr/missing-root-params.ts +105 -0
- package/src/compat/ppr/root-params-scan.ts +164 -0
- package/src/compat/ppr/root-params-transform.ts +75 -0
- package/src/compat/ppr/root-params.ts +129 -0
- package/src/compat/ppr/segment-config-incompat.ts +34 -0
- package/src/compat/protocol.ts +202 -0
- package/src/compat/react/client.ts +59 -0
- package/src/compat/react/compiler-runtime.ts +60 -0
- package/src/compat/react/dom-client.ts +115 -0
- package/src/compat/react/dom-react-server.ts +20 -0
- package/src/compat/react/dom-server.ts +40 -0
- package/src/compat/react/dom.ts +154 -0
- package/src/compat/react/preact.ts +522 -0
- package/src/compat/react/react-server.ts +84 -0
- package/src/compat/react/router-shim.ts +26 -0
- package/src/compat/react/server-component-use.ts +48 -0
- package/src/compat/react/server-inserted-html.ts +87 -0
- package/src/compat/react/server.ts +156 -0
- package/src/compat/react/view-transition.ts +60 -0
- package/src/compat/register/actions.ts +875 -0
- package/src/compat/register/boot.ts +141 -0
- package/src/compat/register/build-tier.ts +11 -0
- package/src/compat/register/build.ts +182 -0
- package/src/compat/register/bundler.ts +587 -0
- package/src/compat/register/cache.ts +85 -0
- package/src/compat/register/client-errors.ts +18 -0
- package/src/compat/register/config.ts +17 -0
- package/src/compat/register/css-extras.ts +120 -0
- package/src/compat/register/edge-runtime.ts +6 -0
- package/src/compat/register/errors.ts +46 -0
- package/src/compat/register/export.ts +23 -0
- package/src/compat/register/font.ts +36 -0
- package/src/compat/register/hooks.ts +34 -0
- package/src/compat/register/image.ts +133 -0
- package/src/compat/register/index.ts +111 -0
- package/src/compat/register/instrumentation-client.ts +35 -0
- package/src/compat/register/lifecycle.ts +86 -0
- package/src/compat/register/mdx.ts +48 -0
- package/src/compat/register/middleware.ts +36 -0
- package/src/compat/register/misc.ts +44 -0
- package/src/compat/register/otel.ts +288 -0
- package/src/compat/register/pages-api.ts +473 -0
- package/src/compat/register/ppr.ts +56 -0
- package/src/compat/register/protocol.ts +57 -0
- package/src/compat/register/proxy.ts +127 -0
- package/src/compat/register/render.ts +268 -0
- package/src/compat/register/routing.ts +410 -0
- package/src/compat/register/segment.ts +1903 -0
- package/src/compat/register/static-image.ts +21 -0
- package/src/compat/register/typed-routes.ts +35 -0
- package/src/compat/register/usecache.ts +131 -0
- package/src/compat/register/validation.ts +56 -0
- package/src/compat/segment/loading-boundary.ts +113 -0
- package/src/compat/segment/page-slot.ts +200 -0
- package/src/compat/segment/tree.ts +481 -0
- package/src/compat/segment/vary-key.ts +102 -0
- package/src/compat/segment/vary-params.ts +551 -0
- package/src/compat/static-params.ts +33 -0
- package/src/compat/tsconfig-defaults.ts +301 -0
- package/src/compat/typecheck/index.ts +1481 -0
- package/src/compat/typecheck/worker.ts +26 -0
- package/src/compat/typed-routes/index.ts +92 -0
- package/src/compat/typed-routes/manifest.ts +356 -0
- package/src/compat/typed-routes/typegen.ts +566 -0
- package/src/compat/validation/errors.ts +159 -0
- package/src/compat/validation/index.ts +1770 -0
- package/src/compat/validation/prerender-diagnostics.ts +1508 -0
- package/src/compat-bootstrap.ts +67 -0
- package/src/config.ts +218 -0
- package/src/css/build.ts +697 -0
- package/src/css/index.ts +2 -0
- package/src/css/postcss.ts +236 -0
- package/src/css/worker.ts +34 -0
- package/src/dev/client-actions.ts +35 -0
- package/src/dev/client-chunk-store.ts +92 -0
- package/src/dev/client-key-cache.ts +178 -0
- package/src/dev/global-css-cache.ts +212 -0
- package/src/dev/imports.ts +2430 -0
- package/src/dev/module-cache.ts +721 -0
- package/src/dev/module-generations.ts +38 -0
- package/src/dev/module-transform.ts +188 -0
- package/src/dev/node-module-bundle-cache.ts +63 -0
- package/src/dev/restart-cache.ts +10 -0
- package/src/dev/route-bundle-key-cache.ts +154 -0
- package/src/dev/route-facts-cache.ts +223 -0
- package/src/dev/server.ts +1710 -0
- package/src/dynamic/source.ts +307 -0
- package/src/dynamic/tree-shake.ts +262 -0
- package/src/env.ts +92 -0
- package/src/extensions.ts +1898 -0
- package/src/index.ts +34 -0
- package/src/internal.ts +43 -0
- package/src/islands/boundary-error.ts +8 -0
- package/src/islands/static-children.ts +37 -0
- package/src/islands/static-slots.ts +106 -0
- package/src/ppr-postpone.ts +24 -0
- package/src/ppr.ts +784 -0
- package/src/proxy.ts +752 -0
- package/src/render/hooks.ts +384 -0
- package/src/render/index.ts +1 -0
- package/src/render/island-context.ts +47 -0
- package/src/render/metadata.ts +857 -0
- package/src/render/renderer.ts +7391 -0
- package/src/render/resource-hints.ts +44 -0
- package/src/render/slots.tsx +679 -0
- package/src/request/context.ts +396 -0
- package/src/resolve/engine.ts +219 -0
- package/src/resolve/imports.ts +1104 -0
- package/src/resolve/scan-facts.ts +474 -0
- package/src/resolve/source-text.ts +86 -0
- package/src/routing/forwarded.ts +41 -0
- package/src/routing/handler.ts +271 -0
- package/src/routing/href.ts +203 -0
- package/src/routing/metadata.ts +1018 -0
- package/src/routing/request-runtime.ts +43 -0
- package/src/routing/routes.ts +2560 -0
- package/src/routing/slots.ts +432 -0
- package/src/runtime/server.ts +3453 -0
- package/src/runtime/vendor.ts +1160 -0
- package/src/style-modules.d.ts +9 -0
- package/src/typegen.ts +151 -0
- package/src/types.ts +725 -0
- package/src/utils/ansi.ts +9 -0
- package/src/utils/content-type.ts +31 -0
- package/src/utils/decode.ts +7 -0
- package/src/utils/dev-profile.ts +31 -0
- package/src/utils/error-log.ts +29 -0
- package/src/utils/fs-cache.ts +31 -0
- package/src/utils/fs.ts +119 -0
- package/src/utils/html.ts +46 -0
- package/src/utils/serialize.ts +378 -0
- package/src/utils/source.ts +35 -0
- package/src/utils/verbose.ts +39 -0
- package/tsconfig.json +10 -0
package/reference/dev.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Dev Server
|
|
2
|
+
|
|
3
|
+
`pnext dev` starts a Bun HTTP server for local development.
|
|
4
|
+
|
|
5
|
+
## CLI
|
|
6
|
+
|
|
7
|
+
```txt
|
|
8
|
+
pnext dev [directory] [--port 3000] [--hostname 127.0.0.1]
|
|
9
|
+
pnext build [directory] [--adapter vercel] [--verbose]
|
|
10
|
+
pnext start [directory] [--port 3000] [--hostname 127.0.0.1]
|
|
11
|
+
pnext analyze [route] [directory] [--brotli] [--files] [--json]
|
|
12
|
+
pnext typegen [directory]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`analyze` takes a route (`/users/[id]` or a concrete path like `/users/ada`) to report on that route only.
|
|
16
|
+
|
|
17
|
+
Add `@wular/pnext` as a package dependency and put `pnext` in package scripts:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@wular/pnext": "^0.0.0"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"dev": "pnext dev",
|
|
26
|
+
"build": "pnext build",
|
|
27
|
+
"start": "pnext start",
|
|
28
|
+
"analyze": "pnext analyze"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The package exposes a `pnext` binary. Package managers make it available inside script `PATH`.
|
|
34
|
+
|
|
35
|
+
## Processes
|
|
36
|
+
|
|
37
|
+
pnext processes are labelled in Activity Monitor and `ps`: `pnext dev` shows
|
|
38
|
+
up as a single `pnext-dev` server process, `pnext build` as `pnext-build`, and
|
|
39
|
+
esbuild's service process as `pnext-esbuild`. Tailwind and PostCSS run in a
|
|
40
|
+
worker thread inside the server process, not as child processes.
|
|
41
|
+
|
|
42
|
+
The dev server re-execs itself — same pid — when its memory passes
|
|
43
|
+
`PNEXT_DEV_MAX_RSS_MB` (default 2048). The esbuild service is stopped and
|
|
44
|
+
respawned past `PNEXT_DEV_MAX_ESBUILD_RSS_MB` (default 1024).
|
|
45
|
+
|
|
46
|
+
## Responsibilities
|
|
47
|
+
|
|
48
|
+
- Scan the route tree.
|
|
49
|
+
- Render matching pages on request.
|
|
50
|
+
- Serve static assets from `public/`.
|
|
51
|
+
- Build client route entries on demand with esbuild.
|
|
52
|
+
- Cache generated client entries in `.pnext/cache`.
|
|
53
|
+
- Notify the browser of route-tree changes through `/__pnext/events`.
|
|
54
|
+
|
|
55
|
+
## Cache
|
|
56
|
+
|
|
57
|
+
Client entry cache keys include the route id and source content hash. Cached entries are stored below `.pnext/cache/client`.
|
|
58
|
+
|
|
59
|
+
## Reload Events
|
|
60
|
+
|
|
61
|
+
The dev server exposes a server-sent events stream at `/__pnext/events`. Browser pages reload when app files change.
|
|
62
|
+
|
|
63
|
+
## Typechecking
|
|
64
|
+
|
|
65
|
+
The dev server does not typecheck in the request path. Run package lint or typecheck commands separately. `pnext build` under `compat.next` typechecks off-thread, in parallel with bundling, and reports it on its own line.
|
|
66
|
+
|
|
67
|
+
## Server Adapter
|
|
68
|
+
|
|
69
|
+
Use `pnext build --adapter vercel` to write Vercel Build Output at `.vercel/output`. Static pages and static `GET` route-handler outputs are emitted as files. Everything dynamic — pages, route handlers, and `proxy.ts` — is served by a single `_pnext` function on Vercel's Bun runtime; the proxy's `config.matcher` patterns become routes into it.
|
package/reference/env.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Environment Variables
|
|
2
|
+
|
|
3
|
+
pnext loads `.env*` files from the project root, not from `src/`.
|
|
4
|
+
|
|
5
|
+
Variables already set on `process.env` win. Missing variables are loaded in this order:
|
|
6
|
+
|
|
7
|
+
1. `.env.$NODE_ENV.local`
|
|
8
|
+
2. `.env.local` except when `NODE_ENV` is `test`
|
|
9
|
+
3. `.env.$NODE_ENV`
|
|
10
|
+
4. `.env`
|
|
11
|
+
|
|
12
|
+
If `NODE_ENV` is not set, `pnext dev` uses `development`; other commands use `production`.
|
|
13
|
+
|
|
14
|
+
## Browser Variables
|
|
15
|
+
|
|
16
|
+
Browser bundles inline only:
|
|
17
|
+
|
|
18
|
+
- `process.env.NODE_ENV`
|
|
19
|
+
- `process.env.NEXT_PUBLIC_*`
|
|
20
|
+
- `process.env.PNEXT_PUBLIC_*`
|
|
21
|
+
|
|
22
|
+
Other variables stay server-only. Server Components, layouts, route handlers, and proxy can read them through `process.env`.
|
|
23
|
+
|
|
24
|
+
## Variable References
|
|
25
|
+
|
|
26
|
+
Values can reference earlier environment variables:
|
|
27
|
+
|
|
28
|
+
```env
|
|
29
|
+
API_HOST=api.example.com
|
|
30
|
+
NEXT_PUBLIC_API_URL=https://$API_HOST
|
|
31
|
+
PORT=${APP_PORT:-3000}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`${VAR}` and `${VAR:-fallback}` both work. A variable already on `process.env` wins over the file's own value.
|
|
35
|
+
|
|
36
|
+
Escape `$` when it should be literal:
|
|
37
|
+
|
|
38
|
+
```env
|
|
39
|
+
PRICE=\$10
|
|
40
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Metadata
|
|
2
|
+
|
|
3
|
+
Core metadata takes no request input, so it resolves at build time. `pnext build` evaluates metadata objects, `metadata()`/`viewport()` functions, and metadata route code, and `pnext start` serves the prebuilt head for static routes without recomputing it per request.
|
|
4
|
+
|
|
5
|
+
Use `compat.next` for Next.js request-time metadata APIs such as `generateMetadata()`, `generateViewport()`, streaming metadata, `headers()`, `cookies()`, and full `next/og` behavior.
|
|
6
|
+
|
|
7
|
+
## Page and Layout Exports
|
|
8
|
+
|
|
9
|
+
Pages and layouts can export `metadata` as an object:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import type { Metadata } from '@wular/pnext';
|
|
13
|
+
|
|
14
|
+
export const metadata: Metadata = {
|
|
15
|
+
title: 'About',
|
|
16
|
+
description: 'About the team',
|
|
17
|
+
};
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
They can also export `metadata()` as a no-arg build-time function:
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
export async function metadata() {
|
|
24
|
+
const site = await getSiteMetadata();
|
|
25
|
+
return {
|
|
26
|
+
title: site.title,
|
|
27
|
+
description: site.description,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Metadata is resolved from root layout to page. Nearer segments override or extend earlier segments.
|
|
33
|
+
|
|
34
|
+
pnext follows the Next.js metadata field shapes for common fields such as `title`, `description`, `metadataBase`, `openGraph`, `twitter`, `robots`, `icons`, `manifest`, `alternates`, `verification`, `appleWebApp`, `appLinks`, and `other`. Use the [Next.js metadata fields reference](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#metadata-fields) for the full field schema.
|
|
35
|
+
|
|
36
|
+
## Viewport
|
|
37
|
+
|
|
38
|
+
Pages and layouts can export `viewport` as an object or a build-time function:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import type { Viewport } from '@wular/pnext';
|
|
42
|
+
|
|
43
|
+
export const viewport: Viewport = {
|
|
44
|
+
width: 'device-width',
|
|
45
|
+
initialScale: 1,
|
|
46
|
+
themeColor: '#0b0b0b',
|
|
47
|
+
};
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use `compat.next` for `generateViewport()` with route props.
|
|
51
|
+
|
|
52
|
+
## File Conventions
|
|
53
|
+
|
|
54
|
+
Core supports static metadata files in route segments:
|
|
55
|
+
|
|
56
|
+
- `icon`, `apple-icon`, `opengraph-image`, `twitter-image` — `.ico`, `.jpg`, `.jpeg`, `.png`, `.svg`, `.gif`, or `.webp`, optionally numbered (`icon2.png`)
|
|
57
|
+
- `sitemap.xml`
|
|
58
|
+
- `favicon.ico`, `robots.txt`, `manifest.json`, `manifest.webmanifest` — app root only
|
|
59
|
+
|
|
60
|
+
Static files are copied during `pnext build` and served from the built public output. Image and sitemap files apply to their segment and its descendants; the root-only files are global.
|
|
61
|
+
|
|
62
|
+
## Code Metadata Files
|
|
63
|
+
|
|
64
|
+
Core also supports code variants of each, in `.tsx`, `.ts`, `.jsx`, `.js`, or `.mjs`: `icon`, `apple-icon`, `opengraph-image`, `twitter-image`, `sitemap`, plus root-only `robots` and `manifest`.
|
|
65
|
+
|
|
66
|
+
In core, these modules run at build time. Their generated links and route outputs are stored in the build output, and `pnext start` does not recompute them during page requests.
|
|
67
|
+
|
|
68
|
+
Code metadata route handlers can keep their metadata-route signatures, including `generateImageMetadata()` and `generateSitemaps()`, but core treats their results as build artifacts. Request-time variants that depend on `headers()`, `cookies()`, or dynamic request state belong in `compat.next`.
|
|
69
|
+
|
|
70
|
+
## Precedence
|
|
71
|
+
|
|
72
|
+
File-based metadata has higher priority than page/layout metadata for images and icons. Explicit page/layout image metadata still wins when it defines its own image list.
|
|
73
|
+
|
|
74
|
+
The request path determines which segment metadata files apply. Route groups and parallel-route slot directories do not add URL path segments, but their metadata file output names are disambiguated at build time.
|
|
75
|
+
|
|
76
|
+
## Static Params
|
|
77
|
+
|
|
78
|
+
Core dynamic routes use `params()` for build-time param selection:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
export function params() {
|
|
82
|
+
return [{ slug: 'intro' }];
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`generateStaticParams()` is a Next compat API. In `compat.next`, pnext treats it as the fallback static params provider when a module does not export core `params()`.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Navigation
|
|
2
|
+
|
|
3
|
+
pnext navigation APIs are split by runtime so server routes do not import browser router code.
|
|
4
|
+
|
|
5
|
+
## Typed Hrefs
|
|
6
|
+
|
|
7
|
+
Use `href()` when building URLs outside JSX.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { href } from '@wular/pnext/navigation';
|
|
11
|
+
|
|
12
|
+
const userHref = href('/users/[id]', {
|
|
13
|
+
params: { id: 'ada' },
|
|
14
|
+
search: { tab: 'runs' },
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Dynamic params are checked from generated route types. Routes without params do not take a `params` object.
|
|
19
|
+
|
|
20
|
+
## Link
|
|
21
|
+
|
|
22
|
+
`<Link>` renders a normal anchor with typed route params and pnext prefetch metadata.
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { Link } from '@wular/pnext/link';
|
|
26
|
+
|
|
27
|
+
export function Nav() {
|
|
28
|
+
return (
|
|
29
|
+
<Link href="/users/[id]" params={{ id: 'ada' }} search={{ tab: 'runs' }}>
|
|
30
|
+
Ada
|
|
31
|
+
</Link>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Supported props:
|
|
37
|
+
|
|
38
|
+
- `href`: generated route path.
|
|
39
|
+
- `params`: required for dynamic routes.
|
|
40
|
+
- `search`: query params.
|
|
41
|
+
- `hash`: hash fragment.
|
|
42
|
+
- `prefetch`: `false`, `'intent'`, `'visible'`, or `'load'`.
|
|
43
|
+
- `replace`: marks the navigation as history replace.
|
|
44
|
+
- `scroll={false}`: preserves scroll on client navigation.
|
|
45
|
+
- `onNavigate`: client-only same-origin navigation callback that can call `preventDefault()`.
|
|
46
|
+
- `onPrefetchStart` / `onPrefetchFinish`: fire when this link's prefetch begins and settles, in every prefetch mode.
|
|
47
|
+
|
|
48
|
+
Use a normal `<a>` for external URLs or intentionally untyped links.
|
|
49
|
+
|
|
50
|
+
## Soft Navigation
|
|
51
|
+
|
|
52
|
+
`<Link>` clicks and `router.push()`/`router.replace()` swap the page in place instead of reloading the document, so shared chunks and CSS are never re-downloaded. Back and forward stay soft and restore scroll. Cross-origin targets, non-HTML responses, and fetch failures fall back to a full page load.
|
|
53
|
+
|
|
54
|
+
`prefetch` on `<Link>` warms the target page and its assets:
|
|
55
|
+
|
|
56
|
+
- `'intent'` (default): on hover, touch, or focus.
|
|
57
|
+
- `'visible'`: when the link enters the viewport.
|
|
58
|
+
- `'load'`: as soon as the link renders.
|
|
59
|
+
- `false`: never; pair with `router.prefetch(href)` for manual control.
|
|
60
|
+
|
|
61
|
+
Prefetched pages expire after 30 seconds and each navigation refetches on the next visit, so pages always reflect fresh server state. Prefetch is a no-op under `pnext dev`.
|
|
62
|
+
|
|
63
|
+
## Redirects
|
|
64
|
+
|
|
65
|
+
Use `notFound()` to render the nearest `not-found.tsx` fallback with a 404 response.
|
|
66
|
+
|
|
67
|
+
Use `redirect()` to stop server rendering and return a redirect response. In the browser it navigates directly.
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { redirect } from '@wular/pnext/navigation';
|
|
71
|
+
|
|
72
|
+
export default async function Page() {
|
|
73
|
+
const user = await getUser();
|
|
74
|
+
if (!user) redirect('/login');
|
|
75
|
+
return <Dashboard user={user} />;
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`redirect()` returns a temporary 307 redirect on the server. `permanentRedirect()` returns 308.
|
|
80
|
+
|
|
81
|
+
Client event handlers can call `redirect()` too. Keep it as the last statement, or `return redirect(...)`, because browser navigation does not synchronously stop the rest of the handler. Use `useRouter().push()` or `useRouter().replace()` when you want explicit history control.
|
|
82
|
+
|
|
83
|
+
For typed dynamic redirects, build the URL with `href()`:
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { href, redirect } from '@wular/pnext/navigation';
|
|
87
|
+
|
|
88
|
+
redirect(href('/users/[id]', { params: { id: 'ada' } }));
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Client Hooks
|
|
92
|
+
|
|
93
|
+
Client navigation hooks live in `@wular/pnext/navigation/client`.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
'use client';
|
|
97
|
+
|
|
98
|
+
import { useRouter, useSearchParams } from '@wular/pnext/navigation/client';
|
|
99
|
+
|
|
100
|
+
export function SortButton() {
|
|
101
|
+
const router = useRouter();
|
|
102
|
+
const searchParams = useSearchParams();
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<button
|
|
106
|
+
onClick={() => {
|
|
107
|
+
const search = new URLSearchParams(searchParams.toString());
|
|
108
|
+
search.set('sort', 'asc');
|
|
109
|
+
router.replace('/products', { search, scroll: false });
|
|
110
|
+
}}
|
|
111
|
+
>
|
|
112
|
+
Sort
|
|
113
|
+
</button>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Available hooks:
|
|
119
|
+
|
|
120
|
+
- `useRouter()`: `push`, `replace`, `prefetch`, `refresh`, `back`, and `forward`.
|
|
121
|
+
- `usePathname()`: current browser pathname.
|
|
122
|
+
- `useSearchParams()`: read-only URL search params.
|
|
123
|
+
- `useParams<Route>()`: current route params from the initial route state.
|
|
124
|
+
- `useRoute<Route>()`: pathname, route template, params, and search params together.
|
|
125
|
+
- `useLinkStatus()`: pending shape for link feedback.
|
|
126
|
+
|
|
127
|
+
`push()` and `replace()` are typed like `href()`. They soft-navigate when the path or query changes and fall back to the History API for same-URL hash updates. Cross-origin targets become a full page load. `refresh()` re-fetches the current route in place. `useLinkStatus()` currently always reports `{ pending: false }`.
|
|
128
|
+
|
|
129
|
+
## Dynamic Components
|
|
130
|
+
|
|
131
|
+
Use `dynamic()` for lazily loaded Client Components.
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { dynamic } from '@wular/pnext/dynamic';
|
|
135
|
+
|
|
136
|
+
const Chart = dynamic(() => import('./chart').then(module => module.Chart), {
|
|
137
|
+
load: 'visible',
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For default exports, use the shorter literal form:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
const Chart = dynamic('./chart');
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Literal dynamic imports work from Server Components and Client Components. If the target module starts with `"use client"`, pnext renders it as a Client Component island; otherwise it renders on the server.
|
|
148
|
+
|
|
149
|
+
Dynamic Client Components loaded from Server Components render HTML on the server by default and hydrate when the chunk loads. Pass `ssr: false` for browser-only output. `load: 'visible'` implies browser-only output unless `ssr: true` is set. Inside Client Components, `dynamic()` is a browser lazy loader.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# pnext
|
|
2
|
+
|
|
3
|
+
pnext is a Preact framework for file-routed applications. It keeps familiar Next-style routing and rendering semantics where they help, and diverges where pnext needs a simpler or smaller model.
|
|
4
|
+
|
|
5
|
+
It renders on the server by default, passes request data through explicit props, provides strong generated route type safety, and ships client JavaScript only for components that opt into running in the browser.
|
|
6
|
+
|
|
7
|
+
## Core Model
|
|
8
|
+
|
|
9
|
+
- Routes live in an `app/` directory.
|
|
10
|
+
- `page.tsx` files define UI routes.
|
|
11
|
+
- `layout.tsx` files export metadata, viewport, and can wrap descendant pages.
|
|
12
|
+
- `route.ts` files define HTTP handlers.
|
|
13
|
+
- `proxy.ts` or `middleware.ts` can run before route matching.
|
|
14
|
+
- `loading.tsx`, `error.tsx`, and `not-found.tsx` define route fallbacks.
|
|
15
|
+
- Pages and layouts are pnext Server Components by default.
|
|
16
|
+
- Server components can be async.
|
|
17
|
+
- Pages without client components emit HTML without pnext client JavaScript.
|
|
18
|
+
- Client Components can be loaded with `dynamic({ load: 'visible' })` and deferred until visible.
|
|
19
|
+
- React compatibility mode supported via `compat.react`.
|
|
20
|
+
- Next compatibility mode supported via `compat.next`.
|
|
21
|
+
- Experimental React Compiler support for React-style Client Components via `compat.reactCompiler`.
|
|
22
|
+
|
|
23
|
+
## References
|
|
24
|
+
|
|
25
|
+
- [Config](./config.md)
|
|
26
|
+
- [Routing](./routing.md)
|
|
27
|
+
- [Metadata](./metadata.md)
|
|
28
|
+
- [Navigation](./navigation.md)
|
|
29
|
+
- [Rendering](./rendering.md)
|
|
30
|
+
- [CSS](./css.md)
|
|
31
|
+
- [Environment Variables](./env.md)
|
|
32
|
+
- [Compatibility](./compat.md)
|
|
33
|
+
- [Typegen](./typegen.md)
|
|
34
|
+
- [Dev Server](./dev.md)
|
|
35
|
+
- [Performance](./performance.md)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Performance
|
|
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
|
|
8
|
+
- Next.js: 16.2.12
|
|
9
|
+
- Runs per metric: 3 (first discarded), medians reported
|
|
10
|
+
|
|
11
|
+
Every fixture under `bench/fixtures/` runs unmodified on both frameworks, so the
|
|
12
|
+
two columns render the same app from the same source.
|
|
13
|
+
|
|
14
|
+
## Fixture: hello-world
|
|
15
|
+
|
|
16
|
+
| Metric | pnext | Next.js | Ratio |
|
|
17
|
+
| --- | --- | --- | --- |
|
|
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 |
|
|
23
|
+
| 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 |
|
|
25
|
+
| First-page JS files | 3 | 5 | 1.67x |
|
|
26
|
+
| Zero-island route client JS | 4.43 KB | 502.47 KB | 113.36x |
|
|
27
|
+
|
|
28
|
+
Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
|
|
29
|
+
|
|
30
|
+
## Fixture: ssr
|
|
31
|
+
|
|
32
|
+
| Metric | pnext | Next.js | Ratio |
|
|
33
|
+
| --- | --- | --- | --- |
|
|
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 |
|
|
41
|
+
| First-page JS files | 4 | 6 | 1.50x |
|
|
42
|
+
| Zero-island route client JS | 4.46 KB | 502.47 KB | 112.56x |
|
|
43
|
+
|
|
44
|
+
Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
|
|
45
|
+
|
|
46
|
+
## Fixture: dashboard
|
|
47
|
+
|
|
48
|
+
| Metric | pnext | Next.js | Ratio |
|
|
49
|
+
| --- | --- | --- | --- |
|
|
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 |
|
|
58
|
+
|
|
59
|
+
Ratio is Next.js / pnext, so above `1.00x` means pnext is ahead.
|
|
60
|
+
|
|
61
|
+
## Client runtime bundles
|
|
62
|
+
|
|
63
|
+
| Runtime | Raw | Gzip | Brotli |
|
|
64
|
+
| --- | --- | --- | --- |
|
|
65
|
+
| router-prefetch-only | 473 B | 348 B | 283 B |
|
|
66
|
+
| combined-router-hydrator | 10.47 KB | 4.47 KB | 4.07 KB |
|
|
67
|
+
|
|
68
|
+
## Targets
|
|
69
|
+
|
|
70
|
+
| Target | Limit | Measured | Status |
|
|
71
|
+
| --- | --- | --- | --- |
|
|
72
|
+
| Dev cold start, ssr fixture (pnext) | <= 150 ms | 93.8 ms | PASS |
|
|
73
|
+
| Router runtime | <= 1.00 KB gzip | 348 B gzip | PASS |
|
|
74
|
+
| Hydrated-route framework tax | <= 5.00 KB gzip | 4.47 KB gzip | PASS |
|
|
75
|
+
| Zero-island route client JS (ssr `/about`) | 0 B (core pnext) | 4.46 KB (compat.next) | not exercised |
|
|
76
|
+
|
|
77
|
+
## Notes
|
|
78
|
+
|
|
79
|
+
- First-page client JS counts the `<script src>` and `rel="modulepreload"` assets the
|
|
80
|
+
route executes, fetched from each framework's own production server; `noModule`
|
|
81
|
+
polyfills and speculative `rel="preload"` hints for other routes are excluded on both sides.
|
|
82
|
+
- Dev cold start wipes the output directory, spawns the dev server on a free port and
|
|
83
|
+
stops at the readiness banner; first page HTML is the GET that follows it, so it
|
|
84
|
+
includes on-demand compilation.
|
|
85
|
+
- 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.
|
|
87
|
+
- Fixtures enable `compat.next` so one source tree runs on both frameworks. That ships
|
|
88
|
+
pnext's Next-compat navigation client, which a core pnext app does not carry — the
|
|
89
|
+
0 B zero-island budget is a core-pnext invariant this suite does not exercise.
|
|
90
|
+
|
|
91
|
+
## Commands
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
bun bench # every fixture, both frameworks
|
|
95
|
+
bun bench/run.ts ssr # one fixture
|
|
96
|
+
bun bench/run.ts all --framework=pnext --runs=5
|
|
97
|
+
```
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Rendering
|
|
2
|
+
|
|
3
|
+
pnext renders on the server first and sends browser JavaScript only for interactive components.
|
|
4
|
+
|
|
5
|
+
## Server Components
|
|
6
|
+
|
|
7
|
+
Pages and layout components are Server Components. They run only on the server, so they can read files, query databases, call internal services, and use server-only dependencies without adding that code to the browser bundle.
|
|
8
|
+
|
|
9
|
+
A `layout.tsx` file does not need a default export. Use metadata-only layouts when a segment only needs to set page metadata.
|
|
10
|
+
|
|
11
|
+
Use Server Components for data loading and non-interactive UI:
|
|
12
|
+
|
|
13
|
+
- Render HTML from server data.
|
|
14
|
+
- Keep expensive parsing, formatting, and data libraries on the server.
|
|
15
|
+
- Pass serializable props to Client Components for interactive parts.
|
|
16
|
+
- Render Client Components inside Server Components when part of the page needs interactivity.
|
|
17
|
+
- Read request data from explicit page/layout props when a route depends on headers or cookies.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
export default async function Page() {
|
|
21
|
+
const post = await getPost();
|
|
22
|
+
return <article>{post.title}</article>;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Server components can be async. Static builds wait for the final HTML.
|
|
27
|
+
|
|
28
|
+
Pages and layouts that read `request` receive a `NextRequest` compatible object. It extends the standard Request with convenience access for cookies and headers.
|
|
29
|
+
|
|
30
|
+
Use `request.cookies.get()`, `request.headers.get()`, `request.method`, and `request.url` from props. It keeps static routes static and makes request-dependent routes dynamic.
|
|
31
|
+
|
|
32
|
+
Apps using `compat.next` can also import `cookies()` and `headers()` from `next/headers`.
|
|
33
|
+
|
|
34
|
+
## Document
|
|
35
|
+
|
|
36
|
+
Use the root `layout.tsx` to own `<html>` and `<body>`:
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import type { LayoutProps } from '@wular/pnext';
|
|
40
|
+
|
|
41
|
+
export default function Layout({ children }: LayoutProps) {
|
|
42
|
+
return (
|
|
43
|
+
<html lang="en">
|
|
44
|
+
<body>{children}</body>
|
|
45
|
+
</html>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
If the root layout exports a default component, it must return `<html>` and `<body>`.
|
|
51
|
+
If the root layout only exports metadata, pnext creates the document shell.
|
|
52
|
+
|
|
53
|
+
### Metadata
|
|
54
|
+
|
|
55
|
+
Pages and layouts can export core build-time `metadata`, `metadata()`, `viewport`, and `viewport()`. See [Metadata](./metadata.md) for the full core metadata model.
|
|
56
|
+
|
|
57
|
+
## Request Cache
|
|
58
|
+
|
|
59
|
+
Use server-only `cache()` to dedupe repeated server work during one render or route-handler request:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { cache } from '@wular/pnext/cache';
|
|
63
|
+
|
|
64
|
+
export const getPost = cache(async (id: string) => {
|
|
65
|
+
return db.post.findUnique({ where: { id } });
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Calls to the same cached function with the same arguments share the result inside the current request. The cache is cleared between requests, so it does not share data across users, is not a persistent data cache, and should not be imported from Client Components.
|
|
70
|
+
|
|
71
|
+
## Client Components
|
|
72
|
+
|
|
73
|
+
Add `"use client"` to components that need browser APIs, hooks, or event handlers.
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
// app/counter.tsx
|
|
77
|
+
'use client';
|
|
78
|
+
|
|
79
|
+
import { useState } from 'preact/hooks';
|
|
80
|
+
|
|
81
|
+
export function Counter({ initial }: { initial: number }) {
|
|
82
|
+
const [count, setCount] = useState(initial);
|
|
83
|
+
return <button onClick={() => setCount(count + 1)}>Count {count}</button>;
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
// app/page.tsx
|
|
89
|
+
import { Counter } from './counter';
|
|
90
|
+
|
|
91
|
+
export default function Page() {
|
|
92
|
+
return <Counter initial={0} />;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Files imported by a client component are client code automatically. Add `"use client"` to an imported file only when it is also rendered directly from a server component.
|
|
97
|
+
|
|
98
|
+
Props passed from server components to client components must be serializable. pnext accepts strings, numbers, booleans, `null`, `undefined`, arrays, plain objects, `Map`, `Set`, typed arrays, and `ArrayBuffer`. Self-referencing values are fine — they are written as back-references. It rejects functions, symbols, `bigint`, and class instances, including `Date` and `URL`; pass those as strings.
|
|
99
|
+
|
|
100
|
+
Server Components can render Client Components. Client Components render HTML on the server, then hydrate in the browser. Client Components can wrap server-rendered children; those children stay server-rendered and only the wrapper hydrates in the browser.
|
|
101
|
+
|
|
102
|
+
Use `dynamic({ ssr: false })` for browser-only Client Components. `dynamic({ load: 'visible' })` is browser-only by default, so it can keep Preact and the component chunk out of the initial route bundle until the island enters view.
|
|
103
|
+
|
|
104
|
+
If the whole page needs browser-only behavior, put `"use client"` at the top of the page. Prefer smaller client components when only part of the page is interactive.
|
|
105
|
+
|
|
106
|
+
## Streaming
|
|
107
|
+
|
|
108
|
+
Wrap slower server content in `<Suspense>` to show fallback HTML while the server finishes rendering the rest. The pnext renderer resolves these boundaries itself, so core apps use pnext's own `Suspense` — no `preact/compat` involved. Apps with `compat.react` can keep importing React's `Suspense`; both identities are recognized.
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import { Suspense } from '@wular/pnext';
|
|
112
|
+
|
|
113
|
+
async function Comments() {
|
|
114
|
+
const comments = await getComments();
|
|
115
|
+
return <CommentList comments={comments} />;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export default function Page() {
|
|
119
|
+
return (
|
|
120
|
+
<Suspense fallback={<p>Loading comments...</p>}>
|
|
121
|
+
<Comments />
|
|
122
|
+
</Suspense>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Dev and server responses can stream the fallback first, then replace it when the server content is ready. Static builds wait for the final content.
|