turnish 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.js","sources":["../src/node.ts","../src/utilities.ts","../src/default-rules.ts","../src/rules.ts","../src/collapse-whitespace.ts","../src/html-parser.ts","../src/root-node.ts","../src/index.ts"],"sourcesContent":["import { isBlock, isVoid, hasVoid, isMeaningfulWhenBlank, hasMeaningfulWhenBlank } from '@/utilities'\n\ninterface Options {\n preformattedCode?: boolean;\n}\n\ninterface FlankingWhitespace {\n leading: string;\n trailing: string;\n}\n\ninterface EdgeWhitespace extends FlankingWhitespace {\n leadingAscii: string;\n leadingNonAscii: string;\n trailingNonAscii: string;\n trailingAscii: string;\n}\n\nexport interface ExtendedNode extends Element {\n isBlock: boolean;\n isCode: boolean;\n isBlank: boolean;\n flankingWhitespace: FlankingWhitespace;\n}\n\nexport function ExtendedNode(node: Node, options: Options): ExtendedNode {\n const extended = node as ExtendedNode;\n extended.isBlock = isBlock(extended);\n extended.isCode = extended.nodeName === 'CODE' || (extended.parentNode as ExtendedNode)?.isCode;\n extended.isBlank = isBlank(extended);\n extended.flankingWhitespace = flankingWhitespace(extended, options);\n return extended;\n}\n\nfunction isBlank(node: ExtendedNode): boolean {\n return (\n !isVoid(node) &&\n !isMeaningfulWhenBlank(node) &&\n /^\\s*$/i.test(node.textContent || '') &&\n !hasVoid(node) &&\n !hasMeaningfulWhenBlank(node)\n )\n}\n\nfunction flankingWhitespace(node: ExtendedNode, options: Options): FlankingWhitespace {\n const extendedNode = node as ExtendedNode;\n\n if (extendedNode.isBlock || (options.preformattedCode && extendedNode.isCode)) {\n return { leading: '', trailing: '' };\n }\n\n const edges = edgeWhitespace(node.textContent || '');\n\n // abandon leading ASCII WS if left-flanked by ASCII WS\n if (edges.leadingAscii && isFlankedByWhitespace('left', node, options)) {\n edges.leading = edges.leadingNonAscii;\n }\n\n // abandon trailing ASCII WS if right-flanked by ASCII WS\n if (edges.trailingAscii && isFlankedByWhitespace('right', node, options)) {\n edges.trailing = edges.trailingNonAscii;\n }\n\n return { leading: edges.leading, trailing: edges.trailing };\n}\n\nfunction edgeWhitespace(string: string): EdgeWhitespace {\n const m = string.match(/^(([ \\t\\r\\n]*)(\\s*))(?:(?=\\S)[\\s\\S]*\\S)?((\\s*?)([ \\t\\r\\n]*))$/);\n\n if (!m) {\n return {\n leading: '',\n leadingAscii: '',\n leadingNonAscii: '',\n trailing: '',\n trailingNonAscii: '',\n trailingAscii: ''\n };\n }\n\n return {\n leading: m[1], // whole string for whitespace-only strings\n leadingAscii: m[2],\n leadingNonAscii: m[3],\n trailing: m[4], // empty for whitespace-only strings\n trailingNonAscii: m[5],\n trailingAscii: m[6]\n };\n}\n\nfunction isFlankedByWhitespace(side: 'left' | 'right', node: Element, options: Options): boolean {\n let sibling: Node | null;\n let regExp: RegExp;\n let isFlanked: boolean | undefined;\n\n if (side === 'left') {\n sibling = node.previousSibling;\n regExp = / $/;\n } else {\n sibling = node.nextSibling;\n regExp = /^ /;\n }\n\n if (sibling) {\n if (sibling.nodeType === 3) {\n isFlanked = regExp.test(sibling.nodeValue || '');\n } else if (options.preformattedCode && sibling.nodeName === 'CODE') {\n isFlanked = false;\n } else if (sibling.nodeType === 1 && !isBlock(sibling)) {\n isFlanked = regExp.test(sibling.textContent || '');\n }\n }\n\n return isFlanked || false;\n}\n\nexport const NodeTypes = {\n Element: 1,\n Text: 3,\n CDATASection: 4,\n Comment: 8\n} as const;\n\nexport type NodeType = typeof NodeTypes[keyof typeof NodeTypes];\n","import { ExtendedNode, NodeTypes } from \"./node\";\n\nexport function repeat(character: string, count: number) {\n return Array(count + 1).join(character)\n}\n\nexport function trimLeadingNewlines(string: string) {\n return string.replace(/^\\n*/, '')\n}\n\nexport function trimTrailingNewlines(string: string) {\n // avoid match-at-end regexp bottleneck, see #370\n let indexEnd = string.length\n while (indexEnd > 0 && string[indexEnd - 1] === '\\n') indexEnd--\n return string.substring(0, indexEnd)\n}\n\nexport function trimNewlines(string: string) {\n return trimTrailingNewlines(trimLeadingNewlines(string))\n}\n\nexport const blockElements = [\n 'ADDRESS', 'ARTICLE', 'ASIDE', 'AUDIO', 'BLOCKQUOTE', 'BODY', 'CANVAS',\n 'CENTER', 'DD', 'DIR', 'DIV', 'DL', 'DT', 'FIELDSET', 'FIGCAPTION', 'FIGURE',\n 'FOOTER', 'FORM', 'FRAMESET', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEADER',\n 'HGROUP', 'HR', 'HTML', 'ISINDEX', 'LI', 'MAIN', 'MENU', 'NAV', 'NOFRAMES',\n 'NOSCRIPT', 'OL', 'OUTPUT', 'P', 'PRE', 'SECTION', 'TABLE', 'TBODY', 'TD',\n 'TFOOT', 'TH', 'THEAD', 'TR', 'UL'\n]\n\nexport function isBlock(node: Node) {\n return is(node, blockElements)\n}\n\nexport const voidElements = [\n 'AREA', 'BASE', 'BR', 'COL', 'COMMAND', 'EMBED', 'HR', 'IMG', 'INPUT',\n 'KEYGEN', 'LINK', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR'\n]\n\nexport function isVoid(node: Node) {\n return is(node, voidElements)\n}\n\nexport function hasVoid(node: Node) {\n return has(node, voidElements)\n}\n\nconst meaningfulWhenBlankElements = [\n 'A', 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TH', 'TD', 'IFRAME', 'SCRIPT',\n 'AUDIO', 'VIDEO'\n]\n\nexport const standardMarkdownElements = [\n 'P', 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'BLOCKQUOTE',\n 'UL', 'OL', 'LI', 'PRE', 'CODE', 'HR', 'A', 'EM', 'I', 'STRONG',\n 'B', 'IMG', 'DIV', 'SPAN', 'TABLE', 'THEAD', 'TBODY', 'TR', 'TH', 'TD'\n]\n\nexport function isMeaningfulWhenBlank(node: Node) {\n return is(node, meaningfulWhenBlankElements)\n}\n\nexport function hasMeaningfulWhenBlank(node: Node) {\n return has(node, meaningfulWhenBlankElements)\n}\n\nfunction is(node: Node, tagNames: string[]) {\n return tagNames.indexOf(node.nodeName) >= 0\n}\n\nfunction has(node: Node, tagNames: string[]) {\n return (\n tagNames.some(function (tagName) {\n if (node.nodeType !== NodeTypes.Element) {\n return false;\n }\n return (node as Element).getElementsByTagName(tagName).length\n })\n )\n}\n\nexport function sanitizeWhitespace(string: string): string {\n return string ? string.replace(/(\\n+\\s*)+/g, '\\n') : '';\n}\n\nexport function sanitizedLinkContent(content: string): string {\n const sanitized = sanitizeWhitespace(content);\n return sanitized\n .replace(/[\\t\\r\\n]+/g, ' ')\n .replace(/ {2,}/g, ' ')\n .trim();\n}\n\nexport function sanitizedLinkTitle(content: string): string {\n const sanitized = sanitizeWhitespace(content);\n return sanitized\n .replace(/[\\t\\r\\n]+/g, ' ');\n}\n\nexport type RequireOnly<T, K extends keyof T> =\n T & Required<Pick<T, K>>;\n","\nimport { Rule } from '@/rules';\nimport { TurnishOptions } from '@/index';\nimport { repeat, RequireOnly, sanitizedLinkContent, sanitizedLinkTitle, trimNewlines } from '@/utilities';\nimport { NodeTypes } from './node';\n\nexport const defaultRules: { [key: string]: Rule } = {}\n\ndefaultRules.paragraph = {\n filter: 'p',\n replacement: function (content: string): string {\n return '\\n\\n' + content + '\\n\\n';\n }\n};\n\ndefaultRules.lineBreak = {\n filter: 'br',\n replacement: function (_content: string, _node: Node, options: TurnishOptions): string {\n return options.br + '\\n';\n }\n};\n\ndefaultRules.heading = {\n filter: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],\n replacement: function (content: string, node: Node, options: TurnishOptions): string {\n const hLevel = Number(node.nodeName.charAt(1));\n if (options.headingStyle === 'setext' && hLevel < 3) {\n const underline = repeat((hLevel === 1 ? '=' : '-'), content.length);\n return (\n '\\n\\n' + content + '\\n' + underline + '\\n\\n'\n );\n } else {\n return '\\n\\n' + repeat('#', hLevel) + ' ' + content + '\\n\\n';\n }\n }\n};\n\ndefaultRules.blockquote = {\n filter: 'blockquote',\n replacement: function (content: string): string {\n content = trimNewlines(content).replace(/^/gm, '> ');\n return '\\n\\n' + content + '\\n\\n';\n }\n};\n\ndefaultRules.list = {\n filter: ['ul', 'ol'],\n replacement: function (content: string, node: Node): string {\n const parent = node.parentNode as Element;\n if (parent.nodeName === 'LI' && parent.lastElementChild === node) {\n return '\\n' + content;\n } else {\n return '\\n\\n' + content + '\\n\\n';\n }\n }\n};\n\ndefaultRules.listItem = {\n filter: 'li',\n replacement: function (content: string, node: Node, options: TurnishOptions): string {\n let prefix = options.bulletListMarker + ' '.repeat(options.listMarkerSpaceCount);\n const parent = node.parentNode as Element;\n if (parent.nodeName === 'OL') {\n const start = parent.getAttribute('start');\n const index = Array.prototype.indexOf.call(parent.children, node);\n prefix = (start ? Number(start) + index : index + 1) + '.' + ' '.repeat(options.listMarkerSpaceCount);\n }\n const isParagraph = /\\n$/.test(content);\n content = trimNewlines(content) + (isParagraph ? '\\n' : '');\n\n const hasOnlyNestedList = node.childNodes.length > 0 &&\n Array.from(node.childNodes).every((child: Node) => {\n return (child.nodeType === NodeTypes.Text && /^\\s*$/.test(child.nodeValue || ''))\n || (child.nodeType === NodeTypes.Element && ['UL', 'OL'].includes(child.nodeName));\n });\n if (hasOnlyNestedList && content.trim() !== '') {\n // This list item only contains a nested list, don't duplicate marker\n return content + (node.nextSibling ? '\\n' : '');\n }\n\n let nestingLevel = 0;\n let currentNode: Node | null = parent;\n while (currentNode) {\n if (currentNode.nodeName === 'UL' || currentNode.nodeName === 'OL') {\n const grandparent = currentNode.parentNode as Element | null;\n if (grandparent && grandparent.nodeName === 'LI') {\n nestingLevel++;\n }\n }\n currentNode = currentNode.parentNode;\n }\n\n let oneIndent = options.listItemIndent === 'tab' ? '\\t' : ' '.repeat(options.listItemIndentSpaceCount);\n let indent = oneIndent.repeat(nestingLevel);\n const listMarkerRegex = /\\n(?!\\s*(?:\\d+\\.\\s|[-+*]\\s))/gm;\n content = content.replace(listMarkerRegex, '\\n' + oneIndent);\n return indent + prefix + content + (node.nextSibling ? '\\n' : '');\n }\n};\n\ndefaultRules.indentedCodeBlock = {\n filter: function (node: Node, options: TurnishOptions): boolean {\n return !!(\n options &&\n options.codeBlockStyle === 'indented' &&\n node.nodeName === 'PRE' &&\n node.firstChild &&\n (node.firstChild as Element).nodeName === 'CODE'\n );\n },\n replacement: function (_content: string, node: Node): string {\n if (!node || !node.firstChild) return '';\n return (\n '\\n\\n ' +\n (node.firstChild as Element).textContent!.replace(/\\n/g, '\\n ') +\n '\\n\\n'\n );\n }\n};\n\ndefaultRules.fencedCodeBlock = {\n filter: function (node: Node, options: TurnishOptions): boolean {\n return !!(\n options &&\n options.codeBlockStyle === 'fenced' &&\n node.nodeName === 'PRE' &&\n node.firstChild &&\n (node.firstChild as Element).nodeName === 'CODE'\n );\n },\n replacement: function (_content: string, node: Node, options: TurnishOptions): string {\n if (!node.firstChild) {\n return '';\n }\n const codeElem = node.firstChild as Element;\n const className = codeElem.getAttribute('class') || '';\n const language = (className.match(/language-(\\S+)/) || [null, ''])[1];\n const code = codeElem.textContent || '';\n const fenceChar = options.fence?.charAt(0) || '`';\n let fenceSize = 3;\n const fenceInCodeRegex = new RegExp('^' + fenceChar + '{3,}', 'gm');\n let match;\n while ((match = fenceInCodeRegex.exec(code))) {\n if (match[0].length >= fenceSize) {\n fenceSize = match[0].length + 1;\n }\n }\n const fence = repeat(fenceChar, fenceSize);\n return (\n '\\n\\n' + fence + language + '\\n' +\n code.replace(/\\n$/, '') +\n '\\n' + fence + '\\n\\n'\n );\n }\n};\n\ndefaultRules.horizontalRule = {\n filter: 'hr',\n replacement: function (_content: string, _node: Node, options: TurnishOptions): string {\n return '\\n\\n' + options.hr + '\\n\\n';\n }\n};\n\ndefaultRules.inlineLink = {\n filter: function (node: Node, options: TurnishOptions): boolean {\n return !!(\n options?.linkStyle === 'inlined' &&\n node.nodeName === 'A' &&\n (node as Element).getAttribute('href')\n );\n },\n replacement: function (content: string, node: Node): string {\n const sanitizedContent = sanitizedLinkContent(content);\n let href = (node as Element)\n .getAttribute('href')\n ?.replace(/([()])/g, '\\\\$1');\n let title: string;\n const titleAttr = (node as Element).getAttribute('title');\n if (titleAttr) {\n const sanitizedTitle = sanitizedLinkTitle(titleAttr);\n title = ' \"' + sanitizedTitle.replace(/\"/g, '\\\\\"') + '\"';\n } else {\n title = '';\n }\n return '[' + sanitizedContent + '](' + href + title + ')';\n }\n};\n\nconst referenceLinkRule: RequireOnly<Rule, \"urlReferenceIdMap\" | \"references\"> = {\n filter: function (node: Node, options: TurnishOptions): boolean {\n return !!(\n options &&\n options.linkStyle === 'referenced' &&\n node.nodeName === 'A' &&\n (node as Element).getAttribute('href')\n );\n },\n replacement: function (content: string, node: Node, options: TurnishOptions): string {\n const self = referenceLinkRule;\n\n const href = (node as Element).getAttribute('href');\n let title: string;\n const titleAttr = (node as Element).getAttribute('title');\n if (titleAttr) {\n const sanitizedTitle = sanitizedLinkTitle(titleAttr);\n title = ' \"' + sanitizedTitle + '\"';\n } else {\n title = '';\n }\n const referenceKey = href + title;\n\n let replacement: string;\n let reference: string;\n switch (options.linkReferenceStyle) {\n case 'collapsed':\n replacement = '[' + content + '][]';\n reference = '[' + content + ']: ' + referenceKey;\n break;\n case 'shortcut':\n replacement = '[' + content + ']';\n reference = '[' + content + ']: ' + referenceKey;\n break;\n default: {\n let id: number;\n const existingKey = self.urlReferenceIdMap.get(referenceKey);\n if (options.linkReferenceDeduplication === 'full' && existingKey) {\n id = existingKey;\n reference = '[' + id + ']: ' + href + title;\n } else {\n id = self.references.length + 1;\n self.urlReferenceIdMap.set(referenceKey, id);\n reference = '[' + id + ']: ' + href + title;\n self.references.push(reference);\n }\n replacement = '[' + content + '][' + id + ']';\n break;\n }\n }\n\n if (options.linkReferenceStyle !== 'full') {\n // Check if we should deduplicate\n if (options.linkReferenceDeduplication === 'full') {\n if (!self.urlReferenceIdMap.has(referenceKey)) {\n self.urlReferenceIdMap.set(referenceKey, 1);\n self.references.push(reference);\n }\n } else {\n self.references.push(reference);\n }\n }\n return replacement;\n },\n references: [],\n urlReferenceIdMap: new Map<string, number>(),\n append: (): string => {\n const self = referenceLinkRule;\n let references = '';\n if (self.references && self.references.length) {\n references = '\\n\\n' + self.references.join('\\n') + '\\n\\n';\n self.references = [];\n self.urlReferenceIdMap = new Map();\n }\n return references;\n }\n};\n\ndefaultRules.referenceLink = referenceLinkRule;\n\ndefaultRules.emphasis = {\n filter: ['em', 'i'],\n replacement: (content: string, _node: Node, options: TurnishOptions): string => {\n content = content.trim();\n if (!content) { return ''; }\n return options.emDelimiter + content + options.emDelimiter;\n }\n};\n\ndefaultRules.strong = {\n filter: ['strong', 'b'],\n replacement: (content: string, _node: Node, options: TurnishOptions): string => {\n content = content.trim();\n if (!content) { return ''; }\n return options.strongDelimiter + content + options.strongDelimiter;\n }\n};\n\ndefaultRules.code = {\n filter: (node: Node): boolean => {\n const hasSiblings = node.previousSibling || node.nextSibling;\n const parent = node.parentNode as Element;\n const isCodeBlock = parent.nodeName === 'PRE' && !hasSiblings;\n return node.nodeName === 'CODE' && !isCodeBlock;\n },\n replacement: (content: string): string => {\n const trimmed = content.replace(/\\r?\\n|\\r/g, ' ');\n const extraSpace = /^`|^ .*?[^ ].* $|`$/.test(trimmed) ? ' ' : '';\n let delimiter = '`';\n const matches: string[] = trimmed.match(/`+/gm) || [];\n while (matches.includes(delimiter)) delimiter = delimiter + '`';\n return delimiter + extraSpace + trimmed + extraSpace + delimiter;\n }\n};\n\ndefaultRules.image = {\n filter: 'img',\n replacement: function (_content: string, node: Node): string {\n const altAttr = (node as Element).getAttribute('alt');\n const alt = altAttr ? sanitizedLinkTitle(altAttr) : '';\n const src = (node as Element).getAttribute('src') || '';\n const titleAttr = (node as Element).getAttribute('title');\n const title = titleAttr ? sanitizedLinkTitle(titleAttr) : '';\n const titlePart = title ? ' \"' + title + '\"' : '';\n return src ? '![' + alt + ']' + '(' + src + titlePart + ')' : '';\n }\n};\n","/**\n * Manages a collection of rules used to convert HTML to Markdown\n */\nimport { ExtendedNode } from \"./node\";\nimport { TurnishOptions } from \"@/index\";\nimport { standardMarkdownElements } from \"./utilities\";\n\nexport type RuleFilterFunction = (node: ExtendedNode, options: TurnishOptions) => boolean;\nexport type RuleFilter = string | string[] | RuleFilterFunction;\n\ntype RuleReplacementFunction = (...args: any[]) => string;\n\nexport interface Rule {\n filter?: RuleFilter;\n replacement: RuleReplacementFunction | ((content: string, node: any, options: TurnishOptions, previousNode?: any) => string);\n references?: string[];\n /// Map of URL+title combinations to their reference IDs, used for link reference deduplication.\n /// When linkReferenceDeduplication is 'full', this tracks which URLs have already been assigned a reference number to avoid creating duplicate references.\n urlReferenceIdMap?: Map<string, number>;\n append?: (options: TurnishOptions) => string;\n}\n\nexport class Rules {\n options: TurnishOptions;\n private _keep: Rule[];\n private _remove: Rule[];\n blankRule: Rule;\n keepReplacement: RuleReplacementFunction;\n markdownIncludingHtmlReplacement: RuleReplacementFunction;\n defaultRule: Rule;\n array: Rule[];\n\n constructor(options: TurnishOptions) {\n this.options = options;\n this._keep = [];\n this._remove = [];\n\n this.blankRule = {\n replacement: options.blankReplacement\n };\n\n this.keepReplacement = options.keepReplacement;\n this.markdownIncludingHtmlReplacement = options.markdownIncludingHtmlReplacement;\n\n this.defaultRule = {\n replacement: options.defaultReplacement\n };\n\n this.array = [];\n for (const key in options.rules) {\n this.array.push(options.rules[key]);\n }\n }\n\n add(key: string, rule: Rule): void {\n this.array.unshift(rule);\n }\n\n keep(filter: RuleFilter): void {\n this._keep.unshift({\n filter: filter,\n replacement: this.keepReplacement\n });\n }\n\n remove(filter: RuleFilter): void {\n this._remove.unshift({\n filter: filter,\n replacement: function () {\n return '';\n }\n });\n }\n\n forNode(node: ExtendedNode): Rule {\n if (node.isBlank) {\n return this.blankRule;\n }\n if (this.options.htmlRetentionMode === 'preserveAll' && this.isUnsupportedElement(node)) {\n return {\n replacement: this.keepReplacement\n };\n }\n if (this.options.htmlRetentionMode === 'markdownIncludingHtml' && this.isUnsupportedElement(node)) {\n return {\n replacement: this.markdownIncludingHtmlReplacement\n };\n }\n\n let rule: Rule | undefined;\n if ((rule = findRule(this.array, node, this.options))) {\n return rule;\n }\n if ((rule = findRule(this._keep, node, this.options))) {\n return rule;\n }\n if ((rule = findRule(this._remove, node, this.options))) {\n return rule;\n }\n return this.defaultRule;\n }\n\n /// Check if an element is unsupported for Markdown conversion.\n private isUnsupportedElement(node: ExtendedNode): boolean {\n const nodeName = node.nodeName;\n\n if (nodeName === 'PRE' && node.firstChild && (node.firstChild as Element).nodeName === 'CODE') {\n const codeElem = node.firstChild as Element;\n if (codeElem.attributes && codeElem.attributes.length > 0) {\n for (let i = 0; i < codeElem.attributes.length; i++) {\n const attrName = codeElem.attributes[i].name.toLowerCase();\n if (attrName !== 'class') {\n return true;\n }\n }\n }\n }\n\n if (node.attributes && node.attributes.length > 0) {\n switch (nodeName) {\n case 'IMG':\n for (let i = 0; i < node.attributes.length; i++) {\n const attrName = node.attributes[i].name.toLowerCase();\n if (attrName !== 'src' && attrName !== 'alt' && attrName !== 'title') {\n return true;\n }\n }\n return false;\n case 'A':\n for (let i = 0; i < node.attributes.length; i++) {\n const attrName = node.attributes[i].name.toLowerCase();\n if (attrName !== 'href' && attrName !== 'title') {\n return true;\n }\n }\n return false;\n case 'CODE':\n const parent = node.parentNode as Element;\n if (parent && parent.nodeName === 'PRE') {\n for (let i = 0; i < node.attributes.length; i++) {\n const attrName = node.attributes[i].name.toLowerCase();\n if (attrName !== 'class') {\n return true;\n }\n }\n return false;\n }\n default:\n return true;\n }\n }\n // Elements that are not standard HTML elements are unsupported\n if (standardMarkdownElements.indexOf(nodeName) === -1) {\n return true;\n }\n return false;\n }\n\n forEach(fn: (rule: Rule, index: number) => void): void {\n for (let i = 0; i < this.array.length; i++) {\n fn(this.array[i], i);\n }\n }\n}\n\nfunction findRule(rules: Rule[], node: ExtendedNode, options: TurnishOptions): Rule | undefined {\n for (let i = 0; i < rules.length; i++) {\n const rule = rules[i];\n if (filterValue(rule, node, options)) return rule;\n }\n return undefined;\n}\n\nfunction filterValue(rule: Rule, node: ExtendedNode, options: TurnishOptions): boolean {\n const filter = rule.filter;\n if (typeof filter === 'string') {\n if (filter === node.nodeName.toLowerCase()) {\n return true;\n }\n } else if (Array.isArray(filter)) {\n if (filter.indexOf(node.nodeName.toLowerCase()) > -1) {\n return true;\n }\n } else if (typeof filter === 'function') {\n if (filter(node, options)) {\n return true;\n }\n } else {\n throw new TypeError('`filter` needs to be a string, array, or function');\n }\n return false;\n}\n","/**\n * The collapseWhitespace function is adapted from collapse-whitespace\n * by Luc Thevenard.\n *\n * The MIT License (MIT)\n *\n * Copyright (c) 2014 Luc Thevenard <lucthevenard@gmail.com>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\nimport { NodeTypes } from \"./node\";\n\n/**\n * collapseWhitespace(options) removes extraneous whitespace from an the given element.\n *\n * @param {Object} options\n */\ninterface CollapseWhitespaceOptions {\n element: Node;\n isBlock: (node: Node) => boolean;\n isVoid: (node: Node) => boolean;\n isPre?: (node: Node) => boolean;\n}\n\nfunction collapseWhitespace(options: CollapseWhitespaceOptions): void {\n const element = options.element\n const isBlock = options.isBlock\n const isVoid = options.isVoid\n const isPre = options.isPre || function (node: Node): boolean {\n return node.nodeName === 'PRE';\n };\n\n if (!element.firstChild || isPre(element)) return\n\n let prevText: Text | null = null;\n let keepLeadingWs = false\n\n let prev: Node | null = null;\n let node: Node = next(prev, element, isPre);\n\n while (node !== element) {\n if (node.nodeType === NodeTypes.Text || node.nodeType === NodeTypes.CDATASection) {\n const textNode = node as Text;\n let text = textNode.data.replace(/[ \\r\\n\\t]+/g, ' ');\n\n if ((!prevText || / $/.test(prevText.data)) &&\n !keepLeadingWs && text[0] === ' ') {\n text = text.substr(1);\n }\n\n // `text` might be empty at this point.\n if (!text) {\n node = remove(node);\n continue;\n }\n\n textNode.data = text;\n\n prevText = textNode;\n } else if (node.nodeType === NodeTypes.Element) {\n if (isBlock(node) || node.nodeName === 'BR') {\n if (prevText) {\n prevText.data = prevText.data.replace(/ $/, '');\n }\n\n prevText = null;\n keepLeadingWs = false;\n } else if (isVoid(node) || isPre(node)) {\n // Avoid trimming space around non-block, non-BR void elements and inline PRE.\n prevText = null;\n keepLeadingWs = true;\n } else if (prevText) {\n // Drop protection if set previously.\n keepLeadingWs = false;\n }\n } else {\n node = remove(node);\n continue;\n }\n\n const nextNode = next(prev, node, isPre);\n prev = node;\n node = nextNode;\n }\n\n if (prevText) {\n prevText.data = prevText.data.replace(/ $/, '');\n if (!prevText.data) {\n remove(prevText);\n }\n }\n}\n\n/**\n * remove(node) removes the given node from the DOM and returns the\n * next node in the sequence.\n *\n * @param {Node} node\n * @return {Node} node\n */\nfunction remove(node: Node): Node {\n const nextNode: Node | null = node.nextSibling ?? node.parentNode;\n if (node.parentNode) {\n node.parentNode.removeChild(node);\n }\n return nextNode as Node;\n}\n\n/**\n * next(prev, current, isPre) returns the next node in the sequence, given the\n * current and previous nodes.\n *\n * @param {Node} prev\n * @param {Node} current\n * @param {Function} isPre\n * @return {Node}\n */\nfunction next(prev: Node | null, current: Node, isPre: (node: Node) => boolean): Node {\n if ((prev && prev.parentNode === current) || isPre(current)) {\n const nextNode: Node | null = current.nextSibling ?? current.parentNode;\n return nextNode as Node;\n }\n const nextNode: Node | null = current.firstChild ?? current.nextSibling ?? current.parentNode;\n return nextNode as Node;\n}\n\nexport default collapseWhitespace\n","/*\n * Set up window for Node.js\n */\n\nconst root: typeof globalThis = typeof window !== 'undefined' ? window : (typeof globalThis !== 'undefined' ? globalThis : {} as any)\n\n/*\n * Parsing HTML strings\n */\n\nfunction canParseHTMLNatively() {\n const Parser = typeof root.DOMParser !== 'undefined' ? root.DOMParser : undefined;\n let canParse = false;\n if (!Parser) return false;\n // Adapted from https://gist.github.com/1129031\n // Firefox/Opera/IE throw errors on unsupported types\n try {\n // WebKit returns null on unsupported types\n if (new Parser().parseFromString('', 'text/html')) {\n canParse = true;\n }\n } catch (e) { }\n return canParse;\n}\n\nexport class HTMLParser {\n // This will be assigned per environment below\n parseFromString(_input: string, _type?: string): Document {\n throw new Error('Not implemented')\n }\n}\n\nfunction createParser(): HTMLParser {\n const isBrowser =\n typeof window !== 'undefined' &&\n typeof document !== 'undefined' &&\n (typeof process === 'undefined' || (process as any).browser === true)\n\n if (typeof window !== 'undefined') {\n // Browser environment: use DOM API\n class HTMLParserBrowser extends HTMLParser {\n parseFromString(input: string, _type?: string): Document {\n const doc = document.implementation.createHTMLDocument('')\n doc.open()\n doc.write(input)\n doc.close()\n return doc\n }\n }\n return new HTMLParserBrowser()\n } else {\n // Node environment: use domino\n const domino = require('@mixmark-io/domino') as {\n createDocument: (html: string) => Document\n }\n class HTMLParserNode extends HTMLParser {\n parseFromString(input: string, _type?: string): Document {\n return domino.createDocument(input);\n }\n }\n return new HTMLParserNode()\n }\n}\n\nexport const createHTMLParser = (): HTMLParser =>\n canParseHTMLNatively()\n ? new root.DOMParser()\n : createParser()\n","import collapseWhitespace from '@/collapse-whitespace'\nimport { createHTMLParser, HTMLParser } from '@/html-parser'\nimport { isBlock, isVoid } from '@/utilities'\n\ninterface RootNodeOptions {\n preformattedCode?: boolean\n}\n\nexport default function RootNode(\n input: string | Node,\n { preformattedCode }: RootNodeOptions\n): Element {\n let root: Element\n if (typeof input === 'string') {\n const doc = htmlParser().parseFromString(\n // DOM parsers arrange elements in the <head> and <body>.\n // Wrapping in a custom element ensures elements are reliably arranged in\n // a single element.\n '<x-turnish id=\"turnish-root\">' + input + '</x-turnish>',\n 'text/html'\n )\n root = doc.getElementById('turnish-root') as Element\n } else {\n root = input.cloneNode(true) as Element\n }\n collapseWhitespace({\n element: root,\n isBlock: isBlock,\n isVoid: isVoid,\n isPre: preformattedCode ? isPreOrCode : undefined\n })\n\n return root\n}\n\nlet _htmlParser: HTMLParser | undefined\nfunction htmlParser(): HTMLParser {\n return (_htmlParser ??= createHTMLParser())\n}\n\nfunction isPreOrCode(node: Node): boolean {\n return node.nodeName === 'PRE' || node.nodeName === 'CODE';\n}\n","import { defaultRules } from '@/default-rules'\nimport { Rules, Rule, RuleFilter } from '@/rules'\nimport { trimLeadingNewlines, trimTrailingNewlines } from '@/utilities'\nimport RootNode from '@/root-node'\nimport { ExtendedNode, NodeTypes } from '@/node';\nconst reduce = Array.prototype.reduce\n\ntype EscapeRule = [RegExp, string];\n\nconst escapes: EscapeRule[] = [\n [/\\\\/g, '\\\\\\\\'],\n [/\\*/g, '\\\\*'],\n [/_/g, '\\\\_'],\n [/^-/g, '\\\\-'],\n [/^\\+ /g, '\\\\+ '],\n [/^(=+)/g, '\\\\$1'],\n [/^(#{1,6}) /g, '\\\\$1 '],\n [/`/g, '\\\\`'],\n [/^~~~/g, '\\\\~~~'],\n [/\\[/g, '\\\\['],\n [/\\]/g, '\\\\]'],\n [/<([^>]*)>/g, '\\\\<$1\\\\>'],\n [/^>/g, '\\\\>'],\n [/^(\\d+)\\. /g, '$1\\\\. ']\n];\n\ntype Plugin = (service: Turnish) => void;\n\nexport interface TurnishOptions {\n rules: { [key: string]: Rule };\n headingStyle: 'setext' | 'atx';\n hr: string;\n bulletListMarker: '*' | '-' | '+';\n listMarkerSpaceCount: 1 | 2 | 3 | 4;\n listItemIndent: 'tab' | 'space';\n listItemIndentSpaceCount: 2 | 4;\n codeBlockStyle: 'indented' | 'fenced';\n fence: string;\n emDelimiter: '_' | '*';\n strongDelimiter: '**' | '__';\n linkStyle: 'inlined' | 'referenced';\n linkReferenceStyle: 'full' | 'collapsed' | 'shortcut';\n linkReferenceDeduplication: 'none' | 'full';\n br: string;\n preformattedCode: boolean;\n htmlRetentionMode: 'standard' | 'preserveAll' | 'markdownIncludingHtml';\n blankReplacement: (content: string, node: ExtendedNode) => string;\n keepReplacement: (content: string, node: ExtendedNode) => string;\n markdownIncludingHtmlReplacement: (content: string, node: ExtendedNode) => string;\n defaultReplacement: (content: string, node: ExtendedNode) => string;\n [key: string]: any;\n}\n\nconst defaultOptions: TurnishOptions = {\n rules: defaultRules,\n headingStyle: 'atx',\n hr: '---',\n bulletListMarker: '-',\n listMarkerSpaceCount: 1,\n listItemIndent: 'space',\n listItemIndentSpaceCount: 4,\n codeBlockStyle: 'fenced',\n fence: '```',\n emDelimiter: '*',\n strongDelimiter: '**',\n linkStyle: 'inlined',\n linkReferenceStyle: 'full',\n linkReferenceDeduplication: 'full',\n br: ' ',\n preformattedCode: false,\n htmlRetentionMode: 'standard',\n blankReplacement: (content: string, node: ExtendedNode): string => {\n return node.isBlock ? '\\n\\n' : '';\n },\n keepReplacement: (content: string, node: ExtendedNode): string => {\n return node.isBlock ? '\\n\\n' + node.outerHTML + '\\n\\n' : node.outerHTML;\n },\n markdownIncludingHtmlReplacement: (content: string, node: ExtendedNode): string => {\n const tagName = node.nodeName.toLowerCase();\n\n let attributes = '';\n if (node.attributes && node.attributes.length > 0) {\n const attrs: string[] = [];\n for (let i = 0; i < node.attributes.length; i++) {\n const attr = node.attributes[i];\n attrs.push(`${attr.name}=\"${attr.value}\"`);\n }\n attributes = ' ' + attrs.join(' ');\n }\n\n attributes += ' markdown=\"1\"';\n\n const openTag = `<${tagName}${attributes}>`;\n const closeTag = `</${tagName}>`;\n\n const trimmedContent = content.trim();\n const html = openTag + '\\n' + trimmedContent + '\\n' + closeTag;\n\n return node.isBlock ? '\\n\\n' + html + '\\n\\n' : html;\n },\n defaultReplacement: function (content: string, node: ExtendedNode): string {\n return node.isBlock ? '\\n\\n' + content + '\\n\\n' : content;\n }\n};\n\nexport default class Turnish {\n options: TurnishOptions;\n rules: Rules;\n\n constructor(options?: Partial<TurnishOptions>) {\n this.options = Object.assign({}, defaultOptions, options);\n this.rules = new Rules(this.options);\n }\n\n /**\n * The entry point for converting a string or DOM node to Markdown\n * @public\n * @param {InputType} input The string or DOM node to convert\n * @returns A Markdown representation of the input\n * @type string\n */\n render(input: InputType): string {\n if (!canConvert(input)) {\n throw new TypeError(\n input + ' is not a string, or an element/document/fragment node.'\n );\n }\n if (input === '') {\n return '';\n }\n const output = this.process(RootNode(input, this.options));\n return this.postProcess(output);\n }\n\n /**\n * Add one or more plugins\n * @public\n * @param {Plugin|Plugin[]} plugin The plugin or array of plugins to add\n * @returns The Turnish instance for chaining\n * @type Object\n */\n use(plugin: Plugin | Plugin[]): Turnish {\n if (Array.isArray(plugin)) {\n for (let i = 0; i < plugin.length; i++) this.use(plugin[i]);\n } else if (typeof plugin === 'function') {\n plugin(this);\n } else {\n throw new TypeError('plugin must be a Function or an Array of Functions');\n }\n return this;\n }\n\n /**\n * Adds a rule\n * @public\n * @param {string} key The unique key of the rule\n * @param {Object} rule The rule\n * @returns The Turnish instance for chaining\n * @type Object\n */\n addRule(key: string, rule: Rule): Turnish {\n this.rules.add(key, rule);\n return this;\n }\n\n /**\n * Keep a node (as HTML) that matches the filter\n * @public\n * @param {RuleFilter} filter The unique key of the rule\n * @returns The Turnish instance for chaining\n * @type Object\n */\n keep(filter: RuleFilter): Turnish {\n this.rules.keep(filter);\n return this;\n }\n\n /**\n * Remove a node that matches the filter\n * @public\n * @param {string|Array|Function} filter The unique key of the rule\n * @returns The Turnish instance for chaining\n * @type Object\n */\n remove(filter: RuleFilter): Turnish {\n this.rules.remove(filter);\n return this;\n }\n\n /**\n * Escapes Markdown syntax\n * @public\n * @param {string} string The string to escape\n * @returns A string with Markdown syntax escaped\n * @type string\n */\n escape(string: string): string {\n return escapes.reduce(function (accumulator: string, escape: EscapeRule) {\n return accumulator.replace(escape[0], escape[1]);\n }, string);\n }\n\n\n /**\n * Reduces a DOM node down to its Markdown string equivalent\n * @private\n * @param {HTMLElement} parentNode The node to convert\n * @returns A Markdown representation of the node\n * @type string\n */\n process(this: Turnish, parentNode: Node): string {\n return Array.from(parentNode.childNodes).reduce((output, node) => {\n const extended = ExtendedNode(node, this.options);\n let replacement = '';\n if (extended.nodeType === NodeTypes.Text) {\n let value = extended.nodeValue ?? '';\n // Special handling for text nodes following void elements. Void element replacements (e.g., from GFM plugin for checkboxes) may add trailing whitespace, so we should remove leading whitespace from the following text node.\n if (node.previousSibling && node.previousSibling.nodeType === NodeTypes.Element) {\n const prevElement = node.previousSibling as Element;\n if (prevElement.nodeName === 'INPUT' && prevElement.getAttribute('type') === 'checkbox') {\n value = value.replace(/^\\s+/, '');\n }\n }\n replacement = extended.isCode ? value : this.escape(value);\n } else if (extended.nodeType === NodeTypes.Element) {\n replacement = this.replacementForNode(extended);\n }\n return join(output, replacement);\n }, '');\n }\n\n /**\n * Appends strings as each rule requires and trims the output\n * @private\n * @param {string} output The conversion output\n * @returns A trimmed version of the ouput\n * @type string\n */\n postProcess(output: string): string {\n for (const rule of this.rules.array) {\n if (rule.append) {\n output = join(output, rule.append(this.options))\n }\n }\n return output\n .replace(/^[\\t\\r\\n]+/, '')\n .replace(/[\\t\\r\\n\\s]+$/, '')\n }\n\n\n /**\n * Converts an element node to its Markdown equivalent\n * @private\n * @param {ExtendedNode} node The node to convert\n * @returns A Markdown representation of the node\n * @type string\n */\n replacementForNode(node: ExtendedNode) {\n const rule = this.rules.forNode(node)\n let content = this.process(node)\n const whitespace = node.flankingWhitespace\n if (whitespace.leading || whitespace.trailing) {\n content = content.trim()\n }\n return (\n whitespace.leading +\n rule.replacement(content, node, this.options) +\n whitespace.trailing\n )\n }\n}\n\n/**\n * Joins replacement to the current output with appropriate number of new lines\n * @private\n * @param {string} output The current conversion output\n * @param {string} replacement The string to append to the output\n * @returns Joined output\n * @type string\n */\nfunction join(output: string, replacement: string): string {\n const s1 = trimTrailingNewlines(output)\n const s2 = trimLeadingNewlines(replacement)\n const nls = Math.max(output.length - s1.length, replacement.length - s2.length)\n const separator = '\\n\\n'.substring(0, nls)\n\n return s1 + separator + s2\n}\n\n/**\n * Determines whether an input can be converted\n * @private\n * @param {string|HTMLElement} input Describe this parameter\n * @returns Describe what it returns\n * @type string|Object|Array|Boolean|Number\n */\ntype InputType = string | HTMLElement | Document | DocumentFragment;\nfunction canConvert(input: any): input is InputType {\n return (\n input != null && (\n typeof input === 'string' ||\n (input.nodeType && (\n input.nodeType === 1 || input.nodeType === 9 || input.nodeType === 11\n ))\n )\n );\n}\n"],"names":["ExtendedNode","node","options","extended","isBlock","isBlank","flankingWhitespace","isVoid","isMeaningfulWhenBlank","hasVoid","hasMeaningfulWhenBlank","extendedNode","edges","edgeWhitespace","isFlankedByWhitespace","string","m","side","sibling","regExp","isFlanked","NodeTypes","repeat","character","count","trimLeadingNewlines","trimTrailingNewlines","indexEnd","trimNewlines","blockElements","is","voidElements","has","meaningfulWhenBlankElements","standardMarkdownElements","tagNames","tagName","sanitizeWhitespace","sanitizedLinkContent","content","sanitizedLinkTitle","defaultRules","_content","_node","hLevel","underline","parent","prefix","start","index","isParagraph","child","nestingLevel","currentNode","grandparent","oneIndent","indent","listMarkerRegex","codeElem","language","code","fenceChar","fenceSize","fenceInCodeRegex","match","fence","sanitizedContent","href","title","titleAttr","referenceLinkRule","self","referenceKey","replacement","reference","id","existingKey","references","hasSiblings","isCodeBlock","trimmed","extraSpace","delimiter","matches","altAttr","alt","src","titlePart","Rules","key","rule","filter","findRule","nodeName","attrName","fn","i","rules","filterValue","collapseWhitespace","element","isPre","prevText","keepLeadingWs","prev","next","textNode","text","remove","nextNode","current","root","canParseHTMLNatively","Parser","canParse","HTMLParser","_input","_type","createParser","HTMLParserBrowser","input","doc","domino","HTMLParserNode","createHTMLParser","RootNode","preformattedCode","htmlParser","isPreOrCode","_htmlParser","escapes","defaultOptions","attributes","attrs","attr","openTag","closeTag","trimmedContent","html","Turnish","canConvert","output","plugin","accumulator","escape","parentNode","value","prevElement","join","whitespace","s1","s2","nls","separator"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.js","sources":["../src/node.ts","../src/utilities.ts","../src/default-rules.ts","../src/rules.ts","../src/collapse-whitespace.ts","../src/html-parser.ts","../src/root-node.ts","../src/index.ts"],"sourcesContent":["import { isBlock, isVoid, hasVoid, isMeaningfulWhenBlank, hasMeaningfulWhenBlank } from '@/utilities'\n\ninterface Options {\n preformattedCode?: boolean;\n}\n\ninterface FlankingWhitespace {\n leading: string;\n trailing: string;\n}\n\ninterface EdgeWhitespace extends FlankingWhitespace {\n leadingAscii: string;\n leadingNonAscii: string;\n trailingNonAscii: string;\n trailingAscii: string;\n}\n\nexport interface ExtendedNode extends Element {\n isBlock: boolean;\n isCode: boolean;\n isBlank: boolean;\n flankingWhitespace: FlankingWhitespace;\n}\n\nexport function ExtendedNode(node: Node, options: Options): ExtendedNode {\n const extended = node as ExtendedNode;\n extended.isBlock = isBlock(extended);\n extended.isCode = extended.nodeName === 'CODE' || (extended.parentNode as ExtendedNode)?.isCode;\n extended.isBlank = isBlank(extended);\n extended.flankingWhitespace = flankingWhitespace(extended, options);\n return extended;\n}\n\nfunction isBlank(node: ExtendedNode): boolean {\n return (\n !isVoid(node) &&\n !isMeaningfulWhenBlank(node) &&\n /^\\s*$/i.test(node.textContent || '') &&\n !hasVoid(node) &&\n !hasMeaningfulWhenBlank(node)\n )\n}\n\nfunction flankingWhitespace(node: ExtendedNode, options: Options): FlankingWhitespace {\n const extendedNode = node as ExtendedNode;\n\n if (extendedNode.isBlock || (options.preformattedCode && extendedNode.isCode)) {\n return { leading: '', trailing: '' };\n }\n\n const edges = edgeWhitespace(node.textContent || '');\n\n // abandon leading ASCII WS if left-flanked by ASCII WS\n if (edges.leadingAscii && isFlankedByWhitespace('left', node, options)) {\n edges.leading = edges.leadingNonAscii;\n }\n\n // abandon trailing ASCII WS if right-flanked by ASCII WS\n if (edges.trailingAscii && isFlankedByWhitespace('right', node, options)) {\n edges.trailing = edges.trailingNonAscii;\n }\n\n return { leading: edges.leading, trailing: edges.trailing };\n}\n\nfunction edgeWhitespace(string: string): EdgeWhitespace {\n const m = string.match(/^(([ \\t\\r\\n]*)(\\s*))(?:(?=\\S)[\\s\\S]*\\S)?((\\s*?)([ \\t\\r\\n]*))$/);\n\n if (!m) {\n return {\n leading: '',\n leadingAscii: '',\n leadingNonAscii: '',\n trailing: '',\n trailingNonAscii: '',\n trailingAscii: ''\n };\n }\n\n return {\n leading: m[1], // whole string for whitespace-only strings\n leadingAscii: m[2],\n leadingNonAscii: m[3],\n trailing: m[4], // empty for whitespace-only strings\n trailingNonAscii: m[5],\n trailingAscii: m[6]\n };\n}\n\nfunction isFlankedByWhitespace(side: 'left' | 'right', node: Element, options: Options): boolean {\n let sibling: Node | null;\n let regExp: RegExp;\n let isFlanked: boolean | undefined;\n\n if (side === 'left') {\n sibling = node.previousSibling;\n regExp = / $/;\n } else {\n sibling = node.nextSibling;\n regExp = /^ /;\n }\n\n if (sibling) {\n if (sibling.nodeType === 3) {\n isFlanked = regExp.test(sibling.nodeValue || '');\n } else if (options.preformattedCode && sibling.nodeName === 'CODE') {\n isFlanked = false;\n } else if (sibling.nodeType === 1 && !isBlock(sibling)) {\n isFlanked = regExp.test(sibling.textContent || '');\n }\n }\n\n return isFlanked || false;\n}\n\nexport const NodeTypes = {\n Element: 1,\n Text: 3,\n CDATASection: 4,\n Comment: 8\n} as const;\n\nexport type NodeType = typeof NodeTypes[keyof typeof NodeTypes];\n","import { ExtendedNode, NodeTypes } from \"./node\";\n\nexport function repeat(character: string, count: number) {\n return Array(count + 1).join(character)\n}\n\nexport function trimLeadingNewlines(string: string) {\n return string.replace(/^\\n*/, '')\n}\n\nexport function trimTrailingNewlines(string: string) {\n // avoid match-at-end regexp bottleneck, see #370\n let indexEnd = string.length\n while (indexEnd > 0 && string[indexEnd - 1] === '\\n') indexEnd--\n return string.substring(0, indexEnd)\n}\n\nexport function trimNewlines(string: string) {\n return trimTrailingNewlines(trimLeadingNewlines(string))\n}\n\nexport const blockElements = [\n 'ADDRESS', 'ARTICLE', 'ASIDE', 'AUDIO', 'BLOCKQUOTE', 'BODY', 'CANVAS',\n 'CENTER', 'DD', 'DIR', 'DIV', 'DL', 'DT', 'FIELDSET', 'FIGCAPTION', 'FIGURE',\n 'FOOTER', 'FORM', 'FRAMESET', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEADER',\n 'HGROUP', 'HR', 'HTML', 'ISINDEX', 'LI', 'MAIN', 'MENU', 'NAV', 'NOFRAMES',\n 'NOSCRIPT', 'OL', 'OUTPUT', 'P', 'PRE', 'SECTION', 'TABLE', 'TBODY', 'TD',\n 'TFOOT', 'TH', 'THEAD', 'TR', 'UL'\n]\n\nexport function isBlock(node: Node) {\n return is(node, blockElements)\n}\n\nexport const voidElements = [\n 'AREA', 'BASE', 'BR', 'COL', 'COMMAND', 'EMBED', 'HR', 'IMG', 'INPUT',\n 'KEYGEN', 'LINK', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR'\n]\n\nexport function isVoid(node: Node) {\n return is(node, voidElements)\n}\n\nexport function hasVoid(node: Node) {\n return has(node, voidElements)\n}\n\nconst meaningfulWhenBlankElements = [\n 'A', 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TH', 'TD', 'IFRAME', 'SCRIPT',\n 'AUDIO', 'VIDEO'\n]\n\nexport const standardMarkdownElements = [\n 'P', 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'BLOCKQUOTE',\n 'UL', 'OL', 'LI', 'PRE', 'CODE', 'HR', 'A', 'EM', 'I', 'STRONG',\n 'B', 'IMG', 'DIV', 'SPAN', 'TABLE', 'THEAD', 'TBODY', 'TR', 'TH', 'TD'\n]\n\nexport function isMeaningfulWhenBlank(node: Node) {\n return is(node, meaningfulWhenBlankElements)\n}\n\nexport function hasMeaningfulWhenBlank(node: Node) {\n return has(node, meaningfulWhenBlankElements)\n}\n\nfunction is(node: Node, tagNames: string[]) {\n return tagNames.indexOf(node.nodeName) >= 0\n}\n\nfunction has(node: Node, tagNames: string[]) {\n return (\n tagNames.some(function (tagName) {\n if (node.nodeType !== NodeTypes.Element) {\n return false;\n }\n return (node as Element).getElementsByTagName(tagName).length\n })\n )\n}\n\nexport function sanitizeWhitespace(string: string): string {\n return string ? string.replace(/(\\n+\\s*)+/g, '\\n') : '';\n}\n\nexport function sanitizedLinkContent(content: string): string {\n const sanitized = sanitizeWhitespace(content);\n return sanitized\n .replace(/[\\t\\r\\n]+/g, ' ')\n .replace(/ {2,}/g, ' ')\n .trim();\n}\n\nexport function sanitizedLinkTitle(content: string): string {\n const sanitized = sanitizeWhitespace(content);\n return sanitized\n .replace(/[\\t\\r\\n]+/g, ' ');\n}\n\nexport type RequireOnly<T, K extends keyof T> =\n T & Required<Pick<T, K>>;\n","\nimport { Rule } from '@/rules';\nimport { TurnishOptions } from '@/index';\nimport { repeat, RequireOnly, sanitizedLinkContent, sanitizedLinkTitle, trimNewlines } from '@/utilities';\nimport { NodeTypes } from './node';\n\nexport const defaultRules: { [key: string]: Rule } = {}\n\ndefaultRules.paragraph = {\n filter: 'p',\n replacement: function (content: string): string {\n return '\\n\\n' + content + '\\n\\n';\n }\n};\n\ndefaultRules.lineBreak = {\n filter: 'br',\n replacement: function (_content: string, _node: Node, options: TurnishOptions): string {\n return options.br + '\\n';\n }\n};\n\ndefaultRules.heading = {\n filter: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],\n replacement: function (content: string, node: Node, options: TurnishOptions): string {\n const hLevel = Number(node.nodeName.charAt(1));\n if (options.headingStyle === 'setext' && hLevel < 3) {\n const underline = repeat((hLevel === 1 ? '=' : '-'), content.length);\n return (\n '\\n\\n' + content + '\\n' + underline + '\\n\\n'\n );\n } else {\n return '\\n\\n' + repeat('#', hLevel) + ' ' + content + '\\n\\n';\n }\n }\n};\n\ndefaultRules.blockquote = {\n filter: 'blockquote',\n replacement: function (content: string): string {\n content = trimNewlines(content).replace(/^/gm, '> ');\n return '\\n\\n' + content + '\\n\\n';\n }\n};\n\ndefaultRules.list = {\n filter: ['ul', 'ol'],\n replacement: function (content: string, node: Node): string {\n const parent = node.parentNode as Element;\n if (parent.nodeName === 'LI' && parent.lastElementChild === node) {\n return '\\n' + content;\n } else {\n return '\\n\\n' + content + '\\n\\n';\n }\n }\n};\n\ndefaultRules.listItem = {\n filter: 'li',\n replacement: function (content: string, node: Node, options: TurnishOptions): string {\n let prefix = options.bulletListMarker + ' '.repeat(options.listMarkerSpaceCount);\n const parent = node.parentNode as Element;\n if (parent.nodeName === 'OL') {\n const start = parent.getAttribute('start');\n const index = Array.prototype.indexOf.call(parent.children, node);\n prefix = (start ? Number(start) + index : index + 1) + '.' + ' '.repeat(options.listMarkerSpaceCount);\n }\n const isParagraph = /\\n$/.test(content);\n content = trimNewlines(content) + (isParagraph ? '\\n' : '');\n\n const hasOnlyNestedList = node.childNodes.length > 0 &&\n Array.from(node.childNodes).every((child: Node) => {\n return (child.nodeType === NodeTypes.Text && /^\\s*$/.test(child.nodeValue || ''))\n || (child.nodeType === NodeTypes.Element && ['UL', 'OL'].includes(child.nodeName));\n });\n if (hasOnlyNestedList && content.trim() !== '') {\n // This list item only contains a nested list, don't duplicate marker\n return content + (node.nextSibling ? '\\n' : '');\n }\n\n let nestingLevel = 0;\n let currentNode: Node | null = parent;\n while (currentNode) {\n if (currentNode.nodeName === 'UL' || currentNode.nodeName === 'OL') {\n const grandparent = currentNode.parentNode as Element | null;\n if (grandparent && grandparent.nodeName === 'LI') {\n nestingLevel++;\n }\n }\n currentNode = currentNode.parentNode;\n }\n\n let oneIndent = options.listItemIndent === 'tab' ? '\\t' : ' '.repeat(options.listItemIndentSpaceCount);\n let indent = oneIndent.repeat(nestingLevel);\n const listMarkerRegex = /\\n(?!\\s*(?:\\d+\\.\\s|[-+*]\\s))/gm;\n content = content.replace(listMarkerRegex, '\\n' + oneIndent);\n return indent + prefix + content + (node.nextSibling ? '\\n' : '');\n }\n};\n\ndefaultRules.indentedCodeBlock = {\n filter: function (node: Node, options: TurnishOptions): boolean {\n return !!(\n options &&\n options.codeBlockStyle === 'indented' &&\n node.nodeName === 'PRE' &&\n node.firstChild &&\n (node.firstChild as Element).nodeName === 'CODE'\n );\n },\n replacement: function (_content: string, node: Node): string {\n if (!node || !node.firstChild) return '';\n return (\n '\\n\\n ' +\n (node.firstChild as Element).textContent!.replace(/\\n/g, '\\n ') +\n '\\n\\n'\n );\n }\n};\n\ndefaultRules.fencedCodeBlock = {\n filter: function (node: Node, options: TurnishOptions): boolean {\n return !!(\n options &&\n options.codeBlockStyle === 'fenced' &&\n node.nodeName === 'PRE'\n );\n },\n replacement: function (_content: string, node: Node, options: TurnishOptions): string {\n const preNode = node as Element;\n const firstChild = preNode.firstChild;\n const codeElem = (firstChild && firstChild.nodeName === 'CODE') ? firstChild as Element : preNode;\n\n const className = codeElem.getAttribute('class') || '';\n const language = (className.match(/(?:lang|language)-(\\S+)/) || [null, ''])[1];\n const code = codeElem.textContent || '';\n const fenceChar = options.fence?.charAt(0) || '`';\n let fenceSize = 3;\n const fenceInCodeRegex = new RegExp('^' + fenceChar + '{3,}', 'gm');\n let match;\n while ((match = fenceInCodeRegex.exec(code))) {\n if (match[0].length >= fenceSize) {\n fenceSize = match[0].length + 1;\n }\n }\n const fence = repeat(fenceChar, fenceSize);\n return (\n '\\n\\n' + fence + language + '\\n' +\n code.replace(/\\n$/, '') +\n '\\n' + fence + '\\n\\n'\n );\n }\n};\n\ndefaultRules.horizontalRule = {\n filter: 'hr',\n replacement: function (_content: string, _node: Node, options: TurnishOptions): string {\n return '\\n\\n' + options.hr + '\\n\\n';\n }\n};\n\ndefaultRules.inlineLink = {\n filter: function (node: Node, options: TurnishOptions): boolean {\n return !!(\n options?.linkStyle === 'inlined' &&\n node.nodeName === 'A' &&\n (node as Element).getAttribute('href')\n );\n },\n replacement: function (content: string, node: Node): string {\n const sanitizedContent = sanitizedLinkContent(content);\n let href = (node as Element)\n .getAttribute('href')\n ?.replace(/([()])/g, '\\\\$1');\n let title: string;\n const titleAttr = (node as Element).getAttribute('title');\n if (titleAttr) {\n const sanitizedTitle = sanitizedLinkTitle(titleAttr);\n title = ' \"' + sanitizedTitle.replace(/\"/g, '\\\\\"') + '\"';\n } else {\n title = '';\n }\n return '[' + sanitizedContent + '](' + href + title + ')';\n }\n};\n\nconst referenceLinkRule: RequireOnly<Rule, \"urlReferenceIdMap\" | \"references\"> = {\n filter: function (node: Node, options: TurnishOptions): boolean {\n return !!(\n options &&\n options.linkStyle === 'referenced' &&\n node.nodeName === 'A' &&\n (node as Element).getAttribute('href')\n );\n },\n replacement: function (content: string, node: Node, options: TurnishOptions): string {\n const self = referenceLinkRule;\n\n const href = (node as Element).getAttribute('href');\n let title: string;\n const titleAttr = (node as Element).getAttribute('title');\n if (titleAttr) {\n const sanitizedTitle = sanitizedLinkTitle(titleAttr);\n title = ' \"' + sanitizedTitle + '\"';\n } else {\n title = '';\n }\n const referenceKey = href + title;\n\n let replacement: string;\n let reference: string;\n switch (options.linkReferenceStyle) {\n case 'collapsed':\n replacement = '[' + content + '][]';\n reference = '[' + content + ']: ' + referenceKey;\n break;\n case 'shortcut':\n replacement = '[' + content + ']';\n reference = '[' + content + ']: ' + referenceKey;\n break;\n default: {\n let id: number;\n const existingKey = self.urlReferenceIdMap.get(referenceKey);\n if (options.linkReferenceDeduplication === 'full' && existingKey) {\n id = existingKey;\n reference = '[' + id + ']: ' + href + title;\n } else {\n id = self.references.length + 1;\n self.urlReferenceIdMap.set(referenceKey, id);\n reference = '[' + id + ']: ' + href + title;\n self.references.push(reference);\n }\n replacement = '[' + content + '][' + id + ']';\n break;\n }\n }\n\n if (options.linkReferenceStyle !== 'full') {\n // Check if we should deduplicate\n if (options.linkReferenceDeduplication === 'full') {\n if (!self.urlReferenceIdMap.has(referenceKey)) {\n self.urlReferenceIdMap.set(referenceKey, 1);\n self.references.push(reference);\n }\n } else {\n self.references.push(reference);\n }\n }\n return replacement;\n },\n references: [],\n urlReferenceIdMap: new Map<string, number>(),\n append: (): string => {\n const self = referenceLinkRule;\n let references = '';\n if (self.references && self.references.length) {\n references = '\\n\\n' + self.references.join('\\n') + '\\n\\n';\n self.references = [];\n self.urlReferenceIdMap = new Map();\n }\n return references;\n }\n};\n\ndefaultRules.referenceLink = referenceLinkRule;\n\ndefaultRules.emphasis = {\n filter: ['em', 'i'],\n replacement: (content: string, _node: Node, options: TurnishOptions): string => {\n content = content.trim();\n if (!content) { return ''; }\n return options.emDelimiter + content + options.emDelimiter;\n }\n};\n\ndefaultRules.strong = {\n filter: ['strong', 'b'],\n replacement: (content: string, _node: Node, options: TurnishOptions): string => {\n content = content.trim();\n if (!content) { return ''; }\n return options.strongDelimiter + content + options.strongDelimiter;\n }\n};\n\ndefaultRules.code = {\n filter: (node: Node): boolean => {\n const hasSiblings = node.previousSibling || node.nextSibling;\n const parent = node.parentNode as Element;\n const isCodeBlock = parent.nodeName === 'PRE' && !hasSiblings;\n return node.nodeName === 'CODE' && !isCodeBlock;\n },\n replacement: (content: string): string => {\n const trimmed = content.replace(/\\r?\\n|\\r/g, ' ');\n const extraSpace = /^`|^ .*?[^ ].* $|`$/.test(trimmed) ? ' ' : '';\n let delimiter = '`';\n const matches: string[] = trimmed.match(/`+/gm) || [];\n while (matches.includes(delimiter)) delimiter = delimiter + '`';\n return delimiter + extraSpace + trimmed + extraSpace + delimiter;\n }\n};\n\ndefaultRules.image = {\n filter: 'img',\n replacement: function (_content: string, node: Node): string {\n const altAttr = (node as Element).getAttribute('alt');\n const alt = altAttr ? sanitizedLinkTitle(altAttr) : '';\n const src = (node as Element).getAttribute('src') || '';\n const titleAttr = (node as Element).getAttribute('title');\n const title = titleAttr ? sanitizedLinkTitle(titleAttr) : '';\n const titlePart = title ? ' \"' + title + '\"' : '';\n return src ? '![' + alt + ']' + '(' + src + titlePart + ')' : '';\n }\n};\n","/**\n * Manages a collection of rules used to convert HTML to Markdown\n */\nimport { ExtendedNode } from \"./node\";\nimport { TurnishOptions } from \"@/index\";\nimport { standardMarkdownElements } from \"./utilities\";\n\nexport type RuleFilterFunction = (node: ExtendedNode, options: TurnishOptions) => boolean;\nexport type RuleFilter = string | string[] | RuleFilterFunction;\n\ntype RuleReplacementFunction = (...args: any[]) => string;\n\nexport interface Rule {\n filter?: RuleFilter;\n replacement: RuleReplacementFunction | ((content: string, node: any, options: TurnishOptions, previousNode?: any) => string);\n references?: string[];\n /// Map of URL+title combinations to their reference IDs, used for link reference deduplication.\n /// When linkReferenceDeduplication is 'full', this tracks which URLs have already been assigned a reference number to avoid creating duplicate references.\n urlReferenceIdMap?: Map<string, number>;\n append?: (options: TurnishOptions) => string;\n}\n\nexport class Rules {\n options: TurnishOptions;\n private _keep: Rule[];\n private _remove: Rule[];\n blankRule: Rule;\n keepReplacement: RuleReplacementFunction;\n markdownIncludingHtmlReplacement: RuleReplacementFunction;\n defaultRule: Rule;\n array: Rule[];\n\n constructor(options: TurnishOptions) {\n this.options = options;\n this._keep = [];\n this._remove = [];\n\n this.blankRule = {\n replacement: options.blankReplacement\n };\n\n this.keepReplacement = options.keepReplacement;\n this.markdownIncludingHtmlReplacement = options.markdownIncludingHtmlReplacement;\n\n this.defaultRule = {\n replacement: options.defaultReplacement\n };\n\n this.array = [];\n for (const key in options.rules) {\n this.array.push(options.rules[key]);\n }\n }\n\n add(key: string, rule: Rule): void {\n this.array.unshift(rule);\n }\n\n keep(filter: RuleFilter): void {\n this._keep.unshift({\n filter: filter,\n replacement: this.keepReplacement\n });\n }\n\n remove(filter: RuleFilter): void {\n this._remove.unshift({\n filter: filter,\n replacement: function () {\n return '';\n }\n });\n }\n\n forNode(node: ExtendedNode): Rule {\n if (node.isBlank) {\n return this.blankRule;\n }\n if (this.options.htmlRetentionMode === 'preserveAll' && this.isUnsupportedElement(node)) {\n return {\n replacement: this.keepReplacement\n };\n }\n if (this.options.htmlRetentionMode === 'markdownIncludingHtml' && this.isUnsupportedElement(node)) {\n return {\n replacement: this.markdownIncludingHtmlReplacement\n };\n }\n\n let rule: Rule | undefined;\n if ((rule = findRule(this.array, node, this.options))) {\n return rule;\n }\n if ((rule = findRule(this._keep, node, this.options))) {\n return rule;\n }\n if ((rule = findRule(this._remove, node, this.options))) {\n return rule;\n }\n return this.defaultRule;\n }\n\n /// Check if an element is unsupported for Markdown conversion.\n private isUnsupportedElement(node: ExtendedNode): boolean {\n const nodeName = node.nodeName;\n\n if (nodeName === 'PRE' && node.firstChild && (node.firstChild as Element).nodeName === 'CODE') {\n const codeElem = node.firstChild as Element;\n if (codeElem.attributes && codeElem.attributes.length > 0) {\n for (let i = 0; i < codeElem.attributes.length; i++) {\n const attrName = codeElem.attributes[i].name.toLowerCase();\n if (attrName !== 'class') {\n return true;\n }\n }\n }\n }\n\n if (node.attributes && node.attributes.length > 0) {\n switch (nodeName) {\n case 'IMG':\n for (let i = 0; i < node.attributes.length; i++) {\n const attrName = node.attributes[i].name.toLowerCase();\n if (attrName !== 'src' && attrName !== 'alt' && attrName !== 'title') {\n return true;\n }\n }\n return false;\n case 'A':\n for (let i = 0; i < node.attributes.length; i++) {\n const attrName = node.attributes[i].name.toLowerCase();\n if (attrName !== 'href' && attrName !== 'title') {\n return true;\n }\n }\n return false;\n case 'CODE':\n const parent = node.parentNode as Element;\n if (parent && parent.nodeName === 'PRE') {\n for (let i = 0; i < node.attributes.length; i++) {\n const attrName = node.attributes[i].name.toLowerCase();\n if (attrName !== 'class') {\n return true;\n }\n }\n return false;\n }\n default:\n return true;\n }\n }\n // Elements that are not standard HTML elements are unsupported\n if (standardMarkdownElements.indexOf(nodeName) === -1) {\n return true;\n }\n return false;\n }\n\n forEach(fn: (rule: Rule, index: number) => void): void {\n for (let i = 0; i < this.array.length; i++) {\n fn(this.array[i], i);\n }\n }\n}\n\nfunction findRule(rules: Rule[], node: ExtendedNode, options: TurnishOptions): Rule | undefined {\n for (let i = 0; i < rules.length; i++) {\n const rule = rules[i];\n if (filterValue(rule, node, options)) return rule;\n }\n return undefined;\n}\n\nfunction filterValue(rule: Rule, node: ExtendedNode, options: TurnishOptions): boolean {\n const filter = rule.filter;\n if (typeof filter === 'string') {\n if (filter === node.nodeName.toLowerCase()) {\n return true;\n }\n } else if (Array.isArray(filter)) {\n if (filter.indexOf(node.nodeName.toLowerCase()) > -1) {\n return true;\n }\n } else if (typeof filter === 'function') {\n if (filter(node, options)) {\n return true;\n }\n } else {\n throw new TypeError('`filter` needs to be a string, array, or function');\n }\n return false;\n}\n","/**\n * The collapseWhitespace function is adapted from collapse-whitespace\n * by Luc Thevenard.\n *\n * The MIT License (MIT)\n *\n * Copyright (c) 2014 Luc Thevenard <lucthevenard@gmail.com>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\nimport { NodeTypes } from \"./node\";\n\n/**\n * collapseWhitespace(options) removes extraneous whitespace from an the given element.\n *\n * @param {Object} options\n */\ninterface CollapseWhitespaceOptions {\n element: Node;\n isBlock: (node: Node) => boolean;\n isVoid: (node: Node) => boolean;\n isPre?: (node: Node) => boolean;\n}\n\nfunction collapseWhitespace(options: CollapseWhitespaceOptions): void {\n const element = options.element\n const isBlock = options.isBlock\n const isVoid = options.isVoid\n const isPre = options.isPre || function (node: Node): boolean {\n return node.nodeName === 'PRE';\n };\n\n if (!element.firstChild || isPre(element)) return\n\n let prevText: Text | null = null;\n let keepLeadingWs = false\n\n let prev: Node | null = null;\n let node: Node = next(prev, element, isPre);\n\n while (node !== element) {\n if (node.nodeType === NodeTypes.Text || node.nodeType === NodeTypes.CDATASection) {\n const textNode = node as Text;\n let text = textNode.data.replace(/[ \\r\\n\\t]+/g, ' ');\n\n if ((!prevText || / $/.test(prevText.data)) &&\n !keepLeadingWs && text[0] === ' ') {\n text = text.substr(1);\n }\n\n // `text` might be empty at this point.\n if (!text) {\n node = remove(node);\n continue;\n }\n\n textNode.data = text;\n\n prevText = textNode;\n } else if (node.nodeType === NodeTypes.Element) {\n if (isBlock(node) || node.nodeName === 'BR') {\n if (prevText) {\n prevText.data = prevText.data.replace(/ $/, '');\n }\n\n prevText = null;\n keepLeadingWs = false;\n } else if (isVoid(node) || isPre(node)) {\n // Avoid trimming space around non-block, non-BR void elements and inline PRE.\n prevText = null;\n keepLeadingWs = true;\n } else if (prevText) {\n // Drop protection if set previously.\n keepLeadingWs = false;\n }\n } else {\n node = remove(node);\n continue;\n }\n\n const nextNode = next(prev, node, isPre);\n prev = node;\n node = nextNode;\n }\n\n if (prevText) {\n prevText.data = prevText.data.replace(/ $/, '');\n if (!prevText.data) {\n remove(prevText);\n }\n }\n}\n\n/**\n * remove(node) removes the given node from the DOM and returns the\n * next node in the sequence.\n *\n * @param {Node} node\n * @return {Node} node\n */\nfunction remove(node: Node): Node {\n const nextNode: Node | null = node.nextSibling ?? node.parentNode;\n if (node.parentNode) {\n node.parentNode.removeChild(node);\n }\n return nextNode as Node;\n}\n\n/**\n * next(prev, current, isPre) returns the next node in the sequence, given the\n * current and previous nodes.\n *\n * @param {Node} prev\n * @param {Node} current\n * @param {Function} isPre\n * @return {Node}\n */\nfunction next(prev: Node | null, current: Node, isPre: (node: Node) => boolean): Node {\n if ((prev && prev.parentNode === current) || isPre(current)) {\n const nextNode: Node | null = current.nextSibling ?? current.parentNode;\n return nextNode as Node;\n }\n const nextNode: Node | null = current.firstChild ?? current.nextSibling ?? current.parentNode;\n return nextNode as Node;\n}\n\nexport default collapseWhitespace\n","/*\n * Set up window for Node.js\n */\n\nconst root: typeof globalThis = typeof window !== 'undefined' ? window : (typeof globalThis !== 'undefined' ? globalThis : {} as any)\n\n/*\n * Parsing HTML strings\n */\n\nfunction canParseHTMLNatively() {\n const Parser = typeof root.DOMParser !== 'undefined' ? root.DOMParser : undefined;\n let canParse = false;\n if (!Parser) return false;\n // Adapted from https://gist.github.com/1129031\n // Firefox/Opera/IE throw errors on unsupported types\n try {\n // WebKit returns null on unsupported types\n if (new Parser().parseFromString('', 'text/html')) {\n canParse = true;\n }\n } catch (e) { }\n return canParse;\n}\n\nexport class HTMLParser {\n // This will be assigned per environment below\n parseFromString(_input: string, _type?: string): Document {\n throw new Error('Not implemented')\n }\n}\n\nfunction createParser(): HTMLParser {\n const isBrowser =\n typeof window !== 'undefined' &&\n typeof document !== 'undefined' &&\n (typeof process === 'undefined' || (process as any).browser === true)\n\n if (typeof window !== 'undefined') {\n // Browser environment: use DOM API\n class HTMLParserBrowser extends HTMLParser {\n parseFromString(input: string, _type?: string): Document {\n const doc = document.implementation.createHTMLDocument('')\n doc.open()\n doc.write(input)\n doc.close()\n return doc\n }\n }\n return new HTMLParserBrowser()\n } else {\n // Node environment: use domino\n const domino = require('@mixmark-io/domino') as {\n createDocument: (html: string) => Document\n }\n class HTMLParserNode extends HTMLParser {\n parseFromString(input: string, _type?: string): Document {\n return domino.createDocument(input);\n }\n }\n return new HTMLParserNode()\n }\n}\n\nexport const createHTMLParser = (): HTMLParser =>\n canParseHTMLNatively()\n ? new root.DOMParser()\n : createParser()\n","import collapseWhitespace from '@/collapse-whitespace'\nimport { createHTMLParser, HTMLParser } from '@/html-parser'\nimport { isBlock, isVoid } from '@/utilities'\n\ninterface RootNodeOptions {\n preformattedCode?: boolean\n}\n\nexport default function RootNode(\n input: string | Node,\n { preformattedCode }: RootNodeOptions\n): Element {\n let root: Element\n if (typeof input === 'string') {\n const doc = htmlParser().parseFromString(\n // DOM parsers arrange elements in the <head> and <body>.\n // Wrapping in a custom element ensures elements are reliably arranged in\n // a single element.\n '<x-turnish id=\"turnish-root\">' + input + '</x-turnish>',\n 'text/html'\n )\n root = doc.getElementById('turnish-root') as Element\n } else {\n root = input.cloneNode(true) as Element\n }\n collapseWhitespace({\n element: root,\n isBlock: isBlock,\n isVoid: isVoid,\n isPre: preformattedCode ? isPreOrCode : undefined\n })\n\n return root\n}\n\nlet _htmlParser: HTMLParser | undefined\nfunction htmlParser(): HTMLParser {\n return (_htmlParser ??= createHTMLParser())\n}\n\nfunction isPreOrCode(node: Node): boolean {\n return node.nodeName === 'PRE' || node.nodeName === 'CODE';\n}\n","import { defaultRules } from '@/default-rules'\nimport { Rules, Rule, RuleFilter } from '@/rules'\nimport { trimLeadingNewlines, trimTrailingNewlines } from '@/utilities'\nimport RootNode from '@/root-node'\nimport { ExtendedNode, NodeTypes } from '@/node';\nconst reduce = Array.prototype.reduce\n\ntype EscapeRule = [RegExp, string];\n\nconst escapes: EscapeRule[] = [\n [/\\\\/g, '\\\\\\\\'],\n [/\\*/g, '\\\\*'],\n [/_/g, '\\\\_'],\n [/^-/g, '\\\\-'],\n [/^\\+ /g, '\\\\+ '],\n [/^(=+)/g, '\\\\$1'],\n [/^(#{1,6}) /g, '\\\\$1 '],\n [/`/g, '\\\\`'],\n [/^~~~/g, '\\\\~~~'],\n [/\\[/g, '\\\\['],\n [/\\]/g, '\\\\]'],\n [/<([^>]*)>/g, '\\\\<$1\\\\>'],\n [/^>/g, '\\\\>'],\n [/^(\\d+)\\. /g, '$1\\\\. ']\n];\n\ntype Plugin = (service: Turnish) => void;\n\nexport interface TurnishOptions {\n rules: { [key: string]: Rule };\n headingStyle: 'setext' | 'atx';\n hr: string;\n bulletListMarker: '*' | '-' | '+';\n listMarkerSpaceCount: 1 | 2 | 3 | 4;\n listItemIndent: 'tab' | 'space';\n listItemIndentSpaceCount: 2 | 4;\n codeBlockStyle: 'indented' | 'fenced';\n fence: string;\n emDelimiter: '_' | '*';\n strongDelimiter: '**' | '__';\n linkStyle: 'inlined' | 'referenced';\n linkReferenceStyle: 'full' | 'collapsed' | 'shortcut';\n linkReferenceDeduplication: 'none' | 'full';\n br: string;\n preformattedCode: boolean;\n htmlRetentionMode: 'standard' | 'preserveAll' | 'markdownIncludingHtml';\n blankReplacement: (content: string, node: ExtendedNode) => string;\n keepReplacement: (content: string, node: ExtendedNode) => string;\n markdownIncludingHtmlReplacement: (content: string, node: ExtendedNode) => string;\n defaultReplacement: (content: string, node: ExtendedNode) => string;\n [key: string]: any;\n}\n\nconst defaultOptions: TurnishOptions = {\n rules: defaultRules,\n headingStyle: 'atx',\n hr: '---',\n bulletListMarker: '-',\n listMarkerSpaceCount: 1,\n listItemIndent: 'space',\n listItemIndentSpaceCount: 4,\n codeBlockStyle: 'fenced',\n fence: '```',\n emDelimiter: '*',\n strongDelimiter: '**',\n linkStyle: 'inlined',\n linkReferenceStyle: 'full',\n linkReferenceDeduplication: 'full',\n br: ' ',\n preformattedCode: false,\n htmlRetentionMode: 'standard',\n blankReplacement: (content: string, node: ExtendedNode): string => {\n return node.isBlock ? '\\n\\n' : '';\n },\n keepReplacement: (content: string, node: ExtendedNode): string => {\n return node.isBlock ? '\\n\\n' + node.outerHTML + '\\n\\n' : node.outerHTML;\n },\n markdownIncludingHtmlReplacement: (content: string, node: ExtendedNode): string => {\n const tagName = node.nodeName.toLowerCase();\n\n let attributes = '';\n if (node.attributes && node.attributes.length > 0) {\n const attrs: string[] = [];\n for (let i = 0; i < node.attributes.length; i++) {\n const attr = node.attributes[i];\n attrs.push(`${attr.name}=\"${attr.value}\"`);\n }\n attributes = ' ' + attrs.join(' ');\n }\n\n attributes += ' markdown=\"1\"';\n\n const openTag = `<${tagName}${attributes}>`;\n const closeTag = `</${tagName}>`;\n\n const trimmedContent = content.trim();\n const html = openTag + '\\n' + trimmedContent + '\\n' + closeTag;\n\n return node.isBlock ? '\\n\\n' + html + '\\n\\n' : html;\n },\n defaultReplacement: function (content: string, node: ExtendedNode): string {\n return node.isBlock ? '\\n\\n' + content + '\\n\\n' : content;\n }\n};\n\nexport default class Turnish {\n options: TurnishOptions;\n rules: Rules;\n\n constructor(options?: Partial<TurnishOptions>) {\n this.options = Object.assign({}, defaultOptions, options);\n this.rules = new Rules(this.options);\n }\n\n /**\n * The entry point for converting a string or DOM node to Markdown\n * @public\n * @param {InputType} input The string or DOM node to convert\n * @returns A Markdown representation of the input\n * @type string\n */\n render(input: InputType): string {\n if (!canConvert(input)) {\n throw new TypeError(\n input + ' is not a string, or an element/document/fragment node.'\n );\n }\n if (input === '') {\n return '';\n }\n const output = this.process(RootNode(input, this.options));\n return this.postProcess(output);\n }\n\n /**\n * Add one or more plugins\n * @public\n * @param {Plugin|Plugin[]} plugin The plugin or array of plugins to add\n * @returns The Turnish instance for chaining\n * @type Object\n */\n use(plugin: Plugin | Plugin[]): Turnish {\n if (Array.isArray(plugin)) {\n for (let i = 0; i < plugin.length; i++) this.use(plugin[i]);\n } else if (typeof plugin === 'function') {\n plugin(this);\n } else {\n throw new TypeError('plugin must be a Function or an Array of Functions');\n }\n return this;\n }\n\n /**\n * Adds a rule\n * @public\n * @param {string} key The unique key of the rule\n * @param {Object} rule The rule\n * @returns The Turnish instance for chaining\n * @type Object\n */\n addRule(key: string, rule: Rule): Turnish {\n this.rules.add(key, rule);\n return this;\n }\n\n /**\n * Keep a node (as HTML) that matches the filter\n * @public\n * @param {RuleFilter} filter The unique key of the rule\n * @returns The Turnish instance for chaining\n * @type Object\n */\n keep(filter: RuleFilter): Turnish {\n this.rules.keep(filter);\n return this;\n }\n\n /**\n * Remove a node that matches the filter\n * @public\n * @param {string|Array|Function} filter The unique key of the rule\n * @returns The Turnish instance for chaining\n * @type Object\n */\n remove(filter: RuleFilter): Turnish {\n this.rules.remove(filter);\n return this;\n }\n\n /**\n * Escapes Markdown syntax\n * @public\n * @param {string} string The string to escape\n * @returns A string with Markdown syntax escaped\n * @type string\n */\n escape(string: string): string {\n return escapes.reduce(function (accumulator: string, escape: EscapeRule) {\n return accumulator.replace(escape[0], escape[1]);\n }, string);\n }\n\n\n /**\n * Reduces a DOM node down to its Markdown string equivalent\n * @private\n * @param {HTMLElement} parentNode The node to convert\n * @returns A Markdown representation of the node\n * @type string\n */\n process(this: Turnish, parentNode: Node): string {\n return Array.from(parentNode.childNodes).reduce((output, node) => {\n const extended = ExtendedNode(node, this.options);\n let replacement = '';\n if (extended.nodeType === NodeTypes.Text) {\n let value = extended.nodeValue ?? '';\n // Special handling for text nodes following void elements. Void element replacements (e.g., from GFM plugin for checkboxes) may add trailing whitespace, so we should remove leading whitespace from the following text node.\n if (node.previousSibling && node.previousSibling.nodeType === NodeTypes.Element) {\n const prevElement = node.previousSibling as Element;\n if (prevElement.nodeName === 'INPUT' && prevElement.getAttribute('type') === 'checkbox') {\n value = value.replace(/^\\s+/, '');\n }\n }\n replacement = extended.isCode ? value : this.escape(value);\n } else if (extended.nodeType === NodeTypes.Element) {\n replacement = this.replacementForNode(extended);\n }\n return join(output, replacement);\n }, '');\n }\n\n /**\n * Appends strings as each rule requires and trims the output\n * @private\n * @param {string} output The conversion output\n * @returns A trimmed version of the ouput\n * @type string\n */\n postProcess(output: string): string {\n for (const rule of this.rules.array) {\n if (rule.append) {\n output = join(output, rule.append(this.options))\n }\n }\n return output\n .replace(/^[\\t\\r\\n]+/, '')\n .replace(/[\\t\\r\\n\\s]+$/, '')\n }\n\n\n /**\n * Converts an element node to its Markdown equivalent\n * @private\n * @param {ExtendedNode} node The node to convert\n * @returns A Markdown representation of the node\n * @type string\n */\n replacementForNode(node: ExtendedNode) {\n const rule = this.rules.forNode(node)\n let content = this.process(node)\n const whitespace = node.flankingWhitespace\n if (whitespace.leading || whitespace.trailing) {\n content = content.trim()\n }\n return (\n whitespace.leading +\n rule.replacement(content, node, this.options) +\n whitespace.trailing\n )\n }\n}\n\n/**\n * Joins replacement to the current output with appropriate number of new lines\n * @private\n * @param {string} output The current conversion output\n * @param {string} replacement The string to append to the output\n * @returns Joined output\n * @type string\n */\nfunction join(output: string, replacement: string): string {\n const s1 = trimTrailingNewlines(output)\n const s2 = trimLeadingNewlines(replacement)\n const nls = Math.max(output.length - s1.length, replacement.length - s2.length)\n const separator = '\\n\\n'.substring(0, nls)\n\n return s1 + separator + s2\n}\n\n/**\n * Determines whether an input can be converted\n * @private\n * @param {string|HTMLElement} input Describe this parameter\n * @returns Describe what it returns\n * @type string|Object|Array|Boolean|Number\n */\ntype InputType = string | HTMLElement | Document | DocumentFragment;\nfunction canConvert(input: any): input is InputType {\n return (\n input != null && (\n typeof input === 'string' ||\n (input.nodeType && (\n input.nodeType === 1 || input.nodeType === 9 || input.nodeType === 11\n ))\n )\n );\n}\n"],"names":["ExtendedNode","node","options","extended","isBlock","isBlank","flankingWhitespace","isVoid","isMeaningfulWhenBlank","hasVoid","hasMeaningfulWhenBlank","extendedNode","edges","edgeWhitespace","isFlankedByWhitespace","string","m","side","sibling","regExp","isFlanked","NodeTypes","repeat","character","count","trimLeadingNewlines","trimTrailingNewlines","indexEnd","trimNewlines","blockElements","is","voidElements","has","meaningfulWhenBlankElements","standardMarkdownElements","tagNames","tagName","sanitizeWhitespace","sanitizedLinkContent","content","sanitizedLinkTitle","defaultRules","_content","_node","hLevel","underline","parent","prefix","start","index","isParagraph","child","nestingLevel","currentNode","grandparent","oneIndent","indent","listMarkerRegex","preNode","firstChild","codeElem","language","code","fenceChar","fenceSize","fenceInCodeRegex","match","fence","sanitizedContent","href","title","titleAttr","referenceLinkRule","self","referenceKey","replacement","reference","id","existingKey","references","hasSiblings","isCodeBlock","trimmed","extraSpace","delimiter","matches","altAttr","alt","src","titlePart","Rules","key","rule","filter","findRule","nodeName","attrName","fn","i","rules","filterValue","collapseWhitespace","element","isPre","prevText","keepLeadingWs","prev","next","textNode","text","remove","nextNode","current","root","canParseHTMLNatively","Parser","canParse","HTMLParser","_input","_type","createParser","HTMLParserBrowser","input","doc","domino","HTMLParserNode","createHTMLParser","RootNode","preformattedCode","htmlParser","isPreOrCode","_htmlParser","escapes","defaultOptions","attributes","attrs","attr","openTag","closeTag","trimmedContent","html","Turnish","canConvert","output","plugin","accumulator","escape","parentNode","value","prevElement","join","whitespace","s1","s2","nls","separator"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "turnish",
3
3
  "description": "HTML to Markdown converter",
4
- "version": "1.4.0",
4
+ "version": "1.5.0",
5
5
  "type": "module",
6
6
  "author": "Manabu Nakazawa",
7
7
  "publishConfig": {