@sentzunhat/zacatl
Version:
A modular, high-performance TypeScript microservice framework for Node.js, featuring layered architecture, dependency injection, and robust validation for building scalable APIs and distributed systems.
223 lines (208 loc) • 6.53 kB
JavaScript
/**
* ESLint naming convention rules for Zacatl ports/adapters.
*
* @returns Flat config object; no side effects.
*
* @example
* import { namingConventions } from "@sentzunhat/zacatl/eslint";
*/
// parser, plugins, and project are all provided by baseConfig (tseslint.configs.recommended).
// naming-convention is an AST-based rule; it does not need type information.
const namingConventionsConfig = {
files: ['src/**/*.ts'],
rules: {
'@typescript-eslint/naming-convention': [
'warn',
// Type Aliases Naming Convention:
// - PascalCase for all type aliases
// - Common suffixes: Input, Output, Config, Options, Type, Document, Schema, Handler
// - No prefix patterns (avoid T-prefix, I-prefix)
// - Examples: CreateUserInput, UserOutput, ServerConfig, RouteHandler
// Note: Suffix requirements are intentionally flexible to allow domain-specific naming
// Exported interfaces - allow plain PascalCase names (no forced 'Port' suffix)
{
selector: 'interface',
modifiers: ['exported'],
format: ['PascalCase'],
},
// Configuration interfaces and data structures (no Port suffix)
{
selector: 'interface',
modifiers: ['exported'],
format: ['PascalCase'],
suffix: ['Config', 'Options', 'Args', 'Info', 'Instance'],
filter: {
match: true,
regex: '(Config|Options|Args|Info|Instance)$',
},
},
// Error interfaces (Args suffix for error arguments)
{
selector: 'interface',
modifiers: ['exported'],
format: ['PascalCase'],
suffix: ['Args'],
filter: {
match: true,
regex: 'Error(s)?Args$',
},
},
// Reject I-prefix pattern (anti-pattern)
{
selector: 'interface',
format: ['PascalCase'],
custom: {
match: false,
regex: '^I[A-Z]',
},
},
// Private/internal interfaces (allow more flexibility)
{
selector: 'interface',
modifiers: [],
format: ['PascalCase'],
},
// Classes (Adapters) - Hexagonal Architecture implementations
// Adapters implementing Ports must have "Adapter" suffix
{
selector: 'class',
modifiers: ['exported'],
format: ['PascalCase'],
suffix: ['Adapter'],
filter: {
match: true,
regex:
'(Fastify|Express|Mongoose|Sequelize|Pino|Console|Filesystem|Memory|Logger|Server|ORM|I18n|JSON|YAML|Config.*Loader).*Adapter$',
},
},
// Abstract classes - allow Abstract prefix
{
selector: 'class',
modifiers: ['abstract', 'exported'],
format: ['PascalCase'],
prefix: ['Abstract', 'Base'],
},
// Error classes - require Error suffix
{
selector: 'class',
modifiers: ['exported'],
format: ['PascalCase'],
suffix: ['Error'],
filter: {
match: true,
regex: 'Error$',
},
},
// Service/Application/Infrastructure/Domain layers
{
selector: 'class',
modifiers: ['exported'],
format: ['PascalCase'],
filter: {
match: true,
regex:
'^(Service|Application|Infrastructure|Domain|CLI|Desktop|Server|PageModule|Provider)$',
},
},
// Route handlers - require Handler suffix
{
selector: 'class',
modifiers: ['exported'],
format: ['PascalCase'],
suffix: ['Handler'],
filter: {
match: true,
regex: 'Handler$',
},
},
// All other exported classes must follow PascalCase
{
selector: 'class',
format: ['PascalCase'],
},
// Public methods - camelCase (Verb+Noun pattern allowed)
{
selector: ['method'],
modifiers: ['public'],
format: ['camelCase'],
leadingUnderscore: 'allow',
filter: {
match: false,
regex: '^(constructor|readonly)',
},
},
// Private methods - camelCase
{
selector: ['method'],
modifiers: ['private'],
format: ['camelCase'],
filter: {
match: false,
regex: '^(constructor|readonly)',
},
},
// Protected methods - same as public
{
selector: ['method'],
modifiers: ['protected'],
format: ['camelCase'],
},
// Function declarations - camelCase
{
selector: 'function',
format: ['camelCase'],
},
// Variables and Parameters - camelCase
{
selector: ['variable', 'parameter'],
format: ['camelCase'],
leadingUnderscore: 'allow',
trailingUnderscore: 'allow',
},
// Constants - camelCase or UPPER_CASE
{
selector: ['variable'],
modifiers: ['const'],
format: ['camelCase', 'UPPER_CASE'],
},
// Object properties - camelCase (allows underscores for destructured privates)
{
selector: 'objectLiteralProperty',
format: ['camelCase', 'PascalCase', 'UPPER_CASE'],
leadingUnderscore: 'allow',
trailingUnderscore: 'allow',
filter: {
match: false,
// Allow pure numeric keys (e.g. 200) and dotted keys consisting of
// alphanumeric segments (e.g. "diego.beltran.is.best" or
// "device.browser.fingerprint"). This keeps the rule strict for
// normal identifiers while permitting status codes and dot-delimited
// keys used as literal property names.
regex: '(^\\d+$|^[A-Za-z0-9]+(\\.[A-Za-z0-9]+)+$)',
},
},
// Enum members - UPPER_CASE
{
selector: 'enumMember',
format: ['UPPER_CASE', 'PascalCase'],
},
// Type parameters (generics) - single uppercase letters or PascalCase
{
selector: 'typeParameter',
format: ['PascalCase'],
},
],
// Additional rules to support method patterns
'@typescript-eslint/explicit-function-return-type': [
'warn',
{
allowExpressions: true,
allowTypedFunctionExpressions: true,
allowHigherOrderFunctions: true,
},
],
// Enforce consistent naming in imports (core strict rules are provided by `strict.mjs`)
},
};
export default namingConventionsConfig;
export { namingConventionsConfig };