@ox-content/vite-plugin 2.15.0 → 2.25.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
@@ -1,4 +1,5 @@
1
- import { i as importNapiModuleSync, n as transformMermaidStatic, r as importNapiModule, t as mermaidClientScript } from "./mermaid.mjs";
1
+ import { n as importNapiModuleSync, t as importNapiModule } from "./napi.mjs";
2
+ import { n as transformMermaidStatic, t as mermaidClientScript } from "./mermaid2.mjs";
2
3
  import { n as resetTabGroupCounter, r as transformTabs, t as generateTabsCSS } from "./tabs2.mjs";
3
4
  import { n as transformYouTube, t as extractVideoId } from "./youtube2.mjs";
4
5
  import { a as fetchRepoData, c as parseGitHubPermalink, d as transformGitHub, i as fetchGitHubSource, l as prefetchGitHubRepos, n as collectGitHubSources, s as parseGitHubLineRange, t as collectGitHubRepos, u as prefetchGitHubSources } from "./github2.mjs";
@@ -12,9 +13,9 @@ import rehypeStringify from "rehype-stringify";
12
13
  import { createHighlighter } from "shiki";
13
14
  import * as path from "node:path";
14
15
  import { dirname, join } from "node:path";
15
- import * as fs$2 from "fs";
16
- import * as fs$1 from "fs/promises";
16
+ import * as fs$2 from "fs/promises";
17
17
  import * as crypto from "crypto";
18
+ import * as fs$1 from "fs";
18
19
  import { glob } from "glob";
19
20
  import * as fs from "node:fs/promises";
20
21
  import { mkdir, writeFile } from "node:fs/promises";
@@ -322,7 +323,7 @@ async function transformAllPlugins(html, options = {}) {
322
323
  result = await transformOgp(result, void 0, typeof ogpOptions === "object" ? ogpOptions : {});
323
324
  }
324
325
  if (mermaid) {
325
- const { transformMermaidStatic } = await import("./mermaid2.mjs");
326
+ const { transformMermaidStatic } = await import("./mermaid.mjs");
326
327
  result = await transformMermaidStatic(result);
327
328
  }
328
329
  return result;
