@wular/pnext 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (371) hide show
  1. package/README.md +153 -0
  2. package/bin/pnext +67 -0
  3. package/config/lint/base.js +48 -0
  4. package/config/ts/base.json +26 -0
  5. package/config/ts/react.json +13 -0
  6. package/package.json +70 -0
  7. package/reference/compat.md +63 -0
  8. package/reference/config.md +120 -0
  9. package/reference/css.md +58 -0
  10. package/reference/dev.md +69 -0
  11. package/reference/env.md +40 -0
  12. package/reference/metadata.md +86 -0
  13. package/reference/navigation.md +149 -0
  14. package/reference/overview.md +35 -0
  15. package/reference/performance.md +97 -0
  16. package/reference/rendering.md +127 -0
  17. package/reference/routing.md +167 -0
  18. package/reference/typegen.md +64 -0
  19. package/src/api/cache.ts +80 -0
  20. package/src/api/client-cache.ts +9 -0
  21. package/src/api/client-navigation.ts +279 -0
  22. package/src/api/dynamic.tsx +102 -0
  23. package/src/api/link.tsx +119 -0
  24. package/src/api/navigation.ts +198 -0
  25. package/src/api/router/events.ts +53 -0
  26. package/src/api/router/history.ts +70 -0
  27. package/src/api/router/hub.ts +194 -0
  28. package/src/api/router/policies.ts +107 -0
  29. package/src/api/router/runtime.ts +5238 -0
  30. package/src/api/router/types.ts +299 -0
  31. package/src/api/router.ts +167 -0
  32. package/src/api/server.ts +323 -0
  33. package/src/api/suspense.ts +16 -0
  34. package/src/cache/context.ts +61 -0
  35. package/src/cli/adapters/vercel-warm.ts +375 -0
  36. package/src/cli/adapters/vercel.ts +1310 -0
  37. package/src/cli/analyze-print.ts +181 -0
  38. package/src/cli/analyze.ts +328 -0
  39. package/src/cli/boot-trace.ts +29 -0
  40. package/src/cli/build.ts +3114 -0
  41. package/src/cli/dev.ts +276 -0
  42. package/src/cli/index.ts +196 -0
  43. package/src/cli/named-bin.ts +119 -0
  44. package/src/cli/serve-ui.ts +160 -0
  45. package/src/cli/start.ts +1425 -0
  46. package/src/client/build.ts +2136 -0
  47. package/src/client/chunk-fold.ts +526 -0
  48. package/src/client/entry.ts +1525 -0
  49. package/src/client/paths.ts +22 -0
  50. package/src/client/prebuilt.ts +621 -0
  51. package/src/client/profile.ts +75 -0
  52. package/src/client/react-compiler.ts +94 -0
  53. package/src/client/reference-stub.ts +145 -0
  54. package/src/client/reference.ts +47 -0
  55. package/src/compat/actions/action-client.ts +531 -0
  56. package/src/compat/actions/action-dispatch.ts +676 -0
  57. package/src/compat/actions/action-router.ts +40 -0
  58. package/src/compat/actions/action-shared.ts +95 -0
  59. package/src/compat/actions/client-plugin.ts +135 -0
  60. package/src/compat/actions/client-stub.ts +65 -0
  61. package/src/compat/actions/config.ts +164 -0
  62. package/src/compat/actions/detect.ts +208 -0
  63. package/src/compat/actions/discovery.ts +244 -0
  64. package/src/compat/actions/early-submit.ts +40 -0
  65. package/src/compat/actions/endpoint.ts +602 -0
  66. package/src/compat/actions/flight.ts +52 -0
  67. package/src/compat/actions/form-state.ts +73 -0
  68. package/src/compat/actions/hoist.ts +485 -0
  69. package/src/compat/actions/ids.ts +39 -0
  70. package/src/compat/actions/index.ts +41 -0
  71. package/src/compat/actions/instances.ts +144 -0
  72. package/src/compat/actions/origin.ts +109 -0
  73. package/src/compat/actions/protocol.ts +125 -0
  74. package/src/compat/actions/registry.ts +74 -0
  75. package/src/compat/actions/rewrite.ts +282 -0
  76. package/src/compat/actions/serve.ts +414 -0
  77. package/src/compat/actions/server-tag.ts +21 -0
  78. package/src/compat/actions/unrecognized-error.ts +30 -0
  79. package/src/compat/adapter/build-complete.ts +257 -0
  80. package/src/compat/bundler/bun-externals.ts +53 -0
  81. package/src/compat/bundler/cjs-exports.ts +542 -0
  82. package/src/compat/bundler/config.ts +363 -0
  83. package/src/compat/bundler/externals.ts +34 -0
  84. package/src/compat/bundler/import-meta-url.ts +60 -0
  85. package/src/compat/bundler/modularize-imports.ts +119 -0
  86. package/src/compat/bundler/new-url-asset.ts +87 -0
  87. package/src/compat/bundler/optimize-package-imports.ts +273 -0
  88. package/src/compat/bundler/polyfill.ts +88 -0
  89. package/src/compat/bundler/react-compiler.ts +61 -0
  90. package/src/compat/bundler/react-profiler.tsx +25 -0
  91. package/src/compat/bundler/relay-transform.ts +116 -0
  92. package/src/compat/bundler/require-context.ts +281 -0
  93. package/src/compat/bundler/resolve-extensions.ts +75 -0
  94. package/src/compat/bundler/source-cache.ts +61 -0
  95. package/src/compat/bundler/static-imports.ts +25 -0
  96. package/src/compat/bundler/symlink-imports.ts +119 -0
  97. package/src/compat/bundler/tsconfig-paths.ts +50 -0
  98. package/src/compat/bundler/wasm.ts +153 -0
  99. package/src/compat/bundler/webpack-loaders.ts +685 -0
  100. package/src/compat/bundler/worker.ts +278 -0
  101. package/src/compat/cache/build-flags.ts +81 -0
  102. package/src/compat/cache/build-prerender-errors.ts +163 -0
  103. package/src/compat/cache/custom-handler.ts +159 -0
  104. package/src/compat/cache/fetch-patch.ts +745 -0
  105. package/src/compat/cache/handler.ts +100 -0
  106. package/src/compat/cache/modern-handler.ts +275 -0
  107. package/src/compat/cache/resume-data-cache.ts +143 -0
  108. package/src/compat/cache/revalidate.ts +759 -0
  109. package/src/compat/cache/runtime-error.ts +124 -0
  110. package/src/compat/cache/use-cache-transform.ts +961 -0
  111. package/src/compat/cache/use-cache.ts +1695 -0
  112. package/src/compat/cache-control.ts +269 -0
  113. package/src/compat/client/base-path.ts +64 -0
  114. package/src/compat/client/css-order.ts +36 -0
  115. package/src/compat/client/errors/control-flow.ts +92 -0
  116. package/src/compat/client/errors/error-boundary.ts +222 -0
  117. package/src/compat/client/errors/global-error.ts +238 -0
  118. package/src/compat/client/errors/install.ts +217 -0
  119. package/src/compat/client/errors/lazy.ts +53 -0
  120. package/src/compat/client/errors/primitive-throw.ts +126 -0
  121. package/src/compat/client/errors/soft-refresh.ts +14 -0
  122. package/src/compat/client/link-status.ts +86 -0
  123. package/src/compat/client/nav-compat-runtime.ts +57 -0
  124. package/src/compat/client/nav-compat.ts +42 -0
  125. package/src/compat/client/navigation-scroll.ts +154 -0
  126. package/src/compat/client/optimistic-routing.ts +206 -0
  127. package/src/compat/client/prefetch-cache.ts +111 -0
  128. package/src/compat/client/route-announcer.ts +72 -0
  129. package/src/compat/client/segment-cache-policy.ts +159 -0
  130. package/src/compat/client/segment-cache.ts +1077 -0
  131. package/src/compat/client/segment-prefetch.ts +375 -0
  132. package/src/compat/client/trailing-slash.ts +24 -0
  133. package/src/compat/css/chunking.ts +254 -0
  134. package/src/compat/css/inline-css.ts +73 -0
  135. package/src/compat/css/lightningcss.ts +90 -0
  136. package/src/compat/css/modules.ts +373 -0
  137. package/src/compat/css/nonce.ts +30 -0
  138. package/src/compat/css/sass-plugin.ts +65 -0
  139. package/src/compat/css/sass.ts +392 -0
  140. package/src/compat/css/styled-jsx-runtime.ts +80 -0
  141. package/src/compat/css/styled-jsx.ts +49 -0
  142. package/src/compat/edge-runtime.ts +71 -0
  143. package/src/compat/export/client.ts +112 -0
  144. package/src/compat/export/index.ts +272 -0
  145. package/src/compat/export/standalone.ts +207 -0
  146. package/src/compat/image-optimizer/cache.ts +119 -0
  147. package/src/compat/image-optimizer/detect.ts +143 -0
  148. package/src/compat/image-optimizer/index.ts +601 -0
  149. package/src/compat/image-optimizer/source.ts +243 -0
  150. package/src/compat/index.ts +458 -0
  151. package/src/compat/lifecycle/after-scope.ts +86 -0
  152. package/src/compat/lifecycle/after.ts +173 -0
  153. package/src/compat/lifecycle/error-funnel.ts +306 -0
  154. package/src/compat/lifecycle/error-serialize.ts +87 -0
  155. package/src/compat/lifecycle/error-ui.ts +167 -0
  156. package/src/compat/lifecycle/instrumentation-client.ts +138 -0
  157. package/src/compat/lifecycle/instrumentation.ts +277 -0
  158. package/src/compat/lifecycle/node-console.ts +19 -0
  159. package/src/compat/lifecycle/testmode.ts +263 -0
  160. package/src/compat/mdx/compile.ts +219 -0
  161. package/src/compat/mdx/next-mdx-stub.ts +46 -0
  162. package/src/compat/mdx/plugin.ts +37 -0
  163. package/src/compat/metadata-route-artifacts.ts +458 -0
  164. package/src/compat/metadata.ts +295 -0
  165. package/src/compat/middleware/manifest.ts +210 -0
  166. package/src/compat/misc/action-return.ts +173 -0
  167. package/src/compat/next/cache.ts +211 -0
  168. package/src/compat/next/canonical-url.ts +35 -0
  169. package/src/compat/next/client-cache.ts +57 -0
  170. package/src/compat/next/client-navigation.ts +313 -0
  171. package/src/compat/next/client-only.ts +3 -0
  172. package/src/compat/next/client-script.tsx +215 -0
  173. package/src/compat/next/client-server.ts +39 -0
  174. package/src/compat/next/config-loader.ts +569 -0
  175. package/src/compat/next/config.ts +29 -0
  176. package/src/compat/next/constants.cjs +6 -0
  177. package/src/compat/next/constants.ts +6 -0
  178. package/src/compat/next/custom-server.ts +236 -0
  179. package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
  180. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
  181. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
  182. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
  183. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
  184. package/src/compat/next/dynamic.tsx +46 -0
  185. package/src/compat/next/error.tsx +148 -0
  186. package/src/compat/next/font/cache.ts +171 -0
  187. package/src/compat/next/font/google.ts +2 -0
  188. package/src/compat/next/font/index.ts +8 -0
  189. package/src/compat/next/font/local.ts +5 -0
  190. package/src/compat/next/font/runtime-client.ts +71 -0
  191. package/src/compat/next/font/runtime.ts +974 -0
  192. package/src/compat/next/font/shared.ts +281 -0
  193. package/src/compat/next/form.tsx +156 -0
  194. package/src/compat/next/head.tsx +10 -0
  195. package/src/compat/next/headers.ts +247 -0
  196. package/src/compat/next/image/config.ts +196 -0
  197. package/src/compat/next/image/optimizer.ts +96 -0
  198. package/src/compat/next/image/patterns.ts +103 -0
  199. package/src/compat/next/image/shared.ts +141 -0
  200. package/src/compat/next/image/static-metadata.ts +283 -0
  201. package/src/compat/next/image/validate.ts +269 -0
  202. package/src/compat/next/image-client.tsx +215 -0
  203. package/src/compat/next/image-props.ts +575 -0
  204. package/src/compat/next/image-usage.ts +102 -0
  205. package/src/compat/next/image.tsx +56 -0
  206. package/src/compat/next/index.ts +1 -0
  207. package/src/compat/next/legacy-image.tsx +97 -0
  208. package/src/compat/next/link-usage.ts +29 -0
  209. package/src/compat/next/link-validation-transform.ts +200 -0
  210. package/src/compat/next/link.tsx +466 -0
  211. package/src/compat/next/navigation.cjs +21 -0
  212. package/src/compat/next/navigation.ts +188 -0
  213. package/src/compat/next/offline.ts +51 -0
  214. package/src/compat/next/og.ts +324 -0
  215. package/src/compat/next/optimistic-route-state.ts +188 -0
  216. package/src/compat/next/preferred-region.ts +39 -0
  217. package/src/compat/next/redirects.ts +131 -0
  218. package/src/compat/next/resource-hints.ts +136 -0
  219. package/src/compat/next/rewrites.ts +350 -0
  220. package/src/compat/next/root-params.ts +142 -0
  221. package/src/compat/next/router.cjs +49 -0
  222. package/src/compat/next/router.ts +143 -0
  223. package/src/compat/next/script.tsx +355 -0
  224. package/src/compat/next/server-only.ts +3 -0
  225. package/src/compat/next/server.ts +28 -0
  226. package/src/compat/next/svgr.ts +58 -0
  227. package/src/compat/next/telemetry.ts +77 -0
  228. package/src/compat/next/user-agent.ts +100 -0
  229. package/src/compat/next/web-vitals.ts +56 -0
  230. package/src/compat/otel/api.ts +95 -0
  231. package/src/compat/otel/client-trace-metadata.ts +71 -0
  232. package/src/compat/otel/fetch-span.ts +77 -0
  233. package/src/compat/otel/tracer.ts +944 -0
  234. package/src/compat/pages/client-plugin.ts +108 -0
  235. package/src/compat/pages/index.ts +527 -0
  236. package/src/compat/pages/router-state.ts +94 -0
  237. package/src/compat/ppr/io.ts +38 -0
  238. package/src/compat/ppr/missing-root-params.ts +105 -0
  239. package/src/compat/ppr/root-params-scan.ts +164 -0
  240. package/src/compat/ppr/root-params-transform.ts +75 -0
  241. package/src/compat/ppr/root-params.ts +129 -0
  242. package/src/compat/ppr/segment-config-incompat.ts +34 -0
  243. package/src/compat/protocol.ts +202 -0
  244. package/src/compat/react/client.ts +59 -0
  245. package/src/compat/react/compiler-runtime.ts +60 -0
  246. package/src/compat/react/dom-client.ts +115 -0
  247. package/src/compat/react/dom-react-server.ts +20 -0
  248. package/src/compat/react/dom-server.ts +40 -0
  249. package/src/compat/react/dom.ts +154 -0
  250. package/src/compat/react/preact.ts +522 -0
  251. package/src/compat/react/react-server.ts +84 -0
  252. package/src/compat/react/router-shim.ts +26 -0
  253. package/src/compat/react/server-component-use.ts +48 -0
  254. package/src/compat/react/server-inserted-html.ts +87 -0
  255. package/src/compat/react/server.ts +156 -0
  256. package/src/compat/react/view-transition.ts +60 -0
  257. package/src/compat/register/actions.ts +875 -0
  258. package/src/compat/register/boot.ts +141 -0
  259. package/src/compat/register/build-tier.ts +11 -0
  260. package/src/compat/register/build.ts +182 -0
  261. package/src/compat/register/bundler.ts +587 -0
  262. package/src/compat/register/cache.ts +85 -0
  263. package/src/compat/register/client-errors.ts +18 -0
  264. package/src/compat/register/config.ts +17 -0
  265. package/src/compat/register/css-extras.ts +120 -0
  266. package/src/compat/register/edge-runtime.ts +6 -0
  267. package/src/compat/register/errors.ts +46 -0
  268. package/src/compat/register/export.ts +23 -0
  269. package/src/compat/register/font.ts +36 -0
  270. package/src/compat/register/hooks.ts +34 -0
  271. package/src/compat/register/image.ts +133 -0
  272. package/src/compat/register/index.ts +111 -0
  273. package/src/compat/register/instrumentation-client.ts +35 -0
  274. package/src/compat/register/lifecycle.ts +86 -0
  275. package/src/compat/register/mdx.ts +48 -0
  276. package/src/compat/register/middleware.ts +36 -0
  277. package/src/compat/register/misc.ts +44 -0
  278. package/src/compat/register/otel.ts +288 -0
  279. package/src/compat/register/pages-api.ts +473 -0
  280. package/src/compat/register/ppr.ts +56 -0
  281. package/src/compat/register/protocol.ts +57 -0
  282. package/src/compat/register/proxy.ts +127 -0
  283. package/src/compat/register/render.ts +268 -0
  284. package/src/compat/register/routing.ts +410 -0
  285. package/src/compat/register/segment.ts +1903 -0
  286. package/src/compat/register/static-image.ts +21 -0
  287. package/src/compat/register/typed-routes.ts +35 -0
  288. package/src/compat/register/usecache.ts +131 -0
  289. package/src/compat/register/validation.ts +56 -0
  290. package/src/compat/segment/loading-boundary.ts +113 -0
  291. package/src/compat/segment/page-slot.ts +200 -0
  292. package/src/compat/segment/tree.ts +481 -0
  293. package/src/compat/segment/vary-key.ts +102 -0
  294. package/src/compat/segment/vary-params.ts +551 -0
  295. package/src/compat/static-params.ts +33 -0
  296. package/src/compat/tsconfig-defaults.ts +301 -0
  297. package/src/compat/typecheck/index.ts +1481 -0
  298. package/src/compat/typecheck/worker.ts +26 -0
  299. package/src/compat/typed-routes/index.ts +92 -0
  300. package/src/compat/typed-routes/manifest.ts +356 -0
  301. package/src/compat/typed-routes/typegen.ts +566 -0
  302. package/src/compat/validation/errors.ts +159 -0
  303. package/src/compat/validation/index.ts +1770 -0
  304. package/src/compat/validation/prerender-diagnostics.ts +1508 -0
  305. package/src/compat-bootstrap.ts +67 -0
  306. package/src/config.ts +218 -0
  307. package/src/css/build.ts +697 -0
  308. package/src/css/index.ts +2 -0
  309. package/src/css/postcss.ts +236 -0
  310. package/src/css/worker.ts +34 -0
  311. package/src/dev/client-actions.ts +35 -0
  312. package/src/dev/client-chunk-store.ts +92 -0
  313. package/src/dev/client-key-cache.ts +178 -0
  314. package/src/dev/global-css-cache.ts +212 -0
  315. package/src/dev/imports.ts +2430 -0
  316. package/src/dev/module-cache.ts +721 -0
  317. package/src/dev/module-generations.ts +38 -0
  318. package/src/dev/module-transform.ts +188 -0
  319. package/src/dev/node-module-bundle-cache.ts +63 -0
  320. package/src/dev/restart-cache.ts +10 -0
  321. package/src/dev/route-bundle-key-cache.ts +154 -0
  322. package/src/dev/route-facts-cache.ts +223 -0
  323. package/src/dev/server.ts +1710 -0
  324. package/src/dynamic/source.ts +307 -0
  325. package/src/dynamic/tree-shake.ts +262 -0
  326. package/src/env.ts +92 -0
  327. package/src/extensions.ts +1898 -0
  328. package/src/index.ts +34 -0
  329. package/src/internal.ts +43 -0
  330. package/src/islands/boundary-error.ts +8 -0
  331. package/src/islands/static-children.ts +37 -0
  332. package/src/islands/static-slots.ts +106 -0
  333. package/src/ppr-postpone.ts +24 -0
  334. package/src/ppr.ts +784 -0
  335. package/src/proxy.ts +752 -0
  336. package/src/render/hooks.ts +384 -0
  337. package/src/render/index.ts +1 -0
  338. package/src/render/island-context.ts +47 -0
  339. package/src/render/metadata.ts +857 -0
  340. package/src/render/renderer.ts +7391 -0
  341. package/src/render/resource-hints.ts +44 -0
  342. package/src/render/slots.tsx +679 -0
  343. package/src/request/context.ts +396 -0
  344. package/src/resolve/engine.ts +219 -0
  345. package/src/resolve/imports.ts +1104 -0
  346. package/src/resolve/scan-facts.ts +474 -0
  347. package/src/resolve/source-text.ts +86 -0
  348. package/src/routing/forwarded.ts +41 -0
  349. package/src/routing/handler.ts +271 -0
  350. package/src/routing/href.ts +203 -0
  351. package/src/routing/metadata.ts +1018 -0
  352. package/src/routing/request-runtime.ts +43 -0
  353. package/src/routing/routes.ts +2560 -0
  354. package/src/routing/slots.ts +432 -0
  355. package/src/runtime/server.ts +3453 -0
  356. package/src/runtime/vendor.ts +1160 -0
  357. package/src/style-modules.d.ts +9 -0
  358. package/src/typegen.ts +151 -0
  359. package/src/types.ts +725 -0
  360. package/src/utils/ansi.ts +9 -0
  361. package/src/utils/content-type.ts +31 -0
  362. package/src/utils/decode.ts +7 -0
  363. package/src/utils/dev-profile.ts +31 -0
  364. package/src/utils/error-log.ts +29 -0
  365. package/src/utils/fs-cache.ts +31 -0
  366. package/src/utils/fs.ts +119 -0
  367. package/src/utils/html.ts +46 -0
  368. package/src/utils/serialize.ts +378 -0
  369. package/src/utils/source.ts +35 -0
  370. package/src/utils/verbose.ts +39 -0
  371. package/tsconfig.json +10 -0
