@lumjs/core 1.0.0-beta.4 → 1.1.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 +7 -3
- package/TODO.md +6 -12
- package/docs/changelogs/1.0-beta.md +33 -3
- package/docs/changelogs/1.x.md +32 -0
- package/lib/arrays.js +19 -7
- package/lib/context.js +46 -9
- package/lib/enum.js +29 -15
- package/lib/flags.js +6 -0
- package/lib/index.js +77 -5
- package/lib/lazy.js +7 -5
- package/lib/meta.js +10 -0
- package/lib/modules.js +27 -3
- package/lib/obj/clone.js +11 -9
- package/lib/obj/copyall.js +3 -0
- package/lib/obj/copyprops.js +1 -0
- package/lib/obj/getproperty.js +38 -0
- package/lib/obj/index.js +6 -4
- package/lib/obj/lock.js +13 -9
- package/lib/obj/merge.js +2 -0
- package/lib/obj/ns.js +71 -18
- package/lib/objectid.js +2 -3
- package/lib/observable.js +23 -9
- package/lib/opt.js +6 -1
- package/lib/strings.js +43 -4
- package/lib/types/basics.js +69 -11
- package/lib/types/def.js +21 -18
- package/lib/types/index.js +21 -2
- package/lib/types/isa.js +5 -12
- package/lib/types/needs.js +6 -1
- package/lib/types/root.js +6 -8
- package/lib/types/stringify.js +142 -0
- package/lib/types/typelist.js +66 -2
- package/package.json +20 -3
- package/test/types.js +102 -13
package/lib/types/basics.js
CHANGED
|
@@ -4,6 +4,7 @@ const {O, F, S, SY} = require('./js');
|
|
|
4
4
|
* See if a value is a non-null `object`.
|
|
5
5
|
* @param {*} v - The value we're testing.
|
|
6
6
|
* @returns {boolean}
|
|
7
|
+
* @alias module:@lumjs/core/types.isObj
|
|
7
8
|
*/
|
|
8
9
|
function isObj(v) { return (typeof v === O && v !== null); }
|
|
9
10
|
|
|
@@ -12,6 +13,7 @@ const {O, F, S, SY} = require('./js');
|
|
|
12
13
|
* Like `isObj()`, `null` does not count as an `object`.
|
|
13
14
|
* @param {*} v - The value we're testing.
|
|
14
15
|
* @returns {boolean}
|
|
16
|
+
* @alias module:@lumjs/core/types.isComplex
|
|
15
17
|
*/
|
|
16
18
|
function isComplex(v) { return (typeof v === F || isObj(v)); }
|
|
17
19
|
|
|
@@ -19,6 +21,7 @@ const {O, F, S, SY} = require('./js');
|
|
|
19
21
|
* See if a value is *nil* (i.e. either `null` or `undefined`).
|
|
20
22
|
* @param {*} v - The value we're testing.
|
|
21
23
|
* @returns {boolean}
|
|
24
|
+
* @alias module:@lumjs/core/types.isNil
|
|
22
25
|
*/
|
|
23
26
|
function isNil(v) { return (v === undefined || v === null); }
|
|
24
27
|
|
|
@@ -26,6 +29,7 @@ const {O, F, S, SY} = require('./js');
|
|
|
26
29
|
* See if a value is not *nil* (i.e. neither `null` nor `undefined`).
|
|
27
30
|
* @param {*} v - The value we're testing.
|
|
28
31
|
* @returns {boolean}
|
|
32
|
+
* @alias module:@lumjs/core/types.notNil
|
|
29
33
|
*/
|
|
30
34
|
function notNil(v) { return (v !== undefined && v !== null); }
|
|
31
35
|
|
|
@@ -35,14 +39,17 @@ const {O, F, S, SY} = require('./js');
|
|
|
35
39
|
* is neither *nil* nor *complex*.
|
|
36
40
|
* @param {*} v - The value we're testing.
|
|
37
41
|
* @returns {boolean}
|
|
42
|
+
* @alias module:@lumjs/core/types.isScalar
|
|
38
43
|
*/
|
|
39
44
|
function isScalar(v) { return (notNil(v) && !isComplex(v)); }
|
|
40
45
|
|
|
41
46
|
/**
|
|
42
47
|
* See if a value is an `Array` object.
|
|
43
48
|
* This is literally just a copy of `Array.isArray`.
|
|
49
|
+
* @function
|
|
44
50
|
* @param {*} v - The value we're testing.
|
|
45
51
|
* @returns {boolean}
|
|
52
|
+
* @alias module:@lumjs/core/types.isArray
|
|
46
53
|
*/
|
|
47
54
|
const isArray = Array.isArray;
|
|
48
55
|
|
|
@@ -50,6 +57,7 @@ const {O, F, S, SY} = require('./js');
|
|
|
50
57
|
* See if a value is a `TypedArray` object.
|
|
51
58
|
* @param {*} v - The value we're testing.
|
|
52
59
|
* @returns {boolean}
|
|
60
|
+
* @alias module:@lumjs/core/types.isTypedArray
|
|
53
61
|
*/
|
|
54
62
|
function isTypedArray(v)
|
|
55
63
|
{
|
|
@@ -62,6 +70,7 @@ const {O, F, S, SY} = require('./js');
|
|
|
62
70
|
* @param {boolean} [typed=false] If `true` we want a `TypedArray`.
|
|
63
71
|
* If `false` (default) we want a regular `Array`.
|
|
64
72
|
* @returns {boolean}
|
|
73
|
+
* @alias module:@lumjs/core/types.nonEmptyArray
|
|
65
74
|
*/
|
|
66
75
|
function nonEmptyArray(v, typed=false)
|
|
67
76
|
{
|
|
@@ -75,6 +84,7 @@ const {O, F, S, SY} = require('./js');
|
|
|
75
84
|
* See if a value is an `arguments` object.
|
|
76
85
|
* @param {*} v - The value we're testing.
|
|
77
86
|
* @returns {boolean}
|
|
87
|
+
* @alias module:@lumjs/core/types.isArguments
|
|
78
88
|
*/
|
|
79
89
|
function isArguments(v)
|
|
80
90
|
{
|
|
@@ -85,6 +95,7 @@ const {O, F, S, SY} = require('./js');
|
|
|
85
95
|
* See if a value is a Property name.
|
|
86
96
|
* @param {*} v - The value we're testing.
|
|
87
97
|
* @returns {boolean}
|
|
98
|
+
* @alias module:@lumjs/core/types.isProperty
|
|
88
99
|
*/
|
|
89
100
|
function isProperty(v)
|
|
90
101
|
{
|
|
@@ -93,22 +104,23 @@ function isProperty(v)
|
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
/**
|
|
96
|
-
* See if an object can be used as a valid descriptor
|
|
107
|
+
* See if an object can be used as a valid *descriptor*.
|
|
97
108
|
*
|
|
98
|
-
* Basically in order to be considered a valid descriptor
|
|
109
|
+
* Basically in order to be considered a valid *descriptor*,
|
|
99
110
|
* one of the the following sets of rules must be true:
|
|
100
111
|
*
|
|
101
|
-
* - A Data
|
|
112
|
+
* - A *Data descriptor*:
|
|
102
113
|
* - Has a `value` property.
|
|
103
114
|
* - Does not have a `get` property.
|
|
104
115
|
* - Does not have a `set` property.
|
|
105
|
-
* - An Accessor
|
|
116
|
+
* - An *Accessor descriptor*:
|
|
106
117
|
* - Has a `get` and/or `set` property.
|
|
107
118
|
* - Does not have a `value` property.
|
|
108
119
|
* - Does not have a `writable` property.
|
|
109
120
|
*
|
|
110
121
|
* @param {object} obj - The object we are testing.
|
|
111
122
|
* @returns {boolean} - Is the object a valid descriptor?
|
|
123
|
+
* @alias module:@lumjs/core/types.doesDescriptor
|
|
112
124
|
*/
|
|
113
125
|
function doesDescriptor(obj)
|
|
114
126
|
{
|
|
@@ -133,10 +145,56 @@ function isProperty(v)
|
|
|
133
145
|
return false;
|
|
134
146
|
}
|
|
135
147
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
/**
|
|
149
|
+
* See if an object can be used as a valid *descriptor template*.
|
|
150
|
+
*
|
|
151
|
+
* This is similar to `doesDescriptor`, except that a *template*
|
|
152
|
+
* **must not** contain `value`, `get` or `set` properties.
|
|
153
|
+
*
|
|
154
|
+
* @param {object} obj - The object we are testing.
|
|
155
|
+
* @param {boolean} [accessor=false] Template is for an Accessor.
|
|
156
|
+
* If this is `true` then the `writable` property will also be forbidden.
|
|
157
|
+
* @param {boolean} [strict=true] Only allow valid descriptor properties.
|
|
158
|
+
* If this is `true` then **only** allow `configurable`, `enumerable`, and
|
|
159
|
+
* conditionally `writable` (only if `accessor` is `false`.)
|
|
160
|
+
* If this is `false` then any unknown properties will be ignored.
|
|
161
|
+
*
|
|
162
|
+
* @returns {boolean} Is the object a valid descriptor template?
|
|
163
|
+
* @alias module:@lumjs/core/types.doesDescriptorTemplate
|
|
164
|
+
*/
|
|
165
|
+
function doesDescriptorTemplate(obj, accessor=false, strict=true)
|
|
166
|
+
{
|
|
167
|
+
if (!isObj(obj)) return false;
|
|
168
|
+
|
|
169
|
+
// Get a list of enumerable properties in the object.
|
|
170
|
+
const props = Object.keys(obj);
|
|
171
|
+
|
|
172
|
+
if (strict)
|
|
173
|
+
{ // Strict enforcement, only valid descriptor properties allowed.
|
|
174
|
+
const valid = ['configurable', 'enumerable'];
|
|
175
|
+
if (!accessor) valid.push('writable');
|
|
176
|
+
for (const prop of props)
|
|
177
|
+
{
|
|
178
|
+
if (!valid.includes(prop)) return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else
|
|
182
|
+
{ // Loose enforcement mode, reject on forbidden properties only.
|
|
183
|
+
const forbidden = ['value','get','set'];
|
|
184
|
+
if (accessor) forbidden.push('writable');
|
|
185
|
+
for (const prop of props)
|
|
186
|
+
{
|
|
187
|
+
if (forbidden.includes(prop)) return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// No tests failed, this can be used as a template.
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Now export those.
|
|
196
|
+
module.exports =
|
|
197
|
+
{
|
|
198
|
+
isObj, isComplex, isNil, notNil, isScalar, isArray, isTypedArray,
|
|
199
|
+
nonEmptyArray, isArguments, isProperty, doesDescriptor, doesDescriptorTemplate,
|
|
200
|
+
}
|
package/lib/types/def.js
CHANGED
|
@@ -4,6 +4,9 @@ const {F, B} = require('./js');
|
|
|
4
4
|
const {isObj, isNil, isProperty, doesDescriptor} = require('./basics');
|
|
5
5
|
const copy = require('../obj/copyall');
|
|
6
6
|
|
|
7
|
+
// A really really cheap version of clone().
|
|
8
|
+
const clone = obj => copy({}, obj);
|
|
9
|
+
|
|
7
10
|
/**
|
|
8
11
|
* A wrapper around `Object.defineProperty()`.
|
|
9
12
|
*
|
|
@@ -12,16 +15,22 @@ const copy = require('../obj/copyall');
|
|
|
12
15
|
*
|
|
13
16
|
* @param {(object|function)} obj - The object to add a property to.
|
|
14
17
|
* @param {?(string|boolean|object)} name - If a `string`, the property name.
|
|
18
|
+
* Property names may also be `symbol` values.
|
|
15
19
|
*
|
|
16
20
|
* If this is `null` or `undefined` then the `value` is ignored entirely,
|
|
17
21
|
* and instead a bound version of this function is created with the
|
|
18
|
-
* `obj` already passed as the first parameter
|
|
19
|
-
*
|
|
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.
|
|
20
25
|
*
|
|
21
26
|
* If this is a `boolean`, then the same logic as if it was `null` or
|
|
22
27
|
* `undefined` will apply, except that an `enumerable` property with this
|
|
23
28
|
* value will also be added to the descriptors.
|
|
24
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
|
+
*
|
|
25
34
|
* @param {*} value - Used to determine the value of the property.
|
|
26
35
|
*
|
|
27
36
|
* If it is an a valid descriptor object (as per `doesDescriptor()`),
|
|
@@ -29,31 +38,29 @@ const copy = require('../obj/copyall');
|
|
|
29
38
|
* property defined, one will be added, and will be set to `true`.
|
|
30
39
|
* This behaviour may be overridden by the `opts` parameter.
|
|
31
40
|
*
|
|
32
|
-
* If this
|
|
33
|
-
*
|
|
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*.
|
|
34
43
|
*
|
|
35
44
|
* Any other value passed here will be used as the `value` in a
|
|
36
45
|
* pre-canned descriptor, also with `configurable` set to `true`.
|
|
37
46
|
*
|
|
38
|
-
* @param {
|
|
47
|
+
* @param {*} [opts] - A multi-purpose option.
|
|
39
48
|
*
|
|
40
49
|
* If this is an `object` then it is reserved for named options.
|
|
41
|
-
*
|
|
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.
|
|
42
53
|
*
|
|
43
54
|
* If `value` and this are both `function` values, then `value` will
|
|
44
55
|
* be used as a *getter* and this will be used as a *setter*.
|
|
45
56
|
*
|
|
46
|
-
* If `value` is a `function` and this is `true`, then `value` will
|
|
47
|
-
* be used as a *getter* and no *setter* will be assigned.
|
|
48
|
-
*
|
|
49
|
-
* If `value` is a `function` and this is `false`, then `value` will
|
|
50
|
-
* be used as a *setter* and no *getter* will be assigned.
|
|
51
|
-
*
|
|
52
57
|
* If `value` is a valid descriptor object, then setting this to `false`
|
|
53
58
|
* will disable the assumption that it is the descriptor to set.
|
|
54
59
|
* Setting this to `true` on will instruct the function to make a clone
|
|
55
60
|
* of the descriptor object before modifying any properties in it.
|
|
56
61
|
*
|
|
62
|
+
* This defaults to `undefined`, except if
|
|
63
|
+
*
|
|
57
64
|
* @returns {*} Normally the `obj` argument with new property added.
|
|
58
65
|
*
|
|
59
66
|
* The exception is if this is a bound copy of the function created
|
|
@@ -66,6 +73,7 @@ const copy = require('../obj/copyall');
|
|
|
66
73
|
* def(myObj)('name', "Bob")('age', 42);
|
|
67
74
|
* ```
|
|
68
75
|
*
|
|
76
|
+
* @alias module:@lumjs/core/types.def
|
|
69
77
|
*/
|
|
70
78
|
function def(obj, name, value, opts)
|
|
71
79
|
{
|
|
@@ -132,17 +140,12 @@ function def(obj, name, value, opts)
|
|
|
132
140
|
|
|
133
141
|
if (opts !== false && doesDescriptor(value))
|
|
134
142
|
{ // The value is a descriptor, let's use it.
|
|
135
|
-
desc = (opts === true) ?
|
|
143
|
+
desc = (opts === true) ? clone(value) : value;
|
|
136
144
|
}
|
|
137
145
|
else if (typeof value === F && typeof opts === F)
|
|
138
146
|
{ // A getter and setter were both specified.
|
|
139
147
|
desc = {get: value, set: opts};
|
|
140
148
|
}
|
|
141
|
-
else if (typeof value === F && typeof opts === B)
|
|
142
|
-
{ // A function value with a boolean opts is a getter or setter.
|
|
143
|
-
const prop = opts ? 'get' : 'set';
|
|
144
|
-
desc = {[prop]: value};
|
|
145
|
-
}
|
|
146
149
|
else
|
|
147
150
|
{ // The value is just a value, so let's assign it.
|
|
148
151
|
desc = {value};
|
package/lib/types/index.js
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
|
|
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
|
+
*/
|
|
2
18
|
|
|
3
19
|
// Constants representing core Javascript types.
|
|
4
20
|
const {O, F, S, B, N, U, SY, BI} = require('./js');
|
|
@@ -8,6 +24,7 @@ const
|
|
|
8
24
|
{
|
|
9
25
|
isObj, isComplex, isNil, notNil, isScalar, isArray, isTypedArray,
|
|
10
26
|
nonEmptyArray, isArguments, isProperty, doesDescriptor,
|
|
27
|
+
doesDescriptorTemplate,
|
|
11
28
|
} = require('./basics');
|
|
12
29
|
|
|
13
30
|
// Root namespace helpers.
|
|
@@ -23,6 +40,7 @@ const {needObj, needType, needs} = require('./needs');
|
|
|
23
40
|
|
|
24
41
|
const def = require('./def');
|
|
25
42
|
const TYPES = require('./typelist');
|
|
43
|
+
const stringify = require('./stringify');
|
|
26
44
|
|
|
27
45
|
// Okay, add all those to our exports.
|
|
28
46
|
// Further tests can be added by `TYPES.add()` later.
|
|
@@ -31,7 +49,8 @@ module.exports =
|
|
|
31
49
|
O, F, S, B, N, U, SY, BI, TYPES, root, unbound, def,
|
|
32
50
|
isObj, isComplex, isNil, notNil, isScalar, isArray, isTypedArray,
|
|
33
51
|
nonEmptyArray, isArguments, isProperty, doesDescriptor,
|
|
34
|
-
isInstance, isType, isa, needObj, needType, needs,
|
|
52
|
+
isInstance, isType, isa, needObj, needType, needs, stringify,
|
|
53
|
+
doesDescriptorTemplate,
|
|
35
54
|
}
|
|
36
55
|
|
|
37
56
|
// Last but not least, this will be the module for TYPES.add()
|
package/lib/types/isa.js
CHANGED
|
@@ -8,6 +8,7 @@ const TYPES = require('./typelist');
|
|
|
8
8
|
* @param {function} f - The constructor/class we want.
|
|
9
9
|
* @param {boolean} [needProto=false] If true, the `v` must have a `prototype`.
|
|
10
10
|
* @returns {boolean}
|
|
11
|
+
* @alias module:@lumjs/core/types.isInstance
|
|
11
12
|
*/
|
|
12
13
|
function isInstance(v, f, needProto=false)
|
|
13
14
|
{
|
|
@@ -30,24 +31,15 @@ const TYPES = require('./typelist');
|
|
|
30
31
|
*
|
|
31
32
|
* @param {string} type - The type we're checking for.
|
|
32
33
|
*
|
|
33
|
-
* This supports all the same type names as `typeof` plus
|
|
34
|
-
*
|
|
35
|
-
* - `arguments` → An arguments object inside a function.
|
|
36
|
-
* - `array` → An Array object.
|
|
37
|
-
* - `null` → A null value.
|
|
38
|
-
* - `typedarray` → One of the typed array objects.
|
|
39
|
-
* - `descriptor` → An object which is a valid descriptor.
|
|
40
|
-
* - `complex` → Either an `object` or a `function.
|
|
41
|
-
* - `scalar` → Anything other than an `object` or `function`.
|
|
42
|
-
* - `property` → A `string` or a `symbol`.
|
|
43
|
-
* - `nil` → Either `null` or `undefined`.
|
|
34
|
+
* This supports all the same type names as `typeof` plus any of
|
|
35
|
+
* the properties defined in the `TYPES` object.
|
|
44
36
|
*
|
|
45
37
|
* One other thing, while `typeof` reports `null` as being an `object`,
|
|
46
38
|
* this function does not count `null` as a valid `object`.
|
|
47
39
|
*
|
|
48
40
|
* @param {*} v - The value we're testing.
|
|
49
|
-
*
|
|
50
41
|
* @returns {boolean} If the value was of the desired type.
|
|
42
|
+
* @alias module:@lumjs/core/types.isType
|
|
51
43
|
*/
|
|
52
44
|
function isType(type, v)
|
|
53
45
|
{
|
|
@@ -138,6 +130,7 @@ const TYPES = require('./typelist');
|
|
|
138
130
|
* Any other type value will only match if `v === type`
|
|
139
131
|
*
|
|
140
132
|
* @returns {boolean} Was the value one of the desired types?
|
|
133
|
+
* @alias module:@lumjs/core/types.isa
|
|
141
134
|
*/
|
|
142
135
|
function isa(v, ...types)
|
|
143
136
|
{
|
package/lib/types/needs.js
CHANGED
|
@@ -9,6 +9,7 @@ const {isObj, isComplex} = require('./basics');
|
|
|
9
9
|
* @param {boolean} [allowFunc=false] - Also accept functions?
|
|
10
10
|
* @param {string} [msg] A custom error message.
|
|
11
11
|
* @throws {TypeError} If the type check failed.
|
|
12
|
+
* @alias module:@lumjs/core/types.needObj
|
|
12
13
|
*/
|
|
13
14
|
function needObj (v, allowFunc=false, msg=null)
|
|
14
15
|
{
|
|
@@ -33,6 +34,7 @@ exports.needObj = needObj;
|
|
|
33
34
|
* @param {*} v - The value we're testing.
|
|
34
35
|
* @param {string} [msg] A custom error message.
|
|
35
36
|
* @throws {TypeError} If the type check failed.
|
|
37
|
+
* @alias module:@lumjs/core/types.needType
|
|
36
38
|
*/
|
|
37
39
|
function needType (type, v, msg, unused)
|
|
38
40
|
{
|
|
@@ -77,7 +79,10 @@ const NEEDS_PARSER = function(type, v)
|
|
|
77
79
|
* look for an `object` with a single property named `error`
|
|
78
80
|
* which can be either a `string` or any subclass of `Error`.
|
|
79
81
|
* If specified, it will override the error message that will be thrown.
|
|
80
|
-
*
|
|
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
|
|
81
86
|
*/
|
|
82
87
|
function needs(v, ...types)
|
|
83
88
|
{
|
package/lib/types/root.js
CHANGED
|
@@ -9,6 +9,7 @@ function no_root()
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* The global root object. Usually `globalThis` these days.
|
|
12
|
+
* @alias module:@lumjs/core/types.root
|
|
12
13
|
*/
|
|
13
14
|
const root = typeof globalThis !== U ? globalThis
|
|
14
15
|
: typeof global !== U ? global
|
|
@@ -32,6 +33,7 @@ const unboundObjects = [];
|
|
|
32
33
|
* If the is `true` we use an global list that can register special
|
|
33
34
|
* internal objects. Otherwise an `Array` of unbound objects may be used.
|
|
34
35
|
* @returns {boolean}
|
|
36
|
+
* @alias module:@lumjs/core/types.unbound
|
|
35
37
|
*/
|
|
36
38
|
function unbound(whatIsThis, rootIsUnbound=true, areUnbound=false)
|
|
37
39
|
{
|
|
@@ -56,13 +58,11 @@ const def = require('./def');
|
|
|
56
58
|
/**
|
|
57
59
|
* Add an item to the unbound global objects list.
|
|
58
60
|
*
|
|
59
|
-
* @
|
|
60
|
-
*
|
|
61
|
+
* @function
|
|
61
62
|
* @param {(object|function)} obj - The object to be considered unbound.
|
|
62
|
-
*
|
|
63
63
|
* @returns {boolean} Will be `false` if `obj` is already unbound.
|
|
64
|
-
*
|
|
65
64
|
* @throws {TypeError} If `obj` was neither an `object` nor a `function`.
|
|
65
|
+
* @alias module:@lumjs/core/types.unbound.add
|
|
66
66
|
*/
|
|
67
67
|
def(unbound, 'add', function (obj)
|
|
68
68
|
{
|
|
@@ -79,13 +79,11 @@ def(unbound, 'add', function (obj)
|
|
|
79
79
|
/**
|
|
80
80
|
* Remove an item from the unbound global objects list.
|
|
81
81
|
*
|
|
82
|
-
* @
|
|
83
|
-
*
|
|
82
|
+
* @function
|
|
84
83
|
* @param {(object|function)} obj - The object to be removed.
|
|
85
|
-
*
|
|
86
84
|
* @returns {boolean} Will be `false` if the item was not in the list.
|
|
87
|
-
*
|
|
88
85
|
* @throws {TypeError} If `obj` was neither an `object` nor a `function`.
|
|
86
|
+
* @alias module:@lumjs/core/types.unbound.remove
|
|
89
87
|
*/
|
|
90
88
|
def(unbound, 'remove', function(obj)
|
|
91
89
|
{
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Get the extended type list.
|
|
2
|
+
const TYPES = require('./typelist');
|
|
3
|
+
const {isObj, isArray, isTypedArray} = require('./basics');
|
|
4
|
+
const def = require('./def');
|
|
5
|
+
|
|
6
|
+
const TOSTRING_TYPES = [TYPES.F, TYPES.SY];
|
|
7
|
+
const TOSTRING_INSTANCES = [RegExp];
|
|
8
|
+
const CUSTOM = [];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Stringify a Javascript value.
|
|
12
|
+
*
|
|
13
|
+
* We typically just use `JSON.stringify()` but that doesn't work on
|
|
14
|
+
* some types. So this function adds string formats for:
|
|
15
|
+
* - `function`
|
|
16
|
+
* - `symbol`
|
|
17
|
+
* - `TypedArray`
|
|
18
|
+
* - `Map`
|
|
19
|
+
* - `Set`
|
|
20
|
+
* - `Error`
|
|
21
|
+
* I may add even more extended types in the future, but that's enough
|
|
22
|
+
* for now.
|
|
23
|
+
*
|
|
24
|
+
* This is NOT meant for serializing data, and does not use a JSON-friendly
|
|
25
|
+
* output format. I'm writing a different library for that.
|
|
26
|
+
*
|
|
27
|
+
* @param {*} what - The value to stringify.
|
|
28
|
+
* @param {integer} [recurse=1] Recurse objects to this depth.
|
|
29
|
+
* @param {boolean} [addNew=false] Use 'new Class()' instead of 'Class()'.
|
|
30
|
+
* @returns {string} The stringified value.
|
|
31
|
+
* @alias module:@lumjs/core/types.stringify
|
|
32
|
+
*/
|
|
33
|
+
function stringify (what, recurse=1, addNew=false)
|
|
34
|
+
{
|
|
35
|
+
const whatType = typeof what;
|
|
36
|
+
|
|
37
|
+
for (const test of CUSTOM)
|
|
38
|
+
{ // If there are custom extensions, we check them first.
|
|
39
|
+
const ret = test.call({stringify}, what, recurse, addNew);
|
|
40
|
+
if (typeof ret === TYPES.S)
|
|
41
|
+
{ // The extension processed the item.
|
|
42
|
+
return ret;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// A few types we simply stringify right now.
|
|
47
|
+
if (TOSTRING_TYPES.includes(whatType)) return what.toString();
|
|
48
|
+
|
|
49
|
+
if (isObj(what))
|
|
50
|
+
{ // We support a few kinds of objects.
|
|
51
|
+
|
|
52
|
+
// Any class instance that we can simply call `toString()` on, let's do that.
|
|
53
|
+
for (const aClass of TOSTRING_INSTANCES)
|
|
54
|
+
{
|
|
55
|
+
if (what instanceof aClass)
|
|
56
|
+
{
|
|
57
|
+
return what.toString();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// A few formatting helpers used below.
|
|
62
|
+
const classname = () => (addNew ? 'new ' : '') + what.constructor.name;
|
|
63
|
+
const construct = val => `${classname()}(${val})`;
|
|
64
|
+
const reconstruct = val => construct(stringify(val,recurse,addNew));
|
|
65
|
+
const arrayish = vals => reconstruct(Array.from(vals));
|
|
66
|
+
|
|
67
|
+
if (isTypedArray(what))
|
|
68
|
+
{ // This one is pretty simple.
|
|
69
|
+
return construct(what.toString());
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (what instanceof Map)
|
|
73
|
+
{
|
|
74
|
+
return arrayish(what.entries());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (what instanceof Set)
|
|
78
|
+
{
|
|
79
|
+
return arrayish(what.values());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (what instanceof Error)
|
|
83
|
+
{
|
|
84
|
+
return `${what.name}(${JSON.stringify(what.message)})`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (recurse)
|
|
88
|
+
{ // Recursion mode enabled.
|
|
89
|
+
let out = '';
|
|
90
|
+
if (isArray(what))
|
|
91
|
+
{ // Stringify an array.
|
|
92
|
+
out = '[';
|
|
93
|
+
out += what.map(item => stringify(item, recurse-1, addNew)).join(',');
|
|
94
|
+
out += ']';
|
|
95
|
+
}
|
|
96
|
+
else
|
|
97
|
+
{ // Stringify a plain object.
|
|
98
|
+
out = '{';
|
|
99
|
+
function add(key, pre='')
|
|
100
|
+
{
|
|
101
|
+
out += `${pre}${key}:${stringify(what[key], recurse-1, addNew)}`
|
|
102
|
+
}
|
|
103
|
+
const keys = Object.keys(what);
|
|
104
|
+
//console.debug("keys!", keys);
|
|
105
|
+
if (keys.length > 0)
|
|
106
|
+
{ // Let's add the first key, then all subsequent keys.
|
|
107
|
+
add(keys.shift());
|
|
108
|
+
for (const key of keys)
|
|
109
|
+
{
|
|
110
|
+
add(key, ',');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
out += '}';
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
} // if isObj
|
|
119
|
+
|
|
120
|
+
// If we reached here, there's no special methods, use JSON.
|
|
121
|
+
return JSON.stringify(what);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Add a custom extension.
|
|
125
|
+
def(stringify, '$extend',
|
|
126
|
+
function(func, registration=false)
|
|
127
|
+
{
|
|
128
|
+
if (typeof func === TYPES.F)
|
|
129
|
+
{
|
|
130
|
+
if (registration)
|
|
131
|
+
{ // Using the function to register custom behaviour.
|
|
132
|
+
func.call({stringify, TOSTRING_INSTANCES, TOSTRING_TYPES}, CUSTOM);
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
{ // The function is a custom test.
|
|
136
|
+
CUSTOM.push(func);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Export it.
|
|
142
|
+
module.exports = stringify;
|
package/lib/types/typelist.js
CHANGED
|
@@ -7,12 +7,76 @@ const
|
|
|
7
7
|
} = require('./basics');
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* A map of
|
|
10
|
+
* A map of **Types**, including *special* and *union* types.
|
|
11
|
+
*
|
|
12
|
+
* Contains the same `O, F, S, B, N, U, SY, BI` properties as also
|
|
13
|
+
* found in the top-level {@link module:@lumjs/core/types} module.
|
|
14
|
+
* While most of the core JS types simply use `typeof` as their
|
|
15
|
+
* test, this maps the `O` (`object`) type to the `isObj` test.
|
|
16
|
+
*
|
|
11
17
|
* Will also contain a few helper functions, and a map of tests
|
|
12
|
-
*
|
|
18
|
+
* that are used by `isType`, `isa`, `needType`, and `needs`.
|
|
19
|
+
* Any one of these properties may be passed to those functions as
|
|
20
|
+
* the desired *type* a desired value must be.
|
|
21
|
+
*
|
|
22
|
+
* We will list the types added by the `types` module in
|
|
23
|
+
* the *Properties* table, and any types added by other *core modules*
|
|
24
|
+
* in the *Members* list, prior to listing the *Methods*.
|
|
25
|
+
*
|
|
26
|
+
* @namespace module:@lumjs/core/types.TYPES
|
|
27
|
+
* @property {string} NULL - Represents `null` values.
|
|
28
|
+
* @property {string} ARGS - Represents an *argument* object.
|
|
29
|
+
* @property {string} PROP - A `string` or a `symbol`.
|
|
30
|
+
* @property {string} ARRAY - An `Array` object.
|
|
31
|
+
* @property {string} TYPEDARRAY - A `TypedArray` object.
|
|
32
|
+
* @property {string} DESCRIPTOR - A *Descriptor* object (Data or Accessor).
|
|
33
|
+
* @property {string} COMPLEX - A `function` or an `object`.
|
|
34
|
+
* @property {string} SCALAR - A non-null value that is **not** *complex*.
|
|
35
|
+
* @property {string} NIL - Either `null` or `undefined`.
|
|
36
|
+
* @property {string} NOTNIL - Anything other than `null` or `undefined`.
|
|
37
|
+
* @property {string} MAP - A `Map` object.
|
|
38
|
+
* @property {object} tests - A map of tests for the above types.
|
|
13
39
|
*/
|
|
14
40
|
const TYPES = {};
|
|
15
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Add a new type to the `TYPES`.
|
|
44
|
+
* @name module:@lumjs/core/types.TYPES.add
|
|
45
|
+
* @function
|
|
46
|
+
* @param {(string|object)} name - If a `string` the property name to use.
|
|
47
|
+
* When adding the property the string will be forced to uppercase.
|
|
48
|
+
*
|
|
49
|
+
* If an `object` then its a shortcut for adding a bunch of types at once.
|
|
50
|
+
* Each key will be the `name`, and the type of the *value* can be one of:
|
|
51
|
+
* - `string` → Use as the `ident` parameter.
|
|
52
|
+
* - `function` → Use as the `test` parameter.
|
|
53
|
+
* - `object` → Supports `id`, `test`, and `export` parameters.
|
|
54
|
+
*
|
|
55
|
+
* @param {?string} [ident] The identifier string for the type.
|
|
56
|
+
* If not specified or `null`, it will default to a completely lowercase
|
|
57
|
+
* version of the `name` parameter.
|
|
58
|
+
* @param {function} [test] A type check test.
|
|
59
|
+
* Must accept a single value to test, must return a boolean.
|
|
60
|
+
* @param {string} [exportTest] A name to export the test as.
|
|
61
|
+
* The test will be added to the `types` module with this name.
|
|
62
|
+
*
|
|
63
|
+
* @returns {void}
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get a list of type properties available in `TYPES`.
|
|
68
|
+
* @name module:@lumjs/core/types.TYPES.keys
|
|
69
|
+
* @function
|
|
70
|
+
* @returns {string[]}
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get a list of type identifier values available in `TYPES`.
|
|
75
|
+
* @name module:@lumjs/core/types.TYPES.list
|
|
76
|
+
* @function
|
|
77
|
+
* @returns {string[]}
|
|
78
|
+
*/
|
|
79
|
+
|
|
16
80
|
// Let's setup the TYPES with its magic functions.
|
|
17
81
|
const dt = def(TYPES);
|
|
18
82
|
dt('tests', {})
|