Add non-AMD modules before AMD modules - javascript

I'm using requirejs for a project and I have 2 modules:
a.js: is a non-AMD module that I cannot touch its code
b.js: is a AMD module that I have written with define() function. It requires a.js to work.
app.js: is the actual application code that uses both a.js and b.js.
app.js looks like this:
//source code for app.js
require(['a.js', 'b.js'],
function( a, b ) {
a.x = 2;//this will fail because 'a' is not defined
});
Now the question is: what is the simplest way to require() both modules in app.js? I can't do it like:
//source code for app.js
require(['b.js', 'a.js'],
function( b ) {
a.x = 2;//it works because module 'a' defines a global variable named 'a'
b.x = 2;//this will fail because module 'b' is loaded before 'a' so it doesn't work
});

Since, as you say, a.js exports a global variable called a, you can configure RequireJS to expose it in an AMD manner using the shim config option. Any module requiring a.js won't even know it's not a proper module. In your case the config would be something like this:
requirejs.config({
shim: {
'a.js': {
exports: 'a' // a.js defines 'window.a'
}
}
});

This is standard fare for AMD loaders. Most of the time, a shim will not be needed. Your source for app.js is correct but you didn't show your source for b.js. Since you have control over b.js, it should be written as an AMD module with a dependency on a.js
// source code for b.js
// You have to use define, not require, and the dependency on a.js must be here rather than via a nested or internal require
define(['a.js'], function(a){
// your code for b.js
});
This will ensure that a.js is loaded before b.js when app.js is ready to execute.

Related

What will be format of first define() and require() function in requirejs project

Question#1: After requirejs configuration, what will be the format of first require() call to load my main.js
App.js
requirejs.config({
baseUrl: 'lib',
paths: {
app: '../app'
}
});
require(['app/main'], function(m){
console.log('loading m...');
});
Question#2: And what will be format of first define() function in main.js file.
Main.js
define(function () {
var messages = require('./messages');
var print = require('print');
});
The only issue I see in the code you show is that your main.js should have its define call like this:
define(function (require) {
var messages = require('./messages');
var print = require('print');
});
If you pass a function as the first parameter to define, RequireJS will call it with the special modules require, exports and module but at a minimum you must declare the first parameter so that the calls you made inside your factory function are to the local require (passed to the function) rather than the global require. There are a number of issues if you call the global require. One of them is that relative module references like ./messages won't be resolved correctly.
The require call you have in your app.js looks fine.

How to import from nested module?

I have a Typescript library I've written which is being emitted as follows. I'm not sure how to import from this module, though.
node_modules/my-module/dist/index.js (partial)
define("services/UserService", ["require", "exports", ..., "services/BaseService"], function (require, exports, ..., BaseService_31) {
"use strict";
var UserService = (function (_super) {
// ... stuff here ...
return UserService;
}(BaseService_31.BaseService));
exports.UserService = UserService;
});
I've installed this package with npm and configured JSPM as follows:
config.js (partial)
System.config({
defaultJSExtensions: true,
transpiler: "none",
paths: {
"*": "dist/*",
"github:*": "jspm_packages/github/*",
"npm:*": "jspm_packages/npm/*",
"npm-ext:*": "node_modules/*"
},
map: {
...
"my-module": "npm-ext:my-module/dist/index.js",
...
}
});
I was expecting to be able to import this class as follows...
import {UserService} from "my-module/services/UserService";
I expected SystemJS to resolve the path to my-module and then locate the services/UserService module, and grab the single export UserService. But in the Chrome Console I see this is the path which is being loaded:
node_modules/my-module/dist/index.js/models/UserService.js
What's the correct way to import a module such as this?
Bonus: how can I get around including the full path to index.js?
Your library code was emitted using named define
define("services/UserService",
And it looks like there are several such modules defined in the index.js file, that is, index.js is a bundle.
The only way to import such module using SystemJS is
import {UserService} from "services/UserService";
That is, imported module name must match exactly the name given to define. In your case, the location of the file does not affect module name in any way.
Now, SystemJS has to be configured to load library file, node_modules/my-module/dist/index.js, when it sees that import. For bundles, the preferred way to to it is via bundles config:
bundles: {
"npm-ext:my-module/dist/index.js": ["services/UserService.js"]
}
Module name here, services/UserService.js, must have .js extension because it must match imported module name after defaultJSExtesions:true is applied.
If you want the module to be imported as my-module/services/UserService.js, then you need to make sure it is registered with that name. The easiest way is to have corresponding source file structure when compiling the library - that is, have UserService.ts in my-module/services folder.
You can find more information about named define in SystemJS module format docs, which says in particular
Named defines are supported and will write directly into the loader registry.
A single named define will write into the loader registry but also be treated as the value of the module loaded if the names do not match. This enables loading a module containing define('jquery', ....
P.S. Your browser tries to load
node_modules/my-module/dist/index.js/models/UserService.js
where does models come from?

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.

Javascript Require.js dependencies

if I have 2 .js files (A.js, B.js) and each of them has their own dependencies.
A depends on: C.js, D.js, E.js
B depends on: C.js, F.js, G.js
A and B have C as common dependency. If both A and B are loaded with Require.js, will the C.js be loaded twice?
C.js will only be loaded once, as long as it's loaded using the same name in A.js amd B.js.
Another useful thing you can do is as follows:
require.config({
"paths": {
"C": "/somewhere/js/C-v1.0.0.js"
}
});
If you then require A and B, in A and B you can:
require(["C"], function(C) {
}
This way, if the location or version of C changes, you only have to change it in one place.

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