loading...
Cover image for Reducing npm package size by 83%

Reducing npm package size by 83%

nombrekeff profile image Manolo Edge Updated on ・2 min read

There is also a Spanish version of this post:


NPM recently added Unpacked Size to the package details, I then realized one of my libraries was way too big (350kb) for the code it has...

I started looking into it, and realized a lot of files were being packaged and uploaded, even though they are ignored in .gitignore.

The solution to this was to use .npmignore and ignore all the files you want to ignore, I knew about .npmignore but never thought of trying it out.

As mentioned in the comments, this could also be accomplished using the files property in package.json.

I also used a tool called size-limit that calculates the real cost to run your JS app or lib, thanks to this I changed a couple of dependencies that were way too big for the use case, and reduced the size even more.

Some libraries I changed:

Before Size After Size
cli-color 98.7 kb colors 39.5 kb
requests 201 kb phin 10.1 kb

As you can see, this makes a huge difference (-250 kb) for a little library.

I reduced the package from 359kb to 59kb, around -83% size difference :P

Notice

If you are starting out, please don't waste time optimizing your code until you understand why and how you are optimizing, premature optimization is evil and can cause bigger issues.

Posted on by:

nombrekeff profile

Manolo Edge

@nombrekeff

I don't know... I just code and they pay me!

Discussion

pic
Editor guide
 

Note that you can also use the files property in your package.json file to set a list of files that will be packaged along with the mandatory files. The type of this property is an array of strings so it's pretty easy to add packaged files. See this property as a whitelist. More informations on the official documentation.

Also get used to the npm publish --dry-run command. It can be useful to see what files are packaged before publishing. It can act as a test for your files property. Still in the official documentation.

 

Yup, I read something about the files property yesterday, I will try this approach, as it might be cleaner!

npm publish --dry-run
Damn, thanks, I was looking for this command, and could not find it :) I published 3 beta packages to test this out '

Do you know when should we use files and when to use .npmingore?

 

.npmignore act as a blacklist and files act as a whitelist. I guess i'ts a matter of preferences but I would rather be lazy and only define the files that I want to publish, and ignore all the others I didn't mention by using the `files property intsead of blacklisting all others files that should not be published to NPM. Again, a matter of preferences IMO.

Ok, thanks! Yup I guess it's a matter of preferences.

I would also rather define what is needed! I may change this then :)

 

For TypeScript users, files is definitely better than empty .npmignore that found on some tutorials.

Is there a reason to put it under files? In typescript I mean

better than empty .npmignore that found on some tutorials.

xD Why would they put an empty .npmignore? I guess just to keep the tutorial simple, and not add more complexity to it... they would be better of just by not having a .npmignore at all

Has an article called "For the love of god, don’t use .npmignore" by Jeff Dickey with awesome content explaining why you could use "files" in package.json instead of .npmignore.

Ohh, thanks, I will check it out :)

I've checked the post out, there are definitely some risks about using .npmignore, like unexpectedly upload credentials or critical information, but the risk is tolerable to the project I was optimizing, I will be taking this into account in the future though!

 

Switching from Webpack to Rollup also really crushed the individual assets. With proper tree-shaking, externals and attention to side-effects - we saw some of our original code assets go from 100kB down to 10 kB. Maybe that's also possible with webpack, but the config of Rollup is super easy. (We're shipping esm.js with sourcemaps as well as cjs.min.js and d.ts types.)

 

Cool,

One question though: is it recommended to use such tools, or even to bundle packages for use in pure node-js? Or should it just be done for browser packages?

 

Well, we’re writing in typescript, so it HAS to be transpiled. Generally speaking, I try to write my modules so that they are isomorphic, which means they will work in both browser and node. The thing though, is that these days you just don’t know what context is going to be used for importing your modules. Some people write in TS, others use vanilla JS in node contexts, and others prefer to make use of ecma2015 features and then transpile themselves. Offering multiple versions allows them to choose the best approach for integration - and using tooling like webpack or rollup with Babel makes it possible for you as a developer to be unopinionated and still write the code how you prefer.

 

Well, I'll play the usual jerk now. ^_^;
It says .npmingore instead of .npmignore, where? The first time it is mentioned, just below .gitignore, third paragraph.

Thanks for the article and the advice about premature optimization

 

Thanks, fixed! Glad you liked it :)

 

First of all, it's always a good practice to use extensions like 'Import Cost', that display import/require package size in the code, to notice it right away..

 

Ohh cool plugin :) I currently use Bundlephobia for this... but it's nice to have it in code!

I added size-limit for this, I run it on every commit and pr, and I will not accept pull requests if they exceed this thresholds for no reason!

 

Great idea for a post! Reducing the size of your NPM package also helps adoption and performance. 83% is a meaningful reduction for mobile users as well.

 

Good point, I didn't think about that. Mobile users, and in places were internet is slow it can make a huge impact. I always try and remember that not everybody has gigabit-speed internets and try and apply it to all of the projects I can.

 

Hey, I just released this post in Spanish, in case anybody is interested in reading it: