content: {-# LANGUAGE CPP, FlexibleInstances #-}
  
  module Language.JavaScript.Process.Minify
      ( -- * Minify
        minifyJS
      ) where
  
  #if ! MIN_VERSION_base(4,13,0)
  import Control.Applicative ((<$>))
  #endif
  
  import Language.JavaScript.Parser.AST
  import Language.JavaScript.Parser.SrcLocation
  import Language.JavaScript.Parser.Token
  
  -- ---------------------------------------------------------------------
  
  minifyJS :: JSAST -> JSAST
  minifyJS (JSAstProgram xs _) = JSAstProgram (fixStatementList noSemi xs) emptyAnnot
  minifyJS (JSAstModule xs _) = JSAstModule (map (fix emptyAnnot) xs) emptyAnnot
  minifyJS (JSAstStatement (JSStatementBlock _ [s] _ _) _) = JSAstStatement (fixStmtE noSemi s) emptyAnnot
  minifyJS (JSAstStatement s _) = JSAstStatement (fixStmtE noSemi s) emptyAnnot
  minifyJS (JSAstExpression e _) =  JSAstExpression (fixEmpty e) emptyAnnot
  minifyJS (JSAstLiteral s _)  = JSAstLiteral (fixEmpty s) emptyAnnot
  
  -- ---------------------------------------------------------------------
  
  class MinifyJS a where
      fix :: JSAnnot -> a -> a
  
  
  fixEmpty :: MinifyJS a => a -> a
  fixEmpty = fix emptyAnnot
  
  fixSpace :: MinifyJS a => a -> a
  fixSpace = fix spaceAnnot
  
  -- -----------------------------------------------------------------------------
  -- During minification, Javascript statements may need to have explicit
  -- semicolons inserted between them, so that simply adding a JSStatement
  -- instance for the MinifyJS typeclass would not be sufficient.
  
  fixStmt :: JSAnnot -> JSSemi -> JSStatement -> JSStatement
  fixStmt a s (JSStatementBlock _lb ss _rb _) = fixStatementBlock a s ss
  fixStmt a s (JSBreak _ i _) = JSBreak a (fixSpace i) s
  fixStmt a s (JSClass _ n h _ ms _ _) = JSClass a (fixSpace n) (fixSpace h) emptyAnnot (fixEmpty ms) emptyAnnot s
  fixStmt a s (JSConstant _ ss _) = JSConstant a (fixVarList ss) s
  fixStmt a s (JSContinue _ i _) = JSContinue a (fixSpace i) s
  fixStmt a s (JSDoWhile _ st _ _ e _ _) = JSDoWhile a (mkStatementBlock noSemi st) emptyAnnot emptyAnnot (fixEmpty e) emptyAnnot s
  fixStmt a s (JSFor _ _ el1 _ el2 _ el3 _ st) = JSFor a emptyAnnot (fixEmpty el1) emptyAnnot (fixEmpty el2) emptyAnnot (fixEmpty el3) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForIn _ _ e1 op e2 _ st) = JSForIn a emptyAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForVar _ _ _ el1 _ el2 _ el3 _ st) = JSForVar a emptyAnnot spaceAnnot (fixEmpty el1) emptyAnnot (fixEmpty el2) emptyAnnot (fixEmpty el3) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForVarIn _ _ _ e1 op e2 _ st) = JSForVarIn a emptyAnnot spaceAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForLet _ _ _ el1 _ el2 _ el3 _ st) = JSForLet a emptyAnnot spaceAnnot (fixEmpty el1) emptyAnnot (fixEmpty el2) emptyAnnot (fixEmpty el3) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForLetIn _ _ _ e1 op e2 _ st) = JSForLetIn a emptyAnnot spaceAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForLetOf _ _ _ e1 op e2 _ st) = JSForLetOf a emptyAnnot spaceAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForConst _ _ _ el1 _ el2 _ el3 _ st) = JSForConst a emptyAnnot spaceAnnot (fixEmpty el1) emptyAnnot (fixEmpty el2) emptyAnnot (fixEmpty el3) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForConstIn _ _ _ e1 op e2 _ st) = JSForConstIn a emptyAnnot spaceAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForConstOf _ _ _ e1 op e2 _ st) = JSForConstOf a emptyAnnot spaceAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForOf _ _ e1 op e2 _ st) = JSForOf a emptyAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSForVarOf _ _ _ e1 op e2 _ st) = JSForVarOf a emptyAnnot spaceAnnot (fixEmpty e1) (fixSpace op) (fixSpace e2) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSAsyncFunction _ _ n _ ps _ blk _) = JSAsyncFunction a spaceAnnot (fixSpace n) emptyAnnot (fixEmpty ps) emptyAnnot (fixEmpty blk) s
  fixStmt a s (JSFunction _ n _ ps _ blk _) = JSFunction a (fixSpace n) emptyAnnot (fixEmpty ps) emptyAnnot (fixEmpty blk) s
  fixStmt a s (JSGenerator _ _ n _ ps _ blk _) = JSGenerator a emptyAnnot (fixEmpty n) emptyAnnot (fixEmpty ps) emptyAnnot (fixEmpty blk) s
  fixStmt a s (JSIf _ _ e _ st) = JSIf a emptyAnnot (fixEmpty e) emptyAnnot (fixIfElseBlock emptyAnnot s st)
  fixStmt a s (JSIfElse _ _ e _ (JSEmptyStatement _) _ sf) = JSIfElse a emptyAnnot (fixEmpty e) emptyAnnot (JSEmptyStatement emptyAnnot) emptyAnnot (fixStmt spaceAnnot s sf)
  fixStmt a s (JSIfElse _ _ e _ st _ sf) = JSIfElse a emptyAnnot (fixEmpty e) emptyAnnot (mkStatementBlock noSemi st) emptyAnnot (fixIfElseBlock spaceAnnot s sf)
  fixStmt a s (JSLabelled e _ st) = JSLabelled (fix a e) emptyAnnot (fixStmtE s st)
  fixStmt a s (JSLet _ xs _) = JSLet a (fixVarList xs) s
  fixStmt _ _ (JSEmptyStatement _) = JSEmptyStatement emptyAnnot
  fixStmt a s (JSExpressionStatement e _) = JSExpressionStatement (fix a e) s
  fixStmt a s (JSAssignStatement lhs op rhs _) = JSAssignStatement (fix a lhs) (fixEmpty op) (fixEmpty rhs) s
  fixStmt a s (JSMethodCall e _ args _ _) = JSMethodCall (fix a e) emptyAnnot (fixEmpty args) emptyAnnot s
  fixStmt a s (JSReturn _ me _) = JSReturn a (fixSpace me) s
  fixStmt a s (JSSwitch _ _ e _ _ sps _ _) = JSSwitch a emptyAnnot (fixEmpty e) emptyAnnot emptyAnnot (fixSwitchParts sps) emptyAnnot s
  fixStmt a s (JSThrow _ e _) = JSThrow a (fixSpace e) s
  fixStmt a _ (JSTry _ b tc tf) = JSTry a (fixEmpty b) (map fixEmpty tc) (fixEmpty tf)
  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
  
  
  fixIfElseBlock :: JSAnnot -> JSSemi -> JSStatement -> JSStatement
  fixIfElseBlock _ _ (JSStatementBlock _ [] _ _) = JSEmptyStatement emptyAnnot
  fixIfElseBlock a s st = fixStmt a s st
  
  fixStmtE :: JSSemi -> JSStatement -> JSStatement
  fixStmtE = fixStmt emptyAnnot
  
  -- Turn a single JSStatement into a JSStatementBlock.
  mkStatementBlock :: JSSemi -> JSStatement -> JSStatement
  mkStatementBlock s (JSStatementBlock _ blk _ _) = JSStatementBlock emptyAnnot (fixStatementList noSemi blk) emptyAnnot s
  mkStatementBlock s x = JSStatementBlock emptyAnnot [fixStmtE noSemi x] emptyAnnot s
  
  -- Filter a list of JSStatment, dropping JSEmptyStatement and empty
  -- JSStatementBlocks. If the resulting list contains only a single element,
  -- remove the enclosing JSStatementBlock and return the inner JSStatement.
  fixStatementBlock :: JSAnnot -> JSSemi -> [JSStatement] -> JSStatement
  fixStatementBlock a s ss =
      case filter (not . isEmpty) ss of
          [] -> JSStatementBlock emptyAnnot [] emptyAnnot s
          [sx] -> fixStmt a s sx
          sss -> JSStatementBlock emptyAnnot (fixStatementList noSemi sss) emptyAnnot s
    where
      isEmpty (JSEmptyStatement _) = True
      isEmpty (JSStatementBlock _ [] _ _) = True
      isEmpty _ = False
  
  -- Force semi-colons between statements, and make sure the last statement in a
  -- block has no semi-colon.
  fixStatementList :: JSSemi -> [JSStatement] -> [JSStatement]
  fixStatementList trailingSemi =
      fixList emptyAnnot trailingSemi . filter (not . isRedundant)
    where
      isRedundant (JSStatementBlock _ [] _ _) = True
      isRedundant (JSEmptyStatement _) = True
      isRedundant _ = False
  
      fixList _ _ [] = []
      fixList a s [JSStatementBlock _ blk _ _] = fixList a s blk
      fixList a s [x] = [fixStmt a s x]
      fixList _ s (JSStatementBlock _ blk _ _:xs) = fixList emptyAnnot semi (filter (not . isRedundant) blk) ++ fixList emptyAnnot s xs
      fixList a s (JSConstant _ vs1 _:JSConstant _ vs2 _: xs) = fixList a s (JSConstant spaceAnnot (concatCommaList vs1 vs2) s : xs)
      fixList a s (JSVariable _ vs1 _:JSVariable _ vs2 _: xs) = fixList a s (JSVariable spaceAnnot (concatCommaList vs1 vs2) s : xs)
      fixList a s (x1@JSFunction{}:x2@JSFunction{}:xs) = fixStmt a noSemi x1 : fixList newlineAnnot s (x2:xs)
      fixList a s (x:xs) = fixStmt a semi x : fixList emptyAnnot s xs
  
  concatCommaList :: JSCommaList a -> JSCommaList a -> JSCommaList a
  concatCommaList xs JSLNil = xs
  concatCommaList JSLNil ys = ys
  concatCommaList xs (JSLOne y) = JSLCons xs emptyAnnot y
  concatCommaList xs ys =
      let recurse (z, zs) = concatCommaList (JSLCons xs emptyAnnot z) zs
      in  maybe xs recurse $ headCommaList ys
  
  headCommaList :: JSCommaList a -> Maybe (a, JSCommaList a)
  headCommaList JSLNil = Nothing
  headCommaList (JSLOne x) = Just (x, JSLNil)
  headCommaList (JSLCons (JSLOne x) _ y) = Just (x, JSLOne y)
  headCommaList (JSLCons xs _ y) =
      let rebuild (x, ys) = (x, JSLCons ys emptyAnnot y)
      in  rebuild <$> headCommaList xs
  
  -- -----------------------------------------------------------------------------
  -- JSExpression and the rest can use the MinifyJS typeclass.
  
  instance MinifyJS JSExpression where
      -- Terminals
      fix a (JSIdentifier     _ s) = JSIdentifier a s
      fix a (JSDecimal        _ s) = JSDecimal a s
      fix a (JSLiteral        _ s) = JSLiteral a s
      fix a (JSHexInteger     _ s) = JSHexInteger a s
      fix a (JSOctal          _ s) = JSOctal a s
      fix _ (JSStringLiteral  _ s) = JSStringLiteral emptyAnnot s
      fix _ (JSRegEx          _ s) = JSRegEx emptyAnnot s
  
      -- Non-Terminals
      fix _ (JSArrayLiteral         _ xs _)             = JSArrayLiteral emptyAnnot (map fixEmpty xs) emptyAnnot
      fix a (JSArrowExpression ps _ ss)                 = JSArrowExpression (fix a ps) emptyAnnot (fixStmt emptyAnnot noSemi ss)
      fix a (JSAssignExpression     lhs op rhs)         = JSAssignExpression (fix a lhs) (fixEmpty op) (fixEmpty rhs)
      fix a (JSAwaitExpression      _ ex)               = JSAwaitExpression a (fixSpace ex)
      fix a (JSCallExpression       ex _ xs _)          = JSCallExpression (fix a ex) emptyAnnot (fixEmpty xs) emptyAnnot
      fix a (JSCallExpressionDot    ex _ xs)            = JSCallExpressionDot (fix a ex) emptyAnnot (fixEmpty xs)
      fix a (JSCallExpressionSquare ex _ xs _)          = JSCallExpressionSquare (fix a ex) emptyAnnot (fixEmpty xs) emptyAnnot
      fix a (JSClassExpression      _ n h _ ms _)       = JSClassExpression a (fixSpace n) (fixSpace h) emptyAnnot (fixEmpty ms) emptyAnnot
      fix a (JSCommaExpression      le _ re)            = JSCommaExpression (fix a le) emptyAnnot (fixEmpty re)
      fix a (JSExpressionBinary     lhs op rhs)         = fixBinOpExpression a op lhs rhs
      fix _ (JSExpressionParen      _ e _)              = JSExpressionParen emptyAnnot (fixEmpty e) emptyAnnot
      fix a (JSExpressionPostfix    e op)               = JSExpressionPostfix (fix a e) (fixEmpty op)
      fix a (JSExpressionTernary    cond _ v1 _ v2)     = JSExpressionTernary (fix a cond) emptyAnnot (fixEmpty v1) emptyAnnot (fixEmpty v2)
      fix a (JSFunctionExpression   _ n _ x2s _ x3)     = JSFunctionExpression a (fixSpace n) emptyAnnot (fixEmpty x2s) emptyAnnot (fixEmpty x3)
      fix a (JSGeneratorExpression  _ _ n _ x2s _ x3)   = JSGeneratorExpression a emptyAnnot (fixEmpty n) emptyAnnot (fixEmpty x2s) emptyAnnot (fixEmpty x3)
      fix a (JSMemberDot            xs _ n)             = JSMemberDot (fix a xs) emptyAnnot (fixEmpty n)
      fix a (JSMemberExpression     e _ args _)         = JSMemberExpression (fix a e) emptyAnnot (fixEmpty args) emptyAnnot
      fix a (JSMemberNew            _ n _ s _)          = JSMemberNew a (fix spaceAnnot n) emptyAnnot (fixEmpty s) emptyAnnot
      fix a (JSMemberSquare         xs _ e _)           = JSMemberSquare (fix a xs) emptyAnnot (fixEmpty e) emptyAnnot
      fix a (JSNewExpression        _ e)                = JSNewExpression a (fixSpace e)
      fix _ (JSObjectLiteral        _ xs _)             = JSObjectLiteral emptyAnnot (fixEmpty xs) emptyAnnot
      fix a (JSTemplateLiteral      t _ s ps)           = JSTemplateLiteral (fmap (fix a) t) emptyAnnot s (map fixEmpty ps)
      fix a (JSUnaryExpression      op x)               = let (ta, fop) = fixUnaryOp a op in JSUnaryExpression fop (fix ta x)
      fix a (JSVarInitExpression    x1 x2)              = JSVarInitExpression (fix a x1) (fixEmpty x2)
      fix a (JSYieldExpression      _ x)                = JSYieldExpression a (fixSpace x)
      fix a (JSYieldFromExpression  _ _ x)              = JSYieldFromExpression a emptyAnnot (fixEmpty x)
      fix a (JSSpreadExpression     _ e)                = JSSpreadExpression a (fixEmpty e)
  
  instance MinifyJS JSArrowParameterList where
      fix _ (JSUnparenthesizedArrowParameter p)         = JSUnparenthesizedArrowParameter (fixEmpty p)
      fix _ (JSParenthesizedArrowParameterList _ ps _)  = JSParenthesizedArrowParameterList emptyAnnot (fixEmpty ps) emptyAnnot
  
  fixVarList :: JSCommaList JSExpression -> JSCommaList JSExpression
  fixVarList (JSLCons h _ v) = JSLCons (fixVarList h) emptyAnnot (fixEmpty v)
  fixVarList (JSLOne a) = JSLOne (fixSpace a)
  fixVarList JSLNil = JSLNil
  
  fixBinOpExpression :: JSAnnot -> JSBinOp -> JSExpression -> JSExpression -> JSExpression
  fixBinOpExpression a (JSBinOpPlus _) lhs rhs = fixBinOpPlus a lhs rhs
  fixBinOpExpression a (JSBinOpIn _) lhs rhs = JSExpressionBinary (fix a lhs) (JSBinOpIn spaceAnnot) (fix spaceAnnot rhs)
  fixBinOpExpression a (JSBinOpInstanceOf _) lhs rhs = JSExpressionBinary (fix a lhs) (JSBinOpInstanceOf spaceAnnot) (fix spaceAnnot rhs)
  fixBinOpExpression a op lhs rhs = JSExpressionBinary (fix a lhs) (fixEmpty op) (fixEmpty rhs)
  
  fixBinOpPlus :: JSAnnot -> JSExpression -> JSExpression -> JSExpression
  fixBinOpPlus a lhs rhs =
      case (fix a lhs, fixEmpty rhs) of
          (JSStringLiteral _ s1, JSStringLiteral _ s2) -> stringLitConcat (normalizeToSQ s1) (normalizeToSQ s2)
          (nlhs, nrhs) -> JSExpressionBinary nlhs (JSBinOpPlus emptyAnnot) nrhs
  
  -- Concatenate two JSStringLiterals. Since the strings will include the string
  -- terminators (either single or double quotes) we use whatever terminator is
  -- used by the first string.
  stringLitConcat :: String -> String -> JSExpression
  stringLitConcat xs [] = JSStringLiteral emptyAnnot xs
  stringLitConcat [] ys = JSStringLiteral emptyAnnot ys
  stringLitConcat xall (_:yss) =
      JSStringLiteral emptyAnnot (init xall ++ init yss ++ "'")
  
  -- Normalize a String. If its single quoted, just return it and its double quoted
  -- convert it to single quoted.
  normalizeToSQ :: String -> String
  normalizeToSQ str =
      case str of
          [] -> []
          ('\'' : _) -> str
          ('"' : xs) -> '\'' : convertSQ xs
          other -> other -- Should not happen.
    where
      convertSQ [] = []
      convertSQ [_] = "'"
      convertSQ ('\'':xs) = '\\' : '\'' : convertSQ xs
      convertSQ ('\\':'\"':xs) = '"' : convertSQ xs
      convertSQ (x:xs) = x : convertSQ xs
  
  
  instance MinifyJS JSBinOp where
      fix _ (JSBinOpAnd        _) = JSBinOpAnd emptyAnnot
      fix _ (JSBinOpBitAnd     _) = JSBinOpBitAnd emptyAnnot
      fix _ (JSBinOpBitOr      _) = JSBinOpBitOr emptyAnnot
      fix _ (JSBinOpBitXor     _) = JSBinOpBitXor emptyAnnot
      fix _ (JSBinOpDivide     _) = JSBinOpDivide emptyAnnot
      fix _ (JSBinOpEq         _) = JSBinOpEq emptyAnnot
      fix _ (JSBinOpGe         _) = JSBinOpGe emptyAnnot
      fix _ (JSBinOpGt         _) = JSBinOpGt emptyAnnot
      fix a (JSBinOpIn         _) = JSBinOpIn a
      fix a (JSBinOpInstanceOf _) = JSBinOpInstanceOf a
      fix _ (JSBinOpLe         _) = JSBinOpLe emptyAnnot
      fix _ (JSBinOpLsh        _) = JSBinOpLsh emptyAnnot
      fix _ (JSBinOpLt         _) = JSBinOpLt emptyAnnot
      fix _ (JSBinOpMinus      _) = JSBinOpMinus emptyAnnot
      fix _ (JSBinOpMod        _) = JSBinOpMod emptyAnnot
      fix _ (JSBinOpNeq        _) = JSBinOpNeq emptyAnnot
      fix a (JSBinOpOf         _) = JSBinOpOf a
      fix _ (JSBinOpOr         _) = JSBinOpOr emptyAnnot
      fix _ (JSBinOpPlus       _) = JSBinOpPlus emptyAnnot
      fix _ (JSBinOpRsh        _) = JSBinOpRsh emptyAnnot
      fix _ (JSBinOpStrictEq   _) = JSBinOpStrictEq emptyAnnot
      fix _ (JSBinOpStrictNeq  _) = JSBinOpStrictNeq emptyAnnot
      fix _ (JSBinOpTimes      _) = JSBinOpTimes emptyAnnot
      fix _ (JSBinOpUrsh       _) = JSBinOpUrsh emptyAnnot
  
  
  instance MinifyJS JSUnaryOp where
      fix _ (JSUnaryOpDecr   _) = JSUnaryOpDecr emptyAnnot
      fix _ (JSUnaryOpDelete _) = JSUnaryOpDelete emptyAnnot
      fix _ (JSUnaryOpIncr   _) = JSUnaryOpIncr emptyAnnot
      fix _ (JSUnaryOpMinus  _) = JSUnaryOpMinus emptyAnnot
      fix _ (JSUnaryOpNot    _) = JSUnaryOpNot emptyAnnot
      fix _ (JSUnaryOpPlus   _) = JSUnaryOpPlus emptyAnnot
      fix _ (JSUnaryOpTilde  _) = JSUnaryOpTilde emptyAnnot
      fix _ (JSUnaryOpTypeof _) = JSUnaryOpTypeof emptyAnnot
      fix _ (JSUnaryOpVoid   _) = JSUnaryOpVoid emptyAnnot
  
  fixUnaryOp :: JSAnnot -> JSUnaryOp -> (JSAnnot, JSUnaryOp)
  fixUnaryOp a (JSUnaryOpDelete _) = (spaceAnnot, JSUnaryOpDelete a)
  fixUnaryOp a (JSUnaryOpTypeof _) = (spaceAnnot, JSUnaryOpTypeof a)
  fixUnaryOp a (JSUnaryOpVoid   _) = (spaceAnnot, JSUnaryOpVoid a)
  fixUnaryOp a x = (emptyAnnot, fix a x)
  
  
  instance MinifyJS JSAssignOp where
      fix a (JSAssign       _) = JSAssign a
      fix a (JSTimesAssign  _) = JSTimesAssign a
      fix a (JSDivideAssign _) = JSDivideAssign a
      fix a (JSModAssign    _) = JSModAssign a
      fix a (JSPlusAssign   _) = JSPlusAssign a
      fix a (JSMinusAssign  _) = JSMinusAssign a
      fix a (JSLshAssign    _) = JSLshAssign a
      fix a (JSRshAssign    _) = JSRshAssign a
      fix a (JSUrshAssign   _) = JSUrshAssign a
      fix a (JSBwAndAssign  _) = JSBwAndAssign a
      fix a (JSBwXorAssign  _) = JSBwXorAssign a
      fix a (JSBwOrAssign   _) = JSBwOrAssign a
  
  instance MinifyJS JSModuleItem where
      fix _ (JSModuleImportDeclaration _ x1) = JSModuleImportDeclaration emptyAnnot (fixEmpty x1)
      fix _ (JSModuleExportDeclaration _ x1) = JSModuleExportDeclaration emptyAnnot (fixEmpty x1)
      fix a (JSModuleStatementListItem s) = JSModuleStatementListItem (fixStmt a noSemi s)
  
  instance MinifyJS JSImportDeclaration where
      fix _ (JSImportDeclaration imps from _) = JSImportDeclaration (fixEmpty imps) (fix annot from) noSemi
          where
          annot = case imps of
                      JSImportClauseDefault {} -> spaceAnnot
                      JSImportClauseNameSpace {} -> spaceAnnot
                      JSImportClauseNamed {} -> emptyAnnot
                      JSImportClauseDefaultNameSpace {} -> spaceAnnot
                      JSImportClauseDefaultNamed {} -> emptyAnnot
      fix a (JSImportDeclarationBare _ m _) = JSImportDeclarationBare a m noSemi
  
  instance MinifyJS JSImportClause where
      fix _ (JSImportClauseDefault n) = JSImportClauseDefault (fixSpace n)
      fix _ (JSImportClauseNameSpace ns) = JSImportClauseNameSpace (fixSpace ns)
      fix _ (JSImportClauseNamed named) = JSImportClauseNamed (fixEmpty named)
      fix _ (JSImportClauseDefaultNameSpace def _ ns) = JSImportClauseDefaultNameSpace (fixSpace def) emptyAnnot (fixEmpty ns)
      fix _ (JSImportClauseDefaultNamed def _ ns) = JSImportClauseDefaultNamed (fixSpace def) emptyAnnot (fixEmpty ns)
  
  instance MinifyJS JSFromClause where
      fix a (JSFromClause _ _ m) = JSFromClause a emptyAnnot m
  
  instance MinifyJS JSImportNameSpace where
      fix a (JSImportNameSpace _ _ ident) = JSImportNameSpace (JSBinOpTimes a) spaceAnnot (fixSpace ident)
  
  instance MinifyJS JSImportsNamed where
      fix _ (JSImportsNamed _ imps _) = JSImportsNamed emptyAnnot (fixEmpty imps) emptyAnnot
  
  instance MinifyJS JSImportSpecifier where
      fix _ (JSImportSpecifier x1) = JSImportSpecifier (fixEmpty x1)
      fix _ (JSImportSpecifierAs x1 _ x2) = JSImportSpecifierAs (fixEmpty x1) spaceAnnot (fixSpace x2)
  
  instance MinifyJS JSExportDeclaration where
      fix a (JSExportFrom x1 from _) = JSExportFrom (fix a x1) (fix a from) noSemi
      fix _ (JSExportLocals x1 _) = JSExportLocals (fix emptyAnnot x1) noSemi
      fix _ (JSExport x1 _) = JSExport (fixStmt spaceAnnot noSemi x1) noSemi
  
  instance MinifyJS JSExportClause where
      fix a (JSExportClause _ x1 _) = JSExportClause emptyAnnot (fixEmpty x1) a
  
  instance MinifyJS JSExportSpecifier where
      fix _ (JSExportSpecifier x1) = JSExportSpecifier (fixEmpty x1)
      fix _ (JSExportSpecifierAs x1 _ x2) = JSExportSpecifierAs (fixEmpty x1) spaceAnnot (fixSpace x2)
  
  instance MinifyJS JSTryCatch where
      fix a (JSCatch _ _ x1 _ x3) = JSCatch a emptyAnnot (fixEmpty x1) emptyAnnot (fixEmpty x3)
      fix a (JSCatchIf _ _ x1 _ ex _ x3) = JSCatchIf a emptyAnnot (fixEmpty x1) spaceAnnot (fixSpace ex) emptyAnnot (fixEmpty x3)
  
  
  instance MinifyJS JSTryFinally where
      fix a (JSFinally _ x) = JSFinally a (fixEmpty x)
      fix _ JSNoFinally = JSNoFinally
  
  
  fixSwitchParts :: [JSSwitchParts] -> [JSSwitchParts]
  fixSwitchParts parts =
      case parts of
          [] -> []
          [x] -> [fixPart noSemi x]
          (x:xs) -> fixPart semi x : fixSwitchParts xs
    where
      fixPart s (JSCase _ e _ ss) = JSCase emptyAnnot (fixCase e) emptyAnnot (fixStatementList s ss)
      fixPart s (JSDefault _ _ ss) = JSDefault emptyAnnot emptyAnnot (fixStatementList s ss)
  
  fixCase :: JSExpression -> JSExpression
  fixCase (JSStringLiteral _ s) = JSStringLiteral emptyAnnot s
  fixCase e = fix spaceAnnot e
  
  
  instance MinifyJS JSBlock where
      fix _ (JSBlock _ ss _) = JSBlock emptyAnnot (fixStatementList noSemi ss) emptyAnnot
  
  
  instance MinifyJS JSObjectProperty where
      fix a (JSPropertyNameandValue n _ vs)       = JSPropertyNameandValue (fix a n) emptyAnnot (map fixEmpty vs)
      fix a (JSPropertyIdentRef     _ s)          = JSPropertyIdentRef a s
      fix a (JSObjectMethod         m)            = JSObjectMethod (fix a m)
  
  instance MinifyJS JSMethodDefinition where
      fix a (JSMethodDefinition          n _ ps _ b)   = JSMethodDefinition                     (fix a n)    emptyAnnot (fixEmpty ps) emptyAnnot (fixEmpty b)
      fix _ (JSGeneratorMethodDefinition _ n _ ps _ b) = JSGeneratorMethodDefinition emptyAnnot (fixEmpty n) emptyAnnot (fixEmpty ps) emptyAnnot (fixEmpty b)
      fix a (JSPropertyAccessor          s n _ ps _ b) = JSPropertyAccessor          (fix a s)  (fixSpace n) emptyAnnot (fixEmpty ps) emptyAnnot (fixEmpty b)
  
  instance MinifyJS JSPropertyName where
      fix a (JSPropertyIdent _ s)  = JSPropertyIdent a s
      fix a (JSPropertyString _ s) = JSPropertyString a s
      fix a (JSPropertyNumber _ s) = JSPropertyNumber a s
      fix _ (JSPropertyComputed _ x _) = JSPropertyComputed emptyAnnot (fixEmpty x) emptyAnnot
  
  instance MinifyJS JSAccessor where
      fix a (JSAccessorGet _) = JSAccessorGet a
      fix a (JSAccessorSet _) = JSAccessorSet a
  
  
  instance MinifyJS JSArrayElement where
      fix _ (JSArrayElement e) = JSArrayElement (fixEmpty e)
      fix _ (JSArrayComma _)   = JSArrayComma emptyAnnot
  
  
  instance MinifyJS a => MinifyJS (JSCommaList a) where
      fix _ (JSLCons xs _ x) = JSLCons (fixEmpty xs) emptyAnnot (fixEmpty x)
      fix _ (JSLOne a)       = JSLOne (fixEmpty a)
      fix _ JSLNil           = JSLNil
  
  
  instance MinifyJS a => MinifyJS (JSCommaTrailingList a) where
      fix _ (JSCTLComma xs _) = JSCTLNone (fixEmpty xs)
      fix _ (JSCTLNone xs)    = JSCTLNone (fixEmpty xs)
  
  
  instance MinifyJS JSIdent where
      fix a (JSIdentName _ n) = JSIdentName a n
      fix _ JSIdentNone = JSIdentNone
  
  
  instance MinifyJS (Maybe JSExpression) where
      fix a me = fix a <$> me
  
  
  instance MinifyJS JSVarInitializer where
      fix a (JSVarInit _ x) = JSVarInit a (fix emptyAnnot x)
      fix _ JSVarInitNone = JSVarInitNone
  
  
  instance MinifyJS JSTemplatePart where
      fix _ (JSTemplatePart e _ s) = JSTemplatePart (fixEmpty e) emptyAnnot s
  
  
  instance MinifyJS JSClassHeritage where
      fix _ JSExtendsNone = JSExtendsNone
      fix a (JSExtends _ e) = JSExtends a (fixSpace e)
  
  
  instance MinifyJS [JSClassElement] where
      fix _ [] = []
      fix a (JSClassInstanceMethod m:t) = JSClassInstanceMethod (fix a m) : fixEmpty t
      fix a (JSClassStaticMethod _ m:t) = JSClassStaticMethod a (fixSpace m) : fixEmpty t
      fix a (JSClassSemi _:t) = fix a t
  
  
  spaceAnnot :: JSAnnot
  spaceAnnot = JSAnnot tokenPosnEmpty [WhiteSpace tokenPosnEmpty " "]
  
  emptyAnnot :: JSAnnot
  emptyAnnot = JSNoAnnot
  
  newlineAnnot :: JSAnnot
  newlineAnnot = JSAnnot tokenPosnEmpty [WhiteSpace tokenPosnEmpty "\n"]
  
  semi :: JSSemi
  semi = JSSemi emptyAnnot
  
  noSemi :: JSSemi
  noSemi = JSSemiAuto
  