eslint-plugin-svelte
Version:
ESLint plugin for Svelte using AST
94 lines (93 loc) • 3.73 kB
JavaScript
import { createRule } from '../utils/index.js';
import { getTypeScriptTools, isMethodSymbol, isPropertySignatureKind, isFunctionTypeKind, isMethodSignatureKind, isTypeReferenceKind, isIdentifierKind } from '../utils/ts-utils/index.js';
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, tsTools) &&
!property.getName().startsWith('on') &&
(checkAsyncFunctions || !isFunctionAsync(property, tsTools))) {
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, tsTools) {
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, tsTools) {
return (isMethodSymbol(functionSymbol, tsTools.ts) ||
(functionSymbol.valueDeclaration !== undefined &&
isPropertySignatureKind(functionSymbol.valueDeclaration, tsTools.ts) &&
functionSymbol.valueDeclaration.type !== undefined &&
isFunctionTypeKind(functionSymbol.valueDeclaration.type, tsTools.ts)));
}
function isFunctionAsync(functionSymbol, tsTools) {
return (functionSymbol.getDeclarations()?.some((declaration) => {
if (!isMethodSignatureKind(declaration, tsTools.ts)) {
return false;
}
if (declaration.type === undefined || !isTypeReferenceKind(declaration.type, tsTools.ts)) {
return false;
}
return (isIdentifierKind(declaration.type.typeName, tsTools.ts) &&
declaration.type.typeName.escapedText === 'Promise');
}) ?? false);
}