From 375dde58cd296dbda55abfaccc34c64fc00d68e6 Mon Sep 17 00:00:00 2001 From: ScarletFlash Date: Sat, 22 Jun 2024 16:54:05 +0200 Subject: [PATCH] ci: essential linting --- angular.json | 2 + eslint.config.js | 240 ++++++++++++++++++++-- src/app/app.component.html | 386 ----------------------------------- src/app/app.component.ts | 7 +- src/app/app.config.server.ts | 4 +- src/app/app.config.ts | 4 +- src/app/app.routes.ts | 2 +- src/index.html | 6 +- src/main.server.ts | 3 +- src/main.ts | 8 +- tsconfig.json | 2 + 11 files changed, 245 insertions(+), 419 deletions(-) diff --git a/angular.json b/angular.json index 7030296..487d0df 100644 --- a/angular.json +++ b/angular.json @@ -111,6 +111,8 @@ "lint": { "builder": "@angular-eslint/builder:lint", "options": { + "quiet": true, + "eslintConfig": "eslint.config.js", "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"] } } diff --git a/eslint.config.js b/eslint.config.js index 3f49e50..9f258cd 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,40 +1,240 @@ // @ts-check -const eslint = require('@eslint/js'); -const tseslint = require('typescript-eslint'); -const angular = require('angular-eslint'); +const ESLint = require('@eslint/js'); +const AngularESLint = require('angular-eslint'); +const TypeScriptESLint = require('typescript-eslint'); -module.exports = tseslint.config( +module.exports = TypeScriptESLint.config( { + languageOptions: { + parser: TypeScriptESLint.parser, + parserOptions: { + ecmaFeatures: { modules: true }, + ecmaVersion: 'latest', + project: './tsconfig.app.json' + } + }, files: ['**/*.ts'], extends: [ - eslint.configs.recommended, - ...tseslint.configs.recommended, - ...tseslint.configs.stylistic, - ...angular.configs.tsRecommended + ESLint.configs.recommended, + ...TypeScriptESLint.configs.strictTypeChecked, + ...TypeScriptESLint.configs.stylisticTypeChecked, + ...AngularESLint.configs.tsAll ], - processor: angular.processInlineTemplates, + processor: AngularESLint.processInlineTemplates, rules: { - '@angular-eslint/directive-selector': [ + curly: 'error', + 'max-depth': [ 'error', { - type: 'attribute', - prefix: 'app', - style: 'camelCase' + max: 2 } ], - '@angular-eslint/component-selector': [ + 'no-else-return': [ 'error', { - type: 'element', - prefix: 'app', - style: 'kebab-case' + allowElseIf: false } - ] + ], + 'no-extra-boolean-cast': [ + 'error', + { + enforceForLogicalOperands: true + } + ], + 'no-implicit-coercion': [ + 'error', + { + boolean: true, + number: true, + string: true, + disallowTemplateShorthand: false + } + ], + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': 'error', + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports', + disallowTypeAnnotations: true, + fixStyle: 'inline-type-imports' + } + ], + '@typescript-eslint/consistent-type-exports': [ + 'error', + { + fixMixedExportsWithInlineTypeSpecifier: true + } + ], + '@typescript-eslint/ban-types': [ + 'error', + { + types: { + Object: null + }, + extendDefaults: false + } + ], + '@typescript-eslint/consistent-type-assertions': [ + 'error', + { + assertionStyle: 'as', + objectLiteralTypeAssertions: 'never' + } + ], + '@typescript-eslint/no-unnecessary-condition': [ + 'error', + { + allowConstantLoopConditions: false, + allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: true + } + ], + '@typescript-eslint/explicit-member-accessibility': [ + 'error', + { + accessibility: 'explicit', + overrides: { + constructors: 'off', + parameterProperties: 'off' + } + } + ], + '@typescript-eslint/member-ordering': [ + 'error', + { + classes: [ + 'abstract-field', + 'instance-field', + 'static-field', + + 'static-get', + 'instance-get', + 'abstract-get', + + 'constructor', + + 'abstract-method', + + 'public-instance-method', + 'protected-instance-method', + 'private-instance-method', + '#private-instance-method', + + 'public-static-method', + 'protected-static-method', + 'private-static-method', + '#private-static-method' + ] + } + ], + '@typescript-eslint/no-inferrable-types': 'off', + '@typescript-eslint/no-require-imports': 'error', + 'no-unused-expressions': 'off', + 'no-inner-declarations': 'off', + '@typescript-eslint/no-unused-expressions': [ + 'error', + { + allowShortCircuit: false, + allowTernary: true, + allowTaggedTemplates: false, + enforceForJSX: false + } + ], + '@typescript-eslint/prefer-readonly': 'error', + '@typescript-eslint/strict-boolean-expressions': [ + 'error', + { + allowString: false, + allowNumber: false, + allowRuleToRunWithoutStrictNullChecksIKnowWhatIAmDoing: false + } + ], + '@typescript-eslint/no-extraneous-class': 'off', + '@typescript-eslint/typedef': [ + 'error', + { + arrowParameter: true, + arrayDestructuring: false, + memberVariableDeclaration: true, + objectDestructuring: false, + parameter: true, + propertyDeclaration: true, + variableDeclaration: true, + variableDeclarationIgnoreFunction: true + } + ], + '@typescript-eslint/prefer-as-const': 'off', + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { + allowExpressions: true + } + ], + '@typescript-eslint/no-namespace': 'off', + complexity: [ + 'error', + { + max: 10 + } + ], + 'consistent-return': 'error', + 'no-underscore-dangle': 'error', + 'default-case': 'error', + 'default-case-last': 'error', + eqeqeq: 'error', + 'no-caller': 'error', + 'no-duplicate-imports': 'error', + 'no-sequences': [ + 'error', + { + allowInParentheses: false + } + ], + 'no-template-curly-in-string': 'error', + '@typescript-eslint/no-unsafe-assignment': 'warn', + '@typescript-eslint/no-unsafe-return': 'warn', + '@typescript-eslint/no-unsafe-member-access': 'warn', + '@typescript-eslint/no-unsafe-enum-comparison': 'warn', + '@typescript-eslint/no-unsafe-call': 'warn', + '@typescript-eslint/no-unsafe-argument': 'warn', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-floating-promises': 'warn', + 'prefer-object-spread': 'error', + 'prefer-template': 'error', + 'object-shorthand': 'error', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_' + } + ], + 'no-var': 'error', + 'no-unneeded-ternary': 'error', + 'no-undef-init': 'error', + 'no-new-wrappers': 'error', + 'no-bitwise': 'error', + 'prefer-const': 'error', + radix: 'error', + 'no-eval': 'error', + 'no-console': [ + 'error', + { + allow: ['warn'] + } + ], + 'id-denylist': ['warn', 'data', 'e', 'acc'], + 'arrow-body-style': ['error', 'as-needed'], + 'prefer-arrow-callback': 'error', + 'no-restricted-imports': 'off', + '@typescript-eslint/no-restricted-imports': 'error' } }, { files: ['**/*.html'], - extends: [...angular.configs.templateRecommended, ...angular.configs.templateAccessibility], - rules: {} + extends: [...AngularESLint.configs.templateAll], + rules: { + '@angular-eslint/template/i18n': 'off' + } } ); diff --git a/src/app/app.component.html b/src/app/app.component.html index db2668d..67e7bd4 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,387 +1 @@ - - - - - - - - - - - -
-
-
- -

