New upstream version 5.0.1
Julien Puydt
5 years ago
0 | # http://editorconfig.org/ | |
1 | root = true | |
2 | ||
3 | [*] | |
4 | charset = utf-8 | |
5 | end_of_line = lf | |
6 | indent_size = 2 | |
7 | indent_style = space | |
8 | insert_final_newline = true | |
9 | trim_trailing_whitespace = true | |
10 | ||
11 | [{**/{actual,fixtures,expected,templates}/**,*.md}] | |
12 | trim_trailing_whitespace = false | |
13 | insert_final_newline = false |
0 | { | |
1 | "env": { | |
2 | "browser": false, | |
3 | "es6": true, | |
4 | "node": true, | |
5 | "mocha": true | |
6 | }, | |
7 | ||
8 | "globals": { | |
9 | "document": false, | |
10 | "navigator": false, | |
11 | "window": false | |
12 | }, | |
13 | ||
14 | "rules": { | |
15 | "accessor-pairs": 2, | |
16 | "arrow-spacing": [2, { "before": true, "after": true }], | |
17 | "block-spacing": [2, "always"], | |
18 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], | |
19 | "comma-dangle": [2, "never"], | |
20 | "comma-spacing": [2, { "before": false, "after": true }], | |
21 | "comma-style": [2, "last"], | |
22 | "constructor-super": 2, | |
23 | "curly": [2, "multi-line"], | |
24 | "dot-location": [2, "property"], | |
25 | "eol-last": 2, | |
26 | "eqeqeq": [2, "allow-null"], | |
27 | "generator-star-spacing": [2, { "before": true, "after": true }], | |
28 | "handle-callback-err": [2, "^(err|error)$" ], | |
29 | "indent": [2, 2, { "SwitchCase": 1 }], | |
30 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], | |
31 | "keyword-spacing": [2, { "before": true, "after": true }], | |
32 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], | |
33 | "new-parens": 2, | |
34 | "no-array-constructor": 2, | |
35 | "no-caller": 2, | |
36 | "no-class-assign": 2, | |
37 | "no-cond-assign": 2, | |
38 | "no-const-assign": 2, | |
39 | "no-control-regex": 2, | |
40 | "no-debugger": 2, | |
41 | "no-delete-var": 2, | |
42 | "no-dupe-args": 2, | |
43 | "no-dupe-class-members": 2, | |
44 | "no-dupe-keys": 2, | |
45 | "no-duplicate-case": 2, | |
46 | "no-empty-character-class": 2, | |
47 | "no-eval": 2, | |
48 | "no-ex-assign": 2, | |
49 | "no-extend-native": 2, | |
50 | "no-extra-bind": 2, | |
51 | "no-extra-boolean-cast": 2, | |
52 | "no-extra-parens": [2, "functions"], | |
53 | "no-fallthrough": 2, | |
54 | "no-floating-decimal": 2, | |
55 | "no-func-assign": 2, | |
56 | "no-implied-eval": 2, | |
57 | "no-inner-declarations": [2, "functions"], | |
58 | "no-invalid-regexp": 2, | |
59 | "no-irregular-whitespace": 2, | |
60 | "no-iterator": 2, | |
61 | "no-label-var": 2, | |
62 | "no-labels": 2, | |
63 | "no-lone-blocks": 2, | |
64 | "no-mixed-spaces-and-tabs": 2, | |
65 | "no-multi-spaces": 2, | |
66 | "no-multi-str": 2, | |
67 | "no-multiple-empty-lines": [2, { "max": 1 }], | |
68 | "no-native-reassign": 0, | |
69 | "no-negated-in-lhs": 2, | |
70 | "no-new": 2, | |
71 | "no-new-func": 2, | |
72 | "no-new-object": 2, | |
73 | "no-new-require": 2, | |
74 | "no-new-wrappers": 2, | |
75 | "no-obj-calls": 2, | |
76 | "no-octal": 2, | |
77 | "no-octal-escape": 2, | |
78 | "no-proto": 0, | |
79 | "no-redeclare": 2, | |
80 | "no-regex-spaces": 2, | |
81 | "no-return-assign": 2, | |
82 | "no-self-compare": 2, | |
83 | "no-sequences": 2, | |
84 | "no-shadow-restricted-names": 2, | |
85 | "no-spaced-func": 2, | |
86 | "no-sparse-arrays": 2, | |
87 | "no-this-before-super": 2, | |
88 | "no-throw-literal": 2, | |
89 | "no-trailing-spaces": 0, | |
90 | "no-undef": 2, | |
91 | "no-undef-init": 2, | |
92 | "no-unexpected-multiline": 2, | |
93 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], | |
94 | "no-unreachable": 2, | |
95 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], | |
96 | "no-useless-call": 0, | |
97 | "no-with": 2, | |
98 | "one-var": [0, { "initialized": "never" }], | |
99 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], | |
100 | "padded-blocks": [0, "never"], | |
101 | "quotes": [2, "single", "avoid-escape"], | |
102 | "radix": 2, | |
103 | "semi": [2, "always"], | |
104 | "semi-spacing": [2, { "before": false, "after": true }], | |
105 | "space-before-blocks": [2, "always"], | |
106 | "space-before-function-paren": [2, "never"], | |
107 | "space-in-parens": [2, "never"], | |
108 | "space-infix-ops": 2, | |
109 | "space-unary-ops": [2, { "words": true, "nonwords": false }], | |
110 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], | |
111 | "use-isnan": 2, | |
112 | "valid-typeof": 2, | |
113 | "wrap-iife": [2, "any"], | |
114 | "yoda": [2, "never"] | |
115 | } | |
116 | } |
0 | # Enforce Unix newlines | |
1 | * text eol=lf | |
2 | ||
3 | # binaries | |
4 | *.ai binary | |
5 | *.psd binary | |
6 | *.jpg binary | |
7 | *.gif binary | |
8 | *.png binary | |
9 | *.jpeg binary |
0 | # Contributing to snapdragon-util | |
1 | ||
2 | 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. | |
3 | ||
4 | **What does "contributing" mean?** | |
5 | ||
6 | Creating an issue is the simplest form of contributing to a project. But there are many ways to contribute, including the following: | |
7 | ||
8 | - Updating or correcting documentation | |
9 | - Feature requests | |
10 | - Bug reports | |
11 | ||
12 | 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. | |
13 | ||
14 | **Showing support for snapdragon-util** | |
15 | ||
16 | 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. | |
17 | ||
18 | Don't have time to contribute? No worries, here are some other ways to show your support for snapdragon-util: | |
19 | ||
20 | - star the [project](https://github.com/here-be/snapdragon-util) | |
21 | - tweet your support for snapdragon-util | |
22 | ||
23 | ## Issues | |
24 | ||
25 | ### Before creating an issue | |
26 | ||
27 | 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. | |
28 | ||
29 | Try to follow these guidelines | |
30 | ||
31 | - **Investigate the issue**: | |
32 | - **Check the readme** - oftentimes you will find notes about creating issues, and where to go depending on the type of issue. | |
33 | - Create the issue in the appropriate repository. | |
34 | ||
35 | ### Creating an issue | |
36 | ||
37 | 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: | |
38 | ||
39 | - **version**: please note the version of snapdragon-util are you using | |
40 | - **extensions, plugins, helpers, etc** (if applicable): please list any extensions you're using | |
41 | - **error messages**: please paste any error messages into the issue, or a [gist](https://gist.github.com/) | |
42 | ||
43 | ## Above and beyond | |
44 | ||
45 | 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. | |
46 | ||
47 | - read the [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing) | |
48 | - 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/). | |
49 | - 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/). | |
50 | - use backticks to wrap code. This ensures that code will retain its format, making it much more readable to others | |
51 | - use syntax highlighting by adding the correct language name after the first "code fence" | |
52 | ||
53 | ||
54 | [node-glob]: https://github.com/isaacs/node-glob | |
55 | [micromatch]: https://github.com/jonschlinkert/micromatch | |
56 | [so]: http://stackoverflow.com/questions/tagged/snapdragon-util⏎ |
0 | # always ignore files | |
1 | *.DS_Store | |
2 | .idea | |
3 | .vscode | |
4 | *.sublime-* | |
5 | ||
6 | # test related, or directories generated by tests | |
7 | test/actual | |
8 | actual | |
9 | coverage | |
10 | .nyc* | |
11 | ||
12 | # npm | |
13 | node_modules | |
14 | npm-debug.log | |
15 | ||
16 | # yarn | |
17 | yarn.lock | |
18 | yarn-error.log | |
19 | ||
20 | # misc | |
21 | _gh_pages | |
22 | _draft | |
23 | _drafts | |
24 | bower_components | |
25 | vendor | |
26 | temp | |
27 | tmp | |
28 | TODO.md | |
29 | package-lock.json⏎ |
0 | sudo: false | |
1 | os: | |
2 | - linux | |
3 | - osx | |
4 | language: node_js | |
5 | node_js: | |
6 | - node | |
7 | - '9' | |
8 | - '8' | |
9 | - '7' | |
10 | - '6' |
0 | ## Usage | |
1 | ||
2 | ```js | |
3 | var util = require('snapdragon-util'); | |
4 | ``` | |
5 | ||
6 | ## API | |
7 | {%= apidocs("index.js") %} |
0 | # Release history | |
1 | ||
2 | All notable changes to this project will be documented in this file. | |
3 | ||
4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) | |
5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). | |
6 | ||
7 | <details> | |
8 | <summary><strong>Guiding Principles</strong></summary> | |
9 | ||
10 | - Changelogs are for humans, not machines. | |
11 | - There should be an entry for every single version. | |
12 | - The same types of changes should be grouped. | |
13 | - Versions and sections should be linkable. | |
14 | - The latest version comes first. | |
15 | - The release date of each versions is displayed. | |
16 | - Mention whether you follow Semantic Versioning. | |
17 | ||
18 | </details> | |
19 | ||
20 | <details> | |
21 | <summary><strong>Types of changes</strong></summary> | |
22 | ||
23 | Changelog entries are classified using the following labels _(from [keep-a-changelog](http://keepachangelog.com/)_): | |
24 | ||
25 | * `added`: for new features | |
26 | * `changed`: for changes in existing functionality | |
27 | * `deprecated`: for once-stable features removed in upcoming releases | |
28 | * `removed`: for deprecated features removed in this release | |
29 | * `fixed`: for any bug fixes | |
30 | ||
31 | Custom labels used in this repository: | |
32 | ||
33 | * `dependencies`: bumps dependencies | |
34 | * `housekeeping`: code re-organization, minor edits, or other changes that don't fit in one of the other categories. | |
35 | ||
36 | </details> | |
37 | ||
38 | ||
39 | ### [5.0.0] - 2018-01-11 | |
40 | ||
41 | **Changes** | |
42 | ||
43 | - Adds support for `node.value`, in anticipation of snapdragon v1.0.0. | |
44 | ||
45 | ||
46 | ### [4.0.0] - 2017-11-01 | |
47 | ||
48 | **Dependencies** | |
49 | ||
50 | - Updated `kind-of` to version 6.0 | |
51 | ||
52 | ### [3.0.0] - 2017-05-01 | |
53 | ||
54 | **Changed** | |
55 | ||
56 | - `.emit` was renamed to [.append](README.md#append) | |
57 | - `.addNode` was renamed to [.pushNode](README.md#pushNode) | |
58 | - `.getNode` was renamed to [.findNode](README.md#findNode) | |
59 | - `.isEmptyNodes` was renamed to [.isEmpty](README.md#isEmpty): also now works with `node.nodes` and/or `node.val` | |
60 | ||
61 | **Added** | |
62 | ||
63 | - [.identity](README.md#identity) | |
64 | - [.removeNode](README.md#removeNode) | |
65 | - [.shiftNode](README.md#shiftNode) | |
66 | - [.popNode](README.md#popNode) | |
67 | ||
68 | ### 0.1.0 | |
69 | ||
70 | First release. | |
71 | ||
72 | [keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog | |
73 | ||
74 | [5.0.0]: https://github.com/here-be/snapdragon-util/compare/4.0.0...5.0.0 | |
75 | [4.0.0]: https://github.com/here-be/snapdragon-util/compare/4.0.0...3.0.0 | |
76 | [3.0.1]: https://github.com/here-be/snapdragon-util/compare/3.0.0...3.0.1 | |
77 | [3.0.0]: https://github.com/here-be/snapdragon-util/compare/2.1.1...3.0.0 | |
78 | ||
79 | [Unreleased]: https://github.com/here-be/snapdragon-util/compare/0.1.1...HEAD | |
80 | [keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog | |
81 |
0 | The MIT License (MIT) | |
1 | ||
2 | Copyright (c) 2017-2018, Jon Schlinkert. | |
3 | ||
4 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | of this software and associated documentation files (the "Software"), to deal | |
6 | in the Software without restriction, including without limitation the rights | |
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | copies of the Software, and to permit persons to whom the Software is | |
9 | furnished to do so, subject to the following conditions: | |
10 | ||
11 | The above copyright notice and this permission notice shall be included in | |
12 | all copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | THE SOFTWARE. |
0 | # 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/here-be/snapdragon-util.svg?style=flat&label=Travis)](https://travis-ci.org/here-be/snapdragon-util) | |
1 | ||
2 | > Utilities for the snapdragon parser/compiler. | |
3 | ||
4 | Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support. | |
5 | ||
6 | ## Install | |
7 | ||
8 | Install with [npm](https://www.npmjs.com/): | |
9 | ||
10 | ```sh | |
11 | $ npm install --save snapdragon-util | |
12 | ``` | |
13 | ||
14 | ## Usage | |
15 | ||
16 | ```js | |
17 | var util = require('snapdragon-util'); | |
18 | ``` | |
19 | ||
20 | ## API | |
21 | ||
22 | ### [.isNode](index.js#L20) | |
23 | ||
24 | Returns true if the given value is a node. | |
25 | ||
26 | **Params** | |
27 | ||
28 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
29 | * `returns` **{Boolean}** | |
30 | ||
31 | **Example** | |
32 | ||
33 | ```js | |
34 | var Node = require('snapdragon-node'); | |
35 | var node = new Node({type: 'foo'}); | |
36 | console.log(utils.isNode(node)); //=> true | |
37 | console.log(utils.isNode({})); //=> false | |
38 | ``` | |
39 | ||
40 | ### [.noop](index.js#L36) | |
41 | ||
42 | Emit an empty string for the given `node`. | |
43 | ||
44 | **Params** | |
45 | ||
46 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
47 | * `returns` **{undefined}** | |
48 | ||
49 | **Example** | |
50 | ||
51 | ```js | |
52 | // do nothing for beginning-of-string | |
53 | snapdragon.compiler.set('bos', utils.noop); | |
54 | ``` | |
55 | ||
56 | ### [.value](index.js#L54) | |
57 | ||
58 | Returns `node.value` or `node.val`. | |
59 | ||
60 | **Params** | |
61 | ||
62 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
63 | * `returns` **{String}**: returns | |
64 | ||
65 | **Example** | |
66 | ||
67 | ```js | |
68 | const star = new Node({type: 'star', value: '*'}); | |
69 | const slash = new Node({type: 'slash', val: '/'}); | |
70 | console.log(utils.value(star)) //=> '*' | |
71 | console.log(utils.value(slash)) //=> '/' | |
72 | ``` | |
73 | ||
74 | ### [.identity](index.js#L72) | |
75 | ||
76 | Append `node.value` to `compiler.output`. | |
77 | ||
78 | **Params** | |
79 | ||
80 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
81 | * `returns` **{undefined}** | |
82 | ||
83 | **Example** | |
84 | ||
85 | ```js | |
86 | snapdragon.compiler.set('text', utils.identity); | |
87 | ``` | |
88 | ||
89 | ### [.append](index.js#L95) | |
90 | ||
91 | Previously named `.emit`, this method appends the given `value` to `compiler.output` for the given node. Useful when you know what value should be appended advance, regardless of the actual value of `node.value`. | |
92 | ||
93 | **Params** | |
94 | ||
95 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
96 | * `returns` **{Function}**: Returns a compiler middleware function. | |
97 | ||
98 | **Example** | |
99 | ||
100 | ```js | |
101 | snapdragon.compiler | |
102 | .set('i', function(node) { | |
103 | this.mapVisit(node); | |
104 | }) | |
105 | .set('i.open', utils.append('<i>')) | |
106 | .set('i.close', utils.append('</i>')) | |
107 | ``` | |
108 | ||
109 | ### [.toNoop](index.js#L118) | |
110 | ||
111 | 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. | |
112 | ||
113 | **Params** | |
114 | ||
115 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
116 | * `nodes` **{Array}**: Optionally pass a new `nodes` value, to replace the existing `node.nodes` array. | |
117 | ||
118 | **Example** | |
119 | ||
120 | ```js | |
121 | utils.toNoop(node); | |
122 | // convert `node.nodes` to the given value instead of deleting it | |
123 | utils.toNoop(node, []); | |
124 | ``` | |
125 | ||
126 | ### [.visit](index.js#L147) | |
127 | ||
128 | 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. | |
129 | ||
130 | **Params** | |
131 | ||
132 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
133 | * `fn` **{Function}** | |
134 | * `returns` **{Object}**: returns the node after recursively visiting all child nodes. | |
135 | ||
136 | **Example** | |
137 | ||
138 | ```js | |
139 | snapdragon.compiler.set('i', function(node) { | |
140 | utils.visit(node, function(childNode) { | |
141 | // do stuff with "childNode" | |
142 | return childNode; | |
143 | }); | |
144 | }); | |
145 | ``` | |
146 | ||
147 | ### [.mapVisit](index.js#L174) | |
148 | ||
149 | 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. | |
150 | ||
151 | **Params** | |
152 | ||
153 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
154 | * `options` **{Object}** | |
155 | * `fn` **{Function}** | |
156 | * `returns` **{Object}**: returns the node | |
157 | ||
158 | **Example** | |
159 | ||
160 | ```js | |
161 | snapdragon.compiler.set('i', function(node) { | |
162 | utils.mapVisit(node, function(childNode) { | |
163 | // do stuff with "childNode" | |
164 | return childNode; | |
165 | }); | |
166 | }); | |
167 | ``` | |
168 | ||
169 | ### [.addOpen](index.js#L213) | |
170 | ||
171 | Unshift an `*.open` node onto `node.nodes`. | |
172 | ||
173 | **Params** | |
174 | ||
175 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
176 | * `Node` **{Function}**: (required) Node constructor function from [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node). | |
177 | * `filter` **{Function}**: Optionaly specify a filter function to exclude the node. | |
178 | * `returns` **{Object}**: Returns the created opening node. | |
179 | ||
180 | **Example** | |
181 | ||
182 | ```js | |
183 | var Node = require('snapdragon-node'); | |
184 | snapdragon.parser.set('brace', function(node) { | |
185 | var match = this.match(/^{/); | |
186 | if (match) { | |
187 | var parent = new Node({type: 'brace'}); | |
188 | utils.addOpen(parent, Node); | |
189 | console.log(parent.nodes[0]): | |
190 | // { type: 'brace.open', value: '' }; | |
191 | ||
192 | // push the parent "brace" node onto the stack | |
193 | this.push(parent); | |
194 | ||
195 | // return the parent node, so it's also added to the AST | |
196 | return brace; | |
197 | } | |
198 | }); | |
199 | ``` | |
200 | ||
201 | ### [.addClose](index.js#L263) | |
202 | ||
203 | Push a `*.close` node onto `node.nodes`. | |
204 | ||
205 | **Params** | |
206 | ||
207 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
208 | * `Node` **{Function}**: (required) Node constructor function from [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node). | |
209 | * `filter` **{Function}**: Optionaly specify a filter function to exclude the node. | |
210 | * `returns` **{Object}**: Returns the created closing node. | |
211 | ||
212 | **Example** | |
213 | ||
214 | ```js | |
215 | var Node = require('snapdragon-node'); | |
216 | snapdragon.parser.set('brace', function(node) { | |
217 | var match = this.match(/^}/); | |
218 | if (match) { | |
219 | var parent = this.parent(); | |
220 | if (parent.type !== 'brace') { | |
221 | throw new Error('missing opening: ' + '}'); | |
222 | } | |
223 | ||
224 | utils.addClose(parent, Node); | |
225 | console.log(parent.nodes[parent.nodes.length - 1]): | |
226 | // { type: 'brace.close', value: '' }; | |
227 | ||
228 | // no need to return a node, since the parent | |
229 | // was already added to the AST | |
230 | return; | |
231 | } | |
232 | }); | |
233 | ``` | |
234 | ||
235 | ### [.wrapNodes](index.js#L293) | |
236 | ||
237 | Wraps the given `node` with `*.open` and `*.close` nodes. | |
238 | ||
239 | **Params** | |
240 | ||
241 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
242 | * `Node` **{Function}**: (required) Node constructor function from [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node). | |
243 | * `filter` **{Function}**: Optionaly specify a filter function to exclude the node. | |
244 | * `returns` **{Object}**: Returns the node | |
245 | ||
246 | ### [.pushNode](index.js#L318) | |
247 | ||
248 | Push the given `node` onto `parent.nodes`, and set `parent` as `node.parent. | |
249 | ||
250 | **Params** | |
251 | ||
252 | * `parent` **{Object}** | |
253 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
254 | * `returns` **{Object}**: Returns the child node | |
255 | ||
256 | **Example** | |
257 | ||
258 | ```js | |
259 | var parent = new Node({type: 'foo'}); | |
260 | var node = new Node({type: 'bar'}); | |
261 | utils.pushNode(parent, node); | |
262 | console.log(parent.nodes[0].type) // 'bar' | |
263 | console.log(node.parent.type) // 'foo' | |
264 | ``` | |
265 | ||
266 | ### [.unshiftNode](index.js#L348) | |
267 | ||
268 | Unshift `node` onto `parent.nodes`, and set `parent` as `node.parent. | |
269 | ||
270 | **Params** | |
271 | ||
272 | * `parent` **{Object}** | |
273 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
274 | * `returns` **{undefined}** | |
275 | ||
276 | **Example** | |
277 | ||
278 | ```js | |
279 | var parent = new Node({type: 'foo'}); | |
280 | var node = new Node({type: 'bar'}); | |
281 | utils.unshiftNode(parent, node); | |
282 | console.log(parent.nodes[0].type) // 'bar' | |
283 | console.log(node.parent.type) // 'foo' | |
284 | ``` | |
285 | ||
286 | ### [.popNode](index.js#L381) | |
287 | ||
288 | 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`. | |
289 | ||
290 | **Params** | |
291 | ||
292 | * `parent` **{Object}** | |
293 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
294 | * `returns` **{Number|Undefined}**: Returns the length of `node.nodes` or undefined. | |
295 | ||
296 | **Example** | |
297 | ||
298 | ```js | |
299 | var parent = new Node({type: 'foo'}); | |
300 | utils.pushNode(parent, new Node({type: 'foo'})); | |
301 | utils.pushNode(parent, new Node({type: 'bar'})); | |
302 | utils.pushNode(parent, new Node({type: 'baz'})); | |
303 | console.log(parent.nodes.length); //=> 3 | |
304 | utils.popNode(parent); | |
305 | console.log(parent.nodes.length); //=> 2 | |
306 | ``` | |
307 | ||
308 | ### [.shiftNode](index.js#L409) | |
309 | ||
310 | 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`. | |
311 | ||
312 | **Params** | |
313 | ||
314 | * `parent` **{Object}** | |
315 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
316 | * `returns` **{Number|Undefined}**: Returns the length of `node.nodes` or undefined. | |
317 | ||
318 | **Example** | |
319 | ||
320 | ```js | |
321 | var parent = new Node({type: 'foo'}); | |
322 | utils.pushNode(parent, new Node({type: 'foo'})); | |
323 | utils.pushNode(parent, new Node({type: 'bar'})); | |
324 | utils.pushNode(parent, new Node({type: 'baz'})); | |
325 | console.log(parent.nodes.length); //=> 3 | |
326 | utils.shiftNode(parent); | |
327 | console.log(parent.nodes.length); //=> 2 | |
328 | ``` | |
329 | ||
330 | ### [.removeNode](index.js#L436) | |
331 | ||
332 | Remove the specified `node` from `parent.nodes`. | |
333 | ||
334 | **Params** | |
335 | ||
336 | * `parent` **{Object}** | |
337 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
338 | * `returns` **{Object|undefined}**: Returns the removed node, if successful, or undefined if it does not exist on `parent.nodes`. | |
339 | ||
340 | **Example** | |
341 | ||
342 | ```js | |
343 | var parent = new Node({type: 'abc'}); | |
344 | var foo = new Node({type: 'foo'}); | |
345 | utils.pushNode(parent, foo); | |
346 | utils.pushNode(parent, new Node({type: 'bar'})); | |
347 | utils.pushNode(parent, new Node({type: 'baz'})); | |
348 | console.log(parent.nodes.length); //=> 3 | |
349 | utils.removeNode(parent, foo); | |
350 | console.log(parent.nodes.length); //=> 2 | |
351 | ``` | |
352 | ||
353 | ### [.isType](index.js#L467) | |
354 | ||
355 | Returns true if `node.type` matches the given `type`. Throws a `TypeError` if `node` is not an instance of `Node`. | |
356 | ||
357 | **Params** | |
358 | ||
359 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
360 | * `type` **{String}** | |
361 | * `returns` **{Boolean}** | |
362 | ||
363 | **Example** | |
364 | ||
365 | ```js | |
366 | var Node = require('snapdragon-node'); | |
367 | var node = new Node({type: 'foo'}); | |
368 | console.log(utils.isType(node, 'foo')); // false | |
369 | console.log(utils.isType(node, 'bar')); // true | |
370 | ``` | |
371 | ||
372 | ### [.hasType](index.js#L509) | |
373 | ||
374 | Returns true if the given `node` has the given `type` in `node.nodes`. Throws a `TypeError` if `node` is not an instance of `Node`. | |
375 | ||
376 | **Params** | |
377 | ||
378 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
379 | * `type` **{String}** | |
380 | * `returns` **{Boolean}** | |
381 | ||
382 | **Example** | |
383 | ||
384 | ```js | |
385 | var Node = require('snapdragon-node'); | |
386 | var node = new Node({ | |
387 | type: 'foo', | |
388 | nodes: [ | |
389 | new Node({type: 'bar'}), | |
390 | new Node({type: 'baz'}) | |
391 | ] | |
392 | }); | |
393 | console.log(utils.hasType(node, 'xyz')); // false | |
394 | console.log(utils.hasType(node, 'baz')); // true | |
395 | ``` | |
396 | ||
397 | ### [.firstOfType](index.js#L542) | |
398 | ||
399 | Returns the first node from `node.nodes` of the given `type` | |
400 | ||
401 | **Params** | |
402 | ||
403 | * `nodes` **{Array}** | |
404 | * `type` **{String}** | |
405 | * `returns` **{Object|undefined}**: Returns the first matching node or undefined. | |
406 | ||
407 | **Example** | |
408 | ||
409 | ```js | |
410 | var node = new Node({ | |
411 | type: 'foo', | |
412 | nodes: [ | |
413 | new Node({type: 'text', value: 'abc'}), | |
414 | new Node({type: 'text', value: 'xyz'}) | |
415 | ] | |
416 | }); | |
417 | ||
418 | var textNode = utils.firstOfType(node.nodes, 'text'); | |
419 | console.log(textNode.value); | |
420 | //=> 'abc' | |
421 | ``` | |
422 | ||
423 | ### [.findNode](index.js#L578) | |
424 | ||
425 | Returns the node at the specified index, or the first node of the given `type` from `node.nodes`. | |
426 | ||
427 | **Params** | |
428 | ||
429 | * `nodes` **{Array}** | |
430 | * `type` **{String|Number}**: Node type or index. | |
431 | * `returns` **{Object}**: Returns a node or undefined. | |
432 | ||
433 | **Example** | |
434 | ||
435 | ```js | |
436 | var node = new Node({ | |
437 | type: 'foo', | |
438 | nodes: [ | |
439 | new Node({type: 'text', value: 'abc'}), | |
440 | new Node({type: 'text', value: 'xyz'}) | |
441 | ] | |
442 | }); | |
443 | ||
444 | var nodeOne = utils.findNode(node.nodes, 'text'); | |
445 | console.log(nodeOne.value); | |
446 | //=> 'abc' | |
447 | ||
448 | var nodeTwo = utils.findNode(node.nodes, 1); | |
449 | console.log(nodeTwo.value); | |
450 | //=> 'xyz' | |
451 | ``` | |
452 | ||
453 | ### [.isOpen](index.js#L602) | |
454 | ||
455 | Returns true if the given node is an "*.open" node. | |
456 | ||
457 | **Params** | |
458 | ||
459 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
460 | * `returns` **{Boolean}** | |
461 | ||
462 | **Example** | |
463 | ||
464 | ```js | |
465 | var Node = require('snapdragon-node'); | |
466 | var brace = new Node({type: 'brace'}); | |
467 | var open = new Node({type: 'brace.open'}); | |
468 | var close = new Node({type: 'brace.close'}); | |
469 | ||
470 | console.log(utils.isOpen(brace)); // false | |
471 | console.log(utils.isOpen(open)); // true | |
472 | console.log(utils.isOpen(close)); // false | |
473 | ``` | |
474 | ||
475 | ### [.isClose](index.js#L631) | |
476 | ||
477 | Returns true if the given node is a "*.close" node. | |
478 | ||
479 | **Params** | |
480 | ||
481 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
482 | * `returns` **{Boolean}** | |
483 | ||
484 | **Example** | |
485 | ||
486 | ```js | |
487 | var Node = require('snapdragon-node'); | |
488 | var brace = new Node({type: 'brace'}); | |
489 | var open = new Node({type: 'brace.open'}); | |
490 | var close = new Node({type: 'brace.close'}); | |
491 | ||
492 | console.log(utils.isClose(brace)); // false | |
493 | console.log(utils.isClose(open)); // false | |
494 | console.log(utils.isClose(close)); // true | |
495 | ``` | |
496 | ||
497 | ### [.isBlock](index.js#L662) | |
498 | ||
499 | Returns true if the given node is an "*.open" node. | |
500 | ||
501 | **Params** | |
502 | ||
503 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
504 | * `returns` **{Boolean}** | |
505 | ||
506 | **Example** | |
507 | ||
508 | ```js | |
509 | var Node = require('snapdragon-node'); | |
510 | var brace = new Node({type: 'brace'}); | |
511 | var open = new Node({type: 'brace.open', value: '{'}); | |
512 | var inner = new Node({type: 'text', value: 'a,b,c'}); | |
513 | var close = new Node({type: 'brace.close', value: '}'}); | |
514 | brace.push(open); | |
515 | brace.push(inner); | |
516 | brace.push(close); | |
517 | ||
518 | console.log(utils.isBlock(brace)); // true | |
519 | ``` | |
520 | ||
521 | ### [.hasNode](index.js#L691) | |
522 | ||
523 | Returns true if `parent.nodes` has the given `node`. | |
524 | ||
525 | **Params** | |
526 | ||
527 | * `type` **{String}** | |
528 | * `returns` **{Boolean}** | |
529 | ||
530 | **Example** | |
531 | ||
532 | ```js | |
533 | const foo = new Node({type: 'foo'}); | |
534 | const bar = new Node({type: 'bar'}); | |
535 | cosole.log(util.hasNode(foo, bar)); // false | |
536 | foo.push(bar); | |
537 | cosole.log(util.hasNode(foo, bar)); // true | |
538 | ``` | |
539 | ||
540 | ### [.hasOpen](index.js#L723) | |
541 | ||
542 | Returns true if `node.nodes` **has** an `.open` node | |
543 | ||
544 | **Params** | |
545 | ||
546 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
547 | * `returns` **{Boolean}** | |
548 | ||
549 | **Example** | |
550 | ||
551 | ```js | |
552 | var Node = require('snapdragon-node'); | |
553 | var brace = new Node({ | |
554 | type: 'brace', | |
555 | nodes: [] | |
556 | }); | |
557 | ||
558 | var open = new Node({type: 'brace.open'}); | |
559 | console.log(utils.hasOpen(brace)); // false | |
560 | ||
561 | brace.pushNode(open); | |
562 | console.log(utils.hasOpen(brace)); // true | |
563 | ``` | |
564 | ||
565 | ### [.hasClose](index.js#L754) | |
566 | ||
567 | Returns true if `node.nodes` **has** a `.close` node | |
568 | ||
569 | **Params** | |
570 | ||
571 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
572 | * `returns` **{Boolean}** | |
573 | ||
574 | **Example** | |
575 | ||
576 | ```js | |
577 | var Node = require('snapdragon-node'); | |
578 | var brace = new Node({ | |
579 | type: 'brace', | |
580 | nodes: [] | |
581 | }); | |
582 | ||
583 | var close = new Node({type: 'brace.close'}); | |
584 | console.log(utils.hasClose(brace)); // false | |
585 | ||
586 | brace.pushNode(close); | |
587 | console.log(utils.hasClose(brace)); // true | |
588 | ``` | |
589 | ||
590 | ### [.hasOpenAndClose](index.js#L789) | |
591 | ||
592 | Returns true if `node.nodes` has both `.open` and `.close` nodes | |
593 | ||
594 | **Params** | |
595 | ||
596 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
597 | * `returns` **{Boolean}** | |
598 | ||
599 | **Example** | |
600 | ||
601 | ```js | |
602 | var Node = require('snapdragon-node'); | |
603 | var brace = new Node({ | |
604 | type: 'brace', | |
605 | nodes: [] | |
606 | }); | |
607 | ||
608 | var open = new Node({type: 'brace.open'}); | |
609 | var close = new Node({type: 'brace.close'}); | |
610 | console.log(utils.hasOpen(brace)); // false | |
611 | console.log(utils.hasClose(brace)); // false | |
612 | ||
613 | brace.pushNode(open); | |
614 | brace.pushNode(close); | |
615 | console.log(utils.hasOpen(brace)); // true | |
616 | console.log(utils.hasClose(brace)); // true | |
617 | ``` | |
618 | ||
619 | ### [.addType](index.js#L811) | |
620 | ||
621 | 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`. | |
622 | ||
623 | **Params** | |
624 | ||
625 | * `state` **{Object}**: The `compiler.state` object or custom state object. | |
626 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
627 | * `returns` **{Array}**: Returns the `state.inside` stack for the given type. | |
628 | ||
629 | **Example** | |
630 | ||
631 | ```js | |
632 | var state = { inside: {}}; | |
633 | var node = new Node({type: 'brace'}); | |
634 | utils.addType(state, node); | |
635 | console.log(state.inside); | |
636 | //=> { brace: [{type: 'brace'}] } | |
637 | ``` | |
638 | ||
639 | ### [.removeType](index.js#L851) | |
640 | ||
641 | 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`. | |
642 | ||
643 | **Params** | |
644 | ||
645 | * `state` **{Object}**: The `compiler.state` object or custom state object. | |
646 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
647 | * `returns` **{Array}**: Returns the `state.inside` stack for the given type. | |
648 | ||
649 | **Example** | |
650 | ||
651 | ```js | |
652 | var state = { inside: {}}; | |
653 | var node = new Node({type: 'brace'}); | |
654 | utils.addType(state, node); | |
655 | console.log(state.inside); | |
656 | //=> { brace: [{type: 'brace'}] } | |
657 | utils.removeType(state, node); | |
658 | //=> { brace: [] } | |
659 | ``` | |
660 | ||
661 | ### [.isEmpty](index.js#L880) | |
662 | ||
663 | Returns true if `node.value` is an empty string, or `node.nodes` does not contain any non-empty text nodes. | |
664 | ||
665 | **Params** | |
666 | ||
667 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
668 | * `fn` **{Function}** | |
669 | * `returns` **{Boolean}** | |
670 | ||
671 | **Example** | |
672 | ||
673 | ```js | |
674 | var node = new Node({type: 'text'}); | |
675 | utils.isEmpty(node); //=> true | |
676 | node.value = 'foo'; | |
677 | utils.isEmpty(node); //=> false | |
678 | ``` | |
679 | ||
680 | ### [.isInsideType](index.js#L922) | |
681 | ||
682 | Returns true if the `state.inside` stack for the given type exists and has one or more nodes on it. | |
683 | ||
684 | **Params** | |
685 | ||
686 | * `state` **{Object}** | |
687 | * `type` **{String}** | |
688 | * `returns` **{Boolean}** | |
689 | ||
690 | **Example** | |
691 | ||
692 | ```js | |
693 | var state = { inside: {}}; | |
694 | var node = new Node({type: 'brace'}); | |
695 | console.log(utils.isInsideType(state, 'brace')); //=> false | |
696 | utils.addType(state, node); | |
697 | console.log(utils.isInsideType(state, 'brace')); //=> true | |
698 | utils.removeType(state, node); | |
699 | console.log(utils.isInsideType(state, 'brace')); //=> false | |
700 | ``` | |
701 | ||
702 | ### [.isInside](index.js#L956) | |
703 | ||
704 | Returns true if `node` is either a child or grand-child of the given `type`, or `state.inside[type]` is a non-empty array. | |
705 | ||
706 | **Params** | |
707 | ||
708 | * `state` **{Object}**: Either the `compiler.state` object, if it exists, or a user-supplied state object. | |
709 | * `node` **{Object}**: Instance of [snapdragon-node](https://github.com/jonschlinkert/snapdragon-node) | |
710 | * `type` **{String}**: The `node.type` to check for. | |
711 | * `returns` **{Boolean}** | |
712 | ||
713 | **Example** | |
714 | ||
715 | ```js | |
716 | var state = { inside: {}}; | |
717 | var node = new Node({type: 'brace'}); | |
718 | var open = new Node({type: 'brace.open'}); | |
719 | console.log(utils.isInside(state, open, 'brace')); //=> false | |
720 | utils.pushNode(node, open); | |
721 | console.log(utils.isInside(state, open, 'brace')); //=> true | |
722 | ``` | |
723 | ||
724 | ### [.last](index.js#L1004) | |
725 | ||
726 | Get the last `n` element from the given `array`. Used for getting | |
727 | a node from `node.nodes.` | |
728 | ||
729 | **Params** | |
730 | ||
731 | * `array` **{Array}** | |
732 | * `n` **{Number}** | |
733 | * `returns` **{undefined}** | |
734 | ||
735 | ### [.arrayify](index.js#L1028) | |
736 | ||
737 | Cast the given `value` to an array. | |
738 | ||
739 | **Params** | |
740 | ||
741 | * `value` **{any}** | |
742 | * `returns` **{Array}** | |
743 | ||
744 | **Example** | |
745 | ||
746 | ```js | |
747 | console.log(utils.arrayify('')); | |
748 | //=> [] | |
749 | console.log(utils.arrayify('foo')); | |
750 | //=> ['foo'] | |
751 | console.log(utils.arrayify(['foo'])); | |
752 | //=> ['foo'] | |
753 | ``` | |
754 | ||
755 | ### [.stringify](index.js#L1047) | |
756 | ||
757 | Convert the given `value` to a string by joining with `,`. Useful | |
758 | for creating a cheerio/CSS/DOM-style selector from a list of strings. | |
759 | ||
760 | **Params** | |
761 | ||
762 | * `value` **{any}** | |
763 | * `returns` **{Array}** | |
764 | ||
765 | ### [.trim](index.js#L1060) | |
766 | ||
767 | Ensure that the given value is a string and call `.trim()` on it, | |
768 | or return an empty string. | |
769 | ||
770 | **Params** | |
771 | ||
772 | * `str` **{String}** | |
773 | * `returns` **{String}** | |
774 | ||
775 | ## About | |
776 | ||
777 | <details> | |
778 | <summary><strong>Contributing</strong></summary> | |
779 | ||
780 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). | |
781 | ||
782 | Please read the [contributing guide](.github/contributing.md) for advice on opening issues, pull requests, and coding standards. | |
783 | ||
784 | </details> | |
785 | ||
786 | <details> | |
787 | <summary><strong>Running Tests</strong></summary> | |
788 | ||
789 | 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: | |
790 | ||
791 | ```sh | |
792 | $ npm install && npm test | |
793 | ``` | |
794 | ||
795 | </details> | |
796 | <details> | |
797 | <summary><strong>Building docs</strong></summary> | |
798 | ||
799 | _(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.)_ | |
800 | ||
801 | To generate the readme, run the following command: | |
802 | ||
803 | ```sh | |
804 | $ npm install -g verbose/verb#dev verb-generate-readme && verb | |
805 | ``` | |
806 | ||
807 | </details> | |
808 | ||
809 | ### Related projects | |
810 | ||
811 | You might also be interested in these projects: | |
812 | ||
813 | * [snapdragon-node](https://www.npmjs.com/package/snapdragon-node): Snapdragon utility for creating a new AST node in custom code, such as plugins. | [homepage](https://github.com/jonschlinkert/snapdragon-node "Snapdragon utility for creating a new AST node in custom code, such as plugins.") | |
814 | * [snapdragon-position](https://www.npmjs.com/package/snapdragon-position): Snapdragon util and plugin for patching the position on an AST node. | [homepage](https://github.com/here-be/snapdragon-position "Snapdragon util and plugin for patching the position on an AST node.") | |
815 | * [snapdragon-token](https://www.npmjs.com/package/snapdragon-token): Create a snapdragon token. Used by the snapdragon lexer, but can also be used by… [more](https://github.com/here-be/snapdragon-token) | [homepage](https://github.com/here-be/snapdragon-token "Create a snapdragon token. Used by the snapdragon lexer, but can also be used by plugins.") | |
816 | ||
817 | ### Contributors | |
818 | ||
819 | | **Commits** | **Contributor** | | |
820 | | --- | --- | | |
821 | | 43 | [jonschlinkert](https://github.com/jonschlinkert) | | |
822 | | 2 | [realityking](https://github.com/realityking) | | |
823 | ||
824 | ### Author | |
825 | ||
826 | **Jon Schlinkert** | |
827 | ||
828 | * [linkedin/in/jonschlinkert](https://linkedin.com/in/jonschlinkert) | |
829 | * [github/jonschlinkert](https://github.com/jonschlinkert) | |
830 | * [twitter/jonschlinkert](https://twitter.com/jonschlinkert) | |
831 | ||
832 | ### License | |
833 | ||
834 | Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert). | |
835 | Released under the [MIT License](LICENSE). | |
836 | ||
837 | *** | |
838 | ||
839 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on January 11, 2018._⏎ |
0 | 'use strict'; | |
1 | ||
2 | var gulp = require('gulp'); | |
3 | var mocha = require('gulp-mocha'); | |
4 | var istanbul = require('gulp-istanbul'); | |
5 | var eslint = require('gulp-eslint'); | |
6 | ||
7 | gulp.task('coverage', function() { | |
8 | return gulp.src('index.js') | |
9 | .pipe(istanbul()) | |
10 | .pipe(istanbul.hookRequire()); | |
11 | }); | |
12 | ||
13 | gulp.task('test', ['coverage'], function() { | |
14 | return gulp.src('test/*.js') | |
15 | .pipe(mocha({reporter: 'spec'})) | |
16 | .pipe(istanbul.writeReports()); | |
17 | }); | |
18 | ||
19 | gulp.task('lint', function() { | |
20 | return gulp.src(['index.js', 'test/*.js']) | |
21 | .pipe(eslint()) | |
22 | .pipe(eslint.format()); | |
23 | }); | |
24 | ||
25 | gulp.task('default', ['test', 'lint']); |
0 | 'use strict'; | |
1 | ||
2 | var typeOf = require('kind-of'); | |
3 | var utils = module.exports; | |
4 | ||
5 | /** | |
6 | * Returns true if the given value is a node. | |
7 | * | |
8 | * ```js | |
9 | * var Node = require('snapdragon-node'); | |
10 | * var node = new Node({type: 'foo'}); | |
11 | * console.log(utils.isNode(node)); //=> true | |
12 | * console.log(utils.isNode({})); //=> false | |
13 | * ``` | |
14 | * @param {Object} `node` Instance of [snapdragon-node][] | |
15 | * @returns {Boolean} | |
16 | * @api public | |
17 | */ | |
18 | ||
19 | utils.isNode = function(node) { | |
20 | return typeOf(node) === 'object' && node.isNode === true; | |
21 | }; | |
22 | ||
23 | /** | |
24 | * Emit an empty string for the given `node`. | |
25 | * | |
26 | * ```js | |
27 | * // do nothing for beginning-of-string | |
28 | * snapdragon.compiler.set('bos', utils.noop); | |
29 | * ``` | |
30 | * @param {Object} `node` Instance of [snapdragon-node][] | |
31 | * @returns {undefined} | |
32 | * @api public | |
33 | */ | |
34 | ||
35 | utils.noop = function(node) { | |
36 | append(this, '', node); | |
37 | }; | |
38 | ||
39 | /** | |
40 | * Returns `node.value` or `node.val`. | |
41 | * | |
42 | * ```js | |
43 | * const star = new Node({type: 'star', value: '*'}); | |
44 | * const slash = new Node({type: 'slash', val: '/'}); | |
45 | * console.log(utils.value(star)) //=> '*' | |
46 | * console.log(utils.value(slash)) //=> '/' | |
47 | * ``` | |
48 | * @param {Object} `node` Instance of [snapdragon-node][] | |
49 | * @returns {String} returns | |
50 | * @api public | |
51 | */ | |
52 | ||
53 | utils.value = function(node) { | |
54 | if (typeof node.value === 'string') { | |
55 | return node.value; | |
56 | } | |
57 | return node.val; | |
58 | }; | |
59 | ||
60 | /** | |
61 | * Append `node.value` to `compiler.output`. | |
62 | * | |
63 | * ```js | |
64 | * snapdragon.compiler.set('text', utils.identity); | |
65 | * ``` | |
66 | * @param {Object} `node` Instance of [snapdragon-node][] | |
67 | * @returns {undefined} | |
68 | * @api public | |
69 | */ | |
70 | ||
71 | utils.identity = function(node) { | |
72 | append(this, utils.value(node), node); | |
73 | }; | |
74 | ||
75 | /** | |
76 | * Previously named `.emit`, this method appends the given `value` | |
77 | * to `compiler.output` for the given node. Useful when you know | |
78 | * what value should be appended advance, regardless of the actual | |
79 | * value of `node.value`. | |
80 | * | |
81 | * ```js | |
82 | * snapdragon.compiler | |
83 | * .set('i', function(node) { | |
84 | * this.mapVisit(node); | |
85 | * }) | |
86 | * .set('i.open', utils.append('<i>')) | |
87 | * .set('i.close', utils.append('</i>')) | |
88 | * ``` | |
89 | * @param {Object} `node` Instance of [snapdragon-node][] | |
90 | * @returns {Function} Returns a compiler middleware function. | |
91 | * @api public | |
92 | */ | |
93 | ||
94 | utils.append = function(value) { | |
95 | return function(node) { | |
96 | append(this, value, node); | |
97 | }; | |
98 | }; | |
99 | ||
100 | /** | |
101 | * Used in compiler middleware, this onverts an AST node into | |
102 | * an empty `text` node and deletes `node.nodes` if it exists. | |
103 | * The advantage of this method is that, as opposed to completely | |
104 | * removing the node, indices will not need to be re-calculated | |
105 | * in sibling nodes, and nothing is appended to the output. | |
106 | * | |
107 | * ```js | |
108 | * utils.toNoop(node); | |
109 | * // convert `node.nodes` to the given value instead of deleting it | |
110 | * utils.toNoop(node, []); | |
111 | * ``` | |
112 | * @param {Object} `node` Instance of [snapdragon-node][] | |
113 | * @param {Array} `nodes` Optionally pass a new `nodes` value, to replace the existing `node.nodes` array. | |
114 | * @api public | |
115 | */ | |
116 | ||
117 | utils.toNoop = function(node, nodes) { | |
118 | if (nodes) { | |
119 | node.nodes = nodes; | |
120 | } else { | |
121 | delete node.nodes; | |
122 | node.type = 'text'; | |
123 | node.value = ''; | |
124 | } | |
125 | }; | |
126 | ||
127 | /** | |
128 | * Visit `node` with the given `fn`. The built-in `.visit` method in snapdragon | |
129 | * automatically calls registered compilers, this allows you to pass a visitor | |
130 | * function. | |
131 | * | |
132 | * ```js | |
133 | * snapdragon.compiler.set('i', function(node) { | |
134 | * utils.visit(node, function(childNode) { | |
135 | * // do stuff with "childNode" | |
136 | * return childNode; | |
137 | * }); | |
138 | * }); | |
139 | * ``` | |
140 | * @param {Object} `node` Instance of [snapdragon-node][] | |
141 | * @param {Function} `fn` | |
142 | * @return {Object} returns the node after recursively visiting all child nodes. | |
143 | * @api public | |
144 | */ | |
145 | ||
146 | utils.visit = function(node, fn) { | |
147 | assert(isFunction(fn), 'expected a visitor function'); | |
148 | expect(node, 'node'); | |
149 | fn(node); | |
150 | return node.nodes ? utils.mapVisit(node, fn) : node; | |
151 | }; | |
152 | ||
153 | /** | |
154 | * Map [visit](#visit) the given `fn` over `node.nodes`. This is called by | |
155 | * [visit](#visit), use this method if you do not want `fn` to be called on | |
156 | * the first node. | |
157 | * | |
158 | * ```js | |
159 | * snapdragon.compiler.set('i', function(node) { | |
160 | * utils.mapVisit(node, function(childNode) { | |
161 | * // do stuff with "childNode" | |
162 | * return childNode; | |
163 | * }); | |
164 | * }); | |
165 | * ``` | |
166 | * @param {Object} `node` Instance of [snapdragon-node][] | |
167 | * @param {Object} `options` | |
168 | * @param {Function} `fn` | |
169 | * @return {Object} returns the node | |
170 | * @api public | |
171 | */ | |
172 | ||
173 | utils.mapVisit = function(node, fn) { | |
174 | assert(isFunction(fn), 'expected a visitor function'); | |
175 | expect(node, 'node'); | |
176 | assert(isArray(node.nodes), 'expected node.nodes to be an array'); | |
177 | ||
178 | for (var i = 0; i < node.nodes.length; i++) { | |
179 | utils.visit(node.nodes[i], fn); | |
180 | } | |
181 | return node; | |
182 | }; | |
183 | ||
184 | /** | |
185 | * Unshift an `*.open` node onto `node.nodes`. | |
186 | * | |
187 | * ```js | |
188 | * var Node = require('snapdragon-node'); | |
189 | * snapdragon.parser.set('brace', function(node) { | |
190 | * var match = this.match(/^{/); | |
191 | * if (match) { | |
192 | * var parent = new Node({type: 'brace'}); | |
193 | * utils.addOpen(parent, Node); | |
194 | * console.log(parent.nodes[0]): | |
195 | * // { type: 'brace.open', value: '' }; | |
196 | * | |
197 | * // push the parent "brace" node onto the stack | |
198 | * this.push(parent); | |
199 | * | |
200 | * // return the parent node, so it's also added to the AST | |
201 | * return brace; | |
202 | * } | |
203 | * }); | |
204 | * ``` | |
205 | * @param {Object} `node` Instance of [snapdragon-node][] | |
206 | * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. | |
207 | * @param {Function} `filter` Optionaly specify a filter function to exclude the node. | |
208 | * @return {Object} Returns the created opening node. | |
209 | * @api public | |
210 | */ | |
211 | ||
212 | utils.addOpen = function(node, Node, value, filter) { | |
213 | expect(node, 'node'); | |
214 | assert(isFunction(Node), 'expected Node to be a constructor function'); | |
215 | ||
216 | if (typeof value === 'function') { | |
217 | filter = value; | |
218 | value = ''; | |
219 | } | |
220 | ||
221 | if (typeof filter === 'function' && !filter(node)) return; | |
222 | var open = new Node({ type: node.type + '.open', value: value}); | |
223 | var unshift = node.unshift || node.unshiftNode; | |
224 | if (typeof unshift === 'function') { | |
225 | unshift.call(node, open); | |
226 | } else { | |
227 | utils.unshiftNode(node, open); | |
228 | } | |
229 | return open; | |
230 | }; | |
231 | ||
232 | /** | |
233 | * Push a `*.close` node onto `node.nodes`. | |
234 | * | |
235 | * ```js | |
236 | * var Node = require('snapdragon-node'); | |
237 | * snapdragon.parser.set('brace', function(node) { | |
238 | * var match = this.match(/^}/); | |
239 | * if (match) { | |
240 | * var parent = this.parent(); | |
241 | * if (parent.type !== 'brace') { | |
242 | * throw new Error('missing opening: ' + '}'); | |
243 | * } | |
244 | * | |
245 | * utils.addClose(parent, Node); | |
246 | * console.log(parent.nodes[parent.nodes.length - 1]): | |
247 | * // { type: 'brace.close', value: '' }; | |
248 | * | |
249 | * // no need to return a node, since the parent | |
250 | * // was already added to the AST | |
251 | * return; | |
252 | * } | |
253 | * }); | |
254 | * ``` | |
255 | * @param {Object} `node` Instance of [snapdragon-node][] | |
256 | * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. | |
257 | * @param {Function} `filter` Optionaly specify a filter function to exclude the node. | |
258 | * @return {Object} Returns the created closing node. | |
259 | * @api public | |
260 | */ | |
261 | ||
262 | utils.addClose = function(node, Node, value, filter) { | |
263 | assert(isFunction(Node), 'expected Node to be a constructor function'); | |
264 | expect(node, 'node', Node); | |
265 | ||
266 | if (typeof value === 'function') { | |
267 | filter = value; | |
268 | value = ''; | |
269 | } | |
270 | ||
271 | if (typeof filter === 'function' && !filter(node)) return; | |
272 | var close = new Node({ type: node.type + '.close', value: value}); | |
273 | var push = node.push || node.pushNode; | |
274 | if (typeof push === 'function') { | |
275 | push.call(node, close); | |
276 | } else { | |
277 | utils.pushNode(node, close); | |
278 | } | |
279 | return close; | |
280 | }; | |
281 | ||
282 | /** | |
283 | * Wraps the given `node` with `*.open` and `*.close` nodes. | |
284 | * | |
285 | * @param {Object} `node` Instance of [snapdragon-node][] | |
286 | * @param {Function} `Node` (required) Node constructor function from [snapdragon-node][]. | |
287 | * @param {Function} `filter` Optionaly specify a filter function to exclude the node. | |
288 | * @return {Object} Returns the node | |
289 | * @api public | |
290 | */ | |
291 | ||
292 | utils.wrapNodes = function(node, Node, filter) { | |
293 | assert(utils.isNode(node), 'expected node to be an instance of Node'); | |
294 | assert(isFunction(Node), 'expected Node to be a constructor function'); | |
295 | ||
296 | utils.addOpen(node, Node, filter); | |
297 | utils.addClose(node, Node, filter); | |
298 | return node; | |
299 | }; | |
300 | ||
301 | /** | |
302 | * Push the given `node` onto `parent.nodes`, and set `parent` as `node.parent. | |
303 | * | |
304 | * ```js | |
305 | * var parent = new Node({type: 'foo'}); | |
306 | * var node = new Node({type: 'bar'}); | |
307 | * utils.pushNode(parent, node); | |
308 | * console.log(parent.nodes[0].type) // 'bar' | |
309 | * console.log(node.parent.type) // 'foo' | |
310 | * ``` | |
311 | * @param {Object} `parent` | |
312 | * @param {Object} `node` Instance of [snapdragon-node][] | |
313 | * @return {Object} Returns the child node | |
314 | * @api public | |
315 | */ | |
316 | ||
317 | utils.pushNode = function(parent, node) { | |
318 | assert(utils.isNode(parent), 'expected parent node to be an instance of Node'); | |
319 | if (!node) return; | |
320 | ||
321 | if (typeof parent.push === 'function') { | |
322 | return parent.push(node); | |
323 | } | |
324 | ||
325 | node.define('parent', parent); | |
326 | parent.nodes = parent.nodes || []; | |
327 | parent.nodes.push(node); | |
328 | return node; | |
329 | }; | |
330 | ||
331 | /** | |
332 | * Unshift `node` onto `parent.nodes`, and set `parent` as `node.parent. | |
333 | * | |
334 | * ```js | |
335 | * var parent = new Node({type: 'foo'}); | |
336 | * var node = new Node({type: 'bar'}); | |
337 | * utils.unshiftNode(parent, node); | |
338 | * console.log(parent.nodes[0].type) // 'bar' | |
339 | * console.log(node.parent.type) // 'foo' | |
340 | * ``` | |
341 | * @param {Object} `parent` | |
342 | * @param {Object} `node` Instance of [snapdragon-node][] | |
343 | * @return {undefined} | |
344 | * @api public | |
345 | */ | |
346 | ||
347 | utils.unshiftNode = function(parent, node) { | |
348 | assert(utils.isNode(parent), 'expected parent node to be an instance of Node'); | |
349 | if (!node) return; | |
350 | ||
351 | if (typeof parent.unshift === 'function') { | |
352 | return parent.unshift(node); | |
353 | } | |
354 | ||
355 | node.define('parent', parent); | |
356 | parent.nodes = parent.nodes || []; | |
357 | parent.nodes.unshift(node); | |
358 | }; | |
359 | ||
360 | /** | |
361 | * Pop the last `node` off of `parent.nodes`. The advantage of | |
362 | * using this method is that it checks for `node.nodes` and works | |
363 | * with any version of `snapdragon-node`. | |
364 | * | |
365 | * ```js | |
366 | * var parent = new Node({type: 'foo'}); | |
367 | * utils.pushNode(parent, new Node({type: 'foo'})); | |
368 | * utils.pushNode(parent, new Node({type: 'bar'})); | |
369 | * utils.pushNode(parent, new Node({type: 'baz'})); | |
370 | * console.log(parent.nodes.length); //=> 3 | |
371 | * utils.popNode(parent); | |
372 | * console.log(parent.nodes.length); //=> 2 | |
373 | * ``` | |
374 | * @param {Object} `parent` | |
375 | * @param {Object} `node` Instance of [snapdragon-node][] | |
376 | * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. | |
377 | * @api public | |
378 | */ | |
379 | ||
380 | utils.popNode = function(node) { | |
381 | assert(utils.isNode(node), 'expected node to be an instance of Node'); | |
382 | if (typeof node.pop === 'function') { | |
383 | return node.pop(); | |
384 | } | |
385 | return node.nodes && node.nodes.pop(); | |
386 | }; | |
387 | ||
388 | /** | |
389 | * Shift the first `node` off of `parent.nodes`. The advantage of | |
390 | * using this method is that it checks for `node.nodes` and works | |
391 | * with any version of `snapdragon-node`. | |
392 | * | |
393 | * ```js | |
394 | * var parent = new Node({type: 'foo'}); | |
395 | * utils.pushNode(parent, new Node({type: 'foo'})); | |
396 | * utils.pushNode(parent, new Node({type: 'bar'})); | |
397 | * utils.pushNode(parent, new Node({type: 'baz'})); | |
398 | * console.log(parent.nodes.length); //=> 3 | |
399 | * utils.shiftNode(parent); | |
400 | * console.log(parent.nodes.length); //=> 2 | |
401 | * ``` | |
402 | * @param {Object} `parent` | |
403 | * @param {Object} `node` Instance of [snapdragon-node][] | |
404 | * @return {Number|Undefined} Returns the length of `node.nodes` or undefined. | |
405 | * @api public | |
406 | */ | |
407 | ||
408 | utils.shiftNode = function(node) { | |
409 | assert(utils.isNode(node), 'expected node to be an instance of Node'); | |
410 | if (typeof node.shift === 'function') { | |
411 | return node.shift(); | |
412 | } | |
413 | return node.nodes && node.nodes.shift(); | |
414 | }; | |
415 | ||
416 | /** | |
417 | * Remove the specified `node` from `parent.nodes`. | |
418 | * | |
419 | * ```js | |
420 | * var parent = new Node({type: 'abc'}); | |
421 | * var foo = new Node({type: 'foo'}); | |
422 | * utils.pushNode(parent, foo); | |
423 | * utils.pushNode(parent, new Node({type: 'bar'})); | |
424 | * utils.pushNode(parent, new Node({type: 'baz'})); | |
425 | * console.log(parent.nodes.length); //=> 3 | |
426 | * utils.removeNode(parent, foo); | |
427 | * console.log(parent.nodes.length); //=> 2 | |
428 | * ``` | |
429 | * @param {Object} `parent` | |
430 | * @param {Object} `node` Instance of [snapdragon-node][] | |
431 | * @return {Object|undefined} Returns the removed node, if successful, or undefined if it does not exist on `parent.nodes`. | |
432 | * @api public | |
433 | */ | |
434 | ||
435 | utils.removeNode = function(parent, node) { | |
436 | assert(utils.isNode(parent), 'expected parent to be an instance of Node'); | |
437 | if (!parent.nodes) return; | |
438 | if (!node) return; | |
439 | ||
440 | if (typeof parent.remove === 'function') { | |
441 | return parent.remove(node); | |
442 | } | |
443 | ||
444 | var idx = parent.nodes.indexOf(node); | |
445 | if (idx !== -1) { | |
446 | return parent.nodes.splice(idx, 1); | |
447 | } | |
448 | }; | |
449 | ||
450 | /** | |
451 | * Returns true if `node.type` matches the given `type`. Throws a | |
452 | * `TypeError` if `node` is not an instance of `Node`. | |
453 | * | |
454 | * ```js | |
455 | * var Node = require('snapdragon-node'); | |
456 | * var node = new Node({type: 'foo'}); | |
457 | * console.log(utils.isType(node, 'foo')); // false | |
458 | * console.log(utils.isType(node, 'bar')); // true | |
459 | * ``` | |
460 | * @param {Object} `node` Instance of [snapdragon-node][] | |
461 | * @param {String} `type` | |
462 | * @return {Boolean} | |
463 | * @api public | |
464 | */ | |
465 | ||
466 | utils.isType = function(node, type) { | |
467 | if (!utils.isNode(node)) return false; | |
468 | switch (typeOf(type)) { | |
469 | case 'string': | |
470 | return node.type === type; | |
471 | case 'regexp': | |
472 | return type.test(node.type); | |
473 | case 'array': | |
474 | for (const key of type.slice()) { | |
475 | if (utils.isType(node, key)) { | |
476 | return true; | |
477 | } | |
478 | } | |
479 | return false; | |
480 | default: { | |
481 | throw new TypeError('expected "type" to be an array, string or regexp'); | |
482 | } | |
483 | } | |
484 | }; | |
485 | ||
486 | /** | |
487 | * Returns true if the given `node` has the given `type` in `node.nodes`. | |
488 | * Throws a `TypeError` if `node` is not an instance of `Node`. | |
489 | * | |
490 | * ```js | |
491 | * var Node = require('snapdragon-node'); | |
492 | * var node = new Node({ | |
493 | * type: 'foo', | |
494 | * nodes: [ | |
495 | * new Node({type: 'bar'}), | |
496 | * new Node({type: 'baz'}) | |
497 | * ] | |
498 | * }); | |
499 | * console.log(utils.hasType(node, 'xyz')); // false | |
500 | * console.log(utils.hasType(node, 'baz')); // true | |
501 | * ``` | |
502 | * @param {Object} `node` Instance of [snapdragon-node][] | |
503 | * @param {String} `type` | |
504 | * @return {Boolean} | |
505 | * @api public | |
506 | */ | |
507 | ||
508 | utils.hasType = function(node, type) { | |
509 | if (!utils.isNode(node)) return false; | |
510 | if (!Array.isArray(node.nodes)) return false; | |
511 | for (const child of node.nodes) { | |
512 | if (utils.isType(child, type)) { | |
513 | return true; | |
514 | } | |
515 | } | |
516 | return false; | |
517 | }; | |
518 | ||
519 | /** | |
520 | * Returns the first node from `node.nodes` of the given `type` | |
521 | * | |
522 | * ```js | |
523 | * var node = new Node({ | |
524 | * type: 'foo', | |
525 | * nodes: [ | |
526 | * new Node({type: 'text', value: 'abc'}), | |
527 | * new Node({type: 'text', value: 'xyz'}) | |
528 | * ] | |
529 | * }); | |
530 | * | |
531 | * var textNode = utils.firstOfType(node.nodes, 'text'); | |
532 | * console.log(textNode.value); | |
533 | * //=> 'abc' | |
534 | * ``` | |
535 | * @param {Array} `nodes` | |
536 | * @param {String} `type` | |
537 | * @return {Object|undefined} Returns the first matching node or undefined. | |
538 | * @api public | |
539 | */ | |
540 | ||
541 | utils.firstOfType = function(nodes, type) { | |
542 | for (const node of nodes) { | |
543 | if (utils.isType(node, type)) { | |
544 | return node; | |
545 | } | |
546 | } | |
547 | }; | |
548 | ||
549 | /** | |
550 | * Returns the node at the specified index, or the first node of the | |
551 | * given `type` from `node.nodes`. | |
552 | * | |
553 | * ```js | |
554 | * var node = new Node({ | |
555 | * type: 'foo', | |
556 | * nodes: [ | |
557 | * new Node({type: 'text', value: 'abc'}), | |
558 | * new Node({type: 'text', value: 'xyz'}) | |
559 | * ] | |
560 | * }); | |
561 | * | |
562 | * var nodeOne = utils.findNode(node.nodes, 'text'); | |
563 | * console.log(nodeOne.value); | |
564 | * //=> 'abc' | |
565 | * | |
566 | * var nodeTwo = utils.findNode(node.nodes, 1); | |
567 | * console.log(nodeTwo.value); | |
568 | * //=> 'xyz' | |
569 | * ``` | |
570 | * | |
571 | * @param {Array} `nodes` | |
572 | * @param {String|Number} `type` Node type or index. | |
573 | * @return {Object} Returns a node or undefined. | |
574 | * @api public | |
575 | */ | |
576 | ||
577 | utils.findNode = function(nodes, type) { | |
578 | if (!Array.isArray(nodes)) return null; | |
579 | if (typeof type === 'number') return nodes[type]; | |
580 | return utils.firstOfType(nodes, type); | |
581 | }; | |
582 | ||
583 | /** | |
584 | * Returns true if the given node is an "*.open" node. | |
585 | * | |
586 | * ```js | |
587 | * var Node = require('snapdragon-node'); | |
588 | * var brace = new Node({type: 'brace'}); | |
589 | * var open = new Node({type: 'brace.open'}); | |
590 | * var close = new Node({type: 'brace.close'}); | |
591 | * | |
592 | * console.log(utils.isOpen(brace)); // false | |
593 | * console.log(utils.isOpen(open)); // true | |
594 | * console.log(utils.isOpen(close)); // false | |
595 | * ``` | |
596 | * @param {Object} `node` Instance of [snapdragon-node][] | |
597 | * @return {Boolean} | |
598 | * @api public | |
599 | */ | |
600 | ||
601 | utils.isOpen = function(node) { | |
602 | if (!node) return false; | |
603 | if (node.parent && typeof node.parent.isOpen === 'function') { | |
604 | return node.parent.isOpen(node); | |
605 | } | |
606 | if (node && typeof node.isOpen === 'function') { | |
607 | return node.isOpen(node); | |
608 | } | |
609 | return node.type ? node.type.slice(-5) === '.open' : false; | |
610 | }; | |
611 | ||
612 | /** | |
613 | * Returns true if the given node is a "*.close" node. | |
614 | * | |
615 | * ```js | |
616 | * var Node = require('snapdragon-node'); | |
617 | * var brace = new Node({type: 'brace'}); | |
618 | * var open = new Node({type: 'brace.open'}); | |
619 | * var close = new Node({type: 'brace.close'}); | |
620 | * | |
621 | * console.log(utils.isClose(brace)); // false | |
622 | * console.log(utils.isClose(open)); // false | |
623 | * console.log(utils.isClose(close)); // true | |
624 | * ``` | |
625 | * @param {Object} `node` Instance of [snapdragon-node][] | |
626 | * @return {Boolean} | |
627 | * @api public | |
628 | */ | |
629 | ||
630 | utils.isClose = function(node) { | |
631 | if (!node) return false; | |
632 | if (node.parent && typeof node.parent.isClose === 'function') { | |
633 | return node.parent.isClose(node); | |
634 | } | |
635 | if (node && typeof node.isClose === 'function') { | |
636 | return node.isClose(node); | |
637 | } | |
638 | return node.type ? node.type.slice(-6) === '.close' : false; | |
639 | }; | |
640 | ||
641 | /** | |
642 | * Returns true if the given node is an "*.open" node. | |
643 | * | |
644 | * ```js | |
645 | * var Node = require('snapdragon-node'); | |
646 | * var brace = new Node({type: 'brace'}); | |
647 | * var open = new Node({type: 'brace.open', value: '{'}); | |
648 | * var inner = new Node({type: 'text', value: 'a,b,c'}); | |
649 | * var close = new Node({type: 'brace.close', value: '}'}); | |
650 | * brace.push(open); | |
651 | * brace.push(inner); | |
652 | * brace.push(close); | |
653 | * | |
654 | * console.log(utils.isBlock(brace)); // true | |
655 | * ``` | |
656 | * @param {Object} `node` Instance of [snapdragon-node][] | |
657 | * @return {Boolean} | |
658 | * @api public | |
659 | */ | |
660 | ||
661 | utils.isBlock = function(node) { | |
662 | if (!node || !utils.isNode(node)) return false; | |
663 | if (!Array.isArray(node.nodes)) { | |
664 | return false; | |
665 | } | |
666 | if (node.parent && typeof node.parent.isBlock === 'function') { | |
667 | return node.parent.isBlock(node); | |
668 | } | |
669 | if (typeof node.isBlock === 'function') { | |
670 | return node.isBlock(node); | |
671 | } | |
672 | return utils.hasOpenAndClose(node); | |
673 | }; | |
674 | ||
675 | /** | |
676 | * Returns true if `parent.nodes` has the given `node`. | |
677 | * | |
678 | * ```js | |
679 | * const foo = new Node({type: 'foo'}); | |
680 | * const bar = new Node({type: 'bar'}); | |
681 | * cosole.log(util.hasNode(foo, bar)); // false | |
682 | * foo.push(bar); | |
683 | * cosole.log(util.hasNode(foo, bar)); // true | |
684 | * ``` | |
685 | * @param {String} `type` | |
686 | * @return {Boolean} | |
687 | * @api public | |
688 | */ | |
689 | ||
690 | utils.hasNode = function(node, child) { | |
691 | if (!utils.isNode(node)) return false; | |
692 | if (typeof node.has === 'function') { | |
693 | return node.has(child); | |
694 | } | |
695 | if (node.nodes) { | |
696 | return node.nodes.indexOf(child) !== -1; | |
697 | } | |
698 | return false; | |
699 | }; | |
700 | ||
701 | /** | |
702 | * Returns true if `node.nodes` **has** an `.open` node | |
703 | * | |
704 | * ```js | |
705 | * var Node = require('snapdragon-node'); | |
706 | * var brace = new Node({ | |
707 | * type: 'brace', | |
708 | * nodes: [] | |
709 | * }); | |
710 | * | |
711 | * var open = new Node({type: 'brace.open'}); | |
712 | * console.log(utils.hasOpen(brace)); // false | |
713 | * | |
714 | * brace.pushNode(open); | |
715 | * console.log(utils.hasOpen(brace)); // true | |
716 | * ``` | |
717 | * @param {Object} `node` Instance of [snapdragon-node][] | |
718 | * @return {Boolean} | |
719 | * @api public | |
720 | */ | |
721 | ||
722 | utils.hasOpen = function(node) { | |
723 | assert(utils.isNode(node), 'expected node to be an instance of Node'); | |
724 | var first = node.first || node.nodes ? node.nodes[0] : null; | |
725 | if (!utils.isNode(first)) return false; | |
726 | if (node.isOpen === 'function') { | |
727 | return node.isOpen(first); | |
728 | } | |
729 | return first.type === node.type + '.open'; | |
730 | }; | |
731 | ||
732 | /** | |
733 | * Returns true if `node.nodes` **has** a `.close` node | |
734 | * | |
735 | * ```js | |
736 | * var Node = require('snapdragon-node'); | |
737 | * var brace = new Node({ | |
738 | * type: 'brace', | |
739 | * nodes: [] | |
740 | * }); | |
741 | * | |
742 | * var close = new Node({type: 'brace.close'}); | |
743 | * console.log(utils.hasClose(brace)); // false | |
744 | * | |
745 | * brace.pushNode(close); | |
746 | * console.log(utils.hasClose(brace)); // true | |
747 | * ``` | |
748 | * @param {Object} `node` Instance of [snapdragon-node][] | |
749 | * @return {Boolean} | |
750 | * @api public | |
751 | */ | |
752 | ||
753 | utils.hasClose = function(node) { | |
754 | assert(utils.isNode(node), 'expected node to be an instance of Node'); | |
755 | var last = node.last || node.nodes ? node.nodes[node.nodes.length - 1] : null; | |
756 | if (!utils.isNode(last)) return false; | |
757 | if (typeof node.isClose === 'function') { | |
758 | return node.isClose(last); | |
759 | } | |
760 | return last.type === node.type + '.close'; | |
761 | }; | |
762 | ||
763 | /** | |
764 | * Returns true if `node.nodes` has both `.open` and `.close` nodes | |
765 | * | |
766 | * ```js | |
767 | * var Node = require('snapdragon-node'); | |
768 | * var brace = new Node({ | |
769 | * type: 'brace', | |
770 | * nodes: [] | |
771 | * }); | |
772 | * | |
773 | * var open = new Node({type: 'brace.open'}); | |
774 | * var close = new Node({type: 'brace.close'}); | |
775 | * console.log(utils.hasOpen(brace)); // false | |
776 | * console.log(utils.hasClose(brace)); // false | |
777 | * | |
778 | * brace.pushNode(open); | |
779 | * brace.pushNode(close); | |
780 | * console.log(utils.hasOpen(brace)); // true | |
781 | * console.log(utils.hasClose(brace)); // true | |
782 | * ``` | |
783 | * @param {Object} `node` Instance of [snapdragon-node][] | |
784 | * @return {Boolean} | |
785 | * @api public | |
786 | */ | |
787 | ||
788 | utils.hasOpenAndClose = function(node) { | |
789 | return utils.hasOpen(node) && utils.hasClose(node); | |
790 | }; | |
791 | ||
792 | /** | |
793 | * Push the given `node` onto the `state.inside` array for the | |
794 | * given type. This array is used as a specialized "stack" for | |
795 | * only the given `node.type`. | |
796 | * | |
797 | * ```js | |
798 | * var state = { inside: {}}; | |
799 | * var node = new Node({type: 'brace'}); | |
800 | * utils.addType(state, node); | |
801 | * console.log(state.inside); | |
802 | * //=> { brace: [{type: 'brace'}] } | |
803 | * ``` | |
804 | * @param {Object} `state` The `compiler.state` object or custom state object. | |
805 | * @param {Object} `node` Instance of [snapdragon-node][] | |
806 | * @return {Array} Returns the `state.inside` stack for the given type. | |
807 | * @api public | |
808 | */ | |
809 | ||
810 | utils.addType = function(state, node) { | |
811 | assert(utils.isNode(node), 'expected node to be an instance of Node'); | |
812 | assert(isObject(state), 'expected state to be an object'); | |
813 | ||
814 | var type = node.parent | |
815 | ? node.parent.type | |
816 | : node.type.replace(/\.open$/, ''); | |
817 | ||
818 | if (!state.hasOwnProperty('inside')) { | |
819 | state.inside = {}; | |
820 | } | |
821 | if (!state.inside.hasOwnProperty(type)) { | |
822 | state.inside[type] = []; | |
823 | } | |
824 | ||
825 | var arr = state.inside[type]; | |
826 | arr.push(node); | |
827 | return arr; | |
828 | }; | |
829 | ||
830 | /** | |
831 | * Remove the given `node` from the `state.inside` array for the | |
832 | * given type. This array is used as a specialized "stack" for | |
833 | * only the given `node.type`. | |
834 | * | |
835 | * ```js | |
836 | * var state = { inside: {}}; | |
837 | * var node = new Node({type: 'brace'}); | |
838 | * utils.addType(state, node); | |
839 | * console.log(state.inside); | |
840 | * //=> { brace: [{type: 'brace'}] } | |
841 | * utils.removeType(state, node); | |
842 | * //=> { brace: [] } | |
843 | * ``` | |
844 | * @param {Object} `state` The `compiler.state` object or custom state object. | |
845 | * @param {Object} `node` Instance of [snapdragon-node][] | |
846 | * @return {Array} Returns the `state.inside` stack for the given type. | |
847 | * @api public | |
848 | */ | |
849 | ||
850 | utils.removeType = function(state, node) { | |
851 | assert(utils.isNode(node), 'expected node to be an instance of Node'); | |
852 | assert(isObject(state), 'expected state to be an object'); | |
853 | ||
854 | var type = node.parent | |
855 | ? node.parent.type | |
856 | : node.type.replace(/\.close$/, ''); | |
857 | ||
858 | if (state.inside.hasOwnProperty(type)) { | |
859 | return state.inside[type].pop(); | |
860 | } | |
861 | }; | |
862 | ||
863 | /** | |
864 | * Returns true if `node.value` is an empty string, or `node.nodes` does | |
865 | * not contain any non-empty text nodes. | |
866 | * | |
867 | * ```js | |
868 | * var node = new Node({type: 'text'}); | |
869 | * utils.isEmpty(node); //=> true | |
870 | * node.value = 'foo'; | |
871 | * utils.isEmpty(node); //=> false | |
872 | * ``` | |
873 | * @param {Object} `node` Instance of [snapdragon-node][] | |
874 | * @param {Function} `fn` | |
875 | * @return {Boolean} | |
876 | * @api public | |
877 | */ | |
878 | ||
879 | utils.isEmpty = function(node, fn) { | |
880 | assert(utils.isNode(node), 'expected node to be an instance of Node'); | |
881 | ||
882 | if (!Array.isArray(node.nodes)) { | |
883 | if (typeof fn === 'function') { | |
884 | return fn(node); | |
885 | } | |
886 | return !utils.value(node); | |
887 | } | |
888 | ||
889 | if (node.nodes.length === 0) { | |
890 | return true; | |
891 | } | |
892 | ||
893 | for (const child of node.nodes) { | |
894 | if (!utils.isEmpty(child, fn)) { | |
895 | return false; | |
896 | } | |
897 | } | |
898 | ||
899 | return true; | |
900 | }; | |
901 | ||
902 | /** | |
903 | * Returns true if the `state.inside` stack for the given type exists | |
904 | * and has one or more nodes on it. | |
905 | * | |
906 | * ```js | |
907 | * var state = { inside: {}}; | |
908 | * var node = new Node({type: 'brace'}); | |
909 | * console.log(utils.isInsideType(state, 'brace')); //=> false | |
910 | * utils.addType(state, node); | |
911 | * console.log(utils.isInsideType(state, 'brace')); //=> true | |
912 | * utils.removeType(state, node); | |
913 | * console.log(utils.isInsideType(state, 'brace')); //=> false | |
914 | * ``` | |
915 | * @param {Object} `state` | |
916 | * @param {String} `type` | |
917 | * @return {Boolean} | |
918 | * @api public | |
919 | */ | |
920 | ||
921 | utils.isInsideType = function(state, type) { | |
922 | assert(isObject(state), 'expected state to be an object'); | |
923 | assert(isString(type), 'expected type to be a string'); | |
924 | ||
925 | if (!state.hasOwnProperty('inside')) { | |
926 | return false; | |
927 | } | |
928 | ||
929 | if (!state.inside.hasOwnProperty(type)) { | |
930 | return false; | |
931 | } | |
932 | ||
933 | return state.inside[type].length > 0; | |
934 | }; | |
935 | ||
936 | /** | |
937 | * Returns true if `node` is either a child or grand-child of the given `type`, | |
938 | * or `state.inside[type]` is a non-empty array. | |
939 | * | |
940 | * ```js | |
941 | * var state = { inside: {}}; | |
942 | * var node = new Node({type: 'brace'}); | |
943 | * var open = new Node({type: 'brace.open'}); | |
944 | * console.log(utils.isInside(state, open, 'brace')); //=> false | |
945 | * utils.pushNode(node, open); | |
946 | * console.log(utils.isInside(state, open, 'brace')); //=> true | |
947 | * ``` | |
948 | * @param {Object} `state` Either the `compiler.state` object, if it exists, or a user-supplied state object. | |
949 | * @param {Object} `node` Instance of [snapdragon-node][] | |
950 | * @param {String} `type` The `node.type` to check for. | |
951 | * @return {Boolean} | |
952 | * @api public | |
953 | */ | |
954 | ||
955 | utils.isInside = function(state, node, type) { | |
956 | assert(utils.isNode(node), 'expected node to be an instance of Node'); | |
957 | assert(isObject(state), 'expected state to be an object'); | |
958 | ||
959 | if (Array.isArray(type)) { | |
960 | for (var i = 0; i < type.length; i++) { | |
961 | if (utils.isInside(state, node, type[i])) { | |
962 | return true; | |
963 | } | |
964 | } | |
965 | return false; | |
966 | } | |
967 | ||
968 | var parent = node.parent; | |
969 | if (typeof type === 'string') { | |
970 | return (parent && parent.type === type) || utils.isInsideType(state, type); | |
971 | } | |
972 | ||
973 | if (typeOf(type) === 'regexp') { | |
974 | if (parent && parent.type && type.test(parent.type)) { | |
975 | return true; | |
976 | } | |
977 | ||
978 | var keys = Object.keys(state.inside); | |
979 | var len = keys.length; | |
980 | var idx = -1; | |
981 | while (++idx < len) { | |
982 | var key = keys[idx]; | |
983 | var value = state.inside[key]; | |
984 | ||
985 | if (Array.isArray(value) && value.length !== 0 && type.test(key)) { | |
986 | return true; | |
987 | } | |
988 | } | |
989 | } | |
990 | return false; | |
991 | }; | |
992 | ||
993 | /** | |
994 | * Get the last `n` element from the given `array`. Used for getting | |
995 | * a node from `node.nodes.` | |
996 | * | |
997 | * @param {Array} `array` | |
998 | * @param {Number} `n` | |
999 | * @return {undefined} | |
1000 | * @api public | |
1001 | */ | |
1002 | ||
1003 | utils.last = function(arr, n) { | |
1004 | return Array.isArray(arr) ? arr[arr.length - (n || 1)] : null; | |
1005 | }; | |
1006 | ||
1007 | utils.lastNode = function(node) { | |
1008 | return Array.isArray(node.nodes) ? utils.last(node.nodes) : null; | |
1009 | }; | |
1010 | ||
1011 | /** | |
1012 | * Cast the given `value` to an array. | |
1013 | * | |
1014 | * ```js | |
1015 | * console.log(utils.arrayify('')); | |
1016 | * //=> [] | |
1017 | * console.log(utils.arrayify('foo')); | |
1018 | * //=> ['foo'] | |
1019 | * console.log(utils.arrayify(['foo'])); | |
1020 | * //=> ['foo'] | |
1021 | * ``` | |
1022 | * @param {any} `value` | |
1023 | * @return {Array} | |
1024 | * @api public | |
1025 | */ | |
1026 | ||
1027 | utils.arrayify = function(value) { | |
1028 | if (typeof value === 'string' && value !== '') { | |
1029 | return [value]; | |
1030 | } | |
1031 | if (!Array.isArray(value)) { | |
1032 | return []; | |
1033 | } | |
1034 | return value; | |
1035 | }; | |
1036 | ||
1037 | /** | |
1038 | * Convert the given `value` to a string by joining with `,`. Useful | |
1039 | * for creating a cheerio/CSS/DOM-style selector from a list of strings. | |
1040 | * | |
1041 | * @param {any} `value` | |
1042 | * @return {Array} | |
1043 | * @api public | |
1044 | */ | |
1045 | ||
1046 | utils.stringify = function(value) { | |
1047 | return utils.arrayify(value).join(','); | |
1048 | }; | |
1049 | ||
1050 | /** | |
1051 | * Ensure that the given value is a string and call `.trim()` on it, | |
1052 | * or return an empty string. | |
1053 | * | |
1054 | * @param {String} `str` | |
1055 | * @return {String} | |
1056 | * @api public | |
1057 | */ | |
1058 | ||
1059 | utils.trim = function(str) { | |
1060 | return typeof str === 'string' ? str.trim() : ''; | |
1061 | }; | |
1062 | ||
1063 | /** | |
1064 | * Return true if value is an object | |
1065 | */ | |
1066 | ||
1067 | function isObject(value) { | |
1068 | return typeOf(value) === 'object'; | |
1069 | } | |
1070 | ||
1071 | /** | |
1072 | * Return true if value is a string | |
1073 | */ | |
1074 | ||
1075 | function isString(value) { | |
1076 | return typeof value === 'string'; | |
1077 | } | |
1078 | ||
1079 | /** | |
1080 | * Return true if value is a function | |
1081 | */ | |
1082 | ||
1083 | function isFunction(value) { | |
1084 | return typeof value === 'function'; | |
1085 | } | |
1086 | ||
1087 | /** | |
1088 | * Return true if value is an array | |
1089 | */ | |
1090 | ||
1091 | function isArray(value) { | |
1092 | return Array.isArray(value); | |
1093 | } | |
1094 | ||
1095 | /** | |
1096 | * Shim to ensure the `.append` methods work with any version of snapdragon | |
1097 | */ | |
1098 | ||
1099 | function append(compiler, value, node) { | |
1100 | if (typeof compiler.append !== 'function') { | |
1101 | return compiler.emit(value, node); | |
1102 | } | |
1103 | return compiler.append(value, node); | |
1104 | } | |
1105 | ||
1106 | /** | |
1107 | * Simplified assertion. Throws an error is `value` is falsey. | |
1108 | */ | |
1109 | ||
1110 | function assert(value, message) { | |
1111 | if (!value) throw new Error(message); | |
1112 | } | |
1113 | function expect(node, name, Node) { | |
1114 | const isNode = (Node && Node.isNode) ? Node.isNode : utils.isNode; | |
1115 | assert(isNode(node), `expected ${name} to be an instance of Node`); | |
1116 | } |
0 | { | |
1 | "name": "snapdragon-util", | |
2 | "description": "Utilities for the snapdragon parser/compiler.", | |
3 | "version": "5.0.1", | |
4 | "homepage": "https://github.com/here-be/snapdragon-util", | |
5 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", | |
6 | "contributors": [ | |
7 | "Jon Schlinkert (http://twitter.com/jonschlinkert)", | |
8 | "Rouven Weßling (www.rouvenwessling.de)" | |
9 | ], | |
10 | "repository": "here-be/snapdragon-util", | |
11 | "bugs": { | |
12 | "url": "https://github.com/here-be/snapdragon-util/issues" | |
13 | }, | |
14 | "license": "MIT", | |
15 | "files": [ | |
16 | "index.js" | |
17 | ], | |
18 | "main": "index.js", | |
19 | "engines": { | |
20 | "node": ">=6" | |
21 | }, | |
22 | "scripts": { | |
23 | "test": "mocha" | |
24 | }, | |
25 | "dependencies": { | |
26 | "kind-of": "^6.0.2" | |
27 | }, | |
28 | "devDependencies": { | |
29 | "define-property": "^2.0.0", | |
30 | "gulp": "^3.9.1", | |
31 | "gulp-eslint": "^4.0.1", | |
32 | "gulp-format-md": "^1.0.0", | |
33 | "gulp-istanbul": "^1.1.3", | |
34 | "gulp-mocha": "^5.0.0", | |
35 | "isobject": "^3.0.1", | |
36 | "mocha": "^3.5.3", | |
37 | "snapdragon": "^0.11.0" | |
38 | }, | |
39 | "keywords": [ | |
40 | "capture", | |
41 | "compile", | |
42 | "compiler", | |
43 | "convert", | |
44 | "match", | |
45 | "parse", | |
46 | "parser", | |
47 | "plugin", | |
48 | "render", | |
49 | "snapdragon", | |
50 | "snapdragonplugin", | |
51 | "transform", | |
52 | "util" | |
53 | ], | |
54 | "verb": { | |
55 | "layout": "default", | |
56 | "tasks": [ | |
57 | "readme" | |
58 | ], | |
59 | "related": { | |
60 | "list": [ | |
61 | "snapdragon-node", | |
62 | "snapdragon-position", | |
63 | "snapdragon-token" | |
64 | ] | |
65 | }, | |
66 | "plugins": [ | |
67 | "gulp-format-md" | |
68 | ], | |
69 | "lint": { | |
70 | "reflinks": true | |
71 | } | |
72 | } | |
73 | } |
0 | 'use strict'; | |
1 | ||
2 | var assert = require('assert'); | |
3 | var isObject = require('isobject'); | |
4 | var define = require('define-property'); | |
5 | var getters = ['siblings', 'index', 'first', 'last', 'prev', 'next']; | |
6 | ||
7 | /** | |
8 | * This is a shim used in the unit tests | |
9 | * to ensure that snapdragon-util works with | |
10 | * older and newer versions of snapdragon-node | |
11 | */ | |
12 | ||
13 | function isNode(node) { | |
14 | return isObject(node) && node.isNode === true; | |
15 | } | |
16 | ||
17 | module.exports = function(node) { | |
18 | ||
19 | /** | |
20 | * Define a non-enumberable property on the node instance. | |
21 | * | |
22 | * ```js | |
23 | * var node = new Node(); | |
24 | * node.define('foo', 'something non-enumerable'); | |
25 | * ``` | |
26 | * @param {String} `name` | |
27 | * @param {any} `value` | |
28 | * @return {Object} returns the node instance | |
29 | * @api public | |
30 | */ | |
31 | ||
32 | node.define = function(name, value) { | |
33 | define(this, name, value); | |
34 | return this; | |
35 | }; | |
36 | ||
37 | /** | |
38 | * Given node `foo` and node `bar`, push node `bar` onto `foo.nodes`, and | |
39 | * set `foo` as `bar.parent`. | |
40 | * | |
41 | * ```js | |
42 | * var foo = new Node({type: 'foo'}); | |
43 | * var bar = new Node({type: 'bar'}); | |
44 | * foo.push(bar); | |
45 | * ``` | |
46 | * @param {Object} `node` | |
47 | * @return {Number} Returns the length of `node.nodes` | |
48 | * @api public | |
49 | */ | |
50 | ||
51 | node.push = function(node) { | |
52 | assert(isNode(node), 'expected node to be an instance of Node'); | |
53 | define(node, 'parent', this); | |
54 | ||
55 | this.nodes = this.nodes || []; | |
56 | return this.nodes.push(node); | |
57 | }; | |
58 | ||
59 | /** | |
60 | * Given node `foo` and node `bar`, unshift node `bar` onto `foo.nodes`, and | |
61 | * set `foo` as `bar.parent`. | |
62 | * | |
63 | * ```js | |
64 | * var foo = new Node({type: 'foo'}); | |
65 | * var bar = new Node({type: 'bar'}); | |
66 | * foo.unshift(bar); | |
67 | * ``` | |
68 | * @param {Object} `node` | |
69 | * @return {Number} Returns the length of `node.nodes` | |
70 | * @api public | |
71 | */ | |
72 | ||
73 | node.unshift = function(node) { | |
74 | assert(isNode(node), 'expected node to be an instance of Node'); | |
75 | define(node, 'parent', this); | |
76 | ||
77 | this.nodes = this.nodes || []; | |
78 | return this.nodes.unshift(node); | |
79 | }; | |
80 | ||
81 | /** | |
82 | * Pop a node from `node.nodes`. | |
83 | * | |
84 | * ```js | |
85 | * var node = new Node({type: 'foo'}); | |
86 | * node.push(new Node({type: 'a'})); | |
87 | * node.push(new Node({type: 'b'})); | |
88 | * node.push(new Node({type: 'c'})); | |
89 | * node.push(new Node({type: 'd'})); | |
90 | * console.log(node.nodes.length); | |
91 | * //=> 4 | |
92 | * node.pop(); | |
93 | * console.log(node.nodes.length); | |
94 | * //=> 3 | |
95 | * ``` | |
96 | * @return {Number} Returns the popped `node` | |
97 | * @api public | |
98 | */ | |
99 | ||
100 | node.pop = function() { | |
101 | return this.nodes && this.nodes.pop(); | |
102 | }; | |
103 | ||
104 | /** | |
105 | * Shift a node from `node.nodes`. | |
106 | * | |
107 | * ```js | |
108 | * var node = new Node({type: 'foo'}); | |
109 | * node.push(new Node({type: 'a'})); | |
110 | * node.push(new Node({type: 'b'})); | |
111 | * node.push(new Node({type: 'c'})); | |
112 | * node.push(new Node({type: 'd'})); | |
113 | * console.log(node.nodes.length); | |
114 | * //=> 4 | |
115 | * node.shift(); | |
116 | * console.log(node.nodes.length); | |
117 | * //=> 3 | |
118 | * ``` | |
119 | * @return {Object} Returns the shifted `node` | |
120 | * @api public | |
121 | */ | |
122 | ||
123 | node.shift = function() { | |
124 | return this.nodes && this.nodes.shift(); | |
125 | }; | |
126 | ||
127 | /** | |
128 | * Remove `node` from `node.nodes`. | |
129 | * | |
130 | * ```js | |
131 | * node.remove(childNode); | |
132 | * ``` | |
133 | * @param {Object} `node` | |
134 | * @return {Object} Returns the removed node. | |
135 | * @api public | |
136 | */ | |
137 | ||
138 | node.remove = function(node) { | |
139 | assert(isNode(node), 'expected node to be an instance of Node'); | |
140 | this.nodes = this.nodes || []; | |
141 | var idx = node.index; | |
142 | if (idx !== -1) { | |
143 | return this.nodes.splice(idx, 1); | |
144 | } | |
145 | return null; | |
146 | }; | |
147 | }; |
0 | 'use strict'; | |
1 | ||
2 | require('mocha'); | |
3 | var assert = require('assert'); | |
4 | var Parser = require('snapdragon/lib/parser'); | |
5 | var Compiler = require('snapdragon/lib/compiler'); | |
6 | var decorate = require('./support'); | |
7 | var utils = require('..'); | |
8 | var parser; | |
9 | var ast; | |
10 | ||
11 | class Node { | |
12 | constructor(node) { | |
13 | this.define(this, 'parent', null); | |
14 | this.isNode = true; | |
15 | this.type = node.type; | |
16 | this.value = node.value; | |
17 | if (node.nodes) { | |
18 | this.nodes = node.nodes; | |
19 | } | |
20 | } | |
21 | define(key, value) { | |
22 | Object.defineProperty(this, key, { value: value }); | |
23 | return this; | |
24 | } | |
25 | get siblings() { | |
26 | return this.parent ? this.parent.nodes : null; | |
27 | } | |
28 | get last() { | |
29 | if (this.nodes && this.nodes.length) { | |
30 | return this.nodes[this.nodes.length - 1]; | |
31 | } | |
32 | } | |
33 | } | |
34 | ||
35 | describe('snapdragon-node', function() { | |
36 | beforeEach(function() { | |
37 | parser = new Parser({Node: Node}) | |
38 | .set('text', function() { | |
39 | var match = this.match(/^[a-z]+/); | |
40 | if (match) { | |
41 | return this.node(match[0]); | |
42 | } | |
43 | }) | |
44 | .set('slash', function() { | |
45 | var match = this.match(/^\//); | |
46 | if (match) { | |
47 | return this.node(match[0]); | |
48 | } | |
49 | }) | |
50 | .set('star', function() { | |
51 | var match = this.match(/^\*/); | |
52 | if (match) { | |
53 | return this.node(match[0]); | |
54 | } | |
55 | }) | |
56 | ||
57 | ast = new Node(parser.parse('a/*/c')); | |
58 | ||
59 | // console.log(ast) | |
60 | }); | |
61 | ||
62 | describe('.arrayify', function() { | |
63 | it('should cast a string to an array', function() { | |
64 | assert.deepEqual(utils.arrayify('foo'), ['foo']); | |
65 | }); | |
66 | ||
67 | it('should return an array', function() { | |
68 | assert.deepEqual(utils.arrayify(['foo']), ['foo']); | |
69 | }); | |
70 | ||
71 | it('should return an empty array when not a string or array', function() { | |
72 | assert.deepEqual(utils.arrayify(), []); | |
73 | }); | |
74 | }); | |
75 | ||
76 | describe('.stringify', function() { | |
77 | it('should return a string', function() { | |
78 | assert.equal(utils.stringify('foo'), 'foo'); | |
79 | }); | |
80 | ||
81 | it('should stringify an array', function() { | |
82 | assert.equal(utils.stringify(['foo', 'bar']), 'foo,bar'); | |
83 | }); | |
84 | }); | |
85 | ||
86 | describe('.identity', function() { | |
87 | it('should return node.value as it was created by the parser', function() { | |
88 | var res = new Compiler() | |
89 | .set('star', utils.identity) | |
90 | .set('slash', utils.identity) | |
91 | .set('text', utils.identity) | |
92 | .compile(ast); | |
93 | ||
94 | assert.equal(res.output, 'a/*/c'); | |
95 | }); | |
96 | }); | |
97 | ||
98 | describe('.noop', function() { | |
99 | it('should make a node an empty text node', function() { | |
100 | var res = new Compiler() | |
101 | .set('star', utils.noop) | |
102 | .set('slash', utils.identity) | |
103 | .set('text', utils.identity) | |
104 | .compile(ast); | |
105 | ||
106 | assert.equal(res.output, 'a//c'); | |
107 | }); | |
108 | }); | |
109 | ||
110 | describe('.append', function() { | |
111 | it('should append the specified text', function() { | |
112 | var res = new Compiler() | |
113 | .set('star', utils.append('@')) | |
114 | .set('slash', utils.append('\\')) | |
115 | .set('text', utils.identity) | |
116 | .compile(ast); | |
117 | ||
118 | assert.equal(res.output, 'a\\@\\c'); | |
119 | }); | |
120 | ||
121 | it('should use compiler.append method when it exists', function() { | |
122 | var compiler = new Compiler() | |
123 | compiler.append = compiler.emit.bind(compiler); | |
124 | ||
125 | var res = compiler.set('star', utils.append('@')) | |
126 | .set('slash', utils.append('\\')) | |
127 | .set('text', utils.identity) | |
128 | .compile(ast); | |
129 | ||
130 | assert.equal(res.output, 'a\\@\\c'); | |
131 | }); | |
132 | }); | |
133 | ||
134 | describe('.toNoop', function() { | |
135 | it('should throw an error when node is not a node', function() { | |
136 | assert.throws(function() { | |
137 | utils.toNoop(); | |
138 | }); | |
139 | }); | |
140 | ||
141 | it('should convert a node to a noop node', function() { | |
142 | utils.toNoop(ast); | |
143 | assert(!ast.nodes); | |
144 | }); | |
145 | ||
146 | it('should convert a node to a noop with the given nodes value', function() { | |
147 | utils.toNoop(ast, []); | |
148 | assert.equal(ast.nodes.length, 0); | |
149 | }); | |
150 | }); | |
151 | ||
152 | describe('.visit', function() { | |
153 | it('should throw an error when not a node', function() { | |
154 | assert.throws(function() { | |
155 | utils.visit(); | |
156 | }); | |
157 | }); | |
158 | ||
159 | it('should throw an error when node.nodes is not an array', function() { | |
160 | assert.throws(function() { | |
161 | utils.visit(new Node({type: 'foo', value: ''})); | |
162 | }); | |
163 | }); | |
164 | ||
165 | it('should visit a node with the given function', function() { | |
166 | var type = null; | |
167 | utils.visit(ast, function(node) { | |
168 | if (type === null) { | |
169 | type = node.type; | |
170 | } | |
171 | }); | |
172 | assert.equal(type, 'root'); | |
173 | }); | |
174 | }); | |
175 | ||
176 | describe('.mapVisit', function() { | |
177 | it('should throw an error when not a node', function() { | |
178 | assert.throws(function() { | |
179 | utils.mapVisit(); | |
180 | }); | |
181 | }); | |
182 | ||
183 | it('should throw an error when node.nodes is not an array', function() { | |
184 | assert.throws(function() { | |
185 | utils.mapVisit(new Node({type: 'foo', value: ''})); | |
186 | }); | |
187 | }); | |
188 | ||
189 | it('should map "visit" over node.nodes', function() { | |
190 | var type = null; | |
191 | utils.mapVisit(ast, function(node) { | |
192 | if (type === null && node.parent && node.parent.type === 'root') { | |
193 | type = node.type; | |
194 | } | |
195 | }); | |
196 | assert.equal(type, 'bos'); | |
197 | }); | |
198 | }); | |
199 | ||
200 | describe('.pushNode', function() { | |
201 | it('should throw an error when not a node', function() { | |
202 | assert.throws(function() { | |
203 | utils.pushNode(); | |
204 | }); | |
205 | }); | |
206 | ||
207 | it('should add a node to the end of node.nodes', function() { | |
208 | var node = new Node({type: 'brace'}); | |
209 | var a = new Node({type: 'a', value: 'foo'}); | |
210 | var b = new Node({type: 'b', value: 'foo'}); | |
211 | utils.pushNode(node, a); | |
212 | utils.pushNode(node, b); | |
213 | assert.equal(node.nodes[0].type, 'a'); | |
214 | assert.equal(node.nodes[1].type, 'b'); | |
215 | }); | |
216 | ||
217 | it('should work when node.push is not a function', function() { | |
218 | var node = new Node({type: 'brace'}); | |
219 | var a = new Node({type: 'a', value: 'foo'}); | |
220 | var b = new Node({type: 'b', value: 'foo'}); | |
221 | ||
222 | node.pushNode = null; | |
223 | node.push = null; | |
224 | ||
225 | utils.pushNode(node, a); | |
226 | utils.pushNode(node, b); | |
227 | assert.equal(node.nodes[0].type, 'a'); | |
228 | assert.equal(node.nodes[1].type, 'b'); | |
229 | }); | |
230 | }); | |
231 | ||
232 | describe('.unshiftNode', function() { | |
233 | it('should throw an error when parent is not a node', function() { | |
234 | assert.throws(function() { | |
235 | utils.unshiftNode(); | |
236 | }); | |
237 | }); | |
238 | ||
239 | it('should add a node to the beginning of node.nodes', function() { | |
240 | var node = new Node({type: 'brace'}); | |
241 | var a = new Node({type: 'a', value: 'foo'}); | |
242 | var b = new Node({type: 'b', value: 'foo'}); | |
243 | utils.unshiftNode(node, a); | |
244 | utils.unshiftNode(node, b); | |
245 | assert.equal(node.nodes[1].type, 'a'); | |
246 | assert.equal(node.nodes[0].type, 'b'); | |
247 | }); | |
248 | ||
249 | it('should work when node.unshift is not a function', function() { | |
250 | var node = new Node({type: 'brace'}); | |
251 | var a = new Node({type: 'a', value: 'foo'}); | |
252 | var b = new Node({type: 'b', value: 'foo'}); | |
253 | ||
254 | node.unshiftNode = null; | |
255 | node.unshift = null; | |
256 | ||
257 | utils.unshiftNode(node, a); | |
258 | utils.unshiftNode(node, b); | |
259 | assert.equal(node.nodes[1].type, 'a'); | |
260 | assert.equal(node.nodes[0].type, 'b'); | |
261 | }); | |
262 | }); | |
263 | ||
264 | describe('.popNode', function() { | |
265 | it('should throw an error when not a node', function() { | |
266 | assert.throws(function() { | |
267 | utils.popNode(); | |
268 | }); | |
269 | }); | |
270 | ||
271 | it('should pop a node from node.nodes', function() { | |
272 | var node = new Node({type: 'brace'}); | |
273 | var a = new Node({type: 'a', value: 'foo'}); | |
274 | var b = new Node({type: 'b', value: 'foo'}); | |
275 | utils.pushNode(node, a); | |
276 | utils.pushNode(node, b); | |
277 | assert.equal(node.nodes[0].type, 'a'); | |
278 | assert.equal(node.nodes[1].type, 'b'); | |
279 | ||
280 | utils.popNode(node); | |
281 | utils.popNode(node); | |
282 | assert.equal(node.nodes.length, 0); | |
283 | }); | |
284 | ||
285 | it('should work when node.pop is not a function', function() { | |
286 | var node = new Node({type: 'brace'}); | |
287 | var a = new Node({type: 'a', value: 'foo'}); | |
288 | var b = new Node({type: 'b', value: 'foo'}); | |
289 | ||
290 | node.popNode = null; | |
291 | node.pop = null; | |
292 | ||
293 | utils.pushNode(node, a); | |
294 | utils.pushNode(node, b); | |
295 | assert.equal(node.nodes[0].type, 'a'); | |
296 | assert.equal(node.nodes[1].type, 'b'); | |
297 | ||
298 | utils.popNode(node); | |
299 | utils.popNode(node); | |
300 | assert.equal(node.nodes.length, 0); | |
301 | }); | |
302 | ||
303 | it('should work when node.pop is a function', function() { | |
304 | var node = new Node({type: 'brace'}); | |
305 | var a = new Node({type: 'a', value: 'foo'}); | |
306 | var b = new Node({type: 'b', value: 'foo'}); | |
307 | ||
308 | decorate(node); | |
309 | ||
310 | utils.pushNode(node, a); | |
311 | utils.pushNode(node, b); | |
312 | assert.equal(node.nodes[0].type, 'a'); | |
313 | assert.equal(node.nodes[1].type, 'b'); | |
314 | ||
315 | utils.popNode(node); | |
316 | utils.popNode(node); | |
317 | assert.equal(node.nodes.length, 0); | |
318 | }); | |
319 | }); | |
320 | ||
321 | describe('.shiftNode', function() { | |
322 | it('should throw an error when not a node', function() { | |
323 | assert.throws(function() { | |
324 | utils.shiftNode(); | |
325 | }); | |
326 | }); | |
327 | ||
328 | it('should shift a node from node.nodes', function() { | |
329 | var node = new Node({type: 'brace'}); | |
330 | var a = new Node({type: 'a', value: 'foo'}); | |
331 | var b = new Node({type: 'b', value: 'foo'}); | |
332 | utils.pushNode(node, a); | |
333 | utils.pushNode(node, b); | |
334 | assert.equal(node.nodes[0].type, 'a'); | |
335 | assert.equal(node.nodes[1].type, 'b'); | |
336 | ||
337 | utils.shiftNode(node); | |
338 | utils.shiftNode(node); | |
339 | assert.equal(node.nodes.length, 0); | |
340 | }); | |
341 | ||
342 | it('should work when node.shift is not a function', function() { | |
343 | var node = new Node({type: 'brace'}); | |
344 | var a = new Node({type: 'a', value: 'foo'}); | |
345 | var b = new Node({type: 'b', value: 'foo'}); | |
346 | ||
347 | node.shiftNode = null; | |
348 | node.shift = null; | |
349 | ||
350 | utils.pushNode(node, a); | |
351 | utils.pushNode(node, b); | |
352 | assert.equal(node.nodes[0].type, 'a'); | |
353 | assert.equal(node.nodes[1].type, 'b'); | |
354 | ||
355 | utils.shiftNode(node); | |
356 | utils.shiftNode(node); | |
357 | assert.equal(node.nodes.length, 0); | |
358 | }); | |
359 | ||
360 | it('should work when node.shift is a function', function() { | |
361 | var node = new Node({type: 'brace'}); | |
362 | var a = new Node({type: 'a', value: 'foo'}); | |
363 | var b = new Node({type: 'b', value: 'foo'}); | |
364 | ||
365 | decorate(node); | |
366 | ||
367 | utils.pushNode(node, a); | |
368 | utils.pushNode(node, b); | |
369 | assert.equal(node.nodes[0].type, 'a'); | |
370 | assert.equal(node.nodes[1].type, 'b'); | |
371 | ||
372 | utils.shiftNode(node); | |
373 | utils.shiftNode(node); | |
374 | assert.equal(node.nodes.length, 0); | |
375 | }); | |
376 | }); | |
377 | ||
378 | describe('.removeNode', function() { | |
379 | it('should throw an error when not a node', function() { | |
380 | assert.throws(function() { | |
381 | utils.removeNode(); | |
382 | }); | |
383 | }); | |
384 | ||
385 | it('should remove a node from node.nodes', function() { | |
386 | var node = new Node({type: 'brace'}); | |
387 | var a = new Node({type: 'a', value: 'foo'}); | |
388 | var b = new Node({type: 'b', value: 'foo'}); | |
389 | utils.pushNode(node, a); | |
390 | utils.pushNode(node, b); | |
391 | assert.equal(node.nodes[0].type, 'a'); | |
392 | assert.equal(node.nodes[1].type, 'b'); | |
393 | ||
394 | utils.removeNode(node, a); | |
395 | assert.equal(node.nodes.length, 1); | |
396 | ||
397 | utils.removeNode(node, b); | |
398 | assert.equal(node.nodes.length, 0); | |
399 | }); | |
400 | ||
401 | it('should work when node.remove is not a function', function() { | |
402 | var node = new Node({type: 'brace'}); | |
403 | var a = new Node({type: 'a', value: 'foo'}); | |
404 | var b = new Node({type: 'b', value: 'foo'}); | |
405 | ||
406 | node.removeNode = null; | |
407 | node.remove = null; | |
408 | ||
409 | utils.pushNode(node, a); | |
410 | utils.pushNode(node, b); | |
411 | assert.equal(node.nodes[0].type, 'a'); | |
412 | assert.equal(node.nodes[1].type, 'b'); | |
413 | ||
414 | utils.removeNode(node, a); | |
415 | assert.equal(node.nodes.length, 1); | |
416 | ||
417 | utils.removeNode(node, b); | |
418 | assert.equal(node.nodes.length, 0); | |
419 | }); | |
420 | ||
421 | it('should work when node.remove is a function', function() { | |
422 | var node = new Node({type: 'brace'}); | |
423 | var a = new Node({type: 'a', value: 'foo'}); | |
424 | var b = new Node({type: 'b', value: 'foo'}); | |
425 | ||
426 | decorate(node); | |
427 | ||
428 | utils.pushNode(node, a); | |
429 | utils.pushNode(node, b); | |
430 | assert.equal(node.nodes[0].type, 'a'); | |
431 | assert.equal(node.nodes[1].type, 'b'); | |
432 | ||
433 | utils.removeNode(node, a); | |
434 | utils.removeNode(node, b); | |
435 | assert.equal(node.nodes.length, 0); | |
436 | }); | |
437 | ||
438 | it('should return when node.nodes does not exist', function() { | |
439 | assert.doesNotThrow(function() { | |
440 | var node = new Node({type: 'brace'}); | |
441 | utils.removeNode(node, node); | |
442 | }); | |
443 | ||
444 | assert.doesNotThrow(function() { | |
445 | var node = new Node({type: 'brace'}); | |
446 | node.removeNode = null; | |
447 | node.remove = null; | |
448 | utils.removeNode(node, node); | |
449 | }); | |
450 | }); | |
451 | ||
452 | it('should return when the given node is not in node.nodes', function() { | |
453 | assert.doesNotThrow(function() { | |
454 | var node = new Node({type: 'brace'}); | |
455 | var foo = new Node({type: 'foo'}); | |
456 | var bar = new Node({type: 'bar'}); | |
457 | utils.pushNode(node, bar); | |
458 | utils.removeNode(node, foo); | |
459 | }); | |
460 | ||
461 | assert.doesNotThrow(function() { | |
462 | var node = new Node({type: 'brace'}); | |
463 | var foo = new Node({type: 'foo'}); | |
464 | var bar = new Node({type: 'bar'}); | |
465 | node.removeNode = null; | |
466 | node.remove = null; | |
467 | utils.pushNode(node, bar); | |
468 | utils.removeNode(node, foo); | |
469 | }); | |
470 | }); | |
471 | }); | |
472 | ||
473 | describe('.addOpen', function() { | |
474 | it('should throw an error when not a node', function() { | |
475 | assert.throws(function() { | |
476 | utils.addOpen(); | |
477 | }); | |
478 | }); | |
479 | ||
480 | it('should add an open node', function() { | |
481 | var node = new Node({type: 'brace'}); | |
482 | var text = new Node({type: 'text', value: 'foo'}); | |
483 | utils.addOpen(node, Node); | |
484 | assert.equal(node.nodes[0].type, 'brace.open'); | |
485 | }); | |
486 | ||
487 | it('should work when node.unshift is a function', function() { | |
488 | var node = new Node({type: 'brace'}); | |
489 | var text = new Node({type: 'text', value: 'foo'}); | |
490 | decorate(node); | |
491 | utils.addOpen(node, Node); | |
492 | assert.equal(node.nodes[0].type, 'brace.open'); | |
493 | }); | |
494 | ||
495 | it('should work when node.unshift is not a function', function() { | |
496 | var node = new Node({type: 'brace'}); | |
497 | var text = new Node({type: 'text', value: 'foo'}); | |
498 | node.unshiftNode = null; | |
499 | node.unshift = null; | |
500 | utils.addOpen(node, Node); | |
501 | assert.equal(node.nodes[0].type, 'brace.open'); | |
502 | }); | |
503 | ||
504 | it('should take a filter function', function() { | |
505 | var node = new Node({type: 'brace'}); | |
506 | var text = new Node({type: 'text', value: 'foo'}); | |
507 | utils.addOpen(node, Node, function(node) { | |
508 | return node.type !== 'brace'; | |
509 | }); | |
510 | assert(!node.nodes); | |
511 | }); | |
512 | ||
513 | it('should use the given value on the open node', function() { | |
514 | var node = new Node({type: 'brace'}); | |
515 | var text = new Node({type: 'text', value: 'foo'}); | |
516 | utils.addOpen(node, Node, '{'); | |
517 | assert.equal(node.nodes[0].value, '{'); | |
518 | }); | |
519 | }); | |
520 | ||
521 | describe('.addClose', function() { | |
522 | it('should throw an error when not a node', function() { | |
523 | assert.throws(function() { | |
524 | utils.addClose(); | |
525 | }); | |
526 | }); | |
527 | ||
528 | it('should add a close node', function() { | |
529 | var node = new Node({type: 'brace'}); | |
530 | var text = new Node({type: 'text', value: 'foo'}); | |
531 | utils.pushNode(node, text); | |
532 | utils.addClose(node, Node); | |
533 | ||
534 | assert.equal(node.nodes[0].type, 'text'); | |
535 | assert.equal(node.nodes[1].type, 'brace.close'); | |
536 | }); | |
537 | ||
538 | it('should work when node.push is not a function', function() { | |
539 | var node = new Node({type: 'brace'}); | |
540 | var text = new Node({type: 'text', value: 'foo'}); | |
541 | node.pushNode = null; | |
542 | node.push = null; | |
543 | ||
544 | utils.pushNode(node, text); | |
545 | utils.addClose(node, Node); | |
546 | ||
547 | assert.equal(node.nodes[0].type, 'text'); | |
548 | assert.equal(node.nodes[1].type, 'brace.close'); | |
549 | }); | |
550 | ||
551 | it('should work when node.push is a function', function() { | |
552 | var node = new Node({type: 'brace'}); | |
553 | var text = new Node({type: 'text', value: 'foo'}); | |
554 | decorate(node); | |
555 | ||
556 | utils.pushNode(node, text); | |
557 | utils.addClose(node, Node); | |
558 | ||
559 | assert.equal(node.nodes[0].type, 'text'); | |
560 | assert.equal(node.nodes[1].type, 'brace.close'); | |
561 | }); | |
562 | ||
563 | it('should take a filter function', function() { | |
564 | var node = new Node({type: 'brace'}); | |
565 | var text = new Node({type: 'text', value: 'foo'}); | |
566 | utils.addClose(node, Node, function(node) { | |
567 | return node.type !== 'brace'; | |
568 | }); | |
569 | assert(!node.nodes); | |
570 | }); | |
571 | ||
572 | it('should use the given value on the close node', function() { | |
573 | var node = new Node({type: 'brace'}); | |
574 | var text = new Node({type: 'text', value: 'foo'}); | |
575 | utils.addClose(node, Node, '}'); | |
576 | assert.equal(node.nodes[0].value, '}'); | |
577 | }); | |
578 | }); | |
579 | ||
580 | describe('.wrapNodes', function() { | |
581 | it('should throw an error when not a node', function() { | |
582 | assert.throws(function() { | |
583 | utils.wrapNodes(); | |
584 | }); | |
585 | }); | |
586 | ||
587 | it('should add an open node', function() { | |
588 | var node = new Node({type: 'brace'}); | |
589 | var text = new Node({type: 'text', value: 'foo'}); | |
590 | utils.wrapNodes(node, Node); | |
591 | ||
592 | assert.equal(node.nodes[0].type, 'brace.open'); | |
593 | }); | |
594 | ||
595 | it('should add a close node', function() { | |
596 | var node = new Node({type: 'brace'}); | |
597 | var text = new Node({type: 'text', value: 'foo'}); | |
598 | utils.pushNode(node, text); | |
599 | utils.wrapNodes(node, Node); | |
600 | ||
601 | assert.equal(node.nodes[0].type, 'brace.open'); | |
602 | assert.equal(node.nodes[1].type, 'text'); | |
603 | assert.equal(node.nodes[2].type, 'brace.close'); | |
604 | }); | |
605 | }); | |
606 | ||
607 | describe('.isEmpty', function() { | |
608 | it('should throw an error when not a node', function() { | |
609 | assert.throws(function() { | |
610 | utils.isEmpty(); | |
611 | }); | |
612 | }); | |
613 | ||
614 | it('should return true node.value is an empty string', function() { | |
615 | assert(utils.isEmpty(new Node({type: 'text', value: ''}))); | |
616 | }); | |
617 | ||
618 | it('should return true node.value is undefined', function() { | |
619 | assert(utils.isEmpty(new Node({type: 'text'}))); | |
620 | }); | |
621 | ||
622 | it('should return true when node.nodes is empty', function() { | |
623 | var foo = new Node({type: 'foo'}); | |
624 | var bar = new Node({type: 'text', value: 'bar'}); | |
625 | utils.pushNode(foo, bar); | |
626 | assert(!utils.isEmpty(foo)); | |
627 | utils.shiftNode(foo); | |
628 | assert(utils.isEmpty(foo)); | |
629 | }); | |
630 | ||
631 | it('should return true when node.nodes is all non-text nodes', function() { | |
632 | var node = new Node({type: 'parent'}); | |
633 | var foo = new Node({type: 'foo'}); | |
634 | var bar = new Node({type: 'bar'}); | |
635 | var baz = new Node({type: 'baz'}); | |
636 | utils.pushNode(node, foo); | |
637 | utils.pushNode(node, bar); | |
638 | utils.pushNode(node, baz); | |
639 | assert(utils.isEmpty(foo)); | |
640 | }); | |
641 | ||
642 | it('should return call a custom function if only one node exists', function() { | |
643 | var foo = new Node({type: 'foo'}); | |
644 | var text = new Node({type: 'text', value: ''}); | |
645 | utils.pushNode(foo, text); | |
646 | assert(utils.isEmpty(foo, node => !node.value)); | |
647 | }); | |
648 | ||
649 | it('should return true when only open and close nodes exist', function() { | |
650 | var brace = new Node({type: 'brace'}); | |
651 | var open = new Node({type: 'brace.open'}); | |
652 | var close = new Node({type: 'brace.close'}); | |
653 | utils.pushNode(brace, open); | |
654 | utils.pushNode(brace, close); | |
655 | assert(utils.isEmpty(brace)); | |
656 | }); | |
657 | ||
658 | it('should call a custom function on "middle" nodes (1)', function() { | |
659 | var brace = new Node({type: 'brace'}); | |
660 | var open = new Node({type: 'brace.open'}); | |
661 | var text = new Node({type: 'text', value: ''}); | |
662 | var close = new Node({type: 'brace.close'}); | |
663 | utils.pushNode(brace, open); | |
664 | utils.pushNode(brace, text); | |
665 | utils.pushNode(brace, text); | |
666 | utils.pushNode(brace, text); | |
667 | utils.pushNode(brace, close); | |
668 | assert(utils.isEmpty(brace, function(node) { | |
669 | if (node.nodes && node.nodes.length === 0) { | |
670 | return true; | |
671 | } | |
672 | return !utils.value(node); | |
673 | })); | |
674 | }); | |
675 | ||
676 | it('should call a custom function on "middle" nodes (2)', function() { | |
677 | var brace = new Node({type: 'brace'}); | |
678 | var open = new Node({type: 'brace.open'}); | |
679 | var text = new Node({type: 'text', value: ''}); | |
680 | var close = new Node({type: 'brace.close'}); | |
681 | utils.pushNode(brace, open); | |
682 | utils.pushNode(brace, text); | |
683 | utils.pushNode(brace, text); | |
684 | utils.pushNode(brace, text); | |
685 | utils.pushNode(brace, close); | |
686 | assert(!utils.isEmpty(brace, function(node) { | |
687 | return node.parent.nodes.length === 0; | |
688 | })); | |
689 | }); | |
690 | ||
691 | it('should call a custom function on "middle" nodes (3)', function() { | |
692 | var brace = new Node({type: 'brace'}); | |
693 | var open = new Node({type: 'brace.open'}); | |
694 | var text = new Node({type: 'text', value: 'foo'}); | |
695 | var close = new Node({type: 'brace.close'}); | |
696 | utils.pushNode(brace, open); | |
697 | utils.pushNode(brace, text); | |
698 | utils.pushNode(brace, close); | |
699 | assert(!utils.isEmpty(brace, function(node) { | |
700 | if (node.type !== 'text') { | |
701 | return false; | |
702 | } | |
703 | return node.value.trim() === ''; | |
704 | })); | |
705 | }); | |
706 | ||
707 | it('should call a custom function on "middle" nodes (4)', function() { | |
708 | var brace = new Node({type: 'brace'}); | |
709 | var open = new Node({type: 'brace.open'}); | |
710 | var empty = new Node({type: 'text', value: ''}); | |
711 | var text = new Node({type: 'text', value: 'foo'}); | |
712 | var close = new Node({type: 'brace.close'}); | |
713 | utils.pushNode(brace, open); | |
714 | utils.pushNode(brace, empty); | |
715 | utils.pushNode(brace, empty); | |
716 | utils.pushNode(brace, empty); | |
717 | utils.pushNode(brace, empty); | |
718 | utils.pushNode(brace, text); | |
719 | utils.pushNode(brace, close); | |
720 | assert(!utils.isEmpty(brace, function(node) { | |
721 | if (node.type !== 'text') { | |
722 | return false; | |
723 | } | |
724 | return node.value.trim() === ''; | |
725 | })); | |
726 | }); | |
727 | }); | |
728 | ||
729 | describe('.isType', function() { | |
730 | it('should throw an error when matcher is invalid', function() { | |
731 | assert.throws(function() { | |
732 | utils.isType(new Node({type: 'foo'})); | |
733 | }); | |
734 | }); | |
735 | ||
736 | it('should return false if the node is not the given type', function() { | |
737 | assert(!utils.isType()); | |
738 | assert(!utils.isType({}, 'root')); | |
739 | }); | |
740 | ||
741 | it('should return true if the node is the given type', function() { | |
742 | assert(utils.isType(ast, 'root')); | |
743 | assert(utils.isType(ast.last, 'eos')); | |
744 | }); | |
745 | }); | |
746 | ||
747 | describe('.isInsideType', function() { | |
748 | it('should throw an error when parent is not a node', function() { | |
749 | assert.throws(function() { | |
750 | utils.isInsideType(); | |
751 | }); | |
752 | }); | |
753 | ||
754 | it('should throw an error when child not a node', function() { | |
755 | assert.throws(function() { | |
756 | utils.isInsideType(new Node({type: 'foo'})); | |
757 | }); | |
758 | }); | |
759 | ||
760 | it('should return false when state.inside is not an object', function() { | |
761 | var state = {}; | |
762 | var node = new Node({type: 'brace'}); | |
763 | assert(!utils.isInsideType(state, 'brace')); | |
764 | }); | |
765 | ||
766 | it('should return false when state.inside[type] is not an object', function() { | |
767 | var state = {inside: {}}; | |
768 | var node = new Node({type: 'brace'}); | |
769 | assert(!utils.isInsideType(state, 'brace')); | |
770 | }); | |
771 | ||
772 | it('should return true when state has the given type', function() { | |
773 | var state = { inside: {}}; | |
774 | var node = new Node({type: 'brace'}); | |
775 | utils.addType(state, node); | |
776 | assert(utils.isInsideType(state, 'brace')); | |
777 | }); | |
778 | ||
779 | it('should return false when state does not have the given type', function() { | |
780 | var state = { inside: {}}; | |
781 | var node = new Node({type: 'brace'}); | |
782 | ||
783 | utils.addType(state, node); | |
784 | assert(utils.isInsideType(state, 'brace')); | |
785 | ||
786 | utils.removeType(state, node); | |
787 | assert(!utils.isInsideType(state, 'brace')); | |
788 | }); | |
789 | }); | |
790 | ||
791 | describe('.isInside', function() { | |
792 | it('should throw an error when parent is not a node', function() { | |
793 | assert.throws(function() { | |
794 | utils.isInside(); | |
795 | }); | |
796 | }); | |
797 | ||
798 | it('should throw an error when child not a node', function() { | |
799 | assert.throws(function() { | |
800 | utils.isInside(new Node({type: 'foo'})); | |
801 | }); | |
802 | }); | |
803 | ||
804 | it('should return false when state.inside is not an object', function() { | |
805 | var state = {}; | |
806 | var node = new Node({type: 'brace'}); | |
807 | assert(!utils.isInside(state, node, 'brace')); | |
808 | }); | |
809 | ||
810 | it('should return false when state.inside[type] is not an object', function() { | |
811 | var state = {inside: {}}; | |
812 | var node = new Node({type: 'brace'}); | |
813 | assert(!utils.isInside(state, node, 'brace')); | |
814 | }); | |
815 | ||
816 | it('should return true when state has the given type', function() { | |
817 | var state = { inside: {}}; | |
818 | var node = new Node({type: 'brace'}); | |
819 | utils.addType(state, node); | |
820 | assert(utils.isInside(state, node, 'brace')); | |
821 | }); | |
822 | ||
823 | it('should return true when state has one of the given types', function() { | |
824 | var state = { inside: {}}; | |
825 | var node = new Node({type: 'brace'}); | |
826 | utils.addType(state, node); | |
827 | assert(utils.isInside(state, node, ['foo', 'brace'])); | |
828 | }); | |
829 | ||
830 | it('should return false when state does not have one of the given types', function() { | |
831 | var state = { inside: {}}; | |
832 | var node = new Node({type: 'brace'}); | |
833 | utils.addType(state, node); | |
834 | assert(!utils.isInside(state, node, ['foo', 'bar'])); | |
835 | }); | |
836 | ||
837 | it('should return true when a regex matches a type', function() { | |
838 | var state = { inside: {}}; | |
839 | var node = new Node({type: 'brace'}); | |
840 | utils.addType(state, node); | |
841 | assert(utils.isInside(state, node, /(foo|brace)/)); | |
842 | }); | |
843 | ||
844 | it('should return true when the type matches parent.type', function() { | |
845 | var state = {}; | |
846 | var brace = new Node({type: 'brace'}); | |
847 | var node = new Node({type: 'brace.open'}); | |
848 | utils.pushNode(brace, node); | |
849 | assert(utils.isInside(state, node, 'brace')); | |
850 | }); | |
851 | ||
852 | it('should return true when regex matches parent.type', function() { | |
853 | var state = {}; | |
854 | var brace = new Node({type: 'brace'}); | |
855 | var node = new Node({type: 'brace.open'}); | |
856 | utils.pushNode(brace, node); | |
857 | assert(utils.isInside(state, node, /(foo|brace)/)); | |
858 | }); | |
859 | ||
860 | it('should return false when a regex does not match a type', function() { | |
861 | var state = { inside: {}}; | |
862 | var node = new Node({type: 'brace'}); | |
863 | utils.addType(state, node); | |
864 | assert(!utils.isInside(state, node, /(foo|bar)/)); | |
865 | }); | |
866 | ||
867 | it('should return false when type is invalie', function() { | |
868 | var state = { inside: {}}; | |
869 | var node = new Node({type: 'brace'}); | |
870 | utils.addType(state, node); | |
871 | assert(!utils.isInside(state, node, null)); | |
872 | }); | |
873 | ||
874 | it('should return false when state does not have the given type', function() { | |
875 | var state = { inside: {}}; | |
876 | var node = new Node({type: 'brace'}); | |
877 | ||
878 | utils.addType(state, node); | |
879 | assert(utils.isInside(state, node, 'brace')); | |
880 | ||
881 | utils.removeType(state, node); | |
882 | assert(!utils.isInside(state, node, 'brace')); | |
883 | }); | |
884 | }); | |
885 | ||
886 | describe('.hasType', function() { | |
887 | it('should return true if node.nodes has the given type', function() { | |
888 | assert(utils.hasType(ast, 'text')); | |
889 | assert(!utils.hasType(ast, 'foo')); | |
890 | }); | |
891 | ||
892 | it('should return false when node.nodes does not exist', function() { | |
893 | assert(!utils.hasType(new Node({type: 'foo'}))); | |
894 | }); | |
895 | }); | |
896 | ||
897 | describe('.firstOfType', function() { | |
898 | it('should throw an error when not a node', function() { | |
899 | assert.throws(function() { | |
900 | utils.firstOfType(); | |
901 | }); | |
902 | }); | |
903 | ||
904 | it('should get the first node of the given type', function() { | |
905 | var node = utils.firstOfType(ast.nodes, 'text'); | |
906 | assert.equal(node.type, 'text'); | |
907 | }); | |
908 | }); | |
909 | ||
910 | describe('.last', function() { | |
911 | it('should get the last node', function() { | |
912 | assert.equal(utils.last(ast.nodes).type, 'eos'); | |
913 | }); | |
914 | }); | |
915 | ||
916 | describe('.findNode', function() { | |
917 | it('should get the node with the given type', function() { | |
918 | var text = utils.findNode(ast.nodes, 'text'); | |
919 | assert.equal(text.type, 'text'); | |
920 | }); | |
921 | ||
922 | it('should get the node matching the given regex', function() { | |
923 | var text = utils.findNode(ast.nodes, /text/); | |
924 | assert.equal(text.type, 'text'); | |
925 | }); | |
926 | ||
927 | it('should get the first matching node', function() { | |
928 | var node = utils.findNode(ast.nodes, [/text/, 'bos']); | |
929 | assert.equal(node.type, 'bos'); | |
930 | ||
931 | node = utils.findNode(ast.nodes, [/text/]); | |
932 | assert.equal(node.type, 'text'); | |
933 | }); | |
934 | ||
935 | it('should get the node at the given index', function() { | |
936 | var bos = utils.findNode(ast.nodes, 0); | |
937 | assert.equal(bos.type, 'bos'); | |
938 | ||
939 | var text = utils.findNode(ast.nodes, 1); | |
940 | assert.equal(text.type, 'text'); | |
941 | }); | |
942 | ||
943 | it('should return null when node does not exist', function() { | |
944 | assert.equal(utils.findNode(new Node({type: 'foo'})), null); | |
945 | }); | |
946 | }); | |
947 | ||
948 | describe('.removeNode', function() { | |
949 | it('should throw an error when parent is not a node', function() { | |
950 | assert.throws(function() { | |
951 | utils.removeNode(); | |
952 | }); | |
953 | }); | |
954 | ||
955 | it('should remove a node from parent.nodes', function() { | |
956 | var brace = new Node({type: 'brace'}); | |
957 | var open = new Node({type: 'brace.open'}); | |
958 | var foo = new Node({type: 'foo'}); | |
959 | var bar = new Node({type: 'bar'}); | |
960 | var baz = new Node({type: 'baz'}); | |
961 | var qux = new Node({type: 'qux'}); | |
962 | var close = new Node({type: 'brace.close'}); | |
963 | utils.pushNode(brace, open); | |
964 | utils.pushNode(brace, foo); | |
965 | utils.pushNode(brace, bar); | |
966 | utils.pushNode(brace, baz); | |
967 | utils.pushNode(brace, qux); | |
968 | utils.pushNode(brace, close); | |
969 | ||
970 | assert.equal(brace.nodes.length, 6); | |
971 | assert.equal(brace.nodes[0].type, 'brace.open'); | |
972 | assert.equal(brace.nodes[1].type, 'foo'); | |
973 | assert.equal(brace.nodes[2].type, 'bar'); | |
974 | assert.equal(brace.nodes[3].type, 'baz'); | |
975 | assert.equal(brace.nodes[4].type, 'qux'); | |
976 | assert.equal(brace.nodes[5].type, 'brace.close'); | |
977 | ||
978 | // remove node | |
979 | utils.removeNode(brace, bar); | |
980 | assert.equal(brace.nodes.length, 5); | |
981 | assert.equal(brace.nodes[0].type, 'brace.open'); | |
982 | assert.equal(brace.nodes[1].type, 'foo'); | |
983 | assert.equal(brace.nodes[2].type, 'baz'); | |
984 | assert.equal(brace.nodes[3].type, 'qux'); | |
985 | assert.equal(brace.nodes[4].type, 'brace.close'); | |
986 | }); | |
987 | }); | |
988 | ||
989 | describe('.isOpen', function() { | |
990 | it('should be true if node is an ".open" node', function() { | |
991 | var node = new Node({type: 'foo.open'}); | |
992 | assert(utils.isOpen(node)); | |
993 | }); | |
994 | ||
995 | it('should be false if node is not an ".open" node', function() { | |
996 | var node = new Node({type: 'foo'}); | |
997 | assert(!utils.isOpen(node)); | |
998 | }); | |
999 | }); | |
1000 | ||
1001 | describe('.isClose', function() { | |
1002 | it('should be true if node is a ".close" node', function() { | |
1003 | var node = new Node({type: 'foo.close'}); | |
1004 | assert(utils.isClose(node)); | |
1005 | }); | |
1006 | ||
1007 | it('should be false if node is not a ".close" node', function() { | |
1008 | var node = new Node({type: 'foo'}); | |
1009 | assert(!utils.isClose(node)); | |
1010 | }); | |
1011 | }); | |
1012 | ||
1013 | describe('.hasOpen', function() { | |
1014 | it('should throw an error when not a node', function() { | |
1015 | assert.throws(function() { | |
1016 | utils.hasOpen(); | |
1017 | }); | |
1018 | }); | |
1019 | ||
1020 | it('should be true if node has an ".open" node', function() { | |
1021 | var parent = new Node({type: 'foo'}); | |
1022 | var node = new Node({type: 'foo.open'}); | |
1023 | utils.pushNode(parent, node); | |
1024 | assert(utils.hasOpen(parent)); | |
1025 | }); | |
1026 | ||
1027 | it('should be false if does not have an ".open" node', function() { | |
1028 | var parent = new Node({type: 'foo'}); | |
1029 | assert(!utils.hasOpen(parent)); | |
1030 | }); | |
1031 | }); | |
1032 | ||
1033 | describe('.hasClose', function() { | |
1034 | it('should throw an error when not a node', function() { | |
1035 | assert.throws(function() { | |
1036 | utils.hasClose(); | |
1037 | }); | |
1038 | }); | |
1039 | ||
1040 | it('should be true if node has a ".close" node', function() { | |
1041 | var parent = new Node({type: 'foo'}); | |
1042 | var open = new Node({type: 'foo.open'}); | |
1043 | var close = new Node({type: 'foo.close'}); | |
1044 | utils.pushNode(parent, open); | |
1045 | utils.pushNode(parent, close); | |
1046 | assert(utils.hasClose(parent)); | |
1047 | }); | |
1048 | ||
1049 | it('should be false if does not have a ".close" node', function() { | |
1050 | var parent = new Node({type: 'foo'}); | |
1051 | assert(!utils.hasClose(parent)); | |
1052 | }); | |
1053 | }); | |
1054 | ||
1055 | describe('.hasOpenAndClose', function() { | |
1056 | it('should throw an error when not a node', function() { | |
1057 | assert.throws(function() { | |
1058 | utils.hasOpenAndClose(); | |
1059 | }); | |
1060 | }); | |
1061 | ||
1062 | it('should be true if node has ".open" and ".close" nodes', function() { | |
1063 | var parent = new Node({type: 'foo'}); | |
1064 | var open = new Node({type: 'foo.open'}); | |
1065 | var close = new Node({type: 'foo.close'}); | |
1066 | utils.pushNode(parent, open); | |
1067 | utils.pushNode(parent, close); | |
1068 | assert(utils.hasOpenAndClose(parent)); | |
1069 | }); | |
1070 | ||
1071 | it('should be false if does not have a ".close" node', function() { | |
1072 | var parent = new Node({type: 'foo'}); | |
1073 | var open = new Node({type: 'foo.open'}); | |
1074 | utils.pushNode(parent, open); | |
1075 | assert(!utils.hasOpenAndClose(parent)); | |
1076 | }); | |
1077 | ||
1078 | it('should be false if does not have an ".open" node', function() { | |
1079 | var parent = new Node({type: 'foo'}); | |
1080 | var close = new Node({type: 'foo.close'}); | |
1081 | utils.pushNode(parent, close); | |
1082 | assert(!utils.hasOpenAndClose(parent)); | |
1083 | }); | |
1084 | }); | |
1085 | ||
1086 | describe('.pushNode', function() { | |
1087 | it('should throw an error when parent is not a node', function() { | |
1088 | assert.throws(function() { | |
1089 | utils.pushNode(); | |
1090 | }); | |
1091 | }); | |
1092 | ||
1093 | it('should add a node to `node.nodes`', function() { | |
1094 | var node = new Node({type: 'foo'}); | |
1095 | utils.pushNode(ast, node); | |
1096 | assert.equal(ast.last.type, 'foo'); | |
1097 | }); | |
1098 | ||
1099 | it('should set the parent on the given node', function() { | |
1100 | var node = new Node({type: 'foo'}); | |
1101 | utils.pushNode(ast, node); | |
1102 | assert.equal(node.parent.type, 'root'); | |
1103 | }); | |
1104 | ||
1105 | it('should set the parent.nodes as node.siblings', function() { | |
1106 | var node = new Node({type: 'foo'}); | |
1107 | assert.equal(node.siblings, null); | |
1108 | utils.pushNode(ast, node); | |
1109 | assert.equal(node.siblings.length, 8); | |
1110 | }); | |
1111 | }); | |
1112 | ||
1113 | describe('.addType', function() { | |
1114 | it('should throw an error when state is not given', function() { | |
1115 | assert.throws(function() { | |
1116 | utils.addType(); | |
1117 | }); | |
1118 | }); | |
1119 | ||
1120 | it('should throw an error when a node is not passed', function() { | |
1121 | assert.throws(function() { | |
1122 | utils.addType({}); | |
1123 | }); | |
1124 | }); | |
1125 | ||
1126 | it('should add the type to the state.inside array', function() { | |
1127 | var state = {}; | |
1128 | var node = new Node({type: 'brace'}); | |
1129 | utils.addType(state, node); | |
1130 | assert(state.inside); | |
1131 | assert(state.inside.brace); | |
1132 | assert.equal(state.inside.brace.length, 1); | |
1133 | }); | |
1134 | ||
1135 | it('should add the type based on parent.type', function() { | |
1136 | var state = {}; | |
1137 | var parent = new Node({type: 'brace'}); | |
1138 | var node = new Node({type: 'brace.open'}); | |
1139 | utils.pushNode(parent, node); | |
1140 | utils.addType(state, node); | |
1141 | assert(state.inside); | |
1142 | assert(state.inside.brace); | |
1143 | assert.equal(state.inside.brace.length, 1); | |
1144 | }); | |
1145 | }); | |
1146 | ||
1147 | describe('.removeType', function() { | |
1148 | it('should throw an error when state is not given', function() { | |
1149 | assert.throws(function() { | |
1150 | utils.removeType(); | |
1151 | }); | |
1152 | }); | |
1153 | ||
1154 | it('should throw an error when a node is not passed', function() { | |
1155 | assert.throws(function() { | |
1156 | utils.removeType({}); | |
1157 | }); | |
1158 | }); | |
1159 | ||
1160 | it('should add a state.inside object', function() { | |
1161 | var state = {}; | |
1162 | var node = new Node({type: 'brace'}); | |
1163 | utils.addType(state, node); | |
1164 | assert(state.inside); | |
1165 | }); | |
1166 | ||
1167 | it('should add a type array to the state.inside object', function() { | |
1168 | var state = {}; | |
1169 | var node = new Node({type: 'brace'}); | |
1170 | utils.addType(state, node); | |
1171 | assert(state.inside); | |
1172 | assert(Array.isArray(state.inside.brace)); | |
1173 | }); | |
1174 | ||
1175 | it('should add the node to the state.inside type array', function() { | |
1176 | var state = {}; | |
1177 | var node = new Node({type: 'brace'}); | |
1178 | utils.addType(state, node); | |
1179 | assert(state.inside); | |
1180 | assert(state.inside.brace); | |
1181 | assert.equal(state.inside.brace.length, 1); | |
1182 | utils.removeType(state, node); | |
1183 | assert.equal(state.inside.brace.length, 0); | |
1184 | }); | |
1185 | ||
1186 | it('should use a type array if it already exists', function() { | |
1187 | var state = { inside: { brace: [new Node({type: 'brace.open'})] }}; | |
1188 | var node = new Node({type: 'brace'}); | |
1189 | utils.addType(state, node); | |
1190 | assert(state.inside); | |
1191 | assert(state.inside.brace); | |
1192 | assert.equal(state.inside.brace.length, 2); | |
1193 | utils.removeType(state, node); | |
1194 | assert.equal(state.inside.brace.length, 1); | |
1195 | }); | |
1196 | ||
1197 | it('should remove the type based on parent.type', function() { | |
1198 | var state = { inside: { brace: [new Node({type: 'brace.open'})] }}; | |
1199 | var parent = new Node({type: 'brace'}); | |
1200 | var node = new Node({type: 'brace.open'}); | |
1201 | utils.pushNode(parent, node); | |
1202 | utils.addType(state, node); | |
1203 | assert(state.inside); | |
1204 | assert(state.inside.brace); | |
1205 | assert.equal(state.inside.brace.length, 2); | |
1206 | utils.removeType(state, node); | |
1207 | assert.equal(state.inside.brace.length, 1); | |
1208 | }); | |
1209 | ||
1210 | it('should throw an error when state.inside does not exist', function() { | |
1211 | var state = {}; | |
1212 | var node = new Node({type: 'brace'}); | |
1213 | assert.throws(function() { | |
1214 | utils.removeType(state, node); | |
1215 | }); | |
1216 | }); | |
1217 | ||
1218 | it('should just return when state.inside type does not exist', function() { | |
1219 | var state = {inside: {}}; | |
1220 | var node = new Node({type: 'brace'}); | |
1221 | utils.removeType(state, node); | |
1222 | }); | |
1223 | }); | |
1224 | }); |