perf(binding): reduce plugin string clones#9436
Conversation
✅ Deploy Preview for rolldown-rs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
8272ce0 to
542be04
Compare
Merging this PR will not alter performance
Comparing Footnotes
|
eeeb572 to
7905c07
Compare
There was a problem hiding this comment.
Pull request overview
This PR reduces unnecessary Rust-side full string clones along plugin and N-API binding paths. It introduces a new BindingSharedString wrapper that can hold either an ArcStr or Arc<String> and writes the underlying buffer directly into N-API strings, avoiding an extra intermediate Rust String allocation. Plugin hook argument types are updated accordingly: HookTransformArgs.code becomes &ArcStr and HookRenderChunkArgs.code becomes Arc<String> (with an into_code() helper to recover the owned String once hooks are done).
Changes:
- New
BindingSharedStringN-API type backed byArcStrorArc<String>; plugintransform/renderChunk/callable load output now use it to avoid extra clones. HookTransformArgs.code: &ArcStrandHookRenderChunkArgs.code: Arc<String>(withinto_code()); all internal plugin call sites updated to useas_str()/as_ref().clone().BindingOutputAsset.get_sourceconstructs the JS object directly;update_output_chunkmutatesOutputChunkin place viaArc::get_mutwhen uniquely owned;BindingModuleInfoid/importer string fields switched toBindingSharedString.
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| crates/rolldown_binding/src/options/plugin/types/binding_shared_string.rs | New shared-string N-API wrapper writing UTF-8 directly into napi strings. |
| crates/rolldown_binding/src/options/plugin/types/mod.rs | Register new binding_shared_string module. |
| crates/rolldown_binding/src/options/plugin/js_plugin.rs | Pass BindingSharedString to JS transform/renderChunk callbacks. |
| crates/rolldown_binding/src/options/plugin/binding_plugin_options.rs | Update JS plugin callback signatures to take BindingSharedString. |
| crates/rolldown_binding/src/options/plugin/binding_callable_builtin_plugin.rs | Builtin transform builds ArcStr; load output uses BindingSharedString. |
| crates/rolldown_binding/src/types/binding_module_info.rs | Switch id/importer/imported-id fields to BindingSharedString. |
| crates/rolldown_binding/src/types/binding_output_asset.rs | get_source builds JS object directly without cloning. |
| crates/rolldown_binding/src/types/binding_output_chunk.rs | Mutate Arc<OutputChunk> in place when uniquely owned. |
| crates/rolldown_plugin/src/types/hook_transform_args.rs | code: &'a ArcStr. |
| crates/rolldown_plugin/src/types/hook_render_chunk_args.rs | code: Arc<String> plus into_code() helper. |
| crates/rolldown_plugin/src/plugin_driver/build_hooks.rs | Materialize one ArcStr per transform chain, share across plugins. |
| crates/rolldown_plugin/src/plugin_driver/output_hooks.rs | Wrap render-chunk code in Arc; guard trace clones with trace_action_enabled!(). |
| crates/rolldown/src/utils/render_chunks.rs | Wrap initial render-chunk code in Arc. |
| crates/rolldown_plugin_vite_transform/{src/lib.rs,Cargo.toml} | Pass &ArcStr straight to diagnostics; drop direct arcstr dep. |
| crates/rolldown_plugin_vite_css_post/src/utils.rs | Use args.code.as_str() for MagicString::new. |
| crates/rolldown_plugin_vite_build_import_analysis/src/lib.rs | args.code.as_ref().clone() instead of clone(). |
| crates/rolldown_plugin_vite_asset/src/lib.rs | Use args.code.as_str(). |
| crates/rolldown_plugin_replace/{src/plugin.rs,tests/...} | Use as_str()/to_string() on ArcStr arg. |
| crates/rolldown_plugin_copy_module/src/lib.rs | Use args.code.as_str(). |
| crates/rolldown_plugin_chunk_import_map/src/lib.rs | args.code.as_ref().clone(). |
| crates/rolldown_plugin_asset_module/src/lib.rs | Use args.code.as_str(). |
| crates/rolldown/tests/.../transform_without_runtime_usage/mod.rs | args.code.to_string() to obtain owned String. |
| Cargo.lock | Drop arcstr dep from rolldown_plugin_vite_transform. |
Merge activity
|
## Summary Reduce Rust-side full string clones across plugin and N-API binding paths. This extends `BindingSharedString` so it can carry either `ArcStr` or `Arc<String>` into N-API string creation without first materializing an extra Rust `String`. JavaScript still receives normal strings; the change only reduces native allocation/copy pressure before crossing into JS. The optimization now covers: - transform and renderChunk hook code payloads - callable builtin `load()` output code - module info string fields and id arrays - output asset source access - output chunk updates, including in-place mutation when uniquely owned ## Impact - Reduces redundant native string clones in hot plugin/binding paths. - Keeps the JS plugin API behavior unchanged. - Avoids external V8 strings or zero-copy JS string lifetime risks. ## Benchmarks - apps/10000 clean public comparison: `1.334s ± 0.079s` public `[email protected]` vs `1.249s ± 0.065s` local, about `1.07x` faster. - LobeHub Electron renderer warmed comparison: `9.100s ± 0.289s` public `[email protected]` vs `8.952s ± 0.042s` local, about `1.02x` faster. ## Validation - `cargo fmt --check` - `git diff --check` - `cargo check -p rolldown_binding` - focused `cargo clippy` over affected Rolldown crates with `--deny warnings` - `just build-rolldown-release` - `vp run --filter rolldown build-types-check` - focused fixture tests for sourcemap, renderChunk, emit-file, hybrid-plugin, generate-bundle, and free-external-memory coverage - `cli/cli-e2e.test.ts` Note: full `rolldown-tests test:types` is still blocked by an existing local missing dependency issue resolving `@rolldown/test-dev-server` in `dev/dev-watch.test.ts`; no TypeScript errors were reported before that resolution failure. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes the core plugin hook argument types (`transform` now uses `ArcStr`, `renderChunk` uses `Arc<String>`) and N-API binding parameter/return types, which could introduce subtle lifetime/ownership bugs or integration regressions despite no intended JS API change. > > **Overview** > Reduces native-side string cloning in hot plugin paths by switching hook payloads to shared string types: `HookTransformArgs.code` becomes `&ArcStr`, and `HookRenderChunkArgs.code` becomes `Arc<String>` with `into_code()` to recover ownership after hooks. > > Updates the N-API binding layer to pass/return these shared strings via a new `BindingSharedString` wrapper (backed by `ArcStr` or `Arc<String>`), and adjusts JS plugin hook signatures (`transform`, `renderChunk`), callable builtin plugin `transform`, and callable builtin `load()` outputs accordingly. > > Also optimizes output updates by mutating `OutputChunk` in-place when uniquely owned (`Arc::get_mut`) and tweaks `BindingOutputAsset.get_source()` to build the JS object directly for string/byte sources. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 7905c07. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
805a637 to
0f89b28
Compare
- Hoist `ArcStr::from(code.as_str())` out of the per-plugin transform loop in `build_hooks.rs`; re-allocate only when a plugin returns new code. Previously every transform plugin paid one full ArcStr allocation per module. - Replace the `Arc::get_mut` / explicit struct-rebuild split in `update_output_chunk` with `Arc::make_mut`. - Use `Arc::unwrap_or_clone` in `HookRenderChunkArgs::into_code`. - Drop the `create_napi_string` unsafe helper in `BindingSharedString`; napi-rs already provides `impl ToNapiValue for &str` doing the same `napi_create_string_utf8` call. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…cStr (#9456) ## Summary Follow-up cleanups to #9436: - **Lazy-init `ArcStr::from(code.as_str())` in the transform hook** (`build_hooks.rs`). Before this PR, `transform` allocated one `ArcStr` per plugin per module. The first pass of this PR hoisted that alloc out of the loop — but still paid one allocation per module even when no transform plugins were registered (regression vs. main). Now `code_arc` is an `Option<ArcStr>` initialized on first iteration and reset to `None` when a plugin replaces `code`, so the no-transform path stays a no-op and multi-plugin modules pay one alloc per code revision. - **Use `Arc::unwrap_or_clone`** in `HookRenderChunkArgs::into_code`. - **Drop the `create_napi_string` unsafe helper** in `BindingSharedString` — napi-rs already provides `impl ToNapiValue for &str` doing the same `napi_create_string_utf8` call. Codex review surfaced two issues with an earlier `Arc::make_mut` change in `update_output_chunk` (non-atomic on sourcemap conversion failure; full struct clone including the large `code: String` when the Arc is shared with the JS-side `BindingOutputChunk`). That change has been dropped from this PR. ## Test plan - [x] `cargo check -p rolldown_binding -p rolldown_plugin` passes - [x] `cargo clippy -p rolldown_binding -p rolldown_plugin` passes (no new lints) - [ ] CI green 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## [1.0.2] - 2026-05-20 ### 🚀 Features - devtools: emit package size in PackageGraphReady (#9434) by @IWANABETHATGUY - devtools: classify package dependency types (#9427) by @IWANABETHATGUY - devtools: map packages to modules and chunks (#9426) by @IWANABETHATGUY - devtools: mark used packages (#9423) by @IWANABETHATGUY - devtools: make duplicate packages discoverable (#9422) by @IWANABETHATGUY - devtools: emit package metadata (#9421) by @IWANABETHATGUY - update oxc to 0.132.0 (#9449) by @shulaoda - update oxc to 0.131.0 (#9424) by @shulaoda - allow checks.* to escalate emissions to hard errors (#9388) by @IWANABETHATGUY - dev: support watcher options `include` and `exclude` (#9395) by @h-a-n-a - emit warnings for invalid pure annotations (#9381) by @Kyujenius ### 🐛 Bug Fixes - hash: keep chunk file names stable when an unrelated entry is added (#9444) by @hyf0 - call `codeSplitting.groups[].name` in deterministic order (#9457) by @sapphi-red - dev/lazy: make `resolve_id` idempotent when the resolved id is already a lazy entry (#9439) by @h-a-n-a - chunk-optimization: publish absorbed dynamic-entry namespace cross-chunk (#9448) by @IWANABETHATGUY - treeshake: propagate pure annotation through compound exprs (#9431) by @Dunqing - finalizer: skip redundant init call when barrel executes in same chunk (#9354) by @IWANABETHATGUY - linking: initialize wrapped ESM re-export owners (#9353) by @IWANABETHATGUY - do not inherit __toESM across chunks for named-only external imports (#9333) (#9415) by @IWANABETHATGUY - watcher: don't write output or emit events after close() (#9328) by @situ2001 - chunk-optimization: avoid unsafe dynamic-only merges (#9398) by @IWANABETHATGUY - cjs: rename CJS-wrapped locals that would shadow chunk-scope names (#9392) by @hyf0 - dev/lazy: watch lazy modules added in rebuilds (#9391) by @h-a-n-a ### 🚜 Refactor - rolldown_dev: move dev example to break publish cycle (#9465) by @Boshen - binding: drop unsafe napi string helper, hoist transform ArcStr (#9456) by @hyf0 - ecmascript_utils: split rewrite_ident_reference off JsxExt trait (#9417) by @IWANABETHATGUY - use `ThreadsafeFunction::call_async_catch` (#9390) by @sapphi-red ### 📚 Documentation - devtools: document @rolldown/debug usage and package graph consumption (#9435) by @IWANABETHATGUY - replace `Inter` with system font stack in OG template SVG (#9240) by @yvbopeng - remove `output.comments` warning as all issues have been resolved (#9393) by @sapphi-red - in-depth: clarify @__PURE__ scope and document positions (#9389) by @Kyujenius - readme: remove release candidate notice (#9387) by @shulaoda ### ⚡ Performance - vite-resolve: cache importer existence checks (#9443) by @Brooooooklyn - binding: reduce plugin string clones (#9436) by @Brooooooklyn ### 🧪 Testing - enable `legal_comments_inline` test (#9394) by @sapphi-red ### ⚙️ Miscellaneous Tasks - bump pnpm to v11.1.2 (#9447) by @Boshen - deps: update rust crates (#9461) by @renovate[bot] - deps: update rollup submodule for tests to v4.60.4 (#9453) by @rolldown-guard[bot] - deps: update test262 submodule for tests (#9454) by @rolldown-guard[bot] - deps: update npm packages (#9430) by @renovate[bot] - deps: update github actions (#9429) by @renovate[bot] - deps: update dependency rolldown-plugin-dts to v0.25.1 (#9452) by @renovate[bot] - deps: update rust crates (#9428) by @renovate[bot] - revert allow checks.* to escalate emissions to hard errors (#9388) (#9438) by @IWANABETHATGUY - update mimalloc-safe to 0.1.61 (#9413) by @shulaoda - deny `todo`, `unimplemented`, and `print_stderr` clippy lints (#9412) by @Boshen - deps: update mimalloc-safe to 0.1.60 (#9410) by @shulaoda - remove `pip install setuptools` workaround for node-gyp on macOS (#9370) by @sapphi-red - renovate: disable automerge to require manual approval (#9386) by @shulaoda - deps: update napi (#9385) by @renovate[bot] ### ❤️ New Contributors * @yvbopeng made their first contribution in [#9240](#9240) Co-authored-by: shulaoda <[email protected]>
## Summary Reduce Rust-side full string clones across plugin and N-API binding paths. This extends `BindingSharedString` so it can carry either `ArcStr` or `Arc<String>` into N-API string creation without first materializing an extra Rust `String`. JavaScript still receives normal strings; the change only reduces native allocation/copy pressure before crossing into JS. The optimization now covers: - transform and renderChunk hook code payloads - callable builtin `load()` output code - module info string fields and id arrays - output asset source access - output chunk updates, including in-place mutation when uniquely owned ## Impact - Reduces redundant native string clones in hot plugin/binding paths. - Keeps the JS plugin API behavior unchanged. - Avoids external V8 strings or zero-copy JS string lifetime risks. ## Benchmarks - apps/10000 clean public comparison: `1.334s ± 0.079s` public `[email protected]` vs `1.249s ± 0.065s` local, about `1.07x` faster. - LobeHub Electron renderer warmed comparison: `9.100s ± 0.289s` public `[email protected]` vs `8.952s ± 0.042s` local, about `1.02x` faster. ## Validation - `cargo fmt --check` - `git diff --check` - `cargo check -p rolldown_binding` - focused `cargo clippy` over affected Rolldown crates with `--deny warnings` - `just build-rolldown-release` - `vp run --filter rolldown build-types-check` - focused fixture tests for sourcemap, renderChunk, emit-file, hybrid-plugin, generate-bundle, and free-external-memory coverage - `cli/cli-e2e.test.ts` Note: full `rolldown-tests test:types` is still blocked by an existing local missing dependency issue resolving `@rolldown/test-dev-server` in `dev/dev-watch.test.ts`; no TypeScript errors were reported before that resolution failure. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes the core plugin hook argument types (`transform` now uses `ArcStr`, `renderChunk` uses `Arc<String>`) and N-API binding parameter/return types, which could introduce subtle lifetime/ownership bugs or integration regressions despite no intended JS API change. > > **Overview** > Reduces native-side string cloning in hot plugin paths by switching hook payloads to shared string types: `HookTransformArgs.code` becomes `&ArcStr`, and `HookRenderChunkArgs.code` becomes `Arc<String>` with `into_code()` to recover ownership after hooks. > > Updates the N-API binding layer to pass/return these shared strings via a new `BindingSharedString` wrapper (backed by `ArcStr` or `Arc<String>`), and adjusts JS plugin hook signatures (`transform`, `renderChunk`), callable builtin plugin `transform`, and callable builtin `load()` outputs accordingly. > > Also optimizes output updates by mutating `OutputChunk` in-place when uniquely owned (`Arc::get_mut`) and tweaks `BindingOutputAsset.get_source()` to build the JS object directly for string/byte sources. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 7905c07. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
…cStr (rolldown#9456) ## Summary Follow-up cleanups to rolldown#9436: - **Lazy-init `ArcStr::from(code.as_str())` in the transform hook** (`build_hooks.rs`). Before this PR, `transform` allocated one `ArcStr` per plugin per module. The first pass of this PR hoisted that alloc out of the loop — but still paid one allocation per module even when no transform plugins were registered (regression vs. main). Now `code_arc` is an `Option<ArcStr>` initialized on first iteration and reset to `None` when a plugin replaces `code`, so the no-transform path stays a no-op and multi-plugin modules pay one alloc per code revision. - **Use `Arc::unwrap_or_clone`** in `HookRenderChunkArgs::into_code`. - **Drop the `create_napi_string` unsafe helper** in `BindingSharedString` — napi-rs already provides `impl ToNapiValue for &str` doing the same `napi_create_string_utf8` call. Codex review surfaced two issues with an earlier `Arc::make_mut` change in `update_output_chunk` (non-atomic on sourcemap conversion failure; full struct clone including the large `code: String` when the Arc is shared with the JS-side `BindingOutputChunk`). That change has been dropped from this PR. ## Test plan - [x] `cargo check -p rolldown_binding -p rolldown_plugin` passes - [x] `cargo clippy -p rolldown_binding -p rolldown_plugin` passes (no new lints) - [ ] CI green 🤖 Generated with [Claude Code](https://claude.com/claude-code)
## [1.0.2] - 2026-05-20 ### 🚀 Features - devtools: emit package size in PackageGraphReady (rolldown#9434) by @IWANABETHATGUY - devtools: classify package dependency types (rolldown#9427) by @IWANABETHATGUY - devtools: map packages to modules and chunks (rolldown#9426) by @IWANABETHATGUY - devtools: mark used packages (rolldown#9423) by @IWANABETHATGUY - devtools: make duplicate packages discoverable (rolldown#9422) by @IWANABETHATGUY - devtools: emit package metadata (rolldown#9421) by @IWANABETHATGUY - update oxc to 0.132.0 (rolldown#9449) by @shulaoda - update oxc to 0.131.0 (rolldown#9424) by @shulaoda - allow checks.* to escalate emissions to hard errors (rolldown#9388) by @IWANABETHATGUY - dev: support watcher options `include` and `exclude` (rolldown#9395) by @h-a-n-a - emit warnings for invalid pure annotations (rolldown#9381) by @Kyujenius ### 🐛 Bug Fixes - hash: keep chunk file names stable when an unrelated entry is added (rolldown#9444) by @hyf0 - call `codeSplitting.groups[].name` in deterministic order (rolldown#9457) by @sapphi-red - dev/lazy: make `resolve_id` idempotent when the resolved id is already a lazy entry (rolldown#9439) by @h-a-n-a - chunk-optimization: publish absorbed dynamic-entry namespace cross-chunk (rolldown#9448) by @IWANABETHATGUY - treeshake: propagate pure annotation through compound exprs (rolldown#9431) by @Dunqing - finalizer: skip redundant init call when barrel executes in same chunk (rolldown#9354) by @IWANABETHATGUY - linking: initialize wrapped ESM re-export owners (rolldown#9353) by @IWANABETHATGUY - do not inherit __toESM across chunks for named-only external imports (rolldown#9333) (rolldown#9415) by @IWANABETHATGUY - watcher: don't write output or emit events after close() (rolldown#9328) by @situ2001 - chunk-optimization: avoid unsafe dynamic-only merges (rolldown#9398) by @IWANABETHATGUY - cjs: rename CJS-wrapped locals that would shadow chunk-scope names (rolldown#9392) by @hyf0 - dev/lazy: watch lazy modules added in rebuilds (rolldown#9391) by @h-a-n-a ### 🚜 Refactor - rolldown_dev: move dev example to break publish cycle (rolldown#9465) by @Boshen - binding: drop unsafe napi string helper, hoist transform ArcStr (rolldown#9456) by @hyf0 - ecmascript_utils: split rewrite_ident_reference off JsxExt trait (rolldown#9417) by @IWANABETHATGUY - use `ThreadsafeFunction::call_async_catch` (rolldown#9390) by @sapphi-red ### 📚 Documentation - devtools: document @rolldown/debug usage and package graph consumption (rolldown#9435) by @IWANABETHATGUY - replace `Inter` with system font stack in OG template SVG (rolldown#9240) by @yvbopeng - remove `output.comments` warning as all issues have been resolved (rolldown#9393) by @sapphi-red - in-depth: clarify @__PURE__ scope and document positions (rolldown#9389) by @Kyujenius - readme: remove release candidate notice (rolldown#9387) by @shulaoda ### ⚡ Performance - vite-resolve: cache importer existence checks (rolldown#9443) by @Brooooooklyn - binding: reduce plugin string clones (rolldown#9436) by @Brooooooklyn ### 🧪 Testing - enable `legal_comments_inline` test (rolldown#9394) by @sapphi-red ### ⚙️ Miscellaneous Tasks - bump pnpm to v11.1.2 (rolldown#9447) by @Boshen - deps: update rust crates (rolldown#9461) by @renovate[bot] - deps: update rollup submodule for tests to v4.60.4 (rolldown#9453) by @rolldown-guard[bot] - deps: update test262 submodule for tests (rolldown#9454) by @rolldown-guard[bot] - deps: update npm packages (rolldown#9430) by @renovate[bot] - deps: update github actions (rolldown#9429) by @renovate[bot] - deps: update dependency rolldown-plugin-dts to v0.25.1 (rolldown#9452) by @renovate[bot] - deps: update rust crates (rolldown#9428) by @renovate[bot] - revert allow checks.* to escalate emissions to hard errors (rolldown#9388) (rolldown#9438) by @IWANABETHATGUY - update mimalloc-safe to 0.1.61 (rolldown#9413) by @shulaoda - deny `todo`, `unimplemented`, and `print_stderr` clippy lints (rolldown#9412) by @Boshen - deps: update mimalloc-safe to 0.1.60 (rolldown#9410) by @shulaoda - remove `pip install setuptools` workaround for node-gyp on macOS (rolldown#9370) by @sapphi-red - renovate: disable automerge to require manual approval (rolldown#9386) by @shulaoda - deps: update napi (rolldown#9385) by @renovate[bot] ### ❤️ New Contributors * @yvbopeng made their first contribution in [rolldown#9240](rolldown#9240) Co-authored-by: shulaoda <[email protected]>
Summary
Reduce Rust-side full string clones across plugin and N-API binding paths.
This extends
BindingSharedStringso it can carry eitherArcStrorArc<String>into N-API string creation without first materializing an extra RustString. JavaScript still receives normal strings; the change only reduces native allocation/copy pressure before crossing into JS.The optimization now covers:
load()output codeImpact
Benchmarks
1.334s ± 0.079spublic[email protected]vs1.249s ± 0.065slocal, about1.07xfaster.9.100s ± 0.289spublic[email protected]vs8.952s ± 0.042slocal, about1.02xfaster.Validation
cargo fmt --checkgit diff --checkcargo check -p rolldown_bindingcargo clippyover affected Rolldown crates with--deny warningsjust build-rolldown-releasevp run --filter rolldown build-types-checkcli/cli-e2e.test.tsNote: full
rolldown-tests test:typesis still blocked by an existing local missing dependency issue resolving@rolldown/test-dev-serverindev/dev-watch.test.ts; no TypeScript errors were reported before that resolution failure.Note
Medium Risk
Changes the core plugin hook argument types (
transformnow usesArcStr,renderChunkusesArc<String>) and N-API binding parameter/return types, which could introduce subtle lifetime/ownership bugs or integration regressions despite no intended JS API change.Overview
Reduces native-side string cloning in hot plugin paths by switching hook payloads to shared string types:
HookTransformArgs.codebecomes&ArcStr, andHookRenderChunkArgs.codebecomesArc<String>withinto_code()to recover ownership after hooks.Updates the N-API binding layer to pass/return these shared strings via a new
BindingSharedStringwrapper (backed byArcStrorArc<String>), and adjusts JS plugin hook signatures (transform,renderChunk), callable builtin plugintransform, and callable builtinload()outputs accordingly.Also optimizes output updates by mutating
OutputChunkin-place when uniquely owned (Arc::get_mut) and tweaksBindingOutputAsset.get_source()to build the JS object directly for string/byte sources.Reviewed by Cursor Bugbot for commit 7905c07. Bugbot is set up for automated code reviews on this repo. Configure here.