Importing npm scripts into authored jQuery-plugin - javascript

I'm trying to import a library into my authored jQuery-plugin and am getting stuck with what i believe it's babel's default importing behaviour.
Whenever i try
import * as autosize from 'autosize';
// or
import autosize from 'autosize';
// or
const autosize = require('autosize');
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
... rest of the fucking owl...
My console gives me an importing error, stating that require is not defined (since it's a browser environment) and going into the script only shows me that the importing statement has been transformed into an _interopRequireDefault call.
How can i get around this? Is it even possible to use external libraries in an jQuery plugin?
Thanks in advance!

Related

NodeJS ES6 Modules and "default" export / import

I am trying to use ES6 style modules with NodeJS v13.13 and I am running into conflicting behavior.
Originally my project was transpiled with node-babel, but then I enabled the builtin ES6 module support by adding "type": "module" to my package.json file.
At this point, import statements such as import * as esprima from 'esprima'; stopped working correctly. Upon examination, rather than the import creating the function esprima.parse, it was creating a function esprima.default.parse.
Of course this was all fixable by using something like:
import * as esprimaLoad from 'esprima';
const esprima = esprimaLoad.default;
However, what behavior is correct per the spec? Is babel right to strip out / collapse the default object, and is there a bug in the current node.js behavior which is still labeled as experimental? All of this ES Module vs CommonJS module stuff gives me a headache.
Update:
I still haven't gotten to the bottom of this, but there is more involved. esprima.js, one of the libraries where this was an issue, appears to have been created using webpack and is a UMD (universal module definition) format. It starts with the following:
(function webpackUniversalModuleDefinition(root, factory) {
/* istanbul ignore next */
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
/* istanbul ignore next */
else if(typeof exports === 'object')
exports["esprima"] = factory();
else
root["esprima"] = factory();
})(this, function() { ...
I think the webpack UMD format is already meant to work with both import statements in the browser and require statements in node.js. However, when using the experimental modules / babel in node.js it seems like the following does not work at all:
import esprima from 'esprima'
and the following works with babel but not the experimental modules:
import * as esprima from 'esprima'
and the following works with both babel and the experimental modules:
import * as esprimaLoad from 'esprima'
const esprima = esprimaLoad.default?esprimaLoad.default:esprimaLoad;
For modules that don't use this webpack UMD trickery, both the babel and the experimental modules seems to work just fine.
If you are exporting default then you don't need to import it in that roundabout fashion.
The simplest version directly imports the default:
import myDefault from '/modules/my-module.js';
It is also possible to use the default syntax with the ones seen above
(namespace imports or named imports). In such cases, the default
import will have to be declared first. For instance:
import myDefault, * as myModule from '/modules/my-module.js';
// myModule used as a namespace
You can check out more from MDN
So in your case
import esprima from 'esprima';
This should be simplest import statement.

Import UMD Javascript Modules into Browser

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" />

inject a require into a js vendor module

About the framework (while I think the problem itself does not heavily rely on that): Angular 2 with Webpack
There is the library Leaflet.heat which relies on simpleheat. I got the missing type definitions under control.
I'm importing the libraries in my vendor.ts
[...]
import 'simpleheat';
import 'leaflet.heat/src/HeatLayer';
[...]
Inside of the HeatLayer class, the function simpleheat:
[simpleheat.js]
if (typeof module !== 'undefined') module.exports = simpleheat;
function simpleheat(canvas) {
...
is called. However, the HeatLayer module file does not require simpleheat inside it's file.
Thus, creating an instance of L.HeatLayer works, but the execution of the respective code in it's function fails with
ReferenceError: simpleheat is not defined
Now, adding (for testing purposes) simpleheat = require('simpleheat'); into the HeatLayer file (a vendor), it works.
Understandably, I don't want to modify a vendor file.
Question:
What options do I have, to make the function simpleheat accessible from inside the HeatLayer module?
One Solution I just found:
Change the vendor.ts to the following:
(<any>window).simpleheat = require('simpleheat');
import 'leaflet.heat/src/HeatLayer';
Are there others/better?

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.

Categories