import { parse } from 'svelte/compiler' ; import { Node } from 'estree-walker' ; function parseAttributeValue ( value ) { return /^['"]/ . test ( value ) ? value . slice ( 1 , - 1 ) : value ; } function parseAttributes ( str , start ) { const attrs = [ ] ; str . split ( /\s+/ ) . filter ( Boolean ) . forEach ( ( attr ) => { const attrStart = start + str . indexOf ( attr ) ; const [ name , value ] = attr . split ( '=' ) ; attrs [ name ] = value ? parseAttributeValue ( value ) : name ; attrs . push ( { type : 'Attribute' , name , value : ! value || [ { type : 'Text' , start : attrStart + attr . indexOf ( '=' ) + 1 , end : attrStart + attr . length , raw : parseAttributeValue ( value ) } ] , start : attrStart , end : attrStart + attr . length } ) ; } ) ; return attrs ; } function extractTag ( htmlx , tag ) { const exp = new RegExp ( `(<!--[^]*?-->)|(<${ tag }([\\S\\s]*?)>)([\\S\\s]*?)<\\/${ tag }>` , 'g' ) ; const matches = [ ] ; let match = null ; while ( ( match = exp . exec ( htmlx ) ) != null ) { if ( match [ 0 ] . startsWith ( '<!--' ) ) { continue ; } const content = match [ 4 ] ; if ( ! content ) { continue ; } const start = match . index + match [ 2 ] . length ; const end = start + content . length ; const containerStart = match . index ; const containerEnd = match . index + match [ 0 ] . length ; matches . push ( { start : containerStart , end : containerEnd , name : tag , type : tag === 'style' ? 'Style' : 'Script' , attributes : parseAttributes ( match [ 3 ] , containerStart + `<${ tag }` . length ) , content : { type : 'Text' , start , end , value : content , raw : content } } ) ; } return matches ; } function findVerbatimElements ( htmlx ) { return [ ... extractTag ( htmlx , 'script' ) , ... extractTag ( htmlx , 'style' ) ] ; } function blankVerbatimContent ( htmlx , verbatimElements ) { let output = htmlx ; for ( const node of verbatimElements ) { const content = node . content ; if ( content ) { output = output . substring ( 0 , content . start ) + output . substring ( content . start , content . end ) . replace ( /[^\n]/g , ' ' ) . replace ( /[^\n][^\n][^\n][^\n]\n/g , '/**/\n' ) + output . substring ( content . end ) ; } } return output ; } export function parseHtmlx ( htmlx , options ? ) { const verbatimElements = findVerbatimElements ( htmlx ) ; const deconstructed = blankVerbatimContent ( htmlx , verbatimElements ) ; const parsingCode = options ?. emitOnTemplateError ? blankPossiblyErrorOperatorOrPropertyAccess ( deconstructed ) : deconstructed ; const htmlxAst = parse ( parsingCode ) . html ; for ( const s of verbatimElements ) { htmlxAst . children . push ( s ) ; htmlxAst . start = Math . min ( htmlxAst . start , s . start ) ; htmlxAst . end = Math . max ( htmlxAst . end , s . end ) ; } return { htmlxAst , tags : verbatimElements } ; } const possibleOperatorOrPropertyAccess = new Set ( [ '.' , '?' , '*' , '~' , '=' , '<' , '!' , '&' , '^' , '|' , ',' , '+' , '-' ] ) ; function blankPossiblyErrorOperatorOrPropertyAccess ( htmlx ) { let index = htmlx . indexOf ( '}' ) ; let lastIndex = 0 ; const { length } = htmlx ; while ( index < length && index >= 0 ) { let backwardIndex = index - 1 ; while ( backwardIndex > lastIndex ) { const char = htmlx . charAt ( backwardIndex ) ; if ( possibleOperatorOrPropertyAccess . has ( char ) ) { const isPlusOrMinus = char === '+' || char === '-' ; const isIncrementOrDecrement = isPlusOrMinus && htmlx . charAt ( backwardIndex - 1 ) === char ; if ( isIncrementOrDecrement ) { backwardIndex -= 2 ; continue ; } htmlx = htmlx . substring ( 0 , backwardIndex ) + ' ' + htmlx . substring ( backwardIndex + 1 ) ; } else if ( ! /\s/ . test ( char ) ) { break ; } backwardIndex -- ; } lastIndex = index ; index = htmlx . indexOf ( '}' , index + 1 ) ; } return htmlx ; }