This commit is contained in:
chrosey
2017-09-13 07:52:34 +02:00
parent a1f16c37f4
commit 2340b0226b
24621 changed files with 2912161 additions and 149 deletions
+15
View File
@@ -0,0 +1,15 @@
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
node_modules
npm-debug.log
+7
View File
@@ -0,0 +1,7 @@
sudo: false
language: node_js
node_js:
- "4"
- "6"
before_install:
- npm install -g npm@2
+23
View File
@@ -0,0 +1,23 @@
Copyright 2012 Thorsten Lorenz.
All rights reserved.
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.
+199
View File
@@ -0,0 +1,199 @@
# redeyed [![build status](https://secure.travis-ci.org/thlorenz/redeyed.png?branch=master)](http://travis-ci.org/thlorenz/redeyed)
*Add color to your JavaScript!*
![frog](http://allaboutfrogs.org/gallery/photos/redeyes/red1.gif)
[Red Eyed Tree Frog](http://allaboutfrogs.org/info/species/redeye.html) *(Agalychnis callidryas)*
## What?
Takes JavaScript code, along with a config and returns the original code with tokens wrapped and/or replaced as configured.
## Where?
- server side using nodejs
- in the [browser](#browser-support)
## What for?
One usecase is adding metadata to your code that can then be used to apply syntax highlighting.
## How?
- copy the [config.js](https://github.com/thlorenz/redeyed/blob/master/config.js) and edit it in order to specify how
certain tokens are to be surrounded/replaced
- replace the `undefined` of each token you want to configure with one of the following
### {String} config
`'before:after'`
wraps the token inside before/after
### {Object} config
`{ _before: 'before', _after: 'after' }`
wraps token inside before/after
#### Missing before and after resolution for {String} and {Object} config
For the `{String}` and `{Object}` configurations, 'before' or 'after' may be omitted:
- `{String}`:
- `'before:'` (omitting 'after')
- `':after'` (omitting 'before')
- `{Object}`:
- `{ _before: 'before' }` (omitting '_after')
- `{ _after: 'after' }` (omitting '_before')
In these cases the missing half is resolved as follows:
- from the `parent._default` (i.e., `Keyword._default`) if found
- otherwise from the `config._default` if found
- otherwise `''` (empty string)
### {Function} config
`function (tokenString, info) { return {String}|{Object}; }`
#### Inputs
- tokenString: the content of the token that is currently being processed
- info: an object with the following structure
```js
{
// {Int}
// the index of the token being processed inside tokens
tokenIndex
// {Array}
// all tokens that are being processed including comments
// (i.e. the result of merging esprima tokens and comments)
, tokens
// {Object}
// the abstract syntax tree of the parsed code
, ast
// {String}
// the code that was parsed (same string as the one passed to redeyed(code ..)
, code
}
```
In most cases the `tokenString` is all you need. The extra info object is passed in case you need to gather more
information about the `token`'s surroundings in order to decide how to transform it.
See: [replace-log-example](https://github.com/thlorenz/redeyed/blob/master/examples/replace-log.js)
#### Output
You can return a {String} or an {Object} from a {Function} config.
- when returning a {String}, the token value will be replaced with it
- when returning an {Object}, it should be of the following form:
```js
{
// {String}
// the string that should be substituted for the value of the current and all skipped tokens
replacement
// {Object} (Token)
// the token after which processing should continue
// all tokens in between the current one and this one inclusive will be ignored
, skipPastToken
}
```
### Transforming JavaScript code
***redeyed(code, config[, opts])***
Invoke redeyed with your **config**uration, a **code** snippet and maybe **opts** as in the below example:
```javascript
var redeyed = require('redeyed')
, config = require('./path/to/config')
, code = 'var a = 3;'
, result;
// redeyed will throw an error (caused by the esprima parser) if the code has invalid javascript
try {
result = redeyed(code, config);
console.log(result.code);
} catch(err) {
console.error(err);
}
```
***opts***:
```js
{ // {Boolean}
// if true `result.ast` property contains the abstract syntax tree of the code
// if false (default) `result.ast` is not assigned and therefore `undefined`
buildAst: true|false
// {Boolean}
// if true `result.code` is not assigned and therefore `undefined`
// if false (default) `result.code` property contains the result of `split.join`
nojoin: true|false
// {Object}
// overrides default parser `esprima-fb` and needs to be compatible with it
parser: require('esprima')
}
```
***return value***:
```js
{ ast
, tokens
, comments
, splits
, code
}
```
- ast `{Array}`: [abstract syntax tree](http://en.wikipedia.org/wiki/Abstract_syntax_tree) as returned by [esprima
parse](http://en.wikipedia.org/wiki/Abstract_syntax_tree)
- tokens `{Array}`: [tokens](http://en.wikipedia.org/wiki/Token_(parser)) provided by esprima (excluding
comments)
- comments `{Array}`: block and line comments as provided by esprima
- splits `{Array}`: code pieces split up, some of which where transformed as configured
- code `{String}`: transformed code, same as `splits.join('')` unless this step has been skipped (see opts)
## Browser Support
### AMD
Ensure to include [esprima](https://github.com/ariya/esprima) as one of your dependencies
```js
define(['redeyed'], function (redeyed) {
[ .. ]
});
```
### Attached to global window object
The `redeyed {Function}` will be exposed globally as `window.redeyed` - big surprise!
```html
<script type="text/javascript" src="https://unpkg.com/esprima"></script>
<script type="text/javascript" src="https://unpkg.com/redeyed"></script>
```
## redeyed in the wild
- [cardinal](https://github.com/thlorenz/cardinal): Syntax highlights JavaScript code with ANSI colors to be printed to
the terminal
- [peacock](http://thlorenz.github.com/peacock/): JavaScript syntax highlighter that generates html that is compatible
with pygments styles.
## Examples
- `npm explore redeyed; npm demo` will let you try the [browser example](https://github.com/thlorenz/redeyed/tree/master/examples/browser)
- `npm explore redeyed; npm demo-log` will let you try the [replace log example](https://github.com/thlorenz/redeyed/blob/master/examples/replace-log.js)
+140
View File
@@ -0,0 +1,140 @@
/*
* This config only contains ES5 tokens while the default config.js contains
* ES6 tokens as well.
*
* Copy this file and use it as a starting point for your redeyed config.
* Just fill in the tokens you want to surround/replace.
* Keep in mind that more specific configurations override less specific ones.
*/
module.exports = {
'Boolean': {
'true' : undefined
, 'false' : undefined
, _default : undefined
}
, 'Identifier': {
_default: undefined
}
, 'Null': {
_default: undefined
}
, 'Numeric': {
_default: undefined
}
, 'String': {
_default: undefined
}
, 'Keyword': {
'break' : undefined
, 'case' : undefined
, 'catch' : undefined
, 'continue' : undefined
, 'debugger' : undefined
, 'default' : undefined
, 'delete' : undefined
, 'do' : undefined
, 'else' : undefined
, 'finally' : undefined
, 'for' : undefined
, 'function' : undefined
, 'if' : undefined
, 'in' : undefined
, 'instanceof' : undefined
, 'new' : undefined
, 'return' : undefined
, 'switch' : undefined
, 'this' : undefined
, 'throw' : undefined
, 'try' : undefined
, 'typeof' : undefined
, 'var' : undefined
, 'void' : undefined
, 'while' : undefined
, 'with' : undefined
, _default : undefined
}
, 'Punctuator': {
';': undefined
, '.': undefined
, ',': undefined
, '{': undefined
, '}': undefined
, '(': undefined
, ')': undefined
, '[': undefined
, ']': undefined
, '<': undefined
, '>': undefined
, '+': undefined
, '-': undefined
, '*': undefined
, '%': undefined
, '&': undefined
, '|': undefined
, '^': undefined
, '!': undefined
, '~': undefined
, '?': undefined
, ':': undefined
, '=': undefined
, '<=': undefined
, '>=': undefined
, '==': undefined
, '!=': undefined
, '++': undefined
, '--': undefined
, '<<': undefined
, '>>': undefined
, '&&': undefined
, '||': undefined
, '+=': undefined
, '-=': undefined
, '*=': undefined
, '%=': undefined
, '&=': undefined
, '|=': undefined
, '^=': undefined
, '/=': undefined
, '===': undefined
, '!==': undefined
, '>>>': undefined
, '<<=': undefined
, '>>=': undefined
, '>>>=': undefined
, _default: undefined
}
// line comment
, Line: {
_default: undefined
}
/* block comment */
, Block: {
_default: undefined
}
, _default: undefined
};
+159
View File
@@ -0,0 +1,159 @@
/*
* Copy this file and use it as a starting point for your redeyed config.
* Just fill in the tokens you want to surround/replace.
* Keep in mind that more specific configurations override less specific ones.
*/
module.exports = {
'Boolean': {
'true' : undefined
, 'false' : undefined
, _default : undefined
}
, 'Identifier': {
_default: undefined
}
, 'Null': {
_default: undefined
}
, 'Numeric': {
_default: undefined
}
, 'String': {
_default: undefined
}
, 'Keyword': {
'break' : undefined
, 'case' : undefined
, 'catch' : undefined
, 'class' : undefined
, 'const' : undefined
, 'continue' : undefined
, 'debugger' : undefined
, 'default' : undefined
, 'delete' : undefined
, 'do' : undefined
, 'else' : undefined
, 'enum' : undefined
, 'export' : undefined
, 'extends' : undefined
, 'finally' : undefined
, 'for' : undefined
, 'function' : undefined
, 'if' : undefined
, 'implements' : undefined
, 'import' : undefined
, 'in' : undefined
, 'instanceof' : undefined
, 'interface' : undefined
, 'let' : undefined
, 'new' : undefined
, 'package' : undefined
, 'private' : undefined
, 'protected' : undefined
, 'public' : undefined
, 'return' : undefined
, 'static' : undefined
, 'super' : undefined
, 'switch' : undefined
, 'this' : undefined
, 'throw' : undefined
, 'try' : undefined
, 'typeof' : undefined
, 'var' : undefined
, 'void' : undefined
, 'while' : undefined
, 'with' : undefined
, 'yield' : undefined
, _default : undefined
}
, 'Punctuator': {
';': undefined
, '.': undefined
, ',': undefined
, '{': undefined
, '}': undefined
, '(': undefined
, ')': undefined
, '[': undefined
, ']': undefined
, '<': undefined
, '>': undefined
, '+': undefined
, '-': undefined
, '*': undefined
, '%': undefined
, '&': undefined
, '|': undefined
, '^': undefined
, '!': undefined
, '~': undefined
, '?': undefined
, ':': undefined
, '=': undefined
, '<=': undefined
, '>=': undefined
, '==': undefined
, '!=': undefined
, '++': undefined
, '--': undefined
, '<<': undefined
, '>>': undefined
, '&&': undefined
, '||': undefined
, '+=': undefined
, '-=': undefined
, '*=': undefined
, '%=': undefined
, '&=': undefined
, '|=': undefined
, '^=': undefined
, '/=': undefined
, '=>': undefined
, '**': undefined
, '===': undefined
, '!==': undefined
, '>>>': undefined
, '<<=': undefined
, '>>=': undefined
, '...': undefined
, '**=': undefined
, '>>>=': undefined
, _default: undefined
}
// line comment
, Line: {
_default: undefined
}
/* block comment */
, Block: {
_default: undefined
}
, _default: undefined
};
+31
View File
@@ -0,0 +1,31 @@
.code {
width: 500px;
height: 500px;
display: block;
}
.config {
width: 500px;
height: 400px;
display: block;
}
.go {
display: block;
float: right;
font-size: 18px;
}
.result {
width: 500px;
height: 1000px;
display: block;
}
.edit {
float: left;
}
.results {
float: left;
}
+35
View File
@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title></title>
<link rel="stylesheet" type="text/css" media="screen" href="index.css" />
</head>
<body>
<header>
<span>Change the config and/or the original code in order to affect the "redeyed" result</span>
</header>
<section class="edit">
<section>
<h3>Redeyed Config </h3>
<textarea class="config"></textarea>
<button class="go">Go</button>
</section>
<section>
<h3>Original Code</h3>
<textarea class="code"></textarea>
</section>
</section>
<section class="results">
<h3>Result</h3>
<textarea class="result" readonly="readonly"></textarea>
</section>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/esprima"></script>
<script type="text/javascript" src="../../redeyed.js"></script>
<script type="text/javascript" src="./sample-config.js"></script>
<script type="text/javascript" src="./index.js"></script>
</body>
</html>
+33
View File
@@ -0,0 +1,33 @@
var $code = $('.code')
, $config = $('.config')
, $result = $('.result')
;
function go () {
var config;
try {
config = JSON.parse($config.val());
} catch (e) {
$result.val('In "Redeyed Config": ' + e.toString());
return;
}
try {
var code = $code.val()
, result = redeyed(code, config);
$result.val(result.code);
} catch (e) {
$result.val('In "Original Code": ' + e.toString());
}
}
$code.val(window.redeyed.toString());
$config.val(JSON.stringify(window.sampleConfig, false, 2));
$('.go').click(go);
go();
+131
View File
@@ -0,0 +1,131 @@
window.sampleConfig = {
'Boolean': {
'true' : undefined
, 'false' : undefined
, _default : '?:?'
}
, 'Identifier': {
_default: '-> : <-'
}
, 'Null': {
_default: '**:**'
}
, 'Numeric': {
_default: 'n:N'
}
, 'String': {
_default: 'string -> :'
}
, 'Keyword': {
'break' : undefined
, 'case' : undefined
, 'catch' : undefined
, 'continue' : undefined
, 'debugger' : undefined
, 'default' : undefined
, 'delete' : undefined
, 'do' : undefined
, 'else' : undefined
, 'finally' : undefined
, 'for' : undefined
, 'function' : undefined
, 'if' : undefined
, 'in' : undefined
, 'instanceof' : undefined
, 'new' : undefined
, 'return' : undefined
, 'switch' : undefined
, 'this' : undefined
, 'throw' : undefined
, 'try' : undefined
, 'typeof' : undefined
, 'var' : undefined
, 'void' : undefined
, 'while' : undefined
, 'with' : undefined
, _default : ': <- keyword'
}
, 'Punctuator': {
';': undefined
, '.': undefined
, ',': undefined
, '{': undefined
, '}': undefined
, '(': undefined
, ')': undefined
, '[': undefined
, ']': undefined
, '<': undefined
, '>': undefined
, '+': undefined
, '-': undefined
, '*': undefined
, '%': undefined
, '&': undefined
, '|': undefined
, '^': undefined
, '!': undefined
, '~': undefined
, '?': undefined
, ':': undefined
, '=': undefined
, '<=': undefined
, '>=': undefined
, '==': undefined
, '!=': undefined
, '++': undefined
, '--': undefined
, '<<': undefined
, '>>': undefined
, '&&': undefined
, '||': undefined
, '+=': undefined
, '-=': undefined
, '*=': undefined
, '%=': undefined
, '&=': undefined
, '|=': undefined
, '^=': undefined
, '/=': undefined
, '===': undefined
, '!==': undefined
, '>>>': undefined
, '<<=': undefined
, '>>=': undefined
, '>>>=': undefined
, _default: undefined
}
// line comment
, Line: {
_default: undefined
}
/* block comment */
, Block: {
_default: undefined
}
, _default: undefined
};
+61
View File
@@ -0,0 +1,61 @@
var path = require('path')
, fs = require('fs')
, redeyed = require('..')
, vm = require('vm')
;
var samplePath = path.join(__dirname, 'sources', 'log.js')
, origCode = fs.readFileSync(samplePath, 'utf-8')
, kinds = ['silly', 'info', 'warn', 'error' ]
;
function replaceConsole(s, info) {
var code = info.code
, idx = info.tokenIndex
, tokens = info.tokens
, next = tokens[idx + 1].value
, kind = tokens[idx + 2].value
, openParen = tokens[idx + 3].value
, firstArgTkn = tokens[idx + 4]
, argIdx = idx + 3
, open
, tkn
;
if (kind === 'log') kind = 'silly';
// not a console.xxx(...) statement? -> just return original
if (next !== '.' || !~kinds.indexOf(kind) || openParen !== '(') return s;
// skip past arguments to console.xxx all args from ( to )
open = 1;
while (open) {
tkn = tokens[++argIdx];
// count open parens vs. closed ones to handle things like console.log(new Error('..'));
if (tkn.value === '(') open++;
if (tkn.value === ')') open--;
}
// tkn now is the last closing paren
var argsIncludingClosingParen = code.slice(firstArgTkn.range[0], tkn.range[1])
, result = 'log.' + kind + '("main-logger", ' + argsIncludingClosingParen;
// tell redeyed to skip the entire console.xxx(..) statement since we are replacing it all
return { replacement: result, skipPastToken: tkn };
}
function transformAndRun () {
var config = {
Identifier: { console: replaceConsole }
}
, code = redeyed(origCode, config).code
, context = vm.createContext({ require: require });
console.log('Original code:\n', origCode);
console.log('\nlog calls replaced:\n', code);
console.log('\nLets run it:');
vm.runInContext(code, context, 'transformed-log.vm');
}
transformAndRun();
+8
View File
@@ -0,0 +1,8 @@
// First two lines will be needed when we replaced all console.xxx statements with log.xxx
var log = require('npmlog');
log.level = 'silly';
console.info('info ', 1);
console.log('log ', 2);
console.warn('warn ', 3);
console.error('error ', new Error('oh my!'));
+15
View File
@@ -0,0 +1,15 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../esprima/bin/esparse.js" "$@"
ret=$?
else
node "$basedir/../esprima/bin/esparse.js" "$@"
ret=$?
fi
exit $ret
+7
View File
@@ -0,0 +1,7 @@
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\esprima\bin\esparse.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\esprima\bin\esparse.js" %*
)
+15
View File
@@ -0,0 +1,15 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../esprima/bin/esvalidate.js" "$@"
ret=$?
else
node "$basedir/../esprima/bin/esvalidate.js" "$@"
ret=$?
fi
exit $ret
+7
View File
@@ -0,0 +1,7 @@
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\esprima\bin\esvalidate.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\esprima\bin\esvalidate.js" %*
)
+184
View File
@@ -0,0 +1,184 @@
2016-09-03: Version 3.0.0
* Support ES2016 exponentiation expression (issue 1490)
* Support JSX syntax (issue 1467)
* Use the latest Unicode 8.0 (issue 1475)
* Add the support for syntax node delegate (issue 1435)
* Fix ESTree compatibility on meta property (issue 1338)
* Fix ESTree compatibility on default parameter value (issue 1081)
* Fix ESTree compatibility on try handler (issue 1030)
2016-08-23: Version 2.7.3
* Fix tokenizer confusion with a comment (issue 1493, 1516)
2016-02-02: Version 2.7.2
* Fix out-of-bound error location in an invalid string literal (issue 1457)
* Fix shorthand object destructuring defaults in variable declarations (issue 1459)
2015-12-10: Version 2.7.1
* Do not allow trailing comma in a variable declaration (issue 1360)
* Fix assignment to `let` in non-strict mode (issue 1376)
* Fix missing delegate property in YieldExpression (issue 1407)
2015-10-22: Version 2.7.0
* Fix the handling of semicolon in a break statement (issue 1044)
* Run the test suite with major web browsers (issue 1259, 1317)
* Allow `let` as an identifier in non-strict mode (issue 1289)
* Attach orphaned comments as `innerComments` (issue 1328)
* Add the support for token delegator (issue 1332)
2015-09-01: Version 2.6.0
* Properly allow or prohibit `let` in a binding identifier/pattern (issue 1048, 1098)
* Add sourceType field for Program node (issue 1159)
* Ensure that strict mode reserved word binding throw an error (issue 1171)
* Run the test suite with Node.js and IE 11 on Windows (issue 1294)
* Allow binding pattern with no initializer in a for statement (issue 1301)
2015-07-31: Version 2.5.0
* Run the test suite in a browser environment (issue 1004)
* Ensure a comma between imported default binding and named imports (issue 1046)
* Distinguish `yield` as a keyword vs an identifier (issue 1186)
* Support ES6 meta property `new.target` (issue 1203)
* Fix the syntax node for yield with expression (issue 1223)
* Fix the check of duplicated proto in property names (issue 1225)
* Fix ES6 Unicode escape in identifier name (issue 1229)
* Support ES6 IdentifierStart and IdentifierPart (issue 1232)
* Treat await as a reserved word when parsing as a module (issue 1234)
* Recognize identifier characters from Unicode SMP (issue 1244)
* Ensure that export and import can be followed by a comma (issue 1250)
* Fix yield operator precedence (issue 1262)
2015-07-01: Version 2.4.1
* Fix some cases of comment attachment (issue 1071, 1175)
* Fix the handling of destructuring in function arguments (issue 1193)
* Fix invalid ranges in assignment expression (issue 1201)
2015-06-26: Version 2.4.0
* Support ES6 for-of iteration (issue 1047)
* Support ES6 spread arguments (issue 1169)
* Minimize npm payload (issue 1191)
2015-06-16: Version 2.3.0
* Support ES6 generator (issue 1033)
* Improve parsing of regular expressions with `u` flag (issue 1179)
2015-04-17: Version 2.2.0
* Support ES6 import and export declarations (issue 1000)
* Fix line terminator before arrow not recognized as error (issue 1009)
* Support ES6 destructuring (issue 1045)
* Support ES6 template literal (issue 1074)
* Fix the handling of invalid/incomplete string escape sequences (issue 1106)
* Fix ES3 static member access restriction (issue 1120)
* Support for `super` in ES6 class (issue 1147)
2015-03-09: Version 2.1.0
* Support ES6 class (issue 1001)
* Support ES6 rest parameter (issue 1011)
* Expand the location of property getter, setter, and methods (issue 1029)
* Enable TryStatement transition to a single handler (issue 1031)
* Support ES6 computed property name (issue 1037)
* Tolerate unclosed block comment (issue 1041)
* Support ES6 lexical declaration (issue 1065)
2015-02-06: Version 2.0.0
* Support ES6 arrow function (issue 517)
* Support ES6 Unicode code point escape (issue 521)
* Improve the speed and accuracy of comment attachment (issue 522)
* Support ES6 default parameter (issue 519)
* Support ES6 regular expression flags (issue 557)
* Fix scanning of implicit octal literals (issue 565)
* Fix the handling of automatic semicolon insertion (issue 574)
* Support ES6 method definition (issue 620)
* Support ES6 octal integer literal (issue 621)
* Support ES6 binary integer literal (issue 622)
* Support ES6 object literal property value shorthand (issue 624)
2015-03-03: Version 1.2.5
* Fix scanning of implicit octal literals (issue 565)
2015-02-05: Version 1.2.4
* Fix parsing of LeftHandSideExpression in ForInStatement (issue 560)
* Fix the handling of automatic semicolon insertion (issue 574)
2015-01-18: Version 1.2.3
* Fix division by this (issue 616)
2014-05-18: Version 1.2.2
* Fix duplicated tokens when collecting comments (issue 537)
2014-05-04: Version 1.2.1
* Ensure that Program node may still have leading comments (issue 536)
2014-04-29: Version 1.2.0
* Fix semicolon handling for expression statement (issue 462, 533)
* Disallow escaped characters in regular expression flags (issue 503)
* Performance improvement for location tracking (issue 520)
* Improve the speed of comment attachment (issue 522)
2014-03-26: Version 1.1.1
* Fix token handling of forward slash after an array literal (issue 512)
2014-03-23: Version 1.1.0
* Optionally attach comments to the owning syntax nodes (issue 197)
* Simplify binary parsing with stack-based shift reduce (issue 352)
* Always include the raw source of literals (issue 376)
* Add optional input source information (issue 386)
* Tokenizer API for pure lexical scanning (issue 398)
* Improve the web site and its online demos (issue 337, 400, 404)
* Performance improvement for location tracking (issue 417, 424)
* Support HTML comment syntax (issue 451)
* Drop support for legacy browsers (issue 474)
2013-08-27: Version 1.0.4
* Minimize the payload for packages (issue 362)
* Fix missing cases on an empty switch statement (issue 436)
* Support escaped ] in regexp literal character classes (issue 442)
* Tolerate invalid left-hand side expression (issue 130)
2013-05-17: Version 1.0.3
* Variable declaration needs at least one declarator (issue 391)
* Fix benchmark's variance unit conversion (issue 397)
* IE < 9: \v should be treated as vertical tab (issue 405)
* Unary expressions should always have prefix: true (issue 418)
* Catch clause should only accept an identifier (issue 423)
* Tolerate setters without parameter (issue 426)
2012-11-02: Version 1.0.2
Improvement:
* Fix esvalidate JUnit output upon a syntax error (issue 374)
2012-10-28: Version 1.0.1
Improvements:
* esvalidate understands shebang in a Unix shell script (issue 361)
* esvalidate treats fatal parsing failure as an error (issue 361)
* Reduce Node.js package via .npmignore (issue 362)
2012-10-22: Version 1.0.0
Initial release.
+21
View File
@@ -0,0 +1,21 @@
Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+48
View File
@@ -0,0 +1,48 @@
[![NPM version](https://img.shields.io/npm/v/esprima.svg)](https://www.npmjs.com/package/esprima)
[![npm download](https://img.shields.io/npm/dm/esprima.svg)](https://www.npmjs.com/package/esprima)
[![Build Status](https://img.shields.io/travis/jquery/esprima/master.svg)](https://travis-ci.org/jquery/esprima)
[![Coverage Status](https://img.shields.io/codecov/c/github/jquery/esprima/master.svg)](https://codecov.io/github/jquery/esprima)
**Esprima** ([esprima.org](http://esprima.org), BSD license) is a high performance,
standard-compliant [ECMAScript](http://www.ecma-international.org/publications/standards/Ecma-262.htm)
parser written in ECMAScript (also popularly known as
[JavaScript](https://en.wikipedia.org/wiki/JavaScript)).
Esprima is created and maintained by [Ariya Hidayat](https://twitter.com/ariyahidayat),
with the help of [many contributors](https://github.com/jquery/esprima/contributors).
### Features
- Full support for ECMAScript 2016 ([ECMA-262 7th Edition](http://www.ecma-international.org/publications/standards/Ecma-262.htm))
- Sensible [syntax tree format](https://github.com/estree/estree/blob/master/es5.md) as standardized by [ESTree project](https://github.com/estree/estree)
- Experimental support for [JSX](https://facebook.github.io/jsx/), a syntax extension for [React](https://facebook.github.io/react/)
- Optional tracking of syntax node location (index-based and line-column)
- [Heavily tested](http://esprima.org/test/ci.html) (~1300 [unit tests](https://github.com/jquery/esprima/tree/master/test/fixtures) with [full code coverage](https://codecov.io/github/jquery/esprima))
### API
Esprima can be used to perform [lexical analysis](https://en.wikipedia.org/wiki/Lexical_analysis) (tokenization) or [syntactic analysis](https://en.wikipedia.org/wiki/Parsing) (parsing) of a JavaScript programs.
A simple example:
```javascript
var esprima = require('esprima');
var code = 'const answer = 42';
var tokens = esprima.tokenize(code);
var ast = esprima.parse(code);
```
which gives a list of [tokens](https://en.wikipedia.org/wiki/Lexical_analysis#Token):
```javascript
[ { type: 'Keyword', value: 'const' },
{ type: 'Identifier', value: 'answer' },
{ type: 'Punctuator', value: '=' },
{ type: 'Numeric', value: '42' } ]
```
and an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree):
```javascript
{ type: 'Program',
body:
[ { type: 'VariableDeclaration',
declarations: [Object],
kind: 'const' } ],
sourceType: 'script' }
```
+139
View File
@@ -0,0 +1,139 @@
#!/usr/bin/env node
/*
Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*jslint sloppy:true node:true rhino:true */
var fs, esprima, fname, forceFile, content, options, syntax;
if (typeof require === 'function') {
fs = require('fs');
try {
esprima = require('esprima');
} catch (e) {
esprima = require('../');
}
} else if (typeof load === 'function') {
try {
load('esprima.js');
} catch (e) {
load('../esprima.js');
}
}
// Shims to Node.js objects when running under Rhino.
if (typeof console === 'undefined' && typeof process === 'undefined') {
console = { log: print };
fs = { readFileSync: readFile };
process = { argv: arguments, exit: quit };
process.argv.unshift('esparse.js');
process.argv.unshift('rhino');
}
function showUsage() {
console.log('Usage:');
console.log(' esparse [options] [file.js]');
console.log();
console.log('Available options:');
console.log();
console.log(' --comment Gather all line and block comments in an array');
console.log(' --loc Include line-column location info for each syntax node');
console.log(' --range Include index-based range for each syntax node');
console.log(' --raw Display the raw value of literals');
console.log(' --tokens List all tokens in an array');
console.log(' --tolerant Tolerate errors on a best-effort basis (experimental)');
console.log(' -v, --version Shows program version');
console.log();
process.exit(1);
}
options = {};
process.argv.splice(2).forEach(function (entry) {
if (forceFile || entry === '-' || entry.slice(0, 1) !== '-') {
if (typeof fname === 'string') {
console.log('Error: more than one input file.');
process.exit(1);
} else {
fname = entry;
}
} else if (entry === '-h' || entry === '--help') {
showUsage();
} else if (entry === '-v' || entry === '--version') {
console.log('ECMAScript Parser (using Esprima version', esprima.version, ')');
console.log();
process.exit(0);
} else if (entry === '--comment') {
options.comment = true;
} else if (entry === '--loc') {
options.loc = true;
} else if (entry === '--range') {
options.range = true;
} else if (entry === '--raw') {
options.raw = true;
} else if (entry === '--tokens') {
options.tokens = true;
} else if (entry === '--tolerant') {
options.tolerant = true;
} else if (entry === '--') {
forceFile = true;
} else {
console.log('Error: unknown option ' + entry + '.');
process.exit(1);
}
});
// Special handling for regular expression literal since we need to
// convert it to a string literal, otherwise it will be decoded
// as object "{}" and the regular expression would be lost.
function adjustRegexLiteral(key, value) {
if (key === 'value' && value instanceof RegExp) {
value = value.toString();
}
return value;
}
function run(content) {
syntax = esprima.parse(content, options);
console.log(JSON.stringify(syntax, adjustRegexLiteral, 4));
}
try {
if (fname && (fname !== '-' || forceFile)) {
run(fs.readFileSync(fname, 'utf-8'));
} else {
var content = '';
process.stdin.resume();
process.stdin.on('data', function(chunk) {
content += chunk;
});
process.stdin.on('end', function() {
run(content);
});
}
} catch (e) {
console.log('Error: ' + e.message);
process.exit(1);
}
+236
View File
@@ -0,0 +1,236 @@
#!/usr/bin/env node
/*
Copyright (c) jQuery Foundation, Inc. and Contributors, All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*jslint sloppy:true plusplus:true node:true rhino:true */
/*global phantom:true */
var fs, system, esprima, options, fnames, forceFile, count;
if (typeof esprima === 'undefined') {
// PhantomJS can only require() relative files
if (typeof phantom === 'object') {
fs = require('fs');
system = require('system');
esprima = require('./esprima');
} else if (typeof require === 'function') {
fs = require('fs');
try {
esprima = require('esprima');
} catch (e) {
esprima = require('../');
}
} else if (typeof load === 'function') {
try {
load('esprima.js');
} catch (e) {
load('../esprima.js');
}
}
}
// Shims to Node.js objects when running under PhantomJS 1.7+.
if (typeof phantom === 'object') {
fs.readFileSync = fs.read;
process = {
argv: [].slice.call(system.args),
exit: phantom.exit,
on: function (evt, callback) {
callback();
}
};
process.argv.unshift('phantomjs');
}
// Shims to Node.js objects when running under Rhino.
if (typeof console === 'undefined' && typeof process === 'undefined') {
console = { log: print };
fs = { readFileSync: readFile };
process = {
argv: arguments,
exit: quit,
on: function (evt, callback) {
callback();
}
};
process.argv.unshift('esvalidate.js');
process.argv.unshift('rhino');
}
function showUsage() {
console.log('Usage:');
console.log(' esvalidate [options] [file.js...]');
console.log();
console.log('Available options:');
console.log();
console.log(' --format=type Set the report format, plain (default) or junit');
console.log(' -v, --version Print program version');
console.log();
process.exit(1);
}
options = {
format: 'plain'
};
fnames = [];
process.argv.splice(2).forEach(function (entry) {
if (forceFile || entry === '-' || entry.slice(0, 1) !== '-') {
fnames.push(entry);
} else if (entry === '-h' || entry === '--help') {
showUsage();
} else if (entry === '-v' || entry === '--version') {
console.log('ECMAScript Validator (using Esprima version', esprima.version, ')');
console.log();
process.exit(0);
} else if (entry.slice(0, 9) === '--format=') {
options.format = entry.slice(9);
if (options.format !== 'plain' && options.format !== 'junit') {
console.log('Error: unknown report format ' + options.format + '.');
process.exit(1);
}
} else if (entry === '--') {
forceFile = true;
} else {
console.log('Error: unknown option ' + entry + '.');
process.exit(1);
}
});
if (fnames.length === 0) {
fnames.push('');
}
if (options.format === 'junit') {
console.log('<?xml version="1.0" encoding="UTF-8"?>');
console.log('<testsuites>');
}
count = 0;
function run(fname, content) {
var timestamp, syntax, name;
try {
if (typeof content !== 'string') {
throw content;
}
if (content[0] === '#' && content[1] === '!') {
content = '//' + content.substr(2, content.length);
}
timestamp = Date.now();
syntax = esprima.parse(content, { tolerant: true });
if (options.format === 'junit') {
name = fname;
if (name.lastIndexOf('/') >= 0) {
name = name.slice(name.lastIndexOf('/') + 1);
}
console.log('<testsuite name="' + fname + '" errors="0" ' +
' failures="' + syntax.errors.length + '" ' +
' tests="' + syntax.errors.length + '" ' +
' time="' + Math.round((Date.now() - timestamp) / 1000) +
'">');
syntax.errors.forEach(function (error) {
var msg = error.message;
msg = msg.replace(/^Line\ [0-9]*\:\ /, '');
console.log(' <testcase name="Line ' + error.lineNumber + ': ' + msg + '" ' +
' time="0">');
console.log(' <error type="SyntaxError" message="' + error.message + '">' +
error.message + '(' + name + ':' + error.lineNumber + ')' +
'</error>');
console.log(' </testcase>');
});
console.log('</testsuite>');
} else if (options.format === 'plain') {
syntax.errors.forEach(function (error) {
var msg = error.message;
msg = msg.replace(/^Line\ [0-9]*\:\ /, '');
msg = fname + ':' + error.lineNumber + ': ' + msg;
console.log(msg);
++count;
});
}
} catch (e) {
++count;
if (options.format === 'junit') {
console.log('<testsuite name="' + fname + '" errors="1" failures="0" tests="1" ' +
' time="' + Math.round((Date.now() - timestamp) / 1000) + '">');
console.log(' <testcase name="' + e.message + '" ' + ' time="0">');
console.log(' <error type="ParseError" message="' + e.message + '">' +
e.message + '(' + fname + ((e.lineNumber) ? ':' + e.lineNumber : '') +
')</error>');
console.log(' </testcase>');
console.log('</testsuite>');
} else {
console.log('Error: ' + e.message);
}
}
}
fnames.forEach(function (fname) {
var content = '';
try {
if (fname && (fname !== '-' || forceFile)) {
content = fs.readFileSync(fname, 'utf-8');
} else {
fname = '';
process.stdin.resume();
process.stdin.on('data', function(chunk) {
content += chunk;
});
process.stdin.on('end', function() {
run(fname, content);
});
return;
}
} catch (e) {
content = e;
}
run(fname, content);
});
process.on('exit', function () {
if (options.format === 'junit') {
console.log('</testsuites>');
}
if (count > 0) {
process.exit(1);
}
if (count === 0 && typeof phantom === 'object') {
process.exit(0);
}
});
File diff suppressed because one or more lines are too long
+163
View File
@@ -0,0 +1,163 @@
{
"_args": [
[
{
"raw": "esprima@~3.0.0",
"scope": null,
"escapedName": "esprima",
"name": "esprima",
"rawSpec": "~3.0.0",
"spec": ">=3.0.0 <3.1.0",
"type": "range"
},
"c:\\xampp\\htdocs\\laravel\\node_modules\\redeyed"
]
],
"_from": "esprima@>=3.0.0 <3.1.0",
"_id": "esprima@3.0.0",
"_inCache": true,
"_location": "/redeyed/esprima",
"_nodeVersion": "6.3.1",
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/esprima-3.0.0.tgz_1472911974371_0.6553201307542622"
},
"_npmUser": {
"name": "ariya",
"email": "ariya.hidayat@gmail.com"
},
"_npmVersion": "3.10.3",
"_phantomChildren": {},
"_requested": {
"raw": "esprima@~3.0.0",
"scope": null,
"escapedName": "esprima",
"name": "esprima",
"rawSpec": "~3.0.0",
"spec": ">=3.0.0 <3.1.0",
"type": "range"
},
"_requiredBy": [
"/redeyed"
],
"_resolved": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz",
"_shasum": "53cf247acda77313e551c3aa2e73342d3fb4f7d9",
"_shrinkwrap": null,
"_spec": "esprima@~3.0.0",
"_where": "c:\\xampp\\htdocs\\laravel\\node_modules\\redeyed",
"author": {
"name": "Ariya Hidayat",
"email": "ariya.hidayat@gmail.com"
},
"bin": {
"esparse": "./bin/esparse.js",
"esvalidate": "./bin/esvalidate.js"
},
"bugs": {
"url": "https://github.com/jquery/esprima/issues"
},
"dependencies": {},
"description": "ECMAScript parsing infrastructure for multipurpose analysis",
"devDependencies": {
"codecov.io": "~0.1.6",
"escomplex-js": "1.2.0",
"everything.js": "~1.0.3",
"glob": "~7.0.0",
"istanbul": "~0.4.0",
"jscs": "~3.0.3",
"json-diff": "~0.3.1",
"karma": "~1.2.0",
"karma-chrome-launcher": "~2.0.0",
"karma-detect-browsers": "~2.1.0",
"karma-firefox-launcher": "~1.0.0",
"karma-ie-launcher": "~1.0.0",
"karma-mocha": "~1.1.1",
"karma-safari-launcher": "~1.0.0",
"karma-sauce-launcher": "~1.0.0",
"lodash": "~3.10.1",
"mocha": "~3.0.2",
"node-tick-processor": "~0.0.2",
"regenerate": "~1.3.1",
"temp": "~0.8.3",
"tslint": "~3.15.1",
"typescript": "~1.8.10",
"typescript-formatter": "~1.2.0",
"unicode-8.0.0": "~0.7.0",
"webpack": "~1.13.2"
},
"directories": {},
"dist": {
"shasum": "53cf247acda77313e551c3aa2e73342d3fb4f7d9",
"tarball": "https://registry.npmjs.org/esprima/-/esprima-3.0.0.tgz"
},
"engines": {
"node": ">=0.10.0"
},
"files": [
"bin",
"dist/esprima.js"
],
"gitHead": "dea024fc158259ed513d78c1bb910ce847fd556c",
"homepage": "http://esprima.org",
"keywords": [
"ast",
"ecmascript",
"esprima",
"javascript",
"parser",
"syntax"
],
"license": "BSD-2-Clause",
"main": "dist/esprima.js",
"maintainers": [
{
"name": "ariya",
"email": "ariya.hidayat@gmail.com"
}
],
"name": "esprima",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git+https://github.com/jquery/esprima.git"
},
"scripts": {
"all-tests": "npm run generate-fixtures && npm run unit-tests && npm run api-tests && npm run grammar-tests && npm run regression-tests && npm run hostile-env-tests",
"analyze-coverage": "istanbul cover test/unit-tests.js",
"api-tests": "mocha -R dot test/api-tests.js",
"appveyor": "npm run compile && npm run all-tests && npm run browser-tests",
"benchmark": "npm run benchmark-parser && npm run benchmark-tokenizer",
"benchmark-parser": "node -expose_gc test/benchmark-parser.js",
"benchmark-tokenizer": "node --expose_gc test/benchmark-tokenizer.js",
"browser-tests": "npm run compile && npm run generate-fixtures && cd test && karma start --single-run",
"check-coverage": "istanbul check-coverage --statement 100 --branch 100 --function 100",
"check-version": "node test/check-version.js",
"circleci": "npm test && npm run codecov && npm run downstream",
"code-style": "tsfmt --verify src/*.ts && jscs -p crockford test/*.js",
"codecov": "istanbul report cobertura && codecov < ./coverage/cobertura-coverage.xml",
"compile": "tsc -p src/ && webpack && node tools/fixupbundle.js",
"complexity": "node test/check-complexity.js",
"downstream": "node test/downstream.js",
"droneio": "npm test && npm run saucelabs-evergreen && npm run saucelabs-ie && npm run saucelabs-safari",
"dynamic-analysis": "npm run analyze-coverage && npm run check-coverage",
"format-code": "tsfmt -r src/*.ts",
"generate-fixtures": "node tools/generate-fixtures.js",
"generate-regex": "node tools/generate-identifier-regex.js",
"generate-xhtml-entities": "node tools/generate-xhtml-entities.js",
"grammar-tests": "node test/grammar-tests.js",
"hostile-env-tests": "node test/hostile-environment-tests.js",
"prepublish": "npm run compile",
"profile": "node --prof test/profile.js && mv isolate*.log v8.log && node-tick-processor",
"regression-tests": "node test/regression-tests.js",
"saucelabs-evergreen": "cd test && karma start saucelabs-evergreen.conf.js",
"saucelabs-ie": "cd test && karma start saucelabs-ie.conf.js",
"saucelabs-safari": "cd test && karma start saucelabs-safari.conf.js",
"static-analysis": "npm run check-version && npm run tslint && npm run code-style && npm run complexity",
"test": "npm run compile && npm run all-tests && npm run static-analysis && npm run dynamic-analysis",
"travis": "npm test",
"tslint": "tslint src/*.ts",
"unit-tests": "node test/unit-tests.js"
},
"version": "3.0.0"
}
+101
View File
@@ -0,0 +1,101 @@
{
"_args": [
[
{
"raw": "redeyed@~1.0.0",
"scope": null,
"escapedName": "redeyed",
"name": "redeyed",
"rawSpec": "~1.0.0",
"spec": ">=1.0.0 <1.1.0",
"type": "range"
},
"c:\\xampp\\htdocs\\laravel\\node_modules\\cardinal"
]
],
"_from": "redeyed@>=1.0.0 <1.1.0",
"_id": "redeyed@1.0.1",
"_inCache": true,
"_location": "/redeyed",
"_nodeVersion": "6.9.2-pre",
"_npmOperationalInternal": {
"host": "packages-18-east.internal.npmjs.com",
"tmp": "tmp/redeyed-1.0.1.tgz_1477504514782_0.6673275073990226"
},
"_npmUser": {
"name": "thlorenz",
"email": "thlorenz@gmx.de"
},
"_npmVersion": "2.15.11",
"_phantomChildren": {},
"_requested": {
"raw": "redeyed@~1.0.0",
"scope": null,
"escapedName": "redeyed",
"name": "redeyed",
"rawSpec": "~1.0.0",
"spec": ">=1.0.0 <1.1.0",
"type": "range"
},
"_requiredBy": [
"/cardinal"
],
"_resolved": "https://registry.npmjs.org/redeyed/-/redeyed-1.0.1.tgz",
"_shasum": "e96c193b40c0816b00aec842698e61185e55498a",
"_shrinkwrap": null,
"_spec": "redeyed@~1.0.0",
"_where": "c:\\xampp\\htdocs\\laravel\\node_modules\\cardinal",
"author": {
"name": "Thorsten Lorenz",
"email": "thlorenz@gmx.de",
"url": "thlorenz.com"
},
"bugs": {
"url": "https://github.com/thlorenz/redeyed/issues"
},
"dependencies": {
"esprima": "~3.0.0"
},
"description": "Takes JavaScript code, along with a config and returns the original code with tokens wrapped as configured.",
"devDependencies": {
"cardinal": "~0.4.4",
"readdirp": "~0.3.3",
"tap": "~5.7.0"
},
"directories": {},
"dist": {
"shasum": "e96c193b40c0816b00aec842698e61185e55498a",
"tarball": "https://registry.npmjs.org/redeyed/-/redeyed-1.0.1.tgz"
},
"gitHead": "8e0a38c0e512cb52da4d204d8e2f9c9dfd81bc0b",
"homepage": "https://github.com/thlorenz/redeyed#readme",
"keywords": [
"ast",
"syntax",
"tree",
"source",
"wrap",
"metadata"
],
"license": "MIT",
"main": "redeyed.js",
"maintainers": [
{
"name": "thlorenz",
"email": "thlorenz@gmx.de"
}
],
"name": "redeyed",
"optionalDependencies": {},
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://github.com/thlorenz/redeyed.git"
},
"scripts": {
"demo": "cd examples/browser; open index.html",
"demo-log": "node examples/replace-log",
"test": "tap test/*.js"
},
"version": "1.0.1"
}
+324
View File
@@ -0,0 +1,324 @@
;(function () {
'use strict';
/*jshint laxbreak: true, browser:true */
/*global define*/
var esprima
, exportFn
, toString = Object.prototype.toString
;
if (typeof module === 'object' && typeof module.exports === 'object' && typeof require === 'function') {
// server side
esprima = require('esprima');
exportFn = function (redeyed) { module.exports = redeyed; };
bootstrap(esprima, exportFn);
} else if (typeof define === 'function' && define.amd) {
// client side
// amd
define(['esprima'], function (esprima) {
return bootstrap(esprima);
});
} else if (typeof window === 'object') {
// no amd -> attach to window if it exists
// Note that this requires 'esprima' to be defined on the window, so that script has to be loaded first
window.redeyed = bootstrap(window.esprima);
}
function bootstrap(esprima, exportFn) {
function isFunction (obj) {
return toString.call(obj) === '[object Function]';
}
function isString (obj) {
return toString.call(obj) === '[object String]';
}
function isNumber (obj) {
return toString.call(obj) === '[object Number]';
}
function isObject (obj) {
return toString.call(obj) === '[object Object]';
}
function surroundWith (before, after) {
return function (s) { return before + s + after; };
}
function isNonCircular(key) {
return key !== '_parent';
}
function objectizeString (value) {
var vals = value.split(':');
if (0 === vals.length || vals.length > 2)
throw new Error(
'illegal string config: ' + value +
'\nShould be of format "before:after"'
);
if (vals.length === 1 || vals[1].length === 0) {
return vals.indexOf(':') < 0 ? { _before: vals[0] } : { _after: vals[0] };
} else {
return { _before: vals[0], _after: vals[1] };
}
}
function objectize (node) {
// Converts 'bef:aft' to { _before: bef, _after: aft }
// and resolves undefined before/after from parent or root
function resolve (value, key) {
// resolve before/after from root or parent if it isn't present on the current node
if (!value._parent) return undefined;
// Immediate parent
if (value._parent._default && value._parent._default[key]) return value._parent._default[key];
// Root
var root = value._parent._parent;
if (!root) return undefined;
return root._default ? root._default[key] : undefined;
}
function process (key) {
var value = node[key];
if (!value) return;
if (isFunction(value)) return;
// normalize all strings to objects
if (isString(value)) {
node[key] = value = objectizeString(value);
}
value._parent = node;
if (isObject(value)) {
if (!value._before && !value._after) return objectize (value);
// resolve missing _before or _after from parent(s)
// in case we only have either one on this node
value._before = value._before || resolve(value, '_before');
value._after = value._after || resolve(value, '_after');
return;
}
throw new Error('nodes need to be either {String}, {Object} or {Function}.' + value + ' is neither.');
}
// Process _default ones first so children can resolve missing before/after from them
if (node._default) process('_default');
Object.keys(node)
.filter(function (key) {
return isNonCircular(key)
&& node.hasOwnProperty(key)
&& key !== '_before'
&& key !== '_after'
&& key !== '_default';
})
.forEach(process);
}
function functionize (node) {
Object.keys(node)
.filter(function (key) {
return isNonCircular(key) && node.hasOwnProperty(key);
})
.forEach(function (key) {
var value = node[key];
if (isFunction(value)) return;
if (isObject(value)) {
if (!value._before && !value._after) return functionize(value);
// at this point before/after were "inherited" from the parent or root
// (see objectize)
var before = value._before || '';
var after = value._after || '';
node[key] = surroundWith (before, after);
return node[key];
}
});
}
function normalize (root) {
objectize(root);
functionize(root);
}
function mergeTokensAndComments(tokens, comments) {
var all = {};
function addToAllByRangeStart(t) { all[ t.range[0] ] = t; }
tokens.forEach(addToAllByRangeStart);
comments.forEach(addToAllByRangeStart);
// keys are sorted automatically
return Object.keys(all)
.map(function (k) { return all[k]; });
}
function redeyed (code, config, opts) {
opts = opts || {};
var parser = opts.parser || esprima;
var buildAst = !!opts.buildAst;
var hashbang = ''
, ast
, tokens
, comments
, lastSplitEnd = 0
, splits = []
, transformedCode
, all
, info
;
// Replace hashbang line with empty whitespaces to preserve token locations
if (code[0] === '#' && code[1] === '!') {
hashbang = code.substr(0, code.indexOf('\n') + 1);
code = Array.apply(0, Array(hashbang.length)).join(' ') + '\n' + code.substr(hashbang.length);
}
if (buildAst) {
ast = parser.parse(code, { tokens: true, comment: true, range: true, tolerant: true });
tokens = ast.tokens;
comments = ast.comments;
} else {
tokens = [];
comments = [];
parser.tokenize(code, { range: true, comment: true }, function (token) {
if (token.type === 'LineComment') {
token.type = 'Line';
comments.push(token)
} else if (token.type === 'BlockComment') {
token.type = 'Block';
comments.push(token)
} else {
// Optimistically upgrade 'static' to a keyword
if (token.type === 'Identifier' && token.value === 'static') token.type = 'Keyword';
tokens.push(token);
}
});
}
normalize(config);
function tokenIndex(tokens, tkn, start) {
var current
, rangeStart = tkn.range[0];
for (current = start; current < tokens.length; current++) {
if (tokens[current].range[0] === rangeStart) return current;
}
throw new Error('Token %s not found at or after index: %d', tkn, start);
}
function process(surround) {
var result
, currentIndex
, nextIndex
, skip = 0
, splitEnd
;
result = surround(code.slice(start, end), info);
if (isObject(result)) {
splits.push(result.replacement);
currentIndex = info.tokenIndex;
nextIndex = tokenIndex(info.tokens, result.skipPastToken, currentIndex);
skip = nextIndex - currentIndex;
splitEnd = skip > 0 ? tokens[nextIndex - 1].range[1] : end;
} else {
splits.push(result);
splitEnd = end;
}
return { skip: skip, splitEnd: splitEnd };
}
function addSplit (start, end, surround, info) {
var result
, nextIndex
, skip = 0
;
if (start >= end) return;
if (surround) {
result = process(surround);
skip = result.skip;
lastSplitEnd = result.splitEnd;
} else {
splits.push(code.slice(start, end));
lastSplitEnd = end;
}
return skip;
}
all = mergeTokensAndComments(tokens, comments);
for (var tokenIdx = 0; tokenIdx < all.length; tokenIdx++) {
var token = all[tokenIdx]
, surroundForType = config[token.type]
, surround
, start
, end;
// At least the type (e.g., 'Keyword') needs to be specified for the token to be surrounded
if (surroundForType) {
// root defaults are only taken into account while resolving before/after otherwise
// a root default would apply to everything, even if no type default was specified
surround = surroundForType
&& surroundForType.hasOwnProperty(token.value)
&& surroundForType[token.value]
&& isFunction(surroundForType[token.value])
? surroundForType[token.value]
: surroundForType._default;
start = token.range[0];
end = token.range[1];
addSplit(lastSplitEnd, start);
info = { tokenIndex: tokenIdx, tokens: all, ast: ast, code: code };
tokenIdx += addSplit(start, end, surround, info);
}
}
if (lastSplitEnd < code.length) {
addSplit(lastSplitEnd, code.length);
}
if (!opts.nojoin) {
transformedCode = splits.join('');
if (hashbang.length > 0) {
transformedCode = hashbang + transformedCode.substr(hashbang.length);
}
}
return {
ast : ast
, tokens : tokens
, comments : comments
, splits : splits
, code : transformedCode
};
}
return exportFn ? exportFn(redeyed) : redeyed;
}
})();
+56
View File
@@ -0,0 +1,56 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('adding custom asserts ... ', function (t) {
t.constructor.prototype.assertSurrounds = function (code, opts, expected) {
var result = redeyed(code, opts).code
this.equals(result, expected, inspect(code) + ' => ' + inspect(expected))
return this;
}
t.end()
})
test('\nbefore/after config, keywords', function (t) {
var opts001 = { Keyword: { _default: { _before: '*', _after: '&' } } };
t.test('\n# ' + inspect(opts001), function (t) {
t.assertSurrounds('this', opts001, '*this&')
t.assertSurrounds('if (a == 1) return', opts001, '*if& (a == 1) *return&')
t.assertSurrounds('var n = new Test();', opts001, '*var& n = *new& Test();')
t.end()
})
var opts002 = {
Keyword: {
'function': { _before: '^' }
, 'return': { _before: '(', _after: ')' }
, _default: { _before: '*' , _after: '&' }
}
};
t.test('\n# ' + inspect(opts002), function (t) {
t.assertSurrounds(
[ 'function foo (bar) {'
, ' var a = 3;'
, ' return bar + a;'
, '}'
].join('\n')
, opts002
, [ '^function& foo (bar) {'
, ' *var& a = 3;'
, ' (return) bar + a;'
, '}'
].join('\n'))
t.end()
})
t.end()
})
+69
View File
@@ -0,0 +1,69 @@
'use strict'
/*jshint asi: true, browser: true*/
/*global define window */
var test = require('tap').test
, util = require('util')
, redeyedExport = require('..')
, redeyedkey = require.resolve('..')
, esprima = require('esprima')
function setup() {
// remove redeyed from require cache to force re-require for each test
delete require.cache[redeyedkey];
// remove globals
delete global.window;
delete global.define;
}
// TODO: need to run in vm in order to properly simulate require and module not being present
return;
test('define and window exist', function (t) {
var defineCb
, deps
setup()
// declare browser globals
global.window = { }
global.define = function (deps_, cb) {
deps_ = deps
defineCb = cb
}
define.amd = true
var redeyed = require('..')
, definedredeyed = defineCb(esprima)
t.equal(window.redeyed, undefined, 'redeyed is not attached to window')
t.notEqual(redeyed.toString(), redeyedExport.toString(), 'redeyed is not exported')
t.equal(definedredeyed.toString(), redeyedExport.toString(), 'redeyed is defined')
t.end()
})
test('window exists, but define doesn\'t', function (t) {
setup()
// declare browser globals
global.window = { esprima: esprima }
var redeyed = require('..')
t.equal(window.redeyed.toString(), redeyedExport.toString(), 'redeyed is attached to window')
t.notEqual(redeyed.toString(), redeyedExport.toString(), 'redeyed is not exported')
t.end()
})
test('neither window nor define exist', function (t) {
setup()
var redeyed = require('..')
t.equal(redeyed.toString(), redeyedExport.toString(), 'redeyed is exported')
t.end()
})
+75
View File
@@ -0,0 +1,75 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('adding custom asserts ... ', function (t) {
t.constructor.prototype.assertSurrounds = function (code, opts, expected) {
var result = redeyed(code, opts)
this.equals(result.code, expected, inspect(code) + ' => ' + inspect(expected))
return this;
}
t.end()
})
test('\nstring config, Line comments', function (t) {
var opts = { Line: { _default: '*:&' } };
t.test('\n# ' + inspect(opts), function (t) {
t.assertSurrounds(
'// a comment'
, opts
, '*// a comment&'
)
t.assertSurrounds(
'// comment then new line\nif (a == 1) return'
, opts
, '*// comment then new line&\nif (a == 1) return'
)
t.assertSurrounds(
'var n = new Test();// some comment after\n//more comment\nvar s = 3;'
, opts
, 'var n = new Test();*// some comment after&\n*//more comment&\nvar s = 3;'
)
t.end()
})
t.end()
})
test('\nstring config, Block comments', function (t) {
var opts = { Block: { _default: '_:-' } };
t.test('\n# ' + inspect(opts), function (t) {
t.assertSurrounds(
'/* a comment */'
, opts
, '_/* a comment */-'
)
t.assertSurrounds(
'/* comment then new line*/\nif (a == 1) /* inline */ return'
, opts
, '_/* comment then new line*/-\nif (a == 1) _/* inline */- return'
)
t.assertSurrounds(
'var n = new Test();/* some comment after*/\n/*more comment*/\nvar s = 3;'
, opts
, 'var n = new Test();_/* some comment after*/-\n_/*more comment*/-\nvar s = 3;'
)
t.assertSurrounds(
'var a = 4;\n/* Multi line comment\n * Next line\n * and another\n*/ var morecode = "here";'
, opts
, 'var a = 4;\n_/* Multi line comment\n * Next line\n * and another\n*/- var morecode = "here";'
)
t.end()
})
t.end()
})
+60
View File
@@ -0,0 +1,60 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('adding custom asserts ... ', function (t) {
t.constructor.prototype.assertSurrounds = function (code, opts, expected) {
var optsi = inspect(opts);
var result = redeyed(code, opts).code
this.equals( result
, expected
, util.format('%s: %s => %s', optsi, inspect(code), inspect(expected))
)
return this;
}
t.end()
})
test('\n undefineds only', function (t) {
t.assertSurrounds('1 + 2', { Numeric: { _default: undefined } }, '1 + 2')
t.assertSurrounds('1 + 2', { Numeric: { _default: undefined }, _default: undefined }, '1 + 2')
t.assertSurrounds(
'return true'
, { 'Boolean': { 'true': undefined, 'false': undefined, _default: undefined } , _default: undefined }
, 'return true'
)
t.end()
})
test('\n mixed', function (t) {
t.assertSurrounds(
'return true || false'
, { 'Boolean': { 'true': '&:', 'false': undefined, _default: undefined } , _default: undefined }
, 'return &true || false'
)
t.assertSurrounds(
'return true || false'
, { 'Boolean': { 'true': '&:', 'false': undefined, _default: ':?' } , _default: undefined }
, 'return &true? || false?'
)
t.assertSurrounds(
'return true || false'
, { 'Boolean': { 'true': '&:', 'false': undefined, _default: undefined } , _default: ':?' }
, 'return &true? || false'
)
t.end()
})
+58
View File
@@ -0,0 +1,58 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('function - config passing idx and tokens', function (t) {
var args = []
, opts001 = {
Boolean: {
_default: identity
}
, Keyword: {
_default: identity
}
, Identifier: {
_default: identity
}
, Punctuator: {
_default: identity
}
}
, code = 'var fn = function () { return true; }'
function identity (s, info) {
args.push( { value: s, idx: info.tokenIndex, tokens: info.tokens, code: info.code })
// returning unchanged string will keep the splits be equal to the original tokens
return s
}
function tokenValue (t) { return t.value; }
t.test(inspect(opts001) + ' -- ' + code, function (t) {
var result = redeyed(code, opts001, { splits: true })
, tokens = result.tokens
t.equals(args.length, tokens.length, 'called with all tokens')
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i]
, arg = args[i]
t.equals(arg.value, token.value, 'passes correct value: ' + inspect([ arg.value, token.value ]))
t.equals(arg.idx, i, 'passes correct index')
t.equals(arg.code, code, 'passes code')
t.deepEquals(arg.tokens, tokens, 'passes all tokens')
}
t.end()
})
t.end()
})
+89
View File
@@ -0,0 +1,89 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('given i skip 2 more tokens after each semicolon', function (t) {
var calls = 0
, opts = {
Punctuator: {
';': function identity (s, info) {
// tell it to skip past second to last token that is 2 ahead of the current one
calls++
var skipToken = info.tokens[info.tokenIndex + 2]
return skipToken ? { replacement: s, skipPastToken: skipToken } : s;
}
}
}
;
[ { code: ';;;' , expectedCalls: 1 }
, { code: ';;;;' , expectedCalls: 2 }
, { code: '; ; ; ;' , expectedCalls: 2 }
, { code: ';;; ;;; ;;; ;' , expectedCalls: 4 }
, { code: ';;; ;;; ;;; ;;; ;' , expectedCalls: 5 }
, { code: ';;; ;;; ;;; ;;; ;;;' , expectedCalls: 5 }
, { code: ';;; ;;; ;;; ;;; ;;; ;' , expectedCalls: 6 }
].forEach(function (x) {
calls = 0
redeyed(x.code, opts);
t.equals(calls, x.expectedCalls, 'calls ' + x.expectedCalls + ' times for ' + x.code)
});
t.end()
})
test('replace log', function (t) {
var kinds = [ 'info', 'warn', 'error' ]
, opts = {
Identifier: {
console: function replaceLog(s, info) {
var code = info.code
, idx = info.tokenIndex
, tokens = info.tokens
, kind = tokens[idx + 2].value
, openParen = tokens[idx + 3].value
, firstArgTkn = tokens[idx + 4]
, argIdx = idx + 3
, open
, tkn
;
open = 1;
while (open) {
tkn = tokens[++argIdx];
if (tkn.value === '(') open++;
if (tkn.value === ')') open--;
}
var argsIncludingClosingParen = code.slice(firstArgTkn.range[0], tkn.range[1])
, result = 'log.' + kind + '("main-logger", ' + argsIncludingClosingParen;
return { replacement: result, skipPastToken: tkn };
}
}
}
, origCode = [
'console.info("info ", 1);'
, 'console.warn("warn ", 3);'
, 'console.error("error ", new Error("oh my!"));'
].join('\n')
, expectedCode = [
'log.info("main-logger", "info ", 1));'
, 'log.warn("main-logger", "warn ", 3));'
, 'log.error("main-logger", "error ", new Error("oh my!")));'
].join('\n')
, code = redeyed(origCode, opts).code
t.equals(code, expectedCode, 'transforms all log statements')
t.end()
});
+148
View File
@@ -0,0 +1,148 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('adding custom asserts ... ', function (t) {
t.constructor.prototype.assertSurrounds = function (code, opts, expected) {
var result = redeyed(code, opts).code
this.equals(result, expected, inspect(code) + ' => ' + inspect(expected))
return this;
}
t.end()
})
test('\nfunction config, keywords', function (t) {
var opts001 = { Keyword: { _default: function (s) { return '*' + s + '&'; } } };
t.test('\n# ' + inspect(opts001), function (t) {
t.assertSurrounds('this', opts001, '*this&')
t.assertSurrounds('this ', opts001, '*this& ')
t.assertSurrounds(' this', opts001, ' *this&')
t.assertSurrounds(' this ', opts001, ' *this& ')
t.assertSurrounds('if (a == 1) return', opts001, '*if& (a == 1) *return&')
t.assertSurrounds('var n = new Test();', opts001, '*var& n = *new& Test();')
t.assertSurrounds(
[ 'function foo (bar) {'
, ' var a = 3;'
, ' return bar + a;'
, '}'
].join('\n')
, opts001
, [ '*function& foo (bar) {'
, ' *var& a = 3;'
, ' *return& bar + a;'
, '}'
].join('\n'))
t.end()
})
var opts002 = {
Keyword: {
'function': function (s) { return '^' + s + '&' }
, 'return': function (s) { return '(' + s + ')' }
, _default: function (s) { return '*' + s + '&' }
}
};
t.test('\n# ' + inspect(opts002), function (t) {
t.assertSurrounds(
[ 'function foo (bar) {'
, ' var a = 3;'
, ' return bar + a;'
, '}'
].join('\n')
, opts002
, [ '^function& foo (bar) {'
, ' *var& a = 3;'
, ' (return) bar + a;'
, '}'
].join('\n'))
t.end()
})
t.end()
})
test('#\n functin config - resolving', function (t) {
var opts001 = {
Keyword: {
'var': function (s) { return '^' + s + '&' }
}
, _default: function (s) { return '*' + s + '&' }
};
t.test('\n# specific but no type default and root default - root default not applied' + inspect(opts001), function (t) {
t.assertSurrounds('var n = new Test();', opts001, '^var& n = new Test();').end();
})
var opts002 = {
Keyword: {
'var': function (s) { return '^' + s + '&' }
, _default: function (s) { return '*' + s + '&' }
}
, _default: function (s) { return '(' + s + ')' }
};
t.test('\n# no type default but root default' + inspect(opts002), function (t) {
t.assertSurrounds('var n = new Test();', opts002, '^var& n = *new& Test();').end();
})
t.end()
})
test('#\n function config - replacing', function (t) {
var opts001 = {
Keyword: {
'var': function () { return 'const' }
}
};
t.test('\n# type default and root default (type wins)' + inspect(opts001), function (t) {
t.assertSurrounds('var n = new Test();', opts001, 'const n = new Test();').end();
})
var opts002 = {
Keyword: {
_default: function () { return 'const' }
}
};
t.test('\n# type default' + inspect(opts002), function (t) {
t.assertSurrounds('var n = new Test();', opts002, 'const n = const Test();').end();
})
var opts003 = {
Keyword: {
'new': function () { return 'NEW'; }
, _default: function () { return 'const' }
}
};
t.test('\n# specific and type default' + inspect(opts003), function (t) {
t.assertSurrounds('var n = new Test();', opts003, 'const n = NEW Test();').end();
})
var opts004 = {
Keyword: {
_default: function (s) { return s.toUpperCase() }
}
, _default: function (s) { return 'not applied'; }
};
t.test('\n# type default and root default (type wins)' + inspect(opts004), function (t) {
t.assertSurrounds('var n = new Test();', opts004, 'VAR n = NEW Test();').end();
})
var opts005 = {
Keyword: { }
, _default: function (s) { return s.toUpperCase() }
};
t.test('\n# no type default only root default - not applied' + inspect(opts005), function (t) {
t.assertSurrounds('var n = new Test();', opts005, 'var n = new Test();').end();
})
t.end()
})
+38
View File
@@ -0,0 +1,38 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('adding custom asserts ... ', function (t) {
t.constructor.prototype.assertSurrounds = function (code, opts, expected) {
var optsi = inspect(opts);
var result = redeyed(code, opts).code
this.equals( result
, expected
, util.format('%s: %s => %s', optsi, inspect(code), inspect(expected))
)
return this;
}
t.end()
})
test('incomplete statement', function (t) {
t.test('\n# Keyword', function (t) {
var keyconfig = { 'Keyword': { _default: '$:%' } };
t.assertSurrounds('if(foo) { x', keyconfig, '$if%(foo) { x')
t.assertSurrounds('class Foo { constructor(name)', keyconfig, '$class% Foo { constructor(name)')
t.assertSurrounds('const x = ', keyconfig, '$const% x = ')
t.assertSurrounds('function g() { yield', keyconfig, '$function% g() { $yield%')
t.end()
})
t.end()
})
+45
View File
@@ -0,0 +1,45 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('adding custom asserts ... ', function (t) {
t.constructor.prototype.assertSurrounds = function (code, opts, expected) {
var optsi = inspect(opts);
var result = redeyed(code, opts).code
this.equals( result
, expected
, util.format('%s: %s => %s', optsi, inspect(code), inspect(expected))
)
return this;
}
t.end()
})
test('types', function (t) {
t.test('\n# Keyword', function (t) {
var keyconfig = { 'Keyword': { _default: '$:%' } };
t.assertSurrounds('import foo from \'foo\';', keyconfig, '$import% foo from \'foo\';')
t.assertSurrounds('export default foo;', keyconfig, '$export% $default% foo;')
t.assertSurrounds('if(foo) { let bar = 1;}', keyconfig, '$if%(foo) { $let% bar = 1;}')
t.assertSurrounds('const x = "foo";', keyconfig, '$const% x = "foo";')
t.assertSurrounds('"use strict";(function* () { yield *v })', keyconfig, '"use strict";($function%* () { $yield% *v })')
t.assertSurrounds('"use strict"; (class A { static constructor() { super() }})', keyconfig
,'"use strict"; ($class% A { $static% constructor() { $super%() }})')
t.assertSurrounds('class Foo { constructor(name){this.name = name;}}', keyconfig
, '$class% Foo { constructor(name){$this%.name = name;}}')
t.assertSurrounds('class Foo extends Bar { constructor(name,value){super(value);this.name = name;}}', keyconfig
, '$class% Foo $extends% Bar { constructor(name,value){$super%(value);$this%.name = name;}}')
t.end()
})
t.end()
})
+48
View File
@@ -0,0 +1,48 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('adding custom asserts ... ', function (t) {
t.constructor.prototype.assertSurrounds = function (code, opts, expected) {
var result = redeyed(code, opts).code
this.equals(result, expected, inspect(code) + ' => ' + inspect(expected))
return this;
}
t.end()
})
test('\nmixed config, keywords', function (t) {
var opts001 = {
Keyword: {
'this': function (s) { return '_' + s; }
, 'if': { _before: '^' }
, _default: '*:&'
}
};
t.test('\n# ' + inspect(opts001), function (t) {
t.assertSurrounds('if (this.hello) return "world";', opts001, '^if& (_this.hello) *return& "world";').end()
})
var opts002 = {
Keyword: {
'this': function (s) { return '_' + s; }
, 'if': { _before: '^' }
, 'return': ':)'
, _default: ':&'
}
, _default: '*:&'
};
t.test('\n# ' + inspect(opts002), function (t) {
t.assertSurrounds('if (this.hello) return "world";', opts002, '^if& (_this.hello) *return) "world";').end()
})
t.end()
})
+65
View File
@@ -0,0 +1,65 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
, esprima = require('esprima')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('redeyed result does not have esprima ast by default', function (t) {
var code = '// a comment\nvar a = 3;'
, conf = { Keyword: { _default: '_:-' } }
, ast = esprima.parse(code, { tokens: true, comment: true, range: true, tolerant: true })
, tokens = ast.tokens
, comments = ast.comments
, result = redeyed(code, conf)
t.type(result.ast, 'undefined', 'ast')
t.deepEquals(result.tokens, tokens, 'tokens')
t.deepEquals(result.comments, comments, 'comments')
t.notEquals(result.code, undefined, 'code')
t.end()
});
test('redeyed result has esprima ast, tokens, comments and splits and transformed code', function (t) {
var code = '// a comment\nvar a = 3;'
, conf = { Keyword: { _default: '_:-' } }
, ast = esprima.parse(code, { tokens: true, comment: true, range: true, tolerant: true })
, tokens = ast.tokens
, comments = ast.comments
, result = redeyed(code, conf, { buildAst: true } )
console.log(ast)
t.deepEquals(result.ast, ast, 'ast')
t.deepEquals(result.tokens, tokens, 'tokens')
t.deepEquals(result.comments, comments, 'comments')
t.notEquals(result.code, undefined, 'code')
t.end()
});
test('redeyed result - { nojoin } has esprima ast, tokens, comments and splits but no transformed code', function (t) {
var code = '// a comment\nvar a = 3;'
, conf = { Keyword: { _default: '_:-' } }
, ast = esprima.parse(code, { tokens: true, comment: true, range: true, tolerant: true })
, tokens = ast.tokens
, comments = ast.comments
, result = redeyed(code, conf, { nojoin: true, buildAst: true })
t.deepEquals(result.ast, ast, 'ast')
t.deepEquals(result.tokens, tokens, 'tokens')
t.deepEquals(result.comments, comments, 'comments')
t.equals(result.code, undefined, 'code')
t.end()
});
+22
View File
@@ -0,0 +1,22 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('properly handles script level return -- no blow up', function (t) {
var code = [
, 'return 1;'
].join('\n')
, opts = { Keyword: { 'return': '%:^' } }
, expected = '\n%return^ 1;'
, res = redeyed(code, opts).code
t.equals(res, expected, inspect(code) + ' opts: ' + inspect(opts) + ' => ' + inspect(expected))
t.end()
})
+26
View File
@@ -0,0 +1,26 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('preserves shebang', function (t) {
var code = [
'#!/usr/bin/env node'
, 'var util = require("util");'
].join('\n')
, opts = { Keyword: { 'var': '%:^' } }
, expected = [
'#!/usr/bin/env node'
, '%var^ util = require("util");'
].join('\n')
, res = redeyed(code, opts).code
t.equals(res, expected, inspect(code) + ' opts: ' + inspect(opts) + ' => ' + inspect(expected))
t.end()
})
+78
View File
@@ -0,0 +1,78 @@
'use strict';
/*jshint asi: true*/
// applying redeyed to a bunch of files of contained libraries as a smoke test
var test = require('tap').test
, path = require('path')
, fs = require('fs')
, readdirp = require('readdirp')
, redeyed = require('..')
, esprima = require('esprima')
, node_modules = path.join(__dirname, '..', 'node_modules')
, tapdir = path.join(node_modules, 'tap')
, esprimadir = path.join(node_modules, 'esprima')
test('tap', function (t) {
var invalidTapFiles = [
, 'slide/lib/async-map-ordered.js'
]
function shouldProcess (path) {
var include = true
invalidTapFiles.every(function (entry) {
return include = (path.indexOf(entry) < 0)
});
return include
}
function containsVarKeyword(code) {
code = code.replace(/^#!([^\r\n]+)/, function(match, captured) { return "//" + captured; });
return esprima.tokenize(code).some(function (t) {
return t.type === 'Keyword' && t.value === 'var'
})
}
readdirp({ root: tapdir, fileFilter: '*.js' })
.on('data', function (entry) {
var code = fs.readFileSync(entry.fullPath, 'utf-8')
if (!shouldProcess(entry.fullPath) || !containsVarKeyword(code)) return
var resultAst = redeyed(code, { Keyword: { 'var': '+:-' } }, { buildAst: true }).code
, resultTokenize = redeyed(code, { Keyword: { 'var': '+:-' } }, { buildAst: false }).code
t.assert(~resultAst.indexOf('+var-') || !(~resultAst.indexOf('var ')), 'redeyed ' + entry.path)
t.assert(~resultTokenize.indexOf('+var-') || !(~resultTokenize.indexOf('var ')), 'redeyed ' + entry.path)
})
.on('end', t.end.bind(t))
})
test('esprima', function (t) {
readdirp({ root: esprimadir, fileFilter: '*.js' })
.on('data', function (entry) {
var code = fs.readFileSync(entry.fullPath, 'utf-8')
, resultAst = redeyed(code, { Keyword: { 'var': '+:-' } }, { buildAst: true }).code
, resultTokenize = redeyed(code, { Keyword: { 'var': '+:-' } }, { buildAst: false }).code
t.assert(~resultAst.indexOf('+var-') || !(~resultAst.indexOf('var ')), 'redeyed ' + entry.path)
t.assert(~resultTokenize.indexOf('+var-') || !(~resultTokenize.indexOf('var ')), 'redeyed ' + entry.path)
})
.on('end', t.end.bind(t))
})
test('redeyed', function (t) {
readdirp({ root: path.join(__dirname, '..'), fileFilter: '*.js', directoryFilter: ['!.git', '!node_modules' ] })
.on('data', function (entry) {
var code = fs.readFileSync(entry.fullPath, 'utf-8')
, result = redeyed(code, { Keyword: { 'var': '+:-' } }).code
t.assert(~result.indexOf('+var-') || !(~result.indexOf('var ')), 'redeyed ' + entry.path)
})
.on('end', t.end.bind(t))
})
+127
View File
@@ -0,0 +1,127 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('adding custom asserts ... ', function (t) {
t.constructor.prototype.assertSurrounds = function (code, opts, expected) {
var result = redeyed(code, opts).code
this.equals(result, expected, inspect(code) + ' => ' + inspect(expected))
return this;
}
t.end()
})
test('\nstring config, keywords', function (t) {
var opts001 = { Keyword: { _default: '*:&' } };
t.test('\n# ' + inspect(opts001), function (t) {
t.assertSurrounds('this', opts001, '*this&')
t.assertSurrounds('if (a == 1) return', opts001, '*if& (a == 1) *return&')
t.assertSurrounds('var n = new Test();', opts001, '*var& n = *new& Test();')
t.end()
})
var opts002 = {
Keyword: {
'function': '^:'
, 'return': '(:)'
, _default: '*:&'
}
};
t.test('\n# ' + inspect(opts002), function (t) {
t.assertSurrounds(
[ 'function foo (bar) {'
, ' var a = 3;'
, ' return bar + a;'
, '}'
].join('\n')
, opts002
, [ '^function& foo (bar) {'
, ' *var& a = 3;'
, ' (return) bar + a;'
, '}'
].join('\n'))
t.end()
})
t.end()
})
test('\nstring configs resolve from type and root', function (t) {
var code = 'var a = new Test();'
function run(t, conf, expected, code_) {
t.test('\n# ' + inspect(conf), function (t) {
t.assertSurrounds(code_ || code, conf, expected);
t.end()
})
}
// at least the token kind has to be configured in order for the root_default to be applied
// otherwise a root._default would affect all tokens, even the ones we want to leave unchanged
run(t, { _default: '*:' }, 'var a = new Test();')
t.test('\n\n# only before or after specified, but no root._default', function (t) {
run(t, { Keyword: { _default: '*:' } }, '*var a = *new Test();')
run(t, { Keyword: { _default: ':-' } }, 'var- a = new- Test();')
t.end()
})
t.test('\n\n# resolve missing from root._default', function (t) {
run(t, { Keyword: { _default: '*:' }, _default: '(:-' }, '*var- a = *new- Test();')
run(t, { Keyword: { _default: ':-' }, _default: '*:)' }, '*var- a = *new- Test();')
t.end()
})
t.test('\n\n# no resolve if all specified', function (t) {
run(t, { Keyword: { _default: '+:-' }, _default: '*:)' }, '+var- a = +new- Test();')
run(t, { Keyword: { _default: ':-' }, _default: ':)' }, 'var- a = new- Test();')
t.end()
})
t.test('\n\n# resolve specific token no defaults', function (t) {
run(t, { Keyword: { 'var': '*:' } }, '*var a = new Test();')
run(t, { Keyword: { 'var': ':-' } }, 'var- a = new Test();')
t.end()
})
t.test('\n\n# resolve specific token with type defaults', function (t) {
run(t, { Keyword: { 'var': '*:', _default: ':-' } }, '*var- a = new- Test();')
run(t, { Keyword: { 'var': '*:', _default: '(:-' } }, '*var- a = (new- Test();')
run(t, { Keyword: { 'var': ':-', _default: '*:' } }, '*var- a = *new Test();')
run(t, { Keyword: { 'var': ':-', _default: '*:)' } }, '*var- a = *new) Test();')
run(t, { Keyword: { 'var': ':-', 'new': ':&', _default: '*:' } }, '*var- a = *new& Test();')
t.end()
})
t.test(
'\n\n# resolve specific token with root defaults, but no type default - root default not applied to unspecified tokens'
, function (t) {
run(t, { Keyword: { 'var': '*:' }, _default: ':-' }, '*var- a = new Test();')
run(t, { Keyword: { 'var': ':-' }, _default: '*:' }, '*var- a = new Test();')
t.end()
}
)
t.test('\n\n# resolve specific token with type and root defaults', function (t) {
run(t, { Keyword: { 'var': '*:', _default: '+:-' }, _default: ':)' }, '*var- a = +new- Test();')
run(t, { Keyword: { 'var': ':-', _default: '*:+' }, _default: '(:' }, '*var- a = *new+ Test();')
t.end()
})
t.test('all exact tokens undefined, but type default', function (t) {
run(t, { 'Boolean': { 'true': undefined, 'false': undefined, _default: '+:-' } }, 'return +true- || +false-;', 'return true || false;')
t.end()
})
t.end()
})
+77
View File
@@ -0,0 +1,77 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('adding custom asserts ... ', function (t) {
t.constructor.prototype.assertSurrounds = function (code, opts, expected) {
var optsi = inspect(opts);
var result = redeyed(code, opts).code
this.equals( result
, expected
, util.format('%s: %s => %s', optsi, inspect(code), inspect(expected))
)
return this;
}
t.end()
})
test('types', function (t) {
t.test('\n# Boolean', function (t) {
t.assertSurrounds('return true;', { 'Boolean': { _default: '$:%' } }, 'return $true%;')
t.assertSurrounds( 'return true; return false;'
, { 'Boolean': { 'false': '#:', _default: '$:%' } }
, 'return $true%; return #false%;')
t.end()
})
t.test('\n# Identifier', function (t) {
t.assertSurrounds('var a = 1;', { 'Identifier': { _default: '$:%' } }, 'var $a% = 1;')
t.assertSurrounds( 'var a = 1; const b = 2;'
, { 'Identifier': { 'b': '#:', _default: '$:%' } }
, 'var $a% = 1; const #b% = 2;')
t.end()
})
t.test('\n# Null', function (t) {
t.assertSurrounds('return null;', { 'Null': { _default: '$:%' } }, 'return $null%;').end()
})
t.test('\n# Numeric', function (t) {
t.assertSurrounds('return 1;', { 'Numeric': { _default: '$:%' } }, 'return $1%;')
t.assertSurrounds( 'return 1; return 2;'
, { 'Numeric': { '2': '#:', _default: '$:%' } }
, 'return $1%; return #2%;')
t.end()
})
t.test('\n# Punctuator', function (t) {
var punctuator = { 'Punctuator': { _default: '$:%' } };
t.assertSurrounds('return 2 * 2;', punctuator, 'return 2 $*% 2$;%')
t.assertSurrounds( 'return 2 * 2;'
, { 'Punctuator': {'*': '#:', _default: '$:%' } }
, 'return 2 #*% 2$;%')
t.assertSurrounds('var {op, lhs, rhs} = getASTNode()', punctuator, 'var ${%op$,% lhs$,% rhs$}% $=% getASTNode$(%$)%')
t.assertSurrounds('function f(x, y=12) { return x + y;}', punctuator, 'function f$(%x$,% y$=%12$)% ${% return x $+% y$;%$}%')
t.assertSurrounds('function f(x, ...y) { return x * y.length;}', punctuator, 'function f$(%x$,% $...%y$)% ${% return x $*% y$.%length$;%$}%')
t.end()
})
t.test('\n# String', function (t) {
t.assertSurrounds('return "hello";', { 'String': { _default: '$:%' } }, 'return $"hello"%;')
t.assertSurrounds( 'return "hello"; return "world";'
, { 'String': { '"world"': '#:', _default: '$:%' } }
, 'return $"hello"%; return #"world"%;')
t.end()
})
t.end()
})
+46
View File
@@ -0,0 +1,46 @@
'use strict';
/*jshint asi: true*/
var test = require('tap').test
, util = require('util')
, redeyed = require('..')
function inspect (obj) {
return util.inspect(obj, false, 5, true)
}
test('adding custom asserts ... ', function (t) {
t.constructor.prototype.assertSurrounds = function (code, opts, expected) {
var optsi = inspect(opts);
var result = redeyed(code, opts).code
this.equals( result
, expected
, util.format('%s: %s => %s', optsi, inspect(code), inspect(expected))
)
return this;
}
t.end()
})
test('upcoming syntax: rest and spread properties', function (t) {
t.test('\n# Punctuator', function (t) {
var punctuator = { 'Punctuator': { _default: '$:%' } };
t.assertSurrounds('{a,...b} = c', punctuator, '${%a$,%$...%b$}% $=% c')
t.assertSurrounds('x={y,...z}', punctuator, 'x$=%${%y$,%$...%z$}%')
t.assertSurrounds('x ** y', punctuator, 'x $**% y');
t.assertSurrounds('x **= y', punctuator, 'x $**=% y');
t.end()
})
t.test('\n# Identifier', function (t) {
var identifier = { 'Identifier': { _default: '$:%' } };
t.assertSurrounds('{a,...b} = c', identifier, '{$a%,...$b%} = $c%')
t.assertSurrounds('x={y,...z}', identifier, '$x%={$y%,...$z%}')
t.end()
})
t.end()
})