Skip to content

Commit e0a7037

Browse files
committedNov 27, 2024··
feat: new command no-x-above
1 parent 10cb238 commit e0a7037

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed
 

‎src/commands/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { keepSorted } from './keep-sorted'
44
import { keepUnique } from './keep-unique'
55
import { noShorthand } from './no-shorthand'
66
import { noType } from './no-type'
7+
import { noXAbove } from './no-x-above'
78
import { regex101 } from './regex101'
89
import { toArrow } from './to-arrow'
910
import { toDestructuring } from './to-destructuring'
@@ -24,6 +25,7 @@ export {
2425
keepUnique,
2526
noShorthand,
2627
noType,
28+
noXAbove,
2729
regex101,
2830
toArrow,
2931
toDestructuring,
@@ -45,6 +47,7 @@ export const builtinCommands = [
4547
keepUnique,
4648
noShorthand,
4749
noType,
50+
noXAbove,
4851
regex101,
4952
toArrow,
5053
toDestructuring,

‎src/commands/no-x-above.md

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# `no-x-above`
2+
3+
Disallow certain syntaxes above or below the comment, with in the current block.
4+
5+
## Triggers
6+
7+
- `// @no-await-above` - Disallow `await` above the comment.
8+
- `// @no-await-below` - Disallow `await` below the comment.
9+
10+
## Examples
11+
12+
```js
13+
const foo = syncOp()
14+
const bar = await asyncOp() // <-- this is not allowed
15+
// @no-await-above
16+
const baz = await asyncOp() // <-- this is ok
17+
```
18+
19+
The effect will only affect the current scope, for example:
20+
21+
```js
22+
console.log(await foo()) // <-- this is not checked, as it's not in the function scope where the comment is
23+
24+
async function foo() {
25+
const bar = syncOp()
26+
const baz = await asyncOp() // <-- this is not allowed
27+
// @no-await-above
28+
const qux = await asyncOp() // <-- this is ok
29+
}
30+
```

‎src/commands/no-x-above.test.ts

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { $, run } from './_test-utils'
2+
import { noXAbove as command } from './no-x-above'
3+
4+
run(
5+
command,
6+
// {
7+
// code: $`
8+
// /// no-await-below
9+
// const obj = await foo()
10+
// `,
11+
// errors: ['command-error'],
12+
// },
13+
// $`
14+
// const obj = await foo()
15+
// /// no-await-below
16+
// const obj = foo()
17+
// `,
18+
// {
19+
// code: $`
20+
// const obj = await foo()
21+
// /// no-await-above
22+
// const obj = foo()
23+
// `,
24+
// errors: ['command-fix'],
25+
// },
26+
// // Don't count outside of scope
27+
// $`
28+
// await foo()
29+
// async function foo() {
30+
// /// no-await-above
31+
// const obj = await Promise.all([])
32+
// }
33+
// `,
34+
// Don't count inside
35+
$`
36+
async function foo() {
37+
/// no-await-below
38+
console.log('foo')
39+
const bar = async () => {
40+
const obj = await Promise.all([])
41+
}
42+
}
43+
`,
44+
)

‎src/commands/no-x-above.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { Command } from '../types'
2+
3+
const types = [
4+
'await',
5+
6+
// TODO: implement
7+
// 'statements',
8+
// 'functions',
9+
] as const
10+
11+
export const noXAbove: Command = {
12+
name: 'no-x-above',
13+
match: new RegExp(`^\\s*[/:@]\\s*no-(${types.join('|')})-(above|below)$`),
14+
action(ctx) {
15+
const type = ctx.matches[1] as (typeof types)[number]
16+
const direction = ctx.matches[2] as 'above' | 'below'
17+
18+
const node = ctx.findNodeBelow(() => true)
19+
const parent = node?.parent
20+
if (!parent)
21+
return ctx.reportError('No parent node found')
22+
23+
if (parent.type !== 'Program' && parent.type !== 'BlockStatement')
24+
return ctx.reportError('Parent node is not a block')
25+
26+
const children = parent.body
27+
28+
const targetNodes = direction === 'above'
29+
? children.filter(c => c.range[1] <= ctx.comment.range[0])
30+
: children.filter(c => c.range[0] >= ctx.comment.range[1])
31+
32+
if (!targetNodes.length)
33+
return
34+
35+
switch (type) {
36+
case 'await':
37+
for (const target of targetNodes) {
38+
ctx.traverse(target, (path, { SKIP }) => {
39+
if (path.node.type === 'FunctionDeclaration' || path.node.type === 'FunctionExpression' || path.node.type === 'ArrowFunctionExpression') {
40+
return SKIP
41+
}
42+
if (path.node.type === 'AwaitExpression') {
43+
ctx.report({
44+
node: path.node,
45+
message: 'Disallowed await expression',
46+
})
47+
}
48+
})
49+
}
50+
return
51+
default:
52+
return ctx.reportError(`Unknown type: ${type}`)
53+
}
54+
// console.log({ targetRange: targetNodes })
55+
},
56+
}

0 commit comments

Comments
 (0)
Please sign in to comment.