262 lines
6.2 KiB
JavaScript
262 lines
6.2 KiB
JavaScript
|
/* 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) {
|
||
|
let openTag;
|
||
|
|
||
|
let linkClass = '';
|
||
|
const options = this._options;
|
||
|
|
||
|
if (options.links && Object.prototype.hasOwnProperty.call(options.links, nameString)) {
|
||
|
if (options.linkClass) {
|
||
|
linkClass = ` class="${options.linkClass}"`;
|
||
|
}
|
||
|
|
||
|
openTag = `<a href="${options.links[nameString]}"${linkClass}>`;
|
||
|
nameString = `${openTag + nameString}</a>`;
|
||
|
}
|
||
|
|
||
|
return nameString;
|
||
|
}
|
||
|
|
||
|
_formatNameAndType(type, literal) {
|
||
|
let nameString = type.name || literal || '';
|
||
|
const typeString = type.type ? this.type(type.type) : '';
|
||
|
|
||
|
nameString = this._addLinks(nameString);
|
||
|
|
||
|
return combineNameAndType(nameString, typeString);
|
||
|
}
|
||
|
|
||
|
_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);
|