import * as wrap from "wordwrap" ; import { LocalizedText , NodeClass , QualifiedName } from "node-opcua-data-model" ; import { NodeId } from "node-opcua-nodeid" ; import { IBasicSession } from "node-opcua-pseudo-session" ; import { EnumDefinition , ReferenceDescription , StructureDefinition , _enumerationDataChangeTrigger } from "node-opcua-types" ; import { LineFile , lowerFirstLetter } from "node-opcua-utils" ; import { DataType } from "node-opcua-variant" ; import assert from "node-opcua-assert" ; import { ModellingRuleType } from "node-opcua-address-space-base" ; import * as chalk from "chalk" ; import { convertNodeIdToDataTypeAsync , getBrowseName , getIsAbstract , getDefinition , getDescription , getModellingRule , getNodeClass , getSubtypeNodeId , getSubtypeNodeIdIfAny , getTypeDefOrBaseType , getChildrenOrFolderElements , getDataTypeNodeId , extractBasicDataType , getValueRank } from "./private/utils" ; import { Cache , constructCache , Import , makeTypeNameNew , referenceExtensionObject , RequestedSubSymbol } from "./private/cache" ; import { Options } from "./options" ; import { toFilename } from "./private/to_filename" ; const doDebug = false ; const wrapText = wrap ( 0 , 50 ) ; const f2 = ( str ) => str . padEnd ( 50 , "-" ) ; const f1 = ( str ) => str . padEnd ( 50 , " " ) ; const baseExtension = "_Base" ; export async function convertDataTypeToTypescript ( session , dataTypeId ) { const definition = await getDefinition ( session , dataTypeId ) ; const browseName = await getBrowseName ( session , dataTypeId ) ; const dataTypeTypescriptName = `UA${ browseName . name ! . toString ( ) }` ; const f = new LineFile ( ) ; if ( definition && definition instanceof StructureDefinition ) { f . write ( `interface ${ dataTypeTypescriptName } {` ) ; for ( const field of definition . fields || [ ] ) { } f . write ( `}` ) ; } } function toJavascritPropertyName ( childName ) { childName = lowerFirstLetter ( childName ) ; if ( childName === "namespaceUri" ) { childName = "$namespaceUri" ; } if ( childName === "rolePermissions" ) { childName = "$rolePermissions" ; } if ( childName === "displayName" ) { childName = "$displayName" ; } if ( childName === "eventNotifier" ) { childName = "$eventNotifier" ; } return childName . replace ( /</g , "$" ) . replace ( />/g , "$" ) . replace ( / |\./g , "_" ) . replace ( /#/g , "_" ) ; } function quotifyIfNecessary ( s ) { if ( s . match ( /(^[^a-zA-Z])|([^a-zA-Z_0-9])/ ) ) { return `"${ s }"` ; } if ( s === "nodeClass" ) { return `["$nodeClass"]` ; } return s ; } async function getCorrepondingJavascriptType2 ( session , nodeId , dataTypeNodeId , cache , importCollect ? ) { const q = await getCorrepondingJavascriptType ( session , dataTypeNodeId , cache , importCollect ) ; const valueRank = await getValueRank ( session , nodeId ) ; return { dataType : q . dataType , jtype : q . jtype + ( valueRank >= 1 ? "[]" : "" ) } ; } async function getCorrepondingJavascriptType ( session , dataTypeNodeId , cache , importCollect ? ) { const dataType = await convertNodeIdToDataTypeAsync ( session , dataTypeNodeId ) ; const referenceBasicType = ( name ) : string => { const t = { name , namespace : - 1 , module : "BasicType" } ; importCollect && importCollect ( t ) ; cache . ensureImported ( t ) ; return t . name ; } ; if ( dataType === DataType . ExtensionObject ) { const jtypeImport = await referenceExtensionObject ( session , dataTypeNodeId ) ; const jtype = jtypeImport . name ; importCollect && importCollect ( jtypeImport ) ; return { dataType , jtype : jtype } ; } switch ( dataType ) { case DataType . Null : return { dataType , jtype : "undefined" } ; case DataType . Boolean : return { dataType , jtype : "boolean" } ; case DataType . Byte : return { dataType , jtype : referenceBasicType ( "Byte" ) } ; case DataType . ByteString : return { dataType , jtype : "Buffer" } ; case DataType . DataValue : return { dataType , jtype : referenceBasicType ( "DataValue" ) } ; case DataType . DateTime : return { dataType , jtype : "Date" } ; case DataType . DiagnosticInfo : return { dataType , jtype : referenceBasicType ( "DiagnosticInfo" ) } ; case DataType . Double : return { dataType , jtype : "number" } ; case DataType . Float : return { dataType , jtype : "number" } ; case DataType . Guid : return { dataType , jtype : referenceBasicType ( "Guid" ) } ; case DataType . Int16 : return { dataType , jtype : referenceBasicType ( "Int16" ) } ; case DataType . Int32 : return { dataType , jtype : referenceBasicType ( "Int32" ) } ; case DataType . UInt16 : return { dataType , jtype : referenceBasicType ( "UInt16" ) } ; case DataType . UInt32 : return { dataType , jtype : referenceBasicType ( "UInt32" ) } ; case DataType . UInt64 : return { dataType , jtype : referenceBasicType ( "UInt64" ) } ; case DataType . Int64 : return { dataType , jtype : referenceBasicType ( "Int64" ) } ; case DataType . LocalizedText : return { dataType , jtype : referenceBasicType ( "LocalizedText" ) } ; case DataType . NodeId : return { dataType , jtype : referenceBasicType ( "NodeId" ) } ; case DataType . ExpandedNodeId : return { dataType , jtype : referenceBasicType ( "ExpandedNodeId" ) } ; case DataType . QualifiedName : return { dataType , jtype : referenceBasicType ( "QualifiedName" ) } ; case DataType . SByte : return { dataType , jtype : referenceBasicType ( "SByte" ) } ; case DataType . StatusCode : return { dataType , jtype : referenceBasicType ( "StatusCode" ) } ; case DataType . String : return { dataType , jtype : referenceBasicType ( "UAString" ) } ; case DataType . Variant : return { dataType , jtype : referenceBasicType ( "Variant" ) } ; case DataType . XmlElement : return { dataType , jtype : referenceBasicType ( "UAString" ) } ; default : throw new Error ( "Unsupported " + dataType + " " + DataType [ dataType ] ) ; } } interface ClassDefinition { nodeClass : NodeClass . VariableType | NodeClass . ObjectType ; browseName : QualifiedName ; isAbstract : boolean ; description : LocalizedText ; superType ? : ReferenceDescription ; baseClassDef ? : ClassDefinition | null ; children : ReferenceDescription [ ] ; members : ClassMember [ ] ; interfaceName : Import ; baseInterfaceName : Import | null ; dataTypeNodeId : NodeId | null ; dataType : DataType ; dataTypeName : string ; dataTypeImport ? : Import [ ] ; } export function makeTypeName2 ( nodeClass , browseName , suffix ? ) { assert ( browseName ) ; if ( nodeClass === NodeClass . Method ) { return { name : "UAMethod" , namespace : 0 , module : "UAMethod" } ; } return makeTypeNameNew ( nodeClass , null , browseName , suffix ) ; } export async function extractClassDefinition ( session , nodeId , cache ) { const extraImports = [ ] ; const _c = ( cache as any ) . classDefCache || { } ; ( cache as any ) . classDefCache = _c ; let classDef = _c [ nodeId . toString ( ) ] ; if ( classDef ) { return classDef ; } const nodeClass = await getNodeClass ( session , nodeId ) ; if ( nodeClass !== NodeClass . VariableType && nodeClass !== NodeClass . ObjectType ) { throw new Error ( "Invalid nodeClass " + NodeClass [ nodeClass ] + " nodeId " + nodeId . toString ( ) ) ; } const browseName = await getBrowseName ( session , nodeId ) ; const isAbstract = await getIsAbstract ( session , nodeId ) ; const superType = ( await getSubtypeNodeIdIfAny ( session , nodeId ) ) || undefined ; const dataTypeNodeId = await getDataTypeNodeId ( session , nodeId ) ; let dataTypeName = "" ; let dataType = DataType . Null ; let dataTypeImport = undefined ; if ( nodeClass === NodeClass . VariableType ) { dataType = await extractBasicDataType ( session , dataTypeNodeId ! ) ; const importCollector = ( i ) => { extraImports . push ( i ) ; cache . ensureImported ( i ) ; } ; const { jtype } = await getCorrepondingJavascriptType2 ( session , nodeId , dataTypeNodeId ! , cache , importCollector ) ; dataTypeName = jtype ; if ( ! dataTypeNodeId ?. isEmpty ( ) ) { dataTypeImport = extraImports ; } } const baseClassDef = ! superType ? null : await extractClassDefinition ( session , superType . nodeId , cache ) ; const description = await getDescription ( session , nodeId ) ; const interfaceName = makeTypeName2 ( nodeClass , browseName ) ; const baseInterfaceName = ! superType ? null : makeTypeName2 ( nodeClass , superType . browseName ) ; const children = await getChildrenOrFolderElements ( session , nodeId ) ; const members = [ ] ; classDef = { nodeClass , browseName , isAbstract , dataType , dataTypeNodeId , dataTypeName , dataTypeImport , superType , baseClassDef , description , children , members , interfaceName , baseInterfaceName } ; _c [ nodeId . toString ( ) ] = classDef ; for ( const child of children ) { const c = await extractClassMemberDef ( session , child . nodeId , classDef , cache ) ; classDef . members . push ( c ) ; } return classDef ; } interface ClassMemberBasic { name : string ; childType : Import ; isOptional : boolean ; modellingRule : ModellingRuleType | null ; description : LocalizedText ; suffix ? : string ; suffixInstantiate ? : string ; chevrons : any ; typeToReference : Import [ ] ; } interface ClassMember extends ClassMemberBasic { /**
     * the OPCUA name of the class member
     */ browseName : QualifiedName ; nodeClass : NodeClass . Object | NodeClass . Method | NodeClass . Variable ; /**
     * the Typescript name of the class Member
     */ name : string ; modellingRule : ModellingRuleType | null ; isOptional : boolean ; /**
     * class Def
     */ classDef : ClassDefinition | null ; description : LocalizedText ; typeDefinition : ReferenceDescription ; childType : Import ; children : ReferenceDescription [ ] ; children2 : ClassMemberBasic [ ] ; dataTypeNodeId ? : NodeId | null ; dataType ? : DataType ; jtype ? : string ; suffix ? : string ; suffix2 ? : string ; suffix3 ? : string ; innerClass ? : Import | null ; childBase ? : Import | null ; } interface Classified { Mandatory : ReferenceDescription [ ] ; Optional : ReferenceDescription [ ] ; MandatoryPlaceholder : ReferenceDescription [ ] ; OptionalPlaceholder : ReferenceDescription [ ] ; ExposesItsArray : ReferenceDescription [ ] ; } async function classify ( session , refs ) { const r = { Mandatory : [ ] , Optional : [ ] , MandatoryPlaceholder : [ ] , OptionalPlaceholder : [ ] , ExposesItsArray : [ ] } ; for ( const mm of refs ) { const modellingRule = await getModellingRule ( session , mm . nodeId ) ; if ( modellingRule ) { r [ modellingRule ] = r [ modellingRule ] || [ ] ; r [ modellingRule ] . push ( mm ) ; } } return r ; } async function extractAllMembers ( session , classDef , cache ) { const m = [ ... classDef . children ] ; let s = classDef ; while ( s . baseClassDef ) { s = s . baseClassDef ; for ( const mm of s . children ) { const found = m . findIndex ( ( r ) => r . browseName . toString ( ) === mm . browseName . toString ( ) ) ; if ( found < 0 ) { m . push ( mm ) ; } } } const members = await classify ( session , m ) ; return members ; } export async function findClassMember ( session , browseName , baseParentDef , cache ) { const str = browseName . toString ( ) ; const r = baseParentDef . children . find ( ( a ) => a . browseName . toString ( ) === str ) ; if ( r ) { const d = await extractClassMemberDef ( session , r ! . nodeId , baseParentDef , cache ) ; return d ; } const baseBaseParentDef = baseParentDef . superType && ( await extractClassDefinition ( session , baseParentDef . superType ! . nodeId , cache ) ) ; if ( ! baseBaseParentDef ) { return null ; } return await findClassMember ( session , browseName , baseBaseParentDef , cache ) ; } function hasNewMaterial ( referenceMembers , instanceMembers ) { const ref = referenceMembers . map ( ( x ) => x . browseName . toString ( ) ) . sort ( ) ; const instance = instanceMembers . map ( ( x ) => x . browseName . toString ( ) ) . sort ( ) ; for ( const n of instance ) { if ( ref . findIndex ( ( x ) => x === n ) < 0 ) { return true ; } } return false ; } export function checkIfShouldExposeInnerDefinition ( main , instance ) { const hasNewStuff = hasNewMaterial ( main . Mandatory , instance . Mandatory ) || hasNewMaterial ( main . Optional , instance . Optional ) ; return hasNewStuff ; } function dump1 ( a ) { console . log ( "Mandatory = " , a . Mandatory . map ( ( x ) => x . browseName . toString ( ) ) . sort ( ) . join ( " " ) ) ; console . log ( "Optional  = " , a . Optional . map ( ( x ) => x . browseName . toString ( ) ) . sort ( ) . join ( " " ) ) ; } async function _extractLocalMembers ( session , classMember , cache ) { const children2 = [ ] ; for ( const child of classMember . children ) { const nodeId = child . nodeId ; const browseName = await getBrowseName ( session , nodeId ) ; const name = toJavascritPropertyName ( browseName . name ! ) ; const description = await getDescription ( session , nodeId ) ; const modellingRule = await getModellingRule ( session , nodeId ) ; const isOptional = modellingRule === "Optional" ; const typeDefinition = await getTypeDefOrBaseType ( session , nodeId ) ; const childInBase = classMember . classDef ?. members . find ( ( a ) => a . browseName . toString ( ) === browseName . toString ( ) ) ; let childType ; if ( childInBase ) { childType = childInBase . childType ; } else { const d = typeDefinition . nodeId . isEmpty ( ) ? null : await extractClassDefinition ( session , typeDefinition . nodeId , cache ) ; childType = d ?. interfaceName || { name : "UAMethod" , module : "BasicType" , namespace : - 1 } ; } const { suffix , suffixInstantiate , typeToReference , chevrons } = await extractVariableExtra ( session , child . nodeId , cache , classMember ) ; const c = { childType , description , isOptional , modellingRule , name , suffix , suffixInstantiate , typeToReference , chevrons } ; children2 . push ( c ) ; } return children2 ; } function isUnspecifiedDataType ( dataType ? ) { return dataType === DataType . Null || dataType === DataType . Variant ; } async function extractVariableExtra ( session , nodeId , cache , classMember ) { const typeToReference = [ ] ; const importCollector = ( i ) => { typeToReference . push ( i ) ; cache . ensureImported ( i ) ; } ; const nodeClass = await getNodeClass ( session , nodeId ) ; if ( nodeClass === NodeClass . Variable ) { const dataTypeNodeId = await getDataTypeNodeId ( session , nodeId ) ; const { dataType , jtype } = await getCorrepondingJavascriptType2 ( session , nodeId , dataTypeNodeId ! , cache , importCollector ) ; cache && cache . referenceBasicType ( "DataType" ) ; importCollector ( { name : "DataType" , namespace : - 1 , module : "BasicType" } ) ; const t = isUnspecifiedDataType ( classMember . classDef ?. dataType ) ; const suffix = jtype === "undefined" || isUnspecifiedDataType ( dataType ) ? `<any, any>` : `<${ jtype }${ t ? `, /*c*/DataType.${ DataType [ dataType ] }` : "" }>` ; const suffix3 = `<T${ t ? `, /*b*/DT` : "" }>` ; const typeDef = await getTypeDefOrBaseType ( session , nodeId ) ; const typeDefCD = await extractClassDefinition ( session , typeDef . nodeId ! , cache ) ; const chevrons = calculateChevrons ( typeDefCD , { dataType } ) ; const suffix2 = chevrons . chevronsDef ; /** Suffix for instantiation
         *  if typeDef.dataType is not null, then we need to add a type argument
         */ let suffixInstantiate = "" ; if ( isUnspecifiedDataType ( dataType ) ) { if ( ! isUnspecifiedDataType ( typeDefCD . dataType ) ) { suffixInstantiate = "<any>" ; } else { suffixInstantiate = "<any, any>" ; } } else { if ( ! isUnspecifiedDataType ( typeDefCD . dataType ) ) { suffixInstantiate = `<${ jtype }>` ; } else { suffixInstantiate = `<${ jtype }, /*z*/DataType.${ DataType [ dataType ] }>` ; } } return { suffixInstantiate , suffix , suffix2 , suffix3 , dataTypeNodeId , dataType , jtype , typeToReference , chevrons } ; } return { suffix : "" , typeToReference } ; } interface ClassDefinitionB { superType ? : { nodeId : NodeId ; } ; interfaceName : Import ; } export async function extractClassMemberDef ( session , nodeId , parentDef , cache ) { const nodeClass = await getNodeClass ( session , nodeId ) ; const browseName = await getBrowseName ( session , nodeId ) ; const name = toJavascritPropertyName ( browseName . name ! ) ; if ( nodeClass !== NodeClass . Method && nodeClass !== NodeClass . Object && nodeClass !== NodeClass . Variable ) { throw new Error ( "Invalid property " + NodeClass [ nodeClass ] + " " + browseName ?. toString ( ) + " " + nodeId . toString ( ) ) ; } const description = await getDescription ( session , nodeId ) ; const children = await getChildrenOrFolderElements ( session , nodeId ) ; const modellingRule = await getModellingRule ( session , nodeId ) ; const isOptional = modellingRule === "Optional" ; const typeDefinition = await getTypeDefOrBaseType ( session , nodeId ) ; if ( ! typeDefinition . browseName ) { console . log ( "cannot find typeDefinition for " , browseName . toString ( ) , "( is the namespace loaded ?)" ) ; } let childType = makeTypeName2 ( nodeClass , typeDefinition . browseName ) ; const classDef = typeDefinition . nodeId . isEmpty ( ) ? null : await extractClassDefinition ( session , typeDefinition . nodeId , cache ) ; let innerClass = null ; let childBase = null ; let shouldExposeInnerDefinition = false ; const baseParentDef = parentDef . superType && ( await extractClassDefinition ( session , parentDef . superType ! . nodeId , cache ) ) ; if ( classDef && baseParentDef ) { const chevrons1 = calculateChevrons ( classDef , baseParentDef ) ; assert ( ! innerClass ) ; const membersReference = await extractAllMembers ( session , classDef , cache ) ; const membersInstance = await classify ( session , children ) ; shouldExposeInnerDefinition = checkIfShouldExposeInnerDefinition ( membersReference , membersInstance ) ; const sameMemberInBaseClass = baseParentDef && ( await findClassMember ( session , browseName , baseParentDef , cache ) ) ; if ( shouldExposeInnerDefinition ) { if ( sameMemberInBaseClass ) { innerClass = { module : parentDef . interfaceName . module , name : parentDef . interfaceName . name + "_" + name , namespace : nodeId . namespace } ; childBase = sameMemberInBaseClass . childType ; childType = innerClass ; } else { innerClass = { module : parentDef . interfaceName . module , name : parentDef . interfaceName . name + "_" + name , namespace : nodeId . namespace } ; childBase = childType ; childType = innerClass ; } assert ( innerClass ) ; assert ( childBase ) ; } else { childType = sameMemberInBaseClass ? sameMemberInBaseClass ?. childType : childType ; assert ( ! innerClass ) ; assert ( ! childBase ) ; } } else { assert ( ! innerClass ) ; assert ( ! childBase ) ; } let classMember = { name , nodeClass , browseName , modellingRule , isOptional , classDef , description , typeDefinition , childType , suffix2 : "" , suffix : "" , suffix3 : "" , suffixInstantiate : "" , children , children2 : [ ] , typeToReference : [ ] , innerClass , childBase , chevrons : null } ; classMember . children2 = await _extractLocalMembers ( session , classMember , cache ) ; if ( nodeClass === NodeClass . Variable ) { const extra = await extractVariableExtra ( session , nodeId , cache , classMember ) ; classMember = { ... classMember , ... extra } ; } return classMember ; } async function preDumpChildren ( session , padding , classDef , f , cache ) { f = f || new LineFile ( ) ; for ( const memberDef of classDef . members ) { const { innerClass , suffix2 , suffix3 , nodeClass , childBase , chevrons } = memberDef ; if ( ! innerClass || ! childBase ) { continue ; } cache . ensureImported ( childBase ) ; if ( innerClass . name === "UAPubSubDiagnostics_counters" || innerClass . name === "UAProgramStateMachine_currentState" ) { } const baseStuff = getBaseClassWithOmit2 ( memberDef ) ; f . write ( `export interface ${ innerClass . name }${ suffix2 } extends ${ baseStuff } { // ${ NodeClass [ nodeClass ] }` ) ; await dumpChildren ( session , padding + "  " , memberDef . children2 , f , cache ) ; f . write ( "}" ) ; } } function dumpChildren ( session , padding , children , f , cache ) { f = f || new LineFile ( ) ; for ( const def of children ) { const { suffixInstantiate , name , childType , modellingRule , isOptional , description } = def ; def . typeToReference . forEach ( ( t ) => cache . ensureImported ( t ) ) ; if ( modellingRule === "MandatoryPlaceholder" || modellingRule === "OptionalPlaceholder" ) continue ; cache . ensureImported ( childType ) ; const adjustedName = toJavascritPropertyName ( name ) ; if ( description . text ) { f . write ( `${ padding }/**` ) ; f . write ( `${ padding } * ${ name || "" }` ) ; f . write ( toComment ( `${ padding } * ` , description . text || "" ) ) ; f . write ( `${ padding } */` ) ; } f . write ( `${ padding }${ quotifyIfNecessary ( adjustedName ) }${ isOptional ? "?" : "" }: ${ childType . name }${ suffixInstantiate || "" };` ) ; } } const getSubSymbolList = ( s ) => { const subSymbolList = [ ] ; for ( const [ subSymbol , count ] of Object . entries ( s . subSymbols ) ) { subSymbolList . push ( subSymbol ) ; } return subSymbolList ; } ; export function findUsedImport ( namespaceIndex , cache ) { const usedImport = [ ] ; for ( const imp of Object . keys ( cache . imports ) ) { const symbolToImport = Object . keys ( cache . imports [ imp ] ) . filter ( ( f ) => Object . prototype . hasOwnProperty . call ( cache . requestedBasicTypes , f ) ) ; if ( symbolToImport . length == 0 ) { continue ; } usedImport . push ( imp ) ; } for ( let ns = 0 ; ns < cache . requestedSymbols . namespace . length ; ns ++ ) { if ( ns === namespaceIndex ) { continue ; } const module = cache . namespace [ ns ] . module ; if ( cache . requestedSymbols . namespace [ ns ] && Object . values ( cache . requestedSymbols . namespace [ ns ] ) . length > 0 ) { usedImport . push ( module ) ; } } return usedImport ; } function dumpUsedExport ( currentType , namespaceIndex , cache , f ? ) { f = f || new LineFile ( ) ; for ( const imp of Object . keys ( cache . imports ) ) { const symbolToImport = Object . keys ( cache . imports [ imp ] ) . filter ( ( f ) => f !== currentType && Object . prototype . hasOwnProperty . call ( cache . requestedBasicTypes , f ) ) ; if ( symbolToImport . length == 0 ) { continue ; } f . write ( `import { ${ symbolToImport . join ( ", " ) } } from "${ imp }"` ) ; } for ( let ns = 0 ; ns < cache . requestedSymbols . namespace . length ; ns ++ ) { const n = cache . namespace [ ns ] ; const sourceFolder = n . sourceFolder ; const module = n . module ; if ( ns === namespaceIndex ) { for ( const [ symbol , s ] of Object . entries ( cache . requestedSymbols . namespace [ ns ] . symbols ) . filter ( ( a ) => a [ 0 ] !== currentType ) ) { const filename = toFilename ( symbol ) ; const subSymbolList = getSubSymbolList ( s ) ; f . write ( `import { ${ subSymbolList . join ( ", " ) } } from "./${ filename }"` ) ; } } else { if ( cache . requestedSymbols . namespace [ ns ] ) { for ( const [ symbol , s ] of Object . entries ( cache . requestedSymbols . namespace [ ns ] . symbols ) ) { const subSymbolList = getSubSymbolList ( s ) ; const filename = toFilename ( symbol ) ; f . write ( `import { ${ subSymbolList . join ( ", " ) } } from "${ module }/source/${ filename }"` ) ; } } } } return f . toString ( ) ; } function toComment ( prefix , description ) { const d = wrapText ( description ) ; return d . split ( "\n" ) . map ( ( x ) => prefix + x ) . join ( "\n" ) ; } export type Type = "enum" | "basic" | "structure" | "ua" ; export async function _exportDataTypeToTypescript ( session , nodeId , cache , f ? ) { f = f || new LineFile ( ) ; const importCollector = ( i ) => { cache . ensureImported ( i ) ; } ; const nodeClass = NodeClass . DataType ; const description = await getDescription ( session , nodeId ) ; const definition = await getDefinition ( session , nodeId ) ; const browseName = await getBrowseName ( session , nodeId ) ; const isAbstract = await getIsAbstract ( session , nodeId ) ; const interfaceImport = makeTypeNameNew ( nodeClass , definition , browseName ) ; const interfaceName = interfaceImport . name ; const superType = await getSubtypeNodeId ( session , nodeId ) ; const baseInterfaceImport = makeTypeNameNew ( nodeClass , definition , superType . browseName ) ; cache . ensureImported ( baseInterfaceImport ) ; const baseInterfaceName = baseInterfaceImport . name ; f . write ( `/**` ) ; if ( description . text ) { f . write ( toComment ( " * " , description . text || "" ) ) ; f . write ( ` *` ) ; } f . write ( ` * |           |${ f1 ( " " ) }|` ) ; f . write ( ` * |-----------|${ f2 ( "-" ) }|` ) ; f . write ( ` * | namespace |${ f1 ( cache . namespace [ nodeId . namespace ] . namespaceUri ) }|` ) ; f . write ( ` * | nodeClass |${ f1 ( NodeClass [ nodeClass ] ) }|` ) ; f . write ( ` * | name      |${ f1 ( browseName . toString ( ) ) }|` ) ; f . write ( ` * | isAbstract|${ f1 ( isAbstract . toString ( ) ) }|` ) ; f . write ( ` */` ) ; let type = "basic" ; if ( definition instanceof EnumDefinition ) { type = "enum" ; f . write ( `export enum ${ interfaceName }  {` ) ; for ( const field of definition . fields ! ) { if ( field . description . text ) { f . write ( `  /**` ) ; f . write ( toComment ( "   * " , field . description . text || "" ) ) ; f . write ( `   */` ) ; } f . write ( `  ${ quotifyIfNecessary ( field . name ! ) } = ${ field . value [ 1 ] },` ) ; } f . write ( `}` ) ; } else if ( definition instanceof StructureDefinition ) { type = "structure" ; if ( interfaceName === "DTStructure" ) { f . write ( `export interface ${ interfaceName } {` ) ; } else { f . write ( `export interface ${ interfaceName } extends ${ baseInterfaceName }  {` ) ; } for ( const field of definition . fields ! ) { const fieldName = toJavascritPropertyName ( field . name ! ) ; if ( field . description . text ) { f . write ( `/** ${ field . description . text }*/` ) ; } let ar = "" ; if ( field . valueRank >= 1 ) { ar = "[]" ; } const { dataType , jtype } = await getCorrepondingJavascriptType ( session , field . dataType , cache , importCollector ) ; f . write ( `  ${ quotifyIfNecessary ( fieldName ) }: ${ jtype }${ ar }; // ${ DataType [ dataType ] } ${ field . dataType . toString ( ) }` ) ; } f . write ( `}` ) ; } else { type = "basic" ; f . write ( `// NO DEFINITION` ) ; f . write ( `export interface ${ interfaceName } extends ${ baseInterfaceName }  {` ) ; f . write ( `}` ) ; } return { type , content : f . toString ( ) , typeName : interfaceName } ; } function calculateChevrons ( classDef , classDefDerived ? ) { const { nodeClass , dataType , dataTypeName } = classDef ; let chevronsDef = "" ; let chevronsUse = "" ; let chevronsExtend = "" ; if ( nodeClass !== NodeClass . VariableType ) { return { chevronsDef , chevronsUse , chevronsExtend } ; } if ( ! isUnspecifiedDataType ( dataType ) ) { chevronsDef = `<T extends ${ dataTypeName }/*j*/>` ; chevronsUse = "<T/*k*/>" ; if ( classDefDerived ) { if ( isUnspecifiedDataType ( classDefDerived . dataType ) ) { chevronsExtend = `<T, /*i*/DataType.${ DataType [ dataType ] }>` ; } else { chevronsExtend = `<T/*h*/>` ; } } } else { chevronsDef = `<T, DT extends DataType>` ; if ( classDefDerived ) { if ( isUnspecifiedDataType ( classDefDerived . dataType ) ) { chevronsUse = "<T, /*m*/DT>" ; chevronsExtend = `<T/*g*/, DT>` ; } else { chevronsUse = `<T, /*n*/DataType.${ DataType [ classDefDerived . dataType ] }>` ; chevronsExtend = `<T, /*e*/DataType.${ DataType [ classDefDerived . dataType ] }>` ; } } } return { chevronsDef , chevronsUse , chevronsExtend } ; } function extractMembersRecursively ( classDef ? ) { if ( ! classDef ) { return [ ] ; } const m = classDef . members . map ( ( m ) => m . name ) ; if ( classDef . baseClassDef ) { const m2 = extractMembersRecursively ( classDef . baseClassDef ) ; return m . concat ( m2 ) ; } return m ; } function getBaseClassWithOmit ( classDef ) { const { baseInterfaceName , members } = classDef ; const allMembers = extractMembersRecursively ( classDef . baseClassDef ) ; const conflictingMembers = members . filter ( ( m ) => allMembers . indexOf ( m . name ) !== - 1 ) ; if ( conflictingMembers . length ) { } const chBase = calculateChevrons ( classDef . baseClassDef ! , classDef ) ; let baseStuff = `${ baseInterfaceName ?. name }${ chBase . chevronsUse }` ; if ( conflictingMembers . length ) { baseStuff = `Omit<${ baseStuff }, ${ conflictingMembers . map ( ( a ) => `"${ a . name }"` ) . join ( "|" ) }>` ; } return baseStuff ; } function getBaseClassWithOmit2 ( classMember ) { const members = classMember . children2 ; const allMembers = extractMembersRecursively ( classMember . classDef ) ; const conflictingMembers = members . filter ( ( m ) => allMembers . indexOf ( m . name ) !== - 1 ) ; if ( conflictingMembers . length ) { doDebug && console . log ( "conflictingMembers = " , conflictingMembers . map ( ( a ) => a . name ) . join ( " " ) ) ; } const childBase = classMember . childBase ; let baseStuff = `${ childBase ?. name }${ classMember . suffix3 }` ; if ( conflictingMembers . length ) { baseStuff = `Omit<${ baseStuff }, ${ conflictingMembers . map ( ( a ) => `"${ a . name }"` ) . join ( "|" ) }>` ; } return baseStuff ; } /**
 *  nodeId : a DataType, ReferenceType,AObjectType, VariableType node
 */ export async function _convertTypeToTypescript ( session , nodeId , cache , f ? ) { f = f || new LineFile ( ) ; const nodeClass = await getNodeClass ( session , nodeId ) ; if ( nodeClass === NodeClass . DataType ) { return await _exportDataTypeToTypescript ( session , nodeId , cache , f ) ; } const classDef = await extractClassDefinition ( session , nodeId , cache ) ; const { dataTypeName , dataType , dataTypeNodeId , interfaceName , baseInterfaceName , browseName , description , isAbstract , members } = classDef ; await preDumpChildren ( session , "    " , classDef , f , cache ) ; f . write ( `/**` ) ; if ( description . text ) { f . write ( toComment ( " * " , description . text || "" ) ) ; f . write ( ` *` ) ; } f . write ( ` * |                |${ f1 ( " " ) }|` ) ; f . write ( ` * |----------------|${ f2 ( "-" ) }|` ) ; f . write ( ` * |namespace       |${ f1 ( cache . namespace [ nodeId . namespace ] . namespaceUri ) }|` ) ; f . write ( ` * |nodeClass       |${ f1 ( NodeClass [ nodeClass ] ) }|` ) ; f . write ( ` * |typedDefinition |${ f1 ( browseName . toString ( ) + " " + nodeId . toString ( ) ) }|` ) ; if ( nodeClass === NodeClass . VariableType ) { f . write ( ` * |dataType        |${ f1 ( DataType [ dataType ] ) }|` ) ; f . write ( ` * |dataType Name   |${ f1 ( dataTypeName + " " + dataTypeNodeId ?. toString ( ) ) }|` ) ; } f . write ( ` * |isAbstract      |${ f1 ( isAbstract . toString ( ) ) }|` ) ; f . write ( ` */` ) ; cache . ensureImported ( baseInterfaceName ! ) ; cache . ensureImported ( { ... baseInterfaceName ! , name : baseInterfaceName ! . name + `${ baseExtension }` } ) ; const ch = calculateChevrons ( classDef ) ; if ( nodeClass === NodeClass . VariableType ) { if ( classDef . dataTypeImport ) { console . log ( chalk . red ( " ----------------> " , classDef . browseName ) ) ; classDef . dataTypeImport . forEach ( ( a ) => { console . log ( chalk . red ( " ------------------------> " , a . module , a . name , a . namespace ) ) ; } ) ; classDef . dataTypeImport . forEach ( cache . ensureImported . bind ( cache ) ) ; } cache . referenceBasicType ( "DataType" ) ; const chevrons = calculateChevrons ( classDef ) ; const chevronsBase = calculateChevrons ( classDef . baseClassDef ! , classDef ) ; const classBaseName = `${ interfaceName . name }${ baseExtension }${ chevrons . chevronsDef }` ; if ( baseInterfaceName ?. name === "UAVariableT" ) { f . write ( `export interface ${ classBaseName }  {` ) ; } else { const baseName = `${ baseInterfaceName ?. name }${ baseExtension }${ chevronsBase . chevronsExtend }` ; f . write ( `export interface ${ classBaseName }  extends ${ baseName } {` ) ; } } else { const classBaseName = `${ interfaceName . name }${ baseExtension }` ; if ( baseInterfaceName ?. name === "UAObject" ) { f . write ( `export interface ${ classBaseName } {` ) ; } else { const baseName = `${ baseInterfaceName ?. name }${ baseExtension }` ; f . write ( `export interface ${ classBaseName } extends ${ baseName } {` ) ; } } await dumpChildren ( session , "    " , members , f , cache ) ; f . write ( `}` ) ; const baseStuff = getBaseClassWithOmit ( classDef ) ; if ( nodeClass === NodeClass . VariableType ) { cache . referenceBasicType ( "DataType" ) ; const className = `${ interfaceName . name }${ ch . chevronsDef }` ; const c = isUnspecifiedDataType ( dataType ) ? "<T, DT /*A*/>" : "<T /*B*/>" ; const classBaseName = `${ interfaceName . name }${ baseExtension }${ c }` ; f . write ( `export interface ${ className } extends ${ baseStuff }, ${ classBaseName } {` ) ; } else { const className = `${ interfaceName . name }` ; const classBaseName = `${ interfaceName . name }${ baseExtension }` ; f . write ( `export interface ${ className } extends ${ baseStuff }, ${ classBaseName } {` ) ; } f . write ( `}` ) ; return { type : "ua" , content : f . toString ( ) , typeName : interfaceName . name } ; } export async function convertTypeToTypescript ( session , nodeId , options , cache ? , f ? ) { f = new LineFile ( ) ; cache = cache || ( await constructCache ( session , options ) ) ; const { type , content , typeName } = await _convertTypeToTypescript ( session , nodeId , cache ) ; f . write ( `// ----- this file has been automatically generated - do not edit` ) ; f . write ( dumpUsedExport ( typeName , nodeId . namespace , cache ) ) ; f . write ( content ) ; const folder = cache . namespace [ nodeId . namespace ] . sourceFolder ; const module = cache . namespace [ nodeId . namespace ] . module ; const filename = toFilename ( typeName ) ; const dependencies = findUsedImport ( nodeId . namespace , cache ) ; return { type , content : f . toString ( ) , folder , module , filename , dependencies } ; }