@@ -565,67 +566,7 @@ if (import.meta.hot) {
565
566
  `;
566
567
  }
567
568
  //#endregion
568
- //#region src/nav-generator.ts
569
- function generateNavMetadata(docs, basePath = "/api") {
570
- return importNapiModuleSync().generateDocsNavMetadata(docs.map((doc) => doc.file), basePath);
571
- }
572
- function generateNavCode(navItems, exportName = "apiNav") {
573
- return importNapiModuleSync().generateDocsNavCode(navItems, exportName);
574
- }
575
- //#endregion
576
569
  //#region src/docs.ts
577
- /**
578
- * Source Documentation Extraction and Generation
579
- *
580
- * This module provides comprehensive tools for extracting JSDoc/TSDoc comments
581
- * from TypeScript/JavaScript source files and automatically generating Markdown
582
- * documentation.
583
- *
584
- * ## Features
585
- *
586
- * - **Automatic Extraction**: Parses JSDoc comments from functions, classes, interfaces, and types
587
- * - **Flexible Filtering**: Include/exclude patterns for selective documentation
588
- * - **Markdown Generation**: Converts extracted docs to organized Markdown files
589
- * - **Navigation Generation**: Auto-generates sidebar navigation metadata
590
- * - **GitHub Links**: Includes clickable links to source code on GitHub
591
- *
592
- * ## Supported JSDoc Tags
593
- *
594
- * - `@param {type} name - description` - Function parameter documentation
595
- * - `@returns {type} description` - Return value documentation
596
- * - `@example` - Code examples (multi-line blocks)
597
- * - `@private` - Mark item as private (excluded from docs if private=false)
598
- * - `@default value` - Default parameter value
599
- * - Custom tags are preserved in the `tags` field
600
- *
601
- * ## Usage Flow
602
- *
603
- * 1. Call `extractDocs()` to parse source files
604
- * 2. Call `generateMarkdown()` to create Markdown content
605
- * 3. Call `writeDocs()` to write files to output directory
606
- * 4. Generated nav.ts can be imported for sidebar navigation
607
- *
608
- * @example
609
- * ```typescript
610
- * import { extractDocs, generateMarkdown, writeDocs } from './docs';
611
- *
612
- * const docsOptions = {
613
- * enabled: true,
614
- * src: ['./src'],
615
- * out: './docs/api',
616
- * include: ['**\/*.ts'],
617
- * exclude: ['**\/*.test.ts'],
618
- * groupBy: 'file',
619
- * githubUrl: 'https://github.com/user/project',
620
- * };
621
- *
622
- * const extracted = await extractDocs(['./src'], docsOptions);
623
- * const markdown = generateMarkdown(extracted, docsOptions);
624
- * await writeDocs(markdown, './docs/api', extracted, docsOptions);
625
- * ```
626
- */
627
- const DOCS_MANIFEST_FILE = ".ox-content-docs-manifest.json";
628
- const DOCS_DATA_FILE = "docs.json";
629
570
  const DEFAULT_DOCS_INCLUDE = [
630
571
  "**/*.ts",
631
572
  "**/*.tsx",
@@ -715,20 +656,12 @@ async function extractDocs(srcDirs, options) {
715
656
  entries: doc.entries
716
657
  }));
717
658
  }
718
- const extractFileDocEntries = napi.extractFileDocEntries;
719
- if (!extractFileDocEntries) throw new Error("[ox-content] extractFileDocEntries is not available from @ox-content/napi.");
720
- const results = [];
721
- for (const srcDir of srcDirs) {
722
- const files = napi.collectDocsSourceFiles(srcDir, options.include, options.exclude);
723
- for (const file of files) {
724
- const entries = extractFileDocEntries(file, options.private, options.internal);
725
- if (entries.length > 0) results.push({
726
- file,
727
- entries
728
- });
729
- }
730
- }
731
- return results;
659
+ const extractDocsFromDirectories = napi.extractDocsFromDirectories;
660
+ if (!extractDocsFromDirectories) throw new Error("[ox-content] extractDocsFromDirectories is not available from @ox-content/napi.");
661
+ return extractDocsFromDirectories(srcDirs, options.include, options.exclude, options.private, options.internal).map((doc) => ({
662
+ file: doc.file,
663
+ entries: doc.entries
664
+ }));
732
665
  }
733
666
  /**
734
667
  * Generates Markdown documentation from extracted docs.
@@ -738,42 +671,25 @@ function generateMarkdown(docs, options) {
738
671
  if (typeof napi.generateDocsMarkdown !== "function") throw new Error("[ox-content] generateDocsMarkdown is not available from @ox-content/napi. Please rebuild the NAPI package.");
739
672
  return napi.generateDocsMarkdown(toRustDocsModules(docs), {
740
673
  groupBy: options.groupBy,
741
- githubUrl: options.githubUrl
674
+ githubUrl: options.githubUrl,
675
+ linkStyle: options.linkStyle,
676
+ basePath: options.basePath,
677
+ pathStrategy: options.pathStrategy
742
678
  });
743
679
  }
744
680
  /**
745
681
  * Writes generated documentation to the output directory.
746
682
  */
747
683
  async function writeDocs(docs, outDir, extractedDocs, options) {
748
- await fs$2.promises.mkdir(outDir, { recursive: true });
749
- const generatedFiles = new Set(Object.keys(docs));
750
- if (extractedDocs && options?.generateNav && options.groupBy === "file") generatedFiles.add("nav.ts");
751
- if (extractedDocs) generatedFiles.add(DOCS_DATA_FILE);
752
- const manifestPath = path$1.join(outDir, DOCS_MANIFEST_FILE);
753
- let previousFiles = [];
754
- try {
755
- previousFiles = JSON.parse(await fs$2.promises.readFile(manifestPath, "utf-8"));
756
- } catch {
757
- previousFiles = [];
758
- }
759
- for (const staleFile of previousFiles) {
760
- if (generatedFiles.has(staleFile)) continue;
761
- await fs$2.promises.rm(path$1.join(outDir, staleFile), { force: true });
762
- }
763
- for (const [fileName, content] of Object.entries(docs)) {
764
- const filePath = path$1.join(outDir, fileName);
765
- await fs$2.promises.writeFile(filePath, content, "utf-8");
766
- }
767
- if (extractedDocs && options?.generateNav && options.groupBy === "file") {
768
- const navCode = generateNavCode(generateNavMetadata(extractedDocs, "/api"), "apiNav");
769
- const navFilePath = path$1.join(outDir, "nav.ts");
770
- await fs$2.promises.writeFile(navFilePath, navCode, "utf-8");
771
- }
772
- if (extractedDocs) {
773
- const napi = importNapiModuleSync();
774
- await fs$2.promises.writeFile(path$1.join(outDir, DOCS_DATA_FILE), napi.generateDocsDataJson(toRustDocsModules(extractedDocs), (/* @__PURE__ */ new Date()).toISOString()), "utf-8");
775
- }
776
- await fs$2.promises.writeFile(manifestPath, JSON.stringify([...generatedFiles].sort(), null, 2), "utf-8");
684
+ const napi = importNapiModuleSync();
685
+ if (typeof napi.writeGeneratedDocs !== "function") throw new Error("[ox-content] writeGeneratedDocs is not available from @ox-content/napi. Please rebuild the NAPI package.");
686
+ napi.writeGeneratedDocs(docs, outDir, extractedDocs ? toRustDocsModules(extractedDocs) : void 0, {
687
+ generateNav: options?.generateNav ?? false,
688
+ groupBy: options?.groupBy ?? "file",
689
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
690
+ basePath: options?.basePath,
691
+ pathStrategy: options?.pathStrategy
692
+ });
777
693
  }
778
694
  function toRustDocsModules(docs) {
779
695
  return docs.map((doc) => ({
@@ -798,9 +714,6 @@ function toRustDocsModules(docs) {
798
714
  }))
799
715
  }));
800
716
  }
801
- /**
802
- * Resolves docs options with defaults.
803
- */
804
717
  function resolveDocsOptions(options) {
805
718
  if (options === false) return false;
806
719
  const opts = options || {};
@@ -821,6 +734,9 @@ function resolveDocsOptions(options) {
821
734
  toc: false,
822
735
  groupBy: opts.groupBy ?? "file",
823
736
  githubUrl: opts.githubUrl,
737
+ linkStyle: opts.linkStyle ?? "markdown",
738
+ basePath: opts.basePath,
739
+ pathStrategy: opts.pathStrategy ?? "flat",
824
740
  generateNav: opts.generateNav ?? true
825
741
  };
826
742
  }
@@ -908,6 +824,8 @@ async function renderHtmlToPng(page, html, width, height, publicDir) {
908
824
  }
909
825
  //#endregion
910
826
  //#region src/og-image/browser.ts
827
+ const PLAYWRIGHT_BROWSER_INSTALL_HINT = "Install Playwright browsers with `npx playwright install chromium` to enable OG image generation.";
828
+ let chromiumUnavailableWarned = false;
911
829
  /**
912
830
  * Opens a Chromium browser and returns a session for rendering OG images.
913
831
  * Returns null if Playwright/Chromium is not available.
@@ -947,10 +865,20 @@ async function openBrowser() {
947
865
  }
948
866
  };
949
867
  } catch (err) {
950
- console.warn("[ox-content:og-image] Chromium not available, skipping OG image generation.", err instanceof Error ? err.message : err);
868
+ warnChromiumUnavailableOnce(err);
951
869
  return null;
952
870
  }
953
871
  }
872
+ function warnChromiumUnavailableOnce(err) {
873
+ if (chromiumUnavailableWarned) return;
874
+ chromiumUnavailableWarned = true;
875
+ console.warn(`[ox-content:og-image] Chromium not available, skipping OG image generation. ${formatChromiumUnavailableDetail(err)}`);
876
+ }
877
+ function formatChromiumUnavailableDetail(err) {
878
+ const message = err instanceof Error ? err.message : String(err);
879
+ if (message.includes("Executable doesn't exist") || message.includes("Please run the following command to download new browsers")) return PLAYWRIGHT_BROWSER_INSTALL_HINT;
880
+ return message.split(/\r?\n/).find((line) => line.trim())?.trim() ?? "Unknown launch error.";
881
+ }
954
882
  //#endregion
955
883
  //#region src/og-image/template.ts
956
884
  /**
@@ -1058,7 +986,7 @@ function computeCacheKey(templateSource, props, width, height) {
1058
986
  async function getCached(cacheDir, key) {
1059
987
  const filePath = path$1.join(cacheDir, `${key}.png`);
1060
988
  try {
1061
- return await fs$1.readFile(filePath);
989
+ return await fs$2.readFile(filePath);
1062
990
  } catch {
1063
991
  return null;
1064
992
  }
@@ -1067,9 +995,9 @@ async function getCached(cacheDir, key) {
1067
995
  * Writes a PNG buffer to the cache.
1068
996
  */
1069
997
  async function writeCache(cacheDir, key, png) {
1070
- await fs$1.mkdir(cacheDir, { recursive: true });
998
+ await fs$2.mkdir(cacheDir, { recursive: true });
1071
999
  const filePath = path$1.join(cacheDir, `${key}.png`);
1072
- await fs$1.writeFile(filePath, png);
1000
+ await fs$2.writeFile(filePath, png);
1073
1001
  }
1074
1002
  //#endregion
1075
1003
  //#region \0@oxc-project+runtime@0.129.0/helpers/usingCtx.js
@@ -1840,8 +1768,8 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
1840
1768
  async function externalizeSharedPageAssets(pages, outDir, base) {
1841
1769
  const optimized = (await importNapiModule()).externalizeSsgAssets(pages, outDir, base);
1842
1770
  await Promise.all(optimized.assets.map(async (asset) => {
1843
- await fs$1.mkdir(path$1.dirname(asset.outputPath), { recursive: true });
1844
- await fs$1.writeFile(asset.outputPath, asset.content, "utf-8");
1771
+ await fs$2.mkdir(path$1.dirname(asset.outputPath), { recursive: true });
1772
+ await fs$2.writeFile(asset.outputPath, asset.content, "utf-8");
1845
1773
  }));
1846
1774
  return {
1847
1775
  pages: optimized.pages,
@@ -1903,149 +1831,201 @@ async function buildSsg(options, root) {
1903
1831
  };
1904
1832
  const srcDir = path$1.resolve(root, options.srcDir);
1905
1833
  const outDir = path$1.resolve(root, options.outDir);
1906
- const base = options.base.endsWith("/") ? options.base : options.base + "/";
1907
1834
  const generatedFiles = [];
1908
- const generatedPages = [];
1909
1835
  const errors = [];
1910
- if (ssgOptions.clean) try {
1911
- await fs$1.rm(outDir, {
1836
+ await cleanOutputDirectory(ssgOptions, outDir);
1837
+ const markdownFiles = await collectMarkdownFiles(srcDir, options.extensions);
1838
+ const context = await createBuildSsgContext(options, root, srcDir, outDir, markdownFiles);
1839
+ const collected = await collectPageResults(context, markdownFiles);
1840
+ errors.push(...collected.errors);
1841
+ await generateOgImageAssets(context, collected, generatedFiles, errors);
1842
+ await writeGeneratedPages(await generateHtmlPages(context, collected.pageResults, collected, errors), context, generatedFiles);
1843
+ return {
1844
+ files: generatedFiles,
1845
+ errors
1846
+ };
1847
+ }
1848
+ async function cleanOutputDirectory(ssgOptions, outDir) {
1849
+ if (!ssgOptions.clean) return;
1850
+ try {
1851
+ await fs$2.rm(outDir, {
1912
1852
  recursive: true,
1913
1853
  force: true
1914
1854
  });
1915
1855
  } catch {}
1916
- const markdownFiles = await collectMarkdownFiles(srcDir, options.extensions);
1917
- 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));
1918
- let siteName = ssgOptions.siteName ?? "Documentation";
1919
- if (!ssgOptions.siteName) try {
1856
+ }
1857
+ async function createBuildSsgContext(options, root, srcDir, outDir, markdownFiles) {
1858
+ const ssgOptions = options.ssg;
1859
+ const base = options.base.endsWith("/") ? options.base : options.base + "/";
1860
+ return {
1861
+ options,
1862
+ ssgOptions,
1863
+ root,
1864
+ srcDir,
1865
+ outDir,
1866
+ base,
1867
+ navItems: resolveNavigationGroups(ssgOptions.navigation, base, ssgOptions.extension) ?? (ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension)),
1868
+ siteName: await resolveSiteName$1(root, ssgOptions),
1869
+ shouldGenerateOgImages: (options.ogImage || ssgOptions.generateOgImage) && !ssgOptions.bare,
1870
+ napi: ssgOptions.lastUpdated ? await importNapiModule() : void 0
1871
+ };
1872
+ }
1873
+ async function resolveSiteName$1(root, ssgOptions) {
1874
+ if (ssgOptions.siteName) return ssgOptions.siteName;
1875
+ try {
1920
1876
  const pkgPath = path$1.join(root, "package.json");
1921
- const pkg = JSON.parse(await fs$1.readFile(pkgPath, "utf-8"));
1922
- if (pkg.name) siteName = formatTitle(pkg.name);
1923
- } catch {}
1924
- const ogImageEntries = [];
1925
- const ogImageInputPaths = [];
1926
- const ogImageUrlMap = /* @__PURE__ */ new Map();
1927
- const shouldGenerateOgImages = (options.ogImage || ssgOptions.generateOgImage) && !ssgOptions.bare;
1928
- const pageResults = [];
1929
- const napi = ssgOptions.lastUpdated ? await importNapiModule() : void 0;
1877
+ const pkg = JSON.parse(await fs$2.readFile(pkgPath, "utf-8"));
1878
+ return pkg.name ? formatTitle(pkg.name) : "Documentation";
1879
+ } catch {
1880
+ return "Documentation";
1881
+ }
1882
+ }
1883
+ async function collectPageResults(context, markdownFiles) {
1884
+ const collected = {
1885
+ pageResults: [],
1886
+ ogImageEntries: [],
1887
+ ogImageInputPaths: [],
1888
+ ogImageUrlMap: /* @__PURE__ */ new Map(),
1889
+ errors: []
1890
+ };
1930
1891
  for (const inputPath of markdownFiles) try {
1931
- const result = await transformMarkdown(await fs$1.readFile(inputPath, "utf-8"), inputPath, options, {
1932
- convertMdLinks: true,
1933
- baseUrl: base,
1934
- sourcePath: inputPath
1935
- });
1936
- const frontmatter = normalizeVitePressFrontmatter(result.frontmatter);
1937
- let transformedHtml = result.html;
1938
- const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(transformedHtml);
1939
- transformedHtml = protectedHtml;
1940
- const pluginOptions = {
1941
- tabs: true,
1942
- youtube: true,
1943
- github: options.embeds.github,
1944
- openGraph: options.embeds.openGraph,
1945
- mermaid: true,
1946
- githubToken: process.env.GITHUB_TOKEN
1947
- };
1948
- transformedHtml = await transformAllPlugins(transformedHtml, pluginOptions);
1949
- if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
1950
- transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
1951
- const title = extractTitle$1(transformedHtml, frontmatter);
1952
- const description = frontmatter.description;
1953
- const routePaths = getRoutePaths(inputPath, srcDir, outDir, base, ssgOptions.extension, ssgOptions.siteUrl);
1954
- pageResults.push({
1955
- inputPath,
1956
- routePaths,
1957
- transformedHtml,
1958
- title,
1959
- description,
1960
- lastUpdated: napi?.getGitLastUpdated(inputPath, root) ?? void 0,
1961
- frontmatter,
1962
- toc: result.toc
1963
- });
1964
- if (shouldGenerateOgImages) {
1965
- const { layout: _layout, ...frontmatterRest } = frontmatter;
1966
- ogImageEntries.push({
1967
- props: {
1968
- ...frontmatterRest,
1969
- title,
1970
- description,
1971
- siteName
1972
- },
1973
- outputPath: routePaths.ogImagePath
1974
- });
1975
- ogImageInputPaths.push(inputPath);
1976
- ogImageUrlMap.set(inputPath, routePaths.ogImageUrl);
1977
- }
1892
+ const pageResult = await transformSsgPage(context, inputPath);
1893
+ collected.pageResults.push(pageResult);
1894
+ collectOgImageEntry(context, pageResult, collected);
1978
1895
  } catch (err) {
1979
1896
  const errorMessage = err instanceof Error ? err.message : String(err);
1980
- errors.push(`Failed to process ${inputPath}: ${errorMessage}`);
1897
+ collected.errors.push(`Failed to process ${inputPath}: ${errorMessage}`);
1981
1898
  }
1982
- if (shouldGenerateOgImages && ogImageEntries.length > 0) try {
1983
- const ogResults = await generateOgImages(ogImageEntries, options.ogImageOptions, root);
1984
- let ogSuccessCount = 0;
1985
- for (let i = 0; i < ogResults.length; i++) {
1986
- const result = ogResults[i];
1987
- if (result.error) {
1988
- errors.push(`OG image failed for ${result.outputPath}: ${result.error}`);
1989
- ogImageUrlMap.delete(ogImageInputPaths[i]);
1990
- } else {
1991
- generatedFiles.push(result.outputPath);
1992
- ogSuccessCount++;
1993
- }
1994
- }
1995
- if (ogSuccessCount > 0) {
1996
- const cachedCount = ogResults.filter((r) => r.cached && !r.error).length;
1997
- console.log(`[ox-content:og-image] Generated ${ogSuccessCount} OG images` + (cachedCount > 0 ? ` (${cachedCount} from cache)` : ""));
1998
- }
1899
+ return collected;
1900
+ }
1901
+ async function transformSsgPage(context, inputPath) {
1902
+ const result = await transformMarkdown(await fs$2.readFile(inputPath, "utf-8"), inputPath, context.options, {
1903
+ convertMdLinks: true,
1904
+ baseUrl: context.base,
1905
+ sourcePath: inputPath
1906
+ });
1907
+ const frontmatter = normalizeVitePressFrontmatter(result.frontmatter);
1908
+ const transformedHtml = await transformSsgHtml(result.html, context.options);
1909
+ const title = extractTitle$1(transformedHtml, frontmatter);
1910
+ return {
1911
+ inputPath,
1912
+ routePaths: getRoutePaths(inputPath, context.srcDir, context.outDir, context.base, context.ssgOptions.extension, context.ssgOptions.siteUrl),
1913
+ transformedHtml,
1914
+ title,
1915
+ description: frontmatter.description,
1916
+ lastUpdated: context.napi?.getGitLastUpdated(inputPath, context.root) ?? void 0,
1917
+ frontmatter,
1918
+ toc: result.toc
1919
+ };
1920
+ }
1921
+ async function transformSsgHtml(html, options) {
1922
+ const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(html);
1923
+ let transformedHtml = await transformAllPlugins(protectedHtml, {
1924
+ tabs: true,
1925
+ youtube: true,
1926
+ github: options.embeds.github,
1927
+ openGraph: options.embeds.openGraph,
1928
+ mermaid: true,
1929
+ githubToken: process.env.GITHUB_TOKEN
1930
+ });
1931
+ if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
1932
+ return restoreMermaidSvgs(transformedHtml, mermaidSvgs);
1933
+ }
1934
+ function collectOgImageEntry(context, pageResult, collected) {
1935
+ if (!context.shouldGenerateOgImages) return;
1936
+ const { layout: _layout, ...frontmatterRest } = pageResult.frontmatter;
1937
+ collected.ogImageEntries.push({
1938
+ props: {
1939
+ ...frontmatterRest,
1940
+ title: pageResult.title,
1941
+ description: pageResult.description,
1942
+ siteName: context.siteName
1943
+ },
1944
+ outputPath: pageResult.routePaths.ogImagePath
1945
+ });
1946
+ collected.ogImageInputPaths.push(pageResult.inputPath);
1947
+ collected.ogImageUrlMap.set(pageResult.inputPath, pageResult.routePaths.ogImageUrl);
1948
+ }
1949
+ async function generateOgImageAssets(context, collected, generatedFiles, errors) {
1950
+ if (!context.shouldGenerateOgImages || collected.ogImageEntries.length === 0) return;
1951
+ try {
1952
+ const ogResults = await generateOgImages(collected.ogImageEntries, context.options.ogImageOptions, context.root);
1953
+ if (clearMissingBrowserOgImages(ogResults, collected)) return;
1954
+ reportOgImageResults(ogResults, collected, generatedFiles, errors);
1999
1955
  } catch (err) {
2000
1956
  const errorMessage = err instanceof Error ? err.message : String(err);
2001
1957
  console.warn(`[ox-content:og-image] Batch generation failed: ${errorMessage}`);
2002
- ogImageUrlMap.clear();
1958
+ collected.ogImageUrlMap.clear();
2003
1959
  }
2004
- for (const pageResult of pageResults) try {
2005
- const { inputPath, routePaths, transformedHtml, title, description, lastUpdated, frontmatter, toc } = pageResult;
2006
- let pageOgImage = ssgOptions.ogImage;
2007
- if (shouldGenerateOgImages && ogImageUrlMap.has(inputPath)) pageOgImage = ogImageUrlMap.get(inputPath);
2008
- let entryPage;
2009
- if (frontmatter.layout === "entry") entryPage = {
2010
- hero: frontmatter.hero,
2011
- features: frontmatter.features
2012
- };
2013
- let html;
2014
- if (ssgOptions.bare) html = generateBareHtmlPage(transformedHtml, title);
2015
- else {
2016
- const pageData = {
2017
- title,
2018
- description,
2019
- content: transformedHtml,
2020
- toc,
2021
- lastUpdated,
2022
- frontmatter,
2023
- path: routePaths.urlPath,
2024
- href: routePaths.href,
2025
- entryPage
2026
- };
2027
- html = await generateHtmlPage(pageData, navItems, siteName, base, pageOgImage, ssgOptions.theme, getPageLocale(pageData.path, options.i18n), options.i18n ? options.i18n.locales : void 0);
1960
+ }
1961
+ function clearMissingBrowserOgImages(ogResults, collected) {
1962
+ if (!(ogResults.length > 0 && ogResults.every((result) => result.error === "Chromium not available"))) return false;
1963
+ for (const inputPath of collected.ogImageInputPaths) collected.ogImageUrlMap.delete(inputPath);
1964
+ return true;
1965
+ }
1966
+ function reportOgImageResults(ogResults, collected, generatedFiles, errors) {
1967
+ let ogSuccessCount = 0;
1968
+ for (let i = 0; i < ogResults.length; i++) {
1969
+ const result = ogResults[i];
1970
+ if (result.error) {
1971
+ errors.push(`OG image failed for ${result.outputPath}: ${result.error}`);
1972
+ collected.ogImageUrlMap.delete(collected.ogImageInputPaths[i]);
1973
+ } else {
1974
+ generatedFiles.push(result.outputPath);
1975
+ ogSuccessCount++;
2028
1976
  }
1977
+ }
1978
+ if (ogSuccessCount > 0) {
1979
+ const cachedCount = ogResults.filter((result) => result.cached && !result.error).length;
1980
+ console.log(`[ox-content:og-image] Generated ${ogSuccessCount} OG images` + (cachedCount > 0 ? ` (${cachedCount} from cache)` : ""));
1981
+ }
1982
+ }
1983
+ async function generateHtmlPages(context, pageResults, collected, errors) {
1984
+ const generatedPages = [];
1985
+ for (const pageResult of pageResults) try {
2029
1986
  generatedPages.push({
2030
- inputPath,
2031
- outputPath: routePaths.outputPath,
2032
- html
1987
+ inputPath: pageResult.inputPath,
1988
+ outputPath: pageResult.routePaths.outputPath,
1989
+ html: await renderSsgPage(context, pageResult, collected.ogImageUrlMap)
2033
1990
  });
2034
1991
  } catch (err) {
2035
1992
  const errorMessage = err instanceof Error ? err.message : String(err);
2036
1993
  errors.push(`Failed to generate HTML for ${pageResult.inputPath}: ${errorMessage}`);
2037
1994
  }
2038
- const optimizedOutput = await externalizeSharedPageAssets(generatedPages, outDir, base);
1995
+ return generatedPages;
1996
+ }
1997
+ async function renderSsgPage(context, pageResult, ogImageUrlMap) {
1998
+ if (context.ssgOptions.bare) return generateBareHtmlPage(pageResult.transformedHtml, pageResult.title);
1999
+ const pageData = createSsgPageData(pageResult);
2000
+ const pageOgImage = context.shouldGenerateOgImages && ogImageUrlMap.has(pageResult.inputPath) ? ogImageUrlMap.get(pageResult.inputPath) : context.ssgOptions.ogImage;
2001
+ return generateHtmlPage(pageData, context.navItems, context.siteName, context.base, pageOgImage, context.ssgOptions.theme, getPageLocale(pageData.path, context.options.i18n), context.options.i18n ? context.options.i18n.locales : void 0);
2002
+ }
2003
+ function createSsgPageData(pageResult) {
2004
+ const { frontmatter } = pageResult;
2005
+ const entryPage = frontmatter.layout === "entry" ? {
2006
+ hero: frontmatter.hero,
2007
+ features: frontmatter.features
2008
+ } : void 0;
2009
+ return {
2010
+ title: pageResult.title,
2011
+ description: pageResult.description,
2012
+ content: pageResult.transformedHtml,
2013
+ toc: pageResult.toc,
2014
+ lastUpdated: pageResult.lastUpdated,
2015
+ frontmatter,
2016
+ path: pageResult.routePaths.urlPath,
2017
+ href: pageResult.routePaths.href,
2018
+ entryPage
2019
+ };
2020
+ }
2021
+ async function writeGeneratedPages(generatedPages, context, generatedFiles) {
2022
+ const optimizedOutput = await externalizeSharedPageAssets(generatedPages, context.outDir, context.base);
2039
2023
  generatedFiles.push(...optimizedOutput.assets);
2040
2024
  for (const page of optimizedOutput.pages) {
2041
- await fs$1.mkdir(path$1.dirname(page.outputPath), { recursive: true });
2042
- await fs$1.writeFile(page.outputPath, page.html, "utf-8");
2025
+ await fs$2.mkdir(path$1.dirname(page.outputPath), { recursive: true });
2026
+ await fs$2.writeFile(page.outputPath, page.html, "utf-8");
2043
2027
  generatedFiles.push(page.outputPath);
2044
2028
  }
2045
- return {
2046
- files: generatedFiles,
2047
- errors
2048
- };
2049
2029
  }
2050
2030
  //#endregion
2051
2031
  //#region src/search.ts
@@ -2179,14 +2159,14 @@ async function resolveMarkdownFile(url, srcDir, extensions) {
2179
2159
  for (const relativePath of directCandidates) {
2180
2160
  const filePath = path$1.join(srcDir, relativePath);
2181
2161
  try {
2182
- await fs$1.access(filePath);
2162
+ await fs$2.access(filePath);
2183
2163
  return filePath;
2184
2164
  } catch {}
2185
2165
  }
2186
2166
  for (const extension of extensions) {
2187
2167
  const indexPath = path$1.join(srcDir, routePath, `index${extension}`);
2188
2168
  try {
2189
- await fs$1.access(indexPath);
2169
+ await fs$2.access(indexPath);
2190
2170
  return indexPath;
2191
2171
  } catch {}
2192
2172
  }
@@ -2228,7 +2208,7 @@ async function resolveSiteName(options, root) {
2228
2208
  if (options.ssg.siteName) return options.ssg.siteName;
2229
2209
  try {
2230
2210
  const pkgPath = path$1.join(root, "package.json");
2231
- const pkg = JSON.parse(await fs$1.readFile(pkgPath, "utf-8"));
2211
+ const pkg = JSON.parse(await fs$2.readFile(pkgPath, "utf-8"));
2232
2212
  if (pkg.name) return formatTitle(pkg.name);
2233
2213
  } catch {}
2234
2214
  return "Documentation";
@@ -2240,7 +2220,7 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
2240
2220
  const srcDir = path$1.resolve(root, options.srcDir);
2241
2221
  resetTabGroupCounter();
2242
2222
  resetIslandCounter();
2243
- const result = await transformMarkdown(await fs$1.readFile(filePath, "utf-8"), filePath, options, {
2223
+ const result = await transformMarkdown(await fs$2.readFile(filePath, "utf-8"), filePath, options, {
2244
2224
  convertMdLinks: true,
2245
2225
  baseUrl: base,
2246
2226
  sourcePath: filePath
@@ -2396,7 +2376,7 @@ async function collectPages(options, root) {
2396
2376
  const pages = [];
2397
2377
  const generateOgImage = options.ogImage || options.ssg.generateOgImage;
2398
2378
  for (const file of files.sort()) {
2399
- const content = fs$2.readFileSync(file, "utf-8");
2379
+ const content = fs$1.readFileSync(file, "utf-8");
2400
2380
  const frontmatter = normalizeVitePressFrontmatter(parseFrontmatter(content));
2401
2381
  if (frontmatter.layout === "entry") continue;
2402
2382
  const title = extractTitle(content, frontmatter);
@@ -2720,7 +2700,7 @@ function createI18nPlugin(resolvedOptions) {
2720
2700
  async buildStart() {
2721
2701
  if (!i18nOptions || !i18nOptions.check) return;
2722
2702
  const dictDir = path$1.resolve(root, i18nOptions.dir);
2723
- if (!fs$2.existsSync(dictDir)) {
2703
+ if (!fs$1.existsSync(dictDir)) {
2724
2704
  console.warn(`[ox-content:i18n] Dictionary directory not found: ${dictDir}`);
2725
2705
  return;
2726
2706
  }
@@ -2736,7 +2716,7 @@ function createI18nPlugin(resolvedOptions) {
2736
2716
  configureServer(server) {
2737
2717
  if (!i18nOptions) return;
2738
2718
  const dictDir = path$1.resolve(root, i18nOptions.dir);
2739
- if (fs$2.existsSync(dictDir)) {
2719
+ if (fs$1.existsSync(dictDir)) {
2740
2720
  server.watcher.add(dictDir);
2741
2721
  server.watcher.on("change", (filePath) => {
2742
2722
  if (!filePath.startsWith(dictDir)) return;
@@ -2879,7 +2859,10 @@ function stripMaskedDocument(result) {
2879
2859
  };
2880
2860
  }
2881
2861
  function normalizeLintOptions(options) {
2882
- const languages = options.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language)) ?? options.dictionary?.standard?.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language)) ?? [...DEFAULT_LANGUAGES];
2862
+ const standardDictionary = options.dictionary?.standard && typeof options.dictionary.standard === "object" ? options.dictionary.standard : void 0;
2863
+ const optionLanguages = options.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language));
2864
+ const standardLanguages = standardDictionary?.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language));
2865
+ const languages = optionLanguages ?? standardLanguages ?? [...DEFAULT_LANGUAGES];
2883
2866
  const standard = normalizeStandardDictionaryOptions(options.dictionary?.standard, languages);
2884
2867
  return {
2885
2868
  dictionary: {
@@ -2920,6 +2903,12 @@ async function runStandardSpellcheckDocuments(maskedDocuments, options) {
2920
2903
  const { spellCheckDocument } = await loadCspellLib();
2921
2904
  const locale = standard.languages.join(",");
2922
2905
  const settings = createStandardSpellcheckSettings(options, locale);
2906
+ const spellCheckOptions = {
2907
+ generateSuggestions: true,
2908
+ noConfigSearch: true,
2909
+ numSuggestions: 3,
2910
+ resolveImportsRelativeTo: standard.resolveImportsRelativeTo
2911
+ };
2923
2912
  return Promise.all(maskedDocuments.map(async (maskedDocument, index) => {
2924
2913
  if (maskedDocument.trim().length === 0) return [];
2925
2914
  return (await spellCheckDocument({
@@ -2927,12 +2916,7 @@ async function runStandardSpellcheckDocuments(maskedDocuments, options) {
2927
2916
  locale,
2928
2917
  text: maskedDocument,
2929
2918
  uri: `file:///ox-content-lint-${index}.md`
2930
- }, {
2931
- generateSuggestions: true,
2932
- noConfigSearch: true,
2933
- numSuggestions: 3,
2934
- resolveImportsRelativeTo: standard.resolveImportsRelativeTo
2935
- }, settings)).issues.map((issue) => mapStandardIssueToDiagnostic(issue, standard.languages));
2919
+ }, spellCheckOptions, settings)).issues.map((issue) => mapStandardIssueToDiagnostic(issue, standard.languages, maskedDocument));
2936
2920
  }));
