Skip to content

Remove dead code from the auto-install module queue and its install hooks - #46612

Open
robobun wants to merge 5 commits into
mainfrom
robobun/860b2055/dead-code-sweep
Open

robobun wants to merge 5 commits into
mainfrom
robobun/860b2055/dead-code-sweep

Conversation

@robobun

@robobun robobun commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Fix

  • Remove the field and what only it reaches: AsyncModule, InitOpts, most of Queue, one task tag, two error variants, Source.contents_is_recycled.
  • Remove what that orphans in bun_install: two flags and three hooks of RunTasksCallbacks, the any_root arguments, PackageManifestError.
  • Verified: bun bd, bun run rust:check-all, bun run rust:mordant, clippy, 12 test files. One known debug-build timeout (Notes).
  • Self-reviewed: 17 concerns raised, 13 addressed, 4 in part. Rejected: the two removals below, and a wait for a maintainer's yes.

Background

Downsides

Behaviour change: none

Notes

Why the queue cannot run

Removed, by file

  • src/jsc/AsyncModule.rs (1,159 to 110 lines): InitOpts, AsyncModule and its Taskable impl, PackageDownloadError, PackageResolveError, Map, Queue::{map, scheduled, vm, on_resolve, enqueue, run_tasks, on_package_manifest_error, on_package_download_error, poll_modules}, QueueRunTasksCallbacks. Kept: Queue (now a unit struct), WakeContext, Queue::on_wake_handler, Queue::on_poll, fulfill (was AsyncModule::fulfill, same body).
  • src/bundler/transpiler.rs: ParseResult::{pending_imports, empty, is_pending_import}. src/bundler/linker.rs: is_deferred and skip_deferred, which were always false.
  • src/runtime/jsc_hooks.rs: the enqueue block, the Error::AsyncModule arm and the promise local in Bun__transpileFile, the promise_ptr initialisers. Also intern_transpile_path and its thread-local set: since Fix use-after-free of BuildMessage.position.file: real lifetimes for Source, Path, ImportRecord and resolve results #46834 the enqueue block was their only user, and a private function with no caller does not compile.
  • src/ast/lib.rs and five places that wrote or copied it: Source.contents_is_recycled. The enqueue block was its only reader.
  • src/event_loop/ConcurrentTask.rs, src/runtime/dispatch.rs: the AsyncModule task tag and its two arms. task_tag::COUNT goes from 86 to 85. Tags are not stored anywhere.
  • src/runtime/error.rs, src/jsc/error.rs, src/jsc/VirtualMachine.rs: Error::AsyncModule, UnexpectedPendingResolution (both enums), the "Unexpected pending import" message in process_fetch_log.
  • src/jsc/ModuleLoader.rs: TranspileExtra.promise_ptr. src/jsc/JSValue.rs: as_internal_promise. src/bun_alloc/ast_alloc.rs: ScopedAstAlloc::take_state. src/resolver/lib.rs: cache::Fs::reset_shared_buffer. Each had its last caller in the removed code.
  • src/install/PackageManager/runTasks.rs (2,344 to 2,157 lines): HAS_ON_RESOLVE, HAS_ON_PACKAGE_MANIFEST_ERROR, on_resolve, on_package_manifest_error, on_package_download_error_pkg, their ErasedCallbacks fields, and the branches they guarded (the other branch stays). QueueRunTasksCallbacks was the only impl that set them. resolve_waiters_and_notify only added the on_resolve call, so its two callers now call resolve_waiters_of_appended_package. process_dependency_list_for_ctx loses its two callback arguments and is now process_dependency_list.
  • src/install/PackageManager/processDependencyList.rs: the any_root argument of process_dependency_list_item, which only fed on_resolve.
  • src/install/lib.rs, src/install/error.rs: PackageManifestError and Error::PackageManifest, built only for the manifest hook.
  • src/install/PackageManagerTask.rs, PackageManagerEnqueue.rs: GitCommitRequest.url, read only to pass to the manifest hook, and PackageManager::enqueue_dependency_list, called only by Queue::poll_modules. The free function of that name stays.
  • Comments that named the removed path are shortened or deleted. No comment of two lines or more is added.

