@lumjs/core 1.37.2 → 1.38.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.
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ const def = require('../types/def');
4
+
5
+ /**
6
+ * Copy properties into a target object.
7
+ *
8
+ * Like Object.assign(), but it uses getOwnPropertyDescriptors()
9
+ * to get the properties to copy and type.def() to assign them.
10
+ *
11
+ * Basically a super simple, non-recursive replacement for my copyProps()
12
+ * and cp() functions that were overly complex and tried to do way too much.
13
+ *
14
+ * @param {object} target - The target object to copy properties into
15
+ * @param {...object} sources - Source objects to copy properties from
16
+ * @returns {object} the `target` object
17
+ *
18
+ * @alias module:@lumjs/core/obj.assignd
19
+ */
20
+ function assignd (target, ...sources)
21
+ {
22
+ for (const src of sources)
23
+ {
24
+ const descs = Object.getOwnPropertyDescriptors(src);
25
+ def(target, descs);
26
+ }
27
+ return target;
28
+ }
29
+
30
+ module.exports = assignd;
package/lib/obj/df.js ADDED
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+
3
+ const {doesDescriptor,needObj,needType,lazy,B,TYPES} = require('../types');
4
+
5
+ const DEF_DESC = {configurable: true}
6
+ const DEF_OPTS = {autoDesc: true}
7
+
8
+ const cp = Object.assign;
9
+ const clone = (...args) => cp({}, ...args);
10
+ const dps = Object.defineProperties;
11
+
12
+ /**
13
+ * Handle populating descriptor objects for df() and family.
14
+ *
15
+ * Exported as `obj.df.descriptor`, but since jsdoc doesn't like
16
+ * properties attached to functions, I've listed it as internal.
17
+ *
18
+ * @param {object} desc - Descriptor object from df()
19
+ * @param {object} opts - Options from df();
20
+ *
21
+ * Will be checked for default descriptor options in the
22
+ * case where those options were not explicitly defined
23
+ * in the descriptor object.
24
+ *
25
+ * @param {boolean} [opts.configurable=true] configurable rule;
26
+ * this is the only option with a fallback default value.
27
+ *
28
+ * @param {boolean} [opts.enumerable] enumerable rule
29
+ *
30
+ * @param {boolean} [opts.writable] writable rule;
31
+ * only applies to data descriptors (ones with a `value` property),
32
+ * NOT to accessor descriptors.
33
+ *
34
+ * @returns {object} `desc`
35
+ * @alias module:@lumjs/core/obj~descriptor
36
+ */
37
+ function descriptor(desc, opts)
38
+ {
39
+ opts = clone(DEF_DESC, opts);
40
+
41
+ const dps = ['configurable', 'enumerable'];
42
+ if (desc.value) dps.push('writable');
43
+
44
+ for (const dp of dps)
45
+ {
46
+ if (typeof desc[dp] !== B && typeof opts[dp] === B)
47
+ {
48
+ desc[dp] = opts[dp];
49
+ }
50
+ }
51
+
52
+ return desc;
53
+ }
54
+
55
+ /**
56
+ * A replacement for `types.def()`, but MUCH simpler.
57
+ *
58
+ * Instead of one function where every argument has several different
59
+ * purposes depending on the type of value, there are now a bunch
60
+ * of functions, each one with a single purpose, and a single signature.
61
+ *
62
+ * This is the main one that all the rest use and is just a wrapper
63
+ * around Object.defineProperty() that simplifies its use a bit.
64
+ *
65
+ * @param {(object|function)} obj - Target to define property in
66
+ * @param {(string|symbol)} name - Property identifier
67
+ * @param {*} value - Value to assign
68
+ *
69
+ * If `opts.autoDesc` is `true` and this is an `object` that passes the
70
+ * {@link module:@lumjs/core/types.doesDescriptor} test, it will be
71
+ * used as the template descriptor to assign the property.
72
+ *
73
+ * In any other case, this will be used as the `value` property of
74
+ * a generated descriptor to be assigned.
75
+ *
76
+ * @param {object} [opts] Options
77
+ *
78
+ * @returns {object} `obj`
79
+ * @alias module:@lumjs/core/obj.df
80
+ */
81
+ function df(obj, name, value, opts)
82
+ {
83
+ const log = {obj,name,value,opts};
84
+ needObj(obj, {log, fun: true});
85
+ needType(TYPES.PROP, name, {log});
86
+
87
+ opts = clone(DEF_OPTS, opts);
88
+
89
+ const desc = descriptor(((opts.autoDesc && doesDescriptor(value))
90
+ ? value
91
+ : {value}), opts);
92
+
93
+ return Object.defineProperty(obj, name, desc);
94
+ }
95
+
96
+ // Sub-export
97
+ df(df, 'descriptor', descriptor);
98
+ df(df, 'lazy', lazy);
99
+
100
+ /**
101
+ * A wrapper around df() that defines multiple properties at once.
102
+ *
103
+ * @param {(object|function)} obj - Target to define property in
104
+ * @param {object} values
105
+ * All string-keyed enumerable properties will be passed to df()
106
+ * @param {object} [opts] See df() for details
107
+ *
108
+ * @returns {object} `obj`
109
+ * @alias module:@lumjs/core/obj.dfa
110
+ */
111
+ function dfa(obj, values, opts)
112
+ {
113
+ const log = {obj,values,opts};
114
+ needObj(values, {log});
115
+
116
+ for (const key in values)
117
+ {
118
+ df(obj, key, values[key], opts);
119
+ }
120
+
121
+ return obj;
122
+ }
123
+
124
+ /**
125
+ * Build magic closures to define properties using a chained syntax.
126
+ *
127
+ * This actually creates three closures, one around df(), another
128
+ * that wraps dfa(), and one that wraps the lazy() function.
129
+ * You can switch between them using magic properties.
130
+ *
131
+ * The closures when called will pass their arguments to the function
132
+ * they are wrapping, and in a bit of brain-melting recursion, the
133
+ * return value from each closure is the closure itself.
134
+ *
135
+ * Magic Properties that are added to both closures:
136
+ *
137
+ * - `one(name, value)` → A closure that wraps df()
138
+ * - `all(values)` → A closure that wraps dfa()
139
+ * - `update(opts)` → Update the options used by the closures.
140
+ * Uses Object.assign() to copy options from the specied object
141
+ * to the internal options object from the original dfc() call.
142
+ * This is a method call, NOT a closure function.
143
+ * - `opts` → The actual options object used by the closures.
144
+ * - `obj` → The target object used by the closures.
145
+ * - `next(obj)` → A wrapper method that will call dfc().
146
+ * When using this method, a *new set of closures* will be created,
147
+ * with the current options, but a new target object.
148
+ * If you use this in a chained statement, any calls after this
149
+ * will be using the new closures rather than the original ones.
150
+ *
151
+ * @param {(object|function)} obj - Target to define property in;
152
+ * available as the `obj` magic property on the closures.
153
+ *
154
+ * @param {object} [opts] Options - see df() for details;
155
+ * available as the `opts` magic property on the closures.
156
+ *
157
+ * @returns {function} The `one` closure.
158
+ */
159
+ function dfc(obj, opts={})
160
+ {
161
+ const fnOne = function (name, value)
162
+ {
163
+ df(obj, name, value, opts);
164
+ return fnOne;
165
+ }
166
+
167
+ const fnAll = function (values)
168
+ {
169
+ dfa(obj, values, opts);
170
+ return fnAll;
171
+ }
172
+
173
+ const fnLazy = function (name, initfunc)
174
+ {
175
+ lazy(obj, name, initfunc, opts);
176
+ return fnLazy;
177
+ }
178
+
179
+ const ctx =
180
+ {
181
+ obj: {value: obj},
182
+ opts: {value: opts},
183
+ one: {value: fnOne},
184
+ all: {value: fnAll},
185
+ update:
186
+ {
187
+ value()
188
+ {
189
+ cp(opts, ...arguments);
190
+ return this;
191
+ },
192
+ },
193
+ next:
194
+ {
195
+ value(newobj)
196
+ {
197
+ return dfc(newobj, opts);
198
+ },
199
+ },
200
+ }
201
+
202
+ dps(fnOne, ctx);
203
+ dps(fnAll, ctx);
204
+
205
+ return fnOne;
206
+ }
207
+
208
+
209
+
210
+ module.exports =
211
+ {
212
+ df, dfa, dfc, lazy,
213
+ }
package/lib/obj/index.js CHANGED
@@ -4,9 +4,11 @@
4
4
  */
5
5
 
6
6
  const apply = require('./apply');
7
+ const assignd = require('./assignd');
7
8
  const {copyAll,duplicateOne,duplicateAll} = require('./copyall');
8
9
  const copyProps = require('./copyprops');
9
10
  const {CLONE,clone,addClone,cloneIfLocked} = require('./clone');
11
+ const {df,dfa,dfc,lazy} = require('./df');
10
12
  const {flip, flipKeyVal, flipMap} = require('./flip');
11
13
  const {getMethods,signatureOf,MethodFilter} = require('./getmethods');
12
14
  const getProperty = require('./getproperty');
@@ -24,7 +26,8 @@ const
24
26
 
25
27
  module.exports =
26
28
  {
27
- cp, CLONE, clone, addClone, cloneIfLocked, lock, addLock,
29
+ assignd, cp, CLONE, clone, addClone, cloneIfLocked, lock, addLock,
30
+ df, dfa, dfc, lazy,
28
31
  mergeNested, syncNested, copyProps, copyAll, ns,
29
32
  getObjectPath, setObjectPath, getNamespace, setNamespace,
30
33
  getProperty, duplicateAll, duplicateOne, getMethods, signatureOf,
package/lib/types/def.js CHANGED
@@ -7,6 +7,14 @@ const clone = (...args) => Object.assign({}, ...args);
7
7
  /**
8
8
  * A wrapper around `Object.defineProperty()` with added flair!
9
9
  *
10
+ * FUTURE DEPRECATION: When I release @lumjs/core 2.0, this function
11
+ * will be deprecated! It's gotten almost as convoluted as the old
12
+ * prop() method it replaced from my original Nano.js libraries.
13
+ * I have written new `obj.{df,dfa,dfc}` functions that will replace this
14
+ * function going forward. The related `types.lazy()` function will also
15
+ * be refactored to use the new functions, and moved to the `obj` module,
16
+ * although for the `2.x` lifetime an alias will remain in the `type` module.
17
+ *
10
18
  * @param {(object|function)} obj - The object to add a property to.
11
19
  *
12
20
  * @param {?(string|symbol|boolean|object)} name
@@ -73,8 +81,8 @@ const clone = (...args) => Object.assign({}, ...args);
73
81
  * directly, so any changes will be made on the original.
74
82
  *
75
83
  * @param {boolean} [opts.configurable=true] Default `configurable` value
76
- * @param {boolean} [opts.enumerable=false] Default `enumerable` value
77
- * @param {boolean} [opts.writable=false] Default `writable` value
84
+ * @param {boolean} [opts.enumerable] Default `enumerable` value
85
+ * @param {boolean} [opts.writable] Default `writable` value
78
86
  *
79
87
  * Only used with *data descriptors* and will be ignored with
80
88
  * *accessor* descriptors.
package/lib/types/lazy.js CHANGED
@@ -100,6 +100,12 @@ const {doesDescriptor} = require('./basics');
100
100
  *
101
101
  * This is an extension of the [def()]{@link module:@lumjs/core/types.def}
102
102
  * method, and indeed an alias called `def.lazy()` is also available.
103
+ *
104
+ * IMPORTANT: In version 2.x, this will be refactored to use the new
105
+ * [obj.df()]{@link module:@lumjs/core/obj.df} function that will replace
106
+ * def(). There's already an alias called `df.lazy()` available now.
107
+ * This will also be moved to the `obj` module, with an alias left
108
+ * in the `types` module for the duration of the 2.x lifecycle.
103
109
  *
104
110
  * @param {(object|function)} target - The object to add the property to.
105
111
  *
@@ -2,44 +2,103 @@ const {F, S, B} = require('./js');
2
2
  const {isType, isa} = require('./isa');
3
3
  const {isObj, isComplex} = require('./basics');
4
4
 
5
+ // Internal function used by both needObj and needType
6
+ function needOpts(opts, fn, bopt, ons)
7
+ {
8
+ if (isObj(opts))
9
+ {
10
+ if (opts.log)
11
+ { // Make a closure to handle the logging
12
+ const info = (opts.log === true) ? [opts]
13
+ : (Array.isArray(opts.log) ? opts.log : [opts.log]);
14
+ opts.$log = () => console.error(...info);
15
+ }
16
+ }
17
+ else if (typeof opts === S)
18
+ { // A message was passed.
19
+ opts =
20
+ {
21
+ msg: opts,
22
+ }
23
+ if (ons) ons(opts);
24
+ }
25
+ else if (typeof opts === B)
26
+ {
27
+ opts = {[bopt]: opts}
28
+ }
29
+ else
30
+ {
31
+ console.error('invalid options', {fn, opts});
32
+ opts = {};
33
+ }
34
+ return opts;
35
+ }
36
+
37
+ exports._needOptions = needOpts;
38
+
5
39
  /**
6
40
  * If a value is not an object, throw an error.
7
41
  *
8
42
  * @param {*} v - The value we're testing.
9
- * @param {(boolean|string)} [allowFunc=false] - Also accept `function`?
43
+ *
44
+ * @param {(object|boolean|string)} [opts] Options
45
+ *
46
+ * - If this is a string, it will be used as `opts.msg`,
47
+ * and `opts.fun` will be determined by the presence
48
+ * of the word 'function' (case-insensitive) in the message.
49
+ * - If this is a boolean, it will be used as `opts.fun`,
50
+ * and the default message will be used.
51
+ *
52
+ * @param {boolean} [opts.fun=false] Also accept `function`?
10
53
  *
11
54
  * By default this function uses `isObj()` to perform the
12
- * test. If `allowFunc` is `true` then it'll use `isComplex()` instead.
55
+ * test. If `opts.fun` is `true` then it'll use `isComplex()` instead.
56
+ *
57
+ * @param {(object|string|true)} [opts.log] Optional debugging information.
58
+ *
59
+ * This is a way to specify arguments to be sent to the
60
+ * console.error() method before the TypeError is thrown.
61
+ *
62
+ * If this is an `Array` object, it will be used as positional arguments.
13
63
  *
14
- * If this is a `string` then it'll be assigned to the `msg`
15
- * parameter, and `allowFunc` will be `true` if the word
16
- * `function` is found in the message, or `false` otherwise.
64
+ * If it is boolean `true`, the `opts` object itself will be used.
17
65
  *
18
- * @param {string} [msg] A custom error message.
66
+ * Any other value will be used as the sole argument.
19
67
  *
20
- * If not specified, a generic one will be used.
68
+ * @param {string} [opts.msg] The error message to use on failure.
69
+ *
70
+ * If not specified, a simple default message will be used.
71
+ *
72
+ * @param {?string} [msg=null] A positional version of `opts.msg`.
73
+ *
74
+ * Setting `opts.msg` as a named option will take precedence
75
+ * over this positional argument.
21
76
  *
22
77
  * @throws {TypeError} If the type check failed.
23
78
  * @alias module:@lumjs/core/types.needObj
24
79
  */
25
- function needObj (v, allowFunc=false, msg=null)
80
+ function needObj (v, opts={}, msg=null)
26
81
  {
27
- if (typeof allowFunc === S)
28
- { // A message was passed.
29
- msg = allowFunc;
30
- allowFunc = msg.toLowerCase().includes(F);
31
- }
82
+ opts = needOpts(opts, 'needObj', 'fun',
83
+ (o) => o.fun = o.msg.toLowerCase().includes(F));
32
84
 
33
- const ok = allowFunc ? isComplex(v) : isObj(v);
85
+ const ok = opts.fun ? isComplex(v) : isObj(v);
34
86
 
35
87
  if (!ok)
36
88
  { // Did not pass the test.
37
- if (typeof msg !== S)
89
+ if (typeof opts.msg === S)
90
+ {
91
+ msg = opts.msg;
92
+ }
93
+ else if (typeof msg !== S)
38
94
  { // Use a default message.
39
95
  msg = "Invalid object";
40
- if (allowFunc)
96
+ if (opts.fun)
41
97
  msg += " or function";
42
98
  }
99
+
100
+ if (opts.$log) opts.$log();
101
+
43
102
  throw new TypeError(msg);
44
103
  }
45
104
  }
@@ -51,28 +110,42 @@ exports.needObj = needObj;
51
110
  *
52
111
  * @param {string} type - The type name as per `isType()`.
53
112
  * @param {*} v - The value we're testing.
54
- * @param {string} [msg] A custom error message.
113
+ *
114
+ * @param {(object|string)} [opts] Options
115
+ *
116
+ * If this is a string, it will be used as `opts.msg`.
117
+ *
118
+ * @param {string} [opts.msg] See needObj() for details.
119
+ * @param {(object|string|true)} [opts.log] See needObj() for details.
120
+ *
121
+ * @param {?string} [msg=null] A positional version of `opts.msg`;
122
+ * handled the same was as needObj().
123
+ *
55
124
  * @throws {TypeError} If the type check failed.
56
125
  * @alias module:@lumjs/core/types.needType
57
126
  */
58
- function needType (type, v, msg, unused)
127
+ function needType (type, v, opts={}, msg=null)
59
128
  {
129
+ opts = needOpts(opts, 'needType', 'null');
130
+
131
+ if (typeof opts.null === B)
132
+ {
133
+ console.warn("needType(): 'allowNull' is no longer supported");
134
+ }
135
+
60
136
  if (!isType(type, v))
61
137
  {
62
- if (typeof msg === B)
138
+ if (typeof opts.msg === S)
63
139
  {
64
- console.warn("needType(): 'allowNull' is no longer supported");
65
- if (typeof unused === S)
66
- { // Compatibility with old code.
67
- msg = unused;
68
- }
140
+ msg = opts.msg;
69
141
  }
70
-
71
- if (typeof msg !== S)
142
+ else if (typeof msg !== S)
72
143
  { // Use a default message.
73
144
  msg = `Invalid ${type} value`;
74
145
  }
75
146
 
147
+ if (opts.$log) opts.$log();
148
+
76
149
  throw new TypeError(msg);
77
150
  }
78
151
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumjs/core",
3
- "version": "1.37.2",
3
+ "version": "1.38.0",
4
4
  "main": "lib/index.js",
5
5
  "exports":
6
6
  {
@@ -17,6 +17,7 @@
17
17
  "./modules": "./lib/modules.js",
18
18
  "./obj": "./lib/obj/index.js",
19
19
  "./obj/cp": "./lib/obj/cp.js",
20
+ "./obj/df": "./lib/obj/df.js",
20
21
  "./observable": "./lib/observable.js",
21
22
  "./opt": "./lib/opt/index.js",
22
23
  "./opt/args": "./lib/opt/args.js",