import { TemplatingBindingLanguage , InterpolationBindingExpression } from 'aurelia-templating-binding' ; import { AccessMember , AccessScope , AccessKeyed , NameExpression , ValueConverter } from 'aurelia-binding' ; import { Container } from 'aurelia-dependency-injection' ; import { Rule , Parser , ParserState , Issue , IssueSeverity } from 'template-lint' ; import ts = require ( 'typescript' ) ; import { ViewResources , BindingLanguage , BehaviorInstruction , HtmlBehaviorResource , ViewFactory } from 'aurelia-templating' ; import { ASTAttribute as P5ASTAttribute } from "parse5" ; import { AureliaReflection } from './aurelia-reflection' ; export class ASTBuilder extends Rule { public root : ASTNode ; public reportBindingSyntax = true ; constructor ( protected auReflection ? ) { super ( ) ; this . auReflection = this . auReflection || new AureliaReflection ( ) ; } init ( parser ) { var current = new ASTNode ( ) ; this . root = current ; parser . on ( "startTag" , ( tag , attrs , selfClosing , loc ) => { let next = new ASTElementNode ( ) ; next . tag = tag ; next . parent = current ; next . location = new FileLoc ( loc . line , loc . col ) ; next . attrs = attrs . map ( ( x , i ) => { var attr = new ASTAttribute ( ) ; attr . name = ( x . prefix !== undefined && x . prefix != "" ) ? `${ x . prefix }:${ x . name }` : x . name ; var attrLoc = loc . attrs [ attr . name ] || loc . attrs [ attr . name . toLowerCase ( ) ] ; attr . name = x . name ; attr . instruction = this . createAttributeInstruction ( tag , x . name , x . value , attrLoc . line , attrLoc . col ) ; attr . location = new FileLoc ( attrLoc . line , attrLoc . col ) ; return attr ; } ) ; current . children . push ( next ) ; if ( ! parser . isVoid ( tag ) ) current = next ; } ) ; parser . on ( "endTag" , ( tag , attrs , selfClosing , loc ) => { if ( current . parent === null ) { return ; } current = current . parent ; } ) ; parser . on ( "text" , ( text , loc ) => { let child = new ASTTextNode ( ) ; child . parent = current ; child . expression = this . createTextExpression ( text , loc . line , loc . col ) ; child . location = new FileLoc ( loc . line , loc . col ) ; current . children . push ( child ) ; } ) ; } protected createAttributeInstruction ( tag , name , value , line , column ) { var instruction = null ; try { instruction = this . auReflection . createAttributeInstruction ( tag , name , value ) ; } catch ( error ) { this . reportSyntaxIssue ( error , line , column ) ; } return instruction ; } protected createTextExpression ( text , line , column ) { var exp = null ; try { exp = this . auReflection . createTextExpression ( text ) ; } catch ( error ) { this . reportSyntaxIssue ( error , line , column ) ; } return exp ; } private reportSyntaxIssue ( error , line , column ) { let shorter = error . message . split ( /\./ ) ; let msg = shorter ? shorter [ 0 ] : error . message . trim ( ) ; let detail = shorter && shorter . length > 1 ? shorter . splice ( 1 ) . join ( ) . trim ( ) : null ; let issue = new Issue ( { message : msg , detail : detail , line : line , column : column , severity : IssueSeverity . Error } ) ; if ( this . reportBindingSyntax ) this . reportIssue ( issue ) ; } } export class FileLoc { constructor ( public line , public column ) { } } export class ASTContext { name : string = null ; type : ts . TypeNode = null ; typeDecl : ts . NamedDeclaration = null ; typeValue : Object = null ; constructor ( init ? ) { if ( init ) Object . assign ( this , init ) ; } } export class ASTNode { public context : ASTContext = null ; public locals : ASTContext [ ] = [ ] ; public parent : ASTNode = null ; public children : ASTNode [ ] = [ ] ; public location : FileLoc = null ; constructor ( init ? ) { if ( init ) Object . assign ( this , init ) ; } addChild ( node ) { if ( this . children . indexOf ( node ) == - 1 ) { this . children . push ( node ) ; node . parent = this ; } } public static inheritLocals ( node , ancestor ? ) { let locals = [ ] ; if ( ancestor ) { while ( node != null && ancestor >= 0 ) { node = node . parent ; ancestor -= 1 ; } } while ( node != null ) { node . locals . forEach ( x => { let index = locals . findIndex ( y => y . name == x . name ) ; if ( index == - 1 ) locals . push ( x ) ; } ) ; node = node . parent ; } return locals ; } public static inheritContext ( node , ancestor ? ) { if ( ancestor ) { while ( node != null && ancestor >= 0 ) { node = node . parent ; ancestor -= 1 ; } } while ( node != null ) { if ( node . context != null ) return node . context ; node = node . parent ; } return null ; } } export class ASTAttribute { public name : string ; public instruction : any ; public location : FileLoc ; } export class ASTElementNode extends ASTNode { public tag : string ; public attrs : ASTAttribute [ ] ; constructor ( ) { super ( ) ; } } export class ASTTextNode extends ASTNode { public expression : InterpolationBindingExpression ; constructor ( ) { super ( ) ; } }