Specifying relative paths from `RequireJs` - javascript

I have the baseUrl in a RequireJs based application set to /angular. Sometimes I'd like to set the path relative to the base, other times I'd like to set it relative to the current directory.
I'm very new to RequireJs and very confused.
This is what I have now:
require([
'require',
'angular',
'module/draft/draftDisplay'// module that I want relative to baseUrl
], function(requireLocal, angular) {
requireLocal('./autoSave'); // Modules that I want relative to current url
requireLocal('./module');
Which creates this error: Error: Module name "autoSave" has not been loaded yet for context: _
As I said before, I can't get a good handle on how RequireJs works. Some things I don't understand are:
1) When does RequireJs use the baseUrl and when does it use the current directory
2) What's the difference between a module ID and its path?
3) Does the way previous modules are specified in the require([...],.. array affect how subsequent ones are resolved (which seemed to be the case when I tinkered)
If you could include those things in your answer, that would be very helpful.

1) When does RequireJs use the baseUrl and when does it use the current directory
Always baseUrl, except when the module name is prefixed by ./ or ../, in which case it uses the directory of the module that requires the other.
2) What's the difference between a module ID and its path?
The path includes the baseUrl. E.g. could be something like scripts/vendor/angular/angular.js. The module id does not include the baseUrl or the suffix, e.g. for baseUrl: 'scripts', the module id above would be vendor/angular/angular.
3) Does the way previous modules are specified in the require([...],.. array affect how subsequent ones are resolved (which seemed to be the case when I tinkered)
No.
Your error is caused from using the synchronous version of require for modules that are not loaded. Use the asynchronous version instead; something like:
requireLocal(['./autoSave', './module'], function(autosave, module) {
// here autosave and module are loaded
});
// BEWARE: Here neither autosave nor module are loaded
But are you sure you want the inner require? From the information you provide, pulling the requirements in the outer require would probably suffice, i.e.
require(['require','angular','module/draft/draftDisplay','./autoSave','./module'],...)
Also, the import called 'module' has special meaning in require; it contains information about the current module; If you are literally using './module' it will be quite different than 'module' (the first looks for a module.js file in the same directory as this file, the second provides information about this module).

Related

Relative Path for Typescript module import pointing to wrong Directory

I have an application layer written in visual-studio-2017 using typescript. I also have Web Resources that are built using javascript and html. The html for the Web Resources use requirejs to include the compiled Typescript code wherever necessary. The problem I am seeing is with the relative paths I am using in my Typescript. When I publish the Web Resources to Microsoft Dynamics, RequireJS is attempting to load the relative paths from my Typescript relative to the html file rather than relative to the transpiled Javascript file.
WebAPI
Transpiled to build/CCSEQ/WebAPI.js (Model.js is located at build/CCSEQ/Model.js)
"use strict";
import * as Model from "./Model.js";
export class WebAPI{
// Code here
}
Main.html
Located at /WebResources/html/Main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Main</title>
</head>
<body>
<script src="require.js" data-main="../js/Main.js"></script>
</body>
</html>
Main.js
Located at /WebResources/js/Main.js
"use strict";
// Require fails because it is looking for /WebResources/html/Model.js rather than /build/CCSEQ/Model.js
require(["WebAPI.js"], function(WebApi){
// Code Here
});
The first thing you should do is drop the .js from your module names (both your require call and your import statement). The names you pass in require and define are module names. They look like paths but they are not really paths. RequireJS takes the module names and transforms them to paths using the map, paths and baseUrl configuration options (to name the ones used most frequently). When you do not use these options, it uses sensible defaults.
If you do use .js in a module name, RequireJS assumes you are passing a path rather than an actual module name. (The same is true if you start the string with a slash, use a query parameter like in (foo?blah), or if you have a colon in the string, which RequireJS takes as indication that you are using a protocol (e.g. http://).) In this case, there is no transformation of a module name to a path so map, paths and baseUrl are all ignored. Here is the piece of code:
//If a colon is in the URL, it indicates a protocol is used and it is just
//an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?)
//or ends with .js, then assume the user meant to use an url and not a module id.
//The slash is important for protocol-less URLs as well as full paths.
if (req.jsExtRegExp.test(moduleName)) {
//Just a plain path, not module name lookup, so just return it.
//Add extension if it is included. This is a bit wonky, only non-.js things pass
//an extension, this method probably needs to be reworked.
url = moduleName + (ext || '');
} else {
The value of req.jsExtRegExp is /^\/|:|\?|\.js$/.
(You can keep your .js in the value you pass to data-main because data-main is special.)
Moreover, you need to do something so that require(["WebAPI"]... will resolve to a correct path. You can do this by setting a baseUrl in your RequireJS configuration that points to the directory that contains the file WebAPI.js. Or you can use paths:
paths: {
WebAPI: '../../build/CCSEQ/WebAPI',
}
Note how the value for the key WebAPI also lacks the .js extension. You must not have it there either. The value there is a path (not a module name) and the issue you'll have if you put .js there is that RequireJS will try to find a file with the .js.js extension!
If you want to read more about the difference between module names (aka module ids) and paths, I have an answer that goes over that specifically.

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'], ....

getting jquery load/get use the current require.js path

require.js allows to smartly redefine the "include path" so that you can decide to install your dependancies into arbitrary sub-directories, transparently for them.
Well, this is true for the recursive require() these might do, but unfortunately it doesn't seem to work for the jquery load/get they might do: these still refere to html path, so that datas cannot be moved together with their lib.js.
how to make jquery load/get refer to this current require path ?
or am I missusing require.js features ?
thanks !
You can use toUrl for this. (Documented: go here in the documentation and search for toUrl.)
You need to have require as a dependency of your module (define(["require", ...], function (require, ...) {...) and then you call require.toUrl(path) where path is a path relative to your module. This call will return a path which is the same as if the path argument were a module's path defined in your RequireJS configuration. In other words, it is a path which will be relative if your baseUrl in your configuration was relative, or absolute if baseUrl is absolute. At any rate, you should just be able to feed it to $().load or $.get.

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.

When using Requirejs, is it possible to have the optimizer automatically find all the dependencies?

I have many modules. One module loads another module, which loads another module. Etc...
And of course, when I load the page, all of these modules load. It works perfectly. Without the optimizer. (even though it takes a minute, because the browser has to load 50 things).
When I use optimizer...in my app.build.js, it seems that I have to manually specify each module!??
Why can't optimizer automatically traverse through the modules?
You only have to specify the module you wanna optimize, not its dependencies. From the docs:
In the modules array, specify the module names that you want to
optimize, in the example, "main". "main" will be mapped to
appdirectory/scripts/main.js in your project. The build system will
then trace the dependencies for main.js and inject them into the
appdirectory-build/scripts/main.js file.
({
appDir: "../",
baseUrl: "scripts",
dir: "../../appdirectory-build",
modules: [
{
name: "main"
}
]
})
Solved.
I had my paths wrong (I didn't understand baseURL , etc) . That's why things broke in the middle.

Categories