diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,38 @@
-# Verison v0.3.2
+# ChangeLog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+
+## [Unreleased]
+
+## [v0.5.3]
+### Fixed
+- Dead-code elimination of foreign es-modules now works
+
+## [v0.5.2]
+### Fixed
+- Handling CoreFn generated by purescript since `0.15.3`
+- Handling of multibyte UTF-8 characters correctly in foreign imports
+
+## [v0.5.0]
+### Added
+- Update to PureScript 0.15 (#77 Thanks @i-am-the-slime)
+- Migrate tests to es-modules
+
+## [v0.4.0]
+### Added
+- Use purescript 0.14 for code generation
+
+### Fixed
+- DCE of re-exported symbols for purescipt 0.14 modules
+
+## [v0.3.2]
 - Copy foreign modules from source directories if they are not present in the
   `output` directory.
+
+[Unreleased]: https://github.com/MaybeJustJames/zephyr/compare/v0.5.3...HEAD
+[v0.5.2]: https://github.com/MaybeJustJames/zephyr/compare/v0.5.2...v0.5.3
+[v0.5.2]: https://github.com/MaybeJustJames/zephyr/compare/v0.5.0...v0.5.2
+[v0.5.0]: https://github.com/MaybeJustJames/zephyr/compare/v0.4.0...v0.5.0
+[v0.4.0]: https://github.com/MaybeJustJames/zephyr/compare/v0.3.2...v0.4.0
+[v0.3.2]: https://github.com/MaybeJustJames/zephyr/compare/v0.3.1...v0.3.2
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
 # zephyr
-[![Maintainer: coot](https://img.shields.io/badge/maintainer-coot-lightgrey.svg)](http://github.com/coot)
+[![Maintainer: MaybeJustJames](https://img.shields.io/badge/maintainer-MaybeJustJames-lightgrey.svg)](http://github.com/MaybeJustJames)
 ![zephyr](https://github.com/coot/zephyr/workflows/Haskell%20CI/badge.svg)
 
 A tree-shaking tool for [PureScript](https://github.com/purescript/purescript).
@@ -8,6 +8,9 @@
 and can substantially reduce the size of PureScript bundles.  `zephyr` can also
 evaluate some expressions (an experimental feature).
 
+# Installation
+The simplest option is to download the latest [release](https://github.com/coot/zephyr/releases) binary. You may also build from source (see "Build & Test" section below).
+
 # Usage
 ```sh
 # compile your project
@@ -76,7 +79,7 @@
 * [parcel](https://github.com/parcel-bundler/parcel)
 * [spago](https://github.com/purescript/spago). See
   [this](https://github.com/thomashoneyman/purescript-halogen-realworld)
-  example.  Use `spago build --purs-args '--codegen corefn'` to compile, using
+  example.  Use `spago build --purs-args '--codegen corefn,js'` to compile, using
   `spago bundle` is currently affected by
   [issue](https://github.com/purescript/spago/issues/216).
 
@@ -91,6 +94,10 @@
 ```sh
 cabal run zephyr-test
 ```
+
+# C Libraries
+
+The released binaries are dynamically linked against `glibc`, so if your system is using `musl` (like Alpine Linux in `docker`) or another alternative C library, you will need to compile `zephyr` from source using `ghc` on that system.
 
 # Comments
 
diff --git a/app/Command/Options.hs b/app/Command/Options.hs
--- a/app/Command/Options.hs
+++ b/app/Command/Options.hs
@@ -62,7 +62,7 @@
      Opts.metavar "entry-point"
   <> Opts.help "Qualified identifier or a module name (it may be prefixed with `ident:` or `module:`). All code which is not a transitive dependency of an entry point (or any exported identifier from a give module) will be removed. You can pass multiple entry points."
   where
-  checkIfQualified (EntryPoint q@(P.Qualified Nothing _)) = fail $
+  checkIfQualified (EntryPoint q@(P.Qualified (P.BySourcePos _) _)) = fail $
     "not a qualified indentifier: '" ++ T.unpack (P.showQualified P.runIdent q) ++ "'"
   checkIfQualified e = return e
 
@@ -78,7 +78,7 @@
      Opts.short 'f'
   <> Opts.long "dce-foreign"
   <> Opts.showDefault
-  <> Opts.help "dce foriegn modules"
+  <> Opts.help "dce foreign modules"
 
 comments :: Opts.Parser Bool
 comments = Opts.switch $
diff --git a/app/Command/Run.hs b/app/Command/Run.hs
--- a/app/Command/Run.hs
+++ b/app/Command/Run.hs
@@ -23,16 +23,13 @@
 import qualified Data.Aeson.Internal as A
 import           Data.Aeson.Parser (eitherDecodeWith, json)
 import qualified Data.ByteString.Lazy as BSL
-import qualified Data.ByteString.Lazy.Char8 as BSL.Char8 (unpack)
 import qualified Data.ByteString.Lazy.UTF8 as BU8
 import           Data.Bool (bool)
-import           Data.Either (Either, lefts, rights, partitionEithers)
+import           Data.Either (lefts, rights, partitionEithers)
 import           Data.Foldable (for_, traverse_)
-import           Data.List (null)
 import qualified Data.Map as M
 import qualified Data.Set as S
 import           Data.Maybe (isNothing, listToMaybe)
-import           Data.Monoid ((<>))
 import           Data.Text (Text)
 import qualified Data.Text as T
 import qualified Data.Text.Lazy.Encoding as TE
@@ -118,8 +115,8 @@
 
 printWarningsAndErrors verbose True warnings errors = do
   hPutStrLn stderr . BU8.toString . A.encode $
-    P.JSONResult (P.toJSONErrors verbose P.Warning warnings)
-               (either (P.toJSONErrors verbose P.Error) (const []) errors)
+    P.JSONResult (P.toJSONErrors verbose P.Warning [] warnings)
+               (either (P.toJSONErrors verbose P.Error []) (const []) errors)
   either (const exitFailure) (const (return ())) errors
 
 
@@ -147,19 +144,19 @@
           0 -> []
           x -> ["... (" <> T.pack (show x) <> " more)"]
   in sformat
-        (string%": Failed parsing:\n  "%stext)
+        (string % ": Failed parsing:\n  " % stext)
         (DCE.colorString DCE.errorColor "Error")
         (T.intercalate "\n\t" errs')
 formatDCEAppError _ _ (NoInputs path)
   = sformat
-        (stext%": No inputs found under "%string%" directory.\n"
-              %"       Please run `purs compile --codegen corefn ..` or"
-              %"`pulp build -- --codegen corefn`")
+        (stext % ": No inputs found under " % string % " directory.\n"
+               % "       Please run `purs compile --codegen corefn ..` or"
+               % "`pulp build -- --codegen corefn`")
         (DCE.colorText DCE.errorColor "Error")
         (DCE.colorString DCE.codeColor path)
 formatDCEAppError _ _ (InputNotDirectory path)
   = sformat
-        (stext%": Directory "%string%" does not exists.")
+        (stext % ": Directory "%string%" does not exists.")
         (DCE.colorText DCE.errorColor "Error")
         (DCE.colorString DCE.codeColor path)
 formatDCEAppError _ relPath (DCEAppError err)
@@ -185,21 +182,25 @@
 
     modExports :: P.ModuleName -> [CoreFn.Module CoreFn.Ann] -> [Either EntryPoint (P.Qualified P.Ident)]
     modExports mn [] = [Left (EntryModule mn)]
-    modExports mn (CoreFn.Module{ CoreFn.moduleName, CoreFn.moduleExports } : ms)
+    modExports mn (CoreFn.Module{ CoreFn.moduleName, CoreFn.moduleExports, CoreFn.moduleReExports } : ms)
       | mn == moduleName
-      = (Right . flip P.mkQualified mn) `map` moduleExports
+      = (Right . flip P.mkQualified mn) `map` (moduleExports ++ (snd =<< M.toList moduleReExports))
       | otherwise
       = modExports mn ms
 
     fnd :: P.Qualified P.Ident -> [CoreFn.Module CoreFn.Ann] -> Bool
     fnd _ [] = False
-    fnd qi@(P.Qualified (Just mn) i) (CoreFn.Module{ CoreFn.moduleName, CoreFn.moduleExports } : ms)
-      = if moduleName == mn && i `elem` moduleExports
+    fnd qi@(P.Qualified (P.ByModuleName mn) i) (pModule@CoreFn.Module{ CoreFn.moduleName } : ms)
+      = if moduleName == mn && i `elem` (allExports pModule)
           then True
           else fnd qi ms
     fnd _ _ = False
 
+    allExports :: CoreFn.Module CoreFn.Ann -> [P.Ident]
+    allExports CoreFn.Module{ CoreFn.moduleExports, CoreFn.moduleReExports } =
+      moduleExports ++ (M.toList moduleReExports >>= snd)
 
+
 dceCommand :: Options -> ExceptT DCEAppError IO ()
 dceCommand Options { optEntryPoints
                    , optInputDir
@@ -243,7 +244,7 @@
               fn _                   acc = acc
           in throwError (DCEAppError $ EntryPointsNotParsed (foldr fn [] perrs))
 
-    when (null $ entryPoints) $
+    when (null entryPoints) $
       throwError (DCEAppError NoEntryPoint)
 
     -- run `evaluate` and `runDeadCodeElimination` on `CoreFn` representation
@@ -268,12 +269,12 @@
                   case moduleName `M.lookup` foreigns of
                     -- run `runForeignModuleDeadCodeElimination`
                     Just path | optForeign  -> do
-                      jsCode <- BSL.Char8.unpack <$> BSL.readFile path
-                      case JS.parse jsCode path of
+                      jsCode <- BU8.toString <$> BSL.readFile path
+                      case JS.parseModule jsCode path of
                         Left _ -> return ()
-                        Right (JS.JSAstProgram ss ann) ->
-                          let ss'    = DCE.runForeignModuleDeadCodeElimination moduleForeign ss
-                              jsAst' = JS.JSAstProgram ss' ann
+                        Right (JS.JSAstModule items ann) ->
+                          let items'    = DCE.runForeignModuleDeadCodeElimination moduleForeign items
+                              jsAst' = JS.JSAstModule items' ann
                               foreignFile = optOutputDir
                                         </> T.unpack (P.runModuleName moduleName)
                                         </> "foreign.js"
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -1,14 +1,13 @@
 module Main (main) where
 
-import           Data.Monoid ((<>))
 import           Data.Version (showVersion)
 import qualified Options.Applicative as Opts
 import qualified Paths_zephyr as Paths
 import           System.Environment (getArgs)
 import qualified System.IO as IO
 
-import           Command.Run
-import           Command.Options
+import Command.Run ( runZephyr )
+import Command.Options ( parseOptions )
 
 
 main :: IO ()
@@ -23,7 +22,7 @@
   where
     execParserPure :: Opts.ParserInfo a -> [String] -> Opts.ParserResult a
     execParserPure pinfo [] = Opts.Failure $
-      Opts.parserFailure Opts.defaultPrefs pinfo Opts.ShowHelpText mempty
+      Opts.parserFailure Opts.defaultPrefs pinfo (Opts.ShowHelpText Nothing) mempty
     execParserPure pinfo args = Opts.execParserPure Opts.defaultPrefs pinfo args
 
 versionOpt :: Opts.Parser (a -> a)
diff --git a/src/Language/PureScript/DCE.hs b/src/Language/PureScript/DCE.hs
--- a/src/Language/PureScript/DCE.hs
+++ b/src/Language/PureScript/DCE.hs
@@ -2,6 +2,20 @@
   ( module DCE ) where
 
 import Language.PureScript.DCE.CoreFn as DCE
+    ( runDeadCodeElimination, runBindDeadCodeElimination )
 import Language.PureScript.DCE.Foreign as DCE
+    ( runForeignModuleDeadCodeElimination )
 import Language.PureScript.DCE.Errors as DCE
-import Language.PureScript.DCE.Eval as DCE
+    ( Level(..),
+      DCEError(..),
+      EntryPoint(..),
+      isEntryParseError,
+      showEntryPoint,
+      displayDCEError,
+      displayDCEWarning,
+      errorColor,
+      warnColor,
+      codeColor,
+      colorString,
+      colorText )
+import Language.PureScript.DCE.Eval as DCE ( evaluate )
diff --git a/src/Language/PureScript/DCE/Constants.hs b/src/Language/PureScript/DCE/Constants.hs
--- a/src/Language/PureScript/DCE/Constants.hs
+++ b/src/Language/PureScript/DCE/Constants.hs
@@ -1,49 +1,50 @@
--- |
--- Various constants used by zephyr.
+-- | Various constants used by zephyr.
+--
 module Language.PureScript.DCE.Constants where
 
 import Prelude hiding (maybe)
 
 import Language.PureScript.Names
 
+
 unit :: ModuleName
 unit =  ModuleName "Data.Unit"
 
 pattern Unit :: ModuleName
 pattern Unit = ModuleName "Data.Unit"
 
-semigroup :: ModuleName
-semigroup = ModuleName "Data.Semigroup"
-
 maybeMod :: ModuleName
 maybeMod = ModuleName "Data.Maybe"
 
+semigroup :: ModuleName
+semigroup = ModuleName "Data.Semigroup"
+
 pattern Semigroup :: ModuleName
 pattern Semigroup = ModuleName "Data.Semigroup"
 
 semiring :: ModuleName
 semiring = ModuleName "Data.Semiring"
 
-pattern Ring :: ModuleName
-pattern Ring = ModuleName "Data.Ring"
-
 ring :: ModuleName
 ring = ModuleName "Data.Ring"
 
+pattern Ring :: ModuleName
+pattern Ring = ModuleName "Data.Ring"
+
 pattern Semiring :: ModuleName
 pattern Semiring = ModuleName "Data.Semiring"
 
-pattern HeytingAlgebra :: ModuleName
-pattern HeytingAlgebra = ModuleName "Data.HeytingAlgebra"
-
 heytingAlgebra :: ModuleName
 heytingAlgebra = ModuleName "Data.HeytingAlgebra"
 
-pattern UnsafeCoerce :: ModuleName
-pattern UnsafeCoerce = ModuleName "Unsafe.Coerce"
+pattern HeytingAlgebra :: ModuleName
+pattern HeytingAlgebra = ModuleName "Data.HeytingAlgebra"
 
 unsafeCoerce :: ModuleName
 unsafeCoerce = ModuleName "Unsafe.Coerce"
+
+pattern UnsafeCoerce :: ModuleName
+pattern UnsafeCoerce = ModuleName "Unsafe.Coerce"
 
 eqMod :: ModuleName
 eqMod = ModuleName "Data.Eq"
diff --git a/src/Language/PureScript/DCE/CoreFn.hs b/src/Language/PureScript/DCE/CoreFn.hs
--- a/src/Language/PureScript/DCE/CoreFn.hs
+++ b/src/Language/PureScript/DCE/CoreFn.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
 -- |
 -- Dead code elimination for `CoreFn`.
 module Language.PureScript.DCE.CoreFn
@@ -5,17 +7,35 @@
   , runBindDeadCodeElimination
   ) where
 
-import           Prelude.Compat hiding (mod)
+import           Prelude hiding (mod)
 import           Control.Arrow ((***))
-import           Control.Monad
-import           Data.Graph
+import Control.Monad ( guard )
+import Data.Graph ( graphFromEdges, reachable, Vertex )
 import           Data.Foldable (foldl', foldr')
-import           Data.List (any, elem, filter, groupBy, sortBy)
+import           Data.List (groupBy, sortBy)
+import qualified Data.Map.Strict as M
 import           Data.Maybe (catMaybes, mapMaybe)
 import qualified Data.Set as S
-import           Language.PureScript.CoreFn
+import Language.PureScript.CoreFn
+    ( Literal(ObjectLiteral, ArrayLiteral),
+      Ann,
+      everywhereOnValues,
+      Binder(VarBinder, LiteralBinder, ConstructorBinder, NamedBinder),
+      Bind(..),
+      CaseAlternative(CaseAlternative),
+      Expr(..),
+      Module(Module, moduleImports, moduleExports, moduleForeign,
+             moduleDecls, moduleName, moduleReExports) )
 import           Language.PureScript.DCE.Utils (bindIdents, unBind)
-import           Language.PureScript.Names
+import Language.PureScript.Names
+    ( getQual,
+      isQualified,
+      mkQualified,
+      Ident(Ident),
+      ModuleName,
+      ProperName(runProperName),
+      Qualified(..),
+      QualifiedBy(..) )
 
 type Key = Qualified Ident
 
@@ -23,6 +43,7 @@
 data DCEVertex
   = BindVertex (Bind Ann)
   | ForeignVertex (Qualified Ident)
+  | ReExportedVertex (Qualified Ident)
 
 
 -- | Dead code elimination of a list of modules module
@@ -40,15 +61,16 @@
     -- DCE of a single module.
     runModuleDeadCodeElimination
       :: [(DCEVertex, Key, [Key])]
-      -- list of qualified names that has to be preserved
+      -- list of qualified names that need to be preserved
       -> Module Ann
       -> Module Ann
     runModuleDeadCodeElimination vs mod@Module{ moduleDecls
                         , moduleExports
+                        , moduleReExports
                         , moduleImports
                         , moduleName
                         , moduleForeign
-                        } = 
+                        } =
       let
           -- | filter declarations preserving the order
           moduleDecls' :: [Bind Ann]
@@ -71,6 +93,17 @@
           moduleExports' =
             filter (`elem` (idents ++ moduleForeign')) moduleExports
 
+          moduleReExports' :: M.Map ModuleName [Ident]
+          moduleReExports' =
+            filter (`elem` rexpIdents) <$> moduleReExports
+            where
+              toRexpIdents :: (DCEVertex, Key, [Key]) -> [Ident]
+              toRexpIdents (ReExportedVertex (Qualified _ i), _, _) = [i]
+              toRexpIdents _ = []
+
+              rexpIdents :: [Ident]
+              rexpIdents = concatMap toRexpIdents vs
+
           mods :: [ModuleName]
           mods = mapMaybe getQual (concatMap (\(_, _, ks) -> ks) vs)
 
@@ -79,7 +112,7 @@
 
           moduleForeign' :: [Ident]
           moduleForeign' = filter
-              ((`S.member` reachableSet) . Qualified (Just moduleName))
+              ((`S.member` reachableSet) . Qualified (ByModuleName moduleName))
               moduleForeign
             where
               reachableSet = foldr'
@@ -88,6 +121,7 @@
 
       in mod { moduleImports = moduleImports'
              , moduleExports = moduleExports'
+             , moduleReExports = moduleReExports'
              , moduleForeign = moduleForeign'
              , moduleDecls   = moduleDecls'
              }
@@ -97,9 +131,10 @@
     -- | The Vertex set.
     verts :: [(DCEVertex, Key, [Key])]
     verts = do
-        Module _ _ mn _ _ _ mf ds <- modules
-        concatMap (toVertices mn) ds
-          ++ ((\q -> (ForeignVertex q, q, [])) . flip mkQualified mn) `map` mf
+        Module _ _ mn _ _ _ rexp mf ds <- modules
+        concatMap (toVertices mn) ds -- Module local bindings
+          ++ ((\q -> (ForeignVertex q, q, [])) . flip mkQualified mn) `map` mf -- Foreign bindings
+          ++ reExportedVertices mn rexp -- Re-exported bindings
       where
       toVertices :: ModuleName -> Bind Ann -> [(DCEVertex, Key, [Key])]
       toVertices mn b@(NonRec _ i e) =
@@ -109,26 +144,103 @@
             ks = map (\((_, i), e) -> (mkQualified i mn, deps e)) bs
         in map (\(k, ks') -> (BindVertex b, k, map fst ks ++ ks')) ks
 
+      reExportedVertices :: ModuleName -> M.Map ModuleName [Ident] -> [(DCEVertex, Key, [Key])]
+      reExportedVertices parent rexp =
+        filter (isReExported rexp) modules >>= rexpsFor
+        where
+          rexpsFor :: Module Ann -> [(DCEVertex, Key, [Key])]
+          rexpsFor (Module _ _ mn _ _ _ _ _ _) = case M.lookup mn rexp of
+            Nothing -> []
+            Just ids -> (\i -> (ReExportedVertex (mkQualified i parent)
+                              , mkQualified i parent
+                              , [mkQualified i mn])
+                       ) <$> ids
+
+
+      isReExported :: M.Map ModuleName [Ident] -> Module Ann -> Bool
+      isReExported rexps (Module _ _ name _ _ _ _ _ _) =
+        M.member name rexps
+
       -- | Find dependencies of an expression.
       deps :: Expr Ann -> [Key]
-      deps = go
+      deps = traverseExpr
         where
-          (_, go, _, _) = everythingOnValues (++)
-            (const [])
-            onExpr
-            onBinder
-            (const [])
-
           -- | Build graph from qualified identifiers.
           onExpr :: Expr Ann -> [Key]
           onExpr (Var _ i) = [i | isQualified i]
           onExpr _ = []
 
+          traverseExpr :: Expr Ann -> [Key]
+          traverseExpr v@(Literal _ l) =
+            foldl'
+              (++)
+              (onExpr v)
+              (map traverseExpr (extractLiteral l))
+          traverseExpr v@Constructor {} = onExpr v
+          traverseExpr v@(Accessor _ _ e1) = onExpr v ++ traverseExpr e1
+          traverseExpr v@(ObjectUpdate _ obj vs) =
+            foldl'
+              (++)
+              (onExpr v ++ traverseExpr obj)
+              (map (traverseExpr . snd) vs)
+          traverseExpr v@(Abs _ _ e1) = onExpr v ++ traverseExpr e1
+          traverseExpr v@(App _ e1 e2) =
+            onExpr v ++ traverseExpr e1 ++ traverseExpr e2
+          traverseExpr v@(Var _ _) = onExpr v
+          traverseExpr v@(Case _ vs alts) =
+            foldl'
+              (++)
+              (foldl' (++) (onExpr v) (map traverseExpr vs))
+              (map onCaseAlternative alts)
+          traverseExpr v@(Let _ ds e1) =
+            foldl'
+              (++)
+              (onExpr v)
+              (map onBind ds)
+            ++
+              traverseExpr e1
+
+          onBind :: Bind Ann -> [Key]
+          onBind (NonRec _ _ e) = traverseExpr e
+          onBind (Rec es) = concatMap (traverseExpr . snd) es
+
           onBinder :: Binder Ann -> [Key]
-          onBinder (ConstructorBinder _ _ c _) =
+          onBinder b@(LiteralBinder _ l) =
+            foldl'
+              (++)
+              (onBinder' b)
+              (map onBinder (extractLiteral l))
+          onBinder b@(ConstructorBinder _ _ _ bs) =
+            foldl'
+              (++)
+              (onBinder' b)
+              (map onBinder bs)
+          onBinder b@(NamedBinder _ _ b1) = onBinder' b ++ onBinder b1
+          onBinder b = onBinder' b
+
+          onBinder' :: Binder Ann -> [Key]
+          onBinder' (ConstructorBinder _ _ c _) =
             [fmap (Ident . runProperName) c]
-          onBinder _ = []
+          onBinder' _ = []
 
+          onCaseAlternative :: CaseAlternative Ann -> [Key]
+          onCaseAlternative (CaseAlternative bs (Right val)) =
+            concatMap onBinder bs ++ traverseExpr val
+          onCaseAlternative (CaseAlternative bs (Left gs)) =
+            concat
+              (map
+                 onBinder
+                 bs ++ concatMap
+                         (\(grd, val) ->
+                            [traverseExpr grd, traverseExpr val]) gs)
+
+          -- @f@ is either 'Expr' or 'Binder'
+          extractLiteral :: Literal (f Ann) -> [f Ann]
+          extractLiteral (ArrayLiteral xs) = xs
+          extractLiteral (ObjectLiteral xs) = map snd xs
+          extractLiteral _ = []
+
+
     -- | Vertices corresponding to the entry points which we want to keep.
     entryPointVertices :: [Vertex]
     entryPointVertices = catMaybes $ do
@@ -222,7 +334,7 @@
         else isUsedInExpr i e
   isUsedInExpr i (App _ l r) = isUsedInExpr i l || isUsedInExpr i r
   isUsedInExpr i (Abs _ i' e) = i /= i' && isUsedInExpr i e
-  isUsedInExpr i (Var _ qi) = qi == Qualified Nothing i
+  isUsedInExpr i (Var _ qi@(Qualified by _)) = qi == Qualified by i
   isUsedInExpr i (Case _ es alts)
     = any (isUsedInExpr i) es || any (isUsedInCaseAlternative i) alts
   isUsedInExpr i (Let _ bs e) =
diff --git a/src/Language/PureScript/DCE/Errors.hs b/src/Language/PureScript/DCE/Errors.hs
--- a/src/Language/PureScript/DCE/Errors.hs
+++ b/src/Language/PureScript/DCE/Errors.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
 -- |
 -- Errors used in dead call elimination.
 module Language.PureScript.DCE.Errors
@@ -16,11 +18,9 @@
   )
   where
 
-import Prelude.Compat
-
+import           Prelude hiding (mod)
 import           Data.Char (isLower, isUpper, isSpace)
 import           Data.List (dropWhileEnd, findIndex, intersperse)
-import           Data.Monoid ((<>))
 import           Data.Text (Text)
 import qualified Data.Text as T
 import           Formatting (sformat, string, stext, (%))
diff --git a/src/Language/PureScript/DCE/Eval.hs b/src/Language/PureScript/DCE/Eval.hs
--- a/src/Language/PureScript/DCE/Eval.hs
+++ b/src/Language/PureScript/DCE/Eval.hs
@@ -1,26 +1,27 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
 -- | Evaluation of PureScript's expressions used in dead call elimnation.
 --
 module Language.PureScript.DCE.Eval
   ( evaluate ) where
 
+import Prelude hiding (mod)
+
 import Control.Applicative ((<|>))
 import Control.Exception (Exception (..), throw)
 import Control.Monad
 import Control.Monad.Writer
 
 import Data.List (find)
-import Data.Maybe (Maybe(..), fromMaybe)
-import Data.Monoid (First(..))
+import Data.Maybe (fromMaybe)
 import qualified Data.Text as T
 import qualified Language.PureScript.DCE.Constants as C
-import Prelude.Compat hiding (mod)
 import Safe (atMay)
 
 import Language.PureScript.AST.Literals
 import Language.PureScript.CoreFn
---import Language.PureScript.DCE.Errors
 import Language.PureScript.DCE.Utils
-import Language.PureScript.Names
+import Language.PureScript.Names (Ident(..), ModuleName(..), Qualified(..), QualifiedBy(..))
 import Language.PureScript.PSString
 
 
@@ -243,13 +244,13 @@
 
 -- eval _    _  _  _  = Nothing -- TODO: testing without evaluation
 
-eval mods mn st (Var _ (Qualified Nothing i)) = 
+eval mods mn st (Var _ (Qualified (BySourcePos _) i)) = 
     case lookupStack i st of
       Nothing               -> Nothing
       Just ((_, e), Done)   -> Just e
       Just ((_, e), NotYet) -> eval mods mn (markDone i st) e
 
-eval mods mn st (Var ann qi@(Qualified (Just imn) i)) =
+eval mods mn st (Var ann qi@(Qualified (ByModuleName imn) i)) =
     case lookupQualifiedExpr mods imn i of
       Nothing             -> throw (QualifiedExpresionError ann qi (moduleName `map` mods))
       Just (FoundExpr e)  -> eval mods mn st e
@@ -284,19 +285,19 @@
         (App _
           (Var _
             (Qualified
-              (Just C.Eq)
+              (ByModuleName C.Eq)
               (Ident "eq")))
           (Var _ inst))
           e1)
       e2) =
     if inst `elem`
-          [ Qualified (Just C.eqMod) (Ident "eqBoolean")
-          , Qualified (Just C.eqMod) (Ident "eqInt")
-          , Qualified (Just C.eqMod) (Ident "eqNumber")
-          , Qualified (Just C.eqMod) (Ident "eqChar")
-          , Qualified (Just C.eqMod) (Ident "eqString")
-          , Qualified (Just C.eqMod) (Ident "eqUnit")
-          , Qualified (Just C.eqMod) (Ident "eqVoid")
+          [ Qualified (ByModuleName C.eqMod) (Ident "eqBoolean")
+          , Qualified (ByModuleName C.eqMod) (Ident "eqInt")
+          , Qualified (ByModuleName C.eqMod) (Ident "eqNumber")
+          , Qualified (ByModuleName C.eqMod) (Ident "eqChar")
+          , Qualified (ByModuleName C.eqMod) (Ident "eqString")
+          , Qualified (ByModuleName C.eqMod) (Ident "eqUnit")
+          , Qualified (ByModuleName C.eqMod) (Ident "eqVoid")
           ]
       then case (eval mods mn st e1, eval mods mn st e2) of
           (Just (Literal _ l1), Just (Literal _ l2))
@@ -312,7 +313,7 @@
         (App _
           (Var ann@(ss, _, _, _)
             (Qualified
-              (Just (ModuleName "Data.Array"))
+              (ByModuleName (ModuleName "Data.Array"))
               (Ident "index")))
           (Literal _ (ArrayLiteral as)))
         (Literal _ (NumericLiteral (Left x)))) =
@@ -324,7 +325,7 @@
           Just $ App ann
                   (Var (ss, [], Nothing, Just (IsConstructor SumType [Ident "value0"]))
                     (Qualified
-                      (Just C.maybeMod)
+                      (ByModuleName C.maybeMod)
                       (Ident "Just")))
                   e'
 --
@@ -334,22 +335,22 @@
     (App ann
       (App _
         (App _
-           (Var _ (Qualified (Just C.Semigroup) (Ident "append")))
+           (Var _ (Qualified (ByModuleName C.Semigroup) (Ident "append")))
            (Var _ qi))
         e1)
       e2)
-      | qi == Qualified (Just C.semigroup) (Ident "semigroupArray")
+      | qi == Qualified (ByModuleName C.semigroup) (Ident "semigroupArray")
       , Literal _ (ArrayLiteral a1) <- e1
       , Literal _ (ArrayLiteral a2) <- e2
       = Just $ Literal ann (ArrayLiteral $ a1 ++ a2)
-      | qi == Qualified (Just C.semigroup) (Ident "semigroupString")
+      | qi == Qualified (ByModuleName C.semigroup) (Ident "semigroupString")
       , Literal _ (StringLiteral s1) <- e1
       , Just t1 <- decodeString s1
       , Literal _ (StringLiteral s2) <- e2
       , Just t2 <- decodeString s2
       = Just $ Literal ann (StringLiteral (mkString $ t1 <> t2) )
-      | qi == Qualified (Just C.semigroup) (Ident "semigroupUnit")
-      = Just $ Var ann (Qualified (Just C.unit) (Ident "unit"))
+      | qi == Qualified (ByModuleName C.semigroup) (Ident "semigroupUnit")
+      = Just $ Var ann (Qualified (ByModuleName C.unit) (Ident "unit"))
       | otherwise
       = Nothing
 
@@ -360,66 +361,66 @@
     (App (ss, c, _, _)
       (App _
         (App _
-           (Var _ (Qualified (Just C.Semiring) (Ident "add")))
+           (Var _ (Qualified (ByModuleName C.Semiring) (Ident "add")))
            (Var _ qi))
         e1)
       e2)
-    | qi == Qualified (Just C.semiring) (Ident "semiringInt")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringInt")
     , Literal _ (NumericLiteral (Left a1)) <- e1
     , Literal _ (NumericLiteral (Left a2)) <- e2
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Left (a1 + a2)))
-    | qi == Qualified (Just C.semiring) (Ident "semiringNumber")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringNumber")
     , Literal _ (NumericLiteral (Right a1)) <- e1
     , Literal _ (NumericLiteral (Right a2)) <- e2
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Right (a1 + a2)))
-    | qi == Qualified (Just C.semiring) (Ident "semiringUnit")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringUnit")
     = Just $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
     | otherwise
     = Nothing
 
 eval _ _mn _st
     (App (ss, c, _, _)
-      (Var _ (Qualified (Just C.Semiring) (Ident "zero")))
+      (Var _ (Qualified (ByModuleName C.Semiring) (Ident "zero")))
       (Var _ qi))
-    | qi == Qualified (Just C.semiring) (Ident "semiringInt")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringInt")
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Left 0))
     | qi == Qualified
-        (Just C.semiring)
+        (ByModuleName C.semiring)
         (Ident "semiringNumber")
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Right 0.0))
-    | qi == Qualified (Just C.semiring) (Ident "semiringUnit")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringUnit")
     = Just  $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
     | otherwise
     = Nothing
 
 eval _ _mn _st
     (App (ss, c, _, _)
-      (Var _ (Qualified (Just C.Semiring) (Ident "one")))
+      (Var _ (Qualified (ByModuleName C.Semiring) (Ident "one")))
       (Var _ qi))
-    | qi == Qualified (Just C.semiring) (Ident "semiringInt")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringInt")
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Left 1))
-    | qi == Qualified (Just C.semiring) (Ident "semiringNumber")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringNumber")
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Right 1.0))
-    | qi == Qualified (Just C.semiring) (Ident "semiringUnit")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringUnit")
     = Just  $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
     | otherwise
     = Nothing
 
@@ -427,26 +428,26 @@
     (App (ss, c, _, _)
       (App _
         (App _
-           (Var _ (Qualified (Just C.Semiring) (Ident "mul")))
+           (Var _ (Qualified (ByModuleName C.Semiring) (Ident "mul")))
            (Var _ qi))
         e1)
       e2)
-    | qi == Qualified (Just C.semiring) (Ident "semiringInt")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringInt")
     , Literal _ (NumericLiteral (Left a1)) <- e1
     , Literal _ (NumericLiteral (Left a2)) <- e2
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Left (a1 * a2)))
-    | qi == Qualified (Just C.semiring) (Ident "semiringNumber")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringNumber")
     , Literal _ (NumericLiteral (Right a1)) <- e1
     , Literal _ (NumericLiteral (Right a2)) <- e2
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Right (a1 * a2)))
-    | qi == Qualified (Just C.semiring) (Ident "semiringUnit")
+    | qi == Qualified (ByModuleName C.semiring) (Ident "semiringUnit")
     = Just $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
     | otherwise
     = Nothing
 
@@ -457,92 +458,92 @@
     (App (ss, c, _, _)
       (App _
         (App _
-          (Var _ (Qualified (Just C.Ring) (Ident "sub")))
+          (Var _ (Qualified (ByModuleName C.Ring) (Ident "sub")))
           (Var _ qi))
         e1)
       e2)
-    | qi == Qualified (Just C.ring) (Ident "ringInt")
+    | qi == Qualified (ByModuleName C.ring) (Ident "ringInt")
     , Literal _ (NumericLiteral (Left a1)) <- e1
     , Literal _ (NumericLiteral (Left a2)) <- e2
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Left (quot a1 a2)))
-    | qi == Qualified (Just C.ring) (Ident "ringNumber")
+    | qi == Qualified (ByModuleName C.ring) (Ident "ringNumber")
     , Literal _ (NumericLiteral (Right a1)) <- e1
     , Literal _ (NumericLiteral (Right a2)) <- e2
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Right (a1 / a2)))
-    | qi == Qualified (Just C.ring) (Ident "unitRing")
+    | qi == Qualified (ByModuleName C.ring) (Ident "unitRing")
     = Just $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
 
 eval _ _mn _st
     (App (ss, c, _, _)
       (App _
-        (Var _ (Qualified (Just C.Ring) (Ident "negate")))
+        (Var _ (Qualified (ByModuleName C.Ring) (Ident "negate")))
         (Var _ qi))
       e)
-    | qi == Qualified (Just C.ring) (Ident "ringInt")
+    | qi == Qualified (ByModuleName C.ring) (Ident "ringInt")
     , Literal _ (NumericLiteral (Left a)) <- e
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Left (-a)))
-    | qi == Qualified (Just C.ring) (Ident "ringNumber")
+    | qi == Qualified (ByModuleName C.ring) (Ident "ringNumber")
     , Literal _ (NumericLiteral (Right a)) <- e
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (NumericLiteral (Right (-a)))
-    | qi == Qualified (Just C.ring) (Ident "unitRing")
+    | qi == Qualified (ByModuleName C.ring) (Ident "unitRing")
     = Just  $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
 
 --
 -- evaluate Heyting algebras operations
 --
 eval _ _mn _st
     (App (ss, c, _, _)
-      (Var _ (Qualified (Just C.HeytingAlgebra) (Ident "ff")))
+      (Var _ (Qualified (ByModuleName C.HeytingAlgebra) (Ident "ff")))
       (Var _ qi))
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
     = Just $ Literal (ss, c, Nothing, Nothing) (BooleanLiteral False)
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraUnit")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraUnit")
     = Just $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
     | otherwise
     = Nothing
 
 eval _ _mn _st
     (App (ss, c, _, _)
-      (Var _ (Qualified (Just C.HeytingAlgebra) (Ident "tt")))
+      (Var _ (Qualified (ByModuleName C.HeytingAlgebra) (Ident "tt")))
       (Var _ qi))
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
     = Just $ Literal (ss, c, Nothing, Nothing) (BooleanLiteral True)
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraUnit")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraUnit")
     = Just $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
     | otherwise
     = Nothing
 
 eval _mods _mn _st
     (App (ss, c, _, _)
       (App _
-        (Var _ (Qualified (Just C.HeytingAlgebra) (Ident "not")))
+        (Var _ (Qualified (ByModuleName C.HeytingAlgebra) (Ident "not")))
         (Var _ qi))
       e)
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
     , Literal _ (BooleanLiteral b) <- e
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (BooleanLiteral (not b))
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraUnit")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraUnit")
     = Just $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
     | otherwise
     = Nothing
 
@@ -550,20 +551,20 @@
     (App (ss, c, _, _)
       (App _
         (App _
-           (Var _ (Qualified (Just C.HeytingAlgebra) (Ident "implies")))
+           (Var _ (Qualified (ByModuleName C.HeytingAlgebra) (Ident "implies")))
            (Var _ qi))
         e1)
       e2)
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
     , Literal _ (BooleanLiteral b1) <- e1
     , Literal _ (BooleanLiteral b2) <- e2
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (BooleanLiteral (not b1 && b2))
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraUnit")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraUnit")
     = Just $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
     | otherwise
     = Nothing
 
@@ -571,20 +572,20 @@
     (App (ss, c, _, _)
       (App _
         (App _
-           (Var _ (Qualified (Just C.HeytingAlgebra) (Ident "disj")))
+           (Var _ (Qualified (ByModuleName C.HeytingAlgebra) (Ident "disj")))
            (Var _ qi))
         e1)
       e2)
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
     , Literal _ (BooleanLiteral b1) <- e1
     , Literal _ (BooleanLiteral b2) <- e2
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (BooleanLiteral (b1 || b2))
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraUnit")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraUnit")
     = Just $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
     | otherwise
     = Nothing
 
@@ -592,20 +593,20 @@
     (App (ss, c, _, _)
       (App _
         (App _
-           (Var _ (Qualified (Just C.HeytingAlgebra) (Ident "conj")))
+           (Var _ (Qualified (ByModuleName C.HeytingAlgebra) (Ident "conj")))
            (Var _ qi))
         e1)
       e2)
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraBoolean")
     , Literal _ (BooleanLiteral b1) <- e1
     , Literal _ (BooleanLiteral b2) <- e2
     = Just $ Literal
         (ss, c, Nothing, Nothing)
         (BooleanLiteral (b1 && b2))
-    | qi == Qualified (Just C.heytingAlgebra) (Ident "heytingAlgebraUnit")
+    | qi == Qualified (ByModuleName C.heytingAlgebra) (Ident "heytingAlgebraUnit")
     = Just $ Var
         (ss, c, Nothing, Nothing)
-        (Qualified (Just C.unit) (Ident "unit"))
+        (Qualified (ByModuleName C.unit) (Ident "unit"))
     | otherwise
     = Nothing
 
diff --git a/src/Language/PureScript/DCE/Foreign.hs b/src/Language/PureScript/DCE/Foreign.hs
--- a/src/Language/PureScript/DCE/Foreign.hs
+++ b/src/Language/PureScript/DCE/Foreign.hs
@@ -3,252 +3,267 @@
 -- |
 -- Simple dead call elimination in foreign modules.
 module Language.PureScript.DCE.Foreign
-  ( runForeignModuleDeadCodeElimination
-  ) where
+  ( runForeignModuleDeadCodeElimination,
+  )
+where
 
-import           Prelude.Compat
-import           Control.Monad
-import           Data.Graph
-import           Data.Foldable (foldr')
-import           Data.List (any, elem, filter)
-import           Data.Maybe (catMaybes, fromMaybe, mapMaybe)
-import           Data.Text (Text)
+import Data.Foldable (foldr')
+import Data.Graph (graphFromEdges, reachable, transposeG)
+import Data.List (nub)
+import Data.Maybe (catMaybes, mapMaybe)
+import Data.Text (Text)
 import qualified Data.Text as T
-import           Language.JavaScript.Parser.AST
-                  ( JSStatement(..)
-                  , JSExpression(..)
-                  , JSCommaList(..)
-                  , JSBlock(..)
-                  , JSSwitchParts(..)
-                  , JSTryCatch(..)
-                  , JSTryFinally(..)
-                  , JSArrayElement(..)
-                  , JSObjectProperty(..)
-                  , JSCommaTrailingList(..)
-                  , JSCommaList(..)
-                  , JSMethodDefinition(..)
-                  , JSClassElement(..)
-                  , JSClassHeritage(..)
-                  , JSTemplatePart(..)
-                  )
-import           Language.PureScript.Names
-
--- | foldr over `JSCommaList`
-foldrJSCommaList :: (a -> b -> b) -> JSCommaList a -> b -> b
-foldrJSCommaList _ JSLNil b = b
-foldrJSCommaList fn (JSLOne a) !b = fn a b
-foldrJSCommaList fn (JSLCons as _ a) !b = foldrJSCommaList fn as (fn a b)
+import Language.JavaScript.Parser.AST
+  ( JSArrayElement (..),
+    JSBlock (..),
+    JSClassElement (..),
+    JSClassHeritage (..),
+    JSCommaList (..),
+    JSCommaTrailingList (..),
+    JSExportClause (..),
+    JSExportDeclaration (..),
+    JSExportSpecifier (..),
+    JSExpression (..),
+    JSIdent (..),
+    JSMethodDefinition (..),
+    JSModuleItem (..),
+    JSObjectProperty (..),
+    JSStatement (..),
+    JSSwitchParts (..),
+    JSTemplatePart (..),
+    JSTryCatch (..),
+    JSTryFinally (..),
+    JSVarInitializer (..),
+  )
+import Language.PureScript.Names (Ident, runIdent)
 
 -- | Filter export statements in a foreign module.  This is not 100% safe.  It
 -- might remove declarations that are used somewhere in the foreign module (for
 -- example by using @'eval'@).
---
-runForeignModuleDeadCodeElimination :: [Ident] -> [JSStatement] -> [JSStatement]
-runForeignModuleDeadCodeElimination is stmts = filter filterExports stmts
+runForeignModuleDeadCodeElimination :: [Ident] -> [JSModuleItem] -> [JSModuleItem]
+runForeignModuleDeadCodeElimination is items = nub allReachable
   where
-    filterExports :: JSStatement -> Bool
-    filterExports (JSAssignStatement (JSMemberSquare (JSIdentifier _ "exports") _ (JSStringLiteral _ x) _) _ _ _)
-      = fltr (unquote . T.pack $ x)
-    filterExports (JSAssignStatement (JSMemberDot (JSIdentifier _ "exports") _ (JSIdentifier _ x)) _ _ _)
-      = fltr (T.pack x)
-    filterExports _ = True
-
-    fltr :: Text -> Bool
-    fltr t = any (fromMaybe True . (path graph <$> vertexForKey t <*>) . Just) entryPointVertices
-          -- one of `entryPointVertices` depend on this vertex
-          || any (isUsedInStmt t) nonExps
-          -- it is used in any non export statements
+    allReachable :: [JSModuleItem]
+    allReachable = do
+      ePoint <- mapMaybe (vertexForKey . runIdent) is
+      reachableNode <- reachable (transposeG graph) ePoint
+      pure $ jsM $ fromVertex reachableNode
 
     -- Build a graph of exports statements.  Its initial set of edges point from
     -- an export statement to all other export statements that are using it.
     -- When checking if we need to include that vartex we just check if there is
     -- a path from a vertex to one of `entryPointVertices`.
-    exps :: [JSStatement]
-    exps = filter isExportStatement stmts
+    exps :: [JSModuleItem]
+    exps = filter isExportModuleItem items
 
-    nonExps = filter (not . isExportStatement) stmts
+    (graph, fromVertex, vertexForKey) = graphFromEdges verts
 
-    (graph, _, vertexForKey) = graphFromEdges verts
+    jsM :: (JSModuleItem, Text, [Text]) -> JSModuleItem
+    jsM (a, _, _) = a
 
-    verts :: [(JSStatement, Text, [Text])]
-    verts = mapMaybe toVert exps
+    verts :: [(JSModuleItem, Text, [Text])]
+    verts = items >>= toVert
       where
-      toVert :: JSStatement -> Maybe (JSStatement, Text, [Text])
-      toVert s
-        | Just name <- exportStatementName s = Just (s, name, foldr' (fn name) [] exps)
-        | otherwise = Nothing
+        toVert :: JSModuleItem -> [(JSModuleItem, Text, [Text])]
+        toVert item@(JSModuleExportDeclaration _ jsExportDec) = mkVertex item <$> exportNames jsExportDec
+        toVert item@(JSModuleStatementListItem stmt) = mkVertex item <$> toplevelNames stmt
+        toVert _ = []
 
-      fn name s' nms
-        | isUsedInStmt name s'
-        , Just n <- exportStatementName s' = n:nms
-        | otherwise = nms
+        mkVertex :: JSModuleItem -> Text -> (JSModuleItem, Text, [Text])
+        mkVertex item name =
+          (item, name, foldr' (accumulateDependents name) [] exps)
 
-    entryPointVertices :: [Vertex]
-    entryPointVertices = catMaybes $ do
-      (_, k, _) <- verts
-      guard $ k `elem` ns
-      return (vertexForKey k)
-      where
-      ns = runIdent <$> is
+        accumulateDependents :: Text -> JSModuleItem -> [Text] -> [Text]
+        accumulateDependents name item acc =
+          if isNameUsedByItem name item
+            then exportedModuleItemName item ++ acc
+            else acc
 
-    unquote :: Text -> Text
-    unquote = T.drop 1 . T.dropEnd 1
+-- | foldr over `JSCommaList`
+foldrJSCommaList :: (a -> b -> b) -> JSCommaList a -> b -> b
+foldrJSCommaList _ JSLNil b = b
+foldrJSCommaList fn (JSLOne a) !b = fn a b
+foldrJSCommaList fn (JSLCons as _ a) !b = foldrJSCommaList fn as (fn a b)
 
-    isExportStatement :: JSStatement -> Bool
-    isExportStatement (JSAssignStatement (JSMemberDot (JSIdentifier _ "exports") _ (JSIdentifier _ _)) _ _ _) = True
-    isExportStatement (JSAssignStatement (JSMemberSquare (JSIdentifier _ "exports") _ (JSStringLiteral _ _) _) _ _ _) = True
-    isExportStatement _ = False
+toList :: JSCommaList a -> [a]
+toList ls = foldrJSCommaList (:) ls []
 
-    exportStatementName :: JSStatement -> Maybe Text
-    exportStatementName (JSAssignStatement (JSMemberDot (JSIdentifier _ "exports") _ (JSIdentifier _ i)) _ _ _) = Just . T.pack $ i
-    exportStatementName (JSAssignStatement (JSMemberSquare (JSIdentifier _ "exports") _ (JSStringLiteral _ i) _) _ _ _) = Just . unquote . T.pack $ i
-    exportStatementName _ = Nothing
+toplevelNames :: JSStatement -> [Text]
+toplevelNames (JSFunction _ (JSIdentName _ name) _ _ _ _ _) = [T.pack name]
+toplevelNames (JSConstant _ exprs _) = catMaybes $ foldrJSCommaList (\name acc -> jsIdent name : acc) exprs []
+toplevelNames _ = []
 
-    -- Check if (export) identifier is used within a JSStatement.
-    isUsedInStmt :: Text -> JSStatement -> Bool
-    isUsedInStmt n (JSStatementBlock _ ss _ _) = any (isUsedInStmt n) ss
-    isUsedInStmt n (JSLet _ es _) = isUsedInExprs n es
-    isUsedInStmt n (JSClass _ _ h _ cs _ _) =
-      isUsedInClassHeritage n h || any (isUsedInClassElement n) cs
-    isUsedInStmt n (JSDoWhile _ stm _ _ e _ _) = isUsedInStmt n stm || isUsedInExpr n e
-    isUsedInStmt n (JSFor _ _ es1 _ es2 _ es3 _ s) = isUsedInExprs n es1 || isUsedInExprs n es2 || isUsedInExprs n es3 || isUsedInStmt n s
-    isUsedInStmt n (JSForIn _ _ e1 _ e2 _ s) = isUsedInExpr n e1 || isUsedInExpr n e2 || isUsedInStmt n s
-    isUsedInStmt n (JSForVar _ _ _ es1 _ es2 _ es3 _ s) = isUsedInExprs n es1 || isUsedInExprs n es2 || isUsedInExprs n es3 || isUsedInStmt n s
-    isUsedInStmt n (JSForVarIn _ _ _ e1 _ e2 _ s) = isUsedInExpr n e1 || isUsedInExpr n e2 || isUsedInStmt n s
-    isUsedInStmt n (JSForLet _ _ _ es1 _ es2 _ es3 _ s) =
-      isUsedInExprs n es1 || isUsedInExprs n es2 || isUsedInExprs n es3 ||
-      isUsedInStmt n s
-    isUsedInStmt n (JSForLetIn _ _ _ e1 _ e2 _ s) =
-      isUsedInExpr n e1 || isUsedInExpr n e2 || isUsedInStmt n s
-    isUsedInStmt n (JSForLetOf _ _ _ e1 _ e2 _ s) =
-      isUsedInExpr n e1 || isUsedInExpr n e2 || isUsedInStmt n s
-    isUsedInStmt n (JSForConst _ _ _ es1 _ es2 _ es3 _ s) =
-      isUsedInExprs n es1 || isUsedInExprs n es2 || isUsedInExprs n es3 || isUsedInStmt n s
-    isUsedInStmt n (JSForConstIn _ _ _ e1 _ e2 _ s) =
-      isUsedInExpr n e1 || isUsedInExpr n e2 || isUsedInStmt n s
-    isUsedInStmt n (JSForConstOf _ _ _ e1 _ e2 _ s) =
-      isUsedInExpr n e1 || isUsedInExpr n e2 || isUsedInStmt n s
-    isUsedInStmt n (JSForOf _ _ e1 _ e2 _ s) =
-      isUsedInExpr n e1 || isUsedInExpr n e2 || isUsedInStmt n s
-    isUsedInStmt n (JSForVarOf _ _ _ e1 _ e2 _ s) =
-      isUsedInExpr n e1 || isUsedInExpr n e2 || isUsedInStmt n s
-    isUsedInStmt n (JSAsyncFunction _ _ _ _ es _ (JSBlock _ ss _) _) =
-      isUsedInExprs n es || any (isUsedInStmt n) ss
-    isUsedInStmt n (JSFunction _ _ _ es _ (JSBlock _ ss _) _) =
-      isUsedInExprs n es || any (isUsedInStmt n) ss
-    isUsedInStmt n (JSGenerator _ _ _ _ es _ (JSBlock _ ss _) _) =
-      isUsedInExprs n es || any (isUsedInStmt n) ss
-    isUsedInStmt n (JSIf _ _ e _ s) = isUsedInExpr n e || isUsedInStmt n s
-    isUsedInStmt n (JSIfElse _ _ e _ s1 _ s2) = isUsedInExpr n e || isUsedInStmt n s1 || isUsedInStmt n s2
-    isUsedInStmt n (JSLabelled _ _ s) = isUsedInStmt n s
-    isUsedInStmt _ (JSEmptyStatement _) = False
-    isUsedInStmt n (JSExpressionStatement e _) = isUsedInExpr n e
-    isUsedInStmt n (JSAssignStatement e1 _ e2 _) = isUsedInExpr n e1 || isUsedInExpr n e2
-    isUsedInStmt n (JSMethodCall e _ es _ _) = isUsedInExpr n e || isUsedInExprs n es
-    isUsedInStmt n (JSReturn _ me _) = fromMaybe False (isUsedInExpr n <$> me)
-    isUsedInStmt n (JSSwitch _ _ e _ _ sps _ _) = isUsedInExpr n e || any (isUsedInSwitchParts n) sps
-    isUsedInStmt n (JSThrow _ e _) = isUsedInExpr n e
-    isUsedInStmt n (JSTry _ (JSBlock _ ss _) cs f) = any (isUsedInStmt n) ss || any (isUsedInTryCatch n) cs || isUsedInFinally n f
-    isUsedInStmt n (JSVariable _ es _) = isUsedInExprs n es
-    isUsedInStmt n (JSWhile _ _ e _ s) = isUsedInExpr n e || isUsedInStmt n s
-    isUsedInStmt n (JSWith _ _ e _ s _) = isUsedInExpr n e || isUsedInStmt n s
-    isUsedInStmt _ JSBreak{} = False
-    isUsedInStmt _ JSConstant{} = False
-    isUsedInStmt _ JSContinue{} = False
+exportName' :: JSExportSpecifier -> Maybe Text
+exportName' (JSExportSpecifier (JSIdentName _ name)) = Just $ T.pack name
+exportName' _ = Nothing
 
-    -- Check if an exported identifier is used within a 'JSExpression'
-    isUsedInExpr :: Text -> JSExpression -> Bool
-    isUsedInExpr n (JSMemberDot (JSIdentifier _ "exports") _ (JSIdentifier _ i)) = n == T.pack i
-    isUsedInExpr n (JSMemberDot e1 _ e2) = isUsedInExpr n e1 || isUsedInExpr n e2
-    isUsedInExpr n (JSArrayLiteral _ as _) = any (isUsedInArrayElement n) as
-    isUsedInExpr n (JSAssignExpression e1 _ e2) = isUsedInExpr n e1 || isUsedInExpr n e2
-    isUsedInExpr n (JSAwaitExpression _ e) = isUsedInExpr n e
-    isUsedInExpr n (JSCallExpression e _ es _) = isUsedInExpr n e || isUsedInExprs n es
-    isUsedInExpr n (JSCallExpressionDot e1 _ e2) = isUsedInExpr n e1 || isUsedInExpr n e2
-    isUsedInExpr n (JSCallExpressionSquare e1 _ e2 _) = isUsedInExpr n e1 || isUsedInExpr n e2
-    isUsedInExpr n (JSClassExpression _ _ h _ cs _) =
-      isUsedInClassHeritage n h || any (isUsedInClassElement n) cs
-    isUsedInExpr n (JSExpressionBinary e1 _ e2) = isUsedInExpr n e1 || isUsedInExpr n e2
-    isUsedInExpr n (JSExpressionParen _ e _) = isUsedInExpr n e
-    isUsedInExpr n (JSExpressionPostfix e _) = isUsedInExpr n e
-    isUsedInExpr n (JSExpressionTernary e1 _ e2 _ e3) = isUsedInExpr n e1 || isUsedInExpr n e2 || isUsedInExpr n e3
-    isUsedInExpr n (JSArrowExpression _ _ s) = isUsedInStmt n s
-    isUsedInExpr n (JSFunctionExpression _ _ _ _ _ (JSBlock _ ss _)) = any (isUsedInStmt n) ss
-    isUsedInExpr n (JSGeneratorExpression _ _ _ _ es _ (JSBlock _ ss _)) =
-      isUsedInExprs n es || any (isUsedInStmt n) ss
-    isUsedInExpr n (JSMemberExpression e _ es _) = isUsedInExpr n e || isUsedInExprs n es
-    isUsedInExpr n (JSMemberNew _ e _ es _) = isUsedInExpr n e || isUsedInExprs n es
-    isUsedInExpr n (JSMemberSquare (JSIdentifier _ "exports") _ (JSStringLiteral _ i) _) = n == (unquote .T.pack $ i)
-    isUsedInExpr n (JSMemberSquare e1 _ e2 _) = isUsedInExpr n e1 || isUsedInExpr n e2
-    isUsedInExpr n (JSNewExpression _ e) = isUsedInExpr n e
-    isUsedInExpr n (JSObjectLiteral _ ops _) = foldrJSCommaList (\p b -> isUsedInObjectProperty n p || b) (fromCTList ops) False
-      where
-      fromCTList (JSCTLComma as _) = as
-      fromCTList (JSCTLNone as) = as
-    isUsedInExpr n (JSSpreadExpression _ e) = isUsedInExpr n e
-    isUsedInExpr n (JSTemplateLiteral me _ _ tps) =
-      any (isUsedInExpr n) me || any (\(JSTemplatePart e _ _) -> isUsedInExpr n e) tps
-    isUsedInExpr n (JSUnaryExpression _ e) = isUsedInExpr n e
-    isUsedInExpr n (JSVarInitExpression e _) = isUsedInExpr n e
-    isUsedInExpr _ JSIdentifier{} = False
-    isUsedInExpr _ JSDecimal{} = False
-    isUsedInExpr _ JSLiteral{} = False
-    isUsedInExpr _ JSHexInteger{} = False
-    isUsedInExpr _ JSOctal{} = False
-    isUsedInExpr _ JSStringLiteral{} = False
-    isUsedInExpr _ JSRegEx{} = False
-    isUsedInExpr n (JSCommaExpression e1 _ e2) = isUsedInExpr n e1 || isUsedInExpr n e2
-    isUsedInExpr n (JSYieldExpression _ me) = any (isUsedInExpr n) me
-    isUsedInExpr n (JSYieldFromExpression _ _ e) = isUsedInExpr n e
+jsIdent :: JSExpression -> Maybe Text
+jsIdent (JSVarInitExpression ident _) = jsIdent ident
+jsIdent (JSIdentifier _ i) = Just $ T.pack i
+jsIdent (JSStringLiteral _ s) = Just $ T.pack s
+jsIdent _ = Nothing
 
-    isUsedInExprs :: Text -> JSCommaList JSExpression -> Bool
-    isUsedInExprs n es = foldrJSCommaList fn es False
-      where
-      fn :: JSExpression -> Bool -> Bool
-      fn e b = isUsedInExpr n e || b
+exportNames :: JSExportDeclaration -> [Text]
+exportNames (JSExportLocals (JSExportClause _ names _) _) = catMaybes $ foldrJSCommaList (\ex exs -> exportName' ex : exs) names []
+exportNames (JSExportFrom (JSExportClause _ names _) _ _) = catMaybes $ foldrJSCommaList (\ex exs -> exportName' ex : exs) names []
+exportNames (JSExport (JSConstant _ names _) _) = catMaybes $ foldrJSCommaList (\name nms -> jsIdent name : nms) names []
+exportNames (JSExport (JSFunction _ (JSIdentName _ name) _ _ _ _ _) _) = [T.pack name]
+exportNames _ = []
 
-    -- Check if (export) identifier is used withing a JSSitchParts
-    isUsedInSwitchParts :: Text -> JSSwitchParts -> Bool
-    isUsedInSwitchParts n (JSCase _ e _ ss) = isUsedInExpr n e || any (isUsedInStmt n) ss
-    isUsedInSwitchParts n (JSDefault _ _ ss) = any (isUsedInStmt n) ss
+isExportModuleItem :: JSModuleItem -> Bool
+isExportModuleItem (JSModuleExportDeclaration _ _) = True
+isExportModuleItem _ = False
 
-    -- Check if (export) identifier is used withing a JSTryCatch
-    isUsedInTryCatch :: Text -> JSTryCatch -> Bool
-    isUsedInTryCatch n (JSCatch _ _ e _ (JSBlock _ ss _)) = isUsedInExpr n e || any (isUsedInStmt n) ss
-    isUsedInTryCatch n (JSCatchIf _ _ e1 _ e2 _ (JSBlock _ ss _)) = isUsedInExpr n e1 || isUsedInExpr n e2 || any (isUsedInStmt n) ss
+exportedModuleItemName :: JSModuleItem -> [Text]
+exportedModuleItemName (JSModuleExportDeclaration _ jsModuleExport) = exportNames jsModuleExport
+exportedModuleItemName _ = []
 
-    -- |
-    -- Check if (export) identifier is used withing a JSTryFinally
-    isUsedInFinally :: Text -> JSTryFinally -> Bool
-    isUsedInFinally n (JSFinally _ (JSBlock _ ss _)) = any (isUsedInStmt n) ss
-    isUsedInFinally _ JSNoFinally = False
+isNameUsedByItem :: Text -> JSModuleItem -> Bool
+isNameUsedByItem _ (JSModuleImportDeclaration _ _) = False
+isNameUsedByItem _ (JSModuleExportDeclaration _ (JSExportFrom _ _ _)) = False
+isNameUsedByItem name (JSModuleExportDeclaration _ decl@(JSExportLocals _ _)) = name `elem` exportNames decl
+isNameUsedByItem name (JSModuleExportDeclaration _ (JSExport statement _)) = isNameUsedByStatement name statement
+isNameUsedByItem name (JSModuleStatementListItem stmt) = isNameUsedByStatement name stmt
 
-    -- |
-    -- Check if (export) identifier is used withing a JSArrayElement
-    isUsedInArrayElement :: Text -> JSArrayElement -> Bool
-    isUsedInArrayElement n (JSArrayElement e) = isUsedInExpr n e
-    isUsedInArrayElement _ JSArrayComma{} = False
+isNameUsedByStatement :: Text -> JSStatement -> Bool
+isNameUsedByStatement name (JSStatementBlock _ block _ _) =
+  any (isNameUsedByStatement name) block
+isNameUsedByStatement _ (JSBreak _ _ _) = False
+isNameUsedByStatement name (JSLet _ exprs _) =
+  any (isNameUsedByExpr name) (toList exprs)
+isNameUsedByStatement name (JSClass _ _ heritage _ cls _ _) =
+  isNameUsedInHeritage name heritage || any (isNameUsedByClassElement name) cls
+isNameUsedByStatement name (JSConstant _ exprs _) =
+  any (isNameUsedByExpr name) (toList exprs)
+isNameUsedByStatement _ (JSContinue _ _ _) = False
+isNameUsedByStatement name (JSDoWhile _ stmt _ _ expr _ _) =
+  isNameUsedByStatement name stmt || isNameUsedByExpr name expr
+isNameUsedByStatement name (JSFor _ _ exprs1 _ exprs2 _ exprs3 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) (toList exprs1 ++ toList exprs2 ++ toList exprs3)
+isNameUsedByStatement name (JSForIn _ _ expr1 _ expr2 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) [expr1, expr2]
+isNameUsedByStatement name (JSForVar _ _ _ exprs1 _ exprs2 _ exprs3 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) (toList exprs1 ++ toList exprs2 ++ toList exprs3)
+isNameUsedByStatement name (JSForLet _ _ _ exprs1 _ exprs2 _ exprs3 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) (toList exprs1 ++ toList exprs2 ++ toList exprs3)
+isNameUsedByStatement name (JSForConst _ _ _ exprs1 _ exprs2 _ exprs3 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) (toList exprs1 ++ toList exprs2 ++ toList exprs3)
+isNameUsedByStatement name (JSForVarIn _ _ _ expr1 _ expr2 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) [expr1, expr2]
+isNameUsedByStatement name (JSForLetIn _ _ _ expr1 _ expr2 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) [expr1, expr2]
+isNameUsedByStatement name (JSForConstIn _ _ _ expr1 _ expr2 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) [expr1, expr2]
+isNameUsedByStatement name (JSForVarOf _ _ _ expr1 _ expr2 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) [expr1, expr2]
+isNameUsedByStatement name (JSForLetOf _ _ _ expr1 _ expr2 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) [expr1, expr2]
+isNameUsedByStatement name (JSForConstOf _ _ _ expr1 _ expr2 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) [expr1, expr2]
+isNameUsedByStatement name (JSForOf _ _ expr1 _ expr2 _ stmt) =
+  isNameUsedByStatement name stmt || any (isNameUsedByExpr name) [expr1, expr2]
+isNameUsedByStatement name (JSFunction _ _ _ exprs _ (JSBlock _ stmts _) _) =
+  any (isNameUsedByExpr name) (toList exprs) || any (isNameUsedByStatement name) stmts
+isNameUsedByStatement name (JSGenerator _ _ _ _ exprs _ (JSBlock _ stmts _) _) =
+  any (isNameUsedByExpr name) (toList exprs) || any (isNameUsedByStatement name) stmts
+isNameUsedByStatement name (JSIf _ _ expr _ stmt) = isNameUsedByExpr name expr || isNameUsedByStatement name stmt
+isNameUsedByStatement name (JSIfElse _ _ expr _ stmtIf _ stmtElse) =
+  isNameUsedByExpr name expr || any (isNameUsedByStatement name) [stmtIf, stmtElse]
+isNameUsedByStatement name (JSLabelled _ _ stmt) = isNameUsedByStatement name stmt
+isNameUsedByStatement _ (JSEmptyStatement _) = False
+isNameUsedByStatement name (JSExpressionStatement expr _) = isNameUsedByExpr name expr
+isNameUsedByStatement name (JSAssignStatement lhs _ rhs _) = any (isNameUsedByExpr name) [lhs, rhs]
+isNameUsedByStatement name (JSMethodCall expr _ exprs _ _) = any (isNameUsedByExpr name) (expr : toList exprs)
+isNameUsedByStatement _ (JSReturn _ Nothing _) = False
+isNameUsedByStatement name (JSReturn _ (Just expr) _) = isNameUsedByExpr name expr
+isNameUsedByStatement name (JSSwitch _ _ expr _ _ cases _ _) =
+  isNameUsedByExpr name expr || any (isNameUsedByCase name) cases
+isNameUsedByStatement name (JSThrow _ expr _) = isNameUsedByExpr name expr
+isNameUsedByStatement name (JSTry _ (JSBlock _ stmts _) tryCatch tryFinally) =
+  any (isNameUsedByStatement name) stmts || any (isNameUsedByTryCatch name) tryCatch || isNameUsedByTryFinally name tryFinally
+isNameUsedByStatement name (JSVariable _ exprs _) = any (isNameUsedByExpr name) (toList exprs)
+isNameUsedByStatement name (JSWhile _ _ expr _ stmt) = isNameUsedByExpr name expr || isNameUsedByStatement name stmt
+isNameUsedByStatement name (JSWith _ _ expr _ stmt _) = isNameUsedByExpr name expr || isNameUsedByStatement name stmt
 
-    -- |
-    -- Check if (export) identifier is used withing a JSObjectProperty
-    isUsedInObjectProperty :: Text -> JSObjectProperty -> Bool
-    isUsedInObjectProperty n (JSPropertyNameandValue _ _ es) = any (isUsedInExpr n) es
-    isUsedInObjectProperty _ JSPropertyIdentRef{} = False
-    isUsedInObjectProperty n (JSObjectMethod m) = isUsedInMethodDefinition n m
+isNameUsedByExpr :: Text -> JSExpression -> Bool
+isNameUsedByExpr name (JSIdentifier _ ident) = name == T.pack ident
+isNameUsedByExpr _ (JSDecimal _ _) = False
+isNameUsedByExpr _ (JSLiteral _ _) = False
+isNameUsedByExpr _ (JSHexInteger _ _) = False
+isNameUsedByExpr _ (JSOctal _ _) = False
+isNameUsedByExpr _ (JSStringLiteral _ _) = False
+isNameUsedByExpr _ (JSRegEx _ _) = False
+isNameUsedByExpr name (JSArrayLiteral _ elems _) = any (isNameUsedByArrayElement name) elems
+isNameUsedByExpr name (JSAssignExpression lhs _ rhs) = any (isNameUsedByExpr name) [lhs, rhs]
+isNameUsedByExpr name (JSCallExpression expr _ exprs _) = any (isNameUsedByExpr name) (expr : toList exprs)
+isNameUsedByExpr name (JSCallExpressionDot expr1 _ expr2) = any (isNameUsedByExpr name) [expr1, expr2]
+isNameUsedByExpr name (JSCallExpressionSquare expr1 _ expr2 _) = any (isNameUsedByExpr name) [expr1, expr2]
+isNameUsedByExpr name (JSClassExpression _ _ heritage _ cls _) =
+  isNameUsedInHeritage name heritage || any (isNameUsedByClassElement name) cls
+isNameUsedByExpr name (JSCommaExpression expr1 _ expr2) = isNameUsedByExpr name expr1 || isNameUsedByExpr name expr2
+isNameUsedByExpr name (JSExpressionBinary expr1 _ expr2) = isNameUsedByExpr name expr1 || isNameUsedByExpr name expr2
+isNameUsedByExpr name (JSExpressionParen _ expr _) = isNameUsedByExpr name expr
+isNameUsedByExpr name (JSExpressionPostfix expr _) = isNameUsedByExpr name expr
+isNameUsedByExpr name (JSExpressionTernary cond _ yes _ no) = any (isNameUsedByExpr name) [cond, yes, no]
+isNameUsedByExpr name (JSArrowExpression _ _ stmt) = isNameUsedByStatement name stmt
+isNameUsedByExpr name (JSFunctionExpression _ _ _ exprs _ (JSBlock _ stmts _)) =
+  any (isNameUsedByExpr name) (toList exprs) || any (isNameUsedByStatement name) stmts
+isNameUsedByExpr name (JSGeneratorExpression _ _ _ _ exprs _ (JSBlock _ stmts _)) =
+  any (isNameUsedByExpr name) (toList exprs) || any (isNameUsedByStatement name) stmts
+isNameUsedByExpr name (JSMemberDot expr1 _ expr2) = isNameUsedByExpr name expr1 || isNameUsedByExpr name expr2
+isNameUsedByExpr name (JSMemberExpression expr _ exprs _) = any (isNameUsedByExpr name) (expr : toList exprs)
+isNameUsedByExpr name (JSMemberNew _ expr _ exprs _) = any (isNameUsedByExpr name) (expr : toList exprs)
+isNameUsedByExpr name (JSMemberSquare expr1 _ expr2 _) = isNameUsedByExpr name expr1 || isNameUsedByExpr name expr2
+isNameUsedByExpr name (JSNewExpression _ expr) = isNameUsedByExpr name expr
+isNameUsedByExpr name (JSObjectLiteral _ (JSCTLComma props _) _) = any (isNameUsedByObjectProperty name) (toList props)
+isNameUsedByExpr name (JSObjectLiteral _ (JSCTLNone props) _) = any (isNameUsedByObjectProperty name) (toList props)
+isNameUsedByExpr name (JSSpreadExpression _ expr) = isNameUsedByExpr name expr
+isNameUsedByExpr name (JSTemplateLiteral Nothing _ _ parts) = any (isNameUsedByTemplatePart name) parts
+isNameUsedByExpr name (JSTemplateLiteral (Just expr) _ _ parts) = isNameUsedByExpr name expr || any (isNameUsedByTemplatePart name) parts
+isNameUsedByExpr name (JSUnaryExpression _ expr) = isNameUsedByExpr name expr
+isNameUsedByExpr name (JSVarInitExpression expr initializer) = isNameUsedByExpr name expr || isNameUsedByVarInit name initializer
+isNameUsedByExpr _ (JSYieldExpression _ Nothing) = False
+isNameUsedByExpr name (JSYieldExpression _ (Just expr)) = isNameUsedByExpr name expr
+isNameUsedByExpr name (JSYieldFromExpression _ _ expr) = isNameUsedByExpr name expr
 
-    isUsedInMethodDefinition :: Text -> JSMethodDefinition -> Bool
-    isUsedInMethodDefinition n (JSMethodDefinition _ _ es _ (JSBlock _ ss _))
-      = isUsedInExprs n es || any (isUsedInStmt n) ss
-    isUsedInMethodDefinition n (JSGeneratorMethodDefinition _ _ _ es _ (JSBlock _ ss _))
-      = isUsedInExprs n es || any (isUsedInStmt n) ss
-    isUsedInMethodDefinition n (JSPropertyAccessor _ _ _ es _ (JSBlock _ ss _))
-      = isUsedInExprs n es || any (isUsedInStmt n) ss
+isNameUsedInHeritage :: Text -> JSClassHeritage -> Bool
+isNameUsedInHeritage _ JSExtendsNone = False
+isNameUsedInHeritage name (JSExtends _ expr) = isNameUsedByExpr name expr
 
-    isUsedInClassElement :: Text -> JSClassElement -> Bool
-    isUsedInClassElement n (JSClassInstanceMethod m) = isUsedInMethodDefinition n m
-    isUsedInClassElement n (JSClassStaticMethod _ m) = isUsedInMethodDefinition n m
-    isUsedInClassElement _ JSClassSemi{}             = False
+isNameUsedByClassElement :: Text -> JSClassElement -> Bool
+isNameUsedByClassElement _ (JSClassSemi _) = False
+isNameUsedByClassElement name (JSClassInstanceMethod defn) = isNameUsedByMethodDefinition name defn
+isNameUsedByClassElement name (JSClassStaticMethod _ defn) = isNameUsedByMethodDefinition name defn
 
-    isUsedInClassHeritage :: Text -> JSClassHeritage -> Bool
-    isUsedInClassHeritage n (JSExtends _ e) = isUsedInExpr n e
-    isUsedInClassHeritage _ JSExtendsNone   = False
+isNameUsedByMethodDefinition :: Text -> JSMethodDefinition -> Bool
+isNameUsedByMethodDefinition name (JSMethodDefinition _ _ exprs _ _) = any (isNameUsedByExpr name) (toList exprs)
+isNameUsedByMethodDefinition name (JSGeneratorMethodDefinition _ _ _ exprs _ _) = any (isNameUsedByExpr name) (toList exprs)
+isNameUsedByMethodDefinition name (JSPropertyAccessor _ _ _ exprs _ _) = any (isNameUsedByExpr name) (toList exprs)
+
+isNameUsedByCase :: Text -> JSSwitchParts -> Bool
+isNameUsedByCase name (JSDefault _ _ stmts) = any (isNameUsedByStatement name) stmts
+isNameUsedByCase name (JSCase _ expr _ stmts) = isNameUsedByExpr name expr || any (isNameUsedByStatement name) stmts
+
+isNameUsedByTryCatch :: Text -> JSTryCatch -> Bool
+isNameUsedByTryCatch name (JSCatch _ _ expr _ (JSBlock _ stmts _)) = isNameUsedByExpr name expr || any (isNameUsedByStatement name) stmts
+isNameUsedByTryCatch name (JSCatchIf _ _ expr1 _ expr2 _ (JSBlock _ stmts _)) = any (isNameUsedByExpr name) [expr1, expr2] || any (isNameUsedByStatement name) stmts
+
+isNameUsedByTryFinally :: Text -> JSTryFinally -> Bool
+isNameUsedByTryFinally name (JSFinally _ (JSBlock _ stmts _)) = any (isNameUsedByStatement name) stmts
+isNameUsedByTryFinally _ JSNoFinally = False
+
+isNameUsedByArrayElement :: Text -> JSArrayElement -> Bool
+isNameUsedByArrayElement _ (JSArrayComma _) = False
+isNameUsedByArrayElement name (JSArrayElement expr) = isNameUsedByExpr name expr
+
+isNameUsedByObjectProperty :: Text -> JSObjectProperty -> Bool
+isNameUsedByObjectProperty name (JSPropertyNameandValue _ _ exprs) = any (isNameUsedByExpr name) exprs
+isNameUsedByObjectProperty name (JSPropertyIdentRef _ ref) = name == T.pack ref
+isNameUsedByObjectProperty name (JSObjectMethod mDef) = isNameUsedByMethodDefinition name mDef
+
+isNameUsedByTemplatePart :: Text -> JSTemplatePart -> Bool
+isNameUsedByTemplatePart name (JSTemplatePart expr _ _) = isNameUsedByExpr name expr
+
+isNameUsedByVarInit :: Text -> JSVarInitializer -> Bool
+isNameUsedByVarInit name (JSVarInit _ expr) = isNameUsedByExpr name expr
+isNameUsedByVarInit _ JSVarInitNone = False
diff --git a/src/Language/PureScript/DCE/Utils.hs b/src/Language/PureScript/DCE/Utils.hs
--- a/src/Language/PureScript/DCE/Utils.hs
+++ b/src/Language/PureScript/DCE/Utils.hs
@@ -2,7 +2,6 @@
 -- Helper module for dead call elimination.
 module Language.PureScript.DCE.Utils where
 
-import           Prelude.Compat
 import           Control.Arrow (first, second, (***), (+++))
 import           Language.PureScript.CoreFn
 import           Language.PureScript.Names
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,31 +1,27 @@
 {-# LANGUAGE CPP #-}
-module Main (main) where
 
-import           Prelude ()
-import           Prelude.Compat hiding (exp)
-import           System.IO (hSetEncoding, stdout, stderr, utf8)
-import           System.Process (readProcess)
-import           Test.Hspec
+module Main (main) where
 
+import System.IO (hSetEncoding, stderr, stdout, utf8)
 import qualified Test.CoreFn
 import qualified Test.Eval
+import Test.Hspec
 import qualified Test.Lib
--- TODO: it shouldn't be a CPP FLAG
 #ifdef TEST_CORE_LIBS
 import qualified Test.CoreLib
 #endif
 
-
 main :: IO ()
 main = do
-  readProcess "purs" ["--version"] "" >>= putStrLn . (\v -> "\npurs version: " ++ v)
-
   hSetEncoding stdout utf8
   hSetEncoding stderr utf8
 
   hspec Test.CoreFn.spec
   hspec Test.Eval.spec
   hspec Test.Lib.spec
+
+-- Tree shaking of core libraries is disabled by default because it's not
+-- reliable.
 #ifdef TEST_CORE_LIBS
   hspec Test.CoreLib.spec
 #endif
diff --git a/test/Test/CoreFn.hs b/test/Test/CoreFn.hs
--- a/test/Test/CoreFn.hs
+++ b/test/Test/CoreFn.hs
@@ -1,16 +1,13 @@
 module Test.CoreFn (spec)  where
 
-import Prelude ()
-import Prelude.Compat
-
-import Data.List (concatMap, foldl', intersect)
+import Data.List (foldl', intersect)
 import qualified Data.List as L
 
 import Language.PureScript.AST.Literals
-import Language.PureScript.AST.SourcePos
+import Language.PureScript.AST.SourcePos (SourceSpan (..), SourcePos (..))
 import Language.PureScript.CoreFn
 import Language.PureScript.DCE
-import Language.PureScript.Names
+import Language.PureScript.Names (Ident (..), Qualified (..), QualifiedBy (..))
 import Language.PureScript.PSString
 
 import Test.Hspec
@@ -26,13 +23,18 @@
 hasIdent :: Ident -> [Bind Ann] -> Bool
 hasIdent i = (i `elem`) . concatMap getNames
 
+sp :: SourcePos
+sp = SourcePos 0 0
+
 ann :: Ann
-ann = ssAnn (SourceSpan "src/Test.purs" (SourcePos 0 0) (SourcePos 0 0))
+ann = ssAnn (SourceSpan "src/Test.purs" sp sp)
 
 prop_exprDepth :: PSExpr Ann -> Property
 prop_exprDepth (PSExpr e) =
   let b = NonRec ann (Ident "x") e
-      NonRec _ _ e' = runBindDeadCodeElimination b
+      e' = case runBindDeadCodeElimination b of
+        NonRec _ _ a -> a
+        _ -> error "runBindDeadCodeElimination: recursive binds are unsupported"
       d  = exprDepth e
       d' = exprDepth e'
   in collect (10 * (d' * 100 `div` (10 * d)))
@@ -42,7 +44,9 @@
 prop_lets :: PSExpr Ann -> Property
 prop_lets (PSExpr f) =
   let b = NonRec ann (Ident "x") f
-      NonRec _ _ f' = runBindDeadCodeElimination b
+      f' = case runBindDeadCodeElimination b of
+        NonRec _ _ a -> a
+        _ -> error "runBindDeadCodeElimination: recursive binds are unsupported"
       d  = countLets f
       d' = countLets f'
       idents = findBindIdents f'
@@ -90,99 +94,100 @@
 spec = do
   context "generators" $ do
     specify "should generate Expr" $ property $ prop_exprDistribution
-  context "runBindDeadCodeElimination" $ do
-    specify "should reduce the depth of the tree" $ property $ withMaxSuccess 10000 prop_exprDepth
-    specify "should reduce the number of let bindings" $ property $ withMaxSuccess 10000 prop_lets
-    specify "should remove unused identifier" $ do
-      let e :: Expr Ann
-          e = Let ann
-                [ NonRec ann (Ident "notUsed") (Literal ann (CharLiteral 'a'))
-                , NonRec ann (Ident "used") (Literal ann (CharLiteral 'b'))
-                ]
-                (Var ann (Qualified Nothing (Ident "used")))
-      case runBindDeadCodeElimination (NonRec ann (Ident "v") e) of
-        NonRec _ _ (Let _ bs _) -> do
-          bs `shouldSatisfy` not . hasIdent (Ident "notUsed")
-          bs `shouldSatisfy` hasIdent (Ident "used")
-        _ -> return ()
+  context "corefn" $ 
+    context "runBindDeadCodeElimination" $ do
+      specify "should reduce the depth of the tree" $ property $ withMaxSuccess 10000 prop_exprDepth
+      specify "should reduce the number of let bindings" $ property $ withMaxSuccess 10000 prop_lets
+      specify "should remove unused identifier" $ do
+        let e :: Expr Ann
+            e = Let ann
+                  [ NonRec ann (Ident "notUsed") (Literal ann (CharLiteral 'a'))
+                  , NonRec ann (Ident "used") (Literal ann (CharLiteral 'b'))
+                  ]
+                  (Var ann (Qualified (BySourcePos sp) (Ident "used")))
+        case runBindDeadCodeElimination (NonRec ann (Ident "v") e) of
+          NonRec _ _ (Let _ bs _) -> do
+            bs `shouldSatisfy` not . hasIdent (Ident "notUsed")
+            bs `shouldSatisfy` hasIdent (Ident "used")
+          _ -> return ()
 
-    specify "should not remove transitive dependency" $ do
-      let e :: Expr Ann
-          e = Let ann
-                [ NonRec ann (Ident "used") (Abs ann (Ident "x") (Var ann (Qualified Nothing (Ident "trDep"))))
-                , NonRec ann (Ident "trDep") (Literal ann (CharLiteral 'a'))
-                ]
-                (Var ann (Qualified Nothing (Ident "used")))
-      case runBindDeadCodeElimination (NonRec ann (Ident "v") e) of
-        NonRec _ _ (Let _ bs _) -> do
-          bs `shouldSatisfy` hasIdent (Ident "trDep")
-          bs `shouldSatisfy` hasIdent (Ident "used")
-        _ -> return ()
+      specify "should not remove transitive dependency" $ do
+        let e :: Expr Ann
+            e = Let ann
+                  [ NonRec ann (Ident "used") (Abs ann (Ident "x") (Var ann (Qualified (BySourcePos sp) (Ident "trDep"))))
+                  , NonRec ann (Ident "trDep") (Literal ann (CharLiteral 'a'))
+                  ]
+                  (Var ann (Qualified (BySourcePos sp) (Ident "used")))
+        case runBindDeadCodeElimination (NonRec ann (Ident "v") e) of
+          NonRec _ _ (Let _ bs _) -> do
+            bs `shouldSatisfy` hasIdent (Ident "trDep")
+            bs `shouldSatisfy` hasIdent (Ident "used")
+          _ -> return ()
 
-    specify "should include all used recursive binds" $ do
-      let e :: Expr Ann
-          e = Let ann
-                [ NonRec ann (Ident "entry") (Abs ann (Ident "x") (Var ann (Qualified Nothing (Ident "mutDep1"))))
-                , Rec
-                  [ ((ann, Ident "mutDep1"), Abs ann (Ident "x") (Var ann (Qualified Nothing (Ident "mutDep2"))))
-                  , ((ann, Ident "mutDep2"), Abs ann (Ident "x") (Var ann (Qualified Nothing (Ident "mutDep1"))))
+      specify "should include all used recursive binds" $ do
+        let e :: Expr Ann
+            e = Let ann
+                  [ NonRec ann (Ident "entry") (Abs ann (Ident "x") (Var ann (Qualified (BySourcePos sp) (Ident "mutDep1"))))
+                  , Rec
+                    [ ((ann, Ident "mutDep1"), Abs ann (Ident "x") (Var ann (Qualified (BySourcePos sp) (Ident "mutDep2"))))
+                    , ((ann, Ident "mutDep2"), Abs ann (Ident "x") (Var ann (Qualified (BySourcePos sp) (Ident "mutDep1"))))
+                    ]
                   ]
-                ]
-                (App ann (Var ann (Qualified Nothing (Ident "entry"))) (Literal ann (CharLiteral 'a')))
-      case runBindDeadCodeElimination (NonRec ann (Ident "v") e) of
-        NonRec _ _ (Let _ bs _) -> do
-          bs `shouldSatisfy` hasIdent (Ident "entry")
-          bs `shouldSatisfy` hasIdent (Ident "mutDep1")
-          bs `shouldSatisfy` hasIdent (Ident "mutDep2")
-        _ -> return ()
+                  (App ann (Var ann (Qualified (BySourcePos sp) (Ident "entry"))) (Literal ann (CharLiteral 'a')))
+        case runBindDeadCodeElimination (NonRec ann (Ident "v") e) of
+          NonRec _ _ (Let _ bs _) -> do
+            bs `shouldSatisfy` hasIdent (Ident "entry")
+            bs `shouldSatisfy` hasIdent (Ident "mutDep1")
+            bs `shouldSatisfy` hasIdent (Ident "mutDep2")
+          _ -> return ()
 
-    specify "should dce case expressions" $ do
-      let e :: Expr Ann
-          e = Let ann
-                [ NonRec ann (Ident "usedInExpr") (Literal ann (CharLiteral 'a'))
-                , NonRec ann (Ident "notUsed") (Literal ann (CharLiteral 'a'))
-                , NonRec ann (Ident "usedInGuard") (Literal ann (CharLiteral 'a'))
-                , NonRec ann (Ident "usedInResult1") (Literal ann (CharLiteral 'a'))
-                , NonRec ann (Ident "usedInResult2") (Literal ann (CharLiteral 'a'))
-                ]
-                (Case ann
-                  [Var ann (Qualified Nothing (Ident "usedInExpr"))]
-                  [ CaseAlternative
-                      [NullBinder ann]
-                      (Left
-                        [ ( Var ann (Qualified Nothing (Ident "usedInGuard"))
-                          , Var ann (Qualified Nothing (Ident "usedInResult1"))
-                          )
-                        ])
-                  , CaseAlternative
-                      [NullBinder ann]
-                      (Right $ Var ann (Qualified Nothing (Ident "usedInResult2")))
-                  ])
-      case runBindDeadCodeElimination (NonRec ann (Ident "v") e) of
-        NonRec _ _ (Let _ bs _) -> do
-          bs `shouldSatisfy` hasIdent (Ident "usedInExpr")
-          bs `shouldSatisfy` not . hasIdent (Ident "notUsed")
-          bs `shouldSatisfy` hasIdent (Ident "usedInGuard")
-          bs `shouldSatisfy` hasIdent (Ident "usedInResult1")
-          bs `shouldSatisfy` hasIdent (Ident "usedInResult2")
-        _ -> return ()
+      specify "should dce case expressions" $ do
+        let e :: Expr Ann
+            e = Let ann
+                  [ NonRec ann (Ident "usedInExpr") (Literal ann (CharLiteral 'a'))
+                  , NonRec ann (Ident "notUsed") (Literal ann (CharLiteral 'a'))
+                  , NonRec ann (Ident "usedInGuard") (Literal ann (CharLiteral 'a'))
+                  , NonRec ann (Ident "usedInResult1") (Literal ann (CharLiteral 'a'))
+                  , NonRec ann (Ident "usedInResult2") (Literal ann (CharLiteral 'a'))
+                  ]
+                  (Case ann
+                    [Var ann (Qualified (BySourcePos sp) (Ident "usedInExpr"))]
+                    [ CaseAlternative
+                        [NullBinder ann]
+                        (Left
+                          [ ( Var ann (Qualified (BySourcePos sp) (Ident "usedInGuard"))
+                            , Var ann (Qualified (BySourcePos sp) (Ident "usedInResult1"))
+                            )
+                          ])
+                    , CaseAlternative
+                        [NullBinder ann]
+                        (Right $ Var ann (Qualified (BySourcePos sp) (Ident "usedInResult2")))
+                    ])
+        case runBindDeadCodeElimination (NonRec ann (Ident "v") e) of
+          NonRec _ _ (Let _ bs _) -> do
+            bs `shouldSatisfy` hasIdent (Ident "usedInExpr")
+            bs `shouldSatisfy` not . hasIdent (Ident "notUsed")
+            bs `shouldSatisfy` hasIdent (Ident "usedInGuard")
+            bs `shouldSatisfy` hasIdent (Ident "usedInResult1")
+            bs `shouldSatisfy` hasIdent (Ident "usedInResult2")
+          _ -> return ()
 
-    specify "should not remove shadowed identifiers" $ do
-      let e :: Expr Ann
-          e = Let ann
-                [ NonRec ann (Ident "shadow") (Literal ann (CharLiteral 'a'))
-                , NonRec ann (Ident "sunny") (Literal ann (CharLiteral 'a'))
-                ]
-                $ Let ann
-                  [ NonRec ann (Ident "shadow") (Literal ann (CharLiteral 'a')) ]
-                  $ Literal ann
-                    $ ObjectLiteral 
-                      [ ( mkString "a", Var ann (Qualified Nothing (Ident "shadow")) )
-                      , ( mkString "b", Var ann (Qualified Nothing (Ident "sunny")) )
-                      ]
-      case runBindDeadCodeElimination (NonRec ann (Ident "v") e) of
-        NonRec _ _ (Let _ bs (Let _ cs _)) -> do
-          bs `shouldSatisfy` hasIdent (Ident "sunny")
-          bs `shouldSatisfy` not . hasIdent (Ident "shadow")
-          cs `shouldSatisfy` hasIdent (Ident "shadow")
-        _ -> undefined
+      specify "should not remove shadowed identifiers" $ do
+        let e :: Expr Ann
+            e = Let ann
+                  [ NonRec ann (Ident "shadow") (Literal ann (CharLiteral 'a'))
+                  , NonRec ann (Ident "sunny") (Literal ann (CharLiteral 'a'))
+                  ]
+                  $ Let ann
+                    [ NonRec ann (Ident "shadow") (Literal ann (CharLiteral 'a')) ]
+                    $ Literal ann
+                      $ ObjectLiteral 
+                        [ ( mkString "a", Var ann (Qualified (BySourcePos sp) (Ident "shadow")) )
+                        , ( mkString "b", Var ann (Qualified (BySourcePos sp) (Ident "sunny")) )
+                        ]
+        case runBindDeadCodeElimination (NonRec ann (Ident "v") e) of
+          NonRec _ _ (Let _ bs (Let _ cs _)) -> do
+            bs `shouldSatisfy` hasIdent (Ident "sunny")
+            bs `shouldSatisfy` not . hasIdent (Ident "shadow")
+            cs `shouldSatisfy` hasIdent (Ident "shadow")
+          _ -> error "runBindDeadCodeElimination: recursive binds are unsupported"
diff --git a/test/Test/CoreLib.hs b/test/Test/CoreLib.hs
--- a/test/Test/CoreLib.hs
+++ b/test/Test/CoreLib.hs
@@ -1,12 +1,8 @@
 module Test.CoreLib (spec) where
 
-import           Prelude ()
-import           Prelude.Compat hiding (exp)
-import           Control.Monad (when)
 import           Control.Monad.Trans.Class
 import           Control.Monad.Except
-import           Data.Foldable (forM_)
-import           Data.Maybe (fromJust, isJust, maybe)
+import           Data.Maybe (fromJust, isJust)
 import           Data.Text (Text)
 import qualified Data.Text as T
 import           System.Directory (setCurrentDirectory)
@@ -123,7 +119,7 @@
 
   (ecNode, stdNode, errNode) <- lift
     $ readProcessWithExitCode
-        "node"
+        nodeExe
         [ "-e"
         , T.unpack $ maybe defaultJsCmd fst coreLibTestJsCmd
         ]
diff --git a/test/Test/Eval.hs b/test/Test/Eval.hs
--- a/test/Test/Eval.hs
+++ b/test/Test/Eval.hs
@@ -2,15 +2,14 @@
 
 module Test.Eval (spec) where
 
-import Prelude ()
-import Prelude.Compat
+import qualified Data.Map as Map
 
 import Language.PureScript.AST.Literals
-import Language.PureScript.AST.SourcePos
+import Language.PureScript.AST.SourcePos (SourceSpan(..), SourcePos(..))
 import Language.PureScript.CoreFn
 import Language.PureScript.DCE
 import qualified Language.PureScript.DCE.Constants as C
-import Language.PureScript.Names
+import Language.PureScript.Names (Ident(..), ModuleName(..), Qualified(..), QualifiedBy(..))
 import Language.PureScript.PSString
 
 import Language.PureScript.DCE.Utils (showExpr)
@@ -18,18 +17,20 @@
 import Test.Hspec
 import Test.HUnit (assertFailure)
 
+sp :: SourcePos
+sp = SourcePos 0 0
 
 ss :: SourceSpan
-ss = SourceSpan "src/Test.purs" (SourcePos 0 0) (SourcePos 0 0)
+ss = SourceSpan "src/Test.purs" sp sp
 
 ann :: Ann
 ann = ssAnn ss
 
 eq :: Qualified Ident
-eq = Qualified (Just C.eqMod) (Ident "eq")
+eq = Qualified (ByModuleName C.eqMod) (Ident "eq")
 
 eqBoolean :: Qualified Ident
-eqBoolean = Qualified (Just eqModName) (Ident "eqBoolean")
+eqBoolean = Qualified (ByModuleName eqModName) (Ident "eqBoolean")
 
 eqModName :: ModuleName
 eqModName = ModuleName "Data.Eq"
@@ -42,15 +43,15 @@
 
 dceEvalExpr' :: Expr Ann -> [Module Ann] -> Expr Ann
 dceEvalExpr' e mods = case evaluate ([testMod , eqMod , booleanMod , arrayMod, unsafeCoerceMod] ++ mods) of
-    ((Module _ _ _ _ _ _ _ [NonRec _ _ e', _]) : _) -> e'
+    ((Module _ _ _ _ _ _ _ _ [NonRec _ _ e', _]) : _) -> e'
     _                                               -> error "not supported"
   where
-  testMod = Module ss [] mn mp [] [] []
+  testMod = Module ss [] mn mp [] [] Map.empty []
     [ NonRec ann (Ident "v") e
     , NonRec ann (Ident "f")
-        (Abs ann (Ident "x") (Var ann (Qualified Nothing (Ident "x"))))
+        (Abs ann (Ident "x") (Var ann (Qualified (BySourcePos sp) (Ident "x"))))
     ]
-  eqMod = Module ss [] C.eqMod "" [] []
+  eqMod = Module ss [] C.eqMod "" [] [] Map.empty
     [ Ident "refEq" ]
     [ NonRec ann (Ident "eq")
         (Abs ann (Ident "dictEq")
@@ -59,26 +60,26 @@
               (Literal ann (BooleanLiteral True)))))
     , NonRec ann (Ident "eqBoolean")
         (App ann
-          (Var ann (Qualified (Just C.eqMod) (Ident "Eq")))
-          (Var ann (Qualified (Just C.eqMod) (Ident "refEq"))))
+          (Var ann (Qualified (ByModuleName C.eqMod) (Ident "Eq")))
+          (Var ann (Qualified (ByModuleName C.eqMod) (Ident "refEq"))))
     , NonRec ann (Ident "Eq")
         (Abs ann (Ident "eq")
-          (Literal ann (ObjectLiteral [(mkString "eq", Var ann (Qualified Nothing (Ident "eq")))])))
+          (Literal ann (ObjectLiteral [(mkString "eq", Var ann (Qualified (BySourcePos sp) (Ident "eq")))])))
     ]
-  booleanMod = Module ss [] (ModuleName "Data.Boolean") "" [] [] []
+  booleanMod = Module ss [] (ModuleName "Data.Boolean") "" [] [] Map.empty []
     [ NonRec ann (Ident "otherwise") (Literal ann (BooleanLiteral True)) ]
   arrayMod = Module ss [] (ModuleName "Data.Array") ""
-    [] [] []
+    [] [] Map.empty []
     [ NonRec ann (Ident "index")
         (Abs ann (Ident "as")
           (Abs ann (Ident "ix")
             (Literal ann (CharLiteral 'f'))))
     ]
   unsafeCoerceMod = Module ss [] C.unsafeCoerce ""
-    [] [] []
+    [] [] Map.empty []
     [ NonRec ann (Ident "unsafeCoerce")
         (Abs ann (Ident "x")
-          (Var ann (Qualified Nothing (Ident "x"))))
+          (Var ann (Qualified (BySourcePos sp) (Ident "x"))))
     ]
 
 dceEvalExpr :: Expr Ann -> Expr Ann
@@ -154,7 +155,7 @@
               (Literal ann (BooleanLiteral True))
           e :: Expr Ann
           e = Let ann [NonRec ann (Ident "v") v]
-                (Case ann [Var ann (Qualified Nothing (Ident "v"))]
+                (Case ann [Var ann (Qualified (BySourcePos sp) (Ident "v"))]
                   [ CaseAlternative
                       [ LiteralBinder ann (BooleanLiteral True) ]
                       (Right (Literal ann (CharLiteral 't')))
@@ -174,7 +175,7 @@
             App ann
               (App ann
                 (App ann
-                  (Var ann (Qualified (Just mn) (Ident "f")))
+                  (Var ann (Qualified (ByModuleName mn) (Ident "f")))
                   (Var ann eqBoolean))
                 (Literal ann (BooleanLiteral True)))
               (Literal ann (BooleanLiteral True))
@@ -196,10 +197,10 @@
                         (App ann
                           (Var ann eq)
                           (Var ann eqBoolean))
-                        (Var ann (Qualified Nothing (Ident "x"))))
+                        (Var ann (Qualified (BySourcePos sp) (Ident "x"))))
                       (Literal ann (BooleanLiteral True))
                     , Literal ann (CharLiteral 't'))
-                  , ( Var ann (Qualified (Just (ModuleName "Data.Boolean")) (Ident "otherwise"))
+                  , ( Var ann (Qualified (ByModuleName (ModuleName "Data.Boolean")) (Ident "otherwise"))
                     , (Literal ann (CharLiteral 'f'))
                     )
                   ])
@@ -222,11 +223,12 @@
             "src/Utils.purs"
             []
             [Ident "isProduction"]
+            Map.empty
             []
             [NonRec ann (Ident "isProduction") (Literal ann (BooleanLiteral True))]
           e :: Expr Ann
           e = Case ann
-            [ Var ann (Qualified (Just (ModuleName "Utils")) (Ident "isProduction"))]
+            [ Var ann (Qualified (ByModuleName (ModuleName "Utils")) (Ident "isProduction"))]
             [ CaseAlternative [LiteralBinder ann (BooleanLiteral True)] (Right (Literal ann (CharLiteral 't')))
             , CaseAlternative [LiteralBinder ann (BooleanLiteral False)] (Right (Literal ann (CharLiteral 'f')))
             ]
@@ -238,11 +240,12 @@
             "src/Main.purs"
             []
             []
+            Map.empty
             []
             [NonRec ann (Ident "main") e]
       -- TODO
       case evaluate [mm, um] of
-        ((Module _ _ _ _ _ _ _ [NonRec _ (Ident "main") (Literal _ (CharLiteral 't'))]) : _) -> return ()
+        ((Module _ _ _ _ _ _ _ _ [NonRec _ (Ident "main") (Literal _ (CharLiteral 't'))]) : _) -> return ()
         r -> assertFailure $ "unexpected result:\n" ++ show r
         -- Left err -> assertFailure $ "compilation error: " ++ show err
 
@@ -258,11 +261,11 @@
       let e :: Expr Ann
           e = (App ann
                 (App ann
-                  (Var ann (Qualified (Just (ModuleName "Data.Array")) (Ident "index")))
+                  (Var ann (Qualified (ByModuleName (ModuleName "Data.Array")) (Ident "index")))
                   (Literal ann (ArrayLiteral [Literal ann (CharLiteral 't')])))
                 (Literal ann (NumericLiteral (Left 0))))
       case dceEvalExpr e of
-        (App _ (Var _ (Qualified (Just (ModuleName "Data.Maybe")) (Ident "Just"))) (Literal _ (CharLiteral 't'))) -> return ()
+        (App _ (Var _ (Qualified (ByModuleName (ModuleName "Data.Maybe")) (Ident "Just"))) (Literal _ (CharLiteral 't'))) -> return ()
         x -> assertFailure $ "unexpected expression:\n" ++ showExpr x
         -- Left err -> assertFailure $ "compilation error: " ++ show err
 
@@ -274,7 +277,7 @@
             e :: Expr Ann
             e = Let ann [ NonRec ann (Ident "a") (Literal ann (CharLiteral 'a')) ]
                   (Let ann [ NonRec ann (Ident "a") (Literal ann (CharLiteral 'b')) ]
-                    (Var ann (Qualified Nothing (Ident "a"))))
+                    (Var ann (Qualified (BySourcePos sp) (Ident "a"))))
         case dceEvalExpr e of
           Let _ _ (Let _ _ (Literal _ (CharLiteral 'b'))) -> return ()
           x -> assertFailure $ "unexpected expression:\n" ++ showExpr x
@@ -282,9 +285,9 @@
 
     context "Var inlining" $ do
       let oModName = ModuleName "Other"
-          oMod = Module ss [] oModName "" [] [] []
-            [ NonRec ann (Ident "o") $ Literal ann (ObjectLiteral [(mkString "a", Var ann (Qualified (Just C.eqMod) (Ident "eq"))) ])
-            , NonRec ann (Ident "a") $ Literal ann (ArrayLiteral [ Var ann (Qualified (Just C.eqMod) (Ident "eq")) ])
+          oMod = Module ss [] oModName "" [] [] Map.empty []
+            [ NonRec ann (Ident "o") $ Literal ann (ObjectLiteral [(mkString "a", Var ann (Qualified (ByModuleName C.eqMod) (Ident "eq"))) ])
+            , NonRec ann (Ident "a") $ Literal ann (ArrayLiteral [ Var ann (Qualified (ByModuleName C.eqMod) (Ident "eq")) ])
             , NonRec ann (Ident "s") $ Literal ann (StringLiteral (mkString "very-long-string"))
             , NonRec ann (Ident "b") $ Literal ann (BooleanLiteral True)
             , NonRec ann (Ident "c") $ Literal ann (CharLiteral 'a')
@@ -292,7 +295,7 @@
             ]
       specify "should not inline Var linking to an object literal" $ do
         let e :: Expr Ann
-            e = Var ann (Qualified (Just oModName) (Ident "o"))
+            e = Var ann (Qualified (ByModuleName oModName) (Ident "o"))
         case dceEvalExpr' e [oMod] of
           Var{} -> return ()
           e' -> assertFailure $ "unexpected expression: " ++ showExpr e'
@@ -300,7 +303,7 @@
 
       specify "should not inline Var linking to an array literal" $ do
         let e :: Expr Ann
-            e = Var ann (Qualified (Just oModName) (Ident "a"))
+            e = Var ann (Qualified (ByModuleName oModName) (Ident "a"))
         case dceEvalExpr' e [oMod] of
           Var{} -> return ()
           e' -> assertFailure $ "unexpected expression: " ++ showExpr e'
@@ -308,7 +311,7 @@
 
       specify "should not inline Var linking to a string literal" $ do
         let e :: Expr Ann
-            e = Var ann (Qualified (Just oModName) (Ident "s"))
+            e = Var ann (Qualified (ByModuleName oModName) (Ident "s"))
         case dceEvalExpr' e [oMod] of
           Var{} -> return ()
           e' -> assertFailure $ "unexpected expression: " ++ showExpr e'
@@ -316,7 +319,7 @@
 
       specify "should inline Var lining to a boolean literal" $ do
         let e :: Expr Ann
-            e = Var ann (Qualified (Just oModName) (Ident "b"))
+            e = Var ann (Qualified (ByModuleName oModName) (Ident "b"))
         case dceEvalExpr' e [oMod] of
           (Literal _ (BooleanLiteral{})) -> return ()
           e' -> assertFailure $ "wront expression: " ++ showExpr e'
@@ -324,7 +327,7 @@
 
       specify "should inline Var lining to a char literal" $ do
         let e :: Expr Ann
-            e = Var ann (Qualified (Just oModName) (Ident "c"))
+            e = Var ann (Qualified (ByModuleName oModName) (Ident "c"))
         case dceEvalExpr' e [oMod] of
           (Literal _ (CharLiteral{})) -> return ()
           e' -> assertFailure $ "wront expression: " ++ showExpr e'
@@ -332,7 +335,7 @@
 
       specify "should inline Var lining to a numeric literal" $ do
         let e :: Expr Ann
-            e = Var ann (Qualified (Just oModName) (Ident "n"))
+            e = Var ann (Qualified (ByModuleName oModName) (Ident "n"))
         case dceEvalExpr' e [oMod] of
           (Literal _ (NumericLiteral{})) -> return ()
           e' -> assertFailure $ "wront expression: " ++ showExpr e'
diff --git a/test/Test/Generators.hs b/test/Test/Generators.hs
--- a/test/Test/Generators.hs
+++ b/test/Test/Generators.hs
@@ -7,7 +7,7 @@
 import Data.String (IsString (..))
 import Test.QuickCheck
 
-import Language.PureScript.Names (Ident (..), ModuleName (..), ProperName (..), Qualified (..))
+import Language.PureScript.Names (Ident (..), ModuleName (..), ProperName (..), Qualified (..), QualifiedBy (..))
 import Language.PureScript.PSString (PSString)
 import Language.PureScript.AST.SourcePos (SourceSpan (..), SourcePos (..))
 import Language.PureScript.AST (Literal (..))
@@ -50,20 +50,20 @@
 
 genQualifiedIdent :: Gen (Qualified Ident)
 genQualifiedIdent = oneof
-  [ Qualified <$> liftArbitrary genModuleName <*> genIdent
-  , return (Qualified (Just C.unit) (Ident "unit"))
-  , return (Qualified (Just C.semiring) (Ident "add"))
-  , return (Qualified (Just C.semiring) (Ident "semiringInt"))
-  , return (Qualified (Just C.semiring) (Ident "semiringUnit"))
-  , return (Qualified (Just C.maybeMod) (Ident "Just"))
-  , return (Qualified (Just C.eqMod) (Ident "eq"))
-  , return (Qualified (Just C.ring) (Ident "negate"))
-  , return (Qualified (Just C.ring) (Ident "ringNumber"))
-  , return (Qualified (Just C.ring) (Ident "unitRing"))
+  [ (Qualified . ByModuleName <$> genModuleName) <*> genIdent
+  , return (Qualified (ByModuleName C.unit) (Ident "unit"))
+  , return (Qualified (ByModuleName C.semiring) (Ident "add"))
+  , return (Qualified (ByModuleName C.semiring) (Ident "semiringInt"))
+  , return (Qualified (ByModuleName C.semiring) (Ident "semiringUnit"))
+  , return (Qualified (ByModuleName C.maybeMod) (Ident "Just"))
+  , return (Qualified (ByModuleName C.eqMod) (Ident "eq"))
+  , return (Qualified (ByModuleName C.ring) (Ident "negate"))
+  , return (Qualified (ByModuleName C.ring) (Ident "ringNumber"))
+  , return (Qualified (ByModuleName C.ring) (Ident "unitRing"))
   ]
 
 genQualified :: Gen a -> Gen (Qualified a)
-genQualified gen = Qualified <$> liftArbitrary genModuleName <*> gen
+genQualified gen = Qualified . ByModuleName <$> genModuleName <*> gen
 
 genLiteral :: Gen (Literal (Expr Ann))
 genLiteral = oneof
diff --git a/test/Test/Karma.hs b/test/Test/Karma.hs
--- a/test/Test/Karma.hs
+++ b/test/Test/Karma.hs
@@ -1,12 +1,7 @@
 module Test.Karma (spec) where
 
-import           Prelude ()
-import           Prelude.Compat hiding (exp)
-import           Control.Monad (when)
 import           Control.Monad.Trans.Class
 import           Control.Monad.Except
-import           Data.List (init)
-import           Data.Foldable (forM_)
 import           Data.Text (Text)
 import qualified Data.Text as T
 import           System.Directory (setCurrentDirectory)
diff --git a/test/Test/Lib.hs b/test/Test/Lib.hs
--- a/test/Test/Lib.hs
+++ b/test/Test/Lib.hs
@@ -1,84 +1,176 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+
 module Test.Lib (spec) where
 
-import           Prelude ()
-import           Prelude.Compat hiding (exp)
-import           Control.Monad (when)
-import           Control.Monad.Trans.Class
-import           Control.Monad.Except
-import           Data.Foldable (forM_)
-import           Data.Text (Text)
+import Control.Monad.Except
+import Data.Text (Text)
 import qualified Data.Text as T
-import           Data.Semigroup ((<>))
-import           System.Exit (ExitCode(..))
-import           System.Process (readProcessWithExitCode)
-import           Test.Hspec
-import           Test.HUnit (assertEqual)
-
-import           Test.Utils
-
+import System.Exit (ExitCode (..))
+import System.Process (readProcessWithExitCode)
+import Test.HUnit (assertEqual)
+import Test.Hspec
+import Test.Utils
+import Prelude hiding (exp)
 
 data LibTest = LibTest
-  { libTestEntries :: [Text]
-  , libTestZephyrOptions :: Maybe [Text]
-  , libTestJsCmd :: Text
-  , libTestShouldPass :: Bool
-  -- ^ true if it should run without error, false if it should error
+  { libTestEntries :: [Text],
+    libTestZephyrOptions :: Maybe [Text],
+    libTestJs :: Text,
+    -- | true if it should run without error, false if it should error
+    libTestShouldPass :: Bool
   }
 
-
 libTests :: [LibTest]
 libTests =
-  [ LibTest ["Unsafe.Coerce.Test.unsafeX"] Nothing "require('./dce-output/Unsafe.Coerce.Test').unsafeX(1)(1);" True
-  , LibTest ["Foreign.Test.add"] Nothing "require('./dce-output/Foreign.Test').add(1)(1);" True
-  , LibTest ["Foreign.Test.add"] Nothing "require('./dce-output/Foreign.Test/foreign.js').mult(1)(1);" False
-  , LibTest ["Eval.makeAppQueue"] Nothing "require('./dce-output/Eval').makeAppQueue;" True
-  , LibTest ["Eval.evalUnderArrayLiteral"] Nothing "require('./dce-output/Eval').evalUnderArrayLiteral;" True
-  , LibTest ["Eval.evalUnderObjectLiteral"] Nothing "require('./dce-output/Eval').evalUnderObjectLiteral;" True
-  , LibTest ["Eval.evalVars"] Nothing "require('./dce-output/Eval').evalVars;" True
-  , LibTest ["Eval"] Nothing "require('./dce-output/Eval').evalVars;" True
-  , LibTest ["Eval.recordUpdate"] Nothing
-       ( " var eval = require('./dce-output/Eval');\n"
-      <> " var foo = eval.recordUpdate({foo: '', bar: 0})(eval.Foo.create('foo'));\n"
-      <> " if (foo.foo != 'foo') {\n"
-      <> "    console.error(foo)\n"
-      <> "    throw('Error')\n"
-      <> " }\n"
+  [ LibTest
+      ["Unsafe.Coerce.Test.unsafeX"]
+      Nothing
+      ( "import { unsafeX } from './dce-output/Unsafe.Coerce.Test/index.js';"
+          <> "unsafeX(1)(1);"
       )
+      True,
+    LibTest
+      ["Foreign.Test.add"]
+      Nothing
+      ( "import { add } from './dce-output/Foreign.Test/index.js';"
+          <> "add(1)(1);"
+      )
+      True,
+    LibTest
+      ["Foreign.Test.add"]
+      Nothing
+      ( "import { mult } from './dce-output/Foreign.Test/index.js';"
+          <> "mult(1)(1);"
+      )
+      False,
+    LibTest
+      ["Foreign.Test.add"]
+      Nothing
+      "import { mult } from './dce-output/Foreign.Test/foreign.js';"
+      False,
+    LibTest
+      ["Foreign.Test.snowflake"]
+      Nothing
+      ( "import { snowflake } from './dce-output/Foreign.Test/index.js';"
+          <> "if(snowflake !== '❄'){console.error(`'${snowflake}' !== '❄'`); throw 'Error';}"
+      )
+      True,
+    LibTest
+      ["Foreign.Test.b"]
+      Nothing
+      ( "import { b } from './dce-output/Foreign.Test/index.js';"
+          <> "if(b() !== 5) throw 'Error';"
+      )
+      True,
+    LibTest
+      ["Eval.makeAppQueue"]
+      Nothing
+      "import { makeAppQueue } from './dce-output/Eval/index.js';"
+      True,
+    LibTest
+      ["Eval.evalUnderArrayLiteral"]
+      Nothing
+      "import { evalUnderArrayLiteral } from './dce-output/Eval/index.js';"
+      True,
+    LibTest
+      ["Eval.evalUnderObjectLiteral"]
+      Nothing
+      "import { evalUnderObjectLiteral } from './dce-output/Eval/index.js';"
+      True,
+    LibTest
+      ["Eval.evalVars"]
+      Nothing
+      "import { evalVars } from './dce-output/Eval/index.js';"
+      True,
+    LibTest
+      ["Eval"]
+      Nothing
+      "import { evalVars } from './dce-output/Eval/index.js';"
+      True,
+    LibTest
+      ["Eval.recordUpdate"]
+      Nothing
+      ( "import * as E from './dce-output/Eval/index.js';"
+          <> "var foo = E.recordUpdate({ foo: '', bar: 0 })(E.Foo.create('foo'));"
+          <> "if (foo.foo != 'foo') {"
+          <> "  console.error(foo);"
+          <> "  throw 'Error';"
+          <> "}"
+      )
+      True,
+    LibTest
+      ["Literals.fromAnArray"]
+      Nothing
+      ( "import * as lits from './dce-output/Literals/index.js';"
+          <> "if (lits.fromAnArray == null || lits.AStr == null || lits.AInt != null) {"
+          <> "  throw 'Error';"
+          <> "}"
+      )
+      True,
+    LibTest
+      ["Literals.fromAnObject"]
+      Nothing
+      ( "import * as lits from './dce-output/Literals/index.js';"
+          <> "if (lits.fromAnObject == null || lits.AStr == null || lits.AInt != null) {"
+          <> "  throw 'Error';"
+          <> "}"
+      )
+      True,
+    -- Control.Alt re-exports map from Data.Functor
+    LibTest
+      ["Control.Alt.map"]
+      Nothing
+      "import { map } from './dce-output/Control.Alt/index.js';"
+      True,
+    LibTest
+      ["Data.Array.span"]
+      Nothing
+      ( "import { span } from './dce-output/Data.Array/index.js';"
+          <> "const a = [1, 3, 2, 4, 5];"
+          <> "const p = (x) => x % 2 === 1;"
+          <> "const r = span(p)(a);"
+          <> "const expected = { init: [1, 3], rest: [2, 4, 5] };"
+          <> "console.log(r);"
+          <> "if (!r.init.every((e, i) => e === expected.init[i])) throw 'Error';"
+          <> "if (!r.rest.every((e, i) => e === expected.rest[i])) throw 'Error';"
+      )
       True
   ]
 
-
 assertLib :: LibTest -> Expectation
 assertLib l = do
   res <- runExceptT . runLibTest $ l
   assertEqual "lib should run" (Right ()) res
 
-
 runLibTest :: LibTest -> ExceptT TestError IO ()
-runLibTest LibTest { libTestEntries
-                   , libTestZephyrOptions
-                   , libTestJsCmd
-                   , libTestShouldPass
-                   } = do
-  bowerInstall "LibTest"
-  pursCompile "LibTest"
-  runZephyr "LibTest" libTestEntries libTestZephyrOptions
-  (ecNode, stdNode, errNode) <- lift
-    $ readProcessWithExitCode
-        "node"
-        [ "-e"
-        , T.unpack libTestJsCmd
-        ]
-        ""
-  when (libTestShouldPass && ecNode /= ExitSuccess)
-    (throwError $ NodeError "LibTest (should pass)" ecNode stdNode errNode)
-  when (not libTestShouldPass && ecNode == ExitSuccess)
-    (throwError $ NodeError "LibTest (should fail)" ecNode stdNode errNode)
-
+runLibTest
+  LibTest
+    { libTestEntries,
+      libTestZephyrOptions,
+      libTestJs,
+      libTestShouldPass
+    } = do
+    spagoBuild "LibTest"
+    runZephyr "LibTest" libTestEntries libTestZephyrOptions
+    (ecNode, stdNode, errNode) <-
+      lift $
+        readProcessWithExitCode
+          "node"
+          [ "--input-type=module",
+            "-e",
+            T.unpack libTestJs
+          ]
+          ""
+    when
+      (libTestShouldPass && ecNode /= ExitSuccess)
+      (throwError $ NodeError "LibTest (should pass)" ecNode stdNode errNode)
+    when
+      (not libTestShouldPass && ecNode == ExitSuccess)
+      (throwError $ NodeError "LibTest (should fail)" ecNode stdNode errNode)
 
 spec :: Spec
 spec =
   changeDir "test/lib-tests" $
-    context "TestLib" $
+    context "test-lib" $
       forM_ libTests $ \l ->
         specify (T.unpack $ T.intercalate (T.pack " ") $ libTestEntries l) $ assertLib l
diff --git a/test/Test/Utils.hs b/test/Test/Utils.hs
--- a/test/Test/Utils.hs
+++ b/test/Test/Utils.hs
@@ -1,13 +1,12 @@
 {-# LANGUAGE CPP #-}
+{-# LANGUAGE NoImplicitPrelude #-}
 
 module Test.Utils where
 
-import           Prelude ()
-import           Prelude.Compat hiding (exp)
-import           Control.Monad (when)
+import           Prelude hiding (exp)
+import           Control.Exception (bracket)
 import           Control.Monad.Trans.Class
 import           Control.Monad.Except
-import           Data.List (last)
 import           Data.Maybe (fromMaybe)
 import           Data.Text (Text)
 import qualified Data.Text as T
@@ -33,15 +32,30 @@
 test_args = ["exec", "zephyr", "--"]
 #endif
 
+pursExe, bowerExe, npmExe, nodeExe, gitExe, spagoExe :: String
+#if !defined(mingw32_HOST_OS)
+pursExe  = "purs"
+gitExe   = "git"
+nodeExe  = "node"
+#else
+pursExe  = "purs.exe"
+gitExe   = "git.exe"
+nodeExe  = "node.exe"
+#endif
+bowerExe = "bower"
+npmExe   = "npm"
+spagoExe = "spago"
 
+
 changeDir :: FilePath -> Spec -> Spec
 changeDir path =
     around_ $ \runTests -> do
       createDirectoryIfMissing False path
       cwd <- getCurrentDirectory
-      setCurrentDirectory path
-      runTests
-      setCurrentDirectory cwd
+      bracket
+        (setCurrentDirectory path)
+        (\_ -> setCurrentDirectory cwd)
+        (\_ -> runTests)
 
 
 bowerInstall
@@ -50,9 +64,23 @@
 bowerInstall coreLibTestRepo = do
   bowerComponentsExists <- lift $ doesDirectoryExist "bower_components"
   when (not bowerComponentsExists) $ do
-    (ecBower, _, errBower) <- lift $ readProcessWithExitCode "bower" ["install"] ""
+    (ecBower, _, errBower) <- lift $ readProcessWithExitCode bowerExe ["install"] ""
     when (ecBower /= ecBower) (throwError (BowerError coreLibTestRepo ecBower errBower))
 
+spagoBuild
+  :: Text
+  -> ExceptT TestError IO ()
+spagoBuild coreLibTestRepo = do
+  outputDirExists <- lift $ doesDirectoryExist "output"
+  unless outputDirExists $ do
+    (ecSpago, _, errSpago) <- lift
+      $ readProcessWithExitCode
+          spagoExe
+          [ "build"
+          , "--purs-args" , "--codegen corefn,js"
+          ]
+          ""
+    when (ecSpago /= ExitSuccess) (throwError $ SpagoError coreLibTestRepo ecSpago errSpago)
 
 pursCompile
   :: Text
@@ -62,12 +90,11 @@
   when (not outputDirExists) $ do
     (ecPurs, _, errPurs) <- lift
       $ readProcessWithExitCode
-          "purs"
+          pursExe
           [ "compile"
           , "--codegen" , "corefn"
           , "bower_components/purescript-*/src/**/*.purs"
           , "src/**/*.purs"
-          , "test/**/*.purs"
           ]
           ""
     when (ecPurs /= ExitSuccess) (throwError $ PursError coreLibTestRepo ecPurs errPurs)
@@ -81,7 +108,7 @@
 
   repoExist <- lift $ doesDirectoryExist $ T.unpack dir
   unless repoExist $ do
-    (ecGit, _, errGc) <- lift $ readProcessWithExitCode "git" ["clone", "--depth", "1", T.unpack coreLibTestRepo, T.unpack dir] ""
+    (ecGit, _, errGc) <- lift $ readProcessWithExitCode gitExe ["clone", "--depth", "1", T.unpack coreLibTestRepo, T.unpack dir] ""
     when (ecGit /= ExitSuccess) (throwError (GitError coreLibTestRepo ecGit errGc))
   return (T.unpack dir)
 
@@ -94,10 +121,10 @@
   pkgJson <- lift $ doesFileExist "package.json"
   nodeModulesExists <- lift $ doesDirectoryExist "node_modules"
   when ((pkgJson || not (null npmModules)) && not nodeModulesExists) $ do
-    when (not $ null $ npmModules) $ do
-      (ecNpm, _, errNpm) <- lift $ readProcessWithExitCode "npm" (["install"] ++ T.unpack `map` npmModules) ""
+    unless (null npmModules) $ do
+      (ecNpm, _, errNpm) <- lift $ readProcessWithExitCode npmExe (["install"] ++ T.unpack `map` npmModules) ""
       when (ecNpm /= ExitSuccess) (throwError (NpmError coreLibTestRepo ecNpm errNpm))
-    (ecNpm, _, errNpm) <- lift $ readProcessWithExitCode "npm" ["install"] ""
+    (ecNpm, _, errNpm) <- lift $ readProcessWithExitCode npmExe ["install"] ""
     when (ecNpm /= ExitSuccess) (throwError (NpmError coreLibTestRepo ecNpm errNpm))
 
 
@@ -128,6 +155,7 @@
   | ZephyrError Text ExitCode String
   | NodeError Text ExitCode String String
   | JsCmdError Text Text
+  | SpagoError Text ExitCode String
   deriving (Eq)
 
 instance Show TestError where
@@ -148,3 +176,5 @@
   show (NodeError repo ec std err)
     = "node failed \"" ++ T.unpack repo ++ "\" (" ++ show ec ++ ")\n\n" ++ std ++ "\n\n" ++ err
   show (JsCmdError exp got) = "expected:\n\n" ++ T.unpack exp ++ "\n\nbut got:\n\n" ++ T.unpack got ++ "\n"
+  show (SpagoError repo ec err)
+    = "spago build failed \"" ++ T.unpack repo ++ "\" (" ++ show ec ++ ")\n" ++ err
diff --git a/zephyr.cabal b/zephyr.cabal
--- a/zephyr.cabal
+++ b/zephyr.cabal
@@ -1,23 +1,23 @@
 cabal-version:       2.0
-version:             0.3.2
+version:             0.5.3
 name:                zephyr
 synopsis:
   Zephyr, tree-shaking for the PureScript language
 description:
   Tree shaking tool and partial evaluator for PureScript
   CoreFn AST.
-homepage:            https://github.com/coot/zephyr#readme
+homepage:            https://github.com/MaybeJustJames/zephyr#readme
 license:             MPL-2.0
 license-file:        LICENSE
 author:              Marcin Szamotulski <profunctor@pm.me>
-maintainer:          Marcin Szamotulski <profunctor@pm.me>
-copyright:           (c) 2017-2018 Marcin Szamotulski
+maintainer:          James Collier <jhc_at_home@proton.me>
+copyright:           (c) 2017-2021 Marcin Szamotulski, (c) 2022-2023 James Collier
 build-type:          Simple
 extra-source-files: 
   ChangeLog.md
   README.md
 category:            Development
-tested-with:         ghc
+tested-with:         GHC==9.2.7
 
 flag test-with-stack
   description: use `stack exec zephyr` in tests
@@ -40,7 +40,6 @@
     KindSignatures
     LambdaCase
     MultiParamTypeClasses
-    NoImplicitPrelude
     NamedFieldPuns
     OverloadedStrings
     PatternGuards
@@ -51,7 +50,12 @@
     ViewPatterns
   ghc-options:
       -Wall
-      -fmax-pmcheck-iterations=4000000
+      -Wcompat
+      -Widentities
+      -Wincomplete-record-updates
+      -Wincomplete-uni-patterns
+      -Wpartial-fields
+      -Wredundant-constraints
   exposed-modules:
       Language.PureScript.DCE
     , Language.PureScript.DCE.Constants
@@ -61,18 +65,18 @@
     , Language.PureScript.DCE.Eval
     , Language.PureScript.DCE.Utils
   build-depends:
-      aeson               >=1.0      && <1.5
-    , ansi-terminal       >=0.7.1    && <0.11
-    , base                >=4.8      && <5
-    , base-compat         >=0.6.0
-    , boxes               ^>=0.1.4
-    , containers
+      aeson                >=2.0.3.0  && <2.1
+    , ansi-terminal        >=0.11     && <0.12
+    , base                 >=4.16.2   && <4.17
+    , boxes                >=0.1.5    && <0.2
+    , containers           >=0.6.5.1  && <0.7
     , formatting
-    , language-javascript ^>=0.7.1
-    , mtl                 >=2.1.0    && <2.3.0
-    , purescript          ^>=0.13.8
-    , safe                ^>=0.3.9
-    , text
+    , language-javascript  ==0.7.0.0
+    , mtl                  >=2.2.2    && <2.3
+    , purescript           >=0.15.4   && <0.16
+    , safe                 >=0.3.19   && <0.4
+    , text                 >=1.2.5.0  && <1.3
+    , unordered-containers == 0.2.19.1
   default-language:    Haskell2010
 
 executable zephyr
@@ -91,30 +95,36 @@
     OverloadedStrings
   ghc-options:
     -Wall
+    -Wcompat
+    -Widentities
+    -Wincomplete-record-updates
+    -Wincomplete-uni-patterns
+    -Wpartial-fields
+    -Wredundant-constraints
     -fno-warn-unused-do-bind
     -threaded
     -rtsopts
     -with-rtsopts -N2
   build-depends:
-      aeson                >=1.0      && <1.5
+      aeson                >=2.0.3.0   && <2.1
     , async
-    , ansi-terminal        >=0.7.1    && <0.11
-    , ansi-wl-pprint
-    , base
-    , bytestring
-    , containers
-    , directory            >=1.2.3
-    , filepath
+    , ansi-terminal        >=0.11      && <0.12
+    , ansi-wl-pprint       >=0.6.9     && <0.7
+    , base                 >=4.16.2    && <4.17
+    , bytestring           >=0.11.3.1  && <0.12
+    , containers           >=0.6.5.1   && <0.7
+    , directory            >=1.3.6.2   && <1.4
+    , filepath             >=1.4.2.2   && <1.5
     , formatting
-    , Glob                 >=0.9      && <0.11
-    , language-javascript  ^>=0.7
-    , mtl                  >=2.1.0    && <2.3.0
-    , optparse-applicative >=0.13.0
-    , purescript           ^>=0.13.8
-    , text
-    , transformers         >=0.3.0    && <0.6
-    , utf8-string          >=1        && <2
-    , zephyr
+    , Glob                 >=0.10.2    && <0.11
+    , language-javascript  ==0.7.0.0
+    , mtl                  >=2.2.2     && <2.3
+    , optparse-applicative >=0.17.0.0  && <0.18
+    , purescript           >=0.15.4    && <0.16
+    , text                 >=1.2.5.0   && <1.3
+    , transformers         >=0.5.6.2   && <0.6
+    , utf8-string          >=1.0.2     && <1.1
+    , zephyr               >=0.5       && <0.6
   default-language:    Haskell2010
 
 test-suite zephyr-test
@@ -137,24 +147,22 @@
     , Test.Karma
     , Test.Utils
   build-depends:
-      aeson                >=1.0      && <1.5
-    , base                 >=4.8      && <5
-    , base-compat          >=0.6.0
-    , containers
-    , directory            >=1.2.3
+      aeson                >=2.0.3.0   && <2.1
+    , base                 >=4.16.2    && <4.17
+    , containers           >=0.6.5.1   && <0.7
+    , directory            >=1.3.6.2   && <1.4
     , hspec
-    , hspec-core
+    , hspec-core           <2.9.3
     , HUnit
-    , language-javascript  ^>=0.7
-    , mtl                  >=2.1.0    && <2.3.0
-    , optparse-applicative >=0.13.0
-    , process                            <1.7.0.0
-    , purescript           ^>=0.13.8
+    , language-javascript  ==0.7.0.0
+    , mtl                  >=2.2.2     && <2.3
+    , optparse-applicative ==0.17.0.0
+    , process              ==1.6.13.1
+    , purescript           >=0.15.4    && <0.16
     , QuickCheck           >=2.12.1
-    , text
-    , transformers         >=0.3.0    && <0.6
-    , utf8-string          >=1        && <2
-    , zephyr
+    , text                 >=1.2.5.0   && <1.3
+    , transformers         >=0.5.6.2   && <0.6
+    , zephyr               >=0.5       && <0.6
   build-tool-depends:
       purescript:purs
   if flag(test-with-stack)
@@ -165,6 +173,12 @@
       -DTEST_CORE_LIBS=1
   ghc-options:
     -Wall
+    -Wcompat
+    -Widentities
+    -Wincomplete-record-updates
+    -Wincomplete-uni-patterns
+    -Wpartial-fields
+    -Wredundant-constraints
     -threaded
     -rtsopts
     -with-rtsopts=-N
@@ -172,4 +186,4 @@
 
 source-repository head
   type:     git
-  location: https://github.com/coot/zephyr
+  location: https://github.com/MaybeJustJames/zephyr
