Trying to use a non-AMD module in require - javascript

I'm trying to use a non-AMD JS module I have (called mymodule) in requirejs. This is the first time to try to do so. Following what I saw from bootstrap-amd, and underscore-amd, I tried the following:
// start of mymodule
if(typeof mymodule === "undefined") loaded = {};
mymodule.cache = (function() {
...
return {
...
};
})();
mymodule.main = (function() {
...
return {
doSomethingJQuery: function() { ... }
}
})();
.
.
.
// end of mymodule
// This part I added for the sake of requirejs
if(typeof define === "function" && define.amd) {
define(function() {
return mymodule;
});
}
You can see the first part is my module (using, I hope, good JS module pattern :). I wanted to keep the option of using the module independent from require JS so if I didn't want to use requirejs in another project, I don't have to. So I kinda didn't want to wrap the entire module in a define(...) function... unless it's common practise to have a mymodule.js and mymodule-amd.js files.
Anyway my module loads,
define(["mymodule"], function (mymodule) {
// do it - ReferenceError: jQuery is not defined :(
mymodule.doSomethingJQuery();
.
.
.
..but as doSomethingJQuery() depends on jQuery, I get an error. How can I bring in jQuery here, or should it be done differently? I tried to find some examples but pretty new to requirejs. Would like to do things in the proper way. As mentioned, I'd like to also keep the option to use the module independently. Would appreciate any pointers here, thanks.

You need to make your module dependent on jQuery and pass the reference to a function that will define your module. Change the code that defines your module to:
if(typeof define === "function" && define.amd) {
define(['jquery'], factory);
}
else
mymodule = factory(jQuery);
The else part is necessary for cases that are not AMD because you're now using a factory function. (You could also have a branch to deal with CommonJS environments like Node but I'm not going to add it here.)
Then make a function that defines your module:
function factory($) {
// define the module
return mymodule;
}
The entire thing could be wrapped in an IIFE to avoid polluting the global space with factory.

Related

Include JavaScript code without packaging as a module

How do I require a JavaScript library that is not packaged as a UMD-compatible module (AMD, CommonJS) with webpack?
I don't want the library to pass through a loader. I just want it to be included in a <script> tag whenever it is required, and for webpack to manage this dependency.
I don't want to simply place it in a script tag in my index.html, because I want to take advantage of webpack's code-splitting, and only include it when necessary.
I've read about 'externals', I'm not sure if this has anything to do with it. The docs are not clear enough.
Thanks :)
Updated question
Also this question is specifically about front-end libraries that only need to be included via a <script> tag to work.
You can add amd support to your library and then load it with webpack.
Some links that can help you achieve are:
http://ifandelse.com/its-not-hard-making-your-library-support-amd-and-commonjs/
https://github.com/mbostock/d3/pull/1921
What is basically being done is that at the top of your library you can check whether it's a commonjs environment or an AMD environment. Accordingly you can export your library.
One example can be this ( taken from 1st link )
(function (root, factory) {
if(typeof define === "function" && define.amd) {
// Now we're wrapping the factory and assigning the return
// value to the root (window) and returning it as well to
// the AMD loader.
define(["postal"], function(postal){
return (root.myModule = factory(postal));
});
} else if(typeof module === "object" && module.exports) {
// I've not encountered a need for this yet, since I haven't
// run into a scenario where plain modules depend on CommonJS
// *and* I happen to be loading in a CJS browser environment
// but I'm including it for the sake of being thorough
module.exports = (root.myModule = factory(require("postal")));
} else {
root.myModule = factory(root.postal);
}
}(this, function(postal) {
// module code here....
return myModule;
}));

How to know when to import a package or paste it in index.html

