Requirejs: AMD Module not defined - javascript

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.

Related

Load CycleJS with SystemJS

I tried to load Cycle DOM from their CDN through SystemJS with something like:
System.config({
map: {
'cycle-dom': 'https://unpkg.com/#cycle/dom#17.1.0/dist/cycle-dom.js',
'xstream': 'https://cdnjs.cloudflare.com/ajax/libs/xstream/10.3.0/xstream.min.js',
}
});
System.import('cycle-dom', cycleDOM => {
...
});
But I quickly found out cycle-dom needs xstream. So I try to load both:
Promise.all([
System.import('xstream'),
System.import('cycle-dom')
])
.then(([xs, cycleDOM]) => {
...
});
But I still get the same error. It looks like cycle-dom is expecting xstream to exist on window when it's first loaded. So I tried:
System.import('xstream')
.then(xs => window['xstream'] = xs)
.then(() => System.import('cycle-dom'))
.then(cycleDOM => {
...
});
I feel like I'm going about this all wrong. How can I do this?
Update:
Following martin's advice below, I tried configuring xstream as a dependency of cycle-dom.
Here's a jsbin that demonstrates. What I'm doing is loading cycle-run and cycle-dom and then running the example off the cycle home page.
But I get the error:
"TypeError: Cannot read property 'default' of undefined"
Undefined in this case is cycle-dom trying to load window['xstream'], which isn't being loaded.
Thanks.
The System.import() call returns a Promise so you need to put the callback into its then() method (the second parameter is the parent name; not a callback).
System.import('cycle-dom').then(function(cycleDOM) {
console.log(cycleDOM);
});
This prints the module exports.
I don't have any experience with cycle.js so I can't tell whether this is enough or not. Nonetheless you can set this package dependencies with meta config:
System.config({
map: {
'cycle-dom': 'https://unpkg.com/#cycle/dom#17.1.0/dist/cycle-dom.js',
'xstream': 'https://cdnjs.cloudflare.com/ajax/libs/xstream/10.3.0/xstream.min.js',
},
meta: {
'cycle-dom': {
deps: [
'xstream'
]
}
}
});
Again, I don't know whether this is enough or not. The SystemJS documentation contains pretty well explained example how to load dependencies that need to register some global variables. See https://github.com/systemjs/systemjs/blob/master/docs/module-formats.md#shim-dependencies
Edit:
In this case it's a little more complicated. The cycle-run.js script is generated probably by browserify and you can see it contains a line as follows:
var xstream_1 = (typeof window !== "undefined" ? window['xstream'] : typeof global !== "undefined" ? global['xstream'] : null);
This checks whether window['xstream'] exists when it's loaded. This means that the xstream has to be loaded before loading the cycle-run.js script. The way SystemJS works is that it loads the requested module and then loads its dependencies (you can see the order in Developer Tools). So it's the opposite order than you need (this is very similar to my question on SystemJS GitHub page).
This means you need to restructure the import calls:
System.config({
// ...
meta: {
'xstream': {
format: 'global',
exports: 'xstream',
}
}
});
System.import('xstream').then(function() {
Promise.all([
System.import('cycle-run'),
System.import('cycle-dom'),
])
.then(([cycle, cycleDOM]) => {
// ...
});
});
This registers the xstream before loading cycle-run. Also with the meta configuration for xstream this ensures that the window.xstream exists only inside these callbacks and doesn't leak to the global scope.
See your updated demo: https://jsbin.com/qadezus/35/edit?js,output
Also to use format and exports you need to use the newer SystemJS 0.20.* and not 0.19.*.

Configure a generic jQuery plugin with Browserify-shim?

