diff --git a/dist/defaults.d.ts b/dist/defaults.d.ts new file mode 100644 index 0000000..7e519cd --- /dev/null +++ b/dist/defaults.d.ts @@ -0,0 +1,2 @@ +import * as tags from "./tags"; +export declare const defaultTags: tags.TagCollection; diff --git a/dist/js/defaults.js b/dist/defaults.js similarity index 92% rename from dist/js/defaults.js rename to dist/defaults.js index bf2801f..3679c67 100644 --- a/dist/js/defaults.js +++ b/dist/defaults.js @@ -45,10 +45,14 @@ exports.defaultTags = new tags.TagCollection([ new tags.HardBreak(), new tags.FencedCode(), new tags.HorizontalRule(), - new tags.Heading(), + new tags.Heading1(), + new tags.Heading2(), + new tags.Heading3(), + new tags.Heading4(), + new tags.Heading5(), + new tags.Heading6(), new tags.Blockquote(), new tags.OrderedList(), new tags.UnorderedList(), new tags.Table(), - new tags.Paragraph() ]); diff --git a/dist/hopdown.d.ts b/dist/hopdown.d.ts new file mode 100644 index 0000000..53c0614 --- /dev/null +++ b/dist/hopdown.d.ts @@ -0,0 +1,70 @@ +import type { Token, TagCollectionType } from './types'; +export declare function escapeHtml(source: string): string; +/** + * A configurable markdown <=> HTML converter. + * + * Examples: + * + * const hopdown = new HopDown(); + * + * const html = hopdown.toHTML('**bold**'); + * html === '

bold

'; + * + * const markdown = hopdown.toMarkdown(html); + * markdown === '**bold**'; + * + * const node = hopdown.toEditorNode('**bold**'); + * (node as HTMLElement).outerHTML === ( + * "
" + + * "**" + + * "bold" + + * "" + + * "
" + * ); + */ +export declare class HopDown { + tags: TagCollectionType; + private tokenizer; + constructor(tags?: TagCollectionType | null); + /** + * Convert a markdown string to tokens. + */ + tokenize(source: string): Token[]; + /** + * Convert an HTML string or a DocumentFragment to markdown. + */ + toMarkdown: (html: string | DocumentFragment) => string; + /** + * Convert a markdown string to a DocumentFragment consisting of wysiwyg editor nodes. + */ + toEditorNode: (markdown: string) => DocumentFragment; + toHTML: (markdown: string) => string; + /** + * Recursive entrypoint for converting markdown to html. + */ + inlineToHTML: (markdown: string) => string; + /** + * Recursive entrypoint for converting markdown to editor nodes. + */ + private inlineToEditorNodes; + /** + * Convert an HTML node to a markdown string. Used by toMarkdown(). + */ + private nodeToMarkdown; + /** + * Convert a token stream to editor nodes. + */ + private tokensToEditorNodes; + /** + * Convert a token stream to HTML. Matches open/close delimiter + * pairs and wraps their content in the appropriate HTML tags. + * Unmatched delimiters are emitted as literal text. + */ + private tokensToHTML; + /** + * Match open/close delimiter pairs in a token stream. Unmatched + * openers/closers are converted to text tokens so they render + * as literal characters. + */ + private pairDelimiters; +} diff --git a/dist/js/hopdown.js b/dist/hopdown.js similarity index 54% rename from dist/js/hopdown.js rename to dist/hopdown.js index b7b28b3..e542164 100644 --- a/dist/js/hopdown.js +++ b/dist/hopdown.js @@ -39,7 +39,6 @@ function escapeHtml(source) { */ class HopDown { tags; - referenceLinks = new Map(); tokenizer; constructor(tags) { this.tags = tags || defaults_1.defaultTags; @@ -49,13 +48,7 @@ class HopDown { * Convert a markdown string to tokens. */ tokenize(source) { - let text = source; - // Resolve reference links before tokenizing - text = this.resolveReferenceLinks(text); - // Normalize _ emphasis to * - text = this.normalizeUnderscores(text); - const tokens = this.tokenizer.tokenize(text); - return tokens; + return this.tokenizer.tokenize(source); } /** * Convert an HTML string or a DocumentFragment to markdown. @@ -70,58 +63,8 @@ class HopDown { else { md = this.nodeToMarkdown(html); } - return md.replace(/\n{3,}/g, '\n\n').trim(); - }; - /** - * Convert a markdown string to an HTML string. - */ - toHTML = (markdown) => { - const output = []; - const preprocessed = this.preprocessMarkdown(markdown); - if (!preprocessed.lines) { - return ''; - } - const blockTags = this.tags.ordered.filter(t => { - return t.isBlock && t.constructor.name !== 'Paragraph'; - }); - const pTag = this.tags.getByElementName('p'); - if (pTag === undefined) { - throw new Error("Cannot parse without a paragraph tag!"); - } - let lineIndex = 0; - while (lineIndex < preprocessed.lines.length) { - let context; - let matched = false; - let token; - for (const tag of blockTags) { - context = { - lines: preprocessed.lines, - index: lineIndex, - text: '', - offset: 0, - }; - token = tag.match(context, this.tags); - if (!token) { - continue; - } - output.push(tag.toHTML(token, this.inlineToHTML)); - lineIndex += token.consumed; - matched = true; - break; - } - if (matched) { - continue; - } - /* always check the paragraph tag last */ - token = pTag.match(context, this.tags); - if (token) { - output.push(pTag.toHTML(token, this.inlineToHTML)); - lineIndex += token.consumed; - continue; - } - lineIndex++; - } - return output.join('\n'); + md = md.replace(/^\n+/, '').replace(/\n+$/, ''); + return md; }; /** * Convert a markdown string to a DocumentFragment consisting of wysiwyg editor nodes. @@ -130,6 +73,9 @@ class HopDown { const tokens = this.tokenize(markdown); return this.tokensToEditorNodes(tokens); }; + toHTML = (markdown) => { + return '

' + this.inlineToHTML(markdown) + '

'; + }; /** * Recursive entrypoint for converting markdown to html. */ @@ -144,33 +90,6 @@ class HopDown { const tokens = this.tokenize(markdown); return this.tokensToEditorNodes(tokens); }; - /** - * Process a markdown string by splitting it into lines, stripping empty lines, and capturing reference links. - */ - preprocessMarkdown(markdown) { - const blankLine = /^\s*$/; - const refDefinition = /^\[(?