@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,363 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Bundler config accessors (COMPAT).
|
|
3
|
+
//
|
|
4
|
+
// Small typed readers over the loaded next.config (getNextConfig()) for the
|
|
5
|
+
// bundler-feature plugins. Kept in one place so every plugin reads the same
|
|
6
|
+
// normalized shapes and a pure-core / non-next app (empty config) degrades to
|
|
7
|
+
// safe defaults.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
import { getNextConfig } from '../next/config-loader';
|
|
11
|
+
import type { ModularizeRule } from './modularize-imports';
|
|
12
|
+
|
|
13
|
+
/** A predefined built-in set kept external server-side (Next ships this list). */
|
|
14
|
+
const PREDEFINED_SERVER_EXTERNAL_PACKAGES = [
|
|
15
|
+
'sharp',
|
|
16
|
+
'sqlite3',
|
|
17
|
+
'better-sqlite3',
|
|
18
|
+
'canvas',
|
|
19
|
+
'@node-rs/argon2',
|
|
20
|
+
'@node-rs/bcrypt',
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
function stringArray(value: unknown): string[] {
|
|
24
|
+
return Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `serverExternalPackages` + the predefined built-in list: packages kept external
|
|
29
|
+
* (resolved from node_modules, never bundled) in the server graph.
|
|
30
|
+
*/
|
|
31
|
+
export function serverExternalPackages(): Set<string> {
|
|
32
|
+
const config = getNextConfig();
|
|
33
|
+
return new Set([
|
|
34
|
+
...PREDEFINED_SERVER_EXTERNAL_PACKAGES,
|
|
35
|
+
...stringArray(config.serverExternalPackages),
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The build's deployment id: next.config `deploymentId`, else the
|
|
41
|
+
* `NEXT_DEPLOYMENT_ID` env var (Next accepts either). Inlined into every graph
|
|
42
|
+
* and written to `required-server-files.json` so tooling can read it back.
|
|
43
|
+
*/
|
|
44
|
+
export function deploymentId(): string | undefined {
|
|
45
|
+
const configured = getNextConfig().deploymentId;
|
|
46
|
+
if (typeof configured === 'string' && configured.length > 0) return configured;
|
|
47
|
+
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
48
|
+
const fromEnv = process.env.NEXT_DEPLOYMENT_ID;
|
|
49
|
+
return fromEnv && fromEnv.length > 0 ? fromEnv : undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** `publicRuntimeConfig`: the subset of runtime config safe to inline into client bundles. */
|
|
53
|
+
export function publicRuntimeConfig(): Record<string, unknown> {
|
|
54
|
+
const config = getNextConfig().publicRuntimeConfig;
|
|
55
|
+
return config && typeof config === 'object' ? (config as Record<string, unknown>) : {};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** `transpilePackages`: node_modules packages force-bundled + transpiled. */
|
|
59
|
+
export function transpilePackages(): string[] {
|
|
60
|
+
return stringArray(getNextConfig().transpilePackages);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* `modularizeImports`: specifier -> `{ transform, skipDefaultConversion }`
|
|
65
|
+
* rules that split barrel imports into per-member imports. Only the string
|
|
66
|
+
* `transform` template form is honored (the shape Next's compat fixtures use);
|
|
67
|
+
* malformed/object-template entries are dropped so a build still proceeds.
|
|
68
|
+
*/
|
|
69
|
+
export function modularizeImports(): Record<string, ModularizeRule> {
|
|
70
|
+
const raw = getNextConfig().modularizeImports;
|
|
71
|
+
if (!raw || typeof raw !== 'object') return {};
|
|
72
|
+
const out: Record<string, ModularizeRule> = {};
|
|
73
|
+
for (const [specifier, value] of Object.entries(raw as Record<string, unknown>)) {
|
|
74
|
+
if (value && typeof value === 'object' && typeof (value as ModularizeRule).transform === 'string') {
|
|
75
|
+
const rule = value as ModularizeRule;
|
|
76
|
+
out[specifier] = {
|
|
77
|
+
transform: rule.transform,
|
|
78
|
+
skipDefaultConversion: Boolean(rule.skipDefaultConversion),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* `experimental.optimizePackageImports`: barrel packages whose named imports
|
|
87
|
+
* are rewritten to their leaf modules (see optimize-package-imports.ts). The
|
|
88
|
+
* configured list EXTENDS the built-in default set, as Next's does.
|
|
89
|
+
*/
|
|
90
|
+
export function optimizePackageImports(): string[] {
|
|
91
|
+
const experimental = getNextConfig().experimental as
|
|
92
|
+
| { optimizePackageImports?: unknown }
|
|
93
|
+
| undefined;
|
|
94
|
+
return stringArray(experimental?.optimizePackageImports);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** A normalized `compiler.relay` entry (see relay-transform.ts). */
|
|
98
|
+
export interface RelayCompilerConfig {
|
|
99
|
+
/** Source root the transform applies to, relative to `process.cwd()`. */
|
|
100
|
+
src: string;
|
|
101
|
+
/** Where relay-compiler wrote its artifacts, relative to `process.cwd()`. */
|
|
102
|
+
artifactDirectory?: string;
|
|
103
|
+
/** The artifact language: decides the generated file's extension. */
|
|
104
|
+
language: 'javascript' | 'typescript' | 'flow';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* `compiler.relay`: the Relay SWC transform's config. `src` is required by
|
|
109
|
+
* Next's schema; `language` defaults to `'javascript'`. Returns undefined when
|
|
110
|
+
* unconfigured so the transform stays a no-op.
|
|
111
|
+
*/
|
|
112
|
+
export function relayCompilerConfig(): RelayCompilerConfig | undefined {
|
|
113
|
+
const compiler = getNextConfig().compiler as
|
|
114
|
+
| { relay?: { src?: unknown; artifactDirectory?: unknown; language?: unknown } }
|
|
115
|
+
| undefined;
|
|
116
|
+
const relay = compiler?.relay;
|
|
117
|
+
if (!relay || typeof relay !== 'object' || typeof relay.src !== 'string') return undefined;
|
|
118
|
+
const language = relay.language;
|
|
119
|
+
return {
|
|
120
|
+
src: relay.src,
|
|
121
|
+
artifactDirectory:
|
|
122
|
+
typeof relay.artifactDirectory === 'string' ? relay.artifactDirectory : undefined,
|
|
123
|
+
language:
|
|
124
|
+
language === 'typescript' || language === 'flow' || language === 'javascript'
|
|
125
|
+
? language
|
|
126
|
+
: 'javascript',
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* `experimental.esmExternals`: `true` (default) | `false` | `'loose'`.
|
|
132
|
+
* Controls whether ESM externals stay external or are bundled.
|
|
133
|
+
*/
|
|
134
|
+
export function esmExternals(): boolean | 'loose' {
|
|
135
|
+
const experimental = getNextConfig().experimental as
|
|
136
|
+
| { esmExternals?: boolean | 'loose' }
|
|
137
|
+
| undefined;
|
|
138
|
+
return experimental?.esmExternals ?? true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* `resolveExtensions`: user-supplied resolver extension order, if any. Next accepts the top-level key,
|
|
143
|
+
* `turbopack.resolveExtensions`, and the legacy `experimental.turbo.resolveExtensions` spelling.
|
|
144
|
+
*/
|
|
145
|
+
export function resolveExtensions(): string[] | undefined {
|
|
146
|
+
const config = getNextConfig();
|
|
147
|
+
const turbopack = config.turbopack as { resolveExtensions?: unknown } | undefined;
|
|
148
|
+
const experimental = config.experimental as
|
|
149
|
+
| { turbo?: { resolveExtensions?: unknown } }
|
|
150
|
+
| undefined;
|
|
151
|
+
for (const value of [
|
|
152
|
+
config.resolveExtensions,
|
|
153
|
+
turbopack?.resolveExtensions,
|
|
154
|
+
experimental?.turbo?.resolveExtensions,
|
|
155
|
+
]) {
|
|
156
|
+
const list = stringArray(value);
|
|
157
|
+
if (list.length > 0) return list;
|
|
158
|
+
}
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** `typescript.tsconfigPath`: a non-default tsconfig location. */
|
|
163
|
+
export function tsconfigPath(): string | undefined {
|
|
164
|
+
const ts = getNextConfig().typescript as { tsconfigPath?: string } | undefined;
|
|
165
|
+
return typeof ts?.tsconfigPath === 'string' ? ts.tsconfigPath : undefined;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface CompilerDefines {
|
|
169
|
+
/** Inlined into BOTH server and client bundles. */
|
|
170
|
+
define: Record<string, string>;
|
|
171
|
+
/** Inlined into the SERVER bundle only (client sees the fallback). */
|
|
172
|
+
defineServer: Record<string, string>;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function normalizeDefineMap(value: unknown): Record<string, string> {
|
|
176
|
+
if (!value || typeof value !== 'object') return {};
|
|
177
|
+
const out: Record<string, string> = {};
|
|
178
|
+
for (const [key, raw] of Object.entries(value as Record<string, unknown>)) {
|
|
179
|
+
// esbuild `define` values must be JS source text: strings need to be
|
|
180
|
+
// JSON-encoded, primitives stringified.
|
|
181
|
+
if (typeof raw === 'string') out[key] = JSON.stringify(raw);
|
|
182
|
+
else if (typeof raw === 'number' || typeof raw === 'boolean') out[key] = String(raw);
|
|
183
|
+
}
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** `compiler.define` / `compiler.defineServer` inlined-constant maps. */
|
|
188
|
+
export function compilerDefines(): CompilerDefines {
|
|
189
|
+
const compiler = getNextConfig().compiler as
|
|
190
|
+
| { define?: Record<string, unknown>; defineServer?: Record<string, unknown> }
|
|
191
|
+
| undefined;
|
|
192
|
+
return {
|
|
193
|
+
define: normalizeDefineMap(compiler?.define),
|
|
194
|
+
defineServer: normalizeDefineMap(compiler?.defineServer),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** The console methods Next's SWC `removeConsole` transform can target. */
|
|
199
|
+
const CONSOLE_METHODS = [
|
|
200
|
+
'assert',
|
|
201
|
+
'clear',
|
|
202
|
+
'count',
|
|
203
|
+
'countReset',
|
|
204
|
+
'debug',
|
|
205
|
+
'dir',
|
|
206
|
+
'dirxml',
|
|
207
|
+
'error',
|
|
208
|
+
'group',
|
|
209
|
+
'groupCollapsed',
|
|
210
|
+
'groupEnd',
|
|
211
|
+
'info',
|
|
212
|
+
'log',
|
|
213
|
+
'table',
|
|
214
|
+
'time',
|
|
215
|
+
'timeEnd',
|
|
216
|
+
'timeLog',
|
|
217
|
+
'trace',
|
|
218
|
+
'warn',
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* `compiler.removeConsole`: `true` strips every `console.*` call; `{ exclude }`
|
|
223
|
+
* strips all methods except the listed ones. Returns the `console.<method>`
|
|
224
|
+
* esbuild `pure` targets to strip, or `undefined` when unconfigured.
|
|
225
|
+
*/
|
|
226
|
+
export function removeConsoleTargets(): string[] | undefined {
|
|
227
|
+
const compiler = getNextConfig().compiler as
|
|
228
|
+
| { removeConsole?: boolean | { exclude?: unknown } }
|
|
229
|
+
| undefined;
|
|
230
|
+
const removeConsole = compiler?.removeConsole;
|
|
231
|
+
if (!removeConsole) return undefined;
|
|
232
|
+
const exclude = new Set(
|
|
233
|
+
typeof removeConsole === 'object' ? stringArray(removeConsole.exclude) : [],
|
|
234
|
+
);
|
|
235
|
+
return CONSOLE_METHODS.filter(method => !exclude.has(method)).map(method => `console.${method}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** `cacheComponents` (PPR): gates the `next-js` export condition. */
|
|
239
|
+
export function cacheComponentsEnabled(): boolean {
|
|
240
|
+
const config = getNextConfig();
|
|
241
|
+
const experimental = config.experimental as { cacheComponents?: boolean } | undefined;
|
|
242
|
+
return Boolean(config.cacheComponents ?? experimental?.cacheComponents);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* `experimental.swcEnvOptions`: when `mode: 'usage'`, SWC injects core-js
|
|
247
|
+
* polyfills for JS features the build targets (per browserslist) that the user
|
|
248
|
+
* code actually uses. pnext mirrors the user-visible outcome for the features
|
|
249
|
+
* apps rely on: usage-based injection of the matching core-js module into the
|
|
250
|
+
* client graph. Returns the configured mode (or undefined when unset).
|
|
251
|
+
*/
|
|
252
|
+
export function swcEnvUsageMode(): 'usage' | 'entry' | undefined {
|
|
253
|
+
const experimental = getNextConfig().experimental as
|
|
254
|
+
| { swcEnvOptions?: { mode?: unknown } }
|
|
255
|
+
| undefined;
|
|
256
|
+
const mode = experimental?.swcEnvOptions?.mode;
|
|
257
|
+
return mode === 'usage' || mode === 'entry' ? mode : undefined;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Next's `reactCompiler` / legacy experimental flag. */
|
|
261
|
+
export function reactCompilerEnabled(): boolean {
|
|
262
|
+
const config = getNextConfig();
|
|
263
|
+
const experimental = config.experimental as { reactCompiler?: boolean } | undefined;
|
|
264
|
+
return Boolean(config.reactCompiler ?? experimental?.reactCompiler);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** A single `turbopack.rules` entry, normalized to the shapes pnext supports. */
|
|
268
|
+
export interface WebpackLoaderRule {
|
|
269
|
+
/** The rule's glob key, e.g. `'*.txt'` or `'*.test-file.ts'`. Only a leading `*` wildcard is honored. */
|
|
270
|
+
glob: string;
|
|
271
|
+
/** Loader module specifiers (relative path or bare package name), applied in array order. */
|
|
272
|
+
loaders: string[];
|
|
273
|
+
/** The virtual extension of the loader chain's final output, e.g. `'*.js'`. */
|
|
274
|
+
as?: string;
|
|
275
|
+
/**
|
|
276
|
+
* A conditional rule guard: the rule only applies when the import's
|
|
277
|
+
* `resourceQuery` matches (string equality or a regex `.test`). Comes from the
|
|
278
|
+
* conditional-array rule form `'*.ext': [{ condition: { query }, loaders }]`.
|
|
279
|
+
*/
|
|
280
|
+
condition?: { query: string | RegExp };
|
|
281
|
+
/**
|
|
282
|
+
* A no-loader `type` asset rule: how the raw file is represented as a module.
|
|
283
|
+
* `'resource'` emits the file under `/_next/static/media` and exports its URL
|
|
284
|
+
* string; `'bytes'` exports a `Uint8Array`; `'source'` exports the file text.
|
|
285
|
+
*/
|
|
286
|
+
assetType?: 'resource' | 'bytes' | 'source';
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* `turbopack.rules`: Next's webpack-loader-rule config, read directly (rather
|
|
291
|
+
* than executing the `webpack(config)` mutator) since fixtures configure both
|
|
292
|
+
* spellings with equivalent semantics. Handles the loader shorthand/object
|
|
293
|
+
* form, the conditional-array form (`[{ condition, loaders, as }]`), and no-
|
|
294
|
+
* loader `type` asset rules (`{ type: 'asset' | 'bytes' | 'asset/source' }`).
|
|
295
|
+
*/
|
|
296
|
+
export function turbopackLoaderRules(): WebpackLoaderRule[] {
|
|
297
|
+
const config = getNextConfig();
|
|
298
|
+
const turbopack = config.turbopack as { rules?: Record<string, unknown> } | undefined;
|
|
299
|
+
const rules = turbopack?.rules;
|
|
300
|
+
if (!rules || typeof rules !== 'object') return [];
|
|
301
|
+
const out: WebpackLoaderRule[] = [];
|
|
302
|
+
for (const [glob, raw] of Object.entries(rules)) collectLoaderRuleEntries(glob, raw, out);
|
|
303
|
+
return out;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function collectLoaderRuleEntries(glob: string, raw: unknown, out: WebpackLoaderRule[]): void {
|
|
307
|
+
if (Array.isArray(raw)) {
|
|
308
|
+
// A plain array of loader specifiers, or the conditional-array form whose
|
|
309
|
+
// entries are `{ condition, loaders, as }` objects.
|
|
310
|
+
if (raw.every(item => typeof item === 'string')) {
|
|
311
|
+
const loaders = stringArray(raw);
|
|
312
|
+
if (loaders.length > 0) out.push({ glob, loaders });
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
for (const item of raw) {
|
|
316
|
+
if (item && typeof item === 'object') pushLoaderRuleObject(glob, item as Record<string, unknown>, out);
|
|
317
|
+
}
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (raw && typeof raw === 'object') pushLoaderRuleObject(glob, raw as Record<string, unknown>, out);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function pushLoaderRuleObject(glob: string, entry: Record<string, unknown>, out: WebpackLoaderRule[]): void {
|
|
324
|
+
const loaders = stringArray(entry.loaders);
|
|
325
|
+
const as = typeof entry.as === 'string' ? entry.as : undefined;
|
|
326
|
+
const condition = parseRuleCondition(entry.condition);
|
|
327
|
+
if (loaders.length > 0) {
|
|
328
|
+
out.push({ glob, loaders, as, ...(condition ? { condition } : {}) });
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const assetType = parseAssetRuleType(entry.type);
|
|
332
|
+
if (assetType) out.push({ glob, loaders: [], as, assetType });
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function parseRuleCondition(value: unknown): { query: string | RegExp } | undefined {
|
|
336
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
337
|
+
const query = (value as { query?: unknown }).query;
|
|
338
|
+
if (typeof query === 'string' || query instanceof RegExp) return { query };
|
|
339
|
+
return undefined;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function parseAssetRuleType(value: unknown): 'resource' | 'bytes' | 'source' | undefined {
|
|
343
|
+
if (value === 'asset' || value === 'asset/resource') return 'resource';
|
|
344
|
+
if (value === 'bytes') return 'bytes';
|
|
345
|
+
if (value === 'asset/source') return 'source';
|
|
346
|
+
return undefined;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* `turbopack.resolveAlias`: specifier-to-replacement-path mappings. Exact keys and trailing-glob keys
|
|
351
|
+
* are both honored. Values are returned verbatim - relative paths are resolved against the project root
|
|
352
|
+
* by the consumer.
|
|
353
|
+
*/
|
|
354
|
+
export function turbopackResolveAlias(): Record<string, string> {
|
|
355
|
+
const turbopack = getNextConfig().turbopack as { resolveAlias?: unknown } | undefined;
|
|
356
|
+
const alias = turbopack?.resolveAlias;
|
|
357
|
+
if (!alias || typeof alias !== 'object') return {};
|
|
358
|
+
const out: Record<string, string> = {};
|
|
359
|
+
for (const [key, value] of Object.entries(alias as Record<string, unknown>)) {
|
|
360
|
+
if (typeof value === 'string') out[key] = value;
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Plugin } from 'esbuild';
|
|
2
|
+
import type { ResolvedConfig } from '../../config';
|
|
3
|
+
import { packageNameOfSpecifier, resolvePackageSpecifier } from '../../resolve/imports';
|
|
4
|
+
import { escapeRegex } from '../../utils/source';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Bundle configured server externals into client output with browser exports.
|
|
8
|
+
* The configured package names ARE the filter (plus their subpaths), so an app
|
|
9
|
+
* with no `serverExternalPackages` registers no plugin at all instead of paying
|
|
10
|
+
* a hook fire per bare import in the graph.
|
|
11
|
+
*/
|
|
12
|
+
export function clientExternalPackageResolvePlugin(
|
|
13
|
+
config: ResolvedConfig,
|
|
14
|
+
externals: ReadonlySet<string>,
|
|
15
|
+
): Plugin | undefined {
|
|
16
|
+
if (externals.size === 0) return undefined;
|
|
17
|
+
const filter = new RegExp(`^(?:${[...externals].map(escapeRegex).join('|')})(?:/|$)`);
|
|
18
|
+
return {
|
|
19
|
+
name: 'pnext-client-external-package-resolve',
|
|
20
|
+
setup(build) {
|
|
21
|
+
build.onResolve({ filter }, args => {
|
|
22
|
+
const packageName = packageNameOfSpecifier(args.path);
|
|
23
|
+
if (!packageName || !externals.has(packageName)) return undefined;
|
|
24
|
+
const resolved = resolvePackageSpecifier(
|
|
25
|
+
config.root,
|
|
26
|
+
`${args.importer || config.root}/pnext-resolve.ts`,
|
|
27
|
+
args.path,
|
|
28
|
+
['import', 'browser', 'require', 'default'],
|
|
29
|
+
);
|
|
30
|
+
return resolved ? { path: resolved } : undefined;
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { rewriteFacts } from '../../resolve/scan-facts';
|
|
5
|
+
import { spliceSource } from '../../dev/module-transform';
|
|
6
|
+
|
|
7
|
+
export function sourceHasImportMetaUrl(source: string) {
|
|
8
|
+
return source.includes('import.meta.url');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Inline `import.meta.url` as the module's own file URL. Folded onto the module
|
|
13
|
+
* record's `importMetas` spans: exact by construction, where
|
|
14
|
+
* the regex also fired inside strings and comments.
|
|
15
|
+
*/
|
|
16
|
+
export function rewriteImportMetaUrlSource(source: string, file: string) {
|
|
17
|
+
if (!sourceHasImportMetaUrl(source)) return source;
|
|
18
|
+
const facts = rewriteFacts(file, source);
|
|
19
|
+
if (facts.unreliable) return legacyRewriteImportMetaUrl(source, file);
|
|
20
|
+
const edits = facts.importMetas
|
|
21
|
+
.filter(meta => /^\.url\b/.test(source.slice(meta.end, meta.end + 5)))
|
|
22
|
+
.map(meta => ({
|
|
23
|
+
start: meta.start,
|
|
24
|
+
end: meta.end + 4,
|
|
25
|
+
value: JSON.stringify(pathToFileURL(virtualFixturePath(realSourcePath(file))).href),
|
|
26
|
+
}));
|
|
27
|
+
return edits.length > 0 ? spliceSource(source, edits) : source;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// A source oxc could only partially recover keeps the textual pass: an
|
|
31
|
+
// incomplete record would silently under-rewrite.
|
|
32
|
+
function legacyRewriteImportMetaUrl(source: string, file: string) {
|
|
33
|
+
return source.replace(
|
|
34
|
+
/\bimport\.meta\.url\b/g,
|
|
35
|
+
JSON.stringify(pathToFileURL(virtualFixturePath(realSourcePath(file))).href),
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Modules can reach the bundler through the pages-compat mirror's source-app/
|
|
40
|
+
// source-pages symlinks; inlining that symlinked path breaks code that walks
|
|
41
|
+
// the filesystem relative to import.meta.url (Next inlines the real path).
|
|
42
|
+
function realSourcePath(file: string) {
|
|
43
|
+
try {
|
|
44
|
+
return realpathSync(file);
|
|
45
|
+
} catch {
|
|
46
|
+
return file;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function virtualFixturePath(file: string) {
|
|
51
|
+
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
52
|
+
const root = process.env.PNEXT_IMPORT_META_URL_FIXTURE_ROOT;
|
|
53
|
+
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
54
|
+
const virtualRoot = process.env.PNEXT_IMPORT_META_URL_VIRTUAL_ROOT;
|
|
55
|
+
if (!root || !virtualRoot) return file;
|
|
56
|
+
const relative = path.relative(root, file);
|
|
57
|
+
return relative && !relative.startsWith(`..${path.sep}`) && relative !== '..'
|
|
58
|
+
? path.join(virtualRoot, relative)
|
|
59
|
+
: file;
|
|
60
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// next.config `modularizeImports` (COMPAT).
|
|
2
|
+
//
|
|
3
|
+
// Next rewrites barrel `import { A, B } from '<specifier>'` into per-member imports of individual
|
|
4
|
+
// modules, driven by a `transform` template. For fixtures whose barrel file does not exist at all (only
|
|
5
|
+
// the per-member modules do), the rewrite is the ONLY way the import resolves.
|
|
6
|
+
//
|
|
7
|
+
// This is a plain source transform: for each configured specifier it splits a matching named-import
|
|
8
|
+
// statement into one import per member, applying the template with the kebabCase/camelCase/lowerCase/
|
|
9
|
+
// upperCase helpers. `skipDefaultConversion: true` emits a NAMED import of the original member name
|
|
10
|
+
// from the target module; otherwise a DEFAULT import. Aliasing is preserved.
|
|
11
|
+
//
|
|
12
|
+
// It runs BEFORE path-alias resolution, so the emitted specifiers still flow through tsconfig `paths`
|
|
13
|
+
// resolution. Namespace, default and mixed-default imports of the specifier are left untouched - Next
|
|
14
|
+
// bails on those too.
|
|
15
|
+
|
|
16
|
+
import { rewriteFacts, type ImportBinding } from '../../resolve/scan-facts';
|
|
17
|
+
import { spliceSource } from '../../dev/module-transform';
|
|
18
|
+
import { withSniff, type ServerSourceTransform } from '../../extensions';
|
|
19
|
+
import { specifierSniffTokens } from '../../utils/source';
|
|
20
|
+
|
|
21
|
+
/** A normalized `modularizeImports[specifier]` entry (string-template form). */
|
|
22
|
+
export interface ModularizeRule {
|
|
23
|
+
/** The output-path template, e.g. `'#/design-system/icons/{{ kebabCase member }}'`. */
|
|
24
|
+
transform: string;
|
|
25
|
+
/** `true` => named import of the member from the target; otherwise a default import. */
|
|
26
|
+
skipDefaultConversion?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Split an identifier into words on camelCase / digit / `_` / `-` boundaries. */
|
|
30
|
+
function splitWords(value: string): string[] {
|
|
31
|
+
return value
|
|
32
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
33
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
34
|
+
.replace(/[_-]+/g, ' ')
|
|
35
|
+
.trim()
|
|
36
|
+
.split(/\s+/)
|
|
37
|
+
.filter(Boolean);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Apply a template helper (`kebabCase`/`camelCase`/`lowerCase`/`upperCase`) to a member name. */
|
|
41
|
+
function applyHelper(helper: string, member: string): string {
|
|
42
|
+
switch (helper) {
|
|
43
|
+
case 'kebabCase':
|
|
44
|
+
return splitWords(member)
|
|
45
|
+
.map(word => word.toLowerCase())
|
|
46
|
+
.join('-');
|
|
47
|
+
case 'camelCase':
|
|
48
|
+
return splitWords(member)
|
|
49
|
+
.map((word, index) =>
|
|
50
|
+
index === 0 ? word.toLowerCase() : word[0]!.toUpperCase() + word.slice(1).toLowerCase(),
|
|
51
|
+
)
|
|
52
|
+
.join('');
|
|
53
|
+
case 'lowerCase':
|
|
54
|
+
return member.toLowerCase();
|
|
55
|
+
case 'upperCase':
|
|
56
|
+
return member.toUpperCase();
|
|
57
|
+
default:
|
|
58
|
+
return member;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Expand `{{ member }}` / `{{ helper member }}` placeholders in a transform template. */
|
|
63
|
+
function applyTemplate(template: string, member: string): string {
|
|
64
|
+
return template.replace(/\{\{\s*(?:(\w+)\s+)?member\s*\}\}/g, (_full, helper?: string) =>
|
|
65
|
+
helper ? applyHelper(helper, member) : member,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Rewrite the members of one matched named import into per-member import
|
|
71
|
+
* statements. Returns `undefined` (leaving the original import untouched) for a
|
|
72
|
+
* clause the rewrite does not own: no members, or a `type` import.
|
|
73
|
+
*/
|
|
74
|
+
function expandMembers(bindings: readonly ImportBinding[], rule: ModularizeRule): string | undefined {
|
|
75
|
+
if (bindings.length === 0) return undefined;
|
|
76
|
+
|
|
77
|
+
const statements: string[] = [];
|
|
78
|
+
for (const binding of bindings) {
|
|
79
|
+
if (binding.type || binding.imported === 'default' || binding.imported === '*') return undefined;
|
|
80
|
+
const original = binding.imported;
|
|
81
|
+
const local = binding.local;
|
|
82
|
+
const target = JSON.stringify(applyTemplate(rule.transform, original));
|
|
83
|
+
if (rule.skipDefaultConversion) {
|
|
84
|
+
const named = original === local ? original : `${original} as ${local}`;
|
|
85
|
+
statements.push(`import { ${named} } from ${target};`);
|
|
86
|
+
} else {
|
|
87
|
+
statements.push(`import ${local} from ${target};`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return statements.join(' ');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build a source transform applying the configured `modularizeImports` rules.
|
|
95
|
+
* Returns a no-op transform when nothing is configured so pure-core apps pay
|
|
96
|
+
* nothing.
|
|
97
|
+
*/
|
|
98
|
+
export function createModularizeImportsTransform(
|
|
99
|
+
rules: Record<string, ModularizeRule>,
|
|
100
|
+
): ServerSourceTransform {
|
|
101
|
+
if (Object.keys(rules).length === 0) return withSniff([], source => source);
|
|
102
|
+
|
|
103
|
+
// Folded onto the module record: one memoized parse instead
|
|
104
|
+
// of a per-rule import regex, and a configured specifier quoted in a string or
|
|
105
|
+
// a comment no longer expands.
|
|
106
|
+
return withSniff(specifierSniffTokens(Object.keys(rules)), (source, file) => {
|
|
107
|
+
if (!source.includes('import')) return source;
|
|
108
|
+
const edits: { start: number; end: number; value: string }[] = [];
|
|
109
|
+
for (const statement of rewriteFacts(file, source).imports) {
|
|
110
|
+
const rule = rules[statement.specifier];
|
|
111
|
+
if (!rule) continue;
|
|
112
|
+
const expanded = expandMembers(statement.bindings, rule);
|
|
113
|
+
if (expanded !== undefined) {
|
|
114
|
+
edits.push({ start: statement.start, end: statement.end, value: expanded });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return edits.length > 0 ? spliceSource(source, edits) : source;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// `new URL('./asset.png', import.meta.url)` asset references (COMPAT).
|
|
2
|
+
//
|
|
3
|
+
// Next/webpack rewrites this so it evaluates to the emitted client asset URL - the same URL an
|
|
4
|
+
// `import img from './asset.png'` produces - rather than a runtime file:// URL relative to the compiled
|
|
5
|
+
// bundle. Left intact, the literal resolves against the built bundle location, a path that never gets
|
|
6
|
+
// served.
|
|
7
|
+
//
|
|
8
|
+
// This reuses the static-image import pipeline: rewrite each matched call to `(<binding>.src)` and
|
|
9
|
+
// inject a top-level `import <binding> from '<spec>'`. That import flows through core's
|
|
10
|
+
// staticAssetModule seam, which emits the asset and yields the client URL. The `.src` string is what
|
|
11
|
+
// `.toString()` and `fileURLToPath()` (which then throws, as in Next's webpack path) observe.
|
|
12
|
+
//
|
|
13
|
+
// Applied to both graphs as a registered source transform.
|
|
14
|
+
|
|
15
|
+
import { rewriteReactProfilerServerSource } from './react-compiler';
|
|
16
|
+
|
|
17
|
+
// A `new URL('<image>', import.meta.url)` call whose first argument is a string
|
|
18
|
+
// literal ending in a static-image extension (optional ?query/#hash) and whose
|
|
19
|
+
// second argument is exactly `import.meta.url`. Non-asset `new URL(...)` calls
|
|
20
|
+
// (e.g. `new URL(request.url)`) never match.
|
|
21
|
+
const IMAGE_EXT = 'png|jpe?g|gif|webp|avif|svg|ico|bmp';
|
|
22
|
+
const newUrlAssetPattern = () =>
|
|
23
|
+
new RegExp(
|
|
24
|
+
`new\\s+URL\\(\\s*(['"])([^'"]+?\\.(?:${IMAGE_EXT})(?:[?#][^'"]*)?)\\1\\s*,\\s*import\\.meta\\.url\\s*\\)`,
|
|
25
|
+
'g',
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
/** Cheap gate: does the source contain any candidate `new URL(...import.meta.url)`? */
|
|
29
|
+
export function sourceHasNewUrlAsset(source: string): boolean {
|
|
30
|
+
return source.includes('import.meta.url') && newUrlAssetPattern().test(source);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Rewrite `new URL('<image>', import.meta.url)` occurrences to the emitted
|
|
35
|
+
* static-image URL by threading them through the static-asset import pipeline.
|
|
36
|
+
* Returns the source unchanged when there is nothing to rewrite.
|
|
37
|
+
*/
|
|
38
|
+
export function rewriteNewUrlAssetSource(source: string, filePath?: string): string {
|
|
39
|
+
const rewritten = rewriteReactProfilerServerSource(source, filePath);
|
|
40
|
+
return rewriteNewUrlAssets(
|
|
41
|
+
rewritten,
|
|
42
|
+
typeof filePath === 'string' && filePath.includes('middleware'),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Client-graph variant: no middleware form (a browser module never runs on the edge, and a client file
|
|
48
|
+
* merely living under an `app/middleware/...` path must not get the blob: rewrite) and no Profiler pass
|
|
49
|
+
* - that one is registered as its own client transform.
|
|
50
|
+
*/
|
|
51
|
+
export function rewriteNewUrlAssetClientSource(source: string): string {
|
|
52
|
+
return rewriteNewUrlAssets(source, false);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function rewriteNewUrlAssets(source: string, isMiddleware: boolean): string {
|
|
56
|
+
if (!source.includes('import.meta.url')) return source;
|
|
57
|
+
const bindings = new Map<string, string>();
|
|
58
|
+
const replaced = source.replace(newUrlAssetPattern(), (_match, _quote, spec: string) => {
|
|
59
|
+
let binding = bindings.get(spec);
|
|
60
|
+
if (!binding) {
|
|
61
|
+
binding = `__pnext_new_url_${bindings.size}`;
|
|
62
|
+
bindings.set(spec, binding);
|
|
63
|
+
}
|
|
64
|
+
if (isMiddleware) {
|
|
65
|
+
return `('blob:' + ${binding}.src.split('/').pop())`;
|
|
66
|
+
}
|
|
67
|
+
return `(${binding}.src)`;
|
|
68
|
+
});
|
|
69
|
+
if (bindings.size === 0) return source;
|
|
70
|
+
const imports = [...bindings]
|
|
71
|
+
.map(([spec, binding]) => `import ${binding} from ${JSON.stringify(spec)};`)
|
|
72
|
+
.join('\n');
|
|
73
|
+
return injectAfterDirectives(replaced, imports);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Insert injected imports after any leading directive prologue ('use client' /
|
|
77
|
+
// 'use server' / 'use strict') and optional shebang, so a client module's
|
|
78
|
+
// `'use client'` stays the first statement (otherwise the directive is demoted
|
|
79
|
+
// to a plain expression and the module is treated as server-only).
|
|
80
|
+
function injectAfterDirectives(source: string, injection: string): string {
|
|
81
|
+
const prologue = /^(\uFEFF?(?:#![^\n]*\n)?(?:[ \t]*(['"])use [\w-]+\2[ \t]*;?[ \t]*(?:\r?\n|$))*)/.exec(
|
|
82
|
+
source,
|
|
83
|
+
);
|
|
84
|
+
const index = prologue ? prologue[0].length : 0;
|
|
85
|
+
return `${source.slice(0, index)}${injection}\n${source.slice(index)}`;
|
|
86
|
+
}
|
|
87
|
+
|