UNPKG

9.96 kBJavaScriptView Raw
1"use strict";
2var __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}));
9var __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});
14var __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};
21Object.defineProperty(exports, "__esModule", { value: true });
22const util = __importStar(require("../util"));
23const util_1 = require("../util");
24const definition = {
25 type: 'object',
26 properties: {
27 before: { type: 'boolean' },
28 after: { type: 'boolean' },
29 },
30 additionalProperties: false,
31};
32function createRules(options) {
33 var _a;
34 const globals = Object.assign(Object.assign({}, ((options === null || options === void 0 ? void 0 : options.before) !== undefined ? { before: options.before } : {})), ((options === null || options === void 0 ? void 0 : options.after) !== undefined ? { after: options.after } : {}));
35 const override = (_a = options === null || options === void 0 ? void 0 : options.overrides) !== null && _a !== void 0 ? _a : {};
36 const colon = Object.assign(Object.assign({ before: false, after: true }, globals), override === null || override === void 0 ? void 0 : override.colon);
37 const arrow = Object.assign(Object.assign({ before: true, after: true }, globals), override === null || override === void 0 ? void 0 : override.arrow);
38 return {
39 colon: colon,
40 arrow: arrow,
41 variable: Object.assign(Object.assign({}, colon), override === null || override === void 0 ? void 0 : override.variable),
42 property: Object.assign(Object.assign({}, colon), override === null || override === void 0 ? void 0 : override.property),
43 parameter: Object.assign(Object.assign({}, colon), override === null || override === void 0 ? void 0 : override.parameter),
44 returnType: Object.assign(Object.assign({}, colon), override === null || override === void 0 ? void 0 : override.returnType),
45 };
46}
47function getIdentifierRules(rules, node) {
48 const scope = node === null || node === void 0 ? void 0 : node.parent;
49 if ((0, util_1.isVariableDeclarator)(scope)) {
50 return rules.variable;
51 }
52 else if ((0, util_1.isFunctionOrFunctionType)(scope)) {
53 return rules.parameter;
54 }
55 else {
56 return rules.colon;
57 }
58}
59function getRules(rules, node) {
60 var _a;
61 const scope = (_a = node === null || node === void 0 ? void 0 : node.parent) === null || _a === void 0 ? void 0 : _a.parent;
62 if ((0, util_1.isTSFunctionType)(scope) || (0, util_1.isTSConstructorType)(scope)) {
63 return rules.arrow;
64 }
65 else if ((0, util_1.isIdentifier)(scope)) {
66 return getIdentifierRules(rules, scope);
67 }
68 else if ((0, util_1.isClassOrTypeElement)(scope)) {
69 return rules.property;
70 }
71 else if ((0, util_1.isFunction)(scope)) {
72 return rules.returnType;
73 }
74 else {
75 return rules.colon;
76 }
77}
78exports.default = util.createRule({
79 name: 'type-annotation-spacing',
80 meta: {
81 type: 'layout',
82 docs: {
83 description: 'Require consistent spacing around type annotations',
84 recommended: false,
85 },
86 fixable: 'whitespace',
87 messages: {
88 expectedSpaceAfter: "Expected a space after the '{{type}}'.",
89 expectedSpaceBefore: "Expected a space before the '{{type}}'.",
90 unexpectedSpaceAfter: "Unexpected space after the '{{type}}'.",
91 unexpectedSpaceBefore: "Unexpected space before the '{{type}}'.",
92 unexpectedSpaceBetween: "Unexpected space between the '{{previousToken}}' and the '{{type}}'.",
93 },
94 schema: [
95 {
96 type: 'object',
97 properties: {
98 before: { type: 'boolean' },
99 after: { type: 'boolean' },
100 overrides: {
101 type: 'object',
102 properties: {
103 colon: definition,
104 arrow: definition,
105 variable: definition,
106 parameter: definition,
107 property: definition,
108 returnType: definition,
109 },
110 additionalProperties: false,
111 },
112 },
113 additionalProperties: false,
114 },
115 ],
116 },
117 defaultOptions: [
118 // technically there is a default, but the overrides mean
119 // that if we apply them here, it will break the no override case.
120 {},
121 ],
122 create(context, [options]) {
123 const punctuators = [':', '=>'];
124 const sourceCode = context.getSourceCode();
125 const ruleSet = createRules(options);
126 /**
127 * Checks if there's proper spacing around type annotations (no space
128 * before colon, one space after).
129 */
130 function checkTypeAnnotationSpacing(typeAnnotation) {
131 const nextToken = typeAnnotation;
132 const punctuatorTokenEnd = sourceCode.getTokenBefore(nextToken);
133 let punctuatorTokenStart = punctuatorTokenEnd;
134 let previousToken = sourceCode.getTokenBefore(punctuatorTokenEnd);
135 let type = punctuatorTokenEnd.value;
136 if (!punctuators.includes(type)) {
137 return;
138 }
139 const { before, after } = getRules(ruleSet, typeAnnotation);
140 if (type === ':' && previousToken.value === '?') {
141 if (sourceCode.isSpaceBetweenTokens(previousToken, punctuatorTokenStart)) {
142 context.report({
143 node: punctuatorTokenStart,
144 messageId: 'unexpectedSpaceBetween',
145 data: {
146 type,
147 previousToken: previousToken.value,
148 },
149 fix(fixer) {
150 return fixer.removeRange([
151 previousToken.range[1],
152 punctuatorTokenStart.range[0],
153 ]);
154 },
155 });
156 }
157 // shift the start to the ?
158 type = '?:';
159 punctuatorTokenStart = previousToken;
160 previousToken = sourceCode.getTokenBefore(previousToken);
161 // handle the +/- modifiers for optional modification operators
162 if (previousToken.value === '+' || previousToken.value === '-') {
163 type = `${previousToken.value}?:`;
164 punctuatorTokenStart = previousToken;
165 previousToken = sourceCode.getTokenBefore(previousToken);
166 }
167 }
168 const previousDelta = punctuatorTokenStart.range[0] - previousToken.range[1];
169 const nextDelta = nextToken.range[0] - punctuatorTokenEnd.range[1];
170 if (after && nextDelta === 0) {
171 context.report({
172 node: punctuatorTokenEnd,
173 messageId: 'expectedSpaceAfter',
174 data: {
175 type,
176 },
177 fix(fixer) {
178 return fixer.insertTextAfter(punctuatorTokenEnd, ' ');
179 },
180 });
181 }
182 else if (!after && nextDelta > 0) {
183 context.report({
184 node: punctuatorTokenEnd,
185 messageId: 'unexpectedSpaceAfter',
186 data: {
187 type,
188 },
189 fix(fixer) {
190 return fixer.removeRange([
191 punctuatorTokenEnd.range[1],
192 nextToken.range[0],
193 ]);
194 },
195 });
196 }
197 if (before && previousDelta === 0) {
198 context.report({
199 node: punctuatorTokenStart,
200 messageId: 'expectedSpaceBefore',
201 data: {
202 type,
203 },
204 fix(fixer) {
205 return fixer.insertTextAfter(previousToken, ' ');
206 },
207 });
208 }
209 else if (!before && previousDelta > 0) {
210 context.report({
211 node: punctuatorTokenStart,
212 messageId: 'unexpectedSpaceBefore',
213 data: {
214 type,
215 },
216 fix(fixer) {
217 return fixer.removeRange([
218 previousToken.range[1],
219 punctuatorTokenStart.range[0],
220 ]);
221 },
222 });
223 }
224 }
225 return {
226 TSMappedType(node) {
227 if (node.typeAnnotation) {
228 checkTypeAnnotationSpacing(node.typeAnnotation);
229 }
230 },
231 TSTypeAnnotation(node) {
232 checkTypeAnnotationSpacing(node.typeAnnotation);
233 },
234 };
235 },
236});
237//# sourceMappingURL=type-annotation-spacing.js.map
\No newline at end of file