Skip to content

Commit

Permalink
Provide text escaping and replacement hooks to allow implementing com…
Browse files Browse the repository at this point in the history
…prehensive, context-dependent escaping. See #324.
  • Loading branch information
Michal Bartoš authored and martincizek committed Jul 6, 2020
1 parent 5c4d19b commit f1805ba
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 5 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ var turndownService = new TurndownService({ option: 'value' })
| `blankReplacement` | rule replacement function | See **Special Rules** below |
| `keepReplacement` | rule replacement function | See **Special Rules** below |
| `defaultReplacement` | rule replacement function | See **Special Rules** below |
| `textReplacement` | rule replacement function | See **Special Rules** below |
| `escapes` | array of replacement pairs | See [source code](https://github.com/domchristie/turndown/blob/master/src/turndown.js#L9) |

## Methods

Expand Down Expand Up @@ -197,6 +199,8 @@ rules.emphasis = {

**Default rule** handles nodes which are not recognised by any other rule. By default, it outputs the node's text content (separated by blank lines if it is a block-level element). Its behaviour can be customised with the `defaultReplacement` option.

**Text rule** handles text nodes. By default, it preserves text under `<code>` elements and escapes all other text using the `escape` method that uses the `escapes` option.

### Rule Precedence

Turndown iterates over the set of rules, and picks the first one that matches the `filter`. The following list describes the order of precedence:
Expand Down
5 changes: 5 additions & 0 deletions src/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export default function Rules (options) {
this._keep = []
this._remove = []

this.textRule = {
replacement: options.textReplacement
}

this.blankRule = {
replacement: options.blankReplacement
}
Expand Down Expand Up @@ -43,6 +47,7 @@ Rules.prototype = {
},

forNode: function (node) {
if (node.nodeType === 3) return this.textRule
if (node.isBlank) return this.blankRule
var rule

Expand Down
18 changes: 13 additions & 5 deletions src/turndown.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Node from './node'
var reduce = Array.prototype.reduce
var leadingNewLinesRegExp = /^\n*/
var trailingNewLinesRegExp = /\n*$/
var escapes = [
var ESCAPES = [
[/\\/g, '\\\\'],
[/\*/g, '\\*'],
[/^-/g, '\\-'],
Expand All @@ -25,8 +25,10 @@ var escapes = [
export default function TurndownService (options) {
if (!(this instanceof TurndownService)) return new TurndownService(options)

var self = this
var defaults = {
rules: COMMONMARK_RULES,
escapes: ESCAPES,
headingStyle: 'setext',
hr: '* * *',
bulletListMarker: '*',
Expand All @@ -45,6 +47,9 @@ export default function TurndownService (options) {
},
defaultReplacement: function (content, node) {
return node.isBlock ? '\n\n' + content + '\n\n' : content
},
textReplacement: function (content, node, options) {
return node.isCode ? content : self.escape(content, node, options)
}
}
this.options = extend({}, defaults, options)
Expand Down Expand Up @@ -136,14 +141,16 @@ TurndownService.prototype = {
* Escapes Markdown syntax
* @public
* @param {String} string The string to escape
* @param {Text} node The DOM node context
* @param {Object} options The TurndownService options
* @returns A string with Markdown syntax escaped
* @type String
*/

escape: function (string) {
return escapes.reduce(function (accumulator, escape) {
escape: function (content, node, options) {
return options.escapes.reduce(function (accumulator, escape) {
return accumulator.replace(escape[0], escape[1])
}, string)
}, content)
}
}

Expand All @@ -162,7 +169,8 @@ function process (parentNode) {

var replacement = ''
if (node.nodeType === 3) {
replacement = node.isCode ? node.nodeValue : self.escape(node.nodeValue)
var textRule = self.rules.forNode(node)
replacement = textRule.replacement(node.nodeValue, node, self.options)
} else if (node.nodeType === 1) {
replacement = replacementForNode.call(self, node)
}
Expand Down

0 comments on commit f1805ba

Please sign in to comment.