Cross-platform way to pass environment variables a

2020-02-14 06:26发布

From both Windows or Linux, I want a way to pass args to a npm script, but have them be injected as environment variables

From the command line, I'd start my project in this fashion:

npm run start -- --env=dev --host=localhost --port=1234

To consume my cli args & inject them as env variables regardless of platform, I used the cross-env npm package :

package.json

  "scripts": {
    "start": "cross-env env=%env% host=%host% port=%port% my-app"
  },

I understand the above is invalid syntax, but can that start script somehow consume the args I pass instead of forwarding them on to my-app?

1条回答
疯言疯语
2楼-- · 2020-02-14 06:54

Unfortunately npm does not, nor intends to, provide a builtin feature which allows arguments to be passed to the middle of a npm script (as stated here). Arguments can only be passed to the end of a script.

For Linux and macOS you can utilize bash functions in npm-scripts to pass arguments to the middle of a script, as per my answer here. However Windows will choke at such a solution.

As cross-platform compatibility is a requirement, consider moving the logic currently in your start script into a separate nodejs utility script. The nodejs script can then be invoked via the npm-script named start.

The following describes how to achieve your requirement in a cross-platform compatible way.


1. Custom nodejs utility script.

Create a nodejs script as follows. Let's name the script start.js and save it in the root of your project directory, i.e. at the same level where your package.json file currently resides.

const execSync = require('child_process').execSync;

const args = process.argv.splice(2, process.argv.length - 2)
    .map(arg => arg.replace(/^--/, ''));

execSync(`cross-env ${args.join(' ')} my-app`, {stdio:[0, 1, 2]});

Explanation:

  1. In the first line we require nodes builtin execSync(). We'll utilize this to run cross-env and set the environment variables.

  2. Nodes builtin process.argv obtains the arguments passed via the command-line. The first two items in nodes process.argv are:

    • The path to the executable running the JavaScript file.
    • The path of the JavaScript file being executed.
  3. However, we're only interested in the elements from the third item in the array onwards - as these will be your arguments passed via the CLI. These lines which read;

    const args = process.argv.splice(2, process.argv.length - 2)
        .map(arg => arg.replace(/^--/, ''));
    

    create an args variable and assigns an Array containing each argument passed via the CLI. The first two aforementioned items in point 2 are omitted from the array using the splice() method. In the map() method we remove the -- prefix from each argument.

  4. The last line reading:

    execSync(`cross-env ${args.join(' ')} my-app`, {stdio:[0, 1, 2]});
    

    invokes cross-env and places the arguments as a string using Template Literals and the Array join() method. The stdio part configures the pipes for stdin, stdout, stderr in the child process.

Note: If your targeting older versions of node that don't support Template Literals then you can substitute this line with the following instead. This handles string concatenation using the + operator:

execSync('cross-env ' + args.join(' ') + ' my-app', {stdio:[0, 1, 2]});

Similarly, if ES6 arrow functions aren't supported change the map() to use standard functions. For instance:

.map(function(arg) {
  return arg.replace(/^--/, '');
});

2. package.json script.

Redefine your start script in package.json as follows:

"scripts": {
  "start": "node start"
},

Here we are asking node to invoke the start.js script.

Note If you prefer to save the start.js file in a directory location which is different to the aforementioned root of your project directory, then you'll need to define the path to start.js as necessary. The path should be relative to package.json. For instance:

"scripts": {
  "start": "node ./the/path/to/start"
},

3. Running the npm-script.

The npm start script can be invoked via your CLI by running:

$ npm start -- --env=dev --host=localhost --port=1234

The run part, i.e. npm run start ... is not required when invoking the npm's start script.

查看更多
登录 后发表回答