import { AST_NODE_TYPES , TSESTree , } from "@typescript-eslint/experimental-utils" ; import { Reference , Scope , Variable , } from "@typescript-eslint/experimental-utils/dist/ts-eslint-scope" ; import { isGetFunction } from "./getFunction" ; import { isLoadReference } from "./load" ; export function isContextSyncIdentifier ( node ) { return ( node . name === "context" && node . parent ?. type === AST_NODE_TYPES . MemberExpression && node . parent ?. parent ?. type === AST_NODE_TYPES . CallExpression && node . parent ?. property . type === AST_NODE_TYPES . Identifier && node . parent ?. property . name === "sync" ) ; } export function findTopLevelExpression ( node ) { while ( node . parent && node . parent . type === AST_NODE_TYPES . MemberExpression ) { node = node . parent ; } return node ; } export type OfficeApiReference = { /**
     * Get: An OfficeJs object, which is created when calling a `get` type function
     * Load: A reference to `object.load()` type call
     * Method: The reference is calling a method. Ex: `object.methodCall()`
     * Read: The reference value is being read and it is not a method
     * Sync: A call to `context.sync()`
     */ operation : "Get" | "Load" | "Method" | "Read" | "Sync" ; reference : Reference ; } ; let proxyVariables ; let apiReferences ; export function findOfficeApiReferences ( scope ) { proxyVariables = new Set < Variable > ( ) ; apiReferences = [ ] ; findOfficeApiReferencesInScope ( scope ) ; return apiReferences ; } function findOfficeApiReferencesInScope ( scope ) { scope . references . forEach ( ( reference ) => { const node = reference . identifier ; if ( reference . isWrite ( ) && reference . writeExpr && isGetFunction ( reference . writeExpr ) && reference . resolved ) { proxyVariables . add ( reference . resolved ) ; apiReferences . push ( { operation : "Get" , reference : reference } ) ; } else if ( isContextSyncIdentifier ( reference . identifier ) ) { apiReferences . push ( { operation : "Sync" , reference : reference } ) ; } else if ( reference . isRead ( ) && reference . resolved && proxyVariables . has ( reference . resolved ) ) { if ( isLoadReference ( node ) ) { apiReferences . push ( { operation : "Load" , reference : reference } ) ; } else if ( isMethodReference ( node ) ) { apiReferences . push ( { operation : "Method" , reference : reference } ) ; } else { apiReferences . push ( { operation : "Read" , reference : reference } ) ; } } } ) ; scope . childScopes . forEach ( findOfficeApiReferencesInScope ) ; } function isMethod ( node ) { const topExpression = findTopLevelExpression ( node ) ; return ( topExpression . parent ?. type === AST_NODE_TYPES . CallExpression && topExpression . parent . callee === topExpression ) ; } export function isMethodReference ( node ) { return ( node . parent && node . parent . type === TSESTree . AST_NODE_TYPES . MemberExpression && isMethod ( node . parent ) ) ; } export function findPropertiesRead ( node ) { let propertyName = "" ; while ( node ) { if ( node . type === AST_NODE_TYPES . MemberExpression && node . property . type === AST_NODE_TYPES . Identifier && ! isMethod ( node ) ) { propertyName += node . property . name + "/" ; } node = node . parent ; } return propertyName . slice ( 0 , - 1 ) ; }