@ngcompass/rules 0.1.1-beta → 0.1.3-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,217 @@
1
+ import {groupTasksByFile}from'@ngcompass/planner';import {createAnyAngularClassRule,createCallExpressionRule,createComponentRule,createTemplateExpressionRule,createTemplateRule,createTemplateAttributeRule,configureRuleExecutor,createTypeAwareAnalysisContext,executeBatchedTasks,runSinglePassAnalysis}from'@ngcompass/engine';import {debug}from'@ngcompass/common';import {analyzeComponent,ChangeDetectionStrategy}from'@ngcompass/ast';import Jt from'typescript';var wt=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,r)=>(typeof require<"u"?require:e)[r]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var he=class{_handlers=new Map;_meta=new Map;register(e,r={}){if(this._handlers.has(e.name)&&!r.allowOverride)throw Error(`[ngcompass] Rule "${e.name}" is already registered. Use allowOverride: true to replace an existing rule intentionally.`);this._handlers.set(e.name,e.handler),e.meta&&this._meta.set(e.name,e.meta);}get(e){return this._handlers.get(e)}has(e){return this._handlers.has(e)}getRuleNames(){return Array.from(this._handlers.keys())}getAll(){return this._handlers}getMeta(e){return this._meta.get(e)}getMetadata(e){if(!this._handlers.has(e))return;let r=this._meta.get(e)??{};return {name:e,description:r.description||`Rule: ${e}`,category:r.category||"general",dependencyType:r.dependencyType||"standalone",requires:{tsAst:true,...r.requires},filePatterns:r.filePatterns}}getRegistryEntry(e){let r=this.getMetadata(e);if(r)return {name:e,metadata:r,defaultConfig:{severity:"warn",options:{}}}}toReadonlyMap(){let e=new Map;for(let r of this._handlers.keys()){let n=this.getRegistryEntry(r);n&&e.set(r,n);}return e}get size(){return this._handlers.size}},ye=null,X=()=>(ye||(ye=new he),ye);var C=(t,e)=>{let r={name:t.name,handler:t,meta:{dependencyType:"component",...t.meta,category:e??t.meta?.category??"best-practice",requires:{...t.meta?.requires}}};X().register(r),debug("engine",`Registered rule: ${t.name}`);},Ie=t=>X().has(t);var ke=(t,e)=>{let r=X(),n=[];for(let u of t){let a=r.get(u);a&&n.push(a);}if(n.length===0)return [];debug("engine",`Executing ${n.length} rules in single pass on ${e.filePath}`);let{results:o,performance:l}=runSinglePassAnalysis(n,e);return debug("engine",`Single-pass complete: ${l.traversalMs.toFixed(2)}ms, ${l.nodesVisited} nodes`),debug("engine",`Cache hit rate: ${(l.cacheStats.hits/(l.cacheStats.hits+l.cacheStats.misses||1)*100).toFixed(1)}%`),l.budgetViolations.length>0&&debug("engine","Performance budget violations:",l.budgetViolations),o};var De;function s(t){let e=t;for(;e;){let r=e.type;if(r==="ChainExpression"||r==="TSNonNullExpression"||r==="TSAsExpression"||r==="ParenthesizedExpression"||r==="TSInstantiationExpression"||r==="TSSatisfiesExpression"){e=e.expression;continue}break}return e??null}function x(t){let e=t?.type;return e==="MemberExpression"||e==="StaticMemberExpression"||e==="OptionalMemberExpression"}function v(t){if(!t)return "";let e=t.property;return e?t.computed?e.type==="Literal"&&typeof e.value=="string"?e.value:"":e.name??"":""}function b(t){return t?t.start??t.span?.start??0:0}function O(t,e){let r=s(t);return !!r&&(r.type==="Identifier"?(r.name??"")===e:!!x(r)&&v(r)===e)}function j(t){let e=s(t);if(!e||e.type!=="CallExpression")return "";let r=s(e.callee);return r?.type==="Identifier"?r.name??"":x(r)?v(r):""}function Y(t){let e=s(t?.callee);return !!x(e)&&v(e)==="subscribe"}function W(t,e){if(!t||t.type!=="ObjectExpression")return null;let r=t.properties;if(!Array.isArray(r))return null;for(let n of r){if(!n||n.type!=="Property")continue;let o=n.key;if((o?.type==="Identifier"?o.name:o?.type==="Literal"&&typeof o.value=="string"?o.value:"")===e)return n}return null}function ge(t){let e=s(t);return !!(e&&e.type==="Literal"&&e.value===true)}function Ee(t){let e=s(t);return !e||e.type==="Literal"&&e.value===null||e.type==="Identifier"&&e.name==="undefined"}var Pt=new Set(["parent","span","loc","range","start","end","type"]);function*N(t){if(t&&typeof t=="object"){if(Array.isArray(t)){for(let e of t)e&&typeof e=="object"&&(yield e);return}for(let e in t){if(Pt.has(e))continue;let r=t[e];if(r)if(Array.isArray(r))for(let n of r)n&&typeof n=="object"&&(yield n);else typeof r=="object"&&r.type&&(yield r);}}}function xe(t){if(!t)return false;if(t.kind==="constructor")return true;let e=t.key;return e?.type==="Identifier"&&e.name==="constructor"||e?.type==="Literal"&&e.value==="constructor"}function B(t){if(!t)return false;let e=t.type;return e==="MethodDefinition"||e==="TSAbstractMethodDefinition"||e==="ClassMethod"||e==="MethodDeclaration"}function U(t){for(let e of t)if(e&&B(e)&&xe(e))return e;return null}function L(t){return t?(t.value??t)?.body??null:null}function Le(t){let e=t?.key;return e?e.type==="Identifier"?e.name??"<method>":e.type==="Literal"&&typeof e.value=="string"?e.value:"<method>":"<method>"}function w(t){if(!t)return [];let e=t.body;if(Array.isArray(e))return e;if(e&&typeof e=="object"&&"body"in e){let r=e.body;if(Array.isArray(r))return r}return []}function Ot(t){let e=s(t);if(!e||e.type!=="CallExpression")return false;let r=e.arguments;if(!Array.isArray(r)||r.length<2)return false;let n=s(r[1]);if(!n||n.type!=="ObjectExpression")return false;let o=W(n,"injector");if(o&&!Ee(o.value))return true;let l=W(n,"manualCleanup");return !!(l&&ge(l.value))}function $e(t){if(!t)return [];let e=[],r=[t];for(;r.length;){let n=s(r.pop());if(n)for(let o of(n.type==="CallExpression"&&O(n.callee,"effect")&&!Ot(n)&&e.push(n),N(n)))r.push(o);}return e}function G(t){let e=t?.arguments;if(!Array.isArray(e)||e.length===0)return null;let r=s(e[0]);return r&&(r.type==="ArrowFunctionExpression"||r.type==="FunctionExpression")?r:null}function K(t){if(!t)return null;let e=t.body;return e?s(e):null}function _e(t,e){let r=new Map([...e].map(o=>[o,new Set([o])]));if(typeof t!="string"||t.length===0)return r;let n=new Map([...e].map(o=>[o,RegExp(`^${o}(?:\\s+as\\s+([A-Za-z_$][\\w$]*))?$`)]));for(let o of [/import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]rxjs['"]/g,/import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]rxjs\/[^'"]+['"]/g]){let l;for(;(l=o.exec(t))!==null;)for(let u of (l[1]??"").split(",").map(a=>a.trim()).filter(Boolean))for(let[a,c]of n){let p=c.exec(u);p&&r.get(a).add(p[1]??a);}}return r}var Tt=new Set(["takeUntilDestroyed","takeUntil","take","first","takeWhile"]);function It(t){let e=s(t);if(!e)return "";if(e.type==="Identifier")return e.name??"";if(e.type==="CallExpression"){let r=s(e.callee);return r?.type==="Identifier"?r.name??"":x(r)?v(r):""}return x(e)?v(e):""}function kt(t){let e=t.arguments;if(!Array.isArray(e))return false;for(let r of e)if(Tt.has(It(r)))return true;return false}function Z(t){let e=t;for(;e;){let r=s(e);if(!r||r.type!=="CallExpression")break;let n=s(r.callee);if(!x(n))break;if(v(n)==="pipe"&&kt(r))return true;e=n?.object;}return false}function k(t,e){let r=t.template?.templateStartOffset;return typeof r=="number"&&Number.isFinite(r)?e+r:e}var Dt=new Set(["get","post","put","patch","delete","head","options","jsonp","request"]),jt=new Set(["http","httpClient","_http","_httpClient"]),Lt=["get","fetch","load","save","create","update","delete","remove","submit","send","post","put","patch","upload","download"];function Q(t){let e=t;for(;e;){let r=s(e);if(!r||r.type!=="CallExpression")break;let n=s(r.callee);if(!x(n))break;if(v(n)!=="pipe")return r;e=n?.object;}return null}function J(t){if(!t)return false;let e=s(t);if(!e||e.type!=="CallExpression")return false;let r=s(e.callee);if(!x(r))return false;let n=v(r);if(!n)return false;let o=s(r?.object),l=Array.isArray(e.arguments)?e.arguments:[];if(Dt.has(n)&&x(o)){let u=v(o);if(jt.has(u))return true}if(l.length>0){let u=n.toLowerCase();for(let a of Lt)if(u.startsWith(a)&&n.length>a.length)return true}return false}function ee(t){if(!t)return [];let e=t.params;if(Array.isArray(e))return e;if(e&&typeof e=="object"){if("items"in e&&Array.isArray(e.items))return e.items;if("elements"in e&&Array.isArray(e.elements))return e.elements}return []}function F(t){let e=s(t);if(!e)return "";if(e.type==="Identifier")return e.name??"";if(e.type==="TSParameterProperty")return F(e.parameter);if(e.type==="AssignmentPattern"){let r=s(e.left);return r?.type==="Identifier"?r.name??"":""}if(e.type==="RestElement"){let r=s(e.argument);return r?.type==="Identifier"?r.name??"":""}return ""}function $(t){let e=s(t);if(!e)return "";if(e.type==="TSParameterProperty")return $(e.parameter);let r=e.typeAnnotation,n=s(r?.typeAnnotation??r);if(!n)return "";if(n.type==="TSTypeReference"||n.type==="TypeReference"){let o=n.typeName??n.name;if(o&&typeof o=="object"){if(o.type==="Identifier")return o.name??"";if(o.type==="TSQualifiedName")return o.right?.name??""}if(typeof o=="string")return o}return ""}var je=false;function Be(){if(!je){try{De=wt("typescript");}catch{}je=true;}return De}function q(t,e){if(!t||!e.typeChecker)return;let r=Be();if(r)try{!e.sourceFile&&e.fileContent&&(e.sourceFile=r.createSourceFile(e.filePath,e.fileContent,r.ScriptTarget.Latest,!0));let n=e.sourceFile;if(!n)return;let o=(function(l,u,a){if(u<l.getStart()||u>=l.getEnd())return;let c=l;for(;;){let p=a.forEachChild(c,i=>{if(u>=i.getStart()&&u<i.getEnd())return i});if(!p)break;c=p;}return c})(n,b(t),r);return o?e.typeChecker.getSymbolAtLocation(o):void 0}catch{return}}var $t=["Service","Facade","Store","Client","Repository","Adapter","Controller","Provider","Registry","Logger","Router","Injector","Handler","Interceptor","Guard","Resolver","Validator"];function Ue(t){if(!t)return false;let e=Be();if(!e)return false;try{let r=t.getName();if($t.some(l=>r.endsWith(l)))return !0;let n=t.getDeclarations();if(!n||n.length===0)return !1;let o=n[0];if(!e.canHaveDecorators(o))return !1;for(let l of e.getDecorators(o)??[]){let u=l.expression;if(e.isCallExpression(u)&&e.isIdentifier(u.expression)&&u.expression.text==="Injectable")return !0}return !1}catch{return false}}var E={"prefer-on-push-component-change-detection":"Add `changeDetection: ChangeDetectionStrategy.OnPush` to the component metadata.","template-no-call-expression":"Move the value to a pipe, computed signal, or cached component property.","rxjs-no-subscribe-in-component":"Use `toSignal()` or the `async` pipe for view state; add `takeUntilDestroyed()` for long-lived imperative subscriptions.","template-trackby-required-for-ngfor":"Add `trackBy` to `*ngFor`, or use `track` when migrating to `@for`.","template-no-object-literal-binding":"Move the object to a component property, signal, computed value, or pure pipe.","template-no-array-literal-binding":"Move the array to a component property, signal, computed value, or pure pipe.","toSignal-require-initialValue":"Pass `{ initialValue }` or `{ requireSync: true }` so the signal has a stable type.","rxjs-avoid-subject-as-event-bus":"Use `signal()` for component UI state; move complex async pipelines into a service.","signal-no-side-effects-in-computed":"Move side effects to an `effect()` or method, and keep `computed()` pure.","signal-no-writes-in-computed":"Move `.set()` or `.update()` calls to an `effect()` or event handler.","prefer-inject-over-constructor-di":"Replace constructor parameters with field initializers that call `inject()`.","component-no-manual-detect-changes":"Use signals, `async` pipe, or input-driven state instead of calling `detectChanges()` or `markForCheck()`.","rxjs-require-takeUntilDestroyed":"Add `takeUntilDestroyed()` or another teardown operator before `subscribe()`.","template-no-async-pipe-duplication":"Store the async value once with `@if (... | async; as value)` or a view-model signal.","rxjs-prefer-toSignal-for-template-state":"Convert template-used observables to `toSignal()` and read them as signals in the template.","signal-effect-must-be-destroy-scoped":"Create the effect in an injection context or pass an explicit `{ injector }`.","signal-no-effect-in-constructor":"Move the `effect()` call to a field initializer.","signal-prefer-computed-over-sync-effect":"Replace the write-producing `effect()` with a `computed()` signal.","signal-avoid-untracked-overuse":"Remove `untracked()` unless the dependency must be intentionally ignored.","template-prefer-control-flow":"Replace the legacy directive with the matching `@if`, `@for`, or `@switch` block.","signal-prefer-input-signal":"Replace `@Input()` with `input()` or `input.required()`.","signal-prefer-output-function":"Replace `@Output() EventEmitter` with `output<T>()`.","no-bypass-sanitization":"Use Angular sanitization or a trusted server-side sanitizer instead of `bypassSecurityTrust*`.","rxjs-no-nested-subscribe":"Flatten the stream with `switchMap`, `mergeMap`, `concatMap`, or `exhaustMap`.","no-document-access":"Inject `DOCUMENT` or move browser-only DOM work into `afterNextRender()`.","template-no-unsafe-bindings":"Sanitize the value before binding, or replace the raw HTML binding with structured template content.","signal-prefer-model":"Replace the `@Input()` / `@Output()Change` pair with `model()`.","prefer-after-render-over-after-view-init":"Move browser-only DOM access into `afterNextRender()`.","spec-no-focused-test":"Replace focused or disabled test helpers with normal `describe` and `it` calls."},R={"prefer-on-push-component-change-detection":`// Before:
2
+ @Component({ selector: 'app-foo', template: '...' })
3
+ export class FooComponent { }
4
+
5
+ // After:
6
+ @Component({
7
+ selector: 'app-foo',
8
+ template: '...',
9
+ changeDetection: ChangeDetectionStrategy.OnPush,
10
+ })
11
+ export class FooComponent { }`,"rxjs-no-subscribe-in-component":`// Reactive state (data derived from a stream) \u2192 use toSignal() or async pipe:
12
+ // Before:
13
+ ngOnInit() { this.data$.subscribe(val => this.value = val); }
14
+ // After:
15
+ value = toSignal(this.data$, { initialValue: defaultVal });
16
+
17
+ // Imperative action (user-triggered, one-shot) \u2192 subscription is fine, add teardown:
18
+ // Before:
19
+ onSave() { this.api.save(this.form.value).subscribe(res => this.notify(res)); }
20
+ // After (HTTP auto-completes \u2014 takeUntilDestroyed optional but harmless):
21
+ onSave() {
22
+ this.api.save(this.form.value)
23
+ .pipe(takeUntilDestroyed(this.destroyRef))
24
+ .subscribe(res => this.notify(res));
25
+ }`,"rxjs-avoid-subject-as-event-bus":`// Case A \u2014 UI state: replace with signal()
26
+ // Before:
27
+ private loading$ = new Subject<boolean>();
28
+ // After:
29
+ loading = signal(false);
30
+
31
+ // Case B \u2014 complex async pipeline: move to a service
32
+ // Before (in component):
33
+ private search$ = new Subject<string>();
34
+ ngOnInit() { this.search$.pipe(debounceTime(300), switchMap(...)).subscribe(...); }
35
+ // After:
36
+ // SearchService owns search$ and the pipeline; component calls searchSvc.search(term)
37
+
38
+ // Note: @Input() setter bridges are intentionally exempt:
39
+ // @Input() set query(v: string) { this.search$.next(v); } \u2190 allowed`,"prefer-inject-over-constructor-di":`// Before:
40
+ constructor(private http: HttpClient, private router: Router) { }
41
+
42
+ // After:
43
+ private http = inject(HttpClient);
44
+ private router = inject(Router);`,"component-no-manual-detect-changes":`// Before:
45
+ this.cdr.detectChanges();
46
+ this.cdr.markForCheck();
47
+
48
+ // After:
49
+ // Use signals for reactive state:
50
+ count = signal(0);
51
+ // Template automatically updates when signal changes`,"rxjs-require-takeUntilDestroyed":`// Before:
52
+ this.data$.subscribe(val => this.process(val));
53
+
54
+ // After:
55
+ this.data$.pipe(
56
+ takeUntilDestroyed(this.destroyRef)
57
+ ).subscribe(val => this.process(val));`,"template-no-async-pipe-duplication":`// Before:
58
+ <div>{{ user$ | async }}</div>
59
+ <span>{{ user$ | async }}</span>
60
+
61
+ // After:
62
+ @if (user$ | async; as user) {
63
+ <div>{{ user }}</div>
64
+ <span>{{ user }}</span>
65
+ }`,"signal-no-side-effects-in-computed":`// Before:
66
+ total = computed(() => {
67
+ this.logger.log('computing'); // side effect!
68
+ return this.price() * this.qty();
69
+ });
70
+
71
+ // After:
72
+ total = computed(() => this.price() * this.qty());`,"signal-no-writes-in-computed":`// Before:
73
+ derived = computed(() => {
74
+ const val = this.source();
75
+ this.other.set(val * 2); // write inside computed!
76
+ return val;
77
+ });
78
+
79
+ // After:
80
+ derived = computed(() => this.source());
81
+ // Use effect() for the write:
82
+ syncEffect = effect(() => this.other.set(this.source() * 2));`,"signal-effect-must-be-destroy-scoped":`// Before:
83
+ ngAfterViewInit() {
84
+ effect(() => console.log(this.count())); // no injection context!
85
+ }
86
+
87
+ // After (option A - field initializer):
88
+ logEffect = effect(() => console.log(this.count()));
89
+
90
+ // After (option B - explicit injector):
91
+ ngAfterViewInit() {
92
+ effect(() => console.log(this.count()), { injector: this.injector });
93
+ }`,"signal-no-effect-in-constructor":`// Before:
94
+ constructor() {
95
+ effect(() => console.log(this.count()));
96
+ }
97
+
98
+ // After:
99
+ logEffect = effect(() => console.log(this.count()));`,"signal-prefer-computed-over-sync-effect":`// Before:
100
+ logEffect = effect(() => {
101
+ const total = this.price() * this.qty();
102
+ this.total.set(total);
103
+ });
104
+
105
+ // After:
106
+ total = computed(() => this.price() * this.qty());`,"toSignal-require-initialValue":`// Before:
107
+ data = toSignal(this.data$); // Signal<T | undefined>
108
+
109
+ // After:
110
+ data = toSignal(this.data$, { initialValue: [] }); // Signal<T>`,"template-no-call-expression":`// Before:
111
+ <div>{{ getLabel(item) }}</div>
112
+
113
+ // After (option A - pipe):
114
+ <div>{{ item | labelPipe }}</div>
115
+
116
+ // After (option B - signal):
117
+ label = computed(() => this.getLabel(this.item()));`,"template-no-object-literal-binding":`// Before:
118
+ <app-child [config]="{ color: 'red', size: 10 }"></app-child>
119
+
120
+ // After:
121
+ childConfig = signal({ color: 'red', size: 10 });
122
+ // template: <app-child [config]="childConfig()"></app-child>`,"template-no-array-literal-binding":`// Before:
123
+ <app-child [items]="[1, 2, 3]"></app-child>
124
+
125
+ // After:
126
+ items = signal([1, 2, 3]);
127
+ // template: <app-child [items]="items()"></app-child>`,"template-trackby-required-for-ngfor":`// Before:
128
+ <div *ngFor="let item of items">{{ item.name }}</div>
129
+
130
+ // After:
131
+ <div *ngFor="let item of items; trackBy: trackById">{{ item.name }}</div>
132
+
133
+ // Or with @for (Angular 17+):
134
+ @for (item of items; track item.id) {
135
+ <div>{{ item.name }}</div>
136
+ }`,"rxjs-prefer-toSignal-for-template-state":`// Before:
137
+ data$ = this.http.get('/api/data').pipe(shareReplay(1));
138
+ // template: {{ data$ | async }}
139
+
140
+ // After:
141
+ data = toSignal(this.http.get('/api/data'), { initialValue: null });
142
+ // template: {{ data() }}`,"signal-avoid-untracked-overuse":`// Acceptable:
143
+ effect(() => {
144
+ const value = this.count();
145
+ untracked(() => this.analytics.track(value));
146
+ });
147
+
148
+ // Questionable (review if untracked is needed):
149
+ const val = untracked(() => this.count());`,"template-prefer-control-flow":`// Before:
150
+ <div *ngIf="isLoggedIn">Welcome</div>
151
+ <li *ngFor="let item of items; trackBy: trackById">{{ item.name }}</li>
152
+
153
+ // After:
154
+ @if (isLoggedIn) { <div>Welcome</div> }
155
+ @for (item of items; track item.id) { <li>{{ item.name }}</li> }`,"signal-prefer-input-signal":`// Before:
156
+ @Input() title: string = '';
157
+ @Input() required count!: number;
158
+
159
+ // After:
160
+ title = input('');
161
+ count = input.required<number>();`,"signal-prefer-output-function":`// Before:
162
+ @Output() selected = new EventEmitter<Item>();
163
+
164
+ // After:
165
+ selected = output<Item>();`,"rxjs-no-nested-subscribe":`// Before:
166
+ this.user$.subscribe(user => {
167
+ this.posts$.subscribe(posts => {
168
+ this.render(user, posts);
169
+ });
170
+ });
171
+
172
+ // After:
173
+ this.user$.pipe(
174
+ switchMap(user => this.posts$.pipe(map(posts => ({ user, posts }))))
175
+ ).subscribe(({ user, posts }) => this.render(user, posts));`,"no-document-access":`// Before:
176
+ ngOnInit() {
177
+ document.title = this.title;
178
+ }
179
+
180
+ // After (inject DOCUMENT):
181
+ private doc = inject(DOCUMENT);
182
+ ngOnInit() { this.doc.title = this.title; }
183
+
184
+ // Or (SSR-safe browser-only block):
185
+ afterNextRenderEffect = afterNextRender(() => {
186
+ document.title = this.title;
187
+ });`,"signal-prefer-model":`// Before:
188
+ @Input() value: string = '';
189
+ @Output() valueChange = new EventEmitter<string>();
190
+
191
+ // After:
192
+ value = model('');
193
+ // Parent template: <app-input [(value)]="parentValue" />`,"prefer-after-render-over-after-view-init":`// Before:
194
+ ngAfterViewInit() {
195
+ this.el.nativeElement.focus();
196
+ }
197
+
198
+ // After:
199
+ constructor() {
200
+ afterNextRender(() => {
201
+ this.el.nativeElement.focus();
202
+ });
203
+ }`,"spec-no-focused-test":`// Before (focused \u2014 skips all other tests):
204
+ fdescribe('MyComponent', () => {
205
+ fit('should render', () => { ... });
206
+ });
207
+
208
+ // Before (disabled \u2014 easily forgotten):
209
+ xdescribe('MyComponent', () => {
210
+ xit('should render', () => { ... });
211
+ });
212
+
213
+ // After:
214
+ describe('MyComponent', () => {
215
+ it('should render', () => { ... });
216
+ });`};var te="component-no-manual-detect-changes",Ut=new Set(["detectChanges","markForCheck"]),Ft=new Set(["cdr","cdref","changedetectorref","_cdr","_cdref","changedetector","_changedetector","changedetectionref","cd","_cd"]),qe=createAnyAngularClassRule(te,(t,e)=>{var r,n;let o,l,u=t.node;if(n=u,analyzeComponent(n)?.type!=="Component")return null;let a=(l=(r=(function(i){let f=new Set,m=w(i),d=U(m);if(d){for(let h of ee(d.value??d))if($(h)==="ChangeDetectorRef"){let y=F(h);y&&f.add(y);}}for(let h of m)if(h.type==="PropertyDefinition"&&h.value){let y=s(h.value);if(y&&(function(g){if(g.type!=="CallExpression")return false;let S=s(g.callee);if(S?.type!=="Identifier"||S.name!=="inject")return false;let[A]=g.arguments??[],M=s(A);return M?.type==="Identifier"&&M.name==="ChangeDetectorRef"})(y)){let g=h.key;g?.type==="Identifier"&&f.add(g.name);}}return f})(u)).size>0,i=>!!r.has(i)||!l&&Ft.has(i.toLowerCase())),c=(o=analyzeComponent(u),o?.type==="Component"&&o.changeDetection?.kind==="literal"&&o.changeDetection.value===ChangeDetectionStrategy.OnPush),p=(function(i,f,m,d){let h=[],y=[...w(i)];for(;y.length>0;){let g=s(y.pop());if(!g)continue;let S=(function(A){if(A.type!=="CallExpression")return null;let M=s(A.callee);if(!x(M))return null;let P=v(M);return P&&Ut.has(P)?P:null})(g);for(let A of(S&&(function(M,P){if(M.type!=="CallExpression")return false;let I=s(M.callee);if(!I||!x(I))return false;let T=s(I.object);if(!T)return false;if(T.type==="Identifier")return P(T.name);if(!x(T))return false;let D=s(T.object),_=v(T);return (D?.type==="ThisExpression"||D?.type==="Identifier"&&D.name==="this")&&!!_&&P(_)})(g,d)&&(m&&S==="markForCheck"||h.push((function(M,P,I,T){let D=b(M),{line:_,column:V}=P.locator.location(D);return {filePath:P.filePath,ruleName:te,message:T?"Manual change detection in an OnPush component couples rendering to imperative calls.":`Manual change detection (${I}) can hide state-flow bugs and make rendering harder to predict.`,line:_,column:V,severity:T?"warn":"error",fix:E[te],codeExample:R[te]}})(g,f,S,m))),N(g)))y.push(A);}return h})(u,e,c,a);return p.length>0?p:null});var be="signal-no-side-effects-in-computed",Ht=new Set(["post","put","patch","subscribe","unsubscribe","next","complete","setItem","removeItem","appendChild","removeChild","dispatch"]),zt=new Set(["set","update","mutate"]),Wt=new Set(["ArrowFunctionExpression","FunctionExpression","FunctionDeclaration"]),He=createCallExpressionRule(be,(t,e)=>{if(!O(t.callee,"computed"))return null;let r=G(t),n=r?K(r):null;if(!n)return null;let o=(function(l){let u=[l];for(;u.length>0;){var a;let c=s(u.pop());if(c){if(c!==l){if((function(p){let i=s(p);if(!i)return false;if(i.type==="AssignmentExpression"){let f=s(i.left);return !!f&&x(f)}if(i.type==="UpdateExpression"||i.type==="UnaryExpression"&&i.operator==="delete"){let f=s(i.argument);return !!f&&x(f)}return false})(c))return {node:c,type:"write"};if(c.type==="CallExpression"){let p=(function(i){let f=s(i);if(!f||f.type!=="CallExpression")return "";let m=s(f.callee);return x(m)&&v(m)||""})(c);if(zt.has(p))return {node:c,type:"write"};if(Ht.has(p))return {node:c,type:"effect"}}}if(!(c!==l&&(a=c).type&&Wt.has(a.type)))for(let p of N(c))u.push(p);}}return null})(n);return o?(function(l,u,a,c){let p=b(l)||b(u),{line:i,column:f}=a.locator.location(p);return {filePath:a.filePath,ruleName:be,message:c==="write"?"computed() writes to state, which can create reactive cycles.":"computed() contains a side effect, so it is no longer a pure derivation.",line:i,column:f,severity:"error",fix:E[be]}})(o.node,t,e,o.type):null});var Se="signal-effect-must-be-destroy-scoped",ze=createAnyAngularClassRule(Se,(t,e)=>{let r=w(t.node);if(r.length===0)return null;let n=[];for(let o of r)o&&B(o)&&!xe(o)&&n.push(...(function(l,u){let a=L(l);if(!a)return [];let c=Le(l),p=[];for(let i of $e(a))(function(f){if(f.type!=="CallExpression")return false;let m=s(f.callee);return !!m&&(m.type==="Identifier"?m.name==="effect":!!x(m)&&v(m)==="effect")})(i)&&!(function(f){let m=s((Array.isArray(f.arguments)?f.arguments:[])[1]);if(!m||m.type!=="ObjectExpression")return false;for(let d of Array.isArray(m.properties)?m.properties:[]){let h=s(d);if(h?.type==="Property"){let y=s(h.key),g=y?.type==="Identifier"?y.name:v(y)||"";if(g==="injector")return true;if(g==="manualCleanup"){let S=s(h.value);if(S?.type==="Literal"&&S.value===true)return true}}}return false})(i)&&p.push((function(f,m,d){let h=b(f),{line:y,column:g}=d.locator.location(h);return {filePath:d.filePath,ruleName:Se,message:`effect() inside "${m}" has no explicit lifecycle owner, so cleanup can be unclear.`,line:y,column:g,severity:"error",fix:E[Se]}})(i,c,u));return p})(o,e));return n.length>0?n:null});var re="rxjs-no-nested-subscribe",Yt=new Set(["next","error","complete"]),Gt=new Set(["ArrowFunctionExpression","FunctionExpression","FunctionDeclaration"]);function ve(t){if(t.type!=="CallExpression")return false;let e=s(t.callee);return !!x(e)&&v(e)==="subscribe"}function We(t){return !!t&&(t.type==="ArrowFunctionExpression"||t.type==="FunctionExpression")}var Ve=createCallExpressionRule(re,(t,e)=>{if(!ve(t))return null;for(let r of (function(n){if(!ve(n))return [];let o=Array.isArray(n.arguments)?n.arguments:[];if(o.length===0)return [];let l=s(o[0]);if(l?.type==="ObjectExpression"){if(l.type!=="ObjectExpression")return [];let a=[];for(let c of Array.isArray(l.properties)?l.properties:[]){let p=s(c);if(p?.type==="Property"&&Yt.has((function(i){let f=s(i.key);return f?f.type==="Identifier"?f.name:v(f)||"":""})(p))){let i=s(p.value);We(i)&&a.push(i);}}return a}let u=[];for(let a=0;a<Math.min(o.length,3);a++){let c=s(o[a]);We(c)&&u.push(c);}return u})(t)){let n=s(r.body);if(n&&(function(o){let l=[o];for(;l.length>0;){var u;let a=s(l.pop());if(a){if(a!==o&&ve(a))return true;if(!(a!==o&&(u=a).type&&Gt.has(u.type)))for(let c of N(a))l.push(c);}}return false})(n))return (function(o,l){let u=b(o),{line:a,column:c}=l.locator.location(u);return {filePath:l.filePath,ruleName:re,message:"Nested subscribe() calls make stream lifetimes harder to reason about and can leak work.",line:a,column:c,severity:"error",fix:E[re],codeExample:R[re]}})(t,e)}return null});var ne="prefer-on-push-component-change-detection";function ie(t,e){let r=t?.[e];return typeof r=="number"?r:void 0}function oe(t){return t.replace(/\\/g,"/")}var Xe=createComponentRule(ne,(t,e)=>{let r=t.metadata??{};return r.type!=="Component"||!(function(n){if(!n||typeof n!="object")return false;let{kind:o,value:l}=n;return o!=="non-literal"&&(o==="literal"?l!==ChangeDetectionStrategy.OnPush:o==="missing")})(r.changeDetection)?null:(function(n,o){let l,u,a,c,p=(l=n.metadata??{},u=n.node,ie(l,"decoratorStart")??ie(l,"start")??ie(u,"start")??ie(n,"start")??0),{line:i,column:f}=o.locator.location(p),m=(a=n.metadata,typeof(c=a?.className)=="string"?c:"AnonymousComponent"),d=(function(h,y){if(!y.project)return null;let g=h.metadata,S=g?.className;if(typeof S!="string"||!S)return null;let A=oe(y.filePath),{ngModuleMap:M,classToFile:P}=y.project;for(let[T,D]of M){var I;if(D.isStandalone||!D.declarations.has(S))continue;let _=P.get(S);if(_&&oe(_)!==A)continue;let V=0;for(let Te of D.declarations)Te!==S&&(I=P.get(Te))&&oe(I).endsWith(".component.ts")&&V++;return {moduleName:(oe(T).split("/").pop()??T).replace(/\.ts$/,""),siblingComponentCount:V}}return null})(n,o);return {filePath:o.filePath,ruleName:ne,message:(function(h,y){if(!y)return `Component '${h}' uses default change detection, which can re-render more often than needed.`;let{moduleName:g,siblingComponentCount:S}=y,A=S>0?` ${S} other component${S===1?"":"s"} are declared in '${g}'.`:"";return `Component '${h}' uses default change detection, which can re-render more often than needed.${A}`})(m,d),line:i,column:f,severity:"error",fix:E[ne],codeExample:R[ne]}})(t,e)},{requires:{projectContext:true}});var Ae="template-no-call-expression",er=new Set(["translate","$localize","$any"]),tr=new Set(["slice","toString","toFixed","toUpperCase","toLowerCase","trim","join","includes","indexOf","startsWith","endsWith","charAt","substring","replace","split","concat","toISOString","toLocaleDateString","toLocaleTimeString","toLocaleString"]);function Ye(t){let e=s(t);return !!e&&(e.type==="CallExpression"||e.type==="OptionalCallExpression")}var Ge=createTemplateExpressionRule(Ae,(t,e)=>(function(r,n){let o=r?[r]:[];for(;o.length>0;){let l=s(o.pop());if(l){if(Ye(l)&&!(function(u){let a=s(u);if(!a||!Ye(a))return false;let c=s(a.callee);if(!c)return false;if(c.type==="Identifier")return er.has(c.name);if(!x(c))return false;let p=v(c);return !!p&&tr.has(p)})(l)){if((function(a){let c;return (c=s(a),Array.isArray(c?.arguments)?c.arguments:[]).length>0})(l))return true;let u=(function(a){let c=s(a);if(!c)return "";let p=s(c.callee);return p?p.type==="Identifier"?p.name??"":x(p)?v(p):"":""})(l);if(u&&!(function(a,c){let p=a.startsWith("get")||a.startsWith("is")||a.startsWith("has");if(c.crossRef?.signalMembers?.has(a))return true;if(c.typeChecker&&c.crossRef?.componentPath)try{let i=c.typeChecker,f=c.crossRef.componentPath,m=i.getProgram().getSourceFile(f);if(m){let d=m.statements.find(h=>Jt.isClassDeclaration(h));if(d&&d.name){let h=i.getTypeAtLocation(d),y=i.getPropertyOfType(h,a);if(y){if(!y)return !1;let g=y.valueDeclaration??y.declarations?.[0];if(!g)return !1;let S=i.getTypeOfSymbolAtLocation(y,g);if(!S)return !1;let A=i.typeToString(S);return A.includes("Signal")||A.includes("writable")||A.includes("computed")}}}}catch{}return !p})(u,n))return true}for(let u of N(l))o.push(u);}}return false})(t.expression,e)?[(function(r,n){let o=k(n,r.sourceSpan.start),{line:l,column:u}=n.locator.location(o);return {filePath:n.filePath,ruleName:Ae,message:"Template method calls run on every change detection cycle and can make rendering slower.",line:l,column:u,severity:"error",fix:E[Ae]}})(t,e)]:null,{requires:{htmlAst:true,typeChecker:true,projectContext:true}});function Ke(t,e,r,n){let o=k(t,e.sourceSpan.start),{line:l,column:u}=t.locator.location(o);return {filePath:t.filePath,ruleName:r,message:n,line:l,column:u,severity:"error",fix:E[r]}}var Ze=createTemplateRule("template-trackby-required",(t,e)=>{let r=[];for(let n of t.attributes)n.name==="*ngFor"&&!(function(o){return !!o.match(/\btrackBy\s*:\s*([^;]+?)\s*(?:;|$)/)?.[1]?.trim()})(n.value??"")&&r.push(Ke(e,n,"template-trackby-required-for-ngfor","*ngFor without trackBy can recreate DOM nodes unnecessarily when list items change."));for(let n of t.blocks)n.name!=="for"||n.parameters.some(o=>{let l=o.expression?.trim()??"";return !!l&&!!/^track\b/.test(l)&&l.replace(/^track\b/,"").trim().length>0})||r.push(Ke(e,n,"template-track-required-for-atfor","@for without a track expression can recreate DOM nodes unnecessarily when list items change."));return r.length>0?r:null},{requires:{htmlAst:true}});var Ce="template-no-object-literal-binding",Qe=createTemplateExpressionRule(Ce,(t,e)=>(function(r){let n=r?[r]:[];for(;n.length>0;){let o=s(n.pop());if(o){if(o.type==="ObjectExpression")return true;for(let l of N(o))n.push(l);}}return false})(t.expression)?[(function(r,n){let o=k(n,r.sourceSpan.start),{line:l,column:u}=n.locator.location(o);return {filePath:n.filePath,ruleName:Ce,message:"Object literals in template bindings create a new reference on every change detection cycle.",line:l,column:u,severity:"warn",fix:E[Ce]}})(t,e)]:null,{requires:{htmlAst:true}});var Ne="template-no-array-literal-binding",Je=createTemplateExpressionRule(Ne,(t,e)=>(function(r){let n=r?[r]:[];for(;n.length>0;){let o=s(n.pop());if(o){if(o.type==="ArrayExpression")return true;for(let l of N(o))n.push(l);}}return false})(t.expression)?[(function(r,n){let o=k(n,r.sourceSpan.start),{line:l,column:u}=n.locator.location(o);return {filePath:n.filePath,ruleName:Ne,message:"Array literals in template bindings create a new reference on every change detection cycle.",line:l,column:u,severity:"warn",fix:E[Ne]}})(t,e)]:null,{requires:{htmlAst:true}});var Re=new Map([["bypassSecurityTrustHtml","HTML"],["bypassSecurityTrustScript","Script"],["bypassSecurityTrustStyle","Style"],["bypassSecurityTrustUrl","URL"],["bypassSecurityTrustResourceUrl","Resource URL"]]),et=createCallExpressionRule("no-bypass-sanitization",(t,e)=>{let r=(function(l){let u=s(l.callee);if(!u)return null;if(u.type==="Identifier"&&u.name)return Re.has(u.name)?u.name:null;if(x(u)){let a=v(u);return a&&Re.has(a)?a:null}return null})(t);if(!r)return null;let{line:n,column:o}=e.locator.location(b(t));return {filePath:e.filePath,ruleName:"no-bypass-sanitization",message:`\`${r}\` bypasses Angular's ${Re.get(r)} sanitization, which can expose unsafe content.`,line:n,column:o,severity:"error",fix:E["no-bypass-sanitization"]}});var sr=new Map([["[innerHTML]",{desc:"innerHTML directly renders HTML markup and can execute injected scripts",severity:"error"}],["[outerHTML]",{desc:"outerHTML replaces the element with raw HTML and is susceptible to XSS",severity:"error"}],["[srcdoc]",{desc:"srcdoc embeds raw HTML inside an iframe and can execute injected scripts",severity:"error"}],["[style]",{desc:"binding complex expressions to [style] can enable CSS injection",severity:"warn"}]]),lr=/\|\s*(safeHtml|safeStyle|safeUrl|safeResourceUrl|sanitize|sanitizeHtml|sanitizeUrl|trustHtml|trustStyle|trustUrl|trustResourceUrl|bypassSecurity)\b/i,ur=/\b(getSafeHtml|getSafeUrl|getSafeStyle|getSafeResourceUrl|sanitize|sanitizeHtml|sanitizeUrl|trustHtml|bypassSecurity)\s*\(/i,tt=createTemplateAttributeRule("template-no-unsafe-bindings",(t,e)=>{let r=sr.get(t.name);if(!r)return null;let n=t.value??"";if(lr.test(n)||ur.test(n)||t.name==="[style]"&&!/[.(|?]/.test(n))return null;let{line:o,column:l}=e.locator.location(k(e,t.sourceSpan.start));return {filePath:e.filePath,ruleName:"template-no-unsafe-bindings",message:`\`${t.name}\` binds raw content: ${r.desc}. Unsanitized values can create injection risk.`,line:o,column:l,severity:r.severity,fix:E["template-no-unsafe-bindings"]}},{requires:{htmlAst:true}});var pr=new Set(["document","window","localStorage","sessionStorage","navigator","location"]),rt=createAnyAngularClassRule("no-document-access",(t,e)=>{let r=w(t.node);if(r.length===0)return null;let n=(function(a){let c=new Set,p=[...a];for(;p.length;){let i=s(p.pop());if(i){if(i.type==="VariableDeclarator"&&i.init){let f=s(i.init);if(f?.type==="CallExpression"&&f.callee&&O(f.callee,"isPlatformBrowser")){let m=i.id??i.key;m?.type==="Identifier"&&m.name&&c.add(m.name);}}if(i.type==="AssignmentExpression"&&i.right){let f=s(i.right);if(f?.type==="CallExpression"&&f.callee&&O(f.callee,"isPlatformBrowser")){let m=s(i.left);if(m&&x(m)&&s(m.object)?.type==="ThisExpression"){let d=m.property?.name;typeof d=="string"&&d&&c.add(d);}}}for(let f of N(i))p.push(f);}}return c})(r),o=[],l=new Set,u=[...r];for(;u.length;){let a=s(u.pop());if(a){if(a.type==="IfStatement"&&a.test){let c=s(a.test);if(c){let p=(function(i,f){let m=[i];for(;m.length;){let d=s(m.pop());if(d){if(d.type==="CallExpression"&&d.callee&&O(d.callee,"isPlatformBrowser"))return "browser";if(d.type==="CallExpression"&&d.callee&&O(d.callee,"isPlatformServer"))return "server";if(d.type==="UnaryExpression"&&d.operator==="!"&&d.argument){let h=s(d.argument);if(h?.type==="CallExpression"&&h.callee&&O(h.callee,"isPlatformServer"))return "browser"}if(d.type==="Identifier"&&f.has(d.name))return "browser";for(let h of N(d))m.push(h);}}return null})(c,n);if(p==="browser"){a.alternate&&u.push(a.alternate);continue}if(p==="server"){a.consequent&&u.push(a.consequent);continue}}}if(a.type==="CallExpression"&&a.callee){let c=s(a.callee);if(c&&(O(c,"afterNextRender")||O(c,"afterRender"))){let p=a.arguments??[];for(let i=1;i<p.length;i++)u.push(p[i]);continue}}if(x(a)){let c=(function(p){let i=p;for(;i&&x(i);)i=s(i.object);return i?.type==="Identifier"?i.name??null:null})(a);if(c&&pr.has(c)){let p=a;for(;p&&x(p);)p=s(p.object);let i=b(p);if(i!==void 0&&!l.has(i)){l.add(i);let{line:f,column:m}=e.locator.location(i);o.push({filePath:e.filePath,ruleName:"no-document-access",message:`Direct access to \`${c}\` can run during SSR where browser globals do not exist.`,line:f,column:m,severity:"error",fix:E["no-document-access"],codeExample:R["no-document-access"]});}}}for(let c of N(a))u.push(c);}}return o.length?o:null});var mr=new Set(["nativeElement","getBoundingClientRect","querySelector","querySelectorAll","getElementById","createElement","scrollIntoView","focus","blur","offsetWidth","offsetHeight","clientWidth","clientHeight","scrollTop","scrollLeft","innerHTML","outerHTML","textContent","parentElement","parentNode","firstChild","lastChild","nextSibling","previousSibling","children","ownerDocument","insertBefore","appendChild","removeChild","replaceChild","cloneNode","setAttribute","getAttribute","removeAttribute","hasAttribute","classList","dispatchEvent","contains","matches","closest","getClientRects","addEventListener","removeEventListener","requestAnimationFrame","cancelAnimationFrame"]),dr=["getElementsBy","offset","client","scroll"],nt=createAnyAngularClassRule("prefer-after-render-over-after-view-init",(t,e)=>{if(t.decoratorName!=="Component"&&t.decoratorName!=="Directive")return null;let r=w(t.node),n=[];for(let o of ["ngAfterViewInit","ngAfterContentInit"]){let l=r.find(p=>p.key?.name===o),u=l?L(l):null;if(!u||!l||!(function(p){let i=[p];for(;i.length;){let f=s(i.pop());if(f){if((f.type==="MemberExpression"||f.type==="StaticMemberExpression"||f.type==="OptionalMemberExpression")&&!f.computed){let m=f.property?.name??"";if(mr.has(m))return true;for(let d of dr)if(m.startsWith(d))return true}if(f.type==="Identifier"&&(f.name==="document"||f.name==="window"))return true;for(let m of N(f))i.push(m);}}return false})(u))continue;let{line:a,column:c}=e.locator.location(b(l));n.push({filePath:e.filePath,ruleName:"prefer-after-render-over-after-view-init",message:`\`${o}\` contains DOM access that can run before browser-only APIs are safe.`,line:a,column:c,severity:"warn",fix:E["prefer-after-render-over-after-view-init"],codeExample:R["prefer-after-render-over-after-view-init"]});}return n.length?n:null});var ae=new Map;function we(t){return /ngOnDestroy\s*\([^)]*\)\s*\{[^}]*\.unsubscribe\s*\(\)/s.test(t)}var it=createCallExpressionRule("rxjs-require-takeUntilDestroyed",(t,e)=>{var r,n;let o;if(!e.filePath.endsWith(".component.ts")&&!e.filePath.endsWith(".directive.ts")&&!/\@(Component|Directive)\s*[\(\{]/.test(e.fileContent)||!Y(t))return null;let l=s(t.callee),u=x(l)?l?.object:null;if(u&&Z(u)||J(Q(u))||(r=e.filePath,n=e.fileContent,(o=ae.get(r))!==void 0||(o=we(n),ae.size>=500&&ae.clear(),ae.set(r,o)),o))return null;let a=b(t),{line:c,column:p}=e.locator.location(a);return {filePath:e.filePath,ruleName:"rxjs-require-takeUntilDestroyed",message:"A component subscription without teardown can keep running after the component is destroyed.",line:c,column:p,severity:"error",fix:E["rxjs-require-takeUntilDestroyed"]}});var se=new Map,le="rxjs-no-subscribe-in-component",ot=createCallExpressionRule(le,(t,e)=>{var r,n;let o;if(!e.filePath.endsWith(".component.ts")&&!e.filePath.endsWith(".directive.ts")&&!/\@(Component|Directive)\s*[\(\{]/.test(e.fileContent)||!Y(t)||(function(i){let f=s(i.callee);if(!f||!x(f))return false;let m=s(f.object);if(!m)return false;if(m.type==="CallExpression"){let d=s(m.callee);if(x(d)&&v(d)==="pipe"&&(Array.isArray(m.arguments)?m.arguments:[]).some(h=>{let y=s(h);if(y?.type!=="CallExpression")return false;let g=s(y.callee)?.name;return g==="take"||g==="first"}))return true}return J(Q(m))})(t))return null;let l=s(t.callee),u=s(l?.object);if(u&&Z(u)||(r=e.filePath,n=e.fileContent,(o=se.get(r))!==void 0||(o=we(n),se.size>=500&&se.clear(),se.set(r,o)),o))return null;let a=b(t),{line:c,column:p}=e.locator.location(a);return {filePath:e.filePath,ruleName:le,message:(function(i,f){let m="Open-ended subscriptions in components can outlive the component and make state harder to track.",d=f.crossRef?.templateReferences;if(!d)return m;let h=s(i.callee),y=s(h?.object),g=y?v(y):null;if(!g)return m;let S=g.endsWith("$")?g.slice(0,-1):g;return d.has(g)||d.has(S)?`'${g}' is read by the template, so subscribing manually adds state and teardown work the template can own.`:m})(t,e),line:c,column:p,severity:"error",fix:E[le],codeExample:R[le]}},{requires:{projectContext:true}});var ue="rxjs-avoid-subject-as-event-bus",at=new Set(["Subject","ReplaySubject","AsyncSubject","BehaviorSubject"]),Er=new Set(["destroy$","destroyed$","ondestroy$","ngondestroy$","unsubscribe$","unsub$","teardown$","dispose$","cleanup$","cleanupsubject$"]),xr=/state|loading|error|active|selected|open|visible|disabled|count|value|data|hidden|expanded|pending|success|failed/i;function H(t,e,r){let n=[t];for(;n.length;){let o=s(n.pop());if(!o)continue;let l=s(o.callee);if(o.type==="CallExpression"&&v(l)===e){let u=s(l?.object);if(u&&x(u)&&s(u.object)?.type==="ThisExpression"){let a=v(u);a&&r.add(a);}}for(let u of N(o))n.push(u);}}var st=createAnyAngularClassRule(ue,(t,e)=>{if(t.decoratorName!=="Component"&&t.decoratorName!=="Directive")return null;let r=w(t.node);if(r.length===0)return null;let n=(function(a){let c=_e(a.sourceText,at),p=new Map;for(let[i,f]of c)for(let m of f)p.set(m,i);return p})(e),o=(function(a){let c=new Set,p=U(a);if(p){let i=L(p);i&&H(i,"next",c);}for(let i of a)if(B(i)){i.kind==="set"&&(function(m){let d=m.decorators;if(!Array.isArray(d))return false;for(let h of d){let y=s(h.expression);if(y?.name==="Input"||s(y?.callee)?.name==="Input")return true}return false})(i)&&H(i,"next",c);let f=i.key?.name;if(f==="ngOnChanges"){let m=L(i);m&&H(m,"next",c);}if(f==="ngOnDestroy"){let m=L(i);m&&H(m,"complete",c);}}else i.type==="PropertyDefinition"&&i.value&&H(i.value,"next",c);return c})(r),l=(function(a){let c=new Set;for(let p of a){let i=B(p)?L(p):p.type==="PropertyDefinition"?p.value:null;i&&H(i,"pipe",c);}return c})(r),u=[];for(let a of r){if(a.type!=="PropertyDefinition"||a.accessibility==="public")continue;let c=s(a.value??a.initializer);if(!c||c.type!=="NewExpression")continue;let p=(function(d,h){let y=s(d);if(!y)return null;if(y.type==="Identifier")return h.get(y.name)??null;if(x(y)){let g=v(y);return g&&at.has(g)?g:null}return null})(c.callee,n),i=a.key?.name??"";if(!p||!i||Er.has(i.toLowerCase())||o.has(i)||l.has(i))continue;let{line:f,column:m}=e.locator.location(b(a));u.push({filePath:e.filePath,ruleName:ue,message:xr.test(i)?`${p} '${i}' is used for component UI state, which adds stream overhead to local state updates.`:`${p} '${i}' is acting as a local event bus, which makes component interactions harder to trace.`,line:f,column:m,severity:"warn",fix:E[ue],codeExample:R[ue]});}return u.length?u:null});var Me="rxjs-prefer-toSignal-for-template-state",lt=new Set(["Observable","Subject","BehaviorSubject","ReplaySubject","AsyncSubject"]),Sr=new Set(["destroy$","destroyed$","unsub$","teardown$","dispose$"]),ut=createAnyAngularClassRule(Me,(t,e)=>{if(t.metadata?.type!=="Component")return null;let n=e.crossRef?.templateReferences;if(n===void 0)return null;let o=w(t.node),l=[];for(let u of o){if(u.type!=="PropertyDefinition")continue;let a=u.key?.name??"";if(!a||!a.endsWith("$")||Sr.has(a.toLowerCase())||(function(f){let m=f.modifiers,d=f.decorators??(Array.isArray(m)?m.filter(h=>h.type==="Decorator"):void 0);return Array.isArray(d)&&d.some(h=>{let y=s(h.expression);return (y?.type==="CallExpression"?j(y):y?.type==="Identifier"?y.name:null)==="Output"})})(u))continue;let c=a.slice(0,-1);if(!n.has(a)&&!n.has(c)||!(function(f){if(lt.has((function(y){let g=y.typeAnnotation,S=s(g?.typeAnnotation);if(!S||S.type!=="TSTypeReference"&&S.type!=="TypeReference")return "";let A=S.typeName??S.name;if(typeof A=="string")return A;if(A&&typeof A=="object"){if(A.type==="Identifier")return A.name??"";if(A.type==="TSQualifiedName")return s(A.right)?.name??""}return ""})(f)))return true;let m=s(f.value??f.initializer);if(!m)return false;let d=j(m);if(d==="toSignal"||d==="signal"||d==="computed")return false;if(m.type==="NewExpression"){let y=s(m.callee),g=y?.type==="Identifier"?y.name:v(y);return !!g&&lt.has(g)}let h=[m];for(;h.length>0;){let y=s(h.pop());if(y){if(y.type==="CallExpression"&&j(y)==="pipe")return true;for(let g of N(y))h.push(g);}}return false})(u))continue;let{line:p,column:i}=e.locator.location(b(u));l.push({filePath:e.filePath,ruleName:Me,message:`Observable "${a}" is read by the template, which can add async-pipe churn and weaker signal integration.`,line:p,column:i,severity:"warn",fix:E[Me]});}return l.length?l:null},{requires:{projectContext:true,htmlAst:true}});var Pe="toSignal-require-initialValue",ct=createCallExpressionRule(Pe,(t,e)=>{if(!O(t.callee,"toSignal"))return null;let r=Array.isArray(t.arguments)?t.arguments:[],n=r[1]?s(r[1]):null;if(!n||n.type!=="ObjectExpression"||!(function(o){let l=W(o,"initialValue");if(l&&!Ee(l.value))return true;let u=W(o,"requireSync");return !!u&&ge(u.value)})(n)){let{line:o,column:l}=e.locator.location(b(t));return {filePath:e.filePath,ruleName:Pe,message:"toSignal() can emit undefined before the observable produces a value.",line:o,column:l,severity:"warn",fix:E[Pe]}}return null});var Cr=new Set(["set","update","mutate"]),Nr=new Set(["setTimeout","setInterval","queueMicrotask","requestAnimationFrame","then","catch","finally","subscribe"]),pt=createCallExpressionRule("signal-prefer-computed-over-sync-effect",(t,e)=>{if(!O(t.callee,"effect"))return null;let r=G(t),n=r?K(r):null;if(!n)return null;let{hasRead:o,hasWrite:l,hasAsync:u,hasLinked:a,firstWrite:c}=(function(f,m){let d={hasRead:false,hasWrite:false,hasAsync:false,hasLinked:false,firstWrite:null},h=[f];for(;h.length;){let y=s(h.pop());if(y){if((y.type==="AwaitExpression"||y.type==="YieldExpression")&&(d.hasAsync=true),y.type==="CallExpression"){let g=j(y);g&&Nr.has(g)&&(d.hasAsync=true),g==="linkedSignal"&&(d.hasLinked=true),(function(S){let A=s(S);if(!A||A.type!=="CallExpression")return false;let M=s(A.callee);return x(M)&&Cr.has(v(M)||"")})(y)?(d.hasWrite=true,d.firstWrite||(d.firstWrite=y)):(function(S,A,M){let P=s(S);if(!P||P.type!=="CallExpression"||Array.isArray(P.arguments)&&P.arguments.length>0)return false;let I=s(P.callee);if(!I)return false;if(A.typeChecker)try{let T=q(I,A);if(T){let D=A.typeChecker.getTypeOfSymbolAtLocation(T,T.valueDeclaration);if(D&&A.typeChecker.typeToString(D).includes("Signal"))return !0}}catch{}return x(I)?s(I.object)?.type==="ThisExpression":M&&I.type==="Identifier"})(y,m,true)&&(d.hasRead=true);}for(let g of N(y))h.push(g);}}return d})(n,e);if(!l||!o||u||a)return null;let{line:p,column:i}=e.locator.location(b(c||t));return {filePath:e.filePath,ruleName:"signal-prefer-computed-over-sync-effect",message:"This effect reads reactive values and writes derived state, which adds extra reactive cycles.",line:p,column:i,severity:"warn",fix:E["signal-prefer-computed-over-sync-effect"]}},{requires:{typeChecker:true}});var ft=new Set(["afterRender","afterNextRender"]),wr=/\bafterNextRender\s*\(|\bafterRender\s*\(/,ce=new Map,mt=createCallExpressionRule("signal-avoid-untracked-overuse",(t,e)=>{if(!O(t.callee,"untracked")||(function(o,l){let u=l.sourceText??l.fileContent;if(typeof u=="string"&&!wr.test(u))return false;let a=o.parent;for(;a;){if(a.type==="CallExpression"&&ft.has(j(a)))return true;a=a.parent;}let c=b(o);return (function(p){let i=ce.get(p.filePath);if(i!==void 0)return i;let f=p.program;return i=f?(function(m){let d=[],h=m.body,y=Array.isArray(h)?[...h]:[m];for(;y.length;){let g=s(y.pop());if(g){if(g.type==="CallExpression"&&ft.has(j(g))){let S=s(g.arguments?.[0]);if(S){let A=b(S),M=S.end??S.span?.end??b(S);M>A&&d.push([A,M]);}}for(let S of N(g))y.push(S);}}return d})(f):[],ce.size>=300&&ce.clear(),ce.set(p.filePath,i),i})(l).some(([p,i])=>c>=p&&c<i)})(t,e))return null;let{line:r,column:n}=e.locator.location(b(t));return {filePath:e.filePath,ruleName:"signal-avoid-untracked-overuse",message:"untracked() hides this read from dependency tracking, which can mask stale reactive state.",line:r,column:n,severity:"warn",fix:E["signal-avoid-untracked-overuse"]}});var pe="prefer-inject-over-constructor-di",Pr=new Set(["http","router","route","cdr","cdref","changedetectorref","injector","ngzone","zone","renderer","renderer2","elementref","el","document","platform","location","dialog","snack","toast","store","facade","logger","translate","i18n","auth","api","client","matdialog","overlay","breakpointobserver","snackbar","matsnackbar","bottomsheet","matbottomsheet","clipboard","directionality","focusmonitor","mediamatcher","viewportruler","scrolldispatcher","dragdrop","liveannouncer","activatedroute","fb","firestore","angularfire","formbuilder","titleservice","metaservice","title","meta","sanitizer","domsanitizer","compiler","applicationref","componentfactoryresolver","viewcontainerref","templateref","destroyref"]),Or=new Set(["data","config","options","opts","params","payload","input","value","values","items","item","context","ctx","model","vm","state","initialstate","result","name","id","label","text","title","message","url","path","index","count","size","length","width","height","color","type","key","mode","flag","enabled","disabled","visible","hidden"]),Tr=["Service","Facade","Store","Client","Repository","Adapter","Manager","Controller","Provider","Registry","Logger","Router","ActivatedRoute","ChangeDetectorRef","DestroyRef","Injector","NgZone","Renderer2","ElementRef","HttpClient","ViewContainerRef","TemplateRef","ComponentFactoryResolver","ApplicationRef","MatDialog","MatDialogRef","MatSnackBar","MatBottomSheet","Overlay","OverlayRef","BreakpointObserver","Clipboard","FocusMonitor","MediaMatcher","ScrollDispatcher","DragDrop","LiveAnnouncer","Directionality","ViewportRuler","FormBuilder","DomSanitizer","Title","Meta","Dispatcher","Gateway","Handler","Interceptor","Guard","Resolver","Factory","Strategy","Validator"],Ir=new Set(["string","number","boolean","symbol","bigint","any","unknown","void"]),dt=createAnyAngularClassRule(pe,(t,e)=>{let r=U(w(t.node));if(!r)return null;let n=ee(r.value??r).filter(a=>(function(c,p){let i=s(c);if(!i)return false;if(Array.isArray(i.decorators)&&i.decorators.length>0||i.accessibility||i.readonly)return true;if(p.typeChecker){let d=q(c,p);if(d&&Ue(d))return true}let f=(F(c)||"").toLowerCase().trim();if(!f||Or.has(f))return false;if(Pr.has(f))return true;let m=($(c)||"").trim();return !(!m||Ir.has(m.toLowerCase()))&&Tr.some(d=>m.endsWith(d))})(a,e));if(n.length===0)return null;let{line:o,column:l}=e.locator.location(b(r)),u=n.map(a=>`${F(a)}: ${$(a)}`).join(", ");return {filePath:e.filePath,ruleName:pe,message:`Constructor dependency injection makes class setup less composable than inject().${u?` Offending params: ${u}.`:""}`,line:o,column:l,severity:"warn",fix:E[pe],codeExample:R[pe]}},{requires:{typeChecker:true}});var fe="signal-prefer-input-signal";function Dr(t){let e=s(t.expression??t);if(!e)return false;if(e.type==="Identifier")return e.name==="Input";if(e.type==="CallExpression"){let r=s(e.callee);return r?.type==="Identifier"&&r.name==="Input"}return false}var yt=createAnyAngularClassRule(fe,(t,e)=>{if(t.decoratorName!=="Component"&&t.decoratorName!=="Directive")return null;let r=w(t.node),n=[],o=e.project?.standaloneComponents?.has(e.filePath)??false;for(let l of r){let u=s(l);if(u&&(u.type==="PropertyDefinition"||u.type==="AccessorProperty")&&Array.isArray(u.decorators)&&u.decorators.some(Dr)){let a=b(u),{line:c,column:p}=e.locator.location(a),i=s(u.key),f=(i?.type==="Identifier"?i.name:i?.type==="Literal"?String(i.value):"")||"(unknown)",m=`'${f}' uses @Input(), which keeps this input outside Angular's signal graph.`;o&&(m+=" Standalone declarations benefit most from signal inputs."),n.push({filePath:e.filePath,ruleName:fe,message:m,line:c,column:p,severity:o?"error":"warn",fix:E[fe],codeExample:R[fe]});}}return n.length>0?n:null},{requires:{projectContext:true}});var me="signal-prefer-output-function",ht=createAnyAngularClassRule(me,(t,e)=>{if(t.decoratorName!=="Component"&&t.decoratorName!=="Directive")return null;let r=w(t.node),n=[];for(let o of r){let l=s(o);if(l&&(l.type==="PropertyDefinition"||l.type==="AccessorProperty")&&Array.isArray(l.decorators)&&l.decorators.some(u=>{let a=s(u.expression??u);if(!a)return false;if(a.type==="Identifier")return a.name==="Output";if(a.type==="CallExpression"){let c=s(a.callee);return c?.type==="Identifier"&&c.name==="Output"}return false})&&(function(u,a){let c=s(u.value);if(c?.type==="NewExpression"){let i=s(c.callee);if(i?.type==="Identifier"&&i.name==="EventEmitter")return true}let p=s(u.typeAnnotation);if(p&&$(p).includes("EventEmitter"))return true;if(a.typeChecker){let i=q(u,a);if(i&&(typeof i.getName=="function"?i.getName():"").includes("EventEmitter"))return true}return false})(l,e)){let u=b(l),{line:a,column:c}=e.locator.location(u),p=s(l.key),i=(p?.type==="Identifier"?p.name:p?.type==="Literal"?String(p.value):"")||"(unknown)";n.push({filePath:e.filePath,ruleName:me,message:`'${i}' uses @Output() EventEmitter, which adds boilerplate compared with output().`,line:a,column:c,severity:"warn",fix:E[me],codeExample:R[me]});}}return n.length>0?n:null},{requires:{typeChecker:true}});var de="signal-prefer-model";function gt(t,e){return Array.isArray(t.decorators)&&t.decorators.some(r=>{let n=s(r.expression??r);if(!n)return false;if(n.type==="Identifier")return n.name===e;if(n.type==="CallExpression"){let o=s(n.callee);return o?.type==="Identifier"&&o.name===e}return false})}function Et(t,e){let r=s(t.value);if(!r||r.type!=="CallExpression")return false;let n=s(r.callee);if(!n)return false;if(n.type==="Identifier")return n.name===e;if(x(n)){let o=s(n.object);return o?.type==="Identifier"&&o.name===e}return false}var xt=createAnyAngularClassRule(de,(t,e)=>{if(t.decoratorName!=="Component"&&t.decoratorName!=="Directive")return null;let r=w(t.node),n=new Map,o=new Map;for(let u of r){let a=s(u);if(!a||a.type!=="PropertyDefinition"&&a.type!=="AccessorProperty")continue;let c=(function(p){let i=s(p.key);return i?i.type==="Identifier"?i.name??null:i.type==="Literal"?String(i.value):null:null})(a);c&&((gt(a,"Input")||Et(a,"input"))&&n.set(c,a),(gt(a,"Output")||Et(a,"output"))&&o.set(c,a));}let l=[];for(let[u,a]of o){if(!u.endsWith("Change"))continue;let c=u.slice(0,-6),p=n.get(c);if(p){let i=b(p),{line:f,column:m}=e.locator.location(i);l.push({filePath:e.filePath,ruleName:de,message:`The \`${c}\` / \`${u}\` pair implements two-way binding with extra wiring that model() avoids.`,line:f,column:m,severity:"warn",fix:E[de],codeExample:R[de]});}}return l.length>0?l:null});var _r=new Map([["*ngIf","@if"],["*ngFor","@for"],["*ngSwitch","@switch"],["[ngSwitch]","@switch"],["*ngSwitchCase","@case"],["*ngSwitchDefault","@default"]]),Oe="template-prefer-control-flow",bt=createTemplateAttributeRule(Oe,(t,e)=>{var r;let n=(r=t.name,_r.get(r)??null);if(!n)return null;let o=k(e,t.sourceSpan.start),{line:l,column:u}=e.locator.location(o);return {filePath:e.filePath,ruleName:Oe,message:`\`${t.name}\` uses legacy structural directive syntax, so it misses the \`${n}\` control flow benefits.`,line:l,column:u,severity:"error",fix:E[Oe]}},{requires:{htmlAst:true}});var St=new WeakMap,vt=createTemplateExpressionRule("template-no-async-pipe-duplication",(t,e)=>{let r,n,o=(function p(i){let f=s(i);if(!f)return null;if(f.type==="BinaryExpression"&&f.operator==="|"){let m=s(f.right);return m?.type==="Identifier"&&m.name==="async"?s(f.left):p(f.left)}return null})(t.expression),l=o?(function p(i,f=0){if(!i||f>10)return null;if(i.type==="Identifier")return i.name;if(i.type==="ThisExpression")return "this";if(i.type==="Literal"){let m=i.value;return m===null?"null":typeof m=="string"||typeof m=="number"||typeof m=="boolean"||typeof m=="bigint"?String(m):null}if(i.type==="CallExpression"){let m=p(i.callee,f+1),d=(Array.isArray(i.arguments)?i.arguments:[]).map(h=>p(h,f+1)??"?");return `${m}(${d.join(",")})`}if(x(i)){let m=p(i.object,f+1),d=v(i);if(m&&d)return `${m}.${d}`}if(i.type==="UnaryExpression")return `${i.operator}${p(i.argument,f+1)}`;if(i.type==="ConditionalExpression")return `${p(i.test,f+1)}?${p(i.consequent,f+1)}:${p(i.alternate,f+1)}`;if(i.type==="OptionalExpression"||i.type==="ChainExpression")return p(i.expression,f+1);if(i.type==="LogicalExpression"){let m=p(i.left,f+1),d=p(i.right,f+1);if(m&&d)return `${m}${i.operator}${d}`}if(i.type==="BinaryExpression"&&i.operator!=="|"){let m=p(i.left,f+1),d=p(i.right,f+1);if(m&&d)return `${m}${i.operator}${d}`}if(i.type==="ArrayExpression")return `[${(i.elements??[]).map(d=>p(d,f+1)??"?").join(",")}]`;if(i.type==="ObjectExpression")return `{${(i.properties??[]).map(d=>{let h=d.key?p(d.key,f+1):"?",y=d.value?p(d.value,f+1):"?";return `${h}:${y}`}).join(",")}}`;if(i.type==="TemplateLiteral")return "`tmpl`";if(i.type==="AssignmentExpression"){let m=p(i.left,f+1),d=p(i.right,f+1);if(m&&d)return `${m}${i.operator}${d}`}return null})(o):null;if(!l)return null;let u=(r=e.template,n=r?.templateStartOffset,`${e.filePath}@${typeof n=="number"&&Number.isFinite(n)?n:0}`),a=St.get(e);a||(a=new Map,St.set(e,a));let c=a.get(u);if(c||(c=new Set,a.set(u,c)),c.has(l)){let p=k(e,t.sourceSpan.start),{line:i,column:f}=e.locator.location(p);return {filePath:e.filePath,ruleName:"template-no-async-pipe-duplication",message:`Duplicate async pipe subscriptions for "${l}" can create repeated work and inconsistent loading states.`,line:i,column:f,severity:"warn",fix:E["template-no-async-pipe-duplication"]}}return c.add(l),null},{requires:{htmlAst:true}});var Fr=new Set(["fdescribe","fit","describe.only","it.only","test.only","context.only","xdescribe","xit","xtest","xcontext"]),qr=new Set(["xdescribe","xit","xtest","xcontext"]);function At(t){return t.type==="Identifier"?t.name??null:null}var Ct=createCallExpressionRule("spec-no-focused-test",(t,e)=>{var r;let n;if(!((r=e.filePath).endsWith(".spec.ts")||r.endsWith(".test.ts")))return null;let o=s(t.callee);if(!o)return null;let l=(n=At(o)||(function(i){if(i.type==="MemberExpression"||i.type==="StaticMemberExpression"||i.type==="OptionalMemberExpression"){let f=s(i.object),m=i.property?.name??"",d=(f?.type==="Identifier"?f.name:"")??"";return d?`${d}.${m}`:null}return null})(o))&&Fr.has(n)?n:null;if(!l){if(At(o)==="pending"){let i=b(t),{line:f,column:m}=e.locator.location(i);return {filePath:e.filePath,ruleName:"spec-no-focused-test",message:"`pending()` disables the test body, so the committed suite can miss coverage.",line:f,column:m,severity:"error",fix:E["spec-no-focused-test"]}}return null}let u=b(t),{line:a,column:c}=e.locator.location(u),p=qr.has(l)?`\`${l}\` disables a test that may be forgotten.`:`\`${l}\` is a focused test helper that can skip the rest of the suite in CI.`;return {filePath:e.filePath,ruleName:"spec-no-focused-test",message:p,line:a,column:c,severity:"error",fix:E["spec-no-focused-test"]}},{dependencyType:"spec",requires:{tsAst:true}});function Nt(){C(qe,"correctness"),C(He,"correctness"),C(ze,"correctness"),C(Ve,"correctness"),C(Xe,"performance"),C(Ge,"performance"),C(Ze,"performance"),C(Qe,"performance"),C(Je,"performance"),C(et,"security"),C(tt,"security"),C(rt,"ssr"),C(nt,"ssr"),C(ot,"reactivity"),C(it,"reactivity"),C(st,"reactivity"),C(ut,"reactivity"),C(ct,"reactivity"),C(pt,"reactivity"),C(mt,"reactivity"),C(dt,"modern-api"),C(yt,"modern-api"),C(ht,"modern-api"),C(xt,"modern-api"),C(bt,"template"),C(vt,"template"),C(Ct,"testing");}Nt(),configureRuleExecutor(ke,Ie),process.on("message",t=>{Xr(t);});var Xr=async t=>{let e=[],r=createTypeAwareAnalysisContext(t.rootDir,t.files,t.parserOptions,{buildProjectContext:t.buildProjectContext,programRootFiles:t.programRootFiles});try{await r.warmup();let n=groupTasksByFile(t.tasks),o=await Yr(Array.from(n),Math.max(1,t.fileConcurrency??1),async([l,u])=>{let a=performance.now();try{let c=await executeBatchedTasks(u,r);return Rt(l,u.length,c,performance.now()-a),c}catch{return Rt(l,u.length,[],performance.now()-a),[]}finally{r.evict(l);}});e.push(...o.flat()),process.send?.({kind:"complete",results:e});}catch(n){process.send?.({kind:"error",error:n instanceof Error?n.message:String(n)});}finally{r.dispose(),process.disconnect?.();}};async function Yr(t,e,r){let n=Array(t.length),o=0,l=Math.min(e,t.length);return await Promise.all(Array.from({length:l},async()=>{for(;o<t.length;){let u=o++;n[u]=await r(t[u]);}})),n}var Rt=(t,e,r,n)=>{process.send?.(Gr(t,e,r,n));},Gr=(t,e,r,n)=>{let o=0,l=0;for(let u of r)for(let a of u.failures)a.severity==="error"?o++:a.severity==="warn"&&l++;return {kind:"file-progress",filePath:t,taskCount:e,issueCount:o+l,errorCount:o,warningCount:l,duration:n,typeAware:true}};//# sourceMappingURL=type-aware-worker.js.map
217
+ //# sourceMappingURL=type-aware-worker.js.map