UNPKG

eslint-plugin-filename-export

Version:

ESLint plugin that ensures non-index filenames match to at least one named export.

163 lines 6.92 kB
"use strict"; const utils_1 = require("@typescript-eslint/utils"); const node_path_1 = require("node:path"); const createRule = utils_1.ESLintUtils.RuleCreator(() => 'https://github.com/ekwoka/eslint-plugin-filename-export'); const getName = (exported) => { if ('name' in exported) return exported.name; if ('id' in exported && exported.id) if ('name' in exported.id) return exported.id.name; else if ('value' in exported.id) return exported.id.value; return null; }; const getExportedNames = (exported) => { if (exported.declaration) return getNamesFromDeclarations(exported.declaration); if (exported.specifiers) return getNamesFromSpecifiers(exported.specifiers); return []; }; const getNamesFromDeclarations = (declaration) => { var _a; if ('declarations' in declaration && declaration.declarations) return declaration.declarations.map((declaration) => { var _a; return (_a = getName(declaration)) !== null && _a !== void 0 ? _a : ''; }); return [(_a = getName(declaration)) !== null && _a !== void 0 ? _a : '']; }; const getNamesFromSpecifiers = (specifiers) => specifiers.map((specifier) => specifier.exported.name); const compare = (names, transformers) => { const [name, filename] = names.map((string) => transformers.reduce((acc, fn) => fn(acc), string)); return name === filename; }; const makeTransformers = (options) => { const transformers = []; if (options.stripextra) transformers.push((name) => name.replace(/[^a-zA-Z0-9]/g, '')); if (options.casing !== 'strict') transformers.push((name) => name.toLowerCase()); return transformers; }; module.exports = { rules: { 'match-named-export': createRule({ name: 'match-named-export', defaultOptions: [ { stripextra: false, casing: 'loose', }, ], meta: { docs: { description: 'Enforce filename matches named export', recommended: 'error', }, messages: { noMatchingExport: 'Filename does not match any named exports', }, type: 'suggestion', schema: [ { type: 'object', properties: { stripextra: { type: 'boolean', }, casing: { enum: ['strict', 'loose'], }, }, }, ], }, create(context) { var _a; const transformers = makeTransformers((_a = context.options[0]) !== null && _a !== void 0 ? _a : {}); return { Program(node) { const filename = context.getFilename(); const filenameSansExt = (0, node_path_1.basename)(filename, (0, node_path_1.extname)(filename)); if (['index', 'types'].includes(filenameSansExt) || /\.(test|spec|stories)$/.test(filenameSansExt)) return; if (node.body.find((item) => item.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration)) return; const namedExports = node.body.filter((item) => item.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration); if (!namedExports.length) return; const matchingExport = namedExports .flatMap((exp) => getExportedNames(exp)) .some((name) => compare([name, filenameSansExt], transformers)); if (!matchingExport) context.report({ messageId: 'noMatchingExport', node, }); }, }; }, }), 'match-default-export': createRule({ name: 'match-default-export', defaultOptions: [ { stripextra: false, casing: 'loose', }, ], meta: { docs: { description: 'Enforce filename matches named export', recommended: 'error', }, messages: { defaultExportDoesNotMatch: 'Filename does not match the default export', }, type: 'suggestion', schema: [ { type: 'object', properties: { stripextra: { type: 'boolean', }, casing: { enum: ['strict', 'loose'], }, }, }, ], }, create(context) { var _a; const transformers = makeTransformers((_a = context.options[0]) !== null && _a !== void 0 ? _a : {}); return { Program(node) { const filename = context.getFilename(); const filenameSansExt = (0, node_path_1.basename)(filename, (0, node_path_1.extname)(filename)); if (['index', 'types'].includes(filenameSansExt) || /\.(test|spec|stories)$/.test(filenameSansExt)) return; const defaultExport = node.body.find((item) => item.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration); if (!defaultExport) return; const declaration = defaultExport.declaration; if (!('name' in declaration || 'id' in declaration)) return; const defaultName = getName(declaration); if (!defaultName) return; const isMatching = compare([defaultName, filenameSansExt], transformers); if (!isMatching) context.report({ messageId: 'defaultExportDoesNotMatch', node, }); }, }; }, }), }, }; //# sourceMappingURL=index.js.map