diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 9102c4719b7..00000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,387 +0,0 @@ -{ - "root": true, - "plugins": [ - "@typescript-eslint", - "@angular-eslint/eslint-plugin", - "eslint-plugin-import", - "eslint-plugin-jsdoc", - "unused-imports", - "eslint-plugin-lodash", - "eslint-plugin-jsonc", - "@smarttools/rxjs", - "eslint-plugin-simple-import-sort", - "eslint-plugin-import-newlines", - "@stylistic", - "dspace-angular-ts", - "dspace-angular-html" - ], - "ignorePatterns": [ - "lint/test/fixture" - ], - "overrides": [ - { - "files": [ - "*.ts" - ], - "parserOptions": { - "project": [ - "./tsconfig.json", - "./cypress/tsconfig.json", - "./lint/tsconfig.json" - ], - "createDefaultProgram": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - "plugin:@angular-eslint/recommended", - "plugin:@angular-eslint/template/process-inline-templates", - "plugin:@smarttools/rxjs/recommended-legacy" - ], - "rules": { - "indent": [ - "error", - 2, - { - "SwitchCase": 1, - "ignoredNodes": [ - "ClassBody.body > PropertyDefinition[decorators.length > 0] > .key" - ] - } - ], - "max-classes-per-file": [ - "error", - 1 - ], - "comma-dangle": [ - "error", - "always-multiline" - ], - "object-curly-spacing": [ - "error", - "always" - ], - "eol-last": [ - "error", - "always" - ], - "no-console": [ - "error", - { - "allow": [ - "warn", - "dir", - "timeLog", - "assert", - "clear", - "count", - "countReset", - "group", - "groupEnd", - "table", - "debug", - "info", - "dirxml", - "error", - "groupCollapsed", - "Console", - "profile", - "profileEnd", - "timeStamp", - "context" - ] - } - ], - "curly": "error", - "brace-style": [ - "error", - "1tbs", - { - "allowSingleLine": true - } - ], - "eqeqeq": [ - "error", - "always", - { - "null": "ignore" - } - ], - "radix": "error", - "guard-for-in": "error", - "no-bitwise": "error", - "no-restricted-imports": "error", - "no-caller": "error", - "no-debugger": "error", - "no-redeclare": "error", - "no-eval": "error", - "no-fallthrough": "error", - "no-trailing-spaces": "error", - "space-infix-ops": "error", - "keyword-spacing": "error", - "no-var": "error", - "no-unused-expressions": [ - "error", - { - "allowTernary": true - } - ], - "prefer-const": "error", - "no-case-declarations": "error", - "no-extra-boolean-cast": "error", - "prefer-spread": "off", - "no-underscore-dangle": "off", - "no-prototype-builtins": "off", - "no-useless-escape": "off", - - "@angular-eslint/directive-selector": [ - "error", - { - "type": "attribute", - "prefix": "ds", - "style": "camelCase" - } - ], - "@angular-eslint/component-selector": [ - "error", - { - "type": "element", - "prefix": "ds", - "style": "kebab-case" - } - ], - "@angular-eslint/pipe-prefix": [ - "error", - { - "prefixes": [ - "ds" - ] - } - ], - "@angular-eslint/prefer-standalone": [ - "error" - ], - "@angular-eslint/no-attribute-decorator": "error", - "@angular-eslint/no-output-native": "warn", - "@angular-eslint/no-output-on-prefix": "warn", - "@angular-eslint/no-conflicting-lifecycle": "warn", - "@angular-eslint/use-lifecycle-interface": "error", - - "@typescript-eslint/no-inferrable-types":[ - "error", - { - "ignoreParameters": true - } - ], - "@angular-eslint/prefer-inject": "off", - "@stylistic/quotes": [ - "error", - "single", - { - "avoidEscape": true, - "allowTemplateLiterals": true - } - ], - "@stylistic/semi": "error", - "@typescript-eslint/no-shadow": "error", - "@typescript-eslint/dot-notation": "error", - "@typescript-eslint/consistent-type-definitions": "error", - "@typescript-eslint/prefer-function-type": "error", - "@typescript-eslint/naming-convention": [ - "error", - { - "selector": "property", - "format": null - } - ], - "@typescript-eslint/member-ordering": [ - "error", - { - "default": [ - "static-field", - "instance-field", - "static-method", - "instance-method" - ] - } - ], - "@stylistic/type-annotation-spacing": "error", - "@typescript-eslint/unified-signatures": "error", - "@typescript-eslint/no-restricted-types": "error", - "@typescript-eslint/no-floating-promises": "warn", - "@typescript-eslint/no-misused-promises": "warn", - "@typescript-eslint/restrict-plus-operands": "warn", - "@typescript-eslint/unbound-method": "off", - "@typescript-eslint/ban-ts-comment": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/no-unused-vars": "off", - "@typescript-eslint/no-unnecessary-type-assertion": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/no-redundant-type-constituents": "off", - "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/require-await": "off", - "@typescript-eslint/no-base-to-string": [ - "error", - { - "ignoredTypeNames": [ - "ResourceType", - "Error" - ] - } - ], - - "@typescript-eslint/no-deprecated": "warn", - - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", - "import/order": "off", - "import/first": "error", - "import/newline-after-import": "error", - "import/no-duplicates": "error", - "import/no-deprecated": "warn", - "import/no-namespace": "error", - "import-newlines/enforce": [ - "error", - { - "items": 1, - "semi": true, - "forceSingleLine": true - } - ], - "import/enforce-node-protocol-usage": [ - "error", - "always" - ], - - "unused-imports/no-unused-imports": "error", - "lodash/import-scope": [ - "error", - "method" - ], - - "@smarttools/rxjs/no-nested-subscribe": "off", // todo: go over _all_ cases - - // Custom DSpace Angular rules - "dspace-angular-ts/alias-imports": [ - "error", - { - "aliases": [ - { - "package": "rxjs", - "imported": "of", - "local": "of" - } - ] - } - ], - "dspace-angular-ts/no-default-standalone-value": "error", - "dspace-angular-ts/themed-component-selectors": "error", - "dspace-angular-ts/themed-component-usages": "error", - "dspace-angular-ts/themed-decorators": [ - "off", - { - "decorators": { - "listableObjectComponent": 3, - "rendersSectionForMenu": 2 - } - } - ], - "dspace-angular-ts/themed-wrapper-no-input-defaults": "error", - "dspace-angular-ts/unique-decorators": [ - "off", - { - "decorators": [ - "listableObjectComponent" - ] - } - ], - "dspace-angular-ts/sort-standalone-imports": [ - "error", - { - "locale": "en-US", - "maxItems": 0, - "indent": 2, - "trailingComma": true - } - ] - } - }, - { - "files": [ - "*.spec.ts" - ], - "parserOptions": { - "project": [ - "./tsconfig.json", - "./cypress/tsconfig.json" - ], - "createDefaultProgram": true - }, - "rules": { - "prefer-const": "off", - - // Custom DSpace Angular rules - "dspace-angular-ts/themed-component-usages": "error" - } - }, - { - "files": [ - "*.html" - ], - "extends": [ - "plugin:@angular-eslint/template/recommended" - ], - "rules": { - // Custom DSpace Angular rules - "dspace-angular-html/themed-component-usages": "error", - "dspace-angular-html/no-disabled-attribute-on-button": "error", - "@angular-eslint/template/prefer-control-flow": "error" - } - }, - { - "files": [ - "*.json5" - ], - "extends": [ - "plugin:jsonc/recommended-with-json5" - ], - "rules": { - // The ESLint core no-irregular-whitespace rule doesn't work well in JSON - // See: https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-irregular-whitespace.html - "no-irregular-whitespace": "off", - "jsonc/no-irregular-whitespace": "error", - "no-trailing-spaces": "error", - "jsonc/comma-dangle": [ - "error", - "always-multiline" - ], - "jsonc/indent": [ - "error", - 2 - ], - "jsonc/key-spacing": [ - "error", - { - "beforeColon": false, - "afterColon": true, - "mode": "strict" - } - ], - "jsonc/no-dupe-keys": "off", - "jsonc/quotes": [ - "error", - "double", - { - "avoidEscape": false - } - ] - } - } - ] -} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000000..696c48445b9 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,405 @@ +// @ts-check +import js from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import angular from 'angular-eslint'; +import rxjs from '@smarttools/eslint-plugin-rxjs'; +import stylistic from '@stylistic/eslint-plugin'; +import importPlugin from 'eslint-plugin-import'; +import simpleImportSort from 'eslint-plugin-simple-import-sort'; +import importNewlines from 'eslint-plugin-import-newlines'; +import unusedImports from 'eslint-plugin-unused-imports'; +import lodash from 'eslint-plugin-lodash'; +import jsdoc from 'eslint-plugin-jsdoc'; +import jsonc from 'eslint-plugin-jsonc'; +import dspaceTs from 'eslint-plugin-dspace-angular-ts'; +import dspaceHtml from 'eslint-plugin-dspace-angular-html'; + +export default tseslint.config( + // ── Global ignores (replaces ignorePatterns) ────────────── + { + ignores: ['lint/test/fixture/**'], + }, + + // ── TypeScript files ────────────────────────────────────── + { + files: ['**/*.ts'], + extends: [ + js.configs.recommended, + ...tseslint.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...angular.configs.tsRecommended, + ], + processor: angular.processInlineTemplates, + languageOptions: { + parserOptions: { + project: [ + './tsconfig.json', + './cypress/tsconfig.json', + './lint/tsconfig.json', + ], + createDefaultProgram: true, + }, + }, + plugins: { + '@stylistic': stylistic, + 'import': importPlugin, + 'simple-import-sort': simpleImportSort, + 'import-newlines': importNewlines, + 'unused-imports': unusedImports, + 'lodash': lodash, + 'jsdoc': jsdoc, + '@smarttools/rxjs': rxjs, + 'dspace-angular-ts': dspaceTs, + }, + rules: { + 'indent': [ + 'error', + 2, + { + 'SwitchCase': 1, + 'ignoredNodes': [ + 'ClassBody.body > PropertyDefinition[decorators.length > 0] > .key', + ], + }, + ], + 'max-classes-per-file': [ + 'error', + 1, + ], + 'comma-dangle': [ + 'error', + 'always-multiline', + ], + 'object-curly-spacing': [ + 'error', + 'always', + ], + 'eol-last': [ + 'error', + 'always', + ], + 'no-console': [ + 'error', + { + 'allow': [ + 'log', + 'warn', + 'dir', + 'timeLog', + 'assert', + 'clear', + 'count', + 'countReset', + 'group', + 'groupEnd', + 'table', + 'debug', + 'info', + 'dirxml', + 'error', + 'groupCollapsed', + 'Console', + 'profile', + 'profileEnd', + 'timeStamp', + 'context', + ], + }, + ], + 'curly': 'error', + 'brace-style': [ + 'error', + '1tbs', + { + 'allowSingleLine': true, + }, + ], + 'eqeqeq': [ + 'error', + 'always', + { + 'null': 'ignore', + }, + ], + 'radix': 'error', + 'guard-for-in': 'error', + 'no-bitwise': 'error', + 'no-restricted-imports': 'error', + 'no-caller': 'error', + 'no-debugger': 'error', + 'no-redeclare': 'error', + 'no-eval': 'error', + 'no-fallthrough': 'error', + 'no-trailing-spaces': 'error', + 'space-infix-ops': 'error', + 'keyword-spacing': 'error', + 'no-var': 'error', + 'no-unused-expressions': [ + 'error', + { + 'allowTernary': true, + }, + ], + 'prefer-const': 'error', + 'no-case-declarations': 'error', + 'no-extra-boolean-cast': 'error', + 'prefer-spread': 'off', + 'no-underscore-dangle': 'off', + 'no-prototype-builtins': 'off', + 'no-useless-escape': 'off', + + '@angular-eslint/directive-selector': [ + 'error', + { + 'type': 'attribute', + 'prefix': 'ds', + 'style': 'camelCase', + }, + ], + '@angular-eslint/component-selector': [ + 'error', + { + 'type': 'element', + 'prefix': 'ds', + 'style': 'kebab-case', + }, + ], + '@angular-eslint/pipe-prefix': [ + 'error', + { + 'prefixes': [ + 'ds', + ], + }, + ], + '@angular-eslint/prefer-standalone': [ + 'error', + ], + '@angular-eslint/no-attribute-decorator': 'error', + '@angular-eslint/no-output-native': 'warn', + '@angular-eslint/no-output-on-prefix': 'warn', + '@angular-eslint/no-conflicting-lifecycle': 'warn', + '@angular-eslint/use-lifecycle-interface': 'error', + + '@typescript-eslint/no-inferrable-types': [ + 'error', + { + 'ignoreParameters': true, + }, + ], + '@angular-eslint/prefer-inject': 'off', + '@stylistic/quotes': [ + 'error', + 'single', + { + 'avoidEscape': true, + 'allowTemplateLiterals': true, + }, + ], + '@stylistic/semi': 'error', + '@typescript-eslint/no-shadow': 'error', + '@typescript-eslint/dot-notation': 'error', + '@typescript-eslint/consistent-type-definitions': 'error', + '@typescript-eslint/prefer-function-type': 'error', + '@typescript-eslint/naming-convention': [ + 'error', + { + 'selector': 'property', + 'format': null, + }, + ], + '@typescript-eslint/member-ordering': [ + 'error', + { + 'default': [ + 'static-field', + 'instance-field', + 'static-method', + 'instance-method', + ], + }, + ], + '@stylistic/type-annotation-spacing': 'error', + '@typescript-eslint/unified-signatures': 'error', + '@typescript-eslint/no-restricted-types': 'error', + '@typescript-eslint/no-floating-promises': 'warn', + '@typescript-eslint/no-misused-promises': 'warn', + '@typescript-eslint/restrict-plus-operands': 'warn', + '@typescript-eslint/unbound-method': 'off', + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-unnecessary-type-assertion': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-redundant-type-constituents': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/require-await': 'off', + '@typescript-eslint/no-base-to-string': [ + 'error', + { + 'ignoredTypeNames': [ + 'ResourceType', + 'Error', + ], + }, + ], + + '@typescript-eslint/no-deprecated': 'warn', + + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', + 'import/order': 'off', + 'import/first': 'error', + 'import/newline-after-import': 'error', + 'import/no-duplicates': 'error', + 'import/no-deprecated': 'warn', + 'import/no-namespace': 'error', + 'import-newlines/enforce': [ + 'error', + { + 'items': 1, + 'semi': true, + 'forceSingleLine': true, + }, + ], + 'import/enforce-node-protocol-usage': [ + 'error', + 'always', + ], + + 'unused-imports/no-unused-imports': 'error', + 'lodash/import-scope': [ + 'error', + 'method', + ], + + // todo: go over _all_ cases + '@smarttools/rxjs/no-nested-subscribe': 'off', + + // Custom DSpace Angular rules + 'dspace-angular-ts/alias-imports': [ + 'error', + { + 'aliases': [ + { + 'package': 'rxjs', + 'imported': 'of', + 'local': 'of', + }, + ], + }, + ], + 'dspace-angular-ts/no-default-standalone-value': 'error', + 'dspace-angular-ts/themed-component-selectors': 'error', + 'dspace-angular-ts/themed-component-usages': 'error', + 'dspace-angular-ts/themed-decorators': [ + 'off', + { + 'decorators': { + 'listableObjectComponent': 3, + 'rendersSectionForMenu': 2, + }, + }, + ], + 'dspace-angular-ts/themed-wrapper-no-input-defaults': 'error', + 'dspace-angular-ts/unique-decorators': [ + 'off', + { + 'decorators': [ + 'listableObjectComponent', + ], + }, + ], + 'dspace-angular-ts/sort-standalone-imports': [ + 'error', + { + 'locale': 'en-US', + 'maxItems': 0, + 'indent': 2, + 'trailingComma': true, + }, + ], + }, + }, + + // ── Spec files (overrides for test files) ───────────────── + { + files: ['**/*.spec.ts'], + languageOptions: { + parserOptions: { + project: [ + './tsconfig.json', + './cypress/tsconfig.json', + ], + createDefaultProgram: true, + }, + }, + rules: { + 'prefer-const': 'off', + + // Custom DSpace Angular rules + 'dspace-angular-ts/themed-component-usages': 'error', + }, + }, + + // ── HTML template files ─────────────────────────────────── + { + files: ['**/*.html'], + extends: [ + ...angular.configs.templateRecommended, + ], + plugins: { + 'dspace-angular-html': dspaceHtml, + }, + rules: { + // Custom DSpace Angular rules + 'dspace-angular-html/themed-component-usages': 'error', + 'dspace-angular-html/no-disabled-attribute-on-button': 'error', + '@angular-eslint/template/prefer-control-flow': 'error', + }, + }, + + // ── JSON5 files ─────────────────────────────────────────── + { + files: ['**/*.json5'], + extends: [ + ...jsonc.configs['flat/recommended-with-json5'], + ], + rules: { + // The ESLint core no-irregular-whitespace rule doesn't work well in JSON + // See: https://ota-meshi.github.io/eslint-plugin-jsonc/rules/no-irregular-whitespace.html + 'no-irregular-whitespace': 'off', + 'jsonc/no-irregular-whitespace': 'error', + 'no-trailing-spaces': 'error', + 'jsonc/comma-dangle': [ + 'error', + 'always-multiline', + ], + 'jsonc/indent': [ + 'error', + 2, + ], + 'jsonc/key-spacing': [ + 'error', + { + 'beforeColon': false, + 'afterColon': true, + 'mode': 'strict', + }, + ], + 'jsonc/no-dupe-keys': 'off', + 'jsonc/quotes': [ + 'error', + 'double', + { + 'avoidEscape': false, + }, + ], + }, + }, +); \ No newline at end of file diff --git a/lint/src/util/theme-support.ts b/lint/src/util/theme-support.ts index 6434b5eec11..c92df5c4c06 100644 --- a/lint/src/util/theme-support.ts +++ b/lint/src/util/theme-support.ts @@ -152,7 +152,7 @@ class ThemeableComponentRegistry { } // note: this outputs Unix-style paths on Windows - const wrappers: string[] = globSync(prefix + 'src/app/**/themed-*.component.ts', { ignore: 'node_modules/**' }); + const wrappers: string[] = globSync(prefix + 'src/app/**/themed-*.component.ts', { ignore: 'node_modules/**' }).map(toUnixStylePath); for (const wrapper of wrappers) { registerWrapper(wrapper); @@ -271,7 +271,7 @@ export function fixSelectors(text: string): string { */ export function getFileTheme(context: RuleContext): string | undefined { // note: shouldn't use plain .filename (doesn't work in DSpace Angular 7.4) - const m = context.getFilename()?.match(/\/src\/themes\/([^/]+)\//); + const m = toUnixStylePath(context.getFilename())?.match(/\/src\/themes\/([^/]+)\//); if (m?.length === 2) { return m[1]; diff --git a/lint/src/util/typescript.ts b/lint/src/util/typescript.ts index 7c8446b4fcd..6ff3f1445c2 100644 --- a/lint/src/util/typescript.ts +++ b/lint/src/util/typescript.ts @@ -97,7 +97,8 @@ export function isPartOfClassDeclaration(node: TSESTree.Identifier): boolean { } function fromSrc(path: string): string { - const m = path.match(/^.*(src\/.+)(\.(ts|json|js)?)$/); + const normalized = toUnixStylePath(path); + const m = normalized.match(/^.*(src\/.+)(\.(ts|json|js)?)$/); if (m) { return m[1]; diff --git a/package-lock.json b/package-lock.json index e4b8eb5f233..9c79872704a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -96,6 +96,7 @@ "@angular/cli": "^20.3.22", "@angular/compiler-cli": "^20.3.17", "@cypress/schematic": "^1.5.0", + "@eslint/js": "^9.39.4", "@fortawesome/fontawesome-free": "^6.7.2", "@ngrx/store-devtools": "^20.1.0", "@ngtools/webpack": "^20.3.10", @@ -113,6 +114,7 @@ "@typescript-eslint/parser": "^8.58.0", "@typescript-eslint/rule-tester": "^8.58.0", "@typescript-eslint/utils": "^8.47.0", + "angular-eslint": "^20.7.0", "axe-core": "^4.11.2", "commander": "^14.0.3", "compression-webpack-plugin": "^9.2.0", @@ -155,6 +157,7 @@ "ts-node": "^8.10.2", "tsconfig-paths": "^4.2.0", "typescript": "~5.9.3", + "typescript-eslint": "^8.58.0", "webpack": "^5.105.4", "webpack-cli": "^6.0.1" }, @@ -10784,6 +10787,121 @@ "linux" ] }, + "node_modules/angular-eslint": { + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/angular-eslint/-/angular-eslint-20.7.0.tgz", + "integrity": "sha512-BCiTCLO3dr8pGPaM7qLcCruWNcoNNHnLn4DPqE5tHk1TAnTx5TcGy0p/FygharZw5RjWfDHLBjFfpeh4XWLMmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": ">= 20.0.0 < 21.0.0", + "@angular-devkit/schematics": ">= 20.0.0 < 21.0.0", + "@angular-eslint/builder": "20.7.0", + "@angular-eslint/eslint-plugin": "20.7.0", + "@angular-eslint/eslint-plugin-template": "20.7.0", + "@angular-eslint/schematics": "20.7.0", + "@angular-eslint/template-parser": "20.7.0", + "@typescript-eslint/types": "^8.0.0", + "@typescript-eslint/utils": "^8.0.0" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": "*", + "typescript-eslint": "^8.0.0" + } + }, + "node_modules/angular-eslint/node_modules/@angular-devkit/core": { + "version": "20.3.22", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.22.tgz", + "integrity": "sha512-1vZnZTAjGcCM+86v2al+2eiROiSw0uAWeVllfHSQe0KsKOP1FE8UUUiWChhxVn7vIxypphlfGunkeeIn1C/ZFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.18.0", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.4", + "rxjs": "7.8.2", + "source-map": "0.7.6" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/angular-eslint/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/angular-eslint/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/angular-eslint/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/angular-eslint/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/angulartics2": { "version": "12.2.1", "resolved": "https://registry.npmjs.org/angulartics2/-/angulartics2-12.2.1.tgz", @@ -24802,6 +24920,319 @@ "typescript-logic": "^0.0.0" } }, + "node_modules/typescript-eslint": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/typescript-eslint/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/typescript-eslint/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/typescript-eslint/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript-eslint/node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/typescript-logic": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz", diff --git a/package.json b/package.json index d4f9af15aba..08365e63e05 100644 --- a/package.json +++ b/package.json @@ -164,6 +164,7 @@ "@angular/cli": "^20.3.22", "@angular/compiler-cli": "^20.3.17", "@cypress/schematic": "^1.5.0", + "@eslint/js": "^9.39.4", "@fortawesome/fontawesome-free": "^6.7.2", "@ngrx/store-devtools": "^20.1.0", "@ngtools/webpack": "^20.3.10", @@ -181,6 +182,7 @@ "@typescript-eslint/parser": "^8.58.0", "@typescript-eslint/rule-tester": "^8.58.0", "@typescript-eslint/utils": "^8.47.0", + "angular-eslint": "^20.7.0", "axe-core": "^4.11.2", "commander": "^14.0.3", "compression-webpack-plugin": "^9.2.0", @@ -223,6 +225,7 @@ "ts-node": "^8.10.2", "tsconfig-paths": "^4.2.0", "typescript": "~5.9.3", + "typescript-eslint": "^8.58.0", "webpack": "^5.105.4", "webpack-cli": "^6.0.1" },