-
-
Notifications
You must be signed in to change notification settings - Fork 47
/
Copy pathrequire-event-prefix.ts
122 lines (114 loc) · 3.11 KB
/
require-event-prefix.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { createRule } from '../utils/index.js';
import { type TSTools, getTypeScriptTools } from '../utils/ts-utils/index.js';
import {
type MethodSignature,
type Symbol,
SymbolFlags,
SyntaxKind,
type Type,
type TypeReferenceNode,
type PropertySignature
} from 'typescript';
import type { CallExpression } from 'estree';
export default createRule('require-event-prefix', {
meta: {
docs: {
description: 'require component event names to start with "on"',
category: 'Stylistic Issues',
conflictWithPrettier: false,
recommended: false
},
schema: [
{
type: 'object',
properties: {
checkAsyncFunctions: {
type: 'boolean'
}
},
additionalProperties: false
}
],
messages: {
nonPrefixedFunction: 'Component event name must start with "on".'
},
type: 'suggestion',
conditions: [
{
svelteVersions: ['5'],
svelteFileTypes: ['.svelte']
}
]
},
create(context) {
const tsTools = getTypeScriptTools(context);
if (!tsTools) {
return {};
}
const checkAsyncFunctions = context.options[0]?.checkAsyncFunctions ?? false;
return {
CallExpression(node) {
const propsType = getPropsType(node, tsTools);
if (propsType === undefined) {
return;
}
for (const property of propsType.getProperties()) {
if (
isFunctionLike(property) &&
!property.getName().startsWith('on') &&
(checkAsyncFunctions || !isFunctionAsync(property))
) {
const declarationTsNode = property.getDeclarations()?.[0];
const declarationEstreeNode =
declarationTsNode !== undefined
? tsTools.service.tsNodeToESTreeNodeMap.get(declarationTsNode)
: undefined;
context.report({
node: declarationEstreeNode ?? node,
messageId: 'nonPrefixedFunction'
});
}
}
}
};
}
});
function getPropsType(node: CallExpression, tsTools: TSTools): Type | undefined {
if (
node.callee.type !== 'Identifier' ||
node.callee.name !== '$props' ||
node.parent.type !== 'VariableDeclarator'
) {
return undefined;
}
const tsNode = tsTools.service.esTreeNodeToTSNodeMap.get(node.parent.id);
if (tsNode === undefined) {
return undefined;
}
return tsTools.service.program.getTypeChecker().getTypeAtLocation(tsNode);
}
function isFunctionLike(functionSymbol: Symbol): boolean {
return (
(functionSymbol.getFlags() & SymbolFlags.Method) !== 0 ||
(functionSymbol.valueDeclaration?.kind === SyntaxKind.PropertySignature &&
(functionSymbol.valueDeclaration as PropertySignature).type?.kind === SyntaxKind.FunctionType)
);
}
function isFunctionAsync(functionSymbol: Symbol): boolean {
return (
functionSymbol.getDeclarations()?.some((declaration) => {
if (declaration.kind !== SyntaxKind.MethodSignature) {
return false;
}
const declarationType = (declaration as MethodSignature).type;
if (declarationType?.kind !== SyntaxKind.TypeReference) {
return false;
}
const declarationTypeName = (declarationType as TypeReferenceNode).typeName;
return (
declarationTypeName.kind === SyntaxKind.Identifier &&
declarationTypeName.escapedText === 'Promise'
);
}) ?? false
);
}