├── .coveralls.yml
├── .gitignore
├── .npmignore
├── .travis.yml
├── Readme.md
├── _cmd.js
├── cmd.js
├── examples
├── auto
│ ├── fixture.js
│ └── index.js
├── loadable
│ └── index.js
└── transform
│ ├── fixture.js
│ └── index.js
├── index.js
├── lib
└── auto.js
├── package.json
├── semantics.json
└── test
├── api.js
├── behaviors.js
├── fixtures
├── original.js
└── transformed.js
└── transformations.js
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | repo_token: bQAgHTWcHvzl2isStbMa8y653t25aTvWr
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | coverage
2 | coverage/*
3 | *.log
4 | node_modules
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | coverage
2 | coverage/*
3 | *.log
4 | node_modules
5 | *.yml
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "0.11"
4 | - "0.10"
5 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Decofun – Debug tool
2 |
3 | Decofun is a JavaScript function deanonymizer.
4 |
5 | It parses your code and names any anonymous
6 | functions according to their context.
7 |
8 | [](https://travis-ci.org/davidmarkclements/decofun)
9 |
10 | [](https://coveralls.io/r/davidmarkclements/decofun?branch=master)
11 |
12 | Version 1.2.x
13 |
14 | - [install](#install)
15 | - [features](#features)
16 | - [tests](#tests)
17 | - [examples](#examples)
18 |
19 |
20 | ## Install
21 |
22 | ```sh
23 | npm i decofun
24 | ```
25 |
26 |
27 | # Features
28 |
29 | ## Command Line Tool
30 | ```
31 | sudo npm -g i decofun
32 | ```
33 | New from version 1.2.x we can simply use the `deco`
34 | executable in place of `node` to instrument
35 | anonymous functions.
36 |
37 | ```sh
38 | deco examples/loadable
39 | ```
40 |
41 | Since version 1.3.x the `deco` executable can also be
42 | instructed to use the [cute-stack](https://github.com/davidmarkclements/cute-stack) module to prettify stack traces, just add a `--cute` flag
43 |
44 | ```sh
45 | deco examples/loadable --cute
46 | ```
47 |
48 | To set the `cute-stack`'s display type,
49 | pass it as the value of `--cute`
50 |
51 | ```sh
52 | deco examples/loadable --cute table
53 | ```
54 |
55 | To set the stack size pass a number
56 |
57 | ```sh
58 | deco examples/loadable --cute 20
59 | ```
60 |
61 | To set both the display type and number,
62 | pass a JSON array
63 |
64 | ```sh
65 | deco examples/loadable --cute '["table", 20]'
66 | ```
67 |
68 |
69 | ## Automatic Instrumentation
70 | New from version 1.1.x, we can use decofun to automatically
71 | instrument any required modules, simply call the `auto` method,
72 | before requring any other modules
73 |
74 | ```javascript
75 | require('decofun').auto()
76 | var myMod = require('./lib/myModule.js')
77 | var somePub = require('somePublishedMod')
78 | ```
79 |
80 | Both myMod and somePub will now have their anonymous functions named.
81 |
82 | Alternatively, decofun can be called directly as a
83 | function (without supplying a path argument) to
84 | achieve the same result:
85 |
86 | ```javascript
87 | require('decofun')() # same as require('decofun').auto()
88 | var myMod = require('./lib/myModule.js')
89 | var somePub = require('somePublishedMod')
90 | ```
91 |
92 | To undo automatic instrumentation and restore former
93 | behavior, simply use the `restore` method:
94 |
95 | ```javascript
96 | var decofun = require('decofun');
97 | decofun.auto();
98 | var myMod = require('./aModToDebug')
99 | decofun.restore();
100 | var anotherMod = require('./noDebugNeededThx')
101 | ```
102 |
103 | ## Client-side Instrumenting
104 |
105 | For instrumenting browser code, you can use
106 | the [deco-server](https://npmjs.org/package/deco-server) module.
107 |
108 | There is a live `deco-server` running on heroku, for instance
109 | we can deanonymize jQuery by with the following URL:
110 |
111 | [http://decofun.herokuapp.com/?addr=http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.js](http://decofun.herokuapp.com/?addr=http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.js)
112 |
113 | A deco transform is also planned for those who
114 | use browserify - for now we can simply
115 | use a `deco-server` to host our browserified code.
116 |
117 | ## Programmatic Transform
118 |
119 | We can transform any string containing JavaScript with
120 | the `transform` method.
121 |
122 | ```javascript
123 | var decofun = require('decofun')
124 | var fs = require('fs');
125 | var path = require('path')
126 | var fixture = fs.readFileSync(path.join(__dirname, './fixture.js'));
127 |
128 | console.log(decofun.transform(fixture))
129 | ```
130 |
131 | As with the `auto` method, transforms can also be achieved
132 | with the polymorphic function representing the decofun module.
133 | When we pass a string to the decofun function, it executes a
134 | transform (and when we don't pass a string it calls `decofun.auto`):
135 |
136 | ```javascript
137 | console.log(decofun(fixture))
138 |
139 | //same thing:
140 | //console.log(decofun.transform(fixture))
141 |
142 | ```
143 |
144 |
145 |
146 | ## Tests
147 |
148 | Run tests with
149 |
150 | ```
151 | npm test
152 | ```
153 |
154 |
155 |
156 | ## Examples
157 |
158 | The examples folder is a good place to start.
159 |
160 | To see how a direct transform would work,
161 | from the install directory we could run these commands:
162 |
163 | ```sh
164 | cat node_modules/decofun/examples/transform/fixture.js #view the original
165 | node node_modules/decofun/examples/transform # view the result
166 | ```
167 |
168 | To see instrumentation upon `require` (the Auto feature),
169 | run the following:
170 |
171 | ```sh
172 | cat node_modules/decofun/examples/auto/fixture.js #view the original
173 | node node_modules/decofun/examples/auto # view the result
174 | ```
175 |
176 |
177 |
178 |
179 | ### functions assigned to variables
180 | Are labelled "of var | line N".
181 |
182 | ```javascript
183 | var myFn = function () {}
184 | ```
185 |
186 | Transforms into:
187 | ```javascript
188 | var myFn = function asᅠvarᅠmyFnᅠㅣlineᅠ1 () {}
189 | ```
190 |
191 |
192 | ### function parameters
193 | Are labelled "passed into | line N".
194 |
195 | ```javascript
196 | someFunc('blah', function () {})
197 |
198 | ```
199 |
200 | Transforms into:
201 | ```javascript
202 | someFunc('blah', function passedᅠintoᅠsomeFuncᅠㅣlineᅠ1 () {})
203 | ```
204 |
205 |
206 | ### method parameters
207 | Are labelled "passed into ː | line N".
208 |
209 | ```javascript
210 | obj.prop(function () { })
211 | ```
212 |
213 | Transforms into:
214 | ```javascript
215 | obj.prop(function passedᅠintoᅠobjːpropᅠㅣlineᅠ1 () { })
216 | ```
217 |
218 |
219 | ### sub-object method parameters
220 | Are labelled "passed into ː | line N".
221 |
222 | ```javascript
223 | obj.subobj.prop(function () { })
224 | ```
225 |
226 | Transforms into:
227 | ```javascript
228 | obj.subobj.prop(function passedᅠintoᅠsubobjːpropᅠㅣlineᅠ1 () { })
229 | ```
230 |
231 |
232 | ### returned functions
233 | Are labelled "returned from | line N".
234 |
235 | ```javascript
236 | function f() {return function () { }}
237 | ```
238 |
239 | Transforms into:
240 | ```javascript
241 | function f() {return function returnedᅠfromᅠfᅠㅣlineᅠ1 () { }}
242 | ```
243 |
244 |
245 | ### returned functions of returned anonymous functions
246 | Are labelled "returned from ᐸ ᐳ | line N".
247 |
248 | ```javascript
249 | function contain () {
250 | return function () {
251 | return function () {
252 | }
253 | }
254 | }
255 | ```
256 |
257 | Transforms into:
258 | ```javascript
259 | function contain() {
260 | return function returnedᅠfromᅠcontainᅠㅣlineᅠ2 () {
261 | return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠcontainᅠᐳᅠㅣlineᅠ3 () {
262 |
263 | }
264 | }
265 | }
266 |
267 | ```
268 |
269 |
270 | ### methods declared in object literals
271 | Are labelled "as property ㅣ line N".
272 |
273 | ```javascript
274 | function contain () {
275 | return {
276 | propInLiteral: function () {}
277 | }
278 | }
279 | ```
280 |
281 | Transforms into:
282 | ```javascript
283 | function contain() {
284 | return {
285 | propInLiteral: function asᅠpropertyᅠpropInLiteralᅠㅣlineᅠ3 () {}
286 | }
287 | }
288 | ```
289 |
290 |
291 | ### methods assigned to instantiated objects
292 | Are labelled "as property ㅣ line N".
293 |
294 | ```javascript
295 | var o = {}; o.p = function (cb) { }
296 | ```
297 |
298 | Transforms into:
299 | ```javascript
300 | var o = {}; o.p = function asᅠpropertyᅠpᅠㅣlineᅠ1 (cb) { }
301 | ```
302 |
303 |
304 | ### immediately invoked function expressions
305 | Are labelled "IIFEㅣ line N".
306 |
307 | ```javascript
308 | !function() {}()
309 | ;(function(){}())
310 | ```
311 |
312 | Transforms into:
313 |
314 | ```javascript
315 | !function IIFEᅠㅣlineᅠ1() {}()
316 | ;(function IIFEᅠㅣlineᅠ2(){}())
317 | ```
318 |
319 | # Kudos
320 |
321 | Sponsered by [nearForm](http://nearform.com)
322 |
323 |
324 |
--------------------------------------------------------------------------------
/_cmd.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var argv = require('minimist')(process.argv.slice(2))
4 | var path = require('path');
5 | var cute = require('cute-stack');
6 |
7 | if (!argv._.length) {
8 | //tell parent process to simply spawn a REPL
9 | process.exit(209);
10 | return;
11 | }
12 |
13 | function parse() {
14 | try {
15 | argv.cute = JSON.parse(argv.cute)
16 | } catch(e) {
17 | console.log('shit', e);
18 | }
19 | }
20 |
21 | if (argv.cute) {
22 | parse();
23 | console.log(argv.cute)
24 | if (Array.isArray(argv.cute)) {
25 | console.log('passing', argv.cute)
26 | cute.apply(cute, argv.cute);
27 | } else {
28 | cute(argv.cute !== true && argv.cute);
29 | }
30 | }
31 |
32 | require('./')();
33 | require(path.join(process.cwd(), argv._[0]));
--------------------------------------------------------------------------------
/cmd.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | /**
4 | * This tiny wrapper file checks for known node flags
5 | * and appends them when found, before invoking the "real" _cmd
6 | * executable. Inspired by similar mocha -> _mocha interactions
7 | */
8 |
9 | var spawn = require('child_process').spawn
10 | , args = [ __dirname + '/_cmd' ];
11 |
12 | process.argv.slice(2).forEach(function(arg){
13 | var flag = arg.split('=')[0];
14 |
15 | switch (flag) {
16 | case '-d':
17 | args.unshift('--debug');
18 | args.push('--no-timeouts');
19 | break;
20 | case 'debug':
21 | case '--debug':
22 | case '--debug-brk':
23 | args.unshift(arg);
24 | args.push('--no-timeouts');
25 | break;
26 | case '-gc':
27 | case '--expose-gc':
28 | args.unshift('--expose-gc');
29 | break;
30 | case '--gc-global':
31 | case '--harmony':
32 | case '--harmony-proxies':
33 | case '--harmony-collections':
34 | case '--harmony-generators':
35 | case '--no-deprecation':
36 | case '--prof':
37 | case '--basic-perf-prof':
38 | case '--throw-deprecation':
39 | case '--trace-deprecation':
40 | args.unshift(arg);
41 | break;
42 | default:
43 | if (0 == arg.indexOf('--trace')) args.unshift(arg);
44 | else args.push(arg);
45 | break;
46 | }
47 | });
48 | var proc = spawn(process.argv[0], args, { stdio: 'inherit' });
49 | proc.on('exit', function onExit(code, signal) {
50 | if (+code === 209) {
51 | proc = spawn('node', args.slice(1), { stdio: 'inherit' });
52 | proc.on('exit', onExit);
53 | }
54 | process.on('exit', function(){
55 | if (signal) {
56 | process.kill(process.pid, signal);
57 | } else {
58 | process.exit(code);
59 | }
60 | });
61 | });
62 |
63 | // terminate children.
64 | process.on('SIGINT', function () {
65 | proc.kill('SIGINT'); // calls runner.abort()
66 | proc.kill('SIGTERM'); // if that didn't work, we're probably in an infinite loop, so make it die.
67 | process.kill(process.pid, 'SIGINT');
68 | });
69 |
--------------------------------------------------------------------------------
/examples/auto/fixture.js:
--------------------------------------------------------------------------------
1 | module.exports = function () {
2 |
3 | }
4 | module.exports.gravy = function () {
5 | return function () {
6 | return {
7 | prop: function () {
8 | setTimeout(function () {
9 | console.trace('Getting a trace...');
10 | }, 10)
11 |
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/examples/auto/index.js:
--------------------------------------------------------------------------------
1 | require('../..')();
2 |
3 | var fixture = require('./fixture')
4 |
5 | console.log(fixture.gravy+'')
6 |
7 | fixture.gravy()().prop();
8 |
--------------------------------------------------------------------------------
/examples/loadable/index.js:
--------------------------------------------------------------------------------
1 | function gravy () {
2 | return function () {
3 | return {
4 | prop: function () {
5 | setTimeout(function () {
6 | console.trace('Getting a trace...');
7 | }, 10)
8 |
9 | }
10 | }
11 | }
12 | }
13 |
14 | console.log(gravy+'');
15 | gravy()().prop();
--------------------------------------------------------------------------------
/examples/transform/fixture.js:
--------------------------------------------------------------------------------
1 | function one (a, cb) {
2 |
3 | }
4 |
5 |
6 | one('blah', function () {
7 |
8 | })
9 |
10 | function two () {
11 | return function () { }
12 | }
13 |
14 |
15 | function three () {
16 | return {
17 | shoe: function () {}
18 | }
19 | }
20 |
21 | function four () {
22 | return function () {
23 | return function () {
24 |
25 | }
26 | }
27 | }
28 |
29 | function five () {
30 | return function () {
31 | return function () {
32 | return function () {
33 | foo('blue', function () {
34 |
35 | })
36 | }
37 | }
38 | }
39 | }
40 |
41 |
42 | var six = function () {
43 |
44 | }
45 |
46 |
47 | var seven = function (err, cb) {
48 |
49 | return function () {
50 | cb(function () {
51 |
52 | })
53 | }
54 |
55 | }
56 |
57 | var o = {};
58 | o.eight = function (cb) { }
59 |
60 |
61 | o.eight(function () { })
62 |
63 | o.eight.nine = function () {}
64 | o.eight.nine(function () { })
65 |
66 | var o2;
67 |
68 | o2 = function () { }
69 |
70 |
71 | ;(function () {}())
72 |
73 | !function () { }()
74 |
75 |
76 |
77 | function toodeep () {
78 | return function () {
79 | return function () {
80 | return function () {
81 |
82 | return function () {
83 | return function () {
84 | return function () {
85 |
86 | return function () {
87 | return function () {
88 | return function () {
89 |
90 | return function () {
91 |
92 | }
93 |
94 | }
95 | }
96 | }
97 |
98 | }
99 | }
100 | }
101 | }
102 | }
103 | }
104 | }
105 |
106 |
107 |
--------------------------------------------------------------------------------
/examples/transform/index.js:
--------------------------------------------------------------------------------
1 | var decofun = require('../..')
2 | var fs = require('fs');
3 | var path = require('path')
4 | var fixture = fs.readFileSync(path.join(__dirname, './fixture.js'));
5 |
6 | console.log(decofun(fixture))
7 |
8 | //same result as:
9 | //console.log(decofun.transform(fixture))
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var falafel = require('falafel');
2 | var fs = require('fs');
3 | var semantics = require('./semantics.json')
4 | var space = '\uffa0';
5 |
6 | module.exports = decofun;
7 | decofun.auto = require('./lib/auto')(decofun);
8 | decofun.restore = require('./lib/auto').restore;
9 |
10 | Object.keys(semantics).forEach(function (k) {
11 | semantics[k] = semantics[k].replace(/ /g, space);
12 | })
13 |
14 | function nameFunc(fnstr, name) {
15 | return fnstr.replace(/function/, 'function ' + name);
16 | }
17 |
18 | var again;
19 | var output;
20 |
21 | rewrite.count = 0;
22 | rewrite.max = 44;
23 | function rewrite (src) {
24 | if (rewrite.count > rewrite.max) {
25 | again = false;
26 | rewrite.count = 0;
27 | return;
28 | }
29 | output = falafel(src, {loc: true}, function (node) {
30 | try {
31 | if (node.type !== 'FunctionExpression' || node.id) {
32 | return;
33 | }
34 |
35 | var name = '';
36 | var pType = node.parent.type;
37 | if (pType === 'CallExpression') {
38 |
39 | if (node.parent.callee.name) {
40 | name = semantics.argTo + node.parent.callee.name;
41 | } else {
42 |
43 | if (!node.parent.callee.object) {
44 | name = 'IIFE';
45 |
46 | } else {
47 | if (node.parent.callee.object.type === 'Identifier') {
48 | name = semantics.argTo + node.parent.callee.object.name + 'ː' + node.parent.callee.property.name;
49 | }
50 |
51 | if (node.parent.callee.object.type === 'MemberExpression') {
52 | name = semantics.argTo + node.parent.callee.object.property.name + 'ː' + node.parent.callee.property.name;
53 | }
54 |
55 | }
56 |
57 |
58 | }
59 |
60 | }
61 |
62 | if (pType === 'ReturnStatement') {
63 | if (!node.parent.parent.parent.id) {
64 | again = true;
65 | rewrite.count += 1;
66 | return;
67 | }
68 |
69 |
70 |
71 | if (Object.keys(semantics).map(function (k) {
72 | return node.parent.parent.parent.id.name.match(semantics[k]);
73 | }).filter(Boolean).length) {
74 |
75 | name = semantics.returnedFrom + 'ᐸ' + space + node.parent.parent.parent.id.name.split('ᅠㅣ')[0] + space + 'ᐳ'
76 |
77 | } else {
78 | name = semantics.returnedFrom + node.parent.parent.parent.id.name;
79 | }
80 |
81 | }
82 |
83 | if (pType === 'Property') {
84 | name = semantics.onProperty + node.parent.key.name;
85 | }
86 |
87 | if (pType === 'AssignmentExpression') {
88 | if (node.parent.left.property) {
89 | name = semantics.onProperty + node.parent.left.property.name;
90 | } else {
91 | name = semantics.ofVar + node.parent.left.name;
92 | }
93 |
94 | }
95 |
96 | if (pType === 'VariableDeclarator') {
97 | name = semantics.ofVar + node.parent.id.name;
98 | }
99 |
100 | name += 'ᅠㅣline' + space + node.loc.start.line;
101 |
102 | node.update(nameFunc(node.source(), name))
103 |
104 | } catch (e) {
105 | //if failure should occur, just don't update the node.
106 | }
107 | })
108 |
109 | if (again) { again = false; rewrite(output+''); }
110 |
111 | }
112 |
113 | function decofun(src) {
114 | if ((src instanceof Buffer) || typeof src === 'string') {
115 | return decofun.transform(src);
116 | }
117 | return module.exports.auto.apply(this, arguments);
118 | }
119 |
120 |
121 | decofun.transform = function (src) {
122 | rewrite.count = 0;
123 | rewrite(src+'');
124 | return output+'';
125 | }
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/lib/auto.js:
--------------------------------------------------------------------------------
1 | var originalCompile = module.__proto__._compile;
2 |
3 | module.exports = function (decofun) {
4 | return function () {
5 | module.__proto__._compile = (function (compile) {
6 | return function (content, filename) {
7 | this.deanonymized = true;
8 | compile.call(this, decofun(content), filename);
9 | }
10 | }(module._compile));
11 | }
12 | }
13 |
14 | module.exports.restore = function () {
15 | Object.keys(require.cache).filter(function(k) {
16 | var mod = require.cache[k];
17 | return mod.deanonymized
18 | }).forEach(function (k) {
19 | delete require.cache[k];
20 | })
21 |
22 | module.__proto__._compile = originalCompile;
23 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "decofun",
3 | "version": "1.3.0",
4 | "description": "",
5 | "main": "index.js",
6 | "bin": {
7 | "deco": "./cmd.js"
8 | },
9 | "scripts": {
10 | "test": "npm run coverage",
11 | "test-no-cov": "./node_modules/.bin/mocha -u qunit",
12 | "coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha --report lcovonly -- -u qunit",
13 | "coveralls": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage",
14 | "cov-analysis": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- -u qunit"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git://github.com/davidmarkclements/decofun.git"
19 | },
20 | "author": "David Mark Clements",
21 | "license": "ISC",
22 | "dependencies": {
23 | "cute-stack": "^1.0.2",
24 | "falafel": "^0.3.1",
25 | "minimist": "^1.1.0"
26 | },
27 | "devDependencies": {
28 | "chai": "^1.9.1",
29 | "istanbul": "^0.3.2",
30 | "mocha": "^1.21.4",
31 | "coveralls": "^2.11.1"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/semantics.json:
--------------------------------------------------------------------------------
1 | {
2 | "argTo": "passed into ",
3 | "returnedFrom": "returned from ",
4 | "onProperty": "as property ",
5 | "ofVar": "as var "
6 | }
--------------------------------------------------------------------------------
/test/api.js:
--------------------------------------------------------------------------------
1 | var decofun = require('../')
2 | var should = require('chai').should();
3 | var _ = '\uffa0'; //space
4 |
5 |
6 | //transformations:
7 |
8 | suite('functions assigned to declared variables')
9 |
10 | test('are labelled "of var | line N"', function () {
11 | var input = 'var myFn = function () {}'
12 | var expected = 'var myFn = function as'+_+'var'+_+'myFn'+_+'ㅣline'+_+'1 () {}'
13 |
14 | decofun(input).should.equal(expected)
15 |
16 | })
17 |
18 | suite('functions assigned to variables after declaration')
19 |
20 | test('are labelled "of var | line N"', function () {
21 | var input = 'var myFn; myFn = function () {}'
22 | var expected = 'var myFn; myFn = function as'+_+'var'+_+'myFn'+_+'ㅣline'+_+'1 () {}'
23 |
24 | decofun(input).should.equal(expected)
25 |
26 | })
27 |
28 |
29 | suite('function parameters')
30 |
31 | test('are labelled "passed into | line N"', function () {
32 | var input = 'someFunc(\'blah\', function () {})'
33 | var expected = 'someFunc(\'blah\', function passed'+_+'into'+_+'someFunc'+_+'ㅣline'+_+'1 () {})'
34 |
35 | decofun(input).should.equal(expected)
36 |
37 | })
38 |
39 |
40 | suite('method parameters')
41 |
42 | test('are labelled "passed into ː | line N"', function () {
43 | var input = 'obj.prop(function () { })'
44 | var name = 'passed into objːprop ㅣline 1'.replace(/ /g, _);
45 | var expected = 'obj.prop(function '+name+' () { })'
46 |
47 | decofun(input).should.equal(expected)
48 |
49 | })
50 |
51 | suite('sub-object method parameters')
52 |
53 | test('are labelled "passed into ː | line N"', function () {
54 | var input = 'obj.subobj.prop(function () { })'
55 | var name = 'passed into subobjːprop ㅣline 1'.replace(/ /g, _);
56 | var expected = 'obj.subobj.prop(function '+name+' () { })'
57 |
58 | decofun(input).should.equal(expected)
59 |
60 | })
61 |
62 |
63 | suite('returned functions')
64 |
65 | test('are labelled "returned from | line N"', function () {
66 | var input = 'function f() {return function () { }}'
67 | var name = 'returned from f ㅣline 1'.replace(/ /g, _);
68 | var expected = 'function f() {return function '+name+' () { }}'
69 |
70 | decofun(input).should.equal(expected)
71 |
72 | })
73 |
74 |
75 |
76 | suite('returned functions of returned anonymous functions')
77 |
78 | test('are labelled "returned from ᐸ ᐳ | line N"', function () {
79 | var input = function contain () {
80 | return function () {
81 | return function () {
82 |
83 | }
84 | }
85 | }.toString();
86 |
87 | var firstName = 'returned from contain ㅣline 2'.replace(/ /g, _);
88 | var secondName = 'returned from ᐸ returned from contain ᐳ ㅣline 3'.replace(/ /g, _);
89 |
90 | var expected = ['function contain() {',
91 | ' return function ' + firstName + ' () { ',
92 | ' return function ' + secondName + ' () {',
93 | '',
94 | ' }',
95 | ' }',
96 | ' }'].join('\n')
97 |
98 |
99 |
100 | decofun(input).should.equal(expected)
101 |
102 |
103 |
104 | })
105 |
106 |
107 | suite('methods declared in object literals')
108 |
109 | test('are labelled "as property ㅣ line N"', function () {
110 | var input = function contain () {
111 | return {
112 | propInLiteral: function () {}
113 | }
114 | }.toString()
115 |
116 | var name = 'as property propInLiteral ㅣline 3'.replace(/ /g, _)
117 |
118 | var expected = ['function contain() {',
119 | ' return {',
120 | ' propInLiteral: function ' + name + ' () {}',
121 | ' }',
122 | ' }'].join('\n')
123 |
124 | decofun(input).should.equal(expected)
125 |
126 | })
127 |
128 |
129 | suite('methods assigned to instantiated objects')
130 |
131 | test('are labelled "as property ㅣ line N"', function () {
132 | var input = 'var o = {}; o.p = function (cb) { }'
133 | var name = 'as property p ㅣline 1'.replace(/ /g, _)
134 | var expected = 'var o = {}; o.p = function ' + name + ' (cb) { }'
135 |
136 | decofun(input).should.equal(expected)
137 |
138 | })
139 |
140 |
141 | suite('immediately invoked function expressions')
142 |
143 | test('are labelled "IIFEㅣ line N"', function () {
144 | var input = '!function() {}()'
145 | var expected = '!function IIFE'+_+'ㅣline'+_+'1() {}()';
146 | decofun(input).should.equal(expected)
147 |
148 | input = '(function(){}());'
149 | expected = '(function IIFE'+_+'ㅣline'+_+'1(){}());'
150 |
151 | decofun(input).should.equal(expected)
152 |
153 | })
154 |
155 | //API:
156 |
157 | suite('decofun.auto')
158 |
159 | test('will transform modules loaded with require', function () {
160 | var transformed = require('./fixtures/transformed').test+''
161 | decofun.auto();
162 | var original = require('./fixtures/original').test+'';
163 | original.should.equal(transformed)
164 |
165 | decofun.restore();
166 | })
167 |
168 | suite('decofun.transform')
169 |
170 | test('performs the same as decofun(src)', function () {
171 | var input = 'var myFn = function () {}'
172 | var expected = 'var myFn = function as'+_+'var'+_+'myFn'+_+'ㅣline'+_+'1 () {}'
173 |
174 | decofun.transform(input).should.equal(expected)
175 | })
176 |
177 |
178 | suite('decofun implicit auto via polymorphism')
179 |
180 | test('calls the auto method when no source string is passed', function () {
181 | var transformed = require('./fixtures/transformed').test+''
182 | decofun();
183 | var original = require('./fixtures/original').test+'';
184 | original.should.equal(transformed)
185 |
186 | decofun.restore();
187 | })
188 |
189 | suite('decofun.restore')
190 |
191 | test('will restore the require to pre-auto state', function () {
192 | decofun.auto();
193 | var original = require('./fixtures/original').test+'';
194 |
195 | var transformed = require('./fixtures/transformed').test+''
196 | original.should.equal(transformed)
197 |
198 | decofun.restore();
199 | genuineOriginal = require('./fixtures/original').test+'';
200 |
201 | genuineOriginal.should.not.equal(transformed)
202 | })
203 |
204 |
205 | //features
206 |
207 | suite('recursion safety')
208 |
209 | test('supports a maximum depth of returned functions', function () {
210 | var input = 'function toodeep () {\n return function () {\n return function () {\n return function () {\n \n return function () {\n return function () {\n return function () {\n \n return function () {\n return function () {\n return function () {\n\n return function () {\n \n } \n \n }\n }\n } \n\n }\n }\n } \n }\n }\n }\n}';
211 | var expected = 'function toodeep () {\n return function returnedᅠfromᅠtoodeepᅠㅣlineᅠ2 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠㅣlineᅠ3 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠㅣlineᅠ4 () {\n \n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ6 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ7 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ8 () {\n \n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ10 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ11 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ12 () {\n\n return function () {\n \n } \n \n }\n }\n } \n\n }\n }\n } \n }\n }\n }\n}';
212 |
213 | decofun(input).should.equal.expected;
214 | })
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
--------------------------------------------------------------------------------
/test/behaviors.js:
--------------------------------------------------------------------------------
1 | var decofun = require('../')
2 | var should = require('chai').should();
3 |
4 | suite('recursion safety')
5 |
6 | test('supports a maximum depth of returned functions', function () {
7 | var input = 'function toodeep () {\n return function () {\n return function () {\n return function () {\n \n return function () {\n return function () {\n return function () {\n \n return function () {\n return function () {\n return function () {\n\n return function () {\n \n } \n \n }\n }\n } \n\n }\n }\n } \n }\n }\n }\n}';
8 | var expected = 'function toodeep () {\n return function returnedᅠfromᅠtoodeepᅠㅣlineᅠ2 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠㅣlineᅠ3 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠㅣlineᅠ4 () {\n \n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ6 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ7 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ8 () {\n \n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ10 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ11 () {\n return function returnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠᐸᅠreturnedᅠfromᅠtoodeepᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠᐳᅠㅣlineᅠ12 () {\n\n return function () {\n \n } \n \n }\n }\n } \n\n }\n }\n } \n }\n }\n }\n}';
9 |
10 | decofun(input).should.equal.expected;
11 | })
12 |
--------------------------------------------------------------------------------
/test/fixtures/original.js:
--------------------------------------------------------------------------------
1 | module.exports.test = function () {
2 | return function () {
3 | return {
4 | prop: function () {
5 | setTimeout(function () {
6 | console.trace('Getting a trace...');
7 | }, 10)
8 |
9 | }
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/test/fixtures/transformed.js:
--------------------------------------------------------------------------------
1 | module.exports.test = function asᅠpropertyᅠtestᅠㅣlineᅠ1() {
2 | return function returnedᅠfromᅠᐸᅠasᅠpropertyᅠtestᅠᐳᅠㅣlineᅠ2 () {
3 | return {
4 | prop: function asᅠpropertyᅠpropᅠㅣlineᅠ4 () {
5 | setTimeout(function passedᅠintoᅠsetTimeoutᅠㅣlineᅠ5 () {
6 | console.trace('Getting a trace...');
7 | }, 10)
8 |
9 | }
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/test/transformations.js:
--------------------------------------------------------------------------------
1 | var decofun = require('../')
2 | var should = require('chai').should();
3 | var _ = '\uffa0'; //space
4 |
5 | suite('functions assigned to declared variables')
6 |
7 | test('are labelled "of var | line N"', function () {
8 | var input = 'var myFn = function () {}'
9 | var expected = 'var myFn = function as'+_+'var'+_+'myFn'+_+'ㅣline'+_+'1 () {}'
10 |
11 | decofun(input).should.equal(expected)
12 |
13 | })
14 |
15 | suite('functions assigned to variables after declaration')
16 |
17 | test('are labelled "of var | line N"', function () {
18 | var input = 'var myFn; myFn = function () {}'
19 | var expected = 'var myFn; myFn = function as'+_+'var'+_+'myFn'+_+'ㅣline'+_+'1 () {}'
20 |
21 | decofun(input).should.equal(expected)
22 |
23 | })
24 |
25 |
26 | suite('function parameters')
27 |
28 | test('are labelled "passed into | line N"', function () {
29 | var input = 'someFunc(\'blah\', function () {})'
30 | var expected = 'someFunc(\'blah\', function passed'+_+'into'+_+'someFunc'+_+'ㅣline'+_+'1 () {})'
31 |
32 | decofun(input).should.equal(expected)
33 |
34 | })
35 |
36 |
37 | suite('method parameters')
38 |
39 | test('are labelled "passed into ː | line N"', function () {
40 | var input = 'obj.prop(function () { })'
41 | var name = 'passed into objːprop ㅣline 1'.replace(/ /g, _);
42 | var expected = 'obj.prop(function '+name+' () { })'
43 |
44 | decofun(input).should.equal(expected)
45 |
46 | })
47 |
48 | suite('sub-object method parameters')
49 |
50 | test('are labelled "passed into ː | line N"', function () {
51 | var input = 'obj.subobj.prop(function () { })'
52 | var name = 'passed into subobjːprop ㅣline 1'.replace(/ /g, _);
53 | var expected = 'obj.subobj.prop(function '+name+' () { })'
54 |
55 | decofun(input).should.equal(expected)
56 |
57 | })
58 |
59 |
60 | suite('returned functions')
61 |
62 | test('are labelled "returned from | line N"', function () {
63 | var input = 'function f() {return function () { }}'
64 | var name = 'returned from f ㅣline 1'.replace(/ /g, _);
65 | var expected = 'function f() {return function '+name+' () { }}'
66 |
67 | decofun(input).should.equal(expected)
68 |
69 | })
70 |
71 |
72 |
73 | suite('returned functions of returned anonymous functions')
74 |
75 | test('are labelled "returned from ᐸ ᐳ | line N"', function () {
76 | var input = function contain () {
77 | return function () {
78 | return function () {
79 |
80 | }
81 | }
82 | }.toString();
83 |
84 | var firstName = 'returned from contain ㅣline 2'.replace(/ /g, _);
85 | var secondName = 'returned from ᐸ returned from contain ᐳ ㅣline 3'.replace(/ /g, _);
86 |
87 | var expected = ['function contain() {',
88 | ' return function ' + firstName + ' () { ',
89 | ' return function ' + secondName + ' () {',
90 | '',
91 | ' }',
92 | ' }',
93 | ' }'].join('\n')
94 |
95 |
96 |
97 | decofun(input).should.equal(expected)
98 |
99 |
100 |
101 | })
102 |
103 |
104 | suite('methods declared in object literals')
105 |
106 | test('are labelled "as property ㅣ line N"', function () {
107 | var input = function contain () {
108 | return {
109 | propInLiteral: function () {}
110 | }
111 | }.toString()
112 |
113 | var name = 'as property propInLiteral ㅣline 3'.replace(/ /g, _)
114 |
115 | var expected = ['function contain() {',
116 | ' return {',
117 | ' propInLiteral: function ' + name + ' () {}',
118 | ' }',
119 | ' }'].join('\n')
120 |
121 | decofun(input).should.equal(expected)
122 |
123 | })
124 |
125 |
126 | suite('methods assigned to instantiated objects')
127 |
128 | test('are labelled "as property ㅣ line N"', function () {
129 | var input = 'var o = {}; o.p = function (cb) { }'
130 | var name = 'as property p ㅣline 1'.replace(/ /g, _)
131 | var expected = 'var o = {}; o.p = function ' + name + ' (cb) { }'
132 |
133 | decofun(input).should.equal(expected)
134 |
135 | })
136 |
137 |
138 | suite('immediately invoked function expressions')
139 |
140 | test('are labelled "IIFEㅣ line N"', function () {
141 | var input = '!function() {}()'
142 | var expected = '!function IIFE'+_+'ㅣline'+_+'1() {}()';
143 | decofun(input).should.equal(expected)
144 |
145 | input = '(function(){}());'
146 | expected = '(function IIFE'+_+'ㅣline'+_+'1(){}());'
147 |
148 | decofun(input).should.equal(expected)
149 |
150 | })
--------------------------------------------------------------------------------