UNPKG

@ipfn/ts-neuron-gen

Version:

[![IPFN project](https://img.shields.io/badge/project-IPFN-blue.svg?style=flat-square)](http://github.com/ipfn) [![npm](https://img.shields.io/npm/v/@ipfn/ts-neuron-gen.svg?maxAge=86400&style=flat-square)](https://www.npmjs.com/package/@ipfn/ts-neuron-gen

323 lines (322 loc) 11.3 kB
"use strict"; /** * @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; }