Logo created by Freepik - Flaticon
Another [insert blazing fast synonyms] JavaScript package manager
Zap
is a JavaScript package manager (think npm/pnpm/yarn) that aims to be quick, reliable, memory efficient and developer friendly.
Disclaimer
Zap is a hobby project that I am currently working on in my free time. Documentation is sparse, Windows support is partial at best and the code is not yet ready for production.
I am not looking for contributors at the moment, but feel free to open an issue if you have any question or suggestion.
Warning
Use it at your own risk.
Installation
npm i -g @zap.org/zap
zap --help
Commands
Command | Aliases | Description | Status |
---|---|---|---|
zap install |
i add
|
Install dependencies | âś… |
zap remove |
rm uninstall un
|
Remove dependencies | âś… |
zap init |
create |
Create a new project or initialize a package.json file | âś… |
zap dlx |
x |
Execute a command in a temporary environment | âś… |
zap store |
s |
Manage the store | âś… |
zap run |
r |
Run a script defined in package.json | âś… |
zap rebuild |
rb |
Rebuild installed native node addons | âś… |
zap exec |
e |
Execute a shell command in the scope of the project | âś… |
zap update |
up upgrade
|
Update the lockfile with the newest package versions | ⏳ WIP |
zap why |
y |
Show information about why a package is installed | âś… |
project board for the current status of the project.
Check theFeatures
Here is a non exhaustive list of features that are currently implemented:
- Classic (~npm), isolated (~pnpm) or plug'n'play (~yarn) install strategies
# Classic install by default
zap i # or zap i --classic
# Isolated install
zap i --isolated
# Plug'n'play (experimental - no zero-installs yet)
zap i --pnp
or:
"zap": {
"strategy": "isolated",
"hoist_patterns": [
"react*"
],
"public_hoist_patterns": [
"*eslint*", "*prettier*"
]
}
// package.json
"workspaces": [
"core/*",
"packages/*"
]
// package.json
or to prevent hoisting:
"workspaces": {
"packages": [
"packages/**"
],
"nohoist": [
"react",
"react-dom",
"*babel*"
]
}
// package.json
# Install all workspaces
zap i
# Using pnpm-flavored filters (see: https://pnpm.io/filtering)
zap i -F "./libs/**" -F ...@my/package...[origin/develop]
zap i -w add pkg
## Scripts can be scoped too
# Run a single script in the current workspace.
zap run my-script
# Run scripts in all workspaces in parallel.
# Will use topological ordering by default - dependencies will run first…
zap -r run test
# …or omit the "run" argument.
zap -r test
# Scope to the dependencies of a specific workspace, and pack the output.
zap -F "my_app^..." --deferred-output run build
# Disregard the topological ordering and run the scripts in parallel.
zap run --parallel -r build
; .npmrc file
; default registry:
registry=https://registry.yarnpkg.com/
; scoped registries:
@myorg:registry=https://somewhere-else.com/myorg
@another:registry=https://somewhere-else.com/another
; scoped authentication: (supported fields -> _auth, _authToken, certfile, keyfile)
//registry.org/:_auth=BASICAUTHTOKEN
//registry.npmjs.org/:_authToken=BEARERTOKEN
; disable strict ssl peers checking: (default is true)
strict_ssl=false
; use a custom certificate authority file:
cafile=/certs/rootCA.crt
"overrides": {
"foo": {
".": "1.0.0",
"bar": "1.0.0"
}
},
"zap": {
"package_extensions": {
"react-redux@1": {
"peerDependencies": {
"react-dom": "*"
}
}
}
}
// package.json
zap i my-react@npm:react
zap i jquery2@npm:jquery@2
zap i jquery3@npm:jquery@3
Benchmarks
a.k.a is it fast?
Methodology
Benchmarks consist on installing a fresh create-react-app in various scenarii, with postinstall scripts disabled.
See: https://github.com/elbywan/zap/tree/main/bench
They are performed on my own personal laptop (macbook pro 16" 2019, 2,3 GHz Intel Core i9, 16 Go 2667 MHz DDR4) with 5G wifi and 1 Gb/s fiber.
The benchmarking tool is hyperfine and to make sure that the results are consistent I re-ran unfavorable results (high error delta).
I am aware that this is not a very scientific approach, but it should give rough good idea about what zap is capable of.
Results
Why?
This is a legitimate question. There are already a lot of package managers out there, and they all have their own pros and cons. So why another one?
First, I thought that it would be a good and fun challenge to build a package manager from scratch. I also really like the Crystal language and I have been using it for a couple of years now. So it would be a good opportunity to put my knowledge to the test.
I also experimented with a lot of package managers over the years, and I have a few praise and gripes with the existing ones:
-
npm is the de facto standard for JavaScript package management. It is reliable and has a huge community. But it is also super slow and lack features introduced by other package managers over time even though it is trying to catch up.
-
yarn is a great alternative to npm, it was a pioneer initially in terms of speed and it introduced many improvements and innovations along the years. Yarn is also impressively reliable which is paramount. I never used yarn berry in a significant project but the PnP approach is very interesting - unfortunately the downside is that does not seem to be compatible out of the box with a lot of packages from the ecosystem.
-
pnpm is an impressive package manager which introduced the concept of isolated installs. It handles workspaces very well with a lot of options to customize the behavior. Speed is mostly fine even with big monorepos. I experienced some reliability issues using it over the years though (peer dependencies handling, lockfile inconsistencies, very high memory consumption, need to manually delete the node modules folder…).
-
bun was a great source of inspiration for this project, but it comes with tradeoffs. While tremendously fast, it did not support some critical features when I started working on zap (and it still is very feature-limited), cannot be considered reliable as of today and is not as flexible as I would like it to be.
-
newer contenders (orogene, cotton, ultra…) are kind of interesting but they are clearly lacking in terms of features and/or reliability and/or speed.
So I decided to build a package manager that would be fast, flexible and easy to use. For my own personal use, but also for the community (in the long run).
How?
Zap is written in Crystal which is a compiled language, which means that it should be faster than JavaScript. It can easily tap into system calls and use the fastest ones depending on the platform (for instance clonefile). It is also an excellent fit when dealing with concurrent tasks.
Crystal also has experimental support for parallelism and can dispatch fibers to a pool of worker threads, which means that zap can take advantage of multiple cores. This is especially useful when dealing with CPU-bound tasks.
On top of that, zap will also try to cache package manifests in order to avoid unnecessary network calls in a performant way using messagepack.
Development
Prerequisites
- Install crystal
- (optional) Install the vscode extension and crystalline
Setup
git clone https://github.com/elbywan/zap
shards install
# Run the specs
crystal spec
# Build locally (-Dpreview_mt might not work on some os/arch)
shards build --progress -Dpreview_mt --release
Contributing
- Fork it (https://github.com/elbywan/zap/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
Contributors
- Julien Elbaz - creator and maintainer