eslint 6.6.0 → 6.8.0

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +7 -8
  3. package/conf/config-schema.js +2 -0
  4. package/conf/default-cli-options.js +1 -1
  5. package/conf/eslint-recommended.js +0 -1
  6. package/lib/cli-engine/cascading-config-array-factory.js +38 -13
  7. package/lib/cli-engine/cli-engine.js +41 -14
  8. package/lib/cli-engine/config-array/config-array.js +13 -0
  9. package/lib/cli-engine/config-array/extracted-config.js +27 -0
  10. package/lib/cli-engine/config-array/ignore-pattern.js +231 -0
  11. package/lib/cli-engine/config-array/index.js +2 -0
  12. package/lib/cli-engine/config-array-factory.js +115 -1
  13. package/lib/cli-engine/file-enumerator.js +73 -40
  14. package/lib/cli-engine/lint-result-cache.js +2 -1
  15. package/lib/cli.js +2 -1
  16. package/lib/init/config-initializer.js +4 -3
  17. package/lib/linter/config-comment-parser.js +1 -1
  18. package/lib/linter/report-translator.js +73 -7
  19. package/lib/options.js +6 -0
  20. package/lib/rule-tester/rule-tester.js +42 -6
  21. package/lib/rules/array-bracket-spacing.js +8 -8
  22. package/lib/rules/camelcase.js +19 -6
  23. package/lib/rules/comma-dangle.js +5 -2
  24. package/lib/rules/computed-property-spacing.js +4 -4
  25. package/lib/rules/curly.js +9 -4
  26. package/lib/rules/function-call-argument-newline.js +3 -1
  27. package/lib/rules/grouped-accessor-pairs.js +224 -0
  28. package/lib/rules/indent.js +11 -0
  29. package/lib/rules/index.js +5 -0
  30. package/lib/rules/key-spacing.js +34 -15
  31. package/lib/rules/lines-between-class-members.js +42 -53
  32. package/lib/rules/multiline-comment-style.js +237 -106
  33. package/lib/rules/no-cond-assign.js +14 -4
  34. package/lib/rules/no-constructor-return.js +62 -0
  35. package/lib/rules/no-dupe-else-if.js +122 -0
  36. package/lib/rules/no-implicit-globals.js +90 -8
  37. package/lib/rules/no-inline-comments.js +25 -11
  38. package/lib/rules/no-invalid-this.js +16 -2
  39. package/lib/rules/no-multiple-empty-lines.js +1 -1
  40. package/lib/rules/no-octal-escape.js +1 -1
  41. package/lib/rules/no-restricted-imports.js +2 -2
  42. package/lib/rules/no-setter-return.js +227 -0
  43. package/lib/rules/no-underscore-dangle.js +23 -4
  44. package/lib/rules/no-unexpected-multiline.js +8 -0
  45. package/lib/rules/no-unsafe-negation.js +30 -5
  46. package/lib/rules/no-useless-computed-key.js +60 -33
  47. package/lib/rules/no-useless-escape.js +26 -3
  48. package/lib/rules/object-curly-spacing.js +8 -8
  49. package/lib/rules/operator-assignment.js +11 -2
  50. package/lib/rules/prefer-const.js +14 -7
  51. package/lib/rules/prefer-exponentiation-operator.js +189 -0
  52. package/lib/rules/prefer-numeric-literals.js +29 -28
  53. package/lib/rules/require-atomic-updates.js +1 -1
  54. package/lib/rules/require-await.js +8 -0
  55. package/lib/rules/semi.js +6 -3
  56. package/lib/rules/space-infix-ops.js +1 -1
  57. package/lib/rules/spaced-comment.js +5 -4
  58. package/lib/rules/utils/ast-utils.js +31 -4
  59. package/lib/shared/types.js +9 -0
  60. package/lib/source-code/source-code.js +87 -10
  61. package/package.json +4 -3
  62. package/lib/cli-engine/ignored-paths.js +0 -363
