From 7242c6eb894adab7895573a1d4a47fc0c750a139 Mon Sep 17 00:00:00 2001 From: chloe caruso Date: Sat, 7 Jun 2025 16:45:45 -0700 Subject: [PATCH] fix all type errors --- framework/css.ts | 5 +- framework/definitions.d.ts | 2 + framework/engine/jsx-runtime.ts | 14 + framework/engine/marko-runtime.ts | 10 +- framework/engine/ssr.ts | 37 +- framework/hot.ts | 10 +- framework/meta.ts | 24 + framework/meta/index.ts | 13 - framework/meta/merge.ts | 154 -- framework/meta/nextjs/constants.ts | 15 - framework/meta/nextjs/default-metadata.ts | 50 - framework/meta/nextjs/generate/alternate.tsx | 72 - framework/meta/nextjs/generate/basic.tsx | 171 -- framework/meta/nextjs/generate/icons.tsx | 62 - framework/meta/nextjs/generate/meta.tsx | 124 -- framework/meta/nextjs/generate/opengraph.tsx | 316 --- framework/meta/nextjs/generate/utils.ts | 20 - framework/meta/nextjs/get-metadata-route.ts | 67 - framework/meta/nextjs/is-metadata-route.ts | 136 -- framework/meta/nextjs/metadata.tsx | 58 - framework/meta/nextjs/resolve-metadata.ts | 453 ---- .../meta/nextjs/resolvers/resolve-basics.ts | 259 --- .../meta/nextjs/resolvers/resolve-icons.ts | 34 - .../nextjs/resolvers/resolve-opengraph.ts | 147 -- .../meta/nextjs/resolvers/resolve-title.ts | 39 - .../meta/nextjs/resolvers/resolve-url.ts | 38 - .../nextjs/types/alternative-urls-types.ts | 450 ---- framework/meta/nextjs/types/extra-types.ts | 104 - framework/meta/nextjs/types/manifest-types.ts | 86 - .../meta/nextjs/types/metadata-interface.ts | 566 ----- framework/meta/nextjs/types/metadata-types.ts | 155 -- .../meta/nextjs/types/opengraph-types.ts | 267 --- framework/meta/nextjs/types/resolvers.ts | 17 - framework/meta/nextjs/types/twitter-types.ts | 94 - framework/meta/readme.md | 5 - framework/meta/render.ts | 565 ----- framework/meta/types.ts | 57 - framework/meta/utils.ts | 13 - framework/sitegen.tsx | 19 +- package-lock.json | 12 +- package.json | 6 +- src/pages/index.marko | 4 +- src/q+a/artifacts.ts | 89 + src/q+a/backend.ts | 242 -- src/q+a/format.ts | 39 + src/q+a/simple-markdown.ts | 1957 +++++++++-------- src/q+a/views/backend-edit.tsx | Bin 2000 -> 0 bytes src/q+a/views/backend-inbox.client.ts | Bin 1925 -> 0 bytes src/q+a/views/permalink.tsx | Bin 1881 -> 0 bytes tsconfig.json | 15 +- 50 files changed, 1329 insertions(+), 5763 deletions(-) create mode 100644 framework/meta.ts delete mode 100644 framework/meta/index.ts delete mode 100644 framework/meta/merge.ts delete mode 100644 framework/meta/nextjs/constants.ts delete mode 100644 framework/meta/nextjs/default-metadata.ts delete mode 100644 framework/meta/nextjs/generate/alternate.tsx delete mode 100644 framework/meta/nextjs/generate/basic.tsx delete mode 100644 framework/meta/nextjs/generate/icons.tsx delete mode 100644 framework/meta/nextjs/generate/meta.tsx delete mode 100644 framework/meta/nextjs/generate/opengraph.tsx delete mode 100644 framework/meta/nextjs/generate/utils.ts delete mode 100644 framework/meta/nextjs/get-metadata-route.ts delete mode 100644 framework/meta/nextjs/is-metadata-route.ts delete mode 100644 framework/meta/nextjs/metadata.tsx delete mode 100644 framework/meta/nextjs/resolve-metadata.ts delete mode 100644 framework/meta/nextjs/resolvers/resolve-basics.ts delete mode 100644 framework/meta/nextjs/resolvers/resolve-icons.ts delete mode 100644 framework/meta/nextjs/resolvers/resolve-opengraph.ts delete mode 100644 framework/meta/nextjs/resolvers/resolve-title.ts delete mode 100644 framework/meta/nextjs/resolvers/resolve-url.ts delete mode 100644 framework/meta/nextjs/types/alternative-urls-types.ts delete mode 100644 framework/meta/nextjs/types/extra-types.ts delete mode 100644 framework/meta/nextjs/types/manifest-types.ts delete mode 100644 framework/meta/nextjs/types/metadata-interface.ts delete mode 100644 framework/meta/nextjs/types/metadata-types.ts delete mode 100644 framework/meta/nextjs/types/opengraph-types.ts delete mode 100644 framework/meta/nextjs/types/resolvers.ts delete mode 100644 framework/meta/nextjs/types/twitter-types.ts delete mode 100644 framework/meta/readme.md delete mode 100644 framework/meta/render.ts delete mode 100644 framework/meta/types.ts delete mode 100644 framework/meta/utils.ts create mode 100644 src/q+a/artifacts.ts delete mode 100644 src/q+a/backend.ts create mode 100644 src/q+a/format.ts delete mode 100644 src/q+a/views/backend-edit.tsx delete mode 100644 src/q+a/views/backend-inbox.client.ts delete mode 100644 src/q+a/views/permalink.tsx diff --git a/framework/css.ts b/framework/css.ts index 682f6c7..c7063c5 100644 --- a/framework/css.ts +++ b/framework/css.ts @@ -26,7 +26,10 @@ export function preprocess(css: string, theme: Theme): string { return css.replace( regex, (_, line) => - line.replace(regex2, (_: string, varName: string) => theme[varName]) + + line.replace( + regex2, + (_: string, varName: string) => theme[varName as keyof Theme], + ) + ";" + line.slice(1), ); } diff --git a/framework/definitions.d.ts b/framework/definitions.d.ts index aca66bc..fa876b0 100644 --- a/framework/definitions.d.ts +++ b/framework/definitions.d.ts @@ -1,2 +1,4 @@ declare function UNWRAP(value: T | null | undefined): T; declare function ASSERT(value: unknown, ...log: unknown[]): asserts value; + +type Timer = ReturnType; diff --git a/framework/engine/jsx-runtime.ts b/framework/engine/jsx-runtime.ts index fe84691..999d500 100644 --- a/framework/engine/jsx-runtime.ts +++ b/framework/engine/jsx-runtime.ts @@ -29,4 +29,18 @@ export function jsxDEV( // jsxs export { jsx as jsxs }; +declare global { + namespace JSX { + interface IntrinsicElements { + [name: string]: Record; + } + interface ElementChildrenAttribute { + children: {}; + } + type Element = engine.Node; + type ElementType = keyof IntrinsicElements | engine.Component; + type ElementClass = ReturnType; + } +} + import * as engine from "./ssr.ts"; diff --git a/framework/engine/marko-runtime.ts b/framework/engine/marko-runtime.ts index cdc9c09..7bfe732 100644 --- a/framework/engine/marko-runtime.ts +++ b/framework/engine/marko-runtime.ts @@ -72,7 +72,7 @@ export const dynamicTag = ( ]); if (subRender.async > 0) { - const marker = marko.$global().cloverAsyncMarker; + const marker = marko.$global().cloverAsyncMarker as Async; marker.isAsync = true; // Wait for async work to finish @@ -108,17 +108,21 @@ export const dynamicTag = ( }; export function fork( - scopeId: string, + scopeId: number, accessor: Accessor, promise: Promise, callback: (data: unknown) => void, serializeMarker?: 0 | 1, ) { - const marker = marko.$global().cloverAsyncMarker; + const marker = marko.$global().cloverAsyncMarker as Async; marker.isAsync = true; marko.fork(scopeId, accessor, promise, callback, serializeMarker); } +interface Async { + isAsync: boolean; +} + import * as engine from "./ssr.ts"; import type { ServerRenderer } from "marko/html/template"; import { type Accessor } from "marko/common/types"; diff --git a/framework/engine/ssr.ts b/framework/engine/ssr.ts index 242250d..7dbefe3 100644 --- a/framework/engine/ssr.ts +++ b/framework/engine/ssr.ts @@ -6,24 +6,22 @@ // Add-ons to the rendering engine can provide opaque data, And retrieve it // within component calls with 'getAddonData'. For example, 'sitegen' uses this // to track needed client scripts without introducing patches to the engine. +type Addons = Record; -type AddonData = Record; export function ssrSync(node: Node): Result; -export function ssrSync( - node: Node, - addon: AddonData, -): Result; -export function ssrSync(node: Node, addon: AddonData = {}) { +export function ssrSync(node: Node, addon: A): Result; +export function ssrSync(node: Node, addon: Addons = {}) { const r = initRender(false, addon); const resolved = resolveNode(r, node); return { text: renderNode(resolved), addon }; } + export function ssrAsync(node: Node): Promise; -export function ssrAsync( +export function ssrAsync( node: Node, - addon: AddonData, + addon: A, ): Promise>; -export function ssrAsync(node: Node, addon: AddonData = {}) { +export function ssrAsync(node: Node, addon: Addons = {}) { const r = initRender(true, addon); const resolved = resolveNode(r, node); if (r.async === 0) { @@ -44,7 +42,7 @@ export function html(rawText: string) { return [kDirectHtml, rawText]; } -interface Result { +interface Result { text: string; addon: A; } @@ -59,7 +57,7 @@ export interface Render { /** When components reject, those are logged here */ rejections: unknown[] | null; /** Add-ons to the rendering engine store state here */ - addon: AddonData; + addon: Addons; } export const kElement = Symbol("Element"); @@ -100,7 +98,7 @@ export function resolveNode(r: Render, node: unknown): ResolvedNode { if (!node && node !== 0) return ""; // falsy, non numeric if (typeof node !== "object") { if (node === true) return ""; // booleans are ignored - if (typeof node === "string") return escapeHTML(node); + if (typeof node === "string") return escapeHtml(node); if (typeof node === "number") return String(node); // no escaping ever throw new Error(`Cannot render ${inspect(node)} to HTML`); } @@ -193,12 +191,12 @@ function renderElement(element: ResolvedElement) { let attr; switch (prop) { default: - attr = `${prop}=${quoteIfNeeded(escapeHTML(String(value)))}`; + attr = `${prop}=${quoteIfNeeded(escapeHtml(String(value)))}`; break; case "className": // Legacy React Compat case "class": - attr = `class=${quoteIfNeeded(escapeHTML(clsx(value)))}`; + attr = `class=${quoteIfNeeded(escapeHtml(clsx(value as ClsxInput)))}`; break; case "htmlFor": throw new Error("Do not use the `htmlFor` attribute. Use `for`"); @@ -227,19 +225,19 @@ export function renderStyleAttribute(style: Record) { for (const styleName in style) { if (out) out += ";"; out += `${styleName.replace(/[A-Z]/g, "-$&").toLowerCase()}:${ - escapeHTML(String(style[styleName])) + escapeHtml(String(style[styleName])) }`; } return "style=" + quoteIfNeeded(out); } -export function quoteIfNeeded(text) { +export function quoteIfNeeded(text: string) { if (text.includes(" ")) return '"' + text + '"'; return text; } // -- utility functions -- -export function initRender(allowAsync: boolean, addon: AddonData): Render { +export function initRender(allowAsync: boolean, addon: Addons): Render { return { async: allowAsync ? 0 : -1, rejections: null, @@ -270,11 +268,10 @@ export function inspect(object: unknown) { export type ClsxInput = string | Record | ClsxInput[]; export function clsx(mix: ClsxInput) { - var k, y, str; + var k, y, str = ""; if (typeof mix === "string") { return mix; } else if (typeof mix === "object") { - str = ""; if (Array.isArray(mix)) { for (k = 0; k < mix.length; k++) { if (mix[k] && (y = clsx(mix[k]))) { @@ -294,7 +291,7 @@ export function clsx(mix: ClsxInput) { return str; } -export const escapeHTML = (unsafeText: string) => +export const escapeHtml = (unsafeText: string) => String(unsafeText) .replace(/&/g, "&").replace(//g, ">") .replace(/"/g, """).replace(/'/g, "'").replace(/`/g, "`"); diff --git a/framework/hot.ts b/framework/hot.ts index faa2e87..c1969f4 100644 --- a/framework/hot.ts +++ b/framework/hot.ts @@ -162,7 +162,7 @@ function loadMarko(module: NodeJS.Module, filepath: string) { ) + '\nimport { Script as CloverScriptInclude } from "#sitegen";'; } - src = marko.compileSync(filepath, {}).code; + src = marko.compileSync(src, filepath).code; src = src.replace("marko/debug/html", "#ssr/marko"); return loadEsbuildCode(module, filepath, src); } @@ -174,7 +174,7 @@ function loadMdx(module: NodeJS.Module, filepath: string) { return loadEsbuildCode(module, filepath, src); } -function loadCss(module: NodeJS.Module, filepath: string) { +function loadCss(module: NodeJS.Module, _filepath: string) { module.exports = {}; } @@ -228,6 +228,12 @@ declare global { } } } +declare module "node:module" { + export function _resolveFilename( + id: string, + parent: NodeJS.Module, + ): unknown; +} import * as fs from "./fs.ts"; import * as path from "node:path"; diff --git a/framework/meta.ts b/framework/meta.ts new file mode 100644 index 0000000..cfde26b --- /dev/null +++ b/framework/meta.ts @@ -0,0 +1,24 @@ +export interface Meta { + title: string; + description?: string | undefined; + openGraph?: OpenGraph; + alternates?: Alternates; +} +export interface OpenGraph { + title?: string; + description?: string | undefined; + type: string; + url: string; +} +export interface Alternates { + canonical: string; + types: { [mime: string]: AlternateType }; +} +export interface AlternateType { + url: string; + title: string; +} +export function renderMeta({ title }: Meta): string { + return `${esc(title)}`; +} +import { escapeHtml as esc } from "./engine/ssr.ts"; diff --git a/framework/meta/index.ts b/framework/meta/index.ts deleted file mode 100644 index ffe86b7..0000000 --- a/framework/meta/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { resolveMetadata } from "./merge"; -import { renderMetadata } from "./render"; -import { Metadata } from "./types"; - -export * from "./types"; -export * from "./merge"; -export * from "./render"; - -export function resolveAndRenderMetadata( - ...metadata: [Metadata, ...Metadata[]] -) { - return renderMetadata(resolveMetadata(...metadata)); -} diff --git a/framework/meta/merge.ts b/framework/meta/merge.ts deleted file mode 100644 index 0cbfc75..0000000 --- a/framework/meta/merge.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { createDefaultMetadata } from "./nextjs/default-metadata"; -import { resolveAsArrayOrUndefined } from "./nextjs/generate/utils"; -import { - resolveAlternates, - resolveAppleWebApp, - resolveAppLinks, - resolveRobots, - resolveThemeColor, - resolveVerification, - resolveViewport, -} from "./nextjs/resolvers/resolve-basics"; -import { resolveIcons } from "./nextjs/resolvers/resolve-icons"; -import { - resolveOpenGraph, - resolveTwitter, -} from "./nextjs/resolvers/resolve-opengraph"; -import { resolveTitle } from "./nextjs/resolvers/resolve-title"; -import type { - Metadata, - ResolvedMetadata, -} from "./nextjs/types/metadata-interface"; - -type MetadataAccumulationOptions = { - pathname: string; -}; - -// Merge the source metadata into the resolved target metadata. -function merge( - target: ResolvedMetadata, - source: Metadata | null, - titleTemplates: { - title?: string | null; - twitter?: string | null; - openGraph?: string | null; - } = {}, -) { - const metadataBase = source?.metadataBase || target.metadataBase; - for (const key_ in source) { - const key = key_ as keyof Metadata; - - switch (key) { - case "title": { - target.title = resolveTitle(source.title, titleTemplates.title); - break; - } - case "alternates": { - target.alternates = resolveAlternates(source.alternates, metadataBase, { - pathname: (source as any)._pathname ?? "/", - }); - break; - } - case "openGraph": { - target.openGraph = resolveOpenGraph(source.openGraph, metadataBase); - if (target.openGraph) { - target.openGraph.title = resolveTitle( - target.openGraph.title, - titleTemplates.openGraph, - ); - } - break; - } - case "twitter": { - target.twitter = resolveTwitter(source.twitter, metadataBase); - if (target.twitter) { - target.twitter.title = resolveTitle( - target.twitter.title, - titleTemplates.twitter, - ); - } - break; - } - case "verification": - target.verification = resolveVerification(source.verification); - break; - case "viewport": { - target.viewport = resolveViewport(source.viewport); - break; - } - case "icons": { - target.icons = resolveIcons(source.icons); - break; - } - case "appleWebApp": - target.appleWebApp = resolveAppleWebApp(source.appleWebApp); - break; - case "appLinks": - target.appLinks = resolveAppLinks(source.appLinks); - break; - case "robots": { - target.robots = resolveRobots(source.robots); - break; - } - case "themeColor": { - target.themeColor = resolveThemeColor(source.themeColor); - break; - } - case "archives": - case "assets": - case "bookmarks": - case "keywords": - case "authors": { - // FIXME: type inferring - // @ts-ignore - target[key] = resolveAsArrayOrUndefined(source[key]) || null; - break; - } - // directly assign fields that fallback to null - case "applicationName": - case "description": - case "generator": - case "creator": - case "publisher": - case "category": - case "classification": - case "referrer": - case "colorScheme": - case "itunes": - case "formatDetection": - case "manifest": - // @ts-ignore TODO: support inferring - target[key] = source[key] || null; - break; - case "other": - target.other = Object.assign({}, target.other, source.other); - break; - case "metadataBase": - target.metadataBase = metadataBase; - break; - default: - break; - } - } - - return target; -} - -export interface MetadataWithPathname extends Metadata { - /** Set by framework author to the pathname of the page defining this metadata. */ - _pathname?: string; -} - -export function resolveMetadata( - ...metadata: [MetadataWithPathname, ...MetadataWithPathname[]] -) { - const base = createDefaultMetadata(); - for (const item of metadata) { - merge(base, item, { - title: base.title?.template, - twitter: base.twitter?.title?.template, - openGraph: base.openGraph?.title?.template, - }); - } - return base; -} diff --git a/framework/meta/nextjs/constants.ts b/framework/meta/nextjs/constants.ts deleted file mode 100644 index 5f9b883..0000000 --- a/framework/meta/nextjs/constants.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Viewport } from "./types/extra-types"; -import type { Icons } from "./types/metadata-types"; - -export const ViewPortKeys: { [k in keyof Viewport]: string } = { - width: "width", - height: "height", - initialScale: "initial-scale", - minimumScale: "minimum-scale", - maximumScale: "maximum-scale", - viewportFit: "viewport-fit", - userScalable: "user-scalable", - interactiveWidget: "interactive-widget", -} as const; - -export const IconKeys: (keyof Icons)[] = ["icon", "shortcut", "apple", "other"]; diff --git a/framework/meta/nextjs/default-metadata.ts b/framework/meta/nextjs/default-metadata.ts deleted file mode 100644 index 9b5a8ae..0000000 --- a/framework/meta/nextjs/default-metadata.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type { ResolvedMetadata } from "./types/metadata-interface"; -import process from "node:process"; - -export function createDefaultMetadata(): ResolvedMetadata { - const defaultMetadataBase = - process.env.NODE_ENV === "production" && process.env.VERCEL_URL - ? new URL(`https://${process.env.VERCEL_URL}`) - : null; - - return { - viewport: "width=device-width, initial-scale=1", - metadataBase: defaultMetadataBase, - - // Other values are all null - title: null, - description: null, - applicationName: null, - authors: null, - generator: null, - keywords: null, - referrer: null, - themeColor: null, - colorScheme: null, - creator: null, - publisher: null, - robots: null, - manifest: null, - alternates: { - canonical: null, - languages: null, - media: null, - types: null, - }, - icons: null, - openGraph: null, - twitter: null, - verification: {}, - appleWebApp: null, - formatDetection: null, - itunes: null, - abstract: null, - appLinks: null, - archives: null, - assets: null, - bookmarks: null, - category: null, - classification: null, - other: {}, - }; -} diff --git a/framework/meta/nextjs/generate/alternate.tsx b/framework/meta/nextjs/generate/alternate.tsx deleted file mode 100644 index 26f694d..0000000 --- a/framework/meta/nextjs/generate/alternate.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import type { ResolvedMetadata } from "../types/metadata-interface"; - -import React from "react"; -import { AlternateLinkDescriptor } from "../types/alternative-urls-types"; - -function AlternateLink({ - descriptor, - ...props -}: { - descriptor: AlternateLinkDescriptor; -} & React.LinkHTMLAttributes) { - if (!descriptor.url) return null; - return ( - - ); -} - -export function AlternatesMetadata({ - alternates, -}: { - alternates: ResolvedMetadata["alternates"]; -}) { - if (!alternates) return null; - const { canonical, languages, media, types } = alternates; - return ( - <> - {canonical - ? - : null} - {languages - ? Object.entries(languages).map(([locale, descriptors]) => { - return descriptors?.map((descriptor, index) => ( - - )); - }) - : null} - {media - ? Object.entries(media).map(([mediaName, descriptors]) => - descriptors?.map((descriptor, index) => ( - - )) - ) - : null} - {types - ? Object.entries(types).map(([type, descriptors]) => - descriptors?.map((descriptor, index) => ( - - )) - ) - : null} - - ); -} diff --git a/framework/meta/nextjs/generate/basic.tsx b/framework/meta/nextjs/generate/basic.tsx deleted file mode 100644 index 9585d1b..0000000 --- a/framework/meta/nextjs/generate/basic.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import type { ResolvedMetadata } from "../types/metadata-interface"; - -import React from "react"; -import { Meta, MultiMeta } from "./meta"; - -export function BasicMetadata({ metadata }: { metadata: ResolvedMetadata }) { - return ( - <> - - {metadata.title !== null && metadata.title.absolute - ? {metadata.title.absolute} - : null} - - - {metadata.authors - ? metadata.authors.map((author, index) => ( - - {author.url && } - - - )) - : null} - {metadata.manifest - ? - : null} - - - - {metadata.themeColor - ? metadata.themeColor.map((themeColor, index) => ( - - )) - : null} - - - - - - - - {metadata.archives - ? metadata.archives.map((archive) => ( - - )) - : null} - {metadata.assets - ? metadata.assets.map((asset) => ( - - )) - : null} - {metadata.bookmarks - ? metadata.bookmarks.map((bookmark) => ( - - )) - : null} - - - {metadata.other - ? Object.entries(metadata.other).map(([name, content]) => ( - - )) - : null} - - ); -} - -export function ItunesMeta({ itunes }: { itunes: ResolvedMetadata["itunes"] }) { - if (!itunes) return null; - const { appId, appArgument } = itunes; - let content = `app-id=${appId}`; - if (appArgument) { - content += `, app-argument=${appArgument}`; - } - return ; -} - -const formatDetectionKeys = [ - "telephone", - "date", - "address", - "email", - "url", -] as const; -export function FormatDetectionMeta({ - formatDetection, -}: { - formatDetection: ResolvedMetadata["formatDetection"]; -}) { - if (!formatDetection) return null; - let content = ""; - for (const key of formatDetectionKeys) { - if (key in formatDetection) { - if (content) content += ", "; - content += `${key}=no`; - } - } - return ; -} - -export function AppleWebAppMeta({ - appleWebApp, -}: { - appleWebApp: ResolvedMetadata["appleWebApp"]; -}) { - if (!appleWebApp) return null; - const { capable, title, startupImage, statusBarStyle } = appleWebApp; - - return ( - <> - {capable - ? - : null} - - {startupImage - ? startupImage.map((image, index) => ( - - )) - : null} - {statusBarStyle - ? ( - - ) - : null} - - ); -} - -export function VerificationMeta({ - verification, -}: { - verification: ResolvedMetadata["verification"]; -}) { - if (!verification) return null; - - return ( - <> - - - - - {verification.other - ? Object.entries(verification.other).map(([key, value], index) => ( - - )) - : null} - - ); -} diff --git a/framework/meta/nextjs/generate/icons.tsx b/framework/meta/nextjs/generate/icons.tsx deleted file mode 100644 index af54eb1..0000000 --- a/framework/meta/nextjs/generate/icons.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import type { ResolvedMetadata } from "../types/metadata-interface"; -import type { Icon, IconDescriptor } from "../types/metadata-types"; - -import React from "react"; - -function IconDescriptorLink({ icon }: { icon: IconDescriptor }) { - const { url, rel = "icon", ...props } = icon; - - return ; -} - -function IconLink({ rel, icon }: { rel?: string; icon: Icon }) { - if (typeof icon === "object" && !(icon instanceof URL)) { - if (rel) icon.rel = rel; - return ; - } else { - const href = icon.toString(); - return ; - } -} - -export function IconsMetadata({ icons }: { icons: ResolvedMetadata["icons"] }) { - if (!icons) return null; - - const shortcutList = icons.shortcut; - const iconList = icons.icon; - const appleList = icons.apple; - const otherList = icons.other; - - return ( - <> - {shortcutList - ? shortcutList.map((icon, index) => ( - - )) - : null} - {iconList - ? iconList.map((icon, index) => ( -