Skip to content

Commit ebf5a83

Browse files
committed
✨ isParenthesized supports to check redundant parens
1 parent 89194cb commit ebf5a83

File tree

4 files changed

+117
-7
lines changed

4 files changed

+117
-7
lines changed

.eslintrc.yml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ extends:
66
- plugin:@mysticatea/+node
77

88
rules:
9+
init-declarations: "off"
910
"@mysticatea/node/no-unsupported-features/es-syntax":
1011
- error
1112
- ignores:

docs/api/ast-utils.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,8 @@ module.exports = {
414414
## isParenthesized
415415

416416
```js
417-
const ret = utils.isParenthesized(node, sourceCode)
417+
const ret1 = utils.isParenthesized(times, node, sourceCode)
418+
const ret2 = utils.isParenthesized(node, sourceCode)
418419
```
419420

420421
Check whether a given node is parenthesized or not.
@@ -445,13 +446,16 @@ with ((b)) {}
445446

446447
Name | Type | Description
447448
:-----|:-----|:------------
449+
times | number | Optional. The number of redundant parenthesized. Default is `1`.
448450
node | Node | The node to check.
449451
sourceCode | SourceCode | The source code object to get tokens.
450452

451453
### Return value
452454

453455
`true` if the node is parenthesized.
454456

457+
If `times` was given, it returns `true` only if the node is parenthesized the `times` times. For example, `isParenthesized(2, node, sourceCode)` returns `true` for `((foo))`, but not for `(foo)`.
458+
455459
### Example
456460

457461
```js{9}

src/is-parenthesized.js

+35-6
Original file line numberDiff line numberDiff line change
@@ -54,26 +54,55 @@ function getParentSyntaxParen(node, sourceCode) {
5454
}
5555
}
5656

57+
/**
58+
* Check whether a given node is parenthesized or not.
59+
* @param {number} times The number of parantheses.
60+
* @param {Node} node The AST node to check.
61+
* @param {SourceCode} sourceCode The source code object to get tokens.
62+
* @returns {boolean} `true` if the node is parenthesized the given times.
63+
*/
5764
/**
5865
* Check whether a given node is parenthesized or not.
5966
* @param {Node} node The AST node to check.
6067
* @param {SourceCode} sourceCode The source code object to get tokens.
6168
* @returns {boolean} `true` if the node is parenthesized.
6269
*/
63-
export function isParenthesized(node, sourceCode) {
70+
export function isParenthesized(
71+
timesOrNode,
72+
nodeOrSourceCode,
73+
optionalSourceCode
74+
) {
75+
let times, node, sourceCode, maybeLeftParen, maybeRightParen
76+
if (typeof timesOrNode === "number") {
77+
times = timesOrNode | 0
78+
node = nodeOrSourceCode
79+
sourceCode = optionalSourceCode
80+
if (!(times >= 1)) {
81+
throw new TypeError("'times' should be a positive integer.")
82+
}
83+
} else {
84+
times = 1
85+
node = timesOrNode
86+
sourceCode = nodeOrSourceCode
87+
}
88+
6489
if (node == null) {
6590
return false
6691
}
6792

68-
const maybeLeftParen = sourceCode.getTokenBefore(node)
69-
const maybeRightParen = sourceCode.getTokenAfter(node)
70-
71-
return (
93+
maybeLeftParen = maybeRightParen = node
94+
do {
95+
maybeLeftParen = sourceCode.getTokenBefore(maybeLeftParen)
96+
maybeRightParen = sourceCode.getTokenAfter(maybeRightParen)
97+
} while (
7298
maybeLeftParen != null &&
7399
maybeRightParen != null &&
74100
isOpeningParenToken(maybeLeftParen) &&
75101
isClosingParenToken(maybeRightParen) &&
76102
// Avoid false positive such as `if (a) {}`
77-
maybeLeftParen !== getParentSyntaxParen(node, sourceCode)
103+
maybeLeftParen !== getParentSyntaxParen(node, sourceCode) &&
104+
--times > 0
78105
)
106+
107+
return times === 0
79108
}

test/is-parenthesized.js

+76
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,80 @@ describe("The 'isParenthesized' function", () => {
234234
}
235235
})
236236
}
237+
238+
for (const { code, expected } of [
239+
{
240+
code: "777",
241+
expected: {
242+
"body.0": false,
243+
"body.0.expression": false,
244+
},
245+
},
246+
{
247+
code: "(777)",
248+
expected: {
249+
"body.0": false,
250+
"body.0.expression": false,
251+
},
252+
},
253+
{
254+
code: "((777))",
255+
expected: {
256+
"body.0": false,
257+
"body.0.expression": true,
258+
},
259+
},
260+
{
261+
code: "if (a) ;",
262+
expected: {
263+
"body.0": false,
264+
"body.0.test": false,
265+
},
266+
},
267+
{
268+
code: "if ((a)) ;",
269+
expected: {
270+
"body.0": false,
271+
"body.0.test": false,
272+
},
273+
},
274+
{
275+
code: "if (((a))) ;",
276+
expected: {
277+
"body.0": false,
278+
"body.0.test": true,
279+
},
280+
},
281+
]) {
282+
describe(`on the code \`${code}\` and 2 times`, () => {
283+
for (const key of Object.keys(expected)) {
284+
it(`should return ${expected[key]} at "${key}"`, () => {
285+
const linter = new eslint.Linter()
286+
287+
let actual = null
288+
linter.defineRule("test", context => ({
289+
Program(node) {
290+
actual = isParenthesized(
291+
2,
292+
dotProp.get(node, key),
293+
context.getSourceCode()
294+
)
295+
},
296+
}))
297+
const messages = linter.verify(code, {
298+
env: { es6: true },
299+
parserOptions: { ecmaVersion: 2018 },
300+
rules: { test: "error" },
301+
})
302+
303+
assert.strictEqual(
304+
messages.length,
305+
0,
306+
messages[0] && messages[0].message
307+
)
308+
assert.strictEqual(actual, expected[key])
309+
})
310+
}
311+
})
312+
}
237313
})

0 commit comments

Comments
 (0)