OK, I am using ES6 and ReactJS, some packages you should do import React from "react"; in order to have them working, but others <script src="/semantic/dist/semantic.min.js"></script> in index.html, so, what cases should I apply one or another ?
In general, there are two types of modules. ES6 and non-ES6. If you want to use a non-ES6 modules with ES6, you can try one of the following approaches:
Compile with the CommonJS (for example using jQuery as CommonJS through npm)
Load with SystemJS to allow ES6 to work with CommonJS, AMD and globals
If you don't want to do this, you can try importing the non-ES6 scripts in HTML. In which case, you won't be doing something like
import $ from 'jquery';
So in short, if you want to use a non-ES6 module without compiling with CommonJS or if it is not available through npm, you can use HTML imports instead of ES6 import
I came across this question in the following situation: I want to use the react and react-dom api in a script I'm writing for a local index.html file. I can put a <script src="react-cdn-url"></script> tag in the <head> of index.html, but I would prefer to have the apis on my local system. So I wget react-cdn-url and see if I can just import it. I get some error on the console, so I look at the react.js file I now have and see the following lines:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.React = factory());
}(this, (function () { 'use strict';
...
})));
Once I decipher this, I should be able to make my react api "importable." So let's peel this apart bit by bit. First construct:
(function (global,factory){}(this,(function() {...})));
This is a bit confusing at first, so first look at the expression: (function (params){}(args)); The purpose of this is to create an unnamed function which takes paramaters params, then immediately call it with arguments arg. Contrast this with ;function (params){}(args);. This is not valid javascript, if you try it in node you sort of break the parser, if you do it in firefox console you'll get the error: SyntaxError: function statement requires a name. The reason why is that this is an incomplete syntactic construct. Javascript grammar allows you to write a function definition without a bound name, but this only makes sense in an expression. Enclosing the function definition in parenthesis causes it to be evaluated. Putting this all together we have an unnamed function being called on arguments this and (function(){}) (another unnamed function). this will be whatever the local context is, and the unnamed function corresponds to the factory parameter in the "top-level" anonymous function. Consider the following three lines:
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.React = factory());
These check to see if this module is being imported from node, if so, it exports the result of the factory function. If it is being imported by require.js, it "defines" the factory function, if neither of these then the result of factory is given the name 'React' in the global object. This last case applies when "importing" the library through a <script> tag.
However, none of these cases really apply if you try to es6 import it. In order to make the import work as desired we will just export the factory function, then call it in the importing script. Why not just call the factory function and assign it to a name for export? Because you will need to pass the result of this factory to the factory for react-dom. In general, there are many issues continuing down this path, and eventually it will be better to settle upon some sort of dependency manager, but for now we keep playing.
let ReactFactory;
export default ReactFactory = function () { 'use strict';
...
}
I do the same thing to the react-dom file, changing these lines:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react')) :
typeof define === 'function' && define.amd ? define(['react'], factory) :
(global.ReactDOM = factory(global.React));
}(this, (function (React) { 'use strict';
...
})));
into:
let ReactDOMFactory;
export default ReactDOMFactory = function (React) { 'use strict';
...
}
Note that this time the factory function has the parameter React. I see a few lines down:
if (!React) {
{
throw Error("ReactDOM was loaded before React. Make sure you load the React package before loading ReactDOM.");
}
}
From this I take the hint that I need to create my React object from my first factory and pass it into the ReactDOMFactory. All in all, I put the following lines in my script.js file:
import ReactFactory from '/path-to-modified/react.js';
import ReactDOMFactory from '/path-to-modified/react-dom.js';
let React = ReactFactory();
let ReactDOM = ReactDOMFactory(React);
Where, of course, path-to-modified is the path to the modified api files, and voila! Now it works just fine.

Use AMD defined module within 3rd party, non-AMD library

