You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

320 lines
8.7 KiB

4 years ago
  1. # 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)
  2. > Fast, pluggable and easy-to-use parser-renderer factory.
  3. ## Install
  4. Install with [npm](https://www.npmjs.com/):
  5. ```sh
  6. $ npm install --save snapdragon
  7. ```
  8. Created by [jonschlinkert](https://github.com/jonschlinkert) and [doowb](https://github.com/doowb).
  9. **Features**
  10. * Bootstrap your own parser, get sourcemap support for free
  11. * All parsing and compiling is handled by simple, reusable middleware functions
  12. * Inspired by the parsers in [pug](http://jade-lang.com) and [css](https://github.com/reworkcss/css).
  13. ## History
  14. ### v0.5.0
  15. **Breaking changes**
  16. 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.
  17. * Renderer was renamed to `Compiler`
  18. * the `.render` method was renamed to `.compile`
  19. * 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.
  20. ## Usage examples
  21. ```js
  22. var Snapdragon = require('snapdragon');
  23. var snapdragon = new Snapdragon();
  24. ```
  25. **Parse**
  26. ```js
  27. var ast = snapdragon.parser('some string', options)
  28. // parser middleware that can be called by other middleware
  29. .set('foo', function () {})
  30. // parser middleware, runs immediately in the order defined
  31. .use(bar())
  32. .use(baz())
  33. ```
  34. **Render**
  35. ```js
  36. // pass the `ast` from the parse method
  37. var res = snapdragon.compiler(ast)
  38. // compiler middleware, called when the name of the middleware
  39. // matches the `node.type` (defined in a parser middleware)
  40. .set('bar', function () {})
  41. .set('baz', function () {})
  42. .compile()
  43. ```
  44. See the [examples](./examples/).
  45. ## Getting started
  46. **Parsers**
  47. Parsers are middleware functions used for parsing a string into an ast node.
  48. ```js
  49. var ast = snapdragon.parser(str, options)
  50. .use(function() {
  51. var pos = this.position();
  52. var m = this.match(/^\./);
  53. if (!m) return;
  54. return pos({
  55. // `type` specifies the compiler to use
  56. type: 'dot',
  57. val: m[0]
  58. });
  59. })
  60. ```
  61. **AST node**
  62. When the parser finds a match, `pos()` is called, pushing a token for that node onto the ast that looks something like:
  63. ```js
  64. { type: 'dot',
  65. val: '.',
  66. position:
  67. { start: { lineno: 1, column: 1 },
  68. end: { lineno: 1, column: 2 } }}
  69. ```
  70. **Renderers**
  71. Renderers are _named_ middleware functions that visit over an array of ast nodes to compile a string.
  72. ```js
  73. var res = snapdragon.compiler(ast)
  74. .set('dot', function (node) {
  75. console.log(node.val)
  76. //=> '.'
  77. return this.emit(node.val);
  78. })
  79. ```
  80. **Source maps**
  81. If you want source map support, make sure to emit the position as well.
  82. ```js
  83. var res = snapdragon.compiler(ast)
  84. .set('dot', function (node) {
  85. return this.emit(node.val, node.position);
  86. })
  87. ```
  88. ## Docs
  89. ### Parser middleware
  90. A parser middleware is a function that returns an abject called a `token`. This token is pushed onto the AST as a node.
  91. **Example token**
  92. ```js
  93. { type: 'dot',
  94. val: '.',
  95. position:
  96. { start: { lineno: 1, column: 1 },
  97. end: { lineno: 1, column: 2 } }}
  98. ```
  99. **Example parser middleware**
  100. Match a single `.` in a string:
  101. 1. Get the starting position by calling `this.position()`
  102. 2. pass a regex for matching a single dot to the `.match` method
  103. 3. if **no match** is found, return `undefined`
  104. 4. if a **match** is found, `pos()` is called, which returns a token with:
  105. - `type`: the name of the [compiler] to use
  106. - `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].
  107. - The ending position: automatically calculated by adding the length of the first capture group to the starting position.
  108. ## Renderer middleware
  109. Renderers are run when the name of the compiler middleware matches the `type` defined on an ast `node` (which is defined in a parser).
  110. **Example**
  111. Exercise: Parse a dot, then compile it as an escaped dot.
  112. ```js
  113. var ast = snapdragon.parser('.')
  114. .use(function () {
  115. var pos = this.position();
  116. var m = this.match(/^\./);
  117. if (!m) return;
  118. return pos({
  119. // define the `type` of compiler to use
  120. type: 'dot',
  121. val: m[0]
  122. })
  123. })
  124. var result = snapdragon.compiler(ast)
  125. .set('dot', function (node) {
  126. return this.emit('\\' + node.val);
  127. })
  128. .compile()
  129. console.log(result.output);
  130. //=> '\.'
  131. ```
  132. ## API
  133. ### [Parser](lib/parser.js#L19)
  134. Create a new `Parser` with the given `input` and `options`.
  135. **Params**
  136. * `input` **{String}**
  137. * `options` **{Object}**
  138. ### [.define](lib/parser.js#L103)
  139. Define a non-enumberable property on the `Parser` instance.
  140. **Example**
  141. ```js
  142. parser.define('foo', 'bar');
  143. ```
  144. **Params**
  145. * `key` **{String}**: propery name
  146. * `val` **{any}**: property value
  147. * `returns` **{Object}**: Returns the Parser instance for chaining.
  148. Set parser `name` with the given `fn`
  149. **Params**
  150. * `name` **{String}**
  151. * `fn` **{Function}**
  152. Get parser `name`
  153. **Params**
  154. * `name` **{String}**
  155. Push a `token` onto the `type` stack.
  156. **Params**
  157. * `type` **{String}**
  158. * `returns` **{Object}** `token`
  159. Pop a token off of the `type` stack
  160. **Params**
  161. * `type` **{String}**
  162. * `returns` **{Object}**: Returns a token
  163. Return true if inside a `stack` node. Types are `braces`, `parens` or `brackets`.
  164. **Params**
  165. * `type` **{String}**
  166. * `returns` **{Boolean}**
  167. **Example**
  168. ```js
  169. parser.isType(node, 'brace');
  170. ```
  171. **Params**
  172. * `node` **{Object}**
  173. * `type` **{String}**
  174. * `returns` **{Boolean}**
  175. ### [.define](lib/compiler.js#L71)
  176. Define a non-enumberable property on the `Compiler` instance.
  177. **Example**
  178. ```js
  179. compiler.define('foo', 'bar');
  180. ```
  181. **Params**
  182. * `key` **{String}**: propery name
  183. * `val` **{any}**: property value
  184. * `returns` **{Object}**: Returns the Compiler instance for chaining.
  185. ## About
  186. ### Related projects
  187. * [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.")
  188. * [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.")
  189. * [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.")
  190. * [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.")
  191. ### Contributing
  192. Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
  193. ### Contributors
  194. | **Commits** | **Contributor**<br/> |
  195. | --- | --- |
  196. | 106 | [jonschlinkert](https://github.com/jonschlinkert) |
  197. | 2 | [doowb](https://github.com/doowb) |
  198. ### Building docs
  199. _(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).)_
  200. To generate the readme and API documentation with [verb](https://github.com/verbose/verb):
  201. ```sh
  202. $ npm install -g verb verb-generate-readme && verb
  203. ```
  204. ### Running tests
  205. Install dev dependencies:
  206. ```sh
  207. $ npm install -d && npm test
  208. ```
  209. ### Author
  210. **Jon Schlinkert**
  211. * [github/jonschlinkert](https://github.com/jonschlinkert)
  212. * [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
  213. ### License
  214. Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert).
  215. Released under the [MIT license](https://github.com/jonschlinkert/snapdragon/blob/master/LICENSE).
  216. ***
  217. _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.31, on October 10, 2016._