@lumjs/core 1.6.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +30 -2
- package/lib/modulebuilder.js +45 -14
- package/lib/obj/copyall.js +30 -3
- package/lib/obj/copyprops.js +312 -47
- package/lib/obj/index.js +2 -2
- package/lib/obj/merge.js +40 -32
- package/lib/types/def.js +1 -4
- package/lib/types/isa.js +169 -172
- package/lib/types/needs.js +29 -9
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -51,8 +51,36 @@ has('lazy', {value: types.lazy});
|
|
|
51
51
|
|
|
52
52
|
// The module builder itself.
|
|
53
53
|
has('ModuleBuilder', {value: Builder});
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Return a set of functions for building a module.
|
|
57
|
+
*
|
|
58
|
+
* @name module:@lumjs/core.buildModule
|
|
59
|
+
* @function
|
|
60
|
+
* @see module:@lumjs/core/modulebuilder.build
|
|
61
|
+
*/
|
|
62
|
+
has('buildModule',
|
|
63
|
+
{
|
|
64
|
+
value: function(m,o)
|
|
65
|
+
{
|
|
66
|
+
return Builder.build(m,o);
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Create a new `ModuleBuilder` instance.
|
|
72
|
+
*
|
|
73
|
+
* @name module:@lumjs/core.newBuilder
|
|
74
|
+
* @function
|
|
75
|
+
* @see module:@lumjs/core/modulebuilder.new
|
|
76
|
+
*/
|
|
77
|
+
has('newBuilder',
|
|
78
|
+
{
|
|
79
|
+
value: function(m, o)
|
|
80
|
+
{
|
|
81
|
+
return Builder.new(m,o);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
56
84
|
|
|
57
85
|
/**
|
|
58
86
|
* Array utility functions «Lazy»
|
package/lib/modulebuilder.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
function clone(obj)
|
|
2
|
+
const
|
|
5
3
|
{
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
4
|
+
S,F,B,
|
|
5
|
+
isObj,needObj,needType,
|
|
6
|
+
def,lazy
|
|
7
|
+
} = require('./types');
|
|
8
|
+
|
|
9
|
+
const clone = require('./obj/copyall').duplicateOne;
|
|
13
10
|
|
|
14
11
|
// Methods we want to export in the functional API.
|
|
15
|
-
const BUILD_METHODS = ['has', 'can', 'from'];
|
|
12
|
+
const BUILD_METHODS = ['has', 'can', 'from', 'set'];
|
|
16
13
|
|
|
17
14
|
/**
|
|
18
15
|
* A class to make building modules easier.
|
|
@@ -76,6 +73,11 @@ class ModuleBuilder
|
|
|
76
73
|
// Get a descriptor for a module export.
|
|
77
74
|
requireDescriptor(name, conf={})
|
|
78
75
|
{
|
|
76
|
+
if (typeof conf.getter === F || typeof conf.setter === F)
|
|
77
|
+
{ // It's an accessor-style descriptor already.
|
|
78
|
+
return conf;
|
|
79
|
+
}
|
|
80
|
+
|
|
79
81
|
let value = conf.value;
|
|
80
82
|
|
|
81
83
|
if (value === undefined)
|
|
@@ -268,6 +270,33 @@ class ModuleBuilder
|
|
|
268
270
|
return this;
|
|
269
271
|
}
|
|
270
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Define an exported property in our module.
|
|
275
|
+
*
|
|
276
|
+
* This is literally just a wrapper around the
|
|
277
|
+
* [def()]{@link module:@lumjs/core/types.def} method,
|
|
278
|
+
* that passes `this.module.exports` as the `obj` parameter.
|
|
279
|
+
*
|
|
280
|
+
* @param {*} name - See `def()` for details.
|
|
281
|
+
* @param {*} value - See `def()` for details.
|
|
282
|
+
* @param {*} opts - See `def()` for details.
|
|
283
|
+
* @returns {(object|function)} What is returned may vary:
|
|
284
|
+
*
|
|
285
|
+
* If the `def()` returns a *bound* copy of itself, we'll return that
|
|
286
|
+
* bound function directly.
|
|
287
|
+
*
|
|
288
|
+
* In any other case, we return `this` ModuleBuilder instance.
|
|
289
|
+
*/
|
|
290
|
+
set(name, value, opts)
|
|
291
|
+
{
|
|
292
|
+
const retval = def(this.module.exports, name, value, opts);
|
|
293
|
+
if (typeof retval === F && isObj(retval.$this) && retval.$this.bound === retval)
|
|
294
|
+
{ // A bound copy was returned, we're going to return it directly.
|
|
295
|
+
return retval;
|
|
296
|
+
}
|
|
297
|
+
return this;
|
|
298
|
+
}
|
|
299
|
+
|
|
271
300
|
/**
|
|
272
301
|
* Create a functional API for the ModuleBuilder class.
|
|
273
302
|
*
|
|
@@ -278,12 +307,14 @@ class ModuleBuilder
|
|
|
278
307
|
* They each have a special `builder` property which is a reference
|
|
279
308
|
* to the underlying `ModuleBuilder` instance.
|
|
280
309
|
*
|
|
281
|
-
* There's also a `builder` property in the exported function list
|
|
310
|
+
* There's also a `builder` property in the exported function list,
|
|
311
|
+
* as well as a copy of the `def()` method for quick exporting.
|
|
282
312
|
*
|
|
283
313
|
* Example usage:
|
|
284
314
|
*
|
|
285
315
|
* ```js
|
|
286
|
-
* const {has,can,from}
|
|
316
|
+
* const {has,can,from,set}
|
|
317
|
+
* = require('@lumjs/core').ModuleBuilder.build(module);
|
|
287
318
|
* // or the shortcut: require('@lumjs/core).buildModule(module);
|
|
288
319
|
*
|
|
289
320
|
* // exports.foo = require('./foo');
|
|
@@ -301,7 +332,7 @@ class ModuleBuilder
|
|
|
301
332
|
static build(targetModule, opts)
|
|
302
333
|
{
|
|
303
334
|
const builder = new this(targetModule, opts);
|
|
304
|
-
const funcs = {builder};
|
|
335
|
+
const funcs = {builder, def};
|
|
305
336
|
for (const name of BUILD_METHODS)
|
|
306
337
|
{
|
|
307
338
|
const func = function()
|
package/lib/obj/copyall.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* This is a 'dumb' copy method.
|
|
3
3
|
*
|
|
4
|
-
* It
|
|
5
|
-
*
|
|
4
|
+
* It only copies enumerable properties, does no type checking,
|
|
5
|
+
* and has no qualms about overwriting properties.
|
|
6
|
+
*
|
|
7
|
+
* Use `copyProps`, or `mergeNested` for more robust versions.
|
|
6
8
|
*
|
|
7
9
|
* @alias module:@lumjs/core/obj.copyAll
|
|
8
10
|
*/
|
|
@@ -17,5 +19,30 @@ function copyAll(target, ...sources)
|
|
|
17
19
|
}
|
|
18
20
|
return target;
|
|
19
21
|
}
|
|
22
|
+
exports.copyAll = copyAll;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Make a copy of a single object using `copyAll`.
|
|
26
|
+
*
|
|
27
|
+
* Use `clone` for a more robust version.
|
|
28
|
+
*
|
|
29
|
+
* Alias: `copyAll.clone`
|
|
30
|
+
*
|
|
31
|
+
* @alias module:@lumjs/core/obj.duplicateOne
|
|
32
|
+
* @param {object} obj - The object to duplicate.
|
|
33
|
+
* @returns {object} A clone of the object.
|
|
34
|
+
*/
|
|
35
|
+
exports.duplicateOne = copyAll.clone = obj => copyAll({}, obj);
|
|
20
36
|
|
|
21
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Make a new object containing properties from other objects using `copyAll`.
|
|
39
|
+
*
|
|
40
|
+
* Use `copyProps.into({}).from(...sources)` for a more robust version.
|
|
41
|
+
*
|
|
42
|
+
* Alias: `copyAll.clone`
|
|
43
|
+
*
|
|
44
|
+
* @alias module:@lumjs/core/obj.duplicateOne
|
|
45
|
+
* @param {object} obj - The object to duplicate.
|
|
46
|
+
* @returns {object} A clone of the object.
|
|
47
|
+
*/
|
|
48
|
+
exports.duplicateAll = copyAll.duplicate = () => copyAll({}, ...arguments);
|
package/lib/obj/copyprops.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
// Get some constants
|
|
3
|
-
const {B,isObj,
|
|
3
|
+
const {B,N,F,isObj,isArray,needObj,def} = require('../types');
|
|
4
|
+
const {duplicateOne: clone} = require('./copyall');
|
|
5
|
+
|
|
6
|
+
const RECURSE_NONE = 0;
|
|
7
|
+
const RECURSE_ALL = -1;
|
|
8
|
+
const RECURSE_LIST = -2;
|
|
4
9
|
|
|
5
10
|
/**
|
|
6
11
|
* Copy properties from one object to another.
|
|
@@ -8,61 +13,143 @@ const {B,isObj,isComplex,isArray,def: defProp} = require('../types');
|
|
|
8
13
|
* @param {(object|function)} source - The object to copy properties from.
|
|
9
14
|
* @param {(object|function)} target - The target to copy properties to.
|
|
10
15
|
*
|
|
11
|
-
* @param {object} [
|
|
12
|
-
* @param {boolean} [
|
|
13
|
-
* @param {boolean} [
|
|
14
|
-
* @param {Array} [
|
|
15
|
-
* @param {
|
|
16
|
-
* @param {
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* @param {object} [opts] Options for how to copy properties.
|
|
17
|
+
* @param {boolean} [opts.default=true] Copy only enumerable properties.
|
|
18
|
+
* @param {boolean} [opts.all=false] Copy ALL object properties.
|
|
19
|
+
* @param {Array} [opts.props] A list of specific properties to copy.
|
|
20
|
+
* @param {Array} [opts.exclude] A list of properties NOT to copy.
|
|
21
|
+
* @param {object} [opts.overrides] Descriptor overrides for properties.
|
|
22
|
+
*
|
|
23
|
+
* The object is considered a map, where each *key* is the name of the
|
|
24
|
+
* property, and the value should be an `object` containing any valid
|
|
25
|
+
* descriptor properties.
|
|
26
|
+
*
|
|
27
|
+
* If `opts.default` is explicitly set to `false` and `opts.overrides`
|
|
28
|
+
* is set, then not only will it be used as a list of overrides,
|
|
29
|
+
* but only the properties specified in it will be copied.
|
|
30
|
+
*
|
|
31
|
+
* @param {*} [opts.overwrite=false] Overwrite existing properties.
|
|
32
|
+
*
|
|
33
|
+
* If this is a `boolean` value, it will allow or disallow overwriting
|
|
34
|
+
* of any and all properties in the target object.
|
|
20
35
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
36
|
+
* If this is an `object`, it can be an `Array` of property names to allow
|
|
37
|
+
* to be overwritten, or a *map* of property name to a `boolean` indicating
|
|
38
|
+
* if that property can be overwritten or not.
|
|
39
|
+
*
|
|
40
|
+
* Finally, if this is a `function` it'll be passed the property name and
|
|
41
|
+
* must return a boolean indicating if overwriting is allowed.
|
|
42
|
+
*
|
|
43
|
+
* @param {number} [opts.recursive=0] Enable recursive copying of objects.
|
|
44
|
+
*
|
|
45
|
+
* If it is `0` (also `copyProps.RECURSE_NONE`) then no recursion is done.
|
|
46
|
+
* In this case, the regular assignment rules (including `opts.overwrite`)
|
|
47
|
+
* will be used regardless of the property type.
|
|
48
|
+
* This is the default value.
|
|
49
|
+
*
|
|
50
|
+
* If this is *above zero*, it's the recursion depth for `object` properties.
|
|
51
|
+
*
|
|
52
|
+
* If this is *below zero*, then it should be one of the constant values:
|
|
53
|
+
*
|
|
54
|
+
* | Contant | Value | Description |
|
|
55
|
+
* | ------------------------ | ----- | ------------------------------------ |
|
|
56
|
+
* | `copyProps.RECURSE_ALL` | `-1` | Recurse to an *unlimited* depth. |
|
|
57
|
+
* | `copyProps.RECURSE_LIST` | `-2` | Recurse `opts.recurseOpts` props. |
|
|
58
|
+
*
|
|
59
|
+
* @param {object} [opts.recurseOpts] Options for recursive properties.
|
|
60
|
+
*
|
|
61
|
+
* If `opts.recursive` is not `0` then `opts.recurseOpts` can be a map of
|
|
62
|
+
* property names to further objects, which will be used as the `opts` for
|
|
63
|
+
* that property when calling `copyProps()` recursively.
|
|
64
|
+
*
|
|
65
|
+
* So you could have nested `opts.recurseOpts` values if required.
|
|
66
|
+
*
|
|
67
|
+
* The `recursive` property will automatically be added to the individual
|
|
68
|
+
* `recurseOpts`, automatically applying the correct value.
|
|
69
|
+
*
|
|
70
|
+
* The `opts.recurseOpts` option has an extra-special meaning if `opts.recurse`
|
|
71
|
+
* is set to `RECURSE_LIST`, as then *only* the properties with options defined
|
|
72
|
+
* in `opts.recurseOpts` will be recursed. The rest will simply be copied.
|
|
24
73
|
*
|
|
25
74
|
* @returns {object} The `target` object.
|
|
26
75
|
* @alias module:@lumjs/core/obj.copyProps
|
|
27
76
|
*/
|
|
28
|
-
function copyProps(source, target,
|
|
77
|
+
function copyProps(source, target, opts={})
|
|
29
78
|
{
|
|
30
79
|
//console.debug("copyProps", source, target, propOpts);
|
|
31
|
-
|
|
80
|
+
needObj(source, true, 'source must be an object or function');
|
|
81
|
+
needObj(target, true, 'target must be an object or function');
|
|
82
|
+
needObj(opts, false, 'opts must be an object');
|
|
83
|
+
|
|
84
|
+
const useDefaults = opts.default ?? true;
|
|
85
|
+
const overrides = opts.overrides ?? {};
|
|
86
|
+
|
|
87
|
+
let recursive;
|
|
88
|
+
if (typeof opts.recursive === N)
|
|
89
|
+
{
|
|
90
|
+
recursive = opts.recursive;
|
|
91
|
+
}
|
|
92
|
+
else if (typeof opts.recursive === B)
|
|
32
93
|
{
|
|
33
|
-
|
|
94
|
+
recursive = opts.recursive ? RECURSE_ALL : RECURSE_NONE;
|
|
95
|
+
}
|
|
96
|
+
else
|
|
97
|
+
{
|
|
98
|
+
recursive = RECURSE_NONE;
|
|
34
99
|
}
|
|
35
100
|
|
|
36
|
-
|
|
37
|
-
|
|
101
|
+
let recurseCache, recurseOpts;
|
|
102
|
+
if (recursive !== RECURSE_NONE)
|
|
103
|
+
{
|
|
104
|
+
recurseCache = opts.recurseCache ?? [];
|
|
105
|
+
recurseOpts = opts.recurseOpts ?? {};
|
|
106
|
+
}
|
|
38
107
|
|
|
39
|
-
|
|
40
|
-
|
|
108
|
+
let overwrites;
|
|
109
|
+
if (typeof opts.overwrite === F)
|
|
110
|
+
{ // A custom function.
|
|
111
|
+
overwrites = opts.overwrite;
|
|
112
|
+
}
|
|
113
|
+
else if (isObj(opts.overwrite))
|
|
114
|
+
{ // An object may be an array or a map.
|
|
115
|
+
if (isArray(opts.overwrite))
|
|
116
|
+
{ // A flat array of properties to overwrite.
|
|
117
|
+
overwrites = prop => opts.overwrite.includes(prop);
|
|
118
|
+
}
|
|
119
|
+
else
|
|
120
|
+
{ // A map of properties to overwrite.
|
|
121
|
+
overwrites = prop => opts.overwrite[prop] ?? false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else
|
|
125
|
+
{ // The only other values should be boolean.
|
|
126
|
+
overwrites = () => opts.overwrite ?? false;
|
|
127
|
+
}
|
|
41
128
|
|
|
42
|
-
const exclude = isArray(
|
|
129
|
+
const exclude = isArray(opts.exclude) ? opts.exclude : null;
|
|
43
130
|
|
|
44
131
|
let propDefs;
|
|
45
132
|
|
|
46
|
-
if (
|
|
133
|
+
if (isArray(opts.props))
|
|
47
134
|
{
|
|
48
|
-
propDefs =
|
|
135
|
+
propDefs = opts.props;
|
|
49
136
|
}
|
|
50
|
-
else if (
|
|
137
|
+
else if (opts.all)
|
|
51
138
|
{
|
|
52
139
|
propDefs = Object.getOwnPropertyNames(source);
|
|
53
140
|
}
|
|
54
|
-
else if (
|
|
141
|
+
else if (useDefaults)
|
|
55
142
|
{
|
|
56
143
|
propDefs = Object.keys(source);
|
|
57
144
|
}
|
|
58
|
-
else
|
|
145
|
+
else
|
|
59
146
|
{
|
|
60
|
-
propDefs = Object.keys(
|
|
147
|
+
propDefs = Object.keys(overrides);
|
|
61
148
|
}
|
|
62
149
|
|
|
63
150
|
if (!propDefs)
|
|
64
151
|
{
|
|
65
|
-
console.error("Could not determine properties to copy",
|
|
152
|
+
console.error("Could not determine properties to copy", opts);
|
|
66
153
|
return;
|
|
67
154
|
}
|
|
68
155
|
|
|
@@ -71,34 +158,57 @@ function copyProps(source, target, propOpts)
|
|
|
71
158
|
{
|
|
72
159
|
//console.debug(" @prop:", prop);
|
|
73
160
|
if (exclude && exclude.indexOf(prop) !== -1)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
let overwrite = false;
|
|
77
|
-
if (typeof defOverwrite === B)
|
|
78
|
-
{
|
|
79
|
-
overwrite = defOverwrite;
|
|
80
|
-
}
|
|
81
|
-
else if (isObj(defOverwrite) && typeof defOverwrite[prop] === B)
|
|
82
|
-
{
|
|
83
|
-
overwrite = defOverwrite[prop];
|
|
161
|
+
{ // Excluded property.
|
|
162
|
+
continue;
|
|
84
163
|
}
|
|
85
164
|
|
|
86
|
-
|
|
165
|
+
let desc = Object.getOwnPropertyDescriptor(source, prop);
|
|
87
166
|
//console.debug(" @desc:", def);
|
|
88
|
-
if (
|
|
167
|
+
if (desc === undefined)
|
|
168
|
+
{ // A non-existent property.
|
|
169
|
+
continue; // Invalid property.
|
|
170
|
+
}
|
|
89
171
|
|
|
90
|
-
if (isObj(
|
|
91
|
-
{
|
|
92
|
-
|
|
172
|
+
if (isObj(overrides[prop]))
|
|
173
|
+
{ // Overriding descriptor properties.
|
|
174
|
+
desc = clone(desc);
|
|
175
|
+
for (const key in overrides[prop])
|
|
93
176
|
{
|
|
94
|
-
const val =
|
|
95
|
-
|
|
177
|
+
const val = overrides[prop][key];
|
|
178
|
+
desc[key] = val;
|
|
96
179
|
}
|
|
97
180
|
}
|
|
98
181
|
|
|
99
|
-
|
|
182
|
+
let overwrite = overwrites(prop);
|
|
183
|
+
|
|
184
|
+
if (recursive !== 0
|
|
185
|
+
&& (recursive !== RECURSE_LIST || isObj(recurseOpts[prop]))
|
|
186
|
+
&& isObj(desc.value)
|
|
187
|
+
&& isObj(target[prop])
|
|
188
|
+
&& !recurseCache.includes(desc.value)
|
|
189
|
+
&& !recurseCache.includes(target[prop]))
|
|
190
|
+
{ // Recursive mode is enabled, so we're going to go deeper.
|
|
191
|
+
recurseCache.push(decs.value);
|
|
192
|
+
if (desc.value !== target[prop])
|
|
193
|
+
{ // They're not the same literal object already.
|
|
194
|
+
recurseCache.push(target[prop]);
|
|
195
|
+
const ropts
|
|
196
|
+
= isObj(recurseOpts[prop])
|
|
197
|
+
? clone(recurseOpts[prop])
|
|
198
|
+
: {overwrite};
|
|
199
|
+
// Always set the cache.
|
|
200
|
+
ropts.recurseCache = recurseCache;
|
|
201
|
+
if (typeof ropts.recursive !== N)
|
|
202
|
+
{ // Set the recursive option.
|
|
203
|
+
ropts.recursive = (recursive > 0) ? recursive - 1 : recursive;
|
|
204
|
+
}
|
|
205
|
+
// Okay, we're ready, let's recurse now!
|
|
206
|
+
copyProps(desc.value, target[prop], ropts);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else if (overwrite || target[prop] === undefined)
|
|
100
210
|
{ // Property doesn't already exist, let's add it.
|
|
101
|
-
|
|
211
|
+
def(target, prop, desc);
|
|
102
212
|
}
|
|
103
213
|
}
|
|
104
214
|
|
|
@@ -107,4 +217,159 @@ function copyProps(source, target, propOpts)
|
|
|
107
217
|
return target;
|
|
108
218
|
} // copyProps()
|
|
109
219
|
|
|
220
|
+
def(copyProps, 'RECURSE_NONE', RECURSE_NONE);
|
|
221
|
+
def(copyProps, 'RECURSE_ALL', RECURSE_ALL);
|
|
222
|
+
def(copyProps, 'RECURSE_LIST', RECURSE_LIST);
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* A class providing a declarative `copyProps()` API,
|
|
226
|
+
* which makes it easy to copy one or more sources,
|
|
227
|
+
* into one or more targets.
|
|
228
|
+
*
|
|
229
|
+
* This class is not directly accessible, and instead is available
|
|
230
|
+
* via some special sub-methods of `copyProps`; examples:
|
|
231
|
+
*
|
|
232
|
+
* ```js
|
|
233
|
+
* // Get a copy of the `copyProps` function.
|
|
234
|
+
* const cp = require('@lumjs/core').obj.copyProps;
|
|
235
|
+
*
|
|
236
|
+
* // Starting with the target:
|
|
237
|
+
* cp.into(targetObj).given({all: true}).from(source1, source2);
|
|
238
|
+
*
|
|
239
|
+
* // Starting with the sources:
|
|
240
|
+
* cp.from(sourceObj).given({exclude: ['dontCopy']}).into(target1, target2);
|
|
241
|
+
*
|
|
242
|
+
* // Starting with the options:
|
|
243
|
+
* cp.given({recursive: cp.RECURSE_ALL}).from(source1, source2).into(target1, target2);
|
|
244
|
+
*
|
|
245
|
+
*
|
|
246
|
+
* ```
|
|
247
|
+
*
|
|
248
|
+
* @alias module:@lumjs/core/obj~CopyProps
|
|
249
|
+
*/
|
|
250
|
+
class $CopyProps
|
|
251
|
+
{
|
|
252
|
+
constructor(opts={})
|
|
253
|
+
{
|
|
254
|
+
this.opts = opts;
|
|
255
|
+
this.sources = [];
|
|
256
|
+
this.targets = [];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Set a single option.
|
|
261
|
+
* @param {string} name - The option name
|
|
262
|
+
* @param {*} value - The option value
|
|
263
|
+
* @returns {object} `this`
|
|
264
|
+
*/
|
|
265
|
+
set(name, value)
|
|
266
|
+
{
|
|
267
|
+
this.opts[name] = value;
|
|
268
|
+
return this;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Set all of the options.
|
|
273
|
+
*
|
|
274
|
+
* This replaces the currently set options entirely.
|
|
275
|
+
*
|
|
276
|
+
* @param {object} opts - The options to set.
|
|
277
|
+
* @returns {object} `this`
|
|
278
|
+
*/
|
|
279
|
+
given(opts)
|
|
280
|
+
{
|
|
281
|
+
needObj(opts);
|
|
282
|
+
this.opts = opts;
|
|
283
|
+
return this;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Specify the `targets` to copy properties into.
|
|
288
|
+
*
|
|
289
|
+
* @param {...object} [targets] The target objects
|
|
290
|
+
*
|
|
291
|
+
* If `this.sources` has objects in it already,
|
|
292
|
+
* then we'll run `copyProps()` for each of the
|
|
293
|
+
* sources into each of the `targets`.
|
|
294
|
+
*
|
|
295
|
+
* If `this.sources` is empty, then this will set
|
|
296
|
+
* `this.target` to the specified value.
|
|
297
|
+
*
|
|
298
|
+
* You can specify no sources at all to clear the
|
|
299
|
+
* currently set `this.targets` value.
|
|
300
|
+
*
|
|
301
|
+
* @returns {object} `this`
|
|
302
|
+
*/
|
|
303
|
+
into(...targets)
|
|
304
|
+
{
|
|
305
|
+
if (this.sources.length > 0 && targets.length > 0)
|
|
306
|
+
{
|
|
307
|
+
this.$run(this.sources, targets);
|
|
308
|
+
}
|
|
309
|
+
else
|
|
310
|
+
{
|
|
311
|
+
this.targets = targets;
|
|
312
|
+
}
|
|
313
|
+
return this;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Specify the `sources` to copy properties from.
|
|
318
|
+
*
|
|
319
|
+
* @param {...object} [sources] The source objects.
|
|
320
|
+
*
|
|
321
|
+
* If `this.targets` has objects in it already,
|
|
322
|
+
* then we'll run `copyProps()` for each of the
|
|
323
|
+
* `sources` into each of the targets.
|
|
324
|
+
*
|
|
325
|
+
* If `this.targets` is empty, then this will set
|
|
326
|
+
* `this.sources` to the specified value.
|
|
327
|
+
*
|
|
328
|
+
* You can specify no sources at all to clear the
|
|
329
|
+
* currently set `this.sources` value.
|
|
330
|
+
*
|
|
331
|
+
* @returns {object} `this`
|
|
332
|
+
*/
|
|
333
|
+
from(...sources)
|
|
334
|
+
{
|
|
335
|
+
if (this.targets.length > 0 && sources.length > 0)
|
|
336
|
+
{
|
|
337
|
+
this.$run(sources, this.targets);
|
|
338
|
+
}
|
|
339
|
+
else
|
|
340
|
+
{
|
|
341
|
+
this.sources = sources;
|
|
342
|
+
}
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Protected method doesn't need to be documented.
|
|
347
|
+
$run(sources, targets)
|
|
348
|
+
{
|
|
349
|
+
for (const source of sources)
|
|
350
|
+
{
|
|
351
|
+
for (const target of targets)
|
|
352
|
+
{
|
|
353
|
+
copyProps(source, target, this.opts);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
copyProps.given = function(opts)
|
|
361
|
+
{
|
|
362
|
+
return new $CopyProps(opts);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
copyProps.into = function(...targets)
|
|
366
|
+
{
|
|
367
|
+
return ((new $CopyProps()).into(...targets));
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
copyProps.from = function(...sources)
|
|
371
|
+
{
|
|
372
|
+
return ((new $CopyProps()).from(...sources));
|
|
373
|
+
}
|
|
374
|
+
|
|
110
375
|
module.exports = copyProps;
|
package/lib/obj/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module @lumjs/core/obj
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
const copyAll = require('./copyall');
|
|
6
|
+
const {copyAll,duplicateOne,duplicateAll} = require('./copyall');
|
|
7
7
|
const copyProps = require('./copyprops');
|
|
8
8
|
const {CLONE,clone,addClone,cloneIfLocked} = require('./clone');
|
|
9
9
|
const {lock,addLock} = require('./lock');
|
|
@@ -21,5 +21,5 @@ module.exports =
|
|
|
21
21
|
CLONE, clone, addClone, cloneIfLocked, lock, addLock,
|
|
22
22
|
mergeNested, syncNested, copyProps, copyAll, ns,
|
|
23
23
|
getObjectPath, setObjectPath, getNamespace, setNamespace,
|
|
24
|
-
getProperty,
|
|
24
|
+
getProperty, duplicateAll, duplicateOne,
|
|
25
25
|
}
|
package/lib/obj/merge.js
CHANGED
|
@@ -1,48 +1,56 @@
|
|
|
1
1
|
// Import required bits here.
|
|
2
|
-
const {B, isObj} = require('../types');
|
|
2
|
+
const {B, N, isObj} = require('../types');
|
|
3
|
+
const copyProps = require('./copyprops');
|
|
4
|
+
|
|
5
|
+
// A shortcut for the recursive option.
|
|
6
|
+
const recursive = copyProps.RECURSE_ALL;
|
|
3
7
|
|
|
4
8
|
/**
|
|
5
9
|
* Merge two objects recursively.
|
|
6
10
|
*
|
|
7
|
-
* This
|
|
8
|
-
*
|
|
11
|
+
* This used to be a standalone function, but was poorly designed.
|
|
12
|
+
* It's now a wrapper around the
|
|
13
|
+
* [copyProps()]{@link module:@lumjs/core/obj.copyProps} method.
|
|
9
14
|
*
|
|
10
15
|
* @param {object} source - The source object we're copying from.
|
|
11
16
|
* @param {object} target - The target object we're copying into.
|
|
12
17
|
*
|
|
13
|
-
* @param {object} [opts] Options
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* @param {(object|boolean)} [opts] Options for `copyProps()`
|
|
19
|
+
*
|
|
20
|
+
* If `opts.recursive` is not a `number`, it'll be set to
|
|
21
|
+
* `copyProps.RECURSE_ALL` to enable recursion with no
|
|
22
|
+
* depth limits.
|
|
23
|
+
*
|
|
24
|
+
* Also, if `opts.overwrite` is not explicitly set, it will
|
|
25
|
+
* be set as `true`, a different default value than `copyProps()`.
|
|
26
|
+
*
|
|
27
|
+
* For backwards compatibility, if this specified as a `boolean`
|
|
28
|
+
* value instead of an object, it'll be assumed to be value
|
|
29
|
+
* for the `opts.overwrite` option.
|
|
21
30
|
*
|
|
22
31
|
* @returns {object} The `target` object.
|
|
23
32
|
* @alias module:@lumjs/core/obj.mergeNested
|
|
24
33
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
34
|
+
function mergeNested(source, target, opts={})
|
|
35
|
+
{
|
|
36
|
+
if (typeof opts === B)
|
|
37
|
+
{ // Boolean overwrite option was specified.
|
|
38
|
+
opts = {overwrite: opts, recursive};
|
|
39
|
+
}
|
|
40
|
+
else if (!isObj(opts))
|
|
41
|
+
{ // Wasn't an object, use default values.
|
|
42
|
+
opts = {overwrite: true, recursive};
|
|
43
|
+
}
|
|
44
|
+
else
|
|
45
|
+
{ // If recursive or overwrite aren't set, set them.
|
|
46
|
+
if (opts.recursive === undefined)
|
|
47
|
+
opts.recursive = recursive;
|
|
48
|
+
if (opts.overwrite === undefined)
|
|
49
|
+
opts.overwrite = true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return copyProps(source, target, opts);
|
|
53
|
+
}
|
|
46
54
|
|
|
47
55
|
exports.mergeNested = mergeNested;
|
|
48
56
|
|
package/lib/types/def.js
CHANGED
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
const unbound = require('./root').unbound;
|
|
3
3
|
const {F, B} = require('./js');
|
|
4
4
|
const {isObj, isNil, isProperty, doesDescriptor} = require('./basics');
|
|
5
|
-
const copy = require('../obj/copyall');
|
|
6
|
-
|
|
7
|
-
// A really really cheap version of clone().
|
|
8
|
-
const clone = obj => copy({}, obj);
|
|
5
|
+
const {copy, duplicateOne: clone} = require('../obj/copyall');
|
|
9
6
|
|
|
10
7
|
/**
|
|
11
8
|
* A wrapper around `Object.defineProperty()`.
|
package/lib/types/isa.js
CHANGED
|
@@ -10,177 +10,174 @@ const TYPES = require('./typelist');
|
|
|
10
10
|
* @returns {boolean}
|
|
11
11
|
* @alias module:@lumjs/core/types.isInstance
|
|
12
12
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
exports.isInstance = isInstance;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* A smarter `typeof` function.
|
|
31
|
-
*
|
|
32
|
-
* @param {string} type - The type we're checking for.
|
|
33
|
-
*
|
|
34
|
-
* This supports all the same type names as `typeof` plus any of
|
|
35
|
-
* the properties defined in the `TYPES` object.
|
|
36
|
-
*
|
|
37
|
-
* One other thing, while `typeof` reports `null` as being an `object`,
|
|
38
|
-
* this function does not count `null` as a valid `object`.
|
|
39
|
-
*
|
|
40
|
-
* @param {*} v - The value we're testing.
|
|
41
|
-
* @returns {boolean} If the value was of the desired type.
|
|
42
|
-
* @alias module:@lumjs/core/types.isType
|
|
43
|
-
*/
|
|
44
|
-
function isType(type, v)
|
|
45
|
-
{
|
|
46
|
-
if (typeof type !== S || !TYPES.list.includes(type))
|
|
47
|
-
{
|
|
48
|
-
throw new TypeError(`Invalid type ${JSON.stringify(type)} specified`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (typeof TYPES.tests[type] === F)
|
|
52
|
-
{ // A type-specific test.
|
|
53
|
-
return TYPES.tests[type](v);
|
|
54
|
-
}
|
|
55
|
-
else
|
|
56
|
-
{ // No type-specific tests.
|
|
57
|
-
return (typeof v === type);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
exports.isType = isType;
|
|
62
|
-
|
|
63
|
-
// Default options parser.
|
|
64
|
-
const DEFAULT_ISA_PARSER = function(type, v)
|
|
65
|
-
{ // `this` is the options object itself.
|
|
66
|
-
if (typeof type.is === F)
|
|
67
|
-
{ // We assume `is()` methods are the type check.
|
|
68
|
-
if (type.is(v)) return true;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (typeof type.isa === O)
|
|
72
|
-
{ // Process our known options.
|
|
73
|
-
const opts = type.isa;
|
|
74
|
-
|
|
75
|
-
if (typeof opts.process === F)
|
|
76
|
-
{ // We'll pass the current `opts` as well as the `v` to this method.
|
|
77
|
-
// It doesn't return anything on its own, but can assign further tests,
|
|
78
|
-
// which by default only apply to `object`
|
|
79
|
-
opts.process(this, v);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (typeof opts.parsers === F)
|
|
83
|
-
{ // Assign another parser.
|
|
84
|
-
this.parsers.push(opts.parsers);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (typeof opts.test === F)
|
|
88
|
-
{ // A custom test, will be passed `v` and the current `opts`.
|
|
89
|
-
if (opts.test(v, this))
|
|
90
|
-
{ // Mark this as having passed.
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Okay, now anything else gets set.
|
|
96
|
-
const RESERVED = ['parsers','process','test'];
|
|
97
|
-
for (const opt in opts)
|
|
98
|
-
{
|
|
99
|
-
if (RESERVED.includes(opt)) continue; // Skip it.
|
|
100
|
-
this[opt] = opts[opt];
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// We should almost always return false.
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Is value a certain type, or an instance of a certain class.
|
|
111
|
-
*
|
|
112
|
-
* @param {*} v - The value we're testing.
|
|
113
|
-
* @param {...any} types - The types the value should be one of.
|
|
114
|
-
*
|
|
115
|
-
* For each of the `types`, if it is a `string` we test with `isType()`,
|
|
116
|
-
* if it is a `function` we test with `isInstance()`.
|
|
117
|
-
*
|
|
118
|
-
* If it is an `object` and has an `is()` method, use that as the test.
|
|
119
|
-
*
|
|
120
|
-
* If it is an `object` it will be passed to the current options parsers.
|
|
121
|
-
* The default options parser looks for an `object` property called `isa`,
|
|
122
|
-
* which supports the following child properties:
|
|
123
|
-
*
|
|
124
|
-
* - `needProto: boolean`, Change the `needProto` option for `isInstance()`
|
|
125
|
-
* - `parsers: function`, Add another options parser function.
|
|
126
|
-
* - `process: function`, A one-time set-up function.
|
|
127
|
-
* - `test: function`, Pass the `v` to this test and return `true` if it passes.
|
|
128
|
-
* - Anything else will be set as an option.
|
|
129
|
-
*
|
|
130
|
-
* Any other type value will only match if `v === type`
|
|
131
|
-
*
|
|
132
|
-
* @returns {boolean} Was the value one of the desired types?
|
|
133
|
-
* @alias module:@lumjs/core/types.isa
|
|
134
|
-
*/
|
|
135
|
-
function isa(v, ...types)
|
|
136
|
-
{
|
|
137
|
-
// A special options object.
|
|
138
|
-
const opts =
|
|
139
|
-
{
|
|
140
|
-
needProto: false,
|
|
141
|
-
parsers:
|
|
142
|
-
[
|
|
143
|
-
DEFAULT_ISA_PARSER,
|
|
144
|
-
],
|
|
145
|
-
process(type, v)
|
|
146
|
-
{
|
|
147
|
-
for (const parser of this.parsers)
|
|
148
|
-
{
|
|
149
|
-
if (typeof parser === F)
|
|
150
|
-
{
|
|
151
|
-
if (parser.call(this, type, v) === true)
|
|
152
|
-
{ // Returning true means a custom test passed.
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return false;
|
|
158
|
-
},
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
for (const type of types)
|
|
162
|
-
{
|
|
163
|
-
// First a quick test for absolute equality.
|
|
164
|
-
if (v === type) return true;
|
|
165
|
-
|
|
166
|
-
// With that out of the way, let's go!
|
|
167
|
-
if (typeof type === S)
|
|
168
|
-
{ // A string is passed to isType()
|
|
169
|
-
if (isType(type, v)) return true;
|
|
170
|
-
}
|
|
171
|
-
else if (typeof type === F)
|
|
172
|
-
{ // A function is passed to isInstance()
|
|
173
|
-
if (isInstance(v, type, opts.needProto)) return true;
|
|
174
|
-
}
|
|
175
|
-
else if (isObj(type))
|
|
176
|
-
{ // Objects can be additional tests, or options.
|
|
177
|
-
if (opts.process(type, v)) return true;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
}
|
|
13
|
+
function isInstance(v, f, needProto=false)
|
|
14
|
+
{
|
|
15
|
+
if (!isObj(v)) return false; // Not an object.
|
|
16
|
+
if (needProto && (typeof v.prototype !== O || v.prototype === null))
|
|
17
|
+
{ // Has no prototype.
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (typeof f !== F || !(v instanceof f)) return false;
|
|
22
|
+
|
|
23
|
+
// Everything passed.
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
181
26
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
27
|
+
exports.isInstance = isInstance;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A smarter `typeof` function.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} type - The type we're checking for.
|
|
33
|
+
*
|
|
34
|
+
* This supports all the same type names as `typeof` plus any of
|
|
35
|
+
* the properties defined in the `TYPES` object.
|
|
36
|
+
*
|
|
37
|
+
* One other thing, while `typeof` reports `null` as being an `object`,
|
|
38
|
+
* this function does not count `null` as a valid `object`.
|
|
39
|
+
*
|
|
40
|
+
* @param {*} v - The value we're testing.
|
|
41
|
+
* @returns {boolean} If the value was of the desired type.
|
|
42
|
+
* @alias module:@lumjs/core/types.isType
|
|
43
|
+
*/
|
|
44
|
+
function isType(type, v)
|
|
45
|
+
{
|
|
46
|
+
if (typeof type !== S || !TYPES.list.includes(type))
|
|
47
|
+
{
|
|
48
|
+
throw new TypeError(`Invalid type ${JSON.stringify(type)} specified`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof TYPES.tests[type] === F)
|
|
52
|
+
{ // A type-specific test.
|
|
53
|
+
return TYPES.tests[type](v);
|
|
54
|
+
}
|
|
55
|
+
else
|
|
56
|
+
{ // No type-specific tests.
|
|
57
|
+
return (typeof v === type);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
exports.isType = isType;
|
|
62
|
+
|
|
63
|
+
// Default options parser.
|
|
64
|
+
const DEFAULT_ISA_PARSER = function(type, v)
|
|
65
|
+
{ // `this` is the options object itself.
|
|
66
|
+
if (typeof type.is === F)
|
|
67
|
+
{ // We assume `is()` methods are the type check.
|
|
68
|
+
if (type.is(v)) return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof type.isa === O)
|
|
72
|
+
{ // Process our known options.
|
|
73
|
+
const opts = type.isa;
|
|
74
|
+
|
|
75
|
+
if (typeof opts.process === F)
|
|
76
|
+
{ // Run a method to extend the options further.
|
|
77
|
+
opts.process(this, v, type);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (typeof opts.parsers === F)
|
|
81
|
+
{ // Assign another parser.
|
|
82
|
+
this.parsers.push(opts.parsers);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (typeof opts.test === F)
|
|
86
|
+
{ // A custom test, will be passed `v` and the current `opts`.
|
|
87
|
+
if (opts.test(v, this))
|
|
88
|
+
{ // Mark this as having passed.
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Okay, now anything else gets set.
|
|
94
|
+
const RESERVED = ['parsers','process','test'];
|
|
95
|
+
for (const opt in opts)
|
|
96
|
+
{
|
|
97
|
+
if (RESERVED.includes(opt)) continue; // Skip it.
|
|
98
|
+
this[opt] = opts[opt];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// We should almost always return false.
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Process further options
|
|
108
|
+
function processOptions(type, v)
|
|
109
|
+
{
|
|
110
|
+
for (const parser of this.parsers)
|
|
111
|
+
{
|
|
112
|
+
if (typeof parser === F)
|
|
113
|
+
{
|
|
114
|
+
if (parser.call(this, type, v) === true)
|
|
115
|
+
{ // Returning true means a custom test passed.
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Is value a certain type, or an instance of a certain class.
|
|
125
|
+
*
|
|
126
|
+
* @param {*} v - The value we're testing.
|
|
127
|
+
* @param {...any} types - The types the value should be one of.
|
|
128
|
+
*
|
|
129
|
+
* For each of the `types`, if it is a `string` we test with `isType()`,
|
|
130
|
+
* if it is a `function` we test with `isInstance()`.
|
|
131
|
+
*
|
|
132
|
+
* If it is an `object` and has an `is()` method, use that as the test.
|
|
133
|
+
*
|
|
134
|
+
* If it is an `object` it will be passed to the current options parsers.
|
|
135
|
+
* The default options parser looks for an `object` property called `isa`,
|
|
136
|
+
* which supports the following child properties:
|
|
137
|
+
*
|
|
138
|
+
* - `needProto: boolean`, Change the `needProto` option for `isInstance()`
|
|
139
|
+
* - `parsers: function`, Add another options parser function.
|
|
140
|
+
* - `process: function`, A one-time set-up function.
|
|
141
|
+
* - `test: function`, Pass the `v` to this test and return `true` if it passes.
|
|
142
|
+
* - Anything else will be set as an option.
|
|
143
|
+
*
|
|
144
|
+
* Any other type value will only match if `v === type`
|
|
145
|
+
*
|
|
146
|
+
* @returns {boolean} Was the value one of the desired types?
|
|
147
|
+
* @alias module:@lumjs/core/types.isa
|
|
148
|
+
*/
|
|
149
|
+
function isa(v, ...types)
|
|
150
|
+
{
|
|
151
|
+
// A special options object.
|
|
152
|
+
const opts =
|
|
153
|
+
{
|
|
154
|
+
needProto: false,
|
|
155
|
+
parsers: [DEFAULT_ISA_PARSER],
|
|
156
|
+
process: processOptions,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (const type of types)
|
|
160
|
+
{
|
|
161
|
+
// First a quick test for absolute equality.
|
|
162
|
+
if (v === type) return true;
|
|
163
|
+
|
|
164
|
+
// With that out of the way, let's go!
|
|
165
|
+
if (typeof type === S)
|
|
166
|
+
{ // A string is passed to isType()
|
|
167
|
+
if (isType(type, v)) return true;
|
|
168
|
+
}
|
|
169
|
+
else if (typeof type === F)
|
|
170
|
+
{ // A function is passed to isInstance()
|
|
171
|
+
if (isInstance(v, type, opts.needProto)) return true;
|
|
172
|
+
}
|
|
173
|
+
else if (isObj(type))
|
|
174
|
+
{ // Objects can be additional tests, or options.
|
|
175
|
+
if (opts.process(type, v)) return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// None of the tests passed. We have failed.
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
185
182
|
|
|
186
|
-
|
|
183
|
+
exports.isa = isa;
|
package/lib/types/needs.js
CHANGED
|
@@ -6,23 +6,42 @@ const {isObj, isComplex} = require('./basics');
|
|
|
6
6
|
* If a value is not an object, throw an error.
|
|
7
7
|
*
|
|
8
8
|
* @param {*} v - The value we're testing.
|
|
9
|
-
* @param {boolean} [allowFunc=false] - Also accept
|
|
9
|
+
* @param {(boolean|string)} [allowFunc=false] - Also accept `function`?
|
|
10
|
+
*
|
|
11
|
+
* By default this function uses `isObj()` to perform the
|
|
12
|
+
* test. If `allowFunc` is `true` then it'll use `isComplex()` instead.
|
|
13
|
+
*
|
|
14
|
+
* If this is a `string` then it'll be assigned to the `msg`
|
|
15
|
+
* parameter, and `allowFunc` will be `true` if the word
|
|
16
|
+
* `function` is found in the message, or `false` otherwise.
|
|
17
|
+
*
|
|
10
18
|
* @param {string} [msg] A custom error message.
|
|
19
|
+
*
|
|
20
|
+
* If not specified, a generic one will be used.
|
|
21
|
+
*
|
|
11
22
|
* @throws {TypeError} If the type check failed.
|
|
12
23
|
* @alias module:@lumjs/core/types.needObj
|
|
13
24
|
*/
|
|
14
25
|
function needObj (v, allowFunc=false, msg=null)
|
|
15
26
|
{
|
|
16
|
-
if (allowFunc
|
|
17
|
-
|
|
27
|
+
if (typeof allowFunc === S)
|
|
28
|
+
{ // A message was passed.
|
|
29
|
+
msg = allowFunc;
|
|
30
|
+
allowFunc = msg.toLowerCase().includes(F);
|
|
31
|
+
}
|
|
18
32
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
const ok = allowFunc ? isComplex(v) : isObj(v);
|
|
34
|
+
|
|
35
|
+
if (!ok)
|
|
36
|
+
{ // Did not pass the test.
|
|
37
|
+
if (typeof msg !== S)
|
|
38
|
+
{ // Use a default message.
|
|
39
|
+
msg = "Invalid object";
|
|
40
|
+
if (allowFunc)
|
|
41
|
+
msg += " or function";
|
|
42
|
+
}
|
|
43
|
+
throw new TypeError(msg);
|
|
24
44
|
}
|
|
25
|
-
throw new TypeError(msg);
|
|
26
45
|
}
|
|
27
46
|
|
|
28
47
|
exports.needObj = needObj;
|
|
@@ -101,6 +120,7 @@ function needs(v, ...types)
|
|
|
101
120
|
{
|
|
102
121
|
error = new TypeError("value did not pass needs check");
|
|
103
122
|
}
|
|
123
|
+
console.error("needs()", v, types);
|
|
104
124
|
throw error;
|
|
105
125
|
}
|
|
106
126
|
}
|