@wular/pnext 0.0.4 → 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 -1992
- 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 -1897
- 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} +139 -139
- package/src/resolve/engine.ts +90 -77
- package/src/resolve/imports.ts +475 -463
- package/src/resolve/scan-facts.ts +318 -296
- 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 +827 -815
- 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,14 +65,14 @@ 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
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
@@ -81,45 +81,45 @@ export interface RewriteFacts {
|
|
|
81
81
|
* call with such a loader; consumers gate on their own `dynamic` binding set.
|
|
82
82
|
*/
|
|
83
83
|
export interface DynamicCallFact {
|
|
84
|
-
name: string
|
|
84
|
+
name: string
|
|
85
85
|
/** Callee identifier start — aligns with the textual call scan's index. */
|
|
86
|
-
start: number
|
|
86
|
+
start: number
|
|
87
87
|
/** Call end (past the closing paren). */
|
|
88
|
-
end: number
|
|
88
|
+
end: number
|
|
89
89
|
/** Loader target, cooked value. */
|
|
90
|
-
specifier: string
|
|
90
|
+
specifier: string
|
|
91
91
|
/** Span of the quoted specifier literal, quotes included. */
|
|
92
|
-
specifierStart: number
|
|
93
|
-
specifierEnd: number
|
|
92
|
+
specifierStart: number
|
|
93
|
+
specifierEnd: number
|
|
94
94
|
/** From the `.then(m => m.X)` arm (or awaited member); `default` otherwise. */
|
|
95
|
-
exportName: string
|
|
95
|
+
exportName: string
|
|
96
96
|
/** First arg is a string literal (`dynamic('./x')` form), not a loader fn. */
|
|
97
|
-
literal?: boolean
|
|
97
|
+
literal?: boolean
|
|
98
98
|
/** End of the loader argument — options/appended args follow it. */
|
|
99
|
-
loaderEnd: number
|
|
99
|
+
loaderEnd: number
|
|
100
100
|
/** Second-argument span, when present. */
|
|
101
|
-
optionsStart?: number
|
|
102
|
-
optionsEnd?: number
|
|
101
|
+
optionsStart?: number
|
|
102
|
+
optionsEnd?: number
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
export interface ScanFacts {
|
|
106
106
|
/** Static value imports and `export … from` re-exports, in source order. */
|
|
107
|
-
imports: ScanEdge[]
|
|
107
|
+
imports: ScanEdge[]
|
|
108
108
|
/** `import('literal')` expressions, in source order. */
|
|
109
|
-
dynamicImports: { specifier: string; index: number }[]
|
|
109
|
+
dynamicImports: { specifier: string; index: number }[]
|
|
110
110
|
/** `require('literal')` calls — CJS sources still reach these scanners. */
|
|
111
|
-
requires: { specifier: string; index: number; specifierStart: number }[]
|
|
111
|
+
requires: { specifier: string; index: number; specifierStart: number }[]
|
|
112
112
|
/** This module's own value export names, `default` included. */
|
|
113
|
-
exportNames: string[]
|
|
113
|
+
exportNames: string[]
|
|
114
114
|
/** Specifiers of `export * from` (whose names live in the target). */
|
|
115
|
-
exportStars: string[]
|
|
115
|
+
exportStars: string[]
|
|
116
116
|
/** A real `'use client'` directive — prologue position only, never a comment. */
|
|
117
|
-
useClient: boolean
|
|
117
|
+
useClient: boolean
|
|
118
118
|
/** Loader-shaped calls (see `DynamicCallFact`); only sources that can bind `dynamic` are walked. */
|
|
119
|
-
dynamicCalls: DynamicCallFact[]
|
|
119
|
+
dynamicCalls: DynamicCallFact[]
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
const stylesheetOrData = /\.(?:css|scss|sass|less|styl|json)
|
|
122
|
+
const stylesheetOrData = /\.(?:css|scss|sass|less|styl|json)$/
|
|
123
123
|
|
|
124
124
|
const emptyFacts: ScanFacts = {
|
|
125
125
|
imports: [],
|
|
@@ -129,43 +129,43 @@ const emptyFacts: ScanFacts = {
|
|
|
129
129
|
exportStars: [],
|
|
130
130
|
useClient: false,
|
|
131
131
|
dynamicCalls: [],
|
|
132
|
-
}
|
|
132
|
+
}
|
|
133
133
|
|
|
134
134
|
const emptyRewriteFacts: RewriteFacts = {
|
|
135
135
|
edges: [],
|
|
136
136
|
imports: [],
|
|
137
137
|
importMetas: [],
|
|
138
138
|
unreliable: false,
|
|
139
|
-
}
|
|
139
|
+
}
|
|
140
140
|
|
|
141
|
-
const memo = new Map<string, { scan: ScanFacts; rewrite: RewriteFacts }>()
|
|
142
|
-
let hits = 0
|
|
143
|
-
let misses = 0
|
|
141
|
+
const memo = new Map<string, { scan: ScanFacts; rewrite: RewriteFacts }>()
|
|
142
|
+
let hits = 0
|
|
143
|
+
let misses = 0
|
|
144
144
|
|
|
145
145
|
export function scanFacts(file: string, source: string): ScanFacts {
|
|
146
146
|
// The module walk reaches stylesheets/data as ordinary edges; they hold no
|
|
147
147
|
// module facts and are not parseable as script.
|
|
148
|
-
if (stylesheetOrData.test(file)) return emptyFacts
|
|
149
|
-
return parsed(file, source).scan
|
|
148
|
+
if (stylesheetOrData.test(file)) return emptyFacts
|
|
149
|
+
return parsed(file, source).scan
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/** Every specifier a module pulls in — static, dynamic and `require`, in source order. */
|
|
153
153
|
export function importSpecifiers(source: string, sourcefile = 'source') {
|
|
154
|
-
const facts = scanFacts(sourcefile, source)
|
|
154
|
+
const facts = scanFacts(sourcefile, source)
|
|
155
155
|
return [
|
|
156
156
|
...facts.imports.map(edge => edge.specifier),
|
|
157
157
|
...facts.dynamicImports.map(entry => entry.specifier),
|
|
158
158
|
...facts.requires.map(entry => entry.specifier),
|
|
159
|
-
]
|
|
159
|
+
]
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
export function moduleExportNames(source: string, sourcefile = 'source.tsx') {
|
|
163
|
-
return scanFacts(sourcefile, source).exportNames
|
|
163
|
+
return scanFacts(sourcefile, source).exportNames
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
/** Specifiers of `export * from` — their names live in the target module. */
|
|
167
167
|
export function moduleExportStars(source: string, sourcefile = 'source.tsx') {
|
|
168
|
-
return scanFacts(sourcefile, source).exportStars
|
|
168
|
+
return scanFacts(sourcefile, source).exportStars
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
/**
|
|
@@ -173,27 +173,27 @@ export function moduleExportStars(source: string, sourcefile = 'source.tsx') {
|
|
|
173
173
|
* without a fact is an unanalyzable loader: no detection, nothing to splice.
|
|
174
174
|
*/
|
|
175
175
|
export function dynamicCallFacts(source: string, file = 'module.tsx'): DynamicCallFact[] {
|
|
176
|
-
if (stylesheetOrData.test(file)) return []
|
|
177
|
-
return parsed(file, source).scan.dynamicCalls
|
|
176
|
+
if (stylesheetOrData.test(file)) return []
|
|
177
|
+
return parsed(file, source).scan.dynamicCalls
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
/** Module-record spans for the rewrite passes (see `RewriteFacts`). */
|
|
181
181
|
export function rewriteFacts(file: string, source: string): RewriteFacts {
|
|
182
|
-
if (stylesheetOrData.test(file)) return emptyRewriteFacts
|
|
183
|
-
return parsed(file, source).rewrite
|
|
182
|
+
if (stylesheetOrData.test(file)) return emptyRewriteFacts
|
|
183
|
+
return parsed(file, source).rewrite
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
function parsed(file: string, source: string, lang = langForFile(file)) {
|
|
187
|
-
const key = `${lang}\0${source.length}\0${Bun.hash(source).toString()}
|
|
188
|
-
const cached = memo.get(key)
|
|
187
|
+
const key = `${lang}\0${source.length}\0${Bun.hash(source).toString()}`
|
|
188
|
+
const cached = memo.get(key)
|
|
189
189
|
if (cached) {
|
|
190
|
-
hits += 1
|
|
191
|
-
return cached
|
|
190
|
+
hits += 1
|
|
191
|
+
return cached
|
|
192
192
|
}
|
|
193
|
-
misses += 1
|
|
194
|
-
const facts = parseFacts(file, source, lang)
|
|
195
|
-
memo.set(key, facts)
|
|
196
|
-
return facts
|
|
193
|
+
misses += 1
|
|
194
|
+
const facts = parseFacts(file, source, lang)
|
|
195
|
+
memo.set(key, facts)
|
|
196
|
+
return facts
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
/**
|
|
@@ -207,68 +207,76 @@ export function rewriteSpecifierLiterals(
|
|
|
207
207
|
file: string,
|
|
208
208
|
replace: (specifier: string, kind: SpecifierEdge['kind']) => string | undefined,
|
|
209
209
|
): string {
|
|
210
|
-
const facts = rewriteFacts(file, source)
|
|
210
|
+
const facts = rewriteFacts(file, source)
|
|
211
211
|
// A partially recovered parse has a partial record; the textual scan the
|
|
212
212
|
// folded passes replaced is the safe answer there.
|
|
213
|
-
const edges = facts.unreliable ? textualEdges(source) : facts.edges
|
|
214
|
-
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 }[] = []
|
|
215
215
|
for (const edge of edges) {
|
|
216
|
-
const value = replace(edge.specifier, edge.kind)
|
|
217
|
-
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 })
|
|
218
218
|
}
|
|
219
|
-
return edits.length > 0 ? spliceSource(source, edits) : source
|
|
219
|
+
return edits.length > 0 ? spliceSource(source, edits) : source
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
const textualSpecifier = /(\bfrom\s*|\b(?:import|require)\s*\(\s*)(['"])([^'"]+)\2/g
|
|
222
|
+
const textualSpecifier = /(\bfrom\s*|\b(?:import|require)\s*\(\s*)(['"])([^'"]+)\2/g
|
|
223
223
|
|
|
224
224
|
function textualEdges(source: string): SpecifierEdge[] {
|
|
225
|
-
const edges: SpecifierEdge[] = []
|
|
226
|
-
textualSpecifier.lastIndex = 0
|
|
227
|
-
let match: RegExpExecArray | null
|
|
225
|
+
const edges: SpecifierEdge[] = []
|
|
226
|
+
textualSpecifier.lastIndex = 0
|
|
227
|
+
let match: RegExpExecArray | null
|
|
228
228
|
while ((match = textualSpecifier.exec(source))) {
|
|
229
|
-
const start = match.index + match[1]!.length
|
|
229
|
+
const start = match.index + match[1]!.length
|
|
230
230
|
edges.push({
|
|
231
231
|
specifier: match[3]!,
|
|
232
232
|
start,
|
|
233
233
|
end: start + match[3]!.length + 2,
|
|
234
|
-
kind: match[1]!.includes('require')
|
|
235
|
-
|
|
234
|
+
kind: match[1]!.includes('require')
|
|
235
|
+
? 'require'
|
|
236
|
+
: match[1]!.includes('import')
|
|
237
|
+
? 'dynamic'
|
|
238
|
+
: 'import',
|
|
239
|
+
})
|
|
236
240
|
}
|
|
237
|
-
return edges
|
|
241
|
+
return edges
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
/** Memo effectiveness, for the dev-boot benches. */
|
|
241
245
|
export function scanFactsStats() {
|
|
242
|
-
return { hits, misses, sources: memo.size }
|
|
246
|
+
return { hits, misses, sources: memo.size }
|
|
243
247
|
}
|
|
244
248
|
|
|
245
249
|
function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
246
250
|
// Parse errors are recovered, never thrown: a scan of a half-typed file in
|
|
247
251
|
// the dev watcher must still yield whatever the parser did understand.
|
|
248
|
-
const result = parseSync(file, source, { lang })
|
|
249
|
-
const imports: ScanEdge[] = []
|
|
250
|
-
const exportNames: string[] = []
|
|
251
|
-
const exportStars: string[] = []
|
|
252
|
-
const edges: SpecifierEdge[] = []
|
|
253
|
-
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[] = []
|
|
254
258
|
|
|
255
259
|
for (const statement of result.module.staticImports) {
|
|
256
|
-
const names: string[] = []
|
|
257
|
-
const bindings: ImportBinding[] = []
|
|
258
|
-
let bindsValue = statement.entries.length === 0
|
|
260
|
+
const names: string[] = []
|
|
261
|
+
const bindings: ImportBinding[] = []
|
|
262
|
+
let bindsValue = statement.entries.length === 0
|
|
259
263
|
for (const entry of statement.entries) {
|
|
260
|
-
const kind: string = entry.importName.kind
|
|
264
|
+
const kind: string = entry.importName.kind
|
|
261
265
|
bindings.push({
|
|
262
266
|
imported:
|
|
263
|
-
kind === 'Default'
|
|
267
|
+
kind === 'Default'
|
|
268
|
+
? 'default'
|
|
269
|
+
: kind === 'NamespaceObject'
|
|
270
|
+
? '*'
|
|
271
|
+
: (entry.importName.name ?? ''),
|
|
264
272
|
local: entry.localName.value ?? '',
|
|
265
273
|
type: entry.isType,
|
|
266
|
-
})
|
|
267
|
-
if (entry.isType) continue
|
|
268
|
-
bindsValue = true
|
|
269
|
-
if (kind === 'Default') names.push('default')
|
|
270
|
-
else if (kind === 'NamespaceObject') names.push('*')
|
|
271
|
-
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)
|
|
272
280
|
}
|
|
273
281
|
importStatements.push({
|
|
274
282
|
specifier: statement.moduleRequest.value,
|
|
@@ -277,86 +285,91 @@ function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
|
277
285
|
specifierStart: statement.moduleRequest.start,
|
|
278
286
|
specifierEnd: statement.moduleRequest.end,
|
|
279
287
|
bindings,
|
|
280
|
-
})
|
|
288
|
+
})
|
|
281
289
|
edges.push({
|
|
282
290
|
specifier: statement.moduleRequest.value,
|
|
283
291
|
start: statement.moduleRequest.start,
|
|
284
292
|
end: statement.moduleRequest.end,
|
|
285
293
|
kind: statement.entries.length === 0 ? 'side-effect' : 'import',
|
|
286
|
-
})
|
|
294
|
+
})
|
|
287
295
|
// `import type { … }` binds nothing at runtime: not an edge.
|
|
288
|
-
if (!bindsValue) continue
|
|
296
|
+
if (!bindsValue) continue
|
|
289
297
|
imports.push({
|
|
290
298
|
specifier: statement.moduleRequest.value,
|
|
291
299
|
exports: names,
|
|
292
300
|
index: statement.start,
|
|
293
|
-
})
|
|
301
|
+
})
|
|
294
302
|
}
|
|
295
303
|
|
|
296
304
|
// One `export { a, b } from 'x'` reports the same moduleRequest per entry.
|
|
297
|
-
const seenExportRequests = new Set<number>()
|
|
305
|
+
const seenExportRequests = new Set<number>()
|
|
298
306
|
for (const statement of result.module.staticExports) {
|
|
299
307
|
for (const entry of statement.entries) {
|
|
300
|
-
const request = entry.moduleRequest
|
|
301
|
-
if (!request || seenExportRequests.has(request.start)) continue
|
|
302
|
-
seenExportRequests.add(request.start)
|
|
303
|
-
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
|
+
})
|
|
304
317
|
}
|
|
305
318
|
}
|
|
306
319
|
|
|
307
320
|
for (const statement of result.module.staticExports) {
|
|
308
|
-
const edges = new Map<string, ScanEdge>()
|
|
321
|
+
const edges = new Map<string, ScanEdge>()
|
|
309
322
|
for (const entry of statement.entries) {
|
|
310
|
-
if (entry.isType) continue
|
|
311
|
-
const exportKind: string = entry.exportName.kind
|
|
312
|
-
if (exportKind === 'Default') exportNames.push('default')
|
|
313
|
-
else if (entry.exportName.name) exportNames.push(entry.exportName.name)
|
|
314
|
-
|
|
315
|
-
const specifier = entry.moduleRequest?.value
|
|
316
|
-
if (!specifier) continue
|
|
317
|
-
const importKind: string = entry.importName.kind
|
|
318
|
-
const star = importKind === 'AllButDefault'
|
|
319
|
-
if (star) exportStars.push(specifier)
|
|
320
|
-
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)
|
|
321
334
|
if (!edge) {
|
|
322
|
-
edge = { specifier, exports: [], index: statement.start, reexport: true }
|
|
323
|
-
edges.set(specifier, edge)
|
|
324
|
-
imports.push(edge)
|
|
335
|
+
edge = { specifier, exports: [], index: statement.start, reexport: true }
|
|
336
|
+
edges.set(specifier, edge)
|
|
337
|
+
imports.push(edge)
|
|
325
338
|
}
|
|
326
|
-
if (star) edge.star = true
|
|
327
|
-
else if (importKind === 'All') edge.exports.push('*')
|
|
328
|
-
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)
|
|
329
342
|
}
|
|
330
343
|
}
|
|
331
344
|
|
|
332
|
-
imports.sort((a, b) => a.index - b.index)
|
|
345
|
+
imports.sort((a, b) => a.index - b.index)
|
|
333
346
|
|
|
334
|
-
const dynamicImports: { specifier: string; index: number }[] = []
|
|
347
|
+
const dynamicImports: { specifier: string; index: number }[] = []
|
|
335
348
|
for (const entry of result.module.dynamicImports) {
|
|
336
|
-
const raw = source.slice(entry.moduleRequest.start, entry.moduleRequest.end)
|
|
337
|
-
const specifier = literalSpecifier(raw)
|
|
338
|
-
if (specifier === undefined) continue
|
|
339
|
-
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 })
|
|
340
353
|
// The literal's own span, without the whitespace `moduleRequest` includes.
|
|
341
|
-
const lead = raw.length - raw.trimStart().length
|
|
354
|
+
const lead = raw.length - raw.trimStart().length
|
|
342
355
|
edges.push({
|
|
343
356
|
specifier,
|
|
344
357
|
start: entry.moduleRequest.start + lead,
|
|
345
358
|
end: entry.moduleRequest.start + lead + specifier.length + 2,
|
|
346
359
|
kind: 'dynamic',
|
|
347
|
-
})
|
|
360
|
+
})
|
|
348
361
|
}
|
|
349
362
|
|
|
350
|
-
const requires = requireCalls(source, result.comments)
|
|
363
|
+
const requires = requireCalls(source, result.comments)
|
|
351
364
|
for (const call of requires) {
|
|
352
365
|
edges.push({
|
|
353
366
|
specifier: call.specifier,
|
|
354
367
|
start: call.specifierStart,
|
|
355
368
|
end: call.specifierStart + call.specifier.length + 2,
|
|
356
369
|
kind: 'require',
|
|
357
|
-
})
|
|
370
|
+
})
|
|
358
371
|
}
|
|
359
|
-
edges.sort((a, b) => a.start - b.start)
|
|
372
|
+
edges.sort((a, b) => a.start - b.start)
|
|
360
373
|
|
|
361
374
|
return {
|
|
362
375
|
scan: {
|
|
@@ -367,7 +380,7 @@ function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
|
367
380
|
exportStars,
|
|
368
381
|
useClient: hasUseClient(source, result),
|
|
369
382
|
// `result.program` deserializes the whole AST: walk it only for sources
|
|
370
|
-
// that can bind `dynamic()` at all (same cheap gate as dynamic
|
|
383
|
+
// that can bind `dynamic()` at all (same cheap gate as resolve/dynamic.ts).
|
|
371
384
|
dynamicCalls:
|
|
372
385
|
source.includes('@wular/pnext') || source.includes('next/dynamic')
|
|
373
386
|
? collectDynamicCalls(result.program as unknown as AstNode)
|
|
@@ -379,7 +392,7 @@ function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
|
379
392
|
importMetas: result.module.importMetas.map(meta => ({ start: meta.start, end: meta.end })),
|
|
380
393
|
unreliable: result.errors.length > 0,
|
|
381
394
|
},
|
|
382
|
-
}
|
|
395
|
+
}
|
|
383
396
|
}
|
|
384
397
|
|
|
385
398
|
/**
|
|
@@ -388,23 +401,23 @@ function parseFacts(file: string, source: string, lang: ParserLang) {
|
|
|
388
401
|
* consulted for the prologue shapes the scanner will not commit to.
|
|
389
402
|
*/
|
|
390
403
|
function hasUseClient(source: string, result: { program: { body: readonly Directive[] } }) {
|
|
391
|
-
const sniffed = sniffDirective(source, 'use client')
|
|
392
|
-
if (sniffed !== undefined) return sniffed
|
|
404
|
+
const sniffed = sniffDirective(source, 'use client')
|
|
405
|
+
if (sniffed !== undefined) return sniffed
|
|
393
406
|
// Uncertain, but a directive is its raw text: no occurrence, no directive.
|
|
394
|
-
if (!source.includes('use client')) return false
|
|
395
|
-
return hasDirective(result.program.body, 'use client')
|
|
407
|
+
if (!source.includes('use client')) return false
|
|
408
|
+
return hasDirective(result.program.body, 'use client')
|
|
396
409
|
}
|
|
397
410
|
|
|
398
411
|
interface Directive {
|
|
399
|
-
type: string
|
|
400
|
-
directive?: string | null
|
|
412
|
+
type: string
|
|
413
|
+
directive?: string | null
|
|
401
414
|
}
|
|
402
415
|
|
|
403
416
|
// A string literal is only a directive if what follows it ends the statement; a
|
|
404
417
|
// continuation token means it was an expression (`'use client' + x`).
|
|
405
|
-
const continuesExpression = /[+\-*/%.,?:=<>!&|^~([`)\]}]
|
|
406
|
-
const wordContinuation = /^(?:in|instanceof)\b
|
|
407
|
-
const lineTerminator = /[\n\r\u2028\u2029]
|
|
418
|
+
const continuesExpression = /[+\-*/%.,?:=<>!&|^~([`)\]}]/
|
|
419
|
+
const wordContinuation = /^(?:in|instanceof)\b/
|
|
420
|
+
const lineTerminator = /[\n\r\u2028\u2029]/
|
|
408
421
|
|
|
409
422
|
/**
|
|
410
423
|
* Directive-prologue sniff over raw source. Returns undefined - not a guess - for any shape it cannot decide
|
|
@@ -412,77 +425,82 @@ const lineTerminator = /[\n\r\u2028\u2029]/;
|
|
|
412
425
|
* AST.
|
|
413
426
|
*/
|
|
414
427
|
function sniffDirective(source: string, directive: string): boolean | undefined {
|
|
415
|
-
let index = 0
|
|
428
|
+
let index = 0
|
|
416
429
|
if (source.startsWith('#!')) {
|
|
417
|
-
const nl = source.search(lineTerminator)
|
|
418
|
-
if (nl < 0) return false
|
|
419
|
-
index = nl + 1
|
|
430
|
+
const nl = source.search(lineTerminator)
|
|
431
|
+
if (nl < 0) return false
|
|
432
|
+
index = nl + 1
|
|
420
433
|
}
|
|
421
434
|
for (;;) {
|
|
422
|
-
index = skipTrivia(source, index)
|
|
423
|
-
if (index < 0) return undefined
|
|
424
|
-
const quote = source[index]
|
|
425
|
-
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
|
|
426
439
|
|
|
427
|
-
let end = index + 1
|
|
440
|
+
let end = index + 1
|
|
428
441
|
while (end < source.length) {
|
|
429
|
-
const char = source[end]
|
|
442
|
+
const char = source[end]!
|
|
430
443
|
// An escape makes the raw text differ from the directive, but decoding it
|
|
431
444
|
// exactly is the AST's job, not this scanner's.
|
|
432
|
-
if (char === '\\' || lineTerminator.test(char)) return undefined
|
|
433
|
-
if (char === quote) break
|
|
434
|
-
end += 1
|
|
445
|
+
if (char === '\\' || lineTerminator.test(char)) return undefined
|
|
446
|
+
if (char === quote) break
|
|
447
|
+
end += 1
|
|
435
448
|
}
|
|
436
|
-
if (end >= source.length) return undefined
|
|
437
|
-
const raw = source.slice(index + 1, end)
|
|
449
|
+
if (end >= source.length) return undefined // unterminated literal
|
|
450
|
+
const raw = source.slice(index + 1, end)
|
|
438
451
|
|
|
439
|
-
const after = skipTrivia(source, end + 1)
|
|
440
|
-
if (after < 0) return undefined
|
|
441
|
-
const next = source[after]
|
|
442
|
-
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 === ';'
|
|
443
456
|
if (!terminated && lineTerminator.test(source.slice(end + 1, after))) {
|
|
444
457
|
// ASI closes the statement unless the next token continues the expression.
|
|
445
|
-
terminated =
|
|
458
|
+
terminated =
|
|
459
|
+
!continuesExpression.test(next!) && !wordContinuation.test(source.slice(after, after + 11))
|
|
446
460
|
}
|
|
447
|
-
if (!terminated) return undefined
|
|
448
|
-
if (raw === directive) return true
|
|
449
|
-
index = next === ';' ? after + 1 : after
|
|
450
|
-
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
|
|
451
465
|
}
|
|
452
466
|
}
|
|
453
467
|
|
|
454
468
|
/** Past whitespace and comments; -1 for an unterminated block comment. */
|
|
455
469
|
function skipTrivia(source: string, index: number) {
|
|
456
470
|
for (;;) {
|
|
457
|
-
while (index < source.length && /\s/.test(source[index]!)) index += 1
|
|
458
|
-
if (source[index] !== '/') return index
|
|
471
|
+
while (index < source.length && /\s/.test(source[index]!)) index += 1
|
|
472
|
+
if (source[index] !== '/') return index
|
|
459
473
|
if (source[index + 1] === '/') {
|
|
460
|
-
let end = index + 2
|
|
461
|
-
while (end < source.length && !lineTerminator.test(source[end]!)) end += 1
|
|
462
|
-
index = end
|
|
463
|
-
continue
|
|
474
|
+
let end = index + 2
|
|
475
|
+
while (end < source.length && !lineTerminator.test(source[end]!)) end += 1
|
|
476
|
+
index = end
|
|
477
|
+
continue
|
|
464
478
|
}
|
|
465
479
|
if (source[index + 1] === '*') {
|
|
466
|
-
const end = source.indexOf('*/', index + 2)
|
|
467
|
-
if (end < 0) return -1
|
|
468
|
-
index = end + 2
|
|
469
|
-
continue
|
|
480
|
+
const end = source.indexOf('*/', index + 2)
|
|
481
|
+
if (end < 0) return -1
|
|
482
|
+
index = end + 2
|
|
483
|
+
continue
|
|
470
484
|
}
|
|
471
|
-
return index
|
|
485
|
+
return index
|
|
472
486
|
}
|
|
473
487
|
}
|
|
474
488
|
|
|
475
|
-
function hasDirective(
|
|
489
|
+
function hasDirective(
|
|
490
|
+
body: readonly { type: string; directive?: string | null }[],
|
|
491
|
+
directive: string,
|
|
492
|
+
) {
|
|
476
493
|
// The prologue ends at the first non-directive statement; comments and other
|
|
477
494
|
// directives before it do not.
|
|
478
495
|
for (const statement of body) {
|
|
479
|
-
if (statement.type !== 'ExpressionStatement' || typeof statement.directive !== 'string')
|
|
480
|
-
|
|
496
|
+
if (statement.type !== 'ExpressionStatement' || typeof statement.directive !== 'string')
|
|
497
|
+
return false
|
|
498
|
+
if (statement.directive === directive) return true
|
|
481
499
|
}
|
|
482
|
-
return false
|
|
500
|
+
return false
|
|
483
501
|
}
|
|
484
502
|
|
|
485
|
-
const requirePattern = /\brequire\s*\(\s*(['"])([^'"]+)\1\s*\)/g
|
|
503
|
+
const requirePattern = /\brequire\s*\(\s*(['"])([^'"]+)\1\s*\)/g
|
|
486
504
|
|
|
487
505
|
/**
|
|
488
506
|
* CJS `require` is an ordinary call, not a module record entry, so it stays on a regex - but a regex reads
|
|
@@ -491,87 +509,87 @@ const requirePattern = /\brequire\s*\(\s*(['"])([^'"]+)\1\s*\)/g;
|
|
|
491
509
|
* that lands inside one.
|
|
492
510
|
*/
|
|
493
511
|
function requireCalls(source: string, comments: readonly { start: number; end: number }[]) {
|
|
494
|
-
const calls: { specifier: string; index: number; specifierStart: number }[] = []
|
|
495
|
-
if (!source.includes('require')) return calls
|
|
496
|
-
requirePattern.lastIndex = 0
|
|
497
|
-
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
|
|
498
516
|
// Both comments and matches ascend, so one shared cursor walks them together.
|
|
499
|
-
let cursor = 0
|
|
517
|
+
let cursor = 0
|
|
500
518
|
while ((match = requirePattern.exec(source))) {
|
|
501
|
-
const index = match.index
|
|
502
|
-
while (cursor < comments.length && comments[cursor]!.end <= index) cursor += 1
|
|
503
|
-
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
|
|
504
522
|
calls.push({
|
|
505
523
|
specifier: match[2]!,
|
|
506
524
|
index,
|
|
507
525
|
specifierStart: index + match[0].indexOf(match[1]!),
|
|
508
|
-
})
|
|
526
|
+
})
|
|
509
527
|
}
|
|
510
|
-
return calls
|
|
528
|
+
return calls
|
|
511
529
|
}
|
|
512
530
|
|
|
513
531
|
function literalSpecifier(raw: string) {
|
|
514
|
-
return /^\s*(['"])([^'"]*)\1\s*$/.exec(raw)?.[2] || undefined
|
|
532
|
+
return /^\s*(['"])([^'"]*)\1\s*$/.exec(raw)?.[2] || undefined
|
|
515
533
|
}
|
|
516
534
|
|
|
517
535
|
interface AstNode {
|
|
518
|
-
type: string
|
|
519
|
-
start: number
|
|
520
|
-
end: number
|
|
521
|
-
[key: string]: unknown
|
|
536
|
+
type: string
|
|
537
|
+
start: number
|
|
538
|
+
end: number
|
|
539
|
+
[key: string]: unknown
|
|
522
540
|
}
|
|
523
541
|
|
|
524
542
|
function isNode(value: unknown): value is AstNode {
|
|
525
|
-
return typeof value === 'object' && value !== null && typeof (value as AstNode).type === 'string'
|
|
543
|
+
return typeof value === 'object' && value !== null && typeof (value as AstNode).type === 'string'
|
|
526
544
|
}
|
|
527
545
|
|
|
528
546
|
function child(node: AstNode | undefined, key: string): AstNode | undefined {
|
|
529
|
-
const value = node?.[key]
|
|
530
|
-
return isNode(value) ? value : undefined
|
|
547
|
+
const value = node?.[key]
|
|
548
|
+
return isNode(value) ? value : undefined
|
|
531
549
|
}
|
|
532
550
|
|
|
533
551
|
function collectDynamicCalls(program: AstNode): DynamicCallFact[] {
|
|
534
|
-
const calls: DynamicCallFact[] = []
|
|
552
|
+
const calls: DynamicCallFact[] = []
|
|
535
553
|
const visit = (value: unknown) => {
|
|
536
554
|
if (Array.isArray(value)) {
|
|
537
|
-
for (const item of value) visit(item)
|
|
538
|
-
return
|
|
555
|
+
for (const item of value) visit(item)
|
|
556
|
+
return
|
|
539
557
|
}
|
|
540
|
-
if (!isNode(value)) return
|
|
558
|
+
if (!isNode(value)) return
|
|
541
559
|
if (value.type === 'CallExpression') {
|
|
542
|
-
const fact = dynamicCallFact(value)
|
|
543
|
-
if (fact) calls.push(fact)
|
|
560
|
+
const fact = dynamicCallFact(value)
|
|
561
|
+
if (fact) calls.push(fact)
|
|
544
562
|
}
|
|
545
563
|
for (const key in value) {
|
|
546
|
-
if (key === 'type' || key === 'loc' || key === 'range') continue
|
|
547
|
-
const entry = value[key]
|
|
548
|
-
if (entry && typeof entry === 'object') visit(entry)
|
|
564
|
+
if (key === 'type' || key === 'loc' || key === 'range') continue
|
|
565
|
+
const entry = value[key]
|
|
566
|
+
if (entry && typeof entry === 'object') visit(entry)
|
|
549
567
|
}
|
|
550
|
-
}
|
|
551
|
-
visit(program.body)
|
|
552
|
-
return calls.sort((a, b) => a.start - b.start)
|
|
568
|
+
}
|
|
569
|
+
visit(program.body)
|
|
570
|
+
return calls.sort((a, b) => a.start - b.start)
|
|
553
571
|
}
|
|
554
572
|
|
|
555
573
|
function dynamicCallFact(call: AstNode): DynamicCallFact | undefined {
|
|
556
|
-
const callee = child(call, 'callee')
|
|
557
|
-
if (callee?.type !== 'Identifier') return undefined
|
|
558
|
-
const args = call.arguments
|
|
559
|
-
if (!Array.isArray(args) || !isNode(args[0])) return undefined
|
|
560
|
-
const loader = args[0]
|
|
561
|
-
|
|
562
|
-
let target: { specifier: AstNode; exportName: string } | undefined
|
|
563
|
-
let literal: boolean | 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
|
|
564
582
|
if (stringValue(loader) !== undefined) {
|
|
565
|
-
target = { specifier: loader, exportName: 'default' }
|
|
566
|
-
literal = true
|
|
583
|
+
target = { specifier: loader, exportName: 'default' }
|
|
584
|
+
literal = true
|
|
567
585
|
} else if (loader.type === 'ArrowFunctionExpression' || loader.type === 'FunctionExpression') {
|
|
568
|
-
target = loaderImportTarget(loader) ?? firstImportTarget(loader)
|
|
586
|
+
target = loaderImportTarget(loader) ?? firstImportTarget(loader)
|
|
569
587
|
}
|
|
570
|
-
if (!target) return undefined
|
|
588
|
+
if (!target) return undefined
|
|
571
589
|
|
|
572
|
-
const specifier = stringValue(target.specifier)
|
|
573
|
-
if (specifier === undefined) return undefined
|
|
574
|
-
const options = isNode(args[1]) ? args[1] : undefined
|
|
590
|
+
const specifier = stringValue(target.specifier)
|
|
591
|
+
if (specifier === undefined) return undefined
|
|
592
|
+
const options = isNode(args[1]) ? args[1] : undefined
|
|
575
593
|
return {
|
|
576
594
|
name: callee.name as string,
|
|
577
595
|
start: callee.start,
|
|
@@ -583,51 +601,55 @@ function dynamicCallFact(call: AstNode): DynamicCallFact | undefined {
|
|
|
583
601
|
...(literal ? { literal } : {}),
|
|
584
602
|
loaderEnd: loader.end,
|
|
585
603
|
...(options ? { optionsStart: options.start, optionsEnd: options.end } : {}),
|
|
586
|
-
}
|
|
604
|
+
}
|
|
587
605
|
}
|
|
588
606
|
|
|
589
607
|
function stringValue(node: AstNode | undefined): string | undefined {
|
|
590
|
-
if (!node) return undefined
|
|
591
|
-
if (node.type !== 'Literal' && node.type !== 'StringLiteral') return undefined
|
|
592
|
-
return typeof node.value === 'string' ? node.value : 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
|
|
593
611
|
}
|
|
594
612
|
|
|
595
613
|
/** Past parens, `await`, `as`/`!` and optional-chain wrappers to the loader's real expression. */
|
|
596
614
|
function unwrapExpression(node: AstNode | undefined): AstNode | undefined {
|
|
597
|
-
let current = node
|
|
615
|
+
let current = node
|
|
598
616
|
for (;;) {
|
|
599
|
-
if (!current) return undefined
|
|
617
|
+
if (!current) return undefined
|
|
600
618
|
if (current.type === 'ParenthesizedExpression' || current.type === 'ChainExpression') {
|
|
601
|
-
current = child(current, 'expression')
|
|
619
|
+
current = child(current, 'expression')
|
|
602
620
|
} else if (current.type === 'AwaitExpression') {
|
|
603
|
-
current = child(current, 'argument')
|
|
604
|
-
} else if (
|
|
605
|
-
current
|
|
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')
|
|
606
628
|
} else {
|
|
607
|
-
return current
|
|
629
|
+
return current
|
|
608
630
|
}
|
|
609
631
|
}
|
|
610
632
|
}
|
|
611
633
|
|
|
612
634
|
/** The expression a loader function resolves to: its expression body or first `return`. */
|
|
613
635
|
function loaderResult(fn: AstNode): AstNode | undefined {
|
|
614
|
-
const body = child(fn, 'body')
|
|
615
|
-
if (!body) return undefined
|
|
616
|
-
if (body.type !== 'BlockStatement' && body.type !== 'FunctionBody') return unwrapExpression(body)
|
|
617
|
-
const statements = body.body
|
|
618
|
-
if (!Array.isArray(statements)) return 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
|
|
619
641
|
for (const statement of statements) {
|
|
620
642
|
if (isNode(statement) && statement.type === 'ReturnStatement') {
|
|
621
|
-
return unwrapExpression(child(statement, 'argument'))
|
|
643
|
+
return unwrapExpression(child(statement, 'argument'))
|
|
622
644
|
}
|
|
623
645
|
}
|
|
624
|
-
return undefined
|
|
646
|
+
return undefined
|
|
625
647
|
}
|
|
626
648
|
|
|
627
649
|
function importSource(node: AstNode | undefined): AstNode | undefined {
|
|
628
|
-
if (node?.type !== 'ImportExpression') return undefined
|
|
629
|
-
const source = child(node, 'source')
|
|
630
|
-
return source && stringValue(source) !== undefined ? source : undefined
|
|
650
|
+
if (node?.type !== 'ImportExpression') return undefined
|
|
651
|
+
const source = child(node, 'source')
|
|
652
|
+
return source && stringValue(source) !== undefined ? source : undefined
|
|
631
653
|
}
|
|
632
654
|
|
|
633
655
|
/**
|
|
@@ -635,44 +657,44 @@ function importSource(node: AstNode | undefined): AstNode | undefined {
|
|
|
635
657
|
* (any param/arrow formatting), or `(await import('x')).X`.
|
|
636
658
|
*/
|
|
637
659
|
function loaderImportTarget(fn: AstNode): { specifier: AstNode; exportName: string } | undefined {
|
|
638
|
-
const result = loaderResult(fn)
|
|
639
|
-
if (!result) return undefined
|
|
660
|
+
const result = loaderResult(fn)
|
|
661
|
+
if (!result) return undefined
|
|
640
662
|
|
|
641
|
-
const direct = importSource(result)
|
|
642
|
-
if (direct) return { specifier: direct, exportName: 'default' }
|
|
663
|
+
const direct = importSource(result)
|
|
664
|
+
if (direct) return { specifier: direct, exportName: 'default' }
|
|
643
665
|
|
|
644
666
|
if (result.type === 'MemberExpression' && result.computed !== true) {
|
|
645
|
-
const source = importSource(unwrapExpression(child(result, 'object')))
|
|
646
|
-
const property = child(result, 'property')
|
|
667
|
+
const source = importSource(unwrapExpression(child(result, 'object')))
|
|
668
|
+
const property = child(result, 'property')
|
|
647
669
|
if (source && property?.type === 'Identifier') {
|
|
648
|
-
return { specifier: source, exportName: property.name as string }
|
|
670
|
+
return { specifier: source, exportName: property.name as string }
|
|
649
671
|
}
|
|
650
672
|
}
|
|
651
673
|
|
|
652
|
-
if (result.type !== 'CallExpression') return undefined
|
|
653
|
-
const callee = child(result, 'callee')
|
|
654
|
-
if (callee?.type !== 'MemberExpression' || callee.computed === true) return undefined
|
|
655
|
-
if (child(callee, 'property')?.name !== 'then') return undefined
|
|
656
|
-
const source = importSource(unwrapExpression(child(callee, 'object')))
|
|
657
|
-
if (!source) return undefined
|
|
658
|
-
return { specifier: source, exportName: thenArmExport(result) ?? 'default' }
|
|
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' }
|
|
659
681
|
}
|
|
660
682
|
|
|
661
683
|
/** `X` of a `.then(m => m.X)` arm; undefined for any other arm shape. */
|
|
662
684
|
function thenArmExport(thenCall: AstNode): string | undefined {
|
|
663
|
-
const args = thenCall.arguments
|
|
664
|
-
if (!Array.isArray(args) || !isNode(args[0])) return undefined
|
|
665
|
-
const arm = args[0]
|
|
666
|
-
if (arm.type !== 'ArrowFunctionExpression' && arm.type !== 'FunctionExpression') return undefined
|
|
667
|
-
const params = arm.params
|
|
668
|
-
const param = Array.isArray(params) && isNode(params[0]) ? params[0] : undefined
|
|
669
|
-
if (param?.type !== 'Identifier') return undefined
|
|
670
|
-
const result = loaderResult(arm)
|
|
671
|
-
if (result?.type !== 'MemberExpression' || result.computed === true) return undefined
|
|
672
|
-
const object = unwrapExpression(child(result, 'object'))
|
|
673
|
-
if (object?.type !== 'Identifier' || object.name !== param.name) return undefined
|
|
674
|
-
const property = child(result, 'property')
|
|
675
|
-
return property?.type === 'Identifier' ? (property.name as 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
|
|
676
698
|
}
|
|
677
699
|
|
|
678
700
|
/**
|
|
@@ -681,32 +703,32 @@ function thenArmExport(thenCall: AstNode): string | undefined {
|
|
|
681
703
|
* textual extraction always did for these.
|
|
682
704
|
*/
|
|
683
705
|
function firstImportTarget(fn: AstNode): { specifier: AstNode; exportName: string } | undefined {
|
|
684
|
-
let found: AstNode | undefined
|
|
706
|
+
let found: AstNode | undefined
|
|
685
707
|
const visit = (value: unknown) => {
|
|
686
|
-
if (found) return
|
|
708
|
+
if (found) return
|
|
687
709
|
if (Array.isArray(value)) {
|
|
688
|
-
for (const item of value) visit(item)
|
|
689
|
-
return
|
|
710
|
+
for (const item of value) visit(item)
|
|
711
|
+
return
|
|
690
712
|
}
|
|
691
|
-
if (!isNode(value)) return
|
|
692
|
-
const source = importSource(value)
|
|
713
|
+
if (!isNode(value)) return
|
|
714
|
+
const source = importSource(value)
|
|
693
715
|
if (source) {
|
|
694
|
-
found = source
|
|
695
|
-
return
|
|
716
|
+
found = source
|
|
717
|
+
return
|
|
696
718
|
}
|
|
697
719
|
for (const key in value) {
|
|
698
|
-
const entry = value[key]
|
|
699
|
-
if (entry && typeof entry === 'object') visit(entry)
|
|
720
|
+
const entry = value[key]
|
|
721
|
+
if (entry && typeof entry === 'object') visit(entry)
|
|
700
722
|
}
|
|
701
|
-
}
|
|
702
|
-
visit(fn.body)
|
|
703
|
-
return found ? { specifier: found, exportName: 'default' } : undefined
|
|
723
|
+
}
|
|
724
|
+
visit(fn.body)
|
|
725
|
+
return found ? { specifier: found, exportName: 'default' } : undefined
|
|
704
726
|
}
|
|
705
727
|
|
|
706
|
-
type ParserLang = 'ts' | 'tsx'
|
|
728
|
+
type ParserLang = 'ts' | 'tsx'
|
|
707
729
|
|
|
708
730
|
// `.ts` forbids JSX (`<T>value` is a type assertion there); everything else —
|
|
709
731
|
// including extensionless scan inputs — parses as the tsx superset.
|
|
710
732
|
function langForFile(file: string): ParserLang {
|
|
711
|
-
return /\.[mc]?ts$/.test(file) ? 'ts' : 'tsx'
|
|
733
|
+
return /\.[mc]?ts$/.test(file) ? 'ts' : 'tsx'
|
|
712
734
|
}
|