Skip to content

perf(next-plugin): profile and reuse Turbopack prewarm output - #645

Merged
owjs3901 merged 45 commits into
mainfrom
owjs3901/turbo-profile
Aug 29, 2026
Merged

perf(next-plugin): profile and reuse Turbopack prewarm output#645
owjs3901 merged 45 commits into
mainfrom
owjs3901/turbo-profile

Conversation

@owjs3901

@owjs3901 owjs3901 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add opt-in DEVUP_UI_PROFILE=1 structured Turbopack timings for graph setup, production prewarm, coordinator extraction, snapshots, serialization, and writes
  • reuse byte-identical production prewarm output in both singleCss and per-file mode, avoiding duplicate WASM extraction and duplicate snapshot/write work on repeated Turbopack loader requests
  • keep polymorphic React inference exact while preventing redundant native-prop inference and duplicate intrinsic-prop conditionals
  • ship a 1.18 MB lite extraction engine without Boa (71.8% smaller than the 4.19 MB full engine) and defer the Webpack adapter so Turbopack loads only the engine it needs
  • conservatively translate fully static style({...}) .css.ts/.css.js modules to Devup css({...}) before extraction, allowing those builds to use the lite engine; variables, calls, spreads, extra statements, theme APIs, development mode, and source-map builds retain the full Boa engine
  • make the existing benchmark.js comparison cold and order-balanced: clear .next, dist, df, and tsconfig.tsbuildinfo, use Turbopack for all three compared targets, alternate order, and report six-sample medians

No separate benchmark workflow was added. bun.lock remains unrelated and excluded. The add-bench PR was not touched.

Correctness boundary

The static fast path accepts only one style import and exported style() calls whose values are recursively static objects, arrays, strings, numbers, booleans, or null. Anything executable or unsupported fails closed to the existing full evaluator. Watch mode and production browser source maps always use full WASM. Public React declarations and theme inference are unchanged.

An actual production Turbopack build of the vanilla fixture selected wasmVariant:"lite"; its generated CSS matched the previous full build, prewarm extraction was 0.69 ms on the build config evaluation, and every loader request was a byte-identical cache hit with extractMs:0. An actual build after changing the fixture to use a local variable selected wasmVariant:"full" and also succeeded; that temporary fixture change was reverted.

Validation

  • next-plugin: 126 passed, 0 failed; changed coordinator/static-transform sources report 100% function and line coverage
  • next-plugin lint, TypeScript build, and bundled CJS/ESM entry builds pass
  • full and lite WASM release builds, extractor/WASM/Rust checks, React TypeScript 6/7 inference checks, package/tarball checks, landing E2E, and Codecov passed on the preceding commits
  • current CI benchmark job passed: https://github.com/dev-five-git/devup-ui/actions/runs/33231944233/job/99046168486

Existing benchmark.js CI results

All rows are six cold samples in the existing Publish workflow. Initial one-shot records are excluded from medians.

Commit / target Median Cold samples
4858b339 Tailwind Turbo 6.81 s 6.78, 6.73, 6.84, 6.87, 6.92, 6.68
4858b339 Devup static .css.ts 6.78 s 6.71, 6.76, 6.86, 6.65, 6.86, 6.81
4858b339 Devup direct APIs / singleCss 6.81 s 6.84, 6.81, 6.95, 6.71, 6.81, 6.74
4b94b161 Tailwind Turbo 6.47 s 6.40, 6.40, 6.48, 6.46, 6.82, 6.73
4b94b161 Devup full Boa .css.ts 6.55 s 6.44, 6.51, 6.55, 6.56, 6.91, 6.89

The optimized static .css.ts fixture is 30 ms faster by independent medians; the median of its six same-round Devup-minus-Tailwind deltas is about -20 ms. The ranges overlap, so the defensible conclusion is a narrow win/parity within CI-runner noise, not a universal performance victory. Dynamic .css.ts still takes the full Boa path; the prior fair run was 80 ms slower by independent medians, so this PR does not claim a dynamic-full victory.

Rejected experiments and benchmark limits

  • reusing one Boa context was faster but broke per-file global isolation; a fresh Realm preserved semantics but more than doubled evaluator time, so neither change is included
  • disabling Boa features saved only about 15 KB and could weaken compatibility; it is not included
  • -O3 increased WASM size/package-build time and regressed the CI median; compact z/-Oz settings remain
  • graph caching across Next's duplicate config evaluations was measured locally, but Next reloads the plugin module even in the same PID, so it produced no cache hit and was removed
  • CSS/sheet/class/file serialization is below 1 ms in structured profiles, so batching/debouncing it is not a measured bottleneck

The fixtures are not semantically identical: Tailwind uses untyped class strings and emits framework CSS, while Devup validates typed style props and emits much less CSS. The benchmark is useful for regression/directional evidence, not proof that either library is always faster. Type checking and inference were deliberately not weakened.

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Changepacks

@devup-ui/wasm@1.0.78 - bindings/devup-ui-wasm/package.json

Maybe you forgot to write the following files to the latest version

@devup-ui/bun-plugin@1.0.15 - packages/bun-plugin/package.json

Maybe you forgot to write the following files to the latest version

@devup-ui/components@0.1.53 - packages/components/package.json

Maybe you forgot to write the following files to the latest version

@devup-ui/eslint-plugin@1.0.18 - packages/eslint-plugin/package.json

Maybe you forgot to write the following files to the latest version

