UNPKG

3.28 kBJavaScriptView Raw
1"use strict";
2
3const ruleComposer = require('eslint-rule-composer');
4const eslint = require('eslint');
5const semiRule = new eslint.Linter().getRules().get('semi');
6
7const OPT_OUT_PATTERN = /^[-[(/+`]/; // One of [(/+-`
8
9const isSemicolon = token => token.type === "Punctuator" && token.value === ";";
10
11const 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
30const 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
43const 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
70const 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
99module.exports = ruleComposer.filterReports(
100 semiRuleWithClassProperty,
101 (problem, metadata) => {
102 const node = problem.node;
103
104 // Handle async iterator:
105 // for await (let something of {})
106 if (
107 node.type === "VariableDeclaration" &&
108 node.parent.type === "ForAwaitStatement"
109 ) {
110 return false;
111 }
112
113 return true;
114 }
115);