lint-suite 1.4.0 → 1.6.0

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.
Files changed (48) hide show
  1. package/eslint.cjs +623 -45
  2. package/eslint.cjs.map +4 -4
  3. package/eslint.js +636 -42
  4. package/eslint.js.map +4 -4
  5. package/lib/angular-template.d.ts +1 -2
  6. package/lib/angular-template.d.ts.map +1 -1
  7. package/lib/angular.d.ts +1 -2
  8. package/lib/angular.d.ts.map +1 -1
  9. package/lib/base.d.ts +1 -1
  10. package/lib/base.d.ts.map +1 -1
  11. package/lib/boundaries.d.ts +1 -1
  12. package/lib/boundaries.d.ts.map +1 -1
  13. package/lib/javascript.d.ts +1 -2
  14. package/lib/javascript.d.ts.map +1 -1
  15. package/lib/json.d.ts +1 -1
  16. package/lib/json.d.ts.map +1 -1
  17. package/lib/playwright.d.ts +1 -1
  18. package/lib/playwright.d.ts.map +1 -1
  19. package/lib/prettier.d.ts +1 -1
  20. package/lib/prettier.d.ts.map +1 -1
  21. package/lib/rules/no-unused-angular-instance-fields/angular-class-fields.d.ts +8 -0
  22. package/lib/rules/no-unused-angular-instance-fields/angular-class-fields.d.ts.map +1 -0
  23. package/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.d.ts +3 -0
  24. package/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.d.ts.map +1 -0
  25. package/lib/rules/no-unused-angular-instance-fields/common/no-unused-angular-instance-fields.type.d.ts +10 -0
  26. package/lib/rules/no-unused-angular-instance-fields/common/no-unused-angular-instance-fields.type.d.ts.map +1 -0
  27. package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.d.ts +7 -0
  28. package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.d.ts.map +1 -0
  29. package/lib/rules/no-unused-angular-instance-fields/no-unused-instance-fields.spec.d.ts +2 -0
  30. package/lib/rules/no-unused-angular-instance-fields/no-unused-instance-fields.spec.d.ts.map +1 -0
  31. package/lib/rules/no-unused-angular-instance-fields/utils/angular-expression-reads.util.d.ts +6 -0
  32. package/lib/rules/no-unused-angular-instance-fields/utils/angular-expression-reads.util.d.ts.map +1 -0
  33. package/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.d.ts +7 -0
  34. package/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.d.ts.map +1 -0
  35. package/lib/rxjs.d.ts +1 -1
  36. package/lib/rxjs.d.ts.map +1 -1
  37. package/lib/storybook.d.ts +1 -1
  38. package/lib/storybook.d.ts.map +1 -1
  39. package/lib/typescript.d.ts +1 -2
  40. package/lib/typescript.d.ts.map +1 -1
  41. package/lib/vitest.d.ts +1 -1
  42. package/lib/vitest.d.ts.map +1 -1
  43. package/package.json +23 -22
  44. package/stylelint.cjs +3 -0
  45. package/stylelint.cjs.map +2 -2
  46. package/stylelint.d.ts.map +1 -1
  47. package/stylelint.js +3 -0
  48. package/stylelint.js.map +2 -2
package/eslint.cjs CHANGED
@@ -11,9 +11,9 @@ var __export = (target, all) => {
11
11
  };
12
12
  var __copyProps = (to, from, except, desc) => {
13
13
  if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ for (let key2 of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key2) && key2 !== except)
16
+ __defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
17
17
  }
18
18
  return to;
19
19
  };
