Skip to content

Commit 6eac904

Browse files
author
Jordan Klassen
committed
Optional externals in amd use globals instead of an explicit dependency
There isn't any mechanism in the amdjs spec to provide optional dependancies (although a requirejs loader plugin could do that, it's not standard). The best approach found was knockout's optional dependancy on jquery, for which it doesn't use require, it only uses globals. This solution will need to be improved for use of requirejs/amd in commonjs environments, and could be toggled with a flag.
1 parent 0ab5a3e commit 6eac904

4 files changed

Lines changed: 56 additions & 15 deletions

File tree

lib/UmdMainTemplatePlugin.js

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,27 @@ UmdMainTemplatePlugin.prototype.apply = function(mainTemplate) {
3030
var externals = chunk.modules.filter(function(m) {
3131
return m.external;
3232
});
33+
var optionalExternals = [], requiredExternals = [];
34+
externals.forEach(function(m) {
35+
if (m.optional) optionalExternals.push(m); else requiredExternals.push(m);
36+
});
37+
externals = requiredExternals.concat(optionalExternals);
3338
function replaceKeys(str) {
3439
return (str
3540
.replace(Template.REGEXP_HASH, hash)
3641
.replace(Template.REGEXP_CHUNKHASH, chunk.renderedHash)
3742
.replace(Template.REGEXP_ID, chunk.id)
3843
.replace(Template.REGEXP_NAME, chunk.name || chunk.id));
3944
}
40-
function externalsDepsArray() {
41-
return "[" + replaceKeys(externals.map(function(m) {
45+
function externalsDepsArray(modules) {
46+
modules || (modules = externals);
47+
return "[" + replaceKeys(modules.map(function(m) {
4248
return JSON.stringify(typeof m.request === "object" ? m.request.amd : m.request);
4349
}).join(", ")) + "]";
4450
}
45-
function externalsRootArray() {
46-
return replaceKeys(externals.map(function(m) {
51+
function externalsRootArray(modules) {
52+
modules || (modules = externals);
53+
return replaceKeys(modules.map(function(m) {
4754
var request = m.request;
4855
if(typeof request === "object") request = request.root;
4956
return "root" + accessorToObjectAccess([].concat(request));
@@ -55,26 +62,37 @@ UmdMainTemplatePlugin.prototype.apply = function(mainTemplate) {
5562
if(typeof request === "object") request = request[type];
5663
if(Array.isArray(request)) {
5764
var expr = "require(" + JSON.stringify(request[0]) + ")" + accessorToObjectAccess(request.slice(1));
58-
} else
65+
} else
5966
var expr = "require(" + JSON.stringify(request) + ")";
6067
if(m.optional) {
6168
expr = "(function webpackLoadOptionalExternalModule() { try { return " + expr + "; } catch(e) {} }())";
6269
}
6370
return expr;
6471
}).join(", "));
6572
}
66-
var externalsArguments = externals.map(function(m) {
67-
return "__WEBPACK_EXTERNAL_MODULE_" + m.id + "__";
68-
}).join(", ");
73+
function externalsArguments(modules) {
74+
modules || (modules = externals);
75+
return modules.map(function(m) {
76+
return "__WEBPACK_EXTERNAL_MODULE_" + m.id + "__";
77+
}).join(", ");
78+
}
79+
var amdFactory = optionalExternals.length > 0 ?
80+
"(function webpackLoadOptionalExternalModuleAmd(" + externalsArguments(requiredExternals) + ") { " +
81+
(requiredExternals.length ?
82+
"return factory(" + externalsArguments(requiredExternals) + ", " + externalsRootArray(optionalExternals) + "); })"
83+
:
84+
"return factory(" + externalsRootArray(optionalExternals) + "); })"
85+
) :
86+
"factory";
6987
return new ConcatSource(new OriginalSource(
7088
"(function webpackUniversalModuleDefinition(root, factory) {\n" +
7189
" if(typeof exports === 'object' && typeof module === 'object')\n" +
7290
" module.exports = factory(" + externalsRequireArray("commonjs2") + ");\n" +
7391
" else if(typeof define === 'function' && define.amd)\n" +
74-
(externalsArguments ?
75-
" define(" + externalsDepsArray() + ", factory);\n"
92+
(requiredExternals.length > 0 ?
93+
" define(" + externalsDepsArray(requiredExternals) + ", " + amdFactory + ");\n"
7694
:
77-
" define(factory);\n"
95+
" define(" + amdFactory + ");\n"
7896
) +
7997
(this.name ?
8098
" else if(typeof exports === 'object')\n" +
@@ -83,15 +101,15 @@ UmdMainTemplatePlugin.prototype.apply = function(mainTemplate) {
83101
" " + replaceKeys(accessorAccess("root", this.name)) + " = factory(" + externalsRootArray() + ");\n"
84102
:
85103
" else {\n" +
86-
(externalsArguments ?
104+
(externals.length > 0 ?
87105
" var a = typeof exports === 'object' ? factory(" + externalsRequireArray("commonjs") + ") : factory(" + externalsRootArray() + ");\n"
88106
:
89107
" var a = factory();\n"
90108
) +
91109
" for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];\n" +
92110
" }\n"
93111
) +
94-
"})(this, function(" + externalsArguments + ") {\nreturn ", "webpack/universalModuleDefinition"), source, "\n})\n");
112+
"})(this, function(" + externalsArguments() + ") {\nreturn ", "webpack/universalModuleDefinition"), source, "\n})\n");
95113
}.bind(this));
96114
mainTemplate.plugin("global-hash", function(chunk) {
97115
if(Template.REGEXP_HASH.test([].concat(this.name || "").join("|")))
@@ -101,4 +119,4 @@ UmdMainTemplatePlugin.prototype.apply = function(mainTemplate) {
101119
hash.update("umd");
102120
hash.update(this.name + "");
103121
}.bind(this));
104-
};
122+
};

test/cases/parsing/extract-amd/index.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
var should = require("should");
2+
13
it("should parse fancy function calls", function() {
24
("function"==typeof define && define.amd ?
35
define :
@@ -52,6 +54,21 @@ it("should be able to use require.js-style define", function(done) {
5254
});
5355
});
5456

57+
it("should be able to use require.js-style define, optional dependancies, exist"/*, function(done) {
58+
// TODO: set up AMD shim config for optional dependancies
59+
define("name", ["./optional"], function(optional) {
60+
optional.a.should.be.eql("a");
61+
done();
62+
});
63+
}*/);
64+
65+
it("should be able to use require.js-style define, optional dependancies, not exist", function(done) {
66+
define("name", ["./optional"], function(optional) {
67+
should(optional.b).not.exist;
68+
done();
69+
});
70+
});
71+
5572
it("should be able to use require.js-style define, special string", function(done) {
5673
define(["require"], function(require) {
5774
require("./circular").should.be.eql(1);
@@ -202,4 +219,4 @@ it("should not fail issue #138 second", function() {
202219
});
203220
})(typeof define === 'function' && define.amd ? define : function (factory) { module.exports = factory(require); }, this);
204221
module.exports.should.be.eql("#138 2.");
205-
});
222+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = 2;
2+
try { module.exports.a = require("./a"); } catch (e) {};
3+
try { module.exports.b = require("./b"); } catch (e) {};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = [
2+
[/Module not found/, /Cannot resolve/, / \.\/b /, /b\.js/]
3+
];

0 commit comments

Comments
 (0)