braces.js 2.4 KB

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