A sensible Markdown parser for javascript
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Extensible parsing rule system supporting Gruber (default) and Maruku dialects with capabilities for custom dialect creation.
Access to pre-defined parsing dialects with different feature sets.
/**
* Built-in dialect objects
* @property {Object} Markdown.dialects.Gruber - Default Gruber dialect following original markdown.pl
* @property {Object} Markdown.dialects.Maruku - Extended Maruku dialect with additional features
*/
var Markdown.dialects.Gruber;
var Markdown.dialects.Maruku;
/**
* Dialect structure
* @typedef {Object} Dialect
* @property {Object} block - Block-level processing rules
* @property {Object} inline - Inline processing rules
* @property {Array} block.__order__ - Processing order for block rules
* @property {string} inline.__patterns__ - Compiled regex patterns for inline rules
* @property {Function} inline.__call__ - Main inline processing function
*/Usage Examples:
var { Markdown } = require("markdown").markdown;
// Use built-in dialects
var md1 = new Markdown(Markdown.dialects.Gruber);
var md2 = new Markdown(Markdown.dialects.Maruku);
// Access dialect properties
console.log(Object.keys(Markdown.dialects.Gruber.block));
// ["atxHeader", "setextHeader", "code", "horizRule", "lists", "blockquote", "referenceDefn", "para"]
console.log(Object.keys(Markdown.dialects.Maruku.block));
// Includes additional: ["document_meta", "block_meta", "definition_list", "table"]Default dialect implementing John Gruber's original markdown specification.
Block-level Rules:
# Header)---, ***, ___)> text)Inline Rules:
**text** and __text__*text* and _text_[text](url) and [text][ref] and ![alt][ref]`code`<http://example.com> and <email@example.com>\* \_ etc.Extended dialect with additional markdown features beyond the original specification.
Additional Block-level Rules:
Key: Value){: .class #id})Term:\n: Definition)Additional Inline Rules:
{: .class #id} for inline elementsUsage Examples:
var { toHTML } = require("markdown");
// Gruber dialect (default)
var html1 = toHTML("# Header\n\n**Bold** text");
// Maruku dialect with metadata
var html2 = toHTML("# Header {: .title}\n\n**Bold** text", "Maruku");
// Maruku tables
var table = `
| Header 1 | Header 2 |
|----------|----------|
| Cell 1 | Cell 2 |
`;
var html3 = toHTML(table, "Maruku");Create custom dialects by extending existing ones.
/**
* Create dialect subclass with prototype inheritance
* @param {Object} d - Base dialect to extend
* @returns {Object} New dialect with inherited block and inline processors
*/
function Markdown.subclassDialect(d);Usage Examples:
var { Markdown } = require("markdown").markdown;
// Create custom dialect extending Gruber
var customDialect = Markdown.subclassDialect(Markdown.dialects.Gruber);
// Add custom block rule
customDialect.block.customBlock = function(block, next) {
if (block.match(/^CUSTOM:/)) {
return [["div", { class: "custom" }, block.substr(7)]];
}
return undefined;
};
// Add custom inline rule
customDialect.inline["@@"] = function(text) {
var match = text.match(/^@@(.*?)@@/);
if (match) {
return [match[0].length, ["span", { class: "highlight" }, match[1]]];
}
return [2, "@@"];
};
// Rebuild patterns and order
Markdown.buildBlockOrder(customDialect.block);
Markdown.buildInlinePatterns(customDialect.inline);
// Use custom dialect
var md = new Markdown(customDialect);
var html = md.toTree("CUSTOM: Special content\n\nNormal @@highlighted@@ text");Utility functions for constructing and configuring dialects.
/**
* Build processing order for block rules in dialect
* @param {Object} d - Dialect block rules object
* @modifies {Object} d - Adds __order__ property with rule names array
*/
function Markdown.buildBlockOrder(d);
/**
* Build regex patterns for inline rules in dialect
* @param {Object} d - Dialect inline rules object
* @modifies {Object} d - Adds __patterns__ property and wraps __call__ method
*/
function Markdown.buildInlinePatterns(d);Usage Examples:
var customDialect = {
block: {
customRule1: function() { /* ... */ },
customRule2: function() { /* ... */ },
para: function() { /* fallback */ }
},
inline: {
"**": function() { /* strong */ },
"*": function() { /* em */ },
__call__: function(text, patterns) { /* main processor */ }
}
};
// Build the dialect configuration
Markdown.buildBlockOrder(customDialect.block);
// Sets: customDialect.block.__order__ = ["customRule1", "customRule2", "para"]
Markdown.buildInlinePatterns(customDialect.inline);
// Sets: customDialect.inline.__patterns__ = "\\*\\*|\\*"
// Wraps: customDialect.inline.__call__ with pattern parameterCreating a comprehensive custom dialect with new syntax:
var { Markdown } = require("markdown").markdown;
// Create base dialect
var wikiDialect = Markdown.subclassDialect(Markdown.dialects.Gruber);
// Add wiki-style links: [[Page Name]]
wikiDialect.inline["[["] = function(text) {
var match = text.match(/^\[\[(.*?)\]\]/);
if (match) {
var pageName = match[1];
var href = "/wiki/" + pageName.replace(/\s+/g, "_");
return [
match[0].length,
["link", { href: href, class: "wiki-link" }, pageName]
];
}
return [2, "[["];
};
// Add note blocks: !!! Note: content
wikiDialect.block.noteBlock = function(block, next) {
var match = block.match(/^!!!\s*(\w+):\s*(.*)/);
if (match) {
var noteType = match[1].toLowerCase();
var content = match[2];
return [[
"div",
{ class: "note note-" + noteType },
["strong", {}, match[1] + ": "],
content
]];
}
return undefined;
};
// Configure the dialect
Markdown.buildBlockOrder(wikiDialect.block);
Markdown.buildInlinePatterns(wikiDialect.inline);
// Use the custom dialect
var md = new Markdown(wikiDialect);
var wikiText = `
# Wiki Page
See [[Other Page]] for details.
!!! Warning: This is important information.
Regular **markdown** still works.
`;
var html = md.toTree(wikiText);
// Produces HTML with wiki links and note blocks/**
* Dialect helper utilities
* @namespace Markdown.DialectHelpers
*/
var Markdown.DialectHelpers = {};
/**
* Process inline content until specific character
* @param {string} text - Text to process
* @param {string} want - Character to stop at
* @returns {Array|null} [consumed_length, nodes_array] or null if character not found
*/
Markdown.DialectHelpers.inline_until_char = function(text, want);Usage Examples:
// Used internally by link processing
var result = Markdown.DialectHelpers.inline_until_char("text with **bold**]", "]");
if (result) {
console.log("Consumed:", result[0]); // Characters consumed
console.log("Nodes:", result[1]); // Processed inline nodes
}