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.
Related
Repo is available here to highlight the issue.
I am having a problem with a race condition. I have created a ConfigModule - this has a forRoot and a forChild.
The forRoot sets up the loading of the .env file and the forChild uses it in another module.
The problem is that forChild is called before forRoot. The ConfigService would be injected with missing config because forRoot hasn't executed first.
> AppModule > ConfigModule.forRoot InstanceModule >
> ConfigModule.forChild
I placed some simple console.log commands that output this
I am in Config Module FOR CHILD
I am in Config Module FOR ROOT
As you can see the forChild is being executed first, I tried using forwardRef and that didn't work.
If you let the application run you will see
[2019-03-24T11:49:33.602] [ERROR] ConfigService - There are missing mandatory configuration: Missing PORT
[2019-03-24T11:49:33.602] [FATAL] ConfigService - Missing mandatory configuration, cannot continue!, exiting
This is because I check that some process.env are available which are loaded in via dotenv. Of course, because the forRoot isn't executed first then the forChild returns its own new instance of the ConfigService.
ConfigService validates the availability of the environment variables.
So, basically, the forChild is executing and returning its own ConfigService before forRoot.
TO make it work, if you comment out the InstanceModule inside the AppModule then it will automatically start listening and returns the port number from an environment variable.
Of course, because InstanceModule uses the forChild - there is a race condition.
Why does this not work?
1) Nest builds up a dependency graph and instantiates the given modules and their providers according to this graph. The order of your imports or the naming of your dynamic modules's methods (forRoot/forChild) does not influence the order of the instantiation.
2) When you create dynamic modules, each module will be its own instance, they won't be singletons like regular modules. In your case, you'd create two different ConfigModule instances and with it, two different ConfigService instances; hence they won't share your .env configuration. This can't work, independent of the instantiation order.
Alternatives
Have a look at the nestjs/typeorm package. Under the hood, it creates a shared TypeOrmCoreModule, which is shared between the different dynamic module instances created by TypeOrmModule.forRoot / TypeOrmModule.forChild. For it to be shared and dynamic at the same time, it has to be made #Global though. In your case, since you don't have any configurations in your forChild imports, you would just make the whole ConfigModule global and then omit the forChild() imports, since the ConfigService would be globally available anyway.
If you don't want your service to be globally available, you could initialize your service after the startup process, for example in your AppModule's onModuleInit method.
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.
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'], ....
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.
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.