@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.cjs CHANGED
@@ -5,6 +5,7 @@ const require_tabs = require("./tabs.cjs");
5
5
  const require_youtube = require("./youtube.cjs");
6
6
  const require_github = require("./github.cjs");
7
7
  const require_ogp = require("./ogp.cjs");
8
+ const require_vitepress = require("./vitepress.cjs");
8
9
  let path = require("path");
9
10
  path = require_chunk.__toESM(path);
10
11
  let unified = require("unified");
@@ -8809,213 +8810,6 @@ initIslands((el, props) => {
8809
8810
  `;
8810
8811
  }
8811
8812
  //#endregion
8812
- //#region src/theme.ts
8813
- /**
8814
- * Default theme configuration.
8815
- * Based on the current ox-content SSG styles.
8816
- */
8817
- const defaultTheme = {
8818
- name: "default",
8819
- colors: {
8820
- primary: "#4f6fae",
8821
- primaryHover: "#425f96",
8822
- background: "#ffffff",
8823
- backgroundAlt: "#f5f7fb",
8824
- text: "#131a30",
8825
- textMuted: "#4f607b",
8826
- border: "#d2dbea",
8827
- codeBackground: "#101a31",
8828
- codeText: "#edf3ff"
8829
- },
8830
- darkColors: {
8831
- primary: "#86a4da",
8832
- primaryHover: "#a3bbe8",
8833
- background: "#060816",
8834
- backgroundAlt: "#0d1528",
8835
- text: "#ebf2ff",
8836
- textMuted: "#8ea0bf",
8837
- border: "#223252",
8838
- codeBackground: "#0a1020",
8839
- codeText: "#e7f0ff"
8840
- },
8841
- fonts: {
8842
- sans: "\"IBM Plex Sans\", \"Avenir Next\", \"Segoe UI Variable\", \"Segoe UI\", sans-serif",
8843
- mono: "\"IBM Plex Mono\", \"SFMono-Regular\", Consolas, monospace"
8844
- },
8845
- entryPage: { mode: "default" },
8846
- layout: {
8847
- sidebarWidth: "260px",
8848
- headerHeight: "60px",
8849
- maxContentWidth: "960px"
8850
- },
8851
- header: {
8852
- logo: void 0,
8853
- logoLight: void 0,
8854
- logoDark: void 0,
8855
- showSiteNameText: true,
8856
- logoWidth: 28,
8857
- logoHeight: 28
8858
- },
8859
- footer: {
8860
- message: void 0,
8861
- copyright: void 0
8862
- },
8863
- socialLinks: {},
8864
- embed: {},
8865
- css: "",
8866
- js: ""
8867
- };
8868
- /**
8869
- * Deep merge two objects.
8870
- */
8871
- function deepMerge(target, source) {
8872
- const result = { ...target };
8873
- for (const key of Object.keys(source)) {
8874
- const sourceValue = source[key];
8875
- const targetValue = target[key];
8876
- 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);
8877
- else if (sourceValue !== void 0) result[key] = sourceValue;
8878
- }
8879
- return result;
8880
- }
8881
- /**
8882
- * Defines a theme configuration with type checking.
8883
- *
8884
- * @example
8885
- * ```ts
8886
- * const myTheme = defineTheme({
8887
- * extends: defaultTheme,
8888
- * colors: {
8889
- * primary: '#3498db',
8890
- * },
8891
- * footer: {
8892
- * copyright: '2025 My Company',
8893
- * },
8894
- * });
8895
- * ```
8896
- */
8897
- function defineTheme(config) {
8898
- return config;
8899
- }
8900
- /**
8901
- * Merges multiple theme configurations.
8902
- * Later themes override earlier ones.
8903
- *
8904
- * @example
8905
- * ```ts
8906
- * const merged = mergeThemes(defaultTheme, customTheme, overrides);
8907
- * ```
8908
- */
8909
- function mergeThemes(...themes) {
8910
- if (themes.length === 0) return { ...defaultTheme };
8911
- let result = {};
8912
- for (const theme of themes) result = deepMerge(result, theme);
8913
- return result;
8914
- }
8915
- /**
8916
- * Resolves a theme configuration by merging with its extends chain and defaults.
8917
- */
8918
- function resolveTheme(config) {
8919
- if (!config) return resolveTheme(defaultTheme);
8920
- const chain = [];
8921
- let current = config;
8922
- while (current) {
8923
- chain.unshift(current);
8924
- current = current.extends;
8925
- }
8926
- if (chain[0] !== defaultTheme && chain[0]?.name !== "default") chain.unshift(defaultTheme);
8927
- const merged = mergeThemes(...chain);
8928
- return {
8929
- name: merged.name ?? "custom",
8930
- colors: merged.colors ?? defaultTheme.colors,
8931
- darkColors: merged.darkColors ?? defaultTheme.darkColors,
8932
- fonts: merged.fonts ?? defaultTheme.fonts,
8933
- entryPage: merged.entryPage ?? defaultTheme.entryPage,
8934
- layout: merged.layout ?? defaultTheme.layout,
8935
- header: merged.header ?? defaultTheme.header,
8936
- footer: merged.footer ?? defaultTheme.footer,
8937
- socialLinks: merged.socialLinks ?? defaultTheme.socialLinks,
8938
- sidebar: merged.sidebar ?? [],
8939
- embed: merged.embed ?? {},
8940
- css: merged.css ?? "",
8941
- js: merged.js ?? ""
8942
- };
8943
- }
8944
- /**
8945
- * Converts resolved theme to the format expected by Rust NAPI.
8946
- */
8947
- function themeToNapi(theme) {
8948
- const socialLinks = socialLinksToNapi(theme.socialLinks);
8949
- return {
8950
- colors: theme.colors.primary ? {
8951
- primary: theme.colors.primary,
8952
- primaryHover: theme.colors.primaryHover,
8953
- background: theme.colors.background,
8954
- backgroundAlt: theme.colors.backgroundAlt,
8955
- text: theme.colors.text,
8956
- textMuted: theme.colors.textMuted,
8957
- border: theme.colors.border,
8958
- codeBackground: theme.colors.codeBackground,
8959
- codeText: theme.colors.codeText
8960
- } : void 0,
8961
- darkColors: theme.darkColors.primary ? {
8962
- primary: theme.darkColors.primary,
8963
- primaryHover: theme.darkColors.primaryHover,
8964
- background: theme.darkColors.background,
8965
- backgroundAlt: theme.darkColors.backgroundAlt,
8966
- text: theme.darkColors.text,
8967
- textMuted: theme.darkColors.textMuted,
8968
- border: theme.darkColors.border,
8969
- codeBackground: theme.darkColors.codeBackground,
8970
- codeText: theme.darkColors.codeText
8971
- } : void 0,
8972
- fonts: theme.fonts.sans ? {
8973
- sans: theme.fonts.sans,
8974
- mono: theme.fonts.mono
8975
- } : void 0,
8976
- entryPage: theme.entryPage.mode ? { mode: theme.entryPage.mode } : void 0,
8977
- layout: theme.layout.sidebarWidth ? {
8978
- sidebarWidth: theme.layout.sidebarWidth,
8979
- headerHeight: theme.layout.headerHeight,
8980
- maxContentWidth: theme.layout.maxContentWidth
8981
- } : void 0,
8982
- header: theme.header.logo || theme.header.logoLight || theme.header.logoDark ? {
8983
- logo: theme.header.logo,
8984
- logoLight: theme.header.logoLight,
8985
- logoDark: theme.header.logoDark,
8986
- showSiteNameText: theme.header.showSiteNameText,
8987
- logoWidth: theme.header.logoWidth,
8988
- logoHeight: theme.header.logoHeight
8989
- } : void 0,
8990
- footer: theme.footer.message || theme.footer.copyright ? {
8991
- message: theme.footer.message,
8992
- copyright: theme.footer.copyright
8993
- } : void 0,
8994
- socialLinks,
8995
- embed: Object.keys(theme.embed).length > 0 ? theme.embed : void 0,
8996
- css: theme.css || void 0,
8997
- js: theme.js || void 0
8998
- };
8999
- }
9000
- function socialLinksToNapi(links) {
9001
- if (Array.isArray(links)) {
9002
- const items = links.map((item) => {
9003
- return {
9004
- icon: typeof item.icon === "string" ? item.icon : void 0,
9005
- iconSvg: typeof item.icon === "object" ? item.icon.svg : void 0,
9006
- link: item.link,
9007
- ariaLabel: item.ariaLabel
9008
- };
9009
- });
9010
- return items.length > 0 ? { links: items } : void 0;
9011
- }
9012
- return links.github || links.twitter || links.discord ? {
9013
- github: links.github,
9014
- twitter: links.twitter,
9015
- discord: links.discord
9016
- } : void 0;
9017
- }
9018
- //#endregion
9019
8813
  //#region src/ssg.ts
9020
8814
  /**
9021
8815
  * SSG (Static Site Generation) module for ox-content
@@ -10380,7 +10174,7 @@ function resolveSsgOptions(ssg) {
10380
10174
  bare: false,
10381
10175
  generateOgImage: false,
10382
10176
  lastUpdated: false,
10383
- theme: resolveTheme(void 0)
10177
+ theme: require_vitepress.resolveTheme(void 0)
10384
10178
  };
10385
10179
  return {
10386
10180
  enabled: ssg.enabled ?? true,
@@ -10392,7 +10186,8 @@ function resolveSsgOptions(ssg) {
10392
10186
  generateOgImage: ssg.generateOgImage ?? false,
10393
10187
  lastUpdated: ssg.lastUpdated ?? false,
10394
10188
  siteUrl: ssg.siteUrl,
10395
- theme: resolveTheme(ssg.theme)
10189
+ theme: require_vitepress.resolveTheme(ssg.theme),
10190
+ navigation: ssg.navigation
10396
10191
  };
10397
10192
  }
10398
10193
  /**
@@ -10453,7 +10248,7 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
10453
10248
  collapsed: group.collapsed,
10454
10249
  items: group.items.map(toRustNavItem)
10455
10250
  }));
10456
- const themeForRust = theme ? themeToNapi(theme) : void 0;
10251
+ const themeForRust = theme ? require_vitepress.themeToNapi(theme) : void 0;
10457
10252
  const entryPageForRust = pageData.entryPage ? {
10458
10253
  hero: pageData.entryPage.hero ? {
10459
10254
  name: pageData.entryPage.hero.name,
@@ -10523,6 +10318,59 @@ async function externalizeSharedPageAssets(pages, outDir, base) {
10523
10318
  function getUrlPath$1(inputPath, srcDir) {
10524
10319
  return require_mermaid.importNapiModuleSync().getSsgUrlPath(inputPath, srcDir);
10525
10320
  }
10321
+ function isExternalHref(value) {
10322
+ return /^[a-z][a-z0-9+.-]*:/i.test(value) || value.startsWith("//");
10323
+ }
10324
+ function splitHrefSuffix(value) {
10325
+ const match = /^([^?#]*)([?#].*)?$/.exec(value);
10326
+ return {
10327
+ pathname: match?.[1] ?? value,
10328
+ suffix: match?.[2] ?? ""
10329
+ };
10330
+ }
10331
+ function normalizeNavigationPath(value) {
10332
+ const { pathname, suffix } = splitHrefSuffix(value.trim());
10333
+ let normalized = pathname || "/";
10334
+ if (!normalized.startsWith("/")) normalized = `/${normalized}`;
10335
+ normalized = normalized.replace(/\/index(?:\.(?:html?|md|markdown))?$/i, "/").replace(/\.(?:html?|md|markdown)$/i, "");
10336
+ if (normalized !== "/") normalized = normalized.replace(/\/+$/, "");
10337
+ return {
10338
+ path: normalized || "/",
10339
+ suffix
10340
+ };
10341
+ }
10342
+ function buildHrefFromNavigationPath(pathname, base, extension) {
10343
+ if (pathname === "/" || pathname === "") return `${base}index${extension}`;
10344
+ return `${base}${pathname.replace(/^\/+/, "")}/index${extension}`;
10345
+ }
10346
+ /**
10347
+ * Resolves manual navigation config to the format used by the built-in SSG renderer.
10348
+ */
10349
+ function resolveNavigationGroups(navigation, base, extension) {
10350
+ if (!navigation) return;
10351
+ return navigation.map((group) => ({
10352
+ title: group.title,
10353
+ items: group.items.flatMap((item) => {
10354
+ const rawHref = item.href ?? item.path;
10355
+ if (!rawHref) return [];
10356
+ if (isExternalHref(rawHref) || rawHref.startsWith("#")) return [{
10357
+ title: item.title,
10358
+ path: item.path ?? rawHref,
10359
+ href: rawHref
10360
+ }];
10361
+ const { path: path$2 } = normalizeNavigationPath(item.path ?? rawHref);
10362
+ const href = item.href ? (() => {
10363
+ const normalized = normalizeNavigationPath(item.href);
10364
+ return `${buildHrefFromNavigationPath(normalized.path, base, extension)}${normalized.suffix}`;
10365
+ })() : buildHrefFromNavigationPath(path$2, base, extension);
10366
+ return [{
10367
+ title: item.title,
10368
+ path: path$2,
10369
+ href
10370
+ }];
10371
+ })
10372
+ }));
10373
+ }
10526
10374
  function getPageLocale(urlPath, i18n) {
10527
10375
  if (!i18n) return void 0;
10528
10376
  return require_mermaid.importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, i18n.locales.map((locale) => locale.code)) ?? void 0;
@@ -10583,7 +10431,7 @@ async function buildSsg(options, root) {
10583
10431
  });
10584
10432
  } catch {}
10585
10433
  const markdownFiles = await collectMarkdownFiles$1(srcDir);
10586
- const navItems = ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension);
10434
+ 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));
10587
10435
  let siteName = ssgOptions.siteName ?? "Documentation";
10588
10436
  if (!ssgOptions.siteName) try {
10589
10437
  const pkgPath = path.join(root, "package.json");
@@ -10602,6 +10450,7 @@ async function buildSsg(options, root) {
10602
10450
  baseUrl: base,
10603
10451
  sourcePath: inputPath
10604
10452
  });
10453
+ const frontmatter = require_vitepress.normalizeVitePressFrontmatter(result.frontmatter);
10605
10454
  let transformedHtml = result.html;
10606
10455
  const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(transformedHtml);
10607
10456
  transformedHtml = protectedHtml;
@@ -10616,8 +10465,8 @@ async function buildSsg(options, root) {
10616
10465
  transformedHtml = await transformAllPlugins(transformedHtml, pluginOptions);
10617
10466
  if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
10618
10467
  transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
10619
- const title = extractTitle$1(transformedHtml, result.frontmatter);
10620
- const description = result.frontmatter.description;
10468
+ const title = extractTitle$1(transformedHtml, frontmatter);
10469
+ const description = frontmatter.description;
10621
10470
  const routePaths = getRoutePaths(inputPath, srcDir, outDir, base, ssgOptions.extension, ssgOptions.siteUrl);
10622
10471
  pageResults.push({
10623
10472
  inputPath,
@@ -10626,11 +10475,11 @@ async function buildSsg(options, root) {
10626
10475
  title,
10627
10476
  description,
10628
10477
  lastUpdated: napi?.getGitLastUpdated(inputPath, root) ?? void 0,
10629
- frontmatter: result.frontmatter,
10478
+ frontmatter,
10630
10479
  toc: result.toc
10631
10480
  });
10632
10481
  if (shouldGenerateOgImages) {
10633
- const { layout: _layout, ...frontmatterRest } = result.frontmatter;
10482
+ const { layout: _layout, ...frontmatterRest } = frontmatter;
10634
10483
  ogImageEntries.push({
10635
10484
  props: {
10636
10485
  ...frontmatterRest,
@@ -10957,6 +10806,7 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
10957
10806
  baseUrl: base,
10958
10807
  sourcePath: filePath
10959
10808
  });
10809
+ const frontmatter = require_vitepress.normalizeVitePressFrontmatter(result.frontmatter);
10960
10810
  let transformedHtml = result.html;
10961
10811
  const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(transformedHtml);
10962
10812
  transformedHtml = protectedHtml;
@@ -10970,19 +10820,19 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
10970
10820
  });
10971
10821
  if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
10972
10822
  transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
10973
- const title = extractTitle$1(transformedHtml, result.frontmatter);
10974
- const description = result.frontmatter.description;
10823
+ const title = extractTitle$1(transformedHtml, frontmatter);
10824
+ const description = frontmatter.description;
10975
10825
  let entryPage;
10976
- if (result.frontmatter.layout === "entry") entryPage = {
10977
- hero: result.frontmatter.hero,
10978
- features: result.frontmatter.features
10826
+ if (frontmatter.layout === "entry") entryPage = {
10827
+ hero: frontmatter.hero,
10828
+ features: frontmatter.features
10979
10829
  };
10980
10830
  let html = await generateHtmlPage({
10981
10831
  title,
10982
10832
  description,
10983
10833
  content: transformedHtml,
10984
10834
  toc: result.toc,
10985
- frontmatter: result.frontmatter,
10835
+ frontmatter,
10986
10836
  path: getUrlPath$1(filePath, srcDir),
10987
10837
  href: getUrlPath$1(filePath, srcDir) || "/",
10988
10838
  entryPage
@@ -11013,7 +10863,10 @@ function createDevServerMiddleware(options, root, cache) {
11013
10863
  return;
11014
10864
  }
11015
10865
  if (!cache.siteName) cache.siteName = await resolveSiteName(options, root);
11016
- if (!cache.navGroups) cache.navGroups = buildNavItems(await collectMarkdownFiles$1(srcDir), srcDir, base, ".html");
10866
+ if (!cache.navGroups) {
10867
+ const markdownFiles = await collectMarkdownFiles$1(srcDir);
10868
+ 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));
10869
+ }
11017
10870
  const html = await renderPage$1(filePath, options, cache.navGroups, cache.siteName, base, root);
11018
10871
  cache.pages.set(filePath, html);
11019
10872
  res.setHeader("Content-Type", "text/html");
@@ -11108,7 +10961,7 @@ async function collectPages(options, root) {
11108
10961
  const generateOgImage = options.ogImage || options.ssg.generateOgImage;
11109
10962
  for (const file of files.sort()) {
11110
10963
  const content = fs.readFileSync(file, "utf-8");
11111
- const frontmatter = parseFrontmatter(content);
10964
+ const frontmatter = require_vitepress.normalizeVitePressFrontmatter(parseFrontmatter(content));
11112
10965
  if (frontmatter.layout === "entry") continue;
11113
10966
  const title = extractTitle(content, frontmatter);
11114
10967
  const description = typeof frontmatter.description === "string" ? frontmatter.description : "";
@@ -12744,17 +12597,20 @@ exports.buildSsg = buildSsg;
12744
12597
  exports.clearRenderContext = clearRenderContext;
12745
12598
  exports.collectGitHubRepos = require_github.collectGitHubRepos;
12746
12599
  exports.collectOgpUrls = require_ogp.collectOgpUrls;
12600
+ exports.convertVitePressNav = require_vitepress.convertVitePressNav;
12601
+ exports.convertVitePressSidebar = require_vitepress.convertVitePressSidebar;
12747
12602
  exports.createI18nPlugin = createI18nPlugin;
12748
12603
  exports.createMarkdownEnvironment = createMarkdownEnvironment;
12749
12604
  exports.createTheme = createTheme;
12750
- exports.defaultTheme = defaultTheme;
12751
- exports.defineTheme = defineTheme;
12605
+ exports.defaultTheme = require_vitepress.defaultTheme;
12606
+ exports.defineTheme = require_vitepress.defineTheme;
12752
12607
  exports.each = each;
12753
12608
  exports.extractDocs = extractDocs;
12754
12609
  exports.extractIslandInfo = extractIslandInfo;
12755
12610
  exports.extractVideoId = require_youtube.extractVideoId;
12756
12611
  exports.fetchOgpData = require_ogp.fetchOgpData;
12757
12612
  exports.fetchRepoData = require_github.fetchRepoData;
12613
+ exports.fromVitePressConfig = require_vitepress.fromVitePressConfig;
12758
12614
  exports.generateFrontmatterTypes = generateFrontmatterTypes;
12759
12615
  exports.generateHydrationScript = generateHydrationScript;
12760
12616
  exports.generateMarkdown = generateMarkdown;
@@ -12762,6 +12618,7 @@ exports.generateOgImages = generateOgImages;
12762
12618
  exports.generateTabsCSS = require_tabs.generateTabsCSS;
12763
12619
  exports.generateTypes = generateTypes;
12764
12620
  exports.generateVirtualModule = generateVirtualModule;
12621
+ exports.generateVitePressMigrationConfig = require_vitepress.generateVitePressMigrationConfig;
12765
12622
  exports.hasIslands = hasIslands;
12766
12623
  exports.inferType = inferType;
12767
12624
  exports.jsx = jsx;
@@ -12770,8 +12627,9 @@ exports.lintMarkdown = lintMarkdown;
12770
12627
  exports.lintMarkdownAsync = lintMarkdownAsync;
12771
12628
  exports.lintMarkdownFile = lintMarkdownFile;
12772
12629
  exports.lintMarkdownFiles = lintMarkdownFiles;
12773
- exports.mergeThemes = mergeThemes;
12630
+ exports.mergeThemes = require_vitepress.mergeThemes;
12774
12631
  exports.mermaidClientScript = require_mermaid.mermaidClientScript;
12632
+ exports.normalizeVitePressFrontmatter = require_vitepress.normalizeVitePressFrontmatter;
12775
12633
  exports.oxContent = oxContent;
12776
12634
  exports.prefetchGitHubRepos = require_github.prefetchGitHubRepos;
12777
12635
  exports.prefetchOgpData = require_ogp.prefetchOgpData;
@@ -12784,7 +12642,7 @@ exports.resolveI18nOptions = resolveI18nOptions;
12784
12642
  exports.resolveOgImageOptions = resolveOgImageOptions;
12785
12643
  exports.resolveSearchOptions = resolveSearchOptions;
12786
12644
  exports.resolveSsgOptions = resolveSsgOptions;
12787
- exports.resolveTheme = resolveTheme;
12645
+ exports.resolveTheme = require_vitepress.resolveTheme;
12788
12646
  exports.setRenderContext = setRenderContext;
12789
12647
  exports.shouldLintMarkdownFile = shouldLintMarkdownFile;
12790
12648
  exports.transformAllPlugins = transformAllPlugins;