@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,522 @@
|
|
|
1
|
+
import ReactCompat from 'preact/compat';
|
|
2
|
+
import { options, type ComponentChildren, type ComponentType, type VNode } from 'preact';
|
|
3
|
+
import { wrapComponentForPrimitiveThrows } from '../client/errors/primitive-throw';
|
|
4
|
+
|
|
5
|
+
const reactForwardRefSymbol = Symbol.for('react.forward_ref');
|
|
6
|
+
const react19RefCompatInstalled = Symbol.for('pnext.react19-ref-compat-installed');
|
|
7
|
+
const reactTextSeparatorSymbol = Symbol.for('pnext.react-text-separator');
|
|
8
|
+
|
|
9
|
+
type PNextPreactOptions = typeof options & {
|
|
10
|
+
[react19RefCompatInstalled]?: true;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type RefCompatibleComponent = ComponentType<Record<string, unknown>> & {
|
|
14
|
+
$$typeof?: symbol;
|
|
15
|
+
prototype?: { render?: unknown };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// preact's `options.vnode` is process-global and cannot be uninstalled, so its BEHAVIOR follows the
|
|
19
|
+
// active extension host instead: one server process can serve a compat app and then a pure-core one, and
|
|
20
|
+
// the core app must not inherit React parity it never asked for. Defaults to on - importing this module
|
|
21
|
+
// IS compat being wired, and the client never resets.
|
|
22
|
+
let reactCompatActive = true;
|
|
23
|
+
|
|
24
|
+
/** Enable/disable the react-compat vnode parity pass (see reactCompatActive). */
|
|
25
|
+
export function setReactCompatActive(active: boolean): void {
|
|
26
|
+
reactCompatActive = active;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const pnextOptions = options as PNextPreactOptions;
|
|
30
|
+
if (!pnextOptions[react19RefCompatInstalled]) {
|
|
31
|
+
const previousVNode = options.vnode?.bind(options);
|
|
32
|
+
options.vnode = vnode => {
|
|
33
|
+
previousVNode?.(vnode);
|
|
34
|
+
if (!reactCompatActive) return;
|
|
35
|
+
applyReact19RefProp(vnode);
|
|
36
|
+
applyAsyncClientComponent(vnode);
|
|
37
|
+
applyPrimitiveThrowSafety(vnode);
|
|
38
|
+
applyThenableChildren(vnode);
|
|
39
|
+
applyReactTextSeparators(vnode);
|
|
40
|
+
};
|
|
41
|
+
pnextOptions[react19RefCompatInstalled] = true;
|
|
42
|
+
|
|
43
|
+
// Effect scheduling parity with React: preact/hooks flushes passive effects
|
|
44
|
+
// via requestAnimationFrame + setTimeout(35) (afterNextFrame), both governed
|
|
45
|
+
// by the page clock. React flushes them through its scheduler's
|
|
46
|
+
// MessageChannel, which fake-timer installations (Playwright's CDP clock in
|
|
47
|
+
// Next's own e2e suites) never intercept. Under an installed fake clock the
|
|
48
|
+
// rAF path loses its "before the next idle callback" ordering and
|
|
49
|
+
// Suspense-boundary promotions land after a test's post-drain DOM sample.
|
|
50
|
+
// Route the flush through a MessageChannel task so effect timing is
|
|
51
|
+
// clock-independent, like React's.
|
|
52
|
+
if ((process.browser || typeof window !== 'undefined') && typeof MessageChannel !== 'undefined') {
|
|
53
|
+
const queue: (() => void)[] = [];
|
|
54
|
+
const channel = new MessageChannel();
|
|
55
|
+
channel.port1.onmessage = () => {
|
|
56
|
+
for (const callback of queue.splice(0)) callback();
|
|
57
|
+
};
|
|
58
|
+
(options as { requestAnimationFrame?: (cb: () => void) => void }).requestAnimationFrame =
|
|
59
|
+
callback => {
|
|
60
|
+
queue.push(callback);
|
|
61
|
+
channel.port2.postMessage(null);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function applyReactTextSeparators(vnode: VNode) {
|
|
67
|
+
// Server-only. `process.browser` is a client-bundle define (true) and
|
|
68
|
+
// undefined on the server, so this body folds out of browser builds; the
|
|
69
|
+
// `typeof window` twin keeps the runtime check for unbundled consumers.
|
|
70
|
+
if (process.browser || typeof window !== 'undefined') return;
|
|
71
|
+
const props = vnode.props as Record<string, unknown>;
|
|
72
|
+
const children = props.children;
|
|
73
|
+
if (!Array.isArray(children) || children.length < 2) return;
|
|
74
|
+
const values = children as unknown[];
|
|
75
|
+
|
|
76
|
+
const separated: unknown[] = [];
|
|
77
|
+
let changed = false;
|
|
78
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
79
|
+
const child = values[index];
|
|
80
|
+
separated.push(child);
|
|
81
|
+
if (!mayRenderText(child) || !mayRenderText(values[index + 1])) continue;
|
|
82
|
+
separated.push({
|
|
83
|
+
type: reactTextSeparatorSymbol,
|
|
84
|
+
props: { UNSTABLE_comment: ' ' },
|
|
85
|
+
});
|
|
86
|
+
changed = true;
|
|
87
|
+
}
|
|
88
|
+
if (changed) props.children = separated;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function mayRenderText(value: unknown) {
|
|
92
|
+
if (typeof value === 'string' || typeof value === 'number') return true;
|
|
93
|
+
if (!value || typeof value !== 'object' || !('type' in value)) return false;
|
|
94
|
+
return typeof (value as VNode).type !== 'string';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function applyThenableChildren(vnode: VNode) {
|
|
98
|
+
const props = vnode.props as Record<string, unknown>;
|
|
99
|
+
const children = props.children;
|
|
100
|
+
const resolved = thenableChildren(children);
|
|
101
|
+
if (resolved !== children) props.children = resolved;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function thenableChildren(value: unknown): unknown {
|
|
105
|
+
if (value && typeof (value as PromiseLike<unknown>).then === 'function') {
|
|
106
|
+
return ReactCompat.createElement(
|
|
107
|
+
ReactCompat.Suspense,
|
|
108
|
+
{ fallback: null },
|
|
109
|
+
ReactCompat.createElement(ThenableChild, {
|
|
110
|
+
value: value as PromiseLike<ComponentChildren>,
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
if (!Array.isArray(value)) return value;
|
|
115
|
+
let changed = false;
|
|
116
|
+
const children = value.map(child => {
|
|
117
|
+
const resolved = thenableChildren(child);
|
|
118
|
+
if (resolved !== child) changed = true;
|
|
119
|
+
return resolved;
|
|
120
|
+
});
|
|
121
|
+
return changed ? children : value;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function ThenableChild({ value }: { value: PromiseLike<ComponentChildren> }) {
|
|
125
|
+
return use(value);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const asyncClientComponents = new WeakMap<
|
|
129
|
+
ComponentType<Record<string, unknown>>,
|
|
130
|
+
ComponentType<Record<string, unknown>>
|
|
131
|
+
>();
|
|
132
|
+
|
|
133
|
+
function applyAsyncClientComponent(vnode: VNode) {
|
|
134
|
+
if ((!process.browser && typeof window === 'undefined') || typeof vnode.type !== 'function') return;
|
|
135
|
+
const component = vnode.type as ComponentType<Record<string, unknown>>;
|
|
136
|
+
if (component.constructor.name !== 'AsyncFunction') return;
|
|
137
|
+
const renderComponent = component as (props: Record<string, unknown>) => ComponentChildren;
|
|
138
|
+
let wrapper = asyncClientComponents.get(component);
|
|
139
|
+
if (!wrapper) {
|
|
140
|
+
wrapper = props => {
|
|
141
|
+
const [result] = ReactCompat.useState(() =>
|
|
142
|
+
Promise.resolve(renderComponent(props)),
|
|
143
|
+
);
|
|
144
|
+
return ReactCompat.createElement(
|
|
145
|
+
ReactCompat.Suspense,
|
|
146
|
+
{ fallback: null },
|
|
147
|
+
ReactCompat.createElement(ThenableChild, { value: result }),
|
|
148
|
+
);
|
|
149
|
+
};
|
|
150
|
+
asyncClientComponents.set(component, wrapper);
|
|
151
|
+
}
|
|
152
|
+
vnode.type = wrapper;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function applyPrimitiveThrowSafety(vnode: VNode) {
|
|
156
|
+
// Client-only: this exists to stop a thrown undefined/null from crashing preact's OWN diff.js `e.then`
|
|
157
|
+
// suspense check during real preact diffing. The server's resolveServerTree is a custom RSC-style tree
|
|
158
|
+
// walker, not preact's diff - it never hits that crash, and wrapping component references there risks
|
|
159
|
+
// losing the symbol-keyed metadata resolveServerTree branches on.
|
|
160
|
+
if (!process.browser && typeof window === 'undefined') return;
|
|
161
|
+
if (typeof vnode.type !== 'function') return;
|
|
162
|
+
vnode.type = wrapComponentForPrimitiveThrows(vnode.type);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function applyReact19RefProp(vnode: VNode) {
|
|
166
|
+
if (!vnode.ref || typeof vnode.type !== 'function') return;
|
|
167
|
+
const component = vnode.type as RefCompatibleComponent;
|
|
168
|
+
if (component.prototype?.render || component.$$typeof === reactForwardRefSymbol) return;
|
|
169
|
+
|
|
170
|
+
vnode.props = { ...(vnode.props as Record<string, unknown>), ref: vnode.ref } as unknown as typeof vnode.props;
|
|
171
|
+
vnode.ref = null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// React 19 APIs preact/compat lacks - use(), useActionState, useOptimistic (function declarations below,
|
|
175
|
+
// hoisted) - must ALSO live on the default export: app code does `import React from 'react';
|
|
176
|
+
// React.use(...)`, not only named imports. server.ts spreads this default into ReactServer, so the
|
|
177
|
+
// augmentation covers both layers.
|
|
178
|
+
export default Object.assign(ReactCompat, { use, useActionState, useOptimistic, useTransition });
|
|
179
|
+
|
|
180
|
+
export const Children = ReactCompat.Children;
|
|
181
|
+
export const Component = ReactCompat.Component;
|
|
182
|
+
export const Fragment = ReactCompat.Fragment;
|
|
183
|
+
export const PureComponent = ReactCompat.PureComponent;
|
|
184
|
+
export const StrictMode = ReactCompat.StrictMode;
|
|
185
|
+
export const Suspense = ReactCompat.Suspense;
|
|
186
|
+
export const SuspenseList = ReactCompat.SuspenseList;
|
|
187
|
+
export const cloneElement = ReactCompat.cloneElement;
|
|
188
|
+
export const createContext = ReactCompat.createContext;
|
|
189
|
+
export const createElement = ReactCompat.createElement;
|
|
190
|
+
export const createFactory = ReactCompat.createFactory;
|
|
191
|
+
export const createPortal = ReactCompat.createPortal;
|
|
192
|
+
export const createRef = ReactCompat.createRef;
|
|
193
|
+
export const findDOMNode = ReactCompat.findDOMNode;
|
|
194
|
+
export const flushSync = ReactCompat.flushSync;
|
|
195
|
+
export const forwardRef = ReactCompat.forwardRef;
|
|
196
|
+
export const hydrate = ReactCompat.hydrate;
|
|
197
|
+
export const isFragment = ReactCompat.isFragment;
|
|
198
|
+
export const isMemo = ReactCompat.isMemo;
|
|
199
|
+
export const isValidElement = ReactCompat.isValidElement;
|
|
200
|
+
export const lazy = ReactCompat.lazy;
|
|
201
|
+
export const memo = ReactCompat.memo;
|
|
202
|
+
export const render = ReactCompat.render;
|
|
203
|
+
export const startTransition = ReactCompat.startTransition;
|
|
204
|
+
export const unmountComponentAtNode = ReactCompat.unmountComponentAtNode;
|
|
205
|
+
export const unstable_batchedUpdates = ReactCompat.unstable_batchedUpdates;
|
|
206
|
+
export const useCallback = ReactCompat.useCallback;
|
|
207
|
+
export const useContext = ReactCompat.useContext;
|
|
208
|
+
export const useDebugValue = ReactCompat.useDebugValue;
|
|
209
|
+
export const useDeferredValue = ReactCompat.useDeferredValue;
|
|
210
|
+
export const useEffect = ReactCompat.useEffect;
|
|
211
|
+
export const useId = ReactCompat.useId;
|
|
212
|
+
export const useImperativeHandle = ReactCompat.useImperativeHandle;
|
|
213
|
+
export const useInsertionEffect = ReactCompat.useInsertionEffect;
|
|
214
|
+
export const useLayoutEffect = ReactCompat.useLayoutEffect;
|
|
215
|
+
export const useMemo = ReactCompat.useMemo;
|
|
216
|
+
export const useReducer = ReactCompat.useReducer;
|
|
217
|
+
export const useRef = ReactCompat.useRef;
|
|
218
|
+
export const useState = ReactCompat.useState;
|
|
219
|
+
export const useSyncExternalStore = ReactCompat.useSyncExternalStore;
|
|
220
|
+
export const version = ReactCompat.version;
|
|
221
|
+
|
|
222
|
+
// True when a preact hooks call would succeed (a component render is in flight). preact fires
|
|
223
|
+
// options._render just before invoking a component and options.diffed after it commits; between the two,
|
|
224
|
+
// hooks have a current component. Server-side tree resolution invokes some client components as plain
|
|
225
|
+
// functions, outside any preact render, where hooks would throw - useActionState/useOptimistic fall back
|
|
226
|
+
// to SSR-equivalent static values there instead.
|
|
227
|
+
let renderInFlight = false;
|
|
228
|
+
|
|
229
|
+
function hooksUsable(): boolean {
|
|
230
|
+
return renderInFlight;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
{
|
|
234
|
+
const anyOptions = options as unknown as Record<string, unknown>;
|
|
235
|
+
const previousRender = anyOptions.__r as ((vnode: VNode) => void) | undefined;
|
|
236
|
+
anyOptions.__r = (vnode: VNode) => {
|
|
237
|
+
renderInFlight = true;
|
|
238
|
+
previousRender?.(vnode);
|
|
239
|
+
};
|
|
240
|
+
const previousDiffed = options.diffed?.bind(options);
|
|
241
|
+
options.diffed = vnode => {
|
|
242
|
+
renderInFlight = false;
|
|
243
|
+
previousDiffed?.(vnode);
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* React 19's useActionState, on preact hooks. Returns [state, dispatch, isPending]; dispatch(payload)
|
|
249
|
+
* runs `action(prevState, payload)` (async ok) and swaps the state when it settles. The returned dispatch
|
|
250
|
+
* is also valid as a `<form action={...}>` value - the pnext client runtime intercepts function form
|
|
251
|
+
* actions and calls them with the form's FormData. Queueing follows React: concurrent dispatches chain
|
|
252
|
+
* in order against the latest settled state rather than racing.
|
|
253
|
+
*/
|
|
254
|
+
export function useActionState<State, Payload = FormData>(
|
|
255
|
+
action: (state: Awaited<State>, payload: Payload) => State | Promise<State>,
|
|
256
|
+
initialState: Awaited<State>,
|
|
257
|
+
_permalink?: string,
|
|
258
|
+
): [state: Awaited<State>, dispatch: (payload: Payload) => void, isPending: boolean] {
|
|
259
|
+
// Server resolve may invoke a client component as a plain function (no
|
|
260
|
+
// preact render context, so hooks throw). SSR output for useActionState is
|
|
261
|
+
// always the (possibly progressively-updated) initial state with a dispatch
|
|
262
|
+
// that only works after hydration; fall back to exactly that. The dispatch
|
|
263
|
+
// carries form-state metadata so SSR can progressively enhance
|
|
264
|
+
// <form action={dispatch}>.
|
|
265
|
+
if (!hooksUsable()) {
|
|
266
|
+
const staticInitial = (consumeActionStateOverride() ?? { value: initialState })
|
|
267
|
+
.value as Awaited<State>;
|
|
268
|
+
const staticDispatch = (payload: Payload) =>
|
|
269
|
+
void Promise.resolve(action(staticInitial, payload));
|
|
270
|
+
tagFormStateDispatch(staticDispatch, action, staticInitial, _permalink);
|
|
271
|
+
return [staticInitial, staticDispatch, false];
|
|
272
|
+
}
|
|
273
|
+
const initialRef = ReactCompat.useRef<{ value: Awaited<State> } | null>(null);
|
|
274
|
+
if (!initialRef.current) {
|
|
275
|
+
// A progressive (no-JS) submission re-renders the page with the action's
|
|
276
|
+
// result as the form state; the server (and the inline hydration script)
|
|
277
|
+
// publish it via a consumed-once global override.
|
|
278
|
+
initialRef.current = {
|
|
279
|
+
value: (consumeActionStateOverride() ?? { value: initialState }).value as Awaited<State>,
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const [state, setState] = ReactCompat.useState<Awaited<State>>(initialRef.current.value);
|
|
283
|
+
const [pending, setPending] = ReactCompat.useState(0);
|
|
284
|
+
const lastSettled = ReactCompat.useRef<Awaited<State>>(initialRef.current.value);
|
|
285
|
+
const chain = ReactCompat.useRef<Promise<unknown>>(Promise.resolve());
|
|
286
|
+
// A rejected action must surface to the nearest error boundary during an actual preact render (only
|
|
287
|
+
// diff() wraps component calls in the getDerivedStateFromError/componentDidCatch try/catch).
|
|
288
|
+
// preact/hooks' setState invokes a functional updater EAGERLY at call time, to bail out on an unchanged
|
|
289
|
+
// value, rather than deferring it to the render - so `setState(() => { throw error })` from inside an
|
|
290
|
+
// async .catch handler throws immediately in that microtask, outside any render call stack and outside
|
|
291
|
+
// any try/catch, producing an unhandled rejection instead of reaching the boundary. Stash the error in
|
|
292
|
+
// a ref and force a re-render instead; the throw then happens inside this hook's own render call.
|
|
293
|
+
const pendingError = ReactCompat.useRef<{ error: unknown } | null>(null);
|
|
294
|
+
const [, forceRender] = ReactCompat.useState(0);
|
|
295
|
+
|
|
296
|
+
const dispatch = ReactCompat.useCallback(
|
|
297
|
+
(payload: Payload) => {
|
|
298
|
+
setPending(count => count + 1);
|
|
299
|
+
// Chain in dispatch order against the latest settled state. A rejected
|
|
300
|
+
// action leaves the previous state in place (matching React, where the
|
|
301
|
+
// error propagates to the nearest error boundary via the transition) and
|
|
302
|
+
// must not poison the queue for later dispatches.
|
|
303
|
+
chain.current = chain.current.then(async () => {
|
|
304
|
+
try {
|
|
305
|
+
const redirectsBefore = actionRedirectCount();
|
|
306
|
+
const next = (await action(lastSettled.current, payload));
|
|
307
|
+
// A redirect renders the destination's initial form state even when
|
|
308
|
+
// the shared layout island itself survives the navigation.
|
|
309
|
+
if (next === undefined && actionRedirectCount() !== redirectsBefore) {
|
|
310
|
+
lastSettled.current = initialRef.current!.value;
|
|
311
|
+
setState(() => initialRef.current!.value);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
lastSettled.current = next;
|
|
315
|
+
setState(() => next);
|
|
316
|
+
} finally {
|
|
317
|
+
setPending(count => count - 1);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// React propagates action errors to the nearest error boundary (they
|
|
321
|
+
// are not catchable at the dispatch site). Stash it and force a render:
|
|
322
|
+
// a class error boundary in the tree catches the throw below; without
|
|
323
|
+
// one the uncaught render error reaches the window 'error' event, where
|
|
324
|
+
// the compat entry's error.js overlay picks it up.
|
|
325
|
+
chain.current = chain.current.catch(error => {
|
|
326
|
+
if (!process.browser && typeof window === 'undefined') return;
|
|
327
|
+
pendingError.current = { error };
|
|
328
|
+
forceRender(count => count + 1);
|
|
329
|
+
});
|
|
330
|
+
},
|
|
331
|
+
[action],
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
if (pendingError.current) {
|
|
335
|
+
const { error } = pendingError.current;
|
|
336
|
+
pendingError.current = null;
|
|
337
|
+
throw error;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
tagFormStateDispatch(dispatch, action, state, _permalink);
|
|
341
|
+
return [state, dispatch, pending > 0];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Form-state metadata attached to a useActionState dispatch so the server
|
|
346
|
+
* renderer can progressively enhance <form action={dispatch}>: the underlying
|
|
347
|
+
* action (for its wire id), the state at render time (posted back in a hidden
|
|
348
|
+
* field so the server can run `action(state, formData)` without JS), and the
|
|
349
|
+
* optional permalink target.
|
|
350
|
+
*/
|
|
351
|
+
export interface FormStateDispatchMeta {
|
|
352
|
+
action: (state: never, payload: never) => unknown;
|
|
353
|
+
state: unknown;
|
|
354
|
+
permalink?: string;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function tagFormStateDispatch(
|
|
358
|
+
dispatch: (payload: never) => void,
|
|
359
|
+
action: (state: never, payload: never) => unknown,
|
|
360
|
+
state: unknown,
|
|
361
|
+
permalink?: string,
|
|
362
|
+
) {
|
|
363
|
+
(dispatch as unknown as { $$pnextFormState?: FormStateDispatchMeta }).$$pnextFormState = {
|
|
364
|
+
action: action,
|
|
365
|
+
state,
|
|
366
|
+
...(permalink !== undefined ? { permalink } : {}),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Consumed-once initial-state override for useActionState, published either by
|
|
372
|
+
* the server before re-rendering a page for a progressive form submission, or
|
|
373
|
+
* by the inline hydration script that mirrors it to the client.
|
|
374
|
+
*/
|
|
375
|
+
/** Redirect counter the action-client runtime bumps (see markActionRedirected). */
|
|
376
|
+
function actionRedirectCount(): number {
|
|
377
|
+
return (globalThis as { __pnextActionRedirects?: number }).__pnextActionRedirects ?? 0;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export function consumeActionStateOverride(): { value: unknown } | undefined {
|
|
381
|
+
const holder = globalThis as { __PNEXT_ACTION_STATE__?: unknown };
|
|
382
|
+
if (!('__PNEXT_ACTION_STATE__' in holder)) return undefined;
|
|
383
|
+
const override = holder.__PNEXT_ACTION_STATE__;
|
|
384
|
+
if (
|
|
385
|
+
override !== null &&
|
|
386
|
+
typeof override === 'object' &&
|
|
387
|
+
'skip' in override &&
|
|
388
|
+
typeof override.skip === 'number' &&
|
|
389
|
+
override.skip > 0
|
|
390
|
+
) {
|
|
391
|
+
override.skip--;
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
delete holder.__PNEXT_ACTION_STATE__;
|
|
395
|
+
return {
|
|
396
|
+
value:
|
|
397
|
+
override !== null && typeof override === 'object' && 'value' in override
|
|
398
|
+
? override.value
|
|
399
|
+
: override,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* React 19's useTransition, on preact hooks. preact/compat's own useTransition is a no-op stub that never
|
|
405
|
+
* tracks pending state, so an async transition callback's in-flight window is invisible. Track it with a
|
|
406
|
+
* counter: startTransition(cb) runs cb, and when cb returns a thenable holds `isPending` true until it
|
|
407
|
+
* settles. Concurrent transitions overlap, each incrementing and decrementing, so isPending stays true
|
|
408
|
+
* while ANY is in flight - matching React.
|
|
409
|
+
*/
|
|
410
|
+
export function useTransition(): [boolean, (callback: () => void | Promise<void>) => void] {
|
|
411
|
+
if (!hooksUsable()) return [false, callback => void callback()];
|
|
412
|
+
const [pending, setPending] = ReactCompat.useState(0);
|
|
413
|
+
const startTransition = ReactCompat.useCallback((callback: () => void | Promise<void>) => {
|
|
414
|
+
const mpaBefore = (globalThis as { __pnextMpaNavigation?: number }).__pnextMpaNavigation ?? 0;
|
|
415
|
+
let result: void | Promise<void>;
|
|
416
|
+
try {
|
|
417
|
+
result = callback();
|
|
418
|
+
} catch {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
const mpaAfter = (globalThis as { __pnextMpaNavigation?: number }).__pnextMpaNavigation ?? 0;
|
|
422
|
+
if (mpaAfter !== mpaBefore) {
|
|
423
|
+
setPending(count => count + 1);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
if (result && typeof (result as PromiseLike<unknown>).then === 'function') {
|
|
427
|
+
setPending(count => count + 1);
|
|
428
|
+
void Promise.resolve(result).finally(() => setPending(count => count - 1));
|
|
429
|
+
}
|
|
430
|
+
}, []);
|
|
431
|
+
return [pending > 0, startTransition];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* React 19's useOptimistic, on preact hooks (simplified: the optimistic value
|
|
436
|
+
* resets whenever the passthrough (base) value changes on a rerender, which is
|
|
437
|
+
* when the real state has caught up).
|
|
438
|
+
*/
|
|
439
|
+
export function useOptimistic<State, Payload = State>(
|
|
440
|
+
passthrough: State,
|
|
441
|
+
reducer?: (state: State, payload: Payload) => State,
|
|
442
|
+
): [State, (payload: Payload) => void] {
|
|
443
|
+
if (!hooksUsable()) return [passthrough, () => undefined];
|
|
444
|
+
const [optimistic, setOptimistic] = ReactCompat.useState<{ value: State } | null>(null);
|
|
445
|
+
const lastBase = ReactCompat.useRef(passthrough);
|
|
446
|
+
if (lastBase.current !== passthrough) {
|
|
447
|
+
lastBase.current = passthrough;
|
|
448
|
+
if (optimistic) setOptimistic(null);
|
|
449
|
+
}
|
|
450
|
+
const dispatch = ReactCompat.useCallback(
|
|
451
|
+
(payload: Payload) => {
|
|
452
|
+
setOptimistic(current => ({
|
|
453
|
+
value: reducer
|
|
454
|
+
? reducer(current ? current.value : lastBase.current, payload)
|
|
455
|
+
: (payload as unknown as State),
|
|
456
|
+
}));
|
|
457
|
+
},
|
|
458
|
+
[reducer],
|
|
459
|
+
);
|
|
460
|
+
return [optimistic ? optimistic.value : passthrough, dispatch];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
interface TrackedThenable<T> extends PromiseLike<T> {
|
|
464
|
+
status?: 'pending' | 'fulfilled' | 'rejected';
|
|
465
|
+
value?: T;
|
|
466
|
+
reason?: unknown;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Replay state for use() in server components: the renderer replays a component
|
|
470
|
+
// after a thrown thenable settles, and use() call N must resolve to the thenable
|
|
471
|
+
// recorded on the previous attempt (the rerun creates a fresh promise).
|
|
472
|
+
export interface UseThenableState {
|
|
473
|
+
thenables: TrackedThenable<unknown>[];
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
let activeUseState: UseThenableState | null = null;
|
|
477
|
+
let activeUseIndex = 0;
|
|
478
|
+
|
|
479
|
+
export function withUseThenableState<T>(state: UseThenableState, run: () => T): T {
|
|
480
|
+
const previousState = activeUseState;
|
|
481
|
+
const previousIndex = activeUseIndex;
|
|
482
|
+
activeUseState = state;
|
|
483
|
+
activeUseIndex = 0;
|
|
484
|
+
try {
|
|
485
|
+
return run();
|
|
486
|
+
} finally {
|
|
487
|
+
activeUseState = previousState;
|
|
488
|
+
activeUseIndex = previousIndex;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// React 19's use(): unwrap a thenable via suspense, or read a context.
|
|
493
|
+
export function use<T>(usable: PromiseLike<T> | Parameters<typeof ReactCompat.useContext>[0]): T {
|
|
494
|
+
if (usable && typeof (usable as PromiseLike<T>).then === 'function') {
|
|
495
|
+
let thenable = usable as TrackedThenable<T>;
|
|
496
|
+
if (activeUseState) {
|
|
497
|
+
const index = activeUseIndex++;
|
|
498
|
+
const existing = activeUseState.thenables[index];
|
|
499
|
+
if (existing) thenable = existing as TrackedThenable<T>;
|
|
500
|
+
else activeUseState.thenables[index] = thenable;
|
|
501
|
+
}
|
|
502
|
+
if (thenable.status === 'fulfilled') return thenable.value as T;
|
|
503
|
+
if (thenable.status === 'rejected') throw thenable.reason;
|
|
504
|
+
if (thenable.status !== 'pending') {
|
|
505
|
+
thenable.status = 'pending';
|
|
506
|
+
thenable.then(
|
|
507
|
+
value => {
|
|
508
|
+
thenable.status = 'fulfilled';
|
|
509
|
+
thenable.value = value;
|
|
510
|
+
},
|
|
511
|
+
reason => {
|
|
512
|
+
thenable.status = 'rejected';
|
|
513
|
+
thenable.reason = reason;
|
|
514
|
+
},
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
// React use() protocol: suspend by throwing the thenable itself.
|
|
518
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
519
|
+
throw thenable;
|
|
520
|
+
}
|
|
521
|
+
return ReactCompat.useContext(usable as Parameters<typeof ReactCompat.useContext>[0]) as T;
|
|
522
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// True `react-server` condition entry - used ONLY for modules Next compiles under the react-server layer
|
|
2
|
+
// proper: proxy/middleware and RSC/server-action graphs. Unlike ./server.ts, which keeps no-op hook stubs
|
|
3
|
+
// as NAMED exports for pages/api backward compatibility, this module must NOT expose client hook APIs at
|
|
4
|
+
// all: real React's react-server entry has no hook dispatcher, so useState/useEffect are absent from the
|
|
5
|
+
// module's export list entirely, not merely undefined on the default object.
|
|
6
|
+
export {
|
|
7
|
+
Children,
|
|
8
|
+
Component,
|
|
9
|
+
Fragment,
|
|
10
|
+
PureComponent,
|
|
11
|
+
StrictMode,
|
|
12
|
+
Suspense,
|
|
13
|
+
SuspenseList,
|
|
14
|
+
cloneElement,
|
|
15
|
+
createContext,
|
|
16
|
+
createElement,
|
|
17
|
+
createFactory,
|
|
18
|
+
createPortal,
|
|
19
|
+
createRef,
|
|
20
|
+
findDOMNode,
|
|
21
|
+
flushSync,
|
|
22
|
+
forwardRef,
|
|
23
|
+
hydrate,
|
|
24
|
+
isFragment,
|
|
25
|
+
isMemo,
|
|
26
|
+
isValidElement,
|
|
27
|
+
lazy,
|
|
28
|
+
memo,
|
|
29
|
+
render,
|
|
30
|
+
startTransition,
|
|
31
|
+
unmountComponentAtNode,
|
|
32
|
+
unstable_batchedUpdates,
|
|
33
|
+
useId,
|
|
34
|
+
useContext,
|
|
35
|
+
// React's own react-server entry exports these three alongside useId/use, so
|
|
36
|
+
// mirror it — a server component may legitimately call them.
|
|
37
|
+
useMemo,
|
|
38
|
+
useCallback,
|
|
39
|
+
useDebugValue,
|
|
40
|
+
use,
|
|
41
|
+
useActionState,
|
|
42
|
+
useOptimistic,
|
|
43
|
+
version,
|
|
44
|
+
} from './preact';
|
|
45
|
+
export { cache } from '../../api/cache';
|
|
46
|
+
|
|
47
|
+
import { ViewTransition, addTransitionType } from './view-transition';
|
|
48
|
+
export { ViewTransition, addTransitionType };
|
|
49
|
+
|
|
50
|
+
import { currentPrerenderScope } from '../../ppr';
|
|
51
|
+
import { currentCacheSignal } from '../../cache/context';
|
|
52
|
+
|
|
53
|
+
// React.cacheSignal(): the AbortSignal for the current render. During a build
|
|
54
|
+
// prerender it is the prerender scope's signal; during a request/dynamic render
|
|
55
|
+
// it is the cache scope's. Null outside any render. Slow cache components bound
|
|
56
|
+
// their polling on it (see cache-components ppr-partial-hydration).
|
|
57
|
+
export function cacheSignal(): AbortSignal | null {
|
|
58
|
+
const prerender = currentPrerenderScope();
|
|
59
|
+
if (prerender) return prerender.signal;
|
|
60
|
+
return currentCacheSignal();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
import ReactCompat from './preact';
|
|
64
|
+
import { cache as serverCache } from '../../api/cache';
|
|
65
|
+
|
|
66
|
+
const ReactServer = { ...ReactCompat } as typeof ReactCompat & {
|
|
67
|
+
cache: typeof serverCache;
|
|
68
|
+
cacheSignal: typeof cacheSignal;
|
|
69
|
+
};
|
|
70
|
+
delete (ReactServer as { useState?: unknown }).useState;
|
|
71
|
+
delete (ReactServer as { useEffect?: unknown }).useEffect;
|
|
72
|
+
delete (ReactServer as { useLayoutEffect?: unknown }).useLayoutEffect;
|
|
73
|
+
delete (ReactServer as { useInsertionEffect?: unknown }).useInsertionEffect;
|
|
74
|
+
delete (ReactServer as { useImperativeHandle?: unknown }).useImperativeHandle;
|
|
75
|
+
delete (ReactServer as { useReducer?: unknown }).useReducer;
|
|
76
|
+
delete (ReactServer as { useRef?: unknown }).useRef;
|
|
77
|
+
delete (ReactServer as { useDeferredValue?: unknown }).useDeferredValue;
|
|
78
|
+
delete (ReactServer as { useTransition?: unknown }).useTransition;
|
|
79
|
+
delete (ReactServer as { useSyncExternalStore?: unknown }).useSyncExternalStore;
|
|
80
|
+
ReactServer.cache = serverCache;
|
|
81
|
+
ReactServer.cacheSignal = cacheSignal;
|
|
82
|
+
Object.assign(ReactServer, { ViewTransition, addTransitionType });
|
|
83
|
+
|
|
84
|
+
export default ReactServer;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Next's window.next router shim (COMPAT). Injected into the streamed document
|
|
2
|
+
// body so legacy `window.next.router` callers (tests eval it, older libraries
|
|
3
|
+
// poke it) see a pages-router-shaped surface: isReady, pathname/asPath/query
|
|
4
|
+
// backed by the __PNEXT_PAGES_DATA__ JSON node the materialized pages wrappers
|
|
5
|
+
// emit (fallback: location), and push/replace that prefer the soft-navigation
|
|
6
|
+
// runtime (window.__PNEXT_ROUTER__) with a hard-navigation fallback.
|
|
7
|
+
export function nextRouterShimScript(): string {
|
|
8
|
+
return (
|
|
9
|
+
'<script>window.next=window.next||{};window.next.version=window.next.version||"16.3.0";(function(){' +
|
|
10
|
+
'var r=window.next.router=window.next.router||{};' +
|
|
11
|
+
'function d(){try{var n=document.getElementById("__PNEXT_PAGES_DATA__");' +
|
|
12
|
+
'return n&&n.textContent?JSON.parse(n.textContent):null}catch(e){return null}}' +
|
|
13
|
+
'function q(){var s=d(),o={};if(s&&s.query){for(var k in s.query)o[k]=s.query[k]}' +
|
|
14
|
+
'new URLSearchParams(location.search).forEach(function(v,k){if(k!=="_rsc")o[k]=v});return o}' +
|
|
15
|
+
'r.isReady=true;r.isFallback=r.isFallback||false;' +
|
|
16
|
+
'if(!Object.getOwnPropertyDescriptor(r,"query"))Object.defineProperty(r,"query",{get:q,configurable:true});' +
|
|
17
|
+
'if(!Object.getOwnPropertyDescriptor(r,"pathname"))Object.defineProperty(r,"pathname",{get:function(){var s=d();return(s&&s.pathname)||location.pathname},configurable:true});' +
|
|
18
|
+
'if(!Object.getOwnPropertyDescriptor(r,"asPath"))Object.defineProperty(r,"asPath",{get:function(){return location.pathname+location.search},configurable:true});' +
|
|
19
|
+
'r.push=r.push||function(href){var s=String(href);' +
|
|
20
|
+
'if(window.__PNEXT_ROUTER__&&window.__PNEXT_ROUTER__.navigate){return window.__PNEXT_ROUTER__.navigate(s).then(function(){return true})}' +
|
|
21
|
+
'location.assign(s);return Promise.resolve(true)};' +
|
|
22
|
+
'r.replace=r.replace||function(href){location.replace(String(href));return Promise.resolve(true)};' +
|
|
23
|
+
'r.prefetch=r.prefetch||function(){return Promise.resolve(null)};' +
|
|
24
|
+
'})();</script>'
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// use() replay for server components (COMPAT - react).
|
|
2
|
+
//
|
|
3
|
+
// React 19's use() suspends a synchronous component by throwing a thenable; once the thenable settles
|
|
4
|
+
// the renderer replays the component from the top, and use() call N must resolve to the thenable it
|
|
5
|
+
// recorded on the previous attempt, since the rerun creates fresh promises. preact has no such
|
|
6
|
+
// machinery, so this wraps a single server-component invocation with that replay loop:
|
|
7
|
+
//
|
|
8
|
+
// - a fresh UseThenableState tracks the thenables use() sees, indexed by call order.
|
|
9
|
+
// - the invocation runs inside that state scope. If a synchronous component throws the thenable use()
|
|
10
|
+
// suspended on, it is awaited and the component replays with the SAME state, so the next attempt's
|
|
11
|
+
// use() call reads the now-settled tracked thenable and returns its value.
|
|
12
|
+
// - a non-thenable throw propagates unchanged.
|
|
13
|
+
// - an async component returns a promise and awaits its own use() promises, so it needs no replay.
|
|
14
|
+
//
|
|
15
|
+
// Registered onto renderExtensions.wrapServerComponentInvoke; a pure-core app keeps the pass-through
|
|
16
|
+
// default and never loads this module.
|
|
17
|
+
|
|
18
|
+
import { withUseThenableState, type UseThenableState } from './preact';
|
|
19
|
+
|
|
20
|
+
function isThenable(value: unknown): value is PromiseLike<unknown> {
|
|
21
|
+
return (
|
|
22
|
+
value != null &&
|
|
23
|
+
(typeof value === 'object' || typeof value === 'function') &&
|
|
24
|
+
typeof (value as PromiseLike<unknown>).then === 'function'
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Run a server-component invocation with use()-replay support. Synchronous
|
|
30
|
+
* components that suspend via use() are awaited and replayed against the same
|
|
31
|
+
* thenable state; async components (and plain returns) pass through.
|
|
32
|
+
*/
|
|
33
|
+
export function invokeServerComponentWithUse<T>(invoke: () => T): T {
|
|
34
|
+
const state: UseThenableState = { thenables: [] };
|
|
35
|
+
|
|
36
|
+
const attempt = (): T => {
|
|
37
|
+
try {
|
|
38
|
+
return withUseThenableState(state, invoke);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
// A pending use() thenable suspends the component: await it, then replay
|
|
41
|
+
// with the same state so the settled thenable resolves in place.
|
|
42
|
+
if (isThenable(error)) return Promise.resolve(error).then(attempt, attempt) as T;
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return attempt();
|
|
48
|
+
}
|