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);
});
}
});
Related
I want to use datepickk.js inside my module: even thought this plugin supports AMD I couldn't load it inside RequireJS:
http://jsfiddle.net/numediaweb/5xbqqr0j/13/
// Config Object
requirejs.config({
// Local Directory
baseUrl: "/js",
// Script Locations
paths: {
// Common Libraries
"jquery": "//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min",
"datepickk": "//crsten.github.io/datepickk/dist/datepickk.min"
}
});
// Check Dependencies
requirejs(['jquery', 'datepickk'], function ($, Datepickk) {
var loadedMods = require.s.contexts._.defined;
console.log(loadedMods);
$('#message').text('Loaded modules => '+JSON.stringify(loadedMods));
return {};
});
If you check the console you will see that jquery is defined and the module not.
Any idea why this happens?
I tried another variation of loading this module:
require.config({
paths: {
'Datepickk': '//crsten.github.io/datepickk/dist/datepickk.min'
},
But then I get this error:
datepickk.js:1146 Uncaught TypeError: Cannot freeze
at Function.freeze (<anonymous>)
at Datepickk (datepickk.js:1146)
at Object.execCb (require.js:1693)
at Module.check (require.js:881)
at Module.enable (require.js:1173)
at Module.init (require.js:786)
at callGetModule (require.js:1200)
at Object.completeLoad (require.js:1587)
at HTMLScriptElement.onScriptLoad (require.js:1714)
Whoever wrote the AMD code for datepickk.js needs to read up on how to write AMD modules. There are two problems:
The module name is hardcoded as Datepickk because the define call is define('Datepickk', Datepickk). The first argument hardcodes the name. This is really a bad thing to do, as the RequireJS documentation is clear that developers should not hardcode names and instead let the optimizer add a name as needed, but here someone was not paying attention.
This explains why your 2nd configuration, the one with Datepickk in paths works, but your first one does not. You must refer to it as Datepickk in your paths configuration. If you want your own code to refer to it as datepickk, you can use a map configuration in addition to paths:
map: {
"*": {
datepickk: "Datepickk"
}
}
Yeah, even if you fix the above, you still get the error you ran into. Looking at the documentation for Datepickk I see that you are use it with do new Datepickk(...). If you do this, then the object to be frozen should be the new object that is assigned to this by the JavaScript virtual machine when the constructor executes. If you look at the code that makes Datepickk available to other code, this is what you see:
if ( typeof define === 'function' && define.amd ) define('Datepickk', Datepickk);
else if ( typeof exports === 'object' ) module.exports = Datepickk;
else window.Datepickk = Datepickk;
The 2nd and 3rd branch export the Datepickk constructor to the world. That's fine. The 1st branch though, which is the one that matters to you, calls define with Datepickk acting as a module factory. When RequireJS executes the define call, it immediately calls Datepickk to build the module. In this situation this is not set to any specific value, so it gets set to the current Window instance (the JavaScript virtual machine does that) and Object.freeze fails. The define call should be:
define(function () {
return Datepickk;
});
(I've also removed the hardcoded module name.) This builds a module that has for value the function Datepickk.
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.
The code I'm trying to test relies on RequireJs loader plugins. Example with requirejs/text:
require(['text!templates/foo'], function (data) {
// handle loaded data
});
For a specific unit test, I'm trying to mock the response for text!templates/foo and override with one relevant for the test:
it('should load a template', function (done) {
// TODO: mock 'text!templates/foo' here to return 'mock_data'
// templateViewer uses the text plugin internally to do the actual loading
templateViewer.templateFor('foo', function (error, templateData) {
expect(templateData).toEqual('mock_data');
done();
});
});
I've looked at RequireJs dependency mock solutions, especially Squire.js but it seems they are all suited for mocking regular dependencies and not plugin responses.
I've also looked at stub libraries like sinon to maybe replace the actual require call but that seems problematic.
What's the recommended practice? I prefer not to replace the entire text plugin with a mock one in my requirejs configuration, just override some of its responses in specific tests.
My setup is node+mocha+requirejs
Edit
Please see this example fiddle project to see my issue with Squire:
http://runnable.com/VUBoI0ex6v9Gs-BJ/squirejs-with-plugins-for-node-js-and-hello-world
This will mock what you'd get from requiring text!foo/x.html. Plugins are not special, you just need to mock the entire path, including the plugin name.
var requirejs = require("requirejs");
var assert = require("assert");
requirejs.config({
baseUrl: __dirname,
packages: [
{
name: "squire",
location: "node_modules/squirejs",
main: "src/Squire"
}
]
});
var x;
before(function (done) {
requirejs(["squire"], function (Squire) {
var injector = new Squire();
injector.mock("text!foo/x.html", "foo").require(["text!foo/x.html"],
function (_x) {
x = _x;
done();
});
});
});
it("foo", function () {
assert.equal(x, "foo");
});
The problem you run into with the example code you added to your question is that you use the global require instead of using a require passed by your loader. You should add require as a dependency:
define(['require', ....], function (require, ....) {
The require module is special and reserved by RequireJS. It returns a reference to the require function. You must use it, for instance, when you use RequireJS's contexts so that a module loaded in a specific context uses a require function that is bound to that context. SquireJS also needs you to do this so that it can trap your calls to require. The global require bypasses SquireJS.
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)
});
I'm fairly new to RequireJS and I've run into a bit of a problem. I've written a little framework built on Backbone using RequireJS and I want it to be re-usable in different projects. So, with some searching I learned that require allows packages. This seemed like what I was looking for. I have a main.js file to launch my app that essentially looks like this:
require.config({
packages: ['framework']
});
require(['framework'], function(framework) {
framework.createDash();
});
Then in the same directory as my main.js I have another directory called "framework" which contains another main.js which looks like this:
define(function(require, exports, module) {
exports.createDash = function(dash, element) {
require(['dash/dash.model', 'dash/dash.view'], function(DashModel, DashView) {
return new DashView({
model: new DashModel(dash),
el: element ? element : window
});
});
};
});
In searching I found this page which indicates that the 'require' argument should be scoped to the submodule. However, when I try to require things they are still relative to my original main.js. I've tried a number of things and searched for hours to no avail. Is there any way I can have my require/define calls within my package included relative to the main.js in it's root?
You need to define your submodule as package in the require configuration:
require.config({
packages: [
{ name: 'packagename',
location: 'path/to/your/package/root', // default 'packagename'
main: 'scriptfileToLoad' // default 'main'
}]
... some other stuff ...
});
To load your module you just need to use your 'packagename' at the requirements:
define(['jquery', 'packagename'], function($, MyPackage) {
MyPackage.useIt()
});
In your package you must use the ./ prefix to load your files relative to your submodule:
define(['globalDependency', './myLocalFile'], function(Asdf, LocalFile) {
LocalFile.finallyLoaded();
});
There is a useful shortcut: If your package name equals to your location and your main file is called 'main.js', then you can replace this
packages: [
{ name: 'packagename',
location: 'packagename',
main: 'main'
}]
to this:
packages: ['packagename']
As far as I can see, you already tried to define a package but did you also use the ./ prefix? Without this prefix require will try to find the files in it's global root-path. And without a package, ./ will be useless because the relative path is the same as the global root-path.
Cheers
I figured out the answer to my question, and the solution (they were not the same apparently). I guess I'll post it here in case it can help someone else in the future.
Essentially what I was wanting was to load my framework within its own context. I found the context option under the configuration section on require's website and an example of how to use it. Originally I tried this by doing something like:
var req = require.config({
baseUrl: 'framework',
context: 'framework',
paths: {
jQuery: 'lib/jquery/jquery-1.7.min.js',
Underscore: 'lib/underscore/underscore.min.js',
Backbone: 'lib/backbone/backbone.min.js',
etc...
}
});
req(['main'], function() {});
There were two problems with this. First, my 'req' variable was being defined outside of the framework, but I wanted the framework to define it's own paths. And second, whenever a file outside of the framework would require a file within the framework, which would in turn require 'jQuery', for example, then jQuery (or whatever else) wouldn't be required from within the context of the framework instance of require and so it couldn't find the file.
What I ended up doing was defining my framework's main.js to look something like this:
var paths = {
jQuery: 'lib/jquery/jquery-1.7.min.js',
Underscore: 'lib/underscore/underscore.min.js',
Backbone: 'lib/backbone/backbone.min.js',
etc...
};
define(function() {
var exports = {};
exports.initialize = function(baseUrl, overridePaths, callback) {
if(!overridePaths) {
overridePaths = {};
}
if(baseUrl && baseUrl[baseUrl.length - 1] != '/') {
baseUrl = baseUrl + '/';
}
var fullpaths = {};
for(var path in paths) {
// Don't add baseUrl to anything that looks like a full URL like 'http://...' or anything that begins with a forward slash
if(paths[path].match(/^(?:.*:\/\/|\/)/)) {
fullpaths[path] = paths[path];
}
else {
fullpaths[path] = baseUrl + paths[path];
}
}
var config = {paths: fullpaths};
for(var pathName in overridePaths) {
config.paths[pathName] = overridePaths[pathName];
}
require.config(config);
// Do anything else you need to do such as defining more functions for exports
if(callback) {
callback();
}
}
return exports;
});
And then in my project's main.js file I just do this:
require(['framework/main'], function(framework) {
// NOTE: This setTimeout() call is used because, for whatever reason, if you make
// a 'require' call in here or in the framework without it, it will just hang
// and never actually go fetch the files in the browser. There's probably a
// better way to handle this, but I don't know what it is.
setTimeout(function() {
framework.initialize('framework', null, function() {
// Do stuff here
}
}, 0);
});
This takes whatever is passed in to the framework's initialize() method for 'baseURL' and prepends that to any paths that the framework defines that do not start with a forward slash or 'anything://', unless they are override paths. This allows the package using the framework to override things like 'jQuery'.
This worked for me, adding a "./" prefix to the module names:
define(function (require, exports, module) {
exports.createDash = function (dash, element) {
require([ './dash/dash.model', './dash/dash.view' ], function (DashModel, DashView) {
return new DashView({
model : new DashModel(dash),
el : element ? element : window
});
});
};
});
A process that worked well for me for allowing a package with submodules to be used directly from data-main or from an outside framework, assuming that a main.js (or other package main) is called by a particular name, was to use var baseUrl = require.toUrl('packageName') + '/../' as a prefix to a require.config({ paths: { ... } }) configuration file. For instance:
var music21Base = require.toUrl('music21') + '/../';
require.config({ paths: {
'jquery': music21Base + 'ext/jquery/jquery.2.1.10.min';
'subModuleLoader': music21Base + 'src/subModuleLoader';
} });
The setting of context: "xxx" worked fine for calling normal modules with ./modName but did not work for the paths argument for me.