Skip to content

feat: add @tanstack/devtools-rspack (1:1 Vite parity) + shared @tanstack/devtools-bundler-core#484

Open
AlemTuzlak wants to merge 16 commits into
mainfrom
feat/devtools-rspack
Open

feat: add @tanstack/devtools-rspack (1:1 Vite parity) + shared @tanstack/devtools-bundler-core#484
AlemTuzlak wants to merge 16 commits into
mainfrom
feat/devtools-rspack

Conversation

@AlemTuzlak

@AlemTuzlak AlemTuzlak commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

🎯 Changes

Adds @tanstack/devtools-rspack — an Rspack plugin with 1:1 feature parity to @tanstack/devtools-vite — and extracts the shared, framework-agnostic logic into a new @tanstack/devtools-bundler-core package that both plugins consume, so the two bundler integrations stay in lockstep by construction rather than by hand.

New packages

  • @tanstack/devtools-bundler-core (new, published) — all bundler-agnostic logic: the transforms (source injection, enhanced logs, console-pipe codegen, devtools removal, runtime-bridge), editor integration, package-manager wiring, the shared handleDevToolsRequest middleware handler, the config type, and a small dev-state singleton (connection.ts). Zero vite dependency (local normalizePath, node:http types).
  • @tanstack/devtools-rspack (new, published) — a webpack-compatible loader (the transform pipeline, mirroring the Vite plugin's sub-plugins in order + gating) plus a plugin (apply(compiler): loader-rule injection, event bus, __tsd/* dev endpoints via setupMiddlewares, package-manager events).

Changed

  • @tanstack/devtools-vite — refactored to consume @tanstack/devtools-bundler-core. Public API and behavior unchanged (devtools, defineDevtoolsConfig, TanStackDevtoolsViteConfig, ConsoleLevel all still exported; TanStackDevtoolsViteConfig is now an alias of the shared TanStackDevtoolsConfig).

Feature parity

Source injection, enhanced console logs, console piping (client POST + SSE + server POST), devtools removal on build, editor open, event bus, package-manager install/bump/add-plugin, connection-placeholder injection, and runtime-bridge injection all have equivalent Rspack paths.

Validation

  • A real examples/react-rspack app was booted with rspack serve and manually verified: GET /__tsd/open-source200 (the setupMiddlewares auto-wiring works against @rspack/dev-server), source injection reaches served modules, and a production build strips the devtools code.
  • Unit tests cover the loader pipeline (incl. dev-server-vs-event-bus port routing), the plugin's loader-rule wiring (flat + pnpm-nested node_modules layouts), and the shared core. Counts: bundler-core 197, rspack 15, vite 21.

Known caveats / follow-ups

  • Rspack is ESM-only (mirroring devtools-vite); Rspack configs must be rspack.config.mjs or use await import() (documented in the README). A dual CJS build was considered and deliberately deferred.
  • Runtime-bridge channel wiring is Vite-SSR-only — Rspack has no server.environments[].hot equivalent; the code-injection half runs for server-target builds but is inert under Rspack's module.hot. Documented.
  • A couple of pre-existing best-effort behaviors in the shared handleDevToolsRequest (relocated verbatim from devtools-vite) — e.g. a malformed __tsd/open-source request not ending the response — are left as-is to avoid changing the shipping Vite behavior; noted for a future cleanup.
  • Automated e2e coverage for the Rspack example (the smoke was manual) is a follow-up.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally (per-package test:lib / test:types / test:eslint + full pnpm build all green).

🚀 Release Impact

  • This change affects published code, and I have generated changesets (@tanstack/devtools-bundler-core minor, @tanstack/devtools-rspack minor, @tanstack/devtools-vite patch).
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • New Features

    • Added TanStack Devtools support for Rspack projects, including source locations, enhanced logs, console piping, editor integration, package-manager support, and production cleanup.
    • Added a React Rspack example demonstrating Devtools integration and development workflows.
    • Introduced shared bundler capabilities for consistent Devtools behavior across supported bundlers.
  • Documentation

    • Added Rspack setup instructions, configuration guidance, and known limitations.
  • Bug Fixes

    • Improved cross-platform path handling and development-server request routing.

…eware, package manager) + dev-server port routing
Also removes the redundant @rspack/core devDependency from
devtools-rspack (already declared as peerDependency, unused
otherwise per knip).
…laceholder + runtime-bridge injection

The single loader rule excluded all of node_modules, so the __TANSTACK_DEVTOOLS_*
connection placeholders (which live in @tanstack/devtools-event-bus's client, under
node_modules for real consumers) were never replaced -- breaking a custom
eventBusConfig.port/host/protocol. Add a second loader rule scoped to
@tanstack/devtools*|event-bus* under node_modules (flat + pnpm nested layouts), and
normalize the step-4 id gate to posix so it matches on Windows. Matches vite's
connection-injection, which does not exclude node_modules.
… package.json, port-routing tests, doc accuracy
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change introduces a shared bundler-core package, adds Rspack DevTools integration and a React Rspack example, migrates Vite to shared utilities, and adds associated tests, documentation, package configuration, and release metadata.

Changes

Shared bundler core