Verification (head 5589e8f, which includes main up to 8a0e49e)

  • bun bd (debug, ASAN) builds. bun run rust:check-all: 12 of 12 targets pass. cargo check --workspace --all-targets and cargo clippy --workspace --no-deps pass. cargo fmt --check reports nothing. bun test test/internal/source-lints/: 183 pass. bun run rust:mordant (Linux, Windows, macOS): exit 0.
  • Tests on the debug build of this head: bun-add.test.ts 220, bun-install-retry.test.ts 208, bundler_cjs2esm.test.ts 113, plugins.test.ts 111, node-module-module.test.js 80, bundler_plugin.test.ts 68, bundler_loader.test.ts 62, resolve-error.test.ts 32, autoinstall-cached-manifest.test.ts 4, resolve-autoinstall-invalid-name.test.ts 4, resolve-autoinstall-log-dangling.test.ts 2: all pass. run-autoinstall.test.ts: 299 pass, 1 fail.
  • That one failure is "the dependencies of packages that load from node_modules take no memory", which hits the 5 s default timeout in a debug build. It does the same on a debug build of main: on the head before the merge (3497eba on main 5c6d4e2) it timed out in five of five runs of the file on main and in five of five on the branch.
  • More numbers from that earlier head, main against the branch on one machine, as pass/fail per run. Every failure was a 5 s timeout. run-autoinstall.test.ts: main 297/3, 293/7, 294/6, 292/8, 288/12, branch 295/5, 292/8, 296/4, 297/3, 297/3. isolated-install.test.ts: main 273/10, branch 275/8. bun-install-git-deps.test.ts: main 192/9, branch 197/4. bun-install.test.ts: 772 pass on both. bun-install-streaming-extract.test.ts: 132 pass on both.
  • bun run rust:mordant sees the orphans of a deletion: with PackageManager::enqueue_dependency_list put back it reports unused_pub and exits 101.

Open pull requests (heads of all 5,857, fetched 2026-10-27)

One follow-up, not in this change

  • The resolver's Pending chain. EnqueueResult::Pending (src/install_types/resolver_hooks.rs:1380) has no constructor. It feeds DependencyToResolve::Pending, MatchStatus::Pending and ResultUnion::Pending (src/resolver/resolver.rs, src/resolver/result.rs), and every consumer maps them to not-found (src/jsc/VirtualMachine.rs:5274, src/runtime/jsc_hooks.rs:815). After this change nothing reads the fields of PendingResolution. Open Remove dead code from bun_bundler, bun_install, bun_runtime, bun_parsers, and the JSC private host functions #41088 removes the root, so the chain should leave with it.
  • The wake task. Queue::on_wake_handler posts PollPendingModulesTask for each package manager wake, and Queue::on_poll now only logs. Its removal takes Queue, WakeContext, VirtualMachine.modules, the task tag and WakeHandler with it. It also changes what a wake posts to the JS thread, so it is not a deletion with no effect.
  • After this change bun_jsc names bun_install at four sites (the package_manager accessor, two log swaps, CrateError::Install), so the crate edge can go in a later change.

