UNPKG

legal-markdown-js

Version:

Node.js implementation of LegalMarkdown for processing legal documents with markdown and YAML - Complete feature parity with Ruby version

194 lines 7.06 kB
/** * Phase 2: String-Level Transformations * * This module contains transformations that MUST run on raw string content * BEFORE remark AST parsing. These transformations cannot work as remark plugins * because the AST fragments multi-line patterns across multiple nodes. * * Why String Transformations Exist: * ───────────────────────────────────── * When remark parses markdown into an AST, it splits content into nodes (paragraphs, * text, strong, emphasis, etc.). Multi-line patterns get fragmented across these nodes, * making it impossible for AST plugins to match complete patterns. * * Example: Multi-line Optional Clause * ```markdown * [l. **Warranties** * * The seller provides warranties.]{includeWarranties} * ``` * * As String (BEFORE remark): * ✅ Regex can match: /\[(.*?)\]\{(.*?)\}/s * * As AST (AFTER remark parsing): * ❌ Plugin cannot see complete pattern - it's fragmented across: * - Paragraph with Text "[", Text "l. ", Strong "Warranties", Text "]{", ... * * Transformation Order: * ──────────────────── * 0. Collect escaped template literals: \{{...}} → __LMESC_N__ placeholder * (Must happen before everything - prevents \{{}} from being processed) * * 1. Normalize field patterns: |field| → {{field}} * (Must happen before Handlebars compilation) * * 2. Process optional clauses: [content]{condition} * (Multi-line content with markdown formatting) * * 3. Process template loops: {{#each}}, {{#if}}, etc. * (Handlebars blocks that span multiple lines) * * @module * @see docs/architecture/string-transformations.md * @see Issue #149 - https://github.com/petalo/legal-markdown-js/issues/149 */ import type { YamlValue } from '../../types/index.js'; /** * Options for string-level transformations */ interface StringTransformationOptions { /** Document metadata for condition evaluation and variable expansion */ metadata: Record<string, YamlValue>; /** Enable debug logging */ debug?: boolean; /** Enable field tracking (passed to template loops) */ enableFieldTracking?: boolean; /** Use AST-first tracking tokens instead of direct spans in Phase 2 */ astFieldTracking?: boolean; /** Highlight winner branches for conditional logic */ logicBranchHighlighting?: boolean; /** Disable optional clause processing */ noClauses?: boolean; /** Custom field patterns to normalize (e.g., ['<<(.+?)>>', '|(.+?)|']) */ fieldPatterns?: string[]; } /** * Result of string transformations */ interface StringTransformationResult { /** Transformed content ready for remark AST parsing */ content: string; /** Updated metadata (includes field mappings and other tracked data) */ metadata: Record<string, YamlValue>; /** * Literal template strings collected from \{{...}} escape sequences. * Index N corresponds to placeholder `${prefix}${N}__` in content. * Must be restored after remark processing. */ escapedTemplates: string[]; } /** * Apply all string-level transformations in the correct order * * This is the main entry point for Phase 2 string transformations. * Transformations are applied in a specific order to ensure dependencies: * * 1. Field pattern normalization (|field| → {{field}}) * 2. Optional clauses processing ([content]{condition}) * 3. Template loops ({{#each}}, {{#if}}) * * @param content - Raw markdown content (without YAML frontmatter) * @param options - Transformation options * @returns Transformed content and updated metadata * * @example * ```typescript * const result = await applyStringTransformations( * content, * { * metadata: { items: [...], includeWarranty: true }, * debug: true, * enableFieldTracking: true * } * ); * * // result.content is ready for remark AST parsing * // result.metadata includes field mappings * ``` */ export declare function applyStringTransformations(content: string, options: StringTransformationOptions): Promise<StringTransformationResult>; /** * Normalize custom field patterns to standard {{field}} format * * This function converts custom field patterns (e.g., <<field>>, |field|) * into the standard {{field}} format so all fields use consistent syntax * before Handlebars compilation. * * @param content - The content to normalize * @param fieldPatterns - Array of regex patterns to normalize (e.g., ['<<(.+?)>>', '|(.+?)|']) * @param debug - Enable debug logging * @returns Normalized content and field mappings * * @example * ```typescript * const { content, mappings } = normalizeFieldPatterns( * "Template: {{name1}} and custom: <<name2>>", * ['<<(.+?)>>'], * true * ); * // content: "Template: {{name1}} and custom: {{name2}}" * // mappings: Map { "{{name2}}" => "<<name2>>" } * ``` */ declare function normalizeFieldPatterns(content: string, fieldPatterns?: string[], debug?: boolean): { content: string; mappings: Map<string, string>; }; /** * Pre-process optional clauses [content]{condition} before remark parsing * * This function processes optional clauses BEFORE the content is parsed by remark, * which allows clauses with multi-line content and markdown formatting to work correctly. * Without this pre-processing, remark would split the clause across multiple AST nodes, * making it impossible for an AST plugin to find and process them. * * Why This Cannot Be a Remark Plugin: * ─────────────────────────────────── * Multi-line optional clauses with markdown formatting get fragmented in the AST: * * Input String: * ``` * [l. **Warranties** * * The seller provides warranties.]{includeWarranties} * ``` * * After Remark Parsing (AST): * ``` * Paragraph { * children: [ * Text("["), * Text("l. "), * Strong("Warranties"), * Text("]{"), * Text("includeWarranties"), * Text("}") * ] * } * Paragraph { * children: [ * Text("The seller provides warranties.") * ] * } * ``` * * The pattern is split across multiple nodes and paragraphs, making it impossible * for a plugin to match the complete pattern. * * @param content - The markdown content with optional clauses * @param metadata - Document metadata for evaluating conditions * @param debug - Enable debug logging * @returns Content with optional clauses processed * * @example * ```typescript * const content = "[Optional content]{showThis}"; * const metadata = { showThis: true }; * const result = preprocessOptionalClauses(content, metadata); * // result: "Optional content" * ``` */ declare function preprocessOptionalClauses(content: string, metadata: Record<string, YamlValue>, debug?: boolean): string; export { normalizeFieldPatterns as _normalizeFieldPatterns, preprocessOptionalClauses as _preprocessOptionalClauses, }; //# sourceMappingURL=string-transformations.d.ts.map