From 833e2ea9ec86625c94931f270efa80240d770515 Mon Sep 17 00:00:00 2001 From: Daniel Mantovani Date: Thu, 29 Aug 2024 13:08:44 -0300 Subject: [PATCH 1/3] separate prettier from eslint and migrate eslint to 9.x --- .eslintrc.json | 37 --------------- eslint.config.js | 114 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 19 +++++--- 3 files changed, 126 insertions(+), 44 deletions(-) delete mode 100644 .eslintrc.json create mode 100644 eslint.config.js diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 1b1c9624..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "env": { - "node": true, - "es6": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "prettier" - ], - "plugins": ["import", "prettier"], - "parserOptions": { - "project": "./tsconfig.eslint.json" - }, - "rules": { - "prettier/prettier": "error", - "import/order": [ - "error", - { - "groups": ["type", "builtin", ["sibling", "parent"], "index", "object"], - "newlines-between": "never", - "alphabetize": { - "order": "asc", - "caseInsensitive": true - } - } - ], - "@typescript-eslint/consistent-type-imports": [ - "error", - { - "prefer": "type-imports" - } - ], - "@typescript-eslint/no-explicit-any": "off" - } -} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..8f51d503 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,114 @@ +import importPlugin from 'eslint-plugin-import-x' +import globals from 'globals' +import tsParser from '@typescript-eslint/parser' +import eslintJs from '@eslint/js' +import eslintTs from 'typescript-eslint' + +const tsFiles = ['{src,test}/**/*.ts']; +const jsFiles = ['test/**/*.js']; + +const languageOptions = { + globals: { + ...globals.node + }, + ecmaVersion: 2023, + sourceType: 'module', +} + +const customTypescriptConfig = { + files: tsFiles, + plugins: { + import: importPlugin, + 'import/parsers': tsParser, + }, + languageOptions: { + ...languageOptions, + parser: tsParser, + parserOptions: { + project: './tsconfig.eslint.json', + }, + }, + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts'], + }, + }, + rules: { + 'import/export': 'error', + 'import/no-duplicates': 'warn', + ...importPlugin.configs.typescript.rules, + '@typescript-eslint/no-use-before-define': 'off', + 'require-await': 'off', + 'no-duplicate-imports': 'error', + 'no-unneeded-ternary': 'error', + 'prefer-object-spread': 'error', + + '@typescript-eslint/no-unused-vars': [ + 'error', + { + ignoreRestSiblings: true, + args: 'none', + }, + ], + "import/order": ["error", { + groups: ["type", "builtin", ["sibling", "parent"], "index", "object"], + "newlines-between": "never", + + alphabetize: { + order: "asc", + caseInsensitive: true, + }, + }], + + "@typescript-eslint/consistent-type-imports": ["error", { + prefer: "type-imports", + }], + + "@typescript-eslint/no-explicit-any": "off", + }, +} + +const customJavascriptConfig = { + files: jsFiles, + languageOptions: { + ...languageOptions, + parserOptions: { + ecmaVersion: 2023, + }, + }, + rules: { + 'no-duplicate-imports': 'error', + 'no-unneeded-ternary': 'error', + 'prefer-object-spread': 'error', + 'no-unused-vars': [ + 'error', + { + ignoreRestSiblings: true, + args: 'none', + }, + ], + // Add more JS-specific rules here if needed + }, +} +// Add the files for applying the recommended TypeScript configs +// only for the Typescript files. +// This is necessary when we have the multiple extensions files +// (e.g. .ts, .tsx, .js, .cjs, .mjs, etc.). +const recommendedTypeScriptConfigs = [ + ...eslintTs.configs.recommended.map((config) => ({ + ...config, + files: tsFiles, + })), + ...eslintTs.configs.stylistic.map((config) => ({ + ...config, + files: tsFiles, + })), +] + +export default [ + { ignores: ['docs/*', 'lib/*'] }, // global ignores + eslintJs.configs.recommended, + customJavascriptConfig, + ...recommendedTypeScriptConfigs, + customTypescriptConfig, +] \ No newline at end of file diff --git a/package.json b/package.json index de11f461..60cd8c7f 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,8 @@ "clean": "rm -rf tsconfig.tsbuildinfo test/support/ts/full-app/tsconfig.tsbuildinfo lib test/support/ts/full-app/lib", "coverage": "c8 tap --disable-coverage --allow-empty-coverage test/*.js test/*.ts", "coverage:ci": "c8 --reporter lcovonly tap --disable-coverage --allow-empty-coverage test/*.js test/*.ts", + "prelint": "prettier --check \"{src,test}/**/*.{js,ts}\"", + "prelint:fix": "prettier --write \"{src,test}/**/*.{js,ts}\"", "lint": "eslint \"test/*.js\" \"test/support/js/**/*.js\" \"test/support/ts/**/src/**/*.ts\" \"src/**/*.ts\" \"src/*.ts\"", "lint:fix": "npm run lint -- --fix", "prepublishOnly": "npm run build", @@ -53,6 +55,7 @@ "mojo": "./bin/mojo.js" }, "devDependencies": { + "@eslint/js": "^9.9.1", "@types/busboy": "^1.5.0", "@types/js-yaml": "^4.0.3", "@types/mime-types": "^2.1.0", @@ -62,24 +65,26 @@ "@types/stack-utils": "^2.0.1", "@types/tough-cookie": "^4.0.2", "@types/ws": "^8.5.3", - "@typescript-eslint/eslint-plugin": "^7.1.0", + "@typescript-eslint/eslint-plugin": "^8.3.0", + "@typescript-eslint/parser": "^8.3.0", "autocannon": "^7.3.0", "c8": "^10.1.0", "concurrently": "^8.0.0", - "eslint": "^8.1.0", - "eslint-config-prettier": "^9.0.0", - "eslint-plugin-import": "^2.23.4", + "eslint": "^9.9.1", + "eslint-plugin-import-x": "^4.1.1", "eslint-plugin-node": "^11.1.0", - "eslint-plugin-prettier": "^5.0.0", + "globals": "^15.9.0", "nodemon": "^3.0.0", - "prettier": "^3.0.0", + "prettier": "^3.3.3", "tap": "^21.0.0", - "typescript": "^5.2.0" + "typescript": "^5.5.4", + "typescript-eslint": "^8.3.0" }, "engines": { "node": ">= 16" }, "dependencies": { + "@eslint/compat": "^1.1.1", "@mojojs/dom": "^2.1.0", "@mojojs/path": "^1.6.0", "@mojojs/template": "^2.2.0", From 9aa410d60eb9ebf4c9db28944c71688822d1fdda Mon Sep 17 00:00:00 2001 From: Daniel Mantovani Date: Fri, 30 Aug 2024 20:56:14 -0300 Subject: [PATCH 2/3] fix remaining lint errors manually --- src/cgi.ts | 3 +-- src/plugins/default-helpers.ts | 6 ++---- src/plugins/tmpl-engine.ts | 2 +- src/router.ts | 1 + src/router/pattern.ts | 4 +--- src/router/plan.ts | 2 +- src/router/route.ts | 8 ++++---- src/server.ts | 2 +- src/session.ts | 2 +- src/types.ts | 19 ++++++++----------- src/user-agent/cookie-jar.ts | 3 +-- src/util.ts | 8 ++++---- src/validator.ts | 3 +-- src/websocket.ts | 2 +- test/exception-app.js | 2 +- 15 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/cgi.ts b/src/cgi.ts index a0147927..796c6d38 100644 --- a/src/cgi.ts +++ b/src/cgi.ts @@ -1,6 +1,5 @@ import type {MojoApp, ServerResponseBody} from './types.js'; -import type {Readable} from 'node:stream'; -import {Stream} from 'node:stream'; +import {Stream, type Readable} from 'node:stream'; import {ServerRequest} from './server/request.js'; import {ServerResponse} from './server/response.js'; import {httpStatusMessages} from './util.js'; diff --git a/src/plugins/default-helpers.ts b/src/plugins/default-helpers.ts index 61f8c8f7..494f718b 100644 --- a/src/plugins/default-helpers.ts +++ b/src/plugins/default-helpers.ts @@ -8,11 +8,9 @@ import type { URLTarget, UserAgentRequestOptions } from '../types.js'; -import type {InspectOptions} from 'node:util'; -import {inspect} from 'node:util'; +import {inspect, type InspectOptions} from 'node:util'; import {Logger} from '../logger.js'; -import {SafeString} from '../util.js'; -import {exceptionContext} from '../util.js'; +import {exceptionContext, SafeString} from '../util.js'; import DOM from '@mojojs/dom'; /** diff --git a/src/plugins/tmpl-engine.ts b/src/plugins/tmpl-engine.ts index 93f4117b..f7b73e6e 100644 --- a/src/plugins/tmpl-engine.ts +++ b/src/plugins/tmpl-engine.ts @@ -13,7 +13,7 @@ export default function tmplEnginePlugin(app: App): void { } class TmplEngine { - cache: Cache<(data?: Record) => Promise> = new Cache(); + cache = new Cache<(data?: Record) => Promise>(); async render(ctx: MojoContext, options: MojoRenderOptions): Promise { let template; diff --git a/src/router.ts b/src/router.ts index 371b5521..05df9191 100644 --- a/src/router.ts +++ b/src/router.ts @@ -179,6 +179,7 @@ export class Router extends Route { const customNames: RouteIndex = {}; const children = [...this.children]; + // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let i = 0; i < children.length; i++) { const child = children[i]; if (child.customName !== undefined && customNames[child.customName] === undefined) { diff --git a/src/router/pattern.ts b/src/router/pattern.ts index d5bb878d..a1662a96 100644 --- a/src/router/pattern.ts +++ b/src/router/pattern.ts @@ -4,9 +4,7 @@ import {escapeRegExp} from '@mojojs/util'; interface MatchOptions { isEndpoint: boolean; } -interface PlaceholderTypes { - [name: string]: PlaceholderType; -} +type PlaceholderTypes = Record; type ASTNode = [symbol, ...string[]]; diff --git a/src/router/plan.ts b/src/router/plan.ts index 4e907203..9bfda31c 100644 --- a/src/router/plan.ts +++ b/src/router/plan.ts @@ -11,7 +11,7 @@ export class Plan { /** * Steps in route. */ - steps: Array> = []; + steps: Record[] = []; /** * Dispatch stops in route. */ diff --git a/src/router/route.ts b/src/router/route.ts index 1717a37e..63016fb5 100644 --- a/src/router/route.ts +++ b/src/router/route.ts @@ -33,7 +33,7 @@ export class Route { /** * Activate conditions for this route. */ - requirements: Array> = []; + requirements: Record[] = []; /** * Activate `websocket` semantics for this route. */ @@ -255,7 +255,7 @@ export class Route { /** * Set default parameters for this route. */ - to(...targets: Array>): this { + to(...targets: (string | MojoAction | Record)[]): this { const {defaults} = this.pattern; for (const target of targets) { @@ -299,8 +299,8 @@ export class Route { return child; } - _branch(): Array { - const branch: Array = [this]; + _branch(): (Router | Route)[] { + const branch: (Router | Route)[] = [this]; let current: Router | Route | undefined = branch[0]; while ((current = current.parent) !== undefined) { branch.push(current); diff --git a/src/server.ts b/src/server.ts index a3a44efe..c46084e4 100644 --- a/src/server.ts +++ b/src/server.ts @@ -61,7 +61,7 @@ export class Server { _cluster: boolean; _listen: string[]; - _servers: Array = []; + _servers: (http.Server | https.Server)[] = []; _quiet: boolean; _workers: number; diff --git a/src/session.ts b/src/session.ts index 9c3e1cab..2ab741e9 100644 --- a/src/session.ts +++ b/src/session.ts @@ -63,7 +63,7 @@ export class Session { decipher.setAuthTag(authTag); const decrypted = decipher.update(value, 'base64', 'utf8'); return decrypted + decipher.final('utf8'); - } catch (error) { + } catch { continue; } } diff --git a/src/types.ts b/src/types.ts index c69797cd..721495f7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,8 +4,7 @@ import type {Route} from './router/route.js'; import type {SafeString} from './util.js'; import type {ValidatorResult} from './validator/result.js'; import type {Agent} from 'node:http'; -import type {Readable} from 'node:stream'; -import type {Stream} from 'node:stream'; +import type {Readable, Stream} from 'node:stream'; import type {URL} from 'node:url'; import type {InspectOptions} from 'node:util'; import type {Test} from 'tap'; @@ -45,9 +44,7 @@ export interface MojoContext extends Context { export type MojoAction = (ctx: MojoContext, ...args: any[]) => any; -export interface MojoModels { - [key: string]: any; -} +export type MojoModels = Record; export interface MojoTags { asset: (path: string, attrs?: TagAttrs) => Promise; @@ -84,8 +81,8 @@ export interface MojoTags { [key: string]: any; } -export type AnyArguments = Array>; -export type RouteArguments = Array>; +export type AnyArguments = (string | string[] | MojoAction | Record)[]; +export type RouteArguments = (string | MojoAction | Record)[]; export type PlaceholderType = RegExp | string | string[]; export type TagAttrs = Record>; @@ -102,8 +99,8 @@ export interface BackendInfo { export interface SessionData { expiration?: number; expires?: number; - flash?: {[key: string]: any}; - nextFlash?: {[key: string]: any}; + flash?: Record; + nextFlash?: Record; [key: string]: any; } @@ -130,12 +127,12 @@ export interface CookieOptions { secure?: boolean; } -export type MojoURLOptions = { +export interface MojoURLOptions { absolute?: boolean; fragment?: string; query?: Record; values?: Record; -}; +} export interface MojoRenderOptions { engine?: string; diff --git a/src/user-agent/cookie-jar.ts b/src/user-agent/cookie-jar.ts index 0e5436ae..5997f52e 100644 --- a/src/user-agent/cookie-jar.ts +++ b/src/user-agent/cookie-jar.ts @@ -1,5 +1,4 @@ -import type {URL} from 'node:url'; -import {format} from 'node:url'; +import {format, type URL} from 'node:url'; import tough from 'tough-cookie'; /** diff --git a/src/util.ts b/src/util.ts index bafe32c6..b84fd08a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -5,7 +5,7 @@ import Template from '@mojojs/template'; import chalk from 'chalk'; export * from '@mojojs/util'; -type FixOptions = { +interface FixOptions { author?: string; dependencies?: Record; description?: string; @@ -16,7 +16,7 @@ type FixOptions = { name?: string; scripts?: Record; version?: string; -}; +} // Unmarked codes are from RFC 7231 export const httpStatusMessages: Record = { @@ -183,7 +183,7 @@ export async function devDependencies(regex: RegExp): Promise} | null> { +): Promise<{file: string; line: number; column: number; source: {num: number; code: string}[]} | null> { const stack = error.stack ?? ''; const match = stack.split('\n')[1].match(/^\s*at .+ \(([^)]+):(\d+):(\d+)\)\s*$/); if (match === null || match[1].startsWith('file://') === false) return null; @@ -196,7 +196,7 @@ export async function exceptionContext( const startLine = lineNumber - lines <= 0 ? 1 : lineNumber - lines; const endLine = lineNumber + lines; - const source: Array<{num: number; code: string}> = []; + const source: {num: number; code: string}[] = []; const context = {file: file.toString(), line: lineNumber, column, source}; let currentLine = 0; for await (const line of file.lines({encoding: 'utf8'})) { diff --git a/src/validator.ts b/src/validator.ts index 5f65a16b..8c57d4b4 100644 --- a/src/validator.ts +++ b/src/validator.ts @@ -1,7 +1,6 @@ import type {JSONSchema, ValidatorFunction} from './types.js'; -import type {ValidateFunction} from 'ajv'; import {ValidatorResult} from './validator/result.js'; -import Ajv from 'ajv'; +import Ajv, {type ValidateFunction} from 'ajv'; /** * JSON schema validator class. diff --git a/src/websocket.ts b/src/websocket.ts index afae52d6..dcabba44 100644 --- a/src/websocket.ts +++ b/src/websocket.ts @@ -82,7 +82,7 @@ class WebSocket extends EventEmitter implements WebSocketEventEmitter { return new Promise(resolve => this._ws.send(JSON.stringify(message), () => resolve())); } - _messageIterator(): AsyncIterableIterator> { + _messageIterator(): AsyncIterableIterator<(JSONValue | Buffer)[]> { const ac = new AbortController(); this._ws.on('close', () => ac.abort()); diff --git a/test/exception-app.js b/test/exception-app.js index 821c0939..6f9585bc 100644 --- a/test/exception-app.js +++ b/test/exception-app.js @@ -276,7 +276,7 @@ t.test('Exception app', async t => { app.websocket('/ws/exception/iterator').to(ctx => { ctx.plain(async ws => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars + // eslint-disable-next-line no-unused-vars for await (const message of ws) { throw new Error('WebSocket iterator test exception'); } From cbf9e0a516183987adde88c01b6be1222f6cc7e1 Mon Sep 17 00:00:00 2001 From: Daniel Mantovani Date: Sun, 1 Sep 2024 17:34:01 -0300 Subject: [PATCH 3/3] remove obvious comments from eslint.config.js and apply prettier --- eslint.config.js | 197 ++++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 101 insertions(+), 100 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 8f51d503..71d4f5d8 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,114 +1,115 @@ -import importPlugin from 'eslint-plugin-import-x' -import globals from 'globals' -import tsParser from '@typescript-eslint/parser' -import eslintJs from '@eslint/js' -import eslintTs from 'typescript-eslint' +import importPlugin from 'eslint-plugin-import-x'; +import globals from 'globals'; +import tsParser from '@typescript-eslint/parser'; +import eslintJs from '@eslint/js'; +import eslintTs from 'typescript-eslint'; const tsFiles = ['{src,test}/**/*.ts']; const jsFiles = ['test/**/*.js']; const languageOptions = { - globals: { - ...globals.node - }, - ecmaVersion: 2023, - sourceType: 'module', -} + globals: { + ...globals.node + }, + ecmaVersion: 2023, + sourceType: 'module' +}; const customTypescriptConfig = { - files: tsFiles, - plugins: { - import: importPlugin, - 'import/parsers': tsParser, - }, - languageOptions: { - ...languageOptions, - parser: tsParser, - parserOptions: { - project: './tsconfig.eslint.json', - }, - }, - settings: { - 'import/parsers': { - '@typescript-eslint/parser': ['.ts'], - }, - }, - rules: { - 'import/export': 'error', - 'import/no-duplicates': 'warn', - ...importPlugin.configs.typescript.rules, - '@typescript-eslint/no-use-before-define': 'off', - 'require-await': 'off', - 'no-duplicate-imports': 'error', - 'no-unneeded-ternary': 'error', - 'prefer-object-spread': 'error', + files: tsFiles, + plugins: { + import: importPlugin, + 'import/parsers': tsParser + }, + languageOptions: { + ...languageOptions, + parser: tsParser, + parserOptions: { + project: './tsconfig.eslint.json' + } + }, + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts'] + } + }, + rules: { + 'import/export': 'error', + 'import/no-duplicates': 'warn', + ...importPlugin.configs.typescript.rules, + '@typescript-eslint/no-use-before-define': 'off', + 'require-await': 'off', + 'no-duplicate-imports': 'error', + 'no-unneeded-ternary': 'error', + 'prefer-object-spread': 'error', - '@typescript-eslint/no-unused-vars': [ - 'error', - { - ignoreRestSiblings: true, - args: 'none', - }, - ], - "import/order": ["error", { - groups: ["type", "builtin", ["sibling", "parent"], "index", "object"], - "newlines-between": "never", + '@typescript-eslint/no-unused-vars': [ + 'error', + { + ignoreRestSiblings: true, + args: 'none' + } + ], + 'import/order': [ + 'error', + { + groups: ['type', 'builtin', ['sibling', 'parent'], 'index', 'object'], + 'newlines-between': 'never', - alphabetize: { - order: "asc", - caseInsensitive: true, - }, - }], + alphabetize: { + order: 'asc', + caseInsensitive: true + } + } + ], - "@typescript-eslint/consistent-type-imports": ["error", { - prefer: "type-imports", - }], + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + prefer: 'type-imports' + } + ], - "@typescript-eslint/no-explicit-any": "off", - }, -} + '@typescript-eslint/no-explicit-any': 'off' + } +}; const customJavascriptConfig = { - files: jsFiles, - languageOptions: { - ...languageOptions, - parserOptions: { - ecmaVersion: 2023, - }, - }, - rules: { - 'no-duplicate-imports': 'error', - 'no-unneeded-ternary': 'error', - 'prefer-object-spread': 'error', - 'no-unused-vars': [ - 'error', - { - ignoreRestSiblings: true, - args: 'none', - }, - ], - // Add more JS-specific rules here if needed - }, -} -// Add the files for applying the recommended TypeScript configs -// only for the Typescript files. -// This is necessary when we have the multiple extensions files -// (e.g. .ts, .tsx, .js, .cjs, .mjs, etc.). + files: jsFiles, + languageOptions: { + ...languageOptions, + parserOptions: { + ecmaVersion: 2023 + } + }, + rules: { + 'no-duplicate-imports': 'error', + 'no-unneeded-ternary': 'error', + 'prefer-object-spread': 'error', + 'no-unused-vars': [ + 'error', + { + ignoreRestSiblings: true, + args: 'none' + } + ] + } +}; const recommendedTypeScriptConfigs = [ - ...eslintTs.configs.recommended.map((config) => ({ - ...config, - files: tsFiles, - })), - ...eslintTs.configs.stylistic.map((config) => ({ - ...config, - files: tsFiles, - })), -] + ...eslintTs.configs.recommended.map(config => ({ + ...config, + files: tsFiles + })), + ...eslintTs.configs.stylistic.map(config => ({ + ...config, + files: tsFiles + })) +]; export default [ - { ignores: ['docs/*', 'lib/*'] }, // global ignores - eslintJs.configs.recommended, - customJavascriptConfig, - ...recommendedTypeScriptConfigs, - customTypescriptConfig, -] \ No newline at end of file + {ignores: ['docs/*', 'lib/*']}, + eslintJs.configs.recommended, + customJavascriptConfig, + ...recommendedTypeScriptConfigs, + customTypescriptConfig +]; diff --git a/package.json b/package.json index 60cd8c7f..b620f8c7 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,8 @@ "clean": "rm -rf tsconfig.tsbuildinfo test/support/ts/full-app/tsconfig.tsbuildinfo lib test/support/ts/full-app/lib", "coverage": "c8 tap --disable-coverage --allow-empty-coverage test/*.js test/*.ts", "coverage:ci": "c8 --reporter lcovonly tap --disable-coverage --allow-empty-coverage test/*.js test/*.ts", - "prelint": "prettier --check \"{src,test}/**/*.{js,ts}\"", - "prelint:fix": "prettier --write \"{src,test}/**/*.{js,ts}\"", + "prelint": "prettier --check \"{src,test}/**/*.{js,ts}\" eslint.config.js", + "prelint:fix": "prettier --write \"{src,test}/**/*.{js,ts}\" eslint.config.js", "lint": "eslint \"test/*.js\" \"test/support/js/**/*.js\" \"test/support/ts/**/src/**/*.ts\" \"src/**/*.ts\" \"src/*.ts\"", "lint:fix": "npm run lint -- --fix", "prepublishOnly": "npm run build",