Layer / File(s) Summary
Core contracts and utilities
packages/devtools-bundler-core/src/*, packages/devtools-bundler-core/package.json
Adds shared configuration types, AST utilities, connection state, editor integration, package detection, matching, location mapping, and package-manager operations.
Source transformations
packages/devtools-bundler-core/src/inject-*.ts, remove-devtools.ts, enhance-logs.ts
Adds AST-based DevTools/plugin injection and removal, JSX source annotations, and enhanced console logging.
Runtime bridges and request handling
packages/devtools-bundler-core/src/runtime-bridge.ts, utils.ts, virtual-console.ts
Adds framework-neutral request handling, runtime bridge channels, console piping, and shared build/test configuration.

Rspack integration

Layer / File(s) Summary
Rspack plugin and loader
packages/devtools-rspack/src/*.ts
Adds the Rspack plugin factory, loader pipeline, development middleware, event-bus integration, package-manager wiring, and production DevTools removal.
Rspack package configuration
packages/devtools-rspack/package.json, packages/devtools-rspack/README.md, packages/devtools-rspack/vite.config.ts
Adds package exports, scripts, dependencies, documentation, Vitest configuration, and documented parity caveats.

Vite and example integration

Layer / File(s) Summary
Vite shared-core adoption
packages/devtools-vite/src/plugin.ts, packages/devtools-vite/package.json, packages/devtools-vite/tests/*
Replaces local request/configuration utilities with @tanstack/devtools-bundler-core APIs and updates mocks and dependencies.
React Rspack example
examples/react-rspack/*
Adds a React application, Rspack configuration, HTML entry document, TypeScript configuration, and DevTools-enabled bootstrap.
Release metadata
.changeset/*, packages/devtools-rspack/CHANGELOG.md
Adds release entries for bundler core, Rspack, and the Vite refactor, plus the Rspack changelog heading.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Rspack
  participant DevtoolsPlugin
  participant Loader
  participant DevServer
  participant DevtoolsClient
  Rspack->>DevtoolsPlugin: apply compiler integration
  DevtoolsPlugin->>DevServer: register __tsd endpoints and event handlers
  Rspack->>Loader: process application modules
  Loader->>Loader: inject source data and console/runtime code
  Loader->>DevtoolsPlugin: use connection and server-origin state
  DevtoolsClient->>DevServer: send DevTools requests
  DevServer->>DevtoolsClient: stream console and package-manager events
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly states the main change: adding devtools-rspack and extracting shared devtools-bundler-core.
Description check ✅ Passed The description follows the required template with Changes, Checklist, and Release Impact sections filled out.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/devtools-rspack

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.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​rspack/​cli@​1.7.12991007897100
Added@​rspack/​core@​1.7.12961008097100
Added@​rspack/​dev-server@​1.2.19710010095100

View full report

@socket-security

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn Medium
Deprecated by its maintainer: npm uuid

Reason: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).

From: pnpm-lock.yamlnpm/@rspack/dev-server@1.2.1npm/@rspack/cli@1.7.12npm/uuid@8.3.2

ℹ Read more on: This package | This alert | What is a deprecated package?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Research the state of the package and determine if there are non-deprecated versions that can be used, or if it should be replaced with a new, supported solution.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/uuid@8.3.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@nx-cloud

nx-cloud Bot commented Jul 14, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit f389d2b

Command Status Duration Result
nx affected --targets=test:eslint,test:sherif,t... ✅ Succeeded 3m 33s View ↗
nx run-many --target=test:e2e --parallel=1 --pr... ✅ Succeeded 1m 1s View ↗
nx run-many --targets=build --exclude=examples/... ✅ Succeeded 47s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-14 16:33:48 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jul 14, 2026

Copy link
Copy Markdown
More templates

@tanstack/angular-devtools

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/angular-devtools@484

@tanstack/devtools

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/devtools@484

@tanstack/devtools-a11y

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/devtools-a11y@484

@tanstack/devtools-bundler-core

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/devtools-bundler-core@484

@tanstack/devtools-client

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/devtools-client@484

@tanstack/devtools-rspack

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/devtools-rspack@484

@tanstack/devtools-ui

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/devtools-ui@484

@tanstack/devtools-utils

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/devtools-utils@484

@tanstack/devtools-vite

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/devtools-vite@484

@tanstack/devtools-event-bus

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/devtools-event-bus@484

@tanstack/devtools-event-client

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/devtools-event-client@484

@tanstack/preact-devtools

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/preact-devtools@484

@tanstack/react-devtools

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/react-devtools@484

@tanstack/solid-devtools

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/solid-devtools@484

@tanstack/svelte-devtools

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/svelte-devtools@484

@tanstack/vue-devtools

npm i https://pkg.pr.new/TanStack/devtools/@tanstack/vue-devtools@484

commit: f389d2b

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Nitpick comments (6)
packages/devtools-bundler-core/src/utils.test.ts (1)

209-325: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for console-pipe branches.

The new suite exercises open-source and the generic __tsd JSON path, but __tsd/console-pipe/sse, __tsd/console-pipe/server, and __tsd/console-pipe branches (used by both the Vite and Rspack plugins for log piping) remain untested in this shared core package.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/utils.test.ts` around lines 209 - 325, Add
tests in the handleDevToolsRequest suite covering the __tsd/console-pipe/sse,
__tsd/console-pipe/server, and __tsd/console-pipe request branches. Verify each
branch’s callback payload and response behavior, including any request-body
handling, headers, writes, and termination expected by the shared handler.
packages/devtools-rspack/src/plugin.ts (1)

66-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider typing compiler instead of any.

The entire plugin (compiler, server, middlewares) is typed any, so tsc provides no protection against Rspack API drift (e.g., between the ^1.0.0 peer range and the current 2.x major) on compiler.options.devServer, compiler.hooks.done.tap, etc. Typing against @rspack/core's Compiler/RspackOptions (added as a devDependency) would catch such breaks at compile time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-rspack/src/plugin.ts` around lines 66 - 68, Replace the any
types used by apply’s compiler and the plugin’s related server and middleware
values with the appropriate `@rspack/core` Compiler and RspackOptions types.
Update the devDependency as needed, then adjust the implementation to satisfy
those types while preserving compiler.options.devServer and
compiler.hooks.done.tap behavior.
packages/devtools-rspack/src/plugin.test.ts (1)

20-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Coverage gap: package-manager wiring and event-bus startup are untested.

The suite verifies loader-rule injection and setupMiddlewares presence, but nothing exercises wirePackageManager (compiler.hooks.done.tap/watchRun.tapPromise calls, devtoolsEventClient event handling) or the ServerEventBus startup path triggered inside setupMiddlewares. Given mockCompiler already stubs these hooks as vi.fn(), asserting they're tapped (and that devtoolsEventClient listeners fire as expected) would meaningfully close the gap on this plugin's most stateful logic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-rspack/src/plugin.test.ts` around lines 20 - 91, Expand the
TanStackDevtoolsRspackPlugin tests to cover wirePackageManager by asserting
compiler.hooks.done.tap and watchRun.tapPromise are registered through
mockCompiler. Exercise the devtoolsEventClient listeners and verify their
expected handling. Invoke the development setupMiddlewares path and assert that
the ServerEventBus startup behavior is triggered, preserving the existing
production behavior assertions.
packages/devtools-rspack/src/loader.ts (1)

160-175: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer the documented this.target loader-context property.

Line 167 reaches into this._compiler?.options?.target to read the build target. Rspack's loader API documents this.target directly on the loader context for exactly this purpose, avoiding a dependency on the internal _compiler object shape.

♻️ Proposed fix
-  const target = String(this._compiler?.options?.target ?? 'web')
+  const target = String(this.target ?? 'web')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-rspack/src/loader.ts` around lines 160 - 175, Update
tanstackDevtoolsLoader to read the build target from the documented this.target
loader-context property instead of accessing this._compiler?.options?.target,
while preserving the existing string conversion and fallback behavior.
packages/devtools-bundler-core/src/types.ts (1)

24-30: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Inconsistent optionality of enabled across sub-configs.

enhancedLogs.enabled and injectSource.enabled are required, forcing consumers to pass enabled: true even when they only want to set ignore — contradicting the @default true doc and inconsistent with consolePiping.enabled/eventBusConfig.enabled, which are optional.

♻️ Proposed fix
   enhancedLogs?: {
     /**
      * Whether to enable enhanced logging.
      * `@default` true
      */
-    enabled: boolean
+    enabled?: boolean
   }
@@
   injectSource?: {
     /**
      * Whether to enable source injection via data-tsd-source.
      * `@default` true
      */
-    enabled: boolean
+    enabled?: boolean
     /**
      * List of files or patterns to ignore for source injection.
      */
     ignore?: {
       files?: Array<string | RegExp>
       components?: Array<string | RegExp>
     }
   }

