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 | const LT = `[${Array.from(new Set(['\r\n', '\r', '\n', '\u2028', '\u2029'])).join('')}]`;
|
25 | const PADDING_LINE_SEQUENCE = new RegExp(String.raw `^(\s*?${LT})\s*${LT}(\s*;?)$`, 'u');
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 | function newKeywordTester(type, keyword) {
|
34 | return {
|
35 | test(node, sourceCode) {
|
36 | var _a;
|
37 | const isSameKeyword = ((_a = sourceCode.getFirstToken(node)) === null || _a === void 0 ? void 0 : _a.value) === keyword;
|
38 | const isSameType = Array.isArray(type)
|
39 | ? type.some(val => val === node.type)
|
40 | : type === node.type;
|
41 | return isSameKeyword && isSameType;
|
42 | },
|
43 | };
|
44 | }
|
45 |
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 | function newSinglelineKeywordTester(keyword) {
|
52 | return {
|
53 | test(node, sourceCode) {
|
54 | return (node.loc.start.line === node.loc.end.line &&
|
55 | sourceCode.getFirstToken(node).value === keyword);
|
56 | },
|
57 | };
|
58 | }
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 | function newMultilineKeywordTester(keyword) {
|
66 | return {
|
67 | test(node, sourceCode) {
|
68 | return (node.loc.start.line !== node.loc.end.line &&
|
69 | sourceCode.getFirstToken(node).value === keyword);
|
70 | },
|
71 | };
|
72 | }
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 | function newNodeTypeTester(type) {
|
80 | return {
|
81 | test: (node) => node.type === type,
|
82 | };
|
83 | }
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | function skipChainExpression(node) {
|
91 | return node && node.type === utils_1.AST_NODE_TYPES.ChainExpression
|
92 | ? node.expression
|
93 | : node;
|
94 | }
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 | function isIIFEStatement(node) {
|
102 | if (node.type === utils_1.AST_NODE_TYPES.ExpressionStatement) {
|
103 | let expression = skipChainExpression(node.expression);
|
104 | if (expression.type === utils_1.AST_NODE_TYPES.UnaryExpression) {
|
105 | expression = skipChainExpression(expression.argument);
|
106 | }
|
107 | if (expression.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
108 | let node = expression.callee;
|
109 | while (node.type === utils_1.AST_NODE_TYPES.SequenceExpression) {
|
110 | node = node.expressions[node.expressions.length - 1];
|
111 | }
|
112 | return util.isFunction(node);
|
113 | }
|
114 | }
|
115 | return false;
|
116 | }
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 | function isCJSRequire(node) {
|
124 | if (node.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
125 | const declaration = node.declarations[0];
|
126 | if (declaration === null || declaration === void 0 ? void 0 : declaration.init) {
|
127 | let call = declaration === null || declaration === void 0 ? void 0 : declaration.init;
|
128 | while (call.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
129 | call = call.object;
|
130 | }
|
131 | if (call.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
132 | call.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
|
133 | return call.callee.name === 'require';
|
134 | }
|
135 | }
|
136 | }
|
137 | return false;
|
138 | }
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 |
|
147 | function isBlockLikeStatement(node, sourceCode) {
|
148 |
|
149 | if (node.type === utils_1.AST_NODE_TYPES.DoWhileStatement &&
|
150 | node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
151 | return true;
|
152 | }
|
153 | |
154 |
|
155 |
|
156 |
|
157 | if (isIIFEStatement(node)) {
|
158 | return true;
|
159 | }
|
160 |
|
161 | const lastToken = sourceCode.getLastToken(node, util.isNotSemicolonToken);
|
162 | const belongingNode = lastToken && util.isClosingBraceToken(lastToken)
|
163 | ? sourceCode.getNodeByRangeIndex(lastToken.range[0])
|
164 | : null;
|
165 | return (!!belongingNode &&
|
166 | (belongingNode.type === utils_1.AST_NODE_TYPES.BlockStatement ||
|
167 | belongingNode.type === utils_1.AST_NODE_TYPES.SwitchStatement));
|
168 | }
|
169 |
|
170 |
|
171 |
|
172 |
|
173 |
|
174 |
|
175 | function isDirective(node, sourceCode) {
|
176 | var _a, _b;
|
177 | return (node.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
178 | (((_a = node.parent) === null || _a === void 0 ? void 0 : _a.type) === utils_1.AST_NODE_TYPES.Program ||
|
179 | (((_b = node.parent) === null || _b === void 0 ? void 0 : _b.type) === utils_1.AST_NODE_TYPES.BlockStatement &&
|
180 | util.isFunction(node.parent.parent))) &&
|
181 | node.expression.type === utils_1.AST_NODE_TYPES.Literal &&
|
182 | typeof node.expression.value === 'string' &&
|
183 | !util.isParenthesized(node.expression, sourceCode));
|
184 | }
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 | function isDirectivePrologue(node, sourceCode) {
|
192 | if (isDirective(node, sourceCode) &&
|
193 | node.parent &&
|
194 | 'body' in node.parent &&
|
195 | Array.isArray(node.parent.body)) {
|
196 | for (const sibling of node.parent.body) {
|
197 | if (sibling === node) {
|
198 | break;
|
199 | }
|
200 | if (!isDirective(sibling, sourceCode)) {
|
201 | return false;
|
202 | }
|
203 | }
|
204 | return true;
|
205 | }
|
206 | return false;
|
207 | }
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 | function isCJSExport(node) {
|
215 | if (node.type === utils_1.AST_NODE_TYPES.ExpressionStatement) {
|
216 | const expression = node.expression;
|
217 | if (expression.type === utils_1.AST_NODE_TYPES.AssignmentExpression) {
|
218 | let left = expression.left;
|
219 | if (left.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
220 | while (left.object.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
221 | left = left.object;
|
222 | }
|
223 | return (left.object.type === utils_1.AST_NODE_TYPES.Identifier &&
|
224 | (left.object.name === 'exports' ||
|
225 | (left.object.name === 'module' &&
|
226 | left.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
227 | left.property.name === 'exports')));
|
228 | }
|
229 | }
|
230 | }
|
231 | return false;
|
232 | }
|
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 | function isExpression(node, sourceCode) {
|
240 | return (node.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
241 | !isDirectivePrologue(node, sourceCode));
|
242 | }
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 | function getActualLastToken(node, sourceCode) {
|
257 | const semiToken = sourceCode.getLastToken(node);
|
258 | const prevToken = sourceCode.getTokenBefore(semiToken);
|
259 | const nextToken = sourceCode.getTokenAfter(semiToken);
|
260 | const isSemicolonLessStyle = prevToken &&
|
261 | nextToken &&
|
262 | prevToken.range[0] >= node.range[0] &&
|
263 | util.isSemicolonToken(semiToken) &&
|
264 | semiToken.loc.start.line !== prevToken.loc.end.line &&
|
265 | semiToken.loc.end.line === nextToken.loc.start.line;
|
266 | return isSemicolonLessStyle ? prevToken : semiToken;
|
267 | }
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 | function replacerToRemovePaddingLines(_, trailingSpaces, indentSpaces) {
|
277 | return trailingSpaces + indentSpaces;
|
278 | }
|
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 | function verifyForAny() {
|
286 |
|
287 | }
|
288 |
|
289 |
|
290 |
|
291 |
|
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 |
|
298 |
|
299 |
|
300 |
|
301 | function verifyForNever(context, _, nextNode, paddingLines) {
|
302 | if (paddingLines.length === 0) {
|
303 | return;
|
304 | }
|
305 | context.report({
|
306 | node: nextNode,
|
307 | messageId: 'unexpectedBlankLine',
|
308 | fix(fixer) {
|
309 | if (paddingLines.length >= 2) {
|
310 | return null;
|
311 | }
|
312 | const prevToken = paddingLines[0][0];
|
313 | const nextToken = paddingLines[0][1];
|
314 | const start = prevToken.range[1];
|
315 | const end = nextToken.range[0];
|
316 | const text = context
|
317 | .getSourceCode()
|
318 | .text.slice(start, end)
|
319 | .replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines);
|
320 | return fixer.replaceTextRange([start, end], text);
|
321 | },
|
322 | });
|
323 | }
|
324 |
|
325 |
|
326 |
|
327 |
|
328 |
|
329 |
|
330 |
|
331 |
|
332 |
|
333 |
|
334 |
|
335 |
|
336 |
|
337 | function verifyForAlways(context, prevNode, nextNode, paddingLines) {
|
338 | if (paddingLines.length > 0) {
|
339 | return;
|
340 | }
|
341 | context.report({
|
342 | node: nextNode,
|
343 | messageId: 'expectedBlankLine',
|
344 | fix(fixer) {
|
345 | const sourceCode = context.getSourceCode();
|
346 | let prevToken = getActualLastToken(prevNode, sourceCode);
|
347 | const nextToken = sourceCode.getFirstTokenBetween(prevToken, nextNode, {
|
348 | includeComments: true,
|
349 | |
350 |
|
351 |
|
352 |
|
353 |
|
354 |
|
355 |
|
356 |
|
357 |
|
358 |
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 |
|
366 |
|
367 |
|
368 |
|
369 | filter(token) {
|
370 | if (util.isTokenOnSameLine(prevToken, token)) {
|
371 | prevToken = token;
|
372 | return false;
|
373 | }
|
374 | return true;
|
375 | },
|
376 | }) || nextNode;
|
377 | const insertText = util.isTokenOnSameLine(prevToken, nextToken)
|
378 | ? '\n\n'
|
379 | : '\n';
|
380 | return fixer.insertTextAfter(prevToken, insertText);
|
381 | },
|
382 | });
|
383 | }
|
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 | const PaddingTypes = {
|
391 | any: { verify: verifyForAny },
|
392 | never: { verify: verifyForNever },
|
393 | always: { verify: verifyForAlways },
|
394 | };
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 | const StatementTypes = {
|
401 | '*': { test: () => true },
|
402 | 'block-like': { test: isBlockLikeStatement },
|
403 | exports: { test: isCJSExport },
|
404 | require: { test: isCJSRequire },
|
405 | directive: { test: isDirectivePrologue },
|
406 | expression: { test: isExpression },
|
407 | iife: { test: isIIFEStatement },
|
408 | 'multiline-block-like': {
|
409 | test: (node, sourceCode) => node.loc.start.line !== node.loc.end.line &&
|
410 | isBlockLikeStatement(node, sourceCode),
|
411 | },
|
412 | 'multiline-expression': {
|
413 | test: (node, sourceCode) => node.loc.start.line !== node.loc.end.line &&
|
414 | node.type === utils_1.AST_NODE_TYPES.ExpressionStatement &&
|
415 | !isDirectivePrologue(node, sourceCode),
|
416 | },
|
417 | 'multiline-const': newMultilineKeywordTester('const'),
|
418 | 'multiline-let': newMultilineKeywordTester('let'),
|
419 | 'multiline-var': newMultilineKeywordTester('var'),
|
420 | 'singleline-const': newSinglelineKeywordTester('const'),
|
421 | 'singleline-let': newSinglelineKeywordTester('let'),
|
422 | 'singleline-var': newSinglelineKeywordTester('var'),
|
423 | block: newNodeTypeTester(utils_1.AST_NODE_TYPES.BlockStatement),
|
424 | empty: newNodeTypeTester(utils_1.AST_NODE_TYPES.EmptyStatement),
|
425 | function: newNodeTypeTester(utils_1.AST_NODE_TYPES.FunctionDeclaration),
|
426 | break: newKeywordTester(utils_1.AST_NODE_TYPES.BreakStatement, 'break'),
|
427 | case: newKeywordTester(utils_1.AST_NODE_TYPES.SwitchCase, 'case'),
|
428 | class: newKeywordTester(utils_1.AST_NODE_TYPES.ClassDeclaration, 'class'),
|
429 | const: newKeywordTester(utils_1.AST_NODE_TYPES.VariableDeclaration, 'const'),
|
430 | continue: newKeywordTester(utils_1.AST_NODE_TYPES.ContinueStatement, 'continue'),
|
431 | debugger: newKeywordTester(utils_1.AST_NODE_TYPES.DebuggerStatement, 'debugger'),
|
432 | default: newKeywordTester([utils_1.AST_NODE_TYPES.SwitchCase, utils_1.AST_NODE_TYPES.ExportDefaultDeclaration], 'default'),
|
433 | do: newKeywordTester(utils_1.AST_NODE_TYPES.DoWhileStatement, 'do'),
|
434 | export: newKeywordTester([
|
435 | utils_1.AST_NODE_TYPES.ExportDefaultDeclaration,
|
436 | utils_1.AST_NODE_TYPES.ExportNamedDeclaration,
|
437 | ], 'export'),
|
438 | for: newKeywordTester([
|
439 | utils_1.AST_NODE_TYPES.ForStatement,
|
440 | utils_1.AST_NODE_TYPES.ForInStatement,
|
441 | utils_1.AST_NODE_TYPES.ForOfStatement,
|
442 | ], 'for'),
|
443 | if: newKeywordTester(utils_1.AST_NODE_TYPES.IfStatement, 'if'),
|
444 | import: newKeywordTester(utils_1.AST_NODE_TYPES.ImportDeclaration, 'import'),
|
445 | let: newKeywordTester(utils_1.AST_NODE_TYPES.VariableDeclaration, 'let'),
|
446 | return: newKeywordTester(utils_1.AST_NODE_TYPES.ReturnStatement, 'return'),
|
447 | switch: newKeywordTester(utils_1.AST_NODE_TYPES.SwitchStatement, 'switch'),
|
448 | throw: newKeywordTester(utils_1.AST_NODE_TYPES.ThrowStatement, 'throw'),
|
449 | try: newKeywordTester(utils_1.AST_NODE_TYPES.TryStatement, 'try'),
|
450 | var: newKeywordTester(utils_1.AST_NODE_TYPES.VariableDeclaration, 'var'),
|
451 | while: newKeywordTester([utils_1.AST_NODE_TYPES.WhileStatement, utils_1.AST_NODE_TYPES.DoWhileStatement], 'while'),
|
452 | with: newKeywordTester(utils_1.AST_NODE_TYPES.WithStatement, 'with'),
|
453 | // Additional Typescript constructs
|
454 | interface: newKeywordTester(utils_1.AST_NODE_TYPES.TSInterfaceDeclaration, 'interface'),
|
455 | type: newKeywordTester(utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration, 'type'),
|
456 | };
|
457 | //------------------------------------------------------------------------------
|
458 | // Rule Definition
|
459 | //------------------------------------------------------------------------------
|
460 | exports.default = util.createRule({
|
461 | name: 'padding-line-between-statements',
|
462 | meta: {
|
463 | type: 'layout',
|
464 | docs: {
|
465 | description: 'require or disallow padding lines between statements',
|
466 | recommended: false,
|
467 | extendsBaseRule: true,
|
468 | },
|
469 | fixable: 'whitespace',
|
470 | hasSuggestions: true,
|
471 | schema: {
|
472 | definitions: {
|
473 | paddingType: {
|
474 | enum: Object.keys(PaddingTypes),
|
475 | },
|
476 | statementType: {
|
477 | anyOf: [
|
478 | { enum: Object.keys(StatementTypes) },
|
479 | {
|
480 | type: 'array',
|
481 | items: { enum: Object.keys(StatementTypes) },
|
482 | minItems: 1,
|
483 | uniqueItems: true,
|
484 | additionalItems: false,
|
485 | },
|
486 | ],
|
487 | },
|
488 | },
|
489 | type: 'array',
|
490 | items: {
|
491 | type: 'object',
|
492 | properties: {
|
493 | blankLine: { $ref: '#/definitions/paddingType' },
|
494 | prev: { $ref: '#/definitions/statementType' },
|
495 | next: { $ref: '#/definitions/statementType' },
|
496 | },
|
497 | additionalProperties: false,
|
498 | required: ['blankLine', 'prev', 'next'],
|
499 | },
|
500 | additionalItems: false,
|
501 | },
|
502 | messages: {
|
503 | unexpectedBlankLine: 'Unexpected blank line before this statement.',
|
504 | expectedBlankLine: 'Expected blank line before this statement.',
|
505 | },
|
506 | },
|
507 | defaultOptions: [],
|
508 | create(context) {
|
509 | const sourceCode = context.getSourceCode();
|
510 | const configureList = context.options || [];
|
511 | let scopeInfo = null;
|
512 | |
513 |
|
514 |
|
515 |
|
516 |
|
517 |
|
518 | function enterScope() {
|
519 | scopeInfo = {
|
520 | upper: scopeInfo,
|
521 | prevNode: null,
|
522 | };
|
523 | }
|
524 | |
525 |
|
526 |
|
527 |
|
528 |
|
529 | function exitScope() {
|
530 | if (scopeInfo) {
|
531 | scopeInfo = scopeInfo.upper;
|
532 | }
|
533 | }
|
534 | |
535 |
|
536 |
|
537 |
|
538 |
|
539 |
|
540 |
|
541 | function match(node, type) {
|
542 | let innerStatementNode = node;
|
543 | while (innerStatementNode.type === utils_1.AST_NODE_TYPES.LabeledStatement) {
|
544 | innerStatementNode = innerStatementNode.body;
|
545 | }
|
546 | if (Array.isArray(type)) {
|
547 | return type.some(match.bind(null, innerStatementNode));
|
548 | }
|
549 | return StatementTypes[type].test(innerStatementNode, sourceCode);
|
550 | }
|
551 | |
552 |
|
553 |
|
554 |
|
555 |
|
556 |
|
557 |
|
558 | function getPaddingType(prevNode, nextNode) {
|
559 | for (let i = configureList.length - 1; i >= 0; --i) {
|
560 | const configure = configureList[i];
|
561 | if (match(prevNode, configure.prev) &&
|
562 | match(nextNode, configure.next)) {
|
563 | return PaddingTypes[configure.blankLine];
|
564 | }
|
565 | }
|
566 | return PaddingTypes.any;
|
567 | }
|
568 | |
569 |
|
570 |
|
571 |
|
572 |
|
573 |
|
574 |
|
575 |
|
576 | function getPaddingLineSequences(prevNode, nextNode) {
|
577 | const pairs = [];
|
578 | let prevToken = getActualLastToken(prevNode, sourceCode);
|
579 | if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) {
|
580 | do {
|
581 | const token = sourceCode.getTokenAfter(prevToken, {
|
582 | includeComments: true,
|
583 | });
|
584 | if (token.loc.start.line - prevToken.loc.end.line >= 2) {
|
585 | pairs.push([prevToken, token]);
|
586 | }
|
587 | prevToken = token;
|
588 | } while (prevToken.range[0] < nextNode.range[0]);
|
589 | }
|
590 | return pairs;
|
591 | }
|
592 | |
593 |
|
594 |
|
595 |
|
596 |
|
597 |
|
598 | function verify(node) {
|
599 | if (!node.parent ||
|
600 | ![
|
601 | utils_1.AST_NODE_TYPES.BlockStatement,
|
602 | utils_1.AST_NODE_TYPES.Program,
|
603 | utils_1.AST_NODE_TYPES.SwitchCase,
|
604 | utils_1.AST_NODE_TYPES.SwitchStatement,
|
605 | utils_1.AST_NODE_TYPES.TSModuleBlock,
|
606 | ].includes(node.parent.type)) {
|
607 | return;
|
608 | }
|
609 |
|
610 | const prevNode = scopeInfo.prevNode;
|
611 |
|
612 | if (prevNode) {
|
613 | const type = getPaddingType(prevNode, node);
|
614 | const paddingLines = getPaddingLineSequences(prevNode, node);
|
615 | type.verify(context, prevNode, node, paddingLines);
|
616 | }
|
617 | scopeInfo.prevNode = node;
|
618 | }
|
619 | |
620 |
|
621 |
|
622 |
|
623 |
|
624 |
|
625 |
|
626 | function verifyThenEnterScope(node) {
|
627 | verify(node);
|
628 | enterScope();
|
629 | }
|
630 | return {
|
631 | Program: enterScope,
|
632 | BlockStatement: enterScope,
|
633 | SwitchStatement: enterScope,
|
634 | TSModuleBlock: enterScope,
|
635 | 'Program:exit': exitScope,
|
636 | 'BlockStatement:exit': exitScope,
|
637 | 'SwitchStatement:exit': exitScope,
|
638 | 'TSModuleBlock:exit': exitScope,
|
639 | ':statement': verify,
|
640 | SwitchCase: verifyThenEnterScope,
|
641 | TSDeclareFunction: verifyThenEnterScope,
|
642 | 'SwitchCase:exit': exitScope,
|
643 | 'TSDeclareFunction:exit': exitScope,
|
644 | };
|
645 | },
|
646 | });
|
647 |
|
\ | No newline at end of file |