marktex.js
Advanced tools
Comparing version 0.1.3 to 0.2.0
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const parser_1 = require("./parser"); | ||
const renderer_1 = require("./renderer"); | ||
const source_1 = require("./source"); | ||
Object.defineProperty(exports, "__esModule", {value: true}); | ||
const parser_1 = require("./parser/parser"); | ||
exports.Parser = parser_1.Parser; | ||
const renderer_1 = require("./renderer/renderer"); | ||
exports.Renderer = renderer_1.Renderer; | ||
const stream_1 = require("./lib/stream"); | ||
exports.StringStream = stream_1.StringStream; | ||
const rules_1 = require("./rules"); | ||
@@ -10,15 +13,16 @@ // noinspection JSUnusedGlobalSymbols | ||
author: "Myriad-Dreamin", | ||
Parser(options) { | ||
newParser(options) { | ||
return new parser_1.Parser(options === null || options === void 0 ? void 0 : options.parserOptions); | ||
}, | ||
Renderer(options) { | ||
return new renderer_1.Renderer(myriad.Parser(options), options === null || options === void 0 ? void 0 : options.rendererOptions); | ||
newRenderer(options) { | ||
return new renderer_1.Renderer(myriad.newParser(options), options === null || options === void 0 ? void 0 : options.rendererOptions); | ||
}, | ||
StringStream(str) { | ||
return new source_1.StringStream(str); | ||
newStringStream(str) { | ||
return new stream_1.StringStream(str); | ||
}, | ||
newInlineRules: rules_1.newInlineRules, | ||
newBlockRules: rules_1.newBlockRules, | ||
newRules: rules_1.newRules, | ||
newInlineRules: rules_1.newInlineRules, newBlockRules: rules_1.newBlockRules, newRules: rules_1.newRules, | ||
Parser: parser_1.Parser, Renderer: renderer_1.Renderer, StringStream: stream_1.StringStream, | ||
}; | ||
exports.myriad = myriad; | ||
// noinspection JSUnusedGlobalSymbols | ||
exports.default = myriad; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const token_1 = require("./token"); | ||
const source_1 = require("./source"); | ||
class RegExpWithTagName extends RegExp { | ||
constructor(r, gIndex) { | ||
super(r); | ||
this.gIndex = gIndex; | ||
} | ||
// noinspection JSUnusedGlobalSymbols | ||
getTagName(g) { | ||
return g[this.gIndex]; | ||
} | ||
} | ||
exports.RegExpWithTagName = RegExpWithTagName; | ||
class OpenTagRegExp extends RegExpWithTagName { | ||
constructor(r, gIndex, gOpenIndex) { | ||
super(r, gIndex); | ||
this.gOpenIndex = gOpenIndex; | ||
} | ||
isSingleton(g) { | ||
return g[this.gOpenIndex] !== '/'; | ||
} | ||
} | ||
exports.OpenTagRegExp = OpenTagRegExp; | ||
function forward(s, capturing) { | ||
s.forward(capturing[0].length); | ||
} | ||
const validTags = function () { | ||
let validTags = /^<open_tag>|<close_tag>|<comment>|<processing_instruction>|<declaration>|<cdata>/.source; | ||
let open_tag = /(?:<(<tag_name>)(?:\s*<attribute>)*\s*(\/?)>)/.source; | ||
let close_tag = /(?:<\/(<tag_name>)\s*>)/.source; | ||
let comment = /(?:<!--(?:[^>-]|-[^>])(?:[^-]|-?[^-])*[^-]-->)/; | ||
let processing_instruction = /(?:<?(?:(?!\?>).)*\?>)/; | ||
let declaration = /(?:<![A-Z]+[^>]*>)/; | ||
let cdata = /(?:<!\[CDATA(?:(?!]]>)\s\S)*]]>)/; | ||
let tag_name = /(?:[a-zA-Z][a-zA-Z0-9-]*)/; | ||
let attribute = /(?:<attribute_name>(?:\s*=\s*<attribute_value>)?)/.source; | ||
let attribute_name = /(?:[a-zA-Z_:][a-zA-Z0-9_.:-]*)/; | ||
let attribute_value = /(?:[^/s'"=<>`]|'[^']*'|"[^"]*")/; | ||
attribute = attribute | ||
.replace('<attribute_name>', attribute_name.source) | ||
.replace('<attribute_value>', attribute_value.source); | ||
close_tag = close_tag.replace('<tag_name>', tag_name.source); | ||
open_tag = open_tag | ||
.replace('<tag_name>', tag_name.source) | ||
.replace('<attribute>', attribute); | ||
validTags = validTags | ||
.replace('<open_tag>', open_tag) | ||
.replace('<close_tag>', close_tag) | ||
.replace('<comment>', comment.source) | ||
.replace('<processing_instruction>', processing_instruction.source) | ||
.replace('<declaration>', declaration.source) | ||
.replace('<cdata>', cdata.source); | ||
return { | ||
validTags: new RegExp(validTags), | ||
open_tag: new OpenTagRegExp(new RegExp(open_tag), 1, 2), | ||
close_tag: new RegExpWithTagName(new RegExp(close_tag), 1), | ||
comment, | ||
others: [processing_instruction, declaration, cdata], | ||
Object.defineProperty(exports, "__esModule", {value: true}); | ||
const std_1 = require("./rules/std"); | ||
const latex_1 = require("./rules/latex"); | ||
const gfm_1 = require("./rules/gfm"); | ||
function validTags() { | ||
let _validTags = undefined; | ||
return () => { | ||
if (_validTags !== undefined) { | ||
return _validTags; | ||
} | ||
let validTags = /^<open_tag>|<close_tag>|<comment>|<processing_instruction>|<declaration>|<cdata>/.source; | ||
let open_tag = /(?:<(<tag_name>)(?:\s*<attribute>)*\s*(\/?)>)/.source; | ||
let close_tag = /(?:<\/(<tag_name>)\s*>)/.source; | ||
let comment = /(?:<!--(?:[^>-]|-[^>])(?:[^-]|-?[^-])*[^-]-->)/; | ||
let processing_instruction = /(?:<?(?:(?!\?>).)*\?>)/; | ||
let declaration = /(?:<![A-Z]+[^>]*>)/; | ||
let cdata = /(?:<!\[CDATA(?:(?!]]>)\s\S)*]]>)/; | ||
let tag_name = /(?:[a-zA-Z][a-zA-Z0-9-]*)/; | ||
let attribute = /(?:<attribute_name>(?:\s*=\s*<attribute_value>)?)/.source; | ||
let attribute_name = /(?:[a-zA-Z_:][a-zA-Z0-9_.:-]*)/; | ||
let attribute_value = /(?:[^/s'"=<>`]|'[^']*'|"[^"]*")/; | ||
attribute = attribute | ||
.replace('<attribute_name>', attribute_name.source) | ||
.replace('<attribute_value>', attribute_value.source); | ||
close_tag = close_tag.replace('<tag_name>', tag_name.source); | ||
open_tag = open_tag | ||
.replace('<tag_name>', tag_name.source) | ||
.replace('<attribute>', attribute); | ||
validTags = validTags | ||
.replace('<open_tag>', open_tag) | ||
.replace('<close_tag>', close_tag) | ||
.replace('<comment>', comment.source) | ||
.replace('<processing_instruction>', processing_instruction.source) | ||
.replace('<declaration>', declaration.source) | ||
.replace('<cdata>', cdata.source); | ||
_validTags = { | ||
validTags: new RegExp(validTags), | ||
open_tag: new std_1.OpenTagRegExp(new RegExp(open_tag), 1, 2), | ||
close_tag: new std_1.RegExpWithTagName(new RegExp(close_tag), 1), | ||
comment, | ||
others: [processing_instruction, declaration, cdata], | ||
}; | ||
return _validTags; | ||
}; | ||
}(); | ||
exports.validTags = validTags; | ||
class NewLineRule { | ||
constructor() { | ||
this.name = "NewLine"; | ||
this.description = "Standard Markdown Block Rule"; | ||
this.regex = /^\n+/; | ||
} | ||
match(s, _) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.NewLine(capturing[0]); | ||
} | ||
; | ||
} | ||
exports.NewLineRule = NewLineRule; | ||
class ParagraphRule { | ||
constructor(skipLaTeXBlock) { | ||
this.name = "Paragraph"; | ||
this.description = "Standard Markdown Block Rule"; | ||
this.skipLaTeXBlock = skipLaTeXBlock; | ||
} | ||
match(s, ctx) { | ||
let lastChar = 'a', i = 0; | ||
if (s.source[0] == '\n') { | ||
return undefined; | ||
} | ||
if (this.skipLaTeXBlock) { | ||
for (; i < s.source.length; i++) { | ||
if (lastChar === s.source[i] && (lastChar === '$' || lastChar === '\n')) { | ||
i--; | ||
break; | ||
} | ||
if (lastChar === '\n' && s.source[i] === '\\') { | ||
if (i + 1 < s.source.length && | ||
(('a' <= s.source[i + 1] && s.source[i + 1] <= 'z') || ('A' <= s.source[i + 1] && s.source[i + 1] <= 'Z'))) { | ||
i--; | ||
break; | ||
} | ||
} else if (lastChar === '\\' && s.source[i] !== '\n') { | ||
lastChar = 'a'; | ||
} else { | ||
lastChar = s.source[i]; | ||
} | ||
} | ||
} else { | ||
for (; i < s.source.length; i++) { | ||
if (lastChar === s.source[i] && '$\n'.includes(lastChar)) { | ||
i--; | ||
break; | ||
} | ||
if (lastChar === '\\' && s.source[i] !== '\n') { | ||
lastChar = 'a'; | ||
} else { | ||
lastChar = s.source[i]; | ||
} | ||
} | ||
} | ||
if (!i) { | ||
return undefined; | ||
} | ||
let capturing = s.source.slice(0, i); | ||
s.forward(i); | ||
return new token_1.Paragraph(ctx.parseInlineElements(new source_1.StringStream(capturing))); | ||
} | ||
; | ||
} | ||
exports.ParagraphRule = ParagraphRule; | ||
/* *+- */ | ||
/* 1. */ | ||
class ListBlockRule { | ||
constructor() { | ||
this.name = "ListBlock"; | ||
this.description = "Standard Markdown Block Rule"; | ||
} | ||
match(s, ctx) { | ||
let ordered; | ||
if ("*+-".includes(s.source[0])) { | ||
ordered = false; | ||
} | ||
else if ('0' <= s.source[0] && s.source[0] <= '9') { | ||
ordered = true; | ||
} | ||
else { | ||
return undefined; | ||
} | ||
return ListBlockRule.matchBlock(new token_1.ListBlock(ordered), s, ctx); | ||
} | ||
; | ||
static matchBlock(l, s, ctx) { | ||
let nextMarker; | ||
nextMarker = l.lookAhead(s); | ||
if (!nextMarker) { | ||
return undefined; | ||
} | ||
let blockContent, marker, lastSeparated = false; | ||
for (blockContent = '', marker = nextMarker, nextMarker = undefined; marker !== undefined; blockContent = '', marker = nextMarker, nextMarker = undefined) { | ||
do { | ||
let capturing = ListBlockRule.listBlockRegex.exec(s.source); | ||
if (capturing === null) { | ||
throw new Error("match block failed"); | ||
} | ||
forward(s, capturing); | ||
blockContent += capturing[0]; | ||
} while (l.lookAhead0(s) && (nextMarker = l.lookAhead(s)) === undefined); | ||
let element = new token_1.ListElement(marker, ctx.parseBlockElements(new source_1.StringStream(blockContent.replace(ListBlockRule.replaceRegex, '')))); | ||
if (!nextMarker) { | ||
let capturing = ListBlockRule.blankRegex.exec(s.source); | ||
if (capturing !== null) { | ||
forward(s, capturing); | ||
element.blankSeparated = true; | ||
lastSeparated = true; | ||
if (l.lookAhead0(s)) { | ||
nextMarker = l.lookAhead(s); | ||
} | ||
} | ||
else { | ||
element.blankSeparated = lastSeparated; | ||
lastSeparated = false; | ||
} | ||
} | ||
l.listElements.push(element); | ||
} | ||
return l; | ||
} | ||
} | ||
exports.ListBlockRule = ListBlockRule; | ||
ListBlockRule.blankRegex = /^[\t\v\f ]*\n/; | ||
ListBlockRule.listBlockRegex = /^([^\n]*(?:\n|$)(?:(?=[^\n0-9*+-])[^\n]*(?:\n|$))*)/; | ||
ListBlockRule.replaceRegex = /^(?: {4}|\t)/gm; | ||
class QuotesRule { | ||
constructor() { | ||
this.name = "Quotes"; | ||
this.description = "Standard Markdown Block Rule"; | ||
this.regex = /^( *>[^\n]*(?:\n[^\n]+)*\n?)+/; | ||
} | ||
match(s, ctx) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.Quotes(ctx.parseBlockElements(new source_1.StringStream(capturing[0].replace(/^ *> ?/gm, '')))); | ||
} | ||
; | ||
} | ||
exports.QuotesRule = QuotesRule; | ||
class HorizontalRule { | ||
constructor() { | ||
this.name = "Horizontal"; | ||
this.description = "Standard Markdown Block Rule"; | ||
this.regex = /^(?:(?:\*[\r\t ]*){3,}|(?:-[\r\t ]*){3,})(?:\n|$)/; | ||
} | ||
match(s, _) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.Horizontal(); | ||
} | ||
; | ||
} | ||
exports.HorizontalRule = HorizontalRule; | ||
class CodeBlockRule { | ||
constructor() { | ||
this.name = "CodeBlock"; | ||
this.description = "Standard Markdown Block Rule"; | ||
} | ||
match(s, _) { | ||
let capturing = CodeBlockRule.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.CodeBlock(capturing[0].replace(/^(?: {4}|\t)/gm, '')); | ||
} | ||
; | ||
} | ||
exports.CodeBlockRule = CodeBlockRule; | ||
CodeBlockRule.regex = /^((?: {4}|\t)[^\n]+(\n|$))+/; | ||
class HeaderBlockRule { | ||
constructor() { | ||
this.name = "HeaderBlock"; | ||
this.description = "Standard Markdown Block Rule"; | ||
this.atxRegex = /^(#{1,6}) ([^\n]*?)#*(?:\n|$)/; | ||
this.setextRegex = /^([^\n]+)\n=+(?:\n|$)/; | ||
} | ||
match(s, ctx) { | ||
return this.matchATX(s, ctx) || this.matchSetext(s, ctx); | ||
} | ||
; | ||
matchATX(s, ctx) { | ||
let capturing = this.atxRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.HeaderBlock(ctx.parseInlineElements(new source_1.StringStream(capturing[2])), capturing[1].length); | ||
} | ||
matchSetext(s, ctx) { | ||
let capturing = this.setextRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.HeaderBlock(ctx.parseInlineElements(new source_1.StringStream(capturing[1])), 1); | ||
} | ||
} | ||
exports.HeaderBlockRule = HeaderBlockRule; | ||
class LinkDefinitionRule { | ||
constructor() { | ||
this.name = "LinkDefinition"; | ||
this.description = "Standard Markdown Block Rule"; | ||
this.regex = /^ *\[([^\]]+)]: *<?([^\s>]+)>?(?: +["'(]([^\n]*)["')])? *(?:\n|$)/; | ||
} | ||
match(s, _) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.LinkDefinition(capturing[1], capturing[2], capturing[3]); | ||
} | ||
; | ||
} | ||
exports.LinkDefinitionRule = LinkDefinitionRule; | ||
class HTMLBlockRule { | ||
constructor(validTags) { | ||
this.name = "HTMLBlock"; | ||
this.description = "Standard Markdown Block Rule"; | ||
this.validTags = validTags; | ||
} | ||
// public readonly singleTonRegex: RegExp = /^/; | ||
// public readonly stdRegex: RegExp = /^/; | ||
match(s, ctx) { | ||
let ot = this.validTags.open_tag; | ||
let capturing = ot.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
if (ot.isSingleton(capturing)) { | ||
return new token_1.HTMLBlock(capturing[0]); | ||
} | ||
return undefined; | ||
} | ||
; | ||
} | ||
exports.HTMLBlockRule = HTMLBlockRule; | ||
function _braceMatch(s, l, r) { | ||
if (s.source[0] == l) { | ||
let c = 0; | ||
for (let j = 0; j < s.source.length; j++) { | ||
if (s.source[j] == l) { | ||
c++; | ||
} | ||
else if (s.source[j] == r) { | ||
c--; | ||
if (c === 0) { | ||
let res = s.source.slice(0, j + 1); | ||
s.forward(j + 1); | ||
return res; | ||
} | ||
} | ||
} | ||
let res = s.source; | ||
s.forward(s.source.length); | ||
return res; | ||
} | ||
return ''; | ||
} | ||
class InlineLatexCommandRule { | ||
constructor() { | ||
this.name = "InlineLatexCommand"; | ||
this.description = "Latex Inline Rule"; | ||
} | ||
match(s, _) { | ||
let capturing = InlineLatexCommandRule.cmdNameRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.InlinePlain(capturing[0] + this.braceMatch(s)); | ||
} | ||
braceMatch(s) { | ||
let res = ''; | ||
for (let i = 0; !s.eof; i = 0) { | ||
for (; !s.eof && ' \n\t\v\f\r'.includes(s.source[i]); i++) { | ||
} | ||
if (!'[{'.includes(s.source[i])) { | ||
return res; | ||
} | ||
if (i) { | ||
res += s.source.slice(0, i); | ||
s.forward(i); | ||
} | ||
res += _braceMatch(s, '{', '}'); | ||
res += _braceMatch(s, '[', ']'); | ||
} | ||
return res; | ||
} | ||
} | ||
exports.InlineLatexCommandRule = InlineLatexCommandRule; | ||
InlineLatexCommandRule.cmdNameRegex = /^\\([a-zA-Z_]\w*)/; | ||
class LatexBlockRule { | ||
constructor() { | ||
this.name = "LatexBlock"; | ||
this.description = "Latex Inline Rule"; | ||
} | ||
match(s, _) { | ||
let capturing = InlineLatexCommandRule.cmdNameRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.LateXBlock(capturing[0] + this.braceMatch(s)); | ||
} | ||
braceMatch(s) { | ||
let res = ''; | ||
for (let i = 0; !s.eof; i = 0) { | ||
for (; !s.eof && ' \t\v\f\r'.includes(s.source[i]); i++) { | ||
} | ||
if (s.source[i] == '\n') { | ||
i++; | ||
if (s.source[i] == '\n') { | ||
s.forward(i); | ||
return res; | ||
} | ||
} | ||
if (!'[{'.includes(s.source[i])) { | ||
return res; | ||
} | ||
if (i) { | ||
res += s.source.slice(0, i); | ||
s.forward(i); | ||
} | ||
res += _braceMatch(s, '[', ']'); | ||
res += _braceMatch(s, '{', '}'); | ||
} | ||
return res; | ||
} | ||
} | ||
exports.LatexBlockRule = LatexBlockRule; | ||
LatexBlockRule.cmdNameRegex = /^\\([a-zA-Z_]\w*)/; | ||
class InlinePlainExceptSpecialMarksRule { | ||
constructor() { | ||
this.name = "InlinePlainExceptSpecialMarks"; | ||
this.description = "Standard Markdown Inline Rule"; | ||
this.regex = /^(?:\\[<`_*\[$\\]|[^<`_*\[$\\])+/; | ||
} | ||
match(s, _) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.InlinePlain(capturing[0]); | ||
} | ||
; | ||
} | ||
exports.InlinePlainExceptSpecialMarksRule = InlinePlainExceptSpecialMarksRule; | ||
class InlinePlainRule { | ||
constructor() { | ||
this.name = "InlinePlain"; | ||
this.description = "Standard Markdown Inline Rule"; | ||
this.regex = /^(?:[<`_*\[$\\](?:\\[<`_*\[$\\]|[^<`_*\[$\\])*|(?:\\[<`_*\[$\\]|[^<`_*\[$\\])+)/; | ||
} | ||
match(s, _) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.InlinePlain(capturing[0]); | ||
} | ||
; | ||
} | ||
exports.InlinePlainRule = InlinePlainRule; | ||
class InlineMathRule { | ||
constructor() { | ||
this.name = "InlineMath"; | ||
this.description = "Markdown Inline Rule"; | ||
this.regex = /^\$((?:[^$]|\\\$)+)\$/; | ||
} | ||
match(s, _) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.MathBlock(capturing[1], true); | ||
} | ||
; | ||
} | ||
exports.InlineMathRule = InlineMathRule; | ||
class MathBlockRule { | ||
constructor() { | ||
this.name = "MathBlock"; | ||
this.description = "Markdown Block Rule"; | ||
this.regex = /^\$\$((?:[^$]|\\\$)+)\$\$/; | ||
} | ||
match(s, _) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.MathBlock(capturing[1], false); | ||
} | ||
; | ||
} | ||
exports.MathBlockRule = MathBlockRule; | ||
class LinkOrImageRule { | ||
constructor() { | ||
this.name = "Link"; | ||
this.description = "Standard Markdown Inline Rule"; | ||
this.regex = /^(!?)\[((?:\[[^\]]*]|[^\[\]]|](?=[^\[]*]))*)]\(\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*\)/; | ||
this.refRegex = /^(!?)\[((?:\[[^\]]*]|[^\[\]]|](?=[^\[]*]))*)]\s*\[([^\]]*)]/; | ||
} | ||
match(s, ctx) { | ||
return this.matchInline(s, ctx) || this.matchRef(s, ctx); | ||
} | ||
; | ||
matchInline(s, _) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
if (capturing[1] !== '') { | ||
return new token_1.ImageLink(capturing[2], capturing[3], true, capturing[4]); | ||
} | ||
else { | ||
return new token_1.Link(capturing[2], capturing[3], true, capturing[4]); | ||
} | ||
} | ||
matchRef(s, _) { | ||
let capturing = this.refRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
if (capturing[1] !== '') { | ||
return new token_1.ImageLink(capturing[2], capturing[3], false); | ||
} | ||
else { | ||
return new token_1.Link(capturing[2], capturing[3], false); | ||
} | ||
} | ||
} | ||
exports.LinkOrImageRule = LinkOrImageRule; | ||
class EmphasisRule { | ||
constructor() { | ||
this.name = "Emphasis"; | ||
this.description = "Standard Markdown Inline Rule"; | ||
this.regex = /^(?:(_{1,2})([^_]+?)(_{1,2})|(\*{1,2})([^*]+?)(\*{1,2}))/; | ||
} | ||
match(s, _) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
let l = capturing[1] || capturing[4], r = capturing[3] || capturing[6]; | ||
if (l !== r) { | ||
if (l.length < r.length) { | ||
s.forward(capturing[0].length - 1); | ||
return new token_1.Emphasis(capturing[2] || capturing[5], l.length); | ||
} | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.Emphasis(capturing[2] || capturing[5], l.length); | ||
} | ||
; | ||
} | ||
exports.EmphasisRule = EmphasisRule; | ||
class InlineCodeRule { | ||
constructor() { | ||
this.name = "InlineCode"; | ||
this.description = "Standard Markdown Inline Rule"; | ||
this.regex = /^(?:``([^`\n\r\u2028\u2029](?:`?[^`\n\r\u2028\u2029])*)``|`([^`\n\r\u2028\u2029]+?)`)/; | ||
} | ||
match(s, _) { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new token_1.InlineCode(capturing[1] || capturing[2]); | ||
} | ||
; | ||
} | ||
exports.InlineCodeRule = InlineCodeRule; | ||
class GFMFencedCodeBlockRule { | ||
constructor() { | ||
this.name = "GFMCodeBlock"; | ||
this.description = "GFM Markdown Block Rule"; | ||
} | ||
match(s, _) { | ||
let capturing = GFMFencedCodeBlockRule.backtickRegex.exec(s.source); | ||
if (capturing === null) { | ||
capturing = GFMFencedCodeBlockRule.tildeRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
} | ||
forward(s, capturing); | ||
return new token_1.CodeBlock(capturing[3], capturing[2]); | ||
} | ||
; | ||
} | ||
exports.GFMFencedCodeBlockRule = GFMFencedCodeBlockRule; | ||
GFMFencedCodeBlockRule.backtickRegex = /^(`{3,}) *([^`\s]+)?[^`\n]*(?:\n|$)([\s\S]*?)(?:\1`*|$)/; | ||
GFMFencedCodeBlockRule.tildeRegex = /^(~{3,}) *([^~\s]+)?.*(?:\n|$)([\s\S]*?)(?:\1~*|$)/; | ||
exports.inlineRules = newInlineRules(); | ||
exports.blockRules = newBlockRules(); | ||
let validTagsClosure; | ||
// noinspection JSUnusedGlobalSymbols | ||
function newBlockRules(opts) { | ||
let rules0 = [ | ||
new NewLineRule(), | ||
new CodeBlockRule(), | ||
new LinkDefinitionRule(), | ||
new MathBlockRule(), | ||
new LatexBlockRule(), | ||
new std_1.NewLineRule(), | ||
new std_1.CodeBlockRule(), | ||
new std_1.LinkDefinitionRule(), | ||
new latex_1.MathBlockRule(), | ||
new latex_1.LatexBlockRule(), | ||
]; | ||
let rules1 = [ | ||
new QuotesRule(), | ||
new HeaderBlockRule(), | ||
new HorizontalRule(), | ||
new ListBlockRule(), | ||
new std_1.QuotesRule(), | ||
new std_1.HeaderBlockRule(), | ||
new std_1.HorizontalRule(), | ||
new std_1.ListBlockRule(), | ||
]; | ||
let rules2 = [ | ||
new ParagraphRule((opts === null || opts === void 0 ? void 0 : opts.enableLaTeX) || false), | ||
new latex_1.ParagraphRule({ | ||
skipLaTeXBlock: (opts === null || opts === void 0 ? void 0 : opts.enableLaTeX) || false, | ||
skipMathBlock: true | ||
}), | ||
]; | ||
// default enable | ||
if ((opts === null || opts === void 0 ? void 0 : opts.enableGFMRules) !== false) { | ||
rules0.push(new GFMFencedCodeBlockRule()); | ||
rules0.push(new gfm_1.GFMFencedCodeBlockRule()); | ||
} | ||
if (opts === null || opts === void 0 ? void 0 : opts.enableHtml) { | ||
rules0.push(new HTMLBlockRule((opts === null || opts === void 0 ? void 0 : opts.validTags) || validTags)); | ||
if (!validTagsClosure) { | ||
validTagsClosure = validTags(); | ||
} | ||
rules0.push(new std_1.HTMLBlockRule((opts === null || opts === void 0 ? void 0 : opts.validTags) || validTagsClosure())); | ||
} | ||
@@ -617,20 +87,18 @@ return [...rules0, ...rules1, ...rules2]; | ||
let rules0 = [ | ||
new InlinePlainExceptSpecialMarksRule(), | ||
new LinkOrImageRule(), | ||
new InlineMathRule(), | ||
new std_1.InlinePlainExceptSpecialMarksRule(), | ||
new std_1.LinkOrImageRule(), | ||
new latex_1.InlineMathRule(), | ||
]; | ||
let rules1 = [ | ||
new EmphasisRule(), | ||
new InlineCodeRule(), | ||
new InlinePlainRule(), | ||
new std_1.EmphasisRule(), | ||
new std_1.InlineCodeRule(), | ||
new std_1.InlinePlainRule(), | ||
]; | ||
// default not enable | ||
if ((opts === null || opts === void 0 ? void 0 : opts.enableLaTeX) !== false) { | ||
rules0.push(new InlineLatexCommandRule()); | ||
rules0.push(new latex_1.InlineLatexCommandRule()); | ||
} | ||
return [...rules0, ...rules1]; | ||
} | ||
exports.newInlineRules = newInlineRules; | ||
function newRules(opts) { | ||
@@ -642,3 +110,2 @@ return { | ||
} | ||
exports.newRules = newRules; |
{ | ||
"name": "marktex.js", | ||
"version": "0.1.3", | ||
"version": "0.2.0", | ||
"description": "markdown parser", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
# MarkTeX | ||
LaTeX style extension for markdown | ||
## Installation | ||
Using npm: | ||
```bash | ||
npm i marktex.js | ||
``` | ||
In Node.js: | ||
``` | ||
``` |
@@ -14,5 +14,8 @@ import {myriad} from './index'; | ||
it('can render with default option', () => { | ||
console.log(myriad.Renderer().renderString('## hello marktex.js')) | ||
console.log(myriad.newRenderer().renderString('## hello marktex.js')) | ||
}); | ||
it('can render by default parser option', () => { | ||
console.log((new myriad.Renderer(myriad.newParser())).renderString('## hello marktex.js')) | ||
}) | ||
}); | ||
@@ -1,4 +0,4 @@ | ||
import {Parser, ParserOptions} from "./parser"; | ||
import {Renderer, RenderOptions} from "./renderer"; | ||
import {StringStream} from "./source"; | ||
import {Parser, ParserOptions} from "./parser/parser"; | ||
import {Renderer, RenderOptions} from "./renderer/renderer"; | ||
import {StringStream} from "./lib/stream"; | ||
import {newBlockRules, newInlineRules, newRules} from "./rules"; | ||
@@ -15,20 +15,18 @@ | ||
author: "Myriad-Dreamin", | ||
Parser(options?: Options): Parser { | ||
newParser(options?: Options): Parser { | ||
return new Parser(options?.parserOptions); | ||
}, | ||
Renderer(options?: Options): Renderer { | ||
return new Renderer(myriad.Parser(options), options?.rendererOptions); | ||
newRenderer(options?: Options): Renderer { | ||
return new Renderer(myriad.newParser(options), options?.rendererOptions); | ||
}, | ||
StringStream(str: string): StringStream { | ||
newStringStream(str: string): StringStream { | ||
return new StringStream(str); | ||
}, | ||
newInlineRules, | ||
newBlockRules, | ||
newRules, | ||
newInlineRules, newBlockRules, newRules, | ||
Parser, Renderer, StringStream, | ||
}; | ||
export { | ||
myriad, | ||
} | ||
// noinspection JSUnusedGlobalSymbols | ||
export default myriad; | ||
export {myriad, Options, Parser, Renderer, StringStream}; |
753
src/rules.ts
@@ -0,680 +1,101 @@ | ||
import {Rule, RuleContext} from "./rules/rule"; | ||
import { | ||
BlockElement, | ||
CodeBlock, | ||
Emphasis, | ||
HeaderBlock, | ||
Horizontal, | ||
HTMLBlock, | ||
ImageLink, | ||
InlineCode, | ||
InlineElement, | ||
InlinePlain, | ||
LateXBlock, | ||
Link, | ||
LinkDefinition, | ||
ListBlock, | ||
ListElement, | ||
MathBlock, | ||
MaybeToken, | ||
NewLine, | ||
Paragraph, | ||
Quotes | ||
} from "./token"; | ||
import {StringStream} from './source'; | ||
CodeBlockRule, | ||
EmphasisRule, | ||
HeaderBlockRule, | ||
HorizontalRule, | ||
HTMLBlockRule, | ||
HTMLTagsRegexps, | ||
InlineCodeRule, | ||
InlinePlainExceptSpecialMarksRule, | ||
InlinePlainRule, | ||
LinkDefinitionRule, | ||
LinkOrImageRule, | ||
ListBlockRule, | ||
NewLineRule, | ||
OpenTagRegExp, | ||
QuotesRule, | ||
RegExpWithTagName | ||
} from "./rules/std"; | ||
import {InlineLatexCommandRule, InlineMathRule, LatexBlockRule, MathBlockRule, ParagraphRule} from "./rules/latex"; | ||
import {GFMFencedCodeBlockRule} from "./rules/gfm"; | ||
export {RuleContext, Rule}; | ||
export {HTMLTagsRegexps}; | ||
export class RegExpWithTagName extends RegExp { | ||
protected gIndex: number; | ||
constructor(r: RegExp, gIndex: number) { | ||
super(r); | ||
this.gIndex = gIndex; | ||
} | ||
// noinspection JSUnusedGlobalSymbols | ||
getTagName(g: RegExpExecArray): string { | ||
return g[this.gIndex]; | ||
} | ||
} | ||
export class OpenTagRegExp extends RegExpWithTagName { | ||
protected gOpenIndex: number; | ||
constructor(r: RegExp, gIndex: number, gOpenIndex: number) { | ||
super(r, gIndex); | ||
this.gOpenIndex = gOpenIndex; | ||
} | ||
isSingleton(g: RegExpExecArray): boolean { | ||
return g[this.gOpenIndex] !== '/' | ||
} | ||
} | ||
export interface HTMLTagsRegexps { | ||
validTags: RegExp; | ||
open_tag: OpenTagRegExp; | ||
close_tag: RegExpWithTagName; | ||
comment: RegExp; | ||
others: RegExp[]; | ||
} | ||
export interface RuleContext { | ||
parseBlockElement(source: StringStream): BlockElement | ||
parseInlineElement(source: StringStream): InlineElement | ||
parseBlockElements(source: StringStream): BlockElement[] | ||
parseInlineElements(source: StringStream): InlineElement[] | ||
} | ||
export interface Rule { | ||
readonly name?: string | ||
readonly description?: string | ||
match: (s: StringStream, ctx: RuleContext) => MaybeToken | ||
} | ||
function forward(s: StringStream, capturing: RegExpExecArray) { | ||
s.forward(capturing[0].length) | ||
} | ||
const validTags: HTMLTagsRegexps = function (): HTMLTagsRegexps { | ||
let validTags: string = /^<open_tag>|<close_tag>|<comment>|<processing_instruction>|<declaration>|<cdata>/.source; | ||
let open_tag: string = /(?:<(<tag_name>)(?:\s*<attribute>)*\s*(\/?)>)/.source; | ||
let close_tag: string = /(?:<\/(<tag_name>)\s*>)/.source; | ||
let comment: RegExp = /(?:<!--(?:[^>-]|-[^>])(?:[^-]|-?[^-])*[^-]-->)/; | ||
let processing_instruction: RegExp = /(?:<?(?:(?!\?>).)*\?>)/; | ||
let declaration: RegExp = /(?:<![A-Z]+[^>]*>)/; | ||
let cdata: RegExp = /(?:<!\[CDATA(?:(?!]]>)\s\S)*]]>)/; | ||
let tag_name: RegExp = /(?:[a-zA-Z][a-zA-Z0-9-]*)/; | ||
let attribute: string = /(?:<attribute_name>(?:\s*=\s*<attribute_value>)?)/.source; | ||
let attribute_name: RegExp = /(?:[a-zA-Z_:][a-zA-Z0-9_.:-]*)/; | ||
let attribute_value: RegExp = /(?:[^/s'"=<>`]|'[^']*'|"[^"]*")/; | ||
attribute = attribute | ||
.replace('<attribute_name>', attribute_name.source) | ||
.replace('<attribute_value>', attribute_value.source); | ||
close_tag = close_tag.replace('<tag_name>', tag_name.source); | ||
open_tag = open_tag | ||
.replace('<tag_name>', tag_name.source) | ||
.replace('<attribute>', attribute); | ||
validTags = validTags | ||
.replace('<open_tag>', open_tag) | ||
.replace('<close_tag>', close_tag) | ||
.replace('<comment>', comment.source) | ||
.replace('<processing_instruction>', processing_instruction.source) | ||
.replace('<declaration>', declaration.source) | ||
.replace('<cdata>', cdata.source); | ||
return { | ||
validTags: new RegExp(validTags), | ||
open_tag: new OpenTagRegExp(new RegExp(open_tag), 1, 2), | ||
close_tag: new RegExpWithTagName(new RegExp(close_tag), 1), | ||
comment, | ||
others: [processing_instruction, declaration, cdata], | ||
// processing_instruction, | ||
// declaration, | ||
// cdata, | ||
// tag_name, | ||
// attribute: new RegExp(attribute), | ||
// attribute_name, | ||
// attribute_value, | ||
}; | ||
}(); | ||
export class NewLineRule implements Rule { | ||
readonly name: string = "NewLine"; | ||
readonly description: string = "Standard Markdown Block Rule"; | ||
public readonly regex: RegExp = /^\n+/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
function validTags(): () => HTMLTagsRegexps { | ||
let _validTags: HTMLTagsRegexps | undefined = undefined; | ||
return () => { | ||
if (_validTags !== undefined) { | ||
return _validTags; | ||
} | ||
let validTags: string = /^<open_tag>|<close_tag>|<comment>|<processing_instruction>|<declaration>|<cdata>/.source; | ||
let open_tag: string = /(?:<(<tag_name>)(?:\s*<attribute>)*\s*(\/?)>)/.source; | ||
let close_tag: string = /(?:<\/(<tag_name>)\s*>)/.source; | ||
let comment: RegExp = /(?:<!--(?:[^>-]|-[^>])(?:[^-]|-?[^-])*[^-]-->)/; | ||
let processing_instruction: RegExp = /(?:<?(?:(?!\?>).)*\?>)/; | ||
let declaration: RegExp = /(?:<![A-Z]+[^>]*>)/; | ||
let cdata: RegExp = /(?:<!\[CDATA(?:(?!]]>)\s\S)*]]>)/; | ||
forward(s, capturing); | ||
return new NewLine(capturing[0]); | ||
}; | ||
} | ||
let tag_name: RegExp = /(?:[a-zA-Z][a-zA-Z0-9-]*)/; | ||
let attribute: string = /(?:<attribute_name>(?:\s*=\s*<attribute_value>)?)/.source; | ||
let attribute_name: RegExp = /(?:[a-zA-Z_:][a-zA-Z0-9_.:-]*)/; | ||
let attribute_value: RegExp = /(?:[^/s'"=<>`]|'[^']*'|"[^"]*")/; | ||
export class ParagraphRule implements Rule { | ||
readonly name: string = "Paragraph"; | ||
readonly description: string = "Standard Markdown Block Rule"; | ||
attribute = attribute | ||
.replace('<attribute_name>', attribute_name.source) | ||
.replace('<attribute_value>', attribute_value.source); | ||
close_tag = close_tag.replace('<tag_name>', tag_name.source); | ||
open_tag = open_tag | ||
.replace('<tag_name>', tag_name.source) | ||
.replace('<attribute>', attribute); | ||
validTags = validTags | ||
.replace('<open_tag>', open_tag) | ||
.replace('<close_tag>', close_tag) | ||
.replace('<comment>', comment.source) | ||
.replace('<processing_instruction>', processing_instruction.source) | ||
.replace('<declaration>', declaration.source) | ||
.replace('<cdata>', cdata.source); | ||
// public readonly regex: RegExp = /^(?:(?:[^$]|\$(?!\$))(?:\n|$)?)+/; | ||
protected readonly skipLaTeXBlock: boolean; | ||
constructor(skipLaTeXBlock: boolean) { | ||
this.skipLaTeXBlock = skipLaTeXBlock; | ||
_validTags = { | ||
validTags: new RegExp(validTags), | ||
open_tag: new OpenTagRegExp(new RegExp(open_tag), 1, 2), | ||
close_tag: new RegExpWithTagName(new RegExp(close_tag), 1), | ||
comment, | ||
others: [processing_instruction, declaration, cdata], | ||
// processing_instruction, | ||
// declaration, | ||
// cdata, | ||
// tag_name, | ||
// attribute: new RegExp(attribute), | ||
// attribute_name, | ||
// attribute_value, | ||
}; | ||
return _validTags; | ||
} | ||
match(s: StringStream, ctx: RuleContext): MaybeToken { | ||
let lastChar: string = 'a', i = 0; | ||
if (s.source[0] == '\n') { | ||
return undefined; | ||
} | ||
if (this.skipLaTeXBlock) { | ||
for (; i < s.source.length; i++) { | ||
if (lastChar === s.source[i] && (lastChar === '$' || lastChar === '\n')) { | ||
i--; | ||
break; | ||
} | ||
if (lastChar === '\n' && s.source[i] === '\\') { | ||
if (i + 1 < s.source.length && | ||
(('a' <= s.source[i + 1] && s.source[i + 1] <= 'z') || ('A' <= s.source[i + 1] && s.source[i + 1] <= 'Z')) | ||
) { | ||
i--; | ||
break; | ||
} | ||
} else if (lastChar === '\\' && s.source[i] !== '\n') { | ||
lastChar = 'a'; | ||
} else { | ||
lastChar = s.source[i]; | ||
} | ||
} | ||
} else { | ||
for (; i < s.source.length; i++) { | ||
if (lastChar === s.source[i] && '$\n'.includes(lastChar)) { | ||
i--; | ||
break; | ||
} | ||
if (lastChar === '\\' && s.source[i] !== '\n') { | ||
lastChar = 'a'; | ||
} else { | ||
lastChar = s.source[i]; | ||
} | ||
} | ||
} | ||
if (!i) { | ||
return undefined; | ||
} | ||
let capturing = s.source.slice(0, i); | ||
s.forward(i); | ||
return new Paragraph(ctx.parseInlineElements(new StringStream(capturing))); | ||
}; | ||
} | ||
/* *+- */ | ||
export const inlineRules: Rule[] = newInlineRules(); | ||
export const blockRules: Rule[] = newBlockRules(); | ||
/* 1. */ | ||
export class ListBlockRule implements Rule { | ||
readonly name: string = "ListBlock"; | ||
readonly description: string = "Standard Markdown Block Rule"; | ||
public static readonly blankRegex = /^[\t\v\f ]*\n/; | ||
public static readonly listBlockRegex = /^([^\n]*(?:\n|$)(?:(?=[^\n0-9*+-])[^\n]*(?:\n|$))*)/; | ||
public static readonly replaceRegex = /^(?: {4}|\t)/gm; | ||
match(s: StringStream, ctx: RuleContext): MaybeToken { | ||
let ordered: boolean; | ||
if ("*+-".includes(s.source[0])) { | ||
ordered = false; | ||
} else if ('0' <= s.source[0] && s.source[0] <= '9') { | ||
ordered = true; | ||
} else { | ||
return undefined; | ||
} | ||
return ListBlockRule.matchBlock(new ListBlock(ordered), s, ctx); | ||
}; | ||
private static matchBlock(l: ListBlock, s: StringStream, ctx: RuleContext): ListBlock | undefined { | ||
let nextMarker: string | undefined; | ||
nextMarker = l.lookAhead(s); | ||
if (!nextMarker) { | ||
return undefined; | ||
} | ||
let blockContent: string, marker: string | undefined, lastSeparated = false; | ||
for (blockContent = '', marker = nextMarker, nextMarker = undefined; | ||
marker !== undefined; | ||
blockContent = '', marker = nextMarker, nextMarker = undefined) { | ||
do { | ||
let capturing = ListBlockRule.listBlockRegex.exec(s.source); | ||
if (capturing === null) { | ||
throw new Error("match block failed"); | ||
} | ||
forward(s, capturing); | ||
blockContent += capturing[0]; | ||
} while (l.lookAhead0(s) && (nextMarker = l.lookAhead(s)) === undefined); | ||
let element = new ListElement(marker, ctx.parseBlockElements( | ||
new StringStream(blockContent.replace(ListBlockRule.replaceRegex, '')), | ||
)); | ||
if (!nextMarker) { | ||
let capturing = ListBlockRule.blankRegex.exec(s.source); | ||
if (capturing !== null) { | ||
forward(s, capturing); | ||
element.blankSeparated = true; | ||
lastSeparated = true; | ||
if (l.lookAhead0(s)) { | ||
nextMarker = l.lookAhead(s); | ||
} | ||
} else { | ||
element.blankSeparated = lastSeparated; | ||
lastSeparated = false; | ||
} | ||
} | ||
l.listElements.push(element); | ||
} | ||
return l; | ||
} | ||
export interface CreateBlockRuleOptions { | ||
enableHtml?: boolean; | ||
enableLaTeX?: boolean; | ||
enableGFMRules?: boolean; | ||
validTags?: HTMLTagsRegexps; | ||
} | ||
export class QuotesRule implements Rule { | ||
readonly name: string = "Quotes"; | ||
readonly description: string = "Standard Markdown Block Rule"; | ||
public readonly regex: RegExp = /^( *>[^\n]*(?:\n[^\n]+)*\n?)+/; | ||
match(s: StringStream, ctx: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new Quotes(ctx.parseBlockElements( | ||
new StringStream(capturing[0].replace(/^ *> ?/gm, '')) | ||
)); | ||
}; | ||
export interface CreateInlineRuleOptions { | ||
enableLaTeX?: boolean; | ||
} | ||
export class HorizontalRule implements Rule { | ||
readonly name: string = "Horizontal"; | ||
readonly description: string = "Standard Markdown Block Rule"; | ||
export interface CreateRuleOptions extends CreateInlineRuleOptions, CreateBlockRuleOptions { | ||
public readonly regex: RegExp = /^(?:(?:\*[\r\t ]*){3,}|(?:-[\r\t ]*){3,})(?:\n|$)/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new Horizontal(); | ||
}; | ||
} | ||
export class CodeBlockRule implements Rule { | ||
readonly name: string = "CodeBlock"; | ||
readonly description: string = "Standard Markdown Block Rule"; | ||
let validTagsClosure: () => HTMLTagsRegexps; | ||
public static readonly regex: RegExp = /^((?: {4}|\t)[^\n]+(\n|$))+/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = CodeBlockRule.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new CodeBlock(capturing[0].replace(/^(?: {4}|\t)/gm, '')); | ||
}; | ||
} | ||
export class HeaderBlockRule implements Rule { | ||
readonly name: string = "HeaderBlock"; | ||
readonly description: string = "Standard Markdown Block Rule"; | ||
public readonly atxRegex: RegExp = /^(#{1,6}) ([^\n]*?)#*(?:\n|$)/; | ||
public readonly setextRegex: RegExp = /^([^\n]+)\n=+(?:\n|$)/; | ||
match(s: StringStream, ctx: RuleContext): MaybeToken { | ||
return this.matchATX(s, ctx) || this.matchSetext(s, ctx); | ||
}; | ||
matchATX(s: StringStream, ctx: RuleContext): MaybeToken { | ||
let capturing = this.atxRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new HeaderBlock(ctx.parseInlineElements(new StringStream(capturing[2])), capturing[1].length); | ||
} | ||
matchSetext(s: StringStream, ctx: RuleContext): MaybeToken { | ||
let capturing = this.setextRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new HeaderBlock(ctx.parseInlineElements(new StringStream(capturing[1])), 1); | ||
} | ||
} | ||
export class LinkDefinitionRule implements Rule { | ||
readonly name: string = "LinkDefinition"; | ||
readonly description: string = "Standard Markdown Block Rule"; | ||
public readonly regex: RegExp = /^ *\[([^\]]+)]: *<?([^\s>]+)>?(?: +["'(]([^\n]*)["')])? *(?:\n|$)/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new LinkDefinition(capturing[1], capturing[2], capturing[3]); | ||
}; | ||
} | ||
export class HTMLBlockRule implements Rule { | ||
readonly name: string = "HTMLBlock"; | ||
readonly description: string = "Standard Markdown Block Rule"; | ||
private validTags: HTMLTagsRegexps; | ||
constructor(validTags: HTMLTagsRegexps) { | ||
this.validTags = validTags; | ||
} | ||
// public readonly singleTonRegex: RegExp = /^/; | ||
// public readonly stdRegex: RegExp = /^/; | ||
match(s: StringStream, ctx: RuleContext): MaybeToken { | ||
let ot: OpenTagRegExp = this.validTags.open_tag; | ||
let capturing = ot.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
if (ot.isSingleton(capturing)) { | ||
return new HTMLBlock(capturing[0]); | ||
} | ||
return undefined | ||
}; | ||
} | ||
function _braceMatch(s: StringStream, l: string, r: string): string { | ||
if (s.source[0] == l) { | ||
let c: number = 0; | ||
for (let j = 0; j < s.source.length; j++) { | ||
if (s.source[j] == l) { | ||
c++; | ||
} else if (s.source[j] == r) { | ||
c--; | ||
if (c === 0) { | ||
let res = s.source.slice(0, j + 1); | ||
s.forward(j + 1); | ||
return res; | ||
} | ||
} | ||
} | ||
let res = s.source; | ||
s.forward(s.source.length); | ||
return res; | ||
} | ||
return ''; | ||
} | ||
export class InlineLatexCommandRule implements Rule { | ||
readonly name: string = "InlineLatexCommand"; | ||
readonly description: string = "Latex Inline Rule"; | ||
public static readonly cmdNameRegex = /^\\([a-zA-Z_]\w*)/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = InlineLatexCommandRule.cmdNameRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new InlinePlain(capturing[0] + this.braceMatch(s)); | ||
} | ||
braceMatch(s: StringStream) { | ||
let res: string = ''; | ||
for (let i = 0; !s.eof; i = 0) { | ||
for (; !s.eof && ' \n\t\v\f\r'.includes(s.source[i]); i++) { | ||
} | ||
if (!'[{'.includes(s.source[i])) { | ||
return res; | ||
} | ||
if (i) { | ||
res += s.source.slice(0, i); | ||
s.forward(i); | ||
} | ||
res += _braceMatch(s, '{', '}'); | ||
res += _braceMatch(s, '[', ']'); | ||
} | ||
return res; | ||
} | ||
} | ||
export class LatexBlockRule implements Rule { | ||
readonly name: string = "LatexBlock"; | ||
readonly description: string = "Latex Inline Rule"; | ||
public static readonly cmdNameRegex = /^\\([a-zA-Z_]\w*)/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = InlineLatexCommandRule.cmdNameRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new LateXBlock(capturing[0] + this.braceMatch(s)); | ||
} | ||
braceMatch(s: StringStream) { | ||
let res: string = ''; | ||
for (let i = 0; !s.eof; i = 0) { | ||
for (; !s.eof && ' \t\v\f\r'.includes(s.source[i]); i++) { | ||
} | ||
if (s.source[i] == '\n') { | ||
i++; | ||
if (s.source[i] == '\n') { | ||
s.forward(i); | ||
return res; | ||
} | ||
} | ||
if (!'[{'.includes(s.source[i])) { | ||
return res; | ||
} | ||
if (i) { | ||
res += s.source.slice(0, i); | ||
s.forward(i); | ||
} | ||
res += _braceMatch(s, '[', ']'); | ||
res += _braceMatch(s, '{', '}'); | ||
} | ||
return res; | ||
} | ||
} | ||
export class InlinePlainExceptSpecialMarksRule implements Rule { | ||
readonly name: string = "InlinePlainExceptSpecialMarks"; | ||
readonly description: string = "Standard Markdown Inline Rule"; | ||
public readonly regex: RegExp = /^(?:\\[<`_*\[$\\]|[^<`_*\[$\\])+/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new InlinePlain(capturing[0]); | ||
}; | ||
} | ||
export class InlinePlainRule implements Rule { | ||
readonly name: string = "InlinePlain"; | ||
readonly description: string = "Standard Markdown Inline Rule"; | ||
public readonly regex: RegExp = /^(?:[<`_*\[$\\](?:\\[<`_*\[$\\]|[^<`_*\[$\\])*|(?:\\[<`_*\[$\\]|[^<`_*\[$\\])+)/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new InlinePlain(capturing[0]); | ||
}; | ||
} | ||
export class InlineMathRule implements Rule { | ||
readonly name: string = "InlineMath"; | ||
readonly description: string = "Markdown Inline Rule"; | ||
public readonly regex: RegExp = /^\$((?:[^$]|\\\$)+)\$/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new MathBlock(capturing[1], true); | ||
}; | ||
} | ||
export class MathBlockRule implements Rule { | ||
readonly name: string = "MathBlock"; | ||
readonly description: string = "Markdown Block Rule"; | ||
public readonly regex: RegExp = /^\$\$((?:[^$]|\\\$)+)\$\$/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new MathBlock(capturing[1], false); | ||
}; | ||
} | ||
export class LinkOrImageRule implements Rule { | ||
readonly name: string = "Link"; | ||
readonly description: string = "Standard Markdown Inline Rule"; | ||
public readonly regex: RegExp = /^(!?)\[((?:\[[^\]]*]|[^\[\]]|](?=[^\[]*]))*)]\(\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*\)/; | ||
public readonly refRegex: RegExp = /^(!?)\[((?:\[[^\]]*]|[^\[\]]|](?=[^\[]*]))*)]\s*\[([^\]]*)]/; | ||
match(s: StringStream, ctx: RuleContext): MaybeToken { | ||
return this.matchInline(s, ctx) || this.matchRef(s, ctx); | ||
}; | ||
matchInline(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
if (capturing[1] !== '') { | ||
return new ImageLink(capturing[2], capturing[3], true, capturing[4]); | ||
} else { | ||
return new Link(capturing[2], capturing[3], true, capturing[4]); | ||
} | ||
} | ||
matchRef(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.refRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
if (capturing[1] !== '') { | ||
return new ImageLink(capturing[2], capturing[3], false); | ||
} else { | ||
return new Link(capturing[2], capturing[3], false); | ||
} | ||
} | ||
} | ||
export class EmphasisRule implements Rule { | ||
readonly name: string = "Emphasis"; | ||
readonly description: string = "Standard Markdown Inline Rule"; | ||
public readonly regex: RegExp = /^(?:(_{1,2})([^_]+?)(_{1,2})|(\*{1,2})([^*]+?)(\*{1,2}))/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
let l: string = capturing[1] || capturing[4], r: string = capturing[3] || capturing[6]; | ||
if (l !== r) { | ||
if (l.length < r.length) { | ||
s.forward(capturing[0].length - 1); | ||
return new Emphasis(capturing[2] || capturing[5], l.length); | ||
} | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new Emphasis(capturing[2] || capturing[5], l.length); | ||
}; | ||
} | ||
export class InlineCodeRule implements Rule { | ||
readonly name: string = "InlineCode"; | ||
readonly description: string = "Standard Markdown Inline Rule"; | ||
public readonly regex: RegExp = /^(?:``([^`\n\r\u2028\u2029](?:`?[^`\n\r\u2028\u2029])*)``|`([^`\n\r\u2028\u2029]+?)`)/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = this.regex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
forward(s, capturing); | ||
return new InlineCode(capturing[1] || capturing[2]); | ||
}; | ||
} | ||
export class GFMFencedCodeBlockRule implements Rule { | ||
readonly name: string = "GFMCodeBlock"; | ||
readonly description: string = "GFM Markdown Block Rule"; | ||
public static readonly backtickRegex: RegExp = /^(`{3,}) *([^`\s]+)?[^`\n]*(?:\n|$)([\s\S]*?)(?:\1`*|$)/; | ||
public static readonly tildeRegex: RegExp = /^(~{3,}) *([^~\s]+)?.*(?:\n|$)([\s\S]*?)(?:\1~*|$)/; | ||
match(s: StringStream, _: RuleContext): MaybeToken { | ||
let capturing = GFMFencedCodeBlockRule.backtickRegex.exec(s.source); | ||
if (capturing === null) { | ||
capturing = GFMFencedCodeBlockRule.tildeRegex.exec(s.source); | ||
if (capturing === null) { | ||
return undefined; | ||
} | ||
} | ||
forward(s, capturing); | ||
return new CodeBlock(capturing[3], capturing[2]); | ||
}; | ||
} | ||
export const inlineRules: Rule[] = newInlineRules(); | ||
export const blockRules: Rule[] = newBlockRules(); | ||
export interface CreateBlockRuleOptions { | ||
enableHtml?: boolean; | ||
enableLaTeX?: boolean; | ||
enableGFMRules?: boolean; | ||
validTags?: HTMLTagsRegexps; | ||
} | ||
// noinspection JSUnusedGlobalSymbols | ||
@@ -699,3 +120,3 @@ export function newBlockRules( | ||
let rules2: Rule[] = [ | ||
new ParagraphRule(opts?.enableLaTeX || false), | ||
new ParagraphRule({skipLaTeXBlock: opts?.enableLaTeX || false, skipMathBlock: true}), | ||
]; | ||
@@ -709,3 +130,6 @@ | ||
if (opts?.enableHtml) { | ||
rules0.push(new HTMLBlockRule(opts?.validTags || validTags)); | ||
if (!validTagsClosure) { | ||
validTagsClosure = validTags(); | ||
} | ||
rules0.push(new HTMLBlockRule(opts?.validTags || validTagsClosure())); | ||
} | ||
@@ -716,6 +140,2 @@ | ||
export interface CreateInlineRuleOptions { | ||
enableLaTeX?: boolean; | ||
} | ||
// noinspection JSUnusedGlobalSymbols | ||
@@ -744,6 +164,2 @@ export function newInlineRules( | ||
export interface CreateRuleOptions extends CreateInlineRuleOptions, CreateBlockRuleOptions { | ||
} | ||
export function newRules(opts?: CreateRuleOptions) { | ||
@@ -755,4 +171,1 @@ return { | ||
} | ||
export {validTags}; |
@@ -14,4 +14,4 @@ { | ||
"node_modules", | ||
"src/**/*.test.ts", | ||
// "src/**/*.bench.ts", | ||
// "src/**/*.test.ts", | ||
// "src/**/*.bench.ts", | ||
"dist" | ||
@@ -18,0 +18,0 @@ ], |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
40
17
309891
5816
1