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?

Community
  • 1
  • 1
machineghost
  • 33,529
  • 30
  • 159
  • 234
  • 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. – jgillich Mar 05 '14 at 22:47
  • 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. – machineghost Mar 05 '14 at 23:02

2 Answers2

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}
Louis
  • 146,715
  • 28
  • 274
  • 320
  • 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. – machineghost Mar 07 '14 at 17:42
  • 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): https://github.com/gfranko/amdclean – machineghost Mar 10 '14 at 23:17
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}
});
jgillich
  • 71,459
  • 6
  • 57
  • 85
  • 2
    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. – machineghost Mar 05 '14 at 23:04
  • 1
    @machineghost jQuery is doing exactly what you want in their build process, but it's nothing that's supported by RequireJS itself: https://github.com/jquery/jquery/blob/master/build/tasks/build.js – jgillich Mar 05 '14 at 23:16
  • 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 ... – machineghost Mar 05 '14 at 23:34
  • @machineghost 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(). – Alexander Mills Mar 28 '16 at 05:03