braces.js 2.4 KB

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