CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-remarkable

High-performance Markdown parser with full CommonMark support, syntax extensions, and configurable rule systems.

Pending
Overview
Eval results
Files

rule-system.mddocs/

Rule System

Remarkable uses a sophisticated rule-based system for parsing markdown. The system consists of three main parsers (core, block, inline) each with their own set of configurable rules managed by Ruler instances.

Capabilities

Ruler Class

The Ruler class manages rule chains and execution order for parsing operations.

/**
 * Rule manager for organizing and controlling parsing rules
 */
class Ruler {
  /** Add rule to the end of chain */
  push(name: string, fn: RuleFunction, options?: RuleOptions): void;
  /** Add rule before specified rule */
  before(beforeName: string, ruleName: string, fn: RuleFunction, options?: RuleOptions): void;
  /** Add rule after specified rule */
  after(afterName: string, ruleName: string, fn: RuleFunction, options?: RuleOptions): void;
  /** Replace rule function */
  at(name: string, fn: RuleFunction, options?: RuleOptions): void;
  /** Enable rules by name */
  enable(names: string | string[], ignoreInvalid?: boolean): string[];
  /** Disable rules by name */
  disable(names: string | string[], ignoreInvalid?: boolean): string[];
  /** Get active rules for chain */
  getRules(chainName: string): RuleFunction[];
}

interface RuleOptions {
  /** Alternative rule names this rule can replace */
  alt?: string[];
}

type RuleFunction = (state: StateCore | StateBlock | StateInline) => boolean | void;

Usage Examples:

import { Remarkable } from "remarkable";

const md = new Remarkable();

// Add custom rule to block parser
md.block.ruler.push('custom_block', function(state, start, end, silent) {
  // Custom block parsing logic
  return false;
});

// Insert rule before 'paragraph'
md.block.ruler.before('paragraph', 'custom_before', customRule);

// Enable/disable rules
md.core.ruler.enable(['abbr', 'footnote_tail']);
md.inline.ruler.disable(['autolink']);

Core Parser Rules

Core processing rules that handle document-level operations.

interface CoreRules {
  /** Process block-level elements */
  block: RuleFunction;
  /** Process inline elements within blocks */
  inline: RuleFunction;
  /** Resolve link references */
  references: RuleFunction;
  /** Apply typographic replacements */
  replacements: RuleFunction;
  /** Apply smart quotes */
  smartquotes: RuleFunction;
  /** Process abbreviations */
  abbr: RuleFunction;
  /** Finalize abbreviation processing */
  abbr2: RuleFunction;
  /** Process footnote tails */
  footnote_tail: RuleFunction;
}

Default Core Rule Chain:

  1. block - Parse block elements
  2. inline - Parse inline elements
  3. references - Resolve references
  4. replacements - Typography replacements
  5. smartquotes - Smart quotes
  6. abbr2 - Finalize abbreviations
  7. footnote_tail - Process footnotes

Block Parser Rules

Rules for parsing block-level markdown elements.

interface BlockRules {
  /** Code blocks (indented) */
  code: RuleFunction;
  /** Fenced code blocks */
  fences: RuleFunction;
  /** Blockquotes */
  blockquote: RuleFunction;
  /** Horizontal rules */
  hr: RuleFunction;
  /** Lists (ordered and unordered) */
  list: RuleFunction;
  /** Footnote definitions */
  footnote: RuleFunction;
  /** ATX headings (# ## ###) */
  heading: RuleFunction;
  /** Setext headings (underlined) */
  lheading: RuleFunction;
  /** HTML blocks */
  htmlblock: RuleFunction;
  /** Tables */
  table: RuleFunction;
  /** Definition lists */
  deflist: RuleFunction;
  /** Paragraphs */
  paragraph: RuleFunction;
}

Default Block Rule Chain:

  1. code - Indented code blocks
  2. fences - Fenced code blocks
  3. blockquote - Blockquotes
  4. hr - Horizontal rules
  5. list - Lists
  6. footnote - Footnote definitions
  7. heading - ATX headings
  8. lheading - Setext headings
  9. htmlblock - HTML blocks
  10. table - Tables
  11. deflist - Definition lists
  12. paragraph - Paragraphs

Inline Parser Rules

Rules for parsing inline markdown elements.

interface InlineRules {
  /** Plain text */
  text: RuleFunction;
  /** Line breaks */
  newline: RuleFunction;
  /** Escape sequences */
  escape: RuleFunction;
  /** Code spans */
  backticks: RuleFunction;
  /** Strikethrough */
  del: RuleFunction;
  /** Insert/underline */
  ins: RuleFunction;
  /** Mark/highlight */
  mark: RuleFunction;
  /** Bold and italic */
  emphasis: RuleFunction;
  /** Subscript */
  sub: RuleFunction;
  /** Superscript */
  sup: RuleFunction;
  /** Links and images */
  links: RuleFunction;
  /** Inline footnotes */
  footnote_inline: RuleFunction;
  /** Footnote references */
  footnote_ref: RuleFunction;
  /** Autolinks <url> */
  autolink: RuleFunction;
  /** HTML tags */
  htmltag: RuleFunction;
  /** HTML entities */
  entity: RuleFunction;
}

Default Inline Rule Chain:

  1. text - Plain text
  2. newline - Line breaks
  3. escape - Escape sequences
  4. backticks - Code spans
  5. del - Strikethrough
  6. ins - Insert/underline
  7. mark - Mark/highlight
  8. emphasis - Bold/italic
  9. sub - Subscript
  10. sup - Superscript
  11. links - Links and images
  12. footnote_inline - Inline footnotes
  13. footnote_ref - Footnote references
  14. autolink - Autolinks
  15. htmltag - HTML tags
  16. entity - HTML entities

Custom Rule Development

Creating custom parsing rules for extending Remarkable functionality.

/**
 * State objects passed to rule functions
 */
interface StateCore {
  src: string;
  env: object;
  options: RemarkableOptions;
  tokens: Token[];
  inlineMode: boolean;
  inline: ParserInline;
  block: ParserBlock;
  renderer: Renderer;
}

interface StateBlock {
  src: string;
  md: Remarkable;
  env: object;
  tokens: Token[];
  bMarks: number[];
  eMarks: number[];
  tShift: number[];
  sCount: number[];
  blkIndent: number;
  line: number;
  lineMax: number;
  tight: boolean;
  parentType: string;
  ddIndent: number;
  getLines(begin: number, end: number, indent: number, keepLastLF: boolean): string;
  isEmpty(line: number): boolean;
  skipEmptyLines(from: number): number;
  skipSpaces(pos: number): number;
  skipChars(pos: number, code: number): number;
  skipCharsBack(pos: number, code: number, min: number): number;
  push(token: Token): void;
}

interface StateInline {
  src: string;
  env: object;
  md: Remarkable;
  tokens: Token[];
  pos: number;
  posMax: number;
  level: number;
  pending: string;
  pendingLevel: number;
  cache: object;
  delimiters: object[];
  push(token: Token): void;
  pushPending(): Token;
  scanDelims(start: number, canSplitWord: boolean): object;
}

Usage Examples:

// Custom block rule for special containers
function customContainer(state, start, end, silent) {
  const marker = ':::';
  let pos = state.bMarks[start] + state.tShift[start];
  let max = state.eMarks[start];
  
  // Check for marker
  if (pos + marker.length > max) return false;
  if (state.src.slice(pos, pos + marker.length) !== marker) return false;
  
  if (silent) return true;
  
  // Find closing marker
  let nextLine = start + 1;
  let found = false;
  
  while (nextLine < end) {
    pos = state.bMarks[nextLine] + state.tShift[nextLine];
    max = state.eMarks[nextLine];
    
    if (state.src.slice(pos, pos + marker.length) === marker) {
      found = true;
      break;
    }
    nextLine++;
  }
  
  if (!found) return false;
  
  // Create tokens
  const token_o = state.push({ type: 'container_open', level: state.level++ });
  const token_c = state.push({ type: 'container_close', level: --state.level });
  
  token_o.map = [start, nextLine];
  token_c.map = [nextLine, nextLine + 1];
  
  state.line = nextLine + 1;
  return true;
}

// Add the rule
md.block.ruler.before('paragraph', 'container', customContainer);

Rule Configuration

Configuring which rules are active through presets and manual configuration.

interface PresetConfig {
  options?: RemarkableOptions;
  components?: {
    core?: { rules: string[] };
    block?: { rules: string[] };
    inline?: { rules: string[] };
  };
}

Usage Examples:

// Enable specific rules only
md.configure({
  components: {
    core: { rules: ['block', 'inline'] },
    block: { rules: ['paragraph', 'heading', 'list'] },
    inline: { rules: ['text', 'emphasis', 'links'] }
  }
});

// Enable/disable individual rules
md.block.ruler.enable(['table', 'footnote']);
md.inline.ruler.disable(['autolink', 'htmltag']);

Install with Tessl CLI

npx tessl i tessl/npm-remarkable

docs

core-parser.md

index.md

linkify.md

rendering.md

rule-system.md

utilities.md

tile.json