legal-markdown-js
Version:
Node.js implementation of LegalMarkdown for processing legal documents with markdown and YAML - Complete feature parity with Ruby version
410 lines • 19.1 kB
JavaScript
;
/**
* Main Entry Point for Legal Markdown Processing Library
*
* This module provides the primary API for processing Legal Markdown documents
* with support for YAML front matter, cross-references, optional clauses, mixins,
* header processing, and multi-format output generation (HTML, PDF, DOCX).
*
* Features:
* - YAML front matter parsing and metadata extraction
* - Cross-reference processing and resolution
* - Optional clause conditional rendering
* - Mixin system for reusable content blocks
* - Header numbering and formatting
* - Field tracking for document highlighting
* - HTML, PDF, and DOCX generation with styling
* - RST and LaTeX preprocessing support
* - Metadata export capabilities
*
* @example
* ```typescript
* import { processLegalMarkdown, generateHtml, generatePdf } from 'legal-markdown-js';
*
* // Basic document processing
* const result = await processLegalMarkdown(content, {
* enableFieldTracking: true,
* basePath: './documents'
* });
*
* // Generate HTML output
* const html = await generateHtml(content, {
* title: 'Legal Agreement',
* includeHighlighting: true
* });
*
* // Generate PDF output
* const pdf = await generatePdf(content, './output.pdf', {
* format: 'A4',
* includeHighlighting: true
* });
* ```
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.docxGenerator = exports.pdfGenerator = exports.htmlGenerator = exports.fieldTracker = exports.processLegalMarkdown = void 0;
exports.generateHtml = generateHtml;
exports.generatePdf = generatePdf;
exports.generatePdfVersions = generatePdfVersions;
exports.generateDocx = generateDocx;
exports.generateDocxVersions = generateDocxVersions;
const html_generator_1 = require("./extensions/generators/html-generator");
const pdf_generator_1 = require("./extensions/generators/pdf-generator");
const legal_markdown_processor_1 = require("./extensions/remark/legal-markdown-processor");
const docx_generator_1 = require("./extensions/generators/docx-generator");
const pdf_connectors_1 = require("./extensions/generators/pdf-connectors");
const fs = __importStar(require("fs/promises"));
const path_1 = __importDefault(require("path"));
/**
* Process Legal Markdown content using the canonical async remark pipeline.
*
* @param content - Raw Legal Markdown document content.
* @param options - Optional processing settings for parsing, plugins, tracking, and exports.
* @returns Promise resolving to processed markdown, metadata, and optional tracking statistics.
* @throws {ValidationError | PipelineError | ParseError | ImportError | ProcessingError | YamlParsingError | PdfDependencyError}
* Throws typed processing errors when parsing, import resolution, or pipeline execution fails.
* @example
* ```typescript
* import { processLegalMarkdown } from 'legal-markdown-js';
*
* const result = await processLegalMarkdown(markdown, {
* basePath: './docs',
* enableFieldTracking: true,
* });
*
* console.log(result.content);
* ```
*/
exports.processLegalMarkdown = legal_markdown_processor_1.processLegalMarkdown;
/**
* Generate HTML from Legal Markdown content
*
* This function processes Legal Markdown content and generates a complete HTML
* document with styling, field highlighting, and responsive design features.
* It combines the Legal Markdown processing pipeline with HTML generation.
*
* @param {string} content - The raw Legal Markdown content to convert
* @param {LegalMarkdownOptions & Object} [options={}] - Configuration options
* @param {string} [options.cssPath] - Path to custom CSS file
* @param {string} [options.highlightCssPath] - Path to field highlighting CSS
* @param {boolean} [options.includeHighlighting] - Whether to include field highlighting
* @param {string} [options.title] - Document title for HTML
* @returns {Promise<string>} A promise that resolves to the complete HTML document
* @throws {Error} When HTML generation fails
* @example
* ```typescript
* const html = await generateHtml(content, {
* title: 'Service Agreement',
* cssPath: './custom-styles.css',
* includeHighlighting: true,
* enableFieldTracking: true
* });
* ```
*/
async function generateHtml(content, options = {}) {
try {
// Always use remark processor for HTML generation to ensure field tracking works
// Field tracking is essential for HTML structure (headers, mixins, etc.)
const result = await (0, exports.processLegalMarkdown)(content, {
...options,
enableFieldTracking: true, // Always enabled for HTML generation (structure)
debug: options.debug || false,
});
return html_generator_1.htmlGenerator.generateHtml(result.content, {
cssPath: options.cssPath,
highlightCssPath: options.highlightCssPath || path_1.default.join(process.cwd(), 'src/styles/highlight.css'),
includeHighlighting: options.includeHighlighting,
title: options.title ||
(typeof result.metadata?.title === 'string' ? result.metadata.title : undefined),
metadata: result.metadata,
});
}
catch (error) {
console.warn('HTML generation pipeline failed, falling back to legacy processing:', error);
return generateHtmlLegacy(content, options);
}
}
/**
* Legacy HTML generation as fallback
*/
async function generateHtmlLegacy(content, options = {}) {
// Process the legal markdown first (use async version for better RST/LaTeX support)
const processed = await (0, exports.processLegalMarkdown)(content, {
...options,
enableFieldTracking: true,
});
// Generate HTML
return html_generator_1.htmlGenerator.generateHtml(processed.content, {
cssPath: options.cssPath,
highlightCssPath: options.highlightCssPath || path_1.default.join(process.cwd(), 'src/styles/highlight.css'),
includeHighlighting: options.includeHighlighting,
title: options.title,
metadata: processed.metadata,
});
}
async function generatePdfFromProcessedMarkdown(processedContent, outputPath, metadata, options) {
const connectorPreference = options.pdfConnector ?? 'auto';
const connector = await (0, pdf_connectors_1.resolvePdfConnector)(connectorPreference);
const title = options.title || (typeof metadata?.title === 'string' ? metadata.title : undefined);
// Keep historical behavior for the Puppeteer connector, including template handling.
if (connector.name === 'puppeteer') {
return pdf_generator_1.pdfGenerator.generatePdf(processedContent, outputPath, {
cssPath: options.cssPath,
highlightCssPath: options.highlightCssPath || path_1.default.join(process.cwd(), 'src/styles/highlight.css'),
includeHighlighting: options.includeHighlighting,
title,
metadata,
format: options.format,
landscape: options.landscape,
headerTemplate: options.headerTemplate,
footerTemplate: options.footerTemplate,
});
}
const html = await html_generator_1.htmlGenerator.generateHtml(processedContent, {
cssPath: options.cssPath,
highlightCssPath: options.highlightCssPath || path_1.default.join(process.cwd(), 'src/styles/highlight.css'),
includeHighlighting: options.includeHighlighting,
title,
metadata,
});
const pdfOptions = {
format: options.format || 'A4',
margin: {
top: '1cm',
right: '1cm',
bottom: '1cm',
left: '1cm',
},
landscape: options.landscape,
headerTemplate: options.headerTemplate,
footerTemplate: options.footerTemplate,
};
await connector.generatePdf(html, outputPath, pdfOptions);
return fs.readFile(outputPath);
}
/**
* Generate PDF from Legal Markdown content
*
* This function processes Legal Markdown content and generates a PDF document
* with professional styling, field highlighting, and customizable page formatting.
* It combines the Legal Markdown processing pipeline with PDF generation.
*
* @param {string} content - The raw Legal Markdown content to convert
* @param {string} outputPath - File path where the PDF will be saved
* @param {LegalMarkdownOptions & Object} [options={}] - Configuration options
* @param {string} [options.cssPath] - Path to custom CSS file
* @param {string} [options.highlightCssPath] - Path to field highlighting CSS
* @param {boolean} [options.includeHighlighting] - Whether to include field highlighting
* @param {string} [options.title] - Document title for PDF
* @param {'A4' | 'Letter' | 'Legal'} [options.format] - Page format
* @param {boolean} [options.landscape] - Whether to use landscape orientation
* @param {'auto' | 'puppeteer' | 'system-chrome' | 'weasyprint'} [options.pdfConnector] - PDF backend connector
* @returns {Promise<Buffer>} A promise that resolves to the PDF buffer
* @throws {Error} When PDF generation fails
* @example
* ```typescript
* const pdf = await generatePdf(content, './contract.pdf', {
* title: 'Service Agreement',
* format: 'A4',
* includeHighlighting: true,
* enableFieldTracking: true
* });
* ```
*/
async function generatePdf(content, outputPath, options = {}) {
try {
// Always use remark processor for PDF generation to ensure field tracking works
// Field tracking is essential for PDF structure (headers, mixins, cross-references)
const result = await (0, exports.processLegalMarkdown)(content, {
...options,
enableFieldTracking: true, // Always enabled for PDF generation (structure)
debug: options.debug || false,
});
return generatePdfFromProcessedMarkdown(result.content, outputPath, result.metadata, options);
}
catch (error) {
console.warn('PDF generation pipeline failed, falling back to legacy processing:', error);
return generatePdfLegacy(content, outputPath, options);
}
}
/**
* Legacy PDF generation as fallback
*/
async function generatePdfLegacy(content, outputPath, options = {}) {
// Process the legal markdown first (use async version for better RST/LaTeX support)
const processed = await (0, exports.processLegalMarkdown)(content, {
...options,
enableFieldTracking: true,
});
return generatePdfFromProcessedMarkdown(processed.content, outputPath, processed.metadata, options);
}
/**
* Generate both normal and highlighted PDF versions
*
* This function creates two PDF versions of the same Legal Markdown document:
* one with standard formatting and another with field highlighting enabled.
* This is useful for document review processes where both clean and annotated
* versions are needed.
*
* @param {string} content - The raw Legal Markdown content to convert
* @param {string} outputPath - Base file path for PDFs (will be modified for each version)
* @param {LegalMarkdownOptions & Object} [options={}] - Configuration options
* @param {string} [options.cssPath] - Path to custom CSS file
* @param {string} [options.highlightCssPath] - Path to field highlighting CSS
* @param {string} [options.title] - Document title for PDFs
* @param {'A4' | 'Letter' | 'Legal'} [options.format] - Page format
* @param {boolean} [options.landscape] - Whether to use landscape orientation
* @param {'auto' | 'puppeteer' | 'system-chrome' | 'weasyprint'} [options.pdfConnector] - PDF backend connector
* @returns {Promise<Object>} A promise that resolves to both PDF buffers
* @returns {Buffer} returns.normal - The normal PDF without highlighting
* @returns {Buffer} returns.highlighted - The highlighted PDF with field annotations
* @throws {Error} When PDF generation fails
* @example
* ```typescript
* const { normal, highlighted } = await generatePdfVersions(content, './contract.pdf', {
* title: 'Service Agreement',
* format: 'A4'
* });
* // Creates: contract.pdf and contract.HIGHLIGHT.pdf
* ```
*/
async function generatePdfVersions(content, outputPath, options = {}) {
const normalPath = outputPath;
const highlightedPath = outputPath.replace('.pdf', '.HIGHLIGHT.pdf');
const [normal, highlighted] = await Promise.all([
generatePdf(content, normalPath, { ...options, includeHighlighting: false }),
generatePdf(content, highlightedPath, { ...options, includeHighlighting: true }),
]);
return { normal, highlighted };
}
/**
* Generate DOCX from Legal Markdown content
*
* @param {string} content - The raw Legal Markdown content to convert
* @param {string} outputPath - File path where the DOCX will be saved
* @param {LegalMarkdownOptions & Object} [options={}] - Configuration options
* @param {string} [options.cssPath] - Path to custom CSS file
* @param {string} [options.highlightCssPath] - Path to field highlighting CSS
* @param {boolean} [options.includeHighlighting] - Whether to include field highlighting
* @param {string} [options.title] - Document title for DOCX
* @param {'A4' | 'Letter' | 'Legal'} [options.format] - Page format
* @param {boolean} [options.landscape] - Whether to use landscape orientation
* @returns {Promise<Buffer>} A promise that resolves to the DOCX buffer
* @throws {Error} When DOCX generation fails
*/
async function generateDocx(content, outputPath, options = {}) {
try {
const result = await (0, exports.processLegalMarkdown)(content, {
...options,
enableFieldTracking: true,
debug: options.debug || false,
});
return docx_generator_1.docxGenerator.generateDocx(result.content, outputPath, {
cssPath: options.cssPath,
highlightCssPath: options.highlightCssPath || path_1.default.join(process.cwd(), 'src/styles/highlight.css'),
includeHighlighting: options.includeHighlighting,
title: options.title ||
(typeof result.metadata?.title === 'string' ? result.metadata.title : undefined),
metadata: result.metadata,
format: options.format,
landscape: options.landscape,
basePath: options.basePath,
headerTemplate: options.headerTemplate,
footerTemplate: options.footerTemplate,
version: typeof result.metadata?.version === 'string' ? result.metadata.version : undefined,
});
}
catch (error) {
console.warn('DOCX generation pipeline failed, falling back to legacy processing:', error);
return generateDocxLegacy(content, outputPath, options);
}
}
/**
* Legacy DOCX generation as fallback
*/
async function generateDocxLegacy(content, outputPath, options = {}) {
const processed = await (0, exports.processLegalMarkdown)(content, {
...options,
enableFieldTracking: true,
});
return docx_generator_1.docxGenerator.generateDocx(processed.content, outputPath, {
cssPath: options.cssPath,
highlightCssPath: options.highlightCssPath || path_1.default.join(process.cwd(), 'src/styles/highlight.css'),
includeHighlighting: options.includeHighlighting,
title: options.title,
metadata: processed.metadata,
format: options.format,
landscape: options.landscape,
basePath: options.basePath,
headerTemplate: options.headerTemplate,
footerTemplate: options.footerTemplate,
version: typeof processed.metadata?.version === 'string' ? processed.metadata.version : undefined,
});
}
/**
* Generate both normal and highlighted DOCX versions
*/
async function generateDocxVersions(content, outputPath, options = {}) {
const normalPath = outputPath;
const highlightedPath = outputPath.replace('.docx', '.HIGHLIGHT.docx');
const [normal, highlighted] = await Promise.all([
generateDocx(content, normalPath, { ...options, includeHighlighting: false }),
generateDocx(content, highlightedPath, { ...options, includeHighlighting: true }),
]);
return { normal, highlighted };
}
// Export all sub-modules
__exportStar(require("./types"), exports);
__exportStar(require("./core/index"), exports);
__exportStar(require("./errors/index"), exports);
__exportStar(require("./constants/index"), exports);
__exportStar(require("./utils/index"), exports);
__exportStar(require("./extensions/index"), exports);
// Specific re-exports to avoid conflicts (extensions take precedence)
var field_tracker_1 = require("./extensions/tracking/field-tracker");
Object.defineProperty(exports, "fieldTracker", { enumerable: true, get: function () { return field_tracker_1.fieldTracker; } });
var html_generator_2 = require("./extensions/generators/html-generator");
Object.defineProperty(exports, "htmlGenerator", { enumerable: true, get: function () { return html_generator_2.htmlGenerator; } });
var pdf_generator_2 = require("./extensions/generators/pdf-generator");
Object.defineProperty(exports, "pdfGenerator", { enumerable: true, get: function () { return pdf_generator_2.pdfGenerator; } });
var docx_generator_2 = require("./extensions/generators/docx-generator");
Object.defineProperty(exports, "docxGenerator", { enumerable: true, get: function () { return docx_generator_2.docxGenerator; } });
//# sourceMappingURL=index.js.map