@ox-content/vite-plugin 2.9.0 → 2.11.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");
@@ -25,6 +26,40 @@ crypto = require_chunk.__toESM(crypto);
25
26
  let node_module = require("node:module");
26
27
  let node_fs_promises = require("node:fs/promises");
27
28
  node_fs_promises = require_chunk.__toESM(node_fs_promises);
29
+ //#region src/markdown.ts
30
+ const DEFAULT_MARKDOWN_EXTENSIONS = [
31
+ ".md",
32
+ ".markdown",
33
+ ".mdx"
34
+ ];
35
+ function normalizeMarkdownExtensions(extensions) {
36
+ const values = extensions?.length ? extensions : DEFAULT_MARKDOWN_EXTENSIONS;
37
+ const seen = /* @__PURE__ */ new Set();
38
+ const normalized = [];
39
+ for (const extension of values) {
40
+ const value = extension.startsWith(".") ? extension : `.${extension}`;
41
+ const key = value.toLowerCase();
42
+ if (!seen.has(key)) {
43
+ seen.add(key);
44
+ normalized.push(value);
45
+ }
46
+ }
47
+ return normalized;
48
+ }
49
+ function isMarkdownFilePath(filePath, extensions = DEFAULT_MARKDOWN_EXTENSIONS) {
50
+ const pathname = filePath.split("?")[0].split("#")[0].toLowerCase();
51
+ return extensions.some((extension) => pathname.endsWith(extension.toLowerCase()));
52
+ }
53
+ function stripMarkdownExtension(filePath, extensions = DEFAULT_MARKDOWN_EXTENSIONS) {
54
+ const match = [...extensions].sort((left, right) => right.length - left.length).find((extension) => filePath.toLowerCase().endsWith(extension.toLowerCase()));
55
+ return match ? filePath.slice(0, -match.length) : filePath;
56
+ }
57
+ function markdownGlobPattern(srcDir, extensions) {
58
+ const suffixes = extensions.map((extension) => extension.replace(/^\./, ""));
59
+ if (suffixes.length === 1) return path.join(srcDir, `**/*.${suffixes[0]}`);
60
+ return path.join(srcDir, `**/*.{${suffixes.join(",")}}`);
61
+ }
62
+ //#endregion
28
63
  //#region src/environment.ts
29
64
  /**
30
65
  * Creates the Markdown processing environment configuration.
@@ -57,7 +92,7 @@ function createMarkdownEnvironment(options) {
57
92
  rollupOptions: { external: [/^node:/, /\.node$/] }
58
93
  },
59
94
  resolve: {
60
- extensions: [".md", ".markdown"],
95
+ extensions: options.extensions,
61
96
  conditions: [
62
97
  "markdown",
63
98
  "node",
@@ -6808,6 +6843,58 @@ async function highlightCode(html, theme = "github-dark", langs = []) {
6808
6843
  return String(result);
6809
6844
  }
6810
6845
  //#endregion
6846
+ //#region src/plugins/index.ts
6847
+ /**
6848
+ * Transform all enabled plugins in HTML content.
6849
+ */
6850
+ async function transformAllPlugins(html, options = {}) {
6851
+ const { tabs = true, youtube = true, github = true, ogp, openGraph, mermaid = true, githubToken } = options;
6852
+ let result = html;
6853
+ const ogpOptions = openGraph ?? ogp ?? true;
6854
+ if (tabs) {
6855
+ const { transformTabs } = await Promise.resolve().then(() => require("./tabs.cjs")).then((n) => n.tabs_exports);
6856
+ result = await transformTabs(result);
6857
+ }
6858
+ if (youtube) {
6859
+ const { transformYouTube } = await Promise.resolve().then(() => require("./youtube.cjs")).then((n) => n.youtube_exports);
6860
+ result = await transformYouTube(result);
6861
+ }
6862
+ if (github !== false) {
6863
+ const { transformGitHub } = await Promise.resolve().then(() => require("./github.cjs")).then((n) => n.github_exports);
6864
+ result = await transformGitHub(result, void 0, {
6865
+ token: githubToken,
6866
+ ...typeof github === "object" ? github : {}
6867
+ });
6868
+ }
6869
+ if (ogpOptions !== false) {
6870
+ const { transformOgp } = await Promise.resolve().then(() => require("./ogp.cjs")).then((n) => n.ogp_exports);
6871
+ result = await transformOgp(result, void 0, typeof ogpOptions === "object" ? ogpOptions : {});
6872
+ }
6873
+ if (mermaid) {
6874
+ const { transformMermaidStatic } = await Promise.resolve().then(() => require("./mermaid.cjs")).then((n) => n.mermaid_exports);
6875
+ result = await transformMermaidStatic(result);
6876
+ }
6877
+ return result;
6878
+ }
6879
+ /**
6880
+ * Transform built-in embed components in HTML content.
6881
+ */
6882
+ async function transformBuiltinEmbeds(html, options) {
6883
+ let result = html;
6884
+ if (options.github) {
6885
+ const { transformGitHub } = await Promise.resolve().then(() => require("./github.cjs")).then((n) => n.github_exports);
6886
+ result = await transformGitHub(result, void 0, {
6887
+ token: process.env.GITHUB_TOKEN,
6888
+ ...options.github
6889
+ });
6890
+ }
6891
+ if (options.openGraph) {
6892
+ const { transformOgp } = await Promise.resolve().then(() => require("./ogp.cjs")).then((n) => n.ogp_exports);
6893
+ result = await transformOgp(result, void 0, options.openGraph);
6894
+ }
6895
+ return result;
6896
+ }
6897
+ //#endregion
6811
6898
  //#region src/plugins/mermaid-protect.ts
