1
0
mirror of https://github.com/mgerb/mywebsite synced 2026-01-12 02:42:48 +00:00

Added all files

This commit is contained in:
2015-06-24 11:18:57 -05:00
parent 4c1ff3628c
commit a2c44e494f
2633 changed files with 459257 additions and 0 deletions

15
node_modules/jade/.npmignore generated vendored Normal file
View File

@@ -0,0 +1,15 @@
test
support
benchmarks
examples
lib-cov
coverage
.gitmodules
.travis.yml
History.md
Makefile
test/
support/
benchmarks/
examples/
docs/

1
node_modules/jade/.release.json generated vendored Normal file
View File

@@ -0,0 +1 @@
"38b8c0414318ea474e4014a1f2371e242523833d"

973
node_modules/jade/History.md generated vendored Normal file
View File

@@ -0,0 +1,973 @@
1.9.2 / 2015-01-18
==================
* Do not ignore some parser errors for mismatched parenthesis ([@TimothyGu](https://github.com/TimothyGu))
* Warn for `:` that is not followed by a space ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix #1794 (a bizzare bug with a certain combination of inheritance, mixins and &attributes) ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Add `compileClientWithDependenciesTracked` ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Support comments in `case` blocks ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix blocks in nested mixins ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Lots more documentation ([@enlore](https://github.com/enlore))
* Fix watching in CLI ([@pavel](https://github.com/pavel))
1.9.1 / 2015-01-17
==================
* Clean up path/fs functions in CLI as we no longer support node@0.6 ([@TimothyGu](https://github.com/TimothyGu))
* Update commander ([@TimothyGu](https://github.com/TimothyGu))
* Document `cache` and `parser` options ([@TimothyGu](https://github.com/TimothyGu))
* Fix bug in 1.9.0 where we read the file if cache was enabled, even if a string was provided ([@TimothyGu](https://github.com/TimothyGu))
* Fix year in changelog ([@tomByrer](https://github.com/tomByrer))
1.9.0 / 2015-01-13
==================
* Fix `--watch` sometimes dying when there were file-system errors ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix `--watch` by using `fs.watchFile` correctly ([@TimothyGu](https://github.com/TimothyGu))
* Fix errors with using the CLI to compile from stdin
* Better looking badges ([@TimothyGu](https://github.com/TimothyGu))
* Added `--extension` to CLI([@nicocedron](https://github.com/nicocedron) and [@TimothyGu](https://github.com/TimothyGu))
* Refactor and improve internal cache handling ([@TimothyGu](https://github.com/TimothyGu))
* Loads more tests ([@TimothyGu](https://github.com/TimothyGu))
1.8.2 / 2014-12-16
==================
* Use `-` as the default filename when using stdin on CLI ([@TimothyGu](https://github.com/TimothyGu))
* Prevent some compiler errors being silenced ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove use of non-standard `string.trimLeft()` ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix bug in CLI when no name was provided for child template ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove dependency on monocle (hopefully fixing installation on 0.8) ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Add gitter chat room ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.8.1 / 2014-11-30
==================
* Fix corner case when the pretty option was passed a non-string truthy value ([@TimothyGu](https://github.com/TimothyGu))
* Warn when `lexer` is given as an option ([@TimothyGu](https://github.com/TimothyGu))
* Update dependencies ([@TimothyGu](https://github.com/TimothyGu))
1.8.0 / 2014-11-28
==================
* Fix empty text-only block ([@rlidwka](https://github.com/rlidwka))
* Warn about future change to ISO 8601 style dates ([@TimothyGu](https://github.com/TimothyGu) and [@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Add warnings when data-attributes contain ampersands ([@TimothyGu](https://github.com/TimothyGu))
* Allow custom pretty indentation ([@bfred-it](https://github.com/bfred-it))
* Add support for an object in the style attribute ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Add support for an object in the class attribute ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Ignore fs module in browser builds ([@sokra](https://github.com/sokra))
* Update dependencies ([@hildjj](https://github.com/hildjj))
* Check mixin arguments are valid JavaScript expressions ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove symlink ([@slang800](https://github.com/slang800))
1.7.0 / 2014-09-17
==================
* Add Doctype option on command line ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Support ES6 style rest args in mixins ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix support for unicode newlines (\u2028, \u2029) ([@rlidwka](https://github.com/rlidwka))
* Expose `globals` option from the `with` module ([@sokra](https://github.com/sokra))
* Lots of new documentation ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.6.0 / 2014-08-31
==================
* Allow optional white space after `+` when calling a mixin ([@char101](https://github.com/char101))
* Use void-elements module to replace internal self-closing list ([@hemanth](https://github.com/hemanth))
* Fix a warning that eroniously warned for un-used blocks if in an extending template from an include (Reported by [@Dissimulazione](https://github.com/Dissimulazione))
* Fix mixins not working at end of file ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix error reporting when mixin block was followed by blank lines ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.5.0 / 2014-07-23
==================
* Added compileFile API ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix line number in un-used blocks warning ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix a warning that eroniously warned for un-used blocks if they were under another block (Reported by [@pesho](https://github.com/pesho))
1.4.2 / 2014-07-16
==================
* Fix a warning that eroniously warned for un-used blocks if they were under a "Code" element (Reported by [@narirou](https://github.com/narirou))
1.4.1 / 2014-07-16
==================
* Fix an error that sometimes resulted in 'unexpected token "pipless-text"' being erroniously thrown (Reported by [@Artazor](https://github.com/Artazor) and [@thenitai](https://github.com/thenitai))
1.4.0 / 2014-07-15
==================
* Fix CLI so it keeps watching when errors occur ([@AndrewTsao](https://github.com/AndrewTsao))
* Support custom names for client side templates ([@ForbesLindesay](http://www.forbeslindesay.co.uk/) and [@dscape](https://github.com/dscape))
* Allow whitepsace other than "space" before attributes passed to mixins (N.B. there is a small chance this could be a breaking change for you) ([@regular](https://github.com/regular))
* Track dependencies so file watchers can be more clever ([@ForbesLindesay](http://www.forbeslindesay.co.uk/) and [@sdether](https://github.com/sdether))
* Allow passing options to filtered includes ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix bugs with indentation in filters ([@ForbesLindesay](http://www.forbeslindesay.co.uk/) and [@lackac](https://github.com/lackac))
* Warn on block names that are never used ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.3.1 / 2014-04-04
==================
* Fix error with tags in xml that are self-closing in html ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix error message for inline tags with content ([@hiddentao](https://github.com/hiddentao))
1.3.0 / 2014-03-02
==================
* Fix a bug where sometimes mixins were removed by an optimisation even though they were being called ([@ForbesLindesay](http://www.forbeslindesay.co.uk/), reported by [@leider](https://github.com/leider))
* Updated with to support automatically detecting when a value is "global" and removed redundant `options.globals` option ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Improve warnings for tags with multiple attributes ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Deprecate (with a warning) `node.clone`, `block.replace`, `attrs.removeAttribute`, `attrs.getAttribute` - these are all internal APIs for the AST ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.2.0 / 2014-02-26
==================
* Use variables instead of properties of jade, improving performance and reliability with nested templates ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Support compiling templates from stdin via a user typing ([@yorkie](https://github.com/yorkie))
* Lazily add mixins ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix case fall-through ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Earlier errors for `when` without `case` and `else` without `if` ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Allow `if`/`else` etc. to not have a block.
* Remove lib-cov legacy to make browserify work better ([@silver83](https://github.com/silver83))
* Add and improve test coverage using istanbul ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.1.5 / 2014-01-19
==================
* Add filename to and fix line numbers for missing space before text warning (@ijin82)
* Fix filenames for some error reporting in extends/includes (@doublerebel)
* Fix a corner case where a mixin was called with `&attributes` but no other attributes and a block that was supposed to be fixed in 1.1.4 ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.1.4 / 2014-01-09
==================
* Fix a corner case where a mixin was called with `&attributes` but no other attributes and a block ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.1.3 / 2014-01-09
==================
* Fix failure of npm prepublish not running
1.1.2 / 2014-01-09
==================
* Fix same interaction of `&attributes` with `false` `null` or `undefined` but combined with dynamic attributes ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.1.1 / 2014-01-09
==================
* Fix a bug when `&attributes` is combined with static attributes that evaluate to `false` or `null` or `undefined` ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.1.0 / 2014-01-07
==================
* Fix class merging to work as documented ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Throw an error when the same attribute is duplicated multiple times ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Move more errors into the parser/lexer so they have more info about line numbers ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Support mixin blocks at the end of files ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.0.2 / 2013-12-31
==================
* Fix a bug when `&attributes` is combined with dynamic attributes ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.0.1 / 2013-12-29
==================
* Allow self closing tags to contian whitespace ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Allow tags to have a single white space after them ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Support text bodies of tags that begin with `//` rather than treating them as comments ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
1.0.0 / 2013-12-22
==================
* No longer support node@0.8 ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix error reporting in layouts & includes ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Allow a list of 'globals' to be passed as an array at compile time & don't automatically expose all globals ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Escape apostrophes in data attributes (@qualiabyte)
* Fix mixin/block interaction ([@ForbesLindesay](http://www.forbeslindesay.co.uk/) & [@paulyoung](https://github.com/paulyoung))
* Ignore trailing space after mixin declaration ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Make literal `.` work as expected ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove implicit text only for script/style ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Stop parsing comments and remove support for conditional comments ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Make filtering includes explicit ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove special assignment syntax ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove `!!!` shortcut for `doctype` ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove `5` shorcut for `html` doctype ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove `colons` option from the distant past ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Add a sepatate `compileClient` and `compileFileClient` to replace the `client` option ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove polyfills for supporting old browsers ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Allow interpolation for mixin names ([@jeromew](https://github.com/jeromew)
* Use `node.type` instead of `node.constructor.name` so it can be minified ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Allow hyphens in filter names ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Throw an error if a self closing tag has content ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Support inline tags ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Replace `attributes` magic attribute with `&attributes(attributes)` ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove automatic tag wrapping for filters, you can just put the tags in yourself now ([@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Remove whitespace from tags nested inside pre tags ([@markdalgleish](http://markdalgleish.com))
0.35.0 / 2013-08-21
===================
* Add support for space separated attributes (thanks to [@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Add earlier errors for invalid JavaScript expressions (thanks to [@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* Fix parsing files with UTF8 BOMs when they are includes or parent/layout templates (thanks to [@kiinoo](https://github.com/kiinoo))
0.34.1 / 2013-07-26
===================
* fix render file not working when called with callback (reported by [@xieren58](https://github.com/xieren58))
0.34.0 / 2013-07-26
===================
* callbacks only called once for async methods even if they throw (reported by [@davidcornu](https://github.com/davidcornu))
* HTML comments are pretty printed better (thanks to [@eddiemonge](https://github.com/eddiemonge))
* callbacks are optional and leaving them out results in synchronous operation (thanks to [@ForbesLindesay](http://www.forbeslindesay.co.uk/))
* empty filter nodes are now permitted (thanks to [@coderanger](https://github.com/coderanger))
* overhaul website and documentation (thanks to [@ForbesLindesay](http://www.forbeslindesay.co.uk/)), much more of this to come.
0.33.0 / 2013-07-12
===================
* Hugely more powerful error reporting (especially with `compileDebug` set explicitly to `true`)
* Add a warning for tags with multiple attributes
* be strict about requiring newlines after tags to fix some odd corner cases
* fix escaping of class to allow it to be unescaped (thanks to [@christiangenco](https://github.com/christiangenco))
0.32.0 / 2013-06-28
===================
* remove `jade.version` and fix `jade --version`
* add file name and line number to deprecation warnings
* use constantinople for better constant detection
* update `with` for a massive performance upgrade at compile time
0.31.2 / 2013-06-07
===================
* fix overzealous deprecation warnings
0.31.1 / 2013-05-31
===================
* fix line endings for executable command
* fix `locals` variable being undefined
* fix an obscure bug that could occur if multiple mixins interact badly (see [substack/lexical-scope#13](https://github.com/substack/lexical-scope/issues/13))
0.31.0 / 2013-05-30
==================
* deprecate implicit text-only `script` and `style` tags
* make `with` at compile time using `lexical-scope`
* add `options.parser` that behaves exactly like `options.compiler`
* add "component.json" for component (runtime) support
* removed `hasOwnProperty` check in each loops
* removed .min files from the repository (people can just generate these themselves)
* use browserify to compile client side libraries
* fix buggy block extending should now be fixed
* fix preserve case of custom doctypes
* fix regexps in attributes sometimes not being accepted
* fix allow `$` sign in each loop variable names
* fix mixins with buffered code on the same line
* fix separate class names by ` ` rather than `,` (was sometimes incorrect)
0.30.0 / 2013-04-25
==================
* add support for 'include' and 'extends' to use paths relative to basedir
* fix accidental calling of functions in iteration block. Closes #986
* fix: skip rethrow on client
* fix each/else prefixed with `-`
* fix multi-block prepend/append
* swap -o and -O, set -o to --out
0.29.0 / 2013-04-16
==================
* add "monocle" for watcher that actually works...
* fix interpolation in blocks of text
* fix attribute interpolation
* move filters to an external library
* fix JavaScript escaping corner cases
0.28.2 / 2013-03-04
==================
* wtf coffeescript is not a dep
0.28.1 / 2013-01-10
==================
* add passing of filename to include filters
* fix wrong new lines for include filters
0.28.0 / 2013-01-08
==================
* add .css and .js "filters". re #438
* add include filters. Closes #283
* fix "class:" within attribute escaping
* removing ast filters
* things I can't read:
* 反馈地址
* 样式
* 联系
* 初稿,翻译完
* 接受大鸟的建议
* 头晕,翻译一点点
* 到过滤器翻译完毕
* 翻译一部分
* 中文翻译单独放
* 特性部分
* 再翻
* 翻译一点点
0.27.7 / 2012-11-05
==================
* fix each/else clause for enumerated objects
* fix #764 (incorrect line number for error messages)
* fix double-escaping of interpolated js slashes. Closes #784
0.27.6 / 2012-10-05
==================
* Included templates can not override blocks of their parent. Closes #699
0.27.5 / 2012-09-24
==================
* fix attr interpolation escaping. Closes #771
0.27.4 / 2012-09-18
==================
* fix include yields. Closes #770
0.27.3 / 2012-09-18
==================
* fix escaping of interpolation. Closes #769
* loosen "mkdirp" version restriction [TooTallNate]
0.27.2 / 2012-08-07
==================
* Revert "fixing string interpolation escaping #731", problems reported
0.27.1 / 2012-08-06
==================
* fix attribute interpolation escaping #731
* fix string interpolation escaping #731
0.27.0 / 2012-07-26
==================
* added ability to pass in json file to `--obj`
* add preliminary `each` `else` support. Closes #716
* fix doctype bug overlooked in #712
* fix stripping of utf-8 BOMs
0.26.3 / 2012-06-25
==================
* Update version of commander that supports node v0.8.
0.26.2 / 2012-06-22
==================
* Added --options alias of --obj
* Added reserved word conflict prevention in Google's Closure Compiler
* Added tag interpolation. Closes #657
* Allow the compiled client to use it's own jade util functions [3rd-Eden]
* Fixed `attrs()` escape bug [caseywebdev]
0.26.1 / 2012-05-27
==================
* Changed default doctype to __html5__
* Performance: statically compile attrs when possible [chowey]
* Fixed some class attribute merging cases
* Fixed so `block` doesn't consume `blockquotes` tag [chowey]
* Fixed backslashes in text nodes [chowey]
* Fixed / in text. Closes #638
0.26.0 / 2012-05-04
==================
* Added package.json __component__ support
* Added explicit self-closing tag support. Closes #605
* Added `block` statement
* Added mixin tag-like behaviour [chowey]
* Fixed mixins with extends [chowey]
0.25.0 / 2012-04-18
==================
* Added preliminary mixin block support. Closes #310
* Fixed whitespace handling in various situations [chowey]
* Fixed indentation in various situations [chowey]
0.24.0 / 2012-04-12
==================
* Fixed unescaped attribute compilation
* Fixed pretty-printing of text-only tags (__Warning__: this may affect rendering) [chowey]
0.23.0 / 2012-04-11
==================
* Added data-attr json stringification support. Closes #572
* Added unescaped attr support. Closes #198
* Fixed #1070, reverted mixin function statements
* Fixed jade.1 typo
0.22.1 / 2012-04-04
==================
* Fixed source tags. now self-closing. Closes #308
* Fixed: escape backslashes in coffeescript filter
0.22.0 / 2012-03-22
==================
* Added jade manpage (`man jade` after installation for docs)
* Added `-D, --no-debug` to jade(1)
* Added `-p, --pretty` to jade(1)
* Added `-c, --client` option to jade(1)
* Fixed `-o { client: true }` with stdin
* Fixed: skip blank lines in lexer (unless within pipeless text). Closes #399
0.21.0 / 2012-03-10
==================
* Added new input/output test suite using Mocha's string diffing
* Added alias `extend` -> `extends`. Closes #527 [guillermo]
* Fixed include escapes. Closes #513
* Fixed block-expansion with .foo and #foo short-hands. Closes #498
0.20.3 / 2012-02-16
==================
* Changed: pass `.filename` to filters only
0.20.2 / 2012-02-16
==================
* Fixed `:stylus` import capabilities, pass .filename
0.20.1 / 2012-02-02
==================
* Fixed Block#includeBlock() with textOnly blocks
0.20.0 / 2011-12-28
==================
* Added a browser example
* Added `yield` for block `include`s
* Changed: replaced internal `__` var with `__jade` [chrisleishman]
* Fixed two globals. Closes #433
0.19.0 / 2011-12-02
==================
* Added block `append` / `prepend` support. Closes #355
* Added link in readme to jade-mode for Emacs
* Added link to python implementation
0.18.0 / 2011-11-21
==================
* Changed: only ['script', 'style'] are text-only. Closes #398'
0.17.0 / 2011-11-10
==================
* jade.renderFile() is back! (for express 3.x)
* Fixed `Object.keys()` failover bug
0.16.4 / 2011-10-24
==================
* Fixed a test due to reserved keyword
* Fixed: commander 0.1.x dep for 0.5.x
0.16.3 / 2011-10-24
==================
* Added: allow leading space for conditional comments
* Added quick implementation of a switch statement
* Fixed parens in mixin args. Closes #380
* Fixed: include files with a .jade extension as jade files
0.16.2 / 2011-09-30
==================
* Fixed include regression. Closes #354
0.16.1 / 2011-09-29
==================
* Fixed unexpected `else` bug when compileDebug: false
* Fixed attr state issue for balancing pairs. Closes #353
0.16.0 / 2011-09-26
==================
* Added `include` block support. Closes #303
* Added template inheritance via `block` and `extends`. Closes #242
* Added 'type="text/css"' to the style tags generated by filters.
* Added 'uglifyjs' as an explicit devDependency.
* Added -p, --path <path> flag to jade(1)
* Added support for any arbitrary doctype
* Added `jade.render(str[,options], fn)` back
* Added first-class `while` support
* Added first-class assignment support
* Fixed runtime.js `Array.isArray()` polyfill. Closes #345
* Fixed: set .filename option in jade(1) when passing filenames
* Fixed `Object.keys()` polyfill typo. Closes #331
* Fixed `include` error context
* Renamed magic "index" to "$index". Closes #350
0.15.4 / 2011-09-05
==================
* Fixed script template html. Closes #316
* Revert "Fixed script() tag with trailing ".". Closes #314"
0.15.3 / 2011-08-30
==================
* Added Makefile example. Closes #312
* Fixed script() tag with trailing ".". Closes #314
0.15.2 / 2011-08-26
==================
* Fixed new conditional boundaries. Closes #307
0.15.1 / 2011-08-26
==================
* Fixed jade(1) support due to `res.render()` removal
* Removed --watch support (use a makefile + watch...)
0.15.0 / 2011-08-26
==================
* Added `client` option to reference runtime helpers
* Added `Array.isArray()` for runtime.js as well
* Added `Object.keys()` for the client-side runtime
* Added first-class `if`, `unless`, `else` and `else if` support
* Added first-class `each` / `for` support
* Added `make benchmark` for continuous-bench
* Removed `inline` option, SS helpers are no longer inlined either
* Removed `Parser#debug()`
* Removed `jade.render()` and `jade.renderFile()`
* Fixed runtime.js `escape()` bug causing window.escape to be used
* Fixed a bunch of tests
0.14.2 / 2011-08-16
==================
* Added `include` support for non-jade files
* Fixed code indentation when followed by newline(s). Closes #295 [reported by masylum]
0.14.1 / 2011-08-14
==================
* Added `colons` option for everyone stuck with ":". Closes #231
* Optimization: consecutive lines are merged in compiled js
0.14.0 / 2011-08-08
==================
* Added array iteration with index example. Closes #276
* Added _runtime.js_
* Added `compileDebug` option to enable lineno instrumentation
* Added `inline` option to disable inlining of helpers (for client-side)
0.13.0 / 2011-07-13
==================
* Added `mixin` support
* Added `include` support
* Added array support for the class attribute
0.12.4 / 2011-06-23
==================
* Fixed filter indentation bug. Closes #243
0.12.3 / 2011-06-21
==================
* Fixed empty strings support. Closes #223
* Fixed conditional comments documentation. Closes #245
0.12.2 / 2011-06-16
==================
* Fixed `make test`
* Fixed block comments
0.12.1 / 2011-06-04
==================
* Fixed attribute interpolation with double quotes. Fixes #232 [topaxi]
0.12.0 / 2011-06-03
==================
* Added `doctype` as alias of `!!!`
* Added; doctype value is now case-insensitive
* Added attribute interpolation support
* Fixed; retain original indentation spaces in text blocks
0.11.1 / 2011-06-01
==================
* Fixed text block indentation [Laszlo Bacsi]
* Changed; utilizing devDependencies
* Fixed try/catch issue with renderFile(). Closes #227
* Removed attribute ":" support, use "=" (option for ':' coming soon)
0.11.0 / 2011-05-14
==================
* Added `self` object to avoid poor `with(){}` performance [masylum]
* Added `doctype` option [Jeremy Larkin]
0.10.7 / 2011-05-04
==================
* expose Parser
0.10.6 / 2011-04-29
==================
* Fixed CS `Object.keys()` [reported by robholland]
0.10.5 / 2011-04-26
==================
* Added error context after the lineno
* Added; indicate failing lineno with ">"
* Added `Object.keys()` for the client-side
* Fixed attr strings when containing the opposite quote. Closes 207
* Fixed attr issue with js expressions within strings
* Fixed single-quote filter escape bug. Closes #196
0.10.4 / 2011-04-05
==================
* Added `html` doctype, same as "5"
* Fixed `pre`, no longer text-only
0.10.3 / 2011-03-30
==================
* Fixed support for quoted attribute keys ex `rss("xmlns:atom"="atom")`
0.10.2 / 2011-03-30
==================
* Fixed pipeless text bug with missing outdent
0.10.1 / 2011-03-28
==================
* Fixed `support/compile.js` to exclude browser js in node
* Fixes for IE [Patrick Pfeiffer]
0.10.0 / 2011-03-25
==================
* Added AST-filter support back in the form of `<tag>[attrs]<:><block>`
0.9.3 / 2011-03-24
==================
* Added `Block#unshift(node)`
* Added `jade.js` for the client-side to the repo
* Added `jade.min.js` for the client-side to the repo
* Removed need for pipes in filters. Closes #185
Note that this _will_ break filters used to
manipulate the AST, until we have a different
syntax for doing so.
0.9.2 / 2011-03-23
==================
* Added jade `--version`
* Removed `${}` interpolation support, use `#{}`
0.9.1 / 2011-03-16
==================
* Fixed invalid `.map()` call due to recent changes
0.9.0 / 2011-03-16
==================
* Added client-side browser support via `make jade.js` and `make jade.min.js`.
0.8.9 / 2011-03-15
==================
* Fixed preservation of newlines in text blocks
0.8.8 / 2011-03-14
==================
* Fixed jade(1) stdio
0.8.7 / 2011-03-14
==================
* Added `mkdirs()` to jade(1)
* Added jade(1) stdio support
* Added new features to jade(1), `--watch`, recursive compilation etc [khingebjerg]
* Fixed pipe-less text newlines
* Removed jade(1) `--pipe` flag
0.8.6 / 2011-03-11
==================
* Fixed parenthesized expressions in attrs. Closes #170
* Changed; default interpolation values `== null` to ''. Closes #167
0.8.5 / 2011-03-09
==================
* Added pipe-less text support with immediate ".". Closes #157
* Fixed object support in attrs
* Fixed array support for attrs
0.8.4 / 2011-03-08
==================
* Fixed issue with expressions being evaluated several times. closes #162
0.8.2 / 2011-03-07
==================
* Added markdown, discount, and markdown-js support to `:markdown`. Closes #160
* Removed `:discount`
0.8.1 / 2011-03-04
==================
* Added `pre` pipe-less text support (and auto-escaping)
0.8.0 / 2011-03-04
==================
* Added block-expansion support. Closes #74
* Added support for multi-line attrs without commas. Closes #65
0.7.1 / 2011-03-04
==================
* Fixed `script()` etc pipe-less text with attrs
0.7.0 / 2011-03-04
==================
* Removed `:javascript` filter (it doesn't really do anything special, use `script` tags)
* Added pipe-less text support. Tags that only accept text nodes (`script`, `textarea`, etc) do not require `|`.
* Added `:text` filter for ad-hoc pipe-less
* Added flexible indentation. Tabs, arbitrary number of spaces etc
* Added conditional-comment support. Closes #146
* Added block comment support
* Added rss example
* Added `:stylus` filter
* Added `:discount` filter
* Fixed; auto-detect xml and do not self-close tags. Closes #147
* Fixed whitespace issue. Closes #118
* Fixed attrs. `,`, `=`, and `:` within attr value strings are valid Closes #133
* Fixed; only output "" when code == null. Ex: `span.name= user.name` when undefined or null will not output "undefined". Closes #130
* Fixed; throw on unexpected token instead of hanging
0.6.3 / 2011-02-02
==================
* Added `each` support for Array-like objects [guillermo]
0.6.2 / 2011-02-02
==================
* Added CSRF example, showing how you can transparently add inputs to a form
* Added link to vim-jade
* Fixed self-closing col support [guillermo]
* Fixed exception when getAttribute or removeAttribute run into removed attributes [Naitik Shah]
0.6.0 / 2010-12-19
==================
* Added unescaped interpolation variant `!{code}`. Closes #124
* Changed; escape interpolated code by default `#{code}`
0.5.7 / 2010-12-08
==================
* Fixed; hyphen in get `tag()`
0.5.6 / 2010-11-24
==================
* Added `exports.compile(str, options)`
* Renamed internal `_` to `__`, since `_()` is commonly used for translation
0.5.5 / 2010-10-30
==================
* Add _coffeescript_ filter [Michael Hampton]
* Added link to _slim_; a ruby implementation
* Fixed quoted attributes issue.
* Fixed attribute issue with over greedy regexp.
Previously "p(foo=(((('bar')))))= ((('baz')))"
would __fail__ for example since the regexp
would lookahead to far. Now we simply pair
the delimiters.
0.5.4 / 2010-10-18
==================
* Adding newline when using tag code when preceding text
* Assume newline in tag text when preceding text
* Changed; retain leading text whitespace
* Fixed code block support to prevent multiple buffer openings [Jake Luer]
* Fixed nested filter support
0.5.3 / 2010-10-06
==================
* Fixed bug when tags with code also have a block [reported by chrisirhc]
0.5.2 / 2010-10-05
==================
* Added; Text introduces newlines to mimic the grammar.
Whitespace handling is a little tricky with this sort of grammar.
Jade will now mimic the written grammar, meaning that text blocks
using the "|" margin character will introduce a literal newline,
where as immediate tag text (ex "a(href='#') Link") will not.
This may not be ideal, but it makes more sense than what Jade was
previously doing.
* Added `Tag#text` to disambiguate between immediate / block text
* Removed _pretty_ option (was kinda useless in the state it was in)
* Reverted ignoring of newlines. Closes #92.
* Fixed; `Parser#parse()` ignoring newlines
0.5.1 / 2010-10-04
==================
* Added many examples
* Added; compiler api is now public
* Added; filters can accept / manipulate the parse tree
* Added filter attribute support. Closes #79
* Added LL(*) capabilities
* Performance; wrapping code blocks in {} instead of `(function(){}).call(this)`
* Performance; Optimized attribute buffering
* Fixed trailing newlines in blocks
0.5.0 / 2010-09-11
==================
* __Major__ refactor. Logic now separated into lexer/parser/compiler for future extensibility.
* Added _pretty_ option
* Added parse tree output for _debug_ option
* Added new examples
* Removed _context_ option, use _scope_
0.4.1 / 2010-09-09
==================
* Added support for arbitrary indentation for single-line comments. Closes #71
* Only strip first space in text (ex '| foo' will buffer ' foo')
0.4.0 / 2010-08-30
==================
* Added tab naive support (tabs are converted to a single indent, aka two spaces). Closes #24
* Added unbuffered comment support. Closes #62
* Added hyphen support for tag names, ex: "fb:foo-bar"
* Fixed bug with single quotes in comments. Closes #61
* Fixed comment whitespace issue, previously padding. Closes #55
0.3.0 / 2010-08-04
==================
* Added single line comment support. Closes #25
* Removed CDATA from _:javascript_ filter. Closes #47
* Removed _sys_ local
* Fixed code following tag
0.2.4 / 2010-08-02
==================
* Added Buffer support to `render()`
* Fixed filter text block exception reporting
* Fixed tag exception reporting
0.2.3 / 2010-07-27
==================
* Fixed newlines before block
* Fixed; tag text allowing arbitrary trailing whitespace
0.2.2 / 2010-07-16
==================
* Added support for `jade.renderFile()` to utilize primed cache
* Added link to [textmate bundle](http://github.com/miksago/jade-tmbundle)
* Fixed filter issue with single quotes
* Fixed hyphenated attr bug
* Fixed interpolation single quotes. Closes #28
* Fixed issue with comma in attrs
0.2.1 / 2010-07-09
==================
* Added support for node-discount and markdown-js
depending on which is available.
* Added support for tags to have blocks _and_ text.
this kinda fucks with arbitrary whitespace unfortunately,
but also fixes trailing spaces after tags _with_ blocks.
* Caching generated functions. Closes #46
0.2.0 / 2010-07-08
==================
* Added `- each` support for readable iteration
* Added [markdown-js](http://github.com/evilstreak/markdown-js) support (no compilation required)
* Removed node-discount support
0.1.0 / 2010-07-05
==================
* Added `${}` support for interpolation. Closes #45
* Added support for quoted attr keys: `label("for": 'something')` is allowed (_although not required_) [Guillermo]
* Added `:less` filter [jakeluer]
0.0.2 / 2010-07-03
==================
* Added `context` as synonym for `scope` option [Guillermo]
* Fixed attr splitting: `div(style:"color: red")` is now allowed
* Fixed issue with `(` and `)` within attrs: `a(class: (a ? 'a' : 'b'))` is now allowed
* Fixed issue with leading / trailing spaces in attrs: `a( href="#" )` is now allowed [Guillermo]

22
node_modules/jade/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
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.

153
node_modules/jade/README.md generated vendored Normal file
View File

@@ -0,0 +1,153 @@
# [![Jade - Node Template Engine](http://jade-lang.com/logos/JadeBlack.svg)](http://jade-lang.com/)
Full documentation is at [jade-lang.com](http://jade-lang.com/)
Jade is a high performance template engine heavily influenced by [Haml](http://haml-lang.com)
and implemented with JavaScript for [node](http://nodejs.org) and browsers. For bug reports,
feature requests and questions, [open an issue](https://github.com/jadejs/jade/issues/new).
For discussion join the [chat room](https://gitter.im/jadejs/jade).
You can test drive Jade online [here](http://naltatis.github.com/jade-syntax-docs).
[![Build Status](https://img.shields.io/travis/jadejs/jade/master.svg?style=flat)](https://travis-ci.org/jadejs/jade)
[![Coverage Status](https://img.shields.io/coveralls/jadejs/jade/master.svg?style=flat)](https://coveralls.io/r/jadejs/jade?branch=master)
[![Dependency Status](https://img.shields.io/david/jadejs/jade.svg?style=flat)](https://david-dm.org/jadejs/jade)
[![devDependencies Status](https://img.shields.io/david/dev/jadejs/jade.svg?style=flat)](https://david-dm.org/jadejs/jade#info=devDependencies)
[![NPM version](https://img.shields.io/npm/v/jade.svg?style=flat)](http://badge.fury.io/js/jade)
[![Join Gitter Chat](https://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/jadejs/jade?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Installation
via npm:
```bash
$ npm install jade
```
## Syntax
Jade is a clean, whitespace sensitive syntax for writing html. Here is a simple example:
```jade
doctype html
html(lang="en")
head
title= pageTitle
script(type='text/javascript').
if (foo) bar(1 + 5)
body
h1 Jade - node template engine
#container.col
if youAreUsingJade
p You are amazing
else
p Get on it!
p.
Jade is a terse and simple templating language with a
strong focus on performance and powerful features.
```
becomes
```html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Jade</title>
<script type="text/javascript">
if (foo) bar(1 + 5)
</script>
</head>
<body>
<h1>Jade - node template engine</h1>
<div id="container" class="col">
<p>You are amazing</p>
<p>Jade is a terse and simple templating language with a strong focus on performance and powerful features.</p>
</div>
</body>
</html>
```
The official [jade tutorial](http://jade-lang.com/tutorial/) is a great place to start. While that (and the syntax documentation) is being finished, you can view some of the old documentation [here](https://github.com/jadejs/jade/blob/master/jade.md) and [here](https://github.com/jadejs/jade/blob/master/jade-language.md)
## API
For full API, see [jade-lang.com/api](http://jade-lang.com/api/)
```js
var jade = require('jade');
// compile
var fn = jade.compile('string of jade', options);
var html = fn(locals);
// render
var html = jade.render('string of jade', merge(options, locals));
// renderFile
var html = jade.renderFile('filename.jade', merge(options, locals));
```
### Options
- `filename` Used in exceptions, and required when using includes
- `compileDebug` When `false` no debug instrumentation is compiled
- `pretty` Add pretty-indentation whitespace to output _(false by default)_
## Browser Support
The latest version of jade can be download for the browser in standalone form from [here](https://github.com/jadejs/jade/raw/master/jade.js). It only supports the very latest browsers though, and is a large file. It is recommended that you pre-compile your jade templates to JavaScript and then just use the [runtime.js](https://github.com/jadejs/jade/raw/master/runtime.js) library on the client.
To compile a template for use on the client using the command line, do:
```console
$ jade --client --no-debug filename.jade
```
which will produce `filename.js` containing the compiled template.
## Command Line
After installing the latest version of [node](http://nodejs.org/), install with:
```console
$ npm install jade -g
```
and run with
```console
$ jade --help
```
## Additional Resources
Tutorials:
- cssdeck interactive [Jade syntax tutorial](http://cssdeck.com/labs/learning-the-jade-templating-engine-syntax)
- cssdeck interactive [Jade logic tutorial](http://cssdeck.com/labs/jade-templating-tutorial-codecast-part-2)
- [Jade について。](https://gist.github.com/japboy/5402844) (A Japanese Tutorial)
- [Jade - 模板引擎](https://github.com/jadejs/jade/blob/master/Readme_zh-cn.md)
Implementations in other languages:
- [php](http://github.com/everzet/jade.php)
- [scala](http://scalate.fusesource.org/versions/snapshot/documentation/scaml-reference.html)
- [ruby](https://github.com/slim-template/slim)
- [python](https://github.com/SyrusAkbary/pyjade)
- [java](https://github.com/neuland/jade4j)
Other:
- [Emacs Mode](https://github.com/brianc/jade-mode)
- [Vim Syntax](https://github.com/digitaltoad/vim-jade)
- [TextMate Bundle](http://github.com/miksago/jade-tmbundle)
- [Coda/SubEtha syntax Mode](https://github.com/aaronmccall/jade.mode)
- [Screencasts](http://tjholowaychuk.com/post/1004255394/jade-screencast-template-engine-for-nodejs)
- [html2jade](https://github.com/donpark/html2jade) converter
- [Jade Server](https://github.com/ded/jade-server) Ideal for building local prototypes apart from any application
## License
MIT

1285
node_modules/jade/Readme_zh-cn.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

267
node_modules/jade/bin/jade.js generated vendored Executable file
View File

@@ -0,0 +1,267 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var fs = require('fs')
, program = require('commander')
, path = require('path')
, basename = path.basename
, dirname = path.dirname
, resolve = path.resolve
, normalize = path.normalize
, join = path.join
, mkdirp = require('mkdirp')
, jade = require('../');
// jade options
var options = {};
// options
program
.version(require('../package.json').version)
.usage('[options] [dir|file ...]')
.option('-O, --obj <str|path>', 'JavaScript options object or JSON file containing it')
.option('-o, --out <dir>', 'output the compiled html to <dir>')
.option('-p, --path <path>', 'filename used to resolve includes')
.option('-P, --pretty', 'compile pretty html output')
.option('-c, --client', 'compile function for client-side runtime.js')
.option('-n, --name <str>', 'The name of the compiled template (requires --client)')
.option('-D, --no-debug', 'compile without debugging (smaller functions)')
.option('-w, --watch', 'watch files for changes and automatically re-render')
.option('-E, --extension <ext>', 'specify the output file extension')
.option('--name-after-file', 'Name the template after the last section of the file path (requires --client and overriden by --name)')
.option('--doctype <str>', 'Specify the doctype on the command line (useful if it is not specified by the template)')
program.on('--help', function(){
console.log(' Examples:');
console.log('');
console.log(' # translate jade the templates dir');
console.log(' $ jade templates');
console.log('');
console.log(' # create {foo,bar}.html');
console.log(' $ jade {foo,bar}.jade');
console.log('');
console.log(' # jade over stdio');
console.log(' $ jade < my.jade > my.html');
console.log('');
console.log(' # jade over stdio');
console.log(' $ echo \'h1 Jade!\' | jade');
console.log('');
console.log(' # foo, bar dirs rendering to /tmp');
console.log(' $ jade foo bar --out /tmp ');
console.log('');
});
program.parse(process.argv);
// options given, parse them
if (program.obj) {
options = parseObj(program.obj);
}
/**
* Parse object either in `input` or in the file called `input`. The latter is
* searched first.
*/
function parseObj (input) {
var str, out;
try {
str = fs.readFileSync(program.obj);
} catch (e) {
return eval('(' + program.obj + ')');
}
// We don't want to catch exceptions thrown in JSON.parse() so have to
// use this two-step approach.
return JSON.parse(str);
}
// --path
if (program.path) options.filename = program.path;
// --no-debug
options.compileDebug = program.debug;
// --client
options.client = program.client;
// --pretty
options.pretty = program.pretty;
// --watch
options.watch = program.watch;
// --name
if (typeof program.name === 'string') {
options.name = program.name;
}
// --doctype
options.doctype = program.doctype;
// left-over args are file paths
var files = program.args;
// array of paths that are being watched
var watchList = [];
// compile files
if (files.length) {
console.log();
if (options.watch) {
process.on('SIGINT', function() {
process.exit(1);
});
files.forEach(tryRender);
} else {
files.forEach(renderFile);
}
process.on('exit', function () {
console.log();
});
// stdio
} else {
stdin();
}
/**
* Watch for changes on path
*
* Renders `base` if specified, otherwise renders `path`.
*/
function watchFile(path, base) {
path = normalize(path);
if (watchList.indexOf(path) !== -1) return;
console.log(" \033[90mwatching \033[36m%s\033[0m", path);
fs.watchFile(path, {persistent: true, interval: 200},
function (curr, prev) {
// File doesn't exist anymore. Keep watching.
if (curr.mtime.getTime() === 0) return;
// istanbul ignore if
if (curr.mtime.getTime() === prev.mtime.getTime()) return;
tryRender(base || path);
});
watchList.push(path);
}
/**
* Convert error to string
*/
function errorToString(e) {
return e.stack || /* istanbul ignore next */ (e.message || e);
}
/**
* Try to render `path`; if an exception is thrown it is printed to stderr and
* otherwise ignored.
*
* This is used in watch mode.
*/
function tryRender(path) {
try {
renderFile(path);
} catch (e) {
// keep watching when error occured.
console.error(errorToString(e));
}
}
/**
* Compile from stdin.
*/
function stdin() {
var buf = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(chunk){ buf += chunk; });
process.stdin.on('end', function(){
var output;
if (options.client) {
output = jade.compileClient(buf, options);
} else {
var fn = jade.compile(buf, options);
var output = fn(options);
}
process.stdout.write(output);
}).resume();
process.on('SIGINT', function() {
process.stdout.write('\n');
process.stdin.emit('end');
process.stdout.write('\n');
process.exit();
})
}
/**
* Process the given path, compiling the jade files found.
* Always walk the subdirectories.
*/
function renderFile(path) {
var re = /\.jade$/;
var stat = fs.lstatSync(path);
// Found jade file/\.jade$/
if (stat.isFile() && re.test(path)) {
if (options.watch) watchFile(path);
var str = fs.readFileSync(path, 'utf8');
options.filename = path;
if (program.nameAfterFile) {
options.name = getNameFromFileName(path);
}
var fn = options.client ? jade.compileClient(str, options) : jade.compile(str, options);
if (options.watch && fn.dependencies) {
// watch dependencies, and recompile the base
fn.dependencies.forEach(function (dep) {
watchFile(dep, path);
});
}
// --extension
if (program.extension) var extname = '.' + program.extension;
else if (options.client) var extname = '.js';
else var extname = '.html';
path = path.replace(re, extname);
if (program.out) path = join(program.out, basename(path));
var dir = resolve(dirname(path));
mkdirp.sync(dir, 0755);
var output = options.client ? fn : fn(options);
fs.writeFileSync(path, output);
console.log(' \033[90mrendered \033[36m%s\033[0m', normalize(path));
// Found directory
} else if (stat.isDirectory()) {
var files = fs.readdirSync(path);
files.map(function(filename) {
return path + '/' + filename;
}).forEach(renderFile);
}
}
/**
* Get a sensible name for a template function from a file path
*
* @param {String} filename
* @returns {String}
*/
function getNameFromFileName(filename) {
var file = basename(filename, '.jade');
return file.toLowerCase().replace(/[^a-z0-9]+([a-z])/g, function (_, character) {
return character.toUpperCase();
}) + 'Template';
}

16
node_modules/jade/component.json generated vendored Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "jade",
"repo": "visionmedia/jade",
"description": "Jade template runtime",
"version": "1.9.2",
"keywords": [
"template"
],
"dependencies": {},
"development": {},
"license": "MIT",
"scripts": [
"lib/runtime.js"
],
"main": "lib/runtime.js"
}

7602
node_modules/jade/jade.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

721
node_modules/jade/lib/compiler.js generated vendored Normal file
View File

@@ -0,0 +1,721 @@
'use strict';
var nodes = require('./nodes');
var filters = require('./filters');
var doctypes = require('./doctypes');
var runtime = require('./runtime');
var utils = require('./utils');
var selfClosing = require('void-elements');
var parseJSExpression = require('character-parser').parseMax;
var constantinople = require('constantinople');
function isConstant(src) {
return constantinople(src, {jade: runtime, 'jade_interp': undefined});
}
function toConstant(src) {
return constantinople.toConstant(src, {jade: runtime, 'jade_interp': undefined});
}
function errorAtNode(node, error) {
error.line = node.line;
error.filename = node.filename;
return error;
}
/**
* Initialize `Compiler` with the given `node`.
*
* @param {Node} node
* @param {Object} options
* @api public
*/
var Compiler = module.exports = function Compiler(node, options) {
this.options = options = options || {};
this.node = node;
this.hasCompiledDoctype = false;
this.hasCompiledTag = false;
this.pp = options.pretty || false;
if (this.pp && typeof this.pp !== 'string') {
this.pp = ' ';
}
this.debug = false !== options.compileDebug;
this.indents = 0;
this.parentIndents = 0;
this.terse = false;
this.mixins = {};
this.dynamicMixins = false;
if (options.doctype) this.setDoctype(options.doctype);
};
/**
* Compiler prototype.
*/
Compiler.prototype = {
/**
* Compile parse tree to JavaScript.
*
* @api public
*/
compile: function(){
this.buf = [];
if (this.pp) this.buf.push("var jade_indent = [];");
this.lastBufferedIdx = -1;
this.visit(this.node);
if (!this.dynamicMixins) {
// if there are no dynamic mixins we can remove any un-used mixins
var mixinNames = Object.keys(this.mixins);
for (var i = 0; i < mixinNames.length; i++) {
var mixin = this.mixins[mixinNames[i]];
if (!mixin.used) {
for (var x = 0; x < mixin.instances.length; x++) {
for (var y = mixin.instances[x].start; y < mixin.instances[x].end; y++) {
this.buf[y] = '';
}
}
}
}
}
return this.buf.join('\n');
},
/**
* Sets the default doctype `name`. Sets terse mode to `true` when
* html 5 is used, causing self-closing tags to end with ">" vs "/>",
* and boolean attributes are not mirrored.
*
* @param {string} name
* @api public
*/
setDoctype: function(name){
this.doctype = doctypes[name.toLowerCase()] || '<!DOCTYPE ' + name + '>';
this.terse = this.doctype.toLowerCase() == '<!doctype html>';
this.xml = 0 == this.doctype.indexOf('<?xml');
},
/**
* Buffer the given `str` exactly as is or with interpolation
*
* @param {String} str
* @param {Boolean} interpolate
* @api public
*/
buffer: function (str, interpolate) {
var self = this;
if (interpolate) {
var match = /(\\)?([#!]){((?:.|\n)*)$/.exec(str);
if (match) {
this.buffer(str.substr(0, match.index), false);
if (match[1]) { // escape
this.buffer(match[2] + '{', false);
this.buffer(match[3], true);
return;
} else {
var rest = match[3];
var range = parseJSExpression(rest);
var code = ('!' == match[2] ? '' : 'jade.escape') + "((jade_interp = " + range.src + ") == null ? '' : jade_interp)";
this.bufferExpression(code);
this.buffer(rest.substr(range.end + 1), true);
return;
}
}
}
str = utils.stringify(str);
str = str.substr(1, str.length - 2);
if (this.lastBufferedIdx == this.buf.length) {
if (this.lastBufferedType === 'code') this.lastBuffered += ' + "';
this.lastBufferedType = 'text';
this.lastBuffered += str;
this.buf[this.lastBufferedIdx - 1] = 'buf.push(' + this.bufferStartChar + this.lastBuffered + '");'
} else {
this.buf.push('buf.push("' + str + '");');
this.lastBufferedType = 'text';
this.bufferStartChar = '"';
this.lastBuffered = str;
this.lastBufferedIdx = this.buf.length;
}
},
/**
* Buffer the given `src` so it is evaluated at run time
*
* @param {String} src
* @api public
*/
bufferExpression: function (src) {
if (isConstant(src)) {
return this.buffer(toConstant(src) + '', false)
}
if (this.lastBufferedIdx == this.buf.length) {
if (this.lastBufferedType === 'text') this.lastBuffered += '"';
this.lastBufferedType = 'code';
this.lastBuffered += ' + (' + src + ')';
this.buf[this.lastBufferedIdx - 1] = 'buf.push(' + this.bufferStartChar + this.lastBuffered + ');'
} else {
this.buf.push('buf.push(' + src + ');');
this.lastBufferedType = 'code';
this.bufferStartChar = '';
this.lastBuffered = '(' + src + ')';
this.lastBufferedIdx = this.buf.length;
}
},
/**
* Buffer an indent based on the current `indent`
* property and an additional `offset`.
*
* @param {Number} offset
* @param {Boolean} newline
* @api public
*/
prettyIndent: function(offset, newline){
offset = offset || 0;
newline = newline ? '\n' : '';
this.buffer(newline + Array(this.indents + offset).join(this.pp));
if (this.parentIndents)
this.buf.push("buf.push.apply(buf, jade_indent);");
},
/**
* Visit `node`.
*
* @param {Node} node
* @api public
*/
visit: function(node){
var debug = this.debug;
if (debug) {
this.buf.push('jade_debug.unshift({ lineno: ' + node.line
+ ', filename: ' + (node.filename
? utils.stringify(node.filename)
: 'jade_debug[0].filename')
+ ' });');
}
// Massive hack to fix our context
// stack for - else[ if] etc
if (false === node.debug && this.debug) {
this.buf.pop();
this.buf.pop();
}
this.visitNode(node);
if (debug) this.buf.push('jade_debug.shift();');
},
/**
* Visit `node`.
*
* @param {Node} node
* @api public
*/
visitNode: function(node){
return this['visit' + node.type](node);
},
/**
* Visit case `node`.
*
* @param {Literal} node
* @api public
*/
visitCase: function(node){
var _ = this.withinCase;
this.withinCase = true;
this.buf.push('switch (' + node.expr + '){');
this.visit(node.block);
this.buf.push('}');
this.withinCase = _;
},
/**
* Visit when `node`.
*
* @param {Literal} node
* @api public
*/
visitWhen: function(node){
if ('default' == node.expr) {
this.buf.push('default:');
} else {
this.buf.push('case ' + node.expr + ':');
}
if (node.block) {
this.visit(node.block);
this.buf.push(' break;');
}
},
/**
* Visit literal `node`.
*
* @param {Literal} node
* @api public
*/
visitLiteral: function(node){
this.buffer(node.str);
},
/**
* Visit all nodes in `block`.
*
* @param {Block} block
* @api public
*/
visitBlock: function(block){
var len = block.nodes.length
, escape = this.escape
, pp = this.pp
// Pretty print multi-line text
if (pp && len > 1 && !escape && block.nodes[0].isText && block.nodes[1].isText)
this.prettyIndent(1, true);
for (var i = 0; i < len; ++i) {
// Pretty print text
if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText)
this.prettyIndent(1, false);
this.visit(block.nodes[i]);
// Multiple text nodes are separated by newlines
if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText)
this.buffer('\n');
}
},
/**
* Visit a mixin's `block` keyword.
*
* @param {MixinBlock} block
* @api public
*/
visitMixinBlock: function(block){
if (this.pp) this.buf.push("jade_indent.push('" + Array(this.indents + 1).join(this.pp) + "');");
this.buf.push('block && block();');
if (this.pp) this.buf.push("jade_indent.pop();");
},
/**
* Visit `doctype`. Sets terse mode to `true` when html 5
* is used, causing self-closing tags to end with ">" vs "/>",
* and boolean attributes are not mirrored.
*
* @param {Doctype} doctype
* @api public
*/
visitDoctype: function(doctype){
if (doctype && (doctype.val || !this.doctype)) {
this.setDoctype(doctype.val || 'default');
}
if (this.doctype) this.buffer(this.doctype);
this.hasCompiledDoctype = true;
},
/**
* Visit `mixin`, generating a function that
* may be called within the template.
*
* @param {Mixin} mixin
* @api public
*/
visitMixin: function(mixin){
var name = 'jade_mixins[';
var args = mixin.args || '';
var block = mixin.block;
var attrs = mixin.attrs;
var attrsBlocks = mixin.attributeBlocks.slice();
var pp = this.pp;
var dynamic = mixin.name[0]==='#';
var key = mixin.name;
if (dynamic) this.dynamicMixins = true;
name += (dynamic ? mixin.name.substr(2,mixin.name.length-3):'"'+mixin.name+'"')+']';
this.mixins[key] = this.mixins[key] || {used: false, instances: []};
if (mixin.call) {
this.mixins[key].used = true;
if (pp) this.buf.push("jade_indent.push('" + Array(this.indents + 1).join(pp) + "');")
if (block || attrs.length || attrsBlocks.length) {
this.buf.push(name + '.call({');
if (block) {
this.buf.push('block: function(){');
// Render block with no indents, dynamically added when rendered
this.parentIndents++;
var _indents = this.indents;
this.indents = 0;
this.visit(mixin.block);
this.indents = _indents;
this.parentIndents--;
if (attrs.length || attrsBlocks.length) {
this.buf.push('},');
} else {
this.buf.push('}');
}
}
if (attrsBlocks.length) {
if (attrs.length) {
var val = this.attrs(attrs);
attrsBlocks.unshift(val);
}
this.buf.push('attributes: jade.merge([' + attrsBlocks.join(',') + '])');
} else if (attrs.length) {
var val = this.attrs(attrs);
this.buf.push('attributes: ' + val);
}
if (args) {
this.buf.push('}, ' + args + ');');
} else {
this.buf.push('});');
}
} else {
this.buf.push(name + '(' + args + ');');
}
if (pp) this.buf.push("jade_indent.pop();")
} else {
var mixin_start = this.buf.length;
args = args ? args.split(',') : [];
var rest;
if (args.length && /^\.\.\./.test(args[args.length - 1].trim())) {
rest = args.pop().trim().replace(/^\.\.\./, '');
}
this.buf.push(name + ' = function(' + args.join(',') + '){');
this.buf.push('var block = (this && this.block), attributes = (this && this.attributes) || {};');
if (rest) {
this.buf.push('var ' + rest + ' = [];');
this.buf.push('for (jade_interp = ' + args.length + '; jade_interp < arguments.length; jade_interp++) {');
this.buf.push(' ' + rest + '.push(arguments[jade_interp]);');
this.buf.push('}');
}
this.parentIndents++;
this.visit(block);
this.parentIndents--;
this.buf.push('};');
var mixin_end = this.buf.length;
this.mixins[key].instances.push({start: mixin_start, end: mixin_end});
}
},
/**
* Visit `tag` buffering tag markup, generating
* attributes, visiting the `tag`'s code and block.
*
* @param {Tag} tag
* @api public
*/
visitTag: function(tag){
this.indents++;
var name = tag.name
, pp = this.pp
, self = this;
function bufferName() {
if (tag.buffer) self.bufferExpression(name);
else self.buffer(name);
}
if ('pre' == tag.name) this.escape = true;
if (!this.hasCompiledTag) {
if (!this.hasCompiledDoctype && 'html' == name) {
this.visitDoctype();
}
this.hasCompiledTag = true;
}
// pretty print
if (pp && !tag.isInline())
this.prettyIndent(0, true);
if (tag.selfClosing || (!this.xml && selfClosing[tag.name])) {
this.buffer('<');
bufferName();
this.visitAttributes(tag.attrs, tag.attributeBlocks.slice());
this.terse
? this.buffer('>')
: this.buffer('/>');
// if it is non-empty throw an error
if (tag.block &&
!(tag.block.type === 'Block' && tag.block.nodes.length === 0) &&
tag.block.nodes.some(function (tag) {
return tag.type !== 'Text' || !/^\s*$/.test(tag.val)
})) {
throw errorAtNode(tag, new Error(name + ' is self closing and should not have content.'));
}
} else {
// Optimize attributes buffering
this.buffer('<');
bufferName();
this.visitAttributes(tag.attrs, tag.attributeBlocks.slice());
this.buffer('>');
if (tag.code) this.visitCode(tag.code);
this.visit(tag.block);
// pretty print
if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline())
this.prettyIndent(0, true);
this.buffer('</');
bufferName();
this.buffer('>');
}
if ('pre' == tag.name) this.escape = false;
this.indents--;
},
/**
* Visit `filter`, throwing when the filter does not exist.
*
* @param {Filter} filter
* @api public
*/
visitFilter: function(filter){
var text = filter.block.nodes.map(
function(node){ return node.val; }
).join('\n');
filter.attrs.filename = this.options.filename;
try {
this.buffer(filters(filter.name, text, filter.attrs), true);
} catch (err) {
throw errorAtNode(filter, err);
}
},
/**
* Visit `text` node.
*
* @param {Text} text
* @api public
*/
visitText: function(text){
this.buffer(text.val, true);
},
/**
* Visit a `comment`, only buffering when the buffer flag is set.
*
* @param {Comment} comment
* @api public
*/
visitComment: function(comment){
if (!comment.buffer) return;
if (this.pp) this.prettyIndent(1, true);
this.buffer('<!--' + comment.val + '-->');
},
/**
* Visit a `BlockComment`.
*
* @param {Comment} comment
* @api public
*/
visitBlockComment: function(comment){
if (!comment.buffer) return;
if (this.pp) this.prettyIndent(1, true);
this.buffer('<!--' + comment.val);
this.visit(comment.block);
if (this.pp) this.prettyIndent(1, true);
this.buffer('-->');
},
/**
* Visit `code`, respecting buffer / escape flags.
* If the code is followed by a block, wrap it in
* a self-calling function.
*
* @param {Code} code
* @api public
*/
visitCode: function(code){
// Wrap code blocks with {}.
// we only wrap unbuffered code blocks ATM
// since they are usually flow control
// Buffer code
if (code.buffer) {
var val = code.val.trim();
val = 'null == (jade_interp = '+val+') ? "" : jade_interp';
if (code.escape) val = 'jade.escape(' + val + ')';
this.bufferExpression(val);
} else {
this.buf.push(code.val);
}
// Block support
if (code.block) {
if (!code.buffer) this.buf.push('{');
this.visit(code.block);
if (!code.buffer) this.buf.push('}');
}
},
/**
* Visit `each` block.
*
* @param {Each} each
* @api public
*/
visitEach: function(each){
this.buf.push(''
+ '// iterate ' + each.obj + '\n'
+ ';(function(){\n'
+ ' var $$obj = ' + each.obj + ';\n'
+ ' if (\'number\' == typeof $$obj.length) {\n');
if (each.alternative) {
this.buf.push(' if ($$obj.length) {');
}
this.buf.push(''
+ ' for (var ' + each.key + ' = 0, $$l = $$obj.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n'
+ ' var ' + each.val + ' = $$obj[' + each.key + '];\n');
this.visit(each.block);
this.buf.push(' }\n');
if (each.alternative) {
this.buf.push(' } else {');
this.visit(each.alternative);
this.buf.push(' }');
}
this.buf.push(''
+ ' } else {\n'
+ ' var $$l = 0;\n'
+ ' for (var ' + each.key + ' in $$obj) {\n'
+ ' $$l++;'
+ ' var ' + each.val + ' = $$obj[' + each.key + '];\n');
this.visit(each.block);
this.buf.push(' }\n');
if (each.alternative) {
this.buf.push(' if ($$l === 0) {');
this.visit(each.alternative);
this.buf.push(' }');
}
this.buf.push(' }\n}).call(this);\n');
},
/**
* Visit `attrs`.
*
* @param {Array} attrs
* @api public
*/
visitAttributes: function(attrs, attributeBlocks){
if (attributeBlocks.length) {
if (attrs.length) {
var val = this.attrs(attrs);
attributeBlocks.unshift(val);
}
this.bufferExpression('jade.attrs(jade.merge([' + attributeBlocks.join(',') + ']), ' + utils.stringify(this.terse) + ')');
} else if (attrs.length) {
this.attrs(attrs, true);
}
},
/**
* Compile attributes.
*/
attrs: function(attrs, buffer){
var buf = [];
var classes = [];
var classEscaping = [];
attrs.forEach(function(attr){
var key = attr.name;
var escaped = attr.escaped;
if (key === 'class') {
classes.push(attr.val);
classEscaping.push(attr.escaped);
} else if (isConstant(attr.val)) {
if (buffer) {
this.buffer(runtime.attr(key, toConstant(attr.val), escaped, this.terse));
} else {
var val = toConstant(attr.val);
if (key === 'style') val = runtime.style(val);
if (escaped && !(key.indexOf('data') === 0 && typeof val !== 'string')) {
val = runtime.escape(val);
}
buf.push(utils.stringify(key) + ': ' + utils.stringify(val));
}
} else {
if (buffer) {
this.bufferExpression('jade.attr("' + key + '", ' + attr.val + ', ' + utils.stringify(escaped) + ', ' + utils.stringify(this.terse) + ')');
} else {
var val = attr.val;
if (key === 'style') {
val = 'jade.style(' + val + ')';
}
if (escaped && !(key.indexOf('data') === 0)) {
val = 'jade.escape(' + val + ')';
} else if (escaped) {
val = '(typeof (jade_interp = ' + val + ') == "string" ? jade.escape(jade_interp) : jade_interp)';
}
buf.push(utils.stringify(key) + ': ' + val);
}
}
}.bind(this));
if (buffer) {
if (classes.every(isConstant)) {
this.buffer(runtime.cls(classes.map(toConstant), classEscaping));
} else {
this.bufferExpression('jade.cls([' + classes.join(',') + '], ' + utils.stringify(classEscaping) + ')');
}
} else if (classes.length) {
if (classes.every(isConstant)) {
classes = utils.stringify(runtime.joinClasses(classes.map(toConstant).map(runtime.joinClasses).map(function (cls, i) {
return classEscaping[i] ? runtime.escape(cls) : cls;
})));
} else {
classes = '(jade_interp = ' + utils.stringify(classEscaping) + ',' +
' jade.joinClasses([' + classes.join(',') + '].map(jade.joinClasses).map(function (cls, i) {' +
' return jade_interp[i] ? jade.escape(cls) : cls' +
' }))' +
')';
}
if (classes.length)
buf.push('"class": ' + classes);
}
return '{' + buf.join(',') + '}';
}
};

12
node_modules/jade/lib/doctypes.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
'use strict';
module.exports = {
'default': '<!DOCTYPE html>'
, 'xml': '<?xml version="1.0" encoding="utf-8" ?>'
, 'transitional': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
, 'strict': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
, 'frameset': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
, '1.1': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
, 'basic': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
, 'mobile': '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
};

10
node_modules/jade/lib/filters-client.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
'use strict';
module.exports = filter;
function filter(name, str, options) {
if (typeof filter[name] === 'function') {
return filter[name](str, options);
} else {
throw new Error('unknown filter ":' + name + '"');
}
}

14
node_modules/jade/lib/filters.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
'use strict';
var transformers = require('transformers');
module.exports = filter;
function filter(name, str, options) {
if (typeof filter[name] === 'function') {
return filter[name](str, options);
} else if (transformers[name]) {
return transformers[name].renderSync(str, options);
} else {
throw new Error('unknown filter ":' + name + '"');
}
}

413
node_modules/jade/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,413 @@
'use strict';
/*!
* Jade
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var Parser = require('./parser')
, Lexer = require('./lexer')
, Compiler = require('./compiler')
, runtime = require('./runtime')
, addWith = require('with')
, fs = require('fs')
, utils = require('./utils');
/**
* Expose self closing tags.
*/
// FIXME: either stop exporting selfClosing in v2 or export the new object
// form
exports.selfClosing = Object.keys(require('void-elements'));
/**
* Default supported doctypes.
*/
exports.doctypes = require('./doctypes');
/**
* Text filters.
*/
exports.filters = require('./filters');
/**
* Utilities.
*/
exports.utils = utils;
/**
* Expose `Compiler`.
*/
exports.Compiler = Compiler;
/**
* Expose `Parser`.
*/
exports.Parser = Parser;
/**
* Expose `Lexer`.
*/
exports.Lexer = Lexer;
/**
* Nodes.
*/
exports.nodes = require('./nodes');
/**
* Jade runtime helpers.
*/
exports.runtime = runtime;
/**
* Template function cache.
*/
exports.cache = {};
/**
* Parse the given `str` of jade and return a function body.
*
* @param {String} str
* @param {Object} options
* @return {Object}
* @api private
*/
function parse(str, options){
if (options.lexer) {
console.warn('Using `lexer` as a local in render() is deprecated and '
+ 'will be interpreted as an option in Jade 2.0.0');
}
// Parse
var parser = new (options.parser || Parser)(str, options.filename, options);
var tokens;
try {
// Parse
tokens = parser.parse();
} catch (err) {
parser = parser.context();
runtime.rethrow(err, parser.filename, parser.lexer.lineno, parser.input);
}
// Compile
var compiler = new (options.compiler || Compiler)(tokens, options);
var js;
try {
js = compiler.compile();
} catch (err) {
if (err.line && (err.filename || !options.filename)) {
runtime.rethrow(err, err.filename, err.line, parser.input);
} else {
if (err instanceof Error) {
err.message += '\n\nPlease report this entire error and stack trace to https://github.com/jadejs/jade/issues';
}
throw err;
}
}
// Debug compiler
if (options.debug) {
console.error('\nCompiled Function:\n\n\u001b[90m%s\u001b[0m', js.replace(/^/gm, ' '));
}
var globals = [];
if (options.globals) {
globals = options.globals.slice();
}
globals.push('jade');
globals.push('jade_mixins');
globals.push('jade_interp');
globals.push('jade_debug');
globals.push('buf');
var body = ''
+ 'var buf = [];\n'
+ 'var jade_mixins = {};\n'
+ 'var jade_interp;\n'
+ (options.self
? 'var self = locals || {};\n' + js
: addWith('locals || {}', '\n' + js, globals)) + ';'
+ 'return buf.join("");';
return {body: body, dependencies: parser.dependencies};
}
/**
* Get the template from a string or a file, either compiled on-the-fly or
* read from cache (if enabled), and cache the template if needed.
*
* If `str` is not set, the file specified in `options.filename` will be read.
*
* If `options.cache` is true, this function reads the file from
* `options.filename` so it must be set prior to calling this function.
*
* @param {Object} options
* @param {String=} str
* @return {Function}
* @api private
*/
function handleTemplateCache (options, str) {
var key = options.filename;
if (options.cache && exports.cache[key]) {
return exports.cache[key];
} else {
if (str === undefined) str = fs.readFileSync(options.filename, 'utf8');
var templ = exports.compile(str, options);
if (options.cache) exports.cache[key] = templ;
return templ;
}
}
/**
* Compile a `Function` representation of the given jade `str`.
*
* Options:
*
* - `compileDebug` when `false` debugging code is stripped from the compiled
template, when it is explicitly `true`, the source code is included in
the compiled template for better accuracy.
* - `filename` used to improve errors when `compileDebug` is not `false` and to resolve imports/extends
*
* @param {String} str
* @param {Options} options
* @return {Function}
* @api public
*/
exports.compile = function(str, options){
var options = options || {}
, filename = options.filename
? utils.stringify(options.filename)
: 'undefined'
, fn;
str = String(str);
var parsed = parse(str, options);
if (options.compileDebug !== false) {
fn = [
'var jade_debug = [{ lineno: 1, filename: ' + filename + ' }];'
, 'try {'
, parsed.body
, '} catch (err) {'
, ' jade.rethrow(err, jade_debug[0].filename, jade_debug[0].lineno' + (options.compileDebug === true ? ',' + utils.stringify(str) : '') + ');'
, '}'
].join('\n');
} else {
fn = parsed.body;
}
fn = new Function('locals, jade', fn)
var res = function(locals){ return fn(locals, Object.create(runtime)) };
if (options.client) {
res.toString = function () {
var err = new Error('The `client` option is deprecated, use the `jade.compileClient` method instead');
err.name = 'Warning';
console.error(err.stack || /* istanbul ignore next */ err.message);
return exports.compileClient(str, options);
};
}
res.dependencies = parsed.dependencies;
return res;
};
/**
* Compile a JavaScript source representation of the given jade `str`.
*
* Options:
*
* - `compileDebug` When it is `true`, the source code is included in
* the compiled template for better error messages.
* - `filename` used to improve errors when `compileDebug` is not `true` and to resolve imports/extends
* - `name` the name of the resulting function (defaults to "template")
*
* @param {String} str
* @param {Options} options
* @return {Object}
* @api public
*/
exports.compileClientWithDependenciesTracked = function(str, options){
var options = options || {};
var name = options.name || 'template';
var filename = options.filename ? utils.stringify(options.filename) : 'undefined';
var fn;
str = String(str);
options.compileDebug = options.compileDebug ? true : false;
var parsed = parse(str, options);
if (options.compileDebug) {
fn = [
'var jade_debug = [{ lineno: 1, filename: ' + filename + ' }];'
, 'try {'
, parsed.body
, '} catch (err) {'
, ' jade.rethrow(err, jade_debug[0].filename, jade_debug[0].lineno, ' + utils.stringify(str) + ');'
, '}'
].join('\n');
} else {
fn = parsed.body;
}
return {body: 'function ' + name + '(locals) {\n' + fn + '\n}', dependencies: parsed.dependencies};
};
/**
* Compile a JavaScript source representation of the given jade `str`.
*
* Options:
*
* - `compileDebug` When it is `true`, the source code is included in
* the compiled template for better error messages.
* - `filename` used to improve errors when `compileDebug` is not `true` and to resolve imports/extends
* - `name` the name of the resulting function (defaults to "template")
*
* @param {String} str
* @param {Options} options
* @return {String}
* @api public
*/
exports.compileClient = function (str, options) {
return exports.compileClientWithDependenciesTracked(str, options).body;
};
/**
* Compile a `Function` representation of the given jade file.
*
* Options:
*
* - `compileDebug` when `false` debugging code is stripped from the compiled
template, when it is explicitly `true`, the source code is included in
the compiled template for better accuracy.
*
* @param {String} path
* @param {Options} options
* @return {Function}
* @api public
*/
exports.compileFile = function (path, options) {
options = options || {};
options.filename = path;
return handleTemplateCache(options);
};
/**
* Render the given `str` of jade.
*
* Options:
*
* - `cache` enable template caching
* - `filename` filename required for `include` / `extends` and caching
*
* @param {String} str
* @param {Object|Function} options or fn
* @param {Function|undefined} fn
* @returns {String}
* @api public
*/
exports.render = function(str, options, fn){
// support callback API
if ('function' == typeof options) {
fn = options, options = undefined;
}
if (typeof fn === 'function') {
var res
try {
res = exports.render(str, options);
} catch (ex) {
return fn(ex);
}
return fn(null, res);
}
options = options || {};
// cache requires .filename
if (options.cache && !options.filename) {
throw new Error('the "filename" option is required for caching');
}
return handleTemplateCache(options, str)(options);
};
/**
* Render a Jade file at the given `path`.
*
* @param {String} path
* @param {Object|Function} options or callback
* @param {Function|undefined} fn
* @returns {String}
* @api public
*/
exports.renderFile = function(path, options, fn){
// support callback API
if ('function' == typeof options) {
fn = options, options = undefined;
}
if (typeof fn === 'function') {
var res
try {
res = exports.renderFile(path, options);
} catch (ex) {
return fn(ex);
}
return fn(null, res);
}
options = options || {};
options.filename = path;
return handleTemplateCache(options)(options);
};
/**
* Compile a Jade file at the given `path` for use on the client.
*
* @param {String} path
* @param {Object} options
* @returns {String}
* @api public
*/
exports.compileFileClient = function(path, options){
var key = path + ':client';
options = options || {};
options.filename = path;
if (options.cache && exports.cache[key]) {
return exports.cache[key];
}
var str = fs.readFileSync(options.filename, 'utf8');
var out = exports.compileClient(str, options);
if (options.cache) exports.cache[key] = out;
return out;
};
/**
* Express support.
*/
exports.__express = exports.renderFile;

23
node_modules/jade/lib/inline-tags.js generated vendored Normal file
View File

@@ -0,0 +1,23 @@
'use strict';
module.exports = [
'a'
, 'abbr'
, 'acronym'
, 'b'
, 'br'
, 'code'
, 'em'
, 'font'
, 'i'
, 'img'
, 'ins'
, 'kbd'
, 'map'
, 'samp'
, 'small'
, 'span'
, 'strong'
, 'sub'
, 'sup'
];

932
node_modules/jade/lib/lexer.js generated vendored Normal file
View File

@@ -0,0 +1,932 @@
'use strict';
var utils = require('./utils');
var characterParser = require('character-parser');
/**
* Initialize `Lexer` with the given `str`.
*
* @param {String} str
* @param {String} filename
* @api private
*/
var Lexer = module.exports = function Lexer(str, filename) {
this.input = str.replace(/\r\n|\r/g, '\n');
this.filename = filename;
this.deferredTokens = [];
this.lastIndents = 0;
this.lineno = 1;
this.stash = [];
this.indentStack = [];
this.indentRe = null;
this.pipeless = false;
};
function assertExpression(exp) {
//this verifies that a JavaScript expression is valid
Function('', 'return (' + exp + ')');
}
function assertNestingCorrect(exp) {
//this verifies that code is properly nested, but allows
//invalid JavaScript such as the contents of `attributes`
var res = characterParser(exp)
if (res.isNesting()) {
throw new Error('Nesting must match on expression `' + exp + '`')
}
}
/**
* Lexer prototype.
*/
Lexer.prototype = {
/**
* Construct a token with the given `type` and `val`.
*
* @param {String} type
* @param {String} val
* @return {Object}
* @api private
*/
tok: function(type, val){
return {
type: type
, line: this.lineno
, val: val
}
},
/**
* Consume the given `len` of input.
*
* @param {Number} len
* @api private
*/
consume: function(len){
this.input = this.input.substr(len);
},
/**
* Scan for `type` with the given `regexp`.
*
* @param {String} type
* @param {RegExp} regexp
* @return {Object}
* @api private
*/
scan: function(regexp, type){
var captures;
if (captures = regexp.exec(this.input)) {
this.consume(captures[0].length);
return this.tok(type, captures[1]);
}
},
/**
* Defer the given `tok`.
*
* @param {Object} tok
* @api private
*/
defer: function(tok){
this.deferredTokens.push(tok);
},
/**
* Lookahead `n` tokens.
*
* @param {Number} n
* @return {Object}
* @api private
*/
lookahead: function(n){
var fetch = n - this.stash.length;
while (fetch-- > 0) this.stash.push(this.next());
return this.stash[--n];
},
/**
* Return the indexOf `(` or `{` or `[` / `)` or `}` or `]` delimiters.
*
* @return {Number}
* @api private
*/
bracketExpression: function(skip){
skip = skip || 0;
var start = this.input[skip];
if (start != '(' && start != '{' && start != '[') throw new Error('unrecognized start character');
var end = ({'(': ')', '{': '}', '[': ']'})[start];
var range = characterParser.parseMax(this.input, {start: skip + 1});
if (this.input[range.end] !== end) throw new Error('start character ' + start + ' does not match end character ' + this.input[range.end]);
return range;
},
/**
* Stashed token.
*/
stashed: function() {
return this.stash.length
&& this.stash.shift();
},
/**
* Deferred token.
*/
deferred: function() {
return this.deferredTokens.length
&& this.deferredTokens.shift();
},
/**
* end-of-source.
*/
eos: function() {
if (this.input.length) return;
if (this.indentStack.length) {
this.indentStack.shift();
return this.tok('outdent');
} else {
return this.tok('eos');
}
},
/**
* Blank line.
*/
blank: function() {
var captures;
if (captures = /^\n *\n/.exec(this.input)) {
this.consume(captures[0].length - 1);
++this.lineno;
if (this.pipeless) return this.tok('text', '');
return this.next();
}
},
/**
* Comment.
*/
comment: function() {
var captures;
if (captures = /^\/\/(-)?([^\n]*)/.exec(this.input)) {
this.consume(captures[0].length);
var tok = this.tok('comment', captures[2]);
tok.buffer = '-' != captures[1];
this.pipeless = true;
return tok;
}
},
/**
* Interpolated tag.
*/
interpolation: function() {
if (/^#\{/.test(this.input)) {
var match = this.bracketExpression(1);
this.consume(match.end + 1);
return this.tok('interpolation', match.src);
}
},
/**
* Tag.
*/
tag: function() {
var captures;
if (captures = /^(\w[-:\w]*)(\/?)/.exec(this.input)) {
this.consume(captures[0].length);
var tok, name = captures[1];
if (':' == name[name.length - 1]) {
name = name.slice(0, -1);
tok = this.tok('tag', name);
this.defer(this.tok(':'));
if (this.input[0] !== ' ') {
console.warn('Warning: space required after `:` on line ' + this.lineno +
' of jade file "' + this.filename + '"');
}
while (' ' == this.input[0]) this.input = this.input.substr(1);
} else {
tok = this.tok('tag', name);
}
tok.selfClosing = !!captures[2];
return tok;
}
},
/**
* Filter.
*/
filter: function() {
var tok = this.scan(/^:([\w\-]+)/, 'filter');
if (tok) {
this.pipeless = true;
return tok;
}
},
/**
* Doctype.
*/
doctype: function() {
if (this.scan(/^!!! *([^\n]+)?/, 'doctype')) {
throw new Error('`!!!` is deprecated, you must now use `doctype`');
}
var node = this.scan(/^(?:doctype) *([^\n]+)?/, 'doctype');
if (node && node.val && node.val.trim() === '5') {
throw new Error('`doctype 5` is deprecated, you must now use `doctype html`');
}
return node;
},
/**
* Id.
*/
id: function() {
return this.scan(/^#([\w-]+)/, 'id');
},
/**
* Class.
*/
className: function() {
return this.scan(/^\.([\w-]+)/, 'class');
},
/**
* Text.
*/
text: function() {
return this.scan(/^(?:\| ?| )([^\n]+)/, 'text') ||
this.scan(/^\|?( )/, 'text') ||
this.scan(/^(<[^\n]*)/, 'text');
},
textFail: function () {
var tok;
if (tok = this.scan(/^([^\.\n][^\n]+)/, 'text')) {
console.warn('Warning: missing space before text for line ' + this.lineno +
' of jade file "' + this.filename + '"');
return tok;
}
},
/**
* Dot.
*/
dot: function() {
var match;
if (match = this.scan(/^\./, 'dot')) {
this.pipeless = true;
return match;
}
},
/**
* Extends.
*/
"extends": function() {
return this.scan(/^extends? +([^\n]+)/, 'extends');
},
/**
* Block prepend.
*/
prepend: function() {
var captures;
if (captures = /^prepend +([^\n]+)/.exec(this.input)) {
this.consume(captures[0].length);
var mode = 'prepend'
, name = captures[1]
, tok = this.tok('block', name);
tok.mode = mode;
return tok;
}
},
/**
* Block append.
*/
append: function() {
var captures;
if (captures = /^append +([^\n]+)/.exec(this.input)) {
this.consume(captures[0].length);
var mode = 'append'
, name = captures[1]
, tok = this.tok('block', name);
tok.mode = mode;
return tok;
}
},
/**
* Block.
*/
block: function() {
var captures;
if (captures = /^block\b *(?:(prepend|append) +)?([^\n]+)/.exec(this.input)) {
this.consume(captures[0].length);
var mode = captures[1] || 'replace'
, name = captures[2]
, tok = this.tok('block', name);
tok.mode = mode;
return tok;
}
},
/**
* Mixin Block.
*/
mixinBlock: function() {
var captures;
if (captures = /^block[ \t]*(\n|$)/.exec(this.input)) {
this.consume(captures[0].length - captures[1].length);
return this.tok('mixin-block');
}
},
/**
* Yield.
*/
'yield': function() {
return this.scan(/^yield */, 'yield');
},
/**
* Include.
*/
include: function() {
return this.scan(/^include +([^\n]+)/, 'include');
},
/**
* Include with filter
*/
includeFiltered: function() {
var captures;
if (captures = /^include:([\w\-]+)([\( ])/.exec(this.input)) {
this.consume(captures[0].length - 1);
var filter = captures[1];
var attrs = captures[2] === '(' ? this.attrs() : null;
if (!(captures[2] === ' ' || this.input[0] === ' ')) {
throw new Error('expected space after include:filter but got ' + utils.stringify(this.input[0]));
}
captures = /^ *([^\n]+)/.exec(this.input);
if (!captures || captures[1].trim() === '') {
throw new Error('missing path for include:filter');
}
this.consume(captures[0].length);
var path = captures[1];
var tok = this.tok('include', path);
tok.filter = filter;
tok.attrs = attrs;
return tok;
}
},
/**
* Case.
*/
"case": function() {
return this.scan(/^case +([^\n]+)/, 'case');
},
/**
* When.
*/
when: function() {
return this.scan(/^when +([^:\n]+)/, 'when');
},
/**
* Default.
*/
"default": function() {
return this.scan(/^default */, 'default');
},
/**
* Call mixin.
*/
call: function(){
var tok, captures;
if (captures = /^\+(\s*)(([-\w]+)|(#\{))/.exec(this.input)) {
// try to consume simple or interpolated call
if (captures[3]) {
// simple call
this.consume(captures[0].length);
tok = this.tok('call', captures[3]);
} else {
// interpolated call
var match = this.bracketExpression(2 + captures[1].length);
this.consume(match.end + 1);
assertExpression(match.src);
tok = this.tok('call', '#{'+match.src+'}');
}
// Check for args (not attributes)
if (captures = /^ *\(/.exec(this.input)) {
var range = this.bracketExpression(captures[0].length - 1);
if (!/^\s*[-\w]+ *=/.test(range.src)) { // not attributes
this.consume(range.end + 1);
tok.args = range.src;
}
if (tok.args) {
assertExpression('[' + tok.args + ']');
}
}
return tok;
}
},
/**
* Mixin.
*/
mixin: function(){
var captures;
if (captures = /^mixin +([-\w]+)(?: *\((.*)\))? */.exec(this.input)) {
this.consume(captures[0].length);
var tok = this.tok('mixin', captures[1]);
tok.args = captures[2];
return tok;
}
},
/**
* Conditional.
*/
conditional: function() {
var captures;
if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) {
this.consume(captures[0].length);
var type = captures[1]
var js = captures[2];
var isIf = false;
var isElse = false;
switch (type) {
case 'if':
assertExpression(js)
js = 'if (' + js + ')';
isIf = true;
break;
case 'unless':
assertExpression(js)
js = 'if (!(' + js + '))';
isIf = true;
break;
case 'else if':
assertExpression(js)
js = 'else if (' + js + ')';
isIf = true;
isElse = true;
break;
case 'else':
if (js && js.trim()) {
throw new Error('`else` cannot have a condition, perhaps you meant `else if`');
}
js = 'else';
isElse = true;
break;
}
var tok = this.tok('code', js);
tok.isElse = isElse;
tok.isIf = isIf;
tok.requiresBlock = true;
return tok;
}
},
/**
* While.
*/
"while": function() {
var captures;
if (captures = /^while +([^\n]+)/.exec(this.input)) {
this.consume(captures[0].length);
assertExpression(captures[1])
var tok = this.tok('code', 'while (' + captures[1] + ')');
tok.requiresBlock = true;
return tok;
}
},
/**
* Each.
*/
each: function() {
var captures;
if (captures = /^(?:- *)?(?:each|for) +([a-zA-Z_$][\w$]*)(?: *, *([a-zA-Z_$][\w$]*))? * in *([^\n]+)/.exec(this.input)) {
this.consume(captures[0].length);
var tok = this.tok('each', captures[1]);
tok.key = captures[2] || '$index';
assertExpression(captures[3])
tok.code = captures[3];
return tok;
}
},
/**
* Code.
*/
code: function() {
var captures;
if (captures = /^(!?=|-)[ \t]*([^\n]+)/.exec(this.input)) {
this.consume(captures[0].length);
var flags = captures[1];
captures[1] = captures[2];
var tok = this.tok('code', captures[1]);
tok.escape = flags.charAt(0) === '=';
tok.buffer = flags.charAt(0) === '=' || flags.charAt(1) === '=';
if (tok.buffer) assertExpression(captures[1])
return tok;
}
},
/**
* Attributes.
*/
attrs: function() {
if ('(' == this.input.charAt(0)) {
var index = this.bracketExpression().end
, str = this.input.substr(1, index-1)
, tok = this.tok('attrs');
assertNestingCorrect(str);
var quote = '';
var interpolate = function (attr) {
return attr.replace(/(\\)?#\{(.+)/g, function(_, escape, expr){
if (escape) return _;
try {
var range = characterParser.parseMax(expr);
if (expr[range.end] !== '}') return _.substr(0, 2) + interpolate(_.substr(2));
assertExpression(range.src)
return quote + " + (" + range.src + ") + " + quote + interpolate(expr.substr(range.end + 1));
} catch (ex) {
return _.substr(0, 2) + interpolate(_.substr(2));
}
});
}
this.consume(index + 1);
tok.attrs = [];
var escapedAttr = true
var key = '';
var val = '';
var interpolatable = '';
var state = characterParser.defaultState();
var loc = 'key';
var isEndOfAttribute = function (i) {
if (key.trim() === '') return false;
if (i === str.length) return true;
if (loc === 'key') {
if (str[i] === ' ' || str[i] === '\n') {
for (var x = i; x < str.length; x++) {
if (str[x] != ' ' && str[x] != '\n') {
if (str[x] === '=' || str[x] === '!' || str[x] === ',') return false;
else return true;
}
}
}
return str[i] === ','
} else if (loc === 'value' && !state.isNesting()) {
try {
assertExpression(val);
if (str[i] === ' ' || str[i] === '\n') {
for (var x = i; x < str.length; x++) {
if (str[x] != ' ' && str[x] != '\n') {
if (characterParser.isPunctuator(str[x]) && str[x] != '"' && str[x] != "'") return false;
else return true;
}
}
}
return str[i] === ',';
} catch (ex) {
return false;
}
}
}
this.lineno += str.split("\n").length - 1;
for (var i = 0; i <= str.length; i++) {
if (isEndOfAttribute(i)) {
val = val.trim();
if (val) assertExpression(val)
key = key.trim();
key = key.replace(/^['"]|['"]$/g, '');
tok.attrs.push({
name: key,
val: '' == val ? true : val,
escaped: escapedAttr
});
key = val = '';
loc = 'key';
escapedAttr = false;
} else {
switch (loc) {
case 'key-char':
if (str[i] === quote) {
loc = 'key';
if (i + 1 < str.length && [' ', ',', '!', '=', '\n'].indexOf(str[i + 1]) === -1)
throw new Error('Unexpected character ' + str[i + 1] + ' expected ` `, `\\n`, `,`, `!` or `=`');
} else {
key += str[i];
}
break;
case 'key':
if (key === '' && (str[i] === '"' || str[i] === "'")) {
loc = 'key-char';
quote = str[i];
} else if (str[i] === '!' || str[i] === '=') {
escapedAttr = str[i] !== '!';
if (str[i] === '!') i++;
if (str[i] !== '=') throw new Error('Unexpected character ' + str[i] + ' expected `=`');
loc = 'value';
state = characterParser.defaultState();
} else {
key += str[i]
}
break;
case 'value':
state = characterParser.parseChar(str[i], state);
if (state.isString()) {
loc = 'string';
quote = str[i];
interpolatable = str[i];
} else {
val += str[i];
}
break;
case 'string':
state = characterParser.parseChar(str[i], state);
interpolatable += str[i];
if (!state.isString()) {
loc = 'value';
val += interpolate(interpolatable);
}
break;
}
}
}
if ('/' == this.input.charAt(0)) {
this.consume(1);
tok.selfClosing = true;
}
return tok;
}
},
/**
* &attributes block
*/
attributesBlock: function () {
var captures;
if (/^&attributes\b/.test(this.input)) {
this.consume(11);
var args = this.bracketExpression();
this.consume(args.end + 1);
return this.tok('&attributes', args.src);
}
},
/**
* Indent | Outdent | Newline.
*/
indent: function() {
var captures, re;
// established regexp
if (this.indentRe) {
captures = this.indentRe.exec(this.input);
// determine regexp
} else {
// tabs
re = /^\n(\t*) */;
captures = re.exec(this.input);
// spaces
if (captures && !captures[1].length) {
re = /^\n( *)/;
captures = re.exec(this.input);
}
// established
if (captures && captures[1].length) this.indentRe = re;
}
if (captures) {
var tok
, indents = captures[1].length;
++this.lineno;
this.consume(indents + 1);
if (' ' == this.input[0] || '\t' == this.input[0]) {
throw new Error('Invalid indentation, you can use tabs or spaces but not both');
}
// blank line
if ('\n' == this.input[0]) {
this.pipeless = false;
return this.tok('newline');
}
// outdent
if (this.indentStack.length && indents < this.indentStack[0]) {
while (this.indentStack.length && this.indentStack[0] > indents) {
this.stash.push(this.tok('outdent'));
this.indentStack.shift();
}
tok = this.stash.pop();
// indent
} else if (indents && indents != this.indentStack[0]) {
this.indentStack.unshift(indents);
tok = this.tok('indent', indents);
// newline
} else {
tok = this.tok('newline');
}
this.pipeless = false;
return tok;
}
},
/**
* Pipe-less text consumed only when
* pipeless is true;
*/
pipelessText: function() {
if (!this.pipeless) return;
var captures, re;
// established regexp
if (this.indentRe) {
captures = this.indentRe.exec(this.input);
// determine regexp
} else {
// tabs
re = /^\n(\t*) */;
captures = re.exec(this.input);
// spaces
if (captures && !captures[1].length) {
re = /^\n( *)/;
captures = re.exec(this.input);
}
// established
if (captures && captures[1].length) this.indentRe = re;
}
var indents = captures && captures[1].length;
if (indents && (this.indentStack.length === 0 || indents > this.indentStack[0])) {
var indent = captures[1];
var line;
var tokens = [];
var isMatch;
do {
// text has `\n` as a prefix
var i = this.input.substr(1).indexOf('\n');
if (-1 == i) i = this.input.length - 1;
var str = this.input.substr(1, i);
isMatch = str.substr(0, indent.length) === indent || !str.trim();
if (isMatch) {
// consume test along with `\n` prefix if match
this.consume(str.length + 1);
tokens.push(str.substr(indent.length));
}
} while(this.input.length && isMatch);
while (this.input.length === 0 && tokens[tokens.length - 1] === '') tokens.pop();
return this.tok('pipeless-text', tokens);
}
},
/**
* ':'
*/
colon: function() {
var good = /^: +/.test(this.input);
var res = this.scan(/^: */, ':');
if (res && !good) {
console.warn('Warning: space required after `:` on line ' + this.lineno +
' of jade file "' + this.filename + '"');
}
return res;
},
fail: function () {
throw new Error('unexpected text ' + this.input.substr(0, 5));
},
/**
* Return the next token object, or those
* previously stashed by lookahead.
*
* @return {Object}
* @api private
*/
advance: function(){
return this.stashed()
|| this.next();
},
/**
* Return the next token object.
*
* @return {Object}
* @api private
*/
next: function() {
return this.deferred()
|| this.blank()
|| this.eos()
|| this.pipelessText()
|| this.yield()
|| this.doctype()
|| this.interpolation()
|| this["case"]()
|| this.when()
|| this["default"]()
|| this["extends"]()
|| this.append()
|| this.prepend()
|| this.block()
|| this.mixinBlock()
|| this.include()
|| this.includeFiltered()
|| this.mixin()
|| this.call()
|| this.conditional()
|| this.each()
|| this["while"]()
|| this.tag()
|| this.filter()
|| this.code()
|| this.id()
|| this.className()
|| this.attrs()
|| this.attributesBlock()
|| this.indent()
|| this.text()
|| this.comment()
|| this.colon()
|| this.dot()
|| this.textFail()
|| this.fail();
}
};

83
node_modules/jade/lib/nodes/attrs.js generated vendored Normal file
View File

@@ -0,0 +1,83 @@
'use strict';
var Node = require('./node');
/**
* Initialize a `Attrs` node.
*
* @api public
*/
var Attrs = module.exports = function Attrs() {
this.attributeNames = [];
this.attrs = [];
this.attributeBlocks = [];
};
// Inherit from `Node`.
Attrs.prototype = Object.create(Node.prototype);
Attrs.prototype.constructor = Attrs;
Attrs.prototype.type = 'Attrs';
/**
* Set attribute `name` to `val`, keep in mind these become
* part of a raw js object literal, so to quote a value you must
* '"quote me"', otherwise or example 'user.name' is literal JavaScript.
*
* @param {String} name
* @param {String} val
* @param {Boolean} escaped
* @return {Tag} for chaining
* @api public
*/
Attrs.prototype.setAttribute = function(name, val, escaped){
if (name !== 'class' && this.attributeNames.indexOf(name) !== -1) {
throw new Error('Duplicate attribute "' + name + '" is not allowed.');
}
this.attributeNames.push(name);
this.attrs.push({ name: name, val: val, escaped: escaped });
return this;
};
/**
* Remove attribute `name` when present.
*
* @param {String} name
* @api public
*/
Attrs.prototype.removeAttribute = function(name){
var err = new Error('attrs.removeAttribute is deprecated and will be removed in v2.0.0');
console.warn(err.stack);
for (var i = 0, len = this.attrs.length; i < len; ++i) {
if (this.attrs[i] && this.attrs[i].name == name) {
delete this.attrs[i];
}
}
};
/**
* Get attribute value by `name`.
*
* @param {String} name
* @return {String}
* @api public
*/
Attrs.prototype.getAttribute = function(name){
var err = new Error('attrs.getAttribute is deprecated and will be removed in v2.0.0');
console.warn(err.stack);
for (var i = 0, len = this.attrs.length; i < len; ++i) {
if (this.attrs[i] && this.attrs[i].name == name) {
return this.attrs[i].val;
}
}
};
Attrs.prototype.addAttributes = function (src) {
this.attributeBlocks.push(src);
};

24
node_modules/jade/lib/nodes/block-comment.js generated vendored Normal file
View File

@@ -0,0 +1,24 @@
'use strict';
var Node = require('./node');
/**
* Initialize a `BlockComment` with the given `block`.
*
* @param {String} val
* @param {Block} block
* @param {Boolean} buffer
* @api public
*/
var BlockComment = module.exports = function BlockComment(val, block, buffer) {
this.block = block;
this.val = val;
this.buffer = buffer;
};
// Inherit from `Node`.
BlockComment.prototype = Object.create(Node.prototype);
BlockComment.prototype.constructor = BlockComment;
BlockComment.prototype.type = 'BlockComment';

118
node_modules/jade/lib/nodes/block.js generated vendored Normal file
View File

@@ -0,0 +1,118 @@
'use strict';
var Node = require('./node');
/**
* Initialize a new `Block` with an optional `node`.
*
* @param {Node} node
* @api public
*/
var Block = module.exports = function Block(node){
this.nodes = [];
if (node) this.push(node);
};
// Inherit from `Node`.
Block.prototype = Object.create(Node.prototype);
Block.prototype.constructor = Block;
Block.prototype.type = 'Block';
/**
* Block flag.
*/
Block.prototype.isBlock = true;
/**
* Replace the nodes in `other` with the nodes
* in `this` block.
*
* @param {Block} other
* @api private
*/
Block.prototype.replace = function(other){
var err = new Error('block.replace is deprecated and will be removed in v2.0.0');
console.warn(err.stack);
other.nodes = this.nodes;
};
/**
* Push the given `node`.
*
* @param {Node} node
* @return {Number}
* @api public
*/
Block.prototype.push = function(node){
return this.nodes.push(node);
};
/**
* Check if this block is empty.
*
* @return {Boolean}
* @api public
*/
Block.prototype.isEmpty = function(){
return 0 == this.nodes.length;
};
/**
* Unshift the given `node`.
*
* @param {Node} node
* @return {Number}
* @api public
*/
Block.prototype.unshift = function(node){
return this.nodes.unshift(node);
};
/**
* Return the "last" block, or the first `yield` node.
*
* @return {Block}
* @api private
*/
Block.prototype.includeBlock = function(){
var ret = this
, node;
for (var i = 0, len = this.nodes.length; i < len; ++i) {
node = this.nodes[i];
if (node.yield) return node;
else if (node.textOnly) continue;
else if (node.includeBlock) ret = node.includeBlock();
else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock();
if (ret.yield) return ret;
}
return ret;
};
/**
* Return a clone of this block.
*
* @return {Block}
* @api private
*/
Block.prototype.clone = function(){
var err = new Error('block.clone is deprecated and will be removed in v2.0.0');
console.warn(err.stack);
var clone = new Block;
for (var i = 0, len = this.nodes.length; i < len; ++i) {
clone.push(this.nodes[i].clone());
}
return clone;
};

33
node_modules/jade/lib/nodes/case.js generated vendored Normal file
View File

@@ -0,0 +1,33 @@
'use strict';
var Node = require('./node');
/**
* Initialize a new `Case` with `expr`.
*
* @param {String} expr
* @api public
*/
var Case = exports = module.exports = function Case(expr, block){
this.expr = expr;
this.block = block;
};
// Inherit from `Node`.
Case.prototype = Object.create(Node.prototype);
Case.prototype.constructor = Case;
Case.prototype.type = 'Case';
var When = exports.When = function When(expr, block){
this.expr = expr;
this.block = block;
this.debug = false;
};
// Inherit from `Node`.
When.prototype = Object.create(Node.prototype);
When.prototype.constructor = When;
When.prototype.type = 'When';

26
node_modules/jade/lib/nodes/code.js generated vendored Normal file
View File

@@ -0,0 +1,26 @@
'use strict';
var Node = require('./node');
/**
* Initialize a `Code` node with the given code `val`.
* Code may also be optionally buffered and escaped.
*
* @param {String} val
* @param {Boolean} buffer
* @param {Boolean} escape
* @api public
*/
var Code = module.exports = function Code(val, buffer, escape) {
this.val = val;
this.buffer = buffer;
this.escape = escape;
if (val.match(/^ *else/)) this.debug = false;
};
// Inherit from `Node`.
Code.prototype = Object.create(Node.prototype);
Code.prototype.constructor = Code;
Code.prototype.type = 'Code'; // prevent the minifiers removing this

23
node_modules/jade/lib/nodes/comment.js generated vendored Normal file
View File

@@ -0,0 +1,23 @@
'use strict';
var Node = require('./node');
/**
* Initialize a `Comment` with the given `val`, optionally `buffer`,
* otherwise the comment may render in the output.
*
* @param {String} val
* @param {Boolean} buffer
* @api public
*/
var Comment = module.exports = function Comment(val, buffer) {
this.val = val;
this.buffer = buffer;
};
// Inherit from `Node`.
Comment.prototype = Object.create(Node.prototype);
Comment.prototype.constructor = Comment;
Comment.prototype.type = 'Comment';

20
node_modules/jade/lib/nodes/doctype.js generated vendored Normal file
View File

@@ -0,0 +1,20 @@
'use strict';
var Node = require('./node');
/**
* Initialize a `Doctype` with the given `val`.
*
* @param {String} val
* @api public
*/
var Doctype = module.exports = function Doctype(val) {
this.val = val;
};
// Inherit from `Node`.
Doctype.prototype = Object.create(Node.prototype);
Doctype.prototype.constructor = Doctype;
Doctype.prototype.type = 'Doctype';

26
node_modules/jade/lib/nodes/each.js generated vendored Normal file
View File

@@ -0,0 +1,26 @@
'use strict';
var Node = require('./node');
/**
* Initialize an `Each` node, representing iteration
*
* @param {String} obj
* @param {String} val
* @param {String} key
* @param {Block} block
* @api public
*/
var Each = module.exports = function Each(obj, val, key, block) {
this.obj = obj;
this.val = val;
this.key = key;
this.block = block;
};
// Inherit from `Node`.
Each.prototype = Object.create(Node.prototype);
Each.prototype.constructor = Each;
Each.prototype.type = 'Each';

24
node_modules/jade/lib/nodes/filter.js generated vendored Normal file
View File

@@ -0,0 +1,24 @@
'use strict';
var Node = require('./node');
/**
* Initialize a `Filter` node with the given
* filter `name` and `block`.
*
* @param {String} name
* @param {Block|Node} block
* @api public
*/
var Filter = module.exports = function Filter(name, block, attrs) {
this.name = name;
this.block = block;
this.attrs = attrs;
};
// Inherit from `Node`.
Filter.prototype = Object.create(Node.prototype);
Filter.prototype.constructor = Filter;
Filter.prototype.type = 'Filter';

16
node_modules/jade/lib/nodes/index.js generated vendored Normal file
View File

@@ -0,0 +1,16 @@
'use strict';
exports.Node = require('./node');
exports.Tag = require('./tag');
exports.Code = require('./code');
exports.Each = require('./each');
exports.Case = require('./case');
exports.Text = require('./text');
exports.Block = require('./block');
exports.MixinBlock = require('./mixin-block');
exports.Mixin = require('./mixin');
exports.Filter = require('./filter');
exports.Comment = require('./comment');
exports.Literal = require('./literal');
exports.BlockComment = require('./block-comment');
exports.Doctype = require('./doctype');

20
node_modules/jade/lib/nodes/literal.js generated vendored Normal file
View File

@@ -0,0 +1,20 @@
'use strict';
var Node = require('./node');
/**
* Initialize a `Literal` node with the given `str.
*
* @param {String} str
* @api public
*/
var Literal = module.exports = function Literal(str) {
this.str = str;
};
// Inherit from `Node`.
Literal.prototype = Object.create(Node.prototype);
Literal.prototype.constructor = Literal;
Literal.prototype.type = 'Literal';

18
node_modules/jade/lib/nodes/mixin-block.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
'use strict';
var Node = require('./node');
/**
* Initialize a new `Block` with an optional `node`.
*
* @param {Node} node
* @api public
*/
var MixinBlock = module.exports = function MixinBlock(){};
// Inherit from `Node`.
MixinBlock.prototype = Object.create(Node.prototype);
MixinBlock.prototype.constructor = MixinBlock;
MixinBlock.prototype.type = 'MixinBlock';

26
node_modules/jade/lib/nodes/mixin.js generated vendored Normal file
View File

@@ -0,0 +1,26 @@
'use strict';
var Attrs = require('./attrs');
/**
* Initialize a new `Mixin` with `name` and `block`.
*
* @param {String} name
* @param {String} args
* @param {Block} block
* @api public
*/
var Mixin = module.exports = function Mixin(name, args, block, call){
Attrs.call(this);
this.name = name;
this.args = args;
this.block = block;
this.call = call;
};
// Inherit from `Attrs`.
Mixin.prototype = Object.create(Attrs.prototype);
Mixin.prototype.constructor = Mixin;
Mixin.prototype.type = 'Mixin';

18
node_modules/jade/lib/nodes/node.js generated vendored Normal file
View File

@@ -0,0 +1,18 @@
'use strict';
var Node = module.exports = function Node(){};
/**
* Clone this node (return itself)
*
* @return {Node}
* @api private
*/
Node.prototype.clone = function(){
var err = new Error('node.clone is deprecated and will be removed in v2.0.0');
console.warn(err.stack);
return this;
};
Node.prototype.type = '';

89
node_modules/jade/lib/nodes/tag.js generated vendored Normal file
View File

@@ -0,0 +1,89 @@
'use strict';
var Attrs = require('./attrs');
var Block = require('./block');
var inlineTags = require('../inline-tags');
/**
* Initialize a `Tag` node with the given tag `name` and optional `block`.
*
* @param {String} name
* @param {Block} block
* @api public
*/
var Tag = module.exports = function Tag(name, block) {
Attrs.call(this);
this.name = name;
this.block = block || new Block;
};
// Inherit from `Attrs`.
Tag.prototype = Object.create(Attrs.prototype);
Tag.prototype.constructor = Tag;
Tag.prototype.type = 'Tag';
/**
* Clone this tag.
*
* @return {Tag}
* @api private
*/
Tag.prototype.clone = function(){
var err = new Error('tag.clone is deprecated and will be removed in v2.0.0');
console.warn(err.stack);
var clone = new Tag(this.name, this.block.clone());
clone.line = this.line;
clone.attrs = this.attrs;
clone.textOnly = this.textOnly;
return clone;
};
/**
* Check if this tag is an inline tag.
*
* @return {Boolean}
* @api private
*/
Tag.prototype.isInline = function(){
return ~inlineTags.indexOf(this.name);
};
/**
* Check if this tag's contents can be inlined. Used for pretty printing.
*
* @return {Boolean}
* @api private
*/
Tag.prototype.canInline = function(){
var nodes = this.block.nodes;
function isInline(node){
// Recurse if the node is a block
if (node.isBlock) return node.nodes.every(isInline);
return node.isText || (node.isInline && node.isInline());
}
// Empty tag
if (!nodes.length) return true;
// Text-only or inline-only tag
if (1 == nodes.length) return isInline(nodes[0]);
// Multi-line inline-only tag
if (this.block.nodes.every(isInline)) {
for (var i = 1, len = nodes.length; i < len; ++i) {
if (nodes[i-1].isText && nodes[i].isText)
return false;
}
return true;
}
// Mixed tag
return false;
};

26
node_modules/jade/lib/nodes/text.js generated vendored Normal file
View File

@@ -0,0 +1,26 @@
'use strict';
var Node = require('./node');
/**
* Initialize a `Text` node with optional `line`.
*
* @param {String} line
* @api public
*/
var Text = module.exports = function Text(line) {
this.val = line;
};
// Inherit from `Node`.
Text.prototype = Object.create(Node.prototype);
Text.prototype.constructor = Text;
Text.prototype.type = 'Text';
/**
* Flag as text.
*/
Text.prototype.isText = true;

825
node_modules/jade/lib/parser.js generated vendored Normal file
View File

@@ -0,0 +1,825 @@
'use strict';
var Lexer = require('./lexer');
var nodes = require('./nodes');
var utils = require('./utils');
var filters = require('./filters');
var path = require('path');
var constantinople = require('constantinople');
var parseJSExpression = require('character-parser').parseMax;
var extname = path.extname;
/**
* Initialize `Parser` with the given input `str` and `filename`.
*
* @param {String} str
* @param {String} filename
* @param {Object} options
* @api public
*/
var Parser = exports = module.exports = function Parser(str, filename, options){
//Strip any UTF-8 BOM off of the start of `str`, if it exists.
this.input = str.replace(/^\uFEFF/, '');
this.lexer = new Lexer(this.input, filename);
this.filename = filename;
this.blocks = {};
this.mixins = {};
this.options = options;
this.contexts = [this];
this.inMixin = 0;
this.dependencies = [];
this.inBlock = 0;
};
/**
* Parser prototype.
*/
Parser.prototype = {
/**
* Save original constructor
*/
constructor: Parser,
/**
* Push `parser` onto the context stack,
* or pop and return a `Parser`.
*/
context: function(parser){
if (parser) {
this.contexts.push(parser);
} else {
return this.contexts.pop();
}
},
/**
* Return the next token object.
*
* @return {Object}
* @api private
*/
advance: function(){
return this.lexer.advance();
},
/**
* Single token lookahead.
*
* @return {Object}
* @api private
*/
peek: function() {
return this.lookahead(1);
},
/**
* Return lexer lineno.
*
* @return {Number}
* @api private
*/
line: function() {
return this.lexer.lineno;
},
/**
* `n` token lookahead.
*
* @param {Number} n
* @return {Object}
* @api private
*/
lookahead: function(n){
return this.lexer.lookahead(n);
},
/**
* Parse input returning a string of js for evaluation.
*
* @return {String}
* @api public
*/
parse: function(){
var block = new nodes.Block, parser;
block.line = 0;
block.filename = this.filename;
while ('eos' != this.peek().type) {
if ('newline' == this.peek().type) {
this.advance();
} else {
var next = this.peek();
var expr = this.parseExpr();
expr.filename = expr.filename || this.filename;
expr.line = next.line;
block.push(expr);
}
}
if (parser = this.extending) {
this.context(parser);
var ast = parser.parse();
this.context();
// hoist mixins
for (var name in this.mixins)
ast.unshift(this.mixins[name]);
return ast;
}
if (!this.extending && !this.included && Object.keys(this.blocks).length){
var blocks = [];
utils.walkAST(block, function (node) {
if (node.type === 'Block' && node.name) {
blocks.push(node.name);
}
});
Object.keys(this.blocks).forEach(function (name) {
if (blocks.indexOf(name) === -1 && !this.blocks[name].isSubBlock) {
console.warn('Warning: Unexpected block "'
+ name
+ '" '
+ ' on line '
+ this.blocks[name].line
+ ' of '
+ (this.blocks[name].filename)
+ '. This block is never used. This warning will be an error in v2.0.0');
}
}.bind(this));
}
return block;
},
/**
* Expect the given type, or throw an exception.
*
* @param {String} type
* @api private
*/
expect: function(type){
if (this.peek().type === type) {
return this.advance();
} else {
throw new Error('expected "' + type + '", but got "' + this.peek().type + '"');
}
},
/**
* Accept the given `type`.
*
* @param {String} type
* @api private
*/
accept: function(type){
if (this.peek().type === type) {
return this.advance();
}
},
/**
* tag
* | doctype
* | mixin
* | include
* | filter
* | comment
* | text
* | each
* | code
* | yield
* | id
* | class
* | interpolation
*/
parseExpr: function(){
switch (this.peek().type) {
case 'tag':
return this.parseTag();
case 'mixin':
return this.parseMixin();
case 'block':
return this.parseBlock();
case 'mixin-block':
return this.parseMixinBlock();
case 'case':
return this.parseCase();
case 'extends':
return this.parseExtends();
case 'include':
return this.parseInclude();
case 'doctype':
return this.parseDoctype();
case 'filter':
return this.parseFilter();
case 'comment':
return this.parseComment();
case 'text':
return this.parseText();
case 'each':
return this.parseEach();
case 'code':
return this.parseCode();
case 'call':
return this.parseCall();
case 'interpolation':
return this.parseInterpolation();
case 'yield':
this.advance();
var block = new nodes.Block;
block.yield = true;
return block;
case 'id':
case 'class':
var tok = this.advance();
this.lexer.defer(this.lexer.tok('tag', 'div'));
this.lexer.defer(tok);
return this.parseExpr();
default:
throw new Error('unexpected token "' + this.peek().type + '"');
}
},
/**
* Text
*/
parseText: function(){
var tok = this.expect('text');
var tokens = this.parseInlineTagsInText(tok.val);
if (tokens.length === 1) return tokens[0];
var node = new nodes.Block;
for (var i = 0; i < tokens.length; i++) {
node.push(tokens[i]);
};
return node;
},
/**
* ':' expr
* | block
*/
parseBlockExpansion: function(){
if (':' == this.peek().type) {
this.advance();
return new nodes.Block(this.parseExpr());
} else {
return this.block();
}
},
/**
* case
*/
parseCase: function(){
var val = this.expect('case').val;
var node = new nodes.Case(val);
node.line = this.line();
var block = new nodes.Block;
block.line = this.line();
block.filename = this.filename;
this.expect('indent');
while ('outdent' != this.peek().type) {
switch (this.peek().type) {
case 'comment':
case 'newline':
this.advance();
break;
case 'when':
block.push(this.parseWhen());
break;
case 'default':
block.push(this.parseDefault());
break;
default:
throw new Error('Unexpected token "' + this.peek().type
+ '", expected "when", "default" or "newline"');
}
}
this.expect('outdent');
node.block = block;
return node;
},
/**
* when
*/
parseWhen: function(){
var val = this.expect('when').val;
if (this.peek().type !== 'newline')
return new nodes.Case.When(val, this.parseBlockExpansion());
else
return new nodes.Case.When(val);
},
/**
* default
*/
parseDefault: function(){
this.expect('default');
return new nodes.Case.When('default', this.parseBlockExpansion());
},
/**
* code
*/
parseCode: function(afterIf){
var tok = this.expect('code');
var node = new nodes.Code(tok.val, tok.buffer, tok.escape);
var block;
node.line = this.line();
// throw an error if an else does not have an if
if (tok.isElse && !tok.hasIf) {
throw new Error('Unexpected else without if');
}
// handle block
block = 'indent' == this.peek().type;
if (block) {
node.block = this.block();
}
// handle missing block
if (tok.requiresBlock && !block) {
node.block = new nodes.Block();
}
// mark presense of if for future elses
if (tok.isIf && this.peek().isElse) {
this.peek().hasIf = true;
} else if (tok.isIf && this.peek().type === 'newline' && this.lookahead(2).isElse) {
this.lookahead(2).hasIf = true;
}
return node;
},
/**
* comment
*/
parseComment: function(){
var tok = this.expect('comment');
var node;
var block;
if (block = this.parseTextBlock()) {
node = new nodes.BlockComment(tok.val, block, tok.buffer);
} else {
node = new nodes.Comment(tok.val, tok.buffer);
}
node.line = this.line();
return node;
},
/**
* doctype
*/
parseDoctype: function(){
var tok = this.expect('doctype');
var node = new nodes.Doctype(tok.val);
node.line = this.line();
return node;
},
/**
* filter attrs? text-block
*/
parseFilter: function(){
var tok = this.expect('filter');
var attrs = this.accept('attrs');
var block;
block = this.parseTextBlock() || new nodes.Block();
var options = {};
if (attrs) {
attrs.attrs.forEach(function (attribute) {
options[attribute.name] = constantinople.toConstant(attribute.val);
});
}
var node = new nodes.Filter(tok.val, block, options);
node.line = this.line();
return node;
},
/**
* each block
*/
parseEach: function(){
var tok = this.expect('each');
var node = new nodes.Each(tok.code, tok.val, tok.key);
node.line = this.line();
node.block = this.block();
if (this.peek().type == 'code' && this.peek().val == 'else') {
this.advance();
node.alternative = this.block();
}
return node;
},
/**
* Resolves a path relative to the template for use in
* includes and extends
*
* @param {String} path
* @param {String} purpose Used in error messages.
* @return {String}
* @api private
*/
resolvePath: function (path, purpose) {
var p = require('path');
var dirname = p.dirname;
var basename = p.basename;
var join = p.join;
if (path[0] !== '/' && !this.filename)
throw new Error('the "filename" option is required to use "' + purpose + '" with "relative" paths');
if (path[0] === '/' && !this.options.basedir)
throw new Error('the "basedir" option is required to use "' + purpose + '" with "absolute" paths');
path = join(path[0] === '/' ? this.options.basedir : dirname(this.filename), path);
if (basename(path).indexOf('.') === -1) path += '.jade';
return path;
},
/**
* 'extends' name
*/
parseExtends: function(){
var fs = require('fs');
var path = this.resolvePath(this.expect('extends').val.trim(), 'extends');
if ('.jade' != path.substr(-5)) path += '.jade';
this.dependencies.push(path);
var str = fs.readFileSync(path, 'utf8');
var parser = new this.constructor(str, path, this.options);
parser.dependencies = this.dependencies;
parser.blocks = this.blocks;
parser.included = this.included;
parser.contexts = this.contexts;
this.extending = parser;
// TODO: null node
return new nodes.Literal('');
},
/**
* 'block' name block
*/
parseBlock: function(){
var block = this.expect('block');
var mode = block.mode;
var name = block.val.trim();
var line = block.line;
this.inBlock++;
block = 'indent' == this.peek().type
? this.block()
: new nodes.Block(new nodes.Literal(''));
this.inBlock--;
block.name = name;
block.line = line;
var prev = this.blocks[name] || {prepended: [], appended: []}
if (prev.mode === 'replace') return this.blocks[name] = prev;
var allNodes = prev.prepended.concat(block.nodes).concat(prev.appended);
switch (mode) {
case 'append':
prev.appended = prev.parser === this ?
prev.appended.concat(block.nodes) :
block.nodes.concat(prev.appended);
break;
case 'prepend':
prev.prepended = prev.parser === this ?
block.nodes.concat(prev.prepended) :
prev.prepended.concat(block.nodes);
break;
}
block.nodes = allNodes;
block.appended = prev.appended;
block.prepended = prev.prepended;
block.mode = mode;
block.parser = this;
block.isSubBlock = this.inBlock > 0;
return this.blocks[name] = block;
},
parseMixinBlock: function () {
var block = this.expect('mixin-block');
if (!this.inMixin) {
throw new Error('Anonymous blocks are not allowed unless they are part of a mixin.');
}
return new nodes.MixinBlock();
},
/**
* include block?
*/
parseInclude: function(){
var fs = require('fs');
var tok = this.expect('include');
var path = this.resolvePath(tok.val.trim(), 'include');
this.dependencies.push(path);
// has-filter
if (tok.filter) {
var str = fs.readFileSync(path, 'utf8').replace(/\r/g, '');
var options = {filename: path};
if (tok.attrs) {
tok.attrs.attrs.forEach(function (attribute) {
options[attribute.name] = constantinople.toConstant(attribute.val);
});
}
str = filters(tok.filter, str, options);
return new nodes.Literal(str);
}
// non-jade
if ('.jade' != path.substr(-5)) {
var str = fs.readFileSync(path, 'utf8').replace(/\r/g, '');
return new nodes.Literal(str);
}
var str = fs.readFileSync(path, 'utf8');
var parser = new this.constructor(str, path, this.options);
parser.dependencies = this.dependencies;
parser.blocks = utils.merge({}, this.blocks);
parser.included = true;
parser.mixins = this.mixins;
this.context(parser);
var ast = parser.parse();
this.context();
ast.filename = path;
if ('indent' == this.peek().type) {
ast.includeBlock().push(this.block());
}
return ast;
},
/**
* call ident block
*/
parseCall: function(){
var tok = this.expect('call');
var name = tok.val;
var args = tok.args;
var mixin = new nodes.Mixin(name, args, new nodes.Block, true);
this.tag(mixin);
if (mixin.code) {
mixin.block.push(mixin.code);
mixin.code = null;
}
if (mixin.block.isEmpty()) mixin.block = null;
return mixin;
},
/**
* mixin block
*/
parseMixin: function(){
var tok = this.expect('mixin');
var name = tok.val;
var args = tok.args;
var mixin;
// definition
if ('indent' == this.peek().type) {
this.inMixin++;
mixin = new nodes.Mixin(name, args, this.block(), false);
this.mixins[name] = mixin;
this.inMixin--;
return mixin;
// call
} else {
return new nodes.Mixin(name, args, null, true);
}
},
parseInlineTagsInText: function (str) {
var line = this.line();
var match = /(\\)?#\[((?:.|\n)*)$/.exec(str);
if (match) {
if (match[1]) { // escape
var text = new nodes.Text(str.substr(0, match.index) + '#[');
text.line = line;
var rest = this.parseInlineTagsInText(match[2]);
if (rest[0].type === 'Text') {
text.val += rest[0].val;
rest.shift();
}
return [text].concat(rest);
} else {
var text = new nodes.Text(str.substr(0, match.index));
text.line = line;
var buffer = [text];
var rest = match[2];
var range = parseJSExpression(rest);
var inner = new Parser(range.src, this.filename, this.options);
buffer.push(inner.parse());
return buffer.concat(this.parseInlineTagsInText(rest.substr(range.end + 1)));
}
} else {
var text = new nodes.Text(str);
text.line = line;
return [text];
}
},
/**
* indent (text | newline)* outdent
*/
parseTextBlock: function(){
var block = new nodes.Block;
block.line = this.line();
var body = this.peek();
if (body.type !== 'pipeless-text') return;
this.advance();
block.nodes = body.val.reduce(function (accumulator, text) {
return accumulator.concat(this.parseInlineTagsInText(text));
}.bind(this), []);
return block;
},
/**
* indent expr* outdent
*/
block: function(){
var block = new nodes.Block;
block.line = this.line();
block.filename = this.filename;
this.expect('indent');
while ('outdent' != this.peek().type) {
if ('newline' == this.peek().type) {
this.advance();
} else {
var expr = this.parseExpr();
expr.filename = this.filename;
block.push(expr);
}
}
this.expect('outdent');
return block;
},
/**
* interpolation (attrs | class | id)* (text | code | ':')? newline* block?
*/
parseInterpolation: function(){
var tok = this.advance();
var tag = new nodes.Tag(tok.val);
tag.buffer = true;
return this.tag(tag);
},
/**
* tag (attrs | class | id)* (text | code | ':')? newline* block?
*/
parseTag: function(){
var tok = this.advance();
var tag = new nodes.Tag(tok.val);
tag.selfClosing = tok.selfClosing;
return this.tag(tag);
},
/**
* Parse tag.
*/
tag: function(tag){
tag.line = this.line();
var seenAttrs = false;
// (attrs | class | id)*
out:
while (true) {
switch (this.peek().type) {
case 'id':
case 'class':
var tok = this.advance();
tag.setAttribute(tok.type, "'" + tok.val + "'");
continue;
case 'attrs':
if (seenAttrs) {
console.warn(this.filename + ', line ' + this.peek().line + ':\nYou should not have jade tags with multiple attributes.');
}
seenAttrs = true;
var tok = this.advance();
var attrs = tok.attrs;
if (tok.selfClosing) tag.selfClosing = true;
for (var i = 0; i < attrs.length; i++) {
tag.setAttribute(attrs[i].name, attrs[i].val, attrs[i].escaped);
}
continue;
case '&attributes':
var tok = this.advance();
tag.addAttributes(tok.val);
break;
default:
break out;
}
}
// check immediate '.'
if ('dot' == this.peek().type) {
tag.textOnly = true;
this.advance();
}
// (text | code | ':')?
switch (this.peek().type) {
case 'text':
tag.block.push(this.parseText());
break;
case 'code':
tag.code = this.parseCode();
break;
case ':':
this.advance();
tag.block = new nodes.Block;
tag.block.push(this.parseExpr());
break;
case 'newline':
case 'indent':
case 'outdent':
case 'eos':
case 'pipeless-text':
break;
default:
throw new Error('Unexpected token `' + this.peek().type + '` expected `text`, `code`, `:`, `newline` or `eos`')
}
// newline*
while ('newline' == this.peek().type) this.advance();
// block?
if (tag.textOnly) {
tag.block = this.parseTextBlock() || new nodes.Block();
} else if ('indent' == this.peek().type) {
var block = this.block();
for (var i = 0, len = block.nodes.length; i < len; ++i) {
tag.block.push(block.nodes[i]);
}
}
return tag;
}
};

232
node_modules/jade/lib/runtime.js generated vendored Normal file
View File

@@ -0,0 +1,232 @@
'use strict';
/**
* Merge two attribute objects giving precedence
* to values in object `b`. Classes are special-cased
* allowing for arrays and merging/joining appropriately
* resulting in a string.
*
* @param {Object} a
* @param {Object} b
* @return {Object} a
* @api private
*/
exports.merge = function merge(a, b) {
if (arguments.length === 1) {
var attrs = a[0];
for (var i = 1; i < a.length; i++) {
attrs = merge(attrs, a[i]);
}
return attrs;
}
var ac = a['class'];
var bc = b['class'];
if (ac || bc) {
ac = ac || [];
bc = bc || [];
if (!Array.isArray(ac)) ac = [ac];
if (!Array.isArray(bc)) bc = [bc];
a['class'] = ac.concat(bc).filter(nulls);
}
for (var key in b) {
if (key != 'class') {
a[key] = b[key];
}
}
return a;
};
/**
* Filter null `val`s.
*
* @param {*} val
* @return {Boolean}
* @api private
*/
function nulls(val) {
return val != null && val !== '';
}
/**
* join array as classes.
*
* @param {*} val
* @return {String}
*/
exports.joinClasses = joinClasses;
function joinClasses(val) {
return (Array.isArray(val) ? val.map(joinClasses) :
(val && typeof val === 'object') ? Object.keys(val).filter(function (key) { return val[key]; }) :
[val]).filter(nulls).join(' ');
}
/**
* Render the given classes.
*
* @param {Array} classes
* @param {Array.<Boolean>} escaped
* @return {String}
*/
exports.cls = function cls(classes, escaped) {
var buf = [];
for (var i = 0; i < classes.length; i++) {
if (escaped && escaped[i]) {
buf.push(exports.escape(joinClasses([classes[i]])));
} else {
buf.push(joinClasses(classes[i]));
}
}
var text = joinClasses(buf);
if (text.length) {
return ' class="' + text + '"';
} else {
return '';
}
};
exports.style = function (val) {
if (val && typeof val === 'object') {
return Object.keys(val).map(function (style) {
return style + ':' + val[style];
}).join(';');
} else {
return val;
}
};
/**
* Render the given attribute.
*
* @param {String} key
* @param {String} val
* @param {Boolean} escaped
* @param {Boolean} terse
* @return {String}
*/
exports.attr = function attr(key, val, escaped, terse) {
if (key === 'style') {
val = exports.style(val);
}
if ('boolean' == typeof val || null == val) {
if (val) {
return ' ' + (terse ? key : key + '="' + key + '"');
} else {
return '';
}
} else if (0 == key.indexOf('data') && 'string' != typeof val) {
if (JSON.stringify(val).indexOf('&') !== -1) {
console.warn('Since Jade 2.0.0, ampersands (`&`) in data attributes ' +
'will be escaped to `&amp;`');
};
if (val && typeof val.toISOString === 'function') {
console.warn('Jade will eliminate the double quotes around dates in ' +
'ISO form after 2.0.0');
}
return ' ' + key + "='" + JSON.stringify(val).replace(/'/g, '&apos;') + "'";
} else if (escaped) {
if (val && typeof val.toISOString === 'function') {
console.warn('Jade will stringify dates in ISO form after 2.0.0');
}
return ' ' + key + '="' + exports.escape(val) + '"';
} else {
if (val && typeof val.toISOString === 'function') {
console.warn('Jade will stringify dates in ISO form after 2.0.0');
}
return ' ' + key + '="' + val + '"';
}
};
/**
* Render the given attributes object.
*
* @param {Object} obj
* @param {Object} escaped
* @return {String}
*/
exports.attrs = function attrs(obj, terse){
var buf = [];
var keys = Object.keys(obj);
if (keys.length) {
for (var i = 0; i < keys.length; ++i) {
var key = keys[i]
, val = obj[key];
if ('class' == key) {
if (val = joinClasses(val)) {
buf.push(' ' + key + '="' + val + '"');
}
} else {
buf.push(exports.attr(key, val, false, terse));
}
}
}
return buf.join('');
};
/**
* Escape the given string of `html`.
*
* @param {String} html
* @return {String}
* @api private
*/
exports.escape = function escape(html){
var result = String(html)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
if (result === '' + html) return html;
else return result;
};
/**
* Re-throw the given `err` in context to the
* the jade in `filename` at the given `lineno`.
*
* @param {Error} err
* @param {String} filename
* @param {String} lineno
* @api private
*/
exports.rethrow = function rethrow(err, filename, lineno, str){
if (!(err instanceof Error)) throw err;
if ((typeof window != 'undefined' || !filename) && !str) {
err.message += ' on line ' + lineno;
throw err;
}
try {
str = str || require('fs').readFileSync(filename, 'utf8')
} catch (ex) {
rethrow(err, null, lineno)
}
var context = 3
, lines = str.split('\n')
, start = Math.max(lineno - context, 0)
, end = Math.min(lines.length, lineno + context);
// Error context
var context = lines.slice(start, end).map(function(line, i){
var curr = i + start + 1;
return (curr == lineno ? ' > ' : ' ')
+ curr
+ '| '
+ line;
}).join('\n');
// Alter exception message
err.path = filename;
err.message = (filename || 'Jade') + ':' + lineno
+ '\n' + context + '\n\n' + err.message;
throw err;
};

53
node_modules/jade/lib/utils.js generated vendored Normal file
View File

@@ -0,0 +1,53 @@
'use strict';
/**
* Merge `b` into `a`.
*
* @param {Object} a
* @param {Object} b
* @return {Object}
* @api public
*/
exports.merge = function(a, b) {
for (var key in b) a[key] = b[key];
return a;
};
exports.stringify = function(str) {
return JSON.stringify(str)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
};
exports.walkAST = function walkAST(ast, before, after) {
before && before(ast);
switch (ast.type) {
case 'Block':
ast.nodes.forEach(function (node) {
walkAST(node, before, after);
});
break;
case 'Case':
case 'Each':
case 'Mixin':
case 'Tag':
case 'When':
case 'Code':
ast.block && walkAST(ast.block, before, after);
break;
case 'Attrs':
case 'BlockComment':
case 'Comment':
case 'Doctype':
case 'Filter':
case 'Literal':
case 'MixinBlock':
case 'Text':
break;
default:
throw new Error('Unexpected node type ' + ast.type);
break;
}
after && after(ast);
};

1
node_modules/jade/node_modules/.bin/mkdirp generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../mkdirp/bin/cmd.js

View File

@@ -0,0 +1,2 @@
test/
.travis.yml

View File

@@ -0,0 +1,19 @@
Copyright (c) 2013 Forbes Lindesay
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.

View File

@@ -0,0 +1,142 @@
# character-parser
Parse JavaScript one character at a time to look for snippets in Templates. This is not a validator, it's just designed to allow you to have sections of JavaScript delimited by brackets robustly.
[![Build Status](https://img.shields.io/travis/ForbesLindesay/character-parser/master.svg)](https://travis-ci.org/ForbesLindesay/character-parser)
## Installation
npm install character-parser
## Usage
Work out how much depth changes:
```js
var state = parse('foo(arg1, arg2, {\n foo: [a, b\n');
assert(state.roundDepth === 1);
assert(state.curlyDepth === 1);
assert(state.squareDepth === 1);
parse(' c, d]\n })', state);
assert(state.squareDepth === 0);
assert(state.curlyDepth === 0);
assert(state.roundDepth === 0);
```
### Bracketed Expressions
Find all the contents of a bracketed expression:
```js
var section = parser.parseMax('foo="(", bar="}") bing bong');
assert(section.start === 0);
assert(section.end === 16);//exclusive end of string
assert(section.src = 'foo="(", bar="}"');
var section = parser.parseMax('{foo="(", bar="}"} bing bong', {start: 1});
assert(section.start === 1);
assert(section.end === 17);//exclusive end of string
assert(section.src = 'foo="(", bar="}"');
```
The bracketed expression parsing simply parses up to but excluding the first unmatched closed bracket (`)`, `}`, `]`). It is clever enough to ignore brackets in comments or strings.
### Custom Delimited Expressions
Find code up to a custom delimiter:
```js
var section = parser.parseUntil('foo.bar("%>").baz%> bing bong', '%>');
assert(section.start === 0);
assert(section.end === 17);//exclusive end of string
assert(section.src = 'foo.bar("%>").baz');
var section = parser.parseUntil('<%foo.bar("%>").baz%> bing bong', '%>', {start: 2});
assert(section.start === 2);
assert(section.end === 19);//exclusive end of string
assert(section.src = 'foo.bar("%>").baz');
```
Delimiters are ignored if they are inside strings or comments.
## API
### parse(str, state = defaultState(), options = {start: 0, end: src.length})
Parse a string starting at the index start, and return the state after parsing that string.
If you want to parse one string in multiple sections you should keep passing the resulting state to the next parse operation.
Returns a `State` object.
### parseMax(src, options = {start: 0})
Parses the source until the first unmatched close bracket (any of `)`, `}`, `]`). It returns an object with the structure:
```js
{
start: 0,//index of first character of string
end: 13,//index of first character after the end of string
src: 'source string'
}
```
### parseUntil(src, delimiter, options = {start: 0, includeLineComment: false})
Parses the source until the first occurence of `delimiter` which is not in a string or a comment. If `includeLineComment` is `true`, it will still count if the delimiter occurs in a line comment, but not in a block comment. It returns an object with the structure:
```js
{
start: 0,//index of first character of string
end: 13,//index of first character after the end of string
src: 'source string'
}
```
### parseChar(character, state = defaultState())
Parses the single character and returns the state. See `parse` for the structure of the returned state object. N.B. character must be a single character not a multi character string.
### defaultState()
Get a default starting state.
### isPunctuator(character)
Returns `true` if `character` represents punctuation in JavaScript.
### isKeyword(name)
Returns `true` if `name` is a keyword in JavaScript.
## State
A state is an object with the following structure
```js
{
lineComment: false, //true if inside a line comment
blockComment: false, //true if inside a block comment
singleQuote: false, //true if inside a single quoted string
doubleQuote: false, //true if inside a double quoted string
regexp: false, //true if inside a regular expression
escaped: false, //true if in a string and the last character was an escape character
roundDepth: 0, //number of un-closed open `(` brackets
curlyDepth: 0, //number of un-closed open `{` brackets
squareDepth: 0 //number of un-closed open `[` brackets
}
```
It also has the following useful methods:
- `.isString()` returns `true` if the current location is inside a string.
- `.isComment()` returns `true` if the current location is inside a comment.
- `isNesting()` returns `true` if the current location is anything but at the top level, i.e. with no nesting.
## License
MIT

View File

@@ -0,0 +1,231 @@
exports = (module.exports = parse);
exports.parse = parse;
function parse(src, state, options) {
options = options || {};
state = state || exports.defaultState();
var start = options.start || 0;
var end = options.end || src.length;
var index = start;
while (index < end) {
if (state.roundDepth < 0 || state.curlyDepth < 0 || state.squareDepth < 0) {
throw new SyntaxError('Mismatched Bracket: ' + src[index - 1]);
}
exports.parseChar(src[index++], state);
}
return state;
}
exports.parseMax = parseMax;
function parseMax(src, options) {
options = options || {};
var start = options.start || 0;
var index = start;
var state = exports.defaultState();
while (state.roundDepth >= 0 && state.curlyDepth >= 0 && state.squareDepth >= 0) {
if (index >= src.length) {
throw new Error('The end of the string was reached with no closing bracket found.');
}
exports.parseChar(src[index++], state);
}
var end = index - 1;
return {
start: start,
end: end,
src: src.substring(start, end)
};
}
exports.parseUntil = parseUntil;
function parseUntil(src, delimiter, options) {
options = options || {};
var includeLineComment = options.includeLineComment || false;
var start = options.start || 0;
var index = start;
var state = exports.defaultState();
while (state.isString() || state.regexp || state.blockComment ||
(!includeLineComment && state.lineComment) || !startsWith(src, delimiter, index)) {
exports.parseChar(src[index++], state);
}
var end = index;
return {
start: start,
end: end,
src: src.substring(start, end)
};
}
exports.parseChar = parseChar;
function parseChar(character, state) {
if (character.length !== 1) throw new Error('Character must be a string of length 1');
state = state || exports.defaultState();
state.src = state.src || '';
state.src += character;
var wasComment = state.blockComment || state.lineComment;
var lastChar = state.history ? state.history[0] : '';
if (state.regexpStart) {
if (character === '/' || character == '*') {
state.regexp = false;
}
state.regexpStart = false;
}
if (state.lineComment) {
if (character === '\n') {
state.lineComment = false;
}
} else if (state.blockComment) {
if (state.lastChar === '*' && character === '/') {
state.blockComment = false;
}
} else if (state.singleQuote) {
if (character === '\'' && !state.escaped) {
state.singleQuote = false;
} else if (character === '\\' && !state.escaped) {
state.escaped = true;
} else {
state.escaped = false;
}
} else if (state.doubleQuote) {
if (character === '"' && !state.escaped) {
state.doubleQuote = false;
} else if (character === '\\' && !state.escaped) {
state.escaped = true;
} else {
state.escaped = false;
}
} else if (state.regexp) {
if (character === '/' && !state.escaped) {
state.regexp = false;
} else if (character === '\\' && !state.escaped) {
state.escaped = true;
} else {
state.escaped = false;
}
} else if (lastChar === '/' && character === '/') {
state.history = state.history.substr(1);
state.lineComment = true;
} else if (lastChar === '/' && character === '*') {
state.history = state.history.substr(1);
state.blockComment = true;
} else if (character === '/' && isRegexp(state.history)) {
state.regexp = true;
state.regexpStart = true;
} else if (character === '\'') {
state.singleQuote = true;
} else if (character === '"') {
state.doubleQuote = true;
} else if (character === '(') {
state.roundDepth++;
} else if (character === ')') {
state.roundDepth--;
} else if (character === '{') {
state.curlyDepth++;
} else if (character === '}') {
state.curlyDepth--;
} else if (character === '[') {
state.squareDepth++;
} else if (character === ']') {
state.squareDepth--;
}
if (!state.blockComment && !state.lineComment && !wasComment) state.history = character + state.history;
state.lastChar = character; // store last character for ending block comments
return state;
}
exports.defaultState = function () { return new State() };
function State() {
this.lineComment = false;
this.blockComment = false;
this.singleQuote = false;
this.doubleQuote = false;
this.regexp = false;
this.escaped = false;
this.roundDepth = 0;
this.curlyDepth = 0;
this.squareDepth = 0;
this.history = ''
this.lastChar = ''
}
State.prototype.isString = function () {
return this.singleQuote || this.doubleQuote;
}
State.prototype.isComment = function () {
return this.lineComment || this.blockComment;
}
State.prototype.isNesting = function () {
return this.isString() || this.isComment() || this.regexp || this.roundDepth > 0 || this.curlyDepth > 0 || this.squareDepth > 0
}
function startsWith(str, start, i) {
return str.substr(i || 0, start.length) === start;
}
exports.isPunctuator = isPunctuator
function isPunctuator(c) {
if (!c) return true; // the start of a string is a punctuator
var code = c.charCodeAt(0)
switch (code) {
case 46: // . dot
case 40: // ( open bracket
case 41: // ) close bracket
case 59: // ; semicolon
case 44: // , comma
case 123: // { open curly brace
case 125: // } close curly brace
case 91: // [
case 93: // ]
case 58: // :
case 63: // ?
case 126: // ~
case 37: // %
case 38: // &
case 42: // *:
case 43: // +
case 45: // -
case 47: // /
case 60: // <
case 62: // >
case 94: // ^
case 124: // |
case 33: // !
case 61: // =
return true;
default:
return false;
}
}
exports.isKeyword = isKeyword
function isKeyword(id) {
return (id === 'if') || (id === 'in') || (id === 'do') || (id === 'var') || (id === 'for') || (id === 'new') ||
(id === 'try') || (id === 'let') || (id === 'this') || (id === 'else') || (id === 'case') ||
(id === 'void') || (id === 'with') || (id === 'enum') || (id === 'while') || (id === 'break') || (id === 'catch') ||
(id === 'throw') || (id === 'const') || (id === 'yield') || (id === 'class') || (id === 'super') ||
(id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch') || (id === 'export') ||
(id === 'import') || (id === 'default') || (id === 'finally') || (id === 'extends') || (id === 'function') ||
(id === 'continue') || (id === 'debugger') || (id === 'package') || (id === 'private') || (id === 'interface') ||
(id === 'instanceof') || (id === 'implements') || (id === 'protected') || (id === 'public') || (id === 'static') ||
(id === 'yield') || (id === 'let');
}
function isRegexp(history) {
//could be start of regexp or divide sign
history = history.replace(/^\s*/, '');
//unless its an `if`, `while`, `for` or `with` it's a divide, so we assume it's a divide
if (history[0] === ')') return false;
//unless it's a function expression, it's a regexp, so we assume it's a regexp
if (history[0] === '}') return true;
//any punctuation means it's a regexp
if (isPunctuator(history[0])) return true;
//if the last thing was a keyword then it must be a regexp (e.g. `typeof /foo/`)
if (/^\w+\b/.test(history) && isKeyword(/^\w+\b/.exec(history)[0].split('').reverse().join(''))) return true;
return false;
}

View File

@@ -0,0 +1,56 @@
{
"name": "character-parser",
"version": "1.2.1",
"description": "Parse JavaScript one character at a time to look for snippets in Templates. This is not a validator, it's just designed to allow you to have sections of JavaScript delimited by brackets robustly.",
"main": "index.js",
"scripts": {
"test": "mocha -R spec"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ForbesLindesay/character-parser.git"
},
"keywords": [
"parser",
"JavaScript",
"bracket",
"nesting",
"comment",
"string",
"escape",
"escaping"
],
"author": {
"name": "ForbesLindesay"
},
"license": "MIT",
"devDependencies": {
"better-assert": "~1.0.0",
"mocha": "~1.9.0"
},
"bugs": {
"url": "https://github.com/ForbesLindesay/character-parser/issues"
},
"homepage": "https://github.com/ForbesLindesay/character-parser",
"_id": "character-parser@1.2.1",
"dist": {
"shasum": "c0dde4ab182713b919b970959a123ecc1a30fcd6",
"tarball": "http://registry.npmjs.org/character-parser/-/character-parser-1.2.1.tgz"
},
"_from": "character-parser@1.2.1",
"_npmVersion": "1.4.3",
"_npmUser": {
"name": "forbeslindesay",
"email": "forbes@lindeay.co.uk"
},
"maintainers": [
{
"name": "forbeslindesay",
"email": "forbes@lindesay.co.uk"
}
],
"directories": {},
"_shasum": "c0dde4ab182713b919b970959a123ecc1a30fcd6",
"_resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.1.tgz",
"readme": "ERROR: No README data found!"
}

222
node_modules/jade/node_modules/commander/History.md generated vendored Normal file
View File

@@ -0,0 +1,222 @@
2.6.0 / 2014-12-30
==================
* added `Command#allowUnknownOption` method. Close #138 #318 @doozr @zhiyelee
* Add application description to the help msg. Close #112 @dalssoft
2.5.1 / 2014-12-15
==================
* fixed two bugs incurred by variadic arguments. Close #291 @Quentin01 #302 @zhiyelee
2.5.0 / 2014-10-24
==================
* add support for variadic arguments. Closes #277 @whitlockjc
2.4.0 / 2014-10-17
==================
* fixed a bug on executing the coercion function of subcommands option. Closes #270
* added `Command.prototype.name` to retrieve command name. Closes #264 #266 @tonylukasavage
* added `Command.prototype.opts` to retrieve all the options as a simple object of key-value pairs. Closes #262 @tonylukasavage
* fixed a bug on subcommand name. Closes #248 @jonathandelgado
* fixed function normalize doesnt honor option terminator. Closes #216 @abbr
2.3.0 / 2014-07-16
==================
* add command alias'. Closes PR #210
* fix: Typos. Closes #99
* fix: Unused fs module. Closes #217
2.2.0 / 2014-03-29
==================
* add passing of previous option value
* fix: support subcommands on windows. Closes #142
* Now the defaultValue passed as the second argument of the coercion function.
2.1.0 / 2013-11-21
==================
* add: allow cflag style option params, unit test, fixes #174
2.0.0 / 2013-07-18
==================
* remove input methods (.prompt, .confirm, etc)
1.3.2 / 2013-07-18
==================
* add support for sub-commands to co-exist with the original command
1.3.1 / 2013-07-18
==================
* add quick .runningCommand hack so you can opt-out of other logic when running a sub command
1.3.0 / 2013-07-09
==================
* add EACCES error handling
* fix sub-command --help
1.2.0 / 2013-06-13
==================
* allow "-" hyphen as an option argument
* support for RegExp coercion
1.1.1 / 2012-11-20
==================
* add more sub-command padding
* fix .usage() when args are present. Closes #106
1.1.0 / 2012-11-16
==================
* add git-style executable subcommand support. Closes #94
1.0.5 / 2012-10-09
==================
* fix `--name` clobbering. Closes #92
* fix examples/help. Closes #89
1.0.4 / 2012-09-03
==================
* add `outputHelp()` method.
1.0.3 / 2012-08-30
==================
* remove invalid .version() defaulting
1.0.2 / 2012-08-24
==================
* add `--foo=bar` support [arv]
* fix password on node 0.8.8. Make backward compatible with 0.6 [focusaurus]
1.0.1 / 2012-08-03
==================
* fix issue #56
* fix tty.setRawMode(mode) was moved to tty.ReadStream#setRawMode() (i.e. process.stdin.setRawMode())
1.0.0 / 2012-07-05
==================
* add support for optional option descriptions
* add defaulting of `.version()` to package.json's version
0.6.1 / 2012-06-01
==================
* Added: append (yes or no) on confirmation
* Added: allow node.js v0.7.x
0.6.0 / 2012-04-10
==================
* Added `.prompt(obj, callback)` support. Closes #49
* Added default support to .choose(). Closes #41
* Fixed the choice example
0.5.1 / 2011-12-20
==================
* Fixed `password()` for recent nodes. Closes #36
0.5.0 / 2011-12-04
==================
* Added sub-command option support [itay]
0.4.3 / 2011-12-04
==================
* Fixed custom help ordering. Closes #32
0.4.2 / 2011-11-24
==================
* Added travis support
* Fixed: line-buffered input automatically trimmed. Closes #31
0.4.1 / 2011-11-18
==================
* Removed listening for "close" on --help
0.4.0 / 2011-11-15
==================
* Added support for `--`. Closes #24
0.3.3 / 2011-11-14
==================
* Fixed: wait for close event when writing help info [Jerry Hamlet]
0.3.2 / 2011-11-01
==================
* Fixed long flag definitions with values [felixge]
0.3.1 / 2011-10-31
==================
* Changed `--version` short flag to `-V` from `-v`
* Changed `.version()` so it's configurable [felixge]
0.3.0 / 2011-10-31
==================
* Added support for long flags only. Closes #18
0.2.1 / 2011-10-24
==================
* "node": ">= 0.4.x < 0.7.0". Closes #20
0.2.0 / 2011-09-26
==================
* Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs]
0.1.0 / 2011-08-24
==================
* Added support for custom `--help` output
0.0.5 / 2011-08-18
==================
* Changed: when the user enters nothing prompt for password again
* Fixed issue with passwords beginning with numbers [NuckChorris]
0.0.4 / 2011-08-15
==================
* Fixed `Commander#args`
0.0.3 / 2011-08-15
==================
* Added default option value support
0.0.2 / 2011-08-15
==================
* Added mask support to `Command#password(str[, mask], fn)`
* Added `Command#password(str, fn)`
0.0.1 / 2010-01-03
==================
* Initial release

300
node_modules/jade/node_modules/commander/Readme.md generated vendored Normal file
View File

@@ -0,0 +1,300 @@
# Commander.js
[![Build Status](https://api.travis-ci.org/tj/commander.js.svg)](http://travis-ci.org/tj/commander.js)
[![NPM Version](http://img.shields.io/npm/v/commander.svg?style=flat)](https://www.npmjs.org/package/commander)
[![NPM Downloads](https://img.shields.io/npm/dm/commander.svg?style=flat)](https://www.npmjs.org/package/commander)
The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/tj/commander).
[API documentation](http://tj.github.com/commander.js/)
## Installation
$ npm install commander
## Option parsing
Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.
```js
#!/usr/bin/env node
/**
* Module dependencies.
*/
var program = require('commander');
program
.version('0.0.1')
.option('-p, --peppers', 'Add peppers')
.option('-P, --pineapple', 'Add pineapple')
.option('-b, --bbq', 'Add bbq sauce')
.option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
.parse(process.argv);
console.log('you ordered a pizza with:');
if (program.peppers) console.log(' - peppers');
if (program.pineapple) console.log(' - pineapple');
if (program.bbq) console.log(' - bbq');
console.log(' - %s cheese', program.cheese);
```
Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as "--template-engine" are camel-cased, becoming `program.templateEngine` etc.
## Coercion
```js
function range(val) {
return val.split('..').map(Number);
}
function list(val) {
return val.split(',');
}
function collect(val, memo) {
memo.push(val);
return memo;
}
function increaseVerbosity(v, total) {
return total + 1;
}
program
.version('0.0.1')
.usage('[options] <file ...>')
.option('-i, --integer <n>', 'An integer argument', parseInt)
.option('-f, --float <n>', 'A float argument', parseFloat)
.option('-r, --range <a>..<b>', 'A range', range)
.option('-l, --list <items>', 'A list', list)
.option('-o, --optional [value]', 'An optional value')
.option('-c, --collect [value]', 'A repeatable value', collect, [])
.option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0)
.parse(process.argv);
console.log(' int: %j', program.integer);
console.log(' float: %j', program.float);
console.log(' optional: %j', program.optional);
program.range = program.range || [];
console.log(' range: %j..%j', program.range[0], program.range[1]);
console.log(' list: %j', program.list);
console.log(' collect: %j', program.collect);
console.log(' verbosity: %j', program.verbose);
console.log(' args: %j', program.args);
```
## Variadic arguments
The last argument of a command can be variadic, and only the last argument. To make an argument variadic you have to
append `...` to the argument name. Here is an example:
```js
#!/usr/bin/env node
/**
* Module dependencies.
*/
var program = require('commander');
program
.version('0.0.1')
.command('rmdir <dir> [otherDirs...]')
.action(function (dir, otherDirs) {
console.log('rmdir %s', dir);
if (otherDirs) {
otherDirs.forEach(function (oDir) {
console.log('rmdir %s', oDir);
});
}
});
program.parse(process.argv);
```
An `Array` is used for the value of a variadic argument. This applies to `program.args` as well as the argument passed
to your action as demonstrated above.
## Git-style sub-commands
```js
// file: ./examples/pm
var program = require('..');
program
.version('0.0.1')
.command('install [name]', 'install one or more packages')
.command('search [query]', 'search with optional query')
.command('list', 'list packages installed')
.parse(process.argv);
```
When `.command()` is invoked with a description argument, no `.action(callback)` should be called to handle sub-commands, otherwise there will be an error. This tells commander that you're going to use separate executables for sub-commands, much like `git(1)` and other popular tools.
The commander will try to find the executable script in __current directory__ with the name `scriptBasename-subcommand`, like `pm-install`, `pm-search`.
## Automated --help
The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:
```
$ ./examples/pizza --help
Usage: pizza [options]
An application for pizzas ordering
Options:
-h, --help output usage information
-V, --version output the version number
-p, --peppers Add peppers
-P, --pineapple Add pineapple
-b, --bbq Add bbq sauce
-c, --cheese <type> Add the specified type of cheese [marble]
-C, --no-cheese You do not want any cheese
```
## Custom help
You can display arbitrary `-h, --help` information
by listening for "--help". Commander will automatically
exit once you are done so that the remainder of your program
does not execute causing undesired behaviours, for example
in the following executable "stuff" will not output when
`--help` is used.
```js
#!/usr/bin/env node
/**
* Module dependencies.
*/
var program = require('commander');
program
.version('0.0.1')
.option('-f, --foo', 'enable some foo')
.option('-b, --bar', 'enable some bar')
.option('-B, --baz', 'enable some baz');
// must be before .parse() since
// node's emit() is immediate
program.on('--help', function(){
console.log(' Examples:');
console.log('');
console.log(' $ custom-help --help');
console.log(' $ custom-help -h');
console.log('');
});
program.parse(process.argv);
console.log('stuff');
```
Yields the following help output when `node script-name.js -h` or `node script-name.js --help` are run:
```
Usage: custom-help [options]
Options:
-h, --help output usage information
-V, --version output the version number
-f, --foo enable some foo
-b, --bar enable some bar
-B, --baz enable some baz
Examples:
$ custom-help --help
$ custom-help -h
```
## .outputHelp()
Output help information without exiting.
## .help()
Output help information and exit immediately.
## Examples
```js
var program = require('commander');
program
.version('0.0.1')
.option('-C, --chdir <path>', 'change the working directory')
.option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
.option('-T, --no-tests', 'ignore test hook')
program
.command('setup [env]')
.description('run setup commands for all envs')
.option("-s, --setup_mode [mode]", "Which setup mode to use")
.action(function(env, options){
var mode = options.setup_mode || "normal";
env = env || 'all';
console.log('setup for %s env(s) with %s mode', env, mode);
});
program
.command('exec <cmd>')
.alias('ex')
.description('execute the given remote cmd')
.option("-e, --exec_mode <mode>", "Which exec mode to use")
.action(function(cmd, options){
console.log('exec "%s" using %s mode', cmd, options.exec_mode);
}).on('--help', function() {
console.log(' Examples:');
console.log();
console.log(' $ deploy exec sequential');
console.log(' $ deploy exec async');
console.log();
});
program
.command('*')
.action(function(env){
console.log('deploying "%s"', env);
});
program.parse(process.argv);
```
You can see more Demos in the [examples](https://github.com/tj/commander.js/tree/master/examples) directory.
## License
(The MIT License)
Copyright (c) 2011 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
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.

1020
node_modules/jade/node_modules/commander/index.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

72
node_modules/jade/node_modules/commander/package.json generated vendored Normal file
View File

@@ -0,0 +1,72 @@
{
"name": "commander",
"version": "2.6.0",
"description": "the complete solution for node.js command-line programs",
"keywords": [
"command",
"option",
"parser",
"prompt"
],
"author": {
"name": "TJ Holowaychuk",
"email": "tj@vision-media.ca"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/tj/commander.js.git"
},
"devDependencies": {
"should": ">= 0.0.1"
},
"scripts": {
"test": "make test"
},
"main": "index",
"engines": {
"node": ">= 0.6.x"
},
"files": [
"index.js"
],
"gitHead": "c6807fd154dd3b7ce8756f141f8d3acfcc74be60",
"bugs": {
"url": "https://github.com/tj/commander.js/issues"
},
"homepage": "https://github.com/tj/commander.js",
"_id": "commander@2.6.0",
"_shasum": "9df7e52fb2a0cb0fb89058ee80c3104225f37e1d",
"_from": "commander@>=2.6.0 <2.7.0",
"_npmVersion": "2.1.12",
"_nodeVersion": "0.11.14",
"_npmUser": {
"name": "zhiyelee",
"email": "zhiyelee@gmail.com"
},
"maintainers": [
{
"name": "tjholowaychuk",
"email": "tj@vision-media.ca"
},
{
"name": "somekittens",
"email": "rkoutnik@gmail.com"
},
{
"name": "zhiyelee",
"email": "zhiyelee@gmail.com"
},
{
"name": "thethomaseffect",
"email": "thethomaseffect@gmail.com"
}
],
"dist": {
"shasum": "9df7e52fb2a0cb0fb89058ee80c3104225f37e1d",
"tarball": "http://registry.npmjs.org/commander/-/commander-2.6.0.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz",
"readme": "ERROR: No README data found!"
}

View File

@@ -0,0 +1,22 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

View File

@@ -0,0 +1,13 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
npm-debug.log
node_modules

View File

@@ -0,0 +1,4 @@
language: node_js
node_js:
- "0.8"
- "0.10"

19
node_modules/jade/node_modules/constantinople/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2013 Forbes Lindesay
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.

View File

@@ -0,0 +1,42 @@
# constantinople
Determine whether a JavaScript expression evaluates to a constant (using acorn). Here it is assumed to be safe to underestimate how constant something is.
[![Build Status](https://img.shields.io/travis/ForbesLindesay/constantinople/master.svg)](https://travis-ci.org/ForbesLindesay/constantinople)
[![Dependency Status](https://img.shields.io/gemnasium/ForbesLindesay/constantinople.svg)](https://gemnasium.com/ForbesLindesay/constantinople)
[![NPM version](https://img.shields.io/npm/v/constantinople.svg)](https://www.npmjs.org/package/constantinople)
## Installation
npm install constantinople
## Usage
```js
var isConstant = require('constantinople')
if (isConstant('"foo" + 5')) {
console.dir(isConstant.toConstant('"foo" + 5'))
}
if (isConstant('Math.floor(10.5)', {Math: Math})) {
console.dir(isConstant.toConstant('Math.floor(10.5)', {Math: Math}))
}
```
## API
### isConstant(src, [constants])
Returns `true` if `src` evaluates to a constant, `false` otherwise. It will also return `false` if there is a syntax error, which makes it safe to use on potentially ES6 code.
Constants is an object mapping strings to values, where those values should be treated as constants. Note that this makes it a pretty bad idea to have `Math` in there if the user might make use of `Math.random` and a pretty bad idea to have `Date` in there.
### toConstant(src, [constants])
Returns the value resulting from evaluating `src`. This method throws an error if the expression is not constant. e.g. `toConstant("Math.random()")` would throw an error.
Constants is an object mapping strings to values, where those values should be treated as constants. Note that this makes it a pretty bad idea to have `Math` in there if the user might make use of `Math.random` and a pretty bad idea to have `Date` in there.
## License
MIT

42
node_modules/jade/node_modules/constantinople/index.js generated vendored Normal file
View File

@@ -0,0 +1,42 @@
'use strict'
var detect = require('acorn-globals');
var lastSRC = '(null)';
var lastRes = true;
var lastConstants = undefined;
module.exports = isConstant;
function isConstant(src, constants) {
src = '(' + src + ')';
if (lastSRC === src && lastConstants === constants) return lastRes;
lastSRC = src;
lastConstants = constants;
try {
isExpression(src);
return lastRes = (detect(src).filter(function (key) {
return !constants || !(key.name in constants);
}).length === 0);
} catch (ex) {
return lastRes = false;
}
}
isConstant.isConstant = isConstant;
isConstant.toConstant = toConstant;
function toConstant(src, constants) {
if (!isConstant(src, constants)) throw new Error(JSON.stringify(src) + ' is not constant.');
return Function(Object.keys(constants || {}).join(','), 'return (' + src + ')').apply(null, Object.keys(constants || {}).map(function (key) {
return constants[key];
}));
}
function isExpression(src) {
try {
eval('throw "STOP"; (function () { return (' + src + '); })()');
return false;
}
catch (err) {
return err === 'STOP';
}
}

View File

@@ -0,0 +1,19 @@
Copyright (c) 2014 Forbes Lindesay
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.

View File

@@ -0,0 +1,76 @@
# acorn-globals
Detect global variables in JavaScript using acorn
[![Build Status](https://img.shields.io/travis/ForbesLindesay/acorn-globals/master.svg)](https://travis-ci.org/ForbesLindesay/acorn-globals)
[![Dependency Status](https://img.shields.io/gemnasium/ForbesLindesay/acorn-globals.svg)](https://gemnasium.com/ForbesLindesay/acorn-globals)
[![NPM version](https://img.shields.io/npm/v/acorn-globals.svg)](https://www.npmjs.org/package/acorn-globals)
## Installation
npm install acorn-globals
## Usage
detect.js
```js
var fs = require('fs');
var detect = require('acorn-globals');
var src = fs.readFileSync(__dirname + '/input.js', 'utf8');
var scope = detect(src);
console.dir(scope);
```
input.js
```js
var x = 5;
var y = 3, z = 2;
w.foo();
w = 2;
RAWR=444;
RAWR.foo();
BLARG=3;
foo(function () {
var BAR = 3;
process.nextTick(function (ZZZZZZZZZZZZ) {
console.log('beep boop');
var xyz = 4;
x += 10;
x.zzzzzz;
ZZZ=6;
});
function doom () {
}
ZZZ.foo();
});
console.log(xyz);
```
output:
```
$ node example/detect.js
[ { name: 'BLARG', nodes: [ [Object] ] },
{ name: 'RAWR', nodes: [ [Object], [Object] ] },
{ name: 'ZZZ', nodes: [ [Object], [Object] ] },
{ name: 'console', nodes: [ [Object], [Object] ] },
{ name: 'foo', nodes: [ [Object] ] },
{ name: 'process', nodes: [ [Object] ] },
{ name: 'w', nodes: [ [Object], [Object] ] },
{ name: 'xyz', nodes: [ [Object] ] } ]
```
## License
MIT

View File

@@ -0,0 +1,155 @@
'use strict';
var acorn = require('acorn');
var walk = require('acorn/dist/walk');
// polyfill for https://github.com/marijnh/acorn/pull/231
walk.base.ExportNamedDeclaration = walk.base.ExportDefaultDeclaration = function (node, st, c) {
return c(node.declaration, st);
};
walk.base.ImportDefaultSpecifier = walk.base.ImportNamespaceSpecifier = function () {};
function isScope(node) {
return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'Program';
}
function isBlockScope(node) {
return node.type === 'BlockStatement' || isScope(node);
}
function declaresArguments(node) {
return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunction';
}
function declaresThis(node) {
return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration';
}
function reallyParse(source) {
try {
return acorn.parse(source, {
ecmaVersion: 6,
allowReturnOutsideFunction: true,
sourceType: 'module'
});
} catch (ex) {
if (ex.name !== 'SyntaxError') {
throw ex;
}
try {
return acorn.parse(source, {
ecmaVersion: 6,
allowReturnOutsideFunction: true
});
} catch (ex) {
if (ex.name !== 'SyntaxError') {
throw ex;
}
return acorn.parse(source, {
ecmaVersion: 5,
allowReturnOutsideFunction: true
});
}
}
}
module.exports = findGlobals;
module.exports.parse = reallyParse;
function findGlobals(source) {
var globals = [];
var ast = typeof source === 'string' ?
ast = reallyParse(source) :
source;
if (!(ast && typeof ast === 'object' && ast.type === 'Program')) {
throw new TypeError('Source must be either a string of JavaScript or an acorn AST');
}
var declareFunction = function (node) {
var fn = node;
fn.locals = fn.locals || {};
node.params.forEach(function (node) {
fn.locals[node.name] = true;
});
if (node.id) {
fn.locals[node.id.name] = true;
}
}
walk.ancestor(ast, {
'VariableDeclaration': function (node, parents) {
var parent = null;
for (var i = parents.length - 1; i >= 0 && parent === null; i--) {
if (node.kind === 'var' ? isScope(parents[i]) : isBlockScope(parents[i])) {
parent = parents[i];
}
}
parent.locals = parent.locals || {};
node.declarations.forEach(function (declaration) {
parent.locals[declaration.id.name] = true;
});
},
'FunctionDeclaration': function (node, parents) {
var parent = null;
for (var i = parents.length - 2; i >= 0 && parent === null; i--) {
if (isScope(parents[i])) {
parent = parents[i];
}
}
parent.locals = parent.locals || {};
parent.locals[node.id.name] = true;
declareFunction(node);
},
'Function': declareFunction,
'TryStatement': function (node) {
node.handler.body.locals = node.handler.body.locals || {};
node.handler.body.locals[node.handler.param.name] = true;
},
'ImportDefaultSpecifier': function (node) {
if (node.local.type === 'Identifier') {
ast.locals = ast.locals || {};
ast.locals[node.local.name] = true;
}
},
'ImportSpecifier': function (node) {
var id = node.local ? node.local : node.imported;
if (id.type === 'Identifier') {
ast.locals = ast.locals || {};
ast.locals[id.name] = true;
}
},
'ImportNamespaceSpecifier': function (node) {
if (node.local.type === 'Identifier') {
ast.locals = ast.locals || {};
ast.locals[node.local.name] = true;
}
}
});
walk.ancestor(ast, {
'Identifier': function (node, parents) {
var name = node.name;
if (name === 'undefined') return;
for (var i = 0; i < parents.length; i++) {
if (name === 'arguments' && declaresArguments(parents[i])) {
return;
}
if (parents[i].locals && name in parents[i].locals) {
return;
}
}
node.parents = parents;
globals.push(node);
},
ThisExpression: function (node, parents) {
for (var i = 0; i < parents.length; i++) {
if (declaresThis(parents[i])) {
return;
}
}
node.parents = parents;
globals.push(node);
}
});
var groupedGlobals = {};
globals.forEach(function (node) {
groupedGlobals[node.name] = (groupedGlobals[node.name] || []);
groupedGlobals[node.name].push(node);
});
return Object.keys(groupedGlobals).sort().map(function (name) {
return {name: name, nodes: groupedGlobals[name]};
});
}

View File

@@ -0,0 +1 @@
../acorn/bin/acorn

View File

@@ -0,0 +1,7 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true

View File

@@ -0,0 +1 @@
* text eol=lf

View File

@@ -0,0 +1,3 @@
/.tern-port
/test
/local

View File

@@ -0,0 +1,2 @@
language: node_js
node_js: '0.10'

View File

@@ -0,0 +1,38 @@
List of Acorn contributors. Updated before every release.
Adrian Rakovsky
Alistair Braidwood
Andres Suarez
Aparajita Fishman
Arian Stolwijk
Artem Govorov
Brandon Mills
Charles Hughes
Conrad Irwin
David Bonnet
Forbes Lindesay
Gilad Peleg
impinball
Ingvar Stepanyan
Jiaxing Wang
Johannes Herr
Jürg Lehni
keeyipchan
krator
Marijn Haverbeke
Martin Carlberg
Mathias Bynens
Mathieu 'p01' Henri
Max Schaefer
Max Zerzouri
Mihai Bazon
Mike Rennie
Nick Fitzgerald
Oskar Schöldström
Paul Harper
Peter Rust
PlNG
r-e-d
Rich Harris
Sebastian McKenzie
zsjforcn

View File

@@ -0,0 +1,19 @@
Copyright (C) 2012-2014 by various contributors (see AUTHORS)
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.

View File

@@ -0,0 +1,377 @@
# Acorn
[![Build Status](https://travis-ci.org/marijnh/acorn.svg?branch=master)](https://travis-ci.org/marijnh/acorn)
[![NPM version](https://img.shields.io/npm/v/acorn.svg)](https://www.npmjs.org/package/acorn)
[Author funding status: ![maintainer happiness](https://marijnhaverbeke.nl/fund/status_s.png?force)](https://marijnhaverbeke.nl/fund/)
A tiny, fast JavaScript parser, written completely in JavaScript.
## Installation
The easiest way to install acorn is with [`npm`][npm].
[npm]: http://npmjs.org
```sh
npm install acorn
```
Alternately, download the source.
```sh
git clone https://github.com/marijnh/acorn.git
```
## Components
When run in a CommonJS (node.js) or AMD environment, exported values
appear in the interfaces exposed by the individual files, as usual.
When loaded in the browser (Acorn works in any JS-enabled browser more
recent than IE5) without any kind of module management, a single
global object `acorn` will be defined, and all the exported properties
will be added to that.
### Main parser
This is implemented in `dist/acorn.js`, and is what you get when you
`require("acorn")` in node.js.
**parse**`(input, options)` is used to parse a JavaScript program.
The `input` parameter is a string, `options` can be undefined or an
object setting some of the options listed below. The return value will
be an abstract syntax tree object as specified by the
[Mozilla Parser API][mozapi].
When encountering a syntax error, the parser will raise a
`SyntaxError` object with a meaningful message. The error object will
have a `pos` property that indicates the character offset at which the
error occurred, and a `loc` object that contains a `{line, column}`
object referring to that same position.
[mozapi]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
- **ecmaVersion**: Indicates the ECMAScript version to parse. Must be
either 3, 5, or 6. This influences support for strict mode, the set
of reserved words, and support for new syntax features. Default is 5.
- **sourceType**: Indicate the mode the code should be parsed in. Can be
either `"script"` or `"module"`.
- **onInsertedSemicolon**: If given a callback, that callback will be
called whenever a missing semicolon is inserted by the parser. The
callback will be given the character offset of the point where the
semicolon is inserted as argument, and if `locations` is on, also a
`{line, column}` object representing this position.
- **onTrailingComma**: Like `onInsertedSemicolon`, but for trailing
commas.
- **allowReserved**: If `false`, using a reserved word will generate
an error. Defaults to `true`. When given the value `"never"`,
reserved words and keywords can also not be used as property names
(as in Internet Explorer's old parser).
- **allowReturnOutsideFunction**: By default, a return statement at
the top level raises an error. Set this to `true` to accept such
code.
- **allowImportExportEverywhere**: By default, `import` and `export`
declarations can only appear at a program's top level. Setting this
option to `true` allows them anywhere where a statement is allowed.
- **allowHashBang**: When this is enabled (off by default), if the
code starts with the characters `#!` (as in a shellscript), the
first line will be treated as a comment.
- **locations**: When `true`, each node has a `loc` object attached
with `start` and `end` subobjects, each of which contains the
one-based line and zero-based column numbers in `{line, column}`
form. Default is `false`.
- **onToken**: If a function is passed for this option, each found
token will be passed in same format as `tokenize()` returns.
If array is passed, each found token is pushed to it.
Note that you are not allowed to call the parser from the
callback—that will corrupt its internal state.
- **onComment**: If a function is passed for this option, whenever a
comment is encountered the function will be called with the
following parameters:
- `block`: `true` if the comment is a block comment, false if it
is a line comment.
- `text`: The content of the comment.
- `start`: Character offset of the start of the comment.
- `end`: Character offset of the end of the comment.
When the `locations` options is on, the `{line, column}` locations
of the comments start and end are passed as two additional
parameters.
If array is passed for this option, each found comment is pushed
to it as object in Esprima format:
```javascript
{
"type": "Line" | "Block",
"value": "comment text",
"range": ...,
"loc": ...
}
```
Note that you are not allowed to call the parser from the
callback—that will corrupt its internal state.
- **ranges**: Nodes have their start and end characters offsets
recorded in `start` and `end` properties (directly on the node,
rather than the `loc` object, which holds line/column data. To also
add a [semi-standardized][range] "range" property holding a
`[start, end]` array with the same numbers, set the `ranges` option
to `true`.
- **program**: It is possible to parse multiple files into a single
AST by passing the tree produced by parsing the first file as the
`program` option in subsequent parses. This will add the toplevel
forms of the parsed file to the "Program" (top) node of an existing
parse tree.
- **sourceFile**: When the `locations` option is `true`, you can pass
this option to add a `source` attribute in every nodes `loc`
object. Note that the contents of this option are not examined or
processed in any way; you are free to use whatever format you
choose.
- **directSourceFile**: Like `sourceFile`, but a `sourceFile` property
will be added directly to the nodes, rather than the `loc` object.
- **preserveParens**: If this option is `true`, parenthesized expressions
are represented by (non-standard) `ParenthesizedExpression` nodes
that have a single `expression` property containing the expression
inside parentheses.
[range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
**parseExpressionAt**`(input, offset, options)` will parse a single
expression in a string, and return its AST. It will not complain if
there is more of the string left after the expression.
**getLineInfo**`(input, offset)` can be used to get a `{line,
column}` object for a given program string and character offset.
**tokenizer**`(input, options)` returns an object with a `getToken`
method that can be called repeatedly to get the next token, a `{start,
end, type, value}` object (with added `loc` property when the
`locations` option is enabled and `range` property when the `ranges`
option is enabled). When the token's type is `tokTypes.eof`, you
should stop calling the method, since it will keep returning that same
token forever.
In ES6 environment, returned result can be used as any other
protocol-compliant iterable:
```javascript
for (let token of acorn.tokenize(str)) {
// iterate over the tokens
}
// transform code to array of tokens:
var tokens = [...acorn.tokenize(str)];
```
**tokTypes** holds an object mapping names to the token type objects
that end up in the `type` properties of tokens.
#### Note on using with [Escodegen][escodegen]
Escodegen supports generating comments from AST, attached in
Esprima-specific format. In order to simulate same format in
Acorn, consider following example:
```javascript
var comments = [], tokens = [];
var ast = acorn.parse('var x = 42; // answer', {
// collect ranges for each node
ranges: true,
// collect comments in Esprima's format
onComment: comments,
// collect token ranges
onToken: tokens
});
// attach comments using collected information
escodegen.attachComments(ast, comments, tokens);
// generate code
console.log(escodegen.generate(ast, {comment: true}));
// > 'var x = 42; // answer'
```
[escodegen]: https://github.com/Constellation/escodegen
#### Using Acorn in an environment with a Content Security Policy
Some contexts, such as Chrome Web Apps, disallow run-time code evaluation.
Acorn uses `new Function` to generate fast functions that test whether
a word is in a given set, and will trigger a security error when used
in a context with such a
[Content Security Policy](http://www.html5rocks.com/en/tutorials/security/content-security-policy/#eval-too)
(see [#90](https://github.com/marijnh/acorn/issues/90) and
[#123](https://github.com/marijnh/acorn/issues/123)).
The `dist/acorn_csp.js` file in the distribution (which is built
by the `bin/without_eval` script) has the generated code inlined, and
can thus run without evaluating anything.
### dist/acorn_loose.js ###
This file implements an error-tolerant parser. It exposes a single
function.
**parse_dammit**`(input, options)` takes the same arguments and
returns the same syntax tree as the `parse` function in `acorn.js`,
but never raises an error, and will do its best to parse syntactically
invalid code in as meaningful a way as it can. It'll insert identifier
nodes with name `"✖"` as placeholders in places where it can't make
sense of the input. Depends on `acorn.js`, because it uses the same
tokenizer.
### dist/walk.js ###
Implements an abstract syntax tree walker. Will store its interface in
`acorn.walk` when loaded without a module system.
**simple**`(node, visitors, base, state)` does a 'simple' walk over
a tree. `node` should be the AST node to walk, and `visitors` an
object with properties whose names correspond to node types in the
[Mozilla Parser API][mozapi]. The properties should contain functions
that will be called with the node object and, if applicable the state
at that point. The last two arguments are optional. `base` is a walker
algorithm, and `state` is a start state. The default walker will
simply visit all statements and expressions and not produce a
meaningful state. (An example of a use of state it to track scope at
each point in the tree.)
**ancestor**`(node, visitors, base, state)` does a 'simple' walk over
a tree, building up an array of ancestor nodes (including the current node)
and passing the array to callbacks in the `state` parameter.
**recursive**`(node, state, functions, base)` does a 'recursive'
walk, where the walker functions are responsible for continuing the
walk on the child nodes of their target node. `state` is the start
state, and `functions` should contain an object that maps node types
to walker functions. Such functions are called with `(node, state, c)`
arguments, and can cause the walk to continue on a sub-node by calling
the `c` argument on it with `(node, state)` arguments. The optional
`base` argument provides the fallback walker functions for node types
that aren't handled in the `functions` object. If not given, the
default walkers will be used.
**make**`(functions, base)` builds a new walker object by using the
walker functions in `functions` and filling in the missing ones by
taking defaults from `base`.
**findNodeAt**`(node, start, end, test, base, state)` tries to
locate a node in a tree at the given start and/or end offsets, which
satisfies the predicate `test`. `start` end `end` can be either `null`
(as wildcard) or a number. `test` may be a string (indicating a node
type) or a function that takes `(nodeType, node)` arguments and
returns a boolean indicating whether this node is interesting. `base`
and `state` are optional, and can be used to specify a custom walker.
Nodes are tested from inner to outer, so if two nodes match the
boundaries, the inner one will be preferred.
**findNodeAround**`(node, pos, test, base, state)` is a lot like
`findNodeAt`, but will match any node that exists 'around' (spanning)
the given position.
**findNodeAfter**`(node, pos, test, base, state)` is similar to
`findNodeAround`, but will match all nodes *after* the given position
(testing outer nodes before inner nodes).
## Command line interface
The `bin/acorn` utility can be used to parse a file from the command
line. It accepts as arguments its input file and the following
options:
- `--ecma3|--ecma5|--ecma6`: Sets the ECMAScript version to parse. Default is
version 5.
- `--locations`: Attaches a "loc" object to each node with "start" and
"end" subobjects, each of which contains the one-based line and
zero-based column numbers in `{line, column}` form.
- `--allow-hash-bang`: If the code starts with the characters #! (as in a shellscript), the first line will be treated as a comment.
- `--compact`: No whitespace is used in the AST output.
- `--silent`: Do not output the AST, just return the exit status.
- `--help`: Print the usage information and quit.
The utility spits out the syntax tree as JSON data.
## Build system
Acorn is written in ECMAScript 6, as a set of small modules, in the
project's `src` directory, and compiled down to bigger ECMAScript 3
files in `dist` using [Browserify](http://browserify.org) and
[Babel](http://babeljs.io/). If you are already using Babel, you can
consider including the modules directly.
The command-line test runner (`npm test`) uses the ES6 modules. The
browser-based test page (`test/index.html`) uses the compiled modules.
The `bin/build-acorn.js` script builds the latter from the former.
If you are working on Acorn, you'll probably want to try the code out
directly, without an intermediate build step. In your scripts, you can
register the Babel require shim like this:
require("babelify/node_modules/babel-core/register")
That will allow you to directly `require` the ES6 modules.
## Plugins
Acorn is designed support allow plugins which, within reasonable
bounds, redefine the way the parser works. Plugins can add new token
types and new tokenizer contexts (if necessary), and extend methods in
the parser object. This is not a clean, elegant API—using it requires
an understanding of Acorn's internals, and plugins are likely to break
whenever those internals are significantly changed. But still, it is
_possible_, in this way, to create parsers for JavaScript dialects
without forking all of Acorn. And in principle it is even possible to
combine such plugins, so that if you have, for example, a plugin for
parsing types and a plugin for parsing JSX-style XML literals, you
could load them both and parse code with both JSX tags and types.
A plugin should register itself by adding a property to
`acorn.plugins`, which holds a function. Calling `acorn.parse`, a
`plugin` option can be passed, holding an object mapping plugin names
to configuration values (or just `true` for plugins that don't take
options). After the parser object has been created, the initialization
functions for the chosen plugins are called with `(parser,
configValue)` arguments. They are expected to use the `parser.extend`
method to extend parser methods. For example, the `readToken` method
could be extended like this:
```javascript
parser.extend("readToken", function(nextMethod) {
return function(code) {
console.log("Reading a token!")
return nextMethod.call(this, code)
}
})
```
The `nextMethod` argument passed to `extend`'s second argument is the
previous value of this method, and should usually be called through to
whenever the extended method does not handle the call itself.
There is a proof-of-concept JSX plugin in the [`jsx`
branch](https://github.com/marijnh/acorn/tree/jsx) branch of the
Github repository.

View File

@@ -0,0 +1,54 @@
#!/usr/bin/env node
var path = require("path");
var fs = require("fs");
var acorn = require("../dist/acorn.js");
var infile, parsed, tokens, options = {}, silent = false, compact = false, tokenize = false;
function help(status) {
var print = (status == 0) ? console.log : console.error;
print("usage: " + path.basename(process.argv[1]) + " [--ecma3|--ecma5|--ecma6]");
print(" [--tokenize] [--locations] [---allow-hash-bang] [--compact] [--silent] [--help] [--] infile");
process.exit(status);
}
for (var i = 2; i < process.argv.length; ++i) {
var arg = process.argv[i];
if (arg[0] != "-" && !infile) infile = arg;
else if (arg == "--" && !infile && i + 2 == process.argv.length) infile = process.argv[++i];
else if (arg == "--ecma3") options.ecmaVersion = 3;
else if (arg == "--ecma5") options.ecmaVersion = 5;
else if (arg == "--ecma6") options.ecmaVersion = 6;
else if (arg == "--ecma7") options.ecmaVersion = 7;
else if (arg == "--locations") options.locations = true;
else if (arg == "--allow-hash-bang") options.allowHashBang = true;
else if (arg == "--silent") silent = true;
else if (arg == "--compact") compact = true;
else if (arg == "--help") help(0);
else if (arg == "--tokenize") tokenize = true;
else help(1);
}
try {
var code = fs.readFileSync(infile, "utf8");
if (!tokenize)
parsed = acorn.parse(code, options);
else {
var get = acorn.tokenize(code, options);
tokens = [];
while (true) {
var token = get();
tokens.push(token);
if (token.type.type == "eof")
break;
}
}
} catch(e) {
console.log(e.message);
process.exit(1);
}
if (!silent)
console.log(JSON.stringify(tokenize ? tokens : parsed, null, compact ? null : 2));

View File

@@ -0,0 +1,51 @@
var fs = require("fs"), path = require("path")
var stream = require("stream")
var browserify = require("browserify")
var babelify = require("babelify").configure({loose: "all"})
process.chdir(path.resolve(__dirname, ".."))
browserify({standalone: "acorn"})
.plugin(require('browserify-derequire'))
.transform(babelify)
.require("./src/index.js", {entry: true})
.bundle()
.on("error", function (err) { console.log("Error: " + err.message) })
.pipe(fs.createWriteStream("dist/acorn.js"))
function acornShim(file) {
var tr = new stream.Transform
if (file == path.resolve(__dirname, "../src/index.js")) {
var sent = false
tr._transform = function(chunk, _, callback) {
if (!sent) {
sent = true
callback(null, "module.exports = typeof acorn != 'undefined' ? acorn : _dereq_(\"./acorn\")")
} else {
callback()
}
}
} else {
tr._transform = function(chunk, _, callback) { callback(null, chunk) }
}
return tr
}
browserify({standalone: "acorn.loose"})
.plugin(require('browserify-derequire'))
.transform(acornShim)
.transform(babelify)
.require("./src/loose/index.js", {entry: true})
.bundle()
.on("error", function (err) { console.log("Error: " + err.message) })
.pipe(fs.createWriteStream("dist/acorn_loose.js"))
browserify({standalone: "acorn.walk"})
.plugin(require('browserify-derequire'))
.transform(acornShim)
.transform(babelify)
.require("./src/walk/index.js", {entry: true})
.bundle()
.on("error", function (err) { console.log("Error: " + err.message) })
.pipe(fs.createWriteStream("dist/walk.js"))

View File

@@ -0,0 +1,47 @@
// Note: run `npm install unicode-7.0.0` first.
// Which Unicode version should be used?
var version = '7.0.0';
var start = require('unicode-' + version + '/properties/ID_Start/code-points')
.filter(function(ch) { return ch > 127; });
var cont = [0x200c, 0x200d].concat(require('unicode-' + version + '/properties/ID_Continue/code-points')
.filter(function(ch) { return ch > 127 && start.indexOf(ch) == -1; }));
function pad(str, width) {
while (str.length < width) str = "0" + str;
return str;
}
function esc(code) {
var hex = code.toString(16);
if (hex.length <= 2) return "\\x" + pad(hex, 2);
else return "\\u" + pad(hex, 4);
}
function generate(chars) {
var astral = [], re = "";
for (var i = 0, at = 0x10000; i < chars.length; i++) {
var from = chars[i], to = from;
while (i < chars.length - 1 && chars[i + 1] == to + 1) {
i++;
to++;
}
if (to <= 0xffff) {
if (from == to) re += esc(from);
else if (from + 1 == to) re += esc(from) + esc(to);
else re += esc(from) + "-" + esc(to);
} else {
astral.push(from - at, to - from);
at = to;
}
}
return {nonASCII: re, astral: astral};
}
var startData = generate(start), contData = generate(cont);
console.log(" var nonASCIIidentifierStartChars = \"" + startData.nonASCII + "\";");
console.log(" var nonASCIIidentifierChars = \"" + contData.nonASCII + "\";");
console.log(" var astralIdentifierStartCodes = " + JSON.stringify(startData.astral) + ";");
console.log(" var astralIdentifierCodes = " + JSON.stringify(contData.astral) + ";");

View File

@@ -0,0 +1,2 @@
node bin/build-acorn.js
node bin/without_eval > dist/acorn_csp.js

View File

@@ -0,0 +1,6 @@
# Combine existing list of authors with everyone known in git, sort, add header.
tail --lines=+3 AUTHORS > AUTHORS.tmp
git log --format='%aN' | grep -v abraidwood >> AUTHORS.tmp
echo -e "List of Acorn contributors. Updated before every release.\n" > AUTHORS
sort -u AUTHORS.tmp >> AUTHORS
rm -f AUTHORS.tmp

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env node
var fs = require("fs")
var acornSrc = fs.readFileSync(require.resolve("../dist/acorn"), "utf8")
var acorn = require("../dist/acorn"), walk = require("../dist/walk")
var ast = acorn.parse(acornSrc)
var touchups = [], uses = []
var makePred
walk.simple(ast, {
FunctionDeclaration: function(node) {
if (node.id.name == "makePredicate") {
makePred = node
touchups.push({text: "// Removed to create an eval-free library", from: node.start, to: node.end})
}
},
ObjectExpression: function(node) {
node.properties.forEach(function(prop) {
if (prop.value.type == "CallExpression" &&
prop.value.callee.name == "makePredicate")
uses.push(prop.value)
})
}
})
var results = []
var dryRun = acornSrc.slice(0, makePred.end) + "; makePredicate = (function(mp) {" +
"return function(words) { var r = mp(words); predicates.push(r); return r }})(makePredicate);" +
acornSrc.slice(makePred.end)
;(new Function("predicates", dryRun))(results)
uses.forEach(function (node, i) {
touchups.push({text: results[i].toString(), from: node.start, to: node.end})
})
var result = "", pos = 0
touchups.sort(function(a, b) { return a.from - b.from })
touchups.forEach(function(touchup) {
result += acornSrc.slice(pos, touchup.from)
result += touchup.text
pos = touchup.to
})
result += acornSrc.slice(pos)
process.stdout.write(result)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,342 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.acorn || (g.acorn = {})).walk = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
"use strict";
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
// AST walker module for Mozilla Parser API compatible trees
// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
// walk.simple(myTree, {
// Expression: function(node) { ... }
// });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.
exports.simple = simple;
// An ancestor walk builds up an array of ancestor nodes (including
// the current node) and passes them to the callback as the state parameter.
exports.ancestor = ancestor;
// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).
exports.recursive = recursive;
// Find a node with a given start, end, and type (all are optional,
// null can be used as wildcard). Returns a {node, state} object, or
// undefined when it doesn't find a matching node.
exports.findNodeAt = findNodeAt;
// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
exports.findNodeAround = findNodeAround;
// Find the outermost matching node after a given position.
exports.findNodeAfter = findNodeAfter;
// Find the outermost matching node before a given position.
exports.findNodeBefore = findNodeBefore;
// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.
exports.make = make;
exports.__esModule = true;
function simple(node, visitors, base, state) {
if (!base) base = exports.base;(function c(node, st, override) {
var type = override || node.type,
found = visitors[type];
base[type](node, st, c);
if (found) found(node, st);
})(node, state);
}
function ancestor(node, visitors, base, state) {
if (!base) base = exports.base;
if (!state) state = [];(function c(node, st, override) {
var type = override || node.type,
found = visitors[type];
if (node != st[st.length - 1]) {
st = st.slice();
st.push(node);
}
base[type](node, st, c);
if (found) found(node, st);
})(node, state);
}
function recursive(node, state, funcs, base) {
var visitor = funcs ? exports.make(funcs, base) : base;(function c(node, st, override) {
visitor[override || node.type](node, st, c);
})(node, state);
}
function makeTest(test) {
if (typeof test == "string") {
return function (type) {
return type == test;
};
} else if (!test) {
return function () {
return true;
};
} else {
return test;
}
}
var Found = function Found(node, state) {
_classCallCheck(this, Found);
this.node = node;this.state = state;
};
function findNodeAt(node, start, end, test, base, state) {
test = makeTest(test);
if (!base) base = exports.base;
try {
;(function c(node, st, override) {
var type = override || node.type;
if ((start == null || node.start <= start) && (end == null || node.end >= end)) base[type](node, st, c);
if (test(type, node) && (start == null || node.start == start) && (end == null || node.end == end)) throw new Found(node, st);
})(node, state);
} catch (e) {
if (e instanceof Found) {
return e;
}throw e;
}
}
function findNodeAround(node, pos, test, base, state) {
test = makeTest(test);
if (!base) base = exports.base;
try {
;(function c(node, st, override) {
var type = override || node.type;
if (node.start > pos || node.end < pos) {
return;
}base[type](node, st, c);
if (test(type, node)) throw new Found(node, st);
})(node, state);
} catch (e) {
if (e instanceof Found) {
return e;
}throw e;
}
}
function findNodeAfter(node, pos, test, base, state) {
test = makeTest(test);
if (!base) base = exports.base;
try {
;(function c(node, st, override) {
if (node.end < pos) {
return;
}var type = override || node.type;
if (node.start >= pos && test(type, node)) throw new Found(node, st);
base[type](node, st, c);
})(node, state);
} catch (e) {
if (e instanceof Found) {
return e;
}throw e;
}
}
function findNodeBefore(node, pos, test, base, state) {
test = makeTest(test);
if (!base) base = exports.base;
var max = undefined;(function c(node, st, override) {
if (node.start > pos) {
return;
}var type = override || node.type;
if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node)) max = new Found(node, st);
base[type](node, st, c);
})(node, state);
return max;
}
function make(funcs, base) {
if (!base) base = exports.base;
var visitor = {};
for (var type in base) visitor[type] = base[type];
for (var type in funcs) visitor[type] = funcs[type];
return visitor;
}
function skipThrough(node, st, c) {
c(node, st);
}
function ignore(_node, _st, _c) {}
// Node walkers.
var base = {};
exports.base = base;
base.Program = base.BlockStatement = function (node, st, c) {
for (var i = 0; i < node.body.length; ++i) {
c(node.body[i], st, "Statement");
}
};
base.Statement = skipThrough;
base.EmptyStatement = ignore;
base.ExpressionStatement = base.ParenthesizedExpression = function (node, st, c) {
return c(node.expression, st, "Expression");
};
base.IfStatement = function (node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Statement");
if (node.alternate) c(node.alternate, st, "Statement");
};
base.LabeledStatement = function (node, st, c) {
return c(node.body, st, "Statement");
};
base.BreakStatement = base.ContinueStatement = ignore;
base.WithStatement = function (node, st, c) {
c(node.object, st, "Expression");
c(node.body, st, "Statement");
};
base.SwitchStatement = function (node, st, c) {
c(node.discriminant, st, "Expression");
for (var i = 0; i < node.cases.length; ++i) {
var cs = node.cases[i];
if (cs.test) c(cs.test, st, "Expression");
for (var j = 0; j < cs.consequent.length; ++j) {
c(cs.consequent[j], st, "Statement");
}
}
};
base.ReturnStatement = base.YieldExpression = function (node, st, c) {
if (node.argument) c(node.argument, st, "Expression");
};
base.ThrowStatement = base.SpreadElement = base.RestElement = function (node, st, c) {
return c(node.argument, st, "Expression");
};
base.TryStatement = function (node, st, c) {
c(node.block, st, "Statement");
if (node.handler) c(node.handler.body, st, "ScopeBody");
if (node.finalizer) c(node.finalizer, st, "Statement");
};
base.WhileStatement = base.DoWhileStatement = function (node, st, c) {
c(node.test, st, "Expression");
c(node.body, st, "Statement");
};
base.ForStatement = function (node, st, c) {
if (node.init) c(node.init, st, "ForInit");
if (node.test) c(node.test, st, "Expression");
if (node.update) c(node.update, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInStatement = base.ForOfStatement = function (node, st, c) {
c(node.left, st, "ForInit");
c(node.right, st, "Expression");
c(node.body, st, "Statement");
};
base.ForInit = function (node, st, c) {
if (node.type == "VariableDeclaration") c(node, st);else c(node, st, "Expression");
};
base.DebuggerStatement = ignore;
base.FunctionDeclaration = function (node, st, c) {
return c(node, st, "Function");
};
base.VariableDeclaration = function (node, st, c) {
for (var i = 0; i < node.declarations.length; ++i) {
var decl = node.declarations[i];
if (decl.init) c(decl.init, st, "Expression");
}
};
base.Function = function (node, st, c) {
return c(node.body, st, "ScopeBody");
};
base.ScopeBody = function (node, st, c) {
return c(node, st, "Statement");
};
base.Expression = skipThrough;
base.ThisExpression = base.Super = base.MetaProperty = ignore;
base.ArrayExpression = base.ArrayPattern = function (node, st, c) {
for (var i = 0; i < node.elements.length; ++i) {
var elt = node.elements[i];
if (elt) c(elt, st, "Expression");
}
};
base.ObjectExpression = base.ObjectPattern = function (node, st, c) {
for (var i = 0; i < node.properties.length; ++i) {
c(node.properties[i], st);
}
};
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
base.SequenceExpression = base.TemplateLiteral = function (node, st, c) {
for (var i = 0; i < node.expressions.length; ++i) {
c(node.expressions[i], st, "Expression");
}
};
base.UnaryExpression = base.UpdateExpression = function (node, st, c) {
c(node.argument, st, "Expression");
};
base.BinaryExpression = base.AssignmentExpression = base.AssignmentPattern = base.LogicalExpression = function (node, st, c) {
c(node.left, st, "Expression");
c(node.right, st, "Expression");
};
base.ConditionalExpression = function (node, st, c) {
c(node.test, st, "Expression");
c(node.consequent, st, "Expression");
c(node.alternate, st, "Expression");
};
base.NewExpression = base.CallExpression = function (node, st, c) {
c(node.callee, st, "Expression");
if (node.arguments) for (var i = 0; i < node.arguments.length; ++i) {
c(node.arguments[i], st, "Expression");
}
};
base.MemberExpression = function (node, st, c) {
c(node.object, st, "Expression");
if (node.computed) c(node.property, st, "Expression");
};
base.ExportNamedDeclaration = base.ExportDefaultDeclaration = function (node, st, c) {
return c(node.declaration, st);
};
base.ImportDeclaration = function (node, st, c) {
for (var i = 0; i < node.specifiers.length; i++) {
c(node.specifiers[i], st);
}
};
base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.Literal = ignore;
base.TaggedTemplateExpression = function (node, st, c) {
c(node.tag, st, "Expression");
c(node.quasi, st);
};
base.ClassDeclaration = base.ClassExpression = function (node, st, c) {
if (node.superClass) c(node.superClass, st, "Expression");
for (var i = 0; i < node.body.body.length; i++) {
c(node.body.body[i], st);
}
};
base.MethodDefinition = base.Property = function (node, st, c) {
if (node.computed) c(node.key, st, "Expression");
c(node.value, st, "Expression");
};
base.ComprehensionExpression = function (node, st, c) {
for (var i = 0; i < node.blocks.length; i++) {
c(node.blocks[i].right, st, "Expression");
}c(node.body, st, "Expression");
};
},{}]},{},[1])(1)
});

View File

@@ -0,0 +1,166 @@
{
"name": "acorn",
"description": "ECMAScript parser",
"homepage": "https://github.com/marijnh/acorn",
"main": "dist/acorn.js",
"version": "1.2.2",
"engines": {
"node": ">=0.4.0"
},
"maintainers": [
{
"name": "marijn",
"email": "marijnh@gmail.com"
}
],
"repository": {
"type": "git",
"url": "git+https://github.com/marijnh/acorn.git"
},
"license": "MIT",
"scripts": {
"test": "node test/run.js",
"prepublish": "bin/prepublish.sh"
},
"bin": {
"acorn": "./bin/acorn"
},
"devDependencies": {
"babelify": "^5.0.4",
"browserify": "^9.0.3",
"browserify-derequire": "^0.9.4",
"unicode-7.0.0": "~0.1.5"
},
"contributors": [
{
"name": "List of Acorn contributors. Updated before every release."
},
{
"name": "Adrian Rakovsky"
},
{
"name": "Alistair Braidwood"
},
{
"name": "Andres Suarez"
},
{
"name": "Aparajita Fishman"
},
{
"name": "Arian Stolwijk"
},
{
"name": "Artem Govorov"
},
{
"name": "Brandon Mills"
},
{
"name": "Charles Hughes"
},
{
"name": "Conrad Irwin"
},
{
"name": "David Bonnet"
},
{
"name": "Forbes Lindesay"
},
{
"name": "Gilad Peleg"
},
{
"name": "impinball"
},
{
"name": "Ingvar Stepanyan"
},
{
"name": "Jiaxing Wang"
},
{
"name": "Johannes Herr"
},
{
"name": "Jürg Lehni"
},
{
"name": "keeyipchan"
},
{
"name": "krator"
},
{
"name": "Marijn Haverbeke"
},
{
"name": "Martin Carlberg"
},
{
"name": "Mathias Bynens"
},
{
"name": "Mathieu 'p01' Henri"
},
{
"name": "Max Schaefer"
},
{
"name": "Max Zerzouri"
},
{
"name": "Mihai Bazon"
},
{
"name": "Mike Rennie"
},
{
"name": "Nick Fitzgerald"
},
{
"name": "Oskar Schöldström"
},
{
"name": "Paul Harper"
},
{
"name": "Peter Rust"
},
{
"name": "PlNG"
},
{
"name": "r-e-d"
},
{
"name": "Rich Harris"
},
{
"name": "Sebastian McKenzie"
},
{
"name": "zsjforcn"
}
],
"gitHead": "0857d8bb9c3c05e6c8fac9e83fddaefc4f43816f",
"bugs": {
"url": "https://github.com/marijnh/acorn/issues"
},
"_id": "acorn@1.2.2",
"_shasum": "c8ce27de0acc76d896d2b1fad3df588d9e82f014",
"_from": "acorn@>=1.0.1 <2.0.0",
"_npmVersion": "1.4.21",
"_npmUser": {
"name": "marijn",
"email": "marijnh@gmail.com"
},
"dist": {
"shasum": "c8ce27de0acc76d896d2b1fad3df588d9e82f014",
"tarball": "http://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz",
"readme": "ERROR: No README data found!"
}

View File

@@ -0,0 +1,697 @@
// A recursive descent parser operates by defining functions for all
// syntactic elements, and recursively calling those, each function
// advancing the input stream and returning an AST node. Precedence
// of constructs (for example, the fact that `!x[1]` means `!(x[1])`
// instead of `(!x)[1]` is handled by the fact that the parser
// function that parses unary prefix operators is called first, and
// in turn calls the function that parses `[]` subscripts — that
// way, it'll receive the node for `x[1]` already parsed, and wraps
// *that* in the unary operator node.
//
// Acorn uses an [operator precedence parser][opp] to handle binary
// operator precedence, because it is much more compact than using
// the technique outlined above, which uses different, nesting
// functions to specify precedence, for all of the ten binary
// precedence levels that JavaScript defines.
//
// [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser
import {types as tt} from "./tokentype"
import {Parser} from "./state"
import {reservedWords} from "./identifier"
import {has} from "./util"
const pp = Parser.prototype
// Check if property name clashes with already added.
// Object/class getters and setters are not allowed to clash —
// either with each other or with an init property — and in
// strict mode, init properties are also not allowed to be repeated.
pp.checkPropClash = function(prop, propHash) {
if (this.options.ecmaVersion >= 6) return
let key = prop.key, name
switch (key.type) {
case "Identifier": name = key.name; break
case "Literal": name = String(key.value); break
default: return
}
let kind = prop.kind || "init", other
if (has(propHash, name)) {
other = propHash[name]
let isGetSet = kind !== "init"
if ((this.strict || isGetSet) && other[kind] || !(isGetSet ^ other.init))
this.raise(key.start, "Redefinition of property")
} else {
other = propHash[name] = {
init: false,
get: false,
set: false
}
}
other[kind] = true
}
// ### Expression parsing
// These nest, from the most general expression type at the top to
// 'atomic', nondivisible expression types at the bottom. Most of
// the functions will simply let the function(s) below them parse,
// and, *if* the syntactic construct they handle is present, wrap
// the AST node that the inner parser gave them in another node.
// Parse a full expression. The optional arguments are used to
// forbid the `in` operator (in for loops initalization expressions)
// and provide reference for storing '=' operator inside shorthand
// property assignment in contexts where both object expression
// and object pattern might appear (so it's possible to raise
// delayed syntax error at correct position).
pp.parseExpression = function(noIn, refShorthandDefaultPos) {
let startPos = this.start, startLoc = this.startLoc
let expr = this.parseMaybeAssign(noIn, refShorthandDefaultPos)
if (this.type === tt.comma) {
let node = this.startNodeAt(startPos, startLoc)
node.expressions = [expr]
while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn, refShorthandDefaultPos))
return this.finishNode(node, "SequenceExpression")
}
return expr
}
// Parse an assignment expression. This includes applications of
// operators like `+=`.
pp.parseMaybeAssign = function(noIn, refShorthandDefaultPos, afterLeftParse) {
if (this.type == tt._yield && this.inGenerator) return this.parseYield()
let failOnShorthandAssign
if (!refShorthandDefaultPos) {
refShorthandDefaultPos = {start: 0}
failOnShorthandAssign = true
} else {
failOnShorthandAssign = false
}
let startPos = this.start, startLoc = this.startLoc
if (this.type == tt.parenL || this.type == tt.name)
this.potentialArrowAt = this.start
let left = this.parseMaybeConditional(noIn, refShorthandDefaultPos)
if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc)
if (this.type.isAssign) {
let node = this.startNodeAt(startPos, startLoc)
node.operator = this.value
node.left = this.type === tt.eq ? this.toAssignable(left) : left
refShorthandDefaultPos.start = 0 // reset because shorthand default was used correctly
this.checkLVal(left)
this.next()
node.right = this.parseMaybeAssign(noIn)
return this.finishNode(node, "AssignmentExpression")
} else if (failOnShorthandAssign && refShorthandDefaultPos.start) {
this.unexpected(refShorthandDefaultPos.start)
}
return left
}
// Parse a ternary conditional (`?:`) operator.
pp.parseMaybeConditional = function(noIn, refShorthandDefaultPos) {
let startPos = this.start, startLoc = this.startLoc
let expr = this.parseExprOps(noIn, refShorthandDefaultPos)
if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
if (this.eat(tt.question)) {
let node = this.startNodeAt(startPos, startLoc)
node.test = expr
node.consequent = this.parseMaybeAssign()
this.expect(tt.colon)
node.alternate = this.parseMaybeAssign(noIn)
return this.finishNode(node, "ConditionalExpression")
}
return expr
}
// Start the precedence parser.
pp.parseExprOps = function(noIn, refShorthandDefaultPos) {
let startPos = this.start, startLoc = this.startLoc
let expr = this.parseMaybeUnary(refShorthandDefaultPos)
if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
return this.parseExprOp(expr, startPos, startLoc, -1, noIn)
}
// Parse binary operators with the operator precedence parsing
// algorithm. `left` is the left-hand side of the operator.
// `minPrec` provides context that allows the function to stop and
// defer further parser to one of its callers when it encounters an
// operator that has a lower precedence than the set it is parsing.
pp.parseExprOp = function(left, leftStartPos, leftStartLoc, minPrec, noIn) {
let prec = this.type.binop
if (Array.isArray(leftStartPos)){
if (this.options.locations && noIn === undefined) {
// shift arguments to left by one
noIn = minPrec
minPrec = leftStartLoc
// flatten leftStartPos
leftStartLoc = leftStartPos[1]
leftStartPos = leftStartPos[0]
}
}
if (prec != null && (!noIn || this.type !== tt._in)) {
if (prec > minPrec) {
let node = this.startNodeAt(leftStartPos, leftStartLoc)
node.left = left
node.operator = this.value
let op = this.type
this.next()
let startPos = this.start, startLoc = this.startLoc
node.right = this.parseExprOp(this.parseMaybeUnary(), startPos, startLoc, prec, noIn)
this.finishNode(node, (op === tt.logicalOR || op === tt.logicalAND) ? "LogicalExpression" : "BinaryExpression")
return this.parseExprOp(node, leftStartPos, leftStartLoc, minPrec, noIn)
}
}
return left
}
// Parse unary operators, both prefix and postfix.
pp.parseMaybeUnary = function(refShorthandDefaultPos) {
if (this.type.prefix) {
let node = this.startNode(), update = this.type === tt.incDec
node.operator = this.value
node.prefix = true
this.next()
node.argument = this.parseMaybeUnary()
if (refShorthandDefaultPos && refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start)
if (update) this.checkLVal(node.argument)
else if (this.strict && node.operator === "delete" &&
node.argument.type === "Identifier")
this.raise(node.start, "Deleting local variable in strict mode")
return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression")
}
let startPos = this.start, startLoc = this.startLoc
let expr = this.parseExprSubscripts(refShorthandDefaultPos)
if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
while (this.type.postfix && !this.canInsertSemicolon()) {
let node = this.startNodeAt(startPos, startLoc)
node.operator = this.value
node.prefix = false
node.argument = expr
this.checkLVal(expr)
this.next()
expr = this.finishNode(node, "UpdateExpression")
}
return expr
}
// Parse call, dot, and `[]`-subscript expressions.
pp.parseExprSubscripts = function(refShorthandDefaultPos) {
let startPos = this.start, startLoc = this.startLoc
let expr = this.parseExprAtom(refShorthandDefaultPos)
if (refShorthandDefaultPos && refShorthandDefaultPos.start) return expr
return this.parseSubscripts(expr, startPos, startLoc)
}
pp.parseSubscripts = function(base, startPos, startLoc, noCalls) {
if (Array.isArray(startPos)){
if (this.options.locations && noCalls === undefined) {
// shift arguments to left by one
noCalls = startLoc
// flatten startPos
startLoc = startPos[1]
startPos = startPos[0]
}
}
for (;;) {
if (this.eat(tt.dot)) {
let node = this.startNodeAt(startPos, startLoc)
node.object = base
node.property = this.parseIdent(true)
node.computed = false
base = this.finishNode(node, "MemberExpression")
} else if (this.eat(tt.bracketL)) {
let node = this.startNodeAt(startPos, startLoc)
node.object = base
node.property = this.parseExpression()
node.computed = true
this.expect(tt.bracketR)
base = this.finishNode(node, "MemberExpression")
} else if (!noCalls && this.eat(tt.parenL)) {
let node = this.startNodeAt(startPos, startLoc)
node.callee = base
node.arguments = this.parseExprList(tt.parenR, false)
base = this.finishNode(node, "CallExpression")
} else if (this.type === tt.backQuote) {
let node = this.startNodeAt(startPos, startLoc)
node.tag = base
node.quasi = this.parseTemplate()
base = this.finishNode(node, "TaggedTemplateExpression")
} else {
return base
}
}
}
// Parse an atomic expression — either a single token that is an
// expression, an expression started by a keyword like `function` or
// `new`, or an expression wrapped in punctuation like `()`, `[]`,
// or `{}`.
pp.parseExprAtom = function(refShorthandDefaultPos) {
let node, canBeArrow = this.potentialArrowAt == this.start
switch (this.type) {
case tt._this:
case tt._super:
let type = this.type === tt._this ? "ThisExpression" : "Super"
node = this.startNode()
this.next()
return this.finishNode(node, type)
case tt._yield:
if (this.inGenerator) this.unexpected()
case tt.name:
let startPos = this.start, startLoc = this.startLoc
let id = this.parseIdent(this.type !== tt.name)
if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow))
return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), [id])
return id
case tt.regexp:
let value = this.value
node = this.parseLiteral(value.value)
node.regex = {pattern: value.pattern, flags: value.flags}
return node
case tt.num: case tt.string:
return this.parseLiteral(this.value)
case tt._null: case tt._true: case tt._false:
node = this.startNode()
node.value = this.type === tt._null ? null : this.type === tt._true
node.raw = this.type.keyword
this.next()
return this.finishNode(node, "Literal")
case tt.parenL:
return this.parseParenAndDistinguishExpression(canBeArrow)
case tt.bracketL:
node = this.startNode()
this.next()
// check whether this is array comprehension or regular array
if (this.options.ecmaVersion >= 7 && this.type === tt._for) {
return this.parseComprehension(node, false)
}
node.elements = this.parseExprList(tt.bracketR, true, true, refShorthandDefaultPos)
return this.finishNode(node, "ArrayExpression")
case tt.braceL:
return this.parseObj(false, refShorthandDefaultPos)
case tt._function:
node = this.startNode()
this.next()
return this.parseFunction(node, false)
case tt._class:
return this.parseClass(this.startNode(), false)
case tt._new:
return this.parseNew()
case tt.backQuote:
return this.parseTemplate()
default:
this.unexpected()
}
}
pp.parseLiteral = function(value) {
let node = this.startNode()
node.value = value
node.raw = this.input.slice(this.start, this.end)
this.next()
return this.finishNode(node, "Literal")
}
pp.parseParenExpression = function() {
this.expect(tt.parenL)
let val = this.parseExpression()
this.expect(tt.parenR)
return val
}
pp.parseParenAndDistinguishExpression = function(canBeArrow) {
let startPos = this.start, startLoc = this.startLoc, val
if (this.options.ecmaVersion >= 6) {
this.next()
if (this.options.ecmaVersion >= 7 && this.type === tt._for) {
return this.parseComprehension(this.startNodeAt(startPos, startLoc), true)
}
let innerStartPos = this.start, innerStartLoc = this.startLoc
let exprList = [], first = true
let refShorthandDefaultPos = {start: 0}, spreadStart, innerParenStart
while (this.type !== tt.parenR) {
first ? first = false : this.expect(tt.comma)
if (this.type === tt.ellipsis) {
spreadStart = this.start
exprList.push(this.parseParenItem(this.parseRest()))
break
} else {
if (this.type === tt.parenL && !innerParenStart) {
innerParenStart = this.start
}
exprList.push(this.parseMaybeAssign(false, refShorthandDefaultPos, this.parseParenItem))
}
}
let innerEndPos = this.start, innerEndLoc = this.startLoc
this.expect(tt.parenR)
if (canBeArrow && !this.canInsertSemicolon() && this.eat(tt.arrow)) {
if (innerParenStart) this.unexpected(innerParenStart)
return this.parseParenArrowList(startPos, startLoc, exprList)
}
if (!exprList.length) this.unexpected(this.lastTokStart)
if (spreadStart) this.unexpected(spreadStart)
if (refShorthandDefaultPos.start) this.unexpected(refShorthandDefaultPos.start)
if (exprList.length > 1) {
val = this.startNodeAt(innerStartPos, innerStartLoc)
val.expressions = exprList
this.finishNodeAt(val, "SequenceExpression", innerEndPos, innerEndLoc)
} else {
val = exprList[0]
}
} else {
val = this.parseParenExpression()
}
if (this.options.preserveParens) {
let par = this.startNodeAt(startPos, startLoc)
par.expression = val
return this.finishNode(par, "ParenthesizedExpression")
} else {
return val
}
}
pp.parseParenItem = function(item) {
return item
}
pp.parseParenArrowList = function(startPos, startLoc, exprList) {
return this.parseArrowExpression(this.startNodeAt(startPos, startLoc), exprList)
}
// New's precedence is slightly tricky. It must allow its argument
// to be a `[]` or dot subscript expression, but not a call — at
// least, not without wrapping it in parentheses. Thus, it uses the
const empty = []
pp.parseNew = function() {
let node = this.startNode()
let meta = this.parseIdent(true)
if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) {
node.meta = meta
node.property = this.parseIdent(true)
if (node.property.name !== "target")
this.raise(node.property.start, "The only valid meta property for new is new.target")
return this.finishNode(node, "MetaProperty")
}
let startPos = this.start, startLoc = this.startLoc
node.callee = this.parseSubscripts(this.parseExprAtom(), startPos, startLoc, true)
if (this.eat(tt.parenL)) node.arguments = this.parseExprList(tt.parenR, false)
else node.arguments = empty
return this.finishNode(node, "NewExpression")
}
// Parse template expression.
pp.parseTemplateElement = function() {
let elem = this.startNode()
elem.value = {
raw: this.input.slice(this.start, this.end),
cooked: this.value
}
this.next()
elem.tail = this.type === tt.backQuote
return this.finishNode(elem, "TemplateElement")
}
pp.parseTemplate = function() {
let node = this.startNode()
this.next()
node.expressions = []
let curElt = this.parseTemplateElement()
node.quasis = [curElt]
while (!curElt.tail) {
this.expect(tt.dollarBraceL)
node.expressions.push(this.parseExpression())
this.expect(tt.braceR)
node.quasis.push(curElt = this.parseTemplateElement())
}
this.next()
return this.finishNode(node, "TemplateLiteral")
}
// Parse an object literal or binding pattern.
pp.parseObj = function(isPattern, refShorthandDefaultPos) {
let node = this.startNode(), first = true, propHash = {}
node.properties = []
this.next()
while (!this.eat(tt.braceR)) {
if (!first) {
this.expect(tt.comma)
if (this.afterTrailingComma(tt.braceR)) break
} else first = false
let prop = this.startNode(), isGenerator, startPos, startLoc
if (this.options.ecmaVersion >= 6) {
prop.method = false
prop.shorthand = false
if (isPattern || refShorthandDefaultPos) {
startPos = this.start
startLoc = this.startLoc
}
if (!isPattern)
isGenerator = this.eat(tt.star)
}
this.parsePropertyName(prop)
this.parsePropertyValue(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos)
this.checkPropClash(prop, propHash)
node.properties.push(this.finishNode(prop, "Property"))
}
return this.finishNode(node, isPattern ? "ObjectPattern" : "ObjectExpression")
}
pp.parsePropertyValue = function(prop, isPattern, isGenerator, startPos, startLoc, refShorthandDefaultPos) {
if (this.eat(tt.colon)) {
prop.value = isPattern ? this.parseMaybeDefault(this.start, this.startLoc) : this.parseMaybeAssign(false, refShorthandDefaultPos)
prop.kind = "init"
} else if (this.options.ecmaVersion >= 6 && this.type === tt.parenL) {
if (isPattern) this.unexpected()
prop.kind = "init"
prop.method = true
prop.value = this.parseMethod(isGenerator)
} else if (this.options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
(prop.key.name === "get" || prop.key.name === "set") &&
(this.type != tt.comma && this.type != tt.braceR)) {
if (isGenerator || isPattern) this.unexpected()
prop.kind = prop.key.name
this.parsePropertyName(prop)
prop.value = this.parseMethod(false)
} else if (this.options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
prop.kind = "init"
if (isPattern) {
if (this.isKeyword(prop.key.name) ||
(this.strict && (reservedWords.strictBind(prop.key.name) || reservedWords.strict(prop.key.name))) ||
(!this.options.allowReserved && this.isReservedWord(prop.key.name)))
this.raise(prop.key.start, "Binding " + prop.key.name)
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)
} else if (this.type === tt.eq && refShorthandDefaultPos) {
if (!refShorthandDefaultPos.start)
refShorthandDefaultPos.start = this.start
prop.value = this.parseMaybeDefault(startPos, startLoc, prop.key)
} else {
prop.value = prop.key
}
prop.shorthand = true
} else this.unexpected()
}
pp.parsePropertyName = function(prop) {
if (this.options.ecmaVersion >= 6) {
if (this.eat(tt.bracketL)) {
prop.computed = true
prop.key = this.parseMaybeAssign()
this.expect(tt.bracketR)
return prop.key
} else {
prop.computed = false
}
}
return prop.key = (this.type === tt.num || this.type === tt.string) ? this.parseExprAtom() : this.parseIdent(true)
}
// Initialize empty function node.
pp.initFunction = function(node) {
node.id = null
if (this.options.ecmaVersion >= 6) {
node.generator = false
node.expression = false
}
}
// Parse object or class method.
pp.parseMethod = function(isGenerator) {
let node = this.startNode()
this.initFunction(node)
this.expect(tt.parenL)
node.params = this.parseBindingList(tt.parenR, false, false)
let allowExpressionBody
if (this.options.ecmaVersion >= 6) {
node.generator = isGenerator
allowExpressionBody = true
} else {
allowExpressionBody = false
}
this.parseFunctionBody(node, allowExpressionBody)
return this.finishNode(node, "FunctionExpression")
}
// Parse arrow function expression with given parameters.
pp.parseArrowExpression = function(node, params) {
this.initFunction(node)
node.params = this.toAssignableList(params, true)
this.parseFunctionBody(node, true)
return this.finishNode(node, "ArrowFunctionExpression")
}
// Parse function body and check parameters.
pp.parseFunctionBody = function(node, allowExpression) {
let isExpression = allowExpression && this.type !== tt.braceL
if (isExpression) {
node.body = this.parseMaybeAssign()
node.expression = true
} else {
// Start a new scope with regard to labels and the `inFunction`
// flag (restore them to their old value afterwards).
let oldInFunc = this.inFunction, oldInGen = this.inGenerator, oldLabels = this.labels
this.inFunction = true; this.inGenerator = node.generator; this.labels = []
node.body = this.parseBlock(true)
node.expression = false
this.inFunction = oldInFunc; this.inGenerator = oldInGen; this.labels = oldLabels
}
// If this is a strict mode function, verify that argument names
// are not repeated, and it does not try to bind the words `eval`
// or `arguments`.
if (this.strict || !isExpression && node.body.body.length && this.isUseStrict(node.body.body[0])) {
let nameHash = {}, oldStrict = this.strict
this.strict = true
if (node.id)
this.checkLVal(node.id, true)
for (let i = 0; i < node.params.length; i++)
this.checkLVal(node.params[i], true, nameHash)
this.strict = oldStrict
}
}
// Parses a comma-separated list of expressions, and returns them as
// an array. `close` is the token type that ends the list, and
// `allowEmpty` can be turned on to allow subsequent commas with
// nothing in between them to be parsed as `null` (which is needed
// for array literals).
pp.parseExprList = function(close, allowTrailingComma, allowEmpty, refShorthandDefaultPos) {
let elts = [], first = true
while (!this.eat(close)) {
if (!first) {
this.expect(tt.comma)
if (allowTrailingComma && this.afterTrailingComma(close)) break
} else first = false
if (allowEmpty && this.type === tt.comma) {
elts.push(null)
} else {
if (this.type === tt.ellipsis)
elts.push(this.parseSpread(refShorthandDefaultPos))
else
elts.push(this.parseMaybeAssign(false, refShorthandDefaultPos))
}
}
return elts
}
// Parse the next token as an identifier. If `liberal` is true (used
// when parsing properties), it will also convert keywords into
// identifiers.
pp.parseIdent = function(liberal) {
let node = this.startNode()
if (liberal && this.options.allowReserved == "never") liberal = false
if (this.type === tt.name) {
if (!liberal &&
((!this.options.allowReserved && this.isReservedWord(this.value)) ||
(this.strict && reservedWords.strict(this.value)) &&
(this.options.ecmaVersion >= 6 ||
this.input.slice(this.start, this.end).indexOf("\\") == -1)))
this.raise(this.start, "The keyword '" + this.value + "' is reserved")
node.name = this.value
} else if (liberal && this.type.keyword) {
node.name = this.type.keyword
} else {
this.unexpected()
}
this.next()
return this.finishNode(node, "Identifier")
}
// Parses yield expression inside generator.
pp.parseYield = function() {
let node = this.startNode()
this.next()
if (this.type == tt.semi || this.canInsertSemicolon() || (this.type != tt.star && !this.type.startsExpr)) {
node.delegate = false
node.argument = null
} else {
node.delegate = this.eat(tt.star)
node.argument = this.parseMaybeAssign()
}
return this.finishNode(node, "YieldExpression")
}
// Parses array and generator comprehensions.
pp.parseComprehension = function(node, isGenerator) {
node.blocks = []
while (this.type === tt._for) {
let block = this.startNode()
this.next()
this.expect(tt.parenL)
block.left = this.parseBindingAtom()
this.checkLVal(block.left, true)
this.expectContextual("of")
block.right = this.parseExpression()
this.expect(tt.parenR)
node.blocks.push(this.finishNode(block, "ComprehensionBlock"))
}
node.filter = this.eat(tt._if) ? this.parseParenExpression() : null
node.body = this.parseExpression()
this.expect(isGenerator ? tt.parenR : tt.bracketR)
node.generator = isGenerator
return this.finishNode(node, "ComprehensionExpression")
}

View File

@@ -0,0 +1,129 @@
// This is a trick taken from Esprima. It turns out that, on
// non-Chrome browsers, to check whether a string is in a set, a
// predicate containing a big ugly `switch` statement is faster than
// a regular expression, and on Chrome the two are about on par.
// This function uses `eval` (non-lexical) to produce such a
// predicate from a space-separated string of words.
//
// It starts by sorting the words by length.
function makePredicate(words) {
words = words.split(" ")
let f = "", cats = []
out: for (let i = 0; i < words.length; ++i) {
for (let j = 0; j < cats.length; ++j)
if (cats[j][0].length == words[i].length) {
cats[j].push(words[i])
continue out
}
cats.push([words[i]])
}
function compareTo(arr) {
if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"
f += "switch(str){"
for (let i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"
f += "return true}return false;"
}
// When there are more than three length categories, an outer
// switch first dispatches on the lengths, to save on comparisons.
if (cats.length > 3) {
cats.sort((a, b) => b.length - a.length)
f += "switch(str.length){"
for (let i = 0; i < cats.length; ++i) {
let cat = cats[i]
f += "case " + cat[0].length + ":"
compareTo(cat)
}
f += "}"
// Otherwise, simply generate a flat `switch` statement.
} else {
compareTo(words)
}
return new Function("str", f)
}
// Reserved word lists for various dialects of the language
export const reservedWords = {
3: makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"),
5: makePredicate("class enum extends super const export import"),
6: makePredicate("enum await"),
strict: makePredicate("implements interface let package private protected public static yield"),
strictBind: makePredicate("eval arguments")
}
// And the keywords
var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"
export const keywords = {
5: makePredicate(ecma5AndLessKeywords),
6: makePredicate(ecma5AndLessKeywords + " let const class extends export import yield super")
}
// ## Character categories
// Big ugly regular expressions that match characters in the
// whitespace, identifier, and identifier-start categories. These
// are only applied when a character is found to actually have a
// code point above 128.
// Generated by `tools/generate-identifier-regex.js`.
let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0-\u08b2\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua7ad\ua7b0\ua7b1\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab5f\uab64\uab65\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"
let nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u08e4-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c03\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0d01-\u0d03\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19b0-\u19c0\u19c8\u19c9\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf2-\u1cf4\u1cf8\u1cf9\u1dc0-\u1df5\u1dfc-\u1dff\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua620-\ua629\ua66f\ua674-\ua67d\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua880\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f1\ua900-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2d\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"
const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]")
const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]")
nonASCIIidentifierStartChars = nonASCIIidentifierChars = null
// These are a run-length and offset encoded representation of the
// >0xffff code points that are a valid part of identifiers. The
// offset starts at 0x10000, and each pair of numbers represents an
// offset to the next range, and then a size of the range. They were
// generated by tools/generate-identifier-regex.js
var astralIdentifierStartCodes = [0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,17,26,6,37,11,29,3,35,5,7,2,4,43,157,99,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,98,21,11,25,71,55,7,1,65,0,16,3,2,2,2,26,45,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,955,52,76,44,33,24,27,35,42,34,4,0,13,47,15,3,22,0,38,17,2,24,133,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,32,4,287,47,21,1,2,0,185,46,82,47,21,0,60,42,502,63,32,0,449,56,1288,920,104,110,2962,1070,13266,568,8,30,114,29,19,47,17,3,32,20,6,18,881,68,12,0,67,12,16481,1,3071,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,4149,196,1340,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,16355,541]
var astralIdentifierCodes = [509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,1306,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,52,0,13,2,49,13,16,9,83,11,168,11,6,9,8,2,57,0,2,6,3,1,3,2,10,0,11,1,3,6,4,4,316,19,13,9,214,6,3,8,112,16,16,9,82,12,9,9,535,9,20855,9,135,4,60,6,26,9,1016,45,17,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,4305,6,792618,239]
// This has a complexity linear to the value of the code. The
// assumption is that looking up astral identifier characters is
// rare.
function isInAstralSet(code, set) {
let pos = 0x10000
for (let i = 0; i < set.length; i += 2) {
pos += set[i]
if (pos > code) return false
pos += set[i + 1]
if (pos >= code) return true
}
}
// Test whether a given character code starts an identifier.
export function isIdentifierStart(code, astral) {
if (code < 65) return code === 36
if (code < 91) return true
if (code < 97) return code === 95
if (code < 123) return true
if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code))
if (astral === false) return false
return isInAstralSet(code, astralIdentifierStartCodes)
}
// Test whether a given character is part of an identifier.
export function isIdentifierChar(code, astral) {
if (code < 48) return code === 36
if (code < 58) return true
if (code < 65) return false
if (code < 91) return true
if (code < 97) return code === 95
if (code < 123) return true
if (code <= 0xffff) return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code))
if (astral === false) return false
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes)
}

View File

@@ -0,0 +1,75 @@
// Acorn is a tiny, fast JavaScript parser written in JavaScript.
//
// Acorn was written by Marijn Haverbeke, Ingvar Stepanyan, and
// various contributors and released under an MIT license.
//
// Git repositories for Acorn are available at
//
// http://marijnhaverbeke.nl/git/acorn
// https://github.com/marijnh/acorn.git
//
// Please use the [github bug tracker][ghbt] to report issues.
//
// [ghbt]: https://github.com/marijnh/acorn/issues
//
// This file defines the main parser interface. The library also comes
// with a [error-tolerant parser][dammit] and an
// [abstract syntax tree walker][walk], defined in other files.
//
// [dammit]: acorn_loose.js
// [walk]: util/walk.js
import {Parser} from "./state"
import {getOptions} from "./options"
import "./parseutil"
import "./statement"
import "./lval"
import "./expression"
export {Parser, plugins} from "./state"
export {defaultOptions} from "./options"
export {SourceLocation} from "./location"
export {getLineInfo} from "./location"
export {Node} from "./node"
export {TokenType, types as tokTypes} from "./tokentype"
export {TokContext, types as tokContexts} from "./tokencontext"
export {isIdentifierChar, isIdentifierStart} from "./identifier"
export {Token} from "./tokenize"
export {isNewLine, lineBreak, lineBreakG} from "./whitespace"
export const version = "1.2.2"
// The main exported interface (under `self.acorn` when in the
// browser) is a `parse` function that takes a code string and
// returns an abstract syntax tree as specified by [Mozilla parser
// API][api].
//
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
export function parse(input, options) {
let p = parser(options, input)
let startPos = p.pos, startLoc = p.options.locations && p.curPosition()
p.nextToken()
return p.parseTopLevel(p.options.program || p.startNodeAt(startPos, startLoc))
}
// This function tries to parse a single expression at a given
// offset in a string. Useful for parsing mixed-language formats
// that embed JavaScript expressions.
export function parseExpressionAt(input, pos, options) {
let p = parser(options, input, pos)
p.nextToken()
return p.parseExpression()
}
// Acorn is organized as a tokenizer and a recursive-descent parser.
// The `tokenize` export provides an interface to the tokenizer.
export function tokenizer(input, options) {
return parser(options, input)
}
function parser(options, input) {
return new Parser(getOptions(options), String(input))
}

View File

@@ -0,0 +1,68 @@
import {Parser} from "./state"
import {lineBreakG} from "./whitespace"
import {deprecate} from "util"
// These are used when `options.locations` is on, for the
// `startLoc` and `endLoc` properties.
export class Position {
constructor(line, col) {
this.line = line
this.column = col
}
offset(n) {
return new Position(this.line, this.column + n)
}
}
export class SourceLocation {
constructor(p, start, end) {
this.start = start
this.end = end
if (p.sourceFile !== null) this.source = p.sourceFile
}
}
// The `getLineInfo` function is mostly useful when the
// `locations` option is off (for performance reasons) and you
// want to find the line/column position for a given character
// offset. `input` should be the code string that the offset refers
// into.
export function getLineInfo(input, offset) {
for (let line = 1, cur = 0;;) {
lineBreakG.lastIndex = cur
let match = lineBreakG.exec(input)
if (match && match.index < offset) {
++line
cur = match.index + match[0].length
} else {
return new Position(line, offset - cur)
}
}
}
const pp = Parser.prototype
// This function is used to raise exceptions on parse errors. It
// takes an offset integer (into the current `input`) to indicate
// the location of the error, attaches the position to the end
// of the error message, and then raises a `SyntaxError` with that
// message.
pp.raise = function(pos, message) {
let loc = getLineInfo(this.input, pos)
message += " (" + loc.line + ":" + loc.column + ")"
let err = new SyntaxError(message)
err.pos = pos; err.loc = loc; err.raisedAt = this.pos
throw err
}
pp.curPosition = function() {
return new Position(this.curLine, this.pos - this.lineStart)
}
pp.markPosition = function() {
return this.options.locations ? [this.start, this.startLoc] : this.start
}

View File

@@ -0,0 +1,511 @@
import {LooseParser} from "./state"
import {isDummy} from "./parseutil"
import {tokTypes as tt} from ".."
const lp = LooseParser.prototype
lp.checkLVal = function(expr, binding) {
if (!expr) return expr
switch (expr.type) {
case "Identifier":
return expr
case "MemberExpression":
return binding ? this.dummyIdent() : expr
case "ParenthesizedExpression":
expr.expression = this.checkLVal(expr.expression, binding)
return expr
// FIXME recursively check contents
case "ObjectPattern":
case "ArrayPattern":
case "RestElement":
case "AssignmentPattern":
if (this.options.ecmaVersion >= 6) return expr
default:
return this.dummyIdent()
}
}
lp.parseExpression = function(noIn) {
let start = this.storeCurrentPos()
let expr = this.parseMaybeAssign(noIn)
if (this.tok.type === tt.comma) {
let node = this.startNodeAt(start)
node.expressions = [expr]
while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(noIn))
return this.finishNode(node, "SequenceExpression")
}
return expr
}
lp.parseParenExpression = function() {
this.pushCx()
this.expect(tt.parenL)
let val = this.parseExpression()
this.popCx()
this.expect(tt.parenR)
return val
}
lp.parseMaybeAssign = function(noIn) {
let start = this.storeCurrentPos()
let left = this.parseMaybeConditional(noIn)
if (this.tok.type.isAssign) {
let node = this.startNodeAt(start)
node.operator = this.tok.value
node.left = this.tok.type === tt.eq ? this.toAssignable(left) : this.checkLVal(left)
this.next()
node.right = this.parseMaybeAssign(noIn)
return this.finishNode(node, "AssignmentExpression")
}
return left
}
lp.parseMaybeConditional = function(noIn) {
let start = this.storeCurrentPos()
let expr = this.parseExprOps(noIn)
if (this.eat(tt.question)) {
let node = this.startNodeAt(start)
node.test = expr
node.consequent = this.parseMaybeAssign()
node.alternate = this.expect(tt.colon) ? this.parseMaybeAssign(noIn) : this.dummyIdent()
return this.finishNode(node, "ConditionalExpression")
}
return expr
}
lp.parseExprOps = function(noIn) {
let start = this.storeCurrentPos()
let indent = this.curIndent, line = this.curLineStart
return this.parseExprOp(this.parseMaybeUnary(noIn), start, -1, noIn, indent, line)
}
lp.parseExprOp = function(left, start, minPrec, noIn, indent, line) {
if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) return left
let prec = this.tok.type.binop
if (prec != null && (!noIn || this.tok.type !== tt._in)) {
if (prec > minPrec) {
let node = this.startNodeAt(start)
node.left = left
node.operator = this.tok.value
this.next()
if (this.curLineStart != line && this.curIndent < indent && this.tokenStartsLine()) {
node.right = this.dummyIdent()
} else {
let rightStart = this.storeCurrentPos()
node.right = this.parseExprOp(this.parseMaybeUnary(noIn), rightStart, prec, noIn, indent, line)
}
this.finishNode(node, /&&|\|\|/.test(node.operator) ? "LogicalExpression" : "BinaryExpression")
return this.parseExprOp(node, start, minPrec, noIn, indent, line)
}
}
return left
}
lp.parseMaybeUnary = function(noIn) {
if (this.tok.type.prefix) {
let node = this.startNode(), update = this.tok.type === tt.incDec
node.operator = this.tok.value
node.prefix = true
this.next()
node.argument = this.parseMaybeUnary(noIn)
if (update) node.argument = this.checkLVal(node.argument)
return this.finishNode(node, update ? "UpdateExpression" : "UnaryExpression")
} else if (this.tok.type === tt.ellipsis) {
let node = this.startNode()
this.next()
node.argument = this.parseMaybeUnary(noIn)
return this.finishNode(node, "SpreadElement")
}
let start = this.storeCurrentPos()
let expr = this.parseExprSubscripts()
while (this.tok.type.postfix && !this.canInsertSemicolon()) {
let node = this.startNodeAt(start)
node.operator = this.tok.value
node.prefix = false
node.argument = this.checkLVal(expr)
this.next()
expr = this.finishNode(node, "UpdateExpression")
}
return expr
}
lp.parseExprSubscripts = function() {
let start = this.storeCurrentPos()
return this.parseSubscripts(this.parseExprAtom(), start, false, this.curIndent, this.curLineStart)
}
lp.parseSubscripts = function(base, start, noCalls, startIndent, line) {
for (;;) {
if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine()) {
if (this.tok.type == tt.dot && this.curIndent == startIndent)
--startIndent
else
return base
}
if (this.eat(tt.dot)) {
let node = this.startNodeAt(start)
node.object = base
if (this.curLineStart != line && this.curIndent <= startIndent && this.tokenStartsLine())
node.property = this.dummyIdent()
else
node.property = this.parsePropertyAccessor() || this.dummyIdent()
node.computed = false
base = this.finishNode(node, "MemberExpression")
} else if (this.tok.type == tt.bracketL) {
this.pushCx()
this.next()
let node = this.startNodeAt(start)
node.object = base
node.property = this.parseExpression()
node.computed = true
this.popCx()
this.expect(tt.bracketR)
base = this.finishNode(node, "MemberExpression")
} else if (!noCalls && this.tok.type == tt.parenL) {
let node = this.startNodeAt(start)
node.callee = base
node.arguments = this.parseExprList(tt.parenR)
base = this.finishNode(node, "CallExpression")
} else if (this.tok.type == tt.backQuote) {
let node = this.startNodeAt(start)
node.tag = base
node.quasi = this.parseTemplate()
base = this.finishNode(node, "TaggedTemplateExpression")
} else {
return base
}
}
}
lp.parseExprAtom = function() {
let node
switch (this.tok.type) {
case tt._this:
case tt._super:
let type = this.tok.type === tt._this ? "ThisExpression" : "Super"
node = this.startNode()
this.next()
return this.finishNode(node, type)
case tt.name:
let start = this.storeCurrentPos()
let id = this.parseIdent()
return this.eat(tt.arrow) ? this.parseArrowExpression(this.startNodeAt(start), [id]) : id
case tt.regexp:
node = this.startNode()
let val = this.tok.value
node.regex = {pattern: val.pattern, flags: val.flags}
node.value = val.value
node.raw = this.input.slice(this.tok.start, this.tok.end)
this.next()
return this.finishNode(node, "Literal")
case tt.num: case tt.string:
node = this.startNode()
node.value = this.tok.value
node.raw = this.input.slice(this.tok.start, this.tok.end)
this.next()
return this.finishNode(node, "Literal")
case tt._null: case tt._true: case tt._false:
node = this.startNode()
node.value = this.tok.type === tt._null ? null : this.tok.type === tt._true
node.raw = this.tok.type.keyword
this.next()
return this.finishNode(node, "Literal")
case tt.parenL:
let parenStart = this.storeCurrentPos()
this.next()
let inner = this.parseExpression()
this.expect(tt.parenR)
if (this.eat(tt.arrow)) {
return this.parseArrowExpression(this.startNodeAt(parenStart), inner.expressions || (isDummy(inner) ? [] : [inner]))
}
if (this.options.preserveParens) {
let par = this.startNodeAt(parenStart)
par.expression = inner
inner = this.finishNode(par, "ParenthesizedExpression")
}
return inner
case tt.bracketL:
node = this.startNode()
node.elements = this.parseExprList(tt.bracketR, true)
return this.finishNode(node, "ArrayExpression")
case tt.braceL:
return this.parseObj()
case tt._class:
return this.parseClass()
case tt._function:
node = this.startNode()
this.next()
return this.parseFunction(node, false)
case tt._new:
return this.parseNew()
case tt._yield:
node = this.startNode()
this.next()
if (this.semicolon() || this.canInsertSemicolon() || (this.tok.type != tt.star && !this.tok.type.startsExpr)) {
node.delegate = false
node.argument = null
} else {
node.delegate = this.eat(tt.star)
node.argument = this.parseMaybeAssign()
}
return this.finishNode(node, "YieldExpression")
case tt.backQuote:
return this.parseTemplate()
default:
return this.dummyIdent()
}
}
lp.parseNew = function() {
let node = this.startNode(), startIndent = this.curIndent, line = this.curLineStart
let meta = this.parseIdent(true)
if (this.options.ecmaVersion >= 6 && this.eat(tt.dot)) {
node.meta = meta
node.property = this.parseIdent(true)
return this.finishNode(node, "MetaProperty")
}
let start = this.storeCurrentPos()
node.callee = this.parseSubscripts(this.parseExprAtom(), start, true, startIndent, line)
if (this.tok.type == tt.parenL) {
node.arguments = this.parseExprList(tt.parenR)
} else {
node.arguments = []
}
return this.finishNode(node, "NewExpression")
}
lp.parseTemplateElement = function() {
let elem = this.startNode()
elem.value = {
raw: this.input.slice(this.tok.start, this.tok.end),
cooked: this.tok.value
}
this.next()
elem.tail = this.tok.type === tt.backQuote
return this.finishNode(elem, "TemplateElement")
}
lp.parseTemplate = function() {
let node = this.startNode()
this.next()
node.expressions = []
let curElt = this.parseTemplateElement()
node.quasis = [curElt]
while (!curElt.tail) {
this.next()
node.expressions.push(this.parseExpression())
if (this.expect(tt.braceR)) {
curElt = this.parseTemplateElement()
} else {
curElt = this.startNode()
curElt.value = {cooked: '', raw: ''}
curElt.tail = true
}
node.quasis.push(curElt)
}
this.expect(tt.backQuote)
return this.finishNode(node, "TemplateLiteral")
}
lp.parseObj = function() {
let node = this.startNode()
node.properties = []
this.pushCx()
let indent = this.curIndent + 1, line = this.curLineStart
this.eat(tt.braceL)
if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart }
while (!this.closes(tt.braceR, indent, line)) {
let prop = this.startNode(), isGenerator, start
if (this.options.ecmaVersion >= 6) {
start = this.storeCurrentPos()
prop.method = false
prop.shorthand = false
isGenerator = this.eat(tt.star)
}
this.parsePropertyName(prop)
if (isDummy(prop.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue }
if (this.eat(tt.colon)) {
prop.kind = "init"
prop.value = this.parseMaybeAssign()
} else if (this.options.ecmaVersion >= 6 && (this.tok.type === tt.parenL || this.tok.type === tt.braceL)) {
prop.kind = "init"
prop.method = true
prop.value = this.parseMethod(isGenerator)
} else if (this.options.ecmaVersion >= 5 && prop.key.type === "Identifier" &&
!prop.computed && (prop.key.name === "get" || prop.key.name === "set") &&
(this.tok.type != tt.comma && this.tok.type != tt.braceR)) {
prop.kind = prop.key.name
this.parsePropertyName(prop)
prop.value = this.parseMethod(false)
} else {
prop.kind = "init"
if (this.options.ecmaVersion >= 6) {
if (this.eat(tt.eq)) {
let assign = this.startNodeAt(start)
assign.operator = "="
assign.left = prop.key
assign.right = this.parseMaybeAssign()
prop.value = this.finishNode(assign, "AssignmentExpression")
} else {
prop.value = prop.key
}
} else {
prop.value = this.dummyIdent()
}
prop.shorthand = true
}
node.properties.push(this.finishNode(prop, "Property"))
this.eat(tt.comma)
}
this.popCx()
if (!this.eat(tt.braceR)) {
// If there is no closing brace, make the node span to the start
// of the next token (this is useful for Tern)
this.last.end = this.tok.start
if (this.options.locations) this.last.loc.end = this.tok.loc.start
}
return this.finishNode(node, "ObjectExpression")
}
lp.parsePropertyName = function(prop) {
if (this.options.ecmaVersion >= 6) {
if (this.eat(tt.bracketL)) {
prop.computed = true
prop.key = this.parseExpression()
this.expect(tt.bracketR)
return
} else {
prop.computed = false
}
}
let key = (this.tok.type === tt.num || this.tok.type === tt.string) ? this.parseExprAtom() : this.parseIdent()
prop.key = key || this.dummyIdent()
}
lp.parsePropertyAccessor = function() {
if (this.tok.type === tt.name || this.tok.type.keyword) return this.parseIdent()
}
lp.parseIdent = function() {
let name = this.tok.type === tt.name ? this.tok.value : this.tok.type.keyword
if (!name) return this.dummyIdent()
let node = this.startNode()
this.next()
node.name = name
return this.finishNode(node, "Identifier")
}
lp.initFunction = function(node) {
node.id = null
node.params = []
if (this.options.ecmaVersion >= 6) {
node.generator = false
node.expression = false
}
}
// Convert existing expression atom to assignable pattern
// if possible.
lp.toAssignable = function(node, binding) {
if (this.options.ecmaVersion >= 6 && node) {
switch (node.type) {
case "ObjectExpression":
node.type = "ObjectPattern"
let props = node.properties
for (let i = 0; i < props.length; i++)
this.toAssignable(props[i].value, binding)
break
case "ArrayExpression":
node.type = "ArrayPattern"
this.toAssignableList(node.elements, binding)
break
case "SpreadElement":
node.type = "RestElement"
node.argument = this.toAssignable(node.argument, binding)
break
case "AssignmentExpression":
node.type = "AssignmentPattern"
break
}
}
return this.checkLVal(node, binding)
}
lp.toAssignableList = function(exprList, binding) {
for (let i = 0; i < exprList.length; i++)
exprList[i] = this.toAssignable(exprList[i], binding)
return exprList
}
lp.parseFunctionParams = function(params) {
params = this.parseExprList(tt.parenR)
return this.toAssignableList(params, true)
}
lp.parseMethod = function(isGenerator) {
let node = this.startNode()
this.initFunction(node)
node.params = this.parseFunctionParams()
node.generator = isGenerator || false
node.expression = this.options.ecmaVersion >= 6 && this.tok.type !== tt.braceL
node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock()
return this.finishNode(node, "FunctionExpression")
}
lp.parseArrowExpression = function(node, params) {
this.initFunction(node)
node.params = this.toAssignableList(params, true)
node.expression = this.tok.type !== tt.braceL
node.body = node.expression ? this.parseMaybeAssign() : this.parseBlock()
return this.finishNode(node, "ArrowFunctionExpression")
}
lp.parseExprList = function(close, allowEmpty) {
this.pushCx()
let indent = this.curIndent, line = this.curLineStart, elts = []
this.next(); // Opening bracket
while (!this.closes(close, indent + 1, line)) {
if (this.eat(tt.comma)) {
elts.push(allowEmpty ? null : this.dummyIdent())
continue
}
let elt = this.parseMaybeAssign()
if (isDummy(elt)) {
if (this.closes(close, indent, line)) break
this.next()
} else {
elts.push(elt)
}
this.eat(tt.comma)
}
this.popCx()
if (!this.eat(close)) {
// If there is no closing brace, make the node span to the start
// of the next token (this is useful for Tern)
this.last.end = this.tok.start
if (this.options.locations) this.last.loc.end = this.tok.loc.start
}
return elts
}

View File

@@ -0,0 +1,50 @@
// Acorn: Loose parser
//
// This module provides an alternative parser (`parse_dammit`) that
// exposes that same interface as `parse`, but will try to parse
// anything as JavaScript, repairing syntax error the best it can.
// There are circumstances in which it will raise an error and give
// up, but they are very rare. The resulting AST will be a mostly
// valid JavaScript AST (as per the [Mozilla parser API][api], except
// that:
//
// - Return outside functions is allowed
//
// - Label consistency (no conflicts, break only to existing labels)
// is not enforced.
//
// - Bogus Identifier nodes with a name of `"✖"` are inserted whenever
// the parser got too confused to return anything meaningful.
//
// [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
//
// The expected use for this is to *first* try `acorn.parse`, and only
// if that fails switch to `parse_dammit`. The loose parser might
// parse badly indented code incorrectly, so **don't** use it as
// your default parser.
//
// Quite a lot of acorn.js is duplicated here. The alternative was to
// add a *lot* of extra cruft to that file, making it less readable
// and slower. Copying and editing the code allowed me to make
// invasive changes and simplifications without creating a complicated
// tangle.
import * as acorn from ".."
import {LooseParser} from "./state"
import "./tokenize"
import "./parseutil"
import "./statement"
import "./expression"
export {LooseParser} from "./state"
acorn.defaultOptions.tabSize = 4
export function parse_dammit(input, options) {
let p = new LooseParser(input, options)
p.next()
return p.parseTopLevel()
}
acorn.parse_dammit = parse_dammit
acorn.LooseParser = LooseParser

View File

@@ -0,0 +1,126 @@
import {LooseParser} from "./state"
import {Node, SourceLocation, lineBreak, isNewLine, tokTypes as tt} from ".."
const lp = LooseParser.prototype
lp.startNode = function() {
let node = new Node
node.start = this.tok.start
if (this.options.locations)
node.loc = new SourceLocation(this.toks, this.tok.loc.start)
if (this.options.directSourceFile)
node.sourceFile = this.options.directSourceFile
if (this.options.ranges)
node.range = [this.tok.start, 0]
return node
}
lp.storeCurrentPos = function() {
return this.options.locations ? [this.tok.start, this.tok.loc.start] : this.tok.start
}
lp.startNodeAt = function(pos) {
let node = new Node
if (this.options.locations) {
node.start = pos[0]
node.loc = new SourceLocation(this.toks, pos[1])
pos = pos[0]
} else {
node.start = pos
}
if (this.options.directSourceFile)
node.sourceFile = this.options.directSourceFile
if (this.options.ranges)
node.range = [pos, 0]
return node
}
lp.finishNode = function(node, type) {
node.type = type
node.end = this.last.end
if (this.options.locations)
node.loc.end = this.last.loc.end
if (this.options.ranges)
node.range[1] = this.last.end
return node
}
lp.dummyIdent = function() {
let dummy = this.startNode()
dummy.name = "✖"
return this.finishNode(dummy, "Identifier")
}
export function isDummy(node) { return node.name == "✖" }
lp.eat = function(type) {
if (this.tok.type === type) {
this.next()
return true
} else {
return false
}
}
lp.isContextual = function(name) {
return this.tok.type === tt.name && this.tok.value === name
}
lp.eatContextual = function(name) {
return this.tok.value === name && this.eat(tt.name)
}
lp.canInsertSemicolon = function() {
return this.tok.type === tt.eof || this.tok.type === tt.braceR ||
lineBreak.test(this.input.slice(this.last.end, this.tok.start))
}
lp.semicolon = function() {
return this.eat(tt.semi)
}
lp.expect = function(type) {
if (this.eat(type)) return true
for (let i = 1; i <= 2; i++) {
if (this.lookAhead(i).type == type) {
for (let j = 0; j < i; j++) this.next()
return true
}
}
}
lp.pushCx = function() {
this.context.push(this.curIndent)
}
lp.popCx = function() {
this.curIndent = this.context.pop()
}
lp.lineEnd = function(pos) {
while (pos < this.input.length && !isNewLine(this.input.charCodeAt(pos))) ++pos
return pos
}
lp.indentationAfter = function(pos) {
for (let count = 0;; ++pos) {
let ch = this.input.charCodeAt(pos)
if (ch === 32) ++count
else if (ch === 9) count += this.options.tabSize
else return count
}
}
lp.closes = function(closeTok, indent, line, blockHeuristic) {
if (this.tok.type === closeTok || this.tok.type === tt.eof) return true
return line != this.curLineStart && this.curIndent < indent && this.tokenStartsLine() &&
(!blockHeuristic || this.nextLineStart >= this.input.length ||
this.indentationAfter(this.nextLineStart) < indent)
}
lp.tokenStartsLine = function() {
for (let p = this.tok.start - 1; p >= this.curLineStart; --p) {
let ch = this.input.charCodeAt(p)
if (ch !== 9 && ch !== 32) return false
}
return true
}

View File

@@ -0,0 +1,17 @@
import {tokenizer, SourceLocation, tokTypes as tt} from ".."
export function LooseParser(input, options) {
this.toks = tokenizer(input, options)
this.options = this.toks.options
this.input = this.toks.input
this.tok = this.last = {type: tt.eof, start: 0, end: 0}
if (this.options.locations) {
let here = this.toks.curPosition()
this.tok.loc = new SourceLocation(this.toks, here, here)
}
this.ahead = []; // Tokens ahead
this.context = []; // Indentation contexted
this.curIndent = 0
this.curLineStart = 0
this.nextLineStart = this.lineEnd(this.curLineStart) + 1
}

View File

@@ -0,0 +1,419 @@
import {LooseParser} from "./state"
import {isDummy} from "./parseutil"
import {getLineInfo, tokTypes as tt} from ".."
const lp = LooseParser.prototype
lp.parseTopLevel = function() {
let node = this.startNodeAt(this.options.locations ? [0, getLineInfo(this.input, 0)] : 0)
node.body = []
while (this.tok.type !== tt.eof) node.body.push(this.parseStatement())
this.last = this.tok
if (this.options.ecmaVersion >= 6) {
node.sourceType = this.options.sourceType
}
return this.finishNode(node, "Program")
}
lp.parseStatement = function() {
let starttype = this.tok.type, node = this.startNode()
switch (starttype) {
case tt._break: case tt._continue:
this.next()
let isBreak = starttype === tt._break
if (this.semicolon() || this.canInsertSemicolon()) {
node.label = null
} else {
node.label = this.tok.type === tt.name ? this.parseIdent() : null
this.semicolon()
}
return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
case tt._debugger:
this.next()
this.semicolon()
return this.finishNode(node, "DebuggerStatement")
case tt._do:
this.next()
node.body = this.parseStatement()
node.test = this.eat(tt._while) ? this.parseParenExpression() : this.dummyIdent()
this.semicolon()
return this.finishNode(node, "DoWhileStatement")
case tt._for:
this.next()
this.pushCx()
this.expect(tt.parenL)
if (this.tok.type === tt.semi) return this.parseFor(node, null)
if (this.tok.type === tt._var || this.tok.type === tt._let || this.tok.type === tt._const) {
let init = this.parseVar(true)
if (init.declarations.length === 1 && (this.tok.type === tt._in || this.isContextual("of"))) {
return this.parseForIn(node, init)
}
return this.parseFor(node, init)
}
let init = this.parseExpression(true)
if (this.tok.type === tt._in || this.isContextual("of"))
return this.parseForIn(node, this.toAssignable(init))
return this.parseFor(node, init)
case tt._function:
this.next()
return this.parseFunction(node, true)
case tt._if:
this.next()
node.test = this.parseParenExpression()
node.consequent = this.parseStatement()
node.alternate = this.eat(tt._else) ? this.parseStatement() : null
return this.finishNode(node, "IfStatement")
case tt._return:
this.next()
if (this.eat(tt.semi) || this.canInsertSemicolon()) node.argument = null
else { node.argument = this.parseExpression(); this.semicolon() }
return this.finishNode(node, "ReturnStatement")
case tt._switch:
let blockIndent = this.curIndent, line = this.curLineStart
this.next()
node.discriminant = this.parseParenExpression()
node.cases = []
this.pushCx()
this.expect(tt.braceL)
let cur
while (!this.closes(tt.braceR, blockIndent, line, true)) {
if (this.tok.type === tt._case || this.tok.type === tt._default) {
let isCase = this.tok.type === tt._case
if (cur) this.finishNode(cur, "SwitchCase")
node.cases.push(cur = this.startNode())
cur.consequent = []
this.next()
if (isCase) cur.test = this.parseExpression()
else cur.test = null
this.expect(tt.colon)
} else {
if (!cur) {
node.cases.push(cur = this.startNode())
cur.consequent = []
cur.test = null
}
cur.consequent.push(this.parseStatement())
}
}
if (cur) this.finishNode(cur, "SwitchCase")
this.popCx()
this.eat(tt.braceR)
return this.finishNode(node, "SwitchStatement")
case tt._throw:
this.next()
node.argument = this.parseExpression()
this.semicolon()
return this.finishNode(node, "ThrowStatement")
case tt._try:
this.next()
node.block = this.parseBlock()
node.handler = null
if (this.tok.type === tt._catch) {
let clause = this.startNode()
this.next()
this.expect(tt.parenL)
clause.param = this.toAssignable(this.parseExprAtom(), true)
this.expect(tt.parenR)
clause.guard = null
clause.body = this.parseBlock()
node.handler = this.finishNode(clause, "CatchClause")
}
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null
if (!node.handler && !node.finalizer) return node.block
return this.finishNode(node, "TryStatement")
case tt._var:
case tt._let:
case tt._const:
return this.parseVar()
case tt._while:
this.next()
node.test = this.parseParenExpression()
node.body = this.parseStatement()
return this.finishNode(node, "WhileStatement")
case tt._with:
this.next()
node.object = this.parseParenExpression()
node.body = this.parseStatement()
return this.finishNode(node, "WithStatement")
case tt.braceL:
return this.parseBlock()
case tt.semi:
this.next()
return this.finishNode(node, "EmptyStatement")
case tt._class:
return this.parseClass(true)
case tt._import:
return this.parseImport()
case tt._export:
return this.parseExport()
default:
let expr = this.parseExpression()
if (isDummy(expr)) {
this.next()
if (this.tok.type === tt.eof) return this.finishNode(node, "EmptyStatement")
return this.parseStatement()
} else if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon)) {
node.body = this.parseStatement()
node.label = expr
return this.finishNode(node, "LabeledStatement")
} else {
node.expression = expr
this.semicolon()
return this.finishNode(node, "ExpressionStatement")
}
}
}
lp.parseBlock = function() {
let node = this.startNode()
this.pushCx()
this.expect(tt.braceL)
let blockIndent = this.curIndent, line = this.curLineStart
node.body = []
while (!this.closes(tt.braceR, blockIndent, line, true))
node.body.push(this.parseStatement())
this.popCx()
this.eat(tt.braceR)
return this.finishNode(node, "BlockStatement")
}
lp.parseFor = function(node, init) {
node.init = init
node.test = node.update = null
if (this.eat(tt.semi) && this.tok.type !== tt.semi) node.test = this.parseExpression()
if (this.eat(tt.semi) && this.tok.type !== tt.parenR) node.update = this.parseExpression()
this.popCx()
this.expect(tt.parenR)
node.body = this.parseStatement()
return this.finishNode(node, "ForStatement")
}
lp.parseForIn = function(node, init) {
let type = this.tok.type === tt._in ? "ForInStatement" : "ForOfStatement"
this.next()
node.left = init
node.right = this.parseExpression()
this.popCx()
this.expect(tt.parenR)
node.body = this.parseStatement()
return this.finishNode(node, type)
}
lp.parseVar = function(noIn) {
let node = this.startNode()
node.kind = this.tok.type.keyword
this.next()
node.declarations = []
do {
let decl = this.startNode()
decl.id = this.options.ecmaVersion >= 6 ? this.toAssignable(this.parseExprAtom(), true) : this.parseIdent()
decl.init = this.eat(tt.eq) ? this.parseMaybeAssign(noIn) : null
node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
} while (this.eat(tt.comma))
if (!node.declarations.length) {
let decl = this.startNode()
decl.id = this.dummyIdent()
node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
}
if (!noIn) this.semicolon()
return this.finishNode(node, "VariableDeclaration")
}
lp.parseClass = function(isStatement) {
let node = this.startNode()
this.next()
if (this.tok.type === tt.name) node.id = this.parseIdent()
else if (isStatement) node.id = this.dummyIdent()
else node.id = null
node.superClass = this.eat(tt._extends) ? this.parseExpression() : null
node.body = this.startNode()
node.body.body = []
this.pushCx()
let indent = this.curIndent + 1, line = this.curLineStart
this.eat(tt.braceL)
if (this.curIndent + 1 < indent) { indent = this.curIndent; line = this.curLineStart }
while (!this.closes(tt.braceR, indent, line)) {
if (this.semicolon()) continue
let method = this.startNode(), isGenerator
if (this.options.ecmaVersion >= 6) {
method.static = false
isGenerator = this.eat(tt.star)
}
this.parsePropertyName(method)
if (isDummy(method.key)) { if (isDummy(this.parseMaybeAssign())) this.next(); this.eat(tt.comma); continue }
if (method.key.type === "Identifier" && !method.computed && method.key.name === "static" &&
(this.tok.type != tt.parenL && this.tok.type != tt.braceL)) {
method.static = true
isGenerator = this.eat(tt.star)
this.parsePropertyName(method)
} else {
method.static = false
}
if (this.options.ecmaVersion >= 5 && method.key.type === "Identifier" &&
!method.computed && (method.key.name === "get" || method.key.name === "set") &&
this.tok.type !== tt.parenL && this.tok.type !== tt.braceL) {
method.kind = method.key.name
this.parsePropertyName(method)
method.value = this.parseMethod(false)
} else {
if (!method.computed && !method.static && !isGenerator && (
method.key.type === "Identifier" && method.key.name === "constructor" ||
method.key.type === "Literal" && method.key.value === "constructor")) {
method.kind = "constructor"
} else {
method.kind = "method"
}
method.value = this.parseMethod(isGenerator)
}
node.body.body.push(this.finishNode(method, "MethodDefinition"))
}
this.popCx()
if (!this.eat(tt.braceR)) {
// If there is no closing brace, make the node span to the start
// of the next token (this is useful for Tern)
this.last.end = this.tok.start
if (this.options.locations) this.last.loc.end = this.tok.loc.start
}
this.semicolon()
this.finishNode(node.body, "ClassBody")
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
}
lp.parseFunction = function(node, isStatement) {
this.initFunction(node)
if (this.options.ecmaVersion >= 6) {
node.generator = this.eat(tt.star)
}
if (this.tok.type === tt.name) node.id = this.parseIdent()
else if (isStatement) node.id = this.dummyIdent()
node.params = this.parseFunctionParams()
node.body = this.parseBlock()
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
}
lp.parseExport = function() {
let node = this.startNode()
this.next()
if (this.eat(tt.star)) {
node.source = this.eatContextual("from") ? this.parseExprAtom() : null
return this.finishNode(node, "ExportAllDeclaration")
}
if (this.eat(tt._default)) {
let expr = this.parseMaybeAssign()
if (expr.id) {
switch (expr.type) {
case "FunctionExpression": expr.type = "FunctionDeclaration"; break
case "ClassExpression": expr.type = "ClassDeclaration"; break
}
}
node.declaration = expr
this.semicolon()
return this.finishNode(node, "ExportDefaultDeclaration")
}
if (this.tok.type.keyword) {
node.declaration = this.parseStatement()
node.specifiers = []
node.source = null
} else {
node.declaration = null
node.specifiers = this.parseExportSpecifierList()
node.source = this.eatContextual("from") ? this.parseExprAtom() : null
this.semicolon()
}
return this.finishNode(node, "ExportNamedDeclaration")
}
lp.parseImport = function() {
let node = this.startNode()
this.next()
if (this.tok.type === tt.string) {
node.specifiers = []
node.source = this.parseExprAtom()
node.kind = ''
} else {
let elt
if (this.tok.type === tt.name && this.tok.value !== "from") {
elt = this.startNode()
elt.local = this.parseIdent()
this.finishNode(elt, "ImportDefaultSpecifier")
this.eat(tt.comma)
}
node.specifiers = this.parseImportSpecifierList()
node.source = this.eatContextual("from") ? this.parseExprAtom() : null
if (elt) node.specifiers.unshift(elt)
}
this.semicolon()
return this.finishNode(node, "ImportDeclaration")
}
lp.parseImportSpecifierList = function() {
let elts = []
if (this.tok.type === tt.star) {
let elt = this.startNode()
this.next()
if (this.eatContextual("as")) elt.local = this.parseIdent()
elts.push(this.finishNode(elt, "ImportNamespaceSpecifier"))
} else {
let indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart
this.pushCx()
this.eat(tt.braceL)
if (this.curLineStart > continuedLine) continuedLine = this.curLineStart
while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
let elt = this.startNode()
if (this.eat(tt.star)) {
if (this.eatContextual("as")) elt.local = this.parseIdent()
this.finishNode(elt, "ImportNamespaceSpecifier")
} else {
if (this.isContextual("from")) break
elt.imported = this.parseIdent()
elt.local = this.eatContextual("as") ? this.parseIdent() : elt.imported
this.finishNode(elt, "ImportSpecifier")
}
elts.push(elt)
this.eat(tt.comma)
}
this.eat(tt.braceR)
this.popCx()
}
return elts
}
lp.parseExportSpecifierList = function() {
let elts = []
let indent = this.curIndent, line = this.curLineStart, continuedLine = this.nextLineStart
this.pushCx()
this.eat(tt.braceL)
if (this.curLineStart > continuedLine) continuedLine = this.curLineStart
while (!this.closes(tt.braceR, indent + (this.curLineStart <= continuedLine ? 1 : 0), line)) {
if (this.isContextual("from")) break
let elt = this.startNode()
elt.local = this.parseIdent()
elt.exported = this.eatContextual("as") ? this.parseIdent() : elt.local
this.finishNode(elt, "ExportSpecifier")
elts.push(elt)
this.eat(tt.comma)
}
this.eat(tt.braceR)
this.popCx()
return elts
}

View File

@@ -0,0 +1,108 @@
import {tokTypes as tt, Token, isNewLine, SourceLocation, getLineInfo, lineBreakG} from ".."
import {LooseParser} from "./state"
const lp = LooseParser.prototype
function isSpace(ch) {
return (ch < 14 && ch > 8) || ch === 32 || ch === 160 || isNewLine(ch)
}
lp.next = function() {
this.last = this.tok
if (this.ahead.length)
this.tok = this.ahead.shift()
else
this.tok = this.readToken()
if (this.tok.start >= this.nextLineStart) {
while (this.tok.start >= this.nextLineStart) {
this.curLineStart = this.nextLineStart
this.nextLineStart = this.lineEnd(this.curLineStart) + 1
}
this.curIndent = this.indentationAfter(this.curLineStart)
}
}
lp.readToken = function() {
for (;;) {
try {
this.toks.next()
if (this.toks.type === tt.dot &&
this.input.substr(this.toks.end, 1) === "." &&
this.options.ecmaVersion >= 6) {
this.toks.end++
this.toks.type = tt.ellipsis
}
return new Token(this.toks)
} catch(e) {
if (!(e instanceof SyntaxError)) throw e
// Try to skip some text, based on the error message, and then continue
let msg = e.message, pos = e.raisedAt, replace = true
if (/unterminated/i.test(msg)) {
pos = this.lineEnd(e.pos + 1)
if (/string/.test(msg)) {
replace = {start: e.pos, end: pos, type: tt.string, value: this.input.slice(e.pos + 1, pos)}
} else if (/regular expr/i.test(msg)) {
let re = this.input.slice(e.pos, pos)
try { re = new RegExp(re) } catch(e) {}
replace = {start: e.pos, end: pos, type: tt.regexp, value: re}
} else if (/template/.test(msg)) {
replace = {start: e.pos, end: pos,
type: tt.template,
value: this.input.slice(e.pos, pos)}
} else {
replace = false
}
} else if (/invalid (unicode|regexp|number)|expecting unicode|octal literal|is reserved|directly after number|expected number in radix/i.test(msg)) {
while (pos < this.input.length && !isSpace(this.input.charCodeAt(pos))) ++pos
} else if (/character escape|expected hexadecimal/i.test(msg)) {
while (pos < this.input.length) {
let ch = this.input.charCodeAt(pos++)
if (ch === 34 || ch === 39 || isNewLine(ch)) break
}
} else if (/unexpected character/i.test(msg)) {
pos++
replace = false
} else if (/regular expression/i.test(msg)) {
replace = true
} else {
throw e
}
this.resetTo(pos)
if (replace === true) replace = {start: pos, end: pos, type: tt.name, value: "✖"}
if (replace) {
if (this.options.locations)
replace.loc = new SourceLocation(
this.toks,
getLineInfo(this.input, replace.start),
getLineInfo(this.input, replace.end))
return replace
}
}
}
}
lp.resetTo = function(pos) {
this.toks.pos = pos
let ch = this.input.charAt(pos - 1)
this.toks.exprAllowed = !ch || /[\[\{\(,;:?\/*=+\-~!|&%^<>]/.test(ch) ||
/[enwfd]/.test(ch) &&
/\b(keywords|case|else|return|throw|new|in|(instance|type)of|delete|void)$/.test(this.input.slice(pos - 10, pos))
if (this.options.locations) {
this.toks.curLine = 1
this.toks.lineStart = lineBreakG.lastIndex = 0
let match
while ((match = lineBreakG.exec(this.input)) && match.index < pos) {
++this.toks.curLine
this.toks.lineStart = match.index + match[0].length
}
}
}
lp.lookAhead = function(n) {
while (n > this.ahead.length)
this.ahead.push(this.readToken())
return this.ahead[n - 1]
}

View File

@@ -0,0 +1,213 @@
import {types as tt} from "./tokentype"
import {Parser} from "./state"
import {reservedWords} from "./identifier"
import {has} from "./util"
const pp = Parser.prototype
// Convert existing expression atom to assignable pattern
// if possible.
pp.toAssignable = function(node, isBinding) {
if (this.options.ecmaVersion >= 6 && node) {
switch (node.type) {
case "Identifier":
case "ObjectPattern":
case "ArrayPattern":
case "AssignmentPattern":
break
case "ObjectExpression":
node.type = "ObjectPattern"
for (let i = 0; i < node.properties.length; i++) {
let prop = node.properties[i]
if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter")
this.toAssignable(prop.value, isBinding)
}
break
case "ArrayExpression":
node.type = "ArrayPattern"
this.toAssignableList(node.elements, isBinding)
break
case "AssignmentExpression":
if (node.operator === "=") {
node.type = "AssignmentPattern"
} else {
this.raise(node.left.end, "Only '=' operator can be used for specifying default value.")
}
break
case "ParenthesizedExpression":
node.expression = this.toAssignable(node.expression, isBinding)
break
case "MemberExpression":
if (!isBinding) break
default:
this.raise(node.start, "Assigning to rvalue")
}
}
return node
}
// Convert list of expression atoms to binding list.
pp.toAssignableList = function(exprList, isBinding) {
let end = exprList.length
if (end) {
let last = exprList[end - 1]
if (last && last.type == "RestElement") {
--end
} else if (last && last.type == "SpreadElement") {
last.type = "RestElement"
let arg = last.argument
this.toAssignable(arg, isBinding)
if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern")
this.unexpected(arg.start)
--end
}
}
for (let i = 0; i < end; i++) {
let elt = exprList[i]
if (elt) this.toAssignable(elt, isBinding)
}
return exprList
}
// Parses spread element.
pp.parseSpread = function(refShorthandDefaultPos) {
let node = this.startNode()
this.next()
node.argument = this.parseMaybeAssign(refShorthandDefaultPos)
return this.finishNode(node, "SpreadElement")
}
pp.parseRest = function() {
let node = this.startNode()
this.next()
node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected()
return this.finishNode(node, "RestElement")
}
// Parses lvalue (assignable) atom.
pp.parseBindingAtom = function() {
if (this.options.ecmaVersion < 6) return this.parseIdent()
switch (this.type) {
case tt.name:
return this.parseIdent()
case tt.bracketL:
let node = this.startNode()
this.next()
node.elements = this.parseBindingList(tt.bracketR, true, true)
return this.finishNode(node, "ArrayPattern")
case tt.braceL:
return this.parseObj(true)
default:
this.unexpected()
}
}
pp.parseBindingList = function(close, allowEmpty, allowTrailingComma) {
let elts = [], first = true
while (!this.eat(close)) {
if (first) first = false
else this.expect(tt.comma)
if (allowEmpty && this.type === tt.comma) {
elts.push(null)
} else if (allowTrailingComma && this.afterTrailingComma(close)) {
break
} else if (this.type === tt.ellipsis) {
let rest = this.parseRest()
this.parseBindingListItem(rest)
elts.push(rest)
this.expect(close)
break
} else {
let elem = this.parseMaybeDefault(this.start, this.startLoc)
this.parseBindingListItem(elem)
elts.push(elem)
}
}
return elts
}
pp.parseBindingListItem = function(param) {
return param
}
// Parses assignment pattern around given atom if possible.
pp.parseMaybeDefault = function(startPos, startLoc, left) {
if (Array.isArray(startPos)){
if (this.options.locations && noCalls === undefined) {
// shift arguments to left by one
left = startLoc
// flatten startPos
startLoc = startPos[1]
startPos = startPos[0]
}
}
left = left || this.parseBindingAtom()
if (!this.eat(tt.eq)) return left
let node = this.startNodeAt(startPos, startLoc)
node.operator = "="
node.left = left
node.right = this.parseMaybeAssign()
return this.finishNode(node, "AssignmentPattern")
}
// Verify that a node is an lval — something that can be assigned
// to.
pp.checkLVal = function(expr, isBinding, checkClashes) {
switch (expr.type) {
case "Identifier":
if (this.strict && (reservedWords.strictBind(expr.name) || reservedWords.strict(expr.name)))
this.raise(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode")
if (checkClashes) {
if (has(checkClashes, expr.name))
this.raise(expr.start, "Argument name clash in strict mode")
checkClashes[expr.name] = true
}
break
case "MemberExpression":
if (isBinding) this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression")
break
case "ObjectPattern":
for (let i = 0; i < expr.properties.length; i++)
this.checkLVal(expr.properties[i].value, isBinding, checkClashes)
break
case "ArrayPattern":
for (let i = 0; i < expr.elements.length; i++) {
let elem = expr.elements[i]
if (elem) this.checkLVal(elem, isBinding, checkClashes)
}
break
case "AssignmentPattern":
this.checkLVal(expr.left, isBinding, checkClashes)
break
case "RestElement":
this.checkLVal(expr.argument, isBinding, checkClashes)
break
case "ParenthesizedExpression":
this.checkLVal(expr.expression, isBinding, checkClashes)
break
default:
this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue")
}
}

View File

@@ -0,0 +1,70 @@
import {Parser} from "./state"
import {SourceLocation} from "./location"
// Start an AST node, attaching a start offset.
const pp = Parser.prototype
export class Node {}
pp.startNode = function() {
let node = new Node
node.start = this.start
if (this.options.locations)
node.loc = new SourceLocation(this, this.startLoc)
if (this.options.directSourceFile)
node.sourceFile = this.options.directSourceFile
if (this.options.ranges)
node.range = [this.start, 0]
return node
}
pp.startNodeAt = function(pos, loc) {
let node = new Node
if (Array.isArray(pos)){
if (this.options.locations && loc === undefined) {
// flatten pos
loc = pos[1]
pos = pos[0]
}
}
node.start = pos
if (this.options.locations)
node.loc = new SourceLocation(this, loc)
if (this.options.directSourceFile)
node.sourceFile = this.options.directSourceFile
if (this.options.ranges)
node.range = [pos, 0]
return node
}
// Finish an AST node, adding `type` and `end` properties.
pp.finishNode = function(node, type) {
node.type = type
node.end = this.lastTokEnd
if (this.options.locations)
node.loc.end = this.lastTokEndLoc
if (this.options.ranges)
node.range[1] = this.lastTokEnd
return node
}
// Finish node at given position
pp.finishNodeAt = function(node, type, pos, loc) {
node.type = type
if (Array.isArray(pos)){
if (this.options.locations && loc === undefined) {
// flatten pos
loc = pos[1]
pos = pos[0]
}
}
node.end = pos
if (this.options.locations)
node.loc.end = loc
if (this.options.ranges)
node.range[1] = pos
return node
}

View File

@@ -0,0 +1,119 @@
import {has, isArray} from "./util"
import {SourceLocation} from "./location"
// A second optional argument can be given to further configure
// the parser process. These options are recognized:
export const defaultOptions = {
// `ecmaVersion` indicates the ECMAScript version to parse. Must
// be either 3, or 5, or 6. This influences support for strict
// mode, the set of reserved words, support for getters and
// setters and other features.
ecmaVersion: 5,
// Source type ("script" or "module") for different semantics
sourceType: "script",
// `onInsertedSemicolon` can be a callback that will be called
// when a semicolon is automatically inserted. It will be passed
// th position of the comma as an offset, and if `locations` is
// enabled, it is given the location as a `{line, column}` object
// as second argument.
onInsertedSemicolon: null,
// `onTrailingComma` is similar to `onInsertedSemicolon`, but for
// trailing commas.
onTrailingComma: null,
// By default, reserved words are not enforced. Disable
// `allowReserved` to enforce them. When this option has the
// value "never", reserved words and keywords can also not be
// used as property names.
allowReserved: true,
// When enabled, a return at the top level is not considered an
// error.
allowReturnOutsideFunction: false,
// When enabled, import/export statements are not constrained to
// appearing at the top of the program.
allowImportExportEverywhere: false,
// When enabled, hashbang directive in the beginning of file
// is allowed and treated as a line comment.
allowHashBang: false,
// When `locations` is on, `loc` properties holding objects with
// `start` and `end` properties in `{line, column}` form (with
// line being 1-based and column 0-based) will be attached to the
// nodes.
locations: false,
// A function can be passed as `onToken` option, which will
// cause Acorn to call that function with object in the same
// format as tokenize() returns. Note that you are not
// allowed to call the parser from the callback—that will
// corrupt its internal state.
onToken: null,
// A function can be passed as `onComment` option, which will
// cause Acorn to call that function with `(block, text, start,
// end)` parameters whenever a comment is skipped. `block` is a
// boolean indicating whether this is a block (`/* */`) comment,
// `text` is the content of the comment, and `start` and `end` are
// character offsets that denote the start and end of the comment.
// When the `locations` option is on, two more parameters are
// passed, the full `{line, column}` locations of the start and
// end of the comments. Note that you are not allowed to call the
// parser from the callback—that will corrupt its internal state.
onComment: null,
// Nodes have their start and end characters offsets recorded in
// `start` and `end` properties (directly on the node, rather than
// the `loc` object, which holds line/column data. To also add a
// [semi-standardized][range] `range` property holding a `[start,
// end]` array with the same numbers, set the `ranges` option to
// `true`.
//
// [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
ranges: false,
// It is possible to parse multiple files into a single AST by
// passing the tree produced by parsing the first file as
// `program` option in subsequent parses. This will add the
// toplevel forms of the parsed file to the `Program` (top) node
// of an existing parse tree.
program: null,
// When `locations` is on, you can pass this to record the source
// file in every node's `loc` object.
sourceFile: null,
// This value, if given, is stored in every node, whether
// `locations` is on or off.
directSourceFile: null,
// When enabled, parenthesized expressions are represented by
// (non-standard) ParenthesizedExpression nodes
preserveParens: false,
plugins: {}
}
// Interpret and default an options object
export function getOptions(opts) {
let options = {}
for (let opt in defaultOptions)
options[opt] = opts && has(opts, opt) ? opts[opt] : defaultOptions[opt]
if (isArray(options.onToken)) {
let tokens = options.onToken
options.onToken = (token) => tokens.push(token)
}
if (isArray(options.onComment))
options.onComment = pushComment(options, options.onComment)
return options
}
function pushComment(options, array) {
return function (block, text, start, end, startLoc, endLoc) {
let comment = {
type: block ? 'Block' : 'Line',
value: text,
start: start,
end: end
}
if (options.locations)
comment.loc = new SourceLocation(this, startLoc, endLoc)
if (options.ranges)
comment.range = [start, end]
array.push(comment)
}
}

View File

@@ -0,0 +1,89 @@
import {types as tt} from "./tokentype"
import {Parser} from "./state"
import {lineBreak} from "./whitespace"
const pp = Parser.prototype
// ## Parser utilities
// Test whether a statement node is the string literal `"use strict"`.
pp.isUseStrict = function(stmt) {
return this.options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" &&
stmt.expression.type === "Literal" && stmt.expression.value === "use strict"
}
// Predicate that tests whether the next token is of the given
// type, and if yes, consumes it as a side effect.
pp.eat = function(type) {
if (this.type === type) {
this.next()
return true
} else {
return false
}
}
// Tests whether parsed token is a contextual keyword.
pp.isContextual = function(name) {
return this.type === tt.name && this.value === name
}
// Consumes contextual keyword if possible.
pp.eatContextual = function(name) {
return this.value === name && this.eat(tt.name)
}
// Asserts that following token is given contextual keyword.
pp.expectContextual = function(name) {
if (!this.eatContextual(name)) this.unexpected()
}
// Test whether a semicolon can be inserted at the current position.
pp.canInsertSemicolon = function() {
return this.type === tt.eof ||
this.type === tt.braceR ||
lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
}
pp.insertSemicolon = function() {
if (this.canInsertSemicolon()) {
if (this.options.onInsertedSemicolon)
this.options.onInsertedSemicolon(this.lastTokEnd, this.lastTokEndLoc)
return true
}
}
// Consume a semicolon, or, failing that, see if we are allowed to
// pretend that there is a semicolon at this position.
pp.semicolon = function() {
if (!this.eat(tt.semi) && !this.insertSemicolon()) this.unexpected()
}
pp.afterTrailingComma = function(tokType) {
if (this.type == tokType) {
if (this.options.onTrailingComma)
this.options.onTrailingComma(this.lastTokStart, this.lastTokStartLoc)
this.next()
return true
}
}
// Expect a token of a given type. If found, consume it, otherwise,
// raise an unexpected token error.
pp.expect = function(type) {
this.eat(type) || this.unexpected()
}
// Raise an unexpected token error.
pp.unexpected = function(pos) {
this.raise(pos != null ? pos : this.start, "Unexpected token")
}

View File

@@ -0,0 +1,78 @@
import {reservedWords, keywords} from "./identifier"
import {types as tt} from "./tokentype"
import {lineBreak} from "./whitespace"
export function Parser(options, input, startPos) {
this.options = options
this.sourceFile = this.options.sourceFile || null
this.isKeyword = keywords[this.options.ecmaVersion >= 6 ? 6 : 5]
this.isReservedWord = reservedWords[this.options.ecmaVersion]
this.input = input
// Load plugins
this.loadPlugins(this.options.plugins)
// Set up token state
// The current position of the tokenizer in the input.
if (startPos) {
this.pos = startPos
this.lineStart = Math.max(0, this.input.lastIndexOf("\n", startPos))
this.curLine = this.input.slice(0, this.lineStart).split(lineBreak).length
} else {
this.pos = this.lineStart = 0
this.curLine = 1
}
// Properties of the current token:
// Its type
this.type = tt.eof
// For tokens that include more information than their type, the value
this.value = null
// Its start and end offset
this.start = this.end = this.pos
// And, if locations are used, the {line, column} object
// corresponding to those offsets
this.startLoc = this.endLoc = null
// Position information for the previous token
this.lastTokEndLoc = this.lastTokStartLoc = null
this.lastTokStart = this.lastTokEnd = this.pos
// The context stack is used to superficially track syntactic
// context to predict whether a regular expression is allowed in a
// given position.
this.context = this.initialContext()
this.exprAllowed = true
// Figure out if it's a module code.
this.strict = this.inModule = this.options.sourceType === "module"
// Used to signify the start of a potential arrow function
this.potentialArrowAt = -1
// Flags to track whether we are in a function, a generator.
this.inFunction = this.inGenerator = false
// Labels in scope.
this.labels = []
// If enabled, skip leading hashbang line.
if (this.pos === 0 && this.options.allowHashBang && this.input.slice(0, 2) === '#!')
this.skipLineComment(2)
}
Parser.prototype.extend = function(name, f) {
this[name] = f(this[name])
}
// Registered plugins
export const plugins = {}
Parser.prototype.loadPlugins = function(plugins) {
for (let name in plugins) {
let plugin = exports.plugins[name]
if (!plugin) throw new Error("Plugin '" + name + "' not found")
plugin(this, plugins[name])
}
}

View File

@@ -0,0 +1,594 @@
import {types as tt} from "./tokentype"
import {Parser} from "./state"
import {lineBreak} from "./whitespace"
const pp = Parser.prototype
// ### Statement parsing
// Parse a program. Initializes the parser, reads any number of
// statements, and wraps them in a Program node. Optionally takes a
// `program` argument. If present, the statements will be appended
// to its body instead of creating a new node.
pp.parseTopLevel = function(node) {
let first = true
if (!node.body) node.body = []
while (this.type !== tt.eof) {
let stmt = this.parseStatement(true, true)
node.body.push(stmt)
if (first && this.isUseStrict(stmt)) this.setStrict(true)
first = false
}
this.next()
if (this.options.ecmaVersion >= 6) {
node.sourceType = this.options.sourceType
}
return this.finishNode(node, "Program")
}
const loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"}
// Parse a single statement.
//
// If expecting a statement and finding a slash operator, parse a
// regular expression literal. This is to handle cases like
// `if (foo) /blah/.exec(foo)`, where looking at the previous token
// does not help.
pp.parseStatement = function(declaration, topLevel) {
let starttype = this.type, node = this.startNode()
// Most types of statements are recognized by the keyword they
// start with. Many are trivial to parse, some require a bit of
// complexity.
switch (starttype) {
case tt._break: case tt._continue: return this.parseBreakContinueStatement(node, starttype.keyword)
case tt._debugger: return this.parseDebuggerStatement(node)
case tt._do: return this.parseDoStatement(node)
case tt._for: return this.parseForStatement(node)
case tt._function:
if (!declaration && this.options.ecmaVersion >= 6) this.unexpected()
return this.parseFunctionStatement(node)
case tt._class:
if (!declaration) this.unexpected()
return this.parseClass(node, true)
case tt._if: return this.parseIfStatement(node)
case tt._return: return this.parseReturnStatement(node)
case tt._switch: return this.parseSwitchStatement(node)
case tt._throw: return this.parseThrowStatement(node)
case tt._try: return this.parseTryStatement(node)
case tt._let: case tt._const: if (!declaration) this.unexpected() // NOTE: falls through to _var
case tt._var: return this.parseVarStatement(node, starttype)
case tt._while: return this.parseWhileStatement(node)
case tt._with: return this.parseWithStatement(node)
case tt.braceL: return this.parseBlock()
case tt.semi: return this.parseEmptyStatement(node)
case tt._export:
case tt._import:
if (!this.options.allowImportExportEverywhere) {
if (!topLevel)
this.raise(this.start, "'import' and 'export' may only appear at the top level")
if (!this.inModule)
this.raise(this.start, "'import' and 'export' may appear only with 'sourceType: module'")
}
return starttype === tt._import ? this.parseImport(node) : this.parseExport(node)
// If the statement does not start with a statement keyword or a
// brace, it's an ExpressionStatement or LabeledStatement. We
// simply start parsing an expression, and afterwards, if the
// next token is a colon and the expression was a simple
// Identifier node, we switch to interpreting it as a label.
default:
let maybeName = this.value, expr = this.parseExpression()
if (starttype === tt.name && expr.type === "Identifier" && this.eat(tt.colon))
return this.parseLabeledStatement(node, maybeName, expr)
else return this.parseExpressionStatement(node, expr)
}
}
pp.parseBreakContinueStatement = function(node, keyword) {
let isBreak = keyword == "break"
this.next()
if (this.eat(tt.semi) || this.insertSemicolon()) node.label = null
else if (this.type !== tt.name) this.unexpected()
else {
node.label = this.parseIdent()
this.semicolon()
}
// Verify that there is an actual destination to break or
// continue to.
for (var i = 0; i < this.labels.length; ++i) {
let lab = this.labels[i]
if (node.label == null || lab.name === node.label.name) {
if (lab.kind != null && (isBreak || lab.kind === "loop")) break
if (node.label && isBreak) break
}
}
if (i === this.labels.length) this.raise(node.start, "Unsyntactic " + keyword)
return this.finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement")
}
pp.parseDebuggerStatement = function(node) {
this.next()
this.semicolon()
return this.finishNode(node, "DebuggerStatement")
}
pp.parseDoStatement = function(node) {
this.next()
this.labels.push(loopLabel)
node.body = this.parseStatement(false)
this.labels.pop()
this.expect(tt._while)
node.test = this.parseParenExpression()
if (this.options.ecmaVersion >= 6)
this.eat(tt.semi)
else
this.semicolon()
return this.finishNode(node, "DoWhileStatement")
}
// Disambiguating between a `for` and a `for`/`in` or `for`/`of`
// loop is non-trivial. Basically, we have to parse the init `var`
// statement or expression, disallowing the `in` operator (see
// the second parameter to `parseExpression`), and then check
// whether the next token is `in` or `of`. When there is no init
// part (semicolon immediately after the opening parenthesis), it
// is a regular `for` loop.
pp.parseForStatement = function(node) {
this.next()
this.labels.push(loopLabel)
this.expect(tt.parenL)
if (this.type === tt.semi) return this.parseFor(node, null)
if (this.type === tt._var || this.type === tt._let || this.type === tt._const) {
let init = this.startNode(), varKind = this.type
this.next()
this.parseVar(init, true, varKind)
this.finishNode(init, "VariableDeclaration")
if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1 &&
!(varKind !== tt._var && init.declarations[0].init))
return this.parseForIn(node, init)
return this.parseFor(node, init)
}
let refShorthandDefaultPos = {start: 0}
let init = this.parseExpression(true, refShorthandDefaultPos)
if (this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
this.toAssignable(init)
this.checkLVal(init)
return this.parseForIn(node, init)
} else if (refShorthandDefaultPos.start) {
this.unexpected(refShorthandDefaultPos.start)
}
return this.parseFor(node, init)
}
pp.parseFunctionStatement = function(node) {
this.next()
return this.parseFunction(node, true)
}
pp.parseIfStatement = function(node) {
this.next()
node.test = this.parseParenExpression()
node.consequent = this.parseStatement(false)
node.alternate = this.eat(tt._else) ? this.parseStatement(false) : null
return this.finishNode(node, "IfStatement")
}
pp.parseReturnStatement = function(node) {
if (!this.inFunction && !this.options.allowReturnOutsideFunction)
this.raise(this.start, "'return' outside of function")
this.next()
// In `return` (and `break`/`continue`), the keywords with
// optional arguments, we eagerly look for a semicolon or the
// possibility to insert one.
if (this.eat(tt.semi) || this.insertSemicolon()) node.argument = null
else { node.argument = this.parseExpression(); this.semicolon() }
return this.finishNode(node, "ReturnStatement")
}
pp.parseSwitchStatement = function(node) {
this.next()
node.discriminant = this.parseParenExpression()
node.cases = []
this.expect(tt.braceL)
this.labels.push(switchLabel)
// Statements under must be grouped (by label) in SwitchCase
// nodes. `cur` is used to keep the node that we are currently
// adding statements to.
for (var cur, sawDefault; this.type != tt.braceR;) {
if (this.type === tt._case || this.type === tt._default) {
let isCase = this.type === tt._case
if (cur) this.finishNode(cur, "SwitchCase")
node.cases.push(cur = this.startNode())
cur.consequent = []
this.next()
if (isCase) {
cur.test = this.parseExpression()
} else {
if (sawDefault) this.raise(this.lastTokStart, "Multiple default clauses")
sawDefault = true
cur.test = null
}
this.expect(tt.colon)
} else {
if (!cur) this.unexpected()
cur.consequent.push(this.parseStatement(true))
}
}
if (cur) this.finishNode(cur, "SwitchCase")
this.next() // Closing brace
this.labels.pop()
return this.finishNode(node, "SwitchStatement")
}
pp.parseThrowStatement = function(node) {
this.next()
if (lineBreak.test(this.input.slice(this.lastTokEnd, this.start)))
this.raise(this.lastTokEnd, "Illegal newline after throw")
node.argument = this.parseExpression()
this.semicolon()
return this.finishNode(node, "ThrowStatement")
}
// Reused empty array added for node fields that are always empty.
const empty = []
pp.parseTryStatement = function(node) {
this.next()
node.block = this.parseBlock()
node.handler = null
if (this.type === tt._catch) {
let clause = this.startNode()
this.next()
this.expect(tt.parenL)
clause.param = this.parseBindingAtom()
this.checkLVal(clause.param, true)
this.expect(tt.parenR)
clause.guard = null
clause.body = this.parseBlock()
node.handler = this.finishNode(clause, "CatchClause")
}
node.guardedHandlers = empty
node.finalizer = this.eat(tt._finally) ? this.parseBlock() : null
if (!node.handler && !node.finalizer)
this.raise(node.start, "Missing catch or finally clause")
return this.finishNode(node, "TryStatement")
}
pp.parseVarStatement = function(node, kind) {
this.next()
this.parseVar(node, false, kind)
this.semicolon()
return this.finishNode(node, "VariableDeclaration")
}
pp.parseWhileStatement = function(node) {
this.next()
node.test = this.parseParenExpression()
this.labels.push(loopLabel)
node.body = this.parseStatement(false)
this.labels.pop()
return this.finishNode(node, "WhileStatement")
}
pp.parseWithStatement = function(node) {
if (this.strict) this.raise(this.start, "'with' in strict mode")
this.next()
node.object = this.parseParenExpression()
node.body = this.parseStatement(false)
return this.finishNode(node, "WithStatement")
}
pp.parseEmptyStatement = function(node) {
this.next()
return this.finishNode(node, "EmptyStatement")
}
pp.parseLabeledStatement = function(node, maybeName, expr) {
for (let i = 0; i < this.labels.length; ++i)
if (this.labels[i].name === maybeName) this.raise(expr.start, "Label '" + maybeName + "' is already declared")
let kind = this.type.isLoop ? "loop" : this.type === tt._switch ? "switch" : null
this.labels.push({name: maybeName, kind: kind})
node.body = this.parseStatement(true)
this.labels.pop()
node.label = expr
return this.finishNode(node, "LabeledStatement")
}
pp.parseExpressionStatement = function(node, expr) {
node.expression = expr
this.semicolon()
return this.finishNode(node, "ExpressionStatement")
}
// Parse a semicolon-enclosed block of statements, handling `"use
// strict"` declarations when `allowStrict` is true (used for
// function bodies).
pp.parseBlock = function(allowStrict) {
let node = this.startNode(), first = true, oldStrict
node.body = []
this.expect(tt.braceL)
while (!this.eat(tt.braceR)) {
let stmt = this.parseStatement(true)
node.body.push(stmt)
if (first && allowStrict && this.isUseStrict(stmt)) {
oldStrict = this.strict
this.setStrict(this.strict = true)
}
first = false
}
if (oldStrict === false) this.setStrict(false)
return this.finishNode(node, "BlockStatement")
}
// Parse a regular `for` loop. The disambiguation code in
// `parseStatement` will already have parsed the init statement or
// expression.
pp.parseFor = function(node, init) {
node.init = init
this.expect(tt.semi)
node.test = this.type === tt.semi ? null : this.parseExpression()
this.expect(tt.semi)
node.update = this.type === tt.parenR ? null : this.parseExpression()
this.expect(tt.parenR)
node.body = this.parseStatement(false)
this.labels.pop()
return this.finishNode(node, "ForStatement")
}
// Parse a `for`/`in` and `for`/`of` loop, which are almost
// same from parser's perspective.
pp.parseForIn = function(node, init) {
let type = this.type === tt._in ? "ForInStatement" : "ForOfStatement"
this.next()
node.left = init
node.right = this.parseExpression()
this.expect(tt.parenR)
node.body = this.parseStatement(false)
this.labels.pop()
return this.finishNode(node, type)
}
// Parse a list of variable declarations.
pp.parseVar = function(node, isFor, kind) {
node.declarations = []
node.kind = kind.keyword
for (;;) {
let decl = this.startNode()
this.parseVarId(decl)
if (this.eat(tt.eq)) {
decl.init = this.parseMaybeAssign(isFor)
} else if (kind === tt._const && !(this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of")))) {
this.unexpected()
} else if (decl.id.type != "Identifier" && !(isFor && (this.type === tt._in || this.isContextual("of")))) {
this.raise(this.lastTokEnd, "Complex binding patterns require an initialization value")
} else {
decl.init = null
}
node.declarations.push(this.finishNode(decl, "VariableDeclarator"))
if (!this.eat(tt.comma)) break
}
return node
}
pp.parseVarId = function(decl) {
decl.id = this.parseBindingAtom()
this.checkLVal(decl.id, true)
}
// Parse a function declaration or literal (depending on the
// `isStatement` parameter).
pp.parseFunction = function(node, isStatement, allowExpressionBody) {
this.initFunction(node)
if (this.options.ecmaVersion >= 6)
node.generator = this.eat(tt.star)
if (isStatement || this.type === tt.name)
node.id = this.parseIdent()
this.parseFunctionParams(node)
this.parseFunctionBody(node, allowExpressionBody)
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression")
}
pp.parseFunctionParams = function(node) {
this.expect(tt.parenL)
node.params = this.parseBindingList(tt.parenR, false, false)
}
// Parse a class declaration or literal (depending on the
// `isStatement` parameter).
pp.parseClass = function(node, isStatement) {
this.next()
this.parseClassId(node, isStatement)
this.parseClassSuper(node)
let classBody = this.startNode()
let hadConstructor = false
classBody.body = []
this.expect(tt.braceL)
while (!this.eat(tt.braceR)) {
if (this.eat(tt.semi)) continue
let method = this.startNode()
let isGenerator = this.eat(tt.star)
let isMaybeStatic = this.type === tt.name && this.value === "static"
this.parsePropertyName(method)
method.static = isMaybeStatic && this.type !== tt.parenL
if (method.static) {
if (isGenerator) this.unexpected()
isGenerator = this.eat(tt.star)
this.parsePropertyName(method)
}
method.kind = "method"
if (!method.computed) {
let {key} = method
let isGetSet = false
if (!isGenerator && key.type === "Identifier" && this.type !== tt.parenL && (key.name === "get" || key.name === "set")) {
isGetSet = true
method.kind = key.name
key = this.parsePropertyName(method)
}
if (!method.static && (key.type === "Identifier" && key.name === "constructor" ||
key.type === "Literal" && key.value === "constructor")) {
if (hadConstructor) this.raise(key.start, "Duplicate constructor in the same class")
if (isGetSet) this.raise(key.start, "Constructor can't have get/set modifier")
if (isGenerator) this.raise(key.start, "Constructor can't be a generator")
method.kind = "constructor"
hadConstructor = true
}
}
this.parseClassMethod(classBody, method, isGenerator)
}
node.body = this.finishNode(classBody, "ClassBody")
return this.finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression")
}
pp.parseClassMethod = function(classBody, method, isGenerator) {
method.value = this.parseMethod(isGenerator)
classBody.body.push(this.finishNode(method, "MethodDefinition"))
}
pp.parseClassId = function(node, isStatement) {
node.id = this.type === tt.name ? this.parseIdent() : isStatement ? this.unexpected() : null
}
pp.parseClassSuper = function(node) {
node.superClass = this.eat(tt._extends) ? this.parseExprSubscripts() : null
}
// Parses module export declaration.
pp.parseExport = function(node) {
this.next()
// export * from '...'
if (this.eat(tt.star)) {
this.expectContextual("from")
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
this.semicolon()
return this.finishNode(node, "ExportAllDeclaration")
}
if (this.eat(tt._default)) { // export default ...
let expr = this.parseMaybeAssign()
let needsSemi = true
if (expr.type == "FunctionExpression" ||
expr.type == "ClassExpression") {
needsSemi = false
if (expr.id) {
expr.type = expr.type == "FunctionExpression"
? "FunctionDeclaration"
: "ClassDeclaration"
}
}
node.declaration = expr
if (needsSemi) this.semicolon()
return this.finishNode(node, "ExportDefaultDeclaration")
}
// export var|const|let|function|class ...
if (this.shouldParseExportStatement()) {
node.declaration = this.parseStatement(true)
node.specifiers = []
node.source = null
} else { // export { x, y as z } [from '...']
node.declaration = null
node.specifiers = this.parseExportSpecifiers()
if (this.eatContextual("from")) {
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
} else {
node.source = null
}
this.semicolon()
}
return this.finishNode(node, "ExportNamedDeclaration")
}
pp.shouldParseExportStatement = function() {
return this.type.keyword
}
// Parses a comma-separated list of module exports.
pp.parseExportSpecifiers = function() {
let nodes = [], first = true
// export { x, y as z } [from '...']
this.expect(tt.braceL)
while (!this.eat(tt.braceR)) {
if (!first) {
this.expect(tt.comma)
if (this.afterTrailingComma(tt.braceR)) break
} else first = false
let node = this.startNode()
node.local = this.parseIdent(this.type === tt._default)
node.exported = this.eatContextual("as") ? this.parseIdent(true) : node.local
nodes.push(this.finishNode(node, "ExportSpecifier"))
}
return nodes
}
// Parses import declaration.
pp.parseImport = function(node) {
this.next()
// import '...'
if (this.type === tt.string) {
node.specifiers = empty
node.source = this.parseExprAtom()
node.kind = ""
} else {
node.specifiers = this.parseImportSpecifiers()
this.expectContextual("from")
node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected()
}
this.semicolon()
return this.finishNode(node, "ImportDeclaration")
}
// Parses a comma-separated list of module imports.
pp.parseImportSpecifiers = function() {
let nodes = [], first = true
if (this.type === tt.name) {
// import defaultObj, { x, y as z } from '...'
let node = this.startNode()
node.local = this.parseIdent()
this.checkLVal(node.local, true)
nodes.push(this.finishNode(node, "ImportDefaultSpecifier"))
if (!this.eat(tt.comma)) return nodes
}
if (this.type === tt.star) {
let node = this.startNode()
this.next()
this.expectContextual("as")
node.local = this.parseIdent()
this.checkLVal(node.local, true)
nodes.push(this.finishNode(node, "ImportNamespaceSpecifier"))
return nodes
}
this.expect(tt.braceL)
while (!this.eat(tt.braceR)) {
if (!first) {
this.expect(tt.comma)
if (this.afterTrailingComma(tt.braceR)) break
} else first = false
let node = this.startNode()
node.imported = this.parseIdent(true)
node.local = this.eatContextual("as") ? this.parseIdent() : node.imported
this.checkLVal(node.local, true)
nodes.push(this.finishNode(node, "ImportSpecifier"))
}
return nodes
}

View File

@@ -0,0 +1,107 @@
// The algorithm used to determine whether a regexp can appear at a
// given point in the program is loosely based on sweet.js' approach.
// See https://github.com/mozilla/sweet.js/wiki/design
import {Parser} from "./state"
import {types as tt} from "./tokentype"
import {lineBreak} from "./whitespace"
export class TokContext {
constructor(token, isExpr, preserveSpace, override) {
this.token = token
this.isExpr = isExpr
this.preserveSpace = preserveSpace
this.override = override
}
}
export const types = {
b_stat: new TokContext("{", false),
b_expr: new TokContext("{", true),
b_tmpl: new TokContext("${", true),
p_stat: new TokContext("(", false),
p_expr: new TokContext("(", true),
q_tmpl: new TokContext("`", true, true, p => p.readTmplToken()),
f_expr: new TokContext("function", true)
}
const pp = Parser.prototype
pp.initialContext = function() {
return [types.b_stat]
}
pp.braceIsBlock = function(prevType) {
let parent
if (prevType === tt.colon && (parent = this.curContext()).token == "{")
return !parent.isExpr
if (prevType === tt._return)
return lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof)
return true
if (prevType == tt.braceL)
return this.curContext() === types.b_stat
return !this.exprAllowed
}
pp.updateContext = function(prevType) {
let update, type = this.type
if (type.keyword && prevType == tt.dot)
this.exprAllowed = false
else if (update = type.updateContext)
update.call(this, prevType)
else
this.exprAllowed = type.beforeExpr
}
// Token-specific context update code
tt.parenR.updateContext = tt.braceR.updateContext = function() {
if (this.context.length == 1) {
this.exprAllowed = true
return
}
let out = this.context.pop()
if (out === types.b_stat && this.curContext() === types.f_expr) {
this.context.pop()
this.exprAllowed = false
} else if (out === types.b_tmpl) {
this.exprAllowed = true
} else {
this.exprAllowed = !out.isExpr
}
}
tt.braceL.updateContext = function(prevType) {
this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr)
this.exprAllowed = true
}
tt.dollarBraceL.updateContext = function() {
this.context.push(types.b_tmpl)
this.exprAllowed = true
}
tt.parenL.updateContext = function(prevType) {
let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while
this.context.push(statementParens ? types.p_stat : types.p_expr)
this.exprAllowed = true
}
tt.incDec.updateContext = function() {
// tokExprAllowed stays unchanged
}
tt._function.updateContext = function() {
if (this.curContext() !== types.b_stat)
this.context.push(types.f_expr)
this.exprAllowed = false
}
tt.backQuote.updateContext = function() {
if (this.curContext() === types.q_tmpl)
this.context.pop()
else
this.context.push(types.q_tmpl)
this.exprAllowed = false
}

View File

@@ -0,0 +1,672 @@
import {isIdentifierStart, isIdentifierChar} from "./identifier"
import {types as tt, keywords as keywordTypes} from "./tokentype"
import {Parser} from "./state"
import {SourceLocation} from "./location"
import {lineBreak, lineBreakG, isNewLine, nonASCIIwhitespace} from "./whitespace"
// Object type used to represent tokens. Note that normally, tokens
// simply exist as properties on the parser object. This is only
// used for the onToken callback and the external tokenizer.
export class Token {
constructor(p) {
this.type = p.type
this.value = p.value
this.start = p.start
this.end = p.end
if (p.options.locations)
this.loc = new SourceLocation(p, p.startLoc, p.endLoc)
if (p.options.ranges)
this.range = [p.start, p.end]
}
}
// ## Tokenizer
const pp = Parser.prototype
// Are we running under Rhino?
const isRhino = typeof Packages !== "undefined"
// Move to the next token
pp.next = function() {
if (this.options.onToken)
this.options.onToken(new Token(this))
this.lastTokEnd = this.end
this.lastTokStart = this.start
this.lastTokEndLoc = this.endLoc
this.lastTokStartLoc = this.startLoc
this.nextToken()
}
pp.getToken = function() {
this.next()
return new Token(this)
}
// If we're in an ES6 environment, make parsers iterable
if (typeof Symbol !== "undefined")
pp[Symbol.iterator] = function () {
let self = this
return {next: function () {
let token = self.getToken()
return {
done: token.type === tt.eof,
value: token
}
}}
}
// Toggle strict mode. Re-reads the next number or string to please
// pedantic tests (`"use strict"; 010;` should fail).
pp.setStrict = function(strict) {
this.strict = strict
if (this.type !== tt.num && this.type !== tt.string) return
this.pos = this.start
if (this.options.locations) {
while (this.pos < this.lineStart) {
this.lineStart = this.input.lastIndexOf("\n", this.lineStart - 2) + 1
--this.curLine
}
}
this.nextToken()
}
pp.curContext = function() {
return this.context[this.context.length - 1]
}
// Read a single token, updating the parser object's token-related
// properties.
pp.nextToken = function() {
let curContext = this.curContext()
if (!curContext || !curContext.preserveSpace) this.skipSpace()
this.start = this.pos
if (this.options.locations) this.startLoc = this.curPosition()
if (this.pos >= this.input.length) return this.finishToken(tt.eof)
if (curContext.override) return curContext.override(this)
else this.readToken(this.fullCharCodeAtPos())
}
pp.readToken = function(code) {
// Identifier or keyword. '\uXXXX' sequences are allowed in
// identifiers, so '\' also dispatches to that.
if (isIdentifierStart(code, this.options.ecmaVersion >= 6) || code === 92 /* '\' */)
return this.readWord()
return this.getTokenFromCode(code)
}
pp.fullCharCodeAtPos = function() {
let code = this.input.charCodeAt(this.pos)
if (code <= 0xd7ff || code >= 0xe000) return code
let next = this.input.charCodeAt(this.pos + 1)
return (code << 10) + next - 0x35fdc00
}
pp.skipBlockComment = function() {
let startLoc = this.options.onComment && this.options.locations && this.curPosition()
let start = this.pos, end = this.input.indexOf("*/", this.pos += 2)
if (end === -1) this.raise(this.pos - 2, "Unterminated comment")
this.pos = end + 2
if (this.options.locations) {
lineBreakG.lastIndex = start
let match
while ((match = lineBreakG.exec(this.input)) && match.index < this.pos) {
++this.curLine
this.lineStart = match.index + match[0].length
}
}
if (this.options.onComment)
this.options.onComment(true, this.input.slice(start + 2, end), start, this.pos,
startLoc, this.options.locations && this.curPosition())
}
pp.skipLineComment = function(startSkip) {
let start = this.pos
let startLoc = this.options.onComment && this.options.locations && this.curPosition()
let ch = this.input.charCodeAt(this.pos+=startSkip)
while (this.pos < this.input.length && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {
++this.pos
ch = this.input.charCodeAt(this.pos)
}
if (this.options.onComment)
this.options.onComment(false, this.input.slice(start + startSkip, this.pos), start, this.pos,
startLoc, this.options.locations && this.curPosition())
}
// Called at the start of the parse and after every token. Skips
// whitespace and comments, and.
pp.skipSpace = function() {
while (this.pos < this.input.length) {
let ch = this.input.charCodeAt(this.pos)
if (ch === 32) { // ' '
++this.pos
} else if (ch === 13) {
++this.pos
let next = this.input.charCodeAt(this.pos)
if (next === 10) {
++this.pos
}
if (this.options.locations) {
++this.curLine
this.lineStart = this.pos
}
} else if (ch === 10 || ch === 8232 || ch === 8233) {
++this.pos
if (this.options.locations) {
++this.curLine
this.lineStart = this.pos
}
} else if (ch > 8 && ch < 14) {
++this.pos
} else if (ch === 47) { // '/'
let next = this.input.charCodeAt(this.pos + 1)
if (next === 42) { // '*'
this.skipBlockComment()
} else if (next === 47) { // '/'
this.skipLineComment(2)
} else break
} else if (ch === 160) { // '\xa0'
++this.pos
} else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
++this.pos
} else {
break
}
}
}
// Called at the end of every token. Sets `end`, `val`, and
// maintains `context` and `exprAllowed`, and skips the space after
// the token, so that the next one's `start` will point at the
// right position.
pp.finishToken = function(type, val) {
this.end = this.pos
if (this.options.locations) this.endLoc = this.curPosition()
let prevType = this.type
this.type = type
this.value = val
this.updateContext(prevType)
}
// ### Token reading
// This is the function that is called to fetch the next token. It
// is somewhat obscure, because it works in character codes rather
// than characters, and because operator parsing has been inlined
// into it.
//
// All in the name of speed.
//
pp.readToken_dot = function() {
let next = this.input.charCodeAt(this.pos + 1)
if (next >= 48 && next <= 57) return this.readNumber(true)
let next2 = this.input.charCodeAt(this.pos + 2)
if (this.options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'
this.pos += 3
return this.finishToken(tt.ellipsis)
} else {
++this.pos
return this.finishToken(tt.dot)
}
}
pp.readToken_slash = function() { // '/'
let next = this.input.charCodeAt(this.pos + 1)
if (this.exprAllowed) {++this.pos; return this.readRegexp();}
if (next === 61) return this.finishOp(tt.assign, 2)
return this.finishOp(tt.slash, 1)
}
pp.readToken_mult_modulo = function(code) { // '%*'
let next = this.input.charCodeAt(this.pos + 1)
if (next === 61) return this.finishOp(tt.assign, 2)
return this.finishOp(code === 42 ? tt.star : tt.modulo, 1)
}
pp.readToken_pipe_amp = function(code) { // '|&'
let next = this.input.charCodeAt(this.pos + 1)
if (next === code) return this.finishOp(code === 124 ? tt.logicalOR : tt.logicalAND, 2)
if (next === 61) return this.finishOp(tt.assign, 2)
return this.finishOp(code === 124 ? tt.bitwiseOR : tt.bitwiseAND, 1)
}
pp.readToken_caret = function() { // '^'
let next = this.input.charCodeAt(this.pos + 1)
if (next === 61) return this.finishOp(tt.assign, 2)
return this.finishOp(tt.bitwiseXOR, 1)
}
pp.readToken_plus_min = function(code) { // '+-'
let next = this.input.charCodeAt(this.pos + 1)
if (next === code) {
if (next == 45 && this.input.charCodeAt(this.pos + 2) == 62 &&
lineBreak.test(this.input.slice(this.lastTokEnd, this.pos))) {
// A `-->` line comment
this.skipLineComment(3)
this.skipSpace()
return this.nextToken()
}
return this.finishOp(tt.incDec, 2)
}
if (next === 61) return this.finishOp(tt.assign, 2)
return this.finishOp(tt.plusMin, 1)
}
pp.readToken_lt_gt = function(code) { // '<>'
let next = this.input.charCodeAt(this.pos + 1)
let size = 1
if (next === code) {
size = code === 62 && this.input.charCodeAt(this.pos + 2) === 62 ? 3 : 2
if (this.input.charCodeAt(this.pos + size) === 61) return this.finishOp(tt.assign, size + 1)
return this.finishOp(tt.bitShift, size)
}
if (next == 33 && code == 60 && this.input.charCodeAt(this.pos + 2) == 45 &&
this.input.charCodeAt(this.pos + 3) == 45) {
if (this.inModule) this.unexpected()
// `<!--`, an XML-style comment that should be interpreted as a line comment
this.skipLineComment(4)
this.skipSpace()
return this.nextToken()
}
if (next === 61)
size = this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2
return this.finishOp(tt.relational, size)
}
pp.readToken_eq_excl = function(code) { // '=!'
let next = this.input.charCodeAt(this.pos + 1)
if (next === 61) return this.finishOp(tt.equality, this.input.charCodeAt(this.pos + 2) === 61 ? 3 : 2)
if (code === 61 && next === 62 && this.options.ecmaVersion >= 6) { // '=>'
this.pos += 2
return this.finishToken(tt.arrow)
}
return this.finishOp(code === 61 ? tt.eq : tt.prefix, 1)
}
pp.getTokenFromCode = function(code) {
switch (code) {
// The interpretation of a dot depends on whether it is followed
// by a digit or another two dots.
case 46: // '.'
return this.readToken_dot()
// Punctuation tokens.
case 40: ++this.pos; return this.finishToken(tt.parenL)
case 41: ++this.pos; return this.finishToken(tt.parenR)
case 59: ++this.pos; return this.finishToken(tt.semi)
case 44: ++this.pos; return this.finishToken(tt.comma)
case 91: ++this.pos; return this.finishToken(tt.bracketL)
case 93: ++this.pos; return this.finishToken(tt.bracketR)
case 123: ++this.pos; return this.finishToken(tt.braceL)
case 125: ++this.pos; return this.finishToken(tt.braceR)
case 58: ++this.pos; return this.finishToken(tt.colon)
case 63: ++this.pos; return this.finishToken(tt.question)
case 96: // '`'
if (this.options.ecmaVersion < 6) break
++this.pos
return this.finishToken(tt.backQuote)
case 48: // '0'
let next = this.input.charCodeAt(this.pos + 1)
if (next === 120 || next === 88) return this.readRadixNumber(16); // '0x', '0X' - hex number
if (this.options.ecmaVersion >= 6) {
if (next === 111 || next === 79) return this.readRadixNumber(8); // '0o', '0O' - octal number
if (next === 98 || next === 66) return this.readRadixNumber(2); // '0b', '0B' - binary number
}
// Anything else beginning with a digit is an integer, octal
// number, or float.
case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9
return this.readNumber(false)
// Quotes produce strings.
case 34: case 39: // '"', "'"
return this.readString(code)
// Operators are parsed inline in tiny state machines. '=' (61) is
// often referred to. `finishOp` simply skips the amount of
// characters it is given as second argument, and returns a token
// of the type given by its first argument.
case 47: // '/'
return this.readToken_slash()
case 37: case 42: // '%*'
return this.readToken_mult_modulo(code)
case 124: case 38: // '|&'
return this.readToken_pipe_amp(code)
case 94: // '^'
return this.readToken_caret()
case 43: case 45: // '+-'
return this.readToken_plus_min(code)
case 60: case 62: // '<>'
return this.readToken_lt_gt(code)
case 61: case 33: // '=!'
return this.readToken_eq_excl(code)
case 126: // '~'
return this.finishOp(tt.prefix, 1)
}
this.raise(this.pos, "Unexpected character '" + codePointToString(code) + "'")
}
pp.finishOp = function(type, size) {
let str = this.input.slice(this.pos, this.pos + size)
this.pos += size
return this.finishToken(type, str)
}
var regexpUnicodeSupport = false
try { new RegExp("\uffff", "u"); regexpUnicodeSupport = true }
catch(e) {}
// Parse a regular expression. Some context-awareness is necessary,
// since a '/' inside a '[]' set does not end the expression.
pp.readRegexp = function() {
let escaped, inClass, start = this.pos
for (;;) {
if (this.pos >= this.input.length) this.raise(start, "Unterminated regular expression")
let ch = this.input.charAt(this.pos)
if (lineBreak.test(ch)) this.raise(start, "Unterminated regular expression")
if (!escaped) {
if (ch === "[") inClass = true
else if (ch === "]" && inClass) inClass = false
else if (ch === "/" && !inClass) break
escaped = ch === "\\"
} else escaped = false
++this.pos
}
let content = this.input.slice(start, this.pos)
++this.pos
// Need to use `readWord1` because '\uXXXX' sequences are allowed
// here (don't ask).
let mods = this.readWord1()
let tmp = content
if (mods) {
let validFlags = /^[gmsiy]*$/
if (this.options.ecmaVersion >= 6) validFlags = /^[gmsiyu]*$/
if (!validFlags.test(mods)) this.raise(start, "Invalid regular expression flag")
if (mods.indexOf('u') >= 0 && !regexpUnicodeSupport) {
// Replace each astral symbol and every Unicode escape sequence that
// possibly represents an astral symbol or a paired surrogate with a
// single ASCII symbol to avoid throwing on regular expressions that
// are only valid in combination with the `/u` flag.
// Note: replacing with the ASCII symbol `x` might cause false
// negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
// perfectly valid pattern that is equivalent to `[a-b]`, but it would
// be replaced by `[x-b]` which throws an error.
tmp = tmp.replace(/\\u([a-fA-F0-9]{4})|\\u\{([0-9a-fA-F]+)\}|[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x")
}
}
// Detect invalid regular expressions.
let value = null
// Rhino's regular expression parser is flaky and throws uncatchable exceptions,
// so don't do detection if we are running under Rhino
if (!isRhino) {
try {
new RegExp(tmp)
} catch (e) {
if (e instanceof SyntaxError) this.raise(start, "Error parsing regular expression: " + e.message)
this.raise(e)
}
// Get a regular expression object for this pattern-flag pair, or `null` in
// case the current environment doesn't support the flags it uses.
try {
value = new RegExp(content, mods)
} catch (err) {}
}
return this.finishToken(tt.regexp, {pattern: content, flags: mods, value: value})
}
// Read an integer in the given radix. Return null if zero digits
// were read, the integer value otherwise. When `len` is given, this
// will return `null` unless the integer has exactly `len` digits.
pp.readInt = function(radix, len) {
let start = this.pos, total = 0
for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) {
let code = this.input.charCodeAt(this.pos), val
if (code >= 97) val = code - 97 + 10; // a
else if (code >= 65) val = code - 65 + 10; // A
else if (code >= 48 && code <= 57) val = code - 48; // 0-9
else val = Infinity
if (val >= radix) break
++this.pos
total = total * radix + val
}
if (this.pos === start || len != null && this.pos - start !== len) return null
return total
}
pp.readRadixNumber = function(radix) {
this.pos += 2; // 0x
let val = this.readInt(radix)
if (val == null) this.raise(this.start + 2, "Expected number in radix " + radix)
if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number")
return this.finishToken(tt.num, val)
}
// Read an integer, octal integer, or floating-point number.
pp.readNumber = function(startsWithDot) {
let start = this.pos, isFloat = false, octal = this.input.charCodeAt(this.pos) === 48
if (!startsWithDot && this.readInt(10) === null) this.raise(start, "Invalid number")
if (this.input.charCodeAt(this.pos) === 46) {
++this.pos
this.readInt(10)
isFloat = true
}
let next = this.input.charCodeAt(this.pos)
if (next === 69 || next === 101) { // 'eE'
next = this.input.charCodeAt(++this.pos)
if (next === 43 || next === 45) ++this.pos; // '+-'
if (this.readInt(10) === null) this.raise(start, "Invalid number")
isFloat = true
}
if (isIdentifierStart(this.fullCharCodeAtPos())) this.raise(this.pos, "Identifier directly after number")
let str = this.input.slice(start, this.pos), val
if (isFloat) val = parseFloat(str)
else if (!octal || str.length === 1) val = parseInt(str, 10)
else if (/[89]/.test(str) || this.strict) this.raise(start, "Invalid number")
else val = parseInt(str, 8)
return this.finishToken(tt.num, val)
}
// Read a string value, interpreting backslash-escapes.
pp.readCodePoint = function() {
let ch = this.input.charCodeAt(this.pos), code
if (ch === 123) {
if (this.options.ecmaVersion < 6) this.unexpected()
++this.pos
code = this.readHexChar(this.input.indexOf('}', this.pos) - this.pos)
++this.pos
if (code > 0x10FFFF) this.unexpected()
} else {
code = this.readHexChar(4)
}
return code
}
function codePointToString(code) {
// UTF-16 Decoding
if (code <= 0xFFFF) return String.fromCharCode(code)
return String.fromCharCode(((code - 0x10000) >> 10) + 0xD800,
((code - 0x10000) & 1023) + 0xDC00)
}
pp.readString = function(quote) {
let out = "", chunkStart = ++this.pos
for (;;) {
if (this.pos >= this.input.length) this.raise(this.start, "Unterminated string constant")
let ch = this.input.charCodeAt(this.pos)
if (ch === quote) break
if (ch === 92) { // '\'
out += this.input.slice(chunkStart, this.pos)
out += this.readEscapedChar()
chunkStart = this.pos
} else {
if (isNewLine(ch)) this.raise(this.start, "Unterminated string constant")
++this.pos
}
}
out += this.input.slice(chunkStart, this.pos++)
return this.finishToken(tt.string, out)
}
// Reads template string tokens.
pp.readTmplToken = function() {
let out = "", chunkStart = this.pos
for (;;) {
if (this.pos >= this.input.length) this.raise(this.start, "Unterminated template")
let ch = this.input.charCodeAt(this.pos)
if (ch === 96 || ch === 36 && this.input.charCodeAt(this.pos + 1) === 123) { // '`', '${'
if (this.pos === this.start && this.type === tt.template) {
if (ch === 36) {
this.pos += 2
return this.finishToken(tt.dollarBraceL)
} else {
++this.pos
return this.finishToken(tt.backQuote)
}
}
out += this.input.slice(chunkStart, this.pos)
return this.finishToken(tt.template, out)
}
if (ch === 92) { // '\'
out += this.input.slice(chunkStart, this.pos)
out += this.readEscapedChar()
chunkStart = this.pos
} else if (isNewLine(ch)) {
out += this.input.slice(chunkStart, this.pos)
++this.pos
if (ch === 13 && this.input.charCodeAt(this.pos) === 10) {
++this.pos
out += "\n"
} else {
out += String.fromCharCode(ch)
}
if (this.options.locations) {
++this.curLine
this.lineStart = this.pos
}
chunkStart = this.pos
} else {
++this.pos
}
}
}
// Used to read escaped characters
pp.readEscapedChar = function() {
let ch = this.input.charCodeAt(++this.pos)
let octal = /^[0-7]+/.exec(this.input.slice(this.pos, this.pos + 3))
if (octal) octal = octal[0]
while (octal && parseInt(octal, 8) > 255) octal = octal.slice(0, -1)
if (octal === "0") octal = null
++this.pos
if (octal) {
if (this.strict) this.raise(this.pos - 2, "Octal literal in strict mode")
this.pos += octal.length - 1
return String.fromCharCode(parseInt(octal, 8))
} else {
switch (ch) {
case 110: return "\n"; // 'n' -> '\n'
case 114: return "\r"; // 'r' -> '\r'
case 120: return String.fromCharCode(this.readHexChar(2)); // 'x'
case 117: return codePointToString(this.readCodePoint()); // 'u'
case 116: return "\t"; // 't' -> '\t'
case 98: return "\b"; // 'b' -> '\b'
case 118: return "\u000b"; // 'v' -> '\u000b'
case 102: return "\f"; // 'f' -> '\f'
case 48: return "\0"; // 0 -> '\0'
case 13: if (this.input.charCodeAt(this.pos) === 10) ++this.pos; // '\r\n'
case 10: // ' \n'
if (this.options.locations) { this.lineStart = this.pos; ++this.curLine }
return ""
default: return String.fromCharCode(ch)
}
}
}
// Used to read character escape sequences ('\x', '\u', '\U').
pp.readHexChar = function(len) {
let n = this.readInt(16, len)
if (n === null) this.raise(this.start, "Bad character escape sequence")
return n
}
// Used to signal to callers of `readWord1` whether the word
// contained any escape sequences. This is needed because words with
// escape sequences must not be interpreted as keywords.
var containsEsc
// Read an identifier, and return it as a string. Sets `containsEsc`
// to whether the word contained a '\u' escape.
//
// Incrementally adds only escaped chars, adding other chunks as-is
// as a micro-optimization.
pp.readWord1 = function() {
containsEsc = false
let word = "", first = true, chunkStart = this.pos
let astral = this.options.ecmaVersion >= 6
while (this.pos < this.input.length) {
let ch = this.fullCharCodeAtPos()
if (isIdentifierChar(ch, astral)) {
this.pos += ch <= 0xffff ? 1 : 2
} else if (ch === 92) { // "\"
containsEsc = true
word += this.input.slice(chunkStart, this.pos)
let escStart = this.pos
if (this.input.charCodeAt(++this.pos) != 117) // "u"
this.raise(this.pos, "Expecting Unicode escape sequence \\uXXXX")
++this.pos
let esc = this.readCodePoint()
if (!(first ? isIdentifierStart : isIdentifierChar)(esc, astral))
this.raise(escStart, "Invalid Unicode escape")
word += codePointToString(esc)
chunkStart = this.pos
} else {
break
}
first = false
}
return word + this.input.slice(chunkStart, this.pos)
}
// Read an identifier or keyword token. Will check for reserved
// words when necessary.
pp.readWord = function() {
let word = this.readWord1()
let type = tt.name
if ((this.options.ecmaVersion >= 6 || !containsEsc) && this.isKeyword(word))
type = keywordTypes[word]
return this.finishToken(type, word)
}

View File

@@ -0,0 +1,142 @@
// ## Token types
// The assignment of fine-grained, information-carrying type objects
// allows the tokenizer to store the information it has about a
// token in a way that is very cheap for the parser to look up.
// All token type variables start with an underscore, to make them
// easy to recognize.
// The `beforeExpr` property is used to disambiguate between regular
// expressions and divisions. It is set on all token types that can
// be followed by an expression (thus, a slash after them would be a
// regular expression).
//
// `isLoop` marks a keyword as starting a loop, which is important
// to know when parsing a label, in order to allow or disallow
// continue jumps to that label.
export class TokenType {
constructor(label, conf = {}) {
this.label = label
this.keyword = conf.keyword
this.beforeExpr = !!conf.beforeExpr
this.startsExpr = !!conf.startsExpr
this.isLoop = !!conf.isLoop
this.isAssign = !!conf.isAssign
this.prefix = !!conf.prefix
this.postfix = !!conf.postfix
this.binop = conf.binop || null
this.updateContext = null
}
}
function binop(name, prec) {
return new TokenType(name, {beforeExpr: true, binop: prec})
}
const beforeExpr = {beforeExpr: true}, startsExpr = {startsExpr: true}
export const types = {
num: new TokenType("num", startsExpr),
regexp: new TokenType("regexp", startsExpr),
string: new TokenType("string", startsExpr),
name: new TokenType("name", startsExpr),
eof: new TokenType("eof"),
// Punctuation token types.
bracketL: new TokenType("[", {beforeExpr: true, startsExpr: true}),
bracketR: new TokenType("]"),
braceL: new TokenType("{", {beforeExpr: true, startsExpr: true}),
braceR: new TokenType("}"),
parenL: new TokenType("(", {beforeExpr: true, startsExpr: true}),
parenR: new TokenType(")"),
comma: new TokenType(",", beforeExpr),
semi: new TokenType(";", beforeExpr),
colon: new TokenType(":", beforeExpr),
dot: new TokenType("."),
question: new TokenType("?", beforeExpr),
arrow: new TokenType("=>", beforeExpr),
template: new TokenType("template"),
ellipsis: new TokenType("...", beforeExpr),
backQuote: new TokenType("`", startsExpr),
dollarBraceL: new TokenType("${", {beforeExpr: true, startsExpr: true}),
// Operators. These carry several kinds of properties to help the
// parser use them properly (the presence of these properties is
// what categorizes them as operators).
//
// `binop`, when present, specifies that this operator is a binary
// operator, and will refer to its precedence.
//
// `prefix` and `postfix` mark the operator as a prefix or postfix
// unary operator.
//
// `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
// binary operators with a very low precedence, that should result
// in AssignmentExpression nodes.
eq: new TokenType("=", {beforeExpr: true, isAssign: true}),
assign: new TokenType("_=", {beforeExpr: true, isAssign: true}),
incDec: new TokenType("++/--", {prefix: true, postfix: true, startsExpr: true}),
prefix: new TokenType("prefix", {beforeExpr: true, prefix: true, startsExpr: true}),
logicalOR: binop("||", 1),
logicalAND: binop("&&", 2),
bitwiseOR: binop("|", 3),
bitwiseXOR: binop("^", 4),
bitwiseAND: binop("&", 5),
equality: binop("==/!=", 6),
relational: binop("</>", 7),
bitShift: binop("<</>>", 8),
plusMin: new TokenType("+/-", {beforeExpr: true, binop: 9, prefix: true, startsExpr: true}),
modulo: binop("%", 10),
star: binop("*", 10),
slash: binop("/", 10)
}
// Map keyword names to token types.
export const keywords = {}
// Succinct definitions of keyword token types
function kw(name, options = {}) {
options.keyword = name
keywords[name] = types["_" + name] = new TokenType(name, options)
}
kw("break")
kw("case", beforeExpr)
kw("catch")
kw("continue")
kw("debugger")
kw("default")
kw("do", {isLoop: true})
kw("else", beforeExpr)
kw("finally")
kw("for", {isLoop: true})
kw("function", startsExpr)
kw("if")
kw("return", beforeExpr)
kw("switch")
kw("throw", beforeExpr)
kw("try")
kw("var")
kw("let")
kw("const")
kw("while", {isLoop: true})
kw("with")
kw("new", {beforeExpr: true, startsExpr: true})
kw("this", startsExpr)
kw("super", startsExpr)
kw("class")
kw("extends", beforeExpr)
kw("export")
kw("import")
kw("yield", {beforeExpr: true, startsExpr: true})
kw("null", startsExpr)
kw("true", startsExpr)
kw("false", startsExpr)
kw("in", {beforeExpr: true, binop: 7})
kw("instanceof", {beforeExpr: true, binop: 7})
kw("typeof", {beforeExpr: true, prefix: true, startsExpr: true})
kw("void", {beforeExpr: true, prefix: true, startsExpr: true})
kw("delete", {beforeExpr: true, prefix: true, startsExpr: true})

View File

@@ -0,0 +1,9 @@
export function isArray(obj) {
return Object.prototype.toString.call(obj) === "[object Array]"
}
// Checks if an object has a property.
export function has(obj, propName) {
return Object.prototype.hasOwnProperty.call(obj, propName)
}

View File

@@ -0,0 +1,291 @@
// AST walker module for Mozilla Parser API compatible trees
// A simple walk is one where you simply specify callbacks to be
// called on specific nodes. The last two arguments are optional. A
// simple use would be
//
// walk.simple(myTree, {
// Expression: function(node) { ... }
// });
//
// to do something with all expressions. All Parser API node types
// can be used to identify node types, as well as Expression,
// Statement, and ScopeBody, which denote categories of nodes.
//
// The base argument can be used to pass a custom (recursive)
// walker, and state can be used to give this walked an initial
// state.
export function simple(node, visitors, base, state) {
if (!base) base = exports.base
;(function c(node, st, override) {
let type = override || node.type, found = visitors[type]
base[type](node, st, c)
if (found) found(node, st)
})(node, state)
}
// An ancestor walk builds up an array of ancestor nodes (including
// the current node) and passes them to the callback as the state parameter.
export function ancestor(node, visitors, base, state) {
if (!base) base = exports.base
if (!state) state = []
;(function c(node, st, override) {
let type = override || node.type, found = visitors[type]
if (node != st[st.length - 1]) {
st = st.slice()
st.push(node)
}
base[type](node, st, c)
if (found) found(node, st)
})(node, state)
}
// A recursive walk is one where your functions override the default
// walkers. They can modify and replace the state parameter that's
// threaded through the walk, and can opt how and whether to walk
// their child nodes (by calling their third argument on these
// nodes).
export function recursive(node, state, funcs, base) {
let visitor = funcs ? exports.make(funcs, base) : base
;(function c(node, st, override) {
visitor[override || node.type](node, st, c)
})(node, state)
}
function makeTest(test) {
if (typeof test == "string")
return type => type == test
else if (!test)
return () => true
else
return test
}
class Found {
constructor(node, state) { this.node = node; this.state = state }
}
// Find a node with a given start, end, and type (all are optional,
// null can be used as wildcard). Returns a {node, state} object, or
// undefined when it doesn't find a matching node.
export function findNodeAt(node, start, end, test, base, state) {
test = makeTest(test)
if (!base) base = exports.base
try {
;(function c(node, st, override) {
let type = override || node.type
if ((start == null || node.start <= start) &&
(end == null || node.end >= end))
base[type](node, st, c)
if (test(type, node) &&
(start == null || node.start == start) &&
(end == null || node.end == end))
throw new Found(node, st)
})(node, state)
} catch (e) {
if (e instanceof Found) return e
throw e
}
}
// Find the innermost node of a given type that contains the given
// position. Interface similar to findNodeAt.
export function findNodeAround(node, pos, test, base, state) {
test = makeTest(test)
if (!base) base = exports.base
try {
;(function c(node, st, override) {
let type = override || node.type
if (node.start > pos || node.end < pos) return
base[type](node, st, c)
if (test(type, node)) throw new Found(node, st)
})(node, state)
} catch (e) {
if (e instanceof Found) return e
throw e
}
}
// Find the outermost matching node after a given position.
export function findNodeAfter(node, pos, test, base, state) {
test = makeTest(test)
if (!base) base = exports.base
try {
;(function c(node, st, override) {
if (node.end < pos) return
let type = override || node.type
if (node.start >= pos && test(type, node)) throw new Found(node, st)
base[type](node, st, c)
})(node, state)
} catch (e) {
if (e instanceof Found) return e
throw e
}
}
// Find the outermost matching node before a given position.
export function findNodeBefore(node, pos, test, base, state) {
test = makeTest(test)
if (!base) base = exports.base
let max
;(function c(node, st, override) {
if (node.start > pos) return
let type = override || node.type
if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
max = new Found(node, st)
base[type](node, st, c)
})(node, state)
return max
}
// Used to create a custom walker. Will fill in all missing node
// type properties with the defaults.
export function make(funcs, base) {
if (!base) base = exports.base
let visitor = {}
for (var type in base) visitor[type] = base[type]
for (var type in funcs) visitor[type] = funcs[type]
return visitor
}
function skipThrough(node, st, c) { c(node, st) }
function ignore(_node, _st, _c) {}
// Node walkers.
export const base = {}
base.Program = base.BlockStatement = (node, st, c) => {
for (let i = 0; i < node.body.length; ++i)
c(node.body[i], st, "Statement")
}
base.Statement = skipThrough
base.EmptyStatement = ignore
base.ExpressionStatement = base.ParenthesizedExpression =
(node, st, c) => c(node.expression, st, "Expression")
base.IfStatement = (node, st, c) => {
c(node.test, st, "Expression")
c(node.consequent, st, "Statement")
if (node.alternate) c(node.alternate, st, "Statement")
}
base.LabeledStatement = (node, st, c) => c(node.body, st, "Statement")
base.BreakStatement = base.ContinueStatement = ignore
base.WithStatement = (node, st, c) => {
c(node.object, st, "Expression")
c(node.body, st, "Statement")
}
base.SwitchStatement = (node, st, c) => {
c(node.discriminant, st, "Expression")
for (let i = 0; i < node.cases.length; ++i) {
let cs = node.cases[i]
if (cs.test) c(cs.test, st, "Expression")
for (let j = 0; j < cs.consequent.length; ++j)
c(cs.consequent[j], st, "Statement")
}
}
base.ReturnStatement = base.YieldExpression = (node, st, c) => {
if (node.argument) c(node.argument, st, "Expression")
}
base.ThrowStatement = base.SpreadElement = base.RestElement =
(node, st, c) => c(node.argument, st, "Expression")
base.TryStatement = (node, st, c) => {
c(node.block, st, "Statement")
if (node.handler) c(node.handler.body, st, "ScopeBody")
if (node.finalizer) c(node.finalizer, st, "Statement")
}
base.WhileStatement = base.DoWhileStatement = (node, st, c) => {
c(node.test, st, "Expression")
c(node.body, st, "Statement")
}
base.ForStatement = (node, st, c) => {
if (node.init) c(node.init, st, "ForInit")
if (node.test) c(node.test, st, "Expression")
if (node.update) c(node.update, st, "Expression")
c(node.body, st, "Statement")
}
base.ForInStatement = base.ForOfStatement = (node, st, c) => {
c(node.left, st, "ForInit")
c(node.right, st, "Expression")
c(node.body, st, "Statement")
}
base.ForInit = (node, st, c) => {
if (node.type == "VariableDeclaration") c(node, st)
else c(node, st, "Expression")
}
base.DebuggerStatement = ignore
base.FunctionDeclaration = (node, st, c) => c(node, st, "Function")
base.VariableDeclaration = (node, st, c) => {
for (let i = 0; i < node.declarations.length; ++i) {
let decl = node.declarations[i]
if (decl.init) c(decl.init, st, "Expression")
}
}
base.Function = (node, st, c) => c(node.body, st, "ScopeBody")
base.ScopeBody = (node, st, c) => c(node, st, "Statement")
base.Expression = skipThrough
base.ThisExpression = base.Super = base.MetaProperty = ignore
base.ArrayExpression = base.ArrayPattern = (node, st, c) => {
for (let i = 0; i < node.elements.length; ++i) {
let elt = node.elements[i]
if (elt) c(elt, st, "Expression")
}
}
base.ObjectExpression = base.ObjectPattern = (node, st, c) => {
for (let i = 0; i < node.properties.length; ++i)
c(node.properties[i], st)
}
base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration
base.SequenceExpression = base.TemplateLiteral = (node, st, c) => {
for (let i = 0; i < node.expressions.length; ++i)
c(node.expressions[i], st, "Expression")
}
base.UnaryExpression = base.UpdateExpression = (node, st, c) => {
c(node.argument, st, "Expression")
}
base.BinaryExpression = base.AssignmentExpression = base.AssignmentPattern = base.LogicalExpression = (node, st, c) => {
c(node.left, st, "Expression")
c(node.right, st, "Expression")
}
base.ConditionalExpression = (node, st, c) => {
c(node.test, st, "Expression")
c(node.consequent, st, "Expression")
c(node.alternate, st, "Expression")
}
base.NewExpression = base.CallExpression = (node, st, c) => {
c(node.callee, st, "Expression")
if (node.arguments) for (let i = 0; i < node.arguments.length; ++i)
c(node.arguments[i], st, "Expression")
}
base.MemberExpression = (node, st, c) => {
c(node.object, st, "Expression")
if (node.computed) c(node.property, st, "Expression")
}
base.ExportNamedDeclaration = base.ExportDefaultDeclaration = (node, st, c) => c(node.declaration, st)
base.ImportDeclaration = (node, st, c) => {
for (let i = 0; i < node.specifiers.length; i++)
c(node.specifiers[i], st)
}
base.ImportSpecifier = base.ImportDefaultSpecifier = base.ImportNamespaceSpecifier = base.Identifier = base.Literal = ignore
base.TaggedTemplateExpression = (node, st, c) => {
c(node.tag, st, "Expression")
c(node.quasi, st)
}
base.ClassDeclaration = base.ClassExpression = (node, st, c) => {
if (node.superClass) c(node.superClass, st, "Expression")
for (let i = 0; i < node.body.body.length; i++)
c(node.body.body[i], st)
}
base.MethodDefinition = base.Property = (node, st, c) => {
if (node.computed) c(node.key, st, "Expression")
c(node.value, st, "Expression")
}
base.ComprehensionExpression = (node, st, c) => {
for (let i = 0; i < node.blocks.length; i++)
c(node.blocks[i].right, st, "Expression")
c(node.body, st, "Expression")
}

View File

@@ -0,0 +1,12 @@
// Matches a whole line break (where CRLF is considered a single
// line break). Used to count lines.
export const lineBreak = /\r\n?|\n|\u2028|\u2029/
export const lineBreakG = new RegExp(lineBreak.source, "g")
export function isNewLine(code) {
return code === 10 || code === 13 || code === 0x2028 || code == 0x2029
}
export const nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/

View File

@@ -0,0 +1,63 @@
{
"name": "acorn-globals",
"version": "1.0.4",
"description": "Detect global variables in JavaScript using acorn",
"keywords": [
"ast",
"variable",
"name",
"lexical",
"scope",
"local",
"global",
"implicit"
],
"files": [
"index.js",
"LICENSE"
],
"dependencies": {
"acorn": "^1.0.1"
},
"devDependencies": {
"testit": "^1.2.0"
},
"scripts": {
"test": "node test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ForbesLindesay/acorn-globals.git"
},
"author": {
"name": "ForbesLindesay"
},
"license": "MIT",
"gitHead": "4ad8a64daa0e4028ce0d1596dbceec26b433235c",
"bugs": {
"url": "https://github.com/ForbesLindesay/acorn-globals/issues"
},
"homepage": "https://github.com/ForbesLindesay/acorn-globals",
"_id": "acorn-globals@1.0.4",
"_shasum": "4e8528e724b4fa24ba553ad479c4c78589afbfcf",
"_from": "acorn-globals@>=1.0.3 <2.0.0",
"_npmVersion": "2.7.1",
"_nodeVersion": "1.6.2",
"_npmUser": {
"name": "forbeslindesay",
"email": "forbes@lindesay.co.uk"
},
"maintainers": [
{
"name": "forbeslindesay",
"email": "forbes@lindeay.co.uk"
}
],
"dist": {
"shasum": "4e8528e724b4fa24ba553ad479c4c78589afbfcf",
"tarball": "http://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.4.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.4.tgz",
"readme": "ERROR: No README data found!"
}

View File

@@ -0,0 +1,49 @@
{
"name": "constantinople",
"version": "3.0.1",
"description": "Determine whether a JavaScript expression evaluates to a constant (using UglifyJS)",
"keywords": [],
"dependencies": {
"acorn-globals": "^1.0.0"
},
"devDependencies": {
"mocha": "*"
},
"scripts": {
"test": "mocha -R spec"
},
"repository": {
"type": "git",
"url": "git+https://github.com/ForbesLindesay/constantinople.git"
},
"author": {
"name": "ForbesLindesay"
},
"license": "MIT",
"gitHead": "ed3597688b01f666ec716108bf7c348338ef14af",
"bugs": {
"url": "https://github.com/ForbesLindesay/constantinople/issues"
},
"homepage": "https://github.com/ForbesLindesay/constantinople",
"_id": "constantinople@3.0.1",
"_shasum": "1ddf9deac0d14c4367c1d5b3f16fb2763f123108",
"_from": "constantinople@>=3.0.1 <3.1.0",
"_npmVersion": "1.4.21",
"_npmUser": {
"name": "forbeslindesay",
"email": "forbes@lindeay.co.uk"
},
"maintainers": [
{
"name": "forbeslindesay",
"email": "forbes@lindesay.co.uk"
}
],
"dist": {
"shasum": "1ddf9deac0d14c4367c1d5b3f16fb2763f123108",
"tarball": "http://registry.npmjs.org/constantinople/-/constantinople-3.0.1.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/constantinople/-/constantinople-3.0.1.tgz",
"readme": "ERROR: No README data found!"
}

Some files were not shown because too many files have changed in this diff Show More