fix(web): Run Biome checks and apply fixes

This commit is contained in:
Bjorn Lammers 2025-04-24 16:07:29 +02:00
parent 34fef44222
commit 40482771fa
8 changed files with 123 additions and 82 deletions

View File

@ -22,6 +22,9 @@
"recommended": true, "recommended": true,
"suspicious": { "suspicious": {
"noArrayIndexKey": "off" "noArrayIndexKey": "off"
},
"security": {
"noDangerouslySetInnerHtml": "off"
} }
} }
}, },

View File

@ -1,13 +1,8 @@
import { readFile } from "node:fs/promises" import { readFile } from "node:fs/promises"
import { join } from "node:path" import { join } from "node:path"
import { SITE_NAME, SITE_TAGLINE, WEB_URL, getIconDescription } from "@/constants"
import { getAllIcons } from "@/lib/api" import { getAllIcons } from "@/lib/api"
import { ImageResponse } from "next/og" import { ImageResponse } from "next/og"
import {
SITE_NAME,
SITE_TAGLINE,
getIconDescription,
WEB_URL
} from "@/constants"
export const dynamic = "force-static" export const dynamic = "force-static"

View File

@ -1,10 +1,20 @@
import { IconDetails } from "@/components/icon-details" import { IconDetails } from "@/components/icon-details"
import { StructuredData } from "@/components/structured-data" import { StructuredData } from "@/components/structured-data"
import { BASE_URL, GITHUB_URL, ICON_DETAIL_KEYWORDS, SITE_NAME, SITE_TAGLINE, TITLE_SEPARATOR, WEB_URL, getIconDescription, getIconSchema } from "@/constants" import {
BASE_URL,
GITHUB_URL,
ICON_DETAIL_KEYWORDS,
SITE_NAME,
SITE_TAGLINE,
TITLE_SEPARATOR,
WEB_URL,
getIconDescription,
getIconSchema,
} from "@/constants"
import { getAllIcons, getAuthorData } from "@/lib/api" import { getAllIcons, getAuthorData } from "@/lib/api"
import type { Metadata, ResolvingMetadata } from "next" import type { Metadata, ResolvingMetadata } from "next"
import Script from "next/script"
import { notFound } from "next/navigation" import { notFound } from "next/navigation"
import Script from "next/script"
export const dynamicParams = false export const dynamicParams = false
@ -109,7 +119,7 @@ export default async function IconPage({ params }: { params: { icon: string } })
authorName, authorName,
authorData.html_url, authorData.html_url,
updateDate.toISOString(), updateDate.toISOString(),
Object.keys(iconsData).length Object.keys(iconsData).length,
) )
return ( return (

View File

@ -1,7 +1,19 @@
import { BASE_URL, BROWSE_KEYWORDS, DEFAULT_OG_IMAGE, GITHUB_URL, ORGANIZATION_NAME, ORGANIZATION_SCHEMA, SITE_NAME, SITE_TAGLINE, TITLE_SEPARATOR, WEB_URL, getBrowseDescription } from "@/constants" import { StructuredData } from "@/components/structured-data"
import {
BASE_URL,
BROWSE_KEYWORDS,
DEFAULT_OG_IMAGE,
GITHUB_URL,
ORGANIZATION_NAME,
ORGANIZATION_SCHEMA,
SITE_NAME,
SITE_TAGLINE,
TITLE_SEPARATOR,
WEB_URL,
getBrowseDescription,
} from "@/constants"
import { getIconsArray } from "@/lib/api" import { getIconsArray } from "@/lib/api"
import type { Metadata } from "next" import type { Metadata } from "next"
import { StructuredData } from "@/components/structured-data"
import { IconSearch } from "./components/icon-search" import { IconSearch } from "./components/icon-search"
export async function generateMetadata(): Promise<Metadata> { export async function generateMetadata(): Promise<Metadata> {
@ -42,15 +54,15 @@ export default async function IconsPage() {
const gallerySchema = { const gallerySchema = {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "ImageGallery", "@type": "ImageGallery",
"name": `${SITE_NAME} - Browse ${icons.length} Icons - ${SITE_TAGLINE}`, name: `${SITE_NAME} - Browse ${icons.length} Icons - ${SITE_TAGLINE}`,
"description": getBrowseDescription(icons.length), description: getBrowseDescription(icons.length),
"url": `${WEB_URL}/icons`, url: `${WEB_URL}/icons`,
"numberOfItems": icons.length, numberOfItems: icons.length,
"creator": { creator: {
"@type": "Organization", "@type": "Organization",
"name": ORGANIZATION_NAME, name: ORGANIZATION_NAME,
"url": GITHUB_URL url: GITHUB_URL,
} },
} }
return ( return (
@ -62,7 +74,9 @@ export default async function IconsPage() {
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4"> <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div> <div>
<h1 className="text-3xl font-bold">Icons</h1> <h1 className="text-3xl font-bold">Icons</h1>
<p className="text-muted-foreground">Search our collection of {icons.length} icons - {SITE_TAGLINE}.</p> <p className="text-muted-foreground">
Search our collection of {icons.length} icons - {SITE_TAGLINE}.
</p>
</div> </div>
</div> </div>

View File

@ -8,7 +8,20 @@ import type { Metadata, Viewport } from "next"
import { Inter } from "next/font/google" import { Inter } from "next/font/google"
import { Toaster } from "sonner" import { Toaster } from "sonner"
import "./globals.css" import "./globals.css"
import { DEFAULT_KEYWORDS, DEFAULT_OG_IMAGE, GITHUB_URL, ORGANIZATION_NAME, ORGANIZATION_SCHEMA, SITE_NAME, SITE_TAGLINE, WEB_URL, getDescription, getWebsiteSchema, websiteFullTitle, websiteTitle } from "@/constants" import {
DEFAULT_KEYWORDS,
DEFAULT_OG_IMAGE,
GITHUB_URL,
ORGANIZATION_NAME,
ORGANIZATION_SCHEMA,
SITE_NAME,
SITE_TAGLINE,
WEB_URL,
getDescription,
getWebsiteSchema,
websiteFullTitle,
websiteTitle,
} from "@/constants"
import { ThemeProvider } from "./theme-provider" import { ThemeProvider } from "./theme-provider"
const inter = Inter({ const inter = Inter({
@ -102,10 +115,7 @@ export default async function RootLayout({ children }: Readonly<{ children: Reac
<html lang="en" suppressHydrationWarning> <html lang="en" suppressHydrationWarning>
<body className={`${inter.variable} antialiased bg-background flex flex-col min-h-screen`}> <body className={`${inter.variable} antialiased bg-background flex flex-col min-h-screen`}>
<PostHogProvider> <PostHogProvider>
<WebsiteStructuredData <WebsiteStructuredData websiteSchema={websiteSchema} organizationSchema={ORGANIZATION_SCHEMA} />
websiteSchema={websiteSchema}
organizationSchema={ORGANIZATION_SCHEMA}
/>
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange> <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
<HeaderWrapper /> <HeaderWrapper />
<main className="flex-grow">{children}</main> <main className="flex-grow">{children}</main>

View File

@ -1,6 +1,20 @@
import { HeroSection } from "@/components/hero" import { HeroSection } from "@/components/hero"
import { RecentlyAddedIcons } from "@/components/recently-added-icons" import { RecentlyAddedIcons } from "@/components/recently-added-icons"
import { BASE_URL, DEFAULT_KEYWORDS, DEFAULT_OG_IMAGE, GITHUB_URL, ORGANIZATION_NAME, ORGANIZATION_SCHEMA, SITE_NAME, SITE_TAGLINE, WEB_URL, REPO_NAME, getHomeDescription, websiteFullTitle, websiteTitle } from "@/constants" import {
BASE_URL,
DEFAULT_KEYWORDS,
DEFAULT_OG_IMAGE,
GITHUB_URL,
ORGANIZATION_NAME,
ORGANIZATION_SCHEMA,
REPO_NAME,
SITE_NAME,
SITE_TAGLINE,
WEB_URL,
getHomeDescription,
websiteFullTitle,
websiteTitle,
} from "@/constants"
import { getRecentlyAddedIcons, getTotalIcons } from "@/lib/api" import { getRecentlyAddedIcons, getTotalIcons } from "@/lib/api"
import type { Metadata } from "next" import type { Metadata } from "next"

View File

@ -1,31 +1,22 @@
type StructuredDataProps = { type StructuredDataProps = {
data: any data: Record<string, unknown>
id?: string id?: string
} }
export const StructuredData = ({ data, id }: StructuredDataProps) => { export const StructuredData = ({ data, id }: StructuredDataProps) => {
return ( return <script id={id} type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }} />
<script
id={id}
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
/>
)
} }
type WebsiteStructuredDataProps = { type WebsiteStructuredDataProps = {
websiteSchema: any websiteSchema: Record<string, unknown>
organizationSchema: any organizationSchema: Record<string, unknown>
} }
export const WebsiteStructuredData = ({ export const WebsiteStructuredData = ({ websiteSchema, organizationSchema }: WebsiteStructuredDataProps) => {
websiteSchema, return (
organizationSchema <>
}: WebsiteStructuredDataProps) => { <StructuredData data={websiteSchema} id="website-schema" />
return ( <StructuredData data={organizationSchema} id="organization-schema" />
<> </>
<StructuredData data={websiteSchema} id="website-schema" /> )
<StructuredData data={organizationSchema} id="organization-schema" /> }
</>
)
}

View File

@ -34,7 +34,7 @@ export const DEFAULT_KEYWORDS = [
"free icons", "free icons",
"SVG icons", "SVG icons",
"web dashboard", "web dashboard",
"app directory" "app directory",
] ]
export const BROWSE_KEYWORDS = [ export const BROWSE_KEYWORDS = [
@ -44,7 +44,7 @@ export const BROWSE_KEYWORDS = [
"minimal icons", "minimal icons",
"dashboard design", "dashboard design",
"UI icons", "UI icons",
...DEFAULT_KEYWORDS ...DEFAULT_KEYWORDS,
] ]
// Add format-specific keywords // Add format-specific keywords
@ -56,63 +56,67 @@ export const ICON_DETAIL_KEYWORDS = (iconName: string): string[] => [
`${iconName} webp icon`, // e.g., "Homarr webp icon" `${iconName} webp icon`, // e.g., "Homarr webp icon"
`${iconName} download`, // e.g., "Homarr download" `${iconName} download`, // e.g., "Homarr download"
`${iconName} dashboard icon`, // e.g., "Homarr dashboard icon" `${iconName} dashboard icon`, // e.g., "Homarr dashboard icon"
...DEFAULT_KEYWORDS ...DEFAULT_KEYWORDS,
] ]
// Core structured data for the website (JSON-LD) // Core structured data for the website (JSON-LD)
export const getWebsiteSchema = (totalIcons: number) => ({ export const getWebsiteSchema = (totalIcons: number) => ({
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "WebSite", "@type": "WebSite",
"name": SITE_NAME, name: SITE_NAME,
"url": WEB_URL, url: WEB_URL,
"description": getDescription(totalIcons), description: getDescription(totalIcons),
"potentialAction": { potentialAction: {
"@type": "SearchAction", "@type": "SearchAction",
"target": { target: {
"@type": "EntryPoint", "@type": "EntryPoint",
"urlTemplate": `${WEB_URL}/icons?q={search_term_string}` urlTemplate: `${WEB_URL}/icons?q={search_term_string}`,
}, },
"query-input": "required name=search_term_string" "query-input": "required name=search_term_string",
}, },
"slogan": SITE_TAGLINE slogan: SITE_TAGLINE,
}) })
// Organization schema // Organization schema
export const ORGANIZATION_SCHEMA = { export const ORGANIZATION_SCHEMA = {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "Organization", "@type": "Organization",
"name": ORGANIZATION_NAME, name: ORGANIZATION_NAME,
"url": `https://github.com/${REPO_NAME}`, url: `https://github.com/${REPO_NAME}`,
"logo": `${WEB_URL}/og-image.png`, logo: `${WEB_URL}/og-image.png`,
"sameAs": [ sameAs: [`https://github.com/${REPO_NAME}`, "https://homarr.dev"],
`https://github.com/${REPO_NAME}`, slogan: SITE_TAGLINE,
"https://homarr.dev"
],
"slogan": SITE_TAGLINE
} }
// Social media // Social media
export const GITHUB_URL = `https://github.com/${REPO_NAME}` export const GITHUB_URL = `https://github.com/${REPO_NAME}`
// Image schemas // Image schemas
export const getIconSchema = (iconName: string, iconId: string, authorName: string, authorUrl: string, updateDate: string, totalIcons: number) => ({ export const getIconSchema = (
iconName: string,
iconId: string,
authorName: string,
authorUrl: string,
updateDate: string,
totalIcons: number,
) => ({
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "ImageObject", "@type": "ImageObject",
"name": `${iconName} Icon`, name: `${iconName} Icon`,
"description": getIconDescription(iconName, totalIcons), description: getIconDescription(iconName, totalIcons),
"contentUrl": `${BASE_URL}/png/${iconId}.png`, contentUrl: `${BASE_URL}/png/${iconId}.png`,
"thumbnailUrl": `${BASE_URL}/png/${iconId}.png`, thumbnailUrl: `${BASE_URL}/png/${iconId}.png`,
"uploadDate": updateDate, uploadDate: updateDate,
"author": { author: {
"@type": "Person", "@type": "Person",
"name": authorName, name: authorName,
"url": authorUrl url: authorUrl,
}, },
"encodingFormat": ["image/png", "image/svg+xml", "image/webp"], encodingFormat: ["image/png", "image/svg+xml", "image/webp"],
"contentSize": "Variable", contentSize: "Variable",
"representativeOfPage": true, representativeOfPage: true,
"creditText": `Icon contributed by ${authorName} to the ${SITE_NAME} collection by ${ORGANIZATION_NAME}`, creditText: `Icon contributed by ${authorName} to the ${SITE_NAME} collection by ${ORGANIZATION_NAME}`,
"embedUrl": `${WEB_URL}/icons/${iconId}` embedUrl: `${WEB_URL}/icons/${iconId}`,
}) })
// OpenGraph defaults // OpenGraph defaults
@ -121,5 +125,5 @@ export const DEFAULT_OG_IMAGE = {
width: 1200, width: 1200,
height: 630, height: 630,
alt: `${SITE_NAME} - ${SITE_TAGLINE}`, alt: `${SITE_NAME} - ${SITE_TAGLINE}`,
type: "image/png" type: "image/png",
} }