Get cache location with env variable - npm

I get get the NPM cache location using:
cache_location="$(npm get cache)"
however, is this value also represented by an env variable that I can read?
Something like NPM_CACHE_LOCATION?
https://docs.npmjs.com/cli/cache

Short answer: It depends on when/how you want to access it, as there is no env variable, (e.g. NPM_CACHE_LOCATION), available whilst npm is not running.
You'll need to invoke npm config get cache or npm get cache as you are currently doing.
However, once npm is running the configuration parameters are put into the environment with the npm_ prefix.
The following demonstrates this...
Discover which env variables are available:
As a way to find out what env variable(s) npm puts in the environment, you can utilize printenv in an npm-script. For example in package.json add:
...
"scripts": {
"print-env-vars": "printenv | grep \"^npm_\""
},
...
Then run the following command:
npm run print-env-vars
Get the cache location via an env variable:
In the resultant log to the console, (i.e. after running npm run print-env-vars), you'll see that there's the npm_config_cache environment variable listed. It reads something like this:
npm_config_cache=/Users/UserName/.npm
In the docs it states:
configuration
Configuration parameters are put in the environment with the npm_config_ prefix. For instance, you can view the effective root config by checking the npm_config_root environment variable.
Note: Running printenv | grep "^npm_" directly via the CLI returns nothing.
Accessing the cache location with env variable:
You can access the cache location via an npm-script, For example:
"scripts": {
"cache-loc-using-bash": "echo $npm_config_cache",
"cache-loc-using-win": "echo %npm_config_cache%"
},
See cross-var for utilizing a cross-platforms syntax.
Accessing the npm cache location via a Nodejs script. For example:
const cacheLocation = process.env.npm_config_cache;
console.log(cacheLocation)
Note: This node script will need to be invoked via an npm-script for the process.env.npm_config_cache to be available. Invoking it via the command line running, e.g. node ./somefile.js will return undefined - this further demonstrates that the parameters with the _npm prefix are only put into the environment whilst npm is running.
Not ideal, however you could set your own environment variable using export of course:
export NPM_CACHE_LOCATION="$(npm get cache)"
and unset to remove it:
unset NPM_CACHE_LOCATION

Related

Does package.json support compound variables?

A project that respects the semver directory structure for build artefacts is beginning soon, and package.json or .nmprc would seem to be the right place to define this metadata. This is some preliminary code that demonstrates how the goal is intended to be achieved:
{
"name": "com.vendor.product"
, "version": "0.0.0"
, "directories": {
"build": "./out"
}
, "main": "./${npm_directories_build}/${npm_package_name}/${npm_package_version}/${npm_package_name}.js"
, "exports": "${npm_package_main}"
, "scripts": {
"echo": "echo\"${npm_package_exports}\""
}
}
I expected
npm run echo
to print the compound variable result to standard output,
./out/com.vendor.product/0.0.0/com.vendor.product.js
but instead, it prints the literal text
${npm_package_export}
I attempted to use array variables in .npmrc
outpath[]=./out
outpath[]=/${npm_package_name}
outpath[]=/${npm_package_version}
But
...
{
"echo": "echo \"${npm_config_outpath}\""
}
Simply prints an empty newline
It was expected that package.json supports compound variables, but this assumption is now in question. I have checked documentation, but either I am missing something or such is not defined. Long hand repetition of the same data is to be avoided (e.g. multiple references to package variables in order to make a single path). It is intended for package name and version to always dictate the location of the build files in a reliable and predictable manner.
If compound variables are not supported, could you clarify how .npmrc array variables actually work? Failing that, could you recommend an alternative method to achieve the same ends? Many thanks!
Searched documentation:
https://docs.npmjs.com/misc/config
https://docs.npmjs.com/files/npmrc
https://docs.npmjs.com/configuring-npm/npmrc.html
https://docs.npmjs.com/files/package.json#config
http://doc.codingdict.com/npm-ref/misc/config.html#config-settings
https://github.com/npm/ini
Short Answer:
"Does package.json support compound variables?"
Unfortunately no, not for the way you are wanting to use them. It only has package json vars which can be used in npm scripts only. For example on *Nix defining the echo script as:
"echo": "echo $npm_package_version"
or on Windows defining it as:
"echo": "echo %npm_package_version%"
will print the version e.g. 0.0.0.
Note: cross-env provides a solution for a single syntax that works cross-platform.
You certainly cannot include parameter substitution ${...} elsewhere in package.json fields except for the scripts section.
Additional info:
Regarding your subsequent comment:
How array values defined in .npmrc can be used in package.json
AFAIK I don't think you can. For example let's say we;
Save this contrived .npmrc in the root of the project directory.
.npmrc
quux[]="one"
quux[]="two"
quux[]="three"
foo=foobar
Then cd to the project directory and run the following command to print all environment variables:
npm run env
As you can see, the npm_config_foo=foobar environment variable has been added by npm. However for the quux array there is no npm_config_quux=[ ... ] environment variable added.
So, in npm scripts using package.json vars the following does work:
"echo": "echo $npm_config_foo"
However the following, (for referencing the array), does not - simply because it does not exist;
"echo": "echo $npm_config_quux"
The ini node.js package:
Maybe consider investigating the ini node.js package that npm utilizes for parsing .npmrc files. For example:
If you run the following command to install the package in your project:
npm i -D ini
Then define the npm echo script as per this:
"scripts": {
"echo": "node -e \"var fs = require('fs'), ini = require('ini'); var config = ini.parse(fs.readFileSync('./.npmrc', 'utf-8')); console.log(config.quux)\""
}
Note it uses the nodejs command line option -e to evaluate the JavaScript code. It essentially executes the following:
var fs = require('fs'),
ini = require('ini');
var config = ini.parse(fs.readFileSync('./.npmrc', 'utf-8'));
console.log(config.quux);
Then given the contrived .npmrc file that I mentioned previously when running:
npm run echo
it will print:
[ 'one', 'two', 'three' ]

npm run script, can't access command line args

I'm an electron beginner, and I'm trying to use it to package up a react-based app. I'm trying to run electron via a script entry in my package.json:
"electron-dev": "concurrently \"cross-env BROWSER=none npm start\" \"wait-on http://localhost:3000 && electron . $npm_config_input \"",
That will run electron.js (which is what "main" is defined as earlier in the package.json file), but I need to pass in a command line argument. I've seen references that indicate $npm_config_input will have the argument passed in this way:
% npm run electron-dev --input=file.tif
But that $npm_config_input doesn't seem to get expanded for me. electron.js gets the literal string $npm_config_input. I'm confused why this isn't working.
It seems I could avoid this problem by doing this:
% npm run electron-dev -- --input=file.tif
But I don't know how to associate the input argument to the second command I'm starting using concurrently. It would be nice if I could use something like $1 or $npm_config_input in its definition. Does anyone have a solution for this?
I'm running this on Windows 10 using git bash. Other things generally work. I have nodejs 12.16.2 installed. TIA!
you almost got it right, just use process.argv.
for instance, create a file named cmd.js that looks like this
console.log(process.argv.slice(2));
now create a script hook for it by adding the following to package.json
"scripts": {
"foo": "node cmd.js"
}
now you can try it...
$ npm run foo -- arg1 arg2
> foo#1.0.0 foo /tmp/foo
> node cmd.js "arg1" "arg2"
[ 'arg1', 'arg2' ]
I believe I have found the answer. In electron.js, I printed process.argv to see if there was anything of use there. It turns out process.env.npm_config_input contains file.tif when I run this:
% npm run electron-dev --input=file.tif
That should work for me. I still don't understand why the other things I read about and tried didn't work.

Cross-platform way to pass environment variables as arguments to npm scripts

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?
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:
In the first line we require nodes builtin execSync(). We'll utilize this to run cross-env and set the environment variables.
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.
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.
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.

npm run: how to pass parameter and replace placeholder by param in script call

I want to to define a scripts entry in my package.json to simplify building for several environments.
In the script execution I need to replace $1 (or whatever syntax I need for a placeholder) by a parameter I would pass to npm run build-script like for example --env=prod or even simpler, --prod. How can I do that? Other questions and answers I found here didn't help me solve the problem.
"scripts": {
"build-for": "ng build --output-path=../../web/admin-v2 --env=$1 --base-href=\"/admin-v2/\""
}
I often resort to creating a utility node script for this kind of scenario and invoke it via the scripts section of package.json.
build-for.js
var nodeCLI = require('shelljs-nodecli');
var env = '--env=foo'; // <-- Default env flag/value when no arg provided.
if (process.argv.indexOf('--prod') !== -1) {
env = '--env=prod';
}
// Check for other possible env values
if (process.argv.indexOf('--quux') !== -1) {
env = '--env=quux';
}
// Run the ng build command
nodeCLI.exec('ng', 'build', '--output-path=../../web/admin-v2', env, '--base-href=\"/admin-v2/\"');
build-for.js utilizes nodes process.argv to ascertain the argument/flag passed via the CLI and then invokes the ng command (the one currently defined in your package.json) using shelljs-nodecli.
npm i -D shelljs-nodecli
Lets assume that build-for.js is saved to a hidden folder named .scripts in your projects root directory; then your scripts section of package.json will be defined as follows:
package.json
{
...
"scripts": {
"build-for": "node ./.scripts/build-for"
},
...
}
Running the script
Invoke the npm script by running:
npm run build-for -- --prod
Note the special -- before the argument --prod must be included as explained here
As of npm#2.0.0, you can use custom arguments when executing scripts. The special option -- is used by getopt to delimit the end of the options. npm will pass all the arguments after the -- directly to your script:
Given the logic curently in the build-for.js - when no arguments are passed, for example:
npm run build-for
...the env flag will be set to --env=foo
Running the following:
npm run build-for -- --quux
...will result in the env flag will be set to --env=quux
Caveat
I haven't fully tested build-for.js, so you may find that you don't need to escape the double-quotes in this part '--base-href=\"/admin-v2/\"' of the following command (nodeCLI may handle that for you.):
// Run the ng build command
nodeCLI.exec('ng', 'build', '--output-path=../../web/admin-v2', env, '--base-href=\"/admin-v2/\"');
Not exactly what you're looking for but you can use environment variables & provide them inline:
package.json script:
"<script>": "echo ${ENV1} ${ENV2}"
run like so:
ENV1=a ENV2=b npm run <script>
$ npm run <script>
a b

How do I set an environment variable?

In bash I can see npm environment variables with npm run env. USER=brianmackey is one such environment variable. How can I set an environment variable, say USER=billybob?
I know I can use npm config set <key> <value> [--global]. Is key+value always/in any case an environment variable? Can I set the environment variables in session?
Single Command
If you want to set environment variables for a single node command, you can simply do this:
$ USER=billybob node server.js
Loaded for each session
If you want to permanently set that environment variable for your user, edit your ~/.bash_profile and add the following line:
export USER="billybob"
This will automatically set the given environment variable each time you create a new terminal session.
Existing for the entire current session
Lastly, if you want to set the environment variable only for the current session, just run it as it's own command:
$ USER=billybob
$ node app.js # user is billybob
$ node app.js # user is still billybob
When you exit the session, these temporarily set environment variables will be cleared.