@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
@@ -0,0 +1,181 @@
1
+ import { bold, cyan, dim } from '../utils/ansi';
2
+ import type { analyzeProject } from './analyze';
3
+
4
+ type AnalyzeResult = Awaited<ReturnType<typeof analyzeProject>>;
5
+ type Compression = AnalyzeResult['compression'];
6
+
7
+ export function printAnalyzeResult(
8
+ result: AnalyzeResult,
9
+ options: { route?: string; files: boolean },
10
+ ) {
11
+ console.log(
12
+ `PNext analyze: ${result.mode === 'production' ? 'production build' : 'dev server cache'} at ${dim(result.root)} (${result.compression})`,
13
+ );
14
+ printRouteBundles(result);
15
+ // With a route filter, the remaining files are mostly other routes' bundles —
16
+ // listing them as "other" would misread as unowned weight.
17
+ if (!options.route) printAnalyzeFiles(result, { details: options.files });
18
+ }
19
+
20
+ function printRouteBundles(result: AnalyzeResult) {
21
+ if (result.routeBundles.length === 0) return;
22
+
23
+ console.log(`\n${bold('Routes')}`);
24
+ for (const bundle of result.routeBundles) {
25
+ console.log(`\n${cyan(bundle.route)} ${dim(`(${bundle.page} page)`)}`);
26
+ printBundleGroup('initial', bundle.initial, result.compression);
27
+ printDynamicBundleGroup('visible dynamic', bundle.visibleDynamic, result.compression);
28
+ printDynamicBundleGroup('dynamic', bundle.dynamic, result.compression);
29
+ printBundleGroup('shared after dynamic', bundle.lazyShared, result.compression);
30
+ }
31
+ console.log('');
32
+ }
33
+
34
+ function printBundleGroup(
35
+ label: string,
36
+ files: AnalyzeResult['routeBundles'][number]['initial'],
37
+ compression: Compression,
38
+ ) {
39
+ if (files.length === 0) return;
40
+ console.log(`\n ${bold(label.padEnd(22))} ${formatTotalSize(files, compression)}`);
41
+ printBundleFiles(files, compression, ' ', rowColumns(files));
42
+ }
43
+
44
+ function printDynamicBundleGroup(
45
+ label: string,
46
+ files: AnalyzeResult['routeBundles'][number]['visibleDynamic'],
47
+ compression: Compression,
48
+ ) {
49
+ if (files.length === 0) return;
50
+ console.log(`\n ${bold(label)}`);
51
+ const columns = rowColumns(files);
52
+ const componentWidth = Math.max(...files.map(file => file.exportName.length));
53
+ const pathWidth = Math.max(...files.map(file => file.path.length));
54
+ for (const file of files) {
55
+ console.log(
56
+ ` ${file.exportName.padEnd(componentWidth)} ${dim(file.path.padEnd(pathWidth))} ${formatFileSize(file, compression, columns)}`,
57
+ );
58
+ }
59
+ }
60
+
61
+ function formatTotalSize(
62
+ files: { rawBytes: number; compressedBytes: number }[],
63
+ compression: Compression,
64
+ ) {
65
+ const rawBytes = files.reduce((total, file) => total + file.rawBytes, 0);
66
+ const compressedBytes = files.reduce((total, file) => total + file.compressedBytes, 0);
67
+ return `${formatBytes(compressedBytes)} ${compression} ${dim(`${formatBytes(rawBytes)} raw`)}`;
68
+ }
69
+
70
+ function printAnalyzeFiles(result: AnalyzeResult, options: { details: boolean }) {
71
+ const routeFiles = new Set(
72
+ result.routeBundles.flatMap(bundle => [
73
+ ...bundle.initial.map(file => file.path),
74
+ ...bundle.visibleDynamic.map(file => file.path),
75
+ ...bundle.dynamic.map(file => file.path),
76
+ ...bundle.lazyShared.map(file => file.path),
77
+ ]),
78
+ );
79
+ const sorted = sortedAnalyzeFiles(result.files).filter(file => !routeFiles.has(file.path));
80
+ if (sorted.length === 0) return;
81
+
82
+ if (!options.details) {
83
+ console.log(
84
+ `Other files: ${formatFileCount(sorted)} ${formatTotalSize(sorted, result.compression)}`,
85
+ );
86
+ console.log('Run pnext analyze --files to list them.');
87
+ return;
88
+ }
89
+
90
+ console.log(`${bold('Other files')}\n`);
91
+ for (const type of ['html', 'css', 'js', 'asset'] as const) {
92
+ const files = sorted.filter(file => file.type === type);
93
+ if (files.length === 0) continue;
94
+ console.log(bold(type));
95
+ printBundleFiles(files, result.compression, ' ', rowColumns(files));
96
+ console.log('');
97
+ }
98
+ }
99
+
100
+ function printBundleFiles(
101
+ files: { path: string; rawBytes: number; compressedBytes: number }[],
102
+ compression: Compression,
103
+ indent: string,
104
+ columns: RowColumns,
105
+ ) {
106
+ const pathWidth = Math.max(...files.map(file => file.path.length));
107
+ for (const file of files) {
108
+ console.log(
109
+ `${indent}${dim(file.path.padEnd(pathWidth))} ${formatFileSize(file, compression, columns)}`,
110
+ );
111
+ }
112
+ }
113
+
114
+ interface RowColumns {
115
+ compressedWidth: number;
116
+ rawWidth: number;
117
+ }
118
+
119
+ function rowColumns(files: { rawBytes: number; compressedBytes: number }[]): RowColumns {
120
+ return {
121
+ compressedWidth: Math.max(...files.map(file => formatBytes(file.compressedBytes).length)),
122
+ rawWidth: Math.max(...files.map(file => formatBytes(file.rawBytes).length)),
123
+ };
124
+ }
125
+
126
+ function formatFileSize(
127
+ file: { rawBytes: number; compressedBytes: number },
128
+ compression: Compression,
129
+ columns: RowColumns,
130
+ ) {
131
+ const compressed = formatBytes(file.compressedBytes).padStart(columns.compressedWidth);
132
+ const raw = `${formatBytes(file.rawBytes).padStart(columns.rawWidth)} raw`;
133
+ return `${compressed} ${compression} ${dim(raw)}`;
134
+ }
135
+
136
+ function formatFileCount(files: AnalyzeResult['files']) {
137
+ const counts = new Map<string, number>();
138
+ for (const file of files) counts.set(file.type, (counts.get(file.type) ?? 0) + 1);
139
+ return [...counts].map(([type, count]) => `${count} ${pluralFileType(type, count)}`).join(', ');
140
+ }
141
+
142
+ function pluralFileType(type: string, count: number) {
143
+ if (count === 1) return type;
144
+ if (type === 'js' || type === 'css' || type === 'html') return type;
145
+ return `${type}s`;
146
+ }
147
+
148
+ function sortedAnalyzeFiles(files: AnalyzeResult['files']) {
149
+ return [...files].sort((a, b) => {
150
+ const typeDelta = analyzeTypeRank(a.type) - analyzeTypeRank(b.type);
151
+ if (typeDelta) return typeDelta;
152
+ const sizeDelta = b.compressedBytes - a.compressedBytes;
153
+ if (sizeDelta) return sizeDelta;
154
+ return a.path.localeCompare(b.path);
155
+ });
156
+ }
157
+
158
+ function analyzeTypeRank(type: AnalyzeResult['files'][number]['type']) {
159
+ switch (type) {
160
+ case 'html':
161
+ return 0;
162
+ case 'css':
163
+ return 1;
164
+ case 'js':
165
+ return 2;
166
+ case 'asset':
167
+ return 3;
168
+ }
169
+ }
170
+
171
+ function formatBytes(bytes: number) {
172
+ if (bytes < 1024) return `${bytes} B`;
173
+ const units = ['KB', 'MB', 'GB', 'TB'] as const;
174
+ let value = bytes / 1024;
175
+ let unitIndex = 0;
176
+ while (value >= 1024 && unitIndex < units.length - 1) {
177
+ value /= 1024;
178
+ unitIndex += 1;
179
+ }
180
+ return `${value >= 10 ? value.toFixed(1) : value.toFixed(2)} ${units[unitIndex]}`;
181
+ }
@@ -0,0 +1,328 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import zlib from 'node:zlib';
5
+ import { loadConfig } from '../config';
6
+ import { matchRoute } from '../routing/routes';
7
+ import { listFiles } from '../utils/fs';
8
+ import type { BuildManifest, RouteManifestEntry } from '../types';
9
+
10
+ export interface AnalyzeResult {
11
+ mode: 'production' | 'development';
12
+ root: string;
13
+ compression: AnalyzeCompression;
14
+ files: AnalyzeFile[];
15
+ routeBundles: AnalyzeRouteBundle[];
16
+ totals: {
17
+ rawBytes: number;
18
+ compressedBytes: number;
19
+ };
20
+ }
21
+
22
+ export interface AnalyzeFile {
23
+ path: string;
24
+ type: 'html' | 'css' | 'js' | 'asset';
25
+ rawBytes: number;
26
+ compressedBytes: number;
27
+ }
28
+
29
+ export interface AnalyzeRouteBundle {
30
+ route: string;
31
+ page: 'server' | 'client';
32
+ initial: AnalyzeBundleFile[];
33
+ visibleDynamic: AnalyzeDynamicBundleFile[];
34
+ dynamic: AnalyzeDynamicBundleFile[];
35
+ lazyShared: AnalyzeBundleFile[];
36
+ }
37
+
38
+ export interface AnalyzeBundleFile {
39
+ path: string;
40
+ rawBytes: number;
41
+ compressedBytes: number;
42
+ }
43
+
44
+ export interface AnalyzeDynamicBundleFile extends AnalyzeBundleFile {
45
+ exportName: string;
46
+ load: 'render' | 'visible';
47
+ }
48
+
49
+ export type AnalyzeCompression = 'gzip' | 'brotli';
50
+
51
+ interface AnalyzeOptions {
52
+ compression?: AnalyzeCompression;
53
+ route?: string;
54
+ }
55
+
56
+ export async function analyzeProject(root?: string, options: AnalyzeOptions = {}): Promise<AnalyzeResult> {
57
+ const config = await loadConfig(root);
58
+ if (!existsSync(config.outPath)) {
59
+ throw new Error(`No PNext output found at ${config.outPath}. Run pnext build first.`);
60
+ }
61
+
62
+ const target = analyzeTarget(config.outPath);
63
+ if (!target) {
64
+ throw new Error(`No PNext build or dev output found at ${config.outPath}. Run pnext build or pnext dev first.`);
65
+ }
66
+
67
+ const compression = options.compression ?? 'gzip';
68
+ const files = await listFiles(target.root);
69
+ const rows = await Promise.all(
70
+ files.map(async file => {
71
+ const bytes = await readFile(file);
72
+ return {
73
+ path: path.relative(target.root, file),
74
+ type: fileType(file),
75
+ rawBytes: bytes.length,
76
+ compressedBytes: compressedSize(bytes, compression),
77
+ };
78
+ }),
79
+ );
80
+ const routeBundles =
81
+ target.mode === 'production'
82
+ ? await analyzeRouteBundles(config.outPath, target.root, rows, options.route)
83
+ : [];
84
+
85
+ return {
86
+ mode: target.mode,
87
+ root: path.relative(config.root, target.root),
88
+ compression,
89
+ files: rows,
90
+ routeBundles,
91
+ totals: rows.reduce(
92
+ (total, row) => ({
93
+ rawBytes: total.rawBytes + row.rawBytes,
94
+ compressedBytes: total.compressedBytes + row.compressedBytes,
95
+ }),
96
+ { rawBytes: 0, compressedBytes: 0 },
97
+ ),
98
+ };
99
+ }
100
+
101
+ async function analyzeRouteBundles(
102
+ outPath: string,
103
+ publicPath: string,
104
+ files: AnalyzeFile[],
105
+ routeFilter?: string,
106
+ ) {
107
+ const manifestPath = path.join(outPath, 'manifest.json');
108
+ if (!existsSync(manifestPath)) return [];
109
+
110
+ const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) as BuildManifest;
111
+ const pages = manifest.routes.filter(route => route.kind === 'page');
112
+ const selected = routeFilter ? filterAnalyzeRoutes(pages, routeFilter) : pages;
113
+ if (routeFilter && selected.length === 0) {
114
+ throw new Error(
115
+ `No route matches ${routeFilter}. Routes:\n ${pages
116
+ .map(route => publicRoutePath(route.route))
117
+ .join('\n ')}`,
118
+ );
119
+ }
120
+ const sizes = new Map(files.map(file => [toPosix(file.path), file]));
121
+ const contents = new Map<string, string>();
122
+
123
+ async function js(pathname: string) {
124
+ const normalized = toPosix(pathname);
125
+ if (contents.has(normalized)) return contents.get(normalized) ?? '';
126
+ const text = await readFile(path.join(publicPath, normalized), 'utf8');
127
+ contents.set(normalized, text);
128
+ return text;
129
+ }
130
+
131
+ const bundles: AnalyzeRouteBundle[] = [];
132
+ for (const route of selected) {
133
+ bundles.push(await analyzeRouteBundle(route, sizes, js));
134
+ }
135
+ return bundles;
136
+ }
137
+
138
+ // Accepts the route in ':id' or '[id]' template form, or a concrete pathname
139
+ // (e.g. /users/ada) matched through the route patterns.
140
+ function filterAnalyzeRoutes(pages: RouteManifestEntry[], filter: string) {
141
+ const normalized = filter === '/' ? filter : filter.replace(/\/+$/, '');
142
+ const direct = pages.filter(
143
+ route => route.route === normalized || publicRoutePath(route.route) === normalized,
144
+ );
145
+ if (direct.length > 0) return direct;
146
+ const matched = matchRoute(pages, normalized);
147
+ return matched ? [matched.route] : [];
148
+ }
149
+
150
+ function publicRoutePath(route: string) {
151
+ return route.replace(/:([a-zA-Z0-9_]+)\*/g, '[...$1]').replace(/:([a-zA-Z0-9_]+)/g, '[$1]');
152
+ }
153
+
154
+ async function analyzeRouteBundle(
155
+ route: RouteManifestEntry,
156
+ sizes: Map<string, AnalyzeFile>,
157
+ js: (path: string) => Promise<string>,
158
+ ): Promise<AnalyzeRouteBundle> {
159
+ const initial = new Set<string>();
160
+ addIfExists(initial, sizes, routeHtmlPath(route));
161
+ addIfExists(initial, sizes, 'assets/global.css');
162
+ addIfExists(initial, sizes, `assets/${route.id}.css`);
163
+
164
+ if (route.clientEntry) {
165
+ addIfExists(initial, sizes, route.clientEntry);
166
+ for (const dependency of await staticDependencies(route.clientEntry, sizes, js)) initial.add(dependency);
167
+ }
168
+
169
+ const visibleDynamic: AnalyzeDynamicBundleFile[] = [];
170
+ const dynamic: AnalyzeDynamicBundleFile[] = [];
171
+ const dynamicTargets = new Set<string>();
172
+ const lazyShared = new Set<string>();
173
+
174
+ if (route.clientEntry && sizes.has(toPosix(route.clientEntry))) {
175
+ const entrySource = await js(route.clientEntry);
176
+ const imports = dynamicImports(entrySource, route.clientEntry);
177
+ for (const imported of imports) {
178
+ const reference = route.clientReferences.find(item => item.exportName === imported.exportName);
179
+ const load: AnalyzeDynamicBundleFile['load'] =
180
+ reference?.dynamic?.load === 'visible' ? 'visible' : 'render';
181
+ const row = sizes.get(imported.path);
182
+ if (!row) continue;
183
+ dynamicTargets.add(imported.path);
184
+ const item: AnalyzeDynamicBundleFile = {
185
+ path: imported.path,
186
+ exportName: imported.exportName,
187
+ load,
188
+ rawBytes: row.rawBytes,
189
+ compressedBytes: row.compressedBytes,
190
+ };
191
+ if (load === 'visible') visibleDynamic.push(item);
192
+ else dynamic.push(item);
193
+
194
+ for (const dependency of await staticDependencies(imported.path, sizes, js)) {
195
+ if (!initial.has(dependency) && !dynamicTargets.has(dependency)) lazyShared.add(dependency);
196
+ }
197
+ }
198
+
199
+ for (const importedPath of dynamicImportPaths(entrySource, route.clientEntry)) {
200
+ if (initial.has(importedPath) || dynamicTargets.has(importedPath)) continue;
201
+ if (sizes.has(importedPath)) lazyShared.add(importedPath);
202
+ for (const dependency of await staticDependencies(importedPath, sizes, js)) {
203
+ if (!initial.has(dependency) && !dynamicTargets.has(dependency)) lazyShared.add(dependency);
204
+ }
205
+ }
206
+ }
207
+
208
+ return {
209
+ route: route.route,
210
+ page: route.client ? 'client' : 'server',
211
+ initial: bundleFiles([...initial], sizes),
212
+ visibleDynamic: visibleDynamic.sort((a, b) => b.compressedBytes - a.compressedBytes),
213
+ dynamic: dynamic.sort((a, b) => b.compressedBytes - a.compressedBytes),
214
+ lazyShared: bundleFiles([...lazyShared], sizes),
215
+ };
216
+ }
217
+
218
+ async function staticDependencies(
219
+ entry: string,
220
+ sizes: Map<string, AnalyzeFile>,
221
+ js: (path: string) => Promise<string>,
222
+ seen = new Set<string>(),
223
+ ) {
224
+ const dependencies = new Set<string>();
225
+ const normalized = toPosix(entry);
226
+ if (seen.has(normalized) || !sizes.has(normalized)) return dependencies;
227
+ seen.add(normalized);
228
+
229
+ for (const specifier of staticImports(await js(normalized))) {
230
+ const dependency = resolveBuiltImport(normalized, specifier);
231
+ if (!dependency || !sizes.has(dependency)) continue;
232
+ dependencies.add(dependency);
233
+ for (const transitive of await staticDependencies(dependency, sizes, js, seen)) dependencies.add(transitive);
234
+ }
235
+
236
+ return dependencies;
237
+ }
238
+
239
+ function dynamicImports(source: string, from: string) {
240
+ const imports: { path: string; exportName: string }[] = [];
241
+ const pattern = /import\("([^"]+)"\)\.then\([A-Za-z_$][\w$]*=>[A-Za-z_$][\w$]*\.([A-Za-z_$][\w$]*)\)/g;
242
+ let match: RegExpExecArray | null;
243
+ while ((match = pattern.exec(source))) {
244
+ const specifier = match[1];
245
+ const exportName = match[2];
246
+ if (!specifier || !exportName) continue;
247
+ const resolved = resolveBuiltImport(from, specifier);
248
+ if (resolved) imports.push({ path: resolved, exportName });
249
+ }
250
+ return imports;
251
+ }
252
+
253
+ function dynamicImportPaths(source: string, from: string) {
254
+ const imports: string[] = [];
255
+ const pattern = /import\("([^"]+)"\)/g;
256
+ let match: RegExpExecArray | null;
257
+ while ((match = pattern.exec(source))) {
258
+ const specifier = match[1];
259
+ if (!specifier) continue;
260
+ const resolved = resolveBuiltImport(from, specifier);
261
+ if (resolved) imports.push(resolved);
262
+ }
263
+ return imports;
264
+ }
265
+
266
+ function staticImports(source: string) {
267
+ const imports: string[] = [];
268
+ const pattern = /import\s*(?:[^'"]*?from\s*)?["']([^'"]+)["']/g;
269
+ let match: RegExpExecArray | null;
270
+ while ((match = pattern.exec(source))) {
271
+ if (match[1]) imports.push(match[1]);
272
+ }
273
+ return imports;
274
+ }
275
+
276
+ function resolveBuiltImport(from: string, specifier: string) {
277
+ if (!specifier.startsWith('.')) return null;
278
+ return toPosix(path.posix.normalize(path.posix.join(path.posix.dirname(toPosix(from)), specifier)));
279
+ }
280
+
281
+ function addIfExists(items: Set<string>, sizes: Map<string, AnalyzeFile>, pathname: string) {
282
+ const normalized = toPosix(pathname);
283
+ if (sizes.has(normalized)) items.add(normalized);
284
+ }
285
+
286
+ function bundleFiles(paths: string[], sizes: Map<string, AnalyzeFile>) {
287
+ return paths
288
+ .map(pathname => {
289
+ const row = sizes.get(pathname);
290
+ return row ? { path: pathname, rawBytes: row.rawBytes, compressedBytes: row.compressedBytes } : null;
291
+ })
292
+ .filter((file): file is AnalyzeBundleFile => Boolean(file))
293
+ .sort((a, b) => b.compressedBytes - a.compressedBytes);
294
+ }
295
+
296
+ function routeHtmlPath(route: RouteManifestEntry) {
297
+ return route.route === '/' ? 'index.html' : `${route.route.replace(/^\/+/, '')}/index.html`;
298
+ }
299
+
300
+ function analyzeTarget(outPath: string) {
301
+ const publicPath = path.join(outPath, 'public');
302
+ if (existsSync(publicPath)) return { mode: 'production' as const, root: publicPath };
303
+
304
+ const cachePath = path.join(outPath, 'cache');
305
+ if (existsSync(cachePath)) return { mode: 'development' as const, root: cachePath };
306
+
307
+ return null;
308
+ }
309
+
310
+ function compressedSize(bytes: Buffer, compression: AnalyzeCompression) {
311
+ if (compression === 'brotli') {
312
+ return zlib.brotliCompressSync(bytes, {
313
+ params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 },
314
+ }).length;
315
+ }
316
+ return zlib.gzipSync(bytes, { level: 9 }).length;
317
+ }
318
+
319
+ function fileType(file: string): 'html' | 'css' | 'js' | 'asset' {
320
+ if (file.endsWith('.html')) return 'html';
321
+ if (file.endsWith('.css')) return 'css';
322
+ if (file.endsWith('.js')) return 'js';
323
+ return 'asset';
324
+ }
325
+
326
+ function toPosix(value: string) {
327
+ return value.split(path.sep).join('/');
328
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Boot phase timings for `pnext dev`, printed when `PNEXT_BOOT_TRACE=1`.
3
+ *
4
+ * Timestamps are `performance.now()`, whose origin is process start, so the
5
+ * first mark's absolute value is everything before it: the bun runtime coming
6
+ * up plus this module graph evaluating. Off, a mark costs one already-resolved
7
+ * boolean test.
8
+ */
9
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
10
+ const enabled = process.env.PNEXT_BOOT_TRACE === '1';
11
+ const marks: [string, number, number][] = [];
12
+
13
+ export function markBoot(name: string) {
14
+ if (enabled) marks.push([name, performance.now(), process.memoryUsage.rss()]);
15
+ }
16
+
17
+ export function printBootTrace() {
18
+ if (!enabled || marks.length === 0) return;
19
+ const lines = ['boot trace (ms since process start):'];
20
+ let previous = 0;
21
+ for (const [name, at, rss] of marks) {
22
+ const mb = (rss / 1048576).toFixed(0).padStart(5);
23
+ lines.push(
24
+ ` ${name.padEnd(16)} +${(at - previous).toFixed(1).padStart(7)} @${at.toFixed(1)} rss ${mb} MB`,
25
+ );
26
+ previous = at;
27
+ }
28
+ console.log(lines.join('\n'));
29
+ }