@wular/pnext 0.0.3 → 0.0.6
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/README.md +11 -11
- package/bin/pnext +1 -1
- package/config/lint/base.js +7 -7
- package/config/ts/base.json +2 -4
- package/config/ts/react.json +2 -6
- package/package.json +23 -2
- package/reference/compat.md +1 -1
- package/reference/config.md +2 -2
- package/reference/css.md +13 -9
- package/reference/dev.md +1 -1
- package/reference/metadata.md +7 -7
- package/reference/navigation.md +35 -28
- package/reference/performance.md +70 -70
- package/reference/rendering.md +17 -17
- package/reference/routing.md +17 -17
- package/reference/typegen.md +12 -8
- package/src/api/cache.ts +36 -37
- package/src/api/client-cache.ts +2 -2
- package/src/api/client-navigation.ts +113 -109
- package/src/api/dynamic.tsx +58 -55
- package/src/api/link.tsx +49 -52
- package/src/api/navigation.ts +59 -58
- package/src/api/server.ts +152 -144
- package/src/api/suspense.ts +4 -4
- package/src/cli/adapters/vercel-warm.ts +126 -121
- package/src/cli/adapters/vercel.ts +437 -443
- package/src/cli/analyze.ts +333 -144
- package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
- package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
- package/src/cli/build.ts +976 -978
- package/src/cli/create.ts +71 -63
- package/src/cli/dev.ts +116 -116
- package/src/cli/index.ts +88 -85
- package/src/cli/migrate/package-json.ts +73 -71
- package/src/cli/migrate/report.ts +32 -33
- package/src/cli/migrate/{index.ts → run.ts} +48 -49
- package/src/cli/migrate/scan.ts +46 -46
- package/src/cli/migrate/spinner.ts +9 -9
- package/src/cli/migrate/tsconfig.ts +35 -35
- package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
- package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
- package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
- package/src/cli/start.ts +65 -66
- package/src/{typegen.ts → cli/typegen.ts} +59 -59
- package/src/client/build.ts +794 -768
- package/src/client/chunk-fold.ts +245 -240
- package/src/client/{paths.ts → chunk-name.ts} +6 -6
- package/src/client/entry.ts +162 -147
- package/src/client/prebuilt.ts +231 -223
- package/src/client/profile.ts +29 -29
- package/src/client/react-compiler.ts +20 -15
- package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
- package/src/client/reference-stub.ts +51 -51
- package/src/client/reference.ts +19 -19
- package/src/{api → client}/router/events.ts +17 -17
- package/src/{api → client}/router/history.ts +16 -16
- package/src/{api → client}/router/hub.ts +52 -53
- package/src/{api/router.ts → client/router/index.ts} +61 -60
- package/src/{api → client}/router/policies.ts +24 -24
- package/src/{api → client}/router/runtime.ts +1988 -1961
- package/src/{api → client}/router/types.ts +93 -98
- package/src/compat/actions/client-plugin.ts +42 -43
- package/src/compat/actions/client-stub.ts +14 -14
- package/src/compat/actions/{action-client.ts → client.ts} +194 -193
- package/src/compat/actions/config.ts +63 -64
- package/src/compat/actions/detect.ts +68 -68
- package/src/compat/actions/discovery.ts +105 -105
- package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
- package/src/compat/actions/early-submit.ts +1 -1
- package/src/compat/actions/endpoint.ts +198 -198
- package/src/compat/actions/extensions.ts +811 -0
- package/src/compat/actions/flight.ts +23 -23
- package/src/compat/actions/form-state.ts +34 -34
- package/src/compat/actions/hoist.ts +382 -236
- package/src/compat/actions/ids.ts +7 -12
- package/src/compat/actions/index.ts +8 -8
- package/src/compat/actions/instances.ts +46 -46
- package/src/compat/actions/origin.ts +47 -48
- package/src/compat/actions/protocol.ts +22 -22
- package/src/compat/actions/registry.ts +19 -19
- package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
- package/src/compat/actions/rewrite.ts +125 -125
- package/src/compat/actions/{action-router.ts → router.ts} +10 -10
- package/src/compat/actions/serve.ts +132 -136
- package/src/compat/actions/server-tag.ts +4 -4
- package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
- package/src/compat/actions/unrecognized-error.ts +4 -4
- package/src/compat/{index.ts → aliases.ts} +112 -109
- package/src/compat/bundler/bun-externals.ts +18 -18
- package/src/compat/bundler/cjs-exports.ts +271 -223
- package/src/compat/bundler/config.ts +116 -111
- package/src/compat/bundler/externals.ts +12 -12
- package/src/compat/bundler/import-meta-url.ts +19 -19
- package/src/compat/bundler/modularize-imports.ts +36 -33
- package/src/compat/bundler/new-url-asset.ts +22 -24
- package/src/compat/bundler/optimize-package-imports.ts +111 -107
- package/src/compat/bundler/polyfill.ts +28 -28
- package/src/compat/bundler/react-compiler.ts +35 -29
- package/src/compat/bundler/react-profiler.tsx +11 -11
- package/src/compat/bundler/relay-transform.ts +48 -47
- package/src/compat/bundler/require-context.ts +119 -113
- package/src/compat/bundler/resolve-extensions.ts +19 -19
- package/src/compat/bundler/source-cache.ts +25 -25
- package/src/compat/bundler/static-imports.ts +7 -7
- package/src/compat/bundler/symlink-imports.ts +46 -46
- package/src/compat/bundler/tsconfig-paths.ts +13 -15
- package/src/compat/bundler/wasm.ts +58 -60
- package/src/compat/bundler/webpack-loaders.ts +254 -241
- package/src/compat/bundler/worker.ts +101 -104
- package/src/compat/cache/build-flags.ts +29 -29
- package/src/compat/cache/build-prerender-errors.ts +40 -44
- package/src/compat/cache/custom-handler.ts +60 -53
- package/src/compat/cache/fetch-patch.ts +240 -240
- package/src/compat/cache/handler.ts +27 -31
- package/src/compat/cache/modern-handler.ts +148 -126
- package/src/compat/cache/resume-data-cache.ts +47 -45
- package/src/compat/cache/revalidate.ts +233 -232
- package/src/compat/cache/runtime-error.ts +35 -35
- package/src/compat/cache/use-cache-transform.ts +442 -417
- package/src/compat/cache/use-cache.ts +650 -614
- package/src/compat/cache-control.ts +140 -142
- package/src/compat/client/base-path.ts +21 -20
- package/src/compat/client/css-order.ts +18 -18
- package/src/compat/client/errors/bare-boundary.ts +11 -11
- package/src/compat/client/errors/control-flow.ts +23 -23
- package/src/compat/client/errors/error-boundary.ts +61 -61
- package/src/compat/client/errors/global-error.ts +101 -91
- package/src/compat/client/errors/install.ts +71 -72
- package/src/compat/client/errors/lazy.ts +23 -23
- package/src/compat/client/errors/primitive-throw.ts +47 -45
- package/src/compat/client/errors/soft-refresh.ts +4 -4
- package/src/compat/client/link-status.ts +33 -33
- package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
- package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
- package/src/compat/client/navigation-scroll.ts +63 -63
- package/src/compat/client/optimistic-routing.ts +93 -88
- package/src/compat/client/prefetch-cache.ts +23 -24
- package/src/compat/client/route-announcer.ts +35 -35
- package/src/compat/client/segment-cache-policy.ts +20 -20
- package/src/compat/client/segment-cache.ts +309 -315
- package/src/compat/client/segment-prefetch.ts +127 -132
- package/src/compat/client/trailing-slash.ts +5 -4
- package/src/compat/css/chunking.ts +113 -116
- package/src/compat/css/inline-css.ts +21 -21
- package/src/compat/css/lightningcss.ts +37 -38
- package/src/compat/css/modules.ts +161 -175
- package/src/compat/css/nonce.ts +7 -7
- package/src/compat/css/sass-plugin.ts +18 -21
- package/src/compat/css/sass.ts +150 -152
- package/src/compat/css/styled-jsx-runtime.ts +27 -27
- package/src/compat/css/styled-jsx.ts +21 -21
- package/src/compat/edge-runtime.ts +27 -27
- package/src/compat/{adapter → export}/build-complete.ts +72 -75
- package/src/compat/export/client.ts +29 -31
- package/src/compat/export/{index.ts → emit.ts} +111 -110
- package/src/compat/export/standalone.ts +62 -54
- package/src/compat/image-optimizer/cache.ts +51 -49
- package/src/compat/image-optimizer/detect.ts +52 -52
- package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
- package/src/compat/image-optimizer/source.ts +77 -80
- package/src/compat/lifecycle/after-scope.ts +26 -26
- package/src/compat/lifecycle/after.ts +48 -45
- package/src/compat/lifecycle/error-funnel.ts +98 -102
- package/src/compat/lifecycle/error-serialize.ts +30 -26
- package/src/compat/lifecycle/error-ui.ts +67 -32
- package/src/compat/lifecycle/instrumentation-client.ts +36 -38
- package/src/compat/lifecycle/instrumentation.ts +85 -85
- package/src/compat/lifecycle/node-console.ts +11 -11
- package/src/compat/lifecycle/testmode.ts +160 -132
- package/src/compat/mdx/compile.ts +60 -57
- package/src/compat/mdx/plugin.ts +11 -11
- package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
- package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
- package/src/compat/metadata.ts +75 -81
- package/src/compat/next/cache.ts +68 -69
- package/src/compat/next/canonical-url.ts +9 -9
- package/src/compat/next/client-cache.ts +19 -19
- package/src/compat/next/client-navigation.ts +112 -116
- package/src/compat/next/client-only.ts +1 -1
- package/src/compat/next/client-script.tsx +99 -93
- package/src/compat/next/client-server.ts +10 -10
- package/src/compat/next/config-loader.ts +176 -173
- package/src/compat/next/config.ts +7 -7
- package/src/compat/next/constants.cjs +6 -6
- package/src/compat/next/constants.ts +6 -6
- package/src/compat/next/custom-server.ts +26 -24
- package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
- package/src/compat/next/dynamic.tsx +21 -18
- package/src/compat/next/error.tsx +52 -52
- package/src/compat/next/font/cache.ts +74 -74
- package/src/compat/next/font/google.ts +2 -2
- package/src/compat/next/font/index.ts +1 -1
- package/src/compat/next/font/local.ts +3 -3
- package/src/compat/next/font/runtime-client.ts +17 -15
- package/src/compat/next/font/runtime.ts +443 -408
- package/src/compat/next/font/shared.ts +120 -108
- package/src/compat/next/form.tsx +63 -63
- package/src/compat/next/head.tsx +2 -2
- package/src/compat/next/headers.ts +103 -95
- package/src/compat/next/image/client.tsx +220 -0
- package/src/compat/next/image/config.ts +56 -58
- package/src/compat/next/image/optimizer.ts +40 -36
- package/src/compat/next/image/patterns.ts +37 -40
- package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
- package/src/compat/next/image/shared.ts +65 -57
- package/src/compat/next/image/static-metadata.ts +98 -107
- package/src/compat/next/image/validate.ts +79 -88
- package/src/compat/next/image.tsx +19 -23
- package/src/compat/next/index.ts +1 -1
- package/src/compat/next/legacy-image.tsx +59 -60
- package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
- package/src/compat/next/link.tsx +158 -158
- package/src/compat/next/navigation.cjs +12 -3
- package/src/compat/next/navigation.ts +48 -50
- package/src/compat/next/offline.ts +27 -27
- package/src/compat/next/og.ts +121 -124
- package/src/compat/next/preferred-region.ts +13 -14
- package/src/compat/next/redirects.ts +58 -56
- package/src/compat/next/resource-hints.ts +73 -76
- package/src/compat/next/rewrites.ts +130 -133
- package/src/compat/next/root-params.ts +45 -45
- package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
- package/src/compat/next/router.cjs +4 -2
- package/src/compat/next/router.ts +58 -61
- package/src/compat/next/script.tsx +108 -108
- package/src/compat/next/server-only.ts +1 -1
- package/src/compat/next/server.ts +19 -19
- package/src/compat/next/svgr.ts +18 -17
- package/src/compat/next/telemetry.ts +24 -24
- package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
- package/src/compat/next/user-agent.ts +53 -49
- package/src/compat/next/web-vitals.ts +22 -24
- package/src/compat/otel/api.ts +41 -41
- package/src/compat/otel/client-trace-metadata.ts +25 -27
- package/src/compat/otel/fetch-span.ts +29 -29
- package/src/compat/otel/tracer.ts +331 -331
- package/src/compat/pages/api.ts +456 -0
- package/src/compat/pages/client-plugin.ts +36 -36
- package/src/compat/pages/router-state.ts +34 -34
- package/src/compat/pages/{index.ts → router.ts} +130 -135
- package/src/compat/ppr/io.ts +12 -12
- package/src/compat/ppr/missing-root-params.ts +34 -38
- package/src/compat/ppr/root-params-scan.ts +66 -66
- package/src/compat/ppr/root-params-transform.ts +24 -26
- package/src/compat/ppr/root-params.ts +30 -30
- package/src/compat/ppr/segment-config-incompat.ts +6 -7
- package/src/compat/protocol.ts +71 -70
- package/src/compat/react/action-state.ts +47 -48
- package/src/compat/react/client-lite.ts +15 -15
- package/src/compat/react/client.ts +4 -4
- package/src/compat/react/compiler-runtime.ts +11 -11
- package/src/compat/react/dom-client.ts +44 -44
- package/src/compat/react/dom-react-server.ts +10 -16
- package/src/compat/react/dom-server.ts +10 -10
- package/src/compat/react/dom.ts +52 -52
- package/src/compat/react/hooks-extra.ts +34 -35
- package/src/compat/react/parity.ts +64 -61
- package/src/compat/react/preact.ts +81 -82
- package/src/compat/react/react-server.ts +28 -28
- package/src/compat/react/router-shim.ts +1 -1
- package/src/compat/react/server-component-use.ts +8 -8
- package/src/compat/react/server-inserted-html.ts +30 -31
- package/src/compat/react/server.ts +53 -55
- package/src/compat/react/use.ts +32 -32
- package/src/compat/react/view-transition.ts +20 -20
- package/src/compat/register/actions.ts +35 -824
- package/src/compat/register/boot.ts +41 -41
- package/src/compat/register/build-tier.ts +5 -5
- package/src/compat/register/build.ts +74 -70
- package/src/compat/register/bundler.ts +161 -156
- package/src/compat/register/cache.ts +20 -20
- package/src/compat/register/client-errors.ts +3 -3
- package/src/compat/register/config.ts +6 -6
- package/src/compat/register/css-extras.ts +30 -34
- package/src/compat/register/edge-runtime.ts +3 -3
- package/src/compat/register/errors.ts +10 -12
- package/src/compat/register/export.ts +16 -16
- package/src/compat/register/font.ts +11 -11
- package/src/compat/register/hooks.ts +2 -2
- package/src/compat/register/image.ts +40 -40
- package/src/compat/register/index.ts +59 -62
- package/src/compat/register/instrumentation-client.ts +10 -10
- package/src/compat/register/lifecycle.ts +25 -28
- package/src/compat/register/mdx.ts +10 -10
- package/src/compat/register/middleware.ts +226 -16
- package/src/compat/register/otel.ts +80 -88
- package/src/compat/register/pages-api.ts +10 -463
- package/src/compat/register/ppr.ts +16 -16
- package/src/compat/register/protocol.ts +17 -18
- package/src/compat/register/proxy.ts +49 -51
- package/src/compat/register/render.ts +79 -76
- package/src/compat/register/routing.ts +162 -159
- package/src/compat/register/segment.ts +24 -1887
- package/src/compat/register/static-image.ts +3 -3
- package/src/compat/register/{misc.ts → taint.ts} +9 -9
- package/src/compat/register/typed-routes.ts +14 -14
- package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
- package/src/compat/register/validation.ts +18 -18
- package/src/compat/segment/loading-boundary.ts +43 -45
- package/src/compat/segment/page-slot.ts +69 -69
- package/src/compat/segment/serve.ts +1884 -0
- package/src/compat/segment/tree.ts +113 -112
- package/src/compat/segment/vary-key.ts +38 -38
- package/src/compat/segment/vary-params.ts +138 -142
- package/src/compat/static-params.ts +14 -12
- package/src/compat/tsconfig-defaults.ts +87 -91
- package/src/compat/typecheck/{index.ts → check.ts} +234 -212
- package/src/compat/typecheck/worker.ts +15 -12
- package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
- package/src/compat/typed-routes/manifest.ts +174 -170
- package/src/compat/typed-routes/typegen.ts +127 -110
- package/src/compat/validation/errors.ts +16 -19
- package/src/compat/validation/prerender-diagnostics.ts +521 -504
- package/src/compat/validation/{index.ts → validate.ts} +647 -648
- package/src/compat-bootstrap.ts +16 -16
- package/src/config.ts +69 -69
- package/src/css/build.ts +230 -227
- package/src/css/postcss.ts +79 -80
- package/src/css/worker.ts +14 -15
- package/src/dev/client-actions.ts +10 -10
- package/src/dev/client-chunk-store.ts +27 -27
- package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
- package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
- package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
- package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
- package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
- package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
- package/src/dev/server.ts +804 -820
- package/src/env.ts +46 -43
- package/src/extensions.ts +491 -478
- package/src/index.ts +8 -8
- package/src/internal.ts +20 -23
- package/src/{islands → render}/boundary-error.ts +3 -3
- package/src/render/hooks.ts +71 -71
- package/src/render/island-context.ts +14 -14
- package/src/render/metadata.ts +310 -310
- package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
- package/src/{ppr.ts → render/ppr.ts} +244 -245
- package/src/render/renderer.ts +2076 -2082
- package/src/render/resource-hints.ts +16 -17
- package/src/render/slots.tsx +224 -235
- package/src/{islands → render}/static-children.ts +9 -12
- package/src/{islands → render}/static-slots.ts +37 -37
- package/src/{cache/context.ts → request/cache.ts} +20 -20
- package/src/request/context.ts +107 -107
- package/src/{dynamic/source.ts → resolve/dynamic.ts} +152 -143
- package/src/resolve/engine.ts +90 -77
- package/src/resolve/imports.ts +475 -463
- package/src/resolve/scan-facts.ts +442 -186
- package/src/resolve/source-text.ts +37 -37
- package/src/{dynamic → resolve}/tree-shake.ts +132 -128
- package/src/routing/forwarded.ts +19 -19
- package/src/routing/handler.ts +84 -91
- package/src/routing/href.ts +69 -70
- package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
- package/src/{proxy.ts → routing/proxy.ts} +306 -312
- package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
- package/src/routing/routes.ts +833 -848
- package/src/routing/slots.ts +164 -160
- package/src/runtime/loader.ts +952 -0
- package/src/{dev → runtime}/module-cache.ts +309 -287
- package/src/{dev → runtime}/module-generations.ts +9 -9
- package/src/{dev → runtime}/module-transform.ts +81 -72
- package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
- package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
- package/src/runtime/vendor.ts +425 -404
- package/src/styles.d.ts +9 -0
- package/src/types.ts +320 -335
- package/src/utils/ansi.ts +5 -5
- package/src/utils/{source.ts → code.ts} +15 -12
- package/src/utils/content-type.ts +3 -3
- package/src/utils/decode.ts +2 -2
- package/src/utils/dev-profile.ts +13 -13
- package/src/utils/error-log.ts +6 -6
- package/src/utils/esbuild.ts +18 -18
- package/src/utils/fs-cache.ts +13 -13
- package/src/utils/fs.ts +57 -49
- package/src/utils/html.ts +20 -24
- package/src/utils/native-require.ts +8 -8
- package/src/utils/serialize.ts +139 -146
- package/src/utils/verbose.ts +18 -18
- package/src/cli/analyze-print.ts +0 -181
- package/src/compat/middleware/manifest.ts +0 -210
- package/src/compat/next/image-client.tsx +0 -215
- package/src/compat/next/link-usage.ts +0 -29
- package/src/css/index.ts +0 -2
- package/src/render/index.ts +0 -1
- package/src/style-modules.d.ts +0 -9
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ESM loader: Bun module-resolve/load plugin registration for the app's own source (and its
|
|
3
|
+
* node_modules-relative requires), plus the on-demand transform pipeline (source rewrites, CJS
|
|
4
|
+
* sidecar generation) that feeds compiled code to those plugins. The alias-resolution and
|
|
5
|
+
* asset-stub helpers here are shared with vendor package bundling in `./vendor-build`, which
|
|
6
|
+
* imports them; this module never imports from `./vendor-build`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createHash } from 'node:crypto'
|
|
10
|
+
import { createRequire } from 'node:module'
|
|
11
|
+
import { existsSync, realpathSync } from 'node:fs'
|
|
12
|
+
import { mkdir, readFile, stat, writeFile } from 'node:fs/promises'
|
|
13
|
+
import path from 'node:path'
|
|
14
|
+
import { fileURLToPath } from 'node:url'
|
|
15
|
+
import type { Loader, Plugin } from 'esbuild'
|
|
16
|
+
import { build, transform } from '../utils/esbuild'
|
|
17
|
+
import {
|
|
18
|
+
applyBundledSourceTransforms,
|
|
19
|
+
applyServerSourcePreTransforms,
|
|
20
|
+
applyServerSourceTransforms,
|
|
21
|
+
extraLoadableExtensions,
|
|
22
|
+
getAssetExtensions,
|
|
23
|
+
getBundlerExtensions,
|
|
24
|
+
getCssExtensions,
|
|
25
|
+
getImportAliasExtensions,
|
|
26
|
+
onLoadableExtensionsChanged,
|
|
27
|
+
serverDefineOptions,
|
|
28
|
+
sourceNeedsServerTransforms,
|
|
29
|
+
} from '../extensions'
|
|
30
|
+
import {
|
|
31
|
+
frameworkRuntimeAliasEntries,
|
|
32
|
+
pathToFileHref,
|
|
33
|
+
pnextAliases,
|
|
34
|
+
type CompatAliasTarget,
|
|
35
|
+
type ResolvedConfig,
|
|
36
|
+
} from '../config'
|
|
37
|
+
import {
|
|
38
|
+
rewriteDynamicCallTargets,
|
|
39
|
+
rewriteLiteralDynamicCalls,
|
|
40
|
+
sourceHasDynamicImport,
|
|
41
|
+
} from '../resolve/dynamic'
|
|
42
|
+
import { cacheRoot } from './module-cache'
|
|
43
|
+
import { readNodeModuleBundle, writeNodeModuleBundle } from '../dev/restart/node-modules'
|
|
44
|
+
import { setBasePathPrefix, setTrailingSlashUrls } from '../routing/href'
|
|
45
|
+
import { resolveExternalLoadTarget, resolveImport, workspacePackageRoots } from '../resolve/imports'
|
|
46
|
+
import { escapeRegex } from '../utils/code'
|
|
47
|
+
import { writeFileAtomic } from '../utils/fs'
|
|
48
|
+
|
|
49
|
+
const require = createRequire(import.meta.url)
|
|
50
|
+
|
|
51
|
+
// B4/B5: extra esbuild resolve conditions layered onto the server (RSC) vendor
|
|
52
|
+
// bundle. Core always applies `react-server` (the server graph IS the RSC
|
|
53
|
+
// layer). Compat can add `next-js` (only when cacheComponents is on) via the
|
|
54
|
+
// setter. Kept as a module-level seam so core carries no static edge into
|
|
55
|
+
// compat's config reader.
|
|
56
|
+
export type ServerBundleTarget =
|
|
57
|
+
CompatAliasTarget | 'edge' | 'pages-api' | 'pages-api-edge' | 'pages' | 'pages-edge'
|
|
58
|
+
|
|
59
|
+
let extraServerBundleConditions: (target: ServerBundleTarget) => string[] = () => []
|
|
60
|
+
|
|
61
|
+
/** Install compat-driven extra vendor-bundle conditions (B5 `next-js`). */
|
|
62
|
+
export function setServerBundleConditions(
|
|
63
|
+
factory: ((target: ServerBundleTarget) => string[]) | undefined,
|
|
64
|
+
): void {
|
|
65
|
+
extraServerBundleConditions = factory ?? (() => [])
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function serverBundleTargetForRuntime(runtime: string | undefined): ServerBundleTarget {
|
|
69
|
+
return runtime === 'edge' || runtime === 'experimental-edge' ? 'edge' : 'server'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function pagesApiBundleTargetForRuntime(runtime: string | undefined): ServerBundleTarget {
|
|
73
|
+
return runtime === 'edge' || runtime === 'experimental-edge' ? 'pages-api-edge' : 'pages-api'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Pages-router PAGE SSR layer: Next compiles it in the server bundle with `node` (or
|
|
78
|
+
* `edge-light`/`browser` for edge) export conditions but WITHOUT `react-server` - a pages page is
|
|
79
|
+
* not part of the RSC layer, so `library/react`-style react-server splits must resolve to `default`.
|
|
80
|
+
*/
|
|
81
|
+
export function pagesBundleTargetForRuntime(runtime: string | undefined): ServerBundleTarget {
|
|
82
|
+
return runtime === 'edge' || runtime === 'experimental-edge' ? 'pages-edge' : 'pages'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function serverBundleConditions(target: ServerBundleTarget): string[] {
|
|
86
|
+
// Preserve the original client-target conditions (module/import) to avoid
|
|
87
|
+
// regressing the browser SSR vendor bundle; only the RSC/server layer gains
|
|
88
|
+
// `react-server`. Compat-added conditions (next-js) prepend for either target.
|
|
89
|
+
const base =
|
|
90
|
+
target === 'client'
|
|
91
|
+
? ['module', 'import']
|
|
92
|
+
: target === 'edge'
|
|
93
|
+
? ['react-server', 'edge-light', 'browser', 'module', 'import']
|
|
94
|
+
: target === 'pages-api-edge'
|
|
95
|
+
? ['edge-light', 'browser', 'module', 'import']
|
|
96
|
+
: target === 'pages-api'
|
|
97
|
+
? ['node', 'require', 'import']
|
|
98
|
+
: target === 'pages-edge'
|
|
99
|
+
? ['edge-light', 'browser', 'module', 'import']
|
|
100
|
+
: target === 'pages'
|
|
101
|
+
? ['node', 'module', 'import']
|
|
102
|
+
: ['react-server', 'node', 'module', 'import']
|
|
103
|
+
return [...new Set([...extraServerBundleConditions(target), ...base])]
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function serverBundleRequireConditions(target: ServerBundleTarget): string[] {
|
|
107
|
+
const base =
|
|
108
|
+
target === 'client'
|
|
109
|
+
? ['module', 'require']
|
|
110
|
+
: target === 'edge'
|
|
111
|
+
? ['react-server', 'edge-light', 'browser', 'module', 'require']
|
|
112
|
+
: target === 'pages-api-edge'
|
|
113
|
+
? ['edge-light', 'browser', 'module', 'require']
|
|
114
|
+
: target === 'pages-api'
|
|
115
|
+
? ['node', 'require']
|
|
116
|
+
: target === 'pages-edge'
|
|
117
|
+
? ['edge-light', 'browser', 'module', 'require']
|
|
118
|
+
: target === 'pages'
|
|
119
|
+
? ['node', 'module', 'require']
|
|
120
|
+
: ['react-server', 'node', 'module', 'require']
|
|
121
|
+
return [...new Set([...extraServerBundleConditions(target), ...base])]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const runtimeConfigs = new Map<string, RuntimeConfig>()
|
|
125
|
+
const aliasCache = new WeakMap<ResolvedConfig, Map<string, string>>()
|
|
126
|
+
const packageRootCache = new WeakMap<ResolvedConfig, string[]>()
|
|
127
|
+
const registeredSignatures = new WeakMap<ResolvedConfig, Set<string>>()
|
|
128
|
+
const rootPathCache = new Map<string, string[]>()
|
|
129
|
+
const runtimeConfigLookupCache = new Map<string, RuntimeConfig | null>()
|
|
130
|
+
const firstAliasCache = new Map<string, string | null>()
|
|
131
|
+
const transformCache = new Map<string, TransformCacheEntry>()
|
|
132
|
+
const nodeModuleTransformCache = new Map<string, TransformCacheEntry>()
|
|
133
|
+
// Freshness of generated `.pnext-require.cjs` sidecars (see inlineModuleScopeRequires).
|
|
134
|
+
const requireSidecarCache = new Map<string, { mtimeMs: number; size: number }>()
|
|
135
|
+
const registeredLoadRoots = new Set<string>()
|
|
136
|
+
let resolveRegistered = false
|
|
137
|
+
let nodeModulesLoadRegistered = false
|
|
138
|
+
// A live Bun.plugin forces every later import through the plugin pipeline (and
|
|
139
|
+
// pnext's own src through transformSource), so importing the compat graph with
|
|
140
|
+
// the plugins already up costs ~5x. build/start load compat before they ever
|
|
141
|
+
// register a runtime; dev arms the plugins on the first request instead.
|
|
142
|
+
let pluginsDeferred = false
|
|
143
|
+
const deferredPluginRoots = new Set<string>()
|
|
144
|
+
|
|
145
|
+
export const packageJsxLoaders = {
|
|
146
|
+
'.js': 'jsx',
|
|
147
|
+
'.mjs': 'jsx',
|
|
148
|
+
'.cjs': 'jsx',
|
|
149
|
+
} satisfies Record<string, Loader>
|
|
150
|
+
|
|
151
|
+
interface RuntimeConfig {
|
|
152
|
+
root: string
|
|
153
|
+
roots: Set<string>
|
|
154
|
+
aliases: Map<string, string>
|
|
155
|
+
/** Full resolved config, so compile sites can invoke config-aware extensions. */
|
|
156
|
+
resolved: ResolvedConfig
|
|
157
|
+
missingImportError: (specifier: string) => string | undefined
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface TransformCacheEntry {
|
|
161
|
+
mtimeMs: number
|
|
162
|
+
size: number
|
|
163
|
+
code: string
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function registerServerRuntime(config: ResolvedConfig, sourceFiles: string[] = []) {
|
|
167
|
+
// Server-rendered Links emit canonical (slashed) hrefs under trailingSlash.
|
|
168
|
+
setTrailingSlashUrls(Boolean(config.trailingSlash))
|
|
169
|
+
// File-convention metadata asset hrefs (og-image, manifest) carry the
|
|
170
|
+
// basePath prefix; core render reads it through the href seam.
|
|
171
|
+
setBasePathPrefix(typeof config.basePath === 'string' ? config.basePath : '')
|
|
172
|
+
if (typeof Bun === 'undefined') return
|
|
173
|
+
const sourceRoots = [...new Set(sourceFiles.map(file => sourceRootForFile(config, file)))].sort()
|
|
174
|
+
const signature = sourceRoots.join('\0')
|
|
175
|
+
let registered = registeredSignatures.get(config)
|
|
176
|
+
if (!registered) {
|
|
177
|
+
registered = new Set()
|
|
178
|
+
registeredSignatures.set(config, registered)
|
|
179
|
+
}
|
|
180
|
+
if (registered.has(signature)) return
|
|
181
|
+
registered.add(signature)
|
|
182
|
+
|
|
183
|
+
const aliases = aliasesForConfig(config)
|
|
184
|
+
const roots = [
|
|
185
|
+
...rootPaths(config.appPath),
|
|
186
|
+
...rootPaths(path.join(config.outPath, 'cache', 'server')),
|
|
187
|
+
// The pnext src root, so the framework's own source transforms too.
|
|
188
|
+
...rootPaths(path.join(import.meta.dirname, '..')),
|
|
189
|
+
...sourceRoots.flatMap(rootPaths),
|
|
190
|
+
]
|
|
191
|
+
const key = rootPaths(config.appPath).join('\0')
|
|
192
|
+
const missingImportError = (specifier: string) =>
|
|
193
|
+
getImportAliasExtensions().missingImportError(config, specifier)
|
|
194
|
+
const existing = runtimeConfigs.get(key)
|
|
195
|
+
if (existing) {
|
|
196
|
+
for (const root of roots) existing.roots.add(root)
|
|
197
|
+
for (const [specifier, target] of aliases) existing.aliases.set(specifier, target)
|
|
198
|
+
existing.resolved = config
|
|
199
|
+
existing.missingImportError = missingImportError
|
|
200
|
+
} else {
|
|
201
|
+
runtimeConfigs.set(key, {
|
|
202
|
+
root: config.root,
|
|
203
|
+
roots: new Set(roots),
|
|
204
|
+
aliases: new Map(aliases),
|
|
205
|
+
resolved: config,
|
|
206
|
+
missingImportError,
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
runtimeConfigLookupCache.clear()
|
|
210
|
+
firstAliasCache.clear()
|
|
211
|
+
if (pluginsDeferred) {
|
|
212
|
+
for (const root of roots) deferredPluginRoots.add(root)
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
registerResolvePlugin()
|
|
216
|
+
registerNodeModulesLoadPlugin()
|
|
217
|
+
for (const root of roots) registerLoadPlugin(root)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Hold the Bun module plugins back until installServerRuntimePlugins() runs, so
|
|
222
|
+
* whatever the process still has to import of its own code loads natively.
|
|
223
|
+
*/
|
|
224
|
+
export function deferServerRuntimePlugins(): void {
|
|
225
|
+
pluginsDeferred = true
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Arm the plugins deferServerRuntimePlugins() held back. Idempotent, cheap. */
|
|
229
|
+
export function installServerRuntimePlugins(): void {
|
|
230
|
+
if (!pluginsDeferred) return
|
|
231
|
+
pluginsDeferred = false
|
|
232
|
+
registerResolvePlugin()
|
|
233
|
+
registerNodeModulesLoadPlugin()
|
|
234
|
+
for (const root of deferredPluginRoots) registerLoadPlugin(root)
|
|
235
|
+
deferredPluginRoots.clear()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function resolveRuntimeTarget(target: string) {
|
|
239
|
+
return path.isAbsolute(target) ? target : require.resolve(target)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function aliasesForConfig(config: ResolvedConfig) {
|
|
243
|
+
let aliases = aliasCache.get(config)
|
|
244
|
+
if (!aliases) {
|
|
245
|
+
aliases = new Map<string, string>()
|
|
246
|
+
for (const [specifier, target] of Object.entries(coreAliases(config, 'server'))) {
|
|
247
|
+
aliases.set(specifier, resolveRuntimeTarget(target))
|
|
248
|
+
}
|
|
249
|
+
aliasCache.set(config, aliases)
|
|
250
|
+
}
|
|
251
|
+
return aliases
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function coreAliases(
|
|
255
|
+
config: ResolvedConfig,
|
|
256
|
+
target: CompatAliasTarget,
|
|
257
|
+
): Record<string, string> {
|
|
258
|
+
return {
|
|
259
|
+
...frameworkRuntimeAliasEntries(),
|
|
260
|
+
...pnextAliases(target),
|
|
261
|
+
...getImportAliasExtensions().aliases(config, target),
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export const aliasSpecifierFilter =
|
|
266
|
+
/^(?:@wular\/pnext\/cache|preact(?:\/|$)|react(?:-dom)?(?:\/|$)|react-compiler-runtime$|next(?:\/|$)|server-only$|client-only$|@vercel\/og$)/
|
|
267
|
+
|
|
268
|
+
function registerResolvePlugin() {
|
|
269
|
+
if (resolveRegistered) return
|
|
270
|
+
resolveRegistered = true
|
|
271
|
+
Bun.plugin({
|
|
272
|
+
name: 'pnext-server-runtime-resolve',
|
|
273
|
+
setup(plugin) {
|
|
274
|
+
plugin.onResolve({ filter: aliasSpecifierFilter }, args => {
|
|
275
|
+
const runtime = runtimeConfigForFile(args.importer)
|
|
276
|
+
const resolved = runtime?.aliases.get(args.path) ?? firstAliasForSpecifier(args.path)
|
|
277
|
+
const target = resolved ? serverRequireAlias(args.path, resolved, args.kind) : undefined
|
|
278
|
+
if (target) return { path: target }
|
|
279
|
+
const message = (runtime ?? firstRuntimeConfig())?.missingImportError(args.path)
|
|
280
|
+
if (message) throw new Error(message)
|
|
281
|
+
return undefined
|
|
282
|
+
})
|
|
283
|
+
plugin.onResolve({ filter: /^[^./].*/ }, ({ path: specifier, importer }) => {
|
|
284
|
+
const runtime = runtimeConfigForFile(importer)
|
|
285
|
+
const target =
|
|
286
|
+
runtime &&
|
|
287
|
+
resolveExternalLoadTarget({
|
|
288
|
+
root: runtime.root,
|
|
289
|
+
fromFile: importer,
|
|
290
|
+
specifier,
|
|
291
|
+
target: 'server',
|
|
292
|
+
})
|
|
293
|
+
if (!target) return undefined
|
|
294
|
+
return { path: target }
|
|
295
|
+
})
|
|
296
|
+
},
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function registerLoadPlugin(root: string) {
|
|
301
|
+
if (registeredLoadRoots.has(root)) return
|
|
302
|
+
registeredLoadRoots.add(root)
|
|
303
|
+
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
304
|
+
if (process.env.PNEXT_DEBUG_LOADPLUGIN) {
|
|
305
|
+
console.error(`[loadplugin] register root=${root} extras=${currentLoadExtras().join(',')}`)
|
|
306
|
+
}
|
|
307
|
+
loadPluginExtras.set(root, currentLoadExtras())
|
|
308
|
+
Bun.plugin({
|
|
309
|
+
name: `pnext-server-runtime-load-${hashRoot(root)}`,
|
|
310
|
+
setup(plugin) {
|
|
311
|
+
plugin.onLoad({ filter: rootFilter(root) }, async ({ path: file }) => ({
|
|
312
|
+
contents: await transformSource(file),
|
|
313
|
+
loader: 'js',
|
|
314
|
+
}))
|
|
315
|
+
},
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// The load plugin's filter regex latches the page extensions known at registration time. A root registered
|
|
320
|
+
// BEFORE compat adds `mdx`/`md` (boot order shifts under load) would let those modules fall through to Bun's
|
|
321
|
+
// file loader, whose default export is the file path and renders as a bogus JSX tag - so a late registration
|
|
322
|
+
// extends every latched root with an extras-only plugin.
|
|
323
|
+
const loadPluginExtras = new Map<string, string[]>()
|
|
324
|
+
|
|
325
|
+
function currentLoadExtras(): string[] {
|
|
326
|
+
return extraLoadableExtensions().filter(ext => /^[a-z0-9]+$/i.test(ext))
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
onLoadableExtensionsChanged(() => {
|
|
330
|
+
for (const [root, seen] of loadPluginExtras) {
|
|
331
|
+
const fresh = currentLoadExtras().filter(ext => !seen.includes(ext))
|
|
332
|
+
if (fresh.length === 0) continue
|
|
333
|
+
loadPluginExtras.set(root, [...seen, ...fresh])
|
|
334
|
+
const exts = fresh.map(escapeRegex).join('|')
|
|
335
|
+
const filter = new RegExp(
|
|
336
|
+
`^${escapeRegex(root)}(?!.*\\/node_modules\\/)(?:/.*)?\\.(?:${exts})$`,
|
|
337
|
+
)
|
|
338
|
+
Bun.plugin({
|
|
339
|
+
name: `pnext-server-runtime-load-extras-${hashRoot(root)}-${fresh.join('-')}`,
|
|
340
|
+
setup(plugin) {
|
|
341
|
+
plugin.onLoad({ filter }, async ({ path: file }) => ({
|
|
342
|
+
contents: await transformSource(file),
|
|
343
|
+
loader: 'js',
|
|
344
|
+
}))
|
|
345
|
+
},
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
|
|
350
|
+
function registerNodeModulesLoadPlugin() {
|
|
351
|
+
if (nodeModulesLoadRegistered) return
|
|
352
|
+
nodeModulesLoadRegistered = true
|
|
353
|
+
Bun.plugin({
|
|
354
|
+
name: 'pnext-server-node-modules-load',
|
|
355
|
+
setup(plugin) {
|
|
356
|
+
plugin.onLoad(
|
|
357
|
+
{
|
|
358
|
+
filter:
|
|
359
|
+
/\/node_modules\/.*(?:\.mjs|\.esm\.js|\/(?:es|esm|dist\/esm|build\/modern)\/.*\.js)$/,
|
|
360
|
+
},
|
|
361
|
+
async ({ path: file }) => {
|
|
362
|
+
const code = await transformNodeModuleSource(file)
|
|
363
|
+
return { contents: code, loader: 'js' }
|
|
364
|
+
},
|
|
365
|
+
)
|
|
366
|
+
},
|
|
367
|
+
})
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function runtimeConfigForFile(file: string | undefined) {
|
|
371
|
+
if (!file) return undefined
|
|
372
|
+
const key = path.resolve(file)
|
|
373
|
+
if (runtimeConfigLookupCache.has(key)) {
|
|
374
|
+
return runtimeConfigLookupCache.get(key) ?? undefined
|
|
375
|
+
}
|
|
376
|
+
for (const config of runtimeConfigs.values()) {
|
|
377
|
+
for (const root of config.roots) {
|
|
378
|
+
if (isInside(root, file)) {
|
|
379
|
+
runtimeConfigLookupCache.set(key, config)
|
|
380
|
+
return config
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
runtimeConfigLookupCache.set(key, null)
|
|
385
|
+
return undefined
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function sourceRootForFile(config: ResolvedConfig, file: string) {
|
|
389
|
+
const dir = path.dirname(file)
|
|
390
|
+
if (isInside(config.appPath, dir)) return config.appPath
|
|
391
|
+
return packageRootsForConfig(config).find(root => isInside(root, dir)) ?? dir
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function packageRootsForConfig(config: ResolvedConfig) {
|
|
395
|
+
let roots = packageRootCache.get(config)
|
|
396
|
+
if (!roots) {
|
|
397
|
+
roots = workspacePackageRoots(config.workspaceRoot)
|
|
398
|
+
packageRootCache.set(config, roots)
|
|
399
|
+
}
|
|
400
|
+
return roots
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function firstRuntimeConfig() {
|
|
404
|
+
for (const config of runtimeConfigs.values()) return config
|
|
405
|
+
return undefined
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export function firstAliasForSpecifier(specifier: string) {
|
|
409
|
+
if (firstAliasCache.has(specifier)) return firstAliasCache.get(specifier) ?? undefined
|
|
410
|
+
for (const config of runtimeConfigs.values()) {
|
|
411
|
+
const target = config.aliases.get(specifier)
|
|
412
|
+
if (target) {
|
|
413
|
+
firstAliasCache.set(specifier, target)
|
|
414
|
+
return target
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
firstAliasCache.set(specifier, null)
|
|
418
|
+
return undefined
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async function transformSource(file: string) {
|
|
422
|
+
const fileStat = await stat(file)
|
|
423
|
+
const cached = transformCache.get(file)
|
|
424
|
+
if (cached?.mtimeMs === fileStat.mtimeMs && cached.size === fileStat.size) {
|
|
425
|
+
return cached.code
|
|
426
|
+
}
|
|
427
|
+
// The in-memory cache is empty on every restart, so a restarted server
|
|
428
|
+
// re-transforms the whole materialized module set on the request path.
|
|
429
|
+
const diskCache = transformCacheFile(file, fileStat)
|
|
430
|
+
if (diskCache) {
|
|
431
|
+
const code = await readFile(diskCache, 'utf8').catch(() => undefined)
|
|
432
|
+
if (code !== undefined) {
|
|
433
|
+
transformCache.set(file, { mtimeMs: fileStat.mtimeMs, size: fileStat.size, code })
|
|
434
|
+
return code
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const runtime = runtimeConfigForFile(file)
|
|
439
|
+
let raw = await readFile(file, 'utf8')
|
|
440
|
+
// Raw markdown compiles through the MDX pipeline first; materialized
|
|
441
|
+
// cache/server copies were already compiled in place (runtime/modules) and would
|
|
442
|
+
// be corrupted by a second pass.
|
|
443
|
+
if (/\.mdx?$/.test(file) && !file.includes(`${path.sep}cache${path.sep}server${path.sep}`)) {
|
|
444
|
+
const { compileMdx } = await import('../compat/mdx/compile')
|
|
445
|
+
raw = (await compileMdx(raw, file, runtime?.root ?? path.dirname(file))).code
|
|
446
|
+
}
|
|
447
|
+
const source = await inlineModuleScopeRequires(
|
|
448
|
+
rewriteServerSource(raw, file, {
|
|
449
|
+
root: runtime?.root,
|
|
450
|
+
}),
|
|
451
|
+
file,
|
|
452
|
+
runtime?.root,
|
|
453
|
+
)
|
|
454
|
+
const result = await transform(source, {
|
|
455
|
+
loader: esbuildLoader(file),
|
|
456
|
+
jsx: 'automatic',
|
|
457
|
+
jsxImportSource: 'preact',
|
|
458
|
+
sourcefile: file,
|
|
459
|
+
target: 'es2022',
|
|
460
|
+
...serverDefineOptions(),
|
|
461
|
+
})
|
|
462
|
+
transformCache.set(file, {
|
|
463
|
+
mtimeMs: fileStat.mtimeMs,
|
|
464
|
+
size: fileStat.size,
|
|
465
|
+
code: result.code,
|
|
466
|
+
})
|
|
467
|
+
if (diskCache) {
|
|
468
|
+
await mkdir(path.dirname(diskCache), { recursive: true }).catch(() => undefined)
|
|
469
|
+
void writeFileAtomic(diskCache, result.code).catch(() => undefined)
|
|
470
|
+
}
|
|
471
|
+
return result.code
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
475
|
+
const transformDiskCacheDisabled = process.env.PNEXT_TRANSFORM_DISK_CACHE === '0'
|
|
476
|
+
const materializedSegment = `${path.sep}cache${path.sep}server${path.sep}`
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Where a transform of `file` is remembered across restarts. Only for the
|
|
480
|
+
* materialized `cache/server` modules: they are hash-named build output under
|
|
481
|
+
* our own cache root, so the key (path + mtime + size + the define/extension
|
|
482
|
+
* inputs of the transform) is complete, and the whole tree is discarded when
|
|
483
|
+
* a build or a config change rewrites it.
|
|
484
|
+
*/
|
|
485
|
+
function transformCacheFile(file: string, fileStat: { mtimeMs: number; size: number }) {
|
|
486
|
+
if (transformDiskCacheDisabled) return undefined
|
|
487
|
+
const at = file.lastIndexOf(materializedSegment)
|
|
488
|
+
if (at === -1) return undefined
|
|
489
|
+
const cacheRoot = file.slice(0, at + materializedSegment.length)
|
|
490
|
+
const key = `${file}\0${fileStat.mtimeMs}\0${fileStat.size}\0${currentLoadExtras().join(',')}\0${JSON.stringify(serverDefineOptions())}`
|
|
491
|
+
return path.join(cacheRoot, 'transform', `${hashBundleSpecifier(key)}.js`)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// A synchronous `require('./x')` of an app source module fails at runtime with `require() async
|
|
495
|
+
// module ... is unsupported`: the load plugin's onLoad hook makes every app `[jt]sx?` module an
|
|
496
|
+
// ASYNC Bun module, and Bun cannot `require` one. Bun does not route relative `require-call`s
|
|
497
|
+
// through onResolve plugins (only bare specifiers reach them), so the alias trick cannot intercept
|
|
498
|
+
// them at resolve time. Instead each such require is rewritten to a sibling
|
|
499
|
+
// `<x>.pnext-require.cjs` compiled to CommonJS: a real `.cjs` on disk is deliberately excluded by
|
|
500
|
+
// rootFilter, so Bun loads it natively and synchronously.
|
|
501
|
+
//
|
|
502
|
+
// Only requires that resolve to a rootFilter-matched app module are rewritten (a
|
|
503
|
+
// `.cjs`/`.json`/node_modules require already loads synchronously). The sidecar is a distinct module
|
|
504
|
+
// instance from the module's ESM copy - acceptable here, since the require path was an outright
|
|
505
|
+
// crash before and such modules are typically leaf value modules.
|
|
506
|
+
//
|
|
507
|
+
// Both the source form and the BUILT form are matched: the server bundle keeps such a require
|
|
508
|
+
// unbundled and emits esbuild's `__require("file:///abs/path/...")` shim call, which loads the very
|
|
509
|
+
// same async app module at runtime.
|
|
510
|
+
const REQUIRE_CALL = /\b(?:__)?require\(\s*(['"])((?:\.\.?\/|\/|file:\/\/)[^'"]+)\1\s*\)/g
|
|
511
|
+
|
|
512
|
+
async function inlineModuleScopeRequires(
|
|
513
|
+
source: string,
|
|
514
|
+
file: string,
|
|
515
|
+
root: string | undefined,
|
|
516
|
+
): Promise<string> {
|
|
517
|
+
if (!root || !source.includes('require(')) return source
|
|
518
|
+
const filter = rootFilter(root)
|
|
519
|
+
const sidecars = new Map<string, string>()
|
|
520
|
+
const pending: Promise<void>[] = []
|
|
521
|
+
const seen = new Set<string>()
|
|
522
|
+
for (const match of source.matchAll(REQUIRE_CALL)) {
|
|
523
|
+
const specifier = match[2]!
|
|
524
|
+
if (seen.has(specifier)) continue
|
|
525
|
+
seen.add(specifier)
|
|
526
|
+
const target = requireCallTarget(root, file, specifier)
|
|
527
|
+
if (!target || !filter.test(target)) continue
|
|
528
|
+
pending.push(
|
|
529
|
+
requireCjsSidecar(target).then(sidecar => {
|
|
530
|
+
if (sidecar) sidecars.set(specifier, sidecar)
|
|
531
|
+
}),
|
|
532
|
+
)
|
|
533
|
+
}
|
|
534
|
+
if (pending.length === 0) return source
|
|
535
|
+
await Promise.all(pending)
|
|
536
|
+
if (sidecars.size === 0) return source
|
|
537
|
+
return source.replace(REQUIRE_CALL, (whole, quote: string, specifier: string) => {
|
|
538
|
+
const sidecar = sidecars.get(specifier)
|
|
539
|
+
return sidecar ? `require(${quote}${sidecar}${quote})` : whole
|
|
540
|
+
})
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** The module a rewritable `require(...)` call resolves to, if any. */
|
|
544
|
+
function requireCallTarget(root: string, file: string, specifier: string): string | undefined {
|
|
545
|
+
if (specifier.startsWith('file://')) {
|
|
546
|
+
try {
|
|
547
|
+
return resolveImport(root, file, fileURLToPath(specifier))
|
|
548
|
+
} catch {
|
|
549
|
+
return undefined
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return resolveImport(root, file, specifier)
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Compile an app module to a sibling `.pnext-require.cjs` (CommonJS), reusing a
|
|
556
|
+
// mtime/size-cached copy. Returns the sidecar path, or undefined on any failure
|
|
557
|
+
// (the original require is then left as-is). The module runs through the same
|
|
558
|
+
// server source pipeline as a normal load so the CJS copy behaves like the ESM
|
|
559
|
+
// one; nested module-scope requires inside it are NOT recursively inlined.
|
|
560
|
+
async function requireCjsSidecar(target: string): Promise<string | undefined> {
|
|
561
|
+
try {
|
|
562
|
+
const sidecar = requireCjsSidecarPath(target)
|
|
563
|
+
const fileStat = await stat(target)
|
|
564
|
+
const cached = requireSidecarCache.get(target)
|
|
565
|
+
if (cached?.mtimeMs === fileStat.mtimeMs && cached.size === fileStat.size) return sidecar
|
|
566
|
+
const runtime = runtimeConfigForFile(target)
|
|
567
|
+
const source = rewriteServerSource(await readFile(target, 'utf8'), target, {
|
|
568
|
+
root: runtime?.root,
|
|
569
|
+
})
|
|
570
|
+
const result = await transform(source, {
|
|
571
|
+
loader: esbuildLoader(target),
|
|
572
|
+
jsx: 'automatic',
|
|
573
|
+
jsxImportSource: 'preact',
|
|
574
|
+
sourcefile: target,
|
|
575
|
+
target: 'es2022',
|
|
576
|
+
format: 'cjs',
|
|
577
|
+
...serverDefineOptions(),
|
|
578
|
+
})
|
|
579
|
+
await writeFile(sidecar, result.code)
|
|
580
|
+
requireSidecarCache.set(target, { mtimeMs: fileStat.mtimeMs, size: fileStat.size })
|
|
581
|
+
return sidecar
|
|
582
|
+
} catch {
|
|
583
|
+
return undefined
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function requireCjsSidecarPath(target: string): string {
|
|
588
|
+
return `${target.replace(/\.[^./\\]+$/, '')}.pnext-require.cjs`
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
async function transformNodeModuleSource(file: string) {
|
|
592
|
+
const fileStat = await stat(file)
|
|
593
|
+
const cached = nodeModuleTransformCache.get(file)
|
|
594
|
+
if (cached?.mtimeMs === fileStat.mtimeMs && cached.size === fileStat.size) {
|
|
595
|
+
return cached.code
|
|
596
|
+
}
|
|
597
|
+
// The in-memory cache is empty on every restart, so a restarted server
|
|
598
|
+
// re-BUNDLES each of these packages on the request path.
|
|
599
|
+
const record = nodeModuleBundleRecordFile(file, fileStat)
|
|
600
|
+
const reused = record ? await readNodeModuleBundle(record) : undefined
|
|
601
|
+
if (reused !== undefined) {
|
|
602
|
+
nodeModuleTransformCache.set(file, {
|
|
603
|
+
mtimeMs: fileStat.mtimeMs,
|
|
604
|
+
size: fileStat.size,
|
|
605
|
+
code: reused,
|
|
606
|
+
})
|
|
607
|
+
return reused
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const { code, inputs } = await bundleNodeModuleSource(file)
|
|
611
|
+
nodeModuleTransformCache.set(file, {
|
|
612
|
+
mtimeMs: fileStat.mtimeMs,
|
|
613
|
+
size: fileStat.size,
|
|
614
|
+
code,
|
|
615
|
+
})
|
|
616
|
+
if (record) void writeNodeModuleBundle(record, inputs, code)
|
|
617
|
+
return code
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Where a bundle of `file` is remembered across restarts. The entry's own stat is in the name, but
|
|
622
|
+
* the build inlines the package's relative graph too, so the record carries every esbuild input and
|
|
623
|
+
* is only reused while all of them still stat the same. A record that cannot be validated is a miss.
|
|
624
|
+
*/
|
|
625
|
+
function nodeModuleBundleRecordFile(file: string, fileStat: { mtimeMs: number; size: number }) {
|
|
626
|
+
if (transformDiskCacheDisabled) return undefined
|
|
627
|
+
const outPath = (runtimeConfigForFile(file) ?? firstRuntimeConfig())?.resolved.outPath
|
|
628
|
+
if (!outPath) return undefined
|
|
629
|
+
const key = `${file}\0${fileStat.mtimeMs}\0${fileStat.size}\0${currentLoadExtras().join(',')}\0${JSON.stringify(serverDefineOptions())}`
|
|
630
|
+
return path.join(cacheRoot(outPath), 'node-module-bundle', `${hashBundleSpecifier(key)}.json`)
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
async function bundleNodeModuleSource(file: string) {
|
|
634
|
+
const resolved = runtimeConfigForFile(file)?.resolved
|
|
635
|
+
const extensionPlugins = resolved ? getBundlerExtensions().serverEsbuildPlugins(resolved) : []
|
|
636
|
+
const result = await build({
|
|
637
|
+
// The marker entry only resolves when the extension chain is present.
|
|
638
|
+
entryPoints: [
|
|
639
|
+
(resolved
|
|
640
|
+
? getBundlerExtensions().serverBundleEntry(file, path.dirname(file), file)
|
|
641
|
+
: undefined) ?? file,
|
|
642
|
+
],
|
|
643
|
+
bundle: true,
|
|
644
|
+
write: false,
|
|
645
|
+
format: 'esm',
|
|
646
|
+
platform: 'neutral',
|
|
647
|
+
target: 'es2022',
|
|
648
|
+
loader: packageJsxLoaders,
|
|
649
|
+
jsx: 'automatic',
|
|
650
|
+
jsxImportSource: 'preact',
|
|
651
|
+
packages: 'external',
|
|
652
|
+
logLevel: 'silent',
|
|
653
|
+
metafile: true,
|
|
654
|
+
...serverDefineOptions(),
|
|
655
|
+
plugins: [
|
|
656
|
+
serverAssetPlugin(),
|
|
657
|
+
...extensionPlugins,
|
|
658
|
+
runtimeAliasBuildPlugin(undefined, 'server', { bundleRequireAliases: true }),
|
|
659
|
+
],
|
|
660
|
+
})
|
|
661
|
+
const output = result.outputFiles[0]
|
|
662
|
+
if (!output) throw new Error(`Failed to bundle ${file}`)
|
|
663
|
+
// Packages can ship 'use cache' functions too (Next allows it); everything
|
|
664
|
+
// else in the source chain was already settled by the bundle above (imports
|
|
665
|
+
// resolved by the plugins, constants inlined by esbuild `define`).
|
|
666
|
+
return {
|
|
667
|
+
code: applyBundledSourceTransforms(output.text, file),
|
|
668
|
+
inputs: Object.keys(result.metafile.inputs).map(input => path.resolve(input)),
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* The one vendor-compile entry point. Every demand - a module compile's import scan, an esbuild
|
|
674
|
+
* resolve callback, a transitive dependency of another vendor bundle - arrives here and is scheduled
|
|
675
|
+
* by the pipeline in `./vendor`. `nested` marks demands raised from inside a running vendor build;
|
|
676
|
+
* see `withVendorSlot` for why they must not queue.
|
|
677
|
+
*/
|
|
678
|
+
|
|
679
|
+
// ---------------------------------------------------------------------------
|
|
680
|
+
// Shared with vendor bundling (./vendor-build): alias resolution, asset
|
|
681
|
+
// stubbing, source-rewrite, and root/hash helpers used by both the loader's
|
|
682
|
+
// own plugins and the vendor esbuild passes.
|
|
683
|
+
// ---------------------------------------------------------------------------
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Whether `rewriteServerSource` can possibly change `source`. One regex scan
|
|
687
|
+
* over the union of the core dynamic tokens and every registered transform's
|
|
688
|
+
* own sniff tokens (see `withSniff`); most modules trigger nothing and skip the
|
|
689
|
+
* whole chain.
|
|
690
|
+
*/
|
|
691
|
+
export function needsServerSourceRewrite(source: string) {
|
|
692
|
+
return sourceHasDynamicImport(source) || sourceNeedsServerTransforms(source)
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
export function rewriteServerSource(
|
|
696
|
+
source: string,
|
|
697
|
+
file: string,
|
|
698
|
+
options: { nextFonts?: boolean; root?: string } = {},
|
|
699
|
+
) {
|
|
700
|
+
const root = options.root ?? path.dirname(file)
|
|
701
|
+
const rewritten = sourceHasDynamicImport(source)
|
|
702
|
+
? rewriteDynamicCallTargets(
|
|
703
|
+
rewriteLiteralDynamicCalls(source, file),
|
|
704
|
+
specifier => resolveImport(root, file, specifier),
|
|
705
|
+
file,
|
|
706
|
+
)
|
|
707
|
+
: source
|
|
708
|
+
// Compat pre-transforms (next/font/root-params) run before generic server
|
|
709
|
+
// transforms (use-cache/action tags). Pure-core apps register no transforms.
|
|
710
|
+
if (options.nextFonts === false) return rewritten
|
|
711
|
+
if (!sourceNeedsServerTransforms(rewritten)) return rewritten
|
|
712
|
+
return applyServerSourceTransforms(
|
|
713
|
+
applyServerSourcePreTransforms(rewritten, file, options.root),
|
|
714
|
+
file,
|
|
715
|
+
options.root,
|
|
716
|
+
)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
export function serverRequireAlias(
|
|
720
|
+
specifier: string,
|
|
721
|
+
target: string,
|
|
722
|
+
kind: string | undefined,
|
|
723
|
+
): string {
|
|
724
|
+
if (kind !== 'require-call') return target
|
|
725
|
+
if (specifier === 'next/navigation' && target.endsWith(`${path.sep}navigation.ts`)) {
|
|
726
|
+
return path.join(path.dirname(target), 'navigation.cjs')
|
|
727
|
+
}
|
|
728
|
+
if (specifier === 'next/router' && target.endsWith(`${path.sep}router.ts`)) {
|
|
729
|
+
return path.join(path.dirname(target), 'router.cjs')
|
|
730
|
+
}
|
|
731
|
+
return target
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
export function runtimeAliasBuildPlugin(
|
|
735
|
+
config?: ResolvedConfig,
|
|
736
|
+
target: CompatAliasTarget = 'server',
|
|
737
|
+
options: { bundleRequireAliases?: boolean; reactServerLayer?: boolean } = {},
|
|
738
|
+
): Plugin {
|
|
739
|
+
const aliases = config ? coreAliases(config, target) : undefined
|
|
740
|
+
// A node_modules package bundled into the RSC (react-server) layer that requires 'react' must observe the
|
|
741
|
+
// react-server subset - no client-hook dispatcher - exactly like the framework's own react-server modules,
|
|
742
|
+
// so a package's `'useState' in require('react')` probe reads false on the server. ESM named imports stay
|
|
743
|
+
// external and resolve at runtime against the full-hooks server shim (kept for pages/api back-compat), so
|
|
744
|
+
// this narrow require-call override never breaks them.
|
|
745
|
+
const reactServerLayerAliases =
|
|
746
|
+
config && options.reactServerLayer
|
|
747
|
+
? getImportAliasExtensions().reactServerLayerAliases(config)
|
|
748
|
+
: undefined
|
|
749
|
+
return {
|
|
750
|
+
name: 'pnext-runtime-build-alias',
|
|
751
|
+
setup(build) {
|
|
752
|
+
build.onResolve({ filter: /^\.\/preact$/ }, args => {
|
|
753
|
+
// `./preact` is the compat react shim's private import. It also appears in MIRRORED copies of the
|
|
754
|
+
// shim (build-cache outputs under node_modules paths) where no sibling preact file exists - those
|
|
755
|
+
// must resolve to the single compat instance too. Only a REAL sibling ./preact.* opts out.
|
|
756
|
+
const hasRealSibling = ['.ts', '.tsx', '.js', '.mjs', '.jsx'].some(ext =>
|
|
757
|
+
existsSync(path.join(args.resolveDir, `preact${ext}`)),
|
|
758
|
+
)
|
|
759
|
+
const compatPreact = path.resolve(import.meta.dirname, '..', 'compat', 'react', 'preact.ts')
|
|
760
|
+
if (hasRealSibling && path.resolve(args.resolveDir, 'preact.ts') !== compatPreact)
|
|
761
|
+
return undefined
|
|
762
|
+
return { path: compatPreact }
|
|
763
|
+
})
|
|
764
|
+
build.onResolve({ filter: /^\.\.\/client\/errors\/primitive-throw$/ }, args => {
|
|
765
|
+
const compatPreact = path.resolve(import.meta.dirname, '..', 'compat', 'react', 'preact.ts')
|
|
766
|
+
if (path.resolve(args.importer) !== compatPreact) return undefined
|
|
767
|
+
return {
|
|
768
|
+
path: path.resolve(
|
|
769
|
+
import.meta.dirname,
|
|
770
|
+
'..',
|
|
771
|
+
'compat',
|
|
772
|
+
'client',
|
|
773
|
+
'errors',
|
|
774
|
+
'primitive-throw.ts',
|
|
775
|
+
),
|
|
776
|
+
}
|
|
777
|
+
})
|
|
778
|
+
build.onResolve({ filter: aliasSpecifierFilter }, args => {
|
|
779
|
+
const resolved = aliases?.[args.path] ?? firstAliasForSpecifier(args.path)
|
|
780
|
+
if (!resolved) return undefined
|
|
781
|
+
const requireTarget = serverRequireAlias(args.path, resolved, args.kind)
|
|
782
|
+
if (requireTarget !== resolved) return { path: requireTarget }
|
|
783
|
+
if (options.bundleRequireAliases && args.kind === 'require-call') {
|
|
784
|
+
const layerTarget = serverRequireAlias(
|
|
785
|
+
args.path,
|
|
786
|
+
reactServerLayerAliases?.[args.path] ?? resolved,
|
|
787
|
+
args.kind,
|
|
788
|
+
)
|
|
789
|
+
return { path: path.isAbsolute(layerTarget) ? layerTarget : require.resolve(layerTarget) }
|
|
790
|
+
}
|
|
791
|
+
return {
|
|
792
|
+
path: path.isAbsolute(resolved) ? pathToFileHref(resolved) : resolved,
|
|
793
|
+
external: true,
|
|
794
|
+
}
|
|
795
|
+
})
|
|
796
|
+
},
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
function isInside(root: string, file: string) {
|
|
801
|
+
const relative = path.relative(root, file)
|
|
802
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function rootPaths(root: string) {
|
|
806
|
+
const key = path.resolve(root)
|
|
807
|
+
const cached = rootPathCache.get(key)
|
|
808
|
+
if (cached) return cached
|
|
809
|
+
const paths = new Set([path.resolve(root)])
|
|
810
|
+
try {
|
|
811
|
+
paths.add(realpathSync.native(root))
|
|
812
|
+
} catch {
|
|
813
|
+
// The dev cache root is registered before it exists.
|
|
814
|
+
}
|
|
815
|
+
const resolved = [...paths]
|
|
816
|
+
rootPathCache.set(key, resolved)
|
|
817
|
+
return resolved
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
function rootFilter(root: string) {
|
|
821
|
+
// Base source extensions plus any extra loadable extensions registered by
|
|
822
|
+
// compat (e.g. mdx/md via pageExtensions). Without these, `.mdx` modules skip
|
|
823
|
+
// the transform load hook and resolve as raw assets (default export = path).
|
|
824
|
+
const extras = currentLoadExtras().map(escapeRegex)
|
|
825
|
+
const exts = ['[jt]sx?', ...extras].join('|')
|
|
826
|
+
// Exclude `next.config.{js,cjs,mjs,ts}`: it is loaded for its exported value (often CommonJS
|
|
827
|
+
// `module.exports = ...`), not as part of the app's server module graph. Routing it through the
|
|
828
|
+
// ES-module source transform drops the CJS exports, silently disabling next.config
|
|
829
|
+
// rewrites/redirects.
|
|
830
|
+
// Exclude the vendor tree under the cache root: the `*.dist/` copies copyBrowserReadyEsmDist
|
|
831
|
+
// publishes are verbatim third-party ESM the copy predicate has already proved needs no server
|
|
832
|
+
// rewrite, but they are `.js`, so each was read and re-transformed on every boot. Bun loads them
|
|
833
|
+
// natively instead. Two clauses because the vendor tree is reachable from two registered roots.
|
|
834
|
+
const vendorGuard = vendorLoadsNatively()
|
|
835
|
+
? `${root.endsWith(`${path.sep}cache${path.sep}server`) ? '(?!\\/vendor\\/)' : ''}(?!.*\\/cache\\/server\\/vendor\\/)`
|
|
836
|
+
: ''
|
|
837
|
+
return new RegExp(
|
|
838
|
+
`^${escapeRegex(root)}${vendorGuard}(?!.*\\/node_modules\\/)(?!\\/next\\.config\\.(?:js|cjs|mjs|ts)$)(?:/.*)?\\.(?:${exts})$`,
|
|
839
|
+
)
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function esbuildLoader(file: string) {
|
|
843
|
+
if (file.endsWith('.tsx')) return 'tsx'
|
|
844
|
+
if (file.endsWith('.ts')) return 'ts'
|
|
845
|
+
// Workspace .js/.jsx may contain JSX (Next allows it); jsx is a js superset.
|
|
846
|
+
return 'jsx'
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function hashRoot(value: string) {
|
|
850
|
+
let hash = 5381
|
|
851
|
+
for (const char of value) hash = ((hash << 5) + hash) ^ char.charCodeAt(0)
|
|
852
|
+
return (hash >>> 0).toString(36)
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
export function hashBundleSpecifier(value: string) {
|
|
856
|
+
return createHash('sha256').update(value).digest('hex').slice(0, 16)
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* Bisect switch for the vendor-tree load-plugin exclusion. Deliberately NOT `PNEXT_VENDOR_NATIVE` -
|
|
861
|
+
* that name already gates the plugin-free bundler, and sharing it would arm that opt-in path by
|
|
862
|
+
* accident whenever this one was bisected.
|
|
863
|
+
*/
|
|
864
|
+
export function vendorLoadsNatively() {
|
|
865
|
+
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
866
|
+
return process.env.PNEXT_VENDOR_DIST_NATIVE !== '0'
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// --------------------------------------------------------------------------
|
|
870
|
+
// pre-planned vendor policy (PNEXT_VENDOR_PREPLAN=1 opt-in, server layer only)
|
|
871
|
+
// --------------------------------------------------------------------------
|
|
872
|
+
|
|
873
|
+
export const serverIgnoredAssetFilter =
|
|
874
|
+
/\.(?:css|scss|sass|woff2?|ttf|otf|eot|png|jpe?g|gif|webp|avif|svg|ico|bmp)(?:$|[?#])/
|
|
875
|
+
|
|
876
|
+
function isCssFile(file: string) {
|
|
877
|
+
return file.endsWith('.css')
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
function isStaticImageFile(file: string) {
|
|
881
|
+
return /\.(?:png|jpe?g|gif|webp|avif|svg|ico|bmp)(?:$|[?#])/.test(file)
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
function resolveAssetPath(specifier: string, resolveDir: string) {
|
|
885
|
+
const [sourcePath = '', hash = ''] = specifier.split('#', 2)
|
|
886
|
+
const resolved = path.isAbsolute(sourcePath) ? sourcePath : path.resolve(resolveDir, sourcePath)
|
|
887
|
+
return hash ? `${resolved}#${hash}` : resolved
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
async function staticImageModuleSource(config: ResolvedConfig | undefined, file: string) {
|
|
891
|
+
const [sourcePath = ''] = file.split('#', 1)
|
|
892
|
+
const bytes = new Uint8Array(await readFile(sourcePath))
|
|
893
|
+
const emitted: { relative: string; bytes: Uint8Array }[] = []
|
|
894
|
+
const emit = (relative: string, data: Uint8Array) => {
|
|
895
|
+
emitted.push({ relative, bytes: data })
|
|
896
|
+
return `/${relative}`
|
|
897
|
+
}
|
|
898
|
+
const compat = await getAssetExtensions().staticAssetModule({ sourcePath, bytes, emit })
|
|
899
|
+
const source = compat ?? coreStaticAssetModule(sourcePath, bytes, emit)
|
|
900
|
+
if (config) {
|
|
901
|
+
for (const asset of emitted) {
|
|
902
|
+
const target = path.join(config.outPath, 'public', ...asset.relative.split('/'))
|
|
903
|
+
await mkdir(path.dirname(target), { recursive: true })
|
|
904
|
+
if (!existsSync(target)) await Bun.write(target, asset.bytes)
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return source
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// Core's generic static-asset module: emit the file under a hashed media URL
|
|
911
|
+
// and export it as the default (a plain URL string) when no compat override is
|
|
912
|
+
// registered. next/image compat replaces this with a full StaticImageData shape.
|
|
913
|
+
function coreStaticAssetModule(
|
|
914
|
+
sourcePath: string,
|
|
915
|
+
bytes: Uint8Array,
|
|
916
|
+
emit: (relative: string, bytes: Uint8Array) => string,
|
|
917
|
+
): string {
|
|
918
|
+
const ext = path.extname(sourcePath).toLowerCase() || '.bin'
|
|
919
|
+
const hash = createHash('sha256').update(bytes).digest('hex').slice(0, 8)
|
|
920
|
+
const base = path.basename(sourcePath, path.extname(sourcePath)).replace(/[^A-Za-z0-9_-]+/g, '-')
|
|
921
|
+
const relative = getAssetExtensions().staticAssetRelativePath({ sourcePath, hash, base, ext })
|
|
922
|
+
const src = emit(relative, bytes)
|
|
923
|
+
return `const src = ${JSON.stringify(src)};\nexport default src;\nexport { src };\n`
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
export function serverAssetPlugin(config?: ResolvedConfig): Plugin {
|
|
927
|
+
return {
|
|
928
|
+
name: 'pnext-empty-server-assets',
|
|
929
|
+
setup(build) {
|
|
930
|
+
build.onResolve({ filter: serverIgnoredAssetFilter }, args => {
|
|
931
|
+
const resolved = resolveAssetPath(args.path, args.resolveDir)
|
|
932
|
+
// A configured `turbopack.rules` loader chain for this extension (e.g.
|
|
933
|
+
// `*.svg`) preempts the generic asset pipeline: defer so the compat
|
|
934
|
+
// loader-rule plugin's onLoad runs the chain against the real file.
|
|
935
|
+
if (getAssetExtensions().hasLoaderRuleFor(resolved)) {
|
|
936
|
+
return undefined
|
|
937
|
+
}
|
|
938
|
+
return { path: resolved, namespace: 'pnext-empty-server-asset' }
|
|
939
|
+
})
|
|
940
|
+
build.onLoad({ filter: /.*/, namespace: 'pnext-empty-server-asset' }, async args => ({
|
|
941
|
+
contents:
|
|
942
|
+
getCssExtensions().loadCssModuleForClient(args.path) ??
|
|
943
|
+
(isCssFile(args.path)
|
|
944
|
+
? ''
|
|
945
|
+
: isStaticImageFile(args.path)
|
|
946
|
+
? await staticImageModuleSource(config, args.path)
|
|
947
|
+
: 'export default "";'),
|
|
948
|
+
loader: 'js',
|
|
949
|
+
}))
|
|
950
|
+
},
|
|
951
|
+
}
|
|
952
|
+
}
|