@wular/pnext 0.0.4 → 0.0.7

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 (395) hide show
  1. package/README.md +22 -16
  2. package/bin/pnext +6 -1
  3. package/config/lint/base.js +7 -7
  4. package/config/ts/base.json +2 -4
  5. package/config/ts/react.json +2 -6
  6. package/package.json +23 -2
  7. package/reference/compat.md +87 -22
  8. package/reference/config.md +40 -93
  9. package/reference/css.md +42 -33
  10. package/reference/dev.md +26 -33
  11. package/reference/env.md +18 -20
  12. package/reference/getting-started.md +132 -0
  13. package/reference/metadata.md +31 -46
  14. package/reference/navigation.md +65 -105
  15. package/reference/performance.md +21 -142
  16. package/reference/rendering.md +78 -75
  17. package/reference/routing.md +97 -107
  18. package/reference/typegen.md +27 -29
  19. package/src/api/cache.ts +36 -37
  20. package/src/api/client-cache.ts +2 -2
  21. package/src/api/client-navigation.ts +121 -114
  22. package/src/api/dynamic.tsx +58 -55
  23. package/src/api/link.tsx +52 -53
  24. package/src/api/navigation.ts +59 -58
  25. package/src/api/server.ts +152 -144
  26. package/src/api/suspense.ts +4 -4
  27. package/src/cli/adapters/vercel-warm.ts +126 -121
  28. package/src/cli/adapters/vercel.ts +437 -443
  29. package/src/cli/analyze.ts +333 -144
  30. package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
  31. package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
  32. package/src/cli/build.ts +976 -978
  33. package/src/cli/create.ts +72 -64
  34. package/src/cli/dev.ts +116 -116
  35. package/src/cli/index.ts +94 -85
  36. package/src/cli/migrate/package-json.ts +73 -71
  37. package/src/cli/migrate/report.ts +32 -33
  38. package/src/cli/migrate/{index.ts → run.ts} +48 -49
  39. package/src/cli/migrate/scan.ts +46 -46
  40. package/src/cli/migrate/spinner.ts +9 -9
  41. package/src/cli/migrate/tsconfig.ts +35 -35
  42. package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
  43. package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
  44. package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
  45. package/src/cli/start.ts +65 -66
  46. package/src/{typegen.ts → cli/typegen.ts} +59 -59
  47. package/src/client/build.ts +802 -768
  48. package/src/client/chunk-fold.ts +245 -240
  49. package/src/client/{paths.ts → chunk-name.ts} +6 -6
  50. package/src/client/entry.ts +162 -147
  51. package/src/client/prebuilt.ts +231 -223
  52. package/src/client/profile.ts +29 -29
  53. package/src/client/react-compiler.ts +20 -15
  54. package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
  55. package/src/client/reference-stub.ts +51 -51
  56. package/src/client/reference.ts +19 -19
  57. package/src/{api → client}/router/events.ts +32 -17
  58. package/src/{api → client}/router/history.ts +16 -16
  59. package/src/{api → client}/router/hub.ts +52 -53
  60. package/src/{api/router.ts → client/router/index.ts} +61 -60
  61. package/src/{api → client}/router/policies.ts +24 -24
  62. package/src/{api → client}/router/runtime.ts +2022 -1995
  63. package/src/{api → client}/router/types.ts +98 -98
  64. package/src/compat/actions/client-plugin.ts +42 -43
  65. package/src/compat/actions/client-stub.ts +14 -14
  66. package/src/compat/actions/{action-client.ts → client.ts} +194 -193
  67. package/src/compat/actions/config.ts +63 -64
  68. package/src/compat/actions/detect.ts +68 -68
  69. package/src/compat/actions/discovery.ts +105 -105
  70. package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
  71. package/src/compat/actions/early-submit.ts +1 -1
  72. package/src/compat/actions/endpoint.ts +198 -198
  73. package/src/compat/actions/extensions.ts +811 -0
  74. package/src/compat/actions/flight.ts +23 -23
  75. package/src/compat/actions/form-state.ts +34 -34
  76. package/src/compat/actions/hoist.ts +382 -236
  77. package/src/compat/actions/ids.ts +7 -12
  78. package/src/compat/actions/index.ts +8 -8
  79. package/src/compat/actions/instances.ts +46 -46
  80. package/src/compat/actions/origin.ts +47 -48
  81. package/src/compat/actions/protocol.ts +22 -22
  82. package/src/compat/actions/registry.ts +19 -19
  83. package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
  84. package/src/compat/actions/rewrite.ts +125 -125
  85. package/src/compat/actions/{action-router.ts → router.ts} +10 -10
  86. package/src/compat/actions/serve.ts +132 -136
  87. package/src/compat/actions/server-tag.ts +4 -4
  88. package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
  89. package/src/compat/actions/unrecognized-error.ts +4 -4
  90. package/src/compat/{index.ts → aliases.ts} +112 -109
  91. package/src/compat/bundler/bun-externals.ts +18 -18
  92. package/src/compat/bundler/cjs-exports.ts +271 -223
  93. package/src/compat/bundler/config.ts +116 -111
  94. package/src/compat/bundler/externals.ts +12 -12
  95. package/src/compat/bundler/import-meta-url.ts +19 -19
  96. package/src/compat/bundler/modularize-imports.ts +36 -33
  97. package/src/compat/bundler/new-url-asset.ts +22 -24
  98. package/src/compat/bundler/optimize-package-imports.ts +111 -107
  99. package/src/compat/bundler/polyfill.ts +28 -28
  100. package/src/compat/bundler/react-compiler.ts +35 -29
  101. package/src/compat/bundler/react-profiler.tsx +11 -11
  102. package/src/compat/bundler/relay-transform.ts +48 -47
  103. package/src/compat/bundler/require-context.ts +119 -113
  104. package/src/compat/bundler/resolve-extensions.ts +19 -19
  105. package/src/compat/bundler/source-cache.ts +25 -25
  106. package/src/compat/bundler/static-imports.ts +7 -7
  107. package/src/compat/bundler/symlink-imports.ts +46 -46
  108. package/src/compat/bundler/tsconfig-paths.ts +13 -15
  109. package/src/compat/bundler/wasm.ts +58 -60
  110. package/src/compat/bundler/webpack-loaders.ts +254 -241
  111. package/src/compat/bundler/worker.ts +101 -104
  112. package/src/compat/cache/build-flags.ts +29 -29
  113. package/src/compat/cache/build-prerender-errors.ts +40 -44
  114. package/src/compat/cache/custom-handler.ts +60 -53
  115. package/src/compat/cache/fetch-patch.ts +240 -240
  116. package/src/compat/cache/handler.ts +27 -31
  117. package/src/compat/cache/modern-handler.ts +148 -126
  118. package/src/compat/cache/resume-data-cache.ts +47 -45
  119. package/src/compat/cache/revalidate.ts +233 -232
  120. package/src/compat/cache/runtime-error.ts +35 -35
  121. package/src/compat/cache/use-cache-transform.ts +442 -417
  122. package/src/compat/cache/use-cache.ts +650 -614
  123. package/src/compat/cache-control.ts +140 -142
  124. package/src/compat/client/base-path.ts +21 -20
  125. package/src/compat/client/css-order.ts +18 -18
  126. package/src/compat/client/errors/bare-boundary.ts +11 -11
  127. package/src/compat/client/errors/control-flow.ts +23 -23
  128. package/src/compat/client/errors/error-boundary.ts +61 -61
  129. package/src/compat/client/errors/global-error.ts +101 -91
  130. package/src/compat/client/errors/install.ts +71 -72
  131. package/src/compat/client/errors/lazy.ts +23 -23
  132. package/src/compat/client/errors/primitive-throw.ts +47 -45
  133. package/src/compat/client/errors/soft-refresh.ts +4 -4
  134. package/src/compat/client/link-status.ts +33 -33
  135. package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
  136. package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
  137. package/src/compat/client/navigation-scroll.ts +63 -63
  138. package/src/compat/client/optimistic-routing.ts +93 -88
  139. package/src/compat/client/prefetch-cache.ts +23 -24
  140. package/src/compat/client/route-announcer.ts +35 -35
  141. package/src/compat/client/segment-cache-policy.ts +20 -20
  142. package/src/compat/client/segment-cache.ts +309 -315
  143. package/src/compat/client/segment-prefetch.ts +127 -132
  144. package/src/compat/client/trailing-slash.ts +5 -4
  145. package/src/compat/css/chunking.ts +113 -116
  146. package/src/compat/css/inline-css.ts +21 -21
  147. package/src/compat/css/lightningcss.ts +37 -38
  148. package/src/compat/css/modules.ts +161 -175
  149. package/src/compat/css/nonce.ts +7 -7
  150. package/src/compat/css/sass-plugin.ts +18 -21
  151. package/src/compat/css/sass.ts +150 -152
  152. package/src/compat/css/styled-jsx-runtime.ts +27 -27
  153. package/src/compat/css/styled-jsx.ts +21 -21
  154. package/src/compat/edge-runtime.ts +27 -27
  155. package/src/compat/{adapter → export}/build-complete.ts +72 -75
  156. package/src/compat/export/client.ts +29 -31
  157. package/src/compat/export/{index.ts → emit.ts} +111 -110
  158. package/src/compat/export/standalone.ts +62 -54
  159. package/src/compat/image-optimizer/cache.ts +51 -49
  160. package/src/compat/image-optimizer/detect.ts +52 -52
  161. package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
  162. package/src/compat/image-optimizer/source.ts +77 -80
  163. package/src/compat/lifecycle/after-scope.ts +26 -26
  164. package/src/compat/lifecycle/after.ts +48 -45
  165. package/src/compat/lifecycle/error-funnel.ts +98 -102
  166. package/src/compat/lifecycle/error-serialize.ts +30 -26
  167. package/src/compat/lifecycle/error-ui.ts +67 -32
  168. package/src/compat/lifecycle/instrumentation-client.ts +36 -38
  169. package/src/compat/lifecycle/instrumentation.ts +85 -85
  170. package/src/compat/lifecycle/node-console.ts +11 -11
  171. package/src/compat/lifecycle/testmode.ts +160 -132
  172. package/src/compat/mdx/compile.ts +60 -57
  173. package/src/compat/mdx/plugin.ts +11 -11
  174. package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
  175. package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
  176. package/src/compat/metadata.ts +75 -81
  177. package/src/compat/next/cache.ts +68 -69
  178. package/src/compat/next/canonical-url.ts +9 -9
  179. package/src/compat/next/client-cache.ts +19 -19
  180. package/src/compat/next/client-navigation.ts +112 -116
  181. package/src/compat/next/client-only.ts +1 -1
  182. package/src/compat/next/client-script.tsx +99 -93
  183. package/src/compat/next/client-server.ts +10 -10
  184. package/src/compat/next/config-loader.ts +176 -173
  185. package/src/compat/next/config.ts +7 -7
  186. package/src/compat/next/constants.cjs +6 -6
  187. package/src/compat/next/constants.ts +6 -6
  188. package/src/compat/next/custom-server.ts +26 -24
  189. package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
  190. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
  191. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
  192. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
  193. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
  194. package/src/compat/next/dynamic.tsx +21 -18
  195. package/src/compat/next/error.tsx +52 -52
  196. package/src/compat/next/font/cache.ts +74 -74
  197. package/src/compat/next/font/google.ts +2 -2
  198. package/src/compat/next/font/index.ts +1 -1
  199. package/src/compat/next/font/local.ts +3 -3
  200. package/src/compat/next/font/runtime-client.ts +17 -15
  201. package/src/compat/next/font/runtime.ts +443 -408
  202. package/src/compat/next/font/shared.ts +120 -108
  203. package/src/compat/next/form.tsx +63 -63
  204. package/src/compat/next/head.tsx +2 -2
  205. package/src/compat/next/headers.ts +103 -95
  206. package/src/compat/next/image/client.tsx +220 -0
  207. package/src/compat/next/image/config.ts +56 -58
  208. package/src/compat/next/image/optimizer.ts +40 -36
  209. package/src/compat/next/image/patterns.ts +37 -40
  210. package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
  211. package/src/compat/next/image/shared.ts +65 -57
  212. package/src/compat/next/image/static-metadata.ts +98 -107
  213. package/src/compat/next/image/validate.ts +79 -88
  214. package/src/compat/next/image.tsx +19 -23
  215. package/src/compat/next/index.ts +1 -1
  216. package/src/compat/next/legacy-image.tsx +59 -60
  217. package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
  218. package/src/compat/next/link.tsx +158 -158
  219. package/src/compat/next/navigation.cjs +12 -3
  220. package/src/compat/next/navigation.ts +48 -50
  221. package/src/compat/next/offline.ts +27 -27
  222. package/src/compat/next/og.ts +121 -124
  223. package/src/compat/next/preferred-region.ts +13 -14
  224. package/src/compat/next/redirects.ts +58 -56
  225. package/src/compat/next/resource-hints.ts +73 -76
  226. package/src/compat/next/rewrites.ts +130 -133
  227. package/src/compat/next/root-params.ts +45 -45
  228. package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
  229. package/src/compat/next/router.cjs +4 -2
  230. package/src/compat/next/router.ts +58 -61
  231. package/src/compat/next/script.tsx +108 -108
  232. package/src/compat/next/server-only.ts +1 -1
  233. package/src/compat/next/server.ts +19 -19
  234. package/src/compat/next/svgr.ts +18 -17
  235. package/src/compat/next/telemetry.ts +24 -24
  236. package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
  237. package/src/compat/next/user-agent.ts +53 -49
  238. package/src/compat/next/web-vitals.ts +22 -24
  239. package/src/compat/otel/api.ts +41 -41
  240. package/src/compat/otel/client-trace-metadata.ts +25 -27
  241. package/src/compat/otel/fetch-span.ts +29 -29
  242. package/src/compat/otel/tracer.ts +331 -331
  243. package/src/compat/pages/api.ts +456 -0
  244. package/src/compat/pages/client-plugin.ts +36 -36
  245. package/src/compat/pages/router-state.ts +34 -34
  246. package/src/compat/pages/{index.ts → router.ts} +130 -135
  247. package/src/compat/ppr/io.ts +12 -12
  248. package/src/compat/ppr/missing-root-params.ts +34 -38
  249. package/src/compat/ppr/root-params-scan.ts +66 -66
  250. package/src/compat/ppr/root-params-transform.ts +24 -26
  251. package/src/compat/ppr/root-params.ts +30 -30
  252. package/src/compat/ppr/segment-config-incompat.ts +6 -7
  253. package/src/compat/protocol.ts +71 -70
  254. package/src/compat/react/action-state.ts +47 -48
  255. package/src/compat/react/client-lite.ts +15 -15
  256. package/src/compat/react/client.ts +4 -4
  257. package/src/compat/react/compiler-runtime.ts +11 -11
  258. package/src/compat/react/dom-client.ts +44 -44
  259. package/src/compat/react/dom-react-server.ts +10 -16
  260. package/src/compat/react/dom-server.ts +10 -10
  261. package/src/compat/react/dom.ts +52 -52
  262. package/src/compat/react/hooks-extra.ts +34 -35
  263. package/src/compat/react/parity.ts +64 -61
  264. package/src/compat/react/preact.ts +81 -82
  265. package/src/compat/react/react-server.ts +28 -28
  266. package/src/compat/react/router-shim.ts +1 -1
  267. package/src/compat/react/server-component-use.ts +8 -8
  268. package/src/compat/react/server-inserted-html.ts +30 -31
  269. package/src/compat/react/server.ts +53 -55
  270. package/src/compat/react/use.ts +32 -32
  271. package/src/compat/react/view-transition.ts +20 -20
  272. package/src/compat/register/actions.ts +35 -824
  273. package/src/compat/register/boot.ts +41 -41
  274. package/src/compat/register/build-tier.ts +5 -5
  275. package/src/compat/register/build.ts +74 -70
  276. package/src/compat/register/bundler.ts +161 -156
  277. package/src/compat/register/cache.ts +20 -20
  278. package/src/compat/register/client-errors.ts +3 -3
  279. package/src/compat/register/config.ts +6 -6
  280. package/src/compat/register/css-extras.ts +30 -34
  281. package/src/compat/register/edge-runtime.ts +3 -3
  282. package/src/compat/register/errors.ts +10 -12
  283. package/src/compat/register/export.ts +16 -16
  284. package/src/compat/register/font.ts +11 -11
  285. package/src/compat/register/hooks.ts +2 -2
  286. package/src/compat/register/image.ts +40 -40
  287. package/src/compat/register/index.ts +59 -62
  288. package/src/compat/register/instrumentation-client.ts +10 -10
  289. package/src/compat/register/lifecycle.ts +25 -28
  290. package/src/compat/register/mdx.ts +10 -10
  291. package/src/compat/register/middleware.ts +226 -16
  292. package/src/compat/register/otel.ts +80 -88
  293. package/src/compat/register/pages-api.ts +10 -463
  294. package/src/compat/register/ppr.ts +16 -16
  295. package/src/compat/register/protocol.ts +17 -18
  296. package/src/compat/register/proxy.ts +49 -51
  297. package/src/compat/register/render.ts +79 -76
  298. package/src/compat/register/routing.ts +162 -159
  299. package/src/compat/register/segment.ts +24 -1897
  300. package/src/compat/register/static-image.ts +3 -3
  301. package/src/compat/register/{misc.ts → taint.ts} +9 -9
  302. package/src/compat/register/typed-routes.ts +14 -14
  303. package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
  304. package/src/compat/register/validation.ts +18 -18
  305. package/src/compat/segment/loading-boundary.ts +43 -45
  306. package/src/compat/segment/page-slot.ts +69 -69
  307. package/src/compat/segment/serve.ts +1884 -0
  308. package/src/compat/segment/tree.ts +113 -112
  309. package/src/compat/segment/vary-key.ts +38 -38
  310. package/src/compat/segment/vary-params.ts +138 -142
  311. package/src/compat/static-params.ts +14 -12
  312. package/src/compat/tsconfig-defaults.ts +87 -91
  313. package/src/compat/typecheck/{index.ts → check.ts} +234 -212
  314. package/src/compat/typecheck/worker.ts +15 -12
  315. package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
  316. package/src/compat/typed-routes/manifest.ts +174 -170
  317. package/src/compat/typed-routes/typegen.ts +127 -110
  318. package/src/compat/validation/errors.ts +16 -19
  319. package/src/compat/validation/prerender-diagnostics.ts +521 -504
  320. package/src/compat/validation/{index.ts → validate.ts} +647 -648
  321. package/src/compat-bootstrap.ts +16 -16
  322. package/src/config.ts +86 -69
  323. package/src/css/build.ts +230 -227
  324. package/src/css/postcss.ts +79 -80
  325. package/src/css/worker.ts +14 -15
  326. package/src/dev/client-actions.ts +10 -10
  327. package/src/dev/client-chunk-store.ts +27 -27
  328. package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
  329. package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
  330. package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
  331. package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
  332. package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
  333. package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
  334. package/src/dev/server.ts +804 -820
  335. package/src/env.ts +46 -43
  336. package/src/extensions.ts +491 -478
  337. package/src/index.ts +8 -8
  338. package/src/internal.ts +20 -23
  339. package/src/{islands → render}/boundary-error.ts +3 -3
  340. package/src/render/hooks.ts +71 -71
  341. package/src/render/island-context.ts +14 -14
  342. package/src/render/metadata.ts +310 -310
  343. package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
  344. package/src/{ppr.ts → render/ppr.ts} +244 -245
  345. package/src/render/renderer.ts +2087 -2082
  346. package/src/render/resource-hints.ts +16 -17
  347. package/src/render/slots.tsx +224 -235
  348. package/src/{islands → render}/static-children.ts +9 -12
  349. package/src/{islands → render}/static-slots.ts +37 -37
  350. package/src/{cache/context.ts → request/cache.ts} +20 -20
  351. package/src/request/context.ts +107 -107
  352. package/src/{dynamic/source.ts → resolve/dynamic.ts} +139 -139
  353. package/src/resolve/engine.ts +90 -77
  354. package/src/resolve/imports.ts +475 -463
  355. package/src/resolve/scan-facts.ts +318 -296
  356. package/src/resolve/source-text.ts +37 -37
  357. package/src/{dynamic → resolve}/tree-shake.ts +132 -128
  358. package/src/routing/forwarded.ts +19 -19
  359. package/src/routing/handler.ts +84 -91
  360. package/src/routing/href.ts +87 -70
  361. package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
  362. package/src/{proxy.ts → routing/proxy.ts} +306 -312
  363. package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
  364. package/src/routing/routes.ts +827 -815
  365. package/src/routing/slots.ts +164 -160
  366. package/src/runtime/loader.ts +954 -0
  367. package/src/{dev → runtime}/module-cache.ts +309 -287
  368. package/src/{dev → runtime}/module-generations.ts +9 -9
  369. package/src/{dev → runtime}/module-transform.ts +81 -72
  370. package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
  371. package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
  372. package/src/runtime/vendor.ts +428 -406
  373. package/src/styles.d.ts +9 -0
  374. package/src/types.ts +324 -335
  375. package/src/utils/ansi.ts +5 -5
  376. package/src/utils/{source.ts → code.ts} +15 -12
  377. package/src/utils/content-type.ts +3 -3
  378. package/src/utils/decode.ts +2 -2
  379. package/src/utils/dev-profile.ts +13 -13
  380. package/src/utils/error-log.ts +6 -6
  381. package/src/utils/esbuild.ts +18 -18
  382. package/src/utils/fs-cache.ts +13 -13
  383. package/src/utils/fs.ts +57 -49
  384. package/src/utils/html.ts +20 -24
  385. package/src/utils/native-require.ts +8 -8
  386. package/src/utils/serialize.ts +139 -146
  387. package/src/utils/verbose.ts +18 -18
  388. package/reference/overview.md +0 -35
  389. package/src/cli/analyze-print.ts +0 -181
  390. package/src/compat/middleware/manifest.ts +0 -210
  391. package/src/compat/next/image-client.tsx +0 -215
  392. package/src/compat/next/link-usage.ts +0 -29
  393. package/src/css/index.ts +0 -2
  394. package/src/render/index.ts +0 -1
  395. package/src/style-modules.d.ts +0 -9
