pax_global_header00006660000000000000000000000064131762657000014521gustar00rootroot0000000000000052 comment=51ebbd9ea96455a075d304ad7fbb9701ba9955a0 snapdragon-util-4.0.0/000077500000000000000000000000001317626570000146315ustar00rootroot00000000000000snapdragon-util-4.0.0/.editorconfig000066400000000000000000000004411317626570000173050ustar00rootroot00000000000000# http://editorconfig.org/ root = true [*] charset = utf-8 end_of_line = lf indent_size = 2 indent_style = space insert_final_newline = true trim_trailing_whitespace = true [{**/{actual,fixtures,expected,templates}/**,*.md}] trim_trailing_whitespace = false insert_final_newline = false snapdragon-util-4.0.0/.eslintrc.json000066400000000000000000000067751317626570000174440ustar00rootroot00000000000000{ "env": { "browser": false, "es6": true, "node": true, "mocha": true }, "globals": { "document": false, "navigator": false, "window": false }, "rules": { "accessor-pairs": 2, "arrow-spacing": [2, { "before": true, "after": true }], "block-spacing": [2, "always"], "brace-style": [2, "1tbs", { "allowSingleLine": true }], "comma-dangle": [2, "never"], "comma-spacing": [2, { "before": false, "after": true }], "comma-style": [2, "last"], "constructor-super": 2, "curly": [2, "multi-line"], "dot-location": [2, "property"], "eol-last": 2, "eqeqeq": [2, "allow-null"], "generator-star-spacing": [2, { "before": true, "after": true }], "handle-callback-err": [2, "^(err|error)$" ], "indent": [2, 2, { "SwitchCase": 1 }], "key-spacing": [2, { "beforeColon": false, "afterColon": true }], "keyword-spacing": [2, { "before": true, "after": true }], "new-cap": [2, { "newIsCap": true, "capIsNew": false }], "new-parens": 2, "no-array-constructor": 2, "no-caller": 2, "no-class-assign": 2, "no-cond-assign": 2, "no-const-assign": 2, "no-control-regex": 2, "no-debugger": 2, "no-delete-var": 2, "no-dupe-args": 2, "no-dupe-class-members": 2, "no-dupe-keys": 2, "no-duplicate-case": 2, "no-empty-character-class": 2, "no-eval": 2, "no-ex-assign": 2, "no-extend-native": 2, "no-extra-bind": 2, "no-extra-boolean-cast": 2, "no-extra-parens": [2, "functions"], "no-fallthrough": 2, "no-floating-decimal": 2, "no-func-assign": 2, "no-implied-eval": 2, "no-inner-declarations": [2, "functions"], "no-invalid-regexp": 2, "no-irregular-whitespace": 2, "no-iterator": 2, "no-label-var": 2, "no-labels": 2, "no-lone-blocks": 2, "no-mixed-spaces-and-tabs": 2, "no-multi-spaces": 2, "no-multi-str": 2, "no-multiple-empty-lines": [2, { "max": 1 }], "no-native-reassign": 0, "no-negated-in-lhs": 2, "no-new": 2, "no-new-func": 2, "no-new-object": 2, "no-new-require": 2, "no-new-wrappers": 2, "no-obj-calls": 2, "no-octal": 2, "no-octal-escape": 2, "no-proto": 0, "no-redeclare": 2, "no-regex-spaces": 2, "no-return-assign": 2, "no-self-compare": 2, "no-sequences": 2, "no-shadow-restricted-names": 2, "no-spaced-func": 2, "no-sparse-arrays": 2, "no-this-before-super": 2, "no-throw-literal": 2, "no-trailing-spaces": 0, "no-undef": 2, "no-undef-init": 2, "no-unexpected-multiline": 2, "no-unneeded-ternary": [2, { "defaultAssignment": false }], "no-unreachable": 2, "no-unused-vars": [2, { "vars": "all", "args": "none" }], "no-useless-call": 0, "no-with": 2, "one-var": [0, { "initialized": "never" }], "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], "padded-blocks": [0, "never"], "quotes": [2, "single", "avoid-escape"], "radix": 2, "semi": [2, "always"], "semi-spacing": [2, { "before": false, "after": true }], "space-before-blocks": [2, "always"], "space-before-function-paren": [2, "never"], "space-in-parens": [2, "never"], "space-infix-ops": 2, "space-unary-ops": [2, { "words": true, "nonwords": false }], "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], "use-isnan": 2, "valid-typeof": 2, "wrap-iife": [2, "any"], "yoda": [2, "never"] } } snapdragon-util-4.0.0/.gitattributes000066400000000000000000000002001317626570000175140ustar00rootroot00000000000000# Enforce Unix newlines * text eol=lf # binaries *.ai binary *.psd binary *.jpg binary *.gif binary *.png binary *.jpeg binary snapdragon-util-4.0.0/.github/000077500000000000000000000000001317626570000161715ustar00rootroot00000000000000snapdragon-util-4.0.0/.github/contributing.md000066400000000000000000000063411317626570000212260ustar00rootroot00000000000000# Contributing to snapdragon-util First and foremost, thank you! We appreciate that you want to contribute to snapdragon-util, your time is valuable, and your contributions mean a lot to us. **What does "contributing" mean?** Creating an issue is the simplest form of contributing to a project. But there are many ways to contribute, including the following: - Updating or correcting documentation - Feature requests - Bug reports If you'd like to learn more about contributing in general, the [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing) has a lot of useful information. **Showing support for snapdragon-util** Please keep in mind that open source software is built by people like you, who spend their free time creating things the rest the community can use. Don't have time to contribute? No worries, here are some other ways to show your support for snapdragon-util: - star the [project](https://github.com/jonschlinkert/snapdragon-util) - tweet your support for snapdragon-util ## Issues ### Before creating an issue Please try to determine if the issue is caused by an underlying library, and if so, create the issue there. Sometimes this is difficult to know. We only ask that you attempt to give a reasonable attempt to find out. Oftentimes the readme will have advice about where to go to create issues. Try to follow these guidelines - **Investigate the issue**: - **Check the readme** - oftentimes you will find notes about creating issues, and where to go depending on the type of issue. - Create the issue in the appropriate repository. ### Creating an issue Please be as descriptive as possible when creating an issue. Give us the information we need to successfully answer your question or address your issue by answering the following in your issue: - **version**: please note the version of snapdragon-util are you using - **extensions, plugins, helpers, etc** (if applicable): please list any extensions you're using - **error messages**: please paste any error messages into the issue, or a [gist](https://gist.github.com/) ## Above and beyond Here are some tips for creating idiomatic issues. Taking just a little bit extra time will make your issue easier to read, easier to resolve, more likely to be found by others who have the same or similar issue in the future. - read the [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing) - take some time to learn basic markdown. This [markdown cheatsheet](https://gist.github.com/jonschlinkert/5854601) is super helpful, as is the GitHub guide to [basic markdown](https://help.github.com/articles/markdown-basics/). - Learn about [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/). And if you want to really go above and beyond, read [mastering markdown](https://guides.github.com/features/mastering-markdown/). - use backticks to wrap code. This ensures that code will retain its format, making it much more readable to others - use syntax highlighting by adding the correct language name after the first "code fence" [node-glob]: https://github.com/isaacs/node-glob [micromatch]: https://github.com/jonschlinkert/micromatch [so]: http://stackoverflow.com/questions/tagged/snapdragon-utilsnapdragon-util-4.0.0/.gitignore000066400000000000000000000004461317626570000166250ustar00rootroot00000000000000# always ignore files *.DS_Store .idea *.sublime-* # test related, or directories generated by tests test/actual actual coverage .nyc* # npm node_modules npm-debug.log # yarn yarn.lock yarn-error.log # misc _gh_pages _draft _drafts bower_components vendor temp tmp TODO.md package-lock.jsonsnapdragon-util-4.0.0/.travis.yml000066400000000000000000000002261317626570000167420ustar00rootroot00000000000000sudo: false os: - linux - osx language: node_js node_js: - node - '9' - '8' - '7' - '6' - '5' - '4.7' - '4' - '0.12' - '0.10' snapdragon-util-4.0.0/.verb.md000066400000000000000000000023071317626570000161710ustar00rootroot00000000000000## Usage ```js var util = require('snapdragon-util'); ``` ## API {%= apidocs("index.js") %} ## Release history Changelog entries are classified using the following labels from [keep-a-changelog][]: * `added`: for new features * `changed`: for changes in existing functionality * `deprecated`: for once-stable features removed in upcoming releases * `removed`: for deprecated features removed in this release * `fixed`: for any bug fixes Custom labels used in this changelog: * `dependencies`: bumps dependencies * `housekeeping`: code re-organization, minor edits, or other changes that don't fit in one of the other categories. ### [4.0.0] - 2017-11-01 **Dependencies** - Updated `kind-of` to version 6.0 ### [3.0.0] - 2017-05-01 **Changed** - `.emit` was renamed to [.append](#append) - `.addNode` was renamed to [.pushNode](#pushNode) - `.getNode` was renamed to [.findNode](#findNode) - `.isEmptyNodes` was renamed to [.isEmpty](#isEmpty): also now works with `node.nodes` and/or `node.val` **Added** - [.identity](#identity) - [.removeNode](#removeNode) - [.shiftNode](#shiftNode) - [.popNode](#popNode) ### [0.1.0] First release. [keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog snapdragon-util-4.0.0/LICENSE000066400000000000000000000020731317626570000156400ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2017, Jon Schlinkert. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. snapdragon-util-4.0.0/README.md000066400000000000000000000523041317626570000161140ustar00rootroot00000000000000# snapdragon-util [![NPM version](https://img.shields.io/npm/v/snapdragon-util.svg?style=flat)](https://www.npmjs.com/package/snapdragon-util) [![NPM monthly downloads](https://img.shields.io/npm/dm/snapdragon-util.svg?style=flat)](https://npmjs.org/package/snapdragon-util) [![NPM total downloads](https://img.shields.io/npm/dt/snapdragon-util.svg?style=flat)](https://npmjs.org/package/snapdragon-util) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/snapdragon-util.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/snapdragon-util) > Utilities for the snapdragon parser/compiler. Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support. ## Install Install with [npm](https://www.npmjs.com/): ```sh $ npm install --save snapdragon-util ``` ## Usage ```js var util = require('snapdragon-util'); ``` ## API ### [.isNode](index.js#L20) Returns true if the given value is a node. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Boolean}** **Example** ```js var Node = require('snapdragon-node'); var node = new Node({type: 'foo'}); console.log(utils.isNode(node)); //=> true console.log(utils.isNode({})); //=> false ``` ### [.noop](index.js#L36) Emit an empty string for the given `node`. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{undefined}** **Example** ```js // do nothing for beginning-of-string snapdragon.compiler.set('bos', utils.noop); ``` ### [.identity](index.js#L52) Appdend `node.val` to `compiler.output`, exactly as it was created by the parser. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{undefined}** **Example** ```js snapdragon.compiler.set('text', utils.identity); ``` ### [.append](index.js#L75) Previously named `.emit`, this method appends the given `val` to `compiler.output` for the given node. Useful when you know what value should be appended advance, regardless of the actual value of `node.val`. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Function}**: Returns a compiler middleware function. **Example** ```js snapdragon.compiler .set('i', function(node) { this.mapVisit(node); }) .set('i.open', utils.append('')) .set('i.close', utils.append('')) ``` ### [.toNoop](index.js#L98) Used in compiler middleware, this onverts an AST node into an empty `text` node and deletes `node.nodes` if it exists. The advantage of this method is that, as opposed to completely removing the node, indices will not need to be re-calculated in sibling nodes, and nothing is appended to the output. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `nodes` **{Array}**: Optionally pass a new `nodes` value, to replace the existing `node.nodes` array. **Example** ```js utils.toNoop(node); // convert `node.nodes` to the given value instead of deleting it utils.toNoop(node, []); ``` ### [.visit](index.js#L127) Visit `node` with the given `fn`. The built-in `.visit` method in snapdragon automatically calls registered compilers, this allows you to pass a visitor function. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `fn` **{Function}** * `returns` **{Object}**: returns the node after recursively visiting all child nodes. **Example** ```js snapdragon.compiler.set('i', function(node) { utils.visit(node, function(childNode) { // do stuff with "childNode" return childNode; }); }); ``` ### [.mapVisit](index.js#L154) Map [visit](#visit) the given `fn` over `node.nodes`. This is called by [visit](#visit), use this method if you do not want `fn` to be called on the first node. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `options` **{Object}** * `fn` **{Function}** * `returns` **{Object}**: returns the node **Example** ```js snapdragon.compiler.set('i', function(node) { utils.mapVisit(node, function(childNode) { // do stuff with "childNode" return childNode; }); }); ``` ### [.addOpen](index.js#L193) Unshift an `*.open` node onto `node.nodes`. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `Node` **{Function}**: (required) Node constructor function from [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node). * `filter` **{Function}**: Optionaly specify a filter function to exclude the node. * `returns` **{Object}**: Returns the created opening node. **Example** ```js var Node = require('snapdragon-node'); snapdragon.parser.set('brace', function(node) { var match = this.match(/^{/); if (match) { var parent = new Node({type: 'brace'}); utils.addOpen(parent, Node); console.log(parent.nodes[0]): // { type: 'brace.open', val: '' }; // push the parent "brace" node onto the stack this.push(parent); // return the parent node, so it's also added to the AST return brace; } }); ``` ### [.addClose](index.js#L243) Push a `*.close` node onto `node.nodes`. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `Node` **{Function}**: (required) Node constructor function from [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node). * `filter` **{Function}**: Optionaly specify a filter function to exclude the node. * `returns` **{Object}**: Returns the created closing node. **Example** ```js var Node = require('snapdragon-node'); snapdragon.parser.set('brace', function(node) { var match = this.match(/^}/); if (match) { var parent = this.parent(); if (parent.type !== 'brace') { throw new Error('missing opening: ' + '}'); } utils.addClose(parent, Node); console.log(parent.nodes[parent.nodes.length - 1]): // { type: 'brace.close', val: '' }; // no need to return a node, since the parent // was already added to the AST return; } }); ``` ### [.wrapNodes](index.js#L273) Wraps the given `node` with `*.open` and `*.close` nodes. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `Node` **{Function}**: (required) Node constructor function from [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node). * `filter` **{Function}**: Optionaly specify a filter function to exclude the node. * `returns` **{Object}**: Returns the node ### [.pushNode](index.js#L298) Push the given `node` onto `parent.nodes`, and set `parent` as `node.parent. **Params** * `parent` **{Object}** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Object}**: Returns the child node **Example** ```js var parent = new Node({type: 'foo'}); var node = new Node({type: 'bar'}); utils.pushNode(parent, node); console.log(parent.nodes[0].type) // 'bar' console.log(node.parent.type) // 'foo' ``` ### [.unshiftNode](index.js#L324) Unshift `node` onto `parent.nodes`, and set `parent` as `node.parent. **Params** * `parent` **{Object}** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{undefined}** **Example** ```js var parent = new Node({type: 'foo'}); var node = new Node({type: 'bar'}); utils.unshiftNode(parent, node); console.log(parent.nodes[0].type) // 'bar' console.log(node.parent.type) // 'foo' ``` ### [.popNode](index.js#L353) Pop the last `node` off of `parent.nodes`. The advantage of using this method is that it checks for `node.nodes` and works with any version of `snapdragon-node`. **Params** * `parent` **{Object}** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Number|Undefined}**: Returns the length of `node.nodes` or undefined. **Example** ```js var parent = new Node({type: 'foo'}); utils.pushNode(parent, new Node({type: 'foo'})); utils.pushNode(parent, new Node({type: 'bar'})); utils.pushNode(parent, new Node({type: 'baz'})); console.log(parent.nodes.length); //=> 3 utils.popNode(parent); console.log(parent.nodes.length); //=> 2 ``` ### [.shiftNode](index.js#L381) Shift the first `node` off of `parent.nodes`. The advantage of using this method is that it checks for `node.nodes` and works with any version of `snapdragon-node`. **Params** * `parent` **{Object}** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Number|Undefined}**: Returns the length of `node.nodes` or undefined. **Example** ```js var parent = new Node({type: 'foo'}); utils.pushNode(parent, new Node({type: 'foo'})); utils.pushNode(parent, new Node({type: 'bar'})); utils.pushNode(parent, new Node({type: 'baz'})); console.log(parent.nodes.length); //=> 3 utils.shiftNode(parent); console.log(parent.nodes.length); //=> 2 ``` ### [.removeNode](index.js#L408) Remove the specified `node` from `parent.nodes`. **Params** * `parent` **{Object}** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Object|undefined}**: Returns the removed node, if successful, or undefined if it does not exist on `parent.nodes`. **Example** ```js var parent = new Node({type: 'abc'}); var foo = new Node({type: 'foo'}); utils.pushNode(parent, foo); utils.pushNode(parent, new Node({type: 'bar'})); utils.pushNode(parent, new Node({type: 'baz'})); console.log(parent.nodes.length); //=> 3 utils.removeNode(parent, foo); console.log(parent.nodes.length); //=> 2 ``` ### [.isType](index.js#L442) Returns true if `node.type` matches the given `type`. Throws a `TypeError` if `node` is not an instance of `Node`. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `type` **{String}** * `returns` **{Boolean}** **Example** ```js var Node = require('snapdragon-node'); var node = new Node({type: 'foo'}); console.log(utils.isType(node, 'foo')); // false console.log(utils.isType(node, 'bar')); // true ``` ### [.hasType](index.js#L485) Returns true if the given `node` has the given `type` in `node.nodes`. Throws a `TypeError` if `node` is not an instance of `Node`. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `type` **{String}** * `returns` **{Boolean}** **Example** ```js var Node = require('snapdragon-node'); var node = new Node({ type: 'foo', nodes: [ new Node({type: 'bar'}), new Node({type: 'baz'}) ] }); console.log(utils.hasType(node, 'xyz')); // false console.log(utils.hasType(node, 'baz')); // true ``` ### [.firstOfType](index.js#L518) Returns the first node from `node.nodes` of the given `type` **Params** * `nodes` **{Array}** * `type` **{String}** * `returns` **{Object|undefined}**: Returns the first matching node or undefined. **Example** ```js var node = new Node({ type: 'foo', nodes: [ new Node({type: 'text', val: 'abc'}), new Node({type: 'text', val: 'xyz'}) ] }); var textNode = utils.firstOfType(node.nodes, 'text'); console.log(textNode.val); //=> 'abc' ``` ### [.findNode](index.js#L555) Returns the node at the specified index, or the first node of the given `type` from `node.nodes`. **Params** * `nodes` **{Array}** * `type` **{String|Number}**: Node type or index. * `returns` **{Object}**: Returns a node or undefined. **Example** ```js var node = new Node({ type: 'foo', nodes: [ new Node({type: 'text', val: 'abc'}), new Node({type: 'text', val: 'xyz'}) ] }); var nodeOne = utils.findNode(node.nodes, 'text'); console.log(nodeOne.val); //=> 'abc' var nodeTwo = utils.findNode(node.nodes, 1); console.log(nodeTwo.val); //=> 'xyz' ``` ### [.isOpen](index.js#L583) Returns true if the given node is an "*.open" node. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Boolean}** **Example** ```js var Node = require('snapdragon-node'); var brace = new Node({type: 'brace'}); var open = new Node({type: 'brace.open'}); var close = new Node({type: 'brace.close'}); console.log(utils.isOpen(brace)); // false console.log(utils.isOpen(open)); // true console.log(utils.isOpen(close)); // false ``` ### [.isClose](index.js#L606) Returns true if the given node is a "*.close" node. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Boolean}** **Example** ```js var Node = require('snapdragon-node'); var brace = new Node({type: 'brace'}); var open = new Node({type: 'brace.open'}); var close = new Node({type: 'brace.close'}); console.log(utils.isClose(brace)); // false console.log(utils.isClose(open)); // false console.log(utils.isClose(close)); // true ``` ### [.hasOpen](index.js#L632) Returns true if `node.nodes` **has** an `.open` node **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Boolean}** **Example** ```js var Node = require('snapdragon-node'); var brace = new Node({ type: 'brace', nodes: [] }); var open = new Node({type: 'brace.open'}); console.log(utils.hasOpen(brace)); // false brace.pushNode(open); console.log(utils.hasOpen(brace)); // true ``` ### [.hasClose](index.js#L662) Returns true if `node.nodes` **has** a `.close` node **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Boolean}** **Example** ```js var Node = require('snapdragon-node'); var brace = new Node({ type: 'brace', nodes: [] }); var close = new Node({type: 'brace.close'}); console.log(utils.hasClose(brace)); // false brace.pushNode(close); console.log(utils.hasClose(brace)); // true ``` ### [.hasOpenAndClose](index.js#L696) Returns true if `node.nodes` has both `.open` and `.close` nodes **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Boolean}** **Example** ```js var Node = require('snapdragon-node'); var brace = new Node({ type: 'brace', nodes: [] }); var open = new Node({type: 'brace.open'}); var close = new Node({type: 'brace.close'}); console.log(utils.hasOpen(brace)); // false console.log(utils.hasClose(brace)); // false brace.pushNode(open); brace.pushNode(close); console.log(utils.hasOpen(brace)); // true console.log(utils.hasClose(brace)); // true ``` ### [.addType](index.js#L718) Push the given `node` onto the `state.inside` array for the given type. This array is used as a specialized "stack" for only the given `node.type`. **Params** * `state` **{Object}**: The `compiler.state` object or custom state object. * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Array}**: Returns the `state.inside` stack for the given type. **Example** ```js var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); console.log(state.inside); //=> { brace: [{type: 'brace'}] } ``` ### [.removeType](index.js#L758) Remove the given `node` from the `state.inside` array for the given type. This array is used as a specialized "stack" for only the given `node.type`. **Params** * `state` **{Object}**: The `compiler.state` object or custom state object. * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `returns` **{Array}**: Returns the `state.inside` stack for the given type. **Example** ```js var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); console.log(state.inside); //=> { brace: [{type: 'brace'}] } utils.removeType(state, node); //=> { brace: [] } ``` ### [.isEmpty](index.js#L787) Returns true if `node.val` is an empty string, or `node.nodes` does not contain any non-empty text nodes. **Params** * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `fn` **{Function}** * `returns` **{Boolean}** **Example** ```js var node = new Node({type: 'text'}); utils.isEmpty(node); //=> true node.val = 'foo'; utils.isEmpty(node); //=> false ``` ### [.isInsideType](index.js#L832) Returns true if the `state.inside` stack for the given type exists and has one or more nodes on it. **Params** * `state` **{Object}** * `type` **{String}** * `returns` **{Boolean}** **Example** ```js var state = { inside: {}}; var node = new Node({type: 'brace'}); console.log(utils.isInsideType(state, 'brace')); //=> false utils.addType(state, node); console.log(utils.isInsideType(state, 'brace')); //=> true utils.removeType(state, node); console.log(utils.isInsideType(state, 'brace')); //=> false ``` ### [.isInside](index.js#L866) Returns true if `node` is either a child or grand-child of the given `type`, or `state.inside[type]` is a non-empty array. **Params** * `state` **{Object}**: Either the `compiler.state` object, if it exists, or a user-supplied state object. * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) * `type` **{String}**: The `node.type` to check for. * `returns` **{Boolean}** **Example** ```js var state = { inside: {}}; var node = new Node({type: 'brace'}); var open = new Node({type: 'brace.open'}); console.log(utils.isInside(state, open, 'brace')); //=> false utils.pushNode(node, open); console.log(utils.isInside(state, open, 'brace')); //=> true ``` ### [.last](index.js#L914) Get the last `n` element from the given `array`. Used for getting a node from `node.nodes.` **Params** * `array` **{Array}** * `n` **{Number}** * `returns` **{undefined}** ### [.arrayify](index.js#L934) Cast the given `val` to an array. **Params** * `val` **{any}** * `returns` **{Array}** **Example** ```js console.log(utils.arrayify('')); //=> [] console.log(utils.arrayify('foo')); //=> ['foo'] console.log(utils.arrayify(['foo'])); //=> ['foo'] ``` ### [.stringify](index.js#L953) Convert the given `val` to a string by joining with `,`. Useful for creating a cheerio/CSS/DOM-style selector from a list of strings. **Params** * `val` **{any}** * `returns` **{Array}** ### [.trim](index.js#L966) Ensure that the given value is a string and call `.trim()` on it, or return an empty string. **Params** * `str` **{String}** * `returns` **{String}** ## Release history Changelog entries are classified using the following labels from [keep-a-changelog](https://github.com/olivierlacan/keep-a-changelog): * `added`: for new features * `changed`: for changes in existing functionality * `deprecated`: for once-stable features removed in upcoming releases * `removed`: for deprecated features removed in this release * `fixed`: for any bug fixes Custom labels used in this changelog: * `dependencies`: bumps dependencies * `housekeeping`: code re-organization, minor edits, or other changes that don't fit in one of the other categories. ### [4.0.0] - 2017-11-01 **Dependencies** * Updated `kind-of` to version 6.0 ### [3.0.0] - 2017-05-01 **Changed** * `.emit` was renamed to [.append](#append) * `.addNode` was renamed to [.pushNode](#pushNode) * `.getNode` was renamed to [.findNode](#findNode) * `.isEmptyNodes` was renamed to [.isEmpty](#isEmpty): also now works with `node.nodes` and/or `node.val` **Added** * [.identity](#identity) * [.removeNode](#removeNode) * [.shiftNode](#shiftNode) * [.popNode](#popNode) ### [0.1.0] First release. ## About
Contributing Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). Please read the [contributing guide](.github/contributing.md) for advice on opening issues, pull requests, and coding standards.
Running Tests Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: ```sh $ npm install && npm test ```
Building docs _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ To generate the readme, run the following command: ```sh $ npm install -g verbose/verb#dev verb-generate-readme && verb ```
### Contributors | **Commits** | **Contributor** | | --- | --- | | 40 | [jonschlinkert](https://github.com/jonschlinkert) | | 2 | [realityking](https://github.com/realityking) | ### Author **Jon Schlinkert** * [github/jonschlinkert](https://github.com/jonschlinkert) * [twitter/jonschlinkert](https://twitter.com/jonschlinkert) ### License Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert). Released under the [MIT License](LICENSE). *** _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on November 01, 2017._snapdragon-util-4.0.0/gulpfile.js000066400000000000000000000011371317626570000170000ustar00rootroot00000000000000'use strict'; var gulp = require('gulp'); var mocha = require('gulp-mocha'); var istanbul = require('gulp-istanbul'); var eslint = require('gulp-eslint'); gulp.task('coverage', function() { return gulp.src('index.js') .pipe(istanbul()) .pipe(istanbul.hookRequire()); }); gulp.task('test', ['coverage'], function() { return gulp.src('test/*.js') .pipe(mocha({reporter: 'spec'})) .pipe(istanbul.writeReports()); }); gulp.task('lint', function() { return gulp.src(['index.js', 'test/*.js']) .pipe(eslint()) .pipe(eslint.format()); }); gulp.task('default', ['test', 'lint']); snapdragon-util-4.0.0/index.js000066400000000000000000000642611317626570000163070ustar00rootroot00000000000000'use strict'; var typeOf = require('kind-of'); var utils = module.exports; /** * Returns true if the given value is a node. * * ```js * var Node = require('snapdragon-node'); * var node = new Node({type: 'foo'}); * console.log(utils.isNode(node)); //=> true * console.log(utils.isNode({})); //=> false * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @returns {Boolean} * @api public */ utils.isNode = function(node) { return typeOf(node) === 'object' && node.isNode === true; }; /** * Emit an empty string for the given `node`. * * ```js * // do nothing for beginning-of-string * snapdragon.compiler.set('bos', utils.noop); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @returns {undefined} * @api public */ utils.noop = function(node) { append(this, '', node); }; /** * Appdend `node.val` to `compiler.output`, exactly as it was created * by the parser. * * ```js * snapdragon.compiler.set('text', utils.identity); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @returns {undefined} * @api public */ utils.identity = function(node) { append(this, node.val, node); }; /** * Previously named `.emit`, this method appends the given `val` * to `compiler.output` for the given node. Useful when you know * what value should be appended advance, regardless of the actual * value of `node.val`. * * ```js * snapdragon.compiler * .set('i', function(node) { * this.mapVisit(node); * }) * .set('i.open', utils.append('')) * .set('i.close', utils.append('')) * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @returns {Function} Returns a compiler middleware function. * @api public */ utils.append = function(val) { return function(node) { append(this, val, node); }; }; /** * Used in compiler middleware, this onverts an AST node into * an empty `text` node and deletes `node.nodes` if it exists. * The advantage of this method is that, as opposed to completely * removing the node, indices will not need to be re-calculated * in sibling nodes, and nothing is appended to the output. * * ```js * utils.toNoop(node); * // convert `node.nodes` to the given value instead of deleting it * utils.toNoop(node, []); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Array} `nodes` Optionally pass a new `nodes` value, to replace the existing `node.nodes` array. * @api public */ utils.toNoop = function(node, nodes) { if (nodes) { node.nodes = nodes; } else { delete node.nodes; node.type = 'text'; node.val = ''; } }; /** * Visit `node` with the given `fn`. The built-in `.visit` method in snapdragon * automatically calls registered compilers, this allows you to pass a visitor * function. * * ```js * snapdragon.compiler.set('i', function(node) { * utils.visit(node, function(childNode) { * // do stuff with "childNode" * return childNode; * }); * }); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Function} `fn` * @return {Object} returns the node after recursively visiting all child nodes. * @api public */ utils.visit = function(node, fn) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isFunction(fn), 'expected a visitor function'); fn(node); return node.nodes ? utils.mapVisit(node, fn) : node; }; /** * Map [visit](#visit) the given `fn` over `node.nodes`. This is called by * [visit](#visit), use this method if you do not want `fn` to be called on * the first node. * * ```js * snapdragon.compiler.set('i', function(node) { * utils.mapVisit(node, function(childNode) { * // do stuff with "childNode" * return childNode; * }); * }); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Object} `options` * @param {Function} `fn` * @return {Object} returns the node * @api public */ utils.mapVisit = function(node, fn) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isArray(node.nodes), 'expected node.nodes to be an array'); assert(isFunction(fn), 'expected a visitor function'); for (var i = 0; i < node.nodes.length; i++) { utils.visit(node.nodes[i], fn); } return node; }; /** * Unshift an `*.open` node onto `node.nodes`. * * ```js * var Node = require('snapdragon-node'); * snapdragon.parser.set('brace', function(node) { * var match = this.match(/^{/); * if (match) { * var parent = new Node({type: 'brace'}); * utils.addOpen(parent, Node); * console.log(parent.nodes[0]): * // { type: 'brace.open', val: '' }; * * // push the parent "brace" node onto the stack * this.push(parent); * * // return the parent node, so it's also added to the AST * return brace; * } * }); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. * @param {Function} `filter` Optionaly specify a filter function to exclude the node. * @return {Object} Returns the created opening node. * @api public */ utils.addOpen = function(node, Node, val, filter) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isFunction(Node), 'expected Node to be a constructor function'); if (typeof val === 'function') { filter = val; val = ''; } if (typeof filter === 'function' && !filter(node)) return; var open = new Node({ type: node.type + '.open', val: val}); var unshift = node.unshift || node.unshiftNode; if (typeof unshift === 'function') { unshift.call(node, open); } else { utils.unshiftNode(node, open); } return open; }; /** * Push a `*.close` node onto `node.nodes`. * * ```js * var Node = require('snapdragon-node'); * snapdragon.parser.set('brace', function(node) { * var match = this.match(/^}/); * if (match) { * var parent = this.parent(); * if (parent.type !== 'brace') { * throw new Error('missing opening: ' + '}'); * } * * utils.addClose(parent, Node); * console.log(parent.nodes[parent.nodes.length - 1]): * // { type: 'brace.close', val: '' }; * * // no need to return a node, since the parent * // was already added to the AST * return; * } * }); * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. * @param {Function} `filter` Optionaly specify a filter function to exclude the node. * @return {Object} Returns the created closing node. * @api public */ utils.addClose = function(node, Node, val, filter) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isFunction(Node), 'expected Node to be a constructor function'); if (typeof val === 'function') { filter = val; val = ''; } if (typeof filter === 'function' && !filter(node)) return; var close = new Node({ type: node.type + '.close', val: val}); var push = node.push || node.pushNode; if (typeof push === 'function') { push.call(node, close); } else { utils.pushNode(node, close); } return close; }; /** * Wraps the given `node` with `*.open` and `*.close` nodes. * * @param {Object} `node` Instance of [snapdragon-node][] * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. * @param {Function} `filter` Optionaly specify a filter function to exclude the node. * @return {Object} Returns the node * @api public */ utils.wrapNodes = function(node, Node, filter) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isFunction(Node), 'expected Node to be a constructor function'); utils.addOpen(node, Node, filter); utils.addClose(node, Node, filter); return node; }; /** * Push the given `node` onto `parent.nodes`, and set `parent` as `node.parent. * * ```js * var parent = new Node({type: 'foo'}); * var node = new Node({type: 'bar'}); * utils.pushNode(parent, node); * console.log(parent.nodes[0].type) // 'bar' * console.log(node.parent.type) // 'foo' * ``` * @param {Object} `parent` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Object} Returns the child node * @api public */ utils.pushNode = function(parent, node) { assert(utils.isNode(parent), 'expected parent node to be an instance of Node'); assert(utils.isNode(node), 'expected node to be an instance of Node'); node.define('parent', parent); parent.nodes = parent.nodes || []; parent.nodes.push(node); return node; }; /** * Unshift `node` onto `parent.nodes`, and set `parent` as `node.parent. * * ```js * var parent = new Node({type: 'foo'}); * var node = new Node({type: 'bar'}); * utils.unshiftNode(parent, node); * console.log(parent.nodes[0].type) // 'bar' * console.log(node.parent.type) // 'foo' * ``` * @param {Object} `parent` * @param {Object} `node` Instance of [snapdragon-node][] * @return {undefined} * @api public */ utils.unshiftNode = function(parent, node) { assert(utils.isNode(parent), 'expected parent node to be an instance of Node'); assert(utils.isNode(node), 'expected node to be an instance of Node'); node.define('parent', parent); parent.nodes = parent.nodes || []; parent.nodes.unshift(node); }; /** * Pop the last `node` off of `parent.nodes`. The advantage of * using this method is that it checks for `node.nodes` and works * with any version of `snapdragon-node`. * * ```js * var parent = new Node({type: 'foo'}); * utils.pushNode(parent, new Node({type: 'foo'})); * utils.pushNode(parent, new Node({type: 'bar'})); * utils.pushNode(parent, new Node({type: 'baz'})); * console.log(parent.nodes.length); //=> 3 * utils.popNode(parent); * console.log(parent.nodes.length); //=> 2 * ``` * @param {Object} `parent` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. * @api public */ utils.popNode = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); if (typeof node.pop === 'function') { return node.pop(); } return node.nodes && node.nodes.pop(); }; /** * Shift the first `node` off of `parent.nodes`. The advantage of * using this method is that it checks for `node.nodes` and works * with any version of `snapdragon-node`. * * ```js * var parent = new Node({type: 'foo'}); * utils.pushNode(parent, new Node({type: 'foo'})); * utils.pushNode(parent, new Node({type: 'bar'})); * utils.pushNode(parent, new Node({type: 'baz'})); * console.log(parent.nodes.length); //=> 3 * utils.shiftNode(parent); * console.log(parent.nodes.length); //=> 2 * ``` * @param {Object} `parent` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. * @api public */ utils.shiftNode = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); if (typeof node.shift === 'function') { return node.shift(); } return node.nodes && node.nodes.shift(); }; /** * Remove the specified `node` from `parent.nodes`. * * ```js * var parent = new Node({type: 'abc'}); * var foo = new Node({type: 'foo'}); * utils.pushNode(parent, foo); * utils.pushNode(parent, new Node({type: 'bar'})); * utils.pushNode(parent, new Node({type: 'baz'})); * console.log(parent.nodes.length); //=> 3 * utils.removeNode(parent, foo); * console.log(parent.nodes.length); //=> 2 * ``` * @param {Object} `parent` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Object|undefined} Returns the removed node, if successful, or undefined if it does not exist on `parent.nodes`. * @api public */ utils.removeNode = function(parent, node) { assert(utils.isNode(parent), 'expected parent.node to be an instance of Node'); assert(utils.isNode(node), 'expected node to be an instance of Node'); if (!parent.nodes) { return null; } if (typeof parent.remove === 'function') { return parent.remove(node); } var idx = parent.nodes.indexOf(node); if (idx !== -1) { return parent.nodes.splice(idx, 1); } }; /** * Returns true if `node.type` matches the given `type`. Throws a * `TypeError` if `node` is not an instance of `Node`. * * ```js * var Node = require('snapdragon-node'); * var node = new Node({type: 'foo'}); * console.log(utils.isType(node, 'foo')); // false * console.log(utils.isType(node, 'bar')); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {String} `type` * @return {Boolean} * @api public */ utils.isType = function(node, type) { assert(utils.isNode(node), 'expected node to be an instance of Node'); switch (typeOf(type)) { case 'array': var types = type.slice(); for (var i = 0; i < types.length; i++) { if (utils.isType(node, types[i])) { return true; } } return false; case 'string': return node.type === type; case 'regexp': return type.test(node.type); default: { throw new TypeError('expected "type" to be an array, string or regexp'); } } }; /** * Returns true if the given `node` has the given `type` in `node.nodes`. * Throws a `TypeError` if `node` is not an instance of `Node`. * * ```js * var Node = require('snapdragon-node'); * var node = new Node({ * type: 'foo', * nodes: [ * new Node({type: 'bar'}), * new Node({type: 'baz'}) * ] * }); * console.log(utils.hasType(node, 'xyz')); // false * console.log(utils.hasType(node, 'baz')); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {String} `type` * @return {Boolean} * @api public */ utils.hasType = function(node, type) { assert(utils.isNode(node), 'expected node to be an instance of Node'); if (!Array.isArray(node.nodes)) return false; for (var i = 0; i < node.nodes.length; i++) { if (utils.isType(node.nodes[i], type)) { return true; } } return false; }; /** * Returns the first node from `node.nodes` of the given `type` * * ```js * var node = new Node({ * type: 'foo', * nodes: [ * new Node({type: 'text', val: 'abc'}), * new Node({type: 'text', val: 'xyz'}) * ] * }); * * var textNode = utils.firstOfType(node.nodes, 'text'); * console.log(textNode.val); * //=> 'abc' * ``` * @param {Array} `nodes` * @param {String} `type` * @return {Object|undefined} Returns the first matching node or undefined. * @api public */ utils.firstOfType = function(nodes, type) { for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (utils.isType(node, type)) { return node; } } }; /** * Returns the node at the specified index, or the first node of the * given `type` from `node.nodes`. * * ```js * var node = new Node({ * type: 'foo', * nodes: [ * new Node({type: 'text', val: 'abc'}), * new Node({type: 'text', val: 'xyz'}) * ] * }); * * var nodeOne = utils.findNode(node.nodes, 'text'); * console.log(nodeOne.val); * //=> 'abc' * * var nodeTwo = utils.findNode(node.nodes, 1); * console.log(nodeTwo.val); * //=> 'xyz' * ``` * * @param {Array} `nodes` * @param {String|Number} `type` Node type or index. * @return {Object} Returns a node or undefined. * @api public */ utils.findNode = function(nodes, type) { if (!Array.isArray(nodes)) { return null; } if (typeof type === 'number') { return nodes[type]; } return utils.firstOfType(nodes, type); }; /** * Returns true if the given node is an "*.open" node. * * ```js * var Node = require('snapdragon-node'); * var brace = new Node({type: 'brace'}); * var open = new Node({type: 'brace.open'}); * var close = new Node({type: 'brace.close'}); * * console.log(utils.isOpen(brace)); // false * console.log(utils.isOpen(open)); // true * console.log(utils.isOpen(close)); // false * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Boolean} * @api public */ utils.isOpen = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); return node.type.slice(-5) === '.open'; }; /** * Returns true if the given node is a "*.close" node. * * ```js * var Node = require('snapdragon-node'); * var brace = new Node({type: 'brace'}); * var open = new Node({type: 'brace.open'}); * var close = new Node({type: 'brace.close'}); * * console.log(utils.isClose(brace)); // false * console.log(utils.isClose(open)); // false * console.log(utils.isClose(close)); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Boolean} * @api public */ utils.isClose = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); return node.type.slice(-6) === '.close'; }; /** * Returns true if `node.nodes` **has** an `.open` node * * ```js * var Node = require('snapdragon-node'); * var brace = new Node({ * type: 'brace', * nodes: [] * }); * * var open = new Node({type: 'brace.open'}); * console.log(utils.hasOpen(brace)); // false * * brace.pushNode(open); * console.log(utils.hasOpen(brace)); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Boolean} * @api public */ utils.hasOpen = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); var first = node.first || node.nodes ? node.nodes[0] : null; if (utils.isNode(first)) { return first.type === node.type + '.open'; } return false; }; /** * Returns true if `node.nodes` **has** a `.close` node * * ```js * var Node = require('snapdragon-node'); * var brace = new Node({ * type: 'brace', * nodes: [] * }); * * var close = new Node({type: 'brace.close'}); * console.log(utils.hasClose(brace)); // false * * brace.pushNode(close); * console.log(utils.hasClose(brace)); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Boolean} * @api public */ utils.hasClose = function(node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); var last = node.last || node.nodes ? node.nodes[node.nodes.length - 1] : null; if (utils.isNode(last)) { return last.type === node.type + '.close'; } return false; }; /** * Returns true if `node.nodes` has both `.open` and `.close` nodes * * ```js * var Node = require('snapdragon-node'); * var brace = new Node({ * type: 'brace', * nodes: [] * }); * * var open = new Node({type: 'brace.open'}); * var close = new Node({type: 'brace.close'}); * console.log(utils.hasOpen(brace)); // false * console.log(utils.hasClose(brace)); // false * * brace.pushNode(open); * brace.pushNode(close); * console.log(utils.hasOpen(brace)); // true * console.log(utils.hasClose(brace)); // true * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @return {Boolean} * @api public */ utils.hasOpenAndClose = function(node) { return utils.hasOpen(node) && utils.hasClose(node); }; /** * Push the given `node` onto the `state.inside` array for the * given type. This array is used as a specialized "stack" for * only the given `node.type`. * * ```js * var state = { inside: {}}; * var node = new Node({type: 'brace'}); * utils.addType(state, node); * console.log(state.inside); * //=> { brace: [{type: 'brace'}] } * ``` * @param {Object} `state` The `compiler.state` object or custom state object. * @param {Object} `node` Instance of [snapdragon-node][] * @return {Array} Returns the `state.inside` stack for the given type. * @api public */ utils.addType = function(state, node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isObject(state), 'expected state to be an object'); var type = node.parent ? node.parent.type : node.type.replace(/\.open$/, ''); if (!state.hasOwnProperty('inside')) { state.inside = {}; } if (!state.inside.hasOwnProperty(type)) { state.inside[type] = []; } var arr = state.inside[type]; arr.push(node); return arr; }; /** * Remove the given `node` from the `state.inside` array for the * given type. This array is used as a specialized "stack" for * only the given `node.type`. * * ```js * var state = { inside: {}}; * var node = new Node({type: 'brace'}); * utils.addType(state, node); * console.log(state.inside); * //=> { brace: [{type: 'brace'}] } * utils.removeType(state, node); * //=> { brace: [] } * ``` * @param {Object} `state` The `compiler.state` object or custom state object. * @param {Object} `node` Instance of [snapdragon-node][] * @return {Array} Returns the `state.inside` stack for the given type. * @api public */ utils.removeType = function(state, node) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isObject(state), 'expected state to be an object'); var type = node.parent ? node.parent.type : node.type.replace(/\.close$/, ''); if (state.inside.hasOwnProperty(type)) { return state.inside[type].pop(); } }; /** * Returns true if `node.val` is an empty string, or `node.nodes` does * not contain any non-empty text nodes. * * ```js * var node = new Node({type: 'text'}); * utils.isEmpty(node); //=> true * node.val = 'foo'; * utils.isEmpty(node); //=> false * ``` * @param {Object} `node` Instance of [snapdragon-node][] * @param {Function} `fn` * @return {Boolean} * @api public */ utils.isEmpty = function(node, fn) { assert(utils.isNode(node), 'expected node to be an instance of Node'); if (!Array.isArray(node.nodes)) { if (node.type !== 'text') { return true; } if (typeof fn === 'function') { return fn(node, node.parent); } return !utils.trim(node.val); } for (var i = 0; i < node.nodes.length; i++) { var child = node.nodes[i]; if (utils.isOpen(child) || utils.isClose(child)) { continue; } if (!utils.isEmpty(child, fn)) { return false; } } return true; }; /** * Returns true if the `state.inside` stack for the given type exists * and has one or more nodes on it. * * ```js * var state = { inside: {}}; * var node = new Node({type: 'brace'}); * console.log(utils.isInsideType(state, 'brace')); //=> false * utils.addType(state, node); * console.log(utils.isInsideType(state, 'brace')); //=> true * utils.removeType(state, node); * console.log(utils.isInsideType(state, 'brace')); //=> false * ``` * @param {Object} `state` * @param {String} `type` * @return {Boolean} * @api public */ utils.isInsideType = function(state, type) { assert(isObject(state), 'expected state to be an object'); assert(isString(type), 'expected type to be a string'); if (!state.hasOwnProperty('inside')) { return false; } if (!state.inside.hasOwnProperty(type)) { return false; } return state.inside[type].length > 0; }; /** * Returns true if `node` is either a child or grand-child of the given `type`, * or `state.inside[type]` is a non-empty array. * * ```js * var state = { inside: {}}; * var node = new Node({type: 'brace'}); * var open = new Node({type: 'brace.open'}); * console.log(utils.isInside(state, open, 'brace')); //=> false * utils.pushNode(node, open); * console.log(utils.isInside(state, open, 'brace')); //=> true * ``` * @param {Object} `state` Either the `compiler.state` object, if it exists, or a user-supplied state object. * @param {Object} `node` Instance of [snapdragon-node][] * @param {String} `type` The `node.type` to check for. * @return {Boolean} * @api public */ utils.isInside = function(state, node, type) { assert(utils.isNode(node), 'expected node to be an instance of Node'); assert(isObject(state), 'expected state to be an object'); if (Array.isArray(type)) { for (var i = 0; i < type.length; i++) { if (utils.isInside(state, node, type[i])) { return true; } } return false; } var parent = node.parent; if (typeof type === 'string') { return (parent && parent.type === type) || utils.isInsideType(state, type); } if (typeOf(type) === 'regexp') { if (parent && parent.type && type.test(parent.type)) { return true; } var keys = Object.keys(state.inside); var len = keys.length; var idx = -1; while (++idx < len) { var key = keys[idx]; var val = state.inside[key]; if (Array.isArray(val) && val.length !== 0 && type.test(key)) { return true; } } } return false; }; /** * Get the last `n` element from the given `array`. Used for getting * a node from `node.nodes.` * * @param {Array} `array` * @param {Number} `n` * @return {undefined} * @api public */ utils.last = function(arr, n) { return arr[arr.length - (n || 1)]; }; /** * Cast the given `val` to an array. * * ```js * console.log(utils.arrayify('')); * //=> [] * console.log(utils.arrayify('foo')); * //=> ['foo'] * console.log(utils.arrayify(['foo'])); * //=> ['foo'] * ``` * @param {any} `val` * @return {Array} * @api public */ utils.arrayify = function(val) { if (typeof val === 'string' && val !== '') { return [val]; } if (!Array.isArray(val)) { return []; } return val; }; /** * Convert the given `val` to a string by joining with `,`. Useful * for creating a cheerio/CSS/DOM-style selector from a list of strings. * * @param {any} `val` * @return {Array} * @api public */ utils.stringify = function(val) { return utils.arrayify(val).join(','); }; /** * Ensure that the given value is a string and call `.trim()` on it, * or return an empty string. * * @param {String} `str` * @return {String} * @api public */ utils.trim = function(str) { return typeof str === 'string' ? str.trim() : ''; }; /** * Return true if val is an object */ function isObject(val) { return typeOf(val) === 'object'; } /** * Return true if val is a string */ function isString(val) { return typeof val === 'string'; } /** * Return true if val is a function */ function isFunction(val) { return typeof val === 'function'; } /** * Return true if val is an array */ function isArray(val) { return Array.isArray(val); } /** * Shim to ensure the `.append` methods work with any version of snapdragon */ function append(compiler, val, node) { if (typeof compiler.append !== 'function') { return compiler.emit(val, node); } return compiler.append(val, node); } /** * Simplified assertion. Throws an error is `val` is falsey. */ function assert(val, message) { if (!val) throw new Error(message); } snapdragon-util-4.0.0/package.json000066400000000000000000000026171317626570000171250ustar00rootroot00000000000000{ "name": "snapdragon-util", "description": "Utilities for the snapdragon parser/compiler.", "version": "4.0.0", "homepage": "https://github.com/jonschlinkert/snapdragon-util", "author": "Jon Schlinkert (https://github.com/jonschlinkert)", "contributors": [ "Jon Schlinkert (http://twitter.com/jonschlinkert)", "Rouven Weßling (www.rouvenwessling.de)" ], "repository": "jonschlinkert/snapdragon-util", "bugs": { "url": "https://github.com/jonschlinkert/snapdragon-util/issues" }, "license": "MIT", "files": [ "index.js" ], "main": "index.js", "engines": { "node": ">=0.10.0" }, "scripts": { "test": "mocha" }, "dependencies": { "kind-of": "^6.0.0" }, "devDependencies": { "define-property": "^1.0.0", "gulp": "^3.9.1", "gulp-eslint": "^4.0.0", "gulp-format-md": "^1.0.0", "gulp-istanbul": "^1.1.2", "gulp-mocha": "^3.0.1", "isobject": "^3.0.1", "mocha": "^3.5.3", "snapdragon": "^0.11.0", "snapdragon-node": "^2.1.1" }, "keywords": [ "capture", "compile", "compiler", "convert", "match", "parse", "parser", "plugin", "render", "snapdragon", "snapdragonplugin", "transform", "util" ], "verb": { "layout": "default", "tasks": [ "readme" ], "plugins": [ "gulp-format-md" ], "lint": { "reflinks": true } } } snapdragon-util-4.0.0/test/000077500000000000000000000000001317626570000156105ustar00rootroot00000000000000snapdragon-util-4.0.0/test/support/000077500000000000000000000000001317626570000173245ustar00rootroot00000000000000snapdragon-util-4.0.0/test/support/index.js000066400000000000000000000066241317626570000210010ustar00rootroot00000000000000'use strict'; var assert = require('assert'); var isObject = require('isobject'); var define = require('define-property'); var getters = ['siblings', 'index', 'first', 'last', 'prev', 'next']; /** * This is a shim used in the unit tests * to ensure that snapdragon-util works with * older and newer versions of snapdragon-node */ function isNode(node) { return isObject(node) && node.isNode === true; } module.exports = function(node) { /** * Define a non-enumberable property on the node instance. * * ```js * var node = new Node(); * node.define('foo', 'something non-enumerable'); * ``` * @param {String} `name` * @param {any} `val` * @return {Object} returns the node instance * @api public */ node.define = function(name, val) { define(this, name, val); return this; }; /** * Given node `foo` and node `bar`, push node `bar` onto `foo.nodes`, and * set `foo` as `bar.parent`. * * ```js * var foo = new Node({type: 'foo'}); * var bar = new Node({type: 'bar'}); * foo.push(bar); * ``` * @param {Object} `node` * @return {Number} Returns the length of `node.nodes` * @api public */ node.push = function(node) { assert(isNode(node), 'expected node to be an instance of Node'); define(node, 'parent', this); this.nodes = this.nodes || []; return this.nodes.push(node); }; /** * Given node `foo` and node `bar`, unshift node `bar` onto `foo.nodes`, and * set `foo` as `bar.parent`. * * ```js * var foo = new Node({type: 'foo'}); * var bar = new Node({type: 'bar'}); * foo.unshift(bar); * ``` * @param {Object} `node` * @return {Number} Returns the length of `node.nodes` * @api public */ node.unshift = function(node) { assert(isNode(node), 'expected node to be an instance of Node'); define(node, 'parent', this); this.nodes = this.nodes || []; return this.nodes.unshift(node); }; /** * Pop a node from `node.nodes`. * * ```js * var node = new Node({type: 'foo'}); * node.push(new Node({type: 'a'})); * node.push(new Node({type: 'b'})); * node.push(new Node({type: 'c'})); * node.push(new Node({type: 'd'})); * console.log(node.nodes.length); * //=> 4 * node.pop(); * console.log(node.nodes.length); * //=> 3 * ``` * @return {Number} Returns the popped `node` * @api public */ node.pop = function() { return this.nodes && this.nodes.pop(); }; /** * Shift a node from `node.nodes`. * * ```js * var node = new Node({type: 'foo'}); * node.push(new Node({type: 'a'})); * node.push(new Node({type: 'b'})); * node.push(new Node({type: 'c'})); * node.push(new Node({type: 'd'})); * console.log(node.nodes.length); * //=> 4 * node.shift(); * console.log(node.nodes.length); * //=> 3 * ``` * @return {Object} Returns the shifted `node` * @api public */ node.shift = function() { return this.nodes && this.nodes.shift(); }; /** * Remove `node` from `node.nodes`. * * ```js * node.remove(childNode); * ``` * @param {Object} `node` * @return {Object} Returns the removed node. * @api public */ node.remove = function(node) { assert(isNode(node), 'expected node to be an instance of Node'); this.nodes = this.nodes || []; var idx = node.index; if (idx !== -1) { return this.nodes.splice(idx, 1); } return null; }; }; snapdragon-util-4.0.0/test/test.js000066400000000000000000001124351317626570000171330ustar00rootroot00000000000000'use strict'; require('mocha'); var assert = require('assert'); var Parser = require('snapdragon/lib/parser'); var Compiler = require('snapdragon/lib/compiler'); var Node = require('snapdragon-node'); var decorate = require('./support'); var utils = require('..'); var parser; var ast; describe('snapdragon-node', function() { beforeEach(function() { parser = new Parser({Node: Node}) .set('text', function() { var match = this.match(/^[a-z]+/); if (match) { return this.node(match[0]); } }) .set('slash', function() { var match = this.match(/^\//); if (match) { return this.node(match[0]); } }) .set('star', function() { var match = this.match(/^\*/); if (match) { return this.node(match[0]); } }) ast = parser.parse('a/*/c'); ast.isNode = true; }); describe('.arrayify', function() { it('should cast a string to an array', function() { assert.deepEqual(utils.arrayify('foo'), ['foo']); }); it('should return an array', function() { assert.deepEqual(utils.arrayify(['foo']), ['foo']); }); it('should return an empty array when not a string or array', function() { assert.deepEqual(utils.arrayify(), []); }); }); describe('.stringify', function() { it('should return a string', function() { assert.equal(utils.stringify('foo'), 'foo'); }); it('should stringify an array', function() { assert.equal(utils.stringify(['foo', 'bar']), 'foo,bar'); }); }); describe('.identity', function() { it('should return node.val as it was created by the parser', function() { var res = new Compiler() .set('star', utils.identity) .set('slash', utils.identity) .set('text', utils.identity) .compile(ast); assert.equal(res.output, 'a/*/c'); }); }); describe('.noop', function() { it('should make a node an empty text node', function() { var res = new Compiler() .set('star', utils.noop) .set('slash', utils.identity) .set('text', utils.identity) .compile(ast); assert.equal(res.output, 'a//c'); }); }); describe('.append', function() { it('should append the specified text', function() { var res = new Compiler() .set('star', utils.append('@')) .set('slash', utils.append('\\')) .set('text', utils.identity) .compile(ast); assert.equal(res.output, 'a\\@\\c'); }); it('should use compiler.append method when it exists', function() { var compiler = new Compiler() compiler.append = compiler.emit.bind(compiler); var res = compiler.set('star', utils.append('@')) .set('slash', utils.append('\\')) .set('text', utils.identity) .compile(ast); assert.equal(res.output, 'a\\@\\c'); }); }); describe('.toNoop', function() { it('should throw an error when node is not a node', function() { assert.throws(function() { utils.toNoop(); }); }); it('should convert a node to a noop node', function() { utils.toNoop(ast); assert(!ast.nodes); }); it('should convert a node to a noop with the given nodes value', function() { utils.toNoop(ast, []); assert.equal(ast.nodes.length, 0); }); }); describe('.visit', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.visit(); }); }); it('should throw an error when node.nodes is not an array', function() { assert.throws(function() { utils.visit(new Node({type: 'foo', val: ''})); }); }); it('should visit a node with the given function', function() { var type = null; utils.visit(ast, function(node) { if (type === null) { type = node.type; } }); assert.equal(type, 'root'); }); }); describe('.mapVisit', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.mapVisit(); }); }); it('should throw an error when node.nodes is not an array', function() { assert.throws(function() { utils.mapVisit(new Node({type: 'foo', val: ''})); }); }); it('should map "visit" over node.nodes', function() { var type = null; utils.mapVisit(ast, function(node) { if (type === null && node.parent && node.parent.type === 'root') { type = node.type; } }); assert.equal(type, 'bos'); }); }); describe('.pushNode', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.pushNode(); }); }); it('should add a node to the end of node.nodes', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); }); it('should work when node.push is not a function', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); node.pushNode = null; node.push = null; utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); }); }); describe('.unshiftNode', function() { it('should throw an error when parent is not a node', function() { assert.throws(function() { utils.unshiftNode(); }); }); it('should throw an error when node is not a node', function() { assert.throws(function() { utils.unshiftNode(new Node({type: 'foo'})); }); }); it('should add a node to the beginning of node.nodes', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); utils.unshiftNode(node, a); utils.unshiftNode(node, b); assert.equal(node.nodes[1].type, 'a'); assert.equal(node.nodes[0].type, 'b'); }); it('should work when node.unshift is not a function', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); node.unshiftNode = null; node.unshift = null; utils.unshiftNode(node, a); utils.unshiftNode(node, b); assert.equal(node.nodes[1].type, 'a'); assert.equal(node.nodes[0].type, 'b'); }); }); describe('.popNode', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.popNode(); }); }); it('should pop a node from node.nodes', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); utils.popNode(node); utils.popNode(node); assert.equal(node.nodes.length, 0); }); it('should work when node.pop is not a function', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); node.popNode = null; node.pop = null; utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); utils.popNode(node); utils.popNode(node); assert.equal(node.nodes.length, 0); }); it('should work when node.pop is a function', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); decorate(node); utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); utils.popNode(node); utils.popNode(node); assert.equal(node.nodes.length, 0); }); }); describe('.shiftNode', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.shiftNode(); }); }); it('should shift a node from node.nodes', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); utils.shiftNode(node); utils.shiftNode(node); assert.equal(node.nodes.length, 0); }); it('should work when node.shift is not a function', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); node.shiftNode = null; node.shift = null; utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); utils.shiftNode(node); utils.shiftNode(node); assert.equal(node.nodes.length, 0); }); it('should work when node.shift is a function', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); decorate(node); utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); utils.shiftNode(node); utils.shiftNode(node); assert.equal(node.nodes.length, 0); }); }); describe('.removeNode', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.removeNode(); }); }); it('should remove a node from node.nodes', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); utils.removeNode(node, a); assert.equal(node.nodes.length, 1); utils.removeNode(node, b); assert.equal(node.nodes.length, 0); }); it('should work when node.remove is not a function', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); node.removeNode = null; node.remove = null; utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); utils.removeNode(node, a); assert.equal(node.nodes.length, 1); utils.removeNode(node, b); assert.equal(node.nodes.length, 0); }); it('should work when node.remove is a function', function() { var node = new Node({type: 'brace'}); var a = new Node({type: 'a', val: 'foo'}); var b = new Node({type: 'b', val: 'foo'}); decorate(node); utils.pushNode(node, a); utils.pushNode(node, b); assert.equal(node.nodes[0].type, 'a'); assert.equal(node.nodes[1].type, 'b'); utils.removeNode(node, a); utils.removeNode(node, b); assert.equal(node.nodes.length, 0); }); it('should return when node.nodes does not exist', function() { assert.doesNotThrow(function() { var node = new Node({type: 'brace'}); utils.removeNode(node, node); }); assert.doesNotThrow(function() { var node = new Node({type: 'brace'}); node.removeNode = null; node.remove = null; utils.removeNode(node, node); }); }); it('should return when the given node is not in node.nodes', function() { assert.doesNotThrow(function() { var node = new Node({type: 'brace'}); var foo = new Node({type: 'foo'}); var bar = new Node({type: 'bar'}); utils.pushNode(node, bar); utils.removeNode(node, foo); }); assert.doesNotThrow(function() { var node = new Node({type: 'brace'}); var foo = new Node({type: 'foo'}); var bar = new Node({type: 'bar'}); node.removeNode = null; node.remove = null; utils.pushNode(node, bar); utils.removeNode(node, foo); }); }); }); describe('.addOpen', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.addOpen(); }); }); it('should add an open node', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); utils.addOpen(node, Node); assert.equal(node.nodes[0].type, 'brace.open'); }); it('should work when node.unshift is a function', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); decorate(node); utils.addOpen(node, Node); assert.equal(node.nodes[0].type, 'brace.open'); }); it('should work when node.unshift is not a function', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); node.unshiftNode = null; node.unshift = null; utils.addOpen(node, Node); assert.equal(node.nodes[0].type, 'brace.open'); }); it('should take a filter function', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); utils.addOpen(node, Node, function(node) { return node.type !== 'brace'; }); assert(!node.nodes); }); it('should use the given value on the open node', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); utils.addOpen(node, Node, '{'); assert.equal(node.nodes[0].val, '{'); }); }); describe('.addClose', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.addClose(); }); }); it('should add a close node', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); utils.pushNode(node, text); utils.addClose(node, Node); assert.equal(node.nodes[0].type, 'text'); assert.equal(node.nodes[1].type, 'brace.close'); }); it('should work when node.push is not a function', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); node.pushNode = null; node.push = null; utils.pushNode(node, text); utils.addClose(node, Node); assert.equal(node.nodes[0].type, 'text'); assert.equal(node.nodes[1].type, 'brace.close'); }); it('should work when node.push is a function', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); decorate(node); utils.pushNode(node, text); utils.addClose(node, Node); assert.equal(node.nodes[0].type, 'text'); assert.equal(node.nodes[1].type, 'brace.close'); }); it('should take a filter function', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); utils.addClose(node, Node, function(node) { return node.type !== 'brace'; }); assert(!node.nodes); }); it('should use the given value on the close node', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); utils.addClose(node, Node, '}'); assert.equal(node.nodes[0].val, '}'); }); }); describe('.wrapNodes', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.wrapNodes(); }); }); it('should add an open node', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); utils.wrapNodes(node, Node); assert.equal(node.nodes[0].type, 'brace.open'); }); it('should add a close node', function() { var node = new Node({type: 'brace'}); var text = new Node({type: 'text', val: 'foo'}); utils.pushNode(node, text); utils.wrapNodes(node, Node); assert.equal(node.nodes[0].type, 'brace.open'); assert.equal(node.nodes[1].type, 'text'); assert.equal(node.nodes[2].type, 'brace.close'); }); }); describe('.isEmpty', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.isEmpty(); }); }); it('should return true node.val is an empty string', function() { assert(utils.isEmpty(new Node({type: 'text', val: ''}))); }); it('should return true node.val is undefined', function() { assert(utils.isEmpty(new Node({type: 'text'}))); }); it('should return true when node.nodes is empty', function() { var foo = new Node({type: 'foo'}); var bar = new Node({type: 'text', val: 'bar'}); utils.pushNode(foo, bar); assert(!utils.isEmpty(foo)); utils.shiftNode(foo); assert(utils.isEmpty(foo)); }); it('should return true when node.nodes is all non-text nodes', function() { var node = new Node({type: 'parent'}); var foo = new Node({type: 'foo'}); var bar = new Node({type: 'bar'}); var baz = new Node({type: 'baz'}); utils.pushNode(node, foo); utils.pushNode(node, bar); utils.pushNode(node, baz); assert(utils.isEmpty(foo)); }); it('should return call a custom function if only one node exists', function() { var foo = new Node({type: 'foo'}); var text = new Node({type: 'text', val: ''}); utils.pushNode(foo, text); assert(utils.isEmpty(foo, function(node) { return node.type === 'text' && !node.val.trim(); })); }); it('should return true when only open and close nodes exist', function() { var brace = new Node({type: 'brace'}); var open = new Node({type: 'brace.open'}); var close = new Node({type: 'brace.close'}); utils.pushNode(brace, open); utils.pushNode(brace, close); assert(utils.isEmpty(brace)); }); it('should call a custom function on "middle" nodes (1)', function() { var brace = new Node({type: 'brace'}); var open = new Node({type: 'brace.open'}); var text = new Node({type: 'text', val: ''}); var close = new Node({type: 'brace.close'}); utils.pushNode(brace, open); utils.pushNode(brace, text); utils.pushNode(brace, text); utils.pushNode(brace, text); utils.pushNode(brace, close); assert(utils.isEmpty(brace, function(node) { if (node.type !== 'text') { return false; } return node.val.trim() === ''; })); }); it('should call a custom function on "middle" nodes (2)', function() { var brace = new Node({type: 'brace'}); var open = new Node({type: 'brace.open'}); var text = new Node({type: 'text', val: ''}); var close = new Node({type: 'brace.close'}); utils.pushNode(brace, open); utils.pushNode(brace, text); utils.pushNode(brace, text); utils.pushNode(brace, text); utils.pushNode(brace, close); assert(!utils.isEmpty(brace, function(node, parent) { return parent.nodes.length === 0; })); }); it('should call a custom function on "middle" nodes (3)', function() { var brace = new Node({type: 'brace'}); var open = new Node({type: 'brace.open'}); var text = new Node({type: 'text', val: 'foo'}); var close = new Node({type: 'brace.close'}); utils.pushNode(brace, open); utils.pushNode(brace, text); utils.pushNode(brace, close); assert(!utils.isEmpty(brace, function(node) { if (node.type !== 'text') { return false; } return node.val.trim() === ''; })); }); it('should call a custom function on "middle" nodes (4)', function() { var brace = new Node({type: 'brace'}); var open = new Node({type: 'brace.open'}); var empty = new Node({type: 'text', val: ''}); var text = new Node({type: 'text', val: 'foo'}); var close = new Node({type: 'brace.close'}); utils.pushNode(brace, open); utils.pushNode(brace, empty); utils.pushNode(brace, empty); utils.pushNode(brace, empty); utils.pushNode(brace, empty); utils.pushNode(brace, text); utils.pushNode(brace, close); assert(!utils.isEmpty(brace, function(node) { if (node.type !== 'text') { return false; } return node.val.trim() === ''; })); }); }); describe('.isType', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.isType(); }); }); it('should throw an error when matcher is invalid', function() { assert.throws(function() { utils.isType(new Node({type: 'foo'})); }); }); it('should return true if the node is the given type', function() { assert(utils.isType(ast, 'root')); assert(utils.isType(ast.last, 'eos')); }); }); describe('.isInsideType', function() { it('should throw an error when parent is not a node', function() { assert.throws(function() { utils.isInsideType(); }); }); it('should throw an error when child not a node', function() { assert.throws(function() { utils.isInsideType(new Node({type: 'foo'})); }); }); it('should return false when state.inside is not an object', function() { var state = {}; var node = new Node({type: 'brace'}); assert(!utils.isInsideType(state, 'brace')); }); it('should return false when state.inside[type] is not an object', function() { var state = {inside: {}}; var node = new Node({type: 'brace'}); assert(!utils.isInsideType(state, 'brace')); }); it('should return true when state has the given type', function() { var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(utils.isInsideType(state, 'brace')); }); it('should return false when state does not have the given type', function() { var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(utils.isInsideType(state, 'brace')); utils.removeType(state, node); assert(!utils.isInsideType(state, 'brace')); }); }); describe('.isInside', function() { it('should throw an error when parent is not a node', function() { assert.throws(function() { utils.isInside(); }); }); it('should throw an error when child not a node', function() { assert.throws(function() { utils.isInside(new Node({type: 'foo'})); }); }); it('should return false when state.inside is not an object', function() { var state = {}; var node = new Node({type: 'brace'}); assert(!utils.isInside(state, node, 'brace')); }); it('should return false when state.inside[type] is not an object', function() { var state = {inside: {}}; var node = new Node({type: 'brace'}); assert(!utils.isInside(state, node, 'brace')); }); it('should return true when state has the given type', function() { var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(utils.isInside(state, node, 'brace')); }); it('should return true when state has one of the given types', function() { var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(utils.isInside(state, node, ['foo', 'brace'])); }); it('should return false when state does not have one of the given types', function() { var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(!utils.isInside(state, node, ['foo', 'bar'])); }); it('should return true when a regex matches a type', function() { var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(utils.isInside(state, node, /(foo|brace)/)); }); it('should return true when the type matches parent.type', function() { var state = {}; var brace = new Node({type: 'brace'}); var node = new Node({type: 'brace.open'}); utils.pushNode(brace, node); assert(utils.isInside(state, node, 'brace')); }); it('should return true when regex matches parent.type', function() { var state = {}; var brace = new Node({type: 'brace'}); var node = new Node({type: 'brace.open'}); utils.pushNode(brace, node); assert(utils.isInside(state, node, /(foo|brace)/)); }); it('should return false when a regex does not match a type', function() { var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(!utils.isInside(state, node, /(foo|bar)/)); }); it('should return false when type is invalie', function() { var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(!utils.isInside(state, node, null)); }); it('should return false when state does not have the given type', function() { var state = { inside: {}}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(utils.isInside(state, node, 'brace')); utils.removeType(state, node); assert(!utils.isInside(state, node, 'brace')); }); }); describe('.hasType', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.hasType(); }); }); it('should return true if node.nodes has the given type', function() { assert(utils.hasType(ast, 'text')); assert(!utils.hasType(ast, 'foo')); }); it('should return false when node.nodes does not exist', function() { assert(!utils.hasType(new Node({type: 'foo'}))); }); }); describe('.firstOfType', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.firstOfType(); }); }); it('should get the first node of the given type', function() { var node = utils.firstOfType(ast.nodes, 'text'); assert.equal(node.type, 'text'); }); }); describe('.last', function() { it('should get the last node', function() { assert.equal(utils.last(ast.nodes).type, 'eos'); }); }); describe('.findNode', function() { it('should get the node with the given type', function() { var text = utils.findNode(ast.nodes, 'text'); assert.equal(text.type, 'text'); }); it('should get the node matching the given regex', function() { var text = utils.findNode(ast.nodes, /text/); assert.equal(text.type, 'text'); }); it('should get the first matching node', function() { var node = utils.findNode(ast.nodes, [/text/, 'bos']); assert.equal(node.type, 'bos'); node = utils.findNode(ast.nodes, [/text/]); assert.equal(node.type, 'text'); }); it('should get the node at the given index', function() { var bos = utils.findNode(ast.nodes, 0); assert.equal(bos.type, 'bos'); var text = utils.findNode(ast.nodes, 1); assert.equal(text.type, 'text'); }); it('should return null when node does not exist', function() { assert.equal(utils.findNode(new Node({type: 'foo'})), null); }); }); describe('.removeNode', function() { it('should throw an error when parent is not a node', function() { assert.throws(function() { utils.removeNode(); }); }); it('should throw an error when child is not a node', function() { assert.throws(function() { utils.removeNode(new Node({type: 'foo'})); }); }); it('should remove a node from parent.nodes', function() { var brace = new Node({type: 'brace'}); var open = new Node({type: 'brace.open'}); var foo = new Node({type: 'foo'}); var bar = new Node({type: 'bar'}); var baz = new Node({type: 'baz'}); var qux = new Node({type: 'qux'}); var close = new Node({type: 'brace.close'}); utils.pushNode(brace, open); utils.pushNode(brace, foo); utils.pushNode(brace, bar); utils.pushNode(brace, baz); utils.pushNode(brace, qux); utils.pushNode(brace, close); assert.equal(brace.nodes.length, 6); assert.equal(brace.nodes[0].type, 'brace.open'); assert.equal(brace.nodes[1].type, 'foo'); assert.equal(brace.nodes[2].type, 'bar'); assert.equal(brace.nodes[3].type, 'baz'); assert.equal(brace.nodes[4].type, 'qux'); assert.equal(brace.nodes[5].type, 'brace.close'); // remove node utils.removeNode(brace, bar); assert.equal(brace.nodes.length, 5); assert.equal(brace.nodes[0].type, 'brace.open'); assert.equal(brace.nodes[1].type, 'foo'); assert.equal(brace.nodes[2].type, 'baz'); assert.equal(brace.nodes[3].type, 'qux'); assert.equal(brace.nodes[4].type, 'brace.close'); }); }); describe('.isOpen', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.isOpen(); }); }); it('should be true if node is an ".open" node', function() { var node = new Node({type: 'foo.open'}); assert(utils.isOpen(node)); }); it('should be false if node is not an ".open" node', function() { var node = new Node({type: 'foo'}); assert(!utils.isOpen(node)); }); }); describe('.isClose', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.isClose(); }); }); it('should be true if node is a ".close" node', function() { var node = new Node({type: 'foo.close'}); assert(utils.isClose(node)); }); it('should be false if node is not a ".close" node', function() { var node = new Node({type: 'foo'}); assert(!utils.isClose(node)); }); }); describe('.hasOpen', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.hasOpen(); }); }); it('should be true if node has an ".open" node', function() { var parent = new Node({type: 'foo'}); var node = new Node({type: 'foo.open'}); utils.pushNode(parent, node); assert(utils.hasOpen(parent)); }); it('should be false if does not have an ".open" node', function() { var parent = new Node({type: 'foo'}); assert(!utils.hasOpen(parent)); }); }); describe('.hasClose', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.hasClose(); }); }); it('should be true if node has a ".close" node', function() { var parent = new Node({type: 'foo'}); var open = new Node({type: 'foo.open'}); var close = new Node({type: 'foo.close'}); utils.pushNode(parent, open); utils.pushNode(parent, close); assert(utils.hasClose(parent)); }); it('should be false if does not have a ".close" node', function() { var parent = new Node({type: 'foo'}); assert(!utils.hasClose(parent)); }); }); describe('.hasOpenAndClose', function() { it('should throw an error when not a node', function() { assert.throws(function() { utils.hasOpenAndClose(); }); }); it('should be true if node has ".open" and ".close" nodes', function() { var parent = new Node({type: 'foo'}); var open = new Node({type: 'foo.open'}); var close = new Node({type: 'foo.close'}); utils.pushNode(parent, open); utils.pushNode(parent, close); assert(utils.hasOpenAndClose(parent)); }); it('should be false if does not have a ".close" node', function() { var parent = new Node({type: 'foo'}); var open = new Node({type: 'foo.open'}); utils.pushNode(parent, open); assert(!utils.hasOpenAndClose(parent)); }); it('should be false if does not have an ".open" node', function() { var parent = new Node({type: 'foo'}); var close = new Node({type: 'foo.close'}); utils.pushNode(parent, close); assert(!utils.hasOpenAndClose(parent)); }); }); describe('.pushNode', function() { it('should throw an error when parent is not a node', function() { assert.throws(function() { utils.pushNode(); }); }); it('should throw an error when child is not a node', function() { assert.throws(function() { utils.pushNode(new Node({type: 'foo'})); }); }); it('should add a node to `node.nodes`', function() { var node = new Node({type: 'foo'}); utils.pushNode(ast, node); assert.equal(ast.last.type, 'foo'); }); it('should set the parent on the given node', function() { var node = new Node({type: 'foo'}); utils.pushNode(ast, node); assert.equal(node.parent.type, 'root'); }); it('should set the parent.nodes as `.siblings` on the given node', function() { var node = new Node({type: 'foo'}); assert.equal(node.siblings, null); utils.pushNode(ast, node); assert.equal(node.siblings.length, 8); }); }); describe('.addType', function() { it('should throw an error when state is not given', function() { assert.throws(function() { utils.addType(); }); }); it('should throw an error when a node is not passed', function() { assert.throws(function() { utils.addType({}); }); }); it('should add the type to the state.inside array', function() { var state = {}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(state.inside); assert(state.inside.brace); assert.equal(state.inside.brace.length, 1); }); it('should add the type based on parent.type', function() { var state = {}; var parent = new Node({type: 'brace'}); var node = new Node({type: 'brace.open'}); utils.pushNode(parent, node); utils.addType(state, node); assert(state.inside); assert(state.inside.brace); assert.equal(state.inside.brace.length, 1); }); }); describe('.removeType', function() { it('should throw an error when state is not given', function() { assert.throws(function() { utils.removeType(); }); }); it('should throw an error when a node is not passed', function() { assert.throws(function() { utils.removeType({}); }); }); it('should add a state.inside object', function() { var state = {}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(state.inside); }); it('should add a type array to the state.inside object', function() { var state = {}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(state.inside); assert(Array.isArray(state.inside.brace)); }); it('should add the node to the state.inside type array', function() { var state = {}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(state.inside); assert(state.inside.brace); assert.equal(state.inside.brace.length, 1); utils.removeType(state, node); assert.equal(state.inside.brace.length, 0); }); it('should use a type array if it already exists', function() { var state = { inside: { brace: [new Node({type: 'brace.open'})] }}; var node = new Node({type: 'brace'}); utils.addType(state, node); assert(state.inside); assert(state.inside.brace); assert.equal(state.inside.brace.length, 2); utils.removeType(state, node); assert.equal(state.inside.brace.length, 1); }); it('should remove the type based on parent.type', function() { var state = { inside: { brace: [new Node({type: 'brace.open'})] }}; var parent = new Node({type: 'brace'}); var node = new Node({type: 'brace.open'}); utils.pushNode(parent, node); utils.addType(state, node); assert(state.inside); assert(state.inside.brace); assert.equal(state.inside.brace.length, 2); utils.removeType(state, node); assert.equal(state.inside.brace.length, 1); }); it('should throw an error when state.inside does not exist', function() { var state = {}; var node = new Node({type: 'brace'}); assert.throws(function() { utils.removeType(state, node); }); }); it('should just return when state.inside type does not exist', function() { var state = {inside: {}}; var node = new Node({type: 'brace'}); utils.removeType(state, node); }); }); });