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