@lumjs/core 1.7.1 → 1.8.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/lib/index.js CHANGED
@@ -19,117 +19,78 @@
19
19
  */
20
20
  const types = require('./types');
21
21
 
22
- /**
23
- * A helper for building modules
24
- *
25
- * @alias module:@lumjs/core.ModuleBuilder
26
- * @see module:@lumjs/core/modulebuilder
27
- */
28
- const Builder = require('./modulebuilder');
29
-
30
- // And we'll use the builder to define the rest of this.
31
- const {has,can,from} = Builder.build(module);
32
-
33
22
  /**
34
23
  * Define properties on an object or function
35
24
  * @name module:@lumjs/core.def
36
- * @function
37
25
  * @see module:@lumjs/core/types.def
38
26
  */
27
+ const def = types.def;
39
28
 
40
29
  /**
41
- * Define properties on an object or function
42
- * @name module:@lumjs/core.lazy
43
- * @function
30
+ * Define *lazy* properties on an object or function
31
+ * @alias module:@lumjs/core.lazy
44
32
  * @see module:@lumjs/core/types.lazy
45
33
  */
34
+ const lazy = types.lazy;
46
35
 
47
- // Our fundamental bits.
48
- has('types', {value: types});
49
- has('def', {value: types.def});
50
- has('lazy', {value: types.lazy});
51
-
52
- // The module builder itself.
53
- has('ModuleBuilder', {value: Builder});
36
+ // A descriptor template for def/lazy.
37
+ const E = def.e;
54
38
 
55
- /**
56
- * Return a set of functions for building a module.
57
- *
58
- * @name module:@lumjs/core.buildModule
59
- * @function
60
- * @see module:@lumjs/core/modulebuilder.build
61
- */
62
- has('buildModule',
63
- {
64
- value: function(m,o)
65
- {
66
- return Builder.build(m,o);
67
- },
68
- });
69
-
70
- /**
71
- * Create a new `ModuleBuilder` instance.
72
- *
73
- * @name module:@lumjs/core.newBuilder
74
- * @function
75
- * @see module:@lumjs/core/modulebuilder.new
76
- */
77
- has('newBuilder',
78
- {
79
- value: function(m, o)
80
- {
81
- return Builder.new(m,o);
82
- }
83
- });
39
+ def(exports, 'types', types, E);
40
+ def(exports, 'def', def, E);
41
+ def(exports, 'lazy', lazy, E);
84
42
 
85
43
  /**
86
44
  * Array utility functions «Lazy»
87
45
  * @name module:@lumjs/core.arrays
88
46
  * @type {module:@lumjs/core/arrays}
89
47
  */
90
- can('arrays');
48
+ lazy(exports, 'arrays', () => require('./arrays'), E);
91
49
 
92
50
  /**
93
51
  * Information about the JS context we're running in
94
52
  * @name module:@lumjs/core.context
95
53
  * @type {module:@lumjs/core/context}
96
54
  */
97
- has('context');
55
+ def(exports, 'context', require('./context'), E);
98
56
 
99
57
  /**
100
58
  * Functions for working with strings and locales «Lazy»
101
59
  * @name module:@lumjs/core.strings
102
60
  * @type {module:@lumjs/core/strings}
103
61
  */
104
- can('strings');
62
+ lazy(exports, 'strings', () => require('./strings'), E);
105
63
 
106
64
  /**
107
65
  * Functions for working with binary flags «Lazy»
108
66
  * @name module:@lumjs/core.flags
109
67
  * @type {module:@lumjs/core/flags}
110
68
  */
111
- can('flags');
69
+ lazy(exports, 'flags', () => require('./flags'), E);
112
70
 
113
71
  /**
114
72
  * Functions for manipulating objects «Lazy»
115
73
  * @name module:@lumjs/core.obj
116
74
  * @type {module:@lumjs/core/obj}
117
75
  */
118
- can('obj');
76
+ lazy(exports, 'obj', () => require('./obj'), E);
119
77
 
