import * as ts from 'typescript' ; import { evaluateExpression , evaluateDecoratorNode , evaluateOfType } from './evaluator' ; interface DecoratorEvaluation { expression : string ; arguments : { name : string ; value : any ; } [ ] ; } export interface Mapping { name : string ; inputTypes : string [ ] ; returnType : string [ ] ; fileInfo : string ; } export function generateEffectsMapping ( fileNames ) { const program = ts . createProgram ( fileNames , { target : ts . ScriptTarget . ES5 , module : ts . ModuleKind . CommonJS } ) ; const checker = program . getTypeChecker ( ) ; const output = [ ] ; for ( const sourceFile of program . getSourceFiles ( ) ) { if ( ! sourceFile . isDeclarationFile ) { ts . forEachChild ( sourceFile , node => visit ( node , sourceFile ) ) ; } } return output ; function visit ( node , sourceFile ) { const nodeDecoratorEvaluation = evaluateDecoratorNode ( node ) ; if ( ts . isPropertyDeclaration ( node ) && isDecoratedWithEffect ( nodeDecoratorEvaluation ) ) { const symbol = checker . getSymbolAtLocation ( node . name ) ; const name = symbol ! . getName ( ) ; const { fileInfo } = report ( node , sourceFile ) ; const returnType = getSymbolType ( nodeDecoratorEvaluation , node ) ; if ( returnType . indexOf ( undefined ) > - 1 ) { console . log ( node . getText ( ) ) ; } const inputTypes = getOfType ( node ) ; output . push ( { name , returnType , inputTypes , fileInfo } ) ; } ts . forEachChild ( node , node => visit ( node , sourceFile ) ) ; } function report ( node , sourceFile ) { let { line , character } = sourceFile . getLineAndCharacterOfPosition ( node . getStart ( ) ) ; return { fileInfo : `${ sourceFile . fileName }:${ line + 1 }:${ character + 1 }` } ; } function getSymbolType ( nodeDecoratorEvaluation , node ) { if ( isEffectDecoratorWithoutDispatch ( nodeDecoratorEvaluation ) || ! node . initializer ) { return [ 'void' ] ; } return evaluateExpression ( node . initializer , checker ) ; } function getOfType ( node ) { if ( ! node . initializer ) { return '' ; } return evaluateOfType ( node . initializer , checker ) ; } function isDecoratedWithEffect ( nodeDecoratorEvaluation ) { return ( nodeDecoratorEvaluation && nodeDecoratorEvaluation . some ( e => e . expression === 'Effect' ) ) ; } function isEffectDecoratorWithoutDispatch ( nodeDecoratorEvaluation ) { return nodeDecoratorEvaluation . some ( e => e . arguments . some ( e => e . name === 'dispatch' && e . value === false ) ) ; } }