Also applies to: 45-58, 64-75

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/types.ts` around lines 24 - 30, Make the
enabled property optional in the enhancedLogs and injectSource configuration
types, matching consolePiping and eventBusConfig while preserving the documented
default behavior of true when omitted. Update the corresponding enabled
declarations in the affected sub-config definitions without changing their
ignore or other properties.
packages/devtools-bundler-core/src/matcher.ts (1)

3-19: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Matchers are recompiled on every call instead of being cached.

patterns.map(...) re-runs picomatch(pattern) compilation for every invocation of matcher, even though ignore.files/ignore.components are typically stable config passed repeatedly across many files and JSX elements during the transform pipeline (a hot path re-run on every dev-server rebuild/HMR). Caching the compiled matchers per patterns array reference avoids redundant picomatch compilation.

♻️ Proposed fix: cache compiled matchers per patterns array
+const compiledCache = new WeakMap<Array<string | RegExp>, Array<(s: string) => boolean>>()
+
 export const matcher = (
   patterns: Array<string | RegExp>,
   str: string,
 ): boolean => {
   if (patterns.length === 0) {
     return false
   }
-  const matchers = patterns.map((pattern) => {
-    if (typeof pattern === 'string') {
-      return picomatch(pattern)
-    } else {
-      return (s: string) => pattern.test(s)
-    }
-  })
+  let matchers = compiledCache.get(patterns)
+  if (!matchers) {
+    matchers = patterns.map((pattern) => {
+      if (typeof pattern === 'string') {
+        return picomatch(pattern)
+      } else {
+        return (s: string) => pattern.test(s)
+      }
+    })
+    compiledCache.set(patterns, matchers)
+  }
 
   return matchers.some((isMatch) => isMatch(str))
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/matcher.ts` around lines 3 - 19, Update
the matcher implementation to cache the compiled matchers by patterns array
reference, so repeated matcher calls reuse existing picomatch functions instead
of rerunning patterns.map and picomatch. Preserve the current empty-pattern
behavior and matching semantics, including RegExp handling, while ensuring
different patterns array references receive their own compiled cache entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/devtools-bundler-core/src/ast-utils.ts`:
- Line 42: Update the conditional in the AST value-handling logic around the
`'type' in value` check to first require a non-null object via a typeof guard,
while preserving the existing array exclusion. This must safely skip undefined,
null, and primitive cached values before evaluating the in-operator.

In `@packages/devtools-bundler-core/src/enhance-logs.test.ts`:
- Around line 106-115: Correct the test case titled “it does not add enhanced
console.error to console.error that is not called” so its enhanceConsoleLog
input contains an uncalled console.error scenario rather than console.log. Keep
the existing test setup and undefined-output assertion unchanged.

In `@packages/devtools-bundler-core/src/matcher.ts`:
- Around line 13-15: Update the matcher’s RegExp branch to clone pattern before
testing, removing the global and sticky flags so repeated calls to the returned
function do not share or mutate pattern.lastIndex. Preserve the existing
pattern.test(s) matching behavior for non-stateful expressions.

In `@packages/devtools-bundler-core/src/package-manager.ts`:
- Around line 30-69: Replace the hardcoded [`@tanstack/devtools-vite`] prefix in
addPluginToDevtools and installPackage with a framework-neutral prefix for every
success and error log in this file. Preserve the existing message content and
behavior while ensuring shared bundler-core logs are not branded specifically
for Vite.
- Around line 162-181: Update emitOutdatedDeps so its exec callback always
settles the returned promise, including when stdout is empty or JSON parsing
fails. Preserve emitting outdated-deps-read and returning parsed data for valid
JSON, but resolve with null for no output or invalid output so callers awaiting
outdatedDeps cannot hang.
- Around line 73-136: Remove shell interpolation of packageName from
getInstallCommand and installPackage: represent the package manager executable
and arguments separately, then invoke the installer with execFile or spawn
rather than exec. Validate packageName against the expected package-name format
before spawning and return a failed result with an error for invalid input;
preserve the existing success and failure reporting for valid installs.

In `@packages/devtools-bundler-core/src/remove-devtools.ts`:
- Around line 140-175: The import-rebuild branch in the Pass 3 walker must
preserve surviving default imports. Update the logic around toRemove, remaining,
and specTexts to separate ImportDefaultSpecifier from named specifiers and
reconstruct the statement with the default specifier before the named-specifier
block, preserving valid import semantics for mixed imports.
- Around line 107-138: The JSX removal logic in the devtools stripping pass must
preserve valid JavaScript syntax: update the `walk` callback around `parentNode`
so `s.remove()` is used only when the matched element is an actual JSX child,
while expression contexts such as logical expressions, ternaries, and
initializers are replaced with `null`. Also update the import-rebuilding logic
in the subsequent import cleanup section to preserve each import’s original
default, namespace, or named specifier shape instead of always emitting a named
import.

In `@packages/devtools-bundler-core/src/utils.test.ts`:
- Around line 264-289: Update the __tsd/open-source handling in
handleDevToolsRequest so missing or malformed source values terminate the
request with an appropriate 4xx response instead of returning without action.
Preserve the existing behavior for valid source values and ensure both invalid
cases no longer invoke the normal callback flow or leave the response open.

In `@packages/devtools-rspack/src/plugin.ts`:
- Around line 151-153: Remove the process.env.NODE_ENV === 'development'
condition from the wirePackageManager invocation guard in the plugin, retaining
only the CI check and compiler development-mode gating already provided by the
surrounding logic. Ensure wirePackageManager runs for valid non-CI development
builds regardless of NODE_ENV.
- Around line 140-146: Scope the shared devtools connection/origin state and
event registrations to each compiler instance in the plugin’s apply flow. Update
the setDevtoolsConnection/setDevServerOrigin usage and wirePackageManager
registration so repeated apply() calls neither overwrite another compiler’s
state nor attach duplicate handlers; add appropriate per-compiler cleanup or
registration guards.

---

Nitpick comments:
In `@packages/devtools-bundler-core/src/matcher.ts`:
- Around line 3-19: Update the matcher implementation to cache the compiled
matchers by patterns array reference, so repeated matcher calls reuse existing
picomatch functions instead of rerunning patterns.map and picomatch. Preserve
the current empty-pattern behavior and matching semantics, including RegExp
handling, while ensuring different patterns array references receive their own
compiled cache entry.

In `@packages/devtools-bundler-core/src/types.ts`:
- Around line 24-30: Make the enabled property optional in the enhancedLogs and
injectSource configuration types, matching consolePiping and eventBusConfig
while preserving the documented default behavior of true when omitted. Update
the corresponding enabled declarations in the affected sub-config definitions
without changing their ignore or other properties.

In `@packages/devtools-bundler-core/src/utils.test.ts`:
- Around line 209-325: Add tests in the handleDevToolsRequest suite covering the
__tsd/console-pipe/sse, __tsd/console-pipe/server, and __tsd/console-pipe
request branches. Verify each branch’s callback payload and response behavior,
including any request-body handling, headers, writes, and termination expected
by the shared handler.

In `@packages/devtools-rspack/src/loader.ts`:
- Around line 160-175: Update tanstackDevtoolsLoader to read the build target
from the documented this.target loader-context property instead of accessing
this._compiler?.options?.target, while preserving the existing string conversion
and fallback behavior.

In `@packages/devtools-rspack/src/plugin.test.ts`:
- Around line 20-91: Expand the TanStackDevtoolsRspackPlugin tests to cover
wirePackageManager by asserting compiler.hooks.done.tap and watchRun.tapPromise
are registered through mockCompiler. Exercise the devtoolsEventClient listeners
and verify their expected handling. Invoke the development setupMiddlewares path
and assert that the ServerEventBus startup behavior is triggered, preserving the
existing production behavior assertions.

In `@packages/devtools-rspack/src/plugin.ts`:
- Around line 66-68: Replace the any types used by apply’s compiler and the
plugin’s related server and middleware values with the appropriate `@rspack/core`
Compiler and RspackOptions types. Update the devDependency as needed, then
adjust the implementation to satisfy those types while preserving
compiler.options.devServer and compiler.hooks.done.tap behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9888de2-edba-4e5d-98f7-e4d5256c77c6

📥 Commits

Reviewing files that changed from the base of the PR and between 92f69d0 and f389d2b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (61)
  • .changeset/devtools-bundler-core.md
  • .changeset/devtools-vite-bundler-core-refactor.md
  • .changeset/rspack-plugin.md
  • examples/react-rspack/index.html
  • examples/react-rspack/package.json
  • examples/react-rspack/rspack.config.mjs
  • examples/react-rspack/src/App.tsx
  • examples/react-rspack/src/main.tsx
  • examples/react-rspack/tsconfig.json
  • packages/devtools-bundler-core/eslint.config.js
  • packages/devtools-bundler-core/package.json
  • packages/devtools-bundler-core/src/ast-utils.test.ts
  • packages/devtools-bundler-core/src/ast-utils.ts
  • packages/devtools-bundler-core/src/connection.test.ts
  • packages/devtools-bundler-core/src/connection.ts
  • packages/devtools-bundler-core/src/devtools-packages.test.ts
  • packages/devtools-bundler-core/src/devtools-packages.ts
  • packages/devtools-bundler-core/src/editor.test.ts
  • packages/devtools-bundler-core/src/editor.ts
  • packages/devtools-bundler-core/src/enhance-logs.test.ts
  • packages/devtools-bundler-core/src/enhance-logs.ts
  • packages/devtools-bundler-core/src/index.ts
  • packages/devtools-bundler-core/src/inject-plugin.test.ts
  • packages/devtools-bundler-core/src/inject-plugin.ts
  • packages/devtools-bundler-core/src/inject-source.test.ts
  • packages/devtools-bundler-core/src/inject-source.ts
  • packages/devtools-bundler-core/src/matcher.test.ts
  • packages/devtools-bundler-core/src/matcher.ts
  • packages/devtools-bundler-core/src/normalize-path.ts
  • packages/devtools-bundler-core/src/offset-to-loc.test.ts
  • packages/devtools-bundler-core/src/offset-to-loc.ts
  • packages/devtools-bundler-core/src/package-manager.ts
  • packages/devtools-bundler-core/src/remove-devtools.test.ts
  • packages/devtools-bundler-core/src/remove-devtools.ts
  • packages/devtools-bundler-core/src/runtime-bridge.test.ts
  • packages/devtools-bundler-core/src/runtime-bridge.ts
  • packages/devtools-bundler-core/src/types.ts
  • packages/devtools-bundler-core/src/utils.test.ts
  • packages/devtools-bundler-core/src/utils.ts
  • packages/devtools-bundler-core/src/virtual-console.test.ts
  • packages/devtools-bundler-core/src/virtual-console.ts
  • packages/devtools-bundler-core/tsconfig.docs.json
  • packages/devtools-bundler-core/tsconfig.json
  • packages/devtools-bundler-core/vite.config.ts
  • packages/devtools-rspack/CHANGELOG.md
  • packages/devtools-rspack/README.md
  • packages/devtools-rspack/eslint.config.js
  • packages/devtools-rspack/package.json
  • packages/devtools-rspack/src/index.test.ts
  • packages/devtools-rspack/src/index.ts
  • packages/devtools-rspack/src/loader.test.ts
  • packages/devtools-rspack/src/loader.ts
  • packages/devtools-rspack/src/plugin.test.ts
  • packages/devtools-rspack/src/plugin.ts
  • packages/devtools-rspack/tsconfig.docs.json
  • packages/devtools-rspack/tsconfig.json
  • packages/devtools-rspack/vite.config.ts
  • packages/devtools-vite/package.json
  • packages/devtools-vite/src/plugin.ts
  • packages/devtools-vite/tests/index.test.ts
  • packages/devtools-vite/tests/utils.test.ts
💤 Files with no reviewable changes (1)
  • packages/devtools-vite/tests/utils.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 11

🧹 Nitpick comments (6)
packages/devtools-bundler-core/src/utils.test.ts (1)

209-325: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for console-pipe branches.

The new suite exercises open-source and the generic __tsd JSON path, but __tsd/console-pipe/sse, __tsd/console-pipe/server, and __tsd/console-pipe branches (used by both the Vite and Rspack plugins for log piping) remain untested in this shared core package.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/utils.test.ts` around lines 209 - 325, Add
tests in the handleDevToolsRequest suite covering the __tsd/console-pipe/sse,
__tsd/console-pipe/server, and __tsd/console-pipe request branches. Verify each
branch’s callback payload and response behavior, including any request-body
handling, headers, writes, and termination expected by the shared handler.
packages/devtools-rspack/src/plugin.ts (1)

