blob: 18f718b4a1c94ecc510ee5aec96d485265624bd2 [file] [log] [blame]
'use strict';
const {OPTIONAL} = require('./NodeType');
const {OptionalTypeSyntax, VariadicTypeSyntax} = require('./SyntaxType');
const {format} = require('util');
/** @typedef {import('./parsing').AstNode} AstNode */
/** @typedef {(node) => string} ConcretePublish */
/** @typedef {{ [T in import('./NodeType').Type]: (node: AstNode, publish: ConcretePublish) => string }} Publisher */
/**
* @param {AstNode} node
* @param {Publisher} [opt_publisher]
* @return {string}
*/
function publish(node, opt_publisher) {
const publisher = opt_publisher || createDefaultPublisher();
return publisher[node.type](node, function(childNode) {
return publish(childNode, publisher);
});
}
/**
* @private
* @param {string} str
* @param {'none'|'single'|'double'|undefined} quoteStyle
* @returns {string} Formatted string
*/
function addQuotesForName (str, quoteStyle) {
// For `MemberName`, not strings
if (!quoteStyle || quoteStyle === 'none') {
return str;
}
const singleQuoteStyle = quoteStyle === 'single';
return format(
singleQuoteStyle
? "'%s'"
: '"%s"',
str
.replace(/\\/g, '\\\\')
.replace(
singleQuoteStyle ? /'/gu : /"/gu,
'\\' + (singleQuoteStyle ? "'" : '"')
)
);
}
/** @return {Publisher} */
function createDefaultPublisher() {
return {
NAME (nameNode) {
return nameNode.name;
},
MEMBER (memberNode, concretePublish) {
return format('%s.%s%s', concretePublish(memberNode.owner),
memberNode.hasEventPrefix ? 'event:' : '',
addQuotesForName(memberNode.name, memberNode.quoteStyle));
},
UNION (unionNode, concretePublish) {
return format('%s|%s', concretePublish(unionNode.left),
concretePublish(unionNode.right));
},
VARIADIC (variadicNode, concretePublish) {
if (variadicNode.meta.syntax === VariadicTypeSyntax.ONLY_DOTS) {
return '...';
}
return format('...%s', concretePublish(variadicNode.value));
},
RECORD (recordNode, concretePublish) {
const concretePublishedEntries = recordNode.entries.map(concretePublish);
return format('{%s}', concretePublishedEntries.join(', '));
},
RECORD_ENTRY (entryNode, concretePublish) {
if (!entryNode.value) return addQuotesForName(entryNode.key, entryNode.quoteStyle);
const keySuffix = (
entryNode.value.type === OPTIONAL &&
entryNode.value.meta.syntax === OptionalTypeSyntax.SUFFIX_KEY_QUESTION_MARK
)
? '?'
: '';
return format('%s%s: %s', addQuotesForName(entryNode.key, entryNode.quoteStyle), keySuffix, concretePublish(entryNode.value));
},
TUPLE (tupleNode, concretePublish) {
const concretePublishedEntries = tupleNode.entries.map(concretePublish);
return format('[%s]', concretePublishedEntries.join(', '));
},
GENERIC (genericNode, concretePublish) {
const concretePublishedObjects = genericNode.objects.map(concretePublish);
if (genericNode.meta) {
switch (genericNode.meta.syntax) {
case 'SQUARE_BRACKET':
if (genericNode.subject && genericNode.subject.name === 'Array') {
return format('%s[]', concretePublishedObjects.join(', '));
}
break;
case 'ANGLE_BRACKET_WITH_DOT':
return format('%s.<%s>', concretePublish(genericNode.subject),
concretePublishedObjects.join(', '));
}
}
return format('%s<%s>', concretePublish(genericNode.subject),
concretePublishedObjects.join(', '));
},
MODULE (moduleNode, concretePublish) {
return format('module:%s', concretePublish(moduleNode.value));
},
FILE_PATH (filePathNode) {
return addQuotesForName(filePathNode.path, filePathNode.quoteStyle);
},
OPTIONAL (optionalNode, concretePublish) {
if (optionalNode.meta.syntax === OptionalTypeSyntax.SUFFIX_KEY_QUESTION_MARK) {
return concretePublish(optionalNode.value);
}
return format('%s=', concretePublish(optionalNode.value));
},
NULLABLE (nullableNode, concretePublish) {
return format('?%s', concretePublish(nullableNode.value));
},
NOT_NULLABLE (notNullableNode, concretePublish) {
return format('!%s', concretePublish(notNullableNode.value));
},
FUNCTION (functionNode, concretePublish) {
const publidshedParams = functionNode.params.map(concretePublish);
if (functionNode.new) {
publidshedParams.unshift(format('new: %s',
concretePublish(functionNode.new)));
}
if (functionNode.this) {
publidshedParams.unshift(format('this: %s',
concretePublish(functionNode.this)));
}
if (functionNode.returns) {
return format('function(%s): %s', publidshedParams.join(', '),
concretePublish(functionNode.returns));
}
return format('function(%s)', publidshedParams.join(', '));
},
ARROW (functionNode, concretePublish) {
const publishedParams = functionNode.params.map(concretePublish);
return (functionNode.new ? 'new ' : '') + format('(%s) => %s', publishedParams.join(', '), concretePublish(functionNode.returns));
},
NAMED_PARAMETER (parameterNode, concretePublish) {
return parameterNode.name + (parameterNode.typeName ? ': ' + concretePublish(parameterNode.typeName) : '');
},
ANY () {
return '*';
},
UNKNOWN () {
return '?';
},
INNER_MEMBER (memberNode, concretePublish) {
return concretePublish(memberNode.owner) + '~' +
(memberNode.hasEventPrefix ? 'event:' : '') +
addQuotesForName(memberNode.name, memberNode.quoteStyle);
},
INSTANCE_MEMBER (memberNode, concretePublish) {
return concretePublish(memberNode.owner) + '#' +
(memberNode.hasEventPrefix ? 'event:' : '') +
addQuotesForName(memberNode.name, memberNode.quoteStyle);
},
STRING_VALUE (stringValueNode) {
return addQuotesForName(stringValueNode.string, stringValueNode.quoteStyle)
},
NUMBER_VALUE (numberValueNode) {
return numberValueNode.number;
},
EXTERNAL (externalNode /* , concretePublish */) {
const {name, quoteStyle} = externalNode;
return format('external:%s', addQuotesForName(name, quoteStyle));
},
PARENTHESIS (parenthesizedNode, concretePublish) {
return format('(%s)', concretePublish(parenthesizedNode.value));
},
TYPE_QUERY (typeQueryNode, concretePublish) {
return format('typeof %s', concretePublish(typeQueryNode.name));
},
KEY_QUERY (keyQueryNode, concretePublish) {
return format('keyof %s', concretePublish(keyQueryNode.value));
},
IMPORT (importNode, concretePublish) {
return format('import(%s)', concretePublish(importNode.path));
},
};
}
module.exports = {
publish: publish,
createDefaultPublisher: createDefaultPublisher,
};