123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 |
- /* eslint-disable class-methods-use-this */
- const Types = require('./types');
- function combineNameAndType(nameString, typeString) {
- const separator = (nameString && typeString) ? ':' : '';
- return nameString + separator + typeString;
- }
- class Stringifier {
- constructor(options) {
- this._options = options || {};
- this._options.linkClass = this._options.linkClass || this._options.cssClass;
- }
- applications(applications) {
- let result = '';
- const strings = [];
- if (!applications) {
- return result;
- }
- for (let i = 0, l = applications.length; i < l; i++) {
- strings.push(this.type(applications[i]));
- }
- if (this._options.htmlSafe) {
- result = '.<';
- } else {
- result = '.<';
- }
- result += `${strings.join(', ')}>`;
- return result;
- }
- elements(elements) {
- let result = '';
- const strings = [];
- if (!elements) {
- return result;
- }
- for (let i = 0, l = elements.length; i < l; i++) {
- strings.push(this.type(elements[i]));
- }
- result = `(${strings.join('|')})`;
- return result;
- }
- key(type) {
- return this.type(type);
- }
- name(name) {
- return name || '';
- }
- new(funcNew) {
- return funcNew ? `new:${this.type(funcNew)}` : '';
- }
- nullable(nullable) {
- switch (nullable) {
- case true:
- return '?';
- case false:
- return '!';
- default:
- return '';
- }
- }
- optional(optional) {
- if (optional === true) {
- return '=';
- } else {
- return '';
- }
- }
- params(params) {
- let result = '';
- const strings = [];
- if (!params || params.length === 0) {
- return result;
- }
- for (let i = 0, l = params.length; i < l; i++) {
- strings.push(this.type(params[i]));
- }
- result = strings.join(', ');
- return result;
- }
- result(result) {
- return result ? `: ${this.type(result)}` : '';
- }
- stringify(type) {
- return this.type(type);
- }
- this(funcThis) {
- return funcThis ? `this:${this.type(funcThis)}` : '';
- }
- type(type) {
- let typeString = '';
- if (!type) {
- return typeString;
- }
- switch (type.type) {
- case Types.AllLiteral:
- typeString = this._formatNameAndType(type, '*');
- break;
- case Types.FunctionType:
- typeString = this._signature(type);
- break;
- case Types.NullLiteral:
- typeString = this._formatNameAndType(type, 'null');
- break;
- case Types.RecordType:
- typeString = this._record(type);
- break;
- case Types.TypeApplication:
- typeString = this.type(type.expression) + this.applications(type.applications);
- break;
- case Types.UndefinedLiteral:
- typeString = this._formatNameAndType(type, 'undefined');
- break;
- case Types.TypeUnion:
- typeString = this.elements(type.elements);
- break;
- case Types.UnknownLiteral:
- typeString = this._formatNameAndType(type, '?');
- break;
- default:
- typeString = this._formatNameAndType(type);
- }
- // add optional/nullable/repeatable modifiers
- if (!this._options._ignoreModifiers) {
- typeString = this._addModifiers(type, typeString);
- }
- return typeString;
- }
- _record(type) {
- const fields = this._recordFields(type.fields);
- return `{${fields.join(', ')}}`;
- }
- _recordFields(fields) {
- let field;
- let keyAndValue;
- const result = [];
- if (!fields) {
- return result;
- }
- for (let i = 0, l = fields.length; i < l; i++) {
- field = fields[i];
- keyAndValue = this.key(field.key);
- keyAndValue += field.value ? `: ${this.type(field.value)}` : '';
- result.push(keyAndValue);
- }
- return result;
- }
- // Adds optional, nullable, and repeatable modifiers if necessary.
- _addModifiers(type, typeString) {
- let combined;
- let optional = '';
- let repeatable = '';
- if (type.repeatable) {
- repeatable = '...';
- }
- combined = this.nullable(type.nullable) + combineNameAndType('', typeString);
- optional = this.optional(type.optional);
- return repeatable + combined + optional;
- }
- _addLinks(nameString) {
- const href = this._getHrefForString(nameString);
- let link = nameString;
- let linkClass = this._options.linkClass || '';
- if (href) {
- if (linkClass) {
- linkClass = ` class="${linkClass}"`;
- }
- link = `<a href="${href}"${linkClass}>${nameString}</a>`;
- }
- return link;
- }
- _formatNameAndType(type, literal) {
- let nameString = type.name || literal || '';
- const typeString = type.type ? this.type(type.type) : '';
- nameString = this._addLinks(nameString);
- return combineNameAndType(nameString, typeString);
- }
- _getHrefForString(nameString) {
- let href = '';
- const links = this._options.links;
- if (!links) {
- return href;
- }
- // accept a map or an object
- if (links instanceof Map) {
- href = links.get(nameString);
- } else if ({}.hasOwnProperty.call(links, nameString)) {
- href = links[nameString];
- }
- return href;
- }
- _signature(type) {
- let param;
- let prop;
- let signature;
- const params = [];
- // these go within the signature's parens, in this order
- const props = [
- 'new',
- 'this',
- 'params'
- ];
- for (let i = 0, l = props.length; i < l; i++) {
- prop = props[i];
- param = this[prop](type[prop]);
- if (param.length > 0) {
- params.push(param);
- }
- }
- signature = `function(${params.join(', ')})`;
- signature += this.result(type.result);
- return signature;
- }
- }
- module.exports = (type, options) => new Stringifier(options).stringify(type);
|