@ox-content/vite-plugin 2.9.0 → 2.10.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.mjs CHANGED
@@ -3,6 +3,7 @@ import { i as transformTabs, n as resetTabGroupCounter, t as generateTabsCSS } f
3
3
  import { n as transformYouTube, t as extractVideoId } from "./youtube.mjs";
4
4
  import { a as transformGitHub, i as prefetchGitHubRepos, n as fetchRepoData, t as collectGitHubRepos } from "./github.mjs";
5
5
  import { a as transformOgp, i as prefetchOgpData, n as fetchOgpData, t as collectOgpUrls } from "./ogp.mjs";
6
+ import { a as normalizeVitePressFrontmatter, c as mergeThemes, i as generateVitePressMigrationConfig, l as resolveTheme, n as convertVitePressSidebar, o as defaultTheme, r as fromVitePressConfig, s as defineTheme, t as convertVitePressNav, u as themeToNapi } from "./vitepress.mjs";
6
7
  import { createRequire } from "node:module";
7
8
  import * as path$1 from "path";
8
9
  import { unified } from "unified";
@@ -8798,213 +8799,6 @@ initIslands((el, props) => {
8798
8799
  `;
8799
8800
  }
8800
8801
  //#endregion
8801
- //#region src/theme.ts
8802
- /**
8803
- * Default theme configuration.
8804
- * Based on the current ox-content SSG styles.
8805
- */
8806
- const defaultTheme = {
8807
- name: "default",
8808
- colors: {
8809
- primary: "#4f6fae",
8810
- primaryHover: "#425f96",
8811
- background: "#ffffff",
8812
- backgroundAlt: "#f5f7fb",
8813
- text: "#131a30",
8814
- textMuted: "#4f607b",
8815
- border: "#d2dbea",
8816
- codeBackground: "#101a31",
8817
- codeText: "#edf3ff"
8818
- },
8819
- darkColors: {
8820
- primary: "#86a4da",
8821
- primaryHover: "#a3bbe8",
8822
- background: "#060816",
8823
- backgroundAlt: "#0d1528",
8824
- text: "#ebf2ff",
8825
- textMuted: "#8ea0bf",
8826
- border: "#223252",
8827
- codeBackground: "#0a1020",
8828
- codeText: "#e7f0ff"
8829
- },
8830
- fonts: {
8831
- sans: "\"IBM Plex Sans\", \"Avenir Next\", \"Segoe UI Variable\", \"Segoe UI\", sans-serif",
8832
- mono: "\"IBM Plex Mono\", \"SFMono-Regular\", Consolas, monospace"
8833
- },
8834
- entryPage: { mode: "default" },
8835
- layout: {
8836
- sidebarWidth: "260px",
8837
- headerHeight: "60px",
8838
- maxContentWidth: "960px"
8839
- },
8840
- header: {
8841
- logo: void 0,
8842
- logoLight: void 0,
8843
- logoDark: void 0,
8844
- showSiteNameText: true,
8845
- logoWidth: 28,
8846
- logoHeight: 28
8847
- },
8848
- footer: {
8849
- message: void 0,
8850
- copyright: void 0
8851
- },
8852
- socialLinks: {},
8853
- embed: {},
8854
- css: "",
8855
- js: ""
8856
- };
8857
- /**
8858
- * Deep merge two objects.
8859
- */
8860
- function deepMerge(target, source) {
8861
- const result = { ...target };
8862
- for (const key of Object.keys(source)) {
8863
- const sourceValue = source[key];
8864
- const targetValue = target[key];
8865
- if (sourceValue !== void 0 && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue)) result[key] = deepMerge(targetValue, sourceValue);
8866
- else if (sourceValue !== void 0) result[key] = sourceValue;
8867
- }
8868
- return result;
8869
- }
8870
- /**
8871
- * Defines a theme configuration with type checking.
8872
- *
8873
- * @example
8874
- * ```ts
8875
- * const myTheme = defineTheme({
8876
- * extends: defaultTheme,
8877
- * colors: {
8878
- * primary: '#3498db',
8879
- * },
8880
- * footer: {
8881
- * copyright: '2025 My Company',
8882
- * },
8883
- * });
8884
- * ```
8885
- */
8886
- function defineTheme(config) {
8887
- return config;
8888
- }
8889
- /**
8890
- * Merges multiple theme configurations.
8891
- * Later themes override earlier ones.
8892
- *
8893
- * @example
8894
- * ```ts
8895
- * const merged = mergeThemes(defaultTheme, customTheme, overrides);
8896
- * ```
8897
- */
8898
- function mergeThemes(...themes) {
8899
- if (themes.length === 0) return { ...defaultTheme };
8900
- let result = {};
8901
- for (const theme of themes) result = deepMerge(result, theme);
8902
- return result;
8903
- }
8904
- /**
8905
- * Resolves a theme configuration by merging with its extends chain and defaults.
8906
- */
8907
- function resolveTheme(config) {
8908
- if (!config) return resolveTheme(defaultTheme);
8909
- const chain = [];
8910
- let current = config;
8911
- while (current) {
8912
- chain.unshift(current);
8913
- current = current.extends;
8914
- }
8915
- if (chain[0] !== defaultTheme && chain[0]?.name !== "default") chain.unshift(defaultTheme);
8916
- const merged = mergeThemes(...chain);
8917
- return {
8918
- name: merged.name ?? "custom",
8919
- colors: merged.colors ?? defaultTheme.colors,
8920
- darkColors: merged.darkColors ?? defaultTheme.darkColors,
8921
- fonts: merged.fonts ?? defaultTheme.fonts,
8922
- entryPage: merged.entryPage ?? defaultTheme.entryPage,
8923
- layout: merged.layout ?? defaultTheme.layout,
8924
- header: merged.header ?? defaultTheme.header,
8925
- footer: merged.footer ?? defaultTheme.footer,
8926
- socialLinks: merged.socialLinks ?? defaultTheme.socialLinks,
8927
- sidebar: merged.sidebar ?? [],
8928
- embed: merged.embed ?? {},
8929
- css: merged.css ?? "",
8930
- js: merged.js ?? ""
8931
- };
8932
- }
8933
- /**
8934
- * Converts resolved theme to the format expected by Rust NAPI.
8935
- */
8936
- function themeToNapi(theme) {
8937
- const socialLinks = socialLinksToNapi(theme.socialLinks);
8938
- return {
8939
- colors: theme.colors.primary ? {
8940
- primary: theme.colors.primary,
8941
- primaryHover: theme.colors.primaryHover,
8942
- background: theme.colors.background,
8943
- backgroundAlt: theme.colors.backgroundAlt,
8944
- text: theme.colors.text,
8945
- textMuted: theme.colors.textMuted,
8946
- border: theme.colors.border,
8947
- codeBackground: theme.colors.codeBackground,
8948
- codeText: theme.colors.codeText
8949
- } : void 0,
8950
- darkColors: theme.darkColors.primary ? {
8951
- primary: theme.darkColors.primary,
8952
- primaryHover: theme.darkColors.primaryHover,
8953
- background: theme.darkColors.background,
8954
- backgroundAlt: theme.darkColors.backgroundAlt,
8955
- text: theme.darkColors.text,
8956
- textMuted: theme.darkColors.textMuted,
8957
- border: theme.darkColors.border,
8958
- codeBackground: theme.darkColors.codeBackground,
8959
- codeText: theme.darkColors.codeText
8960
- } : void 0,
8961
- fonts: theme.fonts.sans ? {
8962
- sans: theme.fonts.sans,
8963
- mono: theme.fonts.mono
8964
- } : void 0,
8965
- entryPage: theme.entryPage.mode ? { mode: theme.entryPage.mode } : void 0,
8966
- layout: theme.layout.sidebarWidth ? {
8967
- sidebarWidth: theme.layout.sidebarWidth,
8968
- headerHeight: theme.layout.headerHeight,
8969
- maxContentWidth: theme.layout.maxContentWidth
8970
- } : void 0,
8971
- header: theme.header.logo || theme.header.logoLight || theme.header.logoDark ? {
8972
- logo: theme.header.logo,
8973
- logoLight: theme.header.logoLight,
8974
- logoDark: theme.header.logoDark,
8975
- showSiteNameText: theme.header.showSiteNameText,
8976
- logoWidth: theme.header.logoWidth,
8977
- logoHeight: theme.header.logoHeight
8978
- } : void 0,
8979
- footer: theme.footer.message || theme.footer.copyright ? {
8980
- message: theme.footer.message,
8981
- copyright: theme.footer.copyright
8982
- } : void 0,
8983
- socialLinks,
8984
- embed: Object.keys(theme.embed).length > 0 ? theme.embed : void 0,
8985
- css: theme.css || void 0,
8986
- js: theme.js || void 0
8987
- };
8988
- }
8989
- function socialLinksToNapi(links) {
8990
- if (Array.isArray(links)) {
8991
- const items = links.map((item) => {
8992
- return {
8993
- icon: typeof item.icon === "string" ? item.icon : void 0,
8994
- iconSvg: typeof item.icon === "object" ? item.icon.svg : void 0,
8995
- link: item.link,
8996
- ariaLabel: item.ariaLabel
8997
- };
8998
- });
8999
- return items.length > 0 ? { links: items } : void 0;
9000
- }
9001
- return links.github || links.twitter || links.discord ? {
9002
- github: links.github,
9003
- twitter: links.twitter,
9004
- discord: links.discord
9005
- } : void 0;
9006
- }
9007
- //#endregion
9008
8802
  //#region src/ssg.ts
9009
8803
  /**
9010
8804
  * SSG (Static Site Generation) module for ox-content
@@ -10381,7 +10175,8 @@ function resolveSsgOptions(ssg) {
10381
10175
  generateOgImage: ssg.generateOgImage ?? false,
10382
10176
  lastUpdated: ssg.lastUpdated ?? false,
10383
10177
  siteUrl: ssg.siteUrl,
10384
- theme: resolveTheme(ssg.theme)
10178
+ theme: resolveTheme(ssg.theme),
10179
+ navigation: ssg.navigation
10385
10180
  };
10386
10181
  }
10387
10182
  /**
@@ -10512,6 +10307,59 @@ async function externalizeSharedPageAssets(pages, outDir, base) {
10512
10307
  function getUrlPath$1(inputPath, srcDir) {
10513
10308
  return importNapiModuleSync().getSsgUrlPath(inputPath, srcDir);
10514
10309
  }
10310
+ function isExternalHref(value) {
10311
+ return /^[a-z][a-z0-9+.-]*:/i.test(value) || value.startsWith("//");
10312
+ }
10313
+ function splitHrefSuffix(value) {
10314
+ const match = /^([^?#]*)([?#].*)?$/.exec(value);
10315
+ return {
10316
+ pathname: match?.[1] ?? value,
10317
+ suffix: match?.[2] ?? ""
10318
+ };
10319
+ }
10320
+ function normalizeNavigationPath(value) {
10321
+ const { pathname, suffix } = splitHrefSuffix(value.trim());
10322
+ let normalized = pathname || "/";
10323
+ if (!normalized.startsWith("/")) normalized = `/${normalized}`;
10324
+ normalized = normalized.replace(/\/index(?:\.(?:html?|md|markdown))?$/i, "/").replace(/\.(?:html?|md|markdown)$/i, "");
10325
+ if (normalized !== "/") normalized = normalized.replace(/\/+$/, "");
10326
+ return {
10327
+ path: normalized || "/",
10328
+ suffix
10329
+ };
10330
+ }
10331
+ function buildHrefFromNavigationPath(pathname, base, extension) {
10332
+ if (pathname === "/" || pathname === "") return `${base}index${extension}`;
10333
+ return `${base}${pathname.replace(/^\/+/, "")}/index${extension}`;
10334
+ }
10335
+ /**
10336
+ * Resolves manual navigation config to the format used by the built-in SSG renderer.
10337
+ */
10338
+ function resolveNavigationGroups(navigation, base, extension) {
10339
+ if (!navigation) return;
10340
+ return navigation.map((group) => ({
10341
+ title: group.title,
10342
+ items: group.items.flatMap((item) => {
10343
+ const rawHref = item.href ?? item.path;
10344
+ if (!rawHref) return [];
10345
+ if (isExternalHref(rawHref) || rawHref.startsWith("#")) return [{
10346
+ title: item.title,
10347
+ path: item.path ?? rawHref,
10348
+ href: rawHref
10349
+ }];
10350
+ const { path } = normalizeNavigationPath(item.path ?? rawHref);
10351
+ const href = item.href ? (() => {
10352
+ const normalized = normalizeNavigationPath(item.href);
10353
+ return `${buildHrefFromNavigationPath(normalized.path, base, extension)}${normalized.suffix}`;
10354
+ })() : buildHrefFromNavigationPath(path, base, extension);
10355
+ return [{
10356
+ title: item.title,
10357
+ path,
10358
+ href
10359
+ }];
10360
+ })
10361
+ }));
10362
+ }
10515
10363
  function getPageLocale(urlPath, i18n) {
10516
10364
  if (!i18n) return void 0;
10517
10365
  return importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, i18n.locales.map((locale) => locale.code)) ?? void 0;
@@ -10572,7 +10420,7 @@ async function buildSsg(options, root) {
10572
10420
  });
10573
10421
  } catch {}
10574
10422
  const markdownFiles = await collectMarkdownFiles$1(srcDir);
10575
- const navItems = ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension);
10423
+ const navItems = resolveNavigationGroups(ssgOptions.navigation, base, ssgOptions.extension) ?? (ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension));
10576
10424
  let siteName = ssgOptions.siteName ?? "Documentation";
10577
10425
  if (!ssgOptions.siteName) try {
10578
10426
  const pkgPath = path$1.join(root, "package.json");
@@ -10591,6 +10439,7 @@ async function buildSsg(options, root) {
10591
10439
  baseUrl: base,
10592
10440
  sourcePath: inputPath
10593
10441
  });
10442
+ const frontmatter = normalizeVitePressFrontmatter(result.frontmatter);
10594
10443
  let transformedHtml = result.html;
10595
10444
  const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(transformedHtml);
10596
10445
  transformedHtml = protectedHtml;
@@ -10605,8 +10454,8 @@ async function buildSsg(options, root) {
10605
10454
  transformedHtml = await transformAllPlugins(transformedHtml, pluginOptions);
10606
10455
  if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
10607
10456
  transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
10608
- const title = extractTitle$1(transformedHtml, result.frontmatter);
10609
- const description = result.frontmatter.description;
10457
+ const title = extractTitle$1(transformedHtml, frontmatter);
10458
+ const description = frontmatter.description;
10610
10459
  const routePaths = getRoutePaths(inputPath, srcDir, outDir, base, ssgOptions.extension, ssgOptions.siteUrl);
10611
10460
  pageResults.push({
10612
10461
  inputPath,
@@ -10615,11 +10464,11 @@ async function buildSsg(options, root) {
10615
10464
  title,
10616
10465
  description,
10617
10466
  lastUpdated: napi?.getGitLastUpdated(inputPath, root) ?? void 0,
10618
- frontmatter: result.frontmatter,
10467
+ frontmatter,
10619
10468
  toc: result.toc
10620
10469
  });
10621
10470
  if (shouldGenerateOgImages) {
10622
- const { layout: _layout, ...frontmatterRest } = result.frontmatter;
10471
+ const { layout: _layout, ...frontmatterRest } = frontmatter;
10623
10472
  ogImageEntries.push({
10624
10473
  props: {
10625
10474
  ...frontmatterRest,
@@ -10946,6 +10795,7 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
10946
10795
  baseUrl: base,
10947
10796
  sourcePath: filePath
10948
10797
  });
10798
+ const frontmatter = normalizeVitePressFrontmatter(result.frontmatter);
10949
10799
  let transformedHtml = result.html;
10950
10800
  const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(transformedHtml);
10951
10801
  transformedHtml = protectedHtml;
@@ -10959,19 +10809,19 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
10959
10809
  });
10960
10810
  if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
10961
10811
  transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
10962
- const title = extractTitle$1(transformedHtml, result.frontmatter);
10963
- const description = result.frontmatter.description;
10812
+ const title = extractTitle$1(transformedHtml, frontmatter);
10813
+ const description = frontmatter.description;
10964
10814
  let entryPage;
10965
- if (result.frontmatter.layout === "entry") entryPage = {
10966
- hero: result.frontmatter.hero,
10967
- features: result.frontmatter.features
10815
+ if (frontmatter.layout === "entry") entryPage = {
10816
+ hero: frontmatter.hero,
10817
+ features: frontmatter.features
10968
10818
  };
10969
10819
  let html = await generateHtmlPage({
10970
10820
  title,
10971
10821
  description,
10972
10822
  content: transformedHtml,
10973
10823
  toc: result.toc,
10974
- frontmatter: result.frontmatter,
10824
+ frontmatter,
10975
10825
  path: getUrlPath$1(filePath, srcDir),
10976
10826
  href: getUrlPath$1(filePath, srcDir) || "/",
10977
10827
  entryPage
@@ -11002,7 +10852,10 @@ function createDevServerMiddleware(options, root, cache) {
11002
10852
  return;
11003
10853
  }
11004
10854
  if (!cache.siteName) cache.siteName = await resolveSiteName(options, root);
11005
- if (!cache.navGroups) cache.navGroups = buildNavItems(await collectMarkdownFiles$1(srcDir), srcDir, base, ".html");
10855
+ if (!cache.navGroups) {
10856
+ const markdownFiles = await collectMarkdownFiles$1(srcDir);
10857
+ cache.navGroups = resolveNavigationGroups(options.ssg.navigation, base, options.ssg.extension) ?? (options.ssg.theme?.sidebar.length ? buildThemeNavItems(options.ssg.theme.sidebar, base, options.ssg.extension) : buildNavItems(markdownFiles, srcDir, base, options.ssg.extension));
10858
+ }
11006
10859
  const html = await renderPage$1(filePath, options, cache.navGroups, cache.siteName, base, root);
11007
10860
  cache.pages.set(filePath, html);
11008
10861
  res.setHeader("Content-Type", "text/html");
@@ -11097,7 +10950,7 @@ async function collectPages(options, root) {
11097
10950
  const generateOgImage = options.ogImage || options.ssg.generateOgImage;
11098
10951
  for (const file of files.sort()) {
11099
10952
  const content = fs$2.readFileSync(file, "utf-8");
11100
- const frontmatter = parseFrontmatter(content);
10953
+ const frontmatter = normalizeVitePressFrontmatter(parseFrontmatter(content));
11101
10954
  if (frontmatter.layout === "entry") continue;
11102
10955
  const title = extractTitle(content, frontmatter);
11103
10956
  const description = typeof frontmatter.description === "string" ? frontmatter.description : "";
@@ -12725,6 +12578,6 @@ function normalizeRuntimeBase(base) {
12725
12578
  return withLeading.endsWith("/") ? withLeading : `${withLeading}/`;
12726
12579
  }
12727
12580
  //#endregion
12728
- export { DEFAULT_HTML_TEMPLATE, DefaultTheme, Fragment, buildSearchIndex, buildSsg, clearRenderContext, collectGitHubRepos, collectOgpUrls, createI18nPlugin, createMarkdownEnvironment, createTheme, defaultTheme, defineTheme, each, extractDocs, extractIslandInfo, extractVideoId, fetchOgpData, fetchRepoData, generateFrontmatterTypes, generateHydrationScript, generateMarkdown, generateOgImages, generateTabsCSS, generateTypes, generateVirtualModule, hasIslands, inferType, jsx, jsxs, lintMarkdown, lintMarkdownAsync, lintMarkdownFile, lintMarkdownFiles, mergeThemes, mermaidClientScript, oxContent, prefetchGitHubRepos, prefetchOgpData, raw, renderAllPages, renderPage, renderToString, resolveDocsOptions, resolveI18nOptions, resolveOgImageOptions, resolveSearchOptions, resolveSsgOptions, resolveTheme, setRenderContext, shouldLintMarkdownFile, transformAllPlugins, transformGitHub, transformIslands, transformMarkdown, transformMermaidStatic, transformOgp, transformTabs, transformYouTube, useIsActive, useNav, usePageProps, useRenderContext, useSiteConfig, when, writeDocs, writeSearchIndex };
12581
+ export { DEFAULT_HTML_TEMPLATE, DefaultTheme, Fragment, buildSearchIndex, buildSsg, clearRenderContext, collectGitHubRepos, collectOgpUrls, convertVitePressNav, convertVitePressSidebar, createI18nPlugin, createMarkdownEnvironment, createTheme, defaultTheme, defineTheme, each, extractDocs, extractIslandInfo, extractVideoId, fetchOgpData, fetchRepoData, fromVitePressConfig, generateFrontmatterTypes, generateHydrationScript, generateMarkdown, generateOgImages, generateTabsCSS, generateTypes, generateVirtualModule, generateVitePressMigrationConfig, hasIslands, inferType, jsx, jsxs, lintMarkdown, lintMarkdownAsync, lintMarkdownFile, lintMarkdownFiles, mergeThemes, mermaidClientScript, normalizeVitePressFrontmatter, oxContent, prefetchGitHubRepos, prefetchOgpData, raw, renderAllPages, renderPage, renderToString, resolveDocsOptions, resolveI18nOptions, resolveOgImageOptions, resolveSearchOptions, resolveSsgOptions, resolveTheme, setRenderContext, shouldLintMarkdownFile, transformAllPlugins, transformGitHub, transformIslands, transformMarkdown, transformMermaidStatic, transformOgp, transformTabs, transformYouTube, useIsActive, useNav, usePageProps, useRenderContext, useSiteConfig, when, writeDocs, writeSearchIndex };
12729
12582
 
12730
12583
  //# sourceMappingURL=index.mjs.map