Writing portable npm build scripts

tl;dr; Developers need to install and build Javascript NPM modules on Windows as well as *nix. With a little care this is possible without using heavyweights tools like Grunt and Gulp .

Modern HTML development usually includes a build and deploy process similar to those used in compiled development workflows. In this case, assets that end up deployed and accessed by end users are the result of a pipeline of operations such as transpiling, concatenation, minifying and zipping. In addition,  developers use these and others steps when developing, for example as part of test automation,  on check-in or as part of  continuous integration and deployment process. Perhaps somewhat surprisingly, the traditional build tools such as shell scripts, configure and Make (or Ant) are not commonplace. Rather, we often see newer JavaScript based tools like Grunt, Gulp or Broccoli being the “go-to” choice. Critically, these tools do have the advantage of largely working cross-platform on Linux/OS X and Windows.

npm logo

An alternative build option is to use NPM’s scripts feature in the project or module package.json. You can use commands like ‘npm run test’ to invoke important build processes. This has the advantage of putting the scripts in the same place as the rest of your project configuration. Also, actions may be broken up into sub actions or invoked though life-cycle triggers (like “before publish”). Unfortunately though, while NPM tracks module dependencies, these are not used in the scripts to minimize the required build steps (as Make does). Perhaps that will come in time, but until then, either everything gets built every time or you’ll need to call a build tool like Make from scripts. One issue with While is while it is very effective it has a rather gnarly syntax and plenty of awkward features that you need to get to grips with. That said, common useful rules are simply implemented. Another tool, Webpack looks interesting for building as it manages dependencies and also works with modules rather than files, as Make does.

Both Make and NPM scripts simply evoke the native command line shell to perform the actions for each build step and this raises an issue when you want to have your build work across platforms. The problem is that the shells have different syntax and command sets so you have to restrict npm scripts to a least common subset. Fortunately you can manage portability  with care. Evens  so, plenty of published modules exist that assume they are built on a *Nix Bash shell and so break on Windows. You might think you could get away with running one of the Bash shell systems for Windows (eg MSys, cygwin) but NPM always launches a Cmd shell (you can work around this by having your scripts run an extra bash shell, but that’s a bit hacky). More importantly using bash requires target build system configuration with yet another tool. We’d ideally like our build to work with just node (and thus npm) installed.

So assuming we have to write NPM scripts that run on both Bash and Cmd what can you do to reduce problems?

  • Separate commands in a single script with && (“and if no error”) or || (“or if error”) instead of  the terminator (; or : ). Remember you can invoke subscripts with “npm run xxx”
  • Modules like “concurrently” and “npm-run-all” add further task management options
  • Operators && || & < > and | all work pretty much the same in cmd and bash and offer a lot of power
  • Paths are a pain. While Windows system calls support the / separator it is also used for command options. Avoid as much as possible
  • In npm scripts “node_modules/.bin” is on the path so any CLI command modules installed with –save or –save-dev will available to scripts when the package is installed. For example “rimraf”, “mkdirp” and “ncp”. This avoids tell devs to do global installs of tools which may conflict with other tools.

Of course JavaScript itself is an ideal platform independent script tool so you could use nodejs to create build scripts called from your NPM scripts. And after all, that is what Grunt and Gulp do by providing a full on framework for build services. The choice as always, is yours. A useful approach is to use the “Shelljs” module that provides a unix style set of functions as an alternative to using the bash shell directly. In addition “Node-glob” provides wildcard expansions.

As a final thought, modules are usually distributed in source form and some contain native module source that must be compiled using a toolchain of Python and C++. Fortunately this is getting easier on Windows as described in Microsoft’s new nodejs Guidelines for Windows.

This entry was posted in development, Windows and tagged , , . Bookmark the permalink.
Skip to top


3 Responses to Writing portable npm build scripts

  1. Pingback: More on portable npm scripts | Opening Accessibility

  2. thorn0 says:

    “Operators && || & and | all work pretty much the same in cmd and bash and offer a lot of power”

    An important note: the & operators doesn’t work the same.

Leave a Reply

Your email address will not be published. Required fields are marked *