1 | "use strict";
|
2 |
|
3 | const ruleComposer = require('eslint-rule-composer');
|
4 | const eslint = require('eslint');
|
5 | const semiRule = new eslint.Linter().getRules().get('semi');
|
6 |
|
7 | const OPT_OUT_PATTERN = /^[-[(/+`]/;
|
8 |
|
9 | const isSemicolon = token => token.type === "Punctuator" && token.value === ";";
|
10 |
|
11 | const isUnnecessarySemicolon = (context, lastToken) => {
|
12 | if (!isSemicolon(lastToken)) {
|
13 | return false;
|
14 | }
|
15 |
|
16 | const nextToken = context.getSourceCode().getTokenAfter(lastToken);
|
17 |
|
18 | if (!nextToken) {
|
19 | return true;
|
20 | }
|
21 |
|
22 | const lastTokenLine = lastToken.loc.end.line;
|
23 | const nextTokenLine = nextToken.loc.start.line;
|
24 | const isOptOutToken = OPT_OUT_PATTERN.test(nextToken.value) && nextToken.value !== "++" && nextToken.value !== "--";
|
25 | const isDivider = (nextToken.value === "}" || nextToken.value === ";");
|
26 |
|
27 | return (lastTokenLine !== nextTokenLine && !isOptOutToken) || isDivider;
|
28 | }
|
29 |
|
30 | const isOneLinerBlock = (context, node) => {
|
31 | const nextToken = context.getSourceCode().getTokenAfter(node);
|
32 |
|
33 | if (!nextToken || nextToken.value !== "}") {
|
34 | return false;
|
35 | }
|
36 |
|
37 | const parent = node.parent;
|
38 |
|
39 | return parent && parent.type === "BlockStatement" &&
|
40 | parent.loc.start.line === parent.loc.end.line;
|
41 | };
|
42 |
|
43 | const report = (context, node, missing) => {
|
44 | const lastToken = context.getSourceCode().getLastToken(node);
|
45 |
|
46 | let message, fix, loc = lastToken.loc;
|
47 |
|
48 | if (!missing) {
|
49 | message = "Missing semicolon.";
|
50 | loc = loc.end;
|
51 | fix = function(fixer) {
|
52 | return fixer.insertTextAfter(lastToken, ";");
|
53 | };
|
54 | } else {
|
55 | message = "Extra semicolon.";
|
56 | loc = loc.start;
|
57 | fix = function(fixer) {
|
58 | return fixer.remove(lastToken);
|
59 | };
|
60 | }
|
61 |
|
62 | context.report({
|
63 | node,
|
64 | loc,
|
65 | message,
|
66 | fix
|
67 | });
|
68 | };
|
69 |
|
70 | const semiRuleWithClassProperty = ruleComposer.joinReports([
|
71 | semiRule,
|
72 | context => ({
|
73 | ClassProperty(node) {
|
74 | const options = context.options[1];
|
75 | const exceptOneLine = options && options.omitLastInOneLineBlock === true;
|
76 |
|
77 | const sourceCode = context.getSourceCode();
|
78 | const lastToken = sourceCode.getLastToken(node);
|
79 |
|
80 | if (context.options[0] === "never") {
|
81 | if (isUnnecessarySemicolon(context, lastToken)) {
|
82 | report(context, node, true);
|
83 | }
|
84 | } else {
|
85 | if (!isSemicolon(lastToken)) {
|
86 | if (!exceptOneLine || !isOneLinerBlock(context, node)) {
|
87 | report(context, node);
|
88 | }
|
89 | } else {
|
90 | if (exceptOneLine && isOneLinerBlock(context, node)) {
|
91 | report(context, node, true);
|
92 | }
|
93 | }
|
94 | }
|
95 | },
|
96 | }),
|
97 | ]);
|
98 |
|
99 | module.exports = ruleComposer.filterReports(
|
100 | semiRuleWithClassProperty,
|
101 | (problem, metadata) => {
|
102 | const node = problem.node;
|
103 |
|
104 |
|
105 |
|
106 | if (
|
107 | node.type === "VariableDeclaration" &&
|
108 | node.parent.type === "ForAwaitStatement"
|
109 | ) {
|
110 | return false;
|
111 | }
|
112 |
|
113 | return true;
|
114 | }
|
115 | );
|