import nx from '@nx/eslint-plugin'; import { maxLinesBaseline } from './tools/eslint/max-lines-baseline.mjs'; import { MAX_LINES_OPTIONS, MAX_LINES_PROD, MAX_LINES_TEST, TEST_FILE_GLOBS, } from './tools/eslint/max-lines-config.mjs'; const legacyBareAliases = [ 'components', 'm3u-state', 'm3u-utils', 'services', 'shared-interfaces', 'shared-portals', 'remote-control', 'database', 'database-schema', 'database-path-utils', 'workspace-dashboard-feature', 'workspace-dashboard-data-access', ]; export default [ ...nx.configs['flat/base'], ...nx.configs['flat/typescript'], ...nx.configs['flat/javascript'], { ignores: ['**/dist', '**/.astro'], }, { files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], rules: { '@nx/enforce-module-boundaries': [ 'error', { enforceBuildableLibDependency: true, allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'], depConstraints: [ { sourceTag: 'type:app', onlyDependOnLibsWithTags: [ 'type:feature', 'type:ui', 'type:data-access', 'type:util', ], }, { sourceTag: 'type:e2e', onlyDependOnLibsWithTags: [ 'type:feature', 'type:ui', 'type:data-access', 'type:util', ], }, { sourceTag: 'type:dev-app', onlyDependOnLibsWithTags: [ 'type:feature', 'type:ui', 'type:data-access', 'type:util', ], }, { sourceTag: 'type:website', onlyDependOnLibsWithTags: ['type:ui', 'type:util'], }, { sourceTag: 'type:feature', onlyDependOnLibsWithTags: [ 'type:feature', 'type:ui', 'type:data-access', 'type:util', ], }, { sourceTag: 'type:ui', onlyDependOnLibsWithTags: [ 'type:ui', 'type:data-access', 'type:util', ], }, { sourceTag: 'type:data-access', onlyDependOnLibsWithTags: [ 'type:data-access', 'type:util', ], }, { sourceTag: 'type:util', onlyDependOnLibsWithTags: ['type:util'], }, { sourceTag: 'domain:portal-shared', onlyDependOnLibsWithTags: [ 'domain:portal-shared', 'domain:m3u', 'domain:playback', 'domain:shared-contracts', 'domain:shared-runtime', 'domain:shared-ui', 'scope:shared', 'scope:portal', ], }, { sourceTag: 'domain:m3u', onlyDependOnLibsWithTags: [ 'domain:m3u', 'domain:playback', 'domain:portal-shared', 'domain:shared-contracts', 'domain:shared-runtime', 'domain:shared-ui', 'scope:shared', ], }, { sourceTag: 'domain:playlist-import', onlyDependOnLibsWithTags: [ 'domain:m3u', 'domain:playlist-import', 'domain:playback', 'domain:shared-contracts', 'domain:shared-runtime', 'domain:shared-ui', 'domain:stalker', 'scope:shared', ], }, { sourceTag: 'domain:playback', onlyDependOnLibsWithTags: [ 'domain:m3u', 'domain:playback', 'domain:portal-shared', 'domain:shared-contracts', 'domain:shared-runtime', 'domain:shared-ui', 'scope:shared', ], }, { sourceTag: 'domain:shared-contracts', onlyDependOnLibsWithTags: [ 'domain:shared-contracts', 'scope:shared', ], }, { sourceTag: 'domain:shared-runtime', onlyDependOnLibsWithTags: [ 'domain:m3u', 'domain:shared-contracts', 'domain:shared-runtime', 'scope:shared', ], }, { sourceTag: 'domain:shared-ui', onlyDependOnLibsWithTags: [ 'domain:m3u', 'domain:playback', 'domain:portal-shared', 'domain:shared-contracts', 'domain:shared-runtime', 'domain:shared-ui', 'scope:shared', ], }, { sourceTag: 'domain:xtream', onlyDependOnLibsWithTags: [ 'domain:xtream', 'domain:portal-shared', 'domain:m3u', 'domain:playback', 'domain:shared-contracts', 'domain:shared-runtime', 'domain:shared-ui', 'scope:shared', ], }, { sourceTag: 'domain:stalker', onlyDependOnLibsWithTags: [ 'domain:stalker', 'domain:portal-shared', 'domain:m3u', 'domain:playback', 'domain:shared-contracts', 'domain:shared-runtime', 'domain:shared-ui', 'scope:shared', ], }, { sourceTag: 'domain:workspace', onlyDependOnLibsWithTags: [ 'domain:workspace', 'domain:portal-shared', 'domain:xtream', 'domain:stalker', 'domain:m3u', 'domain:playback', 'domain:shared-contracts', 'domain:shared-runtime', 'domain:shared-ui', 'scope:shared', ], }, ], }, ], 'no-restricted-imports': [ 'error', { paths: legacyBareAliases.map((name) => ({ name, message: 'Use the scoped @iptvnator/* path alias instead of the legacy bare alias.', })), patterns: legacyBareAliases.map((name) => ({ group: [`${name}/*`], message: 'Use the scoped @iptvnator/* path alias instead of the legacy bare alias.', })), }, ], }, }, { files: [ '**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts', '**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs', ], // Override or add rules here rules: { '@angular-eslint/template/click-events-have-key-events': 'off', '@angular-eslint/template/interactive-supports-focus': 'off', }, }, { // CLAUDE.md file-size rule for production code: target under 300 lines, // hard maximum 400. Files that predate the rule are baselined below. files: ['**/*.ts', '**/*.tsx'], ignores: TEST_FILE_GLOBS, rules: { 'max-lines': [ 'error', { max: MAX_LINES_PROD, ...MAX_LINES_OPTIONS }, ], }, }, { // Tests get a much higher ceiling — see tools/eslint/max-lines-config.mjs // for why a long spec is not the same signal as a long component. files: TEST_FILE_GLOBS, rules: { 'max-lines': [ 'error', { max: MAX_LINES_TEST, ...MAX_LINES_OPTIONS }, ], }, }, { files: maxLinesBaseline, rules: { 'max-lines': 'off', }, }, { files: [ 'apps/web/src/app/app.component.ts', 'apps/electron-backend/src/app/events/player.events.ts', 'libs/epg/data-access/**/*.ts', 'libs/ui/playback/**/*.ts', ], rules: { 'no-console': ['warn', { allow: ['debug', 'warn', 'error'] }], }, }, ];