Update plugins to support standalone components

- ThemedComponent wrappers should always import their base component. This ensures that it's always enough to only import the wrapper when we use it.
- This implies that all themeable components must be standalone

→ added rules to enforce this
→ updated usage rule to improve declaration/import handling
This commit is contained in:
Yury Bondarenko
2024-03-21 10:37:20 +01:00
parent 568574585b
commit e40b6ae612
14 changed files with 835 additions and 55 deletions

View File

@@ -10,8 +10,7 @@ import { TSESTree } from '@typescript-eslint/utils';
import { getObjectPropertyNodeByName } from './typescript';
export function getComponentSelectorNode(componentDecoratorNode: TSESTree.Decorator): TSESTree.StringLiteral | undefined {
const initializer = (componentDecoratorNode.expression as TSESTree.CallExpression).arguments[0] as TSESTree.ObjectExpression;
const property = getObjectPropertyNodeByName(initializer, 'selector');
const property = getComponentInitializerNodeByName(componentDecoratorNode, 'selector');
if (property !== undefined) {
// todo: support template literals as well
@@ -23,6 +22,62 @@ export function getComponentSelectorNode(componentDecoratorNode: TSESTree.Decora
return undefined;
}
export function getComponentStandaloneNode(componentDecoratorNode: TSESTree.Decorator): TSESTree.BooleanLiteral | undefined {
const property = getComponentInitializerNodeByName(componentDecoratorNode, 'standalone');
if (property !== undefined) {
if (property.type === TSESTree.AST_NODE_TYPES.Literal && typeof property.value === 'boolean') {
return property as TSESTree.BooleanLiteral;
}
}
return undefined;
}
export function getComponentImportNode(componentDecoratorNode: TSESTree.Decorator): TSESTree.ArrayExpression | undefined {
const property = getComponentInitializerNodeByName(componentDecoratorNode, 'imports');
if (property !== undefined) {
if (property.type === TSESTree.AST_NODE_TYPES.ArrayExpression) {
return property as TSESTree.ArrayExpression;
}
}
return undefined;
}
export function getComponentClassName(decoratorNode: TSESTree.Decorator): string | undefined {
if (decoratorNode.parent.type !== TSESTree.AST_NODE_TYPES.ClassDeclaration) {
return undefined;
}
if (decoratorNode.parent.id?.type !== TSESTree.AST_NODE_TYPES.Identifier) {
return undefined;
}
return decoratorNode.parent.id.name;
}
export function getComponentSuperClassName(decoratorNode: TSESTree.Decorator): string | undefined {
if (decoratorNode.parent.type !== TSESTree.AST_NODE_TYPES.ClassDeclaration) {
return undefined;
}
if (decoratorNode.parent.superClass?.type !== TSESTree.AST_NODE_TYPES.Identifier) {
return undefined;
}
return decoratorNode.parent.superClass.name;
}
export function getComponentInitializer(componentDecoratorNode: TSESTree.Decorator): TSESTree.ObjectExpression {
return (componentDecoratorNode.expression as TSESTree.CallExpression).arguments[0] as TSESTree.ObjectExpression;
}
export function getComponentInitializerNodeByName(componentDecoratorNode: TSESTree.Decorator, name: string): TSESTree.Node | undefined {
const initializer = getComponentInitializer(componentDecoratorNode);
return getObjectPropertyNodeByName(initializer, name);
}
export function isPartOfViewChild(node: TSESTree.Identifier): boolean {
return (node.parent as any)?.callee?.name === 'ViewChild';
}