6812
6899
  /**
6813
6900
  * Extract `<div class="ox-mermaid">...</div>` blocks and replace
@@ -6989,6 +7076,10 @@ async function transformMarkdown(source, filePath, options, ssgOptions) {
6989
7076
  const highlightedHtml = await highlightCode(html, options.highlightTheme, options.highlightLangs);
6990
7077
  html = napi.mergeHighlightedCodeBlocks(originalHtml, highlightedHtml);
6991
7078
  }
7079
+ html = await transformBuiltinEmbeds(html, options.embeds ?? {
7080
+ github: {},
7081
+ openGraph: {}
7082
+ });
6992
7083
  html = restoreMermaidSvgs(html, svgs);
6993
7084
  return {
6994
7085
  code: generateModuleCode(html, frontmatter, toc, filePath, options),
@@ -8588,36 +8679,6 @@ async function renderSinglePage(entry, templateFn, templateSource, options, cach
8588
8679
  }
8589
8680
  }
8590
8681
  //#endregion
8591
- //#region src/plugins/index.ts
8592
- /**
8593
- * Transform all enabled plugins in HTML content.
8594
- */
8595
- async function transformAllPlugins(html, options = {}) {
8596
- const { tabs = true, youtube = true, github = true, ogp = true, mermaid = true, githubToken } = options;
8597
- let result = html;
8598
- if (tabs) {
8599
- const { transformTabs } = await Promise.resolve().then(() => require("./tabs.cjs")).then((n) => n.tabs_exports);
8600
- result = await transformTabs(result);
8601
- }
8602
- if (youtube) {
8603
- const { transformYouTube } = await Promise.resolve().then(() => require("./youtube.cjs")).then((n) => n.youtube_exports);
8604
- result = await transformYouTube(result);
8605
- }
8606
- if (github) {
8607
- const { transformGitHub } = await Promise.resolve().then(() => require("./github.cjs")).then((n) => n.github_exports);
8608
- result = await transformGitHub(result, void 0, { token: githubToken });
8609
- }
8610
- if (ogp) {
8611
- const { transformOgp } = await Promise.resolve().then(() => require("./ogp.cjs")).then((n) => n.ogp_exports);
8612
- result = await transformOgp(result);
8613
- }
8614
- if (mermaid) {
8615
- const { transformMermaidStatic } = await Promise.resolve().then(() => require("./mermaid.cjs")).then((n) => n.mermaid_exports);
8616
- result = await transformMermaidStatic(result);
8617
- }
8618
- return result;
8619
- }
8620
- //#endregion
8621
8682
  //#region src/island/parse.ts
