You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							256 lines
						
					
					
						
							5.3 KiB
						
					
					
				
			
		
		
	
	
							256 lines
						
					
					
						
							5.3 KiB
						
					
					
				var pkg = require('../package.json') | 
						|
var api = require('./api.js') | 
						|
 | 
						|
var parser = require('posthtml-parser') | 
						|
var render = require('posthtml-render') | 
						|
 | 
						|
/** | 
						|
 * @author Ivan Voischev (@voischev), | 
						|
 *         Anton Winogradov (@awinogradov), | 
						|
 *         Alexej Yaroshevich (@zxqfox), | 
						|
 *         Vasiliy (@Yeti-or) | 
						|
 * | 
						|
 * @requires api | 
						|
 * @requires posthtml-parser | 
						|
 * @requires posthtml-render | 
						|
 * | 
						|
 * @constructor PostHTML | 
						|
 * @param {Array} plugins - An array of PostHTML plugins | 
						|
 */ | 
						|
function PostHTML (plugins) { | 
						|
/** | 
						|
 * PostHTML Instance | 
						|
 * | 
						|
 * @prop plugins | 
						|
 * @prop options | 
						|
 */ | 
						|
  this.version = pkg.version | 
						|
  this.name = pkg.name | 
						|
  this.plugins = typeof plugins === 'function' ? [plugins] : plugins || [] | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * @requires posthtml-parser | 
						|
 * | 
						|
 * @param   {String} html - Input (HTML) | 
						|
 * @returns {Array}  tree - PostHTMLTree (JSON) | 
						|
 */ | 
						|
PostHTML.parser = parser | 
						|
/** | 
						|
 * @requires posthtml-render | 
						|
 * | 
						|
 * @param   {Array}  tree - PostHTMLTree (JSON) | 
						|
 * @returns {String} html - HTML | 
						|
 */ | 
						|
PostHTML.render = render | 
						|
 | 
						|
/** | 
						|
* @this posthtml | 
						|
* @param   {Function} plugin - A PostHTML plugin | 
						|
* @returns {Constructor} - this(PostHTML) | 
						|
* | 
						|
* **Usage** | 
						|
* ```js | 
						|
* ph.use((tree) => { tag: 'div', content: tree }) | 
						|
*   .process('<html>..</html>', {}) | 
						|
*   .then((result) => result)) | 
						|
* ``` | 
						|
*/ | 
						|
PostHTML.prototype.use = function () { | 
						|
  [].push.apply(this.plugins, arguments) | 
						|
  return this | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * @param   {String} html - Input (HTML) | 
						|
 * @param   {?Object} options - PostHTML Options | 
						|
 * @returns {Object<{html: String, tree: PostHTMLTree}>} - Sync Mode | 
						|
 * @returns {Promise<{html: String, tree: PostHTMLTree}>} - Async Mode (default) | 
						|
 * | 
						|
 * **Usage** | 
						|
 * | 
						|
 * **Sync** | 
						|
 * ```js | 
						|
 * ph.process('<html>..</html>', { sync: true }).html | 
						|
 * ``` | 
						|
 * | 
						|
 * **Async** | 
						|
 * ```js | 
						|
 * ph.process('<html>..</html>', {}).then((result) => result)) | 
						|
 * ``` | 
						|
 */ | 
						|
PostHTML.prototype.process = function (tree, options) { | 
						|
  /** | 
						|
   * ## PostHTML Options | 
						|
   * | 
						|
   * @type {Object} | 
						|
   * @prop {?Boolean} options.sync - enables sync mode, plugins will run synchronously, throws an error when used with async plugins | 
						|
   * @prop {?Function} options.parser - use custom parser, replaces default (posthtml-parser) | 
						|
   * @prop {?Function} options.render - use custom render, replaces default (posthtml-render) | 
						|
   * @prop {?Boolean} options.skipParse - disable parsing | 
						|
   */ | 
						|
  options = options || {} | 
						|
 | 
						|
  if (options.parser) parser = options.parser | 
						|
  if (options.render) render = options.render | 
						|
 | 
						|
  tree = options.skipParse ? tree : parser(tree) | 
						|
 | 
						|
  tree.options = options | 
						|
  tree.processor = this | 
						|
 | 
						|
  // sync mode | 
						|
  if (options.sync === true) { | 
						|
    this.plugins.forEach(function (plugin) { | 
						|
      apiExtend(tree) | 
						|
 | 
						|
      var result | 
						|
 | 
						|
      if (plugin.length === 2 || isPromise(result = plugin(tree))) { | 
						|
        throw new Error( | 
						|
          'Can’t process contents in sync mode because of async plugin: ' + plugin.name | 
						|
        ) | 
						|
      } | 
						|
      // return the previous tree unless result is fulfilled | 
						|
      tree = result || tree | 
						|
    }) | 
						|
 | 
						|
    return lazyResult(render, tree) | 
						|
  } | 
						|
 | 
						|
  // async mode | 
						|
  var i = 0 | 
						|
 | 
						|
  var next = function (result, cb) { | 
						|
    // all plugins called | 
						|
    if (this.plugins.length <= i) { | 
						|
      cb(null, result) | 
						|
      return | 
						|
    } | 
						|
 | 
						|
    // little helper to go to the next iteration | 
						|
    function _next (res) { | 
						|
      return next(res || result, cb) | 
						|
    } | 
						|
 | 
						|
    // (re)extend the object | 
						|
    apiExtend(result) | 
						|
 | 
						|
    // call next | 
						|
    var plugin = this.plugins[i++] | 
						|
 | 
						|
    if (plugin.length === 2) { | 
						|
      plugin(result, function (err, res) { | 
						|
        if (err) return cb(err) | 
						|
        _next(res) | 
						|
      }) | 
						|
      return | 
						|
    } | 
						|
 | 
						|
    // sync and promised plugins | 
						|
    var err = null | 
						|
 | 
						|
    var res = tryCatch(function () { | 
						|
      return plugin(result) | 
						|
    }, function (err) { | 
						|
      return err | 
						|
    }) | 
						|
 | 
						|
    if (err) { | 
						|
      cb(err) | 
						|
      return | 
						|
    } | 
						|
 | 
						|
    if (isPromise(res)) { | 
						|
      res.then(_next).catch(cb) | 
						|
      return | 
						|
    } | 
						|
 | 
						|
    _next(res) | 
						|
  }.bind(this) | 
						|
 | 
						|
  return new Promise(function (resolve, reject) { | 
						|
    next(tree, function (err, tree) { | 
						|
      if (err) reject(err) | 
						|
      else resolve(lazyResult(render, tree)) | 
						|
    }) | 
						|
  }) | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * @exports posthtml | 
						|
 * | 
						|
 * @param  {Array} plugins | 
						|
 * @return {Function} posthtml | 
						|
 * | 
						|
 * **Usage** | 
						|
 * ```js | 
						|
 * import posthtml from 'posthtml' | 
						|
 * import plugin from 'posthtml-plugin' | 
						|
 * | 
						|
 * const ph = posthtml([ plugin() ]) | 
						|
 * ``` | 
						|
 */ | 
						|
module.exports = function (plugins) { | 
						|
  return new PostHTML(plugins) | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Checks if parameter is a Promise (or thenable) object. | 
						|
 * | 
						|
 * @private | 
						|
 * | 
						|
 * @param   {*} promise - Target `{}` to test | 
						|
 * @returns {Boolean} | 
						|
 */ | 
						|
function isPromise (promise) { | 
						|
  return !!promise && typeof promise.then === 'function' | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Simple try/catch helper, if exists, returns result | 
						|
 * | 
						|
 * @private | 
						|
 * | 
						|
 * @param   {Function} tryFn - try block | 
						|
 * @param   {Function} catchFn - catch block | 
						|
 * @returns {?*} | 
						|
 */ | 
						|
function tryCatch (tryFn, catchFn) { | 
						|
  try { | 
						|
    return tryFn() | 
						|
  } catch (err) { | 
						|
    catchFn(err) | 
						|
  } | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Extends the PostHTMLTree with the Tree API | 
						|
 * | 
						|
 * @private | 
						|
 * | 
						|
 * @param   {Array} tree - PostHTMLTree | 
						|
 * @returns {Array} tree - PostHTMLTree with API | 
						|
 */ | 
						|
function apiExtend (tree) { | 
						|
  tree.walk = api.walk | 
						|
  tree.match = api.match | 
						|
} | 
						|
 | 
						|
/** | 
						|
 * Wraps the PostHTMLTree within an object using a getter to render HTML on demand. | 
						|
 * | 
						|
 * @private | 
						|
 * | 
						|
 * @param   {Function} render | 
						|
 * @param   {Array}    tree | 
						|
 * @returns {Object<{html: String, tree: Array}>} | 
						|
 */ | 
						|
function lazyResult (render, tree) { | 
						|
  return { | 
						|
    get html () { | 
						|
      return render(tree, tree.options) | 
						|
    }, | 
						|
    tree: tree | 
						|
  } | 
						|
}
 | 
						|
 |