2937
2921
  } catch (error) {
2938
2922
  const imports = standard.imports.join(", ");
@@ -2953,8 +2937,8 @@ async function loadCspellLib() {
2953
2937
  cspellLibPromise ??= import("cspell-lib");
2954
2938
  return cspellLibPromise;
2955
2939
  }
2956
- function mapStandardIssueToDiagnostic(issue, languages) {
2957
- const line = issue.line.position.line + 1;
2940
+ function mapStandardIssueToDiagnostic(issue, languages, documentText) {
2941
+ const line = getLineNumberAtOffset(documentText, issue.line.offset);
2958
2942
  const column = issue.offset - issue.line.offset + 1;
2959
2943
  return {
2960
2944
  column,
@@ -2968,6 +2952,11 @@ function mapStandardIssueToDiagnostic(issue, languages) {
2968
2952
  suggestions: issue.suggestions?.slice(0, 3)
2969
2953
  };
2970
2954
  }
2955
+ function getLineNumberAtOffset(text, offset) {
2956
+ let line = 1;
2957
+ for (let index = 0; index < offset && index < text.length; index++) if (text.charCodeAt(index) === 10) line++;
2958
+ return line;
2959
+ }
2971
2960
  function inferStandardIssueLanguage(word, languages) {
2972
2961
  if (/[\p{Script=Hiragana}\p{Script=Katakana}]/u.test(word) && languages.includes("ja")) return "ja";
2973
2962
  if (/[\p{Script=Han}]/u.test(word)) {
@@ -3704,21 +3693,35 @@ init_page_context();
3704
3693
  function oxContent(options = {}) {
3705
3694
  const resolvedOptions = resolveOptions(options);
3706
3695
  let config;
3707
- async function regenerateDocs(root) {
3708
- const docsOptions = resolvedOptions.docs;
3709
- if (!docsOptions || !docsOptions.enabled) return 0;
3710
- const srcDirs = docsOptions.src.map((src) => path$1.resolve(root, src));
3711
- const outDir = path$1.resolve(root, docsOptions.out);
3712
- const extracted = await extractDocs(srcDirs, docsOptions);
3713
- const generated = generateMarkdown(extracted, docsOptions);
3714
- await writeDocs(generated, outDir, extracted, docsOptions);
3715
- return Object.keys(generated).length;
3716
- }
3717
- const mainPlugin = {
3718
- name: "ox-content",
3719
- configResolved(resolvedConfig) {
3696
+ const getRoot = () => config?.root || process.cwd();
3697
+ const ssgDevCache = createDevServerCache();
3698
+ const plugins = [
3699
+ createMainPlugin(resolvedOptions, (resolvedConfig) => {
3720
3700
  config = resolvedConfig;
3721
- },
3701
+ }),
3702
+ createEnvironmentPlugin(resolvedOptions),
3703
+ createDocsPlugin(resolvedOptions, getRoot),
3704
+ createSsgPlugin(resolvedOptions, getRoot, ssgDevCache),
3705
+ createSearchPlugin(resolvedOptions, getRoot)
3706
+ ];
3707
+ if (resolvedOptions.i18n) plugins.push(createI18nPlugin(resolvedOptions));
3708
+ if (resolvedOptions.ogViewer) plugins.push(createOgViewerPlugin(resolvedOptions));
3709
+ return plugins;
3710
+ }
3711
+ async function regenerateDocs(resolvedOptions, root) {
3712
+ const docsOptions = resolvedOptions.docs;
3713
+ if (!docsOptions || !docsOptions.enabled) return 0;
3714
+ const srcDirs = docsOptions.src.map((src) => path$1.resolve(root, src));
3715
+ const outDir = path$1.resolve(root, docsOptions.out);
3716
+ const extracted = await extractDocs(srcDirs, docsOptions);
3717
+ const generated = generateMarkdown(extracted, docsOptions);
3718
+ await writeDocs(generated, outDir, extracted, docsOptions);
3719
+ return Object.keys(generated).length;
3720
+ }
3721
+ function createMainPlugin(resolvedOptions, setConfig) {
3722
+ return {
3723
+ name: "ox-content",
3724
+ configResolved: setConfig,
3722
3725
  configureServer(devServer) {
3723
3726
  devServer.middlewares.use(async (req, res, next) => {
3724
3727
  const url = req.url;
@@ -3743,31 +3746,33 @@ function oxContent(options = {}) {
3743
3746
  };
3744
3747
  },
3745
3748
  async handleHotUpdate({ file, server }) {
3746
- if (isMarkdownFilePath(file, resolvedOptions.extensions)) {
3747
- server.ws.send({
3748
- type: "custom",
3749
- event: "ox-content:update",
3750
- data: { file }
3751
- });
3752
- const modules = server.moduleGraph.getModulesByFile(file);
3753
- return modules ? Array.from(modules) : [];
3754
- }
3749
+ if (!isMarkdownFilePath(file, resolvedOptions.extensions)) return;
3750
+ server.ws.send({
3751
+ type: "custom",
3752
+ event: "ox-content:update",
3753
+ data: { file }
3754
+ });
3755
+ const modules = server.moduleGraph.getModulesByFile(file);
3756
+ return modules ? Array.from(modules) : [];
3755
3757
  }
3756
3758
  };
3757
- const environmentPlugin = {
3759
+ }
3760
+ function createEnvironmentPlugin(resolvedOptions) {
3761
+ return {
3758
3762
  name: "ox-content:environment",
3759
3763
  config() {
3760
3764
  return { environments: { markdown: createMarkdownEnvironment(resolvedOptions) } };
3761
3765
  }
3762
3766
  };
3763
- const docsPlugin = {
3767
+ }
3768
+ function createDocsPlugin(resolvedOptions, getRoot) {
3769
+ return {
3764
3770
  name: "ox-content:docs",
3765
3771
  async buildStart() {
3766
3772
  const docsOptions = resolvedOptions.docs;
3767
3773
  if (!docsOptions || !docsOptions.enabled) return;
3768
- const root = config?.root || process.cwd();
3769
3774
  try {
3770
- const count = await regenerateDocs(root);
3775
+ const count = await regenerateDocs(resolvedOptions, getRoot());
3771
3776
  console.log(`[ox-content] Generated ${count} documentation files to ${docsOptions.out}`);
3772
3777
  } catch (err) {
3773
3778
  console.warn("[ox-content] Failed to generate documentation:", err);
@@ -3776,50 +3781,32 @@ function oxContent(options = {}) {
3776
3781
  configureServer(devServer) {
3777
3782
  const docsOptions = resolvedOptions.docs;
3778
3783
  if (!docsOptions || !docsOptions.enabled) return;
3779
- const root = config?.root || process.cwd();
3784
+ const root = getRoot();
3780
3785
  const srcDirs = docsOptions.src.map((src) => path$1.resolve(root, src));
3781
3786
  for (const srcDir of srcDirs) devServer.watcher.add(srcDir);
3782
3787
  devServer.watcher.on("all", async (event, file) => {
3783
3788
  if (event !== "add" && event !== "change" && event !== "unlink") return;
3784
- if (srcDirs.some((srcDir) => file.startsWith(srcDir) && (file.endsWith(".ts") || file.endsWith(".tsx")))) try {
3785
- await regenerateDocs(root);
3789
+ if (!srcDirs.some((srcDir) => file.startsWith(srcDir) && (file.endsWith(".ts") || file.endsWith(".tsx")))) return;
3790
+ try {
3791
+ await regenerateDocs(resolvedOptions, root);
3786
3792
  } catch {}
3787
3793
  });
3788
3794
  }
3789
3795
  };
3790
- const ssgDevCache = createDevServerCache();
3791
- const ssgPlugin = {
3796
+ }
3797
+ function createSsgPlugin(resolvedOptions, getRoot, ssgDevCache) {
3798
+ return {
3792
3799
  name: "ox-content:ssg",
3793
3800
  configureServer(devServer) {
3794
3801
  if (!resolvedOptions.ssg.enabled) return;
3795
- const root = config?.root || process.cwd();
3802
+ const root = getRoot();
3796
3803
  const srcDir = path$1.resolve(root, resolvedOptions.srcDir);
3797
3804
  devServer.middlewares.use(createDevServerMiddleware(resolvedOptions, root, ssgDevCache));
3798
3805
  devServer.watcher.on("add", (file) => {
3799
- if (file.startsWith(srcDir) && isMarkdownFilePath(file, resolvedOptions.extensions)) {
3800
- invalidateNavCache(ssgDevCache);
3801
- devServer.ws.send({
3802
- type: "custom",
3803
- event: "ox-content:update",
3804
- data: {
3805
- file,
3806
- type: "add"
3807
- }
3808
- });
3809
- }
3806
+ notifySsgFileAddedOrRemoved(devServer, resolvedOptions, ssgDevCache, srcDir, file, "add");
3810
3807
  });
3811
3808
  devServer.watcher.on("unlink", (file) => {
3812
- if (file.startsWith(srcDir) && isMarkdownFilePath(file, resolvedOptions.extensions)) {
3813
- invalidateNavCache(ssgDevCache);
3814
- devServer.ws.send({
3815
- type: "custom",
3816
- event: "ox-content:update",
3817
- data: {
3818
- file,
3819
- type: "unlink"
3820
- }
3821
- });
3822
- }
3809
+ notifySsgFileAddedOrRemoved(devServer, resolvedOptions, ssgDevCache, srcDir, file, "unlink");
3823
3810
  });
3824
3811
  devServer.watcher.on("change", (file) => {
3825
3812
  if (file.startsWith(srcDir) && isMarkdownFilePath(file, resolvedOptions.extensions)) invalidatePageCache(ssgDevCache, file);
@@ -3827,63 +3814,63 @@ function oxContent(options = {}) {
3827
3814
  },
3828
3815
  async closeBundle() {
3829
3816
  if (!resolvedOptions.ssg.enabled) return;
3830
- const root = config?.root || process.cwd();
3831
3817
  try {
3832
- const result = await buildSsg(resolvedOptions, root);
3818
+ const result = await buildSsg(resolvedOptions, getRoot());
3833
3819
  if (result.files.length > 0) console.log(`[ox-content] Generated ${result.files.length} output files`);
3834
- if (result.errors.length > 0) for (const error of result.errors) console.warn(`[ox-content] ${error}`);
3820
+ for (const error of result.errors) console.warn(`[ox-content] ${error}`);
3835
3821
  } catch (err) {
3836
3822
  console.error("[ox-content] SSG build failed:", err);
3837
3823
  }
3838
3824
  }
3839
3825
  };
3826
+ }
3827
+ function notifySsgFileAddedOrRemoved(devServer, resolvedOptions, ssgDevCache, srcDir, file, type) {
3828
+ if (!file.startsWith(srcDir) || !isMarkdownFilePath(file, resolvedOptions.extensions)) return;
3829
+ invalidateNavCache(ssgDevCache);
3830
+ devServer.ws.send({
3831
+ type: "custom",
3832
+ event: "ox-content:update",
3833
+ data: {
3834
+ file,
3835
+ type
3836
+ }
3837
+ });
3838
+ }
3839
+ function createSearchPlugin(resolvedOptions, getRoot) {
3840
3840
  let searchIndexJson = "";
3841
- const plugins = [
3842
- mainPlugin,
3843
- environmentPlugin,
3844
- docsPlugin,
3845
- ssgPlugin,
3846
- {
3847
- name: "ox-content:search",
3848
- resolveId(id) {
3849
- if (id === "virtual:ox-content/search") return "\0virtual:ox-content/search";
3850
- return null;
3851
- },
3852
- async load(id) {
3853
- if (id === "\0virtual:ox-content/search") {
3854
- const searchOptions = resolvedOptions.search;
3855
- if (!searchOptions.enabled) return "export const search = () => []; export const searchOptions = { enabled: false }; export default { search, searchOptions };";
3856
- return generateSearchModule(searchOptions, resolvedOptions.base + "search-index.json");
3857
- }
3858
- return null;
3859
- },
3860
- async buildStart() {
3861
- if (!resolvedOptions.search.enabled) return;
3862
- const root = config?.root || process.cwd();
3863
- const srcDir = path$1.resolve(root, resolvedOptions.srcDir);
3864
- try {
3865
- searchIndexJson = await buildSearchIndex(srcDir, resolvedOptions.base, resolvedOptions.extensions);
3866
- console.log("[ox-content] Search index built");
3867
- } catch (err) {
3868
- console.warn("[ox-content] Failed to build search index:", err);
3869
- }
3870
- },
3871
- async closeBundle() {
3872
- if (!resolvedOptions.search.enabled || !searchIndexJson) return;
3873
- const root = config?.root || process.cwd();
3874
- const outDir = path$1.resolve(root, resolvedOptions.outDir);
3875
- try {
3876
- await writeSearchIndex(searchIndexJson, outDir);
3877
- console.log("[ox-content] Search index written to", path$1.join(outDir, "search-index.json"));
3878
- } catch (err) {
3879
- console.warn("[ox-content] Failed to write search index:", err);
3880
- }
3841
+ return {
3842
+ name: "ox-content:search",
3843
+ resolveId(id) {
3844
+ if (id === "virtual:ox-content/search") return "\0virtual:ox-content/search";
3845
+ return null;
3846
+ },
3847
+ async load(id) {
3848
+ if (id !== "\0virtual:ox-content/search") return null;
3849
+ const searchOptions = resolvedOptions.search;
3850
+ if (!searchOptions.enabled) return "export const search = () => []; export const searchOptions = { enabled: false }; export default { search, searchOptions };";
3851
+ return generateSearchModule(searchOptions, resolvedOptions.base + "search-index.json");
3852
+ },
3853
+ async buildStart() {
3854
+ if (!resolvedOptions.search.enabled) return;
3855
+ const srcDir = path$1.resolve(getRoot(), resolvedOptions.srcDir);
3856
+ try {
3857
+ searchIndexJson = await buildSearchIndex(srcDir, resolvedOptions.base, resolvedOptions.extensions);
3858
+ console.log("[ox-content] Search index built");
3859
+ } catch (err) {
3860
+ console.warn("[ox-content] Failed to build search index:", err);
3861
+ }
3862
+ },
3863
+ async closeBundle() {
3864
+ if (!resolvedOptions.search.enabled || !searchIndexJson) return;
3865
+ const outDir = path$1.resolve(getRoot(), resolvedOptions.outDir);
3866
+ try {
3867
+ await writeSearchIndex(searchIndexJson, outDir);
3868
+ console.log("[ox-content] Search index written to", path$1.join(outDir, "search-index.json"));
3869
+ } catch (err) {
3870
+ console.warn("[ox-content] Failed to write search index:", err);
3881
3871
  }
3882
3872
  }
3883
- ];
3884
- if (resolvedOptions.i18n) plugins.push(createI18nPlugin(resolvedOptions));
3885
- if (resolvedOptions.ogViewer) plugins.push(createOgViewerPlugin(resolvedOptions));
3886
- return plugins;
3873
+ };
3887
3874
  }
3888
3875
  /**
3889
3876
  * Resolves plugin options with defaults.