@ox-content/vite-plugin 2.16.0 → 2.26.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 +371 -367
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +29 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +371 -367
- 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/tabs.cjs +14 -154
- package/dist/tabs.cjs.map +1 -1
- package/dist/tabs2.mjs +14 -152
- package/dist/tabs2.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/dist/youtube.cjs +10 -89
- package/dist/youtube.cjs.map +1 -1
- package/dist/youtube2.mjs +10 -87
- package/dist/youtube2.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);
|
|
@@ -373,9 +374,11 @@ function protectMermaidSvgs(html) {
|
|
|
373
374
|
* Restore protected mermaid SVG blocks from placeholders.
|
|
374
375
|
*/
|
|
375
376
|
function restoreMermaidSvgs(html, svgs) {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
377
|
+
if (svgs.size === 0) return html;
|
|
378
|
+
return html.replace(/<!--ox-mermaid-\d+-->/g, (placeholder) => {
|
|
379
|
+
const content = svgs.get(placeholder);
|
|
380
|
+
return content !== void 0 ? content : placeholder;
|
|
381
|
+
});
|
|
379
382
|
}
|
|
380
383
|
//#endregion
|
|
381
384
|
//#region src/transform.ts
|
|
@@ -432,7 +435,7 @@ async function loadNapiBindings() {
|
|
|
432
435
|
if (napiLoadAttempted) return napiBindings ?? null;
|
|
433
436
|
napiLoadAttempted = true;
|
|
434
437
|
try {
|
|
435
|
-
const mod = await
|
|
438
|
+
const mod = await require_napi.importNapiModule();
|
|
436
439
|
napiBindings = mod;
|
|
437
440
|
return mod;
|
|
438
441
|
} catch (error) {
|
|
@@ -545,67 +548,7 @@ if (import.meta.hot) {
|
|
|
545
548
|
`;
|
|
546
549
|
}
|
|
547
550
|
//#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
551
|
//#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
552
|
const DEFAULT_DOCS_INCLUDE = [
|
|
610
553
|
"**/*.ts",
|
|
611
554
|
"**/*.tsx",
|
|
@@ -682,7 +625,7 @@ const DEFAULT_DOCS_INCLUDE = [
|
|
|
682
625
|
* ```
|
|
683
626
|
*/
|
|
684
627
|
async function extractDocs(srcDirs, options) {
|
|
685
|
-
const napi = await
|
|
628
|
+
const napi = await require_napi.importNapiModule();
|
|
686
629
|
if (options.entryPoints?.length) {
|
|
687
630
|
const extractDocsFromEntryPoints = napi.extractDocsFromEntryPoints;
|
|
688
631
|
if (!extractDocsFromEntryPoints) throw new Error("[ox-content] extractDocsFromEntryPoints is not available from @ox-content/napi.");
|
|
@@ -695,65 +638,40 @@ async function extractDocs(srcDirs, options) {
|
|
|
695
638
|
entries: doc.entries
|
|
696
639
|
}));
|
|
697
640
|
}
|
|
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;
|
|
641
|
+
const extractDocsFromDirectories = napi.extractDocsFromDirectories;
|
|
642
|
+
if (!extractDocsFromDirectories) throw new Error("[ox-content] extractDocsFromDirectories is not available from @ox-content/napi.");
|
|
643
|
+
return extractDocsFromDirectories(srcDirs, options.include, options.exclude, options.private, options.internal).map((doc) => ({
|
|
644
|
+
file: doc.file,
|
|
645
|
+
entries: doc.entries
|
|
646
|
+
}));
|
|
712
647
|
}
|
|
713
648
|
/**
|
|
714
649
|
* Generates Markdown documentation from extracted docs.
|
|
715
650
|
*/
|
|
716
651
|
function generateMarkdown(docs, options) {
|
|
717
|
-
const napi =
|
|
652
|
+
const napi = require_napi.importNapiModuleSync();
|
|
718
653
|
if (typeof napi.generateDocsMarkdown !== "function") throw new Error("[ox-content] generateDocsMarkdown is not available from @ox-content/napi. Please rebuild the NAPI package.");
|
|
719
654
|
return napi.generateDocsMarkdown(toRustDocsModules(docs), {
|
|
720
655
|
groupBy: options.groupBy,
|
|
721
|
-
githubUrl: options.githubUrl
|
|
656
|
+
githubUrl: options.githubUrl,
|
|
657
|
+
linkStyle: options.linkStyle,
|
|
658
|
+
basePath: options.basePath,
|
|
659
|
+
pathStrategy: options.pathStrategy
|
|
722
660
|
});
|
|
723
661
|
}
|
|
724
662
|
/**
|
|
725
663
|
* Writes generated documentation to the output directory.
|
|
726
664
|
*/
|
|
727
665
|
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");
|
|
666
|
+
const napi = require_napi.importNapiModuleSync();
|
|
667
|
+
if (typeof napi.writeGeneratedDocs !== "function") throw new Error("[ox-content] writeGeneratedDocs is not available from @ox-content/napi. Please rebuild the NAPI package.");
|
|
668
|
+
napi.writeGeneratedDocs(docs, outDir, extractedDocs ? toRustDocsModules(extractedDocs) : void 0, {
|
|
669
|
+
generateNav: options?.generateNav ?? false,
|
|
670
|
+
groupBy: options?.groupBy ?? "file",
|
|
671
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
672
|
+
basePath: options?.basePath,
|
|
673
|
+
pathStrategy: options?.pathStrategy
|
|
674
|
+
});
|
|
757
675
|
}
|
|
758
676
|
function toRustDocsModules(docs) {
|
|
759
677
|
return docs.map((doc) => ({
|
|
@@ -778,9 +696,6 @@ function toRustDocsModules(docs) {
|
|
|
778
696
|
}))
|
|
779
697
|
}));
|
|
780
698
|
}
|
|
781
|
-
/**
|
|
782
|
-
* Resolves docs options with defaults.
|
|
783
|
-
*/
|
|
784
699
|
function resolveDocsOptions(options) {
|
|
785
700
|
if (options === false) return false;
|
|
786
701
|
const opts = options || {};
|
|
@@ -801,6 +716,9 @@ function resolveDocsOptions(options) {
|
|
|
801
716
|
toc: false,
|
|
802
717
|
groupBy: opts.groupBy ?? "file",
|
|
803
718
|
githubUrl: opts.githubUrl,
|
|
719
|
+
linkStyle: opts.linkStyle ?? "markdown",
|
|
720
|
+
basePath: opts.basePath,
|
|
721
|
+
pathStrategy: opts.pathStrategy ?? "flat",
|
|
804
722
|
generateNav: opts.generateNav ?? true
|
|
805
723
|
};
|
|
806
724
|
}
|
|
@@ -888,6 +806,8 @@ async function renderHtmlToPng(page, html, width, height, publicDir) {
|
|
|
888
806
|
}
|
|
889
807
|
//#endregion
|
|
890
808
|
//#region src/og-image/browser.ts
|
|
809
|
+
const PLAYWRIGHT_BROWSER_INSTALL_HINT = "Install Playwright browsers with `npx playwright install chromium` to enable OG image generation.";
|
|
810
|
+
let chromiumUnavailableWarned = false;
|
|
891
811
|
/**
|
|
892
812
|
* Opens a Chromium browser and returns a session for rendering OG images.
|
|
893
813
|
* Returns null if Playwright/Chromium is not available.
|
|
@@ -927,10 +847,20 @@ async function openBrowser() {
|
|
|
927
847
|
}
|
|
928
848
|
};
|
|
929
849
|
} catch (err) {
|
|
930
|
-
|
|
850
|
+
warnChromiumUnavailableOnce(err);
|
|
931
851
|
return null;
|
|
932
852
|
}
|
|
933
853
|
}
|
|
854
|
+
function warnChromiumUnavailableOnce(err) {
|
|
855
|
+
if (chromiumUnavailableWarned) return;
|
|
856
|
+
chromiumUnavailableWarned = true;
|
|
857
|
+
console.warn(`[ox-content:og-image] Chromium not available, skipping OG image generation. ${formatChromiumUnavailableDetail(err)}`);
|
|
858
|
+
}
|
|
859
|
+
function formatChromiumUnavailableDetail(err) {
|
|
860
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
861
|
+
if (message.includes("Executable doesn't exist") || message.includes("Please run the following command to download new browsers")) return PLAYWRIGHT_BROWSER_INSTALL_HINT;
|
|
862
|
+
return message.split(/\r?\n/).find((line) => line.trim())?.trim() ?? "Unknown launch error.";
|
|
863
|
+
}
|
|
934
864
|
//#endregion
|
|
935
865
|
//#region src/og-image/template.ts
|
|
936
866
|
/**
|
|
@@ -1732,38 +1662,53 @@ function resolveSsgOptions(ssg) {
|
|
|
1732
1662
|
* Extracts title from content or frontmatter.
|
|
1733
1663
|
*/
|
|
1734
1664
|
function extractTitle$1(content, frontmatter) {
|
|
1735
|
-
return
|
|
1665
|
+
return require_napi.importNapiModuleSync().extractSsgTitle(content, typeof frontmatter.title === "string" ? frontmatter.title : void 0);
|
|
1736
1666
|
}
|
|
1737
1667
|
/**
|
|
1738
1668
|
* Generates bare HTML page (no navigation, no styles).
|
|
1739
1669
|
*/
|
|
1740
1670
|
function generateBareHtmlPage(content, title) {
|
|
1741
|
-
return
|
|
1671
|
+
return require_napi.importNapiModuleSync().generateSsgBareHtml(content, title);
|
|
1742
1672
|
}
|
|
1743
1673
|
/**
|
|
1744
|
-
*
|
|
1674
|
+
* Per-build cache for the Rust-facing nav conversion. `navGroups` is the same
|
|
1675
|
+
* `context.navItems` reference for every page in a build, so the deep recursive
|
|
1676
|
+
* copy below only needs to run once per build instead of once per page.
|
|
1745
1677
|
*/
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
depth: entry.depth,
|
|
1750
|
-
text: entry.text,
|
|
1751
|
-
slug: entry.slug,
|
|
1752
|
-
children: entry.children?.map(toRustTocEntry) ?? []
|
|
1753
|
-
});
|
|
1754
|
-
const tocForRust = pageData.toc.map(toRustTocEntry);
|
|
1755
|
-
const toRustNavItem = (item) => ({
|
|
1678
|
+
const navGroupsForRustCache = /* @__PURE__ */ new WeakMap();
|
|
1679
|
+
function toRustNavItem(item) {
|
|
1680
|
+
return {
|
|
1756
1681
|
title: item.title,
|
|
1757
1682
|
path: item.path,
|
|
1758
1683
|
href: item.href,
|
|
1759
1684
|
children: item.children?.map(toRustNavItem),
|
|
1760
1685
|
collapsed: item.collapsed
|
|
1761
|
-
}
|
|
1762
|
-
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
function convertNavGroupsForRust(navGroups) {
|
|
1689
|
+
const cached = navGroupsForRustCache.get(navGroups);
|
|
1690
|
+
if (cached) return cached;
|
|
1691
|
+
const converted = navGroups.map((group) => ({
|
|
1763
1692
|
title: group.title,
|
|
1764
1693
|
collapsed: group.collapsed,
|
|
1765
1694
|
items: group.items.map(toRustNavItem)
|
|
1766
1695
|
}));
|
|
1696
|
+
navGroupsForRustCache.set(navGroups, converted);
|
|
1697
|
+
return converted;
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Generates HTML page with navigation using Rust NAPI bindings.
|
|
1701
|
+
*/
|
|
1702
|
+
async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, theme, locale, availableLocales) {
|
|
1703
|
+
const mod = await require_napi.importNapiModule();
|
|
1704
|
+
const toRustTocEntry = (entry) => ({
|
|
1705
|
+
depth: entry.depth,
|
|
1706
|
+
text: entry.text,
|
|
1707
|
+
slug: entry.slug,
|
|
1708
|
+
children: entry.children?.map(toRustTocEntry) ?? []
|
|
1709
|
+
});
|
|
1710
|
+
const tocForRust = pageData.toc.map(toRustTocEntry);
|
|
1711
|
+
const navGroupsForRust = convertNavGroupsForRust(navGroups);
|
|
1767
1712
|
const themeForRust = theme ? require_vitepress.themeToNapi(theme) : void 0;
|
|
1768
1713
|
const entryPageForRust = pageData.entryPage ? {
|
|
1769
1714
|
hero: pageData.entryPage.hero ? {
|
|
@@ -1818,7 +1763,7 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
|
|
|
1818
1763
|
});
|
|
1819
1764
|
}
|
|
1820
1765
|
async function externalizeSharedPageAssets(pages, outDir, base) {
|
|
1821
|
-
const optimized = (await
|
|
1766
|
+
const optimized = (await require_napi.importNapiModule()).externalizeSsgAssets(pages, outDir, base);
|
|
1822
1767
|
await Promise.all(optimized.assets.map(async (asset) => {
|
|
1823
1768
|
await fs_promises.mkdir(path.dirname(asset.outputPath), { recursive: true });
|
|
1824
1769
|
await fs_promises.writeFile(asset.outputPath, asset.content, "utf-8");
|
|
@@ -1832,45 +1777,45 @@ async function externalizeSharedPageAssets(pages, outDir, base) {
|
|
|
1832
1777
|
* Converts a markdown file path to a relative URL path.
|
|
1833
1778
|
*/
|
|
1834
1779
|
function getUrlPath$1(inputPath, srcDir) {
|
|
1835
|
-
return
|
|
1780
|
+
return require_napi.importNapiModuleSync().getSsgUrlPath(inputPath, srcDir);
|
|
1836
1781
|
}
|
|
1837
1782
|
/**
|
|
1838
1783
|
* Resolves manual navigation config to the format used by the built-in SSG renderer.
|
|
1839
1784
|
*/
|
|
1840
1785
|
function resolveNavigationGroups(navigation, base, extension) {
|
|
1841
1786
|
if (!navigation) return;
|
|
1842
|
-
return
|
|
1787
|
+
return require_napi.importNapiModuleSync().resolveSsgNavigationGroups(navigation, base, extension);
|
|
1843
1788
|
}
|
|
1844
1789
|
function getPageLocale(urlPath, i18n) {
|
|
1845
1790
|
if (!i18n) return void 0;
|
|
1846
|
-
return
|
|
1791
|
+
return require_napi.importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, i18n.locales.map((locale) => locale.code)) ?? void 0;
|
|
1847
1792
|
}
|
|
1848
1793
|
function getRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl) {
|
|
1849
|
-
return
|
|
1794
|
+
return require_napi.importNapiModuleSync().resolveSsgRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl);
|
|
1850
1795
|
}
|
|
1851
1796
|
/**
|
|
1852
1797
|
* Formats a file/dir name as a title.
|
|
1853
1798
|
*/
|
|
1854
1799
|
function formatTitle(name) {
|
|
1855
|
-
return
|
|
1800
|
+
return require_napi.importNapiModuleSync().formatSsgTitle(name);
|
|
1856
1801
|
}
|
|
1857
1802
|
/**
|
|
1858
1803
|
* Collects all markdown files from the source directory.
|
|
1859
1804
|
*/
|
|
1860
1805
|
async function collectMarkdownFiles(srcDir, extensions = DEFAULT_MARKDOWN_EXTENSIONS) {
|
|
1861
|
-
return
|
|
1806
|
+
return require_napi.importNapiModuleSync().collectSsgMarkdownFiles(srcDir, [...extensions]);
|
|
1862
1807
|
}
|
|
1863
1808
|
/**
|
|
1864
1809
|
* Builds navigation items from markdown files, grouped by directory.
|
|
1865
1810
|
*/
|
|
1866
1811
|
function buildNavItems(markdownFiles, srcDir, base, extension) {
|
|
1867
|
-
return
|
|
1812
|
+
return require_napi.importNapiModuleSync().buildSsgNavItems(markdownFiles, srcDir, base, extension);
|
|
1868
1813
|
}
|
|
1869
1814
|
/**
|
|
1870
1815
|
* Builds navigation items from an explicit theme sidebar tree.
|
|
1871
1816
|
*/
|
|
1872
1817
|
function buildThemeNavItems(sidebar, base, extension) {
|
|
1873
|
-
return
|
|
1818
|
+
return require_napi.importNapiModuleSync().buildSsgThemeNavItems(sidebar, base, extension);
|
|
1874
1819
|
}
|
|
1875
1820
|
/**
|
|
1876
1821
|
* Builds all markdown files to static HTML.
|
|
@@ -1883,149 +1828,201 @@ async function buildSsg(options, root) {
|
|
|
1883
1828
|
};
|
|
1884
1829
|
const srcDir = path.resolve(root, options.srcDir);
|
|
1885
1830
|
const outDir = path.resolve(root, options.outDir);
|
|
1886
|
-
const base = options.base.endsWith("/") ? options.base : options.base + "/";
|
|
1887
1831
|
const generatedFiles = [];
|
|
1888
|
-
const generatedPages = [];
|
|
1889
1832
|
const errors = [];
|
|
1890
|
-
|
|
1833
|
+
await cleanOutputDirectory(ssgOptions, outDir);
|
|
1834
|
+
const markdownFiles = await collectMarkdownFiles(srcDir, options.extensions);
|
|
1835
|
+
const context = await createBuildSsgContext(options, root, srcDir, outDir, markdownFiles);
|
|
1836
|
+
const collected = await collectPageResults(context, markdownFiles);
|
|
1837
|
+
errors.push(...collected.errors);
|
|
1838
|
+
await generateOgImageAssets(context, collected, generatedFiles, errors);
|
|
1839
|
+
await writeGeneratedPages(await generateHtmlPages(context, collected.pageResults, collected, errors), context, generatedFiles);
|
|
1840
|
+
return {
|
|
1841
|
+
files: generatedFiles,
|
|
1842
|
+
errors
|
|
1843
|
+
};
|
|
1844
|
+
}
|
|
1845
|
+
async function cleanOutputDirectory(ssgOptions, outDir) {
|
|
1846
|
+
if (!ssgOptions.clean) return;
|
|
1847
|
+
try {
|
|
1891
1848
|
await fs_promises.rm(outDir, {
|
|
1892
1849
|
recursive: true,
|
|
1893
1850
|
force: true
|
|
1894
1851
|
});
|
|
1895
1852
|
} catch {}
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1853
|
+
}
|
|
1854
|
+
async function createBuildSsgContext(options, root, srcDir, outDir, markdownFiles) {
|
|
1855
|
+
const ssgOptions = options.ssg;
|
|
1856
|
+
const base = options.base.endsWith("/") ? options.base : options.base + "/";
|
|
1857
|
+
return {
|
|
1858
|
+
options,
|
|
1859
|
+
ssgOptions,
|
|
1860
|
+
root,
|
|
1861
|
+
srcDir,
|
|
1862
|
+
outDir,
|
|
1863
|
+
base,
|
|
1864
|
+
navItems: resolveNavigationGroups(ssgOptions.navigation, base, ssgOptions.extension) ?? (ssgOptions.theme?.sidebar.length ? buildThemeNavItems(ssgOptions.theme.sidebar, base, ssgOptions.extension) : buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension)),
|
|
1865
|
+
siteName: await resolveSiteName$1(root, ssgOptions),
|
|
1866
|
+
shouldGenerateOgImages: (options.ogImage || ssgOptions.generateOgImage) && !ssgOptions.bare,
|
|
1867
|
+
napi: ssgOptions.lastUpdated ? await require_napi.importNapiModule() : void 0
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
async function resolveSiteName$1(root, ssgOptions) {
|
|
1871
|
+
if (ssgOptions.siteName) return ssgOptions.siteName;
|
|
1872
|
+
try {
|
|
1900
1873
|
const pkgPath = path.join(root, "package.json");
|
|
1901
1874
|
const pkg = JSON.parse(await fs_promises.readFile(pkgPath, "utf-8"));
|
|
1902
|
-
|
|
1903
|
-
} catch {
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
const
|
|
1909
|
-
|
|
1875
|
+
return pkg.name ? formatTitle(pkg.name) : "Documentation";
|
|
1876
|
+
} catch {
|
|
1877
|
+
return "Documentation";
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
async function collectPageResults(context, markdownFiles) {
|
|
1881
|
+
const collected = {
|
|
1882
|
+
pageResults: [],
|
|
1883
|
+
ogImageEntries: [],
|
|
1884
|
+
ogImageInputPaths: [],
|
|
1885
|
+
ogImageUrlMap: /* @__PURE__ */ new Map(),
|
|
1886
|
+
errors: []
|
|
1887
|
+
};
|
|
1910
1888
|
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
|
-
}
|
|
1889
|
+
const pageResult = await transformSsgPage(context, inputPath);
|
|
1890
|
+
collected.pageResults.push(pageResult);
|
|
1891
|
+
collectOgImageEntry(context, pageResult, collected);
|
|
1958
1892
|
} catch (err) {
|
|
1959
1893
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1960
|
-
errors.push(`Failed to process ${inputPath}: ${errorMessage}`);
|
|
1894
|
+
collected.errors.push(`Failed to process ${inputPath}: ${errorMessage}`);
|
|
1961
1895
|
}
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1896
|
+
return collected;
|
|
1897
|
+
}
|
|
1898
|
+
async function transformSsgPage(context, inputPath) {
|
|
1899
|
+
const result = await transformMarkdown(await fs_promises.readFile(inputPath, "utf-8"), inputPath, context.options, {
|
|
1900
|
+
convertMdLinks: true,
|
|
1901
|
+
baseUrl: context.base,
|
|
1902
|
+
sourcePath: inputPath
|
|
1903
|
+
});
|
|
1904
|
+
const frontmatter = require_vitepress.normalizeVitePressFrontmatter(result.frontmatter);
|
|
1905
|
+
const transformedHtml = await transformSsgHtml(result.html, context.options);
|
|
1906
|
+
const title = extractTitle$1(transformedHtml, frontmatter);
|
|
1907
|
+
return {
|
|
1908
|
+
inputPath,
|
|
1909
|
+
routePaths: getRoutePaths(inputPath, context.srcDir, context.outDir, context.base, context.ssgOptions.extension, context.ssgOptions.siteUrl),
|
|
1910
|
+
transformedHtml,
|
|
1911
|
+
title,
|
|
1912
|
+
description: frontmatter.description,
|
|
1913
|
+
lastUpdated: context.napi?.getGitLastUpdated(inputPath, context.root) ?? void 0,
|
|
1914
|
+
frontmatter,
|
|
1915
|
+
toc: result.toc
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
async function transformSsgHtml(html, options) {
|
|
1919
|
+
const { html: protectedHtml, svgs: mermaidSvgs } = protectMermaidSvgs(html);
|
|
1920
|
+
let transformedHtml = await transformAllPlugins(protectedHtml, {
|
|
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
|
+
if (hasIslands(transformedHtml)) transformedHtml = (await transformIslands(transformedHtml)).html;
|
|
1929
|
+
return restoreMermaidSvgs(transformedHtml, mermaidSvgs);
|
|
1930
|
+
}
|
|
1931
|
+
function collectOgImageEntry(context, pageResult, collected) {
|
|
1932
|
+
if (!context.shouldGenerateOgImages) return;
|
|
1933
|
+
const { layout: _layout, ...frontmatterRest } = pageResult.frontmatter;
|
|
1934
|
+
collected.ogImageEntries.push({
|
|
1935
|
+
props: {
|
|
1936
|
+
...frontmatterRest,
|
|
1937
|
+
title: pageResult.title,
|
|
1938
|
+
description: pageResult.description,
|
|
1939
|
+
siteName: context.siteName
|
|
1940
|
+
},
|
|
1941
|
+
outputPath: pageResult.routePaths.ogImagePath
|
|
1942
|
+
});
|
|
1943
|
+
collected.ogImageInputPaths.push(pageResult.inputPath);
|
|
1944
|
+
collected.ogImageUrlMap.set(pageResult.inputPath, pageResult.routePaths.ogImageUrl);
|
|
1945
|
+
}
|
|
1946
|
+
async function generateOgImageAssets(context, collected, generatedFiles, errors) {
|
|
1947
|
+
if (!context.shouldGenerateOgImages || collected.ogImageEntries.length === 0) return;
|
|
1948
|
+
try {
|
|
1949
|
+
const ogResults = await generateOgImages(collected.ogImageEntries, context.options.ogImageOptions, context.root);
|
|
1950
|
+
if (clearMissingBrowserOgImages(ogResults, collected)) return;
|
|
1951
|
+
reportOgImageResults(ogResults, collected, generatedFiles, errors);
|
|
1979
1952
|
} catch (err) {
|
|
1980
1953
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1981
1954
|
console.warn(`[ox-content:og-image] Batch generation failed: ${errorMessage}`);
|
|
1982
|
-
ogImageUrlMap.clear();
|
|
1955
|
+
collected.ogImageUrlMap.clear();
|
|
1983
1956
|
}
|
|
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);
|
|
1957
|
+
}
|
|
1958
|
+
function clearMissingBrowserOgImages(ogResults, collected) {
|
|
1959
|
+
if (!(ogResults.length > 0 && ogResults.every((result) => result.error === "Chromium not available"))) return false;
|
|
1960
|
+
for (const inputPath of collected.ogImageInputPaths) collected.ogImageUrlMap.delete(inputPath);
|
|
1961
|
+
return true;
|
|
1962
|
+
}
|
|
1963
|
+
function reportOgImageResults(ogResults, collected, generatedFiles, errors) {
|
|
1964
|
+
let ogSuccessCount = 0;
|
|
1965
|
+
for (let i = 0; i < ogResults.length; i++) {
|
|
1966
|
+
const result = ogResults[i];
|
|
1967
|
+
if (result.error) {
|
|
1968
|
+
errors.push(`OG image failed for ${result.outputPath}: ${result.error}`);
|
|
1969
|
+
collected.ogImageUrlMap.delete(collected.ogImageInputPaths[i]);
|
|
1970
|
+
} else {
|
|
1971
|
+
generatedFiles.push(result.outputPath);
|
|
1972
|
+
ogSuccessCount++;
|
|
2008
1973
|
}
|
|
1974
|
+
}
|
|
1975
|
+
if (ogSuccessCount > 0) {
|
|
1976
|
+
const cachedCount = ogResults.filter((result) => result.cached && !result.error).length;
|
|
1977
|
+
console.log(`[ox-content:og-image] Generated ${ogSuccessCount} OG images` + (cachedCount > 0 ? ` (${cachedCount} from cache)` : ""));
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
async function generateHtmlPages(context, pageResults, collected, errors) {
|
|
1981
|
+
const generatedPages = [];
|
|
1982
|
+
for (const pageResult of pageResults) try {
|
|
2009
1983
|
generatedPages.push({
|
|
2010
|
-
inputPath,
|
|
2011
|
-
outputPath: routePaths.outputPath,
|
|
2012
|
-
html
|
|
1984
|
+
inputPath: pageResult.inputPath,
|
|
1985
|
+
outputPath: pageResult.routePaths.outputPath,
|
|
1986
|
+
html: await renderSsgPage(context, pageResult, collected.ogImageUrlMap)
|
|
2013
1987
|
});
|
|
2014
1988
|
} catch (err) {
|
|
2015
1989
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
2016
1990
|
errors.push(`Failed to generate HTML for ${pageResult.inputPath}: ${errorMessage}`);
|
|
2017
1991
|
}
|
|
2018
|
-
|
|
1992
|
+
return generatedPages;
|
|
1993
|
+
}
|
|
1994
|
+
async function renderSsgPage(context, pageResult, ogImageUrlMap) {
|
|
1995
|
+
if (context.ssgOptions.bare) return generateBareHtmlPage(pageResult.transformedHtml, pageResult.title);
|
|
1996
|
+
const pageData = createSsgPageData(pageResult);
|
|
1997
|
+
const pageOgImage = context.shouldGenerateOgImages && ogImageUrlMap.has(pageResult.inputPath) ? ogImageUrlMap.get(pageResult.inputPath) : context.ssgOptions.ogImage;
|
|
1998
|
+
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);
|
|
1999
|
+
}
|
|
2000
|
+
function createSsgPageData(pageResult) {
|
|
2001
|
+
const { frontmatter } = pageResult;
|
|
2002
|
+
const entryPage = frontmatter.layout === "entry" ? {
|
|
2003
|
+
hero: frontmatter.hero,
|
|
2004
|
+
features: frontmatter.features
|
|
2005
|
+
} : void 0;
|
|
2006
|
+
return {
|
|
2007
|
+
title: pageResult.title,
|
|
2008
|
+
description: pageResult.description,
|
|
2009
|
+
content: pageResult.transformedHtml,
|
|
2010
|
+
toc: pageResult.toc,
|
|
2011
|
+
lastUpdated: pageResult.lastUpdated,
|
|
2012
|
+
frontmatter,
|
|
2013
|
+
path: pageResult.routePaths.urlPath,
|
|
2014
|
+
href: pageResult.routePaths.href,
|
|
2015
|
+
entryPage
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
async function writeGeneratedPages(generatedPages, context, generatedFiles) {
|
|
2019
|
+
const optimizedOutput = await externalizeSharedPageAssets(generatedPages, context.outDir, context.base);
|
|
2019
2020
|
generatedFiles.push(...optimizedOutput.assets);
|
|
2020
2021
|
for (const page of optimizedOutput.pages) {
|
|
2021
2022
|
await fs_promises.mkdir(path.dirname(page.outputPath), { recursive: true });
|
|
2022
2023
|
await fs_promises.writeFile(page.outputPath, page.html, "utf-8");
|
|
2023
2024
|
generatedFiles.push(page.outputPath);
|
|
2024
2025
|
}
|
|
2025
|
-
return {
|
|
2026
|
-
files: generatedFiles,
|
|
2027
|
-
errors
|
|
2028
|
-
};
|
|
2029
2026
|
}
|
|
2030
2027
|
//#endregion
|
|
2031
2028
|
//#region src/search.ts
|
|
@@ -2037,7 +2034,7 @@ async function buildSsg(options, root) {
|
|
|
2037
2034
|
let oxContent$1 = null;
|
|
2038
2035
|
async function getOxContent() {
|
|
2039
2036
|
if (!oxContent$1) try {
|
|
2040
|
-
oxContent$1 = await
|
|
2037
|
+
oxContent$1 = await require_napi.importNapiModule();
|
|
2041
2038
|
} catch {
|
|
2042
2039
|
console.warn("[ox-content] Native bindings not available, search disabled");
|
|
2043
2040
|
return null;
|
|
@@ -2091,7 +2088,7 @@ async function writeSearchIndex(indexJson, outDir) {
|
|
|
2091
2088
|
* This is injected into the bundle as a virtual module.
|
|
2092
2089
|
*/
|
|
2093
2090
|
function generateSearchModule(options, indexPath) {
|
|
2094
|
-
return
|
|
2091
|
+
return require_napi.importNapiModuleSync().generateSearchModuleFromOptions(options, indexPath);
|
|
2095
2092
|
}
|
|
2096
2093
|
//#endregion
|
|
2097
2094
|
//#region src/dev-server.ts
|
|
@@ -2705,7 +2702,7 @@ function createI18nPlugin(resolvedOptions) {
|
|
|
2705
2702
|
return;
|
|
2706
2703
|
}
|
|
2707
2704
|
try {
|
|
2708
|
-
const { checkI18nProject } = await
|
|
2705
|
+
const { checkI18nProject } = await require_napi.importNapiModule();
|
|
2709
2706
|
const checkResult = checkI18nProject(dictDir, [path.resolve(root, "src"), path.resolve(root, "content")], i18nOptions.functionNames, i18nOptions.defaultLocale);
|
|
2710
2707
|
if (checkResult.errorCount > 0 || checkResult.warningCount > 0) {
|
|
2711
2708
|
for (const diag of checkResult.diagnostics) if (diag.severity === "error") console.error(`[ox-content:i18n] ${diag.message}`);
|
|
@@ -2859,7 +2856,10 @@ function stripMaskedDocument(result) {
|
|
|
2859
2856
|
};
|
|
2860
2857
|
}
|
|
2861
2858
|
function normalizeLintOptions(options) {
|
|
2862
|
-
const
|
|
2859
|
+
const standardDictionary = options.dictionary?.standard && typeof options.dictionary.standard === "object" ? options.dictionary.standard : void 0;
|
|
2860
|
+
const optionLanguages = options.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language));
|
|
2861
|
+
const standardLanguages = standardDictionary?.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language));
|
|
2862
|
+
const languages = optionLanguages ?? standardLanguages ?? [...DEFAULT_LANGUAGES];
|
|
2863
2863
|
const standard = normalizeStandardDictionaryOptions(options.dictionary?.standard, languages);
|
|
2864
2864
|
return {
|
|
2865
2865
|
dictionary: {
|
|
@@ -2900,6 +2900,12 @@ async function runStandardSpellcheckDocuments(maskedDocuments, options) {
|
|
|
2900
2900
|
const { spellCheckDocument } = await loadCspellLib();
|
|
2901
2901
|
const locale = standard.languages.join(",");
|
|
2902
2902
|
const settings = createStandardSpellcheckSettings(options, locale);
|
|
2903
|
+
const spellCheckOptions = {
|
|
2904
|
+
generateSuggestions: true,
|
|
2905
|
+
noConfigSearch: true,
|
|
2906
|
+
numSuggestions: 3,
|
|
2907
|
+
resolveImportsRelativeTo: standard.resolveImportsRelativeTo
|
|
2908
|
+
};
|
|
2903
2909
|
return Promise.all(maskedDocuments.map(async (maskedDocument, index) => {
|
|
2904
2910
|
if (maskedDocument.trim().length === 0) return [];
|
|
2905
2911
|
return (await spellCheckDocument({
|
|
@@ -2907,12 +2913,7 @@ async function runStandardSpellcheckDocuments(maskedDocuments, options) {
|
|
|
2907
2913
|
locale,
|
|
2908
2914
|
text: maskedDocument,
|
|
2909
2915
|
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));
|
|
2916
|
+
}, spellCheckOptions, settings)).issues.map((issue) => mapStandardIssueToDiagnostic(issue, standard.languages, maskedDocument));
|
|
2916
2917
|
}));
|
|
2917
2918
|
} catch (error) {
|
|
2918
2919
|
const imports = standard.imports.join(", ");
|
|
@@ -2933,8 +2934,8 @@ async function loadCspellLib() {
|
|
|
2933
2934
|
cspellLibPromise ??= import("cspell-lib");
|
|
2934
2935
|
return cspellLibPromise;
|
|
2935
2936
|
}
|
|
2936
|
-
function mapStandardIssueToDiagnostic(issue, languages) {
|
|
2937
|
-
const line = issue.line.
|
|
2937
|
+
function mapStandardIssueToDiagnostic(issue, languages, documentText) {
|
|
2938
|
+
const line = getLineNumberAtOffset(documentText, issue.line.offset);
|
|
2938
2939
|
const column = issue.offset - issue.line.offset + 1;
|
|
2939
2940
|
return {
|
|
2940
2941
|
column,
|
|
@@ -2948,6 +2949,11 @@ function mapStandardIssueToDiagnostic(issue, languages) {
|
|
|
2948
2949
|
suggestions: issue.suggestions?.slice(0, 3)
|
|
2949
2950
|
};
|
|
2950
2951
|
}
|
|
2952
|
+
function getLineNumberAtOffset(text, offset) {
|
|
2953
|
+
let line = 1;
|
|
2954
|
+
for (let index = 0; index < offset && index < text.length; index++) if (text.charCodeAt(index) === 10) line++;
|
|
2955
|
+
return line;
|
|
2956
|
+
}
|
|
2951
2957
|
function inferStandardIssueLanguage(word, languages) {
|
|
2952
2958
|
if (/[\p{Script=Hiragana}\p{Script=Katakana}]/u.test(word) && languages.includes("ja")) return "ja";
|
|
2953
2959
|
if (/[\p{Script=Han}]/u.test(word)) {
|
|
@@ -3684,21 +3690,35 @@ init_page_context();
|
|
|
3684
3690
|
function oxContent(options = {}) {
|
|
3685
3691
|
const resolvedOptions = resolveOptions(options);
|
|
3686
3692
|
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) {
|
|
3693
|
+
const getRoot = () => config?.root || process.cwd();
|
|
3694
|
+
const ssgDevCache = createDevServerCache();
|
|
3695
|
+
const plugins = [
|
|
3696
|
+
createMainPlugin(resolvedOptions, (resolvedConfig) => {
|
|
3700
3697
|
config = resolvedConfig;
|
|
3701
|
-
},
|
|
3698
|
+
}),
|
|
3699
|
+
createEnvironmentPlugin(resolvedOptions),
|
|
3700
|
+
createDocsPlugin(resolvedOptions, getRoot),
|
|
3701
|
+
createSsgPlugin(resolvedOptions, getRoot, ssgDevCache),
|
|
3702
|
+
createSearchPlugin(resolvedOptions, getRoot)
|
|
3703
|
+
];
|
|
3704
|
+
if (resolvedOptions.i18n) plugins.push(createI18nPlugin(resolvedOptions));
|
|
3705
|
+
if (resolvedOptions.ogViewer) plugins.push(createOgViewerPlugin(resolvedOptions));
|
|
3706
|
+
return plugins;
|
|
3707
|
+
}
|
|
3708
|
+
async function regenerateDocs(resolvedOptions, root) {
|
|
3709
|
+
const docsOptions = resolvedOptions.docs;
|
|
3710
|
+
if (!docsOptions || !docsOptions.enabled) return 0;
|
|
3711
|
+
const srcDirs = docsOptions.src.map((src) => path.resolve(root, src));
|
|
3712
|
+
const outDir = path.resolve(root, docsOptions.out);
|
|
3713
|
+
const extracted = await extractDocs(srcDirs, docsOptions);
|
|
3714
|
+
const generated = generateMarkdown(extracted, docsOptions);
|
|
3715
|
+
await writeDocs(generated, outDir, extracted, docsOptions);
|
|
3716
|
+
return Object.keys(generated).length;
|
|
3717
|
+
}
|
|
3718
|
+
function createMainPlugin(resolvedOptions, setConfig) {
|
|
3719
|
+
return {
|
|
3720
|
+
name: "ox-content",
|
|
3721
|
+
configResolved: setConfig,
|
|
3702
3722
|
configureServer(devServer) {
|
|
3703
3723
|
devServer.middlewares.use(async (req, res, next) => {
|
|
3704
3724
|
const url = req.url;
|
|
@@ -3723,31 +3743,33 @@ function oxContent(options = {}) {
|
|
|
3723
3743
|
};
|
|
3724
3744
|
},
|
|
3725
3745
|
async handleHotUpdate({ file, server }) {
|
|
3726
|
-
if (isMarkdownFilePath(file, resolvedOptions.extensions))
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
}
|
|
3746
|
+
if (!isMarkdownFilePath(file, resolvedOptions.extensions)) return;
|
|
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) : [];
|
|
3735
3754
|
}
|
|
3736
3755
|
};
|
|
3737
|
-
|
|
3756
|
+
}
|
|
3757
|
+
function createEnvironmentPlugin(resolvedOptions) {
|
|
3758
|
+
return {
|
|
3738
3759
|
name: "ox-content:environment",
|
|
3739
3760
|
config() {
|
|
3740
3761
|
return { environments: { markdown: createMarkdownEnvironment(resolvedOptions) } };
|
|
3741
3762
|
}
|
|
3742
3763
|
};
|
|
3743
|
-
|
|
3764
|
+
}
|
|
3765
|
+
function createDocsPlugin(resolvedOptions, getRoot) {
|
|
3766
|
+
return {
|
|
3744
3767
|
name: "ox-content:docs",
|
|
3745
3768
|
async buildStart() {
|
|
3746
3769
|
const docsOptions = resolvedOptions.docs;
|
|
3747
3770
|
if (!docsOptions || !docsOptions.enabled) return;
|
|
3748
|
-
const root = config?.root || process.cwd();
|
|
3749
3771
|
try {
|
|
3750
|
-
const count = await regenerateDocs(
|
|
3772
|
+
const count = await regenerateDocs(resolvedOptions, getRoot());
|
|
3751
3773
|
console.log(`[ox-content] Generated ${count} documentation files to ${docsOptions.out}`);
|
|
3752
3774
|
} catch (err) {
|
|
3753
3775
|
console.warn("[ox-content] Failed to generate documentation:", err);
|
|
@@ -3756,50 +3778,32 @@ function oxContent(options = {}) {
|
|
|
3756
3778
|
configureServer(devServer) {
|
|
3757
3779
|
const docsOptions = resolvedOptions.docs;
|
|
3758
3780
|
if (!docsOptions || !docsOptions.enabled) return;
|
|
3759
|
-
const root =
|
|
3781
|
+
const root = getRoot();
|
|
3760
3782
|
const srcDirs = docsOptions.src.map((src) => path.resolve(root, src));
|
|
3761
3783
|
for (const srcDir of srcDirs) devServer.watcher.add(srcDir);
|
|
3762
3784
|
devServer.watcher.on("all", async (event, file) => {
|
|
3763
3785
|
if (event !== "add" && event !== "change" && event !== "unlink") return;
|
|
3764
|
-
if (srcDirs.some((srcDir) => file.startsWith(srcDir) && (file.endsWith(".ts") || file.endsWith(".tsx"))))
|
|
3765
|
-
|
|
3786
|
+
if (!srcDirs.some((srcDir) => file.startsWith(srcDir) && (file.endsWith(".ts") || file.endsWith(".tsx")))) return;
|
|
3787
|
+
try {
|
|
3788
|
+
await regenerateDocs(resolvedOptions, root);
|
|
3766
3789
|
} catch {}
|
|
3767
3790
|
});
|
|
3768
3791
|
}
|
|
3769
3792
|
};
|
|
3770
|
-
|
|
3771
|
-
|
|
3793
|
+
}
|
|
3794
|
+
function createSsgPlugin(resolvedOptions, getRoot, ssgDevCache) {
|
|
3795
|
+
return {
|
|
3772
3796
|
name: "ox-content:ssg",
|
|
3773
3797
|
configureServer(devServer) {
|
|
3774
3798
|
if (!resolvedOptions.ssg.enabled) return;
|
|
3775
|
-
const root =
|
|
3799
|
+
const root = getRoot();
|
|
3776
3800
|
const srcDir = path.resolve(root, resolvedOptions.srcDir);
|
|
3777
3801
|
devServer.middlewares.use(createDevServerMiddleware(resolvedOptions, root, ssgDevCache));
|
|
3778
3802
|
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
|
-
}
|
|
3803
|
+
notifySsgFileAddedOrRemoved(devServer, resolvedOptions, ssgDevCache, srcDir, file, "add");
|
|
3790
3804
|
});
|
|
3791
3805
|
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
|
-
}
|
|
3806
|
+
notifySsgFileAddedOrRemoved(devServer, resolvedOptions, ssgDevCache, srcDir, file, "unlink");
|
|
3803
3807
|
});
|
|
3804
3808
|
devServer.watcher.on("change", (file) => {
|
|
3805
3809
|
if (file.startsWith(srcDir) && isMarkdownFilePath(file, resolvedOptions.extensions)) invalidatePageCache(ssgDevCache, file);
|
|
@@ -3807,63 +3811,63 @@ function oxContent(options = {}) {
|
|
|
3807
3811
|
},
|
|
3808
3812
|
async closeBundle() {
|
|
3809
3813
|
if (!resolvedOptions.ssg.enabled) return;
|
|
3810
|
-
const root = config?.root || process.cwd();
|
|
3811
3814
|
try {
|
|
3812
|
-
const result = await buildSsg(resolvedOptions,
|
|
3815
|
+
const result = await buildSsg(resolvedOptions, getRoot());
|
|
3813
3816
|
if (result.files.length > 0) console.log(`[ox-content] Generated ${result.files.length} output files`);
|
|
3814
|
-
|
|
3817
|
+
for (const error of result.errors) console.warn(`[ox-content] ${error}`);
|
|
3815
3818
|
} catch (err) {
|
|
3816
3819
|
console.error("[ox-content] SSG build failed:", err);
|
|
3817
3820
|
}
|
|
3818
3821
|
}
|
|
3819
3822
|
};
|
|
3823
|
+
}
|
|
3824
|
+
function notifySsgFileAddedOrRemoved(devServer, resolvedOptions, ssgDevCache, srcDir, file, type) {
|
|
3825
|
+
if (!file.startsWith(srcDir) || !isMarkdownFilePath(file, resolvedOptions.extensions)) return;
|
|
3826
|
+
invalidateNavCache(ssgDevCache);
|
|
3827
|
+
devServer.ws.send({
|
|
3828
|
+
type: "custom",
|
|
3829
|
+
event: "ox-content:update",
|
|
3830
|
+
data: {
|
|
3831
|
+
file,
|
|
3832
|
+
type
|
|
3833
|
+
}
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
function createSearchPlugin(resolvedOptions, getRoot) {
|
|
3820
3837
|
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
|
-
}
|
|
3838
|
+
return {
|
|
3839
|
+
name: "ox-content:search",
|
|
3840
|
+
resolveId(id) {
|
|
3841
|
+
if (id === "virtual:ox-content/search") return "\0virtual:ox-content/search";
|
|
3842
|
+
return null;
|
|
3843
|
+
},
|
|
3844
|
+
async load(id) {
|
|
3845
|
+
if (id !== "\0virtual:ox-content/search") return null;
|
|
3846
|
+
const searchOptions = resolvedOptions.search;
|
|
3847
|
+
if (!searchOptions.enabled) return "export const search = () => []; export const searchOptions = { enabled: false }; export default { search, searchOptions };";
|
|
3848
|
+
return generateSearchModule(searchOptions, resolvedOptions.base + "search-index.json");
|
|
3849
|
+
},
|
|
3850
|
+
async buildStart() {
|
|
3851
|
+
if (!resolvedOptions.search.enabled) return;
|
|
3852
|
+
const srcDir = path.resolve(getRoot(), resolvedOptions.srcDir);
|
|
3853
|
+
try {
|
|
3854
|
+
searchIndexJson = await buildSearchIndex(srcDir, resolvedOptions.base, resolvedOptions.extensions);
|
|
3855
|
+
console.log("[ox-content] Search index built");
|
|
3856
|
+
} catch (err) {
|
|
3857
|
+
console.warn("[ox-content] Failed to build search index:", err);
|
|
3858
|
+
}
|
|
3859
|
+
},
|
|
3860
|
+
async closeBundle() {
|
|
3861
|
+
if (!resolvedOptions.search.enabled || !searchIndexJson) return;
|
|
3862
|
+
const outDir = path.resolve(getRoot(), resolvedOptions.outDir);
|
|
3863
|
+
try {
|
|
3864
|
+
await writeSearchIndex(searchIndexJson, outDir);
|
|
3865
|
+
console.log("[ox-content] Search index written to", path.join(outDir, "search-index.json"));
|
|
3866
|
+
} catch (err) {
|
|
3867
|
+
console.warn("[ox-content] Failed to write search index:", err);
|
|
3861
3868
|
}
|
|
3862
3869
|
}
|
|
3863
|
-
|
|
3864
|
-
if (resolvedOptions.i18n) plugins.push(createI18nPlugin(resolvedOptions));
|
|
3865
|
-
if (resolvedOptions.ogViewer) plugins.push(createOgViewerPlugin(resolvedOptions));
|
|
3866
|
-
return plugins;
|
|
3870
|
+
};
|
|
3867
3871
|
}
|
|
3868
3872
|
/**
|
|
3869
3873
|
* Resolves plugin options with defaults.
|