8622
8683
  /**
8623
8684
  * Island Parser
@@ -8809,213 +8870,6 @@ initIslands((el, props) => {
8809
8870
  `;
8810
8871
  }
8811
8872
  //#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
8873
  //#region src/ssg.ts
9020
8874
  /**
9021
8875
  * SSG (Static Site Generation) module for ox-content
@@ -10380,7 +10234,7 @@ function resolveSsgOptions(ssg) {
10380
10234
  bare: false,
10381
10235
  generateOgImage: false,
10382
10236
  lastUpdated: false,
10383
- theme: resolveTheme(void 0)
10237
+ theme: require_vitepress.resolveTheme(void 0)
10384
10238
  };
10385
10239
  return {
10386
10240
  enabled: ssg.enabled ?? true,
@@ -10392,7 +10246,8 @@ function resolveSsgOptions(ssg) {
10392
10246
  generateOgImage: ssg.generateOgImage ?? false,
10393
10247
  lastUpdated: ssg.lastUpdated ?? false,
10394
10248
  siteUrl: ssg.siteUrl,
10395
- theme: resolveTheme(ssg.theme)
10249
+ theme: require_vitepress.resolveTheme(ssg.theme),
10250
+ navigation: ssg.navigation
10396
10251
  };
10397
10252
  }
10398
10253
  /**
@@ -10453,7 +10308,7 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
10453
10308
  collapsed: group.collapsed,
10454
10309
  items: group.items.map(toRustNavItem)
10455
10310
  }));
10456
- const themeForRust = theme ? themeToNapi(theme) : void 0;
10311
+ const themeForRust = theme ? require_vitepress.themeToNapi(theme) : void 0;
10457
10312
  const entryPageForRust = pageData.entryPage ? {
10458
10313
  hero: pageData.entryPage.hero ? {
10459
10314
  name: pageData.entryPage.hero.name,
@@ -10523,6 +10378,59 @@ async function externalizeSharedPageAssets(pages, outDir, base) {
10523
10378
  function getUrlPath$1(inputPath, srcDir) {
10524
10379
  return require_mermaid.importNapiModuleSync().getSsgUrlPath(inputPath, srcDir);
10525
10380
  }
10381
+ function isExternalHref(value) {
10382
+ return /^[a-z][a-z0-9+.-]*:/i.test(value) || value.startsWith("//");
10383
+ }
10384
+ function splitHrefSuffix(value) {
10385
+ const match = /^([^?#]*)([?#].*)?$/.exec(value);
10386
+ return {
10387
+ pathname: match?.[1] ?? value,
10388
+ suffix: match?.[2] ?? ""
10389
+ };
10390
+ }
10391
+ function normalizeNavigationPath(value) {
10392
+ const { pathname, suffix } = splitHrefSuffix(value.trim());
10393
+ let normalized = pathname || "/";
10394
+ if (!normalized.startsWith("/")) normalized = `/${normalized}`;
10395
+ normalized = normalized.replace(/\/index(?:\.(?:html?|md|markdown))?$/i, "/").replace(/\.(?:html?|md|markdown)$/i, "");
10396
+ if (normalized !== "/") normalized = normalized.replace(/\/+$/, "");
10397
+ return {
10398
+ path: normalized || "/",
10399
+ suffix
10400
+ };
10401
+ }
10402
+ function buildHrefFromNavigationPath(pathname, base, extension) {
10403
+ if (pathname === "/" || pathname === "") return `${base}index${extension}`;
10404
+ return `${base}${pathname.replace(/^\/+/, "")}/index${extension}`;
10405
+ }
10406
+ /**
10407
+ * Resolves manual navigation config to the format used by the built-in SSG renderer.
10408
+ */
10409
+ function resolveNavigationGroups(navigation, base, extension) {
10410
+ if (!navigation) return;
10411
+ return navigation.map((group) => ({
10412
+ title: group.title,
10413
+ items: group.items.flatMap((item) => {
10414
+ const rawHref = item.href ?? item.path;
10415
+ if (!rawHref) return [];
10416
+ if (isExternalHref(rawHref) || rawHref.startsWith("#")) return [{
10417
+ title: item.title,
10418
+ path: item.path ?? rawHref,
10419
+ href: rawHref
10420
+ }];
10421
+ const { path: path$2 } = normalizeNavigationPath(item.path ?? rawHref);
10422
+ const href = item.href ? (() => {
10423
+ const normalized = normalizeNavigationPath(item.href);
10424
+ return `${buildHrefFromNavigationPath(normalized.path, base, extension)}${normalized.suffix}`;
10425
+ })() : buildHrefFromNavigationPath(path$2, base, extension);
10426
+ return [{
10427
+ title: item.title,
10428
+ path: path$2,
10429
+ href
10430
+ }];
10431
+ })
10432
+ }));
10433
+ }
10526
10434
  function getPageLocale(urlPath, i18n) {
10527
10435
  if (!i18n) return void 0;
10528
10436
  return require_mermaid.importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, i18n.locales.map((locale) => locale.code)) ?? void 0;
