How do I export a global variable from Require.js?
Asked Answered
D

2

9

I'm trying to make an external library using Require.js. Thanks to Require.js not compiling single js file correctly and Require.js (almond.js) Timing Off I've figured out how to get everything to "compile" in to a single optimized/built file, and that single file works. There's just one problem: I can't figure out how to set a variable for my library.

Let's say I want my library to create window.Foo. I tried using a main.js file with:

window.Foo = require([], function() {
    window.Foo = {someValue: 1};
    return {someValue: 2};
});

and a wrapper end fragment of:

    return require('main');
}));

As you can see, I tried to expose Foo to the global space both by explicitly setting window.Foo from inside the require call, and setting it explicitly from outside via the return value of the end fragment. But neither one works; if I add a console.log(window.foo) right after I load the built file, it tells me that window.Foo is undefined.

If I do a window.setTimeout window.Foo eventually does get set (to {someValue: 1}), but I can't very well expect my users to have to wrap all their code with a timeout. Can anyone please explain how I can get window.Foo to be defined as soon as my optimized/built file is loaded?

Diskson answered 5/3, 2014 at 21:52 Comment(2)
Is there any specific reason for using RequireJS in a library? It's probably better to support both RequireJS and CommonJS and fall back to setting a global like most libraries do it these days.Antioch
Our main codebase uses Require, and this external library uses several pieces of that codebase. In order for the external library to leverage the existing code from our main code base, it needs to be able to "speak" Require ... although once it gets that code it has no need for Require whatsoever, which is why I'm trying to make it appear to the user as a completely standalone library.Diskson
S
13

If you follow James' instructions here, you can easily produce a library designed as a bundle of RequireJS modules that someone can load synchronously.

I've got a github repository illustrating the whole thing. The salient points:

  • The main module exports Foo to the global space:

    define('main', ["./modC"], function () {
    
    console.log("inside");
    window.Foo = {someValue: 1};
    
    return {someValue: 2};
    
    });
    

    It also returns a value that is exported by the start fragment as Bar (that's the line that says root.Bar = factory();). So it illustrates two ways to export to the global space.

  • The wrapper code used by r.js starts main with the synchronous form of require:

    require(`main`);
    

If you load it, you'll see the following output:

outside
loaded modC
inside
out in the HTML!
value of window.Foo: Object {someValue: 1}
value of window.Bar: Object {someValue: 2}
Saiva answered 7/3, 2014 at 1:6 Comment(2)
Ah, thank you for the example code: I think I had a require where I should have had a define. I'll try implementing your approach as soon as I have the time this morning.Diskson
In addition to this answer (which worked great for me, thank you Louis), someone in a Require issues thread also pointed out this library (so I'm mentioning it for completeness): github.com/gfranko/amdcleanDiskson
A
3

Calls to require() are async, meaning the return value of the function that you pass as argument is not returned by require() itself. Your issue could be solved in different ways, the RequireJS way is defining your code in a module and requiring it as a dependency:

// mymodule.js
define([], function() {
    window.Foo = {someValue: 1};
    return {someValue: 2};
});

// main.js
require(['mymodule'], function (mymodule) {
    console.log(window.Foo); // {someValue: 1}
    console.log(mymodule); // {someValue: 2}
});
Antioch answered 5/3, 2014 at 22:9 Comment(4)
Right, but then I have to make my users use Require. It's the difference between them just being able to add the library to the page and call Foo.bar() vs. making them load require then do require(['fooLibrary'], (Foo) { Foo.bar(); });. Most people are used to the former: they download jQuery, then use $ ... they don't have to require(['jQuery'], function($) { $.doSomething()});. Ideally I'd like our library to work the same way, and because I've pre-compiled everything together in to a single file ahead of time there theoretically should be no need for any asynchronicity.Diskson
@Diskson jQuery is doing exactly what you want in their build process, but it's nothing that's supported by RequireJS itself: github.com/jquery/jquery/blob/master/build/tasks/build.jsAntioch
Huh, that is interesting. Still, I'm kinda shocked that it's impossible to convert some Require.js code to be synchronous; the Require Optimizer gets the code 95% of the way there ...Diskson
@Diskson it will be synchronous, you can require things synchronously, if you know they are already loaded. The global or local module require function in RequireJS will look in the cache first, if the file is there it will return it synchronously, and if there is a callback, it will pass the file there as well, and it's all sync. If the file is not in the cache, and if there is a callback provided then it will retrieve the file asynchronously and provide it in the callback to require().Plessor

© 2022 - 2024 — McMap. All rights reserved.