Skip to content

Enhances ESLint configuration and rules#106

Merged
kiliantyler merged 27 commits intomainfrom
add-eslint-rules
Oct 3, 2025
Merged

Enhances ESLint configuration and rules#106
kiliantyler merged 27 commits intomainfrom
add-eslint-rules

Conversation

@kiliantyler
Copy link
Copy Markdown
Collaborator

@kiliantyler kiliantyler commented Oct 3, 2025

Improves code quality and consistency by refining ESLint configuration and rules:

  • Introduces eslint-plugin-unicorn for enhanced linting rules.
  • Migrates cookie operations to js-cookie
  • Fixes codebase issues reported by new and existing linting rules.
  • Refactors code for improved clarity and performance.

This also includes updates to dependencies and build configurations.

Summary by CodeRabbit

  • Bug Fixes

    • More reliable keyboard navigation, pet age display, numeric/date parsing, theme initialization, and error logging.
  • Refactor

    • Modernized DOM, array, and string usage; simplified animations, focus handling, and game overlay logic for smoother behavior.
  • Chores

    • Consolidated runtime build into a single step producing bundled runtime assets, added cookie-based persistence for themes/achievements, upgraded tooling (ESLint Unicorn preset, TypeScript target ES2023) and dependency updates.

@vercel
Copy link
Copy Markdown

