@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/bin/migrate-vitepress.mjs +27 -0
- package/dist/index.cjs +339 -352
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +22 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +339 -352
- package/dist/index.mjs.map +1 -1
- package/dist/mermaid.cjs +19 -48
- package/dist/mermaid.cjs.map +1 -1
- package/dist/mermaid.mjs +2 -109
- package/dist/mermaid2.mjs +92 -2
- package/dist/mermaid2.mjs.map +1 -0
- package/dist/napi.cjs +45 -0
- package/dist/napi.cjs.map +1 -0
- package/dist/napi.mjs +34 -0
- package/dist/napi.mjs.map +1 -0
- package/dist/ogp.cjs +1 -1
- package/dist/ogp.cjs.map +1 -1
- package/dist/ogp2.mjs +1 -1
- package/dist/ogp2.mjs.map +1 -1
- package/dist/vitepress-cli.cjs +1 -1
- package/dist/vitepress-cli.mjs +1 -1
- package/dist/vitepress.cjs +2 -26
- package/dist/vitepress.cjs.map +1 -1
- package/dist/vitepress.mjs +2 -26
- package/dist/vitepress.mjs.map +1 -1
- package/package.json +5 -4
- package/dist/mermaid.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("./chunk.cjs");
|
|
3
|
+
const require_napi = require("./napi.cjs");
|
|
3
4
|
const require_mermaid = require("./mermaid.cjs");
|
|
4
5
|
const require_tabs = require("./tabs.cjs");
|
|
5
6
|
const require_youtube = require("./youtube.cjs");
|
|
@@ -17,12 +18,12 @@ let shiki = require("shiki");
|
|
|
17
18
|
let node_module = require("node:module");
|
|
18
19
|
let node_path = require("node:path");
|
|
19
20
|
node_path = require_chunk.__toESM(node_path, 1);
|
|
20
|
-
let fs = require("fs");
|
|
21
|
-
fs = require_chunk.__toESM(fs, 1);
|
|
22
21
|
let fs_promises = require("fs/promises");
|
|
23
22
|
fs_promises = require_chunk.__toESM(fs_promises, 1);
|
|
24
23
|
let crypto = require("crypto");
|
|
25
24
|
crypto = require_chunk.__toESM(crypto, 1);
|
|
25
|
+
let fs = require("fs");
|
|
26
|
+
fs = require_chunk.__toESM(fs, 1);
|
|
26
27
|
let glob = require("glob");
|
|
27
28
|
let node_fs_promises = require("node:fs/promises");
|
|
28
29
|
node_fs_promises = require_chunk.__toESM(node_fs_promises, 1);
|
|
@@ -432,7 +433,7 @@ async function loadNapiBindings() {
|
|
|
432
433
|
if (napiLoadAttempted) return napiBindings ?? null;
|
|
433
434
|
napiLoadAttempted = true;
|
|
434
435
|
try {
|
|
435
|
-
const mod = await
|
|
436
|
+
const mod = await require_napi.importNapiModule();
|
|
436
437
|
napiBindings = mod;
|
|
437
438
|
return mod;
|
|
438
439
|
} catch (error) {
|
|
@@ -545,67 +546,7 @@ if (import.meta.hot) {
|
|
|
545
546
|
`;
|
|
546
547
|
}
|
|
547
548
|
//#endregion
|
|
548
|
-
//#region src/nav-generator.ts
|
|
549
|
-
function generateNavMetadata(docs, basePath = "/api") {
|
|
550
|
-
return require_mermaid.importNapiModuleSync().generateDocsNavMetadata(docs.map((doc) => doc.file), basePath);
|
|
551
|
-
}
|
|
552
|
-
function generateNavCode(navItems, exportName = "apiNav") {
|
|
553
|
-
return require_mermaid.importNapiModuleSync().generateDocsNavCode(navItems, exportName);
|
|
554
|
-
}
|
|
555
|
-
//#endregion
|
|
556
549
|
//#region src/docs.ts
|
|
557
|
-
/**
|
|
558
|
-
* Source Documentation Extraction and Generation
|
|
559
|
-
*
|
|
560
|
-
* This module provides comprehensive tools for extracting JSDoc/TSDoc comments
|
|
561
|
-
* from TypeScript/JavaScript source files and automatically generating Markdown
|
|
562
|
-
* documentation.
|
|
563
|
-
*
|
|
564
|
-
* ## Features
|
|
565
|
-
*
|
|
566
|
-
* - **Automatic Extraction**: Parses JSDoc comments from functions, classes, interfaces, and types
|
|
567
|
-
* - **Flexible Filtering**: Include/exclude patterns for selective documentation
|
|
568
|
-
* - **Markdown Generation**: Converts extracted docs to organized Markdown files
|
|
569
|
-
* - **Navigation Generation**: Auto-generates sidebar navigation metadata
|
|
570
|
-
* - **GitHub Links**: Includes clickable links to source code on GitHub
|
|
571
|
-
*
|
|
572
|
-
* ## Supported JSDoc Tags
|
|
573
|
-
*
|
|
574
|
-
* - `@param {type} name - description` - Function parameter documentation
|
|
575
|
-
* - `@returns {type} description` - Return value documentation
|
|
576
|
-
* - `@example` - Code examples (multi-line blocks)
|
|
577
|
-
* - `@private` - Mark item as private (excluded from docs if private=false)
|
|
578
|
-
* - `@default value` - Default parameter value
|
|
579
|
-
* - Custom tags are preserved in the `tags` field
|
|
580
|
-
*
|
|
581
|
-
* ## Usage Flow
|
|
582
|
-
*
|
|
583
|
-
* 1. Call `extractDocs()` to parse source files
|
|
584
|
-
* 2. Call `generateMarkdown()` to create Markdown content
|
|
585
|
-
* 3. Call `writeDocs()` to write files to output directory
|
|
586
|
-
* 4. Generated nav.ts can be imported for sidebar navigation
|
|
587
|
-
*
|
|
588
|
-
* @example
|
|
589
|
-
* ```typescript
|
|
590
|
-
* import { extractDocs, generateMarkdown, writeDocs } from './docs';
|
|
591
|
-
*
|
|
592
|
-
* const docsOptions = {
|
|
593
|
-
* enabled: true,
|
|
594
|
-
* src: ['./src'],
|
|
595
|
-
* out: './docs/api',
|
|
596
|
-
* include: ['**\/*.ts'],
|
|
597
|
-
* exclude: ['**\/*.test.ts'],
|
|
598
|
-
* groupBy: 'file',
|
|
599
|
-
* githubUrl: 'https://github.com/user/project',
|
|
600
|
-
* };
|
|
601
|
-
*
|
|
602
|
-
* const extracted = await extractDocs(['./src'], docsOptions);
|
|
603
|
-
* const markdown = generateMarkdown(extracted, docsOptions);
|
|
604
|
-
* await writeDocs(markdown, './docs/api', extracted, docsOptions);
|
|
605
|
-
* ```
|
|
606
|
-
*/
|
|
607
|
-
const DOCS_MANIFEST_FILE = ".ox-content-docs-manifest.json";
|
|
608
|
-
const DOCS_DATA_FILE = "docs.json";
|
|
609
550
|
const DEFAULT_DOCS_INCLUDE = [
|
|
610
551
|
"**/*.ts",
|
|
611
552
|
"**/*.tsx",
|
|
@@ -682,7 +623,7 @@ const DEFAULT_DOCS_INCLUDE = [
|
|
|
682
623
|
* ```
|
|
683
624
|
*/
|
|
684
625
|
async function extractDocs(srcDirs, options) {
|
|
685
|
-
const napi = await
|
|
626
|
+
const napi = await require_napi.importNapiModule();
|
|
686
627
|
if (options.entryPoints?.length) {
|
|
687
628
|
const extractDocsFromEntryPoints = napi.extractDocsFromEntryPoints;
|
|
688
629
|
if (!extractDocsFromEntryPoints) throw new Error("[ox-content] extractDocsFromEntryPoints is not available from @ox-content/napi.");
|
|
@@ -695,65 +636,40 @@ async function extractDocs(srcDirs, options) {
|
|
|
695
636
|
entries: doc.entries
|
|
696
637
|
}));
|
|
697
638
|
}
|
|
698
|
-
const
|
|
699
|
-
if (!
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
const entries = extractFileDocEntries(file, options.private, options.internal);
|
|
705
|
-
if (entries.length > 0) results.push({
|
|
706
|
-
file,
|
|
707
|
-
entries
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
return results;
|
|
639
|
+
const extractDocsFromDirectories = napi.extractDocsFromDirectories;
|
|
640
|
+
if (!extractDocsFromDirectories) throw new Error("[ox-content] extractDocsFromDirectories is not available from @ox-content/napi.");
|
|
641
|
+
return extractDocsFromDirectories(srcDirs, options.include, options.exclude, options.private, options.internal).map((doc) => ({
|
|
642
|
+
file: doc.file,
|
|
643
|
+
entries: doc.entries
|
|
644
|
+
}));
|
|
712
645
|
}
|
|
713
646
|
/**
|
|
714
647
|
* Generates Markdown documentation from extracted docs.
|
|
715
648
|
*/
|
|
716
649
|
function generateMarkdown(docs, options) {
|
|
717
|
-
const napi =
|
|
650
|
+
const napi = require_napi.importNapiModuleSync();
|
|
718
651
|
if (typeof napi.generateDocsMarkdown !== "function") throw new Error("[ox-content] generateDocsMarkdown is not available from @ox-content/napi. Please rebuild the NAPI package.");
|
|
719
652
|
return napi.generateDocsMarkdown(toRustDocsModules(docs), {
|
|
720
653
|
groupBy: options.groupBy,
|
|
721
|
-
githubUrl: options.githubUrl
|
|
654
|
+
githubUrl: options.githubUrl,
|
|
655
|
+
linkStyle: options.linkStyle,
|
|
656
|
+
basePath: options.basePath,
|
|
657
|
+
pathStrategy: options.pathStrategy
|
|
722
658
|
});
|
|
723
659
|
}
|
|
724
660
|
/**
|
|
725
661
|
* Writes generated documentation to the output directory.
|
|
726
662
|
*/
|
|
727
663
|
async function writeDocs(docs, outDir, extractedDocs, options) {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
}
|
|
737
|
-
previousFiles = [];
|
|
738
|
-
}
|
|
739
|
-
for (const staleFile of previousFiles) {
|
|
740
|
-
if (generatedFiles.has(staleFile)) continue;
|
|
741
|
-
await fs.promises.rm(path.join(outDir, staleFile), { force: true });
|
|
742
|
-
}
|
|
743
|
-
for (const [fileName, content] of Object.entries(docs)) {
|
|
744
|
-
const filePath = path.join(outDir, fileName);
|
|
745
|
-
await fs.promises.writeFile(filePath, content, "utf-8");
|
|
746
|
-
}
|
|
747
|
-
if (extractedDocs && options?.generateNav && options.groupBy === "file") {
|
|
748
|
-
const navCode = generateNavCode(generateNavMetadata(extractedDocs, "/api"), "apiNav");
|
|
749
|
-
const navFilePath = path.join(outDir, "nav.ts");
|
|
750
|
-
await fs.promises.writeFile(navFilePath, navCode, "utf-8");
|
|
751
|
-
}
|
|
752
|
-
if (extractedDocs) {
|
|
753
|
-
const napi = require_mermaid.importNapiModuleSync();
|
|
754
|
-
await fs.promises.writeFile(path.join(outDir, DOCS_DATA_FILE), napi.generateDocsDataJson(toRustDocsModules(extractedDocs), (/* @__PURE__ */ new Date()).toISOString()), "utf-8");
|
|
755
|
-
}
|
|
756
|
-
await fs.promises.writeFile(manifestPath, JSON.stringify([...generatedFiles].sort(), null, 2), "utf-8");
|
|
664
|
+
const napi = require_napi.importNapiModuleSync();
|
|
665
|
+
if (typeof napi.writeGeneratedDocs !== "function") throw new Error("[ox-content] writeGeneratedDocs is not available from @ox-content/napi. Please rebuild the NAPI package.");
|
|
666
|
+
napi.writeGeneratedDocs(docs, outDir, extractedDocs ? toRustDocsModules(extractedDocs) : void 0, {
|
|
667
|
+
generateNav: options?.generateNav ?? false,
|
|
668
|
+
groupBy: options?.groupBy ?? "file",
|
|
669
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
670
|
+
basePath: options?.basePath,
|
|
671
|
+
pathStrategy: options?.pathStrategy
|
|
672
|
+
});
|
|
757
673
|
}
|
|
758
674
|
function toRustDocsModules(docs) {
|
|
759
675
|
return docs.map((doc) => ({
|
|
@@ -778,9 +694,6 @@ function toRustDocsModules(docs) {
|
|
|
778
694
|
}))
|
|
779
695
|
}));
|
|
780
696
|
}
|
|
781
|
-
/**
|
|
782
|
-
* Resolves docs options with defaults.
|
|
783
|
-
*/
|
|
784
697
|
function resolveDocsOptions(options) {
|
|
785
698
|
if (options === false) return false;
|
|
786
699
|
const opts = options || {};
|
|
@@ -801,6 +714,9 @@ function resolveDocsOptions(options) {
|
|
|
801
714
|
toc: false,
|
|
802
715
|
groupBy: opts.groupBy ?? "file",
|
|
803
716
|
githubUrl: opts.githubUrl,
|
|
717
|
+
linkStyle: opts.linkStyle ?? "markdown",
|
|
718
|
+
basePath: opts.basePath,
|
|
719
|
+
pathStrategy: opts.pathStrategy ?? "flat",
|
|
804
720
|
generateNav: opts.generateNav ?? true
|
|
805
721
|
};
|
|
806
722
|
}
|
|
@@ -888,6 +804,8 @@ async function renderHtmlToPng(page, html, width, height, publicDir) {
|
|
|
888
804
|
}
|
|
889
805
|
//#endregion
|
|
890
806
|
//#region src/og-image/browser.ts
|
|
807
|
+
const PLAYWRIGHT_BROWSER_INSTALL_HINT = "Install Playwright browsers with `npx playwright install chromium` to enable OG image generation.";
|
|
808
|
+
let chromiumUnavailableWarned = false;
|
|
891
809
|
/**
|
|
892
810
|
* Opens a Chromium browser and returns a session for rendering OG images.
|
|
893
811
|
* Returns null if Playwright/Chromium is not available.
|
|
@@ -927,10 +845,20 @@ async function openBrowser() {
|
|
|
927
845
|
}
|
|
928
846
|
};
|
|
929
847
|
} catch (err) {
|
|
930
|
-
|
|
848
|
+
warnChromiumUnavailableOnce(err);
|
|
931
849
|
return null;
|
|
932
850
|
}
|
|
933
851
|
}
|
|
852
|
+
function warnChromiumUnavailableOnce(err) {
|
|
853
|
+
if (chromiumUnavailableWarned) return;
|
|
854
|
+
chromiumUnavailableWarned = true;
|
|
855
|
+
console.warn(`[ox-content:og-image] Chromium not available, skipping OG image generation. ${formatChromiumUnavailableDetail(err)}`);
|
|
856
|
+
}
|
|
857
|
+
function formatChromiumUnavailableDetail(err) {
|
|
858
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
859
|
+
if (message.includes("Executable doesn't exist") || message.includes("Please run the following command to download new browsers")) return PLAYWRIGHT_BROWSER_INSTALL_HINT;
|
|
860
|
+
return message.split(/\r?\n/).find((line) => line.trim())?.trim() ?? "Unknown launch error.";
|
|
861
|
+
}
|
|
934
862
|
//#endregion
|
|
935
863
|
//#region src/og-image/template.ts
|
|
936
864
|
/**
|
|
@@ -1732,19 +1660,19 @@ function resolveSsgOptions(ssg) {
|
|
|
1732
1660
|
* Extracts title from content or frontmatter.
|
|
1733
1661
|
*/
|
|
1734
1662
|
function extractTitle$1(content, frontmatter) {
|
|
1735
|
-
return
|
|
1663
|
+
return require_napi.importNapiModuleSync().extractSsgTitle(content, typeof frontmatter.title === "string" ? frontmatter.title : void 0);
|
|
1736
1664
|
}
|
|
1737
1665
|
/**
|
|
1738
1666
|
* Generates bare HTML page (no navigation, no styles).
|
|
1739
1667
|
*/
|
|
1740
1668
|
function generateBareHtmlPage(content, title) {
|
|
1741
|
-
return
|
|
1669
|
+
return require_napi.importNapiModuleSync().generateSsgBareHtml(content, title);
|
|
1742
1670
|
}
|
|
1743
1671
|
/**
|
|
1744
1672
|
* Generates HTML page with navigation using Rust NAPI bindings.
|
|
1745
1673
|
*/
|
|
1746
1674
|
async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, theme, locale, availableLocales) {
|
|
1747
|
-
const mod = await
|
|
1675
|
+
const mod = await require_napi.importNapiModule();
|
|
1748
1676
|
const toRustTocEntry = (entry) => ({
|
|
1749
1677
|
depth: entry.depth,
|
|
1750
1678
|
text: entry.text,
|
|
@@ -1818,7 +1746,7 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
|
|
|
1818
1746
|
});
|
|
1819
1747
|
}
|
|
1820
1748
|
async function externalizeSharedPageAssets(pages, outDir, base) {
|
|
1821
|
-
const optimized = (await
|
|
1749
|
+
const optimized = (await require_napi.importNapiModule()).externalizeSsgAssets(pages, outDir, base);
|
|
1822
1750
|
await Promise.all(optimized.assets.map(async (asset) => {
|
|
1823
1751
|
await fs_promises.mkdir(path.dirname(asset.outputPath), { recursive: true });
|
|
1824
1752
|
await fs_promises.writeFile(asset.outputPath, asset.content, "utf-8");
|
|
@@ -1832,45 +1760,45 @@ async function externalizeSharedPageAssets(pages, outDir, base) {
|
|
|
1832
1760
|
* Converts a markdown file path to a relative URL path.
|
|
1833
1761
|
*/
|
|
1834
1762
|
function getUrlPath$1(inputPath, srcDir) {
|
|
1835
|
-
return
|
|
1763
|
+
return require_napi.importNapiModuleSync().getSsgUrlPath(inputPath, srcDir);
|
|
1836
1764
|
}
|
|
1837
1765
|
/**
|
|
1838
1766
|
* Resolves manual navigation config to the format used by the built-in SSG renderer.
|
|
1839
1767
|
*/
|
|
1840
1768
|
function resolveNavigationGroups(navigation, base, extension) {
|
|
1841
1769
|
if (!navigation) return;
|
|
1842
|
-
return
|
|
1770
|
+
return require_napi.importNapiModuleSync().resolveSsgNavigationGroups(navigation, base, extension);
|
|
1843
1771
|
}
|
|
1844
1772
|
function getPageLocale(urlPath, i18n) {
|
|
1845
1773
|
if (!i18n) return void 0;
|
|
1846
|
-
return
|
|
1774
|
+
return require_napi.importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, i18n.locales.map((locale) => locale.code)) ?? void 0;
|
|
1847
1775
|
}
|
|
1848
1776
|
function getRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl) {
|
|
1849
|
-
return
|
|
1777
|
+
return require_napi.importNapiModuleSync().resolveSsgRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl);
|
|
1850
1778
|
}
|
|
1851
1779
|
/**
|
|
1852
1780
|
* Formats a file/dir name as a title.
|
|
1853
1781
|
*/
|
|
1854
1782
|
function formatTitle(name) {
|
|
1855
|
-
return
|
|
1783
|
+
return require_napi.importNapiModuleSync().formatSsgTitle(name);
|
|
1856
1784
|
}
|
|
1857
1785
|
/**
|
|
1858
1786
|
* Collects all markdown files from the source directory.
|
|
1859
1787
|
*/
|
|
1860
1788
|
async function collectMarkdownFiles(srcDir, extensions = DEFAULT_MARKDOWN_EXTENSIONS) {
|
|
1861
|
-
return
|
|
1789
|
+
return require_napi.importNapiModuleSync().collectSsgMarkdownFiles(srcDir, [...extensions]);
|
|
1862
1790
|
}
|
|
1863
1791
|
/**
|
|
1864
1792
|
* Builds navigation items from markdown files, grouped by directory.
|
|
1865
1793
|
*/
|
|
1866
1794
|
function buildNavItems(markdownFiles, srcDir, base, extension) {
|
|
1867
|
-
return
|
|
1795
|
+
return require_napi.importNapiModuleSync().buildSsgNavItems(markdownFiles, srcDir, base, extension);
|
|
1868
1796
|
}
|
|
1869
1797
|
/**
|
|
1870
1798
|
* Builds navigation items from an explicit theme sidebar tree.
|
|
1871
1799
|
*/
|
|
1872
1800
|
function buildThemeNavItems(sidebar, base, extension) {
|
|
1873
|
-
return
|
|
1801
|
+
return require_napi.importNapiModuleSync().buildSsgThemeNavItems(sidebar, base, extension);
|
|
1874
1802
|
}
|
|
1875
1803
|
/**
|
|
1876
1804
|
* Builds all markdown files to static HTML.
|
|
@@ -1883,149 +1811,201 @@ async function buildSsg(options, root) {
|
|
|
1883
1811
|
};
|
|
1884
1812
|
const srcDir = path.resolve(root, options.srcDir);
|
|
1885
1813
|
const outDir = path.resolve(root, options.outDir);
|
|
1886
|
-
const base = options.base.endsWith("/") ? options.base : options.base + "/";
|
|
1887
1814
|
const generatedFiles = [];
|
|
1888
|
-
const generatedPages = [];
|
|
1889
1815
|
const errors = [];
|
|
1890
|
-
|
|
1816
|
+
await cleanOutputDirectory(ssgOptions, outDir);
|
|
1817
|
+
const markdownFiles = await collectMarkdownFiles(srcDir, options.extensions);
|
|
1818
|
+
const context = await createBuildSsgContext(options, root, srcDir, outDir, markdownFiles);
|
|
1819
|
+
const collected = await collectPageResults(context, markdownFiles);
|
|
1820
|
+
errors.push(...collected.errors);
|
|
1821
|
+
await generateOgImageAssets(context, collected, generatedFiles, errors);
|
|
1822
|
+
await writeGeneratedPages(await generateHtmlPages(context, collected.pageResults, collected, errors), context, generatedFiles);
|
|
1823
|
+
return {
|
|
1824
|
+
files: generatedFiles,
|
|
1825
|
+
errors
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
async function cleanOutputDirectory(ssgOptions, outDir) {
|
|
1829
|
+
if (!ssgOptions.clean) return;
|
|
1830
|
+
try {
|
|
1891
1831
|
await fs_promises.rm(outDir, {
|
|
1892
1832
|
recursive: true,
|
|
1893
1833
|
force: true
|
|
1894
1834
|
});
|
|
1895
1835
|
} catch {}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1836
|
+
}
|
|
1837
|
+
async function createBuildSsgContext(options, root, srcDir, outDir, markdownFiles) {
|
|
1838
|
+
const ssgOptions = options.ssg;
|
|
1839
|
+
const base = options.base.endsWith("/") ? options.base : options.base + "/";
|
|
1840
|
+
return {
|
|
1841
|
+
options,
|
|
1842
|
+
ssgOptions,
|
|
1843
|
+
root,
|
|
1844
|
+
srcDir,
|
|
1845
|
+
outDir,
|
|
1846
|
+
base,
|
|
1847
|
+
navItems: resolveNavigationGroups(ssgOptions.navigation, base, ssgOptions.extension) ?? (ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension)),
|
|
1848
|
+
siteName: await resolveSiteName$1(root, ssgOptions),
|
|
1849
|
+
shouldGenerateOgImages: (options.ogImage || ssgOptions.generateOgImage) && !ssgOptions.bare,
|
|
1850
|
+
napi: ssgOptions.lastUpdated ? await require_napi.importNapiModule() : void 0
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
async function resolveSiteName$1(root, ssgOptions) {
|
|
1854
|
+
if (ssgOptions.siteName) return ssgOptions.siteName;
|
|
1855
|
+
try {
|
|
1900
1856
|
const pkgPath = path.join(root, "package.json");
|
|
1901
1857
|
const pkg = JSON.parse(await fs_promises.readFile(pkgPath, "utf-8"));
|
|
1902
|
-
|
|
1903
|
-
} catch {
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
const
|
|
1909
|
-
|
|
1858
|
+
return pkg.name ? formatTitle(pkg.name) : "Documentation";
|
|
1859
|
+
} catch {
|
|
1860
|
+
return "Documentation";
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
async function collectPageResults(context, markdownFiles) {
|
|
1864
|
+
const collected = {
|
|
1865
|
+
pageResults: [],
|
|
1866
|
+
ogImageEntries: [],
|
|
1867
|
+
ogImageInputPaths: [],
|
|
1868
|
+
ogImageUrlMap: /* @__PURE__ */ new Map(),
|
|
1869
|
+
errors: []
|
|
1870
|
+
};
|
|
1910
1871
|
for (const inputPath of markdownFiles) try {
|
|
1911
|
-
const
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
sourcePath: inputPath
|
|
1915
|
-
});
|
|
1916
|
-
const frontmatter = require_vitepress.normalizeVitePressFrontmatter(result.frontmatter);
|
|
1917
|
-
let transformedHtml = result.html;
|
|
1918
|
-
const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(transformedHtml);
|
|
1919
|
-
transformedHtml = protectedHtml;
|
|
1920
|
-
const pluginOptions = {
|
|
1921
|
-
tabs: true,
|
|
1922
|
-
youtube: true,
|
|
1923
|
-
github: options.embeds.github,
|
|
1924
|
-
openGraph: options.embeds.openGraph,
|
|
1925
|
-
mermaid: true,
|
|
1926
|
-
githubToken: process.env.GITHUB_TOKEN
|
|
1927
|
-
};
|
|
1928
|
-
transformedHtml = await transformAllPlugins(transformedHtml, pluginOptions);
|
|
1929
|
-
if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
|
|
1930
|
-
transformedHtml = restoreMermaidSvgs(transformedHtml, mermaidSvgs);
|
|
1931
|
-
const title = extractTitle$1(transformedHtml, frontmatter);
|
|
1932
|
-
const description = frontmatter.description;
|
|
1933
|
-
const routePaths = getRoutePaths(inputPath, srcDir, outDir, base, ssgOptions.extension, ssgOptions.siteUrl);
|
|
1934
|
-
pageResults.push({
|
|
1935
|
-
inputPath,
|
|
1936
|
-
routePaths,
|
|
1937
|
-
transformedHtml,
|
|
1938
|
-
title,
|
|
1939
|
-
description,
|
|
1940
|
-
lastUpdated: napi?.getGitLastUpdated(inputPath, root) ?? void 0,
|
|
1941
|
-
frontmatter,
|
|
1942
|
-
toc: result.toc
|
|
1943
|
-
});
|
|
1944
|
-
if (shouldGenerateOgImages) {
|
|
1945
|
-
const { layout: _layout, ...frontmatterRest } = frontmatter;
|
|
1946
|
-
ogImageEntries.push({
|
|
1947
|
-
props: {
|
|
1948
|
-
...frontmatterRest,
|
|
1949
|
-
title,
|
|
1950
|
-
description,
|
|
1951
|
-
siteName
|
|
1952
|
-
},
|
|
1953
|
-
outputPath: routePaths.ogImagePath
|
|
1954
|
-
});
|
|
1955
|
-
ogImageInputPaths.push(inputPath);
|
|
1956
|
-
ogImageUrlMap.set(inputPath, routePaths.ogImageUrl);
|
|
1957
|
-
}
|
|
1872
|
+
const pageResult = await transformSsgPage(context, inputPath);
|
|
1873
|
+
collected.pageResults.push(pageResult);
|
|
1874
|
+
collectOgImageEntry(context, pageResult, collected);
|
|
1958
1875
|
} catch (err) {
|
|
1959
1876
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1960
|
-
errors.push(`Failed to process ${inputPath}: ${errorMessage}`);
|
|
1877
|
+
collected.errors.push(`Failed to process ${inputPath}: ${errorMessage}`);
|
|
1961
1878
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1879
|
+
return collected;
|
|
1880
|
+
}
|
|
1881
|
+
async function transformSsgPage(context, inputPath) {
|
|
1882
|
+
const result = await transformMarkdown(await fs_promises.readFile(inputPath, "utf-8"), inputPath, context.options, {
|
|
1883
|
+
convertMdLinks: true,
|
|
1884
|
+
baseUrl: context.base,
|
|
1885
|
+
sourcePath: inputPath
|
|
1886
|
+
});
|
|
1887
|
+
const frontmatter = require_vitepress.normalizeVitePressFrontmatter(result.frontmatter);
|
|
1888
|
+
const transformedHtml = await transformSsgHtml(result.html, context.options);
|
|
1889
|
+
const title = extractTitle$1(transformedHtml, frontmatter);
|
|
1890
|
+
return {
|
|
1891
|
+
inputPath,
|
|
1892
|
+
routePaths: getRoutePaths(inputPath, context.srcDir, context.outDir, context.base, context.ssgOptions.extension, context.ssgOptions.siteUrl),
|
|
1893
|
+
transformedHtml,
|
|
1894
|
+
title,
|
|
1895
|
+
description: frontmatter.description,
|
|
1896
|
+
lastUpdated: context.napi?.getGitLastUpdated(inputPath, context.root) ?? void 0,
|
|
1897
|
+
frontmatter,
|
|
1898
|
+
toc: result.toc
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
async function transformSsgHtml(html, options) {
|
|
1902
|
+
const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(html);
|
|
1903
|
+
let transformedHtml = await transformAllPlugins(protectedHtml, {
|
|
1904
|
+
tabs: true,
|
|
1905
|
+
youtube: true,
|
|
1906
|
+
github: options.embeds.github,
|
|
1907
|
+
openGraph: options.embeds.openGraph,
|
|
1908
|
+
mermaid: true,
|
|
1909
|
+
githubToken: process.env.GITHUB_TOKEN
|
|
1910
|
+
});
|
|
1911
|
+
if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
|
|
1912
|
+
return restoreMermaidSvgs(transformedHtml, mermaidSvgs);
|
|
1913
|
+
}
|
|
1914
|
+
function collectOgImageEntry(context, pageResult, collected) {
|
|
1915
|
+
if (!context.shouldGenerateOgImages) return;
|
|
1916
|
+
const { layout: _layout, ...frontmatterRest } = pageResult.frontmatter;
|
|
1917
|
+
collected.ogImageEntries.push({
|
|
1918
|
+
props: {
|
|
1919
|
+
...frontmatterRest,
|
|
1920
|
+
title: pageResult.title,
|
|
1921
|
+
description: pageResult.description,
|
|
1922
|
+
siteName: context.siteName
|
|
1923
|
+
},
|
|
1924
|
+
outputPath: pageResult.routePaths.ogImagePath
|
|
1925
|
+
});
|
|
1926
|
+
collected.ogImageInputPaths.push(pageResult.inputPath);
|
|
1927
|
+
collected.ogImageUrlMap.set(pageResult.inputPath, pageResult.routePaths.ogImageUrl);
|
|
1928
|
+
}
|
|
1929
|
+
async function generateOgImageAssets(context, collected, generatedFiles, errors) {
|
|
1930
|
+
if (!context.shouldGenerateOgImages || collected.ogImageEntries.length === 0) return;
|
|
1931
|
+
try {
|
|
1932
|
+
const ogResults = await generateOgImages(collected.ogImageEntries, context.options.ogImageOptions, context.root);
|
|
1933
|
+
if (clearMissingBrowserOgImages(ogResults, collected)) return;
|
|
1934
|
+
reportOgImageResults(ogResults, collected, generatedFiles, errors);
|
|
1979
1935
|
} catch (err) {
|
|
1980
1936
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1981
1937
|
console.warn(`[ox-content:og-image] Batch generation failed: ${errorMessage}`);
|
|
1982
|
-
ogImageUrlMap.clear();
|
|
1938
|
+
collected.ogImageUrlMap.clear();
|
|
1983
1939
|
}
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
if (
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
toc,
|
|
2001
|
-
lastUpdated,
|
|
2002
|
-
frontmatter,
|
|
2003
|
-
path: routePaths.urlPath,
|
|
2004
|
-
href: routePaths.href,
|
|
2005
|
-
entryPage
|
|
2006
|
-
};
|
|
2007
|
-
html = await generateHtmlPage(pageData, navItems, siteName, base, pageOgImage, ssgOptions.theme, getPageLocale(pageData.path, options.i18n), options.i18n ? options.i18n.locales : void 0);
|
|
1940
|
+
}
|
|
1941
|
+
function clearMissingBrowserOgImages(ogResults, collected) {
|
|
1942
|
+
if (!(ogResults.length > 0 && ogResults.every((result) => result.error === "Chromium not available"))) return false;
|
|
1943
|
+
for (const inputPath of collected.ogImageInputPaths) collected.ogImageUrlMap.delete(inputPath);
|
|
1944
|
+
return true;
|
|
1945
|
+
}
|
|
1946
|
+
function reportOgImageResults(ogResults, collected, generatedFiles, errors) {
|
|
1947
|
+
let ogSuccessCount = 0;
|
|
1948
|
+
for (let i = 0; i < ogResults.length; i++) {
|
|
1949
|
+
const result = ogResults[i];
|
|
1950
|
+
if (result.error) {
|
|
1951
|
+
errors.push(`OG image failed for ${result.outputPath}: ${result.error}`);
|
|
1952
|
+
collected.ogImageUrlMap.delete(collected.ogImageInputPaths[i]);
|
|
1953
|
+
} else {
|
|
1954
|
+
generatedFiles.push(result.outputPath);
|
|
1955
|
+
ogSuccessCount++;
|
|
2008
1956
|
}
|
|
1957
|
+
}
|
|
1958
|
+
if (ogSuccessCount > 0) {
|
|
1959
|
+
const cachedCount = ogResults.filter((result) => result.cached && !result.error).length;
|
|
1960
|
+
console.log(`[ox-content:og-image] Generated ${ogSuccessCount} OG images` + (cachedCount > 0 ? ` (${cachedCount} from cache)` : ""));
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
async function generateHtmlPages(context, pageResults, collected, errors) {
|
|
1964
|
+
const generatedPages = [];
|
|
1965
|
+
for (const pageResult of pageResults) try {
|
|
2009
1966
|
generatedPages.push({
|
|
2010
|
-
inputPath,
|
|
2011
|
-
outputPath: routePaths.outputPath,
|
|
2012
|
-
html
|
|
1967
|
+
inputPath: pageResult.inputPath,
|
|
1968
|
+
outputPath: pageResult.routePaths.outputPath,
|
|
1969
|
+
html: await renderSsgPage(context, pageResult, collected.ogImageUrlMap)
|
|
2013
1970
|
});
|
|
2014
1971
|
} catch (err) {
|
|
2015
1972
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
2016
1973
|
errors.push(`Failed to generate HTML for ${pageResult.inputPath}: ${errorMessage}`);
|
|
2017
1974
|
}
|
|
2018
|
-
|
|
1975
|
+
return generatedPages;
|
|
1976
|
+
}
|
|
1977
|
+
async function renderSsgPage(context, pageResult, ogImageUrlMap) {
|
|
1978
|
+
if (context.ssgOptions.bare) return generateBareHtmlPage(pageResult.transformedHtml, pageResult.title);
|
|
1979
|
+
const pageData = createSsgPageData(pageResult);
|
|
1980
|
+
const pageOgImage = context.shouldGenerateOgImages && ogImageUrlMap.has(pageResult.inputPath) ? ogImageUrlMap.get(pageResult.inputPath) : context.ssgOptions.ogImage;
|
|
1981
|
+
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);
|
|
1982
|
+
}
|
|
1983
|
+
function createSsgPageData(pageResult) {
|
|
1984
|
+
const { frontmatter } = pageResult;
|
|
1985
|
+
const entryPage = frontmatter.layout === "entry" ? {
|
|
1986
|
+
hero: frontmatter.hero,
|
|
1987
|
+
features: frontmatter.features
|
|
1988
|
+
} : void 0;
|
|
1989
|
+
return {
|
|
1990
|
+
title: pageResult.title,
|
|
1991
|
+
description: pageResult.description,
|
|
1992
|
+
content: pageResult.transformedHtml,
|
|
1993
|
+
toc: pageResult.toc,
|
|
1994
|
+
lastUpdated: pageResult.lastUpdated,
|
|
1995
|
+
frontmatter,
|
|
1996
|
+
path: pageResult.routePaths.urlPath,
|
|
1997
|
+
href: pageResult.routePaths.href,
|
|
1998
|
+
entryPage
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
async function writeGeneratedPages(generatedPages, context, generatedFiles) {
|
|
2002
|
+
const optimizedOutput = await externalizeSharedPageAssets(generatedPages, context.outDir, context.base);
|
|
2019
2003
|
generatedFiles.push(...optimizedOutput.assets);
|
|
2020
2004
|
for (const page of optimizedOutput.pages) {
|
|
2021
2005
|
await fs_promises.mkdir(path.dirname(page.outputPath), { recursive: true });
|
|
2022
2006
|
await fs_promises.writeFile(page.outputPath, page.html, "utf-8");
|
|
2023
2007
|
generatedFiles.push(page.outputPath);
|
|
2024
2008
|
}
|
|
2025
|
-
return {
|
|
2026
|
-
files: generatedFiles,
|
|
2027
|
-
errors
|
|
2028
|
-
};
|
|
2029
2009
|
}
|
|
2030
2010
|
//#endregion
|
|
2031
2011
|
//#region src/search.ts
|
|
@@ -2037,7 +2017,7 @@ async function buildSsg(options, root) {
|
|
|
2037
2017
|
let oxContent$1 = null;
|
|
2038
2018
|
async function getOxContent() {
|
|
2039
2019
|
if (!oxContent$1) try {
|
|
2040
|
-
oxContent$1 = await
|
|
2020
|
+
oxContent$1 = await require_napi.importNapiModule();
|
|
2041
2021
|
} catch {
|
|
2042
2022
|
console.warn("[ox-content] Native bindings not available, search disabled");
|
|
2043
2023
|
return null;
|
|
@@ -2091,7 +2071,7 @@ async function writeSearchIndex(indexJson, outDir) {
|
|
|
2091
2071
|
* This is injected into the bundle as a virtual module.
|
|
2092
2072
|
*/
|
|
2093
2073
|
function generateSearchModule(options, indexPath) {
|
|
2094
|
-
return
|
|
2074
|
+
return require_napi.importNapiModuleSync().generateSearchModuleFromOptions(options, indexPath);
|
|
2095
2075
|
}
|
|
2096
2076
|
//#endregion
|
|
2097
2077
|
//#region src/dev-server.ts
|
|
@@ -2705,7 +2685,7 @@ function createI18nPlugin(resolvedOptions) {
|
|
|
2705
2685
|
return;
|
|
2706
2686
|
}
|
|
2707
2687
|
try {
|
|
2708
|
-
const { checkI18nProject } = await
|
|
2688
|
+
const { checkI18nProject } = await require_napi.importNapiModule();
|
|
2709
2689
|
const checkResult = checkI18nProject(dictDir, [path.resolve(root, "src"), path.resolve(root, "content")], i18nOptions.functionNames, i18nOptions.defaultLocale);
|
|
2710
2690
|
if (checkResult.errorCount > 0 || checkResult.warningCount > 0) {
|
|
2711
2691
|
for (const diag of checkResult.diagnostics) if (diag.severity === "error") console.error(`[ox-content:i18n] ${diag.message}`);
|
|
@@ -2859,7 +2839,10 @@ function stripMaskedDocument(result) {
|
|
|
2859
2839
|
};
|
|
2860
2840
|
}
|
|
2861
2841
|
function normalizeLintOptions(options) {
|
|
2862
|
-
const
|
|
2842
|
+
const standardDictionary = options.dictionary?.standard && typeof options.dictionary.standard === "object" ? options.dictionary.standard : void 0;
|
|
2843
|
+
const optionLanguages = options.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language));
|
|
2844
|
+
const standardLanguages = standardDictionary?.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language));
|
|
2845
|
+
const languages = optionLanguages ?? standardLanguages ?? [...DEFAULT_LANGUAGES];
|
|
2863
2846
|
const standard = normalizeStandardDictionaryOptions(options.dictionary?.standard, languages);
|
|
2864
2847
|
return {
|
|
2865
2848
|
dictionary: {
|
|
@@ -2900,6 +2883,12 @@ async function runStandardSpellcheckDocuments(maskedDocuments, options) {
|
|
|
2900
2883
|
const { spellCheckDocument } = await loadCspellLib();
|
|
2901
2884
|
const locale = standard.languages.join(",");
|
|
2902
2885
|
const settings = createStandardSpellcheckSettings(options, locale);
|
|
2886
|
+
const spellCheckOptions = {
|
|
2887
|
+
generateSuggestions: true,
|
|
2888
|
+
noConfigSearch: true,
|
|
2889
|
+
numSuggestions: 3,
|
|
2890
|
+
resolveImportsRelativeTo: standard.resolveImportsRelativeTo
|
|
2891
|
+
};
|
|
2903
2892
|
return Promise.all(maskedDocuments.map(async (maskedDocument, index) => {
|
|
2904
2893
|
if (maskedDocument.trim().length === 0) return [];
|
|
2905
2894
|
return (await spellCheckDocument({
|
|
@@ -2907,12 +2896,7 @@ async function runStandardSpellcheckDocuments(maskedDocuments, options) {
|
|
|
2907
2896
|
locale,
|
|
2908
2897
|
text: maskedDocument,
|
|
2909
2898
|
uri: `file:///ox-content-lint-${index}.md`
|
|
2910
|
-
},
|
|
2911
|
-
generateSuggestions: true,
|
|
2912
|
-
noConfigSearch: true,
|
|
2913
|
-
numSuggestions: 3,
|
|
2914
|
-
resolveImportsRelativeTo: standard.resolveImportsRelativeTo
|
|
2915
|
-
}, settings)).issues.map((issue) => mapStandardIssueToDiagnostic(issue, standard.languages));
|
|
2899
|
+
}, spellCheckOptions, settings)).issues.map((issue) => mapStandardIssueToDiagnostic(issue, standard.languages, maskedDocument));
|
|
2916
2900
|
}));
|
|
2917
2901
|
} catch (error) {
|
|
2918
2902
|
const imports = standard.imports.join(", ");
|
|
@@ -2933,8 +2917,8 @@ async function loadCspellLib() {
|
|
|
2933
2917
|
cspellLibPromise ??= import("cspell-lib");
|
|
2934
2918
|
return cspellLibPromise;
|
|
2935
2919
|
}
|
|
2936
|
-
function mapStandardIssueToDiagnostic(issue, languages) {
|
|
2937
|
-
const line = issue.line.
|
|
2920
|
+
function mapStandardIssueToDiagnostic(issue, languages, documentText) {
|
|
2921
|
+
const line = getLineNumberAtOffset(documentText, issue.line.offset);
|
|
2938
2922
|
const column = issue.offset - issue.line.offset + 1;
|
|
2939
2923
|
return {
|
|
2940
2924
|
column,
|
|
@@ -2948,6 +2932,11 @@ function mapStandardIssueToDiagnostic(issue, languages) {
|
|
|
2948
2932
|
suggestions: issue.suggestions?.slice(0, 3)
|
|
2949
2933
|
};
|
|
2950
2934
|
}
|
|
2935
|
+
function getLineNumberAtOffset(text, offset) {
|
|
2936
|
+
let line = 1;
|
|
2937
|
+
for (let index = 0; index < offset && index < text.length; index++) if (text.charCodeAt(index) === 10) line++;
|
|
2938
|
+
return line;
|
|
2939
|
+
}
|
|
2951
2940
|
function inferStandardIssueLanguage(word, languages) {
|
|
2952
2941
|
if (/[\p{Script=Hiragana}\p{Script=Katakana}]/u.test(word) && languages.includes("ja")) return "ja";
|
|
2953
2942
|
if (/[\p{Script=Han}]/u.test(word)) {
|
|
@@ -3684,21 +3673,35 @@ init_page_context();
|
|
|
3684
3673
|
function oxContent(options = {}) {
|
|
3685
3674
|
const resolvedOptions = resolveOptions(options);
|
|
3686
3675
|
let config;
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
const outDir = path.resolve(root, docsOptions.out);
|
|
3692
|
-
const extracted = await extractDocs(srcDirs, docsOptions);
|
|
3693
|
-
const generated = generateMarkdown(extracted, docsOptions);
|
|
3694
|
-
await writeDocs(generated, outDir, extracted, docsOptions);
|
|
3695
|
-
return Object.keys(generated).length;
|
|
3696
|
-
}
|
|
3697
|
-
const mainPlugin = {
|
|
3698
|
-
name: "ox-content",
|
|
3699
|
-
configResolved(resolvedConfig) {
|
|
3676
|
+
const getRoot = () => config?.root || process.cwd();
|
|
3677
|
+
const ssgDevCache = createDevServerCache();
|
|
3678
|
+
const plugins = [
|
|
3679
|
+
createMainPlugin(resolvedOptions, (resolvedConfig) => {
|
|
3700
3680
|
config = resolvedConfig;
|
|
3701
|
-
},
|
|
3681
|
+
}),
|
|
3682
|
+
createEnvironmentPlugin(resolvedOptions),
|
|
3683
|
+
createDocsPlugin(resolvedOptions, getRoot),
|
|
3684
|
+
createSsgPlugin(resolvedOptions, getRoot, ssgDevCache),
|
|
3685
|
+
createSearchPlugin(resolvedOptions, getRoot)
|
|
3686
|
+
];
|
|
3687
|
+
if (resolvedOptions.i18n) plugins.push(createI18nPlugin(resolvedOptions));
|
|
3688
|
+
if (resolvedOptions.ogViewer) plugins.push(createOgViewerPlugin(resolvedOptions));
|
|
3689
|
+
return plugins;
|
|
3690
|
+
}
|
|
3691
|
+
async function regenerateDocs(resolvedOptions, root) {
|
|
3692
|
+
const docsOptions = resolvedOptions.docs;
|
|
3693
|
+
if (!docsOptions || !docsOptions.enabled) return 0;
|
|
3694
|
+
const srcDirs = docsOptions.src.map((src) => path.resolve(root, src));
|
|
3695
|
+
const outDir = path.resolve(root, docsOptions.out);
|
|
3696
|
+
const extracted = await extractDocs(srcDirs, docsOptions);
|
|
3697
|
+
const generated = generateMarkdown(extracted, docsOptions);
|
|
3698
|
+
await writeDocs(generated, outDir, extracted, docsOptions);
|
|
3699
|
+
return Object.keys(generated).length;
|
|
3700
|
+
}
|
|
3701
|
+
function createMainPlugin(resolvedOptions, setConfig) {
|
|
3702
|
+
return {
|
|
3703
|
+
name: "ox-content",
|
|
3704
|
+
configResolved: setConfig,
|
|
3702
3705
|
configureServer(devServer) {
|
|
3703
3706
|
devServer.middlewares.use(async (req, res, next) => {
|
|
3704
3707
|
const url = req.url;
|
|
@@ -3723,31 +3726,33 @@ function oxContent(options = {}) {
|
|
|
3723
3726
|
};
|
|
3724
3727
|
},
|
|
3725
3728
|
async handleHotUpdate({ file, server }) {
|
|
3726
|
-
if (isMarkdownFilePath(file, resolvedOptions.extensions))
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
}
|
|
3729
|
+
if (!isMarkdownFilePath(file, resolvedOptions.extensions)) return;
|
|
3730
|
+
server.ws.send({
|
|
3731
|
+
type: "custom",
|
|
3732
|
+
event: "ox-content:update",
|
|
3733
|
+
data: { file }
|
|
3734
|
+
});
|
|
3735
|
+
const modules = server.moduleGraph.getModulesByFile(file);
|
|
3736
|
+
return modules ? Array.from(modules) : [];
|
|
3735
3737
|
}
|
|
3736
3738
|
};
|
|
3737
|
-
|
|
3739
|
+
}
|
|
3740
|
+
function createEnvironmentPlugin(resolvedOptions) {
|
|
3741
|
+
return {
|
|
3738
3742
|
name: "ox-content:environment",
|
|
3739
3743
|
config() {
|
|
3740
3744
|
return { environments: { markdown: createMarkdownEnvironment(resolvedOptions) } };
|
|
3741
3745
|
}
|
|
3742
3746
|
};
|
|
3743
|
-
|
|
3747
|
+
}
|
|
3748
|
+
function createDocsPlugin(resolvedOptions, getRoot) {
|
|
3749
|
+
return {
|
|
3744
3750
|
name: "ox-content:docs",
|
|
3745
3751
|
async buildStart() {
|
|
3746
3752
|
const docsOptions = resolvedOptions.docs;
|
|
3747
3753
|
if (!docsOptions || !docsOptions.enabled) return;
|
|
3748
|
-
const root = config?.root || process.cwd();
|
|
3749
3754
|
try {
|
|
3750
|
-
const count = await regenerateDocs(
|
|
3755
|
+
const count = await regenerateDocs(resolvedOptions, getRoot());
|
|
3751
3756
|
console.log(`[ox-content] Generated ${count} documentation files to ${docsOptions.out}`);
|
|
3752
3757
|
} catch (err) {
|
|
3753
3758
|
console.warn("[ox-content] Failed to generate documentation:", err);
|
|
@@ -3756,50 +3761,32 @@ function oxContent(options = {}) {
|
|
|
3756
3761
|
configureServer(devServer) {
|
|
3757
3762
|
const docsOptions = resolvedOptions.docs;
|
|
3758
3763
|
if (!docsOptions || !docsOptions.enabled) return;
|
|
3759
|
-
const root =
|
|
3764
|
+
const root = getRoot();
|
|
3760
3765
|
const srcDirs = docsOptions.src.map((src) => path.resolve(root, src));
|
|
3761
3766
|
for (const srcDir of srcDirs) devServer.watcher.add(srcDir);
|
|
3762
3767
|
devServer.watcher.on("all", async (event, file) => {
|
|
3763
3768
|
if (event !== "add" && event !== "change" && event !== "unlink") return;
|
|
3764
|
-
if (srcDirs.some((srcDir) => file.startsWith(srcDir) && (file.endsWith(".ts") || file.endsWith(".tsx"))))
|
|
3765
|
-
|
|
3769
|
+
if (!srcDirs.some((srcDir) => file.startsWith(srcDir) && (file.endsWith(".ts") || file.endsWith(".tsx")))) return;
|
|
3770
|
+
try {
|
|
3771
|
+
await regenerateDocs(resolvedOptions, root);
|
|
3766
3772
|
} catch {}
|
|
3767
3773
|
});
|
|
3768
3774
|
}
|
|
3769
3775
|
};
|
|
3770
|
-
|
|
3771
|
-
|
|
3776
|
+
}
|
|
3777
|
+
function createSsgPlugin(resolvedOptions, getRoot, ssgDevCache) {
|
|
3778
|
+
return {
|
|
3772
3779
|
name: "ox-content:ssg",
|
|
3773
3780
|
configureServer(devServer) {
|
|
3774
3781
|
if (!resolvedOptions.ssg.enabled) return;
|
|
3775
|
-
const root =
|
|
3782
|
+
const root = getRoot();
|
|
3776
3783
|
const srcDir = path.resolve(root, resolvedOptions.srcDir);
|
|
3777
3784
|
devServer.middlewares.use(createDevServerMiddleware(resolvedOptions, root, ssgDevCache));
|
|
3778
3785
|
devServer.watcher.on("add", (file) => {
|
|
3779
|
-
|
|
3780
|
-
invalidateNavCache(ssgDevCache);
|
|
3781
|
-
devServer.ws.send({
|
|
3782
|
-
type: "custom",
|
|
3783
|
-
event: "ox-content:update",
|
|
3784
|
-
data: {
|
|
3785
|
-
file,
|
|
3786
|
-
type: "add"
|
|
3787
|
-
}
|
|
3788
|
-
});
|
|
3789
|
-
}
|
|
3786
|
+
notifySsgFileAddedOrRemoved(devServer, resolvedOptions, ssgDevCache, srcDir, file, "add");
|
|
3790
3787
|
});
|
|
3791
3788
|
devServer.watcher.on("unlink", (file) => {
|
|
3792
|
-
|
|
3793
|
-
invalidateNavCache(ssgDevCache);
|
|
3794
|
-
devServer.ws.send({
|
|
3795
|
-
type: "custom",
|
|
3796
|
-
event: "ox-content:update",
|
|
3797
|
-
data: {
|
|
3798
|
-
file,
|
|
3799
|
-
type: "unlink"
|
|
3800
|
-
}
|
|
3801
|
-
});
|
|
3802
|
-
}
|
|
3789
|
+
notifySsgFileAddedOrRemoved(devServer, resolvedOptions, ssgDevCache, srcDir, file, "unlink");
|
|
3803
3790
|
});
|
|
3804
3791
|
devServer.watcher.on("change", (file) => {
|
|
3805
3792
|
if (file.startsWith(srcDir) && isMarkdownFilePath(file, resolvedOptions.extensions)) invalidatePageCache(ssgDevCache, file);
|
|
@@ -3807,63 +3794,63 @@ function oxContent(options = {}) {
|
|
|
3807
3794
|
},
|
|
3808
3795
|
async closeBundle() {
|
|
3809
3796
|
if (!resolvedOptions.ssg.enabled) return;
|
|
3810
|
-
const root = config?.root || process.cwd();
|
|
3811
3797
|
try {
|
|
3812
|
-
const result = await buildSsg(resolvedOptions,
|
|
3798
|
+
const result = await buildSsg(resolvedOptions, getRoot());
|
|
3813
3799
|
if (result.files.length > 0) console.log(`[ox-content] Generated ${result.files.length} output files`);
|
|
3814
|
-
|
|
3800
|
+
for (const error of result.errors) console.warn(`[ox-content] ${error}`);
|
|
3815
3801
|
} catch (err) {
|
|
3816
3802
|
console.error("[ox-content] SSG build failed:", err);
|
|
3817
3803
|
}
|
|
3818
3804
|
}
|
|
3819
3805
|
};
|
|
3806
|
+
}
|
|
3807
|
+
function notifySsgFileAddedOrRemoved(devServer, resolvedOptions, ssgDevCache, srcDir, file, type) {
|
|
3808
|
+
if (!file.startsWith(srcDir) || !isMarkdownFilePath(file, resolvedOptions.extensions)) return;
|
|
3809
|
+
invalidateNavCache(ssgDevCache);
|
|
3810
|
+
devServer.ws.send({
|
|
3811
|
+
type: "custom",
|
|
3812
|
+
event: "ox-content:update",
|
|
3813
|
+
data: {
|
|
3814
|
+
file,
|
|
3815
|
+
type
|
|
3816
|
+
}
|
|
3817
|
+
});
|
|
3818
|
+
}
|
|
3819
|
+
function createSearchPlugin(resolvedOptions, getRoot) {
|
|
3820
3820
|
let searchIndexJson = "";
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
}
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
async closeBundle() {
|
|
3852
|
-
if (!resolvedOptions.search.enabled || !searchIndexJson) return;
|
|
3853
|
-
const root = config?.root || process.cwd();
|
|
3854
|
-
const outDir = path.resolve(root, resolvedOptions.outDir);
|
|
3855
|
-
try {
|
|
3856
|
-
await writeSearchIndex(searchIndexJson, outDir);
|
|
3857
|
-
console.log("[ox-content] Search index written to", path.join(outDir, "search-index.json"));
|
|
3858
|
-
} catch (err) {
|
|
3859
|
-
console.warn("[ox-content] Failed to write search index:", err);
|
|
3860
|
-
}
|
|
3821
|
+
return {
|
|
3822
|
+
name: "ox-content:search",
|
|
3823
|
+
resolveId(id) {
|
|
3824
|
+
if (id === "virtual:ox-content/search") return "\0virtual:ox-content/search";
|
|
3825
|
+
return null;
|
|
3826
|
+
},
|
|
3827
|
+
async load(id) {
|
|
3828
|
+
if (id !== "\0virtual:ox-content/search") return null;
|
|
3829
|
+
const searchOptions = resolvedOptions.search;
|
|
3830
|
+
if (!searchOptions.enabled) return "export const search = () => []; export const searchOptions = { enabled: false }; export default { search, searchOptions };";
|
|
3831
|
+
return generateSearchModule(searchOptions, resolvedOptions.base + "search-index.json");
|
|
3832
|
+
},
|
|
3833
|
+
async buildStart() {
|
|
3834
|
+
if (!resolvedOptions.search.enabled) return;
|
|
3835
|
+
const srcDir = path.resolve(getRoot(), resolvedOptions.srcDir);
|
|
3836
|
+
try {
|
|
3837
|
+
searchIndexJson = await buildSearchIndex(srcDir, resolvedOptions.base, resolvedOptions.extensions);
|
|
3838
|
+
console.log("[ox-content] Search index built");
|
|
3839
|
+
} catch (err) {
|
|
3840
|
+
console.warn("[ox-content] Failed to build search index:", err);
|
|
3841
|
+
}
|
|
3842
|
+
},
|
|
3843
|
+
async closeBundle() {
|
|
3844
|
+
if (!resolvedOptions.search.enabled || !searchIndexJson) return;
|
|
3845
|
+
const outDir = path.resolve(getRoot(), resolvedOptions.outDir);
|
|
3846
|
+
try {
|
|
3847
|
+
await writeSearchIndex(searchIndexJson, outDir);
|
|
3848
|
+
console.log("[ox-content] Search index written to", path.join(outDir, "search-index.json"));
|
|
3849
|
+
} catch (err) {
|
|
3850
|
+
console.warn("[ox-content] Failed to write search index:", err);
|
|
3861
3851
|
}
|
|
3862
3852
|
}
|
|
3863
|
-
|
|
3864
|
-
if (resolvedOptions.i18n) plugins.push(createI18nPlugin(resolvedOptions));
|
|
3865
|
-
if (resolvedOptions.ogViewer) plugins.push(createOgViewerPlugin(resolvedOptions));
|
|
3866
|
-
return plugins;
|
|
3853
|
+
};
|
|
3867
3854
|
}
|
|
3868
3855
|
/**
|
|
3869
3856
|
* Resolves plugin options with defaults.
|