@wular/pnext 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +153 -0
- package/bin/pnext +67 -0
- package/config/lint/base.js +48 -0
- package/config/ts/base.json +26 -0
- package/config/ts/react.json +13 -0
- package/package.json +70 -0
- package/reference/compat.md +63 -0
- package/reference/config.md +120 -0
- package/reference/css.md +58 -0
- package/reference/dev.md +69 -0
- package/reference/env.md +40 -0
- package/reference/metadata.md +86 -0
- package/reference/navigation.md +149 -0
- package/reference/overview.md +35 -0
- package/reference/performance.md +97 -0
- package/reference/rendering.md +127 -0
- package/reference/routing.md +167 -0
- package/reference/typegen.md +64 -0
- package/src/api/cache.ts +80 -0
- package/src/api/client-cache.ts +9 -0
- package/src/api/client-navigation.ts +279 -0
- package/src/api/dynamic.tsx +102 -0
- package/src/api/link.tsx +119 -0
- package/src/api/navigation.ts +198 -0
- package/src/api/router/events.ts +53 -0
- package/src/api/router/history.ts +70 -0
- package/src/api/router/hub.ts +194 -0
- package/src/api/router/policies.ts +107 -0
- package/src/api/router/runtime.ts +5238 -0
- package/src/api/router/types.ts +299 -0
- package/src/api/router.ts +167 -0
- package/src/api/server.ts +323 -0
- package/src/api/suspense.ts +16 -0
- package/src/cache/context.ts +61 -0
- package/src/cli/adapters/vercel-warm.ts +375 -0
- package/src/cli/adapters/vercel.ts +1310 -0
- package/src/cli/analyze-print.ts +181 -0
- package/src/cli/analyze.ts +328 -0
- package/src/cli/boot-trace.ts +29 -0
- package/src/cli/build.ts +3114 -0
- package/src/cli/dev.ts +276 -0
- package/src/cli/index.ts +196 -0
- package/src/cli/named-bin.ts +119 -0
- package/src/cli/serve-ui.ts +160 -0
- package/src/cli/start.ts +1425 -0
- package/src/client/build.ts +2136 -0
- package/src/client/chunk-fold.ts +526 -0
- package/src/client/entry.ts +1525 -0
- package/src/client/paths.ts +22 -0
- package/src/client/prebuilt.ts +621 -0
- package/src/client/profile.ts +75 -0
- package/src/client/react-compiler.ts +94 -0
- package/src/client/reference-stub.ts +145 -0
- package/src/client/reference.ts +47 -0
- package/src/compat/actions/action-client.ts +531 -0
- package/src/compat/actions/action-dispatch.ts +676 -0
- package/src/compat/actions/action-router.ts +40 -0
- package/src/compat/actions/action-shared.ts +95 -0
- package/src/compat/actions/client-plugin.ts +135 -0
- package/src/compat/actions/client-stub.ts +65 -0
- package/src/compat/actions/config.ts +164 -0
- package/src/compat/actions/detect.ts +208 -0
- package/src/compat/actions/discovery.ts +244 -0
- package/src/compat/actions/early-submit.ts +40 -0
- package/src/compat/actions/endpoint.ts +602 -0
- package/src/compat/actions/flight.ts +52 -0
- package/src/compat/actions/form-state.ts +73 -0
- package/src/compat/actions/hoist.ts +485 -0
- package/src/compat/actions/ids.ts +39 -0
- package/src/compat/actions/index.ts +41 -0
- package/src/compat/actions/instances.ts +144 -0
- package/src/compat/actions/origin.ts +109 -0
- package/src/compat/actions/protocol.ts +125 -0
- package/src/compat/actions/registry.ts +74 -0
- package/src/compat/actions/rewrite.ts +282 -0
- package/src/compat/actions/serve.ts +414 -0
- package/src/compat/actions/server-tag.ts +21 -0
- package/src/compat/actions/unrecognized-error.ts +30 -0
- package/src/compat/adapter/build-complete.ts +257 -0
- package/src/compat/bundler/bun-externals.ts +53 -0
- package/src/compat/bundler/cjs-exports.ts +542 -0
- package/src/compat/bundler/config.ts +363 -0
- package/src/compat/bundler/externals.ts +34 -0
- package/src/compat/bundler/import-meta-url.ts +60 -0
- package/src/compat/bundler/modularize-imports.ts +119 -0
- package/src/compat/bundler/new-url-asset.ts +87 -0
- package/src/compat/bundler/optimize-package-imports.ts +273 -0
- package/src/compat/bundler/polyfill.ts +88 -0
- package/src/compat/bundler/react-compiler.ts +61 -0
- package/src/compat/bundler/react-profiler.tsx +25 -0
- package/src/compat/bundler/relay-transform.ts +116 -0
- package/src/compat/bundler/require-context.ts +281 -0
- package/src/compat/bundler/resolve-extensions.ts +75 -0
- package/src/compat/bundler/source-cache.ts +61 -0
- package/src/compat/bundler/static-imports.ts +25 -0
- package/src/compat/bundler/symlink-imports.ts +119 -0
- package/src/compat/bundler/tsconfig-paths.ts +50 -0
- package/src/compat/bundler/wasm.ts +153 -0
- package/src/compat/bundler/webpack-loaders.ts +685 -0
- package/src/compat/bundler/worker.ts +278 -0
- package/src/compat/cache/build-flags.ts +81 -0
- package/src/compat/cache/build-prerender-errors.ts +163 -0
- package/src/compat/cache/custom-handler.ts +159 -0
- package/src/compat/cache/fetch-patch.ts +745 -0
- package/src/compat/cache/handler.ts +100 -0
- package/src/compat/cache/modern-handler.ts +275 -0
- package/src/compat/cache/resume-data-cache.ts +143 -0
- package/src/compat/cache/revalidate.ts +759 -0
- package/src/compat/cache/runtime-error.ts +124 -0
- package/src/compat/cache/use-cache-transform.ts +961 -0
- package/src/compat/cache/use-cache.ts +1695 -0
- package/src/compat/cache-control.ts +269 -0
- package/src/compat/client/base-path.ts +64 -0
- package/src/compat/client/css-order.ts +36 -0
- package/src/compat/client/errors/control-flow.ts +92 -0
- package/src/compat/client/errors/error-boundary.ts +222 -0
- package/src/compat/client/errors/global-error.ts +238 -0
- package/src/compat/client/errors/install.ts +217 -0
- package/src/compat/client/errors/lazy.ts +53 -0
- package/src/compat/client/errors/primitive-throw.ts +126 -0
- package/src/compat/client/errors/soft-refresh.ts +14 -0
- package/src/compat/client/link-status.ts +86 -0
- package/src/compat/client/nav-compat-runtime.ts +57 -0
- package/src/compat/client/nav-compat.ts +42 -0
- package/src/compat/client/navigation-scroll.ts +154 -0
- package/src/compat/client/optimistic-routing.ts +206 -0
- package/src/compat/client/prefetch-cache.ts +111 -0
- package/src/compat/client/route-announcer.ts +72 -0
- package/src/compat/client/segment-cache-policy.ts +159 -0
- package/src/compat/client/segment-cache.ts +1077 -0
- package/src/compat/client/segment-prefetch.ts +375 -0
- package/src/compat/client/trailing-slash.ts +24 -0
- package/src/compat/css/chunking.ts +254 -0
- package/src/compat/css/inline-css.ts +73 -0
- package/src/compat/css/lightningcss.ts +90 -0
- package/src/compat/css/modules.ts +373 -0
- package/src/compat/css/nonce.ts +30 -0
- package/src/compat/css/sass-plugin.ts +65 -0
- package/src/compat/css/sass.ts +392 -0
- package/src/compat/css/styled-jsx-runtime.ts +80 -0
- package/src/compat/css/styled-jsx.ts +49 -0
- package/src/compat/edge-runtime.ts +71 -0
- package/src/compat/export/client.ts +112 -0
- package/src/compat/export/index.ts +272 -0
- package/src/compat/export/standalone.ts +207 -0
- package/src/compat/image-optimizer/cache.ts +119 -0
- package/src/compat/image-optimizer/detect.ts +143 -0
- package/src/compat/image-optimizer/index.ts +601 -0
- package/src/compat/image-optimizer/source.ts +243 -0
- package/src/compat/index.ts +458 -0
- package/src/compat/lifecycle/after-scope.ts +86 -0
- package/src/compat/lifecycle/after.ts +173 -0
- package/src/compat/lifecycle/error-funnel.ts +306 -0
- package/src/compat/lifecycle/error-serialize.ts +87 -0
- package/src/compat/lifecycle/error-ui.ts +167 -0
- package/src/compat/lifecycle/instrumentation-client.ts +138 -0
- package/src/compat/lifecycle/instrumentation.ts +277 -0
- package/src/compat/lifecycle/node-console.ts +19 -0
- package/src/compat/lifecycle/testmode.ts +263 -0
- package/src/compat/mdx/compile.ts +219 -0
- package/src/compat/mdx/next-mdx-stub.ts +46 -0
- package/src/compat/mdx/plugin.ts +37 -0
- package/src/compat/metadata-route-artifacts.ts +458 -0
- package/src/compat/metadata.ts +295 -0
- package/src/compat/middleware/manifest.ts +210 -0
- package/src/compat/misc/action-return.ts +173 -0
- package/src/compat/next/cache.ts +211 -0
- package/src/compat/next/canonical-url.ts +35 -0
- package/src/compat/next/client-cache.ts +57 -0
- package/src/compat/next/client-navigation.ts +313 -0
- package/src/compat/next/client-only.ts +3 -0
- package/src/compat/next/client-script.tsx +215 -0
- package/src/compat/next/client-server.ts +39 -0
- package/src/compat/next/config-loader.ts +569 -0
- package/src/compat/next/config.ts +29 -0
- package/src/compat/next/constants.cjs +6 -0
- package/src/compat/next/constants.ts +6 -0
- package/src/compat/next/custom-server.ts +236 -0
- package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
- package/src/compat/next/dynamic.tsx +46 -0
- package/src/compat/next/error.tsx +148 -0
- package/src/compat/next/font/cache.ts +171 -0
- package/src/compat/next/font/google.ts +2 -0
- package/src/compat/next/font/index.ts +8 -0
- package/src/compat/next/font/local.ts +5 -0
- package/src/compat/next/font/runtime-client.ts +71 -0
- package/src/compat/next/font/runtime.ts +974 -0
- package/src/compat/next/font/shared.ts +281 -0
- package/src/compat/next/form.tsx +156 -0
- package/src/compat/next/head.tsx +10 -0
- package/src/compat/next/headers.ts +247 -0
- package/src/compat/next/image/config.ts +196 -0
- package/src/compat/next/image/optimizer.ts +96 -0
- package/src/compat/next/image/patterns.ts +103 -0
- package/src/compat/next/image/shared.ts +141 -0
- package/src/compat/next/image/static-metadata.ts +283 -0
- package/src/compat/next/image/validate.ts +269 -0
- package/src/compat/next/image-client.tsx +215 -0
- package/src/compat/next/image-props.ts +575 -0
- package/src/compat/next/image-usage.ts +102 -0
- package/src/compat/next/image.tsx +56 -0
- package/src/compat/next/index.ts +1 -0
- package/src/compat/next/legacy-image.tsx +97 -0
- package/src/compat/next/link-usage.ts +29 -0
- package/src/compat/next/link-validation-transform.ts +200 -0
- package/src/compat/next/link.tsx +466 -0
- package/src/compat/next/navigation.cjs +21 -0
- package/src/compat/next/navigation.ts +188 -0
- package/src/compat/next/offline.ts +51 -0
- package/src/compat/next/og.ts +324 -0
- package/src/compat/next/optimistic-route-state.ts +188 -0
- package/src/compat/next/preferred-region.ts +39 -0
- package/src/compat/next/redirects.ts +131 -0
- package/src/compat/next/resource-hints.ts +136 -0
- package/src/compat/next/rewrites.ts +350 -0
- package/src/compat/next/root-params.ts +142 -0
- package/src/compat/next/router.cjs +49 -0
- package/src/compat/next/router.ts +143 -0
- package/src/compat/next/script.tsx +355 -0
- package/src/compat/next/server-only.ts +3 -0
- package/src/compat/next/server.ts +28 -0
- package/src/compat/next/svgr.ts +58 -0
- package/src/compat/next/telemetry.ts +77 -0
- package/src/compat/next/user-agent.ts +100 -0
- package/src/compat/next/web-vitals.ts +56 -0
- package/src/compat/otel/api.ts +95 -0
- package/src/compat/otel/client-trace-metadata.ts +71 -0
- package/src/compat/otel/fetch-span.ts +77 -0
- package/src/compat/otel/tracer.ts +944 -0
- package/src/compat/pages/client-plugin.ts +108 -0
- package/src/compat/pages/index.ts +527 -0
- package/src/compat/pages/router-state.ts +94 -0
- package/src/compat/ppr/io.ts +38 -0
- package/src/compat/ppr/missing-root-params.ts +105 -0
- package/src/compat/ppr/root-params-scan.ts +164 -0
- package/src/compat/ppr/root-params-transform.ts +75 -0
- package/src/compat/ppr/root-params.ts +129 -0
- package/src/compat/ppr/segment-config-incompat.ts +34 -0
- package/src/compat/protocol.ts +202 -0
- package/src/compat/react/client.ts +59 -0
- package/src/compat/react/compiler-runtime.ts +60 -0
- package/src/compat/react/dom-client.ts +115 -0
- package/src/compat/react/dom-react-server.ts +20 -0
- package/src/compat/react/dom-server.ts +40 -0
- package/src/compat/react/dom.ts +154 -0
- package/src/compat/react/preact.ts +522 -0
- package/src/compat/react/react-server.ts +84 -0
- package/src/compat/react/router-shim.ts +26 -0
- package/src/compat/react/server-component-use.ts +48 -0
- package/src/compat/react/server-inserted-html.ts +87 -0
- package/src/compat/react/server.ts +156 -0
- package/src/compat/react/view-transition.ts +60 -0
- package/src/compat/register/actions.ts +875 -0
- package/src/compat/register/boot.ts +141 -0
- package/src/compat/register/build-tier.ts +11 -0
- package/src/compat/register/build.ts +182 -0
- package/src/compat/register/bundler.ts +587 -0
- package/src/compat/register/cache.ts +85 -0
- package/src/compat/register/client-errors.ts +18 -0
- package/src/compat/register/config.ts +17 -0
- package/src/compat/register/css-extras.ts +120 -0
- package/src/compat/register/edge-runtime.ts +6 -0
- package/src/compat/register/errors.ts +46 -0
- package/src/compat/register/export.ts +23 -0
- package/src/compat/register/font.ts +36 -0
- package/src/compat/register/hooks.ts +34 -0
- package/src/compat/register/image.ts +133 -0
- package/src/compat/register/index.ts +111 -0
- package/src/compat/register/instrumentation-client.ts +35 -0
- package/src/compat/register/lifecycle.ts +86 -0
- package/src/compat/register/mdx.ts +48 -0
- package/src/compat/register/middleware.ts +36 -0
- package/src/compat/register/misc.ts +44 -0
- package/src/compat/register/otel.ts +288 -0
- package/src/compat/register/pages-api.ts +473 -0
- package/src/compat/register/ppr.ts +56 -0
- package/src/compat/register/protocol.ts +57 -0
- package/src/compat/register/proxy.ts +127 -0
- package/src/compat/register/render.ts +268 -0
- package/src/compat/register/routing.ts +410 -0
- package/src/compat/register/segment.ts +1903 -0
- package/src/compat/register/static-image.ts +21 -0
- package/src/compat/register/typed-routes.ts +35 -0
- package/src/compat/register/usecache.ts +131 -0
- package/src/compat/register/validation.ts +56 -0
- package/src/compat/segment/loading-boundary.ts +113 -0
- package/src/compat/segment/page-slot.ts +200 -0
- package/src/compat/segment/tree.ts +481 -0
- package/src/compat/segment/vary-key.ts +102 -0
- package/src/compat/segment/vary-params.ts +551 -0
- package/src/compat/static-params.ts +33 -0
- package/src/compat/tsconfig-defaults.ts +301 -0
- package/src/compat/typecheck/index.ts +1481 -0
- package/src/compat/typecheck/worker.ts +26 -0
- package/src/compat/typed-routes/index.ts +92 -0
- package/src/compat/typed-routes/manifest.ts +356 -0
- package/src/compat/typed-routes/typegen.ts +566 -0
- package/src/compat/validation/errors.ts +159 -0
- package/src/compat/validation/index.ts +1770 -0
- package/src/compat/validation/prerender-diagnostics.ts +1508 -0
- package/src/compat-bootstrap.ts +67 -0
- package/src/config.ts +218 -0
- package/src/css/build.ts +697 -0
- package/src/css/index.ts +2 -0
- package/src/css/postcss.ts +236 -0
- package/src/css/worker.ts +34 -0
- package/src/dev/client-actions.ts +35 -0
- package/src/dev/client-chunk-store.ts +92 -0
- package/src/dev/client-key-cache.ts +178 -0
- package/src/dev/global-css-cache.ts +212 -0
- package/src/dev/imports.ts +2430 -0
- package/src/dev/module-cache.ts +721 -0
- package/src/dev/module-generations.ts +38 -0
- package/src/dev/module-transform.ts +188 -0
- package/src/dev/node-module-bundle-cache.ts +63 -0
- package/src/dev/restart-cache.ts +10 -0
- package/src/dev/route-bundle-key-cache.ts +154 -0
- package/src/dev/route-facts-cache.ts +223 -0
- package/src/dev/server.ts +1710 -0
- package/src/dynamic/source.ts +307 -0
- package/src/dynamic/tree-shake.ts +262 -0
- package/src/env.ts +92 -0
- package/src/extensions.ts +1898 -0
- package/src/index.ts +34 -0
- package/src/internal.ts +43 -0
- package/src/islands/boundary-error.ts +8 -0
- package/src/islands/static-children.ts +37 -0
- package/src/islands/static-slots.ts +106 -0
- package/src/ppr-postpone.ts +24 -0
- package/src/ppr.ts +784 -0
- package/src/proxy.ts +752 -0
- package/src/render/hooks.ts +384 -0
- package/src/render/index.ts +1 -0
- package/src/render/island-context.ts +47 -0
- package/src/render/metadata.ts +857 -0
- package/src/render/renderer.ts +7391 -0
- package/src/render/resource-hints.ts +44 -0
- package/src/render/slots.tsx +679 -0
- package/src/request/context.ts +396 -0
- package/src/resolve/engine.ts +219 -0
- package/src/resolve/imports.ts +1104 -0
- package/src/resolve/scan-facts.ts +474 -0
- package/src/resolve/source-text.ts +86 -0
- package/src/routing/forwarded.ts +41 -0
- package/src/routing/handler.ts +271 -0
- package/src/routing/href.ts +203 -0
- package/src/routing/metadata.ts +1018 -0
- package/src/routing/request-runtime.ts +43 -0
- package/src/routing/routes.ts +2560 -0
- package/src/routing/slots.ts +432 -0
- package/src/runtime/server.ts +3453 -0
- package/src/runtime/vendor.ts +1160 -0
- package/src/style-modules.d.ts +9 -0
- package/src/typegen.ts +151 -0
- package/src/types.ts +725 -0
- package/src/utils/ansi.ts +9 -0
- package/src/utils/content-type.ts +31 -0
- package/src/utils/decode.ts +7 -0
- package/src/utils/dev-profile.ts +31 -0
- package/src/utils/error-log.ts +29 -0
- package/src/utils/fs-cache.ts +31 -0
- package/src/utils/fs.ts +119 -0
- package/src/utils/html.ts +46 -0
- package/src/utils/serialize.ts +378 -0
- package/src/utils/source.ts +35 -0
- package/src/utils/verbose.ts +39 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import type { Plugin } from 'esbuild';
|
|
4
|
+
import { frameworkRuntimeAliasEntries, pnextAliases, type CompatAliasTarget } from '../config';
|
|
5
|
+
import type { PNextConfig } from '../types';
|
|
6
|
+
import { escapeRegex, isIdentifier, uniqueIdentifier } from '../utils/source';
|
|
7
|
+
import { actionClientStubPlugin, hasClientActionModules } from './actions/client-plugin';
|
|
8
|
+
import { rewriteRootParamsSource } from './ppr/root-params-transform';
|
|
9
|
+
|
|
10
|
+
export type CompatConfig = Pick<PNextConfig, 'compat'>;
|
|
11
|
+
|
|
12
|
+
export function reactCompatEnabled(config: CompatConfig) {
|
|
13
|
+
return Boolean(config.compat?.next || config.compat?.react || reactCompilerOptions(config));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function reactCompilerOptions(config: CompatConfig) {
|
|
17
|
+
if (!config.compat?.reactCompiler) return undefined;
|
|
18
|
+
return { target: '18' };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function nextCompatEnabled(config: CompatConfig) {
|
|
22
|
+
return Boolean(config.compat?.next);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** The only `next/dist/*` deep imports pnext shims (see reference/compat.md "Smaller surfaces"). */
|
|
26
|
+
const SHIMMED_NEXT_DIST_PATHS = [
|
|
27
|
+
'next/dist/client/components/app-router-headers',
|
|
28
|
+
'next/dist/server/web/spec-extension/unstable-cache',
|
|
29
|
+
'next/dist/server/web/spec-extension/unstable-no-store',
|
|
30
|
+
'next/dist/server/web/spec-extension/revalidate',
|
|
31
|
+
'next/dist/server/app-render/work-unit-async-storage.external',
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Clear failure for compat-only imports in apps that never enabled the
|
|
36
|
+
* matching compat mode.
|
|
37
|
+
*/
|
|
38
|
+
export function missingCompatImportError(
|
|
39
|
+
config: CompatConfig,
|
|
40
|
+
specifier: string,
|
|
41
|
+
): string | undefined {
|
|
42
|
+
if (/^next(\/|$)/.test(specifier)) {
|
|
43
|
+
if (!nextCompatEnabled(config)) {
|
|
44
|
+
return `'${specifier}' requires Next compatibility. Enable compat.next in pnext.config.ts.`;
|
|
45
|
+
}
|
|
46
|
+
if (specifier.startsWith('next/dist/')) {
|
|
47
|
+
const bare = specifier.replace(/\.js$/, '');
|
|
48
|
+
if (!SHIMMED_NEXT_DIST_PATHS.includes(bare)) {
|
|
49
|
+
return (
|
|
50
|
+
`'${specifier}' is not a supported import: next/dist internals are not part of the ` +
|
|
51
|
+
`public surface pnext shims. Only these next/dist/* paths are shimmed:\n` +
|
|
52
|
+
SHIMMED_NEXT_DIST_PATHS.map(path => ` - ${path}`).join('\n') +
|
|
53
|
+
`\nSee reference/compat.md ("Smaller surfaces") and report the library that needs a new shim.`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
if (/^react(-dom)?(\/|$)/.test(specifier) && !reactCompatEnabled(config)) {
|
|
60
|
+
return `'${specifier}' requires React compatibility. Enable compat.react (or compat.next) in pnext.config.ts.`;
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function compatAliases(
|
|
66
|
+
config: CompatConfig,
|
|
67
|
+
target: CompatAliasTarget,
|
|
68
|
+
): Record<string, string> {
|
|
69
|
+
return {
|
|
70
|
+
...frameworkRuntimeAliasEntries(),
|
|
71
|
+
...pnextAliases(target),
|
|
72
|
+
...reactCompatAliases(config, target),
|
|
73
|
+
...nextCompatAliases(config, target),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* B4 react-server layer override for App RSC, route handlers, and edge
|
|
79
|
+
* modules. The base server shims keep client hooks for Pages/API and
|
|
80
|
+
* instrumentation compatibility.
|
|
81
|
+
*/
|
|
82
|
+
export function reactServerLayerAliases(config: CompatConfig): Record<string, string> {
|
|
83
|
+
if (!reactCompatEnabled(config)) return {};
|
|
84
|
+
const reactRoot = path.resolve(import.meta.dirname, 'react');
|
|
85
|
+
return {
|
|
86
|
+
react: path.join(reactRoot, 'react-server.ts'),
|
|
87
|
+
'react-dom': path.join(reactRoot, 'dom-react-server.ts'),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function reactCompatAliases(
|
|
92
|
+
config: CompatConfig,
|
|
93
|
+
target: CompatAliasTarget,
|
|
94
|
+
): Record<string, string> {
|
|
95
|
+
if (!reactCompatEnabled(config)) return {};
|
|
96
|
+
const reactRoot = path.resolve(import.meta.dirname, 'react');
|
|
97
|
+
const runtime = frameworkRuntimeAliasEntries();
|
|
98
|
+
return {
|
|
99
|
+
react: path.join(reactRoot, target === 'client' ? 'client.ts' : 'server.ts'),
|
|
100
|
+
// dom.ts wraps preact/compat with the form-action hooks react-dom ships
|
|
101
|
+
// (useFormState/useFormStatus/requestFormReset).
|
|
102
|
+
'react-dom': path.join(reactRoot, 'dom.ts'),
|
|
103
|
+
'react-dom/client': runtime['preact/compat/client'],
|
|
104
|
+
// react-dom/server (+ .edge/.browser entrypoints) shimmed over
|
|
105
|
+
// preact-render-to-string for the synchronous string renderers.
|
|
106
|
+
'react-dom/server': path.join(reactRoot, 'dom-server.ts'),
|
|
107
|
+
'react-dom/server.edge': path.join(reactRoot, 'dom-server.ts'),
|
|
108
|
+
'react-dom/server.browser': path.join(reactRoot, 'dom-server.ts'),
|
|
109
|
+
'react/jsx-runtime': runtime['preact/jsx-runtime'],
|
|
110
|
+
'react/jsx-dev-runtime': runtime['preact/jsx-dev-runtime'],
|
|
111
|
+
// React Compiler memo-cache runtime. Both the React 19 built-in specifier
|
|
112
|
+
// (`react/compiler-runtime`) and the standalone npm package name
|
|
113
|
+
// (`react-compiler-runtime`, emitted for a React 17/18 compiler target, and
|
|
114
|
+
// imported by pre-compiled libraries) map to a preact-backed shim. The npm
|
|
115
|
+
// package's own `require('react').useMemo` throws under preact SSR.
|
|
116
|
+
'react/compiler-runtime': path.join(reactRoot, 'compiler-runtime.ts'),
|
|
117
|
+
'react-compiler-runtime': path.join(reactRoot, 'compiler-runtime.ts'),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function nextCompatAliases(
|
|
122
|
+
config: CompatConfig,
|
|
123
|
+
target: CompatAliasTarget,
|
|
124
|
+
): Record<string, string> {
|
|
125
|
+
if (!nextCompatEnabled(config)) return {};
|
|
126
|
+
return nextCompatAliasEntries(target);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function nextCompatAliasEntries(target: CompatAliasTarget): Record<string, string> {
|
|
130
|
+
const nextRoot = path.resolve(import.meta.dirname, 'next');
|
|
131
|
+
return withJsExtensionAliases({
|
|
132
|
+
next: path.join(nextRoot, 'index.ts'),
|
|
133
|
+
// Client variant avoids node:async_hooks (render cache-meta collector) in
|
|
134
|
+
// browser bundles; the APIs are server-only in Next anyway.
|
|
135
|
+
'next/cache': path.join(nextRoot, target === 'client' ? 'client-cache.ts' : 'cache.ts'),
|
|
136
|
+
'next/config': path.join(nextRoot, 'config.ts'),
|
|
137
|
+
// CJS so an app `require('next/constants')` loads a genuine CommonJS module: the server runtime's
|
|
138
|
+
// onLoad transform only intercepts `[jt]sx?`, so a `.cjs` external is left to Bun's native loader
|
|
139
|
+
// and stays synchronously requirable - an ESM `.ts` external forced through that transform becomes
|
|
140
|
+
// async and require() returns undefined for every named export.
|
|
141
|
+
'next/constants': path.join(nextRoot, 'constants.cjs'),
|
|
142
|
+
'next/dynamic': path.join(nextRoot, 'dynamic.tsx'),
|
|
143
|
+
'next/error': path.join(nextRoot, 'error.tsx'),
|
|
144
|
+
'next/font': path.join(nextRoot, 'font', 'index.ts'),
|
|
145
|
+
'next/font/google': path.join(nextRoot, 'font', 'google.ts'),
|
|
146
|
+
'next/font/local': path.join(nextRoot, 'font', 'local.ts'),
|
|
147
|
+
'next/form': path.join(nextRoot, 'form.tsx'),
|
|
148
|
+
'next/head': path.join(nextRoot, 'head.tsx'),
|
|
149
|
+
'next/headers': path.join(nextRoot, 'headers.ts'),
|
|
150
|
+
'next/image': path.join(nextRoot, 'image.tsx'),
|
|
151
|
+
// The legacy component blanks a lazy image's `src` until it is near the
|
|
152
|
+
// viewport — its own module, not an alias of the modern one.
|
|
153
|
+
'next/legacy/image': path.join(nextRoot, 'legacy-image.tsx'),
|
|
154
|
+
'next/link': path.join(nextRoot, 'link.tsx'),
|
|
155
|
+
'next/navigation': path.join(
|
|
156
|
+
nextRoot,
|
|
157
|
+
target === 'client' ? 'client-navigation.ts' : 'navigation.ts',
|
|
158
|
+
),
|
|
159
|
+
'next/offline': path.join(nextRoot, 'offline.ts'),
|
|
160
|
+
'next/og': path.join(nextRoot, 'og.ts'),
|
|
161
|
+
'@vercel/og': path.join(nextRoot, 'og.ts'),
|
|
162
|
+
'next/root-params': path.join(nextRoot, 'root-params.ts'),
|
|
163
|
+
'next/router': path.join(nextRoot, 'router.ts'),
|
|
164
|
+
'next/script': path.join(nextRoot, target === 'client' ? 'client-script.tsx' : 'script.tsx'),
|
|
165
|
+
'next/web-vitals': path.join(nextRoot, 'web-vitals.ts'),
|
|
166
|
+
// Client variant avoids node:async_hooks (request scope / ppr / revalidate)
|
|
167
|
+
// in browser bundles; the request-scope APIs are server-only in Next anyway.
|
|
168
|
+
'next/server': path.join(nextRoot, target === 'client' ? 'client-server.ts' : 'server.ts'),
|
|
169
|
+
// Server compiles accept the marker (react-server condition equivalent);
|
|
170
|
+
// client bundles keep the package's own error behavior.
|
|
171
|
+
...(target === 'server' ? { 'server-only': path.join(nextRoot, 'server-only.ts') } : {}),
|
|
172
|
+
// client-only must resolve on both targets: a 'use client' page importing it
|
|
173
|
+
// is legitimately SSR'd (server compile) as well as browser-bundled.
|
|
174
|
+
'client-only': path.join(nextRoot, 'client-only.ts'),
|
|
175
|
+
// The styled-jsx source transform (css/styled-jsx.ts) rewrites <style jsx>
|
|
176
|
+
// blocks to import from this specifier; apps never install styled-jsx.
|
|
177
|
+
'styled-jsx': path.join(nextRoot, '..', 'css', 'styled-jsx-runtime.ts'),
|
|
178
|
+
// Deep next/dist imports that show up in real Next apps and e2e fixtures.
|
|
179
|
+
// Exact-match aliases only; unknown next/dist paths keep failing through
|
|
180
|
+
// missingCompatImportError / normal resolution.
|
|
181
|
+
'next/dist/client/components/app-router-headers': path.join(
|
|
182
|
+
nextRoot,
|
|
183
|
+
'dist',
|
|
184
|
+
'client',
|
|
185
|
+
'components',
|
|
186
|
+
'app-router-headers.ts',
|
|
187
|
+
),
|
|
188
|
+
'next/dist/server/web/spec-extension/unstable-cache': path.join(
|
|
189
|
+
nextRoot,
|
|
190
|
+
'dist',
|
|
191
|
+
'server',
|
|
192
|
+
'web',
|
|
193
|
+
'spec-extension',
|
|
194
|
+
'unstable-cache.ts',
|
|
195
|
+
),
|
|
196
|
+
'next/dist/server/web/spec-extension/unstable-no-store': path.join(
|
|
197
|
+
nextRoot,
|
|
198
|
+
'dist',
|
|
199
|
+
'server',
|
|
200
|
+
'web',
|
|
201
|
+
'spec-extension',
|
|
202
|
+
'unstable-no-store.ts',
|
|
203
|
+
),
|
|
204
|
+
'next/dist/server/web/spec-extension/revalidate': path.join(
|
|
205
|
+
nextRoot,
|
|
206
|
+
'dist',
|
|
207
|
+
'server',
|
|
208
|
+
'web',
|
|
209
|
+
'spec-extension',
|
|
210
|
+
'revalidate.ts',
|
|
211
|
+
),
|
|
212
|
+
// Direct import of the work-unit store (see the shim's header). Server-only;
|
|
213
|
+
// a browser bundle never reaches request context. CJS for the same reason as
|
|
214
|
+
// next/constants above: apps `require()` this deep internal, and a `.ts`
|
|
215
|
+
// external would become an async module through the onLoad transform, making
|
|
216
|
+
// the require() throw outright.
|
|
217
|
+
...(target === 'server'
|
|
218
|
+
? {
|
|
219
|
+
'next/dist/server/app-render/work-unit-async-storage.external': path.join(
|
|
220
|
+
nextRoot,
|
|
221
|
+
'dist',
|
|
222
|
+
'server',
|
|
223
|
+
'app-render',
|
|
224
|
+
'work-unit-async-storage.external.cjs',
|
|
225
|
+
),
|
|
226
|
+
}
|
|
227
|
+
: {}),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function withJsExtensionAliases(aliases: Record<string, string>) {
|
|
232
|
+
const entries = { ...aliases };
|
|
233
|
+
for (const [specifier, target] of Object.entries(aliases)) {
|
|
234
|
+
if (specifier !== 'next') entries[`${specifier}.js`] = target;
|
|
235
|
+
}
|
|
236
|
+
return entries;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* esbuild client-bundle aliasing for compat specifiers, with clear failures
|
|
241
|
+
* for compat-only imports instead of resolving to whatever react/next package
|
|
242
|
+
* happens to be installed.
|
|
243
|
+
*/
|
|
244
|
+
export function compatAliasPlugin(config: CompatConfig): Plugin {
|
|
245
|
+
const aliases = compatAliases(config, 'client');
|
|
246
|
+
const specifiers = Object.keys(aliases);
|
|
247
|
+
return {
|
|
248
|
+
name: 'pnext-compat-alias',
|
|
249
|
+
setup(build) {
|
|
250
|
+
// When compat.next discovered server-action modules, swap their client
|
|
251
|
+
// imports for the generated fetch stub. Registered here so the action
|
|
252
|
+
// stub participates in every client build without editing the client
|
|
253
|
+
// build pipeline. No-op when no action modules were discovered.
|
|
254
|
+
if (nextCompatEnabled(config) && hasClientActionModules()) {
|
|
255
|
+
void actionClientStubPlugin().setup(build);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
build.onResolve({ filter: /^(?:react(?:-dom)?|next)(?:\/|$)/ }, args => {
|
|
259
|
+
if (aliases[args.path]) return undefined;
|
|
260
|
+
const message = missingCompatImportError(config, args.path);
|
|
261
|
+
return message ? { errors: [{ text: message }] } : undefined;
|
|
262
|
+
});
|
|
263
|
+
if (specifiers.length === 0) return;
|
|
264
|
+
build.onResolve(
|
|
265
|
+
{ filter: new RegExp(`^(${specifiers.map(escapeRegex).join('|')})$`) },
|
|
266
|
+
async args => {
|
|
267
|
+
const target = aliases[args.path];
|
|
268
|
+
if (!target) return undefined;
|
|
269
|
+
if (path.isAbsolute(target)) return { path: target };
|
|
270
|
+
return build.resolve(target, {
|
|
271
|
+
kind: args.kind,
|
|
272
|
+
importer: args.importer,
|
|
273
|
+
namespace: args.namespace,
|
|
274
|
+
resolveDir: args.resolveDir,
|
|
275
|
+
});
|
|
276
|
+
},
|
|
277
|
+
);
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Compat apps render React's Suspense (the preact/compat class, via the react
|
|
283
|
+
// aliases or a direct import). That identity is loaded lazily and only when
|
|
284
|
+
// compat is enabled, so core apps never pull preact/compat into the server
|
|
285
|
+
// process — core streaming uses PNext's own Suspense marker instead.
|
|
286
|
+
let compatSuspense: unknown;
|
|
287
|
+
|
|
288
|
+
export async function ensureCompatSuspense(config: CompatConfig) {
|
|
289
|
+
if (compatSuspense !== undefined || !reactCompatEnabled(config)) return;
|
|
290
|
+
compatSuspense = ((await import('preact/compat')) as { Suspense: unknown }).Suspense;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function compatSuspenseType() {
|
|
294
|
+
return compatSuspense;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* next/font runtime targets. The SERVER runtime (runtime.ts) emits CSS + font
|
|
299
|
+
* files and imports `node:*` at module scope; the CLIENT runtime
|
|
300
|
+
* (runtime-client.ts) is `node:*`-free and only returns the pre-hashed
|
|
301
|
+
* className/variable/style a browser bundle needs (CORE GAP D). The rewrite
|
|
302
|
+
* binds font imports to the runtime matching the build target, mirroring the
|
|
303
|
+
* next/cache + next/navigation client/server alias split in nextCompatAliasEntries.
|
|
304
|
+
*/
|
|
305
|
+
const fontRuntimePaths: Record<CompatAliasTarget, string> = {
|
|
306
|
+
server: path.join(import.meta.dirname, 'next', 'font', 'runtime.ts'),
|
|
307
|
+
client: path.join(import.meta.dirname, 'next', 'font', 'runtime-client.ts'),
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Next-compat source rewrites for `next/font/google` and `next/font/local` imports. Only invoked when
|
|
312
|
+
* compat.next is enabled; without it the imports survive untouched and fail with the missing-compat
|
|
313
|
+
* resolve error.
|
|
314
|
+
*
|
|
315
|
+
* `target` selects the runtime the rewritten imports bind to: 'server' (default) for server compiles,
|
|
316
|
+
* 'client' for browser bundles, which avoids pulling node:async_hooks / node:crypto into the bundle.
|
|
317
|
+
*/
|
|
318
|
+
export function rewriteNextFontSource(
|
|
319
|
+
source: string,
|
|
320
|
+
file: string,
|
|
321
|
+
target: CompatAliasTarget = 'server',
|
|
322
|
+
root?: string,
|
|
323
|
+
) {
|
|
324
|
+
if (!source.includes('next/font') && !source.includes('next/root-params')) return source;
|
|
325
|
+
// The single next/root-params rewrite in the tree; every other call site
|
|
326
|
+
// reaches it through this pass rather than re-running its own copy.
|
|
327
|
+
source = rewriteRootParamsSource(source);
|
|
328
|
+
const runtimePath = fontRuntimePaths[target];
|
|
329
|
+
let next = rewriteNextGoogleFontImports(source, runtimePath);
|
|
330
|
+
const localFontNames = nextLocalFontImportNames(next);
|
|
331
|
+
if (localFontNames.size === 0) return next;
|
|
332
|
+
const callerFile = localFontCallerFile(file, root);
|
|
333
|
+
// Rewrite each `NAME(...)` call to `NAME.withFile(<file>)(...)` so the runtime
|
|
334
|
+
// gets the caller path (for `src` resolution + a stable per-caller hash). The
|
|
335
|
+
// client runtime only uses `callerFile` for the hash (which must match the
|
|
336
|
+
// server's exactly), so the SAME `withFile(<file>)` wrapping applies to both.
|
|
337
|
+
for (const name of localFontNames) {
|
|
338
|
+
next = next.replace(
|
|
339
|
+
new RegExp(`(?<![.$\\w])${escapeRegex(name)}\\s*\\(`, 'g'),
|
|
340
|
+
`${name}.withFile(${JSON.stringify(callerFile)})(`,
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
// Next names local-font families after the binding (`const myFont = …`),
|
|
344
|
+
// including when the loader lives in a package under node_modules.
|
|
345
|
+
for (const name of localFontNames) {
|
|
346
|
+
next = next.replace(
|
|
347
|
+
new RegExp(`((?:export\\s+)?(?:const|let|var)\\s+)([A-Za-z_$][\\w$]*)(\\s*=\\s*)${escapeRegex(name)}\\.withFile\\(([^)]*)\\)\\(`, 'g'),
|
|
348
|
+
(_statement, declaration: string, binding: string, equals: string, caller: string) =>
|
|
349
|
+
`${declaration}${binding}${equals}${name}.withFile(${caller}, ${JSON.stringify(binding)})(`,
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
// Replace the bare `import NAME from 'next/font/local'` with an import bound
|
|
353
|
+
// directly to the runtime's absolute path. This avoids relying on server-side
|
|
354
|
+
// `next/font/local` alias resolution, which does not reach modules imported
|
|
355
|
+
// from workspace dirs outside app/ (e.g. a `fonts/index.js` barrel).
|
|
356
|
+
next = next.replace(
|
|
357
|
+
/^[ \t]*import\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])([^'"]+)\2\s*;?[ \t]*$/gm,
|
|
358
|
+
(statement, name: string, _quote: string, specifier: string) => {
|
|
359
|
+
if (!isNextLocalFontSpecifier(specifier)) return statement;
|
|
360
|
+
return `import { localFont as ${name} } from ${JSON.stringify(runtimePath)};`;
|
|
361
|
+
},
|
|
362
|
+
);
|
|
363
|
+
return next;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function rewriteNextGoogleFontImports(source: string, runtimePath: string) {
|
|
367
|
+
const statements: string[] = [];
|
|
368
|
+
const helperName = uniqueIdentifier(source, '__pnextGoogleFont');
|
|
369
|
+
const namespaceHelperName = uniqueIdentifier(source, '__pnextGoogleFonts', helperName);
|
|
370
|
+
const rewritten = source.replace(
|
|
371
|
+
/^[ \t]*import\s+(?!type\b)((?:[^\n;]|\n(?![ \t]*(?:import|export)\b))*?)\s+from\s+['"]next\/font\/google['"]\s*;?[ \t]*(?:\r?\n)?/gm,
|
|
372
|
+
(_statement: string, clause: string) => {
|
|
373
|
+
const bindings = nextGoogleFontBindings(clause);
|
|
374
|
+
if (bindings.length === 0) return '';
|
|
375
|
+
statements.push(
|
|
376
|
+
...bindings.map(binding =>
|
|
377
|
+
googleFontBindingSource(binding, helperName, namespaceHelperName),
|
|
378
|
+
),
|
|
379
|
+
);
|
|
380
|
+
return '';
|
|
381
|
+
},
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
if (statements.length === 0) return source;
|
|
385
|
+
return [
|
|
386
|
+
`import { googleFont as ${helperName} } from ${JSON.stringify(runtimePath)};`,
|
|
387
|
+
`const ${namespaceHelperName} = new Proxy({}, { get: (_target, key) => typeof key === 'string' ? ${helperName}(key.replace(/_/g, ' ')) : undefined });`,
|
|
388
|
+
...statements,
|
|
389
|
+
rewritten,
|
|
390
|
+
].join('\n');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
interface GoogleFontBinding {
|
|
394
|
+
imported: string;
|
|
395
|
+
local: string;
|
|
396
|
+
namespace?: boolean;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function nextGoogleFontBindings(clause: string): GoogleFontBinding[] {
|
|
400
|
+
const trimmed = clause.trim();
|
|
401
|
+
const namespace = /^\*\s+as\s+([A-Za-z_$][\w$]*)$/.exec(trimmed)?.[1];
|
|
402
|
+
if (namespace) return [{ imported: namespace, local: namespace, namespace: true }];
|
|
403
|
+
|
|
404
|
+
const named = /\{([\s\S]*)\}/.exec(trimmed)?.[1];
|
|
405
|
+
if (!named) return [];
|
|
406
|
+
return named
|
|
407
|
+
.split(',')
|
|
408
|
+
.map(part => part.trim())
|
|
409
|
+
.filter(Boolean)
|
|
410
|
+
.flatMap(part => {
|
|
411
|
+
if (part.startsWith('type ')) return [];
|
|
412
|
+
const [importedRaw, localRaw] = part.split(/\s+as\s+/);
|
|
413
|
+
const imported = importedRaw?.trim();
|
|
414
|
+
const local = (localRaw ?? importedRaw)?.trim();
|
|
415
|
+
if (!imported || !local || !isIdentifier(imported) || !isIdentifier(local)) return [];
|
|
416
|
+
return [{ imported, local }];
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function googleFontBindingSource(
|
|
421
|
+
binding: GoogleFontBinding,
|
|
422
|
+
helperName: string,
|
|
423
|
+
namespaceHelperName: string,
|
|
424
|
+
) {
|
|
425
|
+
if (binding.namespace) return `const ${binding.local} = ${namespaceHelperName};`;
|
|
426
|
+
return `const ${binding.local} = ${helperName}(${JSON.stringify(googleFontFamily(binding.imported))});`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function googleFontFamily(exportName: string) {
|
|
430
|
+
return exportName.replace(/_/g, ' ');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function nextLocalFontImportNames(source: string) {
|
|
434
|
+
const names = new Set<string>();
|
|
435
|
+
const pattern = /\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])([^'"]+)\2/g;
|
|
436
|
+
let match: RegExpExecArray | null;
|
|
437
|
+
while ((match = pattern.exec(source))) {
|
|
438
|
+
if (match[1] && match[3] && isNextLocalFontSpecifier(match[3])) names.add(match[1]);
|
|
439
|
+
}
|
|
440
|
+
return names;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function isNextLocalFontSpecifier(specifier: string) {
|
|
444
|
+
if (specifier === 'next/font/local' || specifier === 'next/font/local.js') return true;
|
|
445
|
+
const normalized = specifier.replace(/^file:\/\//, '').replace(/\\/g, '/');
|
|
446
|
+
return /\/compat\/next\/font\/local\.[jt]s$/.test(normalized);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function localFontCallerFile(file: string, root: string | undefined) {
|
|
450
|
+
if (path.isAbsolute(file)) return file;
|
|
451
|
+
if (file.startsWith('.')) return path.resolve(root ?? process.cwd(), file);
|
|
452
|
+
if (!root) return file;
|
|
453
|
+
try {
|
|
454
|
+
return createRequire(path.join(root, 'pnext-resolve.cjs')).resolve(file);
|
|
455
|
+
} catch {
|
|
456
|
+
return file;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// after() request-API phase rules (COMPAT).
|
|
2
|
+
//
|
|
3
|
+
// When a page render schedules after() work, the callback runs detached: there is no request scope, so
|
|
4
|
+
// headers()/cookies()/connection() must throw Next's exact "Route <path> used `<api>()` inside
|
|
5
|
+
// `after()`. This is not supported." message. In a route handler or server action, the same APIs called
|
|
6
|
+
// inside after() work because the async context captured a live request scope, so those cases never
|
|
7
|
+
// reach this guard.
|
|
8
|
+
//
|
|
9
|
+
// The route pathname is stashed on the work unit at request start so the detached callback can name it.
|
|
10
|
+
|
|
11
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
12
|
+
import { currentRequest, getWorkUnit } from '../../request/context';
|
|
13
|
+
|
|
14
|
+
const REQUEST_PATH_KEY = Symbol.for('pnext.requestPath');
|
|
15
|
+
const INSPECT_CUSTOM = Symbol.for('nodejs.util.inspect.custom');
|
|
16
|
+
const requestApiScope = new AsyncLocalStorage<boolean>();
|
|
17
|
+
|
|
18
|
+
/** Run one after() task with its request-API capability. */
|
|
19
|
+
export function runWithAfterRequestApiScope<T>(allowed: boolean, callback: () => T): T {
|
|
20
|
+
return requestApiScope.run(allowed, callback);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Whether the current after() task may use request APIs. */
|
|
24
|
+
export function afterRequestApisAllowed(): boolean {
|
|
25
|
+
return requestApiScope.getStore() === true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Stash the request pathname on the active work unit for after() diagnostics. */
|
|
29
|
+
export function recordRequestPath(pathname: string): void {
|
|
30
|
+
const unit = getWorkUnit();
|
|
31
|
+
if (unit) (unit.compat ??= {})[REQUEST_PATH_KEY] = pathname;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function currentRoutePath(): string {
|
|
35
|
+
const recorded = getWorkUnit()?.compat?.[REQUEST_PATH_KEY] as string | undefined;
|
|
36
|
+
if (recorded) return recorded;
|
|
37
|
+
try {
|
|
38
|
+
const request = currentRequest();
|
|
39
|
+
return request ? new URL(request.url).pathname : '';
|
|
40
|
+
} catch {
|
|
41
|
+
return '';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Returns an Error-shaped object rather than a real `new Error`: the app's
|
|
46
|
+
// `catch (err) { console.error(..., err) }` prints it as `Error: <message>`
|
|
47
|
+
// with no misleading pnext-internal stack frame (the suite matches the message
|
|
48
|
+
// text exactly). Typed as Error so throw sites satisfy `only-throw-error`.
|
|
49
|
+
export function lifecycleError(message: string): Error {
|
|
50
|
+
const formatted = `Error: ${message}`;
|
|
51
|
+
return {
|
|
52
|
+
name: 'Error',
|
|
53
|
+
message,
|
|
54
|
+
stack: formatted,
|
|
55
|
+
toString: () => formatted,
|
|
56
|
+
[INSPECT_CUSTOM]: () => formatted,
|
|
57
|
+
} as unknown as Error;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Throw Next's "used inside after()" error when a request API is accessed from a
|
|
62
|
+
* detached after() callback (render phase, no live request scope). Returns
|
|
63
|
+
* without throwing in every legal case so callers proceed to the normal path.
|
|
64
|
+
*/
|
|
65
|
+
export function assertRequestApiAllowedInAfter(api: 'headers()' | 'cookies()' | 'connection()'): void {
|
|
66
|
+
const unit = getWorkUnit();
|
|
67
|
+
if (unit?.phase !== 'after') return;
|
|
68
|
+
// Rendering keeps a synthetic request context during prerendering, so the
|
|
69
|
+
// explicit after-task capability—not merely currentRequest()—defines this.
|
|
70
|
+
if (afterRequestApisAllowed() && currentRequest()) return;
|
|
71
|
+
const suffix = api === 'connection()' ? '' : ' This is not supported.';
|
|
72
|
+
throw lifecycleError(
|
|
73
|
+
`Route ${currentRoutePath()} used \`${api}\` inside \`after()\`.${suffix}`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* draftMode() stays readable inside after(), but .enable()/.disable() throw in
|
|
79
|
+
* every context (Next's exact message names the route + method).
|
|
80
|
+
*/
|
|
81
|
+
export function assertDraftModeToggleAllowedInAfter(method: 'enable' | 'disable'): void {
|
|
82
|
+
if (getWorkUnit()?.phase !== 'after') return;
|
|
83
|
+
throw lifecycleError(
|
|
84
|
+
`Route ${currentRoutePath()} used "draftMode().${method}()" inside \`after()\``,
|
|
85
|
+
);
|
|
86
|
+
}
|