import * as ts from "typescript" ; import { existsSync , readFileSync , statSync } from "fs" ; import * as glob from "glob" ; import { AbstractAstParser } from "./abstract-ast-parser" ; import { xliff2Digest , xliff2LoadToXml , xliff2Write } from "../../src/serializers/xliff2" ; import { xliffDigest , xliffLoadToXml , xliffWrite } from "../../src/serializers/xliff" ; import { xtbDigest , xtbMapper } from "../../src/serializers/xtb" ; import { Message } from "../../src/ast/i18n_ast" ; import { xmbLoadToXml , xmbWrite } from "../../src/serializers/xmb" ; import { Node } from "../../src/serializers/xml_helper" ; import { MessageBundle } from "./message-bundle" ; import { XmlMessagesById } from "../../src/serializers/serializer" ; import { I18nDef } from "../../src/i18n-polyfill" ; export function getAst ( paths ) { const files = [ ] ; paths . forEach ( path => { files . push ( ... glob . sync ( path ) ) ; } ) ; const parser = new ServiceParser ( ) ; const collection = { } ; files . forEach ( path => { if ( statSync ( path ) . isDirectory ) { const contents = readFileSync ( path , "utf-8" ) ; const entries = parser . extract ( contents , path ) ; if ( entries . length ) { collection [ path ] = entries ; } } } ) ; return collection ; } export class ServiceParser extends AbstractAstParser { protected _sourceFile : ts . SourceFile ; public extract ( contents , path ? ) { const entries = [ ] ; this . _sourceFile = this . _createSourceFile ( path , contents ) ; const classNodes = this . _findClassNodes ( this . _sourceFile ) ; classNodes . forEach ( classNode => { const constructorNode = this . _findConstructorNode ( classNode ) ; if ( ! constructorNode ) { return ; } const propertyName = this . _findTranslateServicePropertyName ( constructorNode ) ; if ( ! propertyName ) { return ; } const callNodes = this . _findCallNodes ( classNode , propertyName ) ; callNodes . forEach ( callNode => { entries . push ( ... this . _getCallArgStrings ( callNode ) ) ; } ) ; } ) ; const functionNodes = this . _findFunctionNodes ( this . _sourceFile ) ; functionNodes . forEach ( functionNode => { const propertyName = this . _findTranslateServicePropertyName ( functionNode ) ; if ( ! propertyName ) { return ; } const callNodes = this . _findCallNodes ( functionNode , propertyName ) ; callNodes . forEach ( callNode => { entries . push ( ... this . _getCallArgStrings ( callNode ) ) ; } ) ; } ) ; return entries ; } /**
     * Detect what the TranslateService instance property
     * is called by inspecting constructor arguments
     */ protected _findTranslateServicePropertyName ( constructorNode ) { if ( ! constructorNode ) { return null ; } const result = constructorNode . parameters . find ( parameter => { if ( ! parameter . type ) { return false ; } const parameterType = ( parameter . type as ts . TypeReferenceNode ) . typeName as ts . Identifier ; if ( ! parameterType ) { return false ; } const className = parameterType . text ; return className === "I18n" ; } ) ; if ( result ) { return ( result . name as ts . Identifier ) . text ; } } /**
     * Find class nodes
     */ protected _findClassNodes ( node ) { return this . _findNodes ( node , ts . SyntaxKind . ClassDeclaration ) as ts . ClassDeclaration [ ] ; } /**
     * Find function nodes
     */ protected _findFunctionNodes ( node ) { return this . _findNodes ( node , ts . SyntaxKind . FunctionDeclaration ) as ts . FunctionDeclaration [ ] ; } /**
     * Find constructor
     */ protected _findConstructorNode ( node ) { const constructorNodes = this . _findNodes ( node , ts . SyntaxKind . Constructor ) as ts . ConstructorDeclaration [ ] ; if ( constructorNodes ) { return constructorNodes [ 0 ] ; } } /**
     * Find all calls to TranslateService methods
     */ protected _findCallNodes ( node , propertyIdentifier ) { let callNodes = this . _findNodes ( node , ts . SyntaxKind . CallExpression ) as ts . CallExpression [ ] ; callNodes = callNodes . filter ( callNode => { if ( callNode . arguments . length < 1 ) { return false ; } const expression = callNode . expression as ts . PropertyAccessExpression & ts . Identifier ; return expression . text === propertyIdentifier || ( expression . name && expression . name . text === propertyIdentifier ) ; } ) ; return callNodes ; } } export function getFileContent ( messages , sourcePath , format ? , locale = "en" ) { let loadFct ; let writeFct ; let digest ; let createMapper = ( message ) => null ; format = ( format || "xlf" ) . toLowerCase ( ) ; switch ( format ) { case "xmb" : loadFct = xmbLoadToXml ; writeFct = xmbWrite ; digest = xtbDigest ; createMapper = xtbMapper ; break ; case "xliff2" : case "xlf2" : loadFct = xliff2LoadToXml ; writeFct = xliff2Write ; digest = xliff2Digest ; break ; case "xliff" : case "xlf" : default : loadFct = xliffLoadToXml ; writeFct = xliffWrite ; digest = xliffDigest ; break ; } let xmlMessagesById = { } ; if ( existsSync ( sourcePath ) ) { xmlMessagesById = loadFct ( readFileSync ( sourcePath , { encoding : "utf8" } ) , sourcePath ) ; } const messageBundle = new MessageBundle ( locale ) ; Object . keys ( messages ) . forEach ( url => { messages [ url ] . forEach ( entry => messageBundle . updateFromTemplate ( entry , url ) ) ; } ) ; return messageBundle . write ( writeFct , digest , xmlMessagesById , createMapper ) ; }