@wular/pnext 0.0.4 → 0.0.7
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 +22 -16
- package/bin/pnext +6 -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 +87 -22
- package/reference/config.md +40 -93
- package/reference/css.md +42 -33
- package/reference/dev.md +26 -33
- package/reference/env.md +18 -20
- package/reference/getting-started.md +132 -0
- package/reference/metadata.md +31 -46
- package/reference/navigation.md +65 -105
- package/reference/performance.md +21 -142
- package/reference/rendering.md +78 -75
- package/reference/routing.md +97 -107
- package/reference/typegen.md +27 -29
- package/src/api/cache.ts +36 -37
- package/src/api/client-cache.ts +2 -2
- package/src/api/client-navigation.ts +121 -114
- package/src/api/dynamic.tsx +58 -55
- package/src/api/link.tsx +52 -53
- 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 +72 -64
- package/src/cli/dev.ts +116 -116
- package/src/cli/index.ts +94 -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 +802 -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 +32 -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 +2022 -1995
- package/src/{api → client}/router/types.ts +98 -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 +86 -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 +2087 -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 +87 -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 +954 -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 +428 -406
- package/src/styles.d.ts +9 -0
- package/src/types.ts +324 -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/reference/overview.md +0 -35
- 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
|
@@ -11,29 +11,29 @@
|
|
|
11
11
|
* Limitations: anonymous default exports, object methods, and function expressions passed inline as
|
|
12
12
|
* arguments are not wrapped.
|
|
13
13
|
*/
|
|
14
|
-
import path from 'node:path'
|
|
14
|
+
import path from 'node:path'
|
|
15
15
|
|
|
16
|
-
const useCacheRuntimePath = path.join(import.meta.dirname, 'use-cache.ts')
|
|
16
|
+
const useCacheRuntimePath = path.join(import.meta.dirname, 'use-cache.ts')
|
|
17
17
|
|
|
18
|
-
const quickTest = /(['"])use cache(?:\s*:\s*[\w-]+)?\1
|
|
19
|
-
const directiveText = /(['"])use cache(?:\s*:\s*([\w-]+))?\1\s
|
|
18
|
+
const quickTest = /(['"])use cache(?:\s*:\s*[\w-]+)?\1/
|
|
19
|
+
const directiveText = /(['"])use cache(?:\s*:\s*([\w-]+))?\1\s*;?/
|
|
20
20
|
const rootParamsImportLine =
|
|
21
|
-
/^[ \t]*import\s+\{[\s\S]*?\}\s+from\s+['"]next\/root-params['"]\s*;?[ \t]*(?:\r?\n)?/gm
|
|
21
|
+
/^[ \t]*import\s+\{[\s\S]*?\}\s+from\s+['"]next\/root-params['"]\s*;?[ \t]*(?:\r?\n)?/gm
|
|
22
22
|
|
|
23
|
-
const boundHelper = '__pnextUseCacheBound'
|
|
23
|
+
const boundHelper = '__pnextUseCacheBound'
|
|
24
24
|
|
|
25
25
|
export function rewriteUseCacheSource(source: string, file: string, root?: string): string {
|
|
26
26
|
// `includes` first: over a real module corpus the quoted-directive regex is
|
|
27
27
|
// ~24x the cost of the token scan, and virtually every module fails the token.
|
|
28
|
-
if (!source.includes('use cache') || !quickTest.test(source)) return source
|
|
28
|
+
if (!source.includes('use cache') || !quickTest.test(source)) return source
|
|
29
29
|
|
|
30
|
-
const idBase = root ? path.relative(root, file) : file
|
|
31
|
-
const wrapped = new Set<string>()
|
|
32
|
-
const privateWrapped = new Set<string>()
|
|
33
|
-
let out = source
|
|
34
|
-
let usedBoundHelper = false
|
|
30
|
+
const idBase = root ? path.relative(root, file) : file
|
|
31
|
+
const wrapped = new Set<string>()
|
|
32
|
+
const privateWrapped = new Set<string>()
|
|
33
|
+
let out = source
|
|
34
|
+
let usedBoundHelper = false
|
|
35
35
|
|
|
36
|
-
out = rewriteModuleLevelDirective(out, wrapped)
|
|
36
|
+
out = rewriteModuleLevelDirective(out, wrapped)
|
|
37
37
|
// Nested cached closures capturing enclosing bindings (bound args) must be wrapped in place - a
|
|
38
38
|
// module-level reassignment cannot reach a function-scoped binding, and the captured values
|
|
39
39
|
// participate in the cache key. Runs before the top-level pass so it only claims genuinely-nested
|
|
@@ -41,83 +41,85 @@ export function rewriteUseCacheSource(source: string, file: string, root?: strin
|
|
|
41
41
|
// including any non-serializable function, which is a build error - wrap them first so the directive
|
|
42
42
|
// is claimed here before the nested-named-closure pass, whose brace-balance heuristic would otherwise
|
|
43
43
|
// mis-attribute the arrow's directive to a sibling function declaration.
|
|
44
|
-
const arrows = rewriteReturnedArrowClosures(out, idBase)
|
|
45
|
-
out = arrows.source
|
|
46
|
-
usedBoundHelper = arrows.wrapped
|
|
47
|
-
const nested = rewriteNestedBoundClosures(out, idBase)
|
|
48
|
-
out = nested.source
|
|
49
|
-
usedBoundHelper = usedBoundHelper || nested.wrapped
|
|
44
|
+
const arrows = rewriteReturnedArrowClosures(out, idBase)
|
|
45
|
+
out = arrows.source
|
|
46
|
+
usedBoundHelper = arrows.wrapped
|
|
47
|
+
const nested = rewriteNestedBoundClosures(out, idBase)
|
|
48
|
+
out = nested.source
|
|
49
|
+
usedBoundHelper = usedBoundHelper || nested.wrapped
|
|
50
50
|
// Function expressions in value position (JSX attributes / call arguments):
|
|
51
51
|
// `<Form foo={async function () { 'use cache'; … }} />`.
|
|
52
|
-
const expressions = rewriteExpressionClosures(out, idBase)
|
|
53
|
-
out = expressions.source
|
|
54
|
-
usedBoundHelper = usedBoundHelper || expressions.wrapped
|
|
55
|
-
const objectMethods = rewriteObjectMethods(out, idBase)
|
|
56
|
-
out = objectMethods.source
|
|
57
|
-
usedBoundHelper = usedBoundHelper || objectMethods.wrapped
|
|
58
|
-
const staticMethods = rewriteStaticClassMethods(out, idBase)
|
|
59
|
-
out = staticMethods.source
|
|
60
|
-
const usedDirectHelper = staticMethods.wrapped
|
|
61
|
-
out = rewriteFunctionLevelDirectives(out, wrapped, privateWrapped)
|
|
62
|
-
|
|
63
|
-
if (wrapped.size === 0 && !usedBoundHelper && !usedDirectHelper) return out
|
|
64
|
-
|
|
65
|
-
const imports: string[] = []
|
|
52
|
+
const expressions = rewriteExpressionClosures(out, idBase)
|
|
53
|
+
out = expressions.source
|
|
54
|
+
usedBoundHelper = usedBoundHelper || expressions.wrapped
|
|
55
|
+
const objectMethods = rewriteObjectMethods(out, idBase)
|
|
56
|
+
out = objectMethods.source
|
|
57
|
+
usedBoundHelper = usedBoundHelper || objectMethods.wrapped
|
|
58
|
+
const staticMethods = rewriteStaticClassMethods(out, idBase)
|
|
59
|
+
out = staticMethods.source
|
|
60
|
+
const usedDirectHelper = staticMethods.wrapped
|
|
61
|
+
out = rewriteFunctionLevelDirectives(out, wrapped, privateWrapped)
|
|
62
|
+
|
|
63
|
+
if (wrapped.size === 0 && !usedBoundHelper && !usedDirectHelper) return out
|
|
64
|
+
|
|
65
|
+
const imports: string[] = []
|
|
66
66
|
if (wrapped.size > 0 || usedDirectHelper) {
|
|
67
|
-
imports.push(
|
|
67
|
+
imports.push(
|
|
68
|
+
`import { pnextUseCache as __pnextUseCache } from ${JSON.stringify(useCacheRuntimePath)};`,
|
|
69
|
+
)
|
|
68
70
|
}
|
|
69
71
|
if (privateWrapped.size > 0) {
|
|
70
72
|
imports.push(
|
|
71
73
|
`import { pnextUseCachePrivate as __pnextUseCachePrivate } from ${JSON.stringify(useCacheRuntimePath)};`,
|
|
72
|
-
)
|
|
74
|
+
)
|
|
73
75
|
}
|
|
74
76
|
if (usedBoundHelper) {
|
|
75
77
|
imports.push(
|
|
76
78
|
`import { pnextUseCacheBound as ${boundHelper} } from ${JSON.stringify(useCacheRuntimePath)};`,
|
|
77
|
-
)
|
|
79
|
+
)
|
|
78
80
|
}
|
|
79
81
|
// `export default NAME;` snapshots the binding's value when the statement
|
|
80
82
|
// evaluates (a default-exported identifier is not a live binding, unlike
|
|
81
83
|
// `export default function NAME`), so the module-end reassignment would be
|
|
82
84
|
// invisible to importers. Move the statement after the reassignments.
|
|
83
|
-
let defaultExport = ''
|
|
85
|
+
let defaultExport = ''
|
|
84
86
|
for (const name of wrapped) {
|
|
85
87
|
const statement = new RegExp(
|
|
86
88
|
`(^|[\\n;])[ \\t]*export\\s+default\\s+${escapeIdent(name)}(?![\\w$])\\s*;?`,
|
|
87
|
-
).exec(out)
|
|
88
|
-
if (!statement) continue
|
|
89
|
+
).exec(out)
|
|
90
|
+
if (!statement) continue
|
|
89
91
|
out =
|
|
90
92
|
out.slice(0, statement.index) +
|
|
91
93
|
(statement[1] ?? '') +
|
|
92
|
-
out.slice(statement.index + statement[0].length)
|
|
93
|
-
defaultExport = `\nexport default ${name}
|
|
94
|
-
break
|
|
94
|
+
out.slice(statement.index + statement[0].length)
|
|
95
|
+
defaultExport = `\nexport default ${name};`
|
|
96
|
+
break
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
const hoisted = hoistRootParamsImports(out)
|
|
99
|
+
const hoisted = hoistRootParamsImports(out)
|
|
98
100
|
const assignments = [...wrapped]
|
|
99
101
|
.map(name =>
|
|
100
102
|
privateWrapped.has(name)
|
|
101
103
|
? `${name} = __pnextUseCachePrivate(${JSON.stringify(`${idBase}#${name}`)}, ${name});`
|
|
102
104
|
: `${name} = __pnextUseCache(${JSON.stringify(`${idBase}#${name}`)}, ${name});`,
|
|
103
105
|
)
|
|
104
|
-
.join('\n')
|
|
105
|
-
const suffix = assignments ? `\n;${assignments}${defaultExport}\n` : '\n'
|
|
106
|
-
return `${hoisted.imports}${imports.join('\n')}\n${hoisted.source}${suffix}
|
|
106
|
+
.join('\n')
|
|
107
|
+
const suffix = assignments ? `\n;${assignments}${defaultExport}\n` : '\n'
|
|
108
|
+
return `${hoisted.imports}${imports.join('\n')}\n${hoisted.source}${suffix}`
|
|
107
109
|
}
|
|
108
110
|
|
|
109
111
|
function hoistRootParamsImports(source: string): { imports: string; source: string } {
|
|
110
|
-
if (!source.includes('next/root-params')) return { imports: '', source }
|
|
111
|
-
const imports: string[] = []
|
|
112
|
+
if (!source.includes('next/root-params')) return { imports: '', source }
|
|
113
|
+
const imports: string[] = []
|
|
112
114
|
const body = source.replace(rootParamsImportLine, statement => {
|
|
113
|
-
imports.push(statement.trimEnd())
|
|
114
|
-
return ''
|
|
115
|
-
})
|
|
116
|
-
if (imports.length === 0) return { imports: '', source }
|
|
115
|
+
imports.push(statement.trimEnd())
|
|
116
|
+
return ''
|
|
117
|
+
})
|
|
118
|
+
if (imports.length === 0) return { imports: '', source }
|
|
117
119
|
return {
|
|
118
120
|
imports: `${imports.join('\n')}\n`,
|
|
119
121
|
source: body.replace(/^\s*\n/, ''),
|
|
120
|
-
}
|
|
122
|
+
}
|
|
121
123
|
}
|
|
122
124
|
|
|
123
125
|
/**
|
|
@@ -138,42 +140,41 @@ function rewriteNestedBoundClosures(
|
|
|
138
140
|
source: string,
|
|
139
141
|
idBase: string,
|
|
140
142
|
): { source: string; wrapped: boolean } {
|
|
141
|
-
let out = source
|
|
142
|
-
let wrapped = false
|
|
143
|
-
let searchFrom = 0
|
|
143
|
+
let out = source
|
|
144
|
+
let wrapped = false
|
|
145
|
+
let searchFrom = 0
|
|
144
146
|
for (;;) {
|
|
145
|
-
const tail = out.slice(searchFrom)
|
|
146
|
-
const directive = directiveText.exec(tail)
|
|
147
|
-
if (!directive) break
|
|
148
|
-
const index = searchFrom + directive.index
|
|
149
|
-
const prefix = out.slice(0, index)
|
|
147
|
+
const tail = out.slice(searchFrom)
|
|
148
|
+
const directive = directiveText.exec(tail)
|
|
149
|
+
if (!directive) break
|
|
150
|
+
const index = searchFrom + directive.index
|
|
151
|
+
const prefix = out.slice(0, index)
|
|
150
152
|
if (!/\{\s*$/.test(prefix)) {
|
|
151
|
-
searchFrom = index + directive[0].length
|
|
152
|
-
continue
|
|
153
|
+
searchFrom = index + directive[0].length
|
|
154
|
+
continue
|
|
153
155
|
}
|
|
154
|
-
const closure = findNestedClosure(out, index)
|
|
156
|
+
const closure = findNestedClosure(out, index)
|
|
155
157
|
if (!closure) {
|
|
156
|
-
searchFrom = index + directive[0].length
|
|
157
|
-
continue
|
|
158
|
+
searchFrom = index + directive[0].length
|
|
159
|
+
continue
|
|
158
160
|
}
|
|
159
|
-
const id = `${idBase}#${closure.enclosingName}.${closure.name}
|
|
160
|
-
const captureList = closure.captures.length ? closure.captures.join(', ') : ''
|
|
161
|
+
const id = `${idBase}#${closure.enclosingName}.${closure.name}`
|
|
162
|
+
const captureList = closure.captures.length ? closure.captures.join(', ') : ''
|
|
161
163
|
// Replace `[async ]function NAME(params) {` with a wrapped const binding, and
|
|
162
164
|
// strip the directive from the body.
|
|
163
|
-
const wrappedHead =
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
const rest = out.slice(closure.bodyEnd);
|
|
165
|
+
const wrappedHead = `const ${closure.name} = ${boundHelper}(${JSON.stringify(id)}, ${closure.asyncKeyword}function ${closure.name}(${closure.params}) {`
|
|
166
|
+
const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd)
|
|
167
|
+
const rest = out.slice(closure.bodyEnd)
|
|
167
168
|
out =
|
|
168
169
|
out.slice(0, closure.declStart) +
|
|
169
170
|
wrappedHead +
|
|
170
171
|
bodyAfterDirective +
|
|
171
172
|
`}, () => [${captureList}])` +
|
|
172
|
-
rest.replace(/^\}/, '')
|
|
173
|
-
wrapped = true
|
|
174
|
-
searchFrom = closure.declStart + wrappedHead.length
|
|
173
|
+
rest.replace(/^\}/, '')
|
|
174
|
+
wrapped = true
|
|
175
|
+
searchFrom = closure.declStart + wrappedHead.length
|
|
175
176
|
}
|
|
176
|
-
return { source: out, wrapped }
|
|
177
|
+
return { source: out, wrapped }
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
/**
|
|
@@ -193,41 +194,41 @@ function rewriteReturnedArrowClosures(
|
|
|
193
194
|
source: string,
|
|
194
195
|
idBase: string,
|
|
195
196
|
): { source: string; wrapped: boolean } {
|
|
196
|
-
let out = source
|
|
197
|
-
let wrapped = false
|
|
198
|
-
let searchFrom = 0
|
|
199
|
-
let counter = 0
|
|
197
|
+
let out = source
|
|
198
|
+
let wrapped = false
|
|
199
|
+
let searchFrom = 0
|
|
200
|
+
let counter = 0
|
|
200
201
|
for (;;) {
|
|
201
|
-
const tail = out.slice(searchFrom)
|
|
202
|
-
const directive = directiveText.exec(tail)
|
|
203
|
-
if (!directive) break
|
|
204
|
-
const index = searchFrom + directive.index
|
|
205
|
-
const prefix = out.slice(0, index)
|
|
202
|
+
const tail = out.slice(searchFrom)
|
|
203
|
+
const directive = directiveText.exec(tail)
|
|
204
|
+
if (!directive) break
|
|
205
|
+
const index = searchFrom + directive.index
|
|
206
|
+
const prefix = out.slice(0, index)
|
|
206
207
|
if (!/\{\s*$/.test(prefix)) {
|
|
207
|
-
searchFrom = index + directive[0].length
|
|
208
|
-
continue
|
|
208
|
+
searchFrom = index + directive[0].length
|
|
209
|
+
continue
|
|
209
210
|
}
|
|
210
|
-
const closure = findReturnedArrowClosure(out, index)
|
|
211
|
+
const closure = findReturnedArrowClosure(out, index)
|
|
211
212
|
if (!closure) {
|
|
212
|
-
searchFrom = index + directive[0].length
|
|
213
|
-
continue
|
|
213
|
+
searchFrom = index + directive[0].length
|
|
214
|
+
continue
|
|
214
215
|
}
|
|
215
|
-
const id = `${idBase}#${closure.enclosingName}.arrow${counter === 0 ? '' : counter}
|
|
216
|
-
counter += 1
|
|
217
|
-
const captureList = closure.captures.join(', ')
|
|
218
|
-
const wrappedHead = `${boundHelper}(${JSON.stringify(id)}, ${closure.head}
|
|
219
|
-
const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd)
|
|
220
|
-
const rest = out.slice(closure.bodyEnd)
|
|
216
|
+
const id = `${idBase}#${closure.enclosingName}.arrow${counter === 0 ? '' : counter}`
|
|
217
|
+
counter += 1
|
|
218
|
+
const captureList = closure.captures.join(', ')
|
|
219
|
+
const wrappedHead = `${boundHelper}(${JSON.stringify(id)}, ${closure.head}`
|
|
220
|
+
const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd)
|
|
221
|
+
const rest = out.slice(closure.bodyEnd)
|
|
221
222
|
out =
|
|
222
223
|
out.slice(0, closure.declStart) +
|
|
223
224
|
wrappedHead +
|
|
224
225
|
bodyAfterDirective +
|
|
225
226
|
`}, () => [${captureList}])` +
|
|
226
|
-
rest.replace(/^\}/, '')
|
|
227
|
-
wrapped = true
|
|
228
|
-
searchFrom = closure.declStart + wrappedHead.length
|
|
227
|
+
rest.replace(/^\}/, '')
|
|
228
|
+
wrapped = true
|
|
229
|
+
searchFrom = closure.declStart + wrappedHead.length
|
|
229
230
|
}
|
|
230
|
-
return { source: out, wrapped }
|
|
231
|
+
return { source: out, wrapped }
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
/**
|
|
@@ -241,90 +242,90 @@ function rewriteExpressionClosures(
|
|
|
241
242
|
source: string,
|
|
242
243
|
idBase: string,
|
|
243
244
|
): { source: string; wrapped: boolean } {
|
|
244
|
-
let out = source
|
|
245
|
-
let wrapped = false
|
|
246
|
-
let searchFrom = 0
|
|
247
|
-
let counter = 0
|
|
245
|
+
let out = source
|
|
246
|
+
let wrapped = false
|
|
247
|
+
let searchFrom = 0
|
|
248
|
+
let counter = 0
|
|
248
249
|
for (;;) {
|
|
249
|
-
const tail = out.slice(searchFrom)
|
|
250
|
-
const directive = directiveText.exec(tail)
|
|
251
|
-
if (!directive) break
|
|
252
|
-
const index = searchFrom + directive.index
|
|
250
|
+
const tail = out.slice(searchFrom)
|
|
251
|
+
const directive = directiveText.exec(tail)
|
|
252
|
+
if (!directive) break
|
|
253
|
+
const index = searchFrom + directive.index
|
|
253
254
|
if (directive[2] !== undefined || !/\{\s*$/.test(out.slice(0, index))) {
|
|
254
|
-
searchFrom = index + directive[0].length
|
|
255
|
-
continue
|
|
255
|
+
searchFrom = index + directive[0].length
|
|
256
|
+
continue
|
|
256
257
|
}
|
|
257
|
-
const closure = findExpressionClosure(out, index)
|
|
258
|
+
const closure = findExpressionClosure(out, index)
|
|
258
259
|
if (!closure) {
|
|
259
|
-
searchFrom = index + directive[0].length
|
|
260
|
-
continue
|
|
260
|
+
searchFrom = index + directive[0].length
|
|
261
|
+
continue
|
|
261
262
|
}
|
|
262
|
-
const id = `${idBase}#${closure.enclosingName}.expr${counter === 0 ? '' : counter}
|
|
263
|
-
counter += 1
|
|
264
|
-
const wrappedHead = `${boundHelper}(${JSON.stringify(id)}, ${closure.head}
|
|
265
|
-
const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd)
|
|
266
|
-
const rest = out.slice(closure.bodyEnd)
|
|
263
|
+
const id = `${idBase}#${closure.enclosingName}.expr${counter === 0 ? '' : counter}`
|
|
264
|
+
counter += 1
|
|
265
|
+
const wrappedHead = `${boundHelper}(${JSON.stringify(id)}, ${closure.head}`
|
|
266
|
+
const bodyAfterDirective = out.slice(index + directive[0].length, closure.bodyEnd)
|
|
267
|
+
const rest = out.slice(closure.bodyEnd)
|
|
267
268
|
out =
|
|
268
269
|
out.slice(0, closure.declStart) +
|
|
269
270
|
wrappedHead +
|
|
270
271
|
bodyAfterDirective +
|
|
271
272
|
`}, () => [${closure.captures.join(', ')}])` +
|
|
272
|
-
rest.replace(/^\}/, '')
|
|
273
|
-
wrapped = true
|
|
274
|
-
searchFrom = closure.declStart + wrappedHead.length
|
|
273
|
+
rest.replace(/^\}/, '')
|
|
274
|
+
wrapped = true
|
|
275
|
+
searchFrom = closure.declStart + wrappedHead.length
|
|
275
276
|
}
|
|
276
|
-
return { source: out, wrapped }
|
|
277
|
+
return { source: out, wrapped }
|
|
277
278
|
}
|
|
278
279
|
|
|
279
280
|
function findExpressionClosure(
|
|
280
281
|
source: string,
|
|
281
282
|
directiveIndex: number,
|
|
282
283
|
): ReturnedArrowClosure | null {
|
|
283
|
-
const prefix = source.slice(0, directiveIndex)
|
|
284
|
+
const prefix = source.slice(0, directiveIndex)
|
|
284
285
|
// Value-position contexts: a JSX attribute (`={`), a call argument (`(` or
|
|
285
286
|
// `,`), or an object-property value (`:`), followed by a function expression
|
|
286
287
|
// or an arrow whose body opens with the directive.
|
|
287
288
|
const exprRe =
|
|
288
|
-
/(=\s*\{|[(,:])\s*((?:async\s+)?(?:function(?:\s+[A-Za-z_$][\w$]*)?\s*\(([^)]*)\)|(?:\(([^)]*)\)|[A-Za-z_$][\w$]*)\s*=>)\s*\{)/g
|
|
289
|
-
let match: RegExpExecArray | null
|
|
290
|
-
let best: { headStart: number; head: string; params: string; bodyOpen: number } | null = null
|
|
289
|
+
/(=\s*\{|[(,:])\s*((?:async\s+)?(?:function(?:\s+[A-Za-z_$][\w$]*)?\s*\(([^)]*)\)|(?:\(([^)]*)\)|[A-Za-z_$][\w$]*)\s*=>)\s*\{)/g
|
|
290
|
+
let match: RegExpExecArray | null
|
|
291
|
+
let best: { headStart: number; head: string; params: string; bodyOpen: number } | null = null
|
|
291
292
|
while ((match = exprRe.exec(prefix)) !== null) {
|
|
292
|
-
const head = match[2]
|
|
293
|
-
const headStart = match.index + match[0].length - head.length
|
|
294
|
-
const bodyOpen = match.index + match[0].length
|
|
295
|
-
const between = source.slice(bodyOpen, directiveIndex)
|
|
293
|
+
const head = match[2]!
|
|
294
|
+
const headStart = match.index + match[0].length - head.length
|
|
295
|
+
const bodyOpen = match.index + match[0].length
|
|
296
|
+
const between = source.slice(bodyOpen, directiveIndex)
|
|
296
297
|
// The directive must be the first statement of THIS body: zero net braces
|
|
297
298
|
// between, and the body must never close in between (a dip below zero means
|
|
298
299
|
// this candidate's body ended before the directive).
|
|
299
|
-
let balance = 0
|
|
300
|
-
let closed = false
|
|
300
|
+
let balance = 0
|
|
301
|
+
let closed = false
|
|
301
302
|
for (const ch of between) {
|
|
302
|
-
if (ch === '{') balance += 1
|
|
303
|
+
if (ch === '{') balance += 1
|
|
303
304
|
else if (ch === '}') {
|
|
304
|
-
balance -= 1
|
|
305
|
+
balance -= 1
|
|
305
306
|
if (balance < 0) {
|
|
306
|
-
closed = true
|
|
307
|
-
break
|
|
307
|
+
closed = true
|
|
308
|
+
break
|
|
308
309
|
}
|
|
309
310
|
}
|
|
310
311
|
}
|
|
311
312
|
if (!closed && balance === 0) {
|
|
312
|
-
best = { headStart, head, params: match[3] ?? match[4] ?? '', bodyOpen }
|
|
313
|
+
best = { headStart, head, params: match[3] ?? match[4] ?? '', bodyOpen }
|
|
313
314
|
}
|
|
314
315
|
}
|
|
315
|
-
if (!best) return null
|
|
316
|
+
if (!best) return null
|
|
316
317
|
|
|
317
|
-
const bodyEnd = matchClosingBrace(source, best.bodyOpen)
|
|
318
|
-
if (bodyEnd < directiveIndex) return null
|
|
319
|
-
const body = source.slice(best.bodyOpen, bodyEnd)
|
|
318
|
+
const bodyEnd = matchClosingBrace(source, best.bodyOpen)
|
|
319
|
+
if (bodyEnd < directiveIndex) return null
|
|
320
|
+
const body = source.slice(best.bodyOpen, bodyEnd)
|
|
320
321
|
|
|
321
|
-
const enclosing = findEnclosingFunctionFull(source, best.headStart)
|
|
322
|
-
const ownParams = new Set(paramNames(best.params))
|
|
322
|
+
const enclosing = findEnclosingFunctionFull(source, best.headStart)
|
|
323
|
+
const ownParams = new Set(paramNames(best.params))
|
|
323
324
|
const captures = enclosing
|
|
324
325
|
? enclosingCaptureCandidates(source, enclosing).filter(
|
|
325
326
|
name => !ownParams.has(name) && new RegExp(`\\b${escapeIdent(name)}\\b`).test(body),
|
|
326
327
|
)
|
|
327
|
-
: []
|
|
328
|
+
: []
|
|
328
329
|
|
|
329
330
|
return {
|
|
330
331
|
head: best.head,
|
|
@@ -332,61 +333,61 @@ function findExpressionClosure(
|
|
|
332
333
|
bodyEnd,
|
|
333
334
|
enclosingName: enclosing?.name ?? 'module',
|
|
334
335
|
captures,
|
|
335
|
-
}
|
|
336
|
+
}
|
|
336
337
|
}
|
|
337
338
|
|
|
338
339
|
interface ReturnedArrowClosure {
|
|
339
|
-
head: string
|
|
340
|
-
declStart: number
|
|
341
|
-
bodyEnd: number
|
|
342
|
-
enclosingName: string
|
|
343
|
-
captures: string[]
|
|
340
|
+
head: string
|
|
341
|
+
declStart: number
|
|
342
|
+
bodyEnd: number
|
|
343
|
+
enclosingName: string
|
|
344
|
+
captures: string[]
|
|
344
345
|
}
|
|
345
346
|
|
|
346
347
|
function findReturnedArrowClosure(
|
|
347
348
|
source: string,
|
|
348
349
|
directiveIndex: number,
|
|
349
350
|
): ReturnedArrowClosure | null {
|
|
350
|
-
const prefix = source.slice(0, directiveIndex)
|
|
351
|
+
const prefix = source.slice(0, directiveIndex)
|
|
351
352
|
// The parameter list must stay on one line and carry no `<`: otherwise
|
|
352
353
|
// `return (\n <Form getDate={async () => {` reads as one giant "arrow" whose
|
|
353
354
|
// params run from the returned parenthesis to the first `)` inside the JSX,
|
|
354
355
|
// and the wrap lands on the return expression instead of the attribute's
|
|
355
356
|
// closure (use-cache-with-server-function-props). Value-position closures like
|
|
356
357
|
// that are claimed by rewriteExpressionClosures instead.
|
|
357
|
-
const arrowRe = /\breturn\s+(async\s+)?(\([^)<\n]*\)|[A-Za-z_$][\w$]*)\s*=>\s*\{/g
|
|
358
|
-
let match: RegExpExecArray | null
|
|
359
|
-
let best: { headStart: number; head: string; params: string; bodyOpen: number } | null = null
|
|
358
|
+
const arrowRe = /\breturn\s+(async\s+)?(\([^)<\n]*\)|[A-Za-z_$][\w$]*)\s*=>\s*\{/g
|
|
359
|
+
let match: RegExpExecArray | null
|
|
360
|
+
let best: { headStart: number; head: string; params: string; bodyOpen: number } | null = null
|
|
360
361
|
while ((match = arrowRe.exec(prefix)) !== null) {
|
|
361
|
-
const retPrefix = /^return\s+/.exec(match[0])?.[0] ?? 'return '
|
|
362
|
-
const headStart = match.index + retPrefix.length
|
|
363
|
-
const bodyOpen = match.index + match[0].length
|
|
364
|
-
const between = source.slice(bodyOpen, directiveIndex)
|
|
365
|
-
let balance = 0
|
|
362
|
+
const retPrefix = /^return\s+/.exec(match[0])?.[0] ?? 'return '
|
|
363
|
+
const headStart = match.index + retPrefix.length
|
|
364
|
+
const bodyOpen = match.index + match[0].length
|
|
365
|
+
const between = source.slice(bodyOpen, directiveIndex)
|
|
366
|
+
let balance = 0
|
|
366
367
|
for (const ch of between) {
|
|
367
|
-
if (ch === '{') balance += 1
|
|
368
|
-
else if (ch === '}') balance -= 1
|
|
368
|
+
if (ch === '{') balance += 1
|
|
369
|
+
else if (ch === '}') balance -= 1
|
|
369
370
|
}
|
|
370
371
|
if (balance === 0) {
|
|
371
|
-
const paramSpec = match[2]
|
|
372
|
-
const params = paramSpec.startsWith('(') ? paramSpec.slice(1, -1) : paramSpec
|
|
373
|
-
best = { headStart, head: match[0].slice(retPrefix.length), params, bodyOpen }
|
|
372
|
+
const paramSpec = match[2]!
|
|
373
|
+
const params = paramSpec.startsWith('(') ? paramSpec.slice(1, -1) : paramSpec
|
|
374
|
+
best = { headStart, head: match[0].slice(retPrefix.length), params, bodyOpen }
|
|
374
375
|
}
|
|
375
376
|
}
|
|
376
|
-
if (!best) return null
|
|
377
|
+
if (!best) return null
|
|
377
378
|
|
|
378
|
-
const enclosing = findEnclosingFunctionFull(source, best.headStart)
|
|
379
|
-
if (!enclosing) return null
|
|
379
|
+
const enclosing = findEnclosingFunctionFull(source, best.headStart)
|
|
380
|
+
if (!enclosing) return null
|
|
380
381
|
|
|
381
|
-
const bodyEnd = matchClosingBrace(source, best.bodyOpen)
|
|
382
|
-
if (bodyEnd < 0) return null
|
|
383
|
-
const body = source.slice(best.bodyOpen, bodyEnd)
|
|
382
|
+
const bodyEnd = matchClosingBrace(source, best.bodyOpen)
|
|
383
|
+
if (bodyEnd < 0) return null
|
|
384
|
+
const body = source.slice(best.bodyOpen, bodyEnd)
|
|
384
385
|
|
|
385
|
-
const arrowParams = new Set(paramNames(best.params))
|
|
386
|
-
const candidates = enclosingCaptureCandidates(source, enclosing)
|
|
386
|
+
const arrowParams = new Set(paramNames(best.params))
|
|
387
|
+
const candidates = enclosingCaptureCandidates(source, enclosing)
|
|
387
388
|
const captures = candidates.filter(
|
|
388
389
|
name => !arrowParams.has(name) && new RegExp(`\\b${escapeIdent(name)}\\b`).test(body),
|
|
389
|
-
)
|
|
390
|
+
)
|
|
390
391
|
|
|
391
392
|
return {
|
|
392
393
|
head: best.head,
|
|
@@ -394,7 +395,7 @@ function findReturnedArrowClosure(
|
|
|
394
395
|
bodyEnd,
|
|
395
396
|
enclosingName: enclosing.name,
|
|
396
397
|
captures,
|
|
397
|
-
}
|
|
398
|
+
}
|
|
398
399
|
}
|
|
399
400
|
|
|
400
401
|
/** Nearest enclosing named function wrapping `pos`, with its body span. */
|
|
@@ -402,26 +403,26 @@ function findEnclosingFunctionFull(
|
|
|
402
403
|
source: string,
|
|
403
404
|
pos: number,
|
|
404
405
|
): { name: string; params: string[]; bodyStart: number; bodyEnd: number } | null {
|
|
405
|
-
const before = source.slice(0, pos)
|
|
406
|
-
const re = /(?:^|[\s;{}])(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
|
|
407
|
-
let match: RegExpExecArray | null
|
|
406
|
+
const before = source.slice(0, pos)
|
|
407
|
+
const re = /(?:^|[\s;{}])(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
|
|
408
|
+
let match: RegExpExecArray | null
|
|
408
409
|
let candidate: { name: string; params: string[]; bodyStart: number; bodyEnd: number } | null =
|
|
409
|
-
null
|
|
410
|
+
null
|
|
410
411
|
while ((match = re.exec(before)) !== null) {
|
|
411
|
-
const kwOffset = match[0].search(/async|function/)
|
|
412
|
-
if (!isStatementFunctionHead(source, match.index + kwOffset)) continue
|
|
413
|
-
const bodyOpen = match.index + match[0].length
|
|
414
|
-
const close = matchClosingBrace(source, bodyOpen)
|
|
412
|
+
const kwOffset = match[0].search(/async|function/)
|
|
413
|
+
if (!isStatementFunctionHead(source, match.index + kwOffset)) continue
|
|
414
|
+
const bodyOpen = match.index + match[0].length
|
|
415
|
+
const close = matchClosingBrace(source, bodyOpen)
|
|
415
416
|
if (close > pos) {
|
|
416
417
|
candidate = {
|
|
417
418
|
name: match[1]!,
|
|
418
419
|
params: paramNames(match[2]!),
|
|
419
420
|
bodyStart: bodyOpen,
|
|
420
421
|
bodyEnd: close,
|
|
421
|
-
}
|
|
422
|
+
}
|
|
422
423
|
}
|
|
423
424
|
}
|
|
424
|
-
return candidate
|
|
425
|
+
return candidate
|
|
425
426
|
}
|
|
426
427
|
|
|
427
428
|
/**
|
|
@@ -433,25 +434,25 @@ function enclosingCaptureCandidates(
|
|
|
433
434
|
source: string,
|
|
434
435
|
enclosing: { params: string[]; bodyStart: number; bodyEnd: number },
|
|
435
436
|
): string[] {
|
|
436
|
-
const names = new Set(enclosing.params)
|
|
437
|
-
const body = source.slice(enclosing.bodyStart, enclosing.bodyEnd)
|
|
437
|
+
const names = new Set(enclosing.params)
|
|
438
|
+
const body = source.slice(enclosing.bodyStart, enclosing.bodyEnd)
|
|
438
439
|
for (const match of body.matchAll(/\bfunction\s*\*?\s*([A-Za-z_$][\w$]*)/g)) {
|
|
439
|
-
if (match[1]) names.add(match[1])
|
|
440
|
+
if (match[1]) names.add(match[1])
|
|
440
441
|
}
|
|
441
442
|
for (const match of body.matchAll(/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)/g)) {
|
|
442
|
-
if (match[1]) names.add(match[1])
|
|
443
|
+
if (match[1]) names.add(match[1])
|
|
443
444
|
}
|
|
444
|
-
return [...names]
|
|
445
|
+
return [...names]
|
|
445
446
|
}
|
|
446
447
|
|
|
447
448
|
interface NestedClosure {
|
|
448
|
-
name: string
|
|
449
|
-
params: string
|
|
450
|
-
asyncKeyword: string
|
|
451
|
-
declStart: number
|
|
452
|
-
bodyEnd: number
|
|
453
|
-
enclosingName: string
|
|
454
|
-
captures: string[]
|
|
449
|
+
name: string
|
|
450
|
+
params: string
|
|
451
|
+
asyncKeyword: string
|
|
452
|
+
declStart: number
|
|
453
|
+
bodyEnd: number
|
|
454
|
+
enclosingName: string
|
|
455
|
+
captures: string[]
|
|
455
456
|
}
|
|
456
457
|
|
|
457
458
|
/**
|
|
@@ -461,57 +462,62 @@ interface NestedClosure {
|
|
|
461
462
|
* statics and arrows are left inert, which is safe: they run uncached rather than breaking the build.
|
|
462
463
|
*/
|
|
463
464
|
function findNestedClosure(source: string, directiveIndex: number): NestedClosure | null {
|
|
464
|
-
const prefix = source.slice(0, directiveIndex)
|
|
465
|
+
const prefix = source.slice(0, directiveIndex)
|
|
465
466
|
// Match the nearest `[async ]function NAME(params) {` whose brace opens the
|
|
466
467
|
// directive body (exactly one unclosed brace between it and the directive).
|
|
467
|
-
const declRe = /(?:^|[\s;{}])(async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
|
|
468
|
-
let match: RegExpExecArray | null
|
|
469
|
-
let best: {
|
|
470
|
-
|
|
468
|
+
const declRe = /(?:^|[\s;{}])(async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
|
|
469
|
+
let match: RegExpExecArray | null
|
|
470
|
+
let best: {
|
|
471
|
+
asyncKeyword: string
|
|
472
|
+
name: string
|
|
473
|
+
params: string
|
|
474
|
+
headStart: number
|
|
475
|
+
bodyOpen: number
|
|
476
|
+
} | null = null
|
|
471
477
|
while ((match = declRe.exec(prefix)) !== null) {
|
|
472
478
|
// Clean head start: the position of the `async`/`function` keyword (the
|
|
473
479
|
// match may include a leading whitespace/`{`/`;` separator char).
|
|
474
|
-
const kwOffset = match[0].search(/async|function/)
|
|
475
|
-
const cleanStart = match.index + kwOffset
|
|
476
|
-
if (!isStatementFunctionHead(source, cleanStart)) continue
|
|
477
|
-
const bodyOpen = match.index + match[0].length
|
|
480
|
+
const kwOffset = match[0].search(/async|function/)
|
|
481
|
+
const cleanStart = match.index + kwOffset
|
|
482
|
+
if (!isStatementFunctionHead(source, cleanStart)) continue
|
|
483
|
+
const bodyOpen = match.index + match[0].length // just after `{`
|
|
478
484
|
// Balance braces from bodyOpen to directiveIndex — must be exactly 0 open
|
|
479
485
|
// *within* this function head region, i.e. the directive is the first stmt.
|
|
480
|
-
const between = source.slice(bodyOpen, directiveIndex)
|
|
481
|
-
let balance = 0
|
|
486
|
+
const between = source.slice(bodyOpen, directiveIndex)
|
|
487
|
+
let balance = 0
|
|
482
488
|
for (const ch of between) {
|
|
483
|
-
if (ch === '{') balance += 1
|
|
484
|
-
else if (ch === '}') balance -= 1
|
|
489
|
+
if (ch === '{') balance += 1
|
|
490
|
+
else if (ch === '}') balance -= 1
|
|
485
491
|
}
|
|
486
492
|
if (balance === 0) {
|
|
487
|
-
if (match[2] === 'function') continue
|
|
493
|
+
if (match[2] === 'function') continue
|
|
488
494
|
best = {
|
|
489
495
|
asyncKeyword: match[1] ? 'async ' : '',
|
|
490
496
|
name: match[2]!,
|
|
491
497
|
params: match[3]!.trim(),
|
|
492
498
|
headStart: cleanStart,
|
|
493
499
|
bodyOpen,
|
|
494
|
-
}
|
|
500
|
+
}
|
|
495
501
|
}
|
|
496
502
|
}
|
|
497
|
-
if (!best) return null
|
|
503
|
+
if (!best) return null
|
|
498
504
|
|
|
499
505
|
// The closure must be genuinely nested: there is an enclosing named function
|
|
500
506
|
// whose body contains `best`. Find the nearest such enclosing declaration.
|
|
501
|
-
const enclosing = findEnclosingNamedFunction(source, best.headStart)
|
|
502
|
-
if (!enclosing) return null
|
|
507
|
+
const enclosing = findEnclosingNamedFunction(source, best.headStart)
|
|
508
|
+
if (!enclosing) return null
|
|
503
509
|
|
|
504
510
|
// Find the matching close brace of the closure body.
|
|
505
|
-
const bodyEnd = matchClosingBrace(source, best.bodyOpen)
|
|
506
|
-
if (bodyEnd < 0) return null
|
|
507
|
-
const body = source.slice(best.bodyOpen, bodyEnd)
|
|
511
|
+
const bodyEnd = matchClosingBrace(source, best.bodyOpen)
|
|
512
|
+
if (bodyEnd < 0) return null
|
|
513
|
+
const body = source.slice(best.bodyOpen, bodyEnd)
|
|
508
514
|
|
|
509
515
|
// Captures: enclosing params referenced (whole-word) in the closure body and
|
|
510
516
|
// not shadowed by the closure's own params.
|
|
511
|
-
const closureParams = new Set(paramNames(best.params))
|
|
517
|
+
const closureParams = new Set(paramNames(best.params))
|
|
512
518
|
const captures = enclosing.params.filter(
|
|
513
519
|
param => !closureParams.has(param) && new RegExp(`\\b${escapeIdent(param)}\\b`).test(body),
|
|
514
|
-
)
|
|
520
|
+
)
|
|
515
521
|
|
|
516
522
|
return {
|
|
517
523
|
name: best.name,
|
|
@@ -521,7 +527,7 @@ function findNestedClosure(source: string, directiveIndex: number): NestedClosur
|
|
|
521
527
|
bodyEnd,
|
|
522
528
|
enclosingName: enclosing.name,
|
|
523
529
|
captures,
|
|
524
|
-
}
|
|
530
|
+
}
|
|
525
531
|
}
|
|
526
532
|
|
|
527
533
|
/** Nearest enclosing `[async ]function NAME(params)` whose body wraps `pos`. */
|
|
@@ -529,62 +535,62 @@ function findEnclosingNamedFunction(
|
|
|
529
535
|
source: string,
|
|
530
536
|
pos: number,
|
|
531
537
|
): { name: string; params: string[] } | null {
|
|
532
|
-
const before = source.slice(0, pos)
|
|
533
|
-
const re = /(?:^|[\s;{}])(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
|
|
534
|
-
let match: RegExpExecArray | null
|
|
535
|
-
let candidate: { name: string; params: string[] } | null = null
|
|
538
|
+
const before = source.slice(0, pos)
|
|
539
|
+
const re = /(?:^|[\s;{}])(?:async\s+)?function\s+([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
|
|
540
|
+
let match: RegExpExecArray | null
|
|
541
|
+
let candidate: { name: string; params: string[] } | null = null
|
|
536
542
|
while ((match = re.exec(before)) !== null) {
|
|
537
|
-
const kwOffset = match[0].search(/async|function/)
|
|
538
|
-
if (!isStatementFunctionHead(source, match.index + kwOffset)) continue
|
|
539
|
-
const bodyOpen = match.index + match[0].length
|
|
540
|
-
const close = matchClosingBrace(source, bodyOpen)
|
|
543
|
+
const kwOffset = match[0].search(/async|function/)
|
|
544
|
+
if (!isStatementFunctionHead(source, match.index + kwOffset)) continue
|
|
545
|
+
const bodyOpen = match.index + match[0].length
|
|
546
|
+
const close = matchClosingBrace(source, bodyOpen)
|
|
541
547
|
if (close > pos) {
|
|
542
|
-
candidate = { name: match[1]!, params: paramNames(match[2]!) }
|
|
548
|
+
candidate = { name: match[1]!, params: paramNames(match[2]!) }
|
|
543
549
|
}
|
|
544
550
|
}
|
|
545
|
-
return candidate
|
|
551
|
+
return candidate
|
|
546
552
|
}
|
|
547
553
|
|
|
548
554
|
function isStatementFunctionHead(source: string, index: number): boolean {
|
|
549
|
-
if (/(^|[\s;{}])export\s+(?:default\s+)?$/.test(source.slice(0, index))) return true
|
|
550
|
-
let previous = index - 1
|
|
551
|
-
while (previous >= 0 && /\s/.test(source[previous]!)) previous -= 1
|
|
552
|
-
if (previous < 0) return true
|
|
553
|
-
if (source[previous] === '=') return false
|
|
555
|
+
if (/(^|[\s;{}])export\s+(?:default\s+)?$/.test(source.slice(0, index))) return true
|
|
556
|
+
let previous = index - 1
|
|
557
|
+
while (previous >= 0 && /\s/.test(source[previous]!)) previous -= 1
|
|
558
|
+
if (previous < 0) return true
|
|
559
|
+
if (source[previous] === '=') return false
|
|
554
560
|
if (source[previous] === '{') {
|
|
555
|
-
let beforeBrace = previous - 1
|
|
556
|
-
while (beforeBrace >= 0 && /\s/.test(source[beforeBrace]!)) beforeBrace -= 1
|
|
557
|
-
return source[beforeBrace] !== '='
|
|
561
|
+
let beforeBrace = previous - 1
|
|
562
|
+
while (beforeBrace >= 0 && /\s/.test(source[beforeBrace]!)) beforeBrace -= 1
|
|
563
|
+
return source[beforeBrace] !== '='
|
|
558
564
|
}
|
|
559
|
-
return source[previous] === ';' || source[previous] === '}'
|
|
565
|
+
return source[previous] === ';' || source[previous] === '}'
|
|
560
566
|
}
|
|
561
567
|
|
|
562
568
|
/** Index just past the `}` matching the `{` at `openIndex`, or -1. */
|
|
563
569
|
function matchClosingBrace(source: string, openIndex: number): number {
|
|
564
|
-
let balance = 1
|
|
570
|
+
let balance = 1
|
|
565
571
|
for (let i = openIndex; i < source.length; i += 1) {
|
|
566
|
-
const ch = source[i]
|
|
567
|
-
if (ch === '{') balance += 1
|
|
572
|
+
const ch = source[i]
|
|
573
|
+
if (ch === '{') balance += 1
|
|
568
574
|
else if (ch === '}') {
|
|
569
|
-
balance -= 1
|
|
570
|
-
if (balance === 0) return i
|
|
575
|
+
balance -= 1
|
|
576
|
+
if (balance === 0) return i
|
|
571
577
|
}
|
|
572
578
|
}
|
|
573
|
-
return -1
|
|
579
|
+
return -1
|
|
574
580
|
}
|
|
575
581
|
|
|
576
582
|
function escapeIdent(name: string): string {
|
|
577
|
-
return name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
583
|
+
return name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
578
584
|
}
|
|
579
585
|
|
|
580
586
|
/** Extract simple parameter identifier names from a `(...)` param string. */
|
|
581
587
|
function paramNames(params: string): string[] {
|
|
582
|
-
const names: string[] = []
|
|
588
|
+
const names: string[] = []
|
|
583
589
|
for (const part of params.split(',')) {
|
|
584
|
-
const name = part.trim().split(/[=:\s]/)[0]
|
|
585
|
-
if (name) names.push(name)
|
|
590
|
+
const name = part.trim().split(/[=:\s]/)[0]
|
|
591
|
+
if (name) names.push(name)
|
|
586
592
|
}
|
|
587
|
-
return names
|
|
593
|
+
return names
|
|
588
594
|
}
|
|
589
595
|
|
|
590
596
|
/**
|
|
@@ -593,121 +599,132 @@ function paramNames(params: string): string[] {
|
|
|
593
599
|
* reassignable.
|
|
594
600
|
*/
|
|
595
601
|
function rewriteModuleLevelDirective(source: string, wrapped: Set<string>): string {
|
|
596
|
-
const prologue = /^(?:#![^\n]*\n)?(?:\s|\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)*/.exec(source)
|
|
597
|
-
const offset = prologue?.[0].length ?? 0
|
|
598
|
-
const head = source.slice(offset)
|
|
599
|
-
const directive = /^(['"])use cache(?:\s*:\s*[\w-]+)?\1\s*;?/.exec(head)
|
|
600
|
-
if (!directive) return source
|
|
602
|
+
const prologue = /^(?:#![^\n]*\n)?(?:\s|\/\/[^\n]*\n|\/\*[\s\S]*?\*\/)*/.exec(source)
|
|
603
|
+
const offset = prologue?.[0].length ?? 0
|
|
604
|
+
const head = source.slice(offset)
|
|
605
|
+
const directive = /^(['"])use cache(?:\s*:\s*[\w-]+)?\1\s*;?/.exec(head)
|
|
606
|
+
if (!directive) return source
|
|
601
607
|
|
|
602
|
-
let out = source.slice(0, offset) + head.slice(directive[0].length)
|
|
608
|
+
let out = source.slice(0, offset) + head.slice(directive[0].length)
|
|
603
609
|
|
|
604
610
|
// export [async] function NAME / export [default] [async] function NAME
|
|
605
611
|
for (const match of out.matchAll(
|
|
606
612
|
/\bexport\s+(?:default\s+)?(?:async\s+)?function\s*\*?\s+([A-Za-z_$][\w$]*)/g,
|
|
607
613
|
)) {
|
|
608
|
-
if (match[1]) wrapped.add(match[1])
|
|
614
|
+
if (match[1]) wrapped.add(match[1])
|
|
609
615
|
}
|
|
610
616
|
// export const|let|var NAME = ... (assume function values; const -> let)
|
|
611
617
|
out = out.replace(
|
|
612
618
|
/\bexport\s+(const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/g,
|
|
613
619
|
(full, keyword: string, name: string) => {
|
|
614
|
-
wrapped.add(name)
|
|
615
|
-
return full.replace(keyword, keyword === 'const' ? 'let' : keyword)
|
|
620
|
+
wrapped.add(name)
|
|
621
|
+
return full.replace(keyword, keyword === 'const' ? 'let' : keyword)
|
|
616
622
|
},
|
|
617
|
-
)
|
|
623
|
+
)
|
|
618
624
|
|
|
619
625
|
// `const NAME = ...; export { NAME }` — a re-exported binding under a
|
|
620
626
|
// module-level directive. Wrap each re-exported name declared as a top-level
|
|
621
627
|
// const/let/var function value (flip its const to let so it's reassignable).
|
|
622
|
-
const reExported = new Set<string>()
|
|
628
|
+
const reExported = new Set<string>()
|
|
623
629
|
for (const clause of out.matchAll(/\bexport\s*\{([^}]*)\}/g)) {
|
|
624
630
|
for (const spec of clause[1]!.split(',')) {
|
|
625
|
-
const local = spec
|
|
626
|
-
|
|
631
|
+
const local = spec
|
|
632
|
+
.trim()
|
|
633
|
+
.split(/\s+as\s+/)[0]
|
|
634
|
+
?.trim()
|
|
635
|
+
if (local && /^[A-Za-z_$][\w$]*$/.test(local)) reExported.add(local)
|
|
627
636
|
}
|
|
628
637
|
}
|
|
629
638
|
for (const name of reExported) {
|
|
630
|
-
if (wrapped.has(name)) continue
|
|
631
|
-
const declRe = new RegExp(`(^|[\\n;])\\s*(const|let|var)\\s+${escapeIdent(name)}\\s*=`)
|
|
632
|
-
const declMatch = declRe.exec(out)
|
|
633
|
-
if (!declMatch) continue
|
|
634
|
-
wrapped.add(name)
|
|
639
|
+
if (wrapped.has(name)) continue
|
|
640
|
+
const declRe = new RegExp(`(^|[\\n;])\\s*(const|let|var)\\s+${escapeIdent(name)}\\s*=`)
|
|
641
|
+
const declMatch = declRe.exec(out)
|
|
642
|
+
if (!declMatch) continue
|
|
643
|
+
wrapped.add(name)
|
|
635
644
|
if (declMatch[2] === 'const') {
|
|
636
645
|
out =
|
|
637
646
|
out.slice(0, declMatch.index) +
|
|
638
647
|
declMatch[0].replace(/\bconst\b/, 'let') +
|
|
639
|
-
out.slice(declMatch.index + declMatch[0].length)
|
|
648
|
+
out.slice(declMatch.index + declMatch[0].length)
|
|
640
649
|
}
|
|
641
650
|
}
|
|
642
651
|
|
|
643
652
|
for (const match of out.matchAll(/\bexport\s+default\s+([A-Za-z_$][\w$]*)\s*;?/g)) {
|
|
644
|
-
const name = match[1]
|
|
645
|
-
if (wrapped.has(name)) continue
|
|
646
|
-
const declRe = new RegExp(`(^|[\\n;])\\s*(const|let|var)\\s+${escapeIdent(name)}\\s*=`)
|
|
647
|
-
const declMatch = declRe.exec(out)
|
|
648
|
-
if (!declMatch) continue
|
|
649
|
-
wrapped.add(name)
|
|
653
|
+
const name = match[1]!
|
|
654
|
+
if (wrapped.has(name)) continue
|
|
655
|
+
const declRe = new RegExp(`(^|[\\n;])\\s*(const|let|var)\\s+${escapeIdent(name)}\\s*=`)
|
|
656
|
+
const declMatch = declRe.exec(out)
|
|
657
|
+
if (!declMatch) continue
|
|
658
|
+
wrapped.add(name)
|
|
650
659
|
if (declMatch[2] === 'const') {
|
|
651
660
|
out =
|
|
652
661
|
out.slice(0, declMatch.index) +
|
|
653
662
|
declMatch[0].replace(/\bconst\b/, 'let') +
|
|
654
|
-
out.slice(declMatch.index + declMatch[0].length)
|
|
663
|
+
out.slice(declMatch.index + declMatch[0].length)
|
|
655
664
|
}
|
|
656
665
|
}
|
|
657
|
-
return out
|
|
666
|
+
return out
|
|
658
667
|
}
|
|
659
668
|
|
|
660
|
-
function rewriteObjectMethods(
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
669
|
+
function rewriteObjectMethods(
|
|
670
|
+
source: string,
|
|
671
|
+
idBase: string,
|
|
672
|
+
): { source: string; wrapped: boolean } {
|
|
673
|
+
let out = source
|
|
674
|
+
let wrapped = false
|
|
675
|
+
let searchFrom = 0
|
|
664
676
|
for (;;) {
|
|
665
|
-
const tail = out.slice(searchFrom)
|
|
666
|
-
const directive = directiveText.exec(tail)
|
|
667
|
-
if (!directive) break
|
|
668
|
-
const index = searchFrom + directive.index
|
|
677
|
+
const tail = out.slice(searchFrom)
|
|
678
|
+
const directive = directiveText.exec(tail)
|
|
679
|
+
if (!directive) break
|
|
680
|
+
const index = searchFrom + directive.index
|
|
669
681
|
if (!/\{\s*$/.test(out.slice(0, index))) {
|
|
670
|
-
searchFrom = index + directive[0].length
|
|
671
|
-
continue
|
|
682
|
+
searchFrom = index + directive[0].length
|
|
683
|
+
continue
|
|
672
684
|
}
|
|
673
|
-
const method = findObjectMethod(out, index)
|
|
685
|
+
const method = findObjectMethod(out, index)
|
|
674
686
|
if (!method) {
|
|
675
|
-
searchFrom = index + directive[0].length
|
|
676
|
-
continue
|
|
687
|
+
searchFrom = index + directive[0].length
|
|
688
|
+
continue
|
|
677
689
|
}
|
|
678
|
-
const id = `${idBase}#${method.enclosingName}.${method.name}
|
|
679
|
-
const captureList = method.captures.join(', ')
|
|
680
|
-
const wrappedHead = `${method.name}: ${boundHelper}(${JSON.stringify(id)}, ${method.asyncKeyword}function ${method.name}(${method.params}) {
|
|
690
|
+
const id = `${idBase}#${method.enclosingName}.${method.name}`
|
|
691
|
+
const captureList = method.captures.join(', ')
|
|
692
|
+
const wrappedHead = `${method.name}: ${boundHelper}(${JSON.stringify(id)}, ${method.asyncKeyword}function ${method.name}(${method.params}) {`
|
|
681
693
|
out =
|
|
682
694
|
out.slice(0, method.declStart) +
|
|
683
695
|
wrappedHead +
|
|
684
696
|
out.slice(index + directive[0].length, method.bodyEnd) +
|
|
685
697
|
`}, () => [${captureList}])` +
|
|
686
|
-
out.slice(method.bodyEnd + 1)
|
|
687
|
-
wrapped = true
|
|
688
|
-
searchFrom = method.declStart + wrappedHead.length
|
|
698
|
+
out.slice(method.bodyEnd + 1)
|
|
699
|
+
wrapped = true
|
|
700
|
+
searchFrom = method.declStart + wrappedHead.length
|
|
689
701
|
}
|
|
690
|
-
return { source: out, wrapped }
|
|
702
|
+
return { source: out, wrapped }
|
|
691
703
|
}
|
|
692
704
|
|
|
693
705
|
function findObjectMethod(source: string, directiveIndex: number): NestedClosure | null {
|
|
694
|
-
const prefix = source.slice(0, directiveIndex)
|
|
695
|
-
const methodRe = /(?:^|[,{]\s*)(async\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
|
|
696
|
-
let match: RegExpExecArray | null
|
|
697
|
-
let best: {
|
|
698
|
-
|
|
706
|
+
const prefix = source.slice(0, directiveIndex)
|
|
707
|
+
const methodRe = /(?:^|[,{]\s*)(async\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
|
|
708
|
+
let match: RegExpExecArray | null
|
|
709
|
+
let best: {
|
|
710
|
+
asyncKeyword: string
|
|
711
|
+
name: string
|
|
712
|
+
params: string
|
|
713
|
+
headStart: number
|
|
714
|
+
bodyOpen: number
|
|
715
|
+
} | null = null
|
|
699
716
|
while ((match = methodRe.exec(prefix)) !== null) {
|
|
700
|
-
let beforeMatch = match.index - 1
|
|
701
|
-
while (beforeMatch >= 0 && /\s/.test(source[beforeMatch]!)) beforeMatch -= 1
|
|
702
|
-
if (source[beforeMatch] === '=') continue
|
|
703
|
-
const kwOffset = match[0].search(/async|[A-Za-z_$]/)
|
|
704
|
-
const cleanStart = match.index + kwOffset
|
|
705
|
-
const bodyOpen = match.index + match[0].length
|
|
706
|
-
const between = source.slice(bodyOpen, directiveIndex)
|
|
707
|
-
let balance = 0
|
|
717
|
+
let beforeMatch = match.index - 1
|
|
718
|
+
while (beforeMatch >= 0 && /\s/.test(source[beforeMatch]!)) beforeMatch -= 1
|
|
719
|
+
if (source[beforeMatch] === '=') continue
|
|
720
|
+
const kwOffset = match[0].search(/async|[A-Za-z_$]/)
|
|
721
|
+
const cleanStart = match.index + kwOffset
|
|
722
|
+
const bodyOpen = match.index + match[0].length
|
|
723
|
+
const between = source.slice(bodyOpen, directiveIndex)
|
|
724
|
+
let balance = 0
|
|
708
725
|
for (const ch of between) {
|
|
709
|
-
if (ch === '{') balance += 1
|
|
710
|
-
else if (ch === '}') balance -= 1
|
|
726
|
+
if (ch === '{') balance += 1
|
|
727
|
+
else if (ch === '}') balance -= 1
|
|
711
728
|
}
|
|
712
729
|
if (balance === 0) {
|
|
713
730
|
best = {
|
|
@@ -716,19 +733,19 @@ function findObjectMethod(source: string, directiveIndex: number): NestedClosure
|
|
|
716
733
|
params: match[3]!.trim(),
|
|
717
734
|
headStart: cleanStart,
|
|
718
735
|
bodyOpen,
|
|
719
|
-
}
|
|
736
|
+
}
|
|
720
737
|
}
|
|
721
738
|
}
|
|
722
|
-
if (!best) return null
|
|
723
|
-
const enclosing = findEnclosingNamedFunction(source, best.headStart)
|
|
724
|
-
if (!enclosing) return null
|
|
725
|
-
const bodyEnd = matchClosingBrace(source, best.bodyOpen)
|
|
726
|
-
if (bodyEnd < 0) return null
|
|
727
|
-
const body = source.slice(best.bodyOpen, bodyEnd)
|
|
728
|
-
const methodParams = new Set(paramNames(best.params))
|
|
739
|
+
if (!best) return null
|
|
740
|
+
const enclosing = findEnclosingNamedFunction(source, best.headStart)
|
|
741
|
+
if (!enclosing) return null
|
|
742
|
+
const bodyEnd = matchClosingBrace(source, best.bodyOpen)
|
|
743
|
+
if (bodyEnd < 0) return null
|
|
744
|
+
const body = source.slice(best.bodyOpen, bodyEnd)
|
|
745
|
+
const methodParams = new Set(paramNames(best.params))
|
|
729
746
|
const captures = enclosing.params.filter(
|
|
730
747
|
param => !methodParams.has(param) && new RegExp(`\\b${escapeIdent(param)}\\b`).test(body),
|
|
731
|
-
)
|
|
748
|
+
)
|
|
732
749
|
return {
|
|
733
750
|
name: best.name,
|
|
734
751
|
params: best.params,
|
|
@@ -737,59 +754,67 @@ function findObjectMethod(source: string, directiveIndex: number): NestedClosure
|
|
|
737
754
|
bodyEnd,
|
|
738
755
|
enclosingName: enclosing.name,
|
|
739
756
|
captures,
|
|
740
|
-
}
|
|
757
|
+
}
|
|
741
758
|
}
|
|
742
759
|
|
|
743
|
-
function rewriteStaticClassMethods(
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
760
|
+
function rewriteStaticClassMethods(
|
|
761
|
+
source: string,
|
|
762
|
+
idBase: string,
|
|
763
|
+
): { source: string; wrapped: boolean } {
|
|
764
|
+
let out = source
|
|
765
|
+
let wrapped = false
|
|
766
|
+
let searchFrom = 0
|
|
747
767
|
for (;;) {
|
|
748
|
-
const tail = out.slice(searchFrom)
|
|
749
|
-
const directive = directiveText.exec(tail)
|
|
750
|
-
if (!directive) break
|
|
751
|
-
const index = searchFrom + directive.index
|
|
768
|
+
const tail = out.slice(searchFrom)
|
|
769
|
+
const directive = directiveText.exec(tail)
|
|
770
|
+
if (!directive) break
|
|
771
|
+
const index = searchFrom + directive.index
|
|
752
772
|
if (!/\{\s*$/.test(out.slice(0, index))) {
|
|
753
|
-
searchFrom = index + directive[0].length
|
|
754
|
-
continue
|
|
773
|
+
searchFrom = index + directive[0].length
|
|
774
|
+
continue
|
|
755
775
|
}
|
|
756
|
-
const method = findStaticClassMethod(out, index)
|
|
776
|
+
const method = findStaticClassMethod(out, index)
|
|
757
777
|
if (!method) {
|
|
758
|
-
searchFrom = index + directive[0].length
|
|
759
|
-
continue
|
|
778
|
+
searchFrom = index + directive[0].length
|
|
779
|
+
continue
|
|
760
780
|
}
|
|
761
|
-
const id = `${idBase}#${method.className}.${method.name}
|
|
762
|
-
const wrappedHead = `static ${method.name} = __pnextUseCache(${JSON.stringify(id)}, ${method.asyncKeyword}function ${method.name}(${method.params}) {
|
|
781
|
+
const id = `${idBase}#${method.className}.${method.name}`
|
|
782
|
+
const wrappedHead = `static ${method.name} = __pnextUseCache(${JSON.stringify(id)}, ${method.asyncKeyword}function ${method.name}(${method.params}) {`
|
|
763
783
|
out =
|
|
764
784
|
out.slice(0, method.declStart) +
|
|
765
785
|
wrappedHead +
|
|
766
786
|
out.slice(index + directive[0].length, method.bodyEnd) +
|
|
767
787
|
`})` +
|
|
768
|
-
out.slice(method.bodyEnd + 1)
|
|
769
|
-
wrapped = true
|
|
770
|
-
searchFrom = method.declStart + wrappedHead.length
|
|
788
|
+
out.slice(method.bodyEnd + 1)
|
|
789
|
+
wrapped = true
|
|
790
|
+
searchFrom = method.declStart + wrappedHead.length
|
|
771
791
|
}
|
|
772
|
-
return { source: out, wrapped }
|
|
792
|
+
return { source: out, wrapped }
|
|
773
793
|
}
|
|
774
794
|
|
|
775
795
|
function findStaticClassMethod(
|
|
776
796
|
source: string,
|
|
777
797
|
directiveIndex: number,
|
|
778
798
|
): (NestedClosure & { className: string }) | null {
|
|
779
|
-
const prefix = source.slice(0, directiveIndex)
|
|
780
|
-
const methodRe = /(?:^|[\s;{}])static\s+(async\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
|
|
781
|
-
let match: RegExpExecArray | null
|
|
782
|
-
let best: {
|
|
783
|
-
|
|
799
|
+
const prefix = source.slice(0, directiveIndex)
|
|
800
|
+
const methodRe = /(?:^|[\s;{}])static\s+(async\s+)?([A-Za-z_$][\w$]*)\s*\(([^)]*)\)\s*\{/g
|
|
801
|
+
let match: RegExpExecArray | null
|
|
802
|
+
let best: {
|
|
803
|
+
asyncKeyword: string
|
|
804
|
+
name: string
|
|
805
|
+
params: string
|
|
806
|
+
headStart: number
|
|
807
|
+
bodyOpen: number
|
|
808
|
+
} | null = null
|
|
784
809
|
while ((match = methodRe.exec(prefix)) !== null) {
|
|
785
|
-
const kwOffset = match[0].indexOf('static')
|
|
786
|
-
const cleanStart = match.index + kwOffset
|
|
787
|
-
const bodyOpen = match.index + match[0].length
|
|
788
|
-
const between = source.slice(bodyOpen, directiveIndex)
|
|
789
|
-
let balance = 0
|
|
810
|
+
const kwOffset = match[0].indexOf('static')
|
|
811
|
+
const cleanStart = match.index + kwOffset
|
|
812
|
+
const bodyOpen = match.index + match[0].length
|
|
813
|
+
const between = source.slice(bodyOpen, directiveIndex)
|
|
814
|
+
let balance = 0
|
|
790
815
|
for (const ch of between) {
|
|
791
|
-
if (ch === '{') balance += 1
|
|
792
|
-
else if (ch === '}') balance -= 1
|
|
816
|
+
if (ch === '{') balance += 1
|
|
817
|
+
else if (ch === '}') balance -= 1
|
|
793
818
|
}
|
|
794
819
|
if (balance === 0) {
|
|
795
820
|
best = {
|
|
@@ -798,14 +823,14 @@ function findStaticClassMethod(
|
|
|
798
823
|
params: match[3]!.trim(),
|
|
799
824
|
headStart: cleanStart,
|
|
800
825
|
bodyOpen,
|
|
801
|
-
}
|
|
826
|
+
}
|
|
802
827
|
}
|
|
803
828
|
}
|
|
804
|
-
if (!best) return null
|
|
805
|
-
const className = findEnclosingClassName(source, best.headStart)
|
|
806
|
-
if (!className) return null
|
|
807
|
-
const bodyEnd = matchClosingBrace(source, best.bodyOpen)
|
|
808
|
-
if (bodyEnd < 0) return null
|
|
829
|
+
if (!best) return null
|
|
830
|
+
const className = findEnclosingClassName(source, best.headStart)
|
|
831
|
+
if (!className) return null
|
|
832
|
+
const bodyEnd = matchClosingBrace(source, best.bodyOpen)
|
|
833
|
+
if (bodyEnd < 0) return null
|
|
809
834
|
return {
|
|
810
835
|
name: best.name,
|
|
811
836
|
params: best.params,
|
|
@@ -815,20 +840,20 @@ function findStaticClassMethod(
|
|
|
815
840
|
enclosingName: className,
|
|
816
841
|
className,
|
|
817
842
|
captures: [],
|
|
818
|
-
}
|
|
843
|
+
}
|
|
819
844
|
}
|
|
820
845
|
|
|
821
846
|
function findEnclosingClassName(source: string, pos: number): string | null {
|
|
822
|
-
const before = source.slice(0, pos)
|
|
823
|
-
const re = /(?:^|[\s;{}])class\s+([A-Za-z_$][\w$]*)[^{]*\{/g
|
|
824
|
-
let match: RegExpExecArray | null
|
|
825
|
-
let candidate: string | null = null
|
|
847
|
+
const before = source.slice(0, pos)
|
|
848
|
+
const re = /(?:^|[\s;{}])class\s+([A-Za-z_$][\w$]*)[^{]*\{/g
|
|
849
|
+
let match: RegExpExecArray | null
|
|
850
|
+
let candidate: string | null = null
|
|
826
851
|
while ((match = re.exec(before)) !== null) {
|
|
827
|
-
const bodyOpen = match.index + match[0].length
|
|
828
|
-
const close = matchClosingBrace(source, bodyOpen)
|
|
829
|
-
if (close > pos) candidate = match[1]
|
|
852
|
+
const bodyOpen = match.index + match[0].length
|
|
853
|
+
const close = matchClosingBrace(source, bodyOpen)
|
|
854
|
+
if (close > pos) candidate = match[1]!
|
|
830
855
|
}
|
|
831
|
-
return candidate
|
|
856
|
+
return candidate
|
|
832
857
|
}
|
|
833
858
|
|
|
834
859
|
/**
|
|
@@ -843,36 +868,36 @@ function rewriteFunctionLevelDirectives(
|
|
|
843
868
|
wrapped: Set<string>,
|
|
844
869
|
privateWrapped: Set<string>,
|
|
845
870
|
): string {
|
|
846
|
-
let out = source
|
|
847
|
-
let searchFrom = 0
|
|
871
|
+
let out = source
|
|
872
|
+
let searchFrom = 0
|
|
848
873
|
for (;;) {
|
|
849
|
-
const tail = out.slice(searchFrom)
|
|
850
|
-
const directive = directiveText.exec(tail)
|
|
851
|
-
if (!directive) break
|
|
852
|
-
const index = searchFrom + directive.index
|
|
853
|
-
const prefix = out.slice(0, index)
|
|
874
|
+
const tail = out.slice(searchFrom)
|
|
875
|
+
const directive = directiveText.exec(tail)
|
|
876
|
+
if (!directive) break
|
|
877
|
+
const index = searchFrom + directive.index
|
|
878
|
+
const prefix = out.slice(0, index)
|
|
854
879
|
|
|
855
880
|
// Only a directive when it opens a function body; anything else is a plain
|
|
856
881
|
// string literal.
|
|
857
|
-
const declaration = opensFunctionBody(prefix) ? findEnclosingDeclaration(prefix) : null
|
|
882
|
+
const declaration = opensFunctionBody(prefix) ? findEnclosingDeclaration(prefix) : null
|
|
858
883
|
if (!declaration) {
|
|
859
|
-
searchFrom = index + directive[0].length
|
|
860
|
-
continue
|
|
884
|
+
searchFrom = index + directive[0].length
|
|
885
|
+
continue
|
|
861
886
|
}
|
|
862
887
|
|
|
863
|
-
let head = prefix
|
|
888
|
+
let head = prefix
|
|
864
889
|
if (declaration.constKeywordIndex !== undefined) {
|
|
865
890
|
head =
|
|
866
891
|
prefix.slice(0, declaration.constKeywordIndex) +
|
|
867
892
|
'let ' +
|
|
868
|
-
prefix.slice(declaration.constKeywordIndex + 'const'.length)
|
|
893
|
+
prefix.slice(declaration.constKeywordIndex + 'const'.length)
|
|
869
894
|
}
|
|
870
|
-
wrapped.add(declaration.name)
|
|
871
|
-
if (directive[2] === 'private') privateWrapped.add(declaration.name)
|
|
872
|
-
out = head + out.slice(index + directive[0].length)
|
|
873
|
-
searchFrom = index
|
|
895
|
+
wrapped.add(declaration.name)
|
|
896
|
+
if (directive[2] === 'private') privateWrapped.add(declaration.name)
|
|
897
|
+
out = head + out.slice(index + directive[0].length)
|
|
898
|
+
searchFrom = index
|
|
874
899
|
}
|
|
875
|
-
return out
|
|
900
|
+
return out
|
|
876
901
|
}
|
|
877
902
|
|
|
878
903
|
/**
|
|
@@ -884,78 +909,78 @@ function rewriteFunctionLevelDirectives(
|
|
|
884
909
|
function opensFunctionBody(prefix: string): boolean {
|
|
885
910
|
// The match is a comment's own text, not a directive, when `prefix` ends
|
|
886
911
|
// inside a comment — e.g. a line explaining `'use cache'` semantics.
|
|
887
|
-
if (prefix.includes('//', prefix.lastIndexOf('\n') + 1)) return false
|
|
888
|
-
const blockOpen = prefix.lastIndexOf('/*')
|
|
889
|
-
if (blockOpen !== -1 && !prefix.includes('*/', blockOpen)) return false
|
|
912
|
+
if (prefix.includes('//', prefix.lastIndexOf('\n') + 1)) return false
|
|
913
|
+
const blockOpen = prefix.lastIndexOf('/*')
|
|
914
|
+
if (blockOpen !== -1 && !prefix.includes('*/', blockOpen)) return false
|
|
890
915
|
|
|
891
|
-
let end = prefix.length
|
|
916
|
+
let end = prefix.length
|
|
892
917
|
for (;;) {
|
|
893
|
-
end = prefix.slice(0, end).replace(/\s+$/, '').length
|
|
894
|
-
if (end === 0) return false
|
|
918
|
+
end = prefix.slice(0, end).replace(/\s+$/, '').length
|
|
919
|
+
if (end === 0) return false
|
|
895
920
|
if (prefix.startsWith('*/', end - 2)) {
|
|
896
|
-
const open = prefix.lastIndexOf('/*', end - 2)
|
|
897
|
-
if (open === -1) return false
|
|
898
|
-
end = open
|
|
899
|
-
continue
|
|
921
|
+
const open = prefix.lastIndexOf('/*', end - 2)
|
|
922
|
+
if (open === -1) return false
|
|
923
|
+
end = open
|
|
924
|
+
continue
|
|
900
925
|
}
|
|
901
|
-
const comment = prefix.indexOf('//', prefix.lastIndexOf('\n', end - 1) + 1)
|
|
926
|
+
const comment = prefix.indexOf('//', prefix.lastIndexOf('\n', end - 1) + 1)
|
|
902
927
|
if (comment !== -1 && comment < end) {
|
|
903
|
-
end = comment
|
|
904
|
-
continue
|
|
928
|
+
end = comment
|
|
929
|
+
continue
|
|
905
930
|
}
|
|
906
|
-
return prefix[end - 1] === '{'
|
|
931
|
+
return prefix[end - 1] === '{'
|
|
907
932
|
}
|
|
908
933
|
}
|
|
909
934
|
|
|
910
935
|
interface EnclosingDeclaration {
|
|
911
|
-
name: string
|
|
936
|
+
name: string
|
|
912
937
|
/** Index of a `const` keyword to rewrite to `let`, when applicable. */
|
|
913
|
-
constKeywordIndex?: number
|
|
938
|
+
constKeywordIndex?: number
|
|
914
939
|
}
|
|
915
940
|
|
|
916
941
|
function findEnclosingDeclaration(prefix: string): EnclosingDeclaration | null {
|
|
917
|
-
const candidates: { name: string; end: number; constKeywordIndex?: number }[] = []
|
|
942
|
+
const candidates: { name: string; end: number; constKeywordIndex?: number }[] = []
|
|
918
943
|
|
|
919
944
|
// Only top-level declarations (line starts at column 0): a nested closure's
|
|
920
945
|
// binding is function-scoped and cannot be reassigned from module level —
|
|
921
946
|
// those run uncached (the directive string is inert).
|
|
922
|
-
const topLevel = (index: number) => index === 0 || prefix[index - 1] === '\n'
|
|
947
|
+
const topLevel = (index: number) => index === 0 || prefix[index - 1] === '\n'
|
|
923
948
|
|
|
924
|
-
const fnPattern = /(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s*\*?\s+([A-Za-z_$][\w$]*)/g
|
|
949
|
+
const fnPattern = /(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s*\*?\s+([A-Za-z_$][\w$]*)/g
|
|
925
950
|
for (const match of prefix.matchAll(fnPattern)) {
|
|
926
951
|
if (match[1] && topLevel(match.index)) {
|
|
927
|
-
candidates.push({ name: match[1], end: match.index + match[0].length })
|
|
952
|
+
candidates.push({ name: match[1], end: match.index + match[0].length })
|
|
928
953
|
}
|
|
929
954
|
}
|
|
930
|
-
const bindingPattern = /(?:export\s+)?(const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/g
|
|
955
|
+
const bindingPattern = /(?:export\s+)?(const|let|var)\s+([A-Za-z_$][\w$]*)\s*=/g
|
|
931
956
|
for (const match of prefix.matchAll(bindingPattern)) {
|
|
932
|
-
if (!match[2] || !topLevel(match.index)) continue
|
|
957
|
+
if (!match[2] || !topLevel(match.index)) continue
|
|
933
958
|
candidates.push({
|
|
934
959
|
name: match[2],
|
|
935
960
|
end: match.index + match[0].length,
|
|
936
961
|
...(match[1] === 'const'
|
|
937
962
|
? { constKeywordIndex: match.index + match[0].indexOf('const') }
|
|
938
963
|
: {}),
|
|
939
|
-
})
|
|
964
|
+
})
|
|
940
965
|
}
|
|
941
|
-
if (candidates.length === 0) return null
|
|
942
|
-
candidates.sort((a, b) => a.end - b.end)
|
|
943
|
-
const nearest = candidates[candidates.length - 1]
|
|
966
|
+
if (candidates.length === 0) return null
|
|
967
|
+
candidates.sort((a, b) => a.end - b.end)
|
|
968
|
+
const nearest = candidates[candidates.length - 1]!
|
|
944
969
|
|
|
945
970
|
// The span between the declaration head and the directive must look like a
|
|
946
971
|
// signature followed by the opening body brace: exactly one unclosed '{'.
|
|
947
|
-
const span = prefix.slice(nearest.end)
|
|
948
|
-
let balance = 0
|
|
972
|
+
const span = prefix.slice(nearest.end)
|
|
973
|
+
let balance = 0
|
|
949
974
|
for (const char of span) {
|
|
950
|
-
if (char === '{') balance += 1
|
|
951
|
-
else if (char === '}') balance -= 1
|
|
975
|
+
if (char === '{') balance += 1
|
|
976
|
+
else if (char === '}') balance -= 1
|
|
952
977
|
}
|
|
953
|
-
if (balance !== 1) return null
|
|
978
|
+
if (balance !== 1) return null
|
|
954
979
|
|
|
955
980
|
return {
|
|
956
981
|
name: nearest.name,
|
|
957
982
|
...(nearest.constKeywordIndex !== undefined
|
|
958
983
|
? { constKeywordIndex: nearest.constKeywordIndex }
|
|
959
984
|
: {}),
|
|
960
|
-
}
|
|
985
|
+
}
|
|
961
986
|
}
|