import Lexx , { NodeTypes } from "xml-zero-lexer" ; export enum XmlTagName { GroupId = "groupId" , ArtifactId = "artifactId" , Version = "version" , Dependencies = "dependencies" , DependencyManagement = "dependencyManagement" , Exclusions = "exclusions" , Plugins = "plugins" , Project = "project" } export class ElementNode { public parent : ElementNode ; public tag : string ; public text ? : string ; public contentStart ? : number ; public contentEnd ? : number ; public children ? : ElementNode [ ] ; constructor ( parent , tag ) { this . parent = parent ; this . tag = tag ; this . text = "" ; } param child public addChild ( child ) { if ( this . text !== undefined ) { this . text = undefined ; } if ( this . children === undefined ) { this . children = [ ] ; } this . children . push ( child ) ; } } export function getNodesByTag ( text , tag ) { const tokens = Lexx ( text ) ; return getElementHierarchy ( text , tokens , tag ) ; } export function getCurrentNode ( text , offset ) { const tokens = Lexx ( text ) ; return getElementHierarchy ( text , tokens , offset ) ; } function getElementHierarchy ( text , tokens , targetTag ) ; function getElementHierarchy ( text , tokens , cursorOffset ) ; function getElementHierarchy ( text , tokens , tagOrOffset ) { let targetTag ; let cursorOffset ; if ( typeof tagOrOffset === "string" ) { targetTag = tagOrOffset ; } else if ( typeof tagOrOffset === "number" ) { cursorOffset = tagOrOffset ; } const n = tokens . length ; const elementNodes = [ ] ; const tagNodes = [ ] ; let cursorNode ; let pointer = 0 ; let i = 0 ; while ( i < n ) { const token = tokens [ i ] ; const currentNode = elementNodes [ elementNodes . length - 1 ] ; switch ( token [ 0 ] ) { case NodeTypes . XML_DECLARATION : case NodeTypes . ELEMENT_NODE : { const [ start , end ] = token . slice ( 1 , 3 ) ; const newElement = new ElementNode ( currentNode , text . substring ( start , end ) ) ; if ( currentNode !== undefined ) { currentNode . addChild ( newElement ) ; } pointer = end + 1 ; elementNodes . push ( newElement ) ; newElement . contentStart = pointer ; break ; } case NodeTypes . ATTRIBUTE_NODE : { const valueEnd = token [ 4 ] ; pointer = valueEnd + 1 ; currentNode . contentStart = pointer ; break ; } case NodeTypes . TEXT_NODE : { const [ start , end ] = token . slice ( 1 , 3 ) ; if ( currentNode !== undefined ) { currentNode . text = text . substring ( start , end ) ; } pointer = end ; break ; } case NodeTypes . CLOSE_ELEMENT : { currentNode . contentEnd = pointer ; elementNodes . pop ( ) ; break ; } default : break ; } if ( targetTag !== undefined && currentNode !== undefined && targetTag === currentNode . tag && tagNodes . indexOf ( currentNode ) < 0 ) { tagNodes . push ( currentNode ) ; } if ( cursorOffset !== undefined && cursorNode === undefined && currentNode !== undefined && currentNode . contentStart !== undefined && currentNode . contentStart <= cursorOffset && currentNode . contentEnd !== undefined && cursorOffset <= currentNode . contentEnd ) { cursorNode = currentNode ; } i += 1 ; } if ( targetTag !== undefined ) { return tagNodes ; } else if ( cursorOffset !== undefined ) { return cursorNode !== undefined ? cursorNode : elementNodes [ elementNodes . length - 1 ] ; } return undefined ; }