@@ -1,86 +1,92 @@
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';
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 { bold, cyan, dim } from '../utils/ansi'
8
+ import { listFiles } from '../utils/fs'
9
+ import type { BuildManifest, RouteManifestEntry } from '../types'
9
10
 
10
11
  export interface AnalyzeResult {
11
- mode: 'production' | 'development';
12
- root: string;
13
- compression: AnalyzeCompression;
14
- files: AnalyzeFile[];
15
- routeBundles: AnalyzeRouteBundle[];
12
+ mode: 'production' | 'development'
13
+ root: string
14
+ compression: AnalyzeCompression
15
+ files: AnalyzeFile[]
16
+ routeBundles: AnalyzeRouteBundle[]
16
17
  totals: {
17
- rawBytes: number;
18
- compressedBytes: number;
19
- };
18
+ rawBytes: number
19
+ compressedBytes: number
20
+ }
20
21
  }
21
22
 
22
23
  export interface AnalyzeFile {
23
- path: string;
24
- type: 'html' | 'css' | 'js' | 'asset';
25
- rawBytes: number;
26
- compressedBytes: number;
24
+ path: string
25
+ type: 'html' | 'css' | 'js' | 'asset'
26
+ rawBytes: number
27
+ compressedBytes: number
27
28
  }
28
29
 
29
30
  export interface AnalyzeRouteBundle {
30
- route: string;
31
- page: 'server' | 'client';
32
- initial: AnalyzeBundleFile[];
33
- visibleDynamic: AnalyzeDynamicBundleFile[];
34
- dynamic: AnalyzeDynamicBundleFile[];
35
- lazyShared: AnalyzeBundleFile[];
31
+ route: string
32
+ page: 'server' | 'client'
33
+ initial: AnalyzeBundleFile[]
34
+ visibleDynamic: AnalyzeDynamicBundleFile[]
35
+ dynamic: AnalyzeDynamicBundleFile[]
36
+ lazyShared: AnalyzeBundleFile[]
36
37
  }
37
38
 
38
39
  export interface AnalyzeBundleFile {
39
- path: string;
40
- rawBytes: number;
41
- compressedBytes: number;
40
+ path: string
41
+ rawBytes: number
42
+ compressedBytes: number
42
43
  }
43
44
 
44
45
  export interface AnalyzeDynamicBundleFile extends AnalyzeBundleFile {
45
- exportName: string;
46
- load: 'render' | 'visible';
46
+ exportName: string
47
+ load: 'render' | 'visible'
47
48
  }
48
49
 
49
- export type AnalyzeCompression = 'gzip' | 'brotli';
50
+ export type AnalyzeCompression = 'gzip' | 'brotli'
50
51
 
