@eslint-community/eslint-plugin-eslint-comments 3.2.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/LICENSE +21 -0
- package/README.md +35 -0
- package/index.js +8 -0
- package/lib/configs/recommended.js +13 -0
- package/lib/configs.js +6 -0
- package/lib/internal/disabled-area.js +211 -0
- package/lib/internal/get-linters.js +33 -0
- package/lib/internal/utils.js +152 -0
- package/lib/rules/disable-enable-pair.js +69 -0
- package/lib/rules/no-aggregating-enable.js +47 -0
- package/lib/rules/no-duplicate-disable.js +42 -0
- package/lib/rules/no-restricted-disable.js +62 -0
- package/lib/rules/no-unlimited-disable.js +57 -0
- package/lib/rules/no-unused-disable.js +34 -0
- package/lib/rules/no-unused-enable.js +42 -0
- package/lib/rules/no-use.js +74 -0
- package/lib/rules/require-description.js +78 -0
- package/lib/rules.js +14 -0
- package/lib/utils/patch.js +203 -0
- package/lib/utils.js +6 -0
- package/package.json +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016 Toru Nagashima
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @eslint-community/eslint-plugin-eslint-comments
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@eslint-community/eslint-plugin-eslint-comments)
|
|
4
|
+
[](http://www.npmtrends.com/@eslint-community/eslint-plugin-eslint-comments)
|
|
5
|
+
[](https://github.com/eslint-community/eslint-plugin-eslint-comments/actions)
|
|
6
|
+
[](https://codecov.io/gh/eslint-community/eslint-plugin-eslint-comments)
|
|
7
|
+
|
|
8
|
+
Additional ESLint rules for ESLint directive comments (e.g. `//eslint-disable-line`).
|
|
9
|
+
|
|
10
|
+
## 📖 Usage
|
|
11
|
+
|
|
12
|
+
- [Documentation](https://mysticatea.github.io/eslint-plugin-eslint-comments)
|
|
13
|
+
|
|
14
|
+
## 🚥 Semantic Versioning Policy
|
|
15
|
+
|
|
16
|
+
`@eslint-community/eslint-plugin-eslint-comments` follows [semantic versioning](http://semver.org/) and [ESLint's Semantic Versioning Policy](https://github.com/eslint/eslint#semantic-versioning-policy).
|
|
17
|
+
|
|
18
|
+
## 📰 Changelog
|
|
19
|
+
|
|
20
|
+
- [GitHub Releases](https://github.com/eslint-community/eslint-plugin-eslint-comments/releases)
|
|
21
|
+
|
|
22
|
+
## 🍻 Contributing
|
|
23
|
+
|
|
24
|
+
Welcome contributing!
|
|
25
|
+
|
|
26
|
+
Please use GitHub's Issues/PRs.
|
|
27
|
+
|
|
28
|
+
### Development Tools
|
|
29
|
+
|
|
30
|
+
- `npm test` runs tests and measures coverage.
|
|
31
|
+
- `npm run build` updates `README.md`, `index.js`, and the header of all rule's documents.
|
|
32
|
+
- `npm run clean` removes the coverage of the last `npm test` command.
|
|
33
|
+
- `npm run coverage` shows the coverage of the last `npm test` command.
|
|
34
|
+
- `npm run lint` runs ESLint for this codebase.
|
|
35
|
+
- `npm run watch` runs tests and measures coverage when source code are changed.
|
package/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** DON'T EDIT THIS FILE; was created by scripts. */
|
|
2
|
+
"use strict"
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
plugins: ["@eslint-community/eslint-comments"],
|
|
6
|
+
rules: {
|
|
7
|
+
"@eslint-community/eslint-comments/disable-enable-pair": "error",
|
|
8
|
+
"@eslint-community/eslint-comments/no-aggregating-enable": "error",
|
|
9
|
+
"@eslint-community/eslint-comments/no-duplicate-disable": "error",
|
|
10
|
+
"@eslint-community/eslint-comments/no-unlimited-disable": "error",
|
|
11
|
+
"@eslint-community/eslint-comments/no-unused-enable": "error",
|
|
12
|
+
},
|
|
13
|
+
}
|
package/lib/configs.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const utils = require("./utils")
|
|
8
|
+
const DELIMITER = /[\s,]+/gu
|
|
9
|
+
const pool = new WeakMap()
|
|
10
|
+
|
|
11
|
+
module.exports = class DisabledArea {
|
|
12
|
+
/**
|
|
13
|
+
* Get singleton instance for the given source code.
|
|
14
|
+
*
|
|
15
|
+
* @param {eslint.SourceCode} sourceCode - The source code to get.
|
|
16
|
+
* @returns {DisabledArea} The singleton object for the source code.
|
|
17
|
+
*/
|
|
18
|
+
static get(sourceCode) {
|
|
19
|
+
let retv = pool.get(sourceCode.ast)
|
|
20
|
+
|
|
21
|
+
if (retv == null) {
|
|
22
|
+
retv = new DisabledArea()
|
|
23
|
+
retv._scan(sourceCode)
|
|
24
|
+
pool.set(sourceCode.ast, retv)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return retv
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Constructor.
|
|
32
|
+
*/
|
|
33
|
+
constructor() {
|
|
34
|
+
this.areas = []
|
|
35
|
+
this.duplicateDisableDirectives = []
|
|
36
|
+
this.unusedEnableDirectives = []
|
|
37
|
+
this.numberOfRelatedDisableDirectives = new Map()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Make disabled area.
|
|
42
|
+
*
|
|
43
|
+
* @param {Token} comment - The comment token to disable.
|
|
44
|
+
* @param {object} location - The start location to disable.
|
|
45
|
+
* @param {string[]|null} ruleIds - The ruleId names to disable.
|
|
46
|
+
* @param {string} kind - The kind of disable-comments.
|
|
47
|
+
* @returns {void}
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
_disable(comment, location, ruleIds, kind) {
|
|
51
|
+
if (ruleIds) {
|
|
52
|
+
for (const ruleId of ruleIds) {
|
|
53
|
+
if (this._getArea(ruleId, location) != null) {
|
|
54
|
+
this.duplicateDisableDirectives.push({ comment, ruleId })
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.areas.push({
|
|
58
|
+
comment,
|
|
59
|
+
ruleId,
|
|
60
|
+
kind,
|
|
61
|
+
start: location,
|
|
62
|
+
end: null,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
if (this._getArea(null, location) != null) {
|
|
67
|
+
this.duplicateDisableDirectives.push({ comment, ruleId: null })
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.areas.push({
|
|
71
|
+
comment,
|
|
72
|
+
ruleId: null,
|
|
73
|
+
kind,
|
|
74
|
+
start: location,
|
|
75
|
+
end: null,
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Close disabled area.
|
|
82
|
+
*
|
|
83
|
+
* @param {Token} comment - The comment token to enable.
|
|
84
|
+
* @param {object} location - The start location to enable.
|
|
85
|
+
* @param {string[]|null} ruleIds - The ruleId names to enable.
|
|
86
|
+
* @param {string} kind - The kind of disable-comments.
|
|
87
|
+
* @returns {void}
|
|
88
|
+
* @private
|
|
89
|
+
*/
|
|
90
|
+
_enable(comment, location, ruleIds, kind) {
|
|
91
|
+
const relatedDisableDirectives = new Set()
|
|
92
|
+
|
|
93
|
+
if (ruleIds) {
|
|
94
|
+
for (const ruleId of ruleIds) {
|
|
95
|
+
let used = false
|
|
96
|
+
|
|
97
|
+
for (let i = this.areas.length - 1; i >= 0; --i) {
|
|
98
|
+
const area = this.areas[i]
|
|
99
|
+
|
|
100
|
+
if (
|
|
101
|
+
area.end === null &&
|
|
102
|
+
area.kind === kind &&
|
|
103
|
+
area.ruleId === ruleId
|
|
104
|
+
) {
|
|
105
|
+
relatedDisableDirectives.add(area.comment)
|
|
106
|
+
area.end = location
|
|
107
|
+
used = true
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!used) {
|
|
112
|
+
this.unusedEnableDirectives.push({ comment, ruleId })
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
let used = false
|
|
117
|
+
|
|
118
|
+
for (let i = this.areas.length - 1; i >= 0; --i) {
|
|
119
|
+
const area = this.areas[i]
|
|
120
|
+
|
|
121
|
+
if (area.end === null && area.kind === kind) {
|
|
122
|
+
relatedDisableDirectives.add(area.comment)
|
|
123
|
+
area.end = location
|
|
124
|
+
used = true
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!used) {
|
|
129
|
+
this.unusedEnableDirectives.push({ comment, ruleId: null })
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.numberOfRelatedDisableDirectives.set(
|
|
134
|
+
comment,
|
|
135
|
+
relatedDisableDirectives.size
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Gets the area of the given ruleId and location.
|
|
141
|
+
*
|
|
142
|
+
* @param {string|null} ruleId - The ruleId name to get.
|
|
143
|
+
* @param {object} location - The location to get.
|
|
144
|
+
* @returns {object|null} The area of the given ruleId and location.
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
_getArea(ruleId, location) {
|
|
148
|
+
for (let i = this.areas.length - 1; i >= 0; --i) {
|
|
149
|
+
const area = this.areas[i]
|
|
150
|
+
|
|
151
|
+
if (
|
|
152
|
+
(area.ruleId === null || area.ruleId === ruleId) &&
|
|
153
|
+
utils.lte(area.start, location) &&
|
|
154
|
+
(area.end === null || utils.lte(location, area.end))
|
|
155
|
+
) {
|
|
156
|
+
return area
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Scan the source code and setup disabled area list.
|
|
165
|
+
*
|
|
166
|
+
* @param {eslint.SourceCode} sourceCode - The source code to scan.
|
|
167
|
+
* @returns {void}
|
|
168
|
+
* @private
|
|
169
|
+
*/
|
|
170
|
+
_scan(sourceCode) {
|
|
171
|
+
for (const comment of sourceCode.getAllComments()) {
|
|
172
|
+
const directiveComment = utils.parseDirectiveComment(comment)
|
|
173
|
+
if (directiveComment == null) {
|
|
174
|
+
continue
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const kind = directiveComment.kind
|
|
178
|
+
if (
|
|
179
|
+
kind !== "eslint-disable" &&
|
|
180
|
+
kind !== "eslint-enable" &&
|
|
181
|
+
kind !== "eslint-disable-line" &&
|
|
182
|
+
kind !== "eslint-disable-next-line"
|
|
183
|
+
) {
|
|
184
|
+
continue
|
|
185
|
+
}
|
|
186
|
+
const ruleIds = directiveComment.value
|
|
187
|
+
? directiveComment.value.split(DELIMITER)
|
|
188
|
+
: null
|
|
189
|
+
|
|
190
|
+
if (kind === "eslint-disable") {
|
|
191
|
+
this._disable(comment, comment.loc.start, ruleIds, "block")
|
|
192
|
+
} else if (kind === "eslint-enable") {
|
|
193
|
+
this._enable(comment, comment.loc.start, ruleIds, "block")
|
|
194
|
+
} else if (kind === "eslint-disable-line") {
|
|
195
|
+
const line = comment.loc.start.line
|
|
196
|
+
const start = { line, column: 0 }
|
|
197
|
+
const end = { line: line + 1, column: -1 }
|
|
198
|
+
|
|
199
|
+
this._disable(comment, start, ruleIds, "line")
|
|
200
|
+
this._enable(comment, end, ruleIds, "line")
|
|
201
|
+
} else if (kind === "eslint-disable-next-line") {
|
|
202
|
+
const line = comment.loc.start.line
|
|
203
|
+
const start = { line: line + 1, column: 0 }
|
|
204
|
+
const end = { line: line + 2, column: -1 }
|
|
205
|
+
|
|
206
|
+
this._disable(comment, start, ruleIds, "line")
|
|
207
|
+
this._enable(comment, end, ruleIds, "line")
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const path = require("path")
|
|
8
|
+
const needle = `${path.sep}node_modules${path.sep}eslint${path.sep}`
|
|
9
|
+
|
|
10
|
+
module.exports = () => {
|
|
11
|
+
const eslintPaths = new Set(
|
|
12
|
+
Object.keys(require.cache)
|
|
13
|
+
.filter(id => id.includes(needle))
|
|
14
|
+
.map(id => id.slice(0, id.indexOf(needle) + needle.length))
|
|
15
|
+
)
|
|
16
|
+
const linters = []
|
|
17
|
+
|
|
18
|
+
for (const eslintPath of eslintPaths) {
|
|
19
|
+
try {
|
|
20
|
+
const linter = require(eslintPath).Linter
|
|
21
|
+
|
|
22
|
+
if (linter) {
|
|
23
|
+
linters.push(linter)
|
|
24
|
+
}
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (error.code !== "MODULE_NOT_FOUND") {
|
|
27
|
+
throw error
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return linters
|
|
33
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const escapeStringRegexp = require("escape-string-regexp")
|
|
8
|
+
const LINE_PATTERN = /[^\r\n\u2028\u2029]*(?:\r\n|[\r\n\u2028\u2029]|$)/gu
|
|
9
|
+
|
|
10
|
+
const DIRECTIVE_PATTERN = /^(eslint(?:-env|-enable|-disable(?:(?:-next)?-line)?)?|exported|globals?)(?:\s|$)/u
|
|
11
|
+
const LINE_COMMENT_PATTERN = /^eslint-disable-(next-)?line$/u
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
/**
|
|
15
|
+
* Make the location ignoring `eslint-disable` comments.
|
|
16
|
+
*
|
|
17
|
+
* @param {object} location - The location to convert.
|
|
18
|
+
* @returns {object} Converted location.
|
|
19
|
+
*/
|
|
20
|
+
toForceLocation(location) {
|
|
21
|
+
return {
|
|
22
|
+
start: {
|
|
23
|
+
line: location.start.line,
|
|
24
|
+
column: -1,
|
|
25
|
+
},
|
|
26
|
+
end: location.end,
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Calculate the location of the given rule in the given comment token.
|
|
32
|
+
*
|
|
33
|
+
* @param {Token} comment - The comment token to calculate.
|
|
34
|
+
* @param {string|null} ruleId - The rule name to calculate.
|
|
35
|
+
* @returns {object} The location of the given information.
|
|
36
|
+
*/
|
|
37
|
+
toRuleIdLocation(comment, ruleId) {
|
|
38
|
+
if (ruleId == null) {
|
|
39
|
+
return module.exports.toForceLocation(comment.loc)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const lines = comment.value.match(LINE_PATTERN)
|
|
43
|
+
//eslint-disable-next-line require-unicode-regexp
|
|
44
|
+
const ruleIdPattern = new RegExp(
|
|
45
|
+
`([\\s,]|^)${escapeStringRegexp(ruleId)}(?:[\\s,]|$)`
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
const m = ruleIdPattern.exec(lines[0])
|
|
50
|
+
if (m != null) {
|
|
51
|
+
const start = comment.loc.start
|
|
52
|
+
return {
|
|
53
|
+
start: {
|
|
54
|
+
line: start.line,
|
|
55
|
+
column: 2 + start.column + m.index + m[1].length,
|
|
56
|
+
},
|
|
57
|
+
end: {
|
|
58
|
+
line: start.line,
|
|
59
|
+
column:
|
|
60
|
+
2 +
|
|
61
|
+
start.column +
|
|
62
|
+
m.index +
|
|
63
|
+
m[1].length +
|
|
64
|
+
ruleId.length,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (let i = 1; i < lines.length; ++i) {
|
|
71
|
+
const m = ruleIdPattern.exec(lines[i])
|
|
72
|
+
if (m != null) {
|
|
73
|
+
const start = comment.loc.start
|
|
74
|
+
return {
|
|
75
|
+
start: {
|
|
76
|
+
line: start.line + i,
|
|
77
|
+
column: m.index + m[1].length,
|
|
78
|
+
},
|
|
79
|
+
end: {
|
|
80
|
+
line: start.line + i,
|
|
81
|
+
column: m.index + m[1].length + ruleId.length,
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/*istanbul ignore next : foolproof */
|
|
88
|
+
return comment.loc
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Checks `a` is less than `b` or `a` equals `b`.
|
|
93
|
+
*
|
|
94
|
+
* @param {{line: number, column: number}} a - A location to compare.
|
|
95
|
+
* @param {{line: number, column: number}} b - Another location to compare.
|
|
96
|
+
* @returns {boolean} `true` if `a` is less than `b` or `a` equals `b`.
|
|
97
|
+
*/
|
|
98
|
+
lte(a, b) {
|
|
99
|
+
return a.line < b.line || (a.line === b.line && a.column <= b.column)
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parse the given comment token as a directive comment.
|
|
104
|
+
*
|
|
105
|
+
* @param {Token} comment - The comment token to parse.
|
|
106
|
+
* @returns {{kind: string, value: string, description: string | null}|null} The parsed data of the given comment. If `null`, it is not a directive comment.
|
|
107
|
+
*/
|
|
108
|
+
parseDirectiveComment(comment) {
|
|
109
|
+
const { text, description } = divideDirectiveComment(comment.value)
|
|
110
|
+
const match = DIRECTIVE_PATTERN.exec(text)
|
|
111
|
+
|
|
112
|
+
if (!match) {
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
const directiveText = match[1]
|
|
116
|
+
const lineCommentSupported = LINE_COMMENT_PATTERN.test(directiveText)
|
|
117
|
+
|
|
118
|
+
if (comment.type === "Line" && !lineCommentSupported) {
|
|
119
|
+
return null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
lineCommentSupported &&
|
|
124
|
+
comment.loc.start.line !== comment.loc.end.line
|
|
125
|
+
) {
|
|
126
|
+
// disable-line comment should not span multiple lines.
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const directiveValue = text.slice(match.index + directiveText.length)
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
kind: directiveText,
|
|
134
|
+
value: directiveValue.trim(),
|
|
135
|
+
description,
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Divides and trims description text and directive comments.
|
|
142
|
+
* @param {string} value The comment text to strip.
|
|
143
|
+
* @returns {{text: string, description: string | null}} The stripped text.
|
|
144
|
+
*/
|
|
145
|
+
function divideDirectiveComment(value) {
|
|
146
|
+
const divided = value.split(/\s-{2,}\s/u)
|
|
147
|
+
const text = divided[0].trim()
|
|
148
|
+
return {
|
|
149
|
+
text,
|
|
150
|
+
description: divided.length > 1 ? divided[1].trim() : null,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const DisabledArea = require("../internal/disabled-area")
|
|
8
|
+
const utils = require("../internal/utils")
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description:
|
|
14
|
+
"require a `eslint-enable` comment for every `eslint-disable` comment",
|
|
15
|
+
category: "Best Practices",
|
|
16
|
+
recommended: true,
|
|
17
|
+
url:
|
|
18
|
+
"https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/disable-enable-pair.html",
|
|
19
|
+
},
|
|
20
|
+
fixable: null,
|
|
21
|
+
schema: [
|
|
22
|
+
{
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
allowWholeFile: {
|
|
26
|
+
type: "boolean",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
additionalProperties: false,
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
type: "suggestion",
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
create(context) {
|
|
36
|
+
const allowWholeFile =
|
|
37
|
+
context.options[0] && context.options[0].allowWholeFile
|
|
38
|
+
const sourceCode = context.getSourceCode()
|
|
39
|
+
const disabledArea = DisabledArea.get(sourceCode)
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
Program(node) {
|
|
43
|
+
if (allowWholeFile && node.body.length === 0) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for (const area of disabledArea.areas) {
|
|
48
|
+
if (area.end != null) {
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
51
|
+
if (
|
|
52
|
+
allowWholeFile &&
|
|
53
|
+
utils.lte(area.start, node.loc.start)
|
|
54
|
+
) {
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
context.report({
|
|
59
|
+
loc: utils.toRuleIdLocation(area.comment, area.ruleId),
|
|
60
|
+
message: area.ruleId
|
|
61
|
+
? "Requires 'eslint-enable' directive for '{{ruleId}}'."
|
|
62
|
+
: "Requires 'eslint-enable' directive.",
|
|
63
|
+
data: area,
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const DisabledArea = require("../internal/disabled-area")
|
|
8
|
+
const utils = require("../internal/utils")
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description:
|
|
14
|
+
"disallow a `eslint-enable` comment for multiple `eslint-disable` comments",
|
|
15
|
+
category: "Best Practices",
|
|
16
|
+
recommended: true,
|
|
17
|
+
url:
|
|
18
|
+
"https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/no-aggregating-enable.html",
|
|
19
|
+
},
|
|
20
|
+
fixable: null,
|
|
21
|
+
schema: [],
|
|
22
|
+
type: "suggestion",
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
create(context) {
|
|
26
|
+
const sourceCode = context.getSourceCode()
|
|
27
|
+
const disabledArea = DisabledArea.get(sourceCode)
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
Program() {
|
|
31
|
+
for (const entry of disabledArea.numberOfRelatedDisableDirectives) {
|
|
32
|
+
const comment = entry[0]
|
|
33
|
+
const count = entry[1]
|
|
34
|
+
|
|
35
|
+
if (count >= 2) {
|
|
36
|
+
context.report({
|
|
37
|
+
loc: utils.toForceLocation(comment.loc),
|
|
38
|
+
message:
|
|
39
|
+
"This `eslint-enable` comment affects {{count}} `eslint-disable` comments. An `eslint-enable` comment should be for an `eslint-disable` comment.",
|
|
40
|
+
data: { count },
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const DisabledArea = require("../internal/disabled-area")
|
|
8
|
+
const utils = require("../internal/utils")
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: "disallow duplicate `eslint-disable` comments",
|
|
14
|
+
category: "Best Practices",
|
|
15
|
+
recommended: true,
|
|
16
|
+
url:
|
|
17
|
+
"https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/no-duplicate-disable.html",
|
|
18
|
+
},
|
|
19
|
+
fixable: null,
|
|
20
|
+
schema: [],
|
|
21
|
+
type: "problem",
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
create(context) {
|
|
25
|
+
const sourceCode = context.getSourceCode()
|
|
26
|
+
const disabledArea = DisabledArea.get(sourceCode)
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
Program() {
|
|
30
|
+
for (const item of disabledArea.duplicateDisableDirectives) {
|
|
31
|
+
context.report({
|
|
32
|
+
loc: utils.toRuleIdLocation(item.comment, item.ruleId),
|
|
33
|
+
message: item.ruleId
|
|
34
|
+
? "'{{ruleId}}' rule has been disabled already."
|
|
35
|
+
: "ESLint rules have been disabled already.",
|
|
36
|
+
data: item,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const ignore = require("ignore")
|
|
8
|
+
const DisabledArea = require("../internal/disabled-area")
|
|
9
|
+
const utils = require("../internal/utils")
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
meta: {
|
|
13
|
+
docs: {
|
|
14
|
+
description:
|
|
15
|
+
"disallow `eslint-disable` comments about specific rules",
|
|
16
|
+
category: "Stylistic Issues",
|
|
17
|
+
recommended: false,
|
|
18
|
+
url:
|
|
19
|
+
"https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/no-restricted-disable.html",
|
|
20
|
+
},
|
|
21
|
+
fixable: null,
|
|
22
|
+
schema: {
|
|
23
|
+
type: "array",
|
|
24
|
+
items: { type: "string" },
|
|
25
|
+
uniqueItems: true,
|
|
26
|
+
},
|
|
27
|
+
type: "suggestion",
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
create(context) {
|
|
31
|
+
const sourceCode = context.getSourceCode()
|
|
32
|
+
const disabledArea = DisabledArea.get(sourceCode)
|
|
33
|
+
|
|
34
|
+
if (context.options.length === 0) {
|
|
35
|
+
return {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const ig = ignore()
|
|
39
|
+
for (const pattern of context.options) {
|
|
40
|
+
ig.add(pattern)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
Program() {
|
|
45
|
+
for (const area of disabledArea.areas) {
|
|
46
|
+
if (area.ruleId == null || ig.ignores(area.ruleId)) {
|
|
47
|
+
context.report({
|
|
48
|
+
loc: utils.toRuleIdLocation(
|
|
49
|
+
area.comment,
|
|
50
|
+
area.ruleId
|
|
51
|
+
),
|
|
52
|
+
message: "Disabling '{{ruleId}}' is not allowed.",
|
|
53
|
+
data: {
|
|
54
|
+
ruleId: area.ruleId || String(context.options),
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const utils = require("../internal/utils")
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
meta: {
|
|
11
|
+
docs: {
|
|
12
|
+
description:
|
|
13
|
+
"disallow `eslint-disable` comments without rule names",
|
|
14
|
+
category: "Best Practices",
|
|
15
|
+
recommended: true,
|
|
16
|
+
url:
|
|
17
|
+
"https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/no-unlimited-disable.html",
|
|
18
|
+
},
|
|
19
|
+
fixable: null,
|
|
20
|
+
schema: [],
|
|
21
|
+
type: "suggestion",
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
create(context) {
|
|
25
|
+
const sourceCode = context.getSourceCode()
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
Program() {
|
|
29
|
+
for (const comment of sourceCode.getAllComments()) {
|
|
30
|
+
const directiveComment = utils.parseDirectiveComment(
|
|
31
|
+
comment
|
|
32
|
+
)
|
|
33
|
+
if (directiveComment == null) {
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const kind = directiveComment.kind
|
|
38
|
+
if (
|
|
39
|
+
kind !== "eslint-disable" &&
|
|
40
|
+
kind !== "eslint-disable-line" &&
|
|
41
|
+
kind !== "eslint-disable-next-line"
|
|
42
|
+
) {
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
if (!directiveComment.value) {
|
|
46
|
+
context.report({
|
|
47
|
+
loc: utils.toForceLocation(comment.loc),
|
|
48
|
+
message:
|
|
49
|
+
"Unexpected unlimited '{{kind}}' comment. Specify some rule names to disable.",
|
|
50
|
+
data: { kind: directiveComment.kind },
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
// Patch `Linter#verify` to work.
|
|
8
|
+
require("../utils/patch")()
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: "disallow unused `eslint-disable` comments",
|
|
14
|
+
category: "Best Practices",
|
|
15
|
+
recommended: false,
|
|
16
|
+
url:
|
|
17
|
+
"https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/no-unused-disable.html",
|
|
18
|
+
},
|
|
19
|
+
fixable: null,
|
|
20
|
+
schema: [],
|
|
21
|
+
type: "problem",
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
create() {
|
|
25
|
+
// This rule patches `Linter#verify` method and:
|
|
26
|
+
//
|
|
27
|
+
// 1. enables `reportUnusedDisableDirectives` option.
|
|
28
|
+
// 2. verifies the code.
|
|
29
|
+
// 3. converts `reportUnusedDisableDirectives` errors to `no-unused-disable` errors.
|
|
30
|
+
//
|
|
31
|
+
// So this rule itself does nothing.
|
|
32
|
+
return {}
|
|
33
|
+
},
|
|
34
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const DisabledArea = require("../internal/disabled-area")
|
|
8
|
+
const utils = require("../internal/utils")
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: "disallow unused `eslint-enable` comments",
|
|
14
|
+
category: "Best Practices",
|
|
15
|
+
recommended: true,
|
|
16
|
+
url:
|
|
17
|
+
"https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/no-unused-enable.html",
|
|
18
|
+
},
|
|
19
|
+
fixable: null,
|
|
20
|
+
schema: [],
|
|
21
|
+
type: "problem",
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
create(context) {
|
|
25
|
+
const sourceCode = context.getSourceCode()
|
|
26
|
+
const disabledArea = DisabledArea.get(sourceCode)
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
Program() {
|
|
30
|
+
for (const item of disabledArea.unusedEnableDirectives) {
|
|
31
|
+
context.report({
|
|
32
|
+
loc: utils.toRuleIdLocation(item.comment, item.ruleId),
|
|
33
|
+
message: item.ruleId
|
|
34
|
+
? "'{{ruleId}}' rule is re-enabled but it has not been disabled."
|
|
35
|
+
: "ESLint rules are re-enabled but those have not been disabled.",
|
|
36
|
+
data: item,
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const utils = require("../internal/utils")
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
meta: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: "disallow ESLint directive-comments",
|
|
13
|
+
category: "Stylistic Issues",
|
|
14
|
+
recommended: false,
|
|
15
|
+
url:
|
|
16
|
+
"https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/no-use.html",
|
|
17
|
+
},
|
|
18
|
+
fixable: null,
|
|
19
|
+
schema: [
|
|
20
|
+
{
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
allow: {
|
|
24
|
+
type: "array",
|
|
25
|
+
items: {
|
|
26
|
+
enum: [
|
|
27
|
+
"eslint",
|
|
28
|
+
"eslint-disable",
|
|
29
|
+
"eslint-disable-line",
|
|
30
|
+
"eslint-disable-next-line",
|
|
31
|
+
"eslint-enable",
|
|
32
|
+
"eslint-env",
|
|
33
|
+
"exported",
|
|
34
|
+
"global",
|
|
35
|
+
"globals",
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
additionalItems: false,
|
|
39
|
+
uniqueItems: true,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
additionalProperties: false,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
type: "suggestion",
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
create(context) {
|
|
49
|
+
const sourceCode = context.getSourceCode()
|
|
50
|
+
const allowed = new Set(
|
|
51
|
+
(context.options[0] && context.options[0].allow) || []
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
Program() {
|
|
56
|
+
for (const comment of sourceCode.getAllComments()) {
|
|
57
|
+
const directiveComment = utils.parseDirectiveComment(
|
|
58
|
+
comment
|
|
59
|
+
)
|
|
60
|
+
if (directiveComment == null) {
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!allowed.has(directiveComment.kind)) {
|
|
65
|
+
context.report({
|
|
66
|
+
loc: utils.toForceLocation(comment.loc),
|
|
67
|
+
message: "Unexpected ESLint directive comment.",
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Yosuke Ota <https://github.com/ota-meshi>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const utils = require("../internal/utils")
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
meta: {
|
|
11
|
+
docs: {
|
|
12
|
+
description:
|
|
13
|
+
"require include descriptions in ESLint directive-comments",
|
|
14
|
+
category: "Stylistic Issues",
|
|
15
|
+
recommended: false,
|
|
16
|
+
url:
|
|
17
|
+
"https://mysticatea.github.io/eslint-plugin-eslint-comments/rules/require-description.html",
|
|
18
|
+
},
|
|
19
|
+
fixable: null,
|
|
20
|
+
schema: [
|
|
21
|
+
{
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
ignore: {
|
|
25
|
+
type: "array",
|
|
26
|
+
items: {
|
|
27
|
+
enum: [
|
|
28
|
+
"eslint",
|
|
29
|
+
"eslint-disable",
|
|
30
|
+
"eslint-disable-line",
|
|
31
|
+
"eslint-disable-next-line",
|
|
32
|
+
"eslint-enable",
|
|
33
|
+
"eslint-env",
|
|
34
|
+
"exported",
|
|
35
|
+
"global",
|
|
36
|
+
"globals",
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
additionalItems: false,
|
|
40
|
+
uniqueItems: true,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
type: "suggestion",
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
create(context) {
|
|
50
|
+
const sourceCode = context.getSourceCode()
|
|
51
|
+
const ignores = new Set(
|
|
52
|
+
(context.options[0] && context.options[0].ignore) || []
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
Program() {
|
|
57
|
+
for (const comment of sourceCode.getAllComments()) {
|
|
58
|
+
const directiveComment = utils.parseDirectiveComment(
|
|
59
|
+
comment
|
|
60
|
+
)
|
|
61
|
+
if (directiveComment == null) {
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
if (ignores.has(directiveComment.kind)) {
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
if (!directiveComment.description) {
|
|
68
|
+
context.report({
|
|
69
|
+
loc: utils.toForceLocation(comment.loc),
|
|
70
|
+
message:
|
|
71
|
+
"Unexpected undescribed directive comment. Include descriptions to explain why the comment is necessary.",
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
}
|
package/lib/rules.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** DON'T EDIT THIS FILE; was created by scripts. */
|
|
2
|
+
"use strict"
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
"disable-enable-pair": require("./rules/disable-enable-pair"),
|
|
6
|
+
"no-aggregating-enable": require("./rules/no-aggregating-enable"),
|
|
7
|
+
"no-duplicate-disable": require("./rules/no-duplicate-disable"),
|
|
8
|
+
"no-restricted-disable": require("./rules/no-restricted-disable"),
|
|
9
|
+
"no-unlimited-disable": require("./rules/no-unlimited-disable"),
|
|
10
|
+
"no-unused-disable": require("./rules/no-unused-disable"),
|
|
11
|
+
"no-unused-enable": require("./rules/no-unused-enable"),
|
|
12
|
+
"no-use": require("./rules/no-use"),
|
|
13
|
+
"require-description": require("./rules/require-description"),
|
|
14
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Toru Nagashima <https://github.com/mysticatea>
|
|
3
|
+
* See LICENSE file in root directory for full license.
|
|
4
|
+
*/
|
|
5
|
+
"use strict"
|
|
6
|
+
|
|
7
|
+
const getLinters = require("../internal/get-linters")
|
|
8
|
+
const { toRuleIdLocation } = require("../internal/utils")
|
|
9
|
+
const quotedName = /'(.+?)'/u
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get the severity of a given rule.
|
|
13
|
+
* @param {object} config The config object to check.
|
|
14
|
+
* @param {string} ruleId The rule ID to check.
|
|
15
|
+
* @returns {number} The severity of the rule.
|
|
16
|
+
*/
|
|
17
|
+
function getSeverity(config, ruleId) {
|
|
18
|
+
const rules = config && config.rules
|
|
19
|
+
const ruleOptions = rules && rules[ruleId]
|
|
20
|
+
const severity = Array.isArray(ruleOptions) ? ruleOptions[0] : ruleOptions
|
|
21
|
+
|
|
22
|
+
switch (severity) {
|
|
23
|
+
case 2:
|
|
24
|
+
case "error":
|
|
25
|
+
return 2
|
|
26
|
+
|
|
27
|
+
case 1:
|
|
28
|
+
case "warn":
|
|
29
|
+
return 1
|
|
30
|
+
|
|
31
|
+
default:
|
|
32
|
+
return 0
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the comment which is at a given message location.
|
|
38
|
+
* @param {Message} message The message to get.
|
|
39
|
+
* @param {SourceCode|undefined} sourceCode The source code object to get.
|
|
40
|
+
* @returns {Comment|undefined} The gotten comment.
|
|
41
|
+
*/
|
|
42
|
+
function getCommentAt(message, sourceCode) {
|
|
43
|
+
if (sourceCode != null) {
|
|
44
|
+
const loc = { line: message.line, column: message.column - 1 }
|
|
45
|
+
const index = sourceCode.getIndexFromLoc(loc)
|
|
46
|
+
const options = { includeComments: true }
|
|
47
|
+
const comment = sourceCode.getTokenByRangeStart(index, options)
|
|
48
|
+
if (
|
|
49
|
+
comment != null &&
|
|
50
|
+
(comment.type === "Line" || comment.type === "Block")
|
|
51
|
+
) {
|
|
52
|
+
return comment
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return undefined
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check whether a given message is a `reportUnusedDisableDirectives` error.
|
|
60
|
+
* @param {Message} message The message.
|
|
61
|
+
* @returns {boolean} `true` if the message is a `reportUnusedDisableDirectives` error.
|
|
62
|
+
*/
|
|
63
|
+
function isUnusedDisableDirectiveError(message) {
|
|
64
|
+
return (
|
|
65
|
+
!message.fatal &&
|
|
66
|
+
!message.ruleId &&
|
|
67
|
+
message.message.includes("eslint-disable")
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Create `@eslint-community/eslint-comments/no-unused-disable` error.
|
|
73
|
+
* @param {string} ruleId The ruleId.
|
|
74
|
+
* @param {number} severity The severity of the rule.
|
|
75
|
+
* @param {Message} message The original message.
|
|
76
|
+
* @param {Comment|undefined} comment The directive comment.
|
|
77
|
+
* @returns {Message} The created error.
|
|
78
|
+
*/
|
|
79
|
+
function createNoUnusedDisableError(ruleId, severity, message, comment) {
|
|
80
|
+
const clone = Object.assign({}, message)
|
|
81
|
+
const match = quotedName.exec(message.message)
|
|
82
|
+
const targetRuleId = match && match[1]
|
|
83
|
+
|
|
84
|
+
clone.ruleId = ruleId
|
|
85
|
+
clone.severity = severity
|
|
86
|
+
clone.message = targetRuleId
|
|
87
|
+
? `'${targetRuleId}' rule is disabled but never reported.`
|
|
88
|
+
: "ESLint rules are disabled but never reported."
|
|
89
|
+
clone.suggestions = []
|
|
90
|
+
|
|
91
|
+
if (comment != null) {
|
|
92
|
+
if (targetRuleId) {
|
|
93
|
+
const loc = toRuleIdLocation(comment, targetRuleId)
|
|
94
|
+
clone.line = loc.start.line
|
|
95
|
+
clone.column = loc.start.column + 1
|
|
96
|
+
clone.endLine = loc.end.line
|
|
97
|
+
clone.endColumn = loc.end.column + 1
|
|
98
|
+
} else {
|
|
99
|
+
clone.endLine = comment.loc.end.line
|
|
100
|
+
clone.endColumn = comment.loc.end.column + 1
|
|
101
|
+
}
|
|
102
|
+
// Remove the whole node if it is the only rule, otherwise
|
|
103
|
+
// don't try to fix because it is quite complicated.
|
|
104
|
+
if (!comment.value.includes(",") && !comment.value.includes("--")) {
|
|
105
|
+
// We can't use the typical `fixer` helper because we are injecting
|
|
106
|
+
// this message after the fixes are resolved.
|
|
107
|
+
clone.suggestions = [
|
|
108
|
+
{
|
|
109
|
+
desc: "Remove `eslint-disable` comment.",
|
|
110
|
+
fix: {
|
|
111
|
+
range: comment.range,
|
|
112
|
+
text: comment.value.includes("\n") ? "\n" : "",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return clone
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Convert `reportUnusedDisableDirectives` errors to `@eslint-community/eslint-comments/no-unused-disable` errors.
|
|
124
|
+
* @param {Message[]} messages The original messages.
|
|
125
|
+
* @param {SourceCode|undefined} sourceCode The source code object.
|
|
126
|
+
* @param {string} ruleId The rule ID to convert.
|
|
127
|
+
* @param {number} severity The severity of the rule.
|
|
128
|
+
* @param {boolean} keepAsIs The flag to keep original errors as is.
|
|
129
|
+
* @returns {Message[]} The converted messages.
|
|
130
|
+
*/
|
|
131
|
+
function convert(messages, sourceCode, ruleId, severity, keepAsIs) {
|
|
132
|
+
for (let i = messages.length - 1; i >= 0; --i) {
|
|
133
|
+
const message = messages[i]
|
|
134
|
+
if (!isUnusedDisableDirectiveError(message)) {
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const newMessage = createNoUnusedDisableError(
|
|
139
|
+
ruleId,
|
|
140
|
+
severity,
|
|
141
|
+
message,
|
|
142
|
+
getCommentAt(message, sourceCode)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if (keepAsIs) {
|
|
146
|
+
messages.splice(i + 1, 0, newMessage)
|
|
147
|
+
} else {
|
|
148
|
+
messages.splice(i, 1, newMessage)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return messages
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = (
|
|
156
|
+
ruleId = "@eslint-community/eslint-comments/no-unused-disable"
|
|
157
|
+
) => {
|
|
158
|
+
for (const Linter of getLinters()) {
|
|
159
|
+
const verify0 = Linter.prototype._verifyWithoutProcessors
|
|
160
|
+
Object.defineProperty(Linter.prototype, "_verifyWithoutProcessors", {
|
|
161
|
+
value: function _verifyWithoutProcessors(
|
|
162
|
+
textOrSourceCode,
|
|
163
|
+
config,
|
|
164
|
+
filenameOrOptions
|
|
165
|
+
) {
|
|
166
|
+
const severity = getSeverity(config, ruleId)
|
|
167
|
+
if (severity === 0) {
|
|
168
|
+
return verify0.call(
|
|
169
|
+
this,
|
|
170
|
+
textOrSourceCode,
|
|
171
|
+
config,
|
|
172
|
+
filenameOrOptions
|
|
173
|
+
)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const options =
|
|
177
|
+
typeof filenameOrOptions === "string"
|
|
178
|
+
? { filename: filenameOrOptions }
|
|
179
|
+
: filenameOrOptions || {}
|
|
180
|
+
const reportUnusedDisableDirectives = Boolean(
|
|
181
|
+
options.reportUnusedDisableDirectives
|
|
182
|
+
)
|
|
183
|
+
const messages = verify0.call(
|
|
184
|
+
this,
|
|
185
|
+
textOrSourceCode,
|
|
186
|
+
config,
|
|
187
|
+
Object.assign({}, options, {
|
|
188
|
+
reportUnusedDisableDirectives: true,
|
|
189
|
+
})
|
|
190
|
+
)
|
|
191
|
+
return convert(
|
|
192
|
+
messages,
|
|
193
|
+
this.getSourceCode(),
|
|
194
|
+
ruleId,
|
|
195
|
+
severity,
|
|
196
|
+
reportUnusedDisableDirectives
|
|
197
|
+
)
|
|
198
|
+
},
|
|
199
|
+
configurable: true,
|
|
200
|
+
writable: true,
|
|
201
|
+
})
|
|
202
|
+
}
|
|
203
|
+
}
|
package/lib/utils.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eslint-community/eslint-plugin-eslint-comments",
|
|
3
|
+
"version": "3.2.0",
|
|
4
|
+
"description": "Additional ESLint rules for ESLint directive comments.",
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=6.5.0"
|
|
7
|
+
},
|
|
8
|
+
"main": "index.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"lib"
|
|
11
|
+
],
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"eslint": ">=4.19.1"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"escape-string-regexp": "^1.0.5",
|
|
17
|
+
"ignore": "^5.0.5"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@mysticatea/eslint-plugin": "^13.0.0",
|
|
21
|
+
"@types/node": "^14.0.1",
|
|
22
|
+
"@vuepress/plugin-pwa": "^1.0.1",
|
|
23
|
+
"babel-eslint": "^10.0.1",
|
|
24
|
+
"codecov": "^3.3.0",
|
|
25
|
+
"cross-spawn": "^6.0.5",
|
|
26
|
+
"eslint": "^7.0.0",
|
|
27
|
+
"eslint4b": "^7.0.0",
|
|
28
|
+
"fs-extra": "^8.0.1",
|
|
29
|
+
"mocha": "^6.1.4",
|
|
30
|
+
"nyc": "^14.1.1",
|
|
31
|
+
"opener": "^1.4.3",
|
|
32
|
+
"rimraf": "^2.6.2",
|
|
33
|
+
"semver": "^7.3.2",
|
|
34
|
+
"string-replace-loader": "^2.1.1",
|
|
35
|
+
"vue-eslint-editor": "^1.1.0",
|
|
36
|
+
"vuepress": "^1.0.1"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"preversion": "npm test",
|
|
40
|
+
"version": "node scripts/update && git add .",
|
|
41
|
+
"postversion": "git push && git push --tags",
|
|
42
|
+
"clean": "rimraf .nyc_output coverage docs/.vuepress/dist",
|
|
43
|
+
"docs:build": "vuepress build docs",
|
|
44
|
+
"docs:watch": "vuepress dev docs",
|
|
45
|
+
"docs:deploy": "node scripts/deploy",
|
|
46
|
+
"lint": "eslint lib scripts tests",
|
|
47
|
+
"pretest": "npm run -s lint",
|
|
48
|
+
"test": "nyc npm run -s test:mocha",
|
|
49
|
+
"test:ci": "nyc npm run -s test:mocha",
|
|
50
|
+
"test:mocha": "mocha \"tests/lib/**/*.js\" --reporter dot --timeout 8000",
|
|
51
|
+
"watch": "npm run -s test:mocha -- --watch --growl",
|
|
52
|
+
"coverage": "nyc report --reporter lcov && opener coverage/lcov-report/index.html",
|
|
53
|
+
"codecov": "nyc report --reporter text-lcov | codecov --pipe --disable=gcov"
|
|
54
|
+
},
|
|
55
|
+
"repository": {
|
|
56
|
+
"type": "git",
|
|
57
|
+
"url": "https://github.com/eslint-community/eslint-plugin-eslint-comments"
|
|
58
|
+
},
|
|
59
|
+
"keywords": [
|
|
60
|
+
"eslint",
|
|
61
|
+
"eslintplugin",
|
|
62
|
+
"eslint-plugin",
|
|
63
|
+
"plugin",
|
|
64
|
+
"comment",
|
|
65
|
+
"comments",
|
|
66
|
+
"directive",
|
|
67
|
+
"global",
|
|
68
|
+
"globals",
|
|
69
|
+
"exported",
|
|
70
|
+
"eslint-env",
|
|
71
|
+
"eslint-enable",
|
|
72
|
+
"eslint-disable",
|
|
73
|
+
"eslint-disable-line",
|
|
74
|
+
"eslint-disable-next-line"
|
|
75
|
+
],
|
|
76
|
+
"author": "Toru Nagashima",
|
|
77
|
+
"license": "MIT",
|
|
78
|
+
"bugs": {
|
|
79
|
+
"url": "https://github.com/eslint-community/eslint-plugin-eslint-comments/issues"
|
|
80
|
+
},
|
|
81
|
+
"homepage": "https://github.com/eslint-community/eslint-plugin-eslint-comments#readme"
|
|
82
|
+
}
|