@ox-content/vite-plugin 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -4,18 +4,20 @@ import { n as transformYouTube, t as extractVideoId } from "./youtube2.mjs";
4
4
  import { i as transformGitHub, n as fetchRepoData, r as prefetchGitHubRepos, t as collectGitHubRepos } from "./github2.mjs";
5
5
  import { i as transformOgp, n as fetchOgpData, r as prefetchOgpData, t as collectOgpUrls } from "./ogp2.mjs";
6
6
  import { createRequire } from "node:module";
7
- import * as path$1 from "path";
7
+ import * as path$2 from "path";
8
8
  import path from "path";
9
9
  import { unified } from "unified";
10
10
  import rehypeParse from "rehype-parse";
11
11
  import rehypeStringify from "rehype-stringify";
12
12
  import { createHighlighter } from "shiki";
13
+ import * as path$1 from "node:path";
13
14
  import { dirname, join } from "node:path";
14
- import * as fs$1 from "fs";
15
+ import * as fs$2 from "fs";
15
16
  import { createHash } from "node:crypto";
16
- import * as fs from "fs/promises";
17
+ import * as fs$1 from "fs/promises";
17
18
  import { glob } from "glob";
18
19
  import * as crypto from "crypto";
20
+ import * as fs from "node:fs/promises";
19
21
  import { mkdir, writeFile } from "node:fs/promises";
20
22
  //#region \0rolldown/runtime.js
21
23
  var __create = Object.create;
