I have a need to pass a configuration value into all my AMD modules using requireJS.
I can happily pass a configuration value to a specific module by using the following example; as given in the requireJS API config modules documentation
requirejs.config({
config: {
'bar': {
raw: true
},
'baz': {
raw: true
},
...
}
});
The above works fine, but I have some 50 modules that I want to pass the same configuration value to, and these could increase or change. I could define 50 module names, as above, and pass the value, but I don't really want to have to define each module by name and maintain that list, instead I'd like to do something like this.
requirejs.config({
config: {
'*': {
raw: true
}
}
});
I have tried the above but it did not work.
requireJS config map appears to support the "*" wildcard, but I don't see any mention of similar for "config". I have searched, but I guess I'm having a "bad search day".
So, the question is: Is there a "*" wildcard support for "config" and I am just having a problem with it? Or is there some other way to accomplish this?
I don't know a way to use the wildcard syntax directly, but you can accomplish the same thing through the simplest module definition at the bottom of your data-main:
requirejs.config({
// normal stuff
});
define('moduleconfig', {
color: "black",
size: "unisize"
});
And then instead of requiring the special 'module' module, just require your 'moduleconfig' module:
define( ['underscore', 'jquery', 'moduleconfig'], function( _, $, moduleconfig) {
console.log('Color', moduleconfig.color)
});
Related
So I'm trying to set up Typescript and Chutzpah for testing purposes. Typescript is set up to output in this format:
define(['require', 'exports', './someModule'], function(require, exports, someModule) {
//examplecode
});
Which works fine, the problem occurs when someModule is actually a directory with an index.js.
/app
app.js
/someModule
index.js
require.js is unable to resolve someModule in this way and the test fails.
Is there any way to tell require.js that this is a module?
RequireJS won't automatically check for the presence of index.js and load that as your module. You need to tell RequireJS that when you want to load someModule, it should load someModule/index. I'd set a map in my call to require.config:
require.config({
[ ... ]
map: {
'*': {
someModule: 'someModule/index',
}
},
});
You have to adjust the name you give there so that it is a path relative to your baseUrl. It's not clear from the information you give in your question what it should be.
(For the record, there's also a packages setting that you could probably tweak to do what you want but putting something packages says "this is a package", which is not what you appear to have here. So I would not use it for what you are trying to do.)
I didn't like the configuration in map either. The most simple way I accomplished this was writing a plugin for require.
Let's name the plugin mod, where it is to be used as mod!module/someModule, you can also call it index as in index!module/someModule, whatever suits you best.
define(function(require, exports, module) {
// loading module/someModule/index.js with `mod!`
var someModule = require('mod!module/someModule');
// whatever this is about ..
module.exports = { .. };
});
So lets assume you have paths set in require's configuration with some sort of project structure:
- app
- modules
- someModule/index.js // the index we want to load
- someModule/..
- someModule/..
- etc
- plugins
- mod.js // plugin to load a module with index.js
Requires config:
require.config({
paths: {
'module': 'app/modules',
// the plugin we're going to use so
// require knows what mod! stands for
'mod': 'app/plugins/mod.js'
}
});
To read all the aspects of how to write a plugin, read the docs at requirejs.org. The simplest version would be to just rewrite the name of the requested "module" you are attempting to access and pass it back to load.
app/plugins/mod.js
(function() {
define(function () {
function parse(name, req) {
return req.toUrl(name + '/index.js');
}
return {
normalize: function(name, normalize) {
return normalize(name);
},
load:function (name, req, load) {
req([parse(name, req)], function(o) {
load(o);
});
}
};
});
})();
This is not production code, it's just a simple way to demonstrate that requires config wasn't meant to solve problems like this.
My library use kriskowal/Q promises library and now I'm trying to load (with requirejs) application that use my library so I put all paths and shims and my requirejs.config section looks like this:
requirejs.config({
baseUrl: './',
catchError: false,
paths: {
beril: '../engine/build/src/bundle',
lodash: 'bower_components/lodash/lodash',
three: 'bower_components/three.js/build/three',
q: 'bower_components/q/q',
},
shim: {
lodash: {
exports: '_'
},
three: {
exports: 'THREE'
},
q: {
exports: 'Q'
},
beril: {
deps: ['lodash', 'three', 'q'],
exports: 'beril'
},
}
});
After this I suppose variables THREE, _ and Q to be defined in global space.
Now I'm loading and runinng application with this simple line:
require(['beril', 'js/stepbystep/' + $stateParams.page + '/app'], (beril, app) => app());
but then I'm getting error: ReferenceError: Q is not defined even though I can see in Chrome's network inspector that Q library have been loaded.
Also all the rest dependencies (THREE and _) are defined. Seems like requirejs`s shim does not work for this library. Can it be or am I missing something?
So what am I'm doing wrong and how should I deal with this situation?
I found solution and it was to add init function to my library's shim and then add Q as global object manually so my shim section now looks like this:
shim: {
lodash: {
exports: '_'
},
three: {
exports: 'THREE'
},
q: {
exports: 'Q'
},
beril: {
deps: ['lodash', 'three', 'q'],
exports: 'beril',
init: function(lodash, three, q){
window.Q = q;
}
},
}
however I don't clearly understand why it does not work without this and are there better ways to deal with this situation.
There are a few problems with your configuration. For one thing, you have unnecessary shim configurations. I installed lodash just now (with bower install lodash) and searched its code. It calls define. So you must not set a shim for it. RequireJS won't give you an error but you will get undefined behavior. The same is true of Q: it calls define so, no shim for it. Last I checked, THREE needs a shim.
The fact that Q calls define is also why it does not leak the symbol Q into the global space. It acts like a well-behaved AMD-module.
Ok, so how can we get Beril to find Q? Your solution works but I find it slightly iffy. The problem is that init is executed after the shimmed module is loaded. As long as Beril refers to Q only in the body of functions to be executed later, it will work. And I guess this is how Beril works now. However, if a new version of Beril needs to refer to Q when the file that contains Beril is first executed, it will fail because Q does not exist yet.
One way to work around the problem that is future-proof is to use map and some glue. Keep your shim for beril but remove the init. Define a module named q-glue:
define(['q'], function (Q) {
window.Q = Q;
return Q;
});
And declare this map in your configuration:
map: {
beril: {
q: "q-glue"
}
}
This says "when q is requested from beril load q-glue instead." By doing this, window.Q will be defined before Beril is loaded.
I take it you are the author of Beril. I urge you to make your library AMD-compatible so as to spare users of your library having to go through configuration pains to get it working with AMD-loaders (like RequireJS).
It worked for me after I downgraded to version 0.9 up until 1.0.1 as mentioned in the readme. The current version 2.0.2 has significant braking changes as mentioned by Kris Kowal here.
I didn't have the need to set the global variable as the older version checks the environment and does so if loaded before require.
You need to add q to require()...
require(['beril', 'q', 'js/stepbystep/' + $stateParams.page + '/app'], (app, beril, q) => app());
I'm trying to load a library that compiles to Webpack in a require.js project. While the library exposes an object, it returns null when required from the require.js project :
define(function(require, exports, module) {
[...]
require("./ext/mylib.core.js"); // -> null
})
Is there any flags that I can use in Webpack to enable AMD compliance ? There are some references to AMD in the generated library but as it is it does not seem to do anything.
The solution was in Webpack documentation : there is an outputLibrary flag that can be set to "amd" or "umd" and in that case webpack produces amd compliant modules.
EDIT 3:/EDIT: 4
Webpack is not cooperating it may seem, so another possibility would be to expose the module with the shim config option:
require.config({
paths: {
// Tell require where to find the webpack thingy
yourModule: 'path/to/the/webpack/asset'
},
shim: {
// This lets require ignore that there is no define
// call but will instead use the specified global
// as the module export
yourModule: {
exports: 'theGlobalThatIsPutInPlaceByWebpack'
}
}
});
This obviously only works in the case that the webpack stuff is putting something in the global scope. Hope this helps!
EDIT 2:
So I got the question wrong as pointed out in the comments. I didn't find any built-in functionality to produce AMD modules from webpack - the end result seems to be a static asset js file. You could wrap the result in a
define(function () {
return /* the object that webpack produces */;
});
block, maybe with the help of some after-build event (e.g. using this after build plugin for webpack). Then you should be able to require the module with an AMD loader.
Original Answer:
require.js loads it's dependencies asynchronously, you have to declare them explicitly when you're not using the r.js optimizer or the like. So if the module exposes an AMD definition it should work like this:
// It works the way you did it ...
define(['path/to/your/module'], function (require, exports, module) {
require('path/to/your/module'); // -> { ... }
});
// ... but I personally prefer this explicit syntax + it is
// friendlier to a code minifier
define(['path/to/your/module'], function (yourModule) {
console.log(yourModule); // { ... }
});
Maybe you have to configure your require instance, there are docs for that.
EDIT1: as pointed out the way the module is being accessed is not wrong but the dependencies were missing, so I added code that is closer to the original question.
Is it possible to use the default node require function in a file that has been called through requirejs?
define(["require", "exports"], function(require, exports) {
//...
var Schema = require(DaoPublic._schemasDirectory + schemaFilename);
}
I always get ReferenceError: module is not defined, I also tried to load the schema using requireJs, same, because the file itself is coded as CommonJs, not AMD compatible.
Any solution?
Note that the loaded schema is in CommonJS and I need to keep this way, since it's used by several DAO, some in AMD and other in CommonJs. (Funny part)
Example of requested file (schema):
var userSchema = {
/**
* User Login, used as id to connect between all our platforms.
*/
login: {
type: String,
match: /^[a-zA-Z0-9_-]+$/,
trim: true,
required: true,
notEmpty: true,
unique: true,
check: {
minLength: 4,
maxLength: 16
}
}
};
module.exports = userSchema;
The problem is that your code is set so that RequireJS is able to find the CommonJS module by itself. However, when RequireJS is running in Node and cannot find a module, it will call Node's require function, which is what you need. So it is possible (with RequireJS) to have an AMD module use Node's require but the trick is getting RequireJS to not see the module in the first place.
Proof of Concept
Here's a proof of concept. The main file named test.js:
var requirejs = require("requirejs");
function myRequire(path) {
if (path.lastIndexOf("schemas/", 0) === 0)
path = "./" + path;
return require(path);
}
requirejs.config({
paths: {
"schemas": "BOGUS"
},
nodeRequire: myRequire
});
requirejs(['foo'], function (foo) {
console.log(foo);
});
The file foo.js:
define(["require", "exports"], function(require, exports) {
return require("./schemas/x") + " by way of foo";
});
The file schemas/x.js:
module.exports = "x";
If you run it with node test.js, you'll get on the console:
x by way of foo
Explanation
I'm calling this a "proof of concept" because I've not considered all eventualities.
The paths setting is there to throw RequireJS off track. BOGUS must be a non-existent directory. When RequireJS tries to load the module ./schemas/x, it tries to load the file ./BOGUS/x.js and does not find it. So it calls Node's require.
The nodeRequire setting tells RequireJS that Node's require function is myRequire. This is a useful lie.
The myRequire function changes the path to add the ./ at the start before calling Node's require. The issue here is that for some reason RequireJS transforms ./schemas/x to schemas/x before it gives the path to Node's require function, and Node will then be unable to find the module. Adding back the ./ at the start of the path name fixes this. I've tried a whole bunch of path variants but none of them worked. Some variants were such that RequireJS was able to find the module by itself and thus never tried calling Node's require or they prevented Node from finding the module. There may be a better way to fix this, which I've not found. (This is one reason why I'm calling this a "proof of concept".) Note that I've designed this function to only alter the paths that start with schemas/.
Other Possibilities
I've looked at other possibilities but they did not appear to me very promising. For instance, customizing NODE_PATH would eliminate myRequire but such customization is not always doable or desirable.
I notice in the documentation there is a way to pass custom configuration into a module:
requirejs.config({
baseUrl: './js',
paths: {
jquery: 'libs/jquery-1.9.1',
jqueryui: 'libs/jquery-ui-1.9.2'
},
config: {
'baz': {
color: 'blue'
}
}
});
Which you can then access from the module:
define(['module'], function (module) {
var color = module.config().color; // 'blue'
});
But is there also a way to access the top-level paths configuration, something like this?
define(['module', 'require'], function (module, require) {
console.log( module.paths() ); // no method paths()
console.log( require.paths() ); // no method paths()
});
FYI, this is not for a production site. I'm trying to wire together some odd debug/config code inside a QUnit test page. I want to enumerate which module names have a custom path defined. This question touched on the issue but only lets me query known modules, not enumerate them.
It is available, but it's an implementation detail that shouldn't be depended on in production code ( which you've already said it's not for, but fair warning to others! )
The config for the main context is available at require.s.contexts._.config. Other configurations will also hang off of that contexts property with whatever name you associated with it.
I don't believe require exposes that anywhere, at least I can't find it looking through the immense codebase. There are two ways you could achieve this though. The first and most obvious is to define the config as a global variable. The second, and closer to what you want, is to create a require plugin that overrides the load function to attach the config to the module:
define({
load: function (name, req, onload, config) {
req([name], function (value) {
value.requireConfig = config;
onload(value);
});
}
});