@@ -10539,8 +10447,8 @@ function formatTitle(name) {
10539
10447
  /**
10540
10448
  * Collects all markdown files from the source directory.
10541
10449
  */
10542
- async function collectMarkdownFiles$1(srcDir) {
10543
- return (await (0, glob.glob)(path.join(srcDir, "**/*.{md,markdown}"), {
10450
+ async function collectMarkdownFiles$1(srcDir, extensions = DEFAULT_MARKDOWN_EXTENSIONS) {
10451
+ return (await (0, glob.glob)(markdownGlobPattern(srcDir, extensions), {
10544
10452
  nodir: true,
10545
10453
  ignore: [
10546
10454
  "**/node_modules/**",
@@ -10582,8 +10490,8 @@ async function buildSsg(options, root) {
10582
10490
  force: true
10583
10491
  });
10584
10492
  } catch {}
10585
- 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);
10493
+ const markdownFiles = await collectMarkdownFiles$1(srcDir, options.extensions);
10494
+ 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
10495
  let siteName = ssgOptions.siteName ?? "Documentation";
10588
10496
  if (!ssgOptions.siteName) try {
10589
10497
  const pkgPath = path.join(root, "package.json");
@@ -10602,22 +10510,23 @@ async function buildSsg(options, root) {
10602
10510
  baseUrl: base,
10603
10511
  sourcePath: inputPath
10604
10512
  });
10513
+ const frontmatter = require_vitepress.normalizeVitePressFrontmatter(result.frontmatter);
10605
10514
  let transformedHtml = result.html;
10606
10515
  const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(transformedHtml);
10607
10516
  transformedHtml = protectedHtml;
10608
10517
  const pluginOptions = {
10609
10518
  tabs: true,
10610
10519
  youtube: true,
10611
- github: true,
10612
- ogp: true,
10520
+ github: options.embeds.github,
10521
+ openGraph: options.embeds.openGraph,
10613
10522
  mermaid: true,
10614
10523
  githubToken: process.env.GITHUB_TOKEN
10615
10524
  };
10616
10525
  transformedHtml = await transformAllPlugins(transformedHtml, pluginOptions);
10617
10526
  if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
10618
10527
  transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
10619
- const title = extractTitle$1(transformedHtml, result.frontmatter);
10620
- const description = result.frontmatter.description;
10528
+ const title = extractTitle$1(transformedHtml, frontmatter);
10529
+ const description = frontmatter.description;
10621
10530
  const routePaths = getRoutePaths(inputPath, srcDir, outDir, base, ssgOptions.extension, ssgOptions.siteUrl);
10622
10531
  pageResults.push({
10623
10532
  inputPath,
@@ -10626,11 +10535,11 @@ async function buildSsg(options, root) {
10626
10535
  title,
10627
10536
  description,
10628
10537
  lastUpdated: napi?.getGitLastUpdated(inputPath, root) ?? void 0,
10629
- frontmatter: result.frontmatter,
10538
+ frontmatter,
10630
10539
  toc: result.toc
10631
10540
  });
10632
10541
  if (shouldGenerateOgImages) {
10633
- const { layout: _layout, ...frontmatterRest } = result.frontmatter;
10542
+ const { layout: _layout, ...frontmatterRest } = frontmatter;
10634
10543
  ogImageEntries.push({
10635
10544
  props: {
10636
10545
  ...frontmatterRest,
@@ -10755,7 +10664,7 @@ function resolveSearchOptions(options) {
10755
10664
  /**
10756
10665
  * Collects all Markdown files from a directory.
10757
10666
  */
10758
- async function collectMarkdownFiles(dir) {
10667
+ async function collectMarkdownFiles(dir, extensions = DEFAULT_MARKDOWN_EXTENSIONS) {
10759
10668
  const files = [];
10760
10669
  async function walk(currentDir) {
10761
10670
  try {
@@ -10763,7 +10672,7 @@ async function collectMarkdownFiles(dir) {
10763
10672
  for (const entry of entries) {
10764
10673
  const fullPath = path.join(currentDir, entry.name);
10765
10674
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") await walk(fullPath);
10766
- else if (entry.isFile() && entry.name.endsWith(".md")) files.push(fullPath);
10675
+ else if (entry.isFile() && isMarkdownFilePath(entry.name, extensions)) files.push(fullPath);
10767
10676
  }
10768
10677
  } catch {}
10769
10678
  }
@@ -10773,7 +10682,7 @@ async function collectMarkdownFiles(dir) {
10773
10682
  /**
10774
10683
  * Builds the search index from Markdown files.
10775
10684
  */
10776
- async function buildSearchIndex(srcDir, base) {
10685
+ async function buildSearchIndex(srcDir, base, extensions = DEFAULT_MARKDOWN_EXTENSIONS) {
10777
10686
  const napi = await getOxContent();
10778
10687
  if (!napi) return JSON.stringify({
10779
10688
  documents: [],
@@ -10782,13 +10691,12 @@ async function buildSearchIndex(srcDir, base) {
10782
10691
  avg_dl: 0,
10783
10692
  doc_count: 0
10784
10693
  });
10785
- const files = await collectMarkdownFiles(srcDir);
10694
+ const files = await collectMarkdownFiles(srcDir, extensions);
10786
10695
  const documents = [];
10787
10696
  for (const file of files) try {
10788
10697
  const content = await fs_promises.readFile(file, "utf-8");
10789
- const relativePath = path.relative(srcDir, file);
10790
- const url = base + relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
10791
- const id = relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
10698
+ const id = stripMarkdownExtension(path.relative(srcDir, file), extensions).replace(/\\/g, "/");
10699
+ const url = base + id;
10792
10700
  const extractSearchContent = napi.extractSearchContent;
10793
10701
  if (!extractSearchContent) {
10794
10702
  console.warn("[ox-content] Search not available: extractSearchContent not implemented");
@@ -10883,26 +10791,27 @@ function shouldSkip(url) {
10883
10791
  * Resolve a request URL to a markdown file path.
10884
10792
  * Returns null if no matching file exists.
10885
10793
  */
10886
- async function resolveMarkdownFile(url, srcDir) {
10794
+ async function resolveMarkdownFile(url, srcDir, extensions) {
10887
10795
  let pathname = url.split("?")[0].split("#")[0];
10888
10796
  if (pathname.endsWith("/index.html")) pathname = pathname.slice(0, -11) || "/";
10889
10797
  if (pathname !== "/" && pathname.endsWith("/")) pathname = pathname.slice(0, -1);
10890
- let relativePath;
10891
- if (pathname === "/") relativePath = "index.md";
10892
- else relativePath = pathname.slice(1) + ".md";
10893
- const filePath = path.join(srcDir, relativePath);
10894
- try {
10895
- await fs_promises.access(filePath);
10896
- return filePath;
10897
- } catch {
10898
- const indexPath = path.join(srcDir, pathname === "/" ? "" : pathname.slice(1), "index.md");
10798
+ const routePath = pathname === "/" ? "" : pathname.slice(1);
10799
+ const directCandidates = pathname === "/" ? extensions.map((extension) => `index${extension}`) : isMarkdownFilePath(routePath, extensions) ? [routePath] : extensions.map((extension) => `${routePath}${extension}`);
10800
+ for (const relativePath of directCandidates) {
10801
+ const filePath = path.join(srcDir, relativePath);
10802
+ try {
10803
+ await fs_promises.access(filePath);
10804
+ return filePath;
10805
+ } catch {}
10806
+ }
10807
+ for (const extension of extensions) {
10808
+ const indexPath = path.join(srcDir, routePath, `index${extension}`);
10899
10809
  try {
10900
10810
  await fs_promises.access(indexPath);
10901
10811
  return indexPath;
10902
- } catch {
10903
- return null;
10904
- }
10812
+ } catch {}
10905
10813
  }
10814
+ return null;
10906
10815
  }
10907
10816
  /**
10908
10817
  * Inject Vite HMR client script into the HTML.
@@ -10957,32 +10866,33 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
10957
10866
  baseUrl: base,
10958
10867
  sourcePath: filePath
10959
10868
  });
10869
+ const frontmatter = require_vitepress.normalizeVitePressFrontmatter(result.frontmatter);
10960
10870
  let transformedHtml = result.html;
10961
10871
  const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(transformedHtml);
10962
10872
  transformedHtml = protectedHtml;
10963
10873
  transformedHtml = await transformAllPlugins(transformedHtml, {
10964
10874
  tabs: true,
10965
10875
  youtube: true,
10966
- github: true,
10967
- ogp: true,
10876
+ github: options.embeds.github,
10877
+ openGraph: options.embeds.openGraph,
10968
10878
  mermaid: true,
10969
10879
  githubToken: process.env.GITHUB_TOKEN
10970
10880
  });
10971
10881
  if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
10972
10882
  transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
10973
- const title = extractTitle$1(transformedHtml, result.frontmatter);
10974
- const description = result.frontmatter.description;
10883
+ const title = extractTitle$1(transformedHtml, frontmatter);
10884
+ const description = frontmatter.description;
10975
10885
  let entryPage;
10976
- if (result.frontmatter.layout === "entry") entryPage = {
10977
- hero: result.frontmatter.hero,
10978
- features: result.frontmatter.features
10886
+ if (frontmatter.layout === "entry") entryPage = {
10887
+ hero: frontmatter.hero,
10888
+ features: frontmatter.features
10979
10889
  };
10980
10890
  let html = await generateHtmlPage({
10981
10891
  title,
10982
10892
  description,
10983
10893
  content: transformedHtml,
10984
10894
  toc: result.toc,
10985
- frontmatter: result.frontmatter,
10895
+ frontmatter,
10986
10896
  path: getUrlPath$1(filePath, srcDir),
10987
10897
  href: getUrlPath$1(filePath, srcDir) || "/",
10988
10898
  entryPage
@@ -11002,7 +10912,7 @@ function createDevServerMiddleware(options, root, cache) {
11002
10912
  let routeUrl = url;
11003
10913
  if (base !== "/" && routeUrl.startsWith(base)) routeUrl = "/" + routeUrl.slice(base.length);
11004
10914
  if (shouldSkip(routeUrl)) return next();
11005
- const filePath = await resolveMarkdownFile(routeUrl, srcDir);
10915
+ const filePath = await resolveMarkdownFile(routeUrl, srcDir, options.extensions);
11006
10916
  if (!filePath) return next();
11007
10917
  try {
11008
10918
  const cached = cache.pages.get(filePath);
@@ -11013,7 +10923,10 @@ function createDevServerMiddleware(options, root, cache) {
11013
10923
  return;
11014
10924
  }
11015
10925
  if (!cache.siteName) cache.siteName = await resolveSiteName(options, root);
11016
- if (!cache.navGroups) cache.navGroups = buildNavItems(await collectMarkdownFiles$1(srcDir), srcDir, base, ".html");
10926
+ if (!cache.navGroups) {
10927
+ const markdownFiles = await collectMarkdownFiles$1(srcDir, options.extensions);
10928
+ 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));
10929
+ }
11017
10930
  const html = await renderPage$1(filePath, options, cache.navGroups, cache.siteName, base, root);
11018
10931
  cache.pages.set(filePath, html);
11019
10932
  res.setHeader("Content-Type", "text/html");
@@ -11058,9 +10971,9 @@ function extractTitle(content, frontmatter) {
11058
10971
  const match = content.match(/^#\s+(.+)$/m);
11059
10972
  return match ? match[1].trim() : "";
11060
10973
  }
11061
- function getUrlPath(filePath, srcDir) {
10974
+ function getUrlPath(filePath, srcDir, extensions) {
11062
10975
  let rel = path.relative(srcDir, filePath).replace(/\\/g, "/");
11063
- rel = rel.replace(/\.md$/, "");
10976
+ rel = stripMarkdownExtension(rel, extensions);
11064
10977
  if (rel === "index") return "/";
11065
10978
  if (rel.endsWith("/index")) rel = rel.slice(0, -6);
11066
10979
  return "/" + rel;
@@ -11100,21 +11013,18 @@ function validatePage(page, options) {
11100
11013
  }
11101
11014
  async function collectPages(options, root) {
11102
11015
  const srcDir = path.resolve(root, options.srcDir);
11103
- const files = await (0, glob.glob)("**/*.md", {
11104
- cwd: srcDir,
11105
- absolute: true
11106
- });
11016
+ const files = await (0, glob.glob)(markdownGlobPattern(srcDir, options.extensions), { absolute: true });
11107
11017
  const pages = [];
11108
11018
  const generateOgImage = options.ogImage || options.ssg.generateOgImage;
11109
11019
  for (const file of files.sort()) {
11110
11020
  const content = fs.readFileSync(file, "utf-8");
11111
- const frontmatter = parseFrontmatter(content);
11021
+ const frontmatter = require_vitepress.normalizeVitePressFrontmatter(parseFrontmatter(content));
11112
11022
  if (frontmatter.layout === "entry") continue;
11113
11023
  const title = extractTitle(content, frontmatter);
11114
11024
  const description = typeof frontmatter.description === "string" ? frontmatter.description : "";
11115
11025
  const author = typeof frontmatter.author === "string" ? frontmatter.author : "";
11116
11026
  const tags = Array.isArray(frontmatter.tags) ? frontmatter.tags : typeof frontmatter.tags === "string" ? [frontmatter.tags] : [];
11117
- const urlPath = getUrlPath(file, srcDir);
11027
+ const urlPath = getUrlPath(file, srcDir, options.extensions);
11118
11028
  const ogImageUrl = computeOgImageUrl(urlPath, options.base, options.ssg.siteUrl, generateOgImage, options.ssg.ogImage);
11119
11029
  const page = {
11120
11030
  path: path.relative(srcDir, file),
@@ -11763,7 +11673,11 @@ function sortDiagnostics(diagnostics) {
11763
11673
  }
11764
11674
  //#endregion
11765
11675
  //#region src/lint-files.ts
11766
- const DEFAULT_LINT_FILE_INCLUDE = ["**/*.md", "**/*.markdown"];
11676
+ const DEFAULT_LINT_FILE_INCLUDE = [
11677
+ "**/*.md",
11678
+ "**/*.markdown",
11679
+ "**/*.mdx"
11680
+ ];
11767
11681
  const DEFAULT_LINT_FILE_EXCLUDE = [
11768
11682
  "**/node_modules/**",
11769
11683
  "**/.git/**",
@@ -12470,13 +12384,13 @@ function oxContent(options = {}) {
12470
12384
  configureServer(devServer) {
12471
12385
  devServer.middlewares.use(async (req, res, next) => {
12472
12386
  const url = req.url;
12473
- if (!url || !url.endsWith(".md")) return next();
12387
+ if (!url || !isMarkdownFilePath(url, resolvedOptions.extensions)) return next();
12474
12388
  next();
12475
12389
  });
12476
12390
  },
12477
12391
  resolveId(id) {
12478
12392
  if (id.startsWith("virtual:ox-content/")) return "\0" + id;
12479
- if (id.endsWith(".md")) return id;
12393
+ if (isMarkdownFilePath(id, resolvedOptions.extensions)) return id;
12480
12394
  return null;
12481
12395
  },
12482
12396
  async load(id) {
@@ -12484,14 +12398,14 @@ function oxContent(options = {}) {
12484
12398
  return null;
12485
12399
  },
12486
12400
  async transform(code, id) {
12487
- if (!id.endsWith(".md")) return null;
12401
+ if (!isMarkdownFilePath(id, resolvedOptions.extensions)) return null;
12488
12402
  return {
12489
12403
  code: (await transformMarkdown(code, id, resolvedOptions)).code,
12490
12404
  map: null
12491
12405
  };
12492
12406
  },
12493
12407
  async handleHotUpdate({ file, server }) {
12494
- if (file.endsWith(".md")) {
12408
+ if (isMarkdownFilePath(file, resolvedOptions.extensions)) {
12495
12409
  server.ws.send({
12496
12410
  type: "custom",
12497
12411
  event: "ox-content:update",
@@ -12544,7 +12458,7 @@ function oxContent(options = {}) {
12544
12458
  const srcDir = path.resolve(root, resolvedOptions.srcDir);
12545
12459
  devServer.middlewares.use(createDevServerMiddleware(resolvedOptions, root, ssgDevCache));
12546
12460
  devServer.watcher.on("add", (file) => {
12547
- if (file.startsWith(srcDir) && file.endsWith(".md")) {
12461
+ if (file.startsWith(srcDir) && isMarkdownFilePath(file, resolvedOptions.extensions)) {
12548
12462
  invalidateNavCache(ssgDevCache);
12549
12463
  devServer.ws.send({
12550
12464
  type: "custom",
@@ -12557,7 +12471,7 @@ function oxContent(options = {}) {
12557
12471
  }
12558
12472
  });
12559
12473
  devServer.watcher.on("unlink", (file) => {
12560
- if (file.startsWith(srcDir) && file.endsWith(".md")) {
12474
+ if (file.startsWith(srcDir) && isMarkdownFilePath(file, resolvedOptions.extensions)) {
12561
12475
  invalidateNavCache(ssgDevCache);
12562
12476
  devServer.ws.send({
12563
12477
  type: "custom",
@@ -12570,7 +12484,7 @@ function oxContent(options = {}) {
12570
12484
  }
12571
12485
  });
12572
12486
  devServer.watcher.on("change", (file) => {
12573
- if (file.startsWith(srcDir) && file.endsWith(".md")) invalidatePageCache(ssgDevCache, file);
12487
+ if (file.startsWith(srcDir) && isMarkdownFilePath(file, resolvedOptions.extensions)) invalidatePageCache(ssgDevCache, file);
12574
12488
  });
12575
12489
  },
12576
12490
  async closeBundle() {
@@ -12610,7 +12524,7 @@ function oxContent(options = {}) {
12610
12524
  const root = config?.root || process.cwd();
12611
12525
  const srcDir = path.resolve(root, resolvedOptions.srcDir);
12612
12526
  try {
12613
- searchIndexJson = await buildSearchIndex(srcDir, resolvedOptions.base);
12527
+ searchIndexJson = await buildSearchIndex(srcDir, resolvedOptions.base, resolvedOptions.extensions);
12614
12528
  console.log("[ox-content] Search index built");
12615
12529
  } catch (err) {
12616
12530
  console.warn("[ox-content] Failed to build search index:", err);
@@ -12641,6 +12555,7 @@ function resolveOptions(options) {
12641
12555
  srcDir: options.srcDir ?? "content",
12642
12556
  outDir: options.outDir ?? "dist",
12643
12557
  base: options.base ?? "/",
12558
+ extensions: normalizeMarkdownExtensions(options.extensions),
12644
12559
  ssg: resolveSsgOptions(options.ssg),
12645
12560
  gfm: options.gfm ?? true,
12646
12561
  footnotes: options.footnotes ?? true,
@@ -12661,9 +12576,25 @@ function resolveOptions(options) {
12661
12576
  docs: resolveDocsOptions(options.docs),
12662
12577
  search: resolveSearchOptions(options.search),
12663
12578
  ogViewer: options.ogViewer ?? true,
12579
+ embeds: resolveBuiltinEmbedOptions(options.embeds),
12664
12580
  i18n: resolveI18nOptions(options.i18n)
12665
12581
  };
12666
12582
  }
12583
+ function resolveBuiltinEmbedOptions(options) {
12584
+ if (options === false) return {
12585
+ github: false,
12586
+ openGraph: false
12587
+ };
12588
+ return {
12589
+ github: resolveSingleEmbedOptions(options?.github),
12590
+ openGraph: resolveSingleEmbedOptions(options?.openGraph)
12591
+ };
12592
+ }
12593
+ function resolveSingleEmbedOptions(options) {
12594
+ if (options === false) return false;
12595
+ if (options === true || options === void 0) return {};
12596
+ return options;
12597
+ }
12667
12598
  function resolveCodeAnnotationsOptions(options) {
12668
12599
  if (!options) return {
12669
12600
  enabled: false,
@@ -12737,24 +12668,30 @@ function normalizeRuntimeBase(base) {
12737
12668
  }
12738
12669
  //#endregion
12739
12670
  exports.DEFAULT_HTML_TEMPLATE = DEFAULT_HTML_TEMPLATE;
12671
+ exports.DEFAULT_MARKDOWN_EXTENSIONS = DEFAULT_MARKDOWN_EXTENSIONS;
12740
12672
  exports.DefaultTheme = DefaultTheme;
12741
12673
  exports.Fragment = Fragment;
12742
12674
  exports.buildSearchIndex = buildSearchIndex;
12743
12675
  exports.buildSsg = buildSsg;
12744
12676
  exports.clearRenderContext = clearRenderContext;
12745
12677
  exports.collectGitHubRepos = require_github.collectGitHubRepos;
12678
+ exports.collectGitHubSources = require_github.collectGitHubSources;
12746
12679
  exports.collectOgpUrls = require_ogp.collectOgpUrls;
12680
+ exports.convertVitePressNav = require_vitepress.convertVitePressNav;
12681
+ exports.convertVitePressSidebar = require_vitepress.convertVitePressSidebar;
12747
12682
  exports.createI18nPlugin = createI18nPlugin;
12748
12683
  exports.createMarkdownEnvironment = createMarkdownEnvironment;
12749
12684
  exports.createTheme = createTheme;
12750
- exports.defaultTheme = defaultTheme;
12751
- exports.defineTheme = defineTheme;
12685
+ exports.defaultTheme = require_vitepress.defaultTheme;
12686
+ exports.defineTheme = require_vitepress.defineTheme;
12752
12687
  exports.each = each;
12753
12688
  exports.extractDocs = extractDocs;
12754
12689
  exports.extractIslandInfo = extractIslandInfo;
12755
12690
  exports.extractVideoId = require_youtube.extractVideoId;
12691
+ exports.fetchGitHubSource = require_github.fetchGitHubSource;
12756
12692
  exports.fetchOgpData = require_ogp.fetchOgpData;
12757
12693
  exports.fetchRepoData = require_github.fetchRepoData;
12694
+ exports.fromVitePressConfig = require_vitepress.fromVitePressConfig;
12758
12695
  exports.generateFrontmatterTypes = generateFrontmatterTypes;
12759
12696
  exports.generateHydrationScript = generateHydrationScript;
12760
12697
  exports.generateMarkdown = generateMarkdown;
@@ -12762,31 +12699,40 @@ exports.generateOgImages = generateOgImages;
12762
12699
  exports.generateTabsCSS = require_tabs.generateTabsCSS;
12763
12700
  exports.generateTypes = generateTypes;
12764
12701
  exports.generateVirtualModule = generateVirtualModule;
12702
+ exports.generateVitePressMigrationConfig = require_vitepress.generateVitePressMigrationConfig;
12765
12703
  exports.hasIslands = hasIslands;
12766
12704
  exports.inferType = inferType;
12705
+ exports.isMarkdownFilePath = isMarkdownFilePath;
12767
12706
  exports.jsx = jsx;
12768
12707
  exports.jsxs = jsxs;
12769
12708
  exports.lintMarkdown = lintMarkdown;
12770
12709
  exports.lintMarkdownAsync = lintMarkdownAsync;
12771
12710
  exports.lintMarkdownFile = lintMarkdownFile;
12772
12711
  exports.lintMarkdownFiles = lintMarkdownFiles;
12773
- exports.mergeThemes = mergeThemes;
12712
+ exports.mergeThemes = require_vitepress.mergeThemes;
12774
12713
  exports.mermaidClientScript = require_mermaid.mermaidClientScript;
12714
+ exports.normalizeMarkdownExtensions = normalizeMarkdownExtensions;
12715
+ exports.normalizeVitePressFrontmatter = require_vitepress.normalizeVitePressFrontmatter;
12775
12716
  exports.oxContent = oxContent;
12717
+ exports.parseGitHubLineRange = require_github.parseGitHubLineRange;
12718
+ exports.parseGitHubPermalink = require_github.parseGitHubPermalink;
12776
12719
  exports.prefetchGitHubRepos = require_github.prefetchGitHubRepos;
12720
+ exports.prefetchGitHubSources = require_github.prefetchGitHubSources;
12777
12721
  exports.prefetchOgpData = require_ogp.prefetchOgpData;
12778
12722
  exports.raw = raw;
12779
12723
  exports.renderAllPages = renderAllPages;
12780
12724
  exports.renderPage = renderPage;
12781
12725
  exports.renderToString = renderToString;
12726
+ exports.resolveBuiltinEmbedOptions = resolveBuiltinEmbedOptions;
12782
12727
  exports.resolveDocsOptions = resolveDocsOptions;
12783
12728
  exports.resolveI18nOptions = resolveI18nOptions;
12784
12729
  exports.resolveOgImageOptions = resolveOgImageOptions;
12785
12730
  exports.resolveSearchOptions = resolveSearchOptions;
12786
12731
  exports.resolveSsgOptions = resolveSsgOptions;
12787
- exports.resolveTheme = resolveTheme;
12732
+ exports.resolveTheme = require_vitepress.resolveTheme;
12788
12733
  exports.setRenderContext = setRenderContext;
12789
12734
  exports.shouldLintMarkdownFile = shouldLintMarkdownFile;
12735
+ exports.stripMarkdownExtension = stripMarkdownExtension;
12790
12736
  exports.transformAllPlugins = transformAllPlugins;
12791
12737
  exports.transformGitHub = require_github.transformGitHub;
12792
12738
  exports.transformIslands = transformIslands;