import { ParseErr } from './err' ; import { trimWS } from './utils' ; import type { EtaConfig } from './config' ; export type TagType = 'r' | 'e' | 'i' | '' ; export interface TemplateObject { t : TagType ; val : string ; } export type AstObject = string | TemplateObject ; const templateLitReg = /`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})*}|(?!\${)[^\\`])*`/g ; const singleQuoteReg = /'(?:\\[\s\w"'\\`]|[^\n\r'\\])*?'/g ; const doubleQuoteReg = /"(?:\\[\s\w"'\\`]|[^\n\r"\\])*?"/g ; /** Escape special regular expression characters inside a string */ function escapeRegExp ( string ) { return string . replace ( /[.*+\-?^${}()|[\]\\]/g , '\\$&' ) ; } export default function parse ( str , config ) { let buffer = [ ] ; let trimLeftOfNextStr = false ; let lastIndex = 0 ; const parseOptions = config . parse ; if ( config . plugins ) { for ( let i = 0 ; i < config . plugins . length ; i ++ ) { const plugin = config . plugins [ i ] ; if ( plugin . processTemplate ) { str = plugin . processTemplate ( str , config ) ; } } } if ( config . rmWhitespace ) { str = str . replace ( /[\r\n]+/g , '\n' ) . replace ( /^\s+|\s+$/gm , '' ) ; } templateLitReg . lastIndex = 0 ; singleQuoteReg . lastIndex = 0 ; doubleQuoteReg . lastIndex = 0 ; function pushString ( strng , shouldTrimRightOfString ? ) { if ( strng ) { strng = trimWS ( strng , config , trimLeftOfNextStr , shouldTrimRightOfString ) ; if ( strng ) { strng = strng . replace ( /\\|'/g , '\\$&' ) . replace ( /\r\n|\n|\r/g , '\\n' ) ; buffer . push ( strng ) ; } } } const prefixes = [ parseOptions . exec , parseOptions . interpolate , parseOptions . raw ] . reduce ( function ( accumulator , prefix ) { if ( accumulator && prefix ) { return accumulator + '|' + escapeRegExp ( prefix ) ; } else if ( prefix ) { return escapeRegExp ( prefix ) ; } else { return accumulator ; } } , '' ) ; const parseOpenReg = new RegExp ( '([^]*?)' + escapeRegExp ( config . tags [ 0 ] ) + '(-|_)?\\s*(' + prefixes + ')?\\s*' , 'g' ) ; const parseCloseReg = new RegExp ( '\'|"|`|\\/\\*|(\\s*(-|_)?' + escapeRegExp ( config . tags [ 1 ] ) + ')' , 'g' ) ; let m ; while ( ( m = parseOpenReg . exec ( str ) ) ) { lastIndex = m [ 0 ] . length + m . index ; const precedingString = m [ 1 ] ; const wsLeft = m [ 2 ] ; const prefix = m [ 3 ] || '' ; pushString ( precedingString , wsLeft ) ; parseCloseReg . lastIndex = lastIndex ; let closeTag ; let currentObj = false ; while ( ( closeTag = parseCloseReg . exec ( str ) ) ) { if ( closeTag [ 1 ] ) { let content = str . slice ( lastIndex , closeTag . index ) ; parseOpenReg . lastIndex = lastIndex = parseCloseReg . lastIndex ; trimLeftOfNextStr = closeTag [ 2 ] ; const currentType = prefix === parseOptions . exec ? 'e' : prefix === parseOptions . raw ? 'r' : prefix === parseOptions . interpolate ? 'i' : '' ; currentObj = { t : currentType , val : content } ; break ; } else { const char = closeTag [ 0 ] ; if ( char === '/*' ) { const commentCloseInd = str . indexOf ( '*/' , parseCloseReg . lastIndex ) ; if ( commentCloseInd === - 1 ) { ParseErr ( 'unclosed comment' , str , closeTag . index ) ; } parseCloseReg . lastIndex = commentCloseInd ; } else if ( char === "'" ) { singleQuoteReg . lastIndex = closeTag . index ; const singleQuoteMatch = singleQuoteReg . exec ( str ) ; if ( singleQuoteMatch ) { parseCloseReg . lastIndex = singleQuoteReg . lastIndex ; } else { ParseErr ( 'unclosed string' , str , closeTag . index ) ; } } else if ( char === '"' ) { doubleQuoteReg . lastIndex = closeTag . index ; const doubleQuoteMatch = doubleQuoteReg . exec ( str ) ; if ( doubleQuoteMatch ) { parseCloseReg . lastIndex = doubleQuoteReg . lastIndex ; } else { ParseErr ( 'unclosed string' , str , closeTag . index ) ; } } else if ( char === '`' ) { templateLitReg . lastIndex = closeTag . index ; const templateLitMatch = templateLitReg . exec ( str ) ; if ( templateLitMatch ) { parseCloseReg . lastIndex = templateLitReg . lastIndex ; } else { ParseErr ( 'unclosed string' , str , closeTag . index ) ; } } } } if ( currentObj ) { buffer . push ( currentObj ) ; } else { ParseErr ( 'unclosed tag' , str , m . index + precedingString . length ) ; } } pushString ( str . slice ( lastIndex , str . length ) , false ) ; if ( config . plugins ) { for ( let i = 0 ; i < config . plugins . length ; i ++ ) { const plugin = config . plugins [ i ] ; if ( plugin . processAST ) { buffer = plugin . processAST ( buffer , config ) ; } } } return buffer ; }