How to load multiple named AMD modules defined in a single file?
Asked Answered
S

1

6

My understanding is that it shouldn't happen, but it does. What is the most appropriate workaround for these libraries?

Notes:

  • I figured running these scripts after require.js manually (using a script tag) should work, and indeed it seems like it does. However, the RequireJS documentation explicitly warns that the data-main script is run asynchronously. While require.js should have properly defined the define function needed by the scripts defining multiple named modules, I also assume that without the proper configuration loaded from the data-main script, bad things may happen in a non-deterministic way. Is this correct?
  • I also fail to see how any combination of the shim, map, bundles and paths configuration properties can help in this case, although I hope I'm missing it.

Clarification on the first note: (My bad, it's really not clear)

What I describe here is simply to manually execute (using an HTML script tag) the script that defines multiple modules after RequireJS and the data-main script. With the knowledge that the latter is run async, my worries should become more obvious (but feel free to ask me to detail some more). The bulk of it is that although it seems like I can require each named module successfully, I'm not sure that the behavior is deterministic (also, it's not pretty, I'd much rather avoid additional script tags and properly load everything asynchronously).

<script src="scripts/require.js" data-main="app/main.js"></script>
<script src="scripts/datajs-1.1.2.js"></script>

Here, datajs-1.1.2.js defines two modules, as described in the link above and copied below:

// AMD support
if (typeof define === 'function' && define.amd) {
    define('datajs', datajs);
    define('OData', odata);
} ...
Stoichiometry answered 3/2, 2014 at 15:13 Comment(0)
F
10

What will and will not work depends on the specifics of how the file which defines multiple named modules will be used in an application.

In general, if the order in which modules defined (using named defines) in a single file cannot be determined, then setting paths to map the module names to the file that defines them should prevent problems:

paths: {
    'foo': 'path/to/foobar',
    'bar': 'path/to/foobar'
}

If foo or bar are required, RequireJS will load the file that defines both (path/to/foobar.js), which is not a problem.

With the details that you've added to the question, I can say this. First, this code:

<script src="scripts/require.js" data-main="app/main.js"></script>
<script src="scripts/datajs-1.1.2.js"></script>

is incorrect. Loading a module that calls define through a <script> tag is generally wrong. (I would say it is always wrong, but there may be some really strange cases where to get incompatible assets to work together you have to do something that would normally be wrong. But this is unusual, and has to be justified.) As you suggested by doing this, you open yourself to timing issues. Sometimes it may work, sometimes it may not.

However, this should prevent any timing issues:

<script>
require = {
    paths: {
        datajs: 'scripts/datajs-1.1.2',
        OData: 'scripts/datajs-1.1.2'
    }
};
</script>
<script src="scripts/require.js" data-main="app/main.js"></script>

Whenever anything needs either of the two modules in datajs-1.1.2.js, either because it called require or because it called define with the appropriate module names, the file that defines both modules is going to be loaded.

(Warning: the configuration I show in the example above is an educated guess, which contains enough details to illustrate. It may not work once combined with a configuration already present in app/main.js, and I'm not suggesting that it is the best way to configure RequireJS for your specific application.)

For RequireJS version 2.1.10 and higher, there's also the bundles option, which is nicer to use:

<script>
require = {
    bundles: {
      "js/datajs-1.1.2": ["datajs", "OData"]
    }
};
</script>
<script src="scripts/require.js" data-main="app/main.js"></script>

I suggest reading the documentation on this option to avoid possible misunderstandings as to how it works.

Finder answered 3/2, 2014 at 15:49 Comment(6)
Thanks for your answer; I think there is some confusion regarding my use of data-main (which isn't special in this case). I attempt to clarify in my edited question. To comment on your latter point: I think we can assume that the order of the named modules definitions in the file is either known or irrelevant, so as to keep this question simple (it would also suffice for my own needs, I guess a separate question could be asked with these requirements).Stoichiometry
I've updated the answer in light of your changes to the question.Finder
Your explanation is very clear and confirms my intuition, thanks. However, specifying the same path for multiple modules, even if they are both defined in the same file, doesn't do the trick; I get a Script error for: datajs (the first module I attempt to require). It kind of makes sense; how is RequireJS gonna figure out which module to import? (I suppose it expects a single module at the specified path because the path-config name takes precedence over the named defines). BTW here is the example script if that helps.Stoichiometry
Sorry. I inadvertently left the .js extensions in the paths. Generally, you don't want extensions the paths you pass to RequireJS. I've downloaded datajs and I can get it to load fine using the configuration I have in my answer (after editing out the erroneous extensions).Finder
At the end of the answer, I've added a way to do it with the bundles option, which was added in 2.1.10. I've tested both methods.Finder
My mistake, I apologize -- I normally always double-check these simple things, but you're most likely right, I must have blindly retyped the extension when I tested the path-config alternative this morning. Thank you for your patience! PS: I did try the bundles-config alternative before, but failed to realize we hadn't upgraded to 2.1.10 yet -- note-to-self: gotta start paying attention ;).Stoichiometry

© 2022 - 2024 — McMap. All rights reserved.