如何在 package.json 中使用环境变量

因为我们不希望项目代码中包括 package.json 文件在内的敏感数据,所以在我看来,使用环境变量是一个合乎逻辑的选择。

示例 package.json:

  "dependencies": {
"accounting": "~0.4.0",
"async": "~1.4.2",
"my-private-module":"git+https://${BB_USER}:${BB_PASS}@bitbucket.org/foo/bar.git"

这可能吗?

问题是 没有,如果这是 明智或者 情况不妙,只是 如果可能的话

102053 次浏览

No it isn't possible as npm does not treat any string values as any kind of templates.

It may be better to just use git+ssh (if your provider supports it) with an ssh agent.

No, it's not possible. You should access the repo using git+ssh, and store a private key in ~/.ssh.

Your line then looks like:

"my-private-module":"git+ssh://git@bitbucket.org/foo/bar.git"

Which doesn't contain anything sensitive.

You can use environment values to inject in your package.json like this:

Any environment variables that start with npm_config_ will be interpreted as a configuration parameter. For example, putting npm_config_foo=bar in your environment will set the foo configuration parameter to bar. Any environment configurations that are not given a value will be given the value of true. Config values are case-insensitive, so NPM_CONFIG_FOO=bar will work the same.

https://docs.npmjs.com/misc/config#environment-variables

I have similar but different requirement. For me, I want to use environment variables in the scripts.

Instead of using the environment variables directly in package.json, I do:

"some-script": "./scripts/some-script.sh",

And in some-script.sh:

#!/bin/sh


npm run some-other-script -- --prop=$SOME_ENV_VAR

Here's how I managed to work around package.json to achieve the same purpose. It uses a script that reads from a custom section of package.json for URL modules, interpolates environment variables in them, and installs them with npm install --no-save (the --no-save could be omitted, depending on the usecase).

As a bonus: it tries to read the env variable from .env.json, which can be gitignore'd, and very useful for development.

  1. Create a script that will read from a custom section of package.json

env-dependencies.js

const execSync = require('child_process').execSync
const pkg = require('./package.json')


if (!pkg.envDependencies) {
return process.exit(0)
}


let env = Object.assign({}, process.env)


if (typeof pkg.envDependencies.localJSON === 'string') {
try {
Object.assign(env, require(pkg.envDependencies.localJSON))
} catch (err) {
console.log(`Could not read or parse pkg.envDependencies.localJSON. Processing with env only.`)
}
}


if (typeof pkg.envDependencies.urls === 'undefined') {
console.log(`pkg.envDependencies.urls not found or empty. Passing.`)
process.exit(0)
}


if (
!Array.isArray(pkg.envDependencies.urls) ||
!(pkg.envDependencies.urls.every(url => typeof url === 'string'))
) {
throw new Error(`pkg.envDependencies.urls should have a signature of String[]`)
}


const parsed = pkg.envDependencies.urls
.map(url => url.replace(/\${([0-9a-zA-Z_]*)}/g, (_, varName) => {
if (typeof env[varName] === 'string') {
return env[varName]
} else {
throw new Error(`Could not read env variable ${varName} in url ${url}`)
}
}))
.join(' ')


try {
execSync('npm install --no-save ' + parsed, { stdio: [0, 1, 2] })
process.exit(0)
} catch (err) {
throw new Error('Could not install pkg.envDependencies. Are you sure the remote URLs all have a package.json?')
}
  1. Add a "postinstall": "node env-dependencies.js" to your package.json, that way it will be run on every npm install

  2. Add your private git repos to package.json using the URLs you want (note: they all must have a package.json at root!):

"envDependencies": {
"localJSON": "./.env.json",
"urls": [
"git+https://${GITHUB_PERSONAL_ACCESS_TOKEN}@github.com/user/repo#semver:^2.0.0"
]
},

(the semver specifier #semver:^2.0.0 can be omitted, but refers to a git tag, which can be very useful, as it makes your git server a fully-fledge package manager)

  1. npm install

In case you use .env file, let's use grep or eval to get a value environment variable from the .env file.

Updated start2 as @Paul suggested:

"scripts": {
"start": "NODE_ENV=$(grep NODE_ENV .env | cut -d '=' -f2) some_script",
"start2": "eval $(grep '^NODE_ENV' .env) && some_script"
}

I had the same need and my solution was based on @Long Nguyen's response. This way, I can only rely on what's defined on the .env file.

.env

...
SKIP_PREFLIGHT_CHECK=true
...

package.json

...
"scripts": {
"test": "yarn cross-env $(grep SKIP_PREFLIGHT_CHECK ../../.env) react-app-rewired test --watchAll=false"
}
...

For complicated environment variables, you can use https://stedolan.github.io/jq/ to access JSON file (env file at your case) JSON file could be something like

{
"env" :
{
"username" : "1345345",
"Groups" :  [],
"arraytest" : [
{
"yes" : "1",
"no" : "0"
}
]
}
}

so the script could be something like this to access yes value

"scripts": {
"yes": "jq [].arraytest[0].yes?"
}