1// Set environment variables for any non-default configs, 2// so that they're already there when we run lifecycle scripts. 3// 4// See https://github.com/npm/rfcs/pull/90 5 6// Return the env key if this is a thing that belongs in the env. 7// Ie, if the key isn't a @scope, //nerf.dart, or _private, 8// and the value is a string or array. Otherwise return false. 9const envKey = (key, val) => { 10 return !/^[/@_]/.test(key) && 11 (typeof envVal(val) === 'string') && 12 `npm_config_${key.replace(/-/g, '_').toLowerCase()}` 13} 14 15const envVal = val => Array.isArray(val) ? val.map(v => envVal(v)).join('\n\n') 16 : val === null || val === undefined || val === false ? '' 17 : typeof val === 'object' ? null 18 : String(val) 19 20const sameConfigValue = (def, val) => 21 !Array.isArray(val) || !Array.isArray(def) ? def === val 22 : sameArrayValue(def, val) 23 24const sameArrayValue = (def, val) => { 25 if (def.length !== val.length) { 26 return false 27 } 28 29 for (let i = 0; i < def.length; i++) { 30 /* istanbul ignore next - there are no array configs where the default 31 * is not an empty array, so this loop is a no-op, but it's the correct 32 * thing to do if we ever DO add a config like that. */ 33 if (def[i] !== val[i]) { 34 return false 35 } 36 } 37 return true 38} 39 40const setEnv = (env, rawKey, rawVal) => { 41 const val = envVal(rawVal) 42 const key = envKey(rawKey, val) 43 if (key && val !== null) { 44 env[key] = val 45 } 46} 47 48const setEnvs = (config) => { 49 // This ensures that all npm config values that are not the defaults are 50 // shared appropriately with child processes, without false positives. 51 const { 52 env, 53 defaults, 54 definitions, 55 list: [cliConf, envConf], 56 } = config 57 58 env.INIT_CWD = process.cwd() 59 60 // if the key is deprecated, skip it always. 61 // if the key is the default value, 62 // if the environ is NOT the default value, 63 // set the environ 64 // else skip it, it's fine 65 // if the key is NOT the default value, 66 // if the env is setting it, then leave it (already set) 67 // otherwise, set the env 68 const cliSet = new Set(Object.keys(cliConf)) 69 const envSet = new Set(Object.keys(envConf)) 70 for (const key in cliConf) { 71 const { deprecated, envExport = true } = definitions[key] || {} 72 if (deprecated || envExport === false) { 73 continue 74 } 75 76 if (sameConfigValue(defaults[key], cliConf[key])) { 77 // config is the default, if the env thought different, then we 78 // have to set it BACK to the default in the environment. 79 if (!sameConfigValue(envConf[key], cliConf[key])) { 80 setEnv(env, key, cliConf[key]) 81 } 82 } else { 83 // config is not the default. if the env wasn't the one to set 84 // it that way, then we have to put it in the env 85 if (!(envSet.has(key) && !cliSet.has(key))) { 86 setEnv(env, key, cliConf[key]) 87 } 88 } 89 } 90 91 // also set some other common nice envs that we want to rely on 92 env.HOME = config.home 93 env.npm_config_global_prefix = config.globalPrefix 94 env.npm_config_local_prefix = config.localPrefix 95 if (cliConf.editor) { 96 env.EDITOR = cliConf.editor 97 } 98 99 // note: this doesn't afect the *current* node process, of course, since 100 // it's already started, but it does affect the options passed to scripts. 101 if (cliConf['node-options']) { 102 env.NODE_OPTIONS = cliConf['node-options'] 103 } 104 env.npm_execpath = config.npmBin 105 env.NODE = env.npm_node_execpath = config.execPath 106} 107 108module.exports = setEnvs 109