codekingpro/portable-devtools
114k
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 // TODO this may not be the best away to persist these
94 env.npm_config_global_prefix = config.globalPrefix
95 env.npm_config_local_prefix = config.localPrefix
96 if (cliConf.editor) {
97 env.EDITOR = cliConf.editor
98 }
99
100 // note: this doesn't affect the *current* node process, of course, since
101 // it's already started, but it does affect the options passed to scripts.
102 if (cliConf['node-options']) {
103 env.NODE_OPTIONS = cliConf['node-options']
104 }
105 // the node-gyp bin uses this so we always set it
106 env.npm_config_node_gyp = cliConf['node-gyp']
107 // this doesn't have a full definition so we manually export it here
108 env.npm_config_npm_version = cliConf['npm-version'] || 'unknown'
109 env.npm_execpath = config.npmBin
110 env.NODE = env.npm_node_execpath = config.execPath
111}
112
113module.exports = setEnvs
114 