lint-suite 1.6.0 → 1.6.2
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/README.md +161 -16
- package/eslint.cjs +3085 -998
- package/eslint.cjs.map +4 -4
- package/eslint.d.ts +13 -13
- package/eslint.d.ts.map +1 -1
- package/eslint.js +3173 -990
- package/eslint.js.map +4 -4
- package/lib/angular-template.d.ts.map +1 -1
- package/lib/angular.d.ts +1 -0
- package/lib/angular.d.ts.map +1 -1
- package/lib/base-limits.d.ts +2 -0
- package/lib/base-limits.d.ts.map +1 -0
- package/lib/base-practices.d.ts +2 -0
- package/lib/base-practices.d.ts.map +1 -0
- package/lib/base-recommended.d.ts +2 -0
- package/lib/base-recommended.d.ts.map +1 -0
- package/lib/base.d.ts.map +1 -1
- package/lib/javascript.d.ts.map +1 -1
- package/lib/rules/define-plugin.util.d.ts +6 -0
- package/lib/rules/define-plugin.util.d.ts.map +1 -0
- package/lib/rules/explicit-accessibility/common/explicit-accessibility.const.d.ts +6 -0
- package/lib/rules/explicit-accessibility/common/explicit-accessibility.const.d.ts.map +1 -0
- package/lib/rules/explicit-accessibility/common/explicit-accessibility.type.d.ts +14 -0
- package/lib/rules/explicit-accessibility/common/explicit-accessibility.type.d.ts.map +1 -0
- package/lib/rules/explicit-accessibility/explicit-accessibility.d.ts +7 -0
- package/lib/rules/explicit-accessibility/explicit-accessibility.d.ts.map +1 -0
- package/lib/rules/no-inline-object-types/no-inline-object-types.d.ts +6 -0
- package/lib/rules/no-inline-object-types/no-inline-object-types.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.d.ts +5 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-scope.d.ts +9 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-scope.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-decorator-kind.d.ts +10 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-decorator-kind.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-metadata-values.d.ts +12 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-metadata-values.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.d.ts +3 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-chains.d.ts +17 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-chains.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.d.ts +8 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.d.ts +6 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-references.d.ts +5 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-references.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-index.type.d.ts +54 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-index.type.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-usage.type.d.ts +116 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/common/project-usage.type.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/project-file-entry.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/project-file-entry.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-staleness.d.ts +10 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-staleness.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.d.ts +17 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-angular-interface-methods.d.ts +8 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-angular-interface-methods.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-assignment-pattern-elements.d.ts +5 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-assignment-pattern-elements.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-binding-pattern-elements.d.ts +6 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-binding-pattern-elements.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-candidate-names.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-candidate-names.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-symbol-reads.d.ts +8 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-symbol-reads.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-type-dependencies.d.ts +3 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-type-dependencies.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-write-targets.d.ts +7 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-write-targets.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/read-chain-text.util.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/read-chain-text.util.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/spec-file.util.d.ts +2 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/spec-file.util.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/type-property-symbols.util.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/project-usage/utils/type-property-symbols.util.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.d.ts +3 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/{utils/angular-expression-reads.util.d.ts → rule/angular/angular-expression-reads.d.ts} +1 -1
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-expression-reads.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-imports.d.ts +9 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-imports.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-managed-fields.d.ts +5 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-managed-fields.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-member-candidates.d.ts +6 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-member-candidates.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-literals.d.ts +5 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-literals.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.d.ts +9 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-template-parsing.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-template-parsing.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/common/no-unused-angular-instance-fields.type.d.ts +54 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/common/no-unused-angular-instance-fields.type.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.d.ts +8 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-class-read-visitor.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-class-read-visitor.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-field-reads.d.ts +8 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-field-reads.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-lexical-this.d.ts +3 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-lexical-this.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-write-targets.d.ts +3 -0
- package/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-write-targets.d.ts.map +1 -0
- package/lib/rules/no-unused-angular-instance-fields/utils/angular-read-target.util.d.ts +4 -0
- package/lib/rules/no-unused-angular-instance-fields/utils/angular-read-target.util.d.ts.map +1 -0
- package/lib/rules/readonly-type-properties/readonly-type-properties.d.ts +6 -0
- package/lib/rules/readonly-type-properties/readonly-type-properties.d.ts.map +1 -0
- package/lib/typescript-consistency.d.ts +2 -0
- package/lib/typescript-consistency.d.ts.map +1 -0
- package/lib/typescript-imports.d.ts +2 -0
- package/lib/typescript-imports.d.ts.map +1 -0
- package/lib/typescript-local-plugin.d.ts +2 -0
- package/lib/typescript-local-plugin.d.ts.map +1 -0
- package/lib/typescript-overrides.d.ts +2 -0
- package/lib/typescript-overrides.d.ts.map +1 -0
- package/lib/typescript-recommended.d.ts +2 -0
- package/lib/typescript-recommended.d.ts.map +1 -0
- package/lib/typescript-safety.d.ts +2 -0
- package/lib/typescript-safety.d.ts.map +1 -0
- package/lib/typescript.d.ts +1 -0
- package/lib/typescript.d.ts.map +1 -1
- package/package.json +18 -14
- package/lib/rules/explicit-accessibility.d.ts +0 -12
- package/lib/rules/explicit-accessibility.d.ts.map +0 -1
- package/lib/rules/no-unused-angular-instance-fields/angular-class-fields.d.ts +0 -8
- package/lib/rules/no-unused-angular-instance-fields/angular-class-fields.d.ts.map +0 -1
- package/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.d.ts +0 -3
- package/lib/rules/no-unused-angular-instance-fields/angular-metadata-reads.d.ts.map +0 -1
- package/lib/rules/no-unused-angular-instance-fields/common/no-unused-angular-instance-fields.type.d.ts +0 -10
- package/lib/rules/no-unused-angular-instance-fields/common/no-unused-angular-instance-fields.type.d.ts.map +0 -1
- package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.d.ts +0 -7
- package/lib/rules/no-unused-angular-instance-fields/no-unused-angular-instance-fields.d.ts.map +0 -1
- package/lib/rules/no-unused-angular-instance-fields/no-unused-instance-fields.spec.d.ts +0 -2
- package/lib/rules/no-unused-angular-instance-fields/no-unused-instance-fields.spec.d.ts.map +0 -1
- package/lib/rules/no-unused-angular-instance-fields/utils/angular-expression-reads.util.d.ts.map +0 -1
- package/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.d.ts +0 -7
- package/lib/rules/no-unused-angular-instance-fields/utils/typescript-field-reads.util.d.ts.map +0 -1
package/eslint.js
CHANGED
|
@@ -4,10 +4,7 @@ import { defineConfig } from "eslint/config";
|
|
|
4
4
|
var angularTemplate = defineConfig([
|
|
5
5
|
{
|
|
6
6
|
files: ["**/*.html"],
|
|
7
|
-
extends: [
|
|
8
|
-
...configs.templateAccessibility,
|
|
9
|
-
...configs.templateRecommended
|
|
10
|
-
],
|
|
7
|
+
extends: [...configs.templateAccessibility, ...configs.templateRecommended],
|
|
11
8
|
rules: {
|
|
12
9
|
"@angular-eslint/template/prefer-template-literal": "error",
|
|
13
10
|
"@angular-eslint/template/no-any": "error",
|
|
@@ -76,19 +73,299 @@ var angularTemplate = defineConfig([
|
|
|
76
73
|
]);
|
|
77
74
|
|
|
78
75
|
// packages/lint-suite/src/lib/angular.ts
|
|
79
|
-
import globals from "globals";
|
|
80
76
|
import { configs as configs2, processInlineTemplates } from "angular-eslint";
|
|
81
77
|
import { defineConfig as defineConfig2 } from "eslint/config";
|
|
78
|
+
import globals from "globals";
|
|
79
|
+
|
|
80
|
+
// packages/lint-suite/src/lib/rules/define-plugin.util.ts
|
|
81
|
+
var definePlugin = (name, rules) => {
|
|
82
|
+
const meta5 = { name };
|
|
83
|
+
const plugin = { meta: meta5, rules };
|
|
84
|
+
return plugin;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.ts
|
|
88
|
+
import { ESLintUtils } from "@typescript-eslint/utils";
|
|
89
|
+
|
|
90
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-imports.ts
|
|
91
|
+
import { TSESTree } from "@typescript-eslint/utils";
|
|
92
|
+
var calleeRoot = (node) => {
|
|
93
|
+
if (node.type === TSESTree.AST_NODE_TYPES.Identifier) return node;
|
|
94
|
+
if (node.type !== TSESTree.AST_NODE_TYPES.MemberExpression || node.object.type === TSESTree.AST_NODE_TYPES.Super) {
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
return calleeRoot(node.object);
|
|
98
|
+
};
|
|
99
|
+
var angularName = (node, imports) => {
|
|
100
|
+
if (node.type === TSESTree.AST_NODE_TYPES.Identifier) {
|
|
101
|
+
return imports.get(node.name);
|
|
102
|
+
}
|
|
103
|
+
if (node.type !== TSESTree.AST_NODE_TYPES.MemberExpression || node.computed || node.property.type !== TSESTree.AST_NODE_TYPES.Identifier) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
if (node.object.type === TSESTree.AST_NODE_TYPES.Identifier) {
|
|
107
|
+
const namespaceAlias = imports.get(node.object.name);
|
|
108
|
+
if (namespaceAlias === null) return node.property.name;
|
|
109
|
+
}
|
|
110
|
+
return angularName(node.object, imports);
|
|
111
|
+
};
|
|
112
|
+
var isImportBinding = (node, sourceCode) => {
|
|
113
|
+
const root = calleeRoot(node);
|
|
114
|
+
if (!root) return false;
|
|
115
|
+
for (let scope = sourceCode.getScope(root); scope; scope = scope.upper) {
|
|
116
|
+
const variable = scope.set.get(root.name);
|
|
117
|
+
if (variable) return variable.defs[0]?.type === "ImportBinding";
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
};
|
|
121
|
+
var addAngularImport = (node, imports) => {
|
|
122
|
+
if (node.source.value !== "@angular/core" || node.importKind === "type") {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
for (const specifier of node.specifiers) {
|
|
126
|
+
if (specifier.type === TSESTree.AST_NODE_TYPES.ImportNamespaceSpecifier) {
|
|
127
|
+
imports.set(specifier.local.name, null);
|
|
128
|
+
} else if (specifier.type === TSESTree.AST_NODE_TYPES.ImportSpecifier && specifier.importKind !== "type") {
|
|
129
|
+
const { imported } = specifier;
|
|
130
|
+
const isIdentifier8 = imported.type === TSESTree.AST_NODE_TYPES.Identifier;
|
|
131
|
+
const importedName = isIdentifier8 ? imported.name : imported.value;
|
|
132
|
+
imports.set(specifier.local.name, importedName);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
var angularClassMetadata = (node, imports) => {
|
|
137
|
+
for (const decorator of node.decorators) {
|
|
138
|
+
if (decorator.expression.type !== TSESTree.AST_NODE_TYPES.CallExpression) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const kind = angularName(decorator.expression.callee, imports);
|
|
142
|
+
const metadata = decorator.expression.arguments.at(0);
|
|
143
|
+
const isComponentOrDirective = kind === "Component" || kind === "Directive";
|
|
144
|
+
if (isComponentOrDirective && metadata?.type === TSESTree.AST_NODE_TYPES.ObjectExpression) {
|
|
145
|
+
const classMetadata = {
|
|
146
|
+
component: kind === "Component",
|
|
147
|
+
metadata
|
|
148
|
+
};
|
|
149
|
+
return classMetadata;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
};
|
|
154
|
+
var angularClassImports = (ast) => {
|
|
155
|
+
const imports = /* @__PURE__ */ new Map();
|
|
156
|
+
for (const node of ast.body) {
|
|
157
|
+
if (node.type === TSESTree.AST_NODE_TYPES.ImportDeclaration) {
|
|
158
|
+
addAngularImport(node, imports);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
for (const imported of imports.values()) {
|
|
162
|
+
const isNamespaceImport = imported === null;
|
|
163
|
+
const isComponent = imported === "Component";
|
|
164
|
+
const isDirective = imported === "Directive";
|
|
165
|
+
const isAngularClassImport = isNamespaceImport || isComponent || isDirective;
|
|
166
|
+
if (isAngularClassImport) return imports;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
};
|
|
82
170
|
|
|
83
|
-
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/
|
|
84
|
-
import {
|
|
171
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-member-candidates.ts
|
|
172
|
+
import { TSESTree as TSESTree3 } from "@typescript-eslint/utils";
|
|
85
173
|
|
|
86
|
-
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-
|
|
174
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-managed-fields.ts
|
|
87
175
|
import { TSESTree as TSESTree2 } from "@typescript-eslint/utils";
|
|
176
|
+
var managedApis = /* @__PURE__ */ new Set([
|
|
177
|
+
"input",
|
|
178
|
+
"model",
|
|
179
|
+
"output",
|
|
180
|
+
"viewChild",
|
|
181
|
+
"viewChildren",
|
|
182
|
+
"contentChild",
|
|
183
|
+
"contentChildren"
|
|
184
|
+
]);
|
|
185
|
+
var hasAutomaticEffectCleanup = (node) => {
|
|
186
|
+
const options = node.arguments.at(1);
|
|
187
|
+
if (options === void 0) return true;
|
|
188
|
+
if (options.type !== TSESTree2.AST_NODE_TYPES.ObjectExpression) return false;
|
|
189
|
+
return options.properties.every((property) => {
|
|
190
|
+
if (property.type !== TSESTree2.AST_NODE_TYPES.Property || property.computed) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
const manualCleanup = property.key.type === TSESTree2.AST_NODE_TYPES.Identifier && property.key.name === "manualCleanup" || property.key.type === TSESTree2.AST_NODE_TYPES.Literal && property.key.value === "manualCleanup";
|
|
194
|
+
return !manualCleanup || property.value.type === TSESTree2.AST_NODE_TYPES.Literal && property.value.value === false;
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
var angularCoreImportedName = (definition) => {
|
|
198
|
+
if (definition?.type !== "ImportBinding") return null;
|
|
199
|
+
if (definition.node.type !== TSESTree2.AST_NODE_TYPES.ImportSpecifier) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
if (definition.parent.type !== TSESTree2.AST_NODE_TYPES.ImportDeclaration) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
if (definition.parent.source.value !== "@angular/core") return null;
|
|
206
|
+
const { imported } = definition.node;
|
|
207
|
+
if (imported.type === TSESTree2.AST_NODE_TYPES.Identifier) {
|
|
208
|
+
return imported.name;
|
|
209
|
+
}
|
|
210
|
+
return imported.value;
|
|
211
|
+
};
|
|
212
|
+
var isAngularComponentRefField = (node, sourceCode) => {
|
|
213
|
+
const type = node.typeAnnotation?.typeAnnotation;
|
|
214
|
+
if (type?.type !== TSESTree2.AST_NODE_TYPES.TSTypeReference || type.typeName.type !== TSESTree2.AST_NODE_TYPES.Identifier) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
const reference = sourceCode.getScope(type.typeName).references.find(({ identifier }) => identifier === type.typeName);
|
|
218
|
+
const definition = reference?.resolved?.defs[0];
|
|
219
|
+
return angularCoreImportedName(definition) === "ComponentRef";
|
|
220
|
+
};
|
|
221
|
+
var isManagedField = (node, imports, allowEffectFields, sourceCode) => {
|
|
222
|
+
if (node.value?.type !== TSESTree2.AST_NODE_TYPES.CallExpression) return false;
|
|
223
|
+
const name = angularName(node.value.callee, imports);
|
|
224
|
+
const isString = typeof name === "string";
|
|
225
|
+
if (!isString) return false;
|
|
226
|
+
const isManagedApi = managedApis.has(name);
|
|
227
|
+
if (isManagedApi) return true;
|
|
228
|
+
if (!allowEffectFields) return false;
|
|
229
|
+
if (name !== "effect") return false;
|
|
230
|
+
const isImported = isImportBinding(node.value.callee, sourceCode);
|
|
231
|
+
if (!isImported) return false;
|
|
232
|
+
return hasAutomaticEffectCleanup(node.value);
|
|
233
|
+
};
|
|
88
234
|
|
|
89
|
-
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-
|
|
90
|
-
|
|
235
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-member-candidates.ts
|
|
236
|
+
var lifecycleHooks = /* @__PURE__ */ new Set([
|
|
237
|
+
"ngOnChanges",
|
|
238
|
+
"ngOnInit",
|
|
239
|
+
"ngDoCheck",
|
|
240
|
+
"ngAfterContentInit",
|
|
241
|
+
"ngAfterContentChecked",
|
|
242
|
+
"ngAfterViewInit",
|
|
243
|
+
"ngAfterViewChecked",
|
|
244
|
+
"ngOnDestroy"
|
|
245
|
+
]);
|
|
246
|
+
var formsInterfaceMethods = {
|
|
247
|
+
AsyncValidator: ["validate", "registerOnValidatorChange"],
|
|
248
|
+
ControlValueAccessor: [
|
|
249
|
+
"writeValue",
|
|
250
|
+
"registerOnChange",
|
|
251
|
+
"registerOnTouched",
|
|
252
|
+
"setDisabledState"
|
|
253
|
+
],
|
|
254
|
+
Validator: ["validate", "registerOnValidatorChange"]
|
|
255
|
+
};
|
|
256
|
+
var isInstanceField = (node) => {
|
|
257
|
+
if (node.type !== TSESTree3.AST_NODE_TYPES.PropertyDefinition) return false;
|
|
258
|
+
return node.key.type === TSESTree3.AST_NODE_TYPES.Identifier;
|
|
259
|
+
};
|
|
260
|
+
var isInstanceMethod = (node) => {
|
|
261
|
+
if (node.type !== TSESTree3.AST_NODE_TYPES.MethodDefinition) return false;
|
|
262
|
+
return node.key.type === TSESTree3.AST_NODE_TYPES.Identifier;
|
|
263
|
+
};
|
|
264
|
+
var isExcludedField = (node, localPrivateOnly, sourceCode) => {
|
|
265
|
+
const isModified = node.static || node.declare || node.override;
|
|
266
|
+
const isDecorated = node.decorators.length > 0;
|
|
267
|
+
if (isModified || isDecorated) return true;
|
|
268
|
+
const isComponentRef = isAngularComponentRefField(node, sourceCode);
|
|
269
|
+
if (isComponentRef) return true;
|
|
270
|
+
const isNonPrivate = node.accessibility !== "private";
|
|
271
|
+
return localPrivateOnly && isNonPrivate;
|
|
272
|
+
};
|
|
273
|
+
var isExcludedMethod = (node, localPrivateOnly, implementedMethods) => {
|
|
274
|
+
const isModified = node.static || node.override || node.computed;
|
|
275
|
+
const isDecorated = node.decorators.length > 0;
|
|
276
|
+
const isPlainMethod = node.kind === "method";
|
|
277
|
+
if (isModified || isDecorated || !isPlainMethod) return true;
|
|
278
|
+
const hasNoBody = node.value.body === null;
|
|
279
|
+
const isLifecycleHook = lifecycleHooks.has(node.key.name);
|
|
280
|
+
if (hasNoBody || isLifecycleHook) return true;
|
|
281
|
+
const isImplemented = implementedMethods.has(node.key.name);
|
|
282
|
+
if (isImplemented) return true;
|
|
283
|
+
const isNonPrivate = node.accessibility !== "private";
|
|
284
|
+
return localPrivateOnly && isNonPrivate;
|
|
285
|
+
};
|
|
286
|
+
var implementedFormsMethods = (node) => {
|
|
287
|
+
const methods = /* @__PURE__ */ new Set();
|
|
288
|
+
for (const heritage of node.implements) {
|
|
289
|
+
if (heritage.expression.type !== TSESTree3.AST_NODE_TYPES.Identifier) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
for (const name of formsInterfaceMethods[heritage.expression.name] ?? []) {
|
|
293
|
+
methods.add(name);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return methods;
|
|
297
|
+
};
|
|
298
|
+
var fieldCandidate = (node, options) => {
|
|
299
|
+
const isField = isInstanceField(node);
|
|
300
|
+
if (!isField) return null;
|
|
301
|
+
const { localPrivateOnly, sourceCode } = options;
|
|
302
|
+
const isExcluded = isExcludedField(node, localPrivateOnly, sourceCode);
|
|
303
|
+
if (isExcluded) return null;
|
|
304
|
+
const isManaged = isManagedField(
|
|
305
|
+
node,
|
|
306
|
+
options.imports,
|
|
307
|
+
options.allowEffectFields,
|
|
308
|
+
sourceCode
|
|
309
|
+
);
|
|
310
|
+
if (isManaged) return null;
|
|
311
|
+
const unusedFieldCandidate = {
|
|
312
|
+
messageId: "unusedField",
|
|
313
|
+
name: node.key.name,
|
|
314
|
+
node
|
|
315
|
+
};
|
|
316
|
+
return unusedFieldCandidate;
|
|
317
|
+
};
|
|
318
|
+
var methodCandidate = (node, localPrivateOnly, implementedMethods) => {
|
|
319
|
+
const isMethod = isInstanceMethod(node);
|
|
320
|
+
if (!isMethod) return null;
|
|
321
|
+
const isExcluded = isExcludedMethod(
|
|
322
|
+
node,
|
|
323
|
+
localPrivateOnly,
|
|
324
|
+
implementedMethods
|
|
325
|
+
);
|
|
326
|
+
if (isExcluded) return null;
|
|
327
|
+
const unusedMethodCandidate = {
|
|
328
|
+
messageId: "unusedMethod",
|
|
329
|
+
name: node.key.name,
|
|
330
|
+
node
|
|
331
|
+
};
|
|
332
|
+
return unusedMethodCandidate;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.ts
|
|
91
336
|
import { dirname, resolve } from "node:path";
|
|
337
|
+
import { TSESTree as TSESTree5 } from "@typescript-eslint/utils";
|
|
338
|
+
|
|
339
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-literals.ts
|
|
340
|
+
import { TSESTree as TSESTree4 } from "@typescript-eslint/utils";
|
|
341
|
+
var text = (node) => {
|
|
342
|
+
if (node?.type === TSESTree4.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
343
|
+
return node.value;
|
|
344
|
+
}
|
|
345
|
+
if (node?.type !== TSESTree4.AST_NODE_TYPES.TemplateLiteral) return null;
|
|
346
|
+
const hasExpressions = node.expressions.length > 0;
|
|
347
|
+
if (hasExpressions) return null;
|
|
348
|
+
return node.quasis[0].value.cooked;
|
|
349
|
+
};
|
|
350
|
+
var key = (property) => {
|
|
351
|
+
if (property.type !== TSESTree4.AST_NODE_TYPES.Property || property.computed) {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
if (property.key.type === TSESTree4.AST_NODE_TYPES.Identifier) {
|
|
355
|
+
return property.key.name;
|
|
356
|
+
}
|
|
357
|
+
return text(property.key);
|
|
358
|
+
};
|
|
359
|
+
var metadataValue = (metadata, name) => {
|
|
360
|
+
for (const property of metadata.properties) {
|
|
361
|
+
if (property.type !== TSESTree4.AST_NODE_TYPES.Property) continue;
|
|
362
|
+
const propertyKey = key(property);
|
|
363
|
+
if (propertyKey === name) return property.value;
|
|
364
|
+
}
|
|
365
|
+
return void 0;
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-template-parsing.ts
|
|
92
369
|
import {
|
|
93
370
|
Lexer,
|
|
94
371
|
ParseLocation,
|
|
@@ -98,44 +375,60 @@ import {
|
|
|
98
375
|
R3TargetBinder,
|
|
99
376
|
parseTemplate
|
|
100
377
|
} from "@angular/compiler";
|
|
101
|
-
import {
|
|
378
|
+
import { readFileSync, statSync } from "node:fs";
|
|
102
379
|
|
|
103
|
-
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/
|
|
380
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-expression-reads.ts
|
|
104
381
|
import {
|
|
105
382
|
CombinedRecursiveAstVisitor,
|
|
106
383
|
ImplicitReceiver,
|
|
384
|
+
KeyedRead as KeyedRead2,
|
|
385
|
+
SafeKeyedRead as SafeKeyedRead2,
|
|
386
|
+
ThisReceiver
|
|
387
|
+
} from "@angular/compiler";
|
|
388
|
+
|
|
389
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/utils/angular-read-target.util.ts
|
|
390
|
+
import {
|
|
107
391
|
KeyedRead,
|
|
108
392
|
PropertyRead,
|
|
109
393
|
SafeKeyedRead,
|
|
110
|
-
SafePropertyRead
|
|
111
|
-
ThisReceiver
|
|
394
|
+
SafePropertyRead
|
|
112
395
|
} from "@angular/compiler";
|
|
396
|
+
var isReadTarget = (node) => {
|
|
397
|
+
const isPropertyRead = node instanceof PropertyRead;
|
|
398
|
+
const isSafePropertyRead = node instanceof SafePropertyRead;
|
|
399
|
+
const isKeyedRead = node instanceof KeyedRead;
|
|
400
|
+
const isSafeKeyedRead = node instanceof SafeKeyedRead;
|
|
401
|
+
return isPropertyRead || isSafePropertyRead || isKeyedRead || isSafeKeyedRead;
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-expression-reads.ts
|
|
113
405
|
var ReadCollector = class extends CombinedRecursiveAstVisitor {
|
|
406
|
+
reads = /* @__PURE__ */ new Set();
|
|
407
|
+
boundTarget;
|
|
408
|
+
action;
|
|
114
409
|
constructor(boundTarget, action) {
|
|
115
410
|
super();
|
|
116
411
|
this.boundTarget = boundTarget;
|
|
117
412
|
this.action = action;
|
|
118
413
|
}
|
|
119
|
-
boundTarget;
|
|
120
|
-
action;
|
|
121
|
-
reads = /* @__PURE__ */ new Set();
|
|
122
414
|
record(node) {
|
|
123
415
|
if (node.receiver instanceof ThisReceiver) {
|
|
124
416
|
this.reads.add(node.name);
|
|
125
417
|
return;
|
|
126
418
|
}
|
|
127
|
-
if (node.receiver instanceof ImplicitReceiver
|
|
128
|
-
|
|
129
|
-
|
|
419
|
+
if (!(node.receiver instanceof ImplicitReceiver)) return;
|
|
420
|
+
const isBindableName = !this.action || node.name !== "$event";
|
|
421
|
+
if (!isBindableName) return;
|
|
422
|
+
const expressionTarget = this.boundTarget?.getExpressionTarget(node);
|
|
423
|
+
if (expressionTarget) return;
|
|
424
|
+
this.reads.add(node.name);
|
|
130
425
|
}
|
|
131
426
|
visitBinary(node, context) {
|
|
132
|
-
if (node.operation !== "=")
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
if (node.left instanceof PropertyRead || node.left instanceof SafePropertyRead || node.left instanceof KeyedRead || node.left instanceof SafeKeyedRead) {
|
|
427
|
+
if (node.operation !== "=") return super.visitBinary(node, context);
|
|
428
|
+
if (isReadTarget(node.left)) {
|
|
136
429
|
this.visit(node.left.receiver);
|
|
137
430
|
}
|
|
138
|
-
if (node.left instanceof
|
|
431
|
+
if (node.left instanceof KeyedRead2 || node.left instanceof SafeKeyedRead2) {
|
|
139
432
|
this.visit(node.left.key);
|
|
140
433
|
}
|
|
141
434
|
this.visit(node.right);
|
|
@@ -158,22 +451,14 @@ var collectAngularExpressionReads = (nodes, boundTarget, action) => {
|
|
|
158
451
|
return collector.reads;
|
|
159
452
|
};
|
|
160
453
|
|
|
161
|
-
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-
|
|
454
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-template-parsing.ts
|
|
162
455
|
var externalTemplates = /* @__PURE__ */ new Map();
|
|
163
456
|
var parser = new Parser(new Lexer());
|
|
164
457
|
var templateBinder = new R3TargetBinder(null);
|
|
165
|
-
var text = (node) => {
|
|
166
|
-
if (node?.type === TSESTree.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
167
|
-
return node.value;
|
|
168
|
-
}
|
|
169
|
-
return node?.type === TSESTree.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0 ? node.quasis[0].value.cooked : null;
|
|
170
|
-
};
|
|
171
458
|
var templateReads = (template, filename) => {
|
|
172
459
|
try {
|
|
173
460
|
const result = parseTemplate(template, filename);
|
|
174
|
-
if (result.errors?.length)
|
|
175
|
-
return null;
|
|
176
|
-
}
|
|
461
|
+
if (result.errors?.length) return null;
|
|
177
462
|
return collectAngularExpressionReads(
|
|
178
463
|
result.nodes,
|
|
179
464
|
templateBinder.bind({ template: result.nodes }),
|
|
@@ -188,9 +473,7 @@ var externalTemplateReads = (filename) => {
|
|
|
188
473
|
const stats = statSync(filename, { bigint: true });
|
|
189
474
|
const version = `${stats.mtimeNs}:${stats.size}`;
|
|
190
475
|
const cached = externalTemplates.get(filename);
|
|
191
|
-
if (cached?.version === version)
|
|
192
|
-
return cached.reads;
|
|
193
|
-
}
|
|
476
|
+
if (cached?.version === version) return cached.reads;
|
|
194
477
|
const reads = templateReads(readFileSync(filename, "utf8"), filename);
|
|
195
478
|
externalTemplates.set(filename, { reads, version });
|
|
196
479
|
return reads;
|
|
@@ -198,82 +481,66 @@ var externalTemplateReads = (filename) => {
|
|
|
198
481
|
return null;
|
|
199
482
|
}
|
|
200
483
|
};
|
|
484
|
+
var parseExpression = (expression, span, action) => {
|
|
485
|
+
if (action) return parser.parseAction(expression, span, 0);
|
|
486
|
+
return parser.parseBinding(expression, span, 0);
|
|
487
|
+
};
|
|
201
488
|
var expressionReads = (expression, filename, action) => {
|
|
202
489
|
try {
|
|
203
490
|
const file = new ParseSourceFile(expression, filename);
|
|
204
491
|
const start = new ParseLocation(file, 0, 0, 0);
|
|
205
492
|
const span = new ParseSourceSpan(start, start.moveBy(expression.length));
|
|
206
|
-
const result =
|
|
207
|
-
if (result.errors.length > 0)
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
493
|
+
const result = parseExpression(expression, span, action);
|
|
494
|
+
if (result.errors.length > 0) return null;
|
|
210
495
|
return collectAngularExpressionReads([result], void 0, action);
|
|
211
496
|
} catch {
|
|
212
497
|
return null;
|
|
213
498
|
}
|
|
214
499
|
};
|
|
500
|
+
|
|
501
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-metadata-reads.ts
|
|
215
502
|
var addReads = (target, source) => {
|
|
216
|
-
if (!source)
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
503
|
+
if (!source) return false;
|
|
219
504
|
for (const name of source) {
|
|
220
505
|
target.add(name);
|
|
221
506
|
}
|
|
222
507
|
return true;
|
|
223
508
|
};
|
|
224
|
-
var key = (property) => {
|
|
225
|
-
if (property.type !== TSESTree.AST_NODE_TYPES.Property || property.computed) {
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
228
|
-
return property.key.type === TSESTree.AST_NODE_TYPES.Identifier ? property.key.name : text(property.key);
|
|
229
|
-
};
|
|
230
|
-
var metadataValue = (metadata, name) => {
|
|
231
|
-
for (const property of metadata.properties) {
|
|
232
|
-
if (property.type === TSESTree.AST_NODE_TYPES.Property && key(property) === name) {
|
|
233
|
-
return property.value;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
return void 0;
|
|
237
|
-
};
|
|
238
509
|
var hostPropertyReads = (property, reads, filename) => {
|
|
239
510
|
const name = key(property);
|
|
240
511
|
const expression = text(property.value);
|
|
241
|
-
if (name === null || expression === null)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (name.startsWith("[")) {
|
|
512
|
+
if (name === null || expression === null) return false;
|
|
513
|
+
const isPropertyBinding = name.startsWith("[");
|
|
514
|
+
if (isPropertyBinding) {
|
|
245
515
|
return addReads(reads, expressionReads(expression, filename, false));
|
|
246
516
|
}
|
|
247
|
-
|
|
517
|
+
const isEventBinding = name.startsWith("(");
|
|
518
|
+
if (isEventBinding) {
|
|
248
519
|
return addReads(reads, expressionReads(expression, filename, true));
|
|
249
520
|
}
|
|
250
521
|
return true;
|
|
251
522
|
};
|
|
252
523
|
var hostReads = (host, reads, filename) => {
|
|
253
|
-
if (!host)
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
524
|
+
if (!host) return true;
|
|
525
|
+
if (host.type !== TSESTree5.AST_NODE_TYPES.ObjectExpression) return false;
|
|
526
|
+
const hasUnreadableKey = host.properties.some(
|
|
527
|
+
(property) => key(property) === null
|
|
528
|
+
);
|
|
529
|
+
if (hasUnreadableKey) return false;
|
|
259
530
|
for (const property of host.properties) {
|
|
260
|
-
if (property.type !==
|
|
261
|
-
|
|
262
|
-
|
|
531
|
+
if (property.type !== TSESTree5.AST_NODE_TYPES.Property) return false;
|
|
532
|
+
const hasPropertyReads = hostPropertyReads(property, reads, filename);
|
|
533
|
+
if (!hasPropertyReads) return false;
|
|
263
534
|
}
|
|
264
535
|
return true;
|
|
265
536
|
};
|
|
266
537
|
var inlineTemplateReads = (template, reads, filename) => {
|
|
267
|
-
if (!template)
|
|
268
|
-
return true;
|
|
269
|
-
}
|
|
538
|
+
if (!template) return true;
|
|
270
539
|
const source = text(template);
|
|
271
540
|
return source !== null && addReads(reads, templateReads(source, filename));
|
|
272
541
|
};
|
|
273
542
|
var externalTemplatePropertyReads = (templateUrl, reads, filename) => {
|
|
274
|
-
if (!templateUrl)
|
|
275
|
-
return true;
|
|
276
|
-
}
|
|
543
|
+
if (!templateUrl) return true;
|
|
277
544
|
const url = text(templateUrl);
|
|
278
545
|
return url !== null && addReads(reads, externalTemplateReads(resolve(dirname(filename), url)));
|
|
279
546
|
};
|
|
@@ -282,156 +549,113 @@ var componentTemplateReads = (metadata, reads, filename) => {
|
|
|
282
549
|
const templateUrl = metadataValue(metadata, "templateUrl");
|
|
283
550
|
return inlineTemplateReads(template, reads, filename) && externalTemplatePropertyReads(templateUrl, reads, filename);
|
|
284
551
|
};
|
|
285
|
-
var metadataReads = (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
552
|
+
var metadataReads = (options) => {
|
|
553
|
+
const { filename, metadata } = options;
|
|
554
|
+
const isUnreadableKey = (property) => {
|
|
555
|
+
return key(property) === null;
|
|
556
|
+
};
|
|
557
|
+
const hasUnreadableKey = metadata.properties.some(isUnreadableKey);
|
|
558
|
+
if (hasUnreadableKey) return null;
|
|
289
559
|
const reads = /* @__PURE__ */ new Set();
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
if (component
|
|
294
|
-
|
|
295
|
-
|
|
560
|
+
const hostValue = metadataValue(metadata, "host");
|
|
561
|
+
const hasHostReads = hostReads(hostValue, reads, filename);
|
|
562
|
+
if (!hasHostReads) return null;
|
|
563
|
+
if (!options.component) return reads;
|
|
564
|
+
const isRead = (name) => reads.has(name);
|
|
565
|
+
const hasRemainingReads = options.remainingNames.every(isRead);
|
|
566
|
+
if (hasRemainingReads) return reads;
|
|
567
|
+
const hasTemplateReads = componentTemplateReads(metadata, reads, filename);
|
|
568
|
+
if (hasTemplateReads) return reads;
|
|
569
|
+
if (!options.requireTemplate) return reads;
|
|
296
570
|
return null;
|
|
297
571
|
};
|
|
298
572
|
|
|
299
|
-
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/angular-class-fields.ts
|
|
300
|
-
var
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
return imports.get(node.name);
|
|
308
|
-
}
|
|
309
|
-
if (node.type !== TSESTree2.AST_NODE_TYPES.MemberExpression || node.computed || node.property.type !== TSESTree2.AST_NODE_TYPES.Identifier) {
|
|
310
|
-
return null;
|
|
311
|
-
}
|
|
312
|
-
if (node.object.type === TSESTree2.AST_NODE_TYPES.Identifier && imports.get(node.object.name) === null) {
|
|
313
|
-
return node.property.name;
|
|
314
|
-
}
|
|
315
|
-
return angularName(node.object, imports);
|
|
316
|
-
};
|
|
317
|
-
var angularClass = (node, imports) => {
|
|
318
|
-
for (const decorator of node.decorators) {
|
|
319
|
-
if (decorator.expression.type !== TSESTree2.AST_NODE_TYPES.CallExpression) {
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
const kind = angularName(decorator.expression.callee, imports);
|
|
323
|
-
const metadata = decorator.expression.arguments[0];
|
|
324
|
-
if ((kind === "Component" || kind === "Directive") && metadata?.type === TSESTree2.AST_NODE_TYPES.ObjectExpression) {
|
|
325
|
-
return {
|
|
326
|
-
component: kind === "Component",
|
|
327
|
-
metadata
|
|
328
|
-
};
|
|
573
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/angular/angular-class-fields.ts
|
|
574
|
+
var memberCandidates = (node, options) => {
|
|
575
|
+
const implementedMethods = implementedFormsMethods(node);
|
|
576
|
+
const members = [];
|
|
577
|
+
for (const element of node.body.body) {
|
|
578
|
+
const candidate = fieldCandidate(element, options) ?? methodCandidate(element, options.localPrivateOnly, implementedMethods);
|
|
579
|
+
if (candidate) {
|
|
580
|
+
members.push(candidate);
|
|
329
581
|
}
|
|
330
582
|
}
|
|
331
|
-
return
|
|
332
|
-
};
|
|
333
|
-
var isInstanceField = (node) => node.type === TSESTree2.AST_NODE_TYPES.PropertyDefinition && node.key.type === TSESTree2.AST_NODE_TYPES.Identifier;
|
|
334
|
-
var lifecycleHooks = {
|
|
335
|
-
ngOnChanges: true,
|
|
336
|
-
ngOnInit: true,
|
|
337
|
-
ngDoCheck: true,
|
|
338
|
-
ngAfterContentInit: true,
|
|
339
|
-
ngAfterContentChecked: true,
|
|
340
|
-
ngAfterViewInit: true,
|
|
341
|
-
ngAfterViewChecked: true,
|
|
342
|
-
ngOnDestroy: true
|
|
343
|
-
};
|
|
344
|
-
var isInstanceMethod = (node) => node.type === TSESTree2.AST_NODE_TYPES.MethodDefinition && node.key.type === TSESTree2.AST_NODE_TYPES.Identifier;
|
|
345
|
-
var isExcludedField = (node, component) => node.static || node.declare || node.override || node.decorators.length > 0 || !component && node.accessibility !== "private";
|
|
346
|
-
var isManagedField = (node, imports) => {
|
|
347
|
-
if (node.value?.type !== TSESTree2.AST_NODE_TYPES.CallExpression) {
|
|
348
|
-
return false;
|
|
349
|
-
}
|
|
350
|
-
const name = angularName(node.value.callee, imports);
|
|
351
|
-
return typeof name === "string" && managedApis[name] === true;
|
|
583
|
+
return members;
|
|
352
584
|
};
|
|
353
|
-
var
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
585
|
+
var reportMember = (context, candidate) => {
|
|
586
|
+
const data = { name: candidate.name };
|
|
587
|
+
const report = {
|
|
588
|
+
data,
|
|
589
|
+
messageId: candidate.messageId,
|
|
590
|
+
node: candidate.node.key
|
|
591
|
+
};
|
|
592
|
+
context.report(report);
|
|
358
593
|
};
|
|
359
|
-
var
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
594
|
+
var reportUnreadMembers = (members, reads, options) => {
|
|
595
|
+
for (const candidate of members) {
|
|
596
|
+
const isTemplateRead = reads.has(candidate.name);
|
|
597
|
+
if (isTemplateRead) continue;
|
|
598
|
+
const isProjectUsed = options.projectMemberUsed?.(candidate.node) === true;
|
|
599
|
+
if (isProjectUsed) continue;
|
|
600
|
+
reportMember(options.context, candidate);
|
|
363
601
|
}
|
|
364
|
-
return null;
|
|
365
602
|
};
|
|
366
|
-
var
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
603
|
+
var reportClassMembers = (entry, ngClass, report) => {
|
|
604
|
+
const { options, projectAnalysis } = report;
|
|
605
|
+
const isLocalOnlyClass = !ngClass.component || entry.node.abstract === true;
|
|
606
|
+
const candidateOptions = {
|
|
607
|
+
allowEffectFields: options.allowEffectFields,
|
|
608
|
+
imports: options.imports,
|
|
609
|
+
localPrivateOnly: !projectAnalysis && isLocalOnlyClass,
|
|
610
|
+
sourceCode: options.context.sourceCode
|
|
611
|
+
};
|
|
612
|
+
const members = memberCandidates(entry.node, candidateOptions);
|
|
613
|
+
const unreadMembers = members.filter(
|
|
614
|
+
(candidate) => !entry.reads.has(candidate.name)
|
|
615
|
+
);
|
|
616
|
+
if (unreadMembers.length === 0) return;
|
|
617
|
+
const remainingNames = unreadMembers.map((candidate) => candidate.name);
|
|
618
|
+
const readsOptions = {
|
|
619
|
+
component: ngClass.component && report.localTemplates,
|
|
620
|
+
filename: options.context.filename,
|
|
621
|
+
metadata: ngClass.metadata,
|
|
622
|
+
remainingNames,
|
|
623
|
+
requireTemplate: !projectAnalysis
|
|
624
|
+
};
|
|
625
|
+
const reads = metadataReads(readsOptions);
|
|
626
|
+
if (!reads) return;
|
|
627
|
+
reportUnreadMembers(unreadMembers, reads, options);
|
|
378
628
|
};
|
|
379
|
-
var reportUnusedMembers = (
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (members.length === 0) {
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
const unreadMembers = members.filter(
|
|
396
|
-
(candidate) => !entry.reads.has(candidate.name)
|
|
397
|
-
);
|
|
398
|
-
if (unreadMembers.length === 0) {
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
const reads = metadataReads(
|
|
402
|
-
ngClass.metadata,
|
|
403
|
-
ngClass.component,
|
|
404
|
-
context.filename,
|
|
405
|
-
unreadMembers.map((candidate) => candidate.name)
|
|
406
|
-
);
|
|
407
|
-
if (!reads) {
|
|
408
|
-
continue;
|
|
409
|
-
}
|
|
410
|
-
for (const candidate of unreadMembers) {
|
|
411
|
-
if (!reads.has(candidate.name)) {
|
|
412
|
-
context.report({
|
|
413
|
-
data: { name: candidate.name },
|
|
414
|
-
messageId: candidate.messageId,
|
|
415
|
-
node: candidate.node.key
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
}
|
|
629
|
+
var reportUnusedMembers = (options) => {
|
|
630
|
+
const projectAnalysis = options.projectMemberUsed !== void 0;
|
|
631
|
+
const localTemplates = !projectAnalysis || !options.projectIndexed();
|
|
632
|
+
const report = {
|
|
633
|
+
localTemplates,
|
|
634
|
+
options,
|
|
635
|
+
projectAnalysis
|
|
636
|
+
};
|
|
637
|
+
for (const entry of options.classes) {
|
|
638
|
+
const ngClass = angularClassMetadata(entry.node, options.imports);
|
|
639
|
+
const isDynamicClass = options.dynamicClasses.has(entry);
|
|
640
|
+
if (!ngClass || isDynamicClass) continue;
|
|
641
|
+
reportClassMembers(entry, ngClass, report);
|
|
419
642
|
}
|
|
420
643
|
};
|
|
421
644
|
|
|
422
|
-
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/
|
|
423
|
-
import { TSESTree as
|
|
645
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-class-read-visitor.ts
|
|
646
|
+
import { TSESTree as TSESTree9 } from "@typescript-eslint/utils";
|
|
647
|
+
|
|
648
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-field-reads.ts
|
|
649
|
+
import { TSESTree as TSESTree6 } from "@typescript-eslint/utils";
|
|
650
|
+
var expressionWrapperTypes = /* @__PURE__ */ new Set([
|
|
651
|
+
TSESTree6.AST_NODE_TYPES.TSAsExpression,
|
|
652
|
+
TSESTree6.AST_NODE_TYPES.TSInstantiationExpression,
|
|
653
|
+
TSESTree6.AST_NODE_TYPES.TSNonNullExpression,
|
|
654
|
+
TSESTree6.AST_NODE_TYPES.TSSatisfiesExpression,
|
|
655
|
+
TSESTree6.AST_NODE_TYPES.TSTypeAssertion
|
|
656
|
+
]);
|
|
424
657
|
var isTypeScriptExpressionWrapper = (node) => {
|
|
425
|
-
|
|
426
|
-
case TSESTree3.AST_NODE_TYPES.TSAsExpression:
|
|
427
|
-
case TSESTree3.AST_NODE_TYPES.TSInstantiationExpression:
|
|
428
|
-
case TSESTree3.AST_NODE_TYPES.TSNonNullExpression:
|
|
429
|
-
case TSESTree3.AST_NODE_TYPES.TSSatisfiesExpression:
|
|
430
|
-
case TSESTree3.AST_NODE_TYPES.TSTypeAssertion:
|
|
431
|
-
return true;
|
|
432
|
-
default:
|
|
433
|
-
return false;
|
|
434
|
-
}
|
|
658
|
+
return expressionWrapperTypes.has(node.type);
|
|
435
659
|
};
|
|
436
660
|
var unwrapTypeScriptExpression = (node) => {
|
|
437
661
|
let current = node;
|
|
@@ -440,123 +664,215 @@ var unwrapTypeScriptExpression = (node) => {
|
|
|
440
664
|
}
|
|
441
665
|
return current;
|
|
442
666
|
};
|
|
443
|
-
var isThisExpression = (node) =>
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
case TSESTree3.AST_NODE_TYPES.ArrayPattern:
|
|
447
|
-
return parent.elements.some((element) => element === child);
|
|
448
|
-
case TSESTree3.AST_NODE_TYPES.AssignmentPattern:
|
|
449
|
-
return parent.left === child;
|
|
450
|
-
case TSESTree3.AST_NODE_TYPES.ObjectPattern:
|
|
451
|
-
return parent.properties.some((property) => property === child);
|
|
452
|
-
case TSESTree3.AST_NODE_TYPES.Property:
|
|
453
|
-
return parent.value === child && parent.parent.type === TSESTree3.AST_NODE_TYPES.ObjectPattern;
|
|
454
|
-
case TSESTree3.AST_NODE_TYPES.RestElement:
|
|
455
|
-
return parent.argument === child;
|
|
456
|
-
default:
|
|
457
|
-
return false;
|
|
458
|
-
}
|
|
459
|
-
};
|
|
460
|
-
var isSimpleAssignmentTarget = (parent, current) => parent?.type === TSESTree3.AST_NODE_TYPES.AssignmentExpression && parent.left === current && parent.operator === "=";
|
|
461
|
-
var isLoopTarget = (parent, current) => (parent?.type === TSESTree3.AST_NODE_TYPES.ForInStatement || parent?.type === TSESTree3.AST_NODE_TYPES.ForOfStatement) && parent.left === current;
|
|
462
|
-
var isDirectWrite = (node) => {
|
|
463
|
-
let current = node;
|
|
464
|
-
let parent = node.parent;
|
|
465
|
-
while (parent && isTypeScriptExpressionWrapper(parent) && parent.expression === current) {
|
|
466
|
-
current = parent;
|
|
467
|
-
parent = parent.parent;
|
|
468
|
-
}
|
|
469
|
-
return isSimpleAssignmentTarget(parent, current) || parent?.type === TSESTree3.AST_NODE_TYPES.UnaryExpression && parent.operator === "delete";
|
|
470
|
-
};
|
|
471
|
-
var isDestructuringOrLoopTarget = (node) => {
|
|
472
|
-
let current = node;
|
|
473
|
-
let parent = node.parent;
|
|
474
|
-
let patternTarget = false;
|
|
475
|
-
while (parent) {
|
|
476
|
-
const wrapper = isTypeScriptExpressionWrapper(parent) && parent.expression === current;
|
|
477
|
-
const pattern = isPatternTarget(parent, current);
|
|
478
|
-
if (!wrapper && !pattern) {
|
|
479
|
-
break;
|
|
480
|
-
}
|
|
481
|
-
if (pattern) {
|
|
482
|
-
patternTarget = true;
|
|
483
|
-
}
|
|
484
|
-
current = parent;
|
|
485
|
-
parent = parent.parent;
|
|
486
|
-
}
|
|
487
|
-
return patternTarget && isSimpleAssignmentTarget(parent, current) || isLoopTarget(parent, current);
|
|
667
|
+
var isThisExpression = (node) => {
|
|
668
|
+
const unwrapped = unwrapTypeScriptExpression(node);
|
|
669
|
+
return unwrapped?.type === TSESTree6.AST_NODE_TYPES.ThisExpression;
|
|
488
670
|
};
|
|
489
|
-
var isWriteOnly = (node) => isDirectWrite(node) || isDestructuringOrLoopTarget(node);
|
|
490
671
|
var destructuringParts = (node) => {
|
|
491
|
-
if (node.type ===
|
|
492
|
-
|
|
672
|
+
if (node.type === TSESTree6.AST_NODE_TYPES.VariableDeclarator) {
|
|
673
|
+
const variableParts = {
|
|
674
|
+
pattern: node.id,
|
|
675
|
+
source: node.init
|
|
676
|
+
};
|
|
677
|
+
return variableParts;
|
|
493
678
|
}
|
|
494
679
|
if (node.operator === "=") {
|
|
495
|
-
|
|
680
|
+
const assignmentParts = {
|
|
681
|
+
pattern: node.left,
|
|
682
|
+
source: node.right
|
|
683
|
+
};
|
|
684
|
+
return assignmentParts;
|
|
496
685
|
}
|
|
497
686
|
return null;
|
|
498
687
|
};
|
|
499
688
|
var propertyName = (property) => {
|
|
500
|
-
if (property.type !==
|
|
689
|
+
if (property.type !== TSESTree6.AST_NODE_TYPES.Property || property.computed) {
|
|
501
690
|
return null;
|
|
502
691
|
}
|
|
503
|
-
if (property.key.type ===
|
|
692
|
+
if (property.key.type === TSESTree6.AST_NODE_TYPES.Identifier) {
|
|
504
693
|
return property.key.name;
|
|
505
694
|
}
|
|
506
|
-
|
|
695
|
+
const { value } = property.key;
|
|
696
|
+
if (typeof value !== "string") return null;
|
|
697
|
+
return value;
|
|
507
698
|
};
|
|
508
699
|
var destructuredThisReads = (node) => {
|
|
509
700
|
const parts = destructuringParts(node);
|
|
510
|
-
if (!parts
|
|
701
|
+
if (!parts) return void 0;
|
|
702
|
+
if (parts.pattern.type !== TSESTree6.AST_NODE_TYPES.ObjectPattern) {
|
|
511
703
|
return void 0;
|
|
512
704
|
}
|
|
705
|
+
const isThisSource = isThisExpression(parts.source);
|
|
706
|
+
if (!isThisSource) return void 0;
|
|
513
707
|
const reads = [];
|
|
514
708
|
for (const property of parts.pattern.properties) {
|
|
515
709
|
const name = propertyName(property);
|
|
516
|
-
if (name === null)
|
|
517
|
-
return null;
|
|
518
|
-
}
|
|
710
|
+
if (name === null) return null;
|
|
519
711
|
reads.push(name);
|
|
520
712
|
}
|
|
521
713
|
return reads;
|
|
522
714
|
};
|
|
523
715
|
|
|
524
|
-
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
)
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
classes.push(entry);
|
|
534
|
-
stack.push(entry);
|
|
535
|
-
thisStack.push(true);
|
|
716
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-lexical-this.ts
|
|
717
|
+
import { TSESTree as TSESTree7 } from "@typescript-eslint/utils";
|
|
718
|
+
var componentThis = (node, thisStack) => {
|
|
719
|
+
if (node.type === TSESTree7.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
720
|
+
return thisStack.at(-1) ?? false;
|
|
721
|
+
}
|
|
722
|
+
const { parent } = node;
|
|
723
|
+
if (parent.type !== TSESTree7.AST_NODE_TYPES.MethodDefinition) return false;
|
|
724
|
+
return !parent.static;
|
|
536
725
|
};
|
|
537
|
-
var
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
726
|
+
var lexicalThisVisitor = (thisStack) => {
|
|
727
|
+
const listeners = {
|
|
728
|
+
FunctionDeclaration(node) {
|
|
729
|
+
thisStack.push(componentThis(node, thisStack));
|
|
730
|
+
},
|
|
731
|
+
FunctionExpression(node) {
|
|
732
|
+
thisStack.push(componentThis(node, thisStack));
|
|
733
|
+
},
|
|
734
|
+
ArrowFunctionExpression(node) {
|
|
735
|
+
thisStack.push(componentThis(node, thisStack));
|
|
736
|
+
},
|
|
737
|
+
"FunctionDeclaration:exit"() {
|
|
738
|
+
thisStack.pop();
|
|
739
|
+
},
|
|
740
|
+
"FunctionExpression:exit"() {
|
|
741
|
+
thisStack.pop();
|
|
742
|
+
},
|
|
743
|
+
"ArrowFunctionExpression:exit"() {
|
|
744
|
+
thisStack.pop();
|
|
745
|
+
},
|
|
746
|
+
StaticBlock() {
|
|
747
|
+
thisStack.push(false);
|
|
748
|
+
},
|
|
749
|
+
"StaticBlock:exit"() {
|
|
750
|
+
thisStack.pop();
|
|
751
|
+
},
|
|
752
|
+
PropertyDefinition(node) {
|
|
753
|
+
if (node.static) {
|
|
754
|
+
thisStack.push(false);
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
"PropertyDefinition:exit"(node) {
|
|
758
|
+
if (node.static) {
|
|
759
|
+
thisStack.pop();
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
return listeners;
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-write-targets.ts
|
|
767
|
+
import { TSESTree as TSESTree8 } from "@typescript-eslint/utils";
|
|
768
|
+
var parentOf = (node) => {
|
|
769
|
+
if (node.type === TSESTree8.AST_NODE_TYPES.Program) return void 0;
|
|
770
|
+
return node.parent;
|
|
771
|
+
};
|
|
772
|
+
var isPatternTarget = (parent, child) => {
|
|
773
|
+
if (parent === void 0) return false;
|
|
774
|
+
if (parent.type === TSESTree8.AST_NODE_TYPES.ArrayPattern) {
|
|
775
|
+
return parent.elements.some((element) => element === child);
|
|
776
|
+
}
|
|
777
|
+
if (parent.type === TSESTree8.AST_NODE_TYPES.AssignmentPattern) {
|
|
778
|
+
return parent.left === child;
|
|
779
|
+
}
|
|
780
|
+
if (parent.type === TSESTree8.AST_NODE_TYPES.ObjectPattern) {
|
|
781
|
+
return parent.properties.some((property) => property === child);
|
|
782
|
+
}
|
|
783
|
+
if (parent.type === TSESTree8.AST_NODE_TYPES.Property) {
|
|
784
|
+
const isObjectPattern = parent.parent.type === TSESTree8.AST_NODE_TYPES.ObjectPattern;
|
|
785
|
+
return parent.value === child && isObjectPattern;
|
|
786
|
+
}
|
|
787
|
+
if (parent.type === TSESTree8.AST_NODE_TYPES.RestElement) {
|
|
788
|
+
return parent.argument === child;
|
|
789
|
+
}
|
|
790
|
+
return false;
|
|
791
|
+
};
|
|
792
|
+
var isSimpleAssignmentTarget = (parent, current) => {
|
|
793
|
+
if (parent?.type !== TSESTree8.AST_NODE_TYPES.AssignmentExpression) {
|
|
794
|
+
return false;
|
|
795
|
+
}
|
|
796
|
+
return parent.left === current && parent.operator === "=";
|
|
797
|
+
};
|
|
798
|
+
var isLoopTarget = (parent, current) => {
|
|
799
|
+
const isForIn = parent?.type === TSESTree8.AST_NODE_TYPES.ForInStatement;
|
|
800
|
+
const isForOf = parent?.type === TSESTree8.AST_NODE_TYPES.ForOfStatement;
|
|
801
|
+
const isLoop = isForIn || isForOf;
|
|
802
|
+
if (!isLoop) return false;
|
|
803
|
+
return parent.left === current;
|
|
804
|
+
};
|
|
805
|
+
var isWrapping = (parent, child) => {
|
|
806
|
+
const isWrapper = isTypeScriptExpressionWrapper(parent);
|
|
807
|
+
if (!isWrapper) return false;
|
|
808
|
+
return parent.expression === child;
|
|
809
|
+
};
|
|
810
|
+
var isDirectWrite = (node) => {
|
|
811
|
+
let current = node;
|
|
812
|
+
let parent = parentOf(node);
|
|
813
|
+
while (parent && isTypeScriptExpressionWrapper(parent) && parent.expression === current) {
|
|
814
|
+
current = parent;
|
|
815
|
+
parent = parentOf(parent);
|
|
816
|
+
}
|
|
817
|
+
const isDeleteExpression2 = parent?.type === TSESTree8.AST_NODE_TYPES.UnaryExpression && parent.operator === "delete";
|
|
818
|
+
const isAssignment = isSimpleAssignmentTarget(parent, current);
|
|
819
|
+
return isAssignment || isDeleteExpression2;
|
|
820
|
+
};
|
|
821
|
+
var isDestructuringOrLoopTarget = (node) => {
|
|
822
|
+
let current = node;
|
|
823
|
+
let parent = parentOf(node);
|
|
824
|
+
let patternTarget = false;
|
|
825
|
+
while (parent) {
|
|
826
|
+
const wrapper = isWrapping(parent, current);
|
|
827
|
+
const pattern = isPatternTarget(parent, current);
|
|
828
|
+
if (!wrapper && !pattern) break;
|
|
829
|
+
if (pattern) {
|
|
830
|
+
patternTarget = true;
|
|
831
|
+
}
|
|
832
|
+
current = parent;
|
|
833
|
+
parent = parentOf(parent);
|
|
834
|
+
}
|
|
835
|
+
const isSimpleTarget = isSimpleAssignmentTarget(parent, current);
|
|
836
|
+
const isAssignedPattern = patternTarget && isSimpleTarget;
|
|
837
|
+
return isAssignedPattern || isLoopTarget(parent, current);
|
|
838
|
+
};
|
|
839
|
+
var isWriteOnly = (node) => {
|
|
840
|
+
const isWrite = isDirectWrite(node);
|
|
841
|
+
if (isWrite) return true;
|
|
842
|
+
return isDestructuringOrLoopTarget(node);
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/typescript/typescript-class-read-visitor.ts
|
|
846
|
+
var enterClass = (node, classes, stack, thisStack) => {
|
|
847
|
+
const entry = {
|
|
848
|
+
node,
|
|
849
|
+
reads: /* @__PURE__ */ new Set()
|
|
850
|
+
};
|
|
851
|
+
classes.push(entry);
|
|
852
|
+
stack.push(entry);
|
|
853
|
+
thisStack.push(true);
|
|
854
|
+
};
|
|
855
|
+
var trackMemberRead = (node, stack, thisStack, dynamicClasses) => {
|
|
856
|
+
const current = stack.at(-1);
|
|
857
|
+
const isComponentThis = thisStack.at(-1);
|
|
858
|
+
if (!current || !isComponentThis) return;
|
|
859
|
+
const receiverIsThis = isThisExpression(node.object);
|
|
544
860
|
if (node.computed && receiverIsThis) {
|
|
545
861
|
dynamicClasses.add(current);
|
|
546
862
|
}
|
|
547
|
-
if (receiverIsThis
|
|
863
|
+
if (!receiverIsThis || node.computed) return;
|
|
864
|
+
const isWriteOnlyTarget = isWriteOnly(node);
|
|
865
|
+
if (isWriteOnlyTarget) return;
|
|
866
|
+
if (node.property.type === TSESTree9.AST_NODE_TYPES.Identifier) {
|
|
548
867
|
current.reads.add(node.property.name);
|
|
549
868
|
}
|
|
550
869
|
};
|
|
551
870
|
var trackDestructuringRead = (node, stack, thisStack, dynamicClasses) => {
|
|
552
871
|
const current = stack.at(-1);
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
872
|
+
const isComponentThis = thisStack.at(-1);
|
|
873
|
+
if (!current || !isComponentThis) return;
|
|
556
874
|
const reads = destructuredThisReads(node);
|
|
557
|
-
if (reads === void 0)
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
875
|
+
if (reads === void 0) return;
|
|
560
876
|
if (reads === null) {
|
|
561
877
|
dynamicClasses.add(current);
|
|
562
878
|
return;
|
|
@@ -565,45 +881,9 @@ var trackDestructuringRead = (node, stack, thisStack, dynamicClasses) => {
|
|
|
565
881
|
current.reads.add(name);
|
|
566
882
|
}
|
|
567
883
|
};
|
|
568
|
-
var
|
|
569
|
-
FunctionDeclaration(node) {
|
|
570
|
-
thisStack.push(componentThis(node, thisStack));
|
|
571
|
-
},
|
|
572
|
-
FunctionExpression(node) {
|
|
573
|
-
thisStack.push(componentThis(node, thisStack));
|
|
574
|
-
},
|
|
575
|
-
ArrowFunctionExpression(node) {
|
|
576
|
-
thisStack.push(componentThis(node, thisStack));
|
|
577
|
-
},
|
|
578
|
-
"FunctionDeclaration:exit"() {
|
|
579
|
-
thisStack.pop();
|
|
580
|
-
},
|
|
581
|
-
"FunctionExpression:exit"() {
|
|
582
|
-
thisStack.pop();
|
|
583
|
-
},
|
|
584
|
-
"ArrowFunctionExpression:exit"() {
|
|
585
|
-
thisStack.pop();
|
|
586
|
-
},
|
|
587
|
-
StaticBlock() {
|
|
588
|
-
thisStack.push(false);
|
|
589
|
-
},
|
|
590
|
-
"StaticBlock:exit"() {
|
|
591
|
-
thisStack.pop();
|
|
592
|
-
},
|
|
593
|
-
PropertyDefinition(node) {
|
|
594
|
-
if (node.static) {
|
|
595
|
-
thisStack.push(false);
|
|
596
|
-
}
|
|
597
|
-
},
|
|
598
|
-
"PropertyDefinition:exit"(node) {
|
|
599
|
-
if (node.static) {
|
|
600
|
-
thisStack.pop();
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
});
|
|
604
|
-
var visitor = (context, imports, classes, stack, dynamicClasses) => {
|
|
884
|
+
var classReadVisitor = (classes, stack, dynamicClasses) => {
|
|
605
885
|
const thisStack = [];
|
|
606
|
-
|
|
886
|
+
const listeners = {
|
|
607
887
|
ClassDeclaration(node) {
|
|
608
888
|
enterClass(node, classes, stack, thisStack);
|
|
609
889
|
},
|
|
@@ -627,183 +907,1980 @@ var visitor = (context, imports, classes, stack, dynamicClasses) => {
|
|
|
627
907
|
},
|
|
628
908
|
VariableDeclarator(node) {
|
|
629
909
|
trackDestructuringRead(node, stack, thisStack, dynamicClasses);
|
|
630
|
-
},
|
|
631
|
-
"Program:exit"() {
|
|
632
|
-
reportUnusedMembers(context, imports, classes, dynamicClasses);
|
|
633
910
|
}
|
|
634
911
|
};
|
|
912
|
+
return listeners;
|
|
635
913
|
};
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
914
|
+
|
|
915
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.ts
|
|
916
|
+
import { isClassElement, isIdentifier as isIdentifier7, isStringLiteralLike as isStringLiteralLike6 } from "typescript";
|
|
917
|
+
|
|
918
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-directive-index.ts
|
|
919
|
+
import { CssSelector, SelectorMatcher } from "@angular/compiler";
|
|
920
|
+
var classShape = ({
|
|
921
|
+
component,
|
|
922
|
+
declaration,
|
|
923
|
+
exportAs,
|
|
924
|
+
hostDirectives,
|
|
925
|
+
name,
|
|
926
|
+
selector
|
|
927
|
+
}) => {
|
|
928
|
+
const fields = [
|
|
929
|
+
declaration.getSourceFile().fileName,
|
|
930
|
+
name,
|
|
931
|
+
String(component),
|
|
932
|
+
String(hostDirectives),
|
|
933
|
+
selector ?? "",
|
|
934
|
+
exportAs.join(",")
|
|
935
|
+
];
|
|
936
|
+
return fields.join("\0");
|
|
937
|
+
};
|
|
938
|
+
var directiveShape = (classes) => {
|
|
939
|
+
const shapes = [...classes].map(classShape);
|
|
940
|
+
return shapes.sort().join("\n");
|
|
941
|
+
};
|
|
942
|
+
var parseSelector = (selector) => {
|
|
943
|
+
try {
|
|
944
|
+
return CssSelector.parse(selector);
|
|
945
|
+
} catch {
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
var buildDirectiveIndex = (classes) => {
|
|
950
|
+
const byDeclaration = /* @__PURE__ */ new Map();
|
|
951
|
+
const byExportAs = /* @__PURE__ */ new Map();
|
|
952
|
+
const componentMatcher = new SelectorMatcher();
|
|
953
|
+
for (const angularClass2 of classes) {
|
|
954
|
+
const { component, declaration, exportAs, selector } = angularClass2;
|
|
955
|
+
byDeclaration.set(declaration, angularClass2);
|
|
956
|
+
for (const name of exportAs) {
|
|
957
|
+
const declarations = byExportAs.get(name) ?? [];
|
|
958
|
+
declarations.push(declaration);
|
|
959
|
+
byExportAs.set(name, declarations);
|
|
960
|
+
}
|
|
961
|
+
if (component && selector !== null) {
|
|
962
|
+
const selectors = parseSelector(selector);
|
|
963
|
+
if (selectors) {
|
|
964
|
+
componentMatcher.addSelectables(selectors, [declaration]);
|
|
655
965
|
}
|
|
656
966
|
}
|
|
657
|
-
|
|
658
|
-
|
|
967
|
+
}
|
|
968
|
+
const directiveIndex = {
|
|
969
|
+
byDeclaration,
|
|
970
|
+
byExportAs,
|
|
971
|
+
componentMatcher
|
|
972
|
+
};
|
|
973
|
+
return directiveIndex;
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.ts
|
|
977
|
+
import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
|
|
978
|
+
import { isIdentifier, isStringLiteralLike } from "typescript";
|
|
979
|
+
|
|
980
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.ts
|
|
981
|
+
import {
|
|
982
|
+
R3TargetBinder as R3TargetBinder2,
|
|
983
|
+
ThisReceiver as ThisReceiver3,
|
|
984
|
+
TmplAstReference,
|
|
985
|
+
parseTemplate as parseTemplate2
|
|
986
|
+
} from "@angular/compiler";
|
|
987
|
+
|
|
988
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.ts
|
|
989
|
+
import { SignatureKind, TypeFlags } from "typescript";
|
|
990
|
+
|
|
991
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/utils/type-property-symbols.util.ts
|
|
992
|
+
import { IndexKind } from "typescript";
|
|
993
|
+
var symbolsForName = (checker, type, name) => {
|
|
994
|
+
const symbol = checker.getPropertyOfType(checker.getApparentType(type), name);
|
|
995
|
+
if (symbol) return [symbol];
|
|
996
|
+
const isUnionOrIntersection = type.isUnionOrIntersection();
|
|
997
|
+
if (!isUnionOrIntersection) return [];
|
|
998
|
+
const symbols = /* @__PURE__ */ new Set();
|
|
999
|
+
for (const member of type.types) {
|
|
1000
|
+
for (const memberSymbol of symbolsForName(checker, member, name)) {
|
|
1001
|
+
symbols.add(memberSymbol);
|
|
659
1002
|
}
|
|
660
|
-
const classes = [];
|
|
661
|
-
const stack = [];
|
|
662
|
-
const dynamicClasses = /* @__PURE__ */ new Set();
|
|
663
|
-
return visitor(context, imports, classes, stack, dynamicClasses);
|
|
664
1003
|
}
|
|
665
|
-
|
|
1004
|
+
return [...symbols];
|
|
1005
|
+
};
|
|
1006
|
+
var stringIndexTypes = (checker, type) => {
|
|
1007
|
+
const indexedType = checker.getIndexTypeOfType(
|
|
1008
|
+
checker.getApparentType(type),
|
|
1009
|
+
IndexKind.String
|
|
1010
|
+
);
|
|
1011
|
+
if (indexedType) return [indexedType];
|
|
1012
|
+
const isUnionOrIntersection = type.isUnionOrIntersection();
|
|
1013
|
+
if (!isUnionOrIntersection) return [];
|
|
1014
|
+
const indexedTypes = /* @__PURE__ */ new Set();
|
|
1015
|
+
for (const member of type.types) {
|
|
1016
|
+
for (const memberIndexedType of stringIndexTypes(checker, member)) {
|
|
1017
|
+
indexedTypes.add(memberIndexedType);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
return [...indexedTypes];
|
|
1021
|
+
};
|
|
666
1022
|
|
|
667
|
-
// packages/lint-suite/src/lib/angular.ts
|
|
668
|
-
var
|
|
669
|
-
|
|
670
|
-
|
|
1023
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-resolved-path.ts
|
|
1024
|
+
var hasAnyOrUnknownFlag = (type) => {
|
|
1025
|
+
const anyOrUnknown = TypeFlags.Any | TypeFlags.Unknown;
|
|
1026
|
+
return (type.flags & anyOrUnknown) !== 0;
|
|
1027
|
+
};
|
|
1028
|
+
var isAnyOrUnknown = (types) => {
|
|
1029
|
+
return types.some(hasAnyOrUnknownFlag);
|
|
1030
|
+
};
|
|
1031
|
+
var addTypes = (sink, types) => {
|
|
1032
|
+
for (const type of types) {
|
|
1033
|
+
sink.addType(type);
|
|
671
1034
|
}
|
|
672
1035
|
};
|
|
673
|
-
var
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
1036
|
+
var callReturnTypes = (checker, type) => {
|
|
1037
|
+
const signatures = checker.getSignaturesOfType(type, SignatureKind.Call);
|
|
1038
|
+
return signatures.map((signature) => signature.getReturnType());
|
|
1039
|
+
};
|
|
1040
|
+
var memberTypes = (symbols, declaration, checker, sink) => {
|
|
1041
|
+
const types = [];
|
|
1042
|
+
for (const symbol of symbols) {
|
|
1043
|
+
for (const memberDeclaration of symbol.declarations ?? []) {
|
|
1044
|
+
sink.addDeclaration(memberDeclaration);
|
|
1045
|
+
}
|
|
1046
|
+
types.push(checker.getTypeOfSymbolAtLocation(symbol, declaration));
|
|
1047
|
+
}
|
|
1048
|
+
return types;
|
|
1049
|
+
};
|
|
1050
|
+
var addResolvedPath = ({
|
|
1051
|
+
allowMissingRoot,
|
|
1052
|
+
checker,
|
|
1053
|
+
declaration,
|
|
1054
|
+
names,
|
|
1055
|
+
sink
|
|
1056
|
+
}) => {
|
|
1057
|
+
let types = [checker.getTypeAtLocation(declaration)];
|
|
1058
|
+
const indexTypesOf = (type) => stringIndexTypes(checker, type);
|
|
1059
|
+
const returnTypesOf = (type) => callReturnTypes(checker, type);
|
|
1060
|
+
for (const [index, segment] of names.entries()) {
|
|
1061
|
+
addTypes(sink, types);
|
|
1062
|
+
const symbolsOf = (type) => {
|
|
1063
|
+
return symbolsForName(checker, type, segment.name);
|
|
1064
|
+
};
|
|
1065
|
+
const symbols = new Set(types.flatMap(symbolsOf));
|
|
1066
|
+
if (symbols.size === 0) {
|
|
1067
|
+
const indexedTypes = types.flatMap(indexTypesOf);
|
|
1068
|
+
if (indexedTypes.length > 0) {
|
|
1069
|
+
types = indexedTypes;
|
|
1070
|
+
continue;
|
|
685
1071
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
"@angular-eslint/no-uncalled-signals": "error",
|
|
696
|
-
"@angular-eslint/prefer-host-metadata-property": "error",
|
|
697
|
-
"@angular-eslint/no-implicit-take-until-destroyed": "error",
|
|
698
|
-
// v21.2.0: Enforce explicit DestroyRef
|
|
699
|
-
"@angular-eslint/no-async-lifecycle-method": "error",
|
|
700
|
-
// Prevent async lifecycle hooks
|
|
701
|
-
"@angular-eslint/prefer-output-readonly": "warn",
|
|
702
|
-
// Prevent accidental reassignment
|
|
703
|
-
"@angular-eslint/prefer-output-emitter-ref": "warn",
|
|
704
|
-
// Modern output() function over @Output()
|
|
705
|
-
"@angular-eslint/sort-lifecycle-methods": "warn",
|
|
706
|
-
// Consistent lifecycle method ordering
|
|
707
|
-
"@angular-eslint/computed-must-return": "error",
|
|
708
|
-
// Catch silent computed() bugs missing a return
|
|
709
|
-
"@angular-eslint/no-duplicates-in-metadata-arrays": "error",
|
|
710
|
-
// Catch duplicate imports/providers in @Component/@NgModule
|
|
711
|
-
"@angular-eslint/no-lifecycle-call": "error",
|
|
712
|
-
// Forbid manual this.ngOnInit() calls
|
|
713
|
-
"@angular-eslint/require-lifecycle-on-prototype": "error",
|
|
714
|
-
// Lifecycle hooks must be methods, not arrow-fn fields
|
|
715
|
-
"@angular-eslint/relative-url-prefix": "error",
|
|
716
|
-
// Require ./ in templateUrl/styleUrl for portability
|
|
717
|
-
"@angular-eslint/use-component-selector": "error",
|
|
718
|
-
// All components must declare a selector
|
|
719
|
-
"@angular-eslint/no-developer-preview": "warn",
|
|
720
|
-
// Flag APIs marked @developerPreview (no breaking-change policy)
|
|
721
|
-
"@angular-eslint/no-experimental": "warn"
|
|
722
|
-
// Flag APIs marked @experimental
|
|
1072
|
+
const isAnyOrUnknownType = isAnyOrUnknown(types);
|
|
1073
|
+
if (isAnyOrUnknownType) return true;
|
|
1074
|
+
return index === 0 && allowMissingRoot;
|
|
1075
|
+
}
|
|
1076
|
+
types = memberTypes(symbols, declaration, checker, sink);
|
|
1077
|
+
if (segment.called) {
|
|
1078
|
+
const returnTypes = types.flatMap(returnTypesOf);
|
|
1079
|
+
if (returnTypes.length === 0) return isAnyOrUnknown(types);
|
|
1080
|
+
types = returnTypes;
|
|
723
1081
|
}
|
|
724
1082
|
}
|
|
725
|
-
|
|
1083
|
+
return true;
|
|
1084
|
+
};
|
|
726
1085
|
|
|
727
|
-
// packages/lint-suite/src/lib/
|
|
728
|
-
import {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
{
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
1086
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-chains.ts
|
|
1087
|
+
import {
|
|
1088
|
+
Call,
|
|
1089
|
+
CombinedRecursiveAstVisitor as CombinedRecursiveAstVisitor2,
|
|
1090
|
+
ImplicitReceiver as ImplicitReceiver2,
|
|
1091
|
+
KeyedRead as KeyedRead3,
|
|
1092
|
+
PropertyRead as PropertyRead2,
|
|
1093
|
+
SafeKeyedRead as SafeKeyedRead3,
|
|
1094
|
+
SafePropertyRead as SafePropertyRead2,
|
|
1095
|
+
ThisReceiver as ThisReceiver2
|
|
1096
|
+
} from "@angular/compiler";
|
|
1097
|
+
var isPropertyReadLike = (node) => {
|
|
1098
|
+
const isPlainRead = node instanceof PropertyRead2;
|
|
1099
|
+
const isSafeRead = node instanceof SafePropertyRead2;
|
|
1100
|
+
return isPlainRead || isSafeRead;
|
|
1101
|
+
};
|
|
1102
|
+
var isRootRead = (read) => {
|
|
1103
|
+
const isImplicitReceiver = read.receiver instanceof ImplicitReceiver2;
|
|
1104
|
+
const isThisReceiver = read.receiver instanceof ThisReceiver2;
|
|
1105
|
+
return isImplicitReceiver || isThisReceiver;
|
|
1106
|
+
};
|
|
1107
|
+
var chainStep = (read) => {
|
|
1108
|
+
const receiver = read.receiver;
|
|
1109
|
+
const isReadReceiver = isPropertyReadLike(receiver);
|
|
1110
|
+
if (isReadReceiver) {
|
|
1111
|
+
const readStep = { called: false, next: receiver };
|
|
1112
|
+
return readStep;
|
|
1113
|
+
}
|
|
1114
|
+
const isCallReceiver = receiver instanceof Call;
|
|
1115
|
+
if (!isCallReceiver) return null;
|
|
1116
|
+
const target = receiver.receiver;
|
|
1117
|
+
const isReadLikeTarget = isPropertyReadLike(target);
|
|
1118
|
+
if (!isReadLikeTarget) return null;
|
|
1119
|
+
const calledStep = { called: true, next: target };
|
|
1120
|
+
return calledStep;
|
|
1121
|
+
};
|
|
1122
|
+
var readChain = (node) => {
|
|
1123
|
+
const leaf = { called: false, name: node.name };
|
|
1124
|
+
const names = [leaf];
|
|
1125
|
+
let current = node;
|
|
1126
|
+
while (!isRootRead(current)) {
|
|
1127
|
+
const step = chainStep(current);
|
|
1128
|
+
if (step === null) return null;
|
|
1129
|
+
current = step.next;
|
|
1130
|
+
names.unshift({ called: step.called, name: current.name });
|
|
1131
|
+
}
|
|
1132
|
+
if (!(current instanceof PropertyRead2)) return null;
|
|
1133
|
+
const chain = { names, root: current };
|
|
1134
|
+
return chain;
|
|
1135
|
+
};
|
|
1136
|
+
var ReadCollector2 = class extends CombinedRecursiveAstVisitor2 {
|
|
1137
|
+
reads = [];
|
|
1138
|
+
referenceOwners = /* @__PURE__ */ new Map();
|
|
1139
|
+
record(node) {
|
|
1140
|
+
const chain = readChain(node);
|
|
1141
|
+
if (chain) {
|
|
1142
|
+
this.reads.push(chain);
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
recordReferences(owner) {
|
|
1146
|
+
for (const reference of owner.references) {
|
|
1147
|
+
this.referenceOwners.set(reference, owner);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
visitElement(element) {
|
|
1151
|
+
this.recordReferences(element);
|
|
1152
|
+
super.visitElement(element);
|
|
1153
|
+
}
|
|
1154
|
+
visitTemplate(template) {
|
|
1155
|
+
this.recordReferences(template);
|
|
1156
|
+
super.visitTemplate(template);
|
|
1157
|
+
}
|
|
1158
|
+
visitComponent(component) {
|
|
1159
|
+
this.recordReferences(component);
|
|
1160
|
+
super.visitComponent(component);
|
|
1161
|
+
}
|
|
1162
|
+
visitDirective(directive) {
|
|
1163
|
+
this.recordReferences(directive);
|
|
1164
|
+
super.visitDirective(directive);
|
|
1165
|
+
}
|
|
1166
|
+
visitBinary(node, context) {
|
|
1167
|
+
if (node.operation !== "=") return super.visitBinary(node, context);
|
|
1168
|
+
if (isReadTarget(node.left)) {
|
|
1169
|
+
this.visit(node.left.receiver);
|
|
1170
|
+
}
|
|
1171
|
+
if (node.left instanceof KeyedRead3 || node.left instanceof SafeKeyedRead3) {
|
|
1172
|
+
this.visit(node.left.key);
|
|
1173
|
+
}
|
|
1174
|
+
this.visit(node.right);
|
|
1175
|
+
return void 0;
|
|
1176
|
+
}
|
|
1177
|
+
visitPropertyRead(node, context) {
|
|
1178
|
+
this.record(node);
|
|
1179
|
+
return super.visitPropertyRead(node, context);
|
|
1180
|
+
}
|
|
1181
|
+
visitSafePropertyRead(node, context) {
|
|
1182
|
+
this.record(node);
|
|
1183
|
+
return super.visitSafePropertyRead(node, context);
|
|
1184
|
+
}
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-references.ts
|
|
1188
|
+
import { TmplAstElement, createCssSelectorFromNode } from "@angular/compiler";
|
|
1189
|
+
|
|
1190
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/utils/read-chain-text.util.ts
|
|
1191
|
+
var segmentText = (segment) => {
|
|
1192
|
+
const call = segment.called ? "()" : "";
|
|
1193
|
+
return `${segment.name}${call}`;
|
|
1194
|
+
};
|
|
1195
|
+
var chainText = (names) => {
|
|
1196
|
+
const texts = names.map(segmentText);
|
|
1197
|
+
return texts.join(".");
|
|
1198
|
+
};
|
|
1199
|
+
|
|
1200
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-references.ts
|
|
1201
|
+
var referenceTargets = (reference, owner, directives, scope) => {
|
|
1202
|
+
const exportAs = reference.value.trim();
|
|
1203
|
+
let targets = [];
|
|
1204
|
+
if (exportAs !== "") {
|
|
1205
|
+
const exportedTargets = directives.byExportAs.get(exportAs) ?? [];
|
|
1206
|
+
targets = [...exportedTargets];
|
|
1207
|
+
} else if (owner instanceof TmplAstElement) {
|
|
1208
|
+
directives.componentMatcher.match(
|
|
1209
|
+
createCssSelectorFromNode(owner),
|
|
1210
|
+
(_selector, declarations) => {
|
|
1211
|
+
targets.push(...declarations);
|
|
1212
|
+
}
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
const scoped = scope !== null && !scope.some(
|
|
1216
|
+
(declaration) => directives.byDeclaration.get(declaration)?.hostDirectives
|
|
1217
|
+
);
|
|
1218
|
+
if (!scoped) return targets;
|
|
1219
|
+
const scopedTargets = targets.filter((target) => scope.includes(target));
|
|
1220
|
+
return scopedTargets;
|
|
1221
|
+
};
|
|
1222
|
+
var addReferenceRead = (context, reference, owner, names) => {
|
|
1223
|
+
const { checker, className, directives, fileName, scope, sink } = context;
|
|
1224
|
+
const targets = referenceTargets(reference, owner, directives, scope);
|
|
1225
|
+
const isTargetResolved = (target) => {
|
|
1226
|
+
const options = {
|
|
1227
|
+
allowMissingRoot: false,
|
|
1228
|
+
checker,
|
|
1229
|
+
declaration: target,
|
|
1230
|
+
names,
|
|
1231
|
+
sink
|
|
1232
|
+
};
|
|
1233
|
+
return addResolvedPath(options);
|
|
1234
|
+
};
|
|
1235
|
+
const resolved = targets.map(isTargetResolved);
|
|
1236
|
+
const hasResolvedTarget = resolved.includes(true);
|
|
1237
|
+
if (resolved.length === 0) return;
|
|
1238
|
+
if (hasResolvedTarget) return;
|
|
1239
|
+
const fallbackNames = names.map((segment) => segment.name);
|
|
1240
|
+
const reason = `${fileName}: cannot resolve '#${reference.name}.${chainText(names)}' in ${className}`;
|
|
1241
|
+
sink.addFallbackNames(fallbackNames, reason);
|
|
1242
|
+
};
|
|
1243
|
+
|
|
1244
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-read-resolution.ts
|
|
1245
|
+
var identifierNames = (text2) => {
|
|
1246
|
+
const matches = text2.match(/[A-Za-z_$][\w$]*/gu) ?? [];
|
|
1247
|
+
return new Set(matches);
|
|
1248
|
+
};
|
|
1249
|
+
var templateBinder2 = new R3TargetBinder2(null);
|
|
1250
|
+
var unparsedTemplateReads = (context, source, error) => {
|
|
1251
|
+
const names = identifierNames(source);
|
|
1252
|
+
const message = error ?? "unknown error";
|
|
1253
|
+
const reason = `${context.fileName}: template of ${context.className} does not parse (${message})`;
|
|
1254
|
+
context.sink.addFallbackNames(names, reason);
|
|
1255
|
+
const unparsedReads = { usedDirectiveIndex: false };
|
|
1256
|
+
return unparsedReads;
|
|
1257
|
+
};
|
|
1258
|
+
var addComponentRead = (context, names) => {
|
|
1259
|
+
const { checker, className, declaration, fileName, sink } = context;
|
|
1260
|
+
const options = {
|
|
1261
|
+
allowMissingRoot: true,
|
|
1262
|
+
checker,
|
|
1263
|
+
declaration,
|
|
1264
|
+
names,
|
|
1265
|
+
sink
|
|
1266
|
+
};
|
|
1267
|
+
const isResolved = addResolvedPath(options);
|
|
1268
|
+
if (isResolved) return;
|
|
1269
|
+
const fallbackNames = names.map((segment) => segment.name);
|
|
1270
|
+
const reason = `${fileName}: cannot resolve '${chainText(names)}' on ${className}`;
|
|
1271
|
+
sink.addFallbackNames(fallbackNames, reason);
|
|
1272
|
+
};
|
|
1273
|
+
var chainEntity = (boundTarget, chain) => {
|
|
1274
|
+
const isThisRead = chain.root.receiver instanceof ThisReceiver3;
|
|
1275
|
+
if (isThisRead) return null;
|
|
1276
|
+
return boundTarget.getExpressionTarget(chain.root);
|
|
1277
|
+
};
|
|
1278
|
+
var addChainRead = (context, reads, boundTarget, chain) => {
|
|
1279
|
+
const entity = chainEntity(boundTarget, chain);
|
|
1280
|
+
if (entity === null) {
|
|
1281
|
+
addComponentRead(context, chain.names);
|
|
1282
|
+
return false;
|
|
1283
|
+
}
|
|
1284
|
+
if (!(entity instanceof TmplAstReference)) return false;
|
|
1285
|
+
const owner = reads.referenceOwners.get(entity);
|
|
1286
|
+
const names = chain.names.slice(1);
|
|
1287
|
+
addReferenceRead(context, entity, owner, names);
|
|
1288
|
+
return true;
|
|
1289
|
+
};
|
|
1290
|
+
var addTemplateReads = ({
|
|
1291
|
+
angularClass: angularClass2,
|
|
1292
|
+
checker,
|
|
1293
|
+
directives,
|
|
1294
|
+
fileName,
|
|
1295
|
+
sink,
|
|
1296
|
+
source
|
|
1297
|
+
}) => {
|
|
1298
|
+
const { declaration, scope } = angularClass2;
|
|
1299
|
+
const className = declaration.name?.text ?? "(anonymous)";
|
|
1300
|
+
const context = {
|
|
1301
|
+
checker,
|
|
1302
|
+
className,
|
|
1303
|
+
declaration,
|
|
1304
|
+
directives,
|
|
1305
|
+
fileName,
|
|
1306
|
+
scope,
|
|
1307
|
+
sink
|
|
1308
|
+
};
|
|
1309
|
+
const parsed = parseTemplate2(source, fileName);
|
|
1310
|
+
if (parsed.errors?.length) {
|
|
1311
|
+
const error = parsed.errors[0]?.msg;
|
|
1312
|
+
return unparsedTemplateReads(context, source, error);
|
|
1313
|
+
}
|
|
1314
|
+
const boundTarget = templateBinder2.bind({ template: parsed.nodes });
|
|
1315
|
+
const reads = new ReadCollector2();
|
|
1316
|
+
for (const node of parsed.nodes) {
|
|
1317
|
+
reads.visit(node);
|
|
1318
|
+
}
|
|
1319
|
+
let usedDirectiveIndex = false;
|
|
1320
|
+
for (const chain of reads.reads) {
|
|
1321
|
+
const usedIndex = addChainRead(context, reads, boundTarget, chain);
|
|
1322
|
+
usedDirectiveIndex ||= usedIndex;
|
|
1323
|
+
}
|
|
1324
|
+
const templateReads2 = { usedDirectiveIndex };
|
|
1325
|
+
return templateReads2;
|
|
1326
|
+
};
|
|
1327
|
+
|
|
1328
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-template-reads.ts
|
|
1329
|
+
var memberNameOf = (member) => {
|
|
1330
|
+
const name = member.name;
|
|
1331
|
+
if (!name) return [];
|
|
1332
|
+
const isIdentifierName = isIdentifier(name);
|
|
1333
|
+
const isStringName = isStringLiteralLike(name);
|
|
1334
|
+
const isTextName = isIdentifierName || isStringName;
|
|
1335
|
+
if (!isTextName) return [];
|
|
1336
|
+
const names = [name.text];
|
|
1337
|
+
return names;
|
|
1338
|
+
};
|
|
1339
|
+
var memberNames = (declaration) => {
|
|
1340
|
+
return declaration.members.flatMap(memberNameOf);
|
|
1341
|
+
};
|
|
1342
|
+
var unknownTemplateNames = ({ declaration, scope }, allNames) => {
|
|
1343
|
+
if (scope === null) return allNames;
|
|
1344
|
+
const scopeNames = scope.flatMap(memberNames);
|
|
1345
|
+
const names = [...memberNames(declaration), ...scopeNames];
|
|
1346
|
+
return names;
|
|
1347
|
+
};
|
|
1348
|
+
var templateFileVersion = (fileName) => {
|
|
1349
|
+
const { mtimeNs, size } = statSync2(fileName, { bigint: true });
|
|
1350
|
+
const version = { fileName, mtimeNs, size };
|
|
1351
|
+
return version;
|
|
1352
|
+
};
|
|
1353
|
+
var templateFileIsCurrent = (version) => {
|
|
1354
|
+
try {
|
|
1355
|
+
const current = statSync2(version.fileName, { bigint: true });
|
|
1356
|
+
return current.mtimeNs === version.mtimeNs && current.size === version.size;
|
|
1357
|
+
} catch {
|
|
1358
|
+
return false;
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
var templateSourceOf = (angularClass2, allNames, sink, versions) => {
|
|
1362
|
+
const { declaration, template, valid } = angularClass2;
|
|
1363
|
+
const fileName = declaration.getSourceFile().fileName;
|
|
1364
|
+
const className = declaration.name?.text ?? "(anonymous)";
|
|
1365
|
+
if (!valid) {
|
|
1366
|
+
const names = unknownTemplateNames(angularClass2, allNames);
|
|
1367
|
+
const reason = `${fileName}: metadata of ${className} is not static (template, templateUrl)`;
|
|
1368
|
+
sink.addFallbackNames(names, reason);
|
|
1369
|
+
return null;
|
|
1370
|
+
}
|
|
1371
|
+
if (!template) return null;
|
|
1372
|
+
if (template.kind === "inline") {
|
|
1373
|
+
const inlineSource = { fileName, source: template.source };
|
|
1374
|
+
return inlineSource;
|
|
1375
|
+
}
|
|
1376
|
+
const templateFileName = template.fileName;
|
|
1377
|
+
try {
|
|
1378
|
+
versions.push(templateFileVersion(templateFileName));
|
|
1379
|
+
const source = readFileSync2(templateFileName, "utf8");
|
|
1380
|
+
const externalSource = {
|
|
1381
|
+
fileName: templateFileName,
|
|
1382
|
+
source
|
|
1383
|
+
};
|
|
1384
|
+
return externalSource;
|
|
1385
|
+
} catch {
|
|
1386
|
+
const names = unknownTemplateNames(angularClass2, allNames);
|
|
1387
|
+
const reason = `${fileName}: templateUrl of ${className} cannot be read (${templateFileName})`;
|
|
1388
|
+
sink.addFallbackNames(names, reason);
|
|
1389
|
+
return null;
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
var collectAngularTemplateReads = ({
|
|
1393
|
+
allNames,
|
|
1394
|
+
checker,
|
|
1395
|
+
classes,
|
|
1396
|
+
directives,
|
|
1397
|
+
sink
|
|
1398
|
+
}) => {
|
|
1399
|
+
const templateVersions = [];
|
|
1400
|
+
let usedDirectiveIndex = false;
|
|
1401
|
+
for (const angularClass2 of classes) {
|
|
1402
|
+
const template = templateSourceOf(
|
|
1403
|
+
angularClass2,
|
|
1404
|
+
allNames,
|
|
1405
|
+
sink,
|
|
1406
|
+
templateVersions
|
|
1407
|
+
);
|
|
1408
|
+
if (template === null) continue;
|
|
1409
|
+
const result = addTemplateReads({
|
|
1410
|
+
angularClass: angularClass2,
|
|
1411
|
+
checker,
|
|
1412
|
+
directives,
|
|
1413
|
+
fileName: template.fileName,
|
|
1414
|
+
sink,
|
|
1415
|
+
source: template.source
|
|
1416
|
+
});
|
|
1417
|
+
usedDirectiveIndex ||= result.usedDirectiveIndex;
|
|
1418
|
+
}
|
|
1419
|
+
const templateReads2 = {
|
|
1420
|
+
templateVersions,
|
|
1421
|
+
usedDirectiveIndex
|
|
1422
|
+
};
|
|
1423
|
+
return templateReads2;
|
|
1424
|
+
};
|
|
1425
|
+
|
|
1426
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.ts
|
|
1427
|
+
import {
|
|
1428
|
+
forEachChild,
|
|
1429
|
+
isClassLike,
|
|
1430
|
+
isElementAccessExpression,
|
|
1431
|
+
isPropertyAccessExpression
|
|
1432
|
+
} from "typescript";
|
|
1433
|
+
|
|
1434
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-angular-interface-methods.ts
|
|
1435
|
+
import { SyntaxKind, isMethodSignature } from "typescript";
|
|
1436
|
+
|
|
1437
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-symbol-reads.ts
|
|
1438
|
+
import {
|
|
1439
|
+
SymbolFlags,
|
|
1440
|
+
isBigIntLiteral,
|
|
1441
|
+
isIdentifier as isIdentifier2,
|
|
1442
|
+
isNumericLiteral,
|
|
1443
|
+
isPrivateIdentifier,
|
|
1444
|
+
isStringLiteralLike as isStringLiteralLike2
|
|
1445
|
+
} from "typescript";
|
|
1446
|
+
var aliasTarget = (checker, symbol) => {
|
|
1447
|
+
const isAlias = (symbol.flags & SymbolFlags.Alias) !== 0;
|
|
1448
|
+
if (!isAlias) return symbol;
|
|
1449
|
+
return checker.getAliasedSymbol(symbol);
|
|
1450
|
+
};
|
|
1451
|
+
var addSymbolDeclarations = (checker, symbol, sink) => {
|
|
1452
|
+
const resolved = aliasTarget(checker, symbol);
|
|
1453
|
+
for (const declaration of resolved.declarations ?? []) {
|
|
1454
|
+
sink.addDeclaration(declaration);
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
var allPropertySymbols = (checker, type) => {
|
|
1458
|
+
const symbols = new Set(
|
|
1459
|
+
checker.getPropertiesOfType(checker.getApparentType(type))
|
|
1460
|
+
);
|
|
1461
|
+
const isUnionOrIntersection = type.isUnionOrIntersection();
|
|
1462
|
+
if (isUnionOrIntersection) {
|
|
1463
|
+
for (const member of type.types) {
|
|
1464
|
+
for (const symbol of allPropertySymbols(checker, member)) {
|
|
1465
|
+
symbols.add(symbol);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return [...symbols];
|
|
1470
|
+
};
|
|
1471
|
+
var namedPropertySymbols = (checker, type, names) => {
|
|
1472
|
+
if (names === null) return allPropertySymbols(checker, type);
|
|
1473
|
+
return names.flatMap((name) => symbolsForName(checker, type, name));
|
|
1474
|
+
};
|
|
1475
|
+
var addNamedProperties = (checker, type, names, sink) => {
|
|
1476
|
+
sink.addType(type);
|
|
1477
|
+
const symbols = namedPropertySymbols(checker, type, names);
|
|
1478
|
+
for (const symbol of symbols) {
|
|
1479
|
+
addSymbolDeclarations(checker, symbol, sink);
|
|
1480
|
+
}
|
|
1481
|
+
return symbols;
|
|
1482
|
+
};
|
|
1483
|
+
var literalPropertyNames = (type) => {
|
|
1484
|
+
const isStringLiteral = type.isStringLiteral();
|
|
1485
|
+
const isNumberLiteral = type.isNumberLiteral();
|
|
1486
|
+
const isLiteralType = isStringLiteral || isNumberLiteral;
|
|
1487
|
+
if (isLiteralType) return [String(type.value)];
|
|
1488
|
+
const isUnion = type.isUnion();
|
|
1489
|
+
if (!isUnion) return null;
|
|
1490
|
+
const names = [];
|
|
1491
|
+
for (const member of type.types) {
|
|
1492
|
+
const memberNames2 = literalPropertyNames(member);
|
|
1493
|
+
if (!memberNames2) return null;
|
|
1494
|
+
names.push(...memberNames2);
|
|
1495
|
+
}
|
|
1496
|
+
return names;
|
|
1497
|
+
};
|
|
1498
|
+
var propertyName2 = (checker, node) => {
|
|
1499
|
+
const isIdentifierName = isIdentifier2(node);
|
|
1500
|
+
const isPrivateName = isPrivateIdentifier(node);
|
|
1501
|
+
const isStringName = isStringLiteralLike2(node);
|
|
1502
|
+
const isNumericName = isNumericLiteral(node);
|
|
1503
|
+
const isBigIntName = isBigIntLiteral(node);
|
|
1504
|
+
const isLiteralName = isIdentifierName || isPrivateName || isStringName || isNumericName || isBigIntName;
|
|
1505
|
+
if (isLiteralName) return [node.text];
|
|
1506
|
+
return literalPropertyNames(checker.getTypeAtLocation(node.expression));
|
|
1507
|
+
};
|
|
1508
|
+
|
|
1509
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-angular-interface-methods.ts
|
|
1510
|
+
var isAngularMethodSignature = (declaration) => {
|
|
1511
|
+
const isMethod = isMethodSignature(declaration);
|
|
1512
|
+
if (!isMethod) return false;
|
|
1513
|
+
const fileName = declaration.getSourceFile().fileName.replaceAll("\\", "/");
|
|
1514
|
+
return fileName.includes("/node_modules/@angular/");
|
|
1515
|
+
};
|
|
1516
|
+
var isAngularInterfaceMethod = (symbol) => {
|
|
1517
|
+
const declarations = symbol.declarations ?? [];
|
|
1518
|
+
return declarations.some(isAngularMethodSignature);
|
|
1519
|
+
};
|
|
1520
|
+
var addImplementations = (heritageType, classType, checker, sink) => {
|
|
1521
|
+
for (const interfaceMethod of heritageType.getProperties()) {
|
|
1522
|
+
const isInterfaceMethod = isAngularInterfaceMethod(interfaceMethod);
|
|
1523
|
+
if (!isInterfaceMethod) continue;
|
|
1524
|
+
const implementation = classType.getProperty(interfaceMethod.name);
|
|
1525
|
+
if (implementation) {
|
|
1526
|
+
addSymbolDeclarations(checker, implementation, sink);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
var collectAngularInterfaceMethods = (node, checker, sink) => {
|
|
1531
|
+
const heritageClauses = node.heritageClauses ?? [];
|
|
1532
|
+
const implementsClauses = heritageClauses.filter(
|
|
1533
|
+
(clause) => clause.token === SyntaxKind.ImplementsKeyword
|
|
1534
|
+
);
|
|
1535
|
+
if (implementsClauses.length === 0) return;
|
|
1536
|
+
const classType = checker.getTypeAtLocation(node);
|
|
1537
|
+
for (const clause of implementsClauses) {
|
|
1538
|
+
for (const heritageType of clause.types) {
|
|
1539
|
+
const type = checker.getTypeAtLocation(heritageType);
|
|
1540
|
+
sink.addType(type);
|
|
1541
|
+
addImplementations(type, classType, checker, sink);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
|
|
1546
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.ts
|
|
1547
|
+
import {
|
|
1548
|
+
SyntaxKind as SyntaxKind2,
|
|
1549
|
+
isArrayLiteralExpression as isArrayLiteralExpression2,
|
|
1550
|
+
isBinaryExpression,
|
|
1551
|
+
isBindingElement,
|
|
1552
|
+
isObjectLiteralExpression as isObjectLiteralExpression2,
|
|
1553
|
+
isVariableDeclaration
|
|
1554
|
+
} from "typescript";
|
|
1555
|
+
|
|
1556
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-assignment-pattern-elements.ts
|
|
1557
|
+
import {
|
|
1558
|
+
isArrayLiteralExpression,
|
|
1559
|
+
isObjectLiteralExpression,
|
|
1560
|
+
isOmittedExpression,
|
|
1561
|
+
isPropertyAssignment,
|
|
1562
|
+
isSpreadAssignment,
|
|
1563
|
+
isSpreadElement
|
|
1564
|
+
} from "typescript";
|
|
1565
|
+
var nestedPattern = (expression) => {
|
|
1566
|
+
const isArrayLiteral = isArrayLiteralExpression(expression);
|
|
1567
|
+
const isObjectLiteral = isObjectLiteralExpression(expression);
|
|
1568
|
+
const isNested = isArrayLiteral || isObjectLiteral;
|
|
1569
|
+
if (!isNested) return null;
|
|
1570
|
+
return expression;
|
|
1571
|
+
};
|
|
1572
|
+
var nestedPropertyPattern = (property) => {
|
|
1573
|
+
const isAssignment = isPropertyAssignment(property);
|
|
1574
|
+
if (!isAssignment) return null;
|
|
1575
|
+
return nestedPattern(property.initializer);
|
|
1576
|
+
};
|
|
1577
|
+
var arrayElementRead = (element, index) => {
|
|
1578
|
+
const isOmitted = isOmittedExpression(element);
|
|
1579
|
+
if (isOmitted) return null;
|
|
1580
|
+
const isRestElement = isSpreadElement(element);
|
|
1581
|
+
const indexNames = [String(index)];
|
|
1582
|
+
const names = isRestElement ? null : indexNames;
|
|
1583
|
+
const nested = nestedPattern(element);
|
|
1584
|
+
const read = {
|
|
1585
|
+
location: element,
|
|
1586
|
+
names,
|
|
1587
|
+
nested,
|
|
1588
|
+
rest: false
|
|
1589
|
+
};
|
|
1590
|
+
return read;
|
|
1591
|
+
};
|
|
1592
|
+
var propertyRead = (property, checker) => {
|
|
1593
|
+
const isRestProperty = isSpreadAssignment(property);
|
|
1594
|
+
if (isRestProperty) {
|
|
1595
|
+
const restRead = {
|
|
1596
|
+
location: property,
|
|
1597
|
+
names: null,
|
|
1598
|
+
nested: null,
|
|
1599
|
+
rest: true
|
|
1600
|
+
};
|
|
1601
|
+
return restRead;
|
|
1602
|
+
}
|
|
1603
|
+
const read = {
|
|
1604
|
+
location: property,
|
|
1605
|
+
names: propertyName2(checker, property.name),
|
|
1606
|
+
nested: nestedPropertyPattern(property),
|
|
1607
|
+
rest: false
|
|
1608
|
+
};
|
|
1609
|
+
return read;
|
|
1610
|
+
};
|
|
1611
|
+
var assignmentPatternElements = (pattern, checker) => {
|
|
1612
|
+
const isArrayPattern = isArrayLiteralExpression(pattern);
|
|
1613
|
+
if (isArrayPattern) {
|
|
1614
|
+
const reads = [];
|
|
1615
|
+
for (const [index, element] of pattern.elements.entries()) {
|
|
1616
|
+
const read = arrayElementRead(element, index);
|
|
1617
|
+
if (read !== null) {
|
|
1618
|
+
reads.push(read);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
return reads;
|
|
1622
|
+
}
|
|
1623
|
+
return pattern.properties.map((property) => propertyRead(property, checker));
|
|
1624
|
+
};
|
|
1625
|
+
|
|
1626
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-binding-pattern-elements.ts
|
|
1627
|
+
import {
|
|
1628
|
+
isArrayBindingPattern,
|
|
1629
|
+
isIdentifier as isIdentifier3,
|
|
1630
|
+
isObjectBindingPattern,
|
|
1631
|
+
isOmittedExpression as isOmittedExpression2
|
|
1632
|
+
} from "typescript";
|
|
1633
|
+
var isBindingPattern = (node) => {
|
|
1634
|
+
const isArrayPattern = isArrayBindingPattern(node);
|
|
1635
|
+
const isObjectPattern = isObjectBindingPattern(node);
|
|
1636
|
+
return isArrayPattern || isObjectPattern;
|
|
1637
|
+
};
|
|
1638
|
+
var nestedPattern2 = (element) => {
|
|
1639
|
+
const isNested = isBindingPattern(element.name);
|
|
1640
|
+
if (!isNested) return null;
|
|
1641
|
+
return element.name;
|
|
1642
|
+
};
|
|
1643
|
+
var bindingNames = (checker, element) => {
|
|
1644
|
+
if (element.propertyName) return propertyName2(checker, element.propertyName);
|
|
1645
|
+
const isIdentifierName = isIdentifier3(element.name);
|
|
1646
|
+
if (!isIdentifierName) return null;
|
|
1647
|
+
return [element.name.text];
|
|
1648
|
+
};
|
|
1649
|
+
var arrayElementRead2 = (element, index) => {
|
|
1650
|
+
const isOmitted = isOmittedExpression2(element);
|
|
1651
|
+
if (isOmitted) return null;
|
|
1652
|
+
const isRestElement = Boolean(element.dotDotDotToken);
|
|
1653
|
+
const indexNames = [String(index)];
|
|
1654
|
+
const names = isRestElement ? null : indexNames;
|
|
1655
|
+
const nested = nestedPattern2(element);
|
|
1656
|
+
const read = {
|
|
1657
|
+
location: element,
|
|
1658
|
+
names,
|
|
1659
|
+
nested,
|
|
1660
|
+
rest: false
|
|
1661
|
+
};
|
|
1662
|
+
return read;
|
|
1663
|
+
};
|
|
1664
|
+
var objectElementRead = (element, checker) => {
|
|
1665
|
+
const isRestElement = Boolean(element.dotDotDotToken);
|
|
1666
|
+
if (isRestElement) {
|
|
1667
|
+
const restRead = {
|
|
1668
|
+
location: element,
|
|
1669
|
+
names: null,
|
|
1670
|
+
nested: null,
|
|
1671
|
+
rest: true
|
|
1672
|
+
};
|
|
1673
|
+
return restRead;
|
|
1674
|
+
}
|
|
1675
|
+
const read = {
|
|
1676
|
+
location: element,
|
|
1677
|
+
names: bindingNames(checker, element),
|
|
1678
|
+
nested: nestedPattern2(element),
|
|
1679
|
+
rest: false
|
|
1680
|
+
};
|
|
1681
|
+
return read;
|
|
1682
|
+
};
|
|
1683
|
+
var bindingPatternElements = (pattern, checker) => {
|
|
1684
|
+
const isArrayPattern = isArrayBindingPattern(pattern);
|
|
1685
|
+
if (isArrayPattern) {
|
|
1686
|
+
const reads = [];
|
|
1687
|
+
for (const [index, element] of pattern.elements.entries()) {
|
|
1688
|
+
const read = arrayElementRead2(element, index);
|
|
1689
|
+
if (read !== null) {
|
|
1690
|
+
reads.push(read);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
return reads;
|
|
1694
|
+
}
|
|
1695
|
+
return pattern.elements.map((element) => objectElementRead(element, checker));
|
|
1696
|
+
};
|
|
1697
|
+
|
|
1698
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-destructuring-reads.ts
|
|
1699
|
+
var lazyType = (compute) => {
|
|
1700
|
+
let type;
|
|
1701
|
+
return () => type ??= compute();
|
|
1702
|
+
};
|
|
1703
|
+
var isSkippable = (read, candidateNames) => {
|
|
1704
|
+
if (read.nested !== null) return false;
|
|
1705
|
+
if (read.names === null) return false;
|
|
1706
|
+
const hasCandidateName = read.names.some((name) => candidateNames.has(name));
|
|
1707
|
+
return !hasCandidateName;
|
|
1708
|
+
};
|
|
1709
|
+
var patternElements = (pattern, checker) => {
|
|
1710
|
+
const isBinding = isBindingPattern(pattern);
|
|
1711
|
+
if (isBinding) return bindingPatternElements(pattern, checker);
|
|
1712
|
+
return assignmentPatternElements(pattern, checker);
|
|
1713
|
+
};
|
|
1714
|
+
var addRestReads = (type, consumed, context) => {
|
|
1715
|
+
context.sink.addType(type);
|
|
1716
|
+
for (const symbol of allPropertySymbols(context.checker, type)) {
|
|
1717
|
+
const isConsumed = consumed.has(symbol.name);
|
|
1718
|
+
if (isConsumed) continue;
|
|
1719
|
+
addSymbolDeclarations(context.checker, symbol, context.sink);
|
|
1720
|
+
}
|
|
1721
|
+
};
|
|
1722
|
+
var collectPatternReads = (pattern, type, context) => {
|
|
1723
|
+
const { checker, sink, candidateNames } = context;
|
|
1724
|
+
const consumed = /* @__PURE__ */ new Set();
|
|
1725
|
+
const reads = patternElements(pattern, checker);
|
|
1726
|
+
for (const read of reads) {
|
|
1727
|
+
if (read.rest) {
|
|
1728
|
+
addRestReads(type(), consumed, context);
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
for (const name of read.names ?? []) {
|
|
1732
|
+
consumed.add(name);
|
|
1733
|
+
}
|
|
1734
|
+
const skip = isSkippable(read, candidateNames);
|
|
1735
|
+
if (skip) continue;
|
|
1736
|
+
const symbols = addNamedProperties(checker, type(), read.names, sink);
|
|
1737
|
+
const nested = read.nested;
|
|
1738
|
+
if (nested === null) continue;
|
|
1739
|
+
for (const symbol of symbols) {
|
|
1740
|
+
const nestedType = lazyType(() => {
|
|
1741
|
+
return checker.getTypeOfSymbolAtLocation(symbol, read.location);
|
|
1742
|
+
});
|
|
1743
|
+
collectPatternReads(nested, nestedType, context);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
};
|
|
1747
|
+
var collectBindingReads = (node, context) => {
|
|
1748
|
+
const isBinding = isBindingPattern(node);
|
|
1749
|
+
if (!isBinding) return;
|
|
1750
|
+
const isBindingElementParent = isBindingElement(node.parent);
|
|
1751
|
+
const isNestedBindingName = isBindingElementParent && node.parent.name === node;
|
|
1752
|
+
if (isNestedBindingName) return;
|
|
1753
|
+
const declaration = node.parent;
|
|
1754
|
+
const isDeclaration = isVariableDeclaration(declaration);
|
|
1755
|
+
const isInitializedDeclaration = isDeclaration && declaration.initializer !== void 0;
|
|
1756
|
+
const source = isInitializedDeclaration ? declaration.initializer : declaration;
|
|
1757
|
+
const sourceTypeOf = () => context.checker.getTypeAtLocation(source);
|
|
1758
|
+
const sourceType = lazyType(sourceTypeOf);
|
|
1759
|
+
collectPatternReads(node, sourceType, context);
|
|
1760
|
+
};
|
|
1761
|
+
var collectAssignmentReads = (node, context) => {
|
|
1762
|
+
const isBinary = isBinaryExpression(node);
|
|
1763
|
+
if (!isBinary) return;
|
|
1764
|
+
const isEqualsAssignment = node.operatorToken.kind === SyntaxKind2.EqualsToken;
|
|
1765
|
+
if (!isEqualsAssignment) return;
|
|
1766
|
+
const isArrayTarget = isArrayLiteralExpression2(node.left);
|
|
1767
|
+
const isObjectTarget = isObjectLiteralExpression2(node.left);
|
|
1768
|
+
const isDestructuringAssignment = isArrayTarget || isObjectTarget;
|
|
1769
|
+
if (!isDestructuringAssignment) return;
|
|
1770
|
+
const rightTypeOf = () => context.checker.getTypeAtLocation(node.right);
|
|
1771
|
+
const sourceType = lazyType(rightTypeOf);
|
|
1772
|
+
collectPatternReads(node.left, sourceType, context);
|
|
1773
|
+
};
|
|
1774
|
+
var collectDestructuringReads = (node, checker, sink, candidateNames) => {
|
|
1775
|
+
const context = { checker, sink, candidateNames };
|
|
1776
|
+
collectBindingReads(node, context);
|
|
1777
|
+
collectAssignmentReads(node, context);
|
|
1778
|
+
};
|
|
1779
|
+
|
|
1780
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-write-targets.ts
|
|
1781
|
+
import {
|
|
1782
|
+
SyntaxKind as SyntaxKind3,
|
|
1783
|
+
isArrayLiteralExpression as isArrayLiteralExpression3,
|
|
1784
|
+
isAsExpression,
|
|
1785
|
+
isBinaryExpression as isBinaryExpression2,
|
|
1786
|
+
isDeleteExpression,
|
|
1787
|
+
isForInStatement,
|
|
1788
|
+
isForOfStatement,
|
|
1789
|
+
isNonNullExpression,
|
|
1790
|
+
isObjectLiteralExpression as isObjectLiteralExpression3,
|
|
1791
|
+
isParenthesizedExpression,
|
|
1792
|
+
isPropertyAssignment as isPropertyAssignment2,
|
|
1793
|
+
isSatisfiesExpression,
|
|
1794
|
+
isSpreadAssignment as isSpreadAssignment2,
|
|
1795
|
+
isTypeAssertionExpression
|
|
1796
|
+
} from "typescript";
|
|
1797
|
+
var isExpressionWrapper = (parent, child) => {
|
|
1798
|
+
const isParenthesized = isParenthesizedExpression(parent);
|
|
1799
|
+
const isAs = isAsExpression(parent);
|
|
1800
|
+
const isTypeAssertion = isTypeAssertionExpression(parent);
|
|
1801
|
+
const isNonNull = isNonNullExpression(parent);
|
|
1802
|
+
const isSatisfies = isSatisfiesExpression(parent);
|
|
1803
|
+
const isCast = isAs || isTypeAssertion || isSatisfies;
|
|
1804
|
+
const isWrapper = isParenthesized || isNonNull || isCast;
|
|
1805
|
+
if (!isWrapper) return false;
|
|
1806
|
+
return parent.expression === child;
|
|
1807
|
+
};
|
|
1808
|
+
var isPatternContainer = (parent, child) => {
|
|
1809
|
+
const isArrayLiteral = isArrayLiteralExpression3(parent);
|
|
1810
|
+
if (isArrayLiteral) {
|
|
1811
|
+
return parent.elements.some((element) => element === child);
|
|
1812
|
+
}
|
|
1813
|
+
const isObjectLiteral = isObjectLiteralExpression3(parent);
|
|
1814
|
+
if (isObjectLiteral) {
|
|
1815
|
+
return parent.properties.some((property) => property === child);
|
|
1816
|
+
}
|
|
1817
|
+
const isAssignment = isPropertyAssignment2(parent);
|
|
1818
|
+
if (isAssignment) return parent.initializer === child;
|
|
1819
|
+
const isSpread = isSpreadAssignment2(parent);
|
|
1820
|
+
if (!isSpread) return false;
|
|
1821
|
+
return parent.expression === child;
|
|
1822
|
+
};
|
|
1823
|
+
var isWriteTarget = (parent, current) => {
|
|
1824
|
+
const isBinary = isBinaryExpression2(parent);
|
|
1825
|
+
const isBinaryTarget = isBinary && parent.left === current && parent.operatorToken.kind === SyntaxKind3.EqualsToken;
|
|
1826
|
+
if (isBinaryTarget) return true;
|
|
1827
|
+
const isDelete = isDeleteExpression(parent);
|
|
1828
|
+
const isDeleteTarget = isDelete && parent.expression === current;
|
|
1829
|
+
if (isDeleteTarget) return true;
|
|
1830
|
+
const isForIn = isForInStatement(parent);
|
|
1831
|
+
const isForOf = isForOfStatement(parent);
|
|
1832
|
+
const isIteration = isForIn || isForOf;
|
|
1833
|
+
return isIteration && parent.initializer === current;
|
|
1834
|
+
};
|
|
1835
|
+
var isWriteOnly2 = (node) => {
|
|
1836
|
+
let current = node;
|
|
1837
|
+
let parent = node.parent;
|
|
1838
|
+
while (isExpressionWrapper(parent, current) || isPatternContainer(parent, current)) {
|
|
1839
|
+
current = parent;
|
|
1840
|
+
parent = parent.parent;
|
|
1841
|
+
}
|
|
1842
|
+
return isWriteTarget(parent, current);
|
|
1843
|
+
};
|
|
1844
|
+
|
|
1845
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-reads.ts
|
|
1846
|
+
var addPropertyAccessRead = (node, checker, sink, candidateNames) => {
|
|
1847
|
+
const isCandidateName = candidateNames.has(node.name.text);
|
|
1848
|
+
if (!isCandidateName) return;
|
|
1849
|
+
const isWriteTarget2 = isWriteOnly2(node);
|
|
1850
|
+
if (isWriteTarget2) return;
|
|
1851
|
+
sink.addType(checker.getTypeAtLocation(node.expression));
|
|
1852
|
+
const symbol = checker.getSymbolAtLocation(node.name);
|
|
1853
|
+
if (symbol) {
|
|
1854
|
+
addSymbolDeclarations(checker, symbol, sink);
|
|
1855
|
+
}
|
|
1856
|
+
};
|
|
1857
|
+
var addElementAccessRead = (node, checker, sink, candidateNames) => {
|
|
1858
|
+
const isWriteTarget2 = isWriteOnly2(node);
|
|
1859
|
+
if (isWriteTarget2) return;
|
|
1860
|
+
const argumentType = checker.getTypeAtLocation(node.argumentExpression);
|
|
1861
|
+
const names = literalPropertyNames(argumentType);
|
|
1862
|
+
if (names) {
|
|
1863
|
+
const hasCandidateName = names.some((name) => candidateNames.has(name));
|
|
1864
|
+
if (!hasCandidateName) return;
|
|
1865
|
+
}
|
|
1866
|
+
const targetType = checker.getTypeAtLocation(node.expression);
|
|
1867
|
+
addNamedProperties(checker, targetType, names, sink);
|
|
1868
|
+
};
|
|
1869
|
+
var collectTypeScriptReads = (sourceFile, checker, sink, candidateNames) => {
|
|
1870
|
+
const visit = (node) => {
|
|
1871
|
+
const isPropertyAccess = isPropertyAccessExpression(node);
|
|
1872
|
+
const isElementAccess = isElementAccessExpression(node);
|
|
1873
|
+
const isClass = isClassLike(node);
|
|
1874
|
+
if (isPropertyAccess) {
|
|
1875
|
+
addPropertyAccessRead(node, checker, sink, candidateNames);
|
|
1876
|
+
} else if (isElementAccess) {
|
|
1877
|
+
addElementAccessRead(node, checker, sink, candidateNames);
|
|
1878
|
+
} else if (isClass) {
|
|
1879
|
+
collectAngularInterfaceMethods(node, checker, sink);
|
|
1880
|
+
}
|
|
1881
|
+
collectDestructuringReads(node, checker, sink, candidateNames);
|
|
1882
|
+
forEachChild(node, visit);
|
|
1883
|
+
};
|
|
1884
|
+
visit(sourceFile);
|
|
1885
|
+
};
|
|
1886
|
+
|
|
1887
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-type-dependencies.ts
|
|
1888
|
+
import { ObjectFlags, TypeFlags as TypeFlags2 } from "typescript";
|
|
1889
|
+
var isObjectType = (type) => {
|
|
1890
|
+
return (type.flags & TypeFlags2.Object) !== 0;
|
|
1891
|
+
};
|
|
1892
|
+
var isTypeReference = (type) => {
|
|
1893
|
+
return (type.objectFlags & ObjectFlags.Reference) !== 0;
|
|
1894
|
+
};
|
|
1895
|
+
var isInterfaceType = (type) => {
|
|
1896
|
+
return (type.objectFlags & ObjectFlags.ClassOrInterface) !== 0;
|
|
1897
|
+
};
|
|
1898
|
+
var addDeclarationFiles = (type, dependencies) => {
|
|
1899
|
+
for (const symbol of [type.getSymbol(), type.aliasSymbol]) {
|
|
1900
|
+
const declarations = symbol?.declarations ?? [];
|
|
1901
|
+
for (const declaration of declarations) {
|
|
1902
|
+
dependencies.add(declaration.getSourceFile());
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
};
|
|
1906
|
+
var baseTypesOf = (checker, type) => {
|
|
1907
|
+
const isObject = isObjectType(type);
|
|
1908
|
+
if (!isObject) return [];
|
|
1909
|
+
const isReference = isTypeReference(type);
|
|
1910
|
+
const target = isReference ? type.target : type;
|
|
1911
|
+
const isClassOrInterface = isInterfaceType(target);
|
|
1912
|
+
if (!isClassOrInterface) return [];
|
|
1913
|
+
return checker.getBaseTypes(target);
|
|
1914
|
+
};
|
|
1915
|
+
var addTypeDependencies = (checker, type, dependencies, seen) => {
|
|
1916
|
+
const isSeen = seen.has(type);
|
|
1917
|
+
if (isSeen) return;
|
|
1918
|
+
seen.add(type);
|
|
1919
|
+
const isUnionOrIntersection = type.isUnionOrIntersection();
|
|
1920
|
+
if (isUnionOrIntersection) {
|
|
1921
|
+
for (const member of type.types) {
|
|
1922
|
+
addTypeDependencies(checker, member, dependencies, seen);
|
|
1923
|
+
}
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
addDeclarationFiles(type, dependencies);
|
|
1927
|
+
const isTypeParameter = (type.flags & TypeFlags2.TypeParameter) !== 0;
|
|
1928
|
+
if (isTypeParameter) {
|
|
1929
|
+
const constraint = checker.getBaseConstraintOfType(type);
|
|
1930
|
+
if (constraint) {
|
|
1931
|
+
addTypeDependencies(checker, constraint, dependencies, seen);
|
|
1932
|
+
}
|
|
1933
|
+
return;
|
|
1934
|
+
}
|
|
1935
|
+
for (const base2 of baseTypesOf(checker, type)) {
|
|
1936
|
+
addTypeDependencies(checker, base2, dependencies, seen);
|
|
1937
|
+
}
|
|
1938
|
+
};
|
|
1939
|
+
|
|
1940
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-file-entry.ts
|
|
1941
|
+
var failureText = (error) => {
|
|
1942
|
+
if (error instanceof Error) return error.message;
|
|
1943
|
+
return String(error);
|
|
1944
|
+
};
|
|
1945
|
+
var collectFileReads = (index, sourceFile, checker, sink) => {
|
|
1946
|
+
try {
|
|
1947
|
+
collectTypeScriptReads(sourceFile, checker, sink, index.candidateNames);
|
|
1948
|
+
const classes = index.classes.get(sourceFile.fileName)?.classes ?? [];
|
|
1949
|
+
return collectAngularTemplateReads({
|
|
1950
|
+
allNames: index.candidateNames,
|
|
1951
|
+
checker,
|
|
1952
|
+
classes,
|
|
1953
|
+
directives: index.directives,
|
|
1954
|
+
sink
|
|
1955
|
+
});
|
|
1956
|
+
} catch (error) {
|
|
1957
|
+
const errorMessage = failureText(error);
|
|
1958
|
+
sink.addFallbackNames(
|
|
1959
|
+
index.candidateNames,
|
|
1960
|
+
`${sourceFile.fileName}: indexing failed (${errorMessage})`
|
|
1961
|
+
);
|
|
1962
|
+
const noTemplateReads = {
|
|
1963
|
+
templateVersions: [],
|
|
1964
|
+
usedDirectiveIndex: false
|
|
1965
|
+
};
|
|
1966
|
+
return noTemplateReads;
|
|
1967
|
+
}
|
|
1968
|
+
};
|
|
1969
|
+
var computeEntry = (index, sourceFile, checker) => {
|
|
1970
|
+
const declarations = /* @__PURE__ */ new Set();
|
|
1971
|
+
const dependencies = /* @__PURE__ */ new Set();
|
|
1972
|
+
const fallbackNames = /* @__PURE__ */ new Set();
|
|
1973
|
+
const seenTypes = /* @__PURE__ */ new Set();
|
|
1974
|
+
const sink = {
|
|
1975
|
+
addDeclaration: (declaration) => {
|
|
1976
|
+
declarations.add(declaration);
|
|
1977
|
+
dependencies.add(declaration.getSourceFile());
|
|
1978
|
+
},
|
|
1979
|
+
addFallbackNames: (names) => {
|
|
1980
|
+
for (const name of names) {
|
|
1981
|
+
fallbackNames.add(name);
|
|
1982
|
+
}
|
|
1983
|
+
},
|
|
1984
|
+
addType: (type) => {
|
|
1985
|
+
addTypeDependencies(checker, type, dependencies, seenTypes);
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
const reads = collectFileReads(index, sourceFile, checker, sink);
|
|
1989
|
+
dependencies.delete(sourceFile);
|
|
1990
|
+
const fileEntry = {
|
|
1991
|
+
...reads,
|
|
1992
|
+
declarations,
|
|
1993
|
+
dependencies,
|
|
1994
|
+
fallbackNames,
|
|
1995
|
+
sourceFile
|
|
1996
|
+
};
|
|
1997
|
+
return fileEntry;
|
|
1998
|
+
};
|
|
1999
|
+
|
|
2000
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.ts
|
|
2001
|
+
import {
|
|
2002
|
+
canHaveDecorators as canHaveDecorators2,
|
|
2003
|
+
forEachChild as forEachChild2,
|
|
2004
|
+
getDecorators as getDecorators2,
|
|
2005
|
+
isCallExpression as isCallExpression2,
|
|
2006
|
+
isClassLike as isClassLike3,
|
|
2007
|
+
isObjectLiteralExpression as isObjectLiteralExpression4
|
|
2008
|
+
} from "typescript";
|
|
2009
|
+
|
|
2010
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-scope.ts
|
|
2011
|
+
import {
|
|
2012
|
+
SyntaxKind as SyntaxKind4,
|
|
2013
|
+
isArrayLiteralExpression as isArrayLiteralExpression4,
|
|
2014
|
+
isClassLike as isClassLike2,
|
|
2015
|
+
isIdentifier as isIdentifier5,
|
|
2016
|
+
isPropertyAccessExpression as isPropertyAccessExpression3
|
|
2017
|
+
} from "typescript";
|
|
2018
|
+
|
|
2019
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-decorator-kind.ts
|
|
2020
|
+
import {
|
|
2021
|
+
SymbolFlags as SymbolFlags2,
|
|
2022
|
+
canHaveDecorators,
|
|
2023
|
+
getDecorators,
|
|
2024
|
+
isCallExpression,
|
|
2025
|
+
isImportDeclaration,
|
|
2026
|
+
isStringLiteralLike as isStringLiteralLike3
|
|
2027
|
+
} from "typescript";
|
|
2028
|
+
var decoratorKinds = [
|
|
2029
|
+
"Component",
|
|
2030
|
+
"Directive",
|
|
2031
|
+
"NgModule",
|
|
2032
|
+
"Pipe"
|
|
2033
|
+
];
|
|
2034
|
+
var isDecoratorKind = (name) => {
|
|
2035
|
+
return decoratorKinds.some((kind) => kind === name);
|
|
2036
|
+
};
|
|
2037
|
+
var isAngularCoreFile = (declaration) => {
|
|
2038
|
+
const fileName = declaration.getSourceFile().fileName.replaceAll("\\", "/");
|
|
2039
|
+
return fileName.includes("/node_modules/@angular/core/");
|
|
2040
|
+
};
|
|
2041
|
+
var parentOf2 = (node) => {
|
|
2042
|
+
return node.parent;
|
|
2043
|
+
};
|
|
2044
|
+
var importedFromAngularCore = (declaration) => {
|
|
2045
|
+
let current = parentOf2(declaration);
|
|
2046
|
+
while (current) {
|
|
2047
|
+
if (isImportDeclaration(current)) {
|
|
2048
|
+
return isStringLiteralLike3(current.moduleSpecifier) && current.moduleSpecifier.text === "@angular/core";
|
|
2049
|
+
}
|
|
2050
|
+
current = parentOf2(current);
|
|
2051
|
+
}
|
|
2052
|
+
return false;
|
|
2053
|
+
};
|
|
2054
|
+
var resolveAlias = (symbol, discovery) => {
|
|
2055
|
+
let current = symbol;
|
|
2056
|
+
for (let hop = 0; hop < 32; hop += 1) {
|
|
2057
|
+
for (const declaration of current.declarations ?? []) {
|
|
2058
|
+
discovery.dependencies.add(declaration.getSourceFile());
|
|
2059
|
+
}
|
|
2060
|
+
if ((current.flags & SymbolFlags2.Alias) === 0) return current;
|
|
2061
|
+
const next = discovery.checker.getImmediateAliasedSymbol(current);
|
|
2062
|
+
if (!next || next === current) return current;
|
|
2063
|
+
current = next;
|
|
2064
|
+
}
|
|
2065
|
+
return current;
|
|
2066
|
+
};
|
|
2067
|
+
var angularDecoratorKind = (expression, discovery) => {
|
|
2068
|
+
const unresolved = discovery.checker.getSymbolAtLocation(expression);
|
|
2069
|
+
if (!unresolved) return null;
|
|
2070
|
+
const symbol = resolveAlias(unresolved, discovery);
|
|
2071
|
+
const name = symbol.getName();
|
|
2072
|
+
const isKind = isDecoratorKind(name);
|
|
2073
|
+
if (!isKind) return null;
|
|
2074
|
+
const unresolvedDeclarations = unresolved.declarations ?? [];
|
|
2075
|
+
const symbolDeclarations = symbol.declarations ?? [];
|
|
2076
|
+
const isCoreImport = unresolvedDeclarations.some(importedFromAngularCore);
|
|
2077
|
+
const isCoreDeclaration = symbolDeclarations.some(isAngularCoreFile);
|
|
2078
|
+
const fromAngularCore = isCoreImport || isCoreDeclaration;
|
|
2079
|
+
if (!fromAngularCore) return null;
|
|
2080
|
+
return name;
|
|
2081
|
+
};
|
|
2082
|
+
var classDecoratorKind = (declaration, discovery) => {
|
|
2083
|
+
const hasDecorators = canHaveDecorators(declaration);
|
|
2084
|
+
if (!hasDecorators) return null;
|
|
2085
|
+
for (const decorator of getDecorators(declaration) ?? []) {
|
|
2086
|
+
const isDecoratorCall = isCallExpression(decorator.expression);
|
|
2087
|
+
if (!isDecoratorCall) continue;
|
|
2088
|
+
const kind = angularDecoratorKind(
|
|
2089
|
+
decorator.expression.expression,
|
|
2090
|
+
discovery
|
|
2091
|
+
);
|
|
2092
|
+
if (kind !== null) return kind;
|
|
2093
|
+
}
|
|
2094
|
+
return null;
|
|
2095
|
+
};
|
|
2096
|
+
|
|
2097
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-metadata-values.ts
|
|
2098
|
+
import { dirname as dirname2, resolve as resolve2 } from "node:path";
|
|
2099
|
+
import {
|
|
2100
|
+
isIdentifier as isIdentifier4,
|
|
2101
|
+
isNumericLiteral as isNumericLiteral2,
|
|
2102
|
+
isPropertyAccessExpression as isPropertyAccessExpression2,
|
|
2103
|
+
isPropertyAssignment as isPropertyAssignment3,
|
|
2104
|
+
isStringLiteralLike as isStringLiteralLike4
|
|
2105
|
+
} from "typescript";
|
|
2106
|
+
var stringValue = (node, discovery) => {
|
|
2107
|
+
if (!node) return null;
|
|
2108
|
+
const isStringLiteral = isStringLiteralLike4(node);
|
|
2109
|
+
if (isStringLiteral) return node.text;
|
|
2110
|
+
const isIdentifierNode = isIdentifier4(node);
|
|
2111
|
+
const isPropertyAccessNode = isPropertyAccessExpression2(node);
|
|
2112
|
+
if (isIdentifierNode || isPropertyAccessNode) {
|
|
2113
|
+
const symbol = discovery.checker.getSymbolAtLocation(
|
|
2114
|
+
isIdentifier4(node) ? node : node.name
|
|
2115
|
+
);
|
|
2116
|
+
if (symbol) {
|
|
2117
|
+
resolveAlias(symbol, discovery);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
const type = discovery.checker.getTypeAtLocation(node);
|
|
2121
|
+
return type.isStringLiteral() ? type.value : null;
|
|
2122
|
+
};
|
|
2123
|
+
var metadataProperty = (metadata, name) => {
|
|
2124
|
+
for (const property of metadata.properties) {
|
|
2125
|
+
const isAssignment = isPropertyAssignment3(property);
|
|
2126
|
+
if (!isAssignment) continue;
|
|
2127
|
+
const isIdentifierName = isIdentifier4(property.name);
|
|
2128
|
+
const isStringName = isStringLiteralLike4(property.name);
|
|
2129
|
+
const isNumericName = isNumericLiteral2(property.name);
|
|
2130
|
+
const isLiteralName = isIdentifierName || isStringName || isNumericName;
|
|
2131
|
+
const isPropertyNamed = isLiteralName && property.name.text === name;
|
|
2132
|
+
if (isPropertyNamed) return property.initializer;
|
|
2133
|
+
}
|
|
2134
|
+
return void 0;
|
|
2135
|
+
};
|
|
2136
|
+
var templateOf = (metadata, fileName, discovery) => {
|
|
2137
|
+
const inline = metadataProperty(metadata, "template");
|
|
2138
|
+
if (inline) {
|
|
2139
|
+
const source = stringValue(inline, discovery);
|
|
2140
|
+
if (source === null) return void 0;
|
|
2141
|
+
const inlineTemplate = { kind: "inline", source };
|
|
2142
|
+
return inlineTemplate;
|
|
2143
|
+
}
|
|
2144
|
+
const url = metadataProperty(metadata, "templateUrl");
|
|
2145
|
+
if (url) {
|
|
2146
|
+
const text2 = stringValue(url, discovery);
|
|
2147
|
+
if (text2 === null) return void 0;
|
|
2148
|
+
const externalFileName = resolve2(dirname2(fileName), text2);
|
|
2149
|
+
const externalTemplate = {
|
|
2150
|
+
fileName: externalFileName,
|
|
2151
|
+
kind: "external"
|
|
2152
|
+
};
|
|
2153
|
+
return externalTemplate;
|
|
2154
|
+
}
|
|
2155
|
+
return null;
|
|
2156
|
+
};
|
|
2157
|
+
var exportAsOf = (metadata, discovery) => {
|
|
2158
|
+
const exportAsProperty = metadataProperty(metadata, "exportAs");
|
|
2159
|
+
const exportAs = stringValue(exportAsProperty, discovery) ?? "";
|
|
2160
|
+
const names = exportAs.split(",").map((name) => name.trim());
|
|
2161
|
+
return names.filter(Boolean);
|
|
2162
|
+
};
|
|
2163
|
+
|
|
2164
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-scope.ts
|
|
2165
|
+
var booleanValue = (node) => {
|
|
2166
|
+
if (!node) return void 0;
|
|
2167
|
+
if (node.kind === SyntaxKind4.TrueKeyword) return true;
|
|
2168
|
+
if (node.kind === SyntaxKind4.FalseKeyword) return false;
|
|
2169
|
+
return void 0;
|
|
2170
|
+
};
|
|
2171
|
+
var classesOf = (symbol, discovery) => {
|
|
2172
|
+
const resolved = resolveAlias(symbol, discovery);
|
|
2173
|
+
const declarations = resolved.declarations ?? [];
|
|
2174
|
+
return declarations.filter(isClassLike2);
|
|
2175
|
+
};
|
|
2176
|
+
var scopedClasses = (classes, discovery) => {
|
|
2177
|
+
const scoped = [];
|
|
2178
|
+
for (const declaration of classes) {
|
|
2179
|
+
if (declaration.getSourceFile().isDeclarationFile) continue;
|
|
2180
|
+
const kind = classDecoratorKind(declaration, discovery);
|
|
2181
|
+
if (kind === "Pipe") continue;
|
|
2182
|
+
if (kind !== "Component" && kind !== "Directive") return null;
|
|
2183
|
+
scoped.push(declaration);
|
|
2184
|
+
}
|
|
2185
|
+
return scoped;
|
|
2186
|
+
};
|
|
2187
|
+
var importedClasses = (element, discovery) => {
|
|
2188
|
+
const isIdentifierElement = isIdentifier5(element);
|
|
2189
|
+
const isPropertyAccessElement = isPropertyAccessExpression3(element);
|
|
2190
|
+
if (!isIdentifierElement && !isPropertyAccessElement) return null;
|
|
2191
|
+
const target = isIdentifierElement ? element : element.name;
|
|
2192
|
+
const symbol = discovery.checker.getSymbolAtLocation(target);
|
|
2193
|
+
if (!symbol) return null;
|
|
2194
|
+
const classes = classesOf(symbol, discovery);
|
|
2195
|
+
if (classes.length === 0) return null;
|
|
2196
|
+
return scopedClasses(classes, discovery);
|
|
2197
|
+
};
|
|
2198
|
+
var scopeOf = (metadata, discovery) => {
|
|
2199
|
+
const standalone = booleanValue(metadataProperty(metadata, "standalone"));
|
|
2200
|
+
const imports = metadataProperty(metadata, "imports");
|
|
2201
|
+
const isModuleDeclared = standalone === void 0 && !imports;
|
|
2202
|
+
if (standalone === false || isModuleDeclared) return null;
|
|
2203
|
+
if (!imports) return [];
|
|
2204
|
+
const isImportsArray = isArrayLiteralExpression4(imports);
|
|
2205
|
+
if (!isImportsArray) return null;
|
|
2206
|
+
const scope = [];
|
|
2207
|
+
for (const element of imports.elements) {
|
|
2208
|
+
const classes = importedClasses(element, discovery);
|
|
2209
|
+
if (classes === null) return null;
|
|
2210
|
+
scope.push(...classes);
|
|
2211
|
+
}
|
|
2212
|
+
return scope;
|
|
2213
|
+
};
|
|
2214
|
+
|
|
2215
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/angular/angular-component-discovery.ts
|
|
2216
|
+
var describedClass = (base2, metadata, discovery) => {
|
|
2217
|
+
const selector = metadataProperty(metadata, "selector");
|
|
2218
|
+
const described = {
|
|
2219
|
+
...base2,
|
|
2220
|
+
exportAs: exportAsOf(metadata, discovery),
|
|
2221
|
+
hostDirectives: metadataProperty(metadata, "hostDirectives") !== void 0,
|
|
2222
|
+
selector: stringValue(selector, discovery)
|
|
2223
|
+
};
|
|
2224
|
+
if (!base2.component) {
|
|
2225
|
+
const directiveDiscovery = { ...described, valid: true };
|
|
2226
|
+
return directiveDiscovery;
|
|
2227
|
+
}
|
|
2228
|
+
const fileName = base2.declaration.getSourceFile().fileName;
|
|
2229
|
+
const template = templateOf(metadata, fileName, discovery);
|
|
2230
|
+
const componentDiscovery = {
|
|
2231
|
+
...described,
|
|
2232
|
+
scope: scopeOf(metadata, discovery),
|
|
2233
|
+
template: template ?? null,
|
|
2234
|
+
valid: template !== void 0
|
|
2235
|
+
};
|
|
2236
|
+
return componentDiscovery;
|
|
2237
|
+
};
|
|
2238
|
+
var decoratedClass = (declaration, call, discovery) => {
|
|
2239
|
+
const kind = angularDecoratorKind(call.expression, discovery);
|
|
2240
|
+
if (kind !== "Component" && kind !== "Directive") return null;
|
|
2241
|
+
const component = kind === "Component";
|
|
2242
|
+
const base2 = {
|
|
2243
|
+
component,
|
|
2244
|
+
declaration,
|
|
2245
|
+
exportAs: [],
|
|
2246
|
+
hostDirectives: false,
|
|
2247
|
+
name: declaration.name?.text ?? "",
|
|
2248
|
+
scope: null,
|
|
2249
|
+
selector: null,
|
|
2250
|
+
template: null
|
|
2251
|
+
};
|
|
2252
|
+
const metadata = call.arguments.at(0);
|
|
2253
|
+
if (metadata === void 0) {
|
|
2254
|
+
const bareDiscovery = { ...base2, valid: !component };
|
|
2255
|
+
return bareDiscovery;
|
|
2256
|
+
}
|
|
2257
|
+
const isMetadataObject = isObjectLiteralExpression4(metadata);
|
|
2258
|
+
if (!isMetadataObject) {
|
|
2259
|
+
const invalidDiscovery = { ...base2, valid: false };
|
|
2260
|
+
return invalidDiscovery;
|
|
2261
|
+
}
|
|
2262
|
+
return describedClass(base2, metadata, discovery);
|
|
2263
|
+
};
|
|
2264
|
+
var angularClass = (declaration, discovery) => {
|
|
2265
|
+
const hasDecorators = canHaveDecorators2(declaration);
|
|
2266
|
+
if (!hasDecorators) return null;
|
|
2267
|
+
for (const decorator of getDecorators2(declaration) ?? []) {
|
|
2268
|
+
const isDecoratorCall = isCallExpression2(decorator.expression);
|
|
2269
|
+
if (!isDecoratorCall) continue;
|
|
2270
|
+
const found = decoratedClass(declaration, decorator.expression, discovery);
|
|
2271
|
+
if (found) return found;
|
|
2272
|
+
}
|
|
2273
|
+
return null;
|
|
2274
|
+
};
|
|
2275
|
+
var angularClasses = (sourceFile, checker) => {
|
|
2276
|
+
const classes = [];
|
|
2277
|
+
const discovery = { checker, dependencies: /* @__PURE__ */ new Set() };
|
|
2278
|
+
const visit = (node) => {
|
|
2279
|
+
const isClass = isClassLike3(node);
|
|
2280
|
+
if (isClass) {
|
|
2281
|
+
const found = angularClass(node, discovery);
|
|
2282
|
+
if (found) {
|
|
2283
|
+
classes.push(found);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
forEachChild2(node, visit);
|
|
2287
|
+
};
|
|
2288
|
+
visit(sourceFile);
|
|
2289
|
+
discovery.dependencies.delete(sourceFile);
|
|
2290
|
+
const discovered = {
|
|
2291
|
+
classes,
|
|
2292
|
+
dependencies: discovery.dependencies
|
|
2293
|
+
};
|
|
2294
|
+
return discovered;
|
|
2295
|
+
};
|
|
2296
|
+
|
|
2297
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/utils/spec-file.util.ts
|
|
2298
|
+
var specFilePattern = /\.spec\.[cm]?[tj]sx?$/u;
|
|
2299
|
+
var isSpecFile = (fileName) => {
|
|
2300
|
+
return specFilePattern.test(fileName);
|
|
2301
|
+
};
|
|
2302
|
+
|
|
2303
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-staleness.ts
|
|
2304
|
+
var typeScriptFilePattern = /\.(?:[cm]?ts|tsx)$/u;
|
|
2305
|
+
var isIndexable = (program, sourceFile) => {
|
|
2306
|
+
if (sourceFile.isDeclarationFile) return false;
|
|
2307
|
+
const isExternal = program.isSourceFileFromExternalLibrary(sourceFile);
|
|
2308
|
+
if (isExternal) return false;
|
|
2309
|
+
const isSpec = isSpecFile(sourceFile.fileName);
|
|
2310
|
+
if (isSpec) return false;
|
|
2311
|
+
return typeScriptFilePattern.test(sourceFile.fileName);
|
|
2312
|
+
};
|
|
2313
|
+
var indexableSourceFiles = (program) => {
|
|
2314
|
+
const sourceFiles = program.getSourceFiles();
|
|
2315
|
+
return sourceFiles.filter((sourceFile) => isIndexable(program, sourceFile));
|
|
2316
|
+
};
|
|
2317
|
+
var sourceFileMaps = (program) => {
|
|
2318
|
+
const all = /* @__PURE__ */ new Map();
|
|
2319
|
+
const current = /* @__PURE__ */ new Map();
|
|
2320
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
2321
|
+
all.set(sourceFile.fileName, sourceFile);
|
|
2322
|
+
}
|
|
2323
|
+
for (const sourceFile of indexableSourceFiles(program)) {
|
|
2324
|
+
current.set(sourceFile.fileName, sourceFile);
|
|
2325
|
+
}
|
|
2326
|
+
const maps = { all, current };
|
|
2327
|
+
return maps;
|
|
2328
|
+
};
|
|
2329
|
+
var dropEntries = (index, stale) => {
|
|
2330
|
+
for (const [fileName, entry] of index.entries) {
|
|
2331
|
+
const isStale = stale(entry);
|
|
2332
|
+
if (isStale) {
|
|
2333
|
+
index.entries.delete(fileName);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
};
|
|
2337
|
+
var isReplaced = (sourceFile, dependencies, maps) => {
|
|
2338
|
+
const indexedSourceFile = maps.current.get(sourceFile.fileName);
|
|
2339
|
+
if (indexedSourceFile !== sourceFile) return true;
|
|
2340
|
+
for (const dependency of dependencies) {
|
|
2341
|
+
const indexedDependency = maps.all.get(dependency.fileName);
|
|
2342
|
+
if (indexedDependency !== dependency) return true;
|
|
2343
|
+
}
|
|
2344
|
+
return false;
|
|
2345
|
+
};
|
|
2346
|
+
var dropReplacedEntries = (index, maps) => {
|
|
2347
|
+
const isEntryReplaced = (entry) => {
|
|
2348
|
+
return isReplaced(entry.sourceFile, entry.dependencies, maps);
|
|
2349
|
+
};
|
|
2350
|
+
dropEntries(index, isEntryReplaced);
|
|
2351
|
+
};
|
|
2352
|
+
var dropEntriesMentioning = (index, names) => {
|
|
2353
|
+
if (names.size === 0) return;
|
|
2354
|
+
if (index.entries.size === 0) return;
|
|
2355
|
+
const nameList = [...names];
|
|
2356
|
+
const mentionsName = (entry) => {
|
|
2357
|
+
return nameList.some((name) => entry.sourceFile.text.includes(name));
|
|
2358
|
+
};
|
|
2359
|
+
dropEntries(index, mentionsName);
|
|
2360
|
+
};
|
|
2361
|
+
var templateVersionChecker = () => {
|
|
2362
|
+
const checked = /* @__PURE__ */ new Map();
|
|
2363
|
+
return (version) => {
|
|
2364
|
+
const key2 = `${version.fileName}\0${version.mtimeNs}\0${version.size}`;
|
|
2365
|
+
const known = checked.get(key2);
|
|
2366
|
+
if (known !== void 0) return known;
|
|
2367
|
+
const current = templateFileIsCurrent(version);
|
|
2368
|
+
checked.set(key2, current);
|
|
2369
|
+
return current;
|
|
2370
|
+
};
|
|
2371
|
+
};
|
|
2372
|
+
var dropStaleTemplateEntries = (index, force) => {
|
|
2373
|
+
const now = performance.now();
|
|
2374
|
+
const sinceLastCheck = now - index.templateCheckedAt;
|
|
2375
|
+
const isThrottled = !force && sinceLastCheck < index.templateCheckDuration * 100;
|
|
2376
|
+
if (isThrottled) return;
|
|
2377
|
+
const isCurrent = templateVersionChecker();
|
|
2378
|
+
const hasStaleTemplate = (entry) => {
|
|
2379
|
+
return !entry.templateVersions.every(isCurrent);
|
|
2380
|
+
};
|
|
2381
|
+
dropEntries(index, hasStaleTemplate);
|
|
2382
|
+
index.templateCheckedAt = now;
|
|
2383
|
+
index.templateCheckDuration = performance.now() - now;
|
|
2384
|
+
};
|
|
2385
|
+
|
|
2386
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/typescript/typescript-candidate-names.ts
|
|
2387
|
+
import {
|
|
2388
|
+
canHaveDecorators as canHaveDecorators3,
|
|
2389
|
+
forEachChild as forEachChild3,
|
|
2390
|
+
getDecorators as getDecorators3,
|
|
2391
|
+
isClassLike as isClassLike4,
|
|
2392
|
+
isIdentifier as isIdentifier6,
|
|
2393
|
+
isStringLiteralLike as isStringLiteralLike5
|
|
2394
|
+
} from "typescript";
|
|
2395
|
+
var isDecoratedClass = (node) => {
|
|
2396
|
+
const isDecoratable = canHaveDecorators3(node);
|
|
2397
|
+
if (!isDecoratable) return false;
|
|
2398
|
+
const decorators = getDecorators3(node);
|
|
2399
|
+
const decoratorCount = decorators?.length ?? 0;
|
|
2400
|
+
return decoratorCount > 0;
|
|
2401
|
+
};
|
|
2402
|
+
var addMemberNames = (node, names) => {
|
|
2403
|
+
const isClass = isClassLike4(node);
|
|
2404
|
+
if (!isClass) return;
|
|
2405
|
+
const isDecorated = isDecoratedClass(node);
|
|
2406
|
+
if (!isDecorated) return;
|
|
2407
|
+
for (const member of node.members) {
|
|
2408
|
+
if (!member.name) continue;
|
|
2409
|
+
const isIdentifierName = isIdentifier6(member.name);
|
|
2410
|
+
const isStringName = isStringLiteralLike5(member.name);
|
|
2411
|
+
const isNamedMember = isIdentifierName || isStringName;
|
|
2412
|
+
if (isNamedMember) {
|
|
2413
|
+
names.add(member.name.text);
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
};
|
|
2417
|
+
var collectCandidateNames = (sourceFile) => {
|
|
2418
|
+
const names = /* @__PURE__ */ new Set();
|
|
2419
|
+
const visit = (node) => {
|
|
2420
|
+
addMemberNames(node, names);
|
|
2421
|
+
forEachChild3(node, visit);
|
|
2422
|
+
};
|
|
2423
|
+
visit(sourceFile);
|
|
2424
|
+
return names;
|
|
2425
|
+
};
|
|
2426
|
+
|
|
2427
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-classes.ts
|
|
2428
|
+
var dropReplacedClasses = (index, maps) => {
|
|
2429
|
+
let changed = false;
|
|
2430
|
+
for (const [fileName, fileClasses] of index.classes) {
|
|
2431
|
+
const replaced = isReplaced(
|
|
2432
|
+
fileClasses.sourceFile,
|
|
2433
|
+
fileClasses.dependencies,
|
|
2434
|
+
maps
|
|
2435
|
+
);
|
|
2436
|
+
if (!replaced) continue;
|
|
2437
|
+
index.classes.delete(fileName);
|
|
2438
|
+
changed = true;
|
|
2439
|
+
}
|
|
2440
|
+
return changed;
|
|
2441
|
+
};
|
|
2442
|
+
var addCandidateNames = (index, candidateNames, newNames) => {
|
|
2443
|
+
for (const name of candidateNames) {
|
|
2444
|
+
const isKnownName = index.candidateNames.has(name);
|
|
2445
|
+
if (isKnownName) continue;
|
|
2446
|
+
index.candidateNames.add(name);
|
|
2447
|
+
newNames.add(name);
|
|
2448
|
+
}
|
|
2449
|
+
};
|
|
2450
|
+
var indexFileClasses = (index, sourceFile, checker, newNames) => {
|
|
2451
|
+
const candidateNames = collectCandidateNames(sourceFile);
|
|
2452
|
+
addCandidateNames(index, candidateNames, newNames);
|
|
2453
|
+
const { classes, dependencies } = angularClasses(sourceFile, checker());
|
|
2454
|
+
const fileClasses = {
|
|
2455
|
+
candidateNames,
|
|
2456
|
+
classes,
|
|
2457
|
+
dependencies,
|
|
2458
|
+
sourceFile
|
|
2459
|
+
};
|
|
2460
|
+
index.classes.set(sourceFile.fileName, fileClasses);
|
|
2461
|
+
index.entries.delete(sourceFile.fileName);
|
|
2462
|
+
};
|
|
2463
|
+
var indexNewClasses = (index, current, checker, newNames) => {
|
|
2464
|
+
let changed = false;
|
|
2465
|
+
for (const [fileName, sourceFile] of current) {
|
|
2466
|
+
const isIndexed = index.classes.has(fileName);
|
|
2467
|
+
if (isIndexed) continue;
|
|
2468
|
+
indexFileClasses(index, sourceFile, checker, newNames);
|
|
2469
|
+
changed = true;
|
|
2470
|
+
}
|
|
2471
|
+
return changed;
|
|
2472
|
+
};
|
|
2473
|
+
var rebuildDirectives = (index) => {
|
|
2474
|
+
const fileClasses = [...index.classes.values()];
|
|
2475
|
+
const classes = fileClasses.flatMap((entry) => entry.classes);
|
|
2476
|
+
const shape = directiveShape(classes);
|
|
2477
|
+
index.directives = buildDirectiveIndex(classes);
|
|
2478
|
+
if (shape === index.directiveShape) return;
|
|
2479
|
+
index.directiveShape = shape;
|
|
2480
|
+
dropEntries(index, (entry) => entry.usedDirectiveIndex);
|
|
2481
|
+
};
|
|
2482
|
+
var reconcileClasses = (index, maps, checker) => {
|
|
2483
|
+
const newNames = /* @__PURE__ */ new Set();
|
|
2484
|
+
const droppedClasses = dropReplacedClasses(index, maps);
|
|
2485
|
+
const addedClasses = indexNewClasses(index, maps.current, checker, newNames);
|
|
2486
|
+
const changed = droppedClasses || addedClasses;
|
|
2487
|
+
if (changed) {
|
|
2488
|
+
rebuildDirectives(index);
|
|
2489
|
+
}
|
|
2490
|
+
return newNames;
|
|
2491
|
+
};
|
|
2492
|
+
|
|
2493
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-index-reconciliation.ts
|
|
2494
|
+
var lazyChecker = (program) => {
|
|
2495
|
+
let typeChecker;
|
|
2496
|
+
return () => typeChecker ??= program.getTypeChecker();
|
|
2497
|
+
};
|
|
2498
|
+
var indexMissingEntries = (index, sourceFiles, checker) => {
|
|
2499
|
+
for (const sourceFile of sourceFiles) {
|
|
2500
|
+
const isEntryIndexed = index.entries.has(sourceFile.fileName);
|
|
2501
|
+
if (isEntryIndexed) continue;
|
|
2502
|
+
const entry = computeEntry(index, sourceFile, checker());
|
|
2503
|
+
index.entries.set(sourceFile.fileName, entry);
|
|
2504
|
+
}
|
|
2505
|
+
};
|
|
2506
|
+
var reindexProgram = (index, program, checker) => {
|
|
2507
|
+
const maps = sourceFileMaps(program);
|
|
2508
|
+
dropReplacedEntries(index, maps);
|
|
2509
|
+
const newNames = reconcileClasses(index, maps, checker);
|
|
2510
|
+
dropEntriesMentioning(index, newNames);
|
|
2511
|
+
index.program = program;
|
|
2512
|
+
index.usage = void 0;
|
|
2513
|
+
dropStaleTemplateEntries(index, true);
|
|
2514
|
+
indexMissingEntries(index, maps.current.values(), checker);
|
|
2515
|
+
};
|
|
2516
|
+
var refreshTemplateEntries = (index, program, checker) => {
|
|
2517
|
+
const entryCount = index.entries.size;
|
|
2518
|
+
dropStaleTemplateEntries(index, false);
|
|
2519
|
+
const isUnchanged = index.entries.size === entryCount;
|
|
2520
|
+
if (isUnchanged) return;
|
|
2521
|
+
index.usage = void 0;
|
|
2522
|
+
indexMissingEntries(index, indexableSourceFiles(program), checker);
|
|
2523
|
+
};
|
|
2524
|
+
var reconcile = (index, program) => {
|
|
2525
|
+
const checker = lazyChecker(program);
|
|
2526
|
+
const isNewProgram = index.program !== program;
|
|
2527
|
+
if (isNewProgram) {
|
|
2528
|
+
reindexProgram(index, program, checker);
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
refreshTemplateEntries(index, program, checker);
|
|
2532
|
+
};
|
|
2533
|
+
|
|
2534
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/project-usage/project-usage.ts
|
|
2535
|
+
var indexes = /* @__PURE__ */ new Map();
|
|
2536
|
+
var memberName = (node) => {
|
|
2537
|
+
const isMember = isClassElement(node);
|
|
2538
|
+
if (!isMember) return null;
|
|
2539
|
+
if (!node.name) return null;
|
|
2540
|
+
const isIdentifierName = isIdentifier7(node.name);
|
|
2541
|
+
const isStringName = isStringLiteralLike6(node.name);
|
|
2542
|
+
const isNamedMember = isIdentifierName || isStringName;
|
|
2543
|
+
if (!isNamedMember) return null;
|
|
2544
|
+
return node.name.text;
|
|
2545
|
+
};
|
|
2546
|
+
var usageOf = (index) => {
|
|
2547
|
+
if (index.usage !== void 0) return index.usage;
|
|
2548
|
+
const declarations = /* @__PURE__ */ new Set();
|
|
2549
|
+
const fallbackNames = /* @__PURE__ */ new Set();
|
|
2550
|
+
for (const entry of index.entries.values()) {
|
|
2551
|
+
for (const declaration of entry.declarations) {
|
|
2552
|
+
declarations.add(declaration);
|
|
2553
|
+
}
|
|
2554
|
+
for (const name of entry.fallbackNames) {
|
|
2555
|
+
fallbackNames.add(name);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
index.usage = {
|
|
2559
|
+
has: (node) => {
|
|
2560
|
+
const hasDeclaration = declarations.has(node);
|
|
2561
|
+
if (hasDeclaration) return true;
|
|
2562
|
+
if (fallbackNames.size === 0) return false;
|
|
2563
|
+
const name = memberName(node);
|
|
2564
|
+
return name !== null && fallbackNames.has(name);
|
|
2565
|
+
}
|
|
2566
|
+
};
|
|
2567
|
+
return index.usage;
|
|
2568
|
+
};
|
|
2569
|
+
var createIndex = () => {
|
|
2570
|
+
const emptyIndex = {
|
|
2571
|
+
candidateNames: /* @__PURE__ */ new Set(),
|
|
2572
|
+
classes: /* @__PURE__ */ new Map(),
|
|
2573
|
+
directives: buildDirectiveIndex([]),
|
|
2574
|
+
directiveShape: "",
|
|
2575
|
+
entries: /* @__PURE__ */ new Map(),
|
|
2576
|
+
program: null,
|
|
2577
|
+
templateCheckDuration: 0,
|
|
2578
|
+
templateCheckedAt: 0,
|
|
2579
|
+
usage: void 0
|
|
2580
|
+
};
|
|
2581
|
+
return emptyIndex;
|
|
2582
|
+
};
|
|
2583
|
+
var projectUsageIsCurrent = (program) => {
|
|
2584
|
+
const configFilePath = program.getCompilerOptions()["configFilePath"];
|
|
2585
|
+
return typeof configFilePath === "string" && indexes.get(configFilePath)?.program === program;
|
|
2586
|
+
};
|
|
2587
|
+
var projectUsage = (program) => {
|
|
2588
|
+
const configFilePath = program.getCompilerOptions()["configFilePath"];
|
|
2589
|
+
if (typeof configFilePath !== "string") return null;
|
|
2590
|
+
let index = indexes.get(configFilePath);
|
|
2591
|
+
if (!index) {
|
|
2592
|
+
index = createIndex();
|
|
2593
|
+
indexes.set(configFilePath, index);
|
|
2594
|
+
}
|
|
2595
|
+
try {
|
|
2596
|
+
reconcile(index, program);
|
|
2597
|
+
return usageOf(index);
|
|
2598
|
+
} catch {
|
|
2599
|
+
indexes.delete(configFilePath);
|
|
2600
|
+
return null;
|
|
2601
|
+
}
|
|
2602
|
+
};
|
|
2603
|
+
|
|
2604
|
+
// packages/lint-suite/src/lib/rules/no-unused-angular-instance-fields/rule/no-unused-angular-instance-fields.ts
|
|
2605
|
+
var docs = {
|
|
2606
|
+
description: "Disallow unread instance fields and methods in Angular components and directives"
|
|
2607
|
+
};
|
|
2608
|
+
var messages = {
|
|
2609
|
+
unusedField: "Angular instance field '{{name}}' is never read.",
|
|
2610
|
+
unusedMethod: "Angular instance method '{{name}}' is never read."
|
|
2611
|
+
};
|
|
2612
|
+
var allowEffectFieldsSchema = {
|
|
2613
|
+
type: "boolean",
|
|
2614
|
+
description: "Allow Angular effect() fields with automatic cleanup."
|
|
2615
|
+
};
|
|
2616
|
+
var analysisSchema = {
|
|
2617
|
+
type: "string",
|
|
2618
|
+
enum: ["local", "project"],
|
|
2619
|
+
description: "Choose local-file or typed whole-project analysis."
|
|
2620
|
+
};
|
|
2621
|
+
var properties = {
|
|
2622
|
+
allowEffectFields: allowEffectFieldsSchema,
|
|
2623
|
+
analysis: analysisSchema
|
|
2624
|
+
};
|
|
2625
|
+
var optionsSchema = {
|
|
2626
|
+
type: "object",
|
|
2627
|
+
properties,
|
|
2628
|
+
additionalProperties: false
|
|
2629
|
+
};
|
|
2630
|
+
var schema = [optionsSchema];
|
|
2631
|
+
var meta = {
|
|
2632
|
+
type: "problem",
|
|
2633
|
+
docs,
|
|
2634
|
+
schema,
|
|
2635
|
+
messages
|
|
2636
|
+
};
|
|
2637
|
+
var ruleDefaults = {
|
|
2638
|
+
allowEffectFields: false,
|
|
2639
|
+
analysis: "local"
|
|
2640
|
+
};
|
|
2641
|
+
var defaultOptions = [ruleDefaults];
|
|
2642
|
+
var createRule = ESLintUtils.RuleCreator(
|
|
2643
|
+
() => "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
|
|
2644
|
+
);
|
|
2645
|
+
var projectParserServices = (context, analysis) => {
|
|
2646
|
+
if (analysis !== "project") return void 0;
|
|
2647
|
+
try {
|
|
2648
|
+
return ESLintUtils.getParserServices(context);
|
|
2649
|
+
} catch {
|
|
2650
|
+
throw new Error(
|
|
2651
|
+
"Project analysis requires parser services with type information."
|
|
2652
|
+
);
|
|
2653
|
+
}
|
|
2654
|
+
};
|
|
2655
|
+
var projectMemberUsedFor = (parserServices) => {
|
|
2656
|
+
if (parserServices === void 0) return void 0;
|
|
2657
|
+
let usage;
|
|
2658
|
+
const memberUsed = (node) => {
|
|
2659
|
+
if (usage === void 0) {
|
|
2660
|
+
usage = projectUsage(parserServices.program);
|
|
2661
|
+
}
|
|
2662
|
+
return usage?.has(parserServices.esTreeNodeToTSNodeMap.get(node)) ?? true;
|
|
2663
|
+
};
|
|
2664
|
+
return memberUsed;
|
|
2665
|
+
};
|
|
2666
|
+
var no_unused_angular_instance_fields_default = createRule({
|
|
2667
|
+
name: "no-unused-instance-fields",
|
|
2668
|
+
meta,
|
|
2669
|
+
defaultOptions,
|
|
2670
|
+
create(context, [options]) {
|
|
2671
|
+
const analysis = options.analysis ?? "local";
|
|
2672
|
+
const isSpec = isSpecFile(context.filename);
|
|
2673
|
+
const noListeners = {};
|
|
2674
|
+
if (analysis === "project" && isSpec) return noListeners;
|
|
2675
|
+
const parserServices = projectParserServices(context, analysis);
|
|
2676
|
+
const imports = angularClassImports(context.sourceCode.ast);
|
|
2677
|
+
if (!imports) return noListeners;
|
|
2678
|
+
const classes = [];
|
|
2679
|
+
const stack = [];
|
|
2680
|
+
const dynamicClasses = /* @__PURE__ */ new Set();
|
|
2681
|
+
const projectIndexed = () => {
|
|
2682
|
+
if (parserServices === void 0) return false;
|
|
2683
|
+
return projectUsageIsCurrent(parserServices.program);
|
|
2684
|
+
};
|
|
2685
|
+
const reportOptions = {
|
|
2686
|
+
allowEffectFields: options.allowEffectFields ?? false,
|
|
2687
|
+
classes,
|
|
2688
|
+
context,
|
|
2689
|
+
dynamicClasses,
|
|
2690
|
+
imports,
|
|
2691
|
+
projectIndexed,
|
|
2692
|
+
projectMemberUsed: projectMemberUsedFor(parserServices)
|
|
2693
|
+
};
|
|
2694
|
+
const readListeners = classReadVisitor(classes, stack, dynamicClasses);
|
|
2695
|
+
const listeners = {
|
|
2696
|
+
...readListeners,
|
|
2697
|
+
"Program:exit"() {
|
|
2698
|
+
reportUnusedMembers(reportOptions);
|
|
2699
|
+
}
|
|
2700
|
+
};
|
|
2701
|
+
return listeners;
|
|
2702
|
+
}
|
|
2703
|
+
});
|
|
2704
|
+
|
|
2705
|
+
// packages/lint-suite/src/lib/angular.ts
|
|
2706
|
+
var lintSuiteAngularPlugin = definePlugin("lint-suite-angular", {
|
|
2707
|
+
"no-unused-instance-fields": no_unused_angular_instance_fields_default
|
|
2708
|
+
});
|
|
2709
|
+
var angular = defineConfig2([
|
|
2710
|
+
{
|
|
2711
|
+
files: ["**/*.ts"],
|
|
2712
|
+
extends: [...configs2.tsRecommended],
|
|
2713
|
+
plugins: {
|
|
2714
|
+
"lint-suite-angular": lintSuiteAngularPlugin
|
|
2715
|
+
},
|
|
2716
|
+
languageOptions: {
|
|
2717
|
+
globals: {
|
|
2718
|
+
...globals.browser,
|
|
2719
|
+
...globals.es2025,
|
|
2720
|
+
...globals.node
|
|
2721
|
+
},
|
|
2722
|
+
parserOptions: {
|
|
2723
|
+
projectService: true
|
|
2724
|
+
}
|
|
2725
|
+
},
|
|
2726
|
+
processor: processInlineTemplates,
|
|
2727
|
+
rules: {
|
|
2728
|
+
"lint-suite-angular/no-unused-instance-fields": [
|
|
2729
|
+
"error",
|
|
2730
|
+
{ analysis: "project" }
|
|
2731
|
+
],
|
|
2732
|
+
"@angular-eslint/prefer-on-push-component-change-detection": "error",
|
|
2733
|
+
// v22: flags only explicit opt-out of OnPush (omission now means OnPush)
|
|
2734
|
+
"@angular-eslint/prefer-signals": "error",
|
|
2735
|
+
"@angular-eslint/use-injectable-provided-in": "error",
|
|
2736
|
+
"@angular-eslint/prefer-signal-model": "error",
|
|
2737
|
+
"@angular-eslint/no-uncalled-signals": "error",
|
|
2738
|
+
"@angular-eslint/prefer-host-metadata-property": "error",
|
|
2739
|
+
"@angular-eslint/no-implicit-take-until-destroyed": "error",
|
|
2740
|
+
// v21.2.0: Enforce explicit DestroyRef
|
|
2741
|
+
"@angular-eslint/no-async-lifecycle-method": "error",
|
|
2742
|
+
// Prevent async lifecycle hooks
|
|
2743
|
+
"@angular-eslint/prefer-output-readonly": "warn",
|
|
2744
|
+
// Prevent accidental reassignment
|
|
2745
|
+
"@angular-eslint/prefer-output-emitter-ref": "warn",
|
|
2746
|
+
// Modern output() function over @Output()
|
|
2747
|
+
"@angular-eslint/sort-lifecycle-methods": "warn",
|
|
2748
|
+
// Consistent lifecycle method ordering
|
|
2749
|
+
"@angular-eslint/computed-must-return": "error",
|
|
2750
|
+
// Catch silent computed() bugs missing a return
|
|
2751
|
+
"@angular-eslint/no-duplicates-in-metadata-arrays": "error",
|
|
2752
|
+
// Catch duplicate imports/providers in @Component/@NgModule
|
|
2753
|
+
"@angular-eslint/no-lifecycle-call": "error",
|
|
2754
|
+
// Forbid manual this.ngOnInit() calls
|
|
2755
|
+
"@angular-eslint/require-lifecycle-on-prototype": "error",
|
|
2756
|
+
// Lifecycle hooks must be methods, not arrow-fn fields
|
|
2757
|
+
"@angular-eslint/relative-url-prefix": "error",
|
|
2758
|
+
// Require ./ in templateUrl/styleUrl for portability
|
|
2759
|
+
"@angular-eslint/use-component-selector": "error",
|
|
2760
|
+
// All components must declare a selector
|
|
2761
|
+
"@angular-eslint/no-developer-preview": "warn",
|
|
2762
|
+
// Flag APIs marked @developerPreview (no breaking-change policy)
|
|
2763
|
+
"@angular-eslint/no-experimental": "warn"
|
|
2764
|
+
// Flag APIs marked @experimental
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
]);
|
|
2768
|
+
|
|
2769
|
+
// packages/lint-suite/src/lib/base.ts
|
|
2770
|
+
import { defineConfig as defineConfig6 } from "eslint/config";
|
|
2771
|
+
|
|
2772
|
+
// packages/lint-suite/src/lib/base-limits.ts
|
|
2773
|
+
import { defineConfig as defineConfig3 } from "eslint/config";
|
|
2774
|
+
var baseLimits = defineConfig3([
|
|
2775
|
+
{
|
|
2776
|
+
files: [
|
|
2777
|
+
"**/*.js",
|
|
2778
|
+
"**/*.jsx",
|
|
2779
|
+
"**/*.cjs",
|
|
2780
|
+
"**/*.mjs",
|
|
2781
|
+
"**/*.ts",
|
|
2782
|
+
"**/*.tsx",
|
|
2783
|
+
"**/*.cts",
|
|
2784
|
+
"**/*.mts",
|
|
2785
|
+
"**/*.html"
|
|
2786
|
+
],
|
|
2787
|
+
rules: {
|
|
2788
|
+
"max-lines": [
|
|
2789
|
+
"error",
|
|
2790
|
+
{
|
|
2791
|
+
max: 150,
|
|
2792
|
+
skipBlankLines: true,
|
|
2793
|
+
skipComments: true
|
|
2794
|
+
}
|
|
2795
|
+
]
|
|
2796
|
+
}
|
|
2797
|
+
},
|
|
2798
|
+
{
|
|
2799
|
+
files: [
|
|
2800
|
+
"**/*.spec.ts",
|
|
2801
|
+
"**/*.test.ts",
|
|
2802
|
+
"**/*.spec.js",
|
|
2803
|
+
"**/*.test.js",
|
|
2804
|
+
"**/*.e2e.ts",
|
|
2805
|
+
"**/*.e2e.js"
|
|
2806
|
+
],
|
|
2807
|
+
rules: {
|
|
2808
|
+
"max-lines-per-function": "off",
|
|
2809
|
+
complexity: ["warn", { max: 3 }],
|
|
2810
|
+
"max-lines": [
|
|
2811
|
+
"error",
|
|
2812
|
+
{
|
|
2813
|
+
max: 300,
|
|
2814
|
+
skipBlankLines: true,
|
|
2815
|
+
skipComments: true
|
|
2816
|
+
}
|
|
2817
|
+
]
|
|
2818
|
+
}
|
|
2819
|
+
},
|
|
2820
|
+
{
|
|
2821
|
+
files: ["**/*.action.ts"],
|
|
2822
|
+
rules: {
|
|
2823
|
+
"max-classes-per-file": "off"
|
|
2824
|
+
}
|
|
2825
|
+
},
|
|
2826
|
+
{
|
|
2827
|
+
files: ["**/*.state.ts"],
|
|
2828
|
+
rules: {
|
|
2829
|
+
"class-methods-use-this": "off"
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
]);
|
|
2833
|
+
|
|
2834
|
+
// packages/lint-suite/src/lib/base-practices.ts
|
|
2835
|
+
import { defineConfig as defineConfig4 } from "eslint/config";
|
|
2836
|
+
var basePractices = defineConfig4([
|
|
2837
|
+
{
|
|
2838
|
+
files: [
|
|
2839
|
+
"**/*.js",
|
|
2840
|
+
"**/*.jsx",
|
|
2841
|
+
"**/*.cjs",
|
|
2842
|
+
"**/*.mjs",
|
|
2843
|
+
"**/*.ts",
|
|
2844
|
+
"**/*.tsx",
|
|
2845
|
+
"**/*.cts",
|
|
2846
|
+
"**/*.mts"
|
|
2847
|
+
],
|
|
2848
|
+
rules: {
|
|
2849
|
+
"no-param-reassign": "error",
|
|
2850
|
+
"no-alert": "error",
|
|
2851
|
+
"no-console": ["warn", { allow: ["warn", "error"] }],
|
|
2852
|
+
"no-debugger": "error",
|
|
2853
|
+
eqeqeq: ["error", "always", { null: "ignore" }],
|
|
2854
|
+
curly: ["error", "multi-line"],
|
|
2855
|
+
"prefer-const": ["error", { destructuring: "all" }],
|
|
2856
|
+
"no-var": "error",
|
|
2857
|
+
"object-shorthand": "error",
|
|
2858
|
+
"prefer-arrow-callback": "error",
|
|
2859
|
+
"no-useless-concat": "error",
|
|
2860
|
+
"prefer-template": "error",
|
|
2861
|
+
"no-nested-ternary": "error",
|
|
2862
|
+
"no-useless-assignment": "error",
|
|
2863
|
+
// NEW: Catch unused assignments (ESLint v9)
|
|
2864
|
+
"no-object-constructor": "error",
|
|
2865
|
+
// Disallow new Object() without arguments
|
|
2866
|
+
"no-else-return": ["error", { allowElseIf: false }],
|
|
2867
|
+
// Prefer early returns
|
|
2868
|
+
"no-multi-assign": "error",
|
|
2869
|
+
// Disallow chaining assignments
|
|
2870
|
+
radix: "error",
|
|
2871
|
+
// Require radix parameter for parseInt()
|
|
2872
|
+
yoda: "error",
|
|
2873
|
+
// Disallow yoda conditions (`if ("red" === color)`)
|
|
2874
|
+
complexity: ["warn", 10],
|
|
2875
|
+
// Warn if function complexity is high
|
|
2876
|
+
"max-depth": ["warn", 4],
|
|
2877
|
+
// Warn on deeply nested blocks
|
|
2878
|
+
"max-lines-per-function": [
|
|
2879
|
+
"warn",
|
|
2880
|
+
{ max: 50, skipBlankLines: true, skipComments: true }
|
|
2881
|
+
],
|
|
2882
|
+
// Warn on long functions
|
|
2883
|
+
"max-params": ["warn", 4],
|
|
807
2884
|
// Warn on functions with many parameters
|
|
808
2885
|
"no-lonely-if": "error",
|
|
809
2886
|
// Disallow if statements as the only statement in an else block
|
|
@@ -883,7 +2960,16 @@ var base = defineConfig3([
|
|
|
883
2960
|
}
|
|
884
2961
|
]
|
|
885
2962
|
}
|
|
886
|
-
}
|
|
2963
|
+
}
|
|
2964
|
+
]);
|
|
2965
|
+
|
|
2966
|
+
// packages/lint-suite/src/lib/base-recommended.ts
|
|
2967
|
+
import { configs as configs3 } from "@nx/eslint-plugin";
|
|
2968
|
+
import stylistic from "@stylistic/eslint-plugin";
|
|
2969
|
+
import { defineConfig as defineConfig5 } from "eslint/config";
|
|
2970
|
+
import importPluginX from "eslint-plugin-import-x";
|
|
2971
|
+
var baseRecommended = defineConfig5([
|
|
2972
|
+
{ extends: [...configs3["flat/base"]] },
|
|
887
2973
|
{
|
|
888
2974
|
files: [
|
|
889
2975
|
"**/*.js",
|
|
@@ -893,68 +2979,161 @@ var base = defineConfig3([
|
|
|
893
2979
|
"**/*.ts",
|
|
894
2980
|
"**/*.tsx",
|
|
895
2981
|
"**/*.cts",
|
|
896
|
-
"**/*.mts"
|
|
897
|
-
"**/*.html"
|
|
2982
|
+
"**/*.mts"
|
|
898
2983
|
],
|
|
899
|
-
|
|
900
|
-
"max-lines": [
|
|
901
|
-
"error",
|
|
902
|
-
{
|
|
903
|
-
max: 150,
|
|
904
|
-
skipBlankLines: true,
|
|
905
|
-
skipComments: true
|
|
906
|
-
}
|
|
907
|
-
]
|
|
908
|
-
}
|
|
2984
|
+
extends: [importPluginX.flatConfigs.recommended]
|
|
909
2985
|
},
|
|
910
2986
|
{
|
|
911
2987
|
files: [
|
|
912
|
-
"**/*.
|
|
913
|
-
"**/*.
|
|
914
|
-
"**/*.
|
|
915
|
-
"**/*.
|
|
916
|
-
"**/*.
|
|
917
|
-
"**/*.
|
|
2988
|
+
"**/*.js",
|
|
2989
|
+
"**/*.jsx",
|
|
2990
|
+
"**/*.cjs",
|
|
2991
|
+
"**/*.mjs",
|
|
2992
|
+
"**/*.ts",
|
|
2993
|
+
"**/*.tsx",
|
|
2994
|
+
"**/*.cts",
|
|
2995
|
+
"**/*.mts"
|
|
2996
|
+
],
|
|
2997
|
+
extends: [stylistic.configs.recommended]
|
|
2998
|
+
}
|
|
2999
|
+
]);
|
|
3000
|
+
|
|
3001
|
+
// packages/lint-suite/src/lib/base.ts
|
|
3002
|
+
var base = defineConfig6([
|
|
3003
|
+
...baseRecommended,
|
|
3004
|
+
...basePractices,
|
|
3005
|
+
...baseLimits
|
|
3006
|
+
]);
|
|
3007
|
+
|
|
3008
|
+
// packages/lint-suite/src/lib/boundaries.ts
|
|
3009
|
+
import { defineConfig as defineConfig7 } from "eslint/config";
|
|
3010
|
+
import boundariesPlugin from "eslint-plugin-boundaries";
|
|
3011
|
+
var boundaries = defineConfig7([
|
|
3012
|
+
{
|
|
3013
|
+
files: [
|
|
3014
|
+
"**/*.js",
|
|
3015
|
+
"**/*.jsx",
|
|
3016
|
+
"**/*.ts",
|
|
3017
|
+
"**/*.tsx",
|
|
3018
|
+
"**/*.cts",
|
|
3019
|
+
"**/*.mts"
|
|
918
3020
|
],
|
|
3021
|
+
plugins: {
|
|
3022
|
+
boundaries: boundariesPlugin
|
|
3023
|
+
},
|
|
3024
|
+
settings: {
|
|
3025
|
+
"boundaries/elements": [
|
|
3026
|
+
{
|
|
3027
|
+
type: "feature",
|
|
3028
|
+
pattern: "**/feature/**"
|
|
3029
|
+
},
|
|
3030
|
+
{
|
|
3031
|
+
type: "data-access",
|
|
3032
|
+
pattern: "**/data-access/**"
|
|
3033
|
+
},
|
|
3034
|
+
{
|
|
3035
|
+
type: "domain-logic",
|
|
3036
|
+
pattern: "**/domain-logic/**"
|
|
3037
|
+
},
|
|
3038
|
+
{
|
|
3039
|
+
type: "ui",
|
|
3040
|
+
pattern: "**/ui/**"
|
|
3041
|
+
},
|
|
3042
|
+
{
|
|
3043
|
+
type: "common",
|
|
3044
|
+
pattern: "**/common/**"
|
|
3045
|
+
},
|
|
3046
|
+
{
|
|
3047
|
+
type: "utils",
|
|
3048
|
+
pattern: "**/utils/**"
|
|
3049
|
+
}
|
|
3050
|
+
],
|
|
3051
|
+
"import/resolver": {
|
|
3052
|
+
typescript: {
|
|
3053
|
+
alwaysTryTypes: true
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
},
|
|
919
3057
|
rules: {
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
"max-lines": [
|
|
3058
|
+
...boundariesPlugin.configs.recommended.rules,
|
|
3059
|
+
"boundaries/dependencies": [
|
|
923
3060
|
"error",
|
|
924
3061
|
{
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
3062
|
+
default: "disallow",
|
|
3063
|
+
message: "{{to.element.types}} is not allowed to be imported in {{from.element.types}}",
|
|
3064
|
+
policies: [
|
|
3065
|
+
{
|
|
3066
|
+
from: { element: { type: "feature" } },
|
|
3067
|
+
allow: [
|
|
3068
|
+
{ to: { element: { type: "feature" } } },
|
|
3069
|
+
{ to: { element: { type: "ui" } } },
|
|
3070
|
+
{ to: { element: { type: "domain-logic" } } },
|
|
3071
|
+
{ to: { element: { type: "utils" } } },
|
|
3072
|
+
{ to: { element: { type: "common" } } }
|
|
3073
|
+
]
|
|
3074
|
+
},
|
|
3075
|
+
{
|
|
3076
|
+
from: { element: { type: "domain-logic" } },
|
|
3077
|
+
allow: [
|
|
3078
|
+
{ to: { element: { type: "domain-logic" } } },
|
|
3079
|
+
{ to: { element: { type: "data-access" } } },
|
|
3080
|
+
{ to: { element: { type: "utils" } } },
|
|
3081
|
+
{ to: { element: { type: "common" } } }
|
|
3082
|
+
]
|
|
3083
|
+
},
|
|
3084
|
+
{
|
|
3085
|
+
from: { element: { type: "data-access" } },
|
|
3086
|
+
allow: [
|
|
3087
|
+
{ to: { element: { type: "data-access" } } },
|
|
3088
|
+
{ to: { element: { type: "utils" } } },
|
|
3089
|
+
{ to: { element: { type: "common" } } }
|
|
3090
|
+
]
|
|
3091
|
+
},
|
|
3092
|
+
{
|
|
3093
|
+
from: { element: { type: "ui" } },
|
|
3094
|
+
allow: [
|
|
3095
|
+
{ to: { element: { type: "ui" } } },
|
|
3096
|
+
{ to: { element: { type: "utils" } } },
|
|
3097
|
+
{ to: { element: { type: "common" } } }
|
|
3098
|
+
]
|
|
3099
|
+
},
|
|
3100
|
+
{
|
|
3101
|
+
from: { element: { type: "utils" } },
|
|
3102
|
+
allow: [
|
|
3103
|
+
{ to: { element: { type: "utils" } } },
|
|
3104
|
+
{ to: { element: { type: "common" } } }
|
|
3105
|
+
]
|
|
3106
|
+
},
|
|
3107
|
+
{
|
|
3108
|
+
from: { element: { type: "common" } },
|
|
3109
|
+
allow: [{ to: { element: { type: "common" } } }]
|
|
3110
|
+
}
|
|
3111
|
+
]
|
|
928
3112
|
}
|
|
929
3113
|
]
|
|
930
3114
|
}
|
|
931
3115
|
},
|
|
932
3116
|
{
|
|
933
|
-
files: ["**/*.
|
|
934
|
-
rules: {
|
|
935
|
-
"max-classes-per-file": "off"
|
|
936
|
-
}
|
|
937
|
-
},
|
|
938
|
-
{
|
|
939
|
-
files: ["**/*.state.ts"],
|
|
3117
|
+
files: ["**/*.stories.ts"],
|
|
940
3118
|
rules: {
|
|
941
|
-
"
|
|
3119
|
+
"boundaries/dependencies": "off"
|
|
942
3120
|
}
|
|
943
3121
|
}
|
|
944
3122
|
]);
|
|
945
3123
|
|
|
946
3124
|
// packages/lint-suite/src/lib/javascript.ts
|
|
947
3125
|
import { configs as configs4 } from "@nx/eslint-plugin";
|
|
948
|
-
import { defineConfig as
|
|
949
|
-
var
|
|
3126
|
+
import { defineConfig as defineConfig8 } from "eslint/config";
|
|
3127
|
+
var nxJavascript = configs4["flat/javascript"];
|
|
3128
|
+
var javascript = defineConfig8({
|
|
950
3129
|
files: ["**/*.js", "**/*.jsx", "**/*.cjs", "**/*.mjs"],
|
|
951
|
-
extends:
|
|
3130
|
+
extends: nxJavascript
|
|
952
3131
|
});
|
|
953
3132
|
|
|
954
3133
|
// packages/lint-suite/src/lib/json.ts
|
|
955
3134
|
import jsonPlugin from "@eslint/json";
|
|
956
|
-
import { defineConfig as
|
|
957
|
-
var json =
|
|
3135
|
+
import { defineConfig as defineConfig9 } from "eslint/config";
|
|
3136
|
+
var json = defineConfig9([
|
|
958
3137
|
{
|
|
959
3138
|
plugins: { json: jsonPlugin }
|
|
960
3139
|
},
|
|
@@ -965,15 +3144,40 @@ var json = defineConfig5([
|
|
|
965
3144
|
}
|
|
966
3145
|
]);
|
|
967
3146
|
|
|
3147
|
+
// packages/lint-suite/src/lib/playwright.ts
|
|
3148
|
+
import { defineConfig as defineConfig10 } from "eslint/config";
|
|
3149
|
+
import playwrightEslint from "eslint-plugin-playwright";
|
|
3150
|
+
var playwright = defineConfig10([
|
|
3151
|
+
{
|
|
3152
|
+
...playwrightEslint.configs["flat/recommended"],
|
|
3153
|
+
files: ["**/*.e2e.ts", "**/*.e2e.js"],
|
|
3154
|
+
rules: {
|
|
3155
|
+
...playwrightEslint.configs["flat/recommended"].rules,
|
|
3156
|
+
// Locator best practices
|
|
3157
|
+
"playwright/prefer-locator": "error",
|
|
3158
|
+
"playwright/prefer-native-locators": "error",
|
|
3159
|
+
"playwright/no-nth-methods": "warn",
|
|
3160
|
+
// Matcher improvements
|
|
3161
|
+
"playwright/prefer-to-be": "error",
|
|
3162
|
+
"playwright/prefer-to-have-count": "error",
|
|
3163
|
+
"playwright/prefer-to-have-length": "error",
|
|
3164
|
+
"playwright/prefer-comparison-matcher": "error",
|
|
3165
|
+
// Test structure
|
|
3166
|
+
"playwright/prefer-hooks-on-top": "error",
|
|
3167
|
+
"playwright/require-top-level-describe": "warn"
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
]);
|
|
3171
|
+
|
|
968
3172
|
// packages/lint-suite/src/lib/prettier.ts
|
|
3173
|
+
import { defineConfig as defineConfig11 } from "eslint/config";
|
|
969
3174
|
import eslintConfigPrettier from "eslint-config-prettier";
|
|
970
|
-
|
|
971
|
-
var prettier = defineConfig6([eslintConfigPrettier]);
|
|
3175
|
+
var prettier = defineConfig11([eslintConfigPrettier]);
|
|
972
3176
|
|
|
973
3177
|
// packages/lint-suite/src/lib/rxjs.ts
|
|
974
3178
|
import rxjsEslint from "@smarttools/eslint-plugin-rxjs";
|
|
975
|
-
import { defineConfig as
|
|
976
|
-
var rxjs =
|
|
3179
|
+
import { defineConfig as defineConfig12 } from "eslint/config";
|
|
3180
|
+
var rxjs = defineConfig12([
|
|
977
3181
|
{
|
|
978
3182
|
files: [
|
|
979
3183
|
"**/*.js",
|
|
@@ -1048,9 +3252,9 @@ var rxjs = defineConfig7([
|
|
|
1048
3252
|
]);
|
|
1049
3253
|
|
|
1050
3254
|
// packages/lint-suite/src/lib/storybook.ts
|
|
3255
|
+
import { defineConfig as defineConfig13 } from "eslint/config";
|
|
1051
3256
|
import storybookEslint from "eslint-plugin-storybook";
|
|
1052
|
-
|
|
1053
|
-
var storybook = defineConfig8([
|
|
3257
|
+
var storybook = defineConfig13([
|
|
1054
3258
|
{
|
|
1055
3259
|
ignores: ["!.storybook"],
|
|
1056
3260
|
files: ["**/*.stories.ts"],
|
|
@@ -1065,263 +3269,14 @@ var storybook = defineConfig8([
|
|
|
1065
3269
|
]);
|
|
1066
3270
|
|
|
1067
3271
|
// packages/lint-suite/src/lib/typescript.ts
|
|
1068
|
-
import {
|
|
1069
|
-
import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
|
|
1070
|
-
import importPluginX2 from "eslint-plugin-import-x";
|
|
1071
|
-
import { defineConfig as defineConfig9 } from "eslint/config";
|
|
1072
|
-
|
|
1073
|
-
// packages/lint-suite/src/lib/rules/explicit-accessibility.ts
|
|
1074
|
-
import { ESLintUtils as ESLintUtils2, TSESTree as TSESTree5 } from "@typescript-eslint/utils";
|
|
1075
|
-
var createRule2 = ESLintUtils2.RuleCreator(
|
|
1076
|
-
() => "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
|
|
1077
|
-
);
|
|
1078
|
-
var defaultOptions = {
|
|
1079
|
-
defaultAccessibility: "private"
|
|
1080
|
-
};
|
|
1081
|
-
var getNodeName = (node) => {
|
|
1082
|
-
if (!node) {
|
|
1083
|
-
return "<unknown>";
|
|
1084
|
-
}
|
|
1085
|
-
if ("key" in node && node.key) {
|
|
1086
|
-
if (node.key.type === TSESTree5.AST_NODE_TYPES.Identifier) {
|
|
1087
|
-
return node.key.name;
|
|
1088
|
-
}
|
|
1089
|
-
if (node.key.type === TSESTree5.AST_NODE_TYPES.PrivateIdentifier) {
|
|
1090
|
-
return `#${node.key.name}`;
|
|
1091
|
-
}
|
|
1092
|
-
return "<computed/complex key>";
|
|
1093
|
-
}
|
|
1094
|
-
if ("kind" in node && node.kind === "constructor") {
|
|
1095
|
-
return "constructor";
|
|
1096
|
-
}
|
|
1097
|
-
if (node.type === TSESTree5.AST_NODE_TYPES.TSParameterProperty) {
|
|
1098
|
-
const param = node.parameter;
|
|
1099
|
-
if (param.type === TSESTree5.AST_NODE_TYPES.Identifier) {
|
|
1100
|
-
return param.name;
|
|
1101
|
-
}
|
|
1102
|
-
if (param.type === TSESTree5.AST_NODE_TYPES.AssignmentPattern) {
|
|
1103
|
-
if (param.left.type === TSESTree5.AST_NODE_TYPES.Identifier) {
|
|
1104
|
-
return param.left.name;
|
|
1105
|
-
}
|
|
1106
|
-
return "<destructured parameter>";
|
|
1107
|
-
}
|
|
1108
|
-
return "<complex parameter>";
|
|
1109
|
-
}
|
|
1110
|
-
return "<unknown>";
|
|
1111
|
-
};
|
|
1112
|
-
var explicit_accessibility_default = createRule2({
|
|
1113
|
-
name: "explicit-accessibility",
|
|
1114
|
-
meta: {
|
|
1115
|
-
type: "suggestion",
|
|
1116
|
-
docs: {
|
|
1117
|
-
description: "Require explicit accessibility modifiers on class members"
|
|
1118
|
-
},
|
|
1119
|
-
fixable: "code",
|
|
1120
|
-
schema: [
|
|
1121
|
-
{
|
|
1122
|
-
type: "object",
|
|
1123
|
-
properties: {
|
|
1124
|
-
defaultAccessibility: {
|
|
1125
|
-
type: "string",
|
|
1126
|
-
enum: ["public", "private", "protected"],
|
|
1127
|
-
description: "The accessibility modifier to add if one is missing."
|
|
1128
|
-
}
|
|
1129
|
-
},
|
|
1130
|
-
additionalProperties: false
|
|
1131
|
-
}
|
|
1132
|
-
],
|
|
1133
|
-
messages: {
|
|
1134
|
-
missingAccessibility: "Class member '{{ name }}' is missing an explicit accessibility modifier (public/private/protected)."
|
|
1135
|
-
}
|
|
1136
|
-
},
|
|
1137
|
-
defaultOptions: [defaultOptions],
|
|
1138
|
-
create(context, [options]) {
|
|
1139
|
-
const { defaultAccessibility } = { ...defaultOptions, ...options };
|
|
1140
|
-
const sourceCode = context.sourceCode;
|
|
1141
|
-
function checkNode(node) {
|
|
1142
|
-
if (node.accessibility) {
|
|
1143
|
-
return;
|
|
1144
|
-
}
|
|
1145
|
-
let reportNode = node;
|
|
1146
|
-
const name = getNodeName(node);
|
|
1147
|
-
if (node.type === TSESTree5.AST_NODE_TYPES.MethodDefinition || node.type === TSESTree5.AST_NODE_TYPES.PropertyDefinition) {
|
|
1148
|
-
reportNode = node.key;
|
|
1149
|
-
} else {
|
|
1150
|
-
const param = node.parameter;
|
|
1151
|
-
if (param.type === TSESTree5.AST_NODE_TYPES.Identifier) {
|
|
1152
|
-
reportNode = param;
|
|
1153
|
-
} else if (param.type === TSESTree5.AST_NODE_TYPES.AssignmentPattern && param.left.type === TSESTree5.AST_NODE_TYPES.Identifier) {
|
|
1154
|
-
reportNode = param.left;
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
context.report({
|
|
1158
|
-
node: reportNode ?? node,
|
|
1159
|
-
messageId: "missingAccessibility",
|
|
1160
|
-
data: { name },
|
|
1161
|
-
fix(fixer) {
|
|
1162
|
-
let insertText = `${defaultAccessibility} `;
|
|
1163
|
-
let tokenToInsertBefore;
|
|
1164
|
-
if (node.decorators.length > 0) {
|
|
1165
|
-
const lastDecorator = node.decorators[node.decorators.length - 1];
|
|
1166
|
-
tokenToInsertBefore = sourceCode.getTokenAfter(lastDecorator);
|
|
1167
|
-
} else {
|
|
1168
|
-
tokenToInsertBefore = sourceCode.getFirstToken(node);
|
|
1169
|
-
}
|
|
1170
|
-
if (node.type === TSESTree5.AST_NODE_TYPES.TSParameterProperty && node.readonly) {
|
|
1171
|
-
const firstToken = sourceCode.getFirstToken(node);
|
|
1172
|
-
if (firstToken?.value === "readonly") {
|
|
1173
|
-
tokenToInsertBefore = firstToken;
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
if (!tokenToInsertBefore) return null;
|
|
1177
|
-
const tokenAfterInsertion = sourceCode.getTokenAfter(
|
|
1178
|
-
tokenToInsertBefore,
|
|
1179
|
-
{ includeComments: false }
|
|
1180
|
-
);
|
|
1181
|
-
const textBetween = sourceCode.text.slice(
|
|
1182
|
-
tokenToInsertBefore.range[1],
|
|
1183
|
-
tokenAfterInsertion?.range[0]
|
|
1184
|
-
);
|
|
1185
|
-
if (!/^\s/.test(textBetween)) {
|
|
1186
|
-
insertText = ` ${insertText}`;
|
|
1187
|
-
}
|
|
1188
|
-
return fixer.insertTextBefore(tokenToInsertBefore, insertText);
|
|
1189
|
-
}
|
|
1190
|
-
});
|
|
1191
|
-
}
|
|
1192
|
-
return {
|
|
1193
|
-
MethodDefinition(node) {
|
|
1194
|
-
checkNode(node);
|
|
1195
|
-
},
|
|
1196
|
-
PropertyDefinition(node) {
|
|
1197
|
-
checkNode(node);
|
|
1198
|
-
},
|
|
1199
|
-
TSParameterProperty(node) {
|
|
1200
|
-
checkNode(node);
|
|
1201
|
-
}
|
|
1202
|
-
};
|
|
1203
|
-
}
|
|
1204
|
-
});
|
|
3272
|
+
import { defineConfig as defineConfig19 } from "eslint/config";
|
|
1205
3273
|
|
|
1206
|
-
// packages/lint-suite/src/lib/typescript.ts
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
"explicit-accessibility": explicit_accessibility_default
|
|
1210
|
-
}
|
|
1211
|
-
};
|
|
1212
|
-
var typescript = defineConfig9([
|
|
1213
|
-
// NOTE: Checks for 'prettier' and 'eslint-plugin-prettier'
|
|
1214
|
-
{
|
|
1215
|
-
files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
|
|
1216
|
-
extends: [
|
|
1217
|
-
importPluginX2.flatConfigs.typescript,
|
|
1218
|
-
...configs5["flat/typescript"]
|
|
1219
|
-
]
|
|
1220
|
-
},
|
|
3274
|
+
// packages/lint-suite/src/lib/typescript-consistency.ts
|
|
3275
|
+
import { defineConfig as defineConfig14 } from "eslint/config";
|
|
3276
|
+
var typescriptConsistency = defineConfig14([
|
|
1221
3277
|
{
|
|
1222
3278
|
files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
|
|
1223
|
-
plugins: { local: localTypescriptRules },
|
|
1224
3279
|
rules: {
|
|
1225
|
-
"local/explicit-accessibility": "error",
|
|
1226
|
-
"@typescript-eslint/explicit-member-accessibility": "error",
|
|
1227
|
-
"@typescript-eslint/explicit-function-return-type": "error",
|
|
1228
|
-
"@typescript-eslint/no-empty-function": "error",
|
|
1229
|
-
"@typescript-eslint/no-unused-vars": "error",
|
|
1230
|
-
"@typescript-eslint/no-use-before-define": "error",
|
|
1231
|
-
// A. Eliminating `any` (already covered by strict-type-checked, but explicit for clarity)
|
|
1232
|
-
"@typescript-eslint/no-explicit-any": "error",
|
|
1233
|
-
"@typescript-eslint/no-unsafe-assignment": "error",
|
|
1234
|
-
"@typescript-eslint/no-unsafe-call": "error",
|
|
1235
|
-
"@typescript-eslint/no-unsafe-member-access": "error",
|
|
1236
|
-
"@typescript-eslint/no-unsafe-return": "error",
|
|
1237
|
-
"@typescript-eslint/no-unsafe-argument": "error",
|
|
1238
|
-
// New v8 type-safety rules (replacements for deprecated ban-types)
|
|
1239
|
-
"@typescript-eslint/no-empty-object-type": "error",
|
|
1240
|
-
// Bans confusing {} type
|
|
1241
|
-
"@typescript-eslint/no-unsafe-function-type": "error",
|
|
1242
|
-
// Bans Function type
|
|
1243
|
-
"@typescript-eslint/no-wrapper-object-types": "error",
|
|
1244
|
-
// Bans Number, String, Boolean wrappers
|
|
1245
|
-
// B. Explicit Annotations (partially covered, explicit module boundaries is key)
|
|
1246
|
-
"@typescript-eslint/explicit-module-boundary-types": "error",
|
|
1247
|
-
// Consider enabling if maximum explicitness is desired, even if verbose:
|
|
1248
|
-
// C. Null/Undefined Safety (Requires strictNullChecks)
|
|
1249
|
-
"@typescript-eslint/no-non-null-assertion": "error",
|
|
1250
|
-
"@typescript-eslint/prefer-nullish-coalescing": "error",
|
|
1251
|
-
"@typescript-eslint/prefer-optional-chain": "error",
|
|
1252
|
-
// D. Promise Handling (Supplementing plugin:promise/recommended)
|
|
1253
|
-
"@typescript-eslint/no-floating-promises": [
|
|
1254
|
-
"error",
|
|
1255
|
-
{ ignoreVoid: true }
|
|
1256
|
-
],
|
|
1257
|
-
"@typescript-eslint/no-misused-promises": "warn",
|
|
1258
|
-
// Already in recommended-type-checked
|
|
1259
|
-
"@typescript-eslint/promise-function-async": "error",
|
|
1260
|
-
"@typescript-eslint/await-thenable": "error",
|
|
1261
|
-
// Already in recommended-type-checked
|
|
1262
|
-
// E. Best Practices & Potential Errors (Some covered by presets, fine-tuning here)
|
|
1263
|
-
"@typescript-eslint/no-inferrable-types": "warn",
|
|
1264
|
-
// Keep as warn or disable if inference is preferred
|
|
1265
|
-
"@typescript-eslint/no-require-imports": "error",
|
|
1266
|
-
// Use ES modules (replaces deprecated no-var-requires)
|
|
1267
|
-
"@typescript-eslint/only-throw-error": "error",
|
|
1268
|
-
// Replaces deprecated no-throw-literal
|
|
1269
|
-
"@typescript-eslint/no-array-delete": "error",
|
|
1270
|
-
// Prevents delete arr[0]
|
|
1271
|
-
"@typescript-eslint/return-await": ["error", "in-try-catch"],
|
|
1272
|
-
// Better stack traces in try/catch
|
|
1273
|
-
"@typescript-eslint/require-await": "error",
|
|
1274
|
-
"@typescript-eslint/restrict-template-expressions": [
|
|
1275
|
-
"error",
|
|
1276
|
-
{ allowNumber: true, allowBoolean: true }
|
|
1277
|
-
],
|
|
1278
|
-
// Allow numbers/booleans in templates
|
|
1279
|
-
"@typescript-eslint/restrict-plus-operands": "error",
|
|
1280
|
-
// Enforce consistent types for '+'
|
|
1281
|
-
"@typescript-eslint/prefer-as-const": "error",
|
|
1282
|
-
"@typescript-eslint/no-shadow": "error",
|
|
1283
|
-
"@typescript-eslint/switch-exhaustiveness-check": "error",
|
|
1284
|
-
// Ensure switch on unions is exhaustive
|
|
1285
|
-
"@typescript-eslint/no-unused-expressions": [
|
|
1286
|
-
"error",
|
|
1287
|
-
{ allowShortCircuit: true, allowTernary: true }
|
|
1288
|
-
],
|
|
1289
|
-
"@typescript-eslint/no-useless-constructor": "error",
|
|
1290
|
-
"@typescript-eslint/dot-notation": ["error", { allowKeywords: true }],
|
|
1291
|
-
// Prefer dot notation where possible
|
|
1292
|
-
// Strict type-checked rules
|
|
1293
|
-
"@typescript-eslint/no-unnecessary-condition": "error",
|
|
1294
|
-
// Catches always-true/false conditions
|
|
1295
|
-
"@typescript-eslint/no-confusing-void-expression": [
|
|
1296
|
-
"error",
|
|
1297
|
-
{ ignoreArrowShorthand: true, ignoreVoidOperator: true }
|
|
1298
|
-
],
|
|
1299
|
-
// Prevent confusing void usage
|
|
1300
|
-
"@typescript-eslint/no-dynamic-delete": "error",
|
|
1301
|
-
// Use Map instead of delete obj[key]
|
|
1302
|
-
"@typescript-eslint/no-extraneous-class": [
|
|
1303
|
-
"error",
|
|
1304
|
-
{ allowWithDecorator: true }
|
|
1305
|
-
],
|
|
1306
|
-
// Allow Angular classes
|
|
1307
|
-
"@typescript-eslint/no-invalid-void-type": "error",
|
|
1308
|
-
// Restrict void to return types
|
|
1309
|
-
"@typescript-eslint/unified-signatures": "error",
|
|
1310
|
-
// Merge overloads where possible
|
|
1311
|
-
"@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
|
|
1312
|
-
// Forbids foo! ?? bar
|
|
1313
|
-
// Stylistic type-checked rules
|
|
1314
|
-
"@typescript-eslint/prefer-find": "error",
|
|
1315
|
-
// Use find() instead of filter()[0]
|
|
1316
|
-
"@typescript-eslint/prefer-for-of": "error",
|
|
1317
|
-
// Modern iteration
|
|
1318
|
-
"@typescript-eslint/prefer-includes": "error",
|
|
1319
|
-
// indexOf() → includes()
|
|
1320
|
-
"@typescript-eslint/prefer-string-starts-ends-with": "error",
|
|
1321
|
-
// Modern string methods
|
|
1322
|
-
"@typescript-eslint/prefer-regexp-exec": "error",
|
|
1323
|
-
// Better regex performance
|
|
1324
|
-
// F. Consistency & Style (Some covered by stylistic-type-checked)
|
|
1325
3280
|
"@typescript-eslint/consistent-type-assertions": [
|
|
1326
3281
|
"error",
|
|
1327
3282
|
{ assertionStyle: "as", objectLiteralTypeAssertions: "never" }
|
|
@@ -1334,10 +3289,14 @@ var typescript = defineConfig9([
|
|
|
1334
3289
|
"@typescript-eslint/naming-convention": [
|
|
1335
3290
|
"error",
|
|
1336
3291
|
{
|
|
1337
|
-
selector: "objectLiteralProperty",
|
|
3292
|
+
selector: ["objectLiteralProperty", "objectLiteralMethod"],
|
|
1338
3293
|
modifiers: ["requiresQuotes"],
|
|
1339
3294
|
format: null
|
|
1340
3295
|
},
|
|
3296
|
+
{
|
|
3297
|
+
selector: "objectLiteralMethod",
|
|
3298
|
+
format: ["camelCase", "PascalCase"]
|
|
3299
|
+
},
|
|
1341
3300
|
{
|
|
1342
3301
|
selector: "objectLiteralProperty",
|
|
1343
3302
|
format: ["camelCase", "snake_case", "PascalCase"]
|
|
@@ -1360,7 +3319,28 @@ var typescript = defineConfig9([
|
|
|
1360
3319
|
selector: "property",
|
|
1361
3320
|
format: ["camelCase", "PascalCase", "snake_case"]
|
|
1362
3321
|
}
|
|
1363
|
-
]
|
|
3322
|
+
]
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
]);
|
|
3326
|
+
|
|
3327
|
+
// packages/lint-suite/src/lib/typescript-imports.ts
|
|
3328
|
+
import { defineConfig as defineConfig15 } from "eslint/config";
|
|
3329
|
+
import { createTypeScriptImportResolver } from "eslint-import-resolver-typescript";
|
|
3330
|
+
var pathGroups = [
|
|
3331
|
+
{ pattern: "@angular/**", group: "builtin", position: "before" },
|
|
3332
|
+
{ pattern: "@nestjs/**", group: "builtin", position: "before" },
|
|
3333
|
+
{ pattern: "@shared/**", group: "internal", position: "before" },
|
|
3334
|
+
{ pattern: "@backend/**", group: "internal", position: "before" },
|
|
3335
|
+
{ pattern: "@frontend/**", group: "internal", position: "before" },
|
|
3336
|
+
{ pattern: "@*/shared/**", group: "internal", position: "before" },
|
|
3337
|
+
{ pattern: "@*/frontend/**", group: "internal", position: "after" },
|
|
3338
|
+
{ pattern: "@*/backend/**", group: "internal", position: "after" }
|
|
3339
|
+
];
|
|
3340
|
+
var typescriptImports = defineConfig15([
|
|
3341
|
+
{
|
|
3342
|
+
files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
|
|
3343
|
+
rules: {
|
|
1364
3344
|
"import-x/order": [
|
|
1365
3345
|
"error",
|
|
1366
3346
|
{
|
|
@@ -1372,53 +3352,17 @@ var typescript = defineConfig9([
|
|
|
1372
3352
|
"index",
|
|
1373
3353
|
"unknown"
|
|
1374
3354
|
],
|
|
1375
|
-
pathGroups
|
|
1376
|
-
{
|
|
1377
|
-
pattern: "@angular/**",
|
|
1378
|
-
group: "builtin",
|
|
1379
|
-
position: "before"
|
|
1380
|
-
},
|
|
1381
|
-
{
|
|
1382
|
-
pattern: "@nestjs/**",
|
|
1383
|
-
group: "builtin",
|
|
1384
|
-
position: "before"
|
|
1385
|
-
},
|
|
1386
|
-
{
|
|
1387
|
-
pattern: "@shared/**",
|
|
1388
|
-
group: "internal",
|
|
1389
|
-
position: "before"
|
|
1390
|
-
},
|
|
1391
|
-
{
|
|
1392
|
-
pattern: "@backend/**",
|
|
1393
|
-
group: "internal",
|
|
1394
|
-
position: "before"
|
|
1395
|
-
},
|
|
1396
|
-
{
|
|
1397
|
-
pattern: "@frontend/**",
|
|
1398
|
-
group: "internal",
|
|
1399
|
-
position: "before"
|
|
1400
|
-
},
|
|
1401
|
-
{
|
|
1402
|
-
pattern: "@*/shared/**",
|
|
1403
|
-
group: "internal",
|
|
1404
|
-
position: "before"
|
|
1405
|
-
},
|
|
1406
|
-
{
|
|
1407
|
-
pattern: "@*/frontend/**",
|
|
1408
|
-
group: "internal",
|
|
1409
|
-
position: "after"
|
|
1410
|
-
},
|
|
1411
|
-
{
|
|
1412
|
-
pattern: "@*/backend/**",
|
|
1413
|
-
group: "internal",
|
|
1414
|
-
position: "after"
|
|
1415
|
-
}
|
|
1416
|
-
],
|
|
3355
|
+
pathGroups,
|
|
1417
3356
|
pathGroupsExcludedImportTypes: ["builtin"],
|
|
1418
3357
|
"newlines-between": "always",
|
|
1419
3358
|
alphabetize: { order: "asc", caseInsensitive: true }
|
|
1420
3359
|
}
|
|
1421
3360
|
],
|
|
3361
|
+
// TypeScript already checks these four; import-x would re-parse every import with a parser resolved by name, which fails under pnpm.
|
|
3362
|
+
"import-x/namespace": "off",
|
|
3363
|
+
"import-x/default": "off",
|
|
3364
|
+
"import-x/no-named-as-default": "off",
|
|
3365
|
+
"import-x/no-named-as-default-member": "off",
|
|
1422
3366
|
"import-x/no-unresolved": "error",
|
|
1423
3367
|
"import-x/no-duplicates": "error",
|
|
1424
3368
|
"import-x/prefer-default-export": "off",
|
|
@@ -1427,13 +3371,9 @@ var typescript = defineConfig9([
|
|
|
1427
3371
|
"import-x/first": "error",
|
|
1428
3372
|
"import-x/no-mutable-exports": "error",
|
|
1429
3373
|
"import-x/no-self-import": "error",
|
|
1430
|
-
// Prevents modules importing themselves
|
|
1431
3374
|
"import-x/no-cycle": ["error", { maxDepth: 2, ignoreExternal: true }],
|
|
1432
|
-
// Prevents circular dependencies
|
|
1433
3375
|
"import-x/consistent-type-specifier-style": ["error", "prefer-top-level"],
|
|
1434
|
-
// Consistent type imports
|
|
1435
3376
|
"import-x/no-anonymous-default-export": "error"
|
|
1436
|
-
// Improve code searchability
|
|
1437
3377
|
},
|
|
1438
3378
|
settings: {
|
|
1439
3379
|
"import-x/resolver-next": [
|
|
@@ -1442,7 +3382,12 @@ var typescript = defineConfig9([
|
|
|
1442
3382
|
})
|
|
1443
3383
|
]
|
|
1444
3384
|
}
|
|
1445
|
-
}
|
|
3385
|
+
}
|
|
3386
|
+
]);
|
|
3387
|
+
|
|
3388
|
+
// packages/lint-suite/src/lib/typescript-overrides.ts
|
|
3389
|
+
import { defineConfig as defineConfig16 } from "eslint/config";
|
|
3390
|
+
var typescriptOverrides = defineConfig16([
|
|
1446
3391
|
{
|
|
1447
3392
|
files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
|
|
1448
3393
|
languageOptions: {
|
|
@@ -1475,23 +3420,402 @@ var typescript = defineConfig9([
|
|
|
1475
3420
|
"@typescript-eslint/unbound-method": "off",
|
|
1476
3421
|
"@typescript-eslint/consistent-type-assertions": "off"
|
|
1477
3422
|
}
|
|
1478
|
-
},
|
|
3423
|
+
},
|
|
3424
|
+
{
|
|
3425
|
+
files: ["**/*.stories.ts"],
|
|
3426
|
+
rules: {
|
|
3427
|
+
"@typescript-eslint/consistent-type-assertions": "off",
|
|
3428
|
+
"@typescript-eslint/naming-convention": "off"
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
]);
|
|
3432
|
+
|
|
3433
|
+
// packages/lint-suite/src/lib/typescript-recommended.ts
|
|
3434
|
+
import { configs as configs5 } from "@nx/eslint-plugin";
|
|
3435
|
+
import { defineConfig as defineConfig17 } from "eslint/config";
|
|
3436
|
+
import importPluginX2 from "eslint-plugin-import-x";
|
|
3437
|
+
var nxTypescript = configs5["flat/typescript"];
|
|
3438
|
+
var typescriptRecommended = defineConfig17([
|
|
3439
|
+
{
|
|
3440
|
+
files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
|
|
3441
|
+
extends: [importPluginX2.flatConfigs.typescript, ...nxTypescript]
|
|
3442
|
+
}
|
|
3443
|
+
]);
|
|
3444
|
+
|
|
3445
|
+
// packages/lint-suite/src/lib/typescript-safety.ts
|
|
3446
|
+
import { defineConfig as defineConfig18 } from "eslint/config";
|
|
3447
|
+
|
|
3448
|
+
// packages/lint-suite/src/lib/rules/explicit-accessibility/explicit-accessibility.ts
|
|
3449
|
+
import { ESLintUtils as ESLintUtils2, TSESTree as TSESTree10 } from "@typescript-eslint/utils";
|
|
3450
|
+
|
|
3451
|
+
// packages/lint-suite/src/lib/rules/explicit-accessibility/common/explicit-accessibility.const.ts
|
|
3452
|
+
var accessibilities = [
|
|
3453
|
+
"public",
|
|
3454
|
+
"private",
|
|
3455
|
+
"protected"
|
|
3456
|
+
];
|
|
3457
|
+
var docs2 = {
|
|
3458
|
+
description: "Require explicit accessibility modifiers on class members"
|
|
3459
|
+
};
|
|
3460
|
+
var messages2 = {
|
|
3461
|
+
missingAccessibility: "Class member '{{ name }}' is missing an explicit accessibility modifier (public/private/protected).",
|
|
3462
|
+
setAccessibility: "Add the '{{ accessibility }}' modifier."
|
|
3463
|
+
};
|
|
3464
|
+
var defaultAccessibilitySchema = {
|
|
3465
|
+
type: "string",
|
|
3466
|
+
enum: ["public", "private", "protected", "none"],
|
|
3467
|
+
description: "The accessibility modifier inserted by the auto-fix; constructors always get public. 'none' reports without an auto-fix and offers all three levels as suggestions."
|
|
3468
|
+
};
|
|
3469
|
+
var properties2 = {
|
|
3470
|
+
defaultAccessibility: defaultAccessibilitySchema
|
|
3471
|
+
};
|
|
3472
|
+
var optionsSchema2 = {
|
|
3473
|
+
type: "object",
|
|
3474
|
+
properties: properties2,
|
|
3475
|
+
additionalProperties: false
|
|
3476
|
+
};
|
|
3477
|
+
var schema2 = [optionsSchema2];
|
|
3478
|
+
var meta2 = {
|
|
3479
|
+
type: "suggestion",
|
|
3480
|
+
docs: docs2,
|
|
3481
|
+
fixable: "code",
|
|
3482
|
+
hasSuggestions: true,
|
|
3483
|
+
schema: schema2,
|
|
3484
|
+
messages: messages2
|
|
3485
|
+
};
|
|
3486
|
+
var defaultOptions2 = [{ defaultAccessibility: "public" }];
|
|
3487
|
+
|
|
3488
|
+
// packages/lint-suite/src/lib/rules/explicit-accessibility/explicit-accessibility.ts
|
|
3489
|
+
var createRule2 = ESLintUtils2.RuleCreator(
|
|
3490
|
+
() => "https://github.com/F0rty-Tw0/lint-suite#explicit-accessibility"
|
|
3491
|
+
);
|
|
3492
|
+
var isPrivateMember = (node) => {
|
|
3493
|
+
if (node.type === TSESTree10.AST_NODE_TYPES.TSParameterProperty) return false;
|
|
3494
|
+
return node.key.type === TSESTree10.AST_NODE_TYPES.PrivateIdentifier;
|
|
3495
|
+
};
|
|
3496
|
+
var isConstructor = (node) => {
|
|
3497
|
+
if (node.type !== TSESTree10.AST_NODE_TYPES.MethodDefinition) return false;
|
|
3498
|
+
return node.kind === "constructor";
|
|
3499
|
+
};
|
|
3500
|
+
var reportTarget = (node) => {
|
|
3501
|
+
if (node.type !== TSESTree10.AST_NODE_TYPES.TSParameterProperty) {
|
|
3502
|
+
return node.key;
|
|
3503
|
+
}
|
|
3504
|
+
const { parameter } = node;
|
|
3505
|
+
if (parameter.type === TSESTree10.AST_NODE_TYPES.AssignmentPattern) {
|
|
3506
|
+
return parameter.left;
|
|
3507
|
+
}
|
|
3508
|
+
return parameter;
|
|
3509
|
+
};
|
|
3510
|
+
var memberName2 = (target, sourceCode) => {
|
|
3511
|
+
if (target.type === TSESTree10.AST_NODE_TYPES.Identifier) return target.name;
|
|
3512
|
+
return sourceCode.getText(target);
|
|
3513
|
+
};
|
|
3514
|
+
var modifierToken = (node, sourceCode) => {
|
|
3515
|
+
const lastDecorator = node.decorators.at(-1);
|
|
3516
|
+
if (!lastDecorator) return sourceCode.getFirstToken(node);
|
|
3517
|
+
return sourceCode.getTokenAfter(lastDecorator);
|
|
3518
|
+
};
|
|
3519
|
+
var insertModifier = (node, accessibility, sourceCode) => {
|
|
3520
|
+
const token = modifierToken(node, sourceCode);
|
|
3521
|
+
if (!token) return () => null;
|
|
3522
|
+
const lastDecorator = node.decorators.at(-1);
|
|
3523
|
+
const adjacentToDecorator = lastDecorator?.range[1] === token.range[0];
|
|
3524
|
+
const text2 = `${adjacentToDecorator ? " " : ""}${accessibility} `;
|
|
3525
|
+
return (fixer) => fixer.insertTextBefore(token, text2);
|
|
3526
|
+
};
|
|
3527
|
+
var autoFixOf = (node, accessibility, sourceCode) => {
|
|
3528
|
+
if (accessibility === "none") {
|
|
3529
|
+
const noAutoFix = {};
|
|
3530
|
+
return noAutoFix;
|
|
3531
|
+
}
|
|
3532
|
+
const fix = insertModifier(node, accessibility, sourceCode);
|
|
3533
|
+
const autoFix = { fix };
|
|
3534
|
+
return autoFix;
|
|
3535
|
+
};
|
|
3536
|
+
var suggestionOf = (node, accessibility, sourceCode) => {
|
|
3537
|
+
const data = { accessibility };
|
|
3538
|
+
const fix = insertModifier(node, accessibility, sourceCode);
|
|
3539
|
+
const suggestion = {
|
|
3540
|
+
messageId: "setAccessibility",
|
|
3541
|
+
data,
|
|
3542
|
+
fix
|
|
3543
|
+
};
|
|
3544
|
+
return suggestion;
|
|
3545
|
+
};
|
|
3546
|
+
var suggestionsOf = (node, fixAccessibility, sourceCode) => {
|
|
3547
|
+
const isOtherAccessibility = (accessibility) => {
|
|
3548
|
+
return accessibility !== fixAccessibility;
|
|
3549
|
+
};
|
|
3550
|
+
const suggestionFor = (accessibility) => {
|
|
3551
|
+
return suggestionOf(node, accessibility, sourceCode);
|
|
3552
|
+
};
|
|
3553
|
+
return accessibilities.filter(isOtherAccessibility).map(suggestionFor);
|
|
3554
|
+
};
|
|
3555
|
+
var explicit_accessibility_default = createRule2({
|
|
3556
|
+
name: "explicit-accessibility",
|
|
3557
|
+
meta: meta2,
|
|
3558
|
+
defaultOptions: defaultOptions2,
|
|
3559
|
+
create(context, [{ defaultAccessibility = "public" }]) {
|
|
3560
|
+
const { sourceCode } = context;
|
|
3561
|
+
const check = (node) => {
|
|
3562
|
+
const isPrivate = isPrivateMember(node);
|
|
3563
|
+
if (node.accessibility || isPrivate) return;
|
|
3564
|
+
const target = reportTarget(node);
|
|
3565
|
+
const isConstructorMember = isConstructor(node);
|
|
3566
|
+
const usesPublic = isConstructorMember && defaultAccessibility !== "none";
|
|
3567
|
+
const fixAccessibility = usesPublic ? "public" : defaultAccessibility;
|
|
3568
|
+
const autoFix = autoFixOf(node, fixAccessibility, sourceCode);
|
|
3569
|
+
const name = memberName2(target, sourceCode);
|
|
3570
|
+
const data = { name };
|
|
3571
|
+
const suggest = suggestionsOf(node, fixAccessibility, sourceCode);
|
|
3572
|
+
const report = {
|
|
3573
|
+
node: target,
|
|
3574
|
+
messageId: "missingAccessibility",
|
|
3575
|
+
data,
|
|
3576
|
+
...autoFix,
|
|
3577
|
+
suggest
|
|
3578
|
+
};
|
|
3579
|
+
context.report(report);
|
|
3580
|
+
};
|
|
3581
|
+
const listeners = {
|
|
3582
|
+
AccessorProperty: check,
|
|
3583
|
+
MethodDefinition: check,
|
|
3584
|
+
PropertyDefinition: check,
|
|
3585
|
+
TSAbstractAccessorProperty: check,
|
|
3586
|
+
TSAbstractMethodDefinition: check,
|
|
3587
|
+
TSAbstractPropertyDefinition: check,
|
|
3588
|
+
TSParameterProperty: check
|
|
3589
|
+
};
|
|
3590
|
+
return listeners;
|
|
3591
|
+
}
|
|
3592
|
+
});
|
|
3593
|
+
|
|
3594
|
+
// packages/lint-suite/src/lib/rules/no-inline-object-types/no-inline-object-types.ts
|
|
3595
|
+
import { ESLintUtils as ESLintUtils3, TSESTree as TSESTree11 } from "@typescript-eslint/utils";
|
|
3596
|
+
var docs3 = {
|
|
3597
|
+
description: "Require nested object types inside a type alias to be extracted to a named type alias"
|
|
3598
|
+
};
|
|
3599
|
+
var messages3 = {
|
|
3600
|
+
inlineObjectType: "Inline object type must be extracted to a named type alias."
|
|
3601
|
+
};
|
|
3602
|
+
var meta3 = {
|
|
3603
|
+
type: "suggestion",
|
|
3604
|
+
docs: docs3,
|
|
3605
|
+
schema: [],
|
|
3606
|
+
messages: messages3
|
|
3607
|
+
};
|
|
3608
|
+
var createRule3 = ESLintUtils3.RuleCreator(
|
|
3609
|
+
() => "https://github.com/F0rty-Tw0/lint-suite#no-inline-object-types"
|
|
3610
|
+
);
|
|
3611
|
+
var enclosingTypeAlias = (node) => {
|
|
3612
|
+
let current = node.parent;
|
|
3613
|
+
while (current && current.type !== TSESTree11.AST_NODE_TYPES.Program) {
|
|
3614
|
+
if (current.type === TSESTree11.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
3615
|
+
return current;
|
|
3616
|
+
}
|
|
3617
|
+
current = current.parent;
|
|
3618
|
+
}
|
|
3619
|
+
return void 0;
|
|
3620
|
+
};
|
|
3621
|
+
var no_inline_object_types_default = createRule3({
|
|
3622
|
+
name: "no-inline-object-types",
|
|
3623
|
+
meta: meta3,
|
|
3624
|
+
defaultOptions: [],
|
|
3625
|
+
create(context) {
|
|
3626
|
+
const listeners = {
|
|
3627
|
+
TSTypeLiteral(node) {
|
|
3628
|
+
const isAliasBody = node.parent.type === TSESTree11.AST_NODE_TYPES.TSTypeAliasDeclaration;
|
|
3629
|
+
if (isAliasBody) return;
|
|
3630
|
+
const enclosingAlias = enclosingTypeAlias(node);
|
|
3631
|
+
if (!enclosingAlias) return;
|
|
3632
|
+
const report = {
|
|
3633
|
+
node,
|
|
3634
|
+
messageId: "inlineObjectType"
|
|
3635
|
+
};
|
|
3636
|
+
context.report(report);
|
|
3637
|
+
}
|
|
3638
|
+
};
|
|
3639
|
+
return listeners;
|
|
3640
|
+
}
|
|
3641
|
+
});
|
|
3642
|
+
|
|
3643
|
+
// packages/lint-suite/src/lib/rules/readonly-type-properties/readonly-type-properties.ts
|
|
3644
|
+
import { ESLintUtils as ESLintUtils4, TSESTree as TSESTree12 } from "@typescript-eslint/utils";
|
|
3645
|
+
var docs4 = {
|
|
3646
|
+
description: "Require readonly on primitive-typed type and interface properties"
|
|
3647
|
+
};
|
|
3648
|
+
var messages4 = {
|
|
3649
|
+
missingReadonly: "Type property '{{ name }}' should be readonly. Disable this rule on the line if mutation is required."
|
|
3650
|
+
};
|
|
3651
|
+
var meta4 = {
|
|
3652
|
+
type: "suggestion",
|
|
3653
|
+
docs: docs4,
|
|
3654
|
+
fixable: "code",
|
|
3655
|
+
schema: [],
|
|
3656
|
+
messages: messages4
|
|
3657
|
+
};
|
|
3658
|
+
var createRule4 = ESLintUtils4.RuleCreator(
|
|
3659
|
+
() => "https://github.com/F0rty-Tw0/lint-suite#readonly-type-properties"
|
|
3660
|
+
);
|
|
3661
|
+
var propertyName3 = (node, sourceCode) => {
|
|
3662
|
+
if (node.key.type === TSESTree12.AST_NODE_TYPES.Identifier) {
|
|
3663
|
+
return node.key.name;
|
|
3664
|
+
}
|
|
3665
|
+
return sourceCode.getText(node.key);
|
|
3666
|
+
};
|
|
3667
|
+
var PRIMITIVE_KEYWORDS = /* @__PURE__ */ new Set([
|
|
3668
|
+
TSESTree12.AST_NODE_TYPES.TSStringKeyword,
|
|
3669
|
+
TSESTree12.AST_NODE_TYPES.TSNumberKeyword,
|
|
3670
|
+
TSESTree12.AST_NODE_TYPES.TSBooleanKeyword,
|
|
3671
|
+
TSESTree12.AST_NODE_TYPES.TSBigIntKeyword,
|
|
3672
|
+
TSESTree12.AST_NODE_TYPES.TSSymbolKeyword,
|
|
3673
|
+
TSESTree12.AST_NODE_TYPES.TSNullKeyword,
|
|
3674
|
+
TSESTree12.AST_NODE_TYPES.TSUndefinedKeyword
|
|
3675
|
+
]);
|
|
3676
|
+
var isPrimitive = (type) => {
|
|
3677
|
+
const isPrimitiveKeyword = PRIMITIVE_KEYWORDS.has(type.type);
|
|
3678
|
+
if (isPrimitiveKeyword) return true;
|
|
3679
|
+
if (type.type === TSESTree12.AST_NODE_TYPES.TSLiteralType || type.type === TSESTree12.AST_NODE_TYPES.TSTemplateLiteralType) {
|
|
3680
|
+
return true;
|
|
3681
|
+
}
|
|
3682
|
+
if (type.type === TSESTree12.AST_NODE_TYPES.TSUnionType || type.type === TSESTree12.AST_NODE_TYPES.TSIntersectionType) {
|
|
3683
|
+
return type.types.every(isPrimitive);
|
|
3684
|
+
}
|
|
3685
|
+
return false;
|
|
3686
|
+
};
|
|
3687
|
+
var readonlyFix = (node, sourceCode) => {
|
|
3688
|
+
return (fixer) => {
|
|
3689
|
+
const firstToken = sourceCode.getFirstToken(node);
|
|
3690
|
+
if (!firstToken) return null;
|
|
3691
|
+
return fixer.insertTextBefore(firstToken, "readonly ");
|
|
3692
|
+
};
|
|
3693
|
+
};
|
|
3694
|
+
var readonly_type_properties_default = createRule4({
|
|
3695
|
+
name: "readonly-type-properties",
|
|
3696
|
+
meta: meta4,
|
|
3697
|
+
defaultOptions: [],
|
|
3698
|
+
create(context) {
|
|
3699
|
+
const { sourceCode } = context;
|
|
3700
|
+
const listeners = {
|
|
3701
|
+
TSPropertySignature(node) {
|
|
3702
|
+
if (node.readonly || !node.typeAnnotation) return;
|
|
3703
|
+
const isPrimitiveType = isPrimitive(node.typeAnnotation.typeAnnotation);
|
|
3704
|
+
if (!isPrimitiveType) return;
|
|
3705
|
+
const name = propertyName3(node, sourceCode);
|
|
3706
|
+
const data = { name };
|
|
3707
|
+
const fix = readonlyFix(node, sourceCode);
|
|
3708
|
+
const report = {
|
|
3709
|
+
node: node.key,
|
|
3710
|
+
messageId: "missingReadonly",
|
|
3711
|
+
data,
|
|
3712
|
+
fix
|
|
3713
|
+
};
|
|
3714
|
+
context.report(report);
|
|
3715
|
+
}
|
|
3716
|
+
};
|
|
3717
|
+
return listeners;
|
|
3718
|
+
}
|
|
3719
|
+
});
|
|
3720
|
+
|
|
3721
|
+
// packages/lint-suite/src/lib/typescript-local-plugin.ts
|
|
3722
|
+
var localPlugin = definePlugin("local", {
|
|
3723
|
+
"explicit-accessibility": explicit_accessibility_default,
|
|
3724
|
+
"readonly-type-properties": readonly_type_properties_default,
|
|
3725
|
+
"no-inline-object-types": no_inline_object_types_default
|
|
3726
|
+
});
|
|
3727
|
+
|
|
3728
|
+
// packages/lint-suite/src/lib/typescript-safety.ts
|
|
3729
|
+
var typescriptSafety = defineConfig18([
|
|
1479
3730
|
{
|
|
1480
|
-
files: ["**/*.
|
|
3731
|
+
files: ["**/*.ts", "**/*.tsx", "**/*.cts", "**/*.mts"],
|
|
3732
|
+
plugins: { local: localPlugin },
|
|
1481
3733
|
rules: {
|
|
1482
|
-
"
|
|
1483
|
-
"
|
|
3734
|
+
"local/explicit-accessibility": "error",
|
|
3735
|
+
"local/readonly-type-properties": "error",
|
|
3736
|
+
"local/no-inline-object-types": "error",
|
|
3737
|
+
"@typescript-eslint/explicit-function-return-type": "error",
|
|
3738
|
+
"@typescript-eslint/no-empty-function": "error",
|
|
3739
|
+
"@typescript-eslint/no-unused-vars": "error",
|
|
3740
|
+
"@typescript-eslint/no-use-before-define": "error",
|
|
3741
|
+
"@typescript-eslint/no-explicit-any": "error",
|
|
3742
|
+
"@typescript-eslint/no-unsafe-assignment": "error",
|
|
3743
|
+
"@typescript-eslint/no-unsafe-call": "error",
|
|
3744
|
+
"@typescript-eslint/no-unsafe-member-access": "error",
|
|
3745
|
+
"@typescript-eslint/no-unsafe-return": "error",
|
|
3746
|
+
"@typescript-eslint/no-unsafe-argument": "error",
|
|
3747
|
+
"@typescript-eslint/no-empty-object-type": "error",
|
|
3748
|
+
"@typescript-eslint/no-unsafe-function-type": "error",
|
|
3749
|
+
"@typescript-eslint/no-wrapper-object-types": "error",
|
|
3750
|
+
"@typescript-eslint/explicit-module-boundary-types": "error",
|
|
3751
|
+
"@typescript-eslint/no-non-null-assertion": "error",
|
|
3752
|
+
"@typescript-eslint/prefer-nullish-coalescing": "error",
|
|
3753
|
+
"@typescript-eslint/prefer-optional-chain": "error",
|
|
3754
|
+
"@typescript-eslint/no-floating-promises": [
|
|
3755
|
+
"error",
|
|
3756
|
+
{ ignoreVoid: true }
|
|
3757
|
+
],
|
|
3758
|
+
"@typescript-eslint/no-misused-promises": "warn",
|
|
3759
|
+
"@typescript-eslint/promise-function-async": "error",
|
|
3760
|
+
"@typescript-eslint/await-thenable": "error",
|
|
3761
|
+
"@typescript-eslint/no-inferrable-types": "warn",
|
|
3762
|
+
"@typescript-eslint/no-require-imports": "error",
|
|
3763
|
+
"@typescript-eslint/only-throw-error": "error",
|
|
3764
|
+
"@typescript-eslint/no-array-delete": "error",
|
|
3765
|
+
"@typescript-eslint/return-await": ["error", "in-try-catch"],
|
|
3766
|
+
"@typescript-eslint/require-await": "error",
|
|
3767
|
+
"@typescript-eslint/restrict-template-expressions": [
|
|
3768
|
+
"error",
|
|
3769
|
+
{ allowNumber: true, allowBoolean: true }
|
|
3770
|
+
],
|
|
3771
|
+
"@typescript-eslint/restrict-plus-operands": "error",
|
|
3772
|
+
"@typescript-eslint/prefer-as-const": "error",
|
|
3773
|
+
"@typescript-eslint/no-shadow": "error",
|
|
3774
|
+
"@typescript-eslint/switch-exhaustiveness-check": "error",
|
|
3775
|
+
"@typescript-eslint/no-unused-expressions": [
|
|
3776
|
+
"error",
|
|
3777
|
+
{ allowShortCircuit: true, allowTernary: true }
|
|
3778
|
+
],
|
|
3779
|
+
"@typescript-eslint/no-useless-constructor": "error",
|
|
3780
|
+
"@typescript-eslint/dot-notation": ["error", { allowKeywords: true }],
|
|
3781
|
+
"@typescript-eslint/no-unnecessary-condition": "error",
|
|
3782
|
+
"@typescript-eslint/no-confusing-void-expression": [
|
|
3783
|
+
"error",
|
|
3784
|
+
{ ignoreArrowShorthand: true, ignoreVoidOperator: true }
|
|
3785
|
+
],
|
|
3786
|
+
"@typescript-eslint/no-dynamic-delete": "error",
|
|
3787
|
+
"@typescript-eslint/no-extraneous-class": [
|
|
3788
|
+
"error",
|
|
3789
|
+
{ allowWithDecorator: true }
|
|
3790
|
+
],
|
|
3791
|
+
"@typescript-eslint/no-invalid-void-type": "error",
|
|
3792
|
+
"@typescript-eslint/unified-signatures": "error",
|
|
3793
|
+
"@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
|
|
3794
|
+
"@typescript-eslint/prefer-find": "error",
|
|
3795
|
+
"@typescript-eslint/prefer-for-of": "error",
|
|
3796
|
+
"@typescript-eslint/prefer-includes": "error",
|
|
3797
|
+
"@typescript-eslint/prefer-string-starts-ends-with": "error",
|
|
3798
|
+
"@typescript-eslint/prefer-regexp-exec": "error"
|
|
1484
3799
|
}
|
|
1485
3800
|
}
|
|
1486
3801
|
]);
|
|
1487
3802
|
|
|
3803
|
+
// packages/lint-suite/src/lib/typescript.ts
|
|
3804
|
+
var typescript = defineConfig19([
|
|
3805
|
+
...typescriptRecommended,
|
|
3806
|
+
...typescriptSafety,
|
|
3807
|
+
...typescriptConsistency,
|
|
3808
|
+
...typescriptImports,
|
|
3809
|
+
...typescriptOverrides
|
|
3810
|
+
]);
|
|
3811
|
+
|
|
1488
3812
|
// packages/lint-suite/src/lib/vitest.ts
|
|
1489
3813
|
import vitestEslint from "@vitest/eslint-plugin";
|
|
1490
|
-
import { defineConfig as
|
|
3814
|
+
import { defineConfig as defineConfig20 } from "eslint/config";
|
|
1491
3815
|
var vitestPlugin = {
|
|
1492
3816
|
vitest: vitestEslint
|
|
1493
3817
|
};
|
|
1494
|
-
var vitest =
|
|
3818
|
+
var vitest = defineConfig20([
|
|
1495
3819
|
{
|
|
1496
3820
|
files: ["**/*.spec.ts", "**/*.spec.js", "**/*.test.ts", "**/*.test.js"],
|
|
1497
3821
|
plugins: vitestPlugin,
|
|
@@ -1514,147 +3838,6 @@ var vitest = defineConfig10([
|
|
|
1514
3838
|
}
|
|
1515
3839
|
]);
|
|
1516
3840
|
|
|
1517
|
-
// packages/lint-suite/src/lib/playwright.ts
|
|
1518
|
-
import playwrightEslint from "eslint-plugin-playwright";
|
|
1519
|
-
import { defineConfig as defineConfig11 } from "eslint/config";
|
|
1520
|
-
var playwright = defineConfig11([
|
|
1521
|
-
{
|
|
1522
|
-
...playwrightEslint.configs["flat/recommended"],
|
|
1523
|
-
files: ["**/*.e2e.ts", "**/*.e2e.js"],
|
|
1524
|
-
rules: {
|
|
1525
|
-
...playwrightEslint.configs["flat/recommended"].rules,
|
|
1526
|
-
// Locator best practices
|
|
1527
|
-
"playwright/prefer-locator": "error",
|
|
1528
|
-
"playwright/prefer-native-locators": "error",
|
|
1529
|
-
"playwright/no-nth-methods": "warn",
|
|
1530
|
-
// Matcher improvements
|
|
1531
|
-
"playwright/prefer-to-be": "error",
|
|
1532
|
-
"playwright/prefer-to-have-count": "error",
|
|
1533
|
-
"playwright/prefer-to-have-length": "error",
|
|
1534
|
-
"playwright/prefer-comparison-matcher": "error",
|
|
1535
|
-
// Test structure
|
|
1536
|
-
"playwright/prefer-hooks-on-top": "error",
|
|
1537
|
-
"playwright/require-top-level-describe": "warn"
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
]);
|
|
1541
|
-
|
|
1542
|
-
// packages/lint-suite/src/lib/boundaries.ts
|
|
1543
|
-
import boundariesPlugin from "eslint-plugin-boundaries";
|
|
1544
|
-
import { defineConfig as defineConfig12 } from "eslint/config";
|
|
1545
|
-
var boundaries = defineConfig12([
|
|
1546
|
-
{
|
|
1547
|
-
files: [
|
|
1548
|
-
"**/*.js",
|
|
1549
|
-
"**/*.jsx",
|
|
1550
|
-
"**/*.ts",
|
|
1551
|
-
"**/*.tsx",
|
|
1552
|
-
"**/*.cts",
|
|
1553
|
-
"**/*.mts"
|
|
1554
|
-
],
|
|
1555
|
-
plugins: {
|
|
1556
|
-
boundaries: boundariesPlugin
|
|
1557
|
-
},
|
|
1558
|
-
settings: {
|
|
1559
|
-
"boundaries/elements": [
|
|
1560
|
-
{
|
|
1561
|
-
type: "feature",
|
|
1562
|
-
pattern: "**/feature/**"
|
|
1563
|
-
},
|
|
1564
|
-
{
|
|
1565
|
-
type: "data-access",
|
|
1566
|
-
pattern: "**/data-access/**"
|
|
1567
|
-
},
|
|
1568
|
-
{
|
|
1569
|
-
type: "domain-logic",
|
|
1570
|
-
pattern: "**/domain-logic/**"
|
|
1571
|
-
},
|
|
1572
|
-
{
|
|
1573
|
-
type: "ui",
|
|
1574
|
-
pattern: "**/ui/**"
|
|
1575
|
-
},
|
|
1576
|
-
{
|
|
1577
|
-
type: "common",
|
|
1578
|
-
pattern: "**/common/**"
|
|
1579
|
-
},
|
|
1580
|
-
{
|
|
1581
|
-
type: "utils",
|
|
1582
|
-
pattern: "**/utils/**"
|
|
1583
|
-
}
|
|
1584
|
-
],
|
|
1585
|
-
"import/resolver": {
|
|
1586
|
-
typescript: {
|
|
1587
|
-
alwaysTryTypes: true
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
},
|
|
1591
|
-
rules: {
|
|
1592
|
-
...boundariesPlugin.configs.recommended.rules,
|
|
1593
|
-
"boundaries/dependencies": [
|
|
1594
|
-
"error",
|
|
1595
|
-
{
|
|
1596
|
-
default: "disallow",
|
|
1597
|
-
message: "{{to.element.types}} is not allowed to be imported in {{from.element.types}}",
|
|
1598
|
-
policies: [
|
|
1599
|
-
{
|
|
1600
|
-
from: { element: { type: "feature" } },
|
|
1601
|
-
allow: [
|
|
1602
|
-
{ to: { element: { type: "feature" } } },
|
|
1603
|
-
{ to: { element: { type: "ui" } } },
|
|
1604
|
-
{ to: { element: { type: "domain-logic" } } },
|
|
1605
|
-
{ to: { element: { type: "utils" } } },
|
|
1606
|
-
{ to: { element: { type: "common" } } }
|
|
1607
|
-
]
|
|
1608
|
-
},
|
|
1609
|
-
{
|
|
1610
|
-
from: { element: { type: "domain-logic" } },
|
|
1611
|
-
allow: [
|
|
1612
|
-
{ to: { element: { type: "domain-logic" } } },
|
|
1613
|
-
{ to: { element: { type: "data-access" } } },
|
|
1614
|
-
{ to: { element: { type: "utils" } } },
|
|
1615
|
-
{ to: { element: { type: "common" } } }
|
|
1616
|
-
]
|
|
1617
|
-
},
|
|
1618
|
-
{
|
|
1619
|
-
from: { element: { type: "data-access" } },
|
|
1620
|
-
allow: [
|
|
1621
|
-
{ to: { element: { type: "data-access" } } },
|
|
1622
|
-
{ to: { element: { type: "utils" } } },
|
|
1623
|
-
{ to: { element: { type: "common" } } }
|
|
1624
|
-
]
|
|
1625
|
-
},
|
|
1626
|
-
{
|
|
1627
|
-
from: { element: { type: "ui" } },
|
|
1628
|
-
allow: [
|
|
1629
|
-
{ to: { element: { type: "ui" } } },
|
|
1630
|
-
{ to: { element: { type: "utils" } } },
|
|
1631
|
-
{ to: { element: { type: "common" } } }
|
|
1632
|
-
]
|
|
1633
|
-
},
|
|
1634
|
-
{
|
|
1635
|
-
from: { element: { type: "utils" } },
|
|
1636
|
-
allow: [
|
|
1637
|
-
{ to: { element: { type: "utils" } } },
|
|
1638
|
-
{ to: { element: { type: "common" } } }
|
|
1639
|
-
]
|
|
1640
|
-
},
|
|
1641
|
-
{
|
|
1642
|
-
from: { element: { type: "common" } },
|
|
1643
|
-
allow: [{ to: { element: { type: "common" } } }]
|
|
1644
|
-
}
|
|
1645
|
-
]
|
|
1646
|
-
}
|
|
1647
|
-
]
|
|
1648
|
-
}
|
|
1649
|
-
},
|
|
1650
|
-
{
|
|
1651
|
-
files: ["**/*.stories.ts"],
|
|
1652
|
-
rules: {
|
|
1653
|
-
"boundaries/dependencies": "off"
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
]);
|
|
1657
|
-
|
|
1658
3841
|
// packages/lint-suite/src/eslint.ts
|
|
1659
3842
|
var recommended = [
|
|
1660
3843
|
...base,
|