@@ -0,0 +1,224 @@
1
+ /**
2
+ * @fileoverview Rule to require grouped accessor pairs in object literals and classes
3
+ * @author Milos Djermanovic
4
+ */
5
+
6
+ "use strict";
7
+
8
+ //------------------------------------------------------------------------------
9
+ // Requirements
10
+ //------------------------------------------------------------------------------
11
+
12
+ const astUtils = require("./utils/ast-utils");
13
+
14
+ //------------------------------------------------------------------------------
15
+ // Typedefs
16
+ //------------------------------------------------------------------------------
17
+
18
+ /**
19
+ * Property name if it can be computed statically, otherwise the list of the tokens of the key node.
20
+ * @typedef {string|Token[]} Key
21
+ */
22
+
23
+ /**
24
+ * Accessor nodes with the same key.
25
+ * @typedef {Object} AccessorData
26
+ * @property {Key} key Accessor's key
27
+ * @property {ASTNode[]} getters List of getter nodes.
28
+ * @property {ASTNode[]} setters List of setter nodes.
29
+ */
30
+
31
+ //------------------------------------------------------------------------------
32
+ // Helpers
33
+ //------------------------------------------------------------------------------
34
+
35
+ /**
36
+ * Checks whether or not the given lists represent the equal tokens in the same order.
37
+ * Tokens are compared by their properties, not by instance.
38
+ * @param {Token[]} left First list of tokens.
39
+ * @param {Token[]} right Second list of tokens.
40
+ * @returns {boolean} `true` if the lists have same tokens.
41
+ */
42
+ function areEqualTokenLists(left, right) {
43
+ if (left.length !== right.length) {
44
+ return false;
45
+ }
46
+
47
+ for (let i = 0; i < left.length; i++) {
48
+ const leftToken = left[i],
49
+ rightToken = right[i];
50
+
51
+ if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) {
52
+ return false;
53
+ }
54
+ }
55
+
56
+ return true;
57
+ }
58
+
59
+ /**
60
+ * Checks whether or not the given keys are equal.
61
+ * @param {Key} left First key.
62
+ * @param {Key} right Second key.
63
+ * @returns {boolean} `true` if the keys are equal.
64
+ */
65
+ function areEqualKeys(left, right) {
66
+ if (typeof left === "string" && typeof right === "string") {
67
+
68
+ // Statically computed names.
69
+ return left === right;
70
+ }
71
+ if (Array.isArray(left) && Array.isArray(right)) {
72
+
73
+ // Token lists.
74
+ return areEqualTokenLists(left, right);
75
+ }
76
+
77
+ return false;
78
+ }
79
+
80
+ /**
81
+ * Checks whether or not a given node is of an accessor kind ('get' or 'set').
82
+ * @param {ASTNode} node A node to check.
83
+ * @returns {boolean} `true` if the node is of an accessor kind.
84
+ */
85
+ function isAccessorKind(node) {
86
+ return node.kind === "get" || node.kind === "set";
87
+ }
88
+
89
+ //------------------------------------------------------------------------------
90
+ // Rule Definition
91
+ //------------------------------------------------------------------------------
92
+
93
+ module.exports = {
94
+ meta: {
95
+ type: "suggestion",
96
+
97
+ docs: {
98
+ description: "require grouped accessor pairs in object literals and classes",
99
+ category: "Best Practices",
100
+ recommended: false,
101
+ url: "https://eslint.org/docs/rules/grouped-accessor-pairs"
102
+ },
103
+
104
+ schema: [
105
+ {
106
+ enum: ["anyOrder", "getBeforeSet", "setBeforeGet"]
107
+ }
108
+ ],
109
+
110
+ messages: {
111
+ notGrouped: "Accessor pair {{ formerName }} and {{ latterName }} should be grouped.",
112
+ invalidOrder: "Expected {{ latterName }} to be before {{ formerName }}."
113
+ }
114
+ },
115
+
116
+ create(context) {
117
+ const order = context.options[0] || "anyOrder";
118
+ const sourceCode = context.getSourceCode();
119
+
120
+ /**
121
+ * Reports the given accessor pair.
122
+ * @param {string} messageId messageId to report.
123
+ * @param {ASTNode} formerNode getter/setter node that is defined before `latterNode`.
124
+ * @param {ASTNode} latterNode getter/setter node that is defined after `formerNode`.
125
+ * @returns {void}
126
+ * @private
127
+ */
128
+ function report(messageId, formerNode, latterNode) {
129
+ context.report({
130
+ node: latterNode,
131
+ messageId,
132
+ loc: astUtils.getFunctionHeadLoc(latterNode.value, sourceCode),
133
+ data: {
134
+ formerName: astUtils.getFunctionNameWithKind(formerNode.value),
135
+ latterName: astUtils.getFunctionNameWithKind(latterNode.value)
136
+ }
137
+ });
138
+ }
139
+
140
+ /**
141
+ * Creates a new `AccessorData` object for the given getter or setter node.
142
+ * @param {ASTNode} node A getter or setter node.
143
+ * @returns {AccessorData} New `AccessorData` object that contains the given node.
144
+ * @private
145
+ */
146
+ function createAccessorData(node) {
147
+ const name = astUtils.getStaticPropertyName(node);
148
+ const key = (name !== null) ? name : sourceCode.getTokens(node.key);
149
+
150
+ return {
151
+ key,
152
+ getters: node.kind === "get" ? [node] : [],
153
+ setters: node.kind === "set" ? [node] : []
154
+ };
155
+ }
156
+
157
+ /**
158
+ * Merges the given `AccessorData` object into the given accessors list.
159
+ * @param {AccessorData[]} accessors The list to merge into.
160
+ * @param {AccessorData} accessorData The object to merge.
161
+ * @returns {AccessorData[]} The same instance with the merged object.
162
+ * @private
163
+ */
164
+ function mergeAccessorData(accessors, accessorData) {
165
+ const equalKeyElement = accessors.find(a => areEqualKeys(a.key, accessorData.key));
166
+
167
+ if (equalKeyElement) {
168
+ equalKeyElement.getters.push(...accessorData.getters);
169
+ equalKeyElement.setters.push(...accessorData.setters);
170
+ } else {
171
+ accessors.push(accessorData);
172
+ }
173
+
174
+ return accessors;
175
+ }
176
+
177
+ /**
178
+ * Checks accessor pairs in the given list of nodes.
179
+ * @param {ASTNode[]} nodes The list to check.
180
+ * @param {Function} shouldCheck – Predicate that returns `true` if the node should be checked.
181
+ * @returns {void}
182
+ * @private
183
+ */
184
+ function checkList(nodes, shouldCheck) {
185
+ const accessors = nodes
186
+ .filter(shouldCheck)
187
+ .filter(isAccessorKind)
188
+ .map(createAccessorData)
189
+ .reduce(mergeAccessorData, []);
190
+
191
+ for (const { getters, setters } of accessors) {
192
+
193
+ // Don't report accessor properties that have duplicate getters or setters.
194
+ if (getters.length === 1 && setters.length === 1) {
195
+ const [getter] = getters,
196
+ [setter] = setters,
197
+ getterIndex = nodes.indexOf(getter),
198
+ setterIndex = nodes.indexOf(setter),
199
+ formerNode = getterIndex < setterIndex ? getter : setter,
200
+ latterNode = getterIndex < setterIndex ? setter : getter;
201
+
202
+ if (Math.abs(getterIndex - setterIndex) > 1) {
203
+ report("notGrouped", formerNode, latterNode);
204
+ } else if (
205
+ (order === "getBeforeSet" && getterIndex > setterIndex) ||
206
+ (order === "setBeforeGet" && getterIndex < setterIndex)
207
+ ) {
208
+ report("invalidOrder", formerNode, latterNode);
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ return {
215
+ ObjectExpression(node) {
216
+ checkList(node.properties, n => n.type === "Property");
217
+ },
218
+ ClassBody(node) {
219
+ checkList(node.body, n => n.type === "MethodDefinition" && !n.static);
220
+ checkList(node.body, n => n.type === "MethodDefinition" && n.static);
221
+ }
222
+ };
223
+ }
224
+ };
@@ -1492,6 +1492,17 @@ module.exports = {
1492
1492
  );
1493
1493
  },
1494
1494
 
1495
+ JSXSpreadAttribute(node) {
1496
+ const openingCurly = sourceCode.getFirstToken(node);
1497
+ const closingCurly = sourceCode.getLastToken(node);
1498
+
1499
+ offsets.setDesiredOffsets(
1500
+ [openingCurly.range[1], closingCurly.range[0]],
1501
+ openingCurly,
1502
+ 1
1503
+ );
1504
+ },
1505
+
1495
1506
  "*"(node) {
1496
1507
  const firstToken = sourceCode.getFirstToken(node);
1497
1508
 
@@ -52,6 +52,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
52
52
  "generator-star-spacing": () => require("./generator-star-spacing"),
53
53
  "getter-return": () => require("./getter-return"),
54
54
  "global-require": () => require("./global-require"),
55
+ "grouped-accessor-pairs": () => require("./grouped-accessor-pairs"),
55
56
  "guard-for-in": () => require("./guard-for-in"),
56
57
  "handle-callback-err": () => require("./handle-callback-err"),
57
58
  "id-blacklist": () => require("./id-blacklist"),
@@ -101,6 +102,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
101
102
  "no-console": () => require("./no-console"),
102
103
  "no-const-assign": () => require("./no-const-assign"),
103
104
  "no-constant-condition": () => require("./no-constant-condition"),
105
+ "no-constructor-return": () => require("./no-constructor-return"),
104
106
  "no-continue": () => require("./no-continue"),
105
107
  "no-control-regex": () => require("./no-control-regex"),
106
108
  "no-debugger": () => require("./no-debugger"),
@@ -108,6 +110,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
108
110
  "no-div-regex": () => require("./no-div-regex"),
109
111
  "no-dupe-args": () => require("./no-dupe-args"),
110
112
  "no-dupe-class-members": () => require("./no-dupe-class-members"),
113
+ "no-dupe-else-if": () => require("./no-dupe-else-if"),
111
114
  "no-dupe-keys": () => require("./no-dupe-keys"),
112
115
  "no-duplicate-case": () => require("./no-duplicate-case"),
113
116
  "no-duplicate-imports": () => require("./no-duplicate-imports"),
@@ -186,6 +189,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
186
189
  "no-self-assign": () => require("./no-self-assign"),
187
190
  "no-self-compare": () => require("./no-self-compare"),
188
191
  "no-sequences": () => require("./no-sequences"),
192
+ "no-setter-return": () => require("./no-setter-return"),
189
193
  "no-shadow": () => require("./no-shadow"),
190
194
  "no-shadow-restricted-names": () => require("./no-shadow-restricted-names"),
191
195
  "no-spaced-func": () => require("./no-spaced-func"),
@@ -238,6 +242,7 @@ module.exports = new LazyLoadingRuleMap(Object.entries({
238
242
  "prefer-arrow-callback": () => require("./prefer-arrow-callback"),
239
243
  "prefer-const": () => require("./prefer-const"),
240
244
  "prefer-destructuring": () => require("./prefer-destructuring"),
245
+ "prefer-exponentiation-operator": () => require("./prefer-exponentiation-operator"),
241
246
  "prefer-named-capture-group": () => require("./prefer-named-capture-group"),
242
247
  "prefer-numeric-literals": () => require("./prefer-numeric-literals"),
243
248
  "prefer-object-spread": () => require("./prefer-object-spread"),
@@ -42,6 +42,18 @@ function isSingleLine(node) {
42
42
  return (node.loc.end.line === node.loc.start.line);
43
43
  }
44
44
 
45
+ /**
46
+ * Checks whether the properties on a single line.
47
+ * @param {ASTNode[]} properties List of Property AST nodes.
48
+ * @returns {boolean} True if all properies is on a single line.
49
+ */
50
+ function isSingleLineProperties(properties) {
51
+ const [firstProp] = properties,
52
+ lastProp = last(properties);
53
+
54
+ return firstProp.loc.start.line === lastProp.loc.end.line;
55
+ }
56
+
45
57
  /**
46
58
  * Initializes a single option property from the configuration with defaults for undefined values
47
59
  * @param {Object} toOptions Object to be initialized
@@ -583,17 +595,6 @@ module.exports = {
583
595
  }
584
596
  }
585
597
 
586
- /**
587
- * Verifies vertical alignment, taking into account groups of properties.
588
- * @param {ASTNode} node ObjectExpression node being evaluated.
589
- * @returns {void}
590
- */
591
- function verifyAlignment(node) {
592
- createGroups(node).forEach(group => {
593
- verifyGroupAlignment(group.filter(isKeyValueProperty));
594
- });
595
- }
596
-
597
598
  /**
598
599
  * Verifies spacing of property conforms to specified options.
599
600
  * @param {ASTNode} node Property node being evaluated.
@@ -611,17 +612,35 @@ module.exports = {
611
612
 
612
613
  /**
613
614
  * Verifies spacing of each property in a list.
614
- * @param {ASTNode[]} properties List of Property AST nodes.
615
+ * @param {ASTNode[]} properties List of Property AST nodes.
616
+ * @param {Object} lineOptions Configured singleLine or multiLine options
615
617
  * @returns {void}
616
618
  */
617
- function verifyListSpacing(properties) {
619
+ function verifyListSpacing(properties, lineOptions) {
618
620
  const length = properties.length;
619
621
 
620
622
  for (let i = 0; i < length; i++) {
621
- verifySpacing(properties[i], singleLineOptions);
623
+ verifySpacing(properties[i], lineOptions);
622
624
  }
623
625
  }
624
626
 
627
+ /**
628
+ * Verifies vertical alignment, taking into account groups of properties.
629
+ * @param {ASTNode} node ObjectExpression node being evaluated.
630
+ * @returns {void}
631
+ */
632
+ function verifyAlignment(node) {
633
+ createGroups(node).forEach(group => {
634
+ const properties = group.filter(isKeyValueProperty);
635
+
636
+ if (properties.length > 0 && isSingleLineProperties(properties)) {
637
+ verifyListSpacing(properties, multiLineOptions);
638
+ } else {
639
+ verifyGroupAlignment(properties);
640
+ }
641
+ });
642
+ }
643
+
625
644
  //--------------------------------------------------------------------------
626
645
  // Public API
627
646
  //--------------------------------------------------------------------------
@@ -631,7 +650,7 @@ module.exports = {
631
650
  return {
632
651
  ObjectExpression(node) {
633
652
  if (isSingleLine(node)) {
634
- verifyListSpacing(node.properties.filter(isKeyValueProperty));
653
+ verifyListSpacing(node.properties.filter(isKeyValueProperty), singleLineOptions);
635
654
  } else {
636
655
  verifyAlignment(node);
637
656
  }
@@ -54,62 +54,45 @@ module.exports = {
54
54
  const sourceCode = context.getSourceCode();
55
55
 
56
56
  /**
57
- * Checks if there is padding between two tokens
58
- * @param {Token} first The first token
59
- * @param {Token} second The second token
60
- * @returns {boolean} True if there is at least a line between the tokens
57
+ * Return the last token among the consecutive tokens that have no exceed max line difference in between, before the first token in the next member.
58
+ * @param {Token} prevLastToken The last token in the previous member node.
59
+ * @param {Token} nextFirstToken The first token in the next member node.
60
+ * @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
61
+ * @returns {Token} The last token among the consecutive tokens.
61
62
  */
62
- function isPaddingBetweenTokens(first, second) {
63
- const comments = sourceCode.getCommentsBefore(second);
64
- const len = comments.length;
63
+ function findLastConsecutiveTokenAfter(prevLastToken, nextFirstToken, maxLine) {
64
+ const after = sourceCode.getTokenAfter(prevLastToken, { includeComments: true });
65
65
 
66
- // If there is no comments
67
- if (len === 0) {
68
- const linesBetweenFstAndSnd = second.loc.start.line - first.loc.end.line - 1;
69
-
70
- return linesBetweenFstAndSnd >= 1;
71
- }
72
-
73
-
74
- // If there are comments
75
- let sumOfCommentLines = 0; // the numbers of lines of comments
76
- let prevCommentLineNum = -1; // line number of the end of the previous comment
77
-
78
- for (let i = 0; i < len; i++) {
79
- const commentLinesOfThisComment = comments[i].loc.end.line - comments[i].loc.start.line + 1;
80
-
81
- sumOfCommentLines += commentLinesOfThisComment;
82
-
83
- /*
84
- * If this comment and the previous comment are in the same line,
85
- * the count of comment lines is duplicated. So decrement sumOfCommentLines.
86
- */
87
- if (prevCommentLineNum === comments[i].loc.start.line) {
88
- sumOfCommentLines -= 1;
89
- }
90
-
91
- prevCommentLineNum = comments[i].loc.end.line;
66
+ if (after !== nextFirstToken && after.loc.start.line - prevLastToken.loc.end.line <= maxLine) {
67
+ return findLastConsecutiveTokenAfter(after, nextFirstToken, maxLine);
92
68
  }
69
+ return prevLastToken;
70
+ }
93
71
 
94
- /*
95
- * If the first block and the first comment are in the same line,
96
- * the count of comment lines is duplicated. So decrement sumOfCommentLines.
97
- */
98
- if (first.loc.end.line === comments[0].loc.start.line) {
99
- sumOfCommentLines -= 1;
100
- }
72
+ /**
73
+ * Return the first token among the consecutive tokens that have no exceed max line difference in between, after the last token in the previous member.
74
+ * @param {Token} nextFirstToken The first token in the next member node.
75
+ * @param {Token} prevLastToken The last token in the previous member node.
76
+ * @param {number} maxLine The maximum number of allowed line difference between consecutive tokens.
77
+ * @returns {Token} The first token among the consecutive tokens.
78
+ */
79
+ function findFirstConsecutiveTokenBefore(nextFirstToken, prevLastToken, maxLine) {
80
+ const before = sourceCode.getTokenBefore(nextFirstToken, { includeComments: true });
101
81
 
102
- /*
103
- * If the last comment and the second block are in the same line,
104
- * the count of comment lines is duplicated. So decrement sumOfCommentLines.
105
- */
106
- if (comments[len - 1].loc.end.line === second.loc.start.line) {
107
- sumOfCommentLines -= 1;
82
+ if (before !== prevLastToken && nextFirstToken.loc.start.line - before.loc.end.line <= maxLine) {
83
+ return findFirstConsecutiveTokenBefore(before, prevLastToken, maxLine);
108
84
  }
85
+ return nextFirstToken;
86
+ }
109
87
 
110
- const linesBetweenFstAndSnd = second.loc.start.line - first.loc.end.line - 1;
111
-
112
- return linesBetweenFstAndSnd - sumOfCommentLines >= 1;
88
+ /**
89
+ * Checks if there is a token or comment between two tokens.
90
+ * @param {Token} before The token before.
91
+ * @param {Token} after The token after.
92
+ * @returns {boolean} True if there is a token or comment between two tokens.
93
+ */
94
+ function hasTokenOrCommentBetween(before, after) {
95
+ return sourceCode.getTokensBetween(before, after, { includeComments: true }).length !== 0;
113
96
  }
114
97
 
115
98
  return {
@@ -120,10 +103,13 @@ module.exports = {
120
103
  const curFirst = sourceCode.getFirstToken(body[i]);
121
104
  const curLast = sourceCode.getLastToken(body[i]);
122
105
  const nextFirst = sourceCode.getFirstToken(body[i + 1]);
123
- const isPadded = isPaddingBetweenTokens(curLast, nextFirst);
124
106
  const isMulti = !astUtils.isTokenOnSameLine(curFirst, curLast);
125
107
  const skip = !isMulti && options[1].exceptAfterSingleLine;
126
-
108
+ const beforePadding = findLastConsecutiveTokenAfter(curLast, nextFirst, 1);
109
+ const afterPadding = findFirstConsecutiveTokenBefore(nextFirst, curLast, 1);
110
+ const isPadded = afterPadding.loc.start.line - beforePadding.loc.end.line > 1;
111
+ const hasTokenInPadding = hasTokenOrCommentBetween(beforePadding, afterPadding);
112
+ const curLineLastToken = findLastConsecutiveTokenAfter(curLast, nextFirst, 0);
127
113
 
128
114
  if ((options[0] === "always" && !skip && !isPadded) ||
129
115
  (options[0] === "never" && isPadded)) {
@@ -131,9 +117,12 @@ module.exports = {
131
117
  node: body[i + 1],
132
118
  messageId: isPadded ? "never" : "always",
133
119
  fix(fixer) {
120
+ if (hasTokenInPadding) {
121
+ return null;
122
+ }
134
123
  return isPadded
135
- ? fixer.replaceTextRange([curLast.range[1], nextFirst.range[0]], "\n")
136
- : fixer.insertTextAfter(curLast, "\n");
124
+ ? fixer.replaceTextRange([beforePadding.range[1], afterPadding.range[0]], "\n")
125
+ : fixer.insertTextAfter(curLineLastToken, "\n");
137
126
  }
138
127
  });
139
128
  }