diff --git a/Changelog.md b/Changelog.md
new file mode 100644
--- /dev/null
+++ b/Changelog.md
@@ -0,0 +1,9 @@
+## [_Unreleased_](https://github.com/freckle/yesod-routes-flow/compare/v3.0.0.1...main)
+
+None
+
+## [v3.0.0.1](https://github.com/freckle/yesod-routes-flow/compare/2.0...v3.0.0.1)
+
+- Setup CI/CD
+- Starting in `yesod-core-1.6.2`, `yesod-core` started deriving `Show` for `ResourceTree` and `FlatResource`. To prevent duplicate instance errors, this package now only derives `Show` for `yesod-core < 1.6.2`.
+- An implication of this is that building anything less than `yesod-routes-flow-3.0.0.1` with `yesod-core-1.6.2` will cause duplicate instance compiler errors.
diff --git a/Yesod/Routes/Flow/Generator.hs b/Yesod/Routes/Flow/Generator.hs
--- a/Yesod/Routes/Flow/Generator.hs
+++ b/Yesod/Routes/Flow/Generator.hs
@@ -1,69 +1,104 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE RecordWildCards #-}
 module Yesod.Routes.Flow.Generator
-    ( genFlowRoutesPrefix
-    , genFlowRoutes
-    ) where
+  ( genFlowRoutes
+  , genFlowRoutesPrefix
+  , genFlowSource
+  , genFlowClasses
+  , classesToFlow
+  , Class(..)
+  , ClassMember(..)
+  , RenderedPiece(..)
+  , PieceType(..)
+  ) where
 
 import ClassyPrelude hiding (FilePath)
+import qualified Data.Char as C
+import qualified Data.List as L
+import qualified Data.Map as Map
 import Data.Text (dropWhileEnd)
+import qualified Data.Text as T
 import Filesystem (createTree, writeTextFile)
 import Filesystem.Path (FilePath, directory)
 import Yesod.Routes.TH.Types
-import qualified Data.Char as C
-import qualified Data.Map  as M
-import qualified Data.Text as T
 
+-- An override map from Haskell type name to Flow type name
+type Overrides = Map.Map String PieceType
 
 genFlowRoutes :: [ResourceTree String] -> FilePath -> IO ()
-genFlowRoutes ra fp = genFlowRoutesPrefix [] [] ra fp "''"
+genFlowRoutes ra fp = genFlowRoutesPrefix Map.empty [] [] ra fp "''"
 
-genFlowRoutesPrefix :: [String] -> [String] -> [ResourceTree String] -> FilePath -> Text -> IO ()
-genFlowRoutesPrefix routePrefixes elidedPrefixes fullTree fp prefix = do
+genFlowRoutesPrefix :: Overrides -> [String] -> [String] -> [ResourceTree String] -> FilePath -> Text -> IO ()
+genFlowRoutesPrefix overrides routePrefixes elidedPrefixes fullTree fp prefix = do
     createTree $ directory fp
-    writeTextFile fp routesCs
-  where
-    routesCs =
-      let classes =
-            map disambiguateFields $
-            resourceTreeToClasses elidedPrefixes $
-            ResourceParent "paths" False [] hackedTree
-      in    "/* @flow */\n\n"
-         <> classesToFlow classes
-         <> "\n\nvar PATHS: PATHS_TYPE_paths = new PATHS_TYPE_paths(" <> prefix <> ");\n"
+    writeTextFile fp $ genFlowSource overrides routePrefixes elidedPrefixes prefix fullTree
 
-    -- Route hackery.
-    landingRoutes = flip filter fullTree $ \case
-        ResourceParent _ _ _ _ -> False
-        ResourceLeaf res -> not $ elem (resourceName res) ["AuthR", "StaticR"]
-    parents =
-        -- if routePrefixes is empty, include all routes
-        filter (\n -> null routePrefixes || any (parentName n) routePrefixes) fullTree
-    hackedTree = ResourceParent "staticPages" False [] landingRoutes : parents
+genFlowSource :: Overrides -> [String] -> [String] -> Text -> [ResourceTree String] -> Text
+genFlowSource overrides routePrefixes elidedPrefixes prefix fullTree =
+  mconcat
+    [ "/* @flow */\n\n"
+    , classesToFlow $ genFlowClasses overrides routePrefixes elidedPrefixes fullTree
+    , "\n\nvar PATHS: PATHS_TYPE_paths = new PATHS_TYPE_paths(" <> prefix <> ");\n"
+    ]
 
+genFlowClasses :: Overrides -> [String] -> [String] -> [ResourceTree String] -> [Class]
+genFlowClasses overrides routePrefixes elidedPrefixes fullTree =
+  map disambiguateFields $
+  resourceTreeToClasses overrides elidedPrefixes $
+  ResourceParent "paths" False [] hackedTree
+ where
+  -- Route hackery.
+  landingRoutes = flip filter fullTree $ \case
+      ResourceParent {} -> False
+      ResourceLeaf res  -> notElem (resourceName res) ["AuthR", "StaticR"]
+  parents =
+      -- if routePrefixes is empty, include all routes
+      filter (\n -> null routePrefixes || any (parentName n) routePrefixes) fullTree
+  hackedTree = ResourceParent "staticPages" False [] landingRoutes : parents
+
+
 parentName :: ResourceTree String -> String -> Bool
 parentName (ResourceParent n _ _ _) name = n == name
-parentName _ _  = False
+parentName _ _                           = False
 
 ----------------------------------------------------------------------
 
-data RenderedPiece =
-    Path Text
-  | Number
-  | String
+data RenderedPiece
+  = Path Text
+  | Dyn PieceType
+    deriving (Eq, Show)
 
+data PieceType
+  = NumberT
+  | StringT
+  | NonEmptyT PieceType
+    deriving (Eq, Show)
+
 isVariable :: RenderedPiece -> Bool
 isVariable (Path _) = False
-isVariable _        = True
+isVariable (Dyn _)  = True
 
-renderRoutePieces :: [Piece String] -> [RenderedPiece]
-renderRoutePieces = map renderRoutePiece
+renderRoutePieces :: Overrides -> [Piece String] -> [RenderedPiece]
+renderRoutePieces overrides = map renderRoutePiece
   where
     renderRoutePiece (Static st)   = Path $ T.dropAround (== '/') $ pack st
-    renderRoutePiece (Dynamic typ) = if isNumberType typ then Number else String
+    renderRoutePiece (Dynamic typ) = Dyn $ parseType typ
 
-    isNumberType "Int" = True
-    isNumberType type_ = "Id" `isSuffixOf` type_ -- UserId, PageId, PostId, etc.
+    parseType type_ =
+      fromMaybe
+        (maybe
+          (parseSimpleType type_)
+          (NonEmptyT . parseType)
+          (L.stripPrefix "NonEmpty" type_)) -- NonEmptyUserId ~ NonEmpty UserId
+        $ Map.lookup type_ overrides
 
+    parseSimpleType "Int" = NumberT
+    parseSimpleType type_
+      | "Id" `isSuffixOf` type_ = NumberT -- UserId, PageId, PostId, etc.
+      | otherwise = StringT
+
 ----------------------------------------------------------------------
 
 -- | A Flow class that will be generated.
@@ -72,6 +107,7 @@
     { className    :: Text
     , classMembers :: [ClassMember]
     }
+  deriving (Eq, Show)
 
 data ClassMember =
     -- | A 'ResourceParent' inside the 'ResourceParent'
@@ -82,9 +118,10 @@
       }
     -- | A callable method.
   | Method
-      { cmField     :: Text            -- ^ Field name used to refer to the method.
-      , cmPieces    :: [RenderedPiece] -- ^ Pieces to render the route.
+      { cmField  :: Text            -- ^ Field name used to refer to the method.
+      , cmPieces :: [RenderedPiece] -- ^ Pieces to render the route.
       }
+    deriving (Eq, Show)
 
 variableCount :: ClassMember -> Int
 variableCount ChildClass {} = 0
@@ -96,8 +133,8 @@
 ----------------------------------------------------------------------
 
 -- | Create a list of 'Class'es from a 'ResourceTree'.
-resourceTreeToClasses :: [String] -> ResourceTree String -> [Class]
-resourceTreeToClasses elidedPrefixes = finish . go Nothing []
+resourceTreeToClasses :: Overrides -> [String] -> ResourceTree String -> [Class]
+resourceTreeToClasses overrides elidedPrefixes = finish . go Nothing []
   where
     finish (Right (_, classes)) = classes
     finish (Left _)             = []
@@ -105,19 +142,19 @@
     go :: Maybe Text -> [RenderedPiece] -> ResourceTree String -> Either (Maybe ClassMember) ([ClassMember], [Class])
     go _parent routePrefix (ResourceLeaf res) =
       Left $ do
-        Methods _ methods <- return $ resourceDispatch res -- Ignore subsites.
+        Methods _ methods <- pure $ resourceDispatch res -- Ignore subsites.
         guard (not $ null methods) -- Silently ignore routes without methods.
         let resName  = T.replace "." "" $ T.replace "-" "_" fullName
             fullName = intercalate "_" [pack st :: Text | Static st <- resourcePieces res]
-        return Method
+        pure Method
           { cmField       = if null fullName then "_" else resName
-          , cmPieces      = routePrefix <> renderRoutePieces (resourcePieces res) }
+          , cmPieces      = routePrefix <> renderRoutePieces overrides (resourcePieces res) }
     go parent routePrefix (ResourceParent name _ pieces children) =
       let elideThisPrefix = name `elem` elidedPrefixes
           pref            = cleanName $ pack name
           jsName          = maybe "" (<> "_") parent <> pref
           newParent       = if elideThisPrefix then parent else Just jsName
-          newRoutePrefix  = routePrefix <> renderRoutePieces pieces
+          newRoutePrefix  = routePrefix <> renderRoutePieces overrides pieces
           membersMethods  = catMaybes childrenMethods
           (childrenMethods, childrenClasses) = partitionEithers $ map (go newParent newRoutePrefix) children
           (membersClasses, moreClasses)      = concat *** concat $ unzip childrenClasses
@@ -137,7 +174,7 @@
 
 cleanName :: Text -> Text
 cleanName = underscorize . uncapitalize . dropWhileEnd C.isUpper
-  where uncapitalize t = (toLower $ take 1 t) <> drop 1 t
+  where uncapitalize t = toLower (take 1 t) <> drop 1 t
         underscorize = T.pack . go . T.unpack
           where go (c:cs) | C.isUpper c = '_' : C.toLower c : go cs
                           | otherwise   =  c                : go cs
@@ -150,13 +187,13 @@
 disambiguateFields klass = klass { classMembers = processMembers $ classMembers klass }
   where
     processMembers = fromMap . disambiguate viaLetters . disambiguate viaArgCount . toMap
-    fromMap  = concat . M.elems
-    toMap    = M.fromListWith (++) . labelled
-    labelled = map (cmField &&& return)
-    append t = \cm -> cm { cmField = cmField cm <> t cm }
+    fromMap  = concat . Map.elems
+    toMap    = Map.fromListWith (++) . labelled
+    labelled = map (cmField &&& pure)
+    append t cm = cm { cmField = cmField cm <> t cm }
 
-    disambiguate :: ([ClassMember] -> [ClassMember]) -> M.Map Text [ClassMember] -> M.Map Text [ClassMember]
-    disambiguate inner = M.fromListWith (++) . concatMap f . M.toList
+    disambiguate :: ([ClassMember] -> [ClassMember]) -> Map.Map Text [ClassMember] -> Map.Map Text [ClassMember]
+    disambiguate inner = Map.fromListWith (++) . concatMap f . Map.toList
       where
         f :: (Text, [ClassMember]) -> [(Text, [ClassMember])]
         f y@(_, [ ]) = [y]
@@ -175,16 +212,40 @@
 classMemberToFlowDef ChildClass {..} = "  " <> cmField <> " : " <> cmClassName <> ";\n"
 classMemberToFlowDef Method {..}     = "  " <> cmField <> "(" <> args <> "): string { " <> body <> "; }\n"
   where
-    args = intercalate ", " $ zipWith render variableNames (filter isVariable cmPieces)
+    args = intercalate ", " $ zipWith render variableNames $ mapMaybe getType cmPieces
       where
-        render name typ = name <> ": " <> (case typ of { Number -> "number"; String -> "string" })
+        render name typ = name <> ": " <> argType typ
 
+        getType (Path _) = Nothing
+        getType (Dyn t)  = Just t
+
+        argType NumberT       = "number"
+        argType StringT       = "string"
+        argType (NonEmptyT t) = "Array<" <> argType t <> ">"
+
     body = "return this.root + '" <> routeStr variableNames cmPieces <> "'"
       where
-        routeStr vars (Path p:rest) = (if null p then "" else "/" <> p) <> routeStr vars rest
-        routeStr (v:vars)  (_:rest) = "/' + " <> v <> ".toString() + '" <> routeStr vars rest
-        routeStr _         _        = ""
+        routeStr vars     (Path p:rest) = (if null p then "" else "/" <> p) <> routeStr vars rest
+        routeStr (v:vars) (Dyn t:rest)  = "/' + " <> convert v 0 t <> " + '" <> routeStr vars rest
+        routeStr _         _            = ""
 
+        convert v i StringT = name v i
+        convert v i NumberT = name v i <> ".toString()"
+        convert v i (NonEmptyT t) =
+          T.concat
+            [ name v i
+            , ".map(function("
+            , name v (i + 1)
+            , ") { return "
+            , convert v (i + 1) t
+            , " }).join(',')"
+            ]
+
+        name :: Text -> Int -> Text
+        name v 0 = v
+        name v i = v <> pack (show i)
+
+
 classMemberToFlowInit :: ClassMember -> Text
 classMemberToFlowInit ChildClass {..} = "    this." <> cmField <> " = new " <> cmClassName <> "(root);\n"
 classMemberToFlowInit Method {}       = ""
@@ -204,5 +265,7 @@
 classesToFlow :: [Class] -> Text
 classesToFlow = intercalate "\n" . map classToFlow
 
+#if !MIN_VERSION_yesod_core(1, 6, 2)
 deriving instance (Show a) => Show (ResourceTree a)
 deriving instance (Show a) => Show (FlatResource a)
+#endif
diff --git a/test/GeneratorSpec.hs b/test/GeneratorSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/GeneratorSpec.hs
@@ -0,0 +1,190 @@
+{-# LANGUAGE QuasiQuotes #-}
+
+module GeneratorSpec where
+
+import Data.List.NonEmpty (NonEmpty(..))
+import qualified Data.Map as Map
+import TestImport
+
+type UserId = Int
+type NonEmptyUserId = NonEmpty UserId
+
+resources :: [ResourceTree String]
+resources = [parseRoutes|
+  /api ApiP:
+    /users/#UserId UsersR GET
+    /users/#Int/foo UsersFooR GET
+    /users/#NonEmptyUserId/bar UsersBarR GET
+    /users/#Other/baz UsersBazR GET
+|]
+
+spec :: Spec
+spec = do
+  describe "genFlowClasses" $ do
+    it "should generate classes" $ do
+      let classes = genFlowClasses Map.empty [] [] resources
+      map className classes
+        `shouldBe`
+          [ "PATHS_TYPE_paths"
+          , "PATHS_TYPE_paths_static_pages"
+          , "PATHS_TYPE_paths_api"
+          ]
+
+    it "should identify path variable types" $ do
+      let
+        classes = genFlowClasses Map.empty [] [] resources
+        apiClass = find ((== "PATHS_TYPE_paths_api") . className) classes
+      map classMembers apiClass
+        `shouldBe`
+          Just
+            [ Method
+              "users"
+              [ Path "api"
+              , Path "users"
+              , Dyn NumberT
+              ]
+            , Method
+              "users_bar"
+              [ Path "api"
+              , Path "users"
+              , Dyn (NonEmptyT NumberT)
+              , Path "bar"
+              ]
+            , Method
+              "users_baz"
+              [ Path "api"
+              , Path "users"
+              , Dyn StringT
+              , Path "baz"
+              ]
+            , Method
+              "users_foo"
+              [ Path "api"
+              , Path "users"
+              , Dyn NumberT
+              , Path "foo"
+              ]
+            ]
+    it "should respect overrides " $ do
+      let
+        classes = genFlowClasses (Map.fromList [("UserId", StringT)]) [] [] resources
+        apiClass = find ((== "PATHS_TYPE_paths_api") . className) classes
+      map classMembers apiClass
+        `shouldBe`
+          Just
+            [ Method
+              "users"
+              [ Path "api"
+              , Path "users"
+              , Dyn StringT
+              ]
+            , Method
+              "users_bar"
+              [ Path "api"
+              , Path "users"
+              , Dyn (NonEmptyT StringT)
+              , Path "bar"
+              ]
+            , Method
+              "users_baz"
+              [ Path "api"
+              , Path "users"
+              , Dyn StringT
+              , Path "baz"
+              ]
+            , Method
+              "users_foo"
+              [ Path "api"
+              , Path "users"
+              , Dyn NumberT
+              , Path "foo"
+              ]
+            ]
+  describe "classesToFlow" $ do
+    it "should generate formal parameters for each path variable" $ do
+      let
+        cls =
+          [ Class
+            "x"
+            [ Method
+              "y"
+              [ Path "x"
+              , Dyn StringT
+              , Path "y"
+              , Dyn NumberT
+              , Path "z"
+              , Dyn StringT
+              ]
+            ]
+          ]
+      normalizeText (classesToFlow cls)
+        `shouldBe`
+          normalizeText [st|
+            class x {
+              y(a: string, aa: number, aaa: string): string { return this.root + '/x/' + a + '/y/' + aa.toString() + '/z/' + aaa + ''; }
+              root: string;
+              constructor(root: string) {
+                this.root = root;
+              }
+            }
+          |]
+    it "should avoid name shadowing in nested binders" $ do
+      let
+        cls =
+          [ Class
+            "x"
+            [ Method
+              "y"
+              [ Path "x"
+              , Dyn (NonEmptyT (NonEmptyT NumberT))
+              , Path "y"
+              ]
+            ]
+          ]
+      normalizeText (classesToFlow cls)
+        `shouldBe`
+          normalizeText [st|
+            class x {
+              y(a: Array<Array<number>>): string { return this.root + '/x/' + a.map(function(a1) { return a1.map(function(a2) { return a2.toString() }).join(',') }).join(',') + '/y'; }
+              root: string;
+              constructor(root: string) {
+                this.root = root;
+              }
+            }
+          |]
+
+  describe "genFlowSource" $
+    it "should work end-to-end" $ do
+      let source = genFlowSource Map.empty [] [] "'test'" resources
+      normalizeText source
+        `shouldBe`
+          normalizeText [st|
+            /* @flow */
+            class PATHS_TYPE_paths {
+              api : PATHS_TYPE_paths_api;
+              static_pages : PATHS_TYPE_paths_static_pages;
+              root: string;
+              constructor(root: string) {
+                this.root = root;
+                this.api = new PATHS_TYPE_paths_api(root);
+                this.static_pages = new PATHS_TYPE_paths_static_pages(root);
+              }
+            }
+            class PATHS_TYPE_paths_static_pages {
+              root: string;
+              constructor(root: string) {
+                this.root = root;
+              }
+            }
+            class PATHS_TYPE_paths_api {
+              users(a: number): string { return this.root + '/api/users/' + a.toString() + ''; }
+              users_bar(a: Array<number>): string { return this.root + '/api/users/' + a.map(function(a1) { return a1.toString() }).join(',') + '/bar'; }
+              users_baz(a: string): string { return this.root + '/api/users/' + a + '/baz'; }
+              users_foo(a: number): string { return this.root + '/api/users/' + a.toString() + '/foo'; }
+              root: string;
+              constructor(root: string) {
+                this.root = root;
+              }
+            }
+            var PATHS: PATHS_TYPE_paths = new PATHS_TYPE_paths('test');
+          |]
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/test/TestImport.hs b/test/TestImport.hs
new file mode 100644
--- /dev/null
+++ b/test/TestImport.hs
@@ -0,0 +1,20 @@
+module TestImport
+  ( module X
+  , normalizeText
+  , parseRoutes
+  , ResourceTree(..)
+  , st
+  ) where
+
+import Yesod.Routes.Flow.Generator as X
+
+import ClassyPrelude as X
+import qualified Data.Text as T
+import Test.Hspec as X
+import Text.Shakespeare.Text (st)
+import Yesod.Core.Dispatch (parseRoutes)
+import Yesod.Routes.TH.Types (ResourceTree(..))
+
+-- Avoid having to exactly compare leading whitespace or empty lines
+normalizeText :: Text -> Text
+normalizeText = T.unlines . filter (/="") . map T.strip . T.lines
diff --git a/yesod-routes-flow.cabal b/yesod-routes-flow.cabal
--- a/yesod-routes-flow.cabal
+++ b/yesod-routes-flow.cabal
@@ -1,45 +1,76 @@
-name:                yesod-routes-flow
-version:             2.0
-synopsis:            Generate Flow routes for Yesod
-description:         Parse the Yesod routes data structure and generate routes that can be used with Flow.
-homepage:            https://github.com/frontrowed/yesod-routes-flow
-license:             MIT
-license-file:        LICENSE
-author:              Max Cantor, Felipe Lessa
-maintainer:          Greg Weber <greg@frontrowed.com>
--- copyright:
-category:            Web
-build-type:          Simple
-extra-source-files:  README.md
-cabal-version:       >=1.10
+cabal-version:   1.18
+name:            yesod-routes-flow
+version:         3.0.0.1
+license:         MIT
+license-file:    LICENSE
+copyright:       2021 Renaissance Learning Inc
+maintainer:      engineering@freckle.com
+author:          Freckle Engineering
+homepage:        https://github.com/freckle/yesod-routes-flow
+bug-reports:     https://github.com/freckle/yesod-routes-flow/issues
+synopsis:        Generate Flow routes for Yesod
+description:
+    Parse the Yesod routes data structure and generate routes that can be used with Flow.
 
+category:        Web
+build-type:      Simple
+extra-doc-files:
+    Changelog.md
+    README.md
+
+source-repository head
+    type:     git
+    location: https://github.com/freckle/yesod-routes-flow
+
 library
-  exposed-modules:     Yesod.Routes.Flow.Generator
-  -- other-modules:
-  -- other-extensions:
-  default-extensions:
-                        ConstraintKinds,
-                        DeriveDataTypeable,
-                        ExtendedDefaultRules,
-                        FlexibleContexts,
-                        FlexibleInstances,
-                        LambdaCase,
-                        NoImplicitPrelude,
-                        OverloadedStrings,
-                        RecordWildCards,
-                        ScopedTypeVariables,
-                        StandaloneDeriving,
-                        TemplateHaskell,
-                        TupleSections,
-                        TypeSynonymInstances
-  build-depends:
-                        attoparsec,
-                        base < 5,
-                        classy-prelude >= 0.7,
-                        system-fileio,
-                        system-filepath >= 0.4,
-                        containers,
-                        text,
-                        yesod-core >= 1.4 && < 2.0
-  -- hs-source-dirs:
-  default-language:    Haskell2010
+    exposed-modules:    Yesod.Routes.Flow.Generator
+    other-modules:      Paths_yesod_routes_flow
+    default-language:   Haskell2010
+    default-extensions:
+        ConstraintKinds DeriveDataTypeable ExtendedDefaultRules
+        FlexibleContexts FlexibleInstances LambdaCase NoImplicitPrelude
+        OverloadedStrings RecordWildCards ScopedTypeVariables
+        StandaloneDeriving TemplateHaskell TupleSections
+        TypeSynonymInstances
+
+    build-depends:
+        attoparsec >=0.13.2.0,
+        base >=4.10.1.0 && <5,
+        classy-prelude >=0.7,
+        containers >=0.5.10.2,
+        system-fileio >=0.3.16.3,
+        system-filepath >=0.4,
+        text >=1.2.2.2,
+        yesod-core >=1.4 && <2.0
+
+test-suite spec
+    type:               exitcode-stdio-1.0
+    main-is:            Spec.hs
+    hs-source-dirs:     test
+    other-modules:
+        GeneratorSpec
+        TestImport
+        Paths_yesod_routes_flow
+
+    default-language:   Haskell2010
+    default-extensions:
+        ConstraintKinds DeriveDataTypeable ExtendedDefaultRules
+        FlexibleContexts FlexibleInstances LambdaCase NoImplicitPrelude
+        OverloadedStrings RecordWildCards ScopedTypeVariables
+        StandaloneDeriving TemplateHaskell TupleSections
+        TypeSynonymInstances
+
+    build-depends:
+        attoparsec >=0.13.2.0,
+        base >=4.10.1.0 && <5,
+        classy-prelude >=0.7,
+        containers >=0.5.10.2,
+        hspec >=2.4.4,
+        hspec-expectations >=0.8.2,
+        semigroups >=0.18.3,
+        shakespeare >=2.0.14.1,
+        system-fileio >=0.3.16.3,
+        system-filepath >=0.4,
+        text >=1.2.2.2,
+        yesod-core >=1.4 && <2.0,
+        yesod-routes-flow -any
