@lumjs/core 1.0.0-beta.2 → 1.0.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/README.md +11 -2
- package/TODO.md +12 -5
- package/docs/changelogs/1.0-beta.md +99 -0
- package/docs/changelogs/1.x.md +17 -0
- package/jsdoc.json +33 -0
- package/lib/arrays.js +78 -0
- package/lib/context.js +49 -10
- package/lib/enum.js +30 -13
- package/lib/flags.js +6 -0
- package/lib/index.js +87 -25
- package/lib/lazy.js +33 -35
- package/lib/meta.js +38 -1
- package/lib/modules.js +43 -7
- package/lib/obj/clone.js +158 -139
- package/lib/obj/copyall.js +3 -0
- package/lib/obj/copyprops.js +1 -0
- package/lib/obj/index.js +12 -7
- package/lib/obj/lock.js +16 -13
- package/lib/obj/merge.js +2 -0
- package/lib/obj/ns.js +45 -8
- package/lib/objectid.js +2 -3
- package/lib/observable.js +23 -8
- package/lib/opt.js +6 -1
- package/lib/strings.js +43 -4
- package/lib/types/basics.js +154 -0
- package/lib/types/def.js +183 -0
- package/lib/types/index.js +55 -0
- package/lib/types/isa.js +186 -0
- package/lib/types/js.js +12 -0
- package/lib/types/needs.js +117 -0
- package/lib/types/root.js +92 -0
- package/lib/types/stringify.js +98 -0
- package/lib/types/typelist.js +168 -0
- package/package.json +25 -2
- package/test/arrays.js +19 -0
- package/test/meta.js +17 -0
- package/test/types.js +105 -22
- package/CHANGELOG.md +0 -25
- package/lib/descriptors.js +0 -243
- package/lib/prop.js +0 -170
- package/lib/types.js +0 -545
package/lib/types/def.js
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Thanks to CJS `require()` rules, recursive dependencies are possible.
|
|
2
|
+
const unbound = require('./root').unbound;
|
|
3
|
+
const {F, B} = require('./js');
|
|
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);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A wrapper around `Object.defineProperty()`.
|
|
12
|
+
*
|
|
13
|
+
* This has a few features that makes adding properties a lot nicer.
|
|
14
|
+
* It replaces the `prop()` method from the old Lum.js v4.
|
|
15
|
+
*
|
|
16
|
+
* @param {(object|function)} obj - The object to add a property to.
|
|
17
|
+
* @param {?(string|boolean|object)} name - If a `string`, the property name.
|
|
18
|
+
* Property names may also be `symbol` values.
|
|
19
|
+
*
|
|
20
|
+
* If this is `null` or `undefined` then the `value` is ignored entirely,
|
|
21
|
+
* and instead a bound version of this function is created with the
|
|
22
|
+
* `obj` already passed as the first parameter, and any of the options from
|
|
23
|
+
* `opts` added to a new default set of options bound to the function.
|
|
24
|
+
* Can be useful if you need to add a lot of properties to the same object.
|
|
25
|
+
*
|
|
26
|
+
* If this is a `boolean`, then the same logic as if it was `null` or
|
|
27
|
+
* `undefined` will apply, except that an `enumerable` property with this
|
|
28
|
+
* value will also be added to the descriptors.
|
|
29
|
+
*
|
|
30
|
+
* If this is an `object`, we also ignore `value` entirely, as each of
|
|
31
|
+
* the keys of this object will be used as the name of a property,
|
|
32
|
+
* and the value associated with the key will be the value to assign it.
|
|
33
|
+
*
|
|
34
|
+
* @param {*} value - Used to determine the value of the property.
|
|
35
|
+
*
|
|
36
|
+
* If it is an a valid descriptor object (as per `doesDescriptor()`),
|
|
37
|
+
* it will be used as the descriptor. If it has no `configurable`
|
|
38
|
+
* property defined, one will be added, and will be set to `true`.
|
|
39
|
+
* This behaviour may be overridden by the `opts` parameter.
|
|
40
|
+
*
|
|
41
|
+
* If this and `opts` are both `function` values, then this will
|
|
42
|
+
* be used as a *getter*, and `opts` will be used a *setter*.
|
|
43
|
+
*
|
|
44
|
+
* Any other value passed here will be used as the `value` in a
|
|
45
|
+
* pre-canned descriptor, also with `configurable` set to `true`.
|
|
46
|
+
*
|
|
47
|
+
* @param {*} [opts] - A multi-purpose option.
|
|
48
|
+
*
|
|
49
|
+
* If this is an `object` then it is reserved for named options.
|
|
50
|
+
* The named options `configurable`, `enumerable`, and `writable`
|
|
51
|
+
* can be used to define default values to the descriptor properties
|
|
52
|
+
* of the same name.
|
|
53
|
+
*
|
|
54
|
+
* If `value` and this are both `function` values, then `value` will
|
|
55
|
+
* be used as a *getter* and this will be used as a *setter*.
|
|
56
|
+
*
|
|
57
|
+
* If `value` is a valid descriptor object, then setting this to `false`
|
|
58
|
+
* will disable the assumption that it is the descriptor to set.
|
|
59
|
+
* Setting this to `true` on will instruct the function to make a clone
|
|
60
|
+
* of the descriptor object before modifying any properties in it.
|
|
61
|
+
*
|
|
62
|
+
* This defaults to `undefined`, except if
|
|
63
|
+
*
|
|
64
|
+
* @returns {*} Normally the `obj` argument with new property added.
|
|
65
|
+
*
|
|
66
|
+
* The exception is if this is a bound copy of the function created
|
|
67
|
+
* using the syntax described in the `name` parameter documentation.
|
|
68
|
+
* In that case the return value is the bound copy itself.
|
|
69
|
+
* While that might seem strange, it allows for chaining in a way
|
|
70
|
+
* that otherwise would not be possible, take this example:
|
|
71
|
+
*
|
|
72
|
+
* ```
|
|
73
|
+
* def(myObj)('name', "Bob")('age', 42);
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* @alias module:@lumjs/core/types.def
|
|
77
|
+
*/
|
|
78
|
+
function def(obj, name, value, opts)
|
|
79
|
+
{
|
|
80
|
+
const isBound // Is this a 'bound' def function?
|
|
81
|
+
= !unbound(this, true, true)
|
|
82
|
+
&& typeof this.bound === F;
|
|
83
|
+
|
|
84
|
+
// When we're finished, return this value.
|
|
85
|
+
const done = () =>
|
|
86
|
+
{
|
|
87
|
+
if (isBound)
|
|
88
|
+
{ // Bound version, returns itself recursively.
|
|
89
|
+
return this.bound;
|
|
90
|
+
}
|
|
91
|
+
else
|
|
92
|
+
{ // Not bound, or doesn't have a reference to itself.
|
|
93
|
+
return obj;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (isBound && isNil(opts))
|
|
98
|
+
{ // We'll use `this` as the options.
|
|
99
|
+
opts = this;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (isObj(name))
|
|
103
|
+
{ // Assume it's a map of {name: value} entries.
|
|
104
|
+
for (const key in name)
|
|
105
|
+
{
|
|
106
|
+
def(obj, key, name[key], opts);
|
|
107
|
+
}
|
|
108
|
+
// Okay, we're done now.
|
|
109
|
+
return done();
|
|
110
|
+
}
|
|
111
|
+
else if (isNil(name) || typeof name === B)
|
|
112
|
+
{ // Create a fresh binding context for the bound function.
|
|
113
|
+
const bind = {};
|
|
114
|
+
|
|
115
|
+
if (isObj(opts))
|
|
116
|
+
{ // Copy our existing options as defaults.
|
|
117
|
+
copy(bind, opts);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (typeof name === B)
|
|
121
|
+
{ // A boolean `name` overrides the enumerable option.
|
|
122
|
+
bind.enumerable = name;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Create a bound function.
|
|
126
|
+
const bound = def.bind(bind, obj);
|
|
127
|
+
// Add a reference to the function in the binding context.
|
|
128
|
+
bind.bound = bound;
|
|
129
|
+
// And a reference to the binding options from the function.
|
|
130
|
+
bound.$this = bind;
|
|
131
|
+
|
|
132
|
+
return bound;
|
|
133
|
+
}
|
|
134
|
+
else if (!isProperty(name))
|
|
135
|
+
{ // That's not valid.
|
|
136
|
+
throw new TypeError("Property name must be a string or a Symbol");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let desc;
|
|
140
|
+
|
|
141
|
+
if (opts !== false && doesDescriptor(value))
|
|
142
|
+
{ // The value is a descriptor, let's use it.
|
|
143
|
+
desc = (opts === true) ? clone(value) : value;
|
|
144
|
+
}
|
|
145
|
+
else if (typeof value === F && typeof opts === F)
|
|
146
|
+
{ // A getter and setter were both specified.
|
|
147
|
+
desc = {get: value, set: opts};
|
|
148
|
+
}
|
|
149
|
+
else
|
|
150
|
+
{ // The value is just a value, so let's assign it.
|
|
151
|
+
desc = {value};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (isObj(opts))
|
|
155
|
+
{ // If opts is an object, let's look for some supported defaults.
|
|
156
|
+
const cd = (opt, defval) =>
|
|
157
|
+
{
|
|
158
|
+
if (typeof opts[opt] === B && typeof desc[opt] !== B)
|
|
159
|
+
{ // Default descriptor option specified in `opts`
|
|
160
|
+
desc[opt] = opts[opt];
|
|
161
|
+
}
|
|
162
|
+
else if (typeof defval === B)
|
|
163
|
+
{ // A fallback default.
|
|
164
|
+
desc[opt] = defval;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
cd('configurable', true);
|
|
169
|
+
cd('enumerable');
|
|
170
|
+
if (desc.get === undefined && desc.set === undefined)
|
|
171
|
+
{ // Only look for this one on data descriptors.
|
|
172
|
+
cd('writable');
|
|
173
|
+
}
|
|
174
|
+
} // if isObj(opts)
|
|
175
|
+
|
|
176
|
+
// Now after all that, let's actually define the property!
|
|
177
|
+
Object.defineProperty(obj, name, desc);
|
|
178
|
+
|
|
179
|
+
return done();
|
|
180
|
+
|
|
181
|
+
} // def()
|
|
182
|
+
|
|
183
|
+
module.exports = def;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fundamental Types sub-module.
|
|
3
|
+
*
|
|
4
|
+
* As `@lumjs/core` is the foundation for all my JS libraries,
|
|
5
|
+
* this sub-module is the foundation for `@lumjs/core`.
|
|
6
|
+
* Everything else is built upon this.
|
|
7
|
+
*
|
|
8
|
+
* @module @lumjs/core/types
|
|
9
|
+
* @property {string} O - "object"
|
|
10
|
+
* @property {string} F - "function"
|
|
11
|
+
* @property {string} S - "string"
|
|
12
|
+
* @property {string} B - "binary"
|
|
13
|
+
* @property {string} N - "number"
|
|
14
|
+
* @property {string} U - "undefined"
|
|
15
|
+
* @property {string} SY - "symbol"
|
|
16
|
+
* @property {string} BI - "bigint"
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// Constants representing core Javascript types.
|
|
20
|
+
const {O, F, S, B, N, U, SY, BI} = require('./js');
|
|
21
|
+
|
|
22
|
+
// Basic type check functions.
|
|
23
|
+
const
|
|
24
|
+
{
|
|
25
|
+
isObj, isComplex, isNil, notNil, isScalar, isArray, isTypedArray,
|
|
26
|
+
nonEmptyArray, isArguments, isProperty, doesDescriptor,
|
|
27
|
+
} = require('./basics');
|
|
28
|
+
|
|
29
|
+
// Root namespace helpers.
|
|
30
|
+
const {root, unbound} = require('./root');
|
|
31
|
+
|
|
32
|
+
// Advanced type checks.
|
|
33
|
+
const {isInstance, isType, isa} = require('./isa');
|
|
34
|
+
|
|
35
|
+
// Error-throwing type checks.
|
|
36
|
+
const {needObj, needType, needs} = require('./needs');
|
|
37
|
+
|
|
38
|
+
// A few standalone items.
|
|
39
|
+
|
|
40
|
+
const def = require('./def');
|
|
41
|
+
const TYPES = require('./typelist');
|
|
42
|
+
const stringify = require('./stringify');
|
|
43
|
+
|
|
44
|
+
// Okay, add all those to our exports.
|
|
45
|
+
// Further tests can be added by `TYPES.add()` later.
|
|
46
|
+
module.exports =
|
|
47
|
+
{
|
|
48
|
+
O, F, S, B, N, U, SY, BI, TYPES, root, unbound, def,
|
|
49
|
+
isObj, isComplex, isNil, notNil, isScalar, isArray, isTypedArray,
|
|
50
|
+
nonEmptyArray, isArguments, isProperty, doesDescriptor,
|
|
51
|
+
isInstance, isType, isa, needObj, needType, needs, stringify,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Last but not least, this will be the module for TYPES.add()
|
|
55
|
+
def(TYPES, '$module', module);
|
package/lib/types/isa.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const {O, F, S} = require('./js');
|
|
2
|
+
const {isObj} = require('./basics');
|
|
3
|
+
const TYPES = require('./typelist');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* See if a value is an instance of a class.
|
|
7
|
+
* @param {*} v - The value we're testing.
|
|
8
|
+
* @param {function} f - The constructor/class we want.
|
|
9
|
+
* @param {boolean} [needProto=false] If true, the `v` must have a `prototype`.
|
|
10
|
+
* @returns {boolean}
|
|
11
|
+
* @alias module:@lumjs/core/types.isInstance
|
|
12
|
+
*/
|
|
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
|
+
}
|
|
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
|
+
}
|
|
181
|
+
|
|
182
|
+
// None of the tests passed. We have failed.
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
exports.isa = isa;
|
package/lib/types/js.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Fundamental core types.
|
|
2
|
+
const O='object', F='function', S='string', B='boolean', N='number',
|
|
3
|
+
U='undefined', SY='symbol', BI='bigint';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One or two character identifiers for the core JS type names.
|
|
7
|
+
*
|
|
8
|
+
* These are only the strings returned by the `typeof` operator.
|
|
9
|
+
* See the `TYPES` object (defined in `typelist.js`) for a list
|
|
10
|
+
* that includes special types and compound pseudo-types, etc.
|
|
11
|
+
*/
|
|
12
|
+
module.exports = {O, F, S, B, N, U, SY, BI};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
const {F, S, B} = require('./js');
|
|
2
|
+
const {isType, isa} = require('./isa');
|
|
3
|
+
const {isObj, isComplex} = require('./basics');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* If a value is not an object, throw an error.
|
|
7
|
+
*
|
|
8
|
+
* @param {*} v - The value we're testing.
|
|
9
|
+
* @param {boolean} [allowFunc=false] - Also accept functions?
|
|
10
|
+
* @param {string} [msg] A custom error message.
|
|
11
|
+
* @throws {TypeError} If the type check failed.
|
|
12
|
+
* @alias module:@lumjs/core/types.needObj
|
|
13
|
+
*/
|
|
14
|
+
function needObj (v, allowFunc=false, msg=null)
|
|
15
|
+
{
|
|
16
|
+
if (allowFunc && isComplex(v)) return;
|
|
17
|
+
if (isObj(v)) return;
|
|
18
|
+
|
|
19
|
+
if (typeof msg !== S)
|
|
20
|
+
{ // Use a default message.
|
|
21
|
+
msg = "Invalid object";
|
|
22
|
+
if (allowFunc)
|
|
23
|
+
msg += " or function";
|
|
24
|
+
}
|
|
25
|
+
throw new TypeError(msg);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
exports.needObj = needObj;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* If a value is not a certain type, throw an error.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} type - The type name as per `isType()`.
|
|
34
|
+
* @param {*} v - The value we're testing.
|
|
35
|
+
* @param {string} [msg] A custom error message.
|
|
36
|
+
* @throws {TypeError} If the type check failed.
|
|
37
|
+
* @alias module:@lumjs/core/types.needType
|
|
38
|
+
*/
|
|
39
|
+
function needType (type, v, msg, unused)
|
|
40
|
+
{
|
|
41
|
+
if (!isType(type, v))
|
|
42
|
+
{
|
|
43
|
+
if (typeof msg === B)
|
|
44
|
+
{
|
|
45
|
+
console.warn("needType(): 'allowNull' is no longer supported");
|
|
46
|
+
if (typeof unused === S)
|
|
47
|
+
{ // Compatibility with old code.
|
|
48
|
+
msg = unused;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (typeof msg !== S)
|
|
53
|
+
{ // Use a default message.
|
|
54
|
+
msg = `Invalid ${type} value`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
throw new TypeError(msg);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
exports.needType = needType;
|
|
62
|
+
|
|
63
|
+
// Options parser for needs();
|
|
64
|
+
const NEEDS_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
|
+
|
|
72
|
+
/**
|
|
73
|
+
* A wrapper around `isa()` that will throw an error on failure.
|
|
74
|
+
*
|
|
75
|
+
* @param {*} v - The value we're testing.
|
|
76
|
+
* @param {...any} types - The types the value should be one of.
|
|
77
|
+
*
|
|
78
|
+
* In addition to the `types` supported by `isa()` this will also
|
|
79
|
+
* look for an `object` with a single property named `error`
|
|
80
|
+
* which can be either a `string` or any subclass of `Error`.
|
|
81
|
+
* If specified, it will override the error message that will be thrown.
|
|
82
|
+
*
|
|
83
|
+
* @throws {TypeError} If the type check failed.
|
|
84
|
+
* @throws {Error} If a custom error was specified.
|
|
85
|
+
* @alias module:@lumjs/core/types.needs
|
|
86
|
+
*/
|
|
87
|
+
function needs(v, ...types)
|
|
88
|
+
{
|
|
89
|
+
let error;
|
|
90
|
+
|
|
91
|
+
function parser(type, v)
|
|
92
|
+
{
|
|
93
|
+
// Only process objects with a single `error` property.
|
|
94
|
+
if ('error' in type && Object.keys(type).length === 1)
|
|
95
|
+
{
|
|
96
|
+
if (typeof type.error === 'string')
|
|
97
|
+
{ // An error message.
|
|
98
|
+
error = new TypeError(type.error);
|
|
99
|
+
}
|
|
100
|
+
else if (type.error instanceof Error)
|
|
101
|
+
{ // An error object.
|
|
102
|
+
error = type.error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!isa(v, {isa:{parsers: parser}}, ...types))
|
|
108
|
+
{
|
|
109
|
+
if (!(error instanceof Error))
|
|
110
|
+
{
|
|
111
|
+
error = new TypeError("value did not pass needs check");
|
|
112
|
+
}
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
exports.needs = needs;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
const {U} = require('./js');
|
|
2
|
+
const {isNil,isArray} = require('./basics');
|
|
3
|
+
|
|
4
|
+
// «private»
|
|
5
|
+
function no_root()
|
|
6
|
+
{
|
|
7
|
+
throw new Error("Invalid JS environment, no root object found");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The global root object. Usually `globalThis` these days.
|
|
12
|
+
* @alias module:@lumjs/core/types.root
|
|
13
|
+
*/
|
|
14
|
+
const root = typeof globalThis !== U ? globalThis
|
|
15
|
+
: typeof global !== U ? global
|
|
16
|
+
: typeof self !== U ? self
|
|
17
|
+
: typeof window !== U ? window
|
|
18
|
+
: no_root(); // Unlike the old way, we'll die if the environment is undetermined.
|
|
19
|
+
|
|
20
|
+
exports.root = root;
|
|
21
|
+
|
|
22
|
+
// A list of objects to be considered unbound globally.
|
|
23
|
+
const unboundObjects = [];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Pass `this` here to see if it is bound to an object.
|
|
27
|
+
*
|
|
28
|
+
* Always considers `null` and `undefined` as unbound.
|
|
29
|
+
*
|
|
30
|
+
* @param {*} whatIsThis - The `this` from any context.
|
|
31
|
+
* @param {boolean} [rootIsUnbound=true] The global root is unbound.
|
|
32
|
+
* @param {(boolean|Array)} [areUnbound=false] A list of unbound objects.
|
|
33
|
+
* If the is `true` we use an global list that can register special
|
|
34
|
+
* internal objects. Otherwise an `Array` of unbound objects may be used.
|
|
35
|
+
* @returns {boolean}
|
|
36
|
+
* @alias module:@lumjs/core/types.unbound
|
|
37
|
+
*/
|
|
38
|
+
function unbound(whatIsThis, rootIsUnbound=true, areUnbound=false)
|
|
39
|
+
{
|
|
40
|
+
if (areUnbound === true)
|
|
41
|
+
{ // If areUnbound is true, we use the unboundObjects
|
|
42
|
+
areUnbound = unboundObjects;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (isNil(whatIsThis)) return true;
|
|
46
|
+
if (rootIsUnbound && whatIsThis === root) return true;
|
|
47
|
+
if (isArray(areUnbound) && areUnbound.includes(whatIsThis)) return true;
|
|
48
|
+
|
|
49
|
+
// Nothing considered unbound.
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
exports.unbound = unbound;
|
|
54
|
+
|
|
55
|
+
// Now that 'unbound' is exported, we can do some wibbly wobbly magic.
|
|
56
|
+
const def = require('./def');
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Add an item to the unbound global objects list.
|
|
60
|
+
*
|
|
61
|
+
* @function
|
|
62
|
+
* @param {(object|function)} obj - The object to be considered unbound.
|
|
63
|
+
* @returns {boolean} Will be `false` if `obj` is already unbound.
|
|
64
|
+
* @throws {TypeError} If `obj` was neither an `object` nor a `function`.
|
|
65
|
+
* @alias module:@lumjs/core/types.unbound.add
|
|
66
|
+
*/
|
|
67
|
+
def(unbound, 'add', function (obj)
|
|
68
|
+
{
|
|
69
|
+
needObj(obj, true);
|
|
70
|
+
if (unbound(obj, true, true))
|
|
71
|
+
{ // Item is already unbound.
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
// Add to list and we're done.
|
|
75
|
+
unboundObjects.push(obj);
|
|
76
|
+
return true;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Remove an item from the unbound global objects list.
|
|
81
|
+
*
|
|
82
|
+
* @function
|
|
83
|
+
* @param {(object|function)} obj - The object to be removed.
|
|
84
|
+
* @returns {boolean} Will be `false` if the item was not in the list.
|
|
85
|
+
* @throws {TypeError} If `obj` was neither an `object` nor a `function`.
|
|
86
|
+
* @alias module:@lumjs/core/types.unbound.remove
|
|
87
|
+
*/
|
|
88
|
+
def(unbound, 'remove', function(obj)
|
|
89
|
+
{
|
|
90
|
+
needObj(obj, true);
|
|
91
|
+
return (removeFromArray(unboundObjects, obj) > 0);
|
|
92
|
+
});
|