Getting Started

This page explains how to install md2form v2 and use it from the beginning.

Installation

Using npm

npm install md2form
bun add md2form

Development Environment Setup

If you plan to develop or customize the project:

git clone <repository-url>
cd md2form
bun install

For development, Bun v1.2.18 or later is recommended.

Basic Usage

Simple Example

The most basic usage of v2’s main API parseForm:

import { parseForm, isParsedDocument } from "md2form"
 
const markdown = `
# Basic Form
 
## Question Section
### Your Name
#type short_text
#required true
`
 
const result = parseForm(markdown)
 
if (!isParsedDocument(result)) {
  // If error exists, isParsedDocument returns false
  console.error(result.diagnostics)
} else {
  const form = result.document
  console.log(form.title) // "Basic Form"
}

parseForm is a synchronous function. No await is required.

ParseResult Structure

The return value of parseForm is of type ParseResult with these 3 fields:

type ParseResult = {
  document: FormDocument // Parsed form definition
  diagnostics: Diagnostic[] // Error, warning, and info messages
  ok: boolean // In strict mode: true if no errors
}

Example of checking diagnostics:

const result = parseForm(markdown)
 
// Display all diagnostics
result.diagnostics.forEach((d) => {
  console.log(`[${d.severity}] ${d.code}: ${d.message}`)
})
 
// Parse result can always be obtained (partially recovered if issues exist)
console.log(result.document.title)

When you specify strict: true, ok === false when error-level diagnostics exist:

import { parseForm, isParsedDocument } from "md2form"
 
const result = parseForm(markdown, { strict: true, validateSettings: true })
 
if (!isParsedDocument(result)) {
  console.error("Form has errors:")
  result.diagnostics
    .filter((d) => d.severity === "error")
    .forEach((d) => console.error(`  ${d.code}: ${d.message}`))
  process.exit(1)
}
 
const form = result.document

Example with Frontmatter

import { parseForm, isParsedDocument } from "md2form"
import type { FormDocument } from "md2form"
 
const markdown = `
---
collectEmail: true
showProgressBar: true
allowMultipleResponses: false
themeColor: "#3B82F6"
---
 
# Customer Survey
 
Please share your feedback.
 
## Basic Information
 
### Please Enter Your Name
#type short_text
#placeholder "John Doe"
#required true
 
### Email Address
#type email
#placeholder "example@example.com"
#required true
 
## Rating
 
### Service Satisfaction
#type rating
#scale 5
#labels "Dissatisfied","Very Satisfied"
#required true
`
 
const result = parseForm(markdown, { strict: true })
 
if (!isParsedDocument(result)) {
  throw new Error("Invalid form")
}
 
const form: FormDocument = result.document
 
// Access form information
console.log(form.title) // "Customer Survey"
console.log(form.settings?.collectEmail) // true
console.log(form.settings?.themeColor) // "#3B82F6"
console.log(form.pages.length) // 2 (Basic Information, Rating)

Structure of the Returned Object

The parsed form has this structure:

{
  schemaVersion: 2,
  title: "Customer Survey",
  description: "Please share your feedback.",
  settings: {
    collectEmail: true,
    showProgressBar: true,
    allowMultipleResponses: false,
    themeColor: "#3B82F6"
  },
  pages: [
    {
      title: "Basic Information",
      elements: [
        {
          type: "short_text",
          label: "Please Enter Your Name",  // v2: label field
          placeholder: "John Doe",
          required: true
        },
        {
          type: "email",
          label: "Email Address",
          placeholder: "example@example.com",
          required: true
        }
      ]
    },
    {
      title: "Rating",
      elements: [
        {
          type: "rating",
          label: "Service Satisfaction",
          scale: 5,
          labels: { low: "Dissatisfied", high: "Very Satisfied" },
          required: true
        }
      ]
    }
  ]
}

