@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,15 +1,16 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
- import { createHash } from 'node:crypto';
3
- import { createRequire } from 'node:module';
4
- import { existsSync, readFileSync } from 'node:fs';
5
- import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
6
- import path from 'node:path';
7
- import { fileURLToPath } from 'node:url';
8
- import type { ResolvedConfig } from '../../../config';
9
- import { onExtensionHostReset, registerResponseFinalizers } from '../../../extensions';
10
- import { getWorkUnit } from '../../../request/context';
11
- import type { MetadataLink } from '../../../types';
12
- import { persistFont, persistedFont } from './cache';
1
+ import { AsyncLocalStorage } from 'node:async_hooks'
2
+ import { createHash } from 'node:crypto'
3
+ import { createRequire } from 'node:module'
4
+ import { existsSync, readFileSync } from 'node:fs'
5
+ import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises'
6
+ import path from 'node:path'
7
+ import { fileURLToPath } from 'node:url'
8
+ import type { ResolvedConfig } from '../../../config'
9
+ import { onExtensionHostReset, registerResponseFinalizers } from '../../../extensions'
10
+ import { getWorkUnit } from '../../../request/context'
11
+ import type { MetadataLink } from '../../../types'
12
+ import { persistFont, persistedFont } from './cache'
13
+ import { escapeRegex } from '../../../utils/code'
13
14
  import {
14
15
  familyStack,
15
16
  fontStyle,
@@ -23,7 +24,7 @@ import {
23
24
  type LocalFontSource,
24
25
  type NextFont,
25
26
  type NextFontWithVariable,
26
- } from './shared';
27
+ } from './shared'
27
28
 
28
29
  export type {
29
30
  FontDisplay,
@@ -32,87 +33,88 @@ export type {
32
33
  LocalFontSource,
33
34
  NextFont,
34
35
  NextFontWithVariable,
35
- };
36
+ }
36
37
 
37
38
  interface FontScope {
38
- fonts: Map<string, FontDefinition>;
39
+ fonts: Map<string, FontDefinition>
39
40
  }
40
41
 
41
42
  interface FontDefinition {
42
- className: string;
43
- family: string;
44
- key: string;
45
- resolve(context: FontResolveContext): Promise<ResolvedFont>;
46
- style: NextFont['style'];
47
- variable: string;
48
- hasVariable: boolean;
43
+ className: string
44
+ family: string
45
+ key: string
46
+ resolve(context: FontResolveContext): Promise<ResolvedFont>
47
+ style: NextFont['style']
48
+ variable: string
49
+ hasVariable: boolean
49
50
  }
50
51
 
51
52
  interface FontResolveContext {
52
- config: ResolvedConfig;
53
- dev?: boolean;
53
+ config: ResolvedConfig
54
+ dev?: boolean
54
55
  }
55
56
 
56
57
  export interface ResolvedFontAssets {
57
- css: string;
58
- preloads: MetadataLink[];
58
+ css: string
59
+ preloads: MetadataLink[]
59
60
  }
60
61
 
61
62
  interface ResolvedFont {
62
- css: string;
63
+ css: string
63
64
  /** Preloadable font links (empty when `preload:false`). Delivered via the
64
65
  * `Link` response header in prod, not as HTML `<link>` tags. */
65
- preloads: MetadataLink[];
66
+ preloads: MetadataLink[]
66
67
  /** Whether this definition emitted any font file (used to decide preconnect). */
67
- usesFonts: boolean;
68
+ usesFonts: boolean
68
69
  /** Emitted font files on disk, re-checked on every resolve — a rebuild can
69
70
  * wipe the output directory under a running server. */
70
- files: string[];
71
+ files: string[]
71
72
  /** Local source files this resolution read; the persisted record stats them. */
72
- sources?: string[];
73
+ sources?: string[]
73
74
  }
74
75
 
75
76
  interface GoogleFontMetadata {
76
- axes?: { tag: string; min: number; max: number; defaultValue?: number }[];
77
- styles: string[];
78
- subsets: string[];
79
- weights: string[];
77
+ axes?: { tag: string; min: number; max: number; defaultValue?: number }[]
78
+ styles: string[]
79
+ subsets: string[]
80
+ weights: string[]
80
81
  }
81
82
 
82
83
  interface NormalizedGoogleOptions {
83
- axes?: string[];
84
- adjustFontFallback: boolean;
85
- display: FontDisplay;
86
- fallback: string[];
87
- preload: boolean;
88
- styles: string[];
89
- subsets?: string[];
90
- variable?: string;
91
- weights: string[];
84
+ axes?: string[]
85
+ adjustFontFallback: boolean
86
+ display: FontDisplay
87
+ fallback: string[]
88
+ preload: boolean
89
+ styles: string[]
90
+ subsets?: string[]
91
+ variable?: string
92
+ weights: string[]
92
93
  }
93
94
 
94
95
  interface NormalizedLocalSource {
95
- file: string;
96
- style?: string;
97
- weight?: string;
98
- }
99
-
100
- const fontStorageKey = Symbol.for('pnext.compat.font.storage');
101
- const fontRuntimeGlobal = globalThis as typeof globalThis & Record<symbol, AsyncLocalStorage<FontScope> | undefined>;
102
- const fontStorage = fontRuntimeGlobal[fontStorageKey] ??= new AsyncLocalStorage<FontScope>();
103
- const allowedDisplayValues = new Set(['auto', 'block', 'swap', 'fallback', 'optional']);
104
- const require = createRequire(import.meta.url);
105
- const googleCssCache = new Map<string, Promise<string>>();
106
- const googleFontFileCache = new Map<string, Promise<Uint8Array>>();
107
- const resolvedFontCache = new Map<string, Promise<ResolvedFont>>();
108
- let googleMetadataCache: Record<string, GoogleFontMetadata> | undefined;
96
+ file: string
97
+ style?: string
98
+ weight?: string
99
+ }
100
+
101
+ const fontStorageKey = Symbol.for('pnext.compat.font.storage')
102
+ const fontRuntimeGlobal = globalThis as typeof globalThis &
103
+ Record<symbol, AsyncLocalStorage<FontScope> | undefined>
104
+ const fontStorage = (fontRuntimeGlobal[fontStorageKey] ??= new AsyncLocalStorage<FontScope>())
105
+ const allowedDisplayValues = new Set(['auto', 'block', 'swap', 'fallback', 'optional'])
106
+ const require = createRequire(import.meta.url)
107
+ const googleCssCache = new Map<string, Promise<string>>()
108
+ const googleFontFileCache = new Map<string, Promise<Uint8Array>>()
109
+ const resolvedFontCache = new Map<string, Promise<ResolvedFont>>()
110
+ let googleMetadataCache: Record<string, GoogleFontMetadata> | undefined
109
111
 
110
112
  /**
111
113
  * Every font definition constructed so far, whether or not a render has read its `.className` yet.
112
114
  * Definitions are created when the app's font module evaluates - well before the render that registers
113
115
  * them - which is what makes the resolve prewarmable.
114
116
  */
115
- const declaredFonts = new Map<string, FontDefinition>();
117
+ const declaredFonts = new Map<string, FontDefinition>()
116
118
 
117
119
  /**
118
120
  * Resolve the declared fonts ahead of the render that will ask for them. Fire-and-forget: this only
@@ -124,53 +126,53 @@ export async function prewarmFonts(
124
126
  options: { dev?: boolean } = {},
125
127
  ): Promise<void> {
126
128
  // eslint-disable-next-line turbo/no-undeclared-env-vars
127
- if (process.env.PNEXT_PREWARM_FONTS === '0' || declaredFonts.size === 0) return;
129
+ if (process.env.PNEXT_PREWARM_FONTS === '0' || declaredFonts.size === 0) return
128
130
  await Promise.allSettled(
129
131
  [...declaredFonts.values()].map(font => resolveFont(font, { config, dev: options.dev })),
130
- );
132
+ )
131
133
  }
132
134
 
133
135
  export function runWithFontScope<T>(callback: () => T): T {
134
136
  // Ensure the `Link`-header finalizer is registered before any render runs.
135
137
  // Idempotent; register-font also calls it (canonical orchestrator seam).
136
- registerFontFinalizerOnce();
137
- return fontStorage.run({ fonts: new Map() }, callback);
138
+ registerFontFinalizerOnce()
139
+ return fontStorage.run({ fonts: new Map() }, callback)
138
140
  }
139
141
 
140
142
  /** Work-unit stash key for the font `Link` header preloads collected in a render. */
