Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'tools/eslint/node_modules/remark-stringify/lib/compiler.js')
-rw-r--r--tools/eslint/node_modules/remark-stringify/lib/compiler.js2629
1 files changed, 2629 insertions, 0 deletions
diff --git a/tools/eslint/node_modules/remark-stringify/lib/compiler.js b/tools/eslint/node_modules/remark-stringify/lib/compiler.js
new file mode 100644
index 00000000000..85194a51e3a
--- /dev/null
+++ b/tools/eslint/node_modules/remark-stringify/lib/compiler.js
@@ -0,0 +1,2629 @@
+/**
+ * @author Titus Wormer
+ * @copyright 2015-2016 Titus Wormer
+ * @license MIT
+ * @module remark:compiler
+ * @fileoverview Markdown compiler
+ */
+
+'use strict';
+
+/* eslint-env commonjs */
+
+/*
+ * Dependencies.
+ */
+
+var decode = require('parse-entities');
+var encode = require('stringify-entities');
+var table = require('markdown-table');
+var repeat = require('repeat-string');
+var extend = require('extend');
+var ccount = require('ccount');
+var longestStreak = require('longest-streak');
+var defaultOptions = require('./defaults.js');
+
+/*
+ * Constants.
+ */
+
+var INDENT = 4;
+var MINIMUM_CODE_FENCE_LENGTH = 3;
+var YAML_FENCE_LENGTH = 3;
+var MINIMUM_RULE_LENGTH = 3;
+var MAILTO = 'mailto:';
+var ERROR_LIST_ITEM_INDENT = 'Cannot indent code properly. See ' +
+ 'http://git.io/vgFvT';
+
+/*
+ * Expressions.
+ */
+
+var EXPRESSIONS_WHITE_SPACE = /\s/;
+
+/*
+ * Naive fence expression.
+ */
+
+var FENCE = /([`~])\1{2}/;
+
+/*
+ * Expression for a protocol.
+ *
+ * @see http://en.wikipedia.org/wiki/URI_scheme#Generic_syntax
+ */
+
+var PROTOCOL = /^[a-z][a-z+.-]+:\/?/i;
+
+/*
+ * Punctuation characters.
+ */
+
+var PUNCTUATION = /[-!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~_]/;
+
+/*
+ * Characters.
+ */
+
+var ANGLE_BRACKET_CLOSE = '>';
+var ANGLE_BRACKET_OPEN = '<';
+var ASTERISK = '*';
+var BACKSLASH = '\\';
+var CARET = '^';
+var COLON = ':';
+var SEMICOLON = ';';
+var DASH = '-';
+var DOT = '.';
+var EMPTY = '';
+var EQUALS = '=';
+var EXCLAMATION_MARK = '!';
+var HASH = '#';
+var AMPERSAND = '&';
+var LINE = '\n';
+var CARRIAGE = '\r';
+var FORM_FEED = '\f';
+var PARENTHESIS_OPEN = '(';
+var PARENTHESIS_CLOSE = ')';
+var PIPE = '|';
+var PLUS = '+';
+var QUOTE_DOUBLE = '"';
+var QUOTE_SINGLE = '\'';
+var SPACE = ' ';
+var TAB = '\t';
+var VERTICAL_TAB = '\u000B';
+var SQUARE_BRACKET_OPEN = '[';
+var SQUARE_BRACKET_CLOSE = ']';
+var TICK = '`';
+var TILDE = '~';
+var UNDERSCORE = '_';
+
+/**
+ * Check whether `character` is numeric.
+ *
+ * @param {string} character - Single character to check.
+ * @return {boolean} - Whether `character` is numeric.
+ */
+function isNumeric(character) {
+ return /[0-9]/.test(character);
+}
+
+/**
+ * Check whether `character` is alphanumeric.
+ *
+ * @param {string} character - Single character to check.
+ * @return {boolean} - Whether `character` is alphanumeric.
+ */
+function isAlphanumeric(character) {
+ return /\w/.test(character) && character !== UNDERSCORE;
+}
+
+/*
+ * Entities.
+ */
+
+var ENTITY_AMPERSAND = AMPERSAND + 'amp' + SEMICOLON;
+var ENTITY_ANGLE_BRACKET_OPEN = AMPERSAND + 'lt' + SEMICOLON;
+var ENTITY_COLON = AMPERSAND + '#x3A' + SEMICOLON;
+
+/*
+ * Character combinations.
+ */
+
+var BREAK = LINE + LINE;
+var GAP = BREAK + LINE;
+var DOUBLE_TILDE = TILDE + TILDE;
+
+/*
+ * Allowed entity options.
+ */
+
+var ENTITY_OPTIONS = {};
+
+ENTITY_OPTIONS.true = true;
+ENTITY_OPTIONS.false = true;
+ENTITY_OPTIONS.numbers = true;
+ENTITY_OPTIONS.escape = true;
+
+/*
+ * Allowed list-bullet characters.
+ */
+
+var LIST_BULLETS = {};
+
+LIST_BULLETS[ASTERISK] = true;
+LIST_BULLETS[DASH] = true;
+LIST_BULLETS[PLUS] = true;
+
+/*
+ * Allowed horizontal-rule bullet characters.
+ */
+
+var THEMATIC_BREAK_BULLETS = {};
+
+THEMATIC_BREAK_BULLETS[ASTERISK] = true;
+THEMATIC_BREAK_BULLETS[DASH] = true;
+THEMATIC_BREAK_BULLETS[UNDERSCORE] = true;
+
+/*
+ * Allowed emphasis characters.
+ */
+
+var EMPHASIS_MARKERS = {};
+
+EMPHASIS_MARKERS[UNDERSCORE] = true;
+EMPHASIS_MARKERS[ASTERISK] = true;
+
+/*
+ * Allowed fence markers.
+ */
+
+var FENCE_MARKERS = {};
+
+FENCE_MARKERS[TICK] = true;
+FENCE_MARKERS[TILDE] = true;
+
+/*
+ * Which method to use based on `list.ordered`.
+ */
+
+var ORDERED_MAP = {};
+
+ORDERED_MAP.true = 'visitOrderedItems';
+ORDERED_MAP.false = 'visitUnorderedItems';
+
+/*
+ * Allowed list-item-indent's.
+ */
+
+var LIST_ITEM_INDENTS = {};
+
+var LIST_ITEM_TAB = 'tab';
+var LIST_ITEM_ONE = '1';
+var LIST_ITEM_MIXED = 'mixed';
+
+LIST_ITEM_INDENTS[LIST_ITEM_ONE] = true;
+LIST_ITEM_INDENTS[LIST_ITEM_TAB] = true;
+LIST_ITEM_INDENTS[LIST_ITEM_MIXED] = true;
+
+/*
+ * Which checkbox to use.
+ */
+
+var CHECKBOX_MAP = {};
+
+CHECKBOX_MAP.null = EMPTY;
+CHECKBOX_MAP.undefined = EMPTY;
+CHECKBOX_MAP.true = SQUARE_BRACKET_OPEN + 'x' + SQUARE_BRACKET_CLOSE + SPACE;
+CHECKBOX_MAP.false = SQUARE_BRACKET_OPEN + SPACE + SQUARE_BRACKET_CLOSE +
+ SPACE;
+
+/**
+ * Throw an exception with in its `message` `value`
+ * and `name`.
+ *
+ * @param {*} value - Invalid value.
+ * @param {string} name - Setting name.
+ */
+function raise(value, name) {
+ throw new Error(
+ 'Invalid value `' + value + '` ' +
+ 'for setting `' + name + '`'
+ );
+}
+
+/**
+ * Validate a value to be boolean. Defaults to `def`.
+ * Raises an exception with `context[name]` when not
+ * a boolean.
+ *
+ * @example
+ * validateBoolean({foo: null}, 'foo', true) // true
+ * validateBoolean({foo: false}, 'foo', true) // false
+ * validateBoolean({foo: 'bar'}, 'foo', true) // Throws
+ *
+ * @throws {Error} - When a setting is neither omitted nor
+ * a boolean.
+ * @param {Object} context - Settings.
+ * @param {string} name - Setting name.
+ * @param {boolean} def - Default value.
+ */
+function validateBoolean(context, name, def) {
+ var value = context[name];
+
+ if (value === null || value === undefined) {
+ value = def;
+ }
+
+ if (typeof value !== 'boolean') {
+ raise(value, 'options.' + name);
+ }
+
+ context[name] = value;
+}
+
+/**
+ * Validate a value to be boolean. Defaults to `def`.
+ * Raises an exception with `context[name]` when not
+ * a boolean.
+ *
+ * @example
+ * validateNumber({foo: null}, 'foo', 1) // 1
+ * validateNumber({foo: 2}, 'foo', 1) // 2
+ * validateNumber({foo: 'bar'}, 'foo', 1) // Throws
+ *
+ * @throws {Error} - When a setting is neither omitted nor
+ * a number.
+ * @param {Object} context - Settings.
+ * @param {string} name - Setting name.
+ * @param {number} def - Default value.
+ */
+function validateNumber(context, name, def) {
+ var value = context[name];
+
+ if (value === null || value === undefined) {
+ value = def;
+ }
+
+ if (typeof value !== 'number' || value !== value) {
+ raise(value, 'options.' + name);
+ }
+
+ context[name] = value;
+}
+
+/**
+ * Validate a value to be in `map`. Defaults to `def`.
+ * Raises an exception with `context[name]` when not
+ * in `map`.
+ *
+ * @example
+ * var map = {bar: true, baz: true};
+ * validateString({foo: null}, 'foo', 'bar', map) // 'bar'
+ * validateString({foo: 'baz'}, 'foo', 'bar', map) // 'baz'
+ * validateString({foo: true}, 'foo', 'bar', map) // Throws
+ *
+ * @throws {Error} - When a setting is neither omitted nor
+ * in `map`.
+ * @param {Object} context - Settings.
+ * @param {string} name - Setting name.
+ * @param {string} def - Default value.
+ * @param {Object} map - Enum.
+ */
+function validateString(context, name, def, map) {
+ var value = context[name];
+
+ if (value === null || value === undefined) {
+ value = def;
+ }
+
+ if (!(value in map)) {
+ raise(value, 'options.' + name);
+ }
+
+ context[name] = value;
+}
+
+/*
+ * Expose `validate`.
+ */
+
+var validate = {
+ 'boolean': validateBoolean,
+ 'string': validateString,
+ 'number': validateNumber
+};
+
+/**
+ * Construct a state `toggler`: a function which inverses
+ * `property` in context based on its current value.
+ * The by `toggler` returned function restores that value.
+ *
+ * @example
+ * var context = {};
+ * var key = 'foo';
+ * var val = true;
+ * context[key] = val;
+ * context.enter = toggler(key, val);
+ * context[key]; // true
+ * var exit = context.enter();
+ * context[key]; // false
+ * var nested = context.enter();
+ * context[key]; // false
+ * nested();
+ * context[key]; // false
+ * exit();
+ * context[key]; // true
+ *
+ * @param {string} key - Property to toggle.
+ * @param {boolean} state - It's default state.
+ * @return {function(): function()} - Enter.
+ */
+function toggler(key, state) {
+ /**
+ * Construct a toggler for the bound `key`.
+ *
+ * @return {Function} - Exit state.
+ */
+ function enter() {
+ var self = this;
+ var current = self[key];
+
+ self[key] = !state;
+
+ /**
+ * State canceler, cancels the state, if allowed.
+ */
+ function exit() {
+ self[key] = current;
+ }
+
+ return exit;
+ }
+
+ return enter;
+}
+
+/**
+ * Check whether a node is mergeable with adjacent nodes.
+ *
+ * @param {Object} node - Node to check.
+ * @return {boolean} - Whether `node` is mergable.
+ */
+function mergeable(node) {
+ var start;
+ var end;
+
+ if (node.type !== 'text' || !node.position) {
+ return true;
+ }
+
+ start = node.position.start;
+ end = node.position.end;
+
+ /*
+ * Only merge nodes which occupy the same size as their
+ * `value`.
+ */
+
+ return start.line !== end.line ||
+ end.column - start.column === node.value.length;
+}
+
+/**
+ * Encode noop.
+ * Simply returns the given value.
+ *
+ * @example
+ * var encode = encodeNoop();
+ * encode('AT&T') // 'AT&T'
+ *
+ * @param {string} value - Content.
+ * @return {string} - Content, without any modifications.
+ */
+function encodeNoop(value) {
+ return value;
+}
+
+/**
+ * Factory to encode HTML entities.
+ * Creates a no-operation function when `type` is
+ * `'false'`, a function which encodes using named
+ * references when `type` is `'true'`, and a function
+ * which encodes using numbered references when `type` is
+ * `'numbers'`.
+ *
+ * @example
+ * encodeFactory('false')('AT&T') // 'AT&T'
+ * encodeFactory('true')('AT&T') // 'AT&amp;T'
+ * encodeFactory('numbers')('AT&T') // 'ATT&#x26;T'
+ *
+ * @param {string} type - Either `'true'`, `'false'`, or
+ * `'numbers'`.
+ * @return {function(string): string} - Function which
+ * takes a value and returns its encoded version.
+ */
+function encodeFactory(type) {
+ var options = {};
+
+ if (type === 'false') {
+ return encodeNoop;
+ }
+
+ if (type === 'true') {
+ options.useNamedReferences = true;
+ }
+
+ if (type === 'escape') {
+ options.escapeOnly = options.useNamedReferences = true;
+ }
+
+ /**
+ * Encode HTML entities using the bound options.
+ *
+ * @example
+ * // When `type` is `'true'`.
+ * encode('AT&T'); // 'AT&amp;T'
+ *
+ * // When `type` is `'numbers'`.
+ * encode('AT&T'); // 'ATT&#x26;T'
+ *
+ * @param {string} value - Content.
+ * @param {Object} [node] - Node which is compiled.
+ * @return {string} - Encoded content.
+ */
+ function encoder(value) {
+ return encode(value, options);
+ }
+
+ return encoder;
+}
+
+/**
+ * Returns the length of HTML entity that is a prefix of
+ * the given string (excluding the ampersand), 0 if it
+ * does not start with an entity.
+ *
+ * @example
+ * entityPrefixLength('&copycat') // 4
+ * entityPrefixLength('&foo &amp &bar') // 0
+ *
+ * @param {string} value - Input string.
+ * @return {number} - Length of an entity.
+ */
+function entityPrefixLength(value) {
+ var prefix;
+
+ /* istanbul ignore if - Currently also tested for at
+ * implemention, but we keep it here because that’s
+ * proper. */
+ if (value.charAt(0) !== AMPERSAND) {
+ return 0;
+ }
+
+ prefix = value.split(AMPERSAND, 2).join(AMPERSAND);
+
+ return prefix.length - decode(prefix).length;
+}
+
+/**
+ * Checks if a string starts with HTML entity.
+ *
+ * @example
+ * startsWithEntity('&copycat') // true
+ * startsWithEntity('&foo &amp &bar') // false
+ *
+ * @param {string} value - Value to check.
+ * @return {number} - Whether `value` starts an entity.
+ */
+function startsWithEntity(value) {
+ return entityPrefixLength(value) > 0;
+}
+
+/**
+ * Check if `character` is a valid alignment row character.
+ *
+ * @example
+ * isAlignmentRowCharacter(':') // true
+ * isAlignmentRowCharacter('=') // false
+ *
+ * @param {string} character - Character to check.
+ * @return {boolean} - Whether `character` is a valid
+ * alignment row character.
+ */
+function isAlignmentRowCharacter(character) {
+ return character === COLON ||
+ character === DASH ||
+ character === SPACE ||
+ character === PIPE;
+}
+
+/**
+ * Check if `index` in `value` is inside an alignment row.
+ *
+ * @example
+ * isInAlignmentRow(':--:', 2) // true
+ * isInAlignmentRow(':--:\n:-*-:', 9) // false
+ *
+ * @param {string} value - Value to check.
+ * @param {number} index - Position in `value` to check.
+ * @return {boolean} - Whether `index` in `value` is in
+ * an alignment row.
+ */
+function isInAlignmentRow(value, index) {
+ var length = value.length;
+ var start = index;
+ var character;
+
+ while (++index < length) {
+ character = value.charAt(index);
+
+ if (character === LINE) {
+ break;
+ }
+
+ if (!isAlignmentRowCharacter(character)) {
+ return false;
+ }
+ }
+
+ index = start;
+
+ while (--index > -1) {
+ character = value.charAt(index);
+
+ if (character === LINE) {
+ break;
+ }
+
+ if (!isAlignmentRowCharacter(character)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Factory to escape characters.
+ *
+ * @example
+ * var escape = escapeFactory({ commonmark: true });
+ * escape('x*x', { type: 'text', value: 'x*x' }) // 'x\\*x'
+ *
+ * @param {Object} options - Compiler options.
+ * @return {function(value, node, parent): string} - Function which
+ * takes a value and a node and (optionally) its parent and returns
+ * its escaped value.
+ */
+function escapeFactory(options) {
+ /**
+ * Escape punctuation characters in a node's value.
+ *
+ * @param {string} value - Value to escape.
+ * @param {Object} node - Node in which `value` exists.
+ * @param {Object} [parent] - Parent of `node`.
+ * @return {string} - Escaped `value`.
+ */
+ return function escape(value, node, parent) {
+ var self = this;
+ var gfm = options.gfm;
+ var commonmark = options.commonmark;
+ var pedantic = options.pedantic;
+ var siblings = parent && parent.children;
+ var index = siblings && siblings.indexOf(node);
+ var prev = siblings && siblings[index - 1];
+ var next = siblings && siblings[index + 1];
+ var length = value.length;
+ var position = -1;
+ var queue = [];
+ var escaped = queue;
+ var afterNewLine;
+ var character;
+ var wordCharBefore;
+ var wordCharAfter;
+ var offset;
+
+ if (prev) {
+ afterNewLine = prev.type === 'text' && /\n\s*$/.test(prev.value);
+ } else if (parent) {
+ afterNewLine = parent.type === 'paragraph';
+ }
+
+ while (++position < length) {
+ character = value.charAt(position);
+
+ if (
+ character === BACKSLASH ||
+ character === TICK ||
+ character === ASTERISK ||
+ character === SQUARE_BRACKET_OPEN ||
+ (
+ character === UNDERSCORE &&
+ /*
+ * Delegate leading/trailing underscores
+ * to the multinode version below.
+ */
+ 0 < position &&
+ position < length - 1 &&
+ (
+ pedantic ||
+ !isAlphanumeric(value.charAt(position - 1)) ||
+ !isAlphanumeric(value.charAt(position + 1))
+ )
+ ) ||
+ (self.inLink && character === SQUARE_BRACKET_CLOSE) ||
+ (
+ gfm &&
+ character === PIPE &&
+ (
+ self.inTable ||
+ isInAlignmentRow(value, position)
+ )
+ )
+ ) {
+ afterNewLine = false;
+ queue.push(BACKSLASH);
+ } else if (character === ANGLE_BRACKET_OPEN) {
+ afterNewLine = false;
+
+ if (commonmark) {
+ queue.push(BACKSLASH);
+ } else {
+ queue.push(ENTITY_ANGLE_BRACKET_OPEN);
+ continue;
+ }
+ } else if (
+ gfm &&
+ !self.inLink &&
+ character === COLON &&
+ (
+ queue.slice(-6).join(EMPTY) === 'mailto' ||
+ queue.slice(-5).join(EMPTY) === 'https' ||
+ queue.slice(-4).join(EMPTY) === 'http'
+ )
+ ) {
+ afterNewLine = false;
+
+ if (commonmark) {
+ queue.push(BACKSLASH);
+ } else {
+ queue.push(ENTITY_COLON);
+ continue;
+ }
+ /* istanbul ignore if - Impossible to test with
+ * the current set-up. We need tests which try
+ * to force markdown content into the tree. */
+ } else if (
+ character === AMPERSAND &&
+ startsWithEntity(value.slice(position))
+ ) {
+ afterNewLine = false;
+
+ if (commonmark) {
+ queue.push(BACKSLASH);
+ } else {
+ queue.push(ENTITY_AMPERSAND);
+ continue;
+ }
+ } else if (
+ gfm &&
+ character === TILDE &&
+ value.charAt(position + 1) === TILDE
+ ) {
+ queue.push(BACKSLASH, TILDE);
+ afterNewLine = false;
+ position += 1;
+ } else if (character === LINE) {
+ afterNewLine = true;
+ } else if (afterNewLine) {
+ if (
+ character === ANGLE_BRACKET_CLOSE ||
+ character === HASH ||
+ LIST_BULLETS[character]
+ ) {
+ queue.push(BACKSLASH);
+ afterNewLine = false;
+ } else if (isNumeric(character)) {
+ offset = position + 1;
+
+ while (offset < length) {
+ if (!isNumeric(value.charAt(offset))) {
+ break;
+ }
+
+ offset++;
+ }
+
+ if (
+ value.charAt(offset) === DOT ||
+ (
+ commonmark &&
+ /* istanbul ignore next - hard to test :( */
+ value.charAt(offset) === PARENTHESIS_CLOSE
+ )
+ ) {
+ queue.push(value.slice(position, offset), BACKSLASH);
+ position = offset;
+ character = value.charAt(position);
+ }
+
+ afterNewLine = false;
+ } else if (
+ character !== SPACE &&
+ character !== TAB &&
+ character !== CARRIAGE &&
+ character !== VERTICAL_TAB &&
+ character !== FORM_FEED
+ ) {
+ afterNewLine = false;
+ }
+ }
+
+ queue.push(character);
+ }
+
+ /*
+ * Multi-node versions.
+ */
+
+ if (siblings && node.type === 'text') {
+ /*
+ * Check for an opening parentheses after a
+ * link-reference (which can be joined by
+ * white-space).
+ */
+
+ if (
+ prev &&
+ prev.referenceType === 'shortcut'
+ ) {
+ position = -1;
+ length = escaped.length;
+
+ while (++position < length) {
+ character = escaped[position];
+
+ if (character === SPACE || character === TAB) {
+ continue;
+ }
+
+ if (character === PARENTHESIS_OPEN) {
+ escaped[position] = BACKSLASH + character;
+ }
+
+ if (character === COLON) {
+ if (commonmark) {
+ escaped[position] = BACKSLASH + character;
+ } else {
+ escaped[position] = ENTITY_COLON;
+ }
+ }
+
+ break;
+ }
+
+ /*
+ * If the current node is all spaces / tabs,
+ * preceded by a shortcut, and followed by
+ * a text starting with `(`, escape it.
+ */
+
+ if (
+ next &&
+ position === length &&
+ next.type === 'text' &&
+ next.value.charAt(0) === PARENTHESIS_OPEN
+ ) {
+ escaped.push(BACKSLASH);
+ }
+ }
+
+ /*
+ * Ensure non-auto-links are not seen as links.
+ * This pattern needs to check the preceding
+ * nodes too.
+ */
+
+ if (
+ gfm &&
+ !self.inLink &&
+ prev &&
+ prev.type === 'text' &&
+ value.charAt(0) === COLON
+ ) {
+ queue = prev.value.slice(-6);
+
+ if (
+ queue === 'mailto' ||
+ queue.slice(-5) === 'https' ||
+ queue.slice(-4) === 'http'
+ ) {
+ if (commonmark) {
+ escaped.unshift(BACKSLASH);
+ } else {
+ escaped.splice(0, 1, ENTITY_COLON);
+ }
+ }
+ }
+
+ /*
+ * Escape ampersand if it would otherwise
+ * start an entity.
+ */
+
+ if (
+ next &&
+ next.type === 'text' &&
+ value.slice(-1) === AMPERSAND &&
+ startsWithEntity(AMPERSAND + next.value)
+ ) {
+ if (commonmark) {
+ escaped.splice(escaped.length - 1, 0, BACKSLASH);
+ } else {
+ escaped.push('amp', SEMICOLON);
+ }
+ }
+
+ /*
+ * Escape double tildes in GFM.
+ */
+
+ if (
+ gfm &&
+ next &&
+ next.type === 'text' &&
+ value.slice(-1) === TILDE &&
+ next.value.charAt(0) === TILDE
+ ) {
+ escaped.splice(escaped.length - 1, 0, BACKSLASH);
+ }
+
+ /*
+ * Escape underscores, but not mid-word (unless
+ * in pedantic mode).
+ */
+
+ wordCharBefore = prev &&
+ prev.type === 'text' &&
+ isAlphanumeric(prev.value.slice(-1))
+
+ wordCharAfter = next &&
+ next.type === 'text' &&
+ isAlphanumeric(next.value.charAt(0))
+
+ if (length <= 1) {
+ if (
+ value === UNDERSCORE &&
+ (
+ pedantic ||
+ !wordCharBefore ||
+ !wordCharAfter
+ )
+ ) {
+ escaped.unshift(BACKSLASH);
+ }
+ } else {
+ if (
+ value.charAt(0) === UNDERSCORE &&
+ (
+ pedantic ||
+ !wordCharBefore ||
+ /* istanbul ignore next - only for trees */
+ !isAlphanumeric(value.charAt(1))
+ )
+ ) {
+ escaped.unshift(BACKSLASH);
+ }
+
+ if (
+ value.slice(-1) === UNDERSCORE &&
+ (
+ pedantic ||
+ !wordCharAfter ||
+ /* istanbul ignore next - only for trees */
+ !isAlphanumeric(value.slice(-2).charAt(0))
+ )
+ ) {
+ escaped.splice(escaped.length - 1, 0, BACKSLASH);
+ }
+ }
+ }
+
+ return escaped.join(EMPTY);
+ };
+}
+
+/**
+ * Wrap `url` in angle brackets when needed, or when
+ * forced.
+ *
+ * In links, images, and definitions, the URL part needs
+ * to be enclosed when it:
+ *
+ * - has a length of `0`;
+ * - contains white-space;
+ * - has more or less opening than closing parentheses.
+ *
+ * @example
+ * encloseURI('foo bar') // '<foo bar>'
+ * encloseURI('foo(bar(baz)') // '<foo(bar(baz)>'
+ * encloseURI('') // '<>'
+ * encloseURI('example.com') // 'example.com'
+ * encloseURI('example.com', true) // '<example.com>'
+ *
+ * @param {string} uri - URI to enclose.
+ * @param {boolean?} [always] - Force enclosing.
+ * @return {boolean} - Properly enclosed `uri`.
+ */
+function encloseURI(uri, always) {
+ if (
+ always ||
+ !uri.length ||
+ EXPRESSIONS_WHITE_SPACE.test(uri) ||
+ ccount(uri, PARENTHESIS_OPEN) !== ccount(uri, PARENTHESIS_CLOSE)
+ ) {
+ return ANGLE_BRACKET_OPEN + uri + ANGLE_BRACKET_CLOSE;
+ }
+
+ return uri;
+}
+
+/**
+ * There is currently no way to support nested delimiters
+ * across Markdown.pl, CommonMark, and GitHub (RedCarpet).
+ * The following code supports Markdown.pl and GitHub.
+ * CommonMark is not supported when mixing double- and
+ * single quotes inside a title.
+ *
+ * @see https://github.com/vmg/redcarpet/issues/473
+ * @see https://github.com/jgm/CommonMark/issues/308
+ *
+ * @example
+ * encloseTitle('foo') // '"foo"'
+ * encloseTitle('foo \'bar\' baz') // '"foo \'bar\' baz"'
+ * encloseTitle('foo "bar" baz') // '\'foo "bar" baz\''
+ * encloseTitle('foo "bar" \'baz\'') // '"foo "bar" \'baz\'"'
+ *
+ * @param {string} title - Content.
+ * @return {string} - Properly enclosed title.
+ */
+function encloseTitle(title) {
+ var delimiter = QUOTE_DOUBLE;
+
+ if (title.indexOf(delimiter) !== -1) {
+ delimiter = QUOTE_SINGLE;
+ }
+
+ return delimiter + title + delimiter;
+}
+
+/**
+ * Pad `value` with `level * INDENT` spaces. Respects
+ * lines. Ignores empty lines.
+ *
+ * @example
+ * pad('foo', 1) // ' foo'
+ *
+ * @param {string} value - Content.
+ * @param {number} level - Indentation level.
+ * @return {string} - Padded `value`.
+ */
+function pad(value, level) {
+ var index;
+ var padding;
+
+ value = value.split(LINE);
+
+ index = value.length;
+ padding = repeat(SPACE, level * INDENT);
+
+ while (index--) {
+ if (value[index].length !== 0) {
+ value[index] = padding + value[index];
+ }
+ }
+
+ return value.join(LINE);
+}
+
+/**
+ * Construct a new compiler.
+ *
+ * @example
+ * var compiler = new Compiler(new File('> foo.'));
+ *
+ * @constructor
+ * @class {Compiler}
+ * @param {File} file - Virtual file.
+ * @param {Object?} [options] - Passed to
+ * `Compiler#setOptions()`.
+ */
+function Compiler(file, options) {
+ var self = this;
+
+ self.file = file;
+
+ self.options = extend({}, self.options);
+
+ self.setOptions(options);
+}
+
+/*
+ * Cache prototype.
+ */
+
+var compilerPrototype = Compiler.prototype;
+
+/*
+ * Expose defaults.
+ */
+
+compilerPrototype.options = defaultOptions;
+
+/*
+ * Expose visitors.
+ */
+
+var visitors = compilerPrototype.visitors = {};
+
+/*
+ * Map of applicable enum's.
+ */
+
+var maps = {
+ 'entities': ENTITY_OPTIONS,
+ 'bullet': LIST_BULLETS,
+ 'rule': THEMATIC_BREAK_BULLETS,
+ 'listItemIndent': LIST_ITEM_INDENTS,
+ 'emphasis': EMPHASIS_MARKERS,
+ 'strong': EMPHASIS_MARKERS,
+ 'fence': FENCE_MARKERS
+};
+
+/**
+ * Set options. Does not overwrite previously set
+ * options.
+ *
+ * @example
+ * var compiler = new Compiler();
+ * compiler.setOptions({bullet: '*'});
+ *
+ * @this {Compiler}
+ * @throws {Error} - When an option is invalid.
+ * @param {Object?} [options] - Stringify settings.
+ * @return {Compiler} - `self`.
+ */
+compilerPrototype.setOptions = function (options) {
+ var self = this;
+ var current = self.options;
+ var ruleRepetition;
+ var key;
+
+ if (options === null || options === undefined) {
+ options = {};
+ } else if (typeof options === 'object') {
+ options = extend({}, options);
+ } else {
+ throw new Error(
+ 'Invalid value `' + options + '` ' +
+ 'for setting `options`'
+ );
+ }
+
+ for (key in defaultOptions) {
+ validate[typeof current[key]](
+ options, key, current[key], maps[key]
+ );
+ }
+
+ ruleRepetition = options.ruleRepetition;
+
+ if (ruleRepetition && ruleRepetition < MINIMUM_RULE_LENGTH) {
+ raise(ruleRepetition, 'options.ruleRepetition');
+ }
+
+ self.encode = encodeFactory(String(options.entities));
+ self.escape = escapeFactory(options);
+
+ self.options = options;
+
+ return self;
+};
+
+/*
+ * Enter and exit helpers.
+ */
+
+compilerPrototype.enterLink = toggler('inLink', false);
+compilerPrototype.enterTable = toggler('inTable', false);
+
+/**
+ * Shortcut and collapsed link references need no escaping
+ * and encoding during the processing of child nodes (it
+ * must be implied from identifier).
+ *
+ * This toggler turns encoding and escaping off for shortcut
+ * and collapsed references.
+ *
+ * Implies `enterLink`.
+ *
+ * @param {Compiler} compiler - Compiler instance.
+ * @param {LinkReference} node - LinkReference node.
+ * @return {Function} - Exit state.
+ */
+compilerPrototype.enterLinkReference = function (compiler, node) {
+ var encode = compiler.encode;
+ var escape = compiler.escape;
+ var exitLink = compiler.enterLink();
+
+ if (
+ node.referenceType === 'shortcut' ||
+ node.referenceType === 'collapsed'
+ ) {
+ compiler.encode = compiler.escape = encodeNoop;
+ return function () {
+ compiler.encode = encode;
+ compiler.escape = escape;
+ exitLink();
+ };
+ } else {
+ return exitLink;
+ }
+};
+
+/**
+ * Visit a node.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.visit({
+ * type: 'strong',
+ * children: [{
+ * type: 'text',
+ * value: 'Foo'
+ * }]
+ * });
+ * // '**Foo**'
+ *
+ * @param {Object} node - Node.
+ * @param {Object?} [parent] - `node`s parent.
+ * @return {string} - Compiled `node`.
+ */
+compilerPrototype.visit = function (node, parent) {
+ var self = this;
+
+ /*
+ * Fail on unknown nodes.
+ */
+
+ if (typeof self.visitors[node.type] !== 'function') {
+ self.file.fail(
+ 'Missing compiler for node of type `' +
+ node.type + '`: `' + node + '`',
+ node
+ );
+ }
+
+ return self.visitors[node.type].call(self, node, parent);
+};
+
+/**
+ * Visit all children of `parent`.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.all({
+ * type: 'strong',
+ * children: [{
+ * type: 'text',
+ * value: 'Foo'
+ * },
+ * {
+ * type: 'text',
+ * value: 'Bar'
+ * }]
+ * });
+ * // ['Foo', 'Bar']
+ *
+ * @param {Object} parent - Parent node of children.
+ * @return {Array.<string>} - List of compiled children.
+ */
+compilerPrototype.all = function (parent) {
+ var self = this;
+ var children = parent.children;
+ var values = [];
+ var results = [];
+ var length = children.length;
+ var index = 0;
+ var node = children[0];
+ var next;
+
+ if (!length) {
+ return values;
+ }
+
+ length++;
+
+ while (++index < length) {
+ next = children[index];
+
+ if (
+ next &&
+ node.type === 'text' &&
+ node.type === next.type &&
+ mergeable(node) &&
+ mergeable(next)
+ ) {
+ node.value += next.value;
+ } else {
+ values.push(node);
+ node = next;
+ }
+ }
+
+ index = -1;
+ length = values.length;
+ parent.children = values;
+
+ while (++index < length) {
+ results[index] = self.visit(values[index], parent);
+ }
+
+ return results;
+};
+
+/**
+ * Visit ordered list items.
+ *
+ * Starts the list with
+ * `node.start` and increments each following list item
+ * bullet by one:
+ *
+ * 2. foo
+ * 3. bar
+ *
+ * In `incrementListMarker: false` mode, does not increment
+ * each marker and stays on `node.start`:
+ *
+ * 1. foo
+ * 1. bar
+ *
+ * Adds an extra line after an item if it has
+ * `loose: true`.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.visitOrderedItems({
+ * type: 'list',
+ * ordered: true,
+ * children: [{
+ * type: 'listItem',
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }]
+ * });
+ * // '1. bar'
+ *
+ * @param {Object} node - `list` node with
+ * `ordered: true`.
+ * @return {string} - Markdown list.
+ */
+compilerPrototype.visitOrderedItems = function (node) {
+ var self = this;
+ var increment = self.options.incrementListMarker;
+ var values = [];
+ var start = node.start;
+ var children = node.children;
+ var length = children.length;
+ var index = -1;
+ var bullet;
+ var fn = self.visitors.listItem;
+
+ while (++index < length) {
+ bullet = (increment ? start + index : start) + DOT;
+ values[index] = fn.call(self, children[index], node, index, bullet);
+ }
+
+ return values.join(LINE);
+};
+
+/**
+ * Visit unordered list items.
+ *
+ * Uses `options.bullet` as each item's bullet.
+ *
+ * Adds an extra line after an item if it has
+ * `loose: true`.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.visitUnorderedItems({
+ * type: 'list',
+ * ordered: false,
+ * children: [{
+ * type: 'listItem',
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }]
+ * });
+ * // '- bar'
+ *
+ * @param {Object} node - `list` node with
+ * `ordered: false`.
+ * @return {string} - Markdown list.
+ */
+compilerPrototype.visitUnorderedItems = function (node) {
+ var self = this;
+ var values = [];
+ var children = node.children;
+ var length = children.length;
+ var index = -1;
+ var bullet = self.options.bullet;
+ var fn = self.visitors.listItem;
+
+ while (++index < length) {
+ values[index] = fn.call(self, children[index], node, index, bullet);
+ }
+
+ return values.join(LINE);
+};
+
+/**
+ * Stringify a block node with block children (e.g., `root`
+ * or `blockquote`).
+ *
+ * Knows about code following a list, or adjacent lists
+ * with similar bullets, and places an extra newline
+ * between them.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.block({
+ * type: 'root',
+ * children: [{
+ * type: 'paragraph',
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }]
+ * });
+ * // 'bar'
+ *
+ * @param {Object} node - `root` node.
+ * @return {string} - Markdown block content.
+ */
+compilerPrototype.block = function (node) {
+ var self = this;
+ var values = [];
+ var children = node.children;
+ var length = children.length;
+ var index = -1;
+ var child;
+ var prev;
+
+ while (++index < length) {
+ child = children[index];
+
+ if (prev) {
+ /*
+ * Duplicate nodes, such as a list
+ * directly following another list,
+ * often need multiple new lines.
+ *
+ * Additionally, code blocks following a list
+ * might easily be mistaken for a paragraph
+ * in the list itself.
+ */
+
+ if (child.type === prev.type && prev.type === 'list') {
+ values.push(prev.ordered === child.ordered ? GAP : BREAK);
+ } else if (
+ prev.type === 'list' &&
+ child.type === 'code' &&
+ !child.lang
+ ) {
+ values.push(GAP);
+ } else {
+ values.push(BREAK);
+ }
+ }
+
+ values.push(self.visit(child, node));
+
+ prev = child;
+ }
+
+ return values.join(EMPTY);
+};
+
+/**
+ * Stringify a root.
+ *
+ * Adds a final newline to ensure valid POSIX files.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.root({
+ * type: 'root',
+ * children: [{
+ * type: 'paragraph',
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }]
+ * });
+ * // 'bar'
+ *
+ * @param {Object} node - `root` node.
+ * @return {string} - Markdown document.
+ */
+visitors.root = function (node) {
+ return this.block(node) + LINE;
+};
+
+/**
+ * Stringify a heading.
+ *
+ * In `setext: true` mode and when `depth` is smaller than
+ * three, creates a setext header:
+ *
+ * Foo
+ * ===
+ *
+ * Otherwise, an ATX header is generated:
+ *
+ * ### Foo
+ *
+ * In `closeAtx: true` mode, the header is closed with
+ * hashes:
+ *
+ * ### Foo ###
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.heading({
+ * type: 'heading',
+ * depth: 2,
+ * children: [{
+ * type: 'strong',
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }]
+ * });
+ * // '## **bar**'
+ *
+ * @param {Object} node - `heading` node.
+ * @return {string} - Markdown heading.
+ */
+visitors.heading = function (node) {
+ var self = this;
+ var setext = self.options.setext;
+ var closeAtx = self.options.closeAtx;
+ var depth = node.depth;
+ var content = self.all(node).join(EMPTY);
+ var prefix;
+
+ if (setext && depth < 3) {
+ return content + LINE +
+ repeat(depth === 1 ? EQUALS : DASH, content.length);
+ }
+
+ prefix = repeat(HASH, node.depth);
+ content = prefix + SPACE + content;
+
+ if (closeAtx) {
+ content += SPACE + prefix;
+ }
+
+ return content;
+};
+
+/**
+ * Stringify text.
+ *
+ * Supports named entities in `settings.encode: true` mode:
+ *
+ * AT&amp;T
+ *
+ * Supports numbered entities in `settings.encode: numbers`
+ * mode:
+ *
+ * AT&#x26;T
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.text({
+ * type: 'text',
+ * value: 'foo'
+ * });
+ * // 'foo'
+ *
+ * @param {Object} node - `text` node.
+ * @param {Object} parent - Parent of `node`.
+ * @return {string} - Raw markdown text.
+ */
+visitors.text = function (node, parent) {
+ return this.encode(this.escape(node.value, node, parent), node);
+};
+
+/**
+ * Stringify a paragraph.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.paragraph({
+ * type: 'paragraph',
+ * children: [{
+ * type: 'strong',
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }]
+ * });
+ * // '**bar**'
+ *
+ * @param {Object} node - `paragraph` node.
+ * @return {string} - Markdown paragraph.
+ */
+visitors.paragraph = function (node) {
+ return this.all(node).join(EMPTY);
+};
+
+/**
+ * Stringify a block quote.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.paragraph({
+ * type: 'blockquote',
+ * children: [{
+ * type: 'paragraph',
+ * children: [{
+ * type: 'strong',
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }]
+ * }]
+ * });
+ * // '> **bar**'
+ *
+ * @param {Object} node - `blockquote` node.
+ * @return {string} - Markdown block quote.
+ */
+visitors.blockquote = function (node) {
+ var values = this.block(node).split(LINE);
+ var result = [];
+ var length = values.length;
+ var index = -1;
+ var value;
+
+ while (++index < length) {
+ value = values[index];
+ result[index] = (value ? SPACE : EMPTY) + value;
+ }
+
+ return ANGLE_BRACKET_CLOSE + result.join(LINE + ANGLE_BRACKET_CLOSE);
+};
+
+/**
+ * Stringify a list. See `Compiler#visitOrderedList()` and
+ * `Compiler#visitUnorderedList()` for internal working.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.visitUnorderedItems({
+ * type: 'list',
+ * ordered: false,
+ * children: [{
+ * type: 'listItem',
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }]
+ * });
+ * // '- bar'
+ *
+ * @param {Object} node - `list` node.
+ * @return {string} - Markdown list.
+ */
+visitors.list = function (node) {
+ return this[ORDERED_MAP[node.ordered]](node);
+};
+
+/**
+ * Stringify a list item.
+ *
+ * Prefixes the content with a checked checkbox when
+ * `checked: true`:
+ *
+ * [x] foo
+ *
+ * Prefixes the content with an unchecked checkbox when
+ * `checked: false`:
+ *
+ * [ ] foo
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.listItem({
+ * type: 'listItem',
+ * checked: true,
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }, {
+ * type: 'list',
+ * ordered: false,
+ * children: [{
+ * type: 'listItem',
+ * checked: true,
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }]
+ * }, 0, '*');
+ * '- [x] bar'
+ *
+ * @param {Object} node - `listItem` node.
+ * @param {Object} parent - `list` node.
+ * @param {number} position - Index of `node` in `parent`.
+ * @param {string} bullet - Bullet to use. This, and the
+ * `listItemIndent` setting define the used indent.
+ * @return {string} - Markdown list item.
+ */
+visitors.listItem = function (node, parent, position, bullet) {
+ var self = this;
+ var style = self.options.listItemIndent;
+ var children = node.children;
+ var values = [];
+ var index = -1;
+ var length = children.length;
+ var loose = node.loose;
+ var value;
+ var indent;
+ var spacing;
+
+ while (++index < length) {
+ values[index] = self.visit(children[index], node);
+ }
+
+ value = CHECKBOX_MAP[node.checked] + values.join(loose ? BREAK : LINE);
+
+ if (
+ style === LIST_ITEM_ONE ||
+ (style === LIST_ITEM_MIXED && value.indexOf(LINE) === -1)
+ ) {
+ indent = bullet.length + 1;
+ spacing = SPACE;
+ } else {
+ indent = Math.ceil((bullet.length + 1) / INDENT) * INDENT;
+ spacing = repeat(SPACE, indent - bullet.length);
+ }
+
+ value = bullet + spacing + pad(value, indent / INDENT).slice(indent);
+
+ if (loose && parent.children.length - 1 !== position) {
+ value += LINE;
+ }
+
+ return value;
+};
+
+/**
+ * Stringify inline code.
+ *
+ * Knows about internal ticks (`\``), and ensures one more
+ * tick is used to enclose the inline code:
+ *
+ * ```foo ``bar`` baz```
+ *
+ * Even knows about inital and final ticks:
+ *
+ * `` `foo ``
+ * `` foo` ``
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.inlineCode({
+ * type: 'inlineCode',
+ * value: 'foo(); `bar`; baz()'
+ * });
+ * // '``foo(); `bar`; baz()``'
+ *
+ * @param {Object} node - `inlineCode` node.
+ * @return {string} - Markdown inline code.
+ */
+visitors.inlineCode = function (node) {
+ var value = node.value;
+ var ticks = repeat(TICK, longestStreak(value, TICK) + 1);
+ var start = ticks;
+ var end = ticks;
+
+ if (value.charAt(0) === TICK) {
+ start += SPACE;
+ }
+
+ if (value.charAt(value.length - 1) === TICK) {
+ end = SPACE + end;
+ }
+
+ return start + node.value + end;
+};
+
+/**
+ * Stringify YAML front matter.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.yaml({
+ * type: 'yaml',
+ * value: 'foo: bar'
+ * });
+ * // '---\nfoo: bar\n---'
+ *
+ * @param {Object} node - `yaml` node.
+ * @return {string} - Markdown YAML document.
+ */
+visitors.yaml = function (node) {
+ var delimiter = repeat(DASH, YAML_FENCE_LENGTH);
+ var value = node.value ? LINE + node.value : EMPTY;
+
+ return delimiter + value + LINE + delimiter;
+};
+
+/**
+ * Stringify a code block.
+ *
+ * Creates indented code when:
+ *
+ * - No language tag exists;
+ * - Not in `fences: true` mode;
+ * - A non-empty value exists.
+ *
+ * Otherwise, GFM fenced code is created:
+ *
+ * ```js
+ * foo();
+ * ```
+ *
+ * When in ``fence: `~` `` mode, uses tildes as fences:
+ *
+ * ~~~js
+ * foo();
+ * ~~~
+ *
+ * Knows about internal fences (Note: GitHub/Kramdown does
+ * not support this):
+ *
+ * ````javascript
+ * ```markdown
+ * foo
+ * ```
+ * ````
+ *
+ * Supports named entities in the language flag with
+ * `settings.encode` mode.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.code({
+ * type: 'code',
+ * lang: 'js',
+ * value: 'fooo();'
+ * });
+ * // '```js\nfooo();\n```'
+ *
+ * @param {Object} node - `code` node.
+ * @param {Object} parent - Parent of `node`.
+ * @return {string} - Markdown code block.
+ */
+visitors.code = function (node, parent) {
+ var self = this;
+ var value = node.value;
+ var options = self.options;
+ var marker = options.fence;
+ var language = self.encode(node.lang || EMPTY, node);
+ var fence;
+
+ /*
+ * Without (needed) fences.
+ */
+
+ if (!language && !options.fences && value) {
+ /*
+ * Throw when pedantic, in a list item which
+ * isn’t compiled using a tab.
+ */
+
+ if (
+ parent &&
+ parent.type === 'listItem' &&
+ options.listItemIndent !== LIST_ITEM_TAB &&
+ options.pedantic
+ ) {
+ self.file.fail(ERROR_LIST_ITEM_INDENT, node.position);
+ }
+
+ return pad(value, 1);
+ }
+
+ fence = longestStreak(value, marker) + 1;
+
+ /*
+ * Fix GFM / RedCarpet bug, where fence-like characters
+ * inside fenced code can exit a code-block.
+ * Yes, even when the outer fence uses different
+ * characters, or is longer.
+ * Thus, we can only pad the code to make it work.
+ */
+
+ if (FENCE.test(value)) {
+ value = pad(value, 1);
+ }
+
+ fence = repeat(marker, Math.max(fence, MINIMUM_CODE_FENCE_LENGTH));
+
+ return fence + language + LINE + value + LINE + fence;
+};
+
+/**
+ * Stringify HTML.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.html({
+ * type: 'html',
+ * value: '<div>bar</div>'
+ * });
+ * // '<div>bar</div>'
+ *
+ * @param {Object} node - `html` node.
+ * @return {string} - Markdown HTML.
+ */
+visitors.html = function (node) {
+ return node.value;
+};
+
+/**
+ * Stringify a horizontal rule.
+ *
+ * The character used is configurable by `rule`: (`'_'`)
+ *
+ * ___
+ *
+ * The number of repititions is defined through
+ * `ruleRepetition`: (`6`)
+ *
+ * ******
+ *
+ * Whether spaces delimit each character, is configured
+ * through `ruleSpaces`: (`true`)
+ *
+ * * * *
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.thematicBreak({
+ * type: 'thematicBreak'
+ * });
+ * // '***'
+ *
+ * @return {string} - Markdown rule.
+ */
+visitors.thematicBreak = function () {
+ var options = this.options;
+ var rule = repeat(options.rule, options.ruleRepetition);
+
+ if (options.ruleSpaces) {
+ rule = rule.split(EMPTY).join(SPACE);
+ }
+
+ return rule;
+};
+
+/**
+ * Stringify a strong.
+ *
+ * The marker used is configurable by `strong`, which
+ * defaults to an asterisk (`'*'`) but also accepts an
+ * underscore (`'_'`):
+ *
+ * _foo_
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.strong({
+ * type: 'strong',
+ * children: [{
+ * type: 'text',
+ * value: 'Foo'
+ * }]
+ * });
+ * // '**Foo**'
+ *
+ * @param {Object} node - `strong` node.
+ * @return {string} - Markdown strong-emphasised text.
+ */
+visitors.strong = function (node) {
+ var marker = this.options.strong;
+
+ marker = marker + marker;
+
+ return marker + this.all(node).join(EMPTY) + marker;
+};
+
+/**
+ * Stringify an emphasis.
+ *
+ * The marker used is configurable by `emphasis`, which
+ * defaults to an underscore (`'_'`) but also accepts an
+ * asterisk (`'*'`):
+ *
+ * *foo*
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.emphasis({
+ * type: 'emphasis',
+ * children: [{
+ * type: 'text',
+ * value: 'Foo'
+ * }]
+ * });
+ * // '_Foo_'
+ *
+ * @param {Object} node - `emphasis` node.
+ * @return {string} - Markdown emphasised text.
+ */
+visitors.emphasis = function (node) {
+ var marker = this.options.emphasis;
+
+ return marker + this.all(node).join(EMPTY) + marker;
+};
+
+/**
+ * Stringify a hard break.
+ *
+ * In Commonmark mode, trailing backslash form is used in order
+ * to preserve trailing whitespace that the line may end with,
+ * and also for better visibility.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.break({
+ * type: 'break'
+ * });
+ * // ' \n'
+ *
+ * @return {string} - Hard markdown break.
+ */
+visitors.break = function () {
+ return this.options.commonmark ? BACKSLASH + LINE : SPACE + SPACE + LINE;
+};
+
+/**
+ * Stringify a delete.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.delete({
+ * type: 'delete',
+ * children: [{
+ * type: 'text',
+ * value: 'Foo'
+ * }]
+ * });
+ * // '~~Foo~~'
+ *
+ * @param {Object} node - `delete` node.
+ * @return {string} - Markdown strike-through.
+ */
+visitors.delete = function (node) {
+ return DOUBLE_TILDE + this.all(node).join(EMPTY) + DOUBLE_TILDE;
+};
+
+/**
+ * Stringify a link.
+ *
+ * When no title exists, the compiled `children` equal
+ * `url`, and `url` starts with a protocol, an auto
+ * link is created:
+ *
+ * <http://example.com>
+ *
+ * Otherwise, is smart about enclosing `url` (see
+ * `encloseURI()`) and `title` (see `encloseTitle()`).
+ *
+ * [foo](<foo at bar dot com> 'An "example" e-mail')
+ *
+ * Supports named entities in the `url` and `title` when
+ * in `settings.encode` mode.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.link({
+ * type: 'link',
+ * url: 'http://example.com',
+ * title: 'Example Domain',
+ * children: [{
+ * type: 'text',
+ * value: 'Foo'
+ * }]
+ * });
+ * // '[Foo](http://example.com "Example Domain")'
+ *
+ * @param {Object} node - `link` node.
+ * @return {string} - Markdown link.
+ */
+visitors.link = function (node) {
+ var self = this;
+ var url = self.encode(node.url, node);
+ var exit = self.enterLink();
+ var escapedURL = self.encode(self.escape(node.url, node));
+ var value = self.all(node).join(EMPTY);
+
+ exit();
+
+ if (
+ node.title === null &&
+ PROTOCOL.test(url) &&
+ (escapedURL === value || escapedURL === MAILTO + value)
+ ) {
+ /*
+ * Backslash escapes do not work in autolinks,
+ * so we do not escape.
+ */
+
+ return encloseURI(self.encode(node.url), true);
+ }
+
+ url = encloseURI(url);
+
+ if (node.title) {
+ url += SPACE + encloseTitle(self.encode(self.escape(
+ node.title, node
+ ), node));
+ }
+
+ value = SQUARE_BRACKET_OPEN + value + SQUARE_BRACKET_CLOSE;
+
+ value += PARENTHESIS_OPEN + url + PARENTHESIS_CLOSE;
+
+ return value;
+};
+
+/**
+ * Stringify a link label.
+ *
+ * Because link references are easily, mistakingly,
+ * created (for example, `[foo]`), reference nodes have
+ * an extra property depicting how it looked in the
+ * original document, so stringification can cause minimal
+ * changes.
+ *
+ * @example
+ * label({
+ * type: 'referenceImage',
+ * referenceType: 'full',
+ * identifier: 'foo'
+ * });
+ * // '[foo]'
+ *
+ * label({
+ * type: 'referenceImage',
+ * referenceType: 'collapsed',
+ * identifier: 'foo'
+ * });
+ * // '[]'
+ *
+ * label({
+ * type: 'referenceImage',
+ * referenceType: 'shortcut',
+ * identifier: 'foo'
+ * });
+ * // ''
+ *
+ * @param {Object} node - `linkReference` or
+ * `imageReference` node.
+ * @return {string} - Markdown label reference.
+ */
+function label(node) {
+ var value = EMPTY;
+ var type = node.referenceType;
+
+ if (type === 'full') {
+ value = node.identifier;
+ }
+
+ if (type !== 'shortcut') {
+ value = SQUARE_BRACKET_OPEN + value + SQUARE_BRACKET_CLOSE;
+ }
+
+ return value;
+}
+
+/**
+ * For shortcut and collapsed reference links, the contents
+ * is also an identifier, so we need to restore the original
+ * encoding and escaping that were present in the source
+ * string.
+ *
+ * This function takes the unescaped & unencoded value from
+ * shortcut's child nodes and the identifier and encodes
+ * the former according to the latter.
+ *
+ * @example
+ * copyIdentifierEncoding('a*b', 'a\\*b*c')
+ * // 'a\\*b*c'
+ *
+ * @param {string} value - Unescaped and unencoded stringified
+ * link value.
+ * @param {string} identifier - Link identifier.
+ * @return {string} - Encoded link value.
+ */
+function copyIdentifierEncoding(value, identifier) {
+ var index = 0;
+ var position = 0;
+ var length = value.length;
+ var count = identifier.length;
+ var result = [];
+ var start;
+
+ while (index < length) {
+ /*
+ * Take next non-punctuation characters from `value`.
+ */
+
+ start = index;
+
+ while (
+ index < length &&
+ !PUNCTUATION.test(value.charAt(index))
+ ) {
+ index += 1;
+ }
+
+ result.push(value.slice(start, index));
+
+ /*
+ * Advance `position` to the next punctuation character.
+ */
+ while (
+ position < count &&
+ !PUNCTUATION.test(identifier.charAt(position))
+ ) {
+ position += 1;
+ }
+
+ /*
+ * Take next punctuation characters from `identifier`.
+ */
+ start = position;
+
+ while (
+ position < count &&
+ PUNCTUATION.test(identifier.charAt(position))
+ ) {
+ if (identifier.charAt(position) === AMPERSAND) {
+ position += entityPrefixLength(identifier.slice(position));
+ }
+ position += 1;
+ }
+
+ result.push(identifier.slice(start, position));
+
+ /*
+ * Advance `index` to the next non-punctuation character.
+ */
+ while (index < length && PUNCTUATION.test(value.charAt(index))) {
+ index += 1;
+ }
+ }
+
+ return result.join(EMPTY);
+}
+
+/**
+ * Stringify a link reference.
+ *
+ * See `label()` on how reference labels are created.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.linkReference({
+ * type: 'linkReference',
+ * referenceType: 'collapsed',
+ * identifier: 'foo',
+ * children: [{
+ * type: 'text',
+ * value: 'Foo'
+ * }]
+ * });
+ * // '[Foo][]'
+ *
+ * @param {Object} node - `linkReference` node.
+ * @return {string} - Markdown link reference.
+ */
+visitors.linkReference = function (node) {
+ var self = this;
+ var exitLinkReference = self.enterLinkReference(self, node);
+ var value = self.all(node).join(EMPTY);
+
+ exitLinkReference();
+
+ if (
+ node.referenceType === 'shortcut' ||
+ node.referenceType === 'collapsed'
+ ) {
+ value = copyIdentifierEncoding(value, node.identifier);
+ }
+
+ return SQUARE_BRACKET_OPEN + value + SQUARE_BRACKET_CLOSE + label(node);
+};
+
+/**
+ * Stringify an image reference.
+ *
+ * See `label()` on how reference labels are created.
+ *
+ * Supports named entities in the `alt` when
+ * in `settings.encode` mode.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.imageReference({
+ * type: 'imageReference',
+ * referenceType: 'full',
+ * identifier: 'foo',
+ * alt: 'Foo'
+ * });
+ * // '![Foo][foo]'
+ *
+ * @param {Object} node - `imageReference` node.
+ * @return {string} - Markdown image reference.
+ */
+visitors.imageReference = function (node) {
+ var alt = this.encode(node.alt, node) || EMPTY;
+
+ return EXCLAMATION_MARK +
+ SQUARE_BRACKET_OPEN + alt + SQUARE_BRACKET_CLOSE +
+ label(node);
+};
+
+/**
+ * Stringify a footnote reference.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.footnoteReference({
+ * type: 'footnoteReference',
+ * identifier: 'foo'
+ * });
+ * // '[^foo]'
+ *
+ * @param {Object} node - `footnoteReference` node.
+ * @return {string} - Markdown footnote reference.
+ */
+visitors.footnoteReference = function (node) {
+ return SQUARE_BRACKET_OPEN + CARET + node.identifier +
+ SQUARE_BRACKET_CLOSE;
+};
+
+/**
+ * Stringify a link- or image definition.
+ *
+ * Is smart about enclosing `url` (see `encloseURI()`) and
+ * `title` (see `encloseTitle()`).
+ *
+ * [foo]: <foo at bar dot com> 'An "example" e-mail'
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.definition({
+ * type: 'definition',
+ * url: 'http://example.com',
+ * title: 'Example Domain',
+ * identifier: 'foo'
+ * });
+ * // '[foo]: http://example.com "Example Domain"'
+ *
+ * @param {Object} node - `definition` node.
+ * @return {string} - Markdown link- or image definition.
+ */
+visitors.definition = function (node) {
+ var value = SQUARE_BRACKET_OPEN + node.identifier + SQUARE_BRACKET_CLOSE;
+ var url = encloseURI(node.url);
+
+ if (node.title) {
+ url += SPACE + encloseTitle(node.title);
+ }
+
+ return value + COLON + SPACE + url;
+};
+
+/**
+ * Stringify an image.
+ *
+ * Is smart about enclosing `url` (see `encloseURI()`) and
+ * `title` (see `encloseTitle()`).
+ *
+ * ![foo](</fav icon.png> 'My "favourite" icon')
+ *
+ * Supports named entities in `url`, `alt`, and `title`
+ * when in `settings.encode` mode.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.image({
+ * type: 'image',
+ * url: 'http://example.png/favicon.png',
+ * title: 'Example Icon',
+ * alt: 'Foo'
+ * });
+ * // '![Foo](http://example.png/favicon.png "Example Icon")'
+ *
+ * @param {Object} node - `image` node.
+ * @return {string} - Markdown image.
+ */
+visitors.image = function (node) {
+ var url = encloseURI(this.encode(node.url, node));
+ var value;
+
+ if (node.title) {
+ url += SPACE + encloseTitle(this.encode(node.title, node));
+ }
+
+ value = EXCLAMATION_MARK +
+ SQUARE_BRACKET_OPEN + this.encode(node.alt || EMPTY, node) +
+ SQUARE_BRACKET_CLOSE;
+
+ value += PARENTHESIS_OPEN + url + PARENTHESIS_CLOSE;
+
+ return value;
+};
+
+/**
+ * Stringify a footnote.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.footnote({
+ * type: 'footnote',
+ * children: [{
+ * type: 'text',
+ * value: 'Foo'
+ * }]
+ * });
+ * // '[^Foo]'
+ *
+ * @param {Object} node - `footnote` node.
+ * @return {string} - Markdown footnote.
+ */
+visitors.footnote = function (node) {
+ return SQUARE_BRACKET_OPEN + CARET + this.all(node).join(EMPTY) +
+ SQUARE_BRACKET_CLOSE;
+};
+
+/**
+ * Stringify a footnote definition.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.footnoteDefinition({
+ * type: 'footnoteDefinition',
+ * identifier: 'foo',
+ * children: [{
+ * type: 'paragraph',
+ * children: [{
+ * type: 'text',
+ * value: 'bar'
+ * }]
+ * }]
+ * });
+ * // '[^foo]: bar'
+ *
+ * @param {Object} node - `footnoteDefinition` node.
+ * @return {string} - Markdown footnote definition.
+ */
+visitors.footnoteDefinition = function (node) {
+ var id = node.identifier.toLowerCase();
+
+ return SQUARE_BRACKET_OPEN + CARET + id +
+ SQUARE_BRACKET_CLOSE + COLON + SPACE +
+ this.all(node).join(BREAK + repeat(SPACE, INDENT));
+};
+
+/**
+ * Stringify table.
+ *
+ * Creates a fenced table by default, but not in
+ * `looseTable: true` mode:
+ *
+ * Foo | Bar
+ * :-: | ---
+ * Baz | Qux
+ *
+ * NOTE: Be careful with `looseTable: true` mode, as a
+ * loose table inside an indented code block on GitHub
+ * renders as an actual table!
+ *
+ * Creates a spaces table by default, but not in
+ * `spacedTable: false`:
+ *
+ * |Foo|Bar|
+ * |:-:|---|
+ * |Baz|Qux|
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.table({
+ * type: 'table',
+ * align: ['center', null],
+ * children: [
+ * {
+ * type: 'tableRow',
+ * children: [
+ * {
+ * type: 'tableCell'
+ * children: [{
+ * type: 'text'
+ * value: 'Foo'
+ * }]
+ * },
+ * {
+ * type: 'tableCell'
+ * children: [{
+ * type: 'text'
+ * value: 'Bar'
+ * }]
+ * }
+ * ]
+ * },
+ * {
+ * type: 'tableRow',
+ * children: [
+ * {
+ * type: 'tableCell'
+ * children: [{
+ * type: 'text'
+ * value: 'Baz'
+ * }]
+ * },
+ * {
+ * type: 'tableCell'
+ * children: [{
+ * type: 'text'
+ * value: 'Qux'
+ * }]
+ * }
+ * ]
+ * }
+ * ]
+ * });
+ * // '| Foo | Bar |\n| :-: | --- |\n| Baz | Qux |'
+ *
+ * @param {Object} node - `table` node.
+ * @return {string} - Markdown table.
+ */
+visitors.table = function (node) {
+ var self = this;
+ var loose = self.options.looseTable;
+ var spaced = self.options.spacedTable;
+ var rows = node.children;
+ var index = rows.length;
+ var exit = self.enterTable();
+ var result = [];
+ var start;
+
+ while (index--) {
+ result[index] = self.all(rows[index]);
+ }
+
+ exit();
+
+ start = loose ? EMPTY : spaced ? PIPE + SPACE : PIPE;
+
+ return table(result, {
+ 'align': node.align,
+ 'start': start,
+ 'end': start.split(EMPTY).reverse().join(EMPTY),
+ 'delimiter': spaced ? SPACE + PIPE + SPACE : PIPE
+ });
+};
+
+/**
+ * Stringify a table cell.
+ *
+ * @example
+ * var compiler = new Compiler();
+ *
+ * compiler.tableCell({
+ * type: 'tableCell',
+ * children: [{
+ * type: 'text'
+ * value: 'Qux'
+ * }]
+ * });
+ * // 'Qux'
+ *
+ * @param {Object} node - `tableCell` node.
+ * @return {string} - Markdown table cell.
+ */
+visitors.tableCell = function (node) {
+ return this.all(node).join(EMPTY);
+};
+
+/**
+ * Stringify the bound file.
+ *
+ * @example
+ * var file = new VFile('__Foo__');
+ *
+ * file.namespace('mdast').tree = {
+ * type: 'strong',
+ * children: [{
+ * type: 'text',
+ * value: 'Foo'
+ * }]
+ * });
+ *
+ * new Compiler(file).compile();
+ * // '**Foo**'
+ *
+ * @this {Compiler}
+ * @param {Node} node - Syntax tree.
+ * @return {string} - Markdown document.
+ */
+compilerPrototype.compile = function (node) {
+ return this.visit(node);
+};
+
+/*
+ * Expose `stringify` on `module.exports`.
+ */
+
+module.exports = Compiler;