super-productivity/eslint.config.js
Johannes Millan cbb4e28ec4
build(lint): enforce Log helpers over console.* in src/app (#8239)
* build(lint): enforce Log helpers over console.* in src/app

Adds an ESLint no-console rule scoped to src/app (exempting log.ts,
*.spec.ts, and *.benchmark.ts) and migrates the remaining console.*
call sites to use Log / TaskLog / SyncLog / IssueLog / PluginLog so
their output is captured in the exportable log history users attach to
bug reports.

* test: spy on Log helpers instead of console after no-console migration

* fix(trello): keep username out of exportable log history

* test: derive today expectation from mocked DateService date
2026-06-10 13:08:17 +02:00

251 lines
7.7 KiB
JavaScript

// @ts-check
const tseslint = require('typescript-eslint');
const angular = require('angular-eslint');
const prettierRecommended = require('eslint-plugin-prettier/recommended');
const preferArrow = require('eslint-plugin-prefer-arrow');
const localRules = require('eslint-plugin-local-rules');
module.exports = tseslint.config(
// Global ignores
{
ignores: [
'app-builds/**/*',
'dist/**',
'node_modules/**/*',
'src/app/t.const.ts',
'src/assets/bundled-plugins/**/*',
'src/app/config/env.generated.ts',
'.tmp/**/*',
'packages/plugin-api/**/*',
'packages/plugin-dev/**/*',
'packages/shared-schema/**/*',
'packages/super-sync-server/**/*',
'packages/vite-plugin/**/*',
'packages/*/dist/**/*',
],
},
// TypeScript files
{
files: ['**/*.ts'],
extends: [
...tseslint.configs.recommended,
...angular.configs.tsRecommended,
prettierRecommended,
],
processor: angular.processInlineTemplates,
plugins: {
'prefer-arrow': preferArrow,
},
languageOptions: {
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
},
rules: {
// Disabled rules
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@angular-eslint/component-selector': 'off',
'@angular-eslint/no-input-rename': 'off',
'@typescript-eslint/no-inferrable-types': 'off',
'no-underscore-dangle': 'off',
'arrow-body-style': 'off',
'@typescript-eslint/member-ordering': 'off',
'import/order': 'off',
'arrow-parens': 'off',
'@typescript-eslint/explicit-member-accessibility': 'off',
// Enabled rules
'prettier/prettier': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{ args: 'none', caughtErrors: 'none' },
],
'@typescript-eslint/explicit-function-return-type': [
'error',
{
allowExpressions: true,
allowTypedFunctionExpressions: true,
allowHigherOrderFunctions: true,
allowDirectConstAssertionInArrowFunctions: true,
allowConciseArrowFunctionExpressionsStartingWithVoid: true,
},
],
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'default',
format: ['camelCase', 'snake_case', 'UPPER_CASE', 'PascalCase'],
leadingUnderscore: 'allowSingleOrDouble',
trailingUnderscore: 'allow',
filter: { regex: '(should)|@tags', match: false },
},
{
selector: 'variable',
format: ['camelCase', 'snake_case', 'UPPER_CASE', 'PascalCase'],
leadingUnderscore: 'allowSingleOrDouble',
trailingUnderscore: 'allow',
},
{ selector: 'enum', format: ['PascalCase', 'UPPER_CASE'] },
{ selector: 'typeLike', format: ['PascalCase'] },
],
'prefer-const': 'error',
'@typescript-eslint/no-unused-expressions': 'error',
'@typescript-eslint/no-empty-object-type': 'error',
'max-len': [
'error',
{
ignorePattern: '^import \\{.+;$',
ignoreRegExpLiterals: true,
ignoreStrings: true,
ignoreUrls: true,
code: 150,
},
],
'id-blacklist': 'error',
// @typescript-eslint/member-delimiter-style removed in v8 - Prettier handles this
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
'comma-dangle': ['error', 'always-multiline'],
'no-mixed-operators': 'error',
'prefer-arrow/prefer-arrow-functions': 'error',
'@angular-eslint/directive-selector': [
'error',
{ type: 'attribute', prefix: '', style: 'camelCase' },
],
// @typescript-eslint/ban-types replaced by specific rules in v8
'@typescript-eslint/no-unsafe-function-type': 'error',
'@typescript-eslint/no-wrapper-object-types': 'error',
},
},
{
files: ['packages/sync-core/**/*.ts'],
rules: {
'no-restricted-imports': [
'error',
{
paths: [
{
name: '@sp/shared-schema',
message:
'@sp/sync-core must stay domain-agnostic; shared-schema is SP-specific.',
},
],
patterns: [
{
group: [
'@angular/*',
'@ngrx/*',
'@sp/shared-schema/*',
'../shared-schema/*',
'../shared-schema/**',
'../../shared-schema/*',
'../../shared-schema/**',
'**/shared-schema/*',
'**/shared-schema/**',
'src/app/*',
'src/app/**',
'**/src/app/*',
'**/src/app/**',
],
message:
'@sp/sync-core must not import Angular, NgRx, app code, or SP-specific schema packages.',
},
],
},
],
'no-restricted-syntax': [
'error',
{
selector: 'ImportExpression',
message:
'@sp/sync-core must not use dynamic imports; they bypass package-boundary checks.',
},
],
},
},
{
files: ['packages/sync-providers/**/*.ts'],
rules: {
'no-restricted-imports': [
'error',
{
paths: [
{
name: '@sp/shared-schema',
message: '@sp/sync-providers must not import SP-specific schema packages.',
},
],
patterns: [
{
group: [
'@angular/*',
'@ngrx/*',
'@sp/shared-schema/*',
'@sp/sync-core/*',
'**/shared-schema/*',
'**/shared-schema/**',
'**/sync-core/*',
'**/sync-core/**',
'src/app/*',
'src/app/**',
'**/src/app/*',
'**/src/app/**',
],
message:
'@sp/sync-providers must use only public @sp/sync-core exports and must not import Angular, NgRx, app code, or SP-specific schema packages.',
},
],
},
],
'no-restricted-syntax': [
'error',
{
selector: 'ImportExpression',
message:
'@sp/sync-providers must not use dynamic imports; they bypass package-boundary checks.',
},
],
},
},
// NgRx effects files - require hydration guards on selector-based effects
{
files: ['**/*.effects.ts'],
plugins: {
'local-rules': localRules,
},
rules: {
'local-rules/require-hydration-guard': 'error',
'local-rules/require-entity-registry': 'warn',
'local-rules/no-actions-in-effects': 'error',
'local-rules/no-multi-entity-effect': 'warn',
},
},
// App code must route logging through Log/SyncLog/OpLog/... helpers.
// Direct console.* calls bypass the exportable log history users attach
// to bug reports. The Log implementation itself, tests, and benchmarks
// (which intentionally dump timing numbers to stdout) are exempt.
{
files: ['src/app/**/*.ts'],
ignores: [
'src/app/**/*.spec.ts',
'src/app/**/*.benchmark.ts',
'src/app/core/log.ts',
],
rules: {
'no-console': 'error',
},
},
// HTML files
{
files: ['**/*.html'],
extends: [...angular.configs.templateRecommended, prettierRecommended],
rules: {
'@angular-eslint/template/no-negated-async': 'off',
'prettier/prettier': 'error',
},
},
);