@@ -127,10 +127,588 @@ var angularTemplate = (0, import_config.defineConfig)([
127
127
  var import_globals = __toESM(require("globals"), 1);
128
128
  var import_angular_eslint2 = require("angular-eslint");
129
129
  var import_config2 = require("eslint/config");
130
+
131
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.ts
132
+ var import_utils4 = require("@typescript-eslint/utils");
133
+
134
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-class-fields.ts
135
+ var import_utils2 = require("@typescript-eslint/utils");
136
+
137
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.ts
138
+ var import_node_fs = require("node:fs");
139
+ var import_node_path = require("node:path");
140
+ var import_compiler2 = require("@angular/compiler");
141
+ var import_utils = require("@typescript-eslint/utils");
142
+
143
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/utils/angular-expression-reads.util.ts
144
+ var import_compiler = require("@angular/compiler");
145
+ var ReadCollector = class extends import_compiler.CombinedRecursiveAstVisitor {
146
+ constructor(boundTarget, action) {
147
+ super();
148
+ this.boundTarget = boundTarget;
149
+ this.action = action;
150
+ }
151
+ boundTarget;
152
+ action;
153
+ reads = /* @__PURE__ */ new Set();
154
+ record(node) {
155
+ if (node.receiver instanceof import_compiler.ThisReceiver) {
156
+ this.reads.add(node.name);
157
+ return;
158
+ }
159
+ if (node.receiver instanceof import_compiler.ImplicitReceiver && (!this.action || node.name !== "$event") && !this.boundTarget?.getExpressionTarget(node)) {
160
+ this.reads.add(node.name);
161
+ }
162
+ }
163
+ visitBinary(node, context) {
164
+ if (node.operation !== "=") {
165
+ return super.visitBinary(node, context);
166
+ }
167
+ if (node.left instanceof import_compiler.PropertyRead || node.left instanceof import_compiler.SafePropertyRead || node.left instanceof import_compiler.KeyedRead || node.left instanceof import_compiler.SafeKeyedRead) {
168
+ this.visit(node.left.receiver);
169
+ }
170
+ if (node.left instanceof import_compiler.KeyedRead || node.left instanceof import_compiler.SafeKeyedRead) {
171
+ this.visit(node.left.key);
172
+ }
173
+ this.visit(node.right);
174
+ return void 0;
175
+ }
176
+ visitPropertyRead(node, context) {
177
+ this.record(node);
178
+ return super.visitPropertyRead(node, context);
179
+ }
180
+ visitSafePropertyRead(node, context) {
181
+ this.record(node);
182
+ return super.visitSafePropertyRead(node, context);
183
+ }
184
+ };
185
+ var collectAngularExpressionReads = (nodes, boundTarget, action) => {
186
+ const collector = new ReadCollector(boundTarget, action);
187
+ for (const node of nodes) {
188
+ collector.visit(node);
189
+ }
190
+ return collector.reads;
191
+ };
192
+
193
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.ts
194
+ var externalTemplates = /* @__PURE__ */ new Map();
195
+ var parser = new import_compiler2.Parser(new import_compiler2.Lexer());
196
+ var templateBinder = new import_compiler2.R3TargetBinder(null);
197
+ var text = (node) => {
198
+ if (node?.type === import_utils.TSESTree.AST_NODE_TYPES.Literal && typeof node.value === "string") {
199
+ return node.value;
200
+ }
201
+ return node?.type === import_utils.TSESTree.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0 ? node.quasis[0].value.cooked : null;
202
+ };
203
+ var templateReads = (template, filename) => {
204
+ try {
205
+ const result = (0, import_compiler2.parseTemplate)(template, filename);
206
+ if (result.errors?.length) {
207
+ return null;
208
+ }
209
+ return collectAngularExpressionReads(
210
+ result.nodes,
211
+ templateBinder.bind({ template: result.nodes }),
212
+ false
213
+ );
214
+ } catch {
215
+ return null;
216
+ }
217
+ };
218
+ var externalTemplateReads = (filename) => {
219
+ try {
220
+ const stats = (0, import_node_fs.statSync)(filename, { bigint: true });
221
+ const version = `${stats.mtimeNs}:${stats.size}`;
222
+ const cached = externalTemplates.get(filename);
223
+ if (cached?.version === version) {
224
+ return cached.reads;
225
+ }
226
+ const reads = templateReads((0, import_node_fs.readFileSync)(filename, "utf8"), filename);
227
+ externalTemplates.set(filename, { reads, version });
228
+ return reads;
229
+ } catch {
230
+ return null;
231
+ }
232
+ };
233
+ var expressionReads = (expression, filename, action) => {
234
+ try {
235
+ const file = new import_compiler2.ParseSourceFile(expression, filename);
236
+ const start = new import_compiler2.ParseLocation(file, 0, 0, 0);
237
+ const span = new import_compiler2.ParseSourceSpan(start, start.moveBy(expression.length));
238
+ const result = action ? parser.parseAction(expression, span, 0) : parser.parseBinding(expression, span, 0);
239
+ if (result.errors.length > 0) {
240
+ return null;
241
+ }
242
+ return collectAngularExpressionReads([result], void 0, action);
243
+ } catch {
244
+ return null;
245
+ }
246
+ };
247
+ var addReads = (target, source) => {
248
+ if (!source) {
249
+ return false;
250
+ }
251
+ for (const name of source) {
252
+ target.add(name);
253
+ }
254
+ return true;
255
+ };
256
+ var key = (property) => {
257
+ if (property.type !== import_utils.TSESTree.AST_NODE_TYPES.Property || property.computed) {
258
+ return null;
259
+ }
260
+ return property.key.type === import_utils.TSESTree.AST_NODE_TYPES.Identifier ? property.key.name : text(property.key);
261
+ };
262
+ var metadataValue = (metadata, name) => {
263
+ for (const property of metadata.properties) {
264
+ if (property.type === import_utils.TSESTree.AST_NODE_TYPES.Property && key(property) === name) {
265
+ return property.value;
266
+ }
267
+ }
268
+ return void 0;
269
+ };
270
+ var hostPropertyReads = (property, reads, filename) => {
271
+ const name = key(property);
272
+ const expression = text(property.value);
273
+ if (name === null || expression === null) {
274
+ return false;
275
+ }
276
+ if (name.startsWith("[")) {
277
+ return addReads(reads, expressionReads(expression, filename, false));
278
+ }
279
+ if (name.startsWith("(")) {
280
+ return addReads(reads, expressionReads(expression, filename, true));
281
+ }
282
+ return true;
283
+ };
284
+ var hostReads = (host, reads, filename) => {
285
+ if (!host) {
286
+ return true;
287
+ }
288
+ if (host.type !== import_utils.TSESTree.AST_NODE_TYPES.ObjectExpression || host.properties.some((property) => key(property) === null)) {
289
+ return false;
290
+ }
291
+ for (const property of host.properties) {
292
+ if (property.type !== import_utils.TSESTree.AST_NODE_TYPES.Property || !hostPropertyReads(property, reads, filename)) {
293
+ return false;
294
+ }
295
+ }
296
+ return true;
297
+ };
298
+ var inlineTemplateReads = (template, reads, filename) => {
299
+ if (!template) {
300
+ return true;
301
+ }
302
+ const source = text(template);
303
+ return source !== null && addReads(reads, templateReads(source, filename));
304
+ };
305
+ var externalTemplatePropertyReads = (templateUrl, reads, filename) => {
306
+ if (!templateUrl) {
307
+ return true;
308
+ }
309
+ const url = text(templateUrl);
310
+ return url !== null && addReads(reads, externalTemplateReads((0, import_node_path.resolve)((0, import_node_path.dirname)(filename), url)));
311
+ };
312
+ var componentTemplateReads = (metadata, reads, filename) => {
313
+ const template = metadataValue(metadata, "template");
314
+ const templateUrl = metadataValue(metadata, "templateUrl");
315
+ return inlineTemplateReads(template, reads, filename) && externalTemplatePropertyReads(templateUrl, reads, filename);
316
+ };
317
+ var metadataReads = (metadata, component, filename, remainingNames) => {
318
+ if (metadata.properties.some((property) => key(property) === null)) {
319
+ return null;
320
+ }
321
+ const reads = /* @__PURE__ */ new Set();
322
+ if (!hostReads(metadataValue(metadata, "host"), reads, filename)) {
323
+ return null;
324
+ }
325
+ if (component && remainingNames.every((name) => reads.has(name)) || !component || componentTemplateReads(metadata, reads, filename)) {
326
+ return reads;
327
+ }
328
+ return null;
329
+ };
330
+
331
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-class-fields.ts
332
+ var managedApis = {
333
+ input: true,
334
+ model: true,
335
+ output: true
336
+ };
337
+ var angularName = (node, imports) => {
338
+ if (node.type === import_utils2.TSESTree.AST_NODE_TYPES.Identifier) {
339
+ return imports.get(node.name);
340
+ }
341
+ if (node.type !== import_utils2.TSESTree.AST_NODE_TYPES.MemberExpression || node.computed || node.property.type !== import_utils2.TSESTree.AST_NODE_TYPES.Identifier) {
342
+ return null;
343
+ }
344
+ if (node.object.type === import_utils2.TSESTree.AST_NODE_TYPES.Identifier && imports.get(node.object.name) === null) {
345
+ return node.property.name;
346
+ }
347
+ return angularName(node.object, imports);
348
+ };
349
+ var angularClass = (node, imports) => {
350
+ for (const decorator of node.decorators) {
351
+ if (decorator.expression.type !== import_utils2.TSESTree.AST_NODE_TYPES.CallExpression) {
352
+ continue;
353
+ }
354
+ const kind = angularName(decorator.expression.callee, imports);
355
+ const metadata = decorator.expression.arguments[0];
356
+ if ((kind === "Component" || kind === "Directive") && metadata?.type === import_utils2.TSESTree.AST_NODE_TYPES.ObjectExpression) {
357
+ return {
358
+ component: kind === "Component",
359
+ metadata
360
+ };
361
+ }
362
+ }
363
+ return null;
364
+ };
365
+ var isInstanceField = (node) => node.type === import_utils2.TSESTree.AST_NODE_TYPES.PropertyDefinition && node.key.type === import_utils2.TSESTree.AST_NODE_TYPES.Identifier;
366
+ var lifecycleHooks = {
367
+ ngOnChanges: true,
368
+ ngOnInit: true,
369
+ ngDoCheck: true,
370
+ ngAfterContentInit: true,
371
+ ngAfterContentChecked: true,
372
+ ngAfterViewInit: true,
373
+ ngAfterViewChecked: true,
374
+ ngOnDestroy: true
375
+ };
376
+ var isInstanceMethod = (node) => node.type === import_utils2.TSESTree.AST_NODE_TYPES.MethodDefinition && node.key.type === import_utils2.TSESTree.AST_NODE_TYPES.Identifier;
377
+ var isExcludedField = (node, component) => node.static || node.declare || node.override || node.decorators.length > 0 || !component && node.accessibility !== "private";
378
+ var isManagedField = (node, imports) => {
379
+ if (node.value?.type !== import_utils2.TSESTree.AST_NODE_TYPES.CallExpression) {
380
+ return false;
381
+ }
382
+ const name = angularName(node.value.callee, imports);
383
+ return typeof name === "string" && managedApis[name] === true;
384
+ };
385
+ var field = (node, imports, component) => {
386
+ if (isInstanceField(node) && !isExcludedField(node, component) && !isManagedField(node, imports)) {
387
+ return { messageId: "unusedField", name: node.key.name, node };
388
+ }
389
+ return null;
390
+ };
391
+ var isExcludedMethod = (node, component) => node.static || node.override || node.decorators.length > 0 || node.computed || node.kind !== "method" || node.value.body === null || lifecycleHooks[node.key.name] === true || !component && node.accessibility !== "private";
392
+ var method = (node, component) => {
393
+ if (isInstanceMethod(node) && !isExcludedMethod(node, component)) {
394
+ return { messageId: "unusedMethod", name: node.key.name, node };
395
+ }
396
+ return null;
397
+ };
398
+ var addAngularImport = (node, imports) => {
399
+ if (node.source.value !== "@angular/core" || node.importKind === "type") {
400
+ return;
401
+ }
402
+ for (const specifier of node.specifiers) {
403
+ if (specifier.type === import_utils2.TSESTree.AST_NODE_TYPES.ImportNamespaceSpecifier) {
404
+ imports.set(specifier.local.name, null);
405
+ } else if (specifier.type === import_utils2.TSESTree.AST_NODE_TYPES.ImportSpecifier && specifier.importKind !== "type") {
406
+ const importedName = specifier.imported.type === import_utils2.TSESTree.AST_NODE_TYPES.Identifier ? specifier.imported.name : specifier.imported.value;
407
+ imports.set(specifier.local.name, importedName);
408
+ }
409
+ }
410
+ };
411
+ var reportUnusedMembers = (context, imports, classes, dynamicClasses) => {
412
+ for (const entry of classes) {
413
+ const ngClass = angularClass(entry.node, imports);
414
+ if (!ngClass || dynamicClasses.has(entry)) {
415
+ continue;
416
+ }
417
+ const members = [];
418
+ for (const node of entry.node.body.body) {
419
+ const candidate = field(node, imports, ngClass.component) ?? method(node, ngClass.component);
420
+ if (candidate) {
421
+ members.push(candidate);
422
+ }
423
+ }
424
+ if (members.length === 0) {
425
+ continue;
426
+ }
427
+ const unreadMembers = members.filter(
428
+ (candidate) => !entry.reads.has(candidate.name)
429
+ );
430
+ if (unreadMembers.length === 0) {
431
+ continue;
432
+ }
433
+ const reads = metadataReads(
434
+ ngClass.metadata,
435
+ ngClass.component,
436
+ context.filename,
437
+ unreadMembers.map((candidate) => candidate.name)
438
+ );
439
+ if (!reads) {
440
+ continue;
441
+ }
442
+ for (const candidate of unreadMembers) {
443
+ if (!reads.has(candidate.name)) {
444
+ context.report({
445
+ data: { name: candidate.name },
446
+ messageId: candidate.messageId,
447
+ node: candidate.node.key
448
+ });
449
+ }
450
+ }
451
+ }
452
+ };
453
+
454
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.ts
455
+ var import_utils3 = require("@typescript-eslint/utils");
456
+ var isTypeScriptExpressionWrapper = (node) => {
457
+ switch (node.type) {
458
+ case import_utils3.TSESTree.AST_NODE_TYPES.TSAsExpression:
459
+ case import_utils3.TSESTree.AST_NODE_TYPES.TSInstantiationExpression:
460
+ case import_utils3.TSESTree.AST_NODE_TYPES.TSNonNullExpression:
461
+ case import_utils3.TSESTree.AST_NODE_TYPES.TSSatisfiesExpression:
462
+ case import_utils3.TSESTree.AST_NODE_TYPES.TSTypeAssertion:
463
+ return true;
464
+ default:
465
+ return false;
466
+ }
467
+ };
468
+ var unwrapTypeScriptExpression = (node) => {
469
+ let current = node;
470
+ while (current && isTypeScriptExpressionWrapper(current)) {
471
+ current = current.expression;
472
+ }
473
+ return current;
474
+ };
475
+ var isThisExpression = (node) => unwrapTypeScriptExpression(node)?.type === import_utils3.TSESTree.AST_NODE_TYPES.ThisExpression;
476
+ var isPatternTarget = (parent, child) => {
477
+ switch (parent?.type) {
478
+ case import_utils3.TSESTree.AST_NODE_TYPES.ArrayPattern:
479
+ return parent.elements.some((element) => element === child);
480
+ case import_utils3.TSESTree.AST_NODE_TYPES.AssignmentPattern:
481
+ return parent.left === child;
482
+ case import_utils3.TSESTree.AST_NODE_TYPES.ObjectPattern:
483
+ return parent.properties.some((property) => property === child);
484
+ case import_utils3.TSESTree.AST_NODE_TYPES.Property:
485
+ return parent.value === child && parent.parent.type === import_utils3.TSESTree.AST_NODE_TYPES.ObjectPattern;
486
+ case import_utils3.TSESTree.AST_NODE_TYPES.RestElement:
487
+ return parent.argument === child;
488
+ default:
489
+ return false;
490
+ }
491
+ };
492
+ var isSimpleAssignmentTarget = (parent, current) => parent?.type === import_utils3.TSESTree.AST_NODE_TYPES.AssignmentExpression && parent.left === current && parent.operator === "=";
493
+ var isLoopTarget = (parent, current) => (parent?.type === import_utils3.TSESTree.AST_NODE_TYPES.ForInStatement || parent?.type === import_utils3.TSESTree.AST_NODE_TYPES.ForOfStatement) && parent.left === current;
494
+ var isDirectWrite = (node) => {
495
+ let current = node;
496
+ let parent = node.parent;
497
+ while (parent && isTypeScriptExpressionWrapper(parent) && parent.expression === current) {
498
+ current = parent;
499
+ parent = parent.parent;
500
+ }
501
+ return isSimpleAssignmentTarget(parent, current) || parent?.type === import_utils3.TSESTree.AST_NODE_TYPES.UnaryExpression && parent.operator === "delete";
502
+ };
503
+ var isDestructuringOrLoopTarget = (node) => {
504
+ let current = node;
505
+ let parent = node.parent;
506
+ let patternTarget = false;
507
+ while (parent) {
508
+ const wrapper = isTypeScriptExpressionWrapper(parent) && parent.expression === current;
509
+ const pattern = isPatternTarget(parent, current);
510
+ if (!wrapper && !pattern) {
511
+ break;
512
+ }
513
+ if (pattern) {
514
+ patternTarget = true;
515
+ }
516
+ current = parent;
517
+ parent = parent.parent;
518
+ }
519
+ return patternTarget && isSimpleAssignmentTarget(parent, current) || isLoopTarget(parent, current);
520
+ };
521
+ var isWriteOnly = (node) => isDirectWrite(node) || isDestructuringOrLoopTarget(node);
522
+ var destructuringParts = (node) => {
523
+ if (node.type === import_utils3.TSESTree.AST_NODE_TYPES.VariableDeclarator) {
524
+ return { pattern: node.id, source: node.init };
525
+ }
526
+ if (node.operator === "=") {
527
+ return { pattern: node.left, source: node.right };
528
+ }
529
+ return null;
530
+ };
531
+ var propertyName = (property) => {
532
+ if (property.type !== import_utils3.TSESTree.AST_NODE_TYPES.Property || property.computed) {
533
+ return null;
534
+ }
535
+ if (property.key.type === import_utils3.TSESTree.AST_NODE_TYPES.Identifier) {
536
+ return property.key.name;
537
+ }
538
+ return property.key.type === import_utils3.TSESTree.AST_NODE_TYPES.Literal && typeof property.key.value === "string" ? property.key.value : null;
539
+ };
540
+ var destructuredThisReads = (node) => {
541
+ const parts = destructuringParts(node);
542
+ if (!parts || parts.pattern.type !== import_utils3.TSESTree.AST_NODE_TYPES.ObjectPattern || !isThisExpression(parts.source)) {
543
+ return void 0;
544
+ }
545
+ const reads = [];
546
+ for (const property of parts.pattern.properties) {
547
+ const name = propertyName(property);
548
+ if (name === null) {
549
+ return null;
550
+ }
551
+ reads.push(name);
552
+ }
553
+ return reads;
554
+ };
555
+
556
+ // packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.ts
557
+ var createRule = import_utils4.ESLintUtils.RuleCreator(
558
+ () => "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
559
+ );
560
+ var enterClass = (node, classes, stack, thisStack) => {
561
+ const entry = {
562
+ node,
563
+ reads: /* @__PURE__ */ new Set()
564
+ };
565
+ classes.push(entry);
566
+ stack.push(entry);
567
+ thisStack.push(true);
568
+ };
569
+ var componentThis = (node, thisStack) => node.type === import_utils4.TSESTree.AST_NODE_TYPES.ArrowFunctionExpression ? thisStack.at(-1) ?? false : node.parent.type === import_utils4.TSESTree.AST_NODE_TYPES.MethodDefinition && !node.parent.static;
570
+ var trackMemberRead = (node, stack, thisStack, dynamicClasses) => {
571
+ const current = stack.at(-1);
572
+ if (!current || !thisStack.at(-1)) {
573
+ return;
574
+ }
575
+ const receiverIsThis = isThisExpression(node.object);
576
+ if (node.computed && receiverIsThis) {
577
+ dynamicClasses.add(current);
578
+ }
579
+ if (receiverIsThis && !node.computed && node.property.type === import_utils4.TSESTree.AST_NODE_TYPES.Identifier && !isWriteOnly(node)) {
580
+ current.reads.add(node.property.name);
581
+ }
582
+ };
583
+ var trackDestructuringRead = (node, stack, thisStack, dynamicClasses) => {
584
+ const current = stack.at(-1);
585
+ if (!current || !thisStack.at(-1)) {
586
+ return;
587
+ }
588
+ const reads = destructuredThisReads(node);
589
+ if (reads === void 0) {
590
+ return;
591
+ }
592
+ if (reads === null) {
593
+ dynamicClasses.add(current);
594
+ return;
595
+ }
596
+ for (const name of reads) {
597
+ current.reads.add(name);
598
+ }
599
+ };
600
+ var lexicalThisVisitor = (thisStack) => ({
601
+ FunctionDeclaration(node) {
602
+ thisStack.push(componentThis(node, thisStack));
603
+ },
604
+ FunctionExpression(node) {
605
+ thisStack.push(componentThis(node, thisStack));
606
+ },
607
+ ArrowFunctionExpression(node) {
608
+ thisStack.push(componentThis(node, thisStack));
609
+ },
610
+ "FunctionDeclaration:exit"() {
611
+ thisStack.pop();
612
+ },
613
+ "FunctionExpression:exit"() {
614
+ thisStack.pop();
615
+ },
616
+ "ArrowFunctionExpression:exit"() {
617
+ thisStack.pop();
618
+ },
619
+ StaticBlock() {
620
+ thisStack.push(false);
621
+ },
622
+ "StaticBlock:exit"() {
623
+ thisStack.pop();
624
+ },
625
+ PropertyDefinition(node) {
626
+ if (node.static) {
627
+ thisStack.push(false);
628
+ }
629
+ },
630
+ "PropertyDefinition:exit"(node) {
631
+ if (node.static) {
632
+ thisStack.pop();
633
+ }
634
+ }
635
+ });
636
+ var visitor = (context, imports, classes, stack, dynamicClasses) => {
637
+ const thisStack = [];
638
+ return {
639
+ ClassDeclaration(node) {
640
+ enterClass(node, classes, stack, thisStack);
641
+ },
642
+ ClassExpression(node) {
643
+ enterClass(node, classes, stack, thisStack);
644
+ },
645
+ "ClassDeclaration:exit"() {
646
+ stack.pop();
647
+ thisStack.pop();
648
+ },
649
+ "ClassExpression:exit"() {
650
+ stack.pop();
651
+ thisStack.pop();
652
+ },
653
+ ...lexicalThisVisitor(thisStack),
654
+ AssignmentExpression(node) {
655
+ trackDestructuringRead(node, stack, thisStack, dynamicClasses);
656
+ },
657
+ MemberExpression(node) {
658
+ trackMemberRead(node, stack, thisStack, dynamicClasses);
659
+ },
660
+ VariableDeclarator(node) {
661
+ trackDestructuringRead(node, stack, thisStack, dynamicClasses);
662
+ },
663
+ "Program:exit"() {
664
+ reportUnusedMembers(context, imports, classes, dynamicClasses);
665
+ }
666
+ };
667
+ };
668
+ var no_unused_angular_instance_fields_default = createRule({
669
+ name: "no-unused-instance-fields",
670
+ meta: {
671
+ type: "problem",
672
+ docs: {
673
+ description: "Disallow unread instance fields and methods in Angular components and directives"
674
+ },
675
+ messages: {
676
+ unusedField: "Angular instance field '{{name}}' is never read.",
677
+ unusedMethod: "Angular instance method '{{name}}' is never read."
678
+ },
679
+ schema: []
680
+ },
681
+ defaultOptions: [],
682
+ create(context) {
683
+ const imports = /* @__PURE__ */ new Map();
684
+ for (const node of context.sourceCode.ast.body) {
685
+ if (node.type === import_utils4.TSESTree.AST_NODE_TYPES.ImportDeclaration) {
686
+ addAngularImport(node, imports);
687
+ }
688
+ }
689
+ if (imports.size === 0) {
690
+ return {};
691
+ }
692
+ const classes = [];
693
+ const stack = [];
694
+ const dynamicClasses = /* @__PURE__ */ new Set();
695
+ return visitor(context, imports, classes, stack, dynamicClasses);
696
+ }
697
+ });
698
+
699
+ // packages/lint-suite/src/lib/angular.ts
700
+ var lintSuiteAngularPlugin = {
701
+ rules: {
702
+ "no-unused-instance-fields": no_unused_angular_instance_fields_default
703
+ }
704
+ };
130
705
  var angular = (0, import_config2.defineConfig)([
131
706
  {
132
707
  files: ["**/*.ts"],
133
708
  extends: [...import_angular_eslint2.configs.tsRecommended],
709
+ plugins: {
710
+ "lint-suite-angular": lintSuiteAngularPlugin
711
+ },
134
712
  languageOptions: {
135
713
  globals: {
136
714
  ...import_globals.default.browser,
@@ -140,7 +718,9 @@ var angular = (0, import_config2.defineConfig)([
140
718
  },
141
719
  processor: import_angular_eslint2.processInlineTemplates,
142
720
  rules: {
721
+ "lint-suite-angular/no-unused-instance-fields": "error",
143
722
  "@angular-eslint/prefer-on-push-component-change-detection": "error",
723
+ // v22: flags only explicit opt-out of OnPush (omission now means OnPush)
144
724
  "@angular-eslint/prefer-signals": "error",
145
725
  "@angular-eslint/use-injectable-provided-in": "error",
146
726
  "@angular-eslint/prefer-signal-model": "error",
@@ -158,8 +738,6 @@ var angular = (0, import_config2.defineConfig)([
158
738
  // Consistent lifecycle method ordering
159
739
  "@angular-eslint/computed-must-return": "error",
160
740
  // Catch silent computed() bugs missing a return
161
- "@angular-eslint/no-conflicting-lifecycle": "error",
162
- // Block incompatible hook combinations (OnChanges + DoCheck)
163
741
  "@angular-eslint/no-duplicates-in-metadata-arrays": "error",
164
742
  // Catch duplicate imports/providers in @Component/@NgModule
165
743
  "@angular-eslint/no-lifecycle-call": "error",
@@ -525,8 +1103,8 @@ var import_eslint_plugin_import_x2 = __toESM(require("eslint-plugin-import-x"),
525
1103
  var import_config9 = require("eslint/config");
526
1104
 
527
1105
  // packages/lint-suite/src/lib/rules/explicit-accessibility.ts
528
- var import_utils = require("@typescript-eslint/utils");
529
- var createRule = import_utils.ESLintUtils.RuleCreator(
1106
+ var import_utils5 = require("@typescript-eslint/utils");
1107
+ var createRule2 = import_utils5.ESLintUtils.RuleCreator(
530
1108
  () => "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
531
1109
  );
532
1110
  var defaultOptions = {
@@ -537,10 +1115,10 @@ var getNodeName = (node) => {
537
1115
  return "<unknown>";
538
1116
  }
539
1117
  if ("key" in node && node.key) {
540
- if (node.key.type === import_utils.TSESTree.AST_NODE_TYPES.Identifier) {
1118
+ if (node.key.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
541
1119
  return node.key.name;
542
1120
  }
543
- if (node.key.type === import_utils.TSESTree.AST_NODE_TYPES.PrivateIdentifier) {
1121
+ if (node.key.type === import_utils5.TSESTree.AST_NODE_TYPES.PrivateIdentifier) {
544
1122
  return `#${node.key.name}`;
545
1123
  }
546
1124
  return "<computed/complex key>";
@@ -548,13 +1126,13 @@ var getNodeName = (node) => {
548
1126
  if ("kind" in node && node.kind === "constructor") {
549
1127
  return "constructor";
550
1128
  }
551
- if (node.type === import_utils.TSESTree.AST_NODE_TYPES.TSParameterProperty) {
1129
+ if (node.type === import_utils5.TSESTree.AST_NODE_TYPES.TSParameterProperty) {
552
1130
  const param = node.parameter;
553
- if (param.type === import_utils.TSESTree.AST_NODE_TYPES.Identifier) {
1131
+ if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
554
1132
  return param.name;
555
1133
  }
556
- if (param.type === import_utils.TSESTree.AST_NODE_TYPES.AssignmentPattern) {
557
- if (param.left.type === import_utils.TSESTree.AST_NODE_TYPES.Identifier) {
1134
+ if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.AssignmentPattern) {
1135
+ if (param.left.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
558
1136
  return param.left.name;
559
1137
  }
560
1138
  return "<destructured parameter>";
@@ -563,7 +1141,7 @@ var getNodeName = (node) => {
563
1141
  }
564
1142
  return "<unknown>";
565
1143
  };
566
- var explicit_accessibility_default = createRule({
1144
+ var explicit_accessibility_default = createRule2({
567
1145
  name: "explicit-accessibility",
568
1146
  meta: {
569
1147
  type: "suggestion",
@@ -598,13 +1176,13 @@ var explicit_accessibility_default = createRule({
598
1176
  }
599
1177
  let reportNode = node;
600
1178
  const name = getNodeName(node);
601
- if (node.type === import_utils.TSESTree.AST_NODE_TYPES.MethodDefinition || node.type === import_utils.TSESTree.AST_NODE_TYPES.PropertyDefinition) {
1179
+ if (node.type === import_utils5.TSESTree.AST_NODE_TYPES.MethodDefinition || node.type === import_utils5.TSESTree.AST_NODE_TYPES.PropertyDefinition) {
602
1180
  reportNode = node.key;
603
1181
  } else {
604
1182
  const param = node.parameter;
605
- if (param.type === import_utils.TSESTree.AST_NODE_TYPES.Identifier) {
1183
+ if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
606
1184
  reportNode = param;
607
- } else if (param.type === import_utils.TSESTree.AST_NODE_TYPES.AssignmentPattern && param.left.type === import_utils.TSESTree.AST_NODE_TYPES.Identifier) {
1185
+ } else if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.AssignmentPattern && param.left.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
608
1186
  reportNode = param.left;
609
1187
  }
610
1188
  }
@@ -621,7 +1199,7 @@ var explicit_accessibility_default = createRule({
621
1199
  } else {
622
1200
  tokenToInsertBefore = sourceCode.getFirstToken(node);
623
1201
  }
624
- if (node.type === import_utils.TSESTree.AST_NODE_TYPES.TSParameterProperty && node.readonly) {
1202
+ if (node.type === import_utils5.TSESTree.AST_NODE_TYPES.TSParameterProperty && node.readonly) {
625
1203
  const firstToken = sourceCode.getFirstToken(node);
626
1204
  if (firstToken?.value === "readonly") {
627
1205
  tokenToInsertBefore = firstToken;
@@ -926,7 +1504,7 @@ var typescript = (0, import_config9.defineConfig)([
926
1504
  ],
927
1505
  rules: {
928
1506
  "@typescript-eslint/no-empty-function": "off",
929
- "@typescript-eslint/no-unbound-method": "off",
1507
+ "@typescript-eslint/unbound-method": "off",
930
1508
  "@typescript-eslint/consistent-type-assertions": "off"
931
1509
  }
932
1510
  },
@@ -1048,53 +1626,53 @@ var boundaries = (0, import_config12.defineConfig)([
1048
1626
  "error",
1049
1627
  {
1050
1628
  default: "disallow",
1051
- message: "${dependency.type} is not allowed to be imported in ${file.type}",
1052
- rules: [
1629
+ message: "{{to.element.types}} is not allowed to be imported in {{from.element.types}}",
1630
+ policies: [
1053
1631
  {
1054
- from: { type: "feature" },
1632
+ from: { element: { type: "feature" } },
1055
1633
  allow: [
1056
- { to: { type: "feature" } },
1057
- { to: { type: "ui" } },
1058
- { to: { type: "domain-logic" } },
1059
- { to: { type: "utils" } },
1060
- { to: { type: "common" } }
1634
+ { to: { element: { type: "feature" } } },
1635
+ { to: { element: { type: "ui" } } },
1636
+ { to: { element: { type: "domain-logic" } } },
1637
+ { to: { element: { type: "utils" } } },
1638
+ { to: { element: { type: "common" } } }
1061
1639
  ]
1062
1640
  },
1063
1641
  {
1064
- from: { type: "domain-logic" },
1642
+ from: { element: { type: "domain-logic" } },
1065
1643
  allow: [
1066
- { to: { type: "domain-logic" } },
1067
- { to: { type: "data-access" } },
1068
- { to: { type: "utils" } },
1069
- { to: { type: "common" } }
1644
+ { to: { element: { type: "domain-logic" } } },
1645
+ { to: { element: { type: "data-access" } } },
1646
+ { to: { element: { type: "utils" } } },
1647
+ { to: { element: { type: "common" } } }
1070
1648
  ]
1071
1649
  },
1072
1650
  {
1073
- from: { type: "data-access" },
1651
+ from: { element: { type: "data-access" } },
1074
1652
  allow: [
1075
- { to: { type: "data-access" } },
1076
- { to: { type: "utils" } },
1077
- { to: { type: "common" } }
1653
+ { to: { element: { type: "data-access" } } },
1654
+ { to: { element: { type: "utils" } } },
1655
+ { to: { element: { type: "common" } } }
1078
1656
  ]
1079
1657
  },
1080
1658
  {
1081
- from: { type: "ui" },
1659
+ from: { element: { type: "ui" } },
1082
1660
  allow: [
1083
- { to: { type: "ui" } },
1084
- { to: { type: "utils" } },
1085
- { to: { type: "common" } }
1661
+ { to: { element: { type: "ui" } } },
1662
+ { to: { element: { type: "utils" } } },
1663
+ { to: { element: { type: "common" } } }
1086
1664
  ]
1087
1665
  },
1088
1666
  {
1089
- from: { type: "utils" },
1667
+ from: { element: { type: "utils" } },
1090
1668
  allow: [
1091
- { to: { type: "utils" } },
1092
- { to: { type: "common" } }
1669
+ { to: { element: { type: "utils" } } },
1670
+ { to: { element: { type: "common" } } }
1093
1671
  ]
1094
1672
  },
1095
1673
  {
1096
- from: { type: "common" },
1097
- allow: [{ to: { type: "common" } }]
1674
+ from: { element: { type: "common" } },
1675
+ allow: [{ to: { element: { type: "common" } } }]
1098
1676
  }
1099
1677
  ]
1100
1678
  }