Execute different commands based on operating system using NPM conditional script for Windows and Mac

Scenario:

I am currently configuring a prepublishOnly hook in NPM. This hook is designed to remove the "lib" folder, transpile the typescript source files into a new lib folder, and then execute the tests.

The issue at hand:

There are two individuals responsible for publishing the NPM packages, each using a different operating system (Windows / Mac). Due to this difference, the commands used to delete folders vary between the two OSs.


  "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 query being posed:

Is there a method to conditionally execute NPM scripts based on the operating system being used? Alternatively, is there a command that can be utilized to remove folders across both Windows and Mac?

Answer №1

If you're looking to efficiently delete a folder(s) via an npm script in a cross-platform manner, incorporating gulp into your toolkit might be unnecessary. Consider the following two lightweight alternatives:


Innovative Approach 1

Check out rimraf, which functions as:

The node equivalent of the UNIX command rm -rf.

Implementation Steps

  1. Navigate to your project directory and execute this command:

    npm install rimraf --save-dev
    
  2. Modify your clean script in package.json as shown below:

    "scripts": {
      "clean": "rimraf lib",
      ...
    },
    
  3. Remove the clean:windows script from your package.json since it's now obsolete.

Note While I haven't had much luck with local installation, solution two provides a viable alternative worth exploring.


Creative Solution 2

This approach leverages the shelljs rm command akin to Unix's rm -rf for seamless cross-platform capability.

Here are the steps to implement this:

  1. Head to your project directory and install shelljs using the command below:

    npm install shelljs --save-dev
    
  2. Create a custom node script named clean.js:

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

    Save this script in a hidden directory called .scripts within your project's root directory e.g.:

    .
    ├── .scripts
    │   └── clean.js
    ├── lib
    │   └── ...
    ├── node_modules
    │   └── ...
    ├── package.json
    ├── src
    │   └── ...
    └── ...
    
  3. Update your clean script in package.json as follows:

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

    You can also pass multiple path arguments to clean.js or even filepaths.

  4. Delete the clean:windows script from your package.json as it's no longer needed.

Key Points

  1. clean.js uses process.argv to retrieve command line arguments passed to Node when invoked.

  2. We extract relevant folder/file path arguments by slicing the argument array starting at index 2.

  3. We iterate over each path in the array and delete the assets using shell.rm('-rf', _path);.


Edit / Update:

An additional cross-platform possibility involves utilizing the shx package, a wrapper around ShellJS Unix commands tailored for simple, cross-platform scripts in npm packages:

shx simplifies Unix-like actions within npm package scripts.

  1. To incorporate shx, run this command:

    npm i -D shx
    
  2. Redefine your clean script in package.json like so:

    "scripts": {
      "clean": "shx rm -rf lib"
    }
    

Answer №2

My approach to resolving issues like this involves utilizing gulp to define a series of tasks that are specific to different operating systems, as the code written in these tasks is not tied to any particular OS.

To implement this solution, you will first need to install gulp as a dependency and then create a gulpfile.js in the root directory. Within this file, you can define a task for deleting a folder as follows:

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']);

Next, update your package.json script to include && gulp deleteLib. (Ensure that the packages used in your gulpfile are installed as dev-dependencies in your package.json!)

Please note that the provided code is untested and was composed off the top of my head, but it serves as a basic guideline.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way for me to determine the average number of likes on a post?

