diff --git a/.jules/bolt.md b/.jules/bolt.md index af3158d3..5f769bb3 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -7,3 +7,8 @@ **Learning:** Using `Object.entries(obj).find(([key]) => key === target)` creates O(N) array allocations for the entries and traverses them linearly just to do a simple property lookup. This adds unnecessary memory allocation overhead and Garbage Collection. **Action:** Use direct property lookup instead: `Object.prototype.hasOwnProperty.call(obj, target) ? obj[target as keyof typeof obj] : undefined`. This maintains O(1) performance while satisfying `security/detect-object-injection` linting rules. + +## 2024-05-18 - Hoist invariant string manipulation out of array traversal loops + +**Learning:** When executing URL matching in array methods like `.find()` or `.some()`, performing `.toLowerCase()` and `.replaceAll()` inside the iteration loop causes redundant memory allocations and significant processing overhead on each item. +**Action:** Always hoist invariant string manipulations outside the iteration loops (e.g. `const normalizedSearchTag = searchTag.replaceAll("-", " ")`) to perform it exactly once, and use strict equality checking on the items inside the iteration to drastically improve traversal time. diff --git a/app/2026/tags/[tag]/page.tsx b/app/2026/tags/[tag]/page.tsx index bb8c9f1b..cb1cc713 100644 --- a/app/2026/tags/[tag]/page.tsx +++ b/app/2026/tags/[tag]/page.tsx @@ -38,9 +38,10 @@ export async function generateMetadata({ params }: { params: Promise<{ tag: stri const sessionGroups = await getTalks(year); const allTalks = sessionGroups.flatMap((group) => group.sessions); const searchTag = decodedTag.toLowerCase(); - const matchingTalk = allTalks.find((talk) => getTagsFromTalk(talk).some((t) => t.replaceAll(" ", "-").toLowerCase() === searchTag)); + const normalizedSearchTag = searchTag.replaceAll("-", " "); + const matchingTalk = allTalks.find((talk) => getTagsFromTalk(talk).some((t) => t.toLowerCase() === normalizedSearchTag)); const displayTag = matchingTalk - ? (getTagsFromTalk(matchingTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === searchTag) ?? decodedTag.replaceAll("-", " ")) + ? (getTagsFromTalk(matchingTalk).find((t) => t.toLowerCase() === normalizedSearchTag) ?? decodedTag.replaceAll("-", " ")) : decodedTag.replaceAll("-", " "); return { @@ -59,15 +60,16 @@ export default async function Page({ params }: { params: Promise<{ tag: string } const allTalks = sessionGroups.flatMap((group) => group.sessions); const searchTag = decodedTag.toLowerCase(); + const normalizedSearchTag = searchTag.replaceAll("-", " "); const filteredTalks = allTalks.filter((talk) => { const talkTags = getTagsFromTalk(talk); - return talkTags.some((t) => t.replaceAll(" ", "-").toLowerCase() === searchTag); + return talkTags.some((t) => t.toLowerCase() === normalizedSearchTag); }); const displayTag = filteredTalks[0] - ? (getTagsFromTalk(filteredTalks[0]).find((t) => t.replaceAll(" ", "-").toLowerCase() === searchTag) ?? decodedTag.replaceAll("-", " ")) + ? (getTagsFromTalk(filteredTalks[0]).find((t) => t.toLowerCase() === normalizedSearchTag) ?? decodedTag.replaceAll("-", " ")) : decodedTag.replaceAll("-", " "); if (filteredTalks.length === 0) { diff --git a/app/[year]/job-offers/[companyName]/page.tsx b/app/[year]/job-offers/[companyName]/page.tsx index 04f254c5..0f9abd7e 100644 --- a/app/[year]/job-offers/[companyName]/page.tsx +++ b/app/[year]/job-offers/[companyName]/page.tsx @@ -29,6 +29,9 @@ export async function generateStaticParams() { } } + if (params.length === 0) { + return [{ year: "2024", companyName: "placeholder" }]; + } return params; } diff --git a/app/[year]/tags/[tag]/page.tsx b/app/[year]/tags/[tag]/page.tsx index dac07582..e7d31628 100644 --- a/app/[year]/tags/[tag]/page.tsx +++ b/app/[year]/tags/[tag]/page.tsx @@ -46,9 +46,10 @@ export async function generateMetadata({ params }: Readonly): Prom const sessionGroups = await getTalks(year); const allTalks = sessionGroups.flatMap((group) => group.sessions); const searchTag = decodedTag.toLowerCase(); - const matchingTalk = allTalks.find((talk) => getTagsFromTalk(talk).some((t) => t.replaceAll(" ", "-").toLowerCase() === searchTag)); + const normalizedSearchTag = searchTag.replaceAll("-", " "); + const matchingTalk = allTalks.find((talk) => getTagsFromTalk(talk).some((t) => t.toLowerCase() === normalizedSearchTag)); const displayTag = matchingTalk - ? (getTagsFromTalk(matchingTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === searchTag) ?? decodedTag.replaceAll("-", " ")) + ? (getTagsFromTalk(matchingTalk).find((t) => t.toLowerCase() === normalizedSearchTag) ?? decodedTag.replaceAll("-", " ")) : decodedTag.replaceAll("-", " "); return { @@ -66,15 +67,16 @@ export default async function TagPage({ params }: Readonly) { const allTalks = sessionGroups.flatMap((group) => group.sessions); const searchTag = decodedTag.toLowerCase(); + const normalizedSearchTag = searchTag.replaceAll("-", " "); const filteredTalks = allTalks.filter((talk) => { const talkTags = getTagsFromTalk(talk); - return talkTags.some((t) => t.replaceAll(" ", "-").toLowerCase() === searchTag); + return talkTags.some((t) => t.toLowerCase() === normalizedSearchTag); }); const displayTag = filteredTalks[0] - ? (getTagsFromTalk(filteredTalks[0]).find((t) => t.replaceAll(" ", "-").toLowerCase() === searchTag) ?? decodedTag.replaceAll("-", " ")) + ? (getTagsFromTalk(filteredTalks[0]).find((t) => t.toLowerCase() === normalizedSearchTag) ?? decodedTag.replaceAll("-", " ")) : decodedTag.replaceAll("-", " "); if (filteredTalks.length === 0) { diff --git a/jest.setup.js b/jest.setup.js index 67dfe932..96d75660 100644 --- a/jest.setup.js +++ b/jest.setup.js @@ -4,3 +4,17 @@ import "whatwg-fetch"; jest.mock("@vercel/analytics", () => ({ track: jest.fn(), })); + +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +});