From f2417b6f48541b6baa6dd3475d0fe6cd398da463 Mon Sep 17 00:00:00 2001 From: Valentin Guillois Date: Thu, 5 Mar 2026 06:08:57 +0000 Subject: [PATCH] fix: replace require() with dependency injection for ESM compatibility The footnote rendering code used require() to avoid circular imports, but require() is not available in strict ESM environments (Bun, Deno, Node.js with type: module in strict mode). This change: - Adds BlockRenderer type to types.ts - Adds optional renderBlock to RenderContext - Injects renderBlock via context from markdown2typst.ts - Uses context.renderBlock in inline-renderer.ts Maintains the same circular import avoidance while being fully ESM-compatible. --- src/inline-renderer.ts | 9 ++++----- src/markdown2typst.ts | 4 +++- src/types.ts | 11 +++++++++++ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/inline-renderer.ts b/src/inline-renderer.ts index 4b7d3bb..4ef2e72 100644 --- a/src/inline-renderer.ts +++ b/src/inline-renderer.ts @@ -83,12 +83,11 @@ function renderInline( } // Render footnote content inline try { + if (!context.renderBlock) { + throw new Error('renderBlock not available in context'); + } const content = def.children - .map((child) => { - // Import renderBlock only when needed to avoid circular dependencies - const { renderBlock } = require('./block-renderer.js'); - return renderBlock(child, 0, context); - }) + .map((child) => context.renderBlock!(child, 0, context)) .filter(isNonEmpty) .join(' '); // Join blocks with space for inline footnote return `#footnote[${content.trim()}]`; diff --git a/src/markdown2typst.ts b/src/markdown2typst.ts index 4128b78..42b9c94 100644 --- a/src/markdown2typst.ts +++ b/src/markdown2typst.ts @@ -16,6 +16,7 @@ import { parseMarkdown } from './parser.js'; import { collectDefinitions, collectFootnotes, findLeadingH1 } from './collectors.js'; import { parseFrontmatter, mergeMetadata } from './frontmatter.js'; import { buildOutput } from './output-builder.js'; +import { renderBlock } from './block-renderer.js'; // Re-export types for public API export type { Markdown2TypstOptions, ConversionError, ErrorCallback } from './types.js'; @@ -76,7 +77,8 @@ export function markdown2typst(markdown: string, options: Markdown2TypstOptions definitions, footnoteDefinitions, onError: options.onError, - warnings: { externalImages: false } + warnings: { externalImages: false }, + renderBlock }; return buildOutput(tree, metadata, leadingH1?.index ?? null, context); } catch (error) { diff --git a/src/types.ts b/src/types.ts index 4e46c21..17cdc47 100644 --- a/src/types.ts +++ b/src/types.ts @@ -144,6 +144,15 @@ export type ConversionWarnings = { externalImages: boolean; }; +/** + * Block renderer function type for dependency injection + */ +export type BlockRenderer = ( + node: import('mdast').Content, + indentLevel: number, + context: RenderContext +) => string | null; + /** * Context for rendering nodes */ @@ -153,4 +162,6 @@ export type RenderContext = { onError?: ErrorCallback; /** Track conversion warnings for generating helper functions */ warnings: ConversionWarnings; + /** Block renderer function (injected to avoid circular imports) */ + renderBlock?: BlockRenderer; };