141
- const FONT_HEADER_PRELOADS = Symbol.for('pnext.compat.font.headerPreloads');
143
+ const FONT_HEADER_PRELOADS = Symbol.for('pnext.compat.font.headerPreloads')
142
144
 
143
- export async function fontAssets(config: ResolvedConfig, options: { dev?: boolean } = {}): Promise<ResolvedFontAssets> {
144
- const scope = fontStorage.getStore();
145
- if (!scope || scope.fonts.size === 0) return { css: '', preloads: [] };
145
+ export async function fontAssets(
146
+ config: ResolvedConfig,
147
+ options: { dev?: boolean } = {},
148
+ ): Promise<ResolvedFontAssets> {
149
+ const scope = fontStorage.getStore()
150
+ if (!scope || scope.fonts.size === 0) return { css: '', preloads: [] }
146
151
 
147
152
  const resolved = await Promise.all(
148
153
  [...scope.fonts.values()].map(font => resolveFont(font, { config, dev: options.dev })),
149
- );
154
+ )
150
155
 
151
- const preloads = dedupePreloads(resolved.flatMap(font => font.preloads));
156
+ const preloads = dedupePreloads(resolved.flatMap(font => font.preloads))
152
157
  // Next delivers font preloads via a `Link` response header (not HTML link
153
158
  // tags). Stash them on the request work unit; the response finalizer
154
159
  // (registerFontResponseFinalizer) reads them back and sets the header.
155
- stashHeaderPreloads(preloads);
160
+ stashHeaderPreloads(preloads)
156
161
 
157
- const usesFonts = resolved.some(font => font.usesFonts);
162
+ const usesFonts = resolved.some(font => font.usesFonts)
158
163
  return {
159
- css: resolved.map(font => font.css).filter(Boolean).join('\n'),
164
+ css: resolved
165
+ .map(font => font.css)
166
+ .filter(Boolean)
167
+ .join('\n'),
160
168
  // Prod matches Next: font preloads travel EXCLUSIVELY in the `Link` response header, so the SSR
161
169
  // document carries no `<link as="font">` tags. The live DOM still gets the links post-load via
162
170
  // fontPreloadDomScript, which derives them from the emitted @font-face CSS - Next's client inserts
163
171
  // them the same way, and the navigation suite counts them in the browser. Dev keeps the document
164
172
  // links, since it has no header path.
165
173
  preloads:
166
- preloads.length > 0
167
- ? options.dev
168
- ? preloads
169
- : []
170
- : usesFonts
171
- ? [fontPreconnect()]
172
- : [],
173
- };
174
+ preloads.length > 0 ? (options.dev ? preloads : []) : usesFonts ? [fontPreconnect()] : [],
175
+ }
174
176
  }
175
177
 
176
178
  /**
@@ -190,31 +192,31 @@ export function fontPreloadDomScript(): string {
190
192
  'var u;try{u=new URL(m[1],location.href).href}catch(e){continue}if(seen[u])continue;seen[u]=1;' +
191
193
  'var l=document.createElement("link");l.rel="preload";l.as="font";l.type="font/woff2";l.setAttribute("crossorigin","");l.href=u;document.head.appendChild(l)}}}' +
192
194
  'document.readyState==="loading"?document.addEventListener("DOMContentLoaded",a):a()}catch(e){}})();</script>'
193
- );
195
+ )
194
196
  }
195
197
 
196
198
  function stashHeaderPreloads(preloads: MetadataLink[]): void {
197
- if (preloads.length === 0) return;
198
- const unit = getWorkUnit();
199
- if (!unit) return;
200
- const compat = (unit.compat ??= {});
201
- const existing = (compat[FONT_HEADER_PRELOADS] as MetadataLink[] | undefined) ?? [];
202
- compat[FONT_HEADER_PRELOADS] = dedupePreloads([...existing, ...preloads]);
199
+ if (preloads.length === 0) return
200
+ const unit = getWorkUnit()
201
+ if (!unit) return
202
+ const compat = (unit.compat ??= {})
203
+ const existing = (compat[FONT_HEADER_PRELOADS] as MetadataLink[] | undefined) ?? []
204
+ compat[FONT_HEADER_PRELOADS] = dedupePreloads([...existing, ...preloads])
203
205
  }
204
206
 
205
207
  /** Read the font `Link` header preloads stashed during the current render. */
206
208
  export function takeFontHeaderPreloads(): MetadataLink[] {
207
- const unit = getWorkUnit();
208
- const compat = unit?.compat;
209
- if (!compat) return [];
210
- const preloads = (compat[FONT_HEADER_PRELOADS] as MetadataLink[] | undefined) ?? [];
211
- compat[FONT_HEADER_PRELOADS] = undefined;
212
- return preloads;
209
+ const unit = getWorkUnit()
210
+ const compat = unit?.compat
211
+ if (!compat) return []
212
+ const preloads = (compat[FONT_HEADER_PRELOADS] as MetadataLink[] | undefined) ?? []
213
+ compat[FONT_HEADER_PRELOADS] = undefined
214
+ return preloads
213
215
  }
214
216
 
215
- let fontFinalizerRegistered = false;
217
+ let fontFinalizerRegistered = false
216
218
  // Host-scoped guard: the finalizer lives on the host, so a fresh host needs it again.
217
- onExtensionHostReset(() => (fontFinalizerRegistered = false));
219
+ onExtensionHostReset(() => (fontFinalizerRegistered = false))
218
220
 
219
221
  /**
220
222
  * Register the response finalizer that flushes the stashed font preloads to the
@@ -223,30 +225,30 @@ onExtensionHostReset(() => (fontFinalizerRegistered = false));
223
225
  * header even before the orchestrator wires register-font in.
224
226
  */
225
227
  export function registerFontFinalizerOnce(): void {
226
- if (fontFinalizerRegistered) return;
227
- fontFinalizerRegistered = true;
228
+ if (fontFinalizerRegistered) return
229
+ fontFinalizerRegistered = true
228
230
  registerResponseFinalizers(ctx => {
229
- const preloads = takeFontHeaderPreloads();
230
- if (preloads.length === 0) return;
231
- const value = preloads.map(fontLinkHeaderValue).join(', ');
232
- const existing = ctx.headers.get('link');
233
- ctx.headers.set('link', existing ? `${existing}, ${value}` : value);
234
- });
231
+ const preloads = takeFontHeaderPreloads()
232
+ if (preloads.length === 0) return
233
+ const value = preloads.map(fontLinkHeaderValue).join(', ')
234
+ const existing = ctx.headers.get('link')
235
+ ctx.headers.set('link', existing ? `${existing}, ${value}` : value)
236
+ })
235
237
  }
236
238
 
237
239
  /** `<url>; rel=preload; as=font; type=font/woff2; crossorigin=""` */
238
240
  export function fontLinkHeaderValue(link: MetadataLink): string {
239
- const parts = [`<${link.url}>`, `rel=${link.rel}`];
240
- if (link.as) parts.push(`as=${link.as}`);
241
- if (link.type) parts.push(`type=${link.type}`);
242
- parts.push('crossorigin=""');
243
- return parts.join('; ');
241
+ const parts = [`<${link.url}>`, `rel=${link.rel}`]
242
+ if (link.as) parts.push(`as=${link.as}`)
243
+ if (link.type) parts.push(`type=${link.type}`)
244
+ parts.push('crossorigin=""')
245
+ return parts.join('; ')
244
246
  }
245
247
 
246
248
  export function googleFont(family: string) {
247
249
  return (options: GoogleFontOptions = {}): NextFontWithVariable => {
248
- const naming = googleFontNaming(family, options);
249
- const { className, variable, fontFamily, hash, style, hasVariable } = naming;
250
+ const naming = googleFontNaming(family, options)
251
+ const { className, variable, fontFamily, hash, style, hasVariable } = naming
250
252
  const definition: FontDefinition = {
251
253
  className,
252
254
  family: fontFamily,
@@ -254,38 +256,39 @@ export function googleFont(family: string) {
254
256
  style,
255
257
  variable,
256
258
  hasVariable,
257
- resolve: context => resolveGoogleFont(context, {
258
- className,
259
- family,
260
- fontFamily,
261
- hash,
262
- options,
263
- variable,
264
- }),
265
- };
259
+ resolve: context =>
260
+ resolveGoogleFont(context, {
261
+ className,
262
+ family,
263
+ fontFamily,
264
+ hash,
265
+ options,
266
+ variable,
267
+ }),
268
+ }
266
269
 
267
- return fontObject(definition);
268
- };
270
+ return fontObject(definition)
271
+ }
269
272
  }
