@wular/pnext 0.1.6 → 0.1.8
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.
- package/bin/pnext +9 -2
- package/dist/server/_chunks/aliases-pycbtjt1.js +46 -0
- package/dist/server/_chunks/analyze-4d9e00g9.js +449 -0
- package/dist/server/_chunks/boot-bhemypxz.js +38 -0
- package/dist/server/_chunks/build-c991dv65.js +86 -0
- package/dist/server/_chunks/build-kh5e3x48.js +3341 -0
- package/dist/server/_chunks/build-tier-0a96kmx3.js +2456 -0
- package/dist/server/_chunks/compile-040r79gj.js +22 -0
- package/dist/server/_chunks/config-0tvz03a6.js +24 -0
- package/dist/server/_chunks/config-27rj7830.js +36 -0
- package/dist/server/_chunks/create-9rvwshsp.js +135 -0
- package/dist/server/_chunks/dev-ex9exynt.js +220 -0
- package/dist/server/_chunks/esbuild-yyktfrtt.js +15 -0
- package/dist/server/_chunks/generate-myw9hef7.js +22 -0
- package/dist/server/_chunks/index-0r5eqfc7.js +279 -0
- package/dist/server/_chunks/index-0yjv5v47.js +105 -0
- package/dist/server/_chunks/index-11xpbfej.js +17 -0
- package/dist/server/_chunks/index-141vcwk6.js +109 -0
- package/dist/server/_chunks/index-1pzm1hdy.js +4130 -0
- package/dist/server/_chunks/index-29pq0kax.js +590 -0
- package/dist/server/_chunks/index-2vz0wzdn.js +59 -0
- package/dist/server/_chunks/index-2wfv4kvx.js +182 -0
- package/dist/server/_chunks/index-38xpdxwd.js +194 -0
- package/dist/server/_chunks/index-3e075kny.js +128 -0
- package/dist/server/_chunks/index-3ycf5ckv.js +472 -0
- package/dist/server/_chunks/index-4399bvd2.js +18 -0
- package/dist/server/_chunks/index-4da76t62.js +297 -0
- package/dist/server/_chunks/index-4t92hdxd.js +172 -0
- package/dist/server/_chunks/index-4zzebszd.js +10 -0
- package/dist/server/_chunks/index-52k54ykj.js +95 -0
- package/dist/server/_chunks/index-5hr4062s.js +70 -0
- package/dist/server/_chunks/index-68m3mwbd.js +84 -0
- package/dist/server/_chunks/index-6b009mt0.js +56 -0
- package/dist/server/_chunks/index-6gex3pfa.js +15 -0
- package/dist/server/_chunks/index-6kp9r0d9.js +11 -0
- package/dist/server/_chunks/index-6q7zvrap.js +134 -0
- package/dist/server/_chunks/index-71885zaq.js +284 -0
- package/dist/server/_chunks/index-73z4esab.js +38 -0
- package/dist/server/_chunks/index-79ngvam8.js +60 -0
- package/dist/server/_chunks/index-7wc9n8be.js +25 -0
- package/dist/server/_chunks/index-8ct7rqy3.js +198 -0
- package/dist/server/_chunks/index-8kcxdfya.js +206 -0
- package/dist/server/_chunks/index-9ch50vtq.js +18 -0
- package/dist/server/_chunks/index-9f49w76j.js +213 -0
- package/dist/server/_chunks/index-9hz3s0m8.js +851 -0
- package/dist/server/_chunks/index-akkrvjn6.js +535 -0
- package/dist/server/_chunks/index-b6wtkdp4.js +24 -0
- package/dist/server/_chunks/index-b7j671dj.js +207 -0
- package/dist/server/_chunks/index-bcws5eh2.js +35 -0
- package/dist/server/_chunks/index-bzd2ef7t.js +10 -0
- package/dist/server/_chunks/index-cjgqs13g.js +65 -0
- package/dist/server/_chunks/index-ckkdpht5.js +41 -0
- package/dist/server/_chunks/index-cn86z041.js +296 -0
- package/dist/server/_chunks/index-cvr4z22m.js +92 -0
- package/dist/server/_chunks/index-cyfednwb.js +87 -0
- package/dist/server/_chunks/index-d158y67j.js +152 -0
- package/dist/server/_chunks/index-d1jsmhhk.js +955 -0
- package/dist/server/_chunks/index-dg0f05zs.js +199 -0
- package/dist/server/_chunks/index-dgr390q7.js +9438 -0
- package/dist/server/_chunks/index-drwv61cc.js +185 -0
- package/dist/server/_chunks/index-e3a6yx84.js +18 -0
- package/dist/server/_chunks/index-e88tyv35.js +45 -0
- package/dist/server/_chunks/index-en7n9f7m.js +22 -0
- package/dist/server/_chunks/index-entm8bmz.js +28 -0
- package/dist/server/_chunks/index-evhd413j.js +57 -0
- package/dist/server/_chunks/index-ezgj0tr1.js +88 -0
- package/dist/server/_chunks/index-fjv96re1.js +5295 -0
- package/dist/server/_chunks/index-fn1chkkd.js +214 -0
- package/dist/server/_chunks/index-fp8r0sqs.js +64 -0
- package/dist/server/_chunks/index-gcmj16xy.js +92 -0
- package/dist/server/_chunks/index-gkndxkxf.js +736 -0
- package/dist/server/_chunks/index-grta8ap3.js +238 -0
- package/dist/server/_chunks/index-h2a70tg4.js +7 -0
- package/dist/server/_chunks/index-h4xy648y.js +51 -0
- package/dist/server/_chunks/index-hdbwtdv2.js +352 -0
- package/dist/server/_chunks/index-he6n2bvp.js +1270 -0
- package/dist/server/_chunks/index-j0m1jxqa.js +1253 -0
- package/dist/server/_chunks/index-j2zrcp1k.js +66 -0
- package/dist/server/_chunks/index-j681c48a.js +2530 -0
- package/dist/server/_chunks/index-jbj7ehef.js +50 -0
- package/dist/server/_chunks/index-jcsjq3d1.js +20 -0
- package/dist/server/_chunks/index-jks0x0a1.js +14 -0
- package/dist/server/_chunks/index-jn9rbhrx.js +1599 -0
- package/dist/server/_chunks/index-jt8e1pje.js +12 -0
- package/dist/server/_chunks/index-kdhxpwyq.js +483 -0
- package/dist/server/_chunks/index-knp0s912.js +7 -0
- package/dist/server/_chunks/index-kqyy1d4f.js +50 -0
- package/dist/server/_chunks/index-kresp2n8.js +445 -0
- package/dist/server/_chunks/index-m63czgq5.js +590 -0
- package/dist/server/_chunks/index-ms9v9tnx.js +31 -0
- package/dist/server/_chunks/index-n4hxpng2.js +11 -0
- package/dist/server/_chunks/index-n629dy38.js +1071 -0
- package/dist/server/_chunks/index-nf3df4jy.js +266 -0
- package/dist/server/_chunks/index-nqh3xhdc.js +57 -0
- package/dist/server/_chunks/index-pym1cezq.js +39 -0
- package/dist/server/_chunks/index-q3r9z8mq.js +213 -0
- package/dist/server/_chunks/index-q738kkhj.js +100 -0
- package/dist/server/_chunks/index-qcrvt6er.js +112 -0
- package/dist/server/_chunks/index-qjj6gddn.js +54 -0
- package/dist/server/_chunks/index-qpwsgt3a.js +158 -0
- package/dist/server/_chunks/index-r97ptz1q.js +97 -0
- package/dist/server/_chunks/index-ras0sdab.js +26 -0
- package/dist/server/_chunks/index-rcs322t1.js +19 -0
- package/dist/server/_chunks/index-rgj5mx84.js +101 -0
- package/dist/server/_chunks/index-rjegyfx4.js +1435 -0
- package/dist/server/_chunks/index-rt9qq1va.js +4 -0
- package/dist/server/_chunks/index-rthrsed5.js +356 -0
- package/dist/server/_chunks/index-sq3kvz3g.js +200 -0
- package/dist/server/_chunks/index-stbk3jhn.js +31 -0
- package/dist/server/_chunks/index-tf4j19ge.js +130 -0
- package/dist/server/_chunks/index-tgcakdxc.js +125 -0
- package/dist/server/_chunks/index-tgsfjmep.js +74 -0
- package/dist/server/_chunks/index-tq1f96v7.js +51 -0
- package/dist/server/_chunks/index-tq3b39a5.js +107 -0
- package/dist/server/_chunks/index-typcbcsv.js +819 -0
- package/dist/server/_chunks/index-v1ags1na.js +132 -0
- package/dist/server/_chunks/index-vmr35jjw.js +170 -0
- package/dist/server/_chunks/index-vvq7rqtn.js +329 -0
- package/dist/server/_chunks/index-vz0km5qk.js +33 -0
- package/dist/server/_chunks/index-w6h8m6vp.js +138 -0
- package/dist/server/_chunks/index-web361b8.js +138 -0
- package/dist/server/_chunks/index-wm36rj4v.js +311 -0
- package/dist/server/_chunks/index-x0y005nh.js +283 -0
- package/dist/server/_chunks/index-x2m17v9c.js +6166 -0
- package/dist/server/_chunks/index-x3yn4ywm.js +22 -0
- package/dist/server/_chunks/index-x9c55pzy.js +871 -0
- package/dist/server/_chunks/index-xh1nbk46.js +1299 -0
- package/dist/server/_chunks/index-xvj80c3a.js +30 -0
- package/dist/server/_chunks/index-xywjg2vc.js +100 -0
- package/dist/server/_chunks/index-y4d02znn.js +66 -0
- package/dist/server/_chunks/index-y7mzvq2q.js +110 -0
- package/dist/server/_chunks/index-ye9hmsvf.js +47 -0
- package/dist/server/_chunks/index-yhxkxghe.js +56 -0
- package/dist/server/_chunks/index-yrjkqtfj.js +86 -0
- package/dist/server/_chunks/index-z88qv16t.js +11 -0
- package/dist/server/_chunks/index-zfrwq472.js +154 -0
- package/dist/server/_chunks/index-zqpqyg8p.js +666 -0
- package/dist/server/_chunks/loader-5en7wpt5.js +80 -0
- package/dist/server/_chunks/modules-t92favpj.js +76 -0
- package/dist/server/_chunks/pipeline-4t9q43ht.js +836 -0
- package/dist/server/_chunks/production-385bw8df.js +55 -0
- package/dist/server/_chunks/proxy-config-60gprphe.js +23 -0
- package/dist/server/_chunks/proxy-gfhmktbk.js +35 -0
- package/dist/server/_chunks/router-xnrva921.js +501 -0
- package/dist/server/_chunks/routes-ht868pdc.js +66 -0
- package/dist/server/_chunks/run-d40d63ag.js +697 -0
- package/dist/server/_chunks/server-2p8fgqq2.js +1839 -0
- package/dist/server/_chunks/tsconfig-defaults-bcn34eqb.js +21 -0
- package/dist/server/_chunks/typegen-dvtcjtaw.js +37 -0
- package/dist/server/api/client-cache.js +14 -0
- package/dist/server/cli/index.js +375 -0
- package/dist/server/cli/start.js +158 -0
- package/dist/server/client/reference.js +16 -0
- package/dist/server/compat/next/cache.js +56 -0
- package/dist/server/compat/next/canonical-url.js +13 -0
- package/dist/server/compat/next/client-cache.js +57 -0
- package/dist/server/compat/next/client-navigation.js +232 -0
- package/dist/server/compat/next/client-only.js +1 -0
- package/dist/server/compat/next/client-script.js +178 -0
- package/dist/server/compat/next/client-server.js +34 -0
- package/dist/server/compat/next/config-headers.js +34 -0
- package/dist/server/compat/next/config-loader.js +25 -0
- package/dist/server/compat/next/config.js +10 -0
- package/dist/server/compat/next/constants.js +18 -0
- package/dist/server/compat/next/custom-server.js +10 -0
- package/dist/server/compat/next/dist/client/components/app-router-headers.js +48 -0
- package/dist/server/compat/next/dist/server/web/spec-extension/revalidate.js +27 -0
- package/dist/server/compat/next/dist/server/web/spec-extension/unstable-cache.js +25 -0
- package/dist/server/compat/next/dist/server/web/spec-extension/unstable-no-store.js +25 -0
- package/dist/server/compat/next/dynamic.js +21 -0
- package/dist/server/compat/next/error.js +112 -0
- package/dist/server/compat/next/font/cache.js +16 -0
- package/dist/server/compat/next/font/google.js +3 -0
- package/dist/server/compat/next/font/index.js +1 -0
- package/dist/server/compat/next/font/local.js +24 -0
- package/dist/server/compat/next/font/runtime-client.js +33 -0
- package/dist/server/compat/next/font/runtime.js +43 -0
- package/dist/server/compat/next/font/shared.js +22 -0
- package/dist/server/compat/next/form.js +129 -0
- package/dist/server/compat/next/head.js +10 -0
- package/dist/server/compat/next/headers.js +30 -0
- package/dist/server/compat/next/image/client.js +11 -0
- package/dist/server/compat/next/image/config.js +18 -0
- package/dist/server/compat/next/image/optimizer.js +25 -0
- package/dist/server/compat/next/image/patterns.js +12 -0
- package/dist/server/compat/next/image/props.js +16 -0
- package/dist/server/compat/next/image/shared.js +86 -0
- package/dist/server/compat/next/image/static-metadata.js +32 -0
- package/dist/server/compat/next/image/validate.js +8 -0
- package/dist/server/compat/next/image.js +59 -0
- package/dist/server/compat/next/index.js +1 -0
- package/dist/server/compat/next/legacy-image.js +78 -0
- package/dist/server/compat/next/legacy-request-apis.js +24 -0
- package/dist/server/compat/next/link-transform.js +15 -0
- package/dist/server/compat/next/link.js +14 -0
- package/dist/server/compat/next/navigation.js +191 -0
- package/dist/server/compat/next/offline.js +45 -0
- package/dist/server/compat/next/og.js +8 -0
- package/dist/server/compat/next/preferred-region.js +8 -0
- package/dist/server/compat/next/redirects.js +32 -0
- package/dist/server/compat/next/resource-hints.js +30 -0
- package/dist/server/compat/next/rewrites.js +43 -0
- package/dist/server/compat/next/root-params.js +121 -0
- package/dist/server/compat/next/route-state.js +23 -0
- package/dist/server/compat/next/router.js +103 -0
- package/dist/server/compat/next/script.js +37 -0
- package/dist/server/compat/next/server-only.js +1 -0
- package/dist/server/compat/next/server.js +131 -0
- package/dist/server/compat/next/svgr.js +14 -0
- package/dist/server/compat/next/telemetry.js +10 -0
- package/dist/server/compat/next/usage.js +13 -0
- package/dist/server/compat/next/user-agent.js +12 -0
- package/dist/server/compat/next/web-vitals.js +35 -0
- package/dist/server/compat/react/action-state.js +11 -0
- package/dist/server/compat/react/client-lite.js +83 -0
- package/dist/server/compat/react/client.js +131 -0
- package/dist/server/compat/react/compiler-runtime.js +42 -0
- package/dist/server/compat/react/dom-client.js +106 -0
- package/dist/server/compat/react/dom-react-server.js +37 -0
- package/dist/server/compat/react/dom-server.js +40 -0
- package/dist/server/compat/react/dom.js +52 -0
- package/dist/server/compat/react/hooks-extra.js +12 -0
- package/dist/server/compat/react/internals.js +14 -0
- package/dist/server/compat/react/parity.js +11 -0
- package/dist/server/compat/react/preact.js +113 -0
- package/dist/server/compat/react/react-server.js +133 -0
- package/dist/server/compat/react/router-shim.js +8 -0
- package/dist/server/compat/react/server-component-use.js +14 -0
- package/dist/server/compat/react/server-inserted-html.js +14 -0
- package/dist/server/compat/react/server.js +194 -0
- package/dist/server/compat/react/style-values.js +10 -0
- package/dist/server/compat/react/use.js +10 -0
- package/dist/server/compat/react/view-transition.js +12 -0
- package/dist/server/generation.json +1 -0
- package/package.json +4 -1
- package/reference/compat.md +1 -1
- package/reference/config.md +2 -2
- package/reference/dev.md +1 -1
- package/reference/getting-started.md +1 -1
- package/reference/rendering.md +4 -4
- package/src/api/dynamic.tsx +86 -4
- package/src/api/server.ts +24 -6
- package/src/cli/adapters/vercel-warm.ts +327 -62
- package/src/cli/adapters/vercel.ts +164 -1054
- package/src/cli/analyze.ts +3 -2
- package/src/cli/build/cache.ts +6 -6
- package/src/cli/build.ts +78 -45
- package/src/cli/index.ts +21 -3
- package/src/cli/serve/immutable.ts +31 -0
- package/src/cli/serve/pipeline.ts +97 -93
- package/src/cli/serve/standalone-worker.ts +8 -0
- package/src/cli/serve/standalone.ts +703 -0
- package/src/cli/start.ts +31 -8
- package/src/client/build.ts +1 -1
- package/src/client/chunk-fold.ts +19 -6
- package/src/client/react-tier.ts +3 -2
- package/src/client/reference-stub.ts +23 -36
- package/src/compat/actions/client-plugin.ts +4 -2
- package/src/compat/actions/config.ts +6 -5
- package/src/compat/actions/extensions.ts +5 -5
- package/src/compat/aliases.ts +8 -1
- package/src/compat/bundler/config.ts +88 -0
- package/src/compat/bundler/optimize-package-imports.ts +86 -26
- package/src/compat/bundler/relay-transform.ts +15 -3
- package/src/compat/bundler/require-context.ts +2 -1
- package/src/compat/bundler/webpack-loaders.ts +3 -2
- package/src/compat/bundler/worker.ts +2 -2
- package/src/compat/cache-control.ts +16 -8
- package/src/compat/css/inline-css.ts +1 -1
- package/src/compat/css/modules.ts +18 -5
- package/src/compat/export/build-complete.ts +1 -1
- package/src/compat/export/emit.ts +4 -4
- package/src/compat/export/standalone.ts +30 -65
- package/src/compat/image-optimizer/optimize.ts +1 -1
- package/src/compat/lifecycle/error-funnel.ts +1 -1
- package/src/compat/lifecycle/instrumentation-client.ts +4 -3
- package/src/compat/lifecycle/instrumentation.ts +117 -24
- package/src/compat/lifecycle/testmode.ts +3 -2
- package/src/compat/metadata-artifacts.ts +1 -1
- package/src/compat/next/config-loader.ts +140 -13
- package/src/compat/next/dynamic.tsx +10 -23
- package/src/compat/next/font/cache.ts +2 -1
- package/src/compat/next/font/runtime.ts +23 -3
- package/src/compat/next/og.ts +3 -3
- package/src/compat/next/usage.ts +3 -2
- package/src/compat/pages/api.ts +83 -14
- package/src/compat/pages/router.ts +30 -14
- package/src/compat/register/bundler.ts +3 -1
- package/src/compat/register/image.ts +1 -1
- package/src/compat/register/middleware.ts +1 -1
- package/src/compat/register/otel.ts +5 -9
- package/src/compat/register/render.ts +3 -1
- package/src/compat/register/use-cache.ts +3 -1
- package/src/compat/release.ts +46 -0
- package/src/compat/segment/serve.ts +1 -1
- package/src/compat/typecheck/check.ts +6 -4
- package/src/compat/validation/validate.ts +10 -7
- package/src/config.ts +76 -9
- package/src/css/assets.ts +213 -0
- package/src/css/build.ts +190 -247
- package/src/dev/client-chunk-store.ts +2 -1
- package/src/dev/restart/client-key.ts +2 -1
- package/src/dev/restart/global-css.ts +2 -1
- package/src/dev/restart/route-bundle-key.ts +2 -1
- package/src/dev/restart/route-facts.ts +2 -1
- package/src/dev/server.ts +34 -14
- package/src/extensions.ts +4 -0
- package/src/out-paths.ts +10 -0
- package/src/render/hooks.ts +13 -0
- package/src/render/renderer.ts +53 -33
- package/src/request/context.ts +9 -0
- package/src/resolve/dynamic.ts +7 -1
- package/src/resolve/engine.ts +2 -12
- package/src/resolve/imports.ts +12 -52
- package/src/resolve/package-json.ts +56 -0
- package/src/resolve/scan-facts.ts +250 -1
- package/src/routing/href.ts +10 -3
- package/src/routing/match.ts +287 -0
- package/src/routing/metadata-files.ts +20 -6
- package/src/routing/proxy-config.ts +137 -0
- package/src/routing/proxy.ts +35 -134
- package/src/routing/routes.ts +22 -281
- package/src/runtime/fingerprint.ts +23 -8
- package/src/runtime/load.ts +170 -0
- package/src/runtime/loader.ts +218 -44
- package/src/runtime/module-cache.ts +15 -1
- package/src/runtime/modules.ts +405 -82
- package/src/runtime/prebundle.ts +101 -0
- package/src/runtime/production.ts +456 -0
- package/src/runtime/release-graph.ts +342 -0
- package/src/runtime/vendor-build.ts +750 -17
- package/src/runtime/vendor.ts +16 -3
- package/src/types.ts +4 -3
- package/src/utils/directive.ts +40 -0
- package/src/utils/fs.ts +95 -4
- package/src/utils/resource-query.ts +12 -0
- package/src/cli/serve/entry.ts +0 -45
|
@@ -9,9 +9,18 @@
|
|
|
9
9
|
* the alias/asset helpers this module reuses from it - lives in `./server`.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import { builtinModules, createRequire } from 'node:module'
|
|
13
|
-
import {
|
|
14
|
-
|
|
12
|
+
import { builtinModules, createRequire, isBuiltin } from 'node:module'
|
|
13
|
+
import {
|
|
14
|
+
appendFileSync,
|
|
15
|
+
existsSync,
|
|
16
|
+
mkdirSync,
|
|
17
|
+
readFileSync,
|
|
18
|
+
readlinkSync,
|
|
19
|
+
realpathSync,
|
|
20
|
+
statSync,
|
|
21
|
+
writeFileSync,
|
|
22
|
+
} from 'node:fs'
|
|
23
|
+
import { copyFile, mkdir, readFile, rename, rm, symlink, writeFile } from 'node:fs/promises'
|
|
15
24
|
import path from 'node:path'
|
|
16
25
|
import { fileURLToPath } from 'node:url'
|
|
17
26
|
import type { Loader, Metafile, OnLoadResult, OnResolveResult, Plugin } from 'esbuild'
|
|
@@ -25,7 +34,12 @@ import {
|
|
|
25
34
|
serverDefineOptions,
|
|
26
35
|
type ServerEsbuildPluginOptions,
|
|
27
36
|
} from '../extensions'
|
|
28
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
pathToFileHref,
|
|
39
|
+
type CompatAliasTarget,
|
|
40
|
+
type ResolvedConfig,
|
|
41
|
+
outCachePath,
|
|
42
|
+
} from '../config'
|
|
29
43
|
import {
|
|
30
44
|
cacheRoot,
|
|
31
45
|
devArtifactUsable,
|
|
@@ -33,6 +47,7 @@ import {
|
|
|
33
47
|
noteDevArtifactWritten,
|
|
34
48
|
} from './module-cache'
|
|
35
49
|
import { frameworkFingerprint } from './fingerprint'
|
|
50
|
+
import { isReleaseCompile } from './load'
|
|
36
51
|
import {
|
|
37
52
|
getExternalPackagePolicy,
|
|
38
53
|
isEsmModuleEntry,
|
|
@@ -55,8 +70,10 @@ import {
|
|
|
55
70
|
} from '../client/reference-stub'
|
|
56
71
|
import { noteCompiledClientReference } from '../client/reference'
|
|
57
72
|
import { reactCompatEnabled } from '../render/hooks'
|
|
58
|
-
import { writeFileAtomic } from '../utils/fs'
|
|
73
|
+
import { toPosixPath, writeFileAtomic } from '../utils/fs'
|
|
74
|
+
import { releaseArtifactSource } from './modules'
|
|
59
75
|
import { formatDuration } from '../utils/verbose'
|
|
76
|
+
import { hasModuleState, importMetaRefs, scanFacts } from '../resolve/scan-facts'
|
|
60
77
|
import {
|
|
61
78
|
addCommonJsNamedExports,
|
|
62
79
|
canonicalVendorCode,
|
|
@@ -87,6 +104,9 @@ import {
|
|
|
87
104
|
coreAliases,
|
|
88
105
|
firstAliasForSpecifier,
|
|
89
106
|
hashBundleSpecifier,
|
|
107
|
+
nodeModuleSourceLoadsAsIs,
|
|
108
|
+
recordNativeNodeModuleFiles,
|
|
109
|
+
statSignature,
|
|
90
110
|
packageJsxLoaders,
|
|
91
111
|
runtimeAliasBuildPlugin,
|
|
92
112
|
serverAssetPlugin,
|
|
@@ -114,6 +134,11 @@ export function clearServerRuntimeCaches() {
|
|
|
114
134
|
vendorReuseLoaded.clear()
|
|
115
135
|
vendorReuseProbes.clear()
|
|
116
136
|
vendorLayerProbeMemo.clear()
|
|
137
|
+
nativeServerEntries.clear()
|
|
138
|
+
nativeServerFiles.clear()
|
|
139
|
+
nativeVerdictStores.clear()
|
|
140
|
+
stateCompanions.clear()
|
|
141
|
+
packageFileScans.clear()
|
|
117
142
|
// The marker memo tracks files under `.pnext`; a wipe takes them with it.
|
|
118
143
|
esmDistPackages.clear()
|
|
119
144
|
clearProvidedEntryResolutions()
|
|
@@ -130,6 +155,14 @@ export async function externalServerPackageHref(
|
|
|
130
155
|
nested = false,
|
|
131
156
|
) {
|
|
132
157
|
let plan = vendorBuildPlan(config, specifier, target, resolveDir, conditionTarget)
|
|
158
|
+
const demand = (file: string) => {
|
|
159
|
+
if (!nested) noteReleaseDependency(config, file, specifier, resolveDir, target, conditionTarget)
|
|
160
|
+
return pathToFileHref(file)
|
|
161
|
+
}
|
|
162
|
+
if (target === 'server' && conditionTarget === 'server' && isPackageSpecifier(specifier)) {
|
|
163
|
+
const native = await nativeServerEntry(config, specifier, resolveDir)
|
|
164
|
+
if (native) return demand(native)
|
|
165
|
+
}
|
|
133
166
|
const aliased = await crossLayerReusePlan(
|
|
134
167
|
config,
|
|
135
168
|
specifier,
|
|
@@ -158,7 +191,540 @@ export async function externalServerPackageHref(
|
|
|
158
191
|
if (plan.group) dropVendorGroup(plan.group.key)
|
|
159
192
|
file = await vendorBundle(plan, nested)
|
|
160
193
|
}
|
|
161
|
-
return
|
|
194
|
+
return demand(file)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// --------------------------------------------------------------------------
|
|
198
|
+
// release dependency graph
|
|
199
|
+
// --------------------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* A release bundles each App Router layer's server dependencies into one graph, as Next does. The
|
|
203
|
+
* compile still vendors (or loads natively) what the build's own prerender imports, and logs each
|
|
204
|
+
* App Router demand so the release can repoint its modules at the linked graph once all compile.
|
|
205
|
+
*/
|
|
206
|
+
export interface ReleaseDependency {
|
|
207
|
+
/** What the compile handed out: a vendor artifact or a natively loaded file. */
|
|
208
|
+
file: string
|
|
209
|
+
layer: ReleaseDependencyLayer
|
|
210
|
+
specifier: string
|
|
211
|
+
/** The file this layer resolves `specifier` to: the graph's entry. */
|
|
212
|
+
entry?: string
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** The App Router node layers, `<target>:<conditionTarget>`: RSC and client components' SSR. */
|
|
216
|
+
export type ReleaseDependencyLayer = 'server:server' | 'client:client'
|
|
217
|
+
|
|
218
|
+
const releaseDependencyLayers = new Set<string>(['server:server', 'client:client'])
|
|
219
|
+
const loggedReleaseDependencies = new Set<string>()
|
|
220
|
+
|
|
221
|
+
export function releaseDependencyLog(config: ResolvedConfig) {
|
|
222
|
+
return path.join(cacheRoot(config.outPath), 'dependencies.jsonl')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** @internal Each build starts from an emptied out dir. */
|
|
226
|
+
export function clearReleaseDependencyLog() {
|
|
227
|
+
loggedReleaseDependencies.clear()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Appended, not held: the build and its warm child both compile. */
|
|
231
|
+
function noteReleaseDependency(
|
|
232
|
+
config: ResolvedConfig,
|
|
233
|
+
file: string,
|
|
234
|
+
specifier: string,
|
|
235
|
+
resolveDir: string,
|
|
236
|
+
target: CompatAliasTarget,
|
|
237
|
+
conditionTarget: ServerBundleTarget,
|
|
238
|
+
) {
|
|
239
|
+
const layer = `${target}:${conditionTarget}` as ReleaseDependencyLayer
|
|
240
|
+
if (!isReleaseCompile() || !releaseDependencyLayers.has(layer)) return
|
|
241
|
+
const entry = resolveVendorEntry(config, specifier, resolveDir, conditionTarget)
|
|
242
|
+
appendReleaseLog(config, { file, layer, specifier, entry } satisfies Partial<ReleaseDependency>)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** A release module compiled for another layer than its artifact directory's (a `'use client'` file). */
|
|
246
|
+
export function noteReleaseModuleLayer(
|
|
247
|
+
config: ResolvedConfig,
|
|
248
|
+
module: string,
|
|
249
|
+
target: CompatAliasTarget,
|
|
250
|
+
conditionTarget: ServerBundleTarget,
|
|
251
|
+
) {
|
|
252
|
+
if (isReleaseCompile())
|
|
253
|
+
appendReleaseLog(config, { module, layer: `${target}:${conditionTarget}` })
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function appendReleaseLog(config: ResolvedConfig, record: object) {
|
|
257
|
+
const line = JSON.stringify(record)
|
|
258
|
+
const log = releaseDependencyLog(config)
|
|
259
|
+
if (loggedReleaseDependencies.has(`${log}\0${line}`)) return
|
|
260
|
+
loggedReleaseDependencies.add(`${log}\0${line}`)
|
|
261
|
+
mkdirSync(path.dirname(log), { recursive: true })
|
|
262
|
+
appendFileSync(log, `${line}\n`)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Link one layer's dependency entries in one splitting build: each package file becomes one module and
|
|
267
|
+
* unused code drops out. `serverExternalPackages` and Next's built-in list stay external where the
|
|
268
|
+
* importer resolves them as the root does; Next bundles another copy. A package that reads files beside
|
|
269
|
+
* itself (`import.meta.url`) or imports computed paths loads in place, as in 0.1.6: Next's webpack
|
|
270
|
+
* contexts bundle computed imports instead. Returns the files written and the entries left in place.
|
|
271
|
+
*/
|
|
272
|
+
/**
|
|
273
|
+
* esbuild names CommonJS modules by path from `absWorkingDir`; a `node_modules` linked from outside
|
|
274
|
+
* the app makes those names climb to the build machine's absolute path. The quoted prefix to rewrite.
|
|
275
|
+
*/
|
|
276
|
+
function machineModuleKeyPrefix(root: string) {
|
|
277
|
+
let real: string
|
|
278
|
+
try {
|
|
279
|
+
real = realpathSync(path.join(root, 'node_modules'))
|
|
280
|
+
} catch {
|
|
281
|
+
return undefined
|
|
282
|
+
}
|
|
283
|
+
const relative = toPosixPath(path.relative(root, real))
|
|
284
|
+
return relative.startsWith('../') ? `"${relative}/` : undefined
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export async function linkReleaseDependencies(
|
|
288
|
+
config: ResolvedConfig,
|
|
289
|
+
layer: ReleaseDependencyLayer,
|
|
290
|
+
entries: readonly { in: string; out: string }[],
|
|
291
|
+
outdir: string,
|
|
292
|
+
) {
|
|
293
|
+
const [target, conditionTarget] = layer.split(':') as [CompatAliasTarget, ServerBundleTarget]
|
|
294
|
+
const unresolved = new Set<string>()
|
|
295
|
+
const inPlace = new Set<string>()
|
|
296
|
+
const ordered = [...entries].sort((a, b) => (a.out < b.out ? -1 : 1))
|
|
297
|
+
let linked = ordered
|
|
298
|
+
let outputs: { path: string; text: string }[]
|
|
299
|
+
for (;;) {
|
|
300
|
+
let result
|
|
301
|
+
try {
|
|
302
|
+
result = await build({
|
|
303
|
+
entryPoints: linked.map(entry => ({
|
|
304
|
+
in:
|
|
305
|
+
getBundlerExtensions().serverBundleEntry(entry.in, path.dirname(entry.in), entry.in) ??
|
|
306
|
+
entry.in,
|
|
307
|
+
out: entry.out,
|
|
308
|
+
})),
|
|
309
|
+
absWorkingDir: config.root,
|
|
310
|
+
bundle: true,
|
|
311
|
+
splitting: true,
|
|
312
|
+
write: false,
|
|
313
|
+
metafile: true,
|
|
314
|
+
outdir,
|
|
315
|
+
chunkNames: 'chunks/[name]-[hash]',
|
|
316
|
+
format: 'esm',
|
|
317
|
+
platform: 'neutral',
|
|
318
|
+
target: 'es2022',
|
|
319
|
+
conditions: serverBundleConditions(conditionTarget),
|
|
320
|
+
mainFields: ['module', 'main'],
|
|
321
|
+
loader: packageJsxLoaders,
|
|
322
|
+
jsx: 'automatic',
|
|
323
|
+
jsxImportSource: 'preact',
|
|
324
|
+
logLevel: 'silent',
|
|
325
|
+
...serverDefineOptions(),
|
|
326
|
+
plugins: profiledVendorPlugins([
|
|
327
|
+
serverAssetPlugin(config),
|
|
328
|
+
...getBundlerExtensions().serverEsbuildPlugins(
|
|
329
|
+
config,
|
|
330
|
+
vendorPluginOptions(config, target, conditionTarget),
|
|
331
|
+
),
|
|
332
|
+
runtimeAliasBuildPlugin(config, target, {
|
|
333
|
+
bundleRequireAliases: true,
|
|
334
|
+
reactServerLayer: target === 'server',
|
|
335
|
+
}),
|
|
336
|
+
releaseExternalsPlugin(config, conditionTarget, unresolved, inPlace),
|
|
337
|
+
]),
|
|
338
|
+
})
|
|
339
|
+
} catch (error) {
|
|
340
|
+
// An import that resolves nowhere stays a bare external, as in a vendor bundle: it fails when it runs.
|
|
341
|
+
const missing = unresolvedBareImports(error).filter(specifier => !unresolved.has(specifier))
|
|
342
|
+
if (missing.length === 0) throw error
|
|
343
|
+
for (const specifier of missing) unresolved.add(specifier)
|
|
344
|
+
continue
|
|
345
|
+
}
|
|
346
|
+
const found = (await inPlacePackages(config, result)).filter(root => !inPlace.has(root))
|
|
347
|
+
if (found.length === 0) {
|
|
348
|
+
outputs = result.outputFiles
|
|
349
|
+
break
|
|
350
|
+
}
|
|
351
|
+
for (const root of found) inPlace.add(root)
|
|
352
|
+
linked = ordered.filter(entry => !inPlace.has(nearestManifestDir(path.dirname(entry.in))!))
|
|
353
|
+
if (linked.length === 0) return { files: [], inPlace: ordered.map(entry => entry.in) }
|
|
354
|
+
}
|
|
355
|
+
const entryFiles = new Set(linked.map(entry => path.join(outdir, `${entry.out}.js`)))
|
|
356
|
+
const texts = new Map(outputs.map(output => [output.path, output.text]))
|
|
357
|
+
// Written beside the graph and renamed into place, so a crash never leaves half a graph.
|
|
358
|
+
const staging = `${outdir}.${process.pid}.tmp`
|
|
359
|
+
await rm(staging, { recursive: true, force: true })
|
|
360
|
+
try {
|
|
361
|
+
await mkdir(path.join(staging, 'chunks'), { recursive: true })
|
|
362
|
+
const keys = machineModuleKeyPrefix(config.root)
|
|
363
|
+
for (const output of outputs) {
|
|
364
|
+
const text = keys ? output.text.replaceAll(keys, '"node_modules/') : output.text
|
|
365
|
+
const code = entryFiles.has(output.path)
|
|
366
|
+
? addCommonJsNamedExports(text, sharedCommonJsBody(output.path, text, texts))
|
|
367
|
+
: text
|
|
368
|
+
await writeFile(
|
|
369
|
+
path.join(staging, path.relative(outdir, output.path)),
|
|
370
|
+
releaseArtifactSource(
|
|
371
|
+
output.path,
|
|
372
|
+
target === 'server' ? applyBundledSourceTransforms(code, output.path) : code,
|
|
373
|
+
),
|
|
374
|
+
)
|
|
375
|
+
}
|
|
376
|
+
await rm(outdir, { recursive: true, force: true })
|
|
377
|
+
await rename(staging, outdir)
|
|
378
|
+
} catch (error) {
|
|
379
|
+
await rm(staging, { recursive: true, force: true })
|
|
380
|
+
throw error
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
files: outputs.map(output => output.path).sort(),
|
|
384
|
+
inPlace: ordered.filter(entry => !linked.includes(entry)).map(entry => entry.in),
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// `import.meta` paths and an `import()` of a computed path resolve where the file lies; an import marked
|
|
389
|
+
// `webpackIgnore`/`@vite-ignore` is meant to stay a runtime import, as Next's bundle keeps it.
|
|
390
|
+
/** Reads its own location or imports a computed path (unless it opts out with a magic comment). */
|
|
391
|
+
function readsBesideItself(file: string, source: string) {
|
|
392
|
+
const refs = importMetaRefs(file, source)
|
|
393
|
+
if (refs.readsLocation) return true
|
|
394
|
+
return refs.computedImports.some(
|
|
395
|
+
call => !/webpackIgnore:\s*true|@vite-ignore/.test(source.slice(call.start, call.end)),
|
|
396
|
+
)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** Package dirs whose files read beside themselves or import computed paths. */
|
|
400
|
+
async function inPlacePackages(
|
|
401
|
+
config: ResolvedConfig,
|
|
402
|
+
result: { metafile?: Metafile; warnings: { text: string; location: { file: string } | null }[] },
|
|
403
|
+
) {
|
|
404
|
+
const files = new Set(
|
|
405
|
+
result.warnings
|
|
406
|
+
.filter(warning => warning.text.includes('is not a string literal') && warning.location)
|
|
407
|
+
.map(warning => path.resolve(config.root, warning.location!.file)),
|
|
408
|
+
)
|
|
409
|
+
const installed = Object.keys(result.metafile?.inputs ?? {})
|
|
410
|
+
.filter(input => !input.includes(':') && input.includes('node_modules/'))
|
|
411
|
+
.map(input => path.resolve(config.root, input))
|
|
412
|
+
await Promise.all(
|
|
413
|
+
installed.map(async file => {
|
|
414
|
+
const source = await readFile(file, 'utf8').catch(() => '')
|
|
415
|
+
if (readsBesideItself(file, source)) files.add(file)
|
|
416
|
+
}),
|
|
417
|
+
)
|
|
418
|
+
return [
|
|
419
|
+
...new Set(
|
|
420
|
+
[...files]
|
|
421
|
+
.filter(file => file.includes(`${path.sep}node_modules${path.sep}`))
|
|
422
|
+
.map(file => nearestManifestDir(path.dirname(canonicalVendorEntry(file)!)))
|
|
423
|
+
.filter((root): root is string => root !== undefined),
|
|
424
|
+
),
|
|
425
|
+
]
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Under splitting, a CommonJS entry's body can land in a shared chunk the entry only calls
|
|
430
|
+
* (`export default require_x()`): its named exports are read from that body.
|
|
431
|
+
*/
|
|
432
|
+
function sharedCommonJsBody(file: string, code: string, texts: ReadonlyMap<string, string>) {
|
|
433
|
+
const wrapper = /(?:^|\n)export default (require_[\w$]+)\(\);?/.exec(code)?.[1]
|
|
434
|
+
// The chunk that defines the wrapper, from the parsed imports: `$` in its name is no regex hazard.
|
|
435
|
+
const chunk =
|
|
436
|
+
wrapper && scanFacts(file, code).imports.find(edge => edge.exports.includes(wrapper))?.specifier
|
|
437
|
+
const text = chunk && texts.get(path.resolve(path.dirname(file), chunk))
|
|
438
|
+
if (!text) return code
|
|
439
|
+
const start = text.indexOf(`var ${wrapper} = `)
|
|
440
|
+
if (start === -1) return code
|
|
441
|
+
const next = text.indexOf('\nvar require_', start + 1)
|
|
442
|
+
return text.slice(start, next === -1 ? undefined : next)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const frameworkPackageName = (
|
|
446
|
+
JSON.parse(readFileSync(path.resolve(import.meta.dirname, '../../package.json'), 'utf8')) as {
|
|
447
|
+
name: string
|
|
448
|
+
}
|
|
449
|
+
).name
|
|
450
|
+
|
|
451
|
+
function releaseExternalsPlugin(
|
|
452
|
+
config: ResolvedConfig,
|
|
453
|
+
conditionTarget: ServerBundleTarget,
|
|
454
|
+
unresolved: ReadonlySet<string>,
|
|
455
|
+
inPlace: ReadonlySet<string>,
|
|
456
|
+
): Plugin {
|
|
457
|
+
const policy = getExternalPackagePolicy()
|
|
458
|
+
return {
|
|
459
|
+
name: 'pnext-release-externals',
|
|
460
|
+
setup(build) {
|
|
461
|
+
build.onResolve({ filter: /^[^./]/ }, async args => {
|
|
462
|
+
const name = packageNameFromSpecifier(args.path)
|
|
463
|
+
if (!name || args.kind === 'entry-point') return undefined
|
|
464
|
+
// Builtins, unresolvable imports and the framework (the runtime's own instance) stay external.
|
|
465
|
+
if (
|
|
466
|
+
unresolved.has(args.path) ||
|
|
467
|
+
name === frameworkPackageName ||
|
|
468
|
+
isBuiltin(args.path) ||
|
|
469
|
+
builtinModules.includes(name) ||
|
|
470
|
+
isBunBuiltin(name)
|
|
471
|
+
) {
|
|
472
|
+
return { path: args.path, external: true }
|
|
473
|
+
}
|
|
474
|
+
const conditions =
|
|
475
|
+
args.kind === 'require-call'
|
|
476
|
+
? serverBundleRequireConditions(conditionTarget)
|
|
477
|
+
: serverBundleConditions(conditionTarget)
|
|
478
|
+
const external = policy.external(name) || policy.releaseExternal?.(name)
|
|
479
|
+
if (!external && inPlace.size === 0) return undefined
|
|
480
|
+
const fromImporter = resolveNestedPackageFromImporter(args.importer, args.path, conditions)
|
|
481
|
+
// A package that loads in place is imported where it lies, when Bun loads it as-is.
|
|
482
|
+
const loaded = fromImporter && canonicalVendorEntry(fromImporter)!
|
|
483
|
+
if (
|
|
484
|
+
loaded &&
|
|
485
|
+
inPlace.has(nearestManifestDir(path.dirname(loaded))!) &&
|
|
486
|
+
isEsmModuleEntry(loaded) &&
|
|
487
|
+
(await nativeServerGraph(loaded))
|
|
488
|
+
) {
|
|
489
|
+
return { path: loaded, external: true }
|
|
490
|
+
}
|
|
491
|
+
if (!external) return undefined
|
|
492
|
+
const fromRoot = resolveVendorPackageSpecifier(
|
|
493
|
+
config.root,
|
|
494
|
+
path.join(config.root, 'pnext-resolve.ts'),
|
|
495
|
+
args.path,
|
|
496
|
+
conditions,
|
|
497
|
+
)
|
|
498
|
+
return fromImporter && fromImporter === fromRoot
|
|
499
|
+
? { path: args.path, external: true }
|
|
500
|
+
: undefined
|
|
501
|
+
})
|
|
502
|
+
},
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function unresolvedBareImports(error: unknown) {
|
|
507
|
+
const errors = (error as { errors?: { text?: string }[] } | undefined)?.errors ?? []
|
|
508
|
+
return errors.flatMap(({ text }) => {
|
|
509
|
+
const specifier = /^Could not resolve "([^"]+)"/.exec(text ?? '')?.[1]
|
|
510
|
+
return specifier && isPackageSpecifier(specifier) ? [specifier] : []
|
|
511
|
+
})
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// --------------------------------------------------------------------------
|
|
515
|
+
// native server dependencies
|
|
516
|
+
// --------------------------------------------------------------------------
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* On the server a node_modules package loads natively through Bun, as Vite SSR and Node load it: one
|
|
520
|
+
* module instance per resolved file and no build. A bundle per subpath would inline its own copy of
|
|
521
|
+
* every shared internal file and split module state (better-auth's request-state slots).
|
|
522
|
+
*
|
|
523
|
+
* It is bundled only when pnext has to transform something its server graph reaches: an aliased
|
|
524
|
+
* import (react, next/*), a `'use client'`/`'use server'`/`'use cache'` file, a non-JS file, a
|
|
525
|
+
* `transpilePackages` member, or a bare import Bun resolves to a different file than the server
|
|
526
|
+
* layer's runtime conditions (`react-server`) select. `PNEXT_SERVER_NATIVE=0` bundles everything.
|
|
527
|
+
*/
|
|
528
|
+
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
529
|
+
const serverNativeEnabled = () => process.env.PNEXT_SERVER_NATIVE !== '0'
|
|
530
|
+
|
|
531
|
+
/** The server layer's conditions minus bundler-only ones (`module`): what Node would honour. */
|
|
532
|
+
const serverRuntimeConditions = (kind: string) =>
|
|
533
|
+
(kind === 'require-call'
|
|
534
|
+
? serverBundleRequireConditions('server')
|
|
535
|
+
: serverBundleConditions('server')
|
|
536
|
+
).filter(condition => condition !== 'module')
|
|
537
|
+
|
|
538
|
+
const nativeServerEntries = new Map<string, Promise<string | undefined>>()
|
|
539
|
+
|
|
540
|
+
/** The file Bun loads for `specifier` when its whole server graph needs no transform. */
|
|
541
|
+
function nativeServerEntry(config: ResolvedConfig, specifier: string, resolveDir: string) {
|
|
542
|
+
const packageName = packageNameFromSpecifier(specifier)
|
|
543
|
+
if (
|
|
544
|
+
!serverNativeEnabled() ||
|
|
545
|
+
serverDefineOptions().define ||
|
|
546
|
+
(packageName && getExternalPackagePolicy().transpile(packageName))
|
|
547
|
+
) {
|
|
548
|
+
return Promise.resolve(undefined)
|
|
549
|
+
}
|
|
550
|
+
const key = `${resolveDir}\0${specifier}`
|
|
551
|
+
let entry = nativeServerEntries.get(key)
|
|
552
|
+
if (!entry) {
|
|
553
|
+
const verdicts = nativeVerdictsFor(config)
|
|
554
|
+
const storeKey = `${vendorPortablePath(config, resolveDir)}\0${specifier}`
|
|
555
|
+
const stored = verdicts.get(storeKey)
|
|
556
|
+
const storedEntry = stored && path.resolve(config.workspaceRoot, stored.resolved)
|
|
557
|
+
if (stored && statSignature(storedEntry!) === stored.signature) {
|
|
558
|
+
entry = Promise.resolve(stored.native ? storedEntry : undefined)
|
|
559
|
+
} else {
|
|
560
|
+
entry = nativeServerEntryUncached(config, specifier, resolveDir)
|
|
561
|
+
.then(native => {
|
|
562
|
+
const resolved = nativeResolve(specifier, resolveDir)
|
|
563
|
+
const signature = resolved && statSignature(resolved)
|
|
564
|
+
if (resolved && signature) {
|
|
565
|
+
const relative = path.relative(config.workspaceRoot, resolved)
|
|
566
|
+
verdicts.set(storeKey, { resolved: relative, signature, native: Boolean(native) })
|
|
567
|
+
persistNativeVerdicts()
|
|
568
|
+
}
|
|
569
|
+
return native
|
|
570
|
+
})
|
|
571
|
+
.catch(() => undefined)
|
|
572
|
+
}
|
|
573
|
+
nativeServerEntries.set(key, entry)
|
|
574
|
+
}
|
|
575
|
+
return entry
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Verdicts outlive the process, as vendor artifacts do: re-walking every native graph made a dev
|
|
580
|
+
* restart pay for all of them again. One holds while its resolved entry's stat does; the store name
|
|
581
|
+
* carries what else a verdict reads (pnext's generation, `transpilePackages`, the alias set).
|
|
582
|
+
*/
|
|
583
|
+
interface NativeVerdict {
|
|
584
|
+
resolved: string
|
|
585
|
+
signature: string
|
|
586
|
+
native: boolean
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const nativeVerdictStores = new Map<
|
|
590
|
+
string,
|
|
591
|
+
{ file: string; verdicts: Map<string, NativeVerdict> }
|
|
592
|
+
>()
|
|
593
|
+
let nativeVerdictFlushArmed = false
|
|
594
|
+
|
|
595
|
+
function nativeVerdictsFor(config: ResolvedConfig) {
|
|
596
|
+
let store = nativeVerdictStores.get(config.outPath)
|
|
597
|
+
if (!store) {
|
|
598
|
+
const inputs = JSON.stringify([
|
|
599
|
+
getExternalPackagePolicy().transpiled?.() ?? null,
|
|
600
|
+
Object.keys(coreAliases(config, 'server')).sort(),
|
|
601
|
+
])
|
|
602
|
+
const file = vendorStoreFile(config, `native.${hashBundleSpecifier(inputs)}`)
|
|
603
|
+
store = { file, verdicts: new Map() }
|
|
604
|
+
nativeVerdictStores.set(config.outPath, store)
|
|
605
|
+
try {
|
|
606
|
+
const stored = JSON.parse(readFileSync(file, 'utf8')) as Record<string, NativeVerdict>
|
|
607
|
+
for (const [key, verdict] of Object.entries(stored)) store.verdicts.set(key, verdict)
|
|
608
|
+
} catch {
|
|
609
|
+
// No store yet; verdicts are written at exit.
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return store.verdicts
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function persistNativeVerdicts() {
|
|
616
|
+
if (nativeVerdictFlushArmed) return
|
|
617
|
+
nativeVerdictFlushArmed = true
|
|
618
|
+
process.once('exit', () => {
|
|
619
|
+
for (const { file, verdicts } of nativeVerdictStores.values()) {
|
|
620
|
+
try {
|
|
621
|
+
// Another process (a build's warm child) may have written since this one loaded.
|
|
622
|
+
const stored = JSON.parse(readFileSync(file, 'utf8')) as Record<string, NativeVerdict>
|
|
623
|
+
for (const [key, verdict] of Object.entries(stored)) {
|
|
624
|
+
if (!verdicts.has(key)) verdicts.set(key, verdict)
|
|
625
|
+
}
|
|
626
|
+
} catch {
|
|
627
|
+
// Nothing on disk yet.
|
|
628
|
+
}
|
|
629
|
+
try {
|
|
630
|
+
mkdirSync(path.dirname(file), { recursive: true })
|
|
631
|
+
writeFileSync(file, JSON.stringify(Object.fromEntries(verdicts)))
|
|
632
|
+
} catch {
|
|
633
|
+
// A missing store only costs the walk.
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
})
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
async function nativeServerEntryUncached(
|
|
640
|
+
config: ResolvedConfig,
|
|
641
|
+
specifier: string,
|
|
642
|
+
resolveDir: string,
|
|
643
|
+
) {
|
|
644
|
+
const importer = path.join(resolveDir, 'pnext-resolve.ts')
|
|
645
|
+
const entry = nativeResolve(specifier, resolveDir)
|
|
646
|
+
// A CommonJS entry keeps its vendor bundle: its importers' interop (named-export facades,
|
|
647
|
+
// `require` of the artifact) is built around one.
|
|
648
|
+
if (!entry || !isEsmModuleEntry(entry)) return undefined
|
|
649
|
+
if (nativeImportDiffers(importer, specifier, 'import-statement', entry)) return undefined
|
|
650
|
+
const graph = await nativeServerGraph(entry)
|
|
651
|
+
if (!graph) return undefined
|
|
652
|
+
recordNativeNodeModuleFiles(config, graph)
|
|
653
|
+
return entry
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function nativeResolve(specifier: string, fromDir: string) {
|
|
657
|
+
try {
|
|
658
|
+
return canonicalVendorEntry(Bun.resolveSync(specifier, fromDir))
|
|
659
|
+
} catch {
|
|
660
|
+
return undefined
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Whether the server layer selects another file than Bun loaded, with or without `react-server`:
|
|
666
|
+
* every pnext process runs with that condition, but a native graph must not depend on it.
|
|
667
|
+
*/
|
|
668
|
+
function nativeImportDiffers(importer: string, specifier: string, kind: string, loaded: string) {
|
|
669
|
+
const conditions = serverRuntimeConditions(kind)
|
|
670
|
+
return [conditions, conditions.filter(condition => condition !== 'react-server')].some(set => {
|
|
671
|
+
const selected = resolveNestedPackageFromImporter(importer, specifier, set)
|
|
672
|
+
return selected !== undefined && canonicalVendorEntry(selected) !== loaded
|
|
673
|
+
})
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/** Per file: what it loads natively, or undefined when it needs a transform. */
|
|
677
|
+
const nativeServerFiles = new Map<string, Promise<string[] | undefined>>()
|
|
678
|
+
|
|
679
|
+
function nativeServerFile(file: string) {
|
|
680
|
+
let imports = nativeServerFiles.get(file)
|
|
681
|
+
if (!imports) {
|
|
682
|
+
imports = nativeServerFileUncached(file).catch(() => undefined)
|
|
683
|
+
nativeServerFiles.set(file, imports)
|
|
684
|
+
}
|
|
685
|
+
return imports
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const nativeImportScanner = new Bun.Transpiler({ loader: 'js' })
|
|
689
|
+
|
|
690
|
+
async function nativeServerFileUncached(file: string) {
|
|
691
|
+
// An isolated store (`.pnpm`, `.bun`) is a layout the deployed function does not mirror.
|
|
692
|
+
if (!/\.[cm]?js$/.test(file) || file.includes(`${path.sep}node_modules${path.sep}.`)) {
|
|
693
|
+
return undefined
|
|
694
|
+
}
|
|
695
|
+
const source = await readFile(file, 'utf8')
|
|
696
|
+
// The runtime load plugin's own test: a file it would rewrite is not native.
|
|
697
|
+
if (!nodeModuleSourceLoadsAsIs(source)) return undefined
|
|
698
|
+
const policy = getExternalPackagePolicy()
|
|
699
|
+
const loads: string[] = []
|
|
700
|
+
for (const { path: specifier, kind } of nativeImportScanner.scanImports(source)) {
|
|
701
|
+
if (isBuiltin(specifier) || isBunBuiltin(specifier)) continue
|
|
702
|
+
const packageName = isPackageSpecifier(specifier)
|
|
703
|
+
? packageNameFromSpecifier(specifier)
|
|
704
|
+
: undefined
|
|
705
|
+
// A serverExternalPackages import already loads natively through the runtime resolver.
|
|
706
|
+
if (packageName && policy.external(packageName)) continue
|
|
707
|
+
if (packageName && policy.transpile(packageName)) return undefined
|
|
708
|
+
// Unresolvable fails the same way bundled (a bare external), so it decides nothing.
|
|
709
|
+
const loaded = nativeResolve(specifier, path.dirname(file))
|
|
710
|
+
if (!loaded) continue
|
|
711
|
+
if (packageName && nativeImportDiffers(file, specifier, kind, loaded)) return undefined
|
|
712
|
+
loads.push(loaded)
|
|
713
|
+
}
|
|
714
|
+
return loads
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
async function nativeServerGraph(entry: string) {
|
|
718
|
+
const seen = new Set<string>()
|
|
719
|
+
let level = [entry]
|
|
720
|
+
while (level.length > 0) {
|
|
721
|
+
const fresh = [...new Set(level)].filter(file => !seen.has(file))
|
|
722
|
+
for (const file of fresh) seen.add(file)
|
|
723
|
+
const loads = await Promise.all(fresh.map(nativeServerFile))
|
|
724
|
+
if (loads.some(found => !found)) return undefined
|
|
725
|
+
level = loads.flat() as string[]
|
|
726
|
+
}
|
|
727
|
+
return seen
|
|
162
728
|
}
|
|
163
729
|
|
|
164
730
|
/**
|
|
@@ -196,7 +762,12 @@ const vendorPortablePath = (config: ResolvedConfig, file: string) =>
|
|
|
196
762
|
devSourceIdentity(file, config.workspaceRoot)
|
|
197
763
|
|
|
198
764
|
function vendorArtifactFile(config: ResolvedConfig, key: string) {
|
|
199
|
-
return path.join(
|
|
765
|
+
return path.join(
|
|
766
|
+
outCachePath(config.outPath),
|
|
767
|
+
'server',
|
|
768
|
+
'vendor',
|
|
769
|
+
`${hashBundleSpecifier(key)}.mjs`,
|
|
770
|
+
)
|
|
200
771
|
}
|
|
201
772
|
|
|
202
773
|
/**
|
|
@@ -295,7 +866,7 @@ function vendorBuildPlan(
|
|
|
295
866
|
async function writeVendorArtifact(file: string, code: string) {
|
|
296
867
|
const id = await publishVendorContent(file, code)
|
|
297
868
|
if (id === undefined) {
|
|
298
|
-
await writeFileAtomic(file, code)
|
|
869
|
+
await writeFileAtomic(file, releaseArtifactSource(file, code))
|
|
299
870
|
noteDevArtifactWritten(file)
|
|
300
871
|
}
|
|
301
872
|
return id
|
|
@@ -316,9 +887,17 @@ function vendorContentFile(vendorDir: string, id: string) {
|
|
|
316
887
|
async function publishVendorContent(file: string, code: string) {
|
|
317
888
|
const vendorDir = path.dirname(file)
|
|
318
889
|
if (vendorContentDisabled() || path.basename(vendorDir) !== 'vendor') return undefined
|
|
890
|
+
// First writer wins (see `linkVendorArtifact`): an alias already published keeps its bytes.
|
|
891
|
+
const published = /^([0-9a-f]+)\.content\.mjs$/.exec(publishedVendorContent(file) ?? '')?.[1]
|
|
892
|
+
if (published) {
|
|
893
|
+
noteVendorContentId(file, published)
|
|
894
|
+
noteDevArtifactWritten(file)
|
|
895
|
+
return published
|
|
896
|
+
}
|
|
319
897
|
const id = hashBundleSpecifier(canonicalVendorCode(code))
|
|
320
898
|
const content = vendorContentFile(vendorDir, id)
|
|
321
|
-
if (!cachedExistsSync(content))
|
|
899
|
+
if (!cachedExistsSync(content))
|
|
900
|
+
await writeFileAtomic(content, releaseArtifactSource(content, code))
|
|
322
901
|
noteDevArtifactWritten(content)
|
|
323
902
|
await linkVendorArtifact(file, content)
|
|
324
903
|
noteVendorContentId(file, id)
|
|
@@ -326,16 +905,40 @@ async function publishVendorContent(file: string, code: string) {
|
|
|
326
905
|
return id
|
|
327
906
|
}
|
|
328
907
|
|
|
329
|
-
|
|
908
|
+
function publishedVendorContent(file: string) {
|
|
909
|
+
try {
|
|
910
|
+
return existsSync(file) ? readlinkSync(file) : undefined
|
|
911
|
+
} catch {
|
|
912
|
+
return undefined
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* The layer-keyed path becomes an alias to the shared bytes. The first writer wins: a build and its
|
|
918
|
+
* warm child both publish, and re-pointing a live alias hands later importers a second module
|
|
919
|
+
* instance (the loader keys on the real path). Only a dangling alias is replaced, by rename, so a
|
|
920
|
+
* reader never sees it missing.
|
|
921
|
+
*/
|
|
330
922
|
async function linkVendorArtifact(file: string, content: string) {
|
|
331
|
-
|
|
923
|
+
const link = (at: string) => symlink(path.basename(content), at)
|
|
924
|
+
try {
|
|
925
|
+
await link(file)
|
|
926
|
+
return
|
|
927
|
+
} catch (error) {
|
|
928
|
+
if ((error as NodeJS.ErrnoException).code === 'EEXIST' && existsSync(file)) return
|
|
929
|
+
}
|
|
930
|
+
const temp = `${file}.${process.pid.toString(36)}.${(++vendorLinkSequence).toString(36)}.link`
|
|
332
931
|
try {
|
|
333
|
-
await
|
|
932
|
+
await link(temp)
|
|
933
|
+
await rename(temp, file)
|
|
334
934
|
} catch {
|
|
935
|
+
await rm(temp, { force: true }).catch(() => undefined)
|
|
335
936
|
await copyFile(content, file)
|
|
336
937
|
}
|
|
337
938
|
}
|
|
338
939
|
|
|
940
|
+
let vendorLinkSequence = 0
|
|
941
|
+
|
|
339
942
|
/** What one layer's build of an entry saw, for the cross-layer comparison. */
|
|
340
943
|
interface VendorLayerRecord {
|
|
341
944
|
inputs: string
|
|
@@ -463,7 +1066,7 @@ function reusableVendorContent(
|
|
|
463
1066
|
loadVendorSharedEntries(config)
|
|
464
1067
|
const id = vendorSharedFor(config).get(entry)
|
|
465
1068
|
if (!id) return undefined
|
|
466
|
-
const content = vendorContentFile(path.join(config.outPath, '
|
|
1069
|
+
const content = vendorContentFile(path.join(outCachePath(config.outPath), 'server', 'vendor'), id)
|
|
467
1070
|
return existsSync(content) ? content : undefined
|
|
468
1071
|
}
|
|
469
1072
|
|
|
@@ -1030,9 +1633,23 @@ function vendorGroupPlan(
|
|
|
1030
1633
|
const packageName = packageNameFromSpecifier(member.specifier)
|
|
1031
1634
|
const packageRoot = packageName && vendorPackageRoot(member.entry, packageName)
|
|
1032
1635
|
if (!packageName || !packageRoot || !isEsmModuleEntry(member.entry)) return undefined
|
|
1636
|
+
const key = `${config.outPath}\0${target}\0${conditionTarget}\0${packageRoot}`
|
|
1033
1637
|
return {
|
|
1034
|
-
key
|
|
1638
|
+
key,
|
|
1035
1639
|
member,
|
|
1640
|
+
companions:
|
|
1641
|
+
target === 'server'
|
|
1642
|
+
? () =>
|
|
1643
|
+
stateSharingCompanions(
|
|
1644
|
+
config,
|
|
1645
|
+
key,
|
|
1646
|
+
packageName,
|
|
1647
|
+
packageRoot,
|
|
1648
|
+
member,
|
|
1649
|
+
resolveDir,
|
|
1650
|
+
conditionTarget,
|
|
1651
|
+
)
|
|
1652
|
+
: undefined,
|
|
1036
1653
|
build: (members, external, nested) =>
|
|
1037
1654
|
buildVendorGroup(
|
|
1038
1655
|
config,
|
|
@@ -1047,6 +1664,119 @@ function vendorGroupPlan(
|
|
|
1047
1664
|
}
|
|
1048
1665
|
}
|
|
1049
1666
|
|
|
1667
|
+
/**
|
|
1668
|
+
* A package bundled on the server keeps one instance of a stateful file only if every subpath
|
|
1669
|
+
* reaching it compiles in one splitting build. So the first demand pulls in the published subpaths
|
|
1670
|
+
* that share a stateful file with it (better-auth's `defineRequestState(...)` slots, a React
|
|
1671
|
+
* context), and nothing else: most packages load natively, and a build of every published subpath
|
|
1672
|
+
* would also run code the app never imports.
|
|
1673
|
+
*/
|
|
1674
|
+
function stateSharingCompanions(
|
|
1675
|
+
config: ResolvedConfig,
|
|
1676
|
+
groupKey: string,
|
|
1677
|
+
packageName: string,
|
|
1678
|
+
packageRoot: string,
|
|
1679
|
+
member: VendorGroupMember,
|
|
1680
|
+
resolveDir: string,
|
|
1681
|
+
conditionTarget: ServerBundleTarget,
|
|
1682
|
+
) {
|
|
1683
|
+
const key = `${groupKey}\0${member.specifier}`
|
|
1684
|
+
let companions = stateCompanions.get(key)
|
|
1685
|
+
if (!companions) {
|
|
1686
|
+
companions = (async () => {
|
|
1687
|
+
const shared = await statefulPackageFiles(member.entry, packageRoot)
|
|
1688
|
+
if (shared.length === 0) return []
|
|
1689
|
+
const found: VendorGroupMember[] = []
|
|
1690
|
+
for (const subpath of packageExportSubpaths(packageRoot)) {
|
|
1691
|
+
const specifier = subpath === '.' ? packageName : `${packageName}${subpath.slice(1)}`
|
|
1692
|
+
if (specifier === member.specifier) continue
|
|
1693
|
+
const sibling = vendorBuildPlan(
|
|
1694
|
+
config,
|
|
1695
|
+
specifier,
|
|
1696
|
+
'server',
|
|
1697
|
+
resolveDir,
|
|
1698
|
+
conditionTarget,
|
|
1699
|
+
).group
|
|
1700
|
+
if (sibling?.key !== groupKey || existsSync(sibling.member.file)) continue
|
|
1701
|
+
const reach = await packageReach(sibling.member.entry, packageRoot)
|
|
1702
|
+
if (shared.some(file => reach.has(file))) found.push(sibling.member)
|
|
1703
|
+
}
|
|
1704
|
+
return found.length <= MAX_STATE_COMPANIONS ? found : []
|
|
1705
|
+
})().catch(() => [])
|
|
1706
|
+
stateCompanions.set(key, companions)
|
|
1707
|
+
}
|
|
1708
|
+
return companions
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
/** Past this many, the package is a hub of adapters; it keeps a build per demanded subpath. */
|
|
1712
|
+
const MAX_STATE_COMPANIONS = 16
|
|
1713
|
+
const stateCompanions = new Map<string, Promise<VendorGroupMember[]>>()
|
|
1714
|
+
|
|
1715
|
+
function packageExportSubpaths(packageRoot: string) {
|
|
1716
|
+
try {
|
|
1717
|
+
const { exports } = JSON.parse(
|
|
1718
|
+
readFileSync(path.join(packageRoot, 'package.json'), 'utf8'),
|
|
1719
|
+
) as {
|
|
1720
|
+
exports?: unknown
|
|
1721
|
+
}
|
|
1722
|
+
if (!exports || typeof exports !== 'object' || Array.isArray(exports)) return []
|
|
1723
|
+
return Object.keys(exports).filter(
|
|
1724
|
+
subpath => subpath.startsWith('.') && !subpath.includes('*') && !subpath.endsWith('.json'),
|
|
1725
|
+
)
|
|
1726
|
+
} catch {
|
|
1727
|
+
return []
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
/** A top-level binding created by `new` or a non-PURE call: an identity each copy would duplicate. */
|
|
1732
|
+
async function statefulPackageFiles(entry: string, packageRoot: string) {
|
|
1733
|
+
const files = [...(await packageReach(entry, packageRoot))]
|
|
1734
|
+
const sources = await Promise.all(files.map(file => packageFileScan(file)))
|
|
1735
|
+
return files.filter((_, index) => sources[index]?.stateful)
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1738
|
+
const packageFileScans = new Map<
|
|
1739
|
+
string,
|
|
1740
|
+
Promise<{ imports: string[]; stateful: boolean } | undefined>
|
|
1741
|
+
>()
|
|
1742
|
+
const packageScanner = new Bun.Transpiler({ loader: 'js' })
|
|
1743
|
+
|
|
1744
|
+
function packageFileScan(file: string) {
|
|
1745
|
+
let scan = packageFileScans.get(file)
|
|
1746
|
+
if (!scan) {
|
|
1747
|
+
scan = readFile(file, 'utf8')
|
|
1748
|
+
.then(source => ({
|
|
1749
|
+
imports: packageScanner
|
|
1750
|
+
.scanImports(source)
|
|
1751
|
+
.map(({ path: specifier }) => specifier)
|
|
1752
|
+
.filter(specifier => specifier.startsWith('.')),
|
|
1753
|
+
stateful: hasModuleState(file, source),
|
|
1754
|
+
}))
|
|
1755
|
+
.catch(() => undefined)
|
|
1756
|
+
packageFileScans.set(file, scan)
|
|
1757
|
+
}
|
|
1758
|
+
return scan
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
/** The package's own files `entry` reaches through relative imports. */
|
|
1762
|
+
async function packageReach(entry: string, packageRoot: string) {
|
|
1763
|
+
const seen = new Set<string>()
|
|
1764
|
+
let level = [entry]
|
|
1765
|
+
while (level.length > 0) {
|
|
1766
|
+
const fresh = [...new Set(level)].filter(
|
|
1767
|
+
file => !seen.has(file) && file.startsWith(packageRoot + path.sep) && /\.[cm]?js$/.test(file),
|
|
1768
|
+
)
|
|
1769
|
+
for (const file of fresh) seen.add(file)
|
|
1770
|
+
const scans = await Promise.all(fresh.map(packageFileScan))
|
|
1771
|
+
level = scans.flatMap((scan, index) =>
|
|
1772
|
+
(scan?.imports ?? []).flatMap(
|
|
1773
|
+
specifier => nativeResolve(specifier, path.dirname(fresh[index]!)) ?? [],
|
|
1774
|
+
),
|
|
1775
|
+
)
|
|
1776
|
+
}
|
|
1777
|
+
return seen
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1050
1780
|
/**
|
|
1051
1781
|
* The installed package directory an entry belongs to — the grouping identity,
|
|
1052
1782
|
* so a nested (non-hoisted) copy never shares a group with the hoisted one.
|
|
@@ -2666,9 +3396,12 @@ function externalBuiltinBuildPlugin(): Plugin {
|
|
|
2666
3396
|
setup(build) {
|
|
2667
3397
|
build.onResolve({ filter: /^[^./].*/ }, args => {
|
|
2668
3398
|
const packageName = packageNameFromSpecifier(args.path)
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
3399
|
+
// `isBuiltin` also knows the `node:`-only ones (`node:sqlite`, `node:test`).
|
|
3400
|
+
const builtin =
|
|
3401
|
+
isBuiltin(args.path) ||
|
|
3402
|
+
builtinModules.includes(packageName ?? '') ||
|
|
3403
|
+
isBunBuiltin(packageName ?? '')
|
|
3404
|
+
if (!packageName || !builtin) return undefined
|
|
2672
3405
|
return { path: args.path, external: true }
|
|
2673
3406
|
})
|
|
2674
3407
|
},
|