51
52
  interface AnalyzeOptions {
52
- compression?: AnalyzeCompression;
53
- route?: string;
53
+ compression?: AnalyzeCompression
54
+ route?: string
54
55
  }
55
56
 
56
- export async function analyzeProject(root?: string, options: AnalyzeOptions = {}): Promise<AnalyzeResult> {
57
- const config = await loadConfig(root);
57
+ export async function analyzeProject(
58
+ root?: string,
59
+ options: AnalyzeOptions = {},
60
+ ): Promise<AnalyzeResult> {
61
+ const config = await loadConfig(root)
58
62
  if (!existsSync(config.outPath)) {
59
- throw new Error(`No PNext output found at ${config.outPath}. Run pnext build first.`);
63
+ throw new Error(`No PNext output found at ${config.outPath}. Run pnext build first.`)
60
64
  }
61
65
 
62
- const target = analyzeTarget(config.outPath);
66
+ const target = analyzeTarget(config.outPath)
63
67
  if (!target) {
64
- throw new Error(`No PNext build or dev output found at ${config.outPath}. Run pnext build or pnext dev first.`);
68
+ throw new Error(
69
+ `No PNext build or dev output found at ${config.outPath}. Run pnext build or pnext dev first.`,
70
+ )
65
71
  }
66
72
 
67
- const compression = options.compression ?? 'gzip';
68
- const files = await listFiles(target.root);
73
+ const compression = options.compression ?? 'gzip'
74
+ const files = await listFiles(target.root)
69
75
  const rows = await Promise.all(
70
76
  files.map(async file => {
71
- const bytes = await readFile(file);
77
+ const bytes = await readFile(file)
72
78
  return {
73
79
  path: path.relative(target.root, file),
74
80
  type: fileType(file),
75
81
  rawBytes: bytes.length,
76
82
  compressedBytes: compressedSize(bytes, compression),
77
- };
83
+ }
78
84
  }),
79
- );
85
+ )
80
86
  const routeBundles =
81
87
  target.mode === 'production'
82
88
  ? await analyzeRouteBundles(config.outPath, target.root, rows, options.route)
83
- : [];
89
+ : []
84
90
 
85
91
  return {
86
92
  mode: target.mode,
@@ -95,7 +101,7 @@ export async function analyzeProject(root?: string, options: AnalyzeOptions = {}
95
101
  }),
96
102
  { rawBytes: 0, compressedBytes: 0 },
97
103
  ),
98
- };
104
+ }
99
105
  }
100
106
 
101
107
  async function analyzeRouteBundles(
@@ -104,51 +110,51 @@ async function analyzeRouteBundles(
104
110
  files: AnalyzeFile[],
105
111
  routeFilter?: string,
106
112
  ) {
107
- const manifestPath = path.join(outPath, 'manifest.json');
108
- if (!existsSync(manifestPath)) return [];
113
+ const manifestPath = path.join(outPath, 'manifest.json')
114
+ if (!existsSync(manifestPath)) return []
109
115
 
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;
116
+ const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) as BuildManifest
117
+ const pages = manifest.routes.filter(route => route.kind === 'page')
118
+ const selected = routeFilter ? filterAnalyzeRoutes(pages, routeFilter) : pages
113
119
  if (routeFilter && selected.length === 0) {
114
120
  throw new Error(
115
121
  `No route matches ${routeFilter}. Routes:\n ${pages
116
122
  .map(route => publicRoutePath(route.route))
117
123
  .join('\n ')}`,
118
- );
124
+ )
119
125
  }
120
- const sizes = new Map(files.map(file => [toPosix(file.path), file]));
121
- const contents = new Map<string, string>();
126
+ const sizes = new Map(files.map(file => [toPosix(file.path), file]))
127
+ const contents = new Map<string, string>()
122
128
 
123
129
  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;
130
+ const normalized = toPosix(pathname)
131
+ if (contents.has(normalized)) return contents.get(normalized) ?? ''
132
+ const text = await readFile(path.join(publicPath, normalized), 'utf8')
133
+ contents.set(normalized, text)
134
+ return text
129
135
  }
130
136
 
131
- const bundles: AnalyzeRouteBundle[] = [];
137
+ const bundles: AnalyzeRouteBundle[] = []
132
138
  for (const route of selected) {
133
- bundles.push(await analyzeRouteBundle(route, sizes, js));
139
+ bundles.push(await analyzeRouteBundle(route, sizes, js))
134
140
  }
135
- return bundles;
141
+ return bundles
136
142
  }
137
143
 
138
144
  // Accepts the route in ':id' or '[id]' template form, or a concrete pathname
139
145
  // (e.g. /users/ada) matched through the route patterns.
140
146
  function filterAnalyzeRoutes(pages: RouteManifestEntry[], filter: string) {
141
- const normalized = filter === '/' ? filter : filter.replace(/\/+$/, '');
147
+ const normalized = filter === '/' ? filter : filter.replace(/\/+$/, '')
142
148
  const direct = pages.filter(
143
149
  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] : [];
150
+ )
151
+ if (direct.length > 0) return direct
152
+ const matched = matchRoute(pages, normalized)
153
+ return matched ? [matched.route] : []
148
154
  }
149
155
 
150
156
  function publicRoutePath(route: string) {
151
- return route.replace(/:([a-zA-Z0-9_]+)\*/g, '[...$1]').replace(/:([a-zA-Z0-9_]+)/g, '[$1]');
157
+ return route.replace(/:([a-zA-Z0-9_]+)\*/g, '[...$1]').replace(/:([a-zA-Z0-9_]+)/g, '[$1]')
152
158
  }
