• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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