66-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider typing compiler instead of any.

The entire plugin (compiler, server, middlewares) is typed any, so tsc provides no protection against Rspack API drift (e.g., between the ^1.0.0 peer range and the current 2.x major) on compiler.options.devServer, compiler.hooks.done.tap, etc. Typing against @rspack/core's Compiler/RspackOptions (added as a devDependency) would catch such breaks at compile time.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-rspack/src/plugin.ts` around lines 66 - 68, Replace the any
types used by apply’s compiler and the plugin’s related server and middleware
values with the appropriate `@rspack/core` Compiler and RspackOptions types.
Update the devDependency as needed, then adjust the implementation to satisfy
those types while preserving compiler.options.devServer and
compiler.hooks.done.tap behavior.
packages/devtools-rspack/src/plugin.test.ts (1)

20-91: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Coverage gap: package-manager wiring and event-bus startup are untested.

The suite verifies loader-rule injection and setupMiddlewares presence, but nothing exercises wirePackageManager (compiler.hooks.done.tap/watchRun.tapPromise calls, devtoolsEventClient event handling) or the ServerEventBus startup path triggered inside setupMiddlewares. Given mockCompiler already stubs these hooks as vi.fn(), asserting they're tapped (and that devtoolsEventClient listeners fire as expected) would meaningfully close the gap on this plugin's most stateful logic.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-rspack/src/plugin.test.ts` around lines 20 - 91, Expand the
TanStackDevtoolsRspackPlugin tests to cover wirePackageManager by asserting
compiler.hooks.done.tap and watchRun.tapPromise are registered through
mockCompiler. Exercise the devtoolsEventClient listeners and verify their
expected handling. Invoke the development setupMiddlewares path and assert that
the ServerEventBus startup behavior is triggered, preserving the existing
production behavior assertions.
packages/devtools-rspack/src/loader.ts (1)

160-175: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer the documented this.target loader-context property.

Line 167 reaches into this._compiler?.options?.target to read the build target. Rspack's loader API documents this.target directly on the loader context for exactly this purpose, avoiding a dependency on the internal _compiler object shape.

♻️ Proposed fix
-  const target = String(this._compiler?.options?.target ?? 'web')
+  const target = String(this.target ?? 'web')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-rspack/src/loader.ts` around lines 160 - 175, Update
tanstackDevtoolsLoader to read the build target from the documented this.target
loader-context property instead of accessing this._compiler?.options?.target,
while preserving the existing string conversion and fallback behavior.
packages/devtools-bundler-core/src/types.ts (1)

24-30: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Inconsistent optionality of enabled across sub-configs.

enhancedLogs.enabled and injectSource.enabled are required, forcing consumers to pass enabled: true even when they only want to set ignore — contradicting the @default true doc and inconsistent with consolePiping.enabled/eventBusConfig.enabled, which are optional.

♻️ Proposed fix
   enhancedLogs?: {
     /**
      * Whether to enable enhanced logging.
      * `@default` true
      */
-    enabled: boolean
+    enabled?: boolean
   }
@@
   injectSource?: {
     /**
      * Whether to enable source injection via data-tsd-source.
      * `@default` true
      */
-    enabled: boolean
+    enabled?: boolean
     /**
      * List of files or patterns to ignore for source injection.
      */
     ignore?: {
       files?: Array<string | RegExp>
       components?: Array<string | RegExp>
     }
   }