153
159
 
154
160
  async function analyzeRouteBundle(
@@ -156,51 +162,52 @@ async function analyzeRouteBundle(
156
162
  sizes: Map<string, AnalyzeFile>,
157
163
  js: (path: string) => Promise<string>,
158
164
  ): 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`);
165
+ const initial = new Set<string>()
166
+ addIfExists(initial, sizes, routeHtmlPath(route))
167
+ addIfExists(initial, sizes, 'assets/global.css')
168
+ addIfExists(initial, sizes, `assets/${route.id}.css`)
163
169
 
164
170
  if (route.clientEntry) {
165
- addIfExists(initial, sizes, route.clientEntry);
166
- for (const dependency of await staticDependencies(route.clientEntry, sizes, js)) initial.add(dependency);
171
+ addIfExists(initial, sizes, route.clientEntry)
172
+ for (const dependency of await staticDependencies(route.clientEntry, sizes, js))
173
+ initial.add(dependency)
167
174
  }
168
175
 
169
- const visibleDynamic: AnalyzeDynamicBundleFile[] = [];
170
- const dynamic: AnalyzeDynamicBundleFile[] = [];
171
- const dynamicTargets = new Set<string>();
172
- const lazyShared = new Set<string>();
176
+ const visibleDynamic: AnalyzeDynamicBundleFile[] = []
177
+ const dynamic: AnalyzeDynamicBundleFile[] = []
178
+ const dynamicTargets = new Set<string>()
179
+ const lazyShared = new Set<string>()
173
180
 
174
181
  if (route.clientEntry && sizes.has(toPosix(route.clientEntry))) {
175
- const entrySource = await js(route.clientEntry);
176
- const imports = dynamicImports(entrySource, route.clientEntry);
182
+ const entrySource = await js(route.clientEntry)
183
+ const imports = dynamicImports(entrySource, route.clientEntry)
177
184
  for (const imported of imports) {
178
- const reference = route.clientReferences.find(item => item.exportName === imported.exportName);
185
+ const reference = route.clientReferences.find(item => item.exportName === imported.exportName)
179
186
  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);
187
+ reference?.dynamic?.load === 'visible' ? 'visible' : 'render'
188
+ const row = sizes.get(imported.path)
189
+ if (!row) continue
190
+ dynamicTargets.add(imported.path)
184
191
  const item: AnalyzeDynamicBundleFile = {
185
192
  path: imported.path,
186
193
  exportName: imported.exportName,
187
194
  load,
188
195
  rawBytes: row.rawBytes,
189
196
  compressedBytes: row.compressedBytes,
190
- };
191
- if (load === 'visible') visibleDynamic.push(item);
192
- else dynamic.push(item);
197
+ }
198
+ if (load === 'visible') visibleDynamic.push(item)
199
+ else dynamic.push(item)
193
200
 
194
201
  for (const dependency of await staticDependencies(imported.path, sizes, js)) {
195
- if (!initial.has(dependency) && !dynamicTargets.has(dependency)) lazyShared.add(dependency);
202
+ if (!initial.has(dependency) && !dynamicTargets.has(dependency)) lazyShared.add(dependency)
196
203
  }
197
204
  }
198
205
 
199
206
  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);
207
+ if (initial.has(importedPath) || dynamicTargets.has(importedPath)) continue
208
+ if (sizes.has(importedPath)) lazyShared.add(importedPath)
202
209
  for (const dependency of await staticDependencies(importedPath, sizes, js)) {
203
- if (!initial.has(dependency) && !dynamicTargets.has(dependency)) lazyShared.add(dependency);
210
+ if (!initial.has(dependency) && !dynamicTargets.has(dependency)) lazyShared.add(dependency)
204
211
  }
205
212
  }
206
213
  }
@@ -212,7 +219,7 @@ async function analyzeRouteBundle(
212
219
  visibleDynamic: visibleDynamic.sort((a, b) => b.compressedBytes - a.compressedBytes),
213
220
  dynamic: dynamic.sort((a, b) => b.compressedBytes - a.compressedBytes),
214
221
  lazyShared: bundleFiles([...lazyShared], sizes),
215
- };
222
+ }
216
223
  }
217
224
 
218
225
  async function staticDependencies(
@@ -221,108 +228,290 @@ async function staticDependencies(
221
228
  js: (path: string) => Promise<string>,
222
229
  seen = new Set<string>(),
223
230
  ) {
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);
231
+ const dependencies = new Set<string>()
232
+ const normalized = toPosix(entry)
233
+ if (seen.has(normalized) || !sizes.has(normalized)) return dependencies
234
+ seen.add(normalized)
228
235
 
229
236
  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);
237
+ const dependency = resolveBuiltImport(normalized, specifier)
238
+ if (!dependency || !sizes.has(dependency)) continue
239
+ dependencies.add(dependency)
240
+ for (const transitive of await staticDependencies(dependency, sizes, js, seen))
241
+ dependencies.add(transitive)
234
242
  }
235
243
 
236
- return dependencies;
244
+ return dependencies
237
245
  }
238
246
 
239
247
  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;
248
+ const imports: { path: string; exportName: string }[] = []
249
+ const pattern =
250
+ /import\("([^"]+)"\)\.then\([A-Za-z_$][\w$]*=>[A-Za-z_$][\w$]*\.([A-Za-z_$][\w$]*)\)/g
251
+ let match: RegExpExecArray | null
243
252
  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 });
253
+ const specifier = match[1]
254
+ const exportName = match[2]
255
+ if (!specifier || !exportName) continue
256
+ const resolved = resolveBuiltImport(from, specifier)
257
+ if (resolved) imports.push({ path: resolved, exportName })
249
258
  }
250
- return imports;
259
+ return imports
251
260
  }
252
261
 
253
262
  function dynamicImportPaths(source: string, from: string) {
254
- const imports: string[] = [];
255
- const pattern = /import\("([^"]+)"\)/g;
256
- let match: RegExpExecArray | null;
263
+ const imports: string[] = []
264
+ const pattern = /import\("([^"]+)"\)/g
265
+ let match: RegExpExecArray | null
257
266
  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);
267
+ const specifier = match[1]
268
+ if (!specifier) continue
269
+ const resolved = resolveBuiltImport(from, specifier)
270
+ if (resolved) imports.push(resolved)
262
271
  }
263
- return imports;
272
+ return imports
264
273
  }
265
274
 
266
275
  function staticImports(source: string) {
267
- const imports: string[] = [];
268
- const pattern = /import\s*(?:[^'"]*?from\s*)?["']([^'"]+)["']/g;
269
- let match: RegExpExecArray | null;
276
+ const imports: string[] = []
277
+ const pattern = /import\s*(?:[^'"]*?from\s*)?["']([^'"]+)["']/g
278
+ let match: RegExpExecArray | null
270
279
  while ((match = pattern.exec(source))) {
271
- if (match[1]) imports.push(match[1]);
280
+ if (match[1]) imports.push(match[1])
272
281
  }
273
- return imports;
282
+ return imports
274
283
  }
275
284
 
276
285
  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)));
286
+ if (!specifier.startsWith('.')) return null
287
+ return toPosix(
288
+ path.posix.normalize(path.posix.join(path.posix.dirname(toPosix(from)), specifier)),
289
+ )
279
290
  }
280
291
 
281
292
  function addIfExists(items: Set<string>, sizes: Map<string, AnalyzeFile>, pathname: string) {
282
- const normalized = toPosix(pathname);
283
- if (sizes.has(normalized)) items.add(normalized);
293
+ const normalized = toPosix(pathname)
294
+ if (sizes.has(normalized)) items.add(normalized)
284
295
  }
285
296
 
286
297
  function bundleFiles(paths: string[], sizes: Map<string, AnalyzeFile>) {
287
298
  return paths
288
299
  .map(pathname => {
289
- const row = sizes.get(pathname);
290
- return row ? { path: pathname, rawBytes: row.rawBytes, compressedBytes: row.compressedBytes } : null;
300
+ const row = sizes.get(pathname)
301
+ return row
302
+ ? { path: pathname, rawBytes: row.rawBytes, compressedBytes: row.compressedBytes }
303
+ : null
291
304
  })
292
305
  .filter((file): file is AnalyzeBundleFile => Boolean(file))
293
- .sort((a, b) => b.compressedBytes - a.compressedBytes);
306
+ .sort((a, b) => b.compressedBytes - a.compressedBytes)
294
307
  }
295
308
 
296
309
  function routeHtmlPath(route: RouteManifestEntry) {
297
- return route.route === '/' ? 'index.html' : `${route.route.replace(/^\/+/, '')}/index.html`;
310
+ return route.route === '/' ? 'index.html' : `${route.route.replace(/^\/+/, '')}/index.html`
298
311
  }
299
312
 
300
313
  function analyzeTarget(outPath: string) {
301
- const publicPath = path.join(outPath, 'public');
302
- if (existsSync(publicPath)) return { mode: 'production' as const, root: publicPath };
314
+ const publicPath = path.join(outPath, 'public')
315
+ if (existsSync(publicPath)) return { mode: 'production' as const, root: publicPath }
303
316
 
304
- const cachePath = path.join(outPath, 'cache');
305
- if (existsSync(cachePath)) return { mode: 'development' as const, root: cachePath };
317
+ const cachePath = path.join(outPath, 'cache')
318
+ if (existsSync(cachePath)) return { mode: 'development' as const, root: cachePath }
306
319
 
307
- return null;
320
+ return null
308
321
  }
309
322
 
310
323
  function compressedSize(bytes: Buffer, compression: AnalyzeCompression) {
311
324
  if (compression === 'brotli') {
312
325
  return zlib.brotliCompressSync(bytes, {
313
326
  params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 },
314
- }).length;
327
+ }).length
315
328
  }
316
- return zlib.gzipSync(bytes, { level: 9 }).length;
329
+ return zlib.gzipSync(bytes, { level: 9 }).length
317
330
  }
318
331
 
319
332
  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';
333
+ if (file.endsWith('.html')) return 'html'
334
+ if (file.endsWith('.css')) return 'css'
335
+ if (file.endsWith('.js')) return 'js'
336
+ return 'asset'
324
337
  }
325
338
 
326
339
  function toPosix(value: string) {
327
- return value.split(path.sep).join('/');
340
+ return value.split(path.sep).join('/')
341
+ }
342
+
343
+ export function printAnalyzeResult(
344
+ result: AnalyzeResult,
345
+ options: { route?: string; files: boolean },
346
+ ) {
347
+ console.log(
348
+ `PNext analyze: ${result.mode === 'production' ? 'production build' : 'dev server cache'} at ${dim(result.root)} (${result.compression})`,
349
+ )
350
+ printRouteBundles(result)
351
+ // With a route filter, the remaining files are mostly other routes' bundles —
352
+ // listing them as "other" would misread as unowned weight.
353
+ if (!options.route) printAnalyzeFiles(result, { details: options.files })
354
+ }
355
+
356
+ function printRouteBundles(result: AnalyzeResult) {
357
+ if (result.routeBundles.length === 0) return
358
+
359
+ console.log(`\n${bold('Routes')}`)
360
+ for (const bundle of result.routeBundles) {
361
+ console.log(`\n${cyan(bundle.route)} ${dim(`(${bundle.page} page)`)}`)
362
+ printBundleGroup('initial', bundle.initial, result.compression)
363
+ printDynamicBundleGroup('visible dynamic', bundle.visibleDynamic, result.compression)
364
+ printDynamicBundleGroup('dynamic', bundle.dynamic, result.compression)
365
+ printBundleGroup('shared after dynamic', bundle.lazyShared, result.compression)
366
+ }
367
+ console.log('')
368
+ }
369
+
370
+ function printBundleGroup(
371
+ label: string,
372
+ files: AnalyzeResult['routeBundles'][number]['initial'],
373
+ compression: AnalyzeCompression,
374
+ ) {
375
+ if (files.length === 0) return
376
+ console.log(`\n ${bold(label.padEnd(22))} ${formatTotalSize(files, compression)}`)
377
+ printBundleFiles(files, compression, ' ', rowColumns(files))
378
+ }
379
+
380
+ function printDynamicBundleGroup(
381
+ label: string,
382
+ files: AnalyzeResult['routeBundles'][number]['visibleDynamic'],
383
+ compression: AnalyzeCompression,
384
+ ) {
385
+ if (files.length === 0) return
386
+ console.log(`\n ${bold(label)}`)
387
+ const columns = rowColumns(files)
388
+ const componentWidth = Math.max(...files.map(file => file.exportName.length))
389
+ const pathWidth = Math.max(...files.map(file => file.path.length))
390
+ for (const file of files) {
391
+ console.log(
392
+ ` ${file.exportName.padEnd(componentWidth)} ${dim(file.path.padEnd(pathWidth))} ${formatFileSize(file, compression, columns)}`,
393
+ )
394
+ }
395
+ }
396
+
397
+ function formatTotalSize(
398
+ files: { rawBytes: number; compressedBytes: number }[],
399
+ compression: AnalyzeCompression,
400
+ ) {
401
+ const rawBytes = files.reduce((total, file) => total + file.rawBytes, 0)
402
+ const compressedBytes = files.reduce((total, file) => total + file.compressedBytes, 0)
403
+ return `${formatBytes(compressedBytes)} ${compression} ${dim(`${formatBytes(rawBytes)} raw`)}`
404
+ }
405
+
406
+ function printAnalyzeFiles(result: AnalyzeResult, options: { details: boolean }) {
407
+ const routeFiles = new Set(
408
+ result.routeBundles.flatMap(bundle => [
409
+ ...bundle.initial.map(file => file.path),
410
+ ...bundle.visibleDynamic.map(file => file.path),
411
+ ...bundle.dynamic.map(file => file.path),
412
+ ...bundle.lazyShared.map(file => file.path),
413
+ ]),
414
+ )
415
+ const sorted = sortedAnalyzeFiles(result.files).filter(file => !routeFiles.has(file.path))
416
+ if (sorted.length === 0) return
417
+
418
+ if (!options.details) {
419
+ console.log(
420
+ `Other files: ${formatFileCount(sorted)} ${formatTotalSize(sorted, result.compression)}`,
421
+ )
422
+ console.log('Run pnext analyze --files to list them.')
423
+ return
424
+ }
425
+
426
+ console.log(`${bold('Other files')}\n`)
427
+ for (const type of ['html', 'css', 'js', 'asset'] as const) {
428
+ const files = sorted.filter(file => file.type === type)
429
+ if (files.length === 0) continue
430
+ console.log(bold(type))
431
+ printBundleFiles(files, result.compression, ' ', rowColumns(files))
432
+ console.log('')
433
+ }
434
+ }
435
+
436
+ function printBundleFiles(
437
+ files: { path: string; rawBytes: number; compressedBytes: number }[],
438
+ compression: AnalyzeCompression,
439
+ indent: string,
440
+ columns: RowColumns,
441
+ ) {
442
+ const pathWidth = Math.max(...files.map(file => file.path.length))
443
+ for (const file of files) {
444
+ console.log(
445
+ `${indent}${dim(file.path.padEnd(pathWidth))} ${formatFileSize(file, compression, columns)}`,
446
+ )
447
+ }
448
+ }
449
+
450
+ interface RowColumns {
451
+ compressedWidth: number
452
+ rawWidth: number
453
+ }
454
+
455
+ function rowColumns(files: { rawBytes: number; compressedBytes: number }[]): RowColumns {
456
+ return {
457
+ compressedWidth: Math.max(...files.map(file => formatBytes(file.compressedBytes).length)),
458
+ rawWidth: Math.max(...files.map(file => formatBytes(file.rawBytes).length)),
459
+ }
460
+ }
461
+
462
+ function formatFileSize(
463
+ file: { rawBytes: number; compressedBytes: number },
464
+ compression: AnalyzeCompression,
465
+ columns: RowColumns,
466
+ ) {
467
+ const compressed = formatBytes(file.compressedBytes).padStart(columns.compressedWidth)
468
+ const raw = `${formatBytes(file.rawBytes).padStart(columns.rawWidth)} raw`
469
+ return `${compressed} ${compression} ${dim(raw)}`
470
+ }
471
+
472
+ function formatFileCount(files: AnalyzeResult['files']) {
473
+ const counts = new Map<string, number>()
474
+ for (const file of files) counts.set(file.type, (counts.get(file.type) ?? 0) + 1)
475
+ return [...counts].map(([type, count]) => `${count} ${pluralFileType(type, count)}`).join(', ')
476
+ }
477
+
478
+ function pluralFileType(type: string, count: number) {
479
+ if (count === 1) return type
480
+ if (type === 'js' || type === 'css' || type === 'html') return type
481
+ return `${type}s`
482
+ }
483
+
484
+ function sortedAnalyzeFiles(files: AnalyzeResult['files']) {
485
+ return [...files].sort((a, b) => {
486
+ const typeDelta = analyzeTypeRank(a.type) - analyzeTypeRank(b.type)
487
+ if (typeDelta) return typeDelta
488
+ const sizeDelta = b.compressedBytes - a.compressedBytes
489
+ if (sizeDelta) return sizeDelta
490
+ return a.path.localeCompare(b.path)
491
+ })
492
+ }
493
+
494
+ function analyzeTypeRank(type: AnalyzeResult['files'][number]['type']) {
495
+ switch (type) {
496
+ case 'html':
497
+ return 0
498
+ case 'css':
499
+ return 1
500
+ case 'js':
501
+ return 2
502
+ case 'asset':
503
+ return 3
504
+ }
505
+ }
506
+
507
+ function formatBytes(bytes: number) {
508
+ if (bytes < 1024) return `${bytes} B`
509
+ const units = ['KB', 'MB', 'GB', 'TB'] as const
510
+ let value = bytes / 1024
511
+ let unitIndex = 0
512
+ while (value >= 1024 && unitIndex < units.length - 1) {
513
+ value /= 1024
514
+ unitIndex += 1
515
+ }
516
+ return `${value >= 10 ? value.toFixed(1) : value.toFixed(2)} ${units[unitIndex]}`
328
517
  }