symdeps
is an npm
utility that lets you define custom install paths for your JavaScript dependencies with just a bit of configuration in your project’s package.json
. It supports both symbolic and hard links, can handle individual files or entire directories, and can be set up to run automatically.
Inspiration
Recently I was excited to learn of composer
’s ability to install dependencies to a custom path, and quickly wondered why this isn’t a feature in npm
. The answer is probably that npm
’s requirements as a package manager are defined by the needs of Node.js applications, which expect to find their dependencies in a single place: node_modules/
.
Yet the fact remains that, being the go-to package management tool for JavaScript, npm
is used by many developers working on a wide variety of projects, not all of them Node.js applications. For many of these projects, the node_modules/
directory simply doesn’t cut it. A Drupal project would probably need them in web/libraries/
and an Express project somewhere inside the public/
directory. If you’re using a static site generator, you’ll probably want libraries in js/vendor/
or something similar.
So far, the most popular solution I’ve seen is using Gulp/Grunt tasks to move files around. This is overkill, and we can do better. After all, where your dependencies are installed should be the concern of your dependency management configuration, not code. So I designed an alternative approach.
The Solution
First, install symdeps:
npm install --save-dev symdeps
// or for those using yarn…
yarn add --dev symdeps
With that out of the way, all you need is something like this in your package.json
:
// package.json
{
"scripts": {
"postinstall": "symdeps"
},
// …
"symdeps": {
"paths": {
"web/libraries/js/vendor": [
"jquery/dist/jquery.min.js",
"mediaelement/build/mediaelement.min.js"
],
"web/libraries/css/vendor": [
"mediaelement/build/mediaelementplayer.min.css"
"mediaelement/build/mejs-controls.png",
"mediaelement/build/mejs-controls.svg"
],
}
}
}
Now every time you or your teammates run npm install
, the following files will be symlinked to their appropriate source in ./node_modules/
:
web/libraries/js/vendor/jquery.min.js
web/libraries/js/vendor/mediaelement.min.js
web/libraries/css/vendor/mediaelementplayer.min.css
web/libraries/css/vendor/mejs-controls.png
web/libraries/css/vendor/mejs-controls.svg
If you need hard links (say if you’re creating a deployment artifact and need actual file pointers instead of symbolic links), add the --hard
flag to the command or set "hard": true
in the symdeps
settings, like so:
// package.json
{
"scripts": {
"postinstall": "symdeps --hard"
},
// or…
"symdeps": {
"hard": true,
"paths": { /* … */ }
}
}
This saves you the time and, let’s face it, crud of setting up, maintaining, and running Gulp/Grunt tasks designed exclusively to move files around. It’s also one less task that needs to run on every build.
I hope you find this useful. Bug reports, feature requests, and pull requests are welcome on GitHub!