@trackunit/eslint-plugin-trackunit 0.4.64 → 0.4.66
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/CHANGELOG.md +8 -0
- package/README.md +37 -27
- package/package.json +1 -1
- package/src/index.js.map +1 -0
- package/src/lib/config/fragments/ignores.js.map +1 -0
- package/src/lib/config/fragments/import-rules.js.map +1 -0
- package/src/lib/config/fragments/jest-overrides.js.map +1 -0
- package/src/lib/config/fragments/jsdoc-rules.js.map +1 -0
- package/src/lib/config/fragments/module-boundaries.js.map +1 -0
- package/src/lib/config/fragments/react-rules.js.map +1 -0
- package/src/lib/config/fragments/restricted-imports.js.map +1 -0
- package/src/lib/config/fragments/testing-library.js.map +1 -0
- package/src/lib/config/fragments/typescript-rules.js.map +1 -0
- package/src/lib/config/index.d.ts +21 -0
- package/src/lib/config/index.js.map +1 -0
- package/src/lib/config/plugins.d.ts +7 -0
- package/src/lib/config/plugins.js.map +1 -0
- package/src/lib/config/presets/base.d.ts +14 -0
- package/src/lib/config/presets/base.js.map +1 -0
- package/src/lib/config/presets/e2e.js.map +1 -0
- package/src/lib/config/presets/react.d.ts +7 -0
- package/src/lib/config/presets/react.js.map +1 -0
- package/src/lib/config/presets/server.js.map +1 -0
- package/src/lib/config/utils.js.map +1 -0
- package/src/lib/config-helpers/create-skip-when.js.map +1 -0
- package/src/lib/rules/cva-merge-base-classes-as-array/cva-merge-base-classes-as-array.js.map +1 -0
- package/src/lib/rules/design-guideline-button-icon-size-match/design-guideline-button-icon-size-match.js.map +1 -0
- package/src/lib/rules/no-internal-barrel-files/no-internal-barrel-files.js.map +1 -0
- package/src/lib/rules/no-internal-graphql-when-tagged-with-gql-public/no-internal-graphql-when-tagged-with-gql-public.js.map +1 -0
- package/src/lib/rules/no-jest-mock-trackunit-react-core-hooks/no-jest-mock-trackunit-react-core-hooks.js.map +1 -0
- package/src/lib/rules/no-template-strings-in-classname-prop/no-template-strings-in-classname-prop.js.map +1 -0
- package/src/lib/rules/no-typescript-assertion/no-typescript-assertion.js.map +1 -0
- package/src/lib/rules/prefer-destructured-imports/prefer-destructured-imports.js.map +1 -0
- package/src/lib/rules/prefer-event-specific-callback-naming/name-suggestion-strategies.js.map +1 -0
- package/src/lib/rules/prefer-event-specific-callback-naming/prefer-event-specific-callback-naming.js.map +1 -0
- package/src/lib/rules/prefer-event-specific-callback-naming/strategies/string-based.js.map +1 -0
- package/src/lib/rules/prefer-event-specific-callback-naming/strategies/type-based.js.map +1 -0
- package/src/lib/rules/prefer-event-specific-callback-naming/utils.js.map +1 -0
- package/src/lib/rules/prefer-field-components/prefer-field-components.js.map +1 -0
- package/src/lib/rules/prefer-mouse-event-handler-in-react-props/prefer-mouse-event-handler-in-react-props.js.map +1 -0
- package/src/lib/rules/require-classname-alternatives/require-classname-alternatives.js.map +1 -0
- package/src/lib/rules/require-component-prop-contracts/require-component-prop-contracts.d.ts +15 -0
- package/src/lib/rules/require-component-prop-contracts/require-component-prop-contracts.js +539 -0
- package/src/lib/rules/require-component-prop-contracts/require-component-prop-contracts.js.map +1 -0
- package/src/lib/rules/require-list-item-virtualization-props/require-list-item-virtualization-props.js.map +1 -0
- package/src/lib/rules/require-optional-prop-initialization/require-optional-prop-initialization.js.map +1 -0
- package/src/lib/rules/require-optional-prop-initialization/suggestion-utils.js.map +1 -0
- package/src/lib/rules-map.d.ts +7 -0
- package/src/lib/rules-map.js +2 -0
- package/src/lib/rules-map.js.map +1 -0
- package/src/lib/utils/ast-utils.js.map +1 -0
- package/src/lib/utils/classname-utils.js.map +1 -0
- package/src/lib/utils/file-utils.js.map +1 -0
- package/src/lib/utils/import-utils.js.map +1 -0
- package/src/lib/utils/nx-utils.js.map +1 -0
- package/src/lib/utils/package-utils.js.map +1 -0
- package/src/lib/utils/typescript-utils.js.map +1 -0
|
@@ -0,0 +1,539 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireComponentPropContracts = exports.checkerTypeHasRequiredInterface = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
6
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
7
|
+
const path = tslib_1.__importStar(require("path"));
|
|
8
|
+
const ts = tslib_1.__importStar(require("typescript"));
|
|
9
|
+
const ast_utils_1 = require("../../utils/ast-utils");
|
|
10
|
+
const nx_utils_1 = require("../../utils/nx-utils");
|
|
11
|
+
const createRule = utils_1.ESLintUtils.RuleCreator(name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}/${name}.ts`);
|
|
12
|
+
const componentWrapperNames = new Set(["forwardRef", "memo"]);
|
|
13
|
+
const publicEntrypointFileNames = ["index.ts", "index.tsx"];
|
|
14
|
+
const getTypeSymbolInfo = (type) => {
|
|
15
|
+
try {
|
|
16
|
+
return {
|
|
17
|
+
symbolName: type.symbol.getName(),
|
|
18
|
+
declarations: type.symbol.getDeclarations() ?? [],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return {
|
|
23
|
+
symbolName: undefined,
|
|
24
|
+
declarations: [],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const getTypeReferenceName = (typeName) => {
|
|
29
|
+
if (typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
30
|
+
return typeName.name;
|
|
31
|
+
}
|
|
32
|
+
if (typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName) {
|
|
33
|
+
return typeName.right.name;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
const getCallExpressionName = (node) => {
|
|
38
|
+
const { callee } = node;
|
|
39
|
+
if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
40
|
+
return callee.name;
|
|
41
|
+
}
|
|
42
|
+
if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression && callee.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
43
|
+
return callee.property.name;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
const isPascalCaseName = (name) => /^[A-Z]/.test(name);
|
|
48
|
+
const normalizeFilePath = (filePath) => path.normalize(filePath);
|
|
49
|
+
const getProjectRoot = (projectJsonPath) => path.dirname(projectJsonPath);
|
|
50
|
+
const getPublicEntrypointPaths = (projectRoot) => publicEntrypointFileNames.map(fileName => path.join(projectRoot, "src", fileName));
|
|
51
|
+
const getModulePathCandidates = (fromFilePath, moduleSpecifier) => {
|
|
52
|
+
const modulePath = path.resolve(path.dirname(fromFilePath), moduleSpecifier);
|
|
53
|
+
return [
|
|
54
|
+
modulePath,
|
|
55
|
+
`${modulePath}.ts`,
|
|
56
|
+
`${modulePath}.tsx`,
|
|
57
|
+
path.join(modulePath, "index.ts"),
|
|
58
|
+
path.join(modulePath, "index.tsx"),
|
|
59
|
+
];
|
|
60
|
+
};
|
|
61
|
+
const moduleSpecifierTargetsFile = (fromFilePath, moduleSpecifier, targetFilePath) => {
|
|
62
|
+
if (!moduleSpecifier.startsWith(".")) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const normalizedTargetFilePath = normalizeFilePath(targetFilePath);
|
|
66
|
+
return getModulePathCandidates(fromFilePath, moduleSpecifier).some(candidatePath => normalizeFilePath(candidatePath) === normalizedTargetFilePath);
|
|
67
|
+
};
|
|
68
|
+
const getSourceExportName = (specifier) => {
|
|
69
|
+
if (specifier.propertyName?.text === "default") {
|
|
70
|
+
return specifier.name.text;
|
|
71
|
+
}
|
|
72
|
+
return specifier.propertyName?.text ?? specifier.name.text;
|
|
73
|
+
};
|
|
74
|
+
const getPublicExportedNamesForFile = (projectRoot, filePath) => {
|
|
75
|
+
const result = {
|
|
76
|
+
exportsAll: false,
|
|
77
|
+
names: new Set(),
|
|
78
|
+
};
|
|
79
|
+
for (const entrypointPath of getPublicEntrypointPaths(projectRoot)) {
|
|
80
|
+
let sourceText;
|
|
81
|
+
try {
|
|
82
|
+
sourceText = fs.readFileSync(entrypointPath, "utf8");
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const sourceFile = ts.createSourceFile(entrypointPath, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
88
|
+
for (const statement of sourceFile.statements) {
|
|
89
|
+
if (!ts.isExportDeclaration(statement) || !statement.moduleSpecifier) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (!ts.isStringLiteral(statement.moduleSpecifier)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (!moduleSpecifierTargetsFile(entrypointPath, statement.moduleSpecifier.text, filePath)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (!statement.exportClause) {
|
|
99
|
+
result.exportsAll = true;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (ts.isNamedExports(statement.exportClause)) {
|
|
103
|
+
statement.exportClause.elements.forEach(specifier => {
|
|
104
|
+
result.names.add(getSourceExportName(specifier));
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result;
|
|
110
|
+
};
|
|
111
|
+
const isEntrypointFile = (projectRoot, filePath) => {
|
|
112
|
+
const normalizedFilePath = normalizeFilePath(filePath);
|
|
113
|
+
return getPublicEntrypointPaths(projectRoot).some(entrypointPath => normalizeFilePath(entrypointPath) === normalizedFilePath);
|
|
114
|
+
};
|
|
115
|
+
const isComponentWrapperCallExpression = (node) => {
|
|
116
|
+
const firstArgument = node.arguments[0];
|
|
117
|
+
const callExpressionName = getCallExpressionName(node);
|
|
118
|
+
if (!callExpressionName || !componentWrapperNames.has(callExpressionName)) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
return (firstArgument?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
122
|
+
firstArgument?.type === utils_1.AST_NODE_TYPES.FunctionExpression);
|
|
123
|
+
};
|
|
124
|
+
const getHeritageName = (heritage) => {
|
|
125
|
+
const { expression } = heritage;
|
|
126
|
+
if (expression.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
127
|
+
return expression.name;
|
|
128
|
+
}
|
|
129
|
+
if (expression.type === utils_1.AST_NODE_TYPES.MemberExpression && expression.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
130
|
+
return expression.property.name;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
};
|
|
134
|
+
const getParameterTypeAnnotation = (parameter) => {
|
|
135
|
+
if (!parameter) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
if (parameter.type === utils_1.AST_NODE_TYPES.Identifier || parameter.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
|
|
139
|
+
return parameter.typeAnnotation?.typeAnnotation ?? null;
|
|
140
|
+
}
|
|
141
|
+
if (parameter.type === utils_1.AST_NODE_TYPES.AssignmentPattern) {
|
|
142
|
+
const { left } = parameter;
|
|
143
|
+
if (left.type === utils_1.AST_NODE_TYPES.Identifier || left.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
|
|
144
|
+
return left.typeAnnotation?.typeAnnotation ?? null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
};
|
|
149
|
+
const getFunctionPropsType = (node) => getParameterTypeAnnotation(node.params[0]);
|
|
150
|
+
const getPropsTypeFromCallExpression = (node) => {
|
|
151
|
+
const typeArguments = node.typeArguments?.params ?? [];
|
|
152
|
+
const firstArgument = node.arguments[0];
|
|
153
|
+
if (firstArgument?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
|
|
154
|
+
firstArgument?.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
155
|
+
const parameterType = getParameterTypeAnnotation(firstArgument.params[0]);
|
|
156
|
+
if (parameterType) {
|
|
157
|
+
return parameterType;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (getCallExpressionName(node) === "forwardRef") {
|
|
161
|
+
return typeArguments[1] ?? null;
|
|
162
|
+
}
|
|
163
|
+
return typeArguments[0] ?? null;
|
|
164
|
+
};
|
|
165
|
+
const getPropsTypeFromVariableDeclarator = (node) => {
|
|
166
|
+
const { init } = node;
|
|
167
|
+
if (!init) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
if (init.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
171
|
+
return getFunctionPropsType(init);
|
|
172
|
+
}
|
|
173
|
+
if (init.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
174
|
+
return getPropsTypeFromCallExpression(init);
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
};
|
|
178
|
+
const isExportedDeclaration = (node) => {
|
|
179
|
+
const parent = node.parent;
|
|
180
|
+
if (!parent) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
if (parent.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration ||
|
|
184
|
+
parent.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
const grandParent = parent.parent;
|
|
188
|
+
return grandParent?.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration;
|
|
189
|
+
};
|
|
190
|
+
const typeHasRequiredInterface = (typeNode, requiredInterface, declarations, visitedTypeNames = new Set()) => {
|
|
191
|
+
switch (typeNode.type) {
|
|
192
|
+
case utils_1.AST_NODE_TYPES.TSTypeReference: {
|
|
193
|
+
const referenceName = getTypeReferenceName(typeNode.typeName);
|
|
194
|
+
if (!referenceName) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
if (referenceName === requiredInterface) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
if (visitedTypeNames.has(referenceName)) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
visitedTypeNames.add(referenceName);
|
|
204
|
+
const interfaceDeclaration = declarations.interfaces.get(referenceName);
|
|
205
|
+
if (interfaceDeclaration) {
|
|
206
|
+
return interfaceExtendsRequiredInterface(interfaceDeclaration, requiredInterface, declarations, visitedTypeNames);
|
|
207
|
+
}
|
|
208
|
+
const typeAlias = declarations.typeAliases.get(referenceName);
|
|
209
|
+
if (typeAlias) {
|
|
210
|
+
return typeHasRequiredInterface(typeAlias.typeAnnotation, requiredInterface, declarations, visitedTypeNames);
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
case utils_1.AST_NODE_TYPES.TSIntersectionType:
|
|
215
|
+
return typeNode.types.some(part => typeHasRequiredInterface(part, requiredInterface, declarations, new Set(visitedTypeNames)));
|
|
216
|
+
case utils_1.AST_NODE_TYPES.TSUnionType:
|
|
217
|
+
return typeNode.types.every(part => typeHasRequiredInterface(part, requiredInterface, declarations, new Set(visitedTypeNames)));
|
|
218
|
+
default:
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const interfaceExtendsRequiredInterface = (declaration, requiredInterface, declarations, visitedTypeNames) => {
|
|
223
|
+
return declaration.extends.some(heritage => {
|
|
224
|
+
const heritageName = getHeritageName(heritage);
|
|
225
|
+
if (!heritageName) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
if (heritageName === requiredInterface) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
if (visitedTypeNames.has(heritageName)) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
visitedTypeNames.add(heritageName);
|
|
235
|
+
const interfaceDeclaration = declarations.interfaces.get(heritageName);
|
|
236
|
+
if (interfaceDeclaration) {
|
|
237
|
+
return interfaceExtendsRequiredInterface(interfaceDeclaration, requiredInterface, declarations, visitedTypeNames);
|
|
238
|
+
}
|
|
239
|
+
const typeAlias = declarations.typeAliases.get(heritageName);
|
|
240
|
+
if (typeAlias) {
|
|
241
|
+
return typeHasRequiredInterface(typeAlias.typeAnnotation, requiredInterface, declarations, visitedTypeNames);
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
});
|
|
245
|
+
};
|
|
246
|
+
const interfaceHasRequiredProp = (declaration, requiredProp, declarations, visitedTypeNames) => {
|
|
247
|
+
const hasOwnProp = declaration.body.body.some(member => {
|
|
248
|
+
if (member.type !== utils_1.AST_NODE_TYPES.TSPropertySignature) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
return member.key.type === utils_1.AST_NODE_TYPES.Identifier && member.key.name === requiredProp;
|
|
252
|
+
});
|
|
253
|
+
if (hasOwnProp) {
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
return declaration.extends.some(heritage => {
|
|
257
|
+
const heritageName = getHeritageName(heritage);
|
|
258
|
+
if (!heritageName || visitedTypeNames.has(heritageName)) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
visitedTypeNames.add(heritageName);
|
|
262
|
+
const interfaceDeclaration = declarations.interfaces.get(heritageName);
|
|
263
|
+
if (interfaceDeclaration) {
|
|
264
|
+
return interfaceHasRequiredProp(interfaceDeclaration, requiredProp, declarations, visitedTypeNames);
|
|
265
|
+
}
|
|
266
|
+
const typeAlias = declarations.typeAliases.get(heritageName);
|
|
267
|
+
if (typeAlias) {
|
|
268
|
+
return typeHasRequiredProp(typeAlias.typeAnnotation, requiredProp, declarations, visitedTypeNames);
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
});
|
|
272
|
+
};
|
|
273
|
+
const typeLiteralHasRequiredProp = (typeNode, requiredProp) => {
|
|
274
|
+
return typeNode.members.some(member => {
|
|
275
|
+
if (member.type !== utils_1.AST_NODE_TYPES.TSPropertySignature) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
return member.key.type === utils_1.AST_NODE_TYPES.Identifier && member.key.name === requiredProp;
|
|
279
|
+
});
|
|
280
|
+
};
|
|
281
|
+
const typeHasRequiredProp = (typeNode, requiredProp, declarations, visitedTypeNames = new Set()) => {
|
|
282
|
+
switch (typeNode.type) {
|
|
283
|
+
case utils_1.AST_NODE_TYPES.TSTypeLiteral:
|
|
284
|
+
return typeLiteralHasRequiredProp(typeNode, requiredProp);
|
|
285
|
+
case utils_1.AST_NODE_TYPES.TSTypeReference: {
|
|
286
|
+
const referenceName = getTypeReferenceName(typeNode.typeName);
|
|
287
|
+
if (!referenceName || visitedTypeNames.has(referenceName)) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
visitedTypeNames.add(referenceName);
|
|
291
|
+
const interfaceDeclaration = declarations.interfaces.get(referenceName);
|
|
292
|
+
if (interfaceDeclaration) {
|
|
293
|
+
return interfaceHasRequiredProp(interfaceDeclaration, requiredProp, declarations, visitedTypeNames);
|
|
294
|
+
}
|
|
295
|
+
const typeAlias = declarations.typeAliases.get(referenceName);
|
|
296
|
+
if (typeAlias) {
|
|
297
|
+
return typeHasRequiredProp(typeAlias.typeAnnotation, requiredProp, declarations, visitedTypeNames);
|
|
298
|
+
}
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
case utils_1.AST_NODE_TYPES.TSIntersectionType:
|
|
302
|
+
return typeNode.types.some(part => typeHasRequiredProp(part, requiredProp, declarations, new Set(visitedTypeNames)));
|
|
303
|
+
case utils_1.AST_NODE_TYPES.TSUnionType:
|
|
304
|
+
return typeNode.types.every(part => typeHasRequiredProp(part, requiredProp, declarations, new Set(visitedTypeNames)));
|
|
305
|
+
default:
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
const getTypeCheckerContext = (context) => {
|
|
310
|
+
try {
|
|
311
|
+
const services = utils_1.ESLintUtils.getParserServices(context);
|
|
312
|
+
return {
|
|
313
|
+
checker: services.program.getTypeChecker(),
|
|
314
|
+
services,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
catch {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
const checkerTypeHasRequiredInterface = (type, requiredInterface, checker, visitedTypeNames = new Set()) => {
|
|
322
|
+
if (type.isUnion()) {
|
|
323
|
+
return type.types.every(part => (0, exports.checkerTypeHasRequiredInterface)(part, requiredInterface, checker, new Set()));
|
|
324
|
+
}
|
|
325
|
+
if (type.isIntersection()) {
|
|
326
|
+
return type.types.some(part => (0, exports.checkerTypeHasRequiredInterface)(part, requiredInterface, checker, new Set(visitedTypeNames)));
|
|
327
|
+
}
|
|
328
|
+
const { symbolName, declarations } = getTypeSymbolInfo(type);
|
|
329
|
+
const aliasSymbolName = type.aliasSymbol?.getName();
|
|
330
|
+
if (symbolName === requiredInterface || aliasSymbolName === requiredInterface) {
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
const visitKey = aliasSymbolName ?? symbolName;
|
|
334
|
+
if (visitKey) {
|
|
335
|
+
if (visitedTypeNames.has(visitKey)) {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
visitedTypeNames.add(visitKey);
|
|
339
|
+
}
|
|
340
|
+
if (type.aliasTypeArguments?.some(typeArgument => (0, exports.checkerTypeHasRequiredInterface)(typeArgument, requiredInterface, checker))) {
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
if (type.isClassOrInterface()) {
|
|
344
|
+
const baseTypes = checker.getBaseTypes(type);
|
|
345
|
+
if (baseTypes.some(baseType => (0, exports.checkerTypeHasRequiredInterface)(baseType, requiredInterface, checker, visitedTypeNames))) {
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return declarations.some(declaration => {
|
|
350
|
+
if (!ts.isInterfaceDeclaration(declaration) || !declaration.heritageClauses) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
return declaration.heritageClauses.some(heritageClause => heritageClause.types.some(heritageType => {
|
|
354
|
+
const heritageInterfaceType = checker.getTypeAtLocation(heritageType);
|
|
355
|
+
return (0, exports.checkerTypeHasRequiredInterface)(heritageInterfaceType, requiredInterface, checker, visitedTypeNames);
|
|
356
|
+
}));
|
|
357
|
+
});
|
|
358
|
+
};
|
|
359
|
+
exports.checkerTypeHasRequiredInterface = checkerTypeHasRequiredInterface;
|
|
360
|
+
const checkerTypeHasRequiredProp = (type, requiredProp, checker) => {
|
|
361
|
+
if (type.isUnion()) {
|
|
362
|
+
return type.types.every(part => checkerTypeHasRequiredProp(part, requiredProp, checker));
|
|
363
|
+
}
|
|
364
|
+
return Boolean(type.getProperty(requiredProp));
|
|
365
|
+
};
|
|
366
|
+
const getCheckerType = (typeNode, typeCheckerContext) => {
|
|
367
|
+
if (!typeCheckerContext) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
const tsNode = typeCheckerContext.services.esTreeNodeToTSNodeMap.get(typeNode);
|
|
371
|
+
return typeCheckerContext.checker.getTypeAtLocation(tsNode);
|
|
372
|
+
};
|
|
373
|
+
exports.requireComponentPropContracts = createRule({
|
|
374
|
+
name: "require-component-prop-contracts",
|
|
375
|
+
meta: {
|
|
376
|
+
type: "problem",
|
|
377
|
+
docs: {
|
|
378
|
+
description: "Require exported React component props to include configured prop names or extend configured interfaces.",
|
|
379
|
+
},
|
|
380
|
+
schema: [
|
|
381
|
+
{
|
|
382
|
+
type: "object",
|
|
383
|
+
properties: {
|
|
384
|
+
requiredProps: {
|
|
385
|
+
type: "array",
|
|
386
|
+
items: { type: "string" },
|
|
387
|
+
description: "Prop names every exported component props type must include.",
|
|
388
|
+
},
|
|
389
|
+
requiredInterfaces: {
|
|
390
|
+
type: "array",
|
|
391
|
+
items: { type: "string" },
|
|
392
|
+
description: "Interface names every exported component props type must reference or extend.",
|
|
393
|
+
},
|
|
394
|
+
onlyWhenProjectMatches: {
|
|
395
|
+
type: "object",
|
|
396
|
+
properties: {
|
|
397
|
+
tags: {
|
|
398
|
+
type: "array",
|
|
399
|
+
items: { type: "string" },
|
|
400
|
+
},
|
|
401
|
+
targets: {
|
|
402
|
+
type: "array",
|
|
403
|
+
items: { type: "string" },
|
|
404
|
+
},
|
|
405
|
+
tagsMatchMode: {
|
|
406
|
+
type: "string",
|
|
407
|
+
enum: ["some", "every"],
|
|
408
|
+
},
|
|
409
|
+
targetsMatchMode: {
|
|
410
|
+
type: "string",
|
|
411
|
+
enum: ["some", "every"],
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
additionalProperties: false,
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
additionalProperties: false,
|
|
418
|
+
},
|
|
419
|
+
],
|
|
420
|
+
messages: {
|
|
421
|
+
missingPropContracts: "Public component '{{componentName}}' props must satisfy these contracts: {{missingContracts}}.",
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
defaultOptions: [
|
|
425
|
+
{
|
|
426
|
+
requiredProps: [],
|
|
427
|
+
requiredInterfaces: [],
|
|
428
|
+
onlyWhenProjectMatches: { tags: ["publish:npm"] },
|
|
429
|
+
},
|
|
430
|
+
],
|
|
431
|
+
create(context, [options]) {
|
|
432
|
+
const filename = context.filename;
|
|
433
|
+
if (!filename.endsWith(".tsx")) {
|
|
434
|
+
return {};
|
|
435
|
+
}
|
|
436
|
+
const projectMetadata = (0, nx_utils_1.getProjectMetadata)(filename);
|
|
437
|
+
const projectCriteria = options.onlyWhenProjectMatches ?? { tags: ["publish:npm"] };
|
|
438
|
+
if (!projectMetadata || !(0, nx_utils_1.projectMatchesCriteria)(projectMetadata, projectCriteria)) {
|
|
439
|
+
return {};
|
|
440
|
+
}
|
|
441
|
+
const requiredProps = options.requiredProps ?? [];
|
|
442
|
+
const requiredInterfaces = options.requiredInterfaces ?? [];
|
|
443
|
+
if (requiredProps.length === 0 && requiredInterfaces.length === 0) {
|
|
444
|
+
return {};
|
|
445
|
+
}
|
|
446
|
+
const declarations = {
|
|
447
|
+
interfaces: new Map(),
|
|
448
|
+
typeAliases: new Map(),
|
|
449
|
+
};
|
|
450
|
+
const typeCheckerContext = getTypeCheckerContext(context);
|
|
451
|
+
const exportedIdentifiers = new Set();
|
|
452
|
+
const componentCandidates = [];
|
|
453
|
+
const projectRoot = getProjectRoot(projectMetadata.projectJsonPath);
|
|
454
|
+
const publicExports = getPublicExportedNamesForFile(projectRoot, filename);
|
|
455
|
+
const isCurrentFileEntrypoint = isEntrypointFile(projectRoot, filename);
|
|
456
|
+
const isExportedComponentName = (componentName, node) => {
|
|
457
|
+
if (!isPascalCaseName(componentName) ||
|
|
458
|
+
(!isExportedDeclaration(node) && !exportedIdentifiers.has(componentName))) {
|
|
459
|
+
return false;
|
|
460
|
+
}
|
|
461
|
+
return isCurrentFileEntrypoint || publicExports.exportsAll || publicExports.names.has(componentName);
|
|
462
|
+
};
|
|
463
|
+
const checkCandidate = (candidate) => {
|
|
464
|
+
const checkerType = candidate.propsType ? getCheckerType(candidate.propsType, typeCheckerContext) : null;
|
|
465
|
+
const missingProps = requiredProps.filter(requiredProp => !candidate.propsType ||
|
|
466
|
+
(checkerType && typeCheckerContext
|
|
467
|
+
? !checkerTypeHasRequiredProp(checkerType, requiredProp, typeCheckerContext.checker)
|
|
468
|
+
: !typeHasRequiredProp(candidate.propsType, requiredProp, declarations, new Set())));
|
|
469
|
+
const missingInterfaces = requiredInterfaces.filter(requiredInterface => !candidate.propsType ||
|
|
470
|
+
(checkerType && typeCheckerContext
|
|
471
|
+
? !(0, exports.checkerTypeHasRequiredInterface)(checkerType, requiredInterface, typeCheckerContext.checker)
|
|
472
|
+
: !typeHasRequiredInterface(candidate.propsType, requiredInterface, declarations, new Set())));
|
|
473
|
+
const missingContracts = [...missingProps, ...missingInterfaces];
|
|
474
|
+
if (missingContracts.length === 0) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
context.report({
|
|
478
|
+
node: candidate.node,
|
|
479
|
+
messageId: "missingPropContracts",
|
|
480
|
+
data: {
|
|
481
|
+
componentName: candidate.componentName,
|
|
482
|
+
missingContracts: missingContracts.join(", "),
|
|
483
|
+
},
|
|
484
|
+
});
|
|
485
|
+
};
|
|
486
|
+
return {
|
|
487
|
+
ExportNamedDeclaration(node) {
|
|
488
|
+
for (const specifier of node.specifiers) {
|
|
489
|
+
if (specifier.local.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
490
|
+
exportedIdentifiers.add(specifier.local.name);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
ExportDefaultDeclaration(node) {
|
|
495
|
+
if (node.declaration.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
496
|
+
exportedIdentifiers.add(node.declaration.name);
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
TSInterfaceDeclaration(node) {
|
|
500
|
+
declarations.interfaces.set(node.id.name, node);
|
|
501
|
+
},
|
|
502
|
+
TSTypeAliasDeclaration(node) {
|
|
503
|
+
declarations.typeAliases.set(node.id.name, node);
|
|
504
|
+
},
|
|
505
|
+
FunctionDeclaration(node) {
|
|
506
|
+
const componentName = node.id?.name;
|
|
507
|
+
if (!componentName || !(0, ast_utils_1.isReactComponent)(node) || !isExportedComponentName(componentName, node)) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
componentCandidates.push({
|
|
511
|
+
componentName,
|
|
512
|
+
node,
|
|
513
|
+
propsType: getFunctionPropsType(node),
|
|
514
|
+
});
|
|
515
|
+
},
|
|
516
|
+
VariableDeclarator(node) {
|
|
517
|
+
if (node.id.type !== utils_1.AST_NODE_TYPES.Identifier) {
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const { init } = node;
|
|
521
|
+
const isComponent = init?.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression
|
|
522
|
+
? (0, ast_utils_1.isReactComponent)(init)
|
|
523
|
+
: init?.type === utils_1.AST_NODE_TYPES.CallExpression && isComponentWrapperCallExpression(init);
|
|
524
|
+
if (!isComponent || !isExportedComponentName(node.id.name, node)) {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
componentCandidates.push({
|
|
528
|
+
componentName: node.id.name,
|
|
529
|
+
node,
|
|
530
|
+
propsType: getPropsTypeFromVariableDeclarator(node),
|
|
531
|
+
});
|
|
532
|
+
},
|
|
533
|
+
"Program:exit"() {
|
|
534
|
+
componentCandidates.forEach(checkCandidate);
|
|
535
|
+
},
|
|
536
|
+
};
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
//# sourceMappingURL=require-component-prop-contracts.js.map
|
package/src/lib/rules/require-component-prop-contracts/require-component-prop-contracts.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-component-prop-contracts.js","sourceRoot":"","sources":["../../../../../../../../libs/eslint/plugin-trackunit/src/lib/rules/require-component-prop-contracts/require-component-prop-contracts.ts"],"names":[],"mappings":"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","sourcesContent":["import { AST_NODE_TYPES, ESLintUtils, TSESLint, TSESTree } from \"@typescript-eslint/utils\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as ts from \"typescript\";\nimport { isReactComponent } from \"../../utils/ast-utils\";\nimport { getProjectMetadata, type MatchCriteria, projectMatchesCriteria } from \"../../utils/nx-utils\";\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/trackunit/manager/blob/main/libs/eslint/plugin-trackunit/src/lib/rules/${name}/${name}.ts`\n);\n\ntype Options = [\n {\n requiredProps?: ReadonlyArray<string>;\n requiredInterfaces?: ReadonlyArray<string>;\n onlyWhenProjectMatches?: MatchCriteria;\n },\n];\n\ntype MessageIds = \"missingPropContracts\";\n\ntype ComponentCandidate = {\n componentName: string;\n node: TSESTree.Node;\n propsType: TSESTree.TypeNode | null;\n};\n\ntype TypeDeclarationMaps = {\n interfaces: Map<string, TSESTree.TSInterfaceDeclaration>;\n typeAliases: Map<string, TSESTree.TSTypeAliasDeclaration>;\n};\n\ntype TypeCheckerContext = {\n checker: ts.TypeChecker;\n services: ReturnType<typeof ESLintUtils.getParserServices>;\n};\n\nconst componentWrapperNames = new Set([\"forwardRef\", \"memo\"]);\n\nconst publicEntrypointFileNames = [\"index.ts\", \"index.tsx\"];\n\ntype TypeSymbolInfo = {\n symbolName: string | undefined;\n declarations: Array<ts.Declaration>;\n};\n\nconst getTypeSymbolInfo = (type: ts.Type): TypeSymbolInfo => {\n try {\n return {\n symbolName: type.symbol.getName(),\n declarations: type.symbol.getDeclarations() ?? [],\n };\n } catch {\n return {\n symbolName: undefined,\n declarations: [],\n };\n }\n};\n\nconst getTypeReferenceName = (typeName: TSESTree.EntityName): string | null => {\n if (typeName.type === AST_NODE_TYPES.Identifier) {\n return typeName.name;\n }\n\n if (typeName.type === AST_NODE_TYPES.TSQualifiedName) {\n return typeName.right.name;\n }\n\n return null;\n};\n\nconst getCallExpressionName = (node: TSESTree.CallExpression): string | null => {\n const { callee } = node;\n\n if (callee.type === AST_NODE_TYPES.Identifier) {\n return callee.name;\n }\n\n if (callee.type === AST_NODE_TYPES.MemberExpression && callee.property.type === AST_NODE_TYPES.Identifier) {\n return callee.property.name;\n }\n\n return null;\n};\n\nconst isPascalCaseName = (name: string): boolean => /^[A-Z]/.test(name);\n\nconst normalizeFilePath = (filePath: string): string => path.normalize(filePath);\n\nconst getProjectRoot = (projectJsonPath: string): string => path.dirname(projectJsonPath);\n\nconst getPublicEntrypointPaths = (projectRoot: string): Array<string> =>\n publicEntrypointFileNames.map(fileName => path.join(projectRoot, \"src\", fileName));\n\nconst getModulePathCandidates = (fromFilePath: string, moduleSpecifier: string): Array<string> => {\n const modulePath = path.resolve(path.dirname(fromFilePath), moduleSpecifier);\n\n return [\n modulePath,\n `${modulePath}.ts`,\n `${modulePath}.tsx`,\n path.join(modulePath, \"index.ts\"),\n path.join(modulePath, \"index.tsx\"),\n ];\n};\n\nconst moduleSpecifierTargetsFile = (fromFilePath: string, moduleSpecifier: string, targetFilePath: string): boolean => {\n if (!moduleSpecifier.startsWith(\".\")) {\n return false;\n }\n\n const normalizedTargetFilePath = normalizeFilePath(targetFilePath);\n return getModulePathCandidates(fromFilePath, moduleSpecifier).some(\n candidatePath => normalizeFilePath(candidatePath) === normalizedTargetFilePath\n );\n};\n\nconst getSourceExportName = (specifier: ts.ExportSpecifier): string => {\n if (specifier.propertyName?.text === \"default\") {\n return specifier.name.text;\n }\n\n return specifier.propertyName?.text ?? specifier.name.text;\n};\n\nconst getPublicExportedNamesForFile = (\n projectRoot: string,\n filePath: string\n): { exportsAll: boolean; names: Set<string> } => {\n const result = {\n exportsAll: false,\n names: new Set<string>(),\n };\n\n for (const entrypointPath of getPublicEntrypointPaths(projectRoot)) {\n let sourceText: string;\n try {\n sourceText = fs.readFileSync(entrypointPath, \"utf8\");\n } catch {\n continue;\n }\n\n const sourceFile = ts.createSourceFile(entrypointPath, sourceText, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);\n\n for (const statement of sourceFile.statements) {\n if (!ts.isExportDeclaration(statement) || !statement.moduleSpecifier) {\n continue;\n }\n\n if (!ts.isStringLiteral(statement.moduleSpecifier)) {\n continue;\n }\n\n if (!moduleSpecifierTargetsFile(entrypointPath, statement.moduleSpecifier.text, filePath)) {\n continue;\n }\n\n if (!statement.exportClause) {\n result.exportsAll = true;\n continue;\n }\n\n if (ts.isNamedExports(statement.exportClause)) {\n statement.exportClause.elements.forEach(specifier => {\n result.names.add(getSourceExportName(specifier));\n });\n }\n }\n }\n\n return result;\n};\n\nconst isEntrypointFile = (projectRoot: string, filePath: string): boolean => {\n const normalizedFilePath = normalizeFilePath(filePath);\n return getPublicEntrypointPaths(projectRoot).some(\n entrypointPath => normalizeFilePath(entrypointPath) === normalizedFilePath\n );\n};\n\nconst isComponentWrapperCallExpression = (node: TSESTree.CallExpression): boolean => {\n const firstArgument = node.arguments[0];\n const callExpressionName = getCallExpressionName(node);\n\n if (!callExpressionName || !componentWrapperNames.has(callExpressionName)) {\n return false;\n }\n\n return (\n firstArgument?.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n firstArgument?.type === AST_NODE_TYPES.FunctionExpression\n );\n};\n\nconst getHeritageName = (heritage: TSESTree.TSInterfaceHeritage): string | null => {\n const { expression } = heritage;\n\n if (expression.type === AST_NODE_TYPES.Identifier) {\n return expression.name;\n }\n\n if (expression.type === AST_NODE_TYPES.MemberExpression && expression.property.type === AST_NODE_TYPES.Identifier) {\n return expression.property.name;\n }\n\n return null;\n};\n\nconst getParameterTypeAnnotation = (parameter: TSESTree.Parameter | undefined): TSESTree.TypeNode | null => {\n if (!parameter) {\n return null;\n }\n\n if (parameter.type === AST_NODE_TYPES.Identifier || parameter.type === AST_NODE_TYPES.ObjectPattern) {\n return parameter.typeAnnotation?.typeAnnotation ?? null;\n }\n\n if (parameter.type === AST_NODE_TYPES.AssignmentPattern) {\n const { left } = parameter;\n if (left.type === AST_NODE_TYPES.Identifier || left.type === AST_NODE_TYPES.ObjectPattern) {\n return left.typeAnnotation?.typeAnnotation ?? null;\n }\n }\n\n return null;\n};\n\nconst getFunctionPropsType = (\n node: TSESTree.FunctionDeclaration | TSESTree.ArrowFunctionExpression\n): TSESTree.TypeNode | null => getParameterTypeAnnotation(node.params[0]);\n\nconst getPropsTypeFromCallExpression = (node: TSESTree.CallExpression): TSESTree.TypeNode | null => {\n const typeArguments = node.typeArguments?.params ?? [];\n const firstArgument = node.arguments[0];\n\n if (\n firstArgument?.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n firstArgument?.type === AST_NODE_TYPES.FunctionExpression\n ) {\n const parameterType = getParameterTypeAnnotation(firstArgument.params[0]);\n if (parameterType) {\n return parameterType;\n }\n }\n\n if (getCallExpressionName(node) === \"forwardRef\") {\n return typeArguments[1] ?? null;\n }\n\n return typeArguments[0] ?? null;\n};\n\nconst getPropsTypeFromVariableDeclarator = (node: TSESTree.VariableDeclarator): TSESTree.TypeNode | null => {\n const { init } = node;\n\n if (!init) {\n return null;\n }\n\n if (init.type === AST_NODE_TYPES.ArrowFunctionExpression) {\n return getFunctionPropsType(init);\n }\n\n if (init.type === AST_NODE_TYPES.CallExpression) {\n return getPropsTypeFromCallExpression(init);\n }\n\n return null;\n};\n\nconst isExportedDeclaration = (node: TSESTree.Node): boolean => {\n const parent = node.parent;\n if (!parent) {\n return false;\n }\n\n if (\n parent.type === AST_NODE_TYPES.ExportNamedDeclaration ||\n parent.type === AST_NODE_TYPES.ExportDefaultDeclaration\n ) {\n return true;\n }\n\n const grandParent = parent.parent;\n return grandParent?.type === AST_NODE_TYPES.ExportNamedDeclaration;\n};\n\nconst typeHasRequiredInterface = (\n typeNode: TSESTree.TypeNode,\n requiredInterface: string,\n declarations: TypeDeclarationMaps,\n visitedTypeNames = new Set<string>()\n): boolean => {\n switch (typeNode.type) {\n case AST_NODE_TYPES.TSTypeReference: {\n const referenceName = getTypeReferenceName(typeNode.typeName);\n if (!referenceName) {\n return false;\n }\n\n if (referenceName === requiredInterface) {\n return true;\n }\n\n if (visitedTypeNames.has(referenceName)) {\n return false;\n }\n\n visitedTypeNames.add(referenceName);\n\n const interfaceDeclaration = declarations.interfaces.get(referenceName);\n if (interfaceDeclaration) {\n return interfaceExtendsRequiredInterface(\n interfaceDeclaration,\n requiredInterface,\n declarations,\n visitedTypeNames\n );\n }\n\n const typeAlias = declarations.typeAliases.get(referenceName);\n if (typeAlias) {\n return typeHasRequiredInterface(typeAlias.typeAnnotation, requiredInterface, declarations, visitedTypeNames);\n }\n\n return false;\n }\n\n case AST_NODE_TYPES.TSIntersectionType:\n return typeNode.types.some(part =>\n typeHasRequiredInterface(part, requiredInterface, declarations, new Set(visitedTypeNames))\n );\n\n case AST_NODE_TYPES.TSUnionType:\n return typeNode.types.every(part =>\n typeHasRequiredInterface(part, requiredInterface, declarations, new Set(visitedTypeNames))\n );\n\n default:\n return false;\n }\n};\n\nconst interfaceExtendsRequiredInterface = (\n declaration: TSESTree.TSInterfaceDeclaration,\n requiredInterface: string,\n declarations: TypeDeclarationMaps,\n visitedTypeNames: Set<string>\n): boolean => {\n return declaration.extends.some(heritage => {\n const heritageName = getHeritageName(heritage);\n if (!heritageName) {\n return false;\n }\n\n if (heritageName === requiredInterface) {\n return true;\n }\n\n if (visitedTypeNames.has(heritageName)) {\n return false;\n }\n\n visitedTypeNames.add(heritageName);\n\n const interfaceDeclaration = declarations.interfaces.get(heritageName);\n if (interfaceDeclaration) {\n return interfaceExtendsRequiredInterface(interfaceDeclaration, requiredInterface, declarations, visitedTypeNames);\n }\n\n const typeAlias = declarations.typeAliases.get(heritageName);\n if (typeAlias) {\n return typeHasRequiredInterface(typeAlias.typeAnnotation, requiredInterface, declarations, visitedTypeNames);\n }\n\n return false;\n });\n};\n\nconst interfaceHasRequiredProp = (\n declaration: TSESTree.TSInterfaceDeclaration,\n requiredProp: string,\n declarations: TypeDeclarationMaps,\n visitedTypeNames: Set<string>\n): boolean => {\n const hasOwnProp = declaration.body.body.some(member => {\n if (member.type !== AST_NODE_TYPES.TSPropertySignature) {\n return false;\n }\n\n return member.key.type === AST_NODE_TYPES.Identifier && member.key.name === requiredProp;\n });\n\n if (hasOwnProp) {\n return true;\n }\n\n return declaration.extends.some(heritage => {\n const heritageName = getHeritageName(heritage);\n if (!heritageName || visitedTypeNames.has(heritageName)) {\n return false;\n }\n\n visitedTypeNames.add(heritageName);\n\n const interfaceDeclaration = declarations.interfaces.get(heritageName);\n if (interfaceDeclaration) {\n return interfaceHasRequiredProp(interfaceDeclaration, requiredProp, declarations, visitedTypeNames);\n }\n\n const typeAlias = declarations.typeAliases.get(heritageName);\n if (typeAlias) {\n return typeHasRequiredProp(typeAlias.typeAnnotation, requiredProp, declarations, visitedTypeNames);\n }\n\n return false;\n });\n};\n\nconst typeLiteralHasRequiredProp = (typeNode: TSESTree.TSTypeLiteral, requiredProp: string): boolean => {\n return typeNode.members.some(member => {\n if (member.type !== AST_NODE_TYPES.TSPropertySignature) {\n return false;\n }\n\n return member.key.type === AST_NODE_TYPES.Identifier && member.key.name === requiredProp;\n });\n};\n\nconst typeHasRequiredProp = (\n typeNode: TSESTree.TypeNode,\n requiredProp: string,\n declarations: TypeDeclarationMaps,\n visitedTypeNames = new Set<string>()\n): boolean => {\n switch (typeNode.type) {\n case AST_NODE_TYPES.TSTypeLiteral:\n return typeLiteralHasRequiredProp(typeNode, requiredProp);\n\n case AST_NODE_TYPES.TSTypeReference: {\n const referenceName = getTypeReferenceName(typeNode.typeName);\n if (!referenceName || visitedTypeNames.has(referenceName)) {\n return false;\n }\n\n visitedTypeNames.add(referenceName);\n\n const interfaceDeclaration = declarations.interfaces.get(referenceName);\n if (interfaceDeclaration) {\n return interfaceHasRequiredProp(interfaceDeclaration, requiredProp, declarations, visitedTypeNames);\n }\n\n const typeAlias = declarations.typeAliases.get(referenceName);\n if (typeAlias) {\n return typeHasRequiredProp(typeAlias.typeAnnotation, requiredProp, declarations, visitedTypeNames);\n }\n\n return false;\n }\n\n case AST_NODE_TYPES.TSIntersectionType:\n return typeNode.types.some(part =>\n typeHasRequiredProp(part, requiredProp, declarations, new Set(visitedTypeNames))\n );\n\n case AST_NODE_TYPES.TSUnionType:\n return typeNode.types.every(part =>\n typeHasRequiredProp(part, requiredProp, declarations, new Set(visitedTypeNames))\n );\n\n default:\n return false;\n }\n};\n\nconst getTypeCheckerContext = (context: TSESLint.RuleContext<MessageIds, Options>): TypeCheckerContext | null => {\n try {\n const services = ESLintUtils.getParserServices(context);\n return {\n checker: services.program.getTypeChecker(),\n services,\n };\n } catch {\n return null;\n }\n};\n\nexport const checkerTypeHasRequiredInterface = (\n type: ts.Type,\n requiredInterface: string,\n checker: ts.TypeChecker,\n visitedTypeNames = new Set<string>()\n): boolean => {\n if (type.isUnion()) {\n return type.types.every(part => checkerTypeHasRequiredInterface(part, requiredInterface, checker, new Set()));\n }\n\n if (type.isIntersection()) {\n return type.types.some(part =>\n checkerTypeHasRequiredInterface(part, requiredInterface, checker, new Set(visitedTypeNames))\n );\n }\n\n const { symbolName, declarations } = getTypeSymbolInfo(type);\n const aliasSymbolName = type.aliasSymbol?.getName();\n\n if (symbolName === requiredInterface || aliasSymbolName === requiredInterface) {\n return true;\n }\n\n const visitKey = aliasSymbolName ?? symbolName;\n if (visitKey) {\n if (visitedTypeNames.has(visitKey)) {\n return false;\n }\n visitedTypeNames.add(visitKey);\n }\n\n if (\n type.aliasTypeArguments?.some(typeArgument =>\n checkerTypeHasRequiredInterface(typeArgument, requiredInterface, checker)\n )\n ) {\n return true;\n }\n\n if (type.isClassOrInterface()) {\n const baseTypes = checker.getBaseTypes(type);\n if (\n baseTypes.some(baseType =>\n checkerTypeHasRequiredInterface(baseType, requiredInterface, checker, visitedTypeNames)\n )\n ) {\n return true;\n }\n }\n\n return declarations.some(declaration => {\n if (!ts.isInterfaceDeclaration(declaration) || !declaration.heritageClauses) {\n return false;\n }\n\n return declaration.heritageClauses.some(heritageClause =>\n heritageClause.types.some(heritageType => {\n const heritageInterfaceType = checker.getTypeAtLocation(heritageType);\n return checkerTypeHasRequiredInterface(heritageInterfaceType, requiredInterface, checker, visitedTypeNames);\n })\n );\n });\n};\n\nconst checkerTypeHasRequiredProp = (type: ts.Type, requiredProp: string, checker: ts.TypeChecker): boolean => {\n if (type.isUnion()) {\n return type.types.every(part => checkerTypeHasRequiredProp(part, requiredProp, checker));\n }\n\n return Boolean(type.getProperty(requiredProp));\n};\n\nconst getCheckerType = (typeNode: TSESTree.TypeNode, typeCheckerContext: TypeCheckerContext | null): ts.Type | null => {\n if (!typeCheckerContext) {\n return null;\n }\n\n const tsNode = typeCheckerContext.services.esTreeNodeToTSNodeMap.get(typeNode);\n return typeCheckerContext.checker.getTypeAtLocation(tsNode);\n};\n\nexport const requireComponentPropContracts = createRule<Options, MessageIds>({\n name: \"require-component-prop-contracts\",\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Require exported React component props to include configured prop names or extend configured interfaces.\",\n },\n schema: [\n {\n type: \"object\",\n properties: {\n requiredProps: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Prop names every exported component props type must include.\",\n },\n requiredInterfaces: {\n type: \"array\",\n items: { type: \"string\" },\n description: \"Interface names every exported component props type must reference or extend.\",\n },\n onlyWhenProjectMatches: {\n type: \"object\",\n properties: {\n tags: {\n type: \"array\",\n items: { type: \"string\" },\n },\n targets: {\n type: \"array\",\n items: { type: \"string\" },\n },\n tagsMatchMode: {\n type: \"string\",\n enum: [\"some\", \"every\"],\n },\n targetsMatchMode: {\n type: \"string\",\n enum: [\"some\", \"every\"],\n },\n },\n additionalProperties: false,\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n missingPropContracts:\n \"Public component '{{componentName}}' props must satisfy these contracts: {{missingContracts}}.\",\n },\n },\n defaultOptions: [\n {\n requiredProps: [],\n requiredInterfaces: [],\n onlyWhenProjectMatches: { tags: [\"publish:npm\"] },\n },\n ],\n create(context, [options]) {\n const filename = context.filename;\n if (!filename.endsWith(\".tsx\")) {\n return {};\n }\n\n const projectMetadata = getProjectMetadata(filename);\n const projectCriteria = options.onlyWhenProjectMatches ?? { tags: [\"publish:npm\"] };\n if (!projectMetadata || !projectMatchesCriteria(projectMetadata, projectCriteria)) {\n return {};\n }\n\n const requiredProps = options.requiredProps ?? [];\n const requiredInterfaces = options.requiredInterfaces ?? [];\n if (requiredProps.length === 0 && requiredInterfaces.length === 0) {\n return {};\n }\n\n const declarations: TypeDeclarationMaps = {\n interfaces: new Map(),\n typeAliases: new Map(),\n };\n const typeCheckerContext = getTypeCheckerContext(context);\n const exportedIdentifiers = new Set<string>();\n const componentCandidates: Array<ComponentCandidate> = [];\n const projectRoot = getProjectRoot(projectMetadata.projectJsonPath);\n const publicExports = getPublicExportedNamesForFile(projectRoot, filename);\n const isCurrentFileEntrypoint = isEntrypointFile(projectRoot, filename);\n\n const isExportedComponentName = (componentName: string, node: TSESTree.Node): boolean => {\n if (\n !isPascalCaseName(componentName) ||\n (!isExportedDeclaration(node) && !exportedIdentifiers.has(componentName))\n ) {\n return false;\n }\n\n return isCurrentFileEntrypoint || publicExports.exportsAll || publicExports.names.has(componentName);\n };\n\n const checkCandidate = (candidate: ComponentCandidate): void => {\n const checkerType = candidate.propsType ? getCheckerType(candidate.propsType, typeCheckerContext) : null;\n const missingProps = requiredProps.filter(\n requiredProp =>\n !candidate.propsType ||\n (checkerType && typeCheckerContext\n ? !checkerTypeHasRequiredProp(checkerType, requiredProp, typeCheckerContext.checker)\n : !typeHasRequiredProp(candidate.propsType, requiredProp, declarations, new Set()))\n );\n const missingInterfaces = requiredInterfaces.filter(\n requiredInterface =>\n !candidate.propsType ||\n (checkerType && typeCheckerContext\n ? !checkerTypeHasRequiredInterface(checkerType, requiredInterface, typeCheckerContext.checker)\n : !typeHasRequiredInterface(candidate.propsType, requiredInterface, declarations, new Set()))\n );\n const missingContracts = [...missingProps, ...missingInterfaces];\n\n if (missingContracts.length === 0) {\n return;\n }\n\n context.report({\n node: candidate.node,\n messageId: \"missingPropContracts\",\n data: {\n componentName: candidate.componentName,\n missingContracts: missingContracts.join(\", \"),\n },\n });\n };\n\n return {\n ExportNamedDeclaration(node: TSESTree.ExportNamedDeclaration) {\n for (const specifier of node.specifiers) {\n if (specifier.local.type === AST_NODE_TYPES.Identifier) {\n exportedIdentifiers.add(specifier.local.name);\n }\n }\n },\n\n ExportDefaultDeclaration(node: TSESTree.ExportDefaultDeclaration) {\n if (node.declaration.type === AST_NODE_TYPES.Identifier) {\n exportedIdentifiers.add(node.declaration.name);\n }\n },\n\n TSInterfaceDeclaration(node: TSESTree.TSInterfaceDeclaration) {\n declarations.interfaces.set(node.id.name, node);\n },\n\n TSTypeAliasDeclaration(node: TSESTree.TSTypeAliasDeclaration) {\n declarations.typeAliases.set(node.id.name, node);\n },\n\n FunctionDeclaration(node: TSESTree.FunctionDeclaration) {\n const componentName = node.id?.name;\n if (!componentName || !isReactComponent(node) || !isExportedComponentName(componentName, node)) {\n return;\n }\n\n componentCandidates.push({\n componentName,\n node,\n propsType: getFunctionPropsType(node),\n });\n },\n\n VariableDeclarator(node: TSESTree.VariableDeclarator) {\n if (node.id.type !== AST_NODE_TYPES.Identifier) {\n return;\n }\n\n const { init } = node;\n const isComponent =\n init?.type === AST_NODE_TYPES.ArrowFunctionExpression\n ? isReactComponent(init)\n : init?.type === AST_NODE_TYPES.CallExpression && isComponentWrapperCallExpression(init);\n\n if (!isComponent || !isExportedComponentName(node.id.name, node)) {\n return;\n }\n\n componentCandidates.push({\n componentName: node.id.name,\n node,\n propsType: getPropsTypeFromVariableDeclarator(node),\n });\n },\n\n \"Program:exit\"() {\n componentCandidates.forEach(checkCandidate);\n },\n };\n },\n});\n"]}
|