joelgriffith.net
  • About
  • GitHub
  • Twitter
  • LinkedIn
  • Resume

Require JavaScript files the sane way: with symlinks

October 28, 2015

On an application of medium to large size, requiring JavaScript files with relative paths can become a chore. I think you can agree that seeing: require('../../../some/dir/file_i_want.js') makes you feel somewhat uncomfortable. Though this may work in early-stage projects, and is pretty easy to do, there is pain that comes when refactoring or moving this module. That, plus it’s now harder to search and replace all the instances where file_i_want.js is used since all the require‘s vary just a bit. Ideally, what if we could just do require('my-app/some/dir/file_i_want.js')?

Isomorphic Applications

Let’s talk about the browser for a minute. There are pretty much two workflows for bundling in a browser right now (browserify and webpack), but with their own semantics on loading project code. In webpack world, you need to add some configuration details on how you’d like webpack to resolve modules if you want to more sanely require files. In browserify, you’re left with delving into plugin-land to get this working, or by publishing your modules via a registry, which works well except you still have the same problem inside published registries!

But, what if I were to tell you that there is a way to make Node, webpack, and browserify all understand require('my-app'), and that you don’t need any other plugins or software to do this?

Symlinks to the Rescue

All JavaScript bundlers understand one thing: node_modules. They will all, at some point in building, look into node_modules to see if they can find the file you’re looking for, and this behavior requires no setup or house-keeping to maintain. Since this is the case, you can very easily just cd node_modules and then run ln -s my-application ../ and be ready to go. If your JavaScript files are contained in a sub-folder, then just do ln -s my-application ../my-javascript.

Automation

You might be thinking that this is all well and good, but your node_modules is ignored by source control, so anyone doing work will have to do this every time. And maybe, if you’re like me, you tend to rm -rf node_modules every now-and-then. Well, npm has a very elegant way of handling that with the postinstall hook.

postinstall is an event when npm builds out your application, and happens after all external packages have been installed. These hooks live inside the scripts property of package.json. A simple one would be:

// package.json
{
  // ...
  "scripts": {
    "postinstall": "ln -s ../ ./node_modules/my-application"
  }
  // ...
}

Now, anytime someone stands up your app with npm i, npm will automatically generate this symlink, and anyone can just require('my-app') and have access to the root of the project. Let’s gussy this up just a tad more and only generate that symlink if it’s not there:

// package.json
{
  "scripts": {
    "postinstall": "[ -h ./node_modules/my-application ] || ln -sf ../ ./node_modules/my-application"
  }
}

This will prevent generating that symlink if it’s already present since you could end up with a circular symlink pretty quickly.

And that’s it! You can now just go along your merry way requiring files like a pro, and just have thing work with a lot of fuss since this works practically everywhere.