mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-12-25 18:13:52 +03:00
Ext code files are now nicely represented and copied correctly and lazily. (#68)
Ext code files are now nicely represented and copied correctly and lazily.
This commit is contained in:
parent
a1213677d0
commit
2e409b2854
BIN
examples/todoMVC/src/wasp-logo.png
Normal file
BIN
examples/todoMVC/src/wasp-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
57
src/ExternalCode.hs
Normal file
57
src/ExternalCode.hs
Normal file
@ -0,0 +1,57 @@
|
||||
module ExternalCode
|
||||
( File
|
||||
, getFilePathInExtCodeDir
|
||||
, getFileText
|
||||
, readFiles
|
||||
) where
|
||||
|
||||
import System.FilePath ((</>))
|
||||
import qualified Data.Text.Lazy as TextL
|
||||
import qualified Data.Text.Lazy.IO as TextL.IO
|
||||
import Data.Text (Text)
|
||||
|
||||
import qualified Util.IO
|
||||
|
||||
|
||||
data File = File
|
||||
{ _pathInExtCodeDir :: !FilePath -- ^ Path relative to external code directory.
|
||||
, _text :: TextL.Text -- ^ File content. It will throw error when evaluated if file is not textual file.
|
||||
}
|
||||
|
||||
instance Show File where
|
||||
show = show . _pathInExtCodeDir
|
||||
|
||||
instance Eq File where
|
||||
f1 == f2 = (_pathInExtCodeDir f1) == (_pathInExtCodeDir f2)
|
||||
|
||||
-- | Returns path relative to the external code directory.
|
||||
getFilePathInExtCodeDir :: File -> FilePath
|
||||
getFilePathInExtCodeDir = _pathInExtCodeDir
|
||||
|
||||
-- | Unsafe method: throws error if text could not be read (if file is not a textual file)!
|
||||
getFileText :: File -> Text
|
||||
getFileText = TextL.toStrict . _text
|
||||
|
||||
|
||||
-- | Returns all files contained in the specified external code dir, recursively.
|
||||
-- File paths are relative to the specified external code dir path.
|
||||
readFiles :: FilePath -> IO [File]
|
||||
readFiles extCodeDirPath = do
|
||||
filePaths <- Util.IO.listDirectoryDeep extCodeDirPath
|
||||
-- NOTE: We read text from all the files, regardless if they are text files or not, because
|
||||
-- we don't know if they are a text file or not.
|
||||
-- Since we do lazy reading (Text.Lazy), this is not a problem as long as we don't try to use
|
||||
-- text of a file that is actually not a text file -> then we will get an error when Haskell
|
||||
-- actually tries to read that file.
|
||||
-- TODO: We are doing lazy IO here, and there is an idea of it being a thing to avoid, due to no
|
||||
-- control over when resources are released and similar.
|
||||
-- If we do figure out that this is causing us problems, we could do the following refactoring:
|
||||
-- Don't read files at this point, just list them, and Wasp will contain just list of filepaths.
|
||||
-- Modify TextFileDraft so that it also takes text transformation function (Text -> Text),
|
||||
-- or create new file draft that will support that.
|
||||
-- In generator, when creating TextFileDraft, give it function/logic for text transformation,
|
||||
-- and it will be taken care of when draft will be written to the disk.
|
||||
fileTexts <- mapM (TextL.IO.readFile . (extCodeDirPath </>)) filePaths
|
||||
let files = map (\(path, text) -> File path text) (zip filePaths fileTexts)
|
||||
return files
|
||||
|
@ -85,7 +85,7 @@ generateEntityComponents wasp entity = concat
|
||||
-- {=/ typedFields =}
|
||||
generateEntityCreateForm :: Wasp -> EntityForm -> FileDraft
|
||||
generateEntityCreateForm wasp entityForm =
|
||||
createTemplateFileDraft dstPath templateSrcPath templateData
|
||||
createTemplateFileDraft dstPath templateSrcPath (Just templateData)
|
||||
where
|
||||
-- NOTE(matija): There should always be an entity in wasp for the given entity form,
|
||||
-- we want an error to be thrown otherwise.
|
||||
@ -119,7 +119,7 @@ generateEntityList wasp entity
|
||||
-- | Helper function that captures common logic for generating entity file draft.
|
||||
createSimpleEntityFileDraft :: Wasp -> Entity -> FilePath -> FilePath -> FileDraft
|
||||
createSimpleEntityFileDraft wasp entity dstPathInSrc srcPathInEntityTemplatesDir
|
||||
= createTemplateFileDraft dstPath srcPath templateData
|
||||
= createTemplateFileDraft dstPath srcPath (Just templateData)
|
||||
where
|
||||
srcPath = entityTemplatesDirPath </> srcPathInEntityTemplatesDir
|
||||
dstPath = Common.srcDirPath </> dstPathInSrc
|
||||
|
43
src/Generator/ExternalCode.hs
Normal file
43
src/Generator/ExternalCode.hs
Normal file
@ -0,0 +1,43 @@
|
||||
module Generator.ExternalCode
|
||||
( generateExternalCodeDir
|
||||
, externalCodeDirPathInSrc
|
||||
) where
|
||||
|
||||
import System.FilePath ((</>), takeExtension)
|
||||
|
||||
import CompileOptions (CompileOptions)
|
||||
import qualified CompileOptions
|
||||
import Wasp (Wasp)
|
||||
import qualified Wasp
|
||||
import qualified Generator.FileDraft as FD
|
||||
import qualified Generator.Common as Common
|
||||
import qualified ExternalCode
|
||||
|
||||
|
||||
externalCodeDirPathInSrc :: FilePath
|
||||
externalCodeDirPathInSrc = "ext-src"
|
||||
|
||||
generateExternalCodeDir :: CompileOptions -> Wasp -> [FD.FileDraft]
|
||||
generateExternalCodeDir compileOptions wasp =
|
||||
map (generateFile compileOptions) (Wasp.getExternalCodeFiles wasp)
|
||||
|
||||
getFileDstPath :: ExternalCode.File -> FilePath
|
||||
getFileDstPath file = Common.srcDirPath </> externalCodeDirPathInSrc </> (ExternalCode.getFilePathInExtCodeDir file)
|
||||
|
||||
getFileSrcPath :: CompileOptions -> ExternalCode.File -> FilePath
|
||||
getFileSrcPath compileOptions file =
|
||||
(CompileOptions.externalCodeDirPath compileOptions) </> (ExternalCode.getFilePathInExtCodeDir file)
|
||||
|
||||
generateFile :: CompileOptions -> ExternalCode.File -> FD.FileDraft
|
||||
generateFile compileOptions file
|
||||
| extension `elem` ["js", "jsx"] = generateJsFile file
|
||||
| otherwise = FD.createCopyFileDraft (getFileDstPath file) (getFileSrcPath compileOptions file)
|
||||
where
|
||||
extension = takeExtension (getFileSrcPath compileOptions file)
|
||||
|
||||
-- TODO: Now here we do preprocessing!
|
||||
generateJsFile :: ExternalCode.File -> FD.FileDraft
|
||||
generateJsFile file = FD.createTextFileDraft (getFileDstPath file) (ExternalCode.getFileText file)
|
||||
|
||||
|
||||
|
@ -1,28 +0,0 @@
|
||||
module Generator.ExternalCodeDirGenerator
|
||||
( generateExternalCodeDir
|
||||
, externalCodeDirPathInSrc
|
||||
) where
|
||||
|
||||
import System.FilePath ((</>))
|
||||
import Data.Text (Text)
|
||||
|
||||
import CompileOptions (CompileOptions)
|
||||
import Wasp (Wasp)
|
||||
import qualified Wasp
|
||||
import qualified Generator.FileDraft as FD
|
||||
import qualified Generator.Common as Common
|
||||
|
||||
|
||||
externalCodeDirPathInSrc :: FilePath
|
||||
externalCodeDirPathInSrc = "ext-src"
|
||||
|
||||
generateExternalCodeDir :: CompileOptions -> Wasp -> [FD.FileDraft]
|
||||
generateExternalCodeDir _ wasp = map generateExternalCodeFile (Wasp.getExternalCodeFiles wasp)
|
||||
|
||||
generateExternalCodeFile :: (FilePath, Text) -> FD.FileDraft
|
||||
generateExternalCodeFile (pathInExtCodeDir, content) = FD.createTextFileDraft dstPath content
|
||||
where
|
||||
dstPath = Common.srcDirPath </> externalCodeDirPathInSrc </> pathInExtCodeDir
|
||||
|
||||
|
||||
|
@ -36,7 +36,7 @@ instance Writeable FileDraft where
|
||||
write dstDir (FileDraftTextFd draft) = write dstDir draft
|
||||
|
||||
|
||||
createTemplateFileDraft :: FilePath -> FilePath -> Aeson.Value -> FileDraft
|
||||
createTemplateFileDraft :: FilePath -> FilePath -> Maybe Aeson.Value -> FileDraft
|
||||
createTemplateFileDraft dstPath templateRelPath templateData =
|
||||
FileDraftTemplateFd $ TemplateFD.TemplateFileDraft dstPath templateRelPath templateData
|
||||
|
||||
|
@ -11,15 +11,13 @@ import Generator.FileDraft.WriteableMonad
|
||||
data CopyFileDraft = CopyFileDraft
|
||||
{ -- | Path of file to be written, relative to some root dir.
|
||||
copyFileDraftDstFilepath :: !FilePath
|
||||
-- | Path of source file, relative to some root dir,
|
||||
-- normally not the same one as root dir for dstFilepath.
|
||||
-- | Absolute path of source file.
|
||||
, copyFileDraftSrcFilepath :: !FilePath
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
instance Writeable CopyFileDraft where
|
||||
write dstDir (CopyFileDraft dstFilepath srcFilepath) = do
|
||||
let dstAbsFilepath = dstDir </> dstFilepath
|
||||
srcAbsFilepath <- getTemplateFileAbsPath srcFilepath
|
||||
createDirectoryIfMissing True (takeDirectory dstAbsFilepath)
|
||||
copyFile srcAbsFilepath dstAbsFilepath
|
||||
write dstDir (CopyFileDraft relDstPath absSrcPath) = do
|
||||
let absDstPath = dstDir </> relDstPath
|
||||
createDirectoryIfMissing True (takeDirectory absDstPath)
|
||||
copyFile absSrcPath absDstPath
|
||||
|
@ -3,7 +3,6 @@ module Generator.FileDraft.TemplateFileDraft
|
||||
) where
|
||||
|
||||
import System.FilePath (FilePath, (</>), takeDirectory)
|
||||
import Data.Text (Text)
|
||||
import qualified Data.Aeson as Aeson
|
||||
|
||||
import Generator.FileDraft.Writeable
|
||||
@ -17,22 +16,23 @@ data TemplateFileDraft = TemplateFileDraft
|
||||
-- | Path of template source file, relative to templates root dir.
|
||||
, templateFileDraftTemplateRelFilepath :: !FilePath
|
||||
-- | Data to be fed to the template while rendering it.
|
||||
, templateFileDraftTemplateData :: !Aeson.Value
|
||||
, templateFileDraftTemplateData :: Maybe Aeson.Value
|
||||
}
|
||||
deriving (Show, Eq)
|
||||
|
||||
instance Writeable TemplateFileDraft where
|
||||
write dstDir draft =
|
||||
compileAndRenderTemplate templateRelFilepath templateData >>= writeContentToFile
|
||||
write dstDir draft = do
|
||||
createDirectoryIfMissing True (takeDirectory absDstPath)
|
||||
case templateFileDraftTemplateData draft of
|
||||
Nothing -> do
|
||||
absSrcPath <- getTemplateFileAbsPath templateSrcPathInTemplateDir
|
||||
copyFile absSrcPath absDstPath
|
||||
Just tmplData -> do
|
||||
content <- compileAndRenderTemplate templateSrcPathInTemplateDir tmplData
|
||||
writeFileFromText absDstPath content
|
||||
where
|
||||
templateRelFilepath :: FilePath
|
||||
templateRelFilepath = templateFileDraftTemplateRelFilepath draft
|
||||
templateSrcPathInTemplateDir :: FilePath
|
||||
templateSrcPathInTemplateDir = templateFileDraftTemplateRelFilepath draft
|
||||
|
||||
templateData :: Aeson.Value
|
||||
templateData = templateFileDraftTemplateData draft
|
||||
|
||||
writeContentToFile :: (WriteableMonad m) => Text -> m ()
|
||||
writeContentToFile content = do
|
||||
let absDstFilepath = dstDir </> (templateFileDraftDstFilepath draft)
|
||||
createDirectoryIfMissing True (takeDirectory absDstFilepath)
|
||||
writeFileFromText absDstFilepath content
|
||||
absDstPath :: FilePath
|
||||
absDstPath = dstDir </> (templateFileDraftDstFilepath draft)
|
||||
|
@ -11,7 +11,7 @@ import Wasp
|
||||
import Generator.FileDraft
|
||||
import qualified Generator.EntityGenerator as EntityGenerator
|
||||
import qualified Generator.PageGenerator as PageGenerator
|
||||
import qualified Generator.ExternalCodeDirGenerator as ExternalCodeDirGenerator
|
||||
import qualified Generator.ExternalCode as ExternalCodeGenerator
|
||||
import qualified Generator.Common as Common
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ generateWebApp wasp options = concatMap ($ wasp)
|
||||
, generateGitignore
|
||||
, generatePublicDir
|
||||
, generateSrcDir
|
||||
, ExternalCodeDirGenerator.generateExternalCodeDir options
|
||||
, ExternalCodeGenerator.generateExternalCodeDir options
|
||||
]
|
||||
|
||||
generateReadme :: Wasp -> [FileDraft]
|
||||
@ -32,11 +32,11 @@ generatePackageJson :: Wasp -> [FileDraft]
|
||||
generatePackageJson wasp = [simpleTemplateFileDraft "package.json" wasp]
|
||||
|
||||
generateGitignore :: Wasp -> [FileDraft]
|
||||
generateGitignore wasp = [createTemplateFileDraft ".gitignore" "gitignore" (toJSON wasp)]
|
||||
generateGitignore wasp = [createTemplateFileDraft ".gitignore" "gitignore" (Just $ toJSON wasp)]
|
||||
|
||||
generatePublicDir :: Wasp -> [FileDraft]
|
||||
generatePublicDir wasp
|
||||
= createCopyFileDraft ("public" </> "favicon.ico") ("public" </> "favicon.ico")
|
||||
= createTemplateFileDraft ("public" </> "favicon.ico") ("public" </> "favicon.ico") Nothing
|
||||
: map (\path -> simpleTemplateFileDraft ("public/" </> path) wasp)
|
||||
[ "index.html"
|
||||
, "manifest.json"
|
||||
@ -46,7 +46,7 @@ generatePublicDir wasp
|
||||
|
||||
generateSrcDir :: Wasp -> [FileDraft]
|
||||
generateSrcDir wasp
|
||||
= (createCopyFileDraft (Common.srcDirPath </> "logo.png") ("src" </> "logo.png"))
|
||||
= (createTemplateFileDraft (Common.srcDirPath </> "logo.png") ("src" </> "logo.png") Nothing)
|
||||
: map (\path -> simpleTemplateFileDraft ("src/" </> path) wasp)
|
||||
[ "index.js"
|
||||
, "index.css"
|
||||
@ -60,7 +60,7 @@ generateSrcDir wasp
|
||||
++ [generateReducersJs wasp]
|
||||
|
||||
generateReducersJs :: Wasp -> FileDraft
|
||||
generateReducersJs wasp = createTemplateFileDraft dstPath srcPath templateData
|
||||
generateReducersJs wasp = createTemplateFileDraft dstPath srcPath (Just templateData)
|
||||
where
|
||||
srcPath = "src" </> "reducers.js"
|
||||
dstPath = Common.srcDirPath </> "reducers.js"
|
||||
@ -80,4 +80,4 @@ generateReducersJs wasp = createTemplateFileDraft dstPath srcPath templateData
|
||||
-- | Creates template file draft that uses given path as both src and dst path
|
||||
-- and wasp as template data.
|
||||
simpleTemplateFileDraft :: FilePath -> Wasp -> FileDraft
|
||||
simpleTemplateFileDraft path wasp = createTemplateFileDraft path path (toJSON wasp)
|
||||
simpleTemplateFileDraft path wasp = createTemplateFileDraft path path (Just $ toJSON wasp)
|
||||
|
@ -16,7 +16,7 @@ import qualified Util
|
||||
import Wasp
|
||||
import Generator.FileDraft
|
||||
import qualified Generator.EntityGenerator as EntityGenerator
|
||||
import Generator.ExternalCodeDirGenerator (externalCodeDirPathInSrc)
|
||||
import Generator.ExternalCode (externalCodeDirPathInSrc)
|
||||
import qualified Generator.Common as Common
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ generatePage wasp page =
|
||||
++ generatePageStyle wasp page
|
||||
|
||||
generatePageComponent :: Wasp -> Page -> FileDraft
|
||||
generatePageComponent wasp page = createTemplateFileDraft dstPath srcPath templateData
|
||||
generatePageComponent wasp page = createTemplateFileDraft dstPath srcPath (Just templateData)
|
||||
where
|
||||
srcPath = "src" </> "_Page.js"
|
||||
dstPath = FilePath.normalise $ Common.srcDirPath </> pageDirPathInSrc </> (pageName page) <.> "js"
|
||||
|
16
src/Lib.hs
16
src/Lib.hs
@ -2,13 +2,9 @@ module Lib
|
||||
( compile
|
||||
) where
|
||||
|
||||
import qualified Data.Text.IO as TextIO
|
||||
import Data.Text (Text)
|
||||
import System.FilePath ((</>))
|
||||
|
||||
import qualified Util.IO
|
||||
import CompileOptions (CompileOptions)
|
||||
import qualified CompileOptions
|
||||
import qualified ExternalCode
|
||||
import Parser
|
||||
import Generator
|
||||
import Wasp (setExternalCodeFiles)
|
||||
@ -23,15 +19,7 @@ compile waspFile outDir options = do
|
||||
case parseWasp waspStr of
|
||||
Left err -> return $ Left (show err)
|
||||
Right wasp -> do
|
||||
externalCodeFiles <- readExternalCodeFiles $ CompileOptions.externalCodeDirPath options
|
||||
externalCodeFiles <- ExternalCode.readFiles (CompileOptions.externalCodeDirPath options)
|
||||
generateCode $ wasp `setExternalCodeFiles` externalCodeFiles
|
||||
where
|
||||
generateCode wasp = writeWebAppCode wasp outDir options >> return (Right ())
|
||||
|
||||
-- | Returns paths and contents of external code files.
|
||||
-- Paths are relative to the given external code dir path.
|
||||
readExternalCodeFiles :: FilePath -> IO [(FilePath, Text)]
|
||||
readExternalCodeFiles externalCodeDirPath = do
|
||||
externalCodeFilePaths <- Util.IO.listDirectoryDeep externalCodeDirPath
|
||||
externalCodeFileContents <- mapM (TextIO.readFile . (externalCodeDirPath </>)) externalCodeFilePaths
|
||||
return $ zip externalCodeFilePaths externalCodeFileContents
|
||||
|
11
src/Wasp.hs
11
src/Wasp.hs
@ -28,9 +28,9 @@ module Wasp
|
||||
, getExternalCodeFiles
|
||||
) where
|
||||
|
||||
import Data.Text (Text)
|
||||
import Data.Aeson ((.=), object, ToJSON(..))
|
||||
|
||||
import qualified ExternalCode
|
||||
import Wasp.App
|
||||
import Wasp.Entity
|
||||
import Wasp.EntityForm
|
||||
@ -44,10 +44,7 @@ import qualified Util as U
|
||||
data Wasp = Wasp
|
||||
{ waspElements :: [WaspElement]
|
||||
, waspJsImports :: [JsImport]
|
||||
, externalCodeFiles ::
|
||||
[( FilePath -- ^ Path relative to external code directory.
|
||||
, Text -- ^ Text of that file.
|
||||
)]
|
||||
, externalCodeFiles :: [ExternalCode.File]
|
||||
} deriving (Show, Eq)
|
||||
|
||||
data WaspElement
|
||||
@ -66,10 +63,10 @@ fromWaspElems elems = Wasp
|
||||
|
||||
-- * External code files
|
||||
|
||||
getExternalCodeFiles :: Wasp -> [(FilePath, Text)]
|
||||
getExternalCodeFiles :: Wasp -> [ExternalCode.File]
|
||||
getExternalCodeFiles = externalCodeFiles
|
||||
|
||||
setExternalCodeFiles :: Wasp -> [(FilePath, Text)] -> Wasp
|
||||
setExternalCodeFiles :: Wasp -> [ExternalCode.File] -> Wasp
|
||||
setExternalCodeFiles wasp files = wasp { externalCodeFiles = files }
|
||||
|
||||
-- * Js imports
|
||||
|
@ -22,6 +22,5 @@ spec_CopyFileDraft = do
|
||||
where
|
||||
(dstDir, dstPath, srcPath) = ("a/b", "c/d/dst.txt", "e/src.txt")
|
||||
fileDraft = createCopyFileDraft dstPath srcPath
|
||||
expectedSrcPath = mockTemplatesDirAbsPath </> srcPath
|
||||
expectedSrcPath = srcPath
|
||||
expectedDstPath = dstDir </> dstPath
|
||||
mockTemplatesDirAbsPath = Mock.getTemplatesDirAbsPath_impl Mock.defaultMockConfig
|
||||
|
@ -26,7 +26,7 @@ spec_TemplateFileDraft = do
|
||||
where
|
||||
(dstDir, dstPath, templatePath) = ("a/b", "c/d/dst.txt", "e/tmpl.txt")
|
||||
templateData = object [ "foo" .= ("bar" :: String) ]
|
||||
fileDraft = createTemplateFileDraft dstPath templatePath templateData
|
||||
fileDraft = createTemplateFileDraft dstPath templatePath (Just templateData)
|
||||
expectedDstPath = dstDir </> dstPath
|
||||
mockTemplatesDirAbsPath = "mock/templates/dir"
|
||||
mockTemplateContent = "Mock template content" :: Text
|
||||
|
Loading…
Reference in New Issue
Block a user