UNPKG

3.79 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7/**
8 * Gets the value at path of object
9 * @param {object} obj object to query
10 * @param {string} path query path
11 * @returns {any} - if {@param path} requests element from array, then `undefined` will be returned
12 */
13const getProperty = (obj, path) => {
14 let name = path.split(".");
15 for (let i = 0; i < name.length - 1; i++) {
16 obj = obj[name[i]];
17 if (typeof obj !== "object" || !obj || Array.isArray(obj)) return;
18 }
19 return obj[name.pop()];
20};
21
22/**
23 * Sets the value at path of object. Stops execution, if {@param path} requests element from array to be set
24 * @param {object} obj object to query
25 * @param {string} path query path
26 * @param {any} value value to be set
27 * @returns {void}
28 */
29const setProperty = (obj, path, value) => {
30 let name = path.split(".");
31 for (let i = 0; i < name.length - 1; i++) {
32 if (typeof obj[name[i]] !== "object" && obj[name[i]] !== undefined) return;
33 if (Array.isArray(obj[name[i]])) return;
34 if (!obj[name[i]]) obj[name[i]] = {};
35 obj = obj[name[i]];
36 }
37 obj[name.pop()] = value;
38};
39
40/**
41 * @typedef {'call' | 'make' | 'append'} ConfigType
42 */
43/**
44 * @typedef {(options: object) => any} MakeConfigHandler
45 */
46/**
47 * @typedef {(value: any, options: object) => any} CallConfigHandler
48 */
49/**
50 * @typedef {any[]} AppendConfigValues
51 */
52
53class OptionsDefaulter {
54 constructor() {
55 /**
56 * Stores default options settings or functions for computing them
57 */
58 this.defaults = {};
59 /**
60 * Stores configuration for options
61 * @type {{[key: string]: ConfigType}}
62 */
63 this.config = {};
64 }
65
66 /**
67 * Enhancing {@param options} with default values
68 * @param {object} options provided options
69 * @returns {object} - enhanced options
70 * @throws {Error} - will throw error, if configuration value is other then `undefined` or {@link ConfigType}
71 */
72 process(options) {
73 options = Object.assign({}, options);
74 for (let name in this.defaults) {
75 switch (this.config[name]) {
76 /**
77 * If {@link ConfigType} doesn't specified and current value is `undefined`, then default value will be assigned
78 */
79 case undefined:
80 if (getProperty(options, name) === undefined) {
81 setProperty(options, name, this.defaults[name]);
82 }
83 break;
84 /**
85 * Assign result of {@link CallConfigHandler}
86 */
87 case "call":
88 setProperty(
89 options,
90 name,
91 this.defaults[name].call(this, getProperty(options, name), options)
92 );
93 break;
94 /**
95 * Assign result of {@link MakeConfigHandler}, if current value is `undefined`
96 */
97 case "make":
98 if (getProperty(options, name) === undefined) {
99 setProperty(options, name, this.defaults[name].call(this, options));
100 }
101 break;
102 /**
103 * Adding {@link AppendConfigValues} at the end of the current array
104 */
105 case "append": {
106 let oldValue = getProperty(options, name);
107 if (!Array.isArray(oldValue)) {
108 oldValue = [];
109 }
110 oldValue.push(...this.defaults[name]);
111 setProperty(options, name, oldValue);
112 break;
113 }
114 default:
115 throw new Error(
116 "OptionsDefaulter cannot process " + this.config[name]
117 );
118 }
119 }
120 return options;
121 }
122
123 /**
124 * Builds up default values
125 * @param {string} name option path
126 * @param {ConfigType | any} config if {@param def} is provided, then only {@link ConfigType} is allowed
127 * @param {MakeConfigHandler | CallConfigHandler | AppendConfigValues} [def] defaults
128 * @returns {void}
129 */
130 set(name, config, def) {
131 if (def !== undefined) {
132 this.defaults[name] = def;
133 this.config[name] = config;
134 } else {
135 this.defaults[name] = config;
136 delete this.config[name];
137 }
138 }
139}
140
141module.exports = OptionsDefaulter;