@ox-content/vite-plugin 2.76.0 → 2.79.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +36 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +32 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +112 -77
- package/dist/index.mjs.map +1 -1
- package/dist/media.cjs +283 -2
- package/dist/media.cjs.map +1 -1
- package/dist/media.mjs +302 -2
- package/dist/media.mjs.map +1 -0
- package/dist/media2.mjs +2 -22
- package/dist/vitepress-cli.mjs +4 -4
- package/dist/vitepress-cli.mjs.map +1 -1
- package/dist/vitepress.cjs +19 -1
- package/dist/vitepress.cjs.map +1 -1
- package/dist/vitepress.mjs +19 -1
- package/dist/vitepress.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/media2.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media.mjs","names":[],"sources":["../src/plugins/twitter/url.ts","../src/plugins/twitter/fetch.ts","../src/plugins/twitter/render.ts","../src/plugins/twitter/transform.ts","../src/plugins/media.ts"],"sourcesContent":["import type { TweetReference } from \"./types\";\n\nconst STATUS_PATH = /^\\/(?:[^/]+|i\\/web)\\/status\\/(\\d+)(?:\\/.*)?$/;\n\nexport function createSyndicationToken(id: string): string {\n return ((Number(id) / 1e15) * Math.PI).toString(36).replaceAll(/(0+|\\.)/g, \"\");\n}\n\nexport function parseTweetReference(value: string): TweetReference | null {\n const trimmed = value.trim();\n if (/^\\d+$/.test(trimmed)) {\n return { id: trimmed, url: `https://x.com/i/web/status/${trimmed}` };\n }\n\n try {\n const url = new URL(trimmed);\n const hostname = url.hostname.toLowerCase().replace(/^(?:www\\.|mobile\\.)/, \"\");\n if (url.protocol !== \"https:\" || (hostname !== \"x.com\" && hostname !== \"twitter.com\")) {\n return null;\n }\n\n const match = url.pathname.match(STATUS_PATH);\n if (!match) return null;\n const screenName = url.pathname.startsWith(\"/i/web/status/\")\n ? \"i/web\"\n : url.pathname.split(\"/\")[1];\n return {\n id: match[1],\n url: `https://x.com/${screenName}/status/${match[1]}`,\n };\n } catch {\n return null;\n }\n}\n\nexport function referenceFromAttributes(attributes: string): TweetReference | null {\n const values = new Map<string, string>();\n const pattern = /\\b(url|href|id)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|([^\\s>]+))/gi;\n for (const match of attributes.matchAll(pattern)) {\n values.set(match[1].toLowerCase(), match[2] ?? match[3] ?? match[4] ?? \"\");\n }\n return parseTweetReference(values.get(\"url\") ?? values.get(\"href\") ?? values.get(\"id\") ?? \"\");\n}\n","import { access, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createSyndicationToken } from \"./url\";\nimport type { ResolvedTwitterEmbedOptions, TweetAssets, TweetData, TweetMedia } from \"./types\";\n\nconst tweetCache = new Map<string, TweetData>();\n\nexport function clearTweetCache(): void {\n tweetCache.clear();\n}\n\nexport async function fetchTweetData(\n id: string,\n options: ResolvedTwitterEmbedOptions,\n): Promise<TweetData | null> {\n const key = `${id}-${sanitizeSegment(options.lang)}`;\n if (options.cache) {\n const memory = tweetCache.get(key);\n if (memory) return memory;\n const disk = await readCachedTweet(key, options.cacheDir);\n if (disk) {\n tweetCache.set(key, disk);\n return disk;\n }\n }\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), options.timeout);\n const endpoint = new URL(\"https://cdn.syndication.twimg.com/tweet-result\");\n endpoint.searchParams.set(\"id\", id);\n endpoint.searchParams.set(\"lang\", options.lang);\n endpoint.searchParams.set(\"token\", createSyndicationToken(id));\n\n try {\n const response = await fetch(endpoint, {\n headers: { Accept: \"application/json\" },\n signal: controller.signal,\n });\n if (!response.ok) return null;\n const data: unknown = await response.json();\n if (!isTweetData(data)) return null;\n if (options.cache) {\n tweetCache.set(key, data);\n await writeCachedTweet(key, data, options.cacheDir);\n }\n return data;\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n}\n\nexport async function materializeTweetAssets(\n id: string,\n data: TweetData,\n options: ResolvedTwitterEmbedOptions,\n): Promise<TweetAssets> {\n const assets: TweetAssets = { media: [] };\n const avatarUrl = data.user.profile_image_url_https?.replace(/_normal(?=\\.[^.]+$)/, \"_bigger\");\n if (avatarUrl) {\n assets.avatar = await downloadAsset(avatarUrl, `${id}-avatar`, options);\n }\n\n const media = data.mediaDetails ?? data.entities?.media ?? [];\n for (const [index, item] of media.entries()) {\n if (item.type && item.type !== \"photo\") continue;\n if (!item.media_url_https) continue;\n const src = await downloadAsset(item.media_url_https, `${id}-media-${index + 1}`, options);\n if (src) assets.media.push(assetRecord(src, item));\n }\n return assets;\n}\n\nasync function downloadAsset(\n source: string,\n basename: string,\n options: ResolvedTwitterEmbedOptions,\n): Promise<string | undefined> {\n let url: URL;\n try {\n url = new URL(source);\n } catch {\n return undefined;\n }\n if (url.protocol !== \"https:\" || url.hostname.toLowerCase() !== \"pbs.twimg.com\") {\n return undefined;\n }\n\n const extension = extensionFromUrl(url);\n const filename = `${basename}${extension}`;\n const output = path.join(options.mediaOutputDir, filename);\n try {\n await access(output);\n return joinPublicPath(options.mediaPublicPath, filename);\n } catch {\n // Download the missing asset below.\n }\n\n try {\n const response = await fetch(url, { headers: { Accept: \"image/*\" } });\n if (!response.ok) return undefined;\n await mkdir(options.mediaOutputDir, { recursive: true });\n await writeFile(output, new Uint8Array(await response.arrayBuffer()));\n return joinPublicPath(options.mediaPublicPath, filename);\n } catch {\n return undefined;\n }\n}\n\nasync function readCachedTweet(key: string, directory: string): Promise<TweetData | null> {\n try {\n const data: unknown = JSON.parse(await readFile(path.join(directory, `${key}.json`), \"utf8\"));\n return isTweetData(data) ? data : null;\n } catch {\n return null;\n }\n}\n\nasync function writeCachedTweet(key: string, data: TweetData, directory: string): Promise<void> {\n try {\n await mkdir(directory, { recursive: true });\n await writeFile(path.join(directory, `${key}.json`), `${JSON.stringify(data)}\\n`);\n } catch {\n // A read-only cache directory must not fail the build.\n }\n}\n\nfunction isTweetData(data: unknown): data is TweetData {\n if (!data || typeof data !== \"object\") return false;\n const value = data as Partial<TweetData>;\n return (\n typeof value.text === \"string\" &&\n Boolean(value.user) &&\n typeof value.user?.name === \"string\" &&\n typeof value.user.screen_name === \"string\"\n );\n}\n\nfunction extensionFromUrl(url: URL): string {\n const match = url.pathname.match(/\\.(jpe?g|png|webp|gif)$/i);\n return match ? `.${match[1].toLowerCase().replace(\"jpeg\", \"jpg\")}` : \".jpg\";\n}\n\nfunction joinPublicPath(prefix: string, filename: string): string {\n return `${prefix.replace(/\\/$/, \"\")}/${filename}`;\n}\n\nfunction sanitizeSegment(value: string): string {\n return value.replaceAll(/[^a-zA-Z0-9_-]/g, \"-\");\n}\n\nfunction assetRecord(src: string, media: TweetMedia): TweetAssets[\"media\"][number] {\n return {\n src,\n alt: media.ext_alt_text,\n width: media.original_info?.width,\n height: media.original_info?.height,\n };\n}\n","import type { ResolvedTwitterEmbedOptions, TweetAssets, TweetData, TweetEntity } from \"./types\";\n\nexport function renderFetchedTweet(\n permalink: string,\n data: TweetData,\n assets: TweetAssets,\n options: ResolvedTwitterEmbedOptions,\n): string {\n const profile = `https://x.com/${encodeURIComponent(data.user.screen_name)}`;\n const author = escapeHtml(data.user.name);\n const handle = escapeHtml(data.user.screen_name);\n const avatar = assets.avatar\n ? `<img class=\"ox-tweet__avatar\" src=\"${escapeAttribute(assets.avatar)}\" alt=\"\" width=\"48\" height=\"48\" loading=\"lazy\" decoding=\"async\">`\n : \"\";\n const media = renderMedia(assets);\n const footer = renderFooter(permalink, data.created_at, options.lang);\n\n return [\n '<figure class=\"ox-tweet ox-tweet--fetched\">',\n '<header class=\"ox-tweet__header\">',\n `<a class=\"ox-tweet__profile\" href=\"${escapeAttribute(profile)}\" target=\"_blank\" rel=\"noopener noreferrer\">`,\n avatar,\n `<span class=\"ox-tweet__author-name\">${author}</span>`,\n `<span class=\"ox-tweet__author-handle\">@${handle}</span>`,\n \"</a></header>\",\n `<div class=\"ox-tweet__body\">${renderTweetText(data)}</div>`,\n media,\n footer,\n \"</figure>\",\n ].join(\"\");\n}\n\nexport function renderTweetText(data: TweetData): string {\n const [start, end] = data.display_text_range ?? [0, data.text.length];\n const entities = collectEntities(data)\n .filter((entity) => validRange(entity.indices, start, end))\n .sort((left, right) => left.indices![0] - right.indices![0]);\n\n let cursor = start;\n let output = \"\";\n for (const entity of entities) {\n const [entityStart, entityEnd] = entity.indices!;\n if (entityStart < cursor) continue;\n output += escapeText(data.text.slice(cursor, entityStart));\n if (entity.kind === \"url\") {\n const href = entity.expanded_url ?? entity.url;\n const label = entity.display_url ?? href;\n output += `<a href=\"${escapeAttribute(href)}\" target=\"_blank\" rel=\"noopener noreferrer\">${escapeHtml(label)}</a>`;\n }\n cursor = entityEnd;\n }\n output += escapeText(data.text.slice(cursor, end));\n return output.trim();\n}\n\nfunction collectEntities(data: TweetData): Array<TweetEntity & { kind: \"url\" | \"media\" }> {\n return [\n ...(data.entities?.urls ?? []).map((entity) => ({ ...entity, kind: \"url\" as const })),\n ...(data.entities?.media ?? []).map((entity) => ({ ...entity, kind: \"media\" as const })),\n ];\n}\n\nfunction validRange(\n indices: [number, number] | undefined,\n start: number,\n end: number,\n): indices is [number, number] {\n return Boolean(indices && indices[0] >= start && indices[1] <= end && indices[0] < indices[1]);\n}\n\nfunction renderMedia(assets: TweetAssets): string {\n if (assets.media.length === 0) return \"\";\n const images = assets.media\n .map((item) => {\n const size = [\n item.width ? ` width=\"${item.width}\"` : \"\",\n item.height ? ` height=\"${item.height}\"` : \"\",\n ].join(\"\");\n return `<img class=\"ox-tweet__media-item\" src=\"${escapeAttribute(item.src)}\" alt=\"${escapeAttribute(item.alt ?? \"\")}\"${size} loading=\"lazy\" decoding=\"async\">`;\n })\n .join(\"\");\n return `<div class=\"ox-tweet__media\" data-count=\"${assets.media.length}\">${images}</div>`;\n}\n\nfunction renderFooter(permalink: string, createdAt: string | undefined, lang: string): string {\n if (!createdAt) {\n return `<footer class=\"ox-tweet__footer\"><a class=\"ox-tweet__permalink\" href=\"${escapeAttribute(permalink)}\" target=\"_blank\" rel=\"noopener noreferrer\">View on X</a></footer>`;\n }\n const date = new Date(createdAt);\n if (Number.isNaN(date.valueOf())) return renderFooter(permalink, undefined, lang);\n const iso = date.toISOString();\n let label: string;\n try {\n label = new Intl.DateTimeFormat(lang, { dateStyle: \"medium\", timeZone: \"UTC\" }).format(date);\n } catch {\n label = new Intl.DateTimeFormat(\"en\", { dateStyle: \"medium\", timeZone: \"UTC\" }).format(date);\n }\n return `<footer class=\"ox-tweet__footer\"><a class=\"ox-tweet__permalink\" href=\"${escapeAttribute(permalink)}\" target=\"_blank\" rel=\"noopener noreferrer\"><time datetime=\"${iso}\">${escapeHtml(label)}</time></a></footer>`;\n}\n\nfunction escapeText(value: string): string {\n return escapeHtml(value).replaceAll(\"\\n\", \"<br>\");\n}\n\nfunction escapeAttribute(value: string): string {\n return escapeHtml(value).replaceAll(\"`\", \"`\");\n}\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n}\n","import path from \"node:path\";\nimport { fetchTweetData, materializeTweetAssets } from \"./fetch\";\nimport { renderFetchedTweet } from \"./render\";\nimport type { ResolvedTwitterEmbedOptions, TwitterEmbedOptions } from \"./types\";\nimport { referenceFromAttributes } from \"./url\";\n\nconst TWEET_ELEMENT = /<(tweet|xpost)\\b([^>]*?)(?:\\/\\s*>|>[\\s\\S]*?<\\/\\1\\s*>)/gi;\n\nexport function resolveTwitterEmbedOptions(\n options: TwitterEmbedOptions,\n): ResolvedTwitterEmbedOptions {\n return {\n fetch: options.fetch ?? false,\n lang: options.lang ?? \"en\",\n timeout: options.timeout ?? 10000,\n cache: options.cache ?? true,\n cacheDir: path.resolve(options.cacheDir ?? \".cache/ox-content/twitter\"),\n mediaOutputDir: path.resolve(options.mediaOutputDir ?? \"public/ox-content/twitter\"),\n mediaPublicPath: options.mediaPublicPath ?? \"/ox-content/twitter\",\n };\n}\n\nexport async function transformFetchedTweets(\n html: string,\n options: TwitterEmbedOptions,\n): Promise<string> {\n const resolved = resolveTwitterEmbedOptions(options);\n if (!resolved.fetch) return html;\n\n let output = \"\";\n let cursor = 0;\n for (const match of html.matchAll(TWEET_ELEMENT)) {\n const index = match.index ?? 0;\n output += html.slice(cursor, index);\n const reference = referenceFromAttributes(match[2]);\n if (!reference) {\n output += match[0];\n cursor = index + match[0].length;\n continue;\n }\n\n const data = await fetchTweetData(reference.id, resolved);\n if (!data) {\n output += match[0];\n cursor = index + match[0].length;\n continue;\n }\n\n const assets = await materializeTweetAssets(reference.id, data, resolved);\n output += renderFetchedTweet(reference.url, data, assets, resolved);\n cursor = index + match[0].length;\n }\n return output + html.slice(cursor);\n}\n","import { importNapiModule } from \"../napi\";\nimport { transformFetchedTweets } from \"./twitter\";\nimport type { TwitterEmbedOptions } from \"./twitter\";\n\nexport interface MediaEmbedOptions {\n /**\n * Render `<Spotify>` embeds.\n * @default false\n */\n spotify?: boolean;\n\n /**\n * Render `<StackBlitz>` embeds.\n * @default false\n */\n stackBlitz?: boolean;\n\n /**\n * Render `<Tweet>` / `<XPost>` static cards. Pass `{ fetch: true }` to\n * resolve the post content and self-host its media at build time.\n * @default false\n */\n twitter?: boolean | TwitterEmbedOptions;\n\n /**\n * Render `<Bluesky>` static cards.\n * @default false\n */\n bluesky?: boolean;\n\n /**\n * Render `<WebContainer>` lazy placeholder blocks.\n * @default false\n */\n webContainer?: boolean;\n}\n\nexport async function transformMediaEmbeds(\n html: string,\n options: MediaEmbedOptions,\n): Promise<string> {\n if (!hasEnabledMediaEmbed(options) || !hasMediaMarker(html)) {\n return html;\n }\n\n let result = html;\n if (typeof options.twitter === \"object\") {\n result = await transformFetchedTweets(result, options.twitter);\n }\n if (!hasMediaMarker(result)) return result;\n\n const mod = await importNapiModule();\n return mod.transformMediaEmbeds(result, {\n spotify: options.spotify,\n stackBlitz: options.stackBlitz,\n twitter: Boolean(options.twitter),\n bluesky: options.bluesky,\n webContainer: options.webContainer,\n });\n}\n\nfunction hasEnabledMediaEmbed(options: MediaEmbedOptions): boolean {\n return Boolean(\n options.spotify ||\n options.stackBlitz ||\n options.twitter ||\n options.bluesky ||\n options.webContainer,\n );\n}\n\nfunction hasMediaMarker(html: string): boolean {\n return /<(spotify|stackblitz|tweet|xpost|bluesky|webcontainer)[\\s/>]/i.test(html);\n}\n"],"mappings":";;;;AAEA,MAAM,cAAc;AAEpB,SAAgB,uBAAuB,IAAoB;CACzD,QAAS,OAAO,GAAG,GAAG,kBAAQ,KAAK,IAAI,SAAS,GAAG,CAAC,WAAW,YAAY,GAAG;;AAGhF,SAAgB,oBAAoB,OAAsC;CACxE,MAAM,UAAU,MAAM,MAAM;CAC5B,IAAI,QAAQ,KAAK,QAAQ,EACvB,OAAO;EAAE,IAAI;EAAS,KAAK,8BAA8B;EAAW;CAGtE,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,QAAQ;EAC5B,MAAM,WAAW,IAAI,SAAS,aAAa,CAAC,QAAQ,uBAAuB,GAAG;EAC9E,IAAI,IAAI,aAAa,YAAa,aAAa,WAAW,aAAa,eACrE,OAAO;EAGT,MAAM,QAAQ,IAAI,SAAS,MAAM,YAAY;EAC7C,IAAI,CAAC,OAAO,OAAO;EACnB,MAAM,aAAa,IAAI,SAAS,WAAW,iBAAiB,GACxD,UACA,IAAI,SAAS,MAAM,IAAI,CAAC;EAC5B,OAAO;GACL,IAAI,MAAM;GACV,KAAK,iBAAiB,WAAW,UAAU,MAAM;GAClD;SACK;EACN,OAAO;;;AAIX,SAAgB,wBAAwB,YAA2C;CACjF,MAAM,yBAAS,IAAI,KAAqB;CAExC,KAAK,MAAM,SAAS,WAAW,SAAS,4DAAQ,EAC9C,OAAO,IAAI,MAAM,GAAG,aAAa,EAAE,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,GAAG;CAE5E,OAAO,oBAAoB,OAAO,IAAI,MAAM,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,IAAI,KAAK,IAAI,GAAG;;;;ACpC/F,MAAM,6BAAa,IAAI,KAAwB;AAM/C,eAAsB,eACpB,IACA,SAC2B;CAC3B,MAAM,MAAM,GAAG,GAAG,GAAG,gBAAgB,QAAQ,KAAK;CAClD,IAAI,QAAQ,OAAO;EACjB,MAAM,SAAS,WAAW,IAAI,IAAI;EAClC,IAAI,QAAQ,OAAO;EACnB,MAAM,OAAO,MAAM,gBAAgB,KAAK,QAAQ,SAAS;EACzD,IAAI,MAAM;GACR,WAAW,IAAI,KAAK,KAAK;GACzB,OAAO;;;CAIX,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,UAAU,iBAAiB,WAAW,OAAO,EAAE,QAAQ,QAAQ;CACrE,MAAM,WAAW,IAAI,IAAI,iDAAiD;CAC1E,SAAS,aAAa,IAAI,MAAM,GAAG;CACnC,SAAS,aAAa,IAAI,QAAQ,QAAQ,KAAK;CAC/C,SAAS,aAAa,IAAI,SAAS,uBAAuB,GAAG,CAAC;CAE9D,IAAI;EACF,MAAM,WAAW,MAAM,MAAM,UAAU;GACrC,SAAS,EAAE,QAAQ,oBAAoB;GACvC,QAAQ,WAAW;GACpB,CAAC;EACF,IAAI,CAAC,SAAS,IAAI,OAAO;EACzB,MAAM,OAAgB,MAAM,SAAS,MAAM;EAC3C,IAAI,CAAC,YAAY,KAAK,EAAE,OAAO;EAC/B,IAAI,QAAQ,OAAO;GACjB,WAAW,IAAI,KAAK,KAAK;GACzB,MAAM,iBAAiB,KAAK,MAAM,QAAQ,SAAS;;EAErD,OAAO;SACD;EACN,OAAO;WACC;EACR,aAAa,QAAQ;;;AAIzB,eAAsB,uBACpB,IACA,MACA,SACsB;CACtB,MAAM,SAAsB,EAAE,OAAO,EAAE,EAAE;CACzC,MAAM,YAAY,KAAK,KAAK,yBAAyB,QAAQ,uBAAuB,UAAU;CAC9F,IAAI,WACF,OAAO,SAAS,MAAM,cAAc,WAAW,GAAG,GAAG,UAAU,QAAQ;CAGzE,MAAM,QAAQ,KAAK,gBAAgB,KAAK,UAAU,SAAS,EAAE;CAC7D,KAAK,MAAM,CAAC,OAAO,SAAS,MAAM,SAAS,EAAE;EAC3C,IAAI,KAAK,QAAQ,KAAK,SAAS,SAAS;EACxC,IAAI,CAAC,KAAK,iBAAiB;EAC3B,MAAM,MAAM,MAAM,cAAc,KAAK,iBAAiB,GAAG,GAAG,SAAS,QAAQ,KAAK,QAAQ;EAC1F,IAAI,KAAK,OAAO,MAAM,KAAK,YAAY,KAAK,KAAK,CAAC;;CAEpD,OAAO;;AAGT,eAAe,cACb,QACA,UACA,SAC6B;CAC7B,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,IAAI,OAAO;SACf;EACN;;CAEF,IAAI,IAAI,aAAa,YAAY,IAAI,SAAS,aAAa,KAAK,iBAC9D;CAIF,MAAM,WAAW,GAAG,WADF,iBAAiB,IACK;CACxC,MAAM,SAAS,KAAK,KAAK,QAAQ,gBAAgB,SAAS;CAC1D,IAAI;EACF,MAAM,OAAO,OAAO;EACpB,OAAO,eAAe,QAAQ,iBAAiB,SAAS;SAClD;CAIR,IAAI;EACF,MAAM,WAAW,MAAM,MAAM,KAAK,EAAE,SAAS,EAAE,QAAQ,WAAW,EAAE,CAAC;EACrE,IAAI,CAAC,SAAS,IAAI,OAAO,KAAA;EACzB,MAAM,MAAM,QAAQ,gBAAgB,EAAE,WAAW,MAAM,CAAC;EACxD,MAAM,UAAU,QAAQ,IAAI,WAAW,MAAM,SAAS,aAAa,CAAC,CAAC;EACrE,OAAO,eAAe,QAAQ,iBAAiB,SAAS;SAClD;EACN;;;AAIJ,eAAe,gBAAgB,KAAa,WAA8C;CACxF,IAAI;EACF,MAAM,OAAgB,KAAK,MAAM,MAAM,SAAS,KAAK,KAAK,WAAW,GAAG,IAAI,OAAO,EAAE,OAAO,CAAC;EAC7F,OAAO,YAAY,KAAK,GAAG,OAAO;SAC5B;EACN,OAAO;;;AAIX,eAAe,iBAAiB,KAAa,MAAiB,WAAkC;CAC9F,IAAI;EACF,MAAM,MAAM,WAAW,EAAE,WAAW,MAAM,CAAC;EAC3C,MAAM,UAAU,KAAK,KAAK,WAAW,GAAG,IAAI,OAAO,EAAE,GAAG,KAAK,UAAU,KAAK,CAAC,IAAI;SAC3E;;AAKV,SAAS,YAAY,MAAkC;CACrD,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,QAAQ;CACd,OACE,OAAO,MAAM,SAAS,YACtB,QAAQ,MAAM,KAAK,IACnB,OAAO,MAAM,MAAM,SAAS,YAC5B,OAAO,MAAM,KAAK,gBAAgB;;AAItC,SAAS,iBAAiB,KAAkB;CAC1C,MAAM,QAAQ,IAAI,SAAS,MAAM,2BAA2B;CAC5D,OAAO,QAAQ,IAAI,MAAM,GAAG,aAAa,CAAC,QAAQ,QAAQ,MAAM,KAAK;;AAGvE,SAAS,eAAe,QAAgB,UAA0B;CAChE,OAAO,GAAG,OAAO,QAAQ,OAAO,GAAG,CAAC,GAAG;;AAGzC,SAAS,gBAAgB,OAAuB;CAC9C,OAAO,MAAM,WAAW,mBAAmB,IAAI;;AAGjD,SAAS,YAAY,KAAa,OAAiD;CACjF,OAAO;EACL;EACA,KAAK,MAAM;EACX,OAAO,MAAM,eAAe;EAC5B,QAAQ,MAAM,eAAe;EAC9B;;;;AC5JH,SAAgB,mBACd,WACA,MACA,QACA,SACQ;CACR,MAAM,UAAU,iBAAiB,mBAAmB,KAAK,KAAK,YAAY;CAC1E,MAAM,SAAS,WAAW,KAAK,KAAK,KAAK;CACzC,MAAM,SAAS,WAAW,KAAK,KAAK,YAAY;CAChD,MAAM,SAAS,OAAO,SAClB,sCAAsC,gBAAgB,OAAO,OAAO,CAAC,oEACrE;CACJ,MAAM,QAAQ,YAAY,OAAO;CACjC,MAAM,SAAS,aAAa,WAAW,KAAK,YAAY,QAAQ,KAAK;CAErE,OAAO;EACL;EACA;EACA,sCAAsC,gBAAgB,QAAQ,CAAC;EAC/D;EACA,uCAAuC,OAAO;EAC9C,0CAA0C,OAAO;EACjD;EACA,+BAA+B,gBAAgB,KAAK,CAAC;EACrD;EACA;EACA;EACD,CAAC,KAAK,GAAG;;AAGZ,SAAgB,gBAAgB,MAAyB;CACvD,MAAM,CAAC,OAAO,OAAO,KAAK,sBAAsB,CAAC,GAAG,KAAK,KAAK,OAAO;CACrE,MAAM,WAAW,gBAAgB,KAAK,CACnC,QAAQ,WAAW,WAAW,OAAO,SAAS,OAAO,IAAI,CAAC,CAC1D,MAAM,MAAM,UAAU,KAAK,QAAS,KAAK,MAAM,QAAS,GAAG;CAE9D,IAAI,SAAS;CACb,IAAI,SAAS;CACb,KAAK,MAAM,UAAU,UAAU;EAC7B,MAAM,CAAC,aAAa,aAAa,OAAO;EACxC,IAAI,cAAc,QAAQ;EAC1B,UAAU,WAAW,KAAK,KAAK,MAAM,QAAQ,YAAY,CAAC;EAC1D,IAAI,OAAO,SAAS,OAAO;GACzB,MAAM,OAAO,OAAO,gBAAgB,OAAO;GAC3C,MAAM,QAAQ,OAAO,eAAe;GACpC,UAAU,YAAY,gBAAgB,KAAK,CAAC,8CAA8C,WAAW,MAAM,CAAC;;EAE9G,SAAS;;CAEX,UAAU,WAAW,KAAK,KAAK,MAAM,QAAQ,IAAI,CAAC;CAClD,OAAO,OAAO,MAAM;;AAGtB,SAAS,gBAAgB,MAAiE;CACxF,OAAO,CACL,IAAI,KAAK,UAAU,QAAQ,EAAE,EAAE,KAAK,YAAY;EAAE,GAAG;EAAQ,MAAM;EAAgB,EAAE,EACrF,IAAI,KAAK,UAAU,SAAS,EAAE,EAAE,KAAK,YAAY;EAAE,GAAG;EAAQ,MAAM;EAAkB,EAAE,CACzF;;AAGH,SAAS,WACP,SACA,OACA,KAC6B;CAC7B,OAAO,QAAQ,WAAW,QAAQ,MAAM,SAAS,QAAQ,MAAM,OAAO,QAAQ,KAAK,QAAQ,GAAG;;AAGhG,SAAS,YAAY,QAA6B;CAChD,IAAI,OAAO,MAAM,WAAW,GAAG,OAAO;CACtC,MAAM,SAAS,OAAO,MACnB,KAAK,SAAS;EACb,MAAM,OAAO,CACX,KAAK,QAAQ,WAAW,KAAK,MAAM,KAAK,IACxC,KAAK,SAAS,YAAY,KAAK,OAAO,KAAK,GAC5C,CAAC,KAAK,GAAG;EACV,OAAO,0CAA0C,gBAAgB,KAAK,IAAI,CAAC,SAAS,gBAAgB,KAAK,OAAO,GAAG,CAAC,GAAG,KAAK;GAC5H,CACD,KAAK,GAAG;CACX,OAAO,4CAA4C,OAAO,MAAM,OAAO,IAAI,OAAO;;AAGpF,SAAS,aAAa,WAAmB,WAA+B,MAAsB;CAC5F,IAAI,CAAC,WACH,OAAO,yEAAyE,gBAAgB,UAAU,CAAC;CAE7G,MAAM,OAAO,IAAI,KAAK,UAAU;CAChC,IAAI,OAAO,MAAM,KAAK,SAAS,CAAC,EAAE,OAAO,aAAa,WAAW,KAAA,GAAW,KAAK;CACjF,MAAM,MAAM,KAAK,aAAa;CAC9B,IAAI;CACJ,IAAI;EACF,QAAQ,IAAI,KAAK,eAAe,MAAM;GAAE,WAAW;GAAU,UAAU;GAAO,CAAC,CAAC,OAAO,KAAK;SACtF;EACN,QAAQ,IAAI,KAAK,eAAe,MAAM;GAAE,WAAW;GAAU,UAAU;GAAO,CAAC,CAAC,OAAO,KAAK;;CAE9F,OAAO,yEAAyE,gBAAgB,UAAU,CAAC,8DAA8D,IAAI,IAAI,WAAW,MAAM,CAAC;;AAGrM,SAAS,WAAW,OAAuB;CACzC,OAAO,WAAW,MAAM,CAAC,WAAW,MAAM,OAAO;;AAGnD,SAAS,gBAAgB,OAAuB;CAC9C,OAAO,WAAW,MAAM,CAAC,WAAW,KAAK,QAAQ;;AAGnD,SAAS,WAAW,OAAuB;CACzC,OAAO,MACJ,WAAW,KAAK,QAAQ,CACxB,WAAW,KAAK,OAAO,CACvB,WAAW,KAAK,OAAO,CACvB,WAAW,MAAK,SAAS,CACzB,WAAW,KAAK,QAAQ;;;;AC5G7B,MAAM,gBAAgB;AAEtB,SAAgB,2BACd,SAC6B;CAC7B,OAAO;EACL,OAAO,QAAQ,SAAS;EACxB,MAAM,QAAQ,QAAQ;EACtB,SAAS,QAAQ,WAAW;EAC5B,OAAO,QAAQ,SAAS;EACxB,UAAU,KAAK,QAAQ,QAAQ,YAAY,4BAA4B;EACvE,gBAAgB,KAAK,QAAQ,QAAQ,kBAAkB,4BAA4B;EACnF,iBAAiB,QAAQ,mBAAmB;EAC7C;;AAGH,eAAsB,uBACpB,MACA,SACiB;CACjB,MAAM,WAAW,2BAA2B,QAAQ;CACpD,IAAI,CAAC,SAAS,OAAO,OAAO;CAE5B,IAAI,SAAS;CACb,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,KAAK,SAAS,cAAc,EAAE;EAChD,MAAM,QAAQ,MAAM,SAAS;EAC7B,UAAU,KAAK,MAAM,QAAQ,MAAM;EACnC,MAAM,YAAY,wBAAwB,MAAM,GAAG;EACnD,IAAI,CAAC,WAAW;GACd,UAAU,MAAM;GAChB,SAAS,QAAQ,MAAM,GAAG;GAC1B;;EAGF,MAAM,OAAO,MAAM,eAAe,UAAU,IAAI,SAAS;EACzD,IAAI,CAAC,MAAM;GACT,UAAU,MAAM;GAChB,SAAS,QAAQ,MAAM,GAAG;GAC1B;;EAGF,MAAM,SAAS,MAAM,uBAAuB,UAAU,IAAI,MAAM,SAAS;EACzE,UAAU,mBAAmB,UAAU,KAAK,MAAM,QAAQ,SAAS;EACnE,SAAS,QAAQ,MAAM,GAAG;;CAE5B,OAAO,SAAS,KAAK,MAAM,OAAO;;;;ACfpC,eAAsB,qBACpB,MACA,SACiB;CACjB,IAAI,CAAC,qBAAqB,QAAQ,IAAI,CAAC,eAAe,KAAK,EACzD,OAAO;CAGT,IAAI,SAAS;CACb,IAAI,OAAO,QAAQ,YAAY,UAC7B,SAAS,MAAM,uBAAuB,QAAQ,QAAQ,QAAQ;CAEhE,IAAI,CAAC,eAAe,OAAO,EAAE,OAAO;CAGpC,QAAO,MADW,kBAAkB,EACzB,qBAAqB,QAAQ;EACtC,SAAS,QAAQ;EACjB,YAAY,QAAQ;EACpB,SAAS,QAAQ,QAAQ,QAAQ;EACjC,SAAS,QAAQ;EACjB,cAAc,QAAQ;EACvB,CAAC;;AAGJ,SAAS,qBAAqB,SAAqC;CACjE,OAAO,QACL,QAAQ,WACR,QAAQ,cACR,QAAQ,WACR,QAAQ,WACR,QAAQ,aACT;;AAGH,SAAS,eAAe,MAAuB;CAC7C,OAAO,gEAAgE,KAAK,KAAK"}
|
package/dist/media2.mjs
CHANGED
|
@@ -1,22 +1,2 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
|
|
3
|
-
async function transformMediaEmbeds(html, options) {
|
|
4
|
-
if (!hasEnabledMediaEmbed(options) || !hasMediaMarker(html)) return html;
|
|
5
|
-
return (await importNapiModule()).transformMediaEmbeds(html, {
|
|
6
|
-
spotify: options.spotify,
|
|
7
|
-
stackBlitz: options.stackBlitz,
|
|
8
|
-
twitter: options.twitter,
|
|
9
|
-
bluesky: options.bluesky,
|
|
10
|
-
webContainer: options.webContainer
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
function hasEnabledMediaEmbed(options) {
|
|
14
|
-
return Boolean(options.spotify || options.stackBlitz || options.twitter || options.bluesky || options.webContainer);
|
|
15
|
-
}
|
|
16
|
-
function hasMediaMarker(html) {
|
|
17
|
-
return /<(spotify|stackblitz|tweet|xpost|bluesky|webcontainer)[\s/>]/i.test(html);
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
export { transformMediaEmbeds as t };
|
|
21
|
-
|
|
22
|
-
//# sourceMappingURL=media2.mjs.map
|
|
1
|
+
import { t as transformMediaEmbeds } from "./media.mjs";
|
|
2
|
+
export { transformMediaEmbeds };
|
package/dist/vitepress-cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { i as generateVitePressMigrationConfig } from "./vitepress.mjs";
|
|
3
|
-
import * as path from "node:path";
|
|
3
|
+
import * as path$1 from "node:path";
|
|
4
4
|
import { access, mkdir, writeFile } from "node:fs/promises";
|
|
5
5
|
import { pathToFileURL } from "node:url";
|
|
6
6
|
//#region src/vitepress-cli-runtime.ts
|
|
@@ -30,9 +30,9 @@ async function runVitePressMigrationCli(runtime = createVitePressMigrationCliRun
|
|
|
30
30
|
}
|
|
31
31
|
const outPath = resolvePath(cwd, args.out);
|
|
32
32
|
if (!args.force && await fileExists(outPath)) throw new Error(`Refusing to overwrite existing file: ${outPath}. Pass --force to overwrite.`);
|
|
33
|
-
await mkdir(path.dirname(outPath), { recursive: true });
|
|
33
|
+
await mkdir(path$1.dirname(outPath), { recursive: true });
|
|
34
34
|
await writeFile(outPath, source);
|
|
35
|
-
await runtime.writeStdout(`Wrote ${path.relative(cwd, outPath) || outPath}\n`);
|
|
35
|
+
await runtime.writeStdout(`Wrote ${path$1.relative(cwd, outPath) || outPath}\n`);
|
|
36
36
|
}
|
|
37
37
|
function createVitePressMigrationCliRuntime() {
|
|
38
38
|
const globals = globalThis;
|
|
@@ -155,7 +155,7 @@ function createConfigEnv() {
|
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
157
|
function resolvePath(cwd, value) {
|
|
158
|
-
return path.isAbsolute(value) ? path.normalize(value) : path.resolve(cwd, value);
|
|
158
|
+
return path$1.isAbsolute(value) ? path$1.normalize(value) : path$1.resolve(cwd, value);
|
|
159
159
|
}
|
|
160
160
|
function helpText() {
|
|
161
161
|
return `ox-content-migrate-vitepress [config]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitepress-cli.mjs","names":[],"sources":["../src/vitepress-cli-runtime.ts","../src/vitepress-cli.ts"],"sourcesContent":["import { access, mkdir, writeFile } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { OxContentOptions } from \"./types\";\nimport { generateVitePressMigrationConfig, type VitePressConfig } from \"./vitepress\";\n\ninterface RuntimeGlobals {\n Deno?: {\n args: string[];\n cwd(): string;\n exit(code?: number): never;\n stderr: {\n write(data: Uint8Array): Promise<number>;\n };\n stdout: {\n write(data: Uint8Array): Promise<number>;\n };\n };\n Bun?: unknown;\n process?: NodeJS.Process;\n}\n\nexport type VitePressMigrationCliRuntimeName = \"node\" | \"deno\" | \"bun\";\n\nexport interface VitePressMigrationCliRuntime {\n name: VitePressMigrationCliRuntimeName;\n argv: string[];\n cwd(): string;\n writeStdout(value: string): void | Promise<void>;\n writeStderr(value: string): void | Promise<void>;\n setExitCode(code: number): void;\n}\n\ninterface CliOptions {\n configPath?: string;\n out?: string;\n srcDir?: string;\n outDir?: string;\n force: boolean;\n help: boolean;\n}\n\ninterface ConfigEnv {\n command: \"build\" | \"serve\";\n mode: string;\n isSsrBuild: boolean;\n isPreview: boolean;\n}\n\nconst DEFAULT_CONFIG_FILES = [\n \".vitepress/config.ts\",\n \".vitepress/config.mts\",\n \".vitepress/config.js\",\n \".vitepress/config.mjs\",\n \".vitepress/config.cts\",\n \".vitepress/config.cjs\",\n];\n\nconst textEncoder = new TextEncoder();\n\nexport async function runVitePressMigrationCli(\n runtime = createVitePressMigrationCliRuntime(),\n): Promise<void> {\n const args = parseVitePressMigrationCliArgs(runtime.argv);\n\n if (args.help) {\n await runtime.writeStdout(helpText());\n return;\n }\n\n const cwd = runtime.cwd();\n const configPath = await resolveConfigPath(args.configPath, cwd);\n const config = await loadVitePressConfig(configPath, cwd, runtime.name);\n const overrides: OxContentOptions = {\n ...(args.srcDir ? { srcDir: args.srcDir } : {}),\n ...(args.outDir ? { outDir: args.outDir } : {}),\n };\n const source = generateVitePressMigrationConfig(config, overrides);\n\n if (!args.out) {\n await runtime.writeStdout(source);\n return;\n }\n\n const outPath = resolvePath(cwd, args.out);\n if (!args.force && (await fileExists(outPath))) {\n throw new Error(`Refusing to overwrite existing file: ${outPath}. Pass --force to overwrite.`);\n }\n\n await mkdir(path.dirname(outPath), { recursive: true });\n await writeFile(outPath, source);\n await runtime.writeStdout(`Wrote ${path.relative(cwd, outPath) || outPath}\\n`);\n}\n\nexport function createVitePressMigrationCliRuntime(): VitePressMigrationCliRuntime {\n const globals = globalThis as typeof globalThis & RuntimeGlobals;\n const deno = globals.Deno;\n const process = globals.process;\n\n if (deno) {\n return {\n name: \"deno\",\n argv: deno.args,\n cwd: () => deno.cwd(),\n writeStdout: async (value) => {\n await deno.stdout.write(textEncoder.encode(value));\n },\n writeStderr: async (value) => {\n await deno.stderr.write(textEncoder.encode(value));\n },\n setExitCode: (code) => {\n deno.exit(code);\n },\n };\n }\n\n if (!process) {\n throw new Error(\"Could not detect a supported JavaScript runtime.\");\n }\n\n return {\n name: globals.Bun ? \"bun\" : \"node\",\n argv: process.argv.slice(2),\n cwd: () => process.cwd(),\n writeStdout: (value) => {\n process.stdout.write(value);\n },\n writeStderr: (value) => {\n process.stderr.write(value);\n },\n setExitCode: (code) => {\n process.exitCode = code;\n },\n };\n}\n\nexport function parseVitePressMigrationCliArgs(argv: string[]): CliOptions {\n const options: CliOptions = {\n force: false,\n help: false,\n };\n\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index];\n\n if (arg === \"--help\" || arg === \"-h\") {\n options.help = true;\n continue;\n }\n\n if (arg === \"--force\" || arg === \"-f\") {\n options.force = true;\n continue;\n }\n\n if (arg === \"--out\" || arg === \"-o\") {\n options.out = readOptionValue(argv, ++index, arg);\n continue;\n }\n\n if (arg === \"--src-dir\") {\n options.srcDir = readOptionValue(argv, ++index, arg);\n continue;\n }\n\n if (arg === \"--out-dir\") {\n options.outDir = readOptionValue(argv, ++index, arg);\n continue;\n }\n\n if (arg.startsWith(\"-\")) {\n throw new Error(`Unknown option: ${arg}`);\n }\n\n if (options.configPath) {\n throw new Error(`Unexpected positional argument: ${arg}`);\n }\n\n options.configPath = arg;\n }\n\n return options;\n}\n\nfunction readOptionValue(argv: string[], index: number, option: string): string {\n const value = argv[index];\n if (!value || value.startsWith(\"-\")) {\n throw new Error(`Missing value for ${option}`);\n }\n return value;\n}\n\nasync function resolveConfigPath(configPath: string | undefined, cwd: string): Promise<string> {\n if (configPath) {\n return resolvePath(cwd, configPath);\n }\n\n for (const candidate of DEFAULT_CONFIG_FILES) {\n const resolved = resolvePath(cwd, candidate);\n if (await fileExists(resolved)) {\n return resolved;\n }\n }\n\n throw new Error(\n `Could not find a VitePress config. Pass one explicitly, e.g. ${DEFAULT_CONFIG_FILES[0]}`,\n );\n}\n\nasync function loadVitePressConfig(\n configPath: string,\n cwd: string,\n runtime: VitePressMigrationCliRuntimeName,\n): Promise<VitePressConfig> {\n const loaders =\n runtime === \"deno\" || runtime === \"bun\"\n ? [loadConfigByNativeImport, loadConfigWithVite]\n : [loadConfigWithVite, loadConfigByNativeImport];\n const errors: string[] = [];\n\n for (const load of loaders) {\n try {\n return await load(configPath, cwd);\n } catch (error) {\n errors.push(error instanceof Error ? error.message : String(error));\n }\n }\n\n throw new Error(\n `Could not load VitePress config: ${configPath}\\n${errors.map((error) => `- ${error}`).join(\"\\n\")}`,\n );\n}\n\nasync function loadConfigWithVite(configPath: string, cwd: string): Promise<VitePressConfig> {\n const vite = (await import(\"vite\")) as {\n loadConfigFromFile(\n env: ConfigEnv,\n configFile?: string,\n configRoot?: string,\n logLevel?: \"silent\",\n ): Promise<{ config: unknown } | null>;\n };\n const loaded = await vite.loadConfigFromFile(createConfigEnv(), configPath, cwd, \"silent\");\n\n return normalizeLoadedConfig(loaded?.config, configPath);\n}\n\nasync function loadConfigByNativeImport(configPath: string): Promise<VitePressConfig> {\n const url = pathToFileURL(configPath);\n url.searchParams.set(\"mtime\", String(Date.now()));\n const module = (await import(url.href)) as { default?: unknown };\n\n return normalizeLoadedConfig(module.default ?? module, configPath);\n}\n\nasync function normalizeLoadedConfig(value: unknown, configPath: string): Promise<VitePressConfig> {\n const config = typeof value === \"function\" ? await value(createConfigEnv()) : await value;\n\n if (!config || typeof config !== \"object\" || Array.isArray(config)) {\n throw new Error(`VitePress config did not export an object: ${configPath}`);\n }\n\n return config as VitePressConfig;\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction createConfigEnv(): ConfigEnv {\n return {\n command: \"build\",\n mode: \"production\",\n isSsrBuild: false,\n isPreview: false,\n };\n}\n\nfunction resolvePath(cwd: string, value: string): string {\n return path.isAbsolute(value) ? path.normalize(value) : path.resolve(cwd, value);\n}\n\nfunction helpText(): string {\n return `ox-content-migrate-vitepress [config]\n\nGenerate an editable ox-content options object from a VitePress config.\n\nOptions:\n -o, --out <file> Write the generated TypeScript module to a file.\n --src-dir <dir> Add/override the ox-content srcDir option.\n --out-dir <dir> Add/override the ox-content outDir option.\n -f, --force Overwrite --out when the file already exists.\n -h, --help Show this help.\n\nWhen --out is omitted, the generated module is printed to stdout.\n`;\n}\n","#!/usr/bin/env node\nimport {\n createVitePressMigrationCliRuntime,\n runVitePressMigrationCli,\n} from \"./vitepress-cli-runtime\";\n\nconst runtime = createVitePressMigrationCliRuntime();\n\nrunVitePressMigrationCli(runtime).catch(async (error) => {\n await runtime.writeStderr(`${error instanceof Error ? error.message : String(error)}\\n`);\n runtime.setExitCode(1);\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vitepress-cli.mjs","names":["path"],"sources":["../src/vitepress-cli-runtime.ts","../src/vitepress-cli.ts"],"sourcesContent":["import { access, mkdir, writeFile } from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { OxContentOptions } from \"./types\";\nimport { generateVitePressMigrationConfig, type VitePressConfig } from \"./vitepress\";\n\ninterface RuntimeGlobals {\n Deno?: {\n args: string[];\n cwd(): string;\n exit(code?: number): never;\n stderr: {\n write(data: Uint8Array): Promise<number>;\n };\n stdout: {\n write(data: Uint8Array): Promise<number>;\n };\n };\n Bun?: unknown;\n process?: NodeJS.Process;\n}\n\nexport type VitePressMigrationCliRuntimeName = \"node\" | \"deno\" | \"bun\";\n\nexport interface VitePressMigrationCliRuntime {\n name: VitePressMigrationCliRuntimeName;\n argv: string[];\n cwd(): string;\n writeStdout(value: string): void | Promise<void>;\n writeStderr(value: string): void | Promise<void>;\n setExitCode(code: number): void;\n}\n\ninterface CliOptions {\n configPath?: string;\n out?: string;\n srcDir?: string;\n outDir?: string;\n force: boolean;\n help: boolean;\n}\n\ninterface ConfigEnv {\n command: \"build\" | \"serve\";\n mode: string;\n isSsrBuild: boolean;\n isPreview: boolean;\n}\n\nconst DEFAULT_CONFIG_FILES = [\n \".vitepress/config.ts\",\n \".vitepress/config.mts\",\n \".vitepress/config.js\",\n \".vitepress/config.mjs\",\n \".vitepress/config.cts\",\n \".vitepress/config.cjs\",\n];\n\nconst textEncoder = new TextEncoder();\n\nexport async function runVitePressMigrationCli(\n runtime = createVitePressMigrationCliRuntime(),\n): Promise<void> {\n const args = parseVitePressMigrationCliArgs(runtime.argv);\n\n if (args.help) {\n await runtime.writeStdout(helpText());\n return;\n }\n\n const cwd = runtime.cwd();\n const configPath = await resolveConfigPath(args.configPath, cwd);\n const config = await loadVitePressConfig(configPath, cwd, runtime.name);\n const overrides: OxContentOptions = {\n ...(args.srcDir ? { srcDir: args.srcDir } : {}),\n ...(args.outDir ? { outDir: args.outDir } : {}),\n };\n const source = generateVitePressMigrationConfig(config, overrides);\n\n if (!args.out) {\n await runtime.writeStdout(source);\n return;\n }\n\n const outPath = resolvePath(cwd, args.out);\n if (!args.force && (await fileExists(outPath))) {\n throw new Error(`Refusing to overwrite existing file: ${outPath}. Pass --force to overwrite.`);\n }\n\n await mkdir(path.dirname(outPath), { recursive: true });\n await writeFile(outPath, source);\n await runtime.writeStdout(`Wrote ${path.relative(cwd, outPath) || outPath}\\n`);\n}\n\nexport function createVitePressMigrationCliRuntime(): VitePressMigrationCliRuntime {\n const globals = globalThis as typeof globalThis & RuntimeGlobals;\n const deno = globals.Deno;\n const process = globals.process;\n\n if (deno) {\n return {\n name: \"deno\",\n argv: deno.args,\n cwd: () => deno.cwd(),\n writeStdout: async (value) => {\n await deno.stdout.write(textEncoder.encode(value));\n },\n writeStderr: async (value) => {\n await deno.stderr.write(textEncoder.encode(value));\n },\n setExitCode: (code) => {\n deno.exit(code);\n },\n };\n }\n\n if (!process) {\n throw new Error(\"Could not detect a supported JavaScript runtime.\");\n }\n\n return {\n name: globals.Bun ? \"bun\" : \"node\",\n argv: process.argv.slice(2),\n cwd: () => process.cwd(),\n writeStdout: (value) => {\n process.stdout.write(value);\n },\n writeStderr: (value) => {\n process.stderr.write(value);\n },\n setExitCode: (code) => {\n process.exitCode = code;\n },\n };\n}\n\nexport function parseVitePressMigrationCliArgs(argv: string[]): CliOptions {\n const options: CliOptions = {\n force: false,\n help: false,\n };\n\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index];\n\n if (arg === \"--help\" || arg === \"-h\") {\n options.help = true;\n continue;\n }\n\n if (arg === \"--force\" || arg === \"-f\") {\n options.force = true;\n continue;\n }\n\n if (arg === \"--out\" || arg === \"-o\") {\n options.out = readOptionValue(argv, ++index, arg);\n continue;\n }\n\n if (arg === \"--src-dir\") {\n options.srcDir = readOptionValue(argv, ++index, arg);\n continue;\n }\n\n if (arg === \"--out-dir\") {\n options.outDir = readOptionValue(argv, ++index, arg);\n continue;\n }\n\n if (arg.startsWith(\"-\")) {\n throw new Error(`Unknown option: ${arg}`);\n }\n\n if (options.configPath) {\n throw new Error(`Unexpected positional argument: ${arg}`);\n }\n\n options.configPath = arg;\n }\n\n return options;\n}\n\nfunction readOptionValue(argv: string[], index: number, option: string): string {\n const value = argv[index];\n if (!value || value.startsWith(\"-\")) {\n throw new Error(`Missing value for ${option}`);\n }\n return value;\n}\n\nasync function resolveConfigPath(configPath: string | undefined, cwd: string): Promise<string> {\n if (configPath) {\n return resolvePath(cwd, configPath);\n }\n\n for (const candidate of DEFAULT_CONFIG_FILES) {\n const resolved = resolvePath(cwd, candidate);\n if (await fileExists(resolved)) {\n return resolved;\n }\n }\n\n throw new Error(\n `Could not find a VitePress config. Pass one explicitly, e.g. ${DEFAULT_CONFIG_FILES[0]}`,\n );\n}\n\nasync function loadVitePressConfig(\n configPath: string,\n cwd: string,\n runtime: VitePressMigrationCliRuntimeName,\n): Promise<VitePressConfig> {\n const loaders =\n runtime === \"deno\" || runtime === \"bun\"\n ? [loadConfigByNativeImport, loadConfigWithVite]\n : [loadConfigWithVite, loadConfigByNativeImport];\n const errors: string[] = [];\n\n for (const load of loaders) {\n try {\n return await load(configPath, cwd);\n } catch (error) {\n errors.push(error instanceof Error ? error.message : String(error));\n }\n }\n\n throw new Error(\n `Could not load VitePress config: ${configPath}\\n${errors.map((error) => `- ${error}`).join(\"\\n\")}`,\n );\n}\n\nasync function loadConfigWithVite(configPath: string, cwd: string): Promise<VitePressConfig> {\n const vite = (await import(\"vite\")) as {\n loadConfigFromFile(\n env: ConfigEnv,\n configFile?: string,\n configRoot?: string,\n logLevel?: \"silent\",\n ): Promise<{ config: unknown } | null>;\n };\n const loaded = await vite.loadConfigFromFile(createConfigEnv(), configPath, cwd, \"silent\");\n\n return normalizeLoadedConfig(loaded?.config, configPath);\n}\n\nasync function loadConfigByNativeImport(configPath: string): Promise<VitePressConfig> {\n const url = pathToFileURL(configPath);\n url.searchParams.set(\"mtime\", String(Date.now()));\n const module = (await import(url.href)) as { default?: unknown };\n\n return normalizeLoadedConfig(module.default ?? module, configPath);\n}\n\nasync function normalizeLoadedConfig(value: unknown, configPath: string): Promise<VitePressConfig> {\n const config = typeof value === \"function\" ? await value(createConfigEnv()) : await value;\n\n if (!config || typeof config !== \"object\" || Array.isArray(config)) {\n throw new Error(`VitePress config did not export an object: ${configPath}`);\n }\n\n return config as VitePressConfig;\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction createConfigEnv(): ConfigEnv {\n return {\n command: \"build\",\n mode: \"production\",\n isSsrBuild: false,\n isPreview: false,\n };\n}\n\nfunction resolvePath(cwd: string, value: string): string {\n return path.isAbsolute(value) ? path.normalize(value) : path.resolve(cwd, value);\n}\n\nfunction helpText(): string {\n return `ox-content-migrate-vitepress [config]\n\nGenerate an editable ox-content options object from a VitePress config.\n\nOptions:\n -o, --out <file> Write the generated TypeScript module to a file.\n --src-dir <dir> Add/override the ox-content srcDir option.\n --out-dir <dir> Add/override the ox-content outDir option.\n -f, --force Overwrite --out when the file already exists.\n -h, --help Show this help.\n\nWhen --out is omitted, the generated module is printed to stdout.\n`;\n}\n","#!/usr/bin/env node\nimport {\n createVitePressMigrationCliRuntime,\n runVitePressMigrationCli,\n} from \"./vitepress-cli-runtime\";\n\nconst runtime = createVitePressMigrationCliRuntime();\n\nrunVitePressMigrationCli(runtime).catch(async (error) => {\n await runtime.writeStderr(`${error instanceof Error ? error.message : String(error)}\\n`);\n runtime.setExitCode(1);\n});\n"],"mappings":"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"}
|
package/dist/vitepress.cjs
CHANGED
|
@@ -15,6 +15,7 @@ const defaultTheme = {
|
|
|
15
15
|
textMuted: "#4f607b",
|
|
16
16
|
border: "#d2dbea",
|
|
17
17
|
codeBackground: "#101a31",
|
|
18
|
+
codeBackgroundTop: "#18264a",
|
|
18
19
|
codeText: "#edf3ff"
|
|
19
20
|
},
|
|
20
21
|
darkColors: {
|
|
@@ -26,6 +27,7 @@ const defaultTheme = {
|
|
|
26
27
|
textMuted: "#8ea0bf",
|
|
27
28
|
border: "#223252",
|
|
28
29
|
codeBackground: "#0a1020",
|
|
30
|
+
codeBackgroundTop: "#0a1020",
|
|
29
31
|
codeText: "#e7f0ff"
|
|
30
32
|
},
|
|
31
33
|
fonts: {
|
|
@@ -114,7 +116,7 @@ function resolveTheme(config) {
|
|
|
114
116
|
current = current.extends;
|
|
115
117
|
}
|
|
116
118
|
if (chain[0] !== defaultTheme && chain[0]?.name !== "default") chain.unshift(defaultTheme);
|
|
117
|
-
const merged = mergeThemes(...chain);
|
|
119
|
+
const merged = mergeThemes(...chain.map(withDerivedCodeBackgroundTop));
|
|
118
120
|
return {
|
|
119
121
|
name: merged.name ?? "custom",
|
|
120
122
|
colors: merged.colors ?? defaultTheme.colors,
|
|
@@ -131,6 +133,20 @@ function resolveTheme(config) {
|
|
|
131
133
|
js: merged.js ?? ""
|
|
132
134
|
};
|
|
133
135
|
}
|
|
136
|
+
function withDerivedCodeBackgroundTop(theme) {
|
|
137
|
+
const derive = (colors) => {
|
|
138
|
+
if (colors?.codeBackground !== void 0 && colors.codeBackgroundTop === void 0) return {
|
|
139
|
+
...colors,
|
|
140
|
+
codeBackgroundTop: colors.codeBackground
|
|
141
|
+
};
|
|
142
|
+
return colors;
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
...theme,
|
|
146
|
+
colors: derive(theme.colors),
|
|
147
|
+
darkColors: derive(theme.darkColors)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
134
150
|
/**
|
|
135
151
|
* Converts resolved theme to the format expected by Rust NAPI.
|
|
136
152
|
*/
|
|
@@ -146,6 +162,7 @@ function themeToNapi(theme) {
|
|
|
146
162
|
textMuted: theme.colors.textMuted,
|
|
147
163
|
border: theme.colors.border,
|
|
148
164
|
codeBackground: theme.colors.codeBackground,
|
|
165
|
+
codeBackgroundTop: theme.colors.codeBackgroundTop,
|
|
149
166
|
codeText: theme.colors.codeText
|
|
150
167
|
} : void 0,
|
|
151
168
|
darkColors: theme.darkColors.primary ? {
|
|
@@ -157,6 +174,7 @@ function themeToNapi(theme) {
|
|
|
157
174
|
textMuted: theme.darkColors.textMuted,
|
|
158
175
|
border: theme.darkColors.border,
|
|
159
176
|
codeBackground: theme.darkColors.codeBackground,
|
|
177
|
+
codeBackgroundTop: theme.darkColors.codeBackgroundTop,
|
|
160
178
|
codeText: theme.darkColors.codeText
|
|
161
179
|
} : void 0,
|
|
162
180
|
fonts: theme.fonts.sans ? {
|
package/dist/vitepress.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitepress.cjs","names":["importNapiModuleSync"],"sources":["../src/theme.ts","../src/vitepress.ts"],"sourcesContent":["/**\n * Theme API for ox-content SSG\n *\n * Provides VitePress-like theming with default theme + customization.\n */\n\n/**\n * Theme color configuration.\n */\nexport interface ThemeColors {\n /** Primary accent color */\n primary?: string;\n /** Primary color on hover */\n primaryHover?: string;\n /** Background color */\n background?: string;\n /** Alternative background color (sidebar, code blocks) */\n backgroundAlt?: string;\n /** Main text color */\n text?: string;\n /** Muted/secondary text color */\n textMuted?: string;\n /** Border color */\n border?: string;\n /** Code block background color */\n codeBackground?: string;\n /** Code block text color */\n codeText?: string;\n}\n\n/**\n * Theme layout configuration.\n */\nexport interface ThemeLayout {\n /** Sidebar width (CSS value, e.g., \"260px\") */\n sidebarWidth?: string;\n /** Header height (CSS value, e.g., \"60px\") */\n headerHeight?: string;\n /** Maximum content width (CSS value, e.g., \"960px\") */\n maxContentWidth?: string;\n}\n\n/**\n * Theme font configuration.\n */\nexport interface ThemeFonts {\n /** Sans-serif font stack */\n sans?: string;\n /** Monospace font stack */\n mono?: string;\n}\n\n/**\n * Entry page theme configuration.\n */\nexport interface ThemeEntryPage {\n /** Landing page presentation mode */\n mode?: \"default\" | \"subtle\";\n}\n\n/**\n * Theme header configuration.\n */\nexport interface ThemeHeader {\n /** Logo image URL */\n logo?: string;\n /** Light mode logo image URL */\n logoLight?: string;\n /** Dark mode logo image URL */\n logoDark?: string;\n /** Whether to render the site name text next to the logo */\n showSiteNameText?: boolean;\n /** Logo width in pixels */\n logoWidth?: number;\n /** Logo height in pixels */\n logoHeight?: number;\n}\n\n/**\n * Theme footer configuration.\n */\nexport interface ThemeFooter {\n /** Footer message (supports HTML) */\n message?: string;\n /** Copyright text (supports HTML) */\n copyright?: string;\n}\n\n/** Custom social link icon. */\nexport type SocialLinkIcon = string | { svg: string };\n\n/** Custom social link. */\nexport interface SocialLink {\n icon: SocialLinkIcon;\n link: string;\n ariaLabel?: string;\n}\n\n/** Legacy social links configuration. */\nexport interface LegacySocialLinks {\n /** GitHub URL */\n github?: string;\n /** Twitter/X URL */\n twitter?: string;\n /** Discord URL */\n discord?: string;\n}\n\n/** Social links configuration. */\nexport type SocialLinks = LegacySocialLinks | SocialLink[];\n\n/**\n * Embedded HTML content for specific positions in the page layout.\n */\nexport interface ThemeEmbed {\n /** Content to embed into <head> */\n head?: string;\n /** Content before header */\n headerBefore?: string;\n /** Content after header */\n headerAfter?: string;\n /** Content before sidebar navigation */\n sidebarBefore?: string;\n /** Content after sidebar navigation */\n sidebarAfter?: string;\n /** Content before main content */\n contentBefore?: string;\n /** Content after main content */\n contentAfter?: string;\n /** Content before footer */\n footerBefore?: string;\n /** Custom footer content (replaces default footer) */\n footer?: string;\n}\n\nexport interface SidebarItem {\n text?: string;\n link?: string;\n items?: SidebarItem[];\n collapsed?: boolean;\n stickyCollapsed?: boolean;\n}\n\n/**\n * Complete theme configuration.\n */\nexport interface ThemeConfig {\n /** Theme name for identification */\n name?: string;\n /** Base theme to extend */\n extends?: ThemeConfig;\n /** Light mode colors (maps to CSS variables) */\n colors?: ThemeColors;\n /** Dark mode colors (maps to CSS variables) */\n darkColors?: ThemeColors;\n /** Font configuration (maps to CSS variables) */\n fonts?: ThemeFonts;\n /** Entry page configuration */\n entryPage?: ThemeEntryPage;\n /** Layout configuration (maps to CSS variables) */\n layout?: ThemeLayout;\n /** Header configuration */\n header?: ThemeHeader;\n /** Footer configuration */\n footer?: ThemeFooter;\n /** Social links configuration */\n socialLinks?: SocialLinks;\n sidebar?: SidebarItem[];\n /** Embedded HTML content at specific positions */\n embed?: ThemeEmbed;\n /** Additional custom CSS */\n css?: string;\n /** Additional custom JavaScript */\n js?: string;\n}\n\n/**\n * Resolved theme configuration (after merging with defaults).\n */\nexport interface ResolvedThemeConfig {\n name: string;\n colors: ThemeColors;\n darkColors: ThemeColors;\n fonts: ThemeFonts;\n entryPage: ThemeEntryPage;\n layout: ThemeLayout;\n header: ThemeHeader;\n footer: ThemeFooter;\n socialLinks: SocialLinks;\n sidebar: SidebarItem[];\n embed: ThemeEmbed;\n css: string;\n js: string;\n}\n\n/**\n * Default theme configuration.\n * Based on the current ox-content SSG styles.\n */\nexport const defaultTheme: ThemeConfig = {\n name: \"default\",\n colors: {\n primary: \"#4f6fae\",\n primaryHover: \"#425f96\",\n background: \"#ffffff\",\n backgroundAlt: \"#f5f7fb\",\n text: \"#131a30\",\n textMuted: \"#4f607b\",\n border: \"#d2dbea\",\n codeBackground: \"#101a31\",\n codeText: \"#edf3ff\",\n },\n darkColors: {\n primary: \"#86a4da\",\n primaryHover: \"#a3bbe8\",\n background: \"#060816\",\n backgroundAlt: \"#0d1528\",\n text: \"#ebf2ff\",\n textMuted: \"#8ea0bf\",\n border: \"#223252\",\n codeBackground: \"#0a1020\",\n codeText: \"#e7f0ff\",\n },\n fonts: {\n sans: '\"IBM Plex Sans\", \"Avenir Next\", \"Segoe UI Variable\", \"Segoe UI\", sans-serif',\n mono: '\"IBM Plex Mono\", \"SFMono-Regular\", Consolas, monospace',\n },\n entryPage: {\n mode: \"default\",\n },\n layout: {\n sidebarWidth: \"260px\",\n headerHeight: \"60px\",\n maxContentWidth: \"960px\",\n },\n header: {\n logo: undefined,\n logoLight: undefined,\n logoDark: undefined,\n showSiteNameText: true,\n logoWidth: 28,\n logoHeight: 28,\n },\n footer: {\n message: undefined,\n copyright: undefined,\n },\n socialLinks: {},\n embed: {},\n css: \"\",\n js: \"\",\n};\n\n/**\n * Deep merge two objects.\n */\nfunction deepMerge<T extends Record<string, unknown>>(target: T, source: Partial<T>): T {\n const result = { ...target };\n\n for (const key of Object.keys(source) as (keyof T)[]) {\n const sourceValue = source[key];\n const targetValue = target[key];\n\n if (\n sourceValue !== undefined &&\n typeof sourceValue === \"object\" &&\n sourceValue !== null &&\n !Array.isArray(sourceValue) &&\n typeof targetValue === \"object\" &&\n targetValue !== null &&\n !Array.isArray(targetValue)\n ) {\n result[key] = deepMerge(\n targetValue as Record<string, unknown>,\n sourceValue as Record<string, unknown>,\n ) as T[keyof T];\n } else if (sourceValue !== undefined) {\n result[key] = sourceValue as T[keyof T];\n }\n }\n\n return result;\n}\n\n/**\n * Defines a theme configuration with type checking.\n *\n * @example\n * ```ts\n * const myTheme = defineTheme({\n * extends: defaultTheme,\n * colors: {\n * primary: '#3498db',\n * },\n * footer: {\n * copyright: '2025 My Company',\n * },\n * });\n * ```\n */\nexport function defineTheme(config: ThemeConfig): ThemeConfig {\n return config;\n}\n\n/**\n * Merges multiple theme configurations.\n * Later themes override earlier ones.\n *\n * @example\n * ```ts\n * const merged = mergeThemes(defaultTheme, customTheme, overrides);\n * ```\n */\nexport function mergeThemes(...themes: ThemeConfig[]): ThemeConfig {\n if (themes.length === 0) {\n return { ...defaultTheme };\n }\n\n let result: ThemeConfig = {};\n\n for (const theme of themes) {\n result = deepMerge(\n result as Record<string, unknown>,\n theme as Record<string, unknown>,\n ) as ThemeConfig;\n }\n\n return result;\n}\n\n/**\n * Resolves a theme configuration by merging with its extends chain and defaults.\n */\nexport function resolveTheme(config?: ThemeConfig): ResolvedThemeConfig {\n if (!config) {\n return resolveTheme(defaultTheme);\n }\n\n // Build the extends chain\n const chain: ThemeConfig[] = [];\n let current: ThemeConfig | undefined = config;\n\n while (current) {\n chain.unshift(current);\n current = current.extends;\n }\n\n // Always start with default theme\n if (chain[0] !== defaultTheme && chain[0]?.name !== \"default\") {\n chain.unshift(defaultTheme);\n }\n\n // Merge all themes in the chain\n const merged = mergeThemes(...chain);\n\n // Return resolved config with all required fields\n return {\n name: merged.name ?? \"custom\",\n colors: merged.colors ?? defaultTheme.colors!,\n darkColors: merged.darkColors ?? defaultTheme.darkColors!,\n fonts: merged.fonts ?? defaultTheme.fonts!,\n entryPage: merged.entryPage ?? defaultTheme.entryPage!,\n layout: merged.layout ?? defaultTheme.layout!,\n header: merged.header ?? defaultTheme.header!,\n footer: merged.footer ?? defaultTheme.footer!,\n socialLinks: merged.socialLinks ?? defaultTheme.socialLinks!,\n sidebar: merged.sidebar ?? [],\n embed: merged.embed ?? {},\n css: merged.css ?? \"\",\n js: merged.js ?? \"\",\n };\n}\n\n/**\n * Converts resolved theme to the format expected by Rust NAPI.\n */\nexport function themeToNapi(theme: ResolvedThemeConfig): NapiThemeConfig {\n const socialLinks = socialLinksToNapi(theme.socialLinks);\n\n return {\n colors: theme.colors.primary\n ? {\n primary: theme.colors.primary,\n primaryHover: theme.colors.primaryHover,\n background: theme.colors.background,\n backgroundAlt: theme.colors.backgroundAlt,\n text: theme.colors.text,\n textMuted: theme.colors.textMuted,\n border: theme.colors.border,\n codeBackground: theme.colors.codeBackground,\n codeText: theme.colors.codeText,\n }\n : undefined,\n darkColors: theme.darkColors.primary\n ? {\n primary: theme.darkColors.primary,\n primaryHover: theme.darkColors.primaryHover,\n background: theme.darkColors.background,\n backgroundAlt: theme.darkColors.backgroundAlt,\n text: theme.darkColors.text,\n textMuted: theme.darkColors.textMuted,\n border: theme.darkColors.border,\n codeBackground: theme.darkColors.codeBackground,\n codeText: theme.darkColors.codeText,\n }\n : undefined,\n fonts: theme.fonts.sans\n ? {\n sans: theme.fonts.sans,\n mono: theme.fonts.mono,\n }\n : undefined,\n entryPage: theme.entryPage.mode\n ? {\n mode: theme.entryPage.mode,\n }\n : undefined,\n layout: theme.layout.sidebarWidth\n ? {\n sidebarWidth: theme.layout.sidebarWidth,\n headerHeight: theme.layout.headerHeight,\n maxContentWidth: theme.layout.maxContentWidth,\n }\n : undefined,\n header:\n theme.header.logo || theme.header.logoLight || theme.header.logoDark\n ? {\n logo: theme.header.logo,\n logoLight: theme.header.logoLight,\n logoDark: theme.header.logoDark,\n showSiteNameText: theme.header.showSiteNameText,\n logoWidth: theme.header.logoWidth,\n logoHeight: theme.header.logoHeight,\n }\n : undefined,\n footer:\n theme.footer.message || theme.footer.copyright\n ? {\n message: theme.footer.message,\n copyright: theme.footer.copyright,\n }\n : undefined,\n socialLinks,\n embed: Object.keys(theme.embed).length > 0 ? theme.embed : undefined,\n css: theme.css || undefined,\n js: theme.js || undefined,\n };\n}\n\nfunction socialLinksToNapi(links: SocialLinks): NapiSocialLinks | undefined {\n if (Array.isArray(links)) {\n const items = links.map((item) => {\n const icon = typeof item.icon === \"string\" ? item.icon : undefined;\n const iconSvg = typeof item.icon === \"object\" ? item.icon.svg : undefined;\n return { icon, iconSvg, link: item.link, ariaLabel: item.ariaLabel };\n });\n return items.length > 0 ? { links: items } : undefined;\n }\n\n return links.github || links.twitter || links.discord\n ? { github: links.github, twitter: links.twitter, discord: links.discord }\n : undefined;\n}\n\n/**\n * NAPI-compatible theme colors type.\n */\nexport interface NapiThemeColors {\n primary?: string;\n primaryHover?: string;\n background?: string;\n backgroundAlt?: string;\n text?: string;\n textMuted?: string;\n border?: string;\n codeBackground?: string;\n codeText?: string;\n}\n\n/**\n * NAPI-compatible theme fonts type.\n */\nexport interface NapiThemeFonts {\n sans?: string;\n mono?: string;\n}\n\n/**\n * NAPI-compatible entry page theme type.\n */\nexport interface NapiThemeEntryPage {\n mode?: \"default\" | \"subtle\";\n}\n\n/**\n * NAPI-compatible theme layout type.\n */\nexport interface NapiThemeLayout {\n sidebarWidth?: string;\n headerHeight?: string;\n maxContentWidth?: string;\n}\n\n/**\n * NAPI-compatible theme header type.\n */\nexport interface NapiThemeHeader {\n logo?: string;\n logoLight?: string;\n logoDark?: string;\n showSiteNameText?: boolean;\n logoWidth?: number;\n logoHeight?: number;\n}\n\n/**\n * NAPI-compatible theme footer type.\n */\nexport interface NapiThemeFooter {\n message?: string;\n copyright?: string;\n}\n\n/**\n * NAPI-compatible social links type.\n */\nexport interface NapiSocialLinks {\n github?: string;\n twitter?: string;\n discord?: string;\n links?: NapiSocialLink[];\n}\n\nexport interface NapiSocialLink {\n icon?: string;\n iconSvg?: string;\n link: string;\n ariaLabel?: string;\n}\n\n/**\n * NAPI-compatible theme embed type.\n */\nexport interface NapiThemeEmbed {\n head?: string;\n headerBefore?: string;\n headerAfter?: string;\n sidebarBefore?: string;\n sidebarAfter?: string;\n contentBefore?: string;\n contentAfter?: string;\n footerBefore?: string;\n footer?: string;\n}\n\n/**\n * NAPI-compatible theme configuration type.\n */\nexport interface NapiThemeConfig {\n colors?: NapiThemeColors;\n darkColors?: NapiThemeColors;\n fonts?: NapiThemeFonts;\n entryPage?: NapiThemeEntryPage;\n layout?: NapiThemeLayout;\n header?: NapiThemeHeader;\n footer?: NapiThemeFooter;\n socialLinks?: NapiSocialLinks;\n embed?: NapiThemeEmbed;\n css?: string;\n js?: string;\n}\n","import { importNapiModuleSync } from \"./napi\";\nimport { defineTheme, mergeThemes, type ThemeConfig } from \"./theme\";\nimport type { OxContentOptions, SsgNavigationGroup, SsgNavigationItem } from \"./types\";\n\nexport interface VitePressLogo {\n light?: string;\n dark?: string;\n src?: string;\n alt?: string;\n}\n\nexport interface VitePressSocialLink {\n icon: string;\n link: string;\n ariaLabel?: string;\n}\n\nexport interface VitePressFooter {\n message?: string;\n copyright?: string;\n}\n\nexport interface VitePressSidebarItem {\n text?: string;\n link?: string;\n items?: VitePressSidebarItem[];\n collapsed?: boolean;\n}\n\nexport type VitePressSidebar = VitePressSidebarItem[] | Record<string, VitePressSidebarItem[]>;\n\nexport interface VitePressNavItem {\n text?: string;\n link?: string;\n items?: VitePressNavItem[];\n activeMatch?: string;\n}\n\nexport interface VitePressThemeConfig {\n siteTitle?: string | false;\n logo?: string | VitePressLogo;\n nav?: VitePressNavItem[];\n sidebar?: VitePressSidebar;\n socialLinks?: VitePressSocialLink[];\n footer?: VitePressFooter;\n search?: {\n placeholder?: string;\n };\n}\n\nexport interface VitePressConfig {\n title?: string;\n description?: string;\n base?: string;\n themeConfig?: VitePressThemeConfig;\n}\n\nexport interface GenerateVitePressMigrationConfigOptions {\n importSource?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isExternalLink(value: string): boolean {\n return /^[a-z][a-z0-9+.-]*:/i.test(value) || value.startsWith(\"//\");\n}\n\nfunction splitLink(value: string): { pathname: string; suffix: string } {\n const match = /^([^?#]*)([?#].*)?$/.exec(value);\n return {\n pathname: match?.[1] ?? value,\n suffix: match?.[2] ?? \"\",\n };\n}\n\nfunction normalizeInternalPath(value: string): string {\n const { pathname } = splitLink(value.trim());\n let normalized = pathname || \"/\";\n\n if (!normalized.startsWith(\"/\")) {\n normalized = `/${normalized}`;\n }\n\n normalized = normalized\n .replace(/\\/index(?:\\.(?:html?|md|markdown))?$/i, \"/\")\n .replace(/\\.(?:html?|md|markdown)$/i, \"\");\n\n if (normalized !== \"/\") {\n normalized = normalized.replace(/\\/+$/, \"\");\n }\n\n return normalized || \"/\";\n}\n\nfunction formatTitle(value: string): string {\n return value\n .replace(/[-_]([a-z])/g, (_, char: string) => ` ${char.toUpperCase()}`)\n .replace(/^[a-z]/, (char) => char.toUpperCase());\n}\n\nfunction titleFromPath(value: string): string {\n const normalized = normalizeInternalPath(value);\n if (normalized === \"/\") {\n return \"Home\";\n }\n\n const segment = normalized.split(\"/\").filter(Boolean).pop() ?? \"Page\";\n return formatTitle(segment);\n}\n\nfunction titleFromSidebarKey(value: string): string {\n const segment = value\n .replace(/^\\/+|\\/+$/g, \"\")\n .split(\"/\")\n .filter(Boolean)\n .pop();\n return formatTitle(segment ?? \"guide\");\n}\n\nfunction toNavigationItem(text: string | undefined, link: string): SsgNavigationItem {\n const title = text?.trim() || titleFromPath(link);\n\n if (isExternalLink(link) || link.startsWith(\"#\")) {\n return { title, href: link };\n }\n\n const { suffix } = splitLink(link);\n const path = normalizeInternalPath(link);\n\n return suffix ? { title, path, href: `${path}${suffix}` } : { title, path };\n}\n\nfunction dedupeNavigationItems(items: SsgNavigationItem[]): SsgNavigationItem[] {\n const seen = new Set<string>();\n const next: SsgNavigationItem[] = [];\n\n for (const item of items) {\n const key = `${item.title}::${item.path ?? \"\"}::${item.href ?? \"\"}`;\n if (seen.has(key)) {\n continue;\n }\n seen.add(key);\n next.push(item);\n }\n\n return next;\n}\n\nfunction dedupeNavigationGroups(groups: SsgNavigationGroup[]): SsgNavigationGroup[] {\n const merged = new Map<string, SsgNavigationItem[]>();\n const orderedTitles: string[] = [];\n\n for (const group of groups) {\n if (group.items.length === 0) {\n continue;\n }\n\n if (!merged.has(group.title)) {\n merged.set(group.title, []);\n orderedTitles.push(group.title);\n }\n\n merged.get(group.title)!.push(...group.items);\n }\n\n return orderedTitles.map((title) => ({\n title,\n items: dedupeNavigationItems(merged.get(title) ?? []),\n }));\n}\n\nfunction collectSidebarLinks(items: VitePressSidebarItem[]): SsgNavigationItem[] {\n const links: SsgNavigationItem[] = [];\n\n for (const item of items) {\n if (item.link) {\n links.push(toNavigationItem(item.text, item.link));\n }\n\n if (item.items?.length) {\n links.push(...collectSidebarLinks(item.items));\n }\n }\n\n return dedupeNavigationItems(links);\n}\n\nfunction sidebarArrayToGroups(\n items: VitePressSidebarItem[],\n fallbackTitle: string,\n): SsgNavigationGroup[] {\n const groups: SsgNavigationGroup[] = [];\n const rootItems: SsgNavigationItem[] = [];\n\n for (const item of items) {\n if (item.link) {\n rootItems.push(toNavigationItem(item.text, item.link));\n }\n\n if (item.items?.length) {\n const children = collectSidebarLinks(item.items);\n if (children.length > 0) {\n groups.push({\n title: item.text?.trim() || fallbackTitle,\n items: children,\n });\n }\n }\n }\n\n if (rootItems.length > 0) {\n groups.unshift({\n title: fallbackTitle,\n items: dedupeNavigationItems(rootItems),\n });\n }\n\n return groups;\n}\n\nfunction collectNavLinks(items: VitePressNavItem[]): SsgNavigationItem[] {\n const links: SsgNavigationItem[] = [];\n\n for (const item of items) {\n if (item.link) {\n links.push(toNavigationItem(item.text, item.link));\n }\n\n if (item.items?.length) {\n links.push(...collectNavLinks(item.items));\n }\n }\n\n return dedupeNavigationItems(links);\n}\n\nfunction resolveLogoSrc(logo: string | VitePressLogo | undefined): string | undefined {\n if (!logo) {\n return undefined;\n }\n\n if (typeof logo === \"string\") {\n return logo;\n }\n\n return logo.light ?? logo.dark ?? logo.src;\n}\n\nfunction normalizeSocialIcon(icon: string): \"github\" | \"twitter\" | \"discord\" | undefined {\n const normalized = icon.trim().toLowerCase();\n\n if (normalized === \"github\") return \"github\";\n if (normalized === \"discord\") return \"discord\";\n if (normalized === \"twitter\" || normalized === \"x\" || normalized === \"x-twitter\") {\n return \"twitter\";\n }\n\n return undefined;\n}\n\nfunction toThemeConfig(themeConfig: VitePressThemeConfig | undefined): ThemeConfig | undefined {\n if (!themeConfig) {\n return undefined;\n }\n\n const logo = resolveLogoSrc(themeConfig.logo);\n const socialLinks = Object.fromEntries(\n (themeConfig.socialLinks ?? [])\n .map((link) => {\n const key = normalizeSocialIcon(link.icon);\n return key ? [key, link.link] : null;\n })\n .filter((entry): entry is [string, string] => entry !== null),\n );\n\n const theme: ThemeConfig = {\n ...(logo\n ? {\n header: {\n logo,\n },\n }\n : {}),\n ...(themeConfig.footer?.message || themeConfig.footer?.copyright\n ? {\n footer: {\n message: themeConfig.footer.message,\n copyright: themeConfig.footer.copyright,\n },\n }\n : {}),\n ...(Object.keys(socialLinks).length > 0\n ? {\n socialLinks,\n }\n : {}),\n };\n\n return logo || Object.keys(socialLinks).length > 0 || themeConfig.footer\n ? defineTheme(theme)\n : undefined;\n}\n\nfunction resolveSiteName(config: VitePressConfig): string | undefined {\n const siteTitle = config.themeConfig?.siteTitle;\n if (typeof siteTitle === \"string\" && siteTitle.trim()) {\n return siteTitle;\n }\n\n return config.title;\n}\n\nfunction mergeOxContentOptions(\n baseOptions: OxContentOptions,\n overrides: OxContentOptions,\n): OxContentOptions {\n const mergedSsg =\n overrides.ssg === false\n ? false\n : {\n ...(typeof baseOptions.ssg === \"object\" ? baseOptions.ssg : {}),\n ...(typeof overrides.ssg === \"object\" ? overrides.ssg : {}),\n theme:\n typeof baseOptions.ssg === \"object\" &&\n typeof overrides.ssg === \"object\" &&\n baseOptions.ssg.theme &&\n overrides.ssg.theme\n ? defineTheme(mergeThemes(baseOptions.ssg.theme, overrides.ssg.theme))\n : typeof overrides.ssg === \"object\" && overrides.ssg.theme\n ? overrides.ssg.theme\n : typeof baseOptions.ssg === \"object\"\n ? baseOptions.ssg.theme\n : undefined,\n };\n\n const mergedSearch =\n overrides.search === false\n ? false\n : typeof overrides.search === \"object\"\n ? {\n ...(typeof baseOptions.search === \"object\" ? baseOptions.search : {}),\n ...overrides.search,\n }\n : baseOptions.search;\n\n return {\n ...baseOptions,\n ...overrides,\n ssg: mergedSsg,\n search: mergedSearch,\n };\n}\n\n/**\n * Converts a VitePress sidebar config into ox-content navigation groups.\n * Nested VitePress items are flattened into the nearest ox-content group.\n */\nexport function convertVitePressSidebar(sidebar: VitePressSidebar): SsgNavigationGroup[] {\n if (Array.isArray(sidebar)) {\n return dedupeNavigationGroups(sidebarArrayToGroups(sidebar, \"Guide\"));\n }\n\n const groups = Object.entries(sidebar).flatMap(([key, items]) =>\n sidebarArrayToGroups(items, titleFromSidebarKey(key)),\n );\n\n return dedupeNavigationGroups(groups);\n}\n\n/**\n * Converts VitePress top navigation into ox-content sidebar groups.\n * This is used as a fallback when no explicit sidebar is defined.\n */\nexport function convertVitePressNav(nav: VitePressNavItem[]): SsgNavigationGroup[] {\n const groups: SsgNavigationGroup[] = [];\n const rootItems: SsgNavigationItem[] = [];\n\n for (const item of nav) {\n if (item.link) {\n rootItems.push(toNavigationItem(item.text, item.link));\n }\n\n if (item.items?.length) {\n const children = collectNavLinks(item.items);\n if (children.length > 0) {\n groups.push({\n title: item.text?.trim() || \"Navigation\",\n items: children,\n });\n }\n }\n }\n\n if (rootItems.length > 0) {\n groups.unshift({\n title: \"Navigation\",\n items: dedupeNavigationItems(rootItems),\n });\n }\n\n return dedupeNavigationGroups(groups);\n}\n\n/**\n * Creates ox-content plugin options from an existing VitePress config.\n */\nexport function fromVitePressConfig(\n config: VitePressConfig,\n overrides: OxContentOptions = {},\n): OxContentOptions {\n const theme = toThemeConfig(config.themeConfig);\n const navigation = config.themeConfig?.sidebar\n ? convertVitePressSidebar(config.themeConfig.sidebar)\n : config.themeConfig?.nav\n ? convertVitePressNav(config.themeConfig.nav)\n : undefined;\n\n const migrated: OxContentOptions = {\n ...(config.base ? { base: config.base } : {}),\n ...(config.themeConfig?.search?.placeholder\n ? {\n search: {\n placeholder: config.themeConfig.search.placeholder,\n },\n }\n : {}),\n ssg: {\n ...(resolveSiteName(config) ? { siteName: resolveSiteName(config) } : {}),\n ...(theme ? { theme } : {}),\n ...(navigation ? { navigation } : {}),\n },\n };\n\n return mergeOxContentOptions(migrated, overrides);\n}\n\n/**\n * Generates a TypeScript module exporting migrated ox-content options.\n *\n * This is used by the migration CLI so users can inspect and edit the resulting\n * object instead of keeping a runtime dependency on their VitePress config.\n */\nexport function generateVitePressMigrationConfig(\n config: VitePressConfig,\n overrides: OxContentOptions = {},\n options: GenerateVitePressMigrationConfigOptions = {},\n): string {\n const importSource = options.importSource ?? \"@ox-content/vite-plugin\";\n const migrated = fromVitePressConfig(config, overrides);\n\n return `import type { OxContentOptions } from ${JSON.stringify(importSource)};\n\nconst config = ${formatTsValue(migrated)} satisfies OxContentOptions;\n\nexport default config;\n`;\n}\n\nfunction formatTsValue(value: unknown, depth = 0): string {\n if (value === undefined) {\n return \"undefined\";\n }\n\n if (value === null || typeof value === \"boolean\" || typeof value === \"number\") {\n return JSON.stringify(value);\n }\n\n if (typeof value === \"string\") {\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return \"[]\";\n }\n\n const indent = \" \".repeat(depth + 1);\n const closingIndent = \" \".repeat(depth);\n return `[\\n${value.map((item) => `${indent}${formatTsValue(item, depth + 1)},`).join(\"\\n\")}\\n${closingIndent}]`;\n }\n\n if (isRecord(value)) {\n const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined);\n if (entries.length === 0) {\n return \"{}\";\n }\n\n const indent = \" \".repeat(depth + 1);\n const closingIndent = \" \".repeat(depth);\n return `{\\n${entries\n .map(\n ([key, entryValue]) =>\n `${indent}${formatObjectKey(key)}: ${formatTsValue(entryValue, depth + 1)},`,\n )\n .join(\"\\n\")}\\n${closingIndent}}`;\n }\n\n return \"undefined\";\n}\n\nfunction formatObjectKey(key: string): string {\n return /^[A-Za-z_$][\\w$]*$/.test(key) ? key : JSON.stringify(key);\n}\n\n/**\n * Normalizes VitePress-specific frontmatter into ox-content's entry-page shape.\n */\nexport function normalizeVitePressFrontmatter(\n frontmatter: Record<string, unknown>,\n): Record<string, unknown> {\n return importNapiModuleSync().normalizeVitePressFrontmatter(frontmatter);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vitepress.cjs","names":["importNapiModuleSync"],"sources":["../src/theme.ts","../src/vitepress.ts"],"sourcesContent":["/**\n * Theme API for ox-content SSG\n *\n * Provides VitePress-like theming with default theme + customization.\n */\n\n/**\n * Theme color configuration.\n */\nexport interface ThemeColors {\n /** Primary accent color */\n primary?: string;\n /** Primary color on hover */\n primaryHover?: string;\n /** Background color */\n background?: string;\n /** Alternative background color (sidebar, code blocks) */\n backgroundAlt?: string;\n /** Main text color */\n text?: string;\n /** Muted/secondary text color */\n textMuted?: string;\n /** Border color */\n border?: string;\n /** Code block background color */\n codeBackground?: string;\n /** Code block gradient color at the top; defaults to `codeBackground` when customized */\n codeBackgroundTop?: string;\n /** Code block text color */\n codeText?: string;\n}\n\n/**\n * Theme layout configuration.\n */\nexport interface ThemeLayout {\n /** Sidebar width (CSS value, e.g., \"260px\") */\n sidebarWidth?: string;\n /** Header height (CSS value, e.g., \"60px\") */\n headerHeight?: string;\n /** Maximum content width (CSS value, e.g., \"960px\") */\n maxContentWidth?: string;\n}\n\n/**\n * Theme font configuration.\n */\nexport interface ThemeFonts {\n /** Sans-serif font stack */\n sans?: string;\n /** Monospace font stack */\n mono?: string;\n}\n\n/**\n * Entry page theme configuration.\n */\nexport interface ThemeEntryPage {\n /** Landing page presentation mode */\n mode?: \"default\" | \"subtle\";\n}\n\n/**\n * Theme header configuration.\n */\nexport interface ThemeHeader {\n /** Logo image URL */\n logo?: string;\n /** Light mode logo image URL */\n logoLight?: string;\n /** Dark mode logo image URL */\n logoDark?: string;\n /** Whether to render the site name text next to the logo */\n showSiteNameText?: boolean;\n /** Logo width in pixels */\n logoWidth?: number;\n /** Logo height in pixels */\n logoHeight?: number;\n}\n\n/**\n * Theme footer configuration.\n */\nexport interface ThemeFooter {\n /** Footer message (supports HTML) */\n message?: string;\n /** Copyright text (supports HTML) */\n copyright?: string;\n}\n\n/** Custom social link icon. */\nexport type SocialLinkIcon = string | { svg: string };\n\n/** Custom social link. */\nexport interface SocialLink {\n icon: SocialLinkIcon;\n link: string;\n ariaLabel?: string;\n}\n\n/** Legacy social links configuration. */\nexport interface LegacySocialLinks {\n /** GitHub URL */\n github?: string;\n /** Twitter/X URL */\n twitter?: string;\n /** Discord URL */\n discord?: string;\n}\n\n/** Social links configuration. */\nexport type SocialLinks = LegacySocialLinks | SocialLink[];\n\n/**\n * Embedded HTML content for specific positions in the page layout.\n */\nexport interface ThemeEmbed {\n /** Content to embed into <head> */\n head?: string;\n /** Content before header */\n headerBefore?: string;\n /** Content after header */\n headerAfter?: string;\n /** Content before sidebar navigation */\n sidebarBefore?: string;\n /** Content after sidebar navigation */\n sidebarAfter?: string;\n /** Content before main content */\n contentBefore?: string;\n /** Content after main content */\n contentAfter?: string;\n /** Content before footer */\n footerBefore?: string;\n /** Custom footer content (replaces default footer) */\n footer?: string;\n}\n\nexport interface SidebarItem {\n text?: string;\n link?: string;\n items?: SidebarItem[];\n collapsed?: boolean;\n stickyCollapsed?: boolean;\n}\n\n/**\n * Complete theme configuration.\n */\nexport interface ThemeConfig {\n /** Theme name for identification */\n name?: string;\n /** Base theme to extend */\n extends?: ThemeConfig;\n /** Light mode colors (maps to CSS variables) */\n colors?: ThemeColors;\n /** Dark mode colors (maps to CSS variables) */\n darkColors?: ThemeColors;\n /** Font configuration (maps to CSS variables) */\n fonts?: ThemeFonts;\n /** Entry page configuration */\n entryPage?: ThemeEntryPage;\n /** Layout configuration (maps to CSS variables) */\n layout?: ThemeLayout;\n /** Header configuration */\n header?: ThemeHeader;\n /** Footer configuration */\n footer?: ThemeFooter;\n /** Social links configuration */\n socialLinks?: SocialLinks;\n sidebar?: SidebarItem[];\n /** Embedded HTML content at specific positions */\n embed?: ThemeEmbed;\n /** Additional custom CSS */\n css?: string;\n /** Additional custom JavaScript */\n js?: string;\n}\n\n/**\n * Resolved theme configuration (after merging with defaults).\n */\nexport interface ResolvedThemeConfig {\n name: string;\n colors: ThemeColors;\n darkColors: ThemeColors;\n fonts: ThemeFonts;\n entryPage: ThemeEntryPage;\n layout: ThemeLayout;\n header: ThemeHeader;\n footer: ThemeFooter;\n socialLinks: SocialLinks;\n sidebar: SidebarItem[];\n embed: ThemeEmbed;\n css: string;\n js: string;\n}\n\n/**\n * Default theme configuration.\n * Based on the current ox-content SSG styles.\n */\nexport const defaultTheme: ThemeConfig = {\n name: \"default\",\n colors: {\n primary: \"#4f6fae\",\n primaryHover: \"#425f96\",\n background: \"#ffffff\",\n backgroundAlt: \"#f5f7fb\",\n text: \"#131a30\",\n textMuted: \"#4f607b\",\n border: \"#d2dbea\",\n codeBackground: \"#101a31\",\n codeBackgroundTop: \"#18264a\",\n codeText: \"#edf3ff\",\n },\n darkColors: {\n primary: \"#86a4da\",\n primaryHover: \"#a3bbe8\",\n background: \"#060816\",\n backgroundAlt: \"#0d1528\",\n text: \"#ebf2ff\",\n textMuted: \"#8ea0bf\",\n border: \"#223252\",\n codeBackground: \"#0a1020\",\n codeBackgroundTop: \"#0a1020\",\n codeText: \"#e7f0ff\",\n },\n fonts: {\n sans: '\"IBM Plex Sans\", \"Avenir Next\", \"Segoe UI Variable\", \"Segoe UI\", sans-serif',\n mono: '\"IBM Plex Mono\", \"SFMono-Regular\", Consolas, monospace',\n },\n entryPage: {\n mode: \"default\",\n },\n layout: {\n sidebarWidth: \"260px\",\n headerHeight: \"60px\",\n maxContentWidth: \"960px\",\n },\n header: {\n logo: undefined,\n logoLight: undefined,\n logoDark: undefined,\n showSiteNameText: true,\n logoWidth: 28,\n logoHeight: 28,\n },\n footer: {\n message: undefined,\n copyright: undefined,\n },\n socialLinks: {},\n embed: {},\n css: \"\",\n js: \"\",\n};\n\n/**\n * Deep merge two objects.\n */\nfunction deepMerge<T extends Record<string, unknown>>(target: T, source: Partial<T>): T {\n const result = { ...target };\n\n for (const key of Object.keys(source) as (keyof T)[]) {\n const sourceValue = source[key];\n const targetValue = target[key];\n\n if (\n sourceValue !== undefined &&\n typeof sourceValue === \"object\" &&\n sourceValue !== null &&\n !Array.isArray(sourceValue) &&\n typeof targetValue === \"object\" &&\n targetValue !== null &&\n !Array.isArray(targetValue)\n ) {\n result[key] = deepMerge(\n targetValue as Record<string, unknown>,\n sourceValue as Record<string, unknown>,\n ) as T[keyof T];\n } else if (sourceValue !== undefined) {\n result[key] = sourceValue as T[keyof T];\n }\n }\n\n return result;\n}\n\n/**\n * Defines a theme configuration with type checking.\n *\n * @example\n * ```ts\n * const myTheme = defineTheme({\n * extends: defaultTheme,\n * colors: {\n * primary: '#3498db',\n * },\n * footer: {\n * copyright: '2025 My Company',\n * },\n * });\n * ```\n */\nexport function defineTheme(config: ThemeConfig): ThemeConfig {\n return config;\n}\n\n/**\n * Merges multiple theme configurations.\n * Later themes override earlier ones.\n *\n * @example\n * ```ts\n * const merged = mergeThemes(defaultTheme, customTheme, overrides);\n * ```\n */\nexport function mergeThemes(...themes: ThemeConfig[]): ThemeConfig {\n if (themes.length === 0) {\n return { ...defaultTheme };\n }\n\n let result: ThemeConfig = {};\n\n for (const theme of themes) {\n result = deepMerge(\n result as Record<string, unknown>,\n theme as Record<string, unknown>,\n ) as ThemeConfig;\n }\n\n return result;\n}\n\n/**\n * Resolves a theme configuration by merging with its extends chain and defaults.\n */\nexport function resolveTheme(config?: ThemeConfig): ResolvedThemeConfig {\n if (!config) {\n return resolveTheme(defaultTheme);\n }\n\n // Build the extends chain\n const chain: ThemeConfig[] = [];\n let current: ThemeConfig | undefined = config;\n\n while (current) {\n chain.unshift(current);\n current = current.extends;\n }\n\n // Always start with default theme\n if (chain[0] !== defaultTheme && chain[0]?.name !== \"default\") {\n chain.unshift(defaultTheme);\n }\n\n // Merge all themes in the chain\n const merged = mergeThemes(...chain.map(withDerivedCodeBackgroundTop));\n\n // Return resolved config with all required fields\n return {\n name: merged.name ?? \"custom\",\n colors: merged.colors ?? defaultTheme.colors!,\n darkColors: merged.darkColors ?? defaultTheme.darkColors!,\n fonts: merged.fonts ?? defaultTheme.fonts!,\n entryPage: merged.entryPage ?? defaultTheme.entryPage!,\n layout: merged.layout ?? defaultTheme.layout!,\n header: merged.header ?? defaultTheme.header!,\n footer: merged.footer ?? defaultTheme.footer!,\n socialLinks: merged.socialLinks ?? defaultTheme.socialLinks!,\n sidebar: merged.sidebar ?? [],\n embed: merged.embed ?? {},\n css: merged.css ?? \"\",\n js: merged.js ?? \"\",\n };\n}\n\nfunction withDerivedCodeBackgroundTop(theme: ThemeConfig): ThemeConfig {\n const derive = (colors: ThemeColors | undefined): ThemeColors | undefined => {\n if (colors?.codeBackground !== undefined && colors.codeBackgroundTop === undefined) {\n return { ...colors, codeBackgroundTop: colors.codeBackground };\n }\n return colors;\n };\n\n return {\n ...theme,\n colors: derive(theme.colors),\n darkColors: derive(theme.darkColors),\n };\n}\n\n/**\n * Converts resolved theme to the format expected by Rust NAPI.\n */\nexport function themeToNapi(theme: ResolvedThemeConfig): NapiThemeConfig {\n const socialLinks = socialLinksToNapi(theme.socialLinks);\n\n return {\n colors: theme.colors.primary\n ? {\n primary: theme.colors.primary,\n primaryHover: theme.colors.primaryHover,\n background: theme.colors.background,\n backgroundAlt: theme.colors.backgroundAlt,\n text: theme.colors.text,\n textMuted: theme.colors.textMuted,\n border: theme.colors.border,\n codeBackground: theme.colors.codeBackground,\n codeBackgroundTop: theme.colors.codeBackgroundTop,\n codeText: theme.colors.codeText,\n }\n : undefined,\n darkColors: theme.darkColors.primary\n ? {\n primary: theme.darkColors.primary,\n primaryHover: theme.darkColors.primaryHover,\n background: theme.darkColors.background,\n backgroundAlt: theme.darkColors.backgroundAlt,\n text: theme.darkColors.text,\n textMuted: theme.darkColors.textMuted,\n border: theme.darkColors.border,\n codeBackground: theme.darkColors.codeBackground,\n codeBackgroundTop: theme.darkColors.codeBackgroundTop,\n codeText: theme.darkColors.codeText,\n }\n : undefined,\n fonts: theme.fonts.sans\n ? {\n sans: theme.fonts.sans,\n mono: theme.fonts.mono,\n }\n : undefined,\n entryPage: theme.entryPage.mode\n ? {\n mode: theme.entryPage.mode,\n }\n : undefined,\n layout: theme.layout.sidebarWidth\n ? {\n sidebarWidth: theme.layout.sidebarWidth,\n headerHeight: theme.layout.headerHeight,\n maxContentWidth: theme.layout.maxContentWidth,\n }\n : undefined,\n header:\n theme.header.logo || theme.header.logoLight || theme.header.logoDark\n ? {\n logo: theme.header.logo,\n logoLight: theme.header.logoLight,\n logoDark: theme.header.logoDark,\n showSiteNameText: theme.header.showSiteNameText,\n logoWidth: theme.header.logoWidth,\n logoHeight: theme.header.logoHeight,\n }\n : undefined,\n footer:\n theme.footer.message || theme.footer.copyright\n ? {\n message: theme.footer.message,\n copyright: theme.footer.copyright,\n }\n : undefined,\n socialLinks,\n embed: Object.keys(theme.embed).length > 0 ? theme.embed : undefined,\n css: theme.css || undefined,\n js: theme.js || undefined,\n };\n}\n\nfunction socialLinksToNapi(links: SocialLinks): NapiSocialLinks | undefined {\n if (Array.isArray(links)) {\n const items = links.map((item) => {\n const icon = typeof item.icon === \"string\" ? item.icon : undefined;\n const iconSvg = typeof item.icon === \"object\" ? item.icon.svg : undefined;\n return { icon, iconSvg, link: item.link, ariaLabel: item.ariaLabel };\n });\n return items.length > 0 ? { links: items } : undefined;\n }\n\n return links.github || links.twitter || links.discord\n ? { github: links.github, twitter: links.twitter, discord: links.discord }\n : undefined;\n}\n\n/**\n * NAPI-compatible theme colors type.\n */\nexport interface NapiThemeColors {\n primary?: string;\n primaryHover?: string;\n background?: string;\n backgroundAlt?: string;\n text?: string;\n textMuted?: string;\n border?: string;\n codeBackground?: string;\n codeBackgroundTop?: string;\n codeText?: string;\n}\n\n/**\n * NAPI-compatible theme fonts type.\n */\nexport interface NapiThemeFonts {\n sans?: string;\n mono?: string;\n}\n\n/**\n * NAPI-compatible entry page theme type.\n */\nexport interface NapiThemeEntryPage {\n mode?: \"default\" | \"subtle\";\n}\n\n/**\n * NAPI-compatible theme layout type.\n */\nexport interface NapiThemeLayout {\n sidebarWidth?: string;\n headerHeight?: string;\n maxContentWidth?: string;\n}\n\n/**\n * NAPI-compatible theme header type.\n */\nexport interface NapiThemeHeader {\n logo?: string;\n logoLight?: string;\n logoDark?: string;\n showSiteNameText?: boolean;\n logoWidth?: number;\n logoHeight?: number;\n}\n\n/**\n * NAPI-compatible theme footer type.\n */\nexport interface NapiThemeFooter {\n message?: string;\n copyright?: string;\n}\n\n/**\n * NAPI-compatible social links type.\n */\nexport interface NapiSocialLinks {\n github?: string;\n twitter?: string;\n discord?: string;\n links?: NapiSocialLink[];\n}\n\nexport interface NapiSocialLink {\n icon?: string;\n iconSvg?: string;\n link: string;\n ariaLabel?: string;\n}\n\n/**\n * NAPI-compatible theme embed type.\n */\nexport interface NapiThemeEmbed {\n head?: string;\n headerBefore?: string;\n headerAfter?: string;\n sidebarBefore?: string;\n sidebarAfter?: string;\n contentBefore?: string;\n contentAfter?: string;\n footerBefore?: string;\n footer?: string;\n}\n\n/**\n * NAPI-compatible theme configuration type.\n */\nexport interface NapiThemeConfig {\n colors?: NapiThemeColors;\n darkColors?: NapiThemeColors;\n fonts?: NapiThemeFonts;\n entryPage?: NapiThemeEntryPage;\n layout?: NapiThemeLayout;\n header?: NapiThemeHeader;\n footer?: NapiThemeFooter;\n socialLinks?: NapiSocialLinks;\n embed?: NapiThemeEmbed;\n css?: string;\n js?: string;\n}\n","import { importNapiModuleSync } from \"./napi\";\nimport { defineTheme, mergeThemes, type ThemeConfig } from \"./theme\";\nimport type { OxContentOptions, SsgNavigationGroup, SsgNavigationItem } from \"./types\";\n\nexport interface VitePressLogo {\n light?: string;\n dark?: string;\n src?: string;\n alt?: string;\n}\n\nexport interface VitePressSocialLink {\n icon: string;\n link: string;\n ariaLabel?: string;\n}\n\nexport interface VitePressFooter {\n message?: string;\n copyright?: string;\n}\n\nexport interface VitePressSidebarItem {\n text?: string;\n link?: string;\n items?: VitePressSidebarItem[];\n collapsed?: boolean;\n}\n\nexport type VitePressSidebar = VitePressSidebarItem[] | Record<string, VitePressSidebarItem[]>;\n\nexport interface VitePressNavItem {\n text?: string;\n link?: string;\n items?: VitePressNavItem[];\n activeMatch?: string;\n}\n\nexport interface VitePressThemeConfig {\n siteTitle?: string | false;\n logo?: string | VitePressLogo;\n nav?: VitePressNavItem[];\n sidebar?: VitePressSidebar;\n socialLinks?: VitePressSocialLink[];\n footer?: VitePressFooter;\n search?: {\n placeholder?: string;\n };\n}\n\nexport interface VitePressConfig {\n title?: string;\n description?: string;\n base?: string;\n themeConfig?: VitePressThemeConfig;\n}\n\nexport interface GenerateVitePressMigrationConfigOptions {\n importSource?: string;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isExternalLink(value: string): boolean {\n return /^[a-z][a-z0-9+.-]*:/i.test(value) || value.startsWith(\"//\");\n}\n\nfunction splitLink(value: string): { pathname: string; suffix: string } {\n const match = /^([^?#]*)([?#].*)?$/.exec(value);\n return {\n pathname: match?.[1] ?? value,\n suffix: match?.[2] ?? \"\",\n };\n}\n\nfunction normalizeInternalPath(value: string): string {\n const { pathname } = splitLink(value.trim());\n let normalized = pathname || \"/\";\n\n if (!normalized.startsWith(\"/\")) {\n normalized = `/${normalized}`;\n }\n\n normalized = normalized\n .replace(/\\/index(?:\\.(?:html?|md|markdown))?$/i, \"/\")\n .replace(/\\.(?:html?|md|markdown)$/i, \"\");\n\n if (normalized !== \"/\") {\n normalized = normalized.replace(/\\/+$/, \"\");\n }\n\n return normalized || \"/\";\n}\n\nfunction formatTitle(value: string): string {\n return value\n .replace(/[-_]([a-z])/g, (_, char: string) => ` ${char.toUpperCase()}`)\n .replace(/^[a-z]/, (char) => char.toUpperCase());\n}\n\nfunction titleFromPath(value: string): string {\n const normalized = normalizeInternalPath(value);\n if (normalized === \"/\") {\n return \"Home\";\n }\n\n const segment = normalized.split(\"/\").filter(Boolean).pop() ?? \"Page\";\n return formatTitle(segment);\n}\n\nfunction titleFromSidebarKey(value: string): string {\n const segment = value\n .replace(/^\\/+|\\/+$/g, \"\")\n .split(\"/\")\n .filter(Boolean)\n .pop();\n return formatTitle(segment ?? \"guide\");\n}\n\nfunction toNavigationItem(text: string | undefined, link: string): SsgNavigationItem {\n const title = text?.trim() || titleFromPath(link);\n\n if (isExternalLink(link) || link.startsWith(\"#\")) {\n return { title, href: link };\n }\n\n const { suffix } = splitLink(link);\n const path = normalizeInternalPath(link);\n\n return suffix ? { title, path, href: `${path}${suffix}` } : { title, path };\n}\n\nfunction dedupeNavigationItems(items: SsgNavigationItem[]): SsgNavigationItem[] {\n const seen = new Set<string>();\n const next: SsgNavigationItem[] = [];\n\n for (const item of items) {\n const key = `${item.title}::${item.path ?? \"\"}::${item.href ?? \"\"}`;\n if (seen.has(key)) {\n continue;\n }\n seen.add(key);\n next.push(item);\n }\n\n return next;\n}\n\nfunction dedupeNavigationGroups(groups: SsgNavigationGroup[]): SsgNavigationGroup[] {\n const merged = new Map<string, SsgNavigationItem[]>();\n const orderedTitles: string[] = [];\n\n for (const group of groups) {\n if (group.items.length === 0) {\n continue;\n }\n\n if (!merged.has(group.title)) {\n merged.set(group.title, []);\n orderedTitles.push(group.title);\n }\n\n merged.get(group.title)!.push(...group.items);\n }\n\n return orderedTitles.map((title) => ({\n title,\n items: dedupeNavigationItems(merged.get(title) ?? []),\n }));\n}\n\nfunction collectSidebarLinks(items: VitePressSidebarItem[]): SsgNavigationItem[] {\n const links: SsgNavigationItem[] = [];\n\n for (const item of items) {\n if (item.link) {\n links.push(toNavigationItem(item.text, item.link));\n }\n\n if (item.items?.length) {\n links.push(...collectSidebarLinks(item.items));\n }\n }\n\n return dedupeNavigationItems(links);\n}\n\nfunction sidebarArrayToGroups(\n items: VitePressSidebarItem[],\n fallbackTitle: string,\n): SsgNavigationGroup[] {\n const groups: SsgNavigationGroup[] = [];\n const rootItems: SsgNavigationItem[] = [];\n\n for (const item of items) {\n if (item.link) {\n rootItems.push(toNavigationItem(item.text, item.link));\n }\n\n if (item.items?.length) {\n const children = collectSidebarLinks(item.items);\n if (children.length > 0) {\n groups.push({\n title: item.text?.trim() || fallbackTitle,\n items: children,\n });\n }\n }\n }\n\n if (rootItems.length > 0) {\n groups.unshift({\n title: fallbackTitle,\n items: dedupeNavigationItems(rootItems),\n });\n }\n\n return groups;\n}\n\nfunction collectNavLinks(items: VitePressNavItem[]): SsgNavigationItem[] {\n const links: SsgNavigationItem[] = [];\n\n for (const item of items) {\n if (item.link) {\n links.push(toNavigationItem(item.text, item.link));\n }\n\n if (item.items?.length) {\n links.push(...collectNavLinks(item.items));\n }\n }\n\n return dedupeNavigationItems(links);\n}\n\nfunction resolveLogoSrc(logo: string | VitePressLogo | undefined): string | undefined {\n if (!logo) {\n return undefined;\n }\n\n if (typeof logo === \"string\") {\n return logo;\n }\n\n return logo.light ?? logo.dark ?? logo.src;\n}\n\nfunction normalizeSocialIcon(icon: string): \"github\" | \"twitter\" | \"discord\" | undefined {\n const normalized = icon.trim().toLowerCase();\n\n if (normalized === \"github\") return \"github\";\n if (normalized === \"discord\") return \"discord\";\n if (normalized === \"twitter\" || normalized === \"x\" || normalized === \"x-twitter\") {\n return \"twitter\";\n }\n\n return undefined;\n}\n\nfunction toThemeConfig(themeConfig: VitePressThemeConfig | undefined): ThemeConfig | undefined {\n if (!themeConfig) {\n return undefined;\n }\n\n const logo = resolveLogoSrc(themeConfig.logo);\n const socialLinks = Object.fromEntries(\n (themeConfig.socialLinks ?? [])\n .map((link) => {\n const key = normalizeSocialIcon(link.icon);\n return key ? [key, link.link] : null;\n })\n .filter((entry): entry is [string, string] => entry !== null),\n );\n\n const theme: ThemeConfig = {\n ...(logo\n ? {\n header: {\n logo,\n },\n }\n : {}),\n ...(themeConfig.footer?.message || themeConfig.footer?.copyright\n ? {\n footer: {\n message: themeConfig.footer.message,\n copyright: themeConfig.footer.copyright,\n },\n }\n : {}),\n ...(Object.keys(socialLinks).length > 0\n ? {\n socialLinks,\n }\n : {}),\n };\n\n return logo || Object.keys(socialLinks).length > 0 || themeConfig.footer\n ? defineTheme(theme)\n : undefined;\n}\n\nfunction resolveSiteName(config: VitePressConfig): string | undefined {\n const siteTitle = config.themeConfig?.siteTitle;\n if (typeof siteTitle === \"string\" && siteTitle.trim()) {\n return siteTitle;\n }\n\n return config.title;\n}\n\nfunction mergeOxContentOptions(\n baseOptions: OxContentOptions,\n overrides: OxContentOptions,\n): OxContentOptions {\n const mergedSsg =\n overrides.ssg === false\n ? false\n : {\n ...(typeof baseOptions.ssg === \"object\" ? baseOptions.ssg : {}),\n ...(typeof overrides.ssg === \"object\" ? overrides.ssg : {}),\n theme:\n typeof baseOptions.ssg === \"object\" &&\n typeof overrides.ssg === \"object\" &&\n baseOptions.ssg.theme &&\n overrides.ssg.theme\n ? defineTheme(mergeThemes(baseOptions.ssg.theme, overrides.ssg.theme))\n : typeof overrides.ssg === \"object\" && overrides.ssg.theme\n ? overrides.ssg.theme\n : typeof baseOptions.ssg === \"object\"\n ? baseOptions.ssg.theme\n : undefined,\n };\n\n const mergedSearch =\n overrides.search === false\n ? false\n : typeof overrides.search === \"object\"\n ? {\n ...(typeof baseOptions.search === \"object\" ? baseOptions.search : {}),\n ...overrides.search,\n }\n : baseOptions.search;\n\n return {\n ...baseOptions,\n ...overrides,\n ssg: mergedSsg,\n search: mergedSearch,\n };\n}\n\n/**\n * Converts a VitePress sidebar config into ox-content navigation groups.\n * Nested VitePress items are flattened into the nearest ox-content group.\n */\nexport function convertVitePressSidebar(sidebar: VitePressSidebar): SsgNavigationGroup[] {\n if (Array.isArray(sidebar)) {\n return dedupeNavigationGroups(sidebarArrayToGroups(sidebar, \"Guide\"));\n }\n\n const groups = Object.entries(sidebar).flatMap(([key, items]) =>\n sidebarArrayToGroups(items, titleFromSidebarKey(key)),\n );\n\n return dedupeNavigationGroups(groups);\n}\n\n/**\n * Converts VitePress top navigation into ox-content sidebar groups.\n * This is used as a fallback when no explicit sidebar is defined.\n */\nexport function convertVitePressNav(nav: VitePressNavItem[]): SsgNavigationGroup[] {\n const groups: SsgNavigationGroup[] = [];\n const rootItems: SsgNavigationItem[] = [];\n\n for (const item of nav) {\n if (item.link) {\n rootItems.push(toNavigationItem(item.text, item.link));\n }\n\n if (item.items?.length) {\n const children = collectNavLinks(item.items);\n if (children.length > 0) {\n groups.push({\n title: item.text?.trim() || \"Navigation\",\n items: children,\n });\n }\n }\n }\n\n if (rootItems.length > 0) {\n groups.unshift({\n title: \"Navigation\",\n items: dedupeNavigationItems(rootItems),\n });\n }\n\n return dedupeNavigationGroups(groups);\n}\n\n/**\n * Creates ox-content plugin options from an existing VitePress config.\n */\nexport function fromVitePressConfig(\n config: VitePressConfig,\n overrides: OxContentOptions = {},\n): OxContentOptions {\n const theme = toThemeConfig(config.themeConfig);\n const navigation = config.themeConfig?.sidebar\n ? convertVitePressSidebar(config.themeConfig.sidebar)\n : config.themeConfig?.nav\n ? convertVitePressNav(config.themeConfig.nav)\n : undefined;\n\n const migrated: OxContentOptions = {\n ...(config.base ? { base: config.base } : {}),\n ...(config.themeConfig?.search?.placeholder\n ? {\n search: {\n placeholder: config.themeConfig.search.placeholder,\n },\n }\n : {}),\n ssg: {\n ...(resolveSiteName(config) ? { siteName: resolveSiteName(config) } : {}),\n ...(theme ? { theme } : {}),\n ...(navigation ? { navigation } : {}),\n },\n };\n\n return mergeOxContentOptions(migrated, overrides);\n}\n\n/**\n * Generates a TypeScript module exporting migrated ox-content options.\n *\n * This is used by the migration CLI so users can inspect and edit the resulting\n * object instead of keeping a runtime dependency on their VitePress config.\n */\nexport function generateVitePressMigrationConfig(\n config: VitePressConfig,\n overrides: OxContentOptions = {},\n options: GenerateVitePressMigrationConfigOptions = {},\n): string {\n const importSource = options.importSource ?? \"@ox-content/vite-plugin\";\n const migrated = fromVitePressConfig(config, overrides);\n\n return `import type { OxContentOptions } from ${JSON.stringify(importSource)};\n\nconst config = ${formatTsValue(migrated)} satisfies OxContentOptions;\n\nexport default config;\n`;\n}\n\nfunction formatTsValue(value: unknown, depth = 0): string {\n if (value === undefined) {\n return \"undefined\";\n }\n\n if (value === null || typeof value === \"boolean\" || typeof value === \"number\") {\n return JSON.stringify(value);\n }\n\n if (typeof value === \"string\") {\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return \"[]\";\n }\n\n const indent = \" \".repeat(depth + 1);\n const closingIndent = \" \".repeat(depth);\n return `[\\n${value.map((item) => `${indent}${formatTsValue(item, depth + 1)},`).join(\"\\n\")}\\n${closingIndent}]`;\n }\n\n if (isRecord(value)) {\n const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined);\n if (entries.length === 0) {\n return \"{}\";\n }\n\n const indent = \" \".repeat(depth + 1);\n const closingIndent = \" \".repeat(depth);\n return `{\\n${entries\n .map(\n ([key, entryValue]) =>\n `${indent}${formatObjectKey(key)}: ${formatTsValue(entryValue, depth + 1)},`,\n )\n .join(\"\\n\")}\\n${closingIndent}}`;\n }\n\n return \"undefined\";\n}\n\nfunction formatObjectKey(key: string): string {\n return /^[A-Za-z_$][\\w$]*$/.test(key) ? key : JSON.stringify(key);\n}\n\n/**\n * Normalizes VitePress-specific frontmatter into ox-content's entry-page shape.\n */\nexport function normalizeVitePressFrontmatter(\n frontmatter: Record<string, unknown>,\n): Record<string, unknown> {\n return importNapiModuleSync().normalizeVitePressFrontmatter(frontmatter);\n}\n"],"mappings":"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"}
|
package/dist/vitepress.mjs
CHANGED
|
@@ -15,6 +15,7 @@ const defaultTheme = {
|
|
|
15
15
|
textMuted: "#4f607b",
|
|
16
16
|
border: "#d2dbea",
|
|
17
17
|
codeBackground: "#101a31",
|
|
18
|
+
codeBackgroundTop: "#18264a",
|
|
18
19
|
codeText: "#edf3ff"
|
|
19
20
|
},
|
|
20
21
|
darkColors: {
|
|
@@ -26,6 +27,7 @@ const defaultTheme = {
|
|
|
26
27
|
textMuted: "#8ea0bf",
|
|
27
28
|
border: "#223252",
|
|
28
29
|
codeBackground: "#0a1020",
|
|
30
|
+
codeBackgroundTop: "#0a1020",
|
|
29
31
|
codeText: "#e7f0ff"
|
|
30
32
|
},
|
|
31
33
|
fonts: {
|
|
@@ -114,7 +116,7 @@ function resolveTheme(config) {
|
|
|
114
116
|
current = current.extends;
|
|
115
117
|
}
|
|
116
118
|
if (chain[0] !== defaultTheme && chain[0]?.name !== "default") chain.unshift(defaultTheme);
|
|
117
|
-
const merged = mergeThemes(...chain);
|
|
119
|
+
const merged = mergeThemes(...chain.map(withDerivedCodeBackgroundTop));
|
|
118
120
|
return {
|
|
119
121
|
name: merged.name ?? "custom",
|
|
120
122
|
colors: merged.colors ?? defaultTheme.colors,
|
|
@@ -131,6 +133,20 @@ function resolveTheme(config) {
|
|
|
131
133
|
js: merged.js ?? ""
|
|
132
134
|
};
|
|
133
135
|
}
|
|
136
|
+
function withDerivedCodeBackgroundTop(theme) {
|
|
137
|
+
const derive = (colors) => {
|
|
138
|
+
if (colors?.codeBackground !== void 0 && colors.codeBackgroundTop === void 0) return {
|
|
139
|
+
...colors,
|
|
140
|
+
codeBackgroundTop: colors.codeBackground
|
|
141
|
+
};
|
|
142
|
+
return colors;
|
|
143
|
+
};
|
|
144
|
+
return {
|
|
145
|
+
...theme,
|
|
146
|
+
colors: derive(theme.colors),
|
|
147
|
+
darkColors: derive(theme.darkColors)
|
|
148
|
+
};
|
|
149
|
+
}
|
|
134
150
|
/**
|
|
135
151
|
* Converts resolved theme to the format expected by Rust NAPI.
|
|
136
152
|
*/
|
|
@@ -146,6 +162,7 @@ function themeToNapi(theme) {
|
|
|
146
162
|
textMuted: theme.colors.textMuted,
|
|
147
163
|
border: theme.colors.border,
|
|
148
164
|
codeBackground: theme.colors.codeBackground,
|
|
165
|
+
codeBackgroundTop: theme.colors.codeBackgroundTop,
|
|
149
166
|
codeText: theme.colors.codeText
|
|
150
167
|
} : void 0,
|
|
151
168
|
darkColors: theme.darkColors.primary ? {
|
|
@@ -157,6 +174,7 @@ function themeToNapi(theme) {
|
|
|
157
174
|
textMuted: theme.darkColors.textMuted,
|
|
158
175
|
border: theme.darkColors.border,
|
|
159
176
|
codeBackground: theme.darkColors.codeBackground,
|
|
177
|
+
codeBackgroundTop: theme.darkColors.codeBackgroundTop,
|
|
160
178
|
codeText: theme.darkColors.codeText
|
|
161
179
|
} : void 0,
|
|
162
180
|
fonts: theme.fonts.sans ? {
|