eslint 8.31.0 → 8.33.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 +14 -14
- package/conf/rule-type-list.json +2 -2
- package/lib/cli-engine/formatters/formatters-meta.json +2 -2
- package/lib/linter/linter.js +4 -1
- package/lib/rule-tester/flat-rule-tester.js +1 -1
- package/lib/rule-tester/rule-tester.js +3 -3
- package/lib/rules/no-fallthrough.js +18 -2
- package/lib/rules/no-restricted-exports.js +106 -10
- package/lib/rules/prefer-regex-literals.js +144 -29
- package/lib/shared/directives.js +15 -0
- package/messages/print-config-with-directory-path.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
@@ -10,10 +10,10 @@
|
|
10
10
|
# ESLint
|
11
11
|
|
12
12
|
[Website](https://eslint.org) |
|
13
|
-
[
|
13
|
+
[Configure ESLint](https://eslint.org/docs/latest/use/configure) |
|
14
14
|
[Rules](https://eslint.org/docs/rules/) |
|
15
|
-
[
|
16
|
-
[
|
15
|
+
[Contribute to ESLint](https://eslint.org/docs/latest/contribute) |
|
16
|
+
[Report Bugs](https://eslint.org/docs/latest/contribute/report-bugs) |
|
17
17
|
[Code of Conduct](https://eslint.org/conduct) |
|
18
18
|
[Twitter](https://twitter.com/geteslint) |
|
19
19
|
[Mailing List](https://groups.google.com/group/eslint) |
|
@@ -59,7 +59,7 @@ After that, you can run ESLint on any file or directory like this:
|
|
59
59
|
|
60
60
|
## Configuration
|
61
61
|
|
62
|
-
After running `npm init @eslint/config`, you'll have
|
62
|
+
After running `npm init @eslint/config`, you'll have an `.eslintrc` file in your directory. In it, you'll see some rules configured like this:
|
63
63
|
|
64
64
|
```json
|
65
65
|
{
|
@@ -76,7 +76,7 @@ The names `"semi"` and `"quotes"` are the names of [rules](https://eslint.org/do
|
|
76
76
|
* `"warn"` or `1` - turn the rule on as a warning (doesn't affect exit code)
|
77
77
|
* `"error"` or `2` - turn the rule on as an error (exit code will be 1)
|
78
78
|
|
79
|
-
The three error levels allow you fine-grained control over how ESLint applies rules (for more configuration options and details, see the [configuration docs](https://eslint.org/docs/
|
79
|
+
The three error levels allow you fine-grained control over how ESLint applies rules (for more configuration options and details, see the [configuration docs](https://eslint.org/docs/latest/use/configure)).
|
80
80
|
|
81
81
|
## Code of Conduct
|
82
82
|
|
@@ -86,10 +86,10 @@ ESLint adheres to the [JS Foundation Code of Conduct](https://eslint.org/conduct
|
|
86
86
|
|
87
87
|
Before filing an issue, please be sure to read the guidelines for what you're reporting:
|
88
88
|
|
89
|
-
* [Bug Report](https://eslint.org/docs/
|
90
|
-
* [Propose a New Rule](https://eslint.org/docs/
|
91
|
-
* [Proposing a Rule Change](https://eslint.org/docs/
|
92
|
-
* [Request a Change](https://eslint.org/docs/
|
89
|
+
* [Bug Report](https://eslint.org/docs/latest/contribute/report-bugs)
|
90
|
+
* [Propose a New Rule](https://eslint.org/docs/latest/contribute/propose-new-rule)
|
91
|
+
* [Proposing a Rule Change](https://eslint.org/docs/latest/contribute/propose-rule-change)
|
92
|
+
* [Request a Change](https://eslint.org/docs/latest/contribute/request-change)
|
93
93
|
|
94
94
|
## Frequently Asked Questions
|
95
95
|
|
@@ -97,7 +97,7 @@ Before filing an issue, please be sure to read the guidelines for what you're re
|
|
97
97
|
|
98
98
|
Yes. [JSCS has reached end of life](https://eslint.org/blog/2016/07/jscs-end-of-life) and is no longer supported.
|
99
99
|
|
100
|
-
We have prepared a [migration guide](https://eslint.org/docs/
|
100
|
+
We have prepared a [migration guide](https://eslint.org/docs/latest/use/migrating-from-jscs) to help you convert your JSCS settings to an ESLint configuration.
|
101
101
|
|
102
102
|
We are now at or near 100% compatibility with JSCS. If you try ESLint and believe we are not yet compatible with a JSCS rule/configuration, please create an issue (mentioning that it is a JSCS compatibility issue) and we will evaluate it as per our normal process.
|
103
103
|
|
@@ -113,11 +113,11 @@ No, ESLint does both traditional linting (looking for problematic patterns) and
|
|
113
113
|
|
114
114
|
### Does ESLint support JSX?
|
115
115
|
|
116
|
-
Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [configuration](https://eslint.org/docs/
|
116
|
+
Yes, ESLint natively supports parsing JSX syntax (this must be enabled in [configuration](https://eslint.org/docs/latest/use/configure)). Please note that supporting JSX syntax *is not* the same as supporting React. React applies specific semantics to JSX syntax that ESLint doesn't recognize. We recommend using [eslint-plugin-react](https://www.npmjs.com/package/eslint-plugin-react) if you are using React and want React semantics.
|
117
117
|
|
118
118
|
### What ECMAScript versions does ESLint support?
|
119
119
|
|
120
|
-
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, 2020, 2021 and 2022. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/
|
120
|
+
ESLint has full support for ECMAScript 3, 5 (default), 2015, 2016, 2017, 2018, 2019, 2020, 2021 and 2022. You can set your desired ECMAScript syntax (and other settings, like global variables or your target environments) through [configuration](https://eslint.org/docs/latest/use/configure).
|
121
121
|
|
122
122
|
### What about experimental features?
|
123
123
|
|
@@ -125,7 +125,7 @@ ESLint's parser only officially supports the latest final ECMAScript standard. W
|
|
125
125
|
|
126
126
|
In other cases (including if rules need to warn on more or fewer cases due to new syntax, rather than just not crashing), we recommend you use other parsers and/or rule plugins. If you are using Babel, you can use [@babel/eslint-parser](https://www.npmjs.com/package/@babel/eslint-parser) and [@babel/eslint-plugin](https://www.npmjs.com/package/@babel/eslint-plugin) to use any option available in Babel.
|
127
127
|
|
128
|
-
Once a language feature has been adopted into the ECMAScript standard (stage 4 according to the [TC39 process](https://tc39.github.io/process-document/)), we will accept issues and pull requests related to the new feature, subject to our [contributing guidelines](https://eslint.org/docs/
|
128
|
+
Once a language feature has been adopted into the ECMAScript standard (stage 4 according to the [TC39 process](https://tc39.github.io/process-document/)), we will accept issues and pull requests related to the new feature, subject to our [contributing guidelines](https://eslint.org/docs/latest/contribute). Until then, please use the appropriate parser and plugin(s) for your experimental feature.
|
129
129
|
|
130
130
|
### Where to ask for help?
|
131
131
|
|
@@ -294,7 +294,7 @@ The following companies, organizations, and individuals support ESLint's ongoing
|
|
294
294
|
<p><a href="#"><img src="https://images.opencollective.com/2021-frameworks-fund/logo.png" alt="Chrome Frameworks Fund" height="undefined"></a> <a href="https://automattic.com"><img src="https://images.opencollective.com/automattic/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3>
|
295
295
|
<p><a href="https://ridicorp.com/career/"><img src="https://images.opencollective.com/ridi-corporation/175dcf3/logo.png" alt="RIDI" height="96"></a> <a href="https://engineering.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a></p><h3>Silver Sponsors</h3>
|
296
296
|
<p><a href="https://sentry.io"><img src="https://avatars.githubusercontent.com/u/1396951?v=4" alt="Sentry" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3>
|
297
|
-
<p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
|
297
|
+
<p><a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discord.com"><img src="https://images.opencollective.com/discordapp/f9645d9/logo.png" alt="Discord" height="32"></a> <a href="https://transloadit.com/"><img src="https://avatars.githubusercontent.com/u/125754?v=4" alt="Transloadit" height="32"></a> <a href="https://www.ignitionapp.com"><img src="https://avatars.githubusercontent.com/u/5753491?v=4" alt="Ignition" height="32"></a> <a href="https://herocoders.com"><img src="https://avatars.githubusercontent.com/u/37549774?v=4" alt="HeroCoders" height="32"></a> <a href="https://quickbookstoolhub.com"><img src="https://avatars.githubusercontent.com/u/95090305?u=e5bc398ef775c9ed19f955c675cdc1fb6abf01df&v=4" alt="QuickBooks Tool hub" height="32"></a></p>
|
298
298
|
<!--sponsorsend-->
|
299
299
|
|
300
300
|
## Technology Sponsors
|
package/conf/rule-type-list.json
CHANGED
@@ -6,12 +6,12 @@
|
|
6
6
|
],
|
7
7
|
"deprecated": {
|
8
8
|
"name": "Deprecated",
|
9
|
-
"description": "These rules have been deprecated in accordance with the <a href=\"/docs/
|
9
|
+
"description": "These rules have been deprecated in accordance with the <a href=\"/docs/use/rule-deprecation\">deprecation policy</a>, and replaced by newer rules:",
|
10
10
|
"rules": []
|
11
11
|
},
|
12
12
|
"removed": {
|
13
13
|
"name": "Removed",
|
14
|
-
"description": "These rules from older versions of ESLint (before the <a href=\"/docs/
|
14
|
+
"description": "These rules from older versions of ESLint (before the <a href=\"/docs/use/rule-deprecation\">deprecation policy</a> existed) have been replaced by newer rules:",
|
15
15
|
"rules": [
|
16
16
|
{ "removed": "generator-star", "replacedBy": ["generator-star-spacing"] },
|
17
17
|
{ "removed": "global-strict", "replacedBy": ["strict"] },
|
@@ -17,11 +17,11 @@
|
|
17
17
|
},
|
18
18
|
{
|
19
19
|
"name": "json-with-metadata",
|
20
|
-
"description": "Outputs JSON-serialized results. The `json-with-metadata` provides the same linting results as the [`json`](#json) formatter with additional metadata about the rules applied. The linting results are included in the `results` property and the rules metadata is included in the `metadata` property.\n\nAlternatively, you can use the [ESLint Node.js API](../../
|
20
|
+
"description": "Outputs JSON-serialized results. The `json-with-metadata` provides the same linting results as the [`json`](#json) formatter with additional metadata about the rules applied. The linting results are included in the `results` property and the rules metadata is included in the `metadata` property.\n\nAlternatively, you can use the [ESLint Node.js API](../../integrate/nodejs-api) to programmatically use ESLint."
|
21
21
|
},
|
22
22
|
{
|
23
23
|
"name": "json",
|
24
|
-
"description": "Outputs JSON-serialized results. The `json` formatter is useful when you want to programmatically work with the CLI's linting results.\n\nAlternatively, you can use the [ESLint Node.js API](../../
|
24
|
+
"description": "Outputs JSON-serialized results. The `json` formatter is useful when you want to programmatically work with the CLI's linting results.\n\nAlternatively, you can use the [ESLint Node.js API](../../integrate/nodejs-api) to programmatically use ESLint."
|
25
25
|
},
|
26
26
|
{
|
27
27
|
"name": "junit",
|
package/lib/linter/linter.js
CHANGED
@@ -18,6 +18,9 @@ const
|
|
18
18
|
merge = require("lodash.merge"),
|
19
19
|
pkg = require("../../package.json"),
|
20
20
|
astUtils = require("../shared/ast-utils"),
|
21
|
+
{
|
22
|
+
directivesPattern
|
23
|
+
} = require("../shared/directives"),
|
21
24
|
{
|
22
25
|
Legacy: {
|
23
26
|
ConfigOps,
|
@@ -377,7 +380,7 @@ function getDirectiveComments(ast, ruleMapper, warnInlineConfig) {
|
|
377
380
|
ast.comments.filter(token => token.type !== "Shebang").forEach(comment => {
|
378
381
|
const { directivePart, justificationPart } = extractDirectiveComment(comment.value);
|
379
382
|
|
380
|
-
const match =
|
383
|
+
const match = directivesPattern.exec(directivePart);
|
381
384
|
|
382
385
|
if (!match) {
|
383
386
|
return;
|
@@ -430,7 +430,7 @@ class FlatRuleTester {
|
|
430
430
|
if (typeof this[DESCRIBE] === "function" || typeof this[IT] === "function") {
|
431
431
|
throw new Error(
|
432
432
|
"Set `RuleTester.itOnly` to use `only` with a custom test framework.\n" +
|
433
|
-
"See https://eslint.org/docs/
|
433
|
+
"See https://eslint.org/docs/latest/integrate/nodejs-api#customizing-ruletester for more."
|
434
434
|
);
|
435
435
|
}
|
436
436
|
if (typeof it === "function") {
|
@@ -314,7 +314,7 @@ function emitLegacyRuleAPIWarning(ruleName) {
|
|
314
314
|
if (!emitLegacyRuleAPIWarning[`warned-${ruleName}`]) {
|
315
315
|
emitLegacyRuleAPIWarning[`warned-${ruleName}`] = true;
|
316
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/
|
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/latest/extend/custom-rules`,
|
318
318
|
"DeprecationWarning"
|
319
319
|
);
|
320
320
|
}
|
@@ -329,7 +329,7 @@ function emitMissingSchemaWarning(ruleName) {
|
|
329
329
|
if (!emitMissingSchemaWarning[`warned-${ruleName}`]) {
|
330
330
|
emitMissingSchemaWarning[`warned-${ruleName}`] = true;
|
331
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/
|
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/latest/extend/custom-rules#options-schemas`,
|
333
333
|
"DeprecationWarning"
|
334
334
|
);
|
335
335
|
}
|
@@ -493,7 +493,7 @@ class RuleTester {
|
|
493
493
|
if (typeof this[DESCRIBE] === "function" || typeof this[IT] === "function") {
|
494
494
|
throw new Error(
|
495
495
|
"Set `RuleTester.itOnly` to use `only` with a custom test framework.\n" +
|
496
|
-
"See https://eslint.org/docs/
|
496
|
+
"See https://eslint.org/docs/latest/integrate/nodejs-api#customizing-ruletester for more."
|
497
497
|
);
|
498
498
|
}
|
499
499
|
if (typeof it === "function") {
|
@@ -4,12 +4,28 @@
|
|
4
4
|
*/
|
5
5
|
"use strict";
|
6
6
|
|
7
|
+
//------------------------------------------------------------------------------
|
8
|
+
// Requirements
|
9
|
+
//------------------------------------------------------------------------------
|
10
|
+
|
11
|
+
const { directivesPattern } = require("../shared/directives");
|
12
|
+
|
7
13
|
//------------------------------------------------------------------------------
|
8
14
|
// Helpers
|
9
15
|
//------------------------------------------------------------------------------
|
10
16
|
|
11
17
|
const DEFAULT_FALLTHROUGH_COMMENT = /falls?\s?through/iu;
|
12
18
|
|
19
|
+
/**
|
20
|
+
* Checks whether or not a given comment string is really a fallthrough comment and not an ESLint directive.
|
21
|
+
* @param {string} comment The comment string to check.
|
22
|
+
* @param {RegExp} fallthroughCommentPattern The regular expression used for checking for fallthrough comments.
|
23
|
+
* @returns {boolean} `true` if the comment string is truly a fallthrough comment.
|
24
|
+
*/
|
25
|
+
function isFallThroughComment(comment, fallthroughCommentPattern) {
|
26
|
+
return fallthroughCommentPattern.test(comment) && !directivesPattern.test(comment.trim());
|
27
|
+
}
|
28
|
+
|
13
29
|
/**
|
14
30
|
* Checks whether or not a given case has a fallthrough comment.
|
15
31
|
* @param {ASTNode} caseWhichFallsThrough SwitchCase node which falls through.
|
@@ -25,14 +41,14 @@ function hasFallthroughComment(caseWhichFallsThrough, subsequentCase, context, f
|
|
25
41
|
const trailingCloseBrace = sourceCode.getLastToken(caseWhichFallsThrough.consequent[0]);
|
26
42
|
const commentInBlock = sourceCode.getCommentsBefore(trailingCloseBrace).pop();
|
27
43
|
|
28
|
-
if (commentInBlock &&
|
44
|
+
if (commentInBlock && isFallThroughComment(commentInBlock.value, fallthroughCommentPattern)) {
|
29
45
|
return true;
|
30
46
|
}
|
31
47
|
}
|
32
48
|
|
33
49
|
const comment = sourceCode.getCommentsBefore(subsequentCase).pop();
|
34
50
|
|
35
|
-
return Boolean(comment &&
|
51
|
+
return Boolean(comment && isFallThroughComment(comment.value, fallthroughCommentPattern));
|
36
52
|
}
|
37
53
|
|
38
54
|
/**
|
@@ -27,27 +27,78 @@ module.exports = {
|
|
27
27
|
},
|
28
28
|
|
29
29
|
schema: [{
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
anyOf: [
|
31
|
+
{
|
32
|
+
type: "object",
|
33
|
+
properties: {
|
34
|
+
restrictedNamedExports: {
|
35
|
+
type: "array",
|
36
|
+
items: {
|
37
|
+
type: "string"
|
38
|
+
},
|
39
|
+
uniqueItems: true
|
40
|
+
}
|
36
41
|
},
|
37
|
-
|
42
|
+
additionalProperties: false
|
43
|
+
},
|
44
|
+
{
|
45
|
+
type: "object",
|
46
|
+
properties: {
|
47
|
+
restrictedNamedExports: {
|
48
|
+
type: "array",
|
49
|
+
items: {
|
50
|
+
type: "string",
|
51
|
+
pattern: "^(?!default$)"
|
52
|
+
},
|
53
|
+
uniqueItems: true
|
54
|
+
},
|
55
|
+
restrictDefaultExports: {
|
56
|
+
type: "object",
|
57
|
+
properties: {
|
58
|
+
|
59
|
+
// Allow/Disallow `export default foo; export default 42; export default function foo() {}` format
|
60
|
+
direct: {
|
61
|
+
type: "boolean"
|
62
|
+
},
|
63
|
+
|
64
|
+
// Allow/Disallow `export { foo as default };` declarations
|
65
|
+
named: {
|
66
|
+
type: "boolean"
|
67
|
+
},
|
68
|
+
|
69
|
+
// Allow/Disallow `export { default } from "mod"; export { default as default } from "mod";` declarations
|
70
|
+
defaultFrom: {
|
71
|
+
type: "boolean"
|
72
|
+
},
|
73
|
+
|
74
|
+
// Allow/Disallow `export { foo as default } from "mod";` declarations
|
75
|
+
namedFrom: {
|
76
|
+
type: "boolean"
|
77
|
+
},
|
78
|
+
|
79
|
+
// Allow/Disallow `export * as default from "mod"`; declarations
|
80
|
+
namespaceFrom: {
|
81
|
+
type: "boolean"
|
82
|
+
}
|
83
|
+
},
|
84
|
+
additionalProperties: false
|
85
|
+
}
|
86
|
+
},
|
87
|
+
additionalProperties: false
|
38
88
|
}
|
39
|
-
|
40
|
-
additionalProperties: false
|
89
|
+
]
|
41
90
|
}],
|
42
91
|
|
43
92
|
messages: {
|
44
|
-
restrictedNamed: "'{{name}}' is restricted from being used as an exported name."
|
93
|
+
restrictedNamed: "'{{name}}' is restricted from being used as an exported name.",
|
94
|
+
restrictedDefault: "Exporting 'default' is restricted."
|
45
95
|
}
|
46
96
|
},
|
47
97
|
|
48
98
|
create(context) {
|
49
99
|
|
50
100
|
const restrictedNames = new Set(context.options[0] && context.options[0].restrictedNamedExports);
|
101
|
+
const restrictDefaultExports = context.options[0] && context.options[0].restrictDefaultExports;
|
51
102
|
|
52
103
|
/**
|
53
104
|
* Checks and reports given exported name.
|
@@ -63,6 +114,42 @@ module.exports = {
|
|
63
114
|
messageId: "restrictedNamed",
|
64
115
|
data: { name }
|
65
116
|
});
|
117
|
+
return;
|
118
|
+
}
|
119
|
+
|
120
|
+
if (name === "default") {
|
121
|
+
if (node.parent.type === "ExportAllDeclaration") {
|
122
|
+
if (restrictDefaultExports && restrictDefaultExports.namespaceFrom) {
|
123
|
+
context.report({
|
124
|
+
node,
|
125
|
+
messageId: "restrictedDefault"
|
126
|
+
});
|
127
|
+
}
|
128
|
+
|
129
|
+
} else { // ExportSpecifier
|
130
|
+
const isSourceSpecified = !!node.parent.parent.source;
|
131
|
+
const specifierLocalName = astUtils.getModuleExportName(node.parent.local);
|
132
|
+
|
133
|
+
if (!isSourceSpecified && restrictDefaultExports && restrictDefaultExports.named) {
|
134
|
+
context.report({
|
135
|
+
node,
|
136
|
+
messageId: "restrictedDefault"
|
137
|
+
});
|
138
|
+
return;
|
139
|
+
}
|
140
|
+
|
141
|
+
if (isSourceSpecified && restrictDefaultExports) {
|
142
|
+
if (
|
143
|
+
(specifierLocalName === "default" && restrictDefaultExports.defaultFrom) ||
|
144
|
+
(specifierLocalName !== "default" && restrictDefaultExports.namedFrom)
|
145
|
+
) {
|
146
|
+
context.report({
|
147
|
+
node,
|
148
|
+
messageId: "restrictedDefault"
|
149
|
+
});
|
150
|
+
}
|
151
|
+
}
|
152
|
+
}
|
66
153
|
}
|
67
154
|
}
|
68
155
|
|
@@ -73,6 +160,15 @@ module.exports = {
|
|
73
160
|
}
|
74
161
|
},
|
75
162
|
|
163
|
+
ExportDefaultDeclaration(node) {
|
164
|
+
if (restrictDefaultExports && restrictDefaultExports.direct) {
|
165
|
+
context.report({
|
166
|
+
node,
|
167
|
+
messageId: "restrictedDefault"
|
168
|
+
});
|
169
|
+
}
|
170
|
+
},
|
171
|
+
|
76
172
|
ExportNamedDeclaration(node) {
|
77
173
|
const declaration = node.declaration;
|
78
174
|
|
@@ -146,6 +146,8 @@ module.exports = {
|
|
146
146
|
messages: {
|
147
147
|
unexpectedRegExp: "Use a regular expression literal instead of the 'RegExp' constructor.",
|
148
148
|
replaceWithLiteral: "Replace with an equivalent regular expression literal.",
|
149
|
+
replaceWithLiteralAndFlags: "Replace with an equivalent regular expression literal with flags '{{ flags }}'.",
|
150
|
+
replaceWithIntendedLiteralAndFlags: "Replace with a regular expression literal with flags '{{ flags }}'.",
|
149
151
|
unexpectedRedundantRegExp: "Regular expression literal is unnecessarily wrapped within a 'RegExp' constructor.",
|
150
152
|
unexpectedRedundantRegExpWithFlags: "Use regular expression literal with flags instead of the 'RegExp' constructor."
|
151
153
|
}
|
@@ -258,6 +260,8 @@ module.exports = {
|
|
258
260
|
return Math.min(ecmaVersion, REGEXPP_LATEST_ECMA_VERSION);
|
259
261
|
}
|
260
262
|
|
263
|
+
const regexppEcmaVersion = getRegexppEcmaVersion(context.languageOptions.ecmaVersion);
|
264
|
+
|
261
265
|
/**
|
262
266
|
* Makes a character escaped or else returns null.
|
263
267
|
* @param {string} character The character to escape.
|
@@ -293,6 +297,83 @@ module.exports = {
|
|
293
297
|
}
|
294
298
|
}
|
295
299
|
|
300
|
+
/**
|
301
|
+
* Checks whether the given regex and flags are valid for the ecma version or not.
|
302
|
+
* @param {string} pattern The regex pattern to check.
|
303
|
+
* @param {string | undefined} flags The regex flags to check.
|
304
|
+
* @returns {boolean} True if the given regex pattern and flags are valid for the ecma version.
|
305
|
+
*/
|
306
|
+
function isValidRegexForEcmaVersion(pattern, flags) {
|
307
|
+
const validator = new RegExpValidator({ ecmaVersion: regexppEcmaVersion });
|
308
|
+
|
309
|
+
try {
|
310
|
+
validator.validatePattern(pattern, 0, pattern.length, flags ? flags.includes("u") : false);
|
311
|
+
if (flags) {
|
312
|
+
validator.validateFlags(flags);
|
313
|
+
}
|
314
|
+
return true;
|
315
|
+
} catch {
|
316
|
+
return false;
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
320
|
+
/**
|
321
|
+
* Checks whether two given regex flags contain the same flags or not.
|
322
|
+
* @param {string} flagsA The regex flags.
|
323
|
+
* @param {string} flagsB The regex flags.
|
324
|
+
* @returns {boolean} True if two regex flags contain same flags.
|
325
|
+
*/
|
326
|
+
function areFlagsEqual(flagsA, flagsB) {
|
327
|
+
return [...flagsA].sort().join("") === [...flagsB].sort().join("");
|
328
|
+
}
|
329
|
+
|
330
|
+
|
331
|
+
/**
|
332
|
+
* Merges two regex flags.
|
333
|
+
* @param {string} flagsA The regex flags.
|
334
|
+
* @param {string} flagsB The regex flags.
|
335
|
+
* @returns {string} The merged regex flags.
|
336
|
+
*/
|
337
|
+
function mergeRegexFlags(flagsA, flagsB) {
|
338
|
+
const flagsSet = new Set([
|
339
|
+
...flagsA,
|
340
|
+
...flagsB
|
341
|
+
]);
|
342
|
+
|
343
|
+
return [...flagsSet].join("");
|
344
|
+
}
|
345
|
+
|
346
|
+
/**
|
347
|
+
* Checks whether a give node can be fixed to the given regex pattern and flags.
|
348
|
+
* @param {ASTNode} node The node to check.
|
349
|
+
* @param {string} pattern The regex pattern to check.
|
350
|
+
* @param {string} flags The regex flags
|
351
|
+
* @returns {boolean} True if a node can be fixed to the given regex pattern and flags.
|
352
|
+
*/
|
353
|
+
function canFixTo(node, pattern, flags) {
|
354
|
+
const tokenBefore = sourceCode.getTokenBefore(node);
|
355
|
+
|
356
|
+
return sourceCode.getCommentsInside(node).length === 0 &&
|
357
|
+
(!tokenBefore || validPrecedingTokens.has(tokenBefore.value)) &&
|
358
|
+
isValidRegexForEcmaVersion(pattern, flags);
|
359
|
+
}
|
360
|
+
|
361
|
+
/**
|
362
|
+
* Returns a safe output code considering the before and after tokens.
|
363
|
+
* @param {ASTNode} node The regex node.
|
364
|
+
* @param {string} newRegExpValue The new regex expression value.
|
365
|
+
* @returns {string} The output code.
|
366
|
+
*/
|
367
|
+
function getSafeOutput(node, newRegExpValue) {
|
368
|
+
const tokenBefore = sourceCode.getTokenBefore(node);
|
369
|
+
const tokenAfter = sourceCode.getTokenAfter(node);
|
370
|
+
|
371
|
+
return (tokenBefore && !canTokensBeAdjacent(tokenBefore, newRegExpValue) && tokenBefore.range[1] === node.range[0] ? " " : "") +
|
372
|
+
newRegExpValue +
|
373
|
+
(tokenAfter && !canTokensBeAdjacent(newRegExpValue, tokenAfter) && node.range[1] === tokenAfter.range[0] ? " " : "");
|
374
|
+
|
375
|
+
}
|
376
|
+
|
296
377
|
return {
|
297
378
|
Program() {
|
298
379
|
const scope = context.getScope();
|
@@ -306,10 +387,69 @@ module.exports = {
|
|
306
387
|
|
307
388
|
for (const { node } of tracker.iterateGlobalReferences(traceMap)) {
|
308
389
|
if (disallowRedundantWrapping && isUnnecessarilyWrappedRegexLiteral(node)) {
|
390
|
+
const regexNode = node.arguments[0];
|
391
|
+
|
309
392
|
if (node.arguments.length === 2) {
|
310
|
-
|
393
|
+
const suggests = [];
|
394
|
+
|
395
|
+
const argFlags = getStringValue(node.arguments[1]) || "";
|
396
|
+
|
397
|
+
if (canFixTo(node, regexNode.regex.pattern, argFlags)) {
|
398
|
+
suggests.push({
|
399
|
+
messageId: "replaceWithLiteralAndFlags",
|
400
|
+
pattern: regexNode.regex.pattern,
|
401
|
+
flags: argFlags
|
402
|
+
});
|
403
|
+
}
|
404
|
+
|
405
|
+
const literalFlags = regexNode.regex.flags || "";
|
406
|
+
const mergedFlags = mergeRegexFlags(literalFlags, argFlags);
|
407
|
+
|
408
|
+
if (
|
409
|
+
!areFlagsEqual(mergedFlags, argFlags) &&
|
410
|
+
canFixTo(node, regexNode.regex.pattern, mergedFlags)
|
411
|
+
) {
|
412
|
+
suggests.push({
|
413
|
+
messageId: "replaceWithIntendedLiteralAndFlags",
|
414
|
+
pattern: regexNode.regex.pattern,
|
415
|
+
flags: mergedFlags
|
416
|
+
});
|
417
|
+
}
|
418
|
+
|
419
|
+
context.report({
|
420
|
+
node,
|
421
|
+
messageId: "unexpectedRedundantRegExpWithFlags",
|
422
|
+
suggest: suggests.map(({ flags, pattern, messageId }) => ({
|
423
|
+
messageId,
|
424
|
+
data: {
|
425
|
+
flags
|
426
|
+
},
|
427
|
+
fix(fixer) {
|
428
|
+
return fixer.replaceText(node, getSafeOutput(node, `/${pattern}/${flags}`));
|
429
|
+
}
|
430
|
+
}))
|
431
|
+
});
|
311
432
|
} else {
|
312
|
-
|
433
|
+
const outputs = [];
|
434
|
+
|
435
|
+
if (canFixTo(node, regexNode.regex.pattern, regexNode.regex.flags)) {
|
436
|
+
outputs.push(sourceCode.getText(regexNode));
|
437
|
+
}
|
438
|
+
|
439
|
+
|
440
|
+
context.report({
|
441
|
+
node,
|
442
|
+
messageId: "unexpectedRedundantRegExp",
|
443
|
+
suggest: outputs.map(output => ({
|
444
|
+
messageId: "replaceWithLiteral",
|
445
|
+
fix(fixer) {
|
446
|
+
return fixer.replaceText(
|
447
|
+
node,
|
448
|
+
getSafeOutput(node, output)
|
449
|
+
);
|
450
|
+
}
|
451
|
+
}))
|
452
|
+
});
|
313
453
|
}
|
314
454
|
} else if (hasOnlyStaticStringArguments(node)) {
|
315
455
|
let regexContent = getStringValue(node.arguments[0]);
|
@@ -320,21 +460,7 @@ module.exports = {
|
|
320
460
|
flags = getStringValue(node.arguments[1]);
|
321
461
|
}
|
322
462
|
|
323
|
-
|
324
|
-
const RegExpValidatorInstance = new RegExpValidator({ ecmaVersion: regexppEcmaVersion });
|
325
|
-
|
326
|
-
try {
|
327
|
-
RegExpValidatorInstance.validatePattern(regexContent, 0, regexContent.length, flags ? flags.includes("u") : false);
|
328
|
-
if (flags) {
|
329
|
-
RegExpValidatorInstance.validateFlags(flags);
|
330
|
-
}
|
331
|
-
} catch {
|
332
|
-
noFix = true;
|
333
|
-
}
|
334
|
-
|
335
|
-
const tokenBefore = sourceCode.getTokenBefore(node);
|
336
|
-
|
337
|
-
if (tokenBefore && !validPrecedingTokens.has(tokenBefore.value)) {
|
463
|
+
if (!canFixTo(node, regexContent, flags)) {
|
338
464
|
noFix = true;
|
339
465
|
}
|
340
466
|
|
@@ -342,10 +468,6 @@ module.exports = {
|
|
342
468
|
noFix = true;
|
343
469
|
}
|
344
470
|
|
345
|
-
if (sourceCode.getCommentsInside(node).length > 0) {
|
346
|
-
noFix = true;
|
347
|
-
}
|
348
|
-
|
349
471
|
if (regexContent && !noFix) {
|
350
472
|
let charIncrease = 0;
|
351
473
|
|
@@ -377,14 +499,7 @@ module.exports = {
|
|
377
499
|
suggest: noFix ? [] : [{
|
378
500
|
messageId: "replaceWithLiteral",
|
379
501
|
fix(fixer) {
|
380
|
-
|
381
|
-
|
382
|
-
return fixer.replaceText(
|
383
|
-
node,
|
384
|
-
(tokenBefore && !canTokensBeAdjacent(tokenBefore, newRegExpValue) && tokenBefore.range[1] === node.range[0] ? " " : "") +
|
385
|
-
newRegExpValue +
|
386
|
-
(tokenAfter && !canTokensBeAdjacent(newRegExpValue, tokenAfter) && node.range[1] === tokenAfter.range[0] ? " " : "")
|
387
|
-
);
|
502
|
+
return fixer.replaceText(node, getSafeOutput(node, newRegExpValue));
|
388
503
|
}
|
389
504
|
}]
|
390
505
|
});
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/**
|
2
|
+
* @fileoverview Common utils for directives.
|
3
|
+
*
|
4
|
+
* This file contains only shared items for directives.
|
5
|
+
* If you make a utility for rules, please see `../rules/utils/ast-utils.js`.
|
6
|
+
*
|
7
|
+
* @author gfyoung <https://github.com/gfyoung>
|
8
|
+
*/
|
9
|
+
"use strict";
|
10
|
+
|
11
|
+
const directivesPattern = /^(eslint(?:-env|-enable|-disable(?:(?:-next)?-line)?)?|exported|globals?)(?:\s|$)/u;
|
12
|
+
|
13
|
+
module.exports = {
|
14
|
+
directivesPattern
|
15
|
+
};
|
@@ -3,6 +3,6 @@
|
|
3
3
|
module.exports = function() {
|
4
4
|
return `
|
5
5
|
The '--print-config' CLI option requires a path to a source code file rather than a directory.
|
6
|
-
See also: https://eslint.org/docs/
|
6
|
+
See also: https://eslint.org/docs/latest/use/command-line-interface#--print-config
|
7
7
|
`.trimStart();
|
8
8
|
};
|