Skip to content

Commit c51e308

Browse files
committed
Add local template option
1 parent 62fe117 commit c51e308

File tree

8 files changed

+103
-6
lines changed

8 files changed

+103
-6
lines changed

waspc/cli/exe/Main.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Wasp.Cli.Command.BuildStart (buildStart)
1717
import qualified Wasp.Cli.Command.Call as Command.Call
1818
import Wasp.Cli.Command.Clean (clean)
1919
import Wasp.Cli.Command.Compile (compile)
20-
import Wasp.Cli.Command.CreateNewProject (createNewProject)
20+
import Wasp.Cli.Command.CreateNewProject (createNewCustomProject, createNewProject)
2121
import qualified Wasp.Cli.Command.CreateNewProject.AI as Command.CreateNewProject.AI
2222
import Wasp.Cli.Command.CreateNewProject.AvailableTemplates (availableStarterTemplates)
2323
import Wasp.Cli.Command.Db (runCommandThatRequiresDbRunning)
@@ -51,6 +51,7 @@ main = withUtf8 . (`E.catch` handleInternalErrors) $ do
5151
let commandCall = case args of
5252
("new" : newArgs) -> Command.Call.New newArgs
5353
("new:ai" : newAiArgs) -> Command.Call.NewAi newAiArgs
54+
("new:custom" : newCustomArgs) -> Command.Call.NewCustom newCustomArgs
5455
["start"] -> Command.Call.Start
5556
("start" : "db" : startDbArgs) -> Command.Call.StartDb startDbArgs
5657
["clean"] -> Command.Call.Clean
@@ -89,6 +90,7 @@ main = withUtf8 . (`E.catch` handleInternalErrors) $ do
8990

9091
case commandCall of
9192
Command.Call.New newArgs -> runCommand $ createNewProject newArgs
93+
Command.Call.NewCustom newCustomArgs -> runCommand $ createNewCustomProject newCustomArgs
9294
Command.Call.NewAi newAiArgs -> case newAiArgs of
9395
["--stdout", projectName, appDescription, projectConfigJson] ->
9496
runCommand $

waspc/cli/src/Wasp/Cli/Command/Call.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module Wasp.Cli.Command.Call where
33
data Call
44
= New Arguments
55
| NewAi Arguments
6+
| NewCustom Arguments
67
| Start
78
| StartDb Arguments
89
| Clean

waspc/cli/src/Wasp/Cli/Command/CreateNewProject.hs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Wasp.Cli.Command.CreateNewProject
22
( createNewProject,
3+
createNewCustomProject,
34
)
45
where
56

@@ -9,11 +10,12 @@ import qualified StrongPath as SP
910
import Wasp.Cli.Command (Command)
1011
import Wasp.Cli.Command.Call (Arguments)
1112
import qualified Wasp.Cli.Command.CreateNewProject.AI as AI
12-
import Wasp.Cli.Command.CreateNewProject.ArgumentsParser (newProjectArgsParser)
13+
import Wasp.Cli.Command.CreateNewProject.ArgumentsParser (newCustomProjectArgsParser, newProjectArgsParser)
1314
import Wasp.Cli.Command.CreateNewProject.AvailableTemplates (availableStarterTemplates)
1415
import qualified Wasp.Cli.Command.CreateNewProject.Common as Common
1516
import Wasp.Cli.Command.CreateNewProject.ProjectDescription
1617
( NewProjectDescription (..),
18+
obtainNewCustomProjectDescription,
1719
obtainNewProjectDescription,
1820
)
1921
import Wasp.Cli.Command.CreateNewProject.StarterTemplates
@@ -22,6 +24,7 @@ import Wasp.Cli.Command.CreateNewProject.StarterTemplates
2224
)
2325
import Wasp.Cli.Command.CreateNewProject.StarterTemplates.Bundled (createProjectOnDiskFromBundledTemplate)
2426
import Wasp.Cli.Command.CreateNewProject.StarterTemplates.GhReleaseArchive (createProjectOnDiskFromGhReleaseArchiveTemplate)
27+
import Wasp.Cli.Command.CreateNewProject.StarterTemplates.Local (createProjectOnDiskFromLocalTemplate)
2528
import Wasp.Cli.Command.Message (cliSendMessageC)
2629
import Wasp.Cli.Util.Parser (parseArguments)
2730
import qualified Wasp.Message as Msg
@@ -39,6 +42,19 @@ createNewProject args = do
3942
createProjectOnDisk newProjectDescription
4043
liftIO $ printGettingStartedInstructionsForProject newProjectDescription
4144

