1 | # HappyPack [![Build Status](https://travis-ci.org/amireh/happypack.svg?branch=master)](https://travis-ci.org/amireh/happypack) [![codecov.io](https://codecov.io/github/amireh/happypack/coverage.svg?branch=master)](https://codecov.io/github/amireh/happypack?branch=master)
|
2 |
|
3 | HappyPack makes initial webpack builds faster by transforming files [in
|
4 | parallel](#how-it-works).
|
5 |
|
6 | > **Maintenance mode notice**
|
7 | >
|
8 | > My interest in the project is fading away mainly because I'm not using
|
9 | > JavaScript as much as I was in the past. Additionally, Webpack's native
|
10 | > performance is improving and (I hope) it will soon make this plugin
|
11 | > unnecessary.
|
12 | >
|
13 | > See the FAQ entry about Webpack 4 and [thread-loader][thread-loader].
|
14 | >
|
15 | > Contributions are always welcome. Changes I make from this point will be
|
16 | > restricted to bug-fixing. If someone wants to take over, feel free to get
|
17 | > in touch.
|
18 | >
|
19 | > Thanks to everyone who used the library, contributed to it and helped in
|
20 | > refining it!!!
|
21 |
|
22 | ## Usage
|
23 |
|
24 | ```shell
|
25 | npm install --save-dev happypack
|
26 | ```
|
27 |
|
28 | HappyPack provides both a plugin and a loader in order to do its job so you
|
29 | must use both to enable it.
|
30 |
|
31 | Normally, you define loader rules to tell webpack how to process certain files.
|
32 | With HappyPack, you switch things around so that you pass the loaders to
|
33 | HappyPack's plugin and instead tell webpack to use `happypack/loader`.
|
34 |
|
35 | Below is a sample configuration that shows those steps in action.
|
36 |
|
37 | ```javascript
|
38 | // @file: webpack.config.js
|
39 | const HappyPack = require('happypack');
|
40 |
|
41 | exports.module = {
|
42 | rules: [
|
43 | {
|
44 | test: /.js$/,
|
45 | // 1) replace your original list of loaders with "happypack/loader":
|
46 | // loaders: [ 'babel-loader?presets[]=es2015' ],
|
47 | use: 'happypack/loader',
|
48 | include: [ /* ... */ ],
|
49 | exclude: [ /* ... */ ]
|
50 | }
|
51 | ]
|
52 | };
|
53 |
|
54 | exports.plugins = [
|
55 | // 2) create the plugin:
|
56 | new HappyPack({
|
57 | // 3) re-add the loaders you replaced above in #1:
|
58 | loaders: [ 'babel-loader?presets[]=es2015' ]
|
59 | })
|
60 | ];
|
61 | ```
|
62 |
|
63 | That's it. Now sources that match `.js$` will be handed off to HappyPack which
|
64 | will transform them in parallel using the loaders you specified (`babel-loader`
|
65 | in this example.)
|
66 |
|
67 | ## Configuration
|
68 |
|
69 | These are the parameters you can pass to the plugin when you instantiate it.
|
70 | `loaders` is the only required parameter.
|
71 |
|
72 | ### `loaders: Array`
|
73 |
|
74 | Each entry consists of the name (or absolute path) of the loader that
|
75 | would transform the files and an optional query string to pass to it. This
|
76 | looks similar to what you'd pass to webpack's `loader` config.
|
77 |
|
78 | > **Heads up!**
|
79 | >
|
80 | > HappyPack doesn't work with *all* webpack loaders as some loader API are not
|
81 | > supported.
|
82 | >
|
83 | > See [this wiki page](https://github.com/amireh/happypack/wiki/Webpack-Loader-API-Support) for more details on current Loader API support.
|
84 |
|
85 | The following notations are officially supported and are all equivalent:
|
86 |
|
87 | ```javascript
|
88 | {
|
89 | loaders: [
|
90 | // a string with embedded query for options
|
91 | 'babel-loader?presets[]=es2015',
|
92 |
|
93 | {
|
94 | loader: 'babel-loader'
|
95 | },
|
96 |
|
97 | // "query" string
|
98 | {
|
99 | loader: 'babel-loader',
|
100 | query: '?presets[]=es2015'
|
101 | },
|
102 |
|
103 | // "query" object
|
104 | {
|
105 | loader: 'babel-loader',
|
106 | query: {
|
107 | presets: [ 'es2015' ]
|
108 | }
|
109 | },
|
110 |
|
111 | // Webpack 2+ "options" object instead of "query"
|
112 | {
|
113 | loader: 'babel-loader',
|
114 | options: {
|
115 | presets: [ 'es2015' ]
|
116 | }
|
117 | }
|
118 | ]
|
119 | }
|
120 | ```
|
121 |
|
122 | ### `id: String`
|
123 |
|
124 | A unique id for this happy plugin. This is used by the loader to know which
|
125 | plugin it's supposed to talk to.
|
126 |
|
127 | Normally, you would not need to specify this unless you have more than one
|
128 | HappyPack plugin defined, in which case you'll need distinct IDs to tell them
|
129 | apart. See [this section](#using-multiple-instances) for more information.
|
130 |
|
131 | Defaults to: `"1"`
|
132 |
|
133 | ### `threads: Number`
|
134 |
|
135 | This number indicates how many Node VMs HappyPack will spawn for compiling
|
136 | the source files. After a lot of tinkering, I found 4 to yield the best
|
137 | results. There's certainly a diminishing return on this value and increasing
|
138 | beyond 8 actually slowed things down for me.
|
139 |
|
140 | Keep in mind that this is only relevant when performing **the initial build**
|
141 | as HappyPack will switch into a synchronous mode afterwards (i.e. in `watch`
|
142 | mode.)
|
143 |
|
144 | Defaults to: `3`
|
145 |
|
146 | ### `threadPool: HappyThreadPool`
|
147 |
|
148 | A pre-defined thread-pool to use for retrieving worker threads. Normally, this
|
149 | is managed internally by each `HappyPlugin` instance, but you may override
|
150 | this behavior for better results.
|
151 |
|
152 | [The section on thread pools](#shared-thread-pools) explains how and when to
|
153 | use this.
|
154 |
|
155 | Defaults to: `null`
|
156 |
|
157 | ### `verbose: Boolean`
|
158 |
|
159 | Enable this to log status messages from HappyPack to STDOUT like start-up
|
160 | banner, etc..
|
161 |
|
162 | Defaults to: `true`
|
163 |
|
164 | ### `verboseWhenProfiling: Boolean`
|
165 |
|
166 | Enable this if you want HappyPack to still produce its output even when you're
|
167 | doing a `webpack --profile` run. Since this variable was introduced, HappyPack
|
168 | will be silent when doing a profile build in order not to corrupt any JSON
|
169 | output by webpack (i.e. when using `--json` as well.)
|
170 |
|
171 | Defaults to: `false`
|
172 |
|
173 | ### `debug: Boolean`
|
174 |
|
175 | Enable this to log diagnostic messages from HappyPack to STDOUT. Useful for
|
176 | troubleshooting.
|
177 |
|
178 | Defaults to: `false`
|
179 |
|
180 | ## How it works
|
181 |
|
182 | ![A diagram showing the flow between HappyPack's components](doc/HappyPack_Workflow.png)
|
183 |
|
184 | HappyPack sits between webpack and your primary source files (like JS sources)
|
185 | where the bulk of loader transformations happen. Every time webpack resolves a
|
186 | module, HappyPack will take it and all its dependencies and distributes those
|
187 | files to multiple worker "threads".
|
188 |
|
189 | Those threads are actually simple node processes that invoke your transformer.
|
190 | When the compiled version is retrieved, HappyPack serves it to its loader and
|
191 | eventually your chunk.
|
192 |
|
193 | ## Using multiple instances
|
194 |
|
195 | It's possible to define multiple HappyPack plugins for different types of
|
196 | sources/transformations. Just pass in a unique id for each plugin and make
|
197 | sure you pass it their loaders. For example:
|
198 |
|
199 | ```javascript
|
200 | // @file webpack.config.js
|
201 | exports.plugins = [
|
202 | new HappyPack({
|
203 | id: 'jsx',
|
204 | threads: 4,
|
205 | loaders: [ 'babel-loader' ]
|
206 | }),
|
207 |
|
208 | new HappyPack({
|
209 | id: 'styles',
|
210 | threads: 2,
|
211 | loaders: [ 'style-loader', 'css-loader', 'less-loader' ]
|
212 | })
|
213 | ];
|
214 |
|
215 | exports.module.rules = [
|
216 | {
|
217 | test: /\.js$/,
|
218 | use: 'happypack/loader?id=jsx'
|
219 | },
|
220 |
|
221 | {
|
222 | test: /\.less$/,
|
223 | use: 'happypack/loader?id=styles'
|
224 | },
|
225 | ]
|
226 | ```
|
227 |
|
228 | Now `.js` files will be handled by the first Happy plugin which will use
|
229 | `babel-loader` to transform them, while `.less` files will be handled
|
230 | by the second one using the style loaders.
|
231 |
|
232 | ## Shared thread pools
|
233 |
|
234 | Normally, each HappyPack plugin you create internally creates its own threads
|
235 | which are used to run the loaders. However, if you're using more than one
|
236 | HappyPack plugin it can be more optimal to create a thread pool yourself and
|
237 | then configure the plugins to share that pool, minimizing the idle time of
|
238 | threads within it.
|
239 |
|
240 | Here's an example of using a custom pool of 5 threads that will be shared
|
241 | between loaders for both JS and SCSS/LESS/whatever sources:
|
242 |
|
243 | ```javascript
|
244 | // @file: webpack.config.js
|
245 | var HappyPack = require('happypack');
|
246 | var happyThreadPool = HappyPack.ThreadPool({ size: 5 });
|
247 |
|
248 | module.exports = {
|
249 | // ...
|
250 | plugins: [
|
251 | new HappyPack({
|
252 | id: 'js',
|
253 | threadPool: happyThreadPool,
|
254 | loaders: [ 'babel-loader' ]
|
255 | }),
|
256 |
|
257 | new HappyPack({
|
258 | id: 'styles',
|
259 | threadPool: happyThreadPool,
|
260 | loaders: [ 'style-loader', 'css-loader', 'less-loader' ]
|
261 | })
|
262 | ]
|
263 | };
|
264 | ```
|
265 |
|
266 | ## Benchmarks
|
267 |
|
268 | For the main repository I tested on, which had around 3067 modules, the build time went down from 39 seconds to a whopping ~10 seconds.
|
269 |
|
270 | Here's a rundown of the various states the build was performed in:
|
271 |
|
272 | Elapsed (ms) | Happy? | Using DLLs? |
|
273 | ------------ | ------- | ----------- |
|
274 | 39851 | NO | NO |
|
275 | 37393 | NO | YES |
|
276 | 14605 | YES | NO |
|
277 | 13925 | YES | NO |
|
278 | 11877 | YES | NO |
|
279 | 9228 | YES | YES |
|
280 |
|
281 | The builds above were run under Linux on a machine with 12 cores.
|
282 |
|
283 | ## Changes
|
284 |
|
285 | See [./CHANGELOG.md](./CHANGELOG.md).
|
286 |
|
287 | ## FAQ
|
288 |
|
289 | ### Does it work with Webpack 2 & 3?
|
290 |
|
291 | Yes. You should use version >= 4.0.1 (of HappyPack).
|
292 |
|
293 | ### Is it necessary for Webpack 4?
|
294 |
|
295 | Short answer: _maybe_ not.
|
296 |
|
297 | Long answer: there's now a competing add-on in the form of a _loader_ for
|
298 | processing files in multiple threads, exactly what HappyPack does. The fact
|
299 | that it's a loader and not a plugin (or both, in case of H.P.) makes it much
|
300 | simpler to configure. Look at [thread-loader][thread-loader] and if it works
|
301 | for you - that's great, otherwise you can try HappyPack and see which fares
|
302 | better for you.
|
303 |
|
304 | YMMV.
|
305 |
|
306 | ### Does it work with TypeScript?
|
307 |
|
308 | The short answer is: yes, it finally does! The longer answer is that you need
|
309 | to use [ts-loader](https://github.com/TypeStrong/ts-loader) in
|
310 | "transpiling-only" mode then use the special plugin [fork-ts-checker-notifier-webpack-plugin](https://github.com/johnnyreilly/fork-ts-checker-notifier-webpack-plugin) to perform static type checking.
|
311 |
|
312 | More information can be found in the [ts-loader "happypack mode" section](https://github.com/TypeStrong/ts-loader#happypackmode-boolean-defaultfalse) and you can refer to the [example](./examples/ts-loader) that shows this in action.
|
313 |
|
314 | Big thanks to @johnnyreilly, @aindlq, @piotr-oles, @abergs and many others for
|
315 | making this work.
|
316 |
|
317 | ### Does it work with loader XYZ?
|
318 |
|
319 | We're keeping track of loader support in [this wiki page](https://github.com/amireh/happypack/wiki/Loader-Compatibility-List). Some loaders may require
|
320 | extra configuration to make them work.
|
321 |
|
322 | If the loader you're trying to use isn't listed there, you can refer to [this](https://github.com/amireh/happypack/wiki/Webpack-Loader-API-Support) wiki page
|
323 | to see which loader APIs are supported. If your loader uses any API that is NOT
|
324 | supported, chances are that it will not work with HappyPack.
|
325 |
|
326 | As a general rule, any loader that accepts "functions" in options will not work
|
327 | unless it also accepts reading those options from a file, like babel-loader
|
328 | does with `.babelrc` and postcss-loader too.
|
329 |
|
330 | ### Does it work under Windows?
|
331 |
|
332 | Yes, as of version 4.0.0 it should. If you come across issues using the plugin
|
333 | on Windows, feel free to open a ticket.
|
334 |
|
335 | ## Development
|
336 |
|
337 | See [HACKING.md](./HACKING.md).
|
338 |
|
339 | ## License (MIT)
|
340 |
|
341 | Copyright (c) <2015-2017> <ahmad@amireh.net>
|
342 |
|
343 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
344 |
|
345 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
346 |
|
347 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
348 |
|
349 | [thread-loader]: https://github.com/webpack-contrib/thread-loader
|