dotfiles/vscode/.vscode/extensions/randomfractalsinc.vscode-data-preview-2.3.0/node_modules/synonomous/index.js
Errol Sancaktar ff17c17e23 vscode
2024-06-14 09:31:58 -06:00

255 lines
11 KiB
JavaScript

'use strict';
var transformers = require('./transformers');
var REGEXP_INTEGER = /^\d+$/;
var optionNames = ['transformations', 'propPath', 'dictPath', 'force'];
/**
* @classdesc This object holds a list of transformations used by {@link Synonomous.prototype.getSynonyms} and {@link Synonomous.prototype.decorateList}.
*
* Additional transformer functions may be mixed into the prototype (or added to an instance).
*
* @param {object} [options]
* @param {string[]} [options.transformations] - If omitted, {@link Synonomous.prototype.transformations} serves as a default.
* @param {string|string[]} [options.propPath] - If omitted, {@link Synonomous.prototype.propPath} serves as a default.
* @param {string|string[]} [options.dictPath] - If omitted, {@link Synonomous.prototype.dictPath} serves as a default.
* @param {boolean} [options.force=false] - If truthy, new property values override existing values; else new values are discarded.
* @constructor
*/
function Synonomous(options) {
if (options) {
optionNames.forEach(function(key) {
if (options[key]) {
this[key] = options[key];
}
}, this);
}
}
Synonomous.prototype = {
constructor: Synonomous,
/**
* @summary Default list of active registered transformations by _or_ an object whose keys name the transformations.
* @desc Used by {@link Synonomous.prototype.getSynonyms} and {@link Synonomous.prototype.decorateList}.
* When an object, the former just uses the keys, ignoring the values, while the latter uses both the keys and the values.
*
* An override may be defined on the instance, easily done by supplying as a constructor option.
*
* This is a global default; mutate only if you want to change the default for all your instances.
* @see {@link Synonomous.prototype.verbatim}
* @see {@link Synonomous.prototype.toCamelCase}
* @default
* @type {string[]|object}
* @memberOf Synonomous#
*/
transformations: [
'verbatim',
'toCamelCase'
],
/**
* @summary Drill down path for name to make synonyms of.
* @desc Used by {@link Synonomous.prototype.decorateList}.
*
* This is the default property dot-path within each list element to find the value to make synonyms of.
* If undefined (and no temporary override is given in the call to `decorateList`),
* the element value itself (coerced to a string) is used to make the synonyms.
*
* The setter accepts any falsy value to undefine; or a string of dot-separated parts; or an array of parts.
*
* The getter always returns an array with a `toString` override that returns dot-separated string when coerced to a string.
*
* An override may be defined on the instance, easily done by supplying as a constructor option.
*
* The global default for all instances can be reset using the setter with the prototype as the execution context,
* _e.g._ `Synonomous.prototype.propPath = newValue;`.
*
* @type {undefined|string|string[]}
* @memberOf Synonomous#
*/
set propPath(crumbs) {
this._propPath = newBreadcrumbs(crumbs);
},
get propPath() {
return this._propPath;
},
_propPath: ['name'], // default for all instances
/**
* @summary Default path to property to decorate; or undefined to decorate the object itself.
* @desc Used by {@link Synonomous.prototype.decorate} and {@link Synonomous.prototype.decorateList}.
*
* The setter accepts any falsy value to undefine; or a string of dot-separated parts; or an array of parts.
*
* The getter always returns an array with a `toString` override that returns dot-separated string when coerced to a string.
*
* If undefined, decorations are placed in `list[this.dictPath[0]][this.dictPath[1]][etc]`; else decorations are placed directly on `list` itself.
*
* An override may be defined on the instance, easily done by supplying to as a constructor option.
*
* The global default for all instances can be reset using the setter with the prototype as the execution context,
* _e.g._ `Synonomous.prototype.dictPath = newValue;`.
*
* @type {undefined|string|string[]}
* @memberOf Synonomous#
*/
set dictPath(crumbs) {
this._dictPath = newBreadcrumbs(crumbs);
},
get dictPath() {
return this._dictPath;
},
_dictPath: [], // default for all instances, settable by Synonomous.prototype.dictPath setter
/**
* If `name` is a string and non-blank, returns an array containing unique non-blank synonyms of `name` generated by the transformer functions named in `this.transformations`.
* @param {string} name - String to make synonyms of.
* @parma {string[]|object} transformations - When provided, temporarily overrides `this.transformations`.
* @memberOf Synonomous#
*/
getSynonyms: function(name, transformations) {
var synonyms = [];
if (typeof name === 'string' && name) {
transformations = transformations || this.transformations;
if (!Array.isArray(transformations)) {
transformations = Object.keys(transformations);
}
transformations.forEach(function(key) {
if (typeof transformers[key] !== 'function') {
throw new ReferenceError('Unknown transformer "' + key + '"');
}
var synonym = transformers[key](name);
if (synonym !== '' && !(synonym in synonyms)) {
synonyms.push(synonym);
}
});
}
return synonyms;
},
/**
* Decorate an object `obj` with properties named in `propNames` all referencing `item`.
* @param {object} obj - The object to decorate. If `this.dictPath` is defined, then decorate `obj[this.dictPath]` instead (created as needed).
*
* @param {string[]} propNames
* @param item
* @returns {object} `obj`, now with additional properties (possibly)
*/
decorate: function(obj, propNames, item) {
var drilldownContext = drilldown(obj, this.dictPath),
decoratingObjectItself = drilldownContext === obj,
force = this.force;
propNames.forEach(function(propName) {
if (
!(decoratingObjectItself && REGEXP_INTEGER.test(propName)) &&
(force || !(propName in drilldownContext))
) {
drilldownContext[propName] = item;
}
});
return obj;
},
/**
* @summary Add dictionary synonyms to an array.
*
* @desc Adds synonyms for a single element (`index`) or the entire array, based on a given property of each element (`propPath`) or the element itself.
*
* That is, each element is either itself converted to a string; or is an object with a property named by following `propPath` which is converted to a string.
*
* For each element, all transformers named in `this.transformations` are run on that string.
* * _When `this.transformations` is an array:_
* **Create dictionary entries (synonyms) for the element.**
* Specifically: All the resulting unique non-blank "synonyms" are added as properties to the array with the value of the property being a reference to the element (if it was an object) or a copy of the element (if it was a string), subject to the following rules:
* 1. Duplicate synonyms are not added (unless `this.force` is truthy).
* 2. Blank synonyms are not added.
* 3. Integer synonyms are not added because they are indistinguishable from and may clash with array indexes.
* * _When `this.transformations` is an non-array object:_
* **Create a new property inside the element for each transformation.**
* Specifically: The keys of `this.transformations` name the transformers. The values are dot-paths (dot-separated-strings or arrays) to properties inside each element, set to the string returned by the transformer named by the key.
*
* @param {number} [index] - Index of element of `list` to add synonyms for. If omitted:
* 1. Adds synonyms for all elements of `list`.
* 2. `list` and `propPath` are promoted to the 1st and 2nd parameter positions, respectively.
* @param {(string|Object.<string, string>)[]} list - Array whose element(s) to make synonyms of _and_ the object to decorate. If `this.dictPath` is defined, then decorate `list[this.dictPath]` instead (created as needed).
* @param {string} [propPath=this.propPath] - Name of the property in each element of `list` to make synonyms of. If defined _and_ list element is an object, adds synonyms of `list[propPath]` as string; else adds synonyms of the list element itself as string.
* @returns {Array} `list`
* @memberOf Synonomous#
*/
decorateList: function(index, list, propPath) {
var elements;
if (typeof index === 'number') {
elements = [list[index]];
} else {
// promote args
list = elements = arguments[0];
propPath = arguments[1];
}
propPath = propPath ? newBreadcrumbs(propPath) : this.propPath;
elements.forEach(function(item) {
var value = propPath !== undefined && typeof item === 'object' ? drilldown(item, propPath) : item;
if (Array.isArray(this.transformations)) {
var synonyms = this.getSynonyms(value);
this.decorate(list, synonyms, item);
} else {
Object.keys(this.transformations).forEach(injectTransformedValueIntoItem.bind(this, item, value));
}
}, this);
return list;
}
};
// a.k.a.'s:
Synonomous.prototype.decorateObject = Synonomous.prototype.decorate;
Synonomous.prototype.decorateArray = Synonomous.prototype.decorateList;
function drilldown(collection, breadcrumbs) {
return breadcrumbs.reduce(function(result, crumb) {
return result[crumb] || (result[crumb] = Object.create(null));
}, collection);
}
function newBreadcrumbs(crumbs) {
if (!crumbs) {
crumbs = [];
} else if (Array.isArray(crumbs)) {
crumbs = crumbs.slice();
} else {
crumbs = (crumbs + '').split('.');
}
crumbs.toString = crumbsToString;
return crumbs;
}
/**
* @this {Array}
* @returns {string}
*/
function crumbsToString() {
return this.join('.');
}
function injectTransformedValueIntoItem(item, value, transformation) {
var transformer = transformers[transformation],
path = this.transformations[transformation],
pathList = Array.isArray(path) ? path.slice() : path.split('.'),
propName = pathList.splice(pathList.length - 1, 1)[0],
drillDownContext = drilldown(item, pathList);
if (this.force || !(propName in drillDownContext)) {
drillDownContext[propName] = transformer(value);
}
}
module.exports = Synonomous;