@jkcfg/std
Advanced tools
Comparing version 0.2.10 to 0.3.0
@@ -1,31 +0,4 @@ | ||
import { log } from './log'; | ||
import { Format, write } from './write'; | ||
import { Encoding, read } from './read'; | ||
import { info, dir } from './fs'; | ||
declare const _default: { | ||
log: typeof log; | ||
Format: typeof Format; | ||
write: typeof write; | ||
Encoding: typeof Encoding; | ||
read: typeof read; | ||
info: typeof info; | ||
dir: typeof dir; | ||
param: { | ||
all: typeof import("./param").all; | ||
Boolean: typeof import("./param").Boolean; | ||
Number: typeof import("./param").Number; | ||
String: typeof import("./param").String; | ||
Object: typeof import("./param").Object; | ||
}; | ||
mix: any; | ||
patch: any; | ||
merge: any; | ||
}; | ||
export default _default; | ||
export { log } from './log'; | ||
export { Format, write } from './write'; | ||
export { Encoding, read } from './read'; | ||
export { info, dir } from './fs'; | ||
export { param } from './param'; | ||
export { mix, patch, merge } from './merge'; | ||
export { parse, unparse } from './parse'; | ||
export { parse, stringify } from './parse'; |
25
index.js
@@ -1,27 +0,4 @@ | ||
import { log } from './log'; | ||
import { Format, write } from './write'; | ||
import { Encoding, read } from './read'; | ||
import { info, dir } from './fs'; | ||
import { param } from './param'; | ||
import { mix, patch, merge } from './merge'; | ||
// The default export is deprecated and will be removed in 3.0.0. | ||
export default { | ||
log, | ||
Format, | ||
write, | ||
Encoding, | ||
read, | ||
info, | ||
dir, | ||
param, | ||
mix, | ||
patch, | ||
merge, | ||
}; | ||
export { log } from './log'; | ||
export { Format, write } from './write'; | ||
export { Encoding, read } from './read'; | ||
export { info, dir } from './fs'; | ||
export { param } from './param'; | ||
export { mix, patch, merge } from './merge'; | ||
export { parse, unparse } from './parse'; | ||
export { parse, stringify } from './parse'; |
@@ -20,4 +20,4 @@ var __rest = (this && this.__rest) || function (s, e) { | ||
export default [ | ||
{ file: 'file1.yaml', content: value1 }, | ||
{ file: 'file2.yaml', content: [v0, v1, v2], format: std.Format.YAMLStream }, | ||
{ path: 'file1.yaml', value: value1 }, | ||
{ path: 'file2.yaml', value: [v0, v1, v2], format: std.Format.YAMLStream }, | ||
... | ||
@@ -83,3 +83,3 @@ ]; | ||
function valueFormat(o) { | ||
let { file, format, value } = o; | ||
let { path, format, value } = o; | ||
if (format === undefined || format === std.Format.FromExtension) { | ||
@@ -90,3 +90,3 @@ if (isString(value)) { | ||
else { | ||
format = formatFromPath(file); | ||
format = formatFromPath(path); | ||
} | ||
@@ -128,6 +128,10 @@ } | ||
} | ||
/* an array with each element a { file, value } object */ | ||
/* an array with each element a { path, value } object */ | ||
let valid = true; | ||
value.forEach((e, i) => { | ||
['file', 'value'].forEach((prop) => { | ||
/* 'file' is the old 'path' property name. Fixup things */ | ||
if (e.file !== undefined) { | ||
e.path = e.file; | ||
} | ||
['path', 'value'].forEach((prop) => { | ||
if (!Object.prototype.hasOwnProperty.call(e, prop)) { | ||
@@ -177,3 +181,3 @@ error(`${nth(i + 1)} element does not have a '${prop}' property`); | ||
* The default export can be: | ||
* 1. an array of { file, value } objects, | ||
* 1. an array of { path, value } objects, | ||
* 2. a promise to such an array, | ||
@@ -205,4 +209,4 @@ * 3. a function evaluating to either 1. or 2. | ||
for (const o of files) { | ||
const { file, value } = o, args = __rest(o, ["file", "value"]); | ||
std.write(value, file, args); | ||
const { path, value } = o, args = __rest(o, ["path", "value"]); | ||
std.write(value, path, args); | ||
} | ||
@@ -209,0 +213,0 @@ } |
158
merge.js
/** | ||
* @module std/merge | ||
*/ | ||
// patch returns a new value that has the fields of `obj`, except | ||
// where overridden by fields in `patchObj`. Entries in common are | ||
// themselves patched. This is similar to `merge` below, but always | ||
// does a deep merge on objects, and always replaces other values. | ||
export function patch(obj, patchObj) { | ||
switch (typeof obj) { | ||
case 'object': { | ||
const result = {}; | ||
for (const [k, v] of Object.entries(obj)) { | ||
if (k in patchObj) { | ||
if (Array.isArray(v)) { | ||
result[k] = patchObj[k]; | ||
} | ||
else { | ||
result[k] = patch(v, patchObj[k]); | ||
} | ||
} | ||
else { | ||
result[k] = v; | ||
} | ||
} | ||
for (const [pk, pv] of Object.entries(patchObj)) { | ||
if (!(pk in obj)) { | ||
result[pk] = pv; | ||
} | ||
} | ||
return result; | ||
} | ||
case 'string': | ||
case 'number': | ||
case 'boolean': | ||
return patchObj; | ||
default: | ||
throw new Error(`unhandled patch case: ${typeof obj}`); | ||
} | ||
} | ||
// merge returns a new value which is `a` merged additively with | ||
// `b`. For values other than objects, this means addition (or | ||
// concatenation), with a coercion if necessary. | ||
// | ||
// If both `a` and `b` are objects, there is some fine control over | ||
// each field. If the key in `b` ends with a `+`, the values are | ||
// summed; otherwise, the value is replaced. Any fields in | ||
// `a` and not in `b` are also assigned in the result. | ||
export function merge(a, b) { | ||
const [typeA, typeB] = [typeof a, typeof b]; | ||
if (typeA === 'string') { | ||
if (typeB === 'string') { | ||
return a + b; | ||
} | ||
return a + JSON.stringify(b); | ||
} | ||
if (typeB === 'string') { | ||
return JSON.stringify(a) + b; | ||
} | ||
if (typeA === 'number' && typeB === 'number') | ||
return a + b; | ||
if (Array.isArray(a) && Array.isArray(b)) | ||
return [...a, ...b]; | ||
if (typeA === 'object' && typeB === 'object') | ||
return objectMerge(a, b); | ||
throw new Error(`merge cannot combine values of types ${typeA} and ${typeB}`); | ||
} | ||
function objectMerge(obj, mergeObj) { | ||
const r = {}; | ||
Object.assign(r, obj); | ||
for (let [key, value] of Object.entries(mergeObj)) { | ||
if (key.endsWith('+')) { | ||
key = key.slice(0, -1); | ||
if (key in obj) { | ||
r[key] = merge(obj[key], value); | ||
continue; | ||
} | ||
} | ||
r[key] = value; | ||
} | ||
return r; | ||
} | ||
function mergeFunc(rule, key, defaultFunc) { | ||
@@ -100,6 +22,23 @@ const f = rule && rule[key]; | ||
for (const [key, value] of Object.entries(b)) { | ||
r[key] = mergeFunc(rules, key, mergeFull)(a[key], value); | ||
r[key] = mergeFunc(rules, key, merge)(a[key], value); | ||
} | ||
return r; | ||
} | ||
function isObject(o) { | ||
const t = typeof o; | ||
return t === 'object' && !Array.isArray(o) && !!o; | ||
} | ||
function assertObject(o, prefix) { | ||
if (!isObject(o)) { | ||
throw new Error(`${prefix}: input value is not an object`); | ||
} | ||
} | ||
function isArray(a) { | ||
return Array.isArray(a); | ||
} | ||
function assertArray(o, prefix) { | ||
if (!isArray(o)) { | ||
throw new Error(`${prefix}: input is not an array`); | ||
} | ||
} | ||
/** | ||
@@ -112,6 +51,10 @@ * Merge strategy deep merging objects. | ||
* objects. It's possible to provide a set of rules to override the merge | ||
* strategy for some properties. See [[mergeFull]]. | ||
* strategy for some properties. See [[merge]]. | ||
*/ | ||
export function deep(rules) { | ||
return (a, b) => objectMerge2(a, b, rules); | ||
return (a, b) => { | ||
assertObject(a, 'deep'); | ||
assertObject(b, 'deep'); | ||
return objectMerge2(a, b, rules); | ||
}; | ||
} | ||
@@ -139,3 +82,3 @@ /** | ||
* | ||
* mergeFull(a, b, { o: first() }); | ||
* merge(a, b, { o: first() }); | ||
* ``` | ||
@@ -180,3 +123,3 @@ * | ||
* | ||
* mergeFull(a, b, { o: replace() }); | ||
* merge(a, b, { o: replace() }); | ||
* ``` | ||
@@ -224,3 +167,3 @@ * | ||
* ```js | ||
* import { mergeFull, deep, deepWithKey } from '@jkcfg/std/merge'; | ||
* import { merge, deep, deepWithKey } from '@jkcfg/std/merge'; | ||
* | ||
@@ -249,3 +192,3 @@ * const pod = { | ||
* | ||
* mergeFull(pod, sidecarImage, { | ||
* merge(pod, sidecarImage, { | ||
* spec: { | ||
@@ -282,3 +225,7 @@ * containers: deepWithKey('name'), | ||
export function deepWithKey(mergeKey, rules) { | ||
return (a, b) => arrayMergeWithKey(a, b, mergeKey, rules); | ||
return (a, b) => { | ||
assertArray(a, 'deepWithKey'); | ||
assertArray(b, 'deepWithKey'); | ||
return arrayMergeWithKey(a, b, mergeKey, rules); | ||
}; | ||
} | ||
@@ -292,3 +239,3 @@ /** | ||
* | ||
* `mergeFull` will recursively merge two values `a` and `b`. By default: | ||
* `merge` will recursively merge two values `a` and `b`. By default: | ||
* | ||
@@ -301,3 +248,3 @@ * - if `a` and `b` are primitive types, `b` is the result of the merge. | ||
* | ||
* if `a` and `b` have different types, `mergeFull` will throw an error. | ||
* if `a` and `b` have different types, `merge` will throw an error. | ||
* | ||
@@ -309,3 +256,3 @@ * **Examples**: | ||
* ```js | ||
* mergeFull(1, 2); | ||
* merge(1, 2); | ||
* | ||
@@ -333,3 +280,3 @@ * > 2 | ||
* | ||
* mergeFull(a, b); | ||
* merge(a, b); | ||
* | ||
@@ -351,3 +298,3 @@ * > | ||
* | ||
* For primitive values and arrays, the third argument of `mergeFull` is a | ||
* For primitive values and arrays, the third argument of `merge` is a | ||
* function: | ||
@@ -357,3 +304,3 @@ * | ||
* const add = (a, b) => a + b; | ||
* mergeFull(1, 2, add); | ||
* merge(1, 2, add); | ||
* | ||
@@ -364,3 +311,3 @@ * > 3 | ||
* For objects, each own property can be merged with different strategies. The | ||
* third argument of `mergeFull` is an object associating properties with merge | ||
* third argument of `merge` is an object associating properties with merge | ||
* functions. | ||
@@ -371,3 +318,3 @@ * | ||
* // merge a and b, adding the values of the `k0` property. | ||
* mergeFull(a, b, { k0: add }); | ||
* merge(a, b, { k0: add }); | ||
* | ||
@@ -384,3 +331,3 @@ * > | ||
*/ | ||
export function mergeFull(a, b, rule) { | ||
export function merge(a, b, rule) { | ||
const [typeA, typeB] = [typeof a, typeof b]; | ||
@@ -403,22 +350,1 @@ if (a === undefined) { | ||
} | ||
// Interpret a series of transformations expressed either as object | ||
// patches (as in the argument to `patch` in this module), or | ||
// functions. Usually the first argument will be an object, | ||
// representing an initial value, but it can be a function (that will | ||
// be given an empty object as its argument). | ||
export function mix(...transforms) { | ||
let r = {}; | ||
for (const transform of transforms) { | ||
switch (typeof transform) { | ||
case 'object': | ||
r = patch(r, transform); | ||
break; | ||
case 'function': | ||
r = transform(r); | ||
break; | ||
default: | ||
throw new TypeError('only objects and functions allowed as arguments'); | ||
} | ||
} | ||
return r; | ||
} |
{ | ||
"name": "@jkcfg/std", | ||
"version": "0.2.10", | ||
"version": "0.3.0", | ||
"description": "jk standard library", | ||
@@ -5,0 +5,0 @@ "main": "std.js", |
/** | ||
* @module std/parse | ||
* @module std | ||
*/ | ||
@@ -7,2 +7,2 @@ import { __std } from './internal/__std_generated'; | ||
export declare function parse(input: string, format?: Format): any; | ||
export declare function unparse(obj: any, format?: Format): string; | ||
export declare function stringify(obj: any, format?: Format): string; |
/** | ||
* @module std/parse | ||
* @module std | ||
*/ | ||
@@ -37,3 +37,3 @@ import { flatbuffers } from './internal/flatbuffers'; | ||
} | ||
export function unparse(obj, format) { | ||
export function stringify(obj, format) { | ||
const builder = new flatbuffers.Builder(512); | ||
@@ -40,0 +40,0 @@ const inputOffset = builder.createString(JSON.stringify(obj)); |
190928
5676