270
273
 
271
274
  export interface LocalFontLoader {
272
- (options: LocalFontOptions): NextFontWithVariable;
273
- withFile: typeof localFontWithFile;
275
+ (options: LocalFontOptions): NextFontWithVariable
276
+ withFile: typeof localFontWithFile
274
277
  }
275
278
 
276
279
  export const localFont: LocalFontLoader = Object.assign(
277
280
  (options: LocalFontOptions) => localFontWithFile(inferLocalFontCallerFile())(options),
278
281
  { withFile: localFontWithFile },
279
- );
282
+ )
280
283
 
281
284
  export function localFontWithFile(callerFile: string | undefined, family?: string) {
282
285
  return (options: LocalFontOptions): NextFontWithVariable => {
283
- const naming = localFontNaming(callerFile, options);
286
+ const naming = localFontNaming(callerFile, options)
284
287
  if (family) {
285
- naming.fontFamily = family;
286
- naming.style = fontStyle(family, options, options.fallback ?? []);
288
+ naming.fontFamily = family
289
+ naming.style = fontStyle(family, options, options.fallback ?? [])
287
290
  }
288
- const { className, variable, fontFamily, hash, style, hasVariable } = naming;
291
+ const { className, variable, fontFamily, hash, style, hasVariable } = naming
289
292
  const definition: FontDefinition = {
290
293
  className,
291
294
  family: fontFamily,
@@ -293,73 +296,74 @@ export function localFontWithFile(callerFile: string | undefined, family?: strin
293
296
  style,
294
297
  variable,
295
298
  hasVariable,
296
- resolve: context => resolveLocalFont(context, {
297
- callerFile,
298
- className,
299
- fontFamily,
300
- hash,
301
- options,
302
- variable,
303
- }),
304
- };
299
+ resolve: context =>
300
+ resolveLocalFont(context, {
301
+ callerFile,
302
+ className,
303
+ fontFamily,
304
+ hash,
305
+ options,
306
+ variable,
307
+ }),
308
+ }
305
309
 
306
- return fontObject(definition);
307
- };
310
+ return fontObject(definition)
311
+ }
308
312
  }
309
313
 
310
314
  function fontObject(definition: FontDefinition): NextFontWithVariable {
311
315
  // Recorded at construction, not at first `.className` read: the whole point
312
316
  // is to know the font exists before any render registers it.
313
- declaredFonts.set(definition.key, definition);
317
+ declaredFonts.set(definition.key, definition)
314
318
  // `variable` is exposed only when the loader call passed a `variable` option
315
319
  // (matches Next; an extra key would break `toEqual` on the font object).
316
320
  const descriptors: PropertyDescriptorMap = {
317
321
  className: {
318
322
  enumerable: true,
319
323
  get() {
320
- registerFont(definition);
321
- return definition.className;
324
+ registerFont(definition)
325
+ return definition.className
322
326
  },
323
327
  },
324
328
  style: {
325
329
  enumerable: true,
326
330
  get() {
327
- registerFont(definition);
328
- return definition.style;
331
+ registerFont(definition)
332
+ return definition.style
329
333
  },
330
334
  },
331
- };
335
+ }
332
336
  if (definition.hasVariable) {
333
337
  descriptors.variable = {
334
338
  enumerable: true,
335
339
  get() {
336
- registerFont(definition);
337
- return definition.variable;
340
+ registerFont(definition)
341
+ return definition.variable
338
342
  },
339
- };
343
+ }
340
344
  }
341
- return Object.defineProperties({}, descriptors) as NextFontWithVariable;
345
+ return Object.defineProperties({}, descriptors) as NextFontWithVariable
342
346
  }
343
347
 
344
348
  function registerFont(definition: FontDefinition) {
345
- fontStorage.getStore()?.fonts.set(definition.key, definition);
349
+ fontStorage.getStore()?.fonts.set(definition.key, definition)
346
350
  }
347
351
 
348
352
  async function resolveFont(definition: FontDefinition, context: FontResolveContext) {
349
- const key = `${context.config.outPath}:${context.dev ? 'dev' : 'build'}:${definition.key}`;
350
- let resolved = resolvedFontCache.get(key);
353
+ const key = `${context.config.outPath}:${context.dev ? 'dev' : 'build'}:${definition.key}`
354
+ let resolved = resolvedFontCache.get(key)
351
355
  if (!resolved) {
352
- resolved = resolveThroughCache(definition, context, key);
353
- resolvedFontCache.set(key, resolved);
356
+ resolved = resolveThroughCache(definition, context, key)
357
+ resolvedFontCache.set(key, resolved)
354
358
  }
355
- const font = await resolved;
359
+ const font = await resolved
356
360
  // A `pnext build` wiping the output directory under a running server must
357
361
  // not leave cached font URLs pointing at deleted files; re-emit them.
358
362
  // Font bytes stay cached in memory, so this never refetches the network.
359
- if (font.files.every(file => existsSync(file))) return font;
360
- const fresh = resolveAndPersist(definition, context, key);
361
- resolvedFontCache.set(key, fresh);
362
- return fresh;
363
+ if (font.files.every(file => existsSync(file))) return font
364
+ const fresh = resolveAndPersist(definition, context, key)
365
+ resolvedFontCache.set(key, fresh)
366
+ return fresh
363
367
  }
364
368
 
365
369
  /**
@@ -372,10 +376,10 @@ async function resolveThroughCache(
372
376
  context: FontResolveContext,
373
377
  key: string,
374
378
  ): Promise<ResolvedFont> {
375
- if (!context.dev) return definition.resolve(context);
376
- const persisted = await persistedFont(context.config, key);
377
- if (persisted) return persisted;
378
- return resolveAndPersist(definition, context, key);
379
+ if (!context.dev) return definition.resolve(context)
380
+ const persisted = await persistedFont(context.config, key)
381
+ if (persisted) return persisted
382
+ return resolveAndPersist(definition, context, key)
379
383
  }
380
384
 
381
385
  function resolveAndPersist(
@@ -383,31 +387,32 @@ function resolveAndPersist(
383
387
  context: FontResolveContext,
384
388
  key: string,
385
389
  ): Promise<ResolvedFont> {
386
- const resolved = definition.resolve(context);
390
+ const resolved = definition.resolve(context)
387
391
  if (context.dev) {
388
- void resolved.then(font => persistFont(context.config, key, font)).catch(() => undefined);
392
+ void resolved.then(font => persistFont(context.config, key, font)).catch(() => undefined)
389
393
  }
390
- return resolved;
394
+ return resolved
391
395
  }
392
396
 
393
397
  async function resolveGoogleFont(
394
398
  context: FontResolveContext,
395
399
  request: {
396
- className: string;
397
- family: string;
398
- fontFamily: string;
399
- hash: string;
400
- options: GoogleFontOptions;
401
- variable: string;
400
+ className: string
401
+ family: string
402
+ fontFamily: string
403
+ hash: string
404
+ options: GoogleFontOptions
405
+ variable: string
402
406
  },
403
407
  ): Promise<ResolvedFont> {
404
- const metadata = googleMetadata()[request.family];
405
- if (!metadata) throw new Error(`Unknown font \`${request.family}\``);
406
- const options = normalizeGoogleOptions(request.family, metadata, request.options);
407
- const adjustFontFallback = options.adjustFontFallback;
408
- const url = googleFontsUrl(request.family, googleFontAxes(metadata, options), options.display);
409
- const css = (await googleCss(url, request.family, Boolean(context.dev))).split('body {', 1)[0] ?? '';
410
- const fontFiles = findGoogleFontFiles(css, options.preload ? options.subsets : undefined);
408
+ const metadata = googleMetadata()[request.family]
409
+ if (!metadata) throw new Error(`Unknown font \`${request.family}\``)
410
+ const options = normalizeGoogleOptions(request.family, metadata, request.options)
411
+ const adjustFontFallback = options.adjustFontFallback
412
+ const url = googleFontsUrl(request.family, googleFontAxes(metadata, options), options.display)
413
+ const css =
414
+ (await googleCss(url, request.family, Boolean(context.dev))).split('body {', 1)[0] ?? ''
415
+ const fontFiles = findGoogleFontFiles(css, options.preload ? options.subsets : undefined)
411
416
  const emitted = await Promise.all(
412
417
  fontFiles.map(async fontFile => ({
413
418
  ...fontFile,
@@ -416,20 +421,29 @@ async function resolveGoogleFont(
416
421
  adjustFontFallback,
417
422
  }),
418
423
  })),
419
- );
424
+ )
420
425
 
421
- let rewrittenCss = css;
426
+ let rewrittenCss = css
422
427
  for (const item of emitted) {
423
- rewrittenCss = rewrittenCss.replaceAll(item.googleFontFileUrl, item.emitted.url);
428
+ rewrittenCss = rewrittenCss.replaceAll(item.googleFontFileUrl, item.emitted.url)
424
429
  }
425
430
 
426
- const stack = familyStack(request.fontFamily, options.fallback, adjustFontFallback);
431
+ const stack = familyStack(request.fontFamily, options.fallback, adjustFontFallback)
427
432
  const scopedCss = [
428
433
  rewriteFontFamily(rewrittenCss, request.family, request.fontFamily),
429
434
  adjustFontFallback ? fallbackFaceCss(request.fontFamily) : '',
430
- classRule(request.className, stack, singleCssValue(options.weights), singleCssValue(options.styles)),
431
- request.options.variable ? variableRule(request.variable, customProperty(request.options.variable), stack) : '',
432
- ].filter(Boolean).join('\n');
435
+ classRule(
436
+ request.className,
437
+ stack,
438
+ singleCssValue(options.weights),
439
+ singleCssValue(options.styles),
440
+ ),
441
+ request.options.variable
442
+ ? variableRule(request.variable, customProperty(request.options.variable), stack)
443
+ : '',
444
+ ]
445
+ .filter(Boolean)
446
+ .join('\n')
433
447
 
434
448
  return {
435
449
  usesFonts: emitted.length > 0,
@@ -438,44 +452,52 @@ async function resolveGoogleFont(
438
452
  .filter(item => item.preloadFontFile)
439
453
  .map(item => fontPreload(item.emitted.url, item.emitted.type)),
440
454
  files: emitted.map(item => item.emitted.file),
441
- };
455
+ }
442
456
  }
443
457
 
444
458
  async function resolveLocalFont(
445
459
  context: FontResolveContext,
446
460
  request: {
447
- callerFile: string | undefined;
448
- className: string;
449
- fontFamily: string;
450
- hash: string;
451
- options: LocalFontOptions;
452
- variable: string;
461
+ callerFile: string | undefined
462
+ className: string
463
+ fontFamily: string
464
+ hash: string
465
+ options: LocalFontOptions
466
+ variable: string
453
467
  },
454
468
  ): Promise<ResolvedFont> {
455
- const display = validateDisplay(request.options.display ?? 'swap');
469
+ const display = validateDisplay(request.options.display ?? 'swap')
456
470
  const adjustFontFallback = request.options.declarations?.some(item => item.prop === 'font-family')
457
471
  ? false
458
- : true;
459
- const preload = request.options.preload !== false;
460
- const sources = normalizeLocalSources(request.options, request.callerFile);
461
- const emitted = await Promise.all(sources.map(async source => ({
462
- ...source,
463
- emitted: await emitLocalFontFile(context, source.file, { preload, adjustFontFallback }),
464
- })));
465
- const stack = familyStack(request.fontFamily, request.options.fallback ?? [], adjustFontFallback);
466
- const declarations = request.options.declarations ?? [];
472
+ : true
473
+ const preload = request.options.preload !== false
474
+ const sources = normalizeLocalSources(request.options, request.callerFile)
475
+ const emitted = await Promise.all(
476
+ sources.map(async source => ({
477
+ ...source,
478
+ emitted: await emitLocalFontFile(context, source.file, { preload, adjustFontFallback }),
479
+ })),
480
+ )
481
+ const stack = familyStack(request.fontFamily, request.options.fallback ?? [], adjustFontFallback)
482
+ const declarations = request.options.declarations ?? []
467
483
  const faceCss = emitted.map(source =>
468
484
  [
469
485
  '@font-face{',
470
486
  `font-family:${singleQuoteFamily(request.fontFamily)};`,
471
487
  `src:url("${source.emitted.url}") format("${fontFormat(source.emitted.ext)}");`,
472
488
  `font-display:${display};`,
473
- source.weight ?? request.options.weight ? `font-weight:${cssIdentifier(source.weight ?? request.options.weight ?? '')};` : '',
474
- source.style ?? request.options.style ? `font-style:${cssIdentifier(source.style ?? request.options.style ?? '')};` : '',
489
+ (source.weight ?? request.options.weight)
490
+ ? `font-weight:${cssIdentifier(source.weight ?? request.options.weight ?? '')};`
491
+ : '',
492
+ (source.style ?? request.options.style)
493
+ ? `font-style:${cssIdentifier(source.style ?? request.options.style ?? '')};`
494
+ : '',
475
495
  ...declarations.map(item => `${cssProperty(item.prop)}:${cssDeclarationValue(item.value)};`),
476
496
  '}',
477
- ].filter(Boolean).join(''),
478
- );
497
+ ]
498
+ .filter(Boolean)
499
+ .join(''),
500
+ )
479
501
 
480
502
  return {
481
503
  usesFonts: emitted.length > 0,
@@ -483,14 +505,16 @@ async function resolveLocalFont(
483
505
  ...faceCss,
484
506
  adjustFontFallback ? fallbackFaceCss(request.fontFamily) : '',
485
507
  classRule(request.className, stack, request.options.weight, request.options.style),
486
- request.options.variable ? variableRule(request.variable, customProperty(request.options.variable), stack) : '',
487
- ].filter(Boolean).join('\n'),
488
- preloads: preload
489
- ? emitted.map(item => fontPreload(item.emitted.url, item.emitted.type))
490
- : [],
508
+ request.options.variable
509
+ ? variableRule(request.variable, customProperty(request.options.variable), stack)
510
+ : '',
511
+ ]
512
+ .filter(Boolean)
513
+ .join('\n'),
514
+ preloads: preload ? emitted.map(item => fontPreload(item.emitted.url, item.emitted.type)) : [],
491
515
  files: emitted.map(item => item.emitted.file),
492
516
  sources: emitted.map(source => source.file),
493
- };
517
+ }
494
518
  }
495
519
 
496
520
  function normalizeGoogleOptions(
@@ -498,50 +522,54 @@ function normalizeGoogleOptions(
498
522
  metadata: GoogleFontMetadata,
499
523
  options: GoogleFontOptions,
500
524
  ): NormalizedGoogleOptions {
501
- const display = validateDisplay(options.display ?? 'swap');
502
- const preload = options.preload ?? true;
503
- const subsets = options.subsets;
525
+ const display = validateDisplay(options.display ?? 'swap')
526
+ const preload = options.preload ?? true
527
+ const subsets = options.subsets
504
528
  if (metadata.subsets.length === 0) {
505
529
  // Matches Next: fonts without preloadable subsets silently disable preload.
506
530
  } else if (preload) {
507
531
  if (!subsets) {
508
532
  throw new Error(
509
533
  `Preload is enabled but no subsets were specified for font \`${family}\`. ` +
510
- `Please specify subsets or disable preloading.`,
511
- );
534
+ `Please specify subsets or disable preloading.`,
535
+ )
512
536
  }
513
537
  for (const subset of subsets) {
514
538
  if (!metadata.subsets.includes(subset)) {
515
- throw new Error(`Unknown subset \`${subset}\` for font \`${family}\`.`);
539
+ throw new Error(`Unknown subset \`${subset}\` for font \`${family}\`.`)
516
540
  }
517
541
  }
518
542
  }
519
543
 
520
- const weights = uniqueArray(options.weight ? arrayValue(options.weight) : []);
544
+ const weights = uniqueArray(options.weight ? arrayValue(options.weight) : [])
521
545
  if (weights.length === 0) {
522
- if (metadata.weights.includes('variable')) weights.push('variable');
523
- else throw new Error(`Missing weight for font \`${family}\`.`);
546
+ if (metadata.weights.includes('variable')) weights.push('variable')
547
+ else throw new Error(`Missing weight for font \`${family}\`.`)
524
548
  }
525
549
  if (weights.length > 1 && weights.includes('variable')) {
526
- throw new Error(`Unexpected \`variable\` in weight array for font \`${family}\`.`);
550
+ throw new Error(`Unexpected \`variable\` in weight array for font \`${family}\`.`)
527
551
  }
528
552
  for (const weight of weights) {
529
- if (!metadata.weights.includes(weight)) throw new Error(`Unknown weight \`${weight}\` for font \`${family}\`.`);
553
+ if (!metadata.weights.includes(weight))
554
+ throw new Error(`Unknown weight \`${weight}\` for font \`${family}\`.`)
530
555
  }
531
556
 
532
- const styles = uniqueArray(options.style ? arrayValue(options.style) : []);
533
- if (styles.length === 0) styles.push(metadata.styles.length === 1 ? (metadata.styles[0] ?? 'normal') : 'normal');
557
+ const styles = uniqueArray(options.style ? arrayValue(options.style) : [])
558
+ if (styles.length === 0)
559
+ styles.push(metadata.styles.length === 1 ? (metadata.styles[0] ?? 'normal') : 'normal')
534
560
  for (const style of styles) {
535
- if (!metadata.styles.includes(style)) throw new Error(`Unknown style \`${style}\` for font \`${family}\`.`);
561
+ if (!metadata.styles.includes(style))
562
+ throw new Error(`Unknown style \`${style}\` for font \`${family}\`.`)
536
563
  }
537
564
 
538
565
  if (options.axes) {
539
566
  if (!metadata.weights.includes('variable') || weights[0] !== 'variable') {
540
- throw new Error('Axes can only be defined for variable fonts.');
567
+ throw new Error('Axes can only be defined for variable fonts.')
541
568
  }
542
- const definableAxes = (metadata.axes ?? []).map(axis => axis.tag).filter(tag => tag !== 'wght');
569
+ const definableAxes = (metadata.axes ?? []).map(axis => axis.tag).filter(tag => tag !== 'wght')
543
570
  for (const axis of options.axes) {
544
- if (!definableAxes.includes(axis)) throw new Error(`Invalid axes value \`${axis}\` for font \`${family}\`.`);
571
+ if (!definableAxes.includes(axis))
572
+ throw new Error(`Invalid axes value \`${axis}\` for font \`${family}\`.`)
545
573
  }
546
574
  }
547
575
 
@@ -555,30 +583,35 @@ function normalizeGoogleOptions(
555
583
  subsets,
556
584
  variable: options.variable,
557
585
  weights,
558
- };
586
+ }
559
587
  }
560
588
 
561
589
  function googleFontAxes(metadata: GoogleFontMetadata, options: NormalizedGoogleOptions) {
562
- const hasItalic = options.styles.includes('italic');
563
- const hasNormal = options.styles.includes('normal');
564
- const ital = hasItalic ? [...(hasNormal ? ['0'] : []), '1'] : undefined;
590
+ const hasItalic = options.styles.includes('italic')
591
+ const hasNormal = options.styles.includes('normal')
592
+ const ital = hasItalic ? [...(hasNormal ? ['0'] : []), '1'] : undefined
565
593
 
566
594
  if (options.weights[0] !== 'variable') {
567
- return { ital, wght: options.weights, variableAxes: undefined as [string, string][] | undefined };
595
+ return {
596
+ ital,
597
+ wght: options.weights,
598
+ variableAxes: undefined as [string, string][] | undefined,
599
+ }
568
600
  }
569
601
 
570
- let weightAxis: string | undefined;
571
- const variableAxes: [string, string][] = [];
602
+ let weightAxis: string | undefined
603
+ const variableAxes: [string, string][] = []
572
604
  for (const axis of metadata.axes ?? []) {
573
- if (axis.tag === 'wght') weightAxis = `${axis.min}..${axis.max}`;
574
- else if (options.axes?.includes(axis.tag)) variableAxes.push([axis.tag, `${axis.min}..${axis.max}`]);
605
+ if (axis.tag === 'wght') weightAxis = `${axis.min}..${axis.max}`
606
+ else if (options.axes?.includes(axis.tag))
607
+ variableAxes.push([axis.tag, `${axis.min}..${axis.max}`])
575
608
  }
576
609
 
577
610
  return {
578
611
  ital,
579
612
  wght: weightAxis ? [weightAxis] : undefined,
580
613
  variableAxes: variableAxes.length > 0 ? variableAxes : undefined,
581
- };
614
+ }
582
615
  }
583
616
 
584
617
  function googleFontsUrl(
@@ -586,116 +619,120 @@ function googleFontsUrl(
586
619
  axes: { ital?: string[]; wght?: string[]; variableAxes?: [string, string][] },
587
620
  display: FontDisplay,
588
621
  ) {
589
- const variants: [string, string][][] = [];
622
+ const variants: [string, string][][] = []
590
623
  if (axes.wght) {
591
624
  for (const wght of axes.wght) {
592
625
  if (!axes.ital) {
593
- variants.push([['wght', wght], ...(axes.variableAxes ?? [])]);
626
+ variants.push([['wght', wght], ...(axes.variableAxes ?? [])])
594
627
  } else {
595
- for (const ital of axes.ital) variants.push([['ital', ital], ['wght', wght], ...(axes.variableAxes ?? [])]);
628
+ for (const ital of axes.ital)
629
+ variants.push([['ital', ital], ['wght', wght], ...(axes.variableAxes ?? [])])
596
630
  }
597
631
  }
598
632
  } else if (axes.variableAxes) {
599
- variants.push([...axes.variableAxes]);
633
+ variants.push([...axes.variableAxes])
600
634
  }
601
635
 
602
636
  if (axes.variableAxes) {
603
637
  for (const variant of variants) {
604
638
  variant.sort(([a], [b]) => {
605
- const aLower = a.charCodeAt(0) > 96;
606
- const bLower = b.charCodeAt(0) > 96;
607
- if (aLower && !bLower) return -1;
608
- if (bLower && !aLower) return 1;
609
- return a > b ? 1 : -1;
610
- });
639
+ const aLower = a.charCodeAt(0) > 96
640
+ const bLower = b.charCodeAt(0) > 96
641
+ if (aLower && !bLower) return -1
642
+ if (bLower && !aLower) return 1
643
+ return a > b ? 1 : -1
644
+ })
611
645
  }
612
646
  }
613
647
 
614
- let url = `https://fonts.googleapis.com/css2?family=${family.replace(/ /g, '+')}`;
615
- const firstVariant = variants[0];
648
+ let url = `https://fonts.googleapis.com/css2?family=${family.replace(/ /g, '+')}`
649
+ const firstVariant = variants[0]
616
650
  if (firstVariant) {
617
651
  url += `:${firstVariant.map(([key]) => key).join(',')}@${variants
618
652
  .map(variant => variant.map(([, value]) => value).join(','))
619
653
  .sort(sortFontVariantValues)
620
- .join(';')}`;
654
+ .join(';')}`
621
655
  }
622
- return `${url}&display=${display}`;
656
+ return `${url}&display=${display}`
623
657
  }
624
658
 
625
659
  async function googleCss(url: string, family: string, dev: boolean) {
626
- let cached = googleCssCache.get(url);
660
+ let cached = googleCssCache.get(url)
627
661
  if (!cached) {
628
- cached = fetchGoogleCss(url, family, dev);
629
- googleCssCache.set(url, cached);
662
+ cached = fetchGoogleCss(url, family, dev)
663
+ googleCssCache.set(url, cached)
630
664
  }
631
- return cached;
665
+ return cached
632
666
  }
633
667
 
634
668
  async function fetchGoogleCss(url: string, family: string, dev: boolean) {
635
- const mocked = mockedGoogleResponse(url);
636
- if (typeof mocked === 'string') return mocked;
669
+ const mocked = mockedGoogleResponse(url)
670
+ if (typeof mocked === 'string') return mocked
637
671
 
638
672
  const response = await fetch(url, {
639
673
  headers: {
640
674
  // Google returns modern woff2 CSS for current browsers.
641
675
  'user-agent': 'Mozilla/5.0 AppleWebKit/537.36 Chrome/120 Safari/537.36',
642
676
  },
643
- });
677
+ })
644
678
  if (!response.ok) {
645
- const message = `Failed to fetch font \`${family}\` from Google Fonts: ${response.status} ${response.statusText}`;
646
- throw new Error(dev ? `${message}. Use NEXT_FONT_GOOGLE_MOCKED_RESPONSES for offline tests.` : message);
679
+ const message = `Failed to fetch font \`${family}\` from Google Fonts: ${response.status} ${response.statusText}`
680
+ throw new Error(
681
+ dev ? `${message}. Use NEXT_FONT_GOOGLE_MOCKED_RESPONSES for offline tests.` : message,
682
+ )
647
683
  }
648
- return response.text();
684
+ return response.text()
649
685
  }
650
686
 
651
687
  function findGoogleFontFiles(css: string, subsetsToPreload: string[] | undefined) {
652
- const fontFiles: { googleFontFileUrl: string; preloadFontFile: boolean }[] = [];
653
- let currentSubset = '';
688
+ const fontFiles: { googleFontFileUrl: string; preloadFontFile: boolean }[] = []
689
+ let currentSubset = ''
654
690
  for (const line of css.split('\n')) {
655
- const subset = /\/\* (.+?) \*\//.exec(line)?.[1];
691
+ const subset = /\/\* (.+?) \*\//.exec(line)?.[1]
656
692
  if (subset) {
657
- currentSubset = subset;
658
- continue;
693
+ currentSubset = subset
694
+ continue
659
695
  }
660
696
 
661
- const googleFontFileUrl = /src: url\((.+?)\)/.exec(line)?.[1];
662
- if (!googleFontFileUrl || fontFiles.some(file => file.googleFontFileUrl === googleFontFileUrl)) continue;
697
+ const googleFontFileUrl = /src: url\((.+?)\)/.exec(line)?.[1]
698
+ if (!googleFontFileUrl || fontFiles.some(file => file.googleFontFileUrl === googleFontFileUrl))
699
+ continue
663
700
  fontFiles.push({
664
701
  googleFontFileUrl,
665
702
  preloadFontFile: Boolean(subsetsToPreload?.includes(currentSubset)),
666
- });
703
+ })
667
704
  }
668
- return fontFiles;
705
+ return fontFiles
669
706
  }
670
707
 
671
708
  interface EmitFontOptions {
672
- preload: boolean;
673
- adjustFontFallback: boolean;
709
+ preload: boolean
710
+ adjustFontFallback: boolean
674
711
  }
675
712
 
676
713
  async function emitRemoteFontFile(context: FontResolveContext, url: string, emit: EmitFontOptions) {
677
- let cached = googleFontFileCache.get(url);
714
+ let cached = googleFontFileCache.get(url)
678
715
  if (!cached) {
679
- cached = fetchFontFile(url);
680
- googleFontFileCache.set(url, cached);
716
+ cached = fetchFontFile(url)
717
+ googleFontFileCache.set(url, cached)
681
718
  }
682
- const bytes = await cached;
683
- return emitFontBytes(context, bytes, fontExt(url), emit);
719
+ const bytes = await cached
720
+ return emitFontBytes(context, bytes, fontExt(url), emit)
684
721
  }
685
722
 
686
723
  async function fetchFontFile(url: string) {
687
- const mocked = mockedGoogleResponse(url);
688
- if (mocked instanceof Uint8Array) return mocked;
689
- if (typeof mocked === 'string') return new TextEncoder().encode(mocked);
724
+ const mocked = mockedGoogleResponse(url)
725
+ if (mocked instanceof Uint8Array) return mocked
726
+ if (typeof mocked === 'string') return new TextEncoder().encode(mocked)
690
727
 
691
- const response = await fetch(url);
692
- if (!response.ok) throw new Error(`Failed to fetch font file from \`${url}\`.`);
693
- return new Uint8Array(await response.arrayBuffer());
728
+ const response = await fetch(url)
729
+ if (!response.ok) throw new Error(`Failed to fetch font file from \`${url}\`.`)
730
+ return new Uint8Array(await response.arrayBuffer())
694
731
  }
695
732
 
696
733
  async function emitLocalFontFile(context: FontResolveContext, file: string, emit: EmitFontOptions) {
697
- const bytes = await readFile(file);
698
- return emitFontBytes(context, bytes, fontExt(file), emit, file);
734
+ const bytes = await readFile(file)
735
+ return emitFontBytes(context, bytes, fontExt(file), emit, file)
699
736
  }
700
737
 
701
738
  async function emitFontBytes(
@@ -705,165 +742,168 @@ async function emitFontBytes(
705
742
  emit: EmitFontOptions,
706
743
  sourceFile?: string,
707
744
  ) {
708
- const hash = createHash('sha256').update(bytes).digest('hex').slice(0, 16);
745
+ const hash = createHash('sha256').update(bytes).digest('hex').slice(0, 16)
709
746
  // Next's loader names emitted files `[hash]-s.p.[ext]`: `-s` when a
710
747
  // size-adjust fallback font is used, `.p` when the font is preloaded.
711
- const filename = `${hash}${emit.adjustFontFallback ? '-s' : ''}${emit.preload ? '.p' : ''}.${ext}`;
748
+ const filename = `${hash}${emit.adjustFontFallback ? '-s' : ''}${emit.preload ? '.p' : ''}.${ext}`
712
749
  // Served from `/_next/static/media/` (files under public/ map 1:1 to the URL
713
750
  // path). Dev stashes under cache/ but keeps the same relative layout.
714
- const mediaSegments = ['_next', 'static', 'media'];
751
+ const mediaSegments = ['_next', 'static', 'media']
715
752
  const outRoot = context.dev
716
753
  ? path.join(context.config.outPath, 'cache')
717
- : path.join(context.config.outPath, 'public');
718
- const outDir = path.join(outRoot, ...mediaSegments);
719
- const file = path.join(outDir, filename);
720
- await mkdir(outDir, { recursive: true });
754
+ : path.join(context.config.outPath, 'public')
755
+ const outDir = path.join(outRoot, ...mediaSegments)
756
+ const file = path.join(outDir, filename)
757
+ await mkdir(outDir, { recursive: true })
721
758
  if (!existsSync(file)) {
722
- if (sourceFile) await copyFile(sourceFile, file);
723
- else await writeFile(file, bytes);
759
+ if (sourceFile) await copyFile(sourceFile, file)
760
+ else await writeFile(file, bytes)
724
761
  }
725
- return { ext, file, type: fontMimeType(ext), url: `/${mediaSegments.join('/')}/${filename}` };
762
+ return { ext, file, type: fontMimeType(ext), url: `/${mediaSegments.join('/')}/${filename}` }
726
763
  }
727
764
 
728
- function normalizeLocalSources(options: LocalFontOptions, callerFile: string | undefined): NormalizedLocalSource[] {
729
- if (!callerFile) throw new Error('next/font/local requires PNext server source transforms.');
730
- const values = Array.isArray(options.src) ? options.src : [options.src];
765
+ function normalizeLocalSources(
766
+ options: LocalFontOptions,
767
+ callerFile: string | undefined,
768
+ ): NormalizedLocalSource[] {
769
+ if (!callerFile) throw new Error('next/font/local requires PNext server source transforms.')
770
+ const values = Array.isArray(options.src) ? options.src : [options.src]
731
771
  return values.map(value => {
732
- const item = typeof value === 'string' ? { path: value } : value;
733
- if (!item.path) throw new Error('next/font/local src entries require a path.');
734
- const file = path.resolve(path.dirname(callerFile), item.path);
735
- if (!existsSync(file)) throw new Error(`Font file not found: ${item.path}`);
736
- return { file, style: item.style, weight: item.weight };
737
- });
772
+ const item = typeof value === 'string' ? { path: value } : value
773
+ if (!item.path) throw new Error('next/font/local src entries require a path.')
774
+ const file = path.resolve(path.dirname(callerFile), item.path)
775
+ if (!existsSync(file)) throw new Error(`Font file not found: ${item.path}`)
776
+ return { file, style: item.style, weight: item.weight }
777
+ })
738
778
  }
739
779
 
740
780
  function inferLocalFontCallerFile(): string | undefined {
741
- const stack = new Error().stack;
742
- if (!stack) return undefined;
781
+ const stack = new Error().stack
782
+ if (!stack) return undefined
743
783
  for (const line of stack.split('\n')) {
744
- const file = stackFrameFile(line);
745
- if (!file) continue;
746
- if (file.replace(/\\/g, '/').includes('/src/compat/next/font/')) continue;
747
- return originalSourceFile(file);
784
+ const file = stackFrameFile(line)
785
+ if (!file) continue
786
+ if (file.replace(/\\/g, '/').includes('/src/compat/next/font/')) continue
787
+ return originalSourceFile(file)
748
788
  }
749
- return undefined;
789
+ return undefined
750
790
  }
751
791
 
752
792
  function stackFrameFile(line: string) {
753
- const match = /\(?((?:file:\/\/)?\/[^():]+\.[cm]?[jt]sx?|(?:file:\/\/)?\/[^():]+\.mjs):\d+:\d+\)?/.exec(line);
754
- const file = match?.[1];
755
- if (!file) return undefined;
756
- return file.startsWith('file://') ? fileURLToPath(file) : file;
793
+ const match =
794
+ /\(?((?:file:\/\/)?\/[^():]+\.[cm]?[jt]sx?|(?:file:\/\/)?\/[^():]+\.mjs):\d+:\d+\)?/.exec(line)
795
+ const file = match?.[1]
796
+ if (!file) return undefined
797
+ return file.startsWith('file://') ? fileURLToPath(file) : file
757
798
  }
758
799
 
759
800
  function originalSourceFile(file: string) {
760
- const source = sourceComment(file);
761
- if (!source) return file;
762
- const normalized = source.replace(/\\/g, '/');
763
- if (normalized.startsWith('file://')) return fileURLToPath(normalized);
764
- if (path.isAbsolute(normalized)) return existsSync(normalized) ? normalized : file;
765
- const nodeModulesIndex = normalized.indexOf('node_modules/');
766
- const relative = nodeModulesIndex === -1 ? normalized : normalized.slice(nodeModulesIndex);
767
- const candidate = path.resolve(compiledProjectRoot(file), relative);
768
- return existsSync(candidate) ? candidate : file;
801
+ const source = sourceComment(file)
802
+ if (!source) return file
803
+ const normalized = source.replace(/\\/g, '/')
804
+ if (normalized.startsWith('file://')) return fileURLToPath(normalized)
805
+ if (path.isAbsolute(normalized)) return existsSync(normalized) ? normalized : file
806
+ const nodeModulesIndex = normalized.indexOf('node_modules/')
807
+ const relative = nodeModulesIndex === -1 ? normalized : normalized.slice(nodeModulesIndex)
808
+ const candidate = path.resolve(compiledProjectRoot(file), relative)
809
+ return existsSync(candidate) ? candidate : file
769
810
  }
770
811
 
771
812
  function sourceComment(file: string) {
772
- if (!existsSync(file)) return undefined;
773
- const firstLine = readFileSync(file, 'utf8').split(/\r?\n/, 1)[0]?.trim();
774
- return /^\/\/\s+(.+)$/.exec(firstLine ?? '')?.[1];
813
+ if (!existsSync(file)) return undefined
814
+ const firstLine = readFileSync(file, 'utf8').split(/\r?\n/, 1)[0]?.trim()
815
+ return /^\/\/\s+(.+)$/.exec(firstLine ?? '')?.[1]
775
816
  }
776
817
 
777
818
  function compiledProjectRoot(file: string) {
778
- const marker = `${path.sep}.pnext${path.sep}`;
779
- const index = file.indexOf(marker);
780
- return index === -1 ? process.cwd() : file.slice(0, index);
819
+ const marker = `${path.sep}.pnext${path.sep}`
820
+ const index = file.indexOf(marker)
821
+ return index === -1 ? process.cwd() : file.slice(0, index)
781
822
  }
782
823
 
783
824
  function googleMetadata() {
784
825
  if (!googleMetadataCache) {
785
- googleMetadataCache = loadGoogleMetadata();
826
+ googleMetadataCache = loadGoogleMetadata()
786
827
  }
787
- return googleMetadataCache;
828
+ return googleMetadataCache
788
829
  }
789
830
 
790
831
  function loadGoogleMetadata(): Record<string, GoogleFontMetadata> {
791
- const override = process.env.PNEXT_FONT_GOOGLE_METADATA;
832
+ const override = process.env.PNEXT_FONT_GOOGLE_METADATA
792
833
  if (override) {
793
- const metadata = readGoogleMetadataFile(override);
794
- if (metadata) return metadata;
795
- throw new Error(`Invalid Google font metadata file: ${override}`);
834
+ const metadata = readGoogleMetadataFile(override)
835
+ if (metadata) return metadata
836
+ throw new Error(`Invalid Google font metadata file: ${override}`)
796
837
  }
797
838
 
798
839
  const metadata =
799
- tryLoadGoogleMetadata('next-font/google/font-data.json') ??
800
- tryLoadGoogleMetadata('next-font');
801
- if (metadata) return metadata;
840
+ tryLoadGoogleMetadata('next-font/google/font-data.json') ?? tryLoadGoogleMetadata('next-font')
841
+ if (metadata) return metadata
802
842
 
803
- throw new Error(
804
- 'next/font/google requires Google font metadata. Install `next-font`.',
805
- );
843
+ throw new Error('next/font/google requires Google font metadata. Install `next-font`.')
806
844
  }
807
845
 
808
846
  function tryLoadGoogleMetadata(specifier: string): Record<string, GoogleFontMetadata> | undefined {
809
847
  try {
810
- const loaded = require(specifier) as {
811
- default?: unknown;
812
- googleFontsMetadata?: unknown;
813
- } | Record<string, GoogleFontMetadata>;
848
+ const loaded = require(specifier) as
849
+ | {
850
+ default?: unknown
851
+ googleFontsMetadata?: unknown
852
+ }
853
+ | Record<string, GoogleFontMetadata>
814
854
  const metadata =
815
855
  'googleFontsMetadata' in loaded
816
856
  ? loaded.googleFontsMetadata
817
857
  : 'default' in loaded
818
858
  ? loaded.default
819
- : loaded;
820
- return isGoogleMetadata(metadata) ? metadata : undefined;
859
+ : loaded
860
+ return isGoogleMetadata(metadata) ? metadata : undefined
821
861
  } catch {
822
- return undefined;
862
+ return undefined
823
863
  }
824
864
  }
825
865
 
826
866
  function readGoogleMetadataFile(file: string): Record<string, GoogleFontMetadata> | undefined {
827
- const metadata: unknown = JSON.parse(readFileSync(file, 'utf8'));
828
- return isGoogleMetadata(metadata) ? metadata : undefined;
867
+ const metadata: unknown = JSON.parse(readFileSync(file, 'utf8'))
868
+ return isGoogleMetadata(metadata) ? metadata : undefined
829
869
  }
830
870
 
831
871
  function isGoogleMetadata(value: unknown): value is Record<string, GoogleFontMetadata> {
832
- if (!value || typeof value !== 'object') return false;
833
- const inter = (value as Record<string, GoogleFontMetadata>).Inter;
872
+ if (!value || typeof value !== 'object') return false
873
+ const inter = (value as Record<string, GoogleFontMetadata>).Inter
834
874
  return Boolean(
835
875
  inter &&
836
876
  Array.isArray(inter.weights) &&
837
877
  Array.isArray(inter.styles) &&
838
878
  Array.isArray(inter.subsets),
839
- );
879
+ )
840
880
  }
841
881
 
842
882
  function mockedGoogleResponse(url: string) {
843
- const mockFile = process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES;
844
- if (!mockFile) return undefined;
845
- const mocked = require(mockFile) as Record<string, string>;
846
- const value = mocked[url];
847
- if (value === undefined) throw new Error(`Missing mocked response for URL: ${url}`);
883
+ const mockFile = process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES
884
+ if (!mockFile) return undefined
885
+ const mocked = require(mockFile) as Record<string, string>
886
+ const value = mocked[url]
887
+ if (value === undefined) throw new Error(`Missing mocked response for URL: ${url}`)
848
888
  if (typeof value === 'string' && value.startsWith('/') && existsSync(value)) {
849
- return new Uint8Array(readFileSync(value));
889
+ return new Uint8Array(readFileSync(value))
850
890
  }
851
- return value;
891
+ return value
852
892
  }
853
893
 
854
894
  function rewriteFontFamily(css: string, originalFamily: string, scopedFamily: string) {
855
- const familyPattern = escapeRegex(originalFamily);
895
+ const familyPattern = escapeRegex(originalFamily)
856
896
  return css.replace(
857
897
  new RegExp(`font-family:\\s*(["']?)${familyPattern}\\1`, 'g'),
858
898
  `font-family:${singleQuoteFamily(scopedFamily)}`,
859
- );
899
+ )
860
900
  }
861
901
 
862
902
  /** Metrics-adjusted fallback `@font-face` (`'<family> Fallback'`). We omit the
863
903
  * ascent/descent/size-adjust metrics (would need fontkit); the local() source
864
904
  * never loads, so the browser resolves the real font or the next stack entry. */