I have a library - call it SomeLib - which is defined to support various module loaders:
(function(global, factory) {
if (typeof define === 'function' && define.amd) {
define([], factory);
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = factory();
} else {
global.UriTemplate = factory();
}
})(this, function() {
...
// returns constructor function
});
I can easily load it with RequireJS like
require.config({
paths: {
'theLibrary: '../path/to/the/lib'
}
});
Then I have another 3rd-party library - call it AnotherLib - which internally uses SomeLib like
var the Lib = new SomeLib(...);
That means SomeLib has to be available globally.
AnotherLib is just a plain JavaScript module function
(function(){
// the code
})();
It is not compliant with particular module loaders.
When I include AnotherLib with RequireJS, I do something like
require.config({
paths: {
'theLibrary: '../path/to/the/lib',
'anotherLib: '../path/to/anotherLib'
},
shim: {
'anotherLib: [
'theLibrary'
]
}
});
The problem is that I get a undefined exception on the line within AnotherLib where it instantiates SomeLib (new SomeLib(...)).
This is because SomeLib is not defined on the global object but rather published as an AMD module which AnotherLib doesn't "require".
Can I solve this somehow, or does AnotherLib have to be AMD compliant and properly require SomeLib.
The best thing would be to get an AMD-compliant library or to make the library AMD compliant. The latter option would entail modifying the source manually or having a build step that turns the non-AMD-compliant code into a real AMD module. How to do this depends on how the library is designed.
An method that would work with any library is to deliberately leak the symbol that the library requires into the global space:
Make anotherLib be dependent on a new module, which you could call SomeLib-leak.
Create the new module. This definition does not have to be in a separate file. I usually place such "glue" modules before my call to require.config. The module would be like this:
define('SomeLib-leak', ['SomeLib'], function (SomeLib) {
window.SomeLib = SomeLib;
});
I do purposely have define set the module name here. Usually, you don't want to call have define set the module name but for "glue" modules that are placed like I indicated above, this is necessary.
By the time anotherLib is loaded, SomeLibrary will be in the global space.

Where is the function 'define' defined in this codebase?

I was looking through file upload plugin's javascript code.
There is a piece of code which checks if define is a function and declares a few dependencies i guess
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
// Register as an anonymous AMD module:
define([
'jquery',
'jquery.ui.widget'
], factory);
} else {
// Browser globals:
factory(window.jQuery);
}
}
The comments just above the code says define is global. But i dont have it in my application built using angularjs.
I searched the codebase for declaration of define. BUt couldn't find any. I googled for AMD and i could see define being used here again.
My question is where is 'define' defined?
define is a function whose functionality is defined by the Asynchronous Module Definition spec­if­i­cat­ion. AMD is just a spec; there's multiple implementations, of which RequireJS is one. You could see how RequireJS implements it if you want, but keep in mind that RequireJS's define isn't the only define out there.

Browserify: Use module.exports if required, otherwise expose global

I'm considering adopting browserify for some of my projects, but would like to make sure that others don't have to use browserify if they want to use the (bundled) code. The obvious way to do this is to both expose the modules exports through module.exports as well as through a window. global. However, I'd rather not pollute the global namespace for those who are requireing the script.
Is it possible to detect if a script is being required? If it is, then I could do something like:
var mymodule = (function() { ... })();
if (isRequired()) {
module.exports = mymodule;
} else {
window.mymodule = mymodule;
}
Note that no matter what, this will be bundled beforehand, so the var mymodule won't be exposing a global. Also, currently I'm using the revealing module pattern, but would be willing to switch to something more appropriate for browserify.
What's the best way to make a module both requireable and <script src=able? Is it best to just expose a global in both circumstances?
There is a good article from Forbes Lindesay explaining how to do standalone builds:
http://www.forbeslindesay.co.uk/post/46324645400/standalone-browserify-builds
The short version, use the standalone option:
browserify beep.js --standalone beep-boop > bundle.js
I'm dealing with the same problem building a library and here is a rought opinion. I think we need to separate first the audiences for a library in few categories:
those who use browserify and NPM
those who will just download a mylib.min.js and use one way or another
AMD (with bower?), might be the third category.
So, for 1 it is easy, you will have a your index.js module:
module.exports = function () { /* code */ }
and your package.json will have a main
"main": "index.js"
Notice I haven't add any window.xx code to index.js.
For 2 I think the best idea is to create a standalone.js
var mylib = require('./index.js');
global.window.mylib = mylib;
This is what browserify should build.
For 3 (if you care about) you can tweak standalone.js as follows:
var mylib = require('./index.js');
if (typeof global.window.define == 'function' && global.window.define.amd) {
global.window.define('mylib', function () { return mylib; });
} else {
global.window.mylib = mylib;
}
Assuming another library hasn't created a global module.exports object, you can simply check for the existence of module.exports
var mymodule = (function() { ... })();
if (module && module.exports) {
module.exports = mymodule;
} else {
window.mymodule = mymodule;
}
Why not just wrap the entire thing with a closure and pass exports as a parameter?
(function (exports) {
// code here
// ...
exports.foo = bar;
})(exports || this);
This way it will also export it to WebWorker scope and other 'windowless' environments.

Categories