diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Ian Duncan (c) 2017
+
+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 Ian Duncan 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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,1 @@
+# aeson-asset-map
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/asset-map.cabal b/asset-map.cabal
new file mode 100644
--- /dev/null
+++ b/asset-map.cabal
@@ -0,0 +1,40 @@
+name:                asset-map
+version:             0.1.0.0
+synopsis:            Asset map support for the JavaScript broccoli-asset-rev library.
+description:         Asset map support for the JavaScript broccoli-asset-rev library.
+homepage:            https://github.com/iand675/aeson-asset-map#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Ian Duncan
+maintainer:          ian@iankduncan.com
+copyright:           Ian Duncan
+category:            Web
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Data.AssetMap
+  build-depends:       base >= 4.7 && < 5,
+                       aeson,
+                       bytestring,
+                       template-haskell,
+                       th-lift-instances,
+                       containers,
+                       filepath
+  default-language:    Haskell2010
+
+test-suite aeson-asset-map-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  build-depends:       base
+                     , asset-map
+                     , hspec
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/iand675/aeson-asset-map
diff --git a/src/Data/AssetMap.hs b/src/Data/AssetMap.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/AssetMap.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE DeriveLift          #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module Data.AssetMap where
+import           Control.Exception
+import           Data.Aeson
+import           Data.Aeson.Encoding
+import qualified Data.ByteString.Lazy as BS
+import           Data.Maybe (fromMaybe)
+import qualified Data.Map as M
+import           Data.Monoid ((<>))
+import           Data.Proxy
+import           Instances.TH.Lift
+import           Language.Haskell.TH.Syntax
+import           System.FilePath.Posix
+
+data AssetMap = AssetMap
+  { assets :: M.Map FilePath FilePath
+  , prepend :: Maybe String
+  } deriving (Show, Eq, Lift)
+
+instance ToJSON AssetMap where
+  toJSON a = object
+    [ "assets" .= assets a
+    , "prepend" .= fromMaybe "" (prepend a)
+    ]
+  toEncoding (AssetMap as p) =
+    pairs ("assets" .= as <> "prepend" .= fromMaybe "" p)
+
+instance FromJSON AssetMap where
+  parseJSON = withObject "AssetMap" $ \o ->
+    AssetMap <$> (o .: "assets") <*> (nothingIfEmpty <$> (o .: "prepend"))
+    where
+      nothingIfEmpty [] = Nothing
+      nothingIfEmpty xs = Just xs
+
+denormalizeAssetMap :: AssetMap -> M.Map FilePath FilePath
+denormalizeAssetMap m = case prepend m of
+  Nothing -> assets m
+  Just prefix -> M.map (prefix </>) $ assets m
+
+embedJSONFile :: (FromJSON a, Lift a) => FilePath -> Proxy a -> Q Exp
+embedJSONFile fp p = do
+  addDependentFile fp
+  bs <- runIO $ BS.readFile fp
+  case eitherDecode bs of
+    Left err -> fail err
+    Right x -> lift (x `asProxyTypeOf` p)
+
+embedJSONFileWithFallback :: (FromJSON a, Lift a) => FilePath -> a -> Q Exp
+embedJSONFileWithFallback fp x = do
+  addDependentFile fp
+  ebs <- runIO $ try $ BS.readFile fp
+  res <- case ebs of
+    Left (e :: SomeException) -> return x
+    Right bs -> case eitherDecode bs of
+      Left err -> fail err
+      Right x' -> return x'
+  lift res
+
+-- | Return either an asset map or an empty dictionary if not found
+embedAssetMap :: FilePath -> Q Exp
+embedAssetMap fp = embedJSONFileWithFallback fp $ AssetMap M.empty Nothing
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,4 @@
+module Main where
+
+main :: IO ()
+main = hspec $ return ()