Also applies to: 45-58, 64-75

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/types.ts` around lines 24 - 30, Make the
enabled property optional in the enhancedLogs and injectSource configuration
types, matching consolePiping and eventBusConfig while preserving the documented
default behavior of true when omitted. Update the corresponding enabled
declarations in the affected sub-config definitions without changing their
ignore or other properties.
packages/devtools-bundler-core/src/matcher.ts (1)

3-19: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Matchers are recompiled on every call instead of being cached.

patterns.map(...) re-runs picomatch(pattern) compilation for every invocation of matcher, even though ignore.files/ignore.components are typically stable config passed repeatedly across many files and JSX elements during the transform pipeline (a hot path re-run on every dev-server rebuild/HMR). Caching the compiled matchers per patterns array reference avoids redundant picomatch compilation.

♻️ Proposed fix: cache compiled matchers per patterns array
+const compiledCache = new WeakMap<Array<string | RegExp>, Array<(s: string) => boolean>>()
+
 export const matcher = (
   patterns: Array<string | RegExp>,
   str: string,
 ): boolean => {
   if (patterns.length === 0) {
     return false
   }
-  const matchers = patterns.map((pattern) => {
-    if (typeof pattern === 'string') {
-      return picomatch(pattern)
-    } else {
-      return (s: string) => pattern.test(s)
-    }
-  })
+  let matchers = compiledCache.get(patterns)
+  if (!matchers) {
+    matchers = patterns.map((pattern) => {
+      if (typeof pattern === 'string') {
+        return picomatch(pattern)
+      } else {
+        return (s: string) => pattern.test(s)
+      }
+    })
+    compiledCache.set(patterns, matchers)
+  }
 
   return matchers.some((isMatch) => isMatch(str))
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/matcher.ts` around lines 3 - 19, Update
the matcher implementation to cache the compiled matchers by patterns array
reference, so repeated matcher calls reuse existing picomatch functions instead
of rerunning patterns.map and picomatch. Preserve the current empty-pattern
behavior and matching semantics, including RegExp handling, while ensuring
different patterns array references receive their own compiled cache entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/devtools-bundler-core/src/ast-utils.ts`:
- Line 42: Update the conditional in the AST value-handling logic around the
`'type' in value` check to first require a non-null object via a typeof guard,
while preserving the existing array exclusion. This must safely skip undefined,
null, and primitive cached values before evaluating the in-operator.

In `@packages/devtools-bundler-core/src/enhance-logs.test.ts`:
- Around line 106-115: Correct the test case titled “it does not add enhanced
console.error to console.error that is not called” so its enhanceConsoleLog
input contains an uncalled console.error scenario rather than console.log. Keep
the existing test setup and undefined-output assertion unchanged.

In `@packages/devtools-bundler-core/src/matcher.ts`:
- Around line 13-15: Update the matcher’s RegExp branch to clone pattern before
testing, removing the global and sticky flags so repeated calls to the returned
function do not share or mutate pattern.lastIndex. Preserve the existing
pattern.test(s) matching behavior for non-stateful expressions.

In `@packages/devtools-bundler-core/src/package-manager.ts`:
- Around line 30-69: Replace the hardcoded [`@tanstack/devtools-vite`] prefix in
addPluginToDevtools and installPackage with a framework-neutral prefix for every
success and error log in this file. Preserve the existing message content and
behavior while ensuring shared bundler-core logs are not branded specifically
for Vite.
- Around line 162-181: Update emitOutdatedDeps so its exec callback always
settles the returned promise, including when stdout is empty or JSON parsing
fails. Preserve emitting outdated-deps-read and returning parsed data for valid
JSON, but resolve with null for no output or invalid output so callers awaiting
outdatedDeps cannot hang.
- Around line 73-136: Remove shell interpolation of packageName from
getInstallCommand and installPackage: represent the package manager executable
and arguments separately, then invoke the installer with execFile or spawn
rather than exec. Validate packageName against the expected package-name format
before spawning and return a failed result with an error for invalid input;
preserve the existing success and failure reporting for valid installs.

In `@packages/devtools-bundler-core/src/remove-devtools.ts`:
- Around line 140-175: The import-rebuild branch in the Pass 3 walker must
preserve surviving default imports. Update the logic around toRemove, remaining,
and specTexts to separate ImportDefaultSpecifier from named specifiers and
reconstruct the statement with the default specifier before the named-specifier
block, preserving valid import semantics for mixed imports.
- Around line 107-138: The JSX removal logic in the devtools stripping pass must
preserve valid JavaScript syntax: update the `walk` callback around `parentNode`
so `s.remove()` is used only when the matched element is an actual JSX child,
while expression contexts such as logical expressions, ternaries, and
initializers are replaced with `null`. Also update the import-rebuilding logic
in the subsequent import cleanup section to preserve each import’s original
default, namespace, or named specifier shape instead of always emitting a named
import.

In `@packages/devtools-bundler-core/src/utils.test.ts`:
- Around line 264-289: Update the __tsd/open-source handling in
handleDevToolsRequest so missing or malformed source values terminate the
request with an appropriate 4xx response instead of returning without action.
Preserve the existing behavior for valid source values and ensure both invalid
cases no longer invoke the normal callback flow or leave the response open.

In `@packages/devtools-rspack/src/plugin.ts`:
- Around line 151-153: Remove the process.env.NODE_ENV === 'development'
condition from the wirePackageManager invocation guard in the plugin, retaining
only the CI check and compiler development-mode gating already provided by the
surrounding logic. Ensure wirePackageManager runs for valid non-CI development
builds regardless of NODE_ENV.
- Around line 140-146: Scope the shared devtools connection/origin state and
event registrations to each compiler instance in the plugin’s apply flow. Update
the setDevtoolsConnection/setDevServerOrigin usage and wirePackageManager
registration so repeated apply() calls neither overwrite another compiler’s
state nor attach duplicate handlers; add appropriate per-compiler cleanup or
registration guards.

---

Nitpick comments:
In `@packages/devtools-bundler-core/src/matcher.ts`:
- Around line 3-19: Update the matcher implementation to cache the compiled
matchers by patterns array reference, so repeated matcher calls reuse existing
picomatch functions instead of rerunning patterns.map and picomatch. Preserve
the current empty-pattern behavior and matching semantics, including RegExp
handling, while ensuring different patterns array references receive their own
compiled cache entry.

In `@packages/devtools-bundler-core/src/types.ts`:
- Around line 24-30: Make the enabled property optional in the enhancedLogs and
injectSource configuration types, matching consolePiping and eventBusConfig
while preserving the documented default behavior of true when omitted. Update
the corresponding enabled declarations in the affected sub-config definitions
without changing their ignore or other properties.

In `@packages/devtools-bundler-core/src/utils.test.ts`:
- Around line 209-325: Add tests in the handleDevToolsRequest suite covering the
__tsd/console-pipe/sse, __tsd/console-pipe/server, and __tsd/console-pipe
request branches. Verify each branch’s callback payload and response behavior,
including any request-body handling, headers, writes, and termination expected
by the shared handler.

In `@packages/devtools-rspack/src/loader.ts`:
- Around line 160-175: Update tanstackDevtoolsLoader to read the build target
from the documented this.target loader-context property instead of accessing
this._compiler?.options?.target, while preserving the existing string conversion
and fallback behavior.

In `@packages/devtools-rspack/src/plugin.test.ts`:
- Around line 20-91: Expand the TanStackDevtoolsRspackPlugin tests to cover
wirePackageManager by asserting compiler.hooks.done.tap and watchRun.tapPromise
are registered through mockCompiler. Exercise the devtoolsEventClient listeners
and verify their expected handling. Invoke the development setupMiddlewares path
and assert that the ServerEventBus startup behavior is triggered, preserving the
existing production behavior assertions.

In `@packages/devtools-rspack/src/plugin.ts`:
- Around line 66-68: Replace the any types used by apply’s compiler and the
plugin’s related server and middleware values with the appropriate `@rspack/core`
Compiler and RspackOptions types. Update the devDependency as needed, then
adjust the implementation to satisfy those types while preserving
compiler.options.devServer and compiler.hooks.done.tap behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9888de2-edba-4e5d-98f7-e4d5256c77c6

📥 Commits

Reviewing files that changed from the base of the PR and between 92f69d0 and f389d2b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (61)
  • .changeset/devtools-bundler-core.md
  • .changeset/devtools-vite-bundler-core-refactor.md
  • .changeset/rspack-plugin.md
  • examples/react-rspack/index.html
  • examples/react-rspack/package.json
  • examples/react-rspack/rspack.config.mjs
  • examples/react-rspack/src/App.tsx
  • examples/react-rspack/src/main.tsx
  • examples/react-rspack/tsconfig.json
  • packages/devtools-bundler-core/eslint.config.js
  • packages/devtools-bundler-core/package.json
  • packages/devtools-bundler-core/src/ast-utils.test.ts
  • packages/devtools-bundler-core/src/ast-utils.ts
  • packages/devtools-bundler-core/src/connection.test.ts
  • packages/devtools-bundler-core/src/connection.ts
  • packages/devtools-bundler-core/src/devtools-packages.test.ts
  • packages/devtools-bundler-core/src/devtools-packages.ts
  • packages/devtools-bundler-core/src/editor.test.ts
  • packages/devtools-bundler-core/src/editor.ts
  • packages/devtools-bundler-core/src/enhance-logs.test.ts
  • packages/devtools-bundler-core/src/enhance-logs.ts
  • packages/devtools-bundler-core/src/index.ts
  • packages/devtools-bundler-core/src/inject-plugin.test.ts
  • packages/devtools-bundler-core/src/inject-plugin.ts
  • packages/devtools-bundler-core/src/inject-source.test.ts
  • packages/devtools-bundler-core/src/inject-source.ts
  • packages/devtools-bundler-core/src/matcher.test.ts
  • packages/devtools-bundler-core/src/matcher.ts
  • packages/devtools-bundler-core/src/normalize-path.ts
  • packages/devtools-bundler-core/src/offset-to-loc.test.ts
  • packages/devtools-bundler-core/src/offset-to-loc.ts
  • packages/devtools-bundler-core/src/package-manager.ts
  • packages/devtools-bundler-core/src/remove-devtools.test.ts
  • packages/devtools-bundler-core/src/remove-devtools.ts
  • packages/devtools-bundler-core/src/runtime-bridge.test.ts
  • packages/devtools-bundler-core/src/runtime-bridge.ts
  • packages/devtools-bundler-core/src/types.ts
  • packages/devtools-bundler-core/src/utils.test.ts
  • packages/devtools-bundler-core/src/utils.ts
  • packages/devtools-bundler-core/src/virtual-console.test.ts
  • packages/devtools-bundler-core/src/virtual-console.ts
  • packages/devtools-bundler-core/tsconfig.docs.json
  • packages/devtools-bundler-core/tsconfig.json
  • packages/devtools-bundler-core/vite.config.ts
  • packages/devtools-rspack/CHANGELOG.md
  • packages/devtools-rspack/README.md
  • packages/devtools-rspack/eslint.config.js
  • packages/devtools-rspack/package.json
  • packages/devtools-rspack/src/index.test.ts
  • packages/devtools-rspack/src/index.ts
  • packages/devtools-rspack/src/loader.test.ts
  • packages/devtools-rspack/src/loader.ts
  • packages/devtools-rspack/src/plugin.test.ts
  • packages/devtools-rspack/src/plugin.ts
  • packages/devtools-rspack/tsconfig.docs.json
  • packages/devtools-rspack/tsconfig.json
  • packages/devtools-rspack/vite.config.ts
  • packages/devtools-vite/package.json
  • packages/devtools-vite/src/plugin.ts
  • packages/devtools-vite/tests/index.test.ts
  • packages/devtools-vite/tests/utils.test.ts
💤 Files with no reviewable changes (1)
  • packages/devtools-vite/tests/utils.test.ts
🛑 Comments failed to post (11)
packages/devtools-bundler-core/src/ast-utils.ts (1)

42-42: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Add a type guard to prevent potential TypeError on missing keys.

If a property is cached for a node type but happens to be missing (undefined) on a subsequent instance of the same node type, evaluating 'type' in value will throw a TypeError (since value === null and Array.isArray(value) are both false for undefined).

Adding a typeof value === 'object' check ensures it safely skips undefined or any unexpected primitive values.

🛡️ Proposed fix
-    } else if ('type' in value) {
+    } else if (typeof value === 'object' && 'type' in value) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    } else if (typeof value === 'object' && 'type' in value) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/ast-utils.ts` at line 42, Update the
conditional in the AST value-handling logic around the `'type' in value` check
to first require a non-null object via a typeof guard, while preserving the
existing array exclusion. This must safely skip undefined, null, and primitive
cached values before evaluating the in-operator.
packages/devtools-bundler-core/src/enhance-logs.test.ts (1)

