@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,263 @@
|
|
|
1
|
+
// testmode / experimental.testProxy (COMPAT).
|
|
2
|
+
//
|
|
3
|
+
// When a request carries `Next-Test-Proxy-Port` + `Next-Test-Data` headers, all outgoing fetch during
|
|
4
|
+
// that request is proxied to the named localhost port following Next's testmode wire protocol. A
|
|
5
|
+
// request interceptor stores the proxy info for the duration of the request; global.fetch is patched
|
|
6
|
+
// exactly once at init and consults it - a no-op when no proxy info is active. Gated on
|
|
7
|
+
// experimental.testProxy in next.config.
|
|
8
|
+
|
|
9
|
+
import { PassThrough } from 'node:stream';
|
|
10
|
+
import type * as http from 'node:http';
|
|
11
|
+
import type * as https from 'node:https';
|
|
12
|
+
import type { IncomingMessage } from 'node:http';
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
|
+
import { currentRequest, getWorkUnit } from '../../request/context';
|
|
15
|
+
|
|
16
|
+
// `import * as http` yields a frozen ES module namespace object under strict
|
|
17
|
+
// runtimes (Bun); assigning to `.get` on it throws "Attempted to assign to
|
|
18
|
+
// readonly property." `require()` returns the real, mutable CJS exports
|
|
19
|
+
// object that node/http/https resolve `.get` calls through at runtime, so
|
|
20
|
+
// patch that instead of the namespace binding.
|
|
21
|
+
const requireNode = createRequire(import.meta.url);
|
|
22
|
+
const httpModule = requireNode('node:http') as typeof http;
|
|
23
|
+
const httpsModule = requireNode('node:https') as typeof https;
|
|
24
|
+
|
|
25
|
+
interface TestProxyInfo {
|
|
26
|
+
proxyPort: number;
|
|
27
|
+
testData: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The proxy info rides on the core work unit (which spans the whole request lifecycle) rather than a
|
|
32
|
+
* private ALS, so the patched global.fetch - invoked deep inside a render or handler - can recover it
|
|
33
|
+
* via getWorkUnit(). The interceptor sets it before matching; the fetch patch reads it.
|
|
34
|
+
*/
|
|
35
|
+
const TEST_PROXY_KEY = Symbol.for('pnext.testProxy');
|
|
36
|
+
|
|
37
|
+
/** Extract proxy info from a request's Next-Test-* headers, if present. */
|
|
38
|
+
export function readTestProxyInfo(headers: Headers): TestProxyInfo | undefined {
|
|
39
|
+
const portHeader = headers.get('next-test-proxy-port');
|
|
40
|
+
if (!portHeader) return undefined;
|
|
41
|
+
return { proxyPort: Number(portHeader), testData: headers.get('next-test-data') ?? '' };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Record the request's test-proxy info onto the active work unit (if any). */
|
|
45
|
+
export function activateTestProxy(headers: Headers): void {
|
|
46
|
+
const info = readTestProxyInfo(headers);
|
|
47
|
+
if (!info) return;
|
|
48
|
+
const unit = getWorkUnit();
|
|
49
|
+
if (!unit) return;
|
|
50
|
+
(unit.compat ??= {})[TEST_PROXY_KEY] = info;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function currentTestProxyInfo(): TestProxyInfo | undefined {
|
|
54
|
+
const workUnitInfo = getWorkUnit()?.compat?.[TEST_PROXY_KEY] as TestProxyInfo | undefined;
|
|
55
|
+
if (workUnitInfo) return workUnitInfo;
|
|
56
|
+
const request = currentRequest();
|
|
57
|
+
if (!request) return undefined;
|
|
58
|
+
return readTestProxyInfo(request.headers);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// pnext marks its own proxy round-trips via `next.internal` so the patched
|
|
62
|
+
// fetch (below) doesn't re-intercept them. The `next` field is typed as
|
|
63
|
+
// NextFetchRequestConfig globally, so extend it via intersection rather than
|
|
64
|
+
// interface-extends (which errors on the conflicting `next` property).
|
|
65
|
+
type InternalInit = RequestInit & { next?: { internal?: boolean } };
|
|
66
|
+
|
|
67
|
+
interface ProxyFetchRequest {
|
|
68
|
+
testData: string;
|
|
69
|
+
api: 'fetch';
|
|
70
|
+
request: {
|
|
71
|
+
url: string;
|
|
72
|
+
method: string;
|
|
73
|
+
headers: [string, string][];
|
|
74
|
+
body: string | null;
|
|
75
|
+
cache: RequestCache;
|
|
76
|
+
credentials: RequestCredentials;
|
|
77
|
+
integrity: string;
|
|
78
|
+
mode: RequestMode;
|
|
79
|
+
redirect: RequestRedirect;
|
|
80
|
+
referrer: string;
|
|
81
|
+
referrerPolicy: ReferrerPolicy;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function buildProxyRequest(testData: string, request: Request): Promise<ProxyFetchRequest> {
|
|
86
|
+
return {
|
|
87
|
+
testData,
|
|
88
|
+
api: 'fetch',
|
|
89
|
+
request: {
|
|
90
|
+
url: request.url,
|
|
91
|
+
method: request.method,
|
|
92
|
+
headers: [
|
|
93
|
+
...Array.from(request.headers),
|
|
94
|
+
['next-test-stack', getTestStack()],
|
|
95
|
+
],
|
|
96
|
+
body: request.body ? Buffer.from(await request.arrayBuffer()).toString('base64') : null,
|
|
97
|
+
cache: request.cache,
|
|
98
|
+
credentials: request.credentials,
|
|
99
|
+
integrity: request.integrity,
|
|
100
|
+
// Bun defaults a Request built from a URL string to mode 'navigate'
|
|
101
|
+
// (undici/browsers use 'cors'). 'navigate' cannot be reconstructed via
|
|
102
|
+
// the Request constructor, so the test proxy's buildRequest() would throw
|
|
103
|
+
// "invalid request mode navigate". Normalize it to the browser default so
|
|
104
|
+
// the proxy can rebuild the forwarded request.
|
|
105
|
+
mode: request.mode === 'navigate' ? 'cors' : request.mode,
|
|
106
|
+
redirect: request.redirect,
|
|
107
|
+
referrer: request.referrer,
|
|
108
|
+
referrerPolicy: request.referrerPolicy,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function getTestStack(): string {
|
|
114
|
+
let stack = (new Error().stack ?? '').split('\n');
|
|
115
|
+
for (let i = 1; i < stack.length; i++) {
|
|
116
|
+
const line = stack[i];
|
|
117
|
+
if (line && line.length > 0) {
|
|
118
|
+
stack = stack.slice(i);
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
stack = stack.filter(item => !item.includes('/next/dist/'));
|
|
123
|
+
stack = stack.slice(0, 5);
|
|
124
|
+
stack = stack.map(line => line.replace('webpack-internal:///(rsc)/', '').trim());
|
|
125
|
+
return stack.join(' ');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
interface ProxyResponse {
|
|
129
|
+
api: 'continue' | 'abort' | 'unhandled' | 'fetch';
|
|
130
|
+
response?: { status: number; headers: [string, string][]; body: string | null };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function handleProxiedFetch(
|
|
134
|
+
originalFetch: typeof fetch,
|
|
135
|
+
info: TestProxyInfo,
|
|
136
|
+
request: Request,
|
|
137
|
+
): Promise<Response> {
|
|
138
|
+
const proxyRequest = await buildProxyRequest(info.testData, request);
|
|
139
|
+
const resp = await originalFetch(`http://localhost:${info.proxyPort}`, {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
body: JSON.stringify(proxyRequest),
|
|
142
|
+
next: { internal: true },
|
|
143
|
+
} as InternalInit);
|
|
144
|
+
if (!resp.ok) throw new Error(`Proxy request failed: ${resp.status}`);
|
|
145
|
+
const proxyResponse = (await resp.json()) as ProxyResponse;
|
|
146
|
+
switch (proxyResponse.api) {
|
|
147
|
+
case 'continue':
|
|
148
|
+
return originalFetch(request);
|
|
149
|
+
case 'fetch': {
|
|
150
|
+
const r = proxyResponse.response!;
|
|
151
|
+
return new Response(r.body ? Buffer.from(r.body, 'base64') : null, {
|
|
152
|
+
status: r.status,
|
|
153
|
+
headers: new Headers(r.headers),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
default:
|
|
157
|
+
throw new Error(`Proxy request aborted [${request.method} ${request.url}]`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
type TestGetCallback = (res: IncomingMessage) => void;
|
|
162
|
+
type HttpGetInput = Parameters<typeof http.get>[0];
|
|
163
|
+
|
|
164
|
+
function buildResponseMessage(responsePromise: Promise<Response>, callback?: TestGetCallback): IncomingMessage {
|
|
165
|
+
const stream = new PassThrough();
|
|
166
|
+
const message = stream as unknown as IncomingMessage;
|
|
167
|
+
void responsePromise.then(async response => {
|
|
168
|
+
const headers: Record<string, string> = {};
|
|
169
|
+
const rawHeaders: string[] = [];
|
|
170
|
+
for (const [name, value] of response.headers) {
|
|
171
|
+
headers[name.toLowerCase()] = value;
|
|
172
|
+
rawHeaders.push(name, value);
|
|
173
|
+
}
|
|
174
|
+
message.statusCode = response.status;
|
|
175
|
+
message.statusMessage = response.statusText;
|
|
176
|
+
message.headers = headers;
|
|
177
|
+
message.rawHeaders = rawHeaders;
|
|
178
|
+
message.complete = true;
|
|
179
|
+
callback?.(message);
|
|
180
|
+
const body = await response.arrayBuffer();
|
|
181
|
+
stream.end(Buffer.from(body));
|
|
182
|
+
}).catch(error => {
|
|
183
|
+
stream.emit('error', error);
|
|
184
|
+
stream.end();
|
|
185
|
+
});
|
|
186
|
+
return message;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function getStringForGetInput(input: HttpGetInput): string | undefined {
|
|
190
|
+
if (typeof input === 'string' || input instanceof URL) return input.toString();
|
|
191
|
+
const candidate = input as { href?: string; protocol?: string; hostname?: string; host?: string; path?: string; pathname?: string; search?: string; port?: number | string };
|
|
192
|
+
if (typeof candidate.href === 'string' && /^[a-z][a-z\d+.-]*:\/\//i.test(candidate.href)) return candidate.href;
|
|
193
|
+
const protocol = typeof candidate.protocol === 'string' ? candidate.protocol : 'http:';
|
|
194
|
+
const host = typeof candidate.host === 'string' ? candidate.host : typeof candidate.hostname === 'string' ? candidate.hostname : undefined;
|
|
195
|
+
if (!host) return undefined;
|
|
196
|
+
const port =
|
|
197
|
+
typeof candidate.port === 'number' ? `:${candidate.port}` : typeof candidate.port === 'string' ? `:${candidate.port}` : '';
|
|
198
|
+
const path = typeof candidate.path === 'string' ? candidate.path : typeof candidate.pathname === 'string' ? candidate.pathname : '/';
|
|
199
|
+
const search = typeof candidate.search === 'string' ? candidate.search : '';
|
|
200
|
+
return `${protocol}//${host}${port}${path}${search}`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function patchableGet(
|
|
204
|
+
originalGet: typeof http.get,
|
|
205
|
+
originalFetch: typeof fetch,
|
|
206
|
+
): typeof http.get {
|
|
207
|
+
return ((input: HttpGetInput, options?: Parameters<typeof http.get>[1]) => {
|
|
208
|
+
const callback = typeof options === 'function' ? options : undefined;
|
|
209
|
+
const url = getStringForGetInput(input);
|
|
210
|
+
const info = currentTestProxyInfo();
|
|
211
|
+
if (!url || !info) {
|
|
212
|
+
if (typeof options === 'function') {
|
|
213
|
+
return originalGet(input, options);
|
|
214
|
+
}
|
|
215
|
+
if (options === undefined) {
|
|
216
|
+
return originalGet(input);
|
|
217
|
+
}
|
|
218
|
+
return originalGet(input, options);
|
|
219
|
+
}
|
|
220
|
+
const request = new Request(url, {
|
|
221
|
+
method: typeof options === 'object' && options && 'method' in options && typeof options.method === 'string'
|
|
222
|
+
? options.method
|
|
223
|
+
: 'GET',
|
|
224
|
+
headers:
|
|
225
|
+
typeof options === 'object' && options && 'headers' in options
|
|
226
|
+
? (options.headers as HeadersInit)
|
|
227
|
+
: undefined,
|
|
228
|
+
});
|
|
229
|
+
return buildResponseMessage(handleProxiedFetch(originalFetch, info, request), callback) as unknown as ReturnType<
|
|
230
|
+
typeof http.get
|
|
231
|
+
>;
|
|
232
|
+
}) as typeof http.get;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let patched = false;
|
|
236
|
+
|
|
237
|
+
/** Patch global.fetch and node http/https.get once for testmode outgoing requests. */
|
|
238
|
+
export function installTestProxyFetch(): void {
|
|
239
|
+
if (patched) return;
|
|
240
|
+
patched = true;
|
|
241
|
+
const originalFetch = globalThis.fetch.bind(globalThis);
|
|
242
|
+
|
|
243
|
+
const testFetch = function testFetch(
|
|
244
|
+
input: Parameters<typeof fetch>[0],
|
|
245
|
+
init?: Parameters<typeof fetch>[1],
|
|
246
|
+
): Promise<Response> {
|
|
247
|
+
const nextInit = (init as { next?: { internal?: boolean } } | undefined)?.next;
|
|
248
|
+
if (nextInit?.internal) {
|
|
249
|
+
return originalFetch(input, init);
|
|
250
|
+
}
|
|
251
|
+
const info = currentTestProxyInfo();
|
|
252
|
+
if (!info) return originalFetch(input, init);
|
|
253
|
+
return handleProxiedFetch(originalFetch, info, new Request(input, init));
|
|
254
|
+
};
|
|
255
|
+
// Preserve fetch.preconnect (typeof fetch requires it).
|
|
256
|
+
(testFetch as typeof fetch).preconnect = originalFetch.preconnect?.bind(originalFetch);
|
|
257
|
+
globalThis.fetch = testFetch as typeof fetch;
|
|
258
|
+
|
|
259
|
+
const originalHttpGet = httpModule.get;
|
|
260
|
+
const originalHttpsGet = httpsModule.get;
|
|
261
|
+
httpModule.get = patchableGet(originalHttpGet, originalFetch);
|
|
262
|
+
httpsModule.get = patchableGet(originalHttpsGet, originalFetch);
|
|
263
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// MDX compile core (COMPAT).
|
|
2
|
+
//
|
|
3
|
+
// Thin wrapper over `@mdx-js/mdx`'s `compile`. Kept dependency-light and lazy: `@mdx-js/mdx` is an
|
|
4
|
+
// optionalDependency loaded on first `.mdx` compile, so a pure app that never touches MDX pays nothing
|
|
5
|
+
// and builds without it installed.
|
|
6
|
+
//
|
|
7
|
+
// The user's remark/rehype/recma plugin arrays come from the wrapped next.config, read via
|
|
8
|
+
// `getNextConfig()` so no wiring is required beyond registering the loader.
|
|
9
|
+
//
|
|
10
|
+
// Output is JSX targeting the preact automatic runtime; the esbuild loader that calls this returns
|
|
11
|
+
// `loader: 'jsx'` so esbuild performs the final JSX-to-calls transform with the same jsxImportSource
|
|
12
|
+
// every other compile site uses, keeping a single JSX runtime end to end.
|
|
13
|
+
//
|
|
14
|
+
// `providerImportSource` is set to our components provider only when the app ships an `mdx-components`
|
|
15
|
+
// file; without one, MDX emits bare host elements, which is exactly the mdx-no-mdx-components contract.
|
|
16
|
+
|
|
17
|
+
import { existsSync } from 'node:fs';
|
|
18
|
+
import { createRequire } from 'node:module';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { pathToFileURL } from 'node:url';
|
|
21
|
+
import { getNextConfig } from '../next/config-loader';
|
|
22
|
+
|
|
23
|
+
export interface MdxCompileResult {
|
|
24
|
+
code: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface MdxModule {
|
|
28
|
+
compile: (
|
|
29
|
+
source: string | { value: string; path?: string },
|
|
30
|
+
options?: Record<string, unknown>,
|
|
31
|
+
) => Promise<{ toString: () => string; value: string }>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface MdxOptions {
|
|
35
|
+
remarkPlugins?: unknown[];
|
|
36
|
+
rehypePlugins?: unknown[];
|
|
37
|
+
recmaPlugins?: unknown[];
|
|
38
|
+
remarkRehypeOptions?: Record<string, unknown>;
|
|
39
|
+
providerImportSource?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let cachedModule: MdxModule | null | undefined;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Load `@mdx-js/mdx` lazily. Returns `null` (not throwing) when the optional
|
|
46
|
+
* dependency is absent so the loader can surface a single actionable error only
|
|
47
|
+
* when an app actually imports `.mdx`.
|
|
48
|
+
*/
|
|
49
|
+
async function loadMdx(): Promise<MdxModule | null> {
|
|
50
|
+
if (cachedModule !== undefined) return cachedModule;
|
|
51
|
+
try {
|
|
52
|
+
const specifier = '@mdx-js/mdx';
|
|
53
|
+
cachedModule = (await import(/* @vite-ignore */ specifier)) as MdxModule;
|
|
54
|
+
} catch {
|
|
55
|
+
cachedModule = null;
|
|
56
|
+
}
|
|
57
|
+
return cachedModule;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The user's mdxOptions from the wrapped next.config, normalized. */
|
|
61
|
+
export function mdxOptionsFromConfig(): MdxOptions {
|
|
62
|
+
const config = getNextConfig() as {
|
|
63
|
+
mdxOptions?: MdxOptions;
|
|
64
|
+
experimental?: { mdxRs?: unknown };
|
|
65
|
+
};
|
|
66
|
+
// @next/mdx stores the merged plugin options under `mdxOptions` on the config
|
|
67
|
+
// object the wrapper produces. `experimental.mdxRs` selects the Rust pipeline
|
|
68
|
+
// upstream; we always use the JS pipeline, so it is intentionally ignored.
|
|
69
|
+
return config.mdxOptions ?? {};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Resolve the app's `mdx-components.{tsx,ts,jsx,js,mjs}` provider file if present.
|
|
74
|
+
* Next looks for it at the project root (or `src/`). Returns the absolute path
|
|
75
|
+
* used as `providerImportSource`, or `undefined` when absent (bare host elements).
|
|
76
|
+
*/
|
|
77
|
+
export function resolveMdxComponents(root: string): string | undefined {
|
|
78
|
+
const bases = [root, path.join(root, 'src')];
|
|
79
|
+
const names = ['mdx-components.tsx', 'mdx-components.ts', 'mdx-components.jsx', 'mdx-components.js', 'mdx-components.mjs'];
|
|
80
|
+
for (const base of bases) {
|
|
81
|
+
for (const name of names) {
|
|
82
|
+
const candidate = path.join(base, name);
|
|
83
|
+
if (existsSync(candidate)) return candidate;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function interopDefault(module: unknown): unknown {
|
|
90
|
+
return (module as { default?: unknown })?.default ?? module;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Sentinel returned when a configured plugin package can't be resolved from the
|
|
94
|
+
// app, so `resolvePlugins` can filter it out.
|
|
95
|
+
const UNRESOLVED_PLUGIN = Symbol('pnext.mdx.unresolved-plugin');
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Resolve a single plugin entry to the shape `@mdx-js/mdx` expects. `@next/mdx` stores plugins as
|
|
99
|
+
* serializable STRINGS (a bare package name) or `[name, opts]` tuples, because webpack loader options
|
|
100
|
+
* must serialize. Mirroring its mdx-js-loader: resolve the name from the project root and
|
|
101
|
+
* dynamic-import it, taking the default export. Entries that are already functions or tuples of
|
|
102
|
+
* functions pass through untouched.
|
|
103
|
+
*
|
|
104
|
+
* A configured plugin that cannot be resolved OR fails to load returns UNRESOLVED_PLUGIN (a warn, not a
|
|
105
|
+
* throw): neither a missing optional transform nor a broken transitive dependency may take down the
|
|
106
|
+
* whole `.mdx` render. This also matches `mdxRs: true`, where Next never JS-resolves these packages at
|
|
107
|
+
* all. Load failures are common when the toolchain resolves into a flat auto-install cache rather than
|
|
108
|
+
* a real node_modules tree, so the real error is surfaced to the log instead of silently 500ing pages.
|
|
109
|
+
*/
|
|
110
|
+
async function resolvePlugin(
|
|
111
|
+
entry: unknown,
|
|
112
|
+
resolveFrom: NodeRequire,
|
|
113
|
+
): Promise<unknown> {
|
|
114
|
+
if (typeof entry === 'function') return entry;
|
|
115
|
+
if (typeof entry === 'string') {
|
|
116
|
+
return loadPlugin(entry, resolveFrom);
|
|
117
|
+
}
|
|
118
|
+
if (Array.isArray(entry) && typeof entry[0] === 'string') {
|
|
119
|
+
const tuple = entry as unknown[];
|
|
120
|
+
const plugin = await loadPlugin(tuple[0] as string, resolveFrom);
|
|
121
|
+
if (plugin === UNRESOLVED_PLUGIN) return UNRESOLVED_PLUGIN;
|
|
122
|
+
return [plugin, ...tuple.slice(1)];
|
|
123
|
+
}
|
|
124
|
+
return entry;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Resolve a plugin from the app root and import it. Returns the plugin's default
|
|
129
|
+
* export, or UNRESOLVED_PLUGIN (with a warning) when it is not installed or
|
|
130
|
+
* cannot be loaded. Resolution is rooted at the app (`createRequire(root/...)`)
|
|
131
|
+
* so the toolchain stays inside the app's node_modules.
|
|
132
|
+
*/
|
|
133
|
+
async function loadPlugin(name: string, resolveFrom: NodeRequire): Promise<unknown> {
|
|
134
|
+
let resolved: string;
|
|
135
|
+
try {
|
|
136
|
+
resolved = resolveFrom.resolve(name);
|
|
137
|
+
} catch {
|
|
138
|
+
console.warn(
|
|
139
|
+
`pnext: MDX plugin '${name}' is configured but not installed; skipping it. ` +
|
|
140
|
+
'Install it to apply this remark/rehype/recma plugin.',
|
|
141
|
+
);
|
|
142
|
+
return UNRESOLVED_PLUGIN;
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
return interopDefault(await import(pathToFileURL(resolved).href));
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.warn(
|
|
148
|
+
`pnext: MDX plugin '${name}' failed to load and was skipped: ` +
|
|
149
|
+
`${error instanceof Error ? error.message : String(error)}. ` +
|
|
150
|
+
'Ensure it is installed in the app’s node_modules (not only Bun’s auto-install cache).',
|
|
151
|
+
);
|
|
152
|
+
return UNRESOLVED_PLUGIN;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Resolve every entry of a remark/rehype/recma plugin array (skipping absent packages). */
|
|
157
|
+
async function resolvePlugins(plugins: unknown[], resolveFrom: NodeRequire): Promise<unknown[]> {
|
|
158
|
+
const resolved = await Promise.all(plugins.map(entry => resolvePlugin(entry, resolveFrom)));
|
|
159
|
+
return resolved.filter(entry => entry !== UNRESOLVED_PLUGIN);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Compile MDX source to JSX targeting the preact automatic runtime. `root` locates
|
|
164
|
+
* the optional mdx-components provider; `filePath` feeds source positions.
|
|
165
|
+
*/
|
|
166
|
+
export async function compileMdx(
|
|
167
|
+
source: string,
|
|
168
|
+
filePath: string,
|
|
169
|
+
root: string,
|
|
170
|
+
): Promise<MdxCompileResult> {
|
|
171
|
+
const mdx = await loadMdx();
|
|
172
|
+
if (!mdx) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
"MDX support requires the '@mdx-js/mdx' package. Install it to render .mdx files.",
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const userOptions = mdxOptionsFromConfig();
|
|
179
|
+
const provider = resolveMdxComponents(root);
|
|
180
|
+
|
|
181
|
+
// Resolve string/tuple plugin names (@next/mdx's serialized form) to actual
|
|
182
|
+
// plugin functions from the app's node_modules before handing them to MDX.
|
|
183
|
+
const resolveFrom = createRequire(path.join(root, 'noop.js'));
|
|
184
|
+
|
|
185
|
+
const [remarkPlugins, rehypePlugins, recmaPlugins] = await Promise.all([
|
|
186
|
+
resolvePlugins(userOptions.remarkPlugins ?? [], resolveFrom),
|
|
187
|
+
resolvePlugins(userOptions.rehypePlugins ?? [], resolveFrom),
|
|
188
|
+
resolvePlugins(userOptions.recmaPlugins ?? [], resolveFrom),
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
const compiled = await mdx.compile(
|
|
192
|
+
{ value: source, path: filePath },
|
|
193
|
+
{
|
|
194
|
+
// Emit JSX; esbuild's `jsx` loader performs the final transform with
|
|
195
|
+
// jsxImportSource: 'preact', matching every other compile site.
|
|
196
|
+
jsx: true,
|
|
197
|
+
jsxImportSource: 'preact',
|
|
198
|
+
// Full MDX (not just markdown) so `import`/`export` and JSX in `.mdx` work.
|
|
199
|
+
format: 'mdx',
|
|
200
|
+
// Development positions off — matches production build output.
|
|
201
|
+
development: false,
|
|
202
|
+
remarkPlugins,
|
|
203
|
+
rehypePlugins,
|
|
204
|
+
recmaPlugins,
|
|
205
|
+
...(userOptions.remarkRehypeOptions
|
|
206
|
+
? { remarkRehypeOptions: userOptions.remarkRehypeOptions }
|
|
207
|
+
: {}),
|
|
208
|
+
// Only wire a components provider when the app supplies one; otherwise MDX
|
|
209
|
+
// emits plain host elements (the mdx-no-mdx-components contract).
|
|
210
|
+
...(userOptions.providerImportSource
|
|
211
|
+
? { providerImportSource: userOptions.providerImportSource }
|
|
212
|
+
: provider
|
|
213
|
+
? { providerImportSource: provider }
|
|
214
|
+
: {}),
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
return { code: compiled.toString() };
|
|
219
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// `@next/mdx` config-time stub (COMPAT).
|
|
2
|
+
//
|
|
3
|
+
// `createMDX(pluginOptions)(nextConfig)` embeds the user's remark/rehype/recma plugin options ONLY
|
|
4
|
+
// inside a webpack/turbopack loader config it attaches to the returned next.config - it never exposes
|
|
5
|
+
// them on a readable top-level field. pnext has no webpack, so those options would be lost and every
|
|
6
|
+
// configured MDX plugin would silently not run.
|
|
7
|
+
//
|
|
8
|
+
// During config bundling, `@next/mdx` resolves to this stub instead of the real package. The stub
|
|
9
|
+
// reproduces the wrapper's signature but stashes the plugin options on `config.mdxOptions`, exactly
|
|
10
|
+
// where pnext's MDX compile pipeline reads them. It also removes the need for `@next/mdx` itself to be
|
|
11
|
+
// installed for the config to load. The wrapped config's own webpack/turbopack/pageExtensions are
|
|
12
|
+
// irrelevant to pnext, so the stub only needs to merge `mdxOptions`.
|
|
13
|
+
|
|
14
|
+
import type { Plugin } from 'esbuild';
|
|
15
|
+
|
|
16
|
+
// CJS so esbuild's default-import interop yields the wrapper factory for both
|
|
17
|
+
// `import nextMDX from '@next/mdx'` and `require('@next/mdx')` config authors.
|
|
18
|
+
const NEXT_MDX_STUB_CONTENTS = `
|
|
19
|
+
const createMDX = (pluginOptions = {}) => (nextConfig = {}) => ({
|
|
20
|
+
...nextConfig,
|
|
21
|
+
mdxOptions: (pluginOptions && pluginOptions.options) || {},
|
|
22
|
+
});
|
|
23
|
+
module.exports = createMDX;
|
|
24
|
+
module.exports.default = createMDX;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* esbuild plugin (config-load only) that replaces `@next/mdx` (and its
|
|
29
|
+
* `@next/mdx/*` deep specifiers) with a stub capturing the plugin options onto
|
|
30
|
+
* `config.mdxOptions`.
|
|
31
|
+
*/
|
|
32
|
+
export function nextMdxConfigStubPlugin(): Plugin {
|
|
33
|
+
return {
|
|
34
|
+
name: 'pnext-next-mdx-config-stub',
|
|
35
|
+
setup(build) {
|
|
36
|
+
build.onResolve({ filter: /^@next\/mdx(?:\/|$)/ }, args => ({
|
|
37
|
+
path: args.path,
|
|
38
|
+
namespace: 'pnext-next-mdx-stub',
|
|
39
|
+
}));
|
|
40
|
+
build.onLoad({ filter: /.*/, namespace: 'pnext-next-mdx-stub' }, () => ({
|
|
41
|
+
contents: NEXT_MDX_STUB_CONTENTS,
|
|
42
|
+
loader: 'js',
|
|
43
|
+
}));
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// MDX esbuild plugin (COMPAT).
|
|
2
|
+
//
|
|
3
|
+
// Intercepts `.mdx` (and `.md`) loads across every build graph - client bundle, dev RSC route bundle,
|
|
4
|
+
// and prod server compile - and replaces the file contents with the JSX compiled from MDX. Returned as
|
|
5
|
+
// `loader: 'jsx'` so esbuild does the final JSX-to-preact-runtime transform.
|
|
6
|
+
//
|
|
7
|
+
// The same factory is registered on both the client and server esbuild plugin lists. It resolves the
|
|
8
|
+
// project root from the build's `absWorkingDir` (all pnext esbuild builds set it to the project root)
|
|
9
|
+
// so the optional mdx-components provider is located correctly.
|
|
10
|
+
|
|
11
|
+
import { readFile } from 'node:fs/promises';
|
|
12
|
+
import type { Plugin } from 'esbuild';
|
|
13
|
+
import { applyClientSourceTransforms } from '../../extensions';
|
|
14
|
+
import { compileMdx } from './compile';
|
|
15
|
+
|
|
16
|
+
const MDX_FILTER = /\.mdx?$/;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* esbuild plugin that compiles `.md`/`.mdx` modules through the JS MDX pipeline.
|
|
20
|
+
* `rootOverride` pins the project root; when omitted the plugin uses the build's
|
|
21
|
+
* `absWorkingDir` (falling back to `process.cwd()`).
|
|
22
|
+
*/
|
|
23
|
+
export function mdxEsbuildPlugin(rootOverride?: string): Plugin {
|
|
24
|
+
return {
|
|
25
|
+
name: 'pnext-mdx',
|
|
26
|
+
setup(build) {
|
|
27
|
+
const root = rootOverride ?? build.initialOptions.absWorkingDir ?? process.cwd();
|
|
28
|
+
build.onLoad({ filter: MDX_FILTER }, async args => {
|
|
29
|
+
const source = await readFile(args.path, 'utf8');
|
|
30
|
+
const { code } = await compileMdx(source, args.path, root);
|
|
31
|
+
// Compiled MDX must still pass through registered source transforms
|
|
32
|
+
// (e.g. modularizeImports) — raw compileMdx output bypasses them.
|
|
33
|
+
return { contents: applyClientSourceTransforms(code, args.path, root), loader: 'jsx' };
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|