These rules go together with the Angular rules in AGENTS.md. Prettier formats everything; run pnpm format before you commit.
- Strict mode everywhere. Avoid
any; useunknownand narrow it, or a small local interface. - Prefer
const. Usereadonlyfor members that never change. - Names describe what a thing is or does. Spell words out. Booleans start with
is,hasorshould. - No
Iprefix on interfaces and no$suffix on observables. - Prefer
for...ofoverforEachwhen the loop has side effects. - Only catch errors you expect, and say what happens instead (fall back, skip, report).
- Comment the why, not the what. Public functions and exported types in
packages/ng-devtoolsget a short doc comment when the name alone doesn't explain them.
- Standalone components,
input(),output(),model(),computed(),linkedSignal()andinject(). - Don't set
standalone: trueorchangeDetection: OnPush; both are defaults. - Native control flow (
@if,@for,@switch);classandstylebindings instead ofngClassandngStyle. - Host bindings go in the
hostobject, never@HostBindingor@HostListener. - Derived state is a
computed(). Don't write signals from aneffect()when acomputed()orlinkedSignal()would do. - Clean up subscriptions and listeners with
DestroyRef.
The panel's visual rules are in the UI guidelines.
The overlay runs inside the user's app, so it must be correct, cheap and invisible.
- Use Angular's debug APIs, not guesses:
ng.getComponent,ng.getDirectives,ng.getDirectiveMetadata,ng.getListeners,ng.getInjector,ng.ɵgetInjectorProviders,ng.ɵgetInjectorResolutionPath,ng.ɵgetDependenciesFromInjectable,ng.ɵgetSignalGraph. Check the shapes innode_modules/@angular/core/fesm2022before relying on a field. - Never write to the app's DOM. Give elements and objects ids through a
WeakMap(element-id.ts,idForininjector-tree.ts), so ids stay stable between pushes and selection survives a refresh. - Strip bundler prefixes from class names (
className()ininjector-tree.ts), so_Appshows asApp. - Don't match
_nghost-*or_ngcontent-*attributes with exact selectors; their names carry a suffix. Start from[ng-version]roots and the debug APIs. - Keep pushes cheap. Skip unchanged reports (compare with the last JSON sent), re-send now and then so the server doesn't expire the page, and avoid
querySelectorAll('*')on a timer; cache and rescan on aMutationObserversignal. - Serialize safely with
serialize()fromserialize.ts: it limits depth and size and handlesMap,Set,Date, class instances, cycles and Angular's signal sentinels. - Every report carries a
pageId. The server keeps one entry per page, expires it after 15 seconds without a push, and forgets it onpagehide(forget-<area>-page). Two open tabs must never overwrite each other. - Don't change app behavior. Don't run the app's validators, guards or effects to learn something unless the user asked for it (recording, instrumenting), and say so in the UI.
- Register RPC functions with
defineRpcFunctionand valibot schemas; give them anagentdescription when an agent should call them. - Agent tools (
ctx.agent.registerTool) answer in markdown, say where the data came from (live page or source scan), and say what to do when there is no data. - Source scans (
rpc/source-scan.tsand friends) are a fallback and an enrichment for live data, not a replacement.
- Every behavior change comes with a test in
packages/ng-devtools/src/__tests__orpackages/ng-devtools/src/rpc/__tests__. - Page-side collectors are tested in jsdom with a fake
ngobject; seeinjector-tree.test.tsandcomponent-tree.test.ts. - Test names read as sentences:
it('keeps ids stable between collections'). - Run
pnpm test:devtools,pnpm test:panelandpnpm testbefore you push.