Ignore merge
This commit is contained in:
parent
7d0dfb5600
commit
c65fde6bd6
|
@ -12,4 +12,5 @@
|
||||||
/node_modules/debug
|
/node_modules/debug
|
||||||
/node_modules/jugglingdb
|
/node_modules/jugglingdb
|
||||||
/node_modules/mocha
|
/node_modules/mocha
|
||||||
/node_modules/asteroid-module-loader
|
/node_modules/asteroid-module-loader
|
||||||
|
/node_modules/merge
|
|
@ -1,52 +0,0 @@
|
||||||
JavaScript/NodeJS Merge v1.1.0
|
|
||||||
==================================================
|
|
||||||
|
|
||||||
What is it?
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
JavaScript/NodeJS Merge is a tool to merge multiple objects into one object, with the possibility of create a new object cloned. His operation is very similar to the [jQuery.extend](http://api.jquery.com/jQuery.extend/) function but more flexible.
|
|
||||||
|
|
||||||
Example from NodeJS
|
|
||||||
--------------
|
|
||||||
|
|
||||||
var merge = require('merge'), // npm install -g merge
|
|
||||||
original, cloned;
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
|
|
||||||
merge({ one: 'hello' }, { two: 'world' })
|
|
||||||
|
|
||||||
); // {"one": "hello", "two": "world"}
|
|
||||||
|
|
||||||
original = { x: { y: 1 } };
|
|
||||||
|
|
||||||
cloned = merge(true, original);
|
|
||||||
|
|
||||||
cloned.x.y++;
|
|
||||||
|
|
||||||
console.log(original.x.y, cloned.x.y); // 1, 2
|
|
||||||
|
|
||||||
Example from JavaScript browser
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
<script src="http://yeikos.googlecode.com/files/merge.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
var original, cloned;
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
|
|
||||||
merge({ one: 'hello' }, { two: 'world' })
|
|
||||||
|
|
||||||
); // {"one": "hello", "two": "world"}
|
|
||||||
|
|
||||||
original = { x: { y: 1 } };
|
|
||||||
|
|
||||||
cloned = merge(true, original);
|
|
||||||
|
|
||||||
cloned.x.y++;
|
|
||||||
|
|
||||||
console.log(original.x.y, cloned.x.y); // 1, 2
|
|
||||||
|
|
||||||
</script>
|
|
|
@ -1,82 +0,0 @@
|
||||||
/*!
|
|
||||||
* @name JavaScript/NodeJS Merge v1.1.0
|
|
||||||
* @author yeikos
|
|
||||||
* @repository https://github.com/yeikos/js.merge
|
|
||||||
|
|
||||||
* Copyright 2013
|
|
||||||
* GNU General Public License
|
|
||||||
* http://www.gnu.org/licenses/gpl-3.0.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
;(function(isNode) {
|
|
||||||
|
|
||||||
function merge() {
|
|
||||||
|
|
||||||
var items = Array.prototype.slice.call(arguments),
|
|
||||||
result = items.shift(),
|
|
||||||
deep = (result === true),
|
|
||||||
size = items.length,
|
|
||||||
item, index, key;
|
|
||||||
|
|
||||||
if (deep || typeOf(result) !== 'object')
|
|
||||||
|
|
||||||
result = {};
|
|
||||||
|
|
||||||
for (index=0;index<size;++index)
|
|
||||||
|
|
||||||
if (typeOf(item = items[index]) === 'object')
|
|
||||||
|
|
||||||
for (key in item)
|
|
||||||
|
|
||||||
result[key] = deep ? clone(item[key]) : item[key];
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function clone(input) {
|
|
||||||
|
|
||||||
var output = input,
|
|
||||||
type = typeOf(input),
|
|
||||||
index, size;
|
|
||||||
|
|
||||||
if (type === 'array') {
|
|
||||||
|
|
||||||
output = [];
|
|
||||||
size = input.length;
|
|
||||||
|
|
||||||
for (index=0;index<size;++index)
|
|
||||||
|
|
||||||
output[index] = clone(input[index]);
|
|
||||||
|
|
||||||
} else if (type === 'object') {
|
|
||||||
|
|
||||||
output = {};
|
|
||||||
|
|
||||||
for (index in input)
|
|
||||||
|
|
||||||
output[index] = clone(input[index]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function typeOf(input) {
|
|
||||||
|
|
||||||
return ({}).toString.call(input).match(/\s([\w]+)/)[1].toLowerCase();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNode) {
|
|
||||||
|
|
||||||
module.exports = merge;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
window.merge = merge;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
})(typeof module === 'object' && module && typeof module.exports === 'object' && module.exports);
|
|
|
@ -1,3 +0,0 @@
|
||||||
/* JavaScript/NodeJS Merge v1.1.0 - https://github.com/yeikos/js.merge */
|
|
||||||
|
|
||||||
;(function(e){function t(){var e=Array.prototype.slice.call(arguments),t=e.shift(),i=t===true,s=e.length,o,u,a;if(i||r(t)!=="object")t={};for(u=0;u<s;++u)if(r(o=e[u])==="object")for(a in o)t[a]=i?n(o[a]):o[a];return t}function n(e){var t=e,i=r(e),s,o;if(i==="array"){t=[];o=e.length;for(s=0;s<o;++s)t[s]=n(e[s])}else if(i==="object"){t={};for(s in e)t[s]=n(e[s])}return t}function r(e){return{}.toString.call(e).match(/\s([\w]+)/)[1].toLowerCase()}if(e){module.exports=t}else{window.merge=t}})(typeof module==="object"&&module&&typeof module.exports==="object"&&module.exports);
|
|
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"name": "merge",
|
|
||||||
"version": "1.1.0",
|
|
||||||
"author": {
|
|
||||||
"name": "yeikos"
|
|
||||||
},
|
|
||||||
"description": "JavaScript/NodeJS Merge is a tool to merge multiple objects into one object, with the possibility of create a new object cloned. His operation is very similar to the jQuery.extend function but more flexible.",
|
|
||||||
"homepage": "https://github.com/yeikos/js.merge",
|
|
||||||
"main": "merge.js",
|
|
||||||
"readme": "JavaScript/NodeJS Merge v1.1.0\r\n==================================================\r\n\r\nWhat is it?\r\n--------------------------------------\r\n\r\nJavaScript/NodeJS Merge is a tool to merge multiple objects into one object, with the possibility of create a new object cloned. His operation is very similar to the [jQuery.extend](http://api.jquery.com/jQuery.extend/) function but more flexible.\r\n\r\nExample from NodeJS\r\n--------------\r\n\r\n\tvar merge = require('merge'), // npm install -g merge\r\n\t\toriginal, cloned;\r\n\t\r\n\tconsole.log(\r\n\t\t\r\n\t\tmerge({ one: 'hello' }, { two: 'world' })\r\n\r\n\t); // {\"one\": \"hello\", \"two\": \"world\"}\r\n\t\r\n\toriginal = { x: { y: 1 } };\r\n\r\n\tcloned = merge(true, original);\r\n\r\n\tcloned.x.y++;\r\n\r\n\tconsole.log(original.x.y, cloned.x.y); // 1, 2\r\n\r\nExample from JavaScript browser\r\n--------------------------\r\n\r\n\t<script src=\"http://yeikos.googlecode.com/files/merge.js\"></script>\r\n\t\r\n\t<script>\r\n\t\t\r\n\t\tvar original, cloned;\r\n\t\t\r\n\t\tconsole.log(\r\n\t\t\t\r\n\t\t\tmerge({ one: 'hello' }, { two: 'world' })\r\n\t\r\n\t\t); // {\"one\": \"hello\", \"two\": \"world\"}\r\n\t\t\r\n\t\toriginal = { x: { y: 1 } };\r\n\t\r\n\t\tcloned = merge(true, original);\r\n\t\r\n\t\tcloned.x.y++;\r\n\t\r\n\t\tconsole.log(original.x.y, cloned.x.y); // 1, 2\r\n\r\n\t</script>",
|
|
||||||
"readmeFilename": "README.md",
|
|
||||||
"_id": "merge@1.1.0",
|
|
||||||
"_from": "merge@"
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
<!DOCTYPE html><html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<title>JavaScript/NodeJS Merge v1.1.0</title>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="qunit/qunit.css" />
|
|
||||||
|
|
||||||
<script src="qunit/qunit.js" data-qunit></script>
|
|
||||||
<script src="../merge.js" data-qunit></script>
|
|
||||||
<script src="tests.js" data-qunit></script>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="qunit"></div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,8 +0,0 @@
|
||||||
var qunit = require('./qunit/node_modules/qunit/index.js');
|
|
||||||
|
|
||||||
qunit.run({
|
|
||||||
|
|
||||||
code: { path: '../merge.js', namespace: 'merge' },
|
|
||||||
tests: 'tests.js'
|
|
||||||
|
|
||||||
});
|
|
|
@ -1,15 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
basedir=`dirname "$0"`
|
|
||||||
|
|
||||||
case `uname` in
|
|
||||||
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ -x "$basedir/node" ]; then
|
|
||||||
"$basedir/node" "$basedir/../qunit/bin/cli.js" "$@"
|
|
||||||
ret=$?
|
|
||||||
else
|
|
||||||
node "$basedir/../qunit/bin/cli.js" "$@"
|
|
||||||
ret=$?
|
|
||||||
fi
|
|
||||||
exit $ret
|
|
|
@ -1,5 +0,0 @@
|
||||||
@IF EXIST "%~dp0\node.exe" (
|
|
||||||
"%~dp0\node.exe" "%~dp0\..\qunit\bin\cli.js" %*
|
|
||||||
) ELSE (
|
|
||||||
node "%~dp0\..\qunit\bin\cli.js" %*
|
|
||||||
)
|
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "support/qunit"]
|
|
||||||
path = support/qunit
|
|
||||||
url = https://github.com/jquery/qunit.git
|
|
|
@ -1,2 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
node_modules
|
|
|
@ -1,4 +0,0 @@
|
||||||
test:
|
|
||||||
node ./test/testrunner.js
|
|
||||||
|
|
||||||
.PHONY: install test
|
|
|
@ -1,116 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
var util = require('util'),
|
|
||||||
argsparser = require('argsparser'),
|
|
||||||
fs = require('fs');
|
|
||||||
|
|
||||||
var root = __dirname + '/..',
|
|
||||||
args = argsparser.parse(),
|
|
||||||
testrunner = require(root),
|
|
||||||
o = testrunner.options,
|
|
||||||
code, tests,
|
|
||||||
help;
|
|
||||||
|
|
||||||
help = ''
|
|
||||||
+ '\nUsage: cli [options] value (boolean value can be used)'
|
|
||||||
+ '\n'
|
|
||||||
+ '\nOptions:'
|
|
||||||
+ '\n -c, --code path to code you want to test'
|
|
||||||
+ '\n -t, --tests path to tests (space separated)'
|
|
||||||
+ '\n -d, --deps dependency paths - files required before code (space separated)'
|
|
||||||
+ '\n -l, --log logging options, json have to be used'
|
|
||||||
+ '\n --cov create tests coverage report'
|
|
||||||
+ '\n -h, --help show this help'
|
|
||||||
+ '\n -v, --version show module version'
|
|
||||||
+ '\n';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a code or dependency argument, returning an object defining the
|
|
||||||
* specified file path or/and module name.
|
|
||||||
* The exports of the module will be exposed globally by default. To expose
|
|
||||||
* exports as a named variable, prefix the resource with the desired variable
|
|
||||||
* name followed by a colon.
|
|
||||||
* This allows you to more accurately recreate browser usage of QUnit, for
|
|
||||||
* tests which are portable between browser runtime environmemts and Node.js.
|
|
||||||
* @param {string} path to file or module name to require.
|
|
||||||
* @return {Object} resource
|
|
||||||
*/
|
|
||||||
function parsePath(path) {
|
|
||||||
var parts = path.split(':'),
|
|
||||||
resource = {
|
|
||||||
path: path
|
|
||||||
};
|
|
||||||
|
|
||||||
if (parts.length === 2) {
|
|
||||||
resource.namespace = parts[0];
|
|
||||||
resource.path = parts[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return resource;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var key in args) {
|
|
||||||
switch(key) {
|
|
||||||
case 'node':
|
|
||||||
// Skip the 'node' argument
|
|
||||||
break;
|
|
||||||
case '-c':
|
|
||||||
case '--code':
|
|
||||||
code = parsePath(args[key]);
|
|
||||||
break;
|
|
||||||
case '-t':
|
|
||||||
case '--tests':
|
|
||||||
// it's assumed that tests arguments will be file paths whose
|
|
||||||
// contents are to be made global. This is consistent with use
|
|
||||||
// of QUnit in browsers.
|
|
||||||
tests = args[key];
|
|
||||||
break;
|
|
||||||
case '-d':
|
|
||||||
case '--deps':
|
|
||||||
o.deps = args[key];
|
|
||||||
if (!Array.isArray(o.deps)) {
|
|
||||||
o.deps = [o.deps];
|
|
||||||
}
|
|
||||||
o.deps = o.deps.map(parsePath);
|
|
||||||
break;
|
|
||||||
case '-l':
|
|
||||||
case '--log':
|
|
||||||
eval('o.log = ' + args[key]);
|
|
||||||
break;
|
|
||||||
case '--cov':
|
|
||||||
o.coverage = args[key];
|
|
||||||
break;
|
|
||||||
case '-p':
|
|
||||||
case '--paths':
|
|
||||||
o.paths = args[key];
|
|
||||||
break;
|
|
||||||
case '-v':
|
|
||||||
case '--version':
|
|
||||||
util.print(
|
|
||||||
JSON.parse(
|
|
||||||
fs.readFileSync(__dirname + '/../package.json')
|
|
||||||
).version + '\n'
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
case '-h':
|
|
||||||
case '-?':
|
|
||||||
case '--help':
|
|
||||||
util.print(help);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!code || !tests) {
|
|
||||||
util.print(help);
|
|
||||||
util.print('\nBoth --code and --tests arguments are required\n');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
testrunner.run({ code: code, tests: tests }, function(err, stats) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
process.exit(1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(stats.failed > 0 ? 1 : 0);
|
|
||||||
});
|
|
|
@ -1 +0,0 @@
|
||||||
module.exports = require("./lib/testrunner");
|
|
|
@ -1,138 +0,0 @@
|
||||||
var QUnit = require('../support/qunit/qunit/qunit.js'),
|
|
||||||
path = require('path'),
|
|
||||||
_ = require('underscore'),
|
|
||||||
trace = require('tracejs').trace;
|
|
||||||
|
|
||||||
// cycle.js: This file contains two functions, JSON.decycle and JSON.retrocycle,
|
|
||||||
// which make it possible to encode cyclical structures and dags in JSON, and to
|
|
||||||
// then recover them. JSONPath is used to represent the links.
|
|
||||||
// http://GOESSNER.net/articles/JsonPath/
|
|
||||||
require('../support/json/cycle');
|
|
||||||
|
|
||||||
var options = JSON.parse(process.argv[2]),
|
|
||||||
currentModule = path.basename(options.code.path, '.js'),
|
|
||||||
currentTest;
|
|
||||||
|
|
||||||
process.on('uncaughtException', function(err) {
|
|
||||||
process.send({event: 'uncaughtException'});
|
|
||||||
if (QUnit.config.current) {
|
|
||||||
QUnit.ok(false, 'Test threw unexpected exception: ' + err);
|
|
||||||
QUnit.start();
|
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
QUnit.config.autorun = false;
|
|
||||||
QUnit.config.autostart = false;
|
|
||||||
|
|
||||||
// make qunit api global, like it is in the browser
|
|
||||||
_.extend(global, QUnit);
|
|
||||||
|
|
||||||
// as well as the QUnit variable itself
|
|
||||||
global.QUnit = QUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Require a resource.
|
|
||||||
* @param {Object} res
|
|
||||||
*/
|
|
||||||
function _require(res, addToGlobal) {
|
|
||||||
var exports = require(res.path.replace(/\.js$/, ''));
|
|
||||||
|
|
||||||
if (addToGlobal) {
|
|
||||||
// resource can define 'namespace' to expose its exports as a named object
|
|
||||||
if (res.namespace) {
|
|
||||||
global[res.namespace] = exports;
|
|
||||||
} else {
|
|
||||||
_.extend(global, exports);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QUnit.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate coverage stats using bunker
|
|
||||||
*/
|
|
||||||
function calcCoverage() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for each started test.
|
|
||||||
* @param {Object} test
|
|
||||||
*/
|
|
||||||
QUnit.testStart(function(test) {
|
|
||||||
// currentTest is undefined while first test is not done yet
|
|
||||||
currentTest = test.name;
|
|
||||||
|
|
||||||
// use last module name if no module name defined
|
|
||||||
currentModule = test.module || currentModule;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for each assertion.
|
|
||||||
* @param {Object} data
|
|
||||||
*/
|
|
||||||
QUnit.log(function(data) {
|
|
||||||
data.test = this.config.current.testName;
|
|
||||||
data.module = currentModule;
|
|
||||||
process.send({
|
|
||||||
event: 'assertionDone',
|
|
||||||
data: JSON.decycle(data)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for one done test.
|
|
||||||
* @param {Object} test
|
|
||||||
*/
|
|
||||||
QUnit.testDone(function(data) {
|
|
||||||
// use last module name if no module name defined
|
|
||||||
data.module = data.module || currentModule;
|
|
||||||
process.send({
|
|
||||||
event: 'testDone',
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for all done tests in the file.
|
|
||||||
* @param {Object} res
|
|
||||||
*/
|
|
||||||
QUnit.done(_.debounce(function(data) {
|
|
||||||
if (options.coverage) {
|
|
||||||
data.coverage = calcCoverage();
|
|
||||||
}
|
|
||||||
|
|
||||||
process.send({
|
|
||||||
event: 'done',
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
}, 1000));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide better stack traces
|
|
||||||
*/
|
|
||||||
var error = console.error;
|
|
||||||
console.error = function(obj) {
|
|
||||||
// log full stacktrace
|
|
||||||
if (obj && obj.stack) {
|
|
||||||
obj = trace(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error.apply(this, arguments);
|
|
||||||
};
|
|
||||||
|
|
||||||
// require deps
|
|
||||||
options.deps.forEach(function(dep) {
|
|
||||||
_require(dep, true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// require code
|
|
||||||
_require(options.code, true);
|
|
||||||
|
|
||||||
// require tests
|
|
||||||
options.tests.forEach(function(res) {
|
|
||||||
_require(res, false);
|
|
||||||
});
|
|
|
@ -1,35 +0,0 @@
|
||||||
var fs = require('fs'),
|
|
||||||
util = require('util'),
|
|
||||||
_ = require('underscore'),
|
|
||||||
bunker;
|
|
||||||
|
|
||||||
try {
|
|
||||||
bunker = require('bunker');
|
|
||||||
} catch (err) {}
|
|
||||||
|
|
||||||
exports.instrument = function(path) {
|
|
||||||
var src = fs.readFileSync(path, 'utf-8'),
|
|
||||||
newSrc = bunker(src).compile();
|
|
||||||
|
|
||||||
fs.renameSync(src, '__' + src);
|
|
||||||
fs.writeFileSync(path, newSrc, 'utf-8');
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
exports.restore = function(path) {
|
|
||||||
// do it only if the original file exist
|
|
||||||
if (fs.statSync('__' + path).isFile()) {
|
|
||||||
fs.unlinkSync(path);
|
|
||||||
fs.renameSync('__' + path, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!bunker) {
|
|
||||||
_.each(exports, function(fn, name) {
|
|
||||||
exports[name] = function() {
|
|
||||||
util.error('Module "bunker" is not installed.'.red);
|
|
||||||
process.exit(1);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,197 +0,0 @@
|
||||||
var Table = require('cli-table');
|
|
||||||
|
|
||||||
var data,
|
|
||||||
log = console.log;
|
|
||||||
|
|
||||||
data = {
|
|
||||||
assertions: [],
|
|
||||||
tests: [],
|
|
||||||
summaries: []
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.assertion = function(d) {
|
|
||||||
if (d) {
|
|
||||||
data.assertions.push(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.assertions;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.test = function(d) {
|
|
||||||
if (d) {
|
|
||||||
data.tests.push(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.tests;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.summary = function(d) {
|
|
||||||
if (d) {
|
|
||||||
data.summaries.push(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.summaries;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get global tests stats in unified format
|
|
||||||
*/
|
|
||||||
exports.stats = function() {
|
|
||||||
var stats = {
|
|
||||||
files: 0,
|
|
||||||
assertions: 0,
|
|
||||||
failed: 0,
|
|
||||||
passed: 0,
|
|
||||||
runtime: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
data.summaries.forEach(function(file) {
|
|
||||||
stats.files++;
|
|
||||||
stats.assertions += file.total;
|
|
||||||
stats.failed += file.failed;
|
|
||||||
stats.passed += file.passed;
|
|
||||||
stats.runtime += file.runtime;
|
|
||||||
});
|
|
||||||
|
|
||||||
stats.tests = data.tests.length;
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset global stats data
|
|
||||||
*/
|
|
||||||
exports.reset = function() {
|
|
||||||
data = {
|
|
||||||
assertions: [],
|
|
||||||
tests: [],
|
|
||||||
summaries: []
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var print = exports.print = {};
|
|
||||||
|
|
||||||
print.assertions = function() {
|
|
||||||
var table,
|
|
||||||
currentModule, module,
|
|
||||||
currentTest, test;
|
|
||||||
|
|
||||||
table = new Table({
|
|
||||||
head: ['Module', 'Test', 'Assertion', 'Result'],
|
|
||||||
colWidths: [40, 40, 40, 8]
|
|
||||||
});
|
|
||||||
|
|
||||||
data.assertions.forEach(function(data) {
|
|
||||||
// just easier to read the table
|
|
||||||
if (data.module === currentModule) {
|
|
||||||
module = '';
|
|
||||||
} else {
|
|
||||||
module = currentModule = data.module;
|
|
||||||
}
|
|
||||||
|
|
||||||
// just easier to read the table
|
|
||||||
if (data.test === currentTest) {
|
|
||||||
test = '';
|
|
||||||
} else {
|
|
||||||
test = currentTest = data.test;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.push([module, test, data.message, data.result ? 'ok' : 'fail']);
|
|
||||||
});
|
|
||||||
|
|
||||||
log('\nAssertions:\n' + table.toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
print.errors = function() {
|
|
||||||
var errors = [];
|
|
||||||
|
|
||||||
data.assertions.forEach(function(data) {
|
|
||||||
if (!data.result) {
|
|
||||||
errors.push(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (errors.length) {
|
|
||||||
log('\n\nErrors:');
|
|
||||||
errors.forEach(function(data) {
|
|
||||||
log('\nModule: ' + data.module + ' Test: ' + data.test);
|
|
||||||
if (data.message) {
|
|
||||||
log(data.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.source) {
|
|
||||||
log(data.source);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.expected != null || data.actual != null) {
|
|
||||||
//it will be an error if data.expected !== data.actual, but if they're
|
|
||||||
//both undefined, it means that they were just not filled out because
|
|
||||||
//no assertions were hit (likely due to code error that would have been logged as source or message).
|
|
||||||
log('Actual value:');
|
|
||||||
log(data.actual);
|
|
||||||
log('Expected value:');
|
|
||||||
log(data.expected);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
print.tests = function() {
|
|
||||||
var table,
|
|
||||||
currentModule, module;
|
|
||||||
|
|
||||||
table = new Table({
|
|
||||||
head: ['Module', 'Test', 'Failed', 'Passed', 'Total'],
|
|
||||||
colWidths: [40, 40, 8, 8, 8]
|
|
||||||
});
|
|
||||||
|
|
||||||
data.tests.forEach(function(data) {
|
|
||||||
// just easier to read the table
|
|
||||||
if (data.module === currentModule) {
|
|
||||||
module = '';
|
|
||||||
} else {
|
|
||||||
module = currentModule = data.module;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.push([module, data.name, data.failed, data.passed, data.total]);
|
|
||||||
});
|
|
||||||
|
|
||||||
log('\nTests:\n' + table.toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
print.summary = function() {
|
|
||||||
var table, fileColWidth = 50;
|
|
||||||
|
|
||||||
table = new Table({
|
|
||||||
head: ['File', 'Failed', 'Passed', 'Total', 'Runtime'],
|
|
||||||
colWidths: [fileColWidth + 2, 10, 10, 10, 10]
|
|
||||||
});
|
|
||||||
|
|
||||||
data.summaries.forEach(function(data) {
|
|
||||||
var code = data.code;
|
|
||||||
|
|
||||||
// truncate file name
|
|
||||||
if (code.length > fileColWidth) {
|
|
||||||
code = '...' + code.slice(code.length - fileColWidth + 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
table.push([code, data.failed, data.passed, data.total, data.runtime]);
|
|
||||||
});
|
|
||||||
|
|
||||||
log('\nSummary:\n' + table.toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
print.globalSummary = function() {
|
|
||||||
var table,
|
|
||||||
data = exports.stats();
|
|
||||||
|
|
||||||
table = new Table({
|
|
||||||
head: ['Files', 'Tests', 'Assertions', 'Failed', 'Passed', 'Runtime'],
|
|
||||||
colWidths: [12, 12, 12, 12, 12, 12]
|
|
||||||
});
|
|
||||||
|
|
||||||
table.push([data.files, data.tests, data.assertions, data.failed,
|
|
||||||
data.passed, data.runtime]);
|
|
||||||
|
|
||||||
log('\nGlobal summary:\n' + table.toString());
|
|
||||||
};
|
|
|
@ -1,182 +0,0 @@
|
||||||
var fs = require('fs'),
|
|
||||||
path = require('path'),
|
|
||||||
coverage = require('./coverage'),
|
|
||||||
cp = require('child_process'),
|
|
||||||
_ = require('underscore'),
|
|
||||||
log = exports.log = require('./log'),
|
|
||||||
util = require('util');
|
|
||||||
|
|
||||||
var options,
|
|
||||||
noop = function() {};
|
|
||||||
|
|
||||||
options = exports.options = {
|
|
||||||
|
|
||||||
// logging options
|
|
||||||
log: {
|
|
||||||
|
|
||||||
// log assertions overview
|
|
||||||
assertions: true,
|
|
||||||
|
|
||||||
// log expected and actual values for failed tests
|
|
||||||
errors: true,
|
|
||||||
|
|
||||||
// log tests overview
|
|
||||||
tests: true,
|
|
||||||
|
|
||||||
// log summary
|
|
||||||
summary: true,
|
|
||||||
|
|
||||||
// log global summary (all files)
|
|
||||||
globalSummary: true,
|
|
||||||
|
|
||||||
// log currently testing code file
|
|
||||||
testing: true
|
|
||||||
},
|
|
||||||
|
|
||||||
// run test coverage tool
|
|
||||||
coverage: false,
|
|
||||||
|
|
||||||
// define dependencies, which are required then before code
|
|
||||||
deps: null,
|
|
||||||
|
|
||||||
// define namespace your code will be attached to on global['your namespace']
|
|
||||||
namespace: null
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run one spawned instance with tests
|
|
||||||
* @param {Object} opts
|
|
||||||
* @param {Function} callback
|
|
||||||
*/
|
|
||||||
function runOne(opts, callback) {
|
|
||||||
var child;
|
|
||||||
|
|
||||||
child = cp.fork(
|
|
||||||
__dirname + '/child.js',
|
|
||||||
[JSON.stringify(opts)],
|
|
||||||
{env: process.env}
|
|
||||||
);
|
|
||||||
|
|
||||||
function kill() {
|
|
||||||
process.removeListener('exit', kill);
|
|
||||||
child.kill();
|
|
||||||
}
|
|
||||||
|
|
||||||
child.on('message', function(msg) {
|
|
||||||
if (msg.event === 'assertionDone') {
|
|
||||||
log.assertion(msg.data);
|
|
||||||
} else if (msg.event === 'testDone') {
|
|
||||||
log.test(msg.data);
|
|
||||||
} else if (msg.event === 'done') {
|
|
||||||
msg.data.code = opts.code.path;
|
|
||||||
log.summary(msg.data);
|
|
||||||
if (opts.log.testing) {
|
|
||||||
util.print('done');
|
|
||||||
}
|
|
||||||
callback(null, msg.data);
|
|
||||||
kill();
|
|
||||||
} else if (msg.event === 'uncaughtException') {
|
|
||||||
callback(new Error('Uncaught exception in child process.'));
|
|
||||||
kill();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
process.on('exit', kill);
|
|
||||||
|
|
||||||
if (opts.log.testing) {
|
|
||||||
util.print('\nTesting ', opts.code.path + ' ... ');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make an absolute path from relative
|
|
||||||
* @param {string|Object} file
|
|
||||||
* @return {Object}
|
|
||||||
*/
|
|
||||||
function absPath(file) {
|
|
||||||
if (typeof file === 'string') {
|
|
||||||
file = {path: file};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file.path.charAt(0) != '/') {
|
|
||||||
file.path = path.resolve(process.cwd(), file.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert path or array of paths to array of abs paths
|
|
||||||
* @param {Array|string} files
|
|
||||||
* @return {Array}
|
|
||||||
*/
|
|
||||||
function absPaths(files) {
|
|
||||||
var ret = [];
|
|
||||||
|
|
||||||
if (Array.isArray(files)) {
|
|
||||||
files.forEach(function(file) {
|
|
||||||
ret.push(absPath(file));
|
|
||||||
});
|
|
||||||
} else if (files) {
|
|
||||||
ret.push(absPath(files));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run tests in spawned node instance async for every test.
|
|
||||||
* @param {Object|Array} files
|
|
||||||
* @param {Function} callback optional
|
|
||||||
*/
|
|
||||||
exports.run = function(files, callback) {
|
|
||||||
var filesCount = 0;
|
|
||||||
|
|
||||||
callback || (callback = noop);
|
|
||||||
|
|
||||||
if (!Array.isArray(files)) {
|
|
||||||
files = [files];
|
|
||||||
}
|
|
||||||
|
|
||||||
files.forEach(function(file) {
|
|
||||||
var opts = _.extend({}, options, file);
|
|
||||||
|
|
||||||
!opts.log && (opts.log = {});
|
|
||||||
opts.deps = absPaths(opts.deps);
|
|
||||||
opts.code = absPath(opts.code);
|
|
||||||
opts.tests = absPaths(opts.tests);
|
|
||||||
|
|
||||||
function done(err, stat) {
|
|
||||||
if (err) {
|
|
||||||
return callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
filesCount++;
|
|
||||||
|
|
||||||
if (filesCount >= files.length) {
|
|
||||||
_.each(opts.log, function(val, name) {
|
|
||||||
if (val && log.print[name]) {
|
|
||||||
log.print[name]();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
callback(null, log.stats());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts.coverage) {
|
|
||||||
coverage.instrument(opts.code);
|
|
||||||
} else {
|
|
||||||
runOne(opts, done);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set options
|
|
||||||
* @param {Object}
|
|
||||||
*/
|
|
||||||
exports.setup = function(opts) {
|
|
||||||
_.extend(options, opts);
|
|
||||||
};
|
|
1
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/.npmignore
generated
vendored
1
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/.npmignore
generated
vendored
|
@ -1 +0,0 @@
|
||||||
.DS_Store
|
|
4
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/Makefile
generated
vendored
4
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/Makefile
generated
vendored
|
@ -1,4 +0,0 @@
|
||||||
test:
|
|
||||||
node test/test.js
|
|
||||||
|
|
||||||
.PHONY: test
|
|
1
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/index.js
generated
vendored
1
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/index.js
generated
vendored
|
@ -1 +0,0 @@
|
||||||
module.exports = require('./lib/argsparser');
|
|
|
@ -1,41 +0,0 @@
|
||||||
/**
|
|
||||||
* Parser arguments array
|
|
||||||
* @param {Array} args optional arguments arrray.
|
|
||||||
* @return {Object} opts key value hash.
|
|
||||||
* @export
|
|
||||||
*/
|
|
||||||
exports.parse = function(args) {
|
|
||||||
// args is optional, default is process.argv
|
|
||||||
args = args || process.argv;
|
|
||||||
|
|
||||||
var opts = {}, curSwitch;
|
|
||||||
|
|
||||||
args.forEach(function(arg) {
|
|
||||||
// its a switch
|
|
||||||
if (/^(-|--)/.test(arg) || !curSwitch) {
|
|
||||||
opts[arg] = true;
|
|
||||||
curSwitch = arg;
|
|
||||||
// this arg is a data
|
|
||||||
} else {
|
|
||||||
if (arg === 'false') {
|
|
||||||
arg = false;
|
|
||||||
} else if (arg === 'true') {
|
|
||||||
arg = true;
|
|
||||||
} else if (!isNaN(arg)) {
|
|
||||||
arg = Number(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// it was a boolean switch per default,
|
|
||||||
// now it has got a val
|
|
||||||
if (typeof opts[curSwitch] === 'boolean') {
|
|
||||||
opts[curSwitch] = arg;
|
|
||||||
} else if (Array.isArray(opts[curSwitch])) {
|
|
||||||
opts[curSwitch].push(arg);
|
|
||||||
} else {
|
|
||||||
opts[curSwitch] = [opts[curSwitch], arg];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return opts;
|
|
||||||
};
|
|
35
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/package.json
generated
vendored
35
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/package.json
generated
vendored
|
@ -1,35 +0,0 @@
|
||||||
{
|
|
||||||
"name": "argsparser",
|
|
||||||
"description": "A tiny command line arguments parser",
|
|
||||||
"version": "0.0.6",
|
|
||||||
"author": {
|
|
||||||
"name": "Oleg Slobodskoi",
|
|
||||||
"email": "oleg008@gmail.com"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "http://github.com/kof/node-argsparser.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"arguments",
|
|
||||||
"options",
|
|
||||||
"command line",
|
|
||||||
"parser"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.2.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "node ./test/test.js"
|
|
||||||
},
|
|
||||||
"licenses": [
|
|
||||||
{
|
|
||||||
"type": "MIT",
|
|
||||||
"url": "http://www.opensource.org/licenses/mit-license.php"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"readme": "## Yet another tiny arguments parser for node\n\n## Features\n * extremely tiny\n * instead to parse all possible spellings, it uses just some simple rules\n\n## How this parser works\nThe target is to get a key-value object from an array. A key can be the first element or element prefixed by \"-\" and \"--\" (switch). \nSo the parser loops through the array and looks for keys. After he could detect an a key all next elements will be added as a value of this key until he find another key.\nIf there is no value, then the key is true (boolean). If there are a lot of values, then the key is an array.\n\n## Examples\n\nnode script.js -> {\"node\": \"script.js\"}\n\nnode script.js -o -> {\"node\": \"script.js\", \"-o\": true}\n\nnode script.js -o test -> {\"node\": \"script.js\", \"-o\": \"test\"}\n\nnode script.js -a testa --b testb -> {node: \"script.js\", \"-a\": \"testa\", \"--b\": \"testb\"}\n \nnode script.js -paths /test.js /test1.js -> {node: \"script.js\", \"-paths\": [\"/test.js\", \"/test1.js\"]}\n\n## Usage\n\n // per default it parses process.argv\n var args = require( \"argsparser\" ).parse(); // {\"node\": \"/path/to/your/script.js\"}\n \n // optional you can pass your own arguments array\n var args = require( \"argsparser\" ).parse([\"-a\", \"test\"]); // {\"-a\": \"test\"}\n\n \n## Installation\n npm install argsparser ",
|
|
||||||
"readmeFilename": "readme.md",
|
|
||||||
"_id": "argsparser@0.0.6",
|
|
||||||
"_from": "argsparser@0.0.6"
|
|
||||||
}
|
|
34
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/readme.md
generated
vendored
34
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/readme.md
generated
vendored
|
@ -1,34 +0,0 @@
|
||||||
## Yet another tiny arguments parser for node
|
|
||||||
|
|
||||||
## Features
|
|
||||||
* extremely tiny
|
|
||||||
* instead to parse all possible spellings, it uses just some simple rules
|
|
||||||
|
|
||||||
## How this parser works
|
|
||||||
The target is to get a key-value object from an array. A key can be the first element or element prefixed by "-" and "--" (switch).
|
|
||||||
So the parser loops through the array and looks for keys. After he could detect an a key all next elements will be added as a value of this key until he find another key.
|
|
||||||
If there is no value, then the key is true (boolean). If there are a lot of values, then the key is an array.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
node script.js -> {"node": "script.js"}
|
|
||||||
|
|
||||||
node script.js -o -> {"node": "script.js", "-o": true}
|
|
||||||
|
|
||||||
node script.js -o test -> {"node": "script.js", "-o": "test"}
|
|
||||||
|
|
||||||
node script.js -a testa --b testb -> {node: "script.js", "-a": "testa", "--b": "testb"}
|
|
||||||
|
|
||||||
node script.js -paths /test.js /test1.js -> {node: "script.js", "-paths": ["/test.js", "/test1.js"]}
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
// per default it parses process.argv
|
|
||||||
var args = require( "argsparser" ).parse(); // {"node": "/path/to/your/script.js"}
|
|
||||||
|
|
||||||
// optional you can pass your own arguments array
|
|
||||||
var args = require( "argsparser" ).parse(["-a", "test"]); // {"-a": "test"}
|
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
npm install argsparser
|
|
39
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/test/test.js
generated
vendored
39
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/argsparser/test/test.js
generated
vendored
|
@ -1,39 +0,0 @@
|
||||||
var a = require('assert'),
|
|
||||||
util = require('util'),
|
|
||||||
parse = require('../lib/argsparser').parse;
|
|
||||||
|
|
||||||
util.print('Run tests...\n');
|
|
||||||
|
|
||||||
a.deepEqual(parse(), {node: __filename}, 'node script.js');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['-o']), {'-o': true}, 'node script.js -o');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['-o', 'true']), {'-o': true}, 'node script.js -o true');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['-o', 'false']), {'-o': false}, 'node script.js -o false');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['-o', '123']), {'-o': 123}, 'node script.js -o 123');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['--token', 'bla--bla']), {'--token': 'bla--bla'}, 'node script.js --token bla--bla');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['-o', '123.456']), {'-o': 123.456}, 'node script.js -o 123.456');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['-o', 'test']), {'-o': 'test'}, 'node script.js -o test');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['-a', 'testa', '-b', 'testb']), {'-a': 'testa', '-b': 'testb'}, 'node script.js -a testa -b testb');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['--a', 'testa', '--b', 'testb']), {'--a': 'testa', '--b': 'testb'}, 'node script.js --a testa --b testb ');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['-a', 'testa', '--b', 'testb']), {'-a': 'testa', '--b': 'testb'}, 'node script.js -a testa --b testb');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['--a', 'testa', '-b', 'testb']), {'--a': 'testa', '-b': 'testb'}, 'node script.js --a testa -b testb');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['-paths', '/test.js', '/test1.js']), {'-paths': ['/test.js', '/test1.js']}, 'node script.js -paths /test.js /test1.js');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['--paths', '/test.js', '/test1.js']), {'--paths': ['/test.js', '/test1.js']}, 'node script.js --paths /test.js /test1.js');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['--paths', '/test.js', '/test1.js', '-a', 'testa']), {'--paths': ['/test.js', '/test1.js'], '-a': 'testa'}, 'node script.js --paths /test.js /test1.js -a testa');
|
|
||||||
|
|
||||||
a.deepEqual(parse(['--port', '80', '8080']), {'--port': [80, 8080]}, 'node server.js --port 80 8080');
|
|
||||||
|
|
||||||
util.print('All tests ok\n');
|
|
1
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/.npmignore
generated
vendored
1
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/.npmignore
generated
vendored
|
@ -1 +0,0 @@
|
||||||
node_modules
|
|
4
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/.travis.yml
generated
vendored
4
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/.travis.yml
generated
vendored
|
@ -1,4 +0,0 @@
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- 0.4
|
|
||||||
- 0.6
|
|
82
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/README.markdown
generated
vendored
82
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/README.markdown
generated
vendored
|
@ -1,82 +0,0 @@
|
||||||
bunker
|
|
||||||
======
|
|
||||||
|
|
||||||
Bunker is a module to calculate code coverage using native javascript
|
|
||||||
[burrito](https://github.com/substack/node-burrito) AST trickery.
|
|
||||||
|
|
||||||
[![build status](https://secure.travis-ci.org/substack/node-bunker.png)](http://travis-ci.org/substack/node-bunker)
|
|
||||||
|
|
||||||
![code coverage](http://substack.net/images/code_coverage.png)
|
|
||||||
|
|
||||||
examples
|
|
||||||
========
|
|
||||||
|
|
||||||
tiny
|
|
||||||
----
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
var bunker = require('bunker');
|
|
||||||
var b = bunker('var x = 0; for (var i = 0; i < 30; i++) { x++ }');
|
|
||||||
|
|
||||||
var counts = {};
|
|
||||||
|
|
||||||
b.on('node', function (node) {
|
|
||||||
if (!counts[node.id]) {
|
|
||||||
counts[node.id] = { times : 0, node : node };
|
|
||||||
}
|
|
||||||
counts[node.id].times ++;
|
|
||||||
});
|
|
||||||
|
|
||||||
b.run();
|
|
||||||
|
|
||||||
Object.keys(counts).forEach(function (key) {
|
|
||||||
var count = counts[key];
|
|
||||||
console.log(count.times + ' : ' + count.node.source());
|
|
||||||
});
|
|
||||||
````
|
|
||||||
|
|
||||||
output:
|
|
||||||
|
|
||||||
$ node example/tiny.js
|
|
||||||
1 : var x=0;
|
|
||||||
31 : i<30
|
|
||||||
30 : i++
|
|
||||||
30 : x++;
|
|
||||||
30 : x++
|
|
||||||
|
|
||||||
methods
|
|
||||||
=======
|
|
||||||
|
|
||||||
var bunker = require('bunker');
|
|
||||||
|
|
||||||
var b = bunker(src)
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
Create a new bunker code coverageifier with some source `src`.
|
|
||||||
|
|
||||||
The bunker object `b` is an `EventEmitter` that emits `'node'` events with two
|
|
||||||
parameters:
|
|
||||||
|
|
||||||
* `node` - the [burrito](https://github.com/substack/node-burrito) node object
|
|
||||||
* `stack` - the stack, [stackedy](https://github.com/substack/node-stackedy) style
|
|
||||||
|
|
||||||
b.include(src)
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Include some source into the bunker.
|
|
||||||
|
|
||||||
b.compile()
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Return the source wrapped with burrito.
|
|
||||||
|
|
||||||
b.assign(context={})
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
Assign the statement-tracking functions into `context`.
|
|
||||||
|
|
||||||
b.run(context={})
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Run the source using `vm.runInNewContext()` with some `context`.
|
|
||||||
The statement-tracking functions will be added to `context` by `assign()`.
|
|
51
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/example/prof.js
generated
vendored
51
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/example/prof.js
generated
vendored
|
@ -1,51 +0,0 @@
|
||||||
var bunker = require('bunker');
|
|
||||||
var b = bunker('(' + function () {
|
|
||||||
function beep () {
|
|
||||||
var x = 0;
|
|
||||||
for (var i = 0; i < 1000; i++) {
|
|
||||||
for (var j = 0; j < 100; j++) {
|
|
||||||
x += j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
beep();
|
|
||||||
|
|
||||||
} + ')()');
|
|
||||||
|
|
||||||
var counts = {};
|
|
||||||
|
|
||||||
b.on('node', function (node) {
|
|
||||||
if (!counts[node.id]) {
|
|
||||||
counts[node.id] = { times : 0, node : node, elapsed : 0 };
|
|
||||||
}
|
|
||||||
counts[node.id].times ++;
|
|
||||||
|
|
||||||
var now = Date.now();
|
|
||||||
|
|
||||||
if (last.id !== undefined) {
|
|
||||||
counts[last.id].elapsed += last.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.name === 'call') {
|
|
||||||
var start = now;
|
|
||||||
|
|
||||||
last.id = node.id;
|
|
||||||
counts[node.id].elapsed += Date.now() - start;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
counts[node.id].elapsed += now - last;
|
|
||||||
last = now;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
b.run();
|
|
||||||
|
|
||||||
Object.keys(counts).forEach(function (key) {
|
|
||||||
var count = counts[key];
|
|
||||||
console.log(
|
|
||||||
[ count.times, count.node.source(), count.elapsed ]
|
|
||||||
.join(' : ')
|
|
||||||
);
|
|
||||||
});
|
|
18
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/example/tiny.js
generated
vendored
18
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/example/tiny.js
generated
vendored
|
@ -1,18 +0,0 @@
|
||||||
var bunker = require('bunker');
|
|
||||||
var b = bunker('var x = 0; for (var i = 0; i < 30; i++) { x++ }');
|
|
||||||
|
|
||||||
var counts = {};
|
|
||||||
|
|
||||||
b.on('node', function (node) {
|
|
||||||
if (!counts[node.id]) {
|
|
||||||
counts[node.id] = { times : 0, node : node };
|
|
||||||
}
|
|
||||||
counts[node.id].times ++;
|
|
||||||
});
|
|
||||||
|
|
||||||
b.run();
|
|
||||||
|
|
||||||
Object.keys(counts).forEach(function (key) {
|
|
||||||
var count = counts[key];
|
|
||||||
console.log(count.times + ' : ' + count.node.source());
|
|
||||||
});
|
|
31
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/example/top/run.js
generated
vendored
31
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/example/top/run.js
generated
vendored
|
@ -1,31 +0,0 @@
|
||||||
var bunker = require('bunker');
|
|
||||||
var fs = require('fs');
|
|
||||||
var src = fs.readFileSync(__dirname + '/src.js', 'utf8');
|
|
||||||
|
|
||||||
var counts = {};
|
|
||||||
|
|
||||||
var b = bunker(src);
|
|
||||||
b.on('node', function (node) {
|
|
||||||
if (!counts[node.id]) {
|
|
||||||
counts[node.id] = { times : 0, node : node };
|
|
||||||
}
|
|
||||||
counts[node.id].times ++;
|
|
||||||
});
|
|
||||||
|
|
||||||
b.run({
|
|
||||||
setInterval : setInterval,
|
|
||||||
clearInterval : clearInterval,
|
|
||||||
end : function () {
|
|
||||||
Object.keys(counts)
|
|
||||||
.sort(function (a, b) {
|
|
||||||
return counts[b].times - counts[a].times
|
|
||||||
})
|
|
||||||
.forEach(function (key) {
|
|
||||||
var count = counts[key];
|
|
||||||
console.log(
|
|
||||||
count.times + ' : ' + count.node.source()
|
|
||||||
);
|
|
||||||
})
|
|
||||||
;
|
|
||||||
}
|
|
||||||
});
|
|
18
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/example/top/src.js
generated
vendored
18
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/example/top/src.js
generated
vendored
|
@ -1,18 +0,0 @@
|
||||||
function boop () {
|
|
||||||
for (var i = 0; i < 30; i++) {
|
|
||||||
nop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function nop () {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
var times = 0;
|
|
||||||
var iv = setInterval(function () {
|
|
||||||
if (++times === 10) {
|
|
||||||
clearInterval(iv);
|
|
||||||
end();
|
|
||||||
}
|
|
||||||
else boop()
|
|
||||||
}, 100);
|
|
116
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/index.js
generated
vendored
116
node_modules/merge/tests/qunit/node_modules/qunit/node_modules/bunker/index.js
generated
vendored
|
@ -1,116 +0,0 @@
|
||||||
var burrito = require('burrito');
|
|
||||||
var vm = require('vm');
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
|
||||||
|
|
||||||
module.exports = function (src) {
|
|
||||||
var b = new Bunker();
|
|
||||||
if (src) b.include(src);
|
|
||||||
return b;
|
|
||||||
};
|
|
||||||
|
|
||||||
function Bunker () {
|
|
||||||
this.sources = [];
|
|
||||||
this.nodes = [];
|
|
||||||
|
|
||||||
this.names = {
|
|
||||||
call : burrito.generateName(6),
|
|
||||||
expr : burrito.generateName(6),
|
|
||||||
stat : burrito.generateName(6),
|
|
||||||
return : burrito.generateName(6)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Bunker.prototype = new EventEmitter;
|
|
||||||
|
|
||||||
Bunker.prototype.include = function (src) {
|
|
||||||
this.sources.push(src);
|
|
||||||
this.source = null;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
Bunker.prototype.compile = function () {
|
|
||||||
var src = this.sources.join('\n');
|
|
||||||
var nodes = this.nodes;
|
|
||||||
var names = this.names;
|
|
||||||
|
|
||||||
return burrito(src, function (node) {
|
|
||||||
var i = nodes.length;
|
|
||||||
|
|
||||||
if (node.name === 'call') {
|
|
||||||
nodes.push(node);
|
|
||||||
node.wrap(names.call + '(' + i + ')(%s)');
|
|
||||||
}
|
|
||||||
else if (node.name === 'stat' || node.name === 'throw'
|
|
||||||
|| node.name === 'var') {
|
|
||||||
nodes.push(node);
|
|
||||||
node.wrap('{' + names.stat + '(' + i + ');%s}');
|
|
||||||
}
|
|
||||||
else if (node.name === 'return') {
|
|
||||||
nodes.push(node);
|
|
||||||
// We need to wrap the new source in a function definition
|
|
||||||
// so that UglifyJS will allow the presence of return
|
|
||||||
var stat = names.stat + '(' + i + ');';
|
|
||||||
var wrapped = 'function ' + names.return + '() {'
|
|
||||||
+ stat + node.source()
|
|
||||||
+'}'
|
|
||||||
;
|
|
||||||
var parsed = burrito.parse(wrapped);
|
|
||||||
// Remove the function definition from the AST
|
|
||||||
parsed[1] = parsed[1][0][3];
|
|
||||||
node.state.update(parsed, true);
|
|
||||||
}
|
|
||||||
else if (node.name === 'binary') {
|
|
||||||
nodes.push(node);
|
|
||||||
node.wrap(names.expr + '(' + i + ')(%s)');
|
|
||||||
}
|
|
||||||
else if (node.name === 'unary-postfix' || node.name === 'unary-prefix') {
|
|
||||||
nodes.push(node);
|
|
||||||
node.wrap(names.expr + '(' + i + ')(%s)');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i !== nodes.length) {
|
|
||||||
node.id = i;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Bunker.prototype.assign = function (context) {
|
|
||||||
if (!context) context = {};
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
var stack = [];
|
|
||||||
|
|
||||||
context[self.names.call] = function (i) {
|
|
||||||
var node = self.nodes[i];
|
|
||||||
stack.unshift(node);
|
|
||||||
self.emit('node', node, stack);
|
|
||||||
|
|
||||||
return function (expr) {
|
|
||||||
stack.shift();
|
|
||||||
return expr;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
context[self.names.expr] = function (i) {
|
|
||||||
var node = self.nodes[i];
|
|
||||||
self.emit('node', node, stack);
|
|
||||||
|
|
||||||
return function (expr) {
|
|
||||||
return expr;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
context[self.names.stat] = function (i) {
|
|
||||||
var node = self.nodes[i];
|
|
||||||
self.emit('node', node, stack);
|
|
||||||
};
|
|
||||||
|
|
||||||
return context;
|
|
||||||
};
|
|
||||||
|
|
||||||
Bunker.prototype.run = function (context) {
|
|
||||||
var src = this.compile();
|
|
||||||
vm.runInNewContext(src, this.assign(context));
|
|
||||||
|
|
||||||
return this;
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
node_modules
|
|
|
@ -1,4 +0,0 @@
|
||||||
language: node_js
|
|
||||||
node_js:
|
|
||||||
- 0.4
|
|
||||||
- 0.6
|
|
|
@ -1,187 +0,0 @@
|
||||||
burrito
|
|
||||||
=======
|
|
||||||
|
|
||||||
Burrito makes it easy to do crazy stuff with the javascript AST.
|
|
||||||
|
|
||||||
This is super useful if you want to roll your own stack traces or build a code
|
|
||||||
coverage tool.
|
|
||||||
|
|
||||||
[![build status](https://secure.travis-ci.org/substack/node-burrito.png)](http://travis-ci.org/substack/node-burrito)
|
|
||||||
|
|
||||||
![node.wrap("burrito")](http://substack.net/images/burrito.png)
|
|
||||||
|
|
||||||
examples
|
|
||||||
========
|
|
||||||
|
|
||||||
microwave
|
|
||||||
---------
|
|
||||||
|
|
||||||
examples/microwave.js
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
var burrito = require('burrito');
|
|
||||||
|
|
||||||
var res = burrito.microwave('Math.sin(2)', function (node) {
|
|
||||||
if (node.name === 'num') node.wrap('Math.PI / %s');
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(res); // sin(pi / 2) == 1
|
|
||||||
````
|
|
||||||
|
|
||||||
output:
|
|
||||||
|
|
||||||
1
|
|
||||||
|
|
||||||
wrap
|
|
||||||
----
|
|
||||||
|
|
||||||
examples/wrap.js
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
var burrito = require('burrito');
|
|
||||||
|
|
||||||
var src = burrito('f() && g(h())\nfoo()', function (node) {
|
|
||||||
if (node.name === 'call') node.wrap('qqq(%s)');
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(src);
|
|
||||||
````
|
|
||||||
|
|
||||||
output:
|
|
||||||
|
|
||||||
qqq(f()) && qqq(g(qqq(h())));
|
|
||||||
|
|
||||||
qqq(foo());
|
|
||||||
|
|
||||||
methods
|
|
||||||
=======
|
|
||||||
|
|
||||||
var burrito = require('burrito');
|
|
||||||
|
|
||||||
burrito(code, cb)
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Given some source `code` and a function `trace`, walk the ast by expression.
|
|
||||||
|
|
||||||
The `cb` gets called with a node object described below.
|
|
||||||
|
|
||||||
If `code` is an Array then it is assumbed to be an AST which you can generate
|
|
||||||
yourself with `burrito.parse()`. The AST must be annotated, so make sure to
|
|
||||||
`burrito.parse(src, false, true)`.
|
|
||||||
|
|
||||||
burrito.microwave(code, context={}, cb)
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
Like `burrito()` except the result is run using
|
|
||||||
`vm.runInNewContext(res, context)`.
|
|
||||||
|
|
||||||
node object
|
|
||||||
===========
|
|
||||||
|
|
||||||
node.name
|
|
||||||
---------
|
|
||||||
|
|
||||||
Name is a string that contains the type of the expression as named by uglify.
|
|
||||||
|
|
||||||
node.wrap(s)
|
|
||||||
------------
|
|
||||||
|
|
||||||
Wrap the current expression in `s`.
|
|
||||||
|
|
||||||
If `s` is a string, `"%s"` will be replaced with the stringified current
|
|
||||||
expression.
|
|
||||||
|
|
||||||
If `s` is a function, it is called with the stringified current expression and
|
|
||||||
should return a new stringified expression.
|
|
||||||
|
|
||||||
If the `node.name === "binary"`, you get the subterms "%a" and "%b" to play with
|
|
||||||
too. These subterms are applied if `s` is a function too: `s(expr, a, b)`.
|
|
||||||
|
|
||||||
Protip: to insert multiple statements you can use javascript's lesser-known block
|
|
||||||
syntax that it gets from C:
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
if (node.name === 'stat') node.wrap('{ foo(); %s }')
|
|
||||||
````
|
|
||||||
|
|
||||||
node.node
|
|
||||||
---------
|
|
||||||
|
|
||||||
raw ast data generated by uglify
|
|
||||||
|
|
||||||
node.value
|
|
||||||
----------
|
|
||||||
|
|
||||||
`node.node.slice(1)` to skip the annotations
|
|
||||||
|
|
||||||
node.start
|
|
||||||
----------
|
|
||||||
|
|
||||||
The start location of the expression, like this:
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
{ type: 'name',
|
|
||||||
value: 'b',
|
|
||||||
line: 0,
|
|
||||||
col: 3,
|
|
||||||
pos: 3,
|
|
||||||
nlb: false,
|
|
||||||
comments_before: [] }
|
|
||||||
````
|
|
||||||
|
|
||||||
node.end
|
|
||||||
--------
|
|
||||||
|
|
||||||
The end location of the expression, formatted the same as `node.start`.
|
|
||||||
|
|
||||||
node.state
|
|
||||||
----------
|
|
||||||
|
|
||||||
The state of the traversal using traverse.
|
|
||||||
|
|
||||||
node.source()
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Returns a stringified version of the expression.
|
|
||||||
|
|
||||||
node.parent()
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Returns the parent `node` or `null` if the node is the root element.
|
|
||||||
|
|
||||||
node.label()
|
|
||||||
------------
|
|
||||||
|
|
||||||
Return the label of the present node or `null` if there is no label.
|
|
||||||
|
|
||||||
Labels are returned for "call", "var", "defun", and "function" nodes.
|
|
||||||
|
|
||||||
Returns an array for "var" nodes since `var` statements can
|
|
||||||
contain multiple labels in assignment.
|
|
||||||
|
|
||||||
install
|
|
||||||
=======
|
|
||||||
|
|
||||||
With [npm](http://npmjs.org) you can just:
|
|
||||||
|
|
||||||
npm install burrito
|
|
||||||
|
|
||||||
in the browser
|
|
||||||
==============
|
|
||||||
|
|
||||||
Burrito works in browser with
|
|
||||||
[browserify](https://github.com/substack/node-browserify).
|
|
||||||
|
|
||||||
It has been tested against:
|
|
||||||
|
|
||||||
* Internet Explorer 5.5, 6.0, 7.0, 8.0, 9.0
|
|
||||||
* Firefox 3.5
|
|
||||||
* Chrome 6.0
|
|
||||||
* Opera 10.6
|
|
||||||
* Safari 5.0
|
|
||||||
|
|
||||||
kudos
|
|
||||||
=====
|
|
||||||
|
|
||||||
Heavily inspired by (and previously mostly lifted outright from) isaacs's nifty
|
|
||||||
tmp/instrument.js thingy from uglify-js.
|
|
|
@ -1,8 +0,0 @@
|
||||||
var burrito = require('burrito');
|
|
||||||
|
|
||||||
var res = burrito.microwave('Math.sin(2)', function (node) {
|
|
||||||
console.dir(node);
|
|
||||||
if (node.name === 'num') node.wrap('Math.PI / %s');
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(res); // sin(pi / 2) == 1
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,14 +0,0 @@
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<script type="text/javascript" src="/browserify.js"></script>
|
|
||||||
<style type="text/css">
|
|
||||||
pre {
|
|
||||||
white-space: pre;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<pre id="output"></pre>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,17 +0,0 @@
|
||||||
var burrito = require('burrito');
|
|
||||||
var json = require('jsonify');
|
|
||||||
|
|
||||||
var src = [
|
|
||||||
'function f () { g() }',
|
|
||||||
'function g () { h() }',
|
|
||||||
'function h () { throw "moo" + Array(x).join("!") }',
|
|
||||||
'var x = 4',
|
|
||||||
'f()'
|
|
||||||
].join('\r\n');
|
|
||||||
|
|
||||||
window.onload = function () {
|
|
||||||
burrito(src, function (node) {
|
|
||||||
document.body.innerHTML += node.name + '<br>\n';
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (document.readyState === 'complete') window.onload();
|
|
|
@ -1,12 +0,0 @@
|
||||||
var express = require('express');
|
|
||||||
var browserify = require('browserify');
|
|
||||||
|
|
||||||
var app = express.createServer();
|
|
||||||
app.use(express.static(__dirname));
|
|
||||||
app.use(browserify({
|
|
||||||
entry : __dirname + '/main.js',
|
|
||||||
watch : true,
|
|
||||||
}));
|
|
||||||
|
|
||||||
app.listen(8081);
|
|
||||||
console.log('Listening on :8081');
|
|
|
@ -1,7 +0,0 @@
|
||||||
var burrito = require('burrito');
|
|
||||||
|
|
||||||
var src = burrito('f() && g(h())\nfoo()', function (node) {
|
|
||||||
if (node.name === 'call') node.wrap('qqq(%s)');
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(src);
|
|
|
@ -1,208 +0,0 @@
|
||||||
var uglify = require('uglify-js');
|
|
||||||
var parser = uglify.parser;
|
|
||||||
var parse = function (expr) {
|
|
||||||
if (typeof expr !== 'string') throw 'expression should be a string';
|
|
||||||
|
|
||||||
try {
|
|
||||||
var ast = parser.parse.apply(null, arguments);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
if (err.message === undefined
|
|
||||||
|| err.line === undefined
|
|
||||||
|| err.col === undefined
|
|
||||||
|| err.pos === undefined
|
|
||||||
) { throw err }
|
|
||||||
|
|
||||||
var e = new SyntaxError(
|
|
||||||
err.message
|
|
||||||
+ '\n at line ' + err.line + ':' + err.col + ' in expression:\n\n'
|
|
||||||
+ ' ' + expr.split(/\r?\n/)[err.line]
|
|
||||||
);
|
|
||||||
|
|
||||||
e.original = err;
|
|
||||||
e.line = err.line;
|
|
||||||
e.col = err.col;
|
|
||||||
e.pos = err.pos;
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
return ast;
|
|
||||||
};
|
|
||||||
|
|
||||||
var deparse = function (ast, b) {
|
|
||||||
return uglify.uglify.gen_code(ast, { beautify : b });
|
|
||||||
};
|
|
||||||
|
|
||||||
var traverse = require('traverse');
|
|
||||||
var vm = require('vm');
|
|
||||||
|
|
||||||
var burrito = module.exports = function (code, cb) {
|
|
||||||
var ast = Array_isArray(code)
|
|
||||||
? code // already an ast
|
|
||||||
: parse(code.toString(), false, true)
|
|
||||||
;
|
|
||||||
|
|
||||||
var ast_ = traverse(ast).map(function mapper () {
|
|
||||||
wrapNode(this, cb);
|
|
||||||
});
|
|
||||||
|
|
||||||
return deparse(parse(deparse(ast_)), true);
|
|
||||||
};
|
|
||||||
|
|
||||||
var wrapNode = burrito.wrapNode = function (state, cb) {
|
|
||||||
var node = state.node;
|
|
||||||
|
|
||||||
var ann = Array_isArray(node) && node[0]
|
|
||||||
&& typeof node[0] === 'object' && node[0].name
|
|
||||||
? node[0]
|
|
||||||
: null
|
|
||||||
;
|
|
||||||
|
|
||||||
if (!ann) return undefined;
|
|
||||||
|
|
||||||
var self = {
|
|
||||||
name : ann.name,
|
|
||||||
node : node,
|
|
||||||
start : node[0].start,
|
|
||||||
end : node[0].end,
|
|
||||||
value : node.slice(1),
|
|
||||||
state : state
|
|
||||||
};
|
|
||||||
|
|
||||||
self.wrap = function (s) {
|
|
||||||
var subsrc = deparse(
|
|
||||||
traverse(node).map(function (x) {
|
|
||||||
if (!this.isRoot) wrapNode(this, cb)
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
if (self.name === 'binary') {
|
|
||||||
var a = deparse(traverse(node[2]).map(function (x) {
|
|
||||||
if (!this.isRoot) wrapNode(this, cb)
|
|
||||||
}));
|
|
||||||
var b = deparse(traverse(node[3]).map(function (x) {
|
|
||||||
if (!this.isRoot) wrapNode(this, cb)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
var src = '';
|
|
||||||
|
|
||||||
if (typeof s === 'function') {
|
|
||||||
if (self.name === 'binary') {
|
|
||||||
src = s(subsrc, a, b);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
src = s(subsrc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
src = s.toString()
|
|
||||||
.replace(/%s/g, function () {
|
|
||||||
return subsrc
|
|
||||||
})
|
|
||||||
;
|
|
||||||
|
|
||||||
if (self.name === 'binary') {
|
|
||||||
src = src
|
|
||||||
.replace(/%a/g, function () { return a })
|
|
||||||
.replace(/%b/g, function () { return b })
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var expr = parse(src);
|
|
||||||
state.update(expr, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
var cache = {};
|
|
||||||
|
|
||||||
self.parent = state.isRoot ? null : function () {
|
|
||||||
if (!cache.parent) {
|
|
||||||
var s = state;
|
|
||||||
var x;
|
|
||||||
do {
|
|
||||||
s = s.parent;
|
|
||||||
if (s) x = wrapNode(s);
|
|
||||||
} while (s && !x);
|
|
||||||
|
|
||||||
cache.parent = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
return cache.parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.source = function () {
|
|
||||||
if (!cache.source) cache.source = deparse(node);
|
|
||||||
return cache.source;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.label = function () {
|
|
||||||
return burrito.label(self);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (cb) cb.call(state, self);
|
|
||||||
|
|
||||||
if (self.node[0].name === 'conditional') {
|
|
||||||
self.wrap('[%s][0]');
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
burrito.microwave = function (code, context, cb) {
|
|
||||||
if (!cb) { cb = context; context = {} };
|
|
||||||
if (!context) context = {};
|
|
||||||
|
|
||||||
var src = burrito(code, cb);
|
|
||||||
return vm.runInNewContext(src, context);
|
|
||||||
};
|
|
||||||
|
|
||||||
burrito.generateName = function (len) {
|
|
||||||
var name = '';
|
|
||||||
var lower = '$'.charCodeAt(0);
|
|
||||||
var upper = 'z'.charCodeAt(0);
|
|
||||||
|
|
||||||
while (name.length < len) {
|
|
||||||
var c = String.fromCharCode(Math.floor(
|
|
||||||
Math.random() * (upper - lower + 1) + lower
|
|
||||||
));
|
|
||||||
if ((name + c).match(/^[A-Za-z_$][A-Za-z0-9_$]*$/)) name += c;
|
|
||||||
}
|
|
||||||
|
|
||||||
return name;
|
|
||||||
};
|
|
||||||
|
|
||||||
burrito.parse = parse;
|
|
||||||
burrito.deparse = deparse;
|
|
||||||
|
|
||||||
burrito.label = function (node) {
|
|
||||||
if (node.name === 'call') {
|
|
||||||
if (typeof node.value[0] === 'string') {
|
|
||||||
return node.value[0];
|
|
||||||
}
|
|
||||||
else if (node.value[0] && typeof node.value[0][1] === 'string') {
|
|
||||||
return node.value[0][1];
|
|
||||||
}
|
|
||||||
else if (node.value[0][0] === 'dot') {
|
|
||||||
return node.value[0][node.value[0].length - 1];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (node.name === 'var') {
|
|
||||||
return node.value[0].map(function (x) { return x[0] });
|
|
||||||
}
|
|
||||||
else if (node.name === 'defun') {
|
|
||||||
return node.value[0];
|
|
||||||
}
|
|
||||||
else if (node.name === 'function') {
|
|
||||||
return node.value[0];
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var Array_isArray = Array.isArray || function isArray (xs) {
|
|
||||||
return Object.prototype.toString.call(xs) === '[object Array]';
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
node_modules
|
|
|
@ -1,24 +0,0 @@
|
||||||
Copyright 2010 James Halliday (mail@substack.net)
|
|
||||||
|
|
||||||
This project is free software released under the MIT/X11 license:
|
|
||||||
http://www.opensource.org/licenses/mit-license.php
|
|
||||||
|
|
||||||
Copyright 2010 James Halliday (mail@substack.net)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
|
@ -1,237 +0,0 @@
|
||||||
traverse
|
|
||||||
========
|
|
||||||
|
|
||||||
Traverse and transform objects by visiting every node on a recursive walk.
|
|
||||||
|
|
||||||
examples
|
|
||||||
========
|
|
||||||
|
|
||||||
transform negative numbers in-place
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
negative.js
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
var traverse = require('traverse');
|
|
||||||
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
|
|
||||||
|
|
||||||
traverse(obj).forEach(function (x) {
|
|
||||||
if (x < 0) this.update(x + 128);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.dir(obj);
|
|
||||||
````
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
[ 5, 6, 125, [ 7, 8, 126, 1 ], { f: 10, g: 115 } ]
|
|
||||||
|
|
||||||
collect leaf nodes
|
|
||||||
------------------
|
|
||||||
|
|
||||||
leaves.js
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
var traverse = require('traverse');
|
|
||||||
|
|
||||||
var obj = {
|
|
||||||
a : [1,2,3],
|
|
||||||
b : 4,
|
|
||||||
c : [5,6],
|
|
||||||
d : { e : [7,8], f : 9 },
|
|
||||||
};
|
|
||||||
|
|
||||||
var leaves = traverse(obj).reduce(function (acc, x) {
|
|
||||||
if (this.isLeaf) acc.push(x);
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
console.dir(leaves);
|
|
||||||
````
|
|
||||||
|
|
||||||
Output:
|
|
||||||
|
|
||||||
[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
|
|
||||||
|
|
||||||
scrub circular references
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
scrub.js:
|
|
||||||
|
|
||||||
````javascript
|
|
||||||
var traverse = require('traverse');
|
|
||||||
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
obj.c.push(obj);
|
|
||||||
|
|
||||||
var scrubbed = traverse(obj).map(function (x) {
|
|
||||||
if (this.circular) this.remove()
|
|
||||||
});
|
|
||||||
console.dir(scrubbed);
|
|
||||||
````
|
|
||||||
|
|
||||||
output:
|
|
||||||
|
|
||||||
{ a: 1, b: 2, c: [ 3, 4 ] }
|
|
||||||
|
|
||||||
context
|
|
||||||
=======
|
|
||||||
|
|
||||||
Each method that takes a callback has a context (its `this` object) with these
|
|
||||||
attributes:
|
|
||||||
|
|
||||||
this.node
|
|
||||||
---------
|
|
||||||
|
|
||||||
The present node on the recursive walk
|
|
||||||
|
|
||||||
this.path
|
|
||||||
---------
|
|
||||||
|
|
||||||
An array of string keys from the root to the present node
|
|
||||||
|
|
||||||
this.parent
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The context of the node's parent.
|
|
||||||
This is `undefined` for the root node.
|
|
||||||
|
|
||||||
this.key
|
|
||||||
--------
|
|
||||||
|
|
||||||
The name of the key of the present node in its parent.
|
|
||||||
This is `undefined` for the root node.
|
|
||||||
|
|
||||||
this.isRoot, this.notRoot
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Whether the present node is the root node
|
|
||||||
|
|
||||||
this.isLeaf, this.notLeaf
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Whether or not the present node is a leaf node (has no children)
|
|
||||||
|
|
||||||
this.level
|
|
||||||
----------
|
|
||||||
|
|
||||||
Depth of the node within the traversal
|
|
||||||
|
|
||||||
this.circular
|
|
||||||
-------------
|
|
||||||
|
|
||||||
If the node equals one of its parents, the `circular` attribute is set to the
|
|
||||||
context of that parent and the traversal progresses no deeper.
|
|
||||||
|
|
||||||
this.update(value, stopHere=false)
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
Set a new value for the present node.
|
|
||||||
|
|
||||||
All the elements in `value` will be recursively traversed unless `stopHere` is
|
|
||||||
true.
|
|
||||||
|
|
||||||
this.remove(stopHere=false)
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Remove the current element from the output. If the node is in an Array it will
|
|
||||||
be spliced off. Otherwise it will be deleted from its parent.
|
|
||||||
|
|
||||||
this.delete(stopHere=false)
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Delete the current element from its parent in the output. Calls `delete` even on
|
|
||||||
Arrays.
|
|
||||||
|
|
||||||
this.before(fn)
|
|
||||||
---------------
|
|
||||||
|
|
||||||
Call this function before any of the children are traversed.
|
|
||||||
|
|
||||||
You can assign into `this.keys` here to traverse in a custom order.
|
|
||||||
|
|
||||||
this.after(fn)
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Call this function after any of the children are traversed.
|
|
||||||
|
|
||||||
this.pre(fn)
|
|
||||||
------------
|
|
||||||
|
|
||||||
Call this function before each of the children are traversed.
|
|
||||||
|
|
||||||
this.post(fn)
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Call this function after each of the children are traversed.
|
|
||||||
|
|
||||||
methods
|
|
||||||
=======
|
|
||||||
|
|
||||||
.map(fn)
|
|
||||||
--------
|
|
||||||
|
|
||||||
Execute `fn` for each node in the object and return a new object with the
|
|
||||||
results of the walk. To update nodes in the result use `this.update(value)`.
|
|
||||||
|
|
||||||
.forEach(fn)
|
|
||||||
------------
|
|
||||||
|
|
||||||
Execute `fn` for each node in the object but unlike `.map()`, when
|
|
||||||
`this.update()` is called it updates the object in-place.
|
|
||||||
|
|
||||||
.reduce(fn, acc)
|
|
||||||
----------------
|
|
||||||
|
|
||||||
For each node in the object, perform a
|
|
||||||
[left-fold](http://en.wikipedia.org/wiki/Fold_(higher-order_function))
|
|
||||||
with the return value of `fn(acc, node)`.
|
|
||||||
|
|
||||||
If `acc` isn't specified, `acc` is set to the root object for the first step
|
|
||||||
and the root element is skipped.
|
|
||||||
|
|
||||||
.paths()
|
|
||||||
--------
|
|
||||||
|
|
||||||
Return an `Array` of every possible non-cyclic path in the object.
|
|
||||||
Paths are `Array`s of string keys.
|
|
||||||
|
|
||||||
.nodes()
|
|
||||||
--------
|
|
||||||
|
|
||||||
Return an `Array` of every node in the object.
|
|
||||||
|
|
||||||
.clone()
|
|
||||||
--------
|
|
||||||
|
|
||||||
Create a deep clone of the object.
|
|
||||||
|
|
||||||
install
|
|
||||||
=======
|
|
||||||
|
|
||||||
Using [npm](http://npmjs.org) do:
|
|
||||||
|
|
||||||
$ npm install traverse
|
|
||||||
|
|
||||||
test
|
|
||||||
====
|
|
||||||
|
|
||||||
Using [expresso](http://github.com/visionmedia/expresso) do:
|
|
||||||
|
|
||||||
$ expresso
|
|
||||||
|
|
||||||
100% wahoo, your stuff is not broken!
|
|
||||||
|
|
||||||
in the browser
|
|
||||||
==============
|
|
||||||
|
|
||||||
Use [browserify](https://github.com/substack/node-browserify) to run traverse in
|
|
||||||
the browser.
|
|
||||||
|
|
||||||
traverse has been tested and works with:
|
|
||||||
|
|
||||||
* Internet Explorer 5.5, 6.0, 7.0, 8.0, 9.0
|
|
||||||
* Firefox 3.5
|
|
||||||
* Chrome 6.0
|
|
||||||
* Opera 10.6
|
|
||||||
* Safari 5.0
|
|
|
@ -1,16 +0,0 @@
|
||||||
var traverse = require('traverse');
|
|
||||||
|
|
||||||
var id = 54;
|
|
||||||
var callbacks = {};
|
|
||||||
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };
|
|
||||||
|
|
||||||
var scrubbed = traverse(obj).map(function (x) {
|
|
||||||
if (typeof x === 'function') {
|
|
||||||
callbacks[id] = { id : id, f : x, path : this.path };
|
|
||||||
this.update('[Function]');
|
|
||||||
id++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.dir(scrubbed);
|
|
||||||
console.dir(callbacks);
|
|
|
@ -1,15 +0,0 @@
|
||||||
var traverse = require('traverse');
|
|
||||||
|
|
||||||
var obj = {
|
|
||||||
a : [1,2,3],
|
|
||||||
b : 4,
|
|
||||||
c : [5,6],
|
|
||||||
d : { e : [7,8], f : 9 },
|
|
||||||
};
|
|
||||||
|
|
||||||
var leaves = traverse(obj).reduce(function (acc, x) {
|
|
||||||
if (this.isLeaf) acc.push(x);
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
console.dir(leaves);
|
|
|
@ -1,8 +0,0 @@
|
||||||
var traverse = require('traverse');
|
|
||||||
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
|
|
||||||
|
|
||||||
traverse(obj).forEach(function (x) {
|
|
||||||
if (x < 0) this.update(x + 128);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.dir(obj);
|
|
|
@ -1,10 +0,0 @@
|
||||||
// scrub out circular references
|
|
||||||
var traverse = require('traverse');
|
|
||||||
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
obj.c.push(obj);
|
|
||||||
|
|
||||||
var scrubbed = traverse(obj).map(function (x) {
|
|
||||||
if (this.circular) this.remove()
|
|
||||||
});
|
|
||||||
console.dir(scrubbed);
|
|
|
@ -1,38 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
var traverse = require('traverse');
|
|
||||||
|
|
||||||
var obj = [ 'five', 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
|
|
||||||
|
|
||||||
var s = '';
|
|
||||||
traverse(obj).forEach(function to_s (node) {
|
|
||||||
if (Array.isArray(node)) {
|
|
||||||
this.before(function () { s += '[' });
|
|
||||||
this.post(function (child) {
|
|
||||||
if (!child.isLast) s += ',';
|
|
||||||
});
|
|
||||||
this.after(function () { s += ']' });
|
|
||||||
}
|
|
||||||
else if (typeof node == 'object') {
|
|
||||||
this.before(function () { s += '{' });
|
|
||||||
this.pre(function (x, key) {
|
|
||||||
to_s(key);
|
|
||||||
s += ':';
|
|
||||||
});
|
|
||||||
this.post(function (child) {
|
|
||||||
if (!child.isLast) s += ',';
|
|
||||||
});
|
|
||||||
this.after(function () { s += '}' });
|
|
||||||
}
|
|
||||||
else if (typeof node == 'string') {
|
|
||||||
s += '"' + node.toString().replace(/"/g, '\\"') + '"';
|
|
||||||
}
|
|
||||||
else if (typeof node == 'function') {
|
|
||||||
s += 'null';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
s += node.toString();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('JSON.stringify: ' + JSON.stringify(obj));
|
|
||||||
console.log('this stringify: ' + s);
|
|
|
@ -1,267 +0,0 @@
|
||||||
module.exports = Traverse;
|
|
||||||
function Traverse (obj) {
|
|
||||||
if (!(this instanceof Traverse)) return new Traverse(obj);
|
|
||||||
this.value = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
Traverse.prototype.get = function (ps) {
|
|
||||||
var node = this.value;
|
|
||||||
for (var i = 0; i < ps.length; i ++) {
|
|
||||||
var key = ps[i];
|
|
||||||
if (!Object.hasOwnProperty.call(node, key)) {
|
|
||||||
node = undefined;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
node = node[key];
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
};
|
|
||||||
|
|
||||||
Traverse.prototype.set = function (ps, value) {
|
|
||||||
var node = this.value;
|
|
||||||
for (var i = 0; i < ps.length - 1; i ++) {
|
|
||||||
var key = ps[i];
|
|
||||||
if (!Object.hasOwnProperty.call(node, key)) node[key] = {};
|
|
||||||
node = node[key];
|
|
||||||
}
|
|
||||||
node[ps[i]] = value;
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
Traverse.prototype.map = function (cb) {
|
|
||||||
return walk(this.value, cb, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
Traverse.prototype.forEach = function (cb) {
|
|
||||||
this.value = walk(this.value, cb, false);
|
|
||||||
return this.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
Traverse.prototype.reduce = function (cb, init) {
|
|
||||||
var skip = arguments.length === 1;
|
|
||||||
var acc = skip ? this.value : init;
|
|
||||||
this.forEach(function (x) {
|
|
||||||
if (!this.isRoot || !skip) {
|
|
||||||
acc = cb.call(this, acc, x);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return acc;
|
|
||||||
};
|
|
||||||
|
|
||||||
Traverse.prototype.paths = function () {
|
|
||||||
var acc = [];
|
|
||||||
this.forEach(function (x) {
|
|
||||||
acc.push(this.path);
|
|
||||||
});
|
|
||||||
return acc;
|
|
||||||
};
|
|
||||||
|
|
||||||
Traverse.prototype.nodes = function () {
|
|
||||||
var acc = [];
|
|
||||||
this.forEach(function (x) {
|
|
||||||
acc.push(this.node);
|
|
||||||
});
|
|
||||||
return acc;
|
|
||||||
};
|
|
||||||
|
|
||||||
Traverse.prototype.clone = function () {
|
|
||||||
var parents = [], nodes = [];
|
|
||||||
|
|
||||||
return (function clone (src) {
|
|
||||||
for (var i = 0; i < parents.length; i++) {
|
|
||||||
if (parents[i] === src) {
|
|
||||||
return nodes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof src === 'object' && src !== null) {
|
|
||||||
var dst = copy(src);
|
|
||||||
|
|
||||||
parents.push(src);
|
|
||||||
nodes.push(dst);
|
|
||||||
|
|
||||||
forEach(Object_keys(src), function (key) {
|
|
||||||
dst[key] = clone(src[key]);
|
|
||||||
});
|
|
||||||
|
|
||||||
parents.pop();
|
|
||||||
nodes.pop();
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
})(this.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
function walk (root, cb, immutable) {
|
|
||||||
var path = [];
|
|
||||||
var parents = [];
|
|
||||||
var alive = true;
|
|
||||||
|
|
||||||
return (function walker (node_) {
|
|
||||||
var node = immutable ? copy(node_) : node_;
|
|
||||||
var modifiers = {};
|
|
||||||
|
|
||||||
var keepGoing = true;
|
|
||||||
|
|
||||||
var state = {
|
|
||||||
node : node,
|
|
||||||
node_ : node_,
|
|
||||||
path : [].concat(path),
|
|
||||||
parent : parents[parents.length - 1],
|
|
||||||
parents : parents,
|
|
||||||
key : path.slice(-1)[0],
|
|
||||||
isRoot : path.length === 0,
|
|
||||||
level : path.length,
|
|
||||||
circular : null,
|
|
||||||
update : function (x, stopHere) {
|
|
||||||
if (!state.isRoot) {
|
|
||||||
state.parent.node[state.key] = x;
|
|
||||||
}
|
|
||||||
state.node = x;
|
|
||||||
if (stopHere) keepGoing = false;
|
|
||||||
},
|
|
||||||
'delete' : function (stopHere) {
|
|
||||||
delete state.parent.node[state.key];
|
|
||||||
if (stopHere) keepGoing = false;
|
|
||||||
},
|
|
||||||
remove : function (stopHere) {
|
|
||||||
if (Array_isArray(state.parent.node)) {
|
|
||||||
state.parent.node.splice(state.key, 1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
delete state.parent.node[state.key];
|
|
||||||
}
|
|
||||||
if (stopHere) keepGoing = false;
|
|
||||||
},
|
|
||||||
keys : null,
|
|
||||||
before : function (f) { modifiers.before = f },
|
|
||||||
after : function (f) { modifiers.after = f },
|
|
||||||
pre : function (f) { modifiers.pre = f },
|
|
||||||
post : function (f) { modifiers.post = f },
|
|
||||||
stop : function () { alive = false },
|
|
||||||
block : function () { keepGoing = false }
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!alive) return state;
|
|
||||||
|
|
||||||
if (typeof node === 'object' && node !== null) {
|
|
||||||
state.keys = Object_keys(node);
|
|
||||||
|
|
||||||
state.isLeaf = state.keys.length == 0;
|
|
||||||
|
|
||||||
for (var i = 0; i < parents.length; i++) {
|
|
||||||
if (parents[i].node_ === node_) {
|
|
||||||
state.circular = parents[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
state.isLeaf = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.notLeaf = !state.isLeaf;
|
|
||||||
state.notRoot = !state.isRoot;
|
|
||||||
|
|
||||||
// use return values to update if defined
|
|
||||||
var ret = cb.call(state, state.node);
|
|
||||||
if (ret !== undefined && state.update) state.update(ret);
|
|
||||||
|
|
||||||
if (modifiers.before) modifiers.before.call(state, state.node);
|
|
||||||
|
|
||||||
if (!keepGoing) return state;
|
|
||||||
|
|
||||||
if (typeof state.node == 'object'
|
|
||||||
&& state.node !== null && !state.circular) {
|
|
||||||
parents.push(state);
|
|
||||||
|
|
||||||
forEach(state.keys, function (key, i) {
|
|
||||||
path.push(key);
|
|
||||||
|
|
||||||
if (modifiers.pre) modifiers.pre.call(state, state.node[key], key);
|
|
||||||
|
|
||||||
var child = walker(state.node[key]);
|
|
||||||
if (immutable && Object.hasOwnProperty.call(state.node, key)) {
|
|
||||||
state.node[key] = child.node;
|
|
||||||
}
|
|
||||||
|
|
||||||
child.isLast = i == state.keys.length - 1;
|
|
||||||
child.isFirst = i == 0;
|
|
||||||
|
|
||||||
if (modifiers.post) modifiers.post.call(state, child);
|
|
||||||
|
|
||||||
path.pop();
|
|
||||||
});
|
|
||||||
parents.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (modifiers.after) modifiers.after.call(state, state.node);
|
|
||||||
|
|
||||||
return state;
|
|
||||||
})(root).node;
|
|
||||||
}
|
|
||||||
|
|
||||||
function copy (src) {
|
|
||||||
if (typeof src === 'object' && src !== null) {
|
|
||||||
var dst;
|
|
||||||
|
|
||||||
if (Array_isArray(src)) {
|
|
||||||
dst = [];
|
|
||||||
}
|
|
||||||
else if (src instanceof Date) {
|
|
||||||
dst = new Date(src);
|
|
||||||
}
|
|
||||||
else if (src instanceof Boolean) {
|
|
||||||
dst = new Boolean(src);
|
|
||||||
}
|
|
||||||
else if (src instanceof Number) {
|
|
||||||
dst = new Number(src);
|
|
||||||
}
|
|
||||||
else if (src instanceof String) {
|
|
||||||
dst = new String(src);
|
|
||||||
}
|
|
||||||
else if (Object.create && Object.getPrototypeOf) {
|
|
||||||
dst = Object.create(Object.getPrototypeOf(src));
|
|
||||||
}
|
|
||||||
else if (src.__proto__ || src.constructor.prototype) {
|
|
||||||
var proto = src.__proto__ || src.constructor.prototype || {};
|
|
||||||
var T = function () {};
|
|
||||||
T.prototype = proto;
|
|
||||||
dst = new T;
|
|
||||||
if (!dst.__proto__) dst.__proto__ = proto;
|
|
||||||
}
|
|
||||||
|
|
||||||
forEach(Object_keys(src), function (key) {
|
|
||||||
dst[key] = src[key];
|
|
||||||
});
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
else return src;
|
|
||||||
}
|
|
||||||
|
|
||||||
var Object_keys = Object.keys || function keys (obj) {
|
|
||||||
var res = [];
|
|
||||||
for (var key in obj) res.push(key)
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
var Array_isArray = Array.isArray || function isArray (xs) {
|
|
||||||
return Object.prototype.toString.call(xs) === '[object Array]';
|
|
||||||
};
|
|
||||||
|
|
||||||
var forEach = function (xs, fn) {
|
|
||||||
if (xs.forEach) return xs.forEach(fn)
|
|
||||||
else for (var i = 0; i < xs.length; i++) {
|
|
||||||
fn(xs[i], i, xs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
forEach(Object_keys(Traverse.prototype), function (key) {
|
|
||||||
Traverse[key] = function (obj) {
|
|
||||||
var args = [].slice.call(arguments, 1);
|
|
||||||
var t = Traverse(obj);
|
|
||||||
return t[key].apply(t, args);
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,10 +0,0 @@
|
||||||
// scrub out circular references
|
|
||||||
var traverse = require('./index.js');
|
|
||||||
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
obj.c.push(obj);
|
|
||||||
|
|
||||||
var scrubbed = traverse(obj).map(function (x) {
|
|
||||||
if (this.circular) this.remove()
|
|
||||||
});
|
|
||||||
console.dir(scrubbed);
|
|
|
@ -1,24 +0,0 @@
|
||||||
{
|
|
||||||
"name": "traverse",
|
|
||||||
"version": "0.5.2",
|
|
||||||
"description": "Traverse and transform objects by visiting every node on a recursive walk",
|
|
||||||
"author": {
|
|
||||||
"name": "James Halliday"
|
|
||||||
},
|
|
||||||
"license": "MIT/X11",
|
|
||||||
"main": "./index",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "http://github.com/substack/js-traverse.git"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"expresso": "0.7.x"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "expresso"
|
|
||||||
},
|
|
||||||
"readme": "traverse\n========\n\nTraverse and transform objects by visiting every node on a recursive walk.\n\nexamples\n========\n\ntransform negative numbers in-place\n-----------------------------------\n\nnegative.js\n\n````javascript\nvar traverse = require('traverse');\nvar obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];\n\ntraverse(obj).forEach(function (x) {\n if (x < 0) this.update(x + 128);\n});\n\nconsole.dir(obj);\n````\n\nOutput:\n\n [ 5, 6, 125, [ 7, 8, 126, 1 ], { f: 10, g: 115 } ]\n\ncollect leaf nodes\n------------------\n\nleaves.js\n\n````javascript\nvar traverse = require('traverse');\n\nvar obj = {\n a : [1,2,3],\n b : 4,\n c : [5,6],\n d : { e : [7,8], f : 9 },\n};\n\nvar leaves = traverse(obj).reduce(function (acc, x) {\n if (this.isLeaf) acc.push(x);\n return acc;\n}, []);\n\nconsole.dir(leaves);\n````\n\nOutput:\n\n [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]\n\nscrub circular references\n-------------------------\n\nscrub.js:\n\n````javascript\nvar traverse = require('traverse');\n\nvar obj = { a : 1, b : 2, c : [ 3, 4 ] };\nobj.c.push(obj);\n\nvar scrubbed = traverse(obj).map(function (x) {\n if (this.circular) this.remove()\n});\nconsole.dir(scrubbed);\n````\n\noutput:\n\n { a: 1, b: 2, c: [ 3, 4 ] }\n\ncontext\n=======\n\nEach method that takes a callback has a context (its `this` object) with these\nattributes:\n\nthis.node\n---------\n\nThe present node on the recursive walk\n\nthis.path\n---------\n\nAn array of string keys from the root to the present node\n\nthis.parent\n-----------\n\nThe context of the node's parent.\nThis is `undefined` for the root node.\n\nthis.key\n--------\n\nThe name of the key of the present node in its parent.\nThis is `undefined` for the root node.\n\nthis.isRoot, this.notRoot\n-------------------------\n\nWhether the present node is the root node\n\nthis.isLeaf, this.notLeaf\n-------------------------\n\nWhether or not the present node is a leaf node (has no children)\n\nthis.level\n----------\n\nDepth of the node within the traversal\n\nthis.circular\n-------------\n\nIf the node equals one of its parents, the `circular` attribute is set to the\ncontext of that parent and the traversal progresses no deeper.\n\nthis.update(value, stopHere=false)\n----------------------------------\n\nSet a new value for the present node.\n\nAll the elements in `value` will be recursively traversed unless `stopHere` is\ntrue.\n\nthis.remove(stopHere=false)\n-------------\n\nRemove the current element from the output. If the node is in an Array it will\nbe spliced off. Otherwise it will be deleted from its parent.\n\nthis.delete(stopHere=false)\n-------------\n\nDelete the current element from its parent in the output. Calls `delete` even on\nArrays.\n\nthis.before(fn)\n---------------\n\nCall this function before any of the children are traversed.\n\nYou can assign into `this.keys` here to traverse in a custom order.\n\nthis.after(fn)\n--------------\n\nCall this function after any of the children are traversed.\n\nthis.pre(fn)\n------------\n\nCall this function before each of the children are traversed.\n\nthis.post(fn)\n-------------\n\nCall this function after each of the children are traversed.\n\nmethods\n=======\n\n.map(fn)\n--------\n\nExecute `fn` for each node in the object and return a new object with the\nresults of the walk. To update nodes in the result use `this.update(value)`.\n\n.forEach(fn)\n------------\n\nExecute `fn` for each node in the object but unlike `.map()`, when\n`this.update()` is called it updates the object in-place.\n\n.reduce(fn, acc)\n----------------\n\nFor each node in the object, perform a\n[left-fold](http://en.wikipedia.org/wiki/Fold_(higher-order_function))\nwith the return value of `fn(acc, node)`.\n\nIf `acc` isn't specified, `acc` is set to the root object for the first step\nand the root element is skipped.\n\n.paths()\n--------\n\nReturn an `Array` of every possible non-cyclic path in the object.\nPaths are `Array`s of string keys.\n\n.nodes()\n--------\n\nReturn an `Array` of every node in the object.\n\n.clone()\n--------\n\nCreate a deep clone of the object.\n\ninstall\n=======\n\nUsing [npm](http://npmjs.org) do:\n\n $ npm install traverse\n\ntest\n====\n\nUsing [expresso](http://github.com/visionmedia/expresso) do:\n\n $ expresso\n \n 100% wahoo, your stuff is not broken!\n\nin the browser\n==============\n\nUse [browserify](https://github.com/substack/node-browserify) to run traverse in\nthe browser.\n\ntraverse has been tested and works with:\n\n* Internet Explorer 5.5, 6.0, 7.0, 8.0, 9.0\n* Firefox 3.5\n* Chrome 6.0\n* Opera 10.6\n* Safari 5.0\n",
|
|
||||||
"readmeFilename": "README.markdown",
|
|
||||||
"_id": "traverse@0.5.2",
|
|
||||||
"_from": "traverse@~0.5.1"
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var Traverse = require('../');
|
|
||||||
var deepEqual = require('./lib/deep_equal');
|
|
||||||
var util = require('util');
|
|
||||||
|
|
||||||
exports.circular = function () {
|
|
||||||
var obj = { x : 3 };
|
|
||||||
obj.y = obj;
|
|
||||||
var foundY = false;
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
if (this.path.join('') == 'y') {
|
|
||||||
assert.equal(
|
|
||||||
util.inspect(this.circular.node),
|
|
||||||
util.inspect(obj)
|
|
||||||
);
|
|
||||||
foundY = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assert.ok(foundY);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deepCirc = function () {
|
|
||||||
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
|
|
||||||
obj.y[2] = obj;
|
|
||||||
|
|
||||||
var times = 0;
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
if (this.circular) {
|
|
||||||
assert.deepEqual(this.circular.path, []);
|
|
||||||
assert.deepEqual(this.path, [ 'y', 2 ]);
|
|
||||||
times ++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(times, 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.doubleCirc = function () {
|
|
||||||
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
|
|
||||||
obj.y[2] = obj;
|
|
||||||
obj.x.push(obj.y);
|
|
||||||
|
|
||||||
var circs = [];
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
if (this.circular) {
|
|
||||||
circs.push({ circ : this.circular, self : this, node : x });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(circs[0].self.path, [ 'x', 3, 2 ]);
|
|
||||||
assert.deepEqual(circs[0].circ.path, []);
|
|
||||||
|
|
||||||
assert.deepEqual(circs[1].self.path, [ 'y', 2 ]);
|
|
||||||
assert.deepEqual(circs[1].circ.path, []);
|
|
||||||
|
|
||||||
assert.deepEqual(circs.length, 2);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.circDubForEach = function () {
|
|
||||||
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
|
|
||||||
obj.y[2] = obj;
|
|
||||||
obj.x.push(obj.y);
|
|
||||||
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
if (this.circular) this.update('...');
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(obj, { x : [ 1, 2, 3, [ 4, 5, '...' ] ], y : [ 4, 5, '...' ] });
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.circDubMap = function () {
|
|
||||||
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
|
|
||||||
obj.y[2] = obj;
|
|
||||||
obj.x.push(obj.y);
|
|
||||||
|
|
||||||
var c = Traverse(obj).map(function (x) {
|
|
||||||
if (this.circular) {
|
|
||||||
this.update('...');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(c, { x : [ 1, 2, 3, [ 4, 5, '...' ] ], y : [ 4, 5, '...' ] });
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.circClone = function () {
|
|
||||||
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
|
|
||||||
obj.y[2] = obj;
|
|
||||||
obj.x.push(obj.y);
|
|
||||||
|
|
||||||
var clone = Traverse.clone(obj);
|
|
||||||
assert.ok(obj !== clone);
|
|
||||||
|
|
||||||
assert.ok(clone.y[2] === clone);
|
|
||||||
assert.ok(clone.y[2] !== obj);
|
|
||||||
assert.ok(clone.x[3][2] === clone);
|
|
||||||
assert.ok(clone.x[3][2] !== obj);
|
|
||||||
assert.deepEqual(clone.x.slice(0,3), [1,2,3]);
|
|
||||||
assert.deepEqual(clone.y.slice(0,2), [4,5]);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.circMapScrub = function () {
|
|
||||||
var obj = { a : 1, b : 2 };
|
|
||||||
obj.c = obj;
|
|
||||||
|
|
||||||
var scrubbed = Traverse(obj).map(function (node) {
|
|
||||||
if (this.circular) this.remove();
|
|
||||||
});
|
|
||||||
assert.deepEqual(
|
|
||||||
Object.keys(scrubbed).sort(),
|
|
||||||
[ 'a', 'b' ]
|
|
||||||
);
|
|
||||||
assert.ok(deepEqual(scrubbed, { a : 1, b : 2 }));
|
|
||||||
|
|
||||||
assert.equal(obj.c, obj);
|
|
||||||
};
|
|
|
@ -1,35 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var Traverse = require('../');
|
|
||||||
|
|
||||||
exports.dateEach = function () {
|
|
||||||
var obj = { x : new Date, y : 10, z : 5 };
|
|
||||||
|
|
||||||
var counts = {};
|
|
||||||
|
|
||||||
Traverse(obj).forEach(function (node) {
|
|
||||||
var t = (node instanceof Date && 'Date') || typeof node;
|
|
||||||
counts[t] = (counts[t] || 0) + 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(counts, {
|
|
||||||
object : 1,
|
|
||||||
Date : 1,
|
|
||||||
number : 2,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.dateMap = function () {
|
|
||||||
var obj = { x : new Date, y : 10, z : 5 };
|
|
||||||
|
|
||||||
var res = Traverse(obj).map(function (node) {
|
|
||||||
if (typeof node === 'number') this.update(node + 100);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.ok(obj.x !== res.x);
|
|
||||||
assert.deepEqual(res, {
|
|
||||||
x : obj.x,
|
|
||||||
y : 110,
|
|
||||||
z : 105,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,220 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var traverse = require('../');
|
|
||||||
var deepEqual = require('./lib/deep_equal');
|
|
||||||
|
|
||||||
exports.deepDates = function () {
|
|
||||||
assert.ok(
|
|
||||||
deepEqual(
|
|
||||||
{ d : new Date, x : [ 1, 2, 3 ] },
|
|
||||||
{ d : new Date, x : [ 1, 2, 3 ] }
|
|
||||||
),
|
|
||||||
'dates should be equal'
|
|
||||||
);
|
|
||||||
|
|
||||||
var d0 = new Date;
|
|
||||||
setTimeout(function () {
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(
|
|
||||||
{ d : d0, x : [ 1, 2, 3 ], },
|
|
||||||
{ d : new Date, x : [ 1, 2, 3 ] }
|
|
||||||
),
|
|
||||||
'microseconds should count in date equality'
|
|
||||||
);
|
|
||||||
}, 5);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deepCircular = function () {
|
|
||||||
var a = [1];
|
|
||||||
a.push(a); // a = [ 1, *a ]
|
|
||||||
|
|
||||||
var b = [1];
|
|
||||||
b.push(a); // b = [ 1, [ 1, *a ] ]
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(a, b),
|
|
||||||
'circular ref mount points count towards equality'
|
|
||||||
);
|
|
||||||
|
|
||||||
var c = [1];
|
|
||||||
c.push(c); // c = [ 1, *c ]
|
|
||||||
assert.ok(
|
|
||||||
deepEqual(a, c),
|
|
||||||
'circular refs are structurally the same here'
|
|
||||||
);
|
|
||||||
|
|
||||||
var d = [1];
|
|
||||||
d.push(a); // c = [ 1, [ 1, *d ] ]
|
|
||||||
assert.ok(
|
|
||||||
deepEqual(b, d),
|
|
||||||
'non-root circular ref structural comparison'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deepInstances = function () {
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual([ new Boolean(false) ], [ false ]),
|
|
||||||
'boolean instances are not real booleans'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual([ new String('x') ], [ 'x' ]),
|
|
||||||
'string instances are not real strings'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual([ new Number(4) ], [ 4 ]),
|
|
||||||
'number instances are not real numbers'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
deepEqual([ new RegExp('x') ], [ /x/ ]),
|
|
||||||
'regexp instances are real regexps'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual([ new RegExp(/./) ], [ /../ ]),
|
|
||||||
'these regexps aren\'t the same'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(
|
|
||||||
[ function (x) { return x * 2 } ],
|
|
||||||
[ function (x) { return x * 2 } ]
|
|
||||||
),
|
|
||||||
'functions with the same .toString() aren\'t necessarily the same'
|
|
||||||
);
|
|
||||||
|
|
||||||
var f = function (x) { return x * 2 };
|
|
||||||
assert.ok(
|
|
||||||
deepEqual([ f ], [ f ]),
|
|
||||||
'these functions are actually equal'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deepEqual = function () {
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual([ 1, 2, 3 ], { 0 : 1, 1 : 2, 2 : 3 }),
|
|
||||||
'arrays are not objects'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.falsy = function () {
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual([ undefined ], [ null ]),
|
|
||||||
'null is not undefined!'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual([ null ], [ undefined ]),
|
|
||||||
'undefined is not null!'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(
|
|
||||||
{ a : 1, b : 2, c : [ 3, undefined, 5 ] },
|
|
||||||
{ a : 1, b : 2, c : [ 3, null, 5 ] }
|
|
||||||
),
|
|
||||||
'undefined is not null, however deeply!'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(
|
|
||||||
{ a : 1, b : 2, c : [ 3, undefined, 5 ] },
|
|
||||||
{ a : 1, b : 2, c : [ 3, null, 5 ] }
|
|
||||||
),
|
|
||||||
'null is not undefined, however deeply!'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(
|
|
||||||
{ a : 1, b : 2, c : [ 3, undefined, 5 ] },
|
|
||||||
{ a : 1, b : 2, c : [ 3, null, 5 ] }
|
|
||||||
),
|
|
||||||
'null is not undefined, however deeply!'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deletedArrayEqual = function () {
|
|
||||||
var xs = [ 1, 2, 3, 4 ];
|
|
||||||
delete xs[2];
|
|
||||||
|
|
||||||
var ys = Object.create(Array.prototype);
|
|
||||||
ys[0] = 1;
|
|
||||||
ys[1] = 2;
|
|
||||||
ys[3] = 4;
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
deepEqual(xs, ys),
|
|
||||||
'arrays with deleted elements are only equal to'
|
|
||||||
+ ' arrays with similarly deleted elements'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(xs, [ 1, 2, undefined, 4 ]),
|
|
||||||
'deleted array elements cannot be undefined'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(xs, [ 1, 2, null, 4 ]),
|
|
||||||
'deleted array elements cannot be null'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deletedObjectEqual = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : 3 };
|
|
||||||
delete obj.c;
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
deepEqual(obj, { a : 1, b : 2 }),
|
|
||||||
'deleted object elements should not show up'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(obj, { a : 1, b : 2, c : undefined }),
|
|
||||||
'deleted object elements are not undefined'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(obj, { a : 1, b : 2, c : null }),
|
|
||||||
'deleted object elements are not null'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.emptyKeyEqual = function () {
|
|
||||||
assert.ok(!deepEqual(
|
|
||||||
{ a : 1 }, { a : 1, '' : 55 }
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deepArguments = function () {
|
|
||||||
assert.ok(
|
|
||||||
!deepEqual(
|
|
||||||
[ 4, 5, 6 ],
|
|
||||||
(function () { return arguments })(4, 5, 6)
|
|
||||||
),
|
|
||||||
'arguments are not arrays'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.ok(
|
|
||||||
deepEqual(
|
|
||||||
(function () { return arguments })(4, 5, 6),
|
|
||||||
(function () { return arguments })(4, 5, 6)
|
|
||||||
),
|
|
||||||
'arguments should equal'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deepUn = function () {
|
|
||||||
assert.ok(!deepEqual({ a : 1, b : 2 }, undefined));
|
|
||||||
assert.ok(!deepEqual({ a : 1, b : 2 }, {}));
|
|
||||||
assert.ok(!deepEqual(undefined, { a : 1, b : 2 }));
|
|
||||||
assert.ok(!deepEqual({}, { a : 1, b : 2 }));
|
|
||||||
assert.ok(deepEqual(undefined, undefined));
|
|
||||||
assert.ok(deepEqual(null, null));
|
|
||||||
assert.ok(!deepEqual(undefined, null));
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deepLevels = function () {
|
|
||||||
var xs = [ 1, 2, [ 3, 4, [ 5, 6 ] ] ];
|
|
||||||
assert.ok(!deepEqual(xs, []));
|
|
||||||
};
|
|
|
@ -1,17 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var Traverse = require('../');
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
|
||||||
|
|
||||||
exports['check instanceof on node elems'] = function () {
|
|
||||||
|
|
||||||
var counts = { emitter : 0 };
|
|
||||||
|
|
||||||
Traverse([ new EventEmitter, 3, 4, { ev : new EventEmitter }])
|
|
||||||
.forEach(function (node) {
|
|
||||||
if (node instanceof EventEmitter) counts.emitter ++;
|
|
||||||
})
|
|
||||||
;
|
|
||||||
|
|
||||||
assert.equal(counts.emitter, 2);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var Traverse = require('../');
|
|
||||||
|
|
||||||
exports['interface map'] = function () {
|
|
||||||
var obj = { a : [ 5,6,7 ], b : { c : [8] } };
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
Traverse.paths(obj)
|
|
||||||
.sort()
|
|
||||||
.map(function (path) { return path.join('/') })
|
|
||||||
.slice(1)
|
|
||||||
.join(' ')
|
|
||||||
,
|
|
||||||
'a a/0 a/1 a/2 b b/c b/c/0'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
Traverse.nodes(obj),
|
|
||||||
[
|
|
||||||
{ a: [ 5, 6, 7 ], b: { c: [ 8 ] } },
|
|
||||||
[ 5, 6, 7 ], 5, 6, 7,
|
|
||||||
{ c: [ 8 ] }, [ 8 ], 8
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
Traverse.map(obj, function (node) {
|
|
||||||
if (typeof node == 'number') {
|
|
||||||
return node + 1000;
|
|
||||||
}
|
|
||||||
else if (Array.isArray(node)) {
|
|
||||||
return node.join(' ');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
{ a: '5 6 7', b: { c: '8' } }
|
|
||||||
);
|
|
||||||
|
|
||||||
var nodes = 0;
|
|
||||||
Traverse.forEach(obj, function (node) { nodes ++ });
|
|
||||||
assert.deepEqual(nodes, 8);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var Traverse = require('../');
|
|
||||||
|
|
||||||
exports['json test'] = function () {
|
|
||||||
var id = 54;
|
|
||||||
var callbacks = {};
|
|
||||||
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };
|
|
||||||
|
|
||||||
var scrubbed = Traverse(obj).map(function (x) {
|
|
||||||
if (typeof x === 'function') {
|
|
||||||
callbacks[id] = { id : id, f : x, path : this.path };
|
|
||||||
this.update('[Function]');
|
|
||||||
id++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
scrubbed.moo, '[Function]',
|
|
||||||
'obj.moo replaced with "[Function]"'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
scrubbed.foo[3], '[Function]',
|
|
||||||
'obj.foo[3] replaced with "[Function]"'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.deepEqual(scrubbed, {
|
|
||||||
moo : '[Function]',
|
|
||||||
foo : [ 2, 3, 4, "[Function]" ]
|
|
||||||
}, 'Full JSON string matches');
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
typeof obj.moo, 'function',
|
|
||||||
'Original obj.moo still a function'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.deepEqual(
|
|
||||||
typeof obj.foo[3], 'function',
|
|
||||||
'Original obj.foo[3] still a function'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.deepEqual(callbacks, {
|
|
||||||
54: { id: 54, f : obj.moo, path: [ 'moo' ] },
|
|
||||||
55: { id: 55, f : obj.foo[3], path: [ 'foo', '3' ] },
|
|
||||||
}, 'Check the generated callbacks list');
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var Traverse = require('../');
|
|
||||||
|
|
||||||
exports['sort test'] = function () {
|
|
||||||
var acc = [];
|
|
||||||
Traverse({
|
|
||||||
a: 30,
|
|
||||||
b: 22,
|
|
||||||
id: 9
|
|
||||||
}).forEach(function (node) {
|
|
||||||
if ((! Array.isArray(node)) && typeof node === 'object') {
|
|
||||||
this.before(function(node) {
|
|
||||||
this.keys = Object.keys(node);
|
|
||||||
this.keys.sort(function(a, b) {
|
|
||||||
a = [a === "id" ? 0 : 1, a];
|
|
||||||
b = [b === "id" ? 0 : 1, b];
|
|
||||||
return a < b ? -1 : a > b ? 1 : 0;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (this.isLeaf) acc.push(node);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
acc.join(' '),
|
|
||||||
'9 30 22',
|
|
||||||
'Traversal in a custom order'
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,21 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var Traverse = require('../');
|
|
||||||
|
|
||||||
exports['leaves test'] = function () {
|
|
||||||
var acc = [];
|
|
||||||
Traverse({
|
|
||||||
a : [1,2,3],
|
|
||||||
b : 4,
|
|
||||||
c : [5,6],
|
|
||||||
d : { e : [7,8], f : 9 }
|
|
||||||
}).forEach(function (x) {
|
|
||||||
if (this.isLeaf) acc.push(x);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
acc.join(' '),
|
|
||||||
'1 2 3 4 5 6 7 8 9',
|
|
||||||
'Traversal in the right(?) order'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
var traverse = require('../../');
|
|
||||||
|
|
||||||
module.exports = function (a, b) {
|
|
||||||
if (arguments.length !== 2) {
|
|
||||||
throw new Error(
|
|
||||||
'deepEqual requires exactly two objects to compare against'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var equal = true;
|
|
||||||
var node = b;
|
|
||||||
|
|
||||||
traverse(a).forEach(function (y) {
|
|
||||||
var notEqual = (function () {
|
|
||||||
equal = false;
|
|
||||||
//this.stop();
|
|
||||||
return undefined;
|
|
||||||
}).bind(this);
|
|
||||||
|
|
||||||
//if (node === undefined || node === null) return notEqual();
|
|
||||||
|
|
||||||
if (!this.isRoot) {
|
|
||||||
/*
|
|
||||||
if (!Object.hasOwnProperty.call(node, this.key)) {
|
|
||||||
return notEqual();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (typeof node !== 'object') return notEqual();
|
|
||||||
node = node[this.key];
|
|
||||||
}
|
|
||||||
|
|
||||||
var x = node;
|
|
||||||
|
|
||||||
this.post(function () {
|
|
||||||
node = x;
|
|
||||||
});
|
|
||||||
|
|
||||||
var toS = function (o) {
|
|
||||||
return Object.prototype.toString.call(o);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.circular) {
|
|
||||||
if (traverse(b).get(this.circular.path) !== x) notEqual();
|
|
||||||
}
|
|
||||||
else if (typeof x !== typeof y) {
|
|
||||||
notEqual();
|
|
||||||
}
|
|
||||||
else if (x === null || y === null || x === undefined || y === undefined) {
|
|
||||||
if (x !== y) notEqual();
|
|
||||||
}
|
|
||||||
else if (x.__proto__ !== y.__proto__) {
|
|
||||||
notEqual();
|
|
||||||
}
|
|
||||||
else if (x === y) {
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
else if (typeof x === 'function') {
|
|
||||||
if (x instanceof RegExp) {
|
|
||||||
// both regexps on account of the __proto__ check
|
|
||||||
if (x.toString() != y.toString()) notEqual();
|
|
||||||
}
|
|
||||||
else if (x !== y) notEqual();
|
|
||||||
}
|
|
||||||
else if (typeof x === 'object') {
|
|
||||||
if (toS(y) === '[object Arguments]'
|
|
||||||
|| toS(x) === '[object Arguments]') {
|
|
||||||
if (toS(x) !== toS(y)) {
|
|
||||||
notEqual();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (x instanceof Date || y instanceof Date) {
|
|
||||||
if (!(x instanceof Date) || !(y instanceof Date)
|
|
||||||
|| x.getTime() !== y.getTime()) {
|
|
||||||
notEqual();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var kx = Object.keys(x);
|
|
||||||
var ky = Object.keys(y);
|
|
||||||
if (kx.length !== ky.length) return notEqual();
|
|
||||||
for (var i = 0; i < kx.length; i++) {
|
|
||||||
var k = kx[i];
|
|
||||||
if (!Object.hasOwnProperty.call(y, k)) {
|
|
||||||
notEqual();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return equal;
|
|
||||||
};
|
|
|
@ -1,252 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var Traverse = require('../');
|
|
||||||
var deepEqual = require('./lib/deep_equal');
|
|
||||||
|
|
||||||
exports.mutate = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
var res = Traverse(obj).forEach(function (x) {
|
|
||||||
if (typeof x === 'number' && x % 2 === 0) {
|
|
||||||
this.update(x * 10);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assert.deepEqual(obj, res);
|
|
||||||
assert.deepEqual(obj, { a : 1, b : 20, c : [ 3, 40 ] });
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.mutateT = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
var res = Traverse.forEach(obj, function (x) {
|
|
||||||
if (typeof x === 'number' && x % 2 === 0) {
|
|
||||||
this.update(x * 10);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assert.deepEqual(obj, res);
|
|
||||||
assert.deepEqual(obj, { a : 1, b : 20, c : [ 3, 40 ] });
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.map = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
var res = Traverse(obj).map(function (x) {
|
|
||||||
if (typeof x === 'number' && x % 2 === 0) {
|
|
||||||
this.update(x * 10);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assert.deepEqual(obj, { a : 1, b : 2, c : [ 3, 4 ] });
|
|
||||||
assert.deepEqual(res, { a : 1, b : 20, c : [ 3, 40 ] });
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.mapT = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
var res = Traverse.map(obj, function (x) {
|
|
||||||
if (typeof x === 'number' && x % 2 === 0) {
|
|
||||||
this.update(x * 10);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assert.deepEqual(obj, { a : 1, b : 2, c : [ 3, 4 ] });
|
|
||||||
assert.deepEqual(res, { a : 1, b : 20, c : [ 3, 40 ] });
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.clone = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
var res = Traverse(obj).clone();
|
|
||||||
assert.deepEqual(obj, res);
|
|
||||||
assert.ok(obj !== res);
|
|
||||||
obj.a ++;
|
|
||||||
assert.deepEqual(res.a, 1);
|
|
||||||
obj.c.push(5);
|
|
||||||
assert.deepEqual(res.c, [ 3, 4 ]);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.cloneT = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
var res = Traverse.clone(obj);
|
|
||||||
assert.deepEqual(obj, res);
|
|
||||||
assert.ok(obj !== res);
|
|
||||||
obj.a ++;
|
|
||||||
assert.deepEqual(res.a, 1);
|
|
||||||
obj.c.push(5);
|
|
||||||
assert.deepEqual(res.c, [ 3, 4 ]);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.reduce = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
var res = Traverse(obj).reduce(function (acc, x) {
|
|
||||||
if (this.isLeaf) acc.push(x);
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
assert.deepEqual(obj, { a : 1, b : 2, c : [ 3, 4 ] });
|
|
||||||
assert.deepEqual(res, [ 1, 2, 3, 4 ]);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.reduceInit = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
var res = Traverse(obj).reduce(function (acc, x) {
|
|
||||||
if (this.isRoot) assert.fail('got root');
|
|
||||||
return acc;
|
|
||||||
});
|
|
||||||
assert.deepEqual(obj, { a : 1, b : 2, c : [ 3, 4 ] });
|
|
||||||
assert.deepEqual(res, obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.remove = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
if (this.isLeaf && x % 2 == 0) this.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(obj, { a : 1, c : [ 3 ] });
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.removeNoStop = function() {
|
|
||||||
var obj = { a : 1, b : 2, c : { d: 3, e: 4 }, f: 5 };
|
|
||||||
|
|
||||||
var keys = [];
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
keys.push(this.key)
|
|
||||||
if (this.key == 'c') this.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(keys, [undefined, 'a', 'b', 'c', 'd', 'e', 'f'])
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.removeStop = function() {
|
|
||||||
var obj = { a : 1, b : 2, c : { d: 3, e: 4 }, f: 5 };
|
|
||||||
|
|
||||||
var keys = [];
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
keys.push(this.key)
|
|
||||||
if (this.key == 'c') this.remove(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(keys, [undefined, 'a', 'b', 'c', 'f'])
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.removeMap = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
var res = Traverse(obj).map(function (x) {
|
|
||||||
if (this.isLeaf && x % 2 == 0) this.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(obj, { a : 1, b : 2, c : [ 3, 4 ] });
|
|
||||||
assert.deepEqual(res, { a : 1, c : [ 3 ] });
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.delete = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
if (this.isLeaf && x % 2 == 0) this.delete();
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.ok(!deepEqual(
|
|
||||||
obj, { a : 1, c : [ 3, undefined ] }
|
|
||||||
));
|
|
||||||
|
|
||||||
assert.ok(deepEqual(
|
|
||||||
obj, { a : 1, c : [ 3 ] }
|
|
||||||
));
|
|
||||||
|
|
||||||
assert.ok(!deepEqual(
|
|
||||||
obj, { a : 1, c : [ 3, null ] }
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deleteNoStop = function() {
|
|
||||||
var obj = { a : 1, b : 2, c : { d: 3, e: 4 } };
|
|
||||||
|
|
||||||
var keys = [];
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
keys.push(this.key)
|
|
||||||
if (this.key == 'c') this.delete();
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(keys, [undefined, 'a', 'b', 'c', 'd', 'e'])
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.deleteStop = function() {
|
|
||||||
var obj = { a : 1, b : 2, c : { d: 3, e: 4 } };
|
|
||||||
|
|
||||||
var keys = [];
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
keys.push(this.key)
|
|
||||||
if (this.key == 'c') this.delete(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(keys, [undefined, 'a', 'b', 'c'])
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.deleteRedux = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4, 5 ] };
|
|
||||||
Traverse(obj).forEach(function (x) {
|
|
||||||
if (this.isLeaf && x % 2 == 0) this.delete();
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.ok(!deepEqual(
|
|
||||||
obj, { a : 1, c : [ 3, undefined, 5 ] }
|
|
||||||
));
|
|
||||||
|
|
||||||
assert.ok(deepEqual(
|
|
||||||
obj, { a : 1, c : [ 3 ,, 5 ] }
|
|
||||||
));
|
|
||||||
|
|
||||||
assert.ok(!deepEqual(
|
|
||||||
obj, { a : 1, c : [ 3, null, 5 ] }
|
|
||||||
));
|
|
||||||
|
|
||||||
assert.ok(!deepEqual(
|
|
||||||
obj, { a : 1, c : [ 3, 5 ] }
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deleteMap = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
|
|
||||||
var res = Traverse(obj).map(function (x) {
|
|
||||||
if (this.isLeaf && x % 2 == 0) this.delete();
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.ok(deepEqual(
|
|
||||||
obj,
|
|
||||||
{ a : 1, b : 2, c : [ 3, 4 ] }
|
|
||||||
));
|
|
||||||
|
|
||||||
var xs = [ 3, 4 ];
|
|
||||||
delete xs[1];
|
|
||||||
|
|
||||||
assert.ok(deepEqual(
|
|
||||||
res, { a : 1, c : xs }
|
|
||||||
));
|
|
||||||
|
|
||||||
assert.ok(deepEqual(
|
|
||||||
res, { a : 1, c : [ 3, ] }
|
|
||||||
));
|
|
||||||
|
|
||||||
assert.ok(deepEqual(
|
|
||||||
res, { a : 1, c : [ 3 ] }
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.deleteMapRedux = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 3, 4, 5 ] };
|
|
||||||
var res = Traverse(obj).map(function (x) {
|
|
||||||
if (this.isLeaf && x % 2 == 0) this.delete();
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.ok(deepEqual(
|
|
||||||
obj,
|
|
||||||
{ a : 1, b : 2, c : [ 3, 4, 5 ] }
|
|
||||||
));
|
|
||||||
|
|
||||||
var xs = [ 3, 4, 5 ];
|
|
||||||
delete xs[1];
|
|
||||||
|
|
||||||
assert.ok(deepEqual(
|
|
||||||
res, { a : 1, c : xs }
|
|
||||||
));
|
|
||||||
|
|
||||||
assert.ok(!deepEqual(
|
|
||||||
res, { a : 1, c : [ 3, 5 ] }
|
|
||||||
));
|
|
||||||
|
|
||||||
assert.ok(deepEqual(
|
|
||||||
res, { a : 1, c : [ 3 ,, 5 ] }
|
|
||||||
));
|
|
||||||
};
|
|
|
@ -1,20 +0,0 @@
|
||||||
var Traverse = require('../');
|
|
||||||
var assert = require('assert');
|
|
||||||
|
|
||||||
exports['negative update test'] = function () {
|
|
||||||
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
|
|
||||||
var fixed = Traverse.map(obj, function (x) {
|
|
||||||
if (x < 0) this.update(x + 128);
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(fixed,
|
|
||||||
[ 5, 6, 125, [ 7, 8, 126, 1 ], { f: 10, g: 115 } ],
|
|
||||||
'Negative values += 128'
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.deepEqual(obj,
|
|
||||||
[ 5, 6, -3, [ 7, 8, -2, 1 ], { f: 10, g: -13 } ],
|
|
||||||
'Original references not modified'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var Traverse = require('../');
|
|
||||||
|
|
||||||
exports['traverse an object with nested functions'] = function () {
|
|
||||||
var to = setTimeout(function () {
|
|
||||||
assert.fail('never ran');
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
function Cons (x) {
|
|
||||||
clearTimeout(to);
|
|
||||||
assert.equal(x, 10);
|
|
||||||
};
|
|
||||||
Traverse(new Cons(10));
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var traverse = require('../');
|
|
||||||
|
|
||||||
exports.siblings = function () {
|
|
||||||
var obj = { a : 1, b : 2, c : [ 4, 5, 6 ] };
|
|
||||||
|
|
||||||
var res = traverse(obj).reduce(function (acc, x) {
|
|
||||||
var p = '/' + this.path.join('/');
|
|
||||||
if (this.parent) {
|
|
||||||
acc[p] = {
|
|
||||||
siblings : this.parent.keys,
|
|
||||||
key : this.key,
|
|
||||||
index : this.parent.keys.indexOf(this.key)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
acc[p] = {
|
|
||||||
siblings : [],
|
|
||||||
key : this.key,
|
|
||||||
index : -1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
assert.deepEqual(res, {
|
|
||||||
'/' : { siblings : [], key : undefined, index : -1 },
|
|
||||||
'/a' : { siblings : [ 'a', 'b', 'c' ], key : 'a', index : 0 },
|
|
||||||
'/b' : { siblings : [ 'a', 'b', 'c' ], key : 'b', index : 1 },
|
|
||||||
'/c' : { siblings : [ 'a', 'b', 'c' ], key : 'c', index : 2 },
|
|
||||||
'/c/0' : { siblings : [ '0', '1', '2' ], key : '0', index : 0 },
|
|
||||||
'/c/1' : { siblings : [ '0', '1', '2' ], key : '1', index : 1 },
|
|
||||||
'/c/2' : { siblings : [ '0', '1', '2' ], key : '2', index : 2 }
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,41 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var traverse = require('../');
|
|
||||||
|
|
||||||
exports.stop = function () {
|
|
||||||
var visits = 0;
|
|
||||||
traverse('abcdefghij'.split('')).forEach(function (node) {
|
|
||||||
if (typeof node === 'string') {
|
|
||||||
visits ++;
|
|
||||||
if (node === 'e') this.stop()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(visits, 5);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.stopMap = function () {
|
|
||||||
var s = traverse('abcdefghij'.split('')).map(function (node) {
|
|
||||||
if (typeof node === 'string') {
|
|
||||||
if (node === 'e') this.stop()
|
|
||||||
return node.toUpperCase();
|
|
||||||
}
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
assert.equal(s, 'ABCDEfghij');
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.stopReduce = function () {
|
|
||||||
var obj = {
|
|
||||||
a : [ 4, 5 ],
|
|
||||||
b : [ 6, [ 7, 8, 9 ] ]
|
|
||||||
};
|
|
||||||
var xs = traverse(obj).reduce(function (acc, node) {
|
|
||||||
if (this.isLeaf) {
|
|
||||||
if (node === 7) this.stop();
|
|
||||||
else acc.push(node)
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
assert.deepEqual(xs, [ 4, 5, 6 ]);
|
|
||||||
};
|
|
|
@ -1,36 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var Traverse = require('../');
|
|
||||||
|
|
||||||
exports.stringify = function () {
|
|
||||||
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
|
|
||||||
|
|
||||||
var s = '';
|
|
||||||
Traverse(obj).forEach(function (node) {
|
|
||||||
if (Array.isArray(node)) {
|
|
||||||
this.before(function () { s += '[' });
|
|
||||||
this.post(function (child) {
|
|
||||||
if (!child.isLast) s += ',';
|
|
||||||
});
|
|
||||||
this.after(function () { s += ']' });
|
|
||||||
}
|
|
||||||
else if (typeof node == 'object') {
|
|
||||||
this.before(function () { s += '{' });
|
|
||||||
this.pre(function (x, key) {
|
|
||||||
s += '"' + key + '"' + ':';
|
|
||||||
});
|
|
||||||
this.post(function (child) {
|
|
||||||
if (!child.isLast) s += ',';
|
|
||||||
});
|
|
||||||
this.after(function () { s += '}' });
|
|
||||||
}
|
|
||||||
else if (typeof node == 'function') {
|
|
||||||
s += 'null';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
s += node.toString();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(s, JSON.stringify(obj));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
var traverse = require('../');
|
|
||||||
var assert = require('assert');
|
|
||||||
|
|
||||||
exports.subexpr = function () {
|
|
||||||
var obj = [ 'a', 4, 'b', 5, 'c', 6 ];
|
|
||||||
var r = traverse(obj).map(function (x) {
|
|
||||||
if (typeof x === 'number') {
|
|
||||||
this.update([ x - 0.1, x, x + 0.1 ], true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(obj, [ 'a', 4, 'b', 5, 'c', 6 ]);
|
|
||||||
assert.deepEqual(r, [
|
|
||||||
'a', [ 3.9, 4, 4.1 ],
|
|
||||||
'b', [ 4.9, 5, 5.1 ],
|
|
||||||
'c', [ 5.9, 6, 6.1 ],
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.block = function () {
|
|
||||||
var obj = [ [ 1 ], [ 2 ], [ 3 ] ];
|
|
||||||
var r = traverse(obj).map(function (x) {
|
|
||||||
if (Array.isArray(x) && !this.isRoot) {
|
|
||||||
if (x[0] === 5) this.block()
|
|
||||||
else this.update([ [ x[0] + 1 ] ])
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.deepEqual(r, [
|
|
||||||
[ [ [ [ [ 5 ] ] ] ] ],
|
|
||||||
[ [ [ [ 5 ] ] ] ],
|
|
||||||
[ [ [ 5 ] ] ],
|
|
||||||
]);
|
|
||||||
};
|
|
|
@ -1,55 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var traverse = require('../');
|
|
||||||
var deepEqual = require('./lib/deep_equal');
|
|
||||||
|
|
||||||
exports.super_deep = function () {
|
|
||||||
var util = require('util');
|
|
||||||
var a0 = make();
|
|
||||||
var a1 = make();
|
|
||||||
assert.ok(deepEqual(a0, a1));
|
|
||||||
|
|
||||||
a0.c.d.moo = true;
|
|
||||||
assert.ok(!deepEqual(a0, a1));
|
|
||||||
|
|
||||||
a1.c.d.moo = true;
|
|
||||||
assert.ok(deepEqual(a0, a1));
|
|
||||||
|
|
||||||
// TODO: this one
|
|
||||||
//a0.c.a = a1;
|
|
||||||
//assert.ok(!deepEqual(a0, a1));
|
|
||||||
};
|
|
||||||
|
|
||||||
function make () {
|
|
||||||
var a = { self : 'a' };
|
|
||||||
var b = { self : 'b' };
|
|
||||||
var c = { self : 'c' };
|
|
||||||
var d = { self : 'd' };
|
|
||||||
var e = { self : 'e' };
|
|
||||||
|
|
||||||
a.a = a;
|
|
||||||
a.b = b;
|
|
||||||
a.c = c;
|
|
||||||
|
|
||||||
b.a = a;
|
|
||||||
b.b = b;
|
|
||||||
b.c = c;
|
|
||||||
|
|
||||||
c.a = a;
|
|
||||||
c.b = b;
|
|
||||||
c.c = c;
|
|
||||||
c.d = d;
|
|
||||||
|
|
||||||
d.a = a;
|
|
||||||
d.b = b;
|
|
||||||
d.c = c;
|
|
||||||
d.d = d;
|
|
||||||
d.e = e;
|
|
||||||
|
|
||||||
e.a = a;
|
|
||||||
e.b = b;
|
|
||||||
e.c = c;
|
|
||||||
e.d = d;
|
|
||||||
e.e = e;
|
|
||||||
|
|
||||||
return a;
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
.DS_Store
|
|
||||||
.tmp*~
|
|
||||||
*.local.*
|
|
||||||
.pinf-*
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,571 +0,0 @@
|
||||||
#+TITLE: UglifyJS -- a JavaScript parser/compressor/beautifier
|
|
||||||
#+KEYWORDS: javascript, js, parser, compiler, compressor, mangle, minify, minifier
|
|
||||||
#+DESCRIPTION: a JavaScript parser/compressor/beautifier in JavaScript
|
|
||||||
#+STYLE: <link rel="stylesheet" type="text/css" href="docstyle.css" />
|
|
||||||
#+AUTHOR: Mihai Bazon
|
|
||||||
#+EMAIL: mihai.bazon@gmail.com
|
|
||||||
|
|
||||||
* UglifyJS --- a JavaScript parser/compressor/beautifier
|
|
||||||
|
|
||||||
This package implements a general-purpose JavaScript
|
|
||||||
parser/compressor/beautifier toolkit. It is developed on [[http://nodejs.org/][NodeJS]], but it
|
|
||||||
should work on any JavaScript platform supporting the CommonJS module system
|
|
||||||
(and if your platform of choice doesn't support CommonJS, you can easily
|
|
||||||
implement it, or discard the =exports.*= lines from UglifyJS sources).
|
|
||||||
|
|
||||||
The tokenizer/parser generates an abstract syntax tree from JS code. You
|
|
||||||
can then traverse the AST to learn more about the code, or do various
|
|
||||||
manipulations on it. This part is implemented in [[../lib/parse-js.js][parse-js.js]] and it's a
|
|
||||||
port to JavaScript of the excellent [[http://marijn.haverbeke.nl/parse-js/][parse-js]] Common Lisp library from [[http://marijn.haverbeke.nl/][Marijn
|
|
||||||
Haverbeke]].
|
|
||||||
|
|
||||||
( See [[http://github.com/mishoo/cl-uglify-js][cl-uglify-js]] if you're looking for the Common Lisp version of
|
|
||||||
UglifyJS. )
|
|
||||||
|
|
||||||
The second part of this package, implemented in [[../lib/process.js][process.js]], inspects and
|
|
||||||
manipulates the AST generated by the parser to provide the following:
|
|
||||||
|
|
||||||
- ability to re-generate JavaScript code from the AST. Optionally
|
|
||||||
indented---you can use this if you want to “beautify” a program that has
|
|
||||||
been compressed, so that you can inspect the source. But you can also run
|
|
||||||
our code generator to print out an AST without any whitespace, so you
|
|
||||||
achieve compression as well.
|
|
||||||
|
|
||||||
- shorten variable names (usually to single characters). Our mangler will
|
|
||||||
analyze the code and generate proper variable names, depending on scope
|
|
||||||
and usage, and is smart enough to deal with globals defined elsewhere, or
|
|
||||||
with =eval()= calls or =with{}= statements. In short, if =eval()= or
|
|
||||||
=with{}= are used in some scope, then all variables in that scope and any
|
|
||||||
variables in the parent scopes will remain unmangled, and any references
|
|
||||||
to such variables remain unmangled as well.
|
|
||||||
|
|
||||||
- various small optimizations that may lead to faster code but certainly
|
|
||||||
lead to smaller code. Where possible, we do the following:
|
|
||||||
|
|
||||||
- foo["bar"] ==> foo.bar
|
|
||||||
|
|
||||||
- remove block brackets ={}=
|
|
||||||
|
|
||||||
- join consecutive var declarations:
|
|
||||||
var a = 10; var b = 20; ==> var a=10,b=20;
|
|
||||||
|
|
||||||
- resolve simple constant expressions: 1 +2 * 3 ==> 7. We only do the
|
|
||||||
replacement if the result occupies less bytes; for example 1/3 would
|
|
||||||
translate to 0.333333333333, so in this case we don't replace it.
|
|
||||||
|
|
||||||
- consecutive statements in blocks are merged into a sequence; in many
|
|
||||||
cases, this leaves blocks with a single statement, so then we can remove
|
|
||||||
the block brackets.
|
|
||||||
|
|
||||||
- various optimizations for IF statements:
|
|
||||||
|
|
||||||
- if (foo) bar(); else baz(); ==> foo?bar():baz();
|
|
||||||
- if (!foo) bar(); else baz(); ==> foo?baz():bar();
|
|
||||||
- if (foo) bar(); ==> foo&&bar();
|
|
||||||
- if (!foo) bar(); ==> foo||bar();
|
|
||||||
- if (foo) return bar(); else return baz(); ==> return foo?bar():baz();
|
|
||||||
- if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
|
|
||||||
|
|
||||||
- remove some unreachable code and warn about it (code that follows a
|
|
||||||
=return=, =throw=, =break= or =continue= statement, except
|
|
||||||
function/variable declarations).
|
|
||||||
|
|
||||||
- act a limited version of a pre-processor (c.f. the pre-processor of
|
|
||||||
C/C++) to allow you to safely replace selected global symbols with
|
|
||||||
specified values. When combined with the optimisations above this can
|
|
||||||
make UglifyJS operate slightly more like a compilation process, in
|
|
||||||
that when certain symbols are replaced by constant values, entire code
|
|
||||||
blocks may be optimised away as unreachable.
|
|
||||||
|
|
||||||
** <<Unsafe transformations>>
|
|
||||||
|
|
||||||
The following transformations can in theory break code, although they're
|
|
||||||
probably safe in most practical cases. To enable them you need to pass the
|
|
||||||
=--unsafe= flag.
|
|
||||||
|
|
||||||
*** Calls involving the global Array constructor
|
|
||||||
|
|
||||||
The following transformations occur:
|
|
||||||
|
|
||||||
#+BEGIN_SRC js
|
|
||||||
new Array(1, 2, 3, 4) => [1,2,3,4]
|
|
||||||
Array(a, b, c) => [a,b,c]
|
|
||||||
new Array(5) => Array(5)
|
|
||||||
new Array(a) => Array(a)
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
These are all safe if the Array name isn't redefined. JavaScript does allow
|
|
||||||
one to globally redefine Array (and pretty much everything, in fact) but I
|
|
||||||
personally don't see why would anyone do that.
|
|
||||||
|
|
||||||
UglifyJS does handle the case where Array is redefined locally, or even
|
|
||||||
globally but with a =function= or =var= declaration. Therefore, in the
|
|
||||||
following cases UglifyJS *doesn't touch* calls or instantiations of Array:
|
|
||||||
|
|
||||||
#+BEGIN_SRC js
|
|
||||||
// case 1. globally declared variable
|
|
||||||
var Array;
|
|
||||||
new Array(1, 2, 3);
|
|
||||||
Array(a, b);
|
|
||||||
|
|
||||||
// or (can be declared later)
|
|
||||||
new Array(1, 2, 3);
|
|
||||||
var Array;
|
|
||||||
|
|
||||||
// or (can be a function)
|
|
||||||
new Array(1, 2, 3);
|
|
||||||
function Array() { ... }
|
|
||||||
|
|
||||||
// case 2. declared in a function
|
|
||||||
(function(){
|
|
||||||
a = new Array(1, 2, 3);
|
|
||||||
b = Array(5, 6);
|
|
||||||
var Array;
|
|
||||||
})();
|
|
||||||
|
|
||||||
// or
|
|
||||||
(function(Array){
|
|
||||||
return Array(5, 6, 7);
|
|
||||||
})();
|
|
||||||
|
|
||||||
// or
|
|
||||||
(function(){
|
|
||||||
return new Array(1, 2, 3, 4);
|
|
||||||
function Array() { ... }
|
|
||||||
})();
|
|
||||||
|
|
||||||
// etc.
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
*** =obj.toString()= ==> =obj+“”=
|
|
||||||
|
|
||||||
** Install (NPM)
|
|
||||||
|
|
||||||
UglifyJS is now available through NPM --- =npm install uglify-js= should do
|
|
||||||
the job.
|
|
||||||
|
|
||||||
** Install latest code from GitHub
|
|
||||||
|
|
||||||
#+BEGIN_SRC sh
|
|
||||||
## clone the repository
|
|
||||||
mkdir -p /where/you/wanna/put/it
|
|
||||||
cd /where/you/wanna/put/it
|
|
||||||
git clone git://github.com/mishoo/UglifyJS.git
|
|
||||||
|
|
||||||
## make the module available to Node
|
|
||||||
mkdir -p ~/.node_libraries/
|
|
||||||
cd ~/.node_libraries/
|
|
||||||
ln -s /where/you/wanna/put/it/UglifyJS/uglify-js.js
|
|
||||||
|
|
||||||
## and if you want the CLI script too:
|
|
||||||
mkdir -p ~/bin
|
|
||||||
cd ~/bin
|
|
||||||
ln -s /where/you/wanna/put/it/UglifyJS/bin/uglifyjs
|
|
||||||
# (then add ~/bin to your $PATH if it's not there already)
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
** Usage
|
|
||||||
|
|
||||||
There is a command-line tool that exposes the functionality of this library
|
|
||||||
for your shell-scripting needs:
|
|
||||||
|
|
||||||
#+BEGIN_SRC sh
|
|
||||||
uglifyjs [ options... ] [ filename ]
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
=filename= should be the last argument and should name the file from which
|
|
||||||
to read the JavaScript code. If you don't specify it, it will read code
|
|
||||||
from STDIN.
|
|
||||||
|
|
||||||
Supported options:
|
|
||||||
|
|
||||||
- =-b= or =--beautify= --- output indented code; when passed, additional
|
|
||||||
options control the beautifier:
|
|
||||||
|
|
||||||
- =-i N= or =--indent N= --- indentation level (number of spaces)
|
|
||||||
|
|
||||||
- =-q= or =--quote-keys= --- quote keys in literal objects (by default,
|
|
||||||
only keys that cannot be identifier names will be quotes).
|
|
||||||
|
|
||||||
- =--ascii= --- pass this argument to encode non-ASCII characters as
|
|
||||||
=\uXXXX= sequences. By default UglifyJS won't bother to do it and will
|
|
||||||
output Unicode characters instead. (the output is always encoded in UTF8,
|
|
||||||
but if you pass this option you'll only get ASCII).
|
|
||||||
|
|
||||||
- =-nm= or =--no-mangle= --- don't mangle variable names
|
|
||||||
|
|
||||||
- =-ns= or =--no-squeeze= --- don't call =ast_squeeze()= (which does various
|
|
||||||
optimizations that result in smaller, less readable code).
|
|
||||||
|
|
||||||
- =-mt= or =--mangle-toplevel= --- mangle names in the toplevel scope too
|
|
||||||
(by default we don't do this).
|
|
||||||
|
|
||||||
- =--no-seqs= --- when =ast_squeeze()= is called (thus, unless you pass
|
|
||||||
=--no-squeeze=) it will reduce consecutive statements in blocks into a
|
|
||||||
sequence. For example, "a = 10; b = 20; foo();" will be written as
|
|
||||||
"a=10,b=20,foo();". In various occasions, this allows us to discard the
|
|
||||||
block brackets (since the block becomes a single statement). This is ON
|
|
||||||
by default because it seems safe and saves a few hundred bytes on some
|
|
||||||
libs that I tested it on, but pass =--no-seqs= to disable it.
|
|
||||||
|
|
||||||
- =--no-dead-code= --- by default, UglifyJS will remove code that is
|
|
||||||
obviously unreachable (code that follows a =return=, =throw=, =break= or
|
|
||||||
=continue= statement and is not a function/variable declaration). Pass
|
|
||||||
this option to disable this optimization.
|
|
||||||
|
|
||||||
- =-nc= or =--no-copyright= --- by default, =uglifyjs= will keep the initial
|
|
||||||
comment tokens in the generated code (assumed to be copyright information
|
|
||||||
etc.). If you pass this it will discard it.
|
|
||||||
|
|
||||||
- =-o filename= or =--output filename= --- put the result in =filename=. If
|
|
||||||
this isn't given, the result goes to standard output (or see next one).
|
|
||||||
|
|
||||||
- =--overwrite= --- if the code is read from a file (not from STDIN) and you
|
|
||||||
pass =--overwrite= then the output will be written in the same file.
|
|
||||||
|
|
||||||
- =--ast= --- pass this if you want to get the Abstract Syntax Tree instead
|
|
||||||
of JavaScript as output. Useful for debugging or learning more about the
|
|
||||||
internals.
|
|
||||||
|
|
||||||
- =-v= or =--verbose= --- output some notes on STDERR (for now just how long
|
|
||||||
each operation takes).
|
|
||||||
|
|
||||||
- =-d SYMBOL[=VALUE]= or =--define SYMBOL[=VALUE]= --- will replace
|
|
||||||
all instances of the specified symbol where used as an identifier
|
|
||||||
(except where symbol has properly declared by a var declaration or
|
|
||||||
use as function parameter or similar) with the specified value. This
|
|
||||||
argument may be specified multiple times to define multiple
|
|
||||||
symbols - if no value is specified the symbol will be replaced with
|
|
||||||
the value =true=, or you can specify a numeric value (such as
|
|
||||||
=1024=), a quoted string value (such as ="object"= or
|
|
||||||
='https://github.com'=), or the name of another symbol or keyword
|
|
||||||
(such as =null= or =document=).
|
|
||||||
This allows you, for example, to assign meaningful names to key
|
|
||||||
constant values but discard the symbolic names in the uglified
|
|
||||||
version for brevity/efficiency, or when used wth care, allows
|
|
||||||
UglifyJS to operate as a form of *conditional compilation*
|
|
||||||
whereby defining appropriate values may, by dint of the constant
|
|
||||||
folding and dead code removal features above, remove entire
|
|
||||||
superfluous code blocks (e.g. completely remove instrumentation or
|
|
||||||
trace code for production use).
|
|
||||||
Where string values are being defined, the handling of quotes are
|
|
||||||
likely to be subject to the specifics of your command shell
|
|
||||||
environment, so you may need to experiment with quoting styles
|
|
||||||
depending on your platform, or you may find the option
|
|
||||||
=--define-from-module= more suitable for use.
|
|
||||||
|
|
||||||
- =-define-from-module SOMEMODULE= --- will load the named module (as
|
|
||||||
per the NodeJS =require()= function) and iterate all the exported
|
|
||||||
properties of the module defining them as symbol names to be defined
|
|
||||||
(as if by the =--define= option) per the name of each property
|
|
||||||
(i.e. without the module name prefix) and given the value of the
|
|
||||||
property. This is a much easier way to handle and document groups of
|
|
||||||
symbols to be defined rather than a large number of =--define=
|
|
||||||
options.
|
|
||||||
|
|
||||||
- =--unsafe= --- enable other additional optimizations that are known to be
|
|
||||||
unsafe in some contrived situations, but could still be generally useful.
|
|
||||||
For now only these:
|
|
||||||
|
|
||||||
- foo.toString() ==> foo+""
|
|
||||||
- new Array(x,...) ==> [x,...]
|
|
||||||
- new Array(x) ==> Array(x)
|
|
||||||
|
|
||||||
- =--max-line-len= (default 32K characters) --- add a newline after around
|
|
||||||
32K characters. I've seen both FF and Chrome croak when all the code was
|
|
||||||
on a single line of around 670K. Pass --max-line-len 0 to disable this
|
|
||||||
safety feature.
|
|
||||||
|
|
||||||
- =--reserved-names= --- some libraries rely on certain names to be used, as
|
|
||||||
pointed out in issue #92 and #81, so this option allow you to exclude such
|
|
||||||
names from the mangler. For example, to keep names =require= and =$super=
|
|
||||||
intact you'd specify --reserved-names "require,$super".
|
|
||||||
|
|
||||||
- =--inline-script= -- when you want to include the output literally in an
|
|
||||||
HTML =<script>= tag you can use this option to prevent =</script= from
|
|
||||||
showing up in the output.
|
|
||||||
|
|
||||||
- =--lift-vars= -- when you pass this, UglifyJS will apply the following
|
|
||||||
transformations (see the notes in API, =ast_lift_variables=):
|
|
||||||
|
|
||||||
- put all =var= declarations at the start of the scope
|
|
||||||
- make sure a variable is declared only once
|
|
||||||
- discard unused function arguments
|
|
||||||
- discard unused inner (named) functions
|
|
||||||
- finally, try to merge assignments into that one =var= declaration, if
|
|
||||||
possible.
|
|
||||||
|
|
||||||
*** API
|
|
||||||
|
|
||||||
To use the library from JavaScript, you'd do the following (example for
|
|
||||||
NodeJS):
|
|
||||||
|
|
||||||
#+BEGIN_SRC js
|
|
||||||
var jsp = require("uglify-js").parser;
|
|
||||||
var pro = require("uglify-js").uglify;
|
|
||||||
|
|
||||||
var orig_code = "... JS code here";
|
|
||||||
var ast = jsp.parse(orig_code); // parse code and get the initial AST
|
|
||||||
ast = pro.ast_mangle(ast); // get a new AST with mangled names
|
|
||||||
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
|
|
||||||
var final_code = pro.gen_code(ast); // compressed code here
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
The above performs the full compression that is possible right now. As you
|
|
||||||
can see, there are a sequence of steps which you can apply. For example if
|
|
||||||
you want compressed output but for some reason you don't want to mangle
|
|
||||||
variable names, you would simply skip the line that calls
|
|
||||||
=pro.ast_mangle(ast)=.
|
|
||||||
|
|
||||||
Some of these functions take optional arguments. Here's a description:
|
|
||||||
|
|
||||||
- =jsp.parse(code, strict_semicolons)= -- parses JS code and returns an AST.
|
|
||||||
=strict_semicolons= is optional and defaults to =false=. If you pass
|
|
||||||
=true= then the parser will throw an error when it expects a semicolon and
|
|
||||||
it doesn't find it. For most JS code you don't want that, but it's useful
|
|
||||||
if you want to strictly sanitize your code.
|
|
||||||
|
|
||||||
- =pro.ast_lift_variables(ast)= -- merge and move =var= declarations to the
|
|
||||||
scop of the scope; discard unused function arguments or variables; discard
|
|
||||||
unused (named) inner functions. It also tries to merge assignments
|
|
||||||
following the =var= declaration into it.
|
|
||||||
|
|
||||||
If your code is very hand-optimized concerning =var= declarations, this
|
|
||||||
lifting variable declarations might actually increase size. For me it
|
|
||||||
helps out. On jQuery it adds 865 bytes (243 after gzip). YMMV. Also
|
|
||||||
note that (since it's not enabled by default) this operation isn't yet
|
|
||||||
heavily tested (please report if you find issues!).
|
|
||||||
|
|
||||||
Note that although it might increase the image size (on jQuery it gains
|
|
||||||
865 bytes, 243 after gzip) it's technically more correct: in certain
|
|
||||||
situations, dead code removal might drop variable declarations, which
|
|
||||||
would not happen if the variables are lifted in advance.
|
|
||||||
|
|
||||||
Here's an example of what it does:
|
|
||||||
|
|
||||||
#+BEGIN_SRC js
|
|
||||||
function f(a, b, c, d, e) {
|
|
||||||
var q;
|
|
||||||
var w;
|
|
||||||
w = 10;
|
|
||||||
q = 20;
|
|
||||||
for (var i = 1; i < 10; ++i) {
|
|
||||||
var boo = foo(a);
|
|
||||||
}
|
|
||||||
for (var i = 0; i < 1; ++i) {
|
|
||||||
var boo = bar(c);
|
|
||||||
}
|
|
||||||
function foo(){ ... }
|
|
||||||
function bar(){ ... }
|
|
||||||
function baz(){ ... }
|
|
||||||
}
|
|
||||||
|
|
||||||
// transforms into ==>
|
|
||||||
|
|
||||||
function f(a, b, c) {
|
|
||||||
var i, boo, w = 10, q = 20;
|
|
||||||
for (i = 1; i < 10; ++i) {
|
|
||||||
boo = foo(a);
|
|
||||||
}
|
|
||||||
for (i = 0; i < 1; ++i) {
|
|
||||||
boo = bar(c);
|
|
||||||
}
|
|
||||||
function foo() { ... }
|
|
||||||
function bar() { ... }
|
|
||||||
}
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
- =pro.ast_mangle(ast, options)= -- generates a new AST containing mangled
|
|
||||||
(compressed) variable and function names. It supports the following
|
|
||||||
options:
|
|
||||||
|
|
||||||
- =toplevel= -- mangle toplevel names (by default we don't touch them).
|
|
||||||
- =except= -- an array of names to exclude from compression.
|
|
||||||
- =defines= -- an object with properties named after symbols to
|
|
||||||
replace (see the =--define= option for the script) and the values
|
|
||||||
representing the AST replacement value.
|
|
||||||
|
|
||||||
- =pro.ast_squeeze(ast, options)= -- employs further optimizations designed
|
|
||||||
to reduce the size of the code that =gen_code= would generate from the
|
|
||||||
AST. Returns a new AST. =options= can be a hash; the supported options
|
|
||||||
are:
|
|
||||||
|
|
||||||
- =make_seqs= (default true) which will cause consecutive statements in a
|
|
||||||
block to be merged using the "sequence" (comma) operator
|
|
||||||
|
|
||||||
- =dead_code= (default true) which will remove unreachable code.
|
|
||||||
|
|
||||||
- =pro.gen_code(ast, options)= -- generates JS code from the AST. By
|
|
||||||
default it's minified, but using the =options= argument you can get nicely
|
|
||||||
formatted output. =options= is, well, optional :-) and if you pass it it
|
|
||||||
must be an object and supports the following properties (below you can see
|
|
||||||
the default values):
|
|
||||||
|
|
||||||
- =beautify: false= -- pass =true= if you want indented output
|
|
||||||
- =indent_start: 0= (only applies when =beautify= is =true=) -- initial
|
|
||||||
indentation in spaces
|
|
||||||
- =indent_level: 4= (only applies when =beautify= is =true=) --
|
|
||||||
indentation level, in spaces (pass an even number)
|
|
||||||
- =quote_keys: false= -- if you pass =true= it will quote all keys in
|
|
||||||
literal objects
|
|
||||||
- =space_colon: false= (only applies when =beautify= is =true=) -- wether
|
|
||||||
to put a space before the colon in object literals
|
|
||||||
- =ascii_only: false= -- pass =true= if you want to encode non-ASCII
|
|
||||||
characters as =\uXXXX=.
|
|
||||||
- =inline_script: false= -- pass =true= to escape occurrences of
|
|
||||||
=</script= in strings
|
|
||||||
|
|
||||||
*** Beautifier shortcoming -- no more comments
|
|
||||||
|
|
||||||
The beautifier can be used as a general purpose indentation tool. It's
|
|
||||||
useful when you want to make a minified file readable. One limitation,
|
|
||||||
though, is that it discards all comments, so you don't really want to use it
|
|
||||||
to reformat your code, unless you don't have, or don't care about, comments.
|
|
||||||
|
|
||||||
In fact it's not the beautifier who discards comments --- they are dumped at
|
|
||||||
the parsing stage, when we build the initial AST. Comments don't really
|
|
||||||
make sense in the AST, and while we could add nodes for them, it would be
|
|
||||||
inconvenient because we'd have to add special rules to ignore them at all
|
|
||||||
the processing stages.
|
|
||||||
|
|
||||||
*** Use as a code pre-processor
|
|
||||||
|
|
||||||
The =--define= option can be used, particularly when combined with the
|
|
||||||
constant folding logic, as a form of pre-processor to enable or remove
|
|
||||||
particular constructions, such as might be used for instrumenting
|
|
||||||
development code, or to produce variations aimed at a specific
|
|
||||||
platform.
|
|
||||||
|
|
||||||
The code below illustrates the way this can be done, and how the
|
|
||||||
symbol replacement is performed.
|
|
||||||
|
|
||||||
#+BEGIN_SRC js
|
|
||||||
CLAUSE1: if (typeof DEVMODE === 'undefined') {
|
|
||||||
DEVMODE = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
CLAUSE2: function init() {
|
|
||||||
if (DEVMODE) {
|
|
||||||
console.log("init() called");
|
|
||||||
}
|
|
||||||
....
|
|
||||||
DEVMODE && console.log("init() complete");
|
|
||||||
}
|
|
||||||
|
|
||||||
CLAUSE3: function reportDeviceStatus(device) {
|
|
||||||
var DEVMODE = device.mode, DEVNAME = device.name;
|
|
||||||
if (DEVMODE === 'open') {
|
|
||||||
....
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#+END_SRC
|
|
||||||
|
|
||||||
When the above code is normally executed, the undeclared global
|
|
||||||
variable =DEVMODE= will be assigned the value *true* (see =CLAUSE1=)
|
|
||||||
and so the =init()= function (=CLAUSE2=) will write messages to the
|
|
||||||
console log when executed, but in =CLAUSE3= a locally declared
|
|
||||||
variable will mask access to the =DEVMODE= global symbol.
|
|
||||||
|
|
||||||
If the above code is processed by UglifyJS with an argument of
|
|
||||||
=--define DEVMODE=false= then UglifyJS will replace =DEVMODE= with the
|
|
||||||
boolean constant value *false* within =CLAUSE1= and =CLAUSE2=, but it
|
|
||||||
will leave =CLAUSE3= as it stands because there =DEVMODE= resolves to
|
|
||||||
a validly declared variable.
|
|
||||||
|
|
||||||
And more so, the constant-folding features of UglifyJS will recognise
|
|
||||||
that the =if= condition of =CLAUSE1= is thus always false, and so will
|
|
||||||
remove the test and body of =CLAUSE1= altogether (including the
|
|
||||||
otherwise slightly problematical statement =false = true;= which it
|
|
||||||
will have formed by replacing =DEVMODE= in the body). Similarly,
|
|
||||||
within =CLAUSE2= both calls to =console.log()= will be removed
|
|
||||||
altogether.
|
|
||||||
|
|
||||||
In this way you can mimic, to a limited degree, the functionality of
|
|
||||||
the C/C++ pre-processor to enable or completely remove blocks
|
|
||||||
depending on how certain symbols are defined - perhaps using UglifyJS
|
|
||||||
to generate different versions of source aimed at different
|
|
||||||
environments
|
|
||||||
|
|
||||||
It is recommmended (but not made mandatory) that symbols designed for
|
|
||||||
this purpose are given names consisting of =UPPER_CASE_LETTERS= to
|
|
||||||
distinguish them from other (normal) symbols and avoid the sort of
|
|
||||||
clash that =CLAUSE3= above illustrates.
|
|
||||||
|
|
||||||
** Compression -- how good is it?
|
|
||||||
|
|
||||||
Here are updated statistics. (I also updated my Google Closure and YUI
|
|
||||||
installations).
|
|
||||||
|
|
||||||
We're still a lot better than YUI in terms of compression, though slightly
|
|
||||||
slower. We're still a lot faster than Closure, and compression after gzip
|
|
||||||
is comparable.
|
|
||||||
|
|
||||||
| File | UglifyJS | UglifyJS+gzip | Closure | Closure+gzip | YUI | YUI+gzip |
|
|
||||||
|-----------------------------+------------------+---------------+------------------+--------------+------------------+----------|
|
|
||||||
| jquery-1.6.2.js | 91001 (0:01.59) | 31896 | 90678 (0:07.40) | 31979 | 101527 (0:01.82) | 34646 |
|
|
||||||
| paper.js | 142023 (0:01.65) | 43334 | 134301 (0:07.42) | 42495 | 173383 (0:01.58) | 48785 |
|
|
||||||
| prototype.js | 88544 (0:01.09) | 26680 | 86955 (0:06.97) | 26326 | 92130 (0:00.79) | 28624 |
|
|
||||||
| thelib-full.js (DynarchLIB) | 251939 (0:02.55) | 72535 | 249911 (0:09.05) | 72696 | 258869 (0:01.94) | 76584 |
|
|
||||||
|
|
||||||
** Bugs?
|
|
||||||
|
|
||||||
Unfortunately, for the time being there is no automated test suite. But I
|
|
||||||
ran the compressor manually on non-trivial code, and then I tested that the
|
|
||||||
generated code works as expected. A few hundred times.
|
|
||||||
|
|
||||||
DynarchLIB was started in times when there was no good JS minifier.
|
|
||||||
Therefore I was quite religious about trying to write short code manually,
|
|
||||||
and as such DL contains a lot of syntactic hacks[1] such as “foo == bar ? a
|
|
||||||
= 10 : b = 20”, though the more readable version would clearly be to use
|
|
||||||
“if/else”.
|
|
||||||
|
|
||||||
Since the parser/compressor runs fine on DL and jQuery, I'm quite confident
|
|
||||||
that it's solid enough for production use. If you can identify any bugs,
|
|
||||||
I'd love to hear about them ([[http://groups.google.com/group/uglifyjs][use the Google Group]] or email me directly).
|
|
||||||
|
|
||||||
[1] I even reported a few bugs and suggested some fixes in the original
|
|
||||||
[[http://marijn.haverbeke.nl/parse-js/][parse-js]] library, and Marijn pushed fixes literally in minutes.
|
|
||||||
|
|
||||||
** Links
|
|
||||||
|
|
||||||
- Twitter: [[http://twitter.com/UglifyJS][@UglifyJS]]
|
|
||||||
- Project at GitHub: [[http://github.com/mishoo/UglifyJS][http://github.com/mishoo/UglifyJS]]
|
|
||||||
- Google Group: [[http://groups.google.com/group/uglifyjs][http://groups.google.com/group/uglifyjs]]
|
|
||||||
- Common Lisp JS parser: [[http://marijn.haverbeke.nl/parse-js/][http://marijn.haverbeke.nl/parse-js/]]
|
|
||||||
- JS-to-Lisp compiler: [[http://github.com/marijnh/js][http://github.com/marijnh/js]]
|
|
||||||
- Common Lisp JS uglifier: [[http://github.com/mishoo/cl-uglify-js][http://github.com/mishoo/cl-uglify-js]]
|
|
||||||
|
|
||||||
** License
|
|
||||||
|
|
||||||
UglifyJS is released under the BSD license:
|
|
||||||
|
|
||||||
#+BEGIN_EXAMPLE
|
|
||||||
Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
|
||||||
Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials
|
|
||||||
provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
|
|
||||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
|
|
||||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
|
||||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
|
||||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
||||||
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
SUCH DAMAGE.
|
|
||||||
#+END_EXAMPLE
|
|
|
@ -1,317 +0,0 @@
|
||||||
#! /usr/bin/env node
|
|
||||||
// -*- js -*-
|
|
||||||
|
|
||||||
global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
|
|
||||||
var fs = require("fs");
|
|
||||||
var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js
|
|
||||||
jsp = uglify.parser,
|
|
||||||
pro = uglify.uglify;
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
ast: false,
|
|
||||||
mangle: true,
|
|
||||||
mangle_toplevel: false,
|
|
||||||
squeeze: true,
|
|
||||||
make_seqs: true,
|
|
||||||
dead_code: true,
|
|
||||||
verbose: false,
|
|
||||||
show_copyright: true,
|
|
||||||
out_same_file: false,
|
|
||||||
max_line_length: 32 * 1024,
|
|
||||||
unsafe: false,
|
|
||||||
reserved_names: null,
|
|
||||||
defines: { },
|
|
||||||
lift_vars: false,
|
|
||||||
codegen_options: {
|
|
||||||
ascii_only: false,
|
|
||||||
beautify: false,
|
|
||||||
indent_level: 4,
|
|
||||||
indent_start: 0,
|
|
||||||
quote_keys: false,
|
|
||||||
space_colon: false,
|
|
||||||
inline_script: false
|
|
||||||
},
|
|
||||||
make: false,
|
|
||||||
output: true // stdout
|
|
||||||
};
|
|
||||||
|
|
||||||
var args = jsp.slice(process.argv, 2);
|
|
||||||
var filename;
|
|
||||||
|
|
||||||
out: while (args.length > 0) {
|
|
||||||
var v = args.shift();
|
|
||||||
switch (v) {
|
|
||||||
case "-b":
|
|
||||||
case "--beautify":
|
|
||||||
options.codegen_options.beautify = true;
|
|
||||||
break;
|
|
||||||
case "-i":
|
|
||||||
case "--indent":
|
|
||||||
options.codegen_options.indent_level = args.shift();
|
|
||||||
break;
|
|
||||||
case "-q":
|
|
||||||
case "--quote-keys":
|
|
||||||
options.codegen_options.quote_keys = true;
|
|
||||||
break;
|
|
||||||
case "-mt":
|
|
||||||
case "--mangle-toplevel":
|
|
||||||
options.mangle_toplevel = true;
|
|
||||||
break;
|
|
||||||
case "--no-mangle":
|
|
||||||
case "-nm":
|
|
||||||
options.mangle = false;
|
|
||||||
break;
|
|
||||||
case "--no-squeeze":
|
|
||||||
case "-ns":
|
|
||||||
options.squeeze = false;
|
|
||||||
break;
|
|
||||||
case "--no-seqs":
|
|
||||||
options.make_seqs = false;
|
|
||||||
break;
|
|
||||||
case "--no-dead-code":
|
|
||||||
options.dead_code = false;
|
|
||||||
break;
|
|
||||||
case "--no-copyright":
|
|
||||||
case "-nc":
|
|
||||||
options.show_copyright = false;
|
|
||||||
break;
|
|
||||||
case "-o":
|
|
||||||
case "--output":
|
|
||||||
options.output = args.shift();
|
|
||||||
break;
|
|
||||||
case "--overwrite":
|
|
||||||
options.out_same_file = true;
|
|
||||||
break;
|
|
||||||
case "-v":
|
|
||||||
case "--verbose":
|
|
||||||
options.verbose = true;
|
|
||||||
break;
|
|
||||||
case "--ast":
|
|
||||||
options.ast = true;
|
|
||||||
break;
|
|
||||||
case "--unsafe":
|
|
||||||
options.unsafe = true;
|
|
||||||
break;
|
|
||||||
case "--max-line-len":
|
|
||||||
options.max_line_length = parseInt(args.shift(), 10);
|
|
||||||
break;
|
|
||||||
case "--reserved-names":
|
|
||||||
options.reserved_names = args.shift().split(",");
|
|
||||||
break;
|
|
||||||
case "--lift-vars":
|
|
||||||
options.lift_vars = true;
|
|
||||||
break;
|
|
||||||
case "-d":
|
|
||||||
case "--define":
|
|
||||||
var defarg = args.shift();
|
|
||||||
try {
|
|
||||||
var defsym = function(sym) {
|
|
||||||
// KEYWORDS_ATOM doesn't include NaN or Infinity - should we check
|
|
||||||
// for them too ?? We don't check reserved words and the like as the
|
|
||||||
// define values are only substituted AFTER parsing
|
|
||||||
if (jsp.KEYWORDS_ATOM.hasOwnProperty(sym)) {
|
|
||||||
throw "Don't define values for inbuilt constant '"+sym+"'";
|
|
||||||
}
|
|
||||||
return sym;
|
|
||||||
},
|
|
||||||
defval = function(v) {
|
|
||||||
if (v.match(/^"(.*)"$/) || v.match(/^'(.*)'$/)) {
|
|
||||||
return [ "string", RegExp.$1 ];
|
|
||||||
}
|
|
||||||
else if (!isNaN(parseFloat(v))) {
|
|
||||||
return [ "num", parseFloat(v) ];
|
|
||||||
}
|
|
||||||
else if (v.match(/^[a-z\$_][a-z\$_0-9]*$/i)) {
|
|
||||||
return [ "name", v ];
|
|
||||||
}
|
|
||||||
else if (!v.match(/"/)) {
|
|
||||||
return [ "string", v ];
|
|
||||||
}
|
|
||||||
else if (!v.match(/'/)) {
|
|
||||||
return [ "string", v ];
|
|
||||||
}
|
|
||||||
throw "Can't understand the specified value: "+v;
|
|
||||||
};
|
|
||||||
if (defarg.match(/^([a-z_\$][a-z_\$0-9]*)(=(.*))?$/i)) {
|
|
||||||
var sym = defsym(RegExp.$1),
|
|
||||||
val = RegExp.$2 ? defval(RegExp.$2.substr(1)) : [ 'name', 'true' ];
|
|
||||||
options.defines[sym] = val;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw "The --define option expects SYMBOL[=value]";
|
|
||||||
}
|
|
||||||
} catch(ex) {
|
|
||||||
sys.print("ERROR: In option --define "+defarg+"\n"+ex+"\n");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "--define-from-module":
|
|
||||||
var defmodarg = args.shift(),
|
|
||||||
defmodule = require(defmodarg),
|
|
||||||
sym,
|
|
||||||
val;
|
|
||||||
for (sym in defmodule) {
|
|
||||||
if (defmodule.hasOwnProperty(sym)) {
|
|
||||||
options.defines[sym] = function(val) {
|
|
||||||
if (typeof val == "string")
|
|
||||||
return [ "string", val ];
|
|
||||||
if (typeof val == "number")
|
|
||||||
return [ "num", val ];
|
|
||||||
if (val === true)
|
|
||||||
return [ 'name', 'true' ];
|
|
||||||
if (val === false)
|
|
||||||
return [ 'name', 'false' ];
|
|
||||||
if (val === null)
|
|
||||||
return [ 'name', 'null' ];
|
|
||||||
if (val === undefined)
|
|
||||||
return [ 'name', 'undefined' ];
|
|
||||||
sys.print("ERROR: In option --define-from-module "+defmodarg+"\n");
|
|
||||||
sys.print("ERROR: Unknown object type for: "+sym+"="+val+"\n");
|
|
||||||
process.exit(1);
|
|
||||||
return null;
|
|
||||||
}(defmodule[sym]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "--ascii":
|
|
||||||
options.codegen_options.ascii_only = true;
|
|
||||||
break;
|
|
||||||
case "--make":
|
|
||||||
options.make = true;
|
|
||||||
break;
|
|
||||||
case "--inline-script":
|
|
||||||
options.codegen_options.inline_script = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
filename = v;
|
|
||||||
break out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.verbose) {
|
|
||||||
pro.set_logger(function(msg){
|
|
||||||
sys.debug(msg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
jsp.set_logger(function(msg){
|
|
||||||
sys.debug(msg);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (options.make) {
|
|
||||||
options.out_same_file = false; // doesn't make sense in this case
|
|
||||||
var makefile = JSON.parse(fs.readFileSync(filename || "Makefile.uglify.js").toString());
|
|
||||||
output(makefile.files.map(function(file){
|
|
||||||
var code = fs.readFileSync(file.name);
|
|
||||||
if (file.module) {
|
|
||||||
code = "!function(exports, global){global = this;\n" + code + "\n;this." + file.module + " = exports;}({})";
|
|
||||||
}
|
|
||||||
else if (file.hide) {
|
|
||||||
code = "(function(){" + code + "}());";
|
|
||||||
}
|
|
||||||
return squeeze_it(code);
|
|
||||||
}).join("\n"));
|
|
||||||
}
|
|
||||||
else if (filename) {
|
|
||||||
fs.readFile(filename, "utf8", function(err, text){
|
|
||||||
if (err) throw err;
|
|
||||||
output(squeeze_it(text));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var stdin = process.openStdin();
|
|
||||||
stdin.setEncoding("utf8");
|
|
||||||
var text = "";
|
|
||||||
stdin.on("data", function(chunk){
|
|
||||||
text += chunk;
|
|
||||||
});
|
|
||||||
stdin.on("end", function() {
|
|
||||||
output(squeeze_it(text));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function output(text) {
|
|
||||||
var out;
|
|
||||||
if (options.out_same_file && filename)
|
|
||||||
options.output = filename;
|
|
||||||
if (options.output === true) {
|
|
||||||
out = process.stdout;
|
|
||||||
} else {
|
|
||||||
out = fs.createWriteStream(options.output, {
|
|
||||||
flags: "w",
|
|
||||||
encoding: "utf8",
|
|
||||||
mode: 0644
|
|
||||||
});
|
|
||||||
}
|
|
||||||
out.write(text + ";");
|
|
||||||
if (options.output !== true) {
|
|
||||||
out.end();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// --------- main ends here.
|
|
||||||
|
|
||||||
function show_copyright(comments) {
|
|
||||||
var ret = "";
|
|
||||||
for (var i = 0; i < comments.length; ++i) {
|
|
||||||
var c = comments[i];
|
|
||||||
if (c.type == "comment1") {
|
|
||||||
ret += "//" + c.value + "\n";
|
|
||||||
} else {
|
|
||||||
ret += "/*" + c.value + "*/";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
function squeeze_it(code) {
|
|
||||||
var result = "";
|
|
||||||
if (options.show_copyright) {
|
|
||||||
var tok = jsp.tokenizer(code), c;
|
|
||||||
c = tok();
|
|
||||||
result += show_copyright(c.comments_before);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
var ast = time_it("parse", function(){ return jsp.parse(code); });
|
|
||||||
if (options.lift_vars) {
|
|
||||||
ast = time_it("lift", function(){ return pro.ast_lift_variables(ast); });
|
|
||||||
}
|
|
||||||
if (options.mangle) ast = time_it("mangle", function(){
|
|
||||||
return pro.ast_mangle(ast, {
|
|
||||||
toplevel: options.mangle_toplevel,
|
|
||||||
defines: options.defines,
|
|
||||||
except: options.reserved_names
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (options.squeeze) ast = time_it("squeeze", function(){
|
|
||||||
ast = pro.ast_squeeze(ast, {
|
|
||||||
make_seqs : options.make_seqs,
|
|
||||||
dead_code : options.dead_code,
|
|
||||||
keep_comps : !options.unsafe
|
|
||||||
});
|
|
||||||
if (options.unsafe)
|
|
||||||
ast = pro.ast_squeeze_more(ast);
|
|
||||||
return ast;
|
|
||||||
});
|
|
||||||
if (options.ast)
|
|
||||||
return sys.inspect(ast, null, null);
|
|
||||||
result += time_it("generate", function(){ return pro.gen_code(ast, options.codegen_options) });
|
|
||||||
if (!options.codegen_options.beautify && options.max_line_length) {
|
|
||||||
result = time_it("split", function(){ return pro.split_lines(result, options.max_line_length) });
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} catch(ex) {
|
|
||||||
sys.debug(ex.stack);
|
|
||||||
sys.debug(sys.inspect(ex));
|
|
||||||
sys.debug(JSON.stringify(ex));
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function time_it(name, cont) {
|
|
||||||
if (!options.verbose)
|
|
||||||
return cont();
|
|
||||||
var t1 = new Date().getTime();
|
|
||||||
try { return cont(); }
|
|
||||||
finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
|
|
||||||
};
|
|
|
@ -1,75 +0,0 @@
|
||||||
html { font-family: "Lucida Grande","Trebuchet MS",sans-serif; font-size: 12pt; }
|
|
||||||
body { max-width: 60em; }
|
|
||||||
.title { text-align: center; }
|
|
||||||
.todo { color: red; }
|
|
||||||
.done { color: green; }
|
|
||||||
.tag { background-color:lightblue; font-weight:normal }
|
|
||||||
.target { }
|
|
||||||
.timestamp { color: grey }
|
|
||||||
.timestamp-kwd { color: CadetBlue }
|
|
||||||
p.verse { margin-left: 3% }
|
|
||||||
pre {
|
|
||||||
border: 1pt solid #AEBDCC;
|
|
||||||
background-color: #F3F5F7;
|
|
||||||
padding: 5pt;
|
|
||||||
font-family: monospace;
|
|
||||||
font-size: 90%;
|
|
||||||
overflow:auto;
|
|
||||||
}
|
|
||||||
pre.src {
|
|
||||||
background-color: #eee; color: #112; border: 1px solid #000;
|
|
||||||
}
|
|
||||||
table { border-collapse: collapse; }
|
|
||||||
td, th { vertical-align: top; }
|
|
||||||
dt { font-weight: bold; }
|
|
||||||
div.figure { padding: 0.5em; }
|
|
||||||
div.figure p { text-align: center; }
|
|
||||||
.linenr { font-size:smaller }
|
|
||||||
.code-highlighted {background-color:#ffff00;}
|
|
||||||
.org-info-js_info-navigation { border-style:none; }
|
|
||||||
#org-info-js_console-label { font-size:10px; font-weight:bold;
|
|
||||||
white-space:nowrap; }
|
|
||||||
.org-info-js_search-highlight {background-color:#ffff00; color:#000000;
|
|
||||||
font-weight:bold; }
|
|
||||||
|
|
||||||
sup {
|
|
||||||
vertical-align: baseline;
|
|
||||||
position: relative;
|
|
||||||
top: -0.5em;
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup a:link, sup a:visited {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #c00;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup a:before { content: "["; color: #999; }
|
|
||||||
sup a:after { content: "]"; color: #999; }
|
|
||||||
|
|
||||||
h1.title { border-bottom: 4px solid #000; padding-bottom: 5px; margin-bottom: 2em; }
|
|
||||||
|
|
||||||
#postamble {
|
|
||||||
color: #777;
|
|
||||||
font-size: 90%;
|
|
||||||
padding-top: 1em; padding-bottom: 1em; border-top: 1px solid #999;
|
|
||||||
margin-top: 2em;
|
|
||||||
padding-left: 2em;
|
|
||||||
padding-right: 2em;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
#postamble p { margin: 0; }
|
|
||||||
|
|
||||||
#footnotes { border-top: 1px solid #000; }
|
|
||||||
|
|
||||||
h1 { font-size: 200% }
|
|
||||||
h2 { font-size: 175% }
|
|
||||||
h3 { font-size: 150% }
|
|
||||||
h4 { font-size: 125% }
|
|
||||||
|
|
||||||
h1, h2, h3, h4 { font-family: "Bookman",Georgia,"Times New Roman",serif; font-weight: normal; }
|
|
||||||
|
|
||||||
@media print {
|
|
||||||
html { font-size: 11pt; }
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
var jsp = require("./parse-js"),
|
|
||||||
pro = require("./process");
|
|
||||||
|
|
||||||
var BY_TYPE = {};
|
|
||||||
|
|
||||||
function HOP(obj, prop) {
|
|
||||||
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
||||||
};
|
|
||||||
|
|
||||||
function AST_Node(parent) {
|
|
||||||
this.parent = parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
AST_Node.prototype.init = function(){};
|
|
||||||
|
|
||||||
function DEFINE_NODE_CLASS(type, props, methods) {
|
|
||||||
var base = methods && methods.BASE || AST_Node;
|
|
||||||
if (!base) base = AST_Node;
|
|
||||||
function D(parent, data) {
|
|
||||||
base.apply(this, arguments);
|
|
||||||
if (props) props.forEach(function(name, i){
|
|
||||||
this["_" + name] = data[i];
|
|
||||||
});
|
|
||||||
this.init();
|
|
||||||
};
|
|
||||||
var P = D.prototype = new AST_Node;
|
|
||||||
P.node_type = function(){ return type };
|
|
||||||
if (props) props.forEach(function(name){
|
|
||||||
var propname = "_" + name;
|
|
||||||
P["set_" + name] = function(val) {
|
|
||||||
this[propname] = val;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
P["get_" + name] = function() {
|
|
||||||
return this[propname];
|
|
||||||
};
|
|
||||||
});
|
|
||||||
if (type != null) BY_TYPE[type] = D;
|
|
||||||
if (methods) for (var i in methods) if (HOP(methods, i)) {
|
|
||||||
P[i] = methods[i];
|
|
||||||
}
|
|
||||||
return D;
|
|
||||||
};
|
|
||||||
|
|
||||||
var AST_String_Node = DEFINE_NODE_CLASS("string", ["value"]);
|
|
||||||
var AST_Number_Node = DEFINE_NODE_CLASS("num", ["value"]);
|
|
||||||
var AST_Name_Node = DEFINE_NODE_CLASS("name", ["value"]);
|
|
||||||
|
|
||||||
var AST_Statlist_Node = DEFINE_NODE_CLASS(null, ["body"]);
|
|
||||||
var AST_Root_Node = DEFINE_NODE_CLASS("toplevel", null, { BASE: AST_Statlist_Node });
|
|
||||||
var AST_Block_Node = DEFINE_NODE_CLASS("block", null, { BASE: AST_Statlist_Node });
|
|
||||||
var AST_Splice_Node = DEFINE_NODE_CLASS("splice", null, { BASE: AST_Statlist_Node });
|
|
||||||
|
|
||||||
var AST_Var_Node = DEFINE_NODE_CLASS("var", ["definitions"]);
|
|
||||||
var AST_Const_Node = DEFINE_NODE_CLASS("const", ["definitions"]);
|
|
||||||
|
|
||||||
var AST_Try_Node = DEFINE_NODE_CLASS("try", ["body", "catch", "finally"]);
|
|
||||||
var AST_Throw_Node = DEFINE_NODE_CLASS("throw", ["exception"]);
|
|
||||||
|
|
||||||
var AST_New_Node = DEFINE_NODE_CLASS("new", ["constructor", "arguments"]);
|
|
||||||
|
|
||||||
var AST_Switch_Node = DEFINE_NODE_CLASS("switch", ["expression", "branches"]);
|
|
||||||
var AST_Switch_Branch_Node = DEFINE_NODE_CLASS(null, ["expression", "body"]);
|
|
||||||
|
|
||||||
var AST_Break_Node = DEFINE_NODE_CLASS("break", ["label"]);
|
|
||||||
var AST_Continue_Node = DEFINE_NODE_CLASS("continue", ["label"]);
|
|
||||||
var AST_Assign_Node = DEFINE_NODE_CLASS("assign", ["operator", "lvalue", "rvalue"]);
|
|
||||||
var AST_Dot_Node = DEFINE_NODE_CLASS("dot", ["expression", "name"]);
|
|
||||||
var AST_Call_Node = DEFINE_NODE_CLASS("call", ["function", "arguments"]);
|
|
||||||
|
|
||||||
var AST_Lambda_Node = DEFINE_NODE_CLASS(null, ["name", "arguments", "body"])
|
|
||||||
var AST_Function_Node = DEFINE_NODE_CLASS("function", null, AST_Lambda_Node);
|
|
||||||
var AST_Defun_Node = DEFINE_NODE_CLASS("defun", null, AST_Lambda_Node);
|
|
||||||
|
|
||||||
var AST_If_Node = DEFINE_NODE_CLASS("if", ["condition", "then", "else"]);
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,51 +0,0 @@
|
||||||
var jsp = require("./parse-js"),
|
|
||||||
pro = require("./process"),
|
|
||||||
slice = jsp.slice,
|
|
||||||
member = jsp.member,
|
|
||||||
curry = jsp.curry,
|
|
||||||
MAP = pro.MAP,
|
|
||||||
PRECEDENCE = jsp.PRECEDENCE,
|
|
||||||
OPERATORS = jsp.OPERATORS;
|
|
||||||
|
|
||||||
function ast_squeeze_more(ast) {
|
|
||||||
var w = pro.ast_walker(), walk = w.walk, scope;
|
|
||||||
function with_scope(s, cont) {
|
|
||||||
var save = scope, ret;
|
|
||||||
scope = s;
|
|
||||||
ret = cont();
|
|
||||||
scope = save;
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
function _lambda(name, args, body) {
|
|
||||||
return [ this[0], name, args, with_scope(body.scope, curry(MAP, body, walk)) ];
|
|
||||||
};
|
|
||||||
return w.with_walkers({
|
|
||||||
"toplevel": function(body) {
|
|
||||||
return [ this[0], with_scope(this.scope, curry(MAP, body, walk)) ];
|
|
||||||
},
|
|
||||||
"function": _lambda,
|
|
||||||
"defun": _lambda,
|
|
||||||
"new": function(ctor, args) {
|
|
||||||
if (ctor[0] == "name" && ctor[1] == "Array" && !scope.has("Array")) {
|
|
||||||
if (args.length != 1) {
|
|
||||||
return [ "array", args ];
|
|
||||||
} else {
|
|
||||||
return walk([ "call", [ "name", "Array" ], args ]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"call": function(expr, args) {
|
|
||||||
if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
|
|
||||||
// foo.toString() ==> foo+""
|
|
||||||
return [ "binary", "+", expr[1], [ "string", "" ]];
|
|
||||||
}
|
|
||||||
if (expr[0] == "name" && expr[1] == "Array" && args.length != 1 && !scope.has("Array")) {
|
|
||||||
return [ "array", args ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, function() {
|
|
||||||
return walk(pro.ast_add_scope(ast));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.ast_squeeze_more = ast_squeeze_more;
|
|
File diff suppressed because one or more lines are too long
|
@ -1,24 +0,0 @@
|
||||||
{
|
|
||||||
"name" : "uglify-js",
|
|
||||||
|
|
||||||
"description" : "JavaScript parser and compressor/beautifier toolkit",
|
|
||||||
|
|
||||||
"author" : {
|
|
||||||
"name" : "Mihai Bazon",
|
|
||||||
"email" : "mihai.bazon@gmail.com",
|
|
||||||
"url" : "http://mihai.bazon.net/blog"
|
|
||||||
},
|
|
||||||
|
|
||||||
"version" : "1.1.0",
|
|
||||||
|
|
||||||
"main" : "./uglify-js.js",
|
|
||||||
|
|
||||||
"bin" : {
|
|
||||||
"uglifyjs" : "./bin/uglifyjs"
|
|
||||||
},
|
|
||||||
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git@github.com:mishoo/UglifyJS.git"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
#! /usr/bin/env node
|
|
||||||
|
|
||||||
global.sys = require("sys");
|
|
||||||
var fs = require("fs");
|
|
||||||
|
|
||||||
var jsp = require("../lib/parse-js");
|
|
||||||
var pro = require("../lib/process");
|
|
||||||
|
|
||||||
var filename = process.argv[2];
|
|
||||||
fs.readFile(filename, "utf8", function(err, text){
|
|
||||||
try {
|
|
||||||
var ast = time_it("parse", function(){ return jsp.parse(text); });
|
|
||||||
ast = time_it("mangle", function(){ return pro.ast_mangle(ast); });
|
|
||||||
ast = time_it("squeeze", function(){ return pro.ast_squeeze(ast); });
|
|
||||||
var gen = time_it("generate", function(){ return pro.gen_code(ast, false); });
|
|
||||||
sys.puts(gen);
|
|
||||||
} catch(ex) {
|
|
||||||
sys.debug(ex.stack);
|
|
||||||
sys.debug(sys.inspect(ex));
|
|
||||||
sys.debug(JSON.stringify(ex));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function time_it(name, cont) {
|
|
||||||
var t1 = new Date().getTime();
|
|
||||||
try { return cont(); }
|
|
||||||
finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
|
|
||||||
};
|
|
|
@ -1,403 +0,0 @@
|
||||||
#! /usr/bin/env node
|
|
||||||
|
|
||||||
var parseJS = require("../lib/parse-js");
|
|
||||||
var sys = require("sys");
|
|
||||||
|
|
||||||
// write debug in a very straightforward manner
|
|
||||||
var debug = function(){
|
|
||||||
sys.log(Array.prototype.slice.call(arguments).join(', '));
|
|
||||||
};
|
|
||||||
|
|
||||||
ParserTestSuite(function(i, input, desc){
|
|
||||||
try {
|
|
||||||
parseJS.parse(input);
|
|
||||||
debug("ok " + i + ": " + desc);
|
|
||||||
} catch(e){
|
|
||||||
debug("FAIL " + i + " " + desc + " (" + e + ")");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function ParserTestSuite(callback){
|
|
||||||
var inps = [
|
|
||||||
["var abc;", "Regular variable statement w/o assignment"],
|
|
||||||
["var abc = 5;", "Regular variable statement with assignment"],
|
|
||||||
["/* */;", "Multiline comment"],
|
|
||||||
['/** **/;', 'Double star multiline comment'],
|
|
||||||
["var f = function(){;};", "Function expression in var assignment"],
|
|
||||||
['hi; // moo\n;', 'single line comment'],
|
|
||||||
['var varwithfunction;', 'Dont match keywords as substrings'], // difference between `var withsomevar` and `"str"` (local search and lits)
|
|
||||||
['a + b;', 'addition'],
|
|
||||||
["'a';", 'single string literal'],
|
|
||||||
["'a\\n';", 'single string literal with escaped return'],
|
|
||||||
['"a";', 'double string literal'],
|
|
||||||
['"a\\n";', 'double string literal with escaped return'],
|
|
||||||
['"var";', 'string is a keyword'],
|
|
||||||
['"variable";', 'string starts with a keyword'],
|
|
||||||
['"somevariable";', 'string contains a keyword'],
|
|
||||||
['"somevar";', 'string ends with a keyword'],
|
|
||||||
['500;', 'int literal'],
|
|
||||||
['500.;', 'float literal w/o decimals'],
|
|
||||||
['500.432;', 'float literal with decimals'],
|
|
||||||
['.432432;', 'float literal w/o int'],
|
|
||||||
['(a,b,c);', 'parens and comma'],
|
|
||||||
['[1,2,abc];', 'array literal'],
|
|
||||||
['var o = {a:1};', 'object literal unquoted key'],
|
|
||||||
['var o = {"b":2};', 'object literal quoted key'], // opening curly may not be at the start of a statement...
|
|
||||||
['var o = {c:c};', 'object literal keyname is identifier'],
|
|
||||||
['var o = {a:1,"b":2,c:c};', 'object literal combinations'],
|
|
||||||
['var x;\nvar y;', 'two lines'],
|
|
||||||
['var x;\nfunction n(){; }', 'function def'],
|
|
||||||
['var x;\nfunction n(abc){; }', 'function def with arg'],
|
|
||||||
['var x;\nfunction n(abc, def){ ;}', 'function def with args'],
|
|
||||||
['function n(){ "hello"; }', 'function def with body'],
|
|
||||||
['/a/;', 'regex literal'],
|
|
||||||
['/a/b;', 'regex literal with flag'],
|
|
||||||
['/a/ / /b/;', 'regex div regex'],
|
|
||||||
['a/b/c;', 'triple division looks like regex'],
|
|
||||||
['+function(){/regex/;};', 'regex at start of function body'],
|
|
||||||
// http://code.google.com/p/es-lab/source/browse/trunk/tests/parser/parsertests.js?r=86
|
|
||||||
// http://code.google.com/p/es-lab/source/browse/trunk/tests/parser/parsertests.js?r=430
|
|
||||||
|
|
||||||
// first tests for the lexer, should also parse as program (when you append a semi)
|
|
||||||
|
|
||||||
// comments
|
|
||||||
['//foo!@#^&$1234\nbar;', 'single line comment'],
|
|
||||||
['/* abcd!@#@$* { } && null*/;', 'single line multi line comment'],
|
|
||||||
['/*foo\nbar*/;','multi line comment'],
|
|
||||||
['/*x*x*/;','multi line comment with *'],
|
|
||||||
['/**/;','empty comment'],
|
|
||||||
// identifiers
|
|
||||||
["x;",'1 identifier'],
|
|
||||||
["_x;",'2 identifier'],
|
|
||||||
["xyz;",'3 identifier'],
|
|
||||||
["$x;",'4 identifier'],
|
|
||||||
["x$;",'5 identifier'],
|
|
||||||
["_;",'6 identifier'],
|
|
||||||
["x5;",'7 identifier'],
|
|
||||||
["x_y;",'8 identifier'],
|
|
||||||
["x+5;",'9 identifier'],
|
|
||||||
["xyz123;",'10 identifier'],
|
|
||||||
["x1y1z1;",'11 identifier'],
|
|
||||||
["foo\\u00D8bar;",'12 identifier unicode escape'],
|
|
||||||
//["foo<6F>bar;",'13 identifier unicode embedded (might fail)'],
|
|
||||||
// numbers
|
|
||||||
["5;", '1 number'],
|
|
||||||
["5.5;", '2 number'],
|
|
||||||
["0;", '3 number'],
|
|
||||||
["0.0;", '4 number'],
|
|
||||||
["0.001;", '5 number'],
|
|
||||||
["1.e2;", '6 number'],
|
|
||||||
["1.e-2;", '7 number'],
|
|
||||||
["1.E2;", '8 number'],
|
|
||||||
["1.E-2;", '9 number'],
|
|
||||||
[".5;", '10 number'],
|
|
||||||
[".5e3;", '11 number'],
|
|
||||||
[".5e-3;", '12 number'],
|
|
||||||
["0.5e3;", '13 number'],
|
|
||||||
["55;", '14 number'],
|
|
||||||
["123;", '15 number'],
|
|
||||||
["55.55;", '16 number'],
|
|
||||||
["55.55e10;", '17 number'],
|
|
||||||
["123.456;", '18 number'],
|
|
||||||
["1+e;", '20 number'],
|
|
||||||
["0x01;", '22 number'],
|
|
||||||
["0XCAFE;", '23 number'],
|
|
||||||
["0x12345678;", '24 number'],
|
|
||||||
["0x1234ABCD;", '25 number'],
|
|
||||||
["0x0001;", '26 number'],
|
|
||||||
// strings
|
|
||||||
["\"foo\";", '1 string'],
|
|
||||||
["\'foo\';", '2 string'],
|
|
||||||
["\"x\";", '3 string'],
|
|
||||||
["\'\';", '4 string'],
|
|
||||||
["\"foo\\tbar\";", '5 string'],
|
|
||||||
["\"!@#$%^&*()_+{}[]\";", '6 string'],
|
|
||||||
["\"/*test*/\";", '7 string'],
|
|
||||||
["\"//test\";", '8 string'],
|
|
||||||
["\"\\\\\";", '9 string'],
|
|
||||||
["\"\\u0001\";", '10 string'],
|
|
||||||
["\"\\uFEFF\";", '11 string'],
|
|
||||||
["\"\\u10002\";", '12 string'],
|
|
||||||
["\"\\x55\";", '13 string'],
|
|
||||||
["\"\\x55a\";", '14 string'],
|
|
||||||
["\"a\\\\nb\";", '15 string'],
|
|
||||||
['";"', '16 string: semi in a string'],
|
|
||||||
['"a\\\nb";', '17 string: line terminator escape'],
|
|
||||||
// literals
|
|
||||||
["null;", "null"],
|
|
||||||
["true;", "true"],
|
|
||||||
["false;", "false"],
|
|
||||||
// regex
|
|
||||||
["/a/;", "1 regex"],
|
|
||||||
["/abc/;", "2 regex"],
|
|
||||||
["/abc[a-z]*def/g;", "3 regex"],
|
|
||||||
["/\\b/;", "4 regex"],
|
|
||||||
["/[a-zA-Z]/;", "5 regex"],
|
|
||||||
|
|
||||||
// program tests (for as far as they havent been covered above)
|
|
||||||
|
|
||||||
// regexp
|
|
||||||
["/foo(.*)/g;", "another regexp"],
|
|
||||||
// arrays
|
|
||||||
["[];", "1 array"],
|
|
||||||
["[ ];", "2 array"],
|
|
||||||
["[1];", "3 array"],
|
|
||||||
["[1,2];", "4 array"],
|
|
||||||
["[1,2,,];", "5 array"],
|
|
||||||
["[1,2,3];", "6 array"],
|
|
||||||
["[1,2,3,,,];", "7 array"],
|
|
||||||
// objects
|
|
||||||
["{};", "1 object"],
|
|
||||||
["({x:5});", "2 object"],
|
|
||||||
["({x:5,y:6});", "3 object"],
|
|
||||||
["({x:5,});", "4 object"],
|
|
||||||
["({if:5});", "5 object"],
|
|
||||||
["({ get x() {42;} });", "6 object"],
|
|
||||||
["({ set y(a) {1;} });", "7 object"],
|
|
||||||
// member expression
|
|
||||||
["o.m;", "1 member expression"],
|
|
||||||
["o['m'];", "2 member expression"],
|
|
||||||
["o['n']['m'];", "3 member expression"],
|
|
||||||
["o.n.m;", "4 member expression"],
|
|
||||||
["o.if;", "5 member expression"],
|
|
||||||
// call and invoke expressions
|
|
||||||
["f();", "1 call/invoke expression"],
|
|
||||||
["f(x);", "2 call/invoke expression"],
|
|
||||||
["f(x,y);", "3 call/invoke expression"],
|
|
||||||
["o.m();", "4 call/invoke expression"],
|
|
||||||
["o['m'];", "5 call/invoke expression"],
|
|
||||||
["o.m(x);", "6 call/invoke expression"],
|
|
||||||
["o['m'](x);", "7 call/invoke expression"],
|
|
||||||
["o.m(x,y);", "8 call/invoke expression"],
|
|
||||||
["o['m'](x,y);", "9 call/invoke expression"],
|
|
||||||
["f(x)(y);", "10 call/invoke expression"],
|
|
||||||
["f().x;", "11 call/invoke expression"],
|
|
||||||
|
|
||||||
// eval
|
|
||||||
["eval('x');", "1 eval"],
|
|
||||||
["(eval)('x');", "2 eval"],
|
|
||||||
["(1,eval)('x');", "3 eval"],
|
|
||||||
["eval(x,y);", "4 eval"],
|
|
||||||
// new expression
|
|
||||||
["new f();", "1 new expression"],
|
|
||||||
["new o;", "2 new expression"],
|
|
||||||
["new o.m;", "3 new expression"],
|
|
||||||
["new o.m(x);", "4 new expression"],
|
|
||||||
["new o.m(x,y);", "5 new expression"],
|
|
||||||
// prefix/postfix
|
|
||||||
["++x;", "1 pre/postfix"],
|
|
||||||
["x++;", "2 pre/postfix"],
|
|
||||||
["--x;", "3 pre/postfix"],
|
|
||||||
["x--;", "4 pre/postfix"],
|
|
||||||
["x ++;", "5 pre/postfix"],
|
|
||||||
["x /* comment */ ++;", "6 pre/postfix"],
|
|
||||||
["++ /* comment */ x;", "7 pre/postfix"],
|
|
||||||
// unary operators
|
|
||||||
["delete x;", "1 unary operator"],
|
|
||||||
["void x;", "2 unary operator"],
|
|
||||||
["+ x;", "3 unary operator"],
|
|
||||||
["-x;", "4 unary operator"],
|
|
||||||
["~x;", "5 unary operator"],
|
|
||||||
["!x;", "6 unary operator"],
|
|
||||||
// meh
|
|
||||||
["new Date++;", "new date ++"],
|
|
||||||
["+x++;", " + x ++"],
|
|
||||||
// expression expressions
|
|
||||||
["1 * 2;", "1 expression expressions"],
|
|
||||||
["1 / 2;", "2 expression expressions"],
|
|
||||||
["1 % 2;", "3 expression expressions"],
|
|
||||||
["1 + 2;", "4 expression expressions"],
|
|
||||||
["1 - 2;", "5 expression expressions"],
|
|
||||||
["1 << 2;", "6 expression expressions"],
|
|
||||||
["1 >>> 2;", "7 expression expressions"],
|
|
||||||
["1 >> 2;", "8 expression expressions"],
|
|
||||||
["1 * 2 + 3;", "9 expression expressions"],
|
|
||||||
["(1+2)*3;", "10 expression expressions"],
|
|
||||||
["1*(2+3);", "11 expression expressions"],
|
|
||||||
["x<y;", "12 expression expressions"],
|
|
||||||
["x>y;", "13 expression expressions"],
|
|
||||||
["x<=y;", "14 expression expressions"],
|
|
||||||
["x>=y;", "15 expression expressions"],
|
|
||||||
["x instanceof y;", "16 expression expressions"],
|
|
||||||
["x in y;", "17 expression expressions"],
|
|
||||||
["x&y;", "18 expression expressions"],
|
|
||||||
["x^y;", "19 expression expressions"],
|
|
||||||
["x|y;", "20 expression expressions"],
|
|
||||||
["x+y<z;", "21 expression expressions"],
|
|
||||||
["x<y+z;", "22 expression expressions"],
|
|
||||||
["x+y+z;", "23 expression expressions"],
|
|
||||||
["x+y<z;", "24 expression expressions"],
|
|
||||||
["x<y+z;", "25 expression expressions"],
|
|
||||||
["x&y|z;", "26 expression expressions"],
|
|
||||||
["x&&y;", "27 expression expressions"],
|
|
||||||
["x||y;", "28 expression expressions"],
|
|
||||||
["x&&y||z;", "29 expression expressions"],
|
|
||||||
["x||y&&z;", "30 expression expressions"],
|
|
||||||
["x<y?z:w;", "31 expression expressions"],
|
|
||||||
// assignment
|
|
||||||
["x >>>= y;", "1 assignment"],
|
|
||||||
["x <<= y;", "2 assignment"],
|
|
||||||
["x = y;", "3 assignment"],
|
|
||||||
["x += y;", "4 assignment"],
|
|
||||||
["x /= y;", "5 assignment"],
|
|
||||||
// comma
|
|
||||||
["x, y;", "comma"],
|
|
||||||
// block
|
|
||||||
["{};", "1 block"],
|
|
||||||
["{x;};", "2 block"],
|
|
||||||
["{x;y;};", "3 block"],
|
|
||||||
// vars
|
|
||||||
["var x;", "1 var"],
|
|
||||||
["var x,y;", "2 var"],
|
|
||||||
["var x=1,y=2;", "3 var"],
|
|
||||||
["var x,y=2;", "4 var"],
|
|
||||||
// empty
|
|
||||||
[";", "1 empty"],
|
|
||||||
["\n;", "2 empty"],
|
|
||||||
// expression statement
|
|
||||||
["x;", "1 expression statement"],
|
|
||||||
["5;", "2 expression statement"],
|
|
||||||
["1+2;", "3 expression statement"],
|
|
||||||
// if
|
|
||||||
["if (c) x; else y;", "1 if statement"],
|
|
||||||
["if (c) x;", "2 if statement"],
|
|
||||||
["if (c) {} else {};", "3 if statement"],
|
|
||||||
["if (c1) if (c2) s1; else s2;", "4 if statement"],
|
|
||||||
// while
|
|
||||||
["do s; while (e);", "1 while statement"],
|
|
||||||
["do { s; } while (e);", "2 while statement"],
|
|
||||||
["while (e) s;", "3 while statement"],
|
|
||||||
["while (e) { s; };", "4 while statement"],
|
|
||||||
// for
|
|
||||||
["for (;;) ;", "1 for statement"],
|
|
||||||
["for (;c;x++) x;", "2 for statement"],
|
|
||||||
["for (i;i<len;++i){};", "3 for statement"],
|
|
||||||
["for (var i=0;i<len;++i) {};", "4 for statement"],
|
|
||||||
["for (var i=0,j=0;;){};", "5 for statement"],
|
|
||||||
//["for (x in b; c; u) {};", "6 for statement"],
|
|
||||||
["for ((x in b); c; u) {};", "7 for statement"],
|
|
||||||
["for (x in a);", "8 for statement"],
|
|
||||||
["for (var x in a){};", "9 for statement"],
|
|
||||||
["for (var x=5 in a) {};", "10 for statement"],
|
|
||||||
["for (var x = a in b in c) {};", "11 for statement"],
|
|
||||||
["for (var x=function(){a+b;}; a<b; ++i) some;", "11 for statement, testing for parsingForHeader reset with the function"],
|
|
||||||
["for (var x=function(){for (x=0; x<15; ++x) alert(foo); }; a<b; ++i) some;", "11 for statement, testing for parsingForHeader reset with the function"],
|
|
||||||
// flow statements
|
|
||||||
["while(1){ continue; }", "1 flow statement"],
|
|
||||||
["label: while(1){ continue label; }", "2 flow statement"],
|
|
||||||
["while(1){ break; }", "3 flow statement"],
|
|
||||||
["somewhere: while(1){ break somewhere; }", "4 flow statement"],
|
|
||||||
["while(1){ continue /* comment */ ; }", "5 flow statement"],
|
|
||||||
["while(1){ continue \n; }", "6 flow statement"],
|
|
||||||
["(function(){ return; })()", "7 flow statement"],
|
|
||||||
["(function(){ return 0; })()", "8 flow statement"],
|
|
||||||
["(function(){ return 0 + \n 1; })()", "9 flow statement"],
|
|
||||||
// with
|
|
||||||
["with (e) s;", "with statement"],
|
|
||||||
// switch
|
|
||||||
["switch (e) { case x: s; };", "1 switch statement"],
|
|
||||||
["switch (e) { case x: s1;s2; default: s3; case y: s4; };", "2 switch statement"],
|
|
||||||
["switch (e) { default: s1; case x: s2; case y: s3; };", "3 switch statement"],
|
|
||||||
["switch (e) { default: s; };", "4 switch statement"],
|
|
||||||
["switch (e) { case x: s1; case y: s2; };", "5 switch statement"],
|
|
||||||
// labels
|
|
||||||
["foo : x;", " flow statement"],
|
|
||||||
// throw
|
|
||||||
["throw x;", "1 throw statement"],
|
|
||||||
["throw x\n;", "2 throw statement"],
|
|
||||||
// try catch finally
|
|
||||||
["try { s1; } catch (e) { s2; };", "1 trycatchfinally statement"],
|
|
||||||
["try { s1; } finally { s2; };", "2 trycatchfinally statement"],
|
|
||||||
["try { s1; } catch (e) { s2; } finally { s3; };", "3 trycatchfinally statement"],
|
|
||||||
// debugger
|
|
||||||
["debugger;", "debuger statement"],
|
|
||||||
// function decl
|
|
||||||
["function f(x) { e; return x; };", "1 function declaration"],
|
|
||||||
["function f() { x; y; };", "2 function declaration"],
|
|
||||||
["function f(x,y) { var z; return x; };", "3 function declaration"],
|
|
||||||
// function exp
|
|
||||||
["(function f(x) { return x; });", "1 function expression"],
|
|
||||||
["(function empty() {;});", "2 function expression"],
|
|
||||||
["(function empty() {;});", "3 function expression"],
|
|
||||||
["(function (x) {; });", "4 function expression"],
|
|
||||||
// program
|
|
||||||
["var x; function f(){;}; null;", "1 program"],
|
|
||||||
[";;", "2 program"],
|
|
||||||
["{ x; y; z; }", "3 program"],
|
|
||||||
["function f(){ function g(){;}};", "4 program"],
|
|
||||||
["x;\n/*foo*/\n ;", "5 program"],
|
|
||||||
|
|
||||||
// asi
|
|
||||||
["foo: while(1){ continue \n foo; }", "1 asi"],
|
|
||||||
["foo: while(1){ break \n foo; }", "2 asi"],
|
|
||||||
["(function(){ return\nfoo; })()", "3 asi"],
|
|
||||||
["var x; { 1 \n 2 } 3", "4 asi"],
|
|
||||||
["ab /* hi */\ncd", "5 asi"],
|
|
||||||
["ab/*\n*/cd", "6 asi (multi line multilinecomment counts as eol)"],
|
|
||||||
["foo: while(1){ continue /* wtf \n busta */ foo; }", "7 asi illegal with multi line comment"],
|
|
||||||
["function f() { s }", "8 asi"],
|
|
||||||
["function f() { return }", "9 asi"],
|
|
||||||
|
|
||||||
// use strict
|
|
||||||
// XXX: some of these should actually fail?
|
|
||||||
// no support for "use strict" yet...
|
|
||||||
['"use strict"; \'bla\'\n; foo;', "1 directive"],
|
|
||||||
['(function() { "use strict"; \'bla\';\n foo; });', "2 directive"],
|
|
||||||
['"use\\n strict";', "3 directive"],
|
|
||||||
['foo; "use strict";', "4 directive"],
|
|
||||||
|
|
||||||
// tests from http://es5conform.codeplex.com/
|
|
||||||
|
|
||||||
['"use strict"; var o = { eval: 42};', "8.7.2-3-1-s: the use of eval as property name is allowed"],
|
|
||||||
['({foo:0,foo:1});', 'Duplicate property name allowed in not strict mode'],
|
|
||||||
['function foo(a,a){}', 'Duplicate parameter name allowed in not strict mode'],
|
|
||||||
['(function foo(eval){})', 'Eval allowed as parameter name in non strict mode'],
|
|
||||||
['(function foo(arguments){})', 'Arguments allowed as parameter name in non strict mode'],
|
|
||||||
|
|
||||||
// empty programs
|
|
||||||
|
|
||||||
['', '1 Empty program'],
|
|
||||||
['// test', '2 Empty program'],
|
|
||||||
['//test\n', '3 Empty program'],
|
|
||||||
['\n// test', '4 Empty program'],
|
|
||||||
['\n// test\n', '5 Empty program'],
|
|
||||||
['/* */', '6 Empty program'],
|
|
||||||
['/*\ns,fd\n*/', '7 Empty program'],
|
|
||||||
['/*\ns,fd\n*/\n', '8 Empty program'],
|
|
||||||
[' ', '9 Empty program'],
|
|
||||||
[' /*\nsmeh*/ \n ', '10 Empty program'],
|
|
||||||
|
|
||||||
// trailing whitespace
|
|
||||||
|
|
||||||
['a ', '1 Trailing whitespace'],
|
|
||||||
['a /* something */', '2 Trailing whitespace'],
|
|
||||||
['a\n // hah', '3 Trailing whitespace'],
|
|
||||||
['/abc/de//f', '4 Trailing whitespace'],
|
|
||||||
['/abc/de/*f*/\n ', '5 Trailing whitespace'],
|
|
||||||
|
|
||||||
// things the parser tripped over at one point or the other (prevents regression bugs)
|
|
||||||
['for (x;function(){ a\nb };z) x;', 'for header with function body forcing ASI'],
|
|
||||||
['c=function(){return;return};', 'resetting noAsi after literal'],
|
|
||||||
['d\nd()', 'asi exception causing token overflow'],
|
|
||||||
['for(;;){x=function(){}}', 'function expression in a for header'],
|
|
||||||
['for(var k;;){}', 'parser failing due to ASI accepting the incorrect "for" rule'],
|
|
||||||
['({get foo(){ }})', 'getter with empty function body'],
|
|
||||||
['\nreturnr', 'eol causes return statement to ignore local search requirement'],
|
|
||||||
[' / /', '1 whitespace before regex causes regex to fail?'],
|
|
||||||
['/ // / /', '2 whitespace before regex causes regex to fail?'],
|
|
||||||
['/ / / / /', '3 whitespace before regex causes regex to fail?'],
|
|
||||||
|
|
||||||
['\n\t// Used for trimming whitespace\n\ttrimLeft = /^\\s+/;\n\ttrimRight = /\\s+$/;\t\n','turned out this didnt crash (the test below did), but whatever.'],
|
|
||||||
['/[\\/]/;', 'escaped forward slash inside class group (would choke on fwd slash)'],
|
|
||||||
['/[/]/;', 'also broke but is valid in es5 (not es3)'],
|
|
||||||
['({get:5});','get property name thats not a getter'],
|
|
||||||
['({set:5});','set property name thats not a setter'],
|
|
||||||
['l !== "px" && (d.style(h, c, (k || 1) + l), j = (k || 1) / f.cur() * j, d.style(h, c, j + l)), i[1] && (k = (i[1] === "-=" ? -1 : 1) * k + j), f.custom(j, k, l)', 'this choked regex/div at some point'],
|
|
||||||
['(/\'/g, \'\\\\\\\'\') + "\'";', 'the sequence of escaped characters confused the tokenizer'],
|
|
||||||
['if (true) /=a/.test("a");', 'regexp starting with "=" in not obvious context (not implied by preceding token)']
|
|
||||||
];
|
|
||||||
|
|
||||||
for (var i=0; i<inps.length; ++i) {
|
|
||||||
callback(i, inps[i][0], inps[i][1]);
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1 +0,0 @@
|
||||||
[],Array(1),[1,2,3]
|
|
|
@ -1 +0,0 @@
|
||||||
(function(){var a=function(){};return new a(1,2,3,4)})()
|
|
|
@ -1 +0,0 @@
|
||||||
(function(){function a(){}return new a(1,2,3,4)})()
|
|
|
@ -1 +0,0 @@
|
||||||
(function(){function a(){}(function(){return new a(1,2,3)})()})()
|
|
|
@ -1 +0,0 @@
|
||||||
a=1,b=a,c=1,d=b,e=d,longname=2;if(longname+1){x=3;if(x)var z=7}z=1,y=1,x=1,g+=1,h=g,++i,j=i,i++,j=i+17
|
|
|
@ -1 +0,0 @@
|
||||||
var a=a+"a"+"b"+1+c,b=a+"c"+"ds"+123+c,c=a+"c"+123+d+"ds"+c
|
|
|
@ -1 +0,0 @@
|
||||||
var a=13,b=1/3
|
|
|
@ -1 +0,0 @@
|
||||||
function bar(){return--x}function foo(){while(bar());}function mak(){for(;;);}var x=5
|
|
|
@ -1 +0,0 @@
|
||||||
a=func(),b=z;for(a++;i<10;i++)alert(i);var z=1;g=2;for(;i<10;i++)alert(i);var a=2;for(var i=1;i<10;i++)alert(i)
|
|
|
@ -1 +0,0 @@
|
||||||
var a=1;a==1?a=2:a=17
|
|
|
@ -1 +0,0 @@
|
||||||
function a(a){return a==1?2:17}
|
|
|
@ -1 +0,0 @@
|
||||||
function x(a){return typeof a=="object"?a:a===42?0:a*2}function y(a){return typeof a=="object"?a:null}
|
|
|
@ -1 +0,0 @@
|
||||||
function f(){var a;return(a="a")?a:a}f()
|
|
|
@ -1 +0,0 @@
|
||||||
new(A,B),new(A||B),new(X?A:B)
|
|
|
@ -1 +0,0 @@
|
||||||
var a=/^(?:(\w+):)?(?:\/\/(?:(?:([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#])(?::(\d))?)?(..?$|(?:[^?#\/]\/))([^?#]*)(?:\?([^#]))?(?:#(.))?/
|
|
|
@ -1 +0,0 @@
|
||||||
var a={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue