Skip to content

Parse ES2015 (aka ES6) export Declarations (partial support) #79

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@
/unicode/uc-nl.htm
/unicode/uc-pc.htm
cabal.sandbox.config

# stack
/.stack-work/
2 changes: 0 additions & 2 deletions src/Language/JavaScript/Parser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,3 @@ import Language.JavaScript.Parser.SrcLocation
import Language.JavaScript.Pretty.Printer

-- EOF


31 changes: 28 additions & 3 deletions src/Language/JavaScript/Parser/AST.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ module Language.JavaScript.Parser.AST
, JSTryCatch (..)
, JSTryFinally (..)
, JSStatement (..)
, JSExportBody (..)
, JSExportSpecifier (..)
, JSBlock (..)
, JSSwitchParts (..)
, JSAST (..)
Expand Down Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is introducing JSExportBody to support the following variants the right abstraction?

-- 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] ;

Q: What makes for the right AST abstraction?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. You decide :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decided to revisit this a bit and opened #80. Let me know what you think.

deriving (Data, Eq, Show, Typeable)

data JSExpression
Expand Down Expand Up @@ -112,6 +115,7 @@ data JSExpression

data JSBinOp
= JSBinOpAnd !JSAnnot
| JSBinOpAs !JSAnnot
| JSBinOpBitAnd !JSAnnot
| JSBinOpBitOr !JSAnnot
| JSBinOpBitXor !JSAnnot
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ++ ")"
Expand Down Expand Up @@ -342,6 +365,7 @@ instance ShowStripped JSSwitchParts where

instance ShowStripped JSBinOp where
ss (JSBinOpAnd _) = "'&&'"
ss (JSBinOpAs _) = "'as'"
ss (JSBinOpBitAnd _) = "'&'"
ss (JSBinOpBitOr _) = "'|'"
ss (JSBinOpBitXor _) = "'^'"
Expand Down Expand Up @@ -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
Expand Down
55 changes: 50 additions & 5 deletions src/Language/JavaScript/Parser/Grammar7.y
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -81,6 +81,7 @@ import qualified Language.JavaScript.Parser.AST as AST
'(' { LeftParenToken {} }
')' { RightParenToken {} }

'as' { AsToken {} }
'autosemi' { AutoSemiToken {} }
'break' { BreakToken {} }
'case' { CaseToken {} }
Expand All @@ -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 {} }
Expand Down Expand Up @@ -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) }

Expand Down Expand Up @@ -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 }

Expand Down Expand Up @@ -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" }
Expand All @@ -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" }
Expand Down Expand Up @@ -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' -} }

-- <Property Name and Value List> ::= <Property Name> ':' <Assignment Expression>
-- | <Property Name and Value List> ',' <Property Name> ':' <Assignment Expression>
Expand Down Expand Up @@ -887,6 +897,7 @@ StatementNoEmpty : StatementBlock { $1 {- 'StatementNoEmpty1' -} }
| ThrowStatement { $1 {- 'StatementNoEmpty13' -} }
| TryStatement { $1 {- 'StatementNoEmpty14' -} }
| DebuggerStatement { $1 {- 'StatementNoEmpty15' -} }
| ExportDeclaration { $1 {- 'StatementNoEmpty16' -} }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn’t align with the ECMAScript 2015 spec but it seems the approach that #64 has taken:
https://github.com/erikd/language-javascript/pull/64/files#diff-843804451967a2b03f95fbef01a330d1R905

My first attempt created a separate JSDeclaration data type 9330863 but then I reverted to align with the existing code.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My maintenance of this has focused more on parsing real world JS rather than following the spec. This is fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying. If not following the spec, how do you decide whether a program should pass or fail parsing? I thought most modern JS engines follow the spec but as I said, I don’t have a lot of background in this space.



StatementBlock :: { AST.JSStatement }
Expand Down Expand Up @@ -1145,6 +1156,41 @@ 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' -} }
Expand All @@ -1155,7 +1201,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.
Expand Down
5 changes: 3 additions & 2 deletions src/Language/JavaScript/Parser/Lexer.x
Original file line number Diff line number Diff line change
Expand Up @@ -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 )

Expand All @@ -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

Expand Down Expand Up @@ -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 )
Expand Down
2 changes: 2 additions & 0 deletions src/Language/JavaScript/Parser/Token.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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] }
Expand Down Expand Up @@ -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.
Expand Down
12 changes: 11 additions & 1 deletion src/Language/JavaScript/Pretty/Printer.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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 |> "^"
Expand Down Expand Up @@ -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' (|>)
Expand Down Expand Up @@ -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
Expand All @@ -287,4 +298,3 @@ instance RenderJS JSVarInitializer where
(|>) pacc JSVarInitNone = pacc

-- EOF

9 changes: 9 additions & 0 deletions src/Language/JavaScript/Process/Minify.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions test/Test/Language/Javascript/Minify.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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);"
Expand Down
6 changes: 4 additions & 2 deletions test/Test/Language/Javascript/RoundTrip.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading