@wular/pnext 0.0.3 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/bin/pnext +1 -1
- package/config/lint/base.js +7 -7
- package/config/ts/base.json +2 -4
- package/config/ts/react.json +2 -6
- package/package.json +23 -2
- package/reference/compat.md +1 -1
- package/reference/config.md +2 -2
- package/reference/css.md +13 -9
- package/reference/dev.md +1 -1
- package/reference/metadata.md +7 -7
- package/reference/navigation.md +35 -28
- package/reference/performance.md +70 -70
- package/reference/rendering.md +17 -17
- package/reference/routing.md +17 -17
- package/reference/typegen.md +12 -8
- package/src/api/cache.ts +36 -37
- package/src/api/client-cache.ts +2 -2
- package/src/api/client-navigation.ts +113 -109
- package/src/api/dynamic.tsx +58 -55
- package/src/api/link.tsx +49 -52
- package/src/api/navigation.ts +59 -58
- package/src/api/server.ts +152 -144
- package/src/api/suspense.ts +4 -4
- package/src/cli/adapters/vercel-warm.ts +126 -121
- package/src/cli/adapters/vercel.ts +437 -443
- package/src/cli/analyze.ts +333 -144
- package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
- package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
- package/src/cli/build.ts +976 -978
- package/src/cli/create.ts +71 -63
- package/src/cli/dev.ts +116 -116
- package/src/cli/index.ts +88 -85
- package/src/cli/migrate/package-json.ts +73 -71
- package/src/cli/migrate/report.ts +32 -33
- package/src/cli/migrate/{index.ts → run.ts} +48 -49
- package/src/cli/migrate/scan.ts +46 -46
- package/src/cli/migrate/spinner.ts +9 -9
- package/src/cli/migrate/tsconfig.ts +35 -35
- package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
- package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
- package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
- package/src/cli/start.ts +65 -66
- package/src/{typegen.ts → cli/typegen.ts} +59 -59
- package/src/client/build.ts +794 -768
- package/src/client/chunk-fold.ts +245 -240
- package/src/client/{paths.ts → chunk-name.ts} +6 -6
- package/src/client/entry.ts +162 -147
- package/src/client/prebuilt.ts +231 -223
- package/src/client/profile.ts +29 -29
- package/src/client/react-compiler.ts +20 -15
- package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
- package/src/client/reference-stub.ts +51 -51
- package/src/client/reference.ts +19 -19
- package/src/{api → client}/router/events.ts +17 -17
- package/src/{api → client}/router/history.ts +16 -16
- package/src/{api → client}/router/hub.ts +52 -53
- package/src/{api/router.ts → client/router/index.ts} +61 -60
- package/src/{api → client}/router/policies.ts +24 -24
- package/src/{api → client}/router/runtime.ts +1988 -1961
- package/src/{api → client}/router/types.ts +93 -98
- package/src/compat/actions/client-plugin.ts +42 -43
- package/src/compat/actions/client-stub.ts +14 -14
- package/src/compat/actions/{action-client.ts → client.ts} +194 -193
- package/src/compat/actions/config.ts +63 -64
- package/src/compat/actions/detect.ts +68 -68
- package/src/compat/actions/discovery.ts +105 -105
- package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
- package/src/compat/actions/early-submit.ts +1 -1
- package/src/compat/actions/endpoint.ts +198 -198
- package/src/compat/actions/extensions.ts +811 -0
- package/src/compat/actions/flight.ts +23 -23
- package/src/compat/actions/form-state.ts +34 -34
- package/src/compat/actions/hoist.ts +382 -236
- package/src/compat/actions/ids.ts +7 -12
- package/src/compat/actions/index.ts +8 -8
- package/src/compat/actions/instances.ts +46 -46
- package/src/compat/actions/origin.ts +47 -48
- package/src/compat/actions/protocol.ts +22 -22
- package/src/compat/actions/registry.ts +19 -19
- package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
- package/src/compat/actions/rewrite.ts +125 -125
- package/src/compat/actions/{action-router.ts → router.ts} +10 -10
- package/src/compat/actions/serve.ts +132 -136
- package/src/compat/actions/server-tag.ts +4 -4
- package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
- package/src/compat/actions/unrecognized-error.ts +4 -4
- package/src/compat/{index.ts → aliases.ts} +112 -109
- package/src/compat/bundler/bun-externals.ts +18 -18
- package/src/compat/bundler/cjs-exports.ts +271 -223
- package/src/compat/bundler/config.ts +116 -111
- package/src/compat/bundler/externals.ts +12 -12
- package/src/compat/bundler/import-meta-url.ts +19 -19
- package/src/compat/bundler/modularize-imports.ts +36 -33
- package/src/compat/bundler/new-url-asset.ts +22 -24
- package/src/compat/bundler/optimize-package-imports.ts +111 -107
- package/src/compat/bundler/polyfill.ts +28 -28
- package/src/compat/bundler/react-compiler.ts +35 -29
- package/src/compat/bundler/react-profiler.tsx +11 -11
- package/src/compat/bundler/relay-transform.ts +48 -47
- package/src/compat/bundler/require-context.ts +119 -113
- package/src/compat/bundler/resolve-extensions.ts +19 -19
- package/src/compat/bundler/source-cache.ts +25 -25
- package/src/compat/bundler/static-imports.ts +7 -7
- package/src/compat/bundler/symlink-imports.ts +46 -46
- package/src/compat/bundler/tsconfig-paths.ts +13 -15
- package/src/compat/bundler/wasm.ts +58 -60
- package/src/compat/bundler/webpack-loaders.ts +254 -241
- package/src/compat/bundler/worker.ts +101 -104
- package/src/compat/cache/build-flags.ts +29 -29
- package/src/compat/cache/build-prerender-errors.ts +40 -44
- package/src/compat/cache/custom-handler.ts +60 -53
- package/src/compat/cache/fetch-patch.ts +240 -240
- package/src/compat/cache/handler.ts +27 -31
- package/src/compat/cache/modern-handler.ts +148 -126
- package/src/compat/cache/resume-data-cache.ts +47 -45
- package/src/compat/cache/revalidate.ts +233 -232
- package/src/compat/cache/runtime-error.ts +35 -35
- package/src/compat/cache/use-cache-transform.ts +442 -417
- package/src/compat/cache/use-cache.ts +650 -614
- package/src/compat/cache-control.ts +140 -142
- package/src/compat/client/base-path.ts +21 -20
- package/src/compat/client/css-order.ts +18 -18
- package/src/compat/client/errors/bare-boundary.ts +11 -11
- package/src/compat/client/errors/control-flow.ts +23 -23
- package/src/compat/client/errors/error-boundary.ts +61 -61
- package/src/compat/client/errors/global-error.ts +101 -91
- package/src/compat/client/errors/install.ts +71 -72
- package/src/compat/client/errors/lazy.ts +23 -23
- package/src/compat/client/errors/primitive-throw.ts +47 -45
- package/src/compat/client/errors/soft-refresh.ts +4 -4
- package/src/compat/client/link-status.ts +33 -33
- package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
- package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
- package/src/compat/client/navigation-scroll.ts +63 -63
- package/src/compat/client/optimistic-routing.ts +93 -88
- package/src/compat/client/prefetch-cache.ts +23 -24
- package/src/compat/client/route-announcer.ts +35 -35
- package/src/compat/client/segment-cache-policy.ts +20 -20
- package/src/compat/client/segment-cache.ts +309 -315
- package/src/compat/client/segment-prefetch.ts +127 -132
- package/src/compat/client/trailing-slash.ts +5 -4
- package/src/compat/css/chunking.ts +113 -116
- package/src/compat/css/inline-css.ts +21 -21
- package/src/compat/css/lightningcss.ts +37 -38
- package/src/compat/css/modules.ts +161 -175
- package/src/compat/css/nonce.ts +7 -7
- package/src/compat/css/sass-plugin.ts +18 -21
- package/src/compat/css/sass.ts +150 -152
- package/src/compat/css/styled-jsx-runtime.ts +27 -27
- package/src/compat/css/styled-jsx.ts +21 -21
- package/src/compat/edge-runtime.ts +27 -27
- package/src/compat/{adapter → export}/build-complete.ts +72 -75
- package/src/compat/export/client.ts +29 -31
- package/src/compat/export/{index.ts → emit.ts} +111 -110
- package/src/compat/export/standalone.ts +62 -54
- package/src/compat/image-optimizer/cache.ts +51 -49
- package/src/compat/image-optimizer/detect.ts +52 -52
- package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
- package/src/compat/image-optimizer/source.ts +77 -80
- package/src/compat/lifecycle/after-scope.ts +26 -26
- package/src/compat/lifecycle/after.ts +48 -45
- package/src/compat/lifecycle/error-funnel.ts +98 -102
- package/src/compat/lifecycle/error-serialize.ts +30 -26
- package/src/compat/lifecycle/error-ui.ts +67 -32
- package/src/compat/lifecycle/instrumentation-client.ts +36 -38
- package/src/compat/lifecycle/instrumentation.ts +85 -85
- package/src/compat/lifecycle/node-console.ts +11 -11
- package/src/compat/lifecycle/testmode.ts +160 -132
- package/src/compat/mdx/compile.ts +60 -57
- package/src/compat/mdx/plugin.ts +11 -11
- package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
- package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
- package/src/compat/metadata.ts +75 -81
- package/src/compat/next/cache.ts +68 -69
- package/src/compat/next/canonical-url.ts +9 -9
- package/src/compat/next/client-cache.ts +19 -19
- package/src/compat/next/client-navigation.ts +112 -116
- package/src/compat/next/client-only.ts +1 -1
- package/src/compat/next/client-script.tsx +99 -93
- package/src/compat/next/client-server.ts +10 -10
- package/src/compat/next/config-loader.ts +176 -173
- package/src/compat/next/config.ts +7 -7
- package/src/compat/next/constants.cjs +6 -6
- package/src/compat/next/constants.ts +6 -6
- package/src/compat/next/custom-server.ts +26 -24
- package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
- package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
- package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
- package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
- package/src/compat/next/dynamic.tsx +21 -18
- package/src/compat/next/error.tsx +52 -52
- package/src/compat/next/font/cache.ts +74 -74
- package/src/compat/next/font/google.ts +2 -2
- package/src/compat/next/font/index.ts +1 -1
- package/src/compat/next/font/local.ts +3 -3
- package/src/compat/next/font/runtime-client.ts +17 -15
- package/src/compat/next/font/runtime.ts +443 -408
- package/src/compat/next/font/shared.ts +120 -108
- package/src/compat/next/form.tsx +63 -63
- package/src/compat/next/head.tsx +2 -2
- package/src/compat/next/headers.ts +103 -95
- package/src/compat/next/image/client.tsx +220 -0
- package/src/compat/next/image/config.ts +56 -58
- package/src/compat/next/image/optimizer.ts +40 -36
- package/src/compat/next/image/patterns.ts +37 -40
- package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
- package/src/compat/next/image/shared.ts +65 -57
- package/src/compat/next/image/static-metadata.ts +98 -107
- package/src/compat/next/image/validate.ts +79 -88
- package/src/compat/next/image.tsx +19 -23
- package/src/compat/next/index.ts +1 -1
- package/src/compat/next/legacy-image.tsx +59 -60
- package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
- package/src/compat/next/link.tsx +158 -158
- package/src/compat/next/navigation.cjs +12 -3
- package/src/compat/next/navigation.ts +48 -50
- package/src/compat/next/offline.ts +27 -27
- package/src/compat/next/og.ts +121 -124
- package/src/compat/next/preferred-region.ts +13 -14
- package/src/compat/next/redirects.ts +58 -56
- package/src/compat/next/resource-hints.ts +73 -76
- package/src/compat/next/rewrites.ts +130 -133
- package/src/compat/next/root-params.ts +45 -45
- package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
- package/src/compat/next/router.cjs +4 -2
- package/src/compat/next/router.ts +58 -61
- package/src/compat/next/script.tsx +108 -108
- package/src/compat/next/server-only.ts +1 -1
- package/src/compat/next/server.ts +19 -19
- package/src/compat/next/svgr.ts +18 -17
- package/src/compat/next/telemetry.ts +24 -24
- package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
- package/src/compat/next/user-agent.ts +53 -49
- package/src/compat/next/web-vitals.ts +22 -24
- package/src/compat/otel/api.ts +41 -41
- package/src/compat/otel/client-trace-metadata.ts +25 -27
- package/src/compat/otel/fetch-span.ts +29 -29
- package/src/compat/otel/tracer.ts +331 -331
- package/src/compat/pages/api.ts +456 -0
- package/src/compat/pages/client-plugin.ts +36 -36
- package/src/compat/pages/router-state.ts +34 -34
- package/src/compat/pages/{index.ts → router.ts} +130 -135
- package/src/compat/ppr/io.ts +12 -12
- package/src/compat/ppr/missing-root-params.ts +34 -38
- package/src/compat/ppr/root-params-scan.ts +66 -66
- package/src/compat/ppr/root-params-transform.ts +24 -26
- package/src/compat/ppr/root-params.ts +30 -30
- package/src/compat/ppr/segment-config-incompat.ts +6 -7
- package/src/compat/protocol.ts +71 -70
- package/src/compat/react/action-state.ts +47 -48
- package/src/compat/react/client-lite.ts +15 -15
- package/src/compat/react/client.ts +4 -4
- package/src/compat/react/compiler-runtime.ts +11 -11
- package/src/compat/react/dom-client.ts +44 -44
- package/src/compat/react/dom-react-server.ts +10 -16
- package/src/compat/react/dom-server.ts +10 -10
- package/src/compat/react/dom.ts +52 -52
- package/src/compat/react/hooks-extra.ts +34 -35
- package/src/compat/react/parity.ts +64 -61
- package/src/compat/react/preact.ts +81 -82
- package/src/compat/react/react-server.ts +28 -28
- package/src/compat/react/router-shim.ts +1 -1
- package/src/compat/react/server-component-use.ts +8 -8
- package/src/compat/react/server-inserted-html.ts +30 -31
- package/src/compat/react/server.ts +53 -55
- package/src/compat/react/use.ts +32 -32
- package/src/compat/react/view-transition.ts +20 -20
- package/src/compat/register/actions.ts +35 -824
- package/src/compat/register/boot.ts +41 -41
- package/src/compat/register/build-tier.ts +5 -5
- package/src/compat/register/build.ts +74 -70
- package/src/compat/register/bundler.ts +161 -156
- package/src/compat/register/cache.ts +20 -20
- package/src/compat/register/client-errors.ts +3 -3
- package/src/compat/register/config.ts +6 -6
- package/src/compat/register/css-extras.ts +30 -34
- package/src/compat/register/edge-runtime.ts +3 -3
- package/src/compat/register/errors.ts +10 -12
- package/src/compat/register/export.ts +16 -16
- package/src/compat/register/font.ts +11 -11
- package/src/compat/register/hooks.ts +2 -2
- package/src/compat/register/image.ts +40 -40
- package/src/compat/register/index.ts +59 -62
- package/src/compat/register/instrumentation-client.ts +10 -10
- package/src/compat/register/lifecycle.ts +25 -28
- package/src/compat/register/mdx.ts +10 -10
- package/src/compat/register/middleware.ts +226 -16
- package/src/compat/register/otel.ts +80 -88
- package/src/compat/register/pages-api.ts +10 -463
- package/src/compat/register/ppr.ts +16 -16
- package/src/compat/register/protocol.ts +17 -18
- package/src/compat/register/proxy.ts +49 -51
- package/src/compat/register/render.ts +79 -76
- package/src/compat/register/routing.ts +162 -159
- package/src/compat/register/segment.ts +24 -1887
- package/src/compat/register/static-image.ts +3 -3
- package/src/compat/register/{misc.ts → taint.ts} +9 -9
- package/src/compat/register/typed-routes.ts +14 -14
- package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
- package/src/compat/register/validation.ts +18 -18
- package/src/compat/segment/loading-boundary.ts +43 -45
- package/src/compat/segment/page-slot.ts +69 -69
- package/src/compat/segment/serve.ts +1884 -0
- package/src/compat/segment/tree.ts +113 -112
- package/src/compat/segment/vary-key.ts +38 -38
- package/src/compat/segment/vary-params.ts +138 -142
- package/src/compat/static-params.ts +14 -12
- package/src/compat/tsconfig-defaults.ts +87 -91
- package/src/compat/typecheck/{index.ts → check.ts} +234 -212
- package/src/compat/typecheck/worker.ts +15 -12
- package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
- package/src/compat/typed-routes/manifest.ts +174 -170
- package/src/compat/typed-routes/typegen.ts +127 -110
- package/src/compat/validation/errors.ts +16 -19
- package/src/compat/validation/prerender-diagnostics.ts +521 -504
- package/src/compat/validation/{index.ts → validate.ts} +647 -648
- package/src/compat-bootstrap.ts +16 -16
- package/src/config.ts +69 -69
- package/src/css/build.ts +230 -227
- package/src/css/postcss.ts +79 -80
- package/src/css/worker.ts +14 -15
- package/src/dev/client-actions.ts +10 -10
- package/src/dev/client-chunk-store.ts +27 -27
- package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
- package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
- package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
- package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
- package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
- package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
- package/src/dev/server.ts +804 -820
- package/src/env.ts +46 -43
- package/src/extensions.ts +491 -478
- package/src/index.ts +8 -8
- package/src/internal.ts +20 -23
- package/src/{islands → render}/boundary-error.ts +3 -3
- package/src/render/hooks.ts +71 -71
- package/src/render/island-context.ts +14 -14
- package/src/render/metadata.ts +310 -310
- package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
- package/src/{ppr.ts → render/ppr.ts} +244 -245
- package/src/render/renderer.ts +2076 -2082
- package/src/render/resource-hints.ts +16 -17
- package/src/render/slots.tsx +224 -235
- package/src/{islands → render}/static-children.ts +9 -12
- package/src/{islands → render}/static-slots.ts +37 -37
- package/src/{cache/context.ts → request/cache.ts} +20 -20
- package/src/request/context.ts +107 -107
- package/src/{dynamic/source.ts → resolve/dynamic.ts} +152 -143
- package/src/resolve/engine.ts +90 -77
- package/src/resolve/imports.ts +475 -463
- package/src/resolve/scan-facts.ts +442 -186
- package/src/resolve/source-text.ts +37 -37
- package/src/{dynamic → resolve}/tree-shake.ts +132 -128
- package/src/routing/forwarded.ts +19 -19
- package/src/routing/handler.ts +84 -91
- package/src/routing/href.ts +69 -70
- package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
- package/src/{proxy.ts → routing/proxy.ts} +306 -312
- package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
- package/src/routing/routes.ts +833 -848
- package/src/routing/slots.ts +164 -160
- package/src/runtime/loader.ts +952 -0
- package/src/{dev → runtime}/module-cache.ts +309 -287
- package/src/{dev → runtime}/module-generations.ts +9 -9
- package/src/{dev → runtime}/module-transform.ts +81 -72
- package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
- package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
- package/src/runtime/vendor.ts +425 -404
- package/src/styles.d.ts +9 -0
- package/src/types.ts +320 -335
- package/src/utils/ansi.ts +5 -5
- package/src/utils/{source.ts → code.ts} +15 -12
- package/src/utils/content-type.ts +3 -3
- package/src/utils/decode.ts +2 -2
- package/src/utils/dev-profile.ts +13 -13
- package/src/utils/error-log.ts +6 -6
- package/src/utils/esbuild.ts +18 -18
- package/src/utils/fs-cache.ts +13 -13
- package/src/utils/fs.ts +57 -49
- package/src/utils/html.ts +20 -24
- package/src/utils/native-require.ts +8 -8
- package/src/utils/serialize.ts +139 -146
- package/src/utils/verbose.ts +18 -18
- package/src/cli/analyze-print.ts +0 -181
- package/src/compat/middleware/manifest.ts +0 -210
- package/src/compat/next/image-client.tsx +0 -215
- package/src/compat/next/link-usage.ts +0 -29
- package/src/css/index.ts +0 -2
- package/src/render/index.ts +0 -1
- package/src/style-modules.d.ts +0 -9
package/src/cli/analyze.ts
CHANGED
|
@@ -1,86 +1,92 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs'
|
|
2
|
-
import { readFile } from 'node:fs/promises'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import zlib from 'node:zlib'
|
|
5
|
-
import { loadConfig } from '../config'
|
|
6
|
-
import { matchRoute } from '../routing/routes'
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import zlib from 'node:zlib'
|
|
5
|
+
import { loadConfig } from '../config'
|
|
6
|
+
import { matchRoute } from '../routing/routes'
|
|
7
|
+
import { bold, cyan, dim } from '../utils/ansi'
|
|
8
|
+
import { listFiles } from '../utils/fs'
|
|
9
|
+
import type { BuildManifest, RouteManifestEntry } from '../types'
|
|
9
10
|
|
|
10
11
|
export interface AnalyzeResult {
|
|
11
|
-
mode: 'production' | 'development'
|
|
12
|
-
root: string
|
|
13
|
-
compression: AnalyzeCompression
|
|
14
|
-
files: AnalyzeFile[]
|
|
15
|
-
routeBundles: AnalyzeRouteBundle[]
|
|
12
|
+
mode: 'production' | 'development'
|
|
13
|
+
root: string
|
|
14
|
+
compression: AnalyzeCompression
|
|
15
|
+
files: AnalyzeFile[]
|
|
16
|
+
routeBundles: AnalyzeRouteBundle[]
|
|
16
17
|
totals: {
|
|
17
|
-
rawBytes: number
|
|
18
|
-
compressedBytes: number
|
|
19
|
-
}
|
|
18
|
+
rawBytes: number
|
|
19
|
+
compressedBytes: number
|
|
20
|
+
}
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export interface AnalyzeFile {
|
|
23
|
-
path: string
|
|
24
|
-
type: 'html' | 'css' | 'js' | 'asset'
|
|
25
|
-
rawBytes: number
|
|
26
|
-
compressedBytes: number
|
|
24
|
+
path: string
|
|
25
|
+
type: 'html' | 'css' | 'js' | 'asset'
|
|
26
|
+
rawBytes: number
|
|
27
|
+
compressedBytes: number
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export interface AnalyzeRouteBundle {
|
|
30
|
-
route: string
|
|
31
|
-
page: 'server' | 'client'
|
|
32
|
-
initial: AnalyzeBundleFile[]
|
|
33
|
-
visibleDynamic: AnalyzeDynamicBundleFile[]
|
|
34
|
-
dynamic: AnalyzeDynamicBundleFile[]
|
|
35
|
-
lazyShared: AnalyzeBundleFile[]
|
|
31
|
+
route: string
|
|
32
|
+
page: 'server' | 'client'
|
|
33
|
+
initial: AnalyzeBundleFile[]
|
|
34
|
+
visibleDynamic: AnalyzeDynamicBundleFile[]
|
|
35
|
+
dynamic: AnalyzeDynamicBundleFile[]
|
|
36
|
+
lazyShared: AnalyzeBundleFile[]
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
export interface AnalyzeBundleFile {
|
|
39
|
-
path: string
|
|
40
|
-
rawBytes: number
|
|
41
|
-
compressedBytes: number
|
|
40
|
+
path: string
|
|
41
|
+
rawBytes: number
|
|
42
|
+
compressedBytes: number
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export interface AnalyzeDynamicBundleFile extends AnalyzeBundleFile {
|
|
45
|
-
exportName: string
|
|
46
|
-
load: 'render' | 'visible'
|
|
46
|
+
exportName: string
|
|
47
|
+
load: 'render' | 'visible'
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
export type AnalyzeCompression = 'gzip' | 'brotli'
|
|
50
|
+
export type AnalyzeCompression = 'gzip' | 'brotli'
|
|
50
51
|
|
|
51
52
|
interface AnalyzeOptions {
|
|
52
|
-
compression?: AnalyzeCompression
|
|
53
|
-
route?: string
|
|
53
|
+
compression?: AnalyzeCompression
|
|
54
|
+
route?: string
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
export async function analyzeProject(
|
|
57
|
-
|
|
57
|
+
export async function analyzeProject(
|
|
58
|
+
root?: string,
|
|
59
|
+
options: AnalyzeOptions = {},
|
|
60
|
+
): Promise<AnalyzeResult> {
|
|
61
|
+
const config = await loadConfig(root)
|
|
58
62
|
if (!existsSync(config.outPath)) {
|
|
59
|
-
throw new Error(`No PNext output found at ${config.outPath}. Run pnext build first.`)
|
|
63
|
+
throw new Error(`No PNext output found at ${config.outPath}. Run pnext build first.`)
|
|
60
64
|
}
|
|
61
65
|
|
|
62
|
-
const target = analyzeTarget(config.outPath)
|
|
66
|
+
const target = analyzeTarget(config.outPath)
|
|
63
67
|
if (!target) {
|
|
64
|
-
throw new Error(
|
|
68
|
+
throw new Error(
|
|
69
|
+
`No PNext build or dev output found at ${config.outPath}. Run pnext build or pnext dev first.`,
|
|
70
|
+
)
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
const compression = options.compression ?? 'gzip'
|
|
68
|
-
const files = await listFiles(target.root)
|
|
73
|
+
const compression = options.compression ?? 'gzip'
|
|
74
|
+
const files = await listFiles(target.root)
|
|
69
75
|
const rows = await Promise.all(
|
|
70
76
|
files.map(async file => {
|
|
71
|
-
const bytes = await readFile(file)
|
|
77
|
+
const bytes = await readFile(file)
|
|
72
78
|
return {
|
|
73
79
|
path: path.relative(target.root, file),
|
|
74
80
|
type: fileType(file),
|
|
75
81
|
rawBytes: bytes.length,
|
|
76
82
|
compressedBytes: compressedSize(bytes, compression),
|
|
77
|
-
}
|
|
83
|
+
}
|
|
78
84
|
}),
|
|
79
|
-
)
|
|
85
|
+
)
|
|
80
86
|
const routeBundles =
|
|
81
87
|
target.mode === 'production'
|
|
82
88
|
? await analyzeRouteBundles(config.outPath, target.root, rows, options.route)
|
|
83
|
-
: []
|
|
89
|
+
: []
|
|
84
90
|
|
|
85
91
|
return {
|
|
86
92
|
mode: target.mode,
|
|
@@ -95,7 +101,7 @@ export async function analyzeProject(root?: string, options: AnalyzeOptions = {}
|
|
|
95
101
|
}),
|
|
96
102
|
{ rawBytes: 0, compressedBytes: 0 },
|
|
97
103
|
),
|
|
98
|
-
}
|
|
104
|
+
}
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
async function analyzeRouteBundles(
|
|
@@ -104,51 +110,51 @@ async function analyzeRouteBundles(
|
|
|
104
110
|
files: AnalyzeFile[],
|
|
105
111
|
routeFilter?: string,
|
|
106
112
|
) {
|
|
107
|
-
const manifestPath = path.join(outPath, 'manifest.json')
|
|
108
|
-
if (!existsSync(manifestPath)) return []
|
|
113
|
+
const manifestPath = path.join(outPath, 'manifest.json')
|
|
114
|
+
if (!existsSync(manifestPath)) return []
|
|
109
115
|
|
|
110
|
-
const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) as BuildManifest
|
|
111
|
-
const pages = manifest.routes.filter(route => route.kind === 'page')
|
|
112
|
-
const selected = routeFilter ? filterAnalyzeRoutes(pages, routeFilter) : pages
|
|
116
|
+
const manifest = JSON.parse(await readFile(manifestPath, 'utf8')) as BuildManifest
|
|
117
|
+
const pages = manifest.routes.filter(route => route.kind === 'page')
|
|
118
|
+
const selected = routeFilter ? filterAnalyzeRoutes(pages, routeFilter) : pages
|
|
113
119
|
if (routeFilter && selected.length === 0) {
|
|
114
120
|
throw new Error(
|
|
115
121
|
`No route matches ${routeFilter}. Routes:\n ${pages
|
|
116
122
|
.map(route => publicRoutePath(route.route))
|
|
117
123
|
.join('\n ')}`,
|
|
118
|
-
)
|
|
124
|
+
)
|
|
119
125
|
}
|
|
120
|
-
const sizes = new Map(files.map(file => [toPosix(file.path), file]))
|
|
121
|
-
const contents = new Map<string, string>()
|
|
126
|
+
const sizes = new Map(files.map(file => [toPosix(file.path), file]))
|
|
127
|
+
const contents = new Map<string, string>()
|
|
122
128
|
|
|
123
129
|
async function js(pathname: string) {
|
|
124
|
-
const normalized = toPosix(pathname)
|
|
125
|
-
if (contents.has(normalized)) return contents.get(normalized) ?? ''
|
|
126
|
-
const text = await readFile(path.join(publicPath, normalized), 'utf8')
|
|
127
|
-
contents.set(normalized, text)
|
|
128
|
-
return text
|
|
130
|
+
const normalized = toPosix(pathname)
|
|
131
|
+
if (contents.has(normalized)) return contents.get(normalized) ?? ''
|
|
132
|
+
const text = await readFile(path.join(publicPath, normalized), 'utf8')
|
|
133
|
+
contents.set(normalized, text)
|
|
134
|
+
return text
|
|
129
135
|
}
|
|
130
136
|
|
|
131
|
-
const bundles: AnalyzeRouteBundle[] = []
|
|
137
|
+
const bundles: AnalyzeRouteBundle[] = []
|
|
132
138
|
for (const route of selected) {
|
|
133
|
-
bundles.push(await analyzeRouteBundle(route, sizes, js))
|
|
139
|
+
bundles.push(await analyzeRouteBundle(route, sizes, js))
|
|
134
140
|
}
|
|
135
|
-
return bundles
|
|
141
|
+
return bundles
|
|
136
142
|
}
|
|
137
143
|
|
|
138
144
|
// Accepts the route in ':id' or '[id]' template form, or a concrete pathname
|
|
139
145
|
// (e.g. /users/ada) matched through the route patterns.
|
|
140
146
|
function filterAnalyzeRoutes(pages: RouteManifestEntry[], filter: string) {
|
|
141
|
-
const normalized = filter === '/' ? filter : filter.replace(/\/+$/, '')
|
|
147
|
+
const normalized = filter === '/' ? filter : filter.replace(/\/+$/, '')
|
|
142
148
|
const direct = pages.filter(
|
|
143
149
|
route => route.route === normalized || publicRoutePath(route.route) === normalized,
|
|
144
|
-
)
|
|
145
|
-
if (direct.length > 0) return direct
|
|
146
|
-
const matched = matchRoute(pages, normalized)
|
|
147
|
-
return matched ? [matched.route] : []
|
|
150
|
+
)
|
|
151
|
+
if (direct.length > 0) return direct
|
|
152
|
+
const matched = matchRoute(pages, normalized)
|
|
153
|
+
return matched ? [matched.route] : []
|
|
148
154
|
}
|
|
149
155
|
|
|
150
156
|
function publicRoutePath(route: string) {
|
|
151
|
-
return route.replace(/:([a-zA-Z0-9_]+)\*/g, '[...$1]').replace(/:([a-zA-Z0-9_]+)/g, '[$1]')
|
|
157
|
+
return route.replace(/:([a-zA-Z0-9_]+)\*/g, '[...$1]').replace(/:([a-zA-Z0-9_]+)/g, '[$1]')
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
async function analyzeRouteBundle(
|
|
@@ -156,51 +162,52 @@ async function analyzeRouteBundle(
|
|
|
156
162
|
sizes: Map<string, AnalyzeFile>,
|
|
157
163
|
js: (path: string) => Promise<string>,
|
|
158
164
|
): Promise<AnalyzeRouteBundle> {
|
|
159
|
-
const initial = new Set<string>()
|
|
160
|
-
addIfExists(initial, sizes, routeHtmlPath(route))
|
|
161
|
-
addIfExists(initial, sizes, 'assets/global.css')
|
|
162
|
-
addIfExists(initial, sizes, `assets/${route.id}.css`)
|
|
165
|
+
const initial = new Set<string>()
|
|
166
|
+
addIfExists(initial, sizes, routeHtmlPath(route))
|
|
167
|
+
addIfExists(initial, sizes, 'assets/global.css')
|
|
168
|
+
addIfExists(initial, sizes, `assets/${route.id}.css`)
|
|
163
169
|
|
|
164
170
|
if (route.clientEntry) {
|
|
165
|
-
addIfExists(initial, sizes, route.clientEntry)
|
|
166
|
-
for (const dependency of await staticDependencies(route.clientEntry, sizes, js))
|
|
171
|
+
addIfExists(initial, sizes, route.clientEntry)
|
|
172
|
+
for (const dependency of await staticDependencies(route.clientEntry, sizes, js))
|
|
173
|
+
initial.add(dependency)
|
|
167
174
|
}
|
|
168
175
|
|
|
169
|
-
const visibleDynamic: AnalyzeDynamicBundleFile[] = []
|
|
170
|
-
const dynamic: AnalyzeDynamicBundleFile[] = []
|
|
171
|
-
const dynamicTargets = new Set<string>()
|
|
172
|
-
const lazyShared = new Set<string>()
|
|
176
|
+
const visibleDynamic: AnalyzeDynamicBundleFile[] = []
|
|
177
|
+
const dynamic: AnalyzeDynamicBundleFile[] = []
|
|
178
|
+
const dynamicTargets = new Set<string>()
|
|
179
|
+
const lazyShared = new Set<string>()
|
|
173
180
|
|
|
174
181
|
if (route.clientEntry && sizes.has(toPosix(route.clientEntry))) {
|
|
175
|
-
const entrySource = await js(route.clientEntry)
|
|
176
|
-
const imports = dynamicImports(entrySource, route.clientEntry)
|
|
182
|
+
const entrySource = await js(route.clientEntry)
|
|
183
|
+
const imports = dynamicImports(entrySource, route.clientEntry)
|
|
177
184
|
for (const imported of imports) {
|
|
178
|
-
const reference = route.clientReferences.find(item => item.exportName === imported.exportName)
|
|
185
|
+
const reference = route.clientReferences.find(item => item.exportName === imported.exportName)
|
|
179
186
|
const load: AnalyzeDynamicBundleFile['load'] =
|
|
180
|
-
reference?.dynamic?.load === 'visible' ? 'visible' : 'render'
|
|
181
|
-
const row = sizes.get(imported.path)
|
|
182
|
-
if (!row) continue
|
|
183
|
-
dynamicTargets.add(imported.path)
|
|
187
|
+
reference?.dynamic?.load === 'visible' ? 'visible' : 'render'
|
|
188
|
+
const row = sizes.get(imported.path)
|
|
189
|
+
if (!row) continue
|
|
190
|
+
dynamicTargets.add(imported.path)
|
|
184
191
|
const item: AnalyzeDynamicBundleFile = {
|
|
185
192
|
path: imported.path,
|
|
186
193
|
exportName: imported.exportName,
|
|
187
194
|
load,
|
|
188
195
|
rawBytes: row.rawBytes,
|
|
189
196
|
compressedBytes: row.compressedBytes,
|
|
190
|
-
}
|
|
191
|
-
if (load === 'visible') visibleDynamic.push(item)
|
|
192
|
-
else dynamic.push(item)
|
|
197
|
+
}
|
|
198
|
+
if (load === 'visible') visibleDynamic.push(item)
|
|
199
|
+
else dynamic.push(item)
|
|
193
200
|
|
|
194
201
|
for (const dependency of await staticDependencies(imported.path, sizes, js)) {
|
|
195
|
-
if (!initial.has(dependency) && !dynamicTargets.has(dependency)) lazyShared.add(dependency)
|
|
202
|
+
if (!initial.has(dependency) && !dynamicTargets.has(dependency)) lazyShared.add(dependency)
|
|
196
203
|
}
|
|
197
204
|
}
|
|
198
205
|
|
|
199
206
|
for (const importedPath of dynamicImportPaths(entrySource, route.clientEntry)) {
|
|
200
|
-
if (initial.has(importedPath) || dynamicTargets.has(importedPath)) continue
|
|
201
|
-
if (sizes.has(importedPath)) lazyShared.add(importedPath)
|
|
207
|
+
if (initial.has(importedPath) || dynamicTargets.has(importedPath)) continue
|
|
208
|
+
if (sizes.has(importedPath)) lazyShared.add(importedPath)
|
|
202
209
|
for (const dependency of await staticDependencies(importedPath, sizes, js)) {
|
|
203
|
-
if (!initial.has(dependency) && !dynamicTargets.has(dependency)) lazyShared.add(dependency)
|
|
210
|
+
if (!initial.has(dependency) && !dynamicTargets.has(dependency)) lazyShared.add(dependency)
|
|
204
211
|
}
|
|
205
212
|
}
|
|
206
213
|
}
|
|
@@ -212,7 +219,7 @@ async function analyzeRouteBundle(
|
|
|
212
219
|
visibleDynamic: visibleDynamic.sort((a, b) => b.compressedBytes - a.compressedBytes),
|
|
213
220
|
dynamic: dynamic.sort((a, b) => b.compressedBytes - a.compressedBytes),
|
|
214
221
|
lazyShared: bundleFiles([...lazyShared], sizes),
|
|
215
|
-
}
|
|
222
|
+
}
|
|
216
223
|
}
|
|
217
224
|
|
|
218
225
|
async function staticDependencies(
|
|
@@ -221,108 +228,290 @@ async function staticDependencies(
|
|
|
221
228
|
js: (path: string) => Promise<string>,
|
|
222
229
|
seen = new Set<string>(),
|
|
223
230
|
) {
|
|
224
|
-
const dependencies = new Set<string>()
|
|
225
|
-
const normalized = toPosix(entry)
|
|
226
|
-
if (seen.has(normalized) || !sizes.has(normalized)) return dependencies
|
|
227
|
-
seen.add(normalized)
|
|
231
|
+
const dependencies = new Set<string>()
|
|
232
|
+
const normalized = toPosix(entry)
|
|
233
|
+
if (seen.has(normalized) || !sizes.has(normalized)) return dependencies
|
|
234
|
+
seen.add(normalized)
|
|
228
235
|
|
|
229
236
|
for (const specifier of staticImports(await js(normalized))) {
|
|
230
|
-
const dependency = resolveBuiltImport(normalized, specifier)
|
|
231
|
-
if (!dependency || !sizes.has(dependency)) continue
|
|
232
|
-
dependencies.add(dependency)
|
|
233
|
-
for (const transitive of await staticDependencies(dependency, sizes, js, seen))
|
|
237
|
+
const dependency = resolveBuiltImport(normalized, specifier)
|
|
238
|
+
if (!dependency || !sizes.has(dependency)) continue
|
|
239
|
+
dependencies.add(dependency)
|
|
240
|
+
for (const transitive of await staticDependencies(dependency, sizes, js, seen))
|
|
241
|
+
dependencies.add(transitive)
|
|
234
242
|
}
|
|
235
243
|
|
|
236
|
-
return dependencies
|
|
244
|
+
return dependencies
|
|
237
245
|
}
|
|
238
246
|
|
|
239
247
|
function dynamicImports(source: string, from: string) {
|
|
240
|
-
const imports: { path: string; exportName: string }[] = []
|
|
241
|
-
const pattern =
|
|
242
|
-
|
|
248
|
+
const imports: { path: string; exportName: string }[] = []
|
|
249
|
+
const pattern =
|
|
250
|
+
/import\("([^"]+)"\)\.then\([A-Za-z_$][\w$]*=>[A-Za-z_$][\w$]*\.([A-Za-z_$][\w$]*)\)/g
|
|
251
|
+
let match: RegExpExecArray | null
|
|
243
252
|
while ((match = pattern.exec(source))) {
|
|
244
|
-
const specifier = match[1]
|
|
245
|
-
const exportName = match[2]
|
|
246
|
-
if (!specifier || !exportName) continue
|
|
247
|
-
const resolved = resolveBuiltImport(from, specifier)
|
|
248
|
-
if (resolved) imports.push({ path: resolved, exportName })
|
|
253
|
+
const specifier = match[1]
|
|
254
|
+
const exportName = match[2]
|
|
255
|
+
if (!specifier || !exportName) continue
|
|
256
|
+
const resolved = resolveBuiltImport(from, specifier)
|
|
257
|
+
if (resolved) imports.push({ path: resolved, exportName })
|
|
249
258
|
}
|
|
250
|
-
return imports
|
|
259
|
+
return imports
|
|
251
260
|
}
|
|
252
261
|
|
|
253
262
|
function dynamicImportPaths(source: string, from: string) {
|
|
254
|
-
const imports: string[] = []
|
|
255
|
-
const pattern = /import\("([^"]+)"\)/g
|
|
256
|
-
let match: RegExpExecArray | null
|
|
263
|
+
const imports: string[] = []
|
|
264
|
+
const pattern = /import\("([^"]+)"\)/g
|
|
265
|
+
let match: RegExpExecArray | null
|
|
257
266
|
while ((match = pattern.exec(source))) {
|
|
258
|
-
const specifier = match[1]
|
|
259
|
-
if (!specifier) continue
|
|
260
|
-
const resolved = resolveBuiltImport(from, specifier)
|
|
261
|
-
if (resolved) imports.push(resolved)
|
|
267
|
+
const specifier = match[1]
|
|
268
|
+
if (!specifier) continue
|
|
269
|
+
const resolved = resolveBuiltImport(from, specifier)
|
|
270
|
+
if (resolved) imports.push(resolved)
|
|
262
271
|
}
|
|
263
|
-
return imports
|
|
272
|
+
return imports
|
|
264
273
|
}
|
|
265
274
|
|
|
266
275
|
function staticImports(source: string) {
|
|
267
|
-
const imports: string[] = []
|
|
268
|
-
const pattern = /import\s*(?:[^'"]*?from\s*)?["']([^'"]+)["']/g
|
|
269
|
-
let match: RegExpExecArray | null
|
|
276
|
+
const imports: string[] = []
|
|
277
|
+
const pattern = /import\s*(?:[^'"]*?from\s*)?["']([^'"]+)["']/g
|
|
278
|
+
let match: RegExpExecArray | null
|
|
270
279
|
while ((match = pattern.exec(source))) {
|
|
271
|
-
if (match[1]) imports.push(match[1])
|
|
280
|
+
if (match[1]) imports.push(match[1])
|
|
272
281
|
}
|
|
273
|
-
return imports
|
|
282
|
+
return imports
|
|
274
283
|
}
|
|
275
284
|
|
|
276
285
|
function resolveBuiltImport(from: string, specifier: string) {
|
|
277
|
-
if (!specifier.startsWith('.')) return null
|
|
278
|
-
return toPosix(
|
|
286
|
+
if (!specifier.startsWith('.')) return null
|
|
287
|
+
return toPosix(
|
|
288
|
+
path.posix.normalize(path.posix.join(path.posix.dirname(toPosix(from)), specifier)),
|
|
289
|
+
)
|
|
279
290
|
}
|
|
280
291
|
|
|
281
292
|
function addIfExists(items: Set<string>, sizes: Map<string, AnalyzeFile>, pathname: string) {
|
|
282
|
-
const normalized = toPosix(pathname)
|
|
283
|
-
if (sizes.has(normalized)) items.add(normalized)
|
|
293
|
+
const normalized = toPosix(pathname)
|
|
294
|
+
if (sizes.has(normalized)) items.add(normalized)
|
|
284
295
|
}
|
|
285
296
|
|
|
286
297
|
function bundleFiles(paths: string[], sizes: Map<string, AnalyzeFile>) {
|
|
287
298
|
return paths
|
|
288
299
|
.map(pathname => {
|
|
289
|
-
const row = sizes.get(pathname)
|
|
290
|
-
return row
|
|
300
|
+
const row = sizes.get(pathname)
|
|
301
|
+
return row
|
|
302
|
+
? { path: pathname, rawBytes: row.rawBytes, compressedBytes: row.compressedBytes }
|
|
303
|
+
: null
|
|
291
304
|
})
|
|
292
305
|
.filter((file): file is AnalyzeBundleFile => Boolean(file))
|
|
293
|
-
.sort((a, b) => b.compressedBytes - a.compressedBytes)
|
|
306
|
+
.sort((a, b) => b.compressedBytes - a.compressedBytes)
|
|
294
307
|
}
|
|
295
308
|
|
|
296
309
|
function routeHtmlPath(route: RouteManifestEntry) {
|
|
297
|
-
return route.route === '/' ? 'index.html' : `${route.route.replace(/^\/+/, '')}/index.html
|
|
310
|
+
return route.route === '/' ? 'index.html' : `${route.route.replace(/^\/+/, '')}/index.html`
|
|
298
311
|
}
|
|
299
312
|
|
|
300
313
|
function analyzeTarget(outPath: string) {
|
|
301
|
-
const publicPath = path.join(outPath, 'public')
|
|
302
|
-
if (existsSync(publicPath)) return { mode: 'production' as const, root: publicPath }
|
|
314
|
+
const publicPath = path.join(outPath, 'public')
|
|
315
|
+
if (existsSync(publicPath)) return { mode: 'production' as const, root: publicPath }
|
|
303
316
|
|
|
304
|
-
const cachePath = path.join(outPath, 'cache')
|
|
305
|
-
if (existsSync(cachePath)) return { mode: 'development' as const, root: cachePath }
|
|
317
|
+
const cachePath = path.join(outPath, 'cache')
|
|
318
|
+
if (existsSync(cachePath)) return { mode: 'development' as const, root: cachePath }
|
|
306
319
|
|
|
307
|
-
return null
|
|
320
|
+
return null
|
|
308
321
|
}
|
|
309
322
|
|
|
310
323
|
function compressedSize(bytes: Buffer, compression: AnalyzeCompression) {
|
|
311
324
|
if (compression === 'brotli') {
|
|
312
325
|
return zlib.brotliCompressSync(bytes, {
|
|
313
326
|
params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 11 },
|
|
314
|
-
}).length
|
|
327
|
+
}).length
|
|
315
328
|
}
|
|
316
|
-
return zlib.gzipSync(bytes, { level: 9 }).length
|
|
329
|
+
return zlib.gzipSync(bytes, { level: 9 }).length
|
|
317
330
|
}
|
|
318
331
|
|
|
319
332
|
function fileType(file: string): 'html' | 'css' | 'js' | 'asset' {
|
|
320
|
-
if (file.endsWith('.html')) return 'html'
|
|
321
|
-
if (file.endsWith('.css')) return 'css'
|
|
322
|
-
if (file.endsWith('.js')) return 'js'
|
|
323
|
-
return 'asset'
|
|
333
|
+
if (file.endsWith('.html')) return 'html'
|
|
334
|
+
if (file.endsWith('.css')) return 'css'
|
|
335
|
+
if (file.endsWith('.js')) return 'js'
|
|
336
|
+
return 'asset'
|
|
324
337
|
}
|
|
325
338
|
|
|
326
339
|
function toPosix(value: string) {
|
|
327
|
-
return value.split(path.sep).join('/')
|
|
340
|
+
return value.split(path.sep).join('/')
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export function printAnalyzeResult(
|
|
344
|
+
result: AnalyzeResult,
|
|
345
|
+
options: { route?: string; files: boolean },
|
|
346
|
+
) {
|
|
347
|
+
console.log(
|
|
348
|
+
`PNext analyze: ${result.mode === 'production' ? 'production build' : 'dev server cache'} at ${dim(result.root)} (${result.compression})`,
|
|
349
|
+
)
|
|
350
|
+
printRouteBundles(result)
|
|
351
|
+
// With a route filter, the remaining files are mostly other routes' bundles —
|
|
352
|
+
// listing them as "other" would misread as unowned weight.
|
|
353
|
+
if (!options.route) printAnalyzeFiles(result, { details: options.files })
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function printRouteBundles(result: AnalyzeResult) {
|
|
357
|
+
if (result.routeBundles.length === 0) return
|
|
358
|
+
|
|
359
|
+
console.log(`\n${bold('Routes')}`)
|
|
360
|
+
for (const bundle of result.routeBundles) {
|
|
361
|
+
console.log(`\n${cyan(bundle.route)} ${dim(`(${bundle.page} page)`)}`)
|
|
362
|
+
printBundleGroup('initial', bundle.initial, result.compression)
|
|
363
|
+
printDynamicBundleGroup('visible dynamic', bundle.visibleDynamic, result.compression)
|
|
364
|
+
printDynamicBundleGroup('dynamic', bundle.dynamic, result.compression)
|
|
365
|
+
printBundleGroup('shared after dynamic', bundle.lazyShared, result.compression)
|
|
366
|
+
}
|
|
367
|
+
console.log('')
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function printBundleGroup(
|
|
371
|
+
label: string,
|
|
372
|
+
files: AnalyzeResult['routeBundles'][number]['initial'],
|
|
373
|
+
compression: AnalyzeCompression,
|
|
374
|
+
) {
|
|
375
|
+
if (files.length === 0) return
|
|
376
|
+
console.log(`\n ${bold(label.padEnd(22))} ${formatTotalSize(files, compression)}`)
|
|
377
|
+
printBundleFiles(files, compression, ' ', rowColumns(files))
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function printDynamicBundleGroup(
|
|
381
|
+
label: string,
|
|
382
|
+
files: AnalyzeResult['routeBundles'][number]['visibleDynamic'],
|
|
383
|
+
compression: AnalyzeCompression,
|
|
384
|
+
) {
|
|
385
|
+
if (files.length === 0) return
|
|
386
|
+
console.log(`\n ${bold(label)}`)
|
|
387
|
+
const columns = rowColumns(files)
|
|
388
|
+
const componentWidth = Math.max(...files.map(file => file.exportName.length))
|
|
389
|
+
const pathWidth = Math.max(...files.map(file => file.path.length))
|
|
390
|
+
for (const file of files) {
|
|
391
|
+
console.log(
|
|
392
|
+
` ${file.exportName.padEnd(componentWidth)} ${dim(file.path.padEnd(pathWidth))} ${formatFileSize(file, compression, columns)}`,
|
|
393
|
+
)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function formatTotalSize(
|
|
398
|
+
files: { rawBytes: number; compressedBytes: number }[],
|
|
399
|
+
compression: AnalyzeCompression,
|
|
400
|
+
) {
|
|
401
|
+
const rawBytes = files.reduce((total, file) => total + file.rawBytes, 0)
|
|
402
|
+
const compressedBytes = files.reduce((total, file) => total + file.compressedBytes, 0)
|
|
403
|
+
return `${formatBytes(compressedBytes)} ${compression} ${dim(`${formatBytes(rawBytes)} raw`)}`
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function printAnalyzeFiles(result: AnalyzeResult, options: { details: boolean }) {
|
|
407
|
+
const routeFiles = new Set(
|
|
408
|
+
result.routeBundles.flatMap(bundle => [
|
|
409
|
+
...bundle.initial.map(file => file.path),
|
|
410
|
+
...bundle.visibleDynamic.map(file => file.path),
|
|
411
|
+
...bundle.dynamic.map(file => file.path),
|
|
412
|
+
...bundle.lazyShared.map(file => file.path),
|
|
413
|
+
]),
|
|
414
|
+
)
|
|
415
|
+
const sorted = sortedAnalyzeFiles(result.files).filter(file => !routeFiles.has(file.path))
|
|
416
|
+
if (sorted.length === 0) return
|
|
417
|
+
|
|
418
|
+
if (!options.details) {
|
|
419
|
+
console.log(
|
|
420
|
+
`Other files: ${formatFileCount(sorted)} ${formatTotalSize(sorted, result.compression)}`,
|
|
421
|
+
)
|
|
422
|
+
console.log('Run pnext analyze --files to list them.')
|
|
423
|
+
return
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
console.log(`${bold('Other files')}\n`)
|
|
427
|
+
for (const type of ['html', 'css', 'js', 'asset'] as const) {
|
|
428
|
+
const files = sorted.filter(file => file.type === type)
|
|
429
|
+
if (files.length === 0) continue
|
|
430
|
+
console.log(bold(type))
|
|
431
|
+
printBundleFiles(files, result.compression, ' ', rowColumns(files))
|
|
432
|
+
console.log('')
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function printBundleFiles(
|
|
437
|
+
files: { path: string; rawBytes: number; compressedBytes: number }[],
|
|
438
|
+
compression: AnalyzeCompression,
|
|
439
|
+
indent: string,
|
|
440
|
+
columns: RowColumns,
|
|
441
|
+
) {
|
|
442
|
+
const pathWidth = Math.max(...files.map(file => file.path.length))
|
|
443
|
+
for (const file of files) {
|
|
444
|
+
console.log(
|
|
445
|
+
`${indent}${dim(file.path.padEnd(pathWidth))} ${formatFileSize(file, compression, columns)}`,
|
|
446
|
+
)
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
interface RowColumns {
|
|
451
|
+
compressedWidth: number
|
|
452
|
+
rawWidth: number
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function rowColumns(files: { rawBytes: number; compressedBytes: number }[]): RowColumns {
|
|
456
|
+
return {
|
|
457
|
+
compressedWidth: Math.max(...files.map(file => formatBytes(file.compressedBytes).length)),
|
|
458
|
+
rawWidth: Math.max(...files.map(file => formatBytes(file.rawBytes).length)),
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function formatFileSize(
|
|
463
|
+
file: { rawBytes: number; compressedBytes: number },
|
|
464
|
+
compression: AnalyzeCompression,
|
|
465
|
+
columns: RowColumns,
|
|
466
|
+
) {
|
|
467
|
+
const compressed = formatBytes(file.compressedBytes).padStart(columns.compressedWidth)
|
|
468
|
+
const raw = `${formatBytes(file.rawBytes).padStart(columns.rawWidth)} raw`
|
|
469
|
+
return `${compressed} ${compression} ${dim(raw)}`
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function formatFileCount(files: AnalyzeResult['files']) {
|
|
473
|
+
const counts = new Map<string, number>()
|
|
474
|
+
for (const file of files) counts.set(file.type, (counts.get(file.type) ?? 0) + 1)
|
|
475
|
+
return [...counts].map(([type, count]) => `${count} ${pluralFileType(type, count)}`).join(', ')
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function pluralFileType(type: string, count: number) {
|
|
479
|
+
if (count === 1) return type
|
|
480
|
+
if (type === 'js' || type === 'css' || type === 'html') return type
|
|
481
|
+
return `${type}s`
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function sortedAnalyzeFiles(files: AnalyzeResult['files']) {
|
|
485
|
+
return [...files].sort((a, b) => {
|
|
486
|
+
const typeDelta = analyzeTypeRank(a.type) - analyzeTypeRank(b.type)
|
|
487
|
+
if (typeDelta) return typeDelta
|
|
488
|
+
const sizeDelta = b.compressedBytes - a.compressedBytes
|
|
489
|
+
if (sizeDelta) return sizeDelta
|
|
490
|
+
return a.path.localeCompare(b.path)
|
|
491
|
+
})
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function analyzeTypeRank(type: AnalyzeResult['files'][number]['type']) {
|
|
495
|
+
switch (type) {
|
|
496
|
+
case 'html':
|
|
497
|
+
return 0
|
|
498
|
+
case 'css':
|
|
499
|
+
return 1
|
|
500
|
+
case 'js':
|
|
501
|
+
return 2
|
|
502
|
+
case 'asset':
|
|
503
|
+
return 3
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function formatBytes(bytes: number) {
|
|
508
|
+
if (bytes < 1024) return `${bytes} B`
|
|
509
|
+
const units = ['KB', 'MB', 'GB', 'TB'] as const
|
|
510
|
+
let value = bytes / 1024
|
|
511
|
+
let unitIndex = 0
|
|
512
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
513
|
+
value /= 1024
|
|
514
|
+
unitIndex += 1
|
|
515
|
+
}
|
|
516
|
+
return `${value >= 10 ? value.toFixed(1) : value.toFixed(2)} ${units[unitIndex]}`
|
|
328
517
|
}
|