chore: add oxlint (@miodec) (#6455)

Use oxlint for general linting to provide much quicker feedback. Keep
eslint for type-aware rules. Fully switch to oxlint once it supports
type-aware.
This commit is contained in:
Jack 2025-04-16 17:18:50 +02:00 committed by GitHub
parent 92d97c1fee
commit cac8835c77
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
72 changed files with 588 additions and 177 deletions

View file

@ -3,6 +3,7 @@
![ChartJs](https://img.shields.io/badge/Chart.js-FF6384?style=for-the-badge&logo=chartdotjs&logoColor=white)
![Eslint](https://img.shields.io/badge/eslint-4B32C3?style=for-the-badge&logo=eslint&logoColor=white)
![OXLint](https://img.shields.io/badge/oxlint-91ede9?style=for-the-badge&logoColor=white)
![Express](https://img.shields.io/badge/-Express-373737?style=for-the-badge&logo=Express&logoColor=white)
![Firebase](https://img.shields.io/badge/firebase-ffca28?style=for-the-badge&logo=firebase&logoColor=black)
![Fontawesome](https://img.shields.io/badge/fontawesome-538DD7?style=for-the-badge&logo=fontawesome&logoColor=white)

16
backend/.oxlintrc.json Normal file
View file

@ -0,0 +1,16 @@
{
"extends": [
"../packages/oxlint-config/index.json"
// "@monkeytype/oxlint-config"
],
"overrides": [
{
"files": ["__tests__/**"],
"plugins": ["jest", "vitest"],
"rules": {
"no-explicit-any": "allow",
"explicit-function-return-type": "off"
}
}
]
}

View file

@ -874,26 +874,24 @@ describe("result controller test", () => {
});
});
it("should fail invalid properties", async () => {
//GIVEN
//WHEN
const { body } = await mockApp
.post("/results")
.set("Authorization", `Bearer ${uid}`)
//TODO add all properties
.send({ result: { acc: 25 } })
.expect(422);
//THEN
/*
// it("should fail invalid properties ", async () => {
//GIVEN
//WHEN
// const { body } = await mockApp
// .post("/results")
// .set("Authorization", `Bearer ${uid}`)
// //TODO add all properties
// .send({ result: { acc: 25 } })
// .expect(422);
//THEN
/*
expect(body).toEqual({
message: "Invalid request data schema",
validationErrors: [
],
});
*/
});
// });
});
});

View file

@ -5,7 +5,6 @@ import { generateCurrentTestActivity } from "../../../src/api/controllers/user";
import * as UserDal from "../../../src/dal/user";
import * as AuthUtils from "../../../src/utils/auth";
import * as BlocklistDal from "../../../src/dal/blocklist";
import * as ApeKeys from "../../../src/dal/ape-keys";
import * as PresetDal from "../../../src/dal/preset";
import * as ConfigDal from "../../../src/dal/config";
import * as ResultDal from "../../../src/dal/result";
@ -586,11 +585,11 @@ describe("user controller test", () => {
expect(testsByDays[371]).toEqual(2024094); //2024-01
});
});
describe("delete user", () => {
describe("delete user ", () => {
const getUserMock = vi.spyOn(UserDal, "getPartialUser");
const deleteUserMock = vi.spyOn(UserDal, "deleteUser");
const firebaseDeleteUserMock = vi.spyOn(AuthUtils, "deleteUser");
const deleteAllApeKeysMock = vi.spyOn(ApeKeys, "deleteAllApeKeys");
const deleteAllApeKeysMock = vi.spyOn(ApeKeysDal, "deleteAllApeKeys");
const deleteAllPresetsMock = vi.spyOn(PresetDal, "deleteAllPresets");
const deleteConfigMock = vi.spyOn(ConfigDal, "deleteConfig");
const deleteAllResultMock = vi.spyOn(ResultDal, "deleteAll");
@ -894,13 +893,15 @@ describe("user controller test", () => {
data: null,
});
[
for (const it of [
resetUserMock,
deleteAllApeKeysMock,
deleteAllPresetsMock,
deleteAllResultsMock,
deleteConfigMock,
].forEach((it) => expect(it).toHaveBeenCalledWith(uid));
]) {
expect(it).toHaveBeenCalledWith(uid);
}
expect(purgeUserFromDailyLeaderboardsMock).toHaveBeenCalledWith(
uid,
(await Configuration.getLiveConfiguration()).dailyLeaderboards
@ -1178,19 +1179,18 @@ describe("user controller test", () => {
uid
);
});
it("should fail with unknown properties", async () => {
//WHEN
const { body } = await mockApp
.post("/users/optOutOfLeaderboards")
.set("Authorization", `Bearer ${uid}`)
.send({ extra: "value" });
//TODO.expect(422);
//THEN
/* TODO:
// it("should fail with unknown properties", async () => {
//WHEN
// const { body } = await mockApp
// .post("/users/optOutOfLeaderboards")
// .set("Authorization", `Bearer ${uid}`)
// .send({ extra: "value" });
//TODO.expect(422);
//THEN
/* TODO:
expect(body).toEqual({});
*/
});
// });
});
describe("update email", () => {
const authUpdateEmailMock = vi.spyOn(AuthUtils, "updateUserEmail");
@ -2280,7 +2280,7 @@ describe("user controller test", () => {
updateLbMemoryMock.mockReset();
});
it("should update lb ", async () => {
it("should update lb", async () => {
//WHEN
const { body } = await mockApp
.patch("/users/leaderboardMemory")
@ -2388,7 +2388,7 @@ describe("user controller test", () => {
addThemeMock.mockReset();
});
it("should add ", async () => {
it("should add", async () => {
//GIVEN
const addedTheme: UserDal.DBCustomTheme = {
_id: new ObjectId(),
@ -3812,6 +3812,7 @@ async function enablePremiumFeatures(premium: boolean): Promise<void> {
);
}
// eslint-disable-next-line no-unused-vars
async function enableAdminFeatures(enabled: boolean): Promise<void> {
const mockConfig = _.merge(await configuration, {
admin: { endpointsEnabled: enabled },

View file

@ -93,7 +93,7 @@ describe("LeaderboardsDal", () => {
});
it("should not include discord properties for users without discord connection", async () => {
//GIVEN
const rank1 = await createUser(lbBests(pb(90), pb(100, 90, 2)), {
await createUser(lbBests(pb(90), pb(100, 90, 2)), {
discordId: undefined,
discordAvatar: undefined,
});
@ -138,10 +138,10 @@ describe("LeaderboardsDal", () => {
it("should update public speedHistogram for time english 15", async () => {
//GIVEN
const rank1 = await createUser(lbBests(pb(10), pb(60)));
const rank2 = await createUser(lbBests(pb(24)));
const rank3 = await createUser(lbBests(pb(28)));
const rank4 = await createUser(lbBests(pb(31)));
await createUser(lbBests(pb(10), pb(60)));
await createUser(lbBests(pb(24)));
await createUser(lbBests(pb(28)));
await createUser(lbBests(pb(31)));
//WHEN
await LeaderboardsDal.update("time", "15", "english");
@ -153,10 +153,10 @@ describe("LeaderboardsDal", () => {
it("should update public speedHistogram for time english 60", async () => {
//GIVEN
const rank1 = await createUser(lbBests(pb(60), pb(20)));
const rank2 = await createUser(lbBests(undefined, pb(21)));
const rank3 = await createUser(lbBests(undefined, pb(110)));
const rank4 = await createUser(lbBests(undefined, pb(115)));
await createUser(lbBests(pb(60), pb(20)));
await createUser(lbBests(undefined, pb(21)));
await createUser(lbBests(undefined, pb(110)));
await createUser(lbBests(undefined, pb(115)));
//WHEN
await LeaderboardsDal.update("time", "60", "english");
@ -253,7 +253,7 @@ describe("LeaderboardsDal", () => {
it("should create leaderboard without premium if feature disabled", async () => {
await enablePremiumFeatures(false);
//GIVEN
const lifetime = await createUser(lbBests(pb(3)), premium(-1));
// const lifetime = await createUser(lbBests(pb(3)), premium(-1));
//WHEN
await LeaderboardsDal.update("time", "15", "english");
@ -358,12 +358,12 @@ function premium(expirationDeltaSeconds: number) {
};
}
interface ExpectedLbEntry {
type ExpectedLbEntry = {
rank: number;
user: UserDal.DBUser;
badgeId?: number;
isPremium?: boolean;
}
};
async function enablePremiumFeatures(premium: boolean): Promise<void> {
const mockConfig = _.merge(await configuration, {

View file

@ -15,7 +15,6 @@ import {
} from "@monkeytype/contracts/schemas/api";
import * as Prometheus from "../../src/utils/prometheus";
import { TsRestRequestWithContext } from "../../src/api/types";
import { error } from "console";
const mockDecodedToken: DecodedIdToken = {
uid: "123456789",

View file

@ -2,7 +2,6 @@ import { Collection, Db, MongoClient, WithId } from "mongodb";
import { afterAll, beforeAll, afterEach } from "vitest";
import * as MongoDbMock from "vitest-mongodb";
import { MongoDbMockConfig } from "./global-setup";
import { enableRateLimitExpects } from "./__testData__/rate-limit";
process.env["MODE"] = "dev";
//process.env["MONGOMS_DISTRO"] = "ubuntu-22.04";
@ -29,7 +28,9 @@ beforeAll(async () => {
getDb: (): Db => db,
collection: <T>(name: string): Collection<WithId<T>> =>
db.collection<WithId<T>>(name),
close: () => {},
close: () => {
//
},
}));
vi.mock("../src/utils/logger", () => ({

View file

@ -4,7 +4,9 @@
"license": "GPL-3.0",
"private": true,
"scripts": {
"lint": "eslint \"./src/**/*.ts\"",
"eslint": "eslint \"./src/**/*.ts\"",
"oxlint": "oxlint .",
"lint": "npm run oxlint && npm run eslint",
"build": "npm run gen-docs && tsc --build",
"watch": "tsc --build --watch",
"clean": "tsc --build --clean",
@ -63,6 +65,7 @@
},
"devDependencies": {
"@monkeytype/eslint-config": "workspace:*",
"@monkeytype/oxlint-config": "workspace:*",
"@monkeytype/typescript-config": "workspace:*",
"@redocly/cli": "1.28.5",
"@types/bcrypt": "5.0.2",
@ -89,6 +92,7 @@
"eslint-watch": "8.0.0",
"ioredis-mock": "7.4.0",
"openapi3-ts": "2.0.2",
"oxlint": "0.16.6",
"readline-sync": "1.4.10",
"supertest": "6.2.3",
"tsx": "4.16.2",

View file

@ -74,9 +74,9 @@ export async function handleReports(
const reportsFromDb = await ReportDAL.getReports(reportIds);
const reportById = new Map(reportsFromDb.map((it) => [it.id, it]));
const existingReportIds = reportsFromDb.map((report) => report.id);
const existingReportIds = new Set(reportsFromDb.map((report) => report.id));
const missingReportIds = reportIds.filter(
(reportId) => !existingReportIds.includes(reportId)
(reportId) => !existingReportIds.has(reportId)
);
if (missingReportIds.length > 0) {

View file

@ -13,7 +13,7 @@ import {
GetLeaderboardQuery,
GetLeaderboardRankQuery,
GetLeaderboardRankResponse,
GetLeaderboardResponse as GetLeaderboardResponse,
GetLeaderboardResponse,
GetWeeklyXpLeaderboardQuery,
GetWeeklyXpLeaderboardRankQuery,
GetWeeklyXpLeaderboardRankResponse,

View file

@ -66,7 +66,7 @@ import {
GetProfileQuery,
GetProfileResponse,
GetStatsResponse,
GetStreakResponseSchema,
GetStreakResponse,
GetTagsResponse,
GetTestActivityResponse,
GetUserInboxResponse,
@ -78,7 +78,7 @@ import {
ReportUserRequest,
SetStreakHourOffsetRequest,
TagIdPathParams,
UpdateEmailRequestSchema,
UpdateEmailRequest,
UpdateLeaderboardMemoryRequest,
UpdatePasswordRequest,
UpdateUserInboxRequest,
@ -444,7 +444,7 @@ export async function checkName(
}
export async function updateEmail(
req: MonkeyRequest<undefined, UpdateEmailRequestSchema>
req: MonkeyRequest<undefined, UpdateEmailRequest>
): Promise<MonkeyResponse> {
const { uid } = req.ctx.decodedToken;
let { newEmail, previousEmail } = req.body;
@ -1246,7 +1246,7 @@ export async function getCurrentTestActivity(
export async function getStreak(
req: MonkeyRequest
): Promise<GetStreakResponseSchema> {
): Promise<GetStreakResponse> {
const { uid } = req.ctx.decodedToken;
const user = await UserDAL.getPartialUser(uid, "streak", ["streak"]);

View file

@ -48,10 +48,10 @@ export async function updateTags(
});
if (!result) throw new MonkeyError(404, "Result not found");
const userTags = await getTags(uid);
const userTagIds = userTags.map((tag) => tag._id.toString());
const userTagIds = new Set(userTags.map((tag) => tag._id.toString()));
let validTags = true;
tags.forEach((tagId) => {
if (!userTagIds.includes(tagId)) validTags = false;
if (!userTagIds.has(tagId)) validTags = false;
});
if (!validTags) {
throw new MonkeyError(422, "One of the tag id's is not valid");

View file

@ -1023,8 +1023,9 @@ export async function updateInbox(
//flatMap rewards
const rewards: AllRewards[] = [...toBeRead, ...toBeDeleted]
.filter((it) => !it.read)
.reduce((arr, current) => {
return [...arr, ...current.rewards];
.reduce((arr: AllRewards[], current) => {
return arr.concat(current.rewards);
}, []);
const xpGain = rewards

View file

@ -55,7 +55,7 @@ export async function init(): Promise<void> {
try {
transporter = nodemailer.createTransport({
host: EMAIL_HOST,
secure: EMAIL_PORT === "465" ? true : false,
secure: EMAIL_PORT === "465",
port: parseInt(EMAIL_PORT ?? "578", 10),
auth: {
user: EMAIL_USER,

View file

@ -71,10 +71,10 @@ function initialiseLimiters(): Record<RateLimiterId, RateLimitRequestHandler> {
});
};
return keys.reduce(
(output, key) => ({ ...output, [key]: convert(limits[key]) }),
{}
) as Record<RateLimiterId, RateLimitRequestHandler>;
return keys.reduce((output, key) => {
output[key] = convert(limits[key]);
return output;
}, {}) as Record<RateLimiterId, RateLimitRequestHandler>;
}
function convertWindowToMs(window: Window): number {
@ -198,7 +198,7 @@ export async function incrementBadAuth(
try {
const key = getKey(req, res);
await badAuthRateLimiter.penalty(key, penalty);
} catch (error) {}
} catch {}
}
export const webhookLimit = rateLimit({

View file

@ -198,9 +198,13 @@ export function isDevEnvironment(): boolean {
* @param data database object with `_id: ObjectId`
* @returns api object with `id: string`
*/
// oxlint doesnt understand ts overloading
// eslint-disable-next-line no-redeclare
export function replaceObjectId<T extends { _id: ObjectId }>(
data: T
): T & { _id: string };
// eslint-disable-next-line no-redeclare
export function replaceObjectId<T extends { _id: ObjectId }>(
data: T | null
): (T & { _id: string }) | null;

15
frontend/.oxlintrc.json Normal file
View file

@ -0,0 +1,15 @@
{
"extends": [
"../packages/oxlint-config/index.json"
// "@monkeytype/oxlint-config"
],
"overrides": [
{
"files": ["__tests__/**/*.ts"],
"rules": {
"no-explicit-any": "allow",
"explicit-function-return-type": "off"
}
}
]
}

View file

@ -1116,7 +1116,7 @@ describe("test-activity-calendar.ts", () => {
expect(days[366]).toHaveLevel(0);
expect(days[367]).toBeDate("2024-06-12").toHaveTests(1);
expect(days[368]).toBeFiller;
expect(days[368]).toBeFiller();
});
it("increments in new year", () => {
//GIVEN

View file

@ -1,3 +1,3 @@
export const setup = () => {
export const setup = (): void => {
process.env.TZ = "UTC";
};

View file

@ -1,4 +1,4 @@
import { replace as replace } from "../../src/ts/test/british-english";
import { replace } from "../../src/ts/test/british-english";
import Config from "../../src/ts/config";
describe("british-english", () => {

View file

@ -1,9 +1,6 @@
import { getDefaultConfig } from "../../src/ts/constants/default-config";
import { migrateConfig } from "../../src/ts/utils/config";
import {
PartialConfig,
ShowAverageSchema,
} from "@monkeytype/contracts/schemas/configs";
import { PartialConfig } from "@monkeytype/contracts/schemas/configs";
describe("config.ts", () => {
describe("migrateConfig", () => {

View file

@ -1,3 +1,4 @@
// eslint-disable no-useless-escape
import { Difficulty, Mode, Mode2 } from "@monkeytype/contracts/schemas/shared";
import { compressToURI } from "lz-ts";
import * as UpdateConfig from "../../src/ts/config";
@ -50,7 +51,9 @@ describe("url-handler", () => {
findGetParameterMock.mockImplementation((override) => override);
});
afterEach(() => {});
afterEach(() => {
//
});
it("handles null", () => {
//GIVEN

View file

@ -1,3 +1,4 @@
/* eslint-disable import/no-unresolved */
import eslint, { format, failAfterError } from "gulp-eslint-new";
import gulp from "gulp";
import {

View file

@ -4,7 +4,9 @@
"private": true,
"type": "module",
"scripts": {
"lint": "eslint \"./**/*.ts\"",
"eslint": "eslint \"./**/*.ts\"",
"oxlint": "oxlint .",
"lint": "npm run oxlint && npm run eslint",
"validate-json": "npx gulp validate-json-schema",
"audit": "vite-bundle-visualizer",
"dep-graph": "madge -c -i \"dep-graph.png\" ./src/ts",
@ -31,6 +33,7 @@
"devDependencies": {
"@fortawesome/fontawesome-free": "5.15.4",
"@monkeytype/eslint-config": "workspace:*",
"@monkeytype/oxlint-config": "workspace:*",
"@monkeytype/typescript-config": "workspace:*",
"@types/canvas-confetti": "1.4.3",
"@types/chartjs-plugin-trendline": "1.0.1",
@ -54,6 +57,7 @@
"happy-dom": "15.10.2",
"madge": "8.0.0",
"normalize.css": "8.0.1",
"oxlint": "0.16.6",
"postcss": "8.4.31",
"sass": "1.70.0",
"subset-font": "2.3.0",
@ -66,6 +70,7 @@
"vite-plugin-html-inject": "1.1.2",
"vite-plugin-inspect": "0.8.3",
"vite-plugin-minify": "2.1.0",
"vite-plugin-oxlint": "1.3.1",
"vite-plugin-pwa": "0.20.0",
"vitest": "2.1.9"
},

View file

@ -1,7 +1,8 @@
// eslint-disable no-require-imports
const fs = require("fs");
function main() {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
const listFile = JSON.parse(
fs.readFileSync("../static/themes/_list.json", {
encoding: "utf8",

View file

@ -1,3 +1,4 @@
// eslint-disable no-require-imports
const fs = require("fs");
function fixQuoteLengths() {

View file

@ -87,9 +87,9 @@ export function getFontawesomeConfig(debug = false): FontawesomeConfig {
}
}
const usedClasses = new Array(...usedClassesSet).sort();
const allModuleClasses = Object.values(modules2).flatMap((it) => it);
const icons = usedClasses.filter((it) => !allModuleClasses.includes(it));
const usedClasses = [...usedClassesSet].sort();
const allModuleClasses = new Set(Object.values(modules2).flatMap((it) => it));
const icons = usedClasses.filter((it) => !allModuleClasses.has(it));
const solid = icons.filter((it) => iconSet.solid.includes(it));
const regular = icons.filter((it) => iconSet.regular.includes(it));

View file

@ -1,3 +1,4 @@
// eslint-disable no-require-imports
const fs = require("fs");
const Ajv = require("ajv");
const ajv = new Ajv();

View file

@ -36,7 +36,7 @@ function buildApi(timeout: number): (args: ApiFetcherArgs) => Promise<{
const usePolyfill = AbortSignal?.timeout === undefined;
request.fetchOptions = {
...(request.fetchOptions || {}),
...request.fetchOptions,
signal: usePolyfill
? timeoutSignal(timeout)
: AbortSignal.timeout(timeout),

View file

@ -267,7 +267,7 @@ export function setFunbox(
}
}
const val = funbox ? funbox : "none";
const val = funbox || "none";
config.funbox = val;
saveToLocalStorage("funbox", nosave);
ConfigEvent.dispatch("funbox", config.funbox);

View file

@ -226,7 +226,7 @@ async function readyFunction(
console.debug(`account controller ready`);
if (authInitialisedAndConnected) {
void PSA.show();
console.debug(`auth state changed, user ${user ? true : false}`);
console.debug(`auth state changed, user ${user ? "true" : "false"}`);
console.debug(user);
if (user) {
await loadUser(user);

View file

@ -560,7 +560,7 @@ export const accountHistory = new ChartWithUpdateColors<
if (diff === undefined) {
diff = "normal";
}
label += "\n" + `difficulty: ${diff}`;
label += `\ndifficulty: ${diff}`;
label +=
"\n" +

View file

@ -38,6 +38,8 @@ export class SnapshotInitError extends Error {
constructor(message: string, public responseCode: number) {
super(message);
this.name = "SnapshotInitError";
// TODO INVESTIGATE
// oxlint-disable-next-line
this.responseCode = responseCode;
}
}

View file

@ -76,7 +76,7 @@ type Option = {
mandatory: boolean;
};
const groupsUsingSelect = ["language", "funbox", "tags"];
const groupsUsingSelect = new Set(["language", "funbox", "tags"]);
const groupSelects: Partial<Record<keyof ResultFilters, SlimSelect>> = {};
// current activated filter
@ -315,7 +315,7 @@ export function updateActive(): void {
}
}
if (groupsUsingSelect.includes(group)) {
if (groupsUsingSelect.has(group)) {
const option = $(
`.pageAccount .group.filterButtons .filterGroup[group="${group}"] option[value="${filter}"]`
);

View file

@ -293,7 +293,7 @@ export async function refresh(
continue;
}
const key = rowKeys[i] as string;
const bump = row === "row3" && (i === 3 || i === 6) ? true : false;
const bump = row === "row3" && (i === 3 || i === 6);
let keyDisplay = key[0] as string;
let letterStyle = "";
if (Config.keymapLegendStyle === "blank") {

View file

@ -243,10 +243,10 @@ export async function update(
let socials = false;
if (!banned) {
bio = profile.details?.bio ?? "" ? true : false;
bio = !!(profile.details?.bio ?? "");
details.find(".bio .value").text(profile.details?.bio ?? "");
keyboard = profile.details?.keyboard ?? "" ? true : false;
keyboard = !!(profile.details?.keyboard ?? "");
details.find(".keyboard .value").text(profile.details?.keyboard ?? "");
if (

View file

@ -425,7 +425,7 @@ async function setup(modalEl: HTMLElement): Promise<void> {
)) {
button.addEventListener("click", (e) => {
state.removeFancyTypographyEnabled =
(e.target as HTMLButtonElement).value === "true" ? true : false;
(e.target as HTMLButtonElement).value === "true";
updateUI();
});
}
@ -435,7 +435,7 @@ async function setup(modalEl: HTMLElement): Promise<void> {
)) {
button.addEventListener("click", (e) => {
state.replaceControlCharactersEnabled =
(e.target as HTMLButtonElement).value === "true" ? true : false;
(e.target as HTMLButtonElement).value === "true";
updateUI();
});
}
@ -445,7 +445,7 @@ async function setup(modalEl: HTMLElement): Promise<void> {
)) {
button.addEventListener("click", (e) => {
state.removeZeroWidthCharactersEnabled =
(e.target as HTMLButtonElement).value === "true" ? true : false;
(e.target as HTMLButtonElement).value === "true";
updateUI();
});
}
@ -455,7 +455,7 @@ async function setup(modalEl: HTMLElement): Promise<void> {
)) {
button.addEventListener("click", (e) => {
state.customTextPipeDelimiter =
(e.target as HTMLButtonElement).value === "true" ? true : false;
(e.target as HTMLButtonElement).value === "true";
if (state.customTextPipeDelimiter && state.customTextLimits.word !== "") {
state.customTextLimits.word = "";
}

View file

@ -73,7 +73,7 @@ export async function getQuoteStats(
}
function refreshStars(force?: number): void {
const limit = force ? force : rating;
const limit = force ?? rating;
$(`#quoteRateModal .star`).removeClass("active");
for (let i = 1; i <= limit; i++) {
$(`#quoteRateModal .star[data-rating=${i}]`).addClass("active");

View file

@ -69,11 +69,11 @@ function applyQuoteLengthFilter(quotes: Quote[]): Quote[] {
return quotes;
}
const quoteLengthFilter = quoteLengthFilterValue.map((filterValue) =>
parseInt(filterValue, 10)
const quoteLengthFilter = new Set(
quoteLengthFilterValue.map((filterValue) => parseInt(filterValue, 10))
);
const filteredQuotes = quotes.filter((quote) =>
quoteLengthFilter.includes(quote.group)
quoteLengthFilter.has(quote.group)
);
return filteredQuotes;

View file

@ -28,7 +28,10 @@ for (const [name, data] of Object.entries(metadata)) {
};
}
// oxlint doesnt understand ts overloading
// eslint-disable-next-line no-redeclare
export function get(funboxName: FunboxName): FunboxMetadataWithFunctions;
// eslint-disable-next-line no-redeclare
export function get(funboxNames: FunboxName[]): FunboxMetadataWithFunctions[];
export function get(
funboxNameOrNames: FunboxName | FunboxName[]

View file

@ -1,6 +1,6 @@
import { Section } from "../utils/json-data";
const bannedChars = ["—", "_", " "];
const bannedChars = new Set(["—", "_", " "]);
const maxWords = 100;
const apiURL = "https://poetrydb.org/random";
@ -21,7 +21,7 @@ export class Poem extends Section {
for (const word of this.words) {
let scrubbed = "";
for (const char of word) {
if (!bannedChars.includes(char)) {
if (!bannedChars.has(char)) {
scrubbed += char;
}
}

View file

@ -2,7 +2,7 @@ import { lastElementFromArray } from "../utils/arrays";
import { mean, roundTo2 } from "@monkeytype/util/numbers";
import * as TestState from "./test-state";
const keysToTrack = [
const keysToTrack = new Set([
"NumpadMultiply",
"NumpadSubtract",
"NumpadAdd",
@ -71,7 +71,7 @@ const keysToTrack = [
"Enter",
"Tab",
"NoCode", //android (smells) and some keyboards might send no location data - need to use this as a fallback
];
]);
type KeypressTimings = {
spacing: {
@ -278,7 +278,7 @@ export function forceKeyup(now: number): void {
let noCodeIndex = 0;
export function recordKeyupTime(now: number, key: string): void {
if (!keysToTrack.includes(key)) return;
if (!keysToTrack.has(key)) return;
if (key === "NoCode") {
noCodeIndex--;
@ -300,7 +300,7 @@ export function recordKeyupTime(now: number, key: string): void {
}
export function recordKeydownTime(now: number, key: string): void {
if (!keysToTrack.includes(key)) {
if (!keysToTrack.has(key)) {
console.debug("Key not tracked", key);
return;
}

View file

@ -487,7 +487,7 @@ export async function init(): Promise<void> {
return;
}
const beforeHasNumbers = TestWords.hasNumbers ? true : false;
const beforeHasNumbers = TestWords.hasNumbers;
let hasNumbers = false;

View file

@ -319,15 +319,16 @@ export async function updateHintsPosition(): Promise<void> {
const leftmostIndx = isLanguageRTL
? parseInt(hintEl.dataset["length"] ?? "1") - 1
: 0;
let newLeft = (
letterElements?.[letterIndices?.[leftmostIndx] ?? 0] as HTMLElement
).offsetLeft;
const el = letterElements?.[
letterIndices?.[leftmostIndx] ?? 0
] as HTMLElement;
let newLeft = el.offsetLeft;
const lettersWidth =
letterIndices?.reduce(
(accum, curr) =>
accum + (letterElements?.[curr] as HTMLElement).offsetWidth,
0
) ?? 0;
letterIndices?.reduce((accum, curr) => {
const el = letterElements?.[curr] as HTMLElement;
return accum + el.offsetWidth;
}, 0) ?? 0;
newLeft += lettersWidth / 2;
hintEl.style.left = newLeft.toString() + "px";

View file

@ -162,13 +162,13 @@ export function update(): void {
} else if (Config.timerStyle === "text") {
if (outof === 0) {
if (timerNumberElement !== null) {
timerNumberElement.innerHTML =
"<div>" + `${TestInput.input.getHistory().length}` + "</div>";
timerNumberElement.innerHTML = `<div>${
TestInput.input.getHistory().length
}</div>`;
}
} else {
if (timerNumberElement !== null) {
timerNumberElement.innerHTML =
"<div>" + `${getCurrentCount()}/${outof}` + "</div>";
timerNumberElement.innerHTML = `<div>${getCurrentCount()}/${outof}</div>`;
}
}
} else if (Config.timerStyle === "mini") {
@ -187,8 +187,9 @@ export function update(): void {
} else if (Config.mode === "zen") {
if (Config.timerStyle === "text") {
if (timerNumberElement !== null) {
timerNumberElement.innerHTML =
"<div>" + `${TestInput.input.getHistory().length}` + "</div>";
timerNumberElement.innerHTML = `<div>${
TestInput.input.getHistory().length
}</div>`;
}
} else {
if (miniTimerNumberElement !== null) {

View file

@ -21,7 +21,9 @@ export type FallbackOptions = {
};
export class Formatting {
constructor(private config: ConfigType) {}
constructor(private config: ConfigType) {
//
}
typingSpeed(
wpm: number | null | undefined,

View file

@ -135,7 +135,7 @@ const qwertyKeycodeKeymap: Keycode[][] = [
["Space"],
];
const leftSideKeys: Keycode[] = [
const leftSideKeys: Set<Keycode> = new Set([
"Backquote",
"Digit1",
"Digit2",
@ -166,9 +166,9 @@ const leftSideKeys: Keycode[] = [
"KeyB",
"Space",
];
]);
const rightSideKeys: Keycode[] = [
const rightSideKeys: Set<Keycode> = new Set([
"Digit6",
"Digit7",
"Digit8",
@ -227,7 +227,7 @@ const rightSideKeys: Keycode[] = [
"NumpadEnter",
"Space",
];
]);
/**
* Converts a key to a keycode based on a layout
@ -275,8 +275,8 @@ export function keycodeToKeyboardSide(keycode: Keycode): {
leftSide: boolean;
rightSide: boolean;
} {
const left = leftSideKeys.includes(keycode);
const right = rightSideKeys.includes(keycode);
const left = leftSideKeys.has(keycode);
const right = rightSideKeys.has(keycode);
return { leftSide: left, rightSide: right };
}

View file

@ -248,6 +248,8 @@ type LastIndex = {
lastIndexOfRegex(regex: RegExp): number;
} & string;
// TODO INVESTIGATE IF THIS IS NEEDED
// eslint-disable-next-line no-extend-native
(String.prototype as LastIndex).lastIndexOfRegex = function (
regex: RegExp
): number {
@ -654,8 +656,12 @@ export function isObject(obj: unknown): obj is Record<string, unknown> {
return typeof obj === "object" && !Array.isArray(obj) && obj !== null;
}
// oxlint doesnt understand ts overloading
// eslint-disable-next-line no-redeclare
export function deepClone<T>(obj: T[]): T[];
// eslint-disable-next-line no-redeclare
export function deepClone<T extends object>(obj: T): T;
// eslint-disable-next-line no-redeclare
export function deepClone<T>(obj: T): T;
export function deepClone<T>(obj: T | T[]): T | T[] {
// Check if the value is a primitive (not an object or array)

View file

@ -1,10 +1,14 @@
import { checker } from "vite-plugin-checker";
import oxlintPlugin from "vite-plugin-oxlint";
import Inspect from "vite-plugin-inspect";
import path from "node:path";
/** @type {import("vite").UserConfig} */
export default {
plugins: [
oxlintPlugin({
configFile: path.resolve(__dirname, "./.oxlintrc.json"),
}),
checker({
typescript: {
tsconfigPath: path.resolve(__dirname, "./tsconfig.json"),

View file

@ -72,7 +72,8 @@
"esbenp.prettier-vscode",
"vitest.explorer",
"huntertran.auto-markdown-toc",
"dbaeumer.vscode-eslint"
"dbaeumer.vscode-eslint",
"oxc.oxc-vscode"
]
}
}

View file

@ -13,6 +13,10 @@
"lint-be": "turbo run lint --filter @monkeytype/backend",
"lint-fe": "turbo run lint --filter @monkeytype/frontend",
"lint-pkg": "turbo run lint --filter=\"./packages/*\"",
"oxlint": "turbo run oxlint",
"oxlint-be": "turbo run oxlint --filter @monkeytype/backend",
"oxlint-fe": "turbo run oxlint --filter @monkeytype/frontend",
"oxlint-pkg": "turbo run oxlint --filter=\"./packages/*\"",
"build": "turbo run build",
"build-be": "turbo run build --filter @monkeytype/backend",
"build-fe": "turbo run build --filter @monkeytype/frontend",
@ -62,10 +66,12 @@
"@monkeytype/release": "workspace:*",
"@vitest/coverage-v8": "2.1.9",
"conventional-changelog": "6.0.0",
"eslint": "8.57.1",
"husky": "8.0.1",
"knip": "2.19.2",
"lint-staged": "13.2.3",
"only-allow": "1.2.1",
"oxlint": "0.16.6",
"prettier": "2.8.8",
"turbo": "2.3.3",
"vitest": "2.1.9"
@ -76,6 +82,7 @@
],
"*.{ts,js}": [
"prettier --write",
"oxlint",
"eslint"
]
},

View file

@ -0,0 +1,6 @@
{
"extends": [
"../oxlint-config/index.json"
// "@monkeytype/oxlint-config"
]
}

View file

@ -7,7 +7,9 @@
"test": "vitest run",
"madge": " madge --circular --extensions ts ./src",
"ts-check": "tsc --noEmit",
"lint": "eslint \"./**/*.ts\""
"eslint": "eslint \"./src/**/*.ts\"",
"oxlint": "oxlint .",
"lint": "npm run oxlint && npm run eslint"
},
"peerDependencies": {
"@ts-rest/core": "3.51.0",
@ -20,6 +22,7 @@
"chokidar": "3.6.0",
"eslint": "8.57.1",
"madge": "8.0.0",
"oxlint": "0.16.6",
"tsup": "8.4.0",
"typescript": "5.5.4",
"vitest": "2.1.9"

View file

@ -85,7 +85,7 @@ export const UpdateEmailRequestSchema = z.object({
newEmail: UserEmailSchema,
previousEmail: UserEmailSchema,
});
export type UpdateEmailRequestSchema = z.infer<typeof UpdateEmailRequestSchema>;
export type UpdateEmailRequest = z.infer<typeof UpdateEmailRequestSchema>;
export const UpdatePasswordRequestSchema = z.object({
newPassword: z.string().min(6),
@ -326,7 +326,7 @@ export type GetCurrentTestActivityResponse = z.infer<
export const GetStreakResponseSchema =
responseWithNullableData(UserStreakSchema);
export type GetStreakResponseSchema = z.infer<typeof GetStreakResponseSchema>;
export type GetStreakResponse = z.infer<typeof GetStreakResponseSchema>;
const c = initContract();

View file

@ -13,22 +13,18 @@ module.exports = {
],
extends: [
"eslint:recommended",
"plugin:json/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"prettier",
"plugin:oxlint/recommended",
],
plugins: ["json", "require-path-exists", "@typescript-eslint"],
plugins: ["require-path-exists", "@typescript-eslint"],
parserOptions: {
sourceType: "module",
ecmaVersion: 2020,
},
rules: {
"json/*": ["error"],
indent: ["off"],
"no-empty": ["error", { allowEmptyCatch: true }],
"no-var": 2,
"no-duplicate-imports": ["error"],
"no-constant-condition": ["error"],
"no-constant-binary-expression": "error",
"no-unused-vars": [
@ -39,7 +35,6 @@ module.exports = {
varsIgnorePattern: "^_",
},
],
"import/no-duplicates": "off",
"import/no-unresolved": [
"error",
{
@ -52,8 +47,35 @@ module.exports = {
groups: [["+", "??"]],
},
],
//handled by oxlint
"no-duplicates": "off",
"no-var": "off",
"no-empty": "off",
"no-named-as-default": "off",
"prefer-const": "off",
"prefer-rest-params": "off",
"prefer-spread": "off",
"import/no-named-as-default": "off",
"import/no-named-as-default-member": "off",
"import/no-duplicates": "off",
"import/export": "off",
"no-case-declarations": "off",
"no-fallthrough": "off",
"no-inner-declarations": "off",
"no-prototype-builtins": "off",
"no-regex-spaces": "off",
"no-redeclare": "off",
},
overrides: [
{
files: ["*.json"],
extends: ["plugin:json/recommended"],
plugins: ["json"],
rules: {
"json/*": ["error"],
},
},
{
// enable the rule specifically for TypeScript files
files: ["*.ts", "*.tsx"],
@ -65,6 +87,7 @@ module.exports = {
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/strict",
"plugin:@typescript-eslint/strict-type-checked",
"plugin:oxlint/recommended",
],
rules: {
//strict type checked
@ -73,7 +96,6 @@ module.exports = {
"@typescript-eslint/await-thenable": "off",
"@typescript-eslint/no-unnecessary-template-expression": "off",
"@typescript-eslint/prefer-promise-reject-errors": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-unnecessary-type-arguments": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/no-redundant-type-constituents": "off",
@ -88,29 +110,6 @@ module.exports = {
"error",
{ ignoreArrowShorthand: true },
],
"@typescript-eslint/explicit-function-return-type": [
"error",
{
allowExpressions: true,
},
],
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-function": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^(_|e|event)",
caughtErrorsIgnorePattern: "^(_|e|error)",
varsIgnorePattern: "^_",
},
],
"@typescript-eslint/no-unused-expressions": [
"error",
{
allowTernary: true,
},
],
"@typescript-eslint/no-var-requires": "error",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-misused-promises": [
"error",
@ -126,9 +125,47 @@ module.exports = {
],
"@typescript-eslint/non-nullable-type-assertion-style": "off",
"@typescript-eslint/no-unnecessary-condition": "off",
"@typescript-eslint/consistent-type-definitions": ["error", "type"],
"@typescript-eslint/no-invalid-void-type": "off",
"import/namespace": "off",
//handled by oxlint
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-empty-object-type": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/no-empty-function": "off",
"no-empty": "off",
"@typescript-eslint/only-throw-error": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-unsafe-function-type": "off",
"@typescript-eslint/consistent-type-definitions": "off",
"@typescript-eslint/no-var-requires": "off",
"no-named-as-default": "off",
"no-duplicates": "off",
"@typescript-eslint/no-array-constructor": "off",
"@typescript-eslint/no-extraneous-class": "off",
"@typescript-eslint/no-non-null-asserted-nullish-coalescing": "off",
"@typescript-eslint/no-require-imports": "off",
"@typescript-eslint/no-unnecessary-type-constraint": "off",
"@typescript-eslint/no-useless-constructor": "off",
"@typescript-eslint/prefer-literal-enum-member": "off",
"@typescript-eslint/prefer-namespace-keyword": "off",
"no-var": "off",
"prefer-const": "off",
"prefer-rest-params": "off",
"prefer-spread": "off",
"import/no-named-as-default": "off",
"import/no-named-as-default-member": "off",
"import/no-duplicates": "off",
"import/export": "off",
"no-case-declarations": "off",
"no-fallthrough": "off",
"no-inner-declarations": "off",
"no-prototype-builtins": "off",
"no-regex-spaces": "off",
"no-redeclare": "off",
"@typescript-eslint/no-namespace": "off",
},
settings: {
"import/resolver": {

View file

@ -8,6 +8,7 @@
"eslint-import-resolver-typescript": "3.6.1",
"eslint-plugin-import": "2.29.0",
"eslint-plugin-json": "3.1.0",
"eslint-plugin-oxlint": "0.16.5",
"eslint-plugin-require-path-exists": "1.1.9"
}
}

View file

@ -0,0 +1,6 @@
{
"extends": [
"../oxlint-config/index.json"
// "@monkeytype/oxlint-config"
]
}

View file

@ -7,7 +7,9 @@
"test": "vitest run",
"madge": " madge --circular --extensions ts ./src",
"ts-check": "tsc --noEmit",
"lint": "eslint \"./**/*.ts\""
"eslint": "eslint \"./src/**/*.ts\"",
"oxlint": "oxlint .",
"lint": "npm run oxlint && npm run eslint"
},
"devDependencies": {
"@monkeytype/eslint-config": "workspace:*",
@ -16,6 +18,7 @@
"chokidar": "3.6.0",
"eslint": "8.57.1",
"madge": "8.0.0",
"oxlint": "0.16.6",
"tsup": "8.4.0",
"typescript": "5.5.4",
"vitest": "2.1.9"

View file

@ -439,7 +439,10 @@ const list: Record<FunboxName, FunboxMetadata> = {
},
};
// oxlint doesnt understand ts overloading
// eslint-disable-next-line no-redeclare
export function getFunbox(name: FunboxName): FunboxMetadata;
// eslint-disable-next-line no-redeclare
export function getFunbox(names: FunboxName[]): FunboxMetadata[];
export function getFunbox(
nameOrNames: FunboxName | FunboxName[]

View file

@ -5,9 +5,9 @@ export function stringToFunboxNames(names: string): FunboxName[] {
if (names === "none" || names === "") return [];
const unsafeNames = names.split("#").map((name) => name.trim());
const out: FunboxName[] = [];
const list = getList().map((f) => f.name);
const list = new Set(getList().map((f) => f.name));
for (const unsafeName of unsafeNames) {
if (list.includes(unsafeName as FunboxName)) {
if (list.has(unsafeName as FunboxName)) {
out.push(unsafeName as FunboxName);
} else {
throw new Error("Invalid funbox name: " + unsafeName);

View file

@ -0,0 +1,98 @@
{
"ignorePatterns": ["node_modules", "dist", "coverage", "build"],
"categories": {
"correctness": "error",
"suspicious": "error",
"perf": "error"
},
"plugins": ["typescript", "unicorn", "oxc", "import", "node", "promise"],
"rules": {
"no-unused-vars": [
"error",
{
"argsIgnorePattern": "^(_|e|event)",
"caughtErrorsIgnorePattern": "^(_|e|error)",
"varsIgnorePattern": "^_"
}
],
"no-await-in-loop": "off",
"consistent-function-scoping": "off",
"prefer-add-event-listener": "off",
"no-new": "off",
"no-new-array": "off",
"no-useless-spread": "off",
"no-async-endpoint-handlers": "off",
"no-this-alias": "off",
"no-var": "error",
"no-non-null-assertion": "error",
"no-non-null-asserted-nullish-coalescing": "error",
"no-explicit-any": "error",
"no-empty-object-type": "error",
"explicit-function-return-type": [
"error",
{
"allowExpressions": true
}
],
"no-unused-expressions": [
"error",
{
"allowTernary": true
}
],
"no-unsafe-function-type": "error",
"prefer-for-of": "error",
"consistent-type-definitions": ["error", "type"],
"no-var-requires": "error",
"no-named-as-default": "error",
"no-named-as-default-member": "error",
"no-array-constructor": "error",
"no-dynamic-delete": "error",
"no-extraneous-class": "error",
"no-require-imports": "error",
"no-empty-function": "error",
"no-redeclare": "error",
"no-empty": [
"error",
{
"allowEmptyCatch": true
}
],
"no-self-compare": "error",
"no-throw-literal": "error",
"no-constant-condition": "error",
"no-constant-binary-expression": "error",
"no-unnecessary-type-constraint": "error",
"no-useless-constructor": "error",
"prefer-literal-enum-member": "error",
"prefer-namespace-keyword": "error",
"prefer-rest-params": "error",
"prefer-spread": "error",
"no-duplicates": "error",
"no-case-declarations": "error",
"no-fallthrough": "error",
"no-inner-declarations": "error",
"no-prototype-builtins": "error",
"no-regex-spaces": "error",
"typescript/no-namespace": "error",
"todo-lowerthis": "off",
"max-depth": [
"error",
{
"max": 9
}
],
"todo": "off",
"ban-ts-comment": "off",
"no-array-for-each": "off",
"eqeqeq": "off",
"prefer-ts-expect-error": "off",
"consider": "off",
"no-cycle": "off",
"no-nested-ternary": "off"
}
}

View file

@ -0,0 +1,7 @@
{
"name": "@monkeytype/oxlint-config",
"private": true,
"exports": {
".": "./index.json"
}
}

View file

@ -0,0 +1,6 @@
{
"extends": [
"../oxlint-config/index.json"
// "@monkeytype/oxlint-config"
]
}

View file

@ -5,13 +5,15 @@
"scripts": {
"dev": "nodemon --watch src --exec \"node ./src/index.js --dry\"",
"dev-changelog": "nodemon ./src/buildChangelog.js",
"lint": "eslint \"./**/*.js\"",
"oxlint": "oxlint .",
"lint": "npm run oxlint",
"purge-cf-cache": "./bin/purgeCfCache.sh"
},
"devDependencies": {
"@monkeytype/eslint-config": "workspace:*",
"eslint": "8.57.1",
"nodemon": "3.1.4"
"nodemon": "3.1.4",
"oxlint": "0.16.6"
},
"bin": {
"monkeytype-release": "./src/index.js",

View file

@ -11,12 +11,12 @@ const __dirname = dirname(__filename);
dotenv.config();
const args = process.argv.slice(2);
const isFrontend = args.includes("--fe");
const noDeploy = args.includes("--no-deploy");
const isBackend = args.includes("--be");
const isDryRun = args.includes("--dry");
const noSyncCheck = args.includes("--no-sync-check");
const args = new Set(process.argv.slice(2));
const isFrontend = args.has("--fe");
const noDeploy = args.has("--no-deploy");
const isBackend = args.has("--be");
const isDryRun = args.has("--dry");
const noSyncCheck = args.has("--no-sync-check");
const PROJECT_ROOT = path.resolve(__dirname, "../../../");

View file

@ -0,0 +1,6 @@
{
"extends": [
"../oxlint-config/index.json"
// "@monkeytype/oxlint-config"
]
}

View file

@ -4,7 +4,10 @@
"scripts": {
"dev": "tsup-node --watch",
"build": "tsup-node",
"ts-check": "tsc --noEmit"
"ts-check": "tsc --noEmit",
"eslint": "eslint \"./src/**/*.ts\"",
"oxlint": "oxlint .",
"lint": "npm run oxlint && npm run eslint"
},
"peerDependencies": {
"tsup": "8.4.0"
@ -12,6 +15,7 @@
"devDependencies": {
"@monkeytype/typescript-config": "workspace:*",
"eslint": "8.57.1",
"oxlint": "0.16.6",
"typescript": "5.5.4"
},
"exports": {

View file

@ -15,7 +15,7 @@ export function extendConfig(
format: ["cjs", "esm"],
dts: false,
minify: true,
...(overrideOptions || {}),
...overrideOptions,
};
return defineConfig(config);

View file

@ -0,0 +1,6 @@
{
"extends": [
"../oxlint-config/index.json"
// "@monkeytype/oxlint-config"
]
}

View file

@ -6,7 +6,9 @@
"test": "vitest run",
"madge": " madge --circular --extensions ts ./src",
"ts-check": "tsc --noEmit",
"lint": "eslint \"./**/*.ts\""
"eslint": "eslint \"./src/**/*.ts\"",
"oxlint": "oxlint .",
"lint": "npm run oxlint && npm run eslint"
},
"devDependencies": {
"@monkeytype/eslint-config": "workspace:*",
@ -15,6 +17,7 @@
"chokidar": "3.6.0",
"eslint": "8.57.1",
"madge": "8.0.0",
"oxlint": "0.16.6",
"tsup": "8.4.0",
"typescript": "5.5.4",
"vitest": "2.1.9",

View file

@ -5,9 +5,9 @@
* @returns An array containing the elements that are present in both input arrays.
*/
export function intersect<T>(a: T[], b: T[], removeDuplicates = false): T[] {
let t;
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
if (b.length > a.length) (t = b), (b = a), (a = t); // indexOf to loop over shorter
if (b.length > a.length) {
[a, b] = [b, a]; // Swap a and b to loop over the shorter array
}
const filtered = a.filter(function (e) {
return b.includes(e);
});

146
pnpm-lock.yaml generated
View file

@ -23,6 +23,9 @@ importers:
conventional-changelog:
specifier: 6.0.0
version: 6.0.0(conventional-commits-filter@5.0.0)
eslint:
specifier: 8.57.1
version: 8.57.1
husky:
specifier: 8.0.1
version: 8.0.1
@ -35,6 +38,9 @@ importers:
only-allow:
specifier: 1.2.1
version: 1.2.1
oxlint:
specifier: 0.16.6
version: 0.16.6
prettier:
specifier: 2.8.8
version: 2.8.8
@ -165,6 +171,9 @@ importers:
'@monkeytype/eslint-config':
specifier: workspace:*
version: link:../packages/eslint-config
'@monkeytype/oxlint-config':
specifier: workspace:*
version: link:../packages/oxlint-config
'@monkeytype/typescript-config':
specifier: workspace:*
version: link:../packages/typescript-config
@ -243,6 +252,9 @@ importers:
openapi3-ts:
specifier: 2.0.2
version: 2.0.2
oxlint:
specifier: 0.16.6
version: 0.16.6
readline-sync:
specifier: 1.4.10
version: 1.4.10
@ -358,6 +370,9 @@ importers:
'@monkeytype/eslint-config':
specifier: workspace:*
version: link:../packages/eslint-config
'@monkeytype/oxlint-config':
specifier: workspace:*
version: link:../packages/oxlint-config
'@monkeytype/typescript-config':
specifier: workspace:*
version: link:../packages/typescript-config
@ -427,6 +442,9 @@ importers:
normalize.css:
specifier: 8.0.1
version: 8.0.1
oxlint:
specifier: 0.16.6
version: 0.16.6
postcss:
specifier: 8.4.31
version: 8.4.31
@ -463,6 +481,9 @@ importers:
vite-plugin-minify:
specifier: 2.1.0
version: 2.1.0(vite@6.0.14(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3)(tsx@4.16.2)(yaml@2.5.0))
vite-plugin-oxlint:
specifier: 1.3.1
version: 1.3.1(vite@6.0.14(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3)(tsx@4.16.2)(yaml@2.5.0))
vite-plugin-pwa:
specifier: 0.20.0
version: 0.20.0(vite@6.0.14(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3)(tsx@4.16.2)(yaml@2.5.0))(workbox-build@7.1.1)(workbox-window@7.1.0)
@ -497,6 +518,9 @@ importers:
madge:
specifier: 8.0.0
version: 8.0.0(typescript@5.5.4)
oxlint:
specifier: 0.16.6
version: 0.16.6
tsup:
specifier: 8.4.0
version: 8.4.0(postcss@8.5.1)(tsx@4.16.2)(typescript@5.5.4)(yaml@2.5.0)
@ -527,6 +551,9 @@ importers:
eslint-plugin-json:
specifier: 3.1.0
version: 3.1.0
eslint-plugin-oxlint:
specifier: 0.16.5
version: 0.16.5
eslint-plugin-require-path-exists:
specifier: 1.1.9
version: 1.1.9
@ -555,6 +582,9 @@ importers:
madge:
specifier: 8.0.0
version: 8.0.0(typescript@5.5.4)
oxlint:
specifier: 0.16.6
version: 0.16.6
tsup:
specifier: 8.4.0
version: 8.4.0(postcss@8.5.1)(tsx@4.16.2)(typescript@5.5.4)(yaml@2.5.0)
@ -565,6 +595,8 @@ importers:
specifier: 2.1.9
version: 2.1.9(@types/node@20.14.11)(happy-dom@15.10.2)(sass@1.70.0)(terser@5.31.3)
packages/oxlint-config: {}
packages/release:
dependencies:
'@octokit/rest':
@ -586,6 +618,9 @@ importers:
nodemon:
specifier: 3.1.4
version: 3.1.4
oxlint:
specifier: 0.16.6
version: 0.16.6
packages/tsup-config:
dependencies:
@ -599,6 +634,9 @@ importers:
eslint:
specifier: 8.57.1
version: 8.57.1
oxlint:
specifier: 0.16.6
version: 0.16.6
typescript:
specifier: 5.5.4
version: 5.5.4
@ -625,6 +663,9 @@ importers:
madge:
specifier: 8.0.0
version: 8.0.0(typescript@5.5.4)
oxlint:
specifier: 0.16.6
version: 0.16.6
tsup:
specifier: 8.4.0
version: 8.4.0(postcss@8.5.1)(tsx@4.16.2)(typescript@5.5.4)(yaml@2.5.0)
@ -2368,6 +2409,46 @@ packages:
resolution: {integrity: sha512-lkC8kZYntxVKr7b8xmjCVUgE0a8xgDakPyDo9uSWavXPyYqLgYYGdEd2j8NxihRyb6UwpX3G/hFUF4/9q2V+/g==}
engines: {node: '>=14'}
'@oxlint/darwin-arm64@0.16.6':
resolution: {integrity: sha512-wvW55Br6o08JEmiezMqvo0byZNH9eunCkbouV8rM2gQP6ROv8lbeQdPZLpAeFz0QA4Ca2b2pVo5S3N2fS78d+Q==}
cpu: [arm64]
os: [darwin]
'@oxlint/darwin-x64@0.16.6':
resolution: {integrity: sha512-VezC8yep+1TxVtBsTQz2OHJs9aTuIQ7ISyl5rn1QVQXeG7wdFIIFln3ilu2TtaMjnswEdEsCDqBjyoF1euqQow==}
cpu: [x64]
os: [darwin]
'@oxlint/linux-arm64-gnu@0.16.6':
resolution: {integrity: sha512-hvpBsP5/bERq8ft4KidszGifWV4ZcXeaJrfNI8CqIbfd4AqGJmnc5d6M2Op/sYdEMjRGdpPqftfzw4D6jDHPIQ==}
cpu: [arm64]
os: [linux]
'@oxlint/linux-arm64-musl@0.16.6':
resolution: {integrity: sha512-PolYYEhYELXaQ0ht0g6Z827rRVDgbi/PQcHFpctiDHbSruW3udIOy9nEOAUt0agSHYrdZcC0NWzISE+CPrM0Yw==}
cpu: [arm64]
os: [linux]
'@oxlint/linux-x64-gnu@0.16.6':
resolution: {integrity: sha512-y4Lq4mcheXYzyLiS2TG1CaNDfgK+yVmmyJlms010Gs6nd1ejF6cObMuY5g6GLPGRJMJxG4fhbE955I2y50+Ltg==}
cpu: [x64]
os: [linux]
'@oxlint/linux-x64-musl@0.16.6':
resolution: {integrity: sha512-p3Njn7MzBsIJr+23HtxItA86UP01xhcWfwU35RGWVyTNbXIdNoAkaD+DjXQj2KSEauO7rRDAZbrTA+40NWNNkQ==}
cpu: [x64]
os: [linux]
'@oxlint/win32-arm64@0.16.6':
resolution: {integrity: sha512-IdySuXzslSnZEk9F2mRx1cjyPsHM8ny2xQd+FEbWhDhfwxVZCK+m9hXoEnqVQ6FLXQsOjWVutGtYb+EpDiZxlQ==}
cpu: [arm64]
os: [win32]
'@oxlint/win32-x64@0.16.6':
resolution: {integrity: sha512-DqkFdDX1ULoizFBg21TMIe6B5L2a59KljqpN1S7H4+IXhxmRcc71bpZ7FRwrxjrlRhtCD4SAPTZadBI9qRhViw==}
cpu: [x64]
os: [win32]
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@ -4634,6 +4715,9 @@ packages:
resolution: {integrity: sha512-MrlG2ynFEHe7wDGwbUuFPsaT2b1uhuEFhJ+W1f1u+1C2EkXmTYJp4B1aAdQQ8M+CC3t//N/oRKiIVw14L2HR1g==}
engines: {node: '>=12.0'}
eslint-plugin-oxlint@0.16.5:
resolution: {integrity: sha512-Hzj4jpPw+eESux/r9bC8qozc4PVh25S2JGLXit1BrlghfAHQJWNU1Yhz56p0Tj4Zd+xLA79NimpwbOmLI2isgg==}
eslint-plugin-require-path-exists@1.1.9:
resolution: {integrity: sha512-moZRfrPr4GFyT/W8PHzjzC7D4Hnj7Us+GYj0fbVKQoPvP4xIF8VG702L1jzyhqE8eIYkcs8p1CoqSfjk9WkxBg==}
@ -7207,6 +7291,11 @@ packages:
resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==}
engines: {node: '>=0.10.0'}
oxlint@0.16.6:
resolution: {integrity: sha512-pesehI0loV2h2k95mFRsUg6uNgGw2RPs1pcuAfPRJUwGehkfraMVCQofwqsMUeufmXygweH734vhKzQ24r3djA==}
engines: {node: '>=8.*'}
hasBin: true
p-defer@3.0.0:
resolution: {integrity: sha512-ugZxsxmtTln604yeYd29EGrNhazN2lywetzpKhfmQjW/VJmhpDmWbiX+h0zL8V91R0UXkhb3KtPmyq9PZw3aYw==}
engines: {node: '>=8'}
@ -7254,6 +7343,9 @@ packages:
package-json-from-dist@1.0.0:
resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==}
package-manager-detector@1.1.0:
resolution: {integrity: sha512-Y8f9qUlBzW8qauJjd/eu6jlpJZsuPJm2ZAV0cDVd420o4EdpH5RPdoCv+60/TdJflGatr4sDfpAL6ArWZbM5tA==}
pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
@ -9317,6 +9409,11 @@ packages:
peerDependencies:
vite: '>=5'
vite-plugin-oxlint@1.3.1:
resolution: {integrity: sha512-WqPadqqjHVGjs/rTg3E2h2Ad9AgTlRD5X+YA0WlEke+uC6/P24RmLLucmDatBqi59ccV4xAWzchqIdRZ5Z0CHA==}
peerDependencies:
vite: ^6.0.7
vite-plugin-pwa@0.20.0:
resolution: {integrity: sha512-/kDZyqF8KqoXRpMUQtR5Atri/7BWayW8Gp7Kz/4bfstsV6zSFTxjREbXZYL7zSuRL40HGA+o2hvUAFRmC+bL7g==}
engines: {node: '>=16.0.0'}
@ -11778,6 +11875,30 @@ snapshots:
'@opentelemetry/semantic-conventions@1.21.0': {}
'@oxlint/darwin-arm64@0.16.6':
optional: true
'@oxlint/darwin-x64@0.16.6':
optional: true
'@oxlint/linux-arm64-gnu@0.16.6':
optional: true
'@oxlint/linux-arm64-musl@0.16.6':
optional: true
'@oxlint/linux-x64-gnu@0.16.6':
optional: true
'@oxlint/linux-x64-musl@0.16.6':
optional: true
'@oxlint/win32-arm64@0.16.6':
optional: true
'@oxlint/win32-x64@0.16.6':
optional: true
'@pkgjs/parseargs@0.11.0':
optional: true
@ -14370,6 +14491,10 @@ snapshots:
lodash: 4.17.21
vscode-json-languageservice: 4.2.1
eslint-plugin-oxlint@0.16.5:
dependencies:
jsonc-parser: 3.3.1
eslint-plugin-require-path-exists@1.1.9:
dependencies:
builtin-modules: 1.1.1
@ -17703,6 +17828,17 @@ snapshots:
os-tmpdir@1.0.2: {}
oxlint@0.16.6:
optionalDependencies:
'@oxlint/darwin-arm64': 0.16.6
'@oxlint/darwin-x64': 0.16.6
'@oxlint/linux-arm64-gnu': 0.16.6
'@oxlint/linux-arm64-musl': 0.16.6
'@oxlint/linux-x64-gnu': 0.16.6
'@oxlint/linux-x64-musl': 0.16.6
'@oxlint/win32-arm64': 0.16.6
'@oxlint/win32-x64': 0.16.6
p-defer@3.0.0: {}
p-limit@2.3.0:
@ -17751,6 +17887,8 @@ snapshots:
package-json-from-dist@1.0.0: {}
package-manager-detector@1.1.0: {}
pako@1.0.11: {}
param-case@2.1.1:
@ -18641,7 +18779,7 @@ snapshots:
dependencies:
chokidar: 3.6.0
immutable: 4.3.7
source-map-js: 1.2.0
source-map-js: 1.2.1
scheduler@0.23.2:
dependencies:
@ -20081,6 +20219,12 @@ snapshots:
html-minifier-terser: 7.2.0
vite: 6.0.14(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3)(tsx@4.16.2)(yaml@2.5.0)
vite-plugin-oxlint@1.3.1(vite@6.0.14(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3)(tsx@4.16.2)(yaml@2.5.0)):
dependencies:
oxlint: 0.16.6
package-manager-detector: 1.1.0
vite: 6.0.14(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3)(tsx@4.16.2)(yaml@2.5.0)
vite-plugin-pwa@0.20.0(vite@6.0.14(@types/node@20.14.11)(sass@1.70.0)(terser@5.31.3)(tsx@4.16.2)(yaml@2.5.0))(workbox-build@7.1.1)(workbox-window@7.1.0):
dependencies:
debug: 4.3.6(supports-color@5.5.0)