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