more reliable in parsing AMD stuff

fixes #138
This commit is contained in:
Tobias Koppers 2013-12-29 12:11:03 +01:00
parent 15c225d14d
commit 7cde32c6ca
3 changed files with 78 additions and 31 deletions

View File

@ -4,12 +4,13 @@
*/
var NullDependency = require("./NullDependency");
function AMDDefineDependency(range, arrayRange, functionRange) {
function AMDDefineDependency(range, arrayRange, functionRange, objectRange) {
NullDependency.call(this);
this.Class = AMDDefineDependency;
this.range = range;
this.arrayRange = arrayRange;
this.functionRange = functionRange;
this.objectRange = objectRange;
}
module.exports = AMDDefineDependency;
@ -19,20 +20,25 @@ AMDDefineDependency.prototype.type = "amd define";
AMDDefineDependency.Template = function AMDRequireDependencyTemplate() {};
AMDDefineDependency.Template.prototype.apply = function(dep, source, outputOptions, requestShortener) {
if(dep.arrayRange && !dep.functionRange) {
source.replace(dep.range[0], dep.arrayRange[0]-1,
if(dep.objectRange && !dep.functionRange) {
source.replace(dep.range[0], dep.objectRange[0]-1,
"(module.exports = ");
source.replace(dep.arrayRange[1], dep.range[1]-1, ")");
} else if(!dep.arrayRange && dep.functionRange) {
source.replace(dep.objectRange[1], dep.range[1]-1, ")");
} else if(!dep.arrayRange && dep.functionRange && !dep.objectRange) {
source.replace(dep.range[0], dep.functionRange[0]-1,
"(__WEBPACK_AMD_DEFINE_RESULT__ = (");
source.insert(0, "var __WEBPACK_AMD_DEFINE_RESULT__;");
source.replace(dep.functionRange[1], dep.range[1]-1, "(require, exports, module)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))");
} else if(dep.arrayRange && dep.functionRange) {
source.replace(dep.functionRange[1], dep.range[1]-1, ".call(exports, require, exports, module)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))");
} else if(dep.arrayRange && dep.functionRange && !dep.objectRange) {
source.replace(dep.range[0], dep.arrayRange[0]-1,
"(__WEBPACK_AMD_DEFINE_ARRAY__ = ");
source.insert(0, "var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;");
source.replace(dep.arrayRange[1], dep.functionRange[0]-1, ", __WEBPACK_AMD_DEFINE_RESULT__ = (");
source.replace(dep.functionRange[1], dep.range[1]-1, ".apply(null, __WEBPACK_AMD_DEFINE_ARRAY__)), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))");
} else if(dep.functionRange && dep.objectRange) {
source.replace(dep.range[0], dep.functionRange[0]-1,
"(__WEBPACK_AMD_DEFINE_FACTORY__ = (");
source.insert(0, "var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;");
source.replace(dep.functionRange[1], dep.range[1]-1, "), (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_RESULT__ = __WEBPACK_AMD_DEFINE_FACTORY__.call(null), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)) : module.exports = __WEBPACK_AMD_DEFINE_FACTORY__))");
}
};

View File

@ -11,40 +11,39 @@ var ContextDependencyHelpers = require("./ContextDependencyHelpers");
module.exports = AbstractPlugin.create({
"call define": function(expr) {
var array, fn;
var array, fn, obj;
switch(expr.arguments.length) {
case 1:
if(expr.arguments[0].type == "FunctionExpression") {
// define(f() {...})
fn = expr.arguments[0];
} else {
} else if(expr.arguments[0].type === "ObjectExpression") {
// define({...})
var dep = new AMDDefineDependency(expr.range, expr.arguments[0].range);
dep.loc = expr.loc;
this.state.current.addDependency(dep);
return true;
obj = expr.arguments[0];
} else {
// define(expr)
// unclear if function or object
obj = fn = expr.arguments[0];
}
break;
case 2:
if(expr.arguments[0].type === "Literal") {
if(expr.arguments[1].type == "FunctionExpression") {
// define("...", ...)
if(expr.arguments[1].type === "FunctionExpression") {
// define("...", f() {...})
fn = expr.arguments[1];
} else {
} else if(expr.arguments[1].type === "ObjectExpression") {
// define("...", {...})
var dep = new AMDDefineDependency(expr.range, expr.arguments[1].range);
dep.loc = expr.loc;
this.state.current.addDependency(dep);
return true;
obj = expr.arguments[1];
} else {
// define("...", expr)
// unclear if function or object
obj = fn = expr.arguments[1];
}
} else {
if(expr.arguments[1].type == "FunctionExpression") {
// define([...], f() {...})
array = expr.arguments[0];
fn = expr.arguments[1];
} else {
return;
}
// define([...], f() {})
array = expr.arguments[0];
fn = expr.arguments[1];
}
break;
case 3:
@ -52,8 +51,8 @@ module.exports = AbstractPlugin.create({
array = expr.arguments[1];
fn = expr.arguments[2];
break;
default: return;
}
if(!array && !fn) return;
if(array) {
var param = this.evaluateExpression(array);
var result = this.applyPluginsBailResult("call define:amd:array", expr, param);
@ -70,8 +69,10 @@ module.exports = AbstractPlugin.create({
else
this.walkExpression(fn.body);
}.bind(this));
} else if(fn || obj) {
this.walkExpression(fn || obj);
}
var dep = new AMDDefineDependency(expr.range, array ? array.range : null, fn ? fn.range : null);
var dep = new AMDDefineDependency(expr.range, array ? array.range : null, fn ? fn.range : null, obj ? obj.range : null);
dep.loc = expr.loc;
this.state.current.addDependency(dep);
return true;

View File

@ -35,36 +35,60 @@ it("should be able to use AMD-style require", function(done) {
done();
});
});
it("should be able to use require.js-style define", function(done) {
define("name", ["./circular"], function(circular) {
circular.should.be.eql(1);
done();
});
});
it("should be able to use require.js-style define, without name", function(done) {
true && define(["./circular"], function(circular) {
circular.should.be.eql(1);
done();
});
});
it("should be able to use require.js-style define, with empty dependencies", function(done) {
define("name", [], function() {
done();
});
});
it("should be able to use require.js-style define, with empty dependencies, with a expression", function(done) {
define([], done);
});
it("should be able to use require.js-style define, with empty dependencies, with a expression and name", function(done) {
define("name", [], done);
});
it("should be able to use require.js-style define, without dependencies", function(done) {
true && define("name", function() {
done();
});
});
it("should be able to use require.js-style define, without dependencies, with a expression", function(done) {
true && define("name", done);
});
var obj = {};
it("should be able to use require.js-style define, with an object", function() {
module.exports = null;
true && define("blaaa", obj);
define("blaaa", obj);
});
after(function() {
module.exports.should.be.equal(obj);
module.exports = null;
define("blaaa", obj);
module.exports.should.be.equal(obj);
module.exports = null;
});
it("should offer AMD-style define for CommonJs", function(done) {
var _test_require = require.valueOf();
var _test_exports = exports;
@ -78,12 +102,15 @@ it("should offer AMD-style define for CommonJs", function(done) {
done();
});
});
it("should not crash on require.js require only with array", function() {
require(["./circular"]);
});
it("should be able to use AMD require without function expression (empty array)", function(done) {
require([], done);
});
it("should be able to use AMD require without function expression", function(done) {
require(["./circular"], fn);
function fn(c) {
@ -91,6 +118,7 @@ it("should be able to use AMD require without function expression", function(don
done();
}
});
it("should create a chunk for require.js require", function(done) {
var sameTick = true;
require(["./c"], function(c) {
@ -101,3 +129,15 @@ it("should create a chunk for require.js require", function(done) {
});
sameTick = false;
});
it("should not fail #138", function(done) {
(function (factory) {
if (typeof define === 'function' && define.amd) {
define([], factory); // AMD
} else if (typeof exports === 'object') {
module.exports = factory(); // Node
} else {
factory(); // Browser global
}
}(function () { done() }));
});