{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
import Control.Monad (forM_)
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import Data.FileEmbed (bsToExp, embedFile, getDir)
import Language.Haskell.TH (listE, runIO, tupE)
import Language.Haskell.TH.Syntax (addDependentFile)
import Options.Applicative
import Path
import Path.IO (doesFileExist, ensureDir, resolveFile', withSystemTempDir)
import System.Process.Typed (proc, runProcess_)
data CliOptions = CliOptions
{ cliNode :: Maybe FilePath
, cliConfig :: FilePath
}
parseCliOptions :: Parser CliOptions
parseCliOptions = CliOptions
<$> parseCliNode
<*> parseCliConfig
where
parseCliNode = optional $ strOption $ mconcat
[ long "node"
, metavar "NODE"
, help "Path to Node.JS executable (default to finding on PATH)"
]
parseCliConfig = strOption $ mconcat
[ long "config"
, short 'c'
, metavar "CONFIG"
, help "Path to codegen.yml file (defaults to ./codegen.yml)"
, value "./codegen.yml"
]
graphqlCodegenScript :: ByteString
graphqlCodegenScript = $(do
let script = [relfile|js/graphql-codegen-haskell.js|]
fallbackScript = [relfile|js/graphql-codegen-haskell-fallback.js|]
scriptExists <- runIO $ doesFileExist script
embedFile $ toFilePath $ if scriptExists then script else fallbackScript
)
mockedLibraries :: [(Path Rel File, ByteString)]
mockedLibraries = $(do
let dir = "js/mocks/"
files <- runIO $ getDir dir
listE $ flip map files $ \(fp, content) -> do
addDependentFile $ dir ++ fp
tupE [mkRelFile fp, bsToExp content]
)
main :: IO ()
main = do
CliOptions{..} <- execParser $ info (parseCliOptions <**> helper) $ mconcat
[ fullDesc
, progDesc "Generate Haskell definitions from .graphql files"
]
withSystemTempDir "graphql-codegen" $ \tmpDir -> do
let graphqlCodegen = tmpDir </> [relfile|graphql-codegen-haskell.js|]
writeFile' graphqlCodegen graphqlCodegenScript
let nodeModulesDir = tmpDir </> [reldir|node_modules|]
ensureDir nodeModulesDir
forM_ mockedLibraries $ \(fp, content) ->
writeFile' (nodeModulesDir </> fp) content
nodeExe <- maybe (pure "node") (fmap toFilePath . resolveFile') cliNode
configFile <- resolveFile' cliConfig
runProcess_ $ proc nodeExe [toFilePath graphqlCodegen, toFilePath configFile]
where
writeFile' = ByteString.writeFile . toFilePath