diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,16 @@
+# Changelog
+
+All notable changes to this project will be documented here.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Haskell Package Versioning Policy](https://pvp.haskell.org/).
+
+## [0.1.0] - 2019-09-29
+### Added
+
+- Initial Hakyll compilers for typescript and javascript.
+  - Supports passing arbitrary arguments to the typescript compiler
+  - Provides various helper compilers that pass commonly used typescript arguments
+  - Supports minification of javascript source as a compiler
+  - Provides helper compilers to compile typescript source to minified javascript.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Jim McStanton (c) 2019
+
+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 Jim McStanton 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,37 @@
+# hakyll-typescript
+
+A simple [`hakyll`](http://hackage.haskell.org/package/hakyll)
+typescript compiler for typescript and javascript resources. Uses
+[`hjsmin`](http://hackage.haskell.org/package/hjsmin) for compression.
+
+Assumes that the typescript compiler is installed and available to the
+user running `hakyll`.
+
+## Example Usage
+
+In your hakyll site simply import the compiler you are interested in using
+and provide it as the compiler for your relevant script files.
+
+```haskell
+import Hakyll.Typescript.TS
+
+main = hakyll $ do
+  -- Matches any file inside a the directory ./scripts
+  match "scripts/**" $ do
+    route   $ setExtension "js"
+    -- compiles all typescript and javascript to the js target
+    -- then compresses the result
+    compile compressJtsCompiler
+```
+
+See the documentation for other available compilers.
+
+## Build the Source
+
+This project uses `stack`. Clone the project, then run `stack build` from the root.
+
+## Build the Docs
+
+For whatever reason `stack haddock` fails to pull down some necessary packages.
+To build the docs run `stack exec haddock --  --html --hoogle --html-location=../$pkg-$version/ --hyperlinked-source --quickjump src/**/*.hs  -o doc`. The index
+will be found in `./doc/`.
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/hakyll-typescript.cabal b/hakyll-typescript.cabal
new file mode 100644
--- /dev/null
+++ b/hakyll-typescript.cabal
@@ -0,0 +1,51 @@
+name:                hakyll-typescript
+version:             0.0.1.0
+synopsis:            Typescript and javascript hakyll compilers.
+description:         Provides typescript to javascript hakyll compilers and a
+                     js minifying hakyll compiler. This is a simple wrapper
+                     around other common utilities found in hakyll projects,
+                     particularly `hjsmin`. 
+homepage:            https://github.com/jhmcstanton/hakyll-typescript#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Jim McStanton
+maintainer:          jim@jhmcstanton.com
+copyright:           2019 Jim McStanton
+category:            web
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+extra-source-files:  CHANGELOG.md
+
+library
+  ghc-options:         -Wall
+  hs-source-dirs:      src
+  exposed-modules:     Hakyll.Typescript.JS
+                     , Hakyll.Typescript.TS
+  other-modules:       Hakyll.Typescript.Internal
+  build-depends:       base          >= 4.7      && < 5
+                     , bytestring    >= 0.10.8.2 && < 0.11
+                     , hakyll        >= 4.12.4.0 && < 4.13
+                     , hjsmin        >= 0.2.0.2  && < 0.3 
+                     , typed-process >= 0.2.3.0  && < 0.3
+  default-language:    Haskell2010
+
+test-suite test-hakyll-typescript
+  ghc-options:         -Wall
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      tests
+  main-is:             TestSuite.hs
+  other-modules:       Hakyll.Typescript.TestResources
+                     , Hakyll.Typescript.JS.Tests
+                     , Hakyll.Typescript.TS.Tests
+  build-depends:       base          >= 4.7      && < 5
+                     , directory     >= 1.3.1.5  && < 1.4
+                     , hakyll        >= 4.12.4.0 && < 4.13
+                     , hakyll-typescript
+                     , tasty         >= 1.1.0.4  && < 1.2
+                     , tasty-hunit   >= 0.10.0.1 && < 0.11
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/jhmcstanton/hakyll-typescript
diff --git a/src/Hakyll/Typescript/Internal.hs b/src/Hakyll/Typescript/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Hakyll/Typescript/Internal.hs
@@ -0,0 +1,18 @@
+-- |Internal helper utilities. Subject to change. Avoid relying on anything
+-- found in here.
+module Hakyll.Typescript.Internal
+  (
+    minifyJs,
+    withMinifyJs
+  )
+  where
+
+import           Data.ByteString.Lazy (ByteString)
+import           Hakyll (withItemBody, Compiler, Item)
+import           Text.Jasmine
+
+minifyJs :: ByteString -> Compiler ByteString
+minifyJs = pure . minify
+
+withMinifyJs :: Compiler (Item ByteString) -> Compiler (Item ByteString)
+withMinifyJs m = m >>= withItemBody minifyJs
diff --git a/src/Hakyll/Typescript/JS.hs b/src/Hakyll/Typescript/JS.hs
new file mode 100644
--- /dev/null
+++ b/src/Hakyll/Typescript/JS.hs
@@ -0,0 +1,17 @@
+-- |Exports 'compressJsCompiler', which simply minifies
+-- javascript files (but not typescript). This is strictly less
+-- general than the functions found in "Hakyll.Typescript.TS", but
+-- is useful when you want to skip over running the typescript compiler.
+module Hakyll.Typescript.JS
+  (
+    compressJsCompiler
+  )
+  where
+
+import           Data.ByteString.Lazy
+import           Hakyll.Typescript.Internal
+import           Hakyll
+
+-- |Minifies the javascript 'Hakyll.Core.Item.Item'.
+compressJsCompiler :: Compiler (Item ByteString)
+compressJsCompiler = withMinifyJs getResourceLBS
diff --git a/src/Hakyll/Typescript/TS.hs b/src/Hakyll/Typescript/TS.hs
new file mode 100644
--- /dev/null
+++ b/src/Hakyll/Typescript/TS.hs
@@ -0,0 +1,96 @@
+-- |Compilers for compiling and compressing typescript resources.
+-- There are various helper compilers for common compilation cases.
+-- In particular, 'compressJtsCompiler' will compile both typescript
+-- and javascript files, then compress them.
+--
+-- There are other variations that may be useful as well. 'compressTsCompiler'
+-- is similar to 'compressJtsCompiler', but is more strict and will not accept
+-- arbitrary javascript. 'compressTsCompilerWith' is the most general combinator,
+-- using this you can pass arbitrary arguments to the typescript compiler prior
+-- to compilation. These results are then minified as well.
+--
+-- Finally, if you just want to compile typescript without minification, perhaps
+-- for readability, use any of the functions that are not prefixed with "compress".
+-- Again, here 'tsCompilerWith' is the most general, allowing additional compiler
+-- arguments to be passed prior to compilation. 'jtsCompiler' is provided for
+-- convenience to support flexibly compiling javascript and typescript without
+-- compression.
+module Hakyll.Typescript.TS
+  (
+    compressJtsCompiler,
+    compressJtsCompilerWith,
+    compressTsCompiler,
+    compressTsCompilerWith,
+    jtsCompiler,
+    jtsCompilerWith,
+    tsCompiler,
+    tsCompilerWith,
+    TSArgs
+  )
+  where
+
+import           Data.ByteString.Lazy.Char8 (ByteString)
+import           GHC.Conc                   (atomically)
+import           Hakyll.Core.Item
+import           Hakyll.Core.Compiler
+import           System.Process.Typed
+
+import           Hakyll.Typescript.Internal
+
+-- |Arguments to pass to the typescript compiler.
+type TSArgs = [String]
+
+-- |Compiles the typescript or javascript 'Hakyll.Core.Item.Item' to javascript.
+compressJtsCompiler :: Compiler (Item ByteString)
+compressJtsCompiler = compressJtsCompilerWith mempty
+
+-- |Compiles the typescript 'Hakyll.Core.Item.Item' to javascript, then
+-- minifies the result.
+compressTsCompiler :: Compiler (Item ByteString)
+compressTsCompiler = compressTsCompilerWith mempty
+
+-- |Compiles the typescript or javascript 'Hakyll.Core.Item.Item' to javascript,
+-- then minifies the result
+compressJtsCompilerWith :: TSArgs -> Compiler (Item ByteString)
+compressJtsCompilerWith args = withMinifyJs $ jtsCompilerWith args
+
+-- |Compiles the typescript 'Hakyll.Core.Item.Item' to javascript, then
+-- minifies the result. Passes all typescript arguments to the typescript
+-- compiler for compilation.
+compressTsCompilerWith :: TSArgs -> Compiler (Item ByteString)
+compressTsCompilerWith args = withMinifyJs $ tsCompilerWith args
+
+-- |Compiles the typescript or javascript 'Hakyll.Core.Item.Item' to javascript.
+jtsCompiler :: Compiler (Item ByteString)
+jtsCompiler = jtsCompilerWith mempty
+
+-- |Compiles the typescript 'Hakyll.Core.Item.Item' to javascript.
+tsCompiler :: Compiler (Item ByteString)
+tsCompiler = tsCompilerWith mempty
+
+-- |Compiles the typescript or javascript 'Hakyll.Core.Item.Item' to javascript.
+-- Passes the provided 'TSArgs' to the typescript compiler.
+jtsCompilerWith :: TSArgs -> Compiler (Item ByteString)
+jtsCompilerWith args = tsCompilerWith $ ["--allowJs", "true"] <> args
+
+-- |Compiles the typescript 'Hakyll.Core.Item.Item' to javascript.
+-- Passes the provided 'TSArgs' to the typescript compiler.
+tsCompilerWith :: TSArgs -> Compiler (Item ByteString)
+tsCompilerWith args = do
+  -- this won't work on Windows, but that's probably fine
+  let defaultArgs = ["--outFile", "/dev/stdout"]
+  tsPath <- getResourceFilePath
+  let fullArgs = defaultArgs <> args <> [tsPath]
+  jsBody <- unsafeCompiler $ tsc fullArgs
+  -- just using this to get at the item
+  oldTsBody <- getResourceString
+  pure $ itemSetBody jsBody oldTsBody
+
+tsc :: TSArgs -> IO ByteString
+tsc args = withProcess procConf waitJsOutput where
+  procConf = setStdout byteStringOutput . proc "tsc" $ args
+  waitJsOutput process = do
+    let stmJs = getStdout process
+    js <- atomically stmJs
+    checkExitCode process
+    pure js
diff --git a/tests/Hakyll/Typescript/JS/Tests.hs b/tests/Hakyll/Typescript/JS/Tests.hs
new file mode 100644
--- /dev/null
+++ b/tests/Hakyll/Typescript/JS/Tests.hs
@@ -0,0 +1,27 @@
+module Hakyll.Typescript.JS.Tests
+  (
+    tests
+  )
+  where
+
+import           Hakyll.Typescript.JS
+import           Hakyll.Typescript.TestResources
+
+import           Test.Tasty
+import           Test.Tasty.HUnit
+
+tests :: TestTree
+tests = testGroup "Hakyll.Typescript.JS.Tests"
+  [
+    fileSizeSmaller,
+    errorsOnBadJs
+  ]
+
+fileSizeSmaller :: TestTree
+fileSizeSmaller = testCase "Valid js output is smaller than input" $
+  runTestCompiler helloWorldJS compressJsCompiler
+                  "Compression failed" (outputSmaller helloWorldJS)
+
+errorsOnBadJs :: TestTree
+errorsOnBadJs = testCase "Raises an exception when Js is invalid" $
+  runExceptionCompiler bad compressJsCompiler
diff --git a/tests/Hakyll/Typescript/TS/Tests.hs b/tests/Hakyll/Typescript/TS/Tests.hs
new file mode 100644
--- /dev/null
+++ b/tests/Hakyll/Typescript/TS/Tests.hs
@@ -0,0 +1,108 @@
+module Hakyll.Typescript.TS.Tests
+  (
+    tests
+  )
+  where
+
+import           Hakyll.Typescript.TS
+import           Hakyll.Typescript.TestResources
+
+import           Test.Tasty
+import           Test.Tasty.HUnit
+
+tests :: TestTree
+tests = testGroup "Hakyll.Typescript.TS.Tests"
+  [
+    compilesTs,
+    errorsOnBadTs,
+    tscErrorsReported,
+    handlesUserArgs,
+    compressedFileSizeSmaller,
+    compressedFileSizeSmallerWithArgs,
+    compressionErrorsReported,
+    jtsCompilerWithCompilesJSWithArgs,
+    jtsCompilerCompilesJs,
+    jtsCompilerCompilesTs,
+    compressJtsCompilerCompressesJs,
+    compressJtsCompilerCompressesTs,
+    compressJtsCompilerWithCompressesWithArgs,
+    compressJtsCompilerWithReportsErrors
+  ]
+
+compilesTs :: TestTree
+compilesTs = testCase "Compiles Valid TS" $ do
+  runTestCompiler helloWorldTS tsCompiler
+                  "tsc compiler error" expectSuccess
+
+errorsOnBadTs :: TestTree
+errorsOnBadTs = testCase "Raises an exception on invalid TS" $ do
+  runTestCompiler bad tsCompiler
+                  "tsc compilation success (but it shouldn't have)"
+                  expectFailure
+
+tscErrorsReported :: TestTree
+tscErrorsReported = testCase "Compilation errors reported" $ do
+  runTestCompiler helloWorldJS tsCompiler
+                  "tsc compilation success (but it shouldn't have)"
+                  expectFailure
+
+handlesUserArgs :: TestTree
+handlesUserArgs = testCase "Successfully passes user arguments to tsc" $ do
+  runTestCompiler helloWorldJS (tsCompilerWith ["--allowJs", "true"])
+                  "tsc compilation failure" expectSuccess
+
+compressedFileSizeSmaller :: TestTree
+compressedFileSizeSmaller = testCase "Valid TS minifies successfully" $ do
+  runTestCompiler helloWorldTS compressTsCompiler
+                  "tsc compilation failure" expectSuccess
+
+compressedFileSizeSmallerWithArgs :: TestTree
+compressedFileSizeSmallerWithArgs = testCase "Valid JS minifies successfully" $ do
+  runTestCompiler helloWorldTS (compressTsCompilerWith ["--allowJs", "true"])
+                  "tsc compilation failure" expectSuccess
+
+compressionErrorsReported :: TestTree
+compressionErrorsReported =
+  testCase "Compilation errors in compress ts compiler are reported" $ do
+    runTestCompiler helloWorldJS compressTsCompiler
+                  "tsc compilation success" expectFailure
+
+jtsCompilerWithCompilesJSWithArgs :: TestTree
+jtsCompilerWithCompilesJSWithArgs =
+  testCase "Compiles js and accepts more args" $ do
+    runTestCompiler helloWorldJS (jtsCompilerWith ["--target", "ES6"])
+                  "tsc compilation failed" expectSuccess
+
+jtsCompilerCompilesJs :: TestTree
+jtsCompilerCompilesJs = testCase "Compiles js successfully" $ do
+    runTestCompiler helloWorldJS jtsCompiler
+                  "tsc compilation failed" expectSuccess
+
+jtsCompilerCompilesTs :: TestTree
+jtsCompilerCompilesTs = testCase "Compiles ts successfully" $ do
+    runTestCompiler helloWorldTS jtsCompiler
+                  "tsc compilation failed" expectSuccess
+
+compressJtsCompilerWithCompressesWithArgs :: TestTree
+compressJtsCompilerWithCompressesWithArgs =
+  testCase "Compresses and uses more args" $ do
+    runTestCompiler helloWorldTS (compressJtsCompilerWith ["--target", "ES5"])
+                  "tsc compilation failed" expectSuccess
+
+compressJtsCompilerCompressesTs :: TestTree
+compressJtsCompilerCompressesTs =
+  testCase "Compresses ts" $ do
+    runTestCompiler helloWorldTS compressJtsCompiler
+                  "tsc compilation failed" expectSuccess
+
+compressJtsCompilerCompressesJs :: TestTree
+compressJtsCompilerCompressesJs =
+  testCase "Compresses js" $ do
+    runTestCompiler helloWorldJS compressJtsCompiler
+                  "tsc compilation failed" expectSuccess
+
+compressJtsCompilerWithReportsErrors :: TestTree
+compressJtsCompilerWithReportsErrors =
+  testCase "jts compiler reports errors" $ do
+    runExceptionCompiler helloWorldTS
+      (compressJtsCompilerWith ["--target", "ES2020"])
diff --git a/tests/Hakyll/Typescript/TestResources.hs b/tests/Hakyll/Typescript/TestResources.hs
new file mode 100644
--- /dev/null
+++ b/tests/Hakyll/Typescript/TestResources.hs
@@ -0,0 +1,101 @@
+{-# LANGUAGE PartialTypeSignatures #-}
+module Hakyll.Typescript.TestResources
+  (
+    bad,
+    expectFailure,
+    expectSuccess,
+    fileSize,
+    helloWorldJS,
+    helloWorldTS,
+    outputSmaller,
+    resourceRoot,
+    runExceptionCompiler,
+    runTestCompiler
+  )
+  where
+
+import           Control.Exception   (handle, SomeException)
+import           Hakyll
+import           Hakyll.Core.Logger  (new, Verbosity(..))
+import           Hakyll.Core.Runtime
+import           System.Directory    (removeDirectoryRecursive)
+import           System.Exit
+import           System.IO
+import           Test.Tasty.HUnit    ((@?), assertFailure)
+
+resourceRoot :: FilePath
+resourceRoot = "tests/resources/"
+
+helloWorldTS :: FilePath
+helloWorldTS = resourceRoot <> "hello_world.ts"
+
+helloWorldJS :: FilePath
+helloWorldJS = resourceRoot <> "hello_world.js"
+
+bad          :: FilePath
+bad          = resourceRoot <> "bad.ts"
+
+testTmpDir :: FilePath
+testTmpDir = "_test/"
+
+testSiteDir :: FilePath
+testSiteDir = testTmpDir <> "site/"
+
+testToSitePath :: FilePath -> FilePath
+testToSitePath p = testSiteDir <> name <> ".js" where
+  name = reverse . drop 3 . reverse $ p
+
+testConfiguration :: Configuration
+testConfiguration = defaultConfiguration
+  {
+    destinationDirectory = testSiteDir
+  , storeDirectory       = testTmpDir <> "store"
+  , tmpDirectory         = testTmpDir <> "tmp"
+  , providerDirectory    = "."
+  }
+
+runTestCompiler :: _
+                => FilePath
+                -> Compiler (Item a)
+                -> String -- assertion failure message
+                -> (ExitCode -> IO Bool) -- assertion. Should not throw!
+                -> IO ()
+runTestCompiler testResource testCompiler msg assertion = do
+  logger        <- new Error
+  (exitCode, _) <- run testConfiguration logger $ do
+    match (fromGlob testResource) $ do
+      route $ setExtension "js"
+      compile testCompiler
+  assertResult  <- assertion exitCode
+  removeDirectoryRecursive testTmpDir
+  assertResult @? msg
+
+runExceptionCompiler :: _ => FilePath -> Compiler (Item a) -> IO ()
+runExceptionCompiler testResource testCompiler = do
+  logger <- new Error
+  result <- handle (\e -> printException e >> pure True) $ do
+    _ <- run testConfiguration logger $ do
+      match (fromGlob testResource) $ do
+        route $ setExtension "js"
+        compile testCompiler
+    pure False
+  removeDirectoryRecursive testTmpDir
+  result @? "Expected exception."
+
+expectSuccess :: ExitCode -> IO Bool
+expectSuccess = pure . (== ExitSuccess)
+
+expectFailure :: ExitCode -> IO Bool
+expectFailure = pure . (== ExitFailure 1)
+
+printException :: SomeException -> IO ()
+printException e = putStrLn $ "Caught exception: " ++ show e
+
+fileSize :: FilePath -> IO Integer
+fileSize p = withFile p ReadMode hFileSize
+
+outputSmaller :: FilePath -> ExitCode -> IO Bool
+outputSmaller testPath exitCode = do
+  origSize <- fileSize testPath
+  siteSize <- fileSize $ testToSitePath testPath
+  pure $ exitCode == ExitSuccess && siteSize < origSize
diff --git a/tests/TestSuite.hs b/tests/TestSuite.hs
new file mode 100644
--- /dev/null
+++ b/tests/TestSuite.hs
@@ -0,0 +1,12 @@
+module Main where
+
+import qualified Hakyll.Typescript.JS.Tests as JS
+import qualified Hakyll.Typescript.TS.Tests as TS
+import           Test.Tasty (defaultMain, testGroup)
+
+main :: IO ()
+main = defaultMain $ testGroup "hakyll-typescript"
+  [
+    JS.tests
+  , TS.tests
+  ]
