diff --git a/.gitignore b/.gitignore index f9d1730..a39cb33 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ /unicode/uc-nl.htm /unicode/uc-pc.htm cabal.sandbox.config + +# stack +/.stack-work/ diff --git a/language-javascript.cabal b/language-javascript.cabal index b1900e5..b18afb1 100644 --- a/language-javascript.cabal +++ b/language-javascript.cabal @@ -9,7 +9,9 @@ License: BSD3 License-file: LICENSE Author: Alan Zimmerman Maintainer: Erik de Castro Lopo -Copyright: (c) 2010-2015 Alan Zimmerman, 2015 Erik de Castro Lopo +Copyright: (c) 2010-2015 Alan Zimmerman + (c) 2015-2018 Erik de Castro Lopo + (c) 2018 Daniel Gasienica Category: Language Build-type: Simple homepage: https://github.com/erikd/language-javascript diff --git a/src/Language/JavaScript/Parser.hs b/src/Language/JavaScript/Parser.hs index a813bc9..80394ec 100644 --- a/src/Language/JavaScript/Parser.hs +++ b/src/Language/JavaScript/Parser.hs @@ -40,5 +40,3 @@ import Language.JavaScript.Parser.SrcLocation import Language.JavaScript.Pretty.Printer -- EOF - - diff --git a/src/Language/JavaScript/Parser/AST.hs b/src/Language/JavaScript/Parser/AST.hs index 53e6b31..f8dbbda 100644 --- a/src/Language/JavaScript/Parser/AST.hs +++ b/src/Language/JavaScript/Parser/AST.hs @@ -10,6 +10,8 @@ module Language.JavaScript.Parser.AST , JSTryCatch (..) , JSTryFinally (..) , JSStatement (..) + , JSExportBody (..) + , JSExportSpecifier (..) , JSBlock (..) , JSSwitchParts (..) , JSAST (..) @@ -71,9 +73,10 @@ data JSStatement | JSSwitch !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSAnnot ![JSSwitchParts] !JSAnnot !JSSemi -- ^switch,lb,expr,rb,caseblock,autosemi | JSThrow !JSAnnot !JSExpression !JSSemi -- ^throw val autosemi | JSTry !JSAnnot !JSBlock ![JSTryCatch] !JSTryFinally -- ^try,block,catches,finally - | JSVariable !JSAnnot !(JSCommaList JSExpression) !JSSemi -- ^var|const, decl, autosemi + | JSVariable !JSAnnot !(JSCommaList JSExpression) !JSSemi -- ^var, decl, autosemi | JSWhile !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement -- ^while,lb,expr,rb,stmt | JSWith !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSStatement !JSSemi -- ^with,lb,expr,rb,stmt list + | JSExport !JSAnnot !JSExportBody !JSSemi -- ^export, body, autosemi deriving (Data, Eq, Show, Typeable) data JSExpression @@ -112,6 +115,7 @@ data JSExpression data JSBinOp = JSBinOpAnd !JSAnnot + | JSBinOpAs !JSAnnot | JSBinOpBitAnd !JSAnnot | JSBinOpBitOr !JSAnnot | JSBinOpBitXor !JSAnnot @@ -168,6 +172,16 @@ data JSAssignOp | JSBwOrAssign !JSAnnot deriving (Data, Eq, Show, Typeable) +data JSExportBody + = JSExportStatement !JSStatement + | JSExportClause !JSAnnot !(Maybe (JSCommaList JSExportSpecifier)) !JSAnnot -- ^lb,body,rb + deriving (Data, Eq, Show, Typeable) + +data JSExportSpecifier + = JSExportSpecifier !JSIdent + | JSExportSpecifierAs !JSIdent !JSBinOp !JSIdent + deriving (Data, Eq, Show, Typeable) + data JSTryCatch = JSCatch !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSBlock -- ^catch,lb,ident,rb,block | JSCatchIf !JSAnnot !JSAnnot !JSExpression !JSAnnot !JSExpression !JSAnnot !JSBlock -- ^catch,lb,ident,if,expr,rb,block @@ -233,7 +247,7 @@ data JSCommaTrailingList a deriving (Data, Eq, Show, Typeable) -- ----------------------------------------------------------------------------- --- | Show the AST elements stipped of their JSAnnot data. +-- | Show the AST elements stripped of their JSAnnot data. -- Strip out the location info showStripped :: JSAST -> String @@ -246,7 +260,6 @@ showStripped (JSAstLiteral s _) = "JSAstLiteral (" ++ ss s ++ ")" class ShowStripped a where ss :: a -> String - instance ShowStripped JSStatement where ss (JSStatementBlock _ xs _ _) = "JSStatementBlock " ++ ss xs ss (JSBreak _ JSIdentNone s) = "JSBreak" ++ commaIf (ss s) @@ -276,6 +289,7 @@ instance ShowStripped JSStatement where ss (JSVariable _ xs _as) = "JSVariable " ++ ss xs ss (JSWhile _ _lb x1 _rb x2) = "JSWhile (" ++ ss x1 ++ ") (" ++ ss x2 ++ ")" ss (JSWith _ _lb x1 _rb x _) = "JSWith (" ++ ss x1 ++ ") (" ++ ss x ++ ")" + ss (JSExport _ b _) = "JSExport (" ++ ss b ++ ")" instance ShowStripped JSExpression where ss (JSArrayLiteral _lb xs _rb) = "JSArrayLiteral " ++ ss xs @@ -308,6 +322,15 @@ instance ShowStripped JSExpression where ss (JSVarInitExpression x1 x2) = "JSVarInitExpression (" ++ ss x1 ++ ") " ++ ss x2 ss (JSSpreadExpression _ x1) = "JSSpreadExpression (" ++ ss x1 ++ ")" +instance ShowStripped JSExportBody where + ss (JSExportStatement x1) = "JSExportStatement (" ++ ss x1 ++ ")" + ss (JSExportClause _ Nothing _) = "JSExportClause ()" + ss (JSExportClause _ (Just x1) _) = "JSExportClause (" ++ ss x1 ++ ")" + +instance ShowStripped JSExportSpecifier where + ss (JSExportSpecifier x1) = "JSExportSpecifier (" ++ ss x1 ++ ")" + ss (JSExportSpecifierAs x1 _ x2) = "JSExportSpecifierAs (" ++ ss x1 ++ "," ++ ss x2 ++ ")" + instance ShowStripped JSTryCatch where ss (JSCatch _ _lb x1 _rb x3) = "JSCatch (" ++ ss x1 ++ "," ++ ss x3 ++ ")" ss (JSCatchIf _ _lb x1 _ ex _rb x3) = "JSCatch (" ++ ss x1 ++ ") if " ++ ss ex ++ " (" ++ ss x3 ++ ")" @@ -342,6 +365,7 @@ instance ShowStripped JSSwitchParts where instance ShowStripped JSBinOp where ss (JSBinOpAnd _) = "'&&'" + ss (JSBinOpAs _) = "'as'" ss (JSBinOpBitAnd _) = "'&'" ss (JSBinOpBitOr _) = "'|'" ss (JSBinOpBitXor _) = "'^'" @@ -437,6 +461,7 @@ commaIf xs = ',' : xs deAnnot :: JSBinOp -> JSBinOp deAnnot (JSBinOpAnd _) = JSBinOpAnd JSNoAnnot +deAnnot (JSBinOpAs _) = JSBinOpAs JSNoAnnot deAnnot (JSBinOpBitAnd _) = JSBinOpBitAnd JSNoAnnot deAnnot (JSBinOpBitOr _) = JSBinOpBitOr JSNoAnnot deAnnot (JSBinOpBitXor _) = JSBinOpBitXor JSNoAnnot diff --git a/src/Language/JavaScript/Parser/Grammar7.y b/src/Language/JavaScript/Parser/Grammar7.y index 1d4b2b9..321a6ae 100644 --- a/src/Language/JavaScript/Parser/Grammar7.y +++ b/src/Language/JavaScript/Parser/Grammar7.y @@ -19,7 +19,7 @@ import qualified Language.JavaScript.Parser.AST as AST -- The name of the generated function to be exported from the module %name parseProgram Program %name parseLiteral LiteralMain -%name parseExpression ExpressionMain +%name parseExpression ExpressionMain %name parseStatement StatementMain %tokentype { Token } @@ -81,6 +81,7 @@ import qualified Language.JavaScript.Parser.AST as AST '(' { LeftParenToken {} } ')' { RightParenToken {} } + 'as' { AsToken {} } 'autosemi' { AutoSemiToken {} } 'break' { BreakToken {} } 'case' { CaseToken {} } @@ -93,6 +94,7 @@ import qualified Language.JavaScript.Parser.AST as AST 'do' { DoToken {} } 'else' { ElseToken {} } 'enum' { EnumToken {} } + 'export' { ExportToken {} } 'false' { FalseToken {} } 'finally' { FinallyToken {} } 'for' { ForToken {} } @@ -244,6 +246,9 @@ Ge : '>=' { AST.JSBinOpGe (mkJSAnnot $1) } Gt :: { AST.JSBinOp } Gt : '>' { AST.JSBinOpGt (mkJSAnnot $1) } +As :: { AST.JSBinOp } +As : 'as' { AST.JSBinOpAs (mkJSAnnot $1) } + In :: { AST.JSBinOp } In : 'in' { AST.JSBinOpIn (mkJSAnnot $1) } @@ -305,6 +310,9 @@ Let : 'let' { mkJSAnnot $1 } Const :: { AST.JSAnnot } Const : 'const' { mkJSAnnot $1 } +Export :: { AST.JSAnnot } +Export : 'export' { mkJSAnnot $1 } + If :: { AST.JSAnnot } If : 'if' { mkJSAnnot $1 } @@ -426,6 +434,7 @@ Identifier : 'ident' { AST.JSIdentifier (mkJSAnnot $1) (tokenLiteral $1) } -- TODO: make this include any reserved word too, including future ones IdentifierName :: { AST.JSExpression } IdentifierName : Identifier {$1} + | 'as' { AST.JSIdentifier (mkJSAnnot $1) "as" } | 'break' { AST.JSIdentifier (mkJSAnnot $1) "break" } | 'case' { AST.JSIdentifier (mkJSAnnot $1) "case" } | 'catch' { AST.JSIdentifier (mkJSAnnot $1) "catch" } @@ -437,6 +446,7 @@ IdentifierName : Identifier {$1} | 'do' { AST.JSIdentifier (mkJSAnnot $1) "do" } | 'else' { AST.JSIdentifier (mkJSAnnot $1) "else" } | 'enum' { AST.JSIdentifier (mkJSAnnot $1) "enum" } + | 'export' { AST.JSIdentifier (mkJSAnnot $1) "export" } | 'false' { AST.JSIdentifier (mkJSAnnot $1) "false" } | 'finally' { AST.JSIdentifier (mkJSAnnot $1) "finally" } | 'for' { AST.JSIdentifier (mkJSAnnot $1) "for" } @@ -498,9 +508,9 @@ Elision : Comma { [AST.JSArrayComma $1] {- 'Elision1' -} } -- { PropertyNameAndValueList } -- { PropertyNameAndValueList , } ObjectLiteral :: { AST.JSExpression } -ObjectLiteral : LBrace RBrace { AST.JSObjectLiteral $1 (AST.JSCTLNone AST.JSLNil) $2 {- 'ObjectLiteal1' -} } - | LBrace PropertyNameandValueList RBrace { AST.JSObjectLiteral $1 (AST.JSCTLNone $2) $3 {- 'ObjectLiteal2' -} } - | LBrace PropertyNameandValueList Comma RBrace { AST.JSObjectLiteral $1 (AST.JSCTLComma $2 $3) $4 {- 'ObjectLiteal3' -} } +ObjectLiteral : LBrace RBrace { AST.JSObjectLiteral $1 (AST.JSCTLNone AST.JSLNil) $2 {- 'ObjectLiteral1' -} } + | LBrace PropertyNameandValueList RBrace { AST.JSObjectLiteral $1 (AST.JSCTLNone $2) $3 {- 'ObjectLiteral2' -} } + | LBrace PropertyNameandValueList Comma RBrace { AST.JSObjectLiteral $1 (AST.JSCTLComma $2 $3) $4 {- 'ObjectLiteral3' -} } -- ::= ':' -- | ',' ':' @@ -887,6 +897,7 @@ StatementNoEmpty : StatementBlock { $1 {- 'StatementNoEmpty1' -} } | ThrowStatement { $1 {- 'StatementNoEmpty13' -} } | TryStatement { $1 {- 'StatementNoEmpty14' -} } | DebuggerStatement { $1 {- 'StatementNoEmpty15' -} } + | ExportDeclaration { $1 {- 'StatementNoEmpty16' -} } StatementBlock :: { AST.JSStatement } @@ -1145,6 +1156,49 @@ Program :: { AST.JSAST } Program : StatementList Eof { AST.JSAstProgram $1 $2 {- 'Program1' -} } | Eof { AST.JSAstProgram [] $1 {- 'Program2' -} } +-- ExportDeclaration : See 15.2.3 +-- [ ] export * FromClause ; +-- [ ] export ExportClause FromClause ; +-- [x] export ExportClause ; +-- [x] export VariableStatement +-- [ ] export Declaration +-- [ ] export default HoistableDeclaration[Default] +-- [ ] export default ClassDeclaration[Default] +-- [ ] export default [lookahead ∉ { function, class }] AssignmentExpression[In] ; +ExportDeclaration :: { AST.JSStatement } +ExportDeclaration : Export ExportClause AutoSemi + { AST.JSExport $1 $2 $3 {- 'ExportDeclaration1' -} } + | Export VariableStatement AutoSemi + { AST.JSExport $1 (AST.JSExportStatement $2) $3 {- 'ExportDeclaration2' -} } + +-- ExportClause : +-- { } +-- { ExportsList } +-- { ExportsList , } +ExportClause :: { AST.JSExportBody } +ExportClause : LBrace RBrace + { AST.JSExportClause $1 Nothing $2 {- 'ExportClause1' -} } + | LBrace ExportsList RBrace + { AST.JSExportClause $1 (Just $2) $3 {- 'ExportClause2' -} } + +-- ExportsList : +-- ExportSpecifier +-- ExportsList , ExportSpecifier +ExportsList :: { AST.JSCommaList AST.JSExportSpecifier } +ExportsList : ExportSpecifier + { AST.JSLOne $1 {- 'ExportsList1' -} } + | ExportsList Comma ExportSpecifier + { AST.JSLCons $1 $2 $3 {- 'ExportsList2' -} } + +-- ExportSpecifier : +-- IdentifierName +-- IdentifierName as IdentifierName +ExportSpecifier :: { AST.JSExportSpecifier } +ExportSpecifier : IdentifierName + { AST.JSExportSpecifier (identName $1) {- 'ExportSpecifier1' -} } + | IdentifierName As IdentifierName + { AST.JSExportSpecifierAs (identName $1) $2 (identName $3) {- 'ExportSpecifier2' -} } + -- For debugging/other entry points LiteralMain :: { AST.JSAST } LiteralMain : Literal Eof { AST.JSAstLiteral $1 $2 {- 'LiteralMain' -} } @@ -1155,7 +1209,6 @@ ExpressionMain : Expression Eof { AST.JSAstExpression $1 $2 {- 'ExpressionMa StatementMain :: { AST.JSAST } StatementMain : StatementNoEmpty Eof { AST.JSAstStatement $1 $2 {- 'StatementMain' -} } - { -- Need this type while build the AST, but is not actually part of the AST. diff --git a/src/Language/JavaScript/Parser/Lexer.x b/src/Language/JavaScript/Parser/Lexer.x index 723c21b..863fabd 100644 --- a/src/Language/JavaScript/Parser/Lexer.x +++ b/src/Language/JavaScript/Parser/Lexer.x @@ -503,7 +503,8 @@ keywords = Map.fromList keywordNames keywordNames :: [(String, TokenPosn -> String -> [CommentAnnotation] -> Token)] keywordNames = - [ ( "break", BreakToken ) + [ ( "as", AsToken ) + , ( "break", BreakToken ) , ( "case", CaseToken ) , ( "catch", CatchToken ) @@ -517,6 +518,7 @@ keywordNames = , ( "else", ElseToken ) , ( "enum", EnumToken ) -- not a keyword, nominally a future reserved word, but actually in use + , ( "export", ExportToken ) , ( "false", FalseToken ) -- boolean literal @@ -563,7 +565,6 @@ keywordNames = -- ( "code", FutureToken ) **** not any more -- ( "const", FutureToken ) **** an actual token, used in productions -- enum **** an actual token, used in productions - , ( "export", FutureToken ) , ( "extends", FutureToken ) , ( "import", FutureToken ) diff --git a/src/Language/JavaScript/Parser/Token.hs b/src/Language/JavaScript/Parser/Token.hs index b2f7c4d..4a5a97c 100644 --- a/src/Language/JavaScript/Parser/Token.hs +++ b/src/Language/JavaScript/Parser/Token.hs @@ -56,6 +56,7 @@ data Token -- ^ Literal: Regular Expression -- Keywords + | AsToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } | BreakToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } | CaseToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } | CatchToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } @@ -88,6 +89,7 @@ data Token | VoidToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } | WhileToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } | WithToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } + | ExportToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } -- Future reserved words | FutureToken { tokenSpan :: !TokenPosn, tokenLiteral :: !String, tokenComment :: ![CommentAnnotation] } -- Needed, not sure what they are though. diff --git a/src/Language/JavaScript/Pretty/Printer.hs b/src/Language/JavaScript/Pretty/Printer.hs index fd9dc89..33a23a5 100644 --- a/src/Language/JavaScript/Pretty/Printer.hs +++ b/src/Language/JavaScript/Pretty/Printer.hs @@ -138,6 +138,7 @@ instance RenderJS [JSExpression] where instance RenderJS JSBinOp where (|>) pacc (JSBinOpAnd annot) = pacc |> annot |> "&&" + (|>) pacc (JSBinOpAs annot) = pacc |> annot |> "as" (|>) pacc (JSBinOpBitAnd annot) = pacc |> annot |> "&" (|>) pacc (JSBinOpBitOr annot) = pacc |> annot |> "|" (|>) pacc (JSBinOpBitXor annot) = pacc |> annot |> "^" @@ -238,6 +239,7 @@ instance RenderJS JSStatement where (|>) pacc (JSVariable annot xs s) = pacc |> annot |> "var" |> xs |> s (|>) pacc (JSWhile annot alp x1 arp x2) = pacc |> annot |> "while" |> alp |> "(" |> x1 |> arp |> ")" |> x2 (|>) pacc (JSWith annot alp x1 arp x s) = pacc |> annot |> "with" |> alp |> "(" |> x1 |> arp |> ")" |> x |> s + (|>) pacc (JSExport annot b s) = pacc |> annot |> "export" |> b |> s instance RenderJS [JSStatement] where (|>) = foldl' (|>) @@ -265,6 +267,15 @@ instance RenderJS JSArrayElement where instance RenderJS [JSArrayElement] where (|>) = foldl' (|>) +instance RenderJS JSExportBody where + (|>) pacc (JSExportStatement s) = pacc |> " " |> s + (|>) pacc (JSExportClause alb Nothing arb) = pacc |> alb |> "{}" |> arb + (|>) pacc (JSExportClause alb (Just s) arb) = pacc |> alb |> "{" |> s |> "}" |> arb + +instance RenderJS JSExportSpecifier where + (|>) pacc (JSExportSpecifier i) = pacc |> i + (|>) pacc (JSExportSpecifierAs x1 as x2) = pacc |> x1 |> as |> x2 + instance RenderJS a => RenderJS (JSCommaList a) where (|>) pacc (JSLCons pl a i) = pacc |> pl |> a |> "," |> i (|>) pacc (JSLOne i) = pacc |> i @@ -287,4 +298,3 @@ instance RenderJS JSVarInitializer where (|>) pacc JSVarInitNone = pacc -- EOF - diff --git a/src/Language/JavaScript/Process/Minify.hs b/src/Language/JavaScript/Process/Minify.hs index f4babe3..cb82e05 100644 --- a/src/Language/JavaScript/Process/Minify.hs +++ b/src/Language/JavaScript/Process/Minify.hs @@ -64,6 +64,7 @@ fixStmt a _ (JSTry _ b tc tf) = JSTry a (fixEmpty b) (map fixEmpty tc) (fixEmpty fixStmt a s (JSVariable _ ss _) = JSVariable a (fixVarList ss) s fixStmt a s (JSWhile _ _ e _ st) = JSWhile a emptyAnnot (fixEmpty e) emptyAnnot (fixStmt a s st) fixStmt a s (JSWith _ _ e _ st _) = JSWith a emptyAnnot (fixEmpty e) emptyAnnot (fixStmtE noSemi st) s +fixStmt a s (JSExport _ b _) = JSExport a (fixEmpty b) s fixIfElseBlock :: JSAnnot -> JSSemi -> JSStatement -> JSStatement @@ -209,6 +210,7 @@ normalizeToSQ str = instance MinifyJS JSBinOp where fix _ (JSBinOpAnd _) = JSBinOpAnd emptyAnnot + fix a (JSBinOpAs _) = JSBinOpAs a fix _ (JSBinOpBitAnd _) = JSBinOpBitAnd emptyAnnot fix _ (JSBinOpBitOr _) = JSBinOpBitOr emptyAnnot fix _ (JSBinOpBitXor _) = JSBinOpBitXor emptyAnnot @@ -265,6 +267,13 @@ instance MinifyJS JSAssignOp where fix a (JSBwXorAssign _) = JSBwXorAssign a fix a (JSBwOrAssign _) = JSBwOrAssign a +instance MinifyJS JSExportBody where + fix a (JSExportStatement s) = JSExportStatement (fixStmt a noSemi s) + fix _ (JSExportClause _ x1 _) = JSExportClause emptyAnnot (fixEmpty <$> x1) emptyAnnot + +instance MinifyJS JSExportSpecifier where + fix _ (JSExportSpecifier x1) = JSExportSpecifier (fixEmpty x1) + fix _ (JSExportSpecifierAs x1 as x2) = JSExportSpecifierAs (fixEmpty x1) (fixSpace as) (fixSpace x2) instance MinifyJS JSTryCatch where fix a (JSCatch _ _ x1 _ x3) = JSCatch a emptyAnnot (fixEmpty x1) emptyAnnot (fixEmpty x3) diff --git a/test/Test/Language/Javascript/Minify.hs b/test/Test/Language/Javascript/Minify.hs index 2e63b7e..136fe82 100644 --- a/test/Test/Language/Javascript/Minify.hs +++ b/test/Test/Language/Javascript/Minify.hs @@ -148,6 +148,13 @@ testMinifyStmt = describe "Minify statements:" $ do minifyStmt " { ; e = 1 } " `shouldBe` "e=1" minifyStmt " { { } ; f = 1 ; { } ; } ; " `shouldBe` "f=1" + it "export" $ do + minifyStmt " export { } ; " `shouldBe` "export{}" + minifyStmt " export { a } ; " `shouldBe` "export{a}" + minifyStmt " export { a, b } ; " `shouldBe` "export{a,b}" + minifyStmt " export { a, b as c , d } ; " `shouldBe` "export{a,b as c,d}" + minifyStmt " export const a = 1 ; " `shouldBe` "export const a=1" + it "if" $ do minifyStmt " if ( 1 ) return ; " `shouldBe` "if(1)return" minifyStmt " if ( 1 ) ; " `shouldBe` "if(1);" diff --git a/test/Test/Language/Javascript/RoundTrip.hs b/test/Test/Language/Javascript/RoundTrip.hs index b55efb0..f4135f5 100644 --- a/test/Test/Language/Javascript/RoundTrip.hs +++ b/test/Test/Language/Javascript/RoundTrip.hs @@ -98,8 +98,10 @@ testRoundTrip = describe "Roundtrip:" $ do testRT "switch (x) {default:break;}" testRT "switch (x) {default:\ncase 1:break;}" testRT "var x=1;let y=2;" + -- modules + testRT "export {};" + testRT "export { a, X as B, c}" testRT :: String -> Expectation -testRT str = str `shouldBe` renderToString (readJs str) - +testRT str = renderToString (readJs str) `shouldBe` str diff --git a/test/Test/Language/Javascript/StatementParser.hs b/test/Test/Language/Javascript/StatementParser.hs index e27addf..85cc511 100644 --- a/test/Test/Language/Javascript/StatementParser.hs +++ b/test/Test/Language/Javascript/StatementParser.hs @@ -16,6 +16,14 @@ testStatementParser = describe "Parse statements:" $ do testStmt "x" `shouldBe` "Right (JSAstStatement (JSIdentifier 'x'))" testStmt "null" `shouldBe` "Right (JSAstStatement (JSLiteral 'null'))" testStmt "true?1:2" `shouldBe` "Right (JSAstStatement (JSExpressionTernary (JSLiteral 'true',JSDecimal '1',JSDecimal '2')))" + + it "export" $ do + testStmt "export {}" `shouldBe` "Right (JSAstStatement (JSExport (JSExportClause ())))" + testStmt "export {};" `shouldBe` "Right (JSAstStatement (JSExport (JSExportClause ())))" + testStmt "export const a = 1;" `shouldBe` "Right (JSAstStatement (JSExport (JSExportStatement (JSConstant (JSVarInitExpression (JSIdentifier 'a') [JSDecimal '1'])))))" + testStmt "export { a };" `shouldBe` "Right (JSAstStatement (JSExport (JSExportClause ((JSExportSpecifier (JSIdentifier 'a'))))))" + testStmt "export { X as Y };" `shouldBe` "Right (JSAstStatement (JSExport (JSExportClause ((JSExportSpecifierAs (JSIdentifier 'X',JSIdentifier 'Y'))))))" + it "block" $ do testStmt "{}" `shouldBe` "Right (JSAstStatement (JSStatementBlock []))" testStmt "{x=1}" `shouldBe` "Right (JSAstStatement (JSStatementBlock [JSOpAssign ('=',JSIdentifier 'x',JSDecimal '1')]))" @@ -101,4 +109,3 @@ testStatementParser = describe "Parse statements:" $ do testStmt :: String -> String testStmt str = showStrippedMaybe (parseUsing parseStatement str "src") - diff --git a/test/testsuite.hs b/test/testsuite.hs index 7ba14d2..8f6ccba 100644 --- a/test/testsuite.hs +++ b/test/testsuite.hs @@ -5,9 +5,9 @@ import Test.Hspec import Test.Hspec.Runner +import Test.Language.Javascript.ExpressionParser import Test.Language.Javascript.Lexer import Test.Language.Javascript.LiteralParser -import Test.Language.Javascript.ExpressionParser import Test.Language.Javascript.Minify import Test.Language.Javascript.ProgramParser import Test.Language.Javascript.RoundTrip @@ -33,4 +33,3 @@ testAll = do testMinifyExpr testMinifyStmt testMinifyProg -