865
905
  function fallbackFaceCss(family: string) {
866
- return `@font-face{font-family:${singleQuoteFamily(`${family} Fallback`)};src:local("Arial");}`;
906
+ return `@font-face{font-family:${singleQuoteFamily(`${family} Fallback`)};src:local("Arial");}`
867
907
  }
868
908
 
869
909
  function classRule(className: string, familyStack: string, weight?: string, style?: string) {
@@ -872,103 +912,98 @@ function classRule(className: string, familyStack: string, weight?: string, styl
872
912
  weight && !isRangeValue(weight) ? `font-weight:${cssIdentifier(weight)};` : '',
873
913
  style && !isRangeValue(style) ? `font-style:${cssIdentifier(style)};` : '',
874
914
  '}',
875
- ].join('');
915
+ ].join('')
876
916
  }
877
917
 
878
918
  function variableRule(className: string, variable: string, familyStack: string) {
879
- return `.${className}{${variable}:${familyStack};}`;
919
+ return `.${className}{${variable}:${familyStack};}`
880
920
  }
881
921
 
882
922
  function fontPreload(url: string, type: string): MetadataLink {
883
- return { rel: 'preload', url, as: 'font', type, crossOrigin: '' };
923
+ return { rel: 'preload', url, as: 'font', type, crossOrigin: '' }
884
924
  }
885
925
 
886
926
  /** Single `<link rel="preconnect" href="/" crossorigin="">` for the
887
927
  * `preload:false` (no preloaded fonts) case. */
888
928
  function fontPreconnect(): MetadataLink {
889
- return { rel: 'preconnect', url: '/', crossOrigin: '' };
929
+ return { rel: 'preconnect', url: '/', crossOrigin: '' }
890
930
  }
891
931
 
892
932
  function dedupePreloads(preloads: MetadataLink[]) {
893
- const seen = new Set<string>();
933
+ const seen = new Set<string>()
894
934
  return preloads.filter(preload => {
895
- if (seen.has(preload.url)) return false;
896
- seen.add(preload.url);
897
- return true;
898
- });
935
+ if (seen.has(preload.url)) return false
936
+ seen.add(preload.url)
937
+ return true
938
+ })
899
939
  }
900
940
 
901
941
  function validateDisplay(value: FontDisplay) {
902
- if (!allowedDisplayValues.has(value)) throw new Error(`Invalid font display value \`${value}\`.`);
903
- return value;
942
+ if (!allowedDisplayValues.has(value)) throw new Error(`Invalid font display value \`${value}\`.`)
943
+ return value
904
944
  }
905
945
 
906
946
  function fontExt(value: string) {
907
- return /\.(woff2?|eot|ttf|otf)(?:[?#]|$)/.exec(value)?.[1] ?? 'woff2';
947
+ return /\.(woff2?|eot|ttf|otf)(?:[?#]|$)/.exec(value)?.[1] ?? 'woff2'
908
948
  }
909
949
 
910
950
  function fontMimeType(ext: string) {
911
- if (ext === 'woff') return 'font/woff';
912
- if (ext === 'ttf') return 'font/ttf';
913
- if (ext === 'otf') return 'font/otf';
914
- if (ext === 'eot') return 'application/vnd.ms-fontobject';
915
- return 'font/woff2';
951
+ if (ext === 'woff') return 'font/woff'
952
+ if (ext === 'ttf') return 'font/ttf'
953
+ if (ext === 'otf') return 'font/otf'
954
+ if (ext === 'eot') return 'application/vnd.ms-fontobject'
955
+ return 'font/woff2'
916
956
  }
917
957
 
918
958
  function fontFormat(ext: string) {
919
- if (ext === 'ttf') return 'truetype';
920
- if (ext === 'otf') return 'opentype';
921
- return ext;
959
+ if (ext === 'ttf') return 'truetype'
960
+ if (ext === 'otf') return 'opentype'
961
+ return ext
922
962
  }
923
963
 
924
964
  function singleCssValue(values: string[]) {
925
- return values.length === 1 ? values[0] : undefined;
965
+ return values.length === 1 ? values[0] : undefined
926
966
  }
927
967
 
928
968
  function isRangeValue(value: string) {
929
- return value.trim().includes(' ');
969
+ return value.trim().includes(' ')
930
970
  }
931
971
 
932
972
  function arrayValue<T>(value: T | T[]) {
933
- return Array.isArray(value) ? value : [value];
973
+ return Array.isArray(value) ? value : [value]
934
974
  }
935
975
 
936
976
  function uniqueArray<T>(items: T[]) {
937
- return [...new Set(items)];
977
+ return [...new Set(items)]
938
978
  }
939
979
 
940
-
941
980
  function customProperty(value: string) {
942
- if (!/^--[-_a-zA-Z0-9]+$/.test(value)) throw new Error(`Invalid font variable name: ${value}`);
943
- return value;
981
+ if (!/^--[-_a-zA-Z0-9]+$/.test(value)) throw new Error(`Invalid font variable name: ${value}`)
982
+ return value
944
983
  }
945
984
 
946
985
  function cssIdentifier(value: string) {
947
- if (!/^[-_a-zA-Z0-9. ]+$/.test(value)) throw new Error(`Invalid font option value: ${value}`);
948
- return value;
986
+ if (!/^[-_a-zA-Z0-9. ]+$/.test(value)) throw new Error(`Invalid font option value: ${value}`)
987
+ return value
949
988
  }
950
989
 
951
990
  function cssProperty(value: string) {
952
- if (!/^[-_a-zA-Z0-9]+$/.test(value)) throw new Error(`Invalid CSS property: ${value}`);
953
- return value;
991
+ if (!/^[-_a-zA-Z0-9]+$/.test(value)) throw new Error(`Invalid CSS property: ${value}`)
992
+ return value
954
993
  }
955
994
 
956
995
  function cssDeclarationValue(value: string) {
957
- if (/[;{}]/.test(value)) throw new Error(`Invalid CSS declaration value: ${value}`);
958
- return value;
996
+ if (/[;{}]/.test(value)) throw new Error(`Invalid CSS declaration value: ${value}`)
997
+ return value
959
998
  }
960
999
 
961
1000
  function sortFontVariantValues(a: string, b: string) {
962
1001
  if (a.includes(',') && b.includes(',')) {
963
- const [aPrefix, aSuffix] = a.split(',', 2);
964
- const [bPrefix, bSuffix] = b.split(',', 2);
1002
+ const [aPrefix, aSuffix] = a.split(',', 2)
1003
+ const [bPrefix, bSuffix] = b.split(',', 2)
965
1004
  return aPrefix === bPrefix
966
1005
  ? parseInt(aSuffix ?? '0', 10) - parseInt(bSuffix ?? '0', 10)
967
- : parseInt(aPrefix ?? '0', 10) - parseInt(bPrefix ?? '0', 10);
1006
+ : parseInt(aPrefix ?? '0', 10) - parseInt(bPrefix ?? '0', 10)
968
1007
  }
969
- return parseInt(a, 10) - parseInt(b, 10);
970
- }
971
-
972
- function escapeRegex(value: string) {
973
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1008
+ return parseInt(a, 10) - parseInt(b, 10)
974
1009
  }