UNPKG

@salesforce/core

Version:

Core libraries to interact with SFDX projects, orgs, and APIs.

260 lines 9.27 kB
"use strict"; /* * Copyright (c) 2020, salesforce.com, inc. * All rights reserved. * Licensed under the BSD 3-Clause license. * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SchemaPrinter = exports.SchemaPropertyRenderer = void 0; const ts_types_1 = require("@salesforce/ts-types"); const sfdxError_1 = require("../sfdxError"); /** * Renders schema properties. By default, this is simply an identity transform. Subclasses may provide more * interesting decorations of each values, such as ANSI coloring. */ class SchemaPropertyRenderer { /** * Renders a name. * * @param name The name value to render. */ renderName(name) { return name; } /** * Renders a title. * * @param title The title value to render. */ renderTitle(title) { return title; } /** * Renders a description. * * @param description The description value to render. */ renderDescription(description) { return description; } /** * Renders a type. * * @param propertyType The type value to render. */ renderType(propertyType) { return propertyType; } } exports.SchemaPropertyRenderer = SchemaPropertyRenderer; /** * Prints a JSON schema in a human-friendly format. * * ``` * import chalk from 'chalk'; * class MyPropertyRenderer extends SchemaPropertyRenderer { * renderName(name) { return chalk.bold.blue(name); } * } * * const printer = new SchemaPrinter(logger, schema, new MyPropertyRenderer()); * printer.getLines().forEach(console.log); * ``` */ class SchemaPrinter { /** * Constructs a new `SchemaPrinter`. * * @param logger The logger to use when emitting the printed schema. * @param schema The schema to print. * @param propertyRenderer The property renderer. */ constructor(logger, schema, propertyRenderer = new SchemaPropertyRenderer()) { this.schema = schema; this.propertyRenderer = propertyRenderer; this.lines = []; this.logger = logger.child('SchemaPrinter'); if (!this.schema.properties && !this.schema.items) { // No need to add to messages, since this should never happen. In fact, // this will cause a test failure if there is a command that uses a schema // with no properties defined. throw new sfdxError_1.SfdxError('There is no purpose to print a schema with no properties or items'); } const startLevel = 0; const add = this.addFn(startLevel); // For object schemas, print out the "header" and first level properties differently if (this.schema.properties) { if (typeof this.schema.description === 'string') { // Output the overall schema description before printing the properties add(this.schema.description); add(''); } Object.keys(this.schema.properties).forEach((key) => { const properties = ts_types_1.asJsonMap(this.schema.properties); if (!properties) { return; } this.parseProperty(key, ts_types_1.asJsonMap(properties[key]), startLevel); add(''); }); } else { this.parseProperty('schema', this.schema, startLevel); } } /** * Gets a read-only array of ready-to-display lines. */ getLines() { return this.lines; } /** * Gets a ready-to-display line by index. * * @param index The line index to get. */ getLine(index) { return this.lines[index]; } /** * Prints the accumulated set of schema lines as info log lines to the logger. */ print() { this.lines.forEach((line) => this.logger.info(line)); } addFn(level) { const indent = ' '.repeat(level * 4); return (line) => { this.lines.push(`${indent}${line}`); }; } parseProperty(name, rawProperty, level = 0) { if (!rawProperty) { return; } const add = this.addFn(level); const property = new SchemaProperty(this.logger, this.schema, name, rawProperty, this.propertyRenderer); add(property.renderHeader()); if (property.type === 'object' && property.properties) { Object.keys(property.properties).forEach((key) => { this.parseProperty(key, property.getProperty(key), level + 1); }); } if (property.type === 'array') { add(` ${property.renderArrayHeader()}`); if (property.items && property.items.type === 'object' && property.items.properties) { Object.keys(property.items.properties).forEach((key) => { const items = ts_types_1.asJsonMap(property.items); if (!items) { return; } const properties = ts_types_1.asJsonMap(items.properties); if (!properties) { return; } this.parseProperty(key, ts_types_1.asJsonMap(properties[key]), level + 2); }); } } if (property.required) { add(`Required: ${property.required.join(', ')}`); } } } exports.SchemaPrinter = SchemaPrinter; class SchemaProperty { constructor(logger, schema, name, rawProperty, propertyRenderer) { this.logger = logger; this.schema = schema; this.name = name; this.rawProperty = rawProperty; this.propertyRenderer = propertyRenderer; this.name = name; // Capture the referenced definition, if specified if (typeof this.rawProperty.$ref === 'string') { // Copy the referenced property while adding the original property's properties on top of that -- // if they are defined here, they take precedence over referenced definition properties. this.rawProperty = Object.assign({}, resolveRef(this.schema, this.rawProperty), rawProperty); } const oneOfs = ts_types_1.asJsonArray(this.rawProperty.oneOf); if (oneOfs && !this.rawProperty.type) { this.rawProperty.type = oneOfs .map((value) => { return ts_types_1.isJsonMap(value) ? value.type || value.$ref : value; }) .join('|'); } // Handle items references if (ts_types_1.isJsonMap(this.items) && this.items && this.items.$ref) { Object.assign(this.items, resolveRef(this.schema, this.items)); } } renderName() { return this.propertyRenderer.renderName(this.name); } renderTitle() { return this.propertyRenderer.renderTitle(this.title || ''); } renderDescription() { return this.propertyRenderer.renderDescription(this.description || ''); } renderType() { return this.propertyRenderer.renderType(this.type || ''); } renderHeader() { return `${this.renderName()}(${this.renderType()}) - ${this.renderTitle()}: ${this.renderDescription()}`; } renderArrayHeader() { if (!this.items) { return ''; } const minItems = this.minItems ? ` - min ${this.minItems}` : ''; const prop = new SchemaProperty(this.logger, this.schema, 'items', this.items, this.propertyRenderer); return `items(${prop.renderType()}${minItems}) - ${prop.renderTitle()}: ${prop.renderDescription()}`; } get title() { return ts_types_1.asString(this.rawProperty.title); } get description() { return ts_types_1.asString(this.rawProperty.description); } get type() { return ts_types_1.asString(this.rawProperty.type); } get required() { return ts_types_1.asJsonArray(this.rawProperty.required); } get properties() { return ts_types_1.asJsonMap(this.rawProperty.properties); } get items() { return ts_types_1.asJsonMap(this.rawProperty.items); } get minItems() { return ts_types_1.asNumber(this.rawProperty.minItems); } getProperty(key) { const properties = this.getProperties(); return ts_types_1.asJsonMap(properties && properties[key]); } getProperties() { return ts_types_1.asJsonMap(this.rawProperty.properties); } } /** * Get the referenced definition by following the reference path on the current schema. * * @param schema The source schema containing the property containing a `$ref` field. * @param property The property that contains the `$ref` field. */ function resolveRef(schema, property) { const ref = property.$ref; if (!ref || typeof ref !== 'string') { return null; } return ref.split('/').reduce((prev, key) => { const next = prev[key]; return key === '#' ? schema : ts_types_1.isJsonMap(next) ? next : {}; }, property); } //# sourceMappingURL=printer.js.map