pax_global_header 0000666 0000000 0000000 00000000064 13376213533 0014520 g ustar 00root root 0000000 0000000 52 comment=bd9c41060c9a31f550847b3095148085546db349
snapdragon-node-3.0.0/ 0000775 0000000 0000000 00000000000 13376213533 0014577 5 ustar 00root root 0000000 0000000 snapdragon-node-3.0.0/.editorconfig 0000664 0000000 0000000 00000000441 13376213533 0017253 0 ustar 00root root 0000000 0000000 # http://editorconfig.org/
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[{**/{actual,fixtures,expected,templates}/**,*.md}]
trim_trailing_whitespace = false
insert_final_newline = false
snapdragon-node-3.0.0/.eslintrc.json 0000664 0000000 0000000 00000007325 13376213533 0017402 0 ustar 00root root 0000000 0000000 {
"extends": [
"eslint:recommended"
],
"env": {
"browser": false,
"es6": true,
"node": true,
"mocha": true
},
"parserOptions":{
"ecmaVersion": 9,
"sourceType": "module",
"ecmaFeatures": {
"modules": true,
"experimentalObjectRestSpread": true
}
},
"globals": {
"document": false,
"navigator": false,
"window": false
},
"rules": {
"accessor-pairs": 2,
"arrow-spacing": [2, { "before": true, "after": true }],
"block-spacing": [2, "always"],
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"comma-dangle": [2, "never"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"constructor-super": 2,
"curly": [2, "multi-line"],
"dot-location": [2, "property"],
"eol-last": 2,
"eqeqeq": [2, "allow-null"],
"generator-star-spacing": [2, { "before": true, "after": true }],
"handle-callback-err": [2, "^(err|error)$" ],
"indent": [2, 2, { "SwitchCase": 1 }],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"keyword-spacing": [2, { "before": true, "after": true }],
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
"new-parens": 2,
"no-array-constructor": 2,
"no-caller": 2,
"no-class-assign": 2,
"no-cond-assign": 2,
"no-const-assign": 2,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-dupe-args": 2,
"no-dupe-class-members": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty-character-class": 2,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": [2, "functions"],
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-label-var": 2,
"no-labels": 2,
"no-lone-blocks": 2,
"no-mixed-spaces-and-tabs": 2,
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [2, { "max": 1 }],
"no-native-reassign": 0,
"no-negated-in-lhs": 2,
"no-new": 2,
"no-new-func": 2,
"no-new-object": 2,
"no-new-require": 2,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-proto": 0,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-return-assign": 2,
"no-self-compare": 2,
"no-sequences": 2,
"no-shadow-restricted-names": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-this-before-super": 2,
"no-throw-literal": 2,
"no-trailing-spaces": 0,
"no-undef": 2,
"no-undef-init": 2,
"no-unexpected-multiline": 2,
"no-unneeded-ternary": [2, { "defaultAssignment": false }],
"no-unreachable": 2,
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
"no-useless-call": 0,
"no-with": 2,
"one-var": [0, { "initialized": "never" }],
"operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }],
"padded-blocks": [0, "never"],
"quotes": [2, "single", "avoid-escape"],
"radix": 2,
"semi": [2, "always"],
"semi-spacing": [2, { "before": false, "after": true }],
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, "never"],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
"use-isnan": 2,
"valid-typeof": 2,
"wrap-iife": [2, "any"],
"yoda": [2, "never"]
}
}
snapdragon-node-3.0.0/.gitattributes 0000664 0000000 0000000 00000000200 13376213533 0017462 0 ustar 00root root 0000000 0000000 # Enforce Unix newlines
* text eol=lf
# binaries
*.ai binary
*.psd binary
*.jpg binary
*.gif binary
*.png binary
*.jpeg binary
snapdragon-node-3.0.0/.github/ 0000775 0000000 0000000 00000000000 13376213533 0016137 5 ustar 00root root 0000000 0000000 snapdragon-node-3.0.0/.github/contributing.md 0000664 0000000 0000000 00000006341 13376213533 0021174 0 ustar 00root root 0000000 0000000 # Contributing to snapdragon-node
First and foremost, thank you! We appreciate that you want to contribute to snapdragon-node, your time is valuable, and your contributions mean a lot to us.
**What does "contributing" mean?**
Creating an issue is the simplest form of contributing to a project. But there are many ways to contribute, including the following:
- Updating or correcting documentation
- Feature requests
- Bug reports
If you'd like to learn more about contributing in general, the [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing) has a lot of useful information.
**Showing support for snapdragon-node**
Please keep in mind that open source software is built by people like you, who spend their free time creating things the rest the community can use.
Don't have time to contribute? No worries, here are some other ways to show your support for snapdragon-node:
- star the [project](https://github.com/jonschlinkert/snapdragon-node)
- tweet your support for snapdragon-node
## Issues
### Before creating an issue
Please try to determine if the issue is caused by an underlying library, and if so, create the issue there. Sometimes this is difficult to know. We only ask that you attempt to give a reasonable attempt to find out. Oftentimes the readme will have advice about where to go to create issues.
Try to follow these guidelines
- **Investigate the issue**:
- **Check the readme** - oftentimes you will find notes about creating issues, and where to go depending on the type of issue.
- Create the issue in the appropriate repository.
### Creating an issue
Please be as descriptive as possible when creating an issue. Give us the information we need to successfully answer your question or address your issue by answering the following in your issue:
- **version**: please note the version of snapdragon-node are you using
- **extensions, plugins, helpers, etc** (if applicable): please list any extensions you're using
- **error messages**: please paste any error messages into the issue, or a [gist](https://gist.github.com/)
## Above and beyond
Here are some tips for creating idiomatic issues. Taking just a little bit extra time will make your issue easier to read, easier to resolve, more likely to be found by others who have the same or similar issue in the future.
- read the [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing)
- take some time to learn basic markdown. This [markdown cheatsheet](https://gist.github.com/jonschlinkert/5854601) is super helpful, as is the GitHub guide to [basic markdown](https://help.github.com/articles/markdown-basics/).
- Learn about [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/). And if you want to really go above and beyond, read [mastering markdown](https://guides.github.com/features/mastering-markdown/).
- use backticks to wrap code. This ensures that code will retain its format, making it much more readable to others
- use syntax highlighting by adding the correct language name after the first "code fence"
[node-glob]: https://github.com/isaacs/node-glob
[micromatch]: https://github.com/jonschlinkert/micromatch
[so]: http://stackoverflow.com/questions/tagged/snapdragon-node snapdragon-node-3.0.0/.gitignore 0000664 0000000 0000000 00000000456 13376213533 0016574 0 ustar 00root root 0000000 0000000 # always ignore files
*.DS_Store
.idea
.vscode
*.sublime-*
# test related, or directories generated by tests
test/actual
actual
coverage
.nyc*
# npm
node_modules
npm-debug.log
# yarn
yarn.lock
yarn-error.log
# misc
_gh_pages
_draft
_drafts
bower_components
vendor
temp
tmp
TODO.md
package-lock.json snapdragon-node-3.0.0/.npmrc 0000664 0000000 0000000 00000000022 13376213533 0015711 0 ustar 00root root 0000000 0000000 package-lock=false snapdragon-node-3.0.0/.travis.yml 0000664 0000000 0000000 00000000224 13376213533 0016706 0 ustar 00root root 0000000 0000000 sudo: false
os:
- linux
- osx
- windows
language: node_js
node_js:
- node
- '11'
- '10'
- '9'
- '8'
- '7'
- '6'
- '5'
- '4'
snapdragon-node-3.0.0/.verb.md 0000664 0000000 0000000 00000004775 13376213533 0016152 0 ustar 00root root 0000000 0000000 ## Usage
```js
const Node = require('snapdragon-node');
// either pass on object with "type" and (optional) "val"
const node1 = new Node({type: 'star', val: '*'});
// or pass "val" (first) and "type" (second) as string
const node2 = new Node('*', 'star');
// both result in => Node { type: 'star', val: '*' }
```
## Snapdragon usage
With [snapdragon][] v0.9.0 and higher, it's recommended that you use `this.node()` to create a new `Node` inside parser handlers (instead of doing `new Node()`).
### Snapdragon ^1.0.0
Example usage inside a [snapdragon][] parser handler function.
```js
const Node = require('{%= name %}');
const Token = require('snapdragon-token');
// create a new AST node
const node = new Node({ type: 'star', value: '*' });
// convert a Lexer Token into an AST Node
const token = new Token({ type: 'star', value: '*' });
const node = new Node(token);
```
## Node objects
AST Nodes are represented as `Node` objects that implement the following interface:
```js
interface Node {
type: string;
value: string | undefined
nodes: array | undefined
}
```
- `type` **{string}** - A string representing the node variant type. This property is often used for classifying the purpose or nature of the node, so that parsers or compilers can determine what to do with it.
- `value` **{string|undefined}** (optional) - In general, value should only be a string when `node.nodes` is undefined. This is not reinforced, but is considered good practice. Use a different property name to store arbitrary strings on the node when `node.nodes` is an array.
- `nodes` **{array|undefined}** (optional) - array of child nodes
A number of useful methods and non-enumerable properties are also exposed for adding, finding and removing child nodes, etc.
Continue reading the API documentation for more details.
## Node API
{%= apidocs("index.js") %}
### Non-enumerable properties
- `node.isNode` **{boolean}** - this value is set to `true` when a node is created. This can be useful in situationas as a fast alternative to using `instanceof Node` if you [need to determine](#nodeisnode) if a value is a `node` object.
- `node.size` **{number}** - the number of child nodes that have been pushed or unshifted onto `node.nodes` using the node's API. This is useful for determining if nodes were added to `node.nodes` without using `node.push()` or `node.unshift()` (for example: `if (node.nodes && node.size !== node.nodes.length)`)
- `node.parent` **{object}** (instance of Node)
## Release history
See [the changelog](changelog.md).
snapdragon-node-3.0.0/CHANGELOG.md 0000664 0000000 0000000 00000003055 13376213533 0016413 0 ustar 00root root 0000000 0000000 # Release history
Changelog entries are classified using the following labels from [keep-a-changelog][]:
* `added`: for new features
* `changed`: for changes in existing functionality
* `deprecated`: for once-stable features removed in upcoming releases
* `removed`: for deprecated features removed in this release
* `fixed`: for any bug fixes
Custom labels used in this changelog:
* `dependencies`: bumps dependencies
* `housekeeping`: code re-organization, minor edits, or other changes that don't fit in one of the other categories.
## 3.0.0 - 2018-11-24
**Removed**
- `node.define` was removed. Use [define-property](https://github.com/jonschlinkert/define-property) or `Object.defineProperty` instead.
**Added**
- `node.isEmpty` method
- `node.clone` method, for cloning the node
- `node.stringify` method, for returning a string from `node.value` and/or from recursing over child nodes.
## 2.1.0 - 2017-11-14
**Deprecated**
- `node.define` is deprecated and will be removed in v3.0.0. Use [define-property](https://github.com/jonschlinkert/define-property) or `Object.defineProperty` instead.
## 2.0.0 - 2017-05-01
**Changed**
- `node.unshiftNode` was renamed to [.unshift](readme.md#unshift)
- `node.pushNode` was renamed to [.push](readme.md#push)
- `node.getNode` was renamed to [.find](readme.md#find)
**Added**
- [.isNode](readme.md#isNode)
- [.isEmpty](readme.md#isEmpty)
- [.pop](readme.md#pop)
- [.shift](readme.md#shift)
- [.remove](readme.md#remove)
### 0.1.0
First release.
[keep-a-changelog]: https://github.com/olivierlacan/keep-a-changelog
snapdragon-node-3.0.0/LICENSE 0000664 0000000 0000000 00000002103 13376213533 0015600 0 ustar 00root root 0000000 0000000 The MIT License (MIT)
Copyright (c) 2017-present, Jon Schlinkert.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
snapdragon-node-3.0.0/README.md 0000664 0000000 0000000 00000035325 13376213533 0016066 0 ustar 00root root 0000000 0000000 # snapdragon-node [](https://www.npmjs.com/package/snapdragon-node) [](https://npmjs.org/package/snapdragon-node) [](https://npmjs.org/package/snapdragon-node) [](https://travis-ci.org/here-be/snapdragon-node)
> Class for creating AST nodes.
Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save snapdragon-node
```
## Usage
```js
const Node = require('snapdragon-node');
// either pass on object with "type" and (optional) "val"
const node1 = new Node({type: 'star', val: '*'});
// or pass "val" (first) and "type" (second) as string
const node2 = new Node('*', 'star');
// both result in => Node { type: 'star', val: '*' }
```
## Snapdragon usage
With [snapdragon](https://github.com/here-be/snapdragon) v0.9.0 and higher, it's recommended that you use `this.node()` to create a new `Node` inside parser handlers (instead of doing `new Node()`).
### Snapdragon ^1.0.0
Example usage inside a [snapdragon](https://github.com/here-be/snapdragon) parser handler function.
```js
const Node = require('snapdragon-node');
const Token = require('snapdragon-token');
// create a new AST node
const node = new Node({ type: 'star', value: '*' });
// convert a Lexer Token into an AST Node
const token = new Token({ type: 'star', value: '*' });
const node = new Node(token);
```
## Node objects
AST Nodes are represented as `Node` objects that implement the following interface:
```js
interface Node {
type: string;
value: string | undefined
nodes: array | undefined
}
```
* `type` **{string}** - A string representing the node variant type. This property is often used for classifying the purpose or nature of the node, so that parsers or compilers can determine what to do with it.
* `value` **{string|undefined}** (optional) - In general, value should only be a string when `node.nodes` is undefined. This is not reinforced, but is considered good practice. Use a different property name to store arbitrary strings on the node when `node.nodes` is an array.
* `nodes` **{array|undefined}** (optional) - array of child nodes
A number of useful methods and non-enumerable properties are also exposed for adding, finding and removing child nodes, etc.
Continue reading the API documentation for more details.
## Node API
### [Node](index.js#L20)
Create a new AST `Node` with the given `type` and `value`, or an object to initialize with.
**Params**
* `type` **{object|string}**: Either an object to initialize with, or a string to be used as the `node.type`.
* `value` **{string|boolean}**: If the first argument is a string, the second argument may be a string value to set on `node.value`.
* `clone` **{boolean}**: When an object is passed as the first argument, pass true as the last argument to deep clone values before assigning them to the new node.
* `returns` **{Object}**: node instance
**Example**
```js
console.log(new Node({ type: 'star', value: '*' }));
console.log(new Node('star', '*'));
// both result in => Node { type: 'star', value: '*' }
```
### [.clone](index.js#L50)
Return a clone of the node. Values that are arrays or plain objects are deeply cloned.
* `returns` **{Object}**: returns a clone of the node
**Example**
```js
const node = new Node({type: 'star', value: '*'});
consle.log(node.clone() !== node);
//=> true
```
### [.stringify](index.js#L68)
Return a string created from `node.value` and/or recursively visiting over `node.nodes`.
* `returns` **{String}**
**Example**
```js
const node = new Node({type: 'star', value: '*'});
consle.log(node.stringify());
//=> '*'
```
### [.push](index.js#L88)
Push a child node onto the `node.nodes` array.
**Params**
* `node` **{Object}**
* `returns` **{Number}**: Returns the length of `node.nodes`, like `Array.push`
**Example**
```js
const foo = new Node({type: 'foo'});
const bar = new Node({type: 'bar'});
foo.push(bar);
```
### [.unshift](index.js#L117)
Unshift a child node onto `node.nodes`, and set `node` as the parent on `child.parent`.
**Params**
* `node` **{Object}**
* `returns` **{Number}**: Returns the length of `node.nodes`
**Example**
```js
const foo = new Node({type: 'foo'});
const bar = new Node({type: 'bar'});
foo.unshift(bar);
```
### [.pop](index.js#L151)
Pop a node from `node.nodes`.
* `returns` **{Number}**: Returns the popped `node`
**Example**
```js
const node = new Node({type: 'foo'});
node.push(new Node({type: 'a'}));
node.push(new Node({type: 'b'}));
node.push(new Node({type: 'c'}));
node.push(new Node({type: 'd'}));
console.log(node.nodes.length);
//=> 4
node.pop();
console.log(node.nodes.length);
//=> 3
```
### [.shift](index.js#L178)
Shift a node from `node.nodes`.
* `returns` **{Object}**: Returns the shifted `node`
**Example**
```js
const node = new Node({type: 'foo'});
node.push(new Node({type: 'a'}));
node.push(new Node({type: 'b'}));
node.push(new Node({type: 'c'}));
node.push(new Node({type: 'd'}));
console.log(node.nodes.length);
//=> 4
node.shift();
console.log(node.nodes.length);
//=> 3
```
### [.remove](index.js#L197)
Remove `node` from `node.nodes`.
**Params**
* `node` **{Object}**
* `returns` **{Object}**: Returns the removed node.
**Example**
```js
node.remove(childNode);
```
### [.find](index.js#L228)
Get the first child node from `node.nodes` that matches the given `type`. If `type` is a number, the child node at that index is returned.
**Params**
* `type` **{String}**
* `returns` **{Object}**: Returns a child node or undefined.
**Example**
```js
const child = node.find(1); //<= index of the node to get
const child = node.find('foo'); //<= node.type of a child node
const child = node.find(/^(foo|bar)$/); //<= regex to match node.type
const child = node.find(['foo', 'bar']); //<= array of node.type(s)
```
### [.has](index.js#L259)
Returns true if `node.nodes` array contains the given `node`.
**Params**
* `type` **{String}**
* `returns` **{Boolean}**
**Example**
```js
const foo = new Node({type: 'foo'});
const bar = new Node({type: 'bar'});
cosole.log(foo.has(bar)); // false
foo.push(bar);
cosole.log(foo.has(bar)); // true
```
### [.hasType](index.js#L284)
Return true if the `node.nodes` has the given `type`.
**Params**
* `type` **{String}**
* `returns` **{Boolean}**
**Example**
```js
const foo = new Node({type: 'foo'});
const bar = new Node({type: 'bar'});
foo.push(bar);
cosole.log(foo.hasType('qux')); // false
cosole.log(foo.hasType(/^(qux|bar)$/)); // true
cosole.log(foo.hasType(['qux', 'bar'])); // true
```
### [.isType](index.js#L303)
Return true if the node is the given `type`.
**Params**
* `type` **{String}**
* `returns` **{Boolean}**
**Example**
```js
const node = new Node({type: 'bar'});
cosole.log(node.isType('foo')); // false
cosole.log(node.isType(/^(foo|bar)$/)); // true
cosole.log(node.isType(['foo', 'bar'])); // true
```
### [.isEmpty](index.js#L323)
Returns true if `node.value` is an empty string, or `node.nodes` does not contain any non-empty text nodes.
**Params**
* `fn` **{Function}**: (optional) Filter function that is called on `node` and/or child nodes. `isEmpty` will return false immediately when the filter function returns false on any nodes.
* `returns` **{Boolean}**
**Example**
```js
const node = new Node({type: 'text'});
node.isEmpty(); //=> true
node.value = 'foo';
node.isEmpty(); //=> false
```
### [.isInside](index.js#L342)
Returns true if the node has an ancestor node of the given `type`
**Params**
* `type` **{String}**
* `returns` **{Boolean}**
**Example**
```js
const box = new Node({type: 'box'});
const marble = new Node({type: 'marble'});
box.push(marble);
marble.isInside('box'); //=> true
```
### [.siblings](index.js#L365)
Get the siblings array, or `null` if it doesn't exist.
* `returns` **{Array}**
**Example**
```js
const foo = new Node({type: 'foo'});
const bar = new Node({type: 'bar'});
const baz = new Node({type: 'baz'});
foo.push(bar);
foo.push(baz);
console.log(bar.siblings.length) // 2
console.log(baz.siblings.length) // 2
```
### [.index](index.js#L393)
Calculate the node's current index on `node.parent.nodes`, or `-1` if the node does not have a parent, or is not on `node.parent.nodes`.
* `returns` **{Number}**
**Example**
```js
const foo = new Node({type: 'foo'});
const bar = new Node({type: 'bar'});
const baz = new Node({type: 'baz'});
const qux = new Node({type: 'qux'});
foo.push(bar);
foo.push(baz);
foo.unshift(qux);
console.log(bar.index) // 1
console.log(baz.index) // 2
console.log(qux.index) // 0
```
### [.prev](index.js#L424)
Get the previous node from the [siblings](#siblings) array or `null`.
* `returns` **{Object}**
**Example**
```js
const foo = new Node({type: 'foo'});
const bar = new Node({type: 'bar'});
const baz = new Node({type: 'baz'});
foo.push(bar);
foo.push(baz);
console.log(baz.prev.type) // 'bar'
```
### [.next](index.js#L453)
Get the next element from the [siblings](#siblings) array, or `null` if a next node does not exist.
* `returns` **{Object}**
**Example**
```js
const parent = new Node({type: 'root'});
const foo = new Node({type: 'foo'});
const bar = new Node({type: 'bar'});
const baz = new Node({type: 'baz'});
parent.push(foo);
parent.push(bar);
parent.push(baz);
console.log(foo.next.type) // 'bar'
console.log(bar.next.type) // 'baz'
```
### [.first](index.js#L480)
Get the first child node from `node.nodes`.
* `returns` **{Object}**: The first node, or undefiend
**Example**
```js
const foo = new Node({type: 'foo'});
const bar = new Node({type: 'bar'});
const baz = new Node({type: 'baz'});
const qux = new Node({type: 'qux'});
foo.push(bar);
foo.push(baz);
foo.push(qux);
console.log(foo.first.type) // 'bar'
```
### [.last](index.js#L504)
Get the last child node from `node.nodes`.
* `returns` **{Object}**: The last node, or undefiend
**Example**
```js
const foo = new Node({type: 'foo'});
const bar = new Node({type: 'bar'});
const baz = new Node({type: 'baz'});
const qux = new Node({type: 'qux'});
foo.push(bar);
foo.push(baz);
foo.push(qux);
console.log(foo.last.type) // 'qux'
```
### [.depth](index.js#L525)
Get the `node.depth`. The root node has a depth of 0. Add 1 to child nodes for each level of nesting.
* `returns` **{Object}**: The last node, or undefiend
**Example**
```js
const foo = new Node({type: 'foo'});
foo.push(bar);
console.log(foo.depth) // 1
console.log(bar.depth) // 2
```
### [Node#isNode](index.js#L545)
Static method that returns true if the given value is a node.
**Params**
* `node` **{Object}**
* `returns` **{Boolean}**
**Example**
```js
const Node = require('snapdragon-node');
const node = new Node({type: 'foo'});
console.log(Node.isNode(node)); //=> true
console.log(Node.isNode({})); //=> false
```
### Non-enumerable properties
* `node.isNode` **{boolean}** - this value is set to `true` when a node is created. This can be useful in situationas as a fast alternative to using `instanceof Node` if you [need to determine](#nodeisnode) if a value is a `node` object.
* `node.size` **{number}** - the number of child nodes that have been pushed or unshifted onto `node.nodes` using the node's API. This is useful for determining if nodes were added to `node.nodes` without using `node.push()` or `node.unshift()` (for example: `if (node.nodes && node.size !== node.nodes.length)`)
* `node.parent` **{object}** (instance of Node)
## Release history
See [the changelog](changelog.md).
## About
Contributing
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
Please read the [contributing guide](.github/contributing.md) for advice on opening issues, pull requests, and coding standards.
Running Tests
Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
```sh
$ npm install && npm test
```
Building docs
_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
To generate the readme, run the following command:
```sh
$ npm install -g verbose/verb#dev verb-generate-readme && verb
```
### Related projects
You might also be interested in these projects:
* [breakdance](https://www.npmjs.com/package/breakdance): Breakdance is a node.js library for converting HTML to markdown. Highly pluggable, flexible and easy… [more](http://breakdance.io) | [homepage](http://breakdance.io "Breakdance is a node.js library for converting HTML to markdown. Highly pluggable, flexible and easy to use. It's time for your markup to get down.")
* [snapdragon-capture](https://www.npmjs.com/package/snapdragon-capture): Snapdragon plugin that adds a capture method to the parser instance. | [homepage](https://github.com/jonschlinkert/snapdragon-capture "Snapdragon plugin that adds a capture method to the parser instance.")
* [snapdragon-cheerio](https://www.npmjs.com/package/snapdragon-cheerio): Snapdragon plugin for converting a cheerio AST to a snapdragon AST. | [homepage](https://github.com/jonschlinkert/snapdragon-cheerio "Snapdragon plugin for converting a cheerio AST to a snapdragon AST.")
* [snapdragon-util](https://www.npmjs.com/package/snapdragon-util): Utilities for the snapdragon parser/compiler. | [homepage](https://github.com/here-be/snapdragon-util "Utilities for the snapdragon parser/compiler.")
* [snapdragon](https://www.npmjs.com/package/snapdragon): Easy-to-use plugin system for creating powerful, fast and versatile parsers and compilers, with built-in source-map… [more](https://github.com/here-be/snapdragon) | [homepage](https://github.com/here-be/snapdragon "Easy-to-use plugin system for creating powerful, fast and versatile parsers and compilers, with built-in source-map support.")
### Author
**Jon Schlinkert**
* [GitHub Profile](https://github.com/jonschlinkert)
* [Twitter Profile](https://twitter.com/jonschlinkert)
* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
### License
Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT License](LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on November 24, 2018._ snapdragon-node-3.0.0/example.js 0000664 0000000 0000000 00000000254 13376213533 0016571 0 ustar 00root root 0000000 0000000 const Node = require('./');
console.log(new Node({ type: 'star', value: '*' }));
console.log(new Node('star', '*'));
// both result in => Node { type: 'star', value: '*' }
snapdragon-node-3.0.0/index.js 0000664 0000000 0000000 00000040546 13376213533 0016255 0 ustar 00root root 0000000 0000000 'use strict';
/**
* Create a new AST `Node` with the given `type` and `value`, or an
* object to initialize with.
*
* ```js
* console.log(new Node({ type: 'star', value: '*' }));
* console.log(new Node('star', '*'));
* // both result in => Node { type: 'star', value: '*' }
* ```
* @name Node
* @param {object|string} `type` Either an object to initialize with, or a string to be used as the `node.type`.
* @param {string|boolean} `value` If the first argument is a string, the second argument may be a string value to set on `node.value`.
* @param {boolean} `clone` When an object is passed as the first argument, pass true as the last argument to deep clone values before assigning them to the new node.
* @return {Object} node instance
* @api public
*/
class Node {
constructor(type, value, clone) {
define(this, 'isNode', true);
define(this, 'parent', null);
define(this, 'size', 0);
if (isObject(type)) {
assign(this, type, clone);
} else {
this.type = type;
if (value != null) {
this.value = value;
}
}
}
/**
* Return a clone of the node. Values that are arrays or plain objects
* are deeply cloned.
*
* ```js
* const node = new Node({type: 'star', value: '*'});
* consle.log(node.clone() !== node);
* //=> true
* ```
* @name .clone
* @return {Object} returns a clone of the node
* @api public
*/
clone() {
return new this.constructor(this, null, true);
}
/**
* Return a string created from `node.value` and/or recursively
* visiting over `node.nodes`.
*
* ```js
* const node = new Node({type: 'star', value: '*'});
* consle.log(node.stringify());
* //=> '*'
* ```
* @name .stringify
* @return {String}
* @api public
*/
stringify(fn = n => n.value) {
let str = '';
visit(this, n => (str += fn(n)));
return str;
}
/**
* Push a child node onto the `node.nodes` array.
*
* ```js
* const foo = new Node({type: 'foo'});
* const bar = new Node({type: 'bar'});
* foo.push(bar);
* ```
* @name .push
* @param {Object} `node`
* @return {Number} Returns the length of `node.nodes`, like `Array.push`
* @api public
*/
push(node) {
if (!node) return;
assert(isObject(node), 'expected node to be an object');
assert(node !== this, 'node should not be the same as node.parent');
if (!this.constructor.isNode(node)) {
node = new this.constructor(node);
}
this.nodes = this.nodes || [];
node.parent = this;
ensureNodes(node);
this.size++;
return this.nodes.push(node);
}
/**
* Unshift a child node onto `node.nodes`, and set `node` as
* the parent on `child.parent`.
*
* ```js
* const foo = new Node({type: 'foo'});
* const bar = new Node({type: 'bar'});
* foo.unshift(bar);
* ```
* @name .unshift
* @param {Object} `node`
* @return {Number} Returns the length of `node.nodes`
* @api public
*/
unshift(node) {
if (!node) return;
assert(isObject(node), 'expected node to be an object');
assert(node !== this, 'node should not be the same as node.parent');
if (!this.constructor.isNode(node)) {
node = new this.constructor(node);
}
this.nodes = this.nodes || [];
node.parent = this;
ensureNodes(node);
this.size++;
return this.nodes.unshift(node);
}
/**
* Pop a node from `node.nodes`.
*
* ```js
* const node = new Node({type: 'foo'});
* node.push(new Node({type: 'a'}));
* node.push(new Node({type: 'b'}));
* node.push(new Node({type: 'c'}));
* node.push(new Node({type: 'd'}));
* console.log(node.nodes.length);
* //=> 4
* node.pop();
* console.log(node.nodes.length);
* //=> 3
* ```
* @name .pop
* @return {Number} Returns the popped `node`
* @api public
*/
pop() {
if (this.nodes && this.nodes.length) {
this.size--;
return this.nodes.pop();
}
}
/**
* Shift a node from `node.nodes`.
*
* ```js
* const node = new Node({type: 'foo'});
* node.push(new Node({type: 'a'}));
* node.push(new Node({type: 'b'}));
* node.push(new Node({type: 'c'}));
* node.push(new Node({type: 'd'}));
* console.log(node.nodes.length);
* //=> 4
* node.shift();
* console.log(node.nodes.length);
* //=> 3
* ```
* @name .shift
* @return {Object} Returns the shifted `node`
* @api public
*/
shift() {
if (this.nodes && this.nodes.length) {
this.size--;
return this.nodes.shift();
}
}
/**
* Remove `node` from `node.nodes`.
*
* ```js
* node.remove(childNode);
* ```
* @name .remove
* @param {Object} `node`
* @return {Object} Returns the removed node.
* @api public
*/
remove(node) {
if (!this.nodes) return [];
assert(this.constructor.isNode(node), 'expected an instance of Node');
assert(node !== this, 'cannot remove a node from itself');
const idx = this.nodes.indexOf(node);
node.index = -1;
if (idx !== -1) {
this.size--;
return this.nodes.splice(idx, 1);
}
return [];
}
/**
* Get the first child node from `node.nodes` that matches the given `type`.
* If `type` is a number, the child node at that index is returned.
*
* ```js
* const child = node.find(1); //<= index of the node to get
* const child = node.find('foo'); //<= node.type of a child node
* const child = node.find(/^(foo|bar)$/); //<= regex to match node.type
* const child = node.find(['foo', 'bar']); //<= array of node.type(s)
* ```
* @name .find
* @param {String} `type`
* @return {Object} Returns a child node or undefined.
* @api public
*/
find(type, n = 0) {
if (!Array.isArray(this.nodes) || this.nodes.length === 0) return null;
if (typeof type === 'number') return this.nodes[type];
for (let i = n; i < this.nodes.length; i++) {
const node = this.nodes[i];
if (isType(node, type)) {
return node;
}
}
}
visit(fn) {
return visit(this, fn);
}
/**
* Returns true if `node.nodes` array contains the given `node`.
*
* ```js
* const foo = new Node({type: 'foo'});
* const bar = new Node({type: 'bar'});
* cosole.log(foo.has(bar)); // false
* foo.push(bar);
* cosole.log(foo.has(bar)); // true
* ```
* @name .has
* @param {String} `type`
* @return {Boolean}
* @api public
*/
has(node) {
if (this.constructor.isNode(node)) {
return Array.isArray(this.nodes) && this.nodes.includes(node);
}
return this.hasType(node);
}
/**
* Return true if the `node.nodes` has the given `type`.
*
* ```js
* const foo = new Node({type: 'foo'});
* const bar = new Node({type: 'bar'});
* foo.push(bar);
*
* cosole.log(foo.hasType('qux')); // false
* cosole.log(foo.hasType(/^(qux|bar)$/)); // true
* cosole.log(foo.hasType(['qux', 'bar'])); // true
* ```
* @name .hasType
* @param {String} `type`
* @return {Boolean}
* @api public
*/
hasType(type) {
return Array.isArray(this.nodes) && this.nodes.find(node => isType(node, type));
}
/**
* Return true if the node is the given `type`.
*
* ```js
* const node = new Node({type: 'bar'});
* cosole.log(node.isType('foo')); // false
* cosole.log(node.isType(/^(foo|bar)$/)); // true
* cosole.log(node.isType(['foo', 'bar'])); // true
* ```
* @name .isType
* @param {String} `type`
* @return {Boolean}
* @api public
*/
isType(type) {
return isType(this, type);
}
/**
* Returns true if `node.value` is an empty string, or `node.nodes` does
* not contain any non-empty text nodes.
*
* ```js
* const node = new Node({type: 'text'});
* node.isEmpty(); //=> true
* node.value = 'foo';
* node.isEmpty(); //=> false
* ```
* @name .isEmpty
* @param {Function} `fn` (optional) Filter function that is called on `node` and/or child nodes. `isEmpty` will return false immediately when the filter function returns false on any nodes.
* @return {Boolean}
* @api public
*/
isEmpty(fn) {
return isEmpty(this, fn);
}
/**
* Returns true if the node has an ancestor node of the given `type`
*
* ```js
* const box = new Node({type: 'box'});
* const marble = new Node({type: 'marble'});
* box.push(marble);
* marble.isInside('box'); //=> true
* ```
* @name .isInside
* @param {String} `type`
* @return {Boolean}
* @api public
*/
isInside(type) {
return this.parent && (this.parent.type === type || this.parent.isInside(type));
}
/**
* Get the siblings array, or `null` if it doesn't exist.
*
* ```js
* const foo = new Node({type: 'foo'});
* const bar = new Node({type: 'bar'});
* const baz = new Node({type: 'baz'});
* foo.push(bar);
* foo.push(baz);
*
* console.log(bar.siblings.length) // 2
* console.log(baz.siblings.length) // 2
* ```
* @getter
* @name .siblings
* @return {Array}
* @api public
*/
get siblings() {
return this.parent ? this.parent.nodes : null;
}
/**
* Calculate the node's current index on `node.parent.nodes`, or `-1` if the
* node does not have a parent, or is not on `node.parent.nodes`.
*
* ```js
* const foo = new Node({type: 'foo'});
* const bar = new Node({type: 'bar'});
* const baz = new Node({type: 'baz'});
* const qux = new Node({type: 'qux'});
* foo.push(bar);
* foo.push(baz);
* foo.unshift(qux);
*
* console.log(bar.index) // 1
* console.log(baz.index) // 2
* console.log(qux.index) // 0
* ```
* @setter
* @getter
* @name .index
* @return {Number}
* @api public
*/
set index(index) {
define(this, '_index', index);
}
get index() {
if (!Array.isArray(this.siblings)) {
return -1;
}
if (this._index === -1 || this.siblings[this._index] !== this) {
define(this, '_index', this.siblings.indexOf(this));
}
return this._index;
}
/**
* Get the previous node from the [siblings](#siblings) array or `null`.
*
* ```js
* const foo = new Node({type: 'foo'});
* const bar = new Node({type: 'bar'});
* const baz = new Node({type: 'baz'});
* foo.push(bar);
* foo.push(baz);
*
* console.log(baz.prev.type) // 'bar'
* ```
* @getter
* @name .prev
* @return {Object}
* @api public
*/
get prev() {
if (Array.isArray(this.siblings)) {
return this.siblings[this.index - 1] || this.parent.prev;
}
return null;
}
/**
* Get the next element from the [siblings](#siblings) array, or `null` if
* a next node does not exist.
*
* ```js
* const parent = new Node({type: 'root'});
* const foo = new Node({type: 'foo'});
* const bar = new Node({type: 'bar'});
* const baz = new Node({type: 'baz'});
* parent.push(foo);
* parent.push(bar);
* parent.push(baz);
*
* console.log(foo.next.type) // 'bar'
* console.log(bar.next.type) // 'baz'
* ```
* @getter
* @name .next
* @return {Object}
* @api public
*/
get next() {
if (Array.isArray(this.siblings)) {
return this.siblings[this.index + 1] || this.parent.next;
}
return null;
}
/**
* Get the first child node from `node.nodes`.
*
* ```js
* const foo = new Node({type: 'foo'});
* const bar = new Node({type: 'bar'});
* const baz = new Node({type: 'baz'});
* const qux = new Node({type: 'qux'});
* foo.push(bar);
* foo.push(baz);
* foo.push(qux);
*
* console.log(foo.first.type) // 'bar'
* ```
* @getter
* @name .first
* @return {Object} The first node, or undefiend
* @api public
*/
get first() {
return Array.isArray(this.nodes) ? this.nodes[0] : null;
}
/**
* Get the last child node from `node.nodes`.
*
* ```js
* const foo = new Node({type: 'foo'});
* const bar = new Node({type: 'bar'});
* const baz = new Node({type: 'baz'});
* const qux = new Node({type: 'qux'});
* foo.push(bar);
* foo.push(baz);
* foo.push(qux);
*
* console.log(foo.last.type) // 'qux'
* ```
* @getter
* @name .last
* @return {Object} The last node, or undefiend
* @api public
*/
get last() {
return Array.isArray(this.nodes) ? this.nodes[this.nodes.length - 1] : null;
}
/**
* Get the `node.depth`. The root node has a depth of 0. Add 1 to child nodes
* for each level of nesting.
*
* ```js
* const foo = new Node({type: 'foo'});
* foo.push(bar);
*
* console.log(foo.depth) // 1
* console.log(bar.depth) // 2
* ```
* @getter
* @name .depth
* @return {Object} The last node, or undefiend
* @api public
*/
get depth() {
return this.parent ? this.parent.depth + 1 : 0;
}
/**
* Static method that returns true if the given value is a node.
*
* ```js
* const Node = require('snapdragon-node');
* const node = new Node({type: 'foo'});
* console.log(Node.isNode(node)); //=> true
* console.log(Node.isNode({})); //=> false
* ```
* @name Node#isNode
* @param {Object} `node`
* @returns {Boolean}
* @api public
* @static
*/
static isNode(node) {
return isObject(node) && (node instanceof this || node.isNode === true);
}
}
/**
* Simplified assertion. Throws an error is `value` is not true.
*/
function assert(value, message) {
if (value !== true) throw new Error(message);
}
function expect(value, name) {
assert(value, 'expected ' + name + ' to be an instance of Node');
}
function hasOwn(obj, prop) {
return Object.hasOwnProperty.call(obj, prop);
}
function isEmpty(node, fn) {
expect(Node.isNode(node), 'node');
if (!Array.isArray(node.nodes)) {
if (typeof fn === 'function') {
return fn(node);
}
return !node.value;
}
if (node.nodes.length === 0) {
return true;
}
for (const child of node.nodes) {
if (!isEmpty(child, fn)) {
return false;
}
}
return true;
}
function isType(node, type) {
expect(Node.isNode(node), 'node');
switch (typeOf(type)) {
case 'string':
return node.type === type;
case 'regexp':
return type.test(node.type);
case 'array':
for (const key of type) {
if (node.isType(node, key)) {
return true;
}
}
return false;
default: {
throw new TypeError('expected "type" to be an array, string or regexp');
}
}
}
function isObject(val) {
return typeOf(val) === 'object';
}
function typeOf(val) {
if (typeof val === 'string') return 'string';
if (Array.isArray(val)) return 'array';
if (val instanceof RegExp) {
return 'regexp';
}
if (val === void 0) return 'undefiend';
if (val === null) return 'null';
return typeof val;
}
/**
* assign `token` properties to `node`
*/
function assign(node, token, clone) {
copy(node, token, clone);
ensureNodes(node, clone);
if (token.constructor && token.constructor.name === 'Token') {
copy(node, token.constructor.prototype, clone);
}
}
function copy(receiver, provider, clone) {
const descriptors = Object.getOwnPropertyDescriptors(provider);
for (const key in descriptors) {
if (key === 'constructor' || key in receiver) continue;
const desc = descriptors[key];
if (hasOwn(desc, 'value') && clone === true) {
desc.value = cloneDeep(desc.value);
}
Object.defineProperty(receiver, key, desc);
}
}
function ensureNodes(node, clone) {
if (!node.nodes) return;
if (Array.isArray(node.nodes)) {
const len = node.nodes.length;
for (let i = 0; i < len; i++) {
let child = node.nodes[i];
if (!Node.isNode(child)) {
child = node.nodes[i] = new Node(node.nodes[i], null, true);
child.parent = node;
child.index = i;
}
ensureNodes(child);
}
node.size = len;
}
}
/**
* Deeply clone plain objects and arrays.
*/
function cloneDeep(value) {
const obj = {};
switch (typeOf(value)) {
case 'array':
return value.map(ele => cloneDeep(ele));
case 'object':
for (const key of Object.keys(value)) {
obj[key] = cloneDeep(value[key]);
}
return obj;
default: {
return value;
}
}
}
function visit(node, fn) {
fn(node);
return node.nodes ? mapVisit(node, fn) : node;
}
function mapVisit(node, fn) {
node.nodes.forEach(n => visit(n, fn));
return node;
}
function define(obj, key, value) {
Object.defineProperty(obj, key, {
configurable: true,
enumerable: false,
writable: true,
value: value
});
}
/**
* Expose `Node`
*/
exports = module.exports = Node;
snapdragon-node-3.0.0/package.json 0000664 0000000 0000000 00000002414 13376213533 0017066 0 ustar 00root root 0000000 0000000 {
"name": "snapdragon-node",
"description": "Class for creating AST nodes.",
"version": "3.0.0",
"homepage": "https://github.com/here-be/snapdragon-node",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"repository": "here-be/snapdragon-node",
"bugs": {
"url": "https://github.com/here-be/snapdragon-node/issues"
},
"license": "MIT",
"files": [
"index.js"
],
"main": "index.js",
"engines": {
"node": ">=4"
},
"scripts": {
"test": "mocha",
"cover": "nyc --reporter=text --reporter=html mocha"
},
"devDependencies": {
"define-property": "^2.0.2",
"gulp-format-md": "^2.0.0",
"mocha": "^5.2.0",
"nyc": "^13.1.0",
"snapdragon": "^0.12.0"
},
"keywords": [
"ast",
"compile",
"compiler",
"convert",
"node",
"parse",
"parser",
"plugin",
"render",
"snapdragon",
"snapdragonplugin",
"token",
"transform"
],
"verb": {
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"related": {
"list": [
"breakdance",
"snapdragon",
"snapdragon-capture",
"snapdragon-cheerio",
"snapdragon-util"
]
},
"lint": {
"reflinks": true
}
}
}
snapdragon-node-3.0.0/test/ 0000775 0000000 0000000 00000000000 13376213533 0015556 5 ustar 00root root 0000000 0000000 snapdragon-node-3.0.0/test/test.js 0000664 0000000 0000000 00000032472 13376213533 0017103 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
const assert = require('assert');
const define = require('define-property');
const Parser = require('snapdragon/lib/parser');
const BaseNode = require('..');
let parser;
let ast;
class Node extends BaseNode {
define(key, value) {
define(this, key, value);
return this;
}
}
describe('snapdragon-node', function() {
beforeEach(function() {
parser = new Parser({ Node })
.set('text', function() {
let match = this.match(/^[a-z]+/);
if (match) {
return this.node(match[0]);
}
})
.set('slash', function() {
let match = this.match(/^\//);
if (match) {
return this.node(match[0]);
}
})
.set('star', function() {
let match = this.match(/^\*/);
if (match) {
return this.node(match[0]);
}
});
ast = new Node(parser.parse('a/*/c'));
});
describe('node', function() {
it('should export a function', function() {
assert.equal(typeof Node, 'function');
});
it('should create a new Node with the given object', function() {
let node = new Node({ value: '*', type: 'star' });
assert.equal(node.value, '*');
assert.equal(node.type, 'star');
});
it('should create a new Node with the given position', function() {
let pos = parser.position();
let node = pos(new Node());
assert(node.position);
assert(node.position.start);
assert(node.position.start.line);
assert(node.position.start.column);
assert(node.position.end);
assert(node.position.end.line);
assert(node.position.end.column);
});
it('should create a new Node with the given position and value', function() {
let pos = parser.position();
let node = pos(new Node(null, '*'));
assert.equal(node.value, '*');
assert(node.position);
assert(node.position.start);
assert(node.position.start.line);
assert(node.position.start.column);
assert(node.position.end);
assert(node.position.end.line);
assert(node.position.end.column);
});
it('should create a new Node with the given position, type, and value', function() {
let pos = parser.position();
let node = pos(new Node('star', '*'));
assert.equal(node.value, '*');
assert.equal(node.type, 'star');
assert(node.position);
assert(node.position.start);
assert(node.position.start.line);
assert(node.position.start.column);
assert(node.position.end);
assert(node.position.end.line);
assert(node.position.end.column);
});
it('should create a new Node with the given position and object', function() {
let pos = parser.position();
let node = pos(new Node({ value: '*', type: 'star' }));
assert.equal(node.value, '*');
assert.equal(node.type, 'star');
assert(node.position);
assert(node.position.start);
assert(node.position.start.line);
assert(node.position.start.column);
assert(node.position.end);
assert(node.position.end.line);
assert(node.position.end.column);
});
it('should extend type and value onto a node', function() {
let node = new Node({ type: 'foo', value: 'bar' });
assert.equal(node.type, 'foo');
assert.equal(node.value, 'bar');
});
it('should extend arbitrary properties onto a node', function() {
let node = new Node({ type: 'foo', value: 'bar', baz: 'qux' });
assert.equal(node.baz, 'qux');
});
it('should not extend existing getter properties onto a node', function() {
let node = new Node({ type: 'foo', value: 'bar', index: 11 });
assert.equal(node.index, -1);
});
});
describe('.isType', function() {
it('should return true if the node is the given type', function() {
assert(ast.isType('root'));
assert(ast.last.isType('eos'));
});
});
describe('.hasType', function() {
it('should return true if node.nodes has the given type', function() {
assert(ast.hasType('star'));
assert(!ast.hasType('foo'));
});
it('should return false when a node does not exist', function() {
let node = new Node('foo');
assert.equal(node.hasType('slslsllsls'), false);
});
});
describe('.first', function() {
it('should get the first node from `node.nodes`', function() {
assert(ast.first);
assert.equal(ast.first.type, 'bos');
});
it('should return null when no nodes exist', function() {
let node = new Node('foo');
assert.equal(node.first, null);
});
});
describe('.last', function() {
it('should get the last node from `node.nodes`', function() {
assert(ast.last);
assert.equal(ast.last.type, 'eos');
});
it('should return null when no nodes exist', function() {
let node = new Node('foo');
assert.equal(node.last, null);
});
});
describe('.index', function() {
it('should get the index of a node from node.parent.nodes', function() {
let foo = new Node({ type: 'foo' });
let bar = new Node({ type: 'bar' });
let baz = new Node({ type: 'baz' });
let qux = new Node({ type: 'qux' });
foo.unshift(qux);
foo.push(bar);
foo.push(baz);
assert.equal(bar.index, 1);
assert.equal(baz.index, 2);
assert.equal(qux.index, 0);
});
it('should allow an index to be set but returns the correct index', function() {
let node = new Node('foo');
let foo = new Node('foo');
node.push(foo);
foo.index = 42;
assert.equal(foo.index, 0);
});
it('should return -1 when siblings do not exist', function() {
let foo = new Node('foo');
assert.equal(foo.index, -1);
});
});
describe('.siblings', function() {
it('should get `node.parent.nodes`', function() {
let foo = new Node({ type: 'foo' });
let bar = new Node({ type: 'bar' });
let baz = new Node({ type: 'baz' });
let qux = new Node({ type: 'qux' });
foo.push(bar);
foo.push(baz);
foo.unshift(qux);
assert.equal(foo.siblings, null);
assert.equal(bar.siblings.length, 3);
assert.equal(baz.siblings.length, 3);
assert.equal(qux.siblings.length, 3);
});
it('should throw an error if set', function() {
assert.throws(function() {
let node = new Node('foo');
node.siblings = [];
});
});
});
describe('.push', function() {
it('should push nodes onto node.nodes', function() {
let node = new Node({ type: 'foo' });
assert(!node.nodes);
node.push(new Node({ type: 'a' }));
assert.equal(node.nodes.length, 1);
node.push(new Node({ type: 'b' }));
assert.equal(node.nodes.length, 2);
node.push(new Node({ type: 'c' }));
assert.equal(node.nodes.length, 3);
node.push(new Node({ type: 'd' }));
assert.equal(node.nodes.length, 4);
});
});
describe('.unshift', function() {
it('should unshift nodes onto node.nodes', function() {
let node = new Node({ type: 'foo' });
assert(!node.nodes);
node.unshift(new Node({ type: 'a' }));
assert.equal(node.nodes.length, 1);
node.unshift(new Node({ type: 'b' }));
assert.equal(node.nodes.length, 2);
node.unshift(new Node({ type: 'c' }));
assert.equal(node.nodes.length, 3);
node.unshift(new Node({ type: 'd' }));
assert.equal(node.nodes.length, 4);
});
});
describe('.pop', function() {
it('should remove the last node from node.nodes', function() {
let node = new Node({ type: 'foo' });
node.push(new Node({ type: 'a' }));
node.push(new Node({ type: 'b' }));
node.push(new Node({ type: 'c' }));
node.push(new Node({ type: 'd' }));
assert.equal(node.nodes.length, 4);
node.pop();
assert.equal(node.nodes.length, 3);
});
});
describe('.shift', function() {
it('should remove the last node from node.nodes', function() {
let node = new Node({ type: 'foo' });
node.push(new Node({ type: 'a' }));
node.push(new Node({ type: 'b' }));
node.push(new Node({ type: 'c' }));
node.push(new Node({ type: 'd' }));
assert.equal(node.nodes.length, 4);
node.shift();
assert.equal(node.nodes.length, 3);
});
it('should not blow up when no nodes exist', function() {
let node = new Node({ type: 'foo' });
node.shift();
assert(!node.nodes);
});
});
describe('.remove', function() {
it('should not do anything when a node does not exist', function() {
let node = new Node({ type: 'foo' });
assert(!node.nodes);
node.remove(new Node({ type: 'a' }));
assert(!node.nodes);
});
it('should remove the given node from node.nodes', function() {
let node = new Node({ type: 'foo' });
let a = new Node({ type: 'a' });
let b = new Node({ type: 'b' });
let c = new Node({ type: 'c' });
let d = new Node({ type: 'd' });
node.push(a);
node.push(b);
node.push(c);
node.push(d);
assert.equal(node.nodes.length, 4);
assert.equal(a.index, 0);
assert.equal(b.index, 1);
assert.equal(c.index, 2);
assert.equal(d.index, 3);
node.remove(b);
assert.equal(node.nodes.length, 3);
assert.equal(a.index, 0);
assert.equal(b.index, -1);
assert.equal(c.index, 1);
assert.equal(d.index, 2);
assert.equal(node.find('a'), a);
assert.equal(node.find('b'), null);
assert.equal(node.find('c'), c);
assert.equal(node.find('d'), d);
});
});
describe('.prev', function() {
it('should throw an error when setter is set', function() {
assert.throws(function() {
let node = new Node('foo');
node.prev = new Node('bar');
});
});
it('should get the prev node from node.nodes', function() {
let foo = new Node({ type: 'foo' });
let bar = new Node({ type: 'bar' });
let baz = new Node({ type: 'baz' });
foo.push(bar);
foo.push(baz);
assert.equal(bar.prev, null);
assert.equal(baz.prev.type, 'bar');
});
it('should get the prev node from `node.parent.nodes`', function() {
let parent = new Node({ type: 'parent' });
let a = new Node({ type: 'a' });
let z = new Node({ type: 'z' });
let foo = new Node({ type: 'foo' });
let bar = new Node({ type: 'bar' });
let baz = new Node({ type: 'baz' });
foo.push(bar);
foo.push(baz);
parent.push(a);
parent.push(foo);
parent.push(z);
assert.equal(bar.prev.type, 'a');
assert.equal(baz.prev.type, 'bar');
});
});
describe('.next', function() {
it('should throw an error when setter is set', function() {
assert.throws(function() {
let node = new Node('foo');
node.next = new Node('bar');
});
});
it('should get the next node from `node.nodes`', function() {
let foo = new Node({ type: 'foo' });
let bar = new Node({ type: 'bar' });
let baz = new Node({ type: 'baz' });
foo.push(bar);
foo.push(baz);
assert.equal(bar.next.type, 'baz');
assert.equal(baz.next, null);
});
it('should get the next node from `node.parent.nodes`', function() {
let parent = new Node({ type: 'parent' });
let a = new Node({ type: 'a' });
let z = new Node({ type: 'z' });
let foo = new Node({ type: 'foo' });
let bar = new Node({ type: 'bar' });
let baz = new Node({ type: 'baz' });
foo.push(bar);
foo.push(baz);
parent.push(a);
parent.push(foo);
parent.push(z);
assert.equal(bar.next.type, 'baz');
assert.equal(baz.next.type, 'z');
});
});
describe('.find', function() {
it('should get a node by type from `node.nodes`', function() {
assert.equal(ast.find('text').type, 'text');
assert.equal(ast.find('star').type, 'star');
});
it('should get a node by index from `node.nodes`', function() {
assert.equal(ast.find(0).type, 'bos');
assert.equal(ast.find(1).type, 'text');
});
it('should return null when a node does not exist', function() {
let node = new Node('foo');
assert.equal(node.find('slslsllsls'), null);
});
});
describe('.push', function() {
it('should add a node to `node.nodes`', function() {
let node = new Node({ type: 'foo' });
ast.push(node);
assert.equal(ast.last.type, 'foo');
});
it('should set the parent on the given node', function() {
let node = new Node({ type: 'foo' });
ast.push(node);
assert(ast === node.parent);
});
it('should set the parent.nodes as siblings', function() {
let node = new Node({ type: 'foo' });
ast.push(node);
assert.equal(node.siblings.length, 8);
});
it('should get the node.index from siblings', function() {
let node = new Node({ type: 'foo' });
ast.push(node);
assert.equal(node.index, 7);
});
});
describe('.remove', function() {
it('should remove a node from `node.nodes`', function() {
let node = new Node({ type: 'brace', nodes: [] });
let two = new Node('two', 'text');
node.push(new Node('one', 'text'));
node.push(two);
node.push(new Node('three', 'text'));
assert.equal(node.nodes.length, 3);
node.remove(two);
assert.equal(node.nodes.length, 2);
});
});
});