pax_global_header00006660000000000000000000000064127767034770014536gustar00rootroot0000000000000052 comment=1433d322d00a989984cc58de87ea3c4dac600988 snapdragon-0.8.1/000077500000000000000000000000001277670347700137005ustar00rootroot00000000000000snapdragon-0.8.1/.editorconfig000066400000000000000000000004051277670347700163540ustar00rootroot00000000000000root = 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 = falsesnapdragon-0.8.1/.eslintrc.json000066400000000000000000000071251277670347700165010ustar00rootroot00000000000000{ "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/.gitattributes000066400000000000000000000000171277670347700165710ustar00rootroot00000000000000*.* text eol=lfsnapdragon-0.8.1/.gitignore000066400000000000000000000003431277670347700156700ustar00rootroot00000000000000# 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.yml000066400000000000000000000001001277670347700160000ustar00rootroot00000000000000sudo: false language: node_js node_js: - node - '6' - '5' snapdragon-0.8.1/.verb.md000066400000000000000000000103351277670347700152400ustar00rootroot00000000000000Created 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/LICENSE000066400000000000000000000021001277670347700146760ustar00rootroot00000000000000The 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.md000066400000000000000000000213521277670347700151620ustar00rootroot00000000000000# snapdragon [![NPM version](https://img.shields.io/npm/v/snapdragon.svg?style=flat)](https://www.npmjs.com/package/snapdragon) [![NPM downloads](https://img.shields.io/npm/dm/snapdragon.svg?style=flat)](https://npmjs.org/package/snapdragon) [![Build Status](https://img.shields.io/travis/jonschlinkert/snapdragon.svg?style=flat)](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.js000066400000000000000000000003141277670347700156670ustar00rootroot00000000000000'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/000077500000000000000000000000001277670347700155165ustar00rootroot00000000000000snapdragon-0.8.1/examples/extglob.js000066400000000000000000000036301277670347700175220ustar00rootroot00000000000000'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.js000066400000000000000000000015101277670347700160420ustar00rootroot00000000000000'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.js000066400000000000000000000076211277670347700153530ustar00rootroot00000000000000'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/000077500000000000000000000000001277670347700144465ustar00rootroot00000000000000snapdragon-0.8.1/lib/compiler.js000066400000000000000000000070071277670347700166220ustar00rootroot00000000000000'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.js000066400000000000000000000255361277670347700163130ustar00rootroot00000000000000'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.js000066400000000000000000000005001277670347700166430ustar00rootroot00000000000000'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.js000066400000000000000000000070241277670347700172450ustar00rootroot00000000000000'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.js000066400000000000000000000015701277670347700161470ustar00rootroot00000000000000'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.json000066400000000000000000000032731277670347700161730ustar00rootroot00000000000000{ "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/000077500000000000000000000000001277670347700146575ustar00rootroot00000000000000snapdragon-0.8.1/test/compile.js000066400000000000000000000030751277670347700166520ustar00rootroot00000000000000'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.js000066400000000000000000000014401277670347700170260ustar00rootroot00000000000000'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.js000066400000000000000000000100371277670347700163300ustar00rootroot00000000000000'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.js000066400000000000000000000042361277670347700165160ustar00rootroot00000000000000'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.js000066400000000000000000000003661277670347700170660ustar00rootroot00000000000000'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.js000066400000000000000000000042721277670347700210200ustar00rootroot00000000000000'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.js000066400000000000000000000102601277670347700204610ustar00rootroot00000000000000'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.js000066400000000000000000000012341277670347700204620ustar00rootroot00000000000000'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.js000066400000000000000000000003461277670347700161770ustar00rootroot00000000000000'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.js000066400000000000000000000004331277670347700163550ustar00rootroot00000000000000'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'); }); }); });