lint-suite 1.5.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.
- package/eslint.cjs +596 -17
- package/eslint.cjs.map +4 -4
- package/eslint.js +609 -14
- package/eslint.js.map +4 -4
- package/lib/angular-template.d.ts +1 -2
- package/lib/angular-template.d.ts.map +1 -1
- package/lib/angular.d.ts +1 -2
- package/lib/angular.d.ts.map +1 -1
- package/lib/base.d.ts +1 -1
- package/lib/base.d.ts.map +1 -1
- package/lib/boundaries.d.ts +1 -1
- package/lib/boundaries.d.ts.map +1 -1
- package/lib/javascript.d.ts +1 -2
- package/lib/javascript.d.ts.map +1 -1
- package/lib/json.d.ts +1 -1
- package/lib/json.d.ts.map +1 -1
- package/lib/playwright.d.ts +1 -1
- package/lib/playwright.d.ts.map +1 -1
- package/lib/prettier.d.ts +1 -1
- package/lib/prettier.d.ts.map +1 -1
- package/lib/rules/no-unused-angular-instance-fields/angular-class-fields.d.ts +8 -0
- package/lib/rules/no-unused-angular-instance-fields/angular-class-fields.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.d.ts +3 -0
- package/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/common/no-unused-angular-instance-fields.type.d.ts +10 -0
- package/lib/rules/no-unused-angular-instance-fields/common/no-unused-angular-instance-fields.type.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.d.ts +7 -0
- package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/no-unused-instance-fields.spec.d.ts +2 -0
- package/lib/rules/no-unused-angular-instance-fields/no-unused-instance-fields.spec.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/utils/angular-expression-reads.util.d.ts +6 -0
- package/lib/rules/no-unused-angular-instance-fields/utils/angular-expression-reads.util.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.d.ts +7 -0
- package/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.d.ts.map +1 -0
- package/lib/rxjs.d.ts +1 -1
- package/lib/rxjs.d.ts.map +1 -1
- package/lib/storybook.d.ts +1 -1
- package/lib/storybook.d.ts.map +1 -1
- package/lib/typescript.d.ts +1 -2
- package/lib/typescript.d.ts.map +1 -1
- package/lib/vitest.d.ts +1 -1
- package/lib/vitest.d.ts.map +1 -1
- package/package.json +18 -17
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
|
|
15
|
-
if (!__hasOwnProp.call(to,
|
|
16
|
-
__defProp(to,
|
|
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,6 +718,7 @@ 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",
|
|
144
723
|
// v22: flags only explicit opt-out of OnPush (omission now means OnPush)
|
|
145
724
|
"@angular-eslint/prefer-signals": "error",
|
|
@@ -524,8 +1103,8 @@ var import_eslint_plugin_import_x2 = __toESM(require("eslint-plugin-import-x"),
|
|
|
524
1103
|
var import_config9 = require("eslint/config");
|
|
525
1104
|
|
|
526
1105
|
// packages/lint-suite/src/lib/rules/explicit-accessibility.ts
|
|
527
|
-
var
|
|
528
|
-
var
|
|
1106
|
+
var import_utils5 = require("@typescript-eslint/utils");
|
|
1107
|
+
var createRule2 = import_utils5.ESLintUtils.RuleCreator(
|
|
529
1108
|
() => "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
|
|
530
1109
|
);
|
|
531
1110
|
var defaultOptions = {
|
|
@@ -536,10 +1115,10 @@ var getNodeName = (node) => {
|
|
|
536
1115
|
return "<unknown>";
|
|
537
1116
|
}
|
|
538
1117
|
if ("key" in node && node.key) {
|
|
539
|
-
if (node.key.type ===
|
|
1118
|
+
if (node.key.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
|
|
540
1119
|
return node.key.name;
|
|
541
1120
|
}
|
|
542
|
-
if (node.key.type ===
|
|
1121
|
+
if (node.key.type === import_utils5.TSESTree.AST_NODE_TYPES.PrivateIdentifier) {
|
|
543
1122
|
return `#${node.key.name}`;
|
|
544
1123
|
}
|
|
545
1124
|
return "<computed/complex key>";
|
|
@@ -547,13 +1126,13 @@ var getNodeName = (node) => {
|
|
|
547
1126
|
if ("kind" in node && node.kind === "constructor") {
|
|
548
1127
|
return "constructor";
|
|
549
1128
|
}
|
|
550
|
-
if (node.type ===
|
|
1129
|
+
if (node.type === import_utils5.TSESTree.AST_NODE_TYPES.TSParameterProperty) {
|
|
551
1130
|
const param = node.parameter;
|
|
552
|
-
if (param.type ===
|
|
1131
|
+
if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
|
|
553
1132
|
return param.name;
|
|
554
1133
|
}
|
|
555
|
-
if (param.type ===
|
|
556
|
-
if (param.left.type ===
|
|
1134
|
+
if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.AssignmentPattern) {
|
|
1135
|
+
if (param.left.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
|
|
557
1136
|
return param.left.name;
|
|
558
1137
|
}
|
|
559
1138
|
return "<destructured parameter>";
|
|
@@ -562,7 +1141,7 @@ var getNodeName = (node) => {
|
|
|
562
1141
|
}
|
|
563
1142
|
return "<unknown>";
|
|
564
1143
|
};
|
|
565
|
-
var explicit_accessibility_default =
|
|
1144
|
+
var explicit_accessibility_default = createRule2({
|
|
566
1145
|
name: "explicit-accessibility",
|
|
567
1146
|
meta: {
|
|
568
1147
|
type: "suggestion",
|
|
@@ -597,13 +1176,13 @@ var explicit_accessibility_default = createRule({
|
|
|
597
1176
|
}
|
|
598
1177
|
let reportNode = node;
|
|
599
1178
|
const name = getNodeName(node);
|
|
600
|
-
if (node.type ===
|
|
1179
|
+
if (node.type === import_utils5.TSESTree.AST_NODE_TYPES.MethodDefinition || node.type === import_utils5.TSESTree.AST_NODE_TYPES.PropertyDefinition) {
|
|
601
1180
|
reportNode = node.key;
|
|
602
1181
|
} else {
|
|
603
1182
|
const param = node.parameter;
|
|
604
|
-
if (param.type ===
|
|
1183
|
+
if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
|
|
605
1184
|
reportNode = param;
|
|
606
|
-
} else if (param.type ===
|
|
1185
|
+
} else if (param.type === import_utils5.TSESTree.AST_NODE_TYPES.AssignmentPattern && param.left.type === import_utils5.TSESTree.AST_NODE_TYPES.Identifier) {
|
|
607
1186
|
reportNode = param.left;
|
|
608
1187
|
}
|
|
609
1188
|
}
|
|
@@ -620,7 +1199,7 @@ var explicit_accessibility_default = createRule({
|
|
|
620
1199
|
} else {
|
|
621
1200
|
tokenToInsertBefore = sourceCode.getFirstToken(node);
|
|
622
1201
|
}
|
|
623
|
-
if (node.type ===
|
|
1202
|
+
if (node.type === import_utils5.TSESTree.AST_NODE_TYPES.TSParameterProperty && node.readonly) {
|
|
624
1203
|
const firstToken = sourceCode.getFirstToken(node);
|
|
625
1204
|
if (firstToken?.value === "readonly") {
|
|
626
1205
|
tokenToInsertBefore = firstToken;
|
|
@@ -925,7 +1504,7 @@ var typescript = (0, import_config9.defineConfig)([
|
|
|
925
1504
|
],
|
|
926
1505
|
rules: {
|
|
927
1506
|
"@typescript-eslint/no-empty-function": "off",
|
|
928
|
-
"@typescript-eslint/
|
|
1507
|
+
"@typescript-eslint/unbound-method": "off",
|
|
929
1508
|
"@typescript-eslint/consistent-type-assertions": "off"
|
|
930
1509
|
}
|
|
931
1510
|
},
|