Left alone on purpose

  • FilenameStore::exists and DirnameStore::exists have no caller after this change. They stay, because intern_transpile_path calls exists and three open pull requests need that function back.
  • Items that lost their last user outside their crate and could be pub(crate): RunTasksCallbacks, run_tasks, drain_dependency_list, determine_preinstall_state, start_progress_bar_if_none, end_progress_bar, PackageManager.pending_tasks, PackageManager.total_tasks, cache::Fs::shared_buffer.
  • Found dead in this run and not removed: 14 hand-written Debug impls (as Remove dead code: 49 trait impls nothing uses, across 12 crates #43664 decided), six unused impls and methods in bun_lint and bun_format, and 13 #[bun_jsc::host_fn] attributes whose shim has no caller. IDLByte, IDLShort and IDLLongLong have no user on main, but bun:dom and the Vitest work in one tree (not for merging) #45415 uses them.
  • Needs a decision: completions/bun-cli.json with misctools/generate-cli-completions.ts (5,883 lines, nothing reads the JSON), packages/h3blast, packages/bun-plugin-yaml, the logs build option.

Review

`ParseResult.pending_imports` has no writer, so the block in
`transpile_source_code` that fills the module queue never runs and the
queue stays empty. Remove the field and what only it reaches:

- `AsyncModule`, `InitOpts`, `Queue::{map, scheduled, enqueue,
  run_tasks, poll_modules}` and the error builders in
  `src/jsc/AsyncModule.rs`. The wake handler, `on_poll` and `fulfill`
  stay. `fulfill` is now a free function.
- The `AsyncModule` task tag and its two dispatch arms.
- `Error::AsyncModule`, `UnexpectedPendingResolution` and its message.
- `TranspileExtra.promise_ptr`, `ParseResult::empty`,
  `ParseResult::is_pending_import`, `ScopedAstAlloc::take_state`,
  `cache::Fs::reset_shared_buffer`, `JSValue::as_internal_promise`.
`QueueRunTasksCallbacks` was the one `RunTasksCallbacks` impl that set
`HAS_ON_RESOLVE` and `HAS_ON_PACKAGE_MANIFEST_ERROR`, and the one that
overrode `on_package_download_error_pkg`. With it gone those branches
of `run_tasks` cannot run. Remove them and what only they reach:

- `HAS_ON_RESOLVE`, `HAS_ON_PACKAGE_MANIFEST_ERROR`, `on_resolve`,
  `on_package_manifest_error`, `on_package_download_error_pkg` and
  their `ErasedCallbacks` fields.
- The `any_root` argument of `process_dependency_list_item` and
  `resolve_waiters_of_appended_package`, and
  `resolve_waiters_and_notify`, which only added the `on_resolve` call.
  `process_dependency_list_for_ctx` loses its two callback arguments
  and is now `process_dependency_list`.
- `PackageManifestError` and `Error::PackageManifest`, built only for
  the manifest hook.
- `GitCommitRequest.url`, read only to pass to the manifest hook.
- `PackageManager::enqueue_dependency_list`, called only by
  `Queue::poll_modules`. The free function stays.
@robobun

robobun commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 5:44 AM PT - Oct 27th, 2026

❌ @robobun, your commit 5589e8f has 1 failures in Build #133595 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 46612

That installs a local version of the PR into your bun-46612 executable, so you can run:

bun-46612 --bun

@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 38a97fd and 38735f2.

Files selected for processing: 10
  • src/bundler/linker.rs
  • src/bundler/transpiler.rs
  • src/install/PackageManager/runTasks.rs
  • src/jsc/AsyncModule.rs
  • src/jsc/JSValue.rs
  • src/jsc/ModuleLoader.rs
  • src/jsc/VirtualMachine.rs
  • src/resolver/lib.rs
  • src/runtime/dispatch.rs
  • src/runtime/jsc_hooks.rs
Files with no reviewable changes: 3
  • src/jsc/ModuleLoader.rs
  • src/resolver/lib.rs
  • src/jsc/JSValue.rs

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 1 remain after this review.

Walkthrough

The changes remove the AsyncModule task lifecycle and pending-import promise handling. They update linker import processing, transpile interfaces, and package-manager callback paths. Async-module fulfillment remains available as a crate-visible function used by runtime transpiler completion.

Changes

Async module resolution removal

Layer / File(s) Summary
Pending-import and transpile path
src/bundler/*, src/jsc/ModuleLoader.rs, src/jsc/VirtualMachine.rs, src/runtime/jsc_hooks.rs, src/resolver/lib.rs, src/bun_alloc/ast_alloc.rs
ParseResult and TranspileExtra no longer carry pending-import or promise-pointer state. Runtime hooks remove pending-import handling. The linker skips imports marked unused and processes the remaining records.
AsyncModule task lifecycle and dispatch
src/jsc/AsyncModule.rs, src/jsc/RuntimeTranspilerStore.rs, src/jsc/JSValue.rs, src/event_loop/ConcurrentTask.rs, src/runtime/dispatch.rs, src/jsc/error.rs, src/runtime/error.rs
The queue’s module processing and AsyncModule task dispatch are removed. Fulfillment remains as a crate-visible function called by runtime transpiler completion. Related task tags and error variants are removed.
Package-manager callbacks and dependency processing
src/install/PackageManager/*, src/install/PackageManagerTask.rs, src/install/error.rs, src/install/lib.rs
Dependency processing drops root-resolution tracking and removed callback paths. Manifest failures are logged and passed to waiting dependencies. Related manifest error types, the dependency-list forwarding method, and the Git commit request URL field are removed.

Suggested reviewers: alii

Priority: Low

Merge Risk: Minimal · up to 38735

This change removes unreachable async-module queue code and the install callbacks that only it used, with no intended behavior change. No concrete regression was found, so it looks safe to merge after the usual checks.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: removing dead code from the auto-install module queue and its install hooks.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description is complete and relevant. It explains the problem, the fix, scope boundaries, behavior impact, merge considerations, and detailed verification results. It does not use the exact templa…
Full details: Description check

Explanation

The description is complete and relevant. It explains the problem, the fix, scope boundaries, behavior impact, merge considerations, and detailed verification results. It does not use the exact template headings "What does this PR do?" and "How did you verify your code works?", but it provides the required information under equivalent sections.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: ready for review. The diff is green.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no issues

No high-confidence issues detected in this change.

@robobun

robobun commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

Merged main (8a0e49e) into this branch. Main changed code that this PR deletes, so the merge removes a little more than the PR did.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Beyond the inline nit, I also checked the two refactors in this sweep that could have changed behavior: the collapse of ErasedCallbacks::on_package_download_error in src/install/PackageManager/runTasks.rs drops the !is_store_installer branch, and the only impl with HAS_ON_PACKAGE_DOWNLOAD_ERROR = true (src/install/isolated_install.rs) is also IS_STORE_INSTALLER = true, so that branch was unreachable; and allow_promise in Bun__transpileFile is still read by the RuntimeTranspilerStore async path, so dropping promise_ptr does not orphan it. A repo-wide grep for pending_imports after the change finds nothing, consistent with the no-writer claim.

Extended reasoning...

Dead-code sweep (+141/-1688) removing the AsyncModule queue, its task tag, two error variants, and the install-side hooks only it set; no security-sensitive surface. The branch collapse in the download-error dispatcher and the removal of the promise out-param are the two spots where behavior could have shifted, and both check out as behavior-preserving; the size of the deletion and its reach into module loading and the dispatch table are why a human look is still worthwhile.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🟡 src/bundler/transpiler.rs — nit: maintainers keep a Source field that nothing reads any more. The deleted enqueue block in transpile_source_code_of_any_provenance was the last reader of contents_is_recycled; after this sweep every remaining mention only writes or copies it (src/ast/lib.rs:2091, src/ast/lib.rs:2386, src/bundler/ParseTask.rs:2446, src/bundler/bundle_v2.rs:4222, src/bundler/linker_context/generateCodeForFileInChunkJS.rs:204, src/resolver/package_json.rs:1050) and the comment at src/bundler/transpiler.rs:1363 still says it "records that the bytes are externally-owned". Fix: delete the field and its writers in this PR, or state in the description why it stays, so the sweep leaves no new orphan.

    Why this was flagged

    The removed block in src/runtime/jsc_hooks.rs read parse_result.source.contents_is_recycled to decide whether to detach the resolver's shared buffer before handing the parse to the AsyncModule queue; that was its only reader. A repo-wide grep after the change finds only the declaration (src/ast/lib.rs:2091), the Default and init_recycled_file initialisers (src/ast/lib.rs:2106, src/ast/lib.rs:2386), a literal false write in src/bundler/ParseTask.rs:2446, and three field-to-field copies (src/bundler/bundle_v2.rs:4222, src/bundler/linker_context/generateCodeForFileInChunkJS.rs:204, src/resolver/package_json.rs:1050). No code branches on the value. The PR description lists the orphans it removed and says unused_pub found no new orphan, but that lint covers items, not struct fields, so this one is left behind. This is a dead-code nit with no runtime effect; the base branch had the same field but with a live reader.

    Verification: nit. Triggering condition: none needed — it is a static property of the merged tree. The PR diff for src/runtime/jsc_hooks.rs removes if parse_result.source.contents_is_recycled {, so that was the base branch's sole reader. Being a pub field it escapes rustc's dead_code lint and the unused_pub check the PR description cites. Nothing breaks at runtime.

The removed enqueue block in `transpile_source_code` was its only
reader. The other mentions wrote or copied it.
@robobun

robobun commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

Source.contents_is_recycled is removed in 5589e8f, with the five places that wrote or copied it. The enqueue block was its only reader, as the review says.

Two related items, both in the Notes of the description:

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed the current head (including the new Source.contents_is_recycled commit) and found no bugs; since this deletes ~1.7k lines across the module loader, event-loop dispatch, and the install run_tasks loop, a maintainer look is still worthwhile.

What was reviewed:

  • Traced the "cannot run" claim: ParseResult.pending_imports had no writer, the old Queue::on_poll returned early on an empty map, and map was only pushed by enqueue, whose sole caller was the removed block in src/runtime/jsc_hooks.rs.
  • Checked the collapsed on_package_download_error branch in src/install/PackageManager/runTasks.rs: the only impl setting HAS_ON_PACKAGE_DOWNLOAD_ERROR (StoreRunTasksCallbacks) also sets IS_STORE_INSTALLER, so no non-store impl can now hit the _store default.
  • Checked Bun__transpileFile's sync-success arm now returning null: the old promise local was only written through promise_ptr, which only the removed block wrote; allow_promise still gates the surviving concurrent-transpiler path.
  • Confirmed no remaining references to any removed name (contents_is_recycled, as_internal_promise, take_state, reset_shared_buffer, PackageManifestError, the removed hooks) and that the task-tag count assert matches the one removed tag.
Extended reasoning...

The change is a dead-code sweep of 25 files (+142/-1697) removing the auto-install AsyncModule queue, its task tag, two error variants, the pending_imports field and its linker consumers, Source.contents_is_recycled, and three RunTasksCallbacks hooks plus the any_root plumbing that only fed them. It touches no auth, crypto, or input-parsing surface; the sensitive aspects are the FFI return contract of Bun__transpileFile and the install loop's hook dispatch, both of which I traced as behavior-preserving. No findings, no CODEOWNERS coverage, and the one commit since the last posted review is a small follow-on removal I verified. Size and the subsystems involved (module loader, event-loop task dispatch, install run loop) are what tip this to defer rather than approve.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants