@takazudo/mindmap-parser
Pure markdown parser and serializer for mind map tree data. Parses markdown files with type: mindmap frontmatter into structured MindMapTree objects. All operations are immutable. Zero dependencies. The parser normalizes CRLF and lone-CR input to LF so an editor can safely round-trip files from any platform.
Main Exports
import {
parseMindMapMarkdown,
serializeMindMapMarkdown,
hasMindmapFrontmatter,
addNode,
removeNode,
moveNode,
updateNodeLabel,
findNode,
findParent,
slugify,
uniqueSlug,
validateMindmapMarkdown,
encodeMindMapLabel,
decodeMindMapLabel,
} from "@takazudo/mindmap-parser";
import type { MindMapNode, MindMapTree } from "@takazudo/mindmap-parser";Types
interface MindMapNode {
id: string; // slugified from label
label: string; // display text
children: MindMapNode[];
}
interface MindMapTree {
title: string; // from frontmatter or root heading
root: MindMapNode; // the root node (from # heading)
rawFrontmatter: Record<string, string>; // preserved for round-trip
}parseMindMapMarkdown()
Parses a markdown string into a MindMapTree. The format uses YAML frontmatter for metadata, a # heading for the root node, and indented bullet lists for branches:
const tree = parseMindMapMarkdown(`---
type: mindmap
title: Project Plan
---
# Project Plan
- Research
- User Interviews
- Market Analysis
- Development
- Backend
- Frontend
`);
// tree.title === "Project Plan"
// tree.root.label === "Project Plan"
// tree.root.children.length === 2Parsing rules:
The
#heading becomes the root nodeBullet items (
-) become child nodesIndentation (2 spaces per level) determines nesting depth
Node IDs are generated via
slugify()with uniqueness guarantees
Blank lines and prose outside the tree do not become nodes. Call validateMindmapMarkdown() before setup or persistence to report malformed tree structure rather than committing it.
Multiline labels and codec safety
Labels containing a newline are stored on one physical Markdown line using the declared mindmap-label-codec: base64url-v1 frontmatter field and an unpadded base64url ~mml1~ payload. The parser decodes the label before deriving its ID; the serializer preserves internal, leading, and trailing line breaks and spaces. The same encoding protects literal marker-looking labels from being interpreted as data. Unsupported codec declarations and malformed payloads remain visible as literal text and are validation errors, rather than silently changing the tree. Manual edits should normalize line endings to LF and use canonical base64url spelling.
serializeMindMapMarkdown()
Converts a MindMapTree back to markdown. Preserves the original frontmatter for round-trip fidelity.
const markdown = serializeMindMapMarkdown(tree);
// ---
// type: mindmap
// title: Project Plan
// ---
//
// # Project Plan
// - Research
// - User Interviews
// - Market Analysis
// ...hasMindmapFrontmatter()
Returns true if the markdown content has type: mindmap in its frontmatter. Used to detect mind map files.
hasMindmapFrontmatter("---\ntype: mindmap\n---\n# Root");
// true
hasMindmapFrontmatter("---\ntitle: Note\n---\n# Just a note");
// falsevalidateMindmapMarkdown(markdown) returns line-aware warnings and errors for missing frontmatter/root headings, skipped indentation levels, unsupported codecs, and malformed encoded labels. Setup flows should validate and preview the exact serialized candidate before writing; cancellation or a validation or write failure must leave the original source untouched.
Tree Operations
All operations are immutable — they return a new MindMapTree:
| Function | Description |
|---|---|
addNode(tree, parentId, label) | Add a new child node to a parent |
removeNode(tree, nodeId) | Remove a node and its children (cannot remove root) |
moveNode(tree, nodeId, newParentId, index?) | Reparent a node (cannot move root or into own descendant) |
updateNodeLabel(tree, nodeId, newLabel) | Rename a node (regenerates its ID; updates title if root) |
Examples
// Add a child under "research"
const updated = addNode(tree, "research", "Competitor Analysis");
// Remove a node
const pruned = removeNode(tree, "market-analysis");
// Move "frontend" under "research"
const moved = moveNode(tree, "frontend", "research");
// Rename a node
const renamed = updateNodeLabel(tree, "backend", "Server");Query Functions
| Function | Description |
|---|---|
findNode(tree, nodeId) | Find a node by ID. Returns null if not found |
findParent(tree, nodeId) | Find the parent of a node. Returns null if root or not found |
Utilities
slugify(text)— convert to kebab-case slug (preserves non-ASCII characters)uniqueSlug(title, existingIds)— generate unique slug with-2,-3suffix if needed
Dependencies
None (standalone package).