vercel bot commented Oct 3, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
kil-dev Ready Ready Preview Comment Oct 3, 2025 4:57pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Oct 3, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Enhances ESLint configuration and rules” directly reflects the addition of eslint-plugin-unicorn and rule adjustments, which drive many of the code changes, but it only captures one aspect of this large PR that also includes cookie migrations, dependency updates, and build configuration improvements.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8754f9c and 31ceb52.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • package.json (5 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (6)
src/components/layout/home/hero/profile-image.tsx (1)

78-83: Refactor looks correct, but consider removing type assertions.

The simplified compound conditions correctly preserve the original logic. However, the repeated as AchievementId type assertions suggest these string literals are not properly typed. If 'LADYBIRD_LANDING', 'CONFUSED_CLICK', and 'GRUMPY_GLIMPSE' (lines 94, 110) are valid achievement IDs, consider defining them as const values with explicit AchievementId type or using an enum to eliminate the need for type assertions.

Example refactor to centralize achievement IDs:

// In achievements.ts or a constants file
export const ACHIEVEMENT_IDS = {
  LADYBIRD_LANDING: 'LADYBIRD_LANDING',
  CONFUSED_CLICK: 'CONFUSED_CLICK',
  GRUMPY_GLIMPSE: 'GRUMPY_GLIMPSE',
} as const satisfies Record<string, AchievementId>

Then use throughout the file:

-    if (isLadybird && !has('LADYBIRD_LANDING' as AchievementId)) {
-      unlock('LADYBIRD_LANDING' as AchievementId)
+    if (isLadybird && !has(ACHIEVEMENT_IDS.LADYBIRD_LANDING)) {
+      unlock(ACHIEVEMENT_IDS.LADYBIRD_LANDING)
     }
-    if (useConfused && !has('CONFUSED_CLICK' as AchievementId)) {
-      unlock('CONFUSED_CLICK' as AchievementId)
+    if (useConfused && !has(ACHIEVEMENT_IDS.CONFUSED_CLICK)) {
+      unlock(ACHIEVEMENT_IDS.CONFUSED_CLICK)
     }
src/hooks/use-leaderboard.ts (2)

125-126: Consider reverting to charCodeAt/fromCharCode for ASCII-only operations.

The switch from charCodeAt/fromCharCode to codePointAt/fromCodePoint adds complexity without benefit when handling single ASCII characters (A-Z). Both APIs are functionally identical for Basic Multilingual Plane characters. The ?? 0 fallback could produce invalid results (String.fromCodePoint(1) = '\x01') if the guard on line 124 fails, though that guard should be sufficient.

If you prefer to keep the code point APIs for consistency with the broader codebase modernization, consider adding a type assertion or comment clarifying that these operations are guaranteed to be on ASCII characters:

-            const newChar = currentChar === 'Z' ? 'A' : String.fromCodePoint((currentChar.codePointAt(0) ?? 0) + 1)
+            // Safe: currentChar is guaranteed to be A-Z by guard above
+            const newChar = currentChar === 'Z' ? 'A' : String.fromCodePoint(currentChar.codePointAt(0)! + 1)

Alternatively, revert to the simpler ASCII-only APIs:

-            const newChar = currentChar === 'Z' ? 'A' : String.fromCodePoint((currentChar.codePointAt(0) ?? 0) + 1)
+            const newChar = currentChar === 'Z' ? 'A' : String.fromCharCode(currentChar.charCodeAt(0) + 1)

135-136: Align guard conditions between ArrowUp and ArrowDown handlers.

The ArrowDown handler uses a minimal guard (!currentChar at line 135), while the ArrowUp handler includes comprehensive bounds checking (line 124: currentChar === undefined || nameInputPosition < 0 || nameInputPosition >= next.length). For consistency and clarity, both branches should use equivalent guards.

Apply this diff to align the guards:

           setPlayerName(prev => {
             const next = [...prev]
             const currentChar = next[nameInputPosition]
-            if (!currentChar) return prev
+            if (currentChar === undefined || nameInputPosition < 0 || nameInputPosition >= next.length) return prev
             const newChar = currentChar === 'A' ? 'Z' : String.fromCodePoint((currentChar.codePointAt(0) ?? 0) - 1)
             next[nameInputPosition] = newChar
             return next
           })
src/utils/stable-stringify.ts (2)

84-84: Consider using sort() for performance.

While toSorted() is more functional, it creates an unnecessary array allocation here. Since Object.keys() already returns a new array that's only used once, mutating it with sort() is safe and more efficient.

Apply this diff if you prefer to optimize:

-  const keys = Object.keys(obj as Record<string, unknown>).toSorted()
+  const keys = Object.keys(obj as Record<string, unknown>).sort()

25-25: Inconsistent undefined check.

Line 25 still uses typeof value === 'undefined', while lines 73 and 89 now use the simpler value === undefined. For consistency, update line 25 to use the direct comparison as well.

Apply this diff for consistency:

-  if (valueType === 'undefined' || valueType === 'function' || valueType === 'symbol') {
+  if (value === undefined || valueType === 'function' || valueType === 'symbol') {

Note: You can also simplify by removing the valueType check entirely since you're already comparing the value directly.

src/components/snake/name-input-modal.tsx (1)

38-38: Consider using replace instead of replaceAll with a global regex.

When using a global regex (/[^A-Z]/g), the replace method already replaces all occurrences. The replaceAll method is intended for string literals or non-global regex patterns. While this works correctly, replace is more conventional with global regexes.

Apply this diff for consistency:

-      .replaceAll(/[^A-Z]/g, '')
+      .replace(/[^A-Z]/g, '')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f631085 and a2fdb20.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (35)
  • eslint.config.js (3 hunks)
  • package.json (3 hunks)
  • scripts/build-presence-runtime.ts (2 hunks)
  • scripts/build-theme-runtime.ts (2 hunks)
  • scripts/sync-pet-gallery.ts (3 hunks)
  • src/app/api/local-image/[...path]/route.ts (1 hunks)
  • src/app/api/scores/check/route.ts (1 hunks)
  • src/components/layout/about/aboutme/mode-toggle-link.tsx (1 hunks)
  • src/components/layout/about/pets/pet-card/card-back.tsx (1 hunks)
  • src/components/layout/background/grid-lights.tsx (3 hunks)
  • src/components/layout/header/mobile-nav.tsx (2 hunks)
  • src/components/layout/header/nav-lava.tsx (3 hunks)
  • src/components/layout/home/background-snake-game.tsx (1 hunks)
  • src/components/layout/home/game-overlay.tsx (1 hunks)
  • src/components/layout/home/hero/profile-image.tsx (1 hunks)
  • src/components/layout/pet-gallery/_content.tsx (2 hunks)
  • src/components/providers/achievements-provider.tsx (7 hunks)
  • src/components/providers/confetti-provider.tsx (1 hunks)
  • src/components/providers/review-provider.tsx (1 hunks)
  • src/components/providers/theme-provider.tsx (12 hunks)
  • src/components/snake/name-input-modal.tsx (1 hunks)
  • src/components/ui/theme-toggle.tsx (2 hunks)
  • src/hooks/use-leaderboard.ts (2 hunks)
  • src/lib/game-validation.ts (1 hunks)
  • src/lib/leaderboard.ts (3 hunks)
  • src/lib/score-validation.ts (1 hunks)
  • src/utils/achievements.ts (1 hunks)
  • src/utils/arcade-utils.ts (2 hunks)
  • src/utils/grid.ts (1 hunks)
  • src/utils/presence-script.ts (1 hunks)
  • src/utils/stable-stringify.ts (2 hunks)
  • src/utils/theme-runtime.ts (1 hunks)
  • src/utils/theme-script.ts (5 hunks)
  • src/utils/utils.ts (1 hunks)
  • tsconfig.json (3 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx,js,jsx,cjs,mjs}

📄 CodeRabbit inference engine (.cursor/rules/posthog-integration.mdc)

**/*.{ts,tsx,js,jsx,cjs,mjs}: Never hardcode or hallucinate the PostHog API key; always read it from the value populated in the .env file
Create new feature flag names that are clear and descriptive
Gate flag-dependent code on a check that verifies the flag’s values are valid and expected
If a custom property for a person or event is referenced in two or more files or in two or more callsites within the same file, centralize the property name in an enum (TS) or const object (JS)

Files:

  • src/components/layout/home/background-snake-game.tsx
  • src/utils/stable-stringify.ts
  • src/utils/theme-runtime.ts
  • src/components/layout/about/pets/pet-card/card-back.tsx
  • src/utils/achievements.ts
  • src/utils/theme-script.ts
  • src/components/ui/theme-toggle.tsx
  • src/components/snake/name-input-modal.tsx
  • src/components/providers/theme-provider.tsx
  • src/app/api/local-image/[...path]/route.ts
  • src/components/layout/background/grid-lights.tsx
  • src/utils/arcade-utils.ts
  • src/app/api/scores/check/route.ts
  • src/components/providers/review-provider.tsx
  • src/hooks/use-leaderboard.ts
  • scripts/build-presence-runtime.ts
  • src/components/layout/about/aboutme/mode-toggle-link.tsx
  • src/utils/utils.ts
  • src/lib/game-validation.ts
  • eslint.config.js
  • src/components/providers/confetti-provider.tsx
  • scripts/build-theme-runtime.ts
  • scripts/sync-pet-gallery.ts
  • src/utils/grid.ts
  • src/components/layout/header/mobile-nav.tsx
  • src/components/layout/header/nav-lava.tsx
  • src/components/providers/achievements-provider.tsx
  • src/components/layout/home/hero/profile-image.tsx
  • src/components/layout/home/game-overlay.tsx
  • src/components/layout/pet-gallery/_content.tsx
  • src/lib/score-validation.ts
  • src/lib/leaderboard.ts
  • src/utils/presence-script.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/posthog-integration.mdc)

In TypeScript, store feature flag names in an enum with members written in UPPERCASE_WITH_UNDERSCORE and use a consistent naming convention

**/*.{ts,tsx}: Use Zod for schema validation (client and server)
Model expected errors as return values in Server Actions
Never use unknown or any; always use existing types or define explicit, specific types

Files:

  • src/components/layout/home/background-snake-game.tsx
  • src/utils/stable-stringify.ts
  • src/utils/theme-runtime.ts
  • src/components/layout/about/pets/pet-card/card-back.tsx
  • src/utils/achievements.ts
  • src/utils/theme-script.ts
  • src/components/ui/theme-toggle.tsx
  • src/components/snake/name-input-modal.tsx
  • src/components/providers/theme-provider.tsx
  • src/app/api/local-image/[...path]/route.ts
  • src/components/layout/background/grid-lights.tsx
  • src/utils/arcade-utils.ts
  • src/app/api/scores/check/route.ts
  • src/components/providers/review-provider.tsx
  • src/hooks/use-leaderboard.ts
  • scripts/build-presence-runtime.ts
  • src/components/layout/about/aboutme/mode-toggle-link.tsx
  • src/utils/utils.ts
  • src/lib/game-validation.ts
  • src/components/providers/confetti-provider.tsx
  • scripts/build-theme-runtime.ts
  • scripts/sync-pet-gallery.ts
  • src/utils/grid.ts
  • src/components/layout/header/mobile-nav.tsx
  • src/components/layout/header/nav-lava.tsx
  • src/components/providers/achievements-provider.tsx
  • src/components/layout/home/hero/profile-image.tsx
  • src/components/layout/home/game-overlay.tsx
  • src/components/layout/pet-gallery/_content.tsx
  • src/lib/score-validation.ts
  • src/lib/leaderboard.ts
  • src/utils/presence-script.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use early returns to simplify control flow and reduce nesting
Use descriptive variable and function names; event handlers prefixed with "handle" (e.g., handleClick, handleKeyDown)
Refactor large files into smaller modules; organize components in folders when refactoring
Standard.js: 2-space indentation; single quotes; no semicolons (unless required); no unused variables; space after keywords; === only; spaced infix operators; commas followed by a space; same-line else; curly braces for multi-line if; camelCase for vars/functions; PascalCase for constructors/React components
Minimize global styles; prefer modular or scoped styles and utility classes
Prioritize error handling and edge cases; use guard clauses and early returns; place happy path last; avoid unnecessary else by using if-return
Naming Convention: enforce consistent naming for files, types, and functions per project rules

Files:

  • src/components/layout/home/background-snake-game.tsx
  • src/utils/stable-stringify.ts
  • src/utils/theme-runtime.ts
  • src/components/layout/about/pets/pet-card/card-back.tsx
  • src/utils/achievements.ts
  • src/utils/theme-script.ts
  • src/components/ui/theme-toggle.tsx
  • src/components/snake/name-input-modal.tsx
  • src/components/providers/theme-provider.tsx
  • src/app/api/local-image/[...path]/route.ts
  • src/components/layout/background/grid-lights.tsx
  • src/utils/arcade-utils.ts
  • src/app/api/scores/check/route.ts
  • src/components/providers/review-provider.tsx
  • src/hooks/use-leaderboard.ts
  • scripts/build-presence-runtime.ts
  • src/components/layout/about/aboutme/mode-toggle-link.tsx
  • src/utils/utils.ts
  • src/lib/game-validation.ts
  • eslint.config.js
  • src/components/providers/confetti-provider.tsx
  • scripts/build-theme-runtime.ts
  • scripts/sync-pet-gallery.ts
  • src/utils/grid.ts
  • src/components/layout/header/mobile-nav.tsx
  • src/components/layout/header/nav-lava.tsx
  • src/components/providers/achievements-provider.tsx
  • src/components/layout/home/hero/profile-image.tsx
  • src/components/layout/home/game-overlay.tsx
  • src/components/layout/pet-gallery/_content.tsx
  • src/lib/score-validation.ts
  • src/lib/leaderboard.ts
  • src/utils/presence-script.ts
**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.tsx: Use shadcn/ui (and Radix) as the foundation for UI components; install via shadcn CLI, do not create component files manually
Use shadcn/ui components for UI code

Files:

  • src/components/layout/home/background-snake-game.tsx
  • src/components/layout/about/pets/pet-card/card-back.tsx
  • src/components/ui/theme-toggle.tsx
  • src/components/snake/name-input-modal.tsx
  • src/components/providers/theme-provider.tsx
  • src/components/layout/background/grid-lights.tsx
  • src/components/providers/review-provider.tsx
  • src/components/layout/about/aboutme/mode-toggle-link.tsx
  • src/components/providers/confetti-provider.tsx
  • src/components/layout/header/mobile-nav.tsx
  • src/components/layout/header/nav-lava.tsx
  • src/components/providers/achievements-provider.tsx
  • src/components/layout/home/hero/profile-image.tsx
  • src/components/layout/home/game-overlay.tsx
  • src/components/layout/pet-gallery/_content.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{tsx,jsx}: Implement responsive design with Tailwind CSS; avoid custom style tags and prefer Tailwind utility classes
Add a11y attributes and keyboard handlers (aria-*, onKeyDown) to interactive elements
Use the function keyword for React component definitions (not arrow components for top-level components)
Follow React Hooks rules; use proper hooks (useState, useEffect, useContext, useReducer, useMemo, useCallback)
Extract reusable logic into custom hooks
Use React.memo for component memoization when appropriate
Use useCallback for functions passed as props to avoid unnecessary re-renders
Use useMemo for expensive computations
Avoid inline function definitions in render
Prefer composition over inheritance; use children and render props for flexibility
Use refs sparingly and mainly for DOM access
Prefer controlled components over uncontrolled for forms
Implement error boundaries to catch and handle UI errors
Use cleanup functions in useEffect to prevent memory leaks
Use short-circuit and ternaries for conditional rendering
Minimize 'use client' and client-side hooks; favor React Server Components in Next.js
Wrap client components in with a fallback
Use dynamic loading for non-critical components
Implement route-based code splitting in Next.js
Optimize images in UI: prefer WebP, include intrinsic size, and enable lazy loading when rendering
Use controlled components and client-side validation for forms; consider react-hook-form for complex forms
Provide user-friendly error messages and proper logging in UI paths
Use semantic HTML and proper ARIA attributes; ensure full keyboard navigation
Sanitize user inputs to prevent XSS; avoid dangerouslySetInnerHTML unless content is sanitized
Prefer named exports for components

Files:

  • src/components/layout/home/background-snake-game.tsx
  • src/components/layout/about/pets/pet-card/card-back.tsx
  • src/components/ui/theme-toggle.tsx
  • src/components/snake/name-input-modal.tsx
  • src/components/providers/theme-provider.tsx
  • src/components/layout/background/grid-lights.tsx
  • src/components/providers/review-provider.tsx
  • src/components/layout/about/aboutme/mode-toggle-link.tsx
  • src/components/providers/confetti-provider.tsx
  • src/components/layout/header/mobile-nav.tsx
  • src/components/layout/header/nav-lava.tsx
  • src/components/providers/achievements-provider.tsx
  • src/components/layout/home/hero/profile-image.tsx
  • src/components/layout/home/game-overlay.tsx
  • src/components/layout/pet-gallery/_content.tsx
**/*.{js,jsx,cjs,mjs}

📄 CodeRabbit inference engine (.cursor/rules/posthog-integration.mdc)

In JavaScript, store feature flag names as string values in a const object (simulating an enum) with members written in UPPERCASE_WITH_UNDERSCORE and use a consistent naming convention

Files:

  • eslint.config.js
🧠 Learnings (1)
📚 Learning: 2025-10-03T00:48:27.708Z
Learnt from: kiliantyler
PR: kiliantyler/kil.dev#103
File: src/components/providers/confetti-provider.tsx:214-314
Timestamp: 2025-10-03T00:48:27.708Z
Learning: In src/components/providers/confetti-provider.tsx, the `triggerConfettiChaos` function intentionally creates an infinite confetti loop with no exit condition. This is a deliberate easter egg feature triggered after 20 clicks, and the resource usage is expected behavior. The warning toasts inform users that the only way to stop it is by reloading the page.

Applied to files:

  • src/components/providers/confetti-provider.tsx
🧬 Code graph analysis (3)
src/components/providers/theme-provider.tsx (2)
src/lib/themes.ts (3)
  • Theme (57-57)
  • themes (5-53)
  • ThemeName (56-56)
src/utils/theme-runtime.ts (1)
  • getAvailableThemes (79-90)
src/components/providers/achievements-provider.tsx (1)
src/lib/achievements.ts (1)
  • ACHIEVEMENTS_COOKIE_NAME (4-4)
src/components/layout/home/hero/profile-image.tsx (1)
src/lib/achievements.ts (1)
  • AchievementId (136-136)
🪛 GitHub Actions: Lint
src/components/layout/header/mobile-nav.tsx

[error] 227-227: TS2345: Argument of type 'Element | null' is not assignable to parameter of type 'HTMLAnchorElement | null'.

🪛 GitHub Check: lint
src/components/ui/theme-toggle.tsx

[failure] 286-286:
Argument of type 'Element | null' is not assignable to parameter of type 'HTMLButtonElement | null'.

src/components/layout/header/mobile-nav.tsx

[failure] 227-227:
Argument of type 'Element | null' is not assignable to parameter of type 'HTMLAnchorElement | null'.

src/components/layout/header/nav-lava.tsx

[failure] 149-149:
Argument of type 'Element | null' is not assignable to parameter of type 'HTMLAnchorElement'.

🔇 Additional comments (52)
src/components/layout/home/background-snake-game.tsx (1)

315-317: Body class toggle looks good

The toggle with the explicit boolean keeps the class in sync with isPlaying, and the cleanup still guarantees removal on unmount. No issues spotted here.

src/utils/arcade-utils.ts (2)

24-45: LGTM! Robust cleanup implementation.

The cleanup function correctly uses removeEventListener and includes proper guards, error handling, and a fallback timeout mechanism to handle browser quirks where the 'ended' event may not fire reliably.


47-47: LGTM! Modern event listener API.

The migration from the onended property to addEventListener follows best practices and aligns with modern DOM API usage.

src/components/providers/review-provider.tsx (1)

13-13: LGTM! Clean simplification.

Removing the explicit undefined argument is correct—TypeScript infers undefined as the initial value for optional types when no argument is provided.

src/components/layout/background/grid-lights.tsx (3)

49-49: LGTM! Modern array access pattern.

The use of at(-1) is more concise than points[points.length - 1] and aligns with the ES2023 target. The non-null assertions are safe since the points array is guaranteed to have elements at these call sites.

Also applies to: 54-54


80-80: LGTM! Consistent modernization.

The same at(-1) pattern is applied consistently in the vertical path generation, matching the horizontal path changes. The non-null assertions are safe.

Also applies to: 85-85


105-108: LGTM! Consolidated push operation.

Combining two successive push() calls into a single call with multiple arguments is functionally equivalent and slightly more efficient. The keyframe order and animation behavior remain unchanged.

src/utils/utils.ts (1)

9-21: LGTM! Improved NaN validation.

The switch from isNaN to Number.isNaN is correct and follows modern JavaScript best practices. Since d.getTime() always returns a number type, Number.isNaN provides more precise type checking without unnecessary coercion.

src/app/api/scores/check/route.ts (1)

16-18: LGTM! Consistent numeric handling modernization.

The changes from parseInt to Number.parseInt and from isNaN to Number.isNaN align with modern JavaScript best practices and maintain consistency with the broader codebase pattern (e.g., src/utils/utils.ts). The validation logic remains correct and the changes are safe.

src/utils/stable-stringify.ts (2)

73-73: LGTM! Simplified undefined check.

The change from typeof item === 'undefined' to item === undefined is clearer and more idiomatic. Since item is from array iteration, it's always declared, making the direct comparison safe.


89-89: LGTM! Simplified undefined check.

The change from typeof val === 'undefined' to val === undefined is clearer and more idiomatic, consistent with the change on line 73.

tsconfig.json (1)

6-6: LGTM! TypeScript target appropriately updated to ES2023.

The bump from ES2022 to ES2023 correctly enables modern JavaScript features used throughout the PR, including Array.prototype.at, replaceAll, and top-level await in scripts.

Also applies to: 20-20

src/lib/game-validation.ts (1)

3-3: LGTM! Node.js built-in import modernized.

The node:crypto prefix is the recommended pattern for importing Node.js built-ins, improving clarity and preventing potential conflicts with userland modules.

src/components/layout/about/aboutme/mode-toggle-link.tsx (1)

69-69: LGTM! Modern DOM API usage.

The append() method is the modern API for inserting nodes, offering more flexibility than appendChild() (e.g., accepting multiple arguments and DOMStrings).

src/app/api/local-image/[...path]/route.ts (1)

2-3: LGTM! Node.js built-in imports modernized.

The node: prefix for built-ins improves clarity and prevents conflicts with userland modules, aligning with current Node.js best practices.

src/utils/theme-runtime.ts (1)

11-11: LGTM! Modernized data attribute check.

Using Object.hasOwn(root.dataset, 'hasThemeTapdance') is the modern, idiomatic approach for checking dataset properties. The dataset API automatically converts kebab-case attribute names to camelCase property names.

src/utils/grid.ts (1)

41-42: LGTM! More robust window existence check.

The strict === undefined comparison is more precise than a truthy check, correctly handling edge cases where globalThis.window might be null or other falsy but defined values.

src/components/layout/home/game-overlay.tsx (1)

29-29: LGTM! Simplified keyboard handler logic.

The combined conditional e.key === ' ' && (gameOver || !isPlaying) is more concise and readable than a multi-step check, while preserving the exact same behavior.

scripts/build-theme-runtime.ts (2)

5-14: LGTM! Clean path module modernization.

The changes correctly modernize the path handling:

  • Default import of path module is idiomatic for Node.js
  • Explicit path.dirname(__filename) and path.resolve(...) calls improve clarity
  • Consistent with the broader codebase modernization pattern

43-47: LGTM! Top-level await is appropriate.

Using top-level await with the main function is a modern ESM pattern that simplifies error handling at the script entry point.

src/lib/score-validation.ts (1)

37-43: LGTM! Explicit global replacement intent.

The change from replace(/[^A-Z]/g, '') to replaceAll(/[^A-Z]/g, '') is functionally equivalent but more explicit about performing a global replacement. This aligns with the codebase modernization pattern seen across the PR.

src/components/ui/theme-toggle.tsx (2)

118-118: LGTM! Modern DOM API usage.

The change from appendChild(style) to append(style) uses the modern DOM API, which is more concise and aligns with the codebase modernization pattern seen in other files (e.g., mobile-nav.tsx, mode-toggle-link.tsx).


289-289: LGTM! More explicit -1 check.

The change from currentIndex < 0 to currentIndex === -1 is more explicit and aligns with the standard convention for checking "not found" results from indexOf.

src/components/layout/pet-gallery/_content.tsx (3)

5-6: LGTM! Node built-in import modernization.

The change to node:fs and node:path imports follows Node.js best practices and aligns with the modernization pattern throughout the codebase (also seen in build-theme-runtime.ts, build-presence-runtime.ts).


42-42: LGTM! Non-mutating array sort.

Using toSorted instead of sort creates a new sorted array without mutating the original, which is a better functional programming practice and aligns with modern JavaScript idioms.


48-50: LGTM! Explicit global replacement with replaceAll.

The changes from replace to replaceAll with regex patterns make the global replacement intent explicit and align with the codebase modernization pattern.

src/components/layout/about/pets/pet-card/card-back.tsx (1)

41-41: LGTM! Explicit null handling.

The explicit ageYears === null check improves code clarity and makes the null-handling behavior more obvious. This aligns with the stricter type-checking patterns seen throughout the PR.

eslint.config.js (3)

3-3: LGTM! Unicorn plugin integration.

Adding eslint-plugin-unicorn enhances linting capabilities with modern JavaScript best practices, as evidenced by the codebase modernizations throughout this PR.


28-28: LGTM! Unopinionated preset choice.

Using the unopinionated preset provides a good balance of useful rules without being overly prescriptive, which is appropriate for an established codebase.


43-44: LGTM! Rule disabling with tracked TODO.

Disabling unicorn/no-nested-ternary is acknowledged with a TODO comment noting the Prettier conflict. This is good practice for tracking known technical debt.

The unicorn/numeric-separators-style disable is also reasonable as a style preference.

package.json (3)

44-44: LGTM! Correct js-cookie version.

The js-cookie version 3.0.5 is the latest published runtime release, as confirmed by the official npm package. This aligns with the cookie handling migration seen in theme-provider.tsx and achievements-provider.tsx.

Based on learnings.


67-68: LGTM! Type dependencies correctly organized.

Moving @types/canvas-confetti to devDependencies is correct since types are only needed at build time. The @types/js-cookie version 3.0.6 is from DefinitelyTyped and provides the latest type definitions for the js-cookie 3.x API.

Based on learnings.


72-72: LGTM! ESLint plugin addition.

The eslint-plugin-unicorn devDependency aligns with the ESLint configuration changes in eslint.config.js and enables the modern JavaScript linting rules applied throughout this PR.

scripts/sync-pet-gallery.ts (2)

95-95: LGTM!

The use of replaceAll with two passes (dashes/underscores → space, then collapse multiple spaces) is clear and correct.


193-198: LGTM!

The top-level await pattern with error handling and explicit exit code is consistent with other build scripts in the repository.

scripts/build-presence-runtime.ts (2)

5-14: LGTM!

The migration to node:path default import and explicit path.dirname/path.resolve usage aligns with the broader project refactor to use Node built-in modules consistently.


43-47: LGTM!

The top-level await pattern with error handling is correct and consistent with other build scripts.

src/lib/leaderboard.ts (1)

25-25: LGTM!

The updates to use === -1 for not-found checks and .at(-1) for last-element access improve code clarity and align with modern JavaScript conventions.

Also applies to: 49-49, 133-133

src/utils/theme-script.ts (1)

34-34: LGTM!

The updates to use modern DOM APIs (append, remove), Object.hasOwn for dataset checks, and optional chaining for matchMedia improve code safety and align with modern JavaScript conventions.

Also applies to: 92-93, 103-103, 122-122, 151-151

src/components/providers/theme-provider.tsx (6)

10-10: LGTM!

The migration to js-cookie for cookie handling is correct. The usage of Cookies.set with options (path, maxAge, samesite, secure) aligns with the v3.x API, which requires options to be passed per-call rather than via global defaults.

Based on learnings

Also applies to: 111-122, 140-145


56-56: LGTM!

Refactoring VALID_THEMES to a Set and using has for lookups is more efficient than Array.includes and aligns with best practices for fast membership checks.

Also applies to: 65-65, 75-75, 87-87, 98-98


37-54: LGTM!

The refactor of coerceToValidTheme with a default parameter and simplified validation logic is correct. The use of Object.hasOwn for dataset checks is safer than attribute-based checks.


213-213: LGTM!

The bare returns in server-side branches preserve the same behavior while simplifying the code.

Also applies to: 223-223, 227-227, 234-234


346-346: LGTM!

The use of Object.hasOwn for dataset checks is safer and more direct than attribute-based checks.


374-379: LGTM!

The simplified ternary for computedInitial preserves the same behavior while improving readability.

src/utils/achievements.ts (1)

65-67: LGTM!

The use of replaceAll to escape <, \u2028, and \u2029 ensures the JSON is safe when embedded in HTML and prevents XSS vulnerabilities.

src/components/providers/confetti-provider.tsx (1)

26-31: LGTM!

The updated catch block with an empty comment makes the intent clearer while preserving the same behavior of swallowing promise rejections.

src/components/layout/header/mobile-nav.tsx (2)

85-85: LGTM!

The use of document.head.append aligns with modern DOM APIs and is consistent with other updates in the repository.


230-230: LGTM!

The currentIndex === -1 check correctly handles the not-found case and aligns with the updated patterns in the codebase.

src/components/providers/achievements-provider.tsx (1)

179-244: LGTM! Modern dataset API usage.

The migration from setAttribute/removeAttribute to dataset property assignments and the delete operator is a clean, modern approach that aligns with current DOM API best practices. This improves SSR hydration consistency and simplifies the code.

src/components/layout/header/nav-lava.tsx (2)

41-42: LGTM! Modern dataset API usage.

The refactor from getAttribute to dataset.* is idiomatic and aligns with modern DOM best practices. The string equality checks (=== 'true') correctly handle the fact that dataset values are always strings.


91-99: LGTM! Improved animation initialization logic.

The conditional logic ensures the indicator snaps into place without animation on first render, then enables smooth transitions for subsequent updates. The use of requestAnimationFrame to defer the animation flag is appropriate and prevents visual flicker.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a2fdb20 and f8e85ca.

📒 Files selected for processing (3)
  • src/components/layout/header/mobile-nav.tsx (2 hunks)
  • src/components/layout/header/nav-lava.tsx (3 hunks)
  • src/components/ui/theme-toggle.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/layout/header/nav-lava.tsx
  • src/components/ui/theme-toggle.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,js,jsx,cjs,mjs}

📄 CodeRabbit inference engine (.cursor/rules/posthog-integration.mdc)

**/*.{ts,tsx,js,jsx,cjs,mjs}: Never hardcode or hallucinate the PostHog API key; always read it from the value populated in the .env file
Create new feature flag names that are clear and descriptive
Gate flag-dependent code on a check that verifies the flag’s values are valid and expected
If a custom property for a person or event is referenced in two or more files or in two or more callsites within the same file, centralize the property name in an enum (TS) or const object (JS)

Files:

  • src/components/layout/header/mobile-nav.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/posthog-integration.mdc)

In TypeScript, store feature flag names in an enum with members written in UPPERCASE_WITH_UNDERSCORE and use a consistent naming convention

**/*.{ts,tsx}: Use Zod for schema validation (client and server)
Model expected errors as return values in Server Actions
Never use unknown or any; always use existing types or define explicit, specific types

Files:

  • src/components/layout/header/mobile-nav.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Use early returns to simplify control flow and reduce nesting
Use descriptive variable and function names; event handlers prefixed with "handle" (e.g., handleClick, handleKeyDown)
Refactor large files into smaller modules; organize components in folders when refactoring
Standard.js: 2-space indentation; single quotes; no semicolons (unless required); no unused variables; space after keywords; === only; spaced infix operators; commas followed by a space; same-line else; curly braces for multi-line if; camelCase for vars/functions; PascalCase for constructors/React components
Minimize global styles; prefer modular or scoped styles and utility classes
Prioritize error handling and edge cases; use guard clauses and early returns; place happy path last; avoid unnecessary else by using if-return
Naming Convention: enforce consistent naming for files, types, and functions per project rules

Files:

  • src/components/layout/header/mobile-nav.tsx
**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.tsx: Use shadcn/ui (and Radix) as the foundation for UI components; install via shadcn CLI, do not create component files manually
Use shadcn/ui components for UI code

Files:

  • src/components/layout/header/mobile-nav.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{tsx,jsx}: Implement responsive design with Tailwind CSS; avoid custom style tags and prefer Tailwind utility classes
Add a11y attributes and keyboard handlers (aria-*, onKeyDown) to interactive elements
Use the function keyword for React component definitions (not arrow components for top-level components)
Follow React Hooks rules; use proper hooks (useState, useEffect, useContext, useReducer, useMemo, useCallback)
Extract reusable logic into custom hooks
Use React.memo for component memoization when appropriate
Use useCallback for functions passed as props to avoid unnecessary re-renders
Use useMemo for expensive computations
Avoid inline function definitions in render
Prefer composition over inheritance; use children and render props for flexibility
Use refs sparingly and mainly for DOM access
Prefer controlled components over uncontrolled for forms
Implement error boundaries to catch and handle UI errors
Use cleanup functions in useEffect to prevent memory leaks
Use short-circuit and ternaries for conditional rendering
Minimize 'use client' and client-side hooks; favor React Server Components in Next.js
Wrap client components in with a fallback
Use dynamic loading for non-critical components
Implement route-based code splitting in Next.js
Optimize images in UI: prefer WebP, include intrinsic size, and enable lazy loading when rendering
Use controlled components and client-side validation for forms; consider react-hook-form for complex forms
Provide user-friendly error messages and proper logging in UI paths
Use semantic HTML and proper ARIA attributes; ensure full keyboard navigation
Sanitize user inputs to prevent XSS; avoid dangerouslySetInnerHTML unless content is sanitized
Prefer named exports for components

Files:

  • src/components/layout/header/mobile-nav.tsx
🔇 Additional comments (2)
src/components/layout/header/mobile-nav.tsx (2)

85-85: LGTM!

The modernization from appendChild to append aligns with contemporary DOM APIs and matches the PR's goal of code modernization.


230-230: Good refinement.

Using === -1 is more explicit and idiomatic for indexOf results than < 0, improving readability.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Oct 3, 2025

@kiliantyler kiliantyler merged commit 380c88e into main Oct 3, 2025
5 of 6 checks passed
@kiliantyler kiliantyler deleted the add-eslint-rules branch October 3, 2025 16:58
@coderabbitai coderabbitai bot mentioned this pull request Nov 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant