diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -133,6 +133,7 @@
 | [@jordanmartinez](https://github.com/jordanmartinez) | Jordan Martinez | [MIT license](http://opensource.org/licenses/MIT) |
 | [@Saulukass](https://github.com/Saulukass) | Saulius Skliutas | [MIT license](http://opensource.org/licenses/MIT) |
 | [@adnelson](https://github.com/adnelson) | Allen Nelson | [MIT license](http://opensource.org/licenses/MIT) |
+| [@dyerw](https://github.com/dyerw) | Liam Dyer | [MIT license](http://opensource.org/licenses/MIT) |
 
 ### Contributors using Modified Terms
 
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -19,6 +19,7 @@
   SHA
   aeson
   aeson-better-errors
+  aeson-pretty
   alex
   ansi-terminal
   ansi-wl-pprint
@@ -320,6 +321,39 @@
   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
+aeson-pretty LICENSE file:
+
+  Copyright (c)2011, Falko Peters
+
+  All rights reserved.
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+      * Redistributions of source code must retain the above copyright
+        notice, this list of conditions and the following disclaimer.
+
+      * Redistributions in binary form must reproduce the above
+        copyright notice, this list of conditions and the following
+        disclaimer in the documentation and/or other materials provided
+        with the distribution.
+
+      * Neither the name of Falko Peters nor the names of other
+        contributors may be used to endorse or promote products derived
+        from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 alex LICENSE file:
 
   Copyright (c) 1995-2011, Chris Dornan and Simon Marlow
@@ -2887,7 +2921,7 @@
 
 mtl-compat LICENSE file:
 
-  Copyright (c) 2015, Ryan Scott
+  Copyright (c) 2015-2017, Ryan Scott
 
   All rights reserved.
 
@@ -4118,7 +4152,7 @@
 
 tagsoup LICENSE file:
 
-  Copyright Neil Mitchell 2006-2018.
+  Copyright Neil Mitchell 2006-2019.
   All rights reserved.
 
   Redistribution and use in source and binary forms, with or without
diff --git a/app/Command/Bundle.hs b/app/Command/Bundle.hs
--- a/app/Command/Bundle.hs
+++ b/app/Command/Bundle.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TupleSections #-}
 {-# LANGUAGE RecordWildCards #-}
@@ -8,7 +9,9 @@
 
 import           Data.Traversable (for)
 import           Data.Aeson (encode)
+import           Data.Aeson.Encode.Pretty (confCompare, defConfig, encodePretty', keyOrder)
 import           Data.Maybe (isNothing)
+import           Data.Text (Text)
 import           Control.Applicative
 import           Control.Monad
 import           Control.Monad.Error.Class
@@ -35,6 +38,7 @@
   , optionsMainModule  :: Maybe String
   , optionsNamespace   :: String
   , optionsSourceMaps  :: Bool
+  , optionsDebug       :: Bool
   } deriving Show
 
 -- | The main application function.
@@ -59,8 +63,18 @@
 
   currentDir <- liftIO getCurrentDirectory
   let outFile = if optionsSourceMaps then fmap (currentDir </>) optionsOutputFile else Nothing
-  bundleSM input entryIds optionsMainModule optionsNamespace outFile
+  let withRawModules = if optionsDebug then Just bundleDebug else Nothing
+  bundleSM input entryIds optionsMainModule optionsNamespace outFile withRawModules
 
+-- | Print a JSON representation of a list of modules to stderr.
+bundleDebug :: (MonadIO m) => [Module] -> m ()
+bundleDebug = liftIO . hPutStrLn stderr . LBU8.toString . encodePretty' (defConfig { confCompare = keyComparer })
+  where
+  -- | Some key order hints for improved readability.
+  keyComparer :: Text -> Text -> Ordering
+  keyComparer =  keyOrder ["type", "name", "moduleId"]     -- keys to put first
+              <> flip (keyOrder ["dependsOn", "elements"]) -- keys to put last
+
 -- | Command line options parser.
 options :: Parser Options
 options = Options <$> some inputFile
@@ -69,6 +83,7 @@
                   <*> optional mainModule
                   <*> namespace
                   <*> sourceMaps
+                  <*> debug
   where
   inputFile :: Parser FilePath
   inputFile = Opts.strArgument $
@@ -104,6 +119,11 @@
   sourceMaps = Opts.switch $
        Opts.long "source-maps"
     <> Opts.help "Whether to generate source maps for the bundle (requires --output)."
+
+  debug :: Parser Bool
+  debug = Opts.switch $
+       Opts.long "debug"
+    <> Opts.help "Whether to emit a JSON representation of all parsed modules to stderr."
 
 -- | Make it go.
 command :: Opts.Parser (IO ())
diff --git a/app/Command/Compile.hs b/app/Command/Compile.hs
--- a/app/Command/Compile.hs
+++ b/app/Command/Compile.hs
@@ -2,7 +2,6 @@
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TupleSections #-}
 
 module Command.Compile (command) where
 
@@ -14,7 +13,6 @@
 import           Data.List (intercalate)
 import qualified Data.Map as M
 import qualified Data.Set as S
-import           Data.Text (Text)
 import qualified Data.Text as T
 import           Data.Traversable (for)
 import qualified Language.PureScript as P
@@ -27,7 +25,7 @@
 import           System.Directory (getCurrentDirectory)
 import           System.FilePath.Glob (glob)
 import           System.IO (hPutStr, hPutStrLn, stderr)
-import           System.IO.UTF8 (readUTF8FileT)
+import           System.IO.UTF8 (readUTF8FilesT)
 
 data PSCMakeOptions = PSCMakeOptions
   { pscmInput        :: [FilePath]
@@ -37,7 +35,7 @@
   , pscmJSONErrors   :: Bool
   }
 
--- | Argumnets: verbose, use JSON, warnings, errors
+-- | Arguments: verbose, use JSON, warnings, errors
 printWarningsAndErrors :: Bool -> Bool -> P.MultipleErrors -> Either P.MultipleErrors a -> IO ()
 printWarningsAndErrors verbose False warnings errors = do
   pwd <- getCurrentDirectory
@@ -64,7 +62,7 @@
                              , "Usage: For basic information, try the `--help' option."
                              ]
     exitFailure
-  moduleFiles <- readInput input
+  moduleFiles <- readUTF8FilesT input
   (makeErrors, makeWarnings) <- runMake pscmOpts $ do
     ms <- CST.parseModulesFromFiles id moduleFiles
     let filePathMap = M.fromList $ map (\(fp, pm) -> (P.getModuleName $ CST.resPartial pm, Right fp)) ms
@@ -85,9 +83,6 @@
     when (null paths) $ warn pattern'
     return paths
   concatMapM f = fmap concat . mapM f
-
-readInput :: [FilePath] -> IO [(FilePath, Text)]
-readInput inputFiles = forM inputFiles $ \inFile -> (inFile, ) <$> readUTF8FileT inFile
 
 inputFile :: Opts.Parser FilePath
 inputFile = Opts.strArgument $
diff --git a/app/Command/Hierarchy.hs b/app/Command/Hierarchy.hs
--- a/app/Command/Hierarchy.hs
+++ b/app/Command/Hierarchy.hs
@@ -13,7 +13,6 @@
 --
 -----------------------------------------------------------------------------
 
-{-# LANGUAGE TupleSections #-}
 {-# LANGUAGE DataKinds #-}
 
 module Command.Hierarchy (command) where
@@ -31,7 +30,7 @@
 import           System.FilePath.Glob (glob)
 import           System.Exit (exitFailure, exitSuccess)
 import           System.IO (hPutStr, stderr)
-import           System.IO.UTF8 (readUTF8FileT)
+import           System.IO.UTF8 (readUTF8FilesT)
 import qualified Language.PureScript as P
 import qualified Language.PureScript.CST as CST
 import           Language.PureScript.Hierarchy (Graph(..), _unDigraph, _unGraphName, typeClasses)
@@ -41,15 +40,15 @@
   , _hierarchyOutput :: Maybe FilePath
   }
 
-readInput :: [FilePath] -> IO (Either P.MultipleErrors [P.Module])
-readInput paths = do
-  content <- mapM (\path -> (path, ) <$> readUTF8FileT path) paths
+parseInput :: [FilePath] -> IO (Either P.MultipleErrors [P.Module])
+parseInput paths = do
+  content <- readUTF8FilesT paths
   return $ map snd <$> CST.parseFromFiles id content
 
 compile :: HierarchyOptions -> IO ()
 compile (HierarchyOptions inputGlob mOutput) = do
   input <- glob inputGlob
-  modules <- readInput input
+  modules <- parseInput input
   case modules of
     Left errs -> hPutStr stderr (P.prettyPrintMultipleErrors P.defaultPPEOptions errs) >> exitFailure
     Right ms -> do
diff --git a/purescript.cabal b/purescript.cabal
--- a/purescript.cabal
+++ b/purescript.cabal
@@ -1,26 +1,19 @@
 cabal-version: 1.12
-
--- This file has been generated from package.yaml by hpack version 0.31.1.
---
--- see: https://github.com/sol/hpack
---
--- hash: a831e2c306c517805ea62378fee77f2b0cdb0df3f38171ce63de73f7dcf9455f
-
-name:           purescript
-version:        0.13.0
-synopsis:       PureScript Programming Language Compiler
-description:    A small strongly, statically typed programming language with expressive types, inspired by Haskell and compiling to JavaScript.
-category:       Language
-stability:      experimental
-homepage:       http://www.purescript.org/
-bug-reports:    https://github.com/purescript/purescript/issues
-author:         Phil Freeman <paf31@cantab.net>, Gary Burgess <gary.burgess@gmail.com>, Hardy Jones <jones3.hardy@gmail.com>, Harry Garrood <harry@garrood.me>, Christoph Hegemann <christoph.hegemann1337@gmail.com>
-
-maintainer:     Phil Freeman <paf31@cantab.net>
-copyright:      (c) 2013-17 Phil Freeman, (c) 2014-17 Gary Burgess
-license:        BSD3
-license-file:   LICENSE
-build-type:     Simple
+name: purescript
+version: 0.13.2
+license: BSD3
+license-file: LICENSE
+copyright: (c) 2013-17 Phil Freeman, (c) 2014-19 Gary Burgess, (c) other contributors (see CONTRIBUTORS.md)
+maintainer: Gary Burgess <gary.burgess@gmail.com>, Hardy Jones <jones3.hardy@gmail.com>, Harry Garrood <harry@garrood.me>, Christoph Hegemann <christoph.hegemann1337@gmail.com>, Liam Goodacre <goodacre.liam@gmail.com>, Nathan Faubion <nathan@n-son.com>
+author: Phil Freeman <paf31@cantab.net>
+stability: experimental
+homepage: http://www.purescript.org/
+bug-reports: https://github.com/purescript/purescript/issues
+synopsis: PureScript Programming Language Compiler
+description:
+    A small strongly, statically typed programming language with expressive types, inspired by Haskell and compiling to JavaScript.
+category: Language
+build-type: Simple
 extra-source-files:
     app/static/index.html
     app/static/index.js
@@ -120,6 +113,7 @@
     tests/purs/failing/3549-a.purs
     tests/purs/failing/3549.purs
     tests/purs/failing/365.purs
+    tests/purs/failing/3689.purs
     tests/purs/failing/438.purs
     tests/purs/failing/881.purs
     tests/purs/failing/AnonArgument1.purs
@@ -1194,422 +1188,431 @@
     CONTRIBUTING.md
 
 source-repository head
-  type: git
-  location: https://github.com/purescript/purescript
+    type: git
+    location: https://github.com/purescript/purescript
 
 flag release
-  description: Mark this build as a release build: prevents inclusion of extra info e.g. commit SHA in --version output)
-
-  manual: False
-  default: False
+    description:
+        Mark this build as a release build: prevents inclusion of extra info e.g. commit SHA in --version output)
+    default: False
 
 library
-  exposed-modules:
-      Control.Monad.Logger
-      Control.Monad.Supply
-      Control.Monad.Supply.Class
-      Language.PureScript
-      Language.PureScript.AST
-      Language.PureScript.AST.Binders
-      Language.PureScript.AST.Declarations
-      Language.PureScript.AST.Exported
-      Language.PureScript.AST.Literals
-      Language.PureScript.AST.Operators
-      Language.PureScript.AST.SourcePos
-      Language.PureScript.AST.Traversals
-      Language.PureScript.Bundle
-      Language.PureScript.CodeGen
-      Language.PureScript.CodeGen.JS
-      Language.PureScript.CodeGen.JS.Common
-      Language.PureScript.CodeGen.JS.Printer
-      Language.PureScript.Comments
-      Language.PureScript.Constants
-      Language.PureScript.CoreFn
-      Language.PureScript.CoreFn.Ann
-      Language.PureScript.CoreFn.Binders
-      Language.PureScript.CoreFn.Desugar
-      Language.PureScript.CoreFn.Expr
-      Language.PureScript.CoreFn.FromJSON
-      Language.PureScript.CoreFn.Meta
-      Language.PureScript.CoreFn.Module
-      Language.PureScript.CoreFn.Optimizer
-      Language.PureScript.CoreFn.ToJSON
-      Language.PureScript.CoreFn.Traversals
-      Language.PureScript.CoreImp
-      Language.PureScript.CoreImp.AST
-      Language.PureScript.CoreImp.Optimizer
-      Language.PureScript.CoreImp.Optimizer.Blocks
-      Language.PureScript.CoreImp.Optimizer.Common
-      Language.PureScript.CoreImp.Optimizer.Inliner
-      Language.PureScript.CoreImp.Optimizer.MagicDo
-      Language.PureScript.CoreImp.Optimizer.TCO
-      Language.PureScript.CoreImp.Optimizer.Unused
-      Language.PureScript.Crash
-      Language.PureScript.CST
-      Language.PureScript.CST.Convert
-      Language.PureScript.CST.Errors
-      Language.PureScript.CST.Layout
-      Language.PureScript.CST.Lexer
-      Language.PureScript.CST.Monad
-      Language.PureScript.CST.Parser
-      Language.PureScript.CST.Positions
-      Language.PureScript.CST.Print
-      Language.PureScript.CST.Traversals
-      Language.PureScript.CST.Traversals.Type
-      Language.PureScript.CST.Types
-      Language.PureScript.CST.Utils
-      Language.PureScript.Docs
-      Language.PureScript.Docs.AsHtml
-      Language.PureScript.Docs.AsMarkdown
-      Language.PureScript.Docs.Collect
-      Language.PureScript.Docs.Convert
-      Language.PureScript.Docs.Convert.ReExports
-      Language.PureScript.Docs.Convert.Single
-      Language.PureScript.Docs.Css
-      Language.PureScript.Docs.Prim
-      Language.PureScript.Docs.Render
-      Language.PureScript.Docs.RenderedCode
-      Language.PureScript.Docs.RenderedCode.RenderKind
-      Language.PureScript.Docs.RenderedCode.RenderType
-      Language.PureScript.Docs.RenderedCode.Types
-      Language.PureScript.Docs.Tags
-      Language.PureScript.Docs.Types
-      Language.PureScript.Docs.Utils.MonoidExtras
-      Language.PureScript.Environment
-      Language.PureScript.Errors
-      Language.PureScript.Errors.JSON
-      Language.PureScript.Externs
-      Language.PureScript.Hierarchy
-      Language.PureScript.Ide
-      Language.PureScript.Ide.CaseSplit
-      Language.PureScript.Ide.Command
-      Language.PureScript.Ide.Completion
-      Language.PureScript.Ide.Error
-      Language.PureScript.Ide.Externs
-      Language.PureScript.Ide.Filter
-      Language.PureScript.Ide.Filter.Declaration
-      Language.PureScript.Ide.Imports
-      Language.PureScript.Ide.Logging
-      Language.PureScript.Ide.Matcher
-      Language.PureScript.Ide.Prim
-      Language.PureScript.Ide.Rebuild
-      Language.PureScript.Ide.Reexports
-      Language.PureScript.Ide.SourceFile
-      Language.PureScript.Ide.State
-      Language.PureScript.Ide.Types
-      Language.PureScript.Ide.Usage
-      Language.PureScript.Ide.Util
-      Language.PureScript.Ide.Watcher
-      Language.PureScript.Interactive
-      Language.PureScript.Interactive.Completion
-      Language.PureScript.Interactive.Directive
-      Language.PureScript.Interactive.IO
-      Language.PureScript.Interactive.Message
-      Language.PureScript.Interactive.Module
-      Language.PureScript.Interactive.Parser
-      Language.PureScript.Interactive.Printer
-      Language.PureScript.Interactive.Types
-      Language.PureScript.Kinds
-      Language.PureScript.Label
-      Language.PureScript.Linter
-      Language.PureScript.Linter.Exhaustive
-      Language.PureScript.Linter.Imports
-      Language.PureScript.Make
-      Language.PureScript.Make.Actions
-      Language.PureScript.Make.BuildPlan
-      Language.PureScript.Make.Monad
-      Language.PureScript.ModuleDependencies
-      Language.PureScript.Names
-      Language.PureScript.Options
-      Language.PureScript.Pretty
-      Language.PureScript.Pretty.Common
-      Language.PureScript.Pretty.Kinds
-      Language.PureScript.Pretty.Types
-      Language.PureScript.Pretty.Values
-      Language.PureScript.PSString
-      Language.PureScript.Publish
-      Language.PureScript.Publish.BoxesHelpers
-      Language.PureScript.Publish.ErrorsWarnings
-      Language.PureScript.Publish.Utils
-      Language.PureScript.Renamer
-      Language.PureScript.Sugar
-      Language.PureScript.Sugar.AdoNotation
-      Language.PureScript.Sugar.BindingGroups
-      Language.PureScript.Sugar.CaseDeclarations
-      Language.PureScript.Sugar.DoNotation
-      Language.PureScript.Sugar.LetPattern
-      Language.PureScript.Sugar.Names
-      Language.PureScript.Sugar.Names.Common
-      Language.PureScript.Sugar.Names.Env
-      Language.PureScript.Sugar.Names.Exports
-      Language.PureScript.Sugar.Names.Imports
-      Language.PureScript.Sugar.ObjectWildcards
-      Language.PureScript.Sugar.Operators
-      Language.PureScript.Sugar.Operators.Binders
-      Language.PureScript.Sugar.Operators.Common
-      Language.PureScript.Sugar.Operators.Expr
-      Language.PureScript.Sugar.Operators.Types
-      Language.PureScript.Sugar.TypeClasses
-      Language.PureScript.Sugar.TypeClasses.Deriving
-      Language.PureScript.Sugar.TypeDeclarations
-      Language.PureScript.Traversals
-      Language.PureScript.TypeChecker
-      Language.PureScript.TypeChecker.Entailment
-      Language.PureScript.TypeChecker.Kinds
-      Language.PureScript.TypeChecker.Monad
-      Language.PureScript.TypeChecker.Skolems
-      Language.PureScript.TypeChecker.Subsumption
-      Language.PureScript.TypeChecker.Synonyms
-      Language.PureScript.TypeChecker.Types
-      Language.PureScript.TypeChecker.TypeSearch
-      Language.PureScript.TypeChecker.Unify
-      Language.PureScript.TypeClassDictionaries
-      Language.PureScript.Types
-      System.IO.UTF8
-  other-modules:
-      Paths_purescript
-  hs-source-dirs:
-      src
-  default-extensions: ConstraintKinds DataKinds DeriveFunctor DeriveFoldable DeriveTraversable DeriveGeneric EmptyDataDecls FlexibleContexts KindSignatures LambdaCase MultiParamTypeClasses NoImplicitPrelude PatternGuards PatternSynonyms RankNTypes RecordWildCards OverloadedStrings ScopedTypeVariables TupleSections ViewPatterns
-  ghc-options: -Wall -O2
-  build-depends:
-      Cabal >=2.2
-    , Glob >=0.9 && <0.10
-    , aeson >=1.0 && <1.5
-    , aeson-better-errors >=0.8
-    , ansi-terminal >=0.7.1 && <0.9
-    , array
-    , base >=4.8 && <4.13
-    , base-compat >=0.6.0
-    , blaze-html >=0.8.1 && <0.10
-    , bower-json >=1.0.0.1 && <1.1
-    , boxes >=0.1.4 && <0.2.0
-    , bytestring
-    , cheapskate >=0.1 && <0.2
-    , clock
-    , containers
-    , data-ordlist >=0.4.7.0
-    , deepseq
-    , directory >=1.2.3
-    , dlist
-    , edit-distance
-    , file-embed
-    , filepath
-    , fsnotify >=0.2.1
-    , haskeline >=0.7.0.0
-    , language-javascript >=0.6.0.9 && <0.7
-    , lifted-async >=0.10.0.3 && <0.10.1
-    , lifted-base >=0.2.3 && <0.2.4
-    , microlens-platform >=0.3.9.0 && <0.4
-    , monad-control >=1.0.0.0 && <1.1
-    , monad-logger >=0.3 && <0.4
-    , mtl >=2.1.0 && <2.3.0
-    , parallel >=3.2 && <3.3
-    , parsec >=3.1.10
-    , pattern-arrows >=0.0.2 && <0.1
-    , process >=1.2.0 && <1.7
-    , protolude >=0.1.6
-    , regex-tdfa
-    , safe >=0.3.9 && <0.4
-    , scientific >=0.3.4.9 && <0.4
-    , semigroups >=0.16.2 && <0.19
-    , sourcemap >=0.1.6
-    , split
-    , stm >=0.2.4.0
-    , stringsearch
-    , syb
-    , text
-    , time
-    , transformers >=0.3.0 && <0.6
-    , transformers-base >=0.4.0 && <0.5
-    , transformers-compat >=0.3.0
-    , unordered-containers
-    , utf8-string >=1 && <2
-    , vector
-  default-language: Haskell2010
+    exposed-modules:
+        Control.Monad.Logger
+        Control.Monad.Supply
+        Control.Monad.Supply.Class
+        Language.PureScript
+        Language.PureScript.AST
+        Language.PureScript.AST.Binders
+        Language.PureScript.AST.Declarations
+        Language.PureScript.AST.Exported
+        Language.PureScript.AST.Literals
+        Language.PureScript.AST.Operators
+        Language.PureScript.AST.SourcePos
+        Language.PureScript.AST.Traversals
+        Language.PureScript.Bundle
+        Language.PureScript.CodeGen
+        Language.PureScript.CodeGen.JS
+        Language.PureScript.CodeGen.JS.Common
+        Language.PureScript.CodeGen.JS.Printer
+        Language.PureScript.Comments
+        Language.PureScript.Constants
+        Language.PureScript.CoreFn
+        Language.PureScript.CoreFn.Ann
+        Language.PureScript.CoreFn.Binders
+        Language.PureScript.CoreFn.Desugar
+        Language.PureScript.CoreFn.Expr
+        Language.PureScript.CoreFn.FromJSON
+        Language.PureScript.CoreFn.Meta
+        Language.PureScript.CoreFn.Module
+        Language.PureScript.CoreFn.Optimizer
+        Language.PureScript.CoreFn.ToJSON
+        Language.PureScript.CoreFn.Traversals
+        Language.PureScript.CoreImp
+        Language.PureScript.CoreImp.AST
+        Language.PureScript.CoreImp.Optimizer
+        Language.PureScript.CoreImp.Optimizer.Blocks
+        Language.PureScript.CoreImp.Optimizer.Common
+        Language.PureScript.CoreImp.Optimizer.Inliner
+        Language.PureScript.CoreImp.Optimizer.MagicDo
+        Language.PureScript.CoreImp.Optimizer.TCO
+        Language.PureScript.CoreImp.Optimizer.Unused
+        Language.PureScript.Crash
+        Language.PureScript.CST
+        Language.PureScript.CST.Convert
+        Language.PureScript.CST.Errors
+        Language.PureScript.CST.Layout
+        Language.PureScript.CST.Lexer
+        Language.PureScript.CST.Monad
+        Language.PureScript.CST.Parser
+        Language.PureScript.CST.Positions
+        Language.PureScript.CST.Print
+        Language.PureScript.CST.Traversals
+        Language.PureScript.CST.Traversals.Type
+        Language.PureScript.CST.Types
+        Language.PureScript.CST.Utils
+        Language.PureScript.Docs
+        Language.PureScript.Docs.AsHtml
+        Language.PureScript.Docs.AsMarkdown
+        Language.PureScript.Docs.Collect
+        Language.PureScript.Docs.Convert
+        Language.PureScript.Docs.Convert.ReExports
+        Language.PureScript.Docs.Convert.Single
+        Language.PureScript.Docs.Css
+        Language.PureScript.Docs.Prim
+        Language.PureScript.Docs.Render
+        Language.PureScript.Docs.RenderedCode
+        Language.PureScript.Docs.RenderedCode.RenderKind
+        Language.PureScript.Docs.RenderedCode.RenderType
+        Language.PureScript.Docs.RenderedCode.Types
+        Language.PureScript.Docs.Tags
+        Language.PureScript.Docs.Types
+        Language.PureScript.Docs.Utils.MonoidExtras
+        Language.PureScript.Environment
+        Language.PureScript.Errors
+        Language.PureScript.Errors.JSON
+        Language.PureScript.Externs
+        Language.PureScript.Hierarchy
+        Language.PureScript.Ide
+        Language.PureScript.Ide.CaseSplit
+        Language.PureScript.Ide.Command
+        Language.PureScript.Ide.Completion
+        Language.PureScript.Ide.Error
+        Language.PureScript.Ide.Externs
+        Language.PureScript.Ide.Filter
+        Language.PureScript.Ide.Filter.Declaration
+        Language.PureScript.Ide.Imports
+        Language.PureScript.Ide.Logging
+        Language.PureScript.Ide.Matcher
+        Language.PureScript.Ide.Prim
+        Language.PureScript.Ide.Rebuild
+        Language.PureScript.Ide.Reexports
+        Language.PureScript.Ide.SourceFile
+        Language.PureScript.Ide.State
+        Language.PureScript.Ide.Types
+        Language.PureScript.Ide.Usage
+        Language.PureScript.Ide.Util
+        Language.PureScript.Ide.Watcher
+        Language.PureScript.Interactive
+        Language.PureScript.Interactive.Completion
+        Language.PureScript.Interactive.Directive
+        Language.PureScript.Interactive.IO
+        Language.PureScript.Interactive.Message
+        Language.PureScript.Interactive.Module
+        Language.PureScript.Interactive.Parser
+        Language.PureScript.Interactive.Printer
+        Language.PureScript.Interactive.Types
+        Language.PureScript.Kinds
+        Language.PureScript.Label
+        Language.PureScript.Linter
+        Language.PureScript.Linter.Exhaustive
+        Language.PureScript.Linter.Imports
+        Language.PureScript.Make
+        Language.PureScript.Make.Actions
+        Language.PureScript.Make.BuildPlan
+        Language.PureScript.Make.Monad
+        Language.PureScript.ModuleDependencies
+        Language.PureScript.Names
+        Language.PureScript.Options
+        Language.PureScript.Pretty
+        Language.PureScript.Pretty.Common
+        Language.PureScript.Pretty.Kinds
+        Language.PureScript.Pretty.Types
+        Language.PureScript.Pretty.Values
+        Language.PureScript.PSString
+        Language.PureScript.Publish
+        Language.PureScript.Publish.BoxesHelpers
+        Language.PureScript.Publish.ErrorsWarnings
+        Language.PureScript.Publish.Utils
+        Language.PureScript.Renamer
+        Language.PureScript.Sugar
+        Language.PureScript.Sugar.AdoNotation
+        Language.PureScript.Sugar.BindingGroups
+        Language.PureScript.Sugar.CaseDeclarations
+        Language.PureScript.Sugar.DoNotation
+        Language.PureScript.Sugar.LetPattern
+        Language.PureScript.Sugar.Names
+        Language.PureScript.Sugar.Names.Common
+        Language.PureScript.Sugar.Names.Env
+        Language.PureScript.Sugar.Names.Exports
+        Language.PureScript.Sugar.Names.Imports
+        Language.PureScript.Sugar.ObjectWildcards
+        Language.PureScript.Sugar.Operators
+        Language.PureScript.Sugar.Operators.Binders
+        Language.PureScript.Sugar.Operators.Common
+        Language.PureScript.Sugar.Operators.Expr
+        Language.PureScript.Sugar.Operators.Types
+        Language.PureScript.Sugar.TypeClasses
+        Language.PureScript.Sugar.TypeClasses.Deriving
+        Language.PureScript.Sugar.TypeDeclarations
+        Language.PureScript.Traversals
+        Language.PureScript.TypeChecker
+        Language.PureScript.TypeChecker.Entailment
+        Language.PureScript.TypeChecker.Kinds
+        Language.PureScript.TypeChecker.Monad
+        Language.PureScript.TypeChecker.Skolems
+        Language.PureScript.TypeChecker.Subsumption
+        Language.PureScript.TypeChecker.Synonyms
+        Language.PureScript.TypeChecker.Types
+        Language.PureScript.TypeChecker.TypeSearch
+        Language.PureScript.TypeChecker.Unify
+        Language.PureScript.TypeClassDictionaries
+        Language.PureScript.Types
+        System.IO.UTF8
+    build-tools: happy ==1.19.9
+    hs-source-dirs: src
+    other-modules:
+        Paths_purescript
+    default-language: Haskell2010
+    default-extensions: ConstraintKinds DataKinds DeriveFunctor
+                        DeriveFoldable DeriveTraversable DeriveGeneric EmptyDataDecls
+                        FlexibleContexts KindSignatures LambdaCase MultiParamTypeClasses
+                        NoImplicitPrelude PatternGuards PatternSynonyms RankNTypes
+                        RecordWildCards OverloadedStrings ScopedTypeVariables TupleSections
+                        ViewPatterns
+    ghc-options: -Wall -O2
+    build-depends:
+        Cabal >=2.2 && <2.5,
+        Glob ==0.9.*,
+        aeson >=1.0 && <1.5,
+        aeson-better-errors >=0.8 && <0.10,
+        aeson-pretty <0.9,
+        ansi-terminal >=0.7.1 && <0.9,
+        array <0.6,
+        base >=4.11 && <4.13,
+        base-compat >=0.6.0 && <0.11,
+        blaze-html >=0.8.1 && <0.10,
+        bower-json >=1.0.0.1 && <1.1,
+        boxes >=0.1.4 && <0.2.0,
+        bytestring <0.11,
+        cheapskate ==0.1.*,
+        clock <0.8,
+        containers <0.7,
+        data-ordlist >=0.4.7.0 && <0.5,
+        deepseq <1.5,
+        directory >=1.2.3 && <1.4,
+        dlist <0.9,
+        edit-distance <0.3,
+        file-embed <0.1,
+        filepath <1.5,
+        fsnotify >=0.2.1 && <0.4,
+        haskeline >=0.7.0.0 && <0.8,
+        language-javascript >=0.6.0.13 && <0.7,
+        lifted-async >=0.10.0.3 && <0.10.1,
+        lifted-base ==0.2.3.*,
+        microlens-platform >=0.3.9.0 && <0.4,
+        monad-control >=1.0.0.0 && <1.1,
+        monad-logger ==0.3.*,
+        mtl >=2.1.0 && <2.3.0,
+        parallel ==3.2.*,
+        parsec >=3.1.10 && <3.2,
+        pattern-arrows >=0.0.2 && <0.1,
+        process >=1.2.0 && <1.7,
+        protolude >=0.1.6 && <0.3,
+        regex-tdfa <1.3,
+        safe >=0.3.9 && <0.4,
+        scientific >=0.3.4.9 && <0.4,
+        semigroups >=0.16.2 && <0.19,
+        sourcemap >=0.1.6 && <0.2,
+        split <0.3,
+        stm >=0.2.4.0 && <2.6,
+        stringsearch <0.4,
+        syb <0.8,
+        text <1.3,
+        time <1.9,
+        transformers >=0.3.0 && <0.6,
+        transformers-base >=0.4.0 && <0.5,
+        transformers-compat >=0.3.0 && <0.7,
+        unordered-containers <0.3,
+        utf8-string ==1.*,
+        vector <0.13
 
 executable purs
-  main-is: Main.hs
-  other-modules:
-      Command.Bundle
-      Command.Compile
-      Command.Docs
-      Command.Docs.Html
-      Command.Docs.Markdown
-      Command.Hierarchy
-      Command.Ide
-      Command.Publish
-      Command.REPL
-      Paths_purescript
-      Version
-  hs-source-dirs:
-      app
-  ghc-options: -Wall -O2 -fno-warn-unused-do-bind -threaded -rtsopts -with-rtsopts=-N
-  build-depends:
-      Cabal >=2.2
-    , Glob >=0.9 && <0.10
-    , aeson >=1.0 && <1.5
-    , aeson-better-errors >=0.8
-    , ansi-terminal >=0.7.1 && <0.9
-    , ansi-wl-pprint
-    , array
-    , base >=4.8 && <4.13
-    , base-compat >=0.6.0
-    , blaze-html >=0.8.1 && <0.10
-    , bower-json >=1.0.0.1 && <1.1
-    , boxes >=0.1.4 && <0.2.0
-    , bytestring
-    , cheapskate >=0.1 && <0.2
-    , clock
-    , containers
-    , data-ordlist >=0.4.7.0
-    , deepseq
-    , directory >=1.2.3
-    , dlist
-    , edit-distance
-    , file-embed
-    , filepath
-    , fsnotify >=0.2.1
-    , haskeline >=0.7.0.0
-    , http-types
-    , language-javascript >=0.6.0.9 && <0.7
-    , lifted-async >=0.10.0.3 && <0.10.1
-    , lifted-base >=0.2.3 && <0.2.4
-    , microlens-platform >=0.3.9.0 && <0.4
-    , monad-control >=1.0.0.0 && <1.1
-    , monad-logger >=0.3 && <0.4
-    , mtl >=2.1.0 && <2.3.0
-    , network >=3.0.1.1
-    , optparse-applicative >=0.13.0
-    , parallel >=3.2 && <3.3
-    , parsec >=3.1.10
-    , pattern-arrows >=0.0.2 && <0.1
-    , process >=1.2.0 && <1.7
-    , protolude >=0.1.6
-    , purescript
-    , regex-tdfa
-    , safe >=0.3.9 && <0.4
-    , scientific >=0.3.4.9 && <0.4
-    , semigroups >=0.16.2 && <0.19
-    , sourcemap >=0.1.6
-    , split
-    , stm >=0.2.4.0
-    , stringsearch
-    , syb
-    , text
-    , time
-    , transformers >=0.3.0 && <0.6
-    , transformers-base >=0.4.0 && <0.5
-    , transformers-compat >=0.3.0
-    , unordered-containers
-    , utf8-string >=1 && <2
-    , vector
-    , wai ==3.*
-    , wai-websockets ==3.*
-    , warp ==3.*
-    , websockets >=0.9 && <0.13
-  if flag(release)
-    cpp-options: -DRELEASE
-  else
+    main-is: Main.hs
+    build-tools: happy ==1.19.9
+    hs-source-dirs: app
+    other-modules:
+        Command.Bundle
+        Command.Compile
+        Command.Docs
+        Command.Docs.Html
+        Command.Docs.Markdown
+        Command.Hierarchy
+        Command.Ide
+        Command.Publish
+        Command.REPL
+        Paths_purescript
+        Version
+    default-language: Haskell2010
+    ghc-options: -Wall -O2 -fno-warn-unused-do-bind -threaded -rtsopts
+                 -with-rtsopts=-N
     build-depends:
-        gitrev >=1.2.0 && <1.4
-  default-language: Haskell2010
+        Cabal >=2.2 && <2.5,
+        Glob ==0.9.*,
+        aeson >=1.0 && <1.5,
+        aeson-better-errors >=0.8 && <0.10,
+        aeson-pretty <0.9,
+        ansi-terminal >=0.7.1 && <0.9,
+        ansi-wl-pprint <0.7,
+        array <0.6,
+        base >=4.11 && <4.13,
+        base-compat >=0.6.0 && <0.11,
+        blaze-html >=0.8.1 && <0.10,
+        bower-json >=1.0.0.1 && <1.1,
+        boxes >=0.1.4 && <0.2.0,
+        bytestring <0.11,
+        cheapskate ==0.1.*,
+        clock <0.8,
+        containers <0.7,
+        data-ordlist >=0.4.7.0 && <0.5,
+        deepseq <1.5,
+        directory >=1.2.3 && <1.4,
+        dlist <0.9,
+        edit-distance <0.3,
+        file-embed <0.1,
+        filepath <1.5,
+        fsnotify >=0.2.1 && <0.4,
+        haskeline >=0.7.0.0 && <0.8,
+        http-types <0.13,
+        language-javascript >=0.6.0.13 && <0.7,
+        lifted-async >=0.10.0.3 && <0.10.1,
+        lifted-base ==0.2.3.*,
+        microlens-platform >=0.3.9.0 && <0.4,
+        monad-control >=1.0.0.0 && <1.1,
+        monad-logger ==0.3.*,
+        mtl >=2.1.0 && <2.3.0,
+        network >=3.0.1.1 && <3.1,
+        optparse-applicative >=0.13.0 && <0.15,
+        parallel ==3.2.*,
+        parsec >=3.1.10 && <3.2,
+        pattern-arrows >=0.0.2 && <0.1,
+        process >=1.2.0 && <1.7,
+        protolude >=0.1.6 && <0.3,
+        purescript -any,
+        regex-tdfa <1.3,
+        safe >=0.3.9 && <0.4,
+        scientific >=0.3.4.9 && <0.4,
+        semigroups >=0.16.2 && <0.19,
+        sourcemap >=0.1.6 && <0.2,
+        split <0.3,
+        stm >=0.2.4.0 && <2.6,
+        stringsearch <0.4,
+        syb <0.8,
+        text <1.3,
+        time <1.9,
+        transformers >=0.3.0 && <0.6,
+        transformers-base >=0.4.0 && <0.5,
+        transformers-compat >=0.3.0 && <0.7,
+        unordered-containers <0.3,
+        utf8-string ==1.*,
+        vector <0.13,
+        wai ==3.*,
+        wai-websockets ==3.*,
+        warp ==3.*,
+        websockets >=0.9 && <0.13
+    
+    if flag(release)
+        cpp-options: -DRELEASE
+    else
+        build-depends:
+            gitrev >=1.2.0 && <1.4
 
 test-suite tests
-  type: exitcode-stdio-1.0
-  main-is: Main.hs
-  other-modules:
-      Language.PureScript.Ide.CompletionSpec
-      Language.PureScript.Ide.FilterSpec
-      Language.PureScript.Ide.ImportsSpec
-      Language.PureScript.Ide.MatcherSpec
-      Language.PureScript.Ide.RebuildSpec
-      Language.PureScript.Ide.ReexportsSpec
-      Language.PureScript.Ide.SourceFileSpec
-      Language.PureScript.Ide.StateSpec
-      Language.PureScript.Ide.Test
-      Language.PureScript.Ide.UsageSpec
-      PscIdeSpec
-      TestBundle
-      TestCompiler
-      TestCoreFn
-      TestCst
-      TestDocs
-      TestHierarchy
-      TestIde
-      TestPrimDocs
-      TestPsci
-      TestPsci.CommandTest
-      TestPsci.CompletionTest
-      TestPsci.EvalTest
-      TestPsci.TestEnv
-      TestPscPublish
-      TestUtils
-      Paths_purescript
-  hs-source-dirs:
-      tests
-  default-extensions: NoImplicitPrelude
-  ghc-options: -Wall
-  build-depends:
-      Cabal >=2.2
-    , Glob >=0.9 && <0.10
-    , HUnit
-    , aeson >=1.0 && <1.5
-    , aeson-better-errors >=0.8
-    , ansi-terminal >=0.7.1 && <0.9
-    , array
-    , base >=4.8 && <4.13
-    , base-compat >=0.6.0
-    , blaze-html >=0.8.1 && <0.10
-    , bower-json >=1.0.0.1 && <1.1
-    , boxes >=0.1.4 && <0.2.0
-    , bytestring
-    , cheapskate >=0.1 && <0.2
-    , clock
-    , containers
-    , data-ordlist >=0.4.7.0
-    , deepseq
-    , directory >=1.2.3
-    , dlist
-    , edit-distance
-    , file-embed
-    , filepath
-    , fsnotify >=0.2.1
-    , haskeline >=0.7.0.0
-    , hspec
-    , hspec-discover
-    , language-javascript >=0.6.0.9 && <0.7
-    , lifted-async >=0.10.0.3 && <0.10.1
-    , lifted-base >=0.2.3 && <0.2.4
-    , microlens-platform >=0.3.9.0 && <0.4
-    , monad-control >=1.0.0.0 && <1.1
-    , monad-logger >=0.3 && <0.4
-    , mtl >=2.1.0 && <2.3.0
-    , parallel >=3.2 && <3.3
-    , parsec >=3.1.10
-    , pattern-arrows >=0.0.2 && <0.1
-    , process >=1.2.0 && <1.7
-    , protolude >=0.1.6
-    , purescript
-    , regex-tdfa
-    , safe >=0.3.9 && <0.4
-    , scientific >=0.3.4.9 && <0.4
-    , semigroups >=0.16.2 && <0.19
-    , sourcemap >=0.1.6
-    , split
-    , stm >=0.2.4.0
-    , stringsearch
-    , syb
-    , tasty
-    , tasty-golden
-    , tasty-hspec
-    , tasty-quickcheck
-    , text
-    , time
-    , transformers >=0.3.0 && <0.6
-    , transformers-base >=0.4.0 && <0.5
-    , transformers-compat >=0.3.0
-    , unordered-containers
-    , utf8-string >=1 && <2
-    , vector
-  default-language: Haskell2010
+    type: exitcode-stdio-1.0
+    main-is: Main.hs
+    build-tools: happy ==1.19.9
+    hs-source-dirs: tests
+    other-modules:
+        Language.PureScript.Ide.CompletionSpec
+        Language.PureScript.Ide.FilterSpec
+        Language.PureScript.Ide.ImportsSpec
+        Language.PureScript.Ide.MatcherSpec
+        Language.PureScript.Ide.RebuildSpec
+        Language.PureScript.Ide.ReexportsSpec
+        Language.PureScript.Ide.SourceFileSpec
+        Language.PureScript.Ide.StateSpec
+        Language.PureScript.Ide.Test
+        Language.PureScript.Ide.UsageSpec
+        PscIdeSpec
+        TestBundle
+        TestCompiler
+        TestCoreFn
+        TestCst
+        TestDocs
+        TestHierarchy
+        TestIde
+        TestPrimDocs
+        TestPsci
+        TestPsci.CommandTest
+        TestPsci.CompletionTest
+        TestPsci.EvalTest
+        TestPsci.TestEnv
+        TestPscPublish
+        TestUtils
+        Paths_purescript
+    default-language: Haskell2010
+    default-extensions: NoImplicitPrelude
+    ghc-options: -Wall
+    build-depends:
+        Cabal >=2.2 && <2.5,
+        Glob ==0.9.*,
+        HUnit <1.7,
+        aeson >=1.0 && <1.5,
+        aeson-better-errors >=0.8 && <0.10,
+        aeson-pretty <0.9,
+        ansi-terminal >=0.7.1 && <0.9,
+        array <0.6,
+        base >=4.11 && <4.13,
+        base-compat >=0.6.0 && <0.11,
+        blaze-html >=0.8.1 && <0.10,
+        bower-json >=1.0.0.1 && <1.1,
+        boxes >=0.1.4 && <0.2.0,
+        bytestring <0.11,
+        cheapskate ==0.1.*,
+        clock <0.8,
+        containers <0.7,
+        data-ordlist >=0.4.7.0 && <0.5,
+        deepseq <1.5,
+        directory >=1.2.3 && <1.4,
+        dlist <0.9,
+        edit-distance <0.3,
+        file-embed <0.1,
+        filepath <1.5,
+        fsnotify >=0.2.1 && <0.4,
+        haskeline >=0.7.0.0 && <0.8,
+        hspec <2.7,
+        hspec-discover <2.7,
+        language-javascript >=0.6.0.13 && <0.7,
+        lifted-async >=0.10.0.3 && <0.10.1,
+        lifted-base ==0.2.3.*,
+        microlens-platform >=0.3.9.0 && <0.4,
+        monad-control >=1.0.0.0 && <1.1,
+        monad-logger ==0.3.*,
+        mtl >=2.1.0 && <2.3.0,
+        parallel ==3.2.*,
+        parsec >=3.1.10 && <3.2,
+        pattern-arrows >=0.0.2 && <0.1,
+        process >=1.2.0 && <1.7,
+        protolude >=0.1.6 && <0.3,
+        purescript -any,
+        regex-tdfa <1.3,
+        safe >=0.3.9 && <0.4,
+        scientific >=0.3.4.9 && <0.4,
+        semigroups >=0.16.2 && <0.19,
+        sourcemap >=0.1.6 && <0.2,
+        split <0.3,
+        stm >=0.2.4.0 && <2.6,
+        stringsearch <0.4,
+        syb <0.8,
+        tasty <1.3,
+        tasty-golden <2.4,
+        tasty-hspec <1.2,
+        tasty-quickcheck <0.11,
+        text <1.3,
+        time <1.9,
+        transformers >=0.3.0 && <0.6,
+        transformers-base >=0.4.0 && <0.5,
+        transformers-compat >=0.3.0 && <0.7,
+        unordered-containers <0.3,
+        utf8-string ==1.*,
+        vector <0.13
diff --git a/src/Language/PureScript/Bundle.hs b/src/Language/PureScript/Bundle.hs
--- a/src/Language/PureScript/Bundle.hs
+++ b/src/Language/PureScript/Bundle.hs
@@ -14,6 +14,7 @@
   , ErrorMessage(..)
   , printErrorMessage
   , getExportedIdentifiers
+  , Module
   ) where
 
 import Prelude.Compat
@@ -23,6 +24,7 @@
 import Control.Monad.Error.Class
 import Control.Arrow ((&&&))
 
+import Data.Aeson ((.=))
 import Data.Array ((!))
 import Data.Char (chr, digitToInt)
 import Data.Foldable (fold)
@@ -31,11 +33,14 @@
 import Data.List (stripPrefix)
 import Data.Maybe (catMaybes, fromMaybe, mapMaybe)
 import Data.Version (showVersion)
+import qualified Data.Aeson as A
 import qualified Data.Map as M
 import qualified Data.Set as S
+import qualified Data.Text.Lazy as T
 
 import Language.JavaScript.Parser
 import Language.JavaScript.Parser.AST
+import Language.JavaScript.Process.Minify
 
 import qualified Paths_purescript as Paths
 
@@ -69,6 +74,12 @@
 -- | A module is identified by its module name and its type.
 data ModuleIdentifier = ModuleIdentifier String ModuleType deriving (Show, Eq, Ord)
 
+instance A.ToJSON ModuleIdentifier where
+  toJSON (ModuleIdentifier name mt) =
+    A.object [ "name" .= name
+             , "type" .= show mt
+             ]
+
 moduleName :: ModuleIdentifier -> String
 moduleName (ModuleIdentifier name _) = name
 
@@ -110,9 +121,71 @@
   | Skip JSStatement
   deriving (Show)
 
+instance A.ToJSON ModuleElement where
+  toJSON = \case
+    (Require _ name (Right target)) ->
+      A.object [ "type"   .= A.String "Require"
+               , "name"   .= name
+               , "target" .= target
+               ]
+    (Require _ name (Left targetPath)) ->
+      A.object [ "type"       .= A.String "Require"
+               , "name"       .= name
+               , "targetPath" .= targetPath
+               ]
+    (Member _ public name _ dependsOn) ->
+      A.object [ "type"       .= A.String "Member"
+               , "name"       .= name
+               , "visibility" .= A.String (if public then "Public" else "Internal")
+               , "dependsOn"  .= map keyToJSON dependsOn
+               ]
+    (ExportsList exports) ->
+      A.object [ "type"    .= A.String "ExportsList"
+               , "exports" .= map exportToJSON exports
+               ]
+    (Other stmt) ->
+      A.object [ "type" .= A.String "Other"
+               , "js"   .= getFragment stmt
+               ]
+    (Skip stmt) ->
+      A.object [ "type" .= A.String "Skip"
+               , "js"   .= getFragment stmt
+               ]
+
+    where
+
+    keyToJSON (mid, member) =
+      A.object [ "module" .= mid
+               , "member" .= member
+               ]
+
+    exportToJSON (RegularExport sourceName, name, _, dependsOn) =
+      A.object [ "type"       .= A.String "RegularExport"
+               , "name"       .= name
+               , "sourceName" .= sourceName
+               , "dependsOn"  .= map keyToJSON dependsOn
+               ]
+    exportToJSON (ForeignReexport, name, _, dependsOn) =
+      A.object [ "type"      .= A.String "ForeignReexport"
+               , "name"      .= name
+               , "dependsOn" .= map keyToJSON dependsOn
+               ]
+
+    getFragment = ellipsize . renderToText . minifyJS . flip JSAstStatement JSNoAnnot
+      where
+      ellipsize text = if T.compareLength text 20 == GT then T.take 19 text `T.snoc` ellipsis else text
+      ellipsis = '\x2026'
+
 -- | A module is just a list of elements of the types listed above.
 data Module = Module ModuleIdentifier (Maybe FilePath) [ModuleElement] deriving (Show)
 
+instance A.ToJSON Module where
+  toJSON (Module moduleId filePath elements) =
+    A.object [ "moduleId" .= moduleId
+             , "filePath" .= filePath
+             , "elements" .= elements
+             ]
+
 -- | Prepare an error message for consumption by humans.
 printErrorMessage :: ErrorMessage -> [String]
 printErrorMessage (UnsupportedModulePath s) =
@@ -730,8 +803,9 @@
        -> Maybe String -- ^ An optional main module.
        -> String -- ^ The namespace (e.g. PS).
        -> Maybe FilePath -- ^ The output file name (if there is one - in which case generate source map)
+       -> Maybe ([Module] -> m ()) -- ^ Optionally report the parsed modules prior to DCE -- used by "bundle --debug"
        -> m (Maybe SourceMapping, String)
-bundleSM inputStrs entryPoints mainModule namespace outFilename = do
+bundleSM inputStrs entryPoints mainModule namespace outFilename reportRawModules = do
   let mid (a,_,_) = a
   forM_ mainModule $ \mname ->
     when (mname `notElem` map (moduleName . mid) inputStrs) (throwError (MissingMainModule mname))
@@ -745,6 +819,8 @@
 
   modules <- traverse (fmap withDeps . (\(a,fn,c) -> toModule mids a fn c)) input
 
+  forM_ reportRawModules ($ modules)
+
   let compiled = compile modules entryPoints
       sorted   = sortModules (filter (not . isModuleEmpty) compiled)
 
@@ -759,4 +835,4 @@
        -> Maybe String -- ^ An optional main module.
        -> String -- ^ The namespace (e.g. PS).
        -> m String
-bundle inputStrs entryPoints mainModule namespace = snd <$> bundleSM (map (\(a,b) -> (a,Nothing,b)) inputStrs) entryPoints mainModule namespace Nothing
+bundle inputStrs entryPoints mainModule namespace = snd <$> bundleSM (map (\(a,b) -> (a,Nothing,b)) inputStrs) entryPoints mainModule namespace Nothing Nothing
diff --git a/src/Language/PureScript/CST/Errors.hs b/src/Language/PureScript/CST/Errors.hs
--- a/src/Language/PureScript/CST/Errors.hs
+++ b/src/Language/PureScript/CST/Errors.hs
@@ -33,6 +33,7 @@
   | ErrGuardInLetBinder
   | ErrKeywordVar
   | ErrKeywordSymbol
+  | ErrQuotedPun
   | ErrToken
   | ErrLineFeedInString
   | ErrAstralCodePointInChar
@@ -103,6 +104,8 @@
     "Expected variable, saw keyword"
   ErrKeywordSymbol ->
     "Expected symbol, saw reserved symbol"
+  ErrQuotedPun ->
+    "Unexpected quoted label in record pun, perhaps due to a missing ':'"
   ErrEof ->
     "Unexpected end of input"
   ErrLexeme (Just (hd : _)) _ | isSpace hd ->
diff --git a/src/Language/PureScript/CST/Layout.hs b/src/Language/PureScript/CST/Layout.hs
--- a/src/Language/PureScript/CST/Layout.hs
+++ b/src/Language/PureScript/CST/Layout.hs
@@ -146,13 +146,15 @@
       inP _ lyt    = isIndented lyt
 
     TokLowerName [] "let" ->
-      case stk of
+      state & insertKwProperty next
+      where
+      next state'@(stk', _) = case stk' of
         (p, LytDo) : _ | srcColumn p == srcColumn tokPos ->
-          state & insertKwProperty (insertStart LytLetStmt)
+          state' & insertStart LytLetStmt
         (p, LytAdo) : _ | srcColumn p == srcColumn tokPos ->
-          state & insertKwProperty (insertStart LytLetStmt)
+          state' & insertStart LytLetStmt
         _ ->
-          state & insertKwProperty (insertStart LytLet)
+          state' & insertStart LytLet
 
     TokLowerName _ "do" ->
       state & insertKwProperty (insertStart LytDo)
diff --git a/src/Language/PureScript/CST/Lexer.hs b/src/Language/PureScript/CST/Lexer.hs
--- a/src/Language/PureScript/CST/Lexer.hs
+++ b/src/Language/PureScript/CST/Lexer.hs
@@ -495,9 +495,9 @@
   escape = do
     ch <- peek
     case ch of
-      Just 't'  -> next $> ("\t", '\t')
-      Just 'r'  -> next $> ("\\r", '\r')
-      Just 'n'  -> next $> ("\\n", '\n')
+      Just 't'  -> next $> ("t", '\t')
+      Just 'r'  -> next $> ("r", '\r')
+      Just 'n'  -> next $> ("n", '\n')
       Just '"'  -> next $> ("\"", '"')
       Just '\'' -> next $> ("'", '\'')
       Just '\\' -> next $> ("\\", '\\')
@@ -508,7 +508,7 @@
           go n acc _
             | n <= 0x10FFFF =
                 ksucc (Text.drop (length acc) inp)
-                  (Text.pack $ reverse acc, Char.chr n)
+                  ("x" <> Text.pack (reverse acc), Char.chr n)
             | otherwise =
                 kerr inp ErrCharEscape -- TODO
         go 0 [] $ Text.unpack $ Text.take 6 inp
diff --git a/src/Language/PureScript/CST/Parser.y b/src/Language/PureScript/CST/Parser.y
--- a/src/Language/PureScript/CST/Parser.y
+++ b/src/Language/PureScript/CST/Parser.y
@@ -138,36 +138,36 @@
 
 %%
 
-many(a) :: { NE.NonEmpty _ }
+many(a) :: { NE.NonEmpty a }
   : many1(a) { NE.reverse $1 }
 
-many1(a) :: { NE.NonEmpty _ }
+many1(a) :: { NE.NonEmpty a }
   : a { pure $1 }
   | many1(a) a { NE.cons $2 $1 }
 
-manySep(a, sep) :: { NE.NonEmpty _ }
+manySep(a, sep) :: { NE.NonEmpty a }
   : manySep1(a, sep) { NE.reverse $1 }
 
-manySep1(a, sep) :: { NE.NonEmpty _ }
+manySep1(a, sep) :: { NE.NonEmpty a }
   : a { pure $1 }
   | manySep1(a, sep) sep a { NE.cons $3 $1 }
 
-manySepOrEmpty(a, sep) :: { [_] }
+manySepOrEmpty(a, sep) :: { [a] }
   : {- empty -} { [] }
   | manySep(a, sep) { NE.toList $1 }
 
-manyOrEmpty(a) :: { [_] }
+manyOrEmpty(a) :: { [a] }
   : {- empty -} { [] }
   | many(a) { NE.toList $1 }
 
-sep(a, s) :: { Separated _ }
+sep(a, s) :: { Separated a }
   : sep1(a, s) { separated $1 }
 
-sep1(a, s) :: { [(SourceToken, _)] }
+sep1(a, s) :: { [(SourceToken, a)] }
   : a { [(placeholder, $1)] }
   | sep1(a, s) s a { ($2, $3) : $1 }
 
-delim(a, b, c, d) :: { Delimited _ }
+delim(a, b, c, d) :: { Delimited b }
   : a d { Wrapped $1 Nothing $2 }
   | a sep(b, c) d { Wrapped $1 (Just $2) $3 }
 
@@ -395,7 +395,7 @@
   -- case, since this is used in the wild.
   | 'case' sep(expr, ',') 'of' '\{' sep(binder1, ',') '->' '\}' exprWhere
       { ExprCase () (CaseOf $1 $2 $3 (pure ($5, Unconditional $6 $8))) }
-  | 'case' sep(expr, ',') 'of' '\{' sep(binder1, ',') '\}' guarded('->')
+  | 'case' sep(expr, ',') 'of' '\{' sep(binder1, ',') '\}' guardedCase
       { ExprCase () (CaseOf $1 $2 $3 (pure ($5, $7))) }
 
 expr6 :: { Expr () }
@@ -442,20 +442,27 @@
 
 letBinding :: { LetBinding () }
   : ident '::' type { LetBindingSignature () (Labeled $1 $2 $3) }
-  | ident guarded('=') { LetBindingName () (ValueBindingFields $1 [] $2) }
-  | ident many(binderAtom) guarded('=') { LetBindingName () (ValueBindingFields $1 (NE.toList $2) $3) }
+  | ident guardedDecl { LetBindingName () (ValueBindingFields $1 [] $2) }
+  | ident many(binderAtom) guardedDecl { LetBindingName () (ValueBindingFields $1 (NE.toList $2) $3) }
   | binder1 '=' exprWhere { LetBindingPattern () $1 $2 $3 }
 
 caseBranch :: { (Separated (Binder ()), Guarded ()) }
-  : sep(binder1, ',') guarded('->') { ($1, $2) }
+  : sep(binder1, ',') guardedCase { ($1, $2) }
 
-guarded(a) :: { Guarded () }
-  : a exprWhere { Unconditional $1 $2 }
-  | many(guardedExpr(a)) { Guarded $1 }
+guardedDecl :: { Guarded () }
+  : '=' exprWhere { Unconditional $1 $2 }
+  | many(guardedDeclExpr) { Guarded $1 }
 
-guardedExpr(a) :: { GuardedExpr () }
-  : guard a exprWhere { uncurry GuardedExpr $1 $2 $3 }
+guardedDeclExpr :: { GuardedExpr () }
+  : guard '=' exprWhere { uncurry GuardedExpr $1 $2 $3 }
 
+guardedCase :: { Guarded () }
+  : '->' exprWhere { Unconditional $1 $2 }
+  | many(guardedCaseExpr) { Guarded $1 }
+
+guardedCaseExpr :: { GuardedExpr () }
+  : guard '->' exprWhere { uncurry GuardedExpr $1 $2 $3 }
+
 -- Do/Ado statements and pattern guards require unbounded lookahead due to many
 -- conflicts between `binder` and `expr` syntax. For example `Foo a b c` can
 -- either be a constructor `binder` or several `expr` applications, and we won't
@@ -652,7 +659,7 @@
   | 'derive' instHead { DeclDerive () $1 Nothing $2 }
   | 'derive' 'newtype' instHead { DeclDerive () $1 (Just $2) $3 }
   | ident '::' type { DeclSignature () (Labeled $1 $2 $3) }
-  | ident manyOrEmpty(binderAtom) guarded('=') { DeclValue () (ValueBindingFields $1 $2 $3) }
+  | ident manyOrEmpty(binderAtom) guardedDecl { DeclValue () (ValueBindingFields $1 $2 $3) }
   | fixity { DeclFixity () $1 }
   | 'foreign' 'import' foreign { DeclForeign () $1 $2 $3 }
 
@@ -720,7 +727,7 @@
 
 instBinding :: { InstanceBinding () }
   : ident '::' type { InstanceBindingSignature () (Labeled $1 $2 $3) }
-  | ident manyOrEmpty(binderAtom) guarded('=') { InstanceBindingName () (ValueBindingFields $1 $2 $3) }
+  | ident manyOrEmpty(binderAtom) guardedDecl { InstanceBindingName () (ValueBindingFields $1 $2 $3) }
 
 fixity :: { FixityFields }
   : infix int qualIdent 'as' op { FixityFields $1 $2 (FixityValue (fmap Left $3) $4 $5) }
diff --git a/src/Language/PureScript/CST/Utils.hs b/src/Language/PureScript/CST/Utils.hs
--- a/src/Language/PureScript/CST/Utils.hs
+++ b/src/Language/PureScript/CST/Utils.hs
@@ -107,6 +107,8 @@
   TokLowerName [] a
     | not (Set.member a reservedNames) -> pure $ Name tok (k a)
     | otherwise -> addFailure [tok] ErrKeywordVar $> Name tok (k "<unexpected>")
+  TokString _ _ -> parseFail tok ErrQuotedPun
+  TokRawString _ -> parseFail tok ErrQuotedPun
   TokUpperName [] a  -> pure $ Name tok (k a)
   TokSymbolName [] a -> pure $ Name tok (k a)
   TokOperator [] a   -> pure $ Name tok (k a)
diff --git a/src/Language/PureScript/Docs/Collect.hs b/src/Language/PureScript/Docs/Collect.hs
--- a/src/Language/PureScript/Docs/Collect.hs
+++ b/src/Language/PureScript/Docs/Collect.hs
@@ -13,7 +13,7 @@
 import qualified Data.Set as Set
 import qualified Data.Text as T
 import System.FilePath ((</>))
-import System.IO.UTF8 (readUTF8FileT)
+import System.IO.UTF8 (readUTF8FileT, readUTF8FilesT)
 
 import Language.PureScript.Docs.Convert.ReExports (updateReExports)
 import Language.PureScript.Docs.Prim (primModules)
@@ -89,7 +89,7 @@
   m [P.ExternsFile]
 compileForDocs outputDir inputFiles = do
   result <- liftIO $ do
-    moduleFiles <- readInput inputFiles
+    moduleFiles <- readUTF8FilesT inputFiles
     fmap fst $ P.runMake testOptions $ do
       ms <- P.parseModulesFromFiles identity moduleFiles
       let filePathMap = Map.fromList $ map (\(fp, pm) -> (P.getModuleName $ P.resPartial pm, Right fp)) ms
@@ -105,10 +105,6 @@
   renderProgressMessage :: P.ProgressMessage -> String
   renderProgressMessage (P.CompilingModule mn) =
     "Compiling documentation for " ++ T.unpack (P.runModuleName mn)
-
-  readInput :: [FilePath] -> IO [(FilePath, Text)]
-  readInput files =
-    forM files $ \inFile -> (inFile, ) <$> readUTF8FileT inFile
 
   testOptions :: P.Options
   testOptions = P.defaultOptions { P.optionsCodegenTargets = Set.singleton P.Docs }
diff --git a/src/Language/PureScript/Interactive/Module.hs b/src/Language/PureScript/Interactive/Module.hs
--- a/src/Language/PureScript/Interactive/Module.hs
+++ b/src/Language/PureScript/Interactive/Module.hs
@@ -2,13 +2,12 @@
 
 import           Prelude.Compat
 
-import           Control.Monad
 import qualified Language.PureScript as P
 import qualified Language.PureScript.CST as CST
 import           Language.PureScript.Interactive.Types
 import           System.Directory (getCurrentDirectory)
 import           System.FilePath (pathSeparator, makeRelative)
-import           System.IO.UTF8 (readUTF8FileT)
+import           System.IO.UTF8 (readUTF8FileT, readUTF8FilesT)
 
 -- * Support Module
 
@@ -35,9 +34,7 @@
 loadAllModules :: [FilePath] -> IO (Either P.MultipleErrors [(FilePath, P.Module)])
 loadAllModules files = do
   pwd <- getCurrentDirectory
-  filesAndContent <- forM files $ \filename -> do
-    content <- readUTF8FileT filename
-    return (filename, content)
+  filesAndContent <- readUTF8FilesT files
   return $ CST.parseFromFiles (makeRelative pwd) filesAndContent
 
 -- |
diff --git a/src/System/IO/UTF8.hs b/src/System/IO/UTF8.hs
--- a/src/System/IO/UTF8.hs
+++ b/src/System/IO/UTF8.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE TupleSections #-}
+
 module System.IO.UTF8 where
 
 import Prelude.Compat
@@ -8,11 +10,16 @@
 import qualified Data.ByteString.UTF8 as UTF8
 import           Data.Text (Text)
 import qualified Data.Text.Encoding as TE
+import           Protolude (ordNub)
 
 -- | Unfortunately ByteString's readFile does not convert line endings on
 -- Windows, so we have to do it ourselves
 fixCRLF :: BS.ByteString -> BS.ByteString
 fixCRLF = BSL.toStrict . BSS.replace "\r\n" ("\n" :: BS.ByteString)
+
+readUTF8FilesT :: [FilePath] -> IO [(FilePath, Text)]
+readUTF8FilesT =
+  traverse (\inFile -> (inFile, ) <$> readUTF8FileT inFile) . ordNub
 
 readUTF8FileT :: FilePath -> IO Text
 readUTF8FileT inFile =
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,7 +1,10 @@
-resolver: lts-13.12
+resolver: lts-13.26
+pvp-bounds: upper
 packages:
 - '.'
 extra-deps:
+- happy-1.19.9
+- language-javascript-0.6.0.13
 - network-3.0.1.1
 nix:
   enable: false
@@ -11,3 +14,6 @@
   - nodejs
   - nodePackages.npm
   - nodePackages.bower
+flags:
+  aeson-pretty:
+    lib-only: true
diff --git a/tests/TestBundle.hs b/tests/TestBundle.hs
--- a/tests/TestBundle.hs
+++ b/tests/TestBundle.hs
@@ -74,7 +74,7 @@
             js <- liftIO $ readUTF8File filename
             mid <- guessModuleIdentifier filename
             length js `seq` return (mid, Just filename, js) 
-          bundleSM input entryModule (Just $ "Main") "PS" (Just entryPoint)
+          bundleSM input entryModule (Just $ "Main") "PS" (Just entryPoint) Nothing
         case bundled of
             Right (_, js) -> do
               writeUTF8File entryPoint js
diff --git a/tests/TestCst.hs b/tests/TestCst.hs
--- a/tests/TestCst.hs
+++ b/tests/TestCst.hs
@@ -67,15 +67,18 @@
   , testProperty "Raw String (round trip)" $ roundTripTok . unRawString
   ]
 