120
78
  /**
121
79
  * Functions for getting values and properties with fallback defaults «Lazy»
122
80
  * @name module:@lumjs/core.opt
123
81
  * @type {module:@lumjs/core/opt}
124
82
  */
125
- can('opt');
83
+ lazy(exports, 'opt', () => require('./opt'), E);
126
84
 
127
- /**
128
- * Meta functions related to JS modules «Lazy»
129
- * @alias module:@lumjs/core.modules
130
- * @see module:@lumjs/core/modules
131
- */
132
- can('modules');
85
+ // Get a bunch of properties from a submodule.
86
+ function from(modname, ...libs)
87
+ {
88
+ const submod = require('./'+modname);
89
+ for (const lib of libs)
90
+ {
91
+ def(exports, lib, submod[lib], E);
92
+ }
93
+ }
133
94
 
134
95
  // ObjectID stuff is imported directly without registering a sub-module.
135
96
  from('objectid', 'randomNumber', 'InternalObjectId');
@@ -170,7 +131,7 @@ from('meta', 'stacktrace', 'AbstractClass', 'Functions', 'NYI');
170
131
  * @function
171
132
  * @see module:@lumjs/core/enum
172
133
  */
173
- can('Enum', true);
134
+ lazy(exports, 'Enum', () => require('./enum'), E);
174
135
 
175
136
  /**
176
137
  * Make an object support the *Observable* API «Lazy»
@@ -178,4 +139,4 @@ can('Enum', true);
178
139
  * @function
179
140
  * @see module:@lumjs/core/observable
180
141
  */
181
- can('observable');
142
+ lazy(exports, 'observable', () => require('./observable'), E);
package/lib/types/def.js CHANGED
@@ -221,4 +221,8 @@ function def(obj, name, value, opts)
221
221
 
222
222
  } // def()
223
223
 
224
- module.exports = def;
224
+ module.exports = def;
225
+
226
+ // Now we'll setup the descriptor template accessors.
227
+ const DT = require('./dt');
228
+ DT.addTo(def);
@@ -0,0 +1,70 @@
1
+ const def = require('./def');
2
+
3
+ /**
4
+ * Build a descriptor template using a simple property syntax.
5
+ *
6
+ * TODO: document this further.
7
+ *
8
+ * @alias module:@lumjs/core/types~DescriptorTemplate
9
+ */
10
+ function DescriptorTemplate()
11
+ {
12
+ if (!new.target) return new DescriptorTemplate();
13
+ def(this, '_v', true);
14
+ }
15
+
16
+ const dp = DescriptorTemplate.prototype;
17
+
18
+ function pa(prop)
19
+ {
20
+ return(
21
+ {
22
+ get()
23
+ { // Set a regular, enumerable, writable value.
24
+ this[prop] = this._v;
25
+ return this;
26
+ }
27
+ });
28
+ }
29
+
30
+ function va(value)
31
+ {
32
+ return(
33
+ {
34
+ get()
35
+ { // Set a hidden, but configurable value.
36
+ def(this, '_v', value);
37
+ return this;
38
+ }
39
+ });
40
+ }
41
+
42
+ def(dp, 'is', va(true));
43
+ def(dp, 'not', va(false));
44
+ def(dp, 'e', pa('enumerable'));
45
+ def(dp, 'c', pa('configurable'));
46
+ def(dp, 'w', pa('writable'));
47
+
48
+ function na(accessor)
49
+ {
50
+ return(
51
+ {
52
+ get()
53
+ {
54
+ const dt = new DescriptorTemplate();
55
+ return dt[accessor];
56
+ }
57
+ });
58
+ }
59
+
60
+ const DTA = ['is','not','e','c','w'];
61
+
62
+ DescriptorTemplate.addTo = function(target)
63
+ {
64
+ for (const a of DTA)
65
+ {
66
+ def(target, a, na(a));
67
+ }
68
+ }
69
+
70
+ module.exports = DescriptorTemplate;
package/lib/types/lazy.js CHANGED
@@ -24,6 +24,8 @@ const {doesDescriptor} = require('./basics');
24
24
  * If this is `false`, the value will be returned, but will **not** be
25
25
  * *assigned*, so the lazy accessor property will remain.
26
26
  *
27
+ * Leave it `undefined` to use the default assignment behaviour.
28
+ *
27
29
  * @property {*} [def] Special options for `def()`
28
30
  *
29
31
  * The [def()]{@link module:@lumjs/core/types.def} function has an
@@ -210,4 +212,7 @@ function lazy(target, name, initfunc, opts={})
210
212
  // Gotta be one of the greatest lines...
211
213
  def(def, 'lazy', lazy);
212
214
 
215
+ // And this is a close second...
216
+ def(lazy, 'def', def);
217
+
213
218
  module.exports = lazy;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumjs/core",
3
- "version": "1.7.1",
3
+ "version": "1.8.1",
4
4
  "main": "lib/index.js",
5
5
  "exports":
6
6
  {
@@ -12,7 +12,6 @@
12
12
  "./flags": "./lib/flags.js",
13
13
  "./obj": "./lib/obj/index.js",
14
14
  "./opt": "./lib/opt.js",
15
- "./modulebuilder": "./lib/modulebuilder.js",
16
15
  "./modules": "./lib/modules.js",
17
16
  "./meta": "./lib/meta.js",
18
17
  "./enum": "./lib/enum.js",
@@ -27,12 +26,11 @@
27
26
  },
28
27
  "devDependencies":
29
28
  {
30
- "@lumjs/tests": "^1.3.0"
29
+ "@lumjs/tests": "^1.7.1"
31
30
  },
32
31
  "scripts":
33
32
  {
34
- "-TODO-1": "Use `lumtest` once its available",
35
- "test": "prove -e node --ext js ./test",
33
+ "test": "node ./node_modules/@lumjs/tests/bin/lumtest.js",
36
34
  "build-docs": "jsdoc -c ./jsdoc.json"
37
35
  }
38
36
  }
