diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index f1a1b31d3e9f..000000000000 --- a/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -backend/dist -backend/__migration__ -docker -backend/scripts -backend/private -**/vitest.config.ts -node_modules \ No newline at end of file diff --git a/.github/workflows/monkey-ci.yml b/.github/workflows/monkey-ci.yml index 97009f04ddab..e1ced82c2020 100644 --- a/.github/workflows/monkey-ci.yml +++ b/.github/workflows/monkey-ci.yml @@ -161,7 +161,7 @@ jobs: run: pnpm install - name: Check lint - run: npm run lint-be + run: npm run lint-fast-be && npm run lint-be - name: Build run: npm run build-be @@ -214,7 +214,7 @@ jobs: run: pnpm install - name: Check lint - run: npm run lint-fe + run: npm run lint-fast-fe && npm run lint-fe - name: Build run: npm run build-fe @@ -330,7 +330,7 @@ jobs: run: pnpm install - name: Check lint - run: npm run lint-pkg + run: npm run lint-fast-pkg && npm run lint-pkg - name: Build run: npm run build-pkg diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 000000000000..7258d857a3c2 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,7 @@ +{ + "ignorePatterns": ["node_modules", "dist", ".turbo"], + "extends": [ + "./packages/oxlint-config/index.jsonc" + // "@monkeytype/oxlint-config" + ] +} diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs deleted file mode 100644 index 278eceb29bc0..000000000000 --- a/backend/.eslintrc.cjs +++ /dev/null @@ -1,16 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - extends: ["@monkeytype/eslint-config"], - ignorePatterns: [ - "node_modules/", - "dist/", - "build/", - "__tests__/", - "jest.config.ts", - "__migration__/", - ], - rules: { - eqeqeq: "error", - }, -}; diff --git a/backend/.oxlintrc-plugin.json b/backend/.oxlintrc-plugin.json new file mode 100644 index 000000000000..e388d426ec0a --- /dev/null +++ b/backend/.oxlintrc-plugin.json @@ -0,0 +1,4 @@ +{ + "ignorePatterns": ["node_modules", "__migration__", "dist", ".turbo"], + "extends": ["../packages/oxlint-config/plugin.jsonc"] +} diff --git a/backend/.oxlintrc.json b/backend/.oxlintrc.json index c9ca40fdb7ff..a5e663c35482 100644 --- a/backend/.oxlintrc.json +++ b/backend/.oxlintrc.json @@ -1,33 +1,7 @@ { + "ignorePatterns": ["node_modules", "__migration__", "dist", ".turbo"], "extends": [ "../packages/oxlint-config/index.jsonc" // "@monkeytype/oxlint-config" - ], - "overrides": [ - { - "files": ["__tests__/**"], - "plugins": [ - "typescript", - "unicorn", - "oxc", - "import", - "node", - "promise", - "jest", - "vitest" - ], - "rules": { - "no-explicit-any": "allow", - "explicit-function-return-type": "off", - "ban-ts-comment": "off" - } - }, - { - "files": ["__tests__/**/*.d.ts"], - "rules": { - "typescript/consistent-type-definitions": "off", - "typescript/no-empty-object-type": "off" - } - } ] } diff --git a/backend/__tests__/api/controllers/admin.spec.ts b/backend/__tests__/api/controllers/admin.spec.ts index 8d6a7ca8ff76..c7bcf9a5cd99 100644 --- a/backend/__tests__/api/controllers/admin.spec.ts +++ b/backend/__tests__/api/controllers/admin.spec.ts @@ -10,6 +10,7 @@ import GeorgeQueue from "../../../src/queues/george-queue"; import * as AuthUtil from "../../../src/utils/auth"; import { enableRateLimitExpects } from "../../__testData__/rate-limit"; +import Test from "supertest/lib/test"; const { mockApp, uid } = setup(); const configuration = Configuration.getCachedConfiguration(); @@ -558,12 +559,12 @@ describe("AdminController", () => { }); }); - async function expectFailForNonAdmin(call: SuperTest): Promise { + async function expectFailForNonAdmin(call: Test): Promise { isAdminMock.mockResolvedValue(false); const { body } = await call.expect(403); expect(body.message).toEqual("You don't have permission to do this."); } - async function expectFailForDisabledEndpoint(call: SuperTest): Promise { + async function expectFailForDisabledEndpoint(call: Test): Promise { await enableAdminEndpoints(false); const { body } = await call.expect(503); expect(body.message).toEqual("Admin endpoints are currently disabled."); diff --git a/backend/__tests__/api/controllers/user.spec.ts b/backend/__tests__/api/controllers/user.spec.ts index 374a2b509357..997b8045f7a3 100644 --- a/backend/__tests__/api/controllers/user.spec.ts +++ b/backend/__tests__/api/controllers/user.spec.ts @@ -33,11 +33,10 @@ import { mockAuthenticateWithApeKey } from "../../__testData__/auth"; import { randomUUID } from "node:crypto"; import { MonkeyMail, UserStreak } from "@monkeytype/schemas/users"; import MonkeyError, { isFirebaseError } from "../../../src/utils/error"; -import { LeaderboardEntry } from "@monkeytype/schemas/leaderboards"; import * as WeeklyXpLeaderboard from "../../../src/services/weekly-xp-leaderboard"; import * as ConnectionsDal from "../../../src/dal/connections"; import { pb } from "../../__testData__/users"; -import { SuperTest } from "supertest"; +import Test from "supertest/lib/test"; const { mockApp, uid, mockAuth } = setup(); const configuration = Configuration.getCachedConfiguration(); @@ -2979,7 +2978,7 @@ describe("user controller test", () => { getUserByNameMock.mockResolvedValue(foundUser as any); - const rank = { rank: 24 } as LeaderboardEntry; + const rank = { rank: 24 } as LeaderboardDal.DBLeaderboardEntry; leaderboardGetRankMock.mockResolvedValue(rank); leaderboardGetCountMock.mockResolvedValue(100); @@ -3041,7 +3040,7 @@ describe("user controller test", () => { ...foundUser, profileDetails: { showActivityOnPublicProfile: true }, } as any); - const rank = { rank: 24 } as LeaderboardEntry; + const rank = { rank: 24 } as LeaderboardDal.DBLeaderboardEntry; leaderboardGetRankMock.mockResolvedValue(rank); leaderboardGetCountMock.mockResolvedValue(100); @@ -3063,7 +3062,7 @@ describe("user controller test", () => { ...foundUser, profileDetails: { showActivityOnPublicProfile: false }, } as any); - const rank = { rank: 24 } as LeaderboardEntry; + const rank = { rank: 24 } as LeaderboardDal.DBLeaderboardEntry; leaderboardGetRankMock.mockResolvedValue(rank); leaderboardGetCountMock.mockResolvedValue(100); @@ -3081,7 +3080,7 @@ describe("user controller test", () => { banned: true, } as any); - const rank = { rank: 24 } as LeaderboardEntry; + const rank = { rank: 24 } as LeaderboardDal.DBLeaderboardEntry; leaderboardGetRankMock.mockResolvedValue(rank); leaderboardGetCountMock.mockResolvedValue(100); @@ -3132,7 +3131,7 @@ describe("user controller test", () => { const uid = foundUser.uid; getUserMock.mockResolvedValue(foundUser as any); - const rank = { rank: 24 } as LeaderboardEntry; + const rank = { rank: 24 } as LeaderboardDal.DBLeaderboardEntry; leaderboardGetRankMock.mockResolvedValue(rank); leaderboardGetCountMock.mockResolvedValue(100); @@ -4050,7 +4049,7 @@ async function enableConnectionsEndpoints(enabled: boolean): Promise { ); } -async function expectFailForDisabledEndpoint(call: SuperTest): Promise { +async function expectFailForDisabledEndpoint(call: Test): Promise { await enableConnectionsEndpoints(false); const { body } = await call.expect(503); expect(body.message).toEqual("Connections are not available at this time."); diff --git a/backend/__tests__/tsconfig.json b/backend/__tests__/tsconfig.json index 8f192081b529..09c90f4c0d19 100644 --- a/backend/__tests__/tsconfig.json +++ b/backend/__tests__/tsconfig.json @@ -1,7 +1,11 @@ { "extends": "@monkeytype/typescript-config/base.json", "compilerOptions": { - "noEmit": true + "noEmit": true, + "noImplicitAny": false, + "strictFunctionTypes": false, + "useUnknownInCatchVariables": false, + "strictPropertyInitialization": false }, "files": ["vitest.d.ts"], "include": ["./**/*.ts", "./**/*.spec.ts", "./setup-tests.ts"] diff --git a/backend/package.json b/backend/package.json index 662e0d95ca04..b4f690ec9b08 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,9 +4,8 @@ "license": "GPL-3.0", "private": true, "scripts": { - "eslint": "eslint \"./src/**/*.ts\"", - "oxlint": "oxlint .", - "lint": "npm run oxlint && npm run eslint", + "lint": "oxlint . --type-aware --type-check && oxlint -c .oxlintrc-plugin.json", + "lint-fast": "oxlint .", "build": "npm run gen-docs && tsc --build", "watch": "tsc --build --watch", "clean": "tsc --build --clean", @@ -62,7 +61,6 @@ "zod": "3.23.8" }, "devDependencies": { - "@monkeytype/eslint-config": "workspace:*", "@monkeytype/oxlint-config": "workspace:*", "@monkeytype/typescript-config": "workspace:*", "@redocly/cli": "2.0.5", @@ -84,10 +82,9 @@ "@types/uuid": "10.0.0", "@vitest/coverage-v8": "4.0.8", "concurrently": "8.2.2", - "eslint": "8.57.1", - "eslint-watch": "8.0.0", "openapi3-ts": "2.0.2", - "oxlint": "1.29.0", + "oxlint": "1.32.0", + "oxlint-tsgolint": "0.8.3", "readline-sync": "1.4.10", "supertest": "7.1.4", "testcontainers": "11.4.0", diff --git a/backend/scripts/openapi.ts b/backend/scripts/openapi.ts index 611db9a1fe4c..7fd6897a74ae 100644 --- a/backend/scripts/openapi.ts +++ b/backend/scripts/openapi.ts @@ -219,14 +219,15 @@ function addRateLimit( metadata: EndpointMetadata | undefined, ): void { if (metadata === undefined || metadata.rateLimit === undefined) return; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + // oxlint-disable-next-line no-unsafe-assignment const okResponse = operation.responses["200"]; if (okResponse === undefined) return; operation.description += getRateLimitDescription(metadata.rateLimit); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + // oxlint-disable-next-line no-unsafe-assignment no-unsafe-member-access okResponse["headers"] = { + // oxlint-disable-next-line no-unsafe-member-access ...okResponse["headers"], "x-ratelimit-limit": { schema: { type: "integer" }, diff --git a/backend/src/api/controllers/dev.ts b/backend/src/api/controllers/dev.ts index 31bd30d980a5..f5cb00e0f55d 100644 --- a/backend/src/api/controllers/dev.ts +++ b/backend/src/api/controllers/dev.ts @@ -247,7 +247,7 @@ async function updateUser(uid: string): Promise { lbPersonalBests[mode.mode][mode.mode2] = {}; } - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access lbPersonalBests[mode.mode][mode.mode2][mode.language] = entry; } diff --git a/backend/src/api/controllers/user.ts b/backend/src/api/controllers/user.ts index c0d5213d1f95..964602852f10 100644 --- a/backend/src/api/controllers/user.ts +++ b/backend/src/api/controllers/user.ts @@ -553,7 +553,7 @@ export async function getUser(req: MonkeyRequest): Promise { uid, ); } catch (e) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access if (e.code === "auth/user-not-found") { throw new MonkeyError( 404, diff --git a/backend/src/api/ts-rest-adapter.ts b/backend/src/api/ts-rest-adapter.ts index 0f22d54e18da..292d4dac6ee4 100644 --- a/backend/src/api/ts-rest-adapter.ts +++ b/backend/src/api/ts-rest-adapter.ts @@ -11,7 +11,7 @@ export function callController< TParams, TResponse, //ignoring as it might be used in the future - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters + // oxlint-disable-next-line no-unnecessary-type-parameters TStatus = 200, >( handler: MonkeyHandler, diff --git a/backend/src/api/types.ts b/backend/src/api/types.ts index 4c81395f3ba4..754ddf9dcb27 100644 --- a/backend/src/api/types.ts +++ b/backend/src/api/types.ts @@ -2,7 +2,7 @@ import { TsRestRequest as TsRestRequestGeneric } from "@ts-rest/express"; import { Request as ExpressRequest } from "express"; import { Context } from "../middlewares/context"; -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// oxlint-disable-next-line no-explicit-any export type TsRestRequest = TsRestRequestGeneric; export type ExpressRequestWithContext = { diff --git a/backend/src/dal/connections.ts b/backend/src/dal/connections.ts index 0500dc181890..db10e074872d 100644 --- a/backend/src/dal/connections.ts +++ b/backend/src/dal/connections.ts @@ -74,7 +74,7 @@ export async function create( return created; } catch (e) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access if (e.name === "MongoServerError" && e.code === 11000) { const existing = await getCollection().findOne( { key }, diff --git a/backend/src/dal/leaderboards.ts b/backend/src/dal/leaderboards.ts index 45ebda2346f4..f2a48658f6af 100644 --- a/backend/src/dal/leaderboards.ts +++ b/backend/src/dal/leaderboards.ts @@ -85,7 +85,7 @@ export async function get( return leaderboard; } catch (e) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access if (e.error === 175) { //QueryPlanKilled, collection was removed during the query return false; @@ -162,7 +162,7 @@ export async function getRank( return results[0] ?? null; } } catch (e) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access if (e.error === 175) { //QueryPlanKilled, collection was removed during the query return false; @@ -393,7 +393,7 @@ async function createIndex( Logger.warning(`Index ${key} not matching, dropping and recreating...`); const existingIndex = (await getUsersCollection().listIndexes().toArray()) - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access .map((it) => it.name as string) .find((it) => it.startsWith(key)); diff --git a/backend/src/dal/user.ts b/backend/src/dal/user.ts index 67905f643427..c9d3fcfcc5ef 100644 --- a/backend/src/dal/user.ts +++ b/backend/src/dal/user.ts @@ -772,7 +772,7 @@ export async function getPersonalBests( ]); if (mode2 !== undefined) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access return user.personalBests?.[mode]?.[mode2] as PersonalBest; } diff --git a/backend/src/middlewares/auth.ts b/backend/src/middlewares/auth.ts index 002480a9374b..b7ba840a59a0 100644 --- a/backend/src/middlewares/auth.ts +++ b/backend/src/middlewares/auth.ts @@ -203,7 +203,7 @@ async function authenticateWithBearerToken( ); } - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access const errorCode = error?.errorInfo?.code as string | undefined; if (errorCode?.includes("auth/id-token-expired")) { diff --git a/backend/src/middlewares/utility.ts b/backend/src/middlewares/utility.ts index de8faf6fbfcf..ceb5b4b6c674 100644 --- a/backend/src/middlewares/utility.ts +++ b/backend/src/middlewares/utility.ts @@ -41,7 +41,7 @@ export function onlyAvailableOnDev(): RequestHandler { } export function getMetadata(req: TsRestRequestWithContext): EndpointMetadata { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access return (req.tsRestRoute["metadata"] ?? {}) as EndpointMetadata; } diff --git a/backend/src/utils/auth.ts b/backend/src/utils/auth.ts index 8f507398d696..a218010eb7c9 100644 --- a/backend/src/utils/auth.ts +++ b/backend/src/utils/auth.ts @@ -103,7 +103,7 @@ export async function sendForgotPasswordEmail(email: string): Promise { await emailQueue.sendForgotPasswordEmail(email, name, link); } catch (err) { if (isFirebaseError(err) && err.errorInfo.code !== "auth/user-not-found") { - // eslint-disable-next-line @typescript-eslint/only-throw-error + // oxlint-disable-next-line only-throw-error throw err; } } diff --git a/backend/src/utils/error.ts b/backend/src/utils/error.ts index e3a7f75a6bcd..733b137422c3 100644 --- a/backend/src/utils/error.ts +++ b/backend/src/utils/error.ts @@ -8,18 +8,18 @@ type FirebaseErrorParent = { errorInfo: FirebaseError; }; -// eslint-disable-next-line @typescript-eslint/no-explicit-any +// oxlint-disable-next-line no-explicit-any export function isFirebaseError(err: any): err is FirebaseErrorParent { return ( typeof err === "object" && "code" in err && "errorInfo" in err && "codePrefix" in err && - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access typeof err.errorInfo === "object" && - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access "code" in err.errorInfo && - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access "message" in err.errorInfo ); } diff --git a/backend/src/utils/etag.ts b/backend/src/utils/etag.ts index e74243f91b65..bfacb4c723ca 100644 --- a/backend/src/utils/etag.ts +++ b/backend/src/utils/etag.ts @@ -13,7 +13,7 @@ export function createETagGenerator(options: { return function generateETag(body, encoding) { const buf = !Buffer.isBuffer(body) ? Buffer.from(body, encoding) : body; - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call + // oxlint-disable-next-line no-unsafe-assignment, no-unsafe-call const generatedTag: string = etag(buf, options); //custom code to add the version number diff --git a/backend/src/utils/misc.ts b/backend/src/utils/misc.ts index db39270808ad..4451e8eb3ff2 100644 --- a/backend/src/utils/misc.ts +++ b/backend/src/utils/misc.ts @@ -244,7 +244,7 @@ export function omit( ): Omit { const result = { ...obj }; for (const key of keys) { - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + // oxlint-disable-next-line no-dynamic-delete delete result[key]; } return result; diff --git a/backend/src/utils/prometheus.ts b/backend/src/utils/prometheus.ts index 6aa239e5501c..a396e87298c2 100644 --- a/backend/src/utils/prometheus.ts +++ b/backend/src/utils/prometheus.ts @@ -216,7 +216,7 @@ export function recordAuthTime( req: Request, ): void { // for some reason route is not in the types - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access const reqPath = req.baseUrl + req.route.path; let normalizedPath = "/"; @@ -237,7 +237,7 @@ const requestCountry = new Counter({ export function recordRequestCountry(country: string, req: Request): void { // for some reason route is not in the types - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access const reqPath = req.baseUrl + req.route.path; let normalizedPath = "/"; diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs deleted file mode 100644 index 20b6695b148e..000000000000 --- a/frontend/.eslintrc.cjs +++ /dev/null @@ -1,51 +0,0 @@ -/** @type {import("eslint").Linter.Config} */ -module.exports = { - root: true, - plugins: ["compat"], - extends: ["@monkeytype/eslint-config", "plugin:compat/recommended"], - globals: { - $: "readonly", - jQuery: "readonly", - ClipboardItem: "readonly", - grecaptcha: "readonly", - }, - env: { - browser: true, - es2021: true, - node: true, - }, - ignorePatterns: [ - "node_modules/", - "dist/", - "static/js/", - "__tests__/", - "jest.config.ts", - ], - settings: { - lintAllEsApis: true, - }, - rules: { - // Prevent accessing __testing outside of test files - "no-restricted-syntax": [ - "error", - { - selector: "MemberExpression[property.name='__testing']", - message: - "__testing should only be accessed in test files. Use the public API instead.", - }, - ], - }, - overrides: [ - { - // Allow __testing access in test files - files: [ - "**/__tests__/**/*.{js,ts,tsx}", - "**/*.{test,spec}.{js,ts,tsx}", - "**/tests/**/*.{js,ts,tsx}", - ], - rules: { - "no-restricted-syntax": "off", - }, - }, - ], -}; diff --git a/frontend/.oxlintrc-plugin.json b/frontend/.oxlintrc-plugin.json new file mode 100644 index 000000000000..e9b51939e06a --- /dev/null +++ b/frontend/.oxlintrc-plugin.json @@ -0,0 +1,8 @@ +{ + "ignorePatterns": ["node_modules", "dist", "coverage", ".firebase", ".turbo"], + "extends": ["../packages/oxlint-config/plugin.jsonc"], + "jsPlugins": ["eslint-plugin-compat"], + "rules": { + "compat/compat": "error" + } +} diff --git a/frontend/.oxlintrc.json b/frontend/.oxlintrc.json index b1862d283371..6796d4851155 100644 --- a/frontend/.oxlintrc.json +++ b/frontend/.oxlintrc.json @@ -1,35 +1,7 @@ { + "ignorePatterns": ["node_modules", "dist", "coverage", ".firebase", ".turbo"], "extends": [ "../packages/oxlint-config/index.jsonc" // "@monkeytype/oxlint-config" - ], - "overrides": [ - { - "files": ["__tests__/**/*.ts"], - "plugins": [ - "typescript", - "unicorn", - "oxc", - "import", - "node", - "promise", - "jest", - "vitest" - ], - "rules": { - "no-explicit-any": "allow", - "explicit-function-return-type": "off", - "no-array-for-each": "off", - "eqeqeq": "off", - "ban-ts-comment": "off" - } - }, - { - "files": ["__tests__/**/*.d.ts"], - "rules": { - "typescript/consistent-type-definitions": "off", - "typescript/no-empty-object-type": "off" - } - } ] } diff --git a/frontend/__tests__/commandline/util.spec.ts b/frontend/__tests__/commandline/util.spec.ts index ac69f9db29b3..27e7b988df62 100644 --- a/frontend/__tests__/commandline/util.spec.ts +++ b/frontend/__tests__/commandline/util.spec.ts @@ -158,7 +158,7 @@ describe("CommandlineUtils", () => { schema, }); - const inputCmd = cmd.subgroup?.list.at(cmd.subgroup?.list.length - 1); + const inputCmd = cmd.subgroup?.list[cmd.subgroup?.list.length - 1]; //THEN expect(cmd.subgroup?.list.map((it) => it.id)).toEqual([ diff --git a/frontend/__tests__/root/config-metadata.spec.ts b/frontend/__tests__/root/config-metadata.spec.ts index 04434bfd2f58..b6966eb8db57 100644 --- a/frontend/__tests__/root/config-metadata.spec.ts +++ b/frontend/__tests__/root/config-metadata.spec.ts @@ -16,7 +16,7 @@ describe("ConfigMeta", () => { }); it("should have changeRequiresRestart defined", () => { const configsRequiringRestarts = Object.entries(configMetadata) - .filter(([_key, value]) => value.changeRequiresRestart === true) + .filter(([_key, value]) => value.changeRequiresRestart) .map(([key]) => key) .sort(); diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 000000000000..82eea332a47e --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,28 @@ +import { defineConfig } from "eslint/config"; +import json from "@eslint/json"; + +export default defineConfig([ + // lint JSON files + { + files: ["**/*.json"], + plugins: { json }, + language: "json/json", + extends: ["json/recommended"], + }, + + // lint JSONC files + { + files: ["**/*.jsonc"], + plugins: { json }, + language: "json/jsonc", + extends: ["json/recommended"], + }, + + // lint JSON5 files + { + files: ["**/*.json5"], + plugins: { json }, + language: "json/json5", + extends: ["json/recommended"], + }, +]); diff --git a/frontend/package.json b/frontend/package.json index 2d4482084da5..b4c014a22ab3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,9 +4,9 @@ "private": true, "type": "module", "scripts": { - "eslint": "eslint \"./**/*.ts\"", - "oxlint": "oxlint .", - "lint": "npm run oxlint && npm run eslint", + "lint": "oxlint . --type-aware --type-check && oxlint . -c .oxlintrc-plugin.json", + "lint-fast": "oxlint .", + "lint-json": "eslint static/**/*.json", "check-assets": "tsx ./scripts/check-assets.ts", "audit": "vite-bundle-visualizer", "dep-graph": "madge -c -i \"dep-graph.png\" ./src/ts", @@ -37,13 +37,12 @@ ], "*.{ts,js}": [ "prettier --write", - "oxlint", - "eslint" + "oxlint --type-aware --type-check" ] }, "devDependencies": { + "@eslint/json": "0.14.0", "@fortawesome/fontawesome-free": "5.15.4", - "@monkeytype/eslint-config": "workspace:*", "@monkeytype/oxlint-config": "workspace:*", "@monkeytype/typescript-config": "workspace:*", "@types/canvas-confetti": "1.4.3", @@ -58,7 +57,7 @@ "@vitest/coverage-v8": "4.0.8", "autoprefixer": "10.4.20", "concurrently": "8.2.2", - "eslint": "8.57.1", + "eslint": "9.39.1", "eslint-plugin-compat": "6.0.2", "firebase-tools": "13.15.1", "fontawesome-subset": "4.4.0", @@ -66,7 +65,8 @@ "madge": "8.0.0", "magic-string": "0.30.17", "normalize.css": "8.0.1", - "oxlint": "1.29.0", + "oxlint": "1.32.0", + "oxlint-tsgolint": "0.8.3", "postcss": "8.4.31", "sass": "1.70.0", "subset-font": "2.3.0", diff --git a/frontend/scripts/fill-colors.js b/frontend/scripts/fill-colors.js index e3fbc612e743..826b19d7f147 100644 --- a/frontend/scripts/fill-colors.js +++ b/frontend/scripts/fill-colors.js @@ -1,4 +1,4 @@ -// eslint-disable no-require-imports +// oxlint-disable no-require-imports const fs = require("fs"); function main() { diff --git a/frontend/scripts/fix-quote-lengths.cjs b/frontend/scripts/fix-quote-lengths.cjs index 94cc7f718d6e..6cc19c1e7d68 100644 --- a/frontend/scripts/fix-quote-lengths.cjs +++ b/frontend/scripts/fix-quote-lengths.cjs @@ -1,4 +1,4 @@ -// eslint-disable no-require-imports +// oxlint-disable no-require-imports const fs = require("fs"); function fixQuoteLengths() { diff --git a/frontend/src/ts/ape/adapters/ts-rest-adapter.ts b/frontend/src/ts/ape/adapters/ts-rest-adapter.ts index 7f2a8ebbf8da..58c1363c492b 100644 --- a/frontend/src/ts/ape/adapters/ts-rest-adapter.ts +++ b/frontend/src/ts/ape/adapters/ts-rest-adapter.ts @@ -91,7 +91,7 @@ function buildApi(timeout: number): (args: ApiFetcherArgs) => Promise<{ }; } -/* eslint-disable @typescript-eslint/explicit-function-return-type */ +/* oxlint-disable explicit-function-return-type */ export function buildClient( contract: T, baseUrl: string, @@ -107,4 +107,4 @@ export function buildClient( }, }); } -/* eslint-enable @typescript-eslint/explicit-function-return-type */ +/* eslint-enable explicit-function-return-type */ diff --git a/frontend/src/ts/commandline/commandline.ts b/frontend/src/ts/commandline/commandline.ts index b24021d4717b..0654a6845ae1 100644 --- a/frontend/src/ts/commandline/commandline.ts +++ b/frontend/src/ts/commandline/commandline.ts @@ -595,7 +595,7 @@ function handleInputSubmit(): void { commandlineModal: modal, // @ts-expect-error this is fine - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + // oxlint-disable-next-line no-unsafe-assignment input: inputModeParams.command.inputValueConvert(inputValue), }); } else { diff --git a/frontend/src/ts/commandline/util.ts b/frontend/src/ts/commandline/util.ts index a75089d441b9..35e7347687ff 100644 --- a/frontend/src/ts/commandline/util.ts +++ b/frontend/src/ts/commandline/util.ts @@ -28,7 +28,7 @@ function getOptions(schema: T): undefined | z.infer[] { } export function buildCommandForConfigKey< - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters + // oxlint-disable-next-line no-unnecessary-type-parameters K extends keyof CommandlineConfigMetadataObject, >(key: K): Command { const configMeta = configMetadata[key]; diff --git a/frontend/src/ts/controllers/ad-controller.ts b/frontend/src/ts/controllers/ad-controller.ts index 9dd7cd258228..f705c2bfc2af 100644 --- a/frontend/src/ts/controllers/ad-controller.ts +++ b/frontend/src/ts/controllers/ad-controller.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* oxlint-disable no-unsafe-member-access */ import { debounce } from "throttle-debounce"; // import * as Numbers from "@monkeytype/util/numbers"; import * as ConfigEvent from "../observables/config-event"; @@ -176,7 +176,7 @@ export async function checkCookieblocker(): Promise { return; } //@ts-expect-error 3rd party ad code - // eslint-disable-next-line @typescript-eslint/no-unsafe-call + // oxlint-disable-next-line no-unsafe-call window.__tcfapi("getTCData", 2, (tcData, success) => { if (success as boolean) { if (tcData.eventStatus === "cmpuishown") { @@ -270,13 +270,13 @@ export function updateFooterAndVerticalAds(visible: boolean): void { export function showConsentPopup(): void { if (choice === "eg") { //@ts-expect-error 3rd party ad code, doesnt have types - // eslint-disable-next-line @typescript-eslint/no-unsafe-call + // oxlint-disable-next-line no-unsafe-call window.__tcfapi("displayConsentUi", 2, function () { // }); } else { //@ts-expect-error 3rd party ad code, doesnt have types - // eslint-disable-next-line @typescript-eslint/no-unsafe-call + // oxlint-disable-next-line no-unsafe-call ramp.showCmpModal(); } } diff --git a/frontend/src/ts/controllers/chart-controller.ts b/frontend/src/ts/controllers/chart-controller.ts index 32e4de9d6fd3..5ba57c811219 100644 --- a/frontend/src/ts/controllers/chart-controller.ts +++ b/frontend/src/ts/controllers/chart-controller.ts @@ -75,7 +75,7 @@ class ChartWithUpdateColors< TLabel = unknown, DatasetIds = never, > extends Chart { - // eslint-disable-next-line @typescript-eslint/no-useless-constructor + // oxlint-disable-next-line no-useless-constructor constructor( item: ChartItem, config: ChartConfiguration, @@ -102,7 +102,7 @@ class ChartWithUpdateColors< id: DatasetIds extends never ? never : "x" | DatasetIds, ): DatasetIds extends never ? never : CartesianScaleOptions { //@ts-expect-error its too difficult to figure out these types, but this works - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-return, no-unsafe-member-access return this.options.scales[id]; } } @@ -1269,7 +1269,7 @@ async function updateColors< //@ts-expect-error its too difficult to figure out these types, but this works chart.data.datasets[0].borderColor = (ctx): string => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // oxlint-disable-next-line no-unsafe-member-access const isPb = ctx.raw?.isPb as boolean; const color = isPb ? textcolor : maincolor; return color; diff --git a/frontend/src/ts/controllers/eg-ad-controller.ts b/frontend/src/ts/controllers/eg-ad-controller.ts index 3de85395c018..29b52806c24f 100644 --- a/frontend/src/ts/controllers/eg-ad-controller.ts +++ b/frontend/src/ts/controllers/eg-ad-controller.ts @@ -1,4 +1,4 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* oxlint-disable no-unsafe-member-access */ export function init(): void { $("head").append(`