@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
package/src/client/chunk-fold.ts
CHANGED
|
@@ -4,28 +4,28 @@
|
|
|
4
4
|
* an esbuild re-bundle of a renaming barrel plus an import rewrite in every
|
|
5
5
|
* chunk that referenced a member.
|
|
6
6
|
*/
|
|
7
|
-
import { createHash } from 'node:crypto'
|
|
8
|
-
import { existsSync } from 'node:fs'
|
|
9
|
-
import { unlink } from 'node:fs/promises'
|
|
10
|
-
import path from 'node:path'
|
|
11
|
-
import { build, type BuildOptions, type Metafile } from 'esbuild'
|
|
12
|
-
import { clientProfile } from './profile'
|
|
13
|
-
import { readText, writeText } from '../utils/fs'
|
|
14
|
-
import { escapeRegex } from '../utils/
|
|
7
|
+
import { createHash } from 'node:crypto'
|
|
8
|
+
import { existsSync } from 'node:fs'
|
|
9
|
+
import { unlink } from 'node:fs/promises'
|
|
10
|
+
import path from 'node:path'
|
|
11
|
+
import { build, type BuildOptions, type Metafile } from 'esbuild'
|
|
12
|
+
import { clientProfile } from './profile'
|
|
13
|
+
import { readText, writeText } from '../utils/fs'
|
|
14
|
+
import { escapeRegex } from '../utils/code'
|
|
15
15
|
|
|
16
16
|
/** Unlinking an artifact a concurrent build already removed is not news. */
|
|
17
17
|
function ignoreMissing() {
|
|
18
|
-
return undefined
|
|
18
|
+
return undefined
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/** Import/export forms the rewrite understands; anything else vetoes the fold. */
|
|
22
|
-
const namedClause = String.raw`\{([^}]*)\}
|
|
22
|
+
const namedClause = String.raw`\{([^}]*)\}`
|
|
23
23
|
|
|
24
24
|
interface FoldOptions {
|
|
25
|
-
outDir: string
|
|
26
|
-
metafile: Metafile
|
|
25
|
+
outDir: string
|
|
26
|
+
metafile: Metafile
|
|
27
27
|
/** esbuild flags of the build being folded, so the merge is emitted the same way. */
|
|
28
|
-
buildOptions: Pick<BuildOptions, 'minify' | 'sourcemap' | 'target' | 'format'
|
|
28
|
+
buildOptions: Pick<BuildOptions, 'minify' | 'sourcemap' | 'target' | 'format'>
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
@@ -34,11 +34,11 @@ interface FoldOptions {
|
|
|
34
34
|
*/
|
|
35
35
|
export async function foldInitialChunks(options: FoldOptions): Promise<Metafile> {
|
|
36
36
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
37
|
-
if (process.env.PNEXT_CHUNK_FOLD === '0') return options.metafile
|
|
37
|
+
if (process.env.PNEXT_CHUNK_FOLD === '0') return options.metafile
|
|
38
38
|
// The cost this removes is compressed-transfer only (a gzip header and
|
|
39
39
|
// dictionary reset per chunk), so the pass rides the minify flag.
|
|
40
|
-
if (!options.buildOptions.minify) return options.metafile
|
|
41
|
-
return foldChunks(options)
|
|
40
|
+
if (!options.buildOptions.minify) return options.metafile
|
|
41
|
+
return foldChunks(options)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -47,36 +47,36 @@ export async function foldInitialChunks(options: FoldOptions): Promise<Metafile>
|
|
|
47
47
|
* disk have diverged, so it rethrows. `PNEXT_CHUNK_FOLD=strict` throws for both.
|
|
48
48
|
*/
|
|
49
49
|
async function foldChunks({ outDir, metafile, buildOptions }: FoldOptions): Promise<Metafile> {
|
|
50
|
-
let folded = metafile
|
|
51
|
-
const groups = clientProfile.time('foldGroups', () => coLoadGroups(metafile))
|
|
52
|
-
clientProfile.count('foldGroup#', groups.length)
|
|
53
|
-
clientProfile.count('foldOutput#', Object.keys(metafile.outputs).length)
|
|
54
|
-
if (groups.length === 0) return folded
|
|
50
|
+
let folded = metafile
|
|
51
|
+
const groups = clientProfile.time('foldGroups', () => coLoadGroups(metafile))
|
|
52
|
+
clientProfile.count('foldGroup#', groups.length)
|
|
53
|
+
clientProfile.count('foldOutput#', Object.keys(metafile.outputs).length)
|
|
54
|
+
if (groups.length === 0) return folded
|
|
55
55
|
// Output text is read once and kept in step with what each group writes, and
|
|
56
56
|
// the same state indexes specifiers — so a group visits only the files naming
|
|
57
57
|
// one of its members instead of re-reading and re-filtering every output.
|
|
58
|
-
const state = await clientProfile.timeAsync('foldRead', () => createFoldState(outDir, metafile))
|
|
58
|
+
const state = await clientProfile.timeAsync('foldRead', () => createFoldState(outDir, metafile))
|
|
59
59
|
for (const group of groups) {
|
|
60
|
-
const mutation = { started: false }
|
|
60
|
+
const mutation = { started: false }
|
|
61
61
|
try {
|
|
62
|
-
folded = await foldGroup({ outDir, metafile: folded, buildOptions }, group, mutation, state)
|
|
62
|
+
folded = await foldGroup({ outDir, metafile: folded, buildOptions }, group, mutation, state)
|
|
63
63
|
} catch (error) {
|
|
64
|
-
if (mutation.started) throw error
|
|
65
|
-
reportFoldFailure(group, error)
|
|
66
|
-
break
|
|
64
|
+
if (mutation.started) throw error
|
|
65
|
+
reportFoldFailure(group, error)
|
|
66
|
+
break
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
-
return folded
|
|
69
|
+
return folded
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
function reportFoldFailure(group: string[], error: unknown) {
|
|
73
|
-
const names = group.map(member => path.basename(member)).join(', ')
|
|
74
|
-
const message = error instanceof Error ? error.message : String(error)
|
|
73
|
+
const names = group.map(member => path.basename(member)).join(', ')
|
|
74
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
75
75
|
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
|
76
76
|
if (process.env.PNEXT_CHUNK_FOLD === 'strict') {
|
|
77
|
-
throw new Error(`client chunk fold failed for [${names}]: ${message}`)
|
|
77
|
+
throw new Error(`client chunk fold failed for [${names}]: ${message}`)
|
|
78
78
|
}
|
|
79
|
-
console.warn(`pnext: client chunk fold skipped [${names}] — ${message}`)
|
|
79
|
+
console.warn(`pnext: client chunk fold skipped [${names}] — ${message}`)
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
async function foldGroup(
|
|
@@ -85,65 +85,65 @@ async function foldGroup(
|
|
|
85
85
|
mutation: { started: boolean },
|
|
86
86
|
state: FoldState,
|
|
87
87
|
): Promise<Metafile> {
|
|
88
|
-
if (members.length < 2) return metafile
|
|
88
|
+
if (members.length < 2) return metafile
|
|
89
89
|
|
|
90
|
-
const chunksDir = path.join(outDir, 'chunks')
|
|
91
|
-
const memberNames = members.map(output => path.basename(output))
|
|
92
|
-
if (memberNames.some(name => !existsSync(path.join(chunksDir, name)))) return metafile
|
|
90
|
+
const chunksDir = path.join(outDir, 'chunks')
|
|
91
|
+
const memberNames = members.map(output => path.basename(output))
|
|
92
|
+
if (memberNames.some(name => !existsSync(path.join(chunksDir, name)))) return metafile
|
|
93
93
|
|
|
94
|
-
const sources = new Map<string, string>()
|
|
95
|
-
for (const name of memberNames) sources.set(name, await readText(path.join(chunksDir, name)))
|
|
94
|
+
const sources = new Map<string, string>()
|
|
95
|
+
for (const name of memberNames) sources.set(name, await readText(path.join(chunksDir, name)))
|
|
96
96
|
|
|
97
|
-
const aliases = memberAliases(memberNames, sources)
|
|
98
|
-
if (!aliases) return metafile
|
|
97
|
+
const aliases = memberAliases(memberNames, sources)
|
|
98
|
+
if (!aliases) return metafile
|
|
99
99
|
|
|
100
100
|
// Every file that referenced a member has to be rewritten; if any of them
|
|
101
101
|
// uses a form the rewriter can't express (`import * as`, `export *`, a bare
|
|
102
102
|
// dynamic `import()` of a member), the fold is abandoned whole.
|
|
103
|
-
const rewrites = new Map<string, string>()
|
|
104
|
-
const rewrite = memberRewriter(memberNames, aliases, chunksDir)
|
|
103
|
+
const rewrites = new Map<string, string>()
|
|
104
|
+
const rewrite = memberRewriter(memberNames, aliases, chunksDir)
|
|
105
105
|
const rewritable = clientProfile.time('foldRewrite', () => {
|
|
106
106
|
for (const file of state.referrersOf(memberNames)) {
|
|
107
|
-
if (memberNames.includes(path.basename(file))) continue
|
|
108
|
-
const output = state.outputs.get(file)
|
|
109
|
-
if (!output) continue
|
|
110
|
-
const next = rewrite(output.text, file)
|
|
111
|
-
if (next === null) return false
|
|
112
|
-
if (next !== output.text) rewrites.set(file, next)
|
|
107
|
+
if (memberNames.includes(path.basename(file))) continue
|
|
108
|
+
const output = state.outputs.get(file)
|
|
109
|
+
if (!output) continue
|
|
110
|
+
const next = rewrite(output.text, file)
|
|
111
|
+
if (next === null) return false
|
|
112
|
+
if (next !== output.text) rewrites.set(file, next)
|
|
113
113
|
}
|
|
114
|
-
return true
|
|
115
|
-
})
|
|
116
|
-
if (!rewritable) return metafile
|
|
114
|
+
return true
|
|
115
|
+
})
|
|
116
|
+
if (!rewritable) return metafile
|
|
117
117
|
|
|
118
118
|
const merged = await clientProfile.timeAsync('foldBuild', () =>
|
|
119
119
|
buildMergedChunk(chunksDir, memberNames, aliases, buildOptions),
|
|
120
|
-
)
|
|
121
|
-
if (!merged) return metafile
|
|
120
|
+
)
|
|
121
|
+
if (!merged) return metafile
|
|
122
122
|
|
|
123
123
|
// Read-only up to here, so failing is skippable. From the first rewrite on the
|
|
124
124
|
// metafile and the files on disk must be kept in step, so failures are fatal.
|
|
125
|
-
mutation.started = true
|
|
125
|
+
mutation.started = true
|
|
126
126
|
|
|
127
127
|
// The rewrites were produced against a placeholder href — the merged chunk's
|
|
128
128
|
// name is its own content hash and so isn't known until it is built.
|
|
129
129
|
await Promise.all(
|
|
130
130
|
[...rewrites].map(([file, source]) => {
|
|
131
|
-
const next = source.replaceAll(MERGED_PLACEHOLDER, merged.name)
|
|
132
|
-
state.set(file, foldOutput(next))
|
|
133
|
-
return writeText(file, next)
|
|
131
|
+
const next = source.replaceAll(MERGED_PLACEHOLDER, merged.name)
|
|
132
|
+
state.set(file, foldOutput(next))
|
|
133
|
+
return writeText(file, next)
|
|
134
134
|
}),
|
|
135
|
-
)
|
|
135
|
+
)
|
|
136
136
|
await Promise.all(
|
|
137
137
|
memberNames.flatMap(name => [
|
|
138
138
|
unlink(path.join(chunksDir, name)).catch(ignoreMissing),
|
|
139
139
|
unlink(path.join(chunksDir, `${name}.map`)).catch(ignoreMissing),
|
|
140
140
|
]),
|
|
141
|
-
)
|
|
142
|
-
for (const name of memberNames) state.remove(path.join(chunksDir, name))
|
|
141
|
+
)
|
|
142
|
+
for (const name of memberNames) state.remove(path.join(chunksDir, name))
|
|
143
143
|
// A later group can reference this merged chunk, and it is what the previous
|
|
144
144
|
// per-group re-walk picked up off disk; index it the same way.
|
|
145
|
-
state.set(path.join(chunksDir, merged.name), foldOutput(merged.text))
|
|
146
|
-
return mergeMetafile(metafile, members, path.join(chunksDir, merged.name), merged.bytes)
|
|
145
|
+
state.set(path.join(chunksDir, merged.name), foldOutput(merged.text))
|
|
146
|
+
return mergeMetafile(metafile, members, path.join(chunksDir, merged.name), merged.bytes)
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/**
|
|
@@ -153,10 +153,10 @@ async function foldGroup(
|
|
|
153
153
|
* what a lazily loaded island's CSS/runtime seam resolves against.
|
|
154
154
|
*/
|
|
155
155
|
function coLoadGroups(metafile: Metafile) {
|
|
156
|
-
const dynamicTargets = new Set<string>()
|
|
156
|
+
const dynamicTargets = new Set<string>()
|
|
157
157
|
for (const output of Object.values(metafile.outputs)) {
|
|
158
158
|
for (const imported of output.imports ?? []) {
|
|
159
|
-
if (imported.kind === 'dynamic-import') dynamicTargets.add(imported.path)
|
|
159
|
+
if (imported.kind === 'dynamic-import') dynamicTargets.add(imported.path)
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
// esbuild records an `entryPoint` for the chunk behind every `import()` too,
|
|
@@ -164,19 +164,19 @@ function coLoadGroups(metafile: Metafile) {
|
|
|
164
164
|
// route entry.
|
|
165
165
|
const entries = Object.keys(metafile.outputs).filter(
|
|
166
166
|
file => metafile.outputs[file]?.entryPoint && !dynamicTargets.has(file),
|
|
167
|
-
)
|
|
168
|
-
if (entries.length === 0) return []
|
|
167
|
+
)
|
|
168
|
+
if (entries.length === 0) return []
|
|
169
169
|
|
|
170
|
-
const available = availability(metafile, entries, dynamicTargets)
|
|
171
|
-
const roots = [...available.keys()]
|
|
172
|
-
const groups = new Map<string, string[]>()
|
|
170
|
+
const available = availability(metafile, entries, dynamicTargets)
|
|
171
|
+
const roots = [...available.keys()]
|
|
172
|
+
const groups = new Map<string, string[]>()
|
|
173
173
|
for (const file of Object.keys(metafile.outputs)) {
|
|
174
|
-
if (!file.endsWith('.js')) continue
|
|
175
|
-
if (entries.includes(file) || dynamicTargets.has(file)) continue
|
|
176
|
-
const mask = roots.map(root => (available.get(root)!.has(file) ? '1' : '0')).join('')
|
|
174
|
+
if (!file.endsWith('.js')) continue
|
|
175
|
+
if (entries.includes(file) || dynamicTargets.has(file)) continue
|
|
176
|
+
const mask = roots.map(root => (available.get(root)!.has(file) ? '1' : '0')).join('')
|
|
177
177
|
// A chunk no root can reach is dead output; leave it alone.
|
|
178
|
-
if (!mask.includes('1')) continue
|
|
179
|
-
(groups.get(mask) ?? groups.set(mask, []).get(mask)!).push(file)
|
|
178
|
+
if (!mask.includes('1')) continue
|
|
179
|
+
;(groups.get(mask) ?? groups.set(mask, []).get(mask)!).push(file)
|
|
180
180
|
}
|
|
181
181
|
return [...groups.values()]
|
|
182
182
|
.filter(
|
|
@@ -187,7 +187,7 @@ function coLoadGroups(metafile: Metafile) {
|
|
|
187
187
|
// nobody waits on.
|
|
188
188
|
entries.some(entry => available.get(entry)!.has(group[0]!)),
|
|
189
189
|
)
|
|
190
|
-
.map(group => [...group].sort())
|
|
190
|
+
.map(group => [...group].sort())
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
/**
|
|
@@ -199,21 +199,21 @@ function availabilityAt(
|
|
|
199
199
|
file: string,
|
|
200
200
|
memo: Map<string, Set<string>>,
|
|
201
201
|
) {
|
|
202
|
-
const own = available.get(file)
|
|
203
|
-
if (own) return own
|
|
202
|
+
const own = available.get(file)
|
|
203
|
+
if (own) return own
|
|
204
204
|
// The fixpoint below asks for the same non-root importers once per round per
|
|
205
205
|
// target, and each answer scans every root's availability set — quadratic in
|
|
206
206
|
// roots. The memo is cleared whenever a round changes an answer.
|
|
207
|
-
const cached = memo.get(file)
|
|
208
|
-
if (cached) return cached
|
|
209
|
-
let acc: Set<string> | undefined
|
|
207
|
+
const cached = memo.get(file)
|
|
208
|
+
if (cached) return cached
|
|
209
|
+
let acc: Set<string> | undefined
|
|
210
210
|
for (const set of available.values()) {
|
|
211
|
-
if (!set.has(file)) continue
|
|
212
|
-
acc = acc === undefined ? new Set(set) : intersect(acc, set)
|
|
211
|
+
if (!set.has(file)) continue
|
|
212
|
+
acc = acc === undefined ? new Set(set) : intersect(acc, set)
|
|
213
213
|
}
|
|
214
|
-
const answer = acc ?? new Set([file])
|
|
215
|
-
memo.set(file, answer)
|
|
216
|
-
return answer
|
|
214
|
+
const answer = acc ?? new Set([file])
|
|
215
|
+
memo.set(file, answer)
|
|
216
|
+
return answer
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
/**
|
|
@@ -222,97 +222,98 @@ function availabilityAt(
|
|
|
222
222
|
* importers whose own availability is still an estimate, and lands pessimistic behind route chunks.
|
|
223
223
|
*/
|
|
224
224
|
function entryFloors(metafile: Metafile, entries: string[], closures: Map<string, Set<string>>) {
|
|
225
|
-
const dynamicEdges = new Map<string, string[]>()
|
|
225
|
+
const dynamicEdges = new Map<string, string[]>()
|
|
226
226
|
for (const [file, output] of Object.entries(metafile.outputs)) {
|
|
227
227
|
for (const imported of output.imports ?? []) {
|
|
228
|
-
if (imported.kind !== 'dynamic-import') continue
|
|
229
|
-
(dynamicEdges.get(file) ?? dynamicEdges.set(file, []).get(file)!).push(imported.path)
|
|
228
|
+
if (imported.kind !== 'dynamic-import') continue
|
|
229
|
+
;(dynamicEdges.get(file) ?? dynamicEdges.set(file, []).get(file)!).push(imported.path)
|
|
230
230
|
}
|
|
231
231
|
}
|
|
232
|
-
const floors = new Map<string, Set<string>>()
|
|
232
|
+
const floors = new Map<string, Set<string>>()
|
|
233
233
|
for (const entry of entries) {
|
|
234
|
-
const own = closures.get(entry)
|
|
235
|
-
const seen = new Set(own)
|
|
236
|
-
const queue = [...own]
|
|
234
|
+
const own = closures.get(entry)!
|
|
235
|
+
const seen = new Set(own)
|
|
236
|
+
const queue = [...own]
|
|
237
237
|
// `queue` grows inside the loop: every chunk this entry can reach by any
|
|
238
238
|
// chain of static and dynamic imports.
|
|
239
239
|
for (const current of queue) {
|
|
240
240
|
for (const target of dynamicEdges.get(current) ?? []) {
|
|
241
|
-
if (seen.has(target)) continue
|
|
241
|
+
if (seen.has(target)) continue
|
|
242
242
|
for (const file of closures.get(target) ?? staticClosure(metafile, target)) {
|
|
243
|
-
if (seen.has(file)) continue
|
|
244
|
-
seen.add(file)
|
|
245
|
-
queue.push(file)
|
|
243
|
+
if (seen.has(file)) continue
|
|
244
|
+
seen.add(file)
|
|
245
|
+
queue.push(file)
|
|
246
246
|
}
|
|
247
|
-
const floor = floors.get(target)
|
|
248
|
-
floors.set(target, floor ? intersect(floor, own) : new Set(own))
|
|
247
|
+
const floor = floors.get(target)
|
|
248
|
+
floors.set(target, floor ? intersect(floor, own) : new Set(own))
|
|
249
249
|
}
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
|
-
return floors
|
|
252
|
+
return floors
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
/** Per load root, the chunks guaranteed to be on the page once it runs. */
|
|
256
256
|
function availability(metafile: Metafile, entries: string[], dynamicTargets: Set<string>) {
|
|
257
|
-
const available = new Map<string, Set<string>>()
|
|
258
|
-
const closures = new Map<string, Set<string>>()
|
|
257
|
+
const available = new Map<string, Set<string>>()
|
|
258
|
+
const closures = new Map<string, Set<string>>()
|
|
259
259
|
for (const root of [...entries, ...dynamicTargets]) {
|
|
260
|
-
closures.set(root, staticClosure(metafile, root))
|
|
261
|
-
available.set(root, new Set(closures.get(root)))
|
|
260
|
+
closures.set(root, staticClosure(metafile, root))
|
|
261
|
+
available.set(root, new Set(closures.get(root)))
|
|
262
262
|
}
|
|
263
263
|
for (const [target, floor] of entryFloors(metafile, entries, closures)) {
|
|
264
|
-
const set = available.get(target)
|
|
265
|
-
if (set) for (const file of floor) set.add(file)
|
|
264
|
+
const set = available.get(target)
|
|
265
|
+
if (set) for (const file of floor) set.add(file)
|
|
266
266
|
}
|
|
267
|
-
const importersOf = new Map<string, string[]>()
|
|
267
|
+
const importersOf = new Map<string, string[]>()
|
|
268
268
|
for (const [file, output] of Object.entries(metafile.outputs)) {
|
|
269
269
|
for (const imported of output.imports ?? []) {
|
|
270
|
-
if (imported.kind !== 'dynamic-import') continue
|
|
271
|
-
(
|
|
272
|
-
.
|
|
270
|
+
if (imported.kind !== 'dynamic-import') continue
|
|
271
|
+
;(
|
|
272
|
+
importersOf.get(imported.path) ?? importersOf.set(imported.path, []).get(imported.path)!
|
|
273
|
+
).push(file)
|
|
273
274
|
}
|
|
274
275
|
}
|
|
275
276
|
// `available` only ever grows, so iterating to a fixed point terminates; the
|
|
276
277
|
// bound is the number of roots (each round settles at least one).
|
|
277
|
-
const memo = new Map<string, Set<string>>()
|
|
278
|
+
const memo = new Map<string, Set<string>>()
|
|
278
279
|
for (let round = 0; round < dynamicTargets.size + 1; round += 1) {
|
|
279
|
-
let changed = false
|
|
280
|
+
let changed = false
|
|
280
281
|
for (const target of dynamicTargets) {
|
|
281
|
-
const importers = importersOf.get(target) ?? []
|
|
282
|
-
let inherited: Set<string> | undefined
|
|
282
|
+
const importers = importersOf.get(target) ?? []
|
|
283
|
+
let inherited: Set<string> | undefined
|
|
283
284
|
for (const importer of importers) {
|
|
284
|
-
const from = availabilityAt(available, importer, memo)
|
|
285
|
-
inherited = inherited === undefined ? new Set(from) : intersect(inherited, from)
|
|
285
|
+
const from = availabilityAt(available, importer, memo)
|
|
286
|
+
inherited = inherited === undefined ? new Set(from) : intersect(inherited, from)
|
|
286
287
|
}
|
|
287
|
-
const next = new Set([...closures.get(target)!, ...(inherited ?? [])])
|
|
288
|
+
const next = new Set([...closures.get(target)!, ...(inherited ?? [])])
|
|
288
289
|
if (next.size !== available.get(target)!.size) {
|
|
289
|
-
available.set(target, next)
|
|
290
|
-
memo.clear()
|
|
291
|
-
changed = true
|
|
290
|
+
available.set(target, next)
|
|
291
|
+
memo.clear()
|
|
292
|
+
changed = true
|
|
292
293
|
}
|
|
293
294
|
}
|
|
294
|
-
if (!changed) break
|
|
295
|
+
if (!changed) break
|
|
295
296
|
}
|
|
296
|
-
return available
|
|
297
|
+
return available
|
|
297
298
|
}
|
|
298
299
|
|
|
299
300
|
function staticClosure(metafile: Metafile, root: string) {
|
|
300
|
-
const seen = new Set([root])
|
|
301
|
-
const queue = [root]
|
|
301
|
+
const seen = new Set([root])
|
|
302
|
+
const queue = [root]
|
|
302
303
|
// `queue` grows inside the loop — a breadth-first walk of the chunk graph.
|
|
303
304
|
for (const current of queue) {
|
|
304
305
|
for (const imported of metafile.outputs[current]?.imports ?? []) {
|
|
305
|
-
if (imported.kind !== 'import-statement') continue
|
|
306
|
-
if (seen.has(imported.path)) continue
|
|
307
|
-
seen.add(imported.path)
|
|
308
|
-
queue.push(imported.path)
|
|
306
|
+
if (imported.kind !== 'import-statement') continue
|
|
307
|
+
if (seen.has(imported.path)) continue
|
|
308
|
+
seen.add(imported.path)
|
|
309
|
+
queue.push(imported.path)
|
|
309
310
|
}
|
|
310
311
|
}
|
|
311
|
-
return seen
|
|
312
|
+
return seen
|
|
312
313
|
}
|
|
313
314
|
|
|
314
315
|
function intersect(a: Set<string>, b: Set<string>) {
|
|
315
|
-
return new Set([...a].filter(item => b.has(item)))
|
|
316
|
+
return new Set([...a].filter(item => b.has(item)))
|
|
316
317
|
}
|
|
317
318
|
|
|
318
319
|
/**
|
|
@@ -320,24 +321,26 @@ function intersect(a: Set<string>, b: Set<string>) {
|
|
|
320
321
|
* collide across chunks, so the barrel renames them apart.
|
|
321
322
|
*/
|
|
322
323
|
function memberAliases(memberNames: string[], sources: Map<string, string>) {
|
|
323
|
-
const aliases = new Map<string, Map<string, string>>()
|
|
324
|
+
const aliases = new Map<string, Map<string, string>>()
|
|
324
325
|
for (const [index, name] of memberNames.entries()) {
|
|
325
|
-
const source = sources.get(name)
|
|
326
|
+
const source = sources.get(name)!
|
|
326
327
|
// A star re-export can't be aliased name-by-name; nothing to fold safely.
|
|
327
|
-
if (/export\s*\*/.test(source)) return undefined
|
|
328
|
+
if (/export\s*\*/.test(source)) return undefined
|
|
328
329
|
// The export clause is the chunk's last one — a linked sourcemap comment
|
|
329
330
|
// can follow it, so anchor on "last `export{…}` that isn't a re-export"
|
|
330
331
|
// rather than on end-of-file.
|
|
331
|
-
const clauses = [
|
|
332
|
-
|
|
333
|
-
|
|
332
|
+
const clauses = [
|
|
333
|
+
...source.matchAll(new RegExp(String.raw`export\s*${namedClause}\s*(?!from)`, 'g')),
|
|
334
|
+
]
|
|
335
|
+
const clause = clauses.at(-1)
|
|
336
|
+
const names = new Map<string, string>()
|
|
334
337
|
for (const part of clause?.[1]?.split(',') ?? []) {
|
|
335
|
-
const exported = part.split(' as ').at(-1)?.trim()
|
|
336
|
-
if (exported) names.set(exported, `$${index}${exported}`)
|
|
338
|
+
const exported = part.split(' as ').at(-1)?.trim()
|
|
339
|
+
if (exported) names.set(exported, `$${index}${exported}`)
|
|
337
340
|
}
|
|
338
|
-
aliases.set(name, names)
|
|
341
|
+
aliases.set(name, names)
|
|
339
342
|
}
|
|
340
|
-
return aliases
|
|
343
|
+
return aliases
|
|
341
344
|
}
|
|
342
345
|
|
|
343
346
|
/**
|
|
@@ -350,53 +353,55 @@ function memberRewriter(
|
|
|
350
353
|
aliases: Map<string, Map<string, string>>,
|
|
351
354
|
chunksDir: string,
|
|
352
355
|
) {
|
|
353
|
-
const memberPattern = memberNames.map(escapeRegex).join('|')
|
|
354
|
-
const specifier = String.raw`"\.[^"]*(?:${memberPattern})"
|
|
356
|
+
const memberPattern = memberNames.map(escapeRegex).join('|')
|
|
357
|
+
const specifier = String.raw`"\.[^"]*(?:${memberPattern})"`
|
|
355
358
|
// Forms that can't be expressed against a renaming barrel.
|
|
356
359
|
const vetoes = [
|
|
357
360
|
new RegExp(String.raw`import\s*\*\s*as\s*[\w$]+\s*from\s*${specifier}`),
|
|
358
361
|
new RegExp(String.raw`export\s*\*[^;]*from\s*${specifier}`),
|
|
359
362
|
new RegExp(String.raw`import\(\s*${specifier}`),
|
|
360
|
-
]
|
|
363
|
+
]
|
|
361
364
|
const namedImport = new RegExp(
|
|
362
365
|
String.raw`(import|export)\s*${namedClause}\s*from\s*(${specifier})`,
|
|
363
366
|
'g',
|
|
364
|
-
)
|
|
365
|
-
const sideEffectImport = new RegExp(String.raw`import\s*(${specifier})`, 'g')
|
|
367
|
+
)
|
|
368
|
+
const sideEffectImport = new RegExp(String.raw`import\s*(${specifier})`, 'g')
|
|
366
369
|
|
|
367
|
-
const nameOf = (spec: string) => memberNames.find(name => spec.includes(name))
|
|
370
|
+
const nameOf = (spec: string) => memberNames.find(name => spec.includes(name))!
|
|
368
371
|
const rename = (clause: string, member: string) =>
|
|
369
372
|
clause
|
|
370
373
|
.split(',')
|
|
371
374
|
.map(part => {
|
|
372
|
-
const [imported, local] = part.split(' as ').map(item => item.trim())
|
|
373
|
-
if (!imported) return part
|
|
374
|
-
const alias = aliases.get(member)?.get(imported)
|
|
375
|
-
if (!alias) return part
|
|
376
|
-
return `${alias} as ${local ?? imported}
|
|
375
|
+
const [imported, local] = part.split(' as ').map(item => item.trim())
|
|
376
|
+
if (!imported) return part
|
|
377
|
+
const alias = aliases.get(member)?.get(imported)
|
|
378
|
+
if (!alias) return part
|
|
379
|
+
return `${alias} as ${local ?? imported}`
|
|
377
380
|
})
|
|
378
|
-
.join(',')
|
|
381
|
+
.join(',')
|
|
379
382
|
|
|
380
383
|
return (source: string, file: string) => {
|
|
381
|
-
for (const veto of vetoes) if (veto.test(source)) return null
|
|
384
|
+
for (const veto of vetoes) if (veto.test(source)) return null
|
|
382
385
|
const mergedHref = path
|
|
383
386
|
.relative(path.dirname(file), path.join(chunksDir, MERGED_PLACEHOLDER))
|
|
384
387
|
.split(path.sep)
|
|
385
|
-
.join('/')
|
|
386
|
-
const href = mergedHref.startsWith('.') ? mergedHref : `./${mergedHref}
|
|
387
|
-
return
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
388
|
+
.join('/')
|
|
389
|
+
const href = mergedHref.startsWith('.') ? mergedHref : `./${mergedHref}`
|
|
390
|
+
return (
|
|
391
|
+
source
|
|
392
|
+
.replace(
|
|
393
|
+
namedImport,
|
|
394
|
+
(_all, keyword: string, clause: string, spec: string) =>
|
|
395
|
+
`${keyword}{${rename(clause, nameOf(spec))}}from"${href}"`,
|
|
396
|
+
)
|
|
397
|
+
// Side-effect-only imports of a member collapse onto the merged chunk.
|
|
398
|
+
.replace(sideEffectImport, () => `import"${href}"`)
|
|
399
|
+
)
|
|
400
|
+
}
|
|
396
401
|
}
|
|
397
402
|
|
|
398
403
|
/** Stands in for the merged chunk's final (content-hashed) name. */
|
|
399
|
-
const MERGED_PLACEHOLDER = 'pnext-merged'
|
|
404
|
+
const MERGED_PLACEHOLDER = 'pnext-merged'
|
|
400
405
|
|
|
401
406
|
async function buildMergedChunk(
|
|
402
407
|
chunksDir: string,
|
|
@@ -406,16 +411,16 @@ async function buildMergedChunk(
|
|
|
406
411
|
) {
|
|
407
412
|
const barrel = memberNames
|
|
408
413
|
.map(name => {
|
|
409
|
-
const names = aliases.get(name)
|
|
410
|
-
const spec = JSON.stringify(`./${name}`)
|
|
411
|
-
if (names.size === 0) return `import ${spec}
|
|
412
|
-
const clause = [...names].map(([exported, alias]) => `${exported} as ${alias}`).join(',')
|
|
413
|
-
return `export {${clause}} from ${spec}
|
|
414
|
+
const names = aliases.get(name)!
|
|
415
|
+
const spec = JSON.stringify(`./${name}`)
|
|
416
|
+
if (names.size === 0) return `import ${spec};`
|
|
417
|
+
const clause = [...names].map(([exported, alias]) => `${exported} as ${alias}`).join(',')
|
|
418
|
+
return `export {${clause}} from ${spec};`
|
|
414
419
|
})
|
|
415
|
-
.join('\n')
|
|
420
|
+
.join('\n')
|
|
416
421
|
|
|
417
|
-
const barrelPath = path.join(chunksDir, '__pnext-fold-barrel.js')
|
|
418
|
-
await writeText(barrelPath, barrel)
|
|
422
|
+
const barrelPath = path.join(chunksDir, '__pnext-fold-barrel.js')
|
|
423
|
+
await writeText(barrelPath, barrel)
|
|
419
424
|
try {
|
|
420
425
|
const result = await build({
|
|
421
426
|
minify: buildOptions.minify,
|
|
@@ -433,94 +438,94 @@ async function buildMergedChunk(
|
|
|
433
438
|
name: 'pnext-fold-members',
|
|
434
439
|
setup(builder) {
|
|
435
440
|
builder.onResolve({ filter: /.*/ }, args => {
|
|
436
|
-
if (args.kind === 'entry-point') return undefined
|
|
437
|
-
const base = path.basename(args.path)
|
|
438
|
-
if (memberNames.includes(base)) return { path: path.join(chunksDir, base) }
|
|
439
|
-
return { path: args.path, external: true }
|
|
440
|
-
})
|
|
441
|
+
if (args.kind === 'entry-point') return undefined
|
|
442
|
+
const base = path.basename(args.path)
|
|
443
|
+
if (memberNames.includes(base)) return { path: path.join(chunksDir, base) }
|
|
444
|
+
return { path: args.path, external: true }
|
|
445
|
+
})
|
|
441
446
|
},
|
|
442
447
|
},
|
|
443
448
|
],
|
|
444
|
-
})
|
|
445
|
-
const js = result.outputFiles?.find(file => file.path.endsWith('.js'))
|
|
446
|
-
if (!js) return undefined
|
|
447
|
-
const hash = createHash('sha256').update(js.contents).digest('hex').slice(0, 8).toUpperCase()
|
|
448
|
-
const name = `shared-init-${hash}.js
|
|
449
|
-
const map = result.outputFiles?.find(file => file.path.endsWith('.js.map'))
|
|
450
|
-
let contents = new TextDecoder().decode(js.contents)
|
|
449
|
+
})
|
|
450
|
+
const js = result.outputFiles?.find(file => file.path.endsWith('.js'))
|
|
451
|
+
if (!js) return undefined
|
|
452
|
+
const hash = createHash('sha256').update(js.contents).digest('hex').slice(0, 8).toUpperCase()
|
|
453
|
+
const name = `shared-init-${hash}.js`
|
|
454
|
+
const map = result.outputFiles?.find(file => file.path.endsWith('.js.map'))
|
|
455
|
+
let contents = new TextDecoder().decode(js.contents)
|
|
451
456
|
if (map) {
|
|
452
|
-
contents = contents.replace(/# sourceMappingURL=.*$/m, `# sourceMappingURL=${name}.map`)
|
|
453
|
-
await writeText(path.join(chunksDir, `${name}.map`), new TextDecoder().decode(map.contents))
|
|
457
|
+
contents = contents.replace(/# sourceMappingURL=.*$/m, `# sourceMappingURL=${name}.map`)
|
|
458
|
+
await writeText(path.join(chunksDir, `${name}.map`), new TextDecoder().decode(map.contents))
|
|
454
459
|
}
|
|
455
|
-
await writeText(path.join(chunksDir, name), contents)
|
|
456
|
-
return { name, text: contents, bytes: Buffer.byteLength(contents) }
|
|
460
|
+
await writeText(path.join(chunksDir, name), contents)
|
|
461
|
+
return { name, text: contents, bytes: Buffer.byteLength(contents) }
|
|
457
462
|
} finally {
|
|
458
|
-
await unlink(barrelPath).catch(ignoreMissing)
|
|
463
|
+
await unlink(barrelPath).catch(ignoreMissing)
|
|
459
464
|
}
|
|
460
465
|
}
|
|
461
466
|
|
|
462
467
|
/** An output file's text plus the relative specifiers it imports through. */
|
|
463
468
|
interface FoldOutput {
|
|
464
|
-
text: string
|
|
465
|
-
specifiers: string[]
|
|
469
|
+
text: string
|
|
470
|
+
specifiers: string[]
|
|
466
471
|
}
|
|
467
472
|
|
|
468
473
|
function foldOutput(text: string): FoldOutput {
|
|
469
|
-
return { text, specifiers: [...text.matchAll(/"\.[^"]*"/g)].map(match => match[0]) }
|
|
474
|
+
return { text, specifiers: [...text.matchAll(/"\.[^"]*"/g)].map(match => match[0]) }
|
|
470
475
|
}
|
|
471
476
|
|
|
472
477
|
/** Every output's text plus a basename->referrers index, read once. Chunk names
|
|
473
478
|
* are content-hashed, so a specifier naming a member spells it as its basename. */
|
|
474
479
|
interface FoldState {
|
|
475
|
-
outputs: Map<string, FoldOutput
|
|
480
|
+
outputs: Map<string, FoldOutput>
|
|
476
481
|
/** Files whose specifiers name any of `names`, each visited once. */
|
|
477
|
-
referrersOf(names: string[]): Iterable<string
|
|
478
|
-
set(file: string, output: FoldOutput): void
|
|
479
|
-
remove(file: string): void
|
|
482
|
+
referrersOf(names: string[]): Iterable<string>
|
|
483
|
+
set(file: string, output: FoldOutput): void
|
|
484
|
+
remove(file: string): void
|
|
480
485
|
}
|
|
481
486
|
|
|
482
487
|
async function createFoldState(outDir: string, metafile: Metafile): Promise<FoldState> {
|
|
483
|
-
const outputs = new Map<string, FoldOutput>()
|
|
484
|
-
const referrers = new Map<string, Set<string>>()
|
|
488
|
+
const outputs = new Map<string, FoldOutput>()
|
|
489
|
+
const referrers = new Map<string, Set<string>>()
|
|
485
490
|
|
|
486
491
|
const index = (file: string, output: FoldOutput, add: boolean) => {
|
|
487
492
|
for (const spec of output.specifiers) {
|
|
488
|
-
const base = path.basename(spec.slice(1, -1))
|
|
489
|
-
const set = referrers.get(base) ?? referrers.set(base, new Set()).get(base)
|
|
490
|
-
if (add) set.add(file)
|
|
491
|
-
else set.delete(file)
|
|
493
|
+
const base = path.basename(spec.slice(1, -1))
|
|
494
|
+
const set = referrers.get(base) ?? referrers.set(base, new Set()).get(base)!
|
|
495
|
+
if (add) set.add(file)
|
|
496
|
+
else set.delete(file)
|
|
492
497
|
}
|
|
493
|
-
}
|
|
498
|
+
}
|
|
494
499
|
|
|
495
500
|
for (const name of Object.keys(metafile.outputs)) {
|
|
496
|
-
if (!name.endsWith('.js')) continue
|
|
497
|
-
const resolved = path.resolve(name)
|
|
498
|
-
const local = existsSync(resolved) ? resolved : path.join(outDir, path.basename(name))
|
|
499
|
-
if (outputs.has(local) || !existsSync(local)) continue
|
|
500
|
-
const output = foldOutput(await readText(local))
|
|
501
|
-
outputs.set(local, output)
|
|
502
|
-
index(local, output, true)
|
|
501
|
+
if (!name.endsWith('.js')) continue
|
|
502
|
+
const resolved = path.resolve(name)
|
|
503
|
+
const local = existsSync(resolved) ? resolved : path.join(outDir, path.basename(name))
|
|
504
|
+
if (outputs.has(local) || !existsSync(local)) continue
|
|
505
|
+
const output = foldOutput(await readText(local))
|
|
506
|
+
outputs.set(local, output)
|
|
507
|
+
index(local, output, true)
|
|
503
508
|
}
|
|
504
509
|
|
|
505
510
|
return {
|
|
506
511
|
outputs,
|
|
507
512
|
referrersOf(names) {
|
|
508
|
-
const seen = new Set<string>()
|
|
509
|
-
for (const name of names) for (const file of referrers.get(name) ?? []) seen.add(file)
|
|
510
|
-
return seen
|
|
513
|
+
const seen = new Set<string>()
|
|
514
|
+
for (const name of names) for (const file of referrers.get(name) ?? []) seen.add(file)
|
|
515
|
+
return seen
|
|
511
516
|
},
|
|
512
517
|
set(file, output) {
|
|
513
|
-
const previous = outputs.get(file)
|
|
514
|
-
if (previous) index(file, previous, false)
|
|
515
|
-
outputs.set(file, output)
|
|
516
|
-
index(file, output, true)
|
|
518
|
+
const previous = outputs.get(file)
|
|
519
|
+
if (previous) index(file, previous, false)
|
|
520
|
+
outputs.set(file, output)
|
|
521
|
+
index(file, output, true)
|
|
517
522
|
},
|
|
518
523
|
remove(file) {
|
|
519
|
-
const previous = outputs.get(file)
|
|
520
|
-
if (previous) index(file, previous, false)
|
|
521
|
-
outputs.delete(file)
|
|
524
|
+
const previous = outputs.get(file)
|
|
525
|
+
if (previous) index(file, previous, false)
|
|
526
|
+
outputs.delete(file)
|
|
522
527
|
},
|
|
523
|
-
}
|
|
528
|
+
}
|
|
524
529
|
}
|
|
525
530
|
|
|
526
531
|
/** The shipped graph, with the members collapsed into the merged chunk. */
|
|
@@ -530,37 +535,37 @@ function mergeMetafile(
|
|
|
530
535
|
mergedPath: string,
|
|
531
536
|
bytes: number,
|
|
532
537
|
): Metafile {
|
|
533
|
-
const memberSet = new Set(members)
|
|
534
|
-
const merged = path.relative(process.cwd(), mergedPath)
|
|
535
|
-
const outputs: Metafile['outputs'] = {}
|
|
536
|
-
const mergedImports: Metafile['outputs'][string]['imports'] = []
|
|
537
|
-
const mergedInputs: Metafile['outputs'][string]['inputs'] = {}
|
|
538
|
+
const memberSet = new Set(members)
|
|
539
|
+
const merged = path.relative(process.cwd(), mergedPath)
|
|
540
|
+
const outputs: Metafile['outputs'] = {}
|
|
541
|
+
const mergedImports: Metafile['outputs'][string]['imports'] = []
|
|
542
|
+
const mergedInputs: Metafile['outputs'][string]['inputs'] = {}
|
|
538
543
|
|
|
539
544
|
for (const [file, output] of Object.entries(metafile.outputs)) {
|
|
540
545
|
const remap = (list: typeof output.imports) => {
|
|
541
|
-
const seen = new Set<string>()
|
|
546
|
+
const seen = new Set<string>()
|
|
542
547
|
return list.flatMap(imported => {
|
|
543
|
-
const target = memberSet.has(imported.path) ? merged : imported.path
|
|
544
|
-
const key = `${target}:${imported.kind}
|
|
545
|
-
if (seen.has(key)) return []
|
|
546
|
-
seen.add(key)
|
|
547
|
-
return [{ ...imported, path: target }]
|
|
548
|
-
})
|
|
549
|
-
}
|
|
548
|
+
const target = memberSet.has(imported.path) ? merged : imported.path
|
|
549
|
+
const key = `${target}:${imported.kind}`
|
|
550
|
+
if (seen.has(key)) return []
|
|
551
|
+
seen.add(key)
|
|
552
|
+
return [{ ...imported, path: target }]
|
|
553
|
+
})
|
|
554
|
+
}
|
|
550
555
|
if (memberSet.has(file)) {
|
|
551
|
-
Object.assign(mergedInputs, output.inputs)
|
|
556
|
+
Object.assign(mergedInputs, output.inputs)
|
|
552
557
|
for (const imported of remap(output.imports ?? [])) {
|
|
553
|
-
if (imported.path !== merged) mergedImports.push(imported)
|
|
558
|
+
if (imported.path !== merged) mergedImports.push(imported)
|
|
554
559
|
}
|
|
555
|
-
continue
|
|
560
|
+
continue
|
|
556
561
|
}
|
|
557
|
-
outputs[file] = { ...output, imports: remap(output.imports ?? []) }
|
|
562
|
+
outputs[file] = { ...output, imports: remap(output.imports ?? []) }
|
|
558
563
|
}
|
|
559
564
|
outputs[merged] = {
|
|
560
565
|
imports: mergedImports,
|
|
561
566
|
exports: [],
|
|
562
567
|
inputs: mergedInputs,
|
|
563
568
|
bytes,
|
|
564
|
-
}
|
|
565
|
-
return { ...metafile, outputs }
|
|
569
|
+
}
|
|
570
|
+
return { ...metafile, outputs }
|
|
566
571
|
}
|