From fb9f339157f932a16d013b05d7ac912bb152d9af Mon Sep 17 00:00:00 2001 From: Miodec Date: Tue, 9 Dec 2025 19:25:10 +0100 Subject: [PATCH 1/5] chore: better debug css --- frontend/src/ts/modals/dev-options.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/ts/modals/dev-options.ts b/frontend/src/ts/modals/dev-options.ts index 9742c6e8832c..199f530ab5cd 100644 --- a/frontend/src/ts/modals/dev-options.ts +++ b/frontend/src/ts/modals/dev-options.ts @@ -52,6 +52,8 @@ async function setup(modalEl: HTMLElement): Promise { .querySelector(".showRealWordsInput") ?.addEventListener("click", () => { getInputElement().style.opacity = "1"; + getInputElement().style.marginTop = "1.5em"; + getInputElement().style.caretColor = "red"; void modal.hide(); }); modalEl.querySelector(".quickLogin")?.addEventListener("click", () => { From 0c1a8a78991a0059a9e7e9f8071c11a520cb5df9 Mon Sep 17 00:00:00 2001 From: Miodec Date: Tue, 9 Dec 2025 19:25:36 +0100 Subject: [PATCH 2/5] fix: safari broken input --- frontend/src/styles/test.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/styles/test.scss b/frontend/src/styles/test.scss index e61cdc2fcb6b..82bfdb89c81a 100644 --- a/frontend/src/styles/test.scss +++ b/frontend/src/styles/test.scss @@ -607,7 +607,10 @@ caret-color: transparent; resize: none; overflow: hidden; - white-space: nowrap; // if the text wraps, ctrl backspace will not work on firefox >:| + // if the text wraps, ctrl backspace will not work on firefox >:| + // and holy shit do not use white-space: nowrap here because the hellspawn that is safari + // is TRIMMING the value on focus when using that (and the input system relies on a leading space) + text-wrap-mode: nowrap; } #capsWarning { From b5a03e0040fb8b33662d7f07a2639162d91d116a Mon Sep 17 00:00:00 2001 From: Jack Date: Tue, 9 Dec 2025 20:37:04 +0100 Subject: [PATCH 3/5] chore(linting): enable prefer-nullish-coalescing (@miodec) (#7209) --- backend/src/api/controllers/dev.ts | 2 +- backend/src/api/controllers/quote.ts | 4 +- backend/src/api/controllers/result.ts | 24 ++--- backend/src/dal/result.ts | 2 +- backend/src/dal/user.ts | 6 +- backend/src/jobs/update-leaderboards.ts | 2 +- backend/src/middlewares/auth.ts | 5 +- backend/src/utils/misc.ts | 4 +- .../src/ts/controllers/chart-controller.ts | 5 +- .../src/ts/controllers/pw-ad-controller.ts | 4 +- .../src/ts/controllers/quotes-controller.ts | 8 +- frontend/src/ts/db.ts | 93 +++++++------------ frontend/src/ts/event-handlers/global.ts | 4 +- frontend/src/ts/modals/edit-preset.ts | 22 ++--- frontend/src/ts/modals/edit-profile.ts | 2 +- frontend/src/ts/modals/simple-modals.ts | 2 +- frontend/src/ts/pages/account.ts | 30 +++--- frontend/src/ts/pages/profile-search.ts | 49 +++++----- frontend/src/ts/states/connection.ts | 22 ++--- frontend/src/ts/test/practise-words.ts | 7 +- frontend/src/ts/test/result.ts | 8 +- frontend/src/ts/test/test-ui.ts | 7 +- frontend/src/ts/test/words-generator.ts | 2 +- frontend/src/ts/utils/format.ts | 5 +- frontend/src/ts/utils/sorted-table.ts | 4 +- packages/eslint-config/index.js | 1 + packages/funbox/src/validation.ts | 6 +- 27 files changed, 131 insertions(+), 199 deletions(-) diff --git a/backend/src/api/controllers/dev.ts b/backend/src/api/controllers/dev.ts index 225cd671bbc2..cbc762c55204 100644 --- a/backend/src/api/controllers/dev.ts +++ b/backend/src/api/controllers/dev.ts @@ -221,7 +221,7 @@ async function updateUser(uid: string): Promise { { sort: { wpm: -1, timestamp: 1 } }, )) as DBResult; - if (personalBests[mode.mode] === undefined) personalBests[mode.mode] = {}; + personalBests[mode.mode] ??= {}; if (personalBests[mode.mode][mode.mode2] === undefined) personalBests[mode.mode][mode.mode2] = []; diff --git a/backend/src/api/controllers/quote.ts b/backend/src/api/controllers/quote.ts index 24b858ae9f16..40c64cf0ef8a 100644 --- a/backend/src/api/controllers/quote.ts +++ b/backend/src/api/controllers/quote.ts @@ -125,9 +125,7 @@ export async function submitRating( shouldUpdateRating, ); - if (!userQuoteRatings[language]) { - userQuoteRatings[language] = {}; - } + userQuoteRatings[language] ??= {}; userQuoteRatings[language][quoteId] = rating; await updateQuoteRatings(uid, userQuoteRatings); diff --git a/backend/src/api/controllers/result.ts b/backend/src/api/controllers/result.ts index a6efd233386e..4e7cacdaaf2b 100644 --- a/backend/src/api/controllers/result.ts +++ b/backend/src/api/controllers/result.ts @@ -170,24 +170,12 @@ export async function updateTags( await ResultDAL.updateTags(uid, resultId, tagIds); const result = await ResultDAL.getResult(uid, resultId); - if (!result.difficulty) { - result.difficulty = "normal"; - } - if (!(result.language ?? "")) { - result.language = "english"; - } - if (result.funbox === undefined) { - result.funbox = []; - } - if (!result.lazyMode) { - result.lazyMode = false; - } - if (!result.punctuation) { - result.punctuation = false; - } - if (!result.numbers) { - result.numbers = false; - } + result.difficulty ??= "normal"; + result.language ??= "english"; + result.funbox ??= []; + result.lazyMode ??= false; + result.punctuation ??= false; + result.numbers ??= false; const user = await UserDAL.getPartialUser(uid, "update tags", ["tags"]); const tagPbs = await UserDAL.checkIfTagPb(uid, user, result); diff --git a/backend/src/dal/result.ts b/backend/src/dal/result.ts index a2fc336aadd2..0427b889ca71 100644 --- a/backend/src/dal/result.ts +++ b/backend/src/dal/result.ts @@ -21,7 +21,7 @@ export async function addResult( const { data: user } = await tryCatch(getUser(uid, "add result")); if (!user) throw new MonkeyError(404, "User not found", "add result"); - if (result.uid === undefined) result.uid = uid; + result.uid ??= uid; // result.ir = true; const res = await getResultCollection().insertOne(result); return { diff --git a/backend/src/dal/user.ts b/backend/src/dal/user.ts index 58725b22085e..7190644d6dc0 100644 --- a/backend/src/dal/user.ts +++ b/backend/src/dal/user.ts @@ -275,7 +275,7 @@ export async function findByName(name: string): Promise { { collation: { locale: "en", strength: 1 } }, ); - return found !== null ? found : undefined; + return found ?? undefined; } export async function isNameAvailable( @@ -1053,10 +1053,14 @@ export async function updateInbox( .filter((it) => it.type === "badge") .map((it) => it.item); + // mongo doesnt support ??= i think + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing if (inventory === null) inventory = { badges: [], }; + // mongo doesnt support ??= i think + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing if (inventory.badges === null) inventory.badges = []; const uniqueBadgeIds = new Set(); diff --git a/backend/src/jobs/update-leaderboards.ts b/backend/src/jobs/update-leaderboards.ts index 4f464e9bd62c..e2fdf069423f 100644 --- a/backend/src/jobs/update-leaderboards.ts +++ b/backend/src/jobs/update-leaderboards.ts @@ -46,7 +46,7 @@ async function updateLeaderboardAndNotifyChanges( const isRecentRecord = record.timestamp > Date.now() - RECENT_AGE_MILLISECONDS; - return (userImprovedRank || newUserInTop10) && isRecentRecord; + return (userImprovedRank === true || newUserInTop10) && isRecentRecord; }); if (newRecords.length > 0) { diff --git a/backend/src/middlewares/auth.ts b/backend/src/middlewares/auth.ts index f79e08c9b38d..002480a9374b 100644 --- a/backend/src/middlewares/auth.ts +++ b/backend/src/middlewares/auth.ts @@ -60,7 +60,8 @@ export function authenticateTsRestRequest< let authType = "None"; const isPublic = - options.isPublic || (options.isPublicOnDev && isDevEnvironment()); + options.isPublic === true || + (options.isPublicOnDev && isDevEnvironment()); const { authorization: authHeader, @@ -241,7 +242,7 @@ async function authenticateWithApeKey( options: RequestAuthenticationOptions, ): Promise { const isPublic = - options.isPublic || (options.isPublicOnDev && isDevEnvironment()); + options.isPublic === true || (options.isPublicOnDev && isDevEnvironment()); if (!isPublic) { if (!configuration.apeKeys.acceptKeys) { diff --git a/backend/src/utils/misc.ts b/backend/src/utils/misc.ts index 4c11e0d3cf0e..db39270808ad 100644 --- a/backend/src/utils/misc.ts +++ b/backend/src/utils/misc.ts @@ -195,9 +195,7 @@ export function isDevEnvironment(): boolean { export function getFrontendUrl(): string { return isDevEnvironment() ? "http://localhost:3000" - : process.env["FRONTEND_URL"] !== undefined - ? process.env["FRONTEND_URL"] - : "https://monkeytype.com"; + : (process.env["FRONTEND_URL"] ?? "https://monkeytype.com"); } /** diff --git a/frontend/src/ts/controllers/chart-controller.ts b/frontend/src/ts/controllers/chart-controller.ts index 9aa4fe2f4b21..424a4105713a 100644 --- a/frontend/src/ts/controllers/chart-controller.ts +++ b/frontend/src/ts/controllers/chart-controller.ts @@ -594,10 +594,7 @@ export const accountHistory = new ChartWithUpdateColors< label += resultData.mode2; } - let diff = resultData.difficulty; - if (diff === undefined) { - diff = "normal"; - } + let diff = resultData.difficulty ?? "normal"; label += `\ndifficulty: ${diff}`; label += diff --git a/frontend/src/ts/controllers/pw-ad-controller.ts b/frontend/src/ts/controllers/pw-ad-controller.ts index 159112e6ac34..c2fbcc387036 100644 --- a/frontend/src/ts/controllers/pw-ad-controller.ts +++ b/frontend/src/ts/controllers/pw-ad-controller.ts @@ -146,11 +146,11 @@ export function init(): void { headOfDocument.appendChild(rampScript); window._pwGA4PageviewId = "".concat(Date.now()); - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/no-unsafe-assignment + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/prefer-nullish-coalescing window.dataLayer = window.dataLayer || []; // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment window.gtag = - // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions + // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions, @typescript-eslint/prefer-nullish-coalescing window.gtag || function (): void { // eslint-disable-next-line prefer-rest-params diff --git a/frontend/src/ts/controllers/quotes-controller.ts b/frontend/src/ts/controllers/quotes-controller.ts index db31b80138b1..8c095b3043ec 100644 --- a/frontend/src/ts/controllers/quotes-controller.ts +++ b/frontend/src/ts/controllers/quotes-controller.ts @@ -239,12 +239,8 @@ class QuotesController { }); if (response.status === 200) { - if (snapshot.favoriteQuotes === undefined) { - snapshot.favoriteQuotes = {}; - } - if (!snapshot.favoriteQuotes[quote.language]) { - snapshot.favoriteQuotes[quote.language] = []; - } + snapshot.favoriteQuotes ??= {}; + snapshot.favoriteQuotes[quote.language] ??= []; snapshot.favoriteQuotes[quote.language]?.push(`${quote.id}`); } else { throw new Error(response.body.message); diff --git a/frontend/src/ts/db.ts b/frontend/src/ts/db.ts index 367ce51ad3d3..a8e4301520f1 100644 --- a/frontend/src/ts/db.ts +++ b/frontend/src/ts/db.ts @@ -198,8 +198,7 @@ export async function initSnapshot(): Promise { } const hourOffset = userData?.streak?.hourOffset; - snap.streakHourOffset = - hourOffset === undefined || hourOffset === null ? undefined : hourOffset; + snap.streakHourOffset = hourOffset ?? undefined; if (userData.lbMemory !== undefined) { snap.lbMemory = userData.lbMemory; @@ -308,24 +307,20 @@ export async function getUserResults(offset?: number): Promise { if (!isAuthenticated()) return false; const results: SnapshotResult[] = response.body.data.map((result) => { - if (result.bailedOut === undefined) result.bailedOut = false; - if (result.blindMode === undefined) result.blindMode = false; - if (result.lazyMode === undefined) result.lazyMode = false; - if (result.difficulty === undefined) result.difficulty = "normal"; - if (result.funbox === undefined) result.funbox = []; - if (result.language === undefined || result.language === null) { - result.language = "english"; - } - if (result.numbers === undefined) result.numbers = false; - if (result.punctuation === undefined) result.punctuation = false; - if (result.numbers === undefined) result.numbers = false; - if (result.quoteLength === undefined) result.quoteLength = -1; - if (result.restartCount === undefined) result.restartCount = 0; - if (result.incompleteTestSeconds === undefined) { - result.incompleteTestSeconds = 0; - } - if (result.afkDuration === undefined) result.afkDuration = 0; - if (result.tags === undefined) result.tags = []; + result.bailedOut ??= false; + result.blindMode ??= false; + result.lazyMode ??= false; + result.difficulty ??= "normal"; + result.funbox ??= []; + result.language ??= "english"; + result.numbers ??= false; + result.punctuation ??= false; + result.numbers ??= false; + result.quoteLength ??= -1; + result.restartCount ??= 0; + result.incompleteTestSeconds ??= 0; + result.afkDuration ??= 0; + result.tags ??= []; return result as SnapshotResult; }); results?.sort((a, b) => b.timestamp - a.timestamp); @@ -353,9 +348,7 @@ export async function addCustomTheme( ): Promise { if (!dbSnapshot) return false; - if (dbSnapshot.customThemes === undefined) { - dbSnapshot.customThemes = []; - } + dbSnapshot.customThemes ??= []; if (dbSnapshot.customThemes.length >= 20) { Notifications.add("Too many custom themes!", 0); @@ -392,9 +385,7 @@ export async function editCustomTheme( if (!isAuthenticated()) return false; if (!dbSnapshot) return false; - if (dbSnapshot.customThemes === undefined) { - dbSnapshot.customThemes = []; - } + dbSnapshot.customThemes ??= []; const customTheme = dbSnapshot.customThemes?.find((t) => t._id === themeId); if (!customTheme) { @@ -906,20 +897,14 @@ export async function updateLbMemory( const snapshot = getSnapshot(); if (!snapshot) return; - if (snapshot.lbMemory === undefined) { - snapshot.lbMemory = { - time: { "15": { english: 0 }, "60": { english: 0 } }, - }; - } - if (snapshot.lbMemory[timeMode] === undefined) { - snapshot.lbMemory[timeMode] = { - "15": { english: 0 }, - "60": { english: 0 }, - }; - } - if (snapshot.lbMemory[timeMode][timeMode2] === undefined) { - snapshot.lbMemory[timeMode][timeMode2] = {}; - } + snapshot.lbMemory ??= { + time: { "15": { english: 0 }, "60": { english: 0 } }, + }; + snapshot.lbMemory[timeMode] ??= { + "15": { english: 0 }, + "60": { english: 0 }, + }; + snapshot.lbMemory[timeMode][timeMode2] ??= {}; const current = snapshot.lbMemory?.[timeMode]?.[timeMode2]?.[language]; //this is protected above so not sure why it would be undefined @@ -970,13 +955,11 @@ export function saveLocalResult(data: SaveLocalResultData): void { if (snapshot.testActivity !== undefined) { snapshot.testActivity.increment(new Date(data.result.timestamp)); } - if (snapshot.typingStats === undefined) { - snapshot.typingStats = { - timeTyping: 0, - startedTests: 0, - completedTests: 0, - }; - } + snapshot.typingStats ??= { + timeTyping: 0, + startedTests: 0, + completedTests: 0, + }; const time = data.result.testDuration + @@ -1005,9 +988,7 @@ export function saveLocalResult(data: SaveLocalResultData): void { } if (data.xp !== undefined) { - if (snapshot.xp === undefined) { - snapshot.xp = 0; - } + snapshot.xp ??= 0; snapshot.xp += data.xp; } @@ -1028,9 +1009,7 @@ export function addXp(xp: number): void { const snapshot = getSnapshot(); if (!snapshot) return; - if (snapshot.xp === undefined) { - snapshot.xp = 0; - } + snapshot.xp ??= 0; snapshot.xp += xp; setSnapshot(snapshot, { dispatchEvent: false, @@ -1049,11 +1028,9 @@ export function addBadge(badge: Badge): void { const snapshot = getSnapshot(); if (!snapshot) return; - if (snapshot.inventory === undefined) { - snapshot.inventory = { - badges: [], - }; - } + snapshot.inventory ??= { + badges: [], + }; snapshot.inventory.badges.push(badge); setSnapshot(snapshot); } diff --git a/frontend/src/ts/event-handlers/global.ts b/frontend/src/ts/event-handlers/global.ts index 58b1f9c2a6d1..2fefcad0fb8c 100644 --- a/frontend/src/ts/event-handlers/global.ts +++ b/frontend/src/ts/event-handlers/global.ts @@ -61,8 +61,8 @@ document.addEventListener("keydown", (e) => { document.activeElement?.tagName === "TEXTAREA" || document.activeElement?.tagName === "SELECT" || document.activeElement?.tagName === "BUTTON" || - document.activeElement?.classList.contains("button") || - document.activeElement?.classList.contains("textButton"); + document.activeElement?.classList.contains("button") === true || + document.activeElement?.classList.contains("textButton") === true; if ( (e.key === "Tab" && diff --git a/frontend/src/ts/modals/edit-preset.ts b/frontend/src/ts/modals/edit-preset.ts index 0a0911b7519a..39f819153d84 100644 --- a/frontend/src/ts/modals/edit-preset.ts +++ b/frontend/src/ts/modals/edit-preset.ts @@ -46,16 +46,14 @@ export function show(action: string, id?: string, name?: string): void { beforeAnimation: async () => { $("#editPresetModal .modal .text").addClass("hidden"); addCheckBoxes(); - if (!presetNameEl) { - presetNameEl = new ValidatedHtmlInputElement( - document.querySelector( - "#editPresetModal .modal input", - ) as HTMLInputElement, - { - schema: PresetNameSchema, - }, - ); - } + presetNameEl ??= new ValidatedHtmlInputElement( + document.querySelector( + "#editPresetModal .modal input", + ) as HTMLInputElement, + { + schema: PresetNameSchema, + }, + ); if (action === "add") { $("#editPresetModal .modal").attr("data-action", "add"); $("#editPresetModal .modal .popupTitle").html("Add new preset"); @@ -384,9 +382,7 @@ function getPartialConfigChanges( .forEach((settingName) => { const safeSettingName = settingName; const newValue = - configChanges[safeSettingName] !== undefined - ? configChanges[safeSettingName] - : defaultConfig[safeSettingName]; + configChanges[safeSettingName] ?? defaultConfig[safeSettingName]; // @ts-expect-error cant figure this one out, but it works activeConfigChanges[safeSettingName] = newValue; }); diff --git a/frontend/src/ts/modals/edit-profile.ts b/frontend/src/ts/modals/edit-profile.ts index a389d4d26e64..60d3b637a4b7 100644 --- a/frontend/src/ts/modals/edit-profile.ts +++ b/frontend/src/ts/modals/edit-profile.ts @@ -77,7 +77,7 @@ function hydrateInputs(): void { websiteInput.val(socialProfiles?.website ?? ""); badgeIdsSelect.html(""); showActivityOnPublicProfileInput.checked = - showActivityOnPublicProfile || false; + showActivityOnPublicProfile ?? false; badges?.forEach((badge: Badge) => { if (badge.selected) { diff --git a/frontend/src/ts/modals/simple-modals.ts b/frontend/src/ts/modals/simple-modals.ts index b7e76dbed64d..c7ec89d5aae1 100644 --- a/frontend/src/ts/modals/simple-modals.ts +++ b/frontend/src/ts/modals/simple-modals.ts @@ -143,7 +143,7 @@ function isUsingAuthentication(authProvider: AuthMethod): boolean { return ( getAuthenticatedUser()?.providerData.some( (p) => p.providerId === authProvider, - ) || false + ) ?? false ); } diff --git a/frontend/src/ts/pages/account.ts b/frontend/src/ts/pages/account.ts index 897aa745798b..5ffe525cf07f 100644 --- a/frontend/src/ts/pages/account.ts +++ b/frontend/src/ts/pages/account.ts @@ -71,10 +71,7 @@ function loadMoreLines(lineIndex?: number): void { } function buildResultRow(result: SnapshotResult): HTMLTableRowElement { - let diff = result.difficulty; - if (diff === undefined) { - diff = "normal"; - } + let diff = result.difficulty ?? "normal"; let icons = ` { return; } - let resdiff = result.difficulty; - if (resdiff === undefined) { - resdiff = "normal"; - } + let resdiff = result.difficulty ?? "normal"; if (!ResultFilters.getFilter("difficulty", resdiff)) { if (filterDebug) { console.log(`skipping result due to difficulty filter`, result); @@ -1249,17 +1243,15 @@ export const page = new Page({ snapshot !== undefined ? new Date(snapshot.addedAt).getFullYear() : 2020, ); - if (historyTable === undefined) { - historyTable = new SortedTableWithLimit>({ - limit: 10, - table: ".pageAccount .content .history table", - data: filteredResults, - buildRow: (val) => { - return buildResultRow(val); - }, - initialSort: { property: "timestamp", descending: true }, - }); - } + historyTable ??= new SortedTableWithLimit>({ + limit: 10, + table: ".pageAccount .content .history table", + data: filteredResults, + buildRow: (val) => { + return buildResultRow(val); + }, + initialSort: { property: "timestamp", descending: true }, + }); await update().then(() => { void updateChartColors(); diff --git a/frontend/src/ts/pages/profile-search.ts b/frontend/src/ts/pages/profile-search.ts index 173e3a41c3ea..7c016d1afc3b 100644 --- a/frontend/src/ts/pages/profile-search.ts +++ b/frontend/src/ts/pages/profile-search.ts @@ -27,35 +27,32 @@ export const page = new Page({ beforeShow: async (): Promise => { Skeleton.append("pageProfileSearch", "main"); - if (nameInputEl === null) { - nameInputEl = new ValidatedHtmlInputElement( - document.querySelector( - ".page.pageProfileSearch input", - ) as HTMLInputElement, - { - schema: UserNameSchema, - isValid: remoteValidation( - async (name) => - Ape.users.getProfile({ params: { uidOrName: name } }), - { - check: (data) => { - lastProfile = data; - return true; - }, - on4xx: () => "Unknown user", + nameInputEl ??= new ValidatedHtmlInputElement( + document.querySelector( + ".page.pageProfileSearch input", + ) as HTMLInputElement, + { + schema: UserNameSchema, + isValid: remoteValidation( + async (name) => Ape.users.getProfile({ params: { uidOrName: name } }), + { + check: (data) => { + lastProfile = data; + return true; }, - ), - callback: (result) => { - if (result.status === "success") { - enableButton(); - } else { - disableButton(); - lastProfile = null; - } + on4xx: () => "Unknown user", }, + ), + callback: (result) => { + if (result.status === "success") { + enableButton(); + } else { + disableButton(); + lastProfile = null; + } }, - ); - } + }, + ); nameInputEl.setValue(null); disableButton(); diff --git a/frontend/src/ts/states/connection.ts b/frontend/src/ts/states/connection.ts index 06fe1ac10d68..b9f0327299bc 100644 --- a/frontend/src/ts/states/connection.ts +++ b/frontend/src/ts/states/connection.ts @@ -15,18 +15,16 @@ let bannerAlreadyClosed = false; export function showOfflineBanner(): void { if (bannerAlreadyClosed) return; - if (noInternetBannerId === undefined) { - noInternetBannerId = Notifications.addPSA( - "No internet connection", - 0, - "exclamation-triangle", - false, - () => { - bannerAlreadyClosed = true; - noInternetBannerId = undefined; - }, - ); - } + noInternetBannerId ??= Notifications.addPSA( + "No internet connection", + 0, + "exclamation-triangle", + false, + () => { + bannerAlreadyClosed = true; + noInternetBannerId = undefined; + }, + ); } const throttledHandleState = debounce(5000, () => { diff --git a/frontend/src/ts/test/practise-words.ts b/frontend/src/ts/test/practise-words.ts index efa708bd77f4..3ad29d0eced1 100644 --- a/frontend/src/ts/test/practise-words.ts +++ b/frontend/src/ts/test/practise-words.ts @@ -139,10 +139,9 @@ export function init( } }); - const mode = before.mode === null ? Config.mode : before.mode; - const punctuation = - before.punctuation === null ? Config.punctuation : before.punctuation; - const numbers = before.numbers === null ? Config.numbers : before.numbers; + const mode = before.mode ?? Config.mode; + const punctuation = before.punctuation ?? Config.punctuation; + const numbers = before.numbers ?? Config.numbers; let customText = null; if (Config.mode === "custom") { diff --git a/frontend/src/ts/test/result.ts b/frontend/src/ts/test/result.ts index 6c8b20fc14f5..c1b57ba9b7c1 100644 --- a/frontend/src/ts/test/result.ts +++ b/frontend/src/ts/test/result.ts @@ -1296,12 +1296,8 @@ $(".pageTest #favoriteQuoteButton").on("click", async () => { if (response.status === 200) { $button.removeClass("far").addClass("fas"); - if (dbSnapshot.favoriteQuotes === undefined) { - dbSnapshot.favoriteQuotes = {}; - } - if (!dbSnapshot.favoriteQuotes[quoteLang]) { - dbSnapshot.favoriteQuotes[quoteLang] = []; - } + dbSnapshot.favoriteQuotes ??= {}; + dbSnapshot.favoriteQuotes[quoteLang] ??= []; dbSnapshot.favoriteQuotes[quoteLang]?.push(quoteId); } } diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index d1c1641ec26c..26d69fa004bf 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -872,11 +872,8 @@ export async function updateWordLetters({ for (let i = 0; i < compositionData.length; i++) { const compositionChar = compositionData[i]; - let charToShow = currentWordChars[input.length + i]; - - if (charToShow === undefined) { - charToShow = compositionChar; - } + let charToShow = + currentWordChars[input.length + i] ?? compositionChar; if (Config.indicateTypos === "replace") { charToShow = compositionChar === " " ? "_" : compositionChar; diff --git a/frontend/src/ts/test/words-generator.ts b/frontend/src/ts/test/words-generator.ts index d61029e9a6e8..00ed47d8607c 100644 --- a/frontend/src/ts/test/words-generator.ts +++ b/frontend/src/ts/test/words-generator.ts @@ -392,7 +392,7 @@ function applyLazyModeToWord(word: string, language: LanguageObject): string { ? currentWordset.languageProperties.get(langName) : undefined; const allowLazyMode = - (langProps && !langProps.noLazyMode) || Config.mode === "custom"; + (langProps && !langProps.noLazyMode) === true || Config.mode === "custom"; if (Config.lazyMode && allowLazyMode && langProps) { word = LazyMode.replaceAccents(word, langProps.additionalAccents); } diff --git a/frontend/src/ts/utils/format.ts b/frontend/src/ts/utils/format.ts index 054d4d8fde8c..8ee9f4fd595d 100644 --- a/frontend/src/ts/utils/format.ts +++ b/frontend/src/ts/utils/format.ts @@ -74,9 +74,8 @@ export class Formatting { const suffix = formatOptions.suffix ?? ""; if ( - formatOptions.showDecimalPlaces !== undefined - ? formatOptions.showDecimalPlaces - : this.config.alwaysShowDecimalPlaces + formatOptions.showDecimalPlaces ?? + this.config.alwaysShowDecimalPlaces ) { return Numbers.roundTo2(value).toFixed(2) + suffix; } diff --git a/frontend/src/ts/utils/sorted-table.ts b/frontend/src/ts/utils/sorted-table.ts index 62d536c01732..4c37d33db9c9 100644 --- a/frontend/src/ts/utils/sorted-table.ts +++ b/frontend/src/ts/utils/sorted-table.ts @@ -128,9 +128,7 @@ export class SortedTable { body.empty(); body.append( this.getData().map((data) => { - if (data.element === undefined) { - data.element = this.buildRow(data.source); - } + data.element ??= this.buildRow(data.source); return data.element; }), ); diff --git a/packages/eslint-config/index.js b/packages/eslint-config/index.js index 2d76f819c1ed..940b41ded2db 100644 --- a/packages/eslint-config/index.js +++ b/packages/eslint-config/index.js @@ -122,6 +122,7 @@ module.exports = { ], //using + "@typescript-eslint/prefer-nullish-coalescing": "error", "@typescript-eslint/no-unsafe-member-access": "error", "@typescript-eslint/no-unsafe-call": "error", "@typescript-eslint/no-unsafe-argument": "error", diff --git a/packages/funbox/src/validation.ts b/packages/funbox/src/validation.ts index 8d0cee679736..b7c7c98ea16a 100644 --- a/packages/funbox/src/validation.ts +++ b/packages/funbox/src/validation.ts @@ -33,9 +33,9 @@ export function checkCompatibility( const oneWordModifierMax = funboxesToCheck.filter( (f) => - f.frontendFunctions?.includes("getWord") || - f.frontendFunctions?.includes("pullSection") || - f.frontendFunctions?.includes("withWords"), + f.frontendFunctions?.includes("getWord") === true || + f.frontendFunctions?.includes("pullSection") === true || + f.frontendFunctions?.includes("withWords") === true, ).length <= 1; const oneWordOrderMax = funboxesToCheck.filter( From 2ee582a5974bb81a1b358a3eb843a1a44c12940a Mon Sep 17 00:00:00 2001 From: Miodec Date: Tue, 9 Dec 2025 22:38:27 +0100 Subject: [PATCH 4/5] chore: configure eslint/curly rule --- backend/__tests__/__testData__/auth.ts | 3 ++- backend/src/api/controllers/dev.ts | 9 +++++--- backend/src/api/controllers/user.ts | 3 ++- backend/src/api/controllers/webhooks.ts | 3 ++- backend/src/dal/connections.ts | 3 ++- backend/src/dal/user.ts | 18 ++++++++++------ backend/src/middlewares/configuration.ts | 12 +++++++---- backend/src/middlewares/permission.ts | 9 +++++--- .../src/ts/controllers/chart-controller.ts | 3 ++- frontend/src/ts/elements/keymap.ts | 3 ++- frontend/src/ts/elements/profile.ts | 3 ++- .../src/ts/elements/result-word-highlight.ts | 6 ++++-- .../src/ts/elements/test-activity-calendar.ts | 3 ++- frontend/src/ts/modals/simple-modals.ts | 12 +++++++---- frontend/src/ts/pages/friends.ts | 3 ++- frontend/src/ts/test/custom-text.ts | 6 ++++-- frontend/src/ts/test/replay.ts | 3 ++- frontend/src/ts/test/test-config.ts | 3 ++- frontend/src/ts/test/test-ui.ts | 21 ++++++++++++------- frontend/src/ts/utils/format.ts | 6 ++++-- frontend/src/ts/utils/ip-addresses.ts | 6 ++++-- frontend/src/ts/utils/misc.ts | 3 ++- frontend/src/ts/utils/simple-modal.ts | 3 ++- packages/oxlint-config/index.jsonc | 2 ++ 24 files changed, 98 insertions(+), 48 deletions(-) diff --git a/backend/__tests__/__testData__/auth.ts b/backend/__tests__/__testData__/auth.ts index e185d8b0061c..ec5a446291e2 100644 --- a/backend/__tests__/__testData__/auth.ts +++ b/backend/__tests__/__testData__/auth.ts @@ -12,8 +12,9 @@ export async function mockAuthenticateWithApeKey( uid: string, config: Configuration, ): Promise { - if (!config.apeKeys.acceptKeys) + if (!config.apeKeys.acceptKeys) { throw Error("config.apeKeys.acceptedKeys needs to be set to true"); + } const { apeKeyBytes, apeKeySaltRounds } = config.apeKeys; const apiKey = randomBytes(apeKeyBytes).toString("base64url"); diff --git a/backend/src/api/controllers/dev.ts b/backend/src/api/controllers/dev.ts index cbc762c55204..31bd30d980a5 100644 --- a/backend/src/api/controllers/dev.ts +++ b/backend/src/api/controllers/dev.ts @@ -96,8 +96,9 @@ async function createTestResults( const results = createArray(day.amount, () => createResult(user, day.timestamp), ); - if (results.length > 0) + if (results.length > 0) { await ResultDal.getResultCollection().insertMany(results); + } } } @@ -222,8 +223,9 @@ async function updateUser(uid: string): Promise { )) as DBResult; personalBests[mode.mode] ??= {}; - if (personalBests[mode.mode][mode.mode2] === undefined) + if (personalBests[mode.mode][mode.mode2] === undefined) { personalBests[mode.mode][mode.mode2] = []; + } const entry = { acc: best.acc, @@ -241,8 +243,9 @@ async function updateUser(uid: string): Promise { (personalBests[mode.mode][mode.mode2] as PersonalBest[]).push(entry); if (mode.mode === "time") { - if (lbPersonalBests[mode.mode][mode.mode2] === undefined) + if (lbPersonalBests[mode.mode][mode.mode2] === undefined) { lbPersonalBests[mode.mode][mode.mode2] = {}; + } // eslint-disable-next-line @typescript-eslint/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 6620abf08547..c0d5213d1f95 100644 --- a/backend/src/api/controllers/user.ts +++ b/backend/src/api/controllers/user.ts @@ -1207,8 +1207,9 @@ export function generateCurrentTestActivity( let thisYearData = testActivity?.[thisYear.getFullYear().toString()]; let lastYearData = testActivity?.[lastYear.getFullYear().toString()]; - if (lastYearData === undefined && thisYearData === undefined) + if (lastYearData === undefined && thisYearData === undefined) { return undefined; + } lastYearData = lastYearData ?? []; thisYearData = thisYearData ?? []; diff --git a/backend/src/api/controllers/webhooks.ts b/backend/src/api/controllers/webhooks.ts index 1f3f052ded45..b079ecb74438 100644 --- a/backend/src/api/controllers/webhooks.ts +++ b/backend/src/api/controllers/webhooks.ts @@ -11,8 +11,9 @@ export async function githubRelease( if (action === "published") { const releaseId = req.body.release?.id; - if (releaseId === undefined) + if (releaseId === undefined) { throw new MonkeyError(422, 'Missing property "release.id".'); + } await GeorgeQueue.sendReleaseAnnouncement(releaseId); return new MonkeyResponse("Added release announcement task to queue", null); diff --git a/backend/src/dal/connections.ts b/backend/src/dal/connections.ts index 4945943d0a33..0500dc181890 100644 --- a/backend/src/dal/connections.ts +++ b/backend/src/dal/connections.ts @@ -20,8 +20,9 @@ export async function getConnections(options: { }): Promise { const { initiatorUid, receiverUid, status } = options; - if (initiatorUid === undefined && receiverUid === undefined) + if (initiatorUid === undefined && receiverUid === undefined) { throw new Error("Missing filter"); + } let filter: Filter = { $or: [] }; diff --git a/backend/src/dal/user.ts b/backend/src/dal/user.ts index 7190644d6dc0..67905f643427 100644 --- a/backend/src/dal/user.ts +++ b/backend/src/dal/user.ts @@ -459,8 +459,9 @@ export async function checkIfPb( "stopOnLetter" in result && result.stopOnLetter === true && result.acc < 100 - ) + ) { return false; + } if (mode === "quote") { return false; @@ -510,8 +511,9 @@ export async function checkIfTagPb( "stopOnLetter" in result && result.stopOnLetter === true && result.acc < 100 - ) + ) { return []; + } if (mode === "quote") { return []; @@ -605,8 +607,9 @@ export async function linkDiscord( discordAvatar?: string, ): Promise { const updates: Partial = { discordId }; - if (discordAvatar !== undefined && discordAvatar !== null) + if (discordAvatar !== undefined && discordAvatar !== null) { updates.discordAvatar = discordAvatar; + } await updateUser({ uid }, { $set: updates }, { stack: "link discord" }); } @@ -1055,10 +1058,11 @@ export async function updateInbox( // mongo doesnt support ??= i think // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - if (inventory === null) + if (inventory === null) { inventory = { badges: [], }; + } // mongo doesnt support ??= i think // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing if (inventory.badges === null) inventory.badges = []; @@ -1104,8 +1108,9 @@ export async function updateInbox( { $unset: "tmp" }, ]); - if (update.matchedCount !== 1) + if (update.matchedCount !== 1) { throw new MonkeyError(404, "User not found", "update inbox"); + } } export async function updateStreak( @@ -1229,12 +1234,13 @@ async function updateUser( ): Promise { const result = await getUsersCollection().updateOne(filter, update); - if (result.matchedCount !== 1) + if (result.matchedCount !== 1) { throw new MonkeyError( error.statusCode ?? 404, error.message ?? "User not found", error.stack, ); + } } export async function getFriends(uid: string): Promise { diff --git a/backend/src/middlewares/configuration.ts b/backend/src/middlewares/configuration.ts index 2993df3977d7..c14badbc5a4c 100644 --- a/backend/src/middlewares/configuration.ts +++ b/backend/src/middlewares/configuration.ts @@ -62,26 +62,30 @@ function getValue( result = result[key]; } - if (result === undefined || result === null) + if (result === undefined || result === null) { throw new MonkeyError( 500, `Required configuration doesnt exist: "${path}"`, ); - if (typeof result !== "boolean") + } + if (typeof result !== "boolean") { throw new MonkeyError( 500, `Required configuration is not a boolean: "${path}"`, ); + } return result; } function getRequireConfigurations( metadata: EndpointMetadata | undefined, ): RequireConfiguration[] | undefined { - if (metadata === undefined || metadata.requireConfiguration === undefined) + if (metadata === undefined || metadata.requireConfiguration === undefined) { return undefined; + } - if (Array.isArray(metadata.requireConfiguration)) + if (Array.isArray(metadata.requireConfiguration)) { return metadata.requireConfiguration; + } return [metadata.requireConfiguration]; } diff --git a/backend/src/middlewares/permission.ts b/backend/src/middlewares/permission.ts index 805ce07272a5..ec1fad970090 100644 --- a/backend/src/middlewares/permission.ts +++ b/backend/src/middlewares/permission.ts @@ -136,11 +136,13 @@ export function verifyPermissions< function getRequiredPermissionIds( metadata: EndpointMetadata | undefined, ): PermissionId[] | undefined { - if (metadata === undefined || metadata.requirePermission === undefined) + if (metadata === undefined || metadata.requirePermission === undefined) { return undefined; + } - if (Array.isArray(metadata.requirePermission)) + if (Array.isArray(metadata.requirePermission)) { return metadata.requirePermission; + } return [metadata.requirePermission]; } @@ -186,11 +188,12 @@ async function checkUserPermissions( )) as DBUser; for (const check of checks) { - if (!check.criteria(user)) + if (!check.criteria(user)) { return { passed: false, invalidMessage: check.invalidMessage, }; + } } return { diff --git a/frontend/src/ts/controllers/chart-controller.ts b/frontend/src/ts/controllers/chart-controller.ts index 424a4105713a..32e4de9d6fd3 100644 --- a/frontend/src/ts/controllers/chart-controller.ts +++ b/frontend/src/ts/controllers/chart-controller.ts @@ -1360,8 +1360,9 @@ async function updateColors< ao10accDataset === undefined || ao100wpmDataset === undefined || ao100accDataset === undefined - ) + ) { return; + } if (avg10On && avg100On) { wpmDataset.pointBackgroundColor = main02; diff --git a/frontend/src/ts/elements/keymap.ts b/frontend/src/ts/elements/keymap.ts index 66f7557e7d5a..afbe14c94e79 100644 --- a/frontend/src/ts/elements/keymap.ts +++ b/frontend/src/ts/elements/keymap.ts @@ -563,8 +563,9 @@ async function updateLegends(): Promise { layoutKey === undefined || lowerCaseCharacter === undefined || upperCaseCharacter === undefined - ) + ) { continue; + } const keyIsSymbol = [lowerCaseCharacter, upperCaseCharacter].some( (character) => symbolsPattern.test(character ?? ""), diff --git a/frontend/src/ts/elements/profile.ts b/frontend/src/ts/elements/profile.ts index 40a13c64923b..6db378ad37d3 100644 --- a/frontend/src/ts/elements/profile.ts +++ b/frontend/src/ts/elements/profile.ts @@ -48,8 +48,9 @@ export async function update( profile === undefined || profile.name === undefined || profile.addedAt === undefined - ) + ) { return; + } const avatar = details.find(".avatarAndName .avatar"); avatar.replaceWith(getAvatarElement(profile, { size: 256 })); diff --git a/frontend/src/ts/elements/result-word-highlight.ts b/frontend/src/ts/elements/result-word-highlight.ts index e29b7f5e2c42..fb2905feafe3 100644 --- a/frontend/src/ts/elements/result-word-highlight.ts +++ b/frontend/src/ts/elements/result-word-highlight.ts @@ -418,8 +418,9 @@ function getHighlightElementPositions( line === undefined || nextPosition === undefined || container === undefined - ) + ) { continue; + } if (!isRTL) { position.highlightLeft = @@ -460,8 +461,9 @@ function getHighlightElementPositions( line === undefined || prevHighlightPosition === undefined || container === undefined - ) + ) { continue; + } if (!isRTL) { position.highlightLeft = diff --git a/frontend/src/ts/elements/test-activity-calendar.ts b/frontend/src/ts/elements/test-activity-calendar.ts index a9b0c7495a5c..01b72b158c92 100644 --- a/frontend/src/ts/elements/test-activity-calendar.ts +++ b/frontend/src/ts/elements/test-activity-calendar.ts @@ -125,8 +125,9 @@ export class TestActivityCalendar implements TestActivityCalendar { const getValue = (v: number | null | undefined): string => { if (v === undefined) return "0"; if (v === null || v === 0) return "0"; - for (let b = 0; b < 4; b++) + for (let b = 0; b < 4; b++) { if (v <= (buckets[b] ?? 0)) return (1 + b).toString(); + } return "4"; }; diff --git a/frontend/src/ts/modals/simple-modals.ts b/frontend/src/ts/modals/simple-modals.ts index c7ec89d5aae1..367d3ab5db37 100644 --- a/frontend/src/ts/modals/simple-modals.ts +++ b/frontend/src/ts/modals/simple-modals.ts @@ -1259,14 +1259,18 @@ list.devGenerateData = new SimpleModal({ username, createUser: createUser === "true", }; - if (firstTestTimestamp !== undefined && firstTestTimestamp.length > 0) + if (firstTestTimestamp !== undefined && firstTestTimestamp.length > 0) { request.firstTestTimestamp = Date.parse(firstTestTimestamp); - if (lastTestTimestamp !== undefined && lastTestTimestamp.length > 0) + } + if (lastTestTimestamp !== undefined && lastTestTimestamp.length > 0) { request.lastTestTimestamp = Date.parse(lastTestTimestamp); - if (minTestsPerDay !== undefined && minTestsPerDay.length > 0) + } + if (minTestsPerDay !== undefined && minTestsPerDay.length > 0) { request.minTestsPerDay = Number.parseInt(minTestsPerDay); - if (maxTestsPerDay !== undefined && maxTestsPerDay.length > 0) + } + if (maxTestsPerDay !== undefined && maxTestsPerDay.length > 0) { request.maxTestsPerDay = Number.parseInt(maxTestsPerDay); + } const result = await Ape.dev.generateData({ body: request }); diff --git a/frontend/src/ts/pages/friends.ts b/frontend/src/ts/pages/friends.ts index ae3739b4467c..31d72ad9fff0 100644 --- a/frontend/src/ts/pages/friends.ts +++ b/frontend/src/ts/pages/friends.ts @@ -42,8 +42,9 @@ export function getReceiverUid( connection: Pick, ): string { const me = getAuthenticatedUser(); - if (me === null) + if (me === null) { throw new Error("expected to be authenticated in getReceiverUid"); + } if (me.uid === connection.initiatorUid) return connection.receiverUid; return connection.initiatorUid; diff --git a/frontend/src/ts/test/custom-text.ts b/frontend/src/ts/test/custom-text.ts index 63e065a77621..41fa55953628 100644 --- a/frontend/src/ts/test/custom-text.ts +++ b/frontend/src/ts/test/custom-text.ts @@ -140,13 +140,15 @@ export function getCustomText(name: string, long = false): string[] { if (long) { const customTextLong = getLocalStorageLong(); const customText = customTextLong[name]; - if (customText === undefined) + if (customText === undefined) { throw new Error(`Custom text ${name} not found`); + } return customText.text.split(/ +/); } else { const customText = getLocalStorage()[name]; - if (customText === undefined) + if (customText === undefined) { throw new Error(`Custom text ${name} not found`); + } return customText.split(/ +/); } } diff --git a/frontend/src/ts/test/replay.ts b/frontend/src/ts/test/replay.ts index 4aa3cc104701..0e24c4c61047 100644 --- a/frontend/src/ts/test/replay.ts +++ b/frontend/src/ts/test/replay.ts @@ -149,8 +149,9 @@ function handleDisplayLogic(item: Replay, nosound = false): void { const replayWords = document.getElementById("replayWords"); - if (replayWords !== null) + if (replayWords !== null) { activeWord = replayWords.children[wordPos] as HTMLElement; + } curPos = activeWord.children.length; while (activeWord.children[curPos - 1]?.className === "") curPos--; diff --git a/frontend/src/ts/test/test-config.ts b/frontend/src/ts/test/test-config.ts index ab928ae616db..adde4037d3c7 100644 --- a/frontend/src/ts/test/test-config.ts +++ b/frontend/src/ts/test/test-config.ts @@ -342,8 +342,9 @@ ConfigEvent.subscribe((eventKey, eventValue, _nosave, eventPreviousValue) => { eventKey, ) ) { - if (eventValue !== undefined) + if (eventValue !== undefined) { updateActiveExtraButtons(eventKey, eventValue); + } } }); diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index 26d69fa004bf..f05bc21acf48 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -110,12 +110,13 @@ ConfigEvent.subscribe((eventKey, eventValue, nosave) => { if (eventValue === undefined) return; if (eventKey === "highlightMode") { - if (ActivePage.get() === "test") + if (ActivePage.get() === "test") { void updateWordLetters({ input: TestInput.input.current, wordIndex: TestState.activeWordIndex, compositionData: CompositionState.getData(), }); + } } if ( @@ -384,8 +385,9 @@ async function updateHintsPosition(): Promise { ActivePage.get() !== "test" || TestState.resultVisible || (Config.indicateTypos !== "below" && Config.indicateTypos !== "both") - ) + ) { return; + } let previousHintsContainer: HTMLElement | undefined; let hintIndices: number[][] = []; @@ -465,9 +467,10 @@ function buildWordHTML(word: string, wordIndex: number): string { } } retval += ""; - if (newlineafter) + if (newlineafter) { retval += "
"; + } return retval; } @@ -863,9 +866,11 @@ export async function updateWordLetters({ Config.indicateTypos === "both" ) { const lastBlock = hintIndices[hintIndices.length - 1]; - if (lastBlock && lastBlock[lastBlock.length - 1] === i - 1) + if (lastBlock && lastBlock[lastBlock.length - 1] === i - 1) { lastBlock.push(i); - else hintIndices.push([i]); + } else { + hintIndices.push([i]); + } } } } @@ -923,11 +928,12 @@ export async function updateWordLetters({ ); } - if (newlineafter) + if (newlineafter) { wordAtIndex.insertAdjacentHTML( "afterend", "
", ); + } if (Config.tapeMode !== "off") { void scrollTape(); } @@ -1126,8 +1132,9 @@ export async function scrollTape(noAnimation = false): Promise { if (letterOuterWidth > 0) lastPositiveLetterWidth = letterOuterWidth; } // if current letter has zero width move the tape to previous positive width letter - if (letters[inputLength]?.offsetWidth === 0) + if (letters[inputLength]?.offsetWidth === 0) { currentWordWidth -= lastPositiveLetterWidth; + } } /* change to new #words & .afterNewline margins */ diff --git a/frontend/src/ts/utils/format.ts b/frontend/src/ts/utils/format.ts index 8ee9f4fd595d..c8ebab24b644 100644 --- a/frontend/src/ts/utils/format.ts +++ b/frontend/src/ts/utils/format.ts @@ -69,8 +69,9 @@ export class Formatting { value: number | null | undefined, formatOptions: FormatOptions, ): string { - if (value === undefined || value === null) + if (value === undefined || value === null) { return formatOptions.fallback ?? ""; + } const suffix = formatOptions.suffix ?? ""; if ( @@ -88,8 +89,9 @@ export class Formatting { ): string { const options = { fallback: "-", ...formatOptions }; - if (position === undefined || position === null) + if (position === undefined || position === null) { return options.fallback ?? ""; + } let numend = "th"; const t = position % 10; const h = position % 100; diff --git a/frontend/src/ts/utils/ip-addresses.ts b/frontend/src/ts/utils/ip-addresses.ts index fa25468a35a5..ffa1be81ee36 100644 --- a/frontend/src/ts/utils/ip-addresses.ts +++ b/frontend/src/ts/utils/ip-addresses.ts @@ -118,9 +118,11 @@ export function compressIpv6(ip: string): string { index >= longestStartIndex && index <= longestEndIndex && longestStartIndex !== longestEndIndex - ) + ) { return ":"; - else return "0"; + } else { + return "0"; + } } return word; diff --git a/frontend/src/ts/utils/misc.ts b/frontend/src/ts/utils/misc.ts index 8f63926cb756..e3e17c6a7662 100644 --- a/frontend/src/ts/utils/misc.ts +++ b/frontend/src/ts/utils/misc.ts @@ -69,8 +69,9 @@ export function findGetParameter( .split("&") .forEach(function (item) { tmp = item.split("="); - if (tmp[0] === parameterName) + if (tmp[0] === parameterName) { result = decodeURIComponent(tmp[1] as string); + } }); return result; } diff --git a/frontend/src/ts/utils/simple-modal.ts b/frontend/src/ts/utils/simple-modal.ts index 63364467637a..ae0d19154cee 100644 --- a/frontend/src/ts/utils/simple-modal.ts +++ b/frontend/src/ts/utils/simple-modal.ts @@ -330,8 +330,9 @@ export class SimpleModal { }; input.currentValue = () => { - if (element.type === "checkbox") + if (element.type === "checkbox") { return element.checked ? "true" : "false"; + } return element.value; }; diff --git a/packages/oxlint-config/index.jsonc b/packages/oxlint-config/index.jsonc index ed86cd72349f..c72da54c0af9 100644 --- a/packages/oxlint-config/index.jsonc +++ b/packages/oxlint-config/index.jsonc @@ -96,6 +96,8 @@ }, ], "unicorn/prefer-includes": "error", + "unicorn/prefer-structured-clone": "error", + "eslint/curly": ["error", "multi-line", "consistent"], // todo: enable "no-array-for-each": "off", From dd970cced79a87b712c020928f7b39c95e16da91 Mon Sep 17 00:00:00 2001 From: Miodec Date: Tue, 9 Dec 2025 22:57:25 +0100 Subject: [PATCH 5/5] chore: use pnpm on pre-push --- .husky/pre-push | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.husky/pre-push b/.husky/pre-push index 8abaca04cd69..4b650032d487 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -9,7 +9,7 @@ export NVM_DIR="$HOME/.nvm" if [ $(git branch --no-color | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/') = "master" ] && [ $(git remote get-url origin) = "https://github.com/monkeytypegame/monkeytype" ]; then nvm install echo "Running a full check before pushing to master..." - npm run full-check + pnpm run full-check if [ $? -ne 0 ]; then echo "Full check failed, aborting push." exit 1