eslint 8.19.0 → 8.22.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.
- package/README.md +3 -9
- package/lib/config/default-config.js +16 -7
- package/lib/config/flat-config-array.js +94 -14
- package/lib/config/flat-config-helpers.js +9 -1
- package/lib/eslint/eslint-helpers.js +618 -0
- package/lib/eslint/flat-eslint.js +1179 -0
- package/lib/eslint/index.js +3 -1
- package/lib/linter/linter.js +68 -4
- package/lib/rule-tester/flat-rule-tester.js +41 -38
- package/lib/rule-tester/rule-tester.js +42 -0
- package/lib/rules/comma-spacing.js +35 -41
- package/lib/rules/key-spacing.js +4 -1
- package/lib/rules/lines-around-comment.js +11 -4
- package/lib/rules/no-warning-comments.js +26 -40
- package/lib/rules/object-shorthand.js +15 -0
- package/lib/rules/sort-keys.js +43 -0
- package/lib/unsupported-api.js +4 -0
- package/package.json +15 -10
package/lib/eslint/index.js
CHANGED
package/lib/linter/linter.js
CHANGED
@@ -1510,7 +1510,31 @@ class Linter {
|
|
1510
1510
|
options.filterCodeBlock ||
|
1511
1511
|
(blockFilename => blockFilename.endsWith(".js"));
|
1512
1512
|
const originalExtname = path.extname(filename);
|
1513
|
-
|
1513
|
+
|
1514
|
+
let blocks;
|
1515
|
+
|
1516
|
+
try {
|
1517
|
+
blocks = preprocess(text, filenameToExpose);
|
1518
|
+
} catch (ex) {
|
1519
|
+
|
1520
|
+
// If the message includes a leading line number, strip it:
|
1521
|
+
const message = `Preprocessing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
|
1522
|
+
|
1523
|
+
debug("%s\n%s", message, ex.stack);
|
1524
|
+
|
1525
|
+
return [
|
1526
|
+
{
|
1527
|
+
ruleId: null,
|
1528
|
+
fatal: true,
|
1529
|
+
severity: 2,
|
1530
|
+
message,
|
1531
|
+
line: ex.lineNumber,
|
1532
|
+
column: ex.column
|
1533
|
+
}
|
1534
|
+
];
|
1535
|
+
}
|
1536
|
+
|
1537
|
+
const messageLists = blocks.map((block, i) => {
|
1514
1538
|
debug("A code block was found: %o", block.filename || "(unnamed)");
|
1515
1539
|
|
1516
1540
|
// Keep the legacy behavior.
|
@@ -1584,6 +1608,11 @@ class Linter {
|
|
1584
1608
|
...languageOptions.globals
|
1585
1609
|
};
|
1586
1610
|
|
1611
|
+
// double check that there is a parser to avoid mysterious error messages
|
1612
|
+
if (!languageOptions.parser) {
|
1613
|
+
throw new TypeError(`No parser specified for ${options.filename}`);
|
1614
|
+
}
|
1615
|
+
|
1587
1616
|
// Espree expects this information to be passed in
|
1588
1617
|
if (isEspree(languageOptions.parser)) {
|
1589
1618
|
const parserOptions = languageOptions.parserOptions;
|
@@ -1746,12 +1775,24 @@ class Linter {
|
|
1746
1775
|
debug("With flat config: %s", options.filename);
|
1747
1776
|
|
1748
1777
|
// we need a filename to match configs against
|
1749
|
-
const filename = options.filename || "
|
1778
|
+
const filename = options.filename || "__placeholder__.js";
|
1750
1779
|
|
1751
1780
|
// Store the config array in order to get plugin envs and rules later.
|
1752
1781
|
internalSlotsMap.get(this).lastConfigArray = configArray;
|
1753
1782
|
const config = configArray.getConfig(filename);
|
1754
1783
|
|
1784
|
+
if (!config) {
|
1785
|
+
return [
|
1786
|
+
{
|
1787
|
+
ruleId: null,
|
1788
|
+
severity: 1,
|
1789
|
+
message: `No matching configuration found for ${filename}.`,
|
1790
|
+
line: 0,
|
1791
|
+
column: 0
|
1792
|
+
}
|
1793
|
+
];
|
1794
|
+
}
|
1795
|
+
|
1755
1796
|
// Verify.
|
1756
1797
|
if (config.processor) {
|
1757
1798
|
debug("Apply the processor: %o", config.processor);
|
@@ -1788,13 +1829,36 @@ class Linter {
|
|
1788
1829
|
const physicalFilename = options.physicalFilename || filenameToExpose;
|
1789
1830
|
const text = ensureText(textOrSourceCode);
|
1790
1831
|
const preprocess = options.preprocess || (rawText => [rawText]);
|
1791
|
-
|
1792
1832
|
const postprocess = options.postprocess || (messagesList => messagesList.flat());
|
1793
1833
|
const filterCodeBlock =
|
1794
1834
|
options.filterCodeBlock ||
|
1795
1835
|
(blockFilename => blockFilename.endsWith(".js"));
|
1796
1836
|
const originalExtname = path.extname(filename);
|
1797
|
-
|
1837
|
+
|
1838
|
+
let blocks;
|
1839
|
+
|
1840
|
+
try {
|
1841
|
+
blocks = preprocess(text, filenameToExpose);
|
1842
|
+
} catch (ex) {
|
1843
|
+
|
1844
|
+
// If the message includes a leading line number, strip it:
|
1845
|
+
const message = `Preprocessing error: ${ex.message.replace(/^line \d+:/iu, "").trim()}`;
|
1846
|
+
|
1847
|
+
debug("%s\n%s", message, ex.stack);
|
1848
|
+
|
1849
|
+
return [
|
1850
|
+
{
|
1851
|
+
ruleId: null,
|
1852
|
+
fatal: true,
|
1853
|
+
severity: 2,
|
1854
|
+
message,
|
1855
|
+
line: ex.lineNumber,
|
1856
|
+
column: ex.column
|
1857
|
+
}
|
1858
|
+
];
|
1859
|
+
}
|
1860
|
+
|
1861
|
+
const messageLists = blocks.map((block, i) => {
|
1798
1862
|
debug("A code block was found: %o", block.filename || "(unnamed)");
|
1799
1863
|
|
1800
1864
|
// Keep the legacy behavior.
|
@@ -480,51 +480,54 @@ class FlatRuleTester {
|
|
480
480
|
].concat(scenarioErrors).join("\n"));
|
481
481
|
}
|
482
482
|
|
483
|
-
const baseConfig =
|
484
|
-
|
485
|
-
|
486
|
-
// copy root plugin over
|
487
|
-
"@": {
|
488
|
-
|
489
|
-
/*
|
490
|
-
* Parsers are wrapped to detect more errors, so this needs
|
491
|
-
* to be a new object for each call to run(), otherwise the
|
492
|
-
* parsers will be wrapped multiple times.
|
493
|
-
*/
|
494
|
-
parsers: {
|
495
|
-
...defaultConfig[0].plugins["@"].parsers
|
496
|
-
},
|
483
|
+
const baseConfig = [
|
484
|
+
{
|
485
|
+
plugins: {
|
497
486
|
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
487
|
+
// copy root plugin over
|
488
|
+
"@": {
|
489
|
+
|
490
|
+
/*
|
491
|
+
* Parsers are wrapped to detect more errors, so this needs
|
492
|
+
* to be a new object for each call to run(), otherwise the
|
493
|
+
* parsers will be wrapped multiple times.
|
494
|
+
*/
|
495
|
+
parsers: {
|
496
|
+
...defaultConfig[0].plugins["@"].parsers
|
497
|
+
},
|
498
|
+
|
499
|
+
/*
|
500
|
+
* The rules key on the default plugin is a proxy to lazy-load
|
501
|
+
* just the rules that are needed. So, don't create a new object
|
502
|
+
* here, just use the default one to keep that performance
|
503
|
+
* enhancement.
|
504
|
+
*/
|
505
|
+
rules: defaultConfig[0].plugins["@"].rules
|
506
|
+
},
|
507
|
+
"rule-to-test": {
|
508
|
+
rules: {
|
509
|
+
[ruleName]: Object.assign({}, rule, {
|
509
510
|
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
511
|
+
// Create a wrapper rule that freezes the `context` properties.
|
512
|
+
create(context) {
|
513
|
+
freezeDeeply(context.options);
|
514
|
+
freezeDeeply(context.settings);
|
515
|
+
freezeDeeply(context.parserOptions);
|
515
516
|
|
516
|
-
|
517
|
+
// freezeDeeply(context.languageOptions);
|
517
518
|
|
518
|
-
|
519
|
-
|
520
|
-
|
519
|
+
return (typeof rule === "function" ? rule : rule.create)(context);
|
520
|
+
}
|
521
|
+
})
|
522
|
+
}
|
521
523
|
}
|
524
|
+
},
|
525
|
+
languageOptions: {
|
526
|
+
...defaultConfig[0].languageOptions
|
522
527
|
}
|
523
528
|
},
|
524
|
-
|
525
|
-
|
526
|
-
}
|
527
|
-
};
|
529
|
+
...defaultConfig.slice(1)
|
530
|
+
];
|
528
531
|
|
529
532
|
/**
|
530
533
|
* Run the rule for the given item
|
@@ -305,6 +305,36 @@ function getCommentsDeprecation() {
|
|
305
305
|
);
|
306
306
|
}
|
307
307
|
|
308
|
+
/**
|
309
|
+
* Emit a deprecation warning if function-style format is being used.
|
310
|
+
* @param {string} ruleName Name of the rule.
|
311
|
+
* @returns {void}
|
312
|
+
*/
|
313
|
+
function emitLegacyRuleAPIWarning(ruleName) {
|
314
|
+
if (!emitLegacyRuleAPIWarning[`warned-${ruleName}`]) {
|
315
|
+
emitLegacyRuleAPIWarning[`warned-${ruleName}`] = true;
|
316
|
+
process.emitWarning(
|
317
|
+
`"${ruleName}" rule is using the deprecated function-style format and will stop working in ESLint v9. Please use object-style format: https://eslint.org/docs/developer-guide/working-with-rules`,
|
318
|
+
"DeprecationWarning"
|
319
|
+
);
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
/**
|
324
|
+
* Emit a deprecation warning if rule has options but is missing the "meta.schema" property
|
325
|
+
* @param {string} ruleName Name of the rule.
|
326
|
+
* @returns {void}
|
327
|
+
*/
|
328
|
+
function emitMissingSchemaWarning(ruleName) {
|
329
|
+
if (!emitMissingSchemaWarning[`warned-${ruleName}`]) {
|
330
|
+
emitMissingSchemaWarning[`warned-${ruleName}`] = true;
|
331
|
+
process.emitWarning(
|
332
|
+
`"${ruleName}" rule has options but is missing the "meta.schema" property and will stop working in ESLint v9. Please add a schema: https://eslint.org/docs/developer-guide/working-with-rules#options-schemas`,
|
333
|
+
"DeprecationWarning"
|
334
|
+
);
|
335
|
+
}
|
336
|
+
}
|
337
|
+
|
308
338
|
//------------------------------------------------------------------------------
|
309
339
|
// Public Interface
|
310
340
|
//------------------------------------------------------------------------------
|
@@ -521,6 +551,9 @@ class RuleTester {
|
|
521
551
|
].concat(scenarioErrors).join("\n"));
|
522
552
|
}
|
523
553
|
|
554
|
+
if (typeof rule === "function") {
|
555
|
+
emitLegacyRuleAPIWarning(ruleName);
|
556
|
+
}
|
524
557
|
|
525
558
|
linter.defineRule(ruleName, Object.assign({}, rule, {
|
526
559
|
|
@@ -578,6 +611,15 @@ class RuleTester {
|
|
578
611
|
|
579
612
|
if (hasOwnProperty(item, "options")) {
|
580
613
|
assert(Array.isArray(item.options), "options must be an array");
|
614
|
+
if (
|
615
|
+
item.options.length > 0 &&
|
616
|
+
typeof rule === "object" &&
|
617
|
+
(
|
618
|
+
!rule.meta || (rule.meta && (typeof rule.meta.schema === "undefined" || rule.meta.schema === null))
|
619
|
+
)
|
620
|
+
) {
|
621
|
+
emitMissingSchemaWarning(ruleName);
|
622
|
+
}
|
581
623
|
config.rules[ruleName] = [1].concat(item.options);
|
582
624
|
} else {
|
583
625
|
config.rules[ruleName] = 1;
|
@@ -103,38 +103,6 @@ module.exports = {
|
|
103
103
|
});
|
104
104
|
}
|
105
105
|
|
106
|
-
/**
|
107
|
-
* Validates the spacing around a comma token.
|
108
|
-
* @param {Object} tokens The tokens to be validated.
|
109
|
-
* @param {Token} tokens.comma The token representing the comma.
|
110
|
-
* @param {Token} [tokens.left] The last token before the comma.
|
111
|
-
* @param {Token} [tokens.right] The first token after the comma.
|
112
|
-
* @param {Token|ASTNode} reportItem The item to use when reporting an error.
|
113
|
-
* @returns {void}
|
114
|
-
* @private
|
115
|
-
*/
|
116
|
-
function validateCommaItemSpacing(tokens, reportItem) {
|
117
|
-
if (tokens.left && astUtils.isTokenOnSameLine(tokens.left, tokens.comma) &&
|
118
|
-
(options.before !== sourceCode.isSpaceBetweenTokens(tokens.left, tokens.comma))
|
119
|
-
) {
|
120
|
-
report(reportItem, "before", tokens.left);
|
121
|
-
}
|
122
|
-
|
123
|
-
if (tokens.right && astUtils.isClosingParenToken(tokens.right)) {
|
124
|
-
return;
|
125
|
-
}
|
126
|
-
|
127
|
-
if (tokens.right && !options.after && tokens.right.type === "Line") {
|
128
|
-
return;
|
129
|
-
}
|
130
|
-
|
131
|
-
if (tokens.right && astUtils.isTokenOnSameLine(tokens.comma, tokens.right) &&
|
132
|
-
(options.after !== sourceCode.isSpaceBetweenTokens(tokens.comma, tokens.right))
|
133
|
-
) {
|
134
|
-
report(reportItem, "after", tokens.right);
|
135
|
-
}
|
136
|
-
}
|
137
|
-
|
138
106
|
/**
|
139
107
|
* Adds null elements of the given ArrayExpression or ArrayPattern node to the ignore list.
|
140
108
|
* @param {ASTNode} node An ArrayExpression or ArrayPattern node.
|
@@ -172,18 +140,44 @@ module.exports = {
|
|
172
140
|
return;
|
173
141
|
}
|
174
142
|
|
175
|
-
if (token && token.type === "JSXText") {
|
176
|
-
return;
|
177
|
-
}
|
178
|
-
|
179
143
|
const previousToken = tokensAndComments[i - 1];
|
180
144
|
const nextToken = tokensAndComments[i + 1];
|
181
145
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
146
|
+
if (
|
147
|
+
previousToken &&
|
148
|
+
!astUtils.isCommaToken(previousToken) && // ignore spacing between two commas
|
149
|
+
|
150
|
+
/*
|
151
|
+
* `commaTokensToIgnore` are ending commas of `null` elements (array holes/elisions).
|
152
|
+
* In addition to spacing between two commas, this can also ignore:
|
153
|
+
*
|
154
|
+
* - Spacing after `[` (controlled by array-bracket-spacing)
|
155
|
+
* Example: [ , ]
|
156
|
+
* ^
|
157
|
+
* - Spacing after a comment (for backwards compatibility, this was possibly unintentional)
|
158
|
+
* Example: [a, /* * / ,]
|
159
|
+
* ^
|
160
|
+
*/
|
161
|
+
!commaTokensToIgnore.includes(token) &&
|
162
|
+
|
163
|
+
astUtils.isTokenOnSameLine(previousToken, token) &&
|
164
|
+
options.before !== sourceCode.isSpaceBetweenTokens(previousToken, token)
|
165
|
+
) {
|
166
|
+
report(token, "before", previousToken);
|
167
|
+
}
|
168
|
+
|
169
|
+
if (
|
170
|
+
nextToken &&
|
171
|
+
!astUtils.isCommaToken(nextToken) && // ignore spacing between two commas
|
172
|
+
!astUtils.isClosingParenToken(nextToken) && // controlled by space-in-parens
|
173
|
+
!astUtils.isClosingBracketToken(nextToken) && // controlled by array-bracket-spacing
|
174
|
+
!astUtils.isClosingBraceToken(nextToken) && // controlled by object-curly-spacing
|
175
|
+
!(!options.after && nextToken.type === "Line") && // special case, allow space before line comment
|
176
|
+
astUtils.isTokenOnSameLine(token, nextToken) &&
|
177
|
+
options.after !== sourceCode.isSpaceBetweenTokens(token, nextToken)
|
178
|
+
) {
|
179
|
+
report(token, "after", nextToken);
|
180
|
+
}
|
187
181
|
});
|
188
182
|
},
|
189
183
|
ArrayExpression: addNullElementsToIgnoreList,
|
package/lib/rules/key-spacing.js
CHANGED
@@ -9,6 +9,9 @@
|
|
9
9
|
//------------------------------------------------------------------------------
|
10
10
|
|
11
11
|
const astUtils = require("./utils/ast-utils");
|
12
|
+
const GraphemeSplitter = require("grapheme-splitter");
|
13
|
+
|
14
|
+
const splitter = new GraphemeSplitter();
|
12
15
|
|
13
16
|
//------------------------------------------------------------------------------
|
14
17
|
// Helpers
|
@@ -508,7 +511,7 @@ module.exports = {
|
|
508
511
|
const startToken = sourceCode.getFirstToken(property);
|
509
512
|
const endToken = getLastTokenBeforeColon(property.key);
|
510
513
|
|
511
|
-
return
|
514
|
+
return splitter.countGraphemes(sourceCode.getText().slice(startToken.range[0], endToken.range[1]));
|
512
515
|
}
|
513
516
|
|
514
517
|
/**
|
@@ -231,9 +231,15 @@ module.exports = {
|
|
231
231
|
const parent = getParentNodeOfToken(token);
|
232
232
|
|
233
233
|
if (parent && isParentNodeType(parent, nodeType)) {
|
234
|
-
|
235
|
-
|
236
|
-
|
234
|
+
let parentStartNodeOrToken = parent;
|
235
|
+
|
236
|
+
if (parent.type === "StaticBlock") {
|
237
|
+
parentStartNodeOrToken = sourceCode.getFirstToken(parent, { skip: 1 }); // opening brace of the static block
|
238
|
+
} else if (parent.type === "SwitchStatement") {
|
239
|
+
parentStartNodeOrToken = sourceCode.getTokenAfter(parent.discriminant, {
|
240
|
+
filter: astUtils.isOpeningBraceToken
|
241
|
+
}); // opening brace of the switch statement
|
242
|
+
}
|
237
243
|
|
238
244
|
return token.loc.start.line - parentStartNodeOrToken.loc.start.line === 1;
|
239
245
|
}
|
@@ -264,7 +270,8 @@ module.exports = {
|
|
264
270
|
isCommentAtParentStart(token, "ClassBody") ||
|
265
271
|
isCommentAtParentStart(token, "BlockStatement") ||
|
266
272
|
isCommentAtParentStart(token, "StaticBlock") ||
|
267
|
-
isCommentAtParentStart(token, "SwitchCase")
|
273
|
+
isCommentAtParentStart(token, "SwitchCase") ||
|
274
|
+
isCommentAtParentStart(token, "SwitchStatement")
|
268
275
|
);
|
269
276
|
}
|
270
277
|
|
@@ -64,59 +64,45 @@ module.exports = {
|
|
64
64
|
*/
|
65
65
|
function convertToRegExp(term) {
|
66
66
|
const escaped = escapeRegExp(term);
|
67
|
-
const wordBoundary = "\\b";
|
68
|
-
const eitherOrWordBoundary = `|${wordBoundary}`;
|
69
|
-
let prefix;
|
70
67
|
|
71
68
|
/*
|
72
|
-
*
|
73
|
-
*
|
74
|
-
*
|
75
|
-
*
|
76
|
-
*
|
77
|
-
*
|
78
|
-
*
|
79
|
-
*
|
69
|
+
* When matching at the start, ignore leading whitespace, and
|
70
|
+
* there's no need to worry about word boundaries.
|
71
|
+
*
|
72
|
+
* These expressions for the prefix and suffix are designed as follows:
|
73
|
+
* ^ handles any terms at the beginning of a comment.
|
74
|
+
* e.g. terms ["TODO"] matches `//TODO something`
|
75
|
+
* $ handles any terms at the end of a comment
|
76
|
+
* e.g. terms ["TODO"] matches `// something TODO`
|
77
|
+
* \s* handles optional leading spaces (for "start" location only)
|
78
|
+
* e.g. terms ["TODO"] matches `// TODO something`
|
79
|
+
* \b handles terms preceded/followed by word boundary
|
80
|
+
* e.g. terms: ["!FIX", "FIX!"] matches `// FIX!something` or `// something!FIX`
|
81
|
+
* terms: ["FIX"] matches `// FIX!` or `// !FIX`, but not `// fixed or affix`
|
80
82
|
*/
|
81
|
-
const
|
83
|
+
const wordBoundary = "\\b";
|
82
84
|
|
83
|
-
|
85
|
+
let prefix = "";
|
84
86
|
|
85
|
-
|
86
|
-
* When matching at the start, ignore leading whitespace, and
|
87
|
-
* there's no need to worry about word boundaries.
|
88
|
-
*/
|
87
|
+
if (location === "start") {
|
89
88
|
prefix = "^\\s*";
|
90
89
|
} else if (/^\w/u.test(term)) {
|
91
90
|
prefix = wordBoundary;
|
92
|
-
} else {
|
93
|
-
prefix = "";
|
94
91
|
}
|
95
92
|
|
96
|
-
|
97
|
-
|
98
|
-
/*
|
99
|
-
* For location "start" the regex should be
|
100
|
-
* ^\s*TERM\b. This checks the word boundary
|
101
|
-
* at the beginning of the comment.
|
102
|
-
*/
|
103
|
-
return new RegExp(prefix + escaped + suffix, "iu");
|
104
|
-
}
|
93
|
+
const suffix = /\w$/u.test(term) ? wordBoundary : "";
|
94
|
+
const flags = "iu"; // Case-insensitive with Unicode case folding.
|
105
95
|
|
106
96
|
/*
|
107
|
-
* For location "
|
108
|
-
*
|
109
|
-
*
|
97
|
+
* For location "start", the typical regex is:
|
98
|
+
* /^\s*ESCAPED_TERM\b/iu.
|
99
|
+
*
|
100
|
+
* For location "anywhere" the typical regex is
|
101
|
+
* /\bESCAPED_TERM\b/iu
|
102
|
+
*
|
103
|
+
* If it starts or ends with non-word character, the prefix and suffix empty, respectively.
|
110
104
|
*/
|
111
|
-
return new RegExp(
|
112
|
-
prefix +
|
113
|
-
escaped +
|
114
|
-
suffix +
|
115
|
-
eitherOrWordBoundary +
|
116
|
-
term +
|
117
|
-
wordBoundary,
|
118
|
-
"iu"
|
119
|
-
);
|
105
|
+
return new RegExp(`${prefix}${escaped}${suffix}`, flags);
|
120
106
|
}
|
121
107
|
|
122
108
|
const warningRegExps = warningTerms.map(convertToRegExp);
|
@@ -78,6 +78,9 @@ module.exports = {
|
|
78
78
|
ignoreConstructors: {
|
79
79
|
type: "boolean"
|
80
80
|
},
|
81
|
+
methodsIgnorePattern: {
|
82
|
+
type: "string"
|
83
|
+
},
|
81
84
|
avoidQuotes: {
|
82
85
|
type: "boolean"
|
83
86
|
},
|
@@ -115,6 +118,9 @@ module.exports = {
|
|
115
118
|
|
116
119
|
const PARAMS = context.options[1] || {};
|
117
120
|
const IGNORE_CONSTRUCTORS = PARAMS.ignoreConstructors;
|
121
|
+
const METHODS_IGNORE_PATTERN = PARAMS.methodsIgnorePattern
|
122
|
+
? new RegExp(PARAMS.methodsIgnorePattern, "u")
|
123
|
+
: null;
|
118
124
|
const AVOID_QUOTES = PARAMS.avoidQuotes;
|
119
125
|
const AVOID_EXPLICIT_RETURN_ARROWS = !!PARAMS.avoidExplicitReturnArrows;
|
120
126
|
const sourceCode = context.getSourceCode();
|
@@ -457,6 +463,15 @@ module.exports = {
|
|
457
463
|
if (IGNORE_CONSTRUCTORS && node.key.type === "Identifier" && isConstructor(node.key.name)) {
|
458
464
|
return;
|
459
465
|
}
|
466
|
+
|
467
|
+
if (METHODS_IGNORE_PATTERN) {
|
468
|
+
const propertyName = astUtils.getStaticPropertyName(node);
|
469
|
+
|
470
|
+
if (propertyName !== null && METHODS_IGNORE_PATTERN.test(propertyName)) {
|
471
|
+
return;
|
472
|
+
}
|
473
|
+
}
|
474
|
+
|
460
475
|
if (AVOID_QUOTES && isStringLiteral(node.key)) {
|
461
476
|
return;
|
462
477
|
}
|
package/lib/rules/sort-keys.js
CHANGED
@@ -105,6 +105,10 @@ module.exports = {
|
|
105
105
|
type: "integer",
|
106
106
|
minimum: 2,
|
107
107
|
default: 2
|
108
|
+
},
|
109
|
+
allowLineSeparatedGroups: {
|
110
|
+
type: "boolean",
|
111
|
+
default: false
|
108
112
|
}
|
109
113
|
},
|
110
114
|
additionalProperties: false
|
@@ -124,17 +128,21 @@ module.exports = {
|
|
124
128
|
const insensitive = options && options.caseSensitive === false;
|
125
129
|
const natural = options && options.natural;
|
126
130
|
const minKeys = options && options.minKeys;
|
131
|
+
const allowLineSeparatedGroups = options && options.allowLineSeparatedGroups || false;
|
127
132
|
const isValidOrder = isValidOrders[
|
128
133
|
order + (insensitive ? "I" : "") + (natural ? "N" : "")
|
129
134
|
];
|
130
135
|
|
131
136
|
// The stack to save the previous property's name for each object literals.
|
132
137
|
let stack = null;
|
138
|
+
const sourceCode = context.getSourceCode();
|
133
139
|
|
134
140
|
return {
|
135
141
|
ObjectExpression(node) {
|
136
142
|
stack = {
|
137
143
|
upper: stack,
|
144
|
+
prevNode: null,
|
145
|
+
prevBlankLine: false,
|
138
146
|
prevName: null,
|
139
147
|
numKeys: node.properties.length
|
140
148
|
};
|
@@ -159,10 +167,45 @@ module.exports = {
|
|
159
167
|
const numKeys = stack.numKeys;
|
160
168
|
const thisName = getPropertyName(node);
|
161
169
|
|
170
|
+
// Get tokens between current node and previous node
|
171
|
+
const tokens = stack.prevNode && sourceCode
|
172
|
+
.getTokensBetween(stack.prevNode, node, { includeComments: true });
|
173
|
+
|
174
|
+
let isBlankLineBetweenNodes = stack.prevBlankLine;
|
175
|
+
|
176
|
+
if (tokens) {
|
177
|
+
|
178
|
+
// check blank line between tokens
|
179
|
+
tokens.forEach((token, index) => {
|
180
|
+
const previousToken = tokens[index - 1];
|
181
|
+
|
182
|
+
if (previousToken && (token.loc.start.line - previousToken.loc.end.line > 1)) {
|
183
|
+
isBlankLineBetweenNodes = true;
|
184
|
+
}
|
185
|
+
});
|
186
|
+
|
187
|
+
// check blank line between the current node and the last token
|
188
|
+
if (!isBlankLineBetweenNodes && (node.loc.start.line - tokens[tokens.length - 1].loc.end.line > 1)) {
|
189
|
+
isBlankLineBetweenNodes = true;
|
190
|
+
}
|
191
|
+
|
192
|
+
// check blank line between the first token and the previous node
|
193
|
+
if (!isBlankLineBetweenNodes && (tokens[0].loc.start.line - stack.prevNode.loc.end.line > 1)) {
|
194
|
+
isBlankLineBetweenNodes = true;
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
stack.prevNode = node;
|
199
|
+
|
162
200
|
if (thisName !== null) {
|
163
201
|
stack.prevName = thisName;
|
164
202
|
}
|
165
203
|
|
204
|
+
if (allowLineSeparatedGroups && isBlankLineBetweenNodes) {
|
205
|
+
stack.prevBlankLine = thisName === null;
|
206
|
+
return;
|
207
|
+
}
|
208
|
+
|
166
209
|
if (prevName === null || thisName === null || numKeys < minKeys) {
|
167
210
|
return;
|
168
211
|
}
|
package/lib/unsupported-api.js
CHANGED
@@ -12,6 +12,8 @@
|
|
12
12
|
//-----------------------------------------------------------------------------
|
13
13
|
|
14
14
|
const { FileEnumerator } = require("./cli-engine/file-enumerator");
|
15
|
+
const { FlatESLint } = require("./eslint/flat-eslint");
|
16
|
+
const FlatRuleTester = require("./rule-tester/flat-rule-tester");
|
15
17
|
|
16
18
|
//-----------------------------------------------------------------------------
|
17
19
|
// Exports
|
@@ -19,5 +21,7 @@ const { FileEnumerator } = require("./cli-engine/file-enumerator");
|
|
19
21
|
|
20
22
|
module.exports = {
|
21
23
|
builtinRules: require("./rules"),
|
24
|
+
FlatESLint,
|
25
|
+
FlatRuleTester,
|
22
26
|
FileEnumerator
|
23
27
|
};
|