package.json Scripts

Use Case and Project Components

package.json Scripts

🙋 Need help? Ask an expert now!

package.json scripts Section

Similar to Makefile or Maven, npm promotes the concept of NodeJS project lifecycle stages and their "automation". Some stages are init, install, start. As we agreed, we skip the init stage as all projects these days come from some prototype projects. The install stage is pretty key: npm install loads the dependencies.

The scripts section in the package.json enables configuring custom behavior associated with the stages. Obviously, in the era of sophisticated configuration management tools such as Ansible, Chef, Pappet, Salt, managing project via scripts sounds somewhat basic, so the scripts approach should be taken at its face value and used to handle small specific tasks.

Let's review the content of the package.json scripts section in the demo project:

"scripts": {
    "start": "npm run start:dev",
    "start:production": "better-npm-run start-prod",
    "start:dev": "better-npm-run start-dev",
    "update-schema": "babel-node ./scripts/updateSchema.js",
    "prettier": "prettier --write \"{*.js,!(node*)**/*.js}\"",
    "build": "babel src -d build --copy-files && npm run build:copy",
    "build:copy": "copyfiles package.json ./build",
    "lint-fix": "eslint --fix ."
  },
  "betterScripts": {
    "start-prod": {
      "command": "node build/server.js",
      "env": {
        "NODE_ENV": "production"
      }
    },
    "start-dev": {
      "command": "nodemon -w src/server.js --exec \"babel-node src/server.js\"",
      "env": {
        "NODE_ENV": "development"
      }
    }
  },

The left side represents the name of the "state" or the "command" to be executed via npm run, e.g., npm run update-schema or npm run build. Note that npm has several standard CLI commands which are run by placing the command directly after npm, e.g., npm install, npm start - but we have the start script in the section as well.

Placing or omitting the run in npm seems unnecessary confusing and controversial. Generally, npm run <script name> executes just the script, whereas npm <command name> runs the full logic associated with the command. npm documentation has a full page write up on how individual commands line up against scripts with the same name. Luckily for us and most developers, one would never use this stuff, other than the start override, which in our case works fine via npm start. Our start script acts as an override to what the standard npm start would do, which is kicking off node server.js.

This is the custom script that gets executed via npm start:

"start": "npm run start:dev"

Our start script runs another script named start:dev, which is also present in the scripts section. In turn, start:dev runs better-npm-run start-dev. better-npm-run is a package used to facilitate scripts execution on Windows - it covers the file path differences (/ vs. \). better-npm-run is listed in dependencies, and the start-dev is in the betterScripts section.

Another popular package used on the Windows platform is cross-env. As we pointed out, we do not run NodeJS in Windows, even if we develop on a Windows laptop: NodeJS runs in a Linux container. However, using compatibility packages in NodeJS scripts is a common approach.

nodemon is also a package listed in dependencies. It "watches" the src/server.js file for changes and restarts the process as specified via the --exec option, which in our case is babel-node execution of the src/server.js file, the entry point into the application. Some more on the nodemon watcher below.

As you can see, start-prod executes the code from the build folder, directly from node, so that code must be processed via babel ahead of time - which is done, according to the scripts, in the build step.

This is a typical flow, but there are other ways and packages to use in either dev or prod flows. The options and packaging change, so often times package.json would contain components that are redundant or not even used in the actual implementation. The downside is a larger footprint and longer times building an instance, so, technically, package.json should be periodically optimized, but such an optimization is definitely not on the critical path to getting your application to work.

Will review update-schema in the GraphQL chapters. prettier and lint-fix are examples of helper scripts to manage linting and formatting in bulk, outside of the IDE.

Note On nodemon Watcher

The auto-restart option sounds like a convenient feature that increases development productivity: once some code changes take place, the server restarts by itself, and the developer can see if the changes worked or not. As the IDE should be set to the auto-save mode, edits get flushed to the disk in near real-time (although, there are ways to configure auto-safe with delays or do it on file exit, etc.). So, in reality, the server will be continuously restarting while a series of related edits is being coded. Eventually, the last restart is supposed to be the one reflecting the desired final state of the code.

Keeping the server restarting with incomplete code is Ok - it should not break anything. The problem is that nodemon-babel-node combination appears to have issues when working in a container. On Windows, host-managed file changes are not triggering the watcher functionality in the container, so the method doesn't work at all. When the container does see the changes, the process fails to close the running server before starting it again or works intermittently.

So, it looks like for the time being, till the tools catch up, manual restart of the server in the container shell is the only sure option: Ctrl-C, then Up Arrow to bring npm start back, followed by Enter.


Next, we'll review how the environment configuration is set up and maintained in the demo project

Edit Me on GitHub!