braces.js
  1  'use strict';
  2  
  3  var extend = require('extend-shallow');
  4  var Snapdragon = require('snapdragon');
  5  var compilers = require('./compilers');
  6  var parsers = require('./parsers');
  7  var utils = require('./utils');
  8  
  9  /**
 10   * Customize Snapdragon parser and renderer
 11   */
 12  
 13  function Braces(options) {
 14    this.options = extend({}, options);
 15  }
 16  
 17  /**
 18   * Initialize braces
 19   */
 20  
 21  Braces.prototype.init = function(options) {
 22    if (this.isInitialized) return;
 23    this.isInitialized = true;
 24    var opts = utils.createOptions({}, this.options, options);
 25    this.snapdragon = this.options.snapdragon || new Snapdragon(opts);
 26    this.compiler = this.snapdragon.compiler;
 27    this.parser = this.snapdragon.parser;
 28  
 29    compilers(this.snapdragon, opts);
 30    parsers(this.snapdragon, opts);
 31  
 32    /**
 33     * Call Snapdragon `.parse` method. When AST is returned, we check to
 34     * see if any unclosed braces are left on the stack and, if so, we iterate
 35     * over the stack and correct the AST so that compilers are called in the correct
 36     * order and unbalance braces are properly escaped.
 37     */
 38  
 39    utils.define(this.snapdragon, 'parse', function(pattern, options) {
 40      var parsed = Snapdragon.prototype.parse.apply(this, arguments);
 41      this.parser.ast.input = pattern;
 42  
 43      var stack = this.parser.stack;
 44      while (stack.length) {
 45        addParent({type: 'brace.close', val: ''}, stack.pop());
 46      }
 47  
 48      function addParent(node, parent) {
 49        utils.define(node, 'parent', parent);
 50        parent.nodes.push(node);
 51      }
 52  
 53      // add non-enumerable parser reference
 54      utils.define(parsed, 'parser', this.parser);
 55      return parsed;
 56    });
 57  };
 58  
 59  /**
 60   * Decorate `.parse` method
 61   */
 62  
 63  Braces.prototype.parse = function(ast, options) {
 64    if (ast && typeof ast === 'object' && ast.nodes) return ast;
 65    this.init(options);
 66    return this.snapdragon.parse(ast, options);
 67  };
 68  
 69  /**
 70   * Decorate `.compile` method
 71   */
 72  
 73  Braces.prototype.compile = function(ast, options) {
 74    if (typeof ast === 'string') {
 75      ast = this.parse(ast, options);
 76    } else {
 77      this.init(options);
 78    }
 79    return this.snapdragon.compile(ast, options);
 80  };
 81  
 82  /**
 83   * Expand
 84   */
 85  
 86  Braces.prototype.expand = function(pattern) {
 87    var ast = this.parse(pattern, {expand: true});
 88    return this.compile(ast, {expand: true});
 89  };
 90  
 91  /**
 92   * Optimize
 93   */
 94  
 95  Braces.prototype.optimize = function(pattern) {
 96    var ast = this.parse(pattern, {optimize: true});
 97    return this.compile(ast, {optimize: true});
 98  };
 99  
100  /**
101   * Expose `Braces`
102   */
103  
104  module.exports = Braces;