How to know when to import a package or paste it in index.html
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.