Say there is a module on npm called "awesomepackage". I can register it as a dependency of my application via package.json
like so:
npm i --save awesomepackage
Inspecting my node_modules
folder I see a folder named "awesomepackage" which looks like this:
- index.js
- package.json
- README.md
- lib/
- awesomepackage.min.js
and I can use the package inside my own application like so:
import {AwesomeThing} from 'awesomepackage';
When I build my application using webpack my final bundle.js
always contains the source code from node_modules/awesomepackage/index.js
- regardless of any environment variables I have setup (eg NODE_ENV=production
).
Is there any way to tell webpack to use the pre-built files (eg node_modules/awesomepackage/lib/awesomepackage.min.js
) if they are shipped as part of an NPM package, instead of re-building from the source?
I have seen solutions that use resolve.alias
and noParse
in the webpack config with hardcoded paths to minified source files, but doesn't that mostly defeat the purpose of using npm and webpack? I don't want to have to hard-code and maintain paths to every dependency's minified build files!
I got here because my bundle.js
jumped 2.5mb when I added moment.js
- which is 167kb minified, including all locales, but of which the source code includes 2mb of tests.
Clearly there is more to it than just minifying the final bundle.js
file at the end. For example: why would anybody want to include hundreds of unnecessary unit tests for dependencies in final production code? They don't. Which is why most libraries provide a dist
(or similar) folder as part of their npm module.
Webpack v4+ will minify your code by default in production mode . Note that while the TerserPlugin is a great place to start for minification and being used by default, there are other options out there: ClosureWebpackPlugin.
#1 Putting all dependencies under "devDependencies" This approach considers that since your production app (aka the bundle you built with Webpack) can just run by itself, it means you have no production dependencies. Thus, all dependencies are devDependencies .
js ecosystem. Over the past eight years, webpack has become increasingly powerful. However, due to the additional packaging process, the building speed is getting slower as the project grows. As a result, each startup takes dozens of seconds (or minutes), followed by a round of build optimization.
Let's consider your moment package.
You can always use
import moment from 'node_modules/moment/min/moment-with-locales.min.js'
But it's not a good idea.
If you use
import moment from 'moment';
it will build module from source. But if you have minification process in webpack config file it shouldn't be much bigger than shipped .min.js
files. Often your own build can be even smaller than original .min.js
!
I measure additional size of my bundle.js
after adding moment
and its 190 kB and original moment/min/moment-with-locales.min.js
size is 207 kB. Your 2.5 MB is probably that you add maps (in my configuration with maps moment
weights 2.7 MB) You can analyze that it is build with all locales - webpack can print this statistics (sizes are before minification):
[373] ./~/moment/moment.js 135 kB {0} [built]
[374] ./~/moment/locale/af.js 2.62 kB {0} [optional] [built]
[375] ./~/moment/locale/ar-ma.js 2.12 kB {0} [optional] [built]
[376] ./~/moment/locale/ar-sa.js 3.16 kB {0} [optional] [built]
[377] ./~/moment/locale/ar-tn.js 1.97 kB {0} [optional] [built]
[378] ./~/moment/locale/ar.js 4.59 kB {0} [optional] [built]
[379] ./~/moment/locale/az.js 3.35 kB {0} [optional] [built]
[380] ./~/moment/locale/be.js 4.86 kB {0} [optional] [built]
[381] ./~/moment/locale/bg.js 3.1 kB {0} [optional] [built]
[382] ./~/moment/locale/bn.js 3.62 kB {0} [optional] [built]
[383] ./~/moment/locale/bo.js 3.87 kB {0} [optional] [built]
[384] ./~/moment/locale/br.js 3.44 kB {0} [optional] [built]
[385] ./~/moment/locale/bs.js 4.7 kB {0} [optional] [built]
[386] ./~/moment/locale/ca.js 2.97 kB {0} [optional] [built]
[387] ./~/moment/locale/cs.js 6.4 kB {0} [optional] [built]
[388] ./~/moment/locale/cv.js 2.33 kB {0} [optional] [built]
[389] ./~/moment/locale/cy.js 2.92 kB {0} [optional] [built]
[390] ./~/moment/locale/da.js 2.07 kB {0} [optional] [built]
[391] ./~/moment/locale/de-at.js 3.03 kB {0} [optional] [built]
[392] ./~/moment/locale/de.js 2.95 kB {0} [optional] [built]
[393] ./~/moment/locale/dv.js 2.69 kB {0} [optional] [built]
[394] ./~/moment/locale/el.js 3.73 kB {0} [optional] [built]
[395] ./~/moment/locale/en-au.js 2.32 kB {0} [optional] [built]
[396] ./~/moment/locale/en-ca.js 2.2 kB {0} [optional] [built]
[397] ./~/moment/locale/en-gb.js 2.38 kB {0} [optional] [built]
[398] ./~/moment/locale/en-ie.js 2.37 kB {0} [optional] [built]
[399] ./~/moment/locale/en-nz.js 2.32 kB {0} [optional] [built]
[400] ./~/moment/locale/eo.js 2.67 kB {0} [optional] [built]
[401] ./~/moment/locale/es.js 2.97 kB {0} [optional] [built]
[402] ./~/moment/locale/et.js 3.14 kB {0} [optional] [built]
[403] ./~/moment/locale/eu.js 2.39 kB {0} [optional] [built]
[404] ./~/moment/locale/fa.js 3.27 kB {0} [optional] [built]
[405] ./~/moment/locale/fi.js 3.86 kB {0} [optional] [built]
[406] ./~/moment/locale/fo.js 2.1 kB {0} [optional] [built]
[407] ./~/moment/locale/fr-ca.js 2.09 kB {0} [optional] [built]
[408] ./~/moment/locale/fr-ch.js 2.25 kB {0} [optional] [built]
[409] ./~/moment/locale/fr.js 2.24 kB {0} [optional] [built]
[410] ./~/moment/locale/fy.js 2.64 kB {0} [optional] [built]
[411] ./~/moment/locale/gd.js 2.63 kB {0} [optional] [built]
[412] ./~/moment/locale/gl.js 2.77 kB {0} [optional] [built]
[413] ./~/moment/locale/he.js 3.41 kB {0} [optional] [built]
[414] ./~/moment/locale/hi.js 3.88 kB {0} [optional] [built]
[415] ./~/moment/locale/hr.js 4.83 kB {0} [optional] [built]
[416] ./~/moment/locale/hu.js 4 kB {0} [optional] [built]
[417] ./~/moment/locale/hy-am.js 3.22 kB {0} [optional] [built]
[418] ./~/moment/locale/id.js 2.94 kB {0} [optional] [built]
[419] ./~/moment/locale/is.js 4.43 kB {0} [optional] [built]
[420] ./~/moment/locale/it.js 2.46 kB {0} [optional] [built]
[421] ./~/moment/locale/ja.js 2.21 kB {0} [optional] [built]
[422] ./~/moment/locale/jv.js 2.94 kB {0} [optional] [built]
[423] ./~/moment/locale/ka.js 3.15 kB {0} [optional] [built]
[424] ./~/moment/locale/kk.js 2.68 kB {0} [optional] [built]
[425] ./~/moment/locale/km.js 2.02 kB {0} [optional] [built]
[426] ./~/moment/locale/ko.js 2 kB {0} [optional] [built]
[427] ./~/moment/locale/ky.js 2.69 kB {0} [optional] [built]
[428] ./~/moment/locale/lb.js 4.92 kB {0} [optional] [built]
[429] ./~/moment/locale/lo.js 2.32 kB {0} [optional] [built]
[430] ./~/moment/locale/lt.js 4.37 kB {0} [optional] [built]
[431] ./~/moment/locale/lv.js 3.9 kB {0} [optional] [built]
[432] ./~/moment/locale/me.js 4.1 kB {0} [optional] [built]
[433] ./~/moment/locale/mk.js 3.11 kB {0} [optional] [built]
[434] ./~/moment/locale/ml.js 2.81 kB {0} [optional] [built]
[435] ./~/moment/locale/mr.js 5.39 kB {0} [optional] [built]
[436] ./~/moment/locale/ms-my.js 2.86 kB {0} [optional] [built]
[437] ./~/moment/locale/ms.js 2.85 kB {0} [optional] [built]
[438] ./~/moment/locale/my.js 2.73 kB {0} [optional] [built]
[439] ./~/moment/locale/nb.js 2.23 kB {0} [optional] [built]
[440] ./~/moment/locale/ne.js 3.81 kB {0} [optional] [built]
[441] ./~/moment/locale/nl.js 2.63 kB {0} [optional] [built]
[442] ./~/moment/locale/nn.js 2.09 kB {0} [optional] [built]
[443] ./~/moment/locale/pa-in.js 4.01 kB {0} [optional] [built]
[444] ./~/moment/locale/pl.js 3.94 kB {0} [optional] [built]
[445] ./~/moment/locale/pt-br.js 2.23 kB {0} [optional] [built]
[446] ./~/moment/locale/pt.js 2.34 kB {0} [optional] [built]
[447] ./~/moment/locale/ro.js 2.61 kB {0} [optional] [built]
[448] ./~/moment/locale/ru.js 7.08 kB {0} [optional] [built]
[449] ./~/moment/locale/se.js 2.23 kB {0} [optional] [built]
[450] ./~/moment/locale/si.js 2.43 kB {0} [optional] [built]
[451] ./~/moment/locale/sk.js 5.49 kB {0} [optional] [built]
[452] ./~/moment/locale/sl.js 6.16 kB {0} [optional] [built]
[453] ./~/moment/locale/sq.js 2.45 kB {0} [optional] [built]
[454] ./~/moment/locale/sr-cyrl.js 4.1 kB {0} [optional] [built]
[455] ./~/moment/locale/sr.js 4.09 kB {0} [optional] [built]
[456] ./~/moment/locale/ss.js 3.12 kB {0} [optional] [built]
[457] ./~/moment/locale/sv.js 2.4 kB {0} [optional] [built]
[458] ./~/moment/locale/sw.js 2.08 kB {0} [optional] [built]
[459] ./~/moment/locale/ta.js 4.21 kB {0} [optional] [built]
[460] ./~/moment/locale/te.js 3.09 kB {0} [optional] [built]
[461] ./~/moment/locale/th.js 2.4 kB {0} [optional] [built]
[462] ./~/moment/locale/tl-ph.js 2.14 kB {0} [optional] [built]
[463] ./~/moment/locale/tlh.js 4.09 kB {0} [optional] [built]
[464] ./~/moment/locale/tr.js 2.9 kB {0} [optional] [built]
[465] ./~/moment/locale/tzl.js 3.59 kB {0} [optional] [built]
[466] ./~/moment/locale/tzm-latn.js 2.11 kB {0} [optional] [built]
[467] ./~/moment/locale/tzm.js 2.06 kB {0} [optional] [built]
[468] ./~/moment/locale/uk.js 5.4 kB {0} [optional] [built]
[469] ./~/moment/locale/uz.js 2.03 kB {0} [optional] [built]
[470] ./~/moment/locale/vi.js 2.71 kB {0} [optional] [built]
[471] ./~/moment/locale/x-pseudo.js 2.5 kB {0} [optional] [built]
[472] ./~/moment/locale/zh-cn.js 4.35 kB {0} [optional] [built]
[473] ./~/moment/locale/zh-tw.js 3.12 kB {0} [optional] [built]
[474] ./~/moment/locale ^\.\/.*$ 2.41 kB {0} [optional] [built]
So why your custom build is smaller? You can use more aggressive minification. My build give me this information (probably it loose some weight at this point):
Condition always true [./~/moment/moment.js:8,0]
Condition always true [./~/moment/locale/af.js:6,0]
Condition always true [./~/moment/locale/ar-ma.js:7,0]
Condition always true [./~/moment/locale/ar-sa.js:6,0]
Condition always true [./~/moment/locale/ar-tn.js:5,0]
Condition always true [./~/moment/locale/ar.js:8,0]
Condition always true [./~/moment/locale/az.js:6,0]
Condition always true [./~/moment/locale/be.js:8,0]
Condition always true [./~/moment/locale/bg.js:6,0]
Condition always true [./~/moment/locale/bn.js:6,0]
Condition always true [./~/moment/locale/bo.js:6,0]
Condition always true [./~/moment/locale/br.js:6,0]
Condition always true [./~/moment/locale/bs.js:7,0]
Condition always true [./~/moment/locale/ca.js:6,0]
Condition always true [./~/moment/locale/cs.js:6,0]
Dropping unreachable code [./~/moment/locale/cs.js:31,0]
Dropping unreachable code [./~/moment/locale/cs.js:40,0]
Dropping unreachable code [./~/moment/locale/cs.js:49,0]
Dropping unreachable code [./~/moment/locale/cs.js:58,0]
Dropping unreachable code [./~/moment/locale/cs.js:67,0]
Condition always true [./~/moment/locale/cv.js:6,0]
Condition always true [./~/moment/locale/cy.js:6,0]
Condition always true [./~/moment/locale/da.js:6,0]
Condition always true [./~/moment/locale/de-at.js:9,0]
Condition always true [./~/moment/locale/de.js:8,0]
Condition always true [./~/moment/locale/dv.js:6,0]
Condition always true [./~/moment/locale/el.js:6,0]
Condition always true [./~/moment/locale/en-au.js:5,0]
Condition always true [./~/moment/locale/en-ca.js:6,0]
Condition always true [./~/moment/locale/en-gb.js:6,0]
Condition always true [./~/moment/locale/en-ie.js:6,0]
Condition always true [./~/moment/locale/en-nz.js:5,0]
Condition always true [./~/moment/locale/eo.js:8,0]
Condition always true [./~/moment/locale/es.js:6,0]
Condition always true [./~/moment/locale/et.js:7,0]
Condition always true [./~/moment/locale/eu.js:6,0]
Condition always true [./~/moment/locale/fa.js:6,0]
Condition always true [./~/moment/locale/fi.js:6,0]
Condition always true [./~/moment/locale/fo.js:6,0]
Condition always true [./~/moment/locale/fr-ca.js:6,0]
Condition always true [./~/moment/locale/fr-ch.js:6,0]
Condition always true [./~/moment/locale/fr.js:6,0]
Condition always true [./~/moment/locale/fy.js:6,0]
Condition always true [./~/moment/locale/gd.js:6,0]
Condition always true [./~/moment/locale/gl.js:6,0]
Condition always true [./~/moment/locale/he.js:8,0]
Condition always true [./~/moment/locale/hi.js:6,0]
Condition always true [./~/moment/locale/hr.js:6,0]
Condition always true [./~/moment/locale/hu.js:6,0]
Dropping unused variable suffix [./~/moment/locale/hu.js:16,0]
Condition always true [./~/moment/locale/hy-am.js:6,0]
Condition always true [./~/moment/locale/id.js:7,0]
Condition always true [./~/moment/locale/is.js:6,0]
Condition always true [./~/moment/locale/it.js:7,0]
Condition always true [./~/moment/locale/ja.js:6,0]
Condition always true [./~/moment/locale/jv.js:7,0]
Condition always true [./~/moment/locale/ka.js:6,0]
Condition always true [./~/moment/locale/kk.js:6,0]
Condition always true [./~/moment/locale/km.js:6,0]
Condition always true [./~/moment/locale/ko.js:10,0]
Condition always true [./~/moment/locale/ky.js:6,0]
Condition always true [./~/moment/locale/lb.js:6,0]
Condition always true [./~/moment/locale/lo.js:6,0]
Condition always true [./~/moment/locale/lt.js:6,0]
Condition always true [./~/moment/locale/lv.js:7,0]
Condition always true [./~/moment/locale/me.js:6,0]
Condition always true [./~/moment/locale/mk.js:6,0]
Condition always true [./~/moment/locale/ml.js:6,0]
Condition always true [./~/moment/locale/mr.js:7,0]
Condition always true [./~/moment/locale/ms-my.js:6,0]
Condition always true [./~/moment/locale/ms.js:6,0]
Condition always true [./~/moment/locale/my.js:6,0]
Condition always true [./~/moment/locale/nb.js:7,0]
Condition always true [./~/moment/locale/ne.js:6,0]
Condition always true [./~/moment/locale/nl.js:6,0]
Condition always true [./~/moment/locale/nn.js:6,0]
Condition always true [./~/moment/locale/pa-in.js:6,0]
Condition always true [./~/moment/locale/pl.js:6,0]
Condition always true [./~/moment/locale/pt-br.js:6,0]
Condition always true [./~/moment/locale/pt.js:6,0]
Condition always true [./~/moment/locale/ro.js:7,0]
Condition always true [./~/moment/locale/ru.js:8,0]
Condition always true [./~/moment/locale/se.js:6,0]
Condition always true [./~/moment/locale/si.js:6,0]
Condition always true [./~/moment/locale/sk.js:7,0]
Dropping unreachable code [./~/moment/locale/sk.js:32,0]
Dropping unreachable code [./~/moment/locale/sk.js:41,0]
Dropping unreachable code [./~/moment/locale/sk.js:50,0]
Dropping unreachable code [./~/moment/locale/sk.js:59,0]
Dropping unreachable code [./~/moment/locale/sk.js:68,0]
Condition always true [./~/moment/locale/sl.js:6,0]
Condition always true [./~/moment/locale/sq.js:8,0]
Condition always true [./~/moment/locale/sr-cyrl.js:6,0]
Condition always true [./~/moment/locale/sr.js:6,0]
Condition always true [./~/moment/locale/ss.js:6,0]
Condition always true [./~/moment/locale/sv.js:6,0]
Condition always true [./~/moment/locale/sw.js:6,0]
Condition always true [./~/moment/locale/ta.js:6,0]
Condition always true [./~/moment/locale/te.js:6,0]
Condition always true [./~/moment/locale/th.js:6,0]
Condition always true [./~/moment/locale/tl-ph.js:6,0]
Condition always true [./~/moment/locale/tlh.js:6,0]
Condition always true [./~/moment/locale/tr.js:7,0]
Condition always true [./~/moment/locale/tzl.js:6,0]
Condition always true [./~/moment/locale/tzm-latn.js:6,0]
Condition always true [./~/moment/locale/tzm.js:6,0]
Condition always true [./~/moment/locale/uk.js:7,0]
Condition always true [./~/moment/locale/uz.js:6,0]
Condition always true [./~/moment/locale/vi.js:6,0]
Condition always true [./~/moment/locale/x-pseudo.js:6,0]
Condition always true [./~/moment/locale/zh-cn.js:7,0]
Condition always true [./~/moment/locale/zh-tw.js:6,0]
Moment
isn't the best module for highlight the most powerful feature of webpack - reusing common modules (since moment
doesn't use dependencies).
Lest's consider that your application use 3 modules and each of them have some dependencies.
size
module1 20 kB
dependencies
- moduleA 10 kB
- moduleB 15 kB
- moduleC 5 kB
module1.min 50 kB
module2 10 kB
dependencies
- moduleA 10 kB
- moduleD 5 kB
module2.min 25 kB
module3 30 kB
dependencies
- moduleA 10 kB
- moduleC 5 kB
module3.min 45 kB
If you will only use .min
version of this 3 modules you will add 50 kB + 25 kB + 45 kB = 120 kB to your application.
size
module1 20 kB
dependencies
- moduleA 10 kB
- moduleB 15 kB
- moduleC 5 kB
module1.min 50 kB <-- this
module2 10 kB
dependencies
- moduleA 10 kB
- moduleD 5 kB
module2.min 25 kB <-- this
module3 30 kB
dependencies
- moduleA 10 kB
- moduleC 5 kB
module3.min 45 kB <-- this
On the other hand if you will build with source files, common modules will be added only once. Not like previously moduleA
was included 3 times and moduleC
2 times.
This times to your app will be added:
size
module1 20 kB <-- this
dependencies
- moduleA 10 kB <-- this
- moduleB 15 kB <-- this
- moduleC 5 kB <-- this
module1.min 50 kB
module2 10 kB <-- this
dependencies
- moduleA 10 kB
- moduleD 5 kB <-- this
module2.min 25 kB
module3 30 kB <-- this
dependencies
- moduleA 10 kB
- moduleC 5 kB
module3.min 45 kB
And finally with this approach you add 95 kB to your application.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With