1 | "use strict";
|
2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3 | if (k2 === undefined) k2 = k;
|
4 | Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
5 | }) : (function(o, m, k, k2) {
|
6 | if (k2 === undefined) k2 = k;
|
7 | o[k2] = m[k];
|
8 | }));
|
9 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
10 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
11 | }) : function(o, v) {
|
12 | o["default"] = v;
|
13 | });
|
14 | var __importStar = (this && this.__importStar) || function (mod) {
|
15 | if (mod && mod.__esModule) return mod;
|
16 | var result = {};
|
17 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
18 | __setModuleDefault(result, mod);
|
19 | return result;
|
20 | };
|
21 | Object.defineProperty(exports, "__esModule", { value: true });
|
22 | const utils_1 = require("@typescript-eslint/utils");
|
23 | const scope_manager_1 = require("@typescript-eslint/scope-manager");
|
24 | const util = __importStar(require("../util"));
|
25 | const explicitReturnTypeUtils_1 = require("../util/explicitReturnTypeUtils");
|
26 | exports.default = util.createRule({
|
27 | name: 'explicit-module-boundary-types',
|
28 | meta: {
|
29 | type: 'problem',
|
30 | docs: {
|
31 | description: "Require explicit return and argument types on exported functions' and classes' public class methods",
|
32 | recommended: false,
|
33 | },
|
34 | messages: {
|
35 | missingReturnType: 'Missing return type on function.',
|
36 | missingArgType: "Argument '{{name}}' should be typed.",
|
37 | missingArgTypeUnnamed: '{{type}} argument should be typed.',
|
38 | anyTypedArg: "Argument '{{name}}' should be typed with a non-any type.",
|
39 | anyTypedArgUnnamed: '{{type}} argument should be typed with a non-any type.',
|
40 | },
|
41 | schema: [
|
42 | {
|
43 | type: 'object',
|
44 | properties: {
|
45 | allowArgumentsExplicitlyTypedAsAny: {
|
46 | type: 'boolean',
|
47 | },
|
48 | allowDirectConstAssertionInArrowFunctions: {
|
49 | type: 'boolean',
|
50 | },
|
51 | allowedNames: {
|
52 | type: 'array',
|
53 | items: {
|
54 | type: 'string',
|
55 | },
|
56 | },
|
57 | allowHigherOrderFunctions: {
|
58 | type: 'boolean',
|
59 | },
|
60 | allowTypedFunctionExpressions: {
|
61 | type: 'boolean',
|
62 | },
|
63 |
|
64 | shouldTrackReferences: {
|
65 | type: 'boolean',
|
66 | },
|
67 | },
|
68 | additionalProperties: false,
|
69 | },
|
70 | ],
|
71 | },
|
72 | defaultOptions: [
|
73 | {
|
74 | allowArgumentsExplicitlyTypedAsAny: false,
|
75 | allowDirectConstAssertionInArrowFunctions: true,
|
76 | allowedNames: [],
|
77 | allowHigherOrderFunctions: true,
|
78 | allowTypedFunctionExpressions: true,
|
79 | },
|
80 | ],
|
81 | create(context, [options]) {
|
82 | const sourceCode = context.getSourceCode();
|
83 |
|
84 | const checkedFunctions = new Set();
|
85 |
|
86 | const foundFunctions = [];
|
87 |
|
88 |
|
89 | const alreadyVisited = new Set();
|
90 | |
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 | return {
|
101 | ExportDefaultDeclaration(node) {
|
102 | checkNode(node.declaration);
|
103 | },
|
104 | 'ExportNamedDeclaration:not([source])'(node) {
|
105 | if (node.declaration) {
|
106 | checkNode(node.declaration);
|
107 | }
|
108 | else {
|
109 | for (const specifier of node.specifiers) {
|
110 | followReference(specifier.local);
|
111 | }
|
112 | }
|
113 | },
|
114 | TSExportAssignment(node) {
|
115 | checkNode(node.expression);
|
116 | },
|
117 | 'ArrowFunctionExpression, FunctionDeclaration, FunctionExpression'(node) {
|
118 | foundFunctions.push(node);
|
119 | },
|
120 | 'Program:exit'() {
|
121 | for (const func of foundFunctions) {
|
122 | if (isExportedHigherOrderFunction(func)) {
|
123 | checkNode(func);
|
124 | }
|
125 | }
|
126 | },
|
127 | };
|
128 | function checkParameters(node) {
|
129 | function checkParameter(param) {
|
130 | function report(namedMessageId, unnamedMessageId) {
|
131 | if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
|
132 | context.report({
|
133 | node: param,
|
134 | messageId: namedMessageId,
|
135 | data: { name: param.name },
|
136 | });
|
137 | }
|
138 | else if (param.type === utils_1.AST_NODE_TYPES.ArrayPattern) {
|
139 | context.report({
|
140 | node: param,
|
141 | messageId: unnamedMessageId,
|
142 | data: { type: 'Array pattern' },
|
143 | });
|
144 | }
|
145 | else if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
|
146 | context.report({
|
147 | node: param,
|
148 | messageId: unnamedMessageId,
|
149 | data: { type: 'Object pattern' },
|
150 | });
|
151 | }
|
152 | else if (param.type === utils_1.AST_NODE_TYPES.RestElement) {
|
153 | if (param.argument.type === utils_1.AST_NODE_TYPES.Identifier) {
|
154 | context.report({
|
155 | node: param,
|
156 | messageId: namedMessageId,
|
157 | data: { name: param.argument.name },
|
158 | });
|
159 | }
|
160 | else {
|
161 | context.report({
|
162 | node: param,
|
163 | messageId: unnamedMessageId,
|
164 | data: { type: 'Rest' },
|
165 | });
|
166 | }
|
167 | }
|
168 | }
|
169 | switch (param.type) {
|
170 | case utils_1.AST_NODE_TYPES.ArrayPattern:
|
171 | case utils_1.AST_NODE_TYPES.Identifier:
|
172 | case utils_1.AST_NODE_TYPES.ObjectPattern:
|
173 | case utils_1.AST_NODE_TYPES.RestElement:
|
174 | if (!param.typeAnnotation) {
|
175 | report('missingArgType', 'missingArgTypeUnnamed');
|
176 | }
|
177 | else if (options.allowArgumentsExplicitlyTypedAsAny !== true &&
|
178 | param.typeAnnotation.typeAnnotation.type ===
|
179 | utils_1.AST_NODE_TYPES.TSAnyKeyword) {
|
180 | report('anyTypedArg', 'anyTypedArgUnnamed');
|
181 | }
|
182 | return;
|
183 | case utils_1.AST_NODE_TYPES.TSParameterProperty:
|
184 | return checkParameter(param.parameter);
|
185 | case utils_1.AST_NODE_TYPES.AssignmentPattern:
|
186 | return;
|
187 | }
|
188 | }
|
189 | for (const arg of node.params) {
|
190 | checkParameter(arg);
|
191 | }
|
192 | }
|
193 | |
194 |
|
195 |
|
196 | function isAllowedName(node) {
|
197 | var _a;
|
198 | if (!node || !options.allowedNames || !options.allowedNames.length) {
|
199 | return false;
|
200 | }
|
201 | if (node.type === utils_1.AST_NODE_TYPES.VariableDeclarator ||
|
202 | node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
|
203 | return (((_a = node.id) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.Identifier &&
|
204 | options.allowedNames.includes(node.id.name));
|
205 | }
|
206 | else if (node.type === utils_1.AST_NODE_TYPES.MethodDefinition ||
|
207 | node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition ||
|
208 | (node.type === utils_1.AST_NODE_TYPES.Property && node.method) ||
|
209 | node.type === utils_1.AST_NODE_TYPES.PropertyDefinition) {
|
210 | if (node.key.type === utils_1.AST_NODE_TYPES.Literal &&
|
211 | typeof node.key.value === 'string') {
|
212 | return options.allowedNames.includes(node.key.value);
|
213 | }
|
214 | if (node.key.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
|
215 | node.key.expressions.length === 0) {
|
216 | return options.allowedNames.includes(node.key.quasis[0].value.raw);
|
217 | }
|
218 | if (!node.computed && node.key.type === utils_1.AST_NODE_TYPES.Identifier) {
|
219 | return options.allowedNames.includes(node.key.name);
|
220 | }
|
221 | }
|
222 | return false;
|
223 | }
|
224 | function isExportedHigherOrderFunction(node) {
|
225 | var _a;
|
226 | let current = node.parent;
|
227 | while (current) {
|
228 | if (current.type === utils_1.AST_NODE_TYPES.ReturnStatement) {
|
229 |
|
230 | current = (_a = current.parent) === null || _a === void 0 ? void 0 : _a.parent;
|
231 | continue;
|
232 | }
|
233 | if (!util.isFunction(current) ||
|
234 | !(0, explicitReturnTypeUtils_1.doesImmediatelyReturnFunctionExpression)(current)) {
|
235 | return false;
|
236 | }
|
237 | if (checkedFunctions.has(current)) {
|
238 | return true;
|
239 | }
|
240 | current = current.parent;
|
241 | }
|
242 | return false;
|
243 | }
|
244 | function followReference(node) {
|
245 | const scope = context.getScope();
|
246 | const variable = scope.set.get(node.name);
|
247 | if (!variable) {
|
248 | return;
|
249 | }
|
250 |
|
251 | for (const definition of variable.defs) {
|
252 |
|
253 | if ([
|
254 | scope_manager_1.DefinitionType.ImplicitGlobalVariable,
|
255 | scope_manager_1.DefinitionType.ImportBinding,
|
256 | scope_manager_1.DefinitionType.CatchClause,
|
257 | scope_manager_1.DefinitionType.Parameter,
|
258 | ].includes(definition.type)) {
|
259 | continue;
|
260 | }
|
261 | checkNode(definition.node);
|
262 | }
|
263 |
|
264 | for (const reference of variable.references) {
|
265 | if (
|
266 |
|
267 | !reference.init &&
|
268 | reference.writeExpr) {
|
269 | checkNode(reference.writeExpr);
|
270 | }
|
271 | }
|
272 | }
|
273 | function checkNode(node) {
|
274 | if (node == null || alreadyVisited.has(node)) {
|
275 | return;
|
276 | }
|
277 | alreadyVisited.add(node);
|
278 | switch (node.type) {
|
279 | case utils_1.AST_NODE_TYPES.ArrowFunctionExpression:
|
280 | case utils_1.AST_NODE_TYPES.FunctionExpression:
|
281 | return checkFunctionExpression(node);
|
282 | case utils_1.AST_NODE_TYPES.ArrayExpression:
|
283 | for (const element of node.elements) {
|
284 | checkNode(element);
|
285 | }
|
286 | return;
|
287 | case utils_1.AST_NODE_TYPES.PropertyDefinition:
|
288 | if (node.accessibility === 'private' ||
|
289 | node.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) {
|
290 | return;
|
291 | }
|
292 | return checkNode(node.value);
|
293 | case utils_1.AST_NODE_TYPES.ClassDeclaration:
|
294 | case utils_1.AST_NODE_TYPES.ClassExpression:
|
295 | for (const element of node.body.body) {
|
296 | checkNode(element);
|
297 | }
|
298 | return;
|
299 | case utils_1.AST_NODE_TYPES.FunctionDeclaration:
|
300 | return checkFunction(node);
|
301 | case utils_1.AST_NODE_TYPES.MethodDefinition:
|
302 | case utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition:
|
303 | if (node.accessibility === 'private' ||
|
304 | node.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) {
|
305 | return;
|
306 | }
|
307 | return checkNode(node.value);
|
308 | case utils_1.AST_NODE_TYPES.Identifier:
|
309 | return followReference(node);
|
310 | case utils_1.AST_NODE_TYPES.ObjectExpression:
|
311 | for (const property of node.properties) {
|
312 | checkNode(property);
|
313 | }
|
314 | return;
|
315 | case utils_1.AST_NODE_TYPES.Property:
|
316 | return checkNode(node.value);
|
317 | case utils_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression:
|
318 | return checkEmptyBodyFunctionExpression(node);
|
319 | case utils_1.AST_NODE_TYPES.VariableDeclaration:
|
320 | for (const declaration of node.declarations) {
|
321 | checkNode(declaration);
|
322 | }
|
323 | return;
|
324 | case utils_1.AST_NODE_TYPES.VariableDeclarator:
|
325 | return checkNode(node.init);
|
326 | }
|
327 | }
|
328 | function checkEmptyBodyFunctionExpression(node) {
|
329 | var _a, _b, _c;
|
330 | const isConstructor = ((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
331 | node.parent.kind === 'constructor';
|
332 | const isSetAccessor = (((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition ||
|
333 | ((_c = node.parent) === null || _c === void 0 ? void 0 : _c.type) === utils_1.AST_NODE_TYPES.MethodDefinition) &&
|
334 | node.parent.kind === 'set';
|
335 | if (!isConstructor && !isSetAccessor && !node.returnType) {
|
336 | context.report({
|
337 | node,
|
338 | messageId: 'missingReturnType',
|
339 | });
|
340 | }
|
341 | checkParameters(node);
|
342 | }
|
343 | function checkFunctionExpression(node) {
|
344 | if (checkedFunctions.has(node)) {
|
345 | return;
|
346 | }
|
347 | checkedFunctions.add(node);
|
348 | if (isAllowedName(node.parent) ||
|
349 | (0, explicitReturnTypeUtils_1.isTypedFunctionExpression)(node, options) ||
|
350 | (0, explicitReturnTypeUtils_1.ancestorHasReturnType)(node)) {
|
351 | return;
|
352 | }
|
353 | (0, explicitReturnTypeUtils_1.checkFunctionExpressionReturnType)(node, options, sourceCode, loc => {
|
354 | context.report({
|
355 | node,
|
356 | loc,
|
357 | messageId: 'missingReturnType',
|
358 | });
|
359 | });
|
360 | checkParameters(node);
|
361 | }
|
362 | function checkFunction(node) {
|
363 | if (checkedFunctions.has(node)) {
|
364 | return;
|
365 | }
|
366 | checkedFunctions.add(node);
|
367 | if (isAllowedName(node) || (0, explicitReturnTypeUtils_1.ancestorHasReturnType)(node)) {
|
368 | return;
|
369 | }
|
370 | (0, explicitReturnTypeUtils_1.checkFunctionReturnType)(node, options, sourceCode, loc => {
|
371 | context.report({
|
372 | node,
|
373 | loc,
|
374 | messageId: 'missingReturnType',
|
375 | });
|
376 | });
|
377 | checkParameters(node);
|
378 | }
|
379 | },
|
380 | });
|
381 |
|
\ | No newline at end of file |