I'm trying to convert our requirejs
calls to use SystemJS
, but I'm not exactly sure what I'm doing wrong.
Our original calls look like this:
return function(callback) {
requirejs(["/app/shared.js"], function(result){
callbackFunction = callback;
callback(dashboard);
main();
})
}
And what I'm trying instead is:
return function(callback) {
console.log(callback.toString())
SystemJS.import('app/shared.js').then(function(result){
callbackFunction = callback;
callback(dashboard);
main();
});
}
I've had to remove some leading /
to get things to load properly, which is fine, but I've now ran into an issue where variables that were defined at the top of shared.js
aren't visible in my local main.js
file. In my browser console I get:
Potentially unhandled rejection [1] ReferenceError: dashboard is not defined
shared.js
defines dashboard
:
var dashboard = { rows: [], }
// Other definitions...
define(["/app/custom-config.js", /* etc */]);
I guess I have two questions:
requirejs
calls?shared.js
accessible?For a fuller picture, main()
just sets up the dashboard
object, and then calls callbackFunction(dashboard)
on it.
Your problem can be reduced to the following case where you have two AMD modules, with one that leaks into the global space, and the 2nd one that tries to use what the first one leaked. Like the two following modules.
src/a.js
requires the module that leaks and depends on what that module leaks:
define(["./b"], function () {
console.log("a loaded");
callback();
});
src/b.js
leaks into the global space:
// This leaks `callback` into the global space.
var callback = function () {
console.log("callback called");
}
define(["./b"], function () {
console.log("b loaded");
});
With RequireJS, the code above will work. Oh, it is badly designed because b.js
should not leak into the global space, but it will work. You'll see callback called
on the console.
With SystemJS, the code above won't work. Why? RequireJS loads modules by adding a script
element to the header and lets script
execute the module's code so callback
does end up in the global space in exactly the same way it would if you had written your own script
element with an src
attribute that points to your script. (You'd get an "Mismatched anonymous define" error, but that's a separate issue that need not detain us here.) SystemJS, by default, uses eval
rather than create script
elements, and this changes how the code is evaluated. Usually, it does not matter, but sometimes it does. In the case at hand here callback
does not end up in the global space, and module a
fails.
Ultimately, your AMD modules should be written so that they don't use the global space to pass information from one another.
However, there is another solution which may be useful as a stepping-stone towards a final solution. You can use scriptLoad: true
to tell SystemJS to use script
elements like RequirejS does. (See the documentation on meta
for details and caveats.) Here is a configuration that does that:
System.config({
baseURL: "src",
meta: {
"*": {
scriptLoad: true, // This is what fixes the issue.
}
},
packages: {
// Yes, this empty package does something. It makes `.js` the
// default extension for modules.
"": {}
},
});
// We have to put `define` in the global space to
// so that our modules can find it.
window.define = System.amdDefine;
If I run the example code I've given here without scriptLoad: true
, then module a
cannot call the callback. With scriptLoad: true
, it can call the callback and I get on the console:
b loaded
a loaded
callback called