TypeScript/Node.js implementation of LegalMarkdown
Try it live in the hosted playground.
What It Does
Legal Markdown JS processes legal markdown documents and can generate:
- Processed markdown
- HTML
- DOCX
Core syntax supported:
- Variables/helpers (
{{field}}, helpers, loops) - Legal headers (
l.,ll.,lll., ...) - Optional clauses (
[text]{condition}) - Cross references between sections (
|reference|) - Imports (
@import path/to/file.md)
Check the features overview document for more details.
Install
npm
Run without installing globally:
npx legal-md contract.md output.md
Install the package locally in a project:
npm install legal-markdown-js
Available binaries:
legal-mdlegal-md-uilegal-md-playground
Homebrew
Install the standalone macOS binary:
brew tap petalo/legal-markdown brew install legal-md
After installing with Homebrew, you can use:
legal-mdlegal-md uilegal-md playground
Install script
curl -fsSL https://github.com/petalo/legal-markdown-js/releases/latest/download/install.sh | shQuick Start
Install, then run your first conversion in seconds:
1. Install
# npm npm install legal-markdown-js # Homebrew (macOS) brew tap petalo/legal-markdown && brew install legal-md
2. Process a document
Convert a Legal Markdown file to processed Markdown:
legal-md contract.md output.md
Generate HTML, PDF, or DOCX:
legal-md contract.md --html legal-md contract.md --pdf legal-md contract.md --docx
Input (contract.md) |
Output markdown | Output HTML |
|---|---|---|
|
|
|
Playground And UI
Hosted playground
Use the browser-based playground for a quick interactive test: petalo.github.io/legal-markdown-js
Interactive CLI
Launch the terminal UI:
legal-md-ui
# or
legal-md uiLocal playground
If you installed from npm or Homebrew:
legal-md-playground
# or
legal-md playgroundIf you are working from this repository:
npm run build:web npm run web:serve
Useful variants:
# Vite dev server for playground development npm run dev:web # Serve an existing build on a custom port npm run web:serve -- --port=3000
CLI Usage
Basic processing
# Input -> output markdown legal-md input.md output.md # Input -> stdout markdown legal-md input.md --stdout # Read from stdin cat input.md | legal-md --stdin --stdout
Output formats
# HTML legal-md input.md output.html --html # PDF legal-md input.md output.pdf --pdf # DOCX legal-md input.md output.docx --docx # Highlighted review variants legal-md input.md output.pdf --pdf --highlight legal-md input.md output.docx --docx --highlight
PDF connector selection
# Auto (default) legal-md input.md output.pdf --pdf # Force specific backend legal-md input.md output.pdf --pdf --pdf-connector puppeteer legal-md input.md output.pdf --pdf --pdf-connector system-chrome legal-md input.md output.pdf --pdf --pdf-connector weasyprint
auto resolution order is:
puppeteersystem-chromeweasyprint
Metadata export
legal-md contract.md --export-yaml -o metadata.yaml legal-md contract.md --export-json -o metadata.json
Useful flags
legal-md contract.md --title "Master Services Agreement" --html
legal-md contract.md output.html --html --css ./styles/print.css
legal-md contract.md --enable-field-tracking --stdoutProgrammatic API
import { processLegalMarkdown, generateHtml, generatePdf, generatePdfVersions, generateDocx, generateDocxVersions, } from 'legal-markdown-js'; const source = `---\ntitle: Service Agreement\nclient: ACME\n---\n\nl. Parties\n\nAgreement with {{client}}.`; const processed = await processLegalMarkdown(source, { enableFieldTracking: true, }); const html = await generateHtml(source, { title: 'Service Agreement', includeHighlighting: true, }); const pdf = await generatePdf(source, './output/agreement.pdf', { format: 'A4', includeHighlighting: false, pdfConnector: 'auto', // auto | puppeteer | system-chrome | weasyprint }); const { normal, highlighted } = await generatePdfVersions( source, './output/agreement.pdf', { format: 'Letter', pdfConnector: 'weasyprint', } ); const docx = await generateDocx(source, './output/agreement.docx', { title: 'Service Agreement', }); const docxPair = await generateDocxVersions(source, './output/agreement.docx'); console.log( processed.content, html.length, pdf.length, normal.length, highlighted.length ); console.log(docx.length, docxPair.normal.length, docxPair.highlighted.length);
PDF Backends
Supported backends:
puppeteersystem-chromeweasyprint
Installation examples:
# Puppeteer browser install (if needed) npx puppeteer browsers install chrome # macOS brew install weasyprint # Ubuntu/Debian sudo apt-get install -y weasyprint
Configuration
Configuration loading supports:
package.json(legalmdkey).legalmdrc.legalmdrc.yaml.legalmdrc.jsonlegalmd.config.jslegalmd.config.ts
Useful env overrides:
LEGAL_MD_PDF_CONNECTORLEGAL_MD_VALIDATION_MODELOG_LEVELDEBUGIMAGES_DIRSTYLES_DIRDEFAULT_INPUT_DIRDEFAULT_OUTPUT_DIRARCHIVE_DIR
Example:
LEGAL_MD_PDF_CONNECTOR=weasyprint legal-md input.md --pdf
Testing
# Full local suite npm test # CI-like run (includes PDF backend precheck) npm run test:ci # Backend availability check only npm run test:pdf:backends # Targeted suites npm run test:unit npm run test:integration npm run test:e2e npm run test:e2e:cli
test:e2e and test:ci require both PDF paths to be available:
- Puppeteer launchable Chrome/Chromium
- WeasyPrint executable
Documentation
- Getting Started
- CLI Reference
- Features Overview
- Output Guides
- PDF Generation
- DOCX Generation
- Configuration
- Architecture
- Development Guide
- Contributing
Contributing
See docs/development/contributing.md.
License
MIT. See LICENSE.
Acknowledgments
Based on the original LegalMarkdown project by Casey Kuhlman.
