Path issue with RequireJS - javascript

I'm developing an applicattion with RequireJS and i have a module used as dependece. All dependences's libraries are in "lib/vendor", so, assumming it calls "MyModule", it is in "lib/vendor/MyModule/my.js.."
In my main.js i have:
3 requirejs.config({
4 paths: {
5 my: './vendor/MyModule/my',
....
And it works, but the problem is that "my.js" includes some files too
define(["./lib/a", "./lib/b"], function (a,b) {
....
});
These files "a.js" and "b.js" are inside of module's folder, so the structure should be something like this:
index.html
lib
main.js
vendor
MyModule
my.js
lib
a.js
b.js
The problem is when my.js invoked to "a.js" or "b.js" RequireJS tries to find them to "host.com/lib/lib/a.js" instead "host.com/lib/vendor/MyModule/lib/a.js"
According "http://requirejs.org/docs/api.html#defdep" the thing i'm doing it should work, but it is not happening

The problem is that relative dependencies are loaded relative to the name of your module rather than its path. You refer to this example:
//my/shirt.js now has some dependencies, a cart and inventory
//module in the same directory as shirt.js
define(["./cart", "./inventory"], function(cart, inventory) {
This will work only if the my/shirt.js file is loaded as the my/shirt module. In other words, there is an assumption in this example that there is no paths setting that will change the modules' paths.
Here is what happens with your code:
RequireJS loads main. You don't set a baseUrl so the baseUrl is set to /lib.
You require my. RequireJS resolves it to /lib/vendor/MyModule/my.js and loads it.
The my module requires ./lib/a and ./lib/b. When RequireJS resolves the relative paths, it uses the current module's name (my) as its starting point. The initial period in the two requirements means "get the directory part of the current module name and add the rest of the path to it." The directory part of my is the empty string, so the two paths become lib/a and lib/b and then are combined with the baseUrl to form /lib/lib/a and /lib/lib/b.
Note that the paths setting for my is not used at all in this computation.
One way to get around your issue would be to use map instead of paths. Whereas paths associates paths to module names, map associate module names to module names so:
map: {
'*': {
my: 'vendor/MyModule/my'
}
}
This tells RequireJS that in all modules (*) if the module with name my is required, the module with name vendor/MyModule/my is loaded.

Related

Require module defined in another script

Script A
;(function(g){
define('hotness', [], function() {
function f() {;
console.log('gotem');
}
return f;
});
define('otherModule', [], function() {});
})(this);
Script B
define([
'ScriptA',
], function() {
var hotness = require('hotness')
});
Error: Module name "hotness" has not been loaded yet for context: _
What is the recommended way to require in a definition from ScriptA in ScriptB?
I've also tried:
Script B Alt
define([
'require',
'ScriptA',
'hotness'
], function(require) {
var hotness = require('hotness')
});
Which gives me
Error loading resource ... /hotness.js: No such file or directory
Error: Script error for: hotness
EDIT:
It's important to note that ScriptA stands as is and will not be modified. The question is how can I get at the named module in ScriptA from ScriptB.
EDIT2:
I have no control over the HTML or any other aspect of the page. I must do everything within ScriptB.
EDIT3:
I have an example of something that works but it looks like a horrible antipattern work-around so I didn't even want to mention it:
define(['require'], function(require) {
// why use window? IDK.
// this is just verbatim what is "working" for someone else
window.require(['scriptA'], function(sA) {
//sA never actually used
window.require([
'otherModule'
], function(oM) {
var hotness = require('hotness'),
You should use a bundles configuration. Given what you've described works, you should use:
bundles: {
'scriptA': ['hotness', 'otherModule']
}
This essentially tells RequireJS "when you want to find hotness or otherModule then load scriptA because they are defined there". I use this every now and then to load modules from bundles that have been generated with r.js.
Other than this Joseph the Dreamer is correct that you should not in the same module mix the AMD and CommonJS methods of getting dependencies.
Your ScriptA uses named modules. They must be loaded after RequireJS but prior to any of the dependents. RequireJS does not recommend named modules unless you know what you're doing (like load a library like a regular script, and register it as a module at the same time).
Error: Module name "hotness" has not been loaded yet for context: _
This is a generic RequireJS error, when the module is loaded from the server, but somehow RequireJS can't provide it to the dependent. Usually happens when the module has syntax errors or when there's a circular dependency, among others.
ScriptB also has problems. It's trying to use both AMD style (array dependencies) and CommonJS style (explicit require). Just use one of the two syntaxes. Note that in the CommonJS format, it needs the module to have a first argument named require to trigger CommonJS format of writing.
define(['hotness'], function(hotness) {
// use hotness
});
// or
define(function(require){
var hotness = require('hotness');
// use hotness
});
Error loading resource ... /hotness.js: No such file or directory
Be sure to set a base url. It can be implicit (based on the data-main location) or explicit (using require.config). Module names are usually paths + filenames relative to the base url.
It is advised that you use one file per module, containing a module definition that has no name (the file name becomes the name of the module) and resides somewhere under the set baseUrl.

How do you request a named module with requirejs

I think I'm just missing something simple but I've been struggling with requirejs (v2.1.14). Say I have a module defined with a name (perhaps from the r.js optimizer): i.e.
// mymodule.js
define("modname", ['dep1', 'dep2', 'dep3'], function(){ ... });
If I try to require that module elsewhere it doesn't work. I've tried using
require(['../path/to/file/mymodule'], function(mod){ // mod is undefined here. }
which results in mod being undefined, and
require(['modname'], function(mod){...}
which errors looks for a file named modname.js in the current directory. As soon as I remove the module name part of the define statement from the output then it works fantastically.
define(['dep1', 'dep2', 'dep3'], function(mod){ // mod is defined and good here }
Am I missing some fundamental piece of requiring named modules??
Require will not know where to look for named modules if they are not in the base directory (or defined in the same file as the require statement), so you need to add a paths entry. You can't use the relative path syntax.
In your config:
paths: {
'modname': 'path/to/mymodule',
},
The optimizer uses this to define multiple modules, while keeping them in a single file.

How can I use a local file during Require.js optimisation, but a CDN-hosted version at runtime?

My page includes several components that exist as separate AMD modules. Each of these components is turned into a single file by the Require.js optimiser. Because several of these components share dependencies (e.g. jQuery and d3), the optimiser paths config uses CDN URLs for those dependencies, rather than bundling them into the optimised file.
Here's where it gets tricky. I've written a module loader plugin for Ractive.js called rvc.js, which allows me to include Ractive components that are defined in HTML files. (Yes, I'm asking for help on how to use my own library.)
This works fine - code like this gets optimised as you'd expect:
define( function ( require ) {
var ChartView = require( 'rvc!views/Chart' );
var view = new ChartView({ el: 'chart' });
});
Because Ractive is used by several of the components, it should be served from a CDN like jQuery and d3. But it's used by rvc.js during the optimisation process, which means that the Ractive entry for the optimiser's paths config can't point to a CDN - it has to point to a local file.
Is there a way to tell Require.js 'use the local file during optimisation, but load from CDN at runtime'?
So here's the solution I eventually settled on. It feels somewhat kludgy, but it works:
Stub out the loaders and the library you don't want bundled
Add an onBuildWrite function that rewrites modules depending on the library, so that they think they're requiring something else entirely - in this case Ractive_RUNTIME
Add an entry to your runtime AMD config's paths object, so that Ractive_RUNTIME points to the CDN
My optimiser config now looks like this:
{
baseUrl: 'path/to/js/',
out: 'build/js/app.js',
name: 'app',
optimize: 'none',
paths: {
'amd-loader': 'loaders/amd-loader',
'rvc': 'loaders/rvc',
'Ractive': 'lib/Ractive'
},
stubModules: [ 'amd-loader', 'rvc', 'Ractive' ],
onBuildWrite: function ( name, path, contents ) {
if ( contents === "define('Ractive',{});" ) {
// this is the stub module, we can kill it
return '';
}
// otherwise all references to `Ractive` need replacing
return contents.replace( /['"]Ractive['"]/g, '"Ractive_RUNTIME"' );
}
}
Meanwhile, the script that loads the app.js file created by the optimiser has a config entry that points to the CDN:
require.config({
context: uniqueContext,
baseUrl: baseUrl,
paths: {
'amd-loader': 'loaders/amd-loader',
'rvc': 'loaders/rvc',
'Ractive': 'lib/Ractive',
'Ractive_RUNTIME': 'http://cdn.ractivejs.org/releases/0.3.9/Ractive.min'
}
});

Why can't I make 2 paths point to the same file twice in require.js?

I'd like to have 2 requirejs paths pointing to the same module:
var require = {
paths: {
"hardPath" : "file",
"alias" : "file"
}
}
When I run my app I get a load timeout error for "alias". If my app's js files only reference one of "hardPath" or "alias" but not the other, it works fine. But if I have js files that reference both of these, I get the load timeout. Is there some reason that require.js does not allow this?
The API that RequireJS uses for this is map. You can configure it such that when any of your modules request 'alias' they are automatically given 'hardPath':
require.config({
// paths, shim, etc.
// and now remap requests for the wrong module name to the right one
map: {
'*': {
'alias': 'hardPath'
}
}
});
From the doc linked above:
In addition, the paths config is only for setting up root paths for module IDs, not for mapping one module ID to another one.

How can I load multiple optimized requirejs modules dynamically in a production env?

I've started to play with require js on a dummy project. I now want to use the r.js script to build my project for production.
The context is this:
Main file called start.js is:
require([/* some stuff */], function (){ /* app logic */ });
which has an if that decides what I should require based on some condition.
The required files are either ModuleA or ModuleB
Both ModuleA and ModuleB have dependencies.
define([/*some deps*/], function(dep1, dep2...) {
/* app logic */
return { /* interface */
}
Everything works fine in development mode, before optimization and module concatenation.
When building with r.js I specify as module targets the following :
modules : [ { name : "start" }, { name : "ModuleA" }, { name : "ModuleB" } ]
The problem is that my ModuleA becomes :
define(dep1 ..);
define(dep2 ..);
define(ModuleA ..);
But nothings loads from ModuleA. The code from ModeulA in development loads and executes, the code after building loads but does not run.
How could I fix this problem?
UPDATE
http://pastebin.com/p1xUcY0A --> start.js
http://pastebin.com/dXa6PtpX --> ModuleA js-animation.js
http://pastebin.com/xcCvhLrT --> ModuleB css-animation.js no deps.
http://pastebin.com/j51V5kMt --> The r.js config file used when running the optimizer.
http://pastebin.com/UVkWjwe9 --> How the js-animation.js looks after running r.js. This is the file that has problems. I don't get the js-animation module from this file. The require does not return my js-animation object.
Edit:
After removing the .js at the end of the module definitions and in from start js, the optimized start.js is http://pastebin.com/LfaLkJaT and the js-animations module is http://pastebin.com/qwnpkCC6. In chrome, I get this error in my console http://pastebin.com/Hq7HGcmm
I believe the problem with your setup is that you end your module dependency names in .js. As per the docs:
RequireJS also assumes by default that all dependencies are scripts, so it does not expect to see a trailing ".js" suffix on module IDs. RequireJS will automatically add it when translating the module ID to a path.
If RequireJS sees a module name ending in .js it assumes that the module name is a path relative to the document. By ending your module dependency names in .js it works fine in development mode because RequireJS will go and load the file specified as a dependency. In your case it will load the file js/js-animation.js, see an anonymous define and load the module properly.
In production, your start.js module still requires "js/js-animation.js". RequireJS will load your optimized module at the path js/js-animation.js but now the optimizer has converted your anonymous modules into named modules (in this case "js/js-animation"). The result is the file will be loaded but no define'd modules within the file have a name that matches "js/js-animation.js" so in a sense your animation module is missing.
Solution / TL;DR: Remove the trailing .js from all your module dependency names (and your module definitions in the r.js config) and you should be fine. So your start.js should become (changes on line 4):
require([], function () {
var $html = $("html"),
animationModule = localStorage['cssanimations'] == 'true' ?
'js/css-animation' : 'js/js-animation',
$doc = $html.find("body");
console.debug("loading ", animationModule);
require([animationModule], function( animationModule ) {
animationModule.run({
target : $("div.flex")
});
} );
} );
Also note your may want to use baseUrl and paths in your RequireJS config to clean up module names (e.g. so you can remove the js/ prefix).
It seems this is problematic with the current require.js implementation. The way around it was to create a global mediator or mediator module and have all dynamically loaded modules call the mediator and announce themselves via an event. This worked for me.

Categories

Resources