I'm using browserify-shim and I want to use a generic jQuery plugin. I have looked over the Browserify-shim docs multiple times and I just can't seem to understand what's going on and/or how it knows where to put plugins, attach to the jQuery object etc. Here's what my package.json file looks like:
"browser": {
"jquery": "./src/js/vendor/jquery.js",
"caret": "./src/js/vendor/jquery.caret.js"
},
"browserify-shim": {
"caret": {
"depends": ["jquery:$"]
}
}
According the the example given on the browserify-shim documentation, I don't want to specify an exports because this plugin (and most if not all jQuery plugins) attach themselves to the jQuery object. Unless I'm doing something wrong above, I don't understand why it doesn't work (I get an error telling me the function is undefined) when I use it. See below:
$('#contenteditable').caret(5); // Uncaught TypeError: undefined is not a function
So my question is, how does one configure a generic jQuery plugin (which attaches itself to the jQuery object) with browserify and browserify-shim?
After revisiting this and trying some more things, I finally wrapped my head around what browserify-shim is doing and how to use it. For me, there was one key principle I had to grasp before I finally understood how to use browserify-shim. There are basically two ways to use browserify-shim for two different use cases: exposing & shimming.
Background
Let's say you want to just drop in a script tag in your markup (for testing or performance reasons like caching, CDN & the like). By including a script tag in the markup the browser will hit the script, run it, and most likely attach a property on the window object (also known as a global in JS). Of course this can be accessed by either doing myGlobal or window.myGlobal. But there's an issue with either syntax. It doesn't follow the CommonJS spec which means that if a module begins supporting CommonJS syntax (require()), you're not able to take advantage of it.
The Solution
Browserify-shim allows you to specify a global you'd like "exposed" through CommonJS require() syntax. Remember, you could do var whatever = global; or var whatever = window.global; but you could NOT do var whatever = require('global') and expect it to give you the right lib/module. Don't be confused about the name of the variable. It could be anything arbitrary. You're essentially making a global variable a local variable. It sounds stupid, but its the sad state of JS in the browser. Again, the hope is that once a lib supports CommonJS syntax it will never attach itself via a global on the window object. Which means you MUST use require() syntax and assign it to a local variable and then use it wherever you need it.
Note: I found variable naming slightly confusing in the browserify-shim docs/examples. Remember, the key is that you want to include a lib as if it were a properly behaving CommonJS module. So what you end up doing is telling browserify that when you require myGlobal require('myGlobal') you actually just want to be given the global property on the window object which is window.myGlobal.
In fact, if you're curious as to what the require function actually does, it's pretty simple. Here's what happens under the hood:
var whatever = require('mygGlobal');
becomes...
var whatever = window.mygGlobal;
Exposing
So with that background, let's see how we expose a module/lib in our browserify-shim config. Basically, you tell browserify-shim two things. The name you want it accessible with when you call require() and the global it should expect to find on the window object. So here's where that global:* syntax comes in. Let's look at an example. I want to drop in jquery as a script tag in index.html so I get better performance. Here's what I'd need to do in my config (this would be in package.json or an external config JS file):
"browserify-shim": {
"jquery": "global:$"
}
So here's what that means. I've included jQuery somewhere else (remember, browserify-shim has no idea where we put our tag, but it doesn't need to know), but all I want is to be given the $ property on the window object when I require the module with the string parameter "jquery". To further illustrate. I could also have done this:
"browserify-shim": {
"thingy": "global:$"
}
In this case, I'd have to pass "thingy" as the parameter to the require function in order to get an instance of the jQuery object back (which it's just getting jQuery from window.$):
var $ = require('thingy');
And yes, again, the variable name could be anything. There's nothing special about $ being the same as the global property $ the actual jQuery library uses. Though it makes sense to use the same name to avoid confusion. This ends up referencing the the $ property on the window object, as selected by the global:$ value in the browserify-shim object in package.json.
Shimming
Ok, so that pretty much covers exposing. The other main feature of browserify-shim is shimming. So what's that? Shimming does essentially the same thing as exposing except rather than including the lib or module in HTML markup with something like a script tag, you tell browserify-shim where to grab the JS file locally. There's no need to use the global:* syntax. So let's refer back to our jQuery example, but this time suppose we are not loading jQuery from a CDN, but simply bundling it with all the JS files. So here's what the config would look like:
"browser": {
"jquery": "./src/js/vendor/jquery.js", // Path to the local JS file relative to package.json or an external shim JS file
},
"browserify-shim": {
"jquery": "$"
},
This config tells browserify-shim to load jQuery from the specified local path and then grab the $ property from the window object and return that when you require jQuery with a string parameter to the require function of "jquery". Again, for illustrative purposes, you can also rename this to anything else.
"browser": {
"thingy": "./src/js/vendor/jquery.js", // Path to the local JS file relative to package.json or an external shim JS file
},
"browserify-shim": {
"thingy": "$"
},
Which could be required with:
var whatever = require('thingy');
I'd recommend checking out the browserify-shim docs for more info on the long-hand syntax using the exports property and also the depends property which allows you to tell browserify-shim if a lib depends on another lib/module. What I've explained here applies to both.
Anonymous Shimming
Anonymous shimming is an alternative to browserify-shim which lets you transform libs like jQuery into UMD modules using browserify's --standalone option.
$ browserify ./src/js/vendor/jquery.js -s thingy > ../dist/jquery-UMD.js
If you dropped that into a script tag, this module would add jQuery onto the window object as thingy. Of course it could also be $ or whatever you like.
If however, it's requireed into your browserify'd app bundle, var $ = require("./dist/jquery-UMD.js");, you will have jQuery available inside the app without adding it to the window object.
This method doesn't require browserify-shim and exploits jQuery's CommonJS awareness where it looks for a module object and passes a noGlobal flag into its factory which tells it not to attach itself to the window object.
For everyone, who is looking for a concrete example:
The following is an example of package.json and app.js files for a jQuery plugin that attaches itself to the jQuery/$ object, e.g.: $('div').expose(). I don't want jQuery to be a global variable (window.jQuery) when I require it, that's why jQuery is set to 'exports': null. However, because the plugin is expecting a global jQuery object to which it can attach itself, you have to specify it in the dependency after the filename: ./jquery-2.1.3.js:jQuery. Furthermore you need to actually export the jQuery global when using the plugin, even if you don't want to, because the plugin won't work otherwise (at least this particular one).
package.json
{
"name": "test",
"version": "0.1.0",
"description": "test",
"browserify-shim": {
"./jquery-2.1.3.js": { "exports": null },
"./jquery.expose.js": { "exports": "jQuery", "depends": [ "./jquery-2.1.3.js:jQuery" ] }
},
"browserify": {
"transform": [
"browserify-shim"
]
}
}
app.js
// copy and delete any previously defined jQuery objects
if (window.jQuery) {
window.original_jQuery = window.jQuery;
delete window.jQuery;
if (typeof window.$.fn.jquery === 'string') {
window.original_$ = window.$;
delete window.$;
}
}
// exposes the jQuery global
require('./jquery.expose.js');
// copy it to another variable of my choosing and delete the global one
var my_jQuery = jQuery;
delete window.jQuery;
// re-setting the original jQuery object (if any)
if (window.original_jQuery) { window.jQuery = window.original_jQuery; delete window.original_jQuery; }
if (window.original_$) { window.$ = window.original_$; delete window.original_$; }
my_jQuery(document).ready(function() {
my_jQuery('button').click(function(){
my_jQuery(this).expose();
});
});
In the above example I didn't want my code to set any globals, but I temporarily had to do so, in order to make the plugin work. If you only need jQuery, you could just do this and don't need any workaround: var my_jQuery = require('./jquery-2.1.3.js'). If you are fine with your jQuery being exposed as a global, then you can modify the above package.json example like so:
"browserify-shim": {
"./jquery-2.1.3.js": { "exports": "$" },
"./jquery.expose.js": { "exports": null, "depends": [ "./jquery-2.1.3.js" ] }
Hope that helps some people, who were looking for concrete examples (like I was, when I found this question).
Just for completeness, here is a method that exploits jQuery's CommonJS awareness to avoid having to worry about polluting the window object without actually needing to shim.
Features
jQuery included in the bundle
plugin included in the bundle
no pollution of the window object
Config
In ./package.json, add a browser node to create aliases for the resource locations. This is purely for convenience, there is no need to actually shim anything because there is no communications between the module and the global space (script tags).
{
"main": "app.cb.js",
"scripts": {
"build": "browserify ./app.cb.js > ./app.cb.bundle.js"
},
"browser": {
"jquery": "./node_modules/jquery/dist/jquery.js",
"expose": "./js/jquery.expose.js",
"app": "./app.cb.js"
},
"author": "cool.blue",
"license": "MIT",
"dependencies": {
"jquery": "^3.1.0"
},
"devDependencies": {
"browserify": "^13.0.1",
"browserify-shim": "^3.8.12"
}
}
Method
Because jQuery is CommonJS-aware these days, it will sense the presence of the module object provided by browserify and return an instance, without adding it to the window object.
In the app, require jquery and add it to the module.exports object (along with any other context that needs to be shared).
Add a single line at the start of the plugin to require the app to access the jQuery instance it created.
In the app, copy the jQuery instance to $ and use jQuery with the plugin.
Browserify the app, with default options, and drop the resulting bundle into a script tag in your HTML.
Code
app.cb.js
var $ = module.exports.jQuery = require("jquery");
require('expose');
$(document).ready(function() {
$('body').append(
$('<button name="button" >Click me</button>')
.css({"position": "relative",
"top": "100px", "left": "100px"})
.click(function() {
$(this).expose();
})
);
});
at the top of the plugin
var jQuery = require("app").jQuery;
in the HTML
<script type="text/javascript" src="app.cb.bundle.js"></script>
Background
The pattern used by jQuery is to call it's factory with a noGlobal flag if it senses a CommonJS environment. It will not add an instance to the window object and will return an instance as always.
The CommonJS context is created by browserify by default. Below is an simplified extract from the bundle showing the jQuery module structure. I removed the code dealing with isomorphic handling of the window object for the sake of clarity.
3: [function(require, module, exports) {
( function( global, factory ) {
"use strict";
if ( typeof module === "object" && typeof module.exports === "object" ) {
module.exports = factory( global, true );
} else {
factory( global );
}
// Pass this if window is not defined yet
} )( window, function( window, noGlobal ) {
// ...
if ( !noGlobal ) {
window.jQuery = window.$ = jQuery;
}
return jQuery;
}) );
}, {}]
The best method I found is to get things working in the node module system and then it will work every time after browserify-ing.
Just use jsdom to shim the window object so that the code is isomorphic. Then, just focus on getting it to work in node. Then, shim any traffic between the module and global space and finally browserify it and it will just work in the browser.
I was using wordpress. Hence, I was kind of forced to use the wordpress core's jQuery, available in window object.
It was generating slick() not defined error, when I tried to use slick() plugin from npm. Adding browserify-shim didn't help much.
I did some digging and found out that require('jquery') was not consistent always.
In my theme javascript file, it was calling the wordpress core's jquery.
But, in slick jquery plugin it was calling the latest jquery from node modules.
Finally, I was able to solve it. So, sharing the package.json and gulpfile configuration.
package.json:
"browserify": {
"transform": [
"browserify-shim"
]
},
"browserify-shim": {
"jquery": "global:jQuery"
},
gulpfile.babel.js:
browserify({entries: 'main.js', extensions: ['js'], debug: true})
.transform(babelify.configure({
presets: ["es2015"]
}))
.transform('browserify-shim', {global: true})
Doing transform 'browserify-shim' was crucial part, I was missing earlier. Without it browserify-shim was not consistent.