106-115: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Test doesn't exercise what its title claims.

This test is titled for "console.error that is not called" but its body invokes console.log (copy-pasted from the test above at lines 28-37), so the console.error "not called" scenario is never actually covered.

🐛 Proposed fix
   test('it does not add enhanced console.error to console.error that is not called', () => {
     const output = enhanceConsoleLog(
       `
-        console.log 
+        console.error 
         `,
       'test.jsx',
       3000,
     )
     expect(output).toBe(undefined)
   })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

  test('it does not add enhanced console.error to console.error that is not called', () => {
    const output = enhanceConsoleLog(
      `
        console.error 
        `,
      'test.jsx',
      3000,
    )
    expect(output).toBe(undefined)
  })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/enhance-logs.test.ts` around lines 106 -
115, Correct the test case titled “it does not add enhanced console.error to
console.error that is not called” so its enhanceConsoleLog input contains an
uncalled console.error scenario rather than console.log. Keep the existing test
setup and undefined-output assertion unchanged.
packages/devtools-bundler-core/src/matcher.ts (1)

13-15: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

set -euo pipefail

printf '\n== matcher.ts ==\n'
cat -n packages/devtools-bundler-core/src/matcher.ts

printf '\n== call sites ==\n'
rg -n "matcher\\(" packages/devtools-bundler-core -g '!**/dist/**' -g '!**/build/**'

printf '\n== pattern types/usages ==\n'
rg -n "ignore\\.(files|components)|picomatch|RegExp" packages/devtools-bundler-core -g '!**/dist/**' -g '!**/build/**'

Repository: TanStack/devtools

Length of output: 3381


Avoid reusing stateful regexes here. pattern.test(s) mutates lastIndex on g/y patterns, so a reused RegExp can alternate between match/no-match across calls. Clone it without those flags before testing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/matcher.ts` around lines 13 - 15, Update
the matcher’s RegExp branch to clone pattern before testing, removing the global
and sticky flags so repeated calls to the returned function do not share or
mutate pattern.lastIndex. Preserve the existing pattern.test(s) matching
behavior for non-stateful expressions.
packages/devtools-bundler-core/src/package-manager.ts (3)

30-69: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Shared core hardcodes the @tanstack/devtools-vite log prefix.

addPluginToDevtools and installPackage are framework-agnostic functions in the shared bundler core, but their console messages always say [@tanstack/devtools-vite]. devtools-rspack's wirePackageManager calls both directly, so Rspack users would see misleading Vite-branded logs for install/plugin-injection failures and successes.

♻️ Proposed fix: use a generic prefix
-        `[`@tanstack/devtools-vite`] Could not add plugin. ${error}.`,
+        `[`@tanstack/devtools`] Could not add plugin. ${error}.`,

(and similarly for the other three hardcoded [@tanstack/devtools-vite] strings in this file)

Also applies to: 90-136

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { exec } from 'node:child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/package-manager.ts` around lines 30 - 69,
Replace the hardcoded [`@tanstack/devtools-vite`] prefix in addPluginToDevtools
and installPackage with a framework-neutral prefix for every success and error
log in this file. Preserve the existing message content and behavior while
ensuring shared bundler-core logs are not branded specifically for Vite.

73-136: 🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== package-manager.ts ==\n'
sed -n '1,220p' packages/devtools-bundler-core/src/package-manager.ts

printf '\n== package-manager call sites ==\n'
rg -n "installPackage\(|bump-package-version|install-devtools|packageWithVersion|packageName" packages -g '!**/dist/**'

Repository: TanStack/devtools

Length of output: 21337


Avoid shell-interpolating packageName here in packages/devtools-bundler-core/src/package-manager.ts:75-136: getInstallCommand builds a command string with ${packageName} and installPackage passes it to exec. Both the install-devtools and bump-package-version flows forward event payload values directly, so a crafted package name can inject shell syntax. Use execFile/spawn with args and validate the package name before spawning the installer.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { exec } from 'node:child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🪛 OpenGrep (1.25.0)

[ERROR] 106-134: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/package-manager.ts` around lines 73 - 136,
Remove shell interpolation of packageName from getInstallCommand and
installPackage: represent the package manager executable and arguments
separately, then invoke the installer with execFile or spawn rather than exec.
Validate packageName against the expected package-name format before spawning
and return a failed result with an error for invalid input; preserve the
existing success and failure reporting for valid installs.

162-181: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Promise never resolves when there are no outdated deps.

exec(outdatedCommand, (_, stdout) => { if (stdout) {...; resolve(...) } }) only calls resolve inside the if (stdout) branch. Per npm's own docs, outdated --json produces empty stdout (with exit code 0) when there's nothing outdated — an everyday, common case — so this promise hangs forever whenever a project is fully up to date. Since wirePackageManager's mounted handler does await outdatedDeps before emitting package-json-read, that handler would also hang indefinitely for such projects.

🐛 Proposed fix: always resolve
     exec(outdatedCommand, (_, stdout) => {
       // outdated commands exit with code 1 if there are outdated packages, but still output valid JSON
-      if (stdout) {
-        const newOutdatedDeps = tryParseJson<OutdatedDeps>(stdout)
-        if (!newOutdatedDeps) {
-          return
-        }
-        devtoolsEventClient.emit('outdated-deps-read', {
-          outdatedDeps: newOutdatedDeps,
-        })
-        resolve(newOutdatedDeps)
-      }
+      const newOutdatedDeps = stdout ? tryParseJson<OutdatedDeps>(stdout) : null
+      if (newOutdatedDeps) {
+        devtoolsEventClient.emit('outdated-deps-read', {
+          outdatedDeps: newOutdatedDeps,
+        })
+      }
+      resolve(newOutdatedDeps)
     })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

export const emitOutdatedDeps = async () => {
  return await new Promise<OutdatedDeps | null>((resolve) => {
    const packageManager = detectPackageManager()
    const outdatedCommand = getOutdatedCommand(packageManager)

    exec(outdatedCommand, (_, stdout) => {
      // outdated commands exit with code 1 if there are outdated packages, but still output valid JSON
      const newOutdatedDeps = stdout ? tryParseJson<OutdatedDeps>(stdout) : null
      if (newOutdatedDeps) {
        devtoolsEventClient.emit('outdated-deps-read', {
          outdatedDeps: newOutdatedDeps,
        })
      }
      resolve(newOutdatedDeps)
    })
  })
}
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { exec } from 'node:child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🪛 OpenGrep (1.25.0)

[ERROR] 167-179: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/package-manager.ts` around lines 162 -
181, Update emitOutdatedDeps so its exec callback always settles the returned
promise, including when stdout is empty or JSON parsing fails. Preserve emitting
outdated-deps-read and returning parsed data for valid JSON, but resolve with
null for no output or invalid output so callers awaiting outdatedDeps cannot
hang.
packages/devtools-bundler-core/src/remove-devtools.ts (2)

