@@ -401,6 +401,9 @@ namespace ts {
401401 case CharacterCodes . greaterThan :
402402 // Starts of conflict marker trivia
403403 return true ;
404+ case CharacterCodes . hash :
405+ // Only if its the beginning can we have #! trivia
406+ return pos === 0 ;
404407 default :
405408 return ch > CharacterCodes . maxAsciiCharacter ;
406409 }
@@ -461,6 +464,13 @@ namespace ts {
461464 }
462465 break ;
463466
467+ case CharacterCodes . hash :
468+ if ( isShebangTrivia ( text , pos ) ) {
469+ pos = scanShebangTrivia ( text , pos ) ;
470+ continue ;
471+ }
472+ break ;
473+
464474 default :
465475 if ( ch > CharacterCodes . maxAsciiCharacter && ( isWhiteSpace ( ch ) || isLineBreak ( ch ) ) ) {
466476 pos ++ ;
@@ -528,6 +538,20 @@ namespace ts {
528538 return pos ;
529539 }
530540
541+ const shebangTriviaRegex = / ^ # ! .* / ;
542+
543+ function isShebangTrivia ( text : string , pos : number ) {
544+ // Shebangs check must only be done at the start of the file
545+ Debug . assert ( pos === 0 ) ;
546+ return shebangTriviaRegex . test ( text ) ;
547+ }
548+
549+ function scanShebangTrivia ( text : string , pos : number ) {
550+ let shebang = shebangTriviaRegex . exec ( text ) [ 0 ] ;
551+ pos = pos + shebang . length ;
552+ return pos ;
553+ }
554+
531555 // Extract comments from the given source text starting at the given position. If trailing is
532556 // false, whitespace is skipped until the first line break and comments between that location
533557 // and the next token are returned.If trailing is true, comments occurring between the given
@@ -617,6 +641,13 @@ namespace ts {
617641 export function getTrailingCommentRanges ( text : string , pos : number ) : CommentRange [ ] {
618642 return getCommentRanges ( text , pos , /*trailing*/ true ) ;
619643 }
644+
645+ /** Optionally, get the shebang */
646+ export function getShebang ( text : string ) : string {
647+ return shebangTriviaRegex . test ( text )
648+ ? shebangTriviaRegex . exec ( text ) [ 0 ]
649+ : undefined ;
650+ }
620651
621652 export function isIdentifierStart ( ch : number , languageVersion : ScriptTarget ) : boolean {
622653 return ch >= CharacterCodes . A && ch <= CharacterCodes . Z || ch >= CharacterCodes . a && ch <= CharacterCodes . z ||
@@ -1087,6 +1118,18 @@ namespace ts {
10871118 return token = SyntaxKind . EndOfFileToken ;
10881119 }
10891120 let ch = text . charCodeAt ( pos ) ;
1121+
1122+ // Special handling for shebang
1123+ if ( ch === CharacterCodes . hash && pos === 0 && isShebangTrivia ( text , pos ) ) {
1124+ pos = scanShebangTrivia ( text , pos ) ;
1125+ if ( skipTrivia ) {
1126+ continue ;
1127+ }
1128+ else {
1129+ return token = SyntaxKind . ShebangTrivia ;
1130+ }
1131+ }
1132+
10901133 switch ( ch ) {
10911134 case CharacterCodes . lineFeed :
10921135 case CharacterCodes . carriageReturn :
0 commit comments