45+
-- | It receives all of the arguments that were passed to the `wasp new:custom` command.
46+
createNewCustomProject :: Arguments -> Command ()
47+
createNewCustomProject args = do
48+
newCustomProjectArgs <-
49+
parseArguments "wasp new:custom" newCustomProjectArgsParser args
50+
& either Common.throwProjectCreationError return
51+
52+
newCustomProjectDescription <-
53+
obtainNewCustomProjectDescription newCustomProjectArgs
54+
55+
createProjectOnDisk newCustomProjectDescription
56+
liftIO $ printGettingStartedInstructionsForProject newCustomProjectDescription
57+
4258
createProjectOnDisk :: NewProjectDescription -> Command ()
4359
createProjectOnDisk
4460
NewProjectDescription
@@ -53,6 +69,8 @@ createProjectOnDisk
5369
createProjectOnDiskFromGhReleaseArchiveTemplate absWaspProjectDir projectName appName ghRepoRef archiveName' archivePath'
5470
BundledStarterTemplate {bundledPath = bundledPath'} ->
5571
liftIO $ createProjectOnDiskFromBundledTemplate absWaspProjectDir projectName appName bundledPath'
72+
LocalStarterTemplate {localPath = localPath'} ->
73+
liftIO $ createProjectOnDiskFromLocalTemplate absWaspProjectDir projectName appName localPath'
5674
AiGeneratedStarterTemplate ->
5775
AI.createNewProjectInteractiveOnDisk absWaspProjectDir appName
5876

waspc/cli/src/Wasp/Cli/Command/CreateNewProject/ArgumentsParser.hs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
module Wasp.Cli.Command.CreateNewProject.ArgumentsParser
22
( NewProjectArgs (..),
33
newProjectArgsParser,
4+
NewCustomProjectArgs (..),
5+
newCustomProjectArgsParser,
46
)
57
where
68

79
import qualified Options.Applicative as Opt
10+
import Wasp.Cli.Util.PathArgument (DirPathArgument, dirPathReader)
811

912
data NewProjectArgs = NewProjectArgs
1013
{ _projectName :: Maybe String,
@@ -27,3 +30,25 @@ newProjectArgsParser =
2730
<> Opt.short 't'
2831
<> Opt.metavar "TEMPLATE_NAME"
2932
<> Opt.help "Template to use for the new project"
33+
34+
data NewCustomProjectArgs = NewCustomProjectArgs
35+
{ _customProjectName :: Maybe String,
36+
_customTemplatePath :: DirPathArgument
37+
}
38+
39+
newCustomProjectArgsParser :: Opt.Parser NewCustomProjectArgs
40+
newCustomProjectArgsParser =
41+
NewCustomProjectArgs
42+
<$> Opt.optional projectNameParser
43+
<*> templateNameParser
44+
where
45+
projectNameParser :: Opt.Parser String
46+
projectNameParser = Opt.strArgument $ Opt.metavar "PROJECT_NAME"
47+
48+
templateNameParser :: Opt.Parser DirPathArgument
49+
templateNameParser =
50+
Opt.option dirPathReader $
51+
Opt.long "path"
52+
<> Opt.short 'p'
53+
<> Opt.metavar "TEMPLATE_PATH"
54+
<> Opt.help "Path to the local directory to use as a starter template"

waspc/cli/src/Wasp/Cli/Command/CreateNewProject/ProjectDescription.hs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module Wasp.Cli.Command.CreateNewProject.ProjectDescription
22
( obtainNewProjectDescription,
3+
obtainNewCustomProjectDescription,
34
NewProjectDescription (..),
45
NewProjectName (..),
56
NewProjectAppName (..),
@@ -8,26 +9,31 @@ module Wasp.Cli.Command.CreateNewProject.ProjectDescription
89
)
910
where
1011

12+
import Control.Monad (unless)
1113
import Control.Monad.IO.Class (liftIO)
1214
import Data.List (intercalate)
1315
import Data.List.NonEmpty (fromList)
1416
import Data.Maybe (isNothing)
1517
import Path.IO (doesDirExist)
16-
import StrongPath (Abs, Dir, Path')
18+
import StrongPath (Abs, Dir, Path', fromAbsDir)
1719
import StrongPath.Path (toPathAbsDir)
20+
import System.Directory (doesDirectoryExist)
1821
import Wasp.Analyzer.Parser (isValidWaspIdentifier)
1922
import Wasp.Cli.Command (Command)
2023
import Wasp.Cli.Command.CreateNewProject.ArgumentsParser
21-
( NewProjectArgs (..),
24+
( NewCustomProjectArgs (..),
25+
NewProjectArgs (..),
2226
)
2327
import Wasp.Cli.Command.CreateNewProject.AvailableTemplates (defaultStarterTemplate)
2428
import Wasp.Cli.Command.CreateNewProject.Common (throwProjectCreationError)
2529
import Wasp.Cli.Command.CreateNewProject.StarterTemplates
26-
( StarterTemplate,
30+
( StarterTemplate (LocalStarterTemplate, localPath),
2731
findTemplateByString,
2832
)
2933
import Wasp.Cli.FileSystem (getAbsPathToDirInCwd)
3034
import qualified Wasp.Cli.Interactive as Interactive
35+
import Wasp.Cli.Util.PathArgument (DirPathArgument)
36+
import qualified Wasp.Cli.Util.PathArgument as PathArgument
3137
import Wasp.Project.Common (WaspProjectDir)
3238
import Wasp.Util (indent, kebabToCamelCase, whenM)
3339

@@ -81,6 +87,16 @@ obtainNewProjectDescription NewProjectArgs {_projectName = projectNameArg, _temp
8187
absWaspProjectDir <- obtainAvailableProjectDirPath projectName
8288
return $ mkNewProjectDescription projectName appName absWaspProjectDir template
8389

90+
obtainNewCustomProjectDescription :: NewCustomProjectArgs -> Command NewProjectDescription
91+
obtainNewCustomProjectDescription NewCustomProjectArgs {_customProjectName = projectNameArg, _customTemplatePath = templatePathArg} = do
92+
projectName <- maybe askForName return projectNameArg
93+
appName <-
94+
either throwProjectCreationError pure $
95+
parseWaspProjectNameIntoAppName projectName
96+
template <- findCustomTemplateOrThrow templatePathArg
97+
absWaspProjectDir <- obtainAvailableProjectDirPath projectName
98+
return $ mkNewProjectDescription projectName appName absWaspProjectDir template
99+
84100
askForName :: Command String
85101
askForName =
86102
liftIO $ Interactive.askForRequiredInput "Enter the project name (e.g. my-project)"
@@ -114,6 +130,18 @@ findTemplateOrThrow availableTemplates templateName = case findTemplateByString
114130
<> intercalate ", " (map show availableTemplates)
115131
<> "."
116132

133+
findCustomTemplateOrThrow :: DirPathArgument -> Command StarterTemplate
134+
findCustomTemplateOrThrow templatePathArg = do
135+
absTemplatePath <- liftIO $ PathArgument.getDirPath templatePathArg
136+
templateExists <- liftIO $ doesDirectoryExist $ fromAbsDir absTemplatePath
137+
138+
unless templateExists $ do
139+
throwProjectCreationError $
140+
"The provided template path does not exist or is not a directory: "
141+
++ show templatePathArg
142+
143+
return $ LocalStarterTemplate {localPath = absTemplatePath}
144+
117145
obtainAvailableProjectDirPath :: String -> Command (Path' Abs (Dir WaspProjectDir))
118146
obtainAvailableProjectDirPath projectName = do
119147
absWaspProjectDir <- getAbsPathToNewProjectDirInCwd projectName

waspc/cli/src/Wasp/Cli/Command/CreateNewProject/StarterTemplates.hs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ where
1111

1212
import Data.Foldable (find)
1313
import Data.Text (Text)
14-
import StrongPath (Dir', File', Path, Path', Rel, Rel', System, reldir, (</>))
14+
import StrongPath (Abs, Dir', File', Path, Path', Rel, Rel', System, reldir, (</>))
1515
import qualified StrongPath as SP
1616
import qualified Wasp.Cli.GithubRepo as GhRepo
1717
import qualified Wasp.Cli.Interactive as Interactive
@@ -36,6 +36,8 @@ data StarterTemplate
3636
{ bundledPath :: Path' Rel' Dir',
3737
metadata :: !TemplateMetadata
3838
}
39+
| -- | Template from disk, that the user has locally extracted.
40+
LocalStarterTemplate {localPath :: !(Path' Abs Dir')}
3941
| -- | Template that will be dynamically generated by Wasp AI based on user's input.
4042
AiGeneratedStarterTemplate
4143

@@ -49,13 +51,15 @@ data TemplateMetadata = TemplateMetadata
4951
instance Show StarterTemplate where
5052
show (GhRepoReleaseArchiveTemplate {metadata = metadata'}) = _name metadata'
5153
show (BundledStarterTemplate {metadata = metadata'}) = _name metadata'
54+
show (LocalStarterTemplate _) = "custom"
5255
show AiGeneratedStarterTemplate = "ai-generated"
5356

5457
instance Interactive.IsOption StarterTemplate where
5558
showOption = show
5659

5760
showOptionDescription (GhRepoReleaseArchiveTemplate {metadata = metadata'}) = Just $ _description metadata'
5861
showOptionDescription (BundledStarterTemplate {metadata = metadata'}) = Just $ _description metadata'
62+
showOptionDescription (LocalStarterTemplate _) = Just "A custom starter template from a local path."
5963
showOptionDescription AiGeneratedStarterTemplate =
6064
Just "🤖 Describe an app in a couple of sentences and have Wasp AI generate initial code for you. (experimental)"
6165

@@ -68,6 +72,7 @@ getTemplateStartingInstructions :: String -> StarterTemplate -> String
6872
getTemplateStartingInstructions projectDirName = \case
6973
GhRepoReleaseArchiveTemplate {metadata = metadata'} -> _buildStartingInstructions metadata' projectDirName
7074
BundledStarterTemplate {metadata = metadata'} -> _buildStartingInstructions metadata' projectDirName
75+
LocalStarterTemplate _ -> "Check the starter's documentation for guidance on how to start your app."
7176
AiGeneratedStarterTemplate ->
7277
unlines
7378
[ styleText $ "To run your new app, do:",
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module Wasp.Cli.Command.CreateNewProject.StarterTemplates.Local
2+
( createProjectOnDiskFromLocalTemplate,
3+
)
4+
where
5+
6+
import Path.IO (copyDirRecur)
7+
import StrongPath (Abs, Dir, Dir', Path')
8+
import StrongPath.Path (toPathAbsDir)
9+
import Wasp.Cli.Command.CreateNewProject.ProjectDescription (NewProjectAppName, NewProjectName)
10+
import Wasp.Cli.Command.CreateNewProject.StarterTemplates.Templating (replaceTemplatePlaceholdersInTemplateFiles)
11+
import Wasp.Project (WaspProjectDir)
12+
13+
createProjectOnDiskFromLocalTemplate ::
14+
Path' Abs (Dir WaspProjectDir) -> NewProjectName -> NewProjectAppName -> Path' Abs Dir' -> IO ()
15+
createProjectOnDiskFromLocalTemplate absWaspProjectDir projectName appName templatePath = do
16+
copyDirRecur (toPathAbsDir templatePath) (toPathAbsDir absWaspProjectDir)
17+
replaceTemplatePlaceholdersInTemplateFiles appName projectName absWaspProjectDir

waspc/waspc.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ library cli-lib
591591
Wasp.Cli.Command.CreateNewProject.StarterTemplates
592592
Wasp.Cli.Command.CreateNewProject.StarterTemplates.Bundled
593593
Wasp.Cli.Command.CreateNewProject.StarterTemplates.GhReleaseArchive
594+
Wasp.Cli.Command.CreateNewProject.StarterTemplates.Local
594595
Wasp.Cli.Command.CreateNewProject.StarterTemplates.Templating
595596
Wasp.Cli.Command.Db
596597
Wasp.Cli.Command.Db.Migrate

0 commit comments

Comments
 (0)