About
Introduces how to build a library that supports both browser and Node.js using webpack4 and ES6, and how to use the created library.
There are two ways to create a library that supports both browser and node.js.
One bundle:
The first is a method that covers both browser and Node.js with one bundle.
In this article, we'll take a closer look at how to create one bundle for both browser and Node.js.
Two bundles:
The second is to build libraries for browser and node.js separately.
Table Of Contents
Examples on Github
How to build a library that supports both browser and Node.js using webpack4 and ES6
Recipes on how to create a library using webpack
About
Introduces how to build a library that supports both browser and Node.js using webpack4 and ES6, and how to use the created library.
There are two ways to create a library that supports both browser and node.js.
-
One bundle:
The first is a method that covers both browser and Node.js with one bundle
In this article, we'll take a closer look at how to create one bundle for both browser and Node.js.
-
Two bundles:
The second is to build libraries for browser and node.js separately.
How to run examples
Step 1.Clone this repository.
Step2.Go to example directory like "part_1_1"
cd part_1_1
Step3.Run 'npm start' after npm install
to start examples.
npm install
npm start
1.One class in the library
1-1.Publish an "export default" class.
Build configuration
Build configuration is as follows
family.js is the source code of the…
1.One class in the library
1-1.Publish an "export default" class.
Build configuration
Build configuration is as follows
family.js is the source code of the library to be published
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: '',
libraryExport: '',
libraryTarget: 'umd',
globalObject: 'this',
},
See full source code of webpack.config.js
Source code of the library
family.js has a class named Tom with a single method sayHello.
We will see how to turn this into a library.
family.js
export default class Tom {
sayHello() {
return 'Hi, I am Tom.'
}
}
Source code for using this library
●Using from Browser
<script src="./mylib.min.js"></script>
<script>
const Tom = window.default;
const tom = new Tom();
console.log(tom.sayHello());
</script>
Open demo
●Using from Node.js
const Lib = require('./mylib.min.js');
const Tom = Lib.default;
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
Also works with the following code,
const Tom = require('./mylib.min.js').default;
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
●Example of using ES6's import statement
import * as Lib from './mylib.min.js';
const Tom = Lib.default;
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
Also works with the following code,
import {default as Tom} from './mylib.min.js';
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
or
import Tom from './mylib.min.js';//Pick default
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
●Tips for this recipe
Point 1:What does globalObject: 'this' mean?
The webpacked bundle mylib.min.js is as follows.
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else {
var a = factory();
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
}
})(this, function() {...});
See full source code of bundle(develop build)
This is an immediate function with (this, function ())
as its argument.
This this
is caused by setting globalObject: 'this'.
If you do not specify globalObject:,
the argument of this immediate function will be (window, function ())
.
It works on browsers that have a window
object,
but cannot be run on node.js that does not have a window object.
As you might expect, if you try the above, you will get ReferenceError: window is not defined
.
So if you want to support both browser and node.js,
don't forget to include globalObject: 'this'.
Point 2:Classes you want to publish are stored with the key "default"
If you want to access the classes published in the library,
Use require('./mylib.min.js').default
on node.js and use window.default
(=window["default"]) on the browser.
Remember that in this configuration, the class is identified by the key default.
1-2.Publish an "export default" class with library name (like namespace).
The library name (namespace) can be set by specifying output.library: 'MyLibrary' in webpack.config.js.
Build Configuration
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: 'MyLibrary',
libraryExport: '',
libraryTarget: 'umd',
globalObject: 'this',
},
See full source code of webpack.config.js
Source code of the library
family.js
export default class Tom {
sayHello() {
return 'Hi, I am Tom.'
}
}
Source code for using this library
●Using from Browser
See below.
Tom class can be used as MyLibrary.default.
<script src="./mylib.min.js"></script>
<script>
const Tom = MyLibrary.default;
const tom = new Tom();
console.log(tom.sayHello());
</script>
Open demo
●Using from Node.js
Note that in case of node.js (CommonJS2), library name is ignored .
So output.library: 'MyLibrary' does not work for node.js.
const Lib = require('./mylib.min.js');
const Tom = Lib.default;
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
●Example of using ES6's import statement
import * as Lib from './mylib.min.js';
const Tom = Lib.default;
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
1-3.Publish an "export default" class with library name without using the default property.
You want to access a class without using "default" which looks redundant like below.
const Tom = MyLibrary.default;
Build Configuration
Try to set output.libraryExport: 'default'
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: 'MyLibrary',
libraryExport: 'default',
libraryTarget: 'umd',
globalObject: 'this',
},
See full source code of webpack.config.js
Source code of the library
family.js
export default class Tom {
sayHello() {
return 'Hi, I am Tom.'
}
}
Source code for using this library
●Using from Browser
Let's build the library with this configuration.
Then, instead of MyLibrary.default
,
MyLibrary
itself equals to a reference of Tom
class.
<script src="./mylib.min.js"></script>
<script>
const Tom = MyLibrary;
const tom = new Tom();
console.log(tom.sayHello());
</script>
Open demo
●Using from Node.js
As mentioned above, in case of node.js (CommonJS2), library name is ignored .
const Tom = require('./mylib.min.js');
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
●Example of using ES6's import statement
import Tom from './mylib.min.js';
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
1-4.Publish an "export default" class with the setting "library name = class name".
Build Configuration
- Set
output.libraryExport: 'default'
- Make library name the same as class name like
output.library: 'Tom'
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: 'Tom',
libraryExport: 'default',
libraryTarget: 'umd',
globalObject: 'this',
},
See full source code of webpack.config.js
Source code of the library
family.js
export default class Tom {
sayHello() {
return 'Hi, I am Tom.'
}
}
Source code for using this library
●Using from Browser
<script src="./mylib.min.js"></script>
<script>
const tom = new Tom();
console.log(tom.sayHello());
</script>
Open demo
●Using from Node.js
const Tom = require('./mylib.min.js');
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
●Example of using ES6's import statement
import Tom from './mylib.min.js';
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
●Tips for this recipe
It can be accessed from the browser and node.js with the symbol Tom.
This configuration is one of my favorites.
1-5.Publish an "export default" class in a way called "reexport".
Publish the library using re-export.
Re-export means exporting one module from another.
Build Configuration
Change entry to index.js to re-export from index.js.
webpack.config.js
entry: {
'mylib': [`./src/index.js`],
},
output: {
filename: `[name].min.js`,
library: '',
libraryExport: '',
libraryTarget: 'umd',
globalObject: 'this',
},
See full source code of webpack.config.js
Source code of the library
Now, let's create index.js and re-export the Tom class in family.js from there.
export {default as Tom} from './family.js';
Tom is "export"ed by {default as Tom}
when reexporting by index.js. So, strictly speaking, this method is no longer "default export".
family.js
export default class Tom {
sayHello() {
return 'Hi, I am Tom.'
}
}
Source code for using this library
●Using from Browser
<script src="./mylib.min.js"></script>
<script>
const tom = new Tom();
console.log(tom.sayHello());
</script>
Open demo
●Using from Node.js
We use destructuring assignment to get the Tom class.
const {Tom} = require('./mylib.min.js');
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
●Example of using ES6's import statement
import {Tom} from './mylib.min.js';
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
2.Multiple classes in the library
2-1.Publish multiple classes
Let's look at some examples of publishing multiple classes.
(You can publish not just classes but functions or variables in the same way.)
Source code of the library
As you can see, the following family.js contains two classes Tom and Jack.
family.js
export class Tom {
sayHello() {
return 'Hi, I am Tom.'
}
}
export class Jack {
sayHello() {
return 'Hi, I am Jack.'
}
}
Build Configuration
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: '',
libraryExport: '',
libraryTarget: 'umd',
globalObject: 'this',
},
See full source code of webpack.config.js
Source code for using this library
●Using from Browser
<script src="./mylib.min.js"></script>
<script>
const tom = new Tom();//means window["Tom"]
const jack = new Jack();//means window["Jack"]
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
</script>
Open demo
●Using from Node.js
const {Tom, Jack} = require('./mylib.min.js');
const tom = new Tom();
const jack = new Jack();
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
Also works with the following code,
const Lib = require('./mylib.min.js');
const Tom = Lib.Tom;
const Jack = Lib.Jack;
const tom = new Tom();
const jack = new Jack();
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
●Example of using ES6's import statement
import * as Lib from './mylib.min.js';
const Tom = Lib.Tom;
const Jack = Lib.Jack;
const tom = new Tom();
const jack = new Jack();
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
OR
import {Tom, Jack} from './mylib.min.js';
const tom = new Tom();
const jack = new Jack();
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
2-2.Publish multiple classes with library name
By specifying library:'GreatFamily'
,
you can add a library name (like namespace) like as follows.
Build Configuration
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: 'GreatFamily',
libraryExport: '',
libraryTarget: 'umd',
globalObject: 'this',
},
See full source code of webpack.config.js
Source code of the library
family.js
export class Tom {
sayHello() {
return 'Hi, I am Tom.'
}
}
export class Jack {
sayHello() {
return 'Hi, I am Jack.'
}
}
Source code for using this library
●Using from Browser
When running on a browser, each class(Tom and Jack) is stored in window ["GreatFamily"].
<script src="./mylib.min.js"></script>
<script>
const tom = new GreatFamily.Tom();
const jack = new GreatFamily.Jack();
console.log(tom.sayHello());
console.log(jack.sayHello());
</script>
Open demo
●Using from Node.js
Note that in case of node.js (CommonJS2), library name is ignored .
So output.library: 'GreatFamily' does not work for node.js.
const Lib = require('./mylib.min.js');
const Tom = Lib.Tom;
const Jack = Lib.Jack;
const tom = new Tom();
const jack = new Jack();
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
●Example of using ES6's import statement
import * as Lib from './mylib.min.js';
const Tom = Lib.Tom;
const Jack = Lib.Jack;
const tom = new Tom();
const jack = new Jack();
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
2-3.Publish multiple classes including "export default" class
Source code of the library
family.js
export default class Tom {
sayHello() {
return 'Hi, I am Tom.'
}
}
export class Jack {
sayHello() {
return 'Hi, I am Jack.'
}
}
Build Configuration
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: '',
libraryExport: '',
libraryTarget: 'umd',
globalObject: 'this',
},
See full source code of webpack.config.js
Source code for using this library
●Using from Browser
<script src="./mylib.min.js"></script>
<script>
const Tom = window.default;//means window["default"]
const tom = new Tom();
const jack = new Jack();//means window["Jack"]
console.log(tom.sayHello());
console.log(jack.sayHello());
</script>
Open demo
●Using from Node.js
const Lib = require('./mylib.min.js');
const Tom = Lib.default;
const Jack = Lib.Jack;
const tom = new Tom();
const jack = new Jack();
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
Also works with the following code,
const Tom = require('./mylib.min.js').default;
const {Jack} = require('./mylib.min.js');
const tom = new Tom();
const jack = new Jack();
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
●Example of using ES6's import statement
import * as Lib from './mylib.min.js';
const Tom=Lib.default;
const Jack=Lib.Jack;
const tom = new Tom();
const jack = new Jack();
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
or
import {default as Tom, Jack} from './mylib.min.js';
const tom = new Tom();
const jack = new Jack();
console.log(tom.sayHello());//-> Hi, I am Tom.
console.log(jack.sayHello());//-> Hi, I am Jack.
or
import Tom2 from './mylib.min.js';
import {Jack as Jack2} from './mylib.min.js';
const tom2 = new Tom2();
const jack2 = new Jack2();
console.log(tom2.sayHello());//-> Hi, I am Tom.
console.log(jack2.sayHello());//-> Hi, I am Jack.
2-4.Publish only "export default" class from multiple classes.
Here's a rare pattern, but let's take a look to get a better understanding of what happens when building.
Build Configuration
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: 'Tom',
libraryExport: 'default',
libraryTarget: 'umd',
globalObject: 'this'
},
See full source code of webpack.config.js
Source code of the library
family.js
export default class Tom {
sayHello() {
return 'Hi, I am Tom.'
}
}
export class Jack {
sayHello() {
return 'Hi, I am Jack.'
}
}
Source code for using this library
●Using from Browser
Jack class becomes inaccessible from outside code.
<script src="./mylib.min.js"></script>
<script>
const tom = new Tom();
console.log(tom.sayHello());
</script>
Open demo
●Using from Node.js
const Tom = require('./mylib.min.js');
const tom=new Tom();
console.log(tom.sayHello());//->Hi, I am Tom.
●Example of using ES6's import statement
import Tom from './mylib.min.js';
const tom=new Tom();
console.log(tom.sayHello());//->Hi, I am Tom.
●Tips for this recipe
The Jack class is included as code in the bundle even though it is not accessible from outside.
This is purely wasteful, so if your Jack class isn't used by anyone, don't put it in your source code.
3. Other options
3-1.Set a separate namespace for each module type.
When libraryTarget: 'umd'
is specified
Root, AMD, and CommonJS can have different library names (namespaces).
However, you cannot specify a library name for CommonJS2 (for node.js) as before, it will be ignored.
Build Configuration
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: {
root: 'GreatFamily',
amd: 'great-family',
commonjs: 'common-great-family',
},
libraryExport: '',
libraryTarget: 'umd',
globalObject: 'this',
umdNamedDefine: true,
}
See full source code of webpack.config.js
library: {
root: 'GreatFamily',
amd: 'great-family',
commonjs: 'common-great-family',
},
In the above part,
the library name is given for each module type.
Be careful if you want to use the AMD module type.
Specify umdNamedDefine: trueP
if you want to add library name to AMD.
Let's see the result of building with this setting.
The bundle is shown below.
As you can see, each module type has a specified library name.
family.min.js
(function webpackUniversalModuleDefinition(root, factory) {
//for CommonJS2 environment
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
//for AMD environment
else if(typeof define === 'function' && define.amd)
define("great-family", [], factory);
//for CommonJS environment
else if(typeof exports === 'object')
exports["common-great-family"] = factory();
//for Root
else
root["GreatFamily"] = factory();
})(this, function() {...}
See full source code of bundle(develop build)
3-2.Set a separate comment for each module type.
By writing auxiliaryComment, you can add comments to the source code of each module type definition of the generated code of bundle.
Build Configuration
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: {
root: 'GreatFamily',
amd: 'great-family',
commonjs: 'common-great-family',
},
libraryExport: '',
libraryTarget: 'umd',
globalObject: 'this',
umdNamedDefine: true,
auxiliaryComment: {
root: 'Comment for Root',
commonjs: 'Comment for CommonJS',
commonjs2: 'Comment for CommonJS2',
amd: 'Comment for AMD'
}
}
See full source code of webpack.config.js
As you can see below,
you can see the comments in the bundle.
family.min.js
(function webpackUniversalModuleDefinition(root, factory) {
//Comment for CommonJS2
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
//Comment for AMD
else if(typeof define === 'function' && define.amd)
define("great-family", [], factory);
//Comment for CommonJS
else if(typeof exports === 'object')
exports["common-great-family"] = factory();
//Comment for Root
else
root["GreatFamily"] = factory();
})(this, function() {...}
See full source code of bundle(develop build)
3-3.Make library name looks like namespace separated by periods like "org.riversun.GereatFamily".
If you want to name the library "org.riversun.GreatFamily",
for example, specify an array like library: ['org', 'riversun', 'GreatFamily']
Build Configuration
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: ['org', 'riversun', 'GreatFamily'],
libraryExport: '',
libraryTarget: 'umd',
globalObject: 'this',
umdNamedDefine: true,
},
See full source code of webpack.config.js
Source code of the library
family.js
export class Tom {
sayHello() {
return 'Hi, I am Tom.'
}
}
export class Jack {
sayHello() {
return 'Hi, I am Jack.'
}
}
Source code for using this library
●Using from Browser
<script src="./mylib.min.js"></script>
<script>
const tom = new org.riversun.GreatFamily.Tom();
const jack = new org.riversun.GreatFamily.Jack();
console.log(tom.sayHello());
console.log(jack.sayHello());
</script>
Open demo
As mentioned above, in case of node.js (CommonJS2), library name is ignored .
3-4.Separate external libraries(dependencies) using "externals"
If your library uses an external library (if it has dependencies), there are two types of build methods.
- One method is to bundle an external library into your own library.
- The other method is to externalize an external library.
This section describes "externalization" .
Here is an example of code where the Tom class depends on external library @riversun/simple-date-format.
**Install an external library
Install an external library to be used as development dependencies.
npm install --save-dev @riversun/simple-date-format
Build Configuration
Add externals into webpack.config.js as below.
webpack.config.js
entry: {
'mylib': [`./src/family.js`],
},
output: {
filename: `[name].min.js`,
library: 'Tom',
libraryExport: 'default',
libraryTarget: 'umd',
globalObject: 'this',
},
externals: {
SDF: {
commonjs: '@riversun/simple-date-format',
commonjs2: '@riversun/simple-date-format',
amd: '@riversun/simple-date-format',
root: 'SimpleDateFormat'
}
}
See full source code of webpack.config.js
In the following part, the part specified as "SDF" means the property name for referring to the external library from the source code.
externals: {
SDF: {
commonjs: '@riversun/simple-date-format',
commonjs2: '@riversun/simple-date-format',
amd: '@riversun/simple-date-format',
root: 'SimpleDateFormat'
}
}
Write "Library type name:Library name" (same as npm install
) as shown below in the child element of SDF.
commonjs: '@riversun/simple-date-format',
commonjs2: '@riversun/simple-date-format',
amd: '@riversun/simple-date-format',
Library name can be set for each module type like commonjs, commonjs2, amd.
SDF in the example above acts as an alias. What the SDF actually points to is an external library specified for each module type.
Look at this at the bottom,
When using your own library on the browser,
SDF is built to reference SimpleDateFormat(=window.["SimpleDateFormat"]).
● Generated bundle
When building, the following bundle is generated,
(function webpackUniversalModuleDefinition(root, factory) {
//for CommonJS2
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("@riversun/simple-date-format"));
//for AMD
else if(typeof define === 'function' && define.amd)
define("Tom", ["@riversun/simple-date-format"], factory);
//for CommonJS
else if(typeof exports === 'object')
exports["Tom"] = factory(require("@riversun/simple-date-format"));
//for Root
else
root["Tom"] = factory(root["SimpleDateFormat"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE_SDF__) {...})
See full source code of bundle(develop build)
You can see that the code that loads the external library is generated for each module type.
This way you can avoid bundling external libraries.
Let's look at the source code of "my" library.
Source code of the library
family.js
import SimpleDateFormat from "SDF";
export default class Tom {
sayHello() {
const date = new Date();
const sdf = new SimpleDateFormat();
return `Hi, I am Tom. Today is ${sdf.formatWith("EEE, MMM d, ''yy", date)}`;
}
}
You can see that SDF in import SimpleDateFormat from" SDF ";
is an alias of the original import SimpleDateFormat from"@riversun/simple-date-format";
.
Next, let's look at using my library created by separating the external library.
Source code for using this library
●Using from Browser
- When using from a browser, read the external library from the CDN as follows
- Note that dependent libraries (external libraries) are loaded "before" my library.
<script src="https://cdn.jsdelivr.net/npm/@riversun/simple-date-format@1.1.0/dist/simple-date-format.js"></script>
<script src="./mylib.min.js"></script>
<script>
const tom = new Tom();
document.write(tom.sayHello());
</script>
Open demo
By the way,
the external library used this time was also built by the method of 1-4 in this article.
●Using from Node.js
const Tom = require('./mylib.min.js');
const tom = new Tom();
console.log(tom.sayHello());//-> Hi, I am Tom.
How to run examples
Step 1.Clone this repository.
git clone https://github.com/riversun/making-library-with-webpack
Step2.Go to example directory like "part_1_1"
Step3.Run 'npm start' after npm install
to start examples.
Thank you.
Written by Tom Misawa (https://github.com/riversun) on 2020-02-28