feat: add @tanstack/devtools-rspack (1:1 Vite parity) + shared @tanstack/devtools-bundler-core#484
feat: add @tanstack/devtools-rspack (1:1 Vite parity) + shared @tanstack/devtools-bundler-core#484AlemTuzlak wants to merge 16 commits into
Conversation
…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
📝 WalkthroughWalkthroughThis 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. ChangesShared bundler core
Rspack integration
Vite and example integration
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
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
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.
|
|
View your CI Pipeline Execution ↗ for commit f389d2b
☁️ Nx Cloud last updated this comment at |
More templates
@tanstack/angular-devtools
@tanstack/devtools
@tanstack/devtools-a11y
@tanstack/devtools-bundler-core
@tanstack/devtools-client
@tanstack/devtools-rspack
@tanstack/devtools-ui
@tanstack/devtools-utils
@tanstack/devtools-vite
@tanstack/devtools-event-bus
@tanstack/devtools-event-client
@tanstack/preact-devtools
@tanstack/react-devtools
@tanstack/solid-devtools
@tanstack/svelte-devtools
@tanstack/vue-devtools
commit: |
There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (6)
packages/devtools-bundler-core/src/utils.test.ts (1)
209-325: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for console-pipe branches.
The new suite exercises
open-sourceand the generic__tsdJSON path, but__tsd/console-pipe/sse,__tsd/console-pipe/server, and__tsd/console-pipebranches (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 winConsider typing
compilerinstead ofany.The entire plugin (
compiler,server,middlewares) is typedany, sotscprovides no protection against Rspack API drift (e.g., between the^1.0.0peer range and the current 2.x major) oncompiler.options.devServer,compiler.hooks.done.tap, etc. Typing against@rspack/core'sCompiler/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 winCoverage gap: package-manager wiring and event-bus startup are untested.
The suite verifies loader-rule injection and
setupMiddlewarespresence, but nothing exerciseswirePackageManager(compiler.hooks.done.tap/watchRun.tapPromisecalls,devtoolsEventClientevent handling) or theServerEventBusstartup path triggered insidesetupMiddlewares. GivenmockCompileralready stubs these hooks asvi.fn(), asserting they're tapped (and thatdevtoolsEventClientlisteners 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 winPrefer the documented
this.targetloader-context property.Line 167 reaches into
this._compiler?.options?.targetto read the build target. Rspack's loader API documentsthis.targetdirectly on the loader context for exactly this purpose, avoiding a dependency on the internal_compilerobject 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 winInconsistent optionality of
enabledacross sub-configs.
enhancedLogs.enabledandinjectSource.enabledare required, forcing consumers to passenabled: trueeven when they only want to setignore— contradicting the@default truedoc and inconsistent withconsolePiping.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 winMatchers are recompiled on every call instead of being cached.
patterns.map(...)re-runspicomatch(pattern)compilation for every invocation ofmatcher, even thoughignore.files/ignore.componentsare 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 perpatternsarray 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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.mdexamples/react-rspack/index.htmlexamples/react-rspack/package.jsonexamples/react-rspack/rspack.config.mjsexamples/react-rspack/src/App.tsxexamples/react-rspack/src/main.tsxexamples/react-rspack/tsconfig.jsonpackages/devtools-bundler-core/eslint.config.jspackages/devtools-bundler-core/package.jsonpackages/devtools-bundler-core/src/ast-utils.test.tspackages/devtools-bundler-core/src/ast-utils.tspackages/devtools-bundler-core/src/connection.test.tspackages/devtools-bundler-core/src/connection.tspackages/devtools-bundler-core/src/devtools-packages.test.tspackages/devtools-bundler-core/src/devtools-packages.tspackages/devtools-bundler-core/src/editor.test.tspackages/devtools-bundler-core/src/editor.tspackages/devtools-bundler-core/src/enhance-logs.test.tspackages/devtools-bundler-core/src/enhance-logs.tspackages/devtools-bundler-core/src/index.tspackages/devtools-bundler-core/src/inject-plugin.test.tspackages/devtools-bundler-core/src/inject-plugin.tspackages/devtools-bundler-core/src/inject-source.test.tspackages/devtools-bundler-core/src/inject-source.tspackages/devtools-bundler-core/src/matcher.test.tspackages/devtools-bundler-core/src/matcher.tspackages/devtools-bundler-core/src/normalize-path.tspackages/devtools-bundler-core/src/offset-to-loc.test.tspackages/devtools-bundler-core/src/offset-to-loc.tspackages/devtools-bundler-core/src/package-manager.tspackages/devtools-bundler-core/src/remove-devtools.test.tspackages/devtools-bundler-core/src/remove-devtools.tspackages/devtools-bundler-core/src/runtime-bridge.test.tspackages/devtools-bundler-core/src/runtime-bridge.tspackages/devtools-bundler-core/src/types.tspackages/devtools-bundler-core/src/utils.test.tspackages/devtools-bundler-core/src/utils.tspackages/devtools-bundler-core/src/virtual-console.test.tspackages/devtools-bundler-core/src/virtual-console.tspackages/devtools-bundler-core/tsconfig.docs.jsonpackages/devtools-bundler-core/tsconfig.jsonpackages/devtools-bundler-core/vite.config.tspackages/devtools-rspack/CHANGELOG.mdpackages/devtools-rspack/README.mdpackages/devtools-rspack/eslint.config.jspackages/devtools-rspack/package.jsonpackages/devtools-rspack/src/index.test.tspackages/devtools-rspack/src/index.tspackages/devtools-rspack/src/loader.test.tspackages/devtools-rspack/src/loader.tspackages/devtools-rspack/src/plugin.test.tspackages/devtools-rspack/src/plugin.tspackages/devtools-rspack/tsconfig.docs.jsonpackages/devtools-rspack/tsconfig.jsonpackages/devtools-rspack/vite.config.tspackages/devtools-vite/package.jsonpackages/devtools-vite/src/plugin.tspackages/devtools-vite/tests/index.test.tspackages/devtools-vite/tests/utils.test.ts
💤 Files with no reviewable changes (1)
- packages/devtools-vite/tests/utils.test.ts
There was a problem hiding this comment.
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 winAdd coverage for console-pipe branches.
The new suite exercises
open-sourceand the generic__tsdJSON path, but__tsd/console-pipe/sse,__tsd/console-pipe/server, and__tsd/console-pipebranches (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 winConsider typing
compilerinstead ofany.The entire plugin (
compiler,server,middlewares) is typedany, sotscprovides no protection against Rspack API drift (e.g., between the^1.0.0peer range and the current 2.x major) oncompiler.options.devServer,compiler.hooks.done.tap, etc. Typing against@rspack/core'sCompiler/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 winCoverage gap: package-manager wiring and event-bus startup are untested.
The suite verifies loader-rule injection and
setupMiddlewarespresence, but nothing exerciseswirePackageManager(compiler.hooks.done.tap/watchRun.tapPromisecalls,devtoolsEventClientevent handling) or theServerEventBusstartup path triggered insidesetupMiddlewares. GivenmockCompileralready stubs these hooks asvi.fn(), asserting they're tapped (and thatdevtoolsEventClientlisteners 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 winPrefer the documented
this.targetloader-context property.Line 167 reaches into
this._compiler?.options?.targetto read the build target. Rspack's loader API documentsthis.targetdirectly on the loader context for exactly this purpose, avoiding a dependency on the internal_compilerobject 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 winInconsistent optionality of
enabledacross sub-configs.
enhancedLogs.enabledandinjectSource.enabledare required, forcing consumers to passenabled: trueeven when they only want to setignore— contradicting the@default truedoc and inconsistent withconsolePiping.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 winMatchers are recompiled on every call instead of being cached.
patterns.map(...)re-runspicomatch(pattern)compilation for every invocation ofmatcher, even thoughignore.files/ignore.componentsare 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 perpatternsarray 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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.mdexamples/react-rspack/index.htmlexamples/react-rspack/package.jsonexamples/react-rspack/rspack.config.mjsexamples/react-rspack/src/App.tsxexamples/react-rspack/src/main.tsxexamples/react-rspack/tsconfig.jsonpackages/devtools-bundler-core/eslint.config.jspackages/devtools-bundler-core/package.jsonpackages/devtools-bundler-core/src/ast-utils.test.tspackages/devtools-bundler-core/src/ast-utils.tspackages/devtools-bundler-core/src/connection.test.tspackages/devtools-bundler-core/src/connection.tspackages/devtools-bundler-core/src/devtools-packages.test.tspackages/devtools-bundler-core/src/devtools-packages.tspackages/devtools-bundler-core/src/editor.test.tspackages/devtools-bundler-core/src/editor.tspackages/devtools-bundler-core/src/enhance-logs.test.tspackages/devtools-bundler-core/src/enhance-logs.tspackages/devtools-bundler-core/src/index.tspackages/devtools-bundler-core/src/inject-plugin.test.tspackages/devtools-bundler-core/src/inject-plugin.tspackages/devtools-bundler-core/src/inject-source.test.tspackages/devtools-bundler-core/src/inject-source.tspackages/devtools-bundler-core/src/matcher.test.tspackages/devtools-bundler-core/src/matcher.tspackages/devtools-bundler-core/src/normalize-path.tspackages/devtools-bundler-core/src/offset-to-loc.test.tspackages/devtools-bundler-core/src/offset-to-loc.tspackages/devtools-bundler-core/src/package-manager.tspackages/devtools-bundler-core/src/remove-devtools.test.tspackages/devtools-bundler-core/src/remove-devtools.tspackages/devtools-bundler-core/src/runtime-bridge.test.tspackages/devtools-bundler-core/src/runtime-bridge.tspackages/devtools-bundler-core/src/types.tspackages/devtools-bundler-core/src/utils.test.tspackages/devtools-bundler-core/src/utils.tspackages/devtools-bundler-core/src/virtual-console.test.tspackages/devtools-bundler-core/src/virtual-console.tspackages/devtools-bundler-core/tsconfig.docs.jsonpackages/devtools-bundler-core/tsconfig.jsonpackages/devtools-bundler-core/vite.config.tspackages/devtools-rspack/CHANGELOG.mdpackages/devtools-rspack/README.mdpackages/devtools-rspack/eslint.config.jspackages/devtools-rspack/package.jsonpackages/devtools-rspack/src/index.test.tspackages/devtools-rspack/src/index.tspackages/devtools-rspack/src/loader.test.tspackages/devtools-rspack/src/loader.tspackages/devtools-rspack/src/plugin.test.tspackages/devtools-rspack/src/plugin.tspackages/devtools-rspack/tsconfig.docs.jsonpackages/devtools-rspack/tsconfig.jsonpackages/devtools-rspack/vite.config.tspackages/devtools-vite/package.jsonpackages/devtools-vite/src/plugin.tspackages/devtools-vite/tests/index.test.tspackages/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
TypeErroron 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 valuewill throw aTypeError(sincevalue === nullandArray.isArray(value)are both false forundefined).Adding a
typeof value === 'object'check ensures it safely skipsundefinedor 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)mutateslastIndexong/ypatterns, so a reusedRegExpcan 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-vitelog prefix.
addPluginToDevtoolsandinstallPackageare framework-agnostic functions in the shared bundler core, but their console messages always say[@tanstack/devtools-vite].devtools-rspack'swirePackageManagercalls 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
packageNamehere inpackages/devtools-bundler-core/src/package-manager.ts:75-136:getInstallCommandbuilds a command string with${packageName}andinstallPackagepasses it toexec. Both theinstall-devtoolsandbump-package-versionflows forward event payload values directly, so a crafted package name can inject shell syntax. UseexecFile/spawnwith 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 callsresolveinside theif (stdout)branch. Per npm's own docs,outdated --jsonproduces 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. SincewirePackageManager'smountedhandler doesawait outdatedDepsbefore emittingpackage-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.tsRepository: 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.tsRepository: 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 || trueRepository: TanStack/devtools
Length of output: 3535
Preserve valid syntax when stripping devtools JSX and imports.
packages/devtools-bundler-core/src/remove-devtools.ts:132-136—s.remove()is only safe for JSX children. In expression positions likecond && <TanStackDevtools />, ternaries, or variable initializers, it leaves dangling operators / invalid JS; emitnullfor 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 asimport { ... } 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 -SRepository: 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 -SRepository: 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}') PYRepository: TanStack/devtools
Length of output: 3895
Preserve default specifiers when rebuilding imports
If a default import survives pruning, this branch rewritesimport Default, { PluginA } from 'pkg'asimport { 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/srcRepository: 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/*.tsRepository: 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
sourceshould end the request
Inpackages/devtools-bundler-core/src/utils.ts, the__tsd/open-sourcebranch returns early on absent or malformedsourcewithout callingnext()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.tsRepository: 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 doneRepository: 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-coresetDevtoolsConnection 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 namedsetDevtoolsConnectionfor users to manually manage the bus. Instead, connection is automated [4]. When an EventClient is initialized, it enters a connection lifecycle: - Upon the firstemit()call, the client automatically attempts to discover the event bus by dispatching atanstack-connectevent [1][4]. - It waits for atanstack-connect-successresponse [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 globalEventTarget, 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-clientis 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 withprocess.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:
- 1: https://tanstack.com/devtools/latest/docs/architecture
- 2: https://github.com/TanStack/devtools/blob/main/packages/event-bus-client/skills/devtools-event-client/SKILL.md
- 3: https://tanstack.com/devtools/latest/docs/overview
- 4: https://tanstack.com/devtools/latest/docs/event-system
- 5: 958c683
- 6: https://tanstack.com/devtools/latest/docs/building-custom-plugins
- 7: TanStack/form#2132
- 8: https://tanstack.com/devtools/latest/docs/configuration
🏁 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.tsRepository: 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() PYRepository: 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() PYRepository: 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.mdRepository: 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/setDevServerOriginmutate module-level state, andwirePackageManager()registers handlers on the shareddevtoolsEventClientsingleton every timeapply()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 -nRepository: 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/srcRepository: 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 -nRepository: TanStack/devtools
Length of output: 7544
Remove the extra
NODE_ENVgate
compiler.options.mode === 'development'already limits this to dev builds, butprocess.env.NODE_ENVis independent and can blockwirePackageManager()in valid dev runs. The current tests only covermode, 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.
🎯 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-corepackage 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 sharedhandleDevToolsRequestmiddleware handler, the config type, and a small dev-state singleton (connection.ts). Zerovitedependency (localnormalizePath,node:httptypes).@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 viasetupMiddlewares, package-manager events).Changed
@tanstack/devtools-vite— refactored to consume@tanstack/devtools-bundler-core. Public API and behavior unchanged (devtools,defineDevtoolsConfig,TanStackDevtoolsViteConfig,ConsoleLevelall still exported;TanStackDevtoolsViteConfigis now an alias of the sharedTanStackDevtoolsConfig).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
examples/react-rspackapp was booted withrspack serveand manually verified:GET /__tsd/open-source→200(thesetupMiddlewaresauto-wiring works against@rspack/dev-server), source injection reaches served modules, and a production build strips the devtools code.node_moduleslayouts), and the shared core. Counts: bundler-core 197, rspack 15, vite 21.Known caveats / follow-ups
devtools-vite); Rspack configs must berspack.config.mjsor useawait import()(documented in the README). A dual CJS build was considered and deliberately deferred.server.environments[].hotequivalent; the code-injection half runs for server-target builds but is inert under Rspack'smodule.hot. Documented.handleDevToolsRequest(relocated verbatim fromdevtools-vite) — e.g. a malformed__tsd/open-sourcerequest not ending the response — are left as-is to avoid changing the shipping Vite behavior; noted for a future cleanup.✅ Checklist
test:lib/test:types/test:eslint+ fullpnpm buildall green).🚀 Release Impact
@tanstack/devtools-bundler-coreminor,@tanstack/devtools-rspackminor,@tanstack/devtools-vitepatch).Summary by CodeRabbit
New Features
Documentation
Bug Fixes