Skip to content

Commit 44137d2

Browse files
committed
Rebase
2 parents f769231 + d26c32d commit 44137d2

File tree

1 file changed

+308
-0
lines changed

1 file changed

+308
-0
lines changed

content/guides/author-libraries.md

Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
---
2+
title: Authoring Libraries
3+
sort: 12
4+
contributors:
5+
- pksjce
6+
- johnstew
7+
- simon04
8+
- 5angel
9+
- marioacc
10+
---
11+
12+
webpack is a tool which can be used to bundle application code and also to bundle library code. If you are the author of a JavaScript library and are looking to streamline your bundle strategy then this document will help you with the differents webpack configurations to expose your libraries as you think convenient.
13+
14+
15+
## Authoring a Library
16+
17+
Let's assume that you are writing a small library ,`webpack-numbers`, allowing to convert numbers 1 to 5 from their numeric representation to the textual one and vice-versa, e.g.: 2 to 'two'.
18+
The basic project structure may look like this.
19+
20+
__project__
21+
22+
``` diff
23+
+ |webpack.config.js
24+
+ |- package.json
25+
+ |- /src
26+
+ |- index.js
27+
+ |- ref.json
28+
```
29+
30+
Initialize npm, install webpack and lodash
31+
32+
``` bash
33+
npm init -y
34+
npm install --save-dev webpack lodash
35+
```
36+
37+
__src/ref.json__
38+
39+
```javascript
40+
[{
41+
"num": 1,
42+
"word": "One"
43+
}, {
44+
"num": 2,
45+
"word": "Two"
46+
}, {
47+
"num": 3,
48+
"word": "Three"
49+
}, {
50+
"num": 4,
51+
"word": "Four"
52+
}, {
53+
"num": 5,
54+
"word": "Five"
55+
}, {
56+
"num": 0,
57+
"word": "Zero"
58+
}]
59+
```
60+
61+
__src/index.js__
62+
63+
```javascript
64+
import _ from 'lodash';
65+
import numRef from './ref.json';
66+
67+
export function numToWord(num) {
68+
return _.reduce(numRef, (accum, ref) => {
69+
return ref.num === num ? ref.word : accum;
70+
}, '');
71+
};
72+
73+
export function wordToNum(word) {
74+
return _.reduce(numRef, (accum, ref) => {
75+
return ref.word === word && word.toLowerCase() ? ref.num : accum;
76+
}, -1);
77+
};
78+
```
79+
80+
The usage specification for the library use will be as follows:
81+
82+
```javascript
83+
import * as webpackNumbers from 'webpack-numbers';//ES2015 module import
84+
var webpackNumbers = require('webpack-numbers');// CommonJS module require
85+
...
86+
webpackNumbers.wordToNum('Two') //ES2015 and CommonJS module use
87+
...
88+
//AMD module require
89+
require(['webpackNumbers'], function ( webpackNumbers) {
90+
...
91+
webpackNumbers.wordToNum('Two')//AMD module use
92+
...
93+
});
94+
95+
```
96+
97+
The consumer also can use the library loading it with the script tag:
98+
99+
```html
100+
<html>
101+
...
102+
<script src="https://unpkg.com/webpack-numbers"></script>
103+
<script>
104+
...
105+
/* Global variable */
106+
webpackNumbers.wordToNum('Five')
107+
/* Property in the window object */
108+
window.webpackNumbers.wordToNum('Five')
109+
//
110+
...
111+
</script>
112+
</html>
113+
```
114+
115+
The configurations also can expose the library in the next ways:
116+
117+
- Property in the global object, for node.
118+
- Property in the this object.
119+
120+
121+
For full library configuration and code please refer to [webpack-library-example](https://github.com/kalcifer/webpack-library-example)
122+
123+
124+
## Configure webpack
125+
126+
Now the agenda is to bundle this library achieving the next goals:
127+
128+
- Without bundling `lodash`, but requiring it to be loaded by the consumer using `externals`.
129+
- Setting the library name as `webpack-numbers`.
130+
- Exposing the library as a variable called `webpackNumbers`.
131+
- Being able to access the library inside Node.js.s
132+
133+
Also, the consumer will be able to access` the library the `next ways:
134+
135+
- ES2015 module. i.e. `import webpackNumbers from 'webpack-numbers'`.
136+
- CommonJS module. i.e. `require('webpack-numbers')`.
137+
- Global variable when included through `script` tag.
138+
139+
140+
141+
### Add webpack
142+
143+
Add this basic webpack configuration to bundle the library.
144+
145+
__webpack.config.js__
146+
147+
```javascript
148+
var path = require('path');
149+
150+
module.exports = {
151+
entry: './src/index.js',
152+
output: {
153+
path: path.resolve(__dirname, 'dist'),
154+
filename: 'webpack-numbers.js'
155+
}
156+
};
157+
158+
```
159+
160+
161+
162+
### Add `externals`
163+
164+
Now, if you run `webpack`, you will find that a largish bundle file is created. If you inspect the file, you will find that lodash has been bundled along with your code.
165+
It would be unnecessary for your library to bundle a library like `lodash`. Hence you would want to give up control of this external library to the consumer of your library.
166+
167+
This can be done using the `externals` configuration as:
168+
169+
__webpack.config.js__
170+
171+
```javascript
172+
module.exports = {
173+
...
174+
externals: {
175+
"lodash": {
176+
commonjs: "lodash",
177+
commonjs2: "lodash",
178+
amd: "lodash",
179+
root: "_"
180+
}
181+
}
182+
...
183+
};
184+
```
185+
186+
This means that your library expects a dependency named `lodash` to be available in the consumer's environment.
187+
188+
However, if you only plan on using your library as a dependency in another webpack bundle, you may specify externals as an array.
189+
190+
```javascript
191+
module.exports = {
192+
...
193+
externals: [
194+
'react',
195+
'react-dom'
196+
]
197+
...
198+
};
199+
```
200+
201+
Please note: for bundles that use several files from a package like this:
202+
203+
```javascript
204+
import A from 'library/A';
205+
import B from 'library/B';
206+
...
207+
```
208+
209+
you wont be able to exclude them from bundle by specifying `library` in the externals.
210+
211+
You'll either need to exclude them one by one or by using a regular expression.
212+
213+
```javascript
214+
module.exports = {
215+
...
216+
externals: [
217+
'library/A',
218+
'library/B',
219+
/^library\/.+$/ // everything that starts with "library/"
220+
]
221+
...
222+
};
223+
```
224+
225+
W>At the moment of webpack 3.5.5, using the next configuration is not working properly as stated in the [issue 4824](https://github.com/webpack/webpack/issues/4824):
226+
227+
```javascript
228+
module.exports = {
229+
...
230+
output: {
231+
...
232+
233+
libraryTarget: {
234+
root:'_'
235+
}
236+
}
237+
...
238+
};
239+
```
240+
241+
W> However, you can set libraryTarget.var='_' to expect the library as a global variable
242+
243+
### Add `libraryTarget`
244+
245+
For widespread use of the library, we would like it to be compatible in different environments, i.e. CommonJS, AMD, Node.js and as a global variable.
246+
247+
To make your library available for reuse, add `library` property inside `output` in webpack configuration.
248+
249+
__webpack.config.js__
250+
251+
```javascript
252+
module.exports = {
253+
...
254+
output: {
255+
...
256+
library: 'webpackNumbers'
257+
}
258+
...
259+
};
260+
```
261+
262+
This makes your library bundle to be available as a global variable named `webpackNumbers` when imported. To make the library compatible with other environments, add `libraryTarget` property to the config. This will add the differents options about how the library can be exposed.
263+
264+
__webpack.config.js__
265+
266+
```javascript
267+
module.exports = {
268+
...
269+
output: {
270+
...
271+
library: 'webpackNumbers',
272+
libraryTarget: 'umd',
273+
}
274+
...
275+
};
276+
```
277+
278+
You can expose the library in the next ways:
279+
280+
- Variable: as a global variable. Available in the `script` tag. i.e. `libraryTarget:'var'`.
281+
- This: available trough the this object. i.e. `libraryTarget:'this'`.
282+
- Window: available trough the `window` object, in the browser. i.e. `libraryTarget:'window'`.
283+
- UMD: available after AMD or CommonJS `require`. i.e. `libraryTarget:'umd'`
284+
285+
If `library` is set and `libraryTarget` is not, `libraryTarget` defaults to `var` as specified in the [output configuration documentation](/configuration/output).
286+
See [`output.libraryTarget`](/configuration/output#output-librarytarget) there for a detailed list of all available options.
287+
288+
289+
### Final Steps
290+
291+
[Tweak your production build using webpack](/guides/production).
292+
293+
Add the path to your generated bundle as the package's main file in `package.json`
294+
295+
__package.json__
296+
297+
```javascript
298+
{
299+
"main": "dist/webpack-numbers.js",
300+
"module": "src/index.js", // To add as standard module as per https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md#typical-usage
301+
}
302+
```
303+
304+
The key `main` refers to the [standard from `package.json`](https://docs.npmjs.com/files/package.json#main), and `module` to [a](https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md) [proposal](https://github.com/rollup/rollup/wiki/pkg.module) to allow the JavaScript ecosystem upgrade to use ES2015 modules without breaking backwards compatibility.
305+
306+
W> `module` will point to a module that has ES2015 module syntax but otherwise only syntax features that browser/node supports.
307+
308+
Now you can [publish it as an npm package](https://docs.npmjs.com/getting-started/publishing-npm-packages) and find it at [unpkg.com](https://unpkg.com/#/) to distribute it to your users.

0 commit comments

Comments
 (0)