@wular/pnext 0.0.3 → 0.0.6
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 +11 -11
- package/bin/pnext +1 -1
- package/config/lint/base.js +7 -7
- package/config/ts/base.json +2 -4
- package/config/ts/react.json +2 -6
- package/package.json +23 -2
- package/reference/compat.md +1 -1
- package/reference/config.md +2 -2
- package/reference/css.md +13 -9
- package/reference/dev.md +1 -1
- package/reference/metadata.md +7 -7
- package/reference/navigation.md +35 -28
- package/reference/performance.md +70 -70
- package/reference/rendering.md +17 -17
- package/reference/routing.md +17 -17
- package/reference/typegen.md +12 -8
- package/src/api/cache.ts +36 -37
- package/src/api/client-cache.ts +2 -2
- package/src/api/client-navigation.ts +113 -109
- package/src/api/dynamic.tsx +58 -55
- package/src/api/link.tsx +49 -52
- package/src/api/navigation.ts +59 -58
- package/src/api/server.ts +152 -144
- package/src/api/suspense.ts +4 -4
- package/src/cli/adapters/vercel-warm.ts +126 -121
- package/src/cli/adapters/vercel.ts +437 -443
- package/src/cli/analyze.ts +333 -144
- package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
- package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
- package/src/cli/build.ts +976 -978
- package/src/cli/create.ts +71 -63
- package/src/cli/dev.ts +116 -116
- package/src/cli/index.ts +88 -85
- package/src/cli/migrate/package-json.ts +73 -71
- package/src/cli/migrate/report.ts +32 -33
- package/src/cli/migrate/{index.ts → run.ts} +48 -49
- package/src/cli/migrate/scan.ts +46 -46
- package/src/cli/migrate/spinner.ts +9 -9
- package/src/cli/migrate/tsconfig.ts +35 -35
- package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
- package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
- package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
- package/src/cli/start.ts +65 -66
- package/src/{typegen.ts → cli/typegen.ts} +59 -59
- package/src/client/build.ts +794 -768
- package/src/client/chunk-fold.ts +245 -240
- package/src/client/{paths.ts → chunk-name.ts} +6 -6
- package/src/client/entry.ts +162 -147
- package/src/client/prebuilt.ts +231 -223
- package/src/client/profile.ts +29 -29
- package/src/client/react-compiler.ts +20 -15
- package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
- package/src/client/reference-stub.ts +51 -51
- package/src/client/reference.ts +19 -19
- package/src/{api → client}/router/events.ts +17 -17
- package/src/{api → client}/router/history.ts +16 -16
- package/src/{api → client}/router/hub.ts +52 -53
- package/src/{api/router.ts → client/router/index.ts} +61 -60
- package/src/{api → client}/router/policies.ts +24 -24
- package/src/{api → client}/router/runtime.ts +1988 -1961
- package/src/{api → client}/router/types.ts +93 -98
- package/src/compat/actions/client-plugin.ts +42 -43
- package/src/compat/actions/client-stub.ts +14 -14
- package/src/compat/actions/{action-client.ts → client.ts} +194 -193
- package/src/compat/actions/config.ts +63 -64
- package/src/compat/actions/detect.ts +68 -68
- package/src/compat/actions/discovery.ts +105 -105
- package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
- package/src/compat/actions/early-submit.ts +1 -1
- package/src/compat/actions/endpoint.ts +198 -198
- package/src/compat/actions/extensions.ts +811 -0
- package/src/compat/actions/flight.ts +23 -23
- package/src/compat/actions/form-state.ts +34 -34
- package/src/compat/actions/hoist.ts +382 -236
- package/src/compat/actions/ids.ts +7 -12
- package/src/compat/actions/index.ts +8 -8
- package/src/compat/actions/instances.ts +46 -46
- package/src/compat/actions/origin.ts +47 -48
- package/src/compat/actions/protocol.ts +22 -22
- package/src/compat/actions/registry.ts +19 -19
- package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
- package/src/compat/actions/rewrite.ts +125 -125
- package/src/compat/actions/{action-router.ts → router.ts} +10 -10
- package/src/compat/actions/serve.ts +132 -136
- package/src/compat/actions/server-tag.ts +4 -4
- package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
- package/src/compat/actions/unrecognized-error.ts +4 -4
- package/src/compat/{index.ts → aliases.ts} +112 -109
- package/src/compat/bundler/bun-externals.ts +18 -18
- package/src/compat/bundler/cjs-exports.ts +271 -223
- package/src/compat/bundler/config.ts +116 -111
- package/src/compat/bundler/externals.ts +12 -12
- package/src/compat/bundler/import-meta-url.ts +19 -19
- package/src/compat/bundler/modularize-imports.ts +36 -33
- package/src/compat/bundler/new-url-asset.ts +22 -24
- package/src/compat/bundler/optimize-package-imports.ts +111 -107
- package/src/compat/bundler/polyfill.ts +28 -28
- package/src/compat/bundler/react-compiler.ts +35 -29
- package/src/compat/bundler/react-profiler.tsx +11 -11
- package/src/compat/bundler/relay-transform.ts +48 -47
- package/src/compat/bundler/require-context.ts +119 -113
- package/src/compat/bundler/resolve-extensions.ts +19 -19
- package/src/compat/bundler/source-cache.ts +25 -25
- package/src/compat/bundler/static-imports.ts +7 -7
- package/src/compat/bundler/symlink-imports.ts +46 -46
- package/src/compat/bundler/tsconfig-paths.ts +13 -15
- package/src/compat/bundler/wasm.ts +58 -60
- package/src/compat/bundler/webpack-loaders.ts +254 -241
- package/src/compat/bundler/worker.ts +101 -104
- package/src/compat/cache/build-flags.ts +29 -29
- package/src/compat/cache/build-prerender-errors.ts +40 -44
- package/src/compat/cache/custom-handler.ts +60 -53
- package/src/compat/cache/fetch-patch.ts +240 -240
- package/src/compat/cache/handler.ts +27 -31
- package/src/compat/cache/modern-handler.ts +148 -126
- package/src/compat/cache/resume-data-cache.ts +47 -45
- package/src/compat/cache/revalidate.ts +233 -232
- package/src/compat/cache/runtime-error.ts +35 -35
- package/src/compat/cache/use-cache-transform.ts +442 -417
- package/src/compat/cache/use-cache.ts +650 -614
- package/src/compat/cache-control.ts +140 -142
- package/src/compat/client/base-path.ts +21 -20
- package/src/compat/client/css-order.ts +18 -18
- package/src/compat/client/errors/bare-boundary.ts +11 -11
- package/src/compat/client/errors/control-flow.ts +23 -23
- package/src/compat/client/errors/error-boundary.ts +61 -61
- package/src/compat/client/errors/global-error.ts +101 -91
- package/src/compat/client/errors/install.ts +71 -72
- package/src/compat/client/errors/lazy.ts +23 -23
- package/src/compat/client/errors/primitive-throw.ts +47 -45
- package/src/compat/client/errors/soft-refresh.ts +4 -4
- package/src/compat/client/link-status.ts +33 -33
- package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
- package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
- package/src/compat/client/navigation-scroll.ts +63 -63
- package/src/compat/client/optimistic-routing.ts +93 -88
- package/src/compat/client/prefetch-cache.ts +23 -24
- package/src/compat/client/route-announcer.ts +35 -35
- package/src/compat/client/segment-cache-policy.ts +20 -20
- package/src/compat/client/segment-cache.ts +309 -315
- package/src/compat/client/segment-prefetch.ts +127 -132
- package/src/compat/client/trailing-slash.ts +5 -4
- package/src/compat/css/chunking.ts +113 -116
- package/src/compat/css/inline-css.ts +21 -21
- package/src/compat/css/lightningcss.ts +37 -38
- package/src/compat/css/modules.ts +161 -175
- package/src/compat/css/nonce.ts +7 -7
- package/src/compat/css/sass-plugin.ts +18 -21
- package/src/compat/css/sass.ts +150 -152
- package/src/compat/css/styled-jsx-runtime.ts +27 -27
- package/src/compat/css/styled-jsx.ts +21 -21
- package/src/compat/edge-runtime.ts +27 -27
- package/src/compat/{adapter → export}/build-complete.ts +72 -75
- package/src/compat/export/client.ts +29 -31
- package/src/compat/export/{index.ts → emit.ts} +111 -110
- package/src/compat/export/standalone.ts +62 -54
- package/src/compat/image-optimizer/cache.ts +51 -49
- package/src/compat/image-optimizer/detect.ts +52 -52
- package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
- package/src/compat/image-optimizer/source.ts +77 -80
- package/src/compat/lifecycle/after-scope.ts +26 -26
- package/src/compat/lifecycle/after.ts +48 -45
- package/src/compat/lifecycle/error-funnel.ts +98 -102
- package/src/compat/lifecycle/error-serialize.ts +30 -26
- package/src/compat/lifecycle/error-ui.ts +67 -32
- package/src/compat/lifecycle/instrumentation-client.ts +36 -38
- package/src/compat/lifecycle/instrumentation.ts +85 -85
- package/src/compat/lifecycle/node-console.ts +11 -11
- package/src/compat/lifecycle/testmode.ts +160 -132
- package/src/compat/mdx/compile.ts +60 -57
- package/src/compat/mdx/plugin.ts +11 -11
- package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
- package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
- package/src/compat/metadata.ts +75 -81
- package/src/compat/next/cache.ts +68 -69
- package/src/compat/next/canonical-url.ts +9 -9
- package/src/compat/next/client-cache.ts +19 -19
- package/src/compat/next/client-navigation.ts +112 -116
- package/src/compat/next/client-only.ts +1 -1
- package/src/compat/next/client-script.tsx +99 -93
- package/src/compat/next/client-server.ts +10 -10
- package/src/compat/next/config-loader.ts +176 -173
- package/src/compat/next/config.ts +7 -7
- package/src/compat/next/constants.cjs +6 -6
- package/src/compat/next/constants.ts +6 -6
- package/src/compat/next/custom-server.ts +26 -24
- package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
- package/src/compat/next/dynamic.tsx +21 -18
- package/src/compat/next/error.tsx +52 -52
- package/src/compat/next/font/cache.ts +74 -74
- package/src/compat/next/font/google.ts +2 -2
- package/src/compat/next/font/index.ts +1 -1
- package/src/compat/next/font/local.ts +3 -3
- package/src/compat/next/font/runtime-client.ts +17 -15
- package/src/compat/next/font/runtime.ts +443 -408
- package/src/compat/next/font/shared.ts +120 -108
- package/src/compat/next/form.tsx +63 -63
- package/src/compat/next/head.tsx +2 -2
- package/src/compat/next/headers.ts +103 -95
- package/src/compat/next/image/client.tsx +220 -0
- package/src/compat/next/image/config.ts +56 -58
- package/src/compat/next/image/optimizer.ts +40 -36
- package/src/compat/next/image/patterns.ts +37 -40
- package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
- package/src/compat/next/image/shared.ts +65 -57
- package/src/compat/next/image/static-metadata.ts +98 -107
- package/src/compat/next/image/validate.ts +79 -88
- package/src/compat/next/image.tsx +19 -23
- package/src/compat/next/index.ts +1 -1
- package/src/compat/next/legacy-image.tsx +59 -60
- package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
- package/src/compat/next/link.tsx +158 -158
- package/src/compat/next/navigation.cjs +12 -3
- package/src/compat/next/navigation.ts +48 -50
- package/src/compat/next/offline.ts +27 -27
- package/src/compat/next/og.ts +121 -124
- package/src/compat/next/preferred-region.ts +13 -14
- package/src/compat/next/redirects.ts +58 -56
- package/src/compat/next/resource-hints.ts +73 -76
- package/src/compat/next/rewrites.ts +130 -133
- package/src/compat/next/root-params.ts +45 -45
- package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
- package/src/compat/next/router.cjs +4 -2
- package/src/compat/next/router.ts +58 -61
- package/src/compat/next/script.tsx +108 -108
- package/src/compat/next/server-only.ts +1 -1
- package/src/compat/next/server.ts +19 -19
- package/src/compat/next/svgr.ts +18 -17
- package/src/compat/next/telemetry.ts +24 -24
- package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
- package/src/compat/next/user-agent.ts +53 -49
- package/src/compat/next/web-vitals.ts +22 -24
- package/src/compat/otel/api.ts +41 -41
- package/src/compat/otel/client-trace-metadata.ts +25 -27
- package/src/compat/otel/fetch-span.ts +29 -29
- package/src/compat/otel/tracer.ts +331 -331
- package/src/compat/pages/api.ts +456 -0
- package/src/compat/pages/client-plugin.ts +36 -36
- package/src/compat/pages/router-state.ts +34 -34
- package/src/compat/pages/{index.ts → router.ts} +130 -135
- package/src/compat/ppr/io.ts +12 -12
- package/src/compat/ppr/missing-root-params.ts +34 -38
- package/src/compat/ppr/root-params-scan.ts +66 -66
- package/src/compat/ppr/root-params-transform.ts +24 -26
- package/src/compat/ppr/root-params.ts +30 -30
- package/src/compat/ppr/segment-config-incompat.ts +6 -7
- package/src/compat/protocol.ts +71 -70
- package/src/compat/react/action-state.ts +47 -48
- package/src/compat/react/client-lite.ts +15 -15
- package/src/compat/react/client.ts +4 -4
- package/src/compat/react/compiler-runtime.ts +11 -11
- package/src/compat/react/dom-client.ts +44 -44
- package/src/compat/react/dom-react-server.ts +10 -16
- package/src/compat/react/dom-server.ts +10 -10
- package/src/compat/react/dom.ts +52 -52
- package/src/compat/react/hooks-extra.ts +34 -35
- package/src/compat/react/parity.ts +64 -61
- package/src/compat/react/preact.ts +81 -82
- package/src/compat/react/react-server.ts +28 -28
- package/src/compat/react/router-shim.ts +1 -1
- package/src/compat/react/server-component-use.ts +8 -8
- package/src/compat/react/server-inserted-html.ts +30 -31
- package/src/compat/react/server.ts +53 -55
- package/src/compat/react/use.ts +32 -32
- package/src/compat/react/view-transition.ts +20 -20
- package/src/compat/register/actions.ts +35 -824
- package/src/compat/register/boot.ts +41 -41
- package/src/compat/register/build-tier.ts +5 -5
- package/src/compat/register/build.ts +74 -70
- package/src/compat/register/bundler.ts +161 -156
- package/src/compat/register/cache.ts +20 -20
- package/src/compat/register/client-errors.ts +3 -3
- package/src/compat/register/config.ts +6 -6
- package/src/compat/register/css-extras.ts +30 -34
- package/src/compat/register/edge-runtime.ts +3 -3
- package/src/compat/register/errors.ts +10 -12
- package/src/compat/register/export.ts +16 -16
- package/src/compat/register/font.ts +11 -11
- package/src/compat/register/hooks.ts +2 -2
- package/src/compat/register/image.ts +40 -40
- package/src/compat/register/index.ts +59 -62
- package/src/compat/register/instrumentation-client.ts +10 -10
- package/src/compat/register/lifecycle.ts +25 -28
- package/src/compat/register/mdx.ts +10 -10
- package/src/compat/register/middleware.ts +226 -16
- package/src/compat/register/otel.ts +80 -88
- package/src/compat/register/pages-api.ts +10 -463
- package/src/compat/register/ppr.ts +16 -16
- package/src/compat/register/protocol.ts +17 -18
- package/src/compat/register/proxy.ts +49 -51
- package/src/compat/register/render.ts +79 -76
- package/src/compat/register/routing.ts +162 -159
- package/src/compat/register/segment.ts +24 -1887
- package/src/compat/register/static-image.ts +3 -3
- package/src/compat/register/{misc.ts → taint.ts} +9 -9
- package/src/compat/register/typed-routes.ts +14 -14
- package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
- package/src/compat/register/validation.ts +18 -18
- package/src/compat/segment/loading-boundary.ts +43 -45
- package/src/compat/segment/page-slot.ts +69 -69
- package/src/compat/segment/serve.ts +1884 -0
- package/src/compat/segment/tree.ts +113 -112
- package/src/compat/segment/vary-key.ts +38 -38
- package/src/compat/segment/vary-params.ts +138 -142
- package/src/compat/static-params.ts +14 -12
- package/src/compat/tsconfig-defaults.ts +87 -91
- package/src/compat/typecheck/{index.ts → check.ts} +234 -212
- package/src/compat/typecheck/worker.ts +15 -12
- package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
- package/src/compat/typed-routes/manifest.ts +174 -170
- package/src/compat/typed-routes/typegen.ts +127 -110
- package/src/compat/validation/errors.ts +16 -19
- package/src/compat/validation/prerender-diagnostics.ts +521 -504
- package/src/compat/validation/{index.ts → validate.ts} +647 -648
- package/src/compat-bootstrap.ts +16 -16
- package/src/config.ts +69 -69
- package/src/css/build.ts +230 -227
- package/src/css/postcss.ts +79 -80
- package/src/css/worker.ts +14 -15
- package/src/dev/client-actions.ts +10 -10
- package/src/dev/client-chunk-store.ts +27 -27
- package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
- package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
- package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
- package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
- package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
- package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
- package/src/dev/server.ts +804 -820
- package/src/env.ts +46 -43
- package/src/extensions.ts +491 -478
- package/src/index.ts +8 -8
- package/src/internal.ts +20 -23
- package/src/{islands → render}/boundary-error.ts +3 -3
- package/src/render/hooks.ts +71 -71
- package/src/render/island-context.ts +14 -14
- package/src/render/metadata.ts +310 -310
- package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
- package/src/{ppr.ts → render/ppr.ts} +244 -245
- package/src/render/renderer.ts +2076 -2082
- package/src/render/resource-hints.ts +16 -17
- package/src/render/slots.tsx +224 -235
- package/src/{islands → render}/static-children.ts +9 -12
- package/src/{islands → render}/static-slots.ts +37 -37
- package/src/{cache/context.ts → request/cache.ts} +20 -20
- package/src/request/context.ts +107 -107
- package/src/{dynamic/source.ts → resolve/dynamic.ts} +152 -143
- package/src/resolve/engine.ts +90 -77
- package/src/resolve/imports.ts +475 -463
- package/src/resolve/scan-facts.ts +442 -186
- package/src/resolve/source-text.ts +37 -37
- package/src/{dynamic → resolve}/tree-shake.ts +132 -128
- package/src/routing/forwarded.ts +19 -19
- package/src/routing/handler.ts +84 -91
- package/src/routing/href.ts +69 -70
- package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
- package/src/{proxy.ts → routing/proxy.ts} +306 -312
- package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
- package/src/routing/routes.ts +833 -848
- package/src/routing/slots.ts +164 -160
- package/src/runtime/loader.ts +952 -0
- package/src/{dev → runtime}/module-cache.ts +309 -287
- package/src/{dev → runtime}/module-generations.ts +9 -9
- package/src/{dev → runtime}/module-transform.ts +81 -72
- package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
- package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
- package/src/runtime/vendor.ts +425 -404
- package/src/styles.d.ts +9 -0
- package/src/types.ts +320 -335
- package/src/utils/ansi.ts +5 -5
- package/src/utils/{source.ts → code.ts} +15 -12
- package/src/utils/content-type.ts +3 -3
- package/src/utils/decode.ts +2 -2
- package/src/utils/dev-profile.ts +13 -13
- package/src/utils/error-log.ts +6 -6
- package/src/utils/esbuild.ts +18 -18
- package/src/utils/fs-cache.ts +13 -13
- package/src/utils/fs.ts +57 -49
- package/src/utils/html.ts +20 -24
- package/src/utils/native-require.ts +8 -8
- package/src/utils/serialize.ts +139 -146
- package/src/utils/verbose.ts +18 -18
- package/src/cli/analyze-print.ts +0 -181
- package/src/compat/middleware/manifest.ts +0 -210
- package/src/compat/next/image-client.tsx +0 -215
- package/src/compat/next/link-usage.ts +0 -29
- package/src/css/index.ts +0 -2
- package/src/render/index.ts +0 -1
- package/src/style-modules.d.ts +0 -9
|
@@ -13,50 +13,50 @@
|
|
|
13
13
|
// Lazy: the oxc-parser native binding costs ~12.6 MB RSS; load it only when a parse happens.
|
|
14
14
|
const parseSync: typeof import('oxc-parser').parseSync = (...args) =>
|
|
15
15
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
16
|
-
loadNative(() => require('oxc-parser') as typeof import('oxc-parser')).parseSync(...args)
|
|
17
|
-
import { loadNative } from '../utils/native-require'
|
|
18
|
-
import { spliceSource } from '../
|
|
16
|
+
loadNative(() => require('oxc-parser') as typeof import('oxc-parser')).parseSync(...args)
|
|
17
|
+
import { loadNative } from '../utils/native-require'
|
|
18
|
+
import { spliceSource } from '../runtime/module-transform'
|
|
19
19
|
|
|
20
20
|
export interface ScanEdge {
|
|
21
|
-
specifier: string
|
|
21
|
+
specifier: string
|
|
22
22
|
/** Bound names: `default`, `*` (namespace object), or the imported names. */
|
|
23
|
-
exports: string[]
|
|
23
|
+
exports: string[]
|
|
24
24
|
/** Byte offset of the statement — edges are consumed in true source order. */
|
|
25
|
-
index: number
|
|
25
|
+
index: number
|
|
26
26
|
/** `export * from`: binds every named export of the target, none of them known here. */
|
|
27
|
-
star?: boolean
|
|
27
|
+
star?: boolean
|
|
28
28
|
/** `export … from` rather than `import`. */
|
|
29
|
-
reexport?: boolean
|
|
29
|
+
reexport?: boolean
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/** One module specifier literal, with the span of its quoted text. */
|
|
33
33
|
export interface SpecifierEdge {
|
|
34
|
-
specifier: string
|
|
34
|
+
specifier: string
|
|
35
35
|
/** Span of the quoted literal, quotes included. */
|
|
36
|
-
start: number
|
|
37
|
-
end: number
|
|
36
|
+
start: number
|
|
37
|
+
end: number
|
|
38
38
|
/** `side-effect` is `import 'x'`: no clause, so no `from` for a textual pass to key on. */
|
|
39
|
-
kind: 'import' | 'side-effect' | 'export' | 'dynamic' | 'require'
|
|
39
|
+
kind: 'import' | 'side-effect' | 'export' | 'dynamic' | 'require'
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export interface ImportBinding {
|
|
43
43
|
/** Name in the target module; `default`/`*` for the default/namespace forms. */
|
|
44
|
-
imported: string
|
|
45
|
-
local: string
|
|
44
|
+
imported: string
|
|
45
|
+
local: string
|
|
46
46
|
/** `import type …` / `{ type X }`: erased, but part of the statement's text. */
|
|
47
|
-
type: boolean
|
|
47
|
+
type: boolean
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
/** A static `import` statement, spans included — the unit the folded passes splice. */
|
|
51
51
|
export interface ImportStatement {
|
|
52
|
-
specifier: string
|
|
52
|
+
specifier: string
|
|
53
53
|
/** Statement span (`import` … `;`). */
|
|
54
|
-
start: number
|
|
55
|
-
end: number
|
|
54
|
+
start: number
|
|
55
|
+
end: number
|
|
56
56
|
/** Span of the quoted specifier literal inside the statement. */
|
|
57
|
-
specifierStart: number
|
|
58
|
-
specifierEnd: number
|
|
59
|
-
bindings: ImportBinding[]
|
|
57
|
+
specifierStart: number
|
|
58
|
+
specifierEnd: number
|
|
59
|
+
bindings: ImportBinding[]
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -65,32 +65,61 @@ export interface ImportStatement {
|
|
|
65
65
|
* parse, so a folded pass on an already-scanned source costs nothing beyond the lookup.
|
|
66
66
|
*/
|
|
67
67
|
export interface RewriteFacts {
|
|
68
|
-
edges: SpecifierEdge[]
|
|
69
|
-
imports: ImportStatement[]
|
|
70
|
-
importMetas: { start: number; end: number }[]
|
|
68
|
+
edges: SpecifierEdge[]
|
|
69
|
+
imports: ImportStatement[]
|
|
70
|
+
importMetas: { start: number; end: number }[]
|
|
71
71
|
/**
|
|
72
72
|
* The parse recovered from errors, so the record may be incomplete — folded
|
|
73
73
|
* passes fall back to their textual form rather than silently under-rewrite.
|
|
74
74
|
*/
|
|
75
|
-
unreliable: boolean
|
|
75
|
+
unreliable: boolean
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A `name(loader, options?)` call whose loader statically targets one module —
|
|
80
|
+
* the shape `dynamic()` extraction needs. Emitted for every identifier-callee
|
|
81
|
+
* call with such a loader; consumers gate on their own `dynamic` binding set.
|
|
82
|
+
*/
|
|
83
|
+
export interface DynamicCallFact {
|
|
84
|
+
name: string
|
|
85
|
+
/** Callee identifier start — aligns with the textual call scan's index. */
|
|
86
|
+
start: number
|
|
87
|
+
/** Call end (past the closing paren). */
|
|
88
|
+
end: number
|
|
89
|
+
/** Loader target, cooked value. */
|
|
90
|
+
specifier: string
|
|
91
|
+
/** Span of the quoted specifier literal, quotes included. */
|
|
92
|
+
specifierStart: number
|
|
93
|
+
specifierEnd: number
|
|
94
|
+
/** From the `.then(m => m.X)` arm (or awaited member); `default` otherwise. */
|
|
95
|
+
exportName: string
|
|
96
|
+
/** First arg is a string literal (`dynamic('./x')` form), not a loader fn. */
|
|
97
|
+
literal?: boolean
|
|
98
|
+
/** End of the loader argument — options/appended args follow it. */
|
|
99
|
+
loaderEnd: number
|
|
100
|
+
/** Second-argument span, when present. */
|
|
101
|
+
optionsStart?: number
|
|
102
|
+
optionsEnd?: number
|
|
76
103
|
}
|
|
77
104
|
|
|
78
105
|
export interface ScanFacts {
|
|
79
106
|
/** Static value imports and `export … from` re-exports, in source order. */
|
|
80
|
-
imports: ScanEdge[]
|
|
107
|
+
imports: ScanEdge[]
|
|
81
108
|
/** `import('literal')` expressions, in source order. */
|
|
82
|
-
dynamicImports: { specifier: string; index: number }[]
|
|
109
|
+
dynamicImports: { specifier: string; index: number }[]
|
|
83
110
|
/** `require('literal')` calls — CJS sources still reach these scanners. */
|
|
84
|
-
requires: { specifier: string; index: number; specifierStart: number }[]
|
|
111
|
+
requires: { specifier: string; index: number; specifierStart: number }[]
|
|
85
112
|
/** This module's own value export names, `default` included. */
|
|
86
|
-
exportNames: string[]
|
|
113
|
+
exportNames: string[]
|
|
87
114
|
/** Specifiers of `export * from` (whose names live in the target). */
|
|
88
|
-
exportStars: string[]
|
|
115
|
+
exportStars: string[]
|
|
89
116
|
/** A real `'use client'` directive — prologue position only, never a comment. */
|
|
90
|
-
useClient: boolean
|
|
117
|
+
useClient: boolean
|
|
118
|
+
/** Loader-shaped calls (see `DynamicCallFact`); only sources that can bind `dynamic` are walked. */
|
|
119
|
+
dynamicCalls: DynamicCallFact[]
|
|
91
120
|
}
|
|
92
121
|
|
|
93
|
-
const stylesheetOrData = /\.(?:css|scss|sass|less|styl|json)
|
|
122
|
+
const stylesheetOrData = /\.(?:css|scss|sass|less|styl|json)$/
|
|
94
123
|
|
|
95
124
|
const emptyFacts: ScanFacts = {
|
|
96
125
|
imports: [],
|
|
@@ -99,62 +128,72 @@ const emptyFacts: ScanFacts = {
|
|
|
99
128
|
exportNames: [],
|
|
100
129
|
exportStars: [],
|
|
101
130
|
useClient: false,
|
|
102
|
-
|
|
131
|
+
dynamicCalls: [],
|
|
132
|
+
}
|
|
103
133
|
|
|
104
134
|
const emptyRewriteFacts: RewriteFacts = {
|
|
105
135
|
edges: [],
|
|
106
136
|
imports: [],
|
|
107
137
|
importMetas: [],
|
|
108
138
|
unreliable: false,
|
|
109
|
-
}
|
|
139
|
+
}
|
|
110
140
|
|
|
111
|
-
const memo = new Map<string, { scan: ScanFacts; rewrite: RewriteFacts }>()
|
|
112
|
-
let hits = 0
|
|
113
|
-
let misses = 0
|
|
141
|
+
const memo = new Map<string, { scan: ScanFacts; rewrite: RewriteFacts }>()
|
|
142
|
+
let hits = 0
|
|
143
|
+
let misses = 0
|
|
114
144
|
|
|
115
145
|
export function scanFacts(file: string, source: string): ScanFacts {
|
|
116
146
|
// The module walk reaches stylesheets/data as ordinary edges; they hold no
|
|
117
147
|
// module facts and are not parseable as script.
|
|
118
|
-
if (stylesheetOrData.test(file)) return emptyFacts
|
|
119
|
-
return parsed(file, source).scan
|
|
148
|
+
if (stylesheetOrData.test(file)) return emptyFacts
|
|
149
|
+
return parsed(file, source).scan
|
|
120
150
|
}
|
|
121
151
|
|
|
122
152
|
/** Every specifier a module pulls in — static, dynamic and `require`, in source order. */
|
|
123
153
|
export function importSpecifiers(source: string, sourcefile = 'source') {
|
|
124
|
-
const facts = scanFacts(sourcefile, source)
|
|
154
|
+
const facts = scanFacts(sourcefile, source)
|
|
125
155
|
return [
|
|
126
156
|
...facts.imports.map(edge => edge.specifier),
|
|
127
157
|
...facts.dynamicImports.map(entry => entry.specifier),
|
|
128
158
|
...facts.requires.map(entry => entry.specifier),
|
|
129
|
-
]
|
|
159
|
+
]
|
|
130
160
|
}
|
|
131
161
|
|
|
132
162
|
export function moduleExportNames(source: string, sourcefile = 'source.tsx') {
|
|
133
|
-
return scanFacts(sourcefile, source).exportNames
|
|
163
|
+
return scanFacts(sourcefile, source).exportNames
|
|
134
164
|
}
|
|
135
165
|
|
|
136
166
|
/** Specifiers of `export * from` — their names live in the target module. */
|
|
137
167
|
export function moduleExportStars(source: string, sourcefile = 'source.tsx') {
|
|
138
|
-
return scanFacts(sourcefile, source).exportStars
|
|
168
|
+
return scanFacts(sourcefile, source).exportStars
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* AST-derived `dynamic()` loader facts — the single source of truth. A call
|
|
173
|
+
* without a fact is an unanalyzable loader: no detection, nothing to splice.
|
|
174
|
+
*/
|
|
175
|
+
export function dynamicCallFacts(source: string, file = 'module.tsx'): DynamicCallFact[] {
|
|
176
|
+
if (stylesheetOrData.test(file)) return []
|
|
177
|
+
return parsed(file, source).scan.dynamicCalls
|
|
139
178
|
}
|
|
140
179
|
|
|
141
180
|
/** Module-record spans for the rewrite passes (see `RewriteFacts`). */
|
|
142
181
|
export function rewriteFacts(file: string, source: string): RewriteFacts {
|
|
143
|
-
if (stylesheetOrData.test(file)) return emptyRewriteFacts
|
|
144
|
-
return parsed(file, source).rewrite
|
|
182
|
+
if (stylesheetOrData.test(file)) return emptyRewriteFacts
|
|
183
|
+
return parsed(file, source).rewrite
|
|
145
184
|
}
|
|
146
185
|
|
|
147
186
|
function parsed(file: string, source: string, lang = langForFile(file)) {
|
|
148
|
-
const key = `${lang}\0${source.length}\0${Bun.hash(source).toString()}
|
|
149
|
-
const cached = memo.get(key)
|
|
187
|
+
const key = `${lang}\0${source.length}\0${Bun.hash(source).toString()}`
|
|
188
|
+
const cached = memo.get(key)
|
|
150
189
|
if (cached) {
|
|
151
|
-
hits += 1
|
|
152
|
-
return cached
|
|
190
|
+
hits += 1
|
|
191
|
+
return cached
|
|
153
192
|
}
|
|
154
|
-
misses += 1
|
|
155
|
-
const facts = parseFacts(file, source, lang)
|
|
156
|
-
memo.set(key, facts)
|
|
157
|
-
return facts
|
|
193
|
+
misses += 1
|
|
194
|
+
const facts = parseFacts(file, source, lang)
|
|
195
|
+
memo.set(key, facts)
|
|
196
|
+
return facts
|
|
158
197
|
}
|
|
159
198
|
|
|
160
199
|
/**
|
|
@@ -168,68 +207,76 @@ export function rewriteSpecifierLiterals(
|
|
|
168
207
|
file: string,
|
|
169
208
|
replace: (specifier: string, kind: SpecifierEdge['kind']) => string | undefined,
|
|
170
209
|
): string {
|
|
171
|
-
const facts = rewriteFacts(file, source)
|
|
210
|
+
const facts = rewriteFacts(file, source)
|
|
172
211
|
// A partially recovered parse has a partial record; the textual scan the
|
|
173
212
|
// folded passes replaced is the safe answer there.
|
|
174
|
-
const edges = facts.unreliable ? textualEdges(source) : facts.edges
|
|
175
|
-
const edits: { start: number; end: number; value: string }[] = []
|
|
213
|
+
const edges = facts.unreliable ? textualEdges(source) : facts.edges
|
|
214
|
+
const edits: { start: number; end: number; value: string }[] = []
|
|
176
215
|
for (const edge of edges) {
|
|
177
|
-
const value = replace(edge.specifier, edge.kind)
|
|
178
|
-
if (value !== undefined) edits.push({ start: edge.start, end: edge.end, value })
|
|
216
|
+
const value = replace(edge.specifier, edge.kind)
|
|
217
|
+
if (value !== undefined) edits.push({ start: edge.start, end: edge.end, value })
|
|
179
218
|
}
|
|
180
|
-
return edits.length > 0 ? spliceSource(source, edits) : source
|
|
219
|
+
return edits.length > 0 ? spliceSource(source, edits) : source
|
|
181
220
|
}
|
|
182
221
|
|
|
183
|
-
const textualSpecifier = /(\bfrom\s*|\b(?:import|require)\s*\(\s*)(['"])([^'"]+)\2/g
|
|
222
|
+
const textualSpecifier = /(\bfrom\s*|\b(?:import|require)\s*\(\s*)(['"])([^'"]+)\2/g
|
|
184
223
|
|
|
185
224
|
function textualEdges(source: string): SpecifierEdge[] {
|
|
186
|
-
const edges: SpecifierEdge[] = []
|
|
187
|
-
textualSpecifier.lastIndex = 0
|
|
188
|
-
let match: RegExpExecArray | null
|
|
225
|
+
const edges: SpecifierEdge[] = []
|
|
226
|
+
textualSpecifier.lastIndex = 0
|
|
227
|
+
let match: RegExpExecArray | null
|
|
189
228
|
while ((match = textualSpecifier.exec(source))) {
|
|
190
|
-
const start = match.index + match[1]!.length
|
|
229
|
+
const start = match.index + match[1]!.length
|
|
191
230
|
edges.push({
|
|
192
231
|
specifier: match[3]!,
|
|
193
232
|
start,
|
|
194
233
|
end: start + match[3]!.length + 2,
|
|
195
|
-
kind: match[1]!.includes('require')
|
|
196
|
-
|
|
234
|
+
kind: match[1]!.includes('require')
|
|
235
|
+
? 'require'
|
|
236
|
+
: match[1]!.includes('import')
|
|
237
|
+
? 'dynamic'
|
|
238
|
+
: 'import',
|
|
239
|
+
})
|
|
197
240
|
}
|
|
198
|
-
return edges
|
|
241
|
+
return edges
|
|
199
242
|
}
|
|
200
243
|
|
|
201
244
|
/** Memo effectiveness, for the dev-boot benches. */
|
|
202
245
|
export function scanFactsStats() {
|
|
203
|
-
return { hits, misses, sources: memo.size }
|
|
246
|
+
return { hits, misses, sources: memo.size }
|
|
204
247
|
}
|
|
205
248
|
|
|
206
249
|
function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
207
250
|
// Parse errors are recovered, never thrown: a scan of a half-typed file in
|
|
208
251
|
// the dev watcher must still yield whatever the parser did understand.
|
|
209
|
-
const result = parseSync(file, source, { lang })
|
|
210
|
-
const imports: ScanEdge[] = []
|
|
211
|
-
const exportNames: string[] = []
|
|
212
|
-
const exportStars: string[] = []
|
|
213
|
-
const edges: SpecifierEdge[] = []
|
|
214
|
-
const importStatements: ImportStatement[] = []
|
|
252
|
+
const result = parseSync(file, source, { lang })
|
|
253
|
+
const imports: ScanEdge[] = []
|
|
254
|
+
const exportNames: string[] = []
|
|
255
|
+
const exportStars: string[] = []
|
|
256
|
+
const edges: SpecifierEdge[] = []
|
|
257
|
+
const importStatements: ImportStatement[] = []
|
|
215
258
|
|
|
216
259
|
for (const statement of result.module.staticImports) {
|
|
217
|
-
const names: string[] = []
|
|
218
|
-
const bindings: ImportBinding[] = []
|
|
219
|
-
let bindsValue = statement.entries.length === 0
|
|
260
|
+
const names: string[] = []
|
|
261
|
+
const bindings: ImportBinding[] = []
|
|
262
|
+
let bindsValue = statement.entries.length === 0
|
|
220
263
|
for (const entry of statement.entries) {
|
|
221
|
-
const kind: string = entry.importName.kind
|
|
264
|
+
const kind: string = entry.importName.kind
|
|
222
265
|
bindings.push({
|
|
223
266
|
imported:
|
|
224
|
-
kind === 'Default'
|
|
267
|
+
kind === 'Default'
|
|
268
|
+
? 'default'
|
|
269
|
+
: kind === 'NamespaceObject'
|
|
270
|
+
? '*'
|
|
271
|
+
: (entry.importName.name ?? ''),
|
|
225
272
|
local: entry.localName.value ?? '',
|
|
226
273
|
type: entry.isType,
|
|
227
|
-
})
|
|
228
|
-
if (entry.isType) continue
|
|
229
|
-
bindsValue = true
|
|
230
|
-
if (kind === 'Default') names.push('default')
|
|
231
|
-
else if (kind === 'NamespaceObject') names.push('*')
|
|
232
|
-
else if (entry.importName.name) names.push(entry.importName.name)
|
|
274
|
+
})
|
|
275
|
+
if (entry.isType) continue
|
|
276
|
+
bindsValue = true
|
|
277
|
+
if (kind === 'Default') names.push('default')
|
|
278
|
+
else if (kind === 'NamespaceObject') names.push('*')
|
|
279
|
+
else if (entry.importName.name) names.push(entry.importName.name)
|
|
233
280
|
}
|
|
234
281
|
importStatements.push({
|
|
235
282
|
specifier: statement.moduleRequest.value,
|
|
@@ -238,86 +285,91 @@ function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
|
238
285
|
specifierStart: statement.moduleRequest.start,
|
|
239
286
|
specifierEnd: statement.moduleRequest.end,
|
|
240
287
|
bindings,
|
|
241
|
-
})
|
|
288
|
+
})
|
|
242
289
|
edges.push({
|
|
243
290
|
specifier: statement.moduleRequest.value,
|
|
244
291
|
start: statement.moduleRequest.start,
|
|
245
292
|
end: statement.moduleRequest.end,
|
|
246
293
|
kind: statement.entries.length === 0 ? 'side-effect' : 'import',
|
|
247
|
-
})
|
|
294
|
+
})
|
|
248
295
|
// `import type { … }` binds nothing at runtime: not an edge.
|
|
249
|
-
if (!bindsValue) continue
|
|
296
|
+
if (!bindsValue) continue
|
|
250
297
|
imports.push({
|
|
251
298
|
specifier: statement.moduleRequest.value,
|
|
252
299
|
exports: names,
|
|
253
300
|
index: statement.start,
|
|
254
|
-
})
|
|
301
|
+
})
|
|
255
302
|
}
|
|
256
303
|
|
|
257
304
|
// One `export { a, b } from 'x'` reports the same moduleRequest per entry.
|
|
258
|
-
const seenExportRequests = new Set<number>()
|
|
305
|
+
const seenExportRequests = new Set<number>()
|
|
259
306
|
for (const statement of result.module.staticExports) {
|
|
260
307
|
for (const entry of statement.entries) {
|
|
261
|
-
const request = entry.moduleRequest
|
|
262
|
-
if (!request || seenExportRequests.has(request.start)) continue
|
|
263
|
-
seenExportRequests.add(request.start)
|
|
264
|
-
edges.push({
|
|
308
|
+
const request = entry.moduleRequest
|
|
309
|
+
if (!request || seenExportRequests.has(request.start)) continue
|
|
310
|
+
seenExportRequests.add(request.start)
|
|
311
|
+
edges.push({
|
|
312
|
+
specifier: request.value,
|
|
313
|
+
start: request.start,
|
|
314
|
+
end: request.end,
|
|
315
|
+
kind: 'export',
|
|
316
|
+
})
|
|
265
317
|
}
|
|
266
318
|
}
|
|
267
319
|
|
|
268
320
|
for (const statement of result.module.staticExports) {
|
|
269
|
-
const edges = new Map<string, ScanEdge>()
|
|
321
|
+
const edges = new Map<string, ScanEdge>()
|
|
270
322
|
for (const entry of statement.entries) {
|
|
271
|
-
if (entry.isType) continue
|
|
272
|
-
const exportKind: string = entry.exportName.kind
|
|
273
|
-
if (exportKind === 'Default') exportNames.push('default')
|
|
274
|
-
else if (entry.exportName.name) exportNames.push(entry.exportName.name)
|
|
275
|
-
|
|
276
|
-
const specifier = entry.moduleRequest?.value
|
|
277
|
-
if (!specifier) continue
|
|
278
|
-
const importKind: string = entry.importName.kind
|
|
279
|
-
const star = importKind === 'AllButDefault'
|
|
280
|
-
if (star) exportStars.push(specifier)
|
|
281
|
-
let edge = edges.get(specifier)
|
|
323
|
+
if (entry.isType) continue
|
|
324
|
+
const exportKind: string = entry.exportName.kind
|
|
325
|
+
if (exportKind === 'Default') exportNames.push('default')
|
|
326
|
+
else if (entry.exportName.name) exportNames.push(entry.exportName.name)
|
|
327
|
+
|
|
328
|
+
const specifier = entry.moduleRequest?.value
|
|
329
|
+
if (!specifier) continue
|
|
330
|
+
const importKind: string = entry.importName.kind
|
|
331
|
+
const star = importKind === 'AllButDefault'
|
|
332
|
+
if (star) exportStars.push(specifier)
|
|
333
|
+
let edge = edges.get(specifier)
|
|
282
334
|
if (!edge) {
|
|
283
|
-
edge = { specifier, exports: [], index: statement.start, reexport: true }
|
|
284
|
-
edges.set(specifier, edge)
|
|
285
|
-
imports.push(edge)
|
|
335
|
+
edge = { specifier, exports: [], index: statement.start, reexport: true }
|
|
336
|
+
edges.set(specifier, edge)
|
|
337
|
+
imports.push(edge)
|
|
286
338
|
}
|
|
287
|
-
if (star) edge.star = true
|
|
288
|
-
else if (importKind === 'All') edge.exports.push('*')
|
|
289
|
-
else if (entry.importName.name) edge.exports.push(entry.importName.name)
|
|
339
|
+
if (star) edge.star = true
|
|
340
|
+
else if (importKind === 'All') edge.exports.push('*')
|
|
341
|
+
else if (entry.importName.name) edge.exports.push(entry.importName.name)
|
|
290
342
|
}
|
|
291
343
|
}
|
|
292
344
|
|
|
293
|
-
imports.sort((a, b) => a.index - b.index)
|
|
345
|
+
imports.sort((a, b) => a.index - b.index)
|
|
294
346
|
|
|
295
|
-
const dynamicImports: { specifier: string; index: number }[] = []
|
|
347
|
+
const dynamicImports: { specifier: string; index: number }[] = []
|
|
296
348
|
for (const entry of result.module.dynamicImports) {
|
|
297
|
-
const raw = source.slice(entry.moduleRequest.start, entry.moduleRequest.end)
|
|
298
|
-
const specifier = literalSpecifier(raw)
|
|
299
|
-
if (specifier === undefined) continue
|
|
300
|
-
dynamicImports.push({ specifier, index: entry.start })
|
|
349
|
+
const raw = source.slice(entry.moduleRequest.start, entry.moduleRequest.end)
|
|
350
|
+
const specifier = literalSpecifier(raw)
|
|
351
|
+
if (specifier === undefined) continue
|
|
352
|
+
dynamicImports.push({ specifier, index: entry.start })
|
|
301
353
|
// The literal's own span, without the whitespace `moduleRequest` includes.
|
|
302
|
-
const lead = raw.length - raw.trimStart().length
|
|
354
|
+
const lead = raw.length - raw.trimStart().length
|
|
303
355
|
edges.push({
|
|
304
356
|
specifier,
|
|
305
357
|
start: entry.moduleRequest.start + lead,
|
|
306
358
|
end: entry.moduleRequest.start + lead + specifier.length + 2,
|
|
307
359
|
kind: 'dynamic',
|
|
308
|
-
})
|
|
360
|
+
})
|
|
309
361
|
}
|
|
310
362
|
|
|
311
|
-
const requires = requireCalls(source, result.comments)
|
|
363
|
+
const requires = requireCalls(source, result.comments)
|
|
312
364
|
for (const call of requires) {
|
|
313
365
|
edges.push({
|
|
314
366
|
specifier: call.specifier,
|
|
315
367
|
start: call.specifierStart,
|
|
316
368
|
end: call.specifierStart + call.specifier.length + 2,
|
|
317
369
|
kind: 'require',
|
|
318
|
-
})
|
|
370
|
+
})
|
|
319
371
|
}
|
|
320
|
-
edges.sort((a, b) => a.start - b.start)
|
|
372
|
+
edges.sort((a, b) => a.start - b.start)
|
|
321
373
|
|
|
322
374
|
return {
|
|
323
375
|
scan: {
|
|
@@ -327,6 +379,12 @@ function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
|
327
379
|
exportNames,
|
|
328
380
|
exportStars,
|
|
329
381
|
useClient: hasUseClient(source, result),
|
|
382
|
+
// `result.program` deserializes the whole AST: walk it only for sources
|
|
383
|
+
// that can bind `dynamic()` at all (same cheap gate as resolve/dynamic.ts).
|
|
384
|
+
dynamicCalls:
|
|
385
|
+
source.includes('@wular/pnext') || source.includes('next/dynamic')
|
|
386
|
+
? collectDynamicCalls(result.program as unknown as AstNode)
|
|
387
|
+
: [],
|
|
330
388
|
},
|
|
331
389
|
rewrite: {
|
|
332
390
|
edges,
|
|
@@ -334,7 +392,7 @@ function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
|
334
392
|
importMetas: result.module.importMetas.map(meta => ({ start: meta.start, end: meta.end })),
|
|
335
393
|
unreliable: result.errors.length > 0,
|
|
336
394
|
},
|
|
337
|
-
}
|
|
395
|
+
}
|
|
338
396
|
}
|
|
339
397
|
|
|
340
398
|
/**
|
|
@@ -343,23 +401,23 @@ function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
|
343
401
|
* consulted for the prologue shapes the scanner will not commit to.
|
|
344
402
|
*/
|
|
345
403
|
function hasUseClient(source: string, result: { program: { body: readonly Directive[] } }) {
|
|
346
|
-
const sniffed = sniffDirective(source, 'use client')
|
|
347
|
-
if (sniffed !== undefined) return sniffed
|
|
404
|
+
const sniffed = sniffDirective(source, 'use client')
|
|
405
|
+
if (sniffed !== undefined) return sniffed
|
|
348
406
|
// Uncertain, but a directive is its raw text: no occurrence, no directive.
|
|
349
|
-
if (!source.includes('use client')) return false
|
|
350
|
-
return hasDirective(result.program.body, 'use client')
|
|
407
|
+
if (!source.includes('use client')) return false
|
|
408
|
+
return hasDirective(result.program.body, 'use client')
|
|
351
409
|
}
|
|
352
410
|
|
|
353
411
|
interface Directive {
|
|
354
|
-
type: string
|
|
355
|
-
directive?: string | null
|
|
412
|
+
type: string
|
|
413
|
+
directive?: string | null
|
|
356
414
|
}
|
|
357
415
|
|
|
358
416
|
// A string literal is only a directive if what follows it ends the statement; a
|
|
359
417
|
// continuation token means it was an expression (`'use client' + x`).
|
|
360
|
-
const continuesExpression = /[+\-*/%.,?:=<>!&|^~([`)\]}]
|
|
361
|
-
const wordContinuation = /^(?:in|instanceof)\b
|
|
362
|
-
const lineTerminator = /[\n\r\u2028\u2029]
|
|
418
|
+
const continuesExpression = /[+\-*/%.,?:=<>!&|^~([`)\]}]/
|
|
419
|
+
const wordContinuation = /^(?:in|instanceof)\b/
|
|
420
|
+
const lineTerminator = /[\n\r\u2028\u2029]/
|
|
363
421
|
|
|
364
422
|
/**
|
|
365
423
|
* Directive-prologue sniff over raw source. Returns undefined - not a guess - for any shape it cannot decide
|
|
@@ -367,77 +425,82 @@ const lineTerminator = /[\n\r\u2028\u2029]/;
|
|
|
367
425
|
* AST.
|
|
368
426
|
*/
|
|
369
427
|
function sniffDirective(source: string, directive: string): boolean | undefined {
|
|
370
|
-
let index = 0
|
|
428
|
+
let index = 0
|
|
371
429
|
if (source.startsWith('#!')) {
|
|
372
|
-
const nl = source.search(lineTerminator)
|
|
373
|
-
if (nl < 0) return false
|
|
374
|
-
index = nl + 1
|
|
430
|
+
const nl = source.search(lineTerminator)
|
|
431
|
+
if (nl < 0) return false
|
|
432
|
+
index = nl + 1
|
|
375
433
|
}
|
|
376
434
|
for (;;) {
|
|
377
|
-
index = skipTrivia(source, index)
|
|
378
|
-
if (index < 0) return undefined
|
|
379
|
-
const quote = source[index]
|
|
380
|
-
if (quote !== '"' && quote !== "'") return false
|
|
435
|
+
index = skipTrivia(source, index)
|
|
436
|
+
if (index < 0) return undefined
|
|
437
|
+
const quote = source[index]
|
|
438
|
+
if (quote !== '"' && quote !== "'") return false // prologue is over
|
|
381
439
|
|
|
382
|
-
let end = index + 1
|
|
440
|
+
let end = index + 1
|
|
383
441
|
while (end < source.length) {
|
|
384
|
-
const char = source[end]
|
|
442
|
+
const char = source[end]!
|
|
385
443
|
// An escape makes the raw text differ from the directive, but decoding it
|
|
386
444
|
// exactly is the AST's job, not this scanner's.
|
|
387
|
-
if (char === '\\' || lineTerminator.test(char)) return undefined
|
|
388
|
-
if (char === quote) break
|
|
389
|
-
end += 1
|
|
445
|
+
if (char === '\\' || lineTerminator.test(char)) return undefined
|
|
446
|
+
if (char === quote) break
|
|
447
|
+
end += 1
|
|
390
448
|
}
|
|
391
|
-
if (end >= source.length) return undefined
|
|
392
|
-
const raw = source.slice(index + 1, end)
|
|
449
|
+
if (end >= source.length) return undefined // unterminated literal
|
|
450
|
+
const raw = source.slice(index + 1, end)
|
|
393
451
|
|
|
394
|
-
const after = skipTrivia(source, end + 1)
|
|
395
|
-
if (after < 0) return undefined
|
|
396
|
-
const next = source[after]
|
|
397
|
-
let terminated = after >= source.length || next === ';'
|
|
452
|
+
const after = skipTrivia(source, end + 1)
|
|
453
|
+
if (after < 0) return undefined
|
|
454
|
+
const next = source[after]
|
|
455
|
+
let terminated = after >= source.length || next === ';'
|
|
398
456
|
if (!terminated && lineTerminator.test(source.slice(end + 1, after))) {
|
|
399
457
|
// ASI closes the statement unless the next token continues the expression.
|
|
400
|
-
terminated =
|
|
458
|
+
terminated =
|
|
459
|
+
!continuesExpression.test(next!) && !wordContinuation.test(source.slice(after, after + 11))
|
|
401
460
|
}
|
|
402
|
-
if (!terminated) return undefined
|
|
403
|
-
if (raw === directive) return true
|
|
404
|
-
index = next === ';' ? after + 1 : after
|
|
405
|
-
if (index >= source.length) return false
|
|
461
|
+
if (!terminated) return undefined
|
|
462
|
+
if (raw === directive) return true
|
|
463
|
+
index = next === ';' ? after + 1 : after
|
|
464
|
+
if (index >= source.length) return false
|
|
406
465
|
}
|
|
407
466
|
}
|
|
408
467
|
|
|
409
468
|
/** Past whitespace and comments; -1 for an unterminated block comment. */
|
|
410
469
|
function skipTrivia(source: string, index: number) {
|
|
411
470
|
for (;;) {
|
|
412
|
-
while (index < source.length && /\s/.test(source[index]!)) index += 1
|
|
413
|
-
if (source[index] !== '/') return index
|
|
471
|
+
while (index < source.length && /\s/.test(source[index]!)) index += 1
|
|
472
|
+
if (source[index] !== '/') return index
|
|
414
473
|
if (source[index + 1] === '/') {
|
|
415
|
-
let end = index + 2
|
|
416
|
-
while (end < source.length && !lineTerminator.test(source[end]!)) end += 1
|
|
417
|
-
index = end
|
|
418
|
-
continue
|
|
474
|
+
let end = index + 2
|
|
475
|
+
while (end < source.length && !lineTerminator.test(source[end]!)) end += 1
|
|
476
|
+
index = end
|
|
477
|
+
continue
|
|
419
478
|
}
|
|
420
479
|
if (source[index + 1] === '*') {
|
|
421
|
-
const end = source.indexOf('*/', index + 2)
|
|
422
|
-
if (end < 0) return -1
|
|
423
|
-
index = end + 2
|
|
424
|
-
continue
|
|
480
|
+
const end = source.indexOf('*/', index + 2)
|
|
481
|
+
if (end < 0) return -1
|
|
482
|
+
index = end + 2
|
|
483
|
+
continue
|
|
425
484
|
}
|
|
426
|
-
return index
|
|
485
|
+
return index
|
|
427
486
|
}
|
|
428
487
|
}
|
|
429
488
|
|
|
430
|
-
function hasDirective(
|
|
489
|
+
function hasDirective(
|
|
490
|
+
body: readonly { type: string; directive?: string | null }[],
|
|
491
|
+
directive: string,
|
|
492
|
+
) {
|
|
431
493
|
// The prologue ends at the first non-directive statement; comments and other
|
|
432
494
|
// directives before it do not.
|
|
433
495
|
for (const statement of body) {
|
|
434
|
-
if (statement.type !== 'ExpressionStatement' || typeof statement.directive !== 'string')
|
|
435
|
-
|
|
496
|
+
if (statement.type !== 'ExpressionStatement' || typeof statement.directive !== 'string')
|
|
497
|
+
return false
|
|
498
|
+
if (statement.directive === directive) return true
|
|
436
499
|
}
|
|
437
|
-
return false
|
|
500
|
+
return false
|
|
438
501
|
}
|
|
439
502
|
|
|
440
|
-
const requirePattern = /\brequire\s*\(\s*(['"])([^'"]+)\1\s*\)/g
|
|
503
|
+
const requirePattern = /\brequire\s*\(\s*(['"])([^'"]+)\1\s*\)/g
|
|
441
504
|
|
|
442
505
|
/**
|
|
443
506
|
* CJS `require` is an ordinary call, not a module record entry, so it stays on a regex - but a regex reads
|
|
@@ -446,33 +509,226 @@ const requirePattern = /\brequire\s*\(\s*(['"])([^'"]+)\1\s*\)/g;
|
|
|
446
509
|
* that lands inside one.
|
|
447
510
|
*/
|
|
448
511
|
function requireCalls(source: string, comments: readonly { start: number; end: number }[]) {
|
|
449
|
-
const calls: { specifier: string; index: number; specifierStart: number }[] = []
|
|
450
|
-
if (!source.includes('require')) return calls
|
|
451
|
-
requirePattern.lastIndex = 0
|
|
452
|
-
let match: RegExpExecArray | null
|
|
512
|
+
const calls: { specifier: string; index: number; specifierStart: number }[] = []
|
|
513
|
+
if (!source.includes('require')) return calls
|
|
514
|
+
requirePattern.lastIndex = 0
|
|
515
|
+
let match: RegExpExecArray | null
|
|
453
516
|
// Both comments and matches ascend, so one shared cursor walks them together.
|
|
454
|
-
let cursor = 0
|
|
517
|
+
let cursor = 0
|
|
455
518
|
while ((match = requirePattern.exec(source))) {
|
|
456
|
-
const index = match.index
|
|
457
|
-
while (cursor < comments.length && comments[cursor]!.end <= index) cursor += 1
|
|
458
|
-
if (cursor < comments.length && index >= comments[cursor]!.start) continue
|
|
519
|
+
const index = match.index
|
|
520
|
+
while (cursor < comments.length && comments[cursor]!.end <= index) cursor += 1
|
|
521
|
+
if (cursor < comments.length && index >= comments[cursor]!.start) continue
|
|
459
522
|
calls.push({
|
|
460
523
|
specifier: match[2]!,
|
|
461
524
|
index,
|
|
462
525
|
specifierStart: index + match[0].indexOf(match[1]!),
|
|
463
|
-
})
|
|
526
|
+
})
|
|
464
527
|
}
|
|
465
|
-
return calls
|
|
528
|
+
return calls
|
|
466
529
|
}
|
|
467
530
|
|
|
468
531
|
function literalSpecifier(raw: string) {
|
|
469
|
-
return /^\s*(['"])([^'"]*)\1\s*$/.exec(raw)?.[2] || undefined
|
|
532
|
+
return /^\s*(['"])([^'"]*)\1\s*$/.exec(raw)?.[2] || undefined
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
interface AstNode {
|
|
536
|
+
type: string
|
|
537
|
+
start: number
|
|
538
|
+
end: number
|
|
539
|
+
[key: string]: unknown
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function isNode(value: unknown): value is AstNode {
|
|
543
|
+
return typeof value === 'object' && value !== null && typeof (value as AstNode).type === 'string'
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function child(node: AstNode | undefined, key: string): AstNode | undefined {
|
|
547
|
+
const value = node?.[key]
|
|
548
|
+
return isNode(value) ? value : undefined
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function collectDynamicCalls(program: AstNode): DynamicCallFact[] {
|
|
552
|
+
const calls: DynamicCallFact[] = []
|
|
553
|
+
const visit = (value: unknown) => {
|
|
554
|
+
if (Array.isArray(value)) {
|
|
555
|
+
for (const item of value) visit(item)
|
|
556
|
+
return
|
|
557
|
+
}
|
|
558
|
+
if (!isNode(value)) return
|
|
559
|
+
if (value.type === 'CallExpression') {
|
|
560
|
+
const fact = dynamicCallFact(value)
|
|
561
|
+
if (fact) calls.push(fact)
|
|
562
|
+
}
|
|
563
|
+
for (const key in value) {
|
|
564
|
+
if (key === 'type' || key === 'loc' || key === 'range') continue
|
|
565
|
+
const entry = value[key]
|
|
566
|
+
if (entry && typeof entry === 'object') visit(entry)
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
visit(program.body)
|
|
570
|
+
return calls.sort((a, b) => a.start - b.start)
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function dynamicCallFact(call: AstNode): DynamicCallFact | undefined {
|
|
574
|
+
const callee = child(call, 'callee')
|
|
575
|
+
if (callee?.type !== 'Identifier') return undefined
|
|
576
|
+
const args = call.arguments
|
|
577
|
+
if (!Array.isArray(args) || !isNode(args[0])) return undefined
|
|
578
|
+
const loader = args[0]
|
|
579
|
+
|
|
580
|
+
let target: { specifier: AstNode; exportName: string } | undefined
|
|
581
|
+
let literal: boolean | undefined
|
|
582
|
+
if (stringValue(loader) !== undefined) {
|
|
583
|
+
target = { specifier: loader, exportName: 'default' }
|
|
584
|
+
literal = true
|
|
585
|
+
} else if (loader.type === 'ArrowFunctionExpression' || loader.type === 'FunctionExpression') {
|
|
586
|
+
target = loaderImportTarget(loader) ?? firstImportTarget(loader)
|
|
587
|
+
}
|
|
588
|
+
if (!target) return undefined
|
|
589
|
+
|
|
590
|
+
const specifier = stringValue(target.specifier)
|
|
591
|
+
if (specifier === undefined) return undefined
|
|
592
|
+
const options = isNode(args[1]) ? args[1] : undefined
|
|
593
|
+
return {
|
|
594
|
+
name: callee.name as string,
|
|
595
|
+
start: callee.start,
|
|
596
|
+
end: call.end,
|
|
597
|
+
specifier,
|
|
598
|
+
specifierStart: target.specifier.start,
|
|
599
|
+
specifierEnd: target.specifier.end,
|
|
600
|
+
exportName: target.exportName,
|
|
601
|
+
...(literal ? { literal } : {}),
|
|
602
|
+
loaderEnd: loader.end,
|
|
603
|
+
...(options ? { optionsStart: options.start, optionsEnd: options.end } : {}),
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function stringValue(node: AstNode | undefined): string | undefined {
|
|
608
|
+
if (!node) return undefined
|
|
609
|
+
if (node.type !== 'Literal' && node.type !== 'StringLiteral') return undefined
|
|
610
|
+
return typeof node.value === 'string' ? node.value : undefined
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/** Past parens, `await`, `as`/`!` and optional-chain wrappers to the loader's real expression. */
|
|
614
|
+
function unwrapExpression(node: AstNode | undefined): AstNode | undefined {
|
|
615
|
+
let current = node
|
|
616
|
+
for (;;) {
|
|
617
|
+
if (!current) return undefined
|
|
618
|
+
if (current.type === 'ParenthesizedExpression' || current.type === 'ChainExpression') {
|
|
619
|
+
current = child(current, 'expression')
|
|
620
|
+
} else if (current.type === 'AwaitExpression') {
|
|
621
|
+
current = child(current, 'argument')
|
|
622
|
+
} else if (
|
|
623
|
+
current.type === 'TSAsExpression' ||
|
|
624
|
+
current.type === 'TSNonNullExpression' ||
|
|
625
|
+
current.type === 'TSSatisfiesExpression'
|
|
626
|
+
) {
|
|
627
|
+
current = child(current, 'expression')
|
|
628
|
+
} else {
|
|
629
|
+
return current
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/** The expression a loader function resolves to: its expression body or first `return`. */
|
|
635
|
+
function loaderResult(fn: AstNode): AstNode | undefined {
|
|
636
|
+
const body = child(fn, 'body')
|
|
637
|
+
if (!body) return undefined
|
|
638
|
+
if (body.type !== 'BlockStatement' && body.type !== 'FunctionBody') return unwrapExpression(body)
|
|
639
|
+
const statements = body.body
|
|
640
|
+
if (!Array.isArray(statements)) return undefined
|
|
641
|
+
for (const statement of statements) {
|
|
642
|
+
if (isNode(statement) && statement.type === 'ReturnStatement') {
|
|
643
|
+
return unwrapExpression(child(statement, 'argument'))
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
return undefined
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function importSource(node: AstNode | undefined): AstNode | undefined {
|
|
650
|
+
if (node?.type !== 'ImportExpression') return undefined
|
|
651
|
+
const source = child(node, 'source')
|
|
652
|
+
return source && stringValue(source) !== undefined ? source : undefined
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Statically resolve a loader body: `import('x')`, `import('x').then(m => m.X)`
|
|
657
|
+
* (any param/arrow formatting), or `(await import('x')).X`.
|
|
658
|
+
*/
|
|
659
|
+
function loaderImportTarget(fn: AstNode): { specifier: AstNode; exportName: string } | undefined {
|
|
660
|
+
const result = loaderResult(fn)
|
|
661
|
+
if (!result) return undefined
|
|
662
|
+
|
|
663
|
+
const direct = importSource(result)
|
|
664
|
+
if (direct) return { specifier: direct, exportName: 'default' }
|
|
665
|
+
|
|
666
|
+
if (result.type === 'MemberExpression' && result.computed !== true) {
|
|
667
|
+
const source = importSource(unwrapExpression(child(result, 'object')))
|
|
668
|
+
const property = child(result, 'property')
|
|
669
|
+
if (source && property?.type === 'Identifier') {
|
|
670
|
+
return { specifier: source, exportName: property.name as string }
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (result.type !== 'CallExpression') return undefined
|
|
675
|
+
const callee = child(result, 'callee')
|
|
676
|
+
if (callee?.type !== 'MemberExpression' || callee.computed === true) return undefined
|
|
677
|
+
if (child(callee, 'property')?.name !== 'then') return undefined
|
|
678
|
+
const source = importSource(unwrapExpression(child(callee, 'object')))
|
|
679
|
+
if (!source) return undefined
|
|
680
|
+
return { specifier: source, exportName: thenArmExport(result) ?? 'default' }
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/** `X` of a `.then(m => m.X)` arm; undefined for any other arm shape. */
|
|
684
|
+
function thenArmExport(thenCall: AstNode): string | undefined {
|
|
685
|
+
const args = thenCall.arguments
|
|
686
|
+
if (!Array.isArray(args) || !isNode(args[0])) return undefined
|
|
687
|
+
const arm = args[0]
|
|
688
|
+
if (arm.type !== 'ArrowFunctionExpression' && arm.type !== 'FunctionExpression') return undefined
|
|
689
|
+
const params = arm.params
|
|
690
|
+
const param = Array.isArray(params) && isNode(params[0]) ? params[0] : undefined
|
|
691
|
+
if (param?.type !== 'Identifier') return undefined
|
|
692
|
+
const result = loaderResult(arm)
|
|
693
|
+
if (result?.type !== 'MemberExpression' || result.computed === true) return undefined
|
|
694
|
+
const object = unwrapExpression(child(result, 'object'))
|
|
695
|
+
if (object?.type !== 'Identifier' || object.name !== param.name) return undefined
|
|
696
|
+
const property = child(result, 'property')
|
|
697
|
+
return property?.type === 'Identifier' ? (property.name as string) : undefined
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Fallback for loader shapes the structured pass cannot follow: the first
|
|
702
|
+
* literal `import()` anywhere in the loader, default export — mirrors what the
|
|
703
|
+
* textual extraction always did for these.
|
|
704
|
+
*/
|
|
705
|
+
function firstImportTarget(fn: AstNode): { specifier: AstNode; exportName: string } | undefined {
|
|
706
|
+
let found: AstNode | undefined
|
|
707
|
+
const visit = (value: unknown) => {
|
|
708
|
+
if (found) return
|
|
709
|
+
if (Array.isArray(value)) {
|
|
710
|
+
for (const item of value) visit(item)
|
|
711
|
+
return
|
|
712
|
+
}
|
|
713
|
+
if (!isNode(value)) return
|
|
714
|
+
const source = importSource(value)
|
|
715
|
+
if (source) {
|
|
716
|
+
found = source
|
|
717
|
+
return
|
|
718
|
+
}
|
|
719
|
+
for (const key in value) {
|
|
720
|
+
const entry = value[key]
|
|
721
|
+
if (entry && typeof entry === 'object') visit(entry)
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
visit(fn.body)
|
|
725
|
+
return found ? { specifier: found, exportName: 'default' } : undefined
|
|
470
726
|
}
|
|
471
727
|
|
|
472
|
-
type ParserLang = 'ts' | 'tsx'
|
|
728
|
+
type ParserLang = 'ts' | 'tsx'
|
|
473
729
|
|
|
474
730
|
// `.ts` forbids JSX (`<T>value` is a type assertion there); everything else —
|
|
475
731
|
// including extensionless scan inputs — parses as the tsx superset.
|
|
476
732
|
function langForFile(file: string): ParserLang {
|
|
477
|
-
return /\.[mc]?ts$/.test(file) ? 'ts' : 'tsx'
|
|
733
|
+
return /\.[mc]?ts$/.test(file) ? 'ts' : 'tsx'
|
|
478
734
|
}
|