diff --git a/frontend/cypress/support/PageIdleDetector.ts b/frontend/cypress/support/PageIdleDetector.ts index 3bfa2da3d..ba0cd222f 100644 --- a/frontend/cypress/support/PageIdleDetector.ts +++ b/frontend/cypress/support/PageIdleDetector.ts @@ -1,7 +1,7 @@ // source: chrisp_68 @ https://stackoverflow.com/questions/50525143/how-do-you-reliably-wait-for-page-idle-in-cypress-io-test export class PageIdleDetector { - defaultOptions: Object = { timeout: 60000 }; + defaultOptions: object = { timeout: 60000 }; public WaitForPageToBeIdle(): void { @@ -11,7 +11,7 @@ export class PageIdleDetector this.WaitForAnimationsToStop(); } - public WaitForPageToLoad(options: Object = this.defaultOptions): void + public WaitForPageToLoad(options: object = this.defaultOptions): void { cy.document(options).should((myDocument: any) => { @@ -19,7 +19,7 @@ export class PageIdleDetector }); } - public WaitForAngularRequestsToComplete(options: Object = this.defaultOptions): void + public WaitForAngularRequestsToComplete(options: object = this.defaultOptions): void { cy.window(options).should((myWindow: any) => { @@ -30,7 +30,7 @@ export class PageIdleDetector }); } - public WaitForAngularDigestCycleToComplete(options: Object = this.defaultOptions): void + public WaitForAngularDigestCycleToComplete(options: object = this.defaultOptions): void { cy.window(options).should((myWindow: any) => { @@ -41,7 +41,7 @@ export class PageIdleDetector }); } - public WaitForAnimationsToStop(options: Object = this.defaultOptions): void + public WaitForAnimationsToStop(options: object = this.defaultOptions): void { cy.get(":animated", options).should("not.exist"); } diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 000000000..56cd1f02f --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,92 @@ +import js from '@eslint/js'; +import tsParser from '@typescript-eslint/parser'; +import tsPlugin from '@typescript-eslint/eslint-plugin'; + +// Flat config migrated from legacy .eslintrc +export default [ + { + ignores: [ + 'node_modules/**', + 'dist/**', + 'src/resources/**', + // Keep parity with legacy .eslintignore + 'frontend/**', + 'server.run.js', + ], + }, + js.configs.recommended, + // Node globals for local JS utility scripts in this package + { + // Apply to all JS files in this package (including nested ones) + files: ['**/*.js', '**/*.cjs', '**/*.mjs'], + languageOptions: { + globals: { + require: 'readonly', + module: 'readonly', + process: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + Buffer: 'readonly', + console: 'readonly', + InitWally: 'readonly', + document: 'readonly', + window: 'readonly', + }, + }, + }, + { + files: ['**/*.ts'], + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + // Make common browser globals available in TS as well + globals: { + document: 'readonly', + window: 'readonly', + }, + }, + plugins: { + '@typescript-eslint': tsPlugin, + }, + rules: { + // Adjust base recommended rules for TypeScript (matches legacy extends: plugin:@typescript-eslint/eslint-recommended) + ...tsPlugin.configs['eslint-recommended']?.overrides?.[0]?.rules, + // Start from @typescript-eslint's recommended rules + ...tsPlugin.configs.recommended.rules, + + // Project-specific rules migrated from .eslintrc + '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/no-empty-function': 'warn', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-inferrable-types': 'off', + '@typescript-eslint/no-namespace': 'warn', + '@typescript-eslint/no-this-alias': 'warn', + '@typescript-eslint/no-var-requires': 'warn', + '@typescript-eslint/explicit-function-return-type': 'warn', + '@typescript-eslint/no-unused-vars': 'warn', + '@typescript-eslint/no-unused-expressions': 'warn', + '@typescript-eslint/no-require-imports': 'warn', + '@typescript-eslint/no-unsafe-function-type': 'warn', + 'no-case-declarations': 'warn', + 'no-console': 'warn', + 'no-constant-condition': 'warn', + 'no-dupe-else-if': 'warn', + 'no-empty': 'warn', + 'no-extra-boolean-cast': 'warn', + 'no-prototype-builtins': 'warn', + 'no-self-assign': 'warn', + 'no-useless-catch': 'warn', + 'no-var': 'warn', + 'prefer-const': 'warn', + 'prefer-rest-params': 'warn', + 'quotes': ['warn', 'single', { allowTemplateLiterals: true }], + 'semi': 'warn', + 'curly': ['warn', 'all'], + 'eqeqeq': 'warn', + 'no-trailing-spaces': 'warn', + }, + }, +]; diff --git a/frontend/generate-config.js b/frontend/generate-config.js index 89d7143fd..3ae3870e5 100644 --- a/frontend/generate-config.js +++ b/frontend/generate-config.js @@ -29,7 +29,7 @@ if (configContent && configContent.CUSTOMIZATION) { try { customConfig = readConfig(configContent.CUSTOMIZATION); customConfigContent = JSON.parse(customConfig); - } catch (e) { + } catch { console.log(`failed to load customization config from ${configContent.CUSTOMIZATION}`); } } @@ -54,7 +54,7 @@ try { throw new Error(e); } -for (setting in configContent) { +for (const setting in configContent) { settings.push({ key: setting, value: configContent[setting] @@ -98,7 +98,7 @@ function readConfig(path) { try { const currentConfig = fs.readFileSync(path).toString().trim(); return currentConfig; - } catch (e) { + } catch { return false; } } @@ -136,13 +136,11 @@ writeConfig(GENERATED_CUSTOMIZATION_FILE_NAME, customConfigJs); if (currentConfig && currentConfig === newConfig) { console.log(`No configuration updates, skipping ${GENERATED_CONFIG_FILE_NAME} file update`); - return; } else if (!currentConfig) { console.log(`${GENERATED_CONFIG_FILE_NAME} file not found, creating new config file`); console.log('CONFIG: ', newConfig); writeConfig(GENERATED_CONFIG_FILE_NAME, newConfig); console.log(`${GENERATED_CONFIG_FILE_NAME} file saved`); - return; } else { console.log(`Configuration changes detected, updating ${GENERATED_CONFIG_FILE_NAME} file`); console.log('OLD CONFIG: ', currentConfig); diff --git a/frontend/proxy.conf.parameterized.js b/frontend/proxy.conf.parameterized.js index eee9bf1f7..95ec26ddc 100644 --- a/frontend/proxy.conf.parameterized.js +++ b/frontend/proxy.conf.parameterized.js @@ -1,8 +1,6 @@ -const fs = require('fs'); - const PROXY_CONFIG = require('./proxy.conf'); -const addApiKeyHeader = (proxyReq, req, res) => { +const addApiKeyHeader = (proxyReq) => { if (process.env.MEMPOOL_CI_API_KEY) { proxyReq.setHeader('X-Mempool-Auth', process.env.MEMPOOL_CI_API_KEY); } diff --git a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts index c1a53ea3c..5fb7209fc 100644 --- a/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts +++ b/frontend/src/app/components/acceleration/accelerator-dashboard/accelerator-dashboard.component.ts @@ -52,7 +52,6 @@ export class AcceleratorDashboardComponent implements OnInit, OnDestroy { private serviceApiServices: ServicesApiServices, private audioService: AudioService, private stateService: StateService, - @Inject(PLATFORM_ID) private platformId: Object, ) { this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); this.seoService.setTitle($localize`:@@6b867dc61c6a92f3229f1950f9f2d414790cce95:Accelerator Dashboard`); diff --git a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts index bce4338a5..ba955c21c 100644 --- a/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts +++ b/frontend/src/app/components/custom-dashboard/custom-dashboard.component.ts @@ -90,7 +90,6 @@ export class CustomDashboardComponent implements OnInit, OnDestroy, AfterViewIni private websocketService: WebsocketService, private seoService: SeoService, private cd: ChangeDetectorRef, - @Inject(PLATFORM_ID) private platformId: Object, ) { this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); this.widgets = this.stateService.env.customize?.dashboard.widgets || []; diff --git a/frontend/src/app/components/mempool-block/mempool-block.component.ts b/frontend/src/app/components/mempool-block/mempool-block.component.ts index 6b6bfc758..259f6232e 100644 --- a/frontend/src/app/components/mempool-block/mempool-block.component.ts +++ b/frontend/src/app/components/mempool-block/mempool-block.component.ts @@ -31,8 +31,6 @@ export class MempoolBlockComponent implements OnInit, OnDestroy { public stateService: StateService, private seoService: SeoService, private websocketService: WebsocketService, - private cd: ChangeDetectorRef, - @Inject(PLATFORM_ID) private platformId: Object, ) { this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); } diff --git a/frontend/src/app/dashboard/dashboard.component.ts b/frontend/src/app/dashboard/dashboard.component.ts index d1fc4cf78..2e57e6f81 100644 --- a/frontend/src/app/dashboard/dashboard.component.ts +++ b/frontend/src/app/dashboard/dashboard.component.ts @@ -93,7 +93,7 @@ export class DashboardComponent implements OnInit, OnDestroy, AfterViewInit { private apiService: ApiService, private websocketService: WebsocketService, private seoService: SeoService, - @Inject(PLATFORM_ID) private platformId: Object, + @Inject(PLATFORM_ID) private platformId: object, ) { this.webGlEnabled = this.stateService.isBrowser && detectWebGL(); } diff --git a/frontend/src/app/shared/directives/browser-only.directive.ts b/frontend/src/app/shared/directives/browser-only.directive.ts index a8bbeafd7..db9886ef4 100644 --- a/frontend/src/app/shared/directives/browser-only.directive.ts +++ b/frontend/src/app/shared/directives/browser-only.directive.ts @@ -9,7 +9,7 @@ export class BrowserOnlyDirective { constructor( private templateRef: TemplateRef, private viewContainer: ViewContainerRef, - @Inject(PLATFORM_ID) private platformId: Object + @Inject(PLATFORM_ID) private platformId: object ) { if (isPlatformBrowser(this.platformId)) { this.viewContainer.createEmbeddedView(this.templateRef); diff --git a/frontend/src/app/shared/directives/server-only.directive.ts b/frontend/src/app/shared/directives/server-only.directive.ts index 1a4ac9878..7adba2a3a 100644 --- a/frontend/src/app/shared/directives/server-only.directive.ts +++ b/frontend/src/app/shared/directives/server-only.directive.ts @@ -9,7 +9,7 @@ export class ServerOnlyDirective { constructor( private templateRef: TemplateRef, private viewContainer: ViewContainerRef, - @Inject(PLATFORM_ID) private platformId: Object + @Inject(PLATFORM_ID) private platformId: object ) { if (isPlatformServer(this.platformId)) { this.viewContainer.createEmbeddedView(this.templateRef); diff --git a/frontend/src/app/shared/pipes/bytes-pipe/utils.ts b/frontend/src/app/shared/pipes/bytes-pipe/utils.ts index 138218a43..26d2ed999 100644 --- a/frontend/src/app/shared/pipes/bytes-pipe/utils.ts +++ b/frontend/src/app/shared/pipes/bytes-pipe/utils.ts @@ -62,7 +62,7 @@ export function upperFirst(value: string): string { return value.slice(0, 1).toUpperCase() + value.slice(1); } -export function createRound(method: string): Function { +export function createRound(method: string) { // Math to suppress error const func: any = (Math)[method]; return function (value: number, precision: number = 0) { diff --git a/frontend/sync-assets.js b/frontend/sync-assets.js index e988bcb77..447557a51 100644 --- a/frontend/sync-assets.js +++ b/frontend/sync-assets.js @@ -1,5 +1,4 @@ const https = require('https'); -const fs = require('fs').promises; const fsSync = require('fs'); const crypto = require('crypto'); const path = require('node:path'); diff --git a/frontend/update-config.js b/frontend/update-config.js index 92facefa1..f602c4a45 100644 --- a/frontend/update-config.js +++ b/frontend/update-config.js @@ -32,6 +32,7 @@ function parseGeneratedFile() { if (generatedConfig) { const configContents = generatedConfig.toString(); const regexp = new RegExp(/window.__env.(\w+) = '(.*)'/,'g'); + let match; while ((match = regexp.exec(configContents)) !== null) { // Do not add setting if it's the git hash or package json version if (!packageSettings.includes(match[1])) { @@ -46,8 +47,10 @@ function parseGeneratedFile() { function saveSettingsJson() { settings.forEach(setting => { + // eslint-disable-next-line no-prototype-builtins if (configContent.hasOwnProperty(setting['key']) && normalizedValue(configContent[setting['key']]) !== normalizedValue(setting['value'])) { console.log(setting['key'] + " updated from " + configContent[setting['key']] + " to " + setting['value']); + // eslint-disable-next-line no-prototype-builtins } else if (configContent.hasOwnProperty(setting['key']) && normalizedValue(configContent[setting['key']]) === normalizedValue(setting['value'])) { console.log(setting['key'] + " unchanged, skipping"); } else { @@ -58,15 +61,6 @@ function saveSettingsJson() { fs.writeFileSync(CONFIG_FILE_NAME, JSON.stringify(configContent)); } -function configToJson() { - for (setting in configContent) { - settings.push({ - key: setting, - value: configContent[setting] - }); - } -} - try { const rawConfig = fs.readFileSync(CONFIG_FILE_NAME); configContent = JSON.parse(rawConfig);