eslint 7.0.0 → 7.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -0
- package/README.md +14 -10
- package/lib/cli-engine/cli-engine.js +2 -2
- package/lib/cli-engine/config-array-factory.js +34 -2
- package/lib/init/config-initializer.js +92 -74
- package/lib/linter/code-path-analysis/code-path-analyzer.js +2 -2
- package/lib/linter/code-path-analysis/code-path-state.js +34 -12
- package/lib/linter/config-comment-parser.js +1 -1
- package/lib/linter/linter.js +2 -1
- package/lib/rule-tester/rule-tester.js +9 -0
- package/lib/rules/accessor-pairs.js +1 -1
- package/lib/rules/arrow-parens.js +19 -3
- package/lib/rules/block-spacing.js +19 -2
- package/lib/rules/callback-return.js +1 -1
- package/lib/rules/comma-dangle.js +2 -1
- package/lib/rules/curly.js +8 -1
- package/lib/rules/func-call-spacing.js +18 -3
- package/lib/rules/global-require.js +1 -1
- package/lib/rules/handle-callback-err.js +1 -1
- package/lib/rules/id-match.js +2 -1
- package/lib/rules/index.js +3 -0
- package/lib/rules/key-spacing.js +6 -2
- package/lib/rules/keyword-spacing.js +9 -2
- package/lib/rules/linebreak-style.js +8 -2
- package/lib/rules/max-lines-per-function.js +1 -1
- package/lib/rules/max-lines.js +34 -8
- package/lib/rules/multiline-ternary.js +44 -25
- package/lib/rules/no-buffer-constructor.js +1 -1
- package/lib/rules/no-control-regex.js +1 -1
- package/lib/rules/no-extra-boolean-cast.js +3 -0
- package/lib/rules/no-extra-parens.js +30 -2
- package/lib/rules/no-invalid-regexp.js +1 -1
- package/lib/rules/no-loss-of-precision.js +198 -0
- package/lib/rules/no-misleading-character-class.js +1 -1
- package/lib/rules/no-mixed-operators.js +3 -2
- package/lib/rules/no-mixed-requires.js +1 -1
- package/lib/rules/no-mixed-spaces-and-tabs.js +14 -6
- package/lib/rules/no-new-func.js +22 -19
- package/lib/rules/no-new-require.js +1 -1
- package/lib/rules/no-new-symbol.js +2 -1
- package/lib/rules/no-path-concat.js +1 -1
- package/lib/rules/no-process-env.js +1 -1
- package/lib/rules/no-process-exit.js +1 -1
- package/lib/rules/no-promise-executor-return.js +121 -0
- package/lib/rules/no-regex-spaces.js +1 -1
- package/lib/rules/no-restricted-exports.js +6 -0
- package/lib/rules/no-restricted-modules.js +1 -1
- package/lib/rules/no-sync.js +1 -1
- package/lib/rules/no-unneeded-ternary.js +6 -4
- package/lib/rules/no-unreachable-loop.js +150 -0
- package/lib/rules/no-unused-expressions.js +1 -1
- package/lib/rules/no-unused-vars.js +5 -2
- package/lib/rules/no-useless-backreference.js +1 -1
- package/lib/rules/object-property-newline.js +1 -1
- package/lib/rules/one-var-declaration-per-line.js +1 -1
- package/lib/rules/operator-linebreak.js +2 -5
- package/lib/rules/padded-blocks.js +19 -5
- package/lib/rules/prefer-named-capture-group.js +1 -1
- package/lib/rules/quote-props.js +2 -2
- package/lib/rules/rest-spread-spacing.js +3 -6
- package/lib/rules/semi-spacing.js +33 -8
- package/lib/rules/template-tag-spacing.js +8 -2
- package/lib/rules/utils/ast-utils.js +106 -9
- package/lib/source-code/source-code.js +1 -0
- package/messages/extend-config-missing.txt +1 -1
- package/messages/no-config-found.txt +1 -1
- package/messages/plugin-conflict.txt +1 -1
- package/messages/plugin-missing.txt +1 -1
- package/messages/whitespace-found.txt +1 -1
- package/package.json +6 -6
@@ -201,6 +201,7 @@ function finalizeTestSegmentsOfFor(context, choiceContext, head) {
|
|
201
201
|
if (!choiceContext.processed) {
|
202
202
|
choiceContext.trueForkContext.add(head);
|
203
203
|
choiceContext.falseForkContext.add(head);
|
204
|
+
choiceContext.qqForkContext.add(head);
|
204
205
|
}
|
205
206
|
|
206
207
|
if (context.test !== true) {
|
@@ -351,6 +352,7 @@ class CodePathState {
|
|
351
352
|
isForkingAsResult,
|
352
353
|
trueForkContext: ForkContext.newEmpty(this.forkContext),
|
353
354
|
falseForkContext: ForkContext.newEmpty(this.forkContext),
|
355
|
+
qqForkContext: ForkContext.newEmpty(this.forkContext),
|
354
356
|
processed: false
|
355
357
|
};
|
356
358
|
}
|
@@ -370,6 +372,7 @@ class CodePathState {
|
|
370
372
|
switch (context.kind) {
|
371
373
|
case "&&":
|
372
374
|
case "||":
|
375
|
+
case "??":
|
373
376
|
|
374
377
|
/*
|
375
378
|
* If any result were not transferred from child contexts,
|
@@ -379,6 +382,7 @@ class CodePathState {
|
|
379
382
|
if (!context.processed) {
|
380
383
|
context.trueForkContext.add(headSegments);
|
381
384
|
context.falseForkContext.add(headSegments);
|
385
|
+
context.qqForkContext.add(headSegments);
|
382
386
|
}
|
383
387
|
|
384
388
|
/*
|
@@ -390,6 +394,7 @@ class CodePathState {
|
|
390
394
|
|
391
395
|
parentContext.trueForkContext.addAll(context.trueForkContext);
|
392
396
|
parentContext.falseForkContext.addAll(context.falseForkContext);
|
397
|
+
parentContext.qqForkContext.addAll(context.qqForkContext);
|
393
398
|
parentContext.processed = true;
|
394
399
|
|
395
400
|
return context;
|
@@ -456,13 +461,24 @@ class CodePathState {
|
|
456
461
|
* This got segments already from the child choice context.
|
457
462
|
* Creates the next path from own true/false fork context.
|
458
463
|
*/
|
459
|
-
|
460
|
-
|
461
|
-
|
464
|
+
let prevForkContext;
|
465
|
+
|
466
|
+
switch (context.kind) {
|
467
|
+
case "&&": // if true then go to the right-hand side.
|
468
|
+
prevForkContext = context.trueForkContext;
|
469
|
+
break;
|
470
|
+
case "||": // if false then go to the right-hand side.
|
471
|
+
prevForkContext = context.falseForkContext;
|
472
|
+
break;
|
473
|
+
case "??": // Both true/false can short-circuit, so needs the third path to go to the right-hand side. That's qqForkContext.
|
474
|
+
prevForkContext = context.qqForkContext;
|
475
|
+
break;
|
476
|
+
default:
|
477
|
+
throw new Error("unreachable");
|
478
|
+
}
|
462
479
|
|
463
480
|
forkContext.replaceHead(prevForkContext.makeNext(0, -1));
|
464
481
|
prevForkContext.clear();
|
465
|
-
|
466
482
|
context.processed = false;
|
467
483
|
} else {
|
468
484
|
|
@@ -471,14 +487,19 @@ class CodePathState {
|
|
471
487
|
* So addresses the head segments.
|
472
488
|
* The head segments are the path of the left-hand operand.
|
473
489
|
*/
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
490
|
+
switch (context.kind) {
|
491
|
+
case "&&": // the false path can short-circuit.
|
492
|
+
context.falseForkContext.add(forkContext.head);
|
493
|
+
break;
|
494
|
+
case "||": // the true path can short-circuit.
|
495
|
+
context.trueForkContext.add(forkContext.head);
|
496
|
+
break;
|
497
|
+
case "??": // both can short-circuit.
|
498
|
+
context.trueForkContext.add(forkContext.head);
|
499
|
+
context.falseForkContext.add(forkContext.head);
|
500
|
+
break;
|
501
|
+
default:
|
502
|
+
throw new Error("unreachable");
|
482
503
|
}
|
483
504
|
|
484
505
|
forkContext.replaceHead(forkContext.makeNext(-1, -1));
|
@@ -501,6 +522,7 @@ class CodePathState {
|
|
501
522
|
if (!context.processed) {
|
502
523
|
context.trueForkContext.add(forkContext.head);
|
503
524
|
context.falseForkContext.add(forkContext.head);
|
525
|
+
context.qqForkContext.add(forkContext.head);
|
504
526
|
}
|
505
527
|
|
506
528
|
context.processed = false;
|
package/lib/linter/linter.js
CHANGED
@@ -938,7 +938,8 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
|
|
938
938
|
});
|
939
939
|
});
|
940
940
|
|
941
|
-
|
941
|
+
// only run code path analyzer if the top level node is "Program", skip otherwise
|
942
|
+
const eventGenerator = nodeQueue[0].node.type === "Program" ? new CodePathAnalyzer(new NodeEventGenerator(emitter)) : new NodeEventGenerator(emitter);
|
942
943
|
|
943
944
|
nodeQueue.forEach(traversalInfo => {
|
944
945
|
currentNode = traversalInfo.node;
|
@@ -644,6 +644,10 @@ class RuleTester {
|
|
644
644
|
assert.ok(item.errors || item.errors === 0,
|
645
645
|
`Did not specify errors for an invalid test of ${ruleName}`);
|
646
646
|
|
647
|
+
if (Array.isArray(item.errors) && item.errors.length === 0) {
|
648
|
+
assert.fail("Invalid cases must have at least one error");
|
649
|
+
}
|
650
|
+
|
647
651
|
const ruleHasMetaMessages = hasOwnProperty(rule, "meta") && hasOwnProperty(rule.meta, "messages");
|
648
652
|
const friendlyIDList = ruleHasMetaMessages ? `[${Object.keys(rule.meta.messages).map(key => `'${key}'`).join(", ")}]` : null;
|
649
653
|
|
@@ -651,6 +655,11 @@ class RuleTester {
|
|
651
655
|
const messages = result.messages;
|
652
656
|
|
653
657
|
if (typeof item.errors === "number") {
|
658
|
+
|
659
|
+
if (item.errors === 0) {
|
660
|
+
assert.fail("Invalid cases must have 'error' value greater than 0");
|
661
|
+
}
|
662
|
+
|
654
663
|
assert.strictEqual(messages.length, item.errors, util.format("Should have %d error%s but had %d: %s",
|
655
664
|
item.errors, item.errors === 1 ? "" : "s", messages.length, util.inspect(messages)));
|
656
665
|
} else {
|
@@ -105,10 +105,27 @@ module.exports = {
|
|
105
105
|
], `${shouldAddSpaceForAsync ? " " : ""}${paramToken.value}`);
|
106
106
|
}
|
107
107
|
|
108
|
+
/**
|
109
|
+
* Checks whether there are comments inside the params or not.
|
110
|
+
* @returns {boolean} `true` if there are comments inside of parens, else `false`
|
111
|
+
*/
|
112
|
+
function hasCommentsInParens() {
|
113
|
+
if (astUtils.isOpeningParenToken(firstTokenOfParam)) {
|
114
|
+
const closingParenToken = sourceCode.getTokenAfter(node.params[0], astUtils.isClosingParenToken);
|
115
|
+
|
116
|
+
return closingParenToken && sourceCode.commentsExistBetween(firstTokenOfParam, closingParenToken);
|
117
|
+
}
|
118
|
+
return false;
|
119
|
+
|
120
|
+
}
|
121
|
+
|
122
|
+
if (hasCommentsInParens()) {
|
123
|
+
return;
|
124
|
+
}
|
125
|
+
|
108
126
|
// "as-needed", { "requireForBlockBody": true }: x => x
|
109
127
|
if (
|
110
128
|
requireForBlockBody &&
|
111
|
-
node.params.length === 1 &&
|
112
129
|
node.params[0].type === "Identifier" &&
|
113
130
|
!node.params[0].typeAnnotation &&
|
114
131
|
node.body.type !== "BlockStatement" &&
|
@@ -144,7 +161,6 @@ module.exports = {
|
|
144
161
|
|
145
162
|
// "as-needed": x => x
|
146
163
|
if (asNeeded &&
|
147
|
-
node.params.length === 1 &&
|
148
164
|
node.params[0].type === "Identifier" &&
|
149
165
|
!node.params[0].typeAnnotation &&
|
150
166
|
!node.returnType
|
@@ -178,7 +194,7 @@ module.exports = {
|
|
178
194
|
}
|
179
195
|
|
180
196
|
return {
|
181
|
-
ArrowFunctionExpression: parens
|
197
|
+
"ArrowFunctionExpression[params.length=1]": parens
|
182
198
|
};
|
183
199
|
}
|
184
200
|
};
|
@@ -102,9 +102,18 @@ module.exports = {
|
|
102
102
|
|
103
103
|
// Check.
|
104
104
|
if (!isValid(openBrace, firstToken)) {
|
105
|
+
let loc = openBrace.loc;
|
106
|
+
|
107
|
+
if (messageId === "extra") {
|
108
|
+
loc = {
|
109
|
+
start: openBrace.loc.end,
|
110
|
+
end: firstToken.loc.start
|
111
|
+
};
|
112
|
+
}
|
113
|
+
|
105
114
|
context.report({
|
106
115
|
node,
|
107
|
-
loc
|
116
|
+
loc,
|
108
117
|
messageId,
|
109
118
|
data: {
|
110
119
|
location: "after",
|
@@ -120,9 +129,17 @@ module.exports = {
|
|
120
129
|
});
|
121
130
|
}
|
122
131
|
if (!isValid(lastToken, closeBrace)) {
|
132
|
+
let loc = closeBrace.loc;
|
133
|
+
|
134
|
+
if (messageId === "extra") {
|
135
|
+
loc = {
|
136
|
+
start: lastToken.loc.end,
|
137
|
+
end: closeBrace.loc.start
|
138
|
+
};
|
139
|
+
}
|
123
140
|
context.report({
|
124
141
|
node,
|
125
|
-
loc
|
142
|
+
loc,
|
126
143
|
messageId,
|
127
144
|
data: {
|
128
145
|
location: "before",
|
package/lib/rules/curly.js
CHANGED
@@ -457,11 +457,18 @@ module.exports = {
|
|
457
457
|
|
458
458
|
return {
|
459
459
|
IfStatement(node) {
|
460
|
-
|
460
|
+
const parent = node.parent;
|
461
|
+
const isElseIf = parent.type === "IfStatement" && parent.alternate === node;
|
462
|
+
|
463
|
+
if (!isElseIf) {
|
464
|
+
|
465
|
+
// This is a top `if`, check the whole `if-else-if` chain
|
461
466
|
prepareIfChecks(node).forEach(preparedCheck => {
|
462
467
|
preparedCheck.check();
|
463
468
|
});
|
464
469
|
}
|
470
|
+
|
471
|
+
// Skip `else if`, it's already checked (when the top `if` was visited)
|
465
472
|
},
|
466
473
|
|
467
474
|
WhileStatement(node) {
|
@@ -116,7 +116,13 @@ module.exports = {
|
|
116
116
|
if (never && hasWhitespace) {
|
117
117
|
context.report({
|
118
118
|
node,
|
119
|
-
loc:
|
119
|
+
loc: {
|
120
|
+
start: leftToken.loc.end,
|
121
|
+
end: {
|
122
|
+
line: rightToken.loc.start.line,
|
123
|
+
column: rightToken.loc.start.column - 1
|
124
|
+
}
|
125
|
+
},
|
120
126
|
messageId: "unexpectedWhitespace",
|
121
127
|
fix(fixer) {
|
122
128
|
|
@@ -134,7 +140,13 @@ module.exports = {
|
|
134
140
|
} else if (!never && !hasWhitespace) {
|
135
141
|
context.report({
|
136
142
|
node,
|
137
|
-
loc:
|
143
|
+
loc: {
|
144
|
+
start: {
|
145
|
+
line: leftToken.loc.end.line,
|
146
|
+
column: leftToken.loc.end.column - 1
|
147
|
+
},
|
148
|
+
end: rightToken.loc.start
|
149
|
+
},
|
138
150
|
messageId: "missing",
|
139
151
|
fix(fixer) {
|
140
152
|
return fixer.insertTextBefore(rightToken, " ");
|
@@ -143,7 +155,10 @@ module.exports = {
|
|
143
155
|
} else if (!never && !allowNewlines && hasNewline) {
|
144
156
|
context.report({
|
145
157
|
node,
|
146
|
-
loc:
|
158
|
+
loc: {
|
159
|
+
start: leftToken.loc.end,
|
160
|
+
end: rightToken.loc.start
|
161
|
+
},
|
147
162
|
messageId: "unexpectedNewline",
|
148
163
|
fix(fixer) {
|
149
164
|
return fixer.replaceTextRange([leftToken.range[1], rightToken.range[0]], " ");
|
package/lib/rules/id-match.js
CHANGED
package/lib/rules/index.js
CHANGED
@@ -148,6 +148,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
|
148
148
|
"no-lone-blocks": () => require("./no-lone-blocks"),
|
149
149
|
"no-lonely-if": () => require("./no-lonely-if"),
|
150
150
|
"no-loop-func": () => require("./no-loop-func"),
|
151
|
+
"no-loss-of-precision": () => require("./no-loss-of-precision"),
|
151
152
|
"no-magic-numbers": () => require("./no-magic-numbers"),
|
152
153
|
"no-misleading-character-class": () => require("./no-misleading-character-class"),
|
153
154
|
"no-mixed-operators": () => require("./no-mixed-operators"),
|
@@ -175,6 +176,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
|
175
176
|
"no-plusplus": () => require("./no-plusplus"),
|
176
177
|
"no-process-env": () => require("./no-process-env"),
|
177
178
|
"no-process-exit": () => require("./no-process-exit"),
|
179
|
+
"no-promise-executor-return": () => require("./no-promise-executor-return"),
|
178
180
|
"no-proto": () => require("./no-proto"),
|
179
181
|
"no-prototype-builtins": () => require("./no-prototype-builtins"),
|
180
182
|
"no-redeclare": () => require("./no-redeclare"),
|
@@ -211,6 +213,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
|
|
211
213
|
"no-unmodified-loop-condition": () => require("./no-unmodified-loop-condition"),
|
212
214
|
"no-unneeded-ternary": () => require("./no-unneeded-ternary"),
|
213
215
|
"no-unreachable": () => require("./no-unreachable"),
|
216
|
+
"no-unreachable-loop": () => require("./no-unreachable-loop"),
|
214
217
|
"no-unsafe-finally": () => require("./no-unsafe-finally"),
|
215
218
|
"no-unsafe-negation": () => require("./no-unsafe-negation"),
|
216
219
|
"no-unused-expressions": () => require("./no-unused-expressions"),
|
package/lib/rules/key-spacing.js
CHANGED
@@ -433,11 +433,15 @@ module.exports = {
|
|
433
433
|
tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),
|
434
434
|
tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),
|
435
435
|
isKeySide = side === "key",
|
436
|
-
locStart = isKeySide ? tokenBeforeColon.loc.start : tokenAfterColon.loc.start,
|
437
436
|
isExtra = diff > 0,
|
438
437
|
diffAbs = Math.abs(diff),
|
439
438
|
spaces = Array(diffAbs + 1).join(" ");
|
440
439
|
|
440
|
+
const locStart = isKeySide ? tokenBeforeColon.loc.end : nextColon.loc.start;
|
441
|
+
const locEnd = isKeySide ? nextColon.loc.start : tokenAfterColon.loc.start;
|
442
|
+
const missingLoc = isKeySide ? tokenBeforeColon.loc : tokenAfterColon.loc;
|
443
|
+
const loc = isExtra ? { start: locStart, end: locEnd } : missingLoc;
|
444
|
+
|
441
445
|
if ((
|
442
446
|
diff && mode === "strict" ||
|
443
447
|
diff < 0 && mode === "minimum" ||
|
@@ -482,7 +486,7 @@ module.exports = {
|
|
482
486
|
|
483
487
|
context.report({
|
484
488
|
node: property[side],
|
485
|
-
loc
|
489
|
+
loc,
|
486
490
|
messageId,
|
487
491
|
data: {
|
488
492
|
computed: property.computed ? "computed " : "",
|
@@ -152,7 +152,7 @@ module.exports = {
|
|
152
152
|
sourceCode.isSpaceBetweenTokens(prevToken, token)
|
153
153
|
) {
|
154
154
|
context.report({
|
155
|
-
loc: token.loc.start,
|
155
|
+
loc: { start: prevToken.loc.end, end: token.loc.start },
|
156
156
|
messageId: "unexpectedBefore",
|
157
157
|
data: token,
|
158
158
|
fix(fixer) {
|
@@ -203,8 +203,9 @@ module.exports = {
|
|
203
203
|
astUtils.isTokenOnSameLine(token, nextToken) &&
|
204
204
|
sourceCode.isSpaceBetweenTokens(token, nextToken)
|
205
205
|
) {
|
206
|
+
|
206
207
|
context.report({
|
207
|
-
loc: token.loc.start,
|
208
|
+
loc: { start: token.loc.end, end: nextToken.loc.start },
|
208
209
|
messageId: "unexpectedAfter",
|
209
210
|
data: token,
|
210
211
|
fix(fixer) {
|
@@ -442,6 +443,12 @@ module.exports = {
|
|
442
443
|
checkSpacingAround(sourceCode.getTokenAfter(firstToken));
|
443
444
|
}
|
444
445
|
|
446
|
+
if (node.type === "ExportAllDeclaration" && node.exported) {
|
447
|
+
const asToken = sourceCode.getTokenBefore(node.exported);
|
448
|
+
|
449
|
+
checkSpacingBefore(asToken, PREV_TOKEN_M);
|
450
|
+
}
|
451
|
+
|
445
452
|
if (node.source) {
|
446
453
|
const fromToken = sourceCode.getTokenBefore(node.source);
|
447
454
|
|
@@ -86,8 +86,14 @@ module.exports = {
|
|
86
86
|
context.report({
|
87
87
|
node,
|
88
88
|
loc: {
|
89
|
-
|
90
|
-
|
89
|
+
start: {
|
90
|
+
line: i,
|
91
|
+
column: sourceCode.lines[i - 1].length
|
92
|
+
},
|
93
|
+
end: {
|
94
|
+
line: i + 1,
|
95
|
+
column: 0
|
96
|
+
}
|
91
97
|
},
|
92
98
|
messageId: expectedLF ? "expectedLF" : "expectedCRLF",
|
93
99
|
fix: createFix(range, expectedLFChars)
|
@@ -134,7 +134,7 @@ module.exports = {
|
|
134
134
|
* @returns {boolean} True if it's an IIFE
|
135
135
|
*/
|
136
136
|
function isIIFE(node) {
|
137
|
-
return node.type === "FunctionExpression" && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
|
137
|
+
return (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && node.parent && node.parent.type === "CallExpression" && node.parent.callee === node;
|
138
138
|
}
|
139
139
|
|
140
140
|
/**
|
package/lib/rules/max-lines.js
CHANGED
@@ -53,7 +53,8 @@ module.exports = {
|
|
53
53
|
}
|
54
54
|
],
|
55
55
|
messages: {
|
56
|
-
exceed:
|
56
|
+
exceed:
|
57
|
+
"File has too many lines ({{actual}}). Maximum allowed is {{max}}."
|
57
58
|
}
|
58
59
|
},
|
59
60
|
|
@@ -61,7 +62,10 @@ module.exports = {
|
|
61
62
|
const option = context.options[0];
|
62
63
|
let max = 300;
|
63
64
|
|
64
|
-
if (
|
65
|
+
if (
|
66
|
+
typeof option === "object" &&
|
67
|
+
Object.prototype.hasOwnProperty.call(option, "max")
|
68
|
+
) {
|
65
69
|
max = option.max;
|
66
70
|
} else if (typeof option === "number") {
|
67
71
|
max = option;
|
@@ -94,7 +98,9 @@ module.exports = {
|
|
94
98
|
|
95
99
|
token = comment;
|
96
100
|
do {
|
97
|
-
token = sourceCode.getTokenBefore(token, {
|
101
|
+
token = sourceCode.getTokenBefore(token, {
|
102
|
+
includeComments: true
|
103
|
+
});
|
98
104
|
} while (isCommentNodeType(token));
|
99
105
|
|
100
106
|
if (token && astUtils.isTokenOnSameLine(token, comment)) {
|
@@ -103,7 +109,9 @@ module.exports = {
|
|
103
109
|
|
104
110
|
token = comment;
|
105
111
|
do {
|
106
|
-
token = sourceCode.getTokenAfter(token, {
|
112
|
+
token = sourceCode.getTokenAfter(token, {
|
113
|
+
includeComments: true
|
114
|
+
});
|
107
115
|
} while (isCommentNodeType(token));
|
108
116
|
|
109
117
|
if (token && astUtils.isTokenOnSameLine(comment, token)) {
|
@@ -118,7 +126,10 @@ module.exports = {
|
|
118
126
|
|
119
127
|
return {
|
120
128
|
"Program:exit"() {
|
121
|
-
let lines = sourceCode.lines.map((text, i) => ({
|
129
|
+
let lines = sourceCode.lines.map((text, i) => ({
|
130
|
+
lineNumber: i + 1,
|
131
|
+
text
|
132
|
+
}));
|
122
133
|
|
123
134
|
if (skipBlankLines) {
|
124
135
|
lines = lines.filter(l => l.text.trim() !== "");
|
@@ -127,14 +138,29 @@ module.exports = {
|
|
127
138
|
if (skipComments) {
|
128
139
|
const comments = sourceCode.getAllComments();
|
129
140
|
|
130
|
-
const commentLines = lodash.flatten(
|
141
|
+
const commentLines = lodash.flatten(
|
142
|
+
comments.map(comment => getLinesWithoutCode(comment))
|
143
|
+
);
|
131
144
|
|
132
|
-
lines = lines.filter(
|
145
|
+
lines = lines.filter(
|
146
|
+
l => !lodash.includes(commentLines, l.lineNumber)
|
147
|
+
);
|
133
148
|
}
|
134
149
|
|
135
150
|
if (lines.length > max) {
|
151
|
+
const loc = {
|
152
|
+
start: {
|
153
|
+
line: lines[max].lineNumber,
|
154
|
+
column: 0
|
155
|
+
},
|
156
|
+
end: {
|
157
|
+
line: sourceCode.lines.length,
|
158
|
+
column: lodash.last(sourceCode.lines).length
|
159
|
+
}
|
160
|
+
};
|
161
|
+
|
136
162
|
context.report({
|
137
|
-
loc
|
163
|
+
loc,
|
138
164
|
messageId: "exceed",
|
139
165
|
data: {
|
140
166
|
max,
|
@@ -39,25 +39,7 @@ module.exports = {
|
|
39
39
|
const option = context.options[0];
|
40
40
|
const multiline = option !== "never";
|
41
41
|
const allowSingleLine = option === "always-multiline";
|
42
|
-
|
43
|
-
//--------------------------------------------------------------------------
|
44
|
-
// Helpers
|
45
|
-
//--------------------------------------------------------------------------
|
46
|
-
|
47
|
-
/**
|
48
|
-
* Tests whether node is preceded by supplied tokens
|
49
|
-
* @param {ASTNode} node node to check
|
50
|
-
* @param {ASTNode} parentNode parent of node to report
|
51
|
-
* @param {boolean} expected whether newline was expected or not
|
52
|
-
* @returns {void}
|
53
|
-
* @private
|
54
|
-
*/
|
55
|
-
function reportError(node, parentNode, expected) {
|
56
|
-
context.report({
|
57
|
-
node,
|
58
|
-
messageId: `${expected ? "expected" : "unexpected"}${node === parentNode.test ? "TestCons" : "ConsAlt"}`
|
59
|
-
});
|
60
|
-
}
|
42
|
+
const sourceCode = context.getSourceCode();
|
61
43
|
|
62
44
|
//--------------------------------------------------------------------------
|
63
45
|
// Public
|
@@ -65,16 +47,39 @@ module.exports = {
|
|
65
47
|
|
66
48
|
return {
|
67
49
|
ConditionalExpression(node) {
|
68
|
-
const
|
69
|
-
const
|
50
|
+
const questionToken = sourceCode.getTokenAfter(node.test, astUtils.isNotClosingParenToken);
|
51
|
+
const colonToken = sourceCode.getTokenAfter(node.consequent, astUtils.isNotClosingParenToken);
|
52
|
+
|
53
|
+
const firstTokenOfTest = sourceCode.getFirstToken(node);
|
54
|
+
const lastTokenOfTest = sourceCode.getTokenBefore(questionToken);
|
55
|
+
const firstTokenOfConsequent = sourceCode.getTokenAfter(questionToken);
|
56
|
+
const lastTokenOfConsequent = sourceCode.getTokenBefore(colonToken);
|
57
|
+
const firstTokenOfAlternate = sourceCode.getTokenAfter(colonToken);
|
58
|
+
|
59
|
+
const areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, firstTokenOfConsequent);
|
60
|
+
const areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, firstTokenOfAlternate);
|
70
61
|
|
71
62
|
if (!multiline) {
|
72
63
|
if (!areTestAndConsequentOnSameLine) {
|
73
|
-
|
64
|
+
context.report({
|
65
|
+
node: node.test,
|
66
|
+
loc: {
|
67
|
+
start: firstTokenOfTest.loc.start,
|
68
|
+
end: lastTokenOfTest.loc.end
|
69
|
+
},
|
70
|
+
messageId: "unexpectedTestCons"
|
71
|
+
});
|
74
72
|
}
|
75
73
|
|
76
74
|
if (!areConsequentAndAlternateOnSameLine) {
|
77
|
-
|
75
|
+
context.report({
|
76
|
+
node: node.consequent,
|
77
|
+
loc: {
|
78
|
+
start: firstTokenOfConsequent.loc.start,
|
79
|
+
end: lastTokenOfConsequent.loc.end
|
80
|
+
},
|
81
|
+
messageId: "unexpectedConsAlt"
|
82
|
+
});
|
78
83
|
}
|
79
84
|
} else {
|
80
85
|
if (allowSingleLine && node.loc.start.line === node.loc.end.line) {
|
@@ -82,11 +87,25 @@ module.exports = {
|
|
82
87
|
}
|
83
88
|
|
84
89
|
if (areTestAndConsequentOnSameLine) {
|
85
|
-
|
90
|
+
context.report({
|
91
|
+
node: node.test,
|
92
|
+
loc: {
|
93
|
+
start: firstTokenOfTest.loc.start,
|
94
|
+
end: lastTokenOfTest.loc.end
|
95
|
+
},
|
96
|
+
messageId: "expectedTestCons"
|
97
|
+
});
|
86
98
|
}
|
87
99
|
|
88
100
|
if (areConsequentAndAlternateOnSameLine) {
|
89
|
-
|
101
|
+
context.report({
|
102
|
+
node: node.consequent,
|
103
|
+
loc: {
|
104
|
+
start: firstTokenOfConsequent.loc.start,
|
105
|
+
end: lastTokenOfConsequent.loc.end
|
106
|
+
},
|
107
|
+
messageId: "expectedConsAlt"
|
108
|
+
});
|
90
109
|
}
|
91
110
|
}
|
92
111
|
}
|