1// Parse a field, coercing it to the best type available. 2const typeDefs = require('./type-defs.js') 3const envReplace = require('./env-replace.js') 4const { resolve } = require('path') 5 6const { parse: umaskParse } = require('./umask.js') 7 8const parseField = (f, key, opts, listElement = false) => { 9 if (typeof f !== 'string' && !Array.isArray(f)) { 10 return f 11 } 12 13 const { platform, types, home, env } = opts 14 15 // type can be array or a single thing. coerce to array. 16 const typeList = new Set([].concat(types[key])) 17 const isPath = typeList.has(typeDefs.path.type) 18 const isBool = typeList.has(typeDefs.Boolean.type) 19 const isString = isPath || typeList.has(typeDefs.String.type) 20 const isUmask = typeList.has(typeDefs.Umask.type) 21 const isNumber = typeList.has(typeDefs.Number.type) 22 const isList = !listElement && typeList.has(Array) 23 const isDate = typeList.has(typeDefs.Date.type) 24 25 if (Array.isArray(f)) { 26 return !isList ? f : f.map(field => parseField(field, key, opts, true)) 27 } 28 29 // now we know it's a string 30 f = f.trim() 31 32 // list types get put in the environment separated by double-\n 33 // usually a single \n would suffice, but ca/cert configs can contain 34 // line breaks and multiple entries. 35 if (isList) { 36 return parseField(f.split('\n\n'), key, opts) 37 } 38 39 // --foo is like --foo=true for boolean types 40 if (isBool && !isString && f === '') { 41 return true 42 } 43 44 // string types can be the string 'true', 'false', etc. 45 // otherwise, parse these values out 46 if (!isString && !isPath && !isNumber) { 47 switch (f) { 48 case 'true': return true 49 case 'false': return false 50 case 'null': return null 51 case 'undefined': return undefined 52 } 53 } 54 55 f = envReplace(f, env) 56 57 if (isDate) { 58 return new Date(f) 59 } 60 61 if (isPath) { 62 const homePattern = platform === 'win32' ? /^~(\/|\\)/ : /^~\// 63 if (homePattern.test(f) && home) { 64 f = resolve(home, f.slice(2)) 65 } else { 66 f = resolve(f) 67 } 68 } 69 70 if (isUmask) { 71 try { 72 return umaskParse(f) 73 } catch (er) { 74 // let it warn later when we validate 75 return f 76 } 77 } 78 79 if (isNumber && !isNaN(f)) { 80 f = +f 81 } 82 83 return f 84} 85 86module.exports = parseField 87