Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69931574e1 | ||
|
|
b5af8a1914 | ||
|
|
c14d09ba84 | ||
|
|
4fc39d8dad | ||
|
|
0b7c70f726 | ||
|
|
f72d3029dd | ||
|
|
1a0d6edc81 | ||
|
|
7b59b2f5b2 | ||
|
|
7bc7704edf | ||
|
|
14e712ee80 |
@@ -118,7 +118,8 @@ a double dash to prevent input files being used as option arguments:
|
||||
JS that was generated from some other original
|
||||
code. Specify "inline" if the source map is
|
||||
included within the sources.
|
||||
`filename` Name and/or location of the output source.
|
||||
`filename` Filename and/or location of the output source
|
||||
(sets `file` attribute in source map).
|
||||
`includeSources` Pass this flag if you want to include
|
||||
the content of source files in the
|
||||
source map as sourcesContent property.
|
||||
@@ -149,7 +150,9 @@ debugging your compressed JavaScript. To get a source map, pass
|
||||
|
||||
Additional options:
|
||||
|
||||
- `--source-map "filename='<NAME>'"` to specify the name of the source map.
|
||||
- `--source-map "filename='<NAME>'"` to specify the name of the source map. The value of
|
||||
`filename` is only used to set `file` attribute (see [the spec][sm-spec])
|
||||
in source map file.
|
||||
|
||||
- `--source-map "root='<URL>'"` to pass the URL where the original files can be found.
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ merge(Compressor.prototype, {
|
||||
if (def.scope === scope) return true;
|
||||
} while (scope instanceof AST_Function && (scope = scope.parent_scope));
|
||||
})) {
|
||||
tw.defun_ids[def.id] = undefined;
|
||||
tw.defun_ids[def.id] = false;
|
||||
}
|
||||
def.recursive_refs = 0;
|
||||
def.references = [];
|
||||
@@ -380,29 +380,45 @@ merge(Compressor.prototype, {
|
||||
mark(tw, def, true);
|
||||
}
|
||||
});
|
||||
scope.may_call_this = function() {
|
||||
scope.may_call_this = noop;
|
||||
if (!scope.contains_this()) return;
|
||||
scope.functions.each(function(def) {
|
||||
if (def.init instanceof AST_Defun && !(def.id in tw.defun_ids)) {
|
||||
tw.defun_ids[def.id] = false;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
function mark_defun(tw, def) {
|
||||
if (def.id in tw.defun_ids) {
|
||||
var marker = tw.defun_ids[def.id];
|
||||
if (!marker) return;
|
||||
if (marker !== tw.safe_ids) {
|
||||
tw.defun_ids[def.id] = undefined;
|
||||
return;
|
||||
var visited = tw.defun_visited[def.id];
|
||||
if (marker === tw.safe_ids) {
|
||||
if (!visited) return def.fixed;
|
||||
} else if (visited) {
|
||||
def.init.enclosed.forEach(function(d) {
|
||||
if (def.init.variables.get(d.name) === d) return;
|
||||
if (!safe_to_read(tw, d)) d.fixed = false;
|
||||
});
|
||||
} else {
|
||||
tw.defun_ids[def.id] = false;
|
||||
}
|
||||
return def.fixed;
|
||||
}
|
||||
if (!tw.in_loop) {
|
||||
tw.defun_ids[def.id] = tw.safe_ids;
|
||||
return def.fixed;
|
||||
} else if (tw.defun_ids[def.id] !== false) {
|
||||
tw.defun_ids[def.id] = undefined;
|
||||
} else {
|
||||
if (!tw.in_loop) {
|
||||
tw.defun_ids[def.id] = tw.safe_ids;
|
||||
return def.fixed;
|
||||
}
|
||||
tw.defun_ids[def.id] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function walk_defuns(tw, scope) {
|
||||
scope.functions.each(function(def) {
|
||||
if (def.init instanceof AST_Defun && tw.defun_ids[def.id] === undefined) {
|
||||
if (def.init instanceof AST_Defun && !tw.defun_visited[def.id]) {
|
||||
tw.defun_ids[def.id] = tw.safe_ids;
|
||||
def.init.walk(tw);
|
||||
}
|
||||
@@ -550,6 +566,7 @@ merge(Compressor.prototype, {
|
||||
return true;
|
||||
});
|
||||
def(AST_Call, function(tw, descend) {
|
||||
if (tw.find_parent(AST_Scope).may_call_this()) return;
|
||||
var exp = this.expression;
|
||||
if (!(exp instanceof AST_SymbolRef)) return;
|
||||
var def = exp.definition();
|
||||
@@ -587,8 +604,9 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
def(AST_Defun, function(tw, descend, compressor) {
|
||||
var id = this.name.definition().id;
|
||||
if (tw.defun_visited[id]) return true;
|
||||
if (tw.defun_ids[id] !== tw.safe_ids) return true;
|
||||
tw.defun_ids[id] = false;
|
||||
tw.defun_visited[id] = true;
|
||||
this.inlined = false;
|
||||
push(tw);
|
||||
reset_variables(tw, compressor, this);
|
||||
@@ -820,6 +838,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
// Flow control for visiting `AST_Defun`s
|
||||
tw.defun_ids = Object.create(null);
|
||||
tw.defun_visited = Object.create(null);
|
||||
// Record the loop body in which `AST_SymbolDeclaration` is first encountered
|
||||
tw.in_loop = null;
|
||||
tw.loop_ids = Object.create(null);
|
||||
@@ -919,15 +938,21 @@ merge(Compressor.prototype, {
|
||||
type: typeof val
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function needs_unbinding(compressor, val) {
|
||||
return val instanceof AST_PropAccess
|
||||
|| compressor.has_directive("use strict")
|
||||
&& is_undeclared_ref(val)
|
||||
&& val.name == "eval";
|
||||
}
|
||||
|
||||
// we shouldn't compress (1,func)(something) to
|
||||
// func(something) because that changes the meaning of
|
||||
// the func (becomes lexical instead of global).
|
||||
function maintain_this_binding(parent, orig, val) {
|
||||
function maintain_this_binding(compressor, parent, orig, val) {
|
||||
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|
||||
|| parent.TYPE == "Call" && parent.expression === orig
|
||||
&& (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) {
|
||||
|| parent.TYPE == "Call" && parent.expression === orig && needs_unbinding(compressor, val)) {
|
||||
return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]);
|
||||
}
|
||||
return val;
|
||||
@@ -1098,7 +1123,7 @@ merge(Compressor.prototype, {
|
||||
var def = candidate.name.definition();
|
||||
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
|
||||
def.replaced++;
|
||||
return maintain_this_binding(parent, node, candidate.value);
|
||||
return maintain_this_binding(compressor, parent, node, candidate.value);
|
||||
}
|
||||
return make_node(AST_Assign, candidate, {
|
||||
operator: "=",
|
||||
@@ -1226,7 +1251,10 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function should_stop(node, parent) {
|
||||
if (node instanceof AST_Assign) return node.operator != "=" && lhs.equivalent_to(node.left);
|
||||
if (parent instanceof AST_For) return node !== parent.init;
|
||||
if (node instanceof AST_Assign) {
|
||||
return node.operator != "=" && lhs.equivalent_to(node.left);
|
||||
}
|
||||
if (node instanceof AST_Call) {
|
||||
return lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression);
|
||||
}
|
||||
@@ -1235,7 +1263,6 @@ merge(Compressor.prototype, {
|
||||
if (node instanceof AST_LoopControl) return true;
|
||||
if (node instanceof AST_Try) return true;
|
||||
if (node instanceof AST_With) return true;
|
||||
if (parent instanceof AST_For) return node !== parent.init;
|
||||
if (replace_all) return false;
|
||||
return node instanceof AST_SymbolRef && !node.is_declared(compressor);
|
||||
}
|
||||
@@ -3346,7 +3373,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (value) {
|
||||
props.push(value);
|
||||
return maintain_this_binding(parent, node, make_sequence(node, props.map(function(prop) {
|
||||
return maintain_this_binding(compressor, parent, node, make_sequence(node, props.map(function(prop) {
|
||||
return prop.transform(tt);
|
||||
})));
|
||||
}
|
||||
@@ -4477,7 +4504,7 @@ merge(Compressor.prototype, {
|
||||
var exp = this.expression;
|
||||
if (!(exp instanceof AST_Sequence)) return this;
|
||||
var tail = exp.tail_node();
|
||||
if (tail instanceof AST_PropAccess && !(this instanceof AST_New)) return this;
|
||||
if (needs_unbinding(compressor, tail) && !(this instanceof AST_New)) return this;
|
||||
var expressions = exp.expressions.slice(0, -1);
|
||||
var node = this.clone();
|
||||
node.expression = tail;
|
||||
@@ -5028,7 +5055,7 @@ merge(Compressor.prototype, {
|
||||
var end = expressions.length - 1;
|
||||
trim_right_for_undefined();
|
||||
if (end == 0) {
|
||||
self = maintain_this_binding(compressor.parent(), compressor.self(), expressions[0]);
|
||||
self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
|
||||
if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
|
||||
return self;
|
||||
}
|
||||
@@ -5347,7 +5374,7 @@ merge(Compressor.prototype, {
|
||||
var ll = fuzzy_eval(self.left);
|
||||
if (!ll) {
|
||||
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||
} else if (!(ll instanceof AST_Node)) {
|
||||
compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
|
||||
@@ -5386,7 +5413,7 @@ merge(Compressor.prototype, {
|
||||
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
|
||||
} else if (!(ll instanceof AST_Node)) {
|
||||
compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||
}
|
||||
var rr = self.right.evaluate(compressor);
|
||||
if (!rr) {
|
||||
@@ -6262,7 +6289,7 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
AST_Lambda.DEFMETHOD("contains_this", function() {
|
||||
AST_Scope.DEFMETHOD("contains_this", function() {
|
||||
var result;
|
||||
var self = this;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "3.3.24",
|
||||
"version": "3.3.27",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
@@ -28,7 +28,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"acorn": "~5.5.3",
|
||||
"mocha": "~3.5.1",
|
||||
"semver": "~5.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -4931,6 +4931,27 @@ collapse_rhs_lhs_2: {
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
collapse_rhs_loop: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var s;
|
||||
s = "<tpl>PASS</tpl>";
|
||||
for (var m, r = /<tpl>(.*)<\/tpl>/; m = s.match(r);)
|
||||
s = s.replace(m[0], m[1]);
|
||||
console.log(s);
|
||||
}
|
||||
expect: {
|
||||
var s;
|
||||
s = "<tpl>PASS</tpl>";
|
||||
for (var m, r = /<tpl>(.*)<\/tpl>/; m = s.match(r);)
|
||||
s = s.replace(m[0], m[1]);
|
||||
console.log(s);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
collapse_rhs_side_effects: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
|
||||
@@ -1,11 +1,31 @@
|
||||
remove_redundant_sequence_items: {
|
||||
options = { side_effects: true };
|
||||
remove_sequence: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
(0, 1, eval)();
|
||||
(0, 1, logThis)();
|
||||
(0, 1, _decorators.logThis)();
|
||||
}
|
||||
expect: {
|
||||
eval();
|
||||
logThis();
|
||||
(0, _decorators.logThis)();
|
||||
}
|
||||
}
|
||||
|
||||
remove_redundant_sequence_items: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
(0, 1, eval)();
|
||||
(0, 1, logThis)();
|
||||
(0, 1, _decorators.logThis)();
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
(0, eval)();
|
||||
logThis();
|
||||
(0, _decorators.logThis)();
|
||||
@@ -13,13 +33,17 @@ remove_redundant_sequence_items: {
|
||||
}
|
||||
|
||||
dont_remove_this_binding_sequence: {
|
||||
options = { side_effects: true };
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
(0, eval)();
|
||||
(0, logThis)();
|
||||
(0, _decorators.logThis)();
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
(0, eval)();
|
||||
logThis();
|
||||
(0, _decorators.logThis)();
|
||||
|
||||
@@ -3,8 +3,9 @@ this_binding_conditionals: {
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
side_effects: true,
|
||||
};
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
(1 && a)();
|
||||
(0 || a)();
|
||||
(0 || 1 && a)();
|
||||
@@ -26,6 +27,7 @@ this_binding_conditionals: {
|
||||
(1 ? eval : 0)();
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
a();
|
||||
a();
|
||||
a();
|
||||
@@ -53,13 +55,15 @@ this_binding_collapse_vars: {
|
||||
collapse_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
};
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
var c = a; c();
|
||||
var d = a.b; d();
|
||||
var e = eval; e();
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
a();
|
||||
(0, a.b)();
|
||||
(0, eval)();
|
||||
@@ -69,31 +73,88 @@ this_binding_collapse_vars: {
|
||||
this_binding_side_effects: {
|
||||
options = {
|
||||
side_effects : true
|
||||
};
|
||||
}
|
||||
input: {
|
||||
(function (foo) {
|
||||
(function(foo) {
|
||||
(0, foo)();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
(0, eval)("console.log(foo);");
|
||||
}());
|
||||
(function (foo) {
|
||||
(function(foo) {
|
||||
"use strict";
|
||||
(0, foo)();
|
||||
(0, foo.bar)();
|
||||
(0, eval)("console.log(foo);");
|
||||
}());
|
||||
(function(foo) {
|
||||
var eval = console;
|
||||
(0, foo)();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
(0, eval)("console.log(foo);");
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
(function (foo) {
|
||||
(function(foo) {
|
||||
foo();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
eval("console.log(foo);");
|
||||
}());
|
||||
(function (foo) {
|
||||
(function(foo) {
|
||||
"use strict";
|
||||
foo();
|
||||
(0, foo.bar)();
|
||||
(0, eval)("console.log(foo);");
|
||||
}());
|
||||
(function(foo) {
|
||||
var eval = console;
|
||||
foo();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
eval("console.log(foo);");
|
||||
}());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this_binding_sequences: {
|
||||
options = {
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
console.log(typeof function() {
|
||||
return eval("this");
|
||||
}());
|
||||
console.log(typeof function() {
|
||||
"use strict";
|
||||
return eval("this");
|
||||
}());
|
||||
console.log(typeof function() {
|
||||
return (0, eval)("this");
|
||||
}());
|
||||
console.log(typeof function() {
|
||||
"use strict";
|
||||
return (0, eval)("this");
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(typeof function() {
|
||||
return eval("this");
|
||||
}()),
|
||||
console.log(typeof function() {
|
||||
"use strict";
|
||||
return eval("this");
|
||||
}()),
|
||||
console.log(typeof function() {
|
||||
return eval("this");
|
||||
}()),
|
||||
console.log(typeof function() {
|
||||
"use strict";
|
||||
return (0, eval)("this");
|
||||
}());
|
||||
}
|
||||
expect_stdout: [
|
||||
"object",
|
||||
"undefined",
|
||||
"object",
|
||||
"object",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -6059,6 +6059,38 @@ conditional_nested_2: {
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
conditional_nested_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var n = 2, c = 0;
|
||||
(function f(a) {
|
||||
0 < n-- && g(a = 1);
|
||||
function g() {
|
||||
a && c++;
|
||||
}
|
||||
g();
|
||||
0 < n-- && f();
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var n = 2, c = 0;
|
||||
(function f(a) {
|
||||
0 < n-- && g(a = 1);
|
||||
function g() {
|
||||
a && c++;
|
||||
}
|
||||
g();
|
||||
0 < n-- && f();
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
issue_2436: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
@@ -6146,3 +6178,192 @@ issue_3125: {
|
||||
}
|
||||
expect_stdout: "7"
|
||||
}
|
||||
|
||||
issue_3140_1: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
this();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
this();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3140_2: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
var self = this;
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
self();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
this();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3140_3: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
var self = this;
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
(function() {
|
||||
return self;
|
||||
})()();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
var self = this;
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
(function() {
|
||||
return self;
|
||||
})()();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3140_4: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
var o = {
|
||||
p: this
|
||||
};
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
o.p();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
var o = {
|
||||
p: this
|
||||
};
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
o.p();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
117
test/mocha.js
117
test/mocha.js
@@ -1,24 +1,109 @@
|
||||
var fs = require("fs");
|
||||
var Mocha = require("mocha");
|
||||
var path = require("path");
|
||||
|
||||
// Instantiate a Mocha instance
|
||||
var mocha = new Mocha({
|
||||
timeout: 5000
|
||||
});
|
||||
var testDir = __dirname + "/mocha/";
|
||||
var config = {
|
||||
limit: 5000,
|
||||
timeout: function(limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
};
|
||||
var tasks = [];
|
||||
var titles = [];
|
||||
describe = function(title, fn) {
|
||||
config = Object.create(config);
|
||||
titles.push(title);
|
||||
fn.call(config);
|
||||
titles.pop();
|
||||
config = Object.getPrototypeOf(config);
|
||||
};
|
||||
it = function(title, fn) {
|
||||
fn.limit = config.limit;
|
||||
fn.titles = titles.slice();
|
||||
fn.titles.push(title);
|
||||
tasks.push(fn);
|
||||
};
|
||||
|
||||
// Add each .js file to the Mocha instance
|
||||
fs.readdirSync(testDir).filter(function(file) {
|
||||
fs.readdirSync("test/mocha").filter(function(file) {
|
||||
return /\.js$/.test(file);
|
||||
}).forEach(function(file) {
|
||||
mocha.addFile(path.join(testDir, file));
|
||||
require("./mocha/" + file);
|
||||
});
|
||||
|
||||
module.exports = function() {
|
||||
mocha.run(function(failures) {
|
||||
if (failures) process.on("exit", function() {
|
||||
process.exit(failures);
|
||||
function log_titles(log, current, marker) {
|
||||
var indent = "";
|
||||
var writing = false;
|
||||
for (var i = 0; i < current.length; i++, indent += " ") {
|
||||
if (titles[i] != current[i]) writing = true;
|
||||
if (writing) log(indent + (i == current.length - 1 && marker || "") + current[i]);
|
||||
}
|
||||
titles = current;
|
||||
}
|
||||
|
||||
function red(text) {
|
||||
return "\u001B[31m" + text + "\u001B[39m";
|
||||
}
|
||||
|
||||
function green(text) {
|
||||
return "\u001B[32m" + text + "\u001B[39m";
|
||||
}
|
||||
|
||||
var errors = [];
|
||||
var total = tasks.length;
|
||||
titles = [];
|
||||
process.nextTick(function run() {
|
||||
var task = tasks.shift();
|
||||
if (task) try {
|
||||
var elapsed = Date.now();
|
||||
var timer;
|
||||
var done = function() {
|
||||
clearTimeout(timer);
|
||||
done = function() {};
|
||||
elapsed = Date.now() - elapsed;
|
||||
if (elapsed > task.limit) {
|
||||
throw new Error("Timed out: " + elapsed + "ms > " + task.limit + "ms");
|
||||
}
|
||||
log_titles(console.log, task.titles, green('\u221A '));
|
||||
process.nextTick(run);
|
||||
};
|
||||
if (task.length) {
|
||||
task.timeout = function(limit) {
|
||||
clearTimeout(timer);
|
||||
task.limit = limit;
|
||||
timer = setTimeout(function() {
|
||||
raise(new Error("Timed out: exceeds " + limit + "ms"));
|
||||
}, limit);
|
||||
};
|
||||
task.timeout(task.limit);
|
||||
task.call(task, done);
|
||||
} else {
|
||||
task.timeout = config.timeout;
|
||||
task.call(task);
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
raise(err);
|
||||
} else if (errors.length) {
|
||||
console.error();
|
||||
console.log(red(errors.length + " test(s) failed!"));
|
||||
titles = [];
|
||||
errors.forEach(function(titles, index) {
|
||||
console.error();
|
||||
log_titles(console.error, titles, (index + 1) + ") ");
|
||||
var lines = titles.error.stack.split('\n');
|
||||
console.error(red(lines[0]));
|
||||
console.error(lines.slice(1).join("\n"));
|
||||
});
|
||||
});
|
||||
};
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log();
|
||||
console.log(green(total + " test(s) passed."));
|
||||
}
|
||||
|
||||
function raise(err) {
|
||||
clearTimeout(timer);
|
||||
done = function() {};
|
||||
task.titles.error = err;
|
||||
errors.push(task.titles);
|
||||
log_titles(console.log, task.titles, red('\u00D7 '));
|
||||
process.nextTick(run);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -18,9 +18,8 @@ if (failures) {
|
||||
console.error("!!! " + Object.keys(failed_files).join(", "));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var mocha_tests = require("./mocha.js");
|
||||
mocha_tests();
|
||||
console.log();
|
||||
require("./mocha.js");
|
||||
|
||||
/* -----[ utils ]----- */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user