feat(angular): angular adapter and utils#368
feat(angular): angular adapter and utils#368riccardoperra wants to merge 22 commits intoTanStack:mainfrom
Conversation
|
a9d3f16 to
3349a19
Compare
|
| Command | Status | Duration | Result |
|---|---|---|---|
nx affected --targets=test:eslint,test:sherif,t... |
❌ Failed | 2m 55s | View ↗ |
☁️ Nx Cloud last updated this comment at 2026-03-11 12:22:30 UTC
More templates
@tanstack/angular-devtools
@tanstack/devtools
@tanstack/devtools-client
@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/vue-devtools
commit: |
d1cafc8 to
3f27799
Compare
d82d6a2 to
efa3afd
Compare
5abbcba to
e5e5ec1
Compare
|
@AlemTuzlak integration is done. I probably need pipelines so I can check with pkg.pr.new if everything works with some other devtools like table which is not published yet (unfortunately I always have a lot of issues running pnpm link with angular 😄) |
|
@riccardoperra mind just fixing conflicts and we can get this merged |
Add the @tanstack/angular-devtools package with: - TanStackDevtoolsComponent: standalone Angular component that wraps TanStackDevtoolsCore - Plugin system supporting Angular components rendered into devtools containers - Type definitions for Angular-specific plugin and init configuration - Build config using @analogjs/vite-plugin-angular for AOT compilation
- Remove @analogjs/vite-plugin-angular from build (produces empty chunks for library builds). Use plain esbuild via Vite instead - Angular AOT compilation happens at consumer's build time. - Upgrade Angular dev deps from ^19 to ^20 for TypeScript 5.9 compat. - Remove @angular/build dep (only needed by analogjs plugin). - Add @tanstack/angular-devtools workspace override to root.
- Fix array-type ESLint rule in angular-devtools - Fix import/consistent-type-specifier-style in devtools-utils angular - Sort peerDependencies alphabetically (sherif) - Remove unused devDependencies flagged by knip - Remove angularCompilerOptions from tsconfig (not using ngc)
Since angular-devtools already run the code within afterNextRender in browser env, we don't like to wrap the panel returned from utils
c2ffbb9 to
084d4b1
Compare
084d4b1 to
c52c310
Compare
|
@AlemTuzlak done! |
|
Seems some stuff is failing |
e62e4f7 to
c728cfe
Compare
|
@AlemTuzlak fixed, i tested in my local env and all 28 tasks succeded Anyway, I noticed while testing my examples that warning. I saw "sideEffects" false in core but the generated dist contains some
|
📝 WalkthroughWalkthroughAdds first-class Angular support: docs, three Angular examples, an Changes
Sequence Diagram(s)sequenceDiagram
participant App as Angular App
participant Provider as provideTanStackDevtools
participant CoreComp as TanStackDevtools (Angular)
participant CoreLib as TanStackDevtoolsCore
participant Plugins as Plugins Signal
participant EventBus as EventClient
participant Renderer as Component Renderer
App->>Provider: call provideTanStackDevtools(withDevtoolsFn)
Provider->>Plugins: initialize TANSTACK_DEVTOOLS_PLUGINS signal
Provider->>CoreComp: dynamic import & attach TanStackDevtools component
CoreComp->>CoreComp: after first render
CoreComp->>Plugins: read plugins signal
CoreComp->>Renderer: convertPlugin / resolve render (sync or async)
Renderer->>CoreComp: mount Angular component or functional render
CoreComp->>CoreLib: create TanStackDevtoolsCore with converted plugins
CoreLib->>CoreLib: mount core into host element
App->>EventBus: emit "counter-state"
EventBus->>CoreComp: deliver event to plugin subscribers
CoreComp->>Renderer: update component inputs / props
Renderer->>CoreComp: reflect UI changes in plugin panels
App->>Plugins: update plugins signal (add/remove)
CoreComp->>Renderer: destroyComponentsInContainer(old)
CoreComp->>Renderer: render new plugins
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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 Tip Migrating from UI to YAML configuration.Use the |
There was a problem hiding this comment.
Actionable comments posted: 4
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (19)
examples/angular/with-devtools/src/index.html-5-5 (1)
5-5:⚠️ Potential issue | 🟡 MinorTitle does not match the example name.
The title is "Basic" but this example is "with-devtools". This appears to be a copy-paste artifact from another example.
📝 Suggested fix
- <title>Basic</title> + <title>With Devtools</title>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/with-devtools/src/index.html` at line 5, The page title in index.html is incorrect ("Basic"); update the <title> element in the file so it matches the example name ("with-devtools" or a human-friendly variant like "With Devtools") to avoid the copy-paste artifact and ensure the example metadata/title reflects this example.examples/angular/with-devtools/.editorconfig-11-17 (1)
11-17:⚠️ Potential issue | 🟡 MinorRemove the redundant IntelliJ-specific TypeScript setting.
The
ij_typescript_use_double_quotes = falsesetting at line 13 is redundant with the standardquote_type = singlesetting above it. Since this.editorconfigsetsroot = true, it's independent of the parent config and doesn't need to maintain consistency with it. However, the duplicate setting should be removed for clarity:Remove redundant IntelliJ setting
[*.ts] quote_type = single -ij_typescript_use_double_quotes = false🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/with-devtools/.editorconfig` around lines 11 - 17, Remove the redundant IntelliJ-specific TypeScript setting by deleting the line "ij_typescript_use_double_quotes = false" from the file; keep the standard "[*.ts]" block with "quote_type = single" as the single source of truth for quote style and remove only the "ij_typescript_use_double_quotes" entry so there is no duplicate configuration.examples/angular/panel/src/index.html-5-5 (1)
5-5:⚠️ Potential issue | 🟡 MinorUpdate the page title to match the panel example.
<title>Basic</title>looks like a copy/paste mismatch for this panel app and can be confusing in browser tabs.Suggested fix
- <title>Basic</title> + <title>Panel</title>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/panel/src/index.html` at line 5, Update the HTML title in examples/angular/panel/src/index.html: replace the current <title>Basic</title> with a more accurate title reflecting the app (for example "Angular Panel Example" or "Panel") so the browser tab and metadata correctly identify this panel demo; modify the <title> tag content only to match the panel example name.examples/angular/panel/.vscode/launch.json-9-16 (1)
9-16:⚠️ Potential issue | 🟡 Minor
preLaunchTaskreferences npm but project uses pnpm.The
preLaunchTaskvalues use"npm: start"and"npm: test", but thepackage.jsonspecifiespnpm@10.24.0as the package manager. VS Code may not recognize these tasks correctly. Consider updating to match the project's package manager or ensure VS Code's task auto-detection handles this.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/panel/.vscode/launch.json` around lines 9 - 16, Update the VS Code launch configuration so the preLaunchTask matches the project's package manager: replace the "preLaunchTask" values referencing "npm: start" and "npm: test" with the corresponding "pnpm: start" and "pnpm: test" (or ensure tasks with those names exist), targeting the entries that define the Chrome launches ("name": "ng serve"/implicit block and "name": "ng test") so VS Code will run the correct pnpm tasks before launching.examples/angular/with-devtools/angular.json-12-13 (1)
12-13:⚠️ Potential issue | 🟡 MinorProject name should match the example folder.
The project is named
"basic"but this configuration is in thewith-devtoolsexample directory. This appears to be a copy-paste oversight from another example. Consider renaming to"with-devtools"for consistency and to avoid confusion.📝 Proposed fix
"projects": { - "basic": { + "with-devtools": { "projectType": "application",Also update build targets on lines 61 and 64:
- "buildTarget": "basic:build:production" + "buildTarget": "with-devtools:build:production" }, "development": { - "buildTarget": "basic:build:development" + "buildTarget": "with-devtools:build:development"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/with-devtools/angular.json` around lines 12 - 13, The project key in the angular.json "projects" object is incorrectly named "basic" but should match the example folder; rename the project key from "basic" to "with-devtools" and update every reference to that key inside the file (notably the build and serve targets currently referencing "basic") so they point to "with-devtools" instead; ensure target entries such as the "build" and "serve" (and any "architect" or "targets" blocks) are updated to use the new project name so configuration and CLI commands remain consistent.examples/angular/panel/README.md-1-3 (1)
1-3:⚠️ Potential issue | 🟡 MinorUpdate README title to match the panel example directory.
The README title says "# Basic" but this file is in the
panelexample directory. The title should be "# Panel" for consistency with the directory name.📝 Proposed fix
-# Basic +# Panel🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/panel/README.md` around lines 1 - 3, The README title currently reads "Basic" but should match the example directory name; update the top-level heading in the README.md by replacing the heading text "Basic" with "Panel" so the file title reads "# Panel" for consistency with the panel example directory.docs/framework/angular/guides/custom-plugins.md-178-178 (1)
178-178:⚠️ Potential issue | 🟡 MinorMinor grammar: "built in" should be hyphenated.
📝 Suggested fix
-Both the `TanStackDevtools` and the TanStack `EventClient` come with built in debug mode which will log to the console the emitted event as well as the EventClient status. +Both the `TanStackDevtools` and the TanStack `EventClient` come with built-in debug mode which will log to the console the emitted event as well as the EventClient status.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/angular/guides/custom-plugins.md` at line 178, The phrase "built in" in the sentence referencing TanStackDevtools and the TanStack EventClient should be hyphenated; update the sentence so it reads "built-in debug mode" where "built in" appears (affecting the line mentioning TanStackDevtools and EventClient) to correct the grammar.examples/angular/with-devtools/README.md-1-3 (1)
1-3:⚠️ Potential issue | 🟡 MinorREADME title mismatch with project directory.
The title says "Basic" but this file is in the
with-devtoolsexample directory. This appears to be a copy-paste oversight from the basic example.📝 Suggested fix
-# Basic +# With Devtools -This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.0. +This project was generated using [Angular CLI](https://github.com/angular/angular-cli).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/with-devtools/README.md` around lines 1 - 3, The README title currently reads "Basic" which doesn't match this example's directory; update the top-level heading in examples/angular/with-devtools/README.md (the line containing "# Basic") to a correct, descriptive title such as "# with-devtools" or "# Angular with DevTools" so the file name and content reflect the example's purpose.docs/framework/angular/guides/custom-plugins.md-197-197 (1)
197-197:⚠️ Potential issue | 🟡 MinorMinor spelling: "Where as" should be "Whereas".
📝 Suggested fix
-Where as the EventClient's debug mode can be activated by: +Whereas the EventClient's debug mode can be activated by:🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/angular/guides/custom-plugins.md` at line 197, Replace the incorrect two-word phrase "Where as" with the correct single-word "Whereas" in the docs/framework/angular/guides/custom-plugins.md content to fix the minor spelling error.docs/framework/angular/guides/custom-plugins.md-213-216 (1)
213-216:⚠️ Potential issue | 🟡 MinorAdd language specifier to fenced code block.
The code block showing debug output should have a language specifier for consistent formatting.
📝 Suggested fix
-``` +```text [tanstack-devtools:client-bus] Initializing client event bus [tanstack-devtools:custom-devtools-plugin] Registered event to bus custom-devtools:counter-state ```🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/angular/guides/custom-plugins.md` around lines 213 - 216, The fenced code block showing debug output lacks a language specifier; update the block in docs/framework/angular/guides/custom-plugins.md around the debug output containing "[tanstack-devtools:client-bus] Initializing client event bus" and "[tanstack-devtools:custom-devtools-plugin] Registered event to bus custom-devtools:counter-state" by adding a language tag (e.g., ```text) after the opening backticks so the block becomes a fenced code block with the "text" specifier for consistent formatting.docs/framework/angular/guides/custom-plugins.md-122-135 (1)
122-135:⚠️ Potential issue | 🟡 MinorTemplate incorrectly calls getter with parentheses.
In the template at line 125,
stateHistory()is called with parentheses, butstateHistoryis defined as a getter (line 133-135), not a method. Getters should be accessed without parentheses.📝 Suggested fix
template: ` <div> <div>{{ state()?.count }}</div> - <div>{{ stateHistory() }}</div> + <div>{{ stateHistory }}</div> </div> `,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/angular/guides/custom-plugins.md` around lines 122 - 135, The template incorrectly invokes the getter stateHistory as a method; update the template to reference the getter without parentheses (use {{ stateHistory }} instead of {{ stateHistory() }}) so it matches the DevtoolPanel get stateHistory() accessor and returns the computed JSON string properly.docs/framework/angular/basic-setup.md-18-32 (1)
18-32:⚠️ Potential issue | 🟡 MinorFix import name mismatch in code example.
The import statement on line 20 imports
TanStackDevtoolsComponent, but theimportsarray on line 25 referencesTanStackDevtools. This inconsistency will cause a compilation error when users follow this documentation.📝 Proposed fix
```typescript import { Component } from '@angular/core' -import { TanStackDevtoolsComponent } from '@tanstack/angular-devtools' +import { TanStackDevtools } from '@tanstack/angular-devtools' `@Component`({ selector: 'app-root', standalone: true, imports: [TanStackDevtools],🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/angular/basic-setup.md` around lines 18 - 32, The import name in the example is inconsistent: the file imports TanStackDevtoolsComponent but the App component's standalone imports array references TanStackDevtools. Fix by making the imported symbol match the one used in the App component — either change the import to TanStackDevtools (recommended) or change the imports array to use TanStackDevtoolsComponent so the imported identifier and the imports entry are the same; update the import statement near the top that currently names TanStackDevtoolsComponent accordingly.docs/framework/angular/adapter.md-46-48 (1)
46-48:⚠️ Potential issue | 🟡 MinorMisleading text references non-existent
componentfield.Line 48 states the adapter "uses
component... instead ofrender", but the actual field name shown in the type definition and code examples isrender. This appears to be outdated or incorrect.📝 Proposed fix
-The Angular adapter uses `component` (an Angular component class reference) instead of `render` (a JSX element) in plugin definitions. Inputs are provided through the `inputs` field and bound to the component with `setInput()`, rather than being embedded directly in a JSX expression or passed via `v-bind`. +The Angular adapter uses `render` with an Angular component class reference (rather than a JSX element) in plugin definitions. Inputs are provided through the `inputs` field and bound to the component with `setInput()`, rather than being embedded directly in a JSX expression or passed via `v-bind`.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/angular/adapter.md` around lines 46 - 48, The sentence incorrectly refers to a non-existent `component` field; update the documentation text to reference the actual `render` field used in the adapter type and examples (e.g., change "uses `component` (an Angular component class reference) instead of `render`" to indicate that the adapter uses `render` and explain that inputs are provided via the `inputs` field and bound with `setInput()`), or if the implementation should instead use `component`, update the type/examples to match—ensure consistency between the docs and the symbols `render`, `inputs`, and `setInput()`.docs/framework/angular/adapter.md-38-45 (1)
38-45:⚠️ Potential issue | 🟡 MinorDocumentation table has incorrect type for
renderfield.The table states
renderhas typeType<any>, but the type definition above (line 31) shows it should beTanStackDevtoolsAngularPluginRender. The description should align with the actual type definition for accuracy.📝 Proposed fix
-| `render` | `Type<any>` | The Angular component class to render as the plugin panel content. | +| `render` | `TanStackDevtoolsAngularPluginRender` | The Angular component class, lazy component (via dynamic import), or custom function to render as the plugin panel content. |🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/angular/adapter.md` around lines 38 - 45, The docs table incorrectly types the render field as Type<any>; update the table entry for the `render` row to use the actual type name `TanStackDevtoolsAngularPluginRender` and adjust the description to reference that type (e.g., "The render property is a TanStackDevtoolsAngularPluginRender that defines the Angular component/class used for plugin panel content"); ensure the `render` column now matches the type alias `TanStackDevtoolsAngularPluginRender` used in the code.packages/angular-devtools/src/types.ts-44-62 (1)
44-62:⚠️ Potential issue | 🟡 MinorJSDoc example references
componentbut type usesrender.The JSDoc comment states "Each plugin should have a
component" and the example showscomponent: MyPluginComponent, but the actualTanStackDevtoolsAngularPlugintype at line 38 definesrender, notcomponent. This mismatch will confuse users.📝 Proposed fix to align JSDoc with actual type
/** * Array of plugins to be used in the devtools. - * Each plugin should have a `component` that is an Angular component class. + * Each plugin should have a `render` property that specifies an Angular component class or render function. * * Example: * ```typescript * `@Component`({ * template: `<tanstack-devtools [plugins]="plugins" />`, * imports: [TanStackDevtoolsComponent], * }) * class AppComponent { * plugins: TanStackDevtoolsAngularPlugin[] = [ - * { name: 'My Plugin', component: MyPluginComponent } + * { name: 'My Plugin', render: MyPluginComponent } * ]; * } * ``` */🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/angular-devtools/src/types.ts` around lines 44 - 62, The JSDoc for TanStackDevtoolsAngularInit and its example incorrectly refer to a `component` property while the actual type TanStackDevtoolsAngularPlugin defines `render`; update the JSDoc/example to use `render` (e.g., replace `{ name: 'My Plugin', component: MyPluginComponent }` with `{ name: 'My Plugin', render: MyPluginComponent }`) so the documentation matches the TanStackDevtoolsAngularPlugin type and avoids confusion.packages/devtools-utils/src/angular/panel.ts-53-57 (1)
53-57:⚠️ Potential issue | 🟡 MinorAdd error handling for the lazy-loaded panel.
When
CoreClassis a function returning a Promise (lazy loading), there's no.catch()handler. If the dynamic import fails, the error will be unhandled.Proposed fix
if (isConstructor) { mount(new CoreClass(inputs())) } else { CoreClass() .then((ResolvedCoreClass) => new ResolvedCoreClass(inputs())) .then(mount) + .catch((error) => { + console.error('[createAngularPanel] Failed to load panel:', error) + }) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/devtools-utils/src/angular/panel.ts` around lines 53 - 57, The lazy-load branch calling CoreClass() lacks error handling; wrap the promise chain starting at CoreClass() so rejections are caught and handled—add a .catch handler to the CoreClass().then(...).then(mount) chain that logs the error and/or invokes the existing mount/error handling flow (reference CoreClass, inputs(), and mount) to avoid unhandled promise rejections and surface failures during dynamic import.packages/angular-devtools/provider/with-devtools.ts-75-80 (1)
75-80:⚠️ Potential issue | 🟡 MinorUse
isPlatformBrowser()instead of string comparison for platform check.The
PLATFORM_IDvalue should be checked using Angular'sisPlatformBrowser()utility from@angular/commonrather than direct string comparison. The string'browser'is not the documented API contract.Proposed fix
Add to imports:
import { isPlatformBrowser } from '@angular/common'- if (inject(PLATFORM_ID) !== 'browser') { + if (!isPlatformBrowser(inject(PLATFORM_ID))) { return }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/angular-devtools/provider/with-devtools.ts` around lines 75 - 80, Replace the direct string comparison of PLATFORM_ID with Angular's isPlatformBrowser check: import isPlatformBrowser from '@angular/common' and change the platform guard in the provideEnvironmentInitializer block (where inject(PLATFORM_ID) is used) to use if (!isPlatformBrowser(inject(PLATFORM_ID))) return; so the environment check uses the documented API rather than comparing to the string 'browser'.packages/angular-devtools/provider/with-devtools.ts-91-103 (1)
91-103:⚠️ Potential issue | 🟡 MinorAdd error handling for the dynamic import.
The dynamic import of
@tanstack/angular-devtoolshas no.catch()handler. If the import fails (e.g., network error, bundler misconfiguration), the error will be silently swallowed or result in an unhandled promise rejection.Proposed fix
import('@tanstack/angular-devtools').then(({ TanStackDevtools }) => { const componentRef = createComponent(TanStackDevtools, { environmentInjector: injector, bindings: [ inputBinding('config', config), inputBinding('plugins', plugins), inputBinding('eventBusConfig', eventBusConfig), ], }) applicationRef.attachView(componentRef.hostView) componentRef.changeDetectorRef.detectChanges() - }) + }).catch((error) => { + console.error('[`@tanstack/angular-devtools`] Failed to load devtools:', error) + })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/angular-devtools/provider/with-devtools.ts` around lines 91 - 103, The dynamic import of '@tanstack/angular-devtools' in with-devtools.ts lacks error handling; update the import promise chain that calls import('@tanstack/angular-devtools').then(...) to add a .catch handler that logs the error and a contextual message (e.g., "Failed to load TanStackDevtools") and avoids leaving an unhandled rejection; reference the import call and the createComponent/TanStackDevtools block so the catch can report the error and optionally perform a safe fallback (e.g., skip attaching the devtools) if the module fails to load.packages/devtools-utils/src/angular/panel.ts-15-20 (1)
15-20:⚠️ Potential issue | 🟡 MinorType guard using
!!o.prototypeis fragile.This check relies on the fact that arrow functions and async functions lack a
prototypeproperty, while class constructors have one. However, regular functions also haveprototype, so if someone passes a non-arrow function that returns a Promise, it would incorrectly be identified as a constructor.Consider a more robust check:
Proposed fix
function isPanelClassConstructor< TComponentProps extends DevtoolsPanelProps, TCoreDevtoolsClass extends BaseCorePanelClass, >(o: any): o is CoreClassConstructor<TComponentProps, TCoreDevtoolsClass> { - return !!o.prototype + // Classes have a prototype with a constructor property pointing back to themselves + return typeof o === 'function' && o.prototype?.constructor === o && !o.prototype[Symbol.toStringTag] }Alternatively, if the lazy factory is always expected to be an arrow function or async function (which is typical for dynamic imports), the current approach works in practice but deserves a comment explaining the assumption.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/devtools-utils/src/angular/panel.ts` around lines 15 - 20, The type-guard isPanelClassConstructor currently uses !!o.prototype which misclassifies plain functions as class constructors; replace that heuristic with a robust check that ensures o is an ES6 class constructor, e.g. verify typeof o === 'function' and use Function.prototype.toString.call(o) to test for a leading "class" token (e.g. /^class\s/.test(...)); update the function isPanelClassConstructor<TComponentProps, TCoreDevtoolsClass> to use this test (or, if you intentionally only accept arrow/async factories, add a clear comment explaining that assumption) so non-class functions are not treated as constructors.
🧹 Nitpick comments (14)
examples/angular/with-devtools/.vscode/mcp.json (1)
6-6: Angular's official MCP documentation uses unpinned@angular/cliintentionally.The unpinned version on line 6 aligns with Angular's documented setup for
.vscode/mcp.json. Since the MCP server is experimental, Angular recommends tracking the latest CLI to pick up fixes and improvements rather than pinning.While pinning to
@angular/cli@20would improve reproducibility across team members, it goes against Angular's official guidance for this feature. If your team prefers consistency and controlled upgrades, pinning is a valid choice, but it's not the documented approach.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/with-devtools/.vscode/mcp.json` at line 6, The current args entry ["-y", "@angular/cli", "mcp"] intentionally uses an unpinned `@angular/cli` per Angular's MCP docs—leave it as-is to track latest fixes; if you instead want reproducible behavior across the team, change that args element to "@angular/cli@20" (i.e., replace "@angular/cli" with "@angular/cli@20") so the MCP launch uses the pinned CLI version.examples/angular/panel/package.json (1)
22-22: Inconsistent version specifier for@tanstack/devtools-event-client.This dependency uses an exact version (
0.4.2) while other@tanstack/*packages use caret ranges (^0.0.1,^0.10.13,^0.3.3). Consider using a caret range for consistency unless pinning is intentional.Suggested fix
- "@tanstack/devtools-event-client": "0.4.2", + "@tanstack/devtools-event-client": "^0.4.2",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/panel/package.json` at line 22, The dependency "@tanstack/devtools-event-client" is pinned to an exact version "0.4.2" while other `@tanstack` packages in package.json use caret ranges; update the version specifier for "@tanstack/devtools-event-client" to use a caret range (e.g. "^0.4.2") to match the other `@tanstack/`* entries and maintain consistent semver behavior across the project.packages/devtools-utils/vite.config.angular.ts (1)
5-7: Drop the emptypluginsfield for clarity.No behavior change, just removes noise in the config object.
♻️ Suggested cleanup
const config = defineConfig({ - plugins: [], test: { name: packageJson.name,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/devtools-utils/vite.config.angular.ts` around lines 5 - 7, The config object created via defineConfig currently includes an unnecessary empty "plugins" property; remove the "plugins: []" entry from the defineConfig call (the config constant) to clean up the Vite configuration without changing behavior—locate the defineConfig({ ... }) block that assigns to config and delete the plugins key.examples/angular/basic/src/app/devtools/custom-devtools-panel.ts (1)
27-28: Clarify the "Automatically added" comment.The comment
// Automatically addedon thethemeinput is unclear. Consider elaborating on what "automatically adds" this input or remove the comment if it's no longer relevant.📝 Suggested clarification
- // Automatically added + // Injected by the devtools plugin framework readonly theme = input<string>()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/basic/src/app/devtools/custom-devtools-panel.ts` around lines 27 - 28, The "// Automatically added" comment above the readonly theme = input<string>() declaration is ambiguous; either replace it with a clearer note that states who/what adds this input and when (e.g., "Added by DevTools host at runtime" or "Injected by framework build step") or remove the comment entirely if it's stale; update the comment adjacent to the readonly theme = input<string>() symbol to explicitly state its source and purpose so future readers understand why the input exists.examples/angular/panel/src/app/counter.ts (2)
10-28: Consider extracting duplicated emit logic.The
DevtoolsEventClient.emit('counter-state', ...)call is duplicated in bothincrementanddecrement. Consider extracting this into a helper function for DRY code.♻️ Proposed refactor
export function createCounter() { const count = signal(0) const history: Array<number> = [] + const emitState = () => { + DevtoolsEventClient.emit('counter-state', { + count: count(), + history, + }) + } + return { value: () => count(), increment: () => { count.update((n) => n + 1) history.push(count()) - - // The emit eventSuffix must match that of the EventMap defined in eventClient.ts - DevtoolsEventClient.emit('counter-state', { - count: count(), - history, - }) + emitState() }, decrement: () => { count.update((n) => n - 1) history.push(count()) - - DevtoolsEventClient.emit('counter-state', { - count: count(), - history, - }) + emitState() }, } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/panel/src/app/counter.ts` around lines 10 - 28, increment and decrement both call DevtoolsEventClient.emit('counter-state', { count: count(), history }) causing duplicated emit logic; extract that into a small helper (e.g., emitCounterState or notifyCounterState) that reads count() and history and calls DevtoolsEventClient.emit('counter-state', payload), then replace the duplicated emit calls in the increment and decrement functions with a single call to the new helper; ensure the helper is colocated with the count/history variables and used after updating history so payload stays correct.
4-9: Consider emitting initial state.The panel won't display any state until the user clicks increment or decrement for the first time. Consider emitting the initial state when the counter is created, or document this behavior as intentional.
💡 Optional: Emit initial state
export function createCounter() { const count = signal(0) const history: Array<number> = [] + + // Emit initial state + DevtoolsEventClient.emit('counter-state', { + count: count(), + history, + }) return {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/panel/src/app/counter.ts` around lines 4 - 9, createCounter currently never emits its initial value so the UI sees no state until the first increment/decrement; fix it by emitting the initial state when the counter is created: after initializing count and history in createCounter, record the initial value (e.g., push count() into history) and invoke the same notifier/emit function you use for subsequent updates (or expose/ call value() once) so the panel receives the initial state; update references in createCounter, count, history and the notifier/emit handler accordingly.docs/framework/angular/basic-setup.md (1)
51-58: Class name inconsistency between examples.The first example uses
Appas the class name (line 31), while this example usesAppComponent(line 51). For consistency within the same documentation page, consider using the same naming convention.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/framework/angular/basic-setup.md` around lines 51 - 58, The examples use inconsistent class names — one declares class App and the later snippet declares class AppComponent; update the later example so the class name matches the first example (e.g., rename AppComponent to App) and ensure the export and any internal references (plugins array, TanStackDevtoolsAngularPlugin, AngularQueryDevtoolsPanel) are updated accordingly so both examples use the same class identifier.packages/angular-devtools/package.json (2)
55-63: Duplicate tslib in dependencies and devDependencies.
tslibis listed in bothdependencies(line 57) anddevDependencies(line 62). The runtime dependency listing is sufficient; the devDependencies entry is redundant.♻️ Remove duplicate tslib
"devDependencies": { "@angular/core": "^21.2.0", - "ng-packagr": "^21.2.0", - "tslib": "^2.3.0" + "ng-packagr": "^21.2.0" },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/angular-devtools/package.json` around lines 55 - 63, Remove the duplicate tslib entry by keeping it under "dependencies" and deleting the "tslib" entry from "devDependencies" in package.json; update the package.json object so only the top-level "dependencies" contains "tslib" and ensure no other references to tslib remain in dev tooling config (look for "tslib" in the package.json dependencies/devDependencies blocks).
53-53: Build script uses Unix-specific commands.The
rm -rfcommands in the build script are not cross-platform. This may cause issues for contributors on Windows.♻️ Use cross-platform alternative
Consider using
rimrafor the existingpremove(used in the clean script) for cross-platform compatibility:- "build": "ng-packagr -p ng-package.json -c tsconfig.build.json && rm -rf ./dist/package.json && rm -rf ./dist/provider/package.json" + "build": "ng-packagr -p ng-package.json -c tsconfig.build.json && premove ./dist/package.json ./dist/provider/package.json"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/angular-devtools/package.json` at line 53, The build script in package.json ("build") uses Unix-only rm -rf; replace those calls with a cross-platform remover such as rimraf (or reuse the existing "premove" helper used by "clean") so Windows contributors won't fail; update the "build" script to call rimraf on ./dist/package.json and ./dist/provider/package.json (or call the existing preremove command), and if using rimraf add it to devDependencies and/or the scripts so CI and local installs have the tool available.examples/angular/basic/src/app/app.ts (1)
19-21: Consider using a more meaningful defer trigger condition.
@defer (when true)is equivalent to an unconditional defer, which loads the content when the browser is idle. This works but thewhen truecondition is redundant and potentially confusing.♻️ Optional: Simplified defer syntax
- `@defer` (when true) { + `@defer` { <tanstack-devtools [plugins]="plugins()" [eventBusConfig]="{ debug: true }" /> }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/basic/src/app/app.ts` around lines 19 - 21, The `@defer` block uses an explicit always-true condition (`@defer` (when true)) which is redundant; change it to use the simpler unconditional defer syntax or a meaningful predicate instead. Update the `@defer` usage wrapping <tanstack-devtools [plugins]="plugins()" [eventBusConfig]="{ debug: true }" /> by removing "(when true)" or replacing it with a real trigger (e.g., a boolean like isIdle or isDevMode) so the intent is clear and the defer behavior for tanstack-devtools and plugins() is preserved.examples/angular/panel/src/app/devtools/custom-devtools-panel.ts (1)
12-17: Consider using textContent for safer DOM updates (optional for example code).The static analysis flagged the
innerHTMLusage. While in this example the data is trusted internal state (numeric count and array history), for production patterns consider using DOM APIs liketextContentor a sanitization library.For this example demonstrating devtools integration, the current approach is acceptable.
♻️ Optional: Safer DOM update pattern
- function render(count: number, history: string) { - plugin.innerHTML = ` - <div>counter state: ${count}</div> - <div>counter history: ${history}</div> - ` - } + function render(count: number, history: string) { + plugin.textContent = '' + const countDiv = element.ownerDocument.createElement('div') + countDiv.textContent = `counter state: ${count}` + const historyDiv = element.ownerDocument.createElement('div') + historyDiv.textContent = `counter history: ${history}` + plugin.appendChild(countDiv) + plugin.appendChild(historyDiv) + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/panel/src/app/devtools/custom-devtools-panel.ts` around lines 12 - 17, The render(count: number, history: string) function writes unescaped HTML into plugin via plugin.innerHTML which static analysis flagged; change render to avoid innerHTML by creating or selecting child elements under the plugin container and setting their textContent (or concatenating safe strings) to display count and history, e.g., create two divs or reuse existing elements and assign their textContent using the plugin reference instead of assigning plugin.innerHTML.packages/angular-devtools/provider/with-devtools.ts (2)
86-89: Plugins are set once and won't react to changes indevtoolsOptions().plugins.
configandeventBusConfigare wrapped incomputed()to stay reactive, butpluginsis set imperatively withplugins.set(...)only during initialization. IfdevtoolsOptions().pluginschanges later, the devtools won't reflect those changes.If this is intentional (plugins are expected to be static), consider adding a brief comment. Otherwise, consider using an
effect()to keep plugins in sync:Proposed fix if reactivity is needed
+ import { effect } from '@angular/core' // ... - plugins.set(devtoolsOptions().plugins ?? []) + effect(() => { + plugins.set(devtoolsOptions().plugins ?? []) + })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/angular-devtools/provider/with-devtools.ts` around lines 86 - 89, The plugins array is only set once with plugins.set(devtoolsOptions().plugins ?? []) and won't update when devtoolsOptions().plugins changes; change this to a reactive effect that watches devtoolsOptions().plugins and calls plugins.set(...) whenever it updates (or add a comment if immutability is intentional). Specifically, replace the one-time call to plugins.set with an effect/autorun that reads devtoolsOptions().plugins and updates plugins via plugins.set(...) so it stays in sync with the computed config/eventBusConfig used elsewhere.
32-34: Consider tightening theArray<any>types.Both
WithDevtoolsOptionsFnandWithDevtoolsOptions.depsuseArray<any>, which loses type safety. If stronger typing isn't feasible due to Angular DI constraints, this is acceptable, but a brief JSDoc comment explaining whyanyis necessary would help future maintainers.Also applies to: 56-58
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/angular-devtools/provider/with-devtools.ts` around lines 32 - 34, Tighten the loose Array<any> usages by replacing them with a safer type (prefer unknown[] or make WithDevtoolsOptionsFn generic like <Args extends any[]>(...args: Args) => TanStackDevtoolsAllOptions and use Args for WithDevtoolsOptions.deps) so callers retain type information; if Angular DI constraints prevent stronger typing, add a short JSDoc above WithDevtoolsOptionsFn and the WithDevtoolsOptions.deps field explaining why any is necessary and referencing the DI limitation. Ensure you update the declarations for WithDevtoolsOptionsFn and the WithDevtoolsOptions.deps usage (the symbols named WithDevtoolsOptionsFn and WithDevtoolsOptions.deps) consistently.packages/devtools-utils/src/angular/panel.ts (1)
22-65: Consider adding JSDoc and explicit return type for public API clarity.
createAngularPanelis a public API but lacks documentation explaining its purpose, the tuple return structure, and why the second element is() => null. Adding a brief JSDoc (without@params/@returns per coding guidelines) and an explicit return type would improve discoverability.Example documentation
+/** + * Creates an Angular panel adapter for TanStack Devtools. + * + * Accepts either a CoreClass constructor directly or a lazy loader function + * that returns a Promise resolving to the constructor. + * + * Returns a tuple where the first element is a factory for mounting the panel, + * and the second is a no-op placeholder for framework compatibility. + */ export function createAngularPanel< TComponentProps extends DevtoolsPanelProps, TCoreDevtoolsClass extends BaseCorePanelClass, >( CoreClass: | CoreClassConstructor<TComponentProps, TCoreDevtoolsClass> | (() => Promise< CoreClassConstructor<TComponentProps, TCoreDevtoolsClass> >), -) { +): readonly [ + () => (inputs: () => TComponentProps, host: HTMLElement) => () => void, + () => null, +] {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/devtools-utils/src/angular/panel.ts` around lines 22 - 65, Add JSDoc and an explicit return type to the public function createAngularPanel to document its purpose, the tuple structure, and why the second tuple entry is () => null; update the function signature to include a clear return type (e.g., readonly [ (inputs: () => TComponentProps, host: HTMLElement) => () => void, () => null ]) and add a brief JSDoc block above createAngularPanel describing what the factory returns, what the first tuple function does (mount/unmount via CoreClass), and why the second is a stub; reference the createAngularPanel function, the returned tuple and the second element (() => null) when making these edits.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/angular/with-devtools/src/app/devtools/custom-devtools-panel.ts`:
- Around line 13-23: The `@Component` metadata for the custom devtools panel
includes an imports array (JsonPipe) but is missing standalone: true, which is
required for using imports; update the Component decorator metadata (the
`@Component` applied to the custom devtool panel) to include standalone: true so
the imports property is valid and the Angular compiler will succeed.
In `@packages/angular-devtools/src/devtools.ts`:
- Around line 275-279: The current isClassConstructor wrongly treats plain
functions as classes; replace its heuristic with a reliable ES6-class check: if
o is a function, call Function.prototype.toString on it and test for a leading
"class" token (e.g. /^\s*class\b/), returning true only for that case. Update
the isClassConstructor<T> implementation (the function named isClassConstructor)
to use this toString/regex check so functional renderers are not misclassified
and therefore not handed to renderComponent().
- Around line 125-151: The spread usage ...(plugin.inputs ?? {}) drops
non-plain-object inputs (functions/signals); instead stop spreading and pass the
inputs value directly as a property so functions/signals are preserved. Update
the three call sites that build the options object — the calls to
this.renderComponent(plugin.name...), this.renderComponent(plugin.render...),
and this.#renderComponentFunction(...) — to construct options like { theme,
inputs: plugin.inputs ?? {} } (or { theme, inputs: plugin.inputs } if a default
empty object isn't desired) rather than spreading plugin.inputs.
- Around line 142-153: The async call to this.#renderComponentFunction with
plugin.render (typed as TanStackDevtoolsAngularPluginRender) can reject and
currently has no error handling; wrap the call in a try/catch (or attach a
.catch) to handle rejections, log the error (include plugin identity/context),
and optionally fall back or swallow to prevent unhandled promise
rejections—update the call site where this.#renderComponentFunction(...) is
invoked to catch and handle any thrown errors.
---
Minor comments:
In `@docs/framework/angular/adapter.md`:
- Around line 46-48: The sentence incorrectly refers to a non-existent
`component` field; update the documentation text to reference the actual
`render` field used in the adapter type and examples (e.g., change "uses
`component` (an Angular component class reference) instead of `render`" to
indicate that the adapter uses `render` and explain that inputs are provided via
the `inputs` field and bound with `setInput()`), or if the implementation should
instead use `component`, update the type/examples to match—ensure consistency
between the docs and the symbols `render`, `inputs`, and `setInput()`.
- Around line 38-45: The docs table incorrectly types the render field as
Type<any>; update the table entry for the `render` row to use the actual type
name `TanStackDevtoolsAngularPluginRender` and adjust the description to
reference that type (e.g., "The render property is a
TanStackDevtoolsAngularPluginRender that defines the Angular component/class
used for plugin panel content"); ensure the `render` column now matches the type
alias `TanStackDevtoolsAngularPluginRender` used in the code.
In `@docs/framework/angular/basic-setup.md`:
- Around line 18-32: The import name in the example is inconsistent: the file
imports TanStackDevtoolsComponent but the App component's standalone imports
array references TanStackDevtools. Fix by making the imported symbol match the
one used in the App component — either change the import to TanStackDevtools
(recommended) or change the imports array to use TanStackDevtoolsComponent so
the imported identifier and the imports entry are the same; update the import
statement near the top that currently names TanStackDevtoolsComponent
accordingly.
In `@docs/framework/angular/guides/custom-plugins.md`:
- Line 178: The phrase "built in" in the sentence referencing TanStackDevtools
and the TanStack EventClient should be hyphenated; update the sentence so it
reads "built-in debug mode" where "built in" appears (affecting the line
mentioning TanStackDevtools and EventClient) to correct the grammar.
- Line 197: Replace the incorrect two-word phrase "Where as" with the correct
single-word "Whereas" in the docs/framework/angular/guides/custom-plugins.md
content to fix the minor spelling error.
- Around line 213-216: The fenced code block showing debug output lacks a
language specifier; update the block in
docs/framework/angular/guides/custom-plugins.md around the debug output
containing "[tanstack-devtools:client-bus] Initializing client event bus" and
"[tanstack-devtools:custom-devtools-plugin] Registered event to bus
custom-devtools:counter-state" by adding a language tag (e.g., ```text) after
the opening backticks so the block becomes a fenced code block with the "text"
specifier for consistent formatting.
- Around line 122-135: The template incorrectly invokes the getter stateHistory
as a method; update the template to reference the getter without parentheses
(use {{ stateHistory }} instead of {{ stateHistory() }}) so it matches the
DevtoolPanel get stateHistory() accessor and returns the computed JSON string
properly.
In `@examples/angular/panel/.vscode/launch.json`:
- Around line 9-16: Update the VS Code launch configuration so the preLaunchTask
matches the project's package manager: replace the "preLaunchTask" values
referencing "npm: start" and "npm: test" with the corresponding "pnpm: start"
and "pnpm: test" (or ensure tasks with those names exist), targeting the entries
that define the Chrome launches ("name": "ng serve"/implicit block and "name":
"ng test") so VS Code will run the correct pnpm tasks before launching.
In `@examples/angular/panel/README.md`:
- Around line 1-3: The README title currently reads "Basic" but should match the
example directory name; update the top-level heading in the README.md by
replacing the heading text "Basic" with "Panel" so the file title reads "#
Panel" for consistency with the panel example directory.
In `@examples/angular/panel/src/index.html`:
- Line 5: Update the HTML title in examples/angular/panel/src/index.html:
replace the current <title>Basic</title> with a more accurate title reflecting
the app (for example "Angular Panel Example" or "Panel") so the browser tab and
metadata correctly identify this panel demo; modify the <title> tag content only
to match the panel example name.
In `@examples/angular/with-devtools/.editorconfig`:
- Around line 11-17: Remove the redundant IntelliJ-specific TypeScript setting
by deleting the line "ij_typescript_use_double_quotes = false" from the file;
keep the standard "[*.ts]" block with "quote_type = single" as the single source
of truth for quote style and remove only the "ij_typescript_use_double_quotes"
entry so there is no duplicate configuration.
In `@examples/angular/with-devtools/angular.json`:
- Around line 12-13: The project key in the angular.json "projects" object is
incorrectly named "basic" but should match the example folder; rename the
project key from "basic" to "with-devtools" and update every reference to that
key inside the file (notably the build and serve targets currently referencing
"basic") so they point to "with-devtools" instead; ensure target entries such as
the "build" and "serve" (and any "architect" or "targets" blocks) are updated to
use the new project name so configuration and CLI commands remain consistent.
In `@examples/angular/with-devtools/README.md`:
- Around line 1-3: The README title currently reads "Basic" which doesn't match
this example's directory; update the top-level heading in
examples/angular/with-devtools/README.md (the line containing "# Basic") to a
correct, descriptive title such as "# with-devtools" or "# Angular with
DevTools" so the file name and content reflect the example's purpose.
In `@examples/angular/with-devtools/src/index.html`:
- Line 5: The page title in index.html is incorrect ("Basic"); update the
<title> element in the file so it matches the example name ("with-devtools" or a
human-friendly variant like "With Devtools") to avoid the copy-paste artifact
and ensure the example metadata/title reflects this example.
In `@packages/angular-devtools/provider/with-devtools.ts`:
- Around line 75-80: Replace the direct string comparison of PLATFORM_ID with
Angular's isPlatformBrowser check: import isPlatformBrowser from
'@angular/common' and change the platform guard in the
provideEnvironmentInitializer block (where inject(PLATFORM_ID) is used) to use
if (!isPlatformBrowser(inject(PLATFORM_ID))) return; so the environment check
uses the documented API rather than comparing to the string 'browser'.
- Around line 91-103: The dynamic import of '@tanstack/angular-devtools' in
with-devtools.ts lacks error handling; update the import promise chain that
calls import('@tanstack/angular-devtools').then(...) to add a .catch handler
that logs the error and a contextual message (e.g., "Failed to load
TanStackDevtools") and avoids leaving an unhandled rejection; reference the
import call and the createComponent/TanStackDevtools block so the catch can
report the error and optionally perform a safe fallback (e.g., skip attaching
the devtools) if the module fails to load.
In `@packages/angular-devtools/src/types.ts`:
- Around line 44-62: The JSDoc for TanStackDevtoolsAngularInit and its example
incorrectly refer to a `component` property while the actual type
TanStackDevtoolsAngularPlugin defines `render`; update the JSDoc/example to use
`render` (e.g., replace `{ name: 'My Plugin', component: MyPluginComponent }`
with `{ name: 'My Plugin', render: MyPluginComponent }`) so the documentation
matches the TanStackDevtoolsAngularPlugin type and avoids confusion.
In `@packages/devtools-utils/src/angular/panel.ts`:
- Around line 53-57: The lazy-load branch calling CoreClass() lacks error
handling; wrap the promise chain starting at CoreClass() so rejections are
caught and handled—add a .catch handler to the CoreClass().then(...).then(mount)
chain that logs the error and/or invokes the existing mount/error handling flow
(reference CoreClass, inputs(), and mount) to avoid unhandled promise rejections
and surface failures during dynamic import.
- Around line 15-20: The type-guard isPanelClassConstructor currently uses
!!o.prototype which misclassifies plain functions as class constructors; replace
that heuristic with a robust check that ensures o is an ES6 class constructor,
e.g. verify typeof o === 'function' and use Function.prototype.toString.call(o)
to test for a leading "class" token (e.g. /^class\s/.test(...)); update the
function isPanelClassConstructor<TComponentProps, TCoreDevtoolsClass> to use
this test (or, if you intentionally only accept arrow/async factories, add a
clear comment explaining that assumption) so non-class functions are not treated
as constructors.
---
Nitpick comments:
In `@docs/framework/angular/basic-setup.md`:
- Around line 51-58: The examples use inconsistent class names — one declares
class App and the later snippet declares class AppComponent; update the later
example so the class name matches the first example (e.g., rename AppComponent
to App) and ensure the export and any internal references (plugins array,
TanStackDevtoolsAngularPlugin, AngularQueryDevtoolsPanel) are updated
accordingly so both examples use the same class identifier.
In `@examples/angular/basic/src/app/app.ts`:
- Around line 19-21: The `@defer` block uses an explicit always-true condition
(`@defer` (when true)) which is redundant; change it to use the simpler
unconditional defer syntax or a meaningful predicate instead. Update the `@defer`
usage wrapping <tanstack-devtools [plugins]="plugins()" [eventBusConfig]="{
debug: true }" /> by removing "(when true)" or replacing it with a real trigger
(e.g., a boolean like isIdle or isDevMode) so the intent is clear and the defer
behavior for tanstack-devtools and plugins() is preserved.
In `@examples/angular/basic/src/app/devtools/custom-devtools-panel.ts`:
- Around line 27-28: The "// Automatically added" comment above the readonly
theme = input<string>() declaration is ambiguous; either replace it with a
clearer note that states who/what adds this input and when (e.g., "Added by
DevTools host at runtime" or "Injected by framework build step") or remove the
comment entirely if it's stale; update the comment adjacent to the readonly
theme = input<string>() symbol to explicitly state its source and purpose so
future readers understand why the input exists.
In `@examples/angular/panel/package.json`:
- Line 22: The dependency "@tanstack/devtools-event-client" is pinned to an
exact version "0.4.2" while other `@tanstack` packages in package.json use caret
ranges; update the version specifier for "@tanstack/devtools-event-client" to
use a caret range (e.g. "^0.4.2") to match the other `@tanstack/`* entries and
maintain consistent semver behavior across the project.
In `@examples/angular/panel/src/app/counter.ts`:
- Around line 10-28: increment and decrement both call
DevtoolsEventClient.emit('counter-state', { count: count(), history }) causing
duplicated emit logic; extract that into a small helper (e.g., emitCounterState
or notifyCounterState) that reads count() and history and calls
DevtoolsEventClient.emit('counter-state', payload), then replace the duplicated
emit calls in the increment and decrement functions with a single call to the
new helper; ensure the helper is colocated with the count/history variables and
used after updating history so payload stays correct.
- Around line 4-9: createCounter currently never emits its initial value so the
UI sees no state until the first increment/decrement; fix it by emitting the
initial state when the counter is created: after initializing count and history
in createCounter, record the initial value (e.g., push count() into history) and
invoke the same notifier/emit function you use for subsequent updates (or
expose/ call value() once) so the panel receives the initial state; update
references in createCounter, count, history and the notifier/emit handler
accordingly.
In `@examples/angular/panel/src/app/devtools/custom-devtools-panel.ts`:
- Around line 12-17: The render(count: number, history: string) function writes
unescaped HTML into plugin via plugin.innerHTML which static analysis flagged;
change render to avoid innerHTML by creating or selecting child elements under
the plugin container and setting their textContent (or concatenating safe
strings) to display count and history, e.g., create two divs or reuse existing
elements and assign their textContent using the plugin reference instead of
assigning plugin.innerHTML.
In `@examples/angular/with-devtools/.vscode/mcp.json`:
- Line 6: The current args entry ["-y", "@angular/cli", "mcp"] intentionally
uses an unpinned `@angular/cli` per Angular's MCP docs—leave it as-is to track
latest fixes; if you instead want reproducible behavior across the team, change
that args element to "@angular/cli@20" (i.e., replace "@angular/cli" with
"@angular/cli@20") so the MCP launch uses the pinned CLI version.
In `@packages/angular-devtools/package.json`:
- Around line 55-63: Remove the duplicate tslib entry by keeping it under
"dependencies" and deleting the "tslib" entry from "devDependencies" in
package.json; update the package.json object so only the top-level
"dependencies" contains "tslib" and ensure no other references to tslib remain
in dev tooling config (look for "tslib" in the package.json
dependencies/devDependencies blocks).
- Line 53: The build script in package.json ("build") uses Unix-only rm -rf;
replace those calls with a cross-platform remover such as rimraf (or reuse the
existing "premove" helper used by "clean") so Windows contributors won't fail;
update the "build" script to call rimraf on ./dist/package.json and
./dist/provider/package.json (or call the existing preremove command), and if
using rimraf add it to devDependencies and/or the scripts so CI and local
installs have the tool available.
In `@packages/angular-devtools/provider/with-devtools.ts`:
- Around line 86-89: The plugins array is only set once with
plugins.set(devtoolsOptions().plugins ?? []) and won't update when
devtoolsOptions().plugins changes; change this to a reactive effect that watches
devtoolsOptions().plugins and calls plugins.set(...) whenever it updates (or add
a comment if immutability is intentional). Specifically, replace the one-time
call to plugins.set with an effect/autorun that reads devtoolsOptions().plugins
and updates plugins via plugins.set(...) so it stays in sync with the computed
config/eventBusConfig used elsewhere.
- Around line 32-34: Tighten the loose Array<any> usages by replacing them with
a safer type (prefer unknown[] or make WithDevtoolsOptionsFn generic like <Args
extends any[]>(...args: Args) => TanStackDevtoolsAllOptions and use Args for
WithDevtoolsOptions.deps) so callers retain type information; if Angular DI
constraints prevent stronger typing, add a short JSDoc above
WithDevtoolsOptionsFn and the WithDevtoolsOptions.deps field explaining why any
is necessary and referencing the DI limitation. Ensure you update the
declarations for WithDevtoolsOptionsFn and the WithDevtoolsOptions.deps usage
(the symbols named WithDevtoolsOptionsFn and WithDevtoolsOptions.deps)
consistently.
In `@packages/devtools-utils/src/angular/panel.ts`:
- Around line 22-65: Add JSDoc and an explicit return type to the public
function createAngularPanel to document its purpose, the tuple structure, and
why the second tuple entry is () => null; update the function signature to
include a clear return type (e.g., readonly [ (inputs: () => TComponentProps,
host: HTMLElement) => () => void, () => null ]) and add a brief JSDoc block
above createAngularPanel describing what the factory returns, what the first
tuple function does (mount/unmount via CoreClass), and why the second is a stub;
reference the createAngularPanel function, the returned tuple and the second
element (() => null) when making these edits.
In `@packages/devtools-utils/vite.config.angular.ts`:
- Around line 5-7: The config object created via defineConfig currently includes
an unnecessary empty "plugins" property; remove the "plugins: []" entry from the
defineConfig call (the config constant) to clean up the Vite configuration
without changing behavior—locate the defineConfig({ ... }) block that assigns to
config and delete the plugins key.
examples/angular/with-devtools/src/app/devtools/custom-devtools-panel.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
packages/devtools-utils/src/angular/panel.ts (1)
17-22: Type guard may produce false positives for arrow functions with prototype properties.The check
!!o.prototypedistinguishes class constructors from arrow functions (which lack.prototype), but it could incorrectly classify a regular function as a constructor since non-arrow functions also have.prototype. Consider a more robust check.🔧 Suggested improvement
function isPanelClassConstructor< TComponentProps extends DevtoolsPanelProps, TCoreDevtoolsClass extends BaseCorePanelClass, >(o: any): o is CoreClassConstructor<TComponentProps, TCoreDevtoolsClass> { - return !!o.prototype + return typeof o === 'function' && !!o.prototype && o.prototype.constructor === o }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/devtools-utils/src/angular/panel.ts` around lines 17 - 22, The isPanelClassConstructor type guard currently returns true for any function with a .prototype and can misclassify plain functions; update isPanelClassConstructor to more robustly detect ES class constructors (e.g., first confirm typeof o === 'function' and then verify it's an ES class by checking Function.prototype.toString.call(o) starts with "class " or by ensuring o.prototype && o.prototype.constructor === o and that the prototype is non-enumerable), so replace the simple !!o.prototype check in isPanelClassConstructor (affecting CoreClassConstructor<TComponentProps, TCoreDevtoolsClass>) with one of these stronger checks to avoid false positives for regular functions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/devtools-utils/src/angular/panel.ts`:
- Around line 56-58: The promise chain that resolves CoreClass
(CoreClass().then((ResolvedCoreClass) => new
ResolvedCoreClass(inputs())).then(mount)) lacks error handling; add a
.catch(...) after .then(mount) to handle rejection from CoreClass or
construction, logging the error (e.g., console.error or the project logger) and
cleaning up the appended panel div (remove or unmount the element) to avoid
leaving a dangling DOM node; ensure the catch references the same panel element
used when appending so it can be removed on failure.
- Around line 37-63: The async branch can append the panel to the DOM then
resolve after cleanup runs, leaving an orphaned element and later mounting into
it; update the logic around CoreClass, mount, and unmount to track cancellation:
when entering the async path (the CoreClass() promise) capture a cancelled flag
(or an AbortSignal) so that the cleanup returned by the outer function sets
cancelled = true and removes the panel (panel.remove()), and the promise
continuation checks the flag before calling mount; also ensure that when mount
runs it still assigns unmount so the usual unmount?.() will work. This uses the
existing identifiers panel, mount, unmount, CoreClass, isPanelClassConstructor,
and host.appendChild to locate where to add the flag, the panel.remove() call in
the cleanup, and the pre-mount cancellation check.
---
Nitpick comments:
In `@packages/devtools-utils/src/angular/panel.ts`:
- Around line 17-22: The isPanelClassConstructor type guard currently returns
true for any function with a .prototype and can misclassify plain functions;
update isPanelClassConstructor to more robustly detect ES class constructors
(e.g., first confirm typeof o === 'function' and then verify it's an ES class by
checking Function.prototype.toString.call(o) starts with "class " or by ensuring
o.prototype && o.prototype.constructor === o and that the prototype is
non-enumerable), so replace the simple !!o.prototype check in
isPanelClassConstructor (affecting CoreClassConstructor<TComponentProps,
TCoreDevtoolsClass>) with one of these stronger checks to avoid false positives
for regular functions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: c12aa529-65fd-41c9-8f8b-6a7f9d292feb
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (4)
package.jsonpackages/devtools-utils/package.jsonpackages/devtools-utils/src/angular/panel.tspackages/devtools-utils/src/angular/plugin.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- package.json
- packages/devtools-utils/src/angular/plugin.ts
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (1)
packages/devtools-utils/src/angular/panel.ts (1)
62-64:⚠️ Potential issue | 🟡 MinorCleanup function should remove the panel element.
Even in the synchronous path, the cleanup function only calls
unmount?.()but doesn't remove the panel div from the DOM, potentially leaving orphaned elements.🐛 Proposed fix
return () => { unmount?.() + panel.remove() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/devtools-utils/src/angular/panel.ts` around lines 62 - 64, The cleanup returned by the panel mounting function currently only calls unmount?.() and never removes the panel DOM node, leaving orphaned elements; update the returned function so after calling unmount?.() it also removes the panel element (e.g., check the panel variable and call panel.remove() or parent.removeChild(panel) as appropriate), and guard against panel being null/already removed so this works both in synchronous and async paths (referencing the unmount and panel variables in the mount/cleanup closure).
🧹 Nitpick comments (7)
packages/devtools-a11y/package.json (1)
109-109: Minor formatting inconsistency.Missing space after the colon for consistency with other peer dependency entries.
🧹 Optional formatting fix
- "@angular/core":">=19.0.0", + "@angular/core": ">=19.0.0",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/devtools-a11y/package.json` at line 109, The peerDependencies entry for "@angular/core" has a missing space after the colon; open package.json and in the peerDependencies object adjust the "@angular/core" entry (the string key "@angular/core") so the value is formatted with a space after the colon (make it consistent with other entries by changing ":"">=19.0.0"," to ": ">=19.0.0",).packages/angular-devtools/src/types.ts (1)
30-35: Consider usingType<unknown>instead ofType<any>for consistency.Line 32 uses
Type<any>while lines 26-27 and 40 useType<unknown>. Usingunknownis generally safer as it forces explicit type narrowing. Consider aligning for consistency.♻️ Suggested change for type consistency
export type TanStackDevtoolsAngularPluginRender = | null - | Type<any> + | Type<unknown> | (() => | TanStackDevtoolsAngularPluginRenderFn | Promise<TanStackDevtoolsAngularPluginRenderFn>)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/angular-devtools/src/types.ts` around lines 30 - 35, Replace the inconsistent Type<any> usage in the TanStackDevtoolsAngularPluginRender type with Type<unknown> to match the other declarations; update the union member in the TanStackDevtoolsAngularPluginRender type so it uses Type<unknown> (keeping the rest of the union, including the function return types TanStackDevtoolsAngularPluginRenderFn and Promise<...>, unchanged).examples/angular/a11y-devtools/README.md (1)
1-59: Consider adding a11y-devtools specific documentation.The README contains standard Angular CLI instructions but lacks any information about the a11y-devtools integration or TanStack Devtools usage. Adding a brief section about the purpose of this example and how to use the a11y-devtools would improve the developer experience.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/a11y-devtools/README.md` around lines 1 - 59, The README currently only has generic Angular CLI instructions; add an "About a11y-devtools" section that briefly explains the purpose of this example (demonstrates integrating a11y-devtools with TanStack Devtools), a short "How to use" with steps referencing existing commands (e.g., run ng serve, open http://localhost:4200), notes on where to find demo components/pages in the example, and a small "Devtools usage" subsection that explains how to open/enable the TanStack Devtools panel and the a11y-devtools accessibility panel (and any optional config flags or environment variables the example supports). Include a pointer to relevant docs (TanStack Devtools/a11y-devtools) and mention the example name "a11y-devtools" so contributors can quickly locate the integration code.examples/angular/a11y-devtools/package.json (1)
20-22: Inconsistent version specifier for@tanstack/devtools-event-client.Line 21 uses an exact version
"0.4.2"while adjacent TanStack packages use caret ranges (^0.0.1,^0.1.2). This inconsistency may cause unexpected version drift when other packages update.Suggested fix
"@tanstack/angular-devtools": "^0.0.1", - "@tanstack/devtools-event-client": "0.4.2", + "@tanstack/devtools-event-client": "^0.4.2", "@tanstack/devtools-a11y": "^0.1.2",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/a11y-devtools/package.json` around lines 20 - 22, The package.json dependency for `@tanstack/devtools-event-client` uses an exact version "0.4.2" while neighboring TanStack deps use caret ranges; update the dependency entry for `@tanstack/devtools-event-client` to use a caret range (e.g., ^0.4.2) to match the others (`@tanstack/angular-devtools` and `@tanstack/devtools-a11y`) and ensure consistent semver behavior.examples/angular/a11y-devtools/angular.json (1)
12-14: Consider renaming the project to reflect its purpose.The project is named
"basic"but this is the a11y-devtools example. While Angular workspaces are isolated, a more descriptive name like"a11y-devtools"would improve clarity and consistency with the example's directory name.Suggested changes
"projects": { - "basic": { + "a11y-devtools": { "projectType": "application",Note: Also update
buildTargetreferences on lines 61 and 64 from"basic:build:..."to"a11y-devtools:build:...".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/a11y-devtools/angular.json` around lines 12 - 14, Rename the Angular project entry named "basic" under "projects" to "a11y-devtools" and update any build target references that still point to "basic:build:..." to "a11y-devtools:build:..." so the project name matches the example directory; ensure you update the project name string and all occurrences of the buildTarget values (e.g., "basic:build:...") to the new "a11y-devtools:build:..." identifier.examples/angular/a11y-devtools/src/app/counter.ts (1)
15-18: Consider passing a copy of the history array.The
historyarray is passed by reference in the emit payload. If the event handler stores this reference, subsequent mutations will affect the stored value unexpectedly. For example code this is likely fine, but passing a copy is safer.Suggested fix
DevtoolsEventClient.emit('counter-state', { count: count(), - history, + history: [...history], })Apply the same change to both
incrementanddecrementemit calls.Also applies to: 24-27
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/angular/a11y-devtools/src/app/counter.ts` around lines 15 - 18, Pass a shallow copy of the history array instead of the original when emitting events to avoid downstream mutation: update the DevtoolsEventClient.emit calls (used in the increment and decrement flows) to send history.slice() (or [...history]) in the payload alongside count(), so the emitted object contains an immutable snapshot rather than a reference to the live history array.packages/devtools-utils/src/angular/panel.ts (1)
18-23: Type guard relies solely on.prototypeexistence.The
isPanelClassConstructorcheck using!!o.prototypemay return true for arrow functions or objects that happen to have aprototypeproperty but are not constructors. Consider a more robust check.♻️ Suggested improvement
function isPanelClassConstructor< TComponentProps extends DevtoolsPanelProps, TCoreDevtoolsClass extends BaseCorePanelClass, >(o: any): o is CoreClassConstructor<TComponentProps, TCoreDevtoolsClass> { - return !!o.prototype + return typeof o === 'function' && !!o.prototype && o.prototype.constructor === o }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/devtools-utils/src/angular/panel.ts` around lines 18 - 23, Replace the fragile check in isPanelClassConstructor with a robust constructor test: verify o is a function (typeof o === 'function'), that o.prototype exists, and that o.prototype.constructor === o (or that o.prototype implements expected instance members from BaseCorePanelClass). Update the function isPanelClassConstructor to use these checks (referencing isPanelClassConstructor, CoreClassConstructor, and BaseCorePanelClass) so it rejects plain objects with a prototype property and non-constructable values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/angular/a11y-devtools/README.md`:
- Line 1: Replace the top-level Markdown title "Basic" in the README with a
title that matches the example directory name (e.g., "a11y-devtools" or
"a11y-devtools — Accessibility DevTools Example") so the README heading aligns
with the example; locate the header string "Basic" in the README.md and update
it accordingly.
- Line 3: Update the README line that claims Vitest is the test runner to
correctly state the project uses Angular's standard unit test setup; replace the
Vitest reference with a note that tests run via the npm test script (runs "ng
test") and that the project uses the Angular unit test builder
(`@angular/build`:unit-test) as shown in package.json (no Vitest dependency
present). Ensure the corrected sentence explicitly mentions "ng test" and
"@angular/build:unit-test" so readers aren’t misled.
In `@examples/angular/a11y-devtools/src/index.html`:
- Line 5: The page title currently reads "Basic" and should be updated to
reflect the example name; change the <title> element in index.html to a
meaningful name like "a11y-devtools" (or "a11y-devtools — Accessibility DevTools
Example") so the document title matches the project; locate the <title> tag in
index.html and replace its content accordingly.
In `@examples/angular/basic/src/app/app.ts`:
- Around line 7-10: The `@Component` decorator currently includes an imports
property (imports: [TanStackDevtools]) but is missing standalone: true, which is
required when using imports in component metadata; update the `@Component`
metadata for this component to add standalone: true alongside the existing
imports so the decorator is valid (i.e., modify the `@Component`({...}) that
contains imports: [TanStackDevtools] to include standalone: true).
In `@packages/angular-devtools/src/types.ts`:
- Around line 46-62: The JSDoc example incorrectly references a non-existent
`component` property for TanStackDevtoolsAngularPlugin; update the example to
use the actual `render` property defined on TanStackDevtoolsAngularPlugin
(replace `{ name: 'My Plugin', component: MyPluginComponent }` with `{ name: 'My
Plugin', render: MyPluginComponent }`) so the docs match the
`TanStackDevtoolsAngularPlugin` type and `render` usage.
In `@packages/devtools-a11y/src/angular/index.ts`:
- Around line 7-13: The module-level conditionals using isDevMode() (in exports
A11yDevtoolsPanel and a11yDevtoolsPlugin) won't be tree-shaken; replace the
runtime isDevMode() call with a build-time boolean (e.g. a __DEV__ or PROD
constant provided via environment/file-replacement/DefinePlugin) or convert the
exports to lazy accessor functions so evaluation happens at call time; update
the two exports (A11yDevtoolsPanel and a11yDevtoolsPlugin) to use that
build-time constant (or wrap them in functions like
getA11yDevtoolsPanel()/getA11yDevtoolsPlugin) and configure your Angular
file-replacement/webpack define plugin to supply the constant so production
builds can dead-code eliminate the no-op branches.
---
Duplicate comments:
In `@packages/devtools-utils/src/angular/panel.ts`:
- Around line 62-64: The cleanup returned by the panel mounting function
currently only calls unmount?.() and never removes the panel DOM node, leaving
orphaned elements; update the returned function so after calling unmount?.() it
also removes the panel element (e.g., check the panel variable and call
panel.remove() or parent.removeChild(panel) as appropriate), and guard against
panel being null/already removed so this works both in synchronous and async
paths (referencing the unmount and panel variables in the mount/cleanup
closure).
---
Nitpick comments:
In `@examples/angular/a11y-devtools/angular.json`:
- Around line 12-14: Rename the Angular project entry named "basic" under
"projects" to "a11y-devtools" and update any build target references that still
point to "basic:build:..." to "a11y-devtools:build:..." so the project name
matches the example directory; ensure you update the project name string and all
occurrences of the buildTarget values (e.g., "basic:build:...") to the new
"a11y-devtools:build:..." identifier.
In `@examples/angular/a11y-devtools/package.json`:
- Around line 20-22: The package.json dependency for
`@tanstack/devtools-event-client` uses an exact version "0.4.2" while neighboring
TanStack deps use caret ranges; update the dependency entry for
`@tanstack/devtools-event-client` to use a caret range (e.g., ^0.4.2) to match the
others (`@tanstack/angular-devtools` and `@tanstack/devtools-a11y`) and ensure
consistent semver behavior.
In `@examples/angular/a11y-devtools/README.md`:
- Around line 1-59: The README currently only has generic Angular CLI
instructions; add an "About a11y-devtools" section that briefly explains the
purpose of this example (demonstrates integrating a11y-devtools with TanStack
Devtools), a short "How to use" with steps referencing existing commands (e.g.,
run ng serve, open http://localhost:4200), notes on where to find demo
components/pages in the example, and a small "Devtools usage" subsection that
explains how to open/enable the TanStack Devtools panel and the a11y-devtools
accessibility panel (and any optional config flags or environment variables the
example supports). Include a pointer to relevant docs (TanStack
Devtools/a11y-devtools) and mention the example name "a11y-devtools" so
contributors can quickly locate the integration code.
In `@examples/angular/a11y-devtools/src/app/counter.ts`:
- Around line 15-18: Pass a shallow copy of the history array instead of the
original when emitting events to avoid downstream mutation: update the
DevtoolsEventClient.emit calls (used in the increment and decrement flows) to
send history.slice() (or [...history]) in the payload alongside count(), so the
emitted object contains an immutable snapshot rather than a reference to the
live history array.
In `@packages/angular-devtools/src/types.ts`:
- Around line 30-35: Replace the inconsistent Type<any> usage in the
TanStackDevtoolsAngularPluginRender type with Type<unknown> to match the other
declarations; update the union member in the TanStackDevtoolsAngularPluginRender
type so it uses Type<unknown> (keeping the rest of the union, including the
function return types TanStackDevtoolsAngularPluginRenderFn and Promise<...>,
unchanged).
In `@packages/devtools-a11y/package.json`:
- Line 109: The peerDependencies entry for "@angular/core" has a missing space
after the colon; open package.json and in the peerDependencies object adjust the
"@angular/core" entry (the string key "@angular/core") so the value is formatted
with a space after the colon (make it consistent with other entries by changing
":"">=19.0.0"," to ": ">=19.0.0",).
In `@packages/devtools-utils/src/angular/panel.ts`:
- Around line 18-23: Replace the fragile check in isPanelClassConstructor with a
robust constructor test: verify o is a function (typeof o === 'function'), that
o.prototype exists, and that o.prototype.constructor === o (or that o.prototype
implements expected instance members from BaseCorePanelClass). Update the
function isPanelClassConstructor to use these checks (referencing
isPanelClassConstructor, CoreClassConstructor, and BaseCorePanelClass) so it
rejects plain objects with a prototype property and non-constructable values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0279b535-5010-483f-be85-6fc186968d18
⛔ Files ignored due to path filters (2)
examples/angular/a11y-devtools/public/favicon.icois excluded by!**/*.icopnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (33)
examples/angular/a11y-devtools/.editorconfigexamples/angular/a11y-devtools/.gitignoreexamples/angular/a11y-devtools/.prettierrcexamples/angular/a11y-devtools/.vscode/extensions.jsonexamples/angular/a11y-devtools/.vscode/launch.jsonexamples/angular/a11y-devtools/.vscode/mcp.jsonexamples/angular/a11y-devtools/.vscode/tasks.jsonexamples/angular/a11y-devtools/README.mdexamples/angular/a11y-devtools/angular.jsonexamples/angular/a11y-devtools/package.jsonexamples/angular/a11y-devtools/src/app/app.config.tsexamples/angular/a11y-devtools/src/app/app.tsexamples/angular/a11y-devtools/src/app/counter.tsexamples/angular/a11y-devtools/src/app/devtools/custom-devtools-panel.tsexamples/angular/a11y-devtools/src/app/devtools/eventClient.tsexamples/angular/a11y-devtools/src/index.htmlexamples/angular/a11y-devtools/src/main.tsexamples/angular/a11y-devtools/src/styles.cssexamples/angular/a11y-devtools/tsconfig.app.jsonexamples/angular/a11y-devtools/tsconfig.jsonexamples/angular/a11y-devtools/tsconfig.spec.jsonexamples/angular/basic/src/app/app.tsexamples/angular/panel/src/app/app.tspackages/angular-devtools/src/types.tspackages/devtools-a11y/package.jsonpackages/devtools-a11y/src/angular/A11yDevtools.tspackages/devtools-a11y/src/angular/index.tspackages/devtools-a11y/src/angular/plugin.tspackages/devtools-a11y/src/angular/production.tspackages/devtools-a11y/src/angular/production/A11yDevtools.tspackages/devtools-a11y/src/angular/production/plugin.tspackages/devtools-a11y/vite.config.tspackages/devtools-utils/src/angular/panel.ts
✅ Files skipped from review due to trivial changes (4)
- examples/angular/a11y-devtools/tsconfig.spec.json
- examples/angular/a11y-devtools/src/styles.css
- examples/angular/a11y-devtools/tsconfig.app.json
- examples/angular/a11y-devtools/.vscode/extensions.json
🚧 Files skipped from review as they are similar to previous changes (1)
- examples/angular/panel/src/app/app.ts
| @@ -0,0 +1,59 @@ | |||
| # Basic | |||
There was a problem hiding this comment.
Update the title to match the example name.
The title "Basic" doesn't match the directory name a11y-devtools. This appears to have been copied from another example without updating the title.
📝 Proposed fix
-# Basic
+# A11y Devtools📝 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.
| # Basic | |
| # A11y Devtools |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/angular/a11y-devtools/README.md` at line 1, Replace the top-level
Markdown title "Basic" in the README with a title that matches the example
directory name (e.g., "a11y-devtools" or "a11y-devtools — Accessibility DevTools
Example") so the README heading aligns with the example; locate the header
string "Basic" in the README.md and update it accordingly.
| @@ -0,0 +1,59 @@ | |||
| # Basic | |||
|
|
|||
| This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.0. | |||
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check Angular CLI version and test runner configuration
# Check package.json for Angular CLI version and test dependencies
cat examples/angular/a11y-devtools/package.json | jq '{
devDependencies: .devDependencies | with_entries(select(.key | contains("angular") or contains("vitest") or contains("karma") or contains("jasmine") or contains("jest"))),
scripts: .scripts | with_entries(select(.key == "test"))
}'
# Check angular.json for test configuration
if [ -f examples/angular/a11y-devtools/angular.json ]; then
cat examples/angular/a11y-devtools/angular.json | jq '.projects[].architect.test'
fiRepository: TanStack/devtools
Length of output: 280
🏁 Script executed:
cat examples/angular/a11y-devtools/README.md | head -n 50 | cat -nRepository: TanStack/devtools
Length of output: 1529
Fix README: Remove incorrect Vitest reference in test runner documentation.
Line 41 incorrectly claims Vitest is the test runner, but the project is actually configured to use Angular's standard test setup (@angular/build:unit-test). The package.json contains no Vitest dependency and the test script runs ng test. Update line 41 to accurately reflect the actual testing configuration.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/angular/a11y-devtools/README.md` at line 3, Update the README line
that claims Vitest is the test runner to correctly state the project uses
Angular's standard unit test setup; replace the Vitest reference with a note
that tests run via the npm test script (runs "ng test") and that the project
uses the Angular unit test builder (`@angular/build`:unit-test) as shown in
package.json (no Vitest dependency present). Ensure the corrected sentence
explicitly mentions "ng test" and "@angular/build:unit-test" so readers aren’t
misled.
| <html lang="en"> | ||
| <head> | ||
| <meta charset="utf-8" /> | ||
| <title>Basic</title> |
There was a problem hiding this comment.
Update the page title to reflect the example name.
The title is set to "Basic" but this is the a11y-devtools example. This appears to be leftover from copying the template.
Suggested fix
- <title>Basic</title>
+ <title>A11y Devtools</title>📝 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.
| <title>Basic</title> | |
| <title>A11y Devtools</title> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/angular/a11y-devtools/src/index.html` at line 5, The page title
currently reads "Basic" and should be updated to reflect the example name;
change the <title> element in index.html to a meaningful name like
"a11y-devtools" (or "a11y-devtools — Accessibility DevTools Example") so the
document title matches the project; locate the <title> tag in index.html and
replace its content accordingly.
| @Component({ | ||
| selector: 'app-root', | ||
| imports: [TanStackDevtools], | ||
| template: ` |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Read-only check: detect `@Component` blocks that use `imports` without `standalone: true`
python - <<'PY'
import re
import pathlib
import sys
violations = []
for path in pathlib.Path(".").rglob("*.ts"):
text = path.read_text(encoding="utf-8", errors="ignore")
for m in re.finditer(r'@Component\s*\(\s*\{', text):
i = m.end()
depth = 1
while i < len(text) and depth > 0:
if text[i] == '{':
depth += 1
elif text[i] == '}':
depth -= 1
i += 1
block = text[m.end():i-1]
has_imports = re.search(r'\bimports\s*:', block) is not None
has_standalone_true = re.search(r'\bstandalone\s*:\s*true\b', block) is not None
if has_imports and not has_standalone_true:
line = text.count('\n', 0, m.start()) + 1
violations.append(f"{path}:{line}")
if violations:
print("Found components using `imports` without `standalone: true`:")
print("\n".join(violations))
sys.exit(1)
print("No violations found.")
PYRepository: TanStack/devtools
Length of output: 512
🏁 Script executed:
cat -n examples/angular/basic/src/app/app.ts | head -15Repository: TanStack/devtools
Length of output: 772
🏁 Script executed:
cat -n examples/angular/basic/src/app/app.ts | head -40Repository: TanStack/devtools
Length of output: 1535
Add standalone: true when using imports in @Component.
Line 9 defines imports, but the decorator metadata does not set standalone: true. In Angular, this makes the imports property invalid and causes a TypeScript compilation error.
✅ Proposed fix
`@Component`({
selector: 'app-root',
+ standalone: true,
imports: [TanStackDevtools],
template: `📝 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.
| @Component({ | |
| selector: 'app-root', | |
| imports: [TanStackDevtools], | |
| template: ` | |
| `@Component`({ | |
| selector: 'app-root', | |
| standalone: true, | |
| imports: [TanStackDevtools], | |
| template: ` |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/angular/basic/src/app/app.ts` around lines 7 - 10, The `@Component`
decorator currently includes an imports property (imports: [TanStackDevtools])
but is missing standalone: true, which is required when using imports in
component metadata; update the `@Component` metadata for this component to add
standalone: true alongside the existing imports so the decorator is valid (i.e.,
modify the `@Component`({...}) that contains imports: [TanStackDevtools] to
include standalone: true).
| /** | ||
| * Array of plugins to be used in the devtools. | ||
| * Each plugin should have a `component` that is an Angular component class. | ||
| * | ||
| * Example: | ||
| * ```typescript | ||
| * @Component({ | ||
| * template: `<tanstack-devtools [plugins]="plugins" />`, | ||
| * imports: [TanStackDevtoolsComponent], | ||
| * }) | ||
| * class AppComponent { | ||
| * plugins: TanStackDevtoolsAngularPlugin[] = [ | ||
| * { name: 'My Plugin', component: MyPluginComponent } | ||
| * ]; | ||
| * } | ||
| * ``` | ||
| */ |
There was a problem hiding this comment.
JSDoc example uses non-existent component property.
The example shows { name: 'My Plugin', component: MyPluginComponent } but TanStackDevtoolsAngularPlugin (lines 37-43) defines render, not component. This will mislead users.
📝 Proposed fix to correct the documentation
* Example:
* ```typescript
* `@Component`({
* template: `<tanstack-devtools [plugins]="plugins" />`,
* imports: [TanStackDevtoolsComponent],
* })
* class AppComponent {
* plugins: TanStackDevtoolsAngularPlugin[] = [
- * { name: 'My Plugin', component: MyPluginComponent }
+ * { name: 'My Plugin', render: MyPluginComponent }
* ];
* }
* ```🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/angular-devtools/src/types.ts` around lines 46 - 62, The JSDoc
example incorrectly references a non-existent `component` property for
TanStackDevtoolsAngularPlugin; update the example to use the actual `render`
property defined on TanStackDevtoolsAngularPlugin (replace `{ name: 'My Plugin',
component: MyPluginComponent }` with `{ name: 'My Plugin', render:
MyPluginComponent }`) so the docs match the `TanStackDevtoolsAngularPlugin` type
and `render` usage.
| export const A11yDevtoolsPanel = !isDevMode() | ||
| ? Devtools.A11yDevtoolsPanelNoOp | ||
| : Devtools.A11yDevtoolsPanel | ||
|
|
||
| export const a11yDevtoolsPlugin = !isDevMode() | ||
| ? plugin.a11yDevtoolsNoOpPlugin | ||
| : plugin.a11yDevtoolsPlugin |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Angular isDevMode() module evaluation time vs runtime
💡 Result:
isDevMode() is runtime, not “Angular compile-time”.
-
When it runs (module evaluation time): If you call
isDevMode()at the top level of an ES module (outside any function/class), it executes when that module is imported and evaluated by the JS runtime (i.e., during app startup / chunk load). That’s still runtime—just early runtime. -
What it checks:
isDevMode()returnstrueunless the globalngDevModeisfalse/ unset in a way that makes it falsy. Concretely, it’s implemented astypeof ngDevMode === 'undefined' || !!ngDevMode.enableProdMode()setsglobal['ngDevMode'] = false(but must happen before Angular is initialized). [1], [2] -
Why it usually won’t tree-shake: Minifiers/tree-shakers can only reliably remove branches when they see a compile-time constant. A call like
if (isDevMode()) { ... }is a function call, so it’s generally treated as needing a runtime value and won’t be dropped just because you built with prod optimizations. This is whyif (!environment.production) { ... }(which can be inlined tofalse) often does get removed. [3] -
Related:
ngDevModeand build optimizers: In Angular CLI prod builds,ngDevModecan be replaced via optimizer/minifier global definitions, but that happens after Angular’s compilation/transforms—songDevModeis not something Angular can statically evaluate inside decorators/metadata (e.g., in@NgModule({ imports: ... })). If you need decorator-level conditional inclusion with tree-shaking, use a DefinePlugin-style compile-time constant (or other build-time configuration), notisDevMode()/ngDevMode. [4]
Rule of thumb
- Use
isDevMode()for runtime behavior toggles (logging, dev-only checks). - Use a build-time constant (file replacements / define) when you need dead-code elimination / conditional provider/module wiring. [3], [4]
Sources
- Angular
isDevMode()API docs (notes CLIoptimization/enableProdMode). [1] - Angular core source for
isDevMode()/enableProdMode()implementation. [2] - Explanation of why
isDevMode()conditions aren’t tree-shaken (function call vs inlined constant). [3] - Explanation that
ngDevModeis only replaced by minifier globals and isn’t statically usable in decorators. [4]
[1] (angular.dev)
[2] (raw.githubusercontent.com)
[3] (stackoverflow.com)
[4] (ng-guru.io)
Citations:
- 1: https://angular.dev/api/core/isDevMode
- 2: https://raw.githubusercontent.com/angular/angular/main/packages/core/src/util/is_dev_mode.ts
- 3: https://stackoverflow.com/questions/63654038/conditional-using-isdevmode-is-not-being-tree-shaked
- 4: https://ng-guru.io/guide/webpack/
isDevMode() won't be tree-shaken in production builds; use a build-time constant instead.
While isDevMode() does execute at module evaluation time (early runtime), it's a function call that won't be eliminated during tree-shaking in production builds. For module-level conditional exports, Angular recommends using build-time constants (file replacements or DefinePlugin) to achieve proper dead-code elimination.
Consider either:
- Using a build-time environment constant similar to the React pattern
- Making the exports functions that lazily evaluate at call time
- Using Angular's file replacement or optimizer strategies for conditional module wiring
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/devtools-a11y/src/angular/index.ts` around lines 7 - 13, The
module-level conditionals using isDevMode() (in exports A11yDevtoolsPanel and
a11yDevtoolsPlugin) won't be tree-shaken; replace the runtime isDevMode() call
with a build-time boolean (e.g. a __DEV__ or PROD constant provided via
environment/file-replacement/DefinePlugin) or convert the exports to lazy
accessor functions so evaluation happens at call time; update the two exports
(A11yDevtoolsPanel and a11yDevtoolsPlugin) to use that build-time constant (or
wrap them in functions like getA11yDevtoolsPanel()/getA11yDevtoolsPlugin) and
configure your Angular file-replacement/webpack define plugin to supply the
constant so production builds can dead-code eliminate the no-op branches.


The following PR continues the work of #364 to improve and fix angular integration:
Added some examples:
Summary by CodeRabbit
New Features
Documentation
Examples
Chores