By default, the entire module is published to the NPM registry, except for
files listed inside your .gitignore
file. This filters out the dependencies but
still allows quite a lot of files to be pushed. The users then have to wait
for the package to download on every install. See the complaint in this
article.
As an example, the author reports that the grunt module is a 6MB download.
Can you measure your own modules and make them smaller? I have decided to find out. I will use check-more-types as an example. It has just a few files, plus unit tests and documentation folder used to build the README markdown file.
First, we need to find out the size of the current package. NPM stores all files
as tar files, thus we need to make one and see its size. You can make a tar
archive from the current folder using npm pack .
command. Let us make a tar file,
show its contents and size using a script suggested by Mathias Bynens
mathias
tarball="$(npm pack .)"; wc -c "${tarball}"; tar tvf "${tarball}"; rm "${tarball}";
I measured the tar size at the commit 3ada360
1 | $ tarball="$(npm pack .)"; wc -c "${tarball}"; tar tvf "${tarball}"; rm "${tarball}"; |
Lots of files, and the packed size is 25184 bytes. Let us first automate this process a little. I found a good utility for reporting the same size, but avoiding the shell command - pkgfiles by Tim Oxley @secoif.
I installed this as dev dependency and added a prepublish script to the package.json
1 | { |
The prepublish script will run every time on local install and when I run npm publish
command.
Let us see the output right now from npm run publish
command
1 | $ npm run prepublish |
Very detailed information, and the heaviest files are reported last, making it convenient
to see in the terminal. The heaviest is the docs
and tests
folders - we don't actually want to
publish them!
There are 3 ways to limit the files published to the NPM registry. We are using the default -
the files listed in .gitignore
are black listed. We could write a separate .npmignore
file
to black list more files. Or we could white list files inside package.json
. I prefer to white list
files. Note that some files (like package.json
, README
, etc) are always white listed.
1 | { |
Note that in the files
list you can exclude files using !
notation. For example
if we have a test subfolder inside src
folder, we can exclude it
1 | { |
If we had test files and production files in the same folder "src" we could ban individual files like this
1 | { |
The new commit is bc3e2a1, let us see the size break down
1 | $ npm run prepublish |
The published size went down 55% - from 107 kB to 48 kB. This is just raw source size,
let us see the change in the tar file. Unfortunately, using the npm pack .
command will call
our prepublish
script and getting confused by its output.
Thus I renamed the prepublish
step for now before running the tar command again.
I even added the tarball=...
command to the NPM scripts (under the name size
) for reuse.
1 | kensho at Kenshos-MacBook-Air.local ~/git/check-more-types on master* |
Any dependent client will have to download only 13 kB instead of 25 kB, 50% size reduction down the wire!
Finally, I have decided to show the npm published size on every code push from the local repo to
the remote master. I use my own [pre-git][pre-git] for managing Git hooks. I just added both
the size
and pkgfiles
commands to the pre-push
steps to run
npm install -D pre-git
1 | { |
I bumped the package version from 2.1.2
to 2.2.0
to publish this smaller module.
To verify, I used a temp folder and NPM version 3.4.0
1 | $ time npm i [email protected] |
Almost 3 seconds. Let us delete the node_modules
folder and try the new package
$ rm -rf node_modules/
1 | $ time npm i [email protected] |
We have shaved 1 second, almost 30% from the installation time for our single tiny module!
The final commands together in one place for simple copying (from kensho/check-more-types/package.json)
npm install -D pkgfiles pre-git
1 | { |
or even a shorter "size" command with variable "t" instead of "tarball" to keep the filename of the temporary archive
1 | "scripts": { |