@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,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source-text scanning to find Next-style server actions ('use server').
|
|
3
|
+
*
|
|
4
|
+
* This is deliberately regex/heuristic based, not a full parser. It targets the
|
|
5
|
+
* common Next fixture shapes and documents its limitations rather than trying to
|
|
6
|
+
* be a compiler. The reliable, fully-static path is a module-level 'use server'
|
|
7
|
+
* file with named exports; inline actions inside components are best-effort.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { actionId, workspaceRelative } from './ids';
|
|
11
|
+
|
|
12
|
+
const DIRECTIVE = /^(['"])use server\1\s*;?$/;
|
|
13
|
+
const CACHE_DIRECTIVE = /^(['"])use cache\1\s*;?$/;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* True when the file's FIRST statement is a 'use server' directive, marking the
|
|
17
|
+
* whole module as a server-action module (every export is an action).
|
|
18
|
+
*
|
|
19
|
+
* Leading comments (line and block), a shebang, and blank lines are skipped, as
|
|
20
|
+
* a real JS engine would when looking for a directive prologue.
|
|
21
|
+
*/
|
|
22
|
+
export function moduleLevelUseServer(source: string): boolean {
|
|
23
|
+
const first = firstStatementText(stripLeading(source));
|
|
24
|
+
return first !== undefined && DIRECTIVE.test(first.trim());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* True when the file's FIRST statement is a plain 'use cache' directive. Such a module's exports are
|
|
29
|
+
* server references exactly like a 'use server' module: a client component that imports one must get
|
|
30
|
+
* RPC stubs, since the cached functions always execute (and cache) on the server. Named handler
|
|
31
|
+
* variants are function-level only in practice and excluded.
|
|
32
|
+
*/
|
|
33
|
+
export function moduleLevelUseCache(source: string): boolean {
|
|
34
|
+
const first = firstStatementText(stripLeading(source));
|
|
35
|
+
return first !== undefined && CACHE_DIRECTIVE.test(first.trim());
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface InlineAction {
|
|
39
|
+
/** Local (and, when exported, exported) name of the action function. */
|
|
40
|
+
name: string;
|
|
41
|
+
/** True when the function is a named export (`export ...` / re-exported). */
|
|
42
|
+
exported: boolean;
|
|
43
|
+
/** Character offset of the start of the matched declaration in `source`. */
|
|
44
|
+
start: number;
|
|
45
|
+
/** Character offset just past the `"use server"` directive that marked it. */
|
|
46
|
+
directiveEnd: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Find inline 'use server' actions: functions whose body opens with a
|
|
51
|
+
* `"use server"` directive. Handles the two common Next shapes:
|
|
52
|
+
*
|
|
53
|
+
* async function foo() { "use server"; ... }
|
|
54
|
+
* const foo = async (...) => { "use server"; ... }
|
|
55
|
+
*
|
|
56
|
+
* Both with and without a leading `export`. Returns local names + spans.
|
|
57
|
+
*
|
|
58
|
+
* Limitations (documented, not fixed):
|
|
59
|
+
* - No full parser: a `"use server"` string that is not the first statement of a
|
|
60
|
+
* function body may still match if it sits right after `{`; conversely unusual
|
|
61
|
+
* formatting (directive on a line with trailing code) can be missed.
|
|
62
|
+
* - Default exports and object-method shorthand are not detected here.
|
|
63
|
+
* - Actions defined inline in a component and passed to <form action={fn}> are
|
|
64
|
+
* found as local declarations, but whether they are truly exported/reachable
|
|
65
|
+
* as an RPC target can't be resolved statically. Prefer named exports of a
|
|
66
|
+
* module-level 'use server' file for the reliable wire path.
|
|
67
|
+
*/
|
|
68
|
+
export function inlineUseServerFunctions(source: string): InlineAction[] {
|
|
69
|
+
const found: InlineAction[] = [];
|
|
70
|
+
const seen = new Set<string>();
|
|
71
|
+
|
|
72
|
+
// Shape 1: [export] [async] function NAME(...) { "use server"
|
|
73
|
+
const fnDecl =
|
|
74
|
+
/(\bexport\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\([^)]*\)\s*\{\s*(['"])use server\3\s*;?/g;
|
|
75
|
+
collect(fnDecl, source, found, seen, { nameGroup: 2, exportGroup: 1, dirGroup: 3 });
|
|
76
|
+
|
|
77
|
+
// Shape 2: [export] const NAME = [async] (...) => { "use server"
|
|
78
|
+
// also covers `= async function (...) { "use server"`
|
|
79
|
+
const arrowOrExpr =
|
|
80
|
+
/(\bexport\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[A-Za-z_$][\w$]*|function\s*\*?\s*\([^)]*\))\s*(?:=>\s*)?\{\s*(['"])use server\3\s*;?/g;
|
|
81
|
+
collect(arrowOrExpr, source, found, seen, { nameGroup: 2, exportGroup: 1, dirGroup: 3 });
|
|
82
|
+
|
|
83
|
+
return found.sort((a, b) => a.start - b.start);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface Groups {
|
|
87
|
+
nameGroup: number;
|
|
88
|
+
exportGroup: number;
|
|
89
|
+
dirGroup: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function collect(
|
|
93
|
+
pattern: RegExp,
|
|
94
|
+
source: string,
|
|
95
|
+
out: InlineAction[],
|
|
96
|
+
seen: Set<string>,
|
|
97
|
+
groups: Groups,
|
|
98
|
+
) {
|
|
99
|
+
let match: RegExpExecArray | null;
|
|
100
|
+
while ((match = pattern.exec(source))) {
|
|
101
|
+
const name = match[groups.nameGroup];
|
|
102
|
+
if (!name || seen.has(name)) continue;
|
|
103
|
+
seen.add(name);
|
|
104
|
+
out.push({
|
|
105
|
+
name,
|
|
106
|
+
exported: Boolean(match[groups.exportGroup]),
|
|
107
|
+
start: match.index,
|
|
108
|
+
directiveEnd: match.index + match[0].length,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Names to treat as action RPC targets for a module-level 'use server' file:
|
|
115
|
+
* every named export plus a default export flagged as `'default'`. Regex-based
|
|
116
|
+
* export scanning; covers `export function`, `export const`, `export { a, b }`,
|
|
117
|
+
* `export { a as b }`, `export default`.
|
|
118
|
+
*/
|
|
119
|
+
export function moduleActionExports(source: string): string[] {
|
|
120
|
+
const names = new Set<string>();
|
|
121
|
+
|
|
122
|
+
// export [async] function NAME / export const|let|var NAME
|
|
123
|
+
const declRe =
|
|
124
|
+
/\bexport\s+(?:async\s+)?(?:function\s*\*?\s+|const\s+|let\s+|var\s+|class\s+)([A-Za-z_$][\w$]*)/g;
|
|
125
|
+
for (let m; (m = declRe.exec(source)); ) if (m[1]) names.add(m[1]);
|
|
126
|
+
|
|
127
|
+
// export default ...
|
|
128
|
+
if (/\bexport\s+default\b/.test(source)) names.add('default');
|
|
129
|
+
|
|
130
|
+
// export { a, b as c } (ignore `export type { ... }`)
|
|
131
|
+
const listRe = /\bexport\s+(?!type\b)\{([^}]*)\}/g;
|
|
132
|
+
for (let m; (m = listRe.exec(source)); ) {
|
|
133
|
+
for (const part of (m[1] ?? '').split(',')) {
|
|
134
|
+
const trimmed = part.trim();
|
|
135
|
+
if (!trimmed || trimmed.startsWith('type ')) continue;
|
|
136
|
+
const exportedName = /\bas\s+([A-Za-z_$][\w$]*)\s*$/.exec(trimmed)?.[1];
|
|
137
|
+
const name = exportedName ?? trimmed;
|
|
138
|
+
if (/^[A-Za-z_$][\w$]*$/.test(name)) names.add(name);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return [...names];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Strip a leading shebang and BOM so directive detection starts at real code. */
|
|
146
|
+
function stripLeading(source: string): string {
|
|
147
|
+
let s = source.replace(/^\uFEFF/, '');
|
|
148
|
+
if (s.startsWith('#!')) {
|
|
149
|
+
const nl = s.indexOf('\n');
|
|
150
|
+
s = nl === -1 ? '' : s.slice(nl + 1);
|
|
151
|
+
}
|
|
152
|
+
return s;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Return the text of the first statement, skipping leading comments and blank
|
|
157
|
+
* lines. Used only to check the directive prologue; returns the first
|
|
158
|
+
* `;`-or-newline-terminated token run.
|
|
159
|
+
*/
|
|
160
|
+
function firstStatementText(source: string): string | undefined {
|
|
161
|
+
let i = 0;
|
|
162
|
+
const n = source.length;
|
|
163
|
+
while (i < n) {
|
|
164
|
+
const ch = source[i];
|
|
165
|
+
if (ch === ' ' || ch === '\t' || ch === '\r' || ch === '\n') {
|
|
166
|
+
i++;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (ch === '/' && source[i + 1] === '/') {
|
|
170
|
+
const nl = source.indexOf('\n', i);
|
|
171
|
+
if (nl === -1) return undefined;
|
|
172
|
+
i = nl + 1;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (ch === '/' && source[i + 1] === '*') {
|
|
176
|
+
const end = source.indexOf('*/', i + 2);
|
|
177
|
+
if (end === -1) return undefined;
|
|
178
|
+
i = end + 2;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
if (i >= n) return undefined;
|
|
184
|
+
// Read until end of line or a semicolon (directive is a single statement).
|
|
185
|
+
let j = i;
|
|
186
|
+
while (j < n && source[j] !== '\n' && source[j] !== ';') j++;
|
|
187
|
+
const end = source[j] === ';' ? j + 1 : j;
|
|
188
|
+
return source.slice(i, end);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Anonymous inline actions (`onClick={async () => { 'use server'; ... }}`)
|
|
193
|
+
* carry no compile-tagged id; the directive in the live function source is the
|
|
194
|
+
* only signal separating them from plain client handlers when serialization is
|
|
195
|
+
* restricted to identified actions.
|
|
196
|
+
*/
|
|
197
|
+
export function opensWithUseServerDirective(fn: unknown): boolean {
|
|
198
|
+
if (typeof fn !== 'function') return false;
|
|
199
|
+
let source: string;
|
|
200
|
+
try {
|
|
201
|
+
source = String(fn);
|
|
202
|
+
} catch {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
const body = source.indexOf('{', source.indexOf(')') + 1);
|
|
206
|
+
if (body === -1) return false;
|
|
207
|
+
return /^\s*(['"])use server\1/.test(source.slice(body + 1));
|
|
208
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { builtinModules } from 'node:module';
|
|
4
|
+
import { listFilesSync } from '../../utils/fs';
|
|
5
|
+
import type { ResolvedConfig } from '../../config';
|
|
6
|
+
import { nextCompatEnabled } from '..';
|
|
7
|
+
import { importSpecifiers } from '../../resolve/scan-facts';
|
|
8
|
+
import { readSourceSync } from '../../resolve/source-text';
|
|
9
|
+
import {
|
|
10
|
+
packageNameOfSpecifier,
|
|
11
|
+
resolutionRootForFile,
|
|
12
|
+
resolvePackageSpecifier,
|
|
13
|
+
} from '../../resolve/imports';
|
|
14
|
+
import { moduleActionExports, moduleLevelUseCache, moduleLevelUseServer } from './detect';
|
|
15
|
+
import { canonical, canonicalActionId } from './client-plugin';
|
|
16
|
+
import { registerActionModule, type ActionEntry } from './registry';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One discovered server-action module: its source path, the action export
|
|
20
|
+
* names, the wire ids for each, and the module path that runAction's
|
|
21
|
+
* importModule() should load (a compiled output path in prod, the source path
|
|
22
|
+
* in dev).
|
|
23
|
+
*/
|
|
24
|
+
export interface DiscoveredActionModule {
|
|
25
|
+
/** Source file of the 'use server' module. */
|
|
26
|
+
file: string;
|
|
27
|
+
/** Action export names (named exports + 'default'). */
|
|
28
|
+
exports: string[];
|
|
29
|
+
/** actionId(file, exportName, root) keyed by export name. */
|
|
30
|
+
ids: Record<string, string>;
|
|
31
|
+
/** What runAction hands to importModule() (defaults to `file`). */
|
|
32
|
+
modulePath: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ActionDiscovery {
|
|
36
|
+
modules: DiscoveredActionModule[];
|
|
37
|
+
/** Every source file that is a 'use server' module, for the client alias. */
|
|
38
|
+
actionFiles: Set<string>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Scan the app for module-level `'use server'` files - the reliable, fully static wire path. Each such
|
|
43
|
+
* file contributes every named export (plus a default export) as an RPC target.
|
|
44
|
+
*
|
|
45
|
+
* Inline `'use server'` functions inside Server Components are intentionally NOT discovered here: they
|
|
46
|
+
* cannot be resolved to a stable module export reachable as an RPC target. They still work when passed
|
|
47
|
+
* to <form action={fn}> as a local declaration rendered on the server.
|
|
48
|
+
*/
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
50
|
+
export async function discoverActions(
|
|
51
|
+
config: ResolvedConfig,
|
|
52
|
+
options: { modulePathFor?: (file: string) => string } = {},
|
|
53
|
+
): Promise<ActionDiscovery> {
|
|
54
|
+
const empty: ActionDiscovery = { modules: [], actionFiles: new Set() };
|
|
55
|
+
if (!nextCompatEnabled(config)) return empty;
|
|
56
|
+
|
|
57
|
+
const roots = actionScanRoots(config);
|
|
58
|
+
const seen = new Set<string>();
|
|
59
|
+
const files: string[] = [];
|
|
60
|
+
// Synchronous on purpose: every await hands the loop back to the build's sync route-fact walk and
|
|
61
|
+
// gate steps, which hold it long enough to stretch this work into a span the client stage then
|
|
62
|
+
// waits on. Running it straight through costs the same CPU and unblocks the stage.
|
|
63
|
+
const listings = roots.map(root => (existsSync(root) ? listFilesSync(root) : []));
|
|
64
|
+
for (const listing of listings) {
|
|
65
|
+
for (const file of listing) {
|
|
66
|
+
const resolved = path.resolve(file);
|
|
67
|
+
if (seen.has(resolved) || !isScriptFile(resolved)) continue;
|
|
68
|
+
seen.add(resolved);
|
|
69
|
+
files.push(resolved);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const sources = readSources(files);
|
|
73
|
+
for (const file of discoverNodeModuleActionFiles(config, files, sources, seen)) {
|
|
74
|
+
files.push(file);
|
|
75
|
+
seen.add(file);
|
|
76
|
+
}
|
|
77
|
+
for (const [file, source] of readSources(files.filter(file => !sources.has(file)))) {
|
|
78
|
+
sources.set(file, source);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const modules: DiscoveredActionModule[] = [];
|
|
82
|
+
const actionFiles = new Set<string>();
|
|
83
|
+
for (const file of files) {
|
|
84
|
+
const source = sources.get(file)!;
|
|
85
|
+
// Module-level 'use cache' files are server-reference modules too: their
|
|
86
|
+
// exports cross to client components as RPC stubs (Next semantics — the
|
|
87
|
+
// cached function always runs, and caches, on the server).
|
|
88
|
+
if (!moduleLevelUseServer(source) && !moduleLevelUseCache(source)) continue;
|
|
89
|
+
const exports = moduleActionExports(source);
|
|
90
|
+
if (exports.length === 0) continue;
|
|
91
|
+
|
|
92
|
+
const ids: Record<string, string> = {};
|
|
93
|
+
for (const exportName of exports) ids[exportName] = canonicalActionId(file, exportName, config.root);
|
|
94
|
+
modules.push({
|
|
95
|
+
file,
|
|
96
|
+
exports,
|
|
97
|
+
ids,
|
|
98
|
+
modulePath: options.modulePathFor ? options.modulePathFor(file) : file,
|
|
99
|
+
});
|
|
100
|
+
actionFiles.add(file);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { modules, actionFiles };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Register discovered action modules into the process registry so the endpoint
|
|
108
|
+
* can resolve incoming ids. Returns the flat entry list (id/modulePath/export).
|
|
109
|
+
*/
|
|
110
|
+
export function registerDiscoveredActions(
|
|
111
|
+
config: ResolvedConfig,
|
|
112
|
+
discovery: ActionDiscovery,
|
|
113
|
+
): ActionEntry[] {
|
|
114
|
+
const entries: ActionEntry[] = [];
|
|
115
|
+
const root = canonical(config.root);
|
|
116
|
+
for (const module of discovery.modules) {
|
|
117
|
+
// Canonical (file, root) so registry ids match the client stub / manifest.
|
|
118
|
+
entries.push(
|
|
119
|
+
...registerActionModule(canonical(module.file), module.exports, {
|
|
120
|
+
root,
|
|
121
|
+
modulePath: module.modulePath,
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
return entries;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Server actions live under the app dir and (for monorepos) may be shared from
|
|
129
|
+
// workspace packages, but scanning the whole workspace is too broad. Scan the
|
|
130
|
+
// app dir plus the project src dir if present — the common home for a shared
|
|
131
|
+
// actions module. node_modules and the build output are excluded.
|
|
132
|
+
function actionScanRoots(config: ResolvedConfig) {
|
|
133
|
+
const roots = new Set<string>([config.appPath]);
|
|
134
|
+
if (config.appPath.includes('pnext-pages-compat')) {
|
|
135
|
+
const rootApp = path.join(config.root, 'app');
|
|
136
|
+
if (existsSync(rootApp)) roots.add(rootApp);
|
|
137
|
+
const srcApp = path.join(config.root, 'src', 'app');
|
|
138
|
+
if (existsSync(srcApp)) roots.add(srcApp);
|
|
139
|
+
}
|
|
140
|
+
const srcDir = path.join(config.root, 'src');
|
|
141
|
+
if (existsSync(srcDir)) roots.add(srcDir);
|
|
142
|
+
return [...roots];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function isScriptFile(file: string) {
|
|
146
|
+
if (file.includes(`${path.sep}node_modules${path.sep}`)) return false;
|
|
147
|
+
return /\.(?:tsx?|jsx?|mjs)$/.test(file) && !file.endsWith('.d.ts');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const ACTION_RESOLVE_CONDITIONS = ['react-server', 'node', 'import', 'module', 'default'] as const;
|
|
151
|
+
/**
|
|
152
|
+
* node_modules packages that export module-level 'use server' modules the app imports directly.
|
|
153
|
+
* Reading a file's real imports costs an oxc parse - but an import specifier is always a quoted string
|
|
154
|
+
* literal, so the cheap textual superset below decides which files can possibly reach an action
|
|
155
|
+
* package. Only those are parsed; everything the set returns is confirmed against the parse.
|
|
156
|
+
*/
|
|
157
|
+
function discoverNodeModuleActionFiles(
|
|
158
|
+
config: ResolvedConfig,
|
|
159
|
+
sourceFiles: string[],
|
|
160
|
+
sources: Map<string, string>,
|
|
161
|
+
seen: Set<string>,
|
|
162
|
+
) {
|
|
163
|
+
const actionFiles = new Set<string>();
|
|
164
|
+
// A bare specifier resolves purely from the importer's resolution root, so
|
|
165
|
+
// both the resolution and the 'use server' verdict are cached app-wide, and
|
|
166
|
+
// each is derived only for the specifiers a file actually reaches.
|
|
167
|
+
const targets = new Map<string, string | undefined>();
|
|
168
|
+
const verdicts = new Map<string, boolean>();
|
|
169
|
+
const actionTarget = (resolveRoot: string, file: string, specifier: string) => {
|
|
170
|
+
const packageName = packageNameOfSpecifier(specifier);
|
|
171
|
+
if (!packageName || builtinModules.includes(packageName)) return undefined;
|
|
172
|
+
const key = `${resolveRoot}\0${specifier}`;
|
|
173
|
+
if (!targets.has(key)) {
|
|
174
|
+
targets.set(key, resolvePackageSpecifier(config.root, file, specifier, ACTION_RESOLVE_CONDITIONS));
|
|
175
|
+
}
|
|
176
|
+
const resolved = targets.get(key);
|
|
177
|
+
if (!resolved) return undefined;
|
|
178
|
+
const absolute = path.resolve(resolved);
|
|
179
|
+
if (seen.has(absolute) || !isNodeModuleScriptFile(absolute)) return undefined;
|
|
180
|
+
let verdict = verdicts.get(absolute);
|
|
181
|
+
if (verdict === undefined) {
|
|
182
|
+
// An unreadable candidate is simply not an action module.
|
|
183
|
+
try {
|
|
184
|
+
verdict = moduleLevelUseServer(readFileSync(absolute, 'utf8'));
|
|
185
|
+
} catch {
|
|
186
|
+
verdict = false;
|
|
187
|
+
}
|
|
188
|
+
verdicts.set(absolute, verdict);
|
|
189
|
+
}
|
|
190
|
+
return verdict ? absolute : undefined;
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
for (const file of sourceFiles) {
|
|
194
|
+
const source = sources.get(file) ?? '';
|
|
195
|
+
const resolveRoot = resolutionRootForFile(config.root, file);
|
|
196
|
+
let reachable = false;
|
|
197
|
+
for (const candidate of candidateSpecifiers(source)) {
|
|
198
|
+
if (actionTarget(resolveRoot, file, candidate)) {
|
|
199
|
+
reachable = true;
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// No candidate resolves to an action module, so neither can the file's real
|
|
204
|
+
// imports (a subset) — the parse would only reproduce this answer.
|
|
205
|
+
if (!reachable) continue;
|
|
206
|
+
for (const specifier of importSpecifiers(source, file)) {
|
|
207
|
+
const target = actionTarget(resolveRoot, file, specifier);
|
|
208
|
+
if (target) actionFiles.add(target);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return [...actionFiles];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Every string literal in an import position: `from 'x'`, `import 'x'`,
|
|
215
|
+
// `import('x')`, `require('x')`. Deliberately looser than the grammar (it also
|
|
216
|
+
// matches inside comments and strings) — over-reporting only costs a resolve.
|
|
217
|
+
const importedLiteral =
|
|
218
|
+
/\b(?:from|import|require)\s*\(?\s*(?:\/\*[\s\S]*?\*\/\s*)?(['"])([^'"\n]{1,256})\1/g;
|
|
219
|
+
// npm package names are lowercase `[a-z0-9._-]` with an optional @scope, so a
|
|
220
|
+
// literal with whitespace or a leading ./ can never be a bare specifier.
|
|
221
|
+
const bareSpecifier = /^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*(?:\/[^\s]+)?$/i;
|
|
222
|
+
|
|
223
|
+
/** Every literal in the source that could be an imported bare package specifier. */
|
|
224
|
+
function candidateSpecifiers(source: string) {
|
|
225
|
+
const found = new Set<string>();
|
|
226
|
+
importedLiteral.lastIndex = 0;
|
|
227
|
+
let match: RegExpExecArray | null;
|
|
228
|
+
while ((match = importedLiteral.exec(source))) {
|
|
229
|
+
const value = match[2]!;
|
|
230
|
+
if (bareSpecifier.test(value)) found.add(value);
|
|
231
|
+
}
|
|
232
|
+
return found;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Read each file once (build-scope cached: the walk's reads are free here). */
|
|
236
|
+
function readSources(files: string[]) {
|
|
237
|
+
const sources = new Map<string, string>();
|
|
238
|
+
for (const file of files) sources.set(file, readSourceSync(file));
|
|
239
|
+
return sources;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function isNodeModuleScriptFile(file: string) {
|
|
243
|
+
return /\.(?:tsx?|jsx?|mjs|cjs|cts)$/.test(file) && !file.endsWith('.d.ts');
|
|
244
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-hydration server-action form capture (Next parity).
|
|
3
|
+
*
|
|
4
|
+
* React/Next capture form submissions that happen BEFORE hydration and replay them once the client
|
|
5
|
+
* runtime is up, instead of letting the browser run the native progressive POST (which reloads the
|
|
6
|
+
* document, losing pending client state and streaming the response into a half-parsed page). This
|
|
7
|
+
* inline script mirrors that: it queues the submission (FormData snapshotted at submit time, including
|
|
8
|
+
* the submitter's pair) on the form node and in `window.__pnextEarlySubmits`; the action client
|
|
9
|
+
* runtime replays queued submissions when the form hydrates, or falls back to the native submit if
|
|
10
|
+
* hydration never claims it.
|
|
11
|
+
*
|
|
12
|
+
* Emitted for every next-compat document after the body content, so the forms it guards are parsed.
|
|
13
|
+
*/
|
|
14
|
+
export function earlySubmitCaptureScript(): string {
|
|
15
|
+
// Keep in sync with compat/actions/protocol.ts field names.
|
|
16
|
+
return (
|
|
17
|
+
'(function(){var h=function(e){' +
|
|
18
|
+
"var f=e.target;if(!f||f.nodeName!=='FORM')return;" +
|
|
19
|
+
'if(f.__pnextNativeReplay){f.__pnextNativeReplay=0;return;}' +
|
|
20
|
+
'if(e.defaultPrevented||window.__PNEXT_ACTIONS__)return;' +
|
|
21
|
+
'var s=e.submitter||null;' +
|
|
22
|
+
'var m=f.querySelector(\'input[name="$pnext_action_id"],input[name="$pnext_form_state"]\');' +
|
|
23
|
+
"if(!m&&!(s&&s.getAttribute('name')==='$pnext_submit_action_id'))return;" +
|
|
24
|
+
'e.preventDefault();' +
|
|
25
|
+
'var d;try{d=new FormData(f,s||undefined)}catch(_){d=new FormData(f)}' +
|
|
26
|
+
'f.__pnextQueuedSubmit={data:d,submitter:s};' +
|
|
27
|
+
'(window.__pnextEarlySubmits=window.__pnextEarlySubmits||[]).push(f);' +
|
|
28
|
+
// A page whose route emits no client runtime never drains the queue —
|
|
29
|
+
// fall back to the browser's native progressive POST (the whole point of
|
|
30
|
+
// progressive enhancement) once hydration has clearly not claimed it.
|
|
31
|
+
'setTimeout(function(){' +
|
|
32
|
+
'if(window.__PNEXT_ACTIONS__||!f.__pnextQueuedSubmit)return;' +
|
|
33
|
+
'f.__pnextQueuedSubmit=undefined;f.__pnextNativeReplay=1;' +
|
|
34
|
+
'try{s?f.requestSubmit(s):f.requestSubmit()}catch(_){f.submit()}' +
|
|
35
|
+
'},3000);' +
|
|
36
|
+
'};document.addEventListener("submit",h,false);' +
|
|
37
|
+
'window.__pnextRemoveEarlySubmit=function(){document.removeEventListener("submit",h,false)};' +
|
|
38
|
+
'})();'
|
|
39
|
+
);
|
|
40
|
+
}
|