Comparing version 0.1.2 to 0.1.3
@@ -7,5 +7,7 @@ 'use strict'; | ||
var he; | ||
var he, | ||
utilities; | ||
he = require('he'); | ||
utilities = require('./utilities.js'); | ||
@@ -33,10 +35,2 @@ /* | ||
/* | ||
* Cached methods. | ||
*/ | ||
var has; | ||
has = Object.prototype.hasOwnProperty; | ||
/* | ||
* Expressions. | ||
@@ -113,22 +107,2 @@ */ | ||
/** | ||
* Shallow copy `context` into `target`. | ||
* | ||
* @param {Object} target | ||
* @return {Object} context | ||
* @return {Object} - target. | ||
*/ | ||
function copy(target, context) { | ||
var key; | ||
for (key in context) { | ||
/* istanbul ignore else */ | ||
if (has.call(context, key)) { | ||
target[key] = context[key]; | ||
} | ||
} | ||
return target; | ||
} | ||
/** | ||
* Remove the minimum indent from `value`. | ||
@@ -183,22 +157,3 @@ * | ||
/* | ||
* Unique id counter. | ||
*/ | ||
var uid; | ||
uid = 0; | ||
/** | ||
* Get a uid. | ||
* | ||
* @param {string} value | ||
* @return {string} | ||
*/ | ||
function getUID(value) { | ||
uid += 1; | ||
return value + uid; | ||
} | ||
/** | ||
* Get the alignment from a table rule. | ||
@@ -430,3 +385,3 @@ * | ||
rules = copy({}, block); | ||
rules = utilities.copy({}, block); | ||
@@ -1040,5 +995,7 @@ if (options.gfm) { | ||
self.links = links; | ||
self.footnoteCounter = 1; | ||
self.footnotes = footnotes; | ||
self.footnotesAsArray = footnotes ? Object.keys(footnotes) : []; | ||
rules = copy({}, inline); | ||
rules = utilities.copy({}, inline); | ||
@@ -1317,4 +1274,11 @@ if (options.breaks) { | ||
text = text.substr(1); | ||
footnote = getUID('footnote-'); | ||
while ( | ||
'footnote-' + this.footnoteCounter in this.footnotes | ||
) { | ||
++this.footnoteCounter; | ||
} | ||
footnote = 'footnote-' + this.footnoteCounter; | ||
this.footnotes[footnote] = [{ | ||
@@ -1325,2 +1289,4 @@ 'type': 'paragraph', | ||
this.footnotesAsArray.push(footnote); | ||
this.addFootnoteToken(tokens, footnote); | ||
@@ -1451,3 +1417,5 @@ | ||
footnotes, | ||
footnotesAsArray, | ||
footnote, | ||
index, | ||
tokens; | ||
@@ -1463,8 +1431,10 @@ | ||
footnotesAsArray = parser.inline.footnotesAsArray; | ||
if (footnotes) { | ||
for (footnote in footnotes) { | ||
/* istanbul ignore else: hasownproperty */ | ||
if (has.call(footnotes, footnote)) { | ||
footnotes[footnote] = parser.parse(footnotes[footnote]); | ||
} | ||
index = -1; | ||
while (footnotesAsArray[++index]) { | ||
footnote = footnotesAsArray[index]; | ||
footnotes[footnote] = parser.parse(footnotes[footnote]); | ||
} | ||
@@ -1654,6 +1624,6 @@ } | ||
settings = copy({}, defaults); | ||
settings = utilities.copy({}, defaults); | ||
if (options) { | ||
copy(settings, options); | ||
utilities.copy(settings, options); | ||
} | ||
@@ -1660,0 +1630,0 @@ |
@@ -7,5 +7,7 @@ 'use strict'; | ||
var table; | ||
var table, | ||
utilities; | ||
table = require('markdown-table'); | ||
utilities = require('./utilities.js'); | ||
@@ -16,5 +18,7 @@ /* | ||
var ESCAPE_CHARACTERS; | ||
var EXPRESSION_ESCAPE, | ||
EXPRESSION_DIGIT; | ||
ESCAPE_CHARACTERS = /[\\`*{}\[\]()#+\-.!_>]/g; | ||
EXPRESSION_ESCAPE = /[\\`*{}\[\]()#+\-._>]/g; | ||
EXPRESSION_DIGIT = /\d/; | ||
@@ -127,2 +131,21 @@ /* | ||
/** | ||
* Helper to get the keys in an object. | ||
* | ||
* @param {Object} object | ||
* @return {Array.<string>} | ||
*/ | ||
function getKeys(object) { | ||
var results, | ||
key; | ||
results = []; | ||
for (key in object) { | ||
results.push(key); | ||
} | ||
return results; | ||
} | ||
/** | ||
* Repeat `character` `times` times. | ||
@@ -221,11 +244,20 @@ * | ||
function Compiler(options) { | ||
var bullet, | ||
var self, | ||
bullet, | ||
horizontalRule, | ||
horizontalRuleSpaces, | ||
horizontalRuleRepetition, | ||
preferSetextHeadings, | ||
preferReferenceLinks, | ||
preferReferenceFootnotes, | ||
preferFences, | ||
emphasis, | ||
strong; | ||
this.footnoteCounter = 0; | ||
self = this; | ||
self.footnoteCounter = 0; | ||
self.linkCounter = 0; | ||
self.links = []; | ||
if (options === null || options === undefined) { | ||
@@ -241,2 +273,6 @@ options = {}; | ||
horizontalRuleRepetition = options.horizontalRuleRepetition; | ||
preferSetextHeadings = options.preferSetextHeadings; | ||
preferReferenceLinks = options.preferReferenceLinks; | ||
preferReferenceFootnotes = options.preferReferenceFootnotes; | ||
preferFences = options.preferFences; | ||
emphasis = options.emphasis; | ||
@@ -275,3 +311,30 @@ strong = options.strong; | ||
if (preferSetextHeadings === null || preferSetextHeadings === undefined) { | ||
options.preferSetextHeadings = false; | ||
} else if (typeof preferSetextHeadings !== 'boolean') { | ||
raise(preferSetextHeadings, 'options.preferSetextHeadings'); | ||
} | ||
if (preferReferenceLinks === null || preferReferenceLinks === undefined) { | ||
options.preferReferenceLinks = false; | ||
} else if (typeof preferReferenceLinks !== 'boolean') { | ||
raise(preferReferenceLinks, 'options.preferReferenceLinks'); | ||
} | ||
if ( | ||
preferReferenceFootnotes === null || | ||
preferReferenceFootnotes === undefined | ||
) { | ||
options.preferReferenceFootnotes = true; | ||
} else if (typeof preferReferenceFootnotes !== 'boolean') { | ||
raise(preferReferenceFootnotes, 'options.preferReferenceFootnotes'); | ||
} | ||
if (preferFences === null || preferFences === undefined) { | ||
options.preferFences = false; | ||
} else if (typeof preferFences !== 'boolean') { | ||
raise(preferFences, 'options.preferFences'); | ||
} | ||
if ( | ||
horizontalRuleRepetition === null || | ||
@@ -289,3 +352,3 @@ horizontalRuleRepetition === undefined | ||
this.options = options; | ||
self.options = options; | ||
} | ||
@@ -482,3 +545,3 @@ | ||
preferSetextHeadings = Boolean(this.options.preferSetextHeadings); | ||
preferSetextHeadings = this.options.preferSetextHeadings; | ||
@@ -510,3 +573,12 @@ depth = token.depth; | ||
compilerPrototype.text = function (token) { | ||
return token.value.replace(ESCAPE_CHARACTERS, '\\$&'); | ||
return token.value.replace(EXPRESSION_ESCAPE, function ($0, index) { | ||
if ( | ||
$0 === DOT && | ||
!EXPRESSION_DIGIT.test(token.value.charAt(index - 1)) | ||
) { | ||
return $0; | ||
} | ||
return '\\' + $0; | ||
}); | ||
}; | ||
@@ -549,16 +621,30 @@ | ||
compilerPrototype.link = function (token, parent, level) { | ||
var value; | ||
var self, | ||
value, | ||
link; | ||
self = this; | ||
value = SQUARE_BRACKET_OPEN + | ||
this.visitAll(token, token.children, level).join(EMPTY) + | ||
self.visitAll(token, token.children, level).join(EMPTY) + | ||
SQUARE_BRACKET_CLOSE; | ||
value += PARENTHESIS_OPEN + token.href; | ||
link = token.href; | ||
if (token.title) { | ||
value += SPACE + QUOTE_DOUBLE + token.title + QUOTE_DOUBLE; | ||
link += SPACE + QUOTE_DOUBLE + token.title + QUOTE_DOUBLE; | ||
} | ||
value += PARENTHESIS_CLOSE; | ||
if (self.options.preferReferenceLinks) { | ||
value += SQUARE_BRACKET_OPEN + (++self.linkCounter) + | ||
SQUARE_BRACKET_CLOSE; | ||
self.links.push( | ||
SQUARE_BRACKET_OPEN + self.linkCounter + SQUARE_BRACKET_CLOSE + | ||
COLON + SPACE + link | ||
); | ||
} else { | ||
value += PARENTHESIS_OPEN + link + PARENTHESIS_CLOSE; | ||
} | ||
return value; | ||
@@ -670,3 +756,3 @@ }; | ||
if (!token.lang) { | ||
if (!token.lang && !this.options.preferFences) { | ||
return pad(value, 1); | ||
@@ -679,3 +765,3 @@ } | ||
return ticks + token.lang + LINE + value + LINE + ticks; | ||
return ticks + (token.lang || '') + LINE + value + LINE + ticks; | ||
}; | ||
@@ -801,3 +887,24 @@ | ||
compilerPrototype.footnote = function (token) { | ||
return SQUARE_BRACKET_OPEN + CARET + token.id + SQUARE_BRACKET_CLOSE; | ||
var footnote; | ||
footnote = this.footnotes[token.id]; | ||
/* | ||
* Check if the user prefers inline footnotes. | ||
* But, only expose footnotes inline when the | ||
* footnote contains just one paragraph. | ||
*/ | ||
if ( | ||
this.options.preferReferenceFootnotes || | ||
footnote.length !== 1 || | ||
footnote[0].type !== 'paragraph' | ||
) { | ||
return SQUARE_BRACKET_OPEN + CARET + token.id + SQUARE_BRACKET_CLOSE; | ||
} | ||
this.footnotes[token.id] = false; | ||
return SQUARE_BRACKET_OPEN + CARET + this.visit(footnote[0], null) + | ||
SQUARE_BRACKET_CLOSE; | ||
}; | ||
@@ -836,3 +943,3 @@ | ||
* in markdown-table@0.3.1, which modified the `align` | ||
* array, changing the ast. | ||
* array, changing the AST. | ||
*/ | ||
@@ -858,2 +965,64 @@ | ||
/** | ||
* Visit the footnote definition block. | ||
* | ||
* @param {Object} footnotes | ||
* @return {string} | ||
*/ | ||
compilerPrototype.visitFootnoteDefinitions = function (footnotes) { | ||
var self, | ||
keys, | ||
key, | ||
index, | ||
length, | ||
results; | ||
self = this; | ||
keys = getKeys(footnotes); | ||
/* | ||
* First stringify the footnotes definitions, | ||
* because new footnote references could be found. | ||
*/ | ||
index = -1; | ||
length = keys.length; | ||
while (++index < length) { | ||
key = keys[index]; | ||
if (footnotes[key]) { | ||
footnotes[key] = SQUARE_BRACKET_OPEN + CARET + key + | ||
SQUARE_BRACKET_CLOSE + COLON + SPACE + | ||
self.visitAll(null, footnotes[key]) | ||
.join(BREAK + repeat(INDENT, SPACE)); | ||
} | ||
} | ||
/* | ||
* Then, all footnotes that are stringified | ||
* inline, are set to `false`. The reset, we | ||
* add to the buffer below. | ||
*/ | ||
results = []; | ||
index = -1; | ||
while (++index < length) { | ||
key = keys[index]; | ||
if (footnotes[key] !== false) { | ||
results.push(footnotes[key]); | ||
} | ||
} | ||
if (results.length) { | ||
return LINE + results.join(LINE) + LINE; | ||
} | ||
return EMPTY; | ||
}; | ||
/** | ||
* Stringify an AST token. | ||
@@ -873,4 +1042,5 @@ * | ||
if (token.footnotes) { | ||
compiler.footnotes = token.footnotes; | ||
compiler.footnoteMap = {}; | ||
footnotes = utilities.copy({}, token.footnotes); | ||
compiler.footnotes = footnotes; | ||
} | ||
@@ -880,15 +1050,8 @@ | ||
if (token.footnotes) { | ||
footnotes = BREAK + Object.keys(token.footnotes).map(function (key) { | ||
var footnote; | ||
if (compiler.links.length) { | ||
value += LINE + compiler.links.join(LINE) + LINE; | ||
} | ||
footnote = token.footnotes[key]; | ||
return SQUARE_BRACKET_OPEN + CARET + key + SQUARE_BRACKET_CLOSE + | ||
COLON + SPACE + compiler.visitAll(null, footnote).join( | ||
BREAK + repeat(INDENT, SPACE) | ||
); | ||
}).join(BREAK); | ||
value += footnotes; | ||
if (footnotes) { | ||
value += compiler.visitFootnoteDefinitions(footnotes); | ||
} | ||
@@ -895,0 +1058,0 @@ |
{ | ||
"name": "mdast", | ||
"version": "0.1.2", | ||
"version": "0.1.3", | ||
"description": "Speedy Markdown parser/stringified for multipurpose analysis", | ||
@@ -35,3 +35,3 @@ "license": "MIT", | ||
"jscs": "^1.0.0", | ||
"jscs-jsdoc": "^0.3.0", | ||
"jscs-jsdoc": "^0.4.0", | ||
"matcha": "^0.6.0", | ||
@@ -44,6 +44,6 @@ "mocha": "^2.0.0" | ||
"coverage": "istanbul cover _mocha -- -- test/index.js", | ||
"lint-api": "eslint index.js lib/parse.js lib/stringify.js", | ||
"lint-api": "eslint index.js lib/parse.js lib/stringify.js lib/utilities.js", | ||
"lint-test": "eslint --env mocha test/index.js test/fixtures.js", | ||
"lint-benchmark": "eslint --global suite,set,bench benchmark.js", | ||
"lint-style": "jscs --reporter inline index.js lib/parse.js lib/stringify.js test/index.js test/fixtures.js benchmark.js", | ||
"lint-style": "jscs --reporter inline index.js lib/parse.js lib/stringify.js lib/utilities.js test/index.js test/fixtures.js benchmark.js", | ||
"lint": "npm run lint-api && npm run lint-test && npm run lint-benchmark && npm run lint-style", | ||
@@ -50,0 +50,0 @@ "make": "npm run lint && npm run coverage", |
@@ -120,2 +120,5 @@ # ![mdast](https://cdn.rawgit.com/wooorm/mdast/master/logo.svg) | ||
- `options.preferSetextHeadings` (`boolean`, default: `false`). See [Setext Headings](#setext-headings); | ||
- `options.preferReferenceLinks` (`boolean`, default: `false`). See [Reference Links](#reference-links); | ||
- `options.preferReferenceFootnotes` (`boolean`, default: `true`). See [Inline Footnotes](#inline-footnotes); | ||
- `options.preferFences` (`boolean`, default: `false`). See [Fences](#fences); | ||
- `options.bullet` (`"-"`, `"*"`, or `"+"`, default: `"-"`). See [List Item Bullets](#list-item-bullets); | ||
@@ -563,2 +566,79 @@ - `options.horizontalRule` (`"-"`, `"*"`, or `"_"`, default: `"*"`). See [Horizontal Rules](#horizontal-rules); | ||
### Reference Links | ||
Instead of exposing links inline, it’s possible to stringify reference-style links. | ||
By default, links are stringified inline. | ||
```js | ||
var ast = mdast.parse( | ||
'Lorem ipsum dolor sit [amet](http://amet.com "Amet").' | ||
); | ||
mdast.stringify(ast, { | ||
'preferReferenceLinks': true | ||
}); | ||
``` | ||
Yields: | ||
```md | ||
Lorem ipsum dolor sit [amet][1]. | ||
[1]: http://amet.com "Amet" | ||
``` | ||
### Inline Footnotes | ||
Instead of exposing reference-style footnotes, it’s possible to stringify inline footnotes (when possible). | ||
By default, footnotes are stringified after the document. By setting `preferReferenceFootnotes: false` footnotes containing a single paragraph can be stringified inline. | ||
```js | ||
var ast = mdast.parse( | ||
'Some text[^1].\n' + | ||
'\n' + | ||
'[^1]: And a footnote\n', { | ||
'footnotes': true | ||
}); | ||
mdast.stringify(ast, { | ||
'preferReferenceFootnotes': false | ||
}); | ||
``` | ||
Yields: | ||
```md | ||
Some text[^And a footnote]. | ||
``` | ||
### Fences | ||
By default, code blocks without a programming-language flag are stringified using indentation, instead of [GFMs fences](https://help.github.com/articles/github-flavored-markdown/#fenced-code-blocks). | ||
```js | ||
var ast = mdast.parse( | ||
'A code block:\n' + | ||
'\n' + | ||
' alert(\'Hello World!\');\n' | ||
); | ||
mdast.stringify(ast, { | ||
'preferFences': true | ||
}); | ||
``` | ||
Yields: | ||
``````md | ||
A code block: | ||
\``` | ||
alert('Hello World!'); | ||
\``` | ||
`````` | ||
The above example contains two slahes to make sure GitHub renders the markdown (in markdown) properly. | ||
## Nodes | ||
@@ -565,0 +645,0 @@ |
79520
7
2136
928