UNPKG

2.94 kBJavaScriptView Raw
1'use strict';
2
3var debug = require('debug')('braces');
4var Snapdragon = require('snapdragon');
5var compilers = require('./compilers');
6var parsers = require('./parsers');
7var utils = require('./utils');
8
9/**
10 * Customize Snapdragon parser and renderer
11 */
12
13function Braces(options) {
14 debug('initializing from <%s>', __filename);
15 this.options = utils.extend({}, options);
16}
17
18/**
19 * Initialize braces
20 */
21
22Braces.prototype.init = function(options) {
23 var opts = utils.createOptions({}, this.options, options);
24 this.snapdragon = this.options.snapdragon || new Snapdragon(opts);
25 this.compiler = this.snapdragon.compiler;
26 this.parser = this.snapdragon.parser;
27
28 compilers(this.snapdragon);
29 parsers(this.snapdragon);
30
31 /**
32 * Call Snapdragon `.parse` method. When AST is returned, we check to
33 * see if any unclosed braces are left on the stack and, if so, we iterate
34 * over the stack and correct the AST so that compilers are called in the correct
35 * order and unbalance braces are properly escaped.
36 */
37
38 utils.define(this.snapdragon, 'parse', function(pattern, options) {
39 var parsed = Snapdragon.prototype.parse.apply(this, arguments);
40 this.parser.ast.input = pattern;
41
42 var stack = this.parser.stack;
43 while (stack.length) {
44 addParent({type: 'brace.close', val: ''}, stack.pop());
45 }
46
47 function addParent(node, parent) {
48 utils.define(node, 'parent', parent);
49 parent.nodes.push(node);
50 }
51
52 // add non-enumerable parser reference
53 utils.define(parsed, 'parser', this.parser);
54 return parsed;
55 });
56};
57
58/**
59 * Lazily initialize braces
60 */
61
62Braces.prototype.lazyInit = function(options) {
63 if (!this.isInitialized) {
64 this.isInitialized = true;
65 this.init(options);
66 }
67};
68
69/**
70 * Decorate `.parse` method
71 */
72
73Braces.prototype.parse = function(ast, options) {
74 if (utils.isObject(ast) && ast.nodes) return ast;
75 this.lazyInit(options);
76 return this.snapdragon.parse(ast, options);
77};
78
79/**
80 * Decorate `.compile` method
81 */
82
83Braces.prototype.compile = function(ast, options) {
84 if (typeof ast === 'string') ast = this.parse(ast, options);
85 this.lazyInit(options);
86 return this.snapdragon.compile(ast, options);
87};
88
89/**
90 * Expand
91 */
92
93Braces.prototype.expand = function(pattern) {
94 var ast = this.parse(pattern, {expand: true});
95 var res = this.compile(ast, {expand: true});
96 return res.output;
97};
98
99/**
100 * Optimize
101 */
102
103Braces.prototype.optimize = function(pattern) {
104 var ast = this.parse(pattern, {optimize: true});
105 var res = this.compile(ast, {optimize: true});
106 return res.output;
107};
108
109/**
110 * Visit `node` with the given `fn`
111 */
112
113function visit(node, fn) {
114 return node.nodes ? mapVisit(node.nodes, fn) : fn(node);
115}
116
117/**
118 * Map visit over array of `nodes`.
119 */
120
121function mapVisit(nodes, fn) {
122 var len = nodes.length;
123 var idx = -1;
124 while (++idx < len) {
125 visit(nodes[idx], fn);
126 }
127}
128
129/**
130 * Expose `Braces`
131 */
132
133module.exports = Braces;