v2 Changes: The question heading (### heading) is now stored in the label field in v2. In v1 it was description.

Reusable Parser

When parsing multiple Markdown files with the same options, use createParser:

import { createParser } from "md2form"
 
// Create parser instance once
const parser = createParser({ strict: false })
 
// Efficiently parse multiple Markdown files
const result1 = parser.parse(markdown1)
const result2 = parser.parse(markdown2)
const result3 = parser.parse(markdown3)

Running Samples

The project includes complete samples:

# Parse sample form and check results
bun run workspace

This will:

  1. Parse workspace/sample-form.md
  2. Output results to workspace/workspace.json
  3. Display form structure in the console

Using Diagnostics

In v2, errors are reported as diagnostics array instead of exceptions:

import { parseForm } from "md2form"
import type { Diagnostic } from "md2form"
 
const result = parseForm(markdown)
 
// Filter by severity
const errors = result.diagnostics.filter((d) => d.severity === "error")
const warnings = result.diagnostics.filter((d) => d.severity === "warning")
const infos = result.diagnostics.filter((d) => d.severity === "info")
 
// Detect specific issues by diagnostic code
const missingTypes = result.diagnostics.filter((d) => d.code === "MISSING_ELEMENT_TYPE")
 
// Format and display diagnostics
function printDiagnostics(diagnostics: Diagnostic[]): void {
  diagnostics.forEach((d) => {
    const loc = d.line ? ` (line ${d.line})` : ""
    console.log(`[${d.severity.toUpperCase()}] ${d.code}${loc}: ${d.message}`)
  })
}

Common diagnostic codes:

CodeSeverityDescription
MISSING_FORM_TITLEwarning# Title not found
MISSING_ELEMENT_TYPEwarningQuestion without #type specification
UNSUPPORTED_ELEMENT_TYPEwarningUnknown #type value
MISSING_REQUIRED_FIELDerrorRequired property (like #options) not set
INVALID_PROPERTY_VALUEwarningInvalid property value format
UNKNOWN_SETTING_KEYwarningUnknown key in frontmatter
INVALID_SETTING_VALUEwarningInvalid value in frontmatter

Using Type Safety

When using TypeScript, you can safely handle data using type definitions:

import { parseForm, isParsedDocument } from "md2form"
import type { FormDocument, ShortText, NumberField } from "md2form"
 
const result = parseForm(markdown, { strict: true })
 
if (!isParsedDocument(result)) {
  throw new Error("Parse failed")
}
 
const form: FormDocument = result.document
 
form.pages.forEach((page) => {
  page.elements.forEach((element) => {
    if (element.type === "short_text") {
      const shortText = element as ShortText
      console.log(shortText.placeholder) // Type-safe
    }
 
    if (element.type === "number") {
      const numberField = element as NumberField
      console.log(numberField.min, numberField.max) // Type-safe
    }
  })
})

v1 Compatibility

The v1 parseMarkdownToForm continues to work in v2, but is deprecated:

// ❌ Deprecated (v1 compatible)
import { parseMarkdownToForm } from "md2form"
const form = await parseMarkdownToForm(markdown) // Cannot get diagnostics
 
// ✅ Recommended (v2)
import { parseForm, isParsedDocument } from "md2form"
const result = parseForm(markdown)
const form = result.document

Next Steps

After understanding the basics, check these pages for more detailed information:

FAQ

Q: Can I use md2form with npm or yarn instead of Bun?

A: Yes, md2form works correctly with npm and yarn. However, Bun is recommended for development.

Q: Is parseForm async?

A: No, v2’s parseForm is a synchronous function. No await is needed. v1’s parseMarkdownToForm returned a Promise, but v2 changed to a synchronous API.

Q: Can I use it directly in the browser?

A: md2form is intended for Node.js/Bun environments. For browser use, use a bundler (webpack, Vite, etc.).

Q: How do I save parse results to a JSON file?

A: You can save it like this:

import { parseForm } from "md2form"
import { writeFileSync } from "fs"
 
const result = parseForm(markdown)
writeFileSync("form.json", JSON.stringify(result.document, null, 2))