-readTok :: Text -> Gen SourceToken
-readTok t = case CST.lex t of
+readTok' :: String -> Text -> Gen SourceToken
+readTok' failMsg t = case CST.lex t of
   Right tok : _ ->
     pure tok
   Left (_, err) : _ ->
-    fail $ "Failed to parse: " <> CST.prettyPrintError err
+    fail $ failMsg <> ": " <> CST.prettyPrintError err
   [] ->
     fail "Empty token stream"
 
+readTok :: Text -> Gen SourceToken
+readTok = readTok' "Failed to parse"
+
 checkTok
   :: (Text -> a -> Gen Bool)
   -> (Token -> Maybe a)
@@ -91,7 +94,7 @@
 roundTripTok t = do
   tok <- readTok t
   let t' = CST.printTokens [tok]
-  tok' <- readTok t'
+  tok' <- readTok' "Failed to re-parse" t'
   pure $ tok == tok'
 
 checkReadNum :: (Eq a, Read a) => Text -> a -> Gen Bool
@@ -168,22 +171,22 @@
 
 genChar :: Gen PSSourceChar
 genChar = PSSourceChar <$> do
-  ch  <- (toEnum :: Int -> Char) <$> resize 0xFFFF arbitrarySizedNatural
-  ch' <- case ch of
-    '\'' -> discard
-    '\\' -> genCharEscape
-    c    ->  pure $ Text.singleton c
-  pure $ "'" <> ch' <> "'"
+  ch <- resize 0xFFFF arbitrarySizedNatural >>= (genStringChar '\'' . toEnum)
+  pure $ "'" <> ch <> "'"
 
 genString :: Gen PSSourceString
 genString = PSSourceString <$> do
-  chs <- listOf $ arbitraryUnicodeChar >>= \case
-    '"'  -> discard
-    '\n' -> discard
-    '\r' -> discard
-    '\\' -> genCharEscape
-    c    -> pure $ Text.singleton c
+  chs <- listOf $ arbitraryUnicodeChar >>= genStringChar '"'
   pure $ "\"" <> Text.concat chs <> "\""
+
+genStringChar :: Char -> Char -> Gen Text
+genStringChar delimiter ch = frequency
+  [ (1, genCharEscape)
+  , (10, if ch `elem` [delimiter, '\n', '\r', '\\']
+           then discard
+           else pure $ Text.singleton ch
+    )
+  ]
 
 genRawString :: Gen PSSourceRawString
 genRawString = PSSourceRawString <$> do
diff --git a/tests/purs/failing/3689.purs b/tests/purs/failing/3689.purs
new file mode 100644
--- /dev/null
+++ b/tests/purs/failing/3689.purs
@@ -0,0 +1,6 @@
+-- @shouldFailWith ErrorParsingModule
+module Main where
+
+test =
+  { "bad"
+  }
diff --git a/tests/purs/layout/AdoIn.purs b/tests/purs/layout/AdoIn.purs
--- a/tests/purs/layout/AdoIn.purs
+++ b/tests/purs/layout/AdoIn.purs
@@ -11,3 +11,9 @@
   foo <- bar $ let a = 42 in a
   baz <- b
   in bar
+
+test = ado
+  foo
+  let bar = let a = 42 in a
+  let baz = 42
+  in bar
diff --git a/tests/purs/warning/CustomWarning4.purs b/tests/purs/warning/CustomWarning4.purs
--- a/tests/purs/warning/CustomWarning4.purs
+++ b/tests/purs/warning/CustomWarning4.purs
@@ -6,7 +6,7 @@
 
 import Prim.TypeError (class Warn, Beside, QuoteLabel, Text)
 import Prim
-import Type.Row (class RowToList, Cons, Nil)
+import Type.RowList (class RowToList, Cons, Nil)
 
 data Label (l :: Symbol) = Label
 
diff --git a/tests/support/bower.json b/tests/support/bower.json
--- a/tests/support/bower.json
+++ b/tests/support/bower.json
@@ -33,7 +33,7 @@
     "purescript-tailrec": "4.0.0",
     "purescript-tuples": "5.0.0",
     "purescript-type-equality": "3.0.0",
-    "purescript-typelevel-prelude": "4.0.1",
+    "purescript-typelevel-prelude": "5.0.0",
     "purescript-unfoldable": "4.0.0",
     "purescript-unsafe-coerce": "4.0.0"
   }
