Skip to content

Commit 84edfcb

Browse files
committed
Add invalid-node-types rule
1 parent ade4b6a commit 84edfcb

File tree

5 files changed

+139
-3
lines changed

5 files changed

+139
-3
lines changed

specification/eslint.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export default defineConfig({
1313
},
1414
plugins: { 'es-spec-validator': validator },
1515
rules: {
16-
'es-spec-validator/single-key-dictionary-key-is-string': 'error'
16+
'es-spec-validator/single-key-dictionary-key-is-string': 'error',
17+
'es-spec-validator/invalid-node-types': 'warn',
1718
}
1819
})

validator/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ It is configured [in the specification directory](../specification/eslint.config
77

88
| Name | Description |
99
| - | - |
10-
| `single-key-dictionary-key-is-string` | `SingleKeyDictionary` keys must be strings |
10+
| `single-key-dictionary-key-is-string` | `SingleKeyDictionary` keys must be strings. |
11+
| `invalid-node-types` | The spec uses a subset of TypeScript, so some types, clauses and expressions are not allowed. |
1112

1213
## Usage
1314

validator/eslint-plugin-es-spec.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import singleKeyDict from './rules/single-key-dictionary-key-is-string.js'
2+
import invalidNodeTypes from './rules/invalid-node-types.js'
23

34
export default {
45
rules: {
5-
'single-key-dictionary-key-is-string': singleKeyDict
6+
'single-key-dictionary-key-is-string': singleKeyDict,
7+
'invalid-node-types': invalidNodeTypes,
68
}
79
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { ESLintUtils } from '@typescript-eslint/utils';
2+
3+
const createRule = ESLintUtils.RuleCreator(name => `https://example.com/rule/${name}`)
4+
5+
export default createRule({
6+
name: 'invalid-node-types',
7+
create(context) {
8+
return {
9+
'ArrowFunctionExpression, FunctionDeclaration, FunctionExpression, TSFunctionType, TSEmptyBodyFunctionExpression, TSDeclareFunction, MethodDefinition'(node) {
10+
context.report({ node, messageId: 'function' })
11+
},
12+
'DoWhileStatement, ForInStatement, ForOfStatement, ForStatement, WhileStatement'(node) {
13+
context.report({ node, messageId: 'loop' })
14+
},
15+
'CatchClause, ThrowStatement, TryStatement'(node) {
16+
context.report({ node, messageId: 'exception' })
17+
},
18+
'TSAsyncKeyword, AwaitExpression'(node) {
19+
context.report({ node, messageId: 'async' })
20+
},
21+
'SwitchStatement, SwitchCase, ConditionalExpression, IfStatement, TSConditionalType'(node) {
22+
context.report({ node, messageId: 'condition' })
23+
},
24+
TSUndefinedKeyword(node) {
25+
context.report({ node, messageId: 'undefined' })
26+
},
27+
TSNeverKeyword(node) {
28+
context.report({ node, messageId: 'never' })
29+
},
30+
}
31+
},
32+
meta: {
33+
docs: {
34+
description: 'The Elasticsearch specification only uses a subset of the TypeScript language, primarily classes, interfaces, enums and type aliases.',
35+
},
36+
messages: {
37+
function: 'Functions are not supported by the Elasticsearch specification',
38+
loop: 'Loops are not supported by the Elasticsearch specification',
39+
exception: 'Exception management is not supported by the Elasticsearch specification',
40+
async: 'Async/await syntax is not supported by the Elasticsearch specification',
41+
condition: 'Conditional expressions are not supported by the Elasticsearch specification',
42+
undefined: '`undefined` is not a valid type in the Elasticsearch specification',
43+
never: '`never` is not a valid type in the Elasticsearch specification',
44+
},
45+
type: 'suggestion',
46+
},
47+
defaultOptions: []
48+
})
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { RuleTester } from '@typescript-eslint/rule-tester'
2+
import rule from '../rules/invalid-node-types.js'
3+
4+
const ruleTester = new RuleTester({
5+
languageOptions: {
6+
parserOptions: {
7+
projectService: {
8+
allowDefaultProject: ['*.ts*'],
9+
},
10+
tsconfigRootDir: import.meta.dirname,
11+
},
12+
},
13+
})
14+
15+
ruleTester.run('invalid-node-types', rule, {
16+
valid: [
17+
`type MyType = number`,
18+
`enum MyEnum { foo, bar, baz }`,
19+
`class MyClass {
20+
foo: boolean
21+
bar: object
22+
baz: any
23+
}`,
24+
`interface MyInterface {
25+
id: number
26+
data: object
27+
records: Record<string, number[]>[]
28+
}`
29+
],
30+
invalid: [
31+
{
32+
code: `type MyFn = (name: string) => boolean`,
33+
errors: [{ messageId: 'function' }]
34+
},
35+
{
36+
code: `function foo(bar: string): number { return 1 }`,
37+
errors: [{ messageId: 'function' }]
38+
},
39+
{
40+
code: `try {
41+
doThing()
42+
} catch (err: any) {
43+
logError(err)
44+
}`,
45+
errors: [
46+
// two messages because TryStatement and CatchClause get flagged separately
47+
{ messageId: 'exception' },
48+
{ messageId: 'exception' },
49+
]
50+
},
51+
{
52+
code: `for (let i = 0; i++; i < 5) { console.log(i) }`,
53+
errors: [{ messageId: 'loop' }]
54+
},
55+
{
56+
code: `while (true) { console.log('hello') }`,
57+
errors: [{ messageId: 'loop' }]
58+
},
59+
{
60+
code: `for (const i of myArray) { console.log(i) }`,
61+
errors: [{ messageId: 'loop' }]
62+
},
63+
{
64+
code: `const val = await myFn()`,
65+
errors: [{ messageId: 'async' }]
66+
},
67+
{
68+
code: `if (true) console.log('true!')`,
69+
errors: [{ messageId: 'condition' }]
70+
},
71+
{
72+
code: 'const CondType = someCondition ? TypeOne : TypeTwo',
73+
errors: [{ messageId: 'condition' }]
74+
},
75+
{
76+
code: 'type Foo = undefined',
77+
errors: [{ messageId: 'undefined' }]
78+
},
79+
{
80+
code: 'type Foo = never',
81+
errors: [{ messageId: 'never' }]
82+
}
83+
],
84+
})

0 commit comments

Comments
 (0)