Search code examples
npmuuidnpm-scripts

Assign a random number (UUID) value to a environment variable in npm script


I'd like to add a UUID argument when calling my npm script. Each time the script is called, I'd like to generate a new number. It should look like:

"build": "cross-env UUID=unique_number ng build"

The only thing I need is generating the unique_number here. I tried to use the uuid package but I don't know how to fire it in the script and pass the number as the argument.


Solution

  • tl;dr As you're question shows the use of cross-var I've assumed a cross-platform solution is required. In which case refer to the Solution A. However refer to either Solution B or C if my assumption is incorrect.


    Solution A: Cross Platform (Windows/Linux/macOS...)

    Fo a cross platform solution, (i.e. one that runs successfully on Windows, Linux, and macOS...), you'll need to utilize nodejs to achieve your requirement. There are a couple of different ways to approach this as described in the following two sub-sections titled:

    • Using an external nodejs (.js) file
    • Inlining your JavaScript in package.json.

    Note both approaches are effectively the same

    Using an external nodejs (.js) file

    1. Create a nodejs utility script. Let's name the file run-ng-build.js and save it in the root of your project directory, i.e. in the same directory where package.json currently resides:

      run-ng-build.js

      const uuid = require('uuid/v1');
      const execSync = require('child_process').execSync;
      
      process.env.UUID = uuid();
      
      execSync('ng build', { stdio: [0, 1, 2]} );
      
    2. In the scripts section of your package.json replace your current build script with the following:

      package.json

      "scripts": {
        "build": "node run-ng-build"
      }
      

    Explanation:

    • In run-ng-build.js we require the uuid package and the nodejs built-in execSync().
    • To create the environment variable named UUID we utilize the nodejs builtin process.env, and assign a uuid value to it by invoking uuid().
    • We then invoke the ng build command using execSync.
    • The options.stdio option configures the pipes between the parent and child process - [0, 1, 2] effectively inherit's stdin, stdout, and stderr.

    Inlining your JavaScript in package.json.

    Alternatively, you can inline your nodejs/JavaScript code in the scripts section of your package.json.

    1. In the scripts section of your package.json replace your current build script with the following instead:

      package.json

      "scripts": {
        "build": "node -e \"process.env.UUID = require('uuid/v1')(); require('child_process').execSync('ng build', { stdio: [0, 1, 2]} );\""
      }
      

    Explanation:

    • This is effectively the same as the aforementioned solution that utilized a separate .js file, however the use of a separate nodejs script/file is now redundant.
    • The nodejs command line option -e is utilized to evaluate the inline JavaScript.

    Important The cross-env package is redundant utilizing either of the two aforementioned solutions. To uninstall it run: npm un -D cross-env via your CLI.


    Solution B: *Nix platforms only (Linux/MacOS...)

    For *nix platforms only it becomes very terse, you can just define your build script in package.json as follows:

    package.json

    "scripts": {
      "build": "cross-env UUID=$(uuid) ng build"
    }
    

    This utilizes a Bash feature known as command substitution, i.e. $(uuid). However, if *nix is the only platform you need to support, then cross-env is really not necessary. Use the built-in export feature instead. For instance:

    package.json

    "scripts": {
      "build": "export UUID=$(uuid) && ng build"
    }
    

    Solution C: Windows only (cmd.exe)

    On Windows (only) running via Command Prompt or PowerShell you can do the following:

    package.json

    "scripts": {
      "build": "FOR /F %U IN ('uuid') DO cross-env UUID=%~U node -e \"process.env.UUID = require('uuid/v1')(); require('child_process').execSync('ng buuld', { stdio: [0, 1, 2] });\""
    }
    

    This is similar to the first example shown in Solution B however command substitution is achieved (very) differently in cmd.exe. See this answer for further explanation.