107-138: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== outline remove-devtools.ts ==\n'
ast-grep outline packages/devtools-bundler-core/src/remove-devtools.ts --view expanded || true

printf '\n== relevant tests ==\n'
rg -n "removeDevtools|remove devtools|devtools" packages/devtools-bundler-core -g '*.{test,spec}.{ts,js,tsx,jsx}' -A 3 -B 3 || true

printf '\n== file excerpt ==\n'
sed -n '1,240p' packages/devtools-bundler-core/src/remove-devtools.ts

Repository: TanStack/devtools

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== remove-devtools.ts 100-185 ==\n'
sed -n '100,185p' packages/devtools-bundler-core/src/remove-devtools.ts

printf '\n== remove-devtools.test.ts 285-360 ==\n'
sed -n '285,360p' packages/devtools-bundler-core/src/remove-devtools.test.ts

printf '\n== remove-devtools.test.ts 575-640 ==\n'
sed -n '575,640p' packages/devtools-bundler-core/src/remove-devtools.test.ts

Repository: TanStack/devtools

Length of output: 4921


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== default import coverage in remove-devtools.test.ts ==\n'
rg -n "ImportDefaultSpecifier|default import|import .* from '`@tanstack`|import .* from \".*devtools" packages/devtools-bundler-core/src/remove-devtools.test.ts -A 2 -B 2 || true

printf '\n== expression-context coverage ==\n'
rg -n "LogicalExpression|ConditionalExpression|ParenthesizedExpression|&&|\\?" packages/devtools-bundler-core/src/remove-devtools.test.ts -A 2 -B 2 || true

Repository: TanStack/devtools

Length of output: 3535


Preserve valid syntax when stripping devtools JSX and imports.

  • packages/devtools-bundler-core/src/remove-devtools.ts:132-136s.remove() is only safe for JSX children. In expression positions like cond && <TanStackDevtools />, ternaries, or variable initializers, it leaves dangling operators / invalid JS; emit null for non-JSX contexts and only delete when the element is actually inside JSX.
  • packages/devtools-bundler-core/src/remove-devtools.ts:149-173 — rebuilding the remainder as import { ... } from ... drops the original specifier kind. If a partial removal leaves a default import, this turns it into a named import and breaks the binding; preserve the import clause shape when rewriting.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/remove-devtools.ts` around lines 107 -
138, The JSX removal logic in the devtools stripping pass must preserve valid
JavaScript syntax: update the `walk` callback around `parentNode` so
`s.remove()` is used only when the matched element is an actual JSX child, while
expression contexts such as logical expressions, ternaries, and initializers are
replaced with `null`. Also update the import-rebuilding logic in the subsequent
import cleanup section to preserve each import’s original default, namespace, or
named specifier shape instead of always emitting a named import.

140-175: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files 'packages/devtools-bundler-core/**' | sed -n '1,200p'
printf '\n--- remove-devtools.ts outline ---\n'
ast-grep outline packages/devtools-bundler-core/src/remove-devtools.ts --view expanded || true
printf '\n--- search tests/references ---\n'
rg -n "remove-devtools|plugin imports|ImportDefaultSpecifier|ImportSpecifier|mixed" packages/devtools-bundler-core -S

Repository: TanStack/devtools

Length of output: 3056


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- remove-devtools.ts (relevant section) ---'
nl -ba packages/devtools-bundler-core/src/remove-devtools.ts | sed -n '130,190p'

printf '\n%s\n' '--- tests in package ---'
git ls-files 'packages/devtools-bundler-core/**' | rg 'test|spec'

printf '\n%s\n' '--- search for import-shape coverage ---'
rg -n "import .*Default|ImportDefaultSpecifier|mixed|named import|remove plugin imports|remove-devtools" packages/devtools-bundler-core -S

Repository: TanStack/devtools

Length of output: 241


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

path = Path('packages/devtools-bundler-core/src/remove-devtools.ts')
lines = path.read_text().splitlines()
for start, end, title in [(135, 180, 'remove-devtools.ts'), (285, 360, 'remove-devtools.test.ts')]:
    print(f'--- {title} {start}-{end} ---')
    test_path = Path('packages/devtools-bundler-core/src/remove-devtools.test.ts') if 'test' in title else path
    for i, line in enumerate(test_path.read_text().splitlines(), start=1):
        if start <= i <= end:
            print(f'{i:4d}: {line}')
PY

Repository: TanStack/devtools

Length of output: 3895


Preserve default specifiers when rebuilding imports
If a default import survives pruning, this branch rewrites import Default, { PluginA } from 'pkg' as import { Default, PluginA } from 'pkg', which changes the import meaning. Rebuild default and named specifiers separately.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/remove-devtools.ts` around lines 140 -
175, The import-rebuild branch in the Pass 3 walker must preserve surviving
default imports. Update the logic around toRemove, remaining, and specTexts to
separate ImportDefaultSpecifier from named specifiers and reconstruct the
statement with the default specifier before the named-specifier block,
preserving valid import semantics for mixed imports.
packages/devtools-bundler-core/src/utils.test.ts (1)

264-289: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the implementation and the relevant tests.
git ls-files 'packages/devtools-bundler-core/src/**' | sed -n '1,200p'
echo '--- utils test outline ---'
ast-grep outline packages/devtools-bundler-core/src/utils.test.ts --view expanded
echo '--- search handleDevToolsRequest ---'
rg -n "handleDevToolsRequest|__tsd/open-source|source=" packages/devtools-bundler-core/src

Repository: TanStack/devtools

Length of output: 11154


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- utils.ts (handleDevToolsRequest) ---'
sed -n '1,120p' packages/devtools-bundler-core/src/utils.ts

echo '--- call sites / middleware context ---'
rg -n "handleDevToolsRequest\(" packages/devtools-bundler-core/src
rg -n "next\(\)|res\.end\(|res\.writeHead\(" packages/devtools-bundler-core/src/utils.ts packages/devtools-bundler-core/src/*.ts

Repository: TanStack/devtools

Length of output: 5830


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- utils.ts tail ---'
sed -n '120,180p' packages/devtools-bundler-core/src/utils.ts

echo '--- integration / wiring ---'
rg -n "handleDevToolsRequest|onOpenSource|__tsd/open-source|__tsd/console-pipe" packages/devtools-bundler-core/src packages/devtools-bundler-core -g '!**/*.test.ts'

Repository: TanStack/devtools

Length of output: 6353