@@ -1,415 +0,0 @@
1
-
2
- const
3
- {
4
- S,F,B,
5
- isObj,needObj,needType,
6
- def,lazy
7
- } = require('./types');
8
-
9
- const clone = require('./obj/copyall').duplicateOne;
10
-
11
- // Methods we want to export in the functional API.
12
- const BUILD_METHODS = ['has', 'can', 'from', 'set'];
13
-
14
- // Fallback locale
15
- const DLOC = 'en-US';
16
-
17
- /**
18
- * A class to make building modules easier.
19
- *
20
- * Basically wraps calls to `require()`, `def()`, `lazy()`,
21
- * and assignments to `module.exports` into a simple set of methods.
22
- *
23
- * @exports module:@lumjs/core/modulebuilder
24
- */
25
- class ModuleBuilder
26
- {
27
- /**
28
- * Build a new ModuleBuilder instance.
29
- *
30
- * @param {object} targetModule - The `module` context variable.
31
- * @param {object} [opts] - Options to change some default settings.
32
- * @param {boolean} [opts.configurable=false] Default `configurable` value.
33
- * @param {boolean} [opts.enumerable=true] Default `enumerable` value.
34
- * @param {boolean} [opts.writable=false] Default `writable` value.
35
- *
36
- * @param {number} [opts.nested=NESTED_ERROR] How to handle nested names.
37
- *
38
- * If the `name` parameter in either the `has()` or `can()` method is passed
39
- * as the path to a nested module with one or more `/` characters,
40
- * this mode will determine how we derive the property name.
41
- *
42
- * For each mode, we'll use an example `name` of `./some/nested/path`.
43
- *
44
- * - `ModuleBuilder.NESTED_ERROR` `[0]` (default mode)
45
- * Throw an `Error` saying paths are unhandled.
46
- * - `ModuleBuilder.NESTED_FIRST` `[1]`
47
- * Use the first (non-dot) path element, e.g. `some`
48
- * - `ModuleBuilder.NESTED_LAST` `[2]`
49
- * Use the last path element, e.g. `path`
50
- * - `ModuleBuilder.NESTED_CAMEL` `[3]`
51
- * Convert the name to camelCase, e.g. `someNestedPath`
52
- *
53
- * It's always possible to set the `conf.module` parameter manually as well,
54
- * which avoids the need to generate a separate parameter name.
55
- *
56
- * @param {(string|boolean)} [opts.withLocale=false] Use locales?
57
- *
58
- * If this is a `string` it's the name of the locale (e.g. `en-US`).
59
- *
60
- * If this is `true` we'll use the default locale as determined by
61
- * [getLocale()]{@link module:@lumjs/core/strings.getLocale}.
62
- *
63
- * If this is `false` we won't use locales (unless the `NESTED_CAMEL`
64
- * mode is in use, in which case we'll use the default locale.)
65
- *
66
- */
67
- constructor(targetModule, opts={})
68
- {
69
- needObj(opts, 'opts was not an object');
70
- needObj(targetModule, 'targetModule was not an object');
71
- needObj(targetModule.exports, 'targetModule.exports was not an object');
72
- needType(F, targetModule.require, 'targetModule.require was not a function');
73
-
74
- this.module = targetModule;
75
-
76
- this.configurable = opts.configurable ?? false;
77
- this.enumerable = opts.enumerable ?? true;
78
- this.writable = opts.writable ?? false;
79
-
80
- this.nested = opts.nested ?? ModuleBuilder.NESTED_ERROR;
81
-
82
- if (opts.withLocale)
83
- { // Initialize the strings here.
84
- this.withLocale(opts.locale);
85
- }
86
- }
87
-
88
- // Get a descriptor for a module export.
89
- requireDescriptor(name, conf={})
90
- {
91
- if (typeof conf === S)
92
- { // A quick shortcut for setting module name.
93
- conf = {module: conf};
94
- }
95
- else if (conf === true)
96
- { // Use the lowercase name as the module.
97
- conf = {module: this.$lc(name)};
98
- }
99
- else if (typeof conf.getter === F || typeof conf.setter === F)
100
- { // It's an accessor-style descriptor already.
101
- return conf;
102
- }
103
-
104
- let value = conf.value;
105
-
106
- if (value === undefined)
107
- {
108
- if (typeof conf.module === S)
109
- name = conf.module;
110
- if (!name.startsWith('./'))
111
- name = './'+name;
112
-
113
- value = this.module.require(name);
114
-
115
- if (value && conf.prop && value[conf.prop] !== undefined)
116
- {
117
- value = value[conf.prop];
118
- }
119
- }
120
-
121
- const configurable = conf.configurable ?? this.configurable;
122
- const enumerable = conf.enumerable ?? this.enumerable;
123
- const writable = conf.writable ?? this.writable;
124
-
125
- return {configurable, enumerable, writable, value};
126
- }
127
-
128
- $lc(name)
129
- {
130
- return name.toLocaleLowerCase(this.locale ?? DLOC);
131
- }
132
-
133
- // Normalize a property name.
134
- $normalizeProperty(name)
135
- {
136
- if (name.startsWith('./'))
137
- { // Get rid of the prefix.
138
- name = name.substring(2);
139
- }
140
-
141
- if (name.includes('/'))
142
- { // Multiple paths found.
143
- const names = name.split('/');
144
- if (this.nested === ModuleBuilder.NESTED_FIRST)
145
- {
146
- name = names[0];
147
- }
148
- else if (this.nested === ModuleBuilder.NESTED_LAST)
149
- {
150
- name = names[names.length-1];
151
- }
152
- else if (this.nested === ModuleBuilder.NESTED_CAMEL)
153
- {
154
- name = this.$normalizeWithCamelCase(names);
155
- }
156
- else
157
- {
158
- throw new Error("No valid nested path handling method was set");
159
- }
160
- }
161
-
162
- return name;
163
- }
164
-
165
- withLocale(loc)
166
- {
167
- if (this.strings === undefined)
168
- { // Load the strings library.
169
- this.strings = require('./strings');
170
- }
171
-
172
- if (this.locale === undefined)
173
- { // Set the locale.
174
- if (typeof loc === S)
175
- { // Use an explicitly passed value.
176
- this.locale = loc;
177
- }
178
- else
179
- { // Use the default locale.
180
- this.locale = this.strings.getLocale();
181
- }
182
- }
183
- }
184
-
185
- $normalizeWithCamelCase(names)
186
- {
187
- this.withLocale();
188
- let name = names.shift().toLocaleLowerCase(this.locale);
189
- for (const path in names)
190
- {
191
- name += this.strings.ucfirst(path, true, this.locale);
192
- }
193
- return name;
194
- }
195
-
196
- /**
197
- * Export a module directly.
198
- *
199
- * @param {string} name - The property name to export.
200
- *
201
- * @param {(object|string|true)} [conf] Additional export configuration options.
202
- *
203
- * If this is a `string` instead of an `object`, it's assumed to be the
204
- * `conf.module` value.
205
- *
206
- * If this is `true` then the `conf` will be set with `module` being the
207
- * lowercase version of `name`.
208
- *
209
- * @param {string} [conf.module=`./${name}`] The module to `require()`.
210
- * @param {string} [conf.prop] If set, we want an exported property
211
- * of this name from the loaded module.
212
- *
213
- * If not set, the entire loaded module will be our exported value.
214
- *
215
- * @param {boolean} [conf.configurable=this.configurable]
216
- * Descriptor `configurable` value.
217
- * @param {boolean} [conf.enumerable=this.enumerable]
218
- * Descriptor `enumerable` value.
219
- * @param {boolean} [conf.writable=this.writable]
220
- * Descriptor `writable` value.
221
- *
222
- * @param {*} [conf.value] An explicit value to set.
223
- *
224
- * This skips the `require()` call entirely.
225
- *
226
- * @returns {object} `this`
227
- */
228
- has(name, conf={})
229
- {
230
- const pname = this.$normalizeProperty(name);
231
- const desc = this.requireDescriptor(name, conf);
232
- def(this.module.exports, pname, desc);
233
- return this;
234
- }
235
-
236
- /**
237
- * Export a lazy-loaded module.
238
- *
239
- * @param {string} name - The property name to export.
240
- *
241
- * @param {object} [conf] Additional export configuration options.
242
- *
243
- * In addition to all the options supported by
244
- * [has()]{@link module:@lumjs/core/modules#has}
245
- * this also supports one further option.
246
- *
247
- * @param {object} [conf.lazy] Advanced options for the `lazy()` call.
248
- *
249
- * If not specified, sane defaults will be used, that ensures the
250
- * `enumerable` descriptor property for the lazy getter is set the
251
- * same as the final descriptor property once its loaded.
252
- *
253
- * @returns {object} `this`
254
- */
255
- can(name, conf={})
256
- {
257
- const pname = this.$normalizeProperty(name);
258
- const enumerable = conf.enumerable ?? this.enumerable;
259
- const lazyOpts = isObj(conf.lazy) ? conf.lazy : {enumerable};
260
- const getter = () => this.requireDescriptor(name, conf);
261
- lazy(this.module.exports, pname, getter, lazyOpts);
262
- return this;
263
- }
264
-
265
- /**
266
- * Re-export some exported properties from a module.
267
- *
268
- * By default this uses `has()` to define the exports, but that
269
- * can be changed using special parameter values.
270
- *
271
- * @param {string} modname - The module to export the properties from.
272
- * @param {...any} libs - What we're exporting.
273
- *
274
- * If this is a `string` it is the name of an exported property we want to
275
- * re-export directly.
276
- *
277
- * If this is `true`, all subsequent values will be exported using `can()`.
278
- *
279
- * If this is `false`, all subsequent values will be exported using `has()`.
280
- *
281
- * If this is an `object`, then it's considered the `conf` parameter for
282
- * the `has()` or `can()` methods. If the `conf` does not have a `module`
283
- * property, the `modname` will be assigned as the `module` property.
284
- * You cannot set the `prop` property, as it's overwritten for every
285
- * exported property.
286
- *
287
- * @returns {object} `this`
288
- */
289
- from(modname, ...libs)
290
- {
291
- let func = 'has';
292
- let curConf = {module: modname};
293
- for (const lib of libs)
294
- {
295
- if (isObj(lib))
296
- { // Change the current config.
297
- curConf = clone(lib);
298
- if (curConf.module === undefined)
299
- { // Make sure the module name is assigned.
300
- curConf.module = modname;
301
- }
302
- }
303
- else if (typeof lib === B)
304
- { // Change the function we're calling.
305
- func = lib ? 'can' : 'has';
306
- }
307
- else if (typeof lib === S)
308
- {
309
- const conf = clone(curConf);
310
- conf.prop = lib;
311
- this[func](lib, conf);
312
- }
313
- else
314
- {
315
- throw new TypeError("libs must be strings, booleans, or objects");
316
- }
317
- }
318
- return this;
319
- }
320
-
321
- /**
322
- * Define an exported property in our module.
323
- *
324
- * This is literally just a wrapper around the
325
- * [def()]{@link module:@lumjs/core/types.def} method,
326
- * that passes `this.module.exports` as the `obj` parameter.
327
- *
328
- * @param {*} name - See `def()` for details.
329
- * @param {*} value - See `def()` for details.
330
- * @param {*} opts - See `def()` for details.
331
- * @returns {(object|function)} What is returned may vary:
332
- *
333
- * If the `def()` returns a *bound* copy of itself, we'll return that
334
- * bound function directly.
335
- *
336
- * In any other case, we return `this` ModuleBuilder instance.
337
- */
338
- set(name, value, opts)
339
- {
340
- const retval = def(this.module.exports, name, value, opts);
341
- if (typeof retval === F && isObj(retval.$this) && retval.$this.bound === retval)
342
- { // A bound copy was returned, we're going to return it directly.
343
- return retval;
344
- }
345
- return this;
346
- }
347
-
348
- /**
349
- * Create a functional API for the ModuleBuilder class.
350
- *
351
- * Basically makes a new `ModuleBuilder` instance, then creates standalone
352
- * closure functions that wrap the main instance methods.
353
- *
354
- * These functions can be imported into a namespace directly.
355
- * They each have a special `builder` property which is a reference
356
- * to the underlying `ModuleBuilder` instance.
357
- *
358
- * There's also a `builder` property in the exported function list,
359
- * as well as a copy of the `def()` method for quick exporting.
360
- *
361
- * Example usage:
362
- *
363
- * ```js
364
- * const {has,can,from,set}
365
- * = require('@lumjs/core').ModuleBuilder.build(module);
366
- * // or the shortcut: require('@lumjs/core).buildModule(module);
367
- *
368
- * // exports.foo = require('./foo');
369
- * has('foo');
370
- *
371
- * // exports.someNestedPath = require('./some/nested/path');
372
- * has('./some/nested/path');
373
- *
374
- * ```
375
- *
376
- * @param {object} targetModule - The `module` for the Builder instance.
377
- * @param {object} [opts] - Any options for the Builder instance.
378
- * @returns {object} An object containing the closure functions.
379
- */
380
- static build(targetModule, opts)
381
- {
382
- const builder = new this(targetModule, opts);
383
- const funcs = {builder, def};
384
- for (const name of BUILD_METHODS)
385
- {
386
- const func = function()
387
- {
388
- return builder[name](...arguments);
389
- }
390
- def(func, 'builder', builder);
391
- funcs[name] = func;
392
- }
393
- return funcs;
394
- }
395
-
396
- /**
397
- * A static alternative to `new ModuleBuilder()`;
398
- *
399
- * @param {object} targetModule
400
- * @param {object} [opts]
401
- * @returns {object} The new `ModuleBuilder` instance.
402
- */
403
- static new(targetModule, opts)
404
- {
405
- return new this(targetModule, opts);
406
- }
407
-
408
- }
409
-
410
- def(ModuleBuilder, 'NESTED_ERROR', 0)
411
- def(ModuleBuilder, 'NESTED_FIRST', 1);
412
- def(ModuleBuilder, 'NESTED_LAST', 2);
413
- def(ModuleBuilder, 'NESTED_CAMEL', 3);
414
-
415
- module.exports = ModuleBuilder;