.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / snapdragon-util / index.js
1 'use strict';
2
3 var typeOf = require('kind-of');
4 var utils = module.exports;
5
6 /**
7  * Returns true if the given value is a node.
8  *
9  * ```js
10  * var Node = require('snapdragon-node');
11  * var node = new Node({type: 'foo'});
12  * console.log(utils.isNode(node)); //=> true
13  * console.log(utils.isNode({})); //=> false
14  * ```
15  * @param {Object} `node` Instance of [snapdragon-node][]
16  * @returns {Boolean}
17  * @api public
18  */
19
20 utils.isNode = function(node) {
21   return typeOf(node) === 'object' && node.isNode === true;
22 };
23
24 /**
25  * Emit an empty string for the given `node`.
26  *
27  * ```js
28  * // do nothing for beginning-of-string
29  * snapdragon.compiler.set('bos', utils.noop);
30  * ```
31  * @param {Object} `node` Instance of [snapdragon-node][]
32  * @returns {undefined}
33  * @api public
34  */
35
36 utils.noop = function(node) {
37   append(this, '', node);
38 };
39
40 /**
41  * Appdend `node.val` to `compiler.output`, exactly as it was created
42  * by the parser.
43  *
44  * ```js
45  * snapdragon.compiler.set('text', utils.identity);
46  * ```
47  * @param {Object} `node` Instance of [snapdragon-node][]
48  * @returns {undefined}
49  * @api public
50  */
51
52 utils.identity = function(node) {
53   append(this, node.val, node);
54 };
55
56 /**
57  * Previously named `.emit`, this method appends the given `val`
58  * to `compiler.output` for the given node. Useful when you know
59  * what value should be appended advance, regardless of the actual
60  * value of `node.val`.
61  *
62  * ```js
63  * snapdragon.compiler
64  *   .set('i', function(node) {
65  *     this.mapVisit(node);
66  *   })
67  *   .set('i.open', utils.append('<i>'))
68  *   .set('i.close', utils.append('</i>'))
69  * ```
70  * @param {Object} `node` Instance of [snapdragon-node][]
71  * @returns {Function} Returns a compiler middleware function.
72  * @api public
73  */
74
75 utils.append = function(val) {
76   return function(node) {
77     append(this, val, node);
78   };
79 };
80
81 /**
82  * Used in compiler middleware, this onverts an AST node into
83  * an empty `text` node and deletes `node.nodes` if it exists.
84  * The advantage of this method is that, as opposed to completely
85  * removing the node, indices will not need to be re-calculated
86  * in sibling nodes, and nothing is appended to the output.
87  *
88  * ```js
89  * utils.toNoop(node);
90  * // convert `node.nodes` to the given value instead of deleting it
91  * utils.toNoop(node, []);
92  * ```
93  * @param {Object} `node` Instance of [snapdragon-node][]
94  * @param {Array} `nodes` Optionally pass a new `nodes` value, to replace the existing `node.nodes` array.
95  * @api public
96  */
97
98 utils.toNoop = function(node, nodes) {
99   if (nodes) {
100     node.nodes = nodes;
101   } else {
102     delete node.nodes;
103     node.type = 'text';
104     node.val = '';
105   }
106 };
107
108 /**
109  * Visit `node` with the given `fn`. The built-in `.visit` method in snapdragon
110  * automatically calls registered compilers, this allows you to pass a visitor
111  * function.
112  *
113  * ```js
114  * snapdragon.compiler.set('i', function(node) {
115  *   utils.visit(node, function(childNode) {
116  *     // do stuff with "childNode"
117  *     return childNode;
118  *   });
119  * });
120  * ```
121  * @param {Object} `node` Instance of [snapdragon-node][]
122  * @param {Function} `fn`
123  * @return {Object} returns the node after recursively visiting all child nodes.
124  * @api public
125  */
126
127 utils.visit = function(node, fn) {
128   assert(utils.isNode(node), 'expected node to be an instance of Node');
129   assert(isFunction(fn), 'expected a visitor function');
130   fn(node);
131   return node.nodes ? utils.mapVisit(node, fn) : node;
132 };
133
134 /**
135  * Map [visit](#visit) the given `fn` over `node.nodes`. This is called by
136  * [visit](#visit), use this method if you do not want `fn` to be called on
137  * the first node.
138  *
139  * ```js
140  * snapdragon.compiler.set('i', function(node) {
141  *   utils.mapVisit(node, function(childNode) {
142  *     // do stuff with "childNode"
143  *     return childNode;
144  *   });
145  * });
146  * ```
147  * @param {Object} `node` Instance of [snapdragon-node][]
148  * @param {Object} `options`
149  * @param {Function} `fn`
150  * @return {Object} returns the node
151  * @api public
152  */
153
154 utils.mapVisit = function(node, fn) {
155   assert(utils.isNode(node), 'expected node to be an instance of Node');
156   assert(isArray(node.nodes), 'expected node.nodes to be an array');
157   assert(isFunction(fn), 'expected a visitor function');
158
159   for (var i = 0; i < node.nodes.length; i++) {
160     utils.visit(node.nodes[i], fn);
161   }
162   return node;
163 };
164
165 /**
166  * Unshift an `*.open` node onto `node.nodes`.
167  *
168  * ```js
169  * var Node = require('snapdragon-node');
170  * snapdragon.parser.set('brace', function(node) {
171  *   var match = this.match(/^{/);
172  *   if (match) {
173  *     var parent = new Node({type: 'brace'});
174  *     utils.addOpen(parent, Node);
175  *     console.log(parent.nodes[0]):
176  *     // { type: 'brace.open', val: '' };
177  *
178  *     // push the parent "brace" node onto the stack
179  *     this.push(parent);
180  *
181  *     // return the parent node, so it's also added to the AST
182  *     return brace;
183  *   }
184  * });
185  * ```
186  * @param {Object} `node` Instance of [snapdragon-node][]
187  * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][].
188  * @param {Function} `filter` Optionaly specify a filter function to exclude the node.
189  * @return {Object} Returns the created opening node.
190  * @api public
191  */
192
193 utils.addOpen = function(node, Node, val, filter) {
194   assert(utils.isNode(node), 'expected node to be an instance of Node');
195   assert(isFunction(Node), 'expected Node to be a constructor function');
196
197   if (typeof val === 'function') {
198     filter = val;
199     val = '';
200   }
201
202   if (typeof filter === 'function' && !filter(node)) return;
203   var open = new Node({ type: node.type + '.open', val: val});
204   var unshift = node.unshift || node.unshiftNode;
205   if (typeof unshift === 'function') {
206     unshift.call(node, open);
207   } else {
208     utils.unshiftNode(node, open);
209   }
210   return open;
211 };
212
213 /**
214  * Push a `*.close` node onto `node.nodes`.
215  *
216  * ```js
217  * var Node = require('snapdragon-node');
218  * snapdragon.parser.set('brace', function(node) {
219  *   var match = this.match(/^}/);
220  *   if (match) {
221  *     var parent = this.parent();
222  *     if (parent.type !== 'brace') {
223  *       throw new Error('missing opening: ' + '}');
224  *     }
225  *
226  *     utils.addClose(parent, Node);
227  *     console.log(parent.nodes[parent.nodes.length - 1]):
228  *     // { type: 'brace.close', val: '' };
229  *
230  *     // no need to return a node, since the parent
231  *     // was already added to the AST
232  *     return;
233  *   }
234  * });
235  * ```
236  * @param {Object} `node` Instance of [snapdragon-node][]
237  * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][].
238  * @param {Function} `filter` Optionaly specify a filter function to exclude the node.
239  * @return {Object} Returns the created closing node.
240  * @api public
241  */
242
243 utils.addClose = function(node, Node, val, filter) {
244   assert(utils.isNode(node), 'expected node to be an instance of Node');
245   assert(isFunction(Node), 'expected Node to be a constructor function');
246
247   if (typeof val === 'function') {
248     filter = val;
249     val = '';
250   }
251
252   if (typeof filter === 'function' && !filter(node)) return;
253   var close = new Node({ type: node.type + '.close', val: val});
254   var push = node.push || node.pushNode;
255   if (typeof push === 'function') {
256     push.call(node, close);
257   } else {
258     utils.pushNode(node, close);
259   }
260   return close;
261 };
262
263 /**
264  * Wraps the given `node` with `*.open` and `*.close` nodes.
265  *
266  * @param {Object} `node` Instance of [snapdragon-node][]
267  * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][].
268  * @param {Function} `filter` Optionaly specify a filter function to exclude the node.
269  * @return {Object} Returns the node
270  * @api public
271  */
272
273 utils.wrapNodes = function(node, Node, filter) {
274   assert(utils.isNode(node), 'expected node to be an instance of Node');
275   assert(isFunction(Node), 'expected Node to be a constructor function');
276
277   utils.addOpen(node, Node, filter);
278   utils.addClose(node, Node, filter);
279   return node;
280 };
281
282 /**
283  * Push the given `node` onto `parent.nodes`, and set `parent` as `node.parent.
284  *
285  * ```js
286  * var parent = new Node({type: 'foo'});
287  * var node = new Node({type: 'bar'});
288  * utils.pushNode(parent, node);
289  * console.log(parent.nodes[0].type) // 'bar'
290  * console.log(node.parent.type) // 'foo'
291  * ```
292  * @param {Object} `parent`
293  * @param {Object} `node` Instance of [snapdragon-node][]
294  * @return {Object} Returns the child node
295  * @api public
296  */
297
298 utils.pushNode = function(parent, node) {
299   assert(utils.isNode(parent), 'expected parent node to be an instance of Node');
300   assert(utils.isNode(node), 'expected node to be an instance of Node');
301
302   node.define('parent', parent);
303   parent.nodes = parent.nodes || [];
304   parent.nodes.push(node);
305   return node;
306 };
307
308 /**
309  * Unshift `node` onto `parent.nodes`, and set `parent` as `node.parent.
310  *
311  * ```js
312  * var parent = new Node({type: 'foo'});
313  * var node = new Node({type: 'bar'});
314  * utils.unshiftNode(parent, node);
315  * console.log(parent.nodes[0].type) // 'bar'
316  * console.log(node.parent.type) // 'foo'
317  * ```
318  * @param {Object} `parent`
319  * @param {Object} `node` Instance of [snapdragon-node][]
320  * @return {undefined}
321  * @api public
322  */
323
324 utils.unshiftNode = function(parent, node) {
325   assert(utils.isNode(parent), 'expected parent node to be an instance of Node');
326   assert(utils.isNode(node), 'expected node to be an instance of Node');
327
328   node.define('parent', parent);
329   parent.nodes = parent.nodes || [];
330   parent.nodes.unshift(node);
331 };
332
333 /**
334  * Pop the last `node` off of `parent.nodes`. The advantage of
335  * using this method is that it checks for `node.nodes` and works
336  * with any version of `snapdragon-node`.
337  *
338  * ```js
339  * var parent = new Node({type: 'foo'});
340  * utils.pushNode(parent, new Node({type: 'foo'}));
341  * utils.pushNode(parent, new Node({type: 'bar'}));
342  * utils.pushNode(parent, new Node({type: 'baz'}));
343  * console.log(parent.nodes.length); //=> 3
344  * utils.popNode(parent);
345  * console.log(parent.nodes.length); //=> 2
346  * ```
347  * @param {Object} `parent`
348  * @param {Object} `node` Instance of [snapdragon-node][]
349  * @return {Number|Undefined} Returns the length of `node.nodes` or undefined.
350  * @api public
351  */
352
353 utils.popNode = function(node) {
354   assert(utils.isNode(node), 'expected node to be an instance of Node');
355   if (typeof node.pop === 'function') {
356     return node.pop();
357   }
358   return node.nodes && node.nodes.pop();
359 };
360
361 /**
362  * Shift the first `node` off of `parent.nodes`. The advantage of
363  * using this method is that it checks for `node.nodes` and works
364  * with any version of `snapdragon-node`.
365  *
366  * ```js
367  * var parent = new Node({type: 'foo'});
368  * utils.pushNode(parent, new Node({type: 'foo'}));
369  * utils.pushNode(parent, new Node({type: 'bar'}));
370  * utils.pushNode(parent, new Node({type: 'baz'}));
371  * console.log(parent.nodes.length); //=> 3
372  * utils.shiftNode(parent);
373  * console.log(parent.nodes.length); //=> 2
374  * ```
375  * @param {Object} `parent`
376  * @param {Object} `node` Instance of [snapdragon-node][]
377  * @return {Number|Undefined} Returns the length of `node.nodes` or undefined.
378  * @api public
379  */
380
381 utils.shiftNode = function(node) {
382   assert(utils.isNode(node), 'expected node to be an instance of Node');
383   if (typeof node.shift === 'function') {
384     return node.shift();
385   }
386   return node.nodes && node.nodes.shift();
387 };
388
389 /**
390  * Remove the specified `node` from `parent.nodes`.
391  *
392  * ```js
393  * var parent = new Node({type: 'abc'});
394  * var foo = new Node({type: 'foo'});
395  * utils.pushNode(parent, foo);
396  * utils.pushNode(parent, new Node({type: 'bar'}));
397  * utils.pushNode(parent, new Node({type: 'baz'}));
398  * console.log(parent.nodes.length); //=> 3
399  * utils.removeNode(parent, foo);
400  * console.log(parent.nodes.length); //=> 2
401  * ```
402  * @param {Object} `parent`
403  * @param {Object} `node` Instance of [snapdragon-node][]
404  * @return {Object|undefined} Returns the removed node, if successful, or undefined if it does not exist on `parent.nodes`.
405  * @api public
406  */
407
408 utils.removeNode = function(parent, node) {
409   assert(utils.isNode(parent), 'expected parent.node to be an instance of Node');
410   assert(utils.isNode(node), 'expected node to be an instance of Node');
411
412   if (!parent.nodes) {
413     return null;
414   }
415
416   if (typeof parent.remove === 'function') {
417     return parent.remove(node);
418   }
419
420   var idx = parent.nodes.indexOf(node);
421   if (idx !== -1) {
422     return parent.nodes.splice(idx, 1);
423   }
424 };
425
426 /**
427  * Returns true if `node.type` matches the given `type`. Throws a
428  * `TypeError` if `node` is not an instance of `Node`.
429  *
430  * ```js
431  * var Node = require('snapdragon-node');
432  * var node = new Node({type: 'foo'});
433  * console.log(utils.isType(node, 'foo')); // false
434  * console.log(utils.isType(node, 'bar')); // true
435  * ```
436  * @param {Object} `node` Instance of [snapdragon-node][]
437  * @param {String} `type`
438  * @return {Boolean}
439  * @api public
440  */
441
442 utils.isType = function(node, type) {
443   assert(utils.isNode(node), 'expected node to be an instance of Node');
444   switch (typeOf(type)) {
445     case 'array':
446       var types = type.slice();
447       for (var i = 0; i < types.length; i++) {
448         if (utils.isType(node, types[i])) {
449           return true;
450         }
451       }
452       return false;
453     case 'string':
454       return node.type === type;
455     case 'regexp':
456       return type.test(node.type);
457     default: {
458       throw new TypeError('expected "type" to be an array, string or regexp');
459     }
460   }
461 };
462
463 /**
464  * Returns true if the given `node` has the given `type` in `node.nodes`.
465  * Throws a `TypeError` if `node` is not an instance of `Node`.
466  *
467  * ```js
468  * var Node = require('snapdragon-node');
469  * var node = new Node({
470  *   type: 'foo',
471  *   nodes: [
472  *     new Node({type: 'bar'}),
473  *     new Node({type: 'baz'})
474  *   ]
475  * });
476  * console.log(utils.hasType(node, 'xyz')); // false
477  * console.log(utils.hasType(node, 'baz')); // true
478  * ```
479  * @param {Object} `node` Instance of [snapdragon-node][]
480  * @param {String} `type`
481  * @return {Boolean}
482  * @api public
483  */
484
485 utils.hasType = function(node, type) {
486   assert(utils.isNode(node), 'expected node to be an instance of Node');
487   if (!Array.isArray(node.nodes)) return false;
488   for (var i = 0; i < node.nodes.length; i++) {
489     if (utils.isType(node.nodes[i], type)) {
490       return true;
491     }
492   }
493   return false;
494 };
495
496 /**
497  * Returns the first node from `node.nodes` of the given `type`
498  *
499  * ```js
500  * var node = new Node({
501  *   type: 'foo',
502  *   nodes: [
503  *     new Node({type: 'text', val: 'abc'}),
504  *     new Node({type: 'text', val: 'xyz'})
505  *   ]
506  * });
507  *
508  * var textNode = utils.firstOfType(node.nodes, 'text');
509  * console.log(textNode.val);
510  * //=> 'abc'
511  * ```
512  * @param {Array} `nodes`
513  * @param {String} `type`
514  * @return {Object|undefined} Returns the first matching node or undefined.
515  * @api public
516  */
517
518 utils.firstOfType = function(nodes, type) {
519   for (var i = 0; i < nodes.length; i++) {
520     var node = nodes[i];
521     if (utils.isType(node, type)) {
522       return node;
523     }
524   }
525 };
526
527 /**
528  * Returns the node at the specified index, or the first node of the
529  * given `type` from `node.nodes`.
530  *
531  * ```js
532  * var node = new Node({
533  *   type: 'foo',
534  *   nodes: [
535  *     new Node({type: 'text', val: 'abc'}),
536  *     new Node({type: 'text', val: 'xyz'})
537  *   ]
538  * });
539  *
540  * var nodeOne = utils.findNode(node.nodes, 'text');
541  * console.log(nodeOne.val);
542  * //=> 'abc'
543  *
544  * var nodeTwo = utils.findNode(node.nodes, 1);
545  * console.log(nodeTwo.val);
546  * //=> 'xyz'
547  * ```
548  *
549  * @param {Array} `nodes`
550  * @param {String|Number} `type` Node type or index.
551  * @return {Object} Returns a node or undefined.
552  * @api public
553  */
554
555 utils.findNode = function(nodes, type) {
556   if (!Array.isArray(nodes)) {
557     return null;
558   }
559   if (typeof type === 'number') {
560     return nodes[type];
561   }
562   return utils.firstOfType(nodes, type);
563 };
564
565 /**
566  * Returns true if the given node is an "*.open" node.
567  *
568  * ```js
569  * var Node = require('snapdragon-node');
570  * var brace = new Node({type: 'brace'});
571  * var open = new Node({type: 'brace.open'});
572  * var close = new Node({type: 'brace.close'});
573  *
574  * console.log(utils.isOpen(brace)); // false
575  * console.log(utils.isOpen(open)); // true
576  * console.log(utils.isOpen(close)); // false
577  * ```
578  * @param {Object} `node` Instance of [snapdragon-node][]
579  * @return {Boolean}
580  * @api public
581  */
582
583 utils.isOpen = function(node) {
584   assert(utils.isNode(node), 'expected node to be an instance of Node');
585   return node.type.slice(-5) === '.open';
586 };
587
588 /**
589  * Returns true if the given node is a "*.close" node.
590  *
591  * ```js
592  * var Node = require('snapdragon-node');
593  * var brace = new Node({type: 'brace'});
594  * var open = new Node({type: 'brace.open'});
595  * var close = new Node({type: 'brace.close'});
596  *
597  * console.log(utils.isClose(brace)); // false
598  * console.log(utils.isClose(open)); // false
599  * console.log(utils.isClose(close)); // true
600  * ```
601  * @param {Object} `node` Instance of [snapdragon-node][]
602  * @return {Boolean}
603  * @api public
604  */
605
606 utils.isClose = function(node) {
607   assert(utils.isNode(node), 'expected node to be an instance of Node');
608   return node.type.slice(-6) === '.close';
609 };
610
611 /**
612  * Returns true if `node.nodes` **has** an `.open` node
613  *
614  * ```js
615  * var Node = require('snapdragon-node');
616  * var brace = new Node({
617  *   type: 'brace',
618  *   nodes: []
619  * });
620  *
621  * var open = new Node({type: 'brace.open'});
622  * console.log(utils.hasOpen(brace)); // false
623  *
624  * brace.pushNode(open);
625  * console.log(utils.hasOpen(brace)); // true
626  * ```
627  * @param {Object} `node` Instance of [snapdragon-node][]
628  * @return {Boolean}
629  * @api public
630  */
631
632 utils.hasOpen = function(node) {
633   assert(utils.isNode(node), 'expected node to be an instance of Node');
634   var first = node.first || node.nodes ? node.nodes[0] : null;
635   if (utils.isNode(first)) {
636     return first.type === node.type + '.open';
637   }
638   return false;
639 };
640
641 /**
642  * Returns true if `node.nodes` **has** a `.close` node
643  *
644  * ```js
645  * var Node = require('snapdragon-node');
646  * var brace = new Node({
647  *   type: 'brace',
648  *   nodes: []
649  * });
650  *
651  * var close = new Node({type: 'brace.close'});
652  * console.log(utils.hasClose(brace)); // false
653  *
654  * brace.pushNode(close);
655  * console.log(utils.hasClose(brace)); // true
656  * ```
657  * @param {Object} `node` Instance of [snapdragon-node][]
658  * @return {Boolean}
659  * @api public
660  */
661
662 utils.hasClose = function(node) {
663   assert(utils.isNode(node), 'expected node to be an instance of Node');
664   var last = node.last || node.nodes ? node.nodes[node.nodes.length - 1] : null;
665   if (utils.isNode(last)) {
666     return last.type === node.type + '.close';
667   }
668   return false;
669 };
670
671 /**
672  * Returns true if `node.nodes` has both `.open` and `.close` nodes
673  *
674  * ```js
675  * var Node = require('snapdragon-node');
676  * var brace = new Node({
677  *   type: 'brace',
678  *   nodes: []
679  * });
680  *
681  * var open = new Node({type: 'brace.open'});
682  * var close = new Node({type: 'brace.close'});
683  * console.log(utils.hasOpen(brace)); // false
684  * console.log(utils.hasClose(brace)); // false
685  *
686  * brace.pushNode(open);
687  * brace.pushNode(close);
688  * console.log(utils.hasOpen(brace)); // true
689  * console.log(utils.hasClose(brace)); // true
690  * ```
691  * @param {Object} `node` Instance of [snapdragon-node][]
692  * @return {Boolean}
693  * @api public
694  */
695
696 utils.hasOpenAndClose = function(node) {
697   return utils.hasOpen(node) && utils.hasClose(node);
698 };
699
700 /**
701  * Push the given `node` onto the `state.inside` array for the
702  * given type. This array is used as a specialized "stack" for
703  * only the given `node.type`.
704  *
705  * ```js
706  * var state = { inside: {}};
707  * var node = new Node({type: 'brace'});
708  * utils.addType(state, node);
709  * console.log(state.inside);
710  * //=> { brace: [{type: 'brace'}] }
711  * ```
712  * @param {Object} `state` The `compiler.state` object or custom state object.
713  * @param {Object} `node` Instance of [snapdragon-node][]
714  * @return {Array} Returns the `state.inside` stack for the given type.
715  * @api public
716  */
717
718 utils.addType = function(state, node) {
719   assert(utils.isNode(node), 'expected node to be an instance of Node');
720   assert(isObject(state), 'expected state to be an object');
721
722   var type = node.parent
723     ? node.parent.type
724     : node.type.replace(/\.open$/, '');
725
726   if (!state.hasOwnProperty('inside')) {
727     state.inside = {};
728   }
729   if (!state.inside.hasOwnProperty(type)) {
730     state.inside[type] = [];
731   }
732
733   var arr = state.inside[type];
734   arr.push(node);
735   return arr;
736 };
737
738 /**
739  * Remove the given `node` from the `state.inside` array for the
740  * given type. This array is used as a specialized "stack" for
741  * only the given `node.type`.
742  *
743  * ```js
744  * var state = { inside: {}};
745  * var node = new Node({type: 'brace'});
746  * utils.addType(state, node);
747  * console.log(state.inside);
748  * //=> { brace: [{type: 'brace'}] }
749  * utils.removeType(state, node);
750  * //=> { brace: [] }
751  * ```
752  * @param {Object} `state` The `compiler.state` object or custom state object.
753  * @param {Object} `node` Instance of [snapdragon-node][]
754  * @return {Array} Returns the `state.inside` stack for the given type.
755  * @api public
756  */
757
758 utils.removeType = function(state, node) {
759   assert(utils.isNode(node), 'expected node to be an instance of Node');
760   assert(isObject(state), 'expected state to be an object');
761
762   var type = node.parent
763     ? node.parent.type
764     : node.type.replace(/\.close$/, '');
765
766   if (state.inside.hasOwnProperty(type)) {
767     return state.inside[type].pop();
768   }
769 };
770
771 /**
772  * Returns true if `node.val` is an empty string, or `node.nodes` does
773  * not contain any non-empty text nodes.
774  *
775  * ```js
776  * var node = new Node({type: 'text'});
777  * utils.isEmpty(node); //=> true
778  * node.val = 'foo';
779  * utils.isEmpty(node); //=> false
780  * ```
781  * @param {Object} `node` Instance of [snapdragon-node][]
782  * @param {Function} `fn`
783  * @return {Boolean}
784  * @api public
785  */
786
787 utils.isEmpty = function(node, fn) {
788   assert(utils.isNode(node), 'expected node to be an instance of Node');
789
790   if (!Array.isArray(node.nodes)) {
791     if (node.type !== 'text') {
792       return true;
793     }
794     if (typeof fn === 'function') {
795       return fn(node, node.parent);
796     }
797     return !utils.trim(node.val);
798   }
799
800   for (var i = 0; i < node.nodes.length; i++) {
801     var child = node.nodes[i];
802     if (utils.isOpen(child) || utils.isClose(child)) {
803       continue;
804     }
805     if (!utils.isEmpty(child, fn)) {
806       return false;
807     }
808   }
809
810   return true;
811 };
812
813 /**
814  * Returns true if the `state.inside` stack for the given type exists
815  * and has one or more nodes on it.
816  *
817  * ```js
818  * var state = { inside: {}};
819  * var node = new Node({type: 'brace'});
820  * console.log(utils.isInsideType(state, 'brace')); //=> false
821  * utils.addType(state, node);
822  * console.log(utils.isInsideType(state, 'brace')); //=> true
823  * utils.removeType(state, node);
824  * console.log(utils.isInsideType(state, 'brace')); //=> false
825  * ```
826  * @param {Object} `state`
827  * @param {String} `type`
828  * @return {Boolean}
829  * @api public
830  */
831
832 utils.isInsideType = function(state, type) {
833   assert(isObject(state), 'expected state to be an object');
834   assert(isString(type), 'expected type to be a string');
835
836   if (!state.hasOwnProperty('inside')) {
837     return false;
838   }
839
840   if (!state.inside.hasOwnProperty(type)) {
841     return false;
842   }
843
844   return state.inside[type].length > 0;
845 };
846
847 /**
848  * Returns true if `node` is either a child or grand-child of the given `type`,
849  * or `state.inside[type]` is a non-empty array.
850  *
851  * ```js
852  * var state = { inside: {}};
853  * var node = new Node({type: 'brace'});
854  * var open = new Node({type: 'brace.open'});
855  * console.log(utils.isInside(state, open, 'brace')); //=> false
856  * utils.pushNode(node, open);
857  * console.log(utils.isInside(state, open, 'brace')); //=> true
858  * ```
859  * @param {Object} `state` Either the `compiler.state` object, if it exists, or a user-supplied state object.
860  * @param {Object} `node` Instance of [snapdragon-node][]
861  * @param {String} `type` The `node.type` to check for.
862  * @return {Boolean}
863  * @api public
864  */
865
866 utils.isInside = function(state, node, type) {
867   assert(utils.isNode(node), 'expected node to be an instance of Node');
868   assert(isObject(state), 'expected state to be an object');
869
870   if (Array.isArray(type)) {
871     for (var i = 0; i < type.length; i++) {
872       if (utils.isInside(state, node, type[i])) {
873         return true;
874       }
875     }
876     return false;
877   }
878
879   var parent = node.parent;
880   if (typeof type === 'string') {
881     return (parent && parent.type === type) || utils.isInsideType(state, type);
882   }
883
884   if (typeOf(type) === 'regexp') {
885     if (parent && parent.type && type.test(parent.type)) {
886       return true;
887     }
888
889     var keys = Object.keys(state.inside);
890     var len = keys.length;
891     var idx = -1;
892     while (++idx < len) {
893       var key = keys[idx];
894       var val = state.inside[key];
895
896       if (Array.isArray(val) && val.length !== 0 && type.test(key)) {
897         return true;
898       }
899     }
900   }
901   return false;
902 };
903
904 /**
905  * Get the last `n` element from the given `array`. Used for getting
906  * a node from `node.nodes.`
907  *
908  * @param {Array} `array`
909  * @param {Number} `n`
910  * @return {undefined}
911  * @api public
912  */
913
914 utils.last = function(arr, n) {
915   return arr[arr.length - (n || 1)];
916 };
917
918 /**
919  * Cast the given `val` to an array.
920  *
921  * ```js
922  * console.log(utils.arrayify(''));
923  * //=> []
924  * console.log(utils.arrayify('foo'));
925  * //=> ['foo']
926  * console.log(utils.arrayify(['foo']));
927  * //=> ['foo']
928  * ```
929  * @param {any} `val`
930  * @return {Array}
931  * @api public
932  */
933
934 utils.arrayify = function(val) {
935   if (typeof val === 'string' && val !== '') {
936     return [val];
937   }
938   if (!Array.isArray(val)) {
939     return [];
940   }
941   return val;
942 };
943
944 /**
945  * Convert the given `val` to a string by joining with `,`. Useful
946  * for creating a cheerio/CSS/DOM-style selector from a list of strings.
947  *
948  * @param {any} `val`
949  * @return {Array}
950  * @api public
951  */
952
953 utils.stringify = function(val) {
954   return utils.arrayify(val).join(',');
955 };
956
957 /**
958  * Ensure that the given value is a string and call `.trim()` on it,
959  * or return an empty string.
960  *
961  * @param {String} `str`
962  * @return {String}
963  * @api public
964  */
965
966 utils.trim = function(str) {
967   return typeof str === 'string' ? str.trim() : '';
968 };
969
970 /**
971  * Return true if val is an object
972  */
973
974 function isObject(val) {
975   return typeOf(val) === 'object';
976 }
977
978 /**
979  * Return true if val is a string
980  */
981
982 function isString(val) {
983   return typeof val === 'string';
984 }
985
986 /**
987  * Return true if val is a function
988  */
989
990 function isFunction(val) {
991   return typeof val === 'function';
992 }
993
994 /**
995  * Return true if val is an array
996  */
997
998 function isArray(val) {
999   return Array.isArray(val);
1000 }
1001
1002 /**
1003  * Shim to ensure the `.append` methods work with any version of snapdragon
1004  */
1005
1006 function append(compiler, val, node) {
1007   if (typeof compiler.append !== 'function') {
1008     return compiler.emit(val, node);
1009   }
1010   return compiler.append(val, node);
1011 }
1012
1013 /**
1014  * Simplified assertion. Throws an error is `val` is falsey.
1015  */
1016
1017 function assert(val, message) {
1018   if (!val) throw new Error(message);
1019 }