markdown2typst 0.1.6 → 0.1.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdown2typst",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "A comprehensive JavaScript library for converting Markdown to Typst code",
5
5
  "type": "module",
6
6
  "main": "./dist/markdown2typst.js",
@@ -97,22 +97,8 @@ export function renderBlock(
97
97
  * @returns Rendered Typst math block
98
98
  */
99
99
  function renderMathBlock(node: MathNode, indentLevel: number, context: RenderContext): string {
100
- // Reconstruct the full LaTeX string.
101
- // If a LaTeX environment is adjacent to $$, remark-math treats the adjacent text as `meta`,
102
- // and may absorb the trailing $$ into `value` if it's on the same line as the environment closure.
103
- let rawLatex = node.value || '';
104
- const meta = (node as any).meta;
105
- if (meta) {
106
- rawLatex = meta + (rawLatex ? '\n' + rawLatex : '');
107
- }
108
-
109
- // Check if the closing $$ was absorbed into the value
110
- if (rawLatex.endsWith('$$')) {
111
- rawLatex = rawLatex.slice(0, -2);
112
- }
113
-
114
100
  // Convert LaTeX to Typst math syntax
115
- const value = rawLatex.trim();
101
+ const value = node.value.trim();
116
102
  try {
117
103
  const typstMath = tex2typst(value);
118
104
  return indentLines(`$ ${typstMath} $`, indentLevel);
package/src/parser.ts CHANGED
@@ -8,6 +8,7 @@ import remarkFrontmatter from 'remark-frontmatter';
8
8
  import remarkGfm from 'remark-gfm';
9
9
  import remarkMath from 'remark-math';
10
10
  import remarkParse from 'remark-parse';
11
+ import { remarkFixAdjacentMath } from './remark-fix-math.js';
11
12
  import type { Root } from 'mdast';
12
13
  import type { ErrorCallback } from './types.js';
13
14
  import { ErrorSeverity } from './types.js';
@@ -31,7 +32,8 @@ export function parseMarkdown(markdown: string, onError?: ErrorCallback): Root {
31
32
  .use(remarkParse)
32
33
  .use(remarkFrontmatter, ['yaml'])
33
34
  .use(remarkGfm, { singleTilde: false })
34
- .use(remarkMath);
35
+ .use(remarkMath)
36
+ .use(remarkFixAdjacentMath);
35
37
 
36
38
  const parsedTree = processor.parse(markdown);
37
39
  const tree = processor.runSync(parsedTree) as Root;
@@ -0,0 +1,68 @@
1
+ import { visit } from 'unist-util-visit';
2
+ import { unified } from 'unified';
3
+ import remarkParse from 'remark-parse';
4
+ import remarkMath from 'remark-math';
5
+ import remarkGfm from 'remark-gfm';
6
+
7
+ /**
8
+ * Remark plugin to fix math blocks that swallowed adjacent text because
9
+ * of a missing newline before the closing $$.
10
+ */
11
+ export function remarkFixAdjacentMath() {
12
+ return (tree: any) => {
13
+ // First pass: fix the nodes and store the extracted nodes
14
+ visit(tree, 'math', (node: any) => {
15
+ let value = node.value || '';
16
+ const meta = node.meta;
17
+
18
+ // Reconstruct the full string because remark-math treats adjacent text as meta
19
+ if (meta) {
20
+ value = meta + (value ? '\n' + value : '');
21
+ node.meta = null; // Clear meta since it was actually part of the math
22
+ }
23
+
24
+ // Look for the first occurrence of $$ in the value
25
+ const match = value.match(/\$\$/);
26
+
27
+ if (match) {
28
+ // Found a stray $$ inside the math block!
29
+ // This means the math block swallowed the closing delimiter and everything after it.
30
+
31
+ const splitIndex = match.index;
32
+ const mathContent = value.substring(0, splitIndex);
33
+ const remainder = value.substring(splitIndex + 2); // skip $$
34
+
35
+ // Fix the math node
36
+ node.value = mathContent.trim();
37
+
38
+ // We need to append the remainder as new parsed markdown nodes after this node
39
+ if (remainder.trim().length > 0) {
40
+ // Parse the remainder with full features
41
+ const processor = unified()
42
+ .use(remarkParse)
43
+ .use(remarkGfm, { singleTilde: false })
44
+ .use(remarkMath)
45
+ // Notice we also recursively apply this plugin in case the remainder has more adjacent math
46
+ .use(remarkFixAdjacentMath);
47
+
48
+ const remainderAst = processor.parse(remainder.trim());
49
+ const remainderTree = processor.runSync(remainderAst);
50
+
51
+ // Store the nodes to be inserted
52
+ node.data = node.data || {};
53
+ node.data.insertAfter = (remainderTree as any).children;
54
+ }
55
+ } else {
56
+ node.value = value.trim();
57
+ }
58
+ });
59
+
60
+ // Second pass: insert the extracted nodes
61
+ visit(tree, (node: any, index: number | undefined, parent: any) => {
62
+ if (node.data && node.data.insertAfter && parent && index !== undefined) {
63
+ parent.children.splice(index + 1, 0, ...node.data.insertAfter);
64
+ delete node.data.insertAfter;
65
+ }
66
+ });
67
+ };
68
+ }