I have a Post model with various fields such as author, content, views, likedBy, tags, and comments. model Post { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt id String @id @default(cuid()) author U ...

Having trouble injecting $resource into my Jasmine unit test

I've encountered an issue while trying to test my self-written service that utilizes $resource. I'm facing difficulties when trying to inject $resource into my test spec. My setup includes Typescript with AngularJS 1.6.x, and here is a snippet o ...

Error Encountered When Searching for Modules in a Yeoman-Generated Express TypeScript Project

After generating an express typescript project using yeoman, I encountered some errors whenever I tried running the application. The errors stated that it could not find modules such as "morgan", "body-parser", and "cookie-parser". Even though these module ...

Using Angular to display asynchronous data with ngIf and observables

In cases where the data is not ready, I prefer to display a loader without sending multiple requests. To achieve this, I utilize the as operator for request reuse. <div class="loading-overlay" *ngIf="this.indicatorService.loadingIndicators[this?.indic ...

Having trouble with installing angular-cli

angular-cli unexpectedly quits while trying installing: (myapp)vagrant@myapp-local:/vagrant$ sudo npm install -g angular-cli npm WARN deprecated <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="caadb8aba9afacbfa6e7acb98afbe4f8e4 ...

Encountered a problem while trying to install the Ionic framework using npm

Having trouble installing ionic on my Windows system 76 error Windows_NT 6.1.7601 77 error argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm& ...

When attempting to install pm2 on a virtual hosting Ubuntu server, I encountered the warning message "npm WARN deprecated [email protected] : Please update to version 7 or higher."

Every time I attempt to install pm2 in Ubuntu on my virtual hosting machine, I encounter the following error. Command: sudo npm install pm2@latest -g Error: npm WARN deprecated <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f ...

Angular 10 and Typescript: Variables assigned within the change event become undefined

In my code, I initialize an Algolia input and set an onchange event to it. This initialization takes place in a service. algolia_data; random_var; this.http.post<any>('APIENDPOINT', formData).subscribe(data => { instance = places({ ...

Creating a custom Angular filter to group every 3 items within an iteration into a new div container

When attempting to organize three instances of app-story-preview within a wrapper div using a ngFor loop, I encountered the following code: <ng-template [ngIf]="stories.length > 0" [ngIfElse]="empty"> <cdk-virtual-scroll-viewport [itemSize ...

Getting rid of Glyphicons in Laravel 5.4

I am currently working on a project using laravel-5.4 and I need to remove Glyphicons from it. To achieve this, I executed the following command from the root folder of my project: npm uninstall glyphicons-halflings After running npm run production, the ...

Encountering an npm error: Directory not found when installing a package that relies on another

Note: Even with the most recent npm and node versions installed, none of the solutions provided in npm install error ENOTDIR have worked for me. My current task involves installing two npm modules that are distributed as .tgz packages on NodeRed. 1. x.tg ...

Despite setting the esModuleInterop flag, I am still encountering an error with default imports

My React project with TypeScript is causing some issues. In the main tsx file, the import React from 'react' line works fine. However, in my test file, I keep getting the TS1259 error. I suspect there might be a problem with my TS/Jest/Babel conf ...

When using string as a primitive type in Vue 3, what distinguishes the usage of PropType in props from not using it?

The documentation explains how Vue does runtime validation on props with defined types. To enable TypeScript to recognize these types, constructors are cast with PropType. The code example in the documentation uses success: { type: String }, whereas it c ...

Compile an empty file using the watch mode feature of the 'sass' package within npm

Using the npm sass library to compile my ".scss" file has been working well for me without any issues. However, I have encountered a peculiar problem in watch mode where, after saving some files, it starts producing empty files. To resolve this, I usuall ...

Error: The specified module 'sqlite' does not have an export named 'default' as requested

Having some difficulty with importing sqlite into my project. When I add this line: import sqlite from 'sqlite'; An error occurs: file:///D:/WebPro/WebProg/cwCode/dbInteract.js:2 import sqlite from 'sqlite'; ^^^^^^ SyntaxError: ...

"Discover the power of Next.js by utilizing dynamic routes from a curated list

I am working on a Next.js application that has a specific pages structure. My goal is to add a prefix to all routes, with the condition that the prefix must be either 'A', 'B', or 'C'. If any other prefix is used, it should re ...

Exploring the capabilities of Vitest by testing a RESTful API in SvelteKit

I am looking to create unit tests for a REST API built with SvelteKit, but most of the available resources focus on testing svelte components. Additionally, I prefer to avoid using Playwright as I do not require browser testing and want to steer clear of d ...

Installing and saving a local relative path with NPM

Having trouble using npm install to add a local .tgz package containing shared Angular components. npm install ./../@company/my-package-0.10.0.tgz --save The issue here is that the code above stores the full file path in the package.json. For example: ...

What could be causing the issue with the Vue CLI not working after installation via npm 6.9.0?

Whenever I try to run the command below: vue create hello-world I receive this message : 'vue' is not recognized as an internal or external command, operable program or batch file. It's been a month and I'm still struggling to fin ...

Employing the filter or find technique to extract an element contained within a JSON data structure

Is it possible to retrieve one of these items using the filter or find method to search for a match within the fiberAgrupations array? I attempted the following: const landlineRate = this.monolineJsonRates[0].cambioCaudal.getAll() .filter(landlinedRat ...