Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide text escaping and replacement hooks for context-dependent escaping #339

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

### 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
16 changes: 11 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 @@ -140,10 +145,10 @@ TurndownService.prototype = {
* @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 +167,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