├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── config-es5.js
├── config.js
├── examples
├── browser
│ ├── index.css
│ ├── index.html
│ ├── index.js
│ └── sample-config.js
├── replace-log.js
└── sources
│ └── log.js
├── package.json
├── redeyed.js
└── test
├── redeyed-before-after-config.js
├── redeyed-comments.js
├── redeyed-config-with-undefineds.js
├── redeyed-function-config-extra-params.js
├── redeyed-function-config-skipping-tokens.js
├── redeyed-function-config.js
├── redeyed-incomplete.js
├── redeyed-jsx.js
├── redeyed-keywords.js
├── redeyed-mixed.js
├── redeyed-result.js
├── redeyed-script-level-return.js
├── redeyed-shebang.js
├── redeyed-smoke.js
├── redeyed-string-config.js
├── redeyed-types.js
└── redeyed-upcoming.js
/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 |
10 | pids
11 | logs
12 | results
13 |
14 | node_modules
15 | npm-debug.log
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - "6"
5 | - "8"
6 | - "10"
7 | before_install:
8 | - npm install -g npm@2
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2012 Thorsten Lorenz.
2 | All rights reserved.
3 |
4 | Permission is hereby granted, free of charge, to any person
5 | obtaining a copy of this software and associated documentation
6 | files (the "Software"), to deal in the Software without
7 | restriction, including without limitation the rights to use,
8 | copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the
10 | Software is furnished to do so, subject to the following
11 | conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | OTHER DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # redeyed [](http://travis-ci.org/thlorenz/redeyed)
2 |
3 |
4 |
5 | *Add color to your JavaScript!*
6 |
7 | 
8 |
9 | [Red Eyed Tree Frog](http://allaboutfrogs.org/info/species/redeye.html) *(Agalychnis callidryas)*
10 |
11 | ## What?
12 |
13 | Takes JavaScript code, along with a config and returns the original code with tokens wrapped and/or replaced as configured.
14 |
15 | ## Where?
16 |
17 | - server side using nodejs
18 | - in the [browser](#browser-support)
19 |
20 | ## What for?
21 |
22 | One usecase is adding metadata to your code that can then be used to apply syntax highlighting.
23 |
24 | ## How?
25 |
26 | - copy the [config.js](https://github.com/thlorenz/redeyed/blob/master/config.js) and edit it in order to specify how
27 | certain tokens are to be surrounded/replaced
28 | - replace the `undefined` of each token you want to configure with one of the following
29 |
30 | ### {String} config
31 |
32 | `'before:after'`
33 |
34 | wraps the token inside before/after
35 |
36 | ### {Object} config
37 |
38 | `{ _before: 'before', _after: 'after' }`
39 |
40 | wraps token inside before/after
41 |
42 | #### Missing before and after resolution for {String} and {Object} config
43 |
44 | For the `{String}` and `{Object}` configurations, 'before' or 'after' may be omitted:
45 |
46 | - `{String}`:
47 | - `'before:'` (omitting 'after')
48 | - `':after'` (omitting 'before')
49 | - `{Object}`:
50 | - `{ _before: 'before' }` (omitting '_after')
51 | - `{ _after: 'after' }` (omitting '_before')
52 |
53 | In these cases the missing half is resolved as follows:
54 |
55 | - from the `parent._default` (i.e., `Keyword._default`) if found
56 | - otherwise from the `config._default` if found
57 | - otherwise `''` (empty string)
58 |
59 | ### {Function} config
60 |
61 | `function (tokenString, info) { return {String}|{Object}; }`
62 |
63 | #### Inputs
64 |
65 | - tokenString: the content of the token that is currently being processed
66 | - info: an object with the following structure
67 |
68 | ```js
69 | {
70 | // {Int}
71 | // the index of the token being processed inside tokens
72 | tokenIndex
73 |
74 | // {Array}
75 | // all tokens that are being processed including comments
76 | // (i.e. the result of merging esprima tokens and comments)
77 | , tokens
78 |
79 | // {Object}
80 | // the abstract syntax tree of the parsed code
81 | , ast
82 |
83 | // {String}
84 | // the code that was parsed (same string as the one passed to redeyed(code ..)
85 | , code
86 | }
87 | ```
88 |
89 | In most cases the `tokenString` is all you need. The extra info object is passed in case you need to gather more
90 | information about the `token`'s surroundings in order to decide how to transform it.
91 | See: [replace-log-example](https://github.com/thlorenz/redeyed/blob/master/examples/replace-log.js)
92 |
93 | #### Output
94 |
95 | You can return a {String} or an {Object} from a {Function} config.
96 |
97 | - when returning a {String}, the token value will be replaced with it
98 | - when returning an {Object}, it should be of the following form:
99 |
100 | ```js
101 | {
102 | // {String}
103 | // the string that should be substituted for the value of the current and all skipped tokens
104 | replacement
105 |
106 | // {Object} (Token)
107 | // the token after which processing should continue
108 | // all tokens in between the current one and this one inclusive will be ignored
109 | , skipPastToken
110 | }
111 | ```
112 |
113 | ### Transforming JavaScript code
114 |
115 | ***redeyed(code, config[, opts])***
116 |
117 | Invoke redeyed with your **config**uration, a **code** snippet and maybe **opts** as in the below example:
118 |
119 | ```javascript
120 | var redeyed = require('redeyed')
121 | , config = require('./path/to/config')
122 | , code = 'var a = 3;'
123 | , result;
124 |
125 | // redeyed will throw an error (caused by the esprima parser) if the code has invalid javascript
126 | try {
127 | result = redeyed(code, config);
128 | console.log(result.code);
129 | } catch(err) {
130 | console.error(err);
131 | }
132 | ```
133 |
134 | ***opts***:
135 | ```js
136 | { // {Boolean}
137 | // if true `result.ast` property contains the abstract syntax tree of the code
138 | // if false (default) `result.ast` is not assigned and therefore `undefined`
139 | buildAst: true|false
140 | // {Boolean}
141 | // if `true`, jsx syntax is supported, default `false`
142 | // due to how esprima works, the AST is built when this option is `true`, even if
143 | // `buildAST` is `false`
144 | , jsx: true|false
145 | // {Boolean}
146 | // if true `result.code` is not assigned and therefore `undefined`
147 | // if false (default) `result.code` property contains the result of `split.join`
148 | nojoin: true|false
149 | // {Object}
150 | // overrides default parser `esprima-fb` and needs to be compatible with it
151 | parser: require('esprima')
152 | }
153 | ```
154 |
155 | ***return value***:
156 |
157 | ```js
158 | { ast
159 | , tokens
160 | , comments
161 | , splits
162 | , code
163 | }
164 | ```
165 |
166 | - ast `{Array}`: [abstract syntax tree](http://en.wikipedia.org/wiki/Abstract_syntax_tree) as returned by [esprima
167 | parse](http://en.wikipedia.org/wiki/Abstract_syntax_tree)
168 | - tokens `{Array}`: [tokens](http://en.wikipedia.org/wiki/Token_(parser)) provided by esprima (excluding
169 | comments)
170 | - comments `{Array}`: block and line comments as provided by esprima
171 | - splits `{Array}`: code pieces split up, some of which where transformed as configured
172 | - code `{String}`: transformed code, same as `splits.join('')` unless this step has been skipped (see opts)
173 |
174 | ## Browser Support
175 |
176 | ### AMD
177 |
178 | Ensure to include [esprima](https://github.com/ariya/esprima) as one of your dependencies
179 |
180 | ```js
181 | define(['redeyed'], function (redeyed) {
182 | [ .. ]
183 | });
184 | ```
185 |
186 | ### Attached to global window object
187 |
188 | The `redeyed {Function}` will be exposed globally as `window.redeyed` - big surprise!
189 |
190 | ```html
191 |
192 |
193 | ```
194 |
195 | ## redeyed in the wild
196 |
197 | - [cardinal](https://github.com/thlorenz/cardinal): Syntax highlights JavaScript code with ANSI colors to be printed to
198 | the terminal
199 | - [peacock](http://thlorenz.github.com/peacock/): JavaScript syntax highlighter that generates html that is compatible
200 | with pygments styles.
201 |
202 | ## Examples
203 |
204 | - `npm explore redeyed; npm demo` will let you try the [browser example](https://github.com/thlorenz/redeyed/tree/master/examples/browser)
205 | - `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)
206 |
207 |
--------------------------------------------------------------------------------
/config-es5.js:
--------------------------------------------------------------------------------
1 | /*
2 | * This config only contains ES5 tokens while the default config.js contains
3 | * ES6 tokens as well.
4 | *
5 | * Copy this file and use it as a starting point for your redeyed config.
6 | * Just fill in the tokens you want to surround/replace.
7 | * Keep in mind that more specific configurations override less specific ones.
8 | */
9 |
10 | module.exports = {
11 |
12 | 'Boolean': {
13 | 'true' : undefined
14 | , 'false' : undefined
15 | , _default : undefined
16 | }
17 |
18 | , 'Identifier': {
19 | _default: undefined
20 | }
21 |
22 | , 'Null': {
23 | _default: undefined
24 | }
25 |
26 | , 'Numeric': {
27 | _default: undefined
28 | }
29 |
30 | , 'String': {
31 | _default: undefined
32 | }
33 |
34 | , 'Keyword': {
35 | 'break' : undefined
36 |
37 | , 'case' : undefined
38 | , 'catch' : undefined
39 | , 'continue' : undefined
40 |
41 | , 'debugger' : undefined
42 | , 'default' : undefined
43 | , 'delete' : undefined
44 | , 'do' : undefined
45 |
46 | , 'else' : undefined
47 |
48 | , 'finally' : undefined
49 | , 'for' : undefined
50 | , 'function' : undefined
51 |
52 | , 'if' : undefined
53 | , 'in' : undefined
54 | , 'instanceof' : undefined
55 |
56 | , 'new' : undefined
57 | , 'return' : undefined
58 | , 'switch' : undefined
59 |
60 | , 'this' : undefined
61 | , 'throw' : undefined
62 | , 'try' : undefined
63 | , 'typeof' : undefined
64 |
65 | , 'var' : undefined
66 | , 'void' : undefined
67 |
68 | , 'while' : undefined
69 | , 'with' : undefined
70 | , _default : undefined
71 | }
72 | , 'Punctuator': {
73 | ';': undefined
74 | , '.': undefined
75 | , ',': undefined
76 |
77 | , '{': undefined
78 | , '}': undefined
79 | , '(': undefined
80 | , ')': undefined
81 | , '[': undefined
82 | , ']': undefined
83 |
84 | , '<': undefined
85 | , '>': undefined
86 | , '+': undefined
87 | , '-': undefined
88 | , '*': undefined
89 | , '%': undefined
90 | , '&': undefined
91 | , '|': undefined
92 | , '^': undefined
93 | , '!': undefined
94 | , '~': undefined
95 | , '?': undefined
96 | , ':': undefined
97 | , '=': undefined
98 |
99 | , '<=': undefined
100 | , '>=': undefined
101 | , '==': undefined
102 | , '!=': undefined
103 | , '++': undefined
104 | , '--': undefined
105 | , '<<': undefined
106 | , '>>': undefined
107 | , '&&': undefined
108 | , '||': undefined
109 | , '+=': undefined
110 | , '-=': undefined
111 | , '*=': undefined
112 | , '%=': undefined
113 | , '&=': undefined
114 | , '|=': undefined
115 | , '^=': undefined
116 | , '/=': undefined
117 |
118 | , '===': undefined
119 | , '!==': undefined
120 | , '>>>': undefined
121 | , '<<=': undefined
122 | , '>>=': undefined
123 |
124 | , '>>>=': undefined
125 |
126 | , _default: undefined
127 | }
128 |
129 | // line comment
130 | , Line: {
131 | _default: undefined
132 | }
133 |
134 | /* block comment */
135 | , Block: {
136 | _default: undefined
137 | }
138 |
139 | , _default: undefined
140 | }
141 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copy this file and use it as a starting point for your redeyed config.
3 | * Just fill in the tokens you want to surround/replace.
4 | * Keep in mind that more specific configurations override less specific ones.
5 | */
6 |
7 | module.exports = {
8 |
9 | 'Boolean': {
10 | 'true' : undefined
11 | , 'false' : undefined
12 | , _default : undefined
13 | }
14 |
15 | , 'Identifier': {
16 | _default: undefined
17 | }
18 |
19 | , 'Null': {
20 | _default: undefined
21 | }
22 |
23 | , 'Numeric': {
24 | _default: undefined
25 | }
26 |
27 | , 'String': {
28 | _default: undefined
29 | }
30 |
31 | , 'Keyword': {
32 | 'break' : undefined
33 |
34 | , 'case' : undefined
35 | , 'catch' : undefined
36 | , 'class' : undefined
37 | , 'const' : undefined
38 | , 'continue' : undefined
39 |
40 | , 'debugger' : undefined
41 | , 'default' : undefined
42 | , 'delete' : undefined
43 | , 'do' : undefined
44 |
45 | , 'else' : undefined
46 | , 'enum' : undefined
47 | , 'export' : undefined
48 | , 'extends' : undefined
49 |
50 | , 'finally' : undefined
51 | , 'for' : undefined
52 | , 'function' : undefined
53 |
54 | , 'if' : undefined
55 | , 'implements' : undefined
56 | , 'import' : undefined
57 | , 'in' : undefined
58 | , 'instanceof' : undefined
59 | , 'interface' : undefined
60 | , 'let' : undefined
61 | , 'new' : undefined
62 |
63 | , 'package' : undefined
64 | , 'private' : undefined
65 | , 'protected' : undefined
66 | , 'public' : undefined
67 |
68 | , 'return' : undefined
69 | , 'static' : undefined
70 | , 'super' : undefined
71 | , 'switch' : undefined
72 |
73 | , 'this' : undefined
74 | , 'throw' : undefined
75 | , 'try' : undefined
76 | , 'typeof' : undefined
77 |
78 | , 'var' : undefined
79 | , 'void' : undefined
80 |
81 | , 'while' : undefined
82 | , 'with' : undefined
83 | , 'yield' : undefined
84 | , _default : undefined
85 | }
86 | , 'Punctuator': {
87 | ';': undefined
88 | , '.': undefined
89 | , ',': undefined
90 |
91 | , '{': undefined
92 | , '}': undefined
93 | , '(': undefined
94 | , ')': undefined
95 | , '[': undefined
96 | , ']': undefined
97 |
98 | , '<': undefined
99 | , '>': undefined
100 | , '+': undefined
101 | , '-': undefined
102 | , '*': undefined
103 | , '%': undefined
104 | , '&': undefined
105 | , '|': undefined
106 | , '^': undefined
107 | , '!': undefined
108 | , '~': undefined
109 | , '?': undefined
110 | , ':': undefined
111 | , '=': undefined
112 |
113 | , '<=': undefined
114 | , '>=': undefined
115 | , '==': undefined
116 | , '!=': undefined
117 | , '++': undefined
118 | , '--': undefined
119 | , '<<': undefined
120 | , '>>': undefined
121 | , '&&': undefined
122 | , '||': undefined
123 | , '+=': undefined
124 | , '-=': undefined
125 | , '*=': undefined
126 | , '%=': undefined
127 | , '&=': undefined
128 | , '|=': undefined
129 | , '^=': undefined
130 | , '/=': undefined
131 | , '=>': undefined
132 | , '**': undefined
133 |
134 | , '===': undefined
135 | , '!==': undefined
136 | , '>>>': undefined
137 | , '<<=': undefined
138 | , '>>=': undefined
139 | , '...': undefined
140 | , '**=': undefined
141 |
142 | , '>>>=': undefined
143 |
144 | , _default: undefined
145 | }
146 |
147 | // line comment
148 | , Line: {
149 | _default: undefined
150 | }
151 |
152 | /* block comment */
153 | , Block: {
154 | _default: undefined
155 | }
156 |
157 | // JSX
158 | , JSXAttribute: {
159 | _default: undefined
160 | }
161 | , JSXClosingElement: {
162 | _default: undefined
163 | }
164 | , JSXElement: {
165 | _default: undefined
166 | }
167 | , JSXEmptyExpression: {
168 | _default: undefined
169 | }
170 | , JSXExpressionContainer: {
171 | _default: undefined
172 | }
173 | , JSXIdentifier: {
174 | // many more identifies are possible, div, table, etc.
175 | className: undefined
176 | , _default: undefined
177 | }
178 | , JSXMemberExpression: {
179 | _default: undefined
180 | }
181 | , JSXNamespacedName: {
182 | _default: undefined
183 | }
184 | , JSXOpeningElement: {
185 | _default: undefined
186 | }
187 | , JSXSpreadAttribute: {
188 | _default: undefined
189 | }
190 | , JSXText: {
191 | _default: undefined
192 | }
193 |
194 | , _default: undefined
195 | }
196 |
--------------------------------------------------------------------------------
/examples/browser/index.css:
--------------------------------------------------------------------------------
1 | .code {
2 | width: 500px;
3 | height: 500px;
4 | display: block;
5 | }
6 |
7 | .config {
8 | width: 500px;
9 | height: 400px;
10 | display: block;
11 | }
12 |
13 | .go {
14 | display: block;
15 | float: right;
16 | font-size: 18px;
17 | }
18 |
19 | .result {
20 | width: 500px;
21 | height: 1000px;
22 | display: block;
23 | }
24 |
25 | .edit {
26 | float: left;
27 | }
28 |
29 | .results {
30 | float: left;
31 | }
32 |
--------------------------------------------------------------------------------
/examples/browser/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Change the config and/or the original code in order to affect the "redeyed" result
11 |
12 |
13 |
14 | Redeyed Config
15 |
16 |
17 |
18 |
19 | Original Code
20 |
21 |
22 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/examples/browser/index.js:
--------------------------------------------------------------------------------
1 | /* global $ redeyed */
2 | var $code = $('.code')
3 | var $config = $('.config')
4 | var $result = $('.result')
5 |
6 | function go() {
7 | var config
8 | try {
9 | config = JSON.parse($config.val())
10 | } catch (e) {
11 | $result.val('In "Redeyed Config": ' + e.toString())
12 | return
13 | }
14 |
15 | try {
16 | var code = $code.val()
17 | var result = redeyed(code, config)
18 |
19 | $result.val(result.code)
20 | } catch (e) {
21 | $result.val('In "Original Code": ' + e.toString())
22 | }
23 | }
24 |
25 | $code.val(window.redeyed.toString())
26 |
27 | $config.val(JSON.stringify(window.sampleConfig, false, 2))
28 |
29 | $('.go').click(go)
30 |
31 | go()
32 |
33 |
--------------------------------------------------------------------------------
/examples/browser/sample-config.js:
--------------------------------------------------------------------------------
1 | window.sampleConfig = {
2 |
3 | 'Boolean': {
4 | 'true' : undefined
5 | , 'false' : undefined
6 | , _default : '?:?'
7 | }
8 |
9 | , 'Identifier': {
10 | _default: '-> : <-'
11 | }
12 |
13 | , 'Null': {
14 | _default: '**:**'
15 | }
16 |
17 | , 'Numeric': {
18 | _default: 'n:N'
19 | }
20 |
21 | , 'String': {
22 | _default: 'string -> :'
23 | }
24 |
25 | , 'Keyword': {
26 | 'break' : undefined
27 |
28 | , 'case' : undefined
29 | , 'catch' : undefined
30 | , 'continue' : undefined
31 |
32 | , 'debugger' : undefined
33 | , 'default' : undefined
34 | , 'delete' : undefined
35 | , 'do' : undefined
36 |
37 | , 'else' : undefined
38 |
39 | , 'finally' : undefined
40 | , 'for' : undefined
41 | , 'function' : undefined
42 |
43 | , 'if' : undefined
44 | , 'in' : undefined
45 | , 'instanceof' : undefined
46 |
47 | , 'new' : undefined
48 | , 'return' : undefined
49 | , 'switch' : undefined
50 |
51 | , 'this' : undefined
52 | , 'throw' : undefined
53 | , 'try' : undefined
54 | , 'typeof' : undefined
55 |
56 | , 'var' : undefined
57 | , 'void' : undefined
58 |
59 | , 'while' : undefined
60 | , 'with' : undefined
61 | , _default : ': <- keyword'
62 | }
63 | , 'Punctuator': {
64 | ';': undefined
65 | , '.': undefined
66 | , ',': undefined
67 |
68 | , '{': undefined
69 | , '}': undefined
70 | , '(': undefined
71 | , ')': undefined
72 | , '[': undefined
73 | , ']': undefined
74 |
75 | , '<': undefined
76 | , '>': undefined
77 | , '+': undefined
78 | , '-': undefined
79 | , '*': undefined
80 | , '%': undefined
81 | , '&': undefined
82 | , '|': undefined
83 | , '^': undefined
84 | , '!': undefined
85 | , '~': undefined
86 | , '?': undefined
87 | , ':': undefined
88 | , '=': undefined
89 |
90 | , '<=': undefined
91 | , '>=': undefined
92 | , '==': undefined
93 | , '!=': undefined
94 | , '++': undefined
95 | , '--': undefined
96 | , '<<': undefined
97 | , '>>': undefined
98 | , '&&': undefined
99 | , '||': undefined
100 | , '+=': undefined
101 | , '-=': undefined
102 | , '*=': undefined
103 | , '%=': undefined
104 | , '&=': undefined
105 | , '|=': undefined
106 | , '^=': undefined
107 | , '/=': undefined
108 |
109 | , '===': undefined
110 | , '!==': undefined
111 | , '>>>': undefined
112 | , '<<=': undefined
113 | , '>>=': undefined
114 |
115 | , '>>>=': undefined
116 |
117 | , _default: undefined
118 | }
119 |
120 | // line comment
121 | , Line: {
122 | _default: undefined
123 | }
124 |
125 | /* block comment */
126 | , Block: {
127 | _default: undefined
128 | }
129 |
130 | , _default: undefined
131 | }
132 |
--------------------------------------------------------------------------------
/examples/replace-log.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var fs = require('fs')
3 | var redeyed = require('..')
4 | var vm = require('vm')
5 |
6 | var samplePath = path.join(__dirname, 'sources', 'log.js')
7 | var origCode = fs.readFileSync(samplePath, 'utf-8')
8 | var kinds = [ 'silly', 'info', 'warn', 'error' ]
9 |
10 | function replaceConsole(s, info) {
11 | var code = info.code
12 | var idx = info.tokenIndex
13 | var tokens = info.tokens
14 | var next = tokens[idx + 1].value
15 | var kind = tokens[idx + 2].value
16 | var openParen = tokens[idx + 3].value
17 | var firstArgTkn = tokens[idx + 4]
18 | var argIdx = idx + 3
19 | var open
20 | var tkn
21 |
22 | if (kind === 'log') kind = 'silly'
23 |
24 | // not a console.xxx(...) statement? -> just return original
25 | if (next !== '.' || !~kinds.indexOf(kind) || openParen !== '(') return s
26 |
27 | // skip past arguments to console.xxx all args from ( to )
28 | open = 1
29 | while (open) {
30 | tkn = tokens[++argIdx]
31 |
32 | // count open parens vs. closed ones to handle things like console.log(new Error('..'));
33 | if (tkn.value === '(') open++
34 | if (tkn.value === ')') open--
35 | }
36 |
37 | // tkn now is the last closing paren
38 | var argsIncludingClosingParen = code.slice(firstArgTkn.range[0], tkn.range[1])
39 | var result = 'log.' + kind + '("main-logger", ' + argsIncludingClosingParen
40 |
41 | // tell redeyed to skip the entire console.xxx(..) statement since we are replacing it all
42 | return { replacement: result, skipPastToken: tkn }
43 | }
44 |
45 | function transformAndRun() {
46 | var config = {
47 | Identifier: { console: replaceConsole }
48 | }
49 | var code = redeyed(origCode, config).code
50 | var context = vm.createContext({ require: require })
51 |
52 | console.log('Original code:\n', origCode)
53 | console.log('\nlog calls replaced:\n', code)
54 | console.log('\nLets run it:')
55 | vm.runInContext(code, context, 'transformed-log.vm')
56 | }
57 |
58 | transformAndRun()
59 |
--------------------------------------------------------------------------------
/examples/sources/log.js:
--------------------------------------------------------------------------------
1 | // First two lines will be needed when we replaced all console.xxx statements with log.xxx
2 | var log = require('npmlog')
3 | log.level = 'silly'
4 |
5 | console.info('info ', 1)
6 | console.log('log ', 2)
7 | console.warn('warn ', 3)
8 | console.error('error ', new Error('oh my!'))
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redeyed",
3 | "version": "2.1.1",
4 | "description": "Takes JavaScript code, along with a config and returns the original code with tokens wrapped as configured.",
5 | "author": "Thorsten Lorenz (thlorenz.com)",
6 | "main": "redeyed.js",
7 | "scripts": {
8 | "test": "npm run run-test && npm run lint",
9 | "run-test": "tape test/*.js",
10 | "lint": "standart",
11 | "demo-log": "node examples/replace-log",
12 | "demo": "cd examples/browser; open index.html"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git://github.com/thlorenz/redeyed.git"
17 | },
18 | "files": [
19 | "redeyed.js"
20 | ],
21 | "keywords": [
22 | "ast",
23 | "syntax",
24 | "tree",
25 | "source",
26 | "wrap",
27 | "metadata"
28 | ],
29 | "license": "MIT",
30 | "devDependencies": {
31 | "cardinal": "~1.0.0",
32 | "readdirp": "~2.1.0",
33 | "standart": "^6.1.0",
34 | "tape": "~4.9.0"
35 | },
36 | "dependencies": {
37 | "esprima": "~4.0.0"
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/redeyed.js:
--------------------------------------------------------------------------------
1 | ;(function() {
2 | 'use strict'
3 | /* global define */
4 |
5 | var esprima
6 | var exportFn
7 | var toString = Object.prototype.toString
8 |
9 | if (typeof module === 'object' && typeof module.exports === 'object' && typeof require === 'function') {
10 | // server side
11 | esprima = require('esprima')
12 | exportFn = function(redeyed) { module.exports = redeyed }
13 | bootstrap(esprima, exportFn)
14 | } else if (typeof define === 'function' && define.amd) {
15 | // client side
16 | // amd
17 | define(['esprima'], function(esprima) {
18 | return bootstrap(esprima)
19 | })
20 | } else if (typeof window === 'object') {
21 | // no amd -> attach to window if it exists
22 | // Note that this requires 'esprima' to be defined on the window, so that script has to be loaded first
23 | window.redeyed = bootstrap(window.esprima)
24 | }
25 |
26 | function bootstrap(esprima, exportFn) {
27 | function isFunction(obj) {
28 | return toString.call(obj) === '[object Function]'
29 | }
30 |
31 | function isString(obj) {
32 | return toString.call(obj) === '[object String]'
33 | }
34 |
35 | function isObject(obj) {
36 | return toString.call(obj) === '[object Object]'
37 | }
38 |
39 | function surroundWith(before, after) {
40 | return function(s) { return before + s + after }
41 | }
42 |
43 | function isNonCircular(key) {
44 | return key !== '_parent'
45 | }
46 |
47 | function objectizeString(value) {
48 | var vals = value.split(':')
49 |
50 | if (vals.length === 0 || vals.length > 2) {
51 | throw new Error(
52 | 'illegal string config: ' + value +
53 | '\nShould be of format "before:after"'
54 | )
55 | }
56 |
57 | if (vals.length === 1 || vals[1].length === 0) {
58 | return vals.indexOf(':') < 0 ? { _before: vals[0] } : { _after: vals[0] }
59 | } else {
60 | return { _before: vals[0], _after: vals[1] }
61 | }
62 | }
63 |
64 | function objectize(node) {
65 | // Converts 'bef:aft' to { _before: bef, _after: aft }
66 | // and resolves undefined before/after from parent or root
67 |
68 | function resolve(value, key) {
69 | // resolve before/after from root or parent if it isn't present on the current node
70 | if (!value._parent) return undefined
71 |
72 | // Immediate parent
73 | if (value._parent._default && value._parent._default[key]) return value._parent._default[key]
74 |
75 | // Root
76 | var root = value._parent._parent
77 | if (!root) return undefined
78 |
79 | return root._default ? root._default[key] : undefined
80 | }
81 |
82 | function process(key) {
83 | var value = node[key]
84 |
85 | if (!value) return
86 | if (isFunction(value)) return
87 |
88 | // normalize all strings to objects
89 | if (isString(value)) {
90 | node[key] = value = objectizeString(value)
91 | }
92 |
93 | value._parent = node
94 | if (isObject(value)) {
95 | if (!value._before && !value._after) return objectize(value)
96 |
97 | // resolve missing _before or _after from parent(s)
98 | // in case we only have either one on this node
99 | value._before = value._before || resolve(value, '_before')
100 | value._after = value._after || resolve(value, '_after')
101 |
102 | return
103 | }
104 |
105 | throw new Error('nodes need to be either {String}, {Object} or {Function}.' + value + ' is neither.')
106 | }
107 |
108 | // Process _default ones first so children can resolve missing before/after from them
109 | if (node._default) process('_default')
110 |
111 | Object.keys(node)
112 | .filter(function(key) {
113 | return isNonCircular(key)
114 | && node.hasOwnProperty(key)
115 | && key !== '_before'
116 | && key !== '_after'
117 | && key !== '_default'
118 | })
119 | .forEach(process)
120 | }
121 |
122 | function functionize(node) {
123 | Object.keys(node)
124 | .filter(function(key) {
125 | return isNonCircular(key) && node.hasOwnProperty(key)
126 | })
127 | .forEach(function(key) {
128 | var value = node[key]
129 |
130 | if (isFunction(value)) return
131 |
132 | if (isObject(value)) {
133 | if (!value._before && !value._after) return functionize(value)
134 |
135 | // at this point before/after were "inherited" from the parent or root
136 | // (see objectize)
137 | var before = value._before || ''
138 | var after = value._after || ''
139 |
140 | node[key] = surroundWith(before, after)
141 | return node[key]
142 | }
143 | })
144 | }
145 |
146 | function normalize(root) {
147 | objectize(root)
148 | functionize(root)
149 | }
150 |
151 | function mergeTokensAndComments(tokens, comments) {
152 | var all = {}
153 |
154 | function addToAllByRangeStart(t) { all[ t.range[0] ] = t }
155 |
156 | tokens.forEach(addToAllByRangeStart)
157 | comments.forEach(addToAllByRangeStart)
158 |
159 | // keys are sorted automatically
160 | return Object.keys(all)
161 | .map(function(k) { return all[k] })
162 | }
163 |
164 | function redeyed(code, config, opts) {
165 | opts = opts || {}
166 | var parser = opts.parser || esprima
167 | var jsx = !!opts.jsx
168 | // tokenizer doesn't support JSX at this point (esprima@4.0.0)
169 | // therefore we need to generate the AST via the parser not only to
170 | // avoid the tokenizer from erroring but also to get JSXIdentifier tokens
171 | var buildAst = jsx || !!opts.buildAst
172 |
173 | var hashbang = ''
174 | var ast
175 | var tokens
176 | var comments
177 | var lastSplitEnd = 0
178 | var splits = []
179 | var transformedCode
180 | var all
181 | var info
182 |
183 | // Replace hashbang line with empty whitespaces to preserve token locations
184 | if (code[0] === '#' && code[1] === '!') {
185 | hashbang = code.substr(0, code.indexOf('\n') + 1)
186 | code = Array.apply(0, Array(hashbang.length)).join(' ') + '\n' + code.substr(hashbang.length)
187 | }
188 |
189 | if (buildAst) {
190 | ast = parser.parse(code, { tokens: true, comment: true, range: true, loc: true, tolerant: true, jsx: true })
191 | tokens = ast.tokens
192 | comments = ast.comments
193 | } else {
194 | tokens = []
195 | comments = []
196 | parser.tokenize(code, { range: true, loc: true, comment: true }, function(token) {
197 | if (token.type === 'LineComment') {
198 | token.type = 'Line'
199 | comments.push(token)
200 | } else if (token.type === 'BlockComment') {
201 | token.type = 'Block'
202 | comments.push(token)
203 | } else {
204 | // Optimistically upgrade 'static' to a keyword
205 | if (token.type === 'Identifier' && token.value === 'static') token.type = 'Keyword'
206 | tokens.push(token)
207 | }
208 | })
209 | }
210 | normalize(config)
211 |
212 | function tokenIndex(tokens, tkn, start) {
213 | var current
214 | var rangeStart = tkn.range[0]
215 |
216 | for (current = start; current < tokens.length; current++) {
217 | if (tokens[current].range[0] === rangeStart) return current
218 | }
219 |
220 | throw new Error('Token %s not found at or after index: %d', tkn, start)
221 | }
222 |
223 | function process(surround) {
224 | var result
225 | var currentIndex
226 | var nextIndex
227 | var skip = 0
228 | var splitEnd
229 |
230 | result = surround(code.slice(start, end), info)
231 | if (isObject(result)) {
232 | splits.push(result.replacement)
233 |
234 | currentIndex = info.tokenIndex
235 | nextIndex = tokenIndex(info.tokens, result.skipPastToken, currentIndex)
236 | skip = nextIndex - currentIndex
237 | splitEnd = skip > 0 ? tokens[nextIndex - 1].range[1] : end
238 | } else {
239 | splits.push(result)
240 | splitEnd = end
241 | }
242 |
243 | return { skip: skip, splitEnd: splitEnd }
244 | }
245 |
246 | function addSplit(start, end, surround, info) {
247 | var result
248 | var skip = 0
249 |
250 | if (start >= end) return
251 | if (surround) {
252 | result = process(surround)
253 | skip = result.skip
254 | lastSplitEnd = result.splitEnd
255 | } else {
256 | splits.push(code.slice(start, end))
257 | lastSplitEnd = end
258 | }
259 |
260 | return skip
261 | }
262 |
263 | all = mergeTokensAndComments(tokens, comments)
264 | for (var tokenIdx = 0; tokenIdx < all.length; tokenIdx++) {
265 | var token = all[tokenIdx]
266 | var surroundForType = config[token.type]
267 | var surround
268 | var start
269 | var end
270 |
271 | // At least the type (e.g., 'Keyword') needs to be specified for the token to be surrounded
272 | if (surroundForType) {
273 | // root defaults are only taken into account while resolving before/after otherwise
274 | // a root default would apply to everything, even if no type default was specified
275 | surround = surroundForType
276 | && surroundForType.hasOwnProperty(token.value)
277 | && surroundForType[token.value]
278 | && isFunction(surroundForType[token.value])
279 | ? surroundForType[token.value]
280 | : surroundForType._default
281 |
282 | start = token.range[0]
283 | end = token.range[1]
284 |
285 | addSplit(lastSplitEnd, start)
286 | info = { tokenIndex: tokenIdx, tokens: all, ast: ast, code: code }
287 | tokenIdx += addSplit(start, end, surround, info)
288 | }
289 | }
290 |
291 | if (lastSplitEnd < code.length) {
292 | addSplit(lastSplitEnd, code.length)
293 | }
294 |
295 | if (!opts.nojoin) {
296 | transformedCode = splits.join('')
297 | if (hashbang.length > 0) {
298 | transformedCode = hashbang + transformedCode.substr(hashbang.length)
299 | }
300 | }
301 |
302 | return {
303 | ast : ast
304 | , tokens : tokens
305 | , comments : comments
306 | , splits : splits
307 | , code : transformedCode
308 | }
309 | }
310 |
311 | return exportFn ? exportFn(redeyed) : redeyed
312 | }
313 | })()
314 |
--------------------------------------------------------------------------------
/test/redeyed-before-after-config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('adding custom asserts ... ', function(t) {
12 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
13 | var result = redeyed(code, opts).code
14 | this.equals(result, expected, inspect(code) + ' => ' + inspect(expected))
15 | return this
16 | }
17 |
18 | t.end()
19 | })
20 | test('\nbefore/after config, keywords', function(t) {
21 | var opts001 = { Keyword: { _default: { _before: '*', _after: '&' } } }
22 | t.test('\n# ' + inspect(opts001), function(t) {
23 | t.assertSurrounds('this', opts001, '*this&')
24 | t.assertSurrounds('if (a == 1) return', opts001, '*if& (a == 1) *return&')
25 | t.assertSurrounds('var n = new Test();', opts001, '*var& n = *new& Test();')
26 | t.end()
27 | })
28 |
29 | var opts002 = {
30 | Keyword: {
31 | 'function': { _before: '^' }
32 | , 'return': { _before: '(', _after: ')' }
33 | , _default: { _before: '*', _after: '&' }
34 | }
35 | }
36 |
37 | t.test('\n# ' + inspect(opts002), function(t) {
38 | t.assertSurrounds(
39 | [ 'function foo (bar) {'
40 | , ' var a = 3;'
41 | , ' return bar + a;'
42 | , '}'
43 | ].join('\n')
44 | , opts002
45 | , [ '^function& foo (bar) {'
46 | , ' *var& a = 3;'
47 | , ' (return) bar + a;'
48 | , '}'
49 | ].join('\n'))
50 | t.end()
51 | })
52 |
53 | t.end()
54 | })
55 |
--------------------------------------------------------------------------------
/test/redeyed-comments.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('adding custom asserts ... ', function(t) {
12 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
13 | var result = redeyed(code, opts)
14 | this.equals(result.code, expected, inspect(code) + ' => ' + inspect(expected))
15 | return this
16 | }
17 |
18 | t.end()
19 | })
20 |
21 | test('\nstring config, Line comments', function(t) {
22 | var opts = { Line: { _default: '*:&' } }
23 | t.test('\n# ' + inspect(opts), function(t) {
24 | t.assertSurrounds(
25 | '// a comment'
26 | , opts
27 | , '*// a comment&'
28 | )
29 | t.assertSurrounds(
30 | '// comment then new line\nif (a == 1) return'
31 | , opts
32 | , '*// comment then new line&\nif (a == 1) return'
33 | )
34 | t.assertSurrounds(
35 | 'var n = new Test();// some comment after\n//more comment\nvar s = 3;'
36 | , opts
37 | , 'var n = new Test();*// some comment after&\n*//more comment&\nvar s = 3;'
38 | )
39 | t.end()
40 | })
41 |
42 | t.end()
43 | })
44 |
45 | test('\nstring config, Block comments', function(t) {
46 | var opts = { Block: { _default: '_:-' } }
47 | t.test('\n# ' + inspect(opts), function(t) {
48 | t.assertSurrounds(
49 | '/* a comment */'
50 | , opts
51 | , '_/* a comment */-'
52 | )
53 | t.assertSurrounds(
54 | '/* comment then new line*/\nif (a == 1) /* inline */ return'
55 | , opts
56 | , '_/* comment then new line*/-\nif (a == 1) _/* inline */- return'
57 | )
58 | t.assertSurrounds(
59 | 'var n = new Test();/* some comment after*/\n/*more comment*/\nvar s = 3;'
60 | , opts
61 | , 'var n = new Test();_/* some comment after*/-\n_/*more comment*/-\nvar s = 3;'
62 | )
63 | t.assertSurrounds(
64 | 'var a = 4;\n/* Multi line comment\n * Next line\n * and another\n*/ var morecode = "here";'
65 | , opts
66 | , 'var a = 4;\n_/* Multi line comment\n * Next line\n * and another\n*/- var morecode = "here";'
67 | )
68 | t.end()
69 | })
70 |
71 | t.end()
72 | })
73 |
--------------------------------------------------------------------------------
/test/redeyed-config-with-undefineds.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('adding custom asserts ... ', function(t) {
12 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
13 | var optsi = inspect(opts)
14 | var result = redeyed(code, opts).code
15 |
16 | this.equals(result
17 | , expected
18 | , util.format('%s: %s => %s', optsi, inspect(code), inspect(expected))
19 | )
20 | return this
21 | }
22 |
23 | t.end()
24 | })
25 |
26 | test('\n undefineds only', function(t) {
27 | t.assertSurrounds('1 + 2', { Numeric: { _default: undefined } }, '1 + 2')
28 | t.assertSurrounds('1 + 2', { Numeric: { _default: undefined }, _default: undefined }, '1 + 2')
29 |
30 | t.assertSurrounds(
31 | 'return true'
32 | , { 'Boolean': { 'true': undefined, 'false': undefined, _default: undefined }, _default: undefined }
33 | , 'return true'
34 | )
35 |
36 | t.end()
37 | })
38 |
39 | test('\n mixed', function(t) {
40 | t.assertSurrounds(
41 | 'return true || false'
42 | , { 'Boolean': { 'true': '&:', 'false': undefined, _default: undefined }, _default: undefined }
43 | , 'return &true || false'
44 | )
45 |
46 | t.assertSurrounds(
47 | 'return true || false'
48 | , { 'Boolean': { 'true': '&:', 'false': undefined, _default: ':?' }, _default: undefined }
49 | , 'return &true? || false?'
50 | )
51 |
52 | t.assertSurrounds(
53 | 'return true || false'
54 | , { 'Boolean': { 'true': '&:', 'false': undefined, _default: undefined }, _default: ':?' }
55 | , 'return &true? || false'
56 | )
57 |
58 | t.end()
59 | })
60 |
--------------------------------------------------------------------------------
/test/redeyed-function-config-extra-params.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('function - config passing idx and tokens', function(t) {
12 | var args = []
13 | var opts001 = {
14 | Boolean: {
15 | _default: identity
16 | }
17 | , Keyword: {
18 | _default: identity
19 | }
20 | , Identifier: {
21 | _default: identity
22 | }
23 | , Punctuator: {
24 | _default: identity
25 | }
26 | }
27 | var code = 'var fn = function () { return true; }'
28 |
29 | function identity(s, info) {
30 | args.push({ value: s, idx: info.tokenIndex, tokens: info.tokens, code: info.code })
31 | // returning unchanged string will keep the splits be equal to the original tokens
32 | return s
33 | }
34 |
35 | t.test(inspect(opts001) + ' -- ' + code, function(t) {
36 | var result = redeyed(code, opts001, { splits: true })
37 | var tokens = result.tokens
38 |
39 | t.equals(args.length, tokens.length, 'called with all tokens')
40 |
41 | for (var i = 0; i < tokens.length; i++) {
42 | var token = tokens[i]
43 | var arg = args[i]
44 |
45 | t.equals(arg.value, token.value, 'passes correct value: ' + inspect([ arg.value, token.value ]))
46 | t.equals(arg.idx, i, 'passes correct index')
47 | t.equals(arg.code, code, 'passes code')
48 | t.deepEquals(arg.tokens, tokens, 'passes all tokens')
49 | }
50 | t.end()
51 | })
52 | t.end()
53 | })
54 |
--------------------------------------------------------------------------------
/test/redeyed-function-config-skipping-tokens.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var redeyed = require('..')
5 |
6 | test('given i skip 2 more tokens after each semicolon', function(t) {
7 | var calls = 0
8 | var opts = {
9 | Punctuator: {
10 | ';': function identity(s, info) {
11 | // tell it to skip past second to last token that is 2 ahead of the current one
12 | calls++
13 | var skipToken = info.tokens[info.tokenIndex + 2]
14 | return skipToken ? { replacement: s, skipPastToken: skipToken } : s
15 | }
16 | }
17 | }
18 |
19 | ;[ { code: ';;;', expectedCalls: 1 }
20 | , { code: ';;;;', expectedCalls: 2 }
21 | , { code: '; ; ; ;', expectedCalls: 2 }
22 | , { code: ';;; ;;; ;;; ;', expectedCalls: 4 }
23 | , { code: ';;; ;;; ;;; ;;; ;', expectedCalls: 5 }
24 | , { code: ';;; ;;; ;;; ;;; ;;;', expectedCalls: 5 }
25 | , { code: ';;; ;;; ;;; ;;; ;;; ;', expectedCalls: 6 }
26 | ].forEach(function(x) {
27 | calls = 0
28 | redeyed(x.code, opts)
29 | t.equals(calls, x.expectedCalls, 'calls ' + x.expectedCalls + ' times for ' + x.code)
30 | })
31 | t.end()
32 | })
33 |
34 | test('replace log', function(t) {
35 | var opts = {
36 | Identifier: {
37 | console: function replaceLog(s, info) {
38 | var code = info.code
39 | var idx = info.tokenIndex
40 | var tokens = info.tokens
41 | var kind = tokens[idx + 2].value
42 | var firstArgTkn = tokens[idx + 4]
43 | var argIdx = idx + 3
44 | var open
45 | var tkn
46 |
47 | open = 1
48 | while (open) {
49 | tkn = tokens[++argIdx]
50 |
51 | if (tkn.value === '(') open++
52 | if (tkn.value === ')') open--
53 | }
54 |
55 | var argsIncludingClosingParen = code.slice(firstArgTkn.range[0], tkn.range[1])
56 | var result = 'log.' + kind + '("main-logger", ' + argsIncludingClosingParen
57 |
58 | return { replacement: result, skipPastToken: tkn }
59 | }
60 | }
61 | }
62 |
63 | var origCode = [
64 | 'console.info("info ", 1);'
65 | , 'console.warn("warn ", 3);'
66 | , 'console.error("error ", new Error("oh my!"));'
67 | ].join('\n')
68 |
69 | var expectedCode = [
70 | 'log.info("main-logger", "info ", 1));'
71 | , 'log.warn("main-logger", "warn ", 3));'
72 | , 'log.error("main-logger", "error ", new Error("oh my!")));'
73 | ].join('\n')
74 | var code = redeyed(origCode, opts).code
75 |
76 | t.equals(code, expectedCode, 'transforms all log statements')
77 | t.end()
78 | })
79 |
--------------------------------------------------------------------------------
/test/redeyed-function-config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('adding custom asserts ... ', function(t) {
12 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
13 | var result = redeyed(code, opts).code
14 | this.equals(result, expected, inspect(code) + ' => ' + inspect(expected))
15 | return this
16 | }
17 |
18 | t.end()
19 | })
20 |
21 | test('\nfunction config, keywords', function(t) {
22 | var opts001 = { Keyword: { _default: function(s) { return '*' + s + '&' } } }
23 | t.test('\n# ' + inspect(opts001), function(t) {
24 | t.assertSurrounds('this', opts001, '*this&')
25 |
26 | t.assertSurrounds('this ', opts001, '*this& ')
27 | t.assertSurrounds(' this', opts001, ' *this&')
28 | t.assertSurrounds(' this ', opts001, ' *this& ')
29 | t.assertSurrounds('if (a == 1) return', opts001, '*if& (a == 1) *return&')
30 | t.assertSurrounds('var n = new Test();', opts001, '*var& n = *new& Test();')
31 | t.assertSurrounds(
32 | [ 'function foo (bar) {'
33 | , ' var a = 3;'
34 | , ' return bar + a;'
35 | , '}'
36 | ].join('\n')
37 | , opts001
38 | , [ '*function& foo (bar) {'
39 | , ' *var& a = 3;'
40 | , ' *return& bar + a;'
41 | , '}'
42 | ].join('\n'))
43 | t.end()
44 | })
45 |
46 | var opts002 = {
47 | Keyword: {
48 | 'function': function(s) { return '^' + s + '&' }
49 | , 'return': function(s) { return '(' + s + ')' }
50 | , _default: function(s) { return '*' + s + '&' }
51 | }
52 | }
53 |
54 | t.test('\n# ' + inspect(opts002), function(t) {
55 | t.assertSurrounds(
56 | [ 'function foo (bar) {'
57 | , ' var a = 3;'
58 | , ' return bar + a;'
59 | , '}'
60 | ].join('\n')
61 | , opts002
62 | , [ '^function& foo (bar) {'
63 | , ' *var& a = 3;'
64 | , ' (return) bar + a;'
65 | , '}'
66 | ].join('\n'))
67 | t.end()
68 | })
69 |
70 | t.end()
71 | })
72 |
73 | test('#\n functin config - resolving', function(t) {
74 | var opts001 = {
75 | Keyword: {
76 | 'var': function(s) { return '^' + s + '&' }
77 | }
78 | , _default: function(s) { return '*' + s + '&' }
79 | }
80 | t.test('\n# specific but no type default and root default - root default not applied' + inspect(opts001), function(t) {
81 | t.assertSurrounds('var n = new Test();', opts001, '^var& n = new Test();').end()
82 | })
83 |
84 | var opts002 = {
85 | Keyword: {
86 | 'var': function(s) { return '^' + s + '&' }
87 | , _default: function(s) { return '*' + s + '&' }
88 | }
89 | , _default: function(s) { return '(' + s + ')' }
90 | }
91 | t.test('\n# no type default but root default' + inspect(opts002), function(t) {
92 | t.assertSurrounds('var n = new Test();', opts002, '^var& n = *new& Test();').end()
93 | })
94 |
95 | t.end()
96 | })
97 |
98 | test('#\n function config - replacing', function(t) {
99 | var opts001 = {
100 | Keyword: {
101 | 'var': function() { return 'const' }
102 | }
103 | }
104 | t.test('\n# type default and root default (type wins)' + inspect(opts001), function(t) {
105 | t.assertSurrounds('var n = new Test();', opts001, 'const n = new Test();').end()
106 | })
107 |
108 | var opts002 = {
109 | Keyword: {
110 | _default: function() { return 'const' }
111 | }
112 | }
113 | t.test('\n# type default' + inspect(opts002), function(t) {
114 | t.assertSurrounds('var n = new Test();', opts002, 'const n = const Test();').end()
115 | })
116 |
117 | var opts003 = {
118 | Keyword: {
119 | 'new': function() { return 'NEW' }
120 | , _default: function() { return 'const' }
121 | }
122 | }
123 | t.test('\n# specific and type default' + inspect(opts003), function(t) {
124 | t.assertSurrounds('var n = new Test();', opts003, 'const n = NEW Test();').end()
125 | })
126 |
127 | var opts004 = {
128 | Keyword: {
129 | _default: function(s) { return s.toUpperCase() }
130 | }
131 | , _default: function(s) { return 'not applied' }
132 | }
133 | t.test('\n# type default and root default (type wins)' + inspect(opts004), function(t) {
134 | t.assertSurrounds('var n = new Test();', opts004, 'VAR n = NEW Test();').end()
135 | })
136 |
137 | var opts005 = {
138 | Keyword: { }
139 | , _default: function(s) { return s.toUpperCase() }
140 | }
141 | t.test('\n# no type default only root default - not applied' + inspect(opts005), function(t) {
142 | t.assertSurrounds('var n = new Test();', opts005, 'var n = new Test();').end()
143 | })
144 |
145 | t.end()
146 | })
147 |
--------------------------------------------------------------------------------
/test/redeyed-incomplete.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('adding custom asserts ... ', function(t) {
12 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
13 | var optsi = inspect(opts)
14 | var result = redeyed(code, opts).code
15 |
16 | this.equals(result
17 | , expected
18 | , util.format('%s: %s => %s', optsi, inspect(code), inspect(expected))
19 | )
20 | return this
21 | }
22 |
23 | t.end()
24 | })
25 |
26 | test('incomplete statement', function(t) {
27 | t.test('\n# Keyword', function(t) {
28 | var keyconfig = { 'Keyword': { _default: '$:%' } }
29 | t.assertSurrounds('if(foo) { x', keyconfig, '$if%(foo) { x')
30 | t.assertSurrounds('class Foo { constructor(name)', keyconfig, '$class% Foo { constructor(name)')
31 | t.assertSurrounds('const x = ', keyconfig, '$const% x = ')
32 | t.assertSurrounds('function g() { yield', keyconfig, '$function% g() { $yield%')
33 | t.end()
34 | })
35 |
36 | t.end()
37 | })
38 |
--------------------------------------------------------------------------------
/test/redeyed-jsx.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /* eslint-disable no-template-curly-in-string */
4 |
5 | var test = require('tape')
6 | var util = require('util')
7 | var redeyed = require('..')
8 |
9 | function inspect(obj) {
10 | return util.inspect(obj, false, 5, true)
11 | }
12 |
13 | test('adding custom asserts ... ', function(t) {
14 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
15 | var result = redeyed(code, opts, { jsx: true }).code
16 | if (expected == null) console.log(result)
17 | else this.equals(result, expected, inspect(code) + ' => ' + inspect(expected))
18 | return this
19 | }
20 |
21 | t.end()
22 | })
23 |
24 | test('\njsx support', function(t) {
25 | var config = {
26 | 'JSXIdentifier': {
27 | className: '$xc:cx%'
28 | , _default: '$x:x%'
29 | }
30 | , 'Punctuator': { _default: '$:%'
31 | }
32 | }
33 | t.assertSurrounds(
34 | ''
35 | , config
36 | , '$<%$xComponentx% $xstartx%$=%${%1$}% $xcclassNamecx%$=%"hello" $/%$>%'
37 | )
38 | t.end()
39 | })
40 |
--------------------------------------------------------------------------------
/test/redeyed-keywords.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('adding custom asserts ... ', function(t) {
12 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
13 | var optsi = inspect(opts)
14 | var result = redeyed(code, opts).code
15 |
16 | this.equals(result
17 | , expected
18 | , util.format('%s: %s => %s', optsi, inspect(code), inspect(expected))
19 | )
20 | return this
21 | }
22 |
23 | t.end()
24 | })
25 |
26 | test('types', function(t) {
27 | t.test('\n# Keyword', function(t) {
28 | var keyconfig = { 'Keyword': { _default: '$:%' } }
29 | t.assertSurrounds('import foo from \'foo\';', keyconfig, '$import% foo from \'foo\';')
30 | t.assertSurrounds('export default foo;', keyconfig, '$export% $default% foo;')
31 | t.assertSurrounds('if(foo) { let bar = 1;}', keyconfig, '$if%(foo) { $let% bar = 1;}')
32 | t.assertSurrounds('const x = "foo";', keyconfig, '$const% x = "foo";')
33 | t.assertSurrounds('"use strict";(function* () { yield *v })', keyconfig, '"use strict";($function%* () { $yield% *v })')
34 | t.assertSurrounds('"use strict"; (class A { static constructor() { super() }})', keyconfig
35 | , '"use strict"; ($class% A { $static% constructor() { $super%() }})')
36 | t.assertSurrounds('class Foo { constructor(name){this.name = name;}}', keyconfig
37 | , '$class% Foo { constructor(name){$this%.name = name;}}')
38 | t.assertSurrounds('class Foo extends Bar { constructor(name,value){super(value);this.name = name;}}', keyconfig
39 | , '$class% Foo $extends% Bar { constructor(name,value){$super%(value);$this%.name = name;}}')
40 | t.end()
41 | })
42 |
43 | t.end()
44 | })
45 |
--------------------------------------------------------------------------------
/test/redeyed-mixed.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('adding custom asserts ... ', function(t) {
12 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
13 | var result = redeyed(code, opts).code
14 | this.equals(result, expected, inspect(code) + ' => ' + inspect(expected))
15 | return this
16 | }
17 |
18 | t.end()
19 | })
20 |
21 | test('\nmixed config, keywords', function(t) {
22 | var opts001 = {
23 | Keyword: {
24 | 'this': function(s) { return '_' + s }
25 | , 'if': { _before: '^' }
26 | , _default: '*:&'
27 | }
28 | }
29 | t.test('\n# ' + inspect(opts001), function(t) {
30 | t.assertSurrounds('if (this.hello) return "world";', opts001, '^if& (_this.hello) *return& "world";').end()
31 | })
32 |
33 | var opts002 = {
34 | Keyword: {
35 | 'this': function(s) { return '_' + s }
36 | , 'if': { _before: '^' }
37 | , 'return': ':)'
38 | , _default: ':&'
39 | }
40 | , _default: '*:&'
41 | }
42 | t.test('\n# ' + inspect(opts002), function(t) {
43 | t.assertSurrounds('if (this.hello) return "world";', opts002, '^if& (_this.hello) *return) "world";').end()
44 | })
45 |
46 | t.end()
47 | })
48 |
--------------------------------------------------------------------------------
/test/redeyed-result.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var redeyed = require('..')
5 | var esprima = require('esprima')
6 |
7 | test('redeyed result does not have esprima ast by default', function(t) {
8 | var code = '// a comment\nvar a = 3;'
9 | var conf = { Keyword: { _default: '_:-' } }
10 |
11 | var ast = esprima.parse(code, { tokens: true, comment: true, range: true, loc: true, tolerant: true })
12 | var tokens = ast.tokens
13 | var comments = ast.comments
14 |
15 | var result = redeyed(code, conf)
16 |
17 | t.equal(typeof result.ast, 'undefined', 'ast')
18 | t.deepEquals(result.tokens, tokens, 'tokens')
19 | t.deepEquals(result.comments, comments, 'comments')
20 | t.notEquals(result.code, undefined, 'code')
21 | t.end()
22 | })
23 |
24 | test('redeyed result has esprima ast, tokens, comments and splits and transformed code', function(t) {
25 | var code = '// a comment\nvar a = 3;'
26 | var conf = { Keyword: { _default: '_:-' } }
27 | var ast = esprima.parse(code, { tokens: true, comment: true, range: true, loc: true, tolerant: true })
28 | var tokens = ast.tokens
29 | var comments = ast.comments
30 |
31 | var result = redeyed(code, conf, { buildAst: true })
32 |
33 | t.deepEquals(result.ast, ast, 'ast')
34 | t.deepEquals(result.tokens, tokens, 'tokens')
35 | t.deepEquals(result.comments, comments, 'comments')
36 | t.notEquals(result.code, undefined, 'code')
37 |
38 | t.end()
39 | })
40 |
41 | test('redeyed result - { nojoin } has esprima ast, tokens, comments and splits but no transformed code', function(t) {
42 | var code = '// a comment\nvar a = 3;'
43 | var conf = { Keyword: { _default: '_:-' } }
44 | var ast = esprima.parse(code, { tokens: true, comment: true, range: true, loc: true, tolerant: true })
45 | var tokens = ast.tokens
46 | var comments = ast.comments
47 |
48 | var result = redeyed(code, conf, { nojoin: true, buildAst: true })
49 |
50 | t.deepEquals(result.ast, ast, 'ast')
51 | t.deepEquals(result.tokens, tokens, 'tokens')
52 | t.deepEquals(result.comments, comments, 'comments')
53 | t.equals(result.code, undefined, 'code')
54 |
55 | t.end()
56 | })
57 |
--------------------------------------------------------------------------------
/test/redeyed-script-level-return.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('properly handles script level return -- no blow up', function(t) {
12 | var code = [
13 | ''
14 | , 'return 1;'
15 | ].join('\n')
16 | var opts = { Keyword: { 'return': '%:^' } }
17 | var expected = '\n%return^ 1;'
18 | var res = redeyed(code, opts).code
19 |
20 | t.equals(res, expected, inspect(code) + ' opts: ' + inspect(opts) + ' => ' + inspect(expected))
21 | t.end()
22 | })
23 |
--------------------------------------------------------------------------------
/test/redeyed-shebang.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('preserves shebang', function(t) {
12 | var code = [
13 | '#!/usr/bin/env node'
14 | , 'var util = require("util");'
15 | ].join('\n')
16 | var opts = { Keyword: { 'var': '%:^' } }
17 | var expected = [
18 | '#!/usr/bin/env node'
19 | , '%var^ util = require("util");'
20 | ].join('\n')
21 | var res = redeyed(code, opts).code
22 |
23 | t.equals(res, expected, inspect(code) + ' opts: ' + inspect(opts) + ' => ' + inspect(expected))
24 | t.end()
25 | })
26 |
--------------------------------------------------------------------------------
/test/redeyed-smoke.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | // applying redeyed to a bunch of files of contained libraries as a smoke test
4 | var test = require('tape')
5 | var path = require('path')
6 | var fs = require('fs')
7 | var readdirp = require('readdirp')
8 | var redeyed = require('..')
9 | var nodeModules = path.join(__dirname, '..', 'node_modules')
10 | var esprimadir = path.join(nodeModules, 'esprima')
11 |
12 | test('esprima', function(t) {
13 | readdirp({ root: esprimadir, fileFilter: '*.js' })
14 | .on('data', function(entry) {
15 | var code = fs.readFileSync(entry.fullPath, 'utf-8')
16 | var resultAst = redeyed(code, { Keyword: { 'var': '+:-' } }, { buildAst: true }).code
17 | var resultTokenize = redeyed(code, { Keyword: { 'var': '+:-' } }, { buildAst: false }).code
18 |
19 | t.assert(~resultAst.indexOf('+var-') || !(~resultAst.indexOf('var ')), 'redeyed ' + entry.path)
20 | t.assert(~resultTokenize.indexOf('+var-') || !(~resultTokenize.indexOf('var ')), 'redeyed ' + entry.path)
21 | })
22 | .on('end', t.end.bind(t))
23 | })
24 |
25 | test('redeyed', function(t) {
26 | readdirp({
27 | root: path.join(__dirname, '..')
28 | , fileFilter: '*.js'
29 | , directoryFilter: [ '!.git', '!node_modules' ]
30 | })
31 | .on('data', function(entry) {
32 | var code = fs.readFileSync(entry.fullPath, 'utf-8')
33 | var result = redeyed(code, { Keyword: { 'var': '+:-' } }).code
34 |
35 | t.assert(~result.indexOf('+var-') || !(~result.indexOf('var ')), 'redeyed ' + entry.path)
36 | })
37 | .on('end', t.end.bind(t))
38 | })
39 |
--------------------------------------------------------------------------------
/test/redeyed-string-config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | var test = require('tape')
4 | var util = require('util')
5 | var redeyed = require('..')
6 |
7 | function inspect(obj) {
8 | return util.inspect(obj, false, 5, true)
9 | }
10 |
11 | test('adding custom asserts ... ', function(t) {
12 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
13 | var result = redeyed(code, opts).code
14 | this.equals(result, expected, inspect(code) + ' => ' + inspect(expected))
15 | return this
16 | }
17 |
18 | t.end()
19 | })
20 |
21 | test('\nstring config, keywords', function(t) {
22 | var opts001 = { Keyword: { _default: '*:&' } }
23 | t.test('\n# ' + inspect(opts001), function(t) {
24 | t.assertSurrounds('this', opts001, '*this&')
25 | t.assertSurrounds('if (a == 1) return', opts001, '*if& (a == 1) *return&')
26 | t.assertSurrounds('var n = new Test();', opts001, '*var& n = *new& Test();')
27 | t.end()
28 | })
29 |
30 | var opts002 = {
31 | Keyword: {
32 | 'function': '^:'
33 | , 'return': '(:)'
34 | , _default: '*:&'
35 | }
36 | }
37 |
38 | t.test('\n# ' + inspect(opts002), function(t) {
39 | t.assertSurrounds(
40 | [ 'function foo (bar) {'
41 | , ' var a = 3;'
42 | , ' return bar + a;'
43 | , '}'
44 | ].join('\n')
45 | , opts002
46 | , [ '^function& foo (bar) {'
47 | , ' *var& a = 3;'
48 | , ' (return) bar + a;'
49 | , '}'
50 | ].join('\n'))
51 | t.end()
52 | })
53 |
54 | t.end()
55 | })
56 |
57 | test('\nstring configs resolve from type and root', function(t) {
58 | var code = 'var a = new Test();'
59 |
60 | function run(t, conf, expected, code_) {
61 | t.test('\n# ' + inspect(conf), function(t) {
62 | t.assertSurrounds(code_ || code, conf, expected)
63 | t.end()
64 | })
65 | }
66 |
67 | // at least the token kind has to be configured in order for the root_default to be applied
68 | // otherwise a root._default would affect all tokens, even the ones we want to leave unchanged
69 | run(t, { _default: '*:' }, 'var a = new Test();')
70 |
71 | t.test('\n\n# only before or after specified, but no root._default', function(t) {
72 | run(t, { Keyword: { _default: '*:' } }, '*var a = *new Test();')
73 | run(t, { Keyword: { _default: ':-' } }, 'var- a = new- Test();')
74 | t.end()
75 | })
76 |
77 | t.test('\n\n# resolve missing from root._default', function(t) {
78 | run(t, { Keyword: { _default: '*:' }, _default: '(:-' }, '*var- a = *new- Test();')
79 | run(t, { Keyword: { _default: ':-' }, _default: '*:)' }, '*var- a = *new- Test();')
80 | t.end()
81 | })
82 |
83 | t.test('\n\n# no resolve if all specified', function(t) {
84 | run(t, { Keyword: { _default: '+:-' }, _default: '*:)' }, '+var- a = +new- Test();')
85 | run(t, { Keyword: { _default: ':-' }, _default: ':)' }, 'var- a = new- Test();')
86 | t.end()
87 | })
88 |
89 | t.test('\n\n# resolve specific token no defaults', function(t) {
90 | run(t, { Keyword: { 'var': '*:' } }, '*var a = new Test();')
91 | run(t, { Keyword: { 'var': ':-' } }, 'var- a = new Test();')
92 | t.end()
93 | })
94 |
95 | t.test('\n\n# resolve specific token with type defaults', function(t) {
96 | run(t, { Keyword: { 'var': '*:', _default: ':-' } }, '*var- a = new- Test();')
97 | run(t, { Keyword: { 'var': '*:', _default: '(:-' } }, '*var- a = (new- Test();')
98 | run(t, { Keyword: { 'var': ':-', _default: '*:' } }, '*var- a = *new Test();')
99 | run(t, { Keyword: { 'var': ':-', _default: '*:)' } }, '*var- a = *new) Test();')
100 | run(t, { Keyword: { 'var': ':-', 'new': ':&', _default: '*:' } }, '*var- a = *new& Test();')
101 | t.end()
102 | })
103 |
104 | t.test(
105 | '\n\n# resolve specific token with root defaults, but no type default - root default not applied to unspecified tokens'
106 | , function(t) {
107 | run(t, { Keyword: { 'var': '*:' }, _default: ':-' }, '*var- a = new Test();')
108 | run(t, { Keyword: { 'var': ':-' }, _default: '*:' }, '*var- a = new Test();')
109 | t.end()
110 | }
111 | )
112 |
113 | t.test('\n\n# resolve specific token with type and root defaults', function(t) {
114 | run(t, { Keyword: { 'var': '*:', _default: '+:-' }, _default: ':)' }, '*var- a = +new- Test();')
115 | run(t, { Keyword: { 'var': ':-', _default: '*:+' }, _default: '(:' }, '*var- a = *new+ Test();')
116 | t.end()
117 | })
118 |
119 | t.test('all exact tokens undefined, but type default', function(t) {
120 | run(t, { 'Boolean': { 'true': undefined, 'false': undefined, _default: '+:-' } }, 'return +true- || +false-;', 'return true || false;')
121 | t.end()
122 | })
123 |
124 | t.end()
125 | })
126 |
--------------------------------------------------------------------------------
/test/redeyed-types.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | /* eslint-disable no-template-curly-in-string */
4 |
5 | var test = require('tape')
6 | var util = require('util')
7 | var redeyed = require('..')
8 |
9 | function inspect(obj) {
10 | return util.inspect(obj, false, 5, true)
11 | }
12 |
13 | test('adding custom asserts ... ', function(t) {
14 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
15 | var optsi = inspect(opts)
16 | var result = redeyed(code, opts).code
17 |
18 | this.equals(result
19 | , expected
20 | , util.format('%s: %s => %s', optsi, inspect(code), inspect(expected))
21 | )
22 | return this
23 | }
24 |
25 | t.end()
26 | })
27 |
28 | test('types', function(t) {
29 | t.test('\n# Boolean', function(t) {
30 | t.assertSurrounds('return true;', { 'Boolean': { _default: '$:%' } }, 'return $true%;')
31 | t.assertSurrounds('return true; return false;'
32 | , { 'Boolean': { 'false': '#:', _default: '$:%' } }
33 | , 'return $true%; return #false%;')
34 | t.end()
35 | })
36 |
37 | t.test('\n# Identifier', function(t) {
38 | t.assertSurrounds('var a = 1;', { 'Identifier': { _default: '$:%' } }, 'var $a% = 1;')
39 | t.assertSurrounds('var a = 1; const b = 2;'
40 | , { 'Identifier': { 'b': '#:', _default: '$:%' } }
41 | , 'var $a% = 1; const #b% = 2;')
42 | t.end()
43 | })
44 |
45 | t.test('\n# Null', function(t) {
46 | t.assertSurrounds('return null;', { 'Null': { _default: '$:%' } }, 'return $null%;').end()
47 | })
48 |
49 | t.test('\n# Numeric', function(t) {
50 | t.assertSurrounds('return 1;', { 'Numeric': { _default: '$:%' } }, 'return $1%;')
51 | t.assertSurrounds('return 1; return 2;'
52 | , { 'Numeric': { '2': '#:', _default: '$:%' } }
53 | , 'return $1%; return #2%;')
54 | t.end()
55 | })
56 |
57 | t.test('\n# Punctuator', function(t) {
58 | var punctuator = { 'Punctuator': { _default: '$:%' } }
59 | t.assertSurrounds('return 2 * 2;', punctuator, 'return 2 $*% 2$;%')
60 | t.assertSurrounds('return 2 * 2;'
61 | , { 'Punctuator': { '*': '#:', _default: '$:%' } }
62 | , 'return 2 #*% 2$;%')
63 | t.assertSurrounds('var {op, lhs, rhs} = getASTNode()', punctuator, 'var ${%op$,% lhs$,% rhs$}% $=% getASTNode$(%$)%')
64 | t.assertSurrounds('function f(x, y=12) { return x + y;}', punctuator, 'function f$(%x$,% y$=%12$)% ${% return x $+% y$;%$}%')
65 | t.assertSurrounds('function f(x, ...y) { return x * y.length;}', punctuator, 'function f$(%x$,% $...%y$)% ${% return x $*% y$.%length$;%$}%')
66 | t.end()
67 | })
68 |
69 | t.test('\n# String', function(t) {
70 | t.assertSurrounds('return "hello";', { 'String': { _default: '$:%' } }, 'return $"hello"%;')
71 | t.assertSurrounds('return "hello"; return "world";'
72 | , { 'String': { '"world"': '#:', _default: '$:%' } }
73 | , 'return $"hello"%; return #"world"%;')
74 | t.end()
75 | })
76 |
77 | t.end()
78 | })
79 |
--------------------------------------------------------------------------------
/test/redeyed-upcoming.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | /* eslint-disable no-template-curly-in-string */
3 |
4 | var test = require('tape')
5 | var util = require('util')
6 | var redeyed = require('..')
7 |
8 | function inspect(obj) {
9 | return util.inspect(obj, false, 5, true)
10 | }
11 |
12 | test('adding custom asserts ... ', function(t) {
13 | t.constructor.prototype.assertSurrounds = function(code, opts, expected) {
14 | var optsi = inspect(opts)
15 | var result = redeyed(code, opts).code
16 |
17 | this.equals(result
18 | , expected
19 | , util.format('%s: %s => %s', optsi, inspect(code), inspect(expected))
20 | )
21 | return this
22 | }
23 |
24 | t.end()
25 | })
26 |
27 | test('upcoming syntax: rest and spread properties', function(t) {
28 | t.test('\n# Punctuator', function(t) {
29 | var punctuator = { 'Punctuator': { _default: '$:%' } }
30 | t.assertSurrounds('{a,...b} = c', punctuator, '${%a$,%$...%b$}% $=% c')
31 | t.assertSurrounds('x={y,...z}', punctuator, 'x$=%${%y$,%$...%z$}%')
32 | t.assertSurrounds('x ** y', punctuator, 'x $**% y')
33 | t.assertSurrounds('x **= y', punctuator, 'x $**=% y')
34 | t.end()
35 | })
36 |
37 | t.test('\n# Identifier', function(t) {
38 | var identifier = { 'Identifier': { _default: '$:%' } }
39 | t.assertSurrounds('{a,...b} = c', identifier, '{$a%,...$b%} = $c%')
40 | t.assertSurrounds('x={y,...z}', identifier, '$x%={$y%,...$z%}')
41 | t.end()
42 | })
43 |
44 | t.end()
45 | })
46 |
--------------------------------------------------------------------------------