diff --git a/astro.config.mjs b/astro.config.mjs index 6922b3be41..9f66fecf2b 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -16,9 +16,13 @@ const skipImageOptimization = process.env.SKIP_IMAGE_OPTIMIZATION === "true"; // https://astro.build/config export default defineConfig({ output: isSSR ? "server" : "static", - adapter: isSSR - ? (await import("@astrojs/node")).default({ mode: "standalone" }) - : undefined, + ...(isSSR + ? { + adapter: (await import("@astrojs/node")).default({ + mode: "standalone", + }), + } + : {}), site: `https://${SITE_ROOTDOMAIN}`, trailingSlash: "always", markdown: { diff --git a/src/components/pages/blog/BlogCard.astro b/src/components/pages/blog/BlogCard.astro index d9e3841255..e589672e48 100644 --- a/src/components/pages/blog/BlogCard.astro +++ b/src/components/pages/blog/BlogCard.astro @@ -12,7 +12,7 @@ interface Props { tags: string[]; title: string; summary: string; - image?: ImageMetadata; + image?: ImageMetadata | undefined; } const { author, tags, title, summary, image, id } = Astro.props; diff --git a/src/components/pages/blog/BlogHeader.astro b/src/components/pages/blog/BlogHeader.astro index d35ed45851..6e34503a9f 100644 --- a/src/components/pages/blog/BlogHeader.astro +++ b/src/components/pages/blog/BlogHeader.astro @@ -7,7 +7,7 @@ interface Props { title: string; id: string; author: string; - tags?: string[]; + tags?: string[] | undefined; } const { title, id, author, tags = [] } = Astro.props; diff --git a/src/components/pages/community/sponsorships/SponsorCard.astro b/src/components/pages/community/sponsorships/SponsorCard.astro index a41e481f9a..a5b902086d 100644 --- a/src/components/pages/community/sponsorships/SponsorCard.astro +++ b/src/components/pages/community/sponsorships/SponsorCard.astro @@ -6,9 +6,9 @@ import type { ImageMetadata } from "astro"; interface Props { name: string; description: string; - href?: string; - logo?: ImageMetadata; - monochrome?: boolean; + href?: string | undefined; + logo?: ImageMetadata | undefined; + monochrome?: boolean | undefined; } const { name, description, href, logo, monochrome = false } = Astro.props; diff --git a/src/components/pages/downloads/CommunityCard.astro b/src/components/pages/downloads/CommunityCard.astro index c6e0fa157d..aab8ac4455 100644 --- a/src/components/pages/downloads/CommunityCard.astro +++ b/src/components/pages/downloads/CommunityCard.astro @@ -20,7 +20,7 @@ interface Props { link: string; linkText?: string; translateTag?: (key: string) => string; - id?: string; + id?: string | undefined; } const { @@ -42,7 +42,9 @@ const ALIAS: Record = { osx: "macos", }; -const ICONS: Record = { +type PlatformKey = "windows" | "macos" | "linux" | "android" | "ios"; + +const ICONS: Record = { windows: icons.mask("brand/windows"), macos: icons.mask("brand/apple"), linux: icons.mask("brand/linux"), @@ -58,17 +60,23 @@ const LABEL: Record = { ios: t("downloads:platforms.ios") || "iOS", }; +const isPlatformKey = (key: string): key is PlatformKey => key in ICONS; + const VALID = { desktop: new Set(["windows", "macos", "linux"]), mobile: new Set(["android", "ios"]), }; -function normalizeKeys(arr: string[] | undefined, set: Set) { +function normalizeKeys( + arr: string[] | undefined, + set: Set, +): PlatformKey[] { return Array.from( new Set( // remove duplicates post-normalization (arr ?? []) .map((s) => ALIAS[norm(s)] ?? norm(s)) - .filter((k) => set.has(k)), + .filter((k) => set.has(k)) + .filter(isPlatformKey), ), ); } diff --git a/src/components/pages/downloads/CoreCard.astro b/src/components/pages/downloads/CoreCard.astro index 9880f7d736..3563cf5356 100644 --- a/src/components/pages/downloads/CoreCard.astro +++ b/src/components/pages/downloads/CoreCard.astro @@ -53,7 +53,7 @@ const { } = Astro.props; const { t, safeMarkdown } = Astro.locals; -const PLATFORM_ICONS: Record = { +const PLATFORM_ICONS: Record = { windows: icons.mask("brand/windows"), macos: icons.mask("brand/apple"), linux: icons.mask("brand/linux"), diff --git a/src/components/pages/resources/research-lab/PaperCard.astro b/src/components/pages/resources/research-lab/PaperCard.astro index 5262b2d084..de8c55876c 100644 --- a/src/components/pages/resources/research-lab/PaperCard.astro +++ b/src/components/pages/resources/research-lab/PaperCard.astro @@ -3,11 +3,11 @@ import Card from "@/components/ui/Card.astro"; import IconLink from "@/components/ui/IconLink.astro"; interface Props { - code?: string; + code?: string | undefined; title: string; abstract: string; url: string; - note?: string; + note?: string | undefined; } const { code, title, abstract, url, note } = Astro.props; diff --git a/src/components/ui/Button.astro b/src/components/ui/Button.astro index bfc9a38009..ee18c3592d 100644 --- a/src/components/ui/Button.astro +++ b/src/components/ui/Button.astro @@ -7,7 +7,7 @@ type Variant = "primary" | "secondary" | "outline" | "danger"; interface Props extends HTMLAttributes { variant?: Variant; class?: string; - href?: string; + href?: string | undefined; target?: string; rel?: string; as?: As; diff --git a/src/components/ui/ExternalResourceButton.astro b/src/components/ui/ExternalResourceButton.astro index bc15ba1253..bb9a7beab0 100644 --- a/src/components/ui/ExternalResourceButton.astro +++ b/src/components/ui/ExternalResourceButton.astro @@ -9,9 +9,9 @@ import { icons } from "@/utils/icons"; export interface Props extends HTMLAttributes<"div"> { href: string; title: string; - subtitle?: string; - logo?: ImageMetadata; - onionHref?: string; + subtitle?: string | undefined; + logo?: ImageMetadata | undefined; + onionHref?: string | undefined; variant?: "default" | "mini"; border?: boolean; } diff --git a/src/components/ui/Paginator.astro b/src/components/ui/Paginator.astro index 54f33c6e4b..6c7f1de1ab 100644 --- a/src/components/ui/Paginator.astro +++ b/src/components/ui/Paginator.astro @@ -34,12 +34,11 @@ if (last <= 5) { ); const sorted = [...visible].sort((a, b) => a - b); - for (let i = 0; i < sorted.length; i++) { - const n = sorted[i]; - const prev = sorted[i - 1]; + for (const [i, n] of sorted.entries()) { + const prev = i > 0 ? sorted[i - 1] : undefined; // Add ellipsis or fill single gap - if (i > 0) { + if (prev !== undefined) { const gap = n - prev; if (gap === 2) { items.push({ type: "page", number: prev + 1 }); diff --git a/src/components/ui/accordion/AccordionItem.astro b/src/components/ui/accordion/AccordionItem.astro index d7190fa954..1e46392acb 100644 --- a/src/components/ui/accordion/AccordionItem.astro +++ b/src/components/ui/accordion/AccordionItem.astro @@ -7,7 +7,7 @@ export interface Props { title: string; content?: string; variant?: "default" | "card"; - name?: string; + name?: string | undefined; open?: boolean; } diff --git a/src/components/ui/dropdown/Dropdown.astro b/src/components/ui/dropdown/Dropdown.astro index f3da43387b..5978b468ee 100644 --- a/src/components/ui/dropdown/Dropdown.astro +++ b/src/components/ui/dropdown/Dropdown.astro @@ -5,7 +5,7 @@ import { icons } from "@/utils/icons"; interface Props { label?: string; - name?: string; + name?: string | undefined; } const { label, name } = Astro.props; --- diff --git a/src/components/ui/header/NavItem.astro b/src/components/ui/header/NavItem.astro index 08dca812cb..2e08162cda 100644 --- a/src/components/ui/header/NavItem.astro +++ b/src/components/ui/header/NavItem.astro @@ -11,7 +11,7 @@ export interface DropdownItem { href: string; desc?: string; icon: ImageMetadata; - iconDark?: ImageMetadata; + iconDark: ImageMetadata; } export interface Props { diff --git a/src/components/ui/icons/MaskIcon.astro b/src/components/ui/icons/MaskIcon.astro index 05bd7f8838..9f2031c78b 100644 --- a/src/components/ui/icons/MaskIcon.astro +++ b/src/components/ui/icons/MaskIcon.astro @@ -6,7 +6,7 @@ export interface Props { src: ImageMetadata; size?: number | string; rtl?: boolean; - color?: string; + color?: string | undefined; spacing?: string; } diff --git a/src/data/navigation.ts b/src/data/navigation.ts index 7b9c58acf4..9ec023dd3a 100644 --- a/src/data/navigation.ts +++ b/src/data/navigation.ts @@ -5,7 +5,7 @@ interface DropdownItem { key: string; href: string; icon: ImageMetadata; - iconDark?: ImageMetadata; + iconDark: ImageMetadata; } interface NavDropdownConfig { diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index f54c1be144..edb4863d38 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -63,14 +63,8 @@ const fullTitle = title.includes("|") ), siteName: "Monero", }, - image: { - alt: openGraph?.image?.alt ?? undefined, - }, - article: { - publishedTime: openGraph?.article?.publishedTime ?? undefined, - authors: openGraph?.article?.authors ?? undefined, - tags: openGraph?.article?.tags ?? undefined, - }, + image: openGraph?.image ?? {}, + article: openGraph?.article ?? {}, }} languageAlternates={Object.keys(locales).map((lang) => ({ hrefLang: lang, diff --git a/src/pages/resources/press-kit.astro b/src/pages/resources/press-kit.astro index 5e6f0a6dc9..30c4c6a8b4 100644 --- a/src/pages/resources/press-kit.astro +++ b/src/pages/resources/press-kit.astro @@ -23,8 +23,11 @@ import { getPublicImageDimensions } from "@/utils/image"; import { icons } from "@/utils/icons"; const { t, localizeHref } = Astro.locals; -const previewDimensions = await Promise.all( - brandAssets.map((asset) => getPublicImageDimensions(asset.preview)), +const brandAssetPreviews = await Promise.all( + brandAssets.map(async (asset) => ({ + asset, + preview: await getPublicImageDimensions(asset.preview), + })), ); const sections = [ @@ -68,11 +71,11 @@ const sections = [ > { - brandAssets.map((asset, i) => ( + brandAssetPreviews.map(({ asset, preview }) => ( )) } diff --git a/src/plugins/remark-moneropedia/index.ts b/src/plugins/remark-moneropedia/index.ts index bc3f9fe5d8..13f74f1243 100644 --- a/src/plugins/remark-moneropedia/index.ts +++ b/src/plugins/remark-moneropedia/index.ts @@ -83,7 +83,7 @@ export const moneropediaLinks: Plugin<[], Root> = () => { ); } catch (error) { console.error("[moneropedia] Processing error:", error); - return tree; + return; } }; }; diff --git a/src/utils/blog.ts b/src/utils/blog.ts index ce12dd8ef8..0d4228a0fc 100644 --- a/src/utils/blog.ts +++ b/src/utils/blog.ts @@ -2,6 +2,12 @@ export const idToDateSlug = (id: string): string => { const [year, month, day, ...rest] = id.split("-"); const title = rest.join("-"); + if (!year || !month || !day) { + throw new Error( + `Blog id "${id}" is not in the expected YYYY-MM-DD-title form.`, + ); + } + return `${year}/${month.padStart(2, "0")}/${day.padStart(2, "0")}/${title}`; }; diff --git a/src/utils/icons.ts b/src/utils/icons.ts index 7cb1e70b46..33af927fb8 100644 --- a/src/utils/icons.ts +++ b/src/utils/icons.ts @@ -39,7 +39,7 @@ export const icons = { return m; }, - color(name: string): { light: ImageMetadata; dark?: ImageMetadata } { + color(name: string): { light: ImageMetadata; dark: ImageMetadata } { const light = lightIcons[name] ?? colorRoot[name]; if (!light) throw new Error(`Unknown color icon: "${name}"`); return { light, dark: darkIcons[name] ?? light }; diff --git a/tsconfig.json b/tsconfig.json index 96521c2696..7f90d5fcbd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "astro/tsconfigs/strict", + "extends": "astro/tsconfigs/strictest", "include": [".astro/types.d.ts", "**/*"], "exclude": ["dist"], "compilerOptions": {