pax_global_header 0000666 0000000 0000000 00000000064 12776703477 0014536 g ustar 00root root 0000000 0000000 52 comment=1433d322d00a989984cc58de87ea3c4dac600988
snapdragon-0.8.1/ 0000775 0000000 0000000 00000000000 12776703477 0013700 5 ustar 00root root 0000000 0000000 snapdragon-0.8.1/.editorconfig 0000664 0000000 0000000 00000000405 12776703477 0016354 0 ustar 00root root 0000000 0000000 root = true
[*]
indent_style = space
end_of_line = lf
charset = utf-8
indent_size = 2
trim_trailing_whitespace = true
insert_final_newline = true
[{**/{actual,fixtures,expected,templates}/**,*.md}]
trim_trailing_whitespace = false
insert_final_newline = false snapdragon-0.8.1/.eslintrc.json 0000664 0000000 0000000 00000007125 12776703477 0016501 0 ustar 00root root 0000000 0000000 {
"ecmaFeatures": {
"modules": true,
"experimentalObjectRestSpread": true
},
"env": {
"browser": false,
"es6": true,
"node": true,
"mocha": true
},
"globals": {
"document": false,
"navigator": false,
"window": false
},
"rules": {
"accessor-pairs": 2,
"arrow-spacing": [2, { "before": true, "after": true }],
"block-spacing": [2, "always"],
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
"comma-dangle": [2, "never"],
"comma-spacing": [2, { "before": false, "after": true }],
"comma-style": [2, "last"],
"constructor-super": 2,
"curly": [2, "multi-line"],
"dot-location": [2, "property"],
"eol-last": 2,
"eqeqeq": [2, "allow-null"],
"generator-star-spacing": [2, { "before": true, "after": true }],
"handle-callback-err": [2, "^(err|error)$" ],
"indent": [2, 2, { "SwitchCase": 1 }],
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"keyword-spacing": [2, { "before": true, "after": true }],
"new-cap": [2, { "newIsCap": true, "capIsNew": false }],
"new-parens": 2,
"no-array-constructor": 2,
"no-caller": 2,
"no-class-assign": 2,
"no-cond-assign": 2,
"no-const-assign": 2,
"no-control-regex": 2,
"no-debugger": 2,
"no-delete-var": 2,
"no-dupe-args": 2,
"no-dupe-class-members": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty-character-class": 2,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": [2, "functions"],
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-label-var": 2,
"no-labels": 2,
"no-lone-blocks": 2,
"no-mixed-spaces-and-tabs": 2,
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [2, { "max": 1 }],
"no-native-reassign": 0,
"no-negated-in-lhs": 2,
"no-new": 2,
"no-new-func": 2,
"no-new-object": 2,
"no-new-require": 2,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-proto": 0,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-return-assign": 2,
"no-self-compare": 2,
"no-sequences": 2,
"no-shadow-restricted-names": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-this-before-super": 2,
"no-throw-literal": 2,
"no-trailing-spaces": 0,
"no-undef": 2,
"no-undef-init": 2,
"no-unexpected-multiline": 2,
"no-unneeded-ternary": [2, { "defaultAssignment": false }],
"no-unreachable": 2,
"no-unused-vars": [2, { "vars": "all", "args": "none" }],
"no-useless-call": 0,
"no-with": 2,
"one-var": [0, { "initialized": "never" }],
"operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }],
"padded-blocks": [0, "never"],
"quotes": [2, "single", "avoid-escape"],
"radix": 2,
"semi": [2, "always"],
"semi-spacing": [2, { "before": false, "after": true }],
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, "never"],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }],
"use-isnan": 2,
"valid-typeof": 2,
"wrap-iife": [2, "any"],
"yoda": [2, "never"]
}
}
snapdragon-0.8.1/.gitattributes 0000664 0000000 0000000 00000000017 12776703477 0016571 0 ustar 00root root 0000000 0000000 *.* text eol=lf snapdragon-0.8.1/.gitignore 0000664 0000000 0000000 00000000343 12776703477 0015670 0 ustar 00root root 0000000 0000000 # always ignore files
*.DS_Store
*.sublime-*
# test related, or directories generated by tests
test/actual
actual
coverage
# npm
node_modules
npm-debug.log
# misc
_gh_pages
benchmark
bower_components
vendor
temp
tmp
TODO.md
snapdragon-0.8.1/.travis.yml 0000664 0000000 0000000 00000000100 12776703477 0016000 0 ustar 00root root 0000000 0000000 sudo: false
language: node_js
node_js:
- node
- '6'
- '5'
snapdragon-0.8.1/.verb.md 0000664 0000000 0000000 00000010335 12776703477 0015240 0 ustar 00root root 0000000 0000000 Created by [jonschlinkert]({%= author.url %}) and [doowb](https://github.com/doowb).
**Features**
- Bootstrap your own parser, get sourcemap support for free
- All parsing and compiling is handled by simple, reusable middleware functions
- Inspired by the parsers in [pug][] and [css][].
## History
### v0.5.0
**Breaking changes**
Substantial breaking changes were made in v0.5.0! Most of these changes are part of a larger refactor that will be finished in 0.6.0, including the introduction of a `Lexer` class.
- Renderer was renamed to `Compiler`
- the `.render` method was renamed to `.compile`
- Many other smaller changes. A more detailed overview will be provided in 0.6.0. If you don't have to time review code, I recommend you wait for the 0.6.0 release.
## Usage examples
```js
var Snapdragon = require('{%= name %}');
var snapdragon = new Snapdragon();
```
**Parse**
```js
var ast = snapdragon.parser('some string', options)
// parser middleware that can be called by other middleware
.set('foo', function () {})
// parser middleware, runs immediately in the order defined
.use(bar())
.use(baz())
```
**Render**
```js
// pass the `ast` from the parse method
var res = snapdragon.compiler(ast)
// compiler middleware, called when the name of the middleware
// matches the `node.type` (defined in a parser middleware)
.set('bar', function () {})
.set('baz', function () {})
.compile()
```
See the [examples](./examples/).
## Getting started
**Parsers**
Parsers are middleware functions used for parsing a string into an ast node.
```js
var ast = snapdragon.parser(str, options)
.use(function() {
var pos = this.position();
var m = this.match(/^\./);
if (!m) return;
return pos({
// `type` specifies the compiler to use
type: 'dot',
val: m[0]
});
})
```
**AST node**
When the parser finds a match, `pos()` is called, pushing a token for that node onto the ast that looks something like:
```js
{ type: 'dot',
val: '.',
position:
{ start: { lineno: 1, column: 1 },
end: { lineno: 1, column: 2 } }}
```
**Renderers**
Renderers are _named_ middleware functions that visit over an array of ast nodes to compile a string.
```js
var res = snapdragon.compiler(ast)
.set('dot', function (node) {
console.log(node.val)
//=> '.'
return this.emit(node.val);
})
```
**Source maps**
If you want source map support, make sure to emit the position as well.
```js
var res = snapdragon.compiler(ast)
.set('dot', function (node) {
return this.emit(node.val, node.position);
})
```
## Docs
### Parser middleware
A parser middleware is a function that returns an abject called a `token`. This token is pushed onto the AST as a node.
**Example token**
```js
{ type: 'dot',
val: '.',
position:
{ start: { lineno: 1, column: 1 },
end: { lineno: 1, column: 2 } }}
```
**Example parser middleware**
Match a single `.` in a string:
1. Get the starting position by calling `this.position()`
1. pass a regex for matching a single dot to the `.match` method
1. if **no match** is found, return `undefined`
1. if a **match** is found, `pos()` is called, which returns a token with:
* `type`: the name of the [compiler] to use
* `val`: The actual value captured by the regex. In this case, a `.`. Note that you can capture and return whatever will be needed by the corresponding [compiler].
* The ending position: automatically calculated by adding the length of the first capture group to the starting position.
## Renderer middleware
Renderers are run when the name of the compiler middleware matches the `type` defined on an ast `node` (which is defined in a parser).
**Example**
Exercise: Parse a dot, then compile it as an escaped dot.
```js
var ast = snapdragon.parser('.')
.use(function () {
var pos = this.position();
var m = this.match(/^\./);
if (!m) return;
return pos({
// define the `type` of compiler to use
type: 'dot',
val: m[0]
})
})
var result = snapdragon.compiler(ast)
.set('dot', function (node) {
return this.emit('\\' + node.val);
})
.compile()
console.log(result.output);
//=> '\.'
```
## API
### Parse
{%= apidocs("lib/parser.js") %}
### Render
{%= apidocs("lib/compiler.js") %}
snapdragon-0.8.1/LICENSE 0000664 0000000 0000000 00000002100 12776703477 0014676 0 ustar 00root root 0000000 0000000 The MIT License (MIT)
Copyright (c) 2015-2016, 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-0.8.1/README.md 0000664 0000000 0000000 00000021352 12776703477 0015162 0 ustar 00root root 0000000 0000000 # snapdragon [](https://www.npmjs.com/package/snapdragon) [](https://npmjs.org/package/snapdragon) [](https://travis-ci.org/jonschlinkert/snapdragon)
> Fast, pluggable and easy-to-use parser-renderer factory.
## Install
Install with [npm](https://www.npmjs.com/):
```sh
$ npm install --save snapdragon
```
Created by [jonschlinkert](https://github.com/jonschlinkert) and [doowb](https://github.com/doowb).
**Features**
* Bootstrap your own parser, get sourcemap support for free
* All parsing and compiling is handled by simple, reusable middleware functions
* Inspired by the parsers in [pug](http://jade-lang.com) and [css](https://github.com/reworkcss/css).
## History
### v0.5.0
**Breaking changes**
Substantial breaking changes were made in v0.5.0! Most of these changes are part of a larger refactor that will be finished in 0.6.0, including the introduction of a `Lexer` class.
* Renderer was renamed to `Compiler`
* the `.render` method was renamed to `.compile`
* Many other smaller changes. A more detailed overview will be provided in 0.6.0. If you don't have to time review code, I recommend you wait for the 0.6.0 release.
## Usage examples
```js
var Snapdragon = require('snapdragon');
var snapdragon = new Snapdragon();
```
**Parse**
```js
var ast = snapdragon.parser('some string', options)
// parser middleware that can be called by other middleware
.set('foo', function () {})
// parser middleware, runs immediately in the order defined
.use(bar())
.use(baz())
```
**Render**
```js
// pass the `ast` from the parse method
var res = snapdragon.compiler(ast)
// compiler middleware, called when the name of the middleware
// matches the `node.type` (defined in a parser middleware)
.set('bar', function () {})
.set('baz', function () {})
.compile()
```
See the [examples](./examples/).
## Getting started
**Parsers**
Parsers are middleware functions used for parsing a string into an ast node.
```js
var ast = snapdragon.parser(str, options)
.use(function() {
var pos = this.position();
var m = this.match(/^\./);
if (!m) return;
return pos({
// `type` specifies the compiler to use
type: 'dot',
val: m[0]
});
})
```
**AST node**
When the parser finds a match, `pos()` is called, pushing a token for that node onto the ast that looks something like:
```js
{ type: 'dot',
val: '.',
position:
{ start: { lineno: 1, column: 1 },
end: { lineno: 1, column: 2 } }}
```
**Renderers**
Renderers are _named_ middleware functions that visit over an array of ast nodes to compile a string.
```js
var res = snapdragon.compiler(ast)
.set('dot', function (node) {
console.log(node.val)
//=> '.'
return this.emit(node.val);
})
```
**Source maps**
If you want source map support, make sure to emit the position as well.
```js
var res = snapdragon.compiler(ast)
.set('dot', function (node) {
return this.emit(node.val, node.position);
})
```
## Docs
### Parser middleware
A parser middleware is a function that returns an abject called a `token`. This token is pushed onto the AST as a node.
**Example token**
```js
{ type: 'dot',
val: '.',
position:
{ start: { lineno: 1, column: 1 },
end: { lineno: 1, column: 2 } }}
```
**Example parser middleware**
Match a single `.` in a string:
1. Get the starting position by calling `this.position()`
2. pass a regex for matching a single dot to the `.match` method
3. if **no match** is found, return `undefined`
4. if a **match** is found, `pos()` is called, which returns a token with:
- `type`: the name of the [compiler] to use
- `val`: The actual value captured by the regex. In this case, a `.`. Note that you can capture and return whatever will be needed by the corresponding [compiler].
- The ending position: automatically calculated by adding the length of the first capture group to the starting position.
## Renderer middleware
Renderers are run when the name of the compiler middleware matches the `type` defined on an ast `node` (which is defined in a parser).
**Example**
Exercise: Parse a dot, then compile it as an escaped dot.
```js
var ast = snapdragon.parser('.')
.use(function () {
var pos = this.position();
var m = this.match(/^\./);
if (!m) return;
return pos({
// define the `type` of compiler to use
type: 'dot',
val: m[0]
})
})
var result = snapdragon.compiler(ast)
.set('dot', function (node) {
return this.emit('\\' + node.val);
})
.compile()
console.log(result.output);
//=> '\.'
```
## API
### [Parser](lib/parser.js#L19)
Create a new `Parser` with the given `input` and `options`.
**Params**
* `input` **{String}**
* `options` **{Object}**
### [.define](lib/parser.js#L103)
Define a non-enumberable property on the `Parser` instance.
**Example**
```js
parser.define('foo', 'bar');
```
**Params**
* `key` **{String}**: propery name
* `val` **{any}**: property value
* `returns` **{Object}**: Returns the Parser instance for chaining.
Set parser `name` with the given `fn`
**Params**
* `name` **{String}**
* `fn` **{Function}**
Get parser `name`
**Params**
* `name` **{String}**
Push a `token` onto the `type` stack.
**Params**
* `type` **{String}**
* `returns` **{Object}** `token`
Pop a token off of the `type` stack
**Params**
* `type` **{String}**
* `returns` **{Object}**: Returns a token
Return true if inside a `stack` node. Types are `braces`, `parens` or `brackets`.
**Params**
* `type` **{String}**
* `returns` **{Boolean}**
**Example**
```js
parser.isType(node, 'brace');
```
**Params**
* `node` **{Object}**
* `type` **{String}**
* `returns` **{Boolean}**
### [.define](lib/compiler.js#L71)
Define a non-enumberable property on the `Compiler` instance.
**Example**
```js
compiler.define('foo', 'bar');
```
**Params**
* `key` **{String}**: propery name
* `val` **{any}**: property value
* `returns` **{Object}**: Returns the Compiler instance for chaining.
## About
### Related projects
* [braces](https://www.npmjs.com/package/braces): Fastest brace expansion for node.js, with the most complete support for the Bash 4.3 braces… [more](https://github.com/jonschlinkert/braces) | [homepage](https://github.com/jonschlinkert/braces "Fastest brace expansion for node.js, with the most complete support for the Bash 4.3 braces specification.")
* [expand-brackets](https://www.npmjs.com/package/expand-brackets): Expand POSIX bracket expressions (character classes) in glob patterns. | [homepage](https://github.com/jonschlinkert/expand-brackets "Expand POSIX bracket expressions (character classes) in glob patterns.")
* [extglob](https://www.npmjs.com/package/extglob): Convert extended globs to regex-compatible strings. Add (almost) the expressive power of regular expressions to… [more](https://github.com/jonschlinkert/extglob) | [homepage](https://github.com/jonschlinkert/extglob "Convert extended globs to regex-compatible strings. Add (almost) the expressive power of regular expressions to glob patterns.")
* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/jonschlinkert/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
### Contributing
Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
### Contributors
| **Commits** | **Contributor**
|
| --- | --- |
| 106 | [jonschlinkert](https://github.com/jonschlinkert) |
| 2 | [doowb](https://github.com/doowb) |
### Building docs
_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_
To generate the readme and API documentation with [verb](https://github.com/verbose/verb):
```sh
$ npm install -g verb verb-generate-readme && verb
```
### Running tests
Install dev dependencies:
```sh
$ npm install -d && npm test
```
### Author
**Jon Schlinkert**
* [github/jonschlinkert](https://github.com/jonschlinkert)
* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
### License
Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert).
Released under the [MIT license](https://github.com/jonschlinkert/snapdragon/blob/master/LICENSE).
***
_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.31, on October 10, 2016._ snapdragon-0.8.1/example.js 0000664 0000000 0000000 00000000314 12776703477 0015667 0 ustar 00root root 0000000 0000000 'use strict';
var Parser = require('./lib/parser');
// var parser = new Parser('**/foo/*.js');
var parser = new Parser('**/{a,b,/{c,d}}/*.js');
var res = parser.parse();
console.log(res.ast.nodes[2]);
snapdragon-0.8.1/examples/ 0000775 0000000 0000000 00000000000 12776703477 0015516 5 ustar 00root root 0000000 0000000 snapdragon-0.8.1/examples/extglob.js 0000664 0000000 0000000 00000003630 12776703477 0017522 0 ustar 00root root 0000000 0000000 'use strict';
var Compiler = require('../lib/compiler');
var Parser = require('../lib/parser');
var parser = new Parser()
.use(function() {
var pos = this.position();
var m = this.match(/^\\(.)/);
if (!m) return;
return pos({
type: 'escaped',
val: m[1]
});
})
.use(function() {
var pos = this.position();
var m = this.match(/^\{/);
if (!m) return;
this.isOpen = true;
return pos({
type: 'brace.open',
val: m[0]
});
})
.use(function() {
var pos = this.position();
var m = this.match(/^\}/);
if (!m) return;
if (!this.isOpen) {
throw new Error('missing opening brace');
}
this.isOpen = false;
return pos({
type: 'brace.close',
val: m[0]
});
})
.use(function() {
var pos = this.position();
var m = this.match(/^,/);
if (!m) return;
return pos({
type: 'comma',
val: m[0]
});
})
.use(function() {
var pos = this.position();
var m = this.match(/^\w+/);
if (!m) return;
return pos({
type: 'text',
val: m[0]
});
})
.use(function() {
var pos = this.position();
var m = this.match(/^\//);
if (!m) return;
return pos({
type: 'slash',
val: m[0]
});
});
var compiler = new Compiler()
.set('escaped', function(node) {
return this.emit('\\' + node.val, node.position);
})
.set('brace.open', function(node) {
return this.emit('(?:', node.position);
})
.set('brace.close', function(node) {
return this.emit(')', node.position);
})
.set('comma', function(node) {
return this.emit('|', node.position);
})
.set('text', function(node) {
return this.emit(node.val, node.position);
})
.set('slash', function(node) {
return this.emit('/', node.position);
});
var ast = parser.parse('a/\\{{b,c,d}/e');
var res = compiler.compile(ast, {sourcemap: true});
console.log(res);
snapdragon-0.8.1/gulpfile.js 0000664 0000000 0000000 00000001510 12776703477 0016042 0 ustar 00root root 0000000 0000000 'use strict';
var gulp = require('gulp');
var istanbul = require('gulp-istanbul');
var eslint = require('gulp-eslint');
var mocha = require('gulp-mocha');
var unused = require('gulp-unused');
gulp.task('coverage', function() {
return gulp.src(['lib/*.js', 'index.js'])
.pipe(istanbul({includeUntested: true}))
.pipe(istanbul.hookRequire());
});
gulp.task('mocha', ['coverage'], function() {
return gulp.src(['test/*.js'])
.pipe(mocha())
.pipe(istanbul.writeReports());
});
gulp.task('eslint', function() {
return gulp.src(['*.js', 'lib/*.js', 'test/*.js'])
.pipe(eslint())
.pipe(eslint.format());
});
gulp.task('unused', function() {
return gulp.src(['index.js', 'lib/*.js'])
.pipe(unused({keys: Object.keys(require('./lib/utils.js'))}));
});
gulp.task('default', ['coverage', 'eslint', 'mocha']);
snapdragon-0.8.1/index.js 0000664 0000000 0000000 00000007621 12776703477 0015353 0 ustar 00root root 0000000 0000000 'use strict';
var Base = require('base');
var define = require('define-property');
var Compiler = require('./lib/compiler');
var Parser = require('./lib/parser');
var utils = require('./lib/utils');
var regexCache = {};
var cache = {};
/**
* Create a new instance of `Snapdragon` with the given `options`.
*
* ```js
* var snapdragon = new Snapdragon();
* ```
*
* @param {Object} `options`
* @api public
*/
function Snapdragon(options) {
Base.call(this, null, options);
this.options = utils.extend({source: 'string'}, this.options);
this.compiler = new Compiler(this.options);
this.parser = new Parser(this.options);
Object.defineProperty(this, 'compilers', {
get: function() {
return this.compiler.compilers;
}
});
Object.defineProperty(this, 'parsers', {
get: function() {
return this.parser.parsers;
}
});
Object.defineProperty(this, 'regex', {
get: function() {
return this.parser.regex;
}
});
}
/**
* Inherit Base
*/
Base.extend(Snapdragon);
/**
* Add a parser to `snapdragon.parsers` for capturing the given `type` using
* the specified regex or parser function. A function is useful if you need
* to customize how the token is created and/or have access to the parser
* instance to check options, etc.
*
* ```js
* snapdragon
* .capture('slash', /^\//)
* .capture('dot', function() {
* var pos = this.position();
* var m = this.match(/^\./);
* if (!m) return;
* return pos({
* type: 'dot',
* val: m[0]
* });
* });
* ```
* @param {String} `type`
* @param {RegExp|Function} `regex`
* @return {Object} Returns the parser instance for chaining
* @api public
*/
Snapdragon.prototype.capture = function() {
return this.parser.capture.apply(this.parser, arguments);
};
/**
* Register a plugin `fn`.
*
* ```js
* var snapdragon = new Snapdgragon([options]);
* snapdragon.use(function() {
* console.log(this); //<= snapdragon instance
* console.log(this.parser); //<= parser instance
* console.log(this.compiler); //<= compiler instance
* });
* ```
* @param {Object} `fn`
* @api public
*/
Snapdragon.prototype.use = function(fn) {
fn.call(this, this);
return this;
};
/**
* Parse the given `str`.
*
* ```js
* var snapdragon = new Snapdgragon([options]);
* // register parsers
* snapdragon.parser.use(function() {});
*
* // parse
* var ast = snapdragon.parse('foo/bar');
* console.log(ast);
* ```
* @param {String} `str`
* @param {Object} `options` Set `options.sourcemap` to true to enable source maps.
* @return {Object} Returns an AST.
* @api public
*/
Snapdragon.prototype.parse = function(str, options) {
this.options = utils.extend({}, this.options, options);
var parsed = this.parser.parse(str, this.options);
// add non-enumerable parser reference
define(parsed, 'parser', this.parser);
return parsed;
};
/**
* Compile the given `AST`.
*
* ```js
* var snapdragon = new Snapdgragon([options]);
* // register plugins
* snapdragon.use(function() {});
* // register parser plugins
* snapdragon.parser.use(function() {});
* // register compiler plugins
* snapdragon.compiler.use(function() {});
*
* // parse
* var ast = snapdragon.parse('foo/bar');
*
* // compile
* var res = snapdragon.compile(ast);
* console.log(res.output);
* ```
* @param {Object} `ast`
* @param {Object} `options`
* @return {Object} Returns an object with an `output` property with the rendered string.
* @api public
*/
Snapdragon.prototype.compile = function(ast, options) {
this.options = utils.extend({}, this.options, options);
var compiled = this.compiler.compile(ast, this.options);
// add non-enumerable compiler reference
define(compiled, 'compiler', this.compiler);
return compiled;
};
/**
* Expose `Snapdragon`
*/
module.exports = Snapdragon;
/**
* Expose `Parser` and `Compiler`
*/
module.exports.Compiler = Compiler;
module.exports.Parser = Parser;
snapdragon-0.8.1/lib/ 0000775 0000000 0000000 00000000000 12776703477 0014446 5 ustar 00root root 0000000 0000000 snapdragon-0.8.1/lib/compiler.js 0000664 0000000 0000000 00000007007 12776703477 0016622 0 ustar 00root root 0000000 0000000 'use strict';
var use = require('use');
var define = require('define-property');
var debug = require('debug')('snapdragon:compiler');
var utils = require('./utils');
/**
* Create a new `Compiler` with the given `options`.
* @param {Object} `options`
*/
function Compiler(options, state) {
debug('initializing', __filename);
this.options = utils.extend({source: 'string'}, options);
this.state = state || {};
this.compilers = {};
this.output = '';
this.set('eos', function(node) {
return this.emit(node.val, node);
});
this.set('noop', function(node) {
return this.emit(node.val, node);
});
this.set('bos', function(node) {
return this.emit(node.val, node);
});
use(this);
}
/**
* Prototype methods
*/
Compiler.prototype = {
/**
* Throw an error message with details including the cursor position.
* @param {String} `msg` Message to use in the Error.
*/
error: function(msg, node) {
var pos = node.position || {start: {column: 0}};
var message = this.options.source + ' column:' + pos.start.column + ': ' + msg;
var err = new Error(message);
err.reason = msg;
err.column = pos.start.column;
err.source = this.pattern;
if (this.options.silent) {
this.errors.push(err);
} else {
throw err;
}
},
/**
* Define a non-enumberable property on the `Compiler` instance.
*
* ```js
* compiler.define('foo', 'bar');
* ```
* @name .define
* @param {String} `key` propery name
* @param {any} `val` property value
* @return {Object} Returns the Compiler instance for chaining.
* @api public
*/
define: function(key, val) {
define(this, key, val);
return this;
},
/**
* Emit `node.val`
*/
emit: function(str, node) {
this.output += str;
return str;
},
/**
* Add a compiler `fn` with the given `name`
*/
set: function(name, fn) {
this.compilers[name] = fn;
return this;
},
/**
* Get compiler `name`.
*/
get: function(name) {
return this.compilers[name];
},
/**
* Get the previous AST node.
*/
prev: function(n) {
return this.ast.nodes[this.idx - (n || 1)] || { type: 'bos', val: '' };
},
/**
* Get the next AST node.
*/
next: function(n) {
return this.ast.nodes[this.idx + (n || 1)] || { type: 'eos', val: '' };
},
/**
* Visit `node`.
*/
visit: function(node, nodes, i) {
var fn = this.compilers[node.type];
this.idx = i;
if (typeof fn !== 'function') {
throw this.error('compiler "' + node.type + '" is not registered', node);
}
return fn.call(this, node, nodes, i);
},
/**
* Map visit over array of `nodes`.
*/
mapVisit: function(nodes) {
if (!Array.isArray(nodes)) {
throw new TypeError('expected an array');
}
var len = nodes.length;
var idx = -1;
while (++idx < len) {
this.visit(nodes[idx], nodes, idx);
}
return this;
},
/**
* Compile `ast`.
*/
compile: function(ast, options) {
var opts = utils.extend({}, this.options, options);
this.ast = ast;
this.parsingErrors = this.ast.errors;
this.output = '';
// source map support
if (opts.sourcemap) {
var sourcemaps = require('./source-maps');
sourcemaps(this);
this.mapVisit(this.ast.nodes);
this.applySourceMaps();
this.map = opts.sourcemap === 'generator' ? this.map : this.map.toJSON();
return this;
}
this.mapVisit(this.ast.nodes);
return this;
}
};
/**
* Expose `Compiler`
*/
module.exports = Compiler;
snapdragon-0.8.1/lib/parser.js 0000664 0000000 0000000 00000025536 12776703477 0016313 0 ustar 00root root 0000000 0000000 'use strict';
var use = require('use');
var util = require('util');
var Cache = require('map-cache');
var define = require('define-property');
var debug = require('debug')('snapdragon:parser');
var Position = require('./position');
var utils = require('./utils');
/**
* Create a new `Parser` with the given `input` and `options`.
* @param {String} `input`
* @param {Object} `options`
* @api public
*/
function Parser(options) {
debug('initializing', __filename);
this.options = utils.extend({source: 'string'}, options);
this.init(this.options);
use(this);
}
/**
* Prototype methods
*/
Parser.prototype = {
constructor: Parser,
init: function(options) {
this.orig = '';
this.input = '';
this.parsed = '';
this.column = 1;
this.line = 1;
this.regex = new Cache();
this.errors = this.errors || [];
this.parsers = this.parsers || {};
this.types = this.types || [];
this.sets = this.sets || {};
this.fns = this.fns || [];
this.currentType = 'root';
var pos = this.position();
this.bos = pos({type: 'bos', val: ''});
this.ast = {
type: 'root',
errors: this.errors,
nodes: [this.bos]
};
define(this.bos, 'parent', this.ast);
this.nodes = [this.ast];
this.count = 0;
this.setCount = 0;
this.stack = [];
},
/**
* Throw a formatted error with the cursor column and `msg`.
* @param {String} `msg` Message to use in the Error.
*/
error: function(msg, node) {
var pos = node.position || {start: {column: 0, line: 0}};
var line = pos.start.line;
var column = pos.start.column;
var source = this.options.source;
var message = source + ' : ' + msg;
var err = new Error(message);
err.source = source;
err.reason = msg;
err.pos = pos;
if (this.options.silent) {
this.errors.push(err);
} else {
throw err;
}
},
/**
* Define a non-enumberable property on the `Parser` instance.
*
* ```js
* parser.define('foo', 'bar');
* ```
* @name .define
* @param {String} `key` propery name
* @param {any} `val` property value
* @return {Object} Returns the Parser instance for chaining.
* @api public
*/
define: function(key, val) {
define(this, key, val);
return this;
},
/**
* Mark position and patch `node.position`.
*/
position: function() {
var start = { line: this.line, column: this.column };
var self = this;
return function(node) {
define(node, 'position', new Position(start, self));
return node;
};
},
/**
* Set parser `name` with the given `fn`
* @param {String} `name`
* @param {Function} `fn`
* @api public
*/
set: function(type, fn) {
if (this.types.indexOf(type) === -1) {
this.types.push(type);
}
this.parsers[type] = fn.bind(this);
return this;
},
/**
* Get parser `name`
* @param {String} `name`
* @api public
*/
get: function(name) {
return this.parsers[name];
},
/**
* Push a `token` onto the `type` stack.
*
* @param {String} `type`
* @return {Object} `token`
* @api public
*/
push: function(type, token) {
this.sets[type] = this.sets[type] || [];
this.count++;
this.stack.push(token);
return this.sets[type].push(token);
},
/**
* Pop a token off of the `type` stack
* @param {String} `type`
* @returns {Object} Returns a token
* @api public
*/
pop: function(type) {
this.sets[type] = this.sets[type] || [];
this.count--;
this.stack.pop();
return this.sets[type].pop();
},
/**
* Return true if inside a `stack` node. Types are `braces`, `parens` or `brackets`.
*
* @param {String} `type`
* @return {Boolean}
* @api public
*/
isInside: function(type) {
this.sets[type] = this.sets[type] || [];
return this.sets[type].length > 0;
},
/**
* Return true if `node` is the given `type`.
*
* ```js
* parser.isType(node, 'brace');
* ```
* @param {Object} `node`
* @param {String} `type`
* @return {Boolean}
* @api public
*/
isType: function(node, type) {
return node && node.type === type;
},
/**
* Get the previous AST node
* @return {Object}
*/
prev: function(n) {
return this.stack.length > 0
? utils.last(this.stack, n)
: utils.last(this.nodes, n);
},
/**
* Update line and column based on `str`.
*/
consume: function(len) {
this.input = this.input.substr(len);
},
/**
* Update column based on `str`.
*/
updatePosition: function(str, len) {
var lines = str.match(/\n/g);
if (lines) this.line += lines.length;
var i = str.lastIndexOf('\n');
this.column = ~i ? len - i : this.column + len;
this.parsed += str;
this.consume(len);
},
/**
* Match `regex`, return captures, and update the cursor position by `match[0]` length.
* @param {RegExp} `regex`
* @return {Object}
*/
match: function(regex) {
var m = regex.exec(this.input);
if (m) {
this.updatePosition(m[0], m[0].length);
return m;
}
},
/**
* Capture `type` with the given regex.
* @param {String} `type`
* @param {RegExp} `regex`
* @return {Function}
*/
capture: function(type, regex) {
if (typeof regex === 'function') {
return this.set.apply(this, arguments);
}
this.regex.set(type, regex);
this.set(type, function() {
var parsed = this.parsed;
var pos = this.position();
var m = this.match(regex);
if (!m || !m[0]) return;
var prev = this.prev();
var node = pos({
type: type,
val: m[0],
parsed: parsed,
rest: this.input
});
if (m[1]) {
node.inner = m[1];
}
define(node, 'inside', this.stack.length > 0);
define(node, 'parent', prev);
prev.nodes.push(node);
}.bind(this));
return this;
},
/**
* Create a parser with open and close for parens,
* brackets or braces
*/
capturePair: function(type, openRegex, closeRegex, fn) {
this.sets[type] = this.sets[type] || [];
/**
* Open
*/
this.set(type + '.open', function() {
var parsed = this.parsed;
var pos = this.position();
var m = this.match(openRegex);
if (!m || !m[0]) return;
var val = m[0];
this.setCount++;
this.specialChars = true;
var open = pos({
type: type + '.open',
val: val,
rest: this.input
});
if (typeof m[1] !== 'undefined') {
open.inner = m[1];
}
var prev = this.prev();
var node = pos({
type: type,
nodes: [open]
});
define(node, 'rest', this.input);
define(node, 'parsed', parsed);
define(node, 'prefix', m[1]);
define(node, 'parent', prev);
define(open, 'parent', node);
if (typeof fn === 'function') {
fn.call(this, open, node);
}
this.push(type, node);
prev.nodes.push(node);
});
/**
* Close
*/
this.set(type + '.close', function() {
var pos = this.position();
var m = this.match(closeRegex);
if (!m || !m[0]) return;
var parent = this.pop(type);
var node = pos({
type: type + '.close',
rest: this.input,
suffix: m[1],
val: m[0]
});
if (!this.isType(parent, type)) {
if (this.options.strict) {
throw new Error('missing opening "' + type + '"');
}
this.setCount--;
node.escaped = true;
return node;
}
if (node.suffix === '\\') {
parent.escaped = true;
node.escaped = true;
}
parent.nodes.push(node);
define(node, 'parent', parent);
});
return this;
},
/**
* Capture end-of-string
*/
eos: function() {
var pos = this.position();
if (this.input) return;
var prev = this.prev();
while (prev.type !== 'root' && !prev.visited) {
if (this.options.strict === true) {
throw new SyntaxError('invalid syntax:' + util.inspect(prev, null, 2));
}
if (!hasDelims(prev)) {
prev.parent.escaped = true;
prev.escaped = true;
}
visit(prev, function(node) {
if (!hasDelims(node.parent)) {
node.parent.escaped = true;
node.escaped = true;
}
});
prev = prev.parent;
}
var tok = pos({
type: 'eos',
val: this.append || ''
});
define(tok, 'parent', this.ast);
return tok;
},
/**
* Run parsers to advance the cursor position
*/
next: function() {
var parsed = this.parsed;
var len = this.types.length;
var idx = -1;
var tok;
while (++idx < len) {
if ((tok = this.parsers[this.types[idx]].call(this))) {
define(tok, 'rest', this.input);
define(tok, 'parsed', parsed);
this.last = tok;
return tok;
}
}
},
/**
* Parse the given string.
* @return {Array}
*/
parse: function(input) {
if (typeof input !== 'string') {
throw new TypeError('expected a string');
}
this.init(this.options);
this.orig = input;
this.input = input;
var self = this;
function parse() {
// check input before calling `.next()`
input = self.input;
// get the next AST ndoe
var node = self.next();
if (node) {
var prev = self.prev();
if (prev) {
define(node, 'parent', prev);
if (prev.nodes) {
prev.nodes.push(node);
}
}
if (self.sets.hasOwnProperty(prev.type)) {
self.currentType = prev.type;
}
}
// if we got here but input is not changed, throw an error
if (self.input && input === self.input) {
throw new Error('no parsers registered for: "' + self.input.slice(0, 5) + '"');
}
}
while (this.input) parse();
if (this.stack.length && this.options.strict) {
var node = this.stack.pop();
throw this.error('missing opening ' + node.type + ': "' + this.orig + '"');
}
var eos = this.eos();
var tok = this.prev();
if (tok.type !== 'eos') {
this.ast.nodes.push(eos);
}
return this.ast;
}
};
/**
* Visit `node` with the given `fn`
*/
function visit(node, fn) {
if (!node.visited) {
define(node, 'visited', true);
return node.nodes ? mapVisit(node.nodes, fn) : fn(node);
}
return node;
}
/**
* Map visit over array of `nodes`.
*/
function mapVisit(nodes, fn) {
var len = nodes.length;
var idx = -1;
while (++idx < len) {
visit(nodes[idx], fn);
}
}
function hasOpen(node) {
return node.nodes && node.nodes[0].type === (node.type + '.open');
}
function hasClose(node) {
return node.nodes && utils.last(node.nodes).type === (node.type + '.close');
}
function hasDelims(node) {
return hasOpen(node) && hasClose(node);
}
/**
* Expose `Parser`
*/
module.exports = Parser;
snapdragon-0.8.1/lib/position.js 0000664 0000000 0000000 00000000500 12776703477 0016643 0 ustar 00root root 0000000 0000000 'use strict';
var define = require('define-property');
/**
* Store position for a node
*/
module.exports = function Position(start, parser) {
this.start = start;
this.end = { line: parser.line, column: parser.column };
define(this, 'content', parser.orig);
define(this, 'source', parser.options.source);
};
snapdragon-0.8.1/lib/source-maps.js 0000664 0000000 0000000 00000007024 12776703477 0017245 0 ustar 00root root 0000000 0000000 'use strict';
var fs = require('fs');
var path = require('path');
var define = require('define-property');
var utils = require('./utils');
/**
* Expose `mixin()`.
* This code is based on `source-maps-support.js` in reworkcss/css
* https://github.com/reworkcss/css/blob/master/lib/stringify/source-map-support.js
* Copyright (c) 2012 TJ Holowaychuk
*/
module.exports = mixin;
/**
* Mixin source map support into `compiler`.
*
* @param {Object} `compiler`
* @api public
*/
function mixin(compiler) {
define(compiler, '_comment', compiler.comment);
compiler.map = new utils.SourceMap.SourceMapGenerator();
compiler.position = { line: 1, column: 1 };
compiler.content = {};
compiler.files = {};
for (var key in exports) {
define(compiler, key, exports[key]);
}
}
/**
* Update position.
*
* @param {String} str
*/
exports.updatePosition = function(str) {
var lines = str.match(/\n/g);
if (lines) this.position.line += lines.length;
var i = str.lastIndexOf('\n');
this.position.column = ~i ? str.length - i : this.position.column + str.length;
};
/**
* Emit `str` with `position`.
*
* @param {String} str
* @param {Object} [pos]
* @return {String}
*/
exports.emit = function(str, node) {
var position = node.position || {};
var source = position.source;
if (source) {
if (position.filepath) {
source = utils.unixify(position.filepath);
}
this.map.addMapping({
source: source,
generated: {
line: this.position.line,
column: Math.max(this.position.column - 1, 0)
},
original: {
line: position.start.line,
column: position.start.column - 1
}
});
if (position.content) {
this.addContent(source, position);
}
if (position.filepath) {
this.addFile(source, position);
}
this.updatePosition(str);
this.output += str;
}
return str;
};
/**
* Adds a file to the source map output if it has not already been added
* @param {String} `file`
* @param {Object} `pos`
*/
exports.addFile = function(file, position) {
if (typeof position.content !== 'string') return;
if (Object.prototype.hasOwnProperty.call(this.files, file)) return;
this.files[file] = position.content;
};
/**
* Adds a content source to the source map output if it has not already been added
* @param {String} `source`
* @param {Object} `position`
*/
exports.addContent = function(source, position) {
if (typeof position.content !== 'string') return;
if (Object.prototype.hasOwnProperty.call(this.content, source)) return;
this.map.setSourceContent(source, position.content);
};
/**
* Applies any original source maps to the output and embeds the source file
* contents in the source map.
*/
exports.applySourceMaps = function() {
Object.keys(this.files).forEach(function(file) {
var content = this.files[file];
this.map.setSourceContent(file, content);
if (this.options.inputSourcemaps === true) {
var originalMap = utils.sourceMapResolve.resolveSync(content, file, fs.readFileSync);
if (originalMap) {
var map = new utils.SourceMap.SourceMapConsumer(originalMap.map);
var relativeTo = originalMap.sourcesRelativeTo;
this.map.applySourceMap(map, file, utils.unixify(path.dirname(relativeTo)));
}
}
}, this);
};
/**
* Process comments, drops sourceMap comments.
* @param {Object} node
*/
exports.comment = function(node) {
if (/^# sourceMappingURL=/.test(node.comment)) {
return this.emit('', node.position);
}
return this._comment(node);
};
snapdragon-0.8.1/lib/utils.js 0000664 0000000 0000000 00000001570 12776703477 0016147 0 ustar 00root root 0000000 0000000 'use strict';
/**
* Module dependencies
*/
exports.extend = require('extend-shallow');
exports.SourceMap = require('source-map');
exports.sourceMapResolve = require('source-map-resolve');
/**
* Convert backslash in the given string to forward slashes
*/
exports.unixify = function(fp) {
return fp.split(/\\+/).join('/');
};
/**
* Return true if `val` is a non-empty string
*
* @param {String} `str`
* @return {Boolean}
*/
exports.isString = function(str) {
return str && typeof str === 'string';
};
/**
* Cast `val` to an array
* @return {Array}
*/
exports.arrayify = function(val) {
if (typeof val === 'string') return [val];
return val ? (Array.isArray(val) ? val : [val]) : [];
};
/**
* Get the last `n` element from the given `array`
* @param {Array} `array`
* @return {*}
*/
exports.last = function(arr, n) {
return arr[arr.length - (n || 1)];
};
snapdragon-0.8.1/package.json 0000664 0000000 0000000 00000003273 12776703477 0016173 0 ustar 00root root 0000000 0000000 {
"name": "snapdragon",
"description": "Fast, pluggable and easy-to-use parser-renderer factory.",
"version": "0.8.1",
"homepage": "https://github.com/jonschlinkert/snapdragon",
"author": "Jon Schlinkert (https://github.com/jonschlinkert)",
"contributors": [
"Brian Woodward (https://github.com/doowb)",
"Jon Schlinkert (http://twitter.com/jonschlinkert)"
],
"repository": "jonschlinkert/snapdragon",
"bugs": {
"url": "https://github.com/jonschlinkert/snapdragon/issues"
},
"license": "MIT",
"files": [
"index.js",
"lib"
],
"main": "index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"test": "mocha"
},
"dependencies": {
"base": "^0.11.1",
"debug": "^2.2.0",
"define-property": "^0.2.5",
"extend-shallow": "^2.0.1",
"map-cache": "^0.2.2",
"source-map": "^0.5.6",
"source-map-resolve": "^0.5.0",
"use": "^2.0.0"
},
"devDependencies": {
"gulp": "^3.9.1",
"gulp-eslint": "^3.0.1",
"gulp-format-md": "^0.1.10",
"gulp-istanbul": "^1.1.1",
"gulp-mocha": "^3.0.1",
"gulp-unused": "^0.2.0",
"mocha": "^3.0.2"
},
"keywords": [
"lexer",
"snapdragon"
],
"verb": {
"toc": false,
"layout": "default",
"tasks": [
"readme"
],
"plugins": [
"gulp-format-md"
],
"related": {
"description": "These libraries use snapdragon:",
"list": [
"braces",
"micromatch",
"expand-brackets",
"extglob"
]
},
"reflinks": [
"css",
"pug",
"verb",
"verb-generate-readme"
],
"lint": {
"reflinks": true
}
}
}
snapdragon-0.8.1/test/ 0000775 0000000 0000000 00000000000 12776703477 0014657 5 ustar 00root root 0000000 0000000 snapdragon-0.8.1/test/compile.js 0000664 0000000 0000000 00000003075 12776703477 0016652 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
var assert = require('assert');
var Compile = require('../lib/compiler');
var Parser = require('../lib/parser');
var compiler;
var parser;
describe('compiler', function() {
beforeEach(function() {
compiler = new Compile();
parser = new Parser();
parser
.set('text', function() {
var pos = this.position();
var m = this.match(/^\w+/);
if (!m) return;
return pos({
type: 'text',
val: m[0]
});
})
.set('slash', function() {
var pos = this.position();
var m = this.match(/^\//);
if (!m) return;
return pos({
type: 'slash',
val: m[0]
});
});
});
describe('errors', function(cb) {
it('should throw an error when a compiler is missing', function(cb) {
try {
var ast = parser.parse('a/b/c');
compiler.compile(ast);
cb(new Error('expected an error'));
} catch (err) {
assert(err);
assert.equal(err.message, 'string column:1: compiler "text" is not registered');
cb();
}
});
});
describe('compiling', function() {
beforeEach(function() {
compiler
.set('text', function(node) {
return this.emit(node.val);
})
.set('slash', function(node) {
return this.emit('-');
});
});
it('should set the result on `output`', function() {
var ast = parser.parse('a/b/c');
var res = compiler.compile(ast);
assert.equal(res.output, 'a-b-c');
});
});
});
snapdragon-0.8.1/test/compiler.js 0000664 0000000 0000000 00000001440 12776703477 0017026 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
var assert = require('assert');
var Compiler = require('../lib/compiler');
var compiler;
describe('compiler', function() {
beforeEach(function() {
compiler = new Compiler();
});
describe('constructor:', function() {
it('should return an instance of Compiler:', function() {
assert(compiler instanceof Compiler);
});
});
// ensures that we catch and document API changes
describe('prototype methods:', function() {
var methods = [
'error',
'set',
'emit',
'visit',
'mapVisit',
'compile'
];
methods.forEach(function(method) {
it('should expose the `' + method + '` method', function() {
assert.equal(typeof compiler[method], 'function', method);
});
});
});
});
snapdragon-0.8.1/test/parse.js 0000664 0000000 0000000 00000010037 12776703477 0016330 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
var assert = require('assert');
var Snapdragon = require('..');
var Parser = require('../lib/parser');
var parser;
describe('parser', function() {
beforeEach(function() {
parser = new Parser();
});
describe('errors', function(cb) {
it('should throw an error when invalid args are passed to parse', function(cb) {
var parser = new Parser();
try {
parser.parse();
cb(new Error('expected an error'));
} catch (err) {
assert(err);
assert.equal(err.message, 'expected a string');
cb();
}
});
});
describe('.set():', function() {
it('should register middleware', function() {
parser.set('all', function() {
var pos = this.position();
var m = this.match(/^.*/);
if (!m) return;
return pos({
type: 'all',
val: m[0]
});
});
parser.parse('a/b');
assert(parser.parsers.hasOwnProperty('all'));
});
it('should use middleware to parse', function() {
parser.set('all', function() {
var pos = this.position();
var m = this.match(/^.*/);
return pos({
type: 'all',
val: m[0]
});
});
parser.parse('a/b');
assert.equal(parser.parsed, 'a/b');
assert.equal(parser.input, '');
});
it('should create ast node:', function() {
parser.set('all', function() {
var pos = this.position();
var m = this.match(/^.*/);
return pos({
type: 'all',
val: m[0]
});
});
parser.parse('a/b');
assert.equal(parser.ast.nodes.length, 3);
});
it('should be chainable:', function() {
parser
.set('text', function() {
var pos = this.position();
var m = this.match(/^\w+/);
if (!m) return;
return pos({
type: 'text',
val: m[0]
});
})
.set('slash', function() {
var pos = this.position();
var m = this.match(/^\//);
if (!m) return;
return pos({
type: 'slash',
val: m[0]
});
});
parser.parse('a/b');
assert.equal(parser.ast.nodes.length, 5);
});
});
});
describe('ast', function() {
beforeEach(function() {
parser = new Parser();
parser
.set('text', function() {
var pos = this.position();
var m = this.match(/^\w+/);
if (!m) return;
return pos({
type: 'text',
val: m[0]
});
})
.set('slash', function() {
var pos = this.position();
var m = this.match(/^\//);
if (!m) return;
return pos({
type: 'slash',
val: m[0]
});
});
});
describe('orig:', function() {
it('should add pattern to orig property', function() {
parser.parse('a/b');
assert.equal(parser.orig, 'a/b');
});
});
describe('recursion', function() {
beforeEach(function() {
parser.set('text', function() {
var pos = this.position();
var m = this.match(/^\w/);
if (!m) return;
return pos({
type: 'text',
val: m[0]
});
});
parser.set('open', function() {
var pos = this.position();
var m = this.match(/^{/);
if (!m) return;
return pos({
type: 'open',
val: m[0]
});
});
parser.set('close', function() {
var pos = this.position();
var m = this.match(/^}/);
if (!m) return;
return pos({
type: 'close',
val: m[0]
});
});
parser.set('comma', function() {
var pos = this.position();
var m = this.match(/,/);
if (!m) return;
return pos({
type: 'comma',
val: m[0]
});
});
});
it('should set original string on `orig`', function() {
parser.parse('a{b,{c,d},e}f');
assert.equal(parser.orig, 'a{b,{c,d},e}f');
});
});
});
snapdragon-0.8.1/test/parser.js 0000664 0000000 0000000 00000004236 12776703477 0016516 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
var assert = require('assert');
var Parser = require('../lib/parser');
var parser;
describe('parser', function() {
beforeEach(function() {
parser = new Parser();
});
describe('constructor:', function() {
it('should return an instance of Parser:', function() {
assert(parser instanceof Parser);
});
});
// ensures that we catch and document API changes
describe('prototype methods:', function() {
var methods = [
'updatePosition',
'position',
'error',
'set',
'parse',
'match',
'use'
];
methods.forEach(function(method) {
it('should expose the `' + method + '` method', function() {
assert.equal(typeof parser[method], 'function');
});
});
});
describe('parsers', function() {
beforeEach(function() {
parser = new Parser();
});
describe('.set():', function() {
it('should register a named middleware', function() {
parser.set('all', function() {
var pos = this.position();
var m = this.match(/^.*/);
return pos({
type: 'all',
val: m[0]
});
});
assert(typeof parser.parsers.all === 'function');
});
it('should expose named parsers to middleware:', function() {
var count = 0;
parser.set('word', function() {
var pos = this.position();
var m = this.match(/^\w/);
if (!m) return;
return pos({
type: 'word',
val: m[0]
});
});
parser.set('slash', function() {
var pos = this.position();
var m = this.match(/^\//);
if (!m) return;
var word = this.parsers.word();
var prev = this.prev();
var node = pos({
type: 'slash',
val: m[0]
});
if (word && word.type === 'word') {
count++;
}
prev.nodes.push(node);
prev.nodes.push(word);
});
parser.parse('a/b');
assert.equal(parser.ast.nodes.length, 5);
assert.equal(count, 1);
});
});
});
});
snapdragon-0.8.1/test/position.js 0000664 0000000 0000000 00000000366 12776703477 0017066 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
var assert = require('assert');
var Position = require('../lib/position');
describe('Position', function() {
it('should export a function', function() {
assert.equal(typeof Position, 'function');
});
});
snapdragon-0.8.1/test/snapdragon.capture.js 0000664 0000000 0000000 00000004272 12776703477 0021020 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
var assert = require('assert');
var Snapdragon = require('..');
var snapdragon;
describe('parser', function() {
beforeEach(function() {
snapdragon = new Snapdragon();
});
describe('errors', function(cb) {
it('should throw an error when invalid args are passed to parse', function(cb) {
try {
snapdragon.parse();
cb(new Error('expected an error'));
} catch (err) {
assert(err);
assert.equal(err.message, 'expected a string');
cb();
}
});
});
describe('.capture():', function() {
it('should register a parser', function() {
snapdragon.capture('all', /^.*/);
snapdragon.parse('a/b');
assert(snapdragon.parsers.hasOwnProperty('all'));
});
it('should use middleware to parse', function() {
snapdragon.capture('all', /^.*/);
snapdragon.parse('a/b');
assert.equal(snapdragon.parser.parsed, 'a/b');
assert.equal(snapdragon.parser.input, '');
});
it('should create ast node:', function() {
snapdragon.capture('all', /^.*/);
snapdragon.parse('a/b');
assert.equal(snapdragon.parser.ast.nodes.length, 3);
});
it('should be chainable:', function() {
snapdragon.parser
.capture('text', /^\w+/)
.capture('slash', /^\//);
snapdragon.parse('a/b');
assert.equal(snapdragon.parser.ast.nodes.length, 5);
});
});
});
describe('ast', function() {
beforeEach(function() {
snapdragon = new Snapdragon();
snapdragon
.capture('text', /^\w+/)
.capture('slash', /^\//);
});
describe('orig:', function() {
it('should add pattern to orig property', function() {
snapdragon.parse('a/b');
assert.equal(snapdragon.parser.orig, 'a/b');
});
});
describe('recursion', function() {
// TODO!
beforeEach(function() {
snapdragon
.capture('text', /^[^{},]+/)
.capture('open', /^\{/)
.capture('close', /^\}/)
.capture('comma', /,/);
});
it('should set original string on `orig`', function() {
snapdragon.parse('a{b,{c,d},e}f');
assert.equal(snapdragon.parser.orig, 'a{b,{c,d},e}f');
});
});
});
snapdragon-0.8.1/test/snapdragon.parse.js 0000664 0000000 0000000 00000010260 12776703477 0020461 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
var assert = require('assert');
var Snapdragon = require('..');
var snapdragon;
describe('parser', function() {
beforeEach(function() {
snapdragon = new Snapdragon();
});
describe('errors', function(cb) {
it('should throw an error when invalid args are passed to parse', function(cb) {
try {
snapdragon.parse();
cb(new Error('expected an error'));
} catch (err) {
assert(err);
assert.equal(err.message, 'expected a string');
cb();
}
});
});
describe('.set():', function() {
it('should register middleware', function() {
snapdragon.parser.set('all', function() {
var pos = this.position();
var m = this.match(/^.*/);
if (!m) return;
return pos({
type: 'all',
val: m[0]
});
});
snapdragon.parse('a/b');
assert(snapdragon.parsers.hasOwnProperty('all'));
});
it('should use middleware to parse', function() {
snapdragon.parser.set('all', function() {
var pos = this.position();
var m = this.match(/^.*/);
return pos({
type: 'all',
val: m[0]
});
});
snapdragon.parse('a/b');
assert.equal(snapdragon.parser.parsed, 'a/b');
assert.equal(snapdragon.parser.input, '');
});
it('should create ast node:', function() {
snapdragon.parser.set('all', function() {
var pos = this.position();
var m = this.match(/^.*/);
return pos({
type: 'all',
val: m[0]
});
});
snapdragon.parse('a/b');
assert.equal(snapdragon.parser.ast.nodes.length, 3);
});
it('should be chainable:', function() {
snapdragon.parser
.set('text', function() {
var pos = this.position();
var m = this.match(/^\w+/);
if (!m) return;
return pos({
type: 'text',
val: m[0]
});
})
.set('slash', function() {
var pos = this.position();
var m = this.match(/^\//);
if (!m) return;
return pos({
type: 'slash',
val: m[0]
});
});
snapdragon.parse('a/b');
assert.equal(snapdragon.parser.ast.nodes.length, 5);
});
});
});
describe('ast', function() {
beforeEach(function() {
snapdragon = new Snapdragon();
snapdragon.parser
.set('text', function() {
var pos = this.position();
var m = this.match(/^\w+/);
if (!m) return;
return pos({
type: 'text',
val: m[0]
});
})
.set('slash', function() {
var pos = this.position();
var m = this.match(/^\//);
if (!m) return;
return pos({
type: 'slash',
val: m[0]
});
});
});
describe('orig:', function() {
it('should add pattern to orig property', function() {
snapdragon.parse('a/b');
assert.equal(snapdragon.parser.orig, 'a/b');
});
});
describe('recursion', function() {
beforeEach(function() {
snapdragon.parser.set('text', function() {
var pos = this.position();
var m = this.match(/^\w/);
if (!m) return;
return pos({
type: 'text',
val: m[0]
});
});
snapdragon.parser.set('open', function() {
var pos = this.position();
var m = this.match(/^{/);
if (!m) return;
return pos({
type: 'open',
val: m[0]
});
});
snapdragon.parser.set('close', function() {
var pos = this.position();
var m = this.match(/^}/);
if (!m) return;
return pos({
type: 'close',
val: m[0]
});
});
snapdragon.parser.set('comma', function() {
var pos = this.position();
var m = this.match(/,/);
if (!m) return;
return pos({
type: 'comma',
val: m[0]
});
});
});
it('should set original string on `orig`', function() {
snapdragon.parse('a{b,{c,d},e}f');
assert.equal(snapdragon.parser.orig, 'a{b,{c,d},e}f');
});
});
});
snapdragon-0.8.1/test/snapdragon.regex.js 0000664 0000000 0000000 00000001234 12776703477 0020462 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
var assert = require('assert');
var Snapdragon = require('..');
var snapdragon;
describe('parser', function() {
beforeEach(function() {
snapdragon = new Snapdragon();
});
describe('.regex():', function() {
it('should expose a regex cache with regex from registered parsers', function() {
snapdragon.capture('dot', /^\./);
snapdragon.capture('text', /^\w+/);
snapdragon.capture('all', /^.+/);
assert(snapdragon.regex.__data__.hasOwnProperty('dot'));
assert(snapdragon.regex.__data__.hasOwnProperty('all'));
assert(snapdragon.regex.__data__.hasOwnProperty('text'));
});
});
});
snapdragon-0.8.1/test/test.js 0000664 0000000 0000000 00000000346 12776703477 0016177 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
var assert = require('assert');
var extglob = require('..');
describe('extglob', function() {
it('should export a function', function() {
assert.equal(typeof extglob, 'function');
});
});
snapdragon-0.8.1/test/utils.js 0000664 0000000 0000000 00000000433 12776703477 0016355 0 ustar 00root root 0000000 0000000 'use strict';
require('mocha');
var assert = require('assert');
var utils = require('../lib/utils');
describe('utils', function() {
describe('main export:', function() {
it('should expose an object', function() {
assert.equal(typeof utils, 'object');
});
});
});