1# Note: pending imminent deprecation
2
3**This module will be deprecated once npm v7 is released.  Please do not rely
4on it more than absolutely necessary (ie, only if you are depending on
5it for use with npm v6 internal dependencies).**
6
7----
8
9# figgy-pudding [![npm version](https://img.shields.io/npm/v/figgy-pudding.svg)](https://npm.im/figgy-pudding) [![license](https://img.shields.io/npm/l/figgy-pudding.svg)](https://npm.im/figgy-pudding) [![Travis](https://img.shields.io/travis/npm/figgy-pudding.svg)](https://travis-ci.org/npm/figgy-pudding) [![Coverage Status](https://coveralls.io/repos/github/npm/figgy-pudding/badge.svg?branch=latest)](https://coveralls.io/github/npm/figgy-pudding?branch=latest)
10
11[`figgy-pudding`](https://github.com/npm/figgy-pudding) is a small JavaScript
12library for managing and composing cascading options objects -- hiding what
13needs to be hidden from each layer, without having to do a lot of manual munging
14and passing of options.
15
16### The God Object is Dead!
17### Now Bring Us Some Figgy Pudding!
18
19## Install
20
21`$ npm install figgy-pudding`
22
23## Table of Contents
24
25* [Example](#example)
26* [Features](#features)
27* [API](#api)
28  * [`figgyPudding(spec)`](#figgy-pudding)
29  * [`PuddingFactory(values)`](#pudding-factory)
30    * [`opts.get()`](#opts-get)
31    * [`opts.concat()`](#opts-concat)
32    * [`opts.toJSON()`](#opts-to-json)
33    * [`opts.forEach()`](#opts-for-each)
34    * [`opts[Symbol.iterator]()`](#opts-symbol-iterator)
35    * [`opts.entries()`](#opts-entries)
36    * [`opts.keys()`](#opts-keys)
37    * [`opts.value()`](#opts-values)
38
39### Example
40
41```javascript
42// print-package.js
43const fetch = require('./fetch.js')
44const puddin = require('figgy-pudding')
45
46const PrintOpts = puddin({
47  json: { default: false }
48})
49
50async function printPkg (name, opts) {
51  // Expected pattern is to call this in every interface function. If `opts` is
52  // not passed in, it will automatically create an (empty) object for it.
53  opts = PrintOpts(opts)
54  const uri = `https://registry.npmjs.com/${name}`
55  const res = await fetch(uri, opts.concat({
56    // Add or override any passed-in configs and pass them down.
57    log: customLogger
58  }))
59  // The following would throw an error, because it's not in PrintOpts:
60  // console.log(opts.log)
61  if (opts.json) {
62    return res.json()
63  } else {
64    return res.text()
65  }
66}
67
68console.log(await printPkg('figgy', {
69  // Pass in *all* configs at the toplevel, as a regular object.
70  json: true,
71  cache: './tmp-cache'
72}))
73```
74
75```javascript
76// fetch.js
77const puddin = require('figgy-pudding')
78
79const FetchOpts = puddin({
80  log: { default: require('npmlog') },
81  cache: {}
82})
83
84module.exports = async function (..., opts) {
85  opts = FetchOpts(opts)
86}
87```
88
89### Features
90
91* hide options from layer that didn't ask for it
92* shared multi-layer options
93* make sure `opts` argument is available
94* transparent key access like normal keys, through a Proxy. No need for`.get()`!
95* default values
96* key aliases
97* arbitrary key filter functions
98* key/value iteration
99* serialization
100* 100% test coverage using `tap --100`
101
102### API
103
104#### <a name="figgy-pudding"></a> `> figgyPudding({ key: { default: val } | String }, [opts]) -> PuddingFactory`
105
106Defines an Options constructor that can be used to collect only the needed
107options.
108
109An optional `default` property for specs can be used to specify default values
110if nothing was passed in.
111
112If the value for a spec is a string, it will be treated as an alias to that
113other key.
114
115##### Example
116
117```javascript
118const MyAppOpts = figgyPudding({
119  lg: 'log',
120  log: {
121    default: () => require('npmlog')
122  },
123  cache: {}
124})
125```
126
127#### <a name="pudding-factory"></a> `> PuddingFactory(...providers) -> FiggyPudding{}`
128
129Instantiates an options object defined by `figgyPudding()`, which uses
130`providers`, in order, to find requested properties.
131
132Each provider can be either a plain object, a `Map`-like object (that is, one
133with a `.get()` method) or another figgyPudding `Opts` object.
134
135When nesting `Opts` objects, their properties will not become available to the
136new object, but any further nested `Opts` that reference that property _will_ be
137able to read from their grandparent, as long as they define that key. Default
138values for nested `Opts` parents will be used, if found.
139
140##### Example
141
142```javascript
143const ReqOpts = figgyPudding({
144  follow: {}
145})
146
147const opts = ReqOpts({
148  follow: true,
149  log: require('npmlog')
150})
151
152opts.follow // => true
153opts.log // => Error: ReqOpts does not define `log`
154
155const MoreOpts = figgyPudding({
156  log: {}
157})
158MoreOpts(opts).log // => npmlog object (passed in from original plain obj)
159MoreOpts(opts).follow // => Error: MoreOpts does not define `follow`
160```
161
162#### <a name="opts-get"></a> `> opts.get(key) -> Value`
163
164Gets a value from the options object.
165
166##### Example
167
168```js
169const opts = MyOpts(config)
170opts.get('foo') // value of `foo`
171opts.foo // Proxy-based access through `.get()`
172```
173
174#### <a name="opts-concat"></a> `> opts.concat(...moreProviders) -> FiggyPudding{}`
175
176Creates a new opts object of the same type as `opts` with additional providers.
177Providers further to the right shadow providers to the left, with properties in
178the original `opts` being shadows by the new providers.
179
180##### Example
181
182```js
183const opts = MyOpts({x: 1})
184opts.get('x') // 1
185opts.concat({x: 2}).get('x') // 2
186opts.get('x') // 1 (original opts object left intact)
187```
188
189#### <a name="opts-to-json"></a> `> opts.toJSON() -> Value`
190
191Converts `opts` to a plain, JSON-stringifiable JavaScript value. Used internally
192by JavaScript to get `JSON.stringify()` working.
193
194Only keys that are readable by the current pudding type will be serialized.
195
196##### Example
197
198```js
199const opts = MyOpts({x: 1})
200opts.toJSON() // {x: 1}
201JSON.stringify(opts) // '{"x":1}'
202```
203
204#### <a name="opts-for-each"></a> `> opts.forEach((value, key, opts) => {}, thisArg) -> undefined`
205
206Iterates over the values of `opts`, limited to the keys readable by the current
207pudding type. `thisArg` will be used to set the `this` argument when calling the
208`fn`.
209
210##### Example
211
212```js
213const opts = MyOpts({x: 1, y: 2})
214opts.forEach((value, key) => console.log(key, '=', value))
215```
216
217#### <a name="opts-entries"></a> `> opts.entries() -> Iterator<[[key, value], ...]>`
218
219Returns an iterator that iterates over the keys and values in `opts`, limited to
220the keys readable by the current pudding type. Each iteration returns an array
221of `[key, value]`.
222
223##### Example
224
225```js
226const opts = MyOpts({x: 1, y: 2})
227[...opts({x: 1, y: 2}).entries()] // [['x', 1], ['y', 2]]
228```
229
230#### <a name="opts-symbol-iterator"></a> `> opts[Symbol.iterator]() -> Iterator<[[key, value], ...]>`
231
232Returns an iterator that iterates over the keys and values in `opts`, limited to
233the keys readable by the current pudding type. Each iteration returns an array
234of `[key, value]`. Makes puddings work natively with JS iteration mechanisms.
235
236##### Example
237
238```js
239const opts = MyOpts({x: 1, y: 2})
240[...opts({x: 1, y: 2})] // [['x', 1], ['y', 2]]
241for (let [key, value] of opts({x: 1, y: 2})) {
242  console.log(key, '=', value)
243}
244```
245
246#### <a name="opts-keys"></a> `> opts.keys() -> Iterator<[key, ...]>`
247
248Returns an iterator that iterates over the keys in `opts`, limited to the keys
249readable by the current pudding type.
250
251##### Example
252
253```js
254const opts = MyOpts({x: 1, y: 2})
255[...opts({x: 1, y: 2}).keys()] // ['x', 'y']
256```
257
258#### <a name="opts-values"></a> `> opts.values() -> Iterator<[value, ...]>`
259
260Returns an iterator that iterates over the values in `opts`, limited to the keys
261readable by the current pudding type.
262
263##### Example
264'
265```js
266const opts = MyOpts({x: 1, y: 2})
267[...opts({x: 1, y: 2}).values()] // [1, 2]
268```
269