CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-milkdown--transformer

Bidirectional transformation library between markdown AST and ProseMirror document structures for the Milkdown editor

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

utilities.mddocs/

Utility System

The utility system provides base classes, type definitions, and utility functions for stack management, type safety, and integration with the remark and ProseMirror ecosystems.

Capabilities

Stack Management

Generic stack implementation for managing nested transformation contexts during parsing and serialization.

/**
 * The stack that is used to store the elements.
 * Generally, you don't need to use this class directly.
 * 
 * When using the stack, users can call stack.open to push a new element into the stack.
 * And use stack.push to push a node into the top element.
 * Then use stack.close to close the top element and pop it.
 * 
 * For example: stack.open(A).push(B).push(C).close() will generate a structure like A(B, C).
 */
class Stack<Node, Element extends StackElement<Node>> {
  protected elements: Element[];
  
  /**
   * Get the size of the stack.
   * @returns Number of elements in the stack
   */
  size(): number;
  
  /**
   * Get the top element of the stack.
   * @returns Top element or undefined if stack is empty
   */
  top(): Element | undefined;
  
  /**
   * Push a node into the top element.
   * @param node - Node to push into top element
   */
  push(node: Node): void;
  
  /**
   * Push a new element.
   * @param node - Element to push onto stack
   */
  open(node: Element): void;
  
  /**
   * Close the top element and pop it.
   * @returns The popped element
   * @throws Error if stack is empty
   */
  close(): Element;
}

/**
 * The element of the stack, which holds an array of nodes.
 */
abstract class StackElement<Node> {
  /**
   * A method that can push a node into the element.
   * @param node - Node to push
   * @param rest - Additional nodes to push
   */
  abstract push(node: Node, ...rest: Node[]): void;
}

Usage Examples:

import { Stack, StackElement } from "@milkdown/transformer";

// Custom stack element implementation
class MyStackElement extends StackElement<string> {
  private items: string[] = [];
  
  push(node: string, ...rest: string[]): void {
    this.items.push(node, ...rest);
  }
  
  getItems(): string[] {
    return this.items;
  }
}

// Using the stack
const stack = new Stack<string, MyStackElement>();
const element = new MyStackElement();

stack.open(element);
stack.push("item1");
stack.push("item2");
const closedElement = stack.close();

console.log(closedElement.getItems()); // ["item1", "item2"]

Type Definitions

Core type definitions for markdown AST, JSON data, and remark integration.

/**
 * The universal type of a node in mdast.
 */
type MarkdownNode = Node & {
  children?: MarkdownNode[];
  [x: string]: unknown;
};

/**
 * JSON value type for representing arbitrary JSON data.
 */
type JSONValue = 
  | string 
  | number 
  | boolean 
  | null 
  | JSONValue[] 
  | { [key: string]: JSONValue };

/**
 * JSON record type for object properties.
 */
type JSONRecord = Record<string, JSONValue>;

/**
 * The type of remark instance.
 */
type RemarkParser = ReturnType<typeof remark>;

/**
 * Node type for remark transformer parameters.
 */
type Node = Parameters<Transformer>[0];

/**
 * Root type for remark stringify parameters.
 */
type Root = Parameters<(typeof remark)['stringify']>[0];

Plugin System Types

Types for extending functionality through remark plugins.

/**
 * Raw plugin type for remark integration.
 */
type RemarkPluginRaw<T> = Plugin<[T], Root>;

/**
 * The universal type of a remark plugin.
 */
interface RemarkPlugin<T = Record<string, unknown>> {
  plugin: Plugin<[T], Root>;
  options: T;
}

Plugin Usage Examples:

import { RemarkPlugin, RemarkParser } from "@milkdown/transformer";
import { remark } from "remark";

// Define a custom plugin
const myPlugin: RemarkPlugin<{ prefix: string }> = {
  plugin: (options) => (tree, file) => {
    // Transform tree based on options
    if (options.prefix) {
      // Add prefix to all text nodes
    }
  },
  options: { prefix: ">> " }
};

// Use plugin with remark
const remarkInstance: RemarkParser = remark()
  .use(myPlugin.plugin, myPlugin.options);

// Plugin with type safety
const typedPlugin: RemarkPlugin<{
  enableFeature: boolean;
  maxDepth: number;
}> = {
  plugin: ({ enableFeature, maxDepth }) => (tree) => {
    if (enableFeature) {
      // Process tree with maxDepth limit
    }
  },
  options: {
    enableFeature: true,
    maxDepth: 5
  }
};

Utility Functions and Constants

Helper utilities for working with markdown and ProseMirror data structures.

/**
 * Type guard to check if a node has text content.
 */
function hasText(node: Node): node is Node & { text: string };

/**
 * Type guard to check if an object is a ProseMirror Fragment.
 */
function isFragment(x: Node | Fragment): x is Fragment;

Advanced Type Usage Examples:

import { MarkdownNode, JSONRecord } from "@milkdown/transformer";

// Working with markdown nodes
function processMarkdownNode(node: MarkdownNode): void {
  console.log(`Processing ${node.type}`);
  
  if (node.children) {
    node.children.forEach(child => {
      processMarkdownNode(child);
    });
  }
  
  // Access arbitrary properties safely
  if (node.depth && typeof node.depth === 'number') {
    console.log(`Heading depth: ${node.depth}`);
  }
}

// Working with JSON records
function createNodeProps(data: any): JSONRecord {
  const props: JSONRecord = {};
  
  if (typeof data.id === 'string') props.id = data.id;
  if (typeof data.level === 'number') props.level = data.level;
  if (Array.isArray(data.classes)) props.classes = data.classes;
  
  return props;
}

// Type-safe node creation
interface CustomMarkdownNode extends MarkdownNode {
  type: 'custom';
  customProp: string;
}

function isCustomNode(node: MarkdownNode): node is CustomMarkdownNode {
  return node.type === 'custom' && typeof node.customProp === 'string';
}

Error Handling Utilities

While not directly exported, the utility system integrates with @milkdown/exception for consistent error handling.

// Error types that may be thrown during transformation
interface TransformationError extends Error {
  name: 'TransformationError';
  node?: MarkdownNode | Node;
  context?: string;
}

interface ParserMatchError extends Error {
  name: 'ParserMatchError';
  node: MarkdownNode;
}

interface SerializerMatchError extends Error {
  name: 'SerializerMatchError'; 
  nodeType: NodeType | MarkType;
}

Error Handling Examples:

import { ParserState } from "@milkdown/transformer";

try {
  const parser = ParserState.create(schema, remark());
  const doc = parser(markdownText);
} catch (error) {
  if (error.name === 'ParserMatchError') {
    console.error(`No parser found for node type: ${error.node.type}`);
  } else if (error.name === 'SerializerMatchError') {
    console.error(`No serializer found for node type: ${error.nodeType.name}`);
  } else {
    console.error('Unexpected transformation error:', error);
  }
}

docs

index.md

parser.md

serializer.md

specifications.md

utilities.md

tile.json