/** * ESLint rule to enforce filename matches exported name * * The filename (without extension) should match the exported name exactly. * For example: AdminDashboardPageQuery.ts should export AdminDashboardPageQuery */ module.exports = { meta: { type: 'problem', docs: { description: 'Enforce filename matches exported name', category: 'Best Practices', recommended: true, }, fixable: null, schema: [], messages: { filenameMismatch: 'Filename "{{filename}}" should match exported name "{{exportName}}"', noMatchFound: 'No export found that matches filename "{{filename}}"', }, }, create(context) { const filename = context.getFilename(); // Extract base filename without extension const baseName = filename.split('/').pop(); // Get last part of path const nameWithoutExt = baseName?.replace(/\.(ts|tsx|js|jsx)$/, ''); // Skip test files, index files, and type definition files if (!nameWithoutExt || nameWithoutExt.endsWith('.test') || nameWithoutExt.endsWith('.spec') || nameWithoutExt === 'index' || nameWithoutExt.endsWith('.d')) { return {}; } let exportedName = null; return { // Track named exports ExportNamedDeclaration(node) { if (node.declaration) { if (node.declaration.id) { exportedName = node.declaration.id.name; } else if (node.declaration.declarations) { // Multiple const exports - use the first one const first = node.declaration.declarations[0]; if (first.id && first.id.name) { exportedName = first.id.name; } } } else if (node.specifiers && node.specifiers.length > 0) { // Re-exports - use the first one exportedName = node.specifiers[0].exported.name; } }, // Track default exports ExportDefaultDeclaration(node) { if (node.declaration && node.declaration.id) { exportedName = node.declaration.id.name; } else { exportedName = 'default'; } }, 'Program:exit'() { if (!exportedName) { // No export found - this might be okay for some files return; } if (exportedName === 'default') { // Default exports don't need to match filename return; } if (exportedName !== nameWithoutExt) { context.report({ node: context.getSourceCode().ast, messageId: 'filenameMismatch', data: { filename: nameWithoutExt, exportName: exportedName, }, }); } }, }; }, };