codekingpro/portable-devtools
114k
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('node: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 