Where does the module id come from in Durandal? - javascript

I'm working on a legacy app in durandal. I noticed when I define a module:
define({
message: 'hello world'
});
Then the output from grunt looks like:
define('filePath\fileName',{
message: 'hello world'
});
Where does filePath\fileName come from? I tried building a module in plain RequireJS and I had to supply this id myself. I read that Durandal uses a pattern called AMD (Asynchronous Module Definition pattern). Is the filePath\fileName using fileName.js convention a part of the AMD pattern?

AMD optimizers add these ids when they optimize your modules.
As long as you have one module per file, an AMD loader is able to infer the module id from the path of the file and from the configuration of the module loader. So it is okay when you have one module per file to omit the module id from the define call, and let the AMD loader infer the module id. This is called an "anonymous define, by the way.
One of the jobs of the optimizer is to combine multiple modules in a single file. When the optimizer combines multiple modules in the same file, then there's no way to distinguish which define call belongs to which module, unless the optimizer also records this information. The way the AMD specification is designed, the way to record which define belongs to which module is to add the module id as the first argument of define.

Related

RequireJS: Not triggering require callback on specifying absolute path name for which paths alias exists in config

Here there is a paths alias "jquery" in require.js config
Require.JS is triggering require callback for "jquery" but it doesn't trigger the require callback if absolute path is specified for jquery module.
<script type="text/javascript">
var require = {
baseUrl : "../../",
paths : {
'jquery' : '/js/jquery-1.8.3',
},
};
</script>
<script type='text/javascript' src='/js/require.js'></script>
<script type="text/javascript">
require(['jquery'],function(obj) {
console.info("jquery loaded","alias");
});
require(['jquery'],function(obj) {
console.info("jquery loaded","alias");
});
require(['/js/jquery-1.8.3.js'],function(obj) {
console.info("jquery loaded","absolute path");
});
</script>
I expected console.info from the last require statement to be executed, but it doesn't trigger. Looks like if you require a path alias and then require the absolute path,it's not triggering the callback.
Is this a bug in Require.JS or any alternate ways to get callback triggered with both path alias and absolute path?
Correct. Trying to load the same module under two different names does not work. There's good evidence this is by design.
RequireJS treats modules as singletons. (The scope of the "singelton-ness" is a RequireJS context. So the same module could be instantiated once for a context and a second time for a different context but it will never be instantiated twice for the same context.) When RequireJS loads a module, the module acquires a name. If the define call has a string as the first argument, the name is this string. Otherwise, if there was no map affecting the loading of the module, the name will be the string that appeared in require or define call that the listed the module as a dependency. So, using your configuration in the question, if you do require(['jquery'], ... or have a module foo that is defined as define(['jquery'], ... then the name that will be given to the module found at js/jquery-1.8.3 is jquery. Then there is the fact that inside a module, you get get the module name by doing something like this:
define(['module'], function (module) {
console.log("my module name: " + module.id);
});
Ok, so what happens if your module is required twice with two different module names? Remember that modules are singletons so RequireJS won't execute the define twice. Which id should the module get?
In practice, it is almost always the case that when the same module code is being loaded under two different names, it is an programming mistake rather than something the developers really wanted to do. I've never run into a codebase where RequireJS was used where it would have been valid to load jQuery both as jquery and using a path with the version number. The latter case would indeed be an error. So rather than use some sort of default behavior that will probably lead to surprises down the road, RequireJS breaks right away when you try to load the same module under two different names. This is the safe thing to do.
Now, if you really really must be able to load the same module with two names, then you can do it but you must be explicit about what you want: you can use map. This will establish an unambiguous relation between the module names you want to use. What a map does is basically saying "when code in module X requires module Y load module Z instead." So in your case:
map: {
'*':
'/js/jquery-1.8.3': 'jquery'
}
}
This says "when code in any module (*) requires /js/jquery-1.8.3 load jquery instead". In case you wonder, this won't result in a circular dependency. RequireJS will see /js/jquery-1.8.3 passed to require or define, will then inspect the map and convert it to jquery, will then find jquery in paths and convert it to /js/jquery-1.8.3 and then add the .js extension and fetch the module. After it has gone through paths it does not go back to map, because the result it gets from paths is a path, not a module name (and map only transforms module names).
Note that with the map above, there is only one module loaded, which is named jquery. If module.id were used in it, it would always have the value "jquery" and could not have any other value.
Important side note: you should not put the .js in the require call, otherwise it won't work: require(['/js/jquery-1.8.3'], ....

Does AMD format require that modules are singletons?

Is there anything in the AMD specification to say that require'd modules must be supplied with the same object? It seems to be fairly common practice to assume that a require'd module is a single instance supplied to all requiring modules, but is there anything to prevent a module loader treating loaded modules as merely cached (possibly reloading them at some point)?
For example (hypothetically speaking), would an AMD loader be guaranteed to distribute the same instance of a message bus module across various different dependent modules, so they could use it to send each other messages?
Yes, modules should be singletons.
From the spec:
define() function
...
factory
The third argument, factory, is a function that should be executed to instantiate the module or an object. If the factory is a function it should only be executed once. If the factory argument is an object, that object should be assigned as the exported value of the module.

requirejs - loading the same module with a different name in different files

Using requirejs, I have two files that depend on the same module.
Must I make sure that I always use the same module name in both dependencies?
For example, if I defined a path for require.config:
'myMod':'src/js/myModule'
And then one time use the name and one time use the actual path, like so:
//in a.js
define(function(require){
var mod = require('myMod');
});
//in b.js
define(function(require){
var mod = require('src/js/myModule');
});
Obviously, the best practice would be to use the module name everywhere, but I want to know why this causes an actual error.
Does requirejs store a map between the module name used and the actual loaded module?
As you yourself say:
Obviously, the best practice would be to use the module name everywhere
RequireJS enforces this practice, up to a point. The thing is that when you tell RequireJS in your paths that 'myMod':'src/js/myModule' you are effectively saying that you want to load src/js/myModule under the name myMod. In all likelihood, if you then refer to it by the full path this is an error in your part. If this were to happen in my code, I'd consider it to be an error. So I'd like RequireJS to fail when this happens.
There's also the issue of semantics. The basic case is that if two names passed to require differ, once relative paths have been resolved, then they refer to two different modules. Going by this, if you do require('myMod') and require('src/js/myModule') you are expecting two different modules. And if you do require('myMod') and require('myMod'), you get the same module. So with the paths you've set, what do you actually want? The same module or different modules?
RequireJS has ways to give you both. If you want both module names to give you the same module, then you remove your paths config and use a map config instead:
map: {
'*': {
myMod: 'src/js/myModule'
}
}
What this means is "in all modules (*), if a request is made for the module myMod, then load module src/js/myModule instead." The intent is crystal clear.
If you'd like instead both paths to give you different modules although they share the same source, you could do it using a RequireJS plugin that loads the same source for your modules but has RequireJS register this single source as different modules.

Is the require function part of the AMD spec?

I've been working with AMD modules and RequireJS quite a bit lately and I'm trying to figure out exactly what the AMD spec is. So far the only thing I've been able to find is the amdjs GitHub page at https://github.com/amdjs/amdjs-api/wiki/AMD.
This page only shows the define( id, dependencies, factory) function as part of the official spec. Require has another function called require( dependencies, callback ) and I'm not sure if its part of the official spec or if its just a conviennece function provided by the RequireJS library.
From the page you linked, under the heading Global Variables:
This specification reserves the global variable "define" for use in
implementing this specification, the package metadata asynchronous
definition API and is reserved for other future CommonJS APIs. Module
loaders SHOULD not add additional methods or properties to this
function.
This specification reserves the global variable "require" for use by
module loaders. Module loaders are free to use this global variable as
they see fit. They may use the variable and add any properties or
functions to it as desired for module loader specific functionality.
They can also choose not to use "require" as well.
Curl is an example of an AMD loader that doesn't use require.
In addition there is a page that details the require API for loaders that support it.

Node.js require() vs RequireJS?

Hello with RequireJS I can set a base path like this: base : './app/' so when I am in ./app/foo/bar/ for example and I have a script where I use require('foo'); RequireJS then would search for ./app/foo.js and not in node_module folder or in ./app/foo/bar/foo.js this comes handy when you have a kind of structure where it would be much cleaner for you as a developer to see the dependencies instead of having ../../foo.js. I could have ./app/foo.js and ./app/foo/foo.js and ./app/foo/bar/foo.js it would be much more cleaner to have:
require('foo');
require('foo/foo');
require('foo/bar/foo');
rather than:
require('../../foo');
require('../foo');
require('./foo');
Now you could say why not change the name and not have foo everywhere, let's say that we can't for any reason…
Another lack of feature that I see in node's require method against RequireJS is the ability of setting path mapping, if I have a directory named ./app/super-sized-directory-name/ in RequireJS I could simply do 'big-dir' : 'super-sized-directory-name' and then I could simply use require('./app/big-dir/foo') with Node.js's require method this is not possible as far as I know…
--alias, -a Register an alias with a colon separator: "to:from"
Example: --alias 'jquery:jquery-browserify'
You can register aliases with browserify, so that covers your renaming.
As for your rooted absolute paths, that can't really be done. As mentioned modul8 has a namespacing mechanism to solve this.
I would recommend you pong SubStack in #stackvm on freenode and ask him directly.
It may or may not help you, but I believe the Dojo Frameworks AMD Loader is API compatible with RequireJS and providing you are using a new microkernel does not pollute the global namespace.
I believe it only has require() and define() in the global namespace now.
Anyway their method of dealing with this is to do something like:
require(["dojo/node!util"], function(util){
// Module available as util
});
The documentation is at http://dojotoolkit.org/reference-guide/1.8/dojo/node.html
Use uRequire which provides a 'bridge' between nodejs require and AMD define modules, without reinventing the wheel (it is build on top of the two standards). It basically converts modules from AMD or commonJS format to the other format or UMD that runs smoothly on both nodejs & the browser.
It is also translating dependency paths with flexible path conventions, so you can have either '../../foo' or 'bar/foo' depending on which makes more sense at the point you are at.
Your AMD or UMD modules are loaded asynchronously on browser (using AMD/requireJs or other AMD loader) and on node the asynchronous require(['dep1', 'dep2'], function(dep1,dep2){...}) is also simulated.

Categories