Missing or malformed source should end the request
In packages/devtools-bundler-core/src/utils.ts, the __tsd/open-source branch returns early on absent or malformed source without calling next() or sending a response. That leaves the request open; return a 4xx or otherwise end it here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-bundler-core/src/utils.test.ts` around lines 264 - 289,
Update the __tsd/open-source handling in handleDevToolsRequest so missing or
malformed source values terminate the request with an appropriate 4xx response
instead of returning without action. Preserve the existing behavior for valid
source values and ensure both invalid cases no longer invoke the normal callback
flow or leave the response open.
packages/devtools-rspack/src/plugin.ts (2)

140-146: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate relevant files
git ls-files 'packages/devtools-rspack/src/plugin.ts' 'packages/devtools-rspack/src/**/*.ts' 'packages/**/plugin.test.ts' 'packages/**/devtools-bundler-core/**' | sed -n '1,200p'

echo '--- plugin.ts outline ---'
ast-grep outline packages/devtools-rspack/src/plugin.ts --view expanded || true

echo '--- search for setDevtoolsConnection/setDevServerOrigin/devtoolsEventClient ---'
rg -n "setDevtoolsConnection|setDevServerOrigin|getDevServerOrigin|devtoolsEventClient|wirePackageManager|on\\(" packages/devtools-rspack packages -g '!**/dist/**' -g '!**/build/**' | sed -n '1,220p'

echo '--- test references ---'
rg -n "getDevServerOrigin|setDevtoolsConnection|setDevServerOrigin|multi-compiler|compiler" packages/devtools-rspack -g '*test.ts' -g '*spec.ts' | sed -n '1,220p'

Repository: TanStack/devtools

Length of output: 25531


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the plugin and tests in manageable slices.
wc -l packages/devtools-rspack/src/plugin.ts packages/devtools-rspack/src/plugin.test.ts 2>/dev/null || true

echo '--- plugin.ts 1-260 ---'
sed -n '1,260p' packages/devtools-rspack/src/plugin.ts

echo '--- plugin.ts 260-520 ---'
sed -n '260,520p' packages/devtools-rspack/src/plugin.ts

echo '--- plugin.test.ts ---'
sed -n '1,260p' packages/devtools-rspack/src/plugin.test.ts

Repository: TanStack/devtools

Length of output: 21056


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect only the files needed for the claim.
for f in packages/devtools-rspack/src/plugin.ts packages/devtools-rspack/src/plugin.test.ts; do
  echo "### $f"
  nl -ba "$f" | sed -n '1,260p'
  echo
done

Repository: TanStack/devtools

Length of output: 238


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find local sources for the shared connection / event-client implementations.
git ls-files 'packages/**/src/**/*.ts' | rg 'devtools-bundler-core|devtoolsEventClient|setDevtoolsConnection|setDevServerOrigin|getDevServerOrigin|connection' -n || true

echo '--- candidate files ---'
fd -H -a 'connection|event|devtools' packages -t f | sed -n '1,200p'

Repository: TanStack/devtools

Length of output: 1990


🌐 Web query:

"@tanstack/devtools-bundler-core setDevtoolsConnection devtoolsEventClient singleton"

💡 Result:

The TanStack Devtools architecture relies on a singleton pattern for the EventClient, which serves as the primary mechanism for plugin communication [1][2][3]. 1. EventClient as a Singleton: The EventClient is designed to be instantiated once per plugin as a module-level singleton [2]. For example, library authors create a typed client by extending EventClient and exporting a single instance, such as export const myPluginClient = new MyEventClient() [2]. This ensures consistent communication channel registration for the lifetime of the plugin [2]. 2. Connection Management (setDevtoolsConnection / Internal Handshake): There is no direct public API named setDevtoolsConnection for users to manually manage the bus. Instead, connection is automated [4]. When an EventClient is initialized, it enters a connection lifecycle: - Upon the first emit() call, the client automatically attempts to discover the event bus by dispatching a tanstack-connect event [1][4]. - It waits for a tanstack-connect-success response [1][4]. - The client includes a retry mechanism (defaulting to 5 attempts every 300ms) to establish this connection [2][4]. - Events emitted before the connection is established are queued and flushed once the handshake succeeds [2][4]. 3. Implementation Details: - Server Bridge: In isolated environments (like server-side runtimes) that lack a shared global EventTarget, the EventClient automatically upgrades to use WebSockets or SSE for communication with the devtools server [5][4]. - Production Safety: By default, the root import of @tanstack/devtools-event-client is designed to be a no-op in production builds [6]. However, improper implementation (e.g., eager instantiation in core libraries without environment guards) can lead to the client being bundled in production [7]. Best practice involves guarding the instantiation with process.env.NODE_ENV !== 'production' [2][7]. The core Devtools shell (provided by @tanstack/devtools) handles the orchestration of the UI and the underlying EventBus [1][3]. Users interact with this primarily through configuration props (eventBusConfig) on the TanStackDevtools component rather than direct method calls on a connection singleton [8].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- packages/devtools-bundler-core/src/connection.ts ---'
sed -n '1,220p' packages/devtools-bundler-core/src/connection.ts

echo '--- devtoolsEventClient definition/imports in repo ---'
rg -n "export const devtoolsEventClient|new .*EventClient|class .*EventClient|on\\('mounted'|on\\('install-devtools'|on\\('add-plugin-to-devtools'|on\\('bump-package-version'" packages -g '!**/dist/**' -g '!**/build/**' | sed -n '1,220p'

echo '--- devtools-bundler-core connection tests ---'
sed -n '1,220p' packages/devtools-bundler-core/src/connection.test.ts

Repository: TanStack/devtools

Length of output: 8009


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for p in [
    Path('packages/devtools-bundler-core/src/connection.ts'),
    Path('packages/devtools-bundler-core/src/connection.test.ts'),
]:
    print(f'### {p}')
    text = p.read_text()
    for i, line in enumerate(text.splitlines(), 1):
        print(f'{i:4}: {line}')
    print()
PY

Repository: TanStack/devtools

Length of output: 2756


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the devtools client source if it exists in this repo.
git ls-files 'packages/**/src/**/*.ts' 'packages/**/src/**/*.tsx' | rg 'devtools-client|event-client|event-bus-client' | sed -n '1,240p'

Repository: TanStack/devtools

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for p in [
    Path('packages/devtools-bundler-core/src/connection.ts'),
    Path('packages/devtools-bundler-core/src/connection.test.ts'),
]:
    print(f'### {p}')
    for i, line in enumerate(p.read_text().splitlines(), 1):
        print(f'{i:4}: {line}')
    print()
PY

Repository: TanStack/devtools

Length of output: 2756


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the local EventClient skill docs that describe singleton/connection behavior.
sed -n '1,260p' packages/event-bus-client/skills/devtools-event-client/SKILL.md

Repository: TanStack/devtools

Length of output: 9361


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "singleton|on\\(suffix|connection|lifecycle|enabled" packages/event-bus-client/skills/devtools-event-client/SKILL.md | sed -n '1,220p'

Repository: TanStack/devtools

Length of output: 2730


Scope the shared devtools state per compiler. setDevtoolsConnection/setDevServerOrigin mutate module-level state, and wirePackageManager() registers handlers on the shared devtoolsEventClient singleton every time apply() runs. In a multi-compiler setup, one instance can overwrite another’s origin/connection and duplicate event handlers will fire twice. Add per-compiler scoping or cleanup/guard the registrations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-rspack/src/plugin.ts` around lines 140 - 146, Scope the
shared devtools connection/origin state and event registrations to each compiler
instance in the plugin’s apply flow. Update the
setDevtoolsConnection/setDevServerOrigin usage and wirePackageManager
registration so repeated apply() calls neither overwrite another compiler’s
state nor attach duplicate handlers; add appropriate per-compiler cleanup or
registration guards.

151-153: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the plugin and relevant tests
git ls-files packages/devtools-rspack/src/plugin.ts packages/devtools-rspack/src/plugin.test.ts

# Show the relevant sections with line numbers
sed -n '1,260p' packages/devtools-rspack/src/plugin.ts | cat -n
printf '\n--- TESTS ---\n'
sed -n '1,260p' packages/devtools-rspack/src/plugin.test.ts | cat -n

Repository: TanStack/devtools

Length of output: 16716


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for the package-manager wiring and any NODE_ENV-related tests/usages
rg -n "wirePackageManager|NODE_ENV|CI|mode === 'development'|isDev|setDevtoolsConnection|getDevServerOrigin|setDevServerOrigin" packages/devtools-rspack/src

Repository: TanStack/devtools

Length of output: 2121


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read just the relevant plugin region to understand the control flow.
sed -n '120,180p' packages/devtools-rspack/src/plugin.ts | cat -n

# Check whether tests cover the NODE_ENV gate or only development mode generally.
sed -n '1,220p' packages/devtools-rspack/src/plugin.test.ts | cat -n

Repository: TanStack/devtools

Length of output: 7544


Remove the extra NODE_ENV gate
compiler.options.mode === 'development' already limits this to dev builds, but process.env.NODE_ENV is independent and can block wirePackageManager() in valid dev runs. The current tests only cover mode, not this env-dependent branch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/devtools-rspack/src/plugin.ts` around lines 151 - 153, Remove the
process.env.NODE_ENV === 'development' condition from the wirePackageManager
invocation guard in the plugin, retaining only the CI check and compiler
development-mode gating already provided by the surrounding logic. Ensure
wirePackageManager runs for valid non-CI development builds regardless of
NODE_ENV.

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