@@ -7655,12 +7657,12 @@ async function findFiles(dir, options) {
7655
7657
  async function walk(currentDir) {
7656
7658
  let entries;
7657
7659
  try {
7658
- entries = await fs$1.promises.readdir(currentDir, { withFileTypes: true });
7660
+ entries = await fs$2.promises.readdir(currentDir, { withFileTypes: true });
7659
7661
  } catch {
7660
7662
  return;
7661
7663
  }
7662
7664
  for (const entry of entries) {
7663
- const fullPath = path$1.join(currentDir, entry.name);
7665
+ const fullPath = path$2.join(currentDir, entry.name);
7664
7666
  if (entry.isDirectory()) {
7665
7667
  if (!isExcluded(fullPath, options.exclude)) await walk(fullPath);
7666
7668
  } else if (entry.isFile()) {
@@ -7817,7 +7819,7 @@ function generateMarkdown(docs, options) {
7817
7819
  if (options.groupBy === "file") {
7818
7820
  const docToFile = /* @__PURE__ */ new Map();
7819
7821
  for (const doc of sortedDocs) {
7820
- let fileName = path$1.basename(doc.file, path$1.extname(doc.file));
7822
+ let fileName = path$2.basename(doc.file, path$2.extname(doc.file));
7821
7823
  if (fileName === "index") fileName = "index-module";
7822
7824
  docToFile.set(doc, fileName);
7823
7825
  const markdown = generateFileMarkdown(doc, options, fileName, symbolMap);
@@ -7850,10 +7852,10 @@ function sortExtractedDocs(docs) {
7850
7852
  return [...docs].map((doc) => ({
7851
7853
  ...doc,
7852
7854
  entries: [...doc.entries].sort(compareEntriesByName)
7853
- })).sort((a, b) => compareStrings(path$1.basename(a.file), path$1.basename(b.file)));
7855
+ })).sort((a, b) => compareStrings(path$2.basename(a.file), path$2.basename(b.file)));
7854
7856
  }
7855
7857
  function generateFileMarkdown(doc, options, currentFileName, symbolMap) {
7856
- let md = `# ${path$1.basename(doc.file)}\n\n`;
7858
+ let md = `# ${path$2.basename(doc.file)}\n\n`;
7857
7859
  if (options.githubUrl) {
7858
7860
  const sourceLink = generateSourceLink(doc.file, options.githubUrl);
7859
7861
  if (sourceLink) md += sourceLink + "\n\n";
@@ -7956,7 +7958,7 @@ function generateIndex(docs, docToFile) {
7956
7958
  md += "## Modules\n\n";
7957
7959
  if (docs.length > 1) md += renderDetailsControlsHtml(".ox-api-module") + "\n\n";
7958
7960
  for (const doc of docs) {
7959
- const displayName = path$1.basename(doc.file, path$1.extname(doc.file));
7961
+ const displayName = path$2.basename(doc.file, path$2.extname(doc.file));
7960
7962
  let fileName = displayName;
7961
7963
  if (docToFile && docToFile.has(doc)) fileName = docToFile.get(doc);
7962
7964
  else if (fileName === "index") fileName = "index-module";
@@ -8037,7 +8039,7 @@ function convertSymbolLinks(text, currentFileName, symbolMap) {
8037
8039
  function buildSymbolMap(docs) {
8038
8040
  const map = /* @__PURE__ */ new Map();
8039
8041
  for (const doc of docs) {
8040
- let fileName = path$1.basename(doc.file, path$1.extname(doc.file));
8042
+ let fileName = path$2.basename(doc.file, path$2.extname(doc.file));
8041
8043
  if (fileName === "index") fileName = "index-module";
8042
8044
  for (const entry of doc.entries) map.set(entry.name, {
8043
8045
  name: entry.name,
@@ -8051,32 +8053,32 @@ function buildSymbolMap(docs) {
8051
8053
  * Writes generated documentation to the output directory.
8052
8054
  */
8053
8055
  async function writeDocs(docs, outDir, extractedDocs, options) {
8054
- await fs$1.promises.mkdir(outDir, { recursive: true });
8056
+ await fs$2.promises.mkdir(outDir, { recursive: true });
8055
8057
  const generatedFiles = new Set(Object.keys(docs));
8056
8058
  if (extractedDocs && options?.generateNav && options.groupBy === "file") generatedFiles.add("nav.ts");
8057
8059
  if (extractedDocs) generatedFiles.add(DOCS_DATA_FILE);
8058
- const manifestPath = path$1.join(outDir, DOCS_MANIFEST_FILE);
8060
+ const manifestPath = path$2.join(outDir, DOCS_MANIFEST_FILE);
8059
8061
  let previousFiles = [];
8060
8062
  try {
8061
- previousFiles = JSON.parse(await fs$1.promises.readFile(manifestPath, "utf-8"));
8063
+ previousFiles = JSON.parse(await fs$2.promises.readFile(manifestPath, "utf-8"));
8062
8064
  } catch {
8063
8065
  previousFiles = [];
8064
8066
  }
8065
8067
  for (const staleFile of previousFiles) {
8066
8068
  if (generatedFiles.has(staleFile)) continue;
8067
- await fs$1.promises.rm(path$1.join(outDir, staleFile), { force: true });
8069
+ await fs$2.promises.rm(path$2.join(outDir, staleFile), { force: true });
8068
8070
  }
8069
8071
  for (const [fileName, content] of Object.entries(docs)) {
8070
- const filePath = path$1.join(outDir, fileName);
8071
- await fs$1.promises.writeFile(filePath, content, "utf-8");
8072
+ const filePath = path$2.join(outDir, fileName);
8073
+ await fs$2.promises.writeFile(filePath, content, "utf-8");
8072
8074
  }
8073
8075
  if (extractedDocs && options?.generateNav && options.groupBy === "file") {
8074
8076
  const navCode = generateNavCode(generateNavMetadata(extractedDocs, "/api"), "apiNav");
8075
- const navFilePath = path$1.join(outDir, "nav.ts");
8076
- await fs$1.promises.writeFile(navFilePath, navCode, "utf-8");
8077
+ const navFilePath = path$2.join(outDir, "nav.ts");
8078
+ await fs$2.promises.writeFile(navFilePath, navCode, "utf-8");
8077
8079
  }
8078
- if (extractedDocs) await fs$1.promises.writeFile(path$1.join(outDir, DOCS_DATA_FILE), JSON.stringify(buildDocsData(extractedDocs), null, 2), "utf-8");
8079
- await fs$1.promises.writeFile(manifestPath, JSON.stringify([...generatedFiles].sort(), null, 2), "utf-8");
8080
+ if (extractedDocs) await fs$2.promises.writeFile(path$2.join(outDir, DOCS_DATA_FILE), JSON.stringify(buildDocsData(extractedDocs), null, 2), "utf-8");
8081
+ await fs$2.promises.writeFile(manifestPath, JSON.stringify([...generatedFiles].sort(), null, 2), "utf-8");
8080
8082
  }
8081
8083
  /**
8082
8084
  * Resolves docs options with defaults.
@@ -8161,10 +8163,10 @@ async function renderHtmlToPng(page, html, width, height, publicDir) {
8161
8163
  await route.continue();
8162
8164
  return;
8163
8165
  }
8164
- const filePath = path$1.join(publicDir, url.pathname);
8166
+ const filePath = path$2.join(publicDir, url.pathname);
8165
8167
  try {
8166
8168
  const body = await fs.readFile(filePath);
8167
- const ext = path$1.extname(filePath).toLowerCase();
8169
+ const ext = path$2.extname(filePath).toLowerCase();
8168
8170
  await route.fulfill({
8169
8171
  body,
8170
8172
  contentType: {
@@ -8349,9 +8351,9 @@ function computeCacheKey(templateSource, props, width, height) {
8349
8351
  * Returns the cached file path if found, null otherwise.
8350
8352
  */
8351
8353
  async function getCached(cacheDir, key) {
8352
- const filePath = path$1.join(cacheDir, `${key}.png`);
8354
+ const filePath = path$2.join(cacheDir, `${key}.png`);
8353
8355
  try {
8354
- return await fs.readFile(filePath);
8356
+ return await fs$1.readFile(filePath);
8355
8357
  } catch {
8356
8358
  return null;
8357
8359
  }
@@ -8360,9 +8362,9 @@ async function getCached(cacheDir, key) {
8360
8362
  * Writes a PNG buffer to the cache.
8361
8363
  */
8362
8364
  async function writeCache(cacheDir, key, png) {
8363
- await fs.mkdir(cacheDir, { recursive: true });
8364
- const filePath = path$1.join(cacheDir, `${key}.png`);
8365
- await fs.writeFile(filePath, png);
8365
+ await fs$1.mkdir(cacheDir, { recursive: true });
8366
+ const filePath = path$2.join(cacheDir, `${key}.png`);
8367
+ await fs$1.writeFile(filePath, png);
8366
8368
  }
8367
8369
  //#endregion
8368
8370
  //#region \0@oxc-project+runtime@0.115.0/helpers/usingCtx.js
@@ -8452,14 +8454,14 @@ function resolveOgImageOptions(options) {
8452
8454
  */
8453
8455
  async function resolveTemplate(options, root) {
8454
8456
  if (!options.template) return getDefaultTemplate();
8455
- const templatePath = path$1.resolve(root, options.template);
8457
+ const templatePath = path$2.resolve(root, options.template);
8456
8458
  const fs = await import("fs/promises");
8457
8459
  try {
8458
8460
  await fs.access(templatePath);
8459
8461
  } catch {
8460
8462
  throw new Error(`[ox-content:og-image] Template file not found: ${templatePath}`);
8461
8463
  }
8462
- switch (path$1.extname(templatePath).toLowerCase()) {
8464
+ switch (path$2.extname(templatePath).toLowerCase()) {
8463
8465
  case ".vue": return resolveVueTemplate(templatePath, options, root);
8464
8466
  case ".svelte": return resolveSvelteTemplate(templatePath, root);
8465
8467
  case ".tsx":
@@ -8473,9 +8475,9 @@ async function resolveTemplate(options, root) {
8473
8475
  async function resolveTsTemplate(templatePath, options, root) {
8474
8476
  const fs = await import("fs/promises");
8475
8477
  const { rolldown } = await import("rolldown");
8476
- const cacheDir = path$1.join(root, ".cache", "og-images");
8478
+ const cacheDir = path$2.join(root, ".cache", "og-images");
8477
8479
  await fs.mkdir(cacheDir, { recursive: true });
8478
- const outfile = path$1.join(cacheDir, "_template.mjs");
8480
+ const outfile = path$2.join(cacheDir, "_template.mjs");
8479
8481
  const bundle = await rolldown({
8480
8482
  input: templatePath,
8481
8483
  platform: "node"
@@ -8498,9 +8500,9 @@ async function resolveTsTemplate(templatePath, options, root) {
8498
8500
  async function resolveVueTemplate(templatePath, options, root) {
8499
8501
  const fs = await import("fs/promises");
8500
8502
  const { rolldown } = await import("rolldown");
8501
- const cacheDir = path$1.join(root, ".cache", "og-images");
8503
+ const cacheDir = path$2.join(root, ".cache", "og-images");
8502
8504
  await fs.mkdir(cacheDir, { recursive: true });
8503
- const outfile = path$1.join(cacheDir, "_template_vue.mjs");
8505
+ const outfile = path$2.join(cacheDir, "_template_vue.mjs");
8504
8506
  const bundle = await rolldown({
8505
8507
  input: templatePath,
8506
8508
  platform: "node",
@@ -8596,9 +8598,9 @@ async function getVizejsPlugin() {
8596
8598
  async function resolveSvelteTemplate(templatePath, root) {
8597
8599
  const fs = await import("fs/promises");
8598
8600
  const { rolldown } = await import("rolldown");
8599
- const cacheDir = path$1.join(root, ".cache", "og-images");
8601
+ const cacheDir = path$2.join(root, ".cache", "og-images");
8600
8602
  await fs.mkdir(cacheDir, { recursive: true });
8601
- const outfile = path$1.join(cacheDir, "_template_svelte.mjs");
8603
+ const outfile = path$2.join(cacheDir, "_template_svelte.mjs");
8602
8604
  const bundle = await rolldown({
8603
8605
  input: templatePath,
8604
8606
  platform: "node",
@@ -8654,9 +8656,9 @@ function createSvelteCompilerPlugin() {
8654
8656
  async function resolveReactTemplate(templatePath, root) {
8655
8657
  const fs = await import("fs/promises");
8656
8658
  const { rolldown } = await import("rolldown");
8657
- const cacheDir = path$1.join(root, ".cache", "og-images");
8659
+ const cacheDir = path$2.join(root, ".cache", "og-images");
8658
8660
  await fs.mkdir(cacheDir, { recursive: true });
8659
- const outfile = path$1.join(cacheDir, "_template_react.mjs");
8661
+ const outfile = path$2.join(cacheDir, "_template_react.mjs");
8660
8662
  const bundle = await rolldown({
8661
8663
  input: templatePath,
8662
8664
  platform: "node",
@@ -8706,7 +8708,7 @@ async function resolveReactTemplate(templatePath, root) {
8706
8708
  async function computeTemplateSource(options, root) {
8707
8709
  if (!options.template) return "__default__";
8708
8710
  const fs = await import("fs/promises");
8709
- const templatePath = path$1.resolve(root, options.template);
8711
+ const templatePath = path$2.resolve(root, options.template);
8710
8712
  const content = await fs.readFile(templatePath, "utf-8");
8711
8713
  return crypto.createHash("sha256").update(content).digest("hex");
8712
8714
  }
@@ -8724,7 +8726,7 @@ async function generateOgImages(pages, options, root) {
8724
8726
  if (pages.length === 0) return [];
8725
8727
  const templateFn = await resolveTemplate(options, root);
8726
8728
  const templateSource = await computeTemplateSource(options, root);
8727
- const cacheDir = path$1.join(root, ".cache", "og-images");
8729
+ const cacheDir = path$2.join(root, ".cache", "og-images");
8728
8730
  if (options.cache) {
8729
8731
  const allCached = await tryServeAllFromCache(pages, templateSource, options, cacheDir);
8730
8732
  if (allCached) return allCached;
@@ -8736,7 +8738,7 @@ async function generateOgImages(pages, options, root) {
8736
8738
  error: "Chromium not available"
8737
8739
  }));
8738
8740
  const results = [];
8739
- const publicDir = path$1.join(root, "public");
8741
+ const publicDir = path$2.join(root, "public");
8740
8742
  const concurrency = Math.max(1, options.concurrency);
8741
8743
  for (let i = 0; i < pages.length; i += concurrency) {
8742
8744
  const batch = pages.slice(i, i + concurrency);
@@ -8760,7 +8762,7 @@ async function tryServeAllFromCache(pages, templateSource, options, cacheDir) {
8760
8762
  for (const entry of pages) {
8761
8763
  const cached = await getCached(cacheDir, computeCacheKey(templateSource, entry.props, options.width, options.height));
8762
8764
  if (!cached) return null;
8763
- await fs.mkdir(path$1.dirname(entry.outputPath), { recursive: true });
8765
+ await fs.mkdir(path$2.dirname(entry.outputPath), { recursive: true });
8764
8766
  await fs.writeFile(entry.outputPath, cached);
8765
8767
  results.push({
8766
8768
  outputPath: entry.outputPath,
@@ -8778,7 +8780,7 @@ async function renderSinglePage(entry, templateFn, templateSource, options, cach
8778
8780
  if (options.cache) {
8779
8781
  const cached = await getCached(cacheDir, computeCacheKey(templateSource, entry.props, options.width, options.height));
8780
8782
  if (cached) {
8781
- await fs.mkdir(path$1.dirname(entry.outputPath), { recursive: true });
8783
+ await fs.mkdir(path$2.dirname(entry.outputPath), { recursive: true });
8782
8784
  await fs.writeFile(entry.outputPath, cached);
8783
8785
  return {
8784
8786
  outputPath: entry.outputPath,
@@ -8788,7 +8790,7 @@ async function renderSinglePage(entry, templateFn, templateSource, options, cach
8788
8790
  }
8789
8791
  const html = await templateFn(entry.props);
8790
8792
  const png = await session.renderPage(html, options.width, options.height, publicDir);
8791
- await fs.mkdir(path$1.dirname(entry.outputPath), { recursive: true });
8793
+ await fs.mkdir(path$2.dirname(entry.outputPath), { recursive: true });
8792
8794
  await fs.writeFile(entry.outputPath, png);
8793
8795
  if (options.cache) await writeCache(cacheDir, computeCacheKey(templateSource, entry.props, options.width, options.height), png);
8794
8796
  return {
@@ -10668,7 +10670,7 @@ function createSharedAssetChunk(type, label, content, outDir, base) {
10668
10670
  const hash = createContentHash(content);
10669
10671
  const fileName = `ox-content-${sanitizeChunkLabel(label)}-${hash}.${type}`;
10670
10672
  return {
10671
- outputPath: path$1.join(outDir, "assets", fileName),
10673
+ outputPath: path$2.join(outDir, "assets", fileName),
10672
10674
  publicPath: toPublicAssetPath(base, fileName),
10673
10675
  content
10674
10676
  };
@@ -10758,8 +10760,8 @@ async function externalizeSharedPageAssets(pages, outDir, base) {
10758
10760
  });
10759
10761
  const chunks = [...cssChunks.values(), ...jsChunks.values()];
10760
10762
  await Promise.all(chunks.map(async (chunk) => {
10761
- await fs.mkdir(path$1.dirname(chunk.outputPath), { recursive: true });
10762
- await fs.writeFile(chunk.outputPath, chunk.content, "utf-8");
10763
+ await fs$1.mkdir(path$2.dirname(chunk.outputPath), { recursive: true });
10764
+ await fs$1.writeFile(chunk.outputPath, chunk.content, "utf-8");
10763
10765
  }));
10764
10766
  return {
10765
10767
  pages: optimizedPages,
@@ -10770,16 +10772,16 @@ async function externalizeSharedPageAssets(pages, outDir, base) {
10770
10772
  * Converts a markdown file path to its corresponding HTML output path.
10771
10773
  */
10772
10774
  function getOutputPath(inputPath, srcDir, outDir, extension) {
10773
- const baseName = path$1.relative(srcDir, inputPath).replace(/\.(?:md|markdown)$/i, extension);
10774
- if (baseName.endsWith(`index${extension}`)) return path$1.join(outDir, baseName);
10775
+ const baseName = path$2.relative(srcDir, inputPath).replace(/\.(?:md|markdown)$/i, extension);
10776
+ if (baseName.endsWith(`index${extension}`)) return path$2.join(outDir, baseName);
10775
10777
  const dirName = baseName.replace(new RegExp(`\\${extension}$`), "");
10776
- return path$1.join(outDir, dirName, `index${extension}`);
10778
+ return path$2.join(outDir, dirName, `index${extension}`);
10777
10779
  }
10778
10780
  /**
10779
10781
  * Converts a markdown file path to a relative URL path.
10780
10782
  */
10781
10783
  function getUrlPath$1(inputPath, srcDir) {
10782
- const baseName = path$1.relative(srcDir, inputPath).replace(/\.(?:md|markdown)$/i, "");
10784
+ const baseName = path$2.relative(srcDir, inputPath).replace(/\.(?:md|markdown)$/i, "");
10783
10785
  if (baseName === "index" || baseName.endsWith("/index")) return baseName.replace(/\/?index$/, "") || "/";
10784
10786
  return baseName;
10785
10787
  }
@@ -10795,12 +10797,12 @@ function getHref(inputPath, srcDir, base, extension) {
10795
10797
  * Gets the OG image output path for a given markdown file.
10796
10798
  */
10797
10799
  function getOgImagePath(inputPath, srcDir, outDir) {
10798
- const baseName = path$1.relative(srcDir, inputPath).replace(/\.(?:md|markdown)$/i, "");
10800
+ const baseName = path$2.relative(srcDir, inputPath).replace(/\.(?:md|markdown)$/i, "");
10799
10801
  if (baseName === "index" || baseName.endsWith("/index")) {
10800
10802
  const dirPath = baseName.replace(/\/?index$/, "") || "";
10801
- return path$1.join(outDir, dirPath, "og-image.png");
10803
+ return path$2.join(outDir, dirPath, "og-image.png");
10802
10804
  }
10803
- return path$1.join(outDir, baseName, "og-image.png");
10805
+ return path$2.join(outDir, baseName, "og-image.png");
10804
10806
  }
10805
10807
  /**
10806
10808
  * Gets the OG image URL for use in meta tags.
@@ -10818,9 +10820,9 @@ function getOgImageUrl(inputPath, srcDir, base, siteUrl) {
10818
10820
  * Gets display title from file path.
10819
10821
  */
10820
10822
  function getDisplayTitle(filePath) {
10821
- const fileName = path$1.basename(filePath, path$1.extname(filePath));
10823
+ const fileName = path$2.basename(filePath, path$2.extname(filePath));
10822
10824
  if (fileName === "index") {
10823
- const dirName = path$1.basename(path$1.dirname(filePath));
10825
+ const dirName = path$2.basename(path$2.dirname(filePath));
10824
10826
  if (dirName && dirName !== ".") return formatTitle(dirName);
10825
10827
  return "Home";
10826
10828
  }
@@ -10836,7 +10838,7 @@ function formatTitle(name) {
10836
10838
  * Collects all markdown files from the source directory.
10837
10839
  */
10838
10840
  async function collectMarkdownFiles$1(srcDir) {
10839
- return (await glob(path$1.join(srcDir, "**/*.{md,markdown}"), {
10841
+ return (await glob(path$2.join(srcDir, "**/*.{md,markdown}"), {
10840
10842
  nodir: true,
10841
10843
  ignore: [
10842
10844
  "**/node_modules/**",
@@ -10857,7 +10859,7 @@ function buildNavItems(markdownFiles, srcDir, base, extension) {
10857
10859
  "api"
10858
10860
  ];
10859
10861
  for (const file of markdownFiles) {
10860
- const parts = path$1.relative(srcDir, file).split(path$1.sep);
10862
+ const parts = path$2.relative(srcDir, file).split(path$2.sep);
10861
10863
  let groupKey = "";
10862
10864
  if (parts.length > 1) groupKey = parts[0];
10863
10865
  if (!groups.has(groupKey)) groups.set(groupKey, []);
@@ -10906,14 +10908,14 @@ async function buildSsg(options, root) {
10906
10908
  files: [],
10907
10909
  errors: []
10908
10910
  };
10909
- const srcDir = path$1.resolve(root, options.srcDir);
10910
- const outDir = path$1.resolve(root, options.outDir);
10911
+ const srcDir = path$2.resolve(root, options.srcDir);
10912
+ const outDir = path$2.resolve(root, options.outDir);
10911
10913
  const base = options.base.endsWith("/") ? options.base : options.base + "/";
10912
10914
  const generatedFiles = [];
10913
10915
  const generatedPages = [];
10914
10916
  const errors = [];
10915
10917
  if (ssgOptions.clean) try {
10916
- await fs.rm(outDir, {
10918
+ await fs$1.rm(outDir, {
10917
10919
  recursive: true,
10918
10920
  force: true
10919
10921
  });
@@ -10922,8 +10924,8 @@ async function buildSsg(options, root) {
10922
10924
  const navItems = buildNavItems(markdownFiles, srcDir, base, ssgOptions.extension);
10923
10925
  let siteName = ssgOptions.siteName ?? "Documentation";
10924
10926
  if (!ssgOptions.siteName) try {
10925
- const pkgPath = path$1.join(root, "package.json");
10926
- const pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
10927
+ const pkgPath = path$2.join(root, "package.json");
10928
+ const pkg = JSON.parse(await fs$1.readFile(pkgPath, "utf-8"));
10927
10929
  if (pkg.name) siteName = formatTitle(pkg.name);
10928
10930
  } catch {}
10929
10931
  const ogImageEntries = [];
@@ -10932,7 +10934,7 @@ async function buildSsg(options, root) {
10932
10934
  const shouldGenerateOgImages = (options.ogImage || ssgOptions.generateOgImage) && !ssgOptions.bare;
10933
10935
  const pageResults = [];
10934
10936
  for (const inputPath of markdownFiles) try {
10935
- const result = await transformMarkdown(await fs.readFile(inputPath, "utf-8"), inputPath, options, {
10937
+ const result = await transformMarkdown(await fs$1.readFile(inputPath, "utf-8"), inputPath, options, {
10936
10938
  convertMdLinks: true,
10937
10939
  baseUrl: base,
10938
10940
  sourcePath: inputPath
@@ -11036,8 +11038,8 @@ async function buildSsg(options, root) {
11036
11038
  const optimizedOutput = await externalizeSharedPageAssets(generatedPages, outDir, base);
11037
11039
  generatedFiles.push(...optimizedOutput.assets);
11038
11040
  for (const page of optimizedOutput.pages) {
11039
- await fs.mkdir(path$1.dirname(page.outputPath), { recursive: true });
11040
- await fs.writeFile(page.outputPath, page.html, "utf-8");
11041
+ await fs$1.mkdir(path$2.dirname(page.outputPath), { recursive: true });
11042
+ await fs$1.writeFile(page.outputPath, page.html, "utf-8");
11041
11043
  generatedFiles.push(page.outputPath);
11042
11044
  }
11043
11045
  return {
@@ -11089,9 +11091,9 @@ async function collectMarkdownFiles(dir) {
11089
11091
  const files = [];
11090
11092
  async function walk(currentDir) {
11091
11093
  try {
11092
- const entries = await fs.readdir(currentDir, { withFileTypes: true });
11094
+ const entries = await fs$1.readdir(currentDir, { withFileTypes: true });
11093
11095
  for (const entry of entries) {
11094
- const fullPath = path$1.join(currentDir, entry.name);
11096
+ const fullPath = path$2.join(currentDir, entry.name);
11095
11097
  if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") await walk(fullPath);
11096
11098
  else if (entry.isFile() && entry.name.endsWith(".md")) files.push(fullPath);
11097
11099
  }
@@ -11115,8 +11117,8 @@ async function buildSearchIndex(srcDir, base) {
11115
11117
  const files = await collectMarkdownFiles(srcDir);
11116
11118
  const documents = [];
11117
11119
  for (const file of files) try {
11118
- const content = await fs.readFile(file, "utf-8");
11119
- const relativePath = path$1.relative(srcDir, file);
11120
+ const content = await fs$1.readFile(file, "utf-8");
11121
+ const relativePath = path$2.relative(srcDir, file);
11120
11122
  const url = base + relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
11121
11123
  const id = relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
11122
11124
  const extractSearchContent = napi.extractSearchContent;
@@ -11145,9 +11147,9 @@ async function buildSearchIndex(srcDir, base) {
11145
11147
  * Writes the search index to a file.
11146
11148
  */
11147
11149
  async function writeSearchIndex(indexJson, outDir) {
11148
- const indexPath = path$1.join(outDir, "search-index.json");
11149
- await fs.mkdir(outDir, { recursive: true });
11150
- await fs.writeFile(indexPath, indexJson, "utf-8");
11150
+ const indexPath = path$2.join(outDir, "search-index.json");
11151
+ await fs$1.mkdir(outDir, { recursive: true });
11152
+ await fs$1.writeFile(indexPath, indexJson, "utf-8");
11151
11153
  }
11152
11154
  /**
11153
11155
  * Client-side search module code.
@@ -11447,14 +11449,14 @@ async function resolveMarkdownFile(url, srcDir) {
11447
11449
  let relativePath;
11448
11450
  if (pathname === "/") relativePath = "index.md";
11449
11451
  else relativePath = pathname.slice(1) + ".md";
11450
- const filePath = path$1.join(srcDir, relativePath);
11452
+ const filePath = path$2.join(srcDir, relativePath);
11451
11453
  try {
11452
- await fs.access(filePath);
11454
+ await fs$1.access(filePath);
11453
11455
  return filePath;
11454
11456
  } catch {
11455
- const indexPath = path$1.join(srcDir, pathname === "/" ? "" : pathname.slice(1), "index.md");
11457
+ const indexPath = path$2.join(srcDir, pathname === "/" ? "" : pathname.slice(1), "index.md");
11456
11458
  try {
11457
- await fs.access(indexPath);
11459
+ await fs$1.access(indexPath);
11458
11460
  return indexPath;
11459
11461
  } catch {
11460
11462
  return null;
@@ -11496,8 +11498,8 @@ function invalidatePageCache(cache, filePath) {
11496
11498
  async function resolveSiteName(options, root) {
11497
11499
  if (options.ssg.siteName) return options.ssg.siteName;
11498
11500
  try {
11499
- const pkgPath = path$1.join(root, "package.json");
11500
- const pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
11501
+ const pkgPath = path$2.join(root, "package.json");
11502
+ const pkg = JSON.parse(await fs$1.readFile(pkgPath, "utf-8"));
11501
11503
  if (pkg.name) return formatTitle(pkg.name);
11502
11504
  } catch {}
11503
11505
  return "Documentation";
@@ -11506,10 +11508,10 @@ async function resolveSiteName(options, root) {
11506
11508
  * Render a single markdown page to full HTML.
11507
11509
  */
11508
11510
  async function renderPage$1(filePath, options, navGroups, siteName, base, root) {
11509
- const srcDir = path$1.resolve(root, options.srcDir);
11511
+ const srcDir = path$2.resolve(root, options.srcDir);
11510
11512
  resetTabGroupCounter();
11511
11513
  resetIslandCounter();
11512
- const result = await transformMarkdown(await fs.readFile(filePath, "utf-8"), filePath, options, {
11514
+ const result = await transformMarkdown(await fs$1.readFile(filePath, "utf-8"), filePath, options, {
11513
11515
  convertMdLinks: true,
11514
11516
  baseUrl: base,
11515
11517
  sourcePath: filePath
@@ -11551,7 +11553,7 @@ async function renderPage$1(filePath, options, navGroups, siteName, base, root)
11551
11553
  * Create the dev server middleware for SSG page serving.
11552
11554
  */
11553
11555
  function createDevServerMiddleware(options, root, cache) {
11554
- const srcDir = path$1.resolve(root, options.srcDir);
11556
+ const srcDir = path$2.resolve(root, options.srcDir);
11555
11557
  const base = options.base.endsWith("/") ? options.base : options.base + "/";
11556
11558
  return async (req, res, next) => {
11557
11559
  const url = req.url;
@@ -11616,7 +11618,7 @@ function extractTitle(content, frontmatter) {
11616
11618
  return match ? match[1].trim() : "";
11617
11619
  }
11618
11620
  function getUrlPath(filePath, srcDir) {
11619
- let rel = path$1.relative(srcDir, filePath).replace(/\\/g, "/");
11621
+ let rel = path$2.relative(srcDir, filePath).replace(/\\/g, "/");
11620
11622
  rel = rel.replace(/\.md$/, "");
11621
11623
  if (rel === "index") return "/";
11622
11624
  if (rel.endsWith("/index")) rel = rel.slice(0, -6);
@@ -11656,7 +11658,7 @@ function validatePage(page, options) {
11656
11658
  return warnings;
11657
11659
  }
11658
11660
  async function collectPages(options, root) {
11659
- const srcDir = path$1.resolve(root, options.srcDir);
11661
+ const srcDir = path$2.resolve(root, options.srcDir);
11660
11662
  const files = await glob("**/*.md", {
11661
11663
  cwd: srcDir,
11662
11664
  absolute: true
@@ -11664,7 +11666,7 @@ async function collectPages(options, root) {
11664
11666
  const pages = [];
11665
11667
  const generateOgImage = options.ogImage || options.ssg.generateOgImage;
11666
11668
  for (const file of files.sort()) {
11667
- const content = fs$1.readFileSync(file, "utf-8");
11669
+ const content = fs$2.readFileSync(file, "utf-8");
11668
11670
  const frontmatter = parseFrontmatter(content);
11669
11671
  if (frontmatter.layout === "entry") continue;
11670
11672
  const title = extractTitle(content, frontmatter);
@@ -11674,7 +11676,7 @@ async function collectPages(options, root) {
11674
11676
  const urlPath = getUrlPath(file, srcDir);
11675
11677
  const ogImageUrl = computeOgImageUrl(urlPath, options.base, options.ssg.siteUrl, generateOgImage, options.ssg.ogImage);
11676
11678
  const page = {
11677
- path: path$1.relative(srcDir, file),
11679
+ path: path$2.relative(srcDir, file),
11678
11680
  urlPath,
11679
11681
  title,
11680
11682
  description,
@@ -11987,8 +11989,8 @@ function createI18nPlugin(resolvedOptions) {
11987
11989
  },
11988
11990
  async buildStart() {
11989
11991
  if (!i18nOptions || !i18nOptions.check) return;
11990
- const dictDir = path$1.resolve(root, i18nOptions.dir);
11991
- if (!fs$1.existsSync(dictDir)) {
11992
+ const dictDir = path$2.resolve(root, i18nOptions.dir);
11993
+ if (!fs$2.existsSync(dictDir)) {
11992
11994
  console.warn(`[ox-content:i18n] Dictionary directory not found: ${dictDir}`);
11993
11995
  return;
11994
11996
  }
@@ -12009,8 +12011,8 @@ function createI18nPlugin(resolvedOptions) {
12009
12011
  },
12010
12012
  configureServer(server) {
12011
12013
  if (!i18nOptions) return;
12012
- const dictDir = path$1.resolve(root, i18nOptions.dir);
12013
- if (fs$1.existsSync(dictDir)) {
12014
+ const dictDir = path$2.resolve(root, i18nOptions.dir);
12015
+ if (fs$2.existsSync(dictDir)) {
12014
12016
  server.watcher.add(dictDir);
12015
12017
  server.watcher.on("change", (filePath) => {
12016
12018
  if (!filePath.startsWith(dictDir)) return;
@@ -12036,7 +12038,7 @@ function createI18nPlugin(resolvedOptions) {
12036
12038
  * Generates the virtual module for i18n configuration.
12037
12039
  */
12038
12040
  function generateI18nModule(options, root) {
12039
- const dictDir = path$1.resolve(root, options.dir);
12041
+ const dictDir = path$2.resolve(root, options.dir);
12040
12042
  const localesJson = JSON.stringify(options.locales);
12041
12043
  const defaultLocale = JSON.stringify(options.defaultLocale);
12042
12044
  let dictionariesCode = "{}";
@@ -12119,15 +12121,15 @@ function flattenObject(obj, prefix, result) {
12119
12121
  function loadDictionariesFallback(options, dictDir) {
12120
12122
  const dictData = {};
12121
12123
  for (const locale of options.locales) {
12122
- const localeDir = path$1.join(dictDir, locale.code);
12123
- if (!fs$1.existsSync(localeDir)) continue;
12124
- const files = fs$1.readdirSync(localeDir);
12124
+ const localeDir = path$2.join(dictDir, locale.code);
12125
+ if (!fs$2.existsSync(localeDir)) continue;
12126
+ const files = fs$2.readdirSync(localeDir);
12125
12127
  const localeDict = {};
12126
12128
  for (const file of files) {
12127
12129
  if (!file.endsWith(".json")) continue;
12128
- const filePath = path$1.join(localeDir, file);
12129
- const content = fs$1.readFileSync(filePath, "utf-8");
12130
- const namespace = path$1.basename(file, ".json");
12130
+ const filePath = path$2.join(localeDir, file);
12131
+ const content = fs$2.readFileSync(filePath, "utf-8");
12132
+ const namespace = path$2.basename(file, ".json");
12131
12133
  try {
12132
12134
  flattenObject(JSON.parse(content), namespace, localeDict);
12133
12135
  } catch {}
@@ -12140,17 +12142,17 @@ function loadDictionariesFallback(options, dictDir) {
12140
12142
  * Collects translation keys from source files using NAPI extractTranslationKeys.
12141
12143
  */
12142
12144
  function collectKeysFromSource(root, extractTranslationKeys, options) {
12143
- const srcDir = path$1.resolve(root, "src");
12145
+ const srcDir = path$2.resolve(root, "src");
12144
12146
  const keys = /* @__PURE__ */ new Set();
12145
- if (fs$1.existsSync(srcDir)) walkDir(srcDir, /\.(ts|tsx|js|jsx)$/, (filePath) => {
12146
- const usages = extractTranslationKeys(fs$1.readFileSync(filePath, "utf-8"), filePath, options.functionNames);
12147
+ if (fs$2.existsSync(srcDir)) walkDir(srcDir, /\.(ts|tsx|js|jsx)$/, (filePath) => {
12148
+ const usages = extractTranslationKeys(fs$2.readFileSync(filePath, "utf-8"), filePath, options.functionNames);
12147
12149
  for (const usage of usages) keys.add(usage.key);
12148
12150
  });
12149
- const contentDir = path$1.resolve(root, "content");
12150
- if (fs$1.existsSync(contentDir)) {
12151
+ const contentDir = path$2.resolve(root, "content");
12152
+ if (fs$2.existsSync(contentDir)) {
12151
12153
  const tPattern = /\{\{t\(['"]([^'"]+)['"]\)\}\}/g;
12152
12154
  walkDir(contentDir, /\.(md|mdx)$/, (filePath) => {
12153
- const content = fs$1.readFileSync(filePath, "utf-8");
12155
+ const content = fs$2.readFileSync(filePath, "utf-8");
12154
12156
  let match;
12155
12157
  while ((match = tPattern.exec(content)) !== null) keys.add(match[1]);
12156
12158
  tPattern.lastIndex = 0;
@@ -12162,9 +12164,9 @@ function collectKeysFromSource(root, extractTranslationKeys, options) {
12162
12164
  * Recursively walks a directory, calling the callback for files matching the pattern.
12163
12165
  */
12164
12166
  function walkDir(dir, pattern, callback) {
12165
- const entries = fs$1.readdirSync(dir, { withFileTypes: true });
12167
+ const entries = fs$2.readdirSync(dir, { withFileTypes: true });
12166
12168
  for (const entry of entries) {
12167
- const fullPath = path$1.join(dir, entry.name);
12169
+ const fullPath = path$2.join(dir, entry.name);
12168
12170
  if (entry.isDirectory()) {
12169
12171
  if (entry.name === "node_modules" || entry.name === ".git") continue;
12170
12172
  walkDir(fullPath, pattern, callback);
@@ -12172,6 +12174,358 @@ function walkDir(dir, pattern, callback) {
12172
12174
  }
12173
12175
  }
12174
12176
  //#endregion
12177
+ //#region src/lint.ts
12178
+ const require$1 = createRequire(import.meta.url);
12179
+ const SUPPORTED_MARKDOWN_LINT_LANGUAGES = [
12180
+ "en",
12181
+ "ja",
12182
+ "zh",
12183
+ "fr",
12184
+ "de",
12185
+ "pl"
12186
+ ];
12187
+ const DEFAULT_LANGUAGES = ["en"];
12188
+ const DEFAULT_RULES = {
12189
+ duplicateHeadings: true,
12190
+ headingIncrement: true,
12191
+ maxConsecutiveBlankLines: 1,
12192
+ repeatedPunctuation: true,
12193
+ repeatedWords: true,
12194
+ spellcheck: true,
12195
+ trailingSpaces: true
12196
+ };
12197
+ const DEFAULT_CSPELL_IMPORTS = {
12198
+ de: "@cspell/dict-de-de/cspell-ext.json",
12199
+ en: "@cspell/dict-en_us/cspell-ext.json",
12200
+ fr: "@cspell/dict-fr-fr/cspell-ext.json",
12201
+ pl: "@cspell/dict-pl_pl/cspell-ext.json"
12202
+ };
12203
+ let napiBinding;
12204
+ let cspellLibPromise;
12205
+ /**
12206
+ * Lints Markdown prose with the Rust-backed built-in rule engine.
12207
+ */
12208
+ function lintMarkdown(source, options = {}) {
12209
+ return lintMarkdownWithNormalizedOptions(source, normalizeLintOptions(options));
12210
+ }
12211
+ /**
12212
+ * Async Markdown linter that supports opt-in standard dictionaries.
12213
+ */
12214
+ async function lintMarkdownAsync(source, options = {}) {
12215
+ const normalizedOptions = normalizeLintOptions(options);
12216
+ const [result] = await lintMarkdownDocumentsWithNormalizedOptions([source], normalizedOptions);
12217
+ return result ?? createEmptyLintResult$1();
12218
+ }
12219
+ /**
12220
+ * Internal batched Markdown linting entry point used by file-based workflows.
12221
+ */
12222
+ async function lintMarkdownDocumentsAsync(sources, options = {}) {
12223
+ return lintMarkdownDocumentsWithNormalizedOptions(sources, normalizeLintOptions(options));
12224
+ }
12225
+ function lintMarkdownWithNormalizedOptions(source, normalizedOptions) {
12226
+ if (normalizedOptions.dictionary.standard) throw new Error("[ox-content] lintMarkdownAsync is required when dictionary.standard is enabled.");
12227
+ return stripMaskedDocument(loadNapiBindingSync().lintMarkdown(source, toNapiMarkdownLintOptions(normalizedOptions)));
12228
+ }
12229
+ async function lintMarkdownDocumentsWithNormalizedOptions(sources, normalizedOptions) {
12230
+ if (sources.length === 0) return [];
12231
+ const napi = loadNapiBindingSync();
12232
+ const napiOptions = toNapiMarkdownLintOptions(normalizedOptions, Boolean(normalizedOptions.dictionary.standard));
12233
+ const builtInResults = typeof napi.lintMarkdownDocuments === "function" ? napi.lintMarkdownDocuments(sources, napiOptions) : sources.map((source) => napi.lintMarkdown(source, napiOptions));
12234
+ if (!normalizedOptions.rules.spellcheck || !normalizedOptions.dictionary.standard) return builtInResults.map(stripMaskedDocument);
12235
+ const standardDiagnostics = await runStandardSpellcheckDocuments(builtInResults.map((result) => result.maskedDocument), normalizedOptions);
12236
+ return builtInResults.map((result, index) => summarizeDiagnostics(sortDiagnostics(result.diagnostics.concat(standardDiagnostics[index] ?? []))));
12237
+ }
12238
+ function loadNapiBindingSync() {
12239
+ if (napiBinding) return napiBinding;
12240
+ if (napiBinding === null) throw new Error("[ox-content] @ox-content/napi is required for Markdown linting. Please ensure the NAPI module is built.");
12241
+ try {
12242
+ const loaded = require$1("@ox-content/napi");
12243
+ napiBinding = loaded.default && typeof loaded.default === "object" ? {
12244
+ ...loaded.default,
12245
+ ...loaded
12246
+ } : loaded;
12247
+ return napiBinding;
12248
+ } catch {
12249
+ napiBinding = null;
12250
+ throw new Error("[ox-content] @ox-content/napi is required for Markdown linting. Please ensure the NAPI module is built.");
12251
+ }
12252
+ }
12253
+ function toNapiMarkdownLintOptions(options, disableBuiltinSpellcheck = false) {
12254
+ return {
12255
+ dictionary: {
12256
+ byLanguage: Object.entries(options.dictionary.byLanguage ?? {}).map(([language, words]) => ({
12257
+ language,
12258
+ words
12259
+ })),
12260
+ ignoredWords: options.dictionary.ignoredWords,
12261
+ words: options.dictionary.words
12262
+ },
12263
+ languages: options.languages,
12264
+ rules: {
12265
+ ...options.rules,
12266
+ spellcheck: disableBuiltinSpellcheck ? false : options.rules.spellcheck
12267
+ }
12268
+ };
12269
+ }
12270
+ function stripMaskedDocument(result) {
12271
+ return {
12272
+ diagnostics: result.diagnostics,
12273
+ errorCount: result.errorCount,
12274
+ infoCount: result.infoCount,
12275
+ warningCount: result.warningCount
12276
+ };
12277
+ }
12278
+ function normalizeLintOptions(options) {
12279
+ const languages = options.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language)) ?? options.dictionary?.standard?.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language)) ?? [...DEFAULT_LANGUAGES];
12280
+ const standard = normalizeStandardDictionaryOptions(options.dictionary?.standard, languages);
12281
+ return {
12282
+ dictionary: {
12283
+ ...options.dictionary,
12284
+ standard
12285
+ },
12286
+ languages: [...new Set(languages)],
12287
+ rules: {
12288
+ duplicateHeadings: options.rules?.duplicateHeadings ?? DEFAULT_RULES.duplicateHeadings,
12289
+ headingIncrement: options.rules?.headingIncrement ?? DEFAULT_RULES.headingIncrement,
12290
+ maxConsecutiveBlankLines: options.rules?.maxConsecutiveBlankLines ?? DEFAULT_RULES.maxConsecutiveBlankLines,
12291
+ repeatedPunctuation: options.rules?.repeatedPunctuation ?? DEFAULT_RULES.repeatedPunctuation,
12292
+ repeatedWords: options.rules?.repeatedWords ?? DEFAULT_RULES.repeatedWords,
12293
+ spellcheck: options.rules?.spellcheck ?? DEFAULT_RULES.spellcheck,
12294
+ trailingSpaces: options.rules?.trailingSpaces ?? DEFAULT_RULES.trailingSpaces
12295
+ }
12296
+ };
12297
+ }
12298
+ function normalizeStandardDictionaryOptions(standard, fallbackLanguages) {
12299
+ if (!standard) return false;
12300
+ const languages = standard.languages?.filter((language) => SUPPORTED_MARKDOWN_LINT_LANGUAGES.includes(language)) ?? fallbackLanguages;
12301
+ const customImports = standard.imports ?? [];
12302
+ const missingPresetLanguages = languages.filter((language) => !DEFAULT_CSPELL_IMPORTS[language]);
12303
+ if (missingPresetLanguages.length > 0 && customImports.length === 0) throw new Error(`[ox-content] No bundled standard dictionary preset exists for ${missingPresetLanguages.join(", ")}. Provide dictionary.standard.imports to enable those languages.`);
12304
+ const imports = [...languages.map((language) => DEFAULT_CSPELL_IMPORTS[language]).filter((value) => Boolean(value)), ...customImports];
12305
+ if (imports.length === 0) throw new Error("[ox-content] dictionary.standard requires at least one bundled preset language or custom import.");
12306
+ return {
12307
+ imports: [...new Set(imports)],
12308
+ languages: [...new Set(languages)],
12309
+ provider: standard.provider ?? "cspell",
12310
+ resolveImportsRelativeTo: standard.resolveImportsRelativeTo ?? new URL(".", import.meta.url)
12311
+ };
12312
+ }
12313
+ async function runStandardSpellcheckDocuments(maskedDocuments, options) {
12314
+ const standard = options.dictionary.standard;
12315
+ if (!standard || maskedDocuments.length === 0) return maskedDocuments.map(() => []);
12316
+ try {
12317
+ const { spellCheckDocument } = await loadCspellLib();
12318
+ const locale = standard.languages.join(",");
12319
+ const settings = createStandardSpellcheckSettings(options, locale);
12320
+ return Promise.all(maskedDocuments.map(async (maskedDocument, index) => {
12321
+ if (maskedDocument.trim().length === 0) return [];
12322
+ return (await spellCheckDocument({
12323
+ languageId: "plaintext",
12324
+ locale,
12325
+ text: maskedDocument,
12326
+ uri: `file:///ox-content-lint-${index}.md`
12327
+ }, {
12328
+ generateSuggestions: true,
12329
+ noConfigSearch: true,
12330
+ numSuggestions: 3,
12331
+ resolveImportsRelativeTo: standard.resolveImportsRelativeTo
12332
+ }, settings)).issues.map((issue) => mapStandardIssueToDiagnostic(issue, standard.languages));
12333
+ }));
12334
+ } catch (error) {
12335
+ const imports = standard.imports.join(", ");
12336
+ const message = imports.length > 0 ? `[ox-content] Failed to load standard dictionaries from ${imports}. Verify the imports and install the referenced CSpell packages.` : "[ox-content] Failed to load the configured standard dictionaries.";
12337
+ throw new Error(message, { cause: error });
12338
+ }
12339
+ }
12340
+ function createStandardSpellcheckSettings(options, locale) {
12341
+ return {
12342
+ import: options.dictionary.standard ? options.dictionary.standard.imports : [],
12343
+ ignoreWords: options.dictionary.ignoredWords,
12344
+ language: locale,
12345
+ version: "0.2",
12346
+ words: [...options.dictionary.words ?? [], ...Object.values(options.dictionary.byLanguage ?? {}).flat()]
12347
+ };
12348
+ }
12349
+ async function loadCspellLib() {
12350
+ cspellLibPromise ??= import("cspell-lib");
12351
+ return cspellLibPromise;
12352
+ }
12353
+ function mapStandardIssueToDiagnostic(issue, languages) {
12354
+ const line = issue.line.position.line + 1;
12355
+ const column = issue.offset - issue.line.offset + 1;
12356
+ return {
12357
+ column,
12358
+ endColumn: column + (issue.length ?? issue.text.length),
12359
+ endLine: line,
12360
+ language: inferStandardIssueLanguage(issue.text, languages),
12361
+ line,
12362
+ message: `Unknown word "${issue.text}".`,
12363
+ ruleId: "spellcheck",
12364
+ severity: "warning",
12365
+ suggestions: issue.suggestions?.slice(0, 3)
12366
+ };
12367
+ }
12368
+ function inferStandardIssueLanguage(word, languages) {
12369
+ if (/[\p{Script=Hiragana}\p{Script=Katakana}]/u.test(word) && languages.includes("ja")) return "ja";
12370
+ if (/[\p{Script=Han}]/u.test(word)) {
12371
+ if (languages.includes("zh") && !languages.includes("ja")) return "zh";
12372
+ if (languages.includes("ja") && !languages.includes("zh")) return "ja";
12373
+ }
12374
+ if (/[\p{Script=Latin}]/u.test(word)) {
12375
+ const latinLanguages = languages.filter((language) => language !== "ja" && language !== "zh");
12376
+ if (latinLanguages.length === 1) return latinLanguages[0];
12377
+ return inferLatinLanguageFromCharacters(word, latinLanguages);
12378
+ }
12379
+ }
12380
+ function inferLatinLanguageFromCharacters(word, languages) {
12381
+ if (languages.includes("pl") && /[ąćęłńóśźż]/iu.test(word)) return "pl";
12382
+ if (languages.includes("de") && /[äöüß]/iu.test(word)) return "de";
12383
+ if (languages.includes("fr") && /[àâæçéèêëîïôœùûüÿ]/iu.test(word)) return "fr";
12384
+ }
12385
+ function summarizeDiagnostics(diagnostics) {
12386
+ let errorCount = 0;
12387
+ let warningCount = 0;
12388
+ let infoCount = 0;
12389
+ for (const diagnostic of diagnostics) if (diagnostic.severity === "error") errorCount += 1;
12390
+ else if (diagnostic.severity === "warning") warningCount += 1;
12391
+ else infoCount += 1;
12392
+ return {
12393
+ diagnostics,
12394
+ errorCount,
12395
+ infoCount,
12396
+ warningCount
12397
+ };
12398
+ }
12399
+ function createEmptyLintResult$1() {
12400
+ return summarizeDiagnostics([]);
12401
+ }
12402
+ function sortDiagnostics(diagnostics) {
12403
+ return [...diagnostics].sort((left, right) => {
12404
+ if (left.line !== right.line) return left.line - right.line;
12405
+ if (left.column !== right.column) return left.column - right.column;
12406
+ return left.ruleId.localeCompare(right.ruleId);
12407
+ });
12408
+ }
12409
+ //#endregion
12410
+ //#region src/lint-files.ts
12411
+ const DEFAULT_LINT_FILE_INCLUDE = ["**/*.md", "**/*.markdown"];
12412
+ const DEFAULT_LINT_FILE_EXCLUDE = [
12413
+ "**/node_modules/**",
12414
+ "**/.git/**",
12415
+ "**/dist/**"
12416
+ ];
12417
+ /**
12418
+ * Returns true if the file path is included by the configured glob filters.
12419
+ */
12420
+ function shouldLintMarkdownFile(filePath, options = {}) {
12421
+ const resolvedOptions = resolveMarkdownLintFileOptions(options);
12422
+ return shouldLintAbsoluteFile(path$1.resolve(resolvedOptions.cwd, filePath), resolvedOptions);
12423
+ }
12424
+ /**
12425
+ * Lints a single Markdown file using project-style include/exclude settings.
12426
+ *
12427
+ * If the file is filtered out by `include` / `exclude`, the returned result is
12428
+ * marked as `skipped` and contains no diagnostics.
12429
+ */
12430
+ async function lintMarkdownFile(filePath, options = {}) {
12431
+ const resolvedOptions = resolveMarkdownLintFileOptions(options);
12432
+ return lintMarkdownFileWithResolvedOptions(path$1.resolve(resolvedOptions.cwd, filePath), resolvedOptions);
12433
+ }
12434
+ /**
12435
+ * Lints all Markdown files matched by the configured include/exclude patterns.
12436
+ */
12437
+ async function lintMarkdownFiles(options = {}) {
12438
+ const resolvedOptions = resolveMarkdownLintFileOptions(options);
12439
+ const matchedFiles = await collectMarkdownLintFileEntries(resolvedOptions);
12440
+ const results = await lintMarkdownDocumentsAsync(await Promise.all(matchedFiles.map((file) => fs.readFile(file.filePath, "utf-8"))), resolvedOptions.lintOptions);
12441
+ const files = matchedFiles.map((file, index) => ({
12442
+ ...results[index] ?? createEmptyLintResult(),
12443
+ filePath: file.filePath,
12444
+ relativePath: file.relativePath,
12445
+ skipped: false
12446
+ }));
12447
+ const diagnostics = files.flatMap((fileResult) => fileResult.diagnostics.map((diagnostic) => ({
12448
+ ...diagnostic,
12449
+ filePath: fileResult.filePath,
12450
+ relativePath: fileResult.relativePath
12451
+ })));
12452
+ return {
12453
+ checkedFileCount: files.length,
12454
+ diagnostics,
12455
+ errorCount: files.reduce((count, fileResult) => count + fileResult.errorCount, 0),
12456
+ files,
12457
+ infoCount: files.reduce((count, fileResult) => count + fileResult.infoCount, 0),
12458
+ warningCount: files.reduce((count, fileResult) => count + fileResult.warningCount, 0)
12459
+ };
12460
+ }
12461
+ function resolveMarkdownLintFileOptions(options) {
12462
+ return {
12463
+ cwd: path$1.resolve(options.cwd ?? process.cwd()),
12464
+ exclude: [...new Set([...options.exclude ?? DEFAULT_LINT_FILE_EXCLUDE, ...options.ignore ?? []])],
12465
+ include: [...new Set(options.include ?? DEFAULT_LINT_FILE_INCLUDE)],
12466
+ lintOptions: {
12467
+ dictionary: options.dictionary,
12468
+ languages: options.languages,
12469
+ rules: options.rules
12470
+ }
12471
+ };
12472
+ }
12473
+ async function lintMarkdownFileWithResolvedOptions(filePath, options) {
12474
+ const absoluteFilePath = path$1.resolve(filePath);
12475
+ const relativePath = normalizePath(path$1.relative(options.cwd, absoluteFilePath));
12476
+ if (!shouldLintAbsoluteFile(absoluteFilePath, options)) return {
12477
+ ...createEmptyLintResult(),
12478
+ filePath: absoluteFilePath,
12479
+ relativePath,
12480
+ skipped: true
12481
+ };
12482
+ return {
12483
+ ...await lintMarkdownAsync(await fs.readFile(absoluteFilePath, "utf-8"), options.lintOptions),
12484
+ filePath: absoluteFilePath,
12485
+ relativePath,
12486
+ skipped: false
12487
+ };
12488
+ }
12489
+ async function collectMarkdownLintFileEntries(options) {
12490
+ const files = /* @__PURE__ */ new Map();
12491
+ for (const pattern of options.include) {
12492
+ const matches = await glob(pattern, {
12493
+ absolute: true,
12494
+ cwd: options.cwd,
12495
+ ignore: options.exclude,
12496
+ nodir: true
12497
+ });
12498
+ for (const filePath of matches) {
12499
+ const absoluteFilePath = path$1.resolve(filePath);
12500
+ if (shouldLintAbsoluteFile(absoluteFilePath, options)) files.set(absoluteFilePath, {
12501
+ filePath: absoluteFilePath,
12502
+ relativePath: normalizePath(path$1.relative(options.cwd, absoluteFilePath))
12503
+ });
12504
+ }
12505
+ }
12506
+ return [...files.values()].sort((left, right) => left.filePath.localeCompare(right.filePath));
12507
+ }
12508
+ function shouldLintAbsoluteFile(filePath, options) {
12509
+ const absolutePath = normalizePath(path$1.resolve(filePath));
12510
+ const relativePath = normalizePath(path$1.relative(options.cwd, absolutePath));
12511
+ const matches = (patterns) => patterns.some((pattern) => {
12512
+ const normalizedPattern = normalizePath(pattern);
12513
+ return path$1.matchesGlob(relativePath, normalizedPattern) || path$1.matchesGlob(absolutePath, normalizedPattern);
12514
+ });
12515
+ return matches(options.include) && !matches(options.exclude);
12516
+ }
12517
+ function normalizePath(value) {
12518
+ return value.split(path$1.sep).join("/");
12519
+ }
12520
+ function createEmptyLintResult() {
12521
+ return {
12522
+ diagnostics: [],
12523
+ errorCount: 0,
12524
+ infoCount: 0,
12525
+ warningCount: 0
12526
+ };
12527
+ }
12528
+ //#endregion
12175
12529
  //#region src/jsx-runtime.ts
12176
12530
  /**
12177
12531
  * Custom JSX Runtime for Static HTML Generation
@@ -12744,8 +13098,8 @@ function oxContent(options = {}) {
12744
13098
  async function regenerateDocs(root) {
12745
13099
  const docsOptions = resolvedOptions.docs;
12746
13100
  if (!docsOptions || !docsOptions.enabled) return 0;
12747
- const srcDirs = docsOptions.src.map((src) => path$1.resolve(root, src));
12748
- const outDir = path$1.resolve(root, docsOptions.out);
13101
+ const srcDirs = docsOptions.src.map((src) => path$2.resolve(root, src));
13102
+ const outDir = path$2.resolve(root, docsOptions.out);
12749
13103
  const extracted = await extractDocs(srcDirs, docsOptions);
12750
13104
  const generated = generateMarkdown(extracted, docsOptions);
12751
13105
  await writeDocs(generated, outDir, extracted, docsOptions);
@@ -12814,7 +13168,7 @@ function oxContent(options = {}) {
12814
13168
  const docsOptions = resolvedOptions.docs;
12815
13169
  if (!docsOptions || !docsOptions.enabled) return;
12816
13170
  const root = config?.root || process.cwd();
12817
- const srcDirs = docsOptions.src.map((src) => path$1.resolve(root, src));
13171
+ const srcDirs = docsOptions.src.map((src) => path$2.resolve(root, src));
12818
13172
  for (const srcDir of srcDirs) devServer.watcher.add(srcDir);
12819
13173
  devServer.watcher.on("all", async (event, file) => {
12820
13174
  if (event !== "add" && event !== "change" && event !== "unlink") return;
@@ -12830,7 +13184,7 @@ function oxContent(options = {}) {
12830
13184
  configureServer(devServer) {
12831
13185
  if (!resolvedOptions.ssg.enabled) return;
12832
13186
  const root = config?.root || process.cwd();
12833
- const srcDir = path$1.resolve(root, resolvedOptions.srcDir);
13187
+ const srcDir = path$2.resolve(root, resolvedOptions.srcDir);
12834
13188
  devServer.middlewares.use(createDevServerMiddleware(resolvedOptions, root, ssgDevCache));
12835
13189
  devServer.watcher.on("add", (file) => {
12836
13190
  if (file.startsWith(srcDir) && file.endsWith(".md")) {
@@ -12897,7 +13251,7 @@ function oxContent(options = {}) {
12897
13251
  async buildStart() {
12898
13252
  if (!resolvedOptions.search.enabled) return;
12899
13253
  const root = config?.root || process.cwd();
12900
- const srcDir = path$1.resolve(root, resolvedOptions.srcDir);
13254
+ const srcDir = path$2.resolve(root, resolvedOptions.srcDir);
12901
13255
  try {
12902
13256
  searchIndexJson = await buildSearchIndex(srcDir, resolvedOptions.base);
12903
13257
  console.log("[ox-content] Search index built");
@@ -12908,10 +13262,10 @@ function oxContent(options = {}) {
12908
13262
  async closeBundle() {
12909
13263
  if (!resolvedOptions.search.enabled || !searchIndexJson) return;
12910
13264
  const root = config?.root || process.cwd();
12911
- const outDir = path$1.resolve(root, resolvedOptions.outDir);
13265
+ const outDir = path$2.resolve(root, resolvedOptions.outDir);
12912
13266
  try {
12913
13267
  await writeSearchIndex(searchIndexJson, outDir);
12914
- console.log("[ox-content] Search index written to", path$1.join(outDir, "search-index.json"));
13268
+ console.log("[ox-content] Search index written to", path$2.join(outDir, "search-index.json"));
12915
13269
  } catch (err) {
12916
13270
  console.warn("[ox-content] Failed to write search index:", err);
12917
13271
  }
@@ -12991,6 +13345,6 @@ function generateVirtualModule(path, options) {
12991
13345
  return "export default {};";
12992
13346
  }
12993
13347
  //#endregion
12994
- export { DEFAULT_HTML_TEMPLATE, DefaultTheme, Fragment, buildSearchIndex, buildSsg, clearRenderContext, collectGitHubRepos, collectOgpUrls, createI18nPlugin, createMarkdownEnvironment, createTheme, defaultTheme, defineTheme, each, extractDocs, extractIslandInfo, extractVideoId, fetchOgpData, fetchRepoData, generateFrontmatterTypes, generateHydrationScript, generateMarkdown, generateOgImages, generateTabsCSS, generateTypes, hasIslands, inferType, jsx, jsxs, mergeThemes, mermaidClientScript, oxContent, prefetchGitHubRepos, prefetchOgpData, raw, renderAllPages, renderPage, renderToString, resolveDocsOptions, resolveI18nOptions, resolveOgImageOptions, resolveSearchOptions, resolveSsgOptions, resolveTheme, setRenderContext, transformAllPlugins, transformGitHub, transformIslands, transformMarkdown, transformMermaidStatic, transformOgp, transformTabs, transformYouTube, useIsActive, useNav, usePageProps, useRenderContext, useSiteConfig, when, writeDocs, writeSearchIndex };
13348
+ export { DEFAULT_HTML_TEMPLATE, DefaultTheme, Fragment, buildSearchIndex, buildSsg, clearRenderContext, collectGitHubRepos, collectOgpUrls, createI18nPlugin, createMarkdownEnvironment, createTheme, defaultTheme, defineTheme, each, extractDocs, extractIslandInfo, extractVideoId, fetchOgpData, fetchRepoData, generateFrontmatterTypes, generateHydrationScript, generateMarkdown, generateOgImages, generateTabsCSS, generateTypes, hasIslands, inferType, jsx, jsxs, lintMarkdown, lintMarkdownAsync, lintMarkdownFile, lintMarkdownFiles, mergeThemes, mermaidClientScript, oxContent, prefetchGitHubRepos, prefetchOgpData, raw, renderAllPages, renderPage, renderToString, resolveDocsOptions, resolveI18nOptions, resolveOgImageOptions, resolveSearchOptions, resolveSsgOptions, resolveTheme, setRenderContext, shouldLintMarkdownFile, transformAllPlugins, transformGitHub, transformIslands, transformMarkdown, transformMermaidStatic, transformOgp, transformTabs, transformYouTube, useIsActive, useNav, usePageProps, useRenderContext, useSiteConfig, when, writeDocs, writeSearchIndex };
12995
13349
 
12996
13350
  //# sourceMappingURL=index.mjs.map