Hello, {{ title }}

-

Congratulations! Your app is running. 🎉

-
- -
-
- @for ( - item of [ - { title: 'Explore the Docs', link: 'https://angular.dev' }, - { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, - { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, - { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, - { - title: - 'Angular - DevTools', - link: 'https://angular.dev/tools/devtools' - } - ]; - track item.title - ) { - - {{ item.title }} - - - - - } -
- -
-
-
- - - - - - - - - diff --git a/src/app/app.component.ts b/src/app/app.component.ts index bf6972d..18d438c 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,12 +1,13 @@ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; @Component({ selector: 'app-root', standalone: true, imports: [RouterOutlet], - templateUrl: './app.component.html' + templateUrl: './app.component.html', + changeDetection: ChangeDetectionStrategy.OnPush }) export class AppComponent { - title = 'frontend'; + public readonly title: string = 'frontend'; } diff --git a/src/app/app.config.server.ts b/src/app/app.config.server.ts index 2149731..beb7523 100644 --- a/src/app/app.config.server.ts +++ b/src/app/app.config.server.ts @@ -1,4 +1,4 @@ -import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; +import { type ApplicationConfig, mergeApplicationConfig } from '@angular/core'; import { provideServerRendering } from '@angular/platform-server'; import { appConfig } from './app.config'; @@ -6,4 +6,4 @@ const serverConfig: ApplicationConfig = { providers: [provideServerRendering()] }; -export const config = mergeApplicationConfig(appConfig, serverConfig); +export const config: ApplicationConfig = mergeApplicationConfig(appConfig, serverConfig); diff --git a/src/app/app.config.ts b/src/app/app.config.ts index 52cd710..389d105 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -1,8 +1,8 @@ -import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { type ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { provideRouter } from '@angular/router'; -import { routes } from './app.routes'; import { provideClientHydration } from '@angular/platform-browser'; +import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { providers: [provideZoneChangeDetection({ eventCoalescing: true }), provideRouter(routes), provideClientHydration()] diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index dc39edb..de34bde 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,3 +1,3 @@ -import { Routes } from '@angular/router'; +import { type Routes } from '@angular/router'; export const routes: Routes = []; diff --git a/src/index.html b/src/index.html index 5e4b72a..14fdfcc 100644 --- a/src/index.html +++ b/src/index.html @@ -2,19 +2,21 @@ - Frontend + Frontend - + diff --git a/src/main.server.ts b/src/main.server.ts index 4b9d4d1..a2f664f 100644 --- a/src/main.server.ts +++ b/src/main.server.ts @@ -1,7 +1,8 @@ +import type { ApplicationRef } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { config } from './app/app.config.server'; -const bootstrap = () => bootstrapApplication(AppComponent, config); +const bootstrap = (): Promise => bootstrapApplication(AppComponent, config); export default bootstrap; diff --git a/src/main.ts b/src/main.ts index 17447a5..6bbacff 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,9 @@ import { bootstrapApplication } from '@angular/platform-browser'; -import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; -bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err)); +bootstrapApplication(AppComponent, appConfig).catch((error: unknown) => { + /* eslint-disable no-console */ + console.error(error); + /* eslint-enable no-console */ +}); diff --git a/tsconfig.json b/tsconfig.json index 4921df4..da7f7b7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,8 @@ "target": "ESNext", "module": "ESNext", "useDefineForClassFields": false, + "exactOptionalPropertyTypes": true, + "verbatimModuleSyntax": true, "lib": ["ESNext", "dom"] }, "angularCompilerOptions": {