@devup-ui/next-plugin@1.0.83 - packages/next-plugin/package.json

Maybe you forgot to write the following files to the latest version

@devup-ui/plugin-utils@1.0.13 - packages/plugin-utils/package.json

Maybe you forgot to write the following files to the latest version

@devup-ui/react@1.0.40 - packages/react/package.json

Maybe you forgot to write the following files to the latest version

@devup-ui/reset-css@1.0.27 - packages/reset-css/package.json

Maybe you forgot to write the following files to the latest version

@devup-ui/rsbuild-plugin@1.0.61 - packages/rsbuild-plugin/package.json

Maybe you forgot to write the following files to the latest version

@devup-ui/vite-plugin@1.0.67 - packages/vite-plugin/package.json

Maybe you forgot to write the following files to the latest version

@devup-ui/webpack-plugin@1.0.66 - packages/webpack-plugin/package.json

Maybe you forgot to write the following files to the latest version

@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
bindings/devup-ui-wasm/src/lib.rs 100.00% <100.00%> (ø)
libs/css/src/is_special_property.rs 100.00% <ø> (ø)
.../extractor/extract_global_style_from_expression.rs 100.00% <100.00%> (ø)
libs/extractor/src/lib.rs 100.00% <100.00%> (ø)
libs/extractor/src/vanilla_extract.rs 100.00% <100.00%> (ø)
...ackages/components/src/components/Select/index.tsx 100.00% <100.00%> (ø)
...ckages/components/src/components/Stepper/index.tsx 100.00% <100.00%> (ø)
packages/components/src/contexts/useSelect.ts 100.00% <100.00%> (ø)
packages/next-plugin/src/coordinator.ts 100.00% <100.00%> (ø)
packages/next-plugin/src/css-loader.ts 100.00% <100.00%> (ø)
... and 16 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@owjs3901
owjs3901 force-pushed the owjs3901/turbo-profile branch from c417e05 to 679c2ec Compare August 28, 2026 16:05
@owjs3901

owjs3901 commented Aug 29, 2026

Copy link
Copy Markdown
Contributor Author

Dependency refresh and CI benchmark update completed.

  • 21da6d75 regenerates bun.lock after bunx @dependency-check-updates/cli -d -u --rm. All stable manifest dependencies are current; cargo update --dry-run reports 0 updates. The only bun outdated --recursive entry is the prerelease-only body-scroll-lock@4.0.0-beta.0 (stable remains 3.1.5).
  • Full local validation passed: package/WASM build (full + lite), dist entry verification, lint (0 errors), 5,159 Bun tests (0 failures, 100% JS coverage), landing Next 16.3.3 build, and targeted light/dark benchmark visual E2E. Windows Application Control did not block WASM tooling after the policy change.
  • CI source run 33254030962 was fully green. Webpack cold build: Devup per-file 10.73s / single CSS 10.81s vs Tailwind 12.81s. Turbopack six-cold-build medians: Devup direct single CSS 5.46s, static .css.ts 5.41s, Tailwind 5.52s. The direct/Tailwind ranges overlap, so this is documented as effective parity rather than a robust win.
  • 7399aefe records those exact Next 16.3.3 CI values and all six samples in the landing benchmark, docs overview, and README.
  • Final head run 33254892704 is fully green across benchmark/profile, publish build/package/lint/full tests/E2E, and landing Next E2E. Its independent medians were Devup 6.89s vs Tailwind 6.88s, reinforcing the documented parity/runner-variance caveat.

The local Bun test-cache false failure was traced to a stale absolute path from another worktree in Bun's global transpiler cache; rerunning with Bun's documented BUN_RUNTIME_TRANSPILER_CACHE_PATH=0 isolation passed. No source workaround or hook skip was committed. The add-bench PR was not touched.

@owjs3901

Copy link
Copy Markdown
Contributor Author

Follow-up warning cleanup is complete at 8bc3e981.

  • Fixed the React publish warnings, including React 19 ref-as-prop, stable non-index keys, AbortController cleanup, and removal of dangerouslySetInnerHTML for executable scripts while escaping closing script tags.
  • Fixed all other React ESLint warnings found in the same pass (23 total), with types/tests updated.
  • Removed the obsolete clippy::from_iter_instead_of_collect workspace lint and made the three affected constructions compatible with both the local and CI Clippy versions.
  • bun.lock was not changed by these follow-ups; add-bench was untouched.

Validation:

  • CI run https://github.com/dev-five-git/devup-ui/actions/runs/33258928673: publish, benchmark, landing-next-e2e, and codecov all green.
  • Publish log scan: In React 19, Do not use item index, AbortController, dangerouslySetInnerHTML, from_iter_instead_of_collect, and lint has been removed are all 0 occurrences.
  • Local commit hook: Clippy/lint passed, Rust coverage 98.22% (8514/8668), Bun 5161 passed / 0 failed / 100% JS/TS functions and lines.

Latest CI cold-build medians were Tailwind Turbo 7.43s vs Devup UI single Turbo 7.52s; the immediately previous green run was 6.72s vs 6.68s. The sign flips within ~0.1s, so the fair conclusion remains effective parity/runner variance, not a decisive performance win. The latest direct sample was 7.75s vs 7.61s in Devup UI's favor.

@owjs3901
owjs3901 merged commit 52ac78f into main Aug 29, 2026
4 checks passed
@owjs3901
owjs3901 deleted the owjs3901/turbo-profile branch August 29, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant