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;
}));
Related
Hi I am doing some research on RxJS. I am able to use the library simply by referencing it in my browser as such:
<script src="https://unpkg.com/#reactivex/rxjs#5.5.6/dist/global/Rx.js"></script>
It imports with the global object namespace variable of 'Rx'. I can make observables and do all the fun stuff.
Where everything breaks down is when I change the src to point to the latest UMD file like this <script src="https://unpkg.com/rxjs/bundles/rxjs.umd.js"></script>
The import seems to not be working as exported object functions don't seem to exist?
There is a specific function I am trying to use called 'fromEvent' that allows an observable to be created from any DOM event.
I am getting an error when using the latest RxJS 6.2.2 UMD file.
Why is this? If you look inside the js file at the bottom you can see the export of the function and at the top of the file you see the global namespace called 'rxjs'.
I am not using any loaders like requirejs nor do I have any experimental browser features enabled. I am not using any 'import' statements.
I am simply trying to reference the global namespace of the script object. The syntax for the module definition is identical except for Rx vs rxjs.
To replicate the error, simply create an Observable.fromEvent(.... and watch the error console.
Thanks!
Here's a oneliner to import UMD modules using browser modules and dynamic imports in 2020.
export default async (url, module = {exports:{}}) =>
(Function('module', 'exports', await (await fetch(url)).text()).call(module, module, module.exports), module).exports
Usage example:
const ednToJS = await importUMD(`https://unpkg.com/edn-to-js#0.1.2/dist/main.js`)
const rxjs = await importUMD('https://unpkg.com/rxjs#6.6.3/bundles/rxjs.umd.js')
tada
Recently the UMD bundle was renamed to just rxjs, see https://github.com/ReactiveX/rxjs/commit/556c904ea61a8424e5d24f170b20eadbc05d01f0#diff-6d2911fe563068b8126098588db98a84
If you want to use RxJS 6 you need to switch to "pipable" operators (and creation functions), see https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/migration.md#operator-pipe-syntax
So for example this works:
<script src="https://unpkg.com/rxjs/bundles/rxjs.umd.js"></script>
<script>
rxjs.fromEvent(document, 'click').subscribe(console.log);
</script>
Demo: https://stackblitz.com/edit/rxjs6-demo-r2rtbz?file=index.html
export default async function (url, module = {exports:{}})
{
const response = await fetch(url);
const script = await response.text();
const func = Function("module", "exports", script)
func.call(module, module, module.exports);
return module.exports;
};
Same code reformatted
Here is an example after doing the proper import , note the pipe.
submission = rxjs.fromEvent($('#mybutton'), 'click')
.pipe(rxjs.operators.map((event) => {
return "something"
}));
I may rename the globals to 'r' and 'ro' to avoid the new verbosity.
Also bonus points if someone can point to piped error handling in 6.0!
It imports with the global object namespace variable of 'Rx'.
Maybe version 5.5.6 does but the latest version which you're trying to use, 6.2.2, does not. The object it exports to the global namespace is called rxjs. If you load https://unpkg.com/rxjs/bundles/rxjs.umd.js in a browser you'll see this in the source in the UMD module definition:
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory):
(factory((global.rxjs = global.rxjs || {})));
}(this, (function (exports) { 'use strict'; // etc
If you want to use fromEvent you can do so via rxjs.fromEvent.
To import any umd module from unpkg or jsdeliver in the browser with JavaScript, what I find mostly useful was using d3#require. Since actually importing the umd modules give me a bare "Module" object.
In your case it would work like this, notice I dont actually use any HTML, as a bonus this would also work for deno
import("https://raw.githack.com/d3/d3-require/main/src/index.mjs")
.then(
_=>_.require("https://unpkg.com/#reactivex/rxjs#5.5.6/dist/global/Rx.js")
)
.then(Object.keys)
.then(console.log)
Now, here I show an use case of require, where I require 2 modules, one is the Rx library, and the other is just an inspector library that shows off elements as if it were in the console
import("https://raw.githack.com/d3/d3-require/main/src/index.mjs").then(async _=>{
var inspector=await _.require("#observablehq/inspector");
var Rx=_.require("https://unpkg.com/#reactivex/rxjs#5.5.6/dist/global/Rx.js");
var x=new inspector.Inspector(window.inspect);
x.fulfilled(await Rx)
return x;
})
#import url("https://raw.githack.com/observablehq/inspector/main/src/style.css")
<div id="inspect" />
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.
I've recently downloaded last version of moment.js and it begins to show the following message when trying to call, for example, moment().add(1, 'day');
"Deprecation warning: Accessing Moment through the global scope is deprecated, and will be removed in an upcoming release."
Which is the best way to call moment methonds?
Update: Figured out the problem
The problem was present because I have requirejs in my project and momentjs was trying to warn me that I should use momentjs as a module dependency instead.
The following code was extracted from momentjs v2.9.0
// CommonJS module is defined
if (hasModule) {
module.exports = moment;
} else if (typeof define === 'function' && define.amd) {
define('moment', function (require, exports, module) {
if (module.config && module.config() && module.config().noGlobal === true) {
// release the global variable
globalScope.moment = oldGlobalMoment;
}
return moment;
});
makeGlobal(true);
} else {
makeGlobal();
}
//And this is the 'makeGlobal' function. globalScope
function makeGlobal(shouldDeprecate) {
/*global ender:false */
if (typeof ender !== 'undefined') {
return;
}
oldGlobalMoment = globalScope.moment;
if (shouldDeprecate) {
globalScope.moment = deprecate(
'Accessing Moment through the global scope is ' +
'deprecated, and will be removed in an upcoming ' +
'release.',
moment);
} else {
globalScope.moment = moment;
}
}
So, if I use this library in a CommonJS environment, then I should use import statement.
If I use requirejs, then I should include momentjs as a dependency of my modules.
Finally, if neither the other cases accomplish, then I can use it directly from global scope (window object in browser)
You can use requirejs to pull it in rather than using the global scope:
require.config({
paths: {
"moment": "path/to/moment",
}
});
define(["moment"], function (moment) {
moment().format();
});
Taken from http://momentjs.com/docs/
This really isn't an answer, but an appreciation of the problem:
There has yet to be a cogent explanation of why the deprecation occurs, or a newbie explanation of what it is. Specifically, not paragraph saying 'if you do it the old way, it breaks in a subtle way'. The closest is a bug report that, using node, a single symbol is defined in the global namespace (https://github.com/moment/moment/issues/1214), which is mostly philosophy.
The deprecation comes on usage, so its unclear to people why. It appears to need be fixed in installation.
No one on any chat node has explained it, outside of mirroring the require.js boilerplate. The comment seems to continue as "do it this way and it works". The boilerplate does not cover all users.
Some failing lines include simple constructors like moment(value), which is the whole point of the library.
It appears the minor version upgrade from moment 2.9.0 to 2.10.0 may have forced deprecated code to break, at least for those using ECMAScript and a fallback. Reverting to 2.9.0 will allow you to keep working for now. If you only had the related breakage of moment.duration.fn disappearing, you can upgrade to 2.10.1 or above.
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.
I'm researching whether there is a way to detect, for sure, whether a given script is currently being loaded by RequireJS. An answer for AMD modules in general would be even better, but my use case is only RequireJS.
jQuery and other libraries "detect" it like so:
if ( typeof define === "function" && define.amd ) {
define( "jquery", [], function() {
return jQuery;
});
}
That is sufficient in most cases, but the problem is that it doesn't detect whether the script is being loaded as an AMD module, it only detects whether define exists and supports the AMD spec.
Is there a way, either with RequireJS or with AMD modules in general, for a script to determine (for real) whether it is being loaded as a module?
Take a look at specified():
parentRequire.specified(moduleName): Returns true if the module has
already been requested or is in the process of loading and should be
available at some point.
if (typeof require === "function" && typeof require.specified === "function" && require.specified("jquery")) {
define("jquery", [], function () {
return jQuery;
});
}
Now the problematic thing is how to get the module name because it may differ depending on the user setup. There is a special module for that, but that works only if you are already in a define call. I recommend you contact jrburke who is the Requirejs developer.