Use node require inside a method called using requireJs

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.

RequireJS Optimization

I`m using r.js to optimize my app,
as i saw in several samples, i used build.json configuration file to config my optimization options.
The problem is that when i set reference to the output javascript file after optimization I`m getting the following error in the browser:
Uncaught ReferenceError: define is not defined main-built.js:14735
Looks like, all my app modules are exists but RequireJs is missing.
This is my build.json configuration file:
{
"baseUrl": "../",
"name": "src/modules/main",
"include": ["src/modules/main", "src/modules/navbar/navbar", "src/modules/contact/contact", "src/modules/about/about"],
"exclude": [], "optimize": "none", "out": "main-built.js",
"insertRequire": ["src/modules/main"]
}
How do i add requirejs to the output js file? maybe i need to add something else to config? or maybe the problem is not the config?
Thanks,
Ori
Try:
<script src="scripts/require.js" data-main="scripts/main-built"></script>
If I understood correctly, this is how it should work.
What r.js does is that it compiles all RequireJS modules into a single file. However you still need to load that file with RequireJS script, for example:
<script data-main="scripts/main" src="scripts/require.js"></script>
So just add a minified version of require.js to your website and load the optimized module using that.
You have to include require.js if you have modularized your project using RequireJS:
<script data-main="scripts/main" src="scripts/require.js"></script>
This is because RequireJS handles the loading of modules and resolving dependencies. Without it, your browser does not know what define means. A way to get around this is to use UMD (Universal Module Definition). This makes it so that your module can be used with or without RequireJS. You can see many examples here. One that fits your use case is:
// Uses AMD or browser globals to create a module.
// If you want something that will also work in Node, see returnExports.js
// If you want to support other stricter CommonJS environments,
// or if you need to create a circular dependency, see commonJsStrict.js
// Defines a module "amdWeb" that depends another module called "b".
// Note that the name of the module is implied by the file name. It is best
// if the file name and the exported global have matching names.
// If the 'b' module also uses this type of boilerplate, then
// in the browser, it will create a global .b that is used below.
// If you do not want to support the browser global path, then you
// can remove the `root` use and the passing `this` as the first arg to
// the top function.
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['b'], factory);
} else {
// Browser globals
root.amdWeb = factory(root.b);
}
}(this, function (b) {
//use b in some fashion.
// Just return a value to define the module export.
// This example returns an object, but the module
// can return a function as the exported value.
return {};
}));

Access RequireJS path configuration

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);
});
}
});

Categories