@lumjs/core 1.0.0-beta.1 → 1.0.0-beta.6
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 +10 -2
- package/TODO.md +12 -4
- package/docs/changelogs/1.0-beta.md +99 -0
- package/{CHANGELOG.md → docs/changelogs/1.x.md} +7 -7
- package/jsdoc.json +33 -0
- package/lib/arrays.js +78 -0
- package/lib/context.js +86 -0
- package/{src → lib}/enum.js +30 -13
- package/{src → lib}/flags.js +6 -0
- package/lib/index.js +131 -0
- package/{src → lib}/lazy.js +33 -35
- package/{src → lib}/meta.js +38 -1
- package/lib/modules.js +113 -0
- package/lib/obj/clone.js +220 -0
- package/{src → lib}/obj/copyall.js +3 -0
- package/{src → lib}/obj/copyprops.js +1 -0
- package/lib/obj/index.js +23 -0
- package/{src → lib}/obj/lock.js +16 -13
- package/{src → lib}/obj/merge.js +2 -0
- package/{src → lib}/obj/ns.js +45 -8
- package/{src → lib}/objectid.js +2 -3
- package/{src → lib}/observable.js +23 -8
- package/{src → lib}/opt.js +6 -1
- package/lib/strings.js +192 -0
- 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 +26 -3
- package/test/arrays.js +19 -0
- package/test/meta.js +17 -0
- package/test/types.js +106 -23
- package/index.js +0 -64
- package/src/context.js +0 -47
- package/src/descriptors.js +0 -243
- package/src/obj/clone.js +0 -201
- package/src/obj/index.js +0 -18
- package/src/prop.js +0 -170
- package/src/strings.js +0 -76
- package/src/types.js +0 -545
package/{src → lib}/lazy.js
RENAMED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
|
|
2
|
-
const {S,F,
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
const {S,F,TYPES:{COMPLEX},needType,needObj,def} = require('./types');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @callback module:@lumjs/core~InitFunc
|
|
6
|
+
* @param {string} name - The `name` parameter passed to `lazy()`
|
|
7
|
+
* @this {object} - The `obj` parameter passed to `lazy()`
|
|
8
|
+
*/
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
11
|
* Build a lazy initializer property.
|
|
8
12
|
*
|
|
9
13
|
* Basically the first time the property is accessed it's built.
|
|
10
14
|
* Subsequent accesses will use the already built property.
|
|
11
|
-
* This is an extension of the {@link
|
|
12
|
-
* alias called `
|
|
15
|
+
* This is an extension of the {@link def} method, and indeed an
|
|
16
|
+
* alias called `def.lazy()` is also available.
|
|
13
17
|
*
|
|
14
|
-
* @param {
|
|
18
|
+
* @param {Object} obj - The object to add the property to.
|
|
15
19
|
* @param {string} name - The name of the property to add.
|
|
16
|
-
* @param {
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* It will also be passed `name` as the sole parameter.
|
|
20
|
-
*
|
|
21
|
-
* @param {mixed} [onset] How to handle assignment.
|
|
20
|
+
* @param {module:@lumjs/core~InitFunc} initfunc
|
|
21
|
+
* The function to initialize the property.
|
|
22
|
+
* @param {*} [onset] How to handle assignment.
|
|
22
23
|
*
|
|
23
24
|
* If this is `true` then the new value will be assigned directly,
|
|
24
25
|
* skipping the initialization process entirely.
|
|
@@ -36,29 +37,26 @@ const {DESC} = require('./descriptors');
|
|
|
36
37
|
*
|
|
37
38
|
* If this is anything else, assignment will do nothing at all.
|
|
38
39
|
*
|
|
39
|
-
* @param {
|
|
40
|
+
* @param {Object} [desc={}] Descriptor rules for the property.
|
|
41
|
+
* We only support two descriptor rules with this function, and
|
|
42
|
+
* their default values are the same as the `def()` function.
|
|
43
|
+
* - `configurable` → `true`
|
|
44
|
+
* - `enumerable` → `false`
|
|
45
|
+
* Any other descriptor properties are invalid here.
|
|
40
46
|
*
|
|
41
|
-
* @return {
|
|
47
|
+
* @return {Object} The object we defined the property on.
|
|
48
|
+
* @alias module:@lumjs/core.lazy
|
|
42
49
|
*/
|
|
43
|
-
function lazy(obj, name, initfunc, onset, desc=
|
|
50
|
+
function lazy(obj, name, initfunc, onset, desc={})
|
|
44
51
|
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (typeof name !== S)
|
|
50
|
-
{
|
|
51
|
-
throw new TypeError("name parameter was not a string");
|
|
52
|
-
}
|
|
53
|
-
if (typeof initfunc !== F)
|
|
54
|
-
{
|
|
55
|
-
throw new TypeError("initfunc parameter was not a function");
|
|
56
|
-
}
|
|
52
|
+
needType(COMPLEX, obj, 'obj must be an object');
|
|
53
|
+
needType(S, name, 'name must be a string');
|
|
54
|
+
needType(F, initfunc, 'initfunc must be a function');
|
|
55
|
+
needObj(desc, 'desc parameter was not an object');
|
|
57
56
|
|
|
58
57
|
let value;
|
|
59
|
-
let setter = null;
|
|
60
58
|
|
|
61
|
-
function
|
|
59
|
+
desc.get = function()
|
|
62
60
|
{
|
|
63
61
|
if (value === undefined)
|
|
64
62
|
{
|
|
@@ -69,23 +67,23 @@ function lazy(obj, name, initfunc, onset, desc=DESC.CONF)
|
|
|
69
67
|
|
|
70
68
|
if (onset === true)
|
|
71
69
|
{ // Allow direct assignment.
|
|
72
|
-
|
|
70
|
+
desc.set = function(newval)
|
|
73
71
|
{
|
|
74
72
|
value = newval;
|
|
75
73
|
}
|
|
76
74
|
}
|
|
77
75
|
else if (onset === false)
|
|
78
76
|
{ // Throw an error on assignment.
|
|
79
|
-
|
|
77
|
+
desc.set = function()
|
|
80
78
|
{
|
|
81
79
|
throw new ReferenceError("The "+name+" property is read-only");
|
|
82
80
|
}
|
|
83
81
|
}
|
|
84
82
|
else if (typeof onset === F)
|
|
85
83
|
{ // A proxy method for assignment.
|
|
86
|
-
|
|
84
|
+
desc.set = function(newval)
|
|
87
85
|
{
|
|
88
|
-
const setval = onset.call(this, newval);
|
|
86
|
+
const setval = onset.call(this, newval, value);
|
|
89
87
|
if (setval !== undefined)
|
|
90
88
|
{
|
|
91
89
|
value = setval;
|
|
@@ -93,10 +91,10 @@ function lazy(obj, name, initfunc, onset, desc=DESC.CONF)
|
|
|
93
91
|
}
|
|
94
92
|
}
|
|
95
93
|
|
|
96
|
-
|
|
94
|
+
def(obj, name, desc);
|
|
97
95
|
}
|
|
98
96
|
|
|
99
97
|
// Gotta be one of the greatest lines...
|
|
100
|
-
|
|
98
|
+
def(def, 'lazy', lazy);
|
|
101
99
|
|
|
102
100
|
module.exports = lazy;
|
package/{src → lib}/meta.js
RENAMED
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Meta-programming helpers.
|
|
3
|
+
* @module @lumjs/core/meta
|
|
4
|
+
*/
|
|
2
5
|
|
|
3
6
|
/**
|
|
4
7
|
* Get a stacktrace. Differs from browser to browser.
|
|
8
|
+
*
|
|
9
|
+
* Uses the `stack` property of an `Error` object as the source.
|
|
10
|
+
* This is a super simplistic hack. For a more complete solution, try
|
|
11
|
+
* the `stacktrace-js` library, which will be used in the new `@lumjs/debug`
|
|
12
|
+
* library as a dependency.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} [msg] - A message for the Error object.
|
|
15
|
+
*
|
|
16
|
+
* @returns {string[]} An array of stack strings.
|
|
17
|
+
* @alias module:@lumjs/core/meta.stacktrace
|
|
5
18
|
*/
|
|
6
19
|
function stacktrace(msg)
|
|
7
20
|
{
|
|
@@ -12,6 +25,7 @@ exports.stacktrace = stacktrace;
|
|
|
12
25
|
|
|
13
26
|
/**
|
|
14
27
|
* Abstract classes for Javascript.
|
|
28
|
+
* @alias module:@lumjs/core/meta.AbstractClass
|
|
15
29
|
*/
|
|
16
30
|
class AbstractClass
|
|
17
31
|
{
|
|
@@ -38,6 +52,8 @@ exports.AbstractClass = AbstractClass;
|
|
|
38
52
|
|
|
39
53
|
/**
|
|
40
54
|
* Function prototypes for async, generator, and async generator functions.
|
|
55
|
+
* @namespace
|
|
56
|
+
* @alias module:@lumjs/core/meta.Functions
|
|
41
57
|
*/
|
|
42
58
|
const Functions =
|
|
43
59
|
{
|
|
@@ -59,3 +75,24 @@ const Functions =
|
|
|
59
75
|
}
|
|
60
76
|
|
|
61
77
|
exports.Functions = Functions;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A placeholder function for when something is not implemented.
|
|
81
|
+
*
|
|
82
|
+
* @param {boolean} [fatal=true] If `true` throw Error.
|
|
83
|
+
* If `false` use `console.error()` instead.
|
|
84
|
+
* @param {string} [prefix=''] A prefix for the error message.
|
|
85
|
+
*
|
|
86
|
+
* @returns {void}
|
|
87
|
+
* @alias module:@lumjs/core/meta.NYI
|
|
88
|
+
*/
|
|
89
|
+
function NYI(fatal=true, prefix='')
|
|
90
|
+
{
|
|
91
|
+
const msg = prefix+"« NOT YET IMPLEMENTED »";
|
|
92
|
+
if (fatal)
|
|
93
|
+
throw new Error(msg);
|
|
94
|
+
else
|
|
95
|
+
console.error(msg);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
exports.NYI = NYI;
|
package/lib/modules.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module helpers.
|
|
3
|
+
* @module @lumjs/core/modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const {S,isObj} = require('./types');
|
|
8
|
+
const replace = require('./strings').replaceItems;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get the name of a module.
|
|
12
|
+
*
|
|
13
|
+
* @param {(object|string)} module - Either a module object, or filename.
|
|
14
|
+
* If it is an `object`, it should be a CommonJS `module` object.
|
|
15
|
+
* If it is a `string`, it should be the module filename.
|
|
16
|
+
* @param {object} [opts] Options.
|
|
17
|
+
*
|
|
18
|
+
* @param {boolean} [opts.useAuto=true] Enable automatic name cleaning.
|
|
19
|
+
* If *basename* mode was **not** used, then once all other rules have been
|
|
20
|
+
* applied, strip any leading `.` and `/` characters, and the file extension.
|
|
21
|
+
*
|
|
22
|
+
* @param {boolean} [opts.basename=false] Use `path.basename()`
|
|
23
|
+
* This will strip all parent directories, and the file extension.
|
|
24
|
+
* If no other rules are specified in the `opts`, then this will
|
|
25
|
+
* be applied automatically as a fallback method. If it is set to
|
|
26
|
+
* `true` explicitly, then it will be applied *before* any other options.
|
|
27
|
+
*
|
|
28
|
+
* @param {object} [opts.replace] Call {@link module:@lumjs/core/strings.replaceItems}
|
|
29
|
+
* This uses the default `useAll` values based on the `object` format.
|
|
30
|
+
* @param {object} [opts.replaceOne] `replace` but `useAll` set to `false`.
|
|
31
|
+
* @param {object} [opts.replaceAll] `replace` but `useAll` set to `true`.
|
|
32
|
+
* @param {(string|string[])} [opts.strip] Sub-strings to remove entirely.
|
|
33
|
+
* @returns {string} The *name* of a module as per the options set.
|
|
34
|
+
* @alias module:@lumjs/core/modules.name
|
|
35
|
+
*/
|
|
36
|
+
function name(module, opts={})
|
|
37
|
+
{
|
|
38
|
+
let filename;
|
|
39
|
+
|
|
40
|
+
if (typeof module === S)
|
|
41
|
+
{ // Going to assume a string is the filename.
|
|
42
|
+
filename = module;
|
|
43
|
+
}
|
|
44
|
+
else if (isObj(module) && typeof module.filename === S)
|
|
45
|
+
{ // It's a CommonJS module context object.
|
|
46
|
+
filename = module.filename;
|
|
47
|
+
}
|
|
48
|
+
else
|
|
49
|
+
{ // Sorry, we don't support that.
|
|
50
|
+
throw new TypeError("Unsupported module parameter");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const ext = path.extname(filename);
|
|
54
|
+
|
|
55
|
+
let useFallback = true;
|
|
56
|
+
let useAuto = opts.useAuto ?? true;
|
|
57
|
+
|
|
58
|
+
if (opts.basename)
|
|
59
|
+
{ // We want to run the basename sequence first.
|
|
60
|
+
filename = path.basename(filename, ext);
|
|
61
|
+
useFallback = false;
|
|
62
|
+
useAuto = false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (isObj(opts.replace))
|
|
66
|
+
{ // Replacements using replace() or replaceAll() based on parameter format.
|
|
67
|
+
useFallback = false;
|
|
68
|
+
filename = replace(filename, opts.replace);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (isObj(opts.replaceOne))
|
|
72
|
+
{ // Replacements using replace() regardless of parameter format.
|
|
73
|
+
useFallback = false;
|
|
74
|
+
filename = replace(filename, opts.replaceOne, false);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (isObj(opts.replaceAll))
|
|
78
|
+
{ // Replacements using replaceAll() regardless of parameter format.
|
|
79
|
+
useFallback = false;
|
|
80
|
+
filename = replace(filename, opts.replaceAll, true);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (typeof opts.strip === S)
|
|
84
|
+
{ // A prefix. This always uses replace(), never replaceAll().
|
|
85
|
+
filename = filename.replace(opts.strip, '');
|
|
86
|
+
useFallback = false;
|
|
87
|
+
}
|
|
88
|
+
else if (Array.isArray(opts.strip))
|
|
89
|
+
{ // A list of strings or regexps to strip. Ditto on the use of replace().
|
|
90
|
+
for (const strip of opts.strip)
|
|
91
|
+
{
|
|
92
|
+
filename = filename.replace(strip, '');
|
|
93
|
+
}
|
|
94
|
+
useFallback = false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (useFallback)
|
|
98
|
+
{ // We're going to use the basename, either as a fallback
|
|
99
|
+
filename = path.basename(filename, ext);
|
|
100
|
+
useAuto = false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (useAuto)
|
|
104
|
+
{ // A few automatic rules that normally apply if the fallback was not used.
|
|
105
|
+
filename = filename
|
|
106
|
+
.replace(/^[./]+/, '')
|
|
107
|
+
.replace(RegExp(ext+'$'), '');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return filename;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
exports.name = name;
|
package/lib/obj/clone.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// Import *most* required bits here.
|
|
2
|
+
const {B,N,F, isObj, isComplex, def} = require('../types');
|
|
3
|
+
const Enum = require('../enum');
|
|
4
|
+
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];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Clone an object or function.
|
|
39
|
+
*
|
|
40
|
+
* @param {object|function} obj - The object we want to clone.
|
|
41
|
+
* @param {object} [opts={}] - Options for the cloning process.
|
|
42
|
+
*
|
|
43
|
+
* @param {number} [opts.mode=CLONE.DEF] - One of the `CLONE.*` enum values.
|
|
44
|
+
*
|
|
45
|
+
* Note: The `CLONE` enum is also aliased as `clone.MODE` as an alternative.
|
|
46
|
+
*
|
|
47
|
+
* @param {boolean} [opts.addClone=false] - Call `addClone()` on the cloned object.
|
|
48
|
+
*
|
|
49
|
+
* The options sent to this function will be used as the defaults in
|
|
50
|
+
* the `clone()` method added to the object.
|
|
51
|
+
*
|
|
52
|
+
* @param {boolean} [opts.addLock=false] - Call `addLock()` on the cloned object.
|
|
53
|
+
*
|
|
54
|
+
* No further options for this, just add a `lock()` method to the clone.
|
|
55
|
+
*
|
|
56
|
+
* @param {?object} [opts.copy] Call `copyProps()` on the cloned object.
|
|
57
|
+
*
|
|
58
|
+
* Will pass the original `obj` as the source to copy from.
|
|
59
|
+
* Will pass `opts.copy` as the options.
|
|
60
|
+
*
|
|
61
|
+
* @return {object} - The clone of the object.
|
|
62
|
+
* @alias module:@lumjs/core/obj.clone
|
|
63
|
+
*/
|
|
64
|
+
function clone(obj, opts={})
|
|
65
|
+
{
|
|
66
|
+
//console.debug("clone()", obj, opts);
|
|
67
|
+
|
|
68
|
+
if (!isComplex(obj))
|
|
69
|
+
{ // Doesn't need cloning.
|
|
70
|
+
//console.debug("no cloning required");
|
|
71
|
+
return obj;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!isObj(opts))
|
|
75
|
+
{ // Opts has to be a valid object.
|
|
76
|
+
opts = {};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const mode = typeof opts.mode === N ? opts.mode : CLONE.DEF;
|
|
80
|
+
const reclone = typeof opts.addClone === B ? opts.addClone : false;
|
|
81
|
+
const relock = typeof opts.addLock === B ? opts.addLock : false;
|
|
82
|
+
|
|
83
|
+
let copy;
|
|
84
|
+
|
|
85
|
+
//console.debug("::clone", {mode, reclone, relock});
|
|
86
|
+
|
|
87
|
+
if (mode === CLONE.JSON)
|
|
88
|
+
{ // Deep clone enumerable properties using JSON trickery.
|
|
89
|
+
//console.debug("::clone using JSON cloning");
|
|
90
|
+
copy = JSON.parse(JSON.stringify(obj));
|
|
91
|
+
}
|
|
92
|
+
else if (Array.isArray(obj) && SLICE_ARRAYS.includes(mode))
|
|
93
|
+
{ // Make a shallow copy using slice.
|
|
94
|
+
//console.debug("::clone using Array.slice()");
|
|
95
|
+
copy = obj.slice();
|
|
96
|
+
}
|
|
97
|
+
else
|
|
98
|
+
{ // Build a clone using a simple loop.
|
|
99
|
+
//console.debug("::clone using simple loop");
|
|
100
|
+
copy = {};
|
|
101
|
+
|
|
102
|
+
let props;
|
|
103
|
+
if (ALL_PROPS.includes(mode))
|
|
104
|
+
{ // All object properties.
|
|
105
|
+
//console.debug("::clone getting all properties");
|
|
106
|
+
props = Object.getOwnPropertyNames(obj);
|
|
107
|
+
}
|
|
108
|
+
else
|
|
109
|
+
{ // Enumerable properties.
|
|
110
|
+
//console.debug("::clone getting enumerable properties");
|
|
111
|
+
props = Object.keys(obj);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
//console.debug("::clone[props]", props);
|
|
115
|
+
|
|
116
|
+
for (const prop of props)
|
|
117
|
+
{
|
|
118
|
+
let val = obj[prop];
|
|
119
|
+
if (isObj(val) && RECURSIVE.includes(mode))
|
|
120
|
+
{ // Deep cloning.
|
|
121
|
+
val = clone(val, {mode});
|
|
122
|
+
}
|
|
123
|
+
copy[prop] = val;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (reclone)
|
|
128
|
+
{ // Add the clone() method to the clone, with the passed opts as defaults.
|
|
129
|
+
addClone(copy, opts);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (opts.copy)
|
|
133
|
+
{ // Pass the clone through the copyProps() function as well.
|
|
134
|
+
copyProps(obj, copy, opts.copy);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (relock)
|
|
138
|
+
{ // Add the lock() method to the clone.
|
|
139
|
+
addLock(copy, opts);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return copy;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Alias the CLONE enum as clone.MODE
|
|
146
|
+
def(clone, 'MODE', CLONE);
|
|
147
|
+
|
|
148
|
+
// Export the clone here.
|
|
149
|
+
exports.clone = clone;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Add a clone() method to an object.
|
|
153
|
+
*
|
|
154
|
+
* @param {object|function} obj - The object to add clone() to.
|
|
155
|
+
* @param {object} [defOpts=null] Default options for the clone() method.
|
|
156
|
+
*
|
|
157
|
+
* If `null` or anything other than an object, the defaults will be:
|
|
158
|
+
*
|
|
159
|
+
* ```{mode: CLONE.DEF, addClone: true, addLock: false}```
|
|
160
|
+
*
|
|
161
|
+
* @alias module:@lumjs/core/obj.addClone
|
|
162
|
+
*/
|
|
163
|
+
function addClone(obj, defOpts=null)
|
|
164
|
+
{
|
|
165
|
+
if (!isObj(defOpts))
|
|
166
|
+
{ // Assign a default set of defaults.
|
|
167
|
+
defOpts = {mode: CLONE.DEF, addClone: true, addLock: false};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const defDesc = defOpts.cloneDesc ?? {};
|
|
171
|
+
|
|
172
|
+
defDesc.value = function (opts)
|
|
173
|
+
{
|
|
174
|
+
if (!isObj(opts))
|
|
175
|
+
opts = defOpts;
|
|
176
|
+
return clone(obj, opts);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return def(obj, 'clone', defDesc);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
exports.addClone = addClone;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Clone an object if it's not extensible (locked, sealed, frozen, etc.)
|
|
186
|
+
*
|
|
187
|
+
* If the object is extensible, it's returned as is.
|
|
188
|
+
*
|
|
189
|
+
* If not, if the object has a `clone()` method it will be used.
|
|
190
|
+
* Otherwise use our `clone()` function.
|
|
191
|
+
*
|
|
192
|
+
* @param {object} obj - The object to clone if needed.
|
|
193
|
+
* @param {object} [opts] - Options to pass to `clone()` method.
|
|
194
|
+
*
|
|
195
|
+
* @return {object} - Either the original object, or an extensible clone.
|
|
196
|
+
*
|
|
197
|
+
* @alias module:@lumjs/core/obj.cloneIfLocked
|
|
198
|
+
*/
|
|
199
|
+
function cloneIfLocked(obj, opts)
|
|
200
|
+
{
|
|
201
|
+
if (!Object.isExtensible(obj))
|
|
202
|
+
{
|
|
203
|
+
if (typeof obj.clone === F)
|
|
204
|
+
{ // Use the object's clone() method.
|
|
205
|
+
return obj.clone(opts);
|
|
206
|
+
}
|
|
207
|
+
else
|
|
208
|
+
{ // Use our own clone method.
|
|
209
|
+
return clone(obj, opts);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Return the object itself, it's fine.
|
|
214
|
+
return obj;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
exports.cloneIfLocked = cloneIfLocked;
|
|
218
|
+
|
|
219
|
+
// Import `addLock()` here *after* assigning the clone methods.
|
|
220
|
+
const {addLock} = require('./lock');
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* It does no type checking, and has no qualms about overwriting properties.
|
|
5
5
|
* You probably want something like `copyProps` instead.
|
|
6
|
+
*
|
|
7
|
+
* @alias module:@lumjs/core/obj.copyAll
|
|
6
8
|
*/
|
|
7
9
|
function copyAll(target, ...sources)
|
|
8
10
|
{
|
|
@@ -13,6 +15,7 @@ function copyAll(target, ...sources)
|
|
|
13
15
|
target[name] = source[name];
|
|
14
16
|
}
|
|
15
17
|
}
|
|
18
|
+
return target;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
module.exports = copyAll;
|
|
@@ -23,6 +23,7 @@ const {B,isObj,isComplex,isArray,def: defProp} = require('../types');
|
|
|
23
23
|
* if that property can be overwritten or not.
|
|
24
24
|
*
|
|
25
25
|
* @returns {object} The `target` object.
|
|
26
|
+
* @alias module:@lumjs/core/obj.copyProps
|
|
26
27
|
*/
|
|
27
28
|
function copyProps(source, target, propOpts)
|
|
28
29
|
{
|
package/lib/obj/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Object helpers sub-module.
|
|
3
|
+
* @module @lumjs/core/obj
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const copyAll = require('./copyall');
|
|
7
|
+
const copyProps = require('./copyprops');
|
|
8
|
+
const {CLONE,clone,addClone,cloneIfLocked} = require('./clone');
|
|
9
|
+
const {lock,addLock} = require('./lock');
|
|
10
|
+
const {mergeNested,syncNested} = require('./merge');
|
|
11
|
+
const ns = require('./ns');
|
|
12
|
+
const
|
|
13
|
+
{
|
|
14
|
+
getObjectPath,setObjectPath,
|
|
15
|
+
getNamespace,setNamespace,
|
|
16
|
+
} = ns;
|
|
17
|
+
|
|
18
|
+
module.exports =
|
|
19
|
+
{
|
|
20
|
+
CLONE, clone, addClone, cloneIfLocked, lock, addLock,
|
|
21
|
+
mergeNested, syncNested, copyProps, copyAll, ns,
|
|
22
|
+
getObjectPath, setObjectPath, getNamespace, setNamespace,
|
|
23
|
+
}
|
package/{src → lib}/obj/lock.js
RENAMED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
// Import *most* required bits here.
|
|
2
2
|
const {B, isObj, def} = require('../types');
|
|
3
|
-
const {getDescriptor, DESC} = require('../descriptors');
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Lock an object using Object.freeze()
|
|
7
6
|
*
|
|
8
7
|
* @param {object} obj - The object we want to lock.
|
|
9
|
-
* @param {boolean} [clonable=true] Pass to
|
|
10
|
-
* @param {object} [cloneOpts=null] Options for addClone
|
|
8
|
+
* @param {boolean} [clonable=true] Pass to `addClone()` first?
|
|
9
|
+
* @param {object} [cloneOpts=null] Options for `addClone()`.
|
|
11
10
|
* @param {boolean} [useSeal=false] Use Object.seal() instead of freeze.
|
|
12
11
|
*
|
|
13
12
|
* If cloneOpts is `null` then we will use the following:
|
|
@@ -15,8 +14,7 @@ const {getDescriptor, DESC} = require('../descriptors');
|
|
|
15
14
|
* ```{mode: CLONE.DEF, addClone: true, addLock: true}```
|
|
16
15
|
*
|
|
17
16
|
* @return {object} The locked object.
|
|
18
|
-
*
|
|
19
|
-
* @method Lum._.lock
|
|
17
|
+
* @alias module:@lumjs/core/obj.lock
|
|
20
18
|
*/
|
|
21
19
|
function lock(obj, clonable=true, cloneOpts=null, useSeal=false)
|
|
22
20
|
{
|
|
@@ -38,23 +36,28 @@ const {getDescriptor, DESC} = require('../descriptors');
|
|
|
38
36
|
/**
|
|
39
37
|
* Add a lock() method to an object.
|
|
40
38
|
*
|
|
41
|
-
* Adds a wrapper version of
|
|
42
|
-
*
|
|
43
|
-
* @param {object} obj - The object we're adding lock() to.
|
|
44
|
-
* @param {object} opts - Options (TODO: document this).
|
|
39
|
+
* Adds a wrapper version of `lock()` to the object as a method.
|
|
45
40
|
*
|
|
46
|
-
* @
|
|
41
|
+
* @param {object} obj - The object we're adding `lock()` to.
|
|
42
|
+
* @param {object} [opts] - Options and defaults.
|
|
43
|
+
* In addition to options specific to this function, any options
|
|
44
|
+
* supported by `addClone()` may be specified here as well.
|
|
45
|
+
* @param {object} [opts.lockDesc] Descriptor rules the `lock()` method.
|
|
46
|
+
* @param {boolean} [opts.addClone=true] Default for `clonable` parameter.
|
|
47
|
+
* @param {object} [opts.useSeal=false] Default for `useSeal` parameter.
|
|
48
|
+
* @returns {object} `obj`
|
|
49
|
+
* @alias module:@lumjs/core/obj.addLock
|
|
47
50
|
*/
|
|
48
51
|
function addLock(obj, opts)
|
|
49
52
|
{
|
|
50
|
-
const defDesc =
|
|
51
|
-
defDesc.
|
|
53
|
+
const defDesc = opts.lockDesc ?? {};
|
|
54
|
+
defDesc.value = function(obj, cloneable, cloneOpts, useSeal)
|
|
52
55
|
{
|
|
53
56
|
if (typeof cloneable !== B) clonable = opts.addClone ?? true;
|
|
54
57
|
if (!isObj(cloneOpts)) cloneOpts = opts; // Yup, just a raw copy.
|
|
55
58
|
if (typeof useSeal !== B) useSeal = opts.useSeal ?? false;
|
|
56
59
|
return lock(obj, cloneable, cloneOpts, useSeal);
|
|
57
|
-
}
|
|
60
|
+
}
|
|
58
61
|
return def(obj, 'lock', defDesc);
|
|
59
62
|
}
|
|
60
63
|
|
package/{src → lib}/obj/merge.js
RENAMED
|
@@ -20,6 +20,7 @@ const {B, isObj} = require('../types');
|
|
|
20
20
|
* as the `opts` argument directly will set this option.
|
|
21
21
|
*
|
|
22
22
|
* @returns {object} The `target` object.
|
|
23
|
+
* @alias module:@lumjs/core/obj.mergeNested
|
|
23
24
|
*/
|
|
24
25
|
function mergeNested(source, target, opts={})
|
|
25
26
|
{
|
|
@@ -64,6 +65,7 @@ const {B, isObj} = require('../types');
|
|
|
64
65
|
* @param {object} [opts2=opts1] Options for the second merge operation.
|
|
65
66
|
* If this is not specified, `opts2` will be the same as `opts1`.
|
|
66
67
|
*
|
|
68
|
+
* @alias module:@lumjs/core/obj.syncNested
|
|
67
69
|
*/
|
|
68
70
|
function syncNested(obj1, obj2, opts1={}, opts2=opts1)
|
|
69
71
|
{
|