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