4

Use case:

I am setting up a prepublishOnly hook in NPM which deletes the "lib" folder, then transpiles the typescript source files into a new lib folder and then it does run the test.

The problem:

Two persons are supposed to publish the NPM packages, both working on different OS (Windows / Mac). Therefore the commands for deleting the folders are different.

  "scripts": {
    "build": "tsc",
    "clean": "rm -rf lib",
    "clean:windows": "if exist lib rmdir /s /q lib",
    "lint": "tslint --project tsconfig.json --format stylish src/**/*.ts",
    "format": "prettier --write \"**/*.ts\""
  },
  "husky": {
    "hooks": {
      "pre-push": "npm run clean:windows && npm run build && npm run test"
    }
  },

The question:

Is there a way to run NPM scripts conditionally (based on the used OS) or is there a remove folder command which works across these OS?

2
  • 1
    You may also want to take a look into ShellJS where you can use JavaScript functions which wrap the common shell commands in an OS specific way and you can just invoke node script.js to run the ShellJS commands. Commented Apr 27, 2018 at 7:46
  • 1
    @TomasHübelbauer - yes utilizing shelljs is more appropriate than using gulp for this kind of task. I've posted an answer to provide further detail of what you were suggesting in your comment. Commented Apr 27, 2018 at 11:50

2 Answers 2

5

Adding gulp to your list of tools seems rather unnecessary if all you want to do is delete a folder(s) via an npm script in a cross-platform way. For a lightweight solution consider the following two solutions instead:


Solution 1

Take a look at rimraf, it's described as:

The UNIX command rm -rf for node.

Usage

  1. cd to your project directory and run the following command:

    npm install rimraf --save-dev
    
  2. Change your clean script in package.json to the following:

    "scripts": {
      "clean": "rimraf lib",
      ...
    },
    
  3. Delete the clean:windows script from your package.json as that's now redundant.

Note I've not had great success with it when installing it as a local dependency, hence I use solution two below. However you may have greater success - so it's worth a try.


Solution 2

The following solution utilizes the shelljs rm command which is analogous to the Unix rm -rf command except it's for node - hence it's a cross platform equivalent.

The following steps shows how this can be achieved:

  1. cd to your project directory and install shelljs by running the following command:

    npm install shelljs --save-dev
    
  2. Create a custom node script as follows, let's name the file clean.js:

    const shell = require('shelljs');
    
    process.argv.slice(2).forEach(function(_path) {
      shell.rm('-rf', _path);
    });
    

    and save it to a hidden directory called .scripts in the projects root directory. For example:

    .
    ├── .scripts
    │   └── clean.js
    ├── lib
    │   └── ...
    ├── node_modules
    │   └── ...
    ├── package.json
    ├── src
    │   └── ...
    └── ...
    
  3. Replace your clean script in package.json to the following:

    "scripts": {
      "clean": "node .scripts/clean lib",
      ...
    },
    

    You can also pass multiple path arguments to clean.js too. For example:

    "scripts": {
      "clean": "node .scripts/clean lib another/folder/path foo/bar",
      ...
    },
    

    Filepaths are handled too. For example:

    "scripts": {
      "clean": "node .scripts/clean path/to/quux.js",
      ...
    },
    
  4. Delete the clean:windows script from your package.json as that's now redundant.

Notes

  1. clean.js utilizes nodes built-in process.argv to obtain an array of command line arguments passed to Node when the script was invoked.

  2. We then slice() the array of arguments from index 2 to ensure that we only include the folder/file path arguments (e.g. lib).

  3. Finally we loop over each folder path in the array using forEach() and invoke shell.rm('-rf', _path); in each turn of the loop to delete the asset.


Edit / Update:

An alternative cross-platform solution, (which wasn't available when originally posting this answer), is to utilize the shx package, which is described as:

shx is a wrapper around ShellJS Unix commands, providing an easy solution for simple Unix-like, cross-platform commands in npm package scripts

  1. Run the following command to install shx:

    npm i -D shx
    
  2. Then redefine your clean script in package.json as follows:

    "scripts": {
      "clean": "shx rm -rf lib"
    }
    
Sign up to request clarification or add additional context in comments.

Comments

1

The way I fix issues like this is to use gulp to define a set of os-specific tasks because the code you write there is OS-agnostic.

You will need to install gulp as a dependency and create a gulpfile.js in the root directory. In it, define a task to delete the folder.

var gulp = require('gulp'),
    path = require('path'),
    fs = require('fs-extra');

const rootFolder = path.join(__dirname);
const libFolder = path.join(rootFolder, 'lib');

gulp.task('clean:lib', function () {
  return deleteFolder(libFolder);
});

function deleteFolder(folder) {
  return fs.removeSync(folder);
}

gulp.task('deleteLib', ['clean:lib']);

Then, in your package.json script, you can then do && gulp deleteLib. (Make sure that the used packages in your gulpfile are installed as a dev-dependency in your package.json!)

Code is untested and written at the top of my head, but it's just to give you a general idea.

1 Comment

Alright makes sense to use gulp then, I always tried to avoid that but I have no better idea either at the moment :).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.