Skip to content

Commit 3b93a30

Browse files
futpibmysticatea
authored andcommitted
Allow disable without enable when disable covers the whole file (#8)
* Move `lte` to `utils` for reuse * Add `disable-enable-pair` `allowWholeFile` option * Reimplement with "eslint@~3.1.0" compatibility in mind * Add documentation of `allowWholeFile` option
1 parent a7ef846 commit 3b93a30

File tree

5 files changed

+186
-16
lines changed

5 files changed

+186
-16
lines changed

docs/rules/disable-enable-pair.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,32 @@ var foo = bar()
4141
var foo = bar()
4242
/*eslint-enable*/
4343
```
44+
45+
## Options
46+
47+
The `allowWholeFile` option lets you allow disabling rules for the entire file while still catching "open" `eslint-disable` directives in the middle of a file.
48+
49+
```json
50+
{
51+
"eslint-comments/disable-enable-pair": ["error", {"allowWholeFile": true}]
52+
}
53+
```
54+
55+
Examples of :-1: **incorrect** code for this rule:
56+
57+
```js
58+
/*eslint-disable no-undef */
59+
var foo = bar()
60+
/*eslint-disable no-unused-vars */
61+
var fizz = buzz()
62+
```
63+
64+
Examples of :+1: **correct** code for this rule:
65+
66+
```js
67+
/*eslint-disable no-undef */
68+
var foo = bar()
69+
/*eslint-disable no-unused-vars */
70+
var fizz = buzz()
71+
/*eslint-enable no-unused-vars */
72+
```

lib/disabled-area.js

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
*/
66
"use strict"
77

8+
//------------------------------------------------------------------------------
9+
// Requirements
10+
//------------------------------------------------------------------------------
11+
12+
const utils = require("./utils")
13+
814
//------------------------------------------------------------------------------
915
// Helpers
1016
//------------------------------------------------------------------------------
@@ -13,18 +19,6 @@ const COMMENT_DIRECTIVE = /^\s*(eslint-(?:en|dis)able(?:(?:-next)?-line)?)\s*(?:
1319
const DELIMITER = /[\s,]+/g
1420
const pool = new WeakMap()
1521

16-
/**
17-
* Checks `a` is less than `b` or `a` equals `b`.
18-
*
19-
* @param {{line: number, column: number}} a - A location to compare.
20-
* @param {{line: number, column: number}} b - Another location to compare.
21-
* @returns {boolean} `true` if `a` is less than `b` or `a` equals `b`.
22-
* @private
23-
*/
24-
function lte(a, b) {
25-
return a.line < b.line || (a.line === b.line && a.column <= b.column)
26-
}
27-
2822
//------------------------------------------------------------------------------
2923
// Exports
3024
//------------------------------------------------------------------------------
@@ -171,8 +165,8 @@ module.exports = class DisabledArea {
171165
const area = this.areas[i]
172166

173167
if ((area.ruleId === null || area.ruleId === ruleId) &&
174-
lte(area.start, location) &&
175-
(area.end === null || lte(location, area.end))
168+
utils.lte(area.start, location) &&
169+
(area.end === null || utils.lte(location, area.end))
176170
) {
177171
return area
178172
}

lib/rules/disable-enable-pair.js

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,41 @@
1212
const DisabledArea = require("../disabled-area")
1313
const utils = require("../utils")
1414

15+
//------------------------------------------------------------------------------
16+
// Helpers
17+
//------------------------------------------------------------------------------
18+
19+
/**
20+
* Checks whether `disabledArea` covers `node`.
21+
*
22+
* @param {DisabledArea} disabledArea - Disabled area in question.
23+
* @param {Node} node - The node that might be covered.
24+
* @returns {boolean} `true` if `disabledArea` covers `node`.
25+
* @private
26+
*/
27+
function disabledAreaCoversNode(disabledArea, node) {
28+
return utils.lte(disabledArea.start, node.loc.start) &&
29+
(!disabledArea.end || utils.lte(node.loc.end, disabledArea.end))
30+
}
31+
32+
/**
33+
* Checks whether `disabledArea` covers `node`'s body.
34+
*
35+
* @param {DisabledArea} disabledArea - Disabled area in question.
36+
* @param {Node} node - The node whose body may or may not be covered.
37+
* Must have a `body` property (a Program or BlockStatement will do).
38+
* @returns {boolean} `true` if `disabledArea` covers `node`'s body.
39+
* @private
40+
*/
41+
function disabledAreaCoversNodeBody(disabledArea, node) {
42+
const body = node.body
43+
const first = body[0]
44+
const last = body[body.length - 1]
45+
return !first ||
46+
(disabledAreaCoversNode(disabledArea, first) &&
47+
disabledAreaCoversNode(disabledArea, last))
48+
}
49+
1550
//------------------------------------------------------------------------------
1651
// Rule Definition
1752
//------------------------------------------------------------------------------
@@ -24,20 +59,39 @@ module.exports = {
2459
recommended: false,
2560
},
2661
fixable: false,
27-
schema: [],
62+
schema: [{
63+
type: "object",
64+
properties: {
65+
allowWholeFile: {
66+
type: "boolean",
67+
},
68+
},
69+
additionalProperties: false,
70+
}],
2871
},
2972

3073
create(context) {
74+
const allowWholeFile = context.options[0] && context.options[0].allowWholeFile
75+
3176
const sourceCode = context.getSourceCode()
3277
const disabledArea = DisabledArea.get(sourceCode)
3378

79+
const firstDisabledArea = disabledArea.areas[0]
80+
3481
return {
35-
Program() {
82+
Program(node) {
3683
for (const area of disabledArea.areas) {
3784
if (area.end != null) {
3885
continue
3986
}
4087

88+
if (allowWholeFile &&
89+
area === firstDisabledArea &&
90+
disabledAreaCoversNodeBody(area, node)
91+
) {
92+
continue
93+
}
94+
4195
context.report({
4296
loc: utils.toRuleIdLocation(area.comment, area.ruleId),
4397
message: (area.ruleId) ?

lib/utils.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,15 @@ module.exports = {
9393
/*istanbul ignore next : foolproof */
9494
return comment.loc
9595
},
96+
97+
/**
98+
* Checks `a` is less than `b` or `a` equals `b`.
99+
*
100+
* @param {{line: number, column: number}} a - A location to compare.
101+
* @param {{line: number, column: number}} b - Another location to compare.
102+
* @returns {boolean} `true` if `a` is less than `b` or `a` equals `b`.
103+
*/
104+
lte(a, b) {
105+
return a.line < b.line || (a.line === b.line && a.column <= b.column)
106+
},
96107
}

tests/lib/rules/disable-enable-pair.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,38 @@ function foo() {
4747
/*eslint-enable*/
4848
/*eslint-enable*/
4949
`,
50+
{
51+
code: `
52+
console.log('This code does not even have any special comments')
53+
`,
54+
options: [{ allowWholeFile: true }],
55+
},
56+
{
57+
code: `
58+
/*eslint-disable*/
59+
`,
60+
options: [{ allowWholeFile: true }],
61+
},
62+
{
63+
code: `
64+
/*eslint-disable no-undef*/
65+
/*eslint-disable no-unused-vars*/
66+
/*eslint-enable*/
67+
`,
68+
options: [{ allowWholeFile: true }],
69+
},
70+
{
71+
code: `
72+
73+
/**
74+
* @file This test case makes sure comments and blank lines
75+
* before "whole-file" eslint-disable are allowed.
76+
*/
77+
78+
/*eslint-disable*/
79+
`,
80+
options: [{ allowWholeFile: true }],
81+
},
5082
],
5183
invalid: [
5284
{
@@ -108,5 +140,55 @@ function foo() {
108140
},
109141
],
110142
},
143+
{
144+
code: `
145+
/*eslint-disable no-undef*/
146+
console.log();
147+
/*eslint-disable no-unused-vars*/
148+
`,
149+
options: [{ allowWholeFile: true }],
150+
errors: [
151+
{
152+
message: "Requires 'eslint-enable' directive for 'no-unused-vars'.",
153+
line: 4,
154+
column: 18,
155+
endLine: 4,
156+
endColumn: 32,
157+
},
158+
],
159+
},
160+
{
161+
code: `
162+
console.log();
163+
/*eslint-disable no-unused-vars*/
164+
`,
165+
options: [{ allowWholeFile: true }],
166+
errors: [
167+
{
168+
message: "Requires 'eslint-enable' directive for 'no-unused-vars'.",
169+
line: 3,
170+
column: 18,
171+
endLine: 3,
172+
endColumn: 32,
173+
},
174+
],
175+
},
176+
{
177+
code: `
178+
{
179+
/*eslint-disable no-unused-vars*/
180+
}
181+
`,
182+
options: [{ allowWholeFile: true }],
183+
errors: [
184+
{
185+
message: "Requires 'eslint-enable' directive for 'no-unused-vars'.",
186+
line: 3,
187+
column: 18,
188+
endLine: 3,
189+
endColumn: 32,
190+
},
191+
],
192+
},
111193
],
112194
})

0 commit comments

Comments
 (0)