@ipfn/ts-neuron-gen
Version:
[](http://github.com/ipfn) [](https://www.npmjs.com/package/@ipfn/ts-neuron-gen
323 lines (322 loc) • 11.3 kB
JavaScript
;
/**
* @license
* Copyright (c) 2017 Łukasz Kurowski <crackcomm@gmail.com>
*/
Object.defineProperty(exports, "__esModule", { value: true });
const flatten = require("lodash.flatten");
const synaptic_types_1 = require("@ipfn/synaptic-types");
const neurons_1 = require("@ipfn/neurons");
/** Converts typedocs JSON output format Object to neurons. */
function convertTypeDocs(root, mods = [], registry = {}) {
const modules = expandModules(root, mods);
const modulePath = modules.join('.');
// register all interfaces
registerInterfaces(root, modules, registry);
// map children to neurons
const results = root.children
.filter(isExported)
.filter(node => node.kindString !== 'Interface')
.map(node => {
// Serialize class
if (node.kindString === 'Class') {
return serializeClass(node, modules, registry);
}
// serialize functions, methods and class constructors
if (node.kindString === 'Function') {
return serializeFunction(node, modules, registry);
}
// recursively convert noderen
if (node.children) {
return convertTypeDocs(node, modules, registry);
}
})
.filter(node => !!node);
return flatten(results);
}
exports.default = convertTypeDocs;
/** Registers typedocs interfaces in registry. */
function registerInterfaces(root, mods = [], registry = {}) {
const modules = expandModules(root, mods);
const modulePath = modules.join('.');
root.children
.filter(isExported)
.forEach(node => {
// serialize interface type
if (node.kindString === 'Interface') {
const terminal = serializeInterface(node, registry);
// register interface type
registry[node.id] = terminal;
}
else if (node.children) {
registerInterfaces(node, modules, registry);
}
});
}
function serializeClassProperty(node, registry) {
const { name } = node;
const terminals = serializeParameters(node, registry);
return {
name,
input: terminals,
output: terminals,
};
}
/** Serializes class to a deep neuron */
function serializeClass(node, mods, registry) {
// create class name witg namespace
const namespace = mods.join('.');
const name = mods.concat([node.name]).join('.');
// serialize class methods
const methods = flatten(node.children
.filter(node => node.kindString === 'Method')
.map(node => serializeMethod(node, mods, registry)));
// serialize class properties
const properties = flatten(node.children
.filter(node => node.kindString === 'Property')
.filter(node => node.flags.isPublic)
.map(node => serializeClassProperty(node, registry)));
// create class contstructor neuron
const constr = node.children.find(node => node.kindString === 'Constructor');
// create class neurons list properties + methods
const neurons = properties.concat(methods);
// constructor neuron definition
const constructors = flatten(serializeMethod(constr, mods, registry)).map(neuron => {
// remove constructor output
delete neuron.output;
// set constructor name to class name
neuron.name = node.name;
// class neurons
neuron.nodes = neurons;
return neuron;
});
// class nucleus descriptor
const nucleus = serializeSources(node.sources, namespace).map(src => (Object.assign({ name: node.name }, src)))[0];
// return class definition
return constructors
.map((neuron, index) => ({
name: `${neuron.name}${index === 0 ? '' : `:${index}`}`,
nucleus,
interface: neuron,
}));
}
/** Serialize method. */
function serializeMethod(node, mods, registry) {
const namespace = mods.join('.');
const name = mods.concat([node.name]).join('.');
return node.signatures.map(sig => (Object.assign({ name }, serializeSignature(sig, registry))));
}
/** Serialize parameter. */
function serializeParameter(node, registry) {
if (node.type.type === 'reflection') {
return serializeReflection(node, registry);
}
if (node.type.type === 'reference') {
return serializeReference(node, registry);
}
const param = {};
if (node.name) {
param.name = node.name;
}
if (node.type) {
param.type = serializeType(node.type);
}
if (node.flags && node.flags.isOptional) {
return param;
}
return Object.assign({}, param, { always: true });
}
/** Serialize parameters. */
function serializeParameters(node, registry) {
if (node.type.type === 'union') {
// serialize union types
const nodes = node.type.types.map(type => (Object.assign({}, serializeInlineType(type, registry), { name: node.name })));
if (node.flags && node.flags.isOptional) {
return nodes;
}
return nodes.map(node => (Object.assign({}, node, { always: true })));
}
// return array with serialized parameter
return [serializeParameter(node, registry)];
}
/** Serialize function signature output type. */
function serializeSignatureOutput(node, registry) {
if (node.type.type === 'reflection') {
const ref = serializeReflection(node, registry);
return ref.nodes;
}
if (node.type.type === 'reference') {
const ref = serializeReference(node, registry);
return ref.nodes;
}
return serializeParameters(node, registry).map(terminal => {
const result = Object.assign({}, terminal);
// remove function name
delete result.name;
// return cleaned result
return result;
});
}
/** Serialize function signature. */
function serializeSignature(signature, registry) {
// build neuron
let builder = neurons_1.neuron(signature.name);
// function input
if (signature.parameters) {
const input = flatten(signature.parameters.map(param => serializeParameters(param, registry)));
// special case: embed input if it is a single structure
if (input.length !== 0) {
builder = builder.input(input);
}
}
// function output
if (signature.type) {
builder = builder.output(serializeSignatureOutput(signature, registry));
}
return builder.build();
}
/** Serialize function. */
function serializeFunction(node, mods, registry) {
const namespace = mods.join('.');
const name = mods.concat([node.name]).join('.');
const nucleus = serializeSources(node.sources, namespace).map(source => (Object.assign({}, source, { name: node.name })))[0];
return node.signatures
.map((sig) => serializeSignature(sig, registry))
.map((neuron, index) => ({
name: `${name}${index === 0 ? '' : `:${index}`}`,
nucleus,
interface: neuron
}));
}
/** Serializes node sources */
function serializeSources(sources, namespace) {
return sources.map(({ fileName: file, line, character }) => {
const result = { file, line, character };
if (namespace) {
result.module = namespace;
}
return result;
});
}
/** Serialize interface properties. */
function serializeProperties(node, registry) {
// serialize interface property
if (node.kindString === 'Interface') {
return [serializeInterface(node, registry)];
}
// serialize parameters
return serializeParameters(node, registry);
}
/** Serialize interface type. */
function serializeInterface(node, registry) {
let builder = neurons_1.synaptic(node.name);
if (node.indexSignature) {
builder = buildIndexSignatures(builder, node.indexSignature, registry);
}
if (node.children) {
node.children.forEach(child => {
serializeProperties(child, registry).map(prop => {
builder = builder.node(prop);
});
});
}
return builder.build();
}
/** Serialize reference parameter. */
function serializeReference(node, registry) {
if (node.type.name === 'Stream' && node.type.typeArguments && node.type.typeArguments.length > 0) {
const type = node.type.typeArguments[0];
return Object.assign({ name: node.name }, serializeInlineType(type, registry), { many: true });
}
if (node.type.name === 'Buffer') {
return {
name: node.name,
type: synaptic_types_1.default.Bytes,
};
}
if (node.type.name === 'Promise') {
const type = node.type.typeArguments[0];
return serializeReference({ type }, registry);
}
const result = Object.assign({}, registry[node.type.id]);
if (node.name) {
result.name = node.name;
}
return result;
}
/** Serialize type reflection. */
function serializeReflection(node, registry) {
let builder = neurons_1.synaptic(node.name);
const { declaration } = node.type;
const { indexSignature } = declaration;
if (indexSignature) {
builder = buildIndexSignatures(builder, indexSignature, registry);
}
if (declaration.children) {
declaration.children.forEach(child => {
serializeProperties(child, registry)
.forEach(prop => {
builder = builder.node(prop);
});
});
}
return builder.build();
}
/** Build type index signatures. */
function buildIndexSignatures(builder, indexSignature, registry) {
const valueTypes = flatten(indexSignature.map(sig => {
const key = sig.parameters.map(param => param.type)[0];
if (sig.type.type === 'union') {
return sig.type.types.map(type => ({
key,
value: type,
}));
}
return { key, value: sig.type };
}));
valueTypes.forEach(({ key, value }) => {
builder = builder.node(neurons_1.object(serializeInlineType(key, registry), serializeInlineType(value, registry)));
});
return builder;
}
/** Serializes inlined type to a neuron. */
function serializeInlineType(type, registry) {
switch (type.type) {
case 'intrinsic':
return neurons_1.synaptic().type(serializeType(type)).build();
case 'reflection':
return serializeReflection({ type }, registry);
case 'reference':
const ref = serializeReference({ type }, registry);
// inline references do not have names
delete ref.name;
return ref;
case 'array':
const arr = serializeInlineType(type.elementType, registry);
arr.many = true;
return arr;
default:
throw new Error(`Unknown inline type: ${type.type}`);
}
}
/** Serializes type to string. */
function serializeType(type) {
switch (type.type) {
case 'intrinsic':
return type.name;
default:
throw new Error(`Unknown type of a Type: ${type.type}`);
}
}
/** Expands modules array. */
function expandModules(node, mods = []) {
if (node.kindString === 'Module') {
mods = [].concat(mods);
mods.push(node.name);
}
return mods;
}
/** Returns true if node is exported. */
function isExported(node) {
return node.flags && node.flags.isExported;
}