import assert from "assert" ; import { parse } from "../lib/parser" ; import { getReprinter } from "../lib/patcher" ; import { Printer } from "../lib/printer" ; import { fromString } from "../lib/lines" ; import * as types from "ast-types" ; const namedTypes = types . namedTypes ; import FastPath from "../lib/fast-path" ; import { EOL as eol } from "os" ; const nodeMajorVersion = parseInt ( process . versions . node , 10 ) ; describe ( "parser" , function ( ) { [ "../parsers/acorn" , "../parsers/babel" , "../parsers/esprima" , "../parsers/flow" , "../parsers/typescript" , ] . forEach ( runTestsForParser ) ; it ( "AlternateParser" , function ( ) { const b = types . builders ; const parser = { parse : function ( ) { const program = b . program ( [ b . expressionStatement ( b . identifier ( "surprise" ) ) , ] ) ; program . comments = [ ] ; return program ; } , } ; function check ( options ? ) { const ast = parse ( "ignored" , options ) ; const printer = new Printer ( ) ; types . namedTypes . File . assert ( ast , true ) ; assert . strictEqual ( printer . printGenerically ( ast ) . code , "surprise;" ) ; } check ( { esprima : parser } ) ; check ( { parser : parser } ) ; } ) ; } ) ; function runTestsForParser ( parserId ) { const parserName = parserId . split ( "/" ) . pop ( ) ; if ( nodeMajorVersion < 6 && ( parserName === "babel" || parserName === "flow" || parserName === "typescript" ) ) { return ; } if ( ! parserName ) { return ; } const parser = require ( parserId ) ; it ( "[" + parserName + "] empty source" , function ( ) { const printer = new Printer ( ) ; function check ( code ) { const ast = parse ( code , { parser } ) ; assert . strictEqual ( printer . print ( ast ) . code , code ) ; } check ( "" ) ; check ( "/* block comment */" ) ; check ( "// line comment" ) ; check ( "\t\t\t" ) ; check ( eol ) ; check ( eol + eol ) ; check ( "    " ) ; } ) ; const lineCommentTypes = { acorn : "Line" , babel : "CommentLine" , esprima : "Line" , flow : "CommentLine" , typescript : "CommentLine" , } ; it ( "[" + parserName + "] parser basics" , function testParser ( done ) { const code = testParser + "" ; const ast = parse ( code , { parser } ) ; namedTypes . File . assert ( ast ) ; assert . ok ( getReprinter ( FastPath . from ( ast ) ) ) ; const funDecl = ast . program . body [ 0 ] ; const funBody = funDecl . body ; namedTypes . FunctionDeclaration . assert ( funDecl ) ; namedTypes . BlockStatement . assert ( funBody ) ; assert . ok ( getReprinter ( FastPath . from ( funBody ) ) ) ; const lastStatement = funBody . body . pop ( ) ; const doneCall = lastStatement . expression ; assert . ok ( ! getReprinter ( FastPath . from ( funBody ) ) ) ; assert . ok ( getReprinter ( FastPath . from ( ast ) ) ) ; funBody . body . push ( lastStatement ) ; assert . ok ( getReprinter ( FastPath . from ( funBody ) ) ) ; assert . strictEqual ( doneCall . callee . name , "done" ) ; assert . strictEqual ( lastStatement . comments . length , 2 ) ; const firstComment = lastStatement . comments [ 0 ] ; assert . strictEqual ( firstComment . type , lineCommentTypes [ parserName ] ) ; assert . strictEqual ( firstComment . leading , true ) ; assert . strictEqual ( firstComment . trailing , false ) ; assert . strictEqual ( firstComment . value , " Make sure done() remains the final statement in this function," ) ; const secondComment = lastStatement . comments [ 1 ] ; assert . strictEqual ( secondComment . type , lineCommentTypes [ parserName ] ) ; assert . strictEqual ( secondComment . leading , true ) ; assert . strictEqual ( secondComment . trailing , false ) ; assert . strictEqual ( secondComment . value , " or the above assertions will probably fail." ) ; done ( ) ; } ) ; it ( "[" + parserName + "] LocationFixer" , function ( ) { const code = [ "function foo() {" , "    a()" , "    b()" , "}" ] . join ( eol ) ; const ast = parse ( code , { parser } ) ; const printer = new Printer ( ) ; types . visit ( ast , { visitFunctionDeclaration : function ( path ) { if ( namedTypes . BlockStatement . check ( path . node . body ) ) { path . node . body . body . reverse ( ) ; } this . traverse ( path ) ; } , } ) ; const altered = code . replace ( "a()" , "xxx" ) . replace ( "b()" , "a()" ) . replace ( "xxx" , "b()" ) ; assert . strictEqual ( altered , printer . print ( ast ) . code ) ; } ) ; it ( "[" + parserName + "] TabHandling" , function ( ) { function check ( code , tabWidth ) { const lines = fromString ( code , { tabWidth : tabWidth } ) ; assert . strictEqual ( lines . length , 1 ) ; function checkId ( s , loc ) { const sliced = lines . slice ( loc . start , loc . end ) ; assert . strictEqual ( s + "" , sliced . toString ( ) ) ; } types . visit ( parse ( code , { tabWidth : tabWidth , parser , } ) , { visitIdentifier ( path ) { const ident = path . node ; checkId ( ident . name , ident . loc ! ) ; this . traverse ( path ) ; } , visitLiteral ( path ) { const lit = path . node ; checkId ( lit . value , lit . loc ! ) ; this . traverse ( path ) ; } , } ) ; } for ( let tabWidth = 1 ; tabWidth <= 8 ; ++ tabWidth ) { check ( "\t\ti = 10;" , tabWidth ) ; check ( "\t\ti \t= 10;" , tabWidth ) ; check ( "\t\ti \t=\t 10;" , tabWidth ) ; check ( "\t \ti \t=\t 10;" , tabWidth ) ; check ( "\t \ti \t=\t 10;\t" , tabWidth ) ; check ( "\t \ti \t=\t 10;\t " , tabWidth ) ; } } ) ; it ( "[" + parserName + "] Only comment followed by space" , function ( ) { const printer = new Printer ( ) ; function check ( code ) { const ast = parse ( code , { parser } ) ; assert . strictEqual ( printer . print ( ast ) . code , code ) ; } check ( "// comment" ) ; check ( "// comment " ) ; check ( "// comment\n" ) ; check ( "// comment\n\n" ) ; check ( " // comment\n" ) ; check ( " // comment\n " ) ; check ( " // comment \n " ) ; check ( "/* comment */" ) ; check ( "/* comment */ " ) ; check ( " /* comment */" ) ; check ( "\n/* comment */" ) ; check ( "\n/* comment */\n" ) ; check ( "\n /* comment */\n " ) ; check ( "/* comment */\n " ) ; check ( "/* com\n\nment */" ) ; check ( "/* com\n\nment */ " ) ; check ( " /* com\n\nment */ " ) ; } ) ; }