UNPKG

@report-toolkit/transformers

Version:

See docs at [https://ibm.github.io/report-toolkit](https://ibm.github.io/report-toolkit)

192 lines (168 loc) 5.13 kB
/** * @module @report-toolkit/transformers */ /** * do not remove this comment (for typedoc) */ import {_, colors, createDebugger, error} from '@report-toolkit/common'; const {RTKERR_INVALID_TRANSFORMER_PIPE, createRTkError} = error; const FIELD_COLORS = Object.freeze(['cyan', 'magenta', 'blue', 'green']); const debug = createDebugger('transformers', 'transformer'); /** * @type {Partial<TransformerMeta>} */ const DEFAULT_TRANSFORMER_META = Object.freeze({ alias: [], input: ['report'] }); const configMap = new WeakMap(); /** * Represents a Transformer having a transform() function and * metadata. * @template T,U */ class Transformer { /** * Sets defaults and instance props * @param {TransformFunction<T,U>} transform * @param {TransformerMeta} meta - Transformer metdata * @param {object} [config] - Transformer config */ constructor(transform, meta, config = {}) { this._meta = _.defaultsDeep(DEFAULT_TRANSFORMER_META, meta); this._transform = transform; if (config.fields) { config.fields = Transformer.normalizeFields(config.fields); } configMap.set(this, config); debug( `created Transform with id "%s" and config %O`, this._meta.id, config ); } get id() { return this._meta.id; } get input() { return this._meta.input; } get output() { return this._meta.output; } get defaults() { return this._meta.defaults; } /** * Pipe one Transformer to another * @param {Transformer<any,any>} transformer * @returns {Transformer<any,any>} */ pipe(transformer) { if (!this.canPipeTo(transformer)) { throw createRTkError( RTKERR_INVALID_TRANSFORMER_PIPE, `Transformer "${this.id}" cannot pipe to transformer "${transformer.id}"` ); } return transformer.pipeFrom(this); } transform() { let defaults = [this.defaults]; if (this._source && configMap.has(this._source)) { const sourceOpts = configMap.get(this._source); defaults = _.defaults({fields: sourceOpts.fields}, defaults); } return this._transform(_.defaultsDeep(defaults, configMap.get(this))); } /** * * @param {Transformer<any,any>} transformer * @returns {Transformer<any,any>} */ pipeFrom(transformer) { this._source = transformer; return this; } /** * Returns `true` if this Transformer can pipe to another * @param {Transformer<any,any>} transformer - Transformer to compare */ canPipeTo(transformer) { return _.includes(this.output, transformer.input); } canBeginWith(type) { return _.includes(type, this.input); } canEndWith(type) { return this.output === type; } /** * Creates a Transformer * @template T,U * @param {TransformFunction<T,U>} transform - Transformer function * @param {TransformerMeta} meta - Transformer meta * @param {object} [config] - Transformer config * @returns {Transformer<T,U>} */ static create(transform, meta, config = {}) { return new Transformer(transform, meta, config); } } Transformer.normalizeFields = _.pipe( _.toPairs, _.map( /** * @param {[number, TransformerField]} value */ ([idx, field]) => { // a field can have a string `color`, no `color`, or a function which accepts a `row` and returns a string. // likewise, it can have a `value` function which accepts a `row` and returns a value, or just a string, which // corresponds to a property of the `row` object. const fieldColor = field.color || FIELD_COLORS[idx % FIELD_COLORS.length]; const colorFn = _.isFunction(fieldColor) ? (row, value) => { // the function might not return a color const result = /** @type {((arg0: any) => string)} */ (fieldColor)( row ); const color = colors[result] ? result : FIELD_COLORS[idx % FIELD_COLORS.length]; return colors[color](value); } : (row, value) => colors[/** @type {string} */ (fieldColor)](value); const valueFn = _.isFunction(field.value) ? row => { // yuck const fn = /** * @type {function(typeof row): string} */ (field.value); return fn(row); } : _.get(field.value); return { ...field, value: row => colorFn(row, valueFn(row)) }; } ) ); export {Transformer}; export const createTransformer = Transformer.create; /** * @typedef {"json"|"csv"|"table"} Formatters * @typedef {{label: string, value: string|function(any): string, color?: string|function(any): string}} TransformerField * @typedef {import('@report-toolkit/common').Report} Report */ /** * @typedef {{id: string, description?: string, input?: string[], output: string, alias?: string[], defaults?: any}} TransformerMeta */ /** * @template T,U * @typedef {import('rxjs/internal/types').OperatorFunction<T,U>} OperatorFunction<T,U> */ /** * @template T,U * @typedef {(opts?: object)=>OperatorFunction<T,U>} TransformFunction */