-
-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathtop-level-function.ts
92 lines (84 loc) · 3.04 KB
/
top-level-function.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import { createEslintRule } from '../utils'
export const RULE_NAME = 'top-level-function'
export type MessageIds = 'topLevelFunctionDeclaration'
export type Options = []
export default createEslintRule<Options, MessageIds>({
name: RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Enforce top-level functions to be declared with function keyword',
},
fixable: 'code',
schema: [],
messages: {
topLevelFunctionDeclaration: 'Top-level functions should be declared with function keyword',
},
},
defaultOptions: [],
create: (context) => {
return {
VariableDeclaration(node) {
if (node.parent.type !== 'Program' && node.parent.type !== 'ExportNamedDeclaration')
return
if (node.declarations.length !== 1)
return
if (node.kind !== 'const')
return
if (node.declare)
return
const declaration = node.declarations[0]
if (
declaration.init?.type !== 'ArrowFunctionExpression'
&& declaration.init?.type !== 'FunctionExpression'
) {
return
}
if (declaration.id?.type !== 'Identifier')
return
if (declaration.id.typeAnnotation)
return
if (
declaration.init.body.type !== 'BlockStatement'
&& declaration.id?.loc.start.line === declaration.init?.body.loc.end.line
) {
return
}
const fnExpression = declaration.init
const body = declaration.init.body
const id = declaration.id
context.report({
node,
loc: {
start: id.loc.start,
end: body.loc.start,
},
messageId: 'topLevelFunctionDeclaration',
fix(fixer) {
const code = context.getSourceCode().text
const textName = code.slice(id.range[0], id.range[1])
const textArgs = fnExpression.params.length
? code.slice(fnExpression.params[0].range[0], fnExpression.params[fnExpression.params.length - 1].range[1])
: ''
const textBody = body.type === 'BlockStatement'
? code.slice(body.range[0], body.range[1])
: `{\n return ${code.slice(body.range[0], body.range[1])}\n}`
const textGeneric = fnExpression.typeParameters
? code.slice(fnExpression.typeParameters.range[0], fnExpression.typeParameters.range[1])
: ''
const textTypeReturn = fnExpression.returnType
? code.slice(fnExpression.returnType.range[0], fnExpression.returnType.range[1])
: ''
const textAsync = fnExpression.async ? 'async ' : ''
const final = `${textAsync}function ${textName} ${textGeneric}(${textArgs})${textTypeReturn} ${textBody}`
// console.log({
// input: code.slice(node.range[0], node.range[1]),
// output: final,
// })
return fixer.replaceTextRange([node.range[0], node.range[1]], final)
},
})
},
}
},
})