import * as ts from 'typescript' ; import * as path from 'path' ; export function formatUrl ( url ) { return url . replace ( /\\/g , '/' ) ; } export function tryFindDeclarationByName ( sourceFile , name ) { let decl ; if ( ts . isIdentifier ( name ) ) { const declName = name . getText ( ) ; sourceFile . statements . forEach ( statement => { if ( decl ) return ; if ( hasName ( statement ) && getText ( statement . name ) === declName ) { decl = statement ; } else if ( ts . isVariableStatement ( statement ) ) { decl = statement . declarationList . declarations . find ( decl => getText ( decl . name ) === declName ) ; } } ) ; } return decl ; } export function formatName ( name , upper ? ) { name = name . replace ( /[\/\\.\@_-][a-z]/gi , s => s . substring ( 1 ) . toUpperCase ( ) ) . replace ( /\/|\\|\./g , '' ) ; return upper ? ( name [ 0 ] . toUpperCase ( ) + name . substring ( 1 ) ) : name ; } export function getTypeArguments ( node ) { return ( node as ts . NodeWithTypeArguments ) . typeArguments ; } export function getSymbol ( node ) { return ( node as any ) . symbol ; } export function hasName ( node ) { return ! ! ( node as ts . NamedDeclaration ) . name ; } export function getDeclarationBySymbol ( symbol ) { return symbol . valueDeclaration || ( symbol . declarations && symbol . declarations [ 0 ] ) ; } export function getJSDocProp ( node ) { return ( node as any ) . jsDoc ; } export function getJSDoc ( node ) { const jsDocs = getJSDocs ( node ) ; return jsDocs ? jsDocs [ jsDocs . length - 1 ] : undefined ; } export function getDeclarationMayHasJSDoc ( node ) { if ( ! getJSDocProp ( node ) ) { const symbol = getSymbol ( node ) ; const declaration = symbol && getDeclarationBySymbol ( symbol ) ; if ( declaration ) { if ( ts . isPropertyAccessExpression ( declaration ) ) { return declaration . parent . parent ; } return declaration ; } } return node ; } export function getJSDocs ( node ) { const decl = getDeclarationMayHasJSDoc ( node ) ; if ( ! decl ) return ; const jsDocs = getJSDocProp ( decl ) ; if ( ! jsDocs ) { const tags = ts . getJSDocTags ( decl ) ; const jsDocArray = [ ] ; tags . forEach ( tag => { if ( ts . isJSDoc ( tag . parent ) && ! jsDocArray . includes ( tag . parent ) ) { jsDocArray . push ( tag . parent ) ; } } ) ; return jsDocArray ; } return jsDocs ; } export function hasQuestionToken ( node ) { return ( node as any ) . questionToken !== undefined ; } export function isDeclareModule ( node ) { return ts . isModuleDeclaration ( node ) && ts . isStringLiteral ( node . name ) ; } export function formatIdentifierName ( name ) { return name . replace ( /^("|')|("|')$/g , '' ) ; } export function getText ( node ? ) { if ( node ) { if ( ts . isIdentifier ( node ) ) { return formatIdentifierName ( node . text ) ; } else if ( ts . isStringLiteral ( node ) ) { return node . text ; } else if ( ts . isQualifiedName ( node ) ) { return getText ( node . right ) ; } } return '' ; } export function findJsDocTag ( node , name ) { const jsDocTags = ts . isJSDoc ( node ) ? node . tags : ts . getJSDocTags ( getDeclarationMayHasJSDoc ( node ) ) ; return jsDocTags && jsDocTags . find ( tag => getText ( tag . tagName ) === name ) ; } export function normalizeDtsUrl ( file ) { const ext = path . extname ( file ) ; file += ext ? '' : '.d.ts' ; return file ; } export function resolveUrl ( url ) { try { return require . resolve ( url ) ; } catch ( e ) { return ; } } export function modifierHas ( node , kind ) { return node . modifiers && node . modifiers . find ( mod => kind === mod . kind ) ; } export interface AssignElement { init ? : boolean ; obj ? : ts . Expression ; key : ts . Identifier ; value ? : ts . Expression ; node : ts . Node ; } export interface ExportObj { node : ts . Node ; originalNode : ts . Node ; } export function findExports ( source ) { let exportEqual ; const exportList = new Map ( ) ; const checker = getAssignChecker ( [ 'exports' , 'module' , 'module.exports' , ] ) ; const addExportNode = ( name , value , node ) => { exportList . set ( name , { node : value ! , originalNode : node , } ) ; } ; source . statements . forEach ( statement => { const isExport = modifierHas ( statement , ts . SyntaxKind . ExportKeyword ) ; if ( ts . isExportAssignment ( statement ) ) { if ( statement . isExportEquals ) { exportEqual = { node : statement . expression , originalNode : statement , } ; } else { addExportNode ( 'default' , statement . expression , statement ) ; } return ; } else if ( isExport && ( ts . isFunctionDeclaration ( statement ) || ts . isClassDeclaration ( statement ) ) ) { if ( modifierHas ( statement , ts . SyntaxKind . DefaultKeyword ) ) { addExportNode ( 'default' , statement , statement ) ; } else { addExportNode ( getText ( statement . name ) , statement , statement ) ; } return ; } else if ( ts . isExportDeclaration ( statement ) && statement . exportClause ) { statement . exportClause . elements . forEach ( spec => { addExportNode ( getText ( spec . name ) , spec . propertyName || spec . name , statement ) ; } ) ; return ; } getAssignResultFromStatement ( statement ) . forEach ( result => { const newResult = checker . check ( result ) ; if ( isExport ) { addExportNode ( getText ( result . key ) , result . value ! , result . node ) ; } if ( ! newResult ) return ; if ( newResult . name === 'exports' || newResult . name === 'module.exports' ) { addExportNode ( getText ( newResult . key ) , newResult . value , newResult . node ) ; } else if ( newResult . name === 'module' && getText ( newResult . key ) === 'exports' ) { exportEqual = { node : newResult . value ! , originalNode : newResult . node , } ; } } ) ; } ) ; return { exportEqual , exportList , } ; } export function findAssign ( statements , cb ) { statements . forEach ( statement => { getAssignResultFromStatement ( statement ) . forEach ( cb ) ; } ) ; } export function findAssignByName ( statements , name , cb ) { const checker = getAssignChecker ( name ) ; return findAssign ( statements , result => { const newResult = checker . check ( result ) ; if ( newResult ) cb ( newResult ) ; } ) ; } type FindAssignNameType = string | RegExp ; interface AssignNameElement extends AssignElement { obj : ts . Expression ; name : string ; value : ts . Expression ; } export function getAssignChecker ( name ) { const variableList = Array . isArray ( name ) ? name : [ name ] ; const nameAlias = { } ; const getRealName = name => { const realName = nameAlias [ name ] || name ; const hitTarget = ! ! variableList . find ( variable => { return ( typeof variable === 'string' ) ? variable === realName : variable . test ( realName ) ; } ) ; return hitTarget ? realName : undefined ; } ; return { check ( el ) { const { obj , key , value , node } = el ; if ( ! obj || ! value ) { if ( value ) { const realName = getRealName ( value . getText ( ) . trim ( ) ) ; if ( realName ) { nameAlias [ getText ( key ) ] = realName ; } } return ; } const realName = getRealName ( obj . getText ( ) . trim ( ) ) ; if ( realName ) { return { name : realName , obj , key , value , node } ; } } , } ; } export function getAssignResultFromStatement ( statement , assignList = [ ] ) { const checkValue = ( node ? ) => { if ( node && ts . isBinaryExpression ( node ) ) { checkBinary ( node ) ; return checkValue ( node . right ) ; } else { return node ; } } ; const checkBinary = ( node ) => { if ( ts . isPropertyAccessExpression ( node . left ) && ts . isIdentifier ( node . left . name ) ) { assignList . push ( { obj : node . left . expression , key : node . left . name , value : checkValue ( node . right ) , node : statement , } ) ; } else if ( ts . isIdentifier ( node . left ) ) { assignList . push ( { key : node . left , value : checkValue ( node . right ) , node : statement , } ) ; } else if ( ts . isElementAccessExpression ( node . left ) && ts . isStringLiteral ( node . left . argumentExpression ) ) { assignList . push ( { obj : node . left . expression , key : ts . createIdentifier ( node . left . argumentExpression . text ) , value : checkValue ( node . right ) , node : statement , } ) ; } } ; const eachStatement = ( statements ) => { statements . forEach ( statement => getAssignResultFromStatement ( statement , assignList ) ) ; } ; const checkIfStatement = ( el ) => { if ( ts . isBlock ( el . thenStatement ) ) { eachStatement ( el . thenStatement . statements ) ; } if ( el . elseStatement ) { if ( ts . isIfStatement ( el . elseStatement ) ) { checkIfStatement ( el . elseStatement ) ; } else if ( ts . isBlock ( el . elseStatement ) ) { eachStatement ( el . elseStatement . statements ) ; } } } ; if ( ts . isExpressionStatement ( statement ) && ts . isBinaryExpression ( statement . expression ) ) { checkBinary ( statement . expression ) ; } else if ( ts . isVariableStatement ( statement ) ) { statement . declarationList . declarations . forEach ( declare => { if ( ts . isIdentifier ( declare . name ) ) { assignList . push ( { init : true , key : declare . name , value : checkValue ( declare . initializer ) , node : declare , } ) ; } } ) ; } else if ( ts . isIfStatement ( statement ) ) { checkIfStatement ( statement ) ; } return assignList ; }