@ox-content/vite-plugin 2.25.0 → 2.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -374,9 +374,11 @@ function protectMermaidSvgs(html) {
374
374
  * Restore protected mermaid SVG blocks from placeholders.
375
375
  */
376
376
  function restoreMermaidSvgs(html, svgs) {
377
- let result = html;
378
- for (const [placeholder, content] of svgs) result = result.replace(placeholder, content);
379
- return result;
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
+ });
380
382
  }
381
383
  //#endregion
382
384
  //#region src/transform.ts
@@ -1669,29 +1671,81 @@ function generateBareHtmlPage(content, title) {
1669
1671
  return require_napi.importNapiModuleSync().generateSsgBareHtml(content, title);
1670
1672
  }
1671
1673
  /**
1672
- * Generates HTML page with navigation using Rust NAPI bindings.
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.
1673
1677
  */
1674
- async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, theme, locale, availableLocales) {
1675
- const mod = await require_napi.importNapiModule();
1676
- const toRustTocEntry = (entry) => ({
1677
- depth: entry.depth,
1678
- text: entry.text,
1679
- slug: entry.slug,
1680
- children: entry.children?.map(toRustTocEntry) ?? []
1681
- });
1682
- const tocForRust = pageData.toc.map(toRustTocEntry);
1683
- const toRustNavItem = (item) => ({
1678
+ const navGroupsForRustCache = /* @__PURE__ */ new WeakMap();
1679
+ function toRustNavItem(item) {
1680
+ return {
1684
1681
  title: item.title,
1685
1682
  path: item.path,
1686
1683
  href: item.href,
1687
1684
  children: item.children?.map(toRustNavItem),
1688
1685
  collapsed: item.collapsed
1689
- });
1690
- const navGroupsForRust = navGroups.map((group) => ({
1686
+ };
1687
+ }
1688
+ function convertNavGroupsForRust(navGroups) {
1689
+ const cached = navGroupsForRustCache.get(navGroups);
1690
+ if (cached) return cached;
1691
+ const converted = navGroups.map((group) => ({
1691
1692
  title: group.title,
1692
1693
  collapsed: group.collapsed,
1693
1694
  items: group.items.map(toRustNavItem)
1694
1695
  }));
1696
+ navGroupsForRustCache.set(navGroups, converted);
1697
+ return converted;
1698
+ }
1699
+ /**
1700
+ * Converts a `TocEntry` tree into the plain shape the Rust binding expects.
1701
+ * Hoisted to module scope so it isn't reallocated for every page; the
1702
+ * per-page `.map` over `pageData.toc` still runs since the TOC is page-specific.
1703
+ */
1704
+ function toRustTocEntry(entry) {
1705
+ return {
1706
+ depth: entry.depth,
1707
+ text: entry.text,
1708
+ slug: entry.slug,
1709
+ children: entry.children?.map(toRustTocEntry) ?? []
1710
+ };
1711
+ }
1712
+ /**
1713
+ * Per-build cache for the Rust-facing locale list. `i18n.locales` is the same
1714
+ * reference for every page in a build, so this mapping (and the `?? "ltr"`
1715
+ * default) only runs once per build instead of once per page.
1716
+ */
1717
+ const rustLocalesCache = /* @__PURE__ */ new WeakMap();
1718
+ function toRustLocales(locales) {
1719
+ const cached = rustLocalesCache.get(locales);
1720
+ if (cached) return cached;
1721
+ const converted = locales.map((locale) => ({
1722
+ code: locale.code,
1723
+ name: locale.name,
1724
+ dir: locale.dir ?? "ltr"
1725
+ }));
1726
+ rustLocalesCache.set(locales, converted);
1727
+ return converted;
1728
+ }
1729
+ /**
1730
+ * Per-build cache for the locale-code list passed to `getSsgPageLocale`. The
1731
+ * `i18n.locales` reference is stable across a build, so the `.map` to codes
1732
+ * runs once instead of once per page.
1733
+ */
1734
+ const localeCodesCache = /* @__PURE__ */ new WeakMap();
1735
+ function localeCodesFor(locales) {
1736
+ const cached = localeCodesCache.get(locales);
1737
+ if (cached) return cached;
1738
+ const codes = locales.map((locale) => locale.code);
1739
+ localeCodesCache.set(locales, codes);
1740
+ return codes;
1741
+ }
1742
+ /**
1743
+ * Generates HTML page with navigation using Rust NAPI bindings.
1744
+ */
1745
+ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, theme, locale, availableLocales) {
1746
+ const mod = await require_napi.importNapiModule();
1747
+ const tocForRust = pageData.toc.map(toRustTocEntry);
1748
+ const navGroupsForRust = convertNavGroupsForRust(navGroups);
1695
1749
  const themeForRust = theme ? require_vitepress.themeToNapi(theme) : void 0;
1696
1750
  const entryPageForRust = pageData.entryPage ? {
1697
1751
  hero: pageData.entryPage.hero ? {
@@ -1738,11 +1792,7 @@ async function generateHtmlPage(pageData, navGroups, siteName, base, ogImage, th
1738
1792
  ogImage,
1739
1793
  theme: themeForRust,
1740
1794
  locale,
1741
- availableLocales: availableLocales?.map((l) => ({
1742
- code: l.code,
1743
- name: l.name,
1744
- dir: l.dir ?? "ltr"
1745
- }))
1795
+ availableLocales: availableLocales ? toRustLocales(availableLocales) : void 0
1746
1796
  });
1747
1797
  }
1748
1798
  async function externalizeSharedPageAssets(pages, outDir, base) {
@@ -1771,7 +1821,7 @@ function resolveNavigationGroups(navigation, base, extension) {
1771
1821
  }
1772
1822
  function getPageLocale(urlPath, i18n) {
1773
1823
  if (!i18n) return void 0;
1774
- return require_napi.importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, i18n.locales.map((locale) => locale.code)) ?? void 0;
1824
+ return require_napi.importNapiModuleSync().getSsgPageLocale(urlPath, i18n.defaultLocale, localeCodesFor(i18n.locales)) ?? void 0;
1775
1825
  }
1776
1826
  function getRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl) {
1777
1827
  return require_napi.importNapiModuleSync().resolveSsgRoutePaths(inputPath, srcDir, outDir, base, extension, siteUrl);
@@ -2891,12 +2941,15 @@ async function runStandardSpellcheckDocuments(maskedDocuments, options) {
2891
2941
  };
2892
2942
  return Promise.all(maskedDocuments.map(async (maskedDocument, index) => {
2893
2943
  if (maskedDocument.trim().length === 0) return [];
2894
- return (await spellCheckDocument({
2944
+ const result = await spellCheckDocument({
2895
2945
  languageId: "plaintext",
2896
2946
  locale,
2897
2947
  text: maskedDocument,
2898
2948
  uri: `file:///ox-content-lint-${index}.md`
2899
- }, spellCheckOptions, settings)).issues.map((issue) => mapStandardIssueToDiagnostic(issue, standard.languages, maskedDocument));
2949
+ }, spellCheckOptions, settings);
2950
+ const newlineOffsets = [];
2951
+ for (let i = 0; i < maskedDocument.length; i++) if (maskedDocument.charCodeAt(i) === 10) newlineOffsets.push(i);
2952
+ return result.issues.map((issue) => mapStandardIssueToDiagnostic(issue, standard.languages, newlineOffsets));
2900
2953
  }));
2901
2954
  } catch (error) {
2902
2955
  const imports = standard.imports.join(", ");
@@ -2917,8 +2970,8 @@ async function loadCspellLib() {
2917
2970
  cspellLibPromise ??= import("cspell-lib");
2918
2971
  return cspellLibPromise;
2919
2972
  }
2920
- function mapStandardIssueToDiagnostic(issue, languages, documentText) {
2921
- const line = getLineNumberAtOffset(documentText, issue.line.offset);
2973
+ function mapStandardIssueToDiagnostic(issue, languages, newlineOffsets) {
2974
+ const line = getLineNumberAtOffset(newlineOffsets, issue.line.offset);
2922
2975
  const column = issue.offset - issue.line.offset + 1;
2923
2976
  return {
2924
2977
  column,
@@ -2932,10 +2985,15 @@ function mapStandardIssueToDiagnostic(issue, languages, documentText) {
2932
2985
  suggestions: issue.suggestions?.slice(0, 3)
2933
2986
  };
2934
2987
  }
2935
- function getLineNumberAtOffset(text, offset) {
2936
- let line = 1;
2937
- for (let index = 0; index < offset && index < text.length; index++) if (text.charCodeAt(index) === 10) line++;
2938
- return line;
2988
+ function getLineNumberAtOffset(newlineOffsets, offset) {
2989
+ let lo = 0;
2990
+ let hi = newlineOffsets.length;
2991
+ while (lo < hi) {
2992
+ const mid = lo + hi >>> 1;
2993
+ if (newlineOffsets[mid] < offset) lo = mid + 1;
2994
+ else hi = mid;
2995
+ }
2996
+ return lo + 1;
2939
2997
  }
2940
2998
  function inferStandardIssueLanguage(word, languages) {
2941
2999
  if (/[\p{Script=Hiragana}\p{Script=Katakana}]/u.test(word) && languages.includes("ja")) return "ja";