@@ -0,0 +1,575 @@
1
+ import type { JSX } from 'preact';
2
+
3
+ interface LocalPattern {
4
+ pathname?: string;
5
+ search?: string;
6
+ }
7
+
8
+ interface RemotePattern {
9
+ protocol?: string;
10
+ hostname?: string;
11
+ port?: string;
12
+ pathname?: string;
13
+ search?: string;
14
+ }
15
+
16
+ export interface ResolvedImagesConfig {
17
+ deviceSizes: number[];
18
+ imageSizes: number[];
19
+ allSizes: number[];
20
+ qualities: number[] | undefined;
21
+ formats: string[];
22
+ path: string;
23
+ loader: string;
24
+ loaderFile: string;
25
+ domains: string[];
26
+ disableStaticImages: boolean;
27
+ minimumCacheTTL: number;
28
+ maximumRedirects: number;
29
+ maximumResponseBody: number;
30
+ dangerouslyAllowLocalIP: boolean;
31
+ dangerouslyAllowSVG: boolean;
32
+ contentSecurityPolicy: string;
33
+ contentDispositionType: string;
34
+ localPatterns: LocalPattern[] | undefined;
35
+ remotePatterns: RemotePattern[];
36
+ unoptimized: boolean;
37
+ customCacheHandler: boolean;
38
+ }
39
+
40
+ declare global {
41
+ var __PNEXT_IMAGE_CONFIG__: ResolvedImagesConfig | undefined;
42
+ }
43
+
44
+ // Client-graph bundler defines (see compat/register/bundler.ts). The boolean
45
+ // twin gates the branch on a literal so DEFAULT_IMAGES_CONFIG is dropped from
46
+ // every client bundle — a `??` against an object define never folds.
47
+ declare const __PNEXT_IMAGE_CONFIG_INLINE__: ResolvedImagesConfig;
48
+ declare const __PNEXT_IMAGE_CONFIG_INLINED__: boolean;
49
+
50
+ const DEFAULT_IMAGES_CONFIG: ResolvedImagesConfig = {
51
+ deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
52
+ imageSizes: [32, 48, 64, 96, 128, 256, 384],
53
+ allSizes: [32, 48, 64, 96, 128, 256, 384, 640, 750, 828, 1080, 1200, 1920, 2048, 3840],
54
+ qualities: [75],
55
+ formats: ['image/webp'],
56
+ path: '/_next/image',
57
+ loader: 'default',
58
+ loaderFile: '',
59
+ domains: [],
60
+ disableStaticImages: false,
61
+ minimumCacheTTL: 14400,
62
+ maximumRedirects: 3,
63
+ maximumResponseBody: 50_000_000,
64
+ dangerouslyAllowLocalIP: false,
65
+ dangerouslyAllowSVG: false,
66
+ contentSecurityPolicy: `script-src 'none'; frame-src 'none'; sandbox;`,
67
+ contentDispositionType: 'attachment',
68
+ localPatterns: undefined,
69
+ remotePatterns: [],
70
+ unoptimized: false,
71
+ customCacheHandler: false,
72
+ };
73
+
74
+ export interface StaticImageData {
75
+ src: string;
76
+ width: number;
77
+ height: number;
78
+ blurDataURL?: string;
79
+ blurWidth?: number;
80
+ blurHeight?: number;
81
+ }
82
+
83
+ type StaticImport = StaticImageData | { default: StaticImageData };
84
+
85
+ interface LoaderProps {
86
+ src: string;
87
+ width: number;
88
+ quality?: number;
89
+ }
90
+
91
+ export type ImageLoader = (props: LoaderProps) => string;
92
+
93
+ export type ImageProps = Omit<
94
+ JSX.HTMLAttributes<HTMLImageElement>,
95
+ 'src' | 'srcset' | 'width' | 'height' | 'loading' | 'ref' | 'alt' | 'style'
96
+ > & {
97
+ src: string | StaticImport;
98
+ alt?: string;
99
+ width?: number | string;
100
+ height?: number | string;
101
+ fill?: boolean;
102
+ loader?: ImageLoader;
103
+ quality?: number | string;
104
+ priority?: boolean;
105
+ /** Preload the image without opting into the `priority` LCP warning gating. */
106
+ preload?: boolean;
107
+ loading?: 'lazy' | 'eager';
108
+ placeholder?: 'blur' | 'empty' | `data:image/${string}`;
109
+ blurDataURL?: string;
110
+ unoptimized?: boolean;
111
+ overrideSrc?: string;
112
+ sizes?: string;
113
+ fetchPriority?: 'high' | 'low' | 'auto';
114
+ decoding?: 'async' | 'auto' | 'sync';
115
+ style?: JSX.CSSProperties | string;
116
+ onLoad?: JSX.GenericEventHandler<HTMLImageElement>;
117
+ onError?: JSX.GenericEventHandler<HTMLImageElement>;
118
+ onLoadingComplete?: (img: HTMLImageElement) => void;
119
+ layout?: 'fill' | 'responsive' | 'intrinsic' | 'fixed';
120
+ objectFit?: string;
121
+ objectPosition?: string;
122
+ };
123
+
124
+ export interface ResolvedImageProps {
125
+ props: Record<string, unknown>;
126
+ meta: {
127
+ preload: boolean;
128
+ fill: boolean;
129
+ unoptimized: boolean;
130
+ placeholder: string;
131
+ imgAttributes: { src: string; srcSet?: string; sizes?: string };
132
+ /**
133
+ * Serialized style WITHOUT the placeholder background props - what the img should carry once loaded.
134
+ * Set only while a placeholder is applied; the server render stamps it as `data-nimg-ph` so the
135
+ * non-hydrated placeholder script can restore it on load.
136
+ */
137
+ loadedStyle?: string;
138
+ };
139
+ }
140
+
141
+ const INVALID_BACKGROUND_SIZE = new Set<string | undefined>([
142
+ '-moz-initial',
143
+ 'fill',
144
+ 'none',
145
+ 'scale-down',
146
+ undefined,
147
+ ]);
148
+
149
+ function unwrapStaticImport(src: StaticImport): StaticImageData {
150
+ return 'default' in src ? src.default : src;
151
+ }
152
+
153
+ function toNumber(value: number | string | undefined): number | undefined {
154
+ if (value === undefined) return undefined;
155
+ if (typeof value === 'number') return value;
156
+ const n = parseInt(value, 10);
157
+ return Number.isNaN(n) ? undefined : n;
158
+ }
159
+
160
+ // The client reads the inlined define, the server the global registerImageExtensions
161
+ // sets — both derive from the same resolver, so props cannot drift across hydration.
162
+ export function getImagesConfig(): ResolvedImagesConfig {
163
+ return typeof __PNEXT_IMAGE_CONFIG_INLINED__ === 'boolean'
164
+ ? __PNEXT_IMAGE_CONFIG_INLINE__
165
+ : (globalThis.__PNEXT_IMAGE_CONFIG__ ?? DEFAULT_IMAGES_CONFIG);
166
+ }
167
+
168
+ function defaultLoader(
169
+ config: ResolvedImagesConfig,
170
+ src: string,
171
+ width: number,
172
+ quality?: number,
173
+ ): string {
174
+ const q = findClosestQuality(config, quality);
175
+ return `${config.path}?url=${encodeURIComponent(src)}&w=${width}&q=${q}`;
176
+ }
177
+
178
+ export function findClosestQuality(config: ResolvedImagesConfig, quality?: number): number {
179
+ const qualities = config.qualities;
180
+ if (!qualities || qualities.length === 0) return quality ?? 75;
181
+ if (quality === undefined) return closest(qualities, 75);
182
+ return closest(qualities, quality);
183
+ }
184
+
185
+ function closest(values: number[], target: number): number {
186
+ let best = values[0]!;
187
+ let bestDelta = Math.abs(best - target);
188
+ for (const value of values) {
189
+ const delta = Math.abs(value - target);
190
+ if (delta < bestDelta) {
191
+ best = value;
192
+ bestDelta = delta;
193
+ }
194
+ }
195
+ return best;
196
+ }
197
+
198
+ interface GenImgAttrsResult {
199
+ src: string;
200
+ srcSet: string | undefined;
201
+ sizes: string | undefined;
202
+ }
203
+
204
+ function getWidths(
205
+ config: ResolvedImagesConfig,
206
+ width: number | undefined,
207
+ sizes: string | undefined,
208
+ ): { widths: number[]; kind: 'w' | 'x' } {
209
+ const { deviceSizes, allSizes } = config;
210
+ if (sizes) {
211
+ const viewportWidthRe = /(^|\s)(1?\d?\d)vw/g;
212
+ const percentSizes: number[] = [];
213
+ for (let match; (match = viewportWidthRe.exec(sizes)); ) {
214
+ percentSizes.push(parseInt(match[2]!, 10));
215
+ }
216
+ if (percentSizes.length) {
217
+ const smallestRatio = Math.min(...percentSizes) * 0.01;
218
+ return {
219
+ widths: allSizes.filter(s => s >= deviceSizes[0]! * smallestRatio),
220
+ kind: 'w',
221
+ };
222
+ }
223
+ return { widths: allSizes, kind: 'w' };
224
+ }
225
+ if (typeof width !== 'number') return { widths: deviceSizes, kind: 'w' };
226
+ const widths = [
227
+ ...new Set(
228
+ [width, width * 2].map(w => allSizes.find(p => p >= w) || allSizes[allSizes.length - 1]!),
229
+ ),
230
+ ];
231
+ return { widths, kind: 'x' };
232
+ }
233
+
234
+ function generateImgAttrs(params: {
235
+ config: ResolvedImagesConfig;
236
+ src: string;
237
+ unoptimized: boolean;
238
+ width: number | undefined;
239
+ quality: number | undefined;
240
+ sizes: string | undefined;
241
+ loader: (args: { config: ResolvedImagesConfig; src: string; width: number; quality?: number }) => string;
242
+ }): GenImgAttrsResult {
243
+ const { config, src, unoptimized, width, quality, sizes, loader } = params;
244
+ if (unoptimized) return { src, srcSet: undefined, sizes: undefined };
245
+ const { widths, kind } = getWidths(config, width, sizes);
246
+ const last = widths.length - 1;
247
+ return {
248
+ sizes: !sizes && kind === 'w' ? '100vw' : sizes,
249
+ srcSet: widths
250
+ .map(
251
+ (w, i) =>
252
+ `${loader({ config, src, quality, width: w })} ${kind === 'w' ? w : i + 1}${kind}`,
253
+ )
254
+ .join(', '),
255
+ src: loader({ config, src, quality, width: widths[last]! }),
256
+ };
257
+ }
258
+
259
+ /**
260
+ * Usage gate for the inline blur SVG below - raw template that only `placeholder="blur"` can reach. The
261
+ * bundler defines it to `false` for an app whose source never asks for a blurred placeholder, and the
262
+ * builder is then dead code. Undefined (no define) keeps it, so the gate can only ever remove bytes an
263
+ * app proved it cannot use.
264
+ */
265
+ declare const __PNEXT_IMAGE_BLUR__: boolean;
266
+
267
+ function getImageBlurSvg(params: {
268
+ widthInt?: number;
269
+ heightInt?: number;
270
+ blurWidth?: number;
271
+ blurHeight?: number;
272
+ blurDataURL: string;
273
+ objectFit?: string;
274
+ }): string {
275
+ const { widthInt, heightInt, blurWidth, blurHeight, blurDataURL, objectFit } = params;
276
+ const std = 20;
277
+ const svgWidth = blurWidth ? blurWidth * 40 : widthInt;
278
+ const svgHeight = blurHeight ? blurHeight * 40 : heightInt;
279
+ const viewBox = svgWidth && svgHeight ? `viewBox='0 0 ${svgWidth} ${svgHeight}'` : '';
280
+ const preserveAspectRatio = viewBox
281
+ ? 'none'
282
+ : objectFit === 'contain'
283
+ ? 'xMidYMid'
284
+ : objectFit === 'cover'
285
+ ? 'xMidYMid slice'
286
+ : 'none';
287
+ return `%3Csvg xmlns='http://www.w3.org/2000/svg' ${viewBox}%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='${std}'/%3E%3CfeColorMatrix values='1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 100 -1' result='s'/%3E%3CfeFlood x='0' y='0' width='100%25' height='100%25'/%3E%3CfeComposite operator='out' in='s'/%3E%3CfeComposite in2='SourceGraphic'/%3E%3CfeGaussianBlur stdDeviation='${std}'/%3E%3C/filter%3E%3Cimage width='100%25' height='100%25' x='0' y='0' preserveAspectRatio='${preserveAspectRatio}' style='filter: url(%23b);' href='${blurDataURL}'/%3E%3C/svg%3E`;
288
+ }
289
+
290
+ export function getImageProps(imageProps: ImageProps): { props: Record<string, unknown> } {
291
+ const resolved = resolveImageProps(imageProps, getImagesConfig());
292
+ return { props: resolved.props };
293
+ }
294
+
295
+ export function unstable_getImgProps(imageProps: ImageProps) {
296
+ return getImageProps(imageProps);
297
+ }
298
+
299
+ export function resolveImageProps(
300
+ imageProps: ImageProps,
301
+ config: ResolvedImagesConfig,
302
+ blurComplete = false,
303
+ showAltText = false,
304
+ ): ResolvedImageProps {
305
+ const {
306
+ src: rawSrc,
307
+ alt = '',
308
+ width: widthProp,
309
+ height: heightProp,
310
+ fill: fillProp = false,
311
+ loader,
312
+ quality: qualityProp,
313
+ priority = false,
314
+ preload = false,
315
+ loading,
316
+ placeholder = 'empty',
317
+ blurDataURL: blurDataURLProp,
318
+ unoptimized: unoptimizedProp,
319
+ overrideSrc,
320
+ sizes: sizesProp,
321
+ fetchPriority,
322
+ decoding = 'async',
323
+ style: styleProp,
324
+ layout,
325
+ objectFit,
326
+ objectPosition,
327
+ onLoad: _onLoad,
328
+ onError: _onError,
329
+ onLoadingComplete: _onLoadingComplete,
330
+ ...rest
331
+ } = imageProps;
332
+
333
+ let fill = fillProp;
334
+ let sizes = sizesProp;
335
+ let userStyle = normalizeStyle(styleProp);
336
+
337
+ if (layout) {
338
+ if (layout === 'fill') fill = true;
339
+ const layoutStyle =
340
+ layout === 'intrinsic'
341
+ ? { maxWidth: '100%', height: 'auto' }
342
+ : layout === 'responsive'
343
+ ? { width: '100%', height: 'auto' }
344
+ : undefined;
345
+ if (layoutStyle) userStyle = { ...userStyle, ...layoutStyle };
346
+ if (!sizes && (layout === 'responsive' || layout === 'fill')) sizes = '100vw';
347
+ }
348
+
349
+ const staticData = rawSrc && typeof rawSrc !== 'string' ? unwrapStaticImport(rawSrc) : undefined;
350
+ const src = typeof rawSrc === 'string' ? rawSrc : (staticData?.src ?? '');
351
+
352
+ let width = toNumber(widthProp);
353
+ let height = toNumber(heightProp);
354
+ if (staticData) {
355
+ if (width === undefined && height === undefined) {
356
+ width = staticData.width;
357
+ height = staticData.height;
358
+ } else if (width !== undefined && height === undefined) {
359
+ height = Math.round((staticData.height / staticData.width) * width);
360
+ } else if (height !== undefined && width === undefined) {
361
+ width = Math.round((staticData.width / staticData.height) * height);
362
+ }
363
+ }
364
+
365
+ const blurDataURL = blurDataURLProp ?? staticData?.blurDataURL;
366
+ const quality = toNumber(qualityProp);
367
+ let unoptimized = Boolean(unoptimizedProp ?? config.unoptimized);
368
+ if (!src || src.startsWith('data:') || src.startsWith('blob:')) unoptimized = true;
369
+ // Serve `.svg` as-is instead of proxying through the Image Optimization API,
370
+ // which refuses SVG unless `dangerouslyAllowSVG` is set (otherwise the request
371
+ // 400s and the image never loads, so onLoad/onLoadingComplete never fire).
372
+ // Only applies to the default loader — a custom loader/loaderFile owns SVG.
373
+ const isDefaultLoader = !loader && !config.loaderFile;
374
+ if (
375
+ isDefaultLoader &&
376
+ !config.dangerouslyAllowSVG &&
377
+ (src.split('?', 1)[0] ?? '').endsWith('.svg')
378
+ ) {
379
+ unoptimized = true;
380
+ }
381
+ if (!unoptimized) validateLocalSrc(config, src);
382
+
383
+ const activeLoader: (args: {
384
+ config: ResolvedImagesConfig;
385
+ src: string;
386
+ width: number;
387
+ quality?: number;
388
+ }) => string = loader
389
+ ? ({ src: s, width: w, quality: q }) => loader({ src: s, width: w, quality: q })
390
+ : ({ config: c, src: s, width: w, quality: q }) => defaultLoader(c, s, w, q);
391
+
392
+ const imgAttrs = generateImgAttrs({
393
+ config,
394
+ src,
395
+ unoptimized,
396
+ width: fill ? undefined : width,
397
+ quality,
398
+ sizes,
399
+ loader: activeLoader,
400
+ });
401
+
402
+ const isBlur = placeholder === 'blur' && Boolean(blurDataURL);
403
+ const imgStyle: Record<string, string | number | undefined> = {
404
+ ...(fill ? { ...fillStyle(), objectFit, objectPosition } : {}),
405
+ ...(showAltText ? {} : { color: 'transparent' }),
406
+ ...userStyle,
407
+ };
408
+
409
+ const objectFitVal = typeof imgStyle.objectFit === 'string' ? imgStyle.objectFit : undefined;
410
+ const backgroundImage =
411
+ !blurComplete && placeholder !== 'empty'
412
+ ? placeholder === 'blur'
413
+ ? // Spelled out here rather than read from a named const: esbuild folds
414
+ // the defined identifier in place, but does not propagate a folded
415
+ // top-level const into this branch, and the const form leaves the
416
+ // whole builder alive (measured — 0 bytes removed).
417
+ (typeof __PNEXT_IMAGE_BLUR__ === 'boolean' ? __PNEXT_IMAGE_BLUR__ : true) && blurDataURL
418
+ ? `url("data:image/svg+xml;charset=utf-8,${getImageBlurSvg({
419
+ widthInt: fill ? undefined : width,
420
+ heightInt: fill ? undefined : height,
421
+ blurWidth: staticData?.blurWidth,
422
+ blurHeight: staticData?.blurHeight,
423
+ blurDataURL,
424
+ objectFit: objectFitVal,
425
+ })}")`
426
+ : undefined
427
+ : `url("${placeholder}")`
428
+ : undefined;
429
+
430
+ let backgroundSize = 'cover';
431
+ if (objectFitVal === 'fill') backgroundSize = '100% 100%';
432
+ else if (objectFitVal && !INVALID_BACKGROUND_SIZE.has(objectFitVal)) backgroundSize = objectFitVal;
433
+
434
+ const placeholderStyle: Record<string, string | number> = backgroundImage
435
+ ? {
436
+ backgroundSize,
437
+ backgroundPosition: imgStyle.objectPosition || '50% 50%',
438
+ backgroundRepeat: 'no-repeat',
439
+ backgroundImage,
440
+ }
441
+ : {};
442
+
443
+ const style = serializeStyle({ ...imgStyle, ...placeholderStyle });
444
+ const resolvedSrc = (overrideSrc ?? imgAttrs.src) || undefined;
445
+ const props: Record<string, unknown> = {
446
+ ...rest,
447
+ alt,
448
+ ...(resolvedSrc !== undefined ? { src: resolvedSrc } : {}),
449
+ ...(imgAttrs.srcSet ? { srcSet: imgAttrs.srcSet } : {}),
450
+ ...(imgAttrs.sizes ? { sizes: imgAttrs.sizes } : {}),
451
+ decoding,
452
+ style,
453
+ };
454
+ if (!fill) {
455
+ if (width !== undefined) props.width = width;
456
+ if (height !== undefined) props.height = height;
457
+ }
458
+ if (loading !== undefined) props.loading = loading;
459
+ else if (!priority && !preload) props.loading = 'lazy';
460
+ if (fetchPriority !== undefined) props.fetchpriority = fetchPriority;
461
+
462
+ return {
463
+ props,
464
+ meta: {
465
+ preload: preload || priority,
466
+ fill,
467
+ unoptimized,
468
+ placeholder: isBlur ? 'blur' : String(placeholder),
469
+ imgAttributes: {
470
+ src: resolvedSrc ?? '',
471
+ ...(imgAttrs.srcSet ? { srcSet: imgAttrs.srcSet } : {}),
472
+ ...(imgAttrs.sizes ? { sizes: imgAttrs.sizes } : {}),
473
+ },
474
+ ...(backgroundImage ? { loadedStyle: serializeStyle(imgStyle) } : {}),
475
+ },
476
+ };
477
+ }
478
+
479
+ function normalizeStyle(style: JSX.CSSProperties | string | undefined): Record<string, unknown> {
480
+ if (!style) return {};
481
+ if (typeof style !== 'string') return { ...style };
482
+ const out: Record<string, unknown> = {};
483
+ for (const decl of style.split(';')) {
484
+ const [k, v] = decl.split(':');
485
+ if (k && v) out[camel(k.trim())] = v.trim();
486
+ }
487
+ return out;
488
+ }
489
+
490
+ function fillStyle(): Record<string, string> {
491
+ return {
492
+ position: 'absolute',
493
+ height: '100%',
494
+ width: '100%',
495
+ left: '0',
496
+ top: '0',
497
+ right: '0',
498
+ bottom: '0',
499
+ };
500
+ }
501
+
502
+ function validateLocalSrc(config: ResolvedImagesConfig, src: string): void {
503
+ if (!src.startsWith('/') || src.startsWith('//') || config.localPatterns !== undefined) return;
504
+ const search = new URL(src, 'http://n').search;
505
+ if (!search) return;
506
+ throw new Error(
507
+ `Image with src "${src}" is using a query string which is not configured in images.localPatterns.\nRead more: https://nextjs.org/docs/messages/next-image-unconfigured-localpatterns`,
508
+ );
509
+ }
510
+
511
+ function camel(prop: string): string {
512
+ return prop.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
513
+ }
514
+
515
+ const UNITLESS_STYLE_PROPS = new Set([
516
+ 'animationIterationCount',
517
+ 'aspectRatio',
518
+ 'borderImageOutset',
519
+ 'borderImageSlice',
520
+ 'borderImageWidth',
521
+ 'boxFlex',
522
+ 'boxFlexGroup',
523
+ 'boxOrdinalGroup',
524
+ 'columnCount',
525
+ 'columns',
526
+ 'flex',
527
+ 'flexGrow',
528
+ 'flexPositive',
529
+ 'flexShrink',
530
+ 'flexNegative',
531
+ 'flexOrder',
532
+ 'gridArea',
533
+ 'gridRow',
534
+ 'gridRowEnd',
535
+ 'gridRowSpan',
536
+ 'gridRowStart',
537
+ 'gridColumn',
538
+ 'gridColumnEnd',
539
+ 'gridColumnSpan',
540
+ 'gridColumnStart',
541
+ 'fontWeight',
542
+ 'lineClamp',
543
+ 'lineHeight',
544
+ 'opacity',
545
+ 'order',
546
+ 'orphans',
547
+ 'tabSize',
548
+ 'widows',
549
+ 'zIndex',
550
+ 'zoom',
551
+ 'fillOpacity',
552
+ 'floodOpacity',
553
+ 'stopOpacity',
554
+ 'strokeDasharray',
555
+ 'strokeDashoffset',
556
+ 'strokeMiterlimit',
557
+ 'strokeOpacity',
558
+ 'strokeWidth',
559
+ ]);
560
+
561
+ function serializeStyle(style: Record<string, string | number | undefined>): string {
562
+ return Object.entries(style)
563
+ .filter(([, v]) => v !== undefined && v !== '')
564
+ .map(([k, v]) => `${kebab(k)}:${cssValue(k, v!)}`)
565
+ .join(';');
566
+ }
567
+
568
+ function cssValue(prop: string, value: string | number): string {
569
+ if (typeof value === 'number' && value !== 0 && !UNITLESS_STYLE_PROPS.has(prop)) return `${value}px`;
570
+ return String(value);
571
+ }
572
+
573
+ function kebab(prop: string): string {
574
+ return prop.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
575
+ }
@@ -0,0 +1,102 @@
1
+ import { existsSync, readdirSync, readFileSync, type Dirent } from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ /**
5
+ * Build-time app facts that let compat drop code an app cannot reach. The fact here: whether any
6
+ * first-party source can ask for a blurred image placeholder. `placeholder="blur"` on `next/image` (or
7
+ * getImageProps) is the only route into image-props' inline blur-SVG builder, which otherwise ships on
8
+ * every page that uses next/image.
9
+ *
10
+ * The gate is one-sided by construction: every unreadable case answers "yes". A wrong "no" would render
11
+ * a blurred placeholder server-side that the client does not, which is a hydration mismatch.
12
+ */
13
+
14
+ /** Skipped wholesale: not first-party source, or build output. */
15
+ const skipDirs = new Set(['node_modules', 'dist', 'build', 'out', 'coverage', 'public', 'target']);
16
+ const sourceFile = /\.(?:[cm]?[jt]sx?|mdx)$/;
17
+
18
+ /** Only these APIs reach the branch, so only files naming one can matter. */
19
+ const imageApi = /next\/(?:legacy\/)?image|getImageProps|unstable_getImgProps/;
20
+
21
+ /** A literal `placeholder="blur"`, in JSX-prop or object-property form. */
22
+ const literalBlur = /\bplaceholder\s*[=:]\s*\{?\s*['"`]blur['"`]/;
23
+
24
+ /**
25
+ * A `placeholder` whose value is not a string literal — `placeholder={value}`,
26
+ * `placeholder: value`. Unreadable from source text, so it counts as usage.
27
+ */
28
+ const opaquePlaceholder = /\bplaceholder\s*[=:]\s*(?!\{?\s*['"`])/;
29
+
30
+ /**
31
+ * Props reaching the image from somewhere this scan cannot see: a spread into
32
+ * any element in an image-using file, or a re-export that lets another file
33
+ * render `<Image>` without ever naming `next/image`.
34
+ */
35
+ const opaqueProps = /\{\s*\.\.\.|export\s[^;]*from\s*['"]next\/(?:legacy\/)?image/;
36
+
37
+ const cache = new Map<string, boolean>();
38
+
39
+ /**
40
+ * Roots to scan: the app, plus the workspace's own packages when the app sits in one - a linked
41
+ * `@scope/ui` is first-party source that compiles into the same client graph, so a `placeholder="blur"`
42
+ * there must be seen.
43
+ */
44
+ function scanRoots(root: string) {
45
+ const roots = [root];
46
+ for (let dir = path.dirname(root); dir !== path.dirname(dir); dir = path.dirname(dir)) {
47
+ if (!existsSync(path.join(dir, 'pnpm-workspace.yaml')) && !existsSync(path.join(dir, 'lerna.json'))) {
48
+ continue;
49
+ }
50
+ const packages = path.join(dir, 'packages');
51
+ if (existsSync(packages)) roots.push(packages);
52
+ break;
53
+ }
54
+ return roots;
55
+ }
56
+
57
+ export function appUsesBlurPlaceholder(root: string) {
58
+ const cached = cache.get(root);
59
+ if (cached !== undefined) return cached;
60
+ const used = scanFirstPartySource(root, blurSource);
61
+ cache.set(root, used);
62
+ return used;
63
+ }
64
+
65
+ function blurSource(source: string): boolean {
66
+ // The common case is one `test` and out: almost no file in an app names an
67
+ // image API, and only those that do pay for the rest.
68
+ if (!imageApi.test(source)) return false;
69
+ return literalBlur.test(source) || opaquePlaceholder.test(source) || opaqueProps.test(source);
70
+ }
71
+
72
+ /** Walk the app's first-party source (plus workspace packages) until `match`. */
73
+ export function scanFirstPartySource(root: string, match: (source: string) => boolean): boolean {
74
+ return scanRoots(root).some(dir => scan(dir, match));
75
+ }
76
+
77
+ function scan(dir: string, match: (source: string) => boolean): boolean {
78
+ let entries: Dirent[];
79
+ try {
80
+ entries = readdirSync(dir, { withFileTypes: true });
81
+ } catch {
82
+ return false;
83
+ }
84
+ for (const entry of entries) {
85
+ if (entry.name.startsWith('.')) continue;
86
+ const file = path.join(dir, entry.name);
87
+ if (entry.isDirectory()) {
88
+ if (skipDirs.has(entry.name)) continue;
89
+ if (scan(file, match)) return true;
90
+ continue;
91
+ }
92
+ if (!sourceFile.test(entry.name)) continue;
93
+ let source: string;
94
+ try {
95
+ source = readFileSync(file, 'utf8');
96
+ } catch {
97
+ continue;
98
+ }
99
+ if (match(source)) return true;
100
+ }
101
+ return false;
102
+ }