@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,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module warm-up for the vercel adapter - run in its own process.
|
|
3
|
+
*
|
|
4
|
+
* Request time on Vercel is read-only, so everything a production start would compile or vendor
|
|
5
|
+
* lazily has to be on disk already: this step compiles every module the production server can need
|
|
6
|
+
* into the build cache and imports the app's route handlers once so their on-demand vendor bundles
|
|
7
|
+
* get written now.
|
|
8
|
+
*
|
|
9
|
+
* Importing handlers runs the app's own server code, which can crash the runtime rather than throw,
|
|
10
|
+
* so the whole pass runs in a child that writes into the same on-disk build cache the parent reads
|
|
11
|
+
* back. Because that cache is on disk, a crash costs one file and not the pass: the child reports
|
|
12
|
+
* each file as it lands, and the parent restarts it with those files skipped until an attempt stops
|
|
13
|
+
* making progress.
|
|
14
|
+
*
|
|
15
|
+
* Ordering is load-bearing, not incidental. Compiles come first and handlers last: handler code
|
|
16
|
+
* running *concurrently* with the compile pass takes the runtime down, while the same compiles with
|
|
17
|
+
* handlers skipped, and the same handlers against a populated cache, both run clean.
|
|
18
|
+
*
|
|
19
|
+
* The child starts when the build starts, so its boot overlaps the build. It then compiles the page
|
|
20
|
+
* routes' server modules as soon as the route scan hands them over, and blocks on stdin for the
|
|
21
|
+
* go-ahead to finish the rest once the manifest is written.
|
|
22
|
+
*/
|
|
23
|
+
import { existsSync } from 'node:fs';
|
|
24
|
+
import { readFile } from 'node:fs/promises';
|
|
25
|
+
import path from 'node:path';
|
|
26
|
+
import { fileURLToPath } from 'node:url';
|
|
27
|
+
import { loadConfig, pathToFileHref, type ResolvedConfig } from '../../config';
|
|
28
|
+
import { bootstrapCompat } from '../../compat-bootstrap';
|
|
29
|
+
import { ssrClientReference } from '../../client/reference';
|
|
30
|
+
import {
|
|
31
|
+
devClientModuleHref,
|
|
32
|
+
devServerModuleHref,
|
|
33
|
+
setEmitCompiledSpecifiersManifest,
|
|
34
|
+
} from '../../dev/imports';
|
|
35
|
+
import { getCompatModeExtensions } from '../../extensions';
|
|
36
|
+
import { findConventionFiles, findLayouts } from '../../routing/routes';
|
|
37
|
+
import { registerServerRuntime } from '../../runtime/server';
|
|
38
|
+
import { namedBunBinary } from '../named-bin';
|
|
39
|
+
import type { VerboseLogger } from '../../utils/verbose';
|
|
40
|
+
import type { BuildManifest } from '../../types';
|
|
41
|
+
|
|
42
|
+
// The child reports one line per file it finishes — `<source>` for an imported
|
|
43
|
+
// route handler, `<source>\t<compiled>` for a compiled module. Everything else
|
|
44
|
+
// it prints is app output the parent passes through.
|
|
45
|
+
const DONE_MARKER = 'pnext-warm-done:';
|
|
46
|
+
|
|
47
|
+
// Printed once compiles finish and before the handler loop starts: the
|
|
48
|
+
// compiled artifact set is already final at that boundary, so the parent can
|
|
49
|
+
// start tracing the node_modules closure while this process runs handlers.
|
|
50
|
+
const COMPILED_MARKER = 'pnext-warm-compiled';
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* What the parent sends over stdin, one JSON object per line. A `prewarm`
|
|
54
|
+
* message names page sources the child may start compiling right away; the
|
|
55
|
+
* `skip` message is the go-ahead for the full pass and always comes last.
|
|
56
|
+
*/
|
|
57
|
+
interface WarmRequest {
|
|
58
|
+
/** Files a previous attempt already finished — or died on. */
|
|
59
|
+
skip: string[];
|
|
60
|
+
}
|
|
61
|
+
interface PrewarmRequest {
|
|
62
|
+
/** Page route sources, known from the route scan long before the manifest. */
|
|
63
|
+
prewarm: string[];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// An attempt still running after this long is not going to finish: something in
|
|
67
|
+
// the app's own code is holding the process. Bounded so the build completes
|
|
68
|
+
// instead of hanging forever.
|
|
69
|
+
const ATTEMPT_TIMEOUT_MS = 10 * 60_000;
|
|
70
|
+
|
|
71
|
+
// One clean pass, plus enough restarts to step over a couple of poisoned files
|
|
72
|
+
// before concluding the app cannot be warmed.
|
|
73
|
+
const MAX_ATTEMPTS = 3;
|
|
74
|
+
|
|
75
|
+
export interface WarmChild {
|
|
76
|
+
/**
|
|
77
|
+
* Hand the child the page sources from the route scan so it compiles them
|
|
78
|
+
* under the rest of the build. Optional and fire-and-forget: whatever it does
|
|
79
|
+
* not get to, `finish` compiles.
|
|
80
|
+
*/
|
|
81
|
+
prewarm(files: string[]): void;
|
|
82
|
+
/**
|
|
83
|
+
* Release the child to warm, and collect the module paths it compiled. `onCompiled`, if given,
|
|
84
|
+
* fires once - as soon as the child reaches its handler phase - with the modules compiled so far,
|
|
85
|
+
* which is already the final set: the caller can start tracing the node_modules closure while the
|
|
86
|
+
* child runs handlers concurrently.
|
|
87
|
+
*/
|
|
88
|
+
finish(log: VerboseLogger, onCompiled?: (modules: string[]) => void): Promise<string[]>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
type WarmProcess = Bun.Subprocess<'pipe', 'pipe', 'inherit'>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Spawn the warm child. Call as early in the build as possible: it boots while the build runs and
|
|
95
|
+
* does no work until `finish`, which must be called only after the build manifest is on disk. Never
|
|
96
|
+
* throws - a warm-up that cannot start degrades to "no warmed modules", which `finish` reports.
|
|
97
|
+
*/
|
|
98
|
+
export function startWarmChild(config: ResolvedConfig): WarmChild {
|
|
99
|
+
const spawn = () => spawnWarmProcess(config);
|
|
100
|
+
const first = spawn();
|
|
101
|
+
return {
|
|
102
|
+
prewarm(files) {
|
|
103
|
+
if (!first || files.length === 0) return;
|
|
104
|
+
try {
|
|
105
|
+
void first.stdin.write(`${JSON.stringify({ prewarm: files } satisfies PrewarmRequest)}\n`);
|
|
106
|
+
void first.stdin.flush();
|
|
107
|
+
} catch {
|
|
108
|
+
// broken pipe: the child is gone; `finish` reports why
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
finish: (log, onCompiled) => warmWithRestarts(first, spawn, log, onCompiled),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function spawnWarmProcess(config: ResolvedConfig): WarmProcess | undefined {
|
|
116
|
+
const entry = fileURLToPath(new URL(import.meta.url));
|
|
117
|
+
try {
|
|
118
|
+
return Bun.spawn(
|
|
119
|
+
[namedBunBinary('pnext-warm'), '--conditions=react-server', entry, config.root],
|
|
120
|
+
{ cwd: config.root, stdin: 'pipe', stdout: 'pipe', stderr: 'inherit', env: process.env },
|
|
121
|
+
);
|
|
122
|
+
} catch {
|
|
123
|
+
return undefined; // no child (fork limit, unsupported platform)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function warmWithRestarts(
|
|
128
|
+
first: WarmProcess | undefined,
|
|
129
|
+
spawn: () => WarmProcess | undefined,
|
|
130
|
+
log: VerboseLogger,
|
|
131
|
+
onCompiled?: (modules: string[]) => void,
|
|
132
|
+
) {
|
|
133
|
+
// Source file -> compiled artifact. Handlers map to nothing but still count as
|
|
134
|
+
// finished, so a restart does not import them a second time.
|
|
135
|
+
const finished = new Map<string, string | undefined>();
|
|
136
|
+
const compiled = () => [...finished.values()].filter((file): file is string => Boolean(file));
|
|
137
|
+
// The marker can only mean "compiled set final" the first time it fires —
|
|
138
|
+
// whichever attempt reaches its own handler phase first.
|
|
139
|
+
let fired = false;
|
|
140
|
+
const fireOnCompiled = onCompiled
|
|
141
|
+
? () => {
|
|
142
|
+
if (fired) return;
|
|
143
|
+
fired = true;
|
|
144
|
+
onCompiled(compiled());
|
|
145
|
+
}
|
|
146
|
+
: undefined;
|
|
147
|
+
let child = first;
|
|
148
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS && child; attempt++) {
|
|
149
|
+
const before = finished.size;
|
|
150
|
+
const failure = await runAttempt(child, { skip: [...finished.keys()] }, finished, fireOnCompiled);
|
|
151
|
+
if (!failure) {
|
|
152
|
+
log.log(`warmed ${compiled().length} modules`);
|
|
153
|
+
return compiled();
|
|
154
|
+
}
|
|
155
|
+
// A restart only helps if the last one got somewhere; otherwise the first
|
|
156
|
+
// thing it would redo is what killed it.
|
|
157
|
+
child = finished.size > before && attempt < MAX_ATTEMPTS ? spawn() : undefined;
|
|
158
|
+
if (child) log.log(`warm-up ${failure} after ${finished.size} files; restarting past them`);
|
|
159
|
+
else warmIncomplete(failure, finished.size);
|
|
160
|
+
}
|
|
161
|
+
fireOnCompiled?.();
|
|
162
|
+
return compiled();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Runs one attempt to completion. Returns undefined on success, else why it died. */
|
|
166
|
+
async function runAttempt(
|
|
167
|
+
child: WarmProcess,
|
|
168
|
+
request: WarmRequest,
|
|
169
|
+
finished: Map<string, string | undefined>,
|
|
170
|
+
onCompiled?: () => void,
|
|
171
|
+
) {
|
|
172
|
+
try {
|
|
173
|
+
// Writing the request and closing stdin is the go-ahead; until then the
|
|
174
|
+
// child sits parked after its boot (or busy on a prewarm batch).
|
|
175
|
+
void child.stdin.write(`${JSON.stringify(request)}\n`);
|
|
176
|
+
void child.stdin.end();
|
|
177
|
+
} catch {
|
|
178
|
+
// broken pipe: the child is already gone, and its exit below says why
|
|
179
|
+
}
|
|
180
|
+
const timeout = setTimeout(() => child.kill('SIGKILL'), ATTEMPT_TIMEOUT_MS);
|
|
181
|
+
timeout.unref?.();
|
|
182
|
+
|
|
183
|
+
// Streamed rather than buffered to completion: the compiled-phase marker
|
|
184
|
+
// has to reach the caller while this attempt is still running (the child's
|
|
185
|
+
// handler phase), not after the process exits.
|
|
186
|
+
const processLine = (line: string) => {
|
|
187
|
+
if (!line) return;
|
|
188
|
+
if (line === COMPILED_MARKER) {
|
|
189
|
+
onCompiled?.();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (!line.startsWith(DONE_MARKER)) {
|
|
193
|
+
console.log(line); // app output
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const [source, artifact] = line.slice(DONE_MARKER.length).split('\t');
|
|
197
|
+
if (source) finished.set(source, artifact || undefined);
|
|
198
|
+
};
|
|
199
|
+
const reader = child.stdout.getReader();
|
|
200
|
+
const decoder = new TextDecoder();
|
|
201
|
+
let buffer = '';
|
|
202
|
+
for (;;) {
|
|
203
|
+
const { done, value } = await reader.read();
|
|
204
|
+
if (done) break;
|
|
205
|
+
buffer += decoder.decode(value, { stream: true });
|
|
206
|
+
let newline: number;
|
|
207
|
+
while ((newline = buffer.indexOf('\n')) >= 0) {
|
|
208
|
+
processLine(buffer.slice(0, newline));
|
|
209
|
+
buffer = buffer.slice(newline + 1);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (buffer) processLine(buffer);
|
|
213
|
+
|
|
214
|
+
const code = await child.exited.finally(() => clearTimeout(timeout));
|
|
215
|
+
if (code === 0) return undefined;
|
|
216
|
+
return child.signalCode ? `was killed by ${child.signalCode}` : `exited with code ${code}`;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function warmIncomplete(failure: string, done: number) {
|
|
220
|
+
console.warn(
|
|
221
|
+
`vercel adapter: module warm-up ${failure} after warming ${done} file(s). The function ships ` +
|
|
222
|
+
'without the rest of its module cache and may fail to render those routes at request time, ' +
|
|
223
|
+
"where Vercel's filesystem is read-only.",
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const report = (file: string, artifact?: string) => {
|
|
228
|
+
console.log(`${DONE_MARKER}${file}${artifact ? `\t${artifact}` : ''}`);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/** Every server module a page route pulls in: the page, its layouts, its conventions. */
|
|
232
|
+
function pageServerFiles(config: ResolvedConfig, routeFiles: Iterable<string>) {
|
|
233
|
+
const files = new Set<string>();
|
|
234
|
+
const globalNotFound = path.join(config.appPath, 'not-found.tsx');
|
|
235
|
+
if (existsSync(globalNotFound)) files.add(globalNotFound);
|
|
236
|
+
for (const routeFile of routeFiles) {
|
|
237
|
+
for (const file of [
|
|
238
|
+
routeFile,
|
|
239
|
+
...findLayouts(config.appPath, routeFile),
|
|
240
|
+
...['loading.tsx', 'error.tsx', 'not-found.tsx'].flatMap(name =>
|
|
241
|
+
findConventionFiles(config.appPath, routeFile, name),
|
|
242
|
+
),
|
|
243
|
+
]) {
|
|
244
|
+
if (existsSync(file)) files.add(file);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return files;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const compileServer = (config: ResolvedConfig, file: string) =>
|
|
251
|
+
devServerModuleHref(config, file, 'build').then(href => report(file, fileURLToPath(href)));
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Compile the page routes' server modules from the build's route scan, long before the manifest
|
|
255
|
+
* exists. Artifacts are content-keyed on disk, so whatever lands here is a straight cache hit for the
|
|
256
|
+
* pass below - the point is to spend this CPU under the build's client stage. Nothing here runs app
|
|
257
|
+
* code, so it is safe to overlap (see the handler note below).
|
|
258
|
+
*/
|
|
259
|
+
export async function prewarmPageModules(config: ResolvedConfig, routeFiles: string[]) {
|
|
260
|
+
if (!getCompatModeExtensions().reactEnabled(config)) return;
|
|
261
|
+
await Promise.all(
|
|
262
|
+
[...pageServerFiles(config, routeFiles)].map(file =>
|
|
263
|
+
compileServer(config, file).catch(() => undefined),
|
|
264
|
+
),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* The warm pass itself, in the child. Reports every file it finishes as it
|
|
270
|
+
* lands, so a parent restart can step over one that takes the process down.
|
|
271
|
+
*/
|
|
272
|
+
export async function warmRouteModules(
|
|
273
|
+
config: ResolvedConfig,
|
|
274
|
+
manifest: BuildManifest,
|
|
275
|
+
skip: ReadonlySet<string> = new Set(),
|
|
276
|
+
) {
|
|
277
|
+
const clientFiles = new Set<string>();
|
|
278
|
+
const handlers: BuildManifest['routes'] = [];
|
|
279
|
+
const pageRoutes: string[] = [];
|
|
280
|
+
|
|
281
|
+
for (const route of manifest.routes) {
|
|
282
|
+
if (route.kind === 'handler') {
|
|
283
|
+
if (!skip.has(route.file)) handlers.push(route);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
pageRoutes.push(route.file);
|
|
287
|
+
for (const reference of route.clientReferences) {
|
|
288
|
+
if (ssrClientReference(reference) && existsSync(reference.file)) {
|
|
289
|
+
clientFiles.add(reference.file);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Without react compat the runtime imports page sources directly (Bun
|
|
295
|
+
// transforms in memory); the compiled build cache is only used with compat.
|
|
296
|
+
if (getCompatModeExtensions().reactEnabled(config)) {
|
|
297
|
+
await Promise.all([
|
|
298
|
+
...[...pageServerFiles(config, pageRoutes)]
|
|
299
|
+
.filter(file => !skip.has(file))
|
|
300
|
+
.map(file => compileServer(config, file)),
|
|
301
|
+
...[...clientFiles]
|
|
302
|
+
.filter(file => !skip.has(file))
|
|
303
|
+
.map(file =>
|
|
304
|
+
devClientModuleHref(config, file, 'build').then(href =>
|
|
305
|
+
report(file, fileURLToPath(href)),
|
|
306
|
+
),
|
|
307
|
+
),
|
|
308
|
+
]);
|
|
309
|
+
}
|
|
310
|
+
console.log(COMPILED_MARKER);
|
|
311
|
+
|
|
312
|
+
// Handlers come last, and only once nothing else is in flight. Importing them runs the app's own
|
|
313
|
+
// server code (auth, db pools, telemetry), and that code running *concurrently* with the compile
|
|
314
|
+
// pass is what takes the runtime down - the same pass with handlers skipped never crashes, and
|
|
315
|
+
// handlers alone against a populated cache never crash. Doing them after the compiles also means a
|
|
316
|
+
// crash here costs only the remaining handlers, not the whole module pass.
|
|
317
|
+
for (const route of handlers) {
|
|
318
|
+
registerServerRuntime(config, route.sourceFiles);
|
|
319
|
+
// Reported before the import, not after: handlers are imported one at a
|
|
320
|
+
// time, so a handler that takes the process down is unambiguous and a
|
|
321
|
+
// restart must not run it again (Bun caches a failed import for the
|
|
322
|
+
// process' lifetime anyway, so a retry could not succeed either).
|
|
323
|
+
report(route.file);
|
|
324
|
+
try {
|
|
325
|
+
await import(pathToFileHref(route.file));
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.warn(`vercel adapter: importing ${route.file} failed during warmup:`, error);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Read the parent's line-delimited requests until the `skip` message, running
|
|
334
|
+
* each prewarm batch as it arrives. Returns the final request.
|
|
335
|
+
*/
|
|
336
|
+
async function readWarmRequests(config: ResolvedConfig): Promise<Partial<WarmRequest>> {
|
|
337
|
+
let buffer = '';
|
|
338
|
+
const decoder = new TextDecoder();
|
|
339
|
+
const reader = Bun.stdin.stream().getReader();
|
|
340
|
+
for (;;) {
|
|
341
|
+
const { done, value } = await reader.read();
|
|
342
|
+
if (done) break;
|
|
343
|
+
buffer += decoder.decode(value, { stream: true });
|
|
344
|
+
let newline: number;
|
|
345
|
+
while ((newline = buffer.indexOf('\n')) >= 0) {
|
|
346
|
+
const line = buffer.slice(0, newline).trim();
|
|
347
|
+
buffer = buffer.slice(newline + 1);
|
|
348
|
+
if (!line) continue;
|
|
349
|
+
const message = JSON.parse(line) as Partial<WarmRequest & PrewarmRequest>;
|
|
350
|
+
if (message.skip) return message;
|
|
351
|
+
if (message.prewarm) await prewarmPageModules(config, message.prewarm);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const rest = buffer.trim();
|
|
355
|
+
return rest ? (JSON.parse(rest) as Partial<WarmRequest>) : {};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (import.meta.main) {
|
|
359
|
+
const root = process.argv[2];
|
|
360
|
+
const config = await loadConfig(root);
|
|
361
|
+
await bootstrapCompat(config);
|
|
362
|
+
// This process only ever runs for a vercel-adapter build; record each
|
|
363
|
+
// artifact's specifiers so the parent's trace step can read them back.
|
|
364
|
+
setEmitCompiledSpecifiersManifest(true);
|
|
365
|
+
// Blocks until the parent sends the go-ahead, i.e. until the manifest is
|
|
366
|
+
// written; a prewarm batch may arrive and run before that.
|
|
367
|
+
const request = await readWarmRequests(config);
|
|
368
|
+
const manifest = JSON.parse(
|
|
369
|
+
await readFile(path.join(config.outPath, 'manifest.json'), 'utf8'),
|
|
370
|
+
) as BuildManifest;
|
|
371
|
+
await warmRouteModules(config, manifest, new Set(request.skip ?? []));
|
|
372
|
+
// App code reached through a route handler can leave the loop alive (a db
|
|
373
|
+
// pool, a stray interval); the warm pass is done either way.
|
|
374
|
+
process.exit(0);
|
|
375
|
+
}
|