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 util = __importStar(require("../util"));
|
24 | exports.default = util.createRule({
|
25 | name: 'method-signature-style',
|
26 | meta: {
|
27 | type: 'suggestion',
|
28 | docs: {
|
29 | description: 'Enforces using a particular method signature syntax.',
|
30 | recommended: false,
|
31 | },
|
32 | fixable: 'code',
|
33 | messages: {
|
34 | errorMethod: 'Shorthand method signature is forbidden. Use a function property instead.',
|
35 | errorProperty: 'Function property signature is forbidden. Use a method shorthand instead.',
|
36 | },
|
37 | schema: [
|
38 | {
|
39 | enum: ['property', 'method'],
|
40 | },
|
41 | ],
|
42 | },
|
43 | defaultOptions: ['property'],
|
44 | create(context, [mode]) {
|
45 | const sourceCode = context.getSourceCode();
|
46 | function getMethodKey(node) {
|
47 | let key = sourceCode.getText(node.key);
|
48 | if (node.computed) {
|
49 | key = `[${key}]`;
|
50 | }
|
51 | if (node.optional) {
|
52 | key = `${key}?`;
|
53 | }
|
54 | if (node.readonly) {
|
55 | key = `readonly ${key}`;
|
56 | }
|
57 | return key;
|
58 | }
|
59 | function getMethodParams(node) {
|
60 | let params = '()';
|
61 | if (node.params.length > 0) {
|
62 | const openingParen = util.nullThrows(sourceCode.getTokenBefore(node.params[0], util.isOpeningParenToken), 'Missing opening paren before first parameter');
|
63 | const closingParen = util.nullThrows(sourceCode.getTokenAfter(node.params[node.params.length - 1], util.isClosingParenToken), 'Missing closing paren after last parameter');
|
64 | params = sourceCode.text.substring(openingParen.range[0], closingParen.range[1]);
|
65 | }
|
66 | if (node.typeParameters != null) {
|
67 | const typeParams = sourceCode.getText(node.typeParameters);
|
68 | params = `${typeParams}${params}`;
|
69 | }
|
70 | return params;
|
71 | }
|
72 | function getMethodReturnType(node) {
|
73 | return node.returnType == null
|
74 | ?
|
75 |
|
76 | 'any'
|
77 | : sourceCode.getText(node.returnType.typeAnnotation);
|
78 | }
|
79 | function getDelimiter(node) {
|
80 | const lastToken = sourceCode.getLastToken(node);
|
81 | if (lastToken &&
|
82 | (util.isSemicolonToken(lastToken) || util.isCommaToken(lastToken))) {
|
83 | return lastToken.value;
|
84 | }
|
85 | return '';
|
86 | }
|
87 | function isNodeParentModuleDeclaration(node) {
|
88 | if (!node.parent) {
|
89 | return false;
|
90 | }
|
91 | if (node.parent.type === utils_1.AST_NODE_TYPES.TSModuleDeclaration) {
|
92 | return true;
|
93 | }
|
94 | if (node.parent.type === utils_1.AST_NODE_TYPES.Program) {
|
95 | return false;
|
96 | }
|
97 | return isNodeParentModuleDeclaration(node.parent);
|
98 | }
|
99 | return Object.assign(Object.assign({}, (mode === 'property' && {
|
100 | TSMethodSignature(methodNode) {
|
101 | const parent = methodNode.parent;
|
102 | const members = (parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.AST_NODE_TYPES.TSInterfaceBody
|
103 | ? parent.body
|
104 | : (parent === null || parent === void 0 ? void 0 : parent.type) === utils_1.AST_NODE_TYPES.TSTypeLiteral
|
105 | ? parent.members
|
106 | : [];
|
107 | const duplicatedKeyMethodNodes = members.filter((element) => element.type === utils_1.AST_NODE_TYPES.TSMethodSignature &&
|
108 | element !== methodNode &&
|
109 | getMethodKey(element) === getMethodKey(methodNode));
|
110 | const isParentModule = isNodeParentModuleDeclaration(methodNode);
|
111 | if (duplicatedKeyMethodNodes.length > 0) {
|
112 | if (isParentModule) {
|
113 | context.report({
|
114 | node: methodNode,
|
115 | messageId: 'errorMethod',
|
116 | });
|
117 | }
|
118 | else {
|
119 | context.report({
|
120 | node: methodNode,
|
121 | messageId: 'errorMethod',
|
122 | *fix(fixer) {
|
123 | const methodNodes = [
|
124 | methodNode,
|
125 | ...duplicatedKeyMethodNodes,
|
126 | ].sort((a, b) => (a.range[0] < b.range[0] ? -1 : 1));
|
127 | const typeString = methodNodes
|
128 | .map(node => {
|
129 | const params = getMethodParams(node);
|
130 | const returnType = getMethodReturnType(node);
|
131 | return `(${params} => ${returnType})`;
|
132 | })
|
133 | .join(' & ');
|
134 | const key = getMethodKey(methodNode);
|
135 | const delimiter = getDelimiter(methodNode);
|
136 | yield fixer.replaceText(methodNode, `${key}: ${typeString}${delimiter}`);
|
137 | for (const node of duplicatedKeyMethodNodes) {
|
138 | const lastToken = sourceCode.getLastToken(node);
|
139 | if (lastToken) {
|
140 | const nextToken = sourceCode.getTokenAfter(lastToken);
|
141 | if (nextToken) {
|
142 | yield fixer.remove(node);
|
143 | yield fixer.replaceTextRange([lastToken.range[1], nextToken.range[0]], '');
|
144 | }
|
145 | }
|
146 | }
|
147 | },
|
148 | });
|
149 | }
|
150 | return;
|
151 | }
|
152 | if (isParentModule) {
|
153 | context.report({
|
154 | node: methodNode,
|
155 | messageId: 'errorMethod',
|
156 | });
|
157 | }
|
158 | else {
|
159 | context.report({
|
160 | node: methodNode,
|
161 | messageId: 'errorMethod',
|
162 | fix: fixer => {
|
163 | const key = getMethodKey(methodNode);
|
164 | const params = getMethodParams(methodNode);
|
165 | const returnType = getMethodReturnType(methodNode);
|
166 | const delimiter = getDelimiter(methodNode);
|
167 | return fixer.replaceText(methodNode, `${key}: ${params} => ${returnType}${delimiter}`);
|
168 | },
|
169 | });
|
170 | }
|
171 | },
|
172 | })), (mode === 'method' && {
|
173 | TSPropertySignature(propertyNode) {
|
174 | var _a;
|
175 | const typeNode = (_a = propertyNode.typeAnnotation) === null || _a === void 0 ? void 0 : _a.typeAnnotation;
|
176 | if ((typeNode === null || typeNode === void 0 ? void 0 : typeNode.type) !== utils_1.AST_NODE_TYPES.TSFunctionType) {
|
177 | return;
|
178 | }
|
179 | context.report({
|
180 | node: propertyNode,
|
181 | messageId: 'errorProperty',
|
182 | fix: fixer => {
|
183 | const key = getMethodKey(propertyNode);
|
184 | const params = getMethodParams(typeNode);
|
185 | const returnType = getMethodReturnType(typeNode);
|
186 | const delimiter = getDelimiter(propertyNode);
|
187 | return fixer.replaceText(propertyNode, `${key}${params}: ${returnType}${delimiter}`);
|
188 | },
|
189 | });
|
190 | },
|
191 | }));
|
192 | },
|
193 | });
|
194 |
|
\ | No newline at end of file |