@ox-content/unplugin 2.90.0 → 3.0.0-alpha.10

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.d.mts CHANGED
@@ -277,11 +277,24 @@ interface OxContentOptions {
277
277
  * @default true
278
278
  */
279
279
  gfm?: boolean;
280
+ /**
281
+ * Enable MDX JSX, ESM, and expressions.
282
+ *
283
+ * When omitted, MDX is enabled for `.mdx` files only. Explicit `true` or
284
+ * `false` overrides extension-based detection.
285
+ * @default inferred from the source extension
286
+ */
287
+ mdx?: boolean;
280
288
  /**
281
289
  * Enable footnotes.
282
290
  * @default true
283
291
  */
284
292
  footnotes?: boolean;
293
+ /**
294
+ * Render footnotes as a semantic ordered section with numeric markers.
295
+ * @default false
296
+ */
297
+ semanticFootnotes?: boolean;
285
298
  /**
286
299
  * Enable tables.
287
300
  * @default true
@@ -302,11 +315,6 @@ interface OxContentOptions {
302
315
  * @default false
303
316
  */
304
317
  highlight?: boolean;
305
- /**
306
- * Syntax highlighting theme.
307
- * @default 'github-dark'
308
- */
309
- highlightTheme?: string;
310
318
  /**
311
319
  * Opt-in line annotations for fenced code blocks.
312
320
  *
@@ -327,6 +335,13 @@ interface OxContentOptions {
327
335
  * @default false
328
336
  */
329
337
  mermaid?: boolean;
338
+ /**
339
+ * Enable `$…$` inline and `$$…$$` block math.
340
+ * @default false
341
+ */
342
+ math?: boolean | {
343
+ enabled?: boolean;
344
+ };
330
345
  /**
331
346
  * Parse YAML frontmatter.
332
347
  * @default true
@@ -392,14 +407,17 @@ interface ResolvedDocsConfig {
392
407
  interface ResolvedOptions {
393
408
  srcDir: string;
394
409
  gfm: boolean;
410
+ mdx?: boolean;
395
411
  footnotes: boolean;
412
+ /** Present after `resolveOptions`. Omitted in hand-built fixtures means off. */
413
+ semanticFootnotes?: boolean;
396
414
  tables: boolean;
397
415
  taskLists: boolean;
398
416
  strikethrough: boolean;
399
417
  highlight: boolean;
400
- highlightTheme: string;
401
418
  codeAnnotations: ResolvedCodeAnnotationsOptions;
402
419
  mermaid: boolean;
420
+ math: boolean;
403
421
  frontmatter: boolean;
404
422
  toc: boolean;
405
423
  tocMaxDepth: number;
@@ -448,6 +466,11 @@ interface OxContentMdastOptions {
448
466
  * @default true
449
467
  */
450
468
  gfm?: boolean;
469
+ /**
470
+ * Enable MDX JSX, ESM, and expression nodes.
471
+ * @default false
472
+ */
473
+ mdx?: boolean;
451
474
  /**
452
475
  * Enable footnotes.
453
476
  * @default true
package/dist/src.mjs CHANGED
@@ -86,6 +86,11 @@ const KIND_DELETE = 19;
86
86
  const KIND_FOOTNOTE_REFERENCE = 20;
87
87
  const KIND_DEFINITION = 21;
88
88
  const KIND_FOOTNOTE_DEFINITION = 22;
89
+ const KIND_MDX_JSX_FLOW = 23;
90
+ const KIND_MDX_JSX_TEXT = 24;
91
+ const KIND_MDX_ESM = 25;
92
+ const KIND_MDX_FLOW_EXPRESSION = 26;
93
+ const KIND_MDX_TEXT_EXPRESSION = 27;
89
94
  const FLAG_ORDERED = 1;
90
95
  const FLAG_SPREAD = 2;
91
96
  const FLAG_CHECKED_PRESENT = 4;
@@ -309,6 +314,35 @@ function deserializeMdastFromRaw(buffer, source) {
309
314
  if (str1 !== void 0) node.label = str1;
310
315
  return node;
311
316
  }
317
+ case KIND_MDX_JSX_FLOW: return {
318
+ type: "mdxJsxFlowElement",
319
+ name: str0 ?? null,
320
+ attributes: parseMdxAttributes(str1),
321
+ children: children ?? [],
322
+ position
323
+ };
324
+ case KIND_MDX_JSX_TEXT: return {
325
+ type: "mdxJsxTextElement",
326
+ name: str0 ?? null,
327
+ attributes: parseMdxAttributes(str1),
328
+ children: children ?? [],
329
+ position
330
+ };
331
+ case KIND_MDX_ESM: return {
332
+ type: "mdxjsEsm",
333
+ value: str0 ?? "",
334
+ position
335
+ };
336
+ case KIND_MDX_FLOW_EXPRESSION: return {
337
+ type: "mdxFlowExpression",
338
+ value: str0 ?? "",
339
+ position
340
+ };
341
+ case KIND_MDX_TEXT_EXPRESSION: return {
342
+ type: "mdxTextExpression",
343
+ value: str0 ?? "",
344
+ position
345
+ };
312
346
  default: throw new Error(`[ox-content] Unsupported mdast raw node kind: ${kind}`);
313
347
  }
314
348
  };
@@ -316,6 +350,17 @@ function deserializeMdastFromRaw(buffer, source) {
316
350
  if (root.type !== "root" || !Array.isArray(root.children)) throw new Error("[ox-content] Native parser returned an invalid mdast root.");
317
351
  return root;
318
352
  }
353
+ function parseMdxAttributes(value) {
354
+ if (value === void 0) return [];
355
+ let attributes;
356
+ try {
357
+ attributes = JSON.parse(value);
358
+ } catch {
359
+ throw new Error("[ox-content] mdast raw transfer contains invalid MDX attributes.");
360
+ }
361
+ if (!Array.isArray(attributes)) throw new Error("[ox-content] mdast raw transfer contains invalid MDX attributes.");
362
+ return attributes;
363
+ }
319
364
  function readSourceOrigin(buffer, view) {
320
365
  const section = parseTransferEnvelope(buffer)?.sections.get(MDAST_SECTION_SOURCE_ORIGIN$1);
321
366
  if (!section) return;
@@ -451,6 +496,7 @@ const require$1 = createRequire(import.meta.url);
451
496
  let cachedNapiBindings$1;
452
497
  const DEFAULT_MDAST_OPTIONS = {
453
498
  gfm: true,
499
+ mdx: false,
454
500
  footnotes: true,
455
501
  taskLists: true,
456
502
  tables: true,
@@ -483,6 +529,7 @@ function parseMarkdownToMdast(source, options = {}) {
483
529
  const resolvedOptions = resolveMdastOptions(options);
484
530
  const parserOptions = {
485
531
  gfm: resolvedOptions.gfm,
532
+ mdx: resolvedOptions.mdx,
486
533
  footnotes: resolvedOptions.footnotes,
487
534
  taskLists: resolvedOptions.taskLists,
488
535
  tables: resolvedOptions.tables,
@@ -550,6 +597,7 @@ function requireNapiMethod$1(method, name) {
550
597
  function resolveMdastOptions(options) {
551
598
  return {
552
599
  gfm: options.gfm ?? DEFAULT_MDAST_OPTIONS.gfm,
600
+ mdx: options.mdx ?? DEFAULT_MDAST_OPTIONS.mdx,
553
601
  footnotes: options.footnotes ?? DEFAULT_MDAST_OPTIONS.footnotes,
554
602
  taskLists: options.taskLists ?? DEFAULT_MDAST_OPTIONS.taskLists,
555
603
  tables: options.tables ?? DEFAULT_MDAST_OPTIONS.tables,
@@ -590,6 +638,21 @@ function slugify$1(text) {
590
638
  return text.toLowerCase().replace(/[^\p{L}\p{N}\s-]+/gu, " ").trim().split(/\s+/).filter(Boolean).join("-");
591
639
  }
592
640
  //#endregion
641
+ //#region src/source-path.ts
642
+ function sourcePathname(id) {
643
+ return id.split("?")[0].split("#")[0];
644
+ }
645
+ function isConfiguredSourceFile(id, extensions) {
646
+ const pathname = sourcePathname(id).toLowerCase();
647
+ return extensions.some((extension) => pathname.endsWith(extension.toLowerCase()));
648
+ }
649
+ function isMdxSourceFile(id) {
650
+ return sourcePathname(id).toLowerCase().endsWith(".mdx");
651
+ }
652
+ function resolveMdxForSourceFile(id, configured) {
653
+ return configured ?? isMdxSourceFile(id);
654
+ }
655
+ //#endregion
593
656
  //#region src/transform.ts
594
657
  /**
595
658
  * Markdown transformation logic for @ox-content/unplugin.
@@ -612,7 +675,7 @@ const PREPARED_SOURCE_SECTION_SOURCE_ORIGIN = 3;
612
675
  * Note: This requires the @ox-content/napi package to be built.
613
676
  */
614
677
  async function transformMarkdown(source, filePath, options) {
615
- const transformed = hasMarkdownItPlugins(options) ? await transformWithMarkdownIt(source, filePath, options) : hasUnifiedPlugins(options) ? await transformWithUnified(source, filePath, options) : transformWithNativePipeline(loadNapiBindings(), source, options);
678
+ const transformed = hasMarkdownItPlugins(options) ? await transformWithMarkdownIt(source, filePath, options) : hasUnifiedPlugins(options) ? await transformWithUnified(source, filePath, options) : transformWithNativePipeline(loadNapiBindings(), source, filePath, options);
616
679
  let nextHtml = transformed.html;
617
680
  for (const plugin of options.plugin.oxContent) nextHtml = await plugin(nextHtml);
618
681
  return {
@@ -633,14 +696,16 @@ function loadNapiBindings() {
633
696
  throw new Error("[ox-content] Failed to load @ox-content/napi. Please ensure the NAPI module is built. Run: mise run build:napi");
634
697
  }
635
698
  }
636
- function createNapiTransformOptions(options) {
699
+ function createNapiTransformOptions(options, filePath) {
637
700
  const codeAnnotations = options.codeAnnotations ?? {
638
701
  enabled: false,
639
702
  metaKey: "annotate"
640
703
  };
641
704
  return {
642
705
  gfm: options.gfm,
706
+ mdx: resolveMdxForSourceFile(filePath, options.mdx),
643
707
  footnotes: options.footnotes,
708
+ semanticFootnotes: options.semanticFootnotes,
644
709
  taskLists: options.taskLists,
645
710
  tables: options.tables,
646
711
  strikethrough: options.strikethrough,
@@ -648,7 +713,8 @@ function createNapiTransformOptions(options) {
648
713
  frontmatter: options.frontmatter,
649
714
  tocMaxDepth: options.tocMaxDepth,
650
715
  codeAnnotations: codeAnnotations.enabled,
651
- codeAnnotationMetaKey: codeAnnotations.metaKey
716
+ codeAnnotationMetaKey: codeAnnotations.metaKey,
717
+ math: options.math
652
718
  };
653
719
  }
654
720
  function parseFrontmatterJson(json) {
@@ -672,8 +738,8 @@ function hasMarkdownItPlugins(options) {
672
738
  function hasMdastOrRemarkPlugins(options) {
673
739
  return options.plugin.mdast.length > 0 || options.plugin.remark.length > 0;
674
740
  }
675
- function transformWithNativePipeline(napi, source, options) {
676
- const result = napi.transform(source, createNapiTransformOptions(options));
741
+ function transformWithNativePipeline(napi, source, filePath, options) {
742
+ const result = napi.transform(source, createNapiTransformOptions(options, filePath));
677
743
  if (result.errors.length > 0) console.warn("[ox-content] Transform warnings:", result.errors);
678
744
  const flatToc = result.toc.map((item) => ({
679
745
  ...item,
@@ -685,8 +751,8 @@ function transformWithNativePipeline(napi, source, options) {
685
751
  toc: options.toc ? buildTocTree(flatToc) : []
686
752
  };
687
753
  }
688
- function transformWithNativeMdastPipeline(napi, source, options) {
689
- return deserializeNativeMdastTransform(requireNapiMethod(napi.transformMdastRaw, "transformMdastRaw")(source, createNapiTransformOptions(options)));
754
+ function transformWithNativeMdastPipeline(napi, source, filePath, options) {
755
+ return deserializeNativeMdastTransform(requireNapiMethod(napi.transformMdastRaw, "transformMdastRaw")(source, createNapiTransformOptions(options, filePath)));
690
756
  }
691
757
  function deserializeNativeMdastTransform(buffer) {
692
758
  const envelope = parseTransferEnvelope(buffer);
@@ -781,7 +847,7 @@ async function transformWithUnified(fullSource, filePath, options) {
781
847
  const { plugins: remarkPlugins, options: remarkRehypeOptions } = extractUnifiedPluginWithOptions(options.plugin.remark, remarkRehype);
782
848
  const { plugins: rehypePlugins, options: rehypeStringifyOptions } = extractUnifiedPluginWithOptions(options.plugin.rehype, rehypeStringify);
783
849
  const parserStrategy = detectUnifiedParserStrategy(fullSource, filePath, options, remarkPlugins);
784
- const nativePayload = parserStrategy === "native" ? transformWithNativeMdastPipeline(loadNapiBindings(), fullSource, options) : null;
850
+ const nativePayload = parserStrategy === "native" ? transformWithNativeMdastPipeline(loadNapiBindings(), fullSource, filePath, options) : null;
785
851
  const preparedInput = parserStrategy === "native" ? null : prepareSourceWithRust(loadNapiBindings(), fullSource, options);
786
852
  const markdownContent = nativePayload?.content ?? preparedInput?.content ?? fullSource;
787
853
  const frontmatter = nativePayload?.frontmatter ?? preparedInput?.frontmatter ?? {};
@@ -790,7 +856,7 @@ async function transformWithUnified(fullSource, filePath, options) {
790
856
  const processor = unified();
791
857
  applyUnifiedPlugins(processor, options.plugin.mdast.map((plugin) => isOxContentMdastPlugin(plugin) ? toUnifiedMdastPlugin(plugin, mdastContext) : plugin));
792
858
  applyUnifiedPlugins(processor, remarkPlugins);
793
- installUnifiedParser(processor, parserStrategy, options, nativePayload?.tree);
859
+ installUnifiedParser(processor, parserStrategy, options, filePath, nativePayload?.tree);
794
860
  let toc = [];
795
861
  processor.use(() => {
796
862
  return (tree) => {
@@ -867,7 +933,7 @@ function applyMarkdownItPlugins(markdownIt, plugins) {
867
933
  function isOxContentMdastPlugin(plugin) {
868
934
  return Boolean(plugin) && typeof plugin === "object" && "transform" in plugin;
869
935
  }
870
- function installUnifiedParser(processor, strategy, options, nativeTree) {
936
+ function installUnifiedParser(processor, strategy, options, filePath, nativeTree) {
871
937
  if (strategy === "custom") return;
872
938
  if (strategy === "remark") {
873
939
  processor.use(remarkParse);
@@ -881,6 +947,7 @@ function installUnifiedParser(processor, strategy, options, nativeTree) {
881
947
  }
882
948
  processor.use(oxContentMdast, {
883
949
  gfm: options.gfm,
950
+ mdx: resolveMdxForSourceFile(filePath, options.mdx),
884
951
  footnotes: options.footnotes,
885
952
  taskLists: options.taskLists,
886
953
  tables: options.tables,
@@ -1222,14 +1289,16 @@ function resolveOptions(options) {
1222
1289
  return {
1223
1290
  srcDir: options.srcDir ?? "docs",
1224
1291
  gfm: options.gfm ?? true,
1292
+ mdx: options.mdx,
1225
1293
  footnotes: options.footnotes ?? true,
1294
+ semanticFootnotes: options.semanticFootnotes ?? false,
1226
1295
  tables: options.tables ?? true,
1227
1296
  taskLists: options.taskLists ?? true,
1228
1297
  strikethrough: options.strikethrough ?? true,
1229
1298
  highlight: options.highlight ?? false,
1230
- highlightTheme: options.highlightTheme ?? "github-dark",
1231
1299
  codeAnnotations: resolveCodeAnnotationsOptions(options.codeAnnotations),
1232
1300
  mermaid: options.mermaid ?? false,
1301
+ math: options.math === true || typeof options.math === "object" && options.math.enabled !== false,
1233
1302
  frontmatter: options.frontmatter ?? true,
1234
1303
  toc: options.toc ?? true,
1235
1304
  tocMaxDepth: options.tocMaxDepth ?? 3,
@@ -1264,7 +1333,7 @@ function resolveCodeAnnotationsOptions(options) {
1264
1333
  * Check if the file should be processed.
1265
1334
  */
1266
1335
  function isMarkdownFile(id, options) {
1267
- return options.extensions.some((ext) => id.endsWith(ext));
1336
+ return isConfiguredSourceFile(id, options.extensions);
1268
1337
  }
1269
1338
  /**
1270
1339
  * The unplugin factory function.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ox-content/unplugin",
3
- "version": "2.90.0",
3
+ "version": "3.0.0-alpha.10",
4
4
  "description": "Universal plugin for Ox Content - Markdown processing for webpack, rollup, esbuild, vite, and more",
5
5
  "keywords": [
6
6
  "esbuild",
@@ -65,11 +65,11 @@
65
65
  "remark-rehype": "^11.1.1",
66
66
  "unified": "^11.0.5",
67
67
  "unplugin": "^3.3.0",
68
- "@ox-content/napi": "2.90.0"
68
+ "@ox-content/napi": "3.0.0-alpha.10"
69
69
  },
70
70
  "devDependencies": {
71
- "@types/markdown-it": "^14.1.2",
72
- "@types/node": "^26.1.2",
71
+ "@types/markdown-it": "^14.2.0",
72
+ "@types/node": "^26.2.0",
73
73
  "@typescript/native-preview": "^7.0.0-dev.20260707.2",
74
74
  "esbuild": "^0.28.1",
75
75
  "rehype-autolink-headings": "^7.1.0",
@@ -84,10 +84,10 @@
84
84
  "remark-math": "^6.0.0",
85
85
  "remark-smartypants": "^3.0.3",
86
86
  "remark-toc": "^9.0.0",
87
- "rollup": "^4.62.4",
87
+ "rollup": "^4.62.5",
88
88
  "typescript": "^7.0.2",
89
89
  "vite": "npm:@voidzero-dev/vite-plus-core@0.2.8",
90
- "vite-plus": "0.2.8",
90
+ "vite-plus": "0.2.9",
91
91
  "webpack": "^5.109.2"
92
92
  },
93
93
  "scripts": {