@lumjs/core 1.2.1 → 1.3.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.0-beta.md +3 -9
- package/docs/changelogs/1.x.md +36 -7
- package/docs/changelogs/index.md +19 -0
- package/lib/arrays.js +4 -0
- package/lib/enum.js +14 -8
- package/lib/index.js +2 -2
- package/lib/obj/clone.js +224 -69
- package/lib/obj/copyprops.js +8 -3
- package/lib/obj/getproperty.js +2 -1
- package/lib/strings.js +1 -2
- package/package.json +1 -1
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
All notable changes to this project will be documented in this file.
|
|
1
|
+
# Changelog → 1.0-beta.x
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
3
|
+
This is the changelog for the `1.0-beta.x` versions.
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
This is the *Changelog* for the `1.0.0-beta.x` releases.
|
|
10
|
-
See [1.x.md](1.x.md) for the stable `1.x` *Changelog*.
|
|
5
|
+
See [Changelogs](index.md) for more information on the changelogs.
|
|
11
6
|
|
|
12
7
|
## [1.0.0-beta.6]
|
|
13
8
|
- This is the **final** beta version.
|
|
@@ -96,4 +91,3 @@ See [1.x.md](1.x.md) for the stable `1.x` *Changelog*.
|
|
|
96
91
|
[1.0.0-beta.3]: https://github.com/supernovus/lum.core.js/compare/v1.0.0-beta.2...v1.0.0-beta.3
|
|
97
92
|
[1.0.0-beta.2]: https://github.com/supernovus/lum.core.js/compare/v1.0.0-beta.1...v1.0.0-beta.2
|
|
98
93
|
[1.0.0-beta.1]: https://github.com/supernovus/lum.core.js/releases/tag/v1.0.0-beta.1
|
|
99
|
-
|
package/docs/changelogs/1.x.md
CHANGED
|
@@ -1,11 +1,39 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
All notable changes to this project will be documented in this file.
|
|
1
|
+
# Changelog → 1.x
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
This is the changelog for the `1.x` versions.
|
|
4
|
+
|
|
5
|
+
See [Changelogs](index.md) for more information on the changelogs.
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.3.1] - 2022-09-23
|
|
10
|
+
### Fixed
|
|
11
|
+
- The `arrays` module exports `powerset` and `random` as it should.
|
|
12
|
+
- Recursive dependency order issues with `clone`, `lock`, and `enum` resolved.
|
|
13
|
+
- Custom `lock` options in `enum` module actually work now.
|
|
14
|
+
### Changed
|
|
15
|
+
- The `Enum()` function now has `configurable` and `enumerable` options.
|
|
16
|
+
- The `configurable` property in `enums` defaults to `false` now.
|
|
17
|
+
- Tweaked the *changelogs* a bit and added a new [changelog listing](index.md).
|
|
18
|
+
|
|
19
|
+
## [1.3.0] - 2022-09-11
|
|
20
|
+
### Changed
|
|
21
|
+
- Overhauled the `obj.clone()` method.
|
|
22
|
+
- Added *named options* for all of the behaviours that the `CLONE` *modes* affect.
|
|
23
|
+
While this technically could make the *modes* obsolete, I'm leaving them in as
|
|
24
|
+
simple sets of default options that can be used instead of manually choosing all
|
|
25
|
+
of the individual options.
|
|
26
|
+
- Added a new `CLONE.N` *mode* which has **no** options turned on.
|
|
27
|
+
It will always remain first in the *enum* list so it's value will always be `0`.
|
|
28
|
+
- Added the ability to clone properties using their descriptors.
|
|
29
|
+
It is enabled by default on most of the *modes* now, as it simply makes sense.
|
|
30
|
+
- Added the ability to set the `prototype` on the cloned object.
|
|
31
|
+
- The `opts` parameter of `clone()` may be a `CLONE.*` enum value.
|
|
32
|
+
It's a shortcut for `{mode: CLONE.MODE}` for convenience.
|
|
33
|
+
- A small tweak to `obj.copyProps()`, not important, just a cleanup.
|
|
34
|
+
### Fixed
|
|
35
|
+
- A reference in the DocBlock for `obj.getProperty()`.
|
|
36
|
+
|
|
9
37
|
## [1.2.1] - 2022-09-05
|
|
10
38
|
### Added
|
|
11
39
|
- `core.InternalObjectId#untag()`
|
|
@@ -44,12 +72,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
44
72
|
### Changed
|
|
45
73
|
- Initial *stable* release.
|
|
46
74
|
- See [1.0-beta.md](1.0-beta.md) for the beta versions of `1.0`
|
|
47
|
-
- See [lum.js](https://github.com/supernovus/lum.js) for the original library this is replacing.
|
|
75
|
+
- See [lum.js](https://github.com/supernovus/lum.js) for the original library set this is replacing.
|
|
48
76
|
|
|
49
|
-
[Unreleased]: https://github.com/supernovus/lum.core.js/compare/v1.
|
|
77
|
+
[Unreleased]: https://github.com/supernovus/lum.core.js/compare/v1.3.1...HEAD
|
|
78
|
+
[1.3.1]: https://github.com/supernovus/lum.core.js/compare/v1.3.0...v1.3.1
|
|
79
|
+
[1.3.0]: https://github.com/supernovus/lum.core.js/compare/v1.2.1...v1.3.0
|
|
50
80
|
[1.2.1]: https://github.com/supernovus/lum.core.js/compare/v1.2.0...v1.2.1
|
|
51
81
|
[1.2.0]: https://github.com/supernovus/lum.core.js/compare/v1.1.1...v1.2.0
|
|
52
82
|
[1.1.1]: https://github.com/supernovus/lum.core.js/compare/v1.1.0...v1.1.1
|
|
53
83
|
[1.1.0]: https://github.com/supernovus/lum.core.js/compare/v1.0.0...v1.1.0
|
|
54
84
|
[1.0.0]: https://github.com/supernovus/lum.core.js/releases/tag/v1.0.0
|
|
55
|
-
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelogs
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in these files.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## Versions
|
|
9
|
+
|
|
10
|
+
To avoid the changelogs getting too big, I'm planning on splitting them up based
|
|
11
|
+
on the major version changes.
|
|
12
|
+
|
|
13
|
+
Each major version will be listed here in reverse chronological order (newest first.)
|
|
14
|
+
|
|
15
|
+
- [1.x](1.x.md) → The first standalone core library.
|
|
16
|
+
- [1.0-beta.x](1.0-beta.md)
|
|
17
|
+
→ The *pre-releases* from when this split off of the older
|
|
18
|
+
[Lum.js](https://github.com/supernovus/lum.js) library collection.
|
|
19
|
+
|
package/lib/arrays.js
CHANGED
|
@@ -99,6 +99,8 @@ function powerset(array)
|
|
|
99
99
|
return ps;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
exports.powerset = powerset;
|
|
103
|
+
|
|
102
104
|
/**
|
|
103
105
|
* Get a random element from an array.
|
|
104
106
|
* @param {Array} array The array to get an item from.
|
|
@@ -109,3 +111,5 @@ function random(array)
|
|
|
109
111
|
{
|
|
110
112
|
return array[Math.floor(Math.random()*array.length)];
|
|
111
113
|
}
|
|
114
|
+
|
|
115
|
+
exports.random = random;
|
package/lib/enum.js
CHANGED
|
@@ -21,6 +21,9 @@ function Enum (spec, opts={})
|
|
|
21
21
|
|
|
22
22
|
const anEnum = ENUM_ID.tag({});
|
|
23
23
|
|
|
24
|
+
const configurable = opts.configurable ?? false;
|
|
25
|
+
const enumerable = opts.enumerable ?? true;
|
|
26
|
+
|
|
24
27
|
function getVal (name, def)
|
|
25
28
|
{
|
|
26
29
|
if (opts.symbols)
|
|
@@ -42,7 +45,7 @@ function Enum (spec, opts={})
|
|
|
42
45
|
|
|
43
46
|
function addVal(pName, sName, inVal)
|
|
44
47
|
{
|
|
45
|
-
const desc = {configurable
|
|
48
|
+
const desc = {configurable, enumerable};
|
|
46
49
|
desc.value = getVal(sName, inVal);
|
|
47
50
|
def(anEnum, pName, desc);
|
|
48
51
|
}
|
|
@@ -83,25 +86,25 @@ function Enum (spec, opts={})
|
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
if (notNil(opts.lock))
|
|
86
|
-
{ // Use lock.
|
|
89
|
+
{ // Use lock() function.
|
|
87
90
|
let lockOpts;
|
|
88
91
|
if (Array.isArray(opts.lock))
|
|
89
|
-
{
|
|
92
|
+
{ // Specified the lock parameters as an array.
|
|
90
93
|
lockOpts = opts.lock;
|
|
91
94
|
}
|
|
92
95
|
else if (isObj(opts.lock))
|
|
93
|
-
{
|
|
96
|
+
{ // An object is assumed to be the `cloneOpts` parameter.
|
|
94
97
|
lockOpts = [true, opts.lock, false];
|
|
95
98
|
}
|
|
96
99
|
else if (typeof opts.lock === B)
|
|
97
|
-
{
|
|
100
|
+
{ // Boolean is assumed to be the `cloneable` parameter.
|
|
98
101
|
lockOpts = [opts.lock, null, false];
|
|
99
102
|
}
|
|
100
103
|
else
|
|
101
|
-
{
|
|
102
|
-
lockOpts = [
|
|
104
|
+
{ // Anything else we use default values.
|
|
105
|
+
lockOpts = [];
|
|
103
106
|
}
|
|
104
|
-
return lock(anEnum);
|
|
107
|
+
return lock(anEnum, ...lockOpts);
|
|
105
108
|
}
|
|
106
109
|
else if (!opts.open)
|
|
107
110
|
{ // Use Object.freeze()
|
|
@@ -138,3 +141,6 @@ def(Enum, 'is', isEnum);
|
|
|
138
141
|
TYPES.add('ENUM', 'enum', isEnum, 'isEnum');
|
|
139
142
|
|
|
140
143
|
module.exports = Enum;
|
|
144
|
+
|
|
145
|
+
// Loading this at the end.
|
|
146
|
+
const {lock} = require('./obj/lock');
|
package/lib/index.js
CHANGED
|
@@ -125,7 +125,7 @@ const def = types.def;
|
|
|
125
125
|
|
|
126
126
|
module.exports =
|
|
127
127
|
{
|
|
128
|
-
types,
|
|
128
|
+
types, context, flags, obj, opt, modules, arrays, strings,
|
|
129
129
|
def, randomNumber, InternalObjectId, Enum, lazy, observable,
|
|
130
|
-
stacktrace, AbstractClass, Functions, NYI,
|
|
130
|
+
stacktrace, AbstractClass, Functions, NYI,
|
|
131
131
|
}
|
package/lib/obj/clone.js
CHANGED
|
@@ -1,64 +1,108 @@
|
|
|
1
1
|
// Import *most* required bits here.
|
|
2
|
-
const {
|
|
3
|
-
const Enum = require('../enum');
|
|
2
|
+
const {N,F, isObj, isComplex, def, isArray} = require('../types');
|
|
4
3
|
const copyProps = require('./copyprops');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* An enum of supported modes for the `clone` method.
|
|
8
|
-
*
|
|
9
|
-
* - **P** = All properties. If unchecked, enumerable properties only.
|
|
10
|
-
* - **A** = Uses `Array.slice()` shortcut for shallow Array cloning.
|
|
11
|
-
* - **R** = Recursive (deep) cloning of nested objects.
|
|
12
|
-
*
|
|
13
|
-
* | Mode | P | A | R | Notes |
|
|
14
|
-
* | ---- | - | - | - | ----- |
|
|
15
|
-
* | `CLONE.DEF` | × | ✓ | × | Default mode for cloning functions. |
|
|
16
|
-
* | `CLONE.DEEP` | × | × | ✓ | |
|
|
17
|
-
* | `CLONE.FULL` | ✓ | ✓ | × | |
|
|
18
|
-
* | `CLONE.ALL` | ✓ | × | × | |
|
|
19
|
-
* | `CLONE.ENTIRE` | ✓ | × | ✓ | |
|
|
20
|
-
* | `CLONE.JSON` | × | × | ✓ | Uses JSON, so no `function` or `symbol` support. |
|
|
21
|
-
*
|
|
22
|
-
* @alias module:@lumjs/core/obj.CLONE
|
|
23
|
-
*/
|
|
24
|
-
const CLONE = Enum(['DEF','FULL','ALL','DEEP','ENTIRE','JSON']);
|
|
25
|
-
|
|
26
|
-
exports.CLONE = CLONE;
|
|
27
|
-
|
|
28
|
-
// A list of modes that should use the array.slice shallow shortcut.
|
|
29
|
-
const SLICE_ARRAYS = [CLONE.DEF, CLONE.FULL];
|
|
30
|
-
|
|
31
|
-
// A list of modes that should get *all* properties.
|
|
32
|
-
const ALL_PROPS = [CLONE.FULL, CLONE.ALL, CLONE.DEEP];
|
|
33
|
-
|
|
34
|
-
// A list of modes that should do recursive cloning of objects.
|
|
35
|
-
const RECURSIVE = [CLONE.DEEP, CLONE.ENTIRE];
|
|
4
|
+
const getProp = require('./getproperty');
|
|
36
5
|
|
|
37
6
|
/**
|
|
38
7
|
* Clone an object or function.
|
|
39
8
|
*
|
|
40
|
-
* @param {object
|
|
41
|
-
*
|
|
9
|
+
* @param {object} obj - The object we want to clone.
|
|
10
|
+
*
|
|
11
|
+
* @param {(object|number)} [opts={}] Options for the cloning process.
|
|
12
|
+
*
|
|
13
|
+
* If this is a `number` then it's assumed to be the `opts.mode` parameter.
|
|
14
|
+
*
|
|
15
|
+
* @param {number} [opts.mode=CLONE.DEF] One of the `CLONE.*` enum values.
|
|
16
|
+
*
|
|
17
|
+
* When the `clone()` method was written to replace some similar methods
|
|
18
|
+
* from earlier libraries, I for some reason decided to simply have a bunch
|
|
19
|
+
* of different cloning modes. I have since added a full set of options that
|
|
20
|
+
* allows overriding the options of any mode (except `CLONE.JSON`).
|
|
42
21
|
*
|
|
43
|
-
*
|
|
22
|
+
* The `CLONE` enum is also aliased as `clone.MODE` as an alternative.
|
|
44
23
|
*
|
|
45
|
-
*
|
|
24
|
+
* @param {boolean} [opts.all] Clone **all** of the object's properties?
|
|
25
|
+
*
|
|
26
|
+
* If `false` only *enumerable* properties will be cloned.
|
|
27
|
+
*
|
|
28
|
+
* The default value depends on the `opts.mode` used.
|
|
29
|
+
*
|
|
30
|
+
* This is not used if `opts.mode` was `CLONE.JSON`.
|
|
46
31
|
*
|
|
47
|
-
* @param {boolean} [opts.
|
|
32
|
+
* @param {boolean} [opts.slice] Use the `Array.slice()` shortcut?
|
|
33
|
+
*
|
|
34
|
+
* If `true` then when cloning `Array` objects, a shallow clone will be
|
|
35
|
+
* created using the `Array.slice()` method.
|
|
36
|
+
*
|
|
37
|
+
* The default value depends on the `opts.mode` used.
|
|
38
|
+
*
|
|
39
|
+
* This is not used if `opts.mode` was `CLONE.JSON`.
|
|
40
|
+
*
|
|
41
|
+
* @param {boolean} [opts.recursive] Clone nested objects recursively?
|
|
42
|
+
*
|
|
43
|
+
* The default value depends on the `opts.mode` used.
|
|
44
|
+
* If `opts.slice` is also `true` then `Array` objects will
|
|
45
|
+
* be *shallow clones* while any other kind of object will be recursive.
|
|
46
|
+
*
|
|
47
|
+
* This is not used if `opts.mode` was `CLONE.JSON`.
|
|
48
|
+
*
|
|
49
|
+
* @param {boolean} [opts.descriptors] Clone using the property descriptors?
|
|
50
|
+
*
|
|
51
|
+
* If `true` we will get the property descriptors from the original object,
|
|
52
|
+
* and assign them to the clone.
|
|
53
|
+
* This is the only way to clone *accessor* type properties properly.
|
|
54
|
+
*
|
|
55
|
+
* If `false` we will directly assign the *property value* from the original
|
|
56
|
+
* object into the clone. This means the *current value* returned from an
|
|
57
|
+
* *accessor* type property will be assigned statically to the clone.
|
|
58
|
+
*
|
|
59
|
+
* The default value will be `true` if `opts.all` **OR** `opts.recursive`
|
|
60
|
+
* are `true`. It will be `false` otherwise.
|
|
61
|
+
*
|
|
62
|
+
* This is not used if `opts.mode` was `CLONE.JSON`.
|
|
63
|
+
*
|
|
64
|
+
* @param {boolean} [opts.prototype] Set the clone's `prototype`?
|
|
65
|
+
*
|
|
66
|
+
* If `true`, once the cloning is complete, we will call
|
|
67
|
+
* `Object.getPrototypeOf()` on the original `obj`, and then call
|
|
68
|
+
* `Object.setProrotypeOf()` on the clone.
|
|
69
|
+
*
|
|
70
|
+
* If `false` then the clone with have a prototype of `Object` or
|
|
71
|
+
* `Array` depending on whether the original object was an `Array`
|
|
72
|
+
* or not. No further prototype handling will be done.
|
|
73
|
+
*
|
|
74
|
+
* The default value will be `true` if `opts.all` **AND** `opts.recursive`
|
|
75
|
+
* are *both* `true`. Otherwise the default value is `false`.
|
|
76
|
+
* So for *modes*, only `CLONE.ENTIRE` uses this by default.
|
|
77
|
+
*
|
|
78
|
+
* @param {(object|boolean)} [opts.addClone=false]
|
|
79
|
+
* Call `addClone()` on the cloned object.
|
|
48
80
|
*
|
|
49
|
-
*
|
|
50
|
-
* the `
|
|
81
|
+
* If `opts.addClone` is an `object` then it will be used as the options
|
|
82
|
+
* passed to the `addClone()` method. If it is `true` then the `opts`
|
|
83
|
+
* will be passed as is.
|
|
51
84
|
*
|
|
52
|
-
* @param {boolean} [opts.addLock=false]
|
|
85
|
+
* @param {(object|boolean)} [opts.addLock=false]
|
|
86
|
+
* Call `addLock()` on the cloned object.
|
|
53
87
|
*
|
|
54
|
-
*
|
|
88
|
+
* If `opts.addLock` is an `object` then it will be used as the options
|
|
89
|
+
* passed to the `addLock()` method. If it is `true` then the `opts`
|
|
90
|
+
* will be passed as is.
|
|
55
91
|
*
|
|
56
|
-
* @param {
|
|
92
|
+
* @param {(object|boolean)} [opts.copy=false]
|
|
93
|
+
* Call `copyProps()` on the cloned object.
|
|
57
94
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
95
|
+
* This is called *after* the normal cloning process, so only properties
|
|
96
|
+
* that weren't copied during the cloning process will be copied here.
|
|
97
|
+
* This is a leftover from when `CLONE.JSON` was the default cloning mode,
|
|
98
|
+
* and this was the only way to restore `function` properties.
|
|
99
|
+
*
|
|
100
|
+
* If `opts.copy` is an `object` then it will be used as the options
|
|
101
|
+
* passed to the `copyProps()` method. If it is `true` then the `opts`
|
|
102
|
+
* will be passed as is.
|
|
60
103
|
*
|
|
61
|
-
* @return {object}
|
|
104
|
+
* @return {object} The clone of the object.
|
|
105
|
+
*
|
|
62
106
|
* @alias module:@lumjs/core/obj.clone
|
|
63
107
|
*/
|
|
64
108
|
function clone(obj, opts={})
|
|
@@ -71,14 +115,38 @@ function clone(obj, opts={})
|
|
|
71
115
|
return obj;
|
|
72
116
|
}
|
|
73
117
|
|
|
74
|
-
if (
|
|
118
|
+
if (typeof opts === N)
|
|
119
|
+
{ // The 'mode' option.
|
|
120
|
+
opts = {mode: opts};
|
|
121
|
+
}
|
|
122
|
+
else if (!isObj(opts))
|
|
75
123
|
{ // Opts has to be a valid object.
|
|
76
124
|
opts = {};
|
|
77
125
|
}
|
|
78
126
|
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
127
|
+
// The mode is the base option.
|
|
128
|
+
const mode = typeof opts.mode === N ? opts.mode : CLONE.DEF;
|
|
129
|
+
|
|
130
|
+
// Options with defaults based on the mode.
|
|
131
|
+
const allProps = opts.all ?? ALL_PROPS.includes(mode);
|
|
132
|
+
const useSlice = opts.slice ?? SLICE_ARRAYS.includes(mode);
|
|
133
|
+
const recursive = opts.recursive ?? RECURSIVE.includes(mode);
|
|
134
|
+
|
|
135
|
+
// Some that depend on the values of the above options.
|
|
136
|
+
const descriptors = opts.descriptors ?? (allProps || recursive);
|
|
137
|
+
const withProto = opts.prototype ?? (allProps && recursive);
|
|
138
|
+
|
|
139
|
+
// Finally, a few that can be boolean or objects.
|
|
140
|
+
const subOpts = opt =>
|
|
141
|
+
{
|
|
142
|
+
if (isObj(opts[opt]))
|
|
143
|
+
return opts[opt];
|
|
144
|
+
else if (opts[opt] === true)
|
|
145
|
+
return opts;
|
|
146
|
+
}
|
|
147
|
+
const reclone = subOpts('addClone');
|
|
148
|
+
const relock = subOpts('addLock');
|
|
149
|
+
const cpProps = subOpts('copy');
|
|
82
150
|
|
|
83
151
|
let copy;
|
|
84
152
|
|
|
@@ -89,7 +157,7 @@ function clone(obj, opts={})
|
|
|
89
157
|
//console.debug("::clone using JSON cloning");
|
|
90
158
|
copy = JSON.parse(JSON.stringify(obj));
|
|
91
159
|
}
|
|
92
|
-
else if (Array.isArray(obj) &&
|
|
160
|
+
else if (Array.isArray(obj) && useSlice)
|
|
93
161
|
{ // Make a shallow copy using slice.
|
|
94
162
|
//console.debug("::clone using Array.slice()");
|
|
95
163
|
copy = obj.slice();
|
|
@@ -97,10 +165,10 @@ function clone(obj, opts={})
|
|
|
97
165
|
else
|
|
98
166
|
{ // Build a clone using a simple loop.
|
|
99
167
|
//console.debug("::clone using simple loop");
|
|
100
|
-
copy = {};
|
|
168
|
+
copy = isArray(obj) ? [] : {};
|
|
101
169
|
|
|
102
170
|
let props;
|
|
103
|
-
if (
|
|
171
|
+
if (allProps)
|
|
104
172
|
{ // All object properties.
|
|
105
173
|
//console.debug("::clone getting all properties");
|
|
106
174
|
props = Object.getOwnPropertyNames(obj);
|
|
@@ -112,39 +180,79 @@ function clone(obj, opts={})
|
|
|
112
180
|
}
|
|
113
181
|
|
|
114
182
|
//console.debug("::clone[props]", props);
|
|
183
|
+
|
|
184
|
+
let recOpts;
|
|
185
|
+
if (recursive)
|
|
186
|
+
{ // Recursive opts; skips addClone, addLock, and copy.
|
|
187
|
+
recOpts =
|
|
188
|
+
{
|
|
189
|
+
mode, recursive, descriptors,
|
|
190
|
+
all: allProps,
|
|
191
|
+
slice: useSlice,
|
|
192
|
+
prototype: withProto,
|
|
193
|
+
}
|
|
194
|
+
}
|
|
115
195
|
|
|
116
196
|
for (const prop of props)
|
|
117
197
|
{
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
198
|
+
if (descriptors)
|
|
199
|
+
{ // Use descriptor assignment.
|
|
200
|
+
const objDesc = getProp(obj, prop);
|
|
201
|
+
if (isObj(objDesc))
|
|
202
|
+
{ // Make a fast, shallow clone of the descriptor.
|
|
203
|
+
const cloneDesc = clone(objDesc);
|
|
204
|
+
if (isObj(objDesc.value) && recursive)
|
|
205
|
+
{ // Recursively clone the value.
|
|
206
|
+
cloneDesc.value = clone(objDesc.value, recOpts);
|
|
207
|
+
}
|
|
208
|
+
def(copy, prop, cloneDesc);
|
|
209
|
+
}
|
|
210
|
+
else
|
|
211
|
+
{
|
|
212
|
+
console.error("getProperty failed", {obj, prop, props, opts});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else
|
|
216
|
+
{ // Use direct assignment.
|
|
217
|
+
let val = obj[prop];
|
|
218
|
+
if (isObj(val) && recursive)
|
|
219
|
+
{ // Deep cloning.
|
|
220
|
+
val = clone(val, recOpts);
|
|
221
|
+
}
|
|
222
|
+
copy[prop] = val;
|
|
122
223
|
}
|
|
123
|
-
|
|
224
|
+
} // for prop
|
|
225
|
+
|
|
226
|
+
} // simple loop cloning algorithm
|
|
227
|
+
|
|
228
|
+
if (withProto)
|
|
229
|
+
{ // Copy the prototype if it is different.
|
|
230
|
+
const objProto = Object.getPrototypeOf(obj);
|
|
231
|
+
const copyProto = Object.getPrototypeOf(copy);
|
|
232
|
+
if (objProto && objProto !== copyProto)
|
|
233
|
+
{ // Update the clone's prototype to match the original.
|
|
234
|
+
Object.setPrototypeOf(copy, objProto);
|
|
124
235
|
}
|
|
125
236
|
}
|
|
126
237
|
|
|
127
|
-
if (reclone)
|
|
238
|
+
if (isObj(reclone))
|
|
128
239
|
{ // Add the clone() method to the clone, with the passed opts as defaults.
|
|
129
|
-
addClone(copy,
|
|
240
|
+
addClone(copy, reclone);
|
|
130
241
|
}
|
|
131
242
|
|
|
132
|
-
if (
|
|
133
|
-
{ //
|
|
134
|
-
|
|
243
|
+
if (isObj(relock))
|
|
244
|
+
{ // Add the lock() method to the clone.
|
|
245
|
+
addLock(copy, relock);
|
|
135
246
|
}
|
|
136
247
|
|
|
137
|
-
if (
|
|
138
|
-
{ //
|
|
139
|
-
|
|
248
|
+
if (isObj(cpProps))
|
|
249
|
+
{ // Pass the clone through the copyProps() function as well.
|
|
250
|
+
copyProps(obj, copy, cpProps);
|
|
140
251
|
}
|
|
141
252
|
|
|
142
253
|
return copy;
|
|
143
254
|
}
|
|
144
255
|
|
|
145
|
-
// Alias the CLONE enum as clone.MODE
|
|
146
|
-
def(clone, 'MODE', CLONE);
|
|
147
|
-
|
|
148
256
|
// Export the clone here.
|
|
149
257
|
exports.clone = clone;
|
|
150
258
|
|
|
@@ -218,3 +326,50 @@ exports.cloneIfLocked = cloneIfLocked;
|
|
|
218
326
|
|
|
219
327
|
// Import `addLock()` here *after* assigning the clone methods.
|
|
220
328
|
const {addLock} = require('./lock');
|
|
329
|
+
|
|
330
|
+
// And setting all the Enum definitions at the very end.
|
|
331
|
+
const Enum = require('../enum');
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* An `enum` of supported *modes* for the `clone()` method.
|
|
335
|
+
*
|
|
336
|
+
* - **P** → All properties. If unchecked, *enumerable* properties only.
|
|
337
|
+
* - **A** → Uses `Array.slice()` shortcut for shallow `Array` cloning.
|
|
338
|
+
* - **R** → Recursive (deep) cloning of nested objects.
|
|
339
|
+
* - **D** → Uses *descriptor* cloning instead of direct assignment.
|
|
340
|
+
* - **T** → Sets the `prototype` of the clone as well.
|
|
341
|
+
*
|
|
342
|
+
* | Mode | P | A | R | D | T | Notes |
|
|
343
|
+
* | ---- | - | - | - | - | - | ----- |
|
|
344
|
+
* | `CLONE.N` | × | × | × | × | × | Can be used to manually specify options. |
|
|
345
|
+
* | `CLONE.DEF` | × | ✓ | × | × | × | Default mode for cloning functions. |
|
|
346
|
+
* | `CLONE.DEEP` | × | × | ✓ | ✓ | × | |
|
|
347
|
+
* | `CLONE.FULL` | ✓ | ✓ | × | ✓ | × | |
|
|
348
|
+
* | `CLONE.ALL` | ✓ | × | × | ✓ | × | |
|
|
349
|
+
* | `CLONE.ENTIRE` | ✓ | × | ✓ | ✓ | ✓ | |
|
|
350
|
+
* | `CLONE.JSON` | - | - | ✓ | - | × | Uses JSON, so no `function` or `symbol` support. |
|
|
351
|
+
*
|
|
352
|
+
* The `✓` and `×` marks signify the *default settings* in the mode.
|
|
353
|
+
* In *most* cases there are options that can override the
|
|
354
|
+
* defaults.
|
|
355
|
+
*
|
|
356
|
+
* Any feature in the `CLONE.JSON` row marked with `-` are
|
|
357
|
+
* incompatible with that mode and cannot be enabled at all.
|
|
358
|
+
*
|
|
359
|
+
* @alias module:@lumjs/core/obj.CLONE
|
|
360
|
+
*/
|
|
361
|
+
const CLONE = Enum(['N','DEF','FULL','ALL','DEEP','ENTIRE','JSON']);
|
|
362
|
+
|
|
363
|
+
exports.CLONE = CLONE;
|
|
364
|
+
|
|
365
|
+
// A list of modes that should use the array.slice shallow shortcut.
|
|
366
|
+
const SLICE_ARRAYS = [CLONE.DEF, CLONE.FULL];
|
|
367
|
+
|
|
368
|
+
// A list of modes that should get *all* properties.
|
|
369
|
+
const ALL_PROPS = [CLONE.FULL, CLONE.ALL, CLONE.ENTIRE];
|
|
370
|
+
|
|
371
|
+
// A list of modes that should do recursive cloning of objects.
|
|
372
|
+
const RECURSIVE = [CLONE.DEEP, CLONE.ENTIRE];
|
|
373
|
+
|
|
374
|
+
// Alias the CLONE enum as clone.MODE
|
|
375
|
+
def(clone, 'MODE', CLONE);
|
package/lib/obj/copyprops.js
CHANGED
|
@@ -27,6 +27,7 @@ const {B,isObj,isComplex,isArray,def: defProp} = require('../types');
|
|
|
27
27
|
*/
|
|
28
28
|
function copyProps(source, target, propOpts)
|
|
29
29
|
{
|
|
30
|
+
//console.debug("copyProps", source, target, propOpts);
|
|
30
31
|
if (!isComplex(source) || !isComplex(target))
|
|
31
32
|
{
|
|
32
33
|
throw new TypeError("source and target both need to be objects");
|
|
@@ -66,9 +67,9 @@ function copyProps(source, target, propOpts)
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
// For each propDef found, add it to the target.
|
|
69
|
-
for (
|
|
70
|
+
for (const prop of propDefs)
|
|
70
71
|
{
|
|
71
|
-
|
|
72
|
+
//console.debug(" @prop:", prop);
|
|
72
73
|
if (exclude && exclude.indexOf(prop) !== -1)
|
|
73
74
|
continue; // Excluded property.
|
|
74
75
|
|
|
@@ -82,7 +83,8 @@ function copyProps(source, target, propOpts)
|
|
|
82
83
|
overwrite = defOverwrite[prop];
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
const def = Object.getOwnPropertyDescriptor(source, prop)
|
|
86
|
+
const def = Object.getOwnPropertyDescriptor(source, prop);
|
|
87
|
+
//console.debug(" @desc:", def);
|
|
86
88
|
if (def === undefined) continue; // Invalid property.
|
|
87
89
|
|
|
88
90
|
if (isObj(defOverrides[prop]))
|
|
@@ -93,12 +95,15 @@ function copyProps(source, target, propOpts)
|
|
|
93
95
|
def[key] = val;
|
|
94
96
|
}
|
|
95
97
|
}
|
|
98
|
+
|
|
96
99
|
if (overwrite || target[prop] === undefined)
|
|
97
100
|
{ // Property doesn't already exist, let's add it.
|
|
98
101
|
defProp(target, prop, def);
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
|
|
105
|
+
//console.debug("copyProps:done", target);
|
|
106
|
+
|
|
102
107
|
return target;
|
|
103
108
|
} // copyProps()
|
|
104
109
|
|
package/lib/obj/getproperty.js
CHANGED
|
@@ -12,7 +12,8 @@ const {isComplex,isObj} = types;
|
|
|
12
12
|
* @param {string} prop - Name of the property we want the descriptor of.
|
|
13
13
|
* @param {mixed} [defval] The fallback value if no descriptor is found.
|
|
14
14
|
*
|
|
15
|
-
* @returns {mixed}
|
|
15
|
+
* @returns {mixed} The descriptor if found, `defval` if not.
|
|
16
|
+
* @alias module:@lumjs/core/obj.getProperty
|
|
16
17
|
*/
|
|
17
18
|
function getProperty(obj, prop, defval)
|
|
18
19
|
{
|
package/lib/strings.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* String and locale related functions.
|
|
3
3
|
* @module @lumjs/core/strings
|
|
4
4
|
*/
|
|
5
|
-
const {S,B,F,isObj,root,isArray,needType,needObj} = require('./types')
|
|
5
|
+
const {S,B,F,isObj,root,isArray,needType,needObj} = require('./types');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Get the locale/language string.
|
|
@@ -189,4 +189,3 @@ function replaceItems(string, replacements, useAll)
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
exports.replaceItems = replaceItems;
|
|
192
|
-
|