Projekt

Obecné

Profil

Stáhnout (4.57 KB) Statistiky
| Větev: | Revize:
1
'use strict'
2

    
3
class FiggyPudding {
4
  constructor (specs, opts, providers) {
5
    this.__specs = specs || {}
6
    Object.keys(this.__specs).forEach(alias => {
7
      if (typeof this.__specs[alias] === 'string') {
8
        const key = this.__specs[alias]
9
        const realSpec = this.__specs[key]
10
        if (realSpec) {
11
          const aliasArr = realSpec.aliases || []
12
          aliasArr.push(alias, key)
13
          realSpec.aliases = [...(new Set(aliasArr))]
14
          this.__specs[alias] = realSpec
15
        } else {
16
          throw new Error(`Alias refers to invalid key: ${key} -> ${alias}`)
17
        }
18
      }
19
    })
20
    this.__opts = opts || {}
21
    this.__providers = reverse((providers).filter(
22
      x => x != null && typeof x === 'object'
23
    ))
24
    this.__isFiggyPudding = true
25
  }
26
  get (key) {
27
    return pudGet(this, key, true)
28
  }
29
  get [Symbol.toStringTag] () { return 'FiggyPudding' }
30
  forEach (fn, thisArg = this) {
31
    for (let [key, value] of this.entries()) {
32
      fn.call(thisArg, value, key, this)
33
    }
34
  }
35
  toJSON () {
36
    const obj = {}
37
    this.forEach((val, key) => {
38
      obj[key] = val
39
    })
40
    return obj
41
  }
42
  * entries (_matcher) {
43
    for (let key of Object.keys(this.__specs)) {
44
      yield [key, this.get(key)]
45
    }
46
    const matcher = _matcher || this.__opts.other
47
    if (matcher) {
48
      const seen = new Set()
49
      for (let p of this.__providers) {
50
        const iter = p.entries ? p.entries(matcher) : entries(p)
51
        for (let [key, val] of iter) {
52
          if (matcher(key) && !seen.has(key)) {
53
            seen.add(key)
54
            yield [key, val]
55
          }
56
        }
57
      }
58
    }
59
  }
60
  * [Symbol.iterator] () {
61
    for (let [key, value] of this.entries()) {
62
      yield [key, value]
63
    }
64
  }
65
  * keys () {
66
    for (let [key] of this.entries()) {
67
      yield key
68
    }
69
  }
70
  * values () {
71
    for (let [, value] of this.entries()) {
72
      yield value
73
    }
74
  }
75
  concat (...moreConfig) {
76
    return new Proxy(new FiggyPudding(
77
      this.__specs,
78
      this.__opts,
79
      reverse(this.__providers).concat(moreConfig)
80
    ), proxyHandler)
81
  }
82
}
83
try {
84
  const util = require('util')
85
  FiggyPudding.prototype[util.inspect.custom] = function (depth, opts) {
86
    return (
87
      this[Symbol.toStringTag] + ' '
88
    ) + util.inspect(this.toJSON(), opts)
89
  }
90
} catch (e) {}
91

    
92
function BadKeyError (key) {
93
  throw Object.assign(new Error(
94
    `invalid config key requested: ${key}`
95
  ), {code: 'EBADKEY'})
96
}
97

    
98
function pudGet (pud, key, validate) {
99
  let spec = pud.__specs[key]
100
  if (validate && !spec && (!pud.__opts.other || !pud.__opts.other(key))) {
101
    BadKeyError(key)
102
  } else {
103
    if (!spec) { spec = {} }
104
    let ret
105
    for (let p of pud.__providers) {
106
      ret = tryGet(key, p)
107
      if (ret === undefined && spec.aliases && spec.aliases.length) {
108
        for (let alias of spec.aliases) {
109
          if (alias === key) { continue }
110
          ret = tryGet(alias, p)
111
          if (ret !== undefined) {
112
            break
113
          }
114
        }
115
      }
116
      if (ret !== undefined) {
117
        break
118
      }
119
    }
120
    if (ret === undefined && spec.default !== undefined) {
121
      if (typeof spec.default === 'function') {
122
        return spec.default(pud)
123
      } else {
124
        return spec.default
125
      }
126
    } else {
127
      return ret
128
    }
129
  }
130
}
131

    
132
function tryGet (key, p) {
133
  let ret
134
  if (p.__isFiggyPudding) {
135
    ret = pudGet(p, key, false)
136
  } else if (typeof p.get === 'function') {
137
    ret = p.get(key)
138
  } else {
139
    ret = p[key]
140
  }
141
  return ret
142
}
143

    
144
const proxyHandler = {
145
  has (obj, prop) {
146
    return prop in obj.__specs && pudGet(obj, prop, false) !== undefined
147
  },
148
  ownKeys (obj) {
149
    return Object.keys(obj.__specs)
150
  },
151
  get (obj, prop) {
152
    if (
153
      typeof prop === 'symbol' ||
154
      prop.slice(0, 2) === '__' ||
155
      prop in FiggyPudding.prototype
156
    ) {
157
      return obj[prop]
158
    }
159
    return obj.get(prop)
160
  },
161
  set (obj, prop, value) {
162
    if (
163
      typeof prop === 'symbol' ||
164
      prop.slice(0, 2) === '__'
165
    ) {
166
      obj[prop] = value
167
      return true
168
    } else {
169
      throw new Error('figgyPudding options cannot be modified. Use .concat() instead.')
170
    }
171
  },
172
  deleteProperty () {
173
    throw new Error('figgyPudding options cannot be deleted. Use .concat() and shadow them instead.')
174
  }
175
}
176

    
177
module.exports = figgyPudding
178
function figgyPudding (specs, opts) {
179
  function factory (...providers) {
180
    return new Proxy(new FiggyPudding(
181
      specs,
182
      opts,
183
      providers
184
    ), proxyHandler)
185
  }
186
  return factory
187
}
188

    
189
function reverse (arr) {
190
  const ret = []
191
  arr.forEach(x => ret.unshift(x))
192
  return ret
193
}
194

    
195
function entries (obj) {
196
  return Object.keys(obj).map(k => [k, obj[k]])
197
}
(4-4/5)