@lumjs/core 1.4.0 → 1.5.1
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/docs/changelogs/1.x.md +20 -1
- package/lib/index.js +111 -62
- package/lib/meta.js +60 -7
- package/lib/types/def.js +102 -58
- package/lib/types/lazy.js +18 -1
- package/package.json +1 -1
package/docs/changelogs/1.x.md
CHANGED
|
@@ -6,6 +6,23 @@ See [Changelogs](index.md) for more information on the changelogs.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.5.1] - 2022-09-29
|
|
10
|
+
### Changed
|
|
11
|
+
- Removed `constructor()` from `AbstractClass`.
|
|
12
|
+
- Added `$needs()` to `AbstractClass`.
|
|
13
|
+
### Fixed
|
|
14
|
+
- `AbstractClass` actually works properly now.
|
|
15
|
+
|
|
16
|
+
## [1.5.0] - 2022-09-27
|
|
17
|
+
### Changed
|
|
18
|
+
- Updated a few DocBlocks.
|
|
19
|
+
- Added a couple explicitly named options to `def()`
|
|
20
|
+
- Rewrote the default module to use `def()` to define its exported properties.
|
|
21
|
+
- Made `arrays`, `strings`, `flags`, `opt`, `modules`, and `observable` use `lazy()`.
|
|
22
|
+
### Fixed
|
|
23
|
+
- Fixed `def()` so that the descriptor defaults are applied properly.
|
|
24
|
+
- Fixed `lazy()` so it returns a proper value and not a descriptor on first access.
|
|
25
|
+
|
|
9
26
|
## [1.4.0] - 2022-09-26
|
|
10
27
|
### Changed
|
|
11
28
|
- Moved `lazy` into `types` module by default (leaving an alias in `core`).
|
|
@@ -81,7 +98,9 @@ See [Changelogs](index.md) for more information on the changelogs.
|
|
|
81
98
|
- See [1.0-beta.md](1.0-beta.md) for the beta versions of `1.0`
|
|
82
99
|
- See [lum.js](https://github.com/supernovus/lum.js) for the original library set this is replacing.
|
|
83
100
|
|
|
84
|
-
[Unreleased]: https://github.com/supernovus/lum.core.js/compare/v1.
|
|
101
|
+
[Unreleased]: https://github.com/supernovus/lum.core.js/compare/v1.5.1...HEAD
|
|
102
|
+
[1.5.1]: https://github.com/supernovus/lum.core.js/compare/v1.5.0...v1.5.1
|
|
103
|
+
[1.5.0]: https://github.com/supernovus/lum.core.js/compare/v1.4.0...v1.5.0
|
|
85
104
|
[1.4.0]: https://github.com/supernovus/lum.core.js/compare/v1.3.1...v1.4.0
|
|
86
105
|
[1.3.1]: https://github.com/supernovus/lum.core.js/compare/v1.3.0...v1.3.1
|
|
87
106
|
[1.3.0]: https://github.com/supernovus/lum.core.js/compare/v1.2.1...v1.3.0
|
package/lib/index.js
CHANGED
|
@@ -4,89 +4,161 @@
|
|
|
4
4
|
* This is the foundation upon which all the rest of my JS libraries
|
|
5
5
|
* are built. It provides fundamental helper functions and classes.
|
|
6
6
|
*
|
|
7
|
+
* Properties marked as «Lazy» will be loaded on-demand the first time
|
|
8
|
+
* the property is accessed. All other properties are always loaded.
|
|
9
|
+
*
|
|
7
10
|
* @module @lumjs/core
|
|
8
11
|
*/
|
|
9
12
|
|
|
10
13
|
/**
|
|
11
|
-
* Constants and functions for
|
|
14
|
+
* Constants and functions for type checks
|
|
15
|
+
* and other fundamental functions
|
|
16
|
+
*
|
|
12
17
|
* @alias module:@lumjs/core.types
|
|
13
18
|
* @see module:@lumjs/core/types
|
|
14
19
|
*/
|
|
15
20
|
const types = require('./types');
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
|
-
*
|
|
19
|
-
* @
|
|
20
|
-
* @
|
|
23
|
+
* Define properties on an object or function
|
|
24
|
+
* @name module:@lumjs/core.def
|
|
25
|
+
* @function
|
|
26
|
+
* @see module:@lumjs/core/types.def
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Define properties on an object or function
|
|
31
|
+
* @name module:@lumjs/core.lazy
|
|
32
|
+
* @function
|
|
33
|
+
* @see module:@lumjs/core/types.lazy
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
// Get a descriptor for one of our sub-modules.
|
|
37
|
+
function lib(name, def={})
|
|
38
|
+
{
|
|
39
|
+
let value = def.value;
|
|
40
|
+
|
|
41
|
+
if (value === undefined)
|
|
42
|
+
{
|
|
43
|
+
const module = def.module ?? name;
|
|
44
|
+
value = require('./'+module);
|
|
45
|
+
|
|
46
|
+
if (value && def.prop && value[def.prop] !== undefined)
|
|
47
|
+
{
|
|
48
|
+
value = value[def.prop];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const desc =
|
|
53
|
+
{
|
|
54
|
+
configurable: false,
|
|
55
|
+
enumerable: true,
|
|
56
|
+
writable: false,
|
|
57
|
+
value,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return desc;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Export one of our always loaded properties.
|
|
64
|
+
function has(name, def)
|
|
65
|
+
{
|
|
66
|
+
types.def(exports, name, lib(name, def));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Export one of our lazy loaded properties.
|
|
70
|
+
function can(name, def)
|
|
71
|
+
{
|
|
72
|
+
types.lazy(exports, name, () => lib(name, def), {enumerable: true});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Export a set of always loaded properties from a sub-module
|
|
76
|
+
function from(modname, ...libs)
|
|
77
|
+
{
|
|
78
|
+
for (const lib of libs)
|
|
79
|
+
{
|
|
80
|
+
has(lib, {module: modname, prop: lib});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Our fundamental bits.
|
|
85
|
+
has('types', {value: types});
|
|
86
|
+
has('def', {value: types.def});
|
|
87
|
+
has('lazy', {value: types.lazy});
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Array utility functions «Lazy»
|
|
91
|
+
* @name module:@lumjs/core.arrays
|
|
92
|
+
* @type {module:@lumjs/core/arrays}
|
|
21
93
|
*/
|
|
22
|
-
|
|
94
|
+
can('arrays');
|
|
23
95
|
|
|
24
96
|
/**
|
|
25
|
-
* Information about the JS context we're running in
|
|
26
|
-
* @
|
|
27
|
-
* @
|
|
97
|
+
* Information about the JS context we're running in
|
|
98
|
+
* @name module:@lumjs/core.context
|
|
99
|
+
* @type {module:@lumjs/core/context}
|
|
28
100
|
*/
|
|
29
|
-
|
|
101
|
+
has('context');
|
|
30
102
|
|
|
31
103
|
/**
|
|
32
|
-
* Functions for working with strings and locales
|
|
33
|
-
* @
|
|
34
|
-
* @
|
|
104
|
+
* Functions for working with strings and locales «Lazy»
|
|
105
|
+
* @name module:@lumjs/core.strings
|
|
106
|
+
* @type {module:@lumjs/core/strings}
|
|
35
107
|
*/
|
|
36
|
-
|
|
108
|
+
can('strings');
|
|
37
109
|
|
|
38
110
|
/**
|
|
39
|
-
* Functions for working with binary flags
|
|
40
|
-
* @
|
|
41
|
-
* @
|
|
111
|
+
* Functions for working with binary flags «Lazy»
|
|
112
|
+
* @name module:@lumjs/core.flags
|
|
113
|
+
* @type {module:@lumjs/core/flags}
|
|
42
114
|
*/
|
|
43
|
-
|
|
115
|
+
can('flags');
|
|
44
116
|
|
|
45
117
|
/**
|
|
46
|
-
* Functions for manipulating objects
|
|
47
|
-
* @
|
|
48
|
-
* @
|
|
118
|
+
* Functions for manipulating objects
|
|
119
|
+
* @name module:@lumjs/core.obj
|
|
120
|
+
* @type {module:@lumjs/core/obj}
|
|
49
121
|
*/
|
|
50
|
-
|
|
122
|
+
has('obj');
|
|
51
123
|
|
|
52
124
|
/**
|
|
53
|
-
* Functions for getting values and properties with fallback defaults
|
|
54
|
-
* @
|
|
55
|
-
* @
|
|
125
|
+
* Functions for getting values and properties with fallback defaults «Lazy»
|
|
126
|
+
* @name module:@lumjs/core.opt
|
|
127
|
+
* @type {module:@lumjs/core/opt}
|
|
56
128
|
*/
|
|
57
|
-
|
|
129
|
+
can('opt');
|
|
58
130
|
|
|
59
131
|
/**
|
|
60
|
-
* Meta functions related to JS modules
|
|
132
|
+
* Meta functions related to JS modules «Lazy»
|
|
61
133
|
* @alias module:@lumjs/core.modules
|
|
62
134
|
* @see module:@lumjs/core/modules
|
|
63
135
|
*/
|
|
64
|
-
|
|
136
|
+
can('modules');
|
|
65
137
|
|
|
66
138
|
// ObjectID stuff is imported directly without registering a sub-module.
|
|
67
|
-
|
|
139
|
+
from('objectid', 'randomNumber', 'InternalObjectId');
|
|
68
140
|
|
|
69
141
|
/**
|
|
70
|
-
* Get a simplistic debugging stacktrace
|
|
142
|
+
* Get a simplistic debugging stacktrace
|
|
71
143
|
* @name module:@lumjs/core.stacktrace
|
|
72
144
|
* @function
|
|
73
145
|
* @see module:@lumjs/core/meta.stacktrace
|
|
74
146
|
*/
|
|
75
147
|
|
|
76
148
|
/**
|
|
77
|
-
* A simple base class for making *abstract* classes
|
|
149
|
+
* A simple base class for making *abstract* classes
|
|
78
150
|
* @name module:@lumjs/core.AbstractClass
|
|
79
151
|
* @see module:@lumjs/core/meta.AbstractClass
|
|
80
152
|
*/
|
|
81
153
|
|
|
82
154
|
/**
|
|
83
|
-
* A *factory* for special types of JS `function` constructors
|
|
155
|
+
* A *factory* for special types of JS `function` constructors
|
|
84
156
|
* @name module:@lumjs/core.Functions
|
|
85
157
|
* @see module:@lumjs/core/meta.Functions
|
|
86
158
|
*/
|
|
87
159
|
|
|
88
160
|
/**
|
|
89
|
-
* Throw an `Error` saying that a feature is not yet implemented
|
|
161
|
+
* Throw an `Error` saying that a feature is not yet implemented
|
|
90
162
|
* @name module:@lumjs/core.NYI
|
|
91
163
|
* @function
|
|
92
164
|
* @see module:@lumjs/core/meta.NYI
|
|
@@ -94,43 +166,20 @@ const {randomNumber, InternalObjectId} = require('./objectid');
|
|
|
94
166
|
|
|
95
167
|
// These are exported directly, but a meta sub-module also exists.
|
|
96
168
|
// Unlike most sub-modules there is no `meta` property in the main library.
|
|
97
|
-
|
|
169
|
+
from('meta', 'stacktrace', 'AbstractClass', 'Functions', 'NYI');
|
|
98
170
|
|
|
99
171
|
/**
|
|
100
|
-
* Create a magic *Enum* object
|
|
101
|
-
* @
|
|
172
|
+
* Create a magic *Enum* object
|
|
173
|
+
* @name module:@lumjs/core.Enum
|
|
102
174
|
* @function
|
|
103
175
|
* @see module:@lumjs/core/enum
|
|
104
176
|
*/
|
|
105
|
-
|
|
177
|
+
has('Enum', {module: 'enum'});
|
|
106
178
|
|
|
107
179
|
/**
|
|
108
|
-
* Make an object support the *Observable* API
|
|
109
|
-
* @
|
|
180
|
+
* Make an object support the *Observable* API «Lazy»
|
|
181
|
+
* @name module:@lumjs/core.observable
|
|
110
182
|
* @function
|
|
111
183
|
* @see module:@lumjs/core/observable
|
|
112
184
|
*/
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Define properties on an object or function.
|
|
117
|
-
* @alias module:@lumjs/core.def
|
|
118
|
-
* @function
|
|
119
|
-
* @see module:@lumjs/core/types.def
|
|
120
|
-
*/
|
|
121
|
-
const def = types.def;
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Define properties on an object or function.
|
|
125
|
-
* @alias module:@lumjs/core.lazy
|
|
126
|
-
* @function
|
|
127
|
-
* @see module:@lumjs/core/types.lazy
|
|
128
|
-
*/
|
|
129
|
-
const lazy = types.lazy;
|
|
130
|
-
|
|
131
|
-
module.exports =
|
|
132
|
-
{
|
|
133
|
-
types, context, flags, obj, opt, modules, arrays, strings,
|
|
134
|
-
def, randomNumber, InternalObjectId, Enum, lazy, observable,
|
|
135
|
-
stacktrace, AbstractClass, Functions, NYI,
|
|
136
|
-
}
|
|
185
|
+
can('observable');
|
package/lib/meta.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* @module @lumjs/core/meta
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
const {F,S,isArray,isa} = require('./types');
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Get a stacktrace. Differs from browser to browser.
|
|
8
10
|
*
|
|
@@ -30,20 +32,71 @@ exports.stacktrace = stacktrace;
|
|
|
30
32
|
class AbstractClass
|
|
31
33
|
{
|
|
32
34
|
/**
|
|
33
|
-
*
|
|
35
|
+
* If you want to mark a method as abstract use this.
|
|
34
36
|
*/
|
|
35
|
-
|
|
37
|
+
$abstract(name)
|
|
36
38
|
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
if (name.indexOf('(') === -1)
|
|
40
|
+
{ // Add empty method signature.
|
|
41
|
+
name += '()';
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`Abstract method ${name} was not implemented`);
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
/**
|
|
42
|
-
*
|
|
47
|
+
* Check for required properties
|
|
48
|
+
*
|
|
49
|
+
* @param {...(string|Array)} needs - What is needed
|
|
50
|
+
*
|
|
51
|
+
* If this is a `string` it should be in a format like:
|
|
52
|
+
*
|
|
53
|
+
* - `methodName(arg1,arg2,arg3)`
|
|
54
|
+
* - `anotherMethod(number, string, object) : boolean`
|
|
55
|
+
* - `yetAnother (className) : resultClass`
|
|
56
|
+
*
|
|
57
|
+
* The names are case sensitive, and we'll look for the method after
|
|
58
|
+
* stripping off anything from the first *non-word* character.
|
|
59
|
+
*
|
|
60
|
+
* If this is an `Array`, the first item must be the name of a property,
|
|
61
|
+
* and each other item should be a type checking value, or array of type
|
|
62
|
+
* checking values from the [TYPES]{@link module:@lumjs/core/types.TYPES}
|
|
63
|
+
* object, as used by [isa()]{@link module:@lumjs/core/types.isa}.
|
|
64
|
+
*
|
|
65
|
+
* If you are calling this in an abstract class constructor, likely only
|
|
66
|
+
* the method checks will be useful, as the `super()` call must be done
|
|
67
|
+
* *before* any instance property assignments.
|
|
68
|
+
*
|
|
43
69
|
*/
|
|
44
|
-
$
|
|
70
|
+
$needs(...needs)
|
|
45
71
|
{
|
|
46
|
-
|
|
72
|
+
const className = this.constructor.name;
|
|
73
|
+
|
|
74
|
+
const getName = fullName => fullName.replace(/\W.*/, '');
|
|
75
|
+
const missing = propName =>
|
|
76
|
+
{
|
|
77
|
+
throw new Error(`${className} is missing ${propName}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
for (const need of needs)
|
|
81
|
+
{
|
|
82
|
+
if (typeof need === S)
|
|
83
|
+
{ // A simple method
|
|
84
|
+
const meth = getName(need);
|
|
85
|
+
if (typeof this[meth] !== F)
|
|
86
|
+
{
|
|
87
|
+
missing(need);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (isArray(need))
|
|
91
|
+
{
|
|
92
|
+
const prop = getName(need[0]);
|
|
93
|
+
const types = need.slice(1);
|
|
94
|
+
if (!isa(this[prop], ...types))
|
|
95
|
+
{
|
|
96
|
+
missing(need);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
47
100
|
}
|
|
48
101
|
|
|
49
102
|
}
|
package/lib/types/def.js
CHANGED
|
@@ -14,60 +14,83 @@ const clone = obj => copy({}, obj);
|
|
|
14
14
|
* It replaces the `prop()` method from the old Lum.js v4.
|
|
15
15
|
*
|
|
16
16
|
* @param {(object|function)} obj - The object to add a property to.
|
|
17
|
-
* @param {?(string|boolean|object)} name
|
|
18
|
-
*
|
|
17
|
+
* @param {?(string|symbol|boolean|object)} name
|
|
18
|
+
* If a `string` or `symbol`, it's the property name.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
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
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
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
29
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
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
33
|
*
|
|
34
34
|
* @param {*} value - Used to determine the value of the property.
|
|
35
35
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
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
40
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
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
43
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
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
46
|
*
|
|
47
47
|
* @param {*} [opts] - A multi-purpose option.
|
|
48
48
|
*
|
|
49
|
-
*
|
|
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.
|
|
49
|
+
* If this is an `object` then it is reserved for named options.
|
|
53
50
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
51
|
+
* The named options `configurable`, `enumerable`, and `writable`
|
|
52
|
+
* can be used to define default values to the descriptor properties
|
|
53
|
+
* of the same name.
|
|
56
54
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
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.
|
|
55
|
+
* If `value` and this are both `function` values, then `value` will
|
|
56
|
+
* be used as a *getter* and this will be used as a *setter*.
|
|
61
57
|
*
|
|
62
|
-
*
|
|
58
|
+
* If this is `true`, it sets `opts.cloneDesc` to `true`.
|
|
59
|
+
*
|
|
60
|
+
* If this is `false`, it sets `opts.autoDesc` to `false`.
|
|
61
|
+
*
|
|
62
|
+
* This defaults to `undefined`, except in bound functions.
|
|
63
|
+
*
|
|
64
|
+
* @param {boolean} [opts.autoDesc=true] Automatically detect descriptors?
|
|
65
|
+
*
|
|
66
|
+
* If `true` (the default value) then the automatic descriptor detection
|
|
67
|
+
* is used any time the `value` is an `object`.
|
|
68
|
+
*
|
|
69
|
+
* If `false` the descriptor detection is disabled, and we'll always
|
|
70
|
+
* wrap all values in a new internal descriptor.
|
|
71
|
+
*
|
|
72
|
+
* @param {boolean} [opts.cloneDesc=false] Clone descriptors?
|
|
73
|
+
*
|
|
74
|
+
* If `true` then we'll clone detected descriptors before making
|
|
75
|
+
* any changes to them.
|
|
76
|
+
*
|
|
77
|
+
* If `false` (the default value), we'll use passed descriptors
|
|
78
|
+
* directly, so any changes will be made on the original.
|
|
79
|
+
*
|
|
80
|
+
* @param {boolean} [opts.configurable=true] Default `configurable` value
|
|
81
|
+
* @param {boolean} [opts.enumerable=false] Default `enumerable` value
|
|
82
|
+
* @param {boolean} [opts.writable=false] Default `writable` value
|
|
83
|
+
*
|
|
84
|
+
* Only used with *data descriptors* and will be ignored with
|
|
85
|
+
* *accessor* descriptors.
|
|
63
86
|
*
|
|
64
87
|
* @returns {*} Normally the `obj` argument with new property added.
|
|
65
88
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
89
|
+
* The exception is if this is a bound copy of the function created
|
|
90
|
+
* using the syntax described in the `name` parameter documentation.
|
|
91
|
+
* In that case the return value is the bound copy itself.
|
|
92
|
+
* While that might seem strange, it allows for chaining in a way
|
|
93
|
+
* that otherwise would not be possible, take this example:
|
|
71
94
|
*
|
|
72
95
|
* ```
|
|
73
96
|
* def(myObj)('name', "Bob")('age', 42);
|
|
@@ -136,11 +159,31 @@ function def(obj, name, value, opts)
|
|
|
136
159
|
throw new TypeError("Property name must be a string or a Symbol");
|
|
137
160
|
}
|
|
138
161
|
|
|
162
|
+
function getOpt(name, defVal)
|
|
163
|
+
{
|
|
164
|
+
const revVal = !defVal;
|
|
165
|
+
if (isObj(opts) && typeof opts[name] === B)
|
|
166
|
+
{ // Found the option.
|
|
167
|
+
return opts[name];
|
|
168
|
+
}
|
|
169
|
+
else if (opts === revVal)
|
|
170
|
+
{ // A special default rule was found.
|
|
171
|
+
return revVal;
|
|
172
|
+
}
|
|
173
|
+
else
|
|
174
|
+
{ // Return the default.
|
|
175
|
+
return defVal;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const autoDesc = getOpt('autoDesc', true);
|
|
180
|
+
const cloneDesc = getOpt('cloneDesc', false);
|
|
181
|
+
|
|
139
182
|
let desc;
|
|
140
183
|
|
|
141
|
-
if (
|
|
184
|
+
if (autoDesc && doesDescriptor(value))
|
|
142
185
|
{ // The value is a descriptor, let's use it.
|
|
143
|
-
desc =
|
|
186
|
+
desc = cloneDesc ? clone(value) : value;
|
|
144
187
|
}
|
|
145
188
|
else if (typeof value === F && typeof opts === F)
|
|
146
189
|
{ // A getter and setter were both specified.
|
|
@@ -151,27 +194,28 @@ function def(obj, name, value, opts)
|
|
|
151
194
|
desc = {value};
|
|
152
195
|
}
|
|
153
196
|
|
|
154
|
-
|
|
155
|
-
{
|
|
156
|
-
|
|
157
|
-
{
|
|
158
|
-
|
|
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
|
-
}
|
|
197
|
+
const cd = (opt, defval) =>
|
|
198
|
+
{
|
|
199
|
+
if (typeof desc[opt] === B)
|
|
200
|
+
{ // Property was set explicitly.
|
|
201
|
+
return;
|
|
166
202
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
203
|
+
else if (isObj(opts) && typeof opts[opt] === B)
|
|
204
|
+
{ // Default descriptor option specified in `opts`
|
|
205
|
+
desc[opt] = opts[opt];
|
|
206
|
+
}
|
|
207
|
+
else if (typeof defval === B)
|
|
208
|
+
{ // A fallback default.
|
|
209
|
+
desc[opt] = defval;
|
|
173
210
|
}
|
|
174
|
-
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
cd('configurable', true);
|
|
214
|
+
cd('enumerable');
|
|
215
|
+
if (desc.get === undefined && desc.set === undefined)
|
|
216
|
+
{ // Only look for this one on data descriptors.
|
|
217
|
+
cd('writable');
|
|
218
|
+
}
|
|
175
219
|
|
|
176
220
|
// Now after all that, let's actually define the property!
|
|
177
221
|
Object.defineProperty(obj, name, desc);
|
package/lib/types/lazy.js
CHANGED
|
@@ -2,6 +2,7 @@ const def = require('./def');
|
|
|
2
2
|
const {S,F} = require('./js');
|
|
3
3
|
const {COMPLEX} = require('./typelist');
|
|
4
4
|
const {needType,needObj} = require('./needs');
|
|
5
|
+
const {doesDescriptor} = require('./basics');
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Metadata for the property definition.
|
|
@@ -169,8 +170,24 @@ function lazy(target, name, initfunc, opts={})
|
|
|
169
170
|
if (assign)
|
|
170
171
|
{ // Replace the lazy accessor with the returned value.
|
|
171
172
|
def(target, name, value, context.def);
|
|
173
|
+
// Now return the newly assigned value.
|
|
174
|
+
return target[name];
|
|
175
|
+
}
|
|
176
|
+
else if (doesDescriptor(value))
|
|
177
|
+
{ // A descriptor was returned, extract the real value.
|
|
178
|
+
if (typeof value.get === F)
|
|
179
|
+
{
|
|
180
|
+
return value.get();
|
|
181
|
+
}
|
|
182
|
+
else
|
|
183
|
+
{
|
|
184
|
+
return value.value;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
else
|
|
188
|
+
{ // Just return the original value.
|
|
189
|
+
return value;
|
|
172
190
|
}
|
|
173
|
-
return value;
|
|
174
191
|
}
|
|
175
192
|
|
|
176
193
|
desc.get = function()
|