website refactor

This commit is contained in:
2026-01-14 02:02:24 +01:00
parent 8d7c709e0c
commit 4522d41aef
291 changed files with 12763 additions and 9309 deletions

View File

@@ -140,63 +140,122 @@ module.exports = {
},
},
create(context) {
// Helper to recursively check if type contains ViewData
function typeContainsViewData(typeNode) {
if (!typeNode) return false;
// Check direct type name
if (typeNode.type === 'TSTypeReference' &&
typeNode.typeName &&
typeNode.typeName.name &&
typeNode.typeName.name.includes('ViewData')) {
return true;
}
// Check nested in object type
if (typeNode.type === 'TSTypeLiteral' && typeNode.members) {
for (const member of typeNode.members) {
if (member.type === 'TSPropertySignature' &&
member.typeAnnotation &&
typeContainsViewData(member.typeAnnotation.typeAnnotation)) {
return true;
const sourceCode = context.getSourceCode();
function isTemplateExportFunction(node) {
const functionName = node.id && node.id.type === 'Identifier' ? node.id.name : null;
if (!functionName || !functionName.endsWith('Template')) return false;
// Only enforce for exported template component functions.
return node.parent && node.parent.type === 'ExportNamedDeclaration';
}
function getTypeReferenceName(typeNode) {
if (!typeNode || typeNode.type !== 'TSTypeReference') return null;
const typeName = typeNode.typeName;
if (!typeName || typeName.type !== 'Identifier') return null;
return typeName.name;
}
function findLocalTypeDeclaration(typeName) {
const programBody = sourceCode.ast && sourceCode.ast.body ? sourceCode.ast.body : [];
for (const stmt of programBody) {
if (!stmt) continue;
// interface Foo { ... }
if (stmt.type === 'TSInterfaceDeclaration' && stmt.id && stmt.id.type === 'Identifier') {
if (stmt.id.name === typeName) return stmt;
}
// type Foo = ...
if (stmt.type === 'TSTypeAliasDeclaration' && stmt.id && stmt.id.type === 'Identifier') {
if (stmt.id.name === typeName) return stmt;
}
// export interface/type Foo ...
if (stmt.type === 'ExportNamedDeclaration' && stmt.declaration) {
const decl = stmt.declaration;
if (decl.type === 'TSInterfaceDeclaration' && decl.id && decl.id.type === 'Identifier' && decl.id.name === typeName) {
return decl;
}
if (decl.type === 'TSTypeAliasDeclaration' && decl.id && decl.id.type === 'Identifier' && decl.id.name === typeName) {
return decl;
}
}
}
// Check union/intersection types
if (typeNode.type === 'TSUnionType' || typeNode.type === 'TSIntersectionType') {
return typeNode.types.some(t => typeContainsViewData(t));
return null;
}
// Helper to recursively check if type contains ViewData (including by resolving local interface/type aliases).
function typeContainsViewData(typeNode, seenTypeNames = new Set()) {
if (!typeNode) return false;
// Direct type name includes ViewData (e.g. ProfileViewData)
if (typeNode.type === 'TSTypeReference' && typeNode.typeName && typeNode.typeName.type === 'Identifier') {
const name = typeNode.typeName.name;
if (name.includes('ViewData')) return true;
// If the param is typed as a local Props interface/type, resolve it and inspect its members.
if (!seenTypeNames.has(name)) {
seenTypeNames.add(name);
const decl = findLocalTypeDeclaration(name);
if (decl && decl.type === 'TSInterfaceDeclaration') {
for (const member of decl.body.body || []) {
if (member.type === 'TSPropertySignature' && member.typeAnnotation) {
if (typeContainsViewData(member.typeAnnotation.typeAnnotation, seenTypeNames)) return true;
}
}
}
if (decl && decl.type === 'TSTypeAliasDeclaration') {
if (typeContainsViewData(decl.typeAnnotation, seenTypeNames)) return true;
}
}
}
// Nested in object type
if (typeNode.type === 'TSTypeLiteral' && typeNode.members) {
for (const member of typeNode.members) {
if (member.type === 'TSPropertySignature' && member.typeAnnotation) {
if (typeContainsViewData(member.typeAnnotation.typeAnnotation, seenTypeNames)) return true;
}
}
}
// Union/intersection types
if (typeNode.type === 'TSUnionType' || typeNode.type === 'TSIntersectionType') {
return typeNode.types.some((t) => typeContainsViewData(t, seenTypeNames));
}
return false;
}
return {
FunctionDeclaration(node) {
if (!isTemplateExportFunction(node)) return;
if (node.params.length === 0) {
context.report({
node,
messageId: 'message',
});
context.report({ node, messageId: 'message' });
return;
}
const firstParam = node.params[0];
// `function FooTemplate({ ... }: Props)` -> the type annotation is on the parameter, not on the destructured properties.
if (!firstParam.typeAnnotation || !firstParam.typeAnnotation.typeAnnotation) {
context.report({
node,
messageId: 'message',
});
context.report({ node, messageId: 'message' });
return;
}
const typeAnnotation = firstParam.typeAnnotation.typeAnnotation;
if (!typeContainsViewData(typeAnnotation)) {
context.report({
node,
messageId: 'message',
});
const firstParamType = firstParam.typeAnnotation.typeAnnotation;
// If it's a reference to a local type/interface, we handle it in typeContainsViewData().
const refName = getTypeReferenceName(firstParamType);
if (refName && refName.includes('ViewData')) return;
if (!typeContainsViewData(firstParamType)) {
context.report({ node, messageId: 'message' });
}
},
};