30

I am creating a bash script which runs through each of my projects and runs npm run test if the test script exists.

I know that if I get into a project and run npm run it will give me the list of available scripts as follows:

Lifecycle scripts included in www:
  start
    node server.js
  test
    mocha --require @babel/register --require dotenv/config --watch-extensions js **/*.test.js

available via `npm run-script`:
  dev
    node -r dotenv/config server.js
  dev:watch
    nodemon -r dotenv/config server.js
  build
    next build

However, I have no idea how to grab that information, see if test is available and then run it.

Here is my current code:

#!/bin/bash

ROOT_PATH="$(cd "$(dirname "$0")" && pwd)"
BASE_PATH="${ROOT_PATH}/../.."

while read MYAPP; do # reads from a list of projects
  PROJECT="${MYAPP}"
  FOLDER="${BASE_PATH}/${PROJECT}"
  cd "$FOLDER"
  if [ check here if the command exists ]; then
    npm run test
    echo ""
  fi
done < "${ROOT_PATH}/../assets/apps-manifest"
theJuls
  • 6,788
  • 14
  • 73
  • 160

3 Answers3

47

EDIT: As mentioned by Marie and James if you only want to run the command if it exists, npm has an option for that:

npm run test --if-present

This way you can have a generic script that work with multiple projects (that may or may not have an specific task) without having the risk of receiving an error.

Source: https://docs.npmjs.com/cli/run-script

EDIT

You could do a grep to check for the word test:

npm run | grep -q test

this return true if the result in npm run contains the word test

In your script it would look like this:

#!/bin/bash

ROOT_PATH="$(cd "$(dirname "$0")" && pwd)"
BASE_PATH="${ROOT_PATH}/../.."

while read MYAPP; do # reads from a list of projects
  PROJECT="${MYAPP}"
  FOLDER="${BASE_PATH}/${PROJECT}"
  cd "$FOLDER"
  if npm run | grep -q test; then
    npm run test
    echo ""
  fi
done < "${ROOT_PATH}/../assets/apps-manifest"

It just would be a problem if the word test is in there with another meaning Hope it helps

Denis
  • 2,030
  • 1
  • 12
  • 15
  • FYI: Answer below (to use `npm run test --if-present`) is a better solution – Marie Hoeger Mar 28 '19 at 18:12
  • 1
    I will add your suggestion @MarieHoeger as for the user case present it is indeed a better solution, I will still leave my other solution because for the exact description of the question (How to check if npm script exists?) my solution can be used in more cases and may help other users. – Denis Apr 08 '19 at 20:09
  • Awesome, thank you @Denis! I think my wording was also more blunt than I meant it to be. Apologies! – Marie Hoeger Apr 08 '19 at 20:17
  • 1
    Is it just me or is the `--if-present` solution... wrong? Not defining a "test" script and then running `npm test`, `npm run test` `npm run-script test` etc. both with and without the `--is-present` flag gives the same result: `> echo 'Error: no test specified'` `Error: no test specified` – Dan Haddigan Feb 11 '20 at 17:33
  • 1
    I checked and got the same behaviour here.. but it seems specific to the "test" command, for any other command it works. I think there is some sort of default test script if you don't define one, so it's always present (at least for the --if-present) but I couldn't anything on the documentation that back this up – Denis Feb 12 '20 at 23:47
  • Would change my vote if possible. See https://stackoverflow.com/a/74694099/901899. – rainabba Dec 05 '22 at 20:35
18

The right solution is using the if-present flag:

npm run test --if-present

Alex Filatov
  • 2,232
  • 3
  • 32
  • 39
James
  • 189
  • 1
  • 3
0

--if-present doesn't allow you to "check if a npm script exists", but runs the script if it exists. If you have fallback logic this won't suffice. In my case, I want to run npm run test:ci if it exists and if not check for and run, npm run test. Using --if-present would run the test:ci AND test scripts if both exists. By checking if one exists first, we can decide which to run.

Because I have both "test" and "test:ci" scripts, the npm run | grep approach wasn't sufficient. As much as I'd like to do this with strictly npm, I have jq in my environments so I decided to go that route to have precision.

To check for a script named "test:ci":

if [[ $(jq '.scripts["test:ci"]' < package.json;) != null ]]; then
  // script exists
fi
rainabba
  • 3,804
  • 35
  • 35