@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.
Files changed (371) hide show
  1. package/README.md +153 -0
  2. package/bin/pnext +67 -0
  3. package/config/lint/base.js +48 -0
  4. package/config/ts/base.json +26 -0
  5. package/config/ts/react.json +13 -0
  6. package/package.json +70 -0
  7. package/reference/compat.md +63 -0
  8. package/reference/config.md +120 -0
  9. package/reference/css.md +58 -0
  10. package/reference/dev.md +69 -0
  11. package/reference/env.md +40 -0
  12. package/reference/metadata.md +86 -0
  13. package/reference/navigation.md +149 -0
  14. package/reference/overview.md +35 -0
  15. package/reference/performance.md +97 -0
  16. package/reference/rendering.md +127 -0
  17. package/reference/routing.md +167 -0
  18. package/reference/typegen.md +64 -0
  19. package/src/api/cache.ts +80 -0
  20. package/src/api/client-cache.ts +9 -0
  21. package/src/api/client-navigation.ts +279 -0
  22. package/src/api/dynamic.tsx +102 -0
  23. package/src/api/link.tsx +119 -0
  24. package/src/api/navigation.ts +198 -0
  25. package/src/api/router/events.ts +53 -0
  26. package/src/api/router/history.ts +70 -0
  27. package/src/api/router/hub.ts +194 -0
  28. package/src/api/router/policies.ts +107 -0
  29. package/src/api/router/runtime.ts +5238 -0
  30. package/src/api/router/types.ts +299 -0
  31. package/src/api/router.ts +167 -0
  32. package/src/api/server.ts +323 -0
  33. package/src/api/suspense.ts +16 -0
  34. package/src/cache/context.ts +61 -0
  35. package/src/cli/adapters/vercel-warm.ts +375 -0
  36. package/src/cli/adapters/vercel.ts +1310 -0
  37. package/src/cli/analyze-print.ts +181 -0
  38. package/src/cli/analyze.ts +328 -0
  39. package/src/cli/boot-trace.ts +29 -0
  40. package/src/cli/build.ts +3114 -0
  41. package/src/cli/dev.ts +276 -0
  42. package/src/cli/index.ts +196 -0
  43. package/src/cli/named-bin.ts +119 -0
  44. package/src/cli/serve-ui.ts +160 -0
  45. package/src/cli/start.ts +1425 -0
  46. package/src/client/build.ts +2136 -0
  47. package/src/client/chunk-fold.ts +526 -0
  48. package/src/client/entry.ts +1525 -0
  49. package/src/client/paths.ts +22 -0
  50. package/src/client/prebuilt.ts +621 -0
  51. package/src/client/profile.ts +75 -0
  52. package/src/client/react-compiler.ts +94 -0
  53. package/src/client/reference-stub.ts +145 -0
  54. package/src/client/reference.ts +47 -0
  55. package/src/compat/actions/action-client.ts +531 -0
  56. package/src/compat/actions/action-dispatch.ts +676 -0
  57. package/src/compat/actions/action-router.ts +40 -0
  58. package/src/compat/actions/action-shared.ts +95 -0
  59. package/src/compat/actions/client-plugin.ts +135 -0
  60. package/src/compat/actions/client-stub.ts +65 -0
  61. package/src/compat/actions/config.ts +164 -0
  62. package/src/compat/actions/detect.ts +208 -0
  63. package/src/compat/actions/discovery.ts +244 -0
  64. package/src/compat/actions/early-submit.ts +40 -0
  65. package/src/compat/actions/endpoint.ts +602 -0
  66. package/src/compat/actions/flight.ts +52 -0
  67. package/src/compat/actions/form-state.ts +73 -0
  68. package/src/compat/actions/hoist.ts +485 -0
  69. package/src/compat/actions/ids.ts +39 -0
  70. package/src/compat/actions/index.ts +41 -0
  71. package/src/compat/actions/instances.ts +144 -0
  72. package/src/compat/actions/origin.ts +109 -0
  73. package/src/compat/actions/protocol.ts +125 -0
  74. package/src/compat/actions/registry.ts +74 -0
  75. package/src/compat/actions/rewrite.ts +282 -0
  76. package/src/compat/actions/serve.ts +414 -0
  77. package/src/compat/actions/server-tag.ts +21 -0
  78. package/src/compat/actions/unrecognized-error.ts +30 -0
  79. package/src/compat/adapter/build-complete.ts +257 -0
  80. package/src/compat/bundler/bun-externals.ts +53 -0
  81. package/src/compat/bundler/cjs-exports.ts +542 -0
  82. package/src/compat/bundler/config.ts +363 -0
  83. package/src/compat/bundler/externals.ts +34 -0
  84. package/src/compat/bundler/import-meta-url.ts +60 -0
  85. package/src/compat/bundler/modularize-imports.ts +119 -0
  86. package/src/compat/bundler/new-url-asset.ts +87 -0
  87. package/src/compat/bundler/optimize-package-imports.ts +273 -0
  88. package/src/compat/bundler/polyfill.ts +88 -0
  89. package/src/compat/bundler/react-compiler.ts +61 -0
  90. package/src/compat/bundler/react-profiler.tsx +25 -0
  91. package/src/compat/bundler/relay-transform.ts +116 -0
  92. package/src/compat/bundler/require-context.ts +281 -0
  93. package/src/compat/bundler/resolve-extensions.ts +75 -0
  94. package/src/compat/bundler/source-cache.ts +61 -0
  95. package/src/compat/bundler/static-imports.ts +25 -0
  96. package/src/compat/bundler/symlink-imports.ts +119 -0
  97. package/src/compat/bundler/tsconfig-paths.ts +50 -0
  98. package/src/compat/bundler/wasm.ts +153 -0
  99. package/src/compat/bundler/webpack-loaders.ts +685 -0
  100. package/src/compat/bundler/worker.ts +278 -0
  101. package/src/compat/cache/build-flags.ts +81 -0
  102. package/src/compat/cache/build-prerender-errors.ts +163 -0
  103. package/src/compat/cache/custom-handler.ts +159 -0
  104. package/src/compat/cache/fetch-patch.ts +745 -0
  105. package/src/compat/cache/handler.ts +100 -0
  106. package/src/compat/cache/modern-handler.ts +275 -0
  107. package/src/compat/cache/resume-data-cache.ts +143 -0
  108. package/src/compat/cache/revalidate.ts +759 -0
  109. package/src/compat/cache/runtime-error.ts +124 -0
  110. package/src/compat/cache/use-cache-transform.ts +961 -0
  111. package/src/compat/cache/use-cache.ts +1695 -0
  112. package/src/compat/cache-control.ts +269 -0
  113. package/src/compat/client/base-path.ts +64 -0
  114. package/src/compat/client/css-order.ts +36 -0
  115. package/src/compat/client/errors/control-flow.ts +92 -0
  116. package/src/compat/client/errors/error-boundary.ts +222 -0
  117. package/src/compat/client/errors/global-error.ts +238 -0
  118. package/src/compat/client/errors/install.ts +217 -0
  119. package/src/compat/client/errors/lazy.ts +53 -0
  120. package/src/compat/client/errors/primitive-throw.ts +126 -0
  121. package/src/compat/client/errors/soft-refresh.ts +14 -0
  122. package/src/compat/client/link-status.ts +86 -0
  123. package/src/compat/client/nav-compat-runtime.ts +57 -0
  124. package/src/compat/client/nav-compat.ts +42 -0
  125. package/src/compat/client/navigation-scroll.ts +154 -0
  126. package/src/compat/client/optimistic-routing.ts +206 -0
  127. package/src/compat/client/prefetch-cache.ts +111 -0
  128. package/src/compat/client/route-announcer.ts +72 -0
  129. package/src/compat/client/segment-cache-policy.ts +159 -0
  130. package/src/compat/client/segment-cache.ts +1077 -0
  131. package/src/compat/client/segment-prefetch.ts +375 -0
  132. package/src/compat/client/trailing-slash.ts +24 -0
  133. package/src/compat/css/chunking.ts +254 -0
  134. package/src/compat/css/inline-css.ts +73 -0
  135. package/src/compat/css/lightningcss.ts +90 -0
  136. package/src/compat/css/modules.ts +373 -0
  137. package/src/compat/css/nonce.ts +30 -0
  138. package/src/compat/css/sass-plugin.ts +65 -0
  139. package/src/compat/css/sass.ts +392 -0
  140. package/src/compat/css/styled-jsx-runtime.ts +80 -0
  141. package/src/compat/css/styled-jsx.ts +49 -0
  142. package/src/compat/edge-runtime.ts +71 -0
  143. package/src/compat/export/client.ts +112 -0
  144. package/src/compat/export/index.ts +272 -0
  145. package/src/compat/export/standalone.ts +207 -0
  146. package/src/compat/image-optimizer/cache.ts +119 -0
  147. package/src/compat/image-optimizer/detect.ts +143 -0
  148. package/src/compat/image-optimizer/index.ts +601 -0
  149. package/src/compat/image-optimizer/source.ts +243 -0
  150. package/src/compat/index.ts +458 -0
  151. package/src/compat/lifecycle/after-scope.ts +86 -0
  152. package/src/compat/lifecycle/after.ts +173 -0
  153. package/src/compat/lifecycle/error-funnel.ts +306 -0
  154. package/src/compat/lifecycle/error-serialize.ts +87 -0
  155. package/src/compat/lifecycle/error-ui.ts +167 -0
  156. package/src/compat/lifecycle/instrumentation-client.ts +138 -0
  157. package/src/compat/lifecycle/instrumentation.ts +277 -0
  158. package/src/compat/lifecycle/node-console.ts +19 -0
  159. package/src/compat/lifecycle/testmode.ts +263 -0
  160. package/src/compat/mdx/compile.ts +219 -0
  161. package/src/compat/mdx/next-mdx-stub.ts +46 -0
  162. package/src/compat/mdx/plugin.ts +37 -0
  163. package/src/compat/metadata-route-artifacts.ts +458 -0
  164. package/src/compat/metadata.ts +295 -0
  165. package/src/compat/middleware/manifest.ts +210 -0
  166. package/src/compat/misc/action-return.ts +173 -0
  167. package/src/compat/next/cache.ts +211 -0
  168. package/src/compat/next/canonical-url.ts +35 -0
  169. package/src/compat/next/client-cache.ts +57 -0
  170. package/src/compat/next/client-navigation.ts +313 -0
  171. package/src/compat/next/client-only.ts +3 -0
  172. package/src/compat/next/client-script.tsx +215 -0
  173. package/src/compat/next/client-server.ts +39 -0
  174. package/src/compat/next/config-loader.ts +569 -0
  175. package/src/compat/next/config.ts +29 -0
  176. package/src/compat/next/constants.cjs +6 -0
  177. package/src/compat/next/constants.ts +6 -0
  178. package/src/compat/next/custom-server.ts +236 -0
  179. package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
  180. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
  181. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
  182. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
  183. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
  184. package/src/compat/next/dynamic.tsx +46 -0
  185. package/src/compat/next/error.tsx +148 -0
  186. package/src/compat/next/font/cache.ts +171 -0
  187. package/src/compat/next/font/google.ts +2 -0
  188. package/src/compat/next/font/index.ts +8 -0
  189. package/src/compat/next/font/local.ts +5 -0
  190. package/src/compat/next/font/runtime-client.ts +71 -0
  191. package/src/compat/next/font/runtime.ts +974 -0
  192. package/src/compat/next/font/shared.ts +281 -0
  193. package/src/compat/next/form.tsx +156 -0
  194. package/src/compat/next/head.tsx +10 -0
  195. package/src/compat/next/headers.ts +247 -0
  196. package/src/compat/next/image/config.ts +196 -0
  197. package/src/compat/next/image/optimizer.ts +96 -0
  198. package/src/compat/next/image/patterns.ts +103 -0
  199. package/src/compat/next/image/shared.ts +141 -0
  200. package/src/compat/next/image/static-metadata.ts +283 -0
  201. package/src/compat/next/image/validate.ts +269 -0
  202. package/src/compat/next/image-client.tsx +215 -0
  203. package/src/compat/next/image-props.ts +575 -0
  204. package/src/compat/next/image-usage.ts +102 -0
  205. package/src/compat/next/image.tsx +56 -0
  206. package/src/compat/next/index.ts +1 -0
  207. package/src/compat/next/legacy-image.tsx +97 -0
  208. package/src/compat/next/link-usage.ts +29 -0
  209. package/src/compat/next/link-validation-transform.ts +200 -0
  210. package/src/compat/next/link.tsx +466 -0
  211. package/src/compat/next/navigation.cjs +21 -0
  212. package/src/compat/next/navigation.ts +188 -0
  213. package/src/compat/next/offline.ts +51 -0
  214. package/src/compat/next/og.ts +324 -0
  215. package/src/compat/next/optimistic-route-state.ts +188 -0
  216. package/src/compat/next/preferred-region.ts +39 -0
  217. package/src/compat/next/redirects.ts +131 -0
  218. package/src/compat/next/resource-hints.ts +136 -0
  219. package/src/compat/next/rewrites.ts +350 -0
  220. package/src/compat/next/root-params.ts +142 -0
  221. package/src/compat/next/router.cjs +49 -0
  222. package/src/compat/next/router.ts +143 -0
  223. package/src/compat/next/script.tsx +355 -0
  224. package/src/compat/next/server-only.ts +3 -0
  225. package/src/compat/next/server.ts +28 -0
  226. package/src/compat/next/svgr.ts +58 -0
  227. package/src/compat/next/telemetry.ts +77 -0
  228. package/src/compat/next/user-agent.ts +100 -0
  229. package/src/compat/next/web-vitals.ts +56 -0
  230. package/src/compat/otel/api.ts +95 -0
  231. package/src/compat/otel/client-trace-metadata.ts +71 -0
  232. package/src/compat/otel/fetch-span.ts +77 -0
  233. package/src/compat/otel/tracer.ts +944 -0
  234. package/src/compat/pages/client-plugin.ts +108 -0
  235. package/src/compat/pages/index.ts +527 -0
  236. package/src/compat/pages/router-state.ts +94 -0
  237. package/src/compat/ppr/io.ts +38 -0
  238. package/src/compat/ppr/missing-root-params.ts +105 -0
  239. package/src/compat/ppr/root-params-scan.ts +164 -0
  240. package/src/compat/ppr/root-params-transform.ts +75 -0
  241. package/src/compat/ppr/root-params.ts +129 -0
  242. package/src/compat/ppr/segment-config-incompat.ts +34 -0
  243. package/src/compat/protocol.ts +202 -0
  244. package/src/compat/react/client.ts +59 -0
  245. package/src/compat/react/compiler-runtime.ts +60 -0
  246. package/src/compat/react/dom-client.ts +115 -0
  247. package/src/compat/react/dom-react-server.ts +20 -0
  248. package/src/compat/react/dom-server.ts +40 -0
  249. package/src/compat/react/dom.ts +154 -0
  250. package/src/compat/react/preact.ts +522 -0
  251. package/src/compat/react/react-server.ts +84 -0
  252. package/src/compat/react/router-shim.ts +26 -0
  253. package/src/compat/react/server-component-use.ts +48 -0
  254. package/src/compat/react/server-inserted-html.ts +87 -0
  255. package/src/compat/react/server.ts +156 -0
  256. package/src/compat/react/view-transition.ts +60 -0
  257. package/src/compat/register/actions.ts +875 -0
  258. package/src/compat/register/boot.ts +141 -0
  259. package/src/compat/register/build-tier.ts +11 -0
  260. package/src/compat/register/build.ts +182 -0
  261. package/src/compat/register/bundler.ts +587 -0
  262. package/src/compat/register/cache.ts +85 -0
  263. package/src/compat/register/client-errors.ts +18 -0
  264. package/src/compat/register/config.ts +17 -0
  265. package/src/compat/register/css-extras.ts +120 -0
  266. package/src/compat/register/edge-runtime.ts +6 -0
  267. package/src/compat/register/errors.ts +46 -0
  268. package/src/compat/register/export.ts +23 -0
  269. package/src/compat/register/font.ts +36 -0
  270. package/src/compat/register/hooks.ts +34 -0
  271. package/src/compat/register/image.ts +133 -0
  272. package/src/compat/register/index.ts +111 -0
  273. package/src/compat/register/instrumentation-client.ts +35 -0
  274. package/src/compat/register/lifecycle.ts +86 -0
  275. package/src/compat/register/mdx.ts +48 -0
  276. package/src/compat/register/middleware.ts +36 -0
  277. package/src/compat/register/misc.ts +44 -0
  278. package/src/compat/register/otel.ts +288 -0
  279. package/src/compat/register/pages-api.ts +473 -0
  280. package/src/compat/register/ppr.ts +56 -0
  281. package/src/compat/register/protocol.ts +57 -0
  282. package/src/compat/register/proxy.ts +127 -0
  283. package/src/compat/register/render.ts +268 -0
  284. package/src/compat/register/routing.ts +410 -0
  285. package/src/compat/register/segment.ts +1903 -0
  286. package/src/compat/register/static-image.ts +21 -0
  287. package/src/compat/register/typed-routes.ts +35 -0
  288. package/src/compat/register/usecache.ts +131 -0
  289. package/src/compat/register/validation.ts +56 -0
  290. package/src/compat/segment/loading-boundary.ts +113 -0
  291. package/src/compat/segment/page-slot.ts +200 -0
  292. package/src/compat/segment/tree.ts +481 -0
  293. package/src/compat/segment/vary-key.ts +102 -0
  294. package/src/compat/segment/vary-params.ts +551 -0
  295. package/src/compat/static-params.ts +33 -0
  296. package/src/compat/tsconfig-defaults.ts +301 -0
  297. package/src/compat/typecheck/index.ts +1481 -0
  298. package/src/compat/typecheck/worker.ts +26 -0
  299. package/src/compat/typed-routes/index.ts +92 -0
  300. package/src/compat/typed-routes/manifest.ts +356 -0
  301. package/src/compat/typed-routes/typegen.ts +566 -0
  302. package/src/compat/validation/errors.ts +159 -0
  303. package/src/compat/validation/index.ts +1770 -0
  304. package/src/compat/validation/prerender-diagnostics.ts +1508 -0
  305. package/src/compat-bootstrap.ts +67 -0
  306. package/src/config.ts +218 -0
  307. package/src/css/build.ts +697 -0
  308. package/src/css/index.ts +2 -0
  309. package/src/css/postcss.ts +236 -0
  310. package/src/css/worker.ts +34 -0
  311. package/src/dev/client-actions.ts +35 -0
  312. package/src/dev/client-chunk-store.ts +92 -0
  313. package/src/dev/client-key-cache.ts +178 -0
  314. package/src/dev/global-css-cache.ts +212 -0
  315. package/src/dev/imports.ts +2430 -0
  316. package/src/dev/module-cache.ts +721 -0
  317. package/src/dev/module-generations.ts +38 -0
  318. package/src/dev/module-transform.ts +188 -0
  319. package/src/dev/node-module-bundle-cache.ts +63 -0
  320. package/src/dev/restart-cache.ts +10 -0
  321. package/src/dev/route-bundle-key-cache.ts +154 -0
  322. package/src/dev/route-facts-cache.ts +223 -0
  323. package/src/dev/server.ts +1710 -0
  324. package/src/dynamic/source.ts +307 -0
  325. package/src/dynamic/tree-shake.ts +262 -0
  326. package/src/env.ts +92 -0
  327. package/src/extensions.ts +1898 -0
  328. package/src/index.ts +34 -0
  329. package/src/internal.ts +43 -0
  330. package/src/islands/boundary-error.ts +8 -0
  331. package/src/islands/static-children.ts +37 -0
  332. package/src/islands/static-slots.ts +106 -0
  333. package/src/ppr-postpone.ts +24 -0
  334. package/src/ppr.ts +784 -0
  335. package/src/proxy.ts +752 -0
  336. package/src/render/hooks.ts +384 -0
  337. package/src/render/index.ts +1 -0
  338. package/src/render/island-context.ts +47 -0
  339. package/src/render/metadata.ts +857 -0
  340. package/src/render/renderer.ts +7391 -0
  341. package/src/render/resource-hints.ts +44 -0
  342. package/src/render/slots.tsx +679 -0
  343. package/src/request/context.ts +396 -0
  344. package/src/resolve/engine.ts +219 -0
  345. package/src/resolve/imports.ts +1104 -0
  346. package/src/resolve/scan-facts.ts +474 -0
  347. package/src/resolve/source-text.ts +86 -0
  348. package/src/routing/forwarded.ts +41 -0
  349. package/src/routing/handler.ts +271 -0
  350. package/src/routing/href.ts +203 -0
  351. package/src/routing/metadata.ts +1018 -0
  352. package/src/routing/request-runtime.ts +43 -0
  353. package/src/routing/routes.ts +2560 -0
  354. package/src/routing/slots.ts +432 -0
  355. package/src/runtime/server.ts +3453 -0
  356. package/src/runtime/vendor.ts +1160 -0
  357. package/src/style-modules.d.ts +9 -0
  358. package/src/typegen.ts +151 -0
  359. package/src/types.ts +725 -0
  360. package/src/utils/ansi.ts +9 -0
  361. package/src/utils/content-type.ts +31 -0
  362. package/src/utils/decode.ts +7 -0
  363. package/src/utils/dev-profile.ts +31 -0
  364. package/src/utils/error-log.ts +29 -0
  365. package/src/utils/fs-cache.ts +31 -0
  366. package/src/utils/fs.ts +119 -0
  367. package/src/utils/html.ts +46 -0
  368. package/src/utils/serialize.ts +378 -0
  369. package/src/utils/source.ts +35 -0
  370. package/src/utils/verbose.ts +39 -0
  371. package/tsconfig.json +10 -0
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ <div align="center">
2
+
3
+ # pnext
4
+
5
+ **A fast little framework for server-first React apps, fully compatible with Next.js.**
6
+
7
+ </div>
8
+
9
+ ## Measured against Next.js
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
37
+
38
+ ```sh
39
+ bun add -d @wular/pnext
40
+ ```
41
+
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
+ ```
53
+
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
71
+ ```
72
+
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
76
+
77
+ Pages and layouts are Server Components by default:
78
+
79
+ ```tsx
80
+ export const metadata = {
81
+ title: 'My app',
82
+ };
83
+
84
+ export default async function Page() {
85
+ const post = await getPost();
86
+ return <article>{post.title}</article>;
87
+ }
88
+ ```
89
+
90
+ Add a Client Component only where the browser matters:
91
+
92
+ ```tsx
93
+ 'use client';
94
+
95
+ import { useState } from 'preact/hooks';
96
+
97
+ export function Counter() {
98
+ const [count, setCount] = useState(0);
99
+ return <button onClick={() => setCount(count + 1)}>{count}</button>;
100
+ }
101
+ ```
102
+
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.
104
+
105
+ Dedupe repeated reads with server-only `cache()`:
106
+
107
+ ```tsx
108
+ import { cache } from '@wular/pnext/cache';
109
+
110
+ export const getPost = cache(async (id: string) => {
111
+ return db.post.findUnique({ where: { id } });
112
+ });
113
+ ```
114
+
115
+ ## Routes
116
+
117
+ `route.ts` files are HTTP handlers:
118
+
119
+ ```tsx
120
+ import type { NextRequest } from '@wular/pnext/server';
121
+
122
+ export function GET(request: NextRequest) {
123
+ const name = request.nextUrl.searchParams.get('name');
124
+ return Response.json({ hello: name ?? 'world' });
125
+ }
126
+ ```
127
+
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
131
+
132
+ `pnext dev`, `build`, and `typegen` generate route types in `.pnext/types`; import typed route props from `#gen/app/...`.
133
+
134
+ ## Benchmarks
135
+
136
+ ```sh
137
+ bun bench
138
+ ```
139
+
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.
141
+
142
+ ## Reference
143
+
144
+ - [Overview](./reference/overview.md)
145
+ - [Routing](./reference/routing.md)
146
+ - [Metadata](./reference/metadata.md)
147
+ - [Rendering](./reference/rendering.md)
148
+ - [CSS](./reference/css.md)
149
+ - [Environment Variables](./reference/env.md)
150
+ - [Compatibility](./reference/compat.md)
151
+ - [Typegen](./reference/typegen.md)
152
+ - [Config](./reference/config.md)
153
+ - [Performance](./reference/performance.md)
package/bin/pnext ADDED
@@ -0,0 +1,67 @@
1
+ #!/bin/sh
2
+
3
+ script=$0
4
+
5
+ # `dirname` is an external binary and `$(...)` forks a subshell, so spelling this
6
+ # the obvious way costs two process creations on every command. Parameter
7
+ # expansion plus a `cd` in *this* shell gets the same answer with no forks; the
8
+ # cwd is restored before exec so relative arguments still resolve.
9
+ resolve_dir() {
10
+ case $1 in
11
+ */*) dirpart=${1%/*} ;;
12
+ *) dirpart=. ;;
13
+ esac
14
+ opwd=$PWD
15
+ CDPATH= cd -P "$dirpart" 2>/dev/null || return 1
16
+ dir=$PWD
17
+ CDPATH= cd "$opwd" 2>/dev/null || return 1
18
+ }
19
+
20
+ while [ -L "$script" ]; do
21
+ resolve_dir "$script" || break
22
+ target=$(readlink "$script")
23
+ case "$target" in
24
+ /*) script=$target ;;
25
+ *) script=$dir/$target ;;
26
+ esac
27
+ done
28
+
29
+ resolve_dir "$script"
30
+
31
+ # Bun decides its JSX transform (jsx vs. the dev-only jsxDEV, which calls
32
+ # preact/jsx-dev-runtime with a different call signature) once per process,
33
+ # from NODE_ENV at startup — not per file. Setting it later from inside
34
+ # index.ts is too late: by the time that line runs, the entry file and
35
+ # everything reached by its static imports have already been transpiled
36
+ # with whatever NODE_ENV was inherited from the shell. So it must be
37
+ # exported here, before exec, not in TS.
38
+ if [ -z "$NODE_ENV" ]; then
39
+ case "$1" in
40
+ dev) export NODE_ENV=development ;;
41
+ *) export NODE_ENV=production ;;
42
+ esac
43
+ fi
44
+
45
+ # Exec through a bun binary literally named for the command ("pnext-dev",
46
+ # "pnext-build", ...) so Activity Monitor and `ps` label every pnext process
47
+ # instead of showing "bun". The kernel takes the process name from the exec'd
48
+ # file's basename; argv0 tricks like `exec -a` never reach it.
49
+ #
50
+ # The cached names are hardlinks of a bun binary (see src/cli/named-bin.ts), so
51
+ # `-ef` — same device and inode — proves a candidate is *this* bun and never
52
+ # execs a stale one left by an earlier version. Resolving it here in sh instead
53
+ # of asking named-bin.ts saves a whole bun process per command; on a miss we run
54
+ # plain bun and the CLI creates the link for the next run.
55
+ case "$1" in
56
+ dev|build|start|analyze|typegen) named=pnext-$1 ;;
57
+ *) named=pnext ;;
58
+ esac
59
+
60
+ bun_bin=$(command -v bun)
61
+ for candidate in "${XDG_CACHE_HOME:-$HOME/.cache}"/pnext/bin/bun-*/"$named"; do
62
+ if [ -x "$candidate" ] && [ "$candidate" -ef "$bun_bin" ]; then
63
+ exec "$candidate" --conditions=react-server "$dir/../src/cli/index.ts" "$@"
64
+ fi
65
+ done
66
+
67
+ exec bun --conditions=react-server "$dir/../src/cli/index.ts" "$@"
@@ -0,0 +1,48 @@
1
+ import * as path from 'node:path';
2
+ import { includeIgnoreFile } from '@eslint/compat';
3
+ // @ts-ignore
4
+ import eslint from '@eslint/js';
5
+ import importPlugin from 'eslint-plugin-import';
6
+ import turboPlugin from 'eslint-plugin-turbo';
7
+ import tseslint from 'typescript-eslint';
8
+
9
+ export default tseslint.config(
10
+ includeIgnoreFile(path.join(import.meta.dirname, '../../.gitignore')),
11
+ { ignores: ['**/*.config.*', '**/*.d.ts', '**/.pnext/**', '**/dist/**'] },
12
+ {
13
+ files: ['**/*.js', '**/*.ts', '**/*.tsx'],
14
+ plugins: {
15
+ import: importPlugin,
16
+ turbo: turboPlugin,
17
+ },
18
+ extends: [
19
+ eslint.configs.recommended,
20
+ ...tseslint.configs.recommended,
21
+ ...tseslint.configs.recommendedTypeChecked,
22
+ ...tseslint.configs.stylisticTypeChecked,
23
+ ],
24
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
25
+ rules: {
26
+ // @ts-ignore
27
+ ...turboPlugin.configs.recommended.rules,
28
+ '@typescript-eslint/no-unused-vars': [
29
+ 'error',
30
+ { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
31
+ ],
32
+ '@typescript-eslint/no-explicit-any': 'off',
33
+ '@typescript-eslint/consistent-type-imports': [
34
+ 'warn',
35
+ { prefer: 'type-imports', fixStyle: 'inline-type-imports', disallowTypeAnnotations: false },
36
+ ],
37
+ 'prefer-const': 'off',
38
+ '@typescript-eslint/ban-ts-comment': 'off',
39
+ '@typescript-eslint/no-misused-promises': 'off',
40
+ '@typescript-eslint/prefer-nullish-coalescing': 'off',
41
+ '@typescript-eslint/prefer-regexp-exec': 'off',
42
+ },
43
+ },
44
+ {
45
+ linterOptions: { reportUnusedDisableDirectives: true },
46
+ languageOptions: { parserOptions: { projectService: true } },
47
+ },
48
+ );
@@ -0,0 +1,26 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "Default",
4
+ "compilerOptions": {
5
+ "declaration": false,
6
+ "esModuleInterop": true,
7
+ "incremental": false,
8
+ "isolatedModules": true,
9
+ "lib": [
10
+ "ESNext"
11
+ ],
12
+ "module": "ESNext",
13
+ "moduleResolution": "Bundler",
14
+ "moduleDetection": "force",
15
+ "noUncheckedIndexedAccess": true,
16
+ "resolveJsonModule": true,
17
+ "verbatimModuleSyntax": true,
18
+ "skipLibCheck": true,
19
+ "strict": true,
20
+ "strictNullChecks": true,
21
+ "target": "ES2021",
22
+ "checkJs": true,
23
+ "allowJs": true,
24
+ "noEmit": true
25
+ }
26
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "React Library",
4
+ "extends": "./base.json",
5
+ "compilerOptions": {
6
+ "lib": [
7
+ "DOM",
8
+ "DOM.Iterable",
9
+ "ESNext"
10
+ ],
11
+ "jsx": "react-jsx"
12
+ }
13
+ }
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@wular/pnext",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "bin": {
6
+ "pnext": "bin/pnext"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "config",
11
+ "src",
12
+ "tsconfig.json",
13
+ "reference"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "exports": {
19
+ ".": "./src/index.ts",
20
+ "./internal": "./src/internal.ts",
21
+ "./cache": "./src/api/cache.ts",
22
+ "./dynamic": "./src/api/dynamic.tsx",
23
+ "./link": "./src/api/link.tsx",
24
+ "./navigation": "./src/api/navigation.ts",
25
+ "./navigation/client": "./src/api/client-navigation.ts",
26
+ "./server": "./src/api/server.ts"
27
+ },
28
+ "scripts": {
29
+ "lint": "bun scripts/check-compat-boundary.ts && eslint . --max-warnings 0 && tsc --noEmit",
30
+ "bench": "bun bench/run.ts",
31
+ "clean": "rm -rf .turbo .pnext node_modules",
32
+ "prepublishOnly": "bun run lint",
33
+ "version-bump:patch": "bun scripts/bump.mjs patch",
34
+ "version-bump:minor": "bun scripts/bump.mjs minor",
35
+ "version-bump:major": "bun scripts/bump.mjs major"
36
+ },
37
+ "dependencies": {
38
+ "client-only": "^0.0.1",
39
+ "dotenv": "^16.0.3",
40
+ "esbuild": "^0.25.12",
41
+ "oxc-parser": "0.142.0",
42
+ "oxc-resolver": "^11.24.2",
43
+ "oxc-transform": "0.136.0",
44
+ "preact": "^10.29.2",
45
+ "preact-render-to-string": "^6.7.0",
46
+ "react-compiler-runtime": "^1.0.0",
47
+ "server-only": "^0.0.1",
48
+ "web-vitals": "^4.2.4"
49
+ },
50
+ "devDependencies": {
51
+ "@eslint/compat": "^1.4.0",
52
+ "@eslint/js": "^9.38.0",
53
+ "@types/bun": "^1.3.0",
54
+ "eslint": "^9.38.0",
55
+ "eslint-plugin-import": "^2.32.0",
56
+ "eslint-plugin-turbo": "^2.5.8",
57
+ "typescript": "^5.9.3",
58
+ "typescript-eslint": "^8.46.2"
59
+ },
60
+ "optionalDependencies": {
61
+ "@mdx-js/mdx": "^3.1.0",
62
+ "@opentelemetry/api": "^1.7.0",
63
+ "@resvg/resvg-js": "^2.6.2",
64
+ "@typescript/native-preview": "^7.0.0-dev.20260707.2",
65
+ "next-font": "^2.0.2",
66
+ "sass": "^1.80.0",
67
+ "satori": "^0.12.0",
68
+ "sharp": "^0.34.0"
69
+ }
70
+ }
@@ -0,0 +1,63 @@
1
+ # Compatibility
2
+
3
+ pnext is Next.js-shaped. With `compat.next` on, App Router apps behave the way they do in Next: the `next/*` module surface, `next.config.js`, server actions, metadata, `proxy`/`middleware`, `'use cache'`, the `/_next/image` optimizer, and the `next/navigation` router. That is validated by running Next.js's own test suite against pnext rather than by hand-written parity claims.
4
+
5
+ ```ts
6
+ export default {
7
+ compat: {
8
+ react: true, // map react/react-dom onto Preact
9
+ next: true, // the next/* surface + next.config.js; implies react
10
+ reactCompiler: true, // React Compiler for React-style Client Components; implies react
11
+ },
12
+ };
13
+ ```
14
+
15
+ `compat.react` aliases `react`, `react-dom`, `react-dom/client`, `react-dom/server`, `react/jsx-runtime`, `react/jsx-dev-runtime`, and `react/compiler-runtime` onto Preact-backed shims. `react`'s `cache()` maps to pnext's request cache on the server.
16
+
17
+ Importing `next/*` or `react` without the matching compat mode is a build error naming the flag to enable.
18
+
19
+ Everything below is a place pnext deliberately does not match Next. It is the whole list.
20
+
21
+ ## Preact, not React
22
+
23
+ `react-dom/server`'s streaming renderers (`renderToReadableStream`, `renderToPipeableStream`) throw; the synchronous `renderToString`/`renderToStaticMarkup` work. Client components run on Preact's reconciler, so code reaching into React internals is out of scope.
24
+
25
+ Direct Preact imports and Preact Signals need no compat and produce smaller bundles than `preact/compat`.
26
+
27
+ ## No Flight payload
28
+
29
+ Soft navigation and server-action responses carry HTML, not a React Flight stream. Streaming, `refresh()`, and revalidation-driven updates all work; consuming the RSC payload as a wire format does not.
30
+
31
+ Server-rendered JSX crosses into a Client Component as `children` or through any other prop (including nested in arrays and plain objects) — both stay server-rendered static HTML and ship no code, like in Next. Elements inside `Map`/`Set` props are the one shape that throws.
32
+
33
+ ## Pages Router is emulated
34
+
35
+ A `pages/` directory is materialized onto App Router routes, so `getStaticProps`/`getServerSideProps` pages and `pages/api` handlers run. `_app`, `_document`, and `_error` are ignored. `next/head` renders nothing — use the metadata exports. `next/router` maps onto the app router where the concepts line up and no-ops elsewhere.
36
+
37
+ ## No webpack or Turbopack
38
+
39
+ esbuild is the only bundler, and a `webpack(config)` function in `next.config.js` is not executed (pnext warns once at config load when one is present). `turbopack.rules` loader chains, `turbopack.resolveAlias`, `transpilePackages`, `modularizeImports`, and `optimizePackageImports` are re-implemented on esbuild directly.
40
+
41
+ If `webpack()` references `@svgr/webpack` — the overwhelmingly common custom-loader use — pnext auto-detects it and compiles `.svg` imports to inline Preact components (root SVG attributes spread first, so `className`/`width`/`height`/etc. props override them), matching `@svgr/webpack`'s default output. Without that reference, `.svg` imports keep the normal static-asset URL behavior.
42
+
43
+ ## Optional native dependencies
44
+
45
+ All three ship as `optionalDependencies`, so a normal install has them — same as Next. The graceful paths below only matter when the optional install fails.
46
+
47
+ - `next/image`'s `/_next/image` optimizer needs `sharp`. The component, `images` config validation, and static imports work without it.
48
+ - `next/og`'s `ImageResponse` needs `satori` and `@resvg/resvg-js`. Without them, or without a usable font, it answers with a valid placeholder PNG instead of failing the request.
49
+ - `next/font/google` resolves the catalog through `next-font`. If it cannot, the build fails rather than falling back to a hosted font.
50
+
51
+ ## Smaller surfaces
52
+
53
+ - `userAgent()` uses an in-house parser covering mainstream browsers, engines, CPUs, and devices — not the full ua-parser-js database.
54
+ - Only these `next/dist/*` paths are shimmed; any other deep import fails with an error naming this list.
55
+ - `next/dist/client/components/app-router-headers`
56
+ - `next/dist/server/web/spec-extension/unstable-cache`
57
+ - `next/dist/server/web/spec-extension/unstable-no-store`
58
+ - `next/dist/server/web/spec-extension/revalidate`
59
+ - `next/dist/server/app-render/work-unit-async-storage.external` (server only)
60
+
61
+ ## Cache state is per process
62
+
63
+ `revalidatePath`/`revalidateTag`, `unstable_cache`, and `'use cache'` entries live in the server process by default, so a multi-instance deployment revalidates one instance. Configure `cacheHandler` in `next.config.js` for a shared store.
@@ -0,0 +1,120 @@
1
+ # Config
2
+
3
+ pnext reads `pnext.config.ts` from the project root passed to the CLI. The file is optional.
4
+
5
+ ```ts
6
+ import type { pnextConfig } from '@wular/pnext';
7
+
8
+ export default {
9
+ outDir: '.pnext',
10
+ basePath: '',
11
+ productionBrowserSourceMaps: false,
12
+ compat: {
13
+ next: true,
14
+ },
15
+ } satisfies pnextConfig;
16
+ ```
17
+
18
+ With `compat.next`, `next.config.js` is loaded too, and it wins on the options both files can set (`basePath`, `assetPrefix`, `outDir`, `trailingSlash`, `skipTrailingSlashRedirect`, `productionBrowserSourceMaps`).
19
+
20
+ ## Fields
21
+
22
+ ### `outDir`
23
+
24
+ Default: `'.pnext'`
25
+
26
+ Build output directory for HTML, client assets, cache files, and `manifest.json`.
27
+
28
+ ### `basePath`
29
+
30
+ Default: `''`
31
+
32
+ Path prefix for apps served below the domain root.
33
+
34
+ ### `assetPrefix`
35
+
36
+ Default: `basePath`
37
+
38
+ URL prefix for emitted assets. Set it to serve `/assets/*` from a CDN.
39
+
40
+ ### `trailingSlash`
41
+
42
+ Default: `false`
43
+
44
+ Canonicalize URLs to a trailing slash, redirecting the unslashed form.
45
+
46
+ ### `skipTrailingSlashRedirect`
47
+
48
+ Default: `false`
49
+
50
+ Serve both the slashed and unslashed URL without a canonical redirect, and leave
51
+ `<Link>` hrefs exactly as authored.
52
+
53
+ ### `productionBrowserSourceMaps`
54
+
55
+ Default: `false`
56
+
57
+ Emit external `.js.map` browser sourcemaps next to each production client chunk,
58
+ linked by a `//# sourceMappingURL=` comment. Off by default (as in Next): maps
59
+ publish your first-party source to every visitor and cost real build time.
60
+ Development never emits them — dev client output is un-minified already.
61
+
62
+ ### `htmlLimitedBots`
63
+
64
+ Default: a regex covering Twitterbot, Slackbot, Bingbot, Discordbot, LinkedInBot,
65
+ and the Google/Lighthouse renderers.
66
+
67
+ User agents matched by this pattern get metadata blocked in `<head>` rather than
68
+ streamed into the body. `compat.next` only.
69
+
70
+ ### `adapter`
71
+
72
+ Default: unset
73
+
74
+ `exclude` and `keep` string lists overriding what a deployment adapter packs into
75
+ the server function. Entries are directory names (`storybook-static`) or file
76
+ suffixes (`.map`).
77
+
78
+ ### `workspaceRoot`
79
+
80
+ Default: inferred from `package.json#workspaces` or `pnpm-workspace.yaml`, otherwise the app root.
81
+
82
+ Monorepo root used to resolve and reload local workspace package imports. This does not change where pnext reads `public/`, writes `.pnext/`, or loads `pnext.config.ts`.
83
+
84
+ ### `compat.react`
85
+
86
+ Default: `false`
87
+
88
+ When `true`, pnext aliases `react`, `react-dom`, `react-dom/client`, `react-dom/server`, `react/jsx-runtime`, `react/jsx-dev-runtime`, and `react/compiler-runtime` onto Preact-backed shims.
89
+
90
+ Use this when moving React-style components into a pnext app. Direct Preact imports are still smaller; `preact/compat` adds compatibility code only when your app imports React APIs.
91
+
92
+ ### `compat.next`
93
+
94
+ Default: `false`
95
+
96
+ Enables the `next/*` module surface, `next.config.js`, and the rest of the Next App Router behaviors for existing apps and shared packages. This implies `compat.react`.
97
+
98
+ See [Compatibility](./compat.md).
99
+
100
+ ### `compat.reactCompiler`
101
+
102
+ Default: `false`
103
+
104
+ Experimental React Compiler support for React-style Client Components. This implies `compat.react`.
105
+
106
+ Use this for components written with React hooks and props-heavy render paths where compiler memoization can reduce rerenders. Direct Preact code still gives the smallest bundles, and React Compiler compat is not recommended for components built around Preact Signals.
107
+
108
+ ## File Conventions
109
+
110
+ Routes are loaded from `app/` or `src/app/`. If both exist, `app/` wins. Under
111
+ `compat.next`, a `pages/` directory is materialized onto App Router routes; a
112
+ hybrid app keeps its native `app/` routes.
113
+
114
+ Static assets are loaded from `public/` at the project root.
115
+
116
+ ## Workspace Root
117
+
118
+ pnext keeps the CLI root as the app root for `public/`, `.pnext/`, and `pnext.config.ts`.
119
+
120
+ For monorepos, pnext also infers a workspace root by walking upward to `package.json#workspaces` or `pnpm-workspace.yaml`. Set `workspaceRoot` only when that inference should be overridden.
@@ -0,0 +1,58 @@
1
+ # CSS
2
+
3
+ ## Global CSS
4
+
5
+ Import global styles from the root layout. pnext compiles global CSS once and links it from every page as `/assets/global.css`.
6
+
7
+ Global CSS can use local workspace package imports, font files, image assets, and the app's PostCSS/Tailwind setup.
8
+
9
+ ## PostCSS
10
+
11
+ When the app root has a `postcss.config.{cjs,js,mjs}`, pnext runs its plugins on every emitted stylesheet, in a worker thread. Tailwind v4 (`@tailwindcss/postcss`) stays warm there, so dev rebuilds are incremental; config-file edits need a dev-server restart.
12
+
13
+ ```tsx
14
+ import type { ComponentChildren } from 'preact';
15
+ import './globals.css';
16
+
17
+ export default function Layout({ children }: { children: ComponentChildren }) {
18
+ return (
19
+ <html>
20
+ <body>{children}</body>
21
+ </html>
22
+ );
23
+ }
24
+ ```
25
+
26
+ ## Route CSS
27
+
28
+ Import plain CSS from pages, layouts, or components:
29
+
30
+ ```tsx
31
+ import './page.css';
32
+
33
+ export default function Page() {
34
+ return <h1 className="title">Hello</h1>;
35
+ }
36
+ ```
37
+
38
+ pnext emits route CSS only for routes that import CSS. A route with CSS imports gets a linked `/assets/<route>.css` chunk.
39
+
40
+ ## CSS Modules
41
+
42
+ Use `.module.css` for scoped classes:
43
+
44
+ ```css
45
+ .title {
46
+ color: rebeccapurple;
47
+ }
48
+ ```
49
+
50
+ ```tsx
51
+ import styles from './page.module.css';
52
+
53
+ export default function Page() {
54
+ return <h1 className={styles.title}>Hello</h1>;
55
+ }
56
+ ```
57
+
58
+ CSS module class names are scoped in the emitted CSS and match the server-rendered HTML.