Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support in Wasp for external/community starter templates #2402

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Prev Previous commit
Next Next commit
More TODOs solved.
Martinsos committed Dec 5, 2024
commit aaeda4fea2dec58a99889849a7a44e8c665307b2
7 changes: 0 additions & 7 deletions waspc/cli/src/Wasp/Cli/Command/CreateNewProject/Common.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
module Wasp.Cli.Command.CreateNewProject.Common
( throwProjectCreationError,
throwInvalidTemplateNameUsedError,
defaultWaspVersionBounds,
)
where
@@ -13,11 +12,5 @@ import qualified Wasp.Version as WV
throwProjectCreationError :: String -> Command a
throwProjectCreationError = throwError . CommandError "Project creation failed"

throwInvalidTemplateNameUsedError :: Command a
throwInvalidTemplateNameUsedError =
throwProjectCreationError $
"Are you sure that the template exists?"
<> " 🤔 Check the list of templates here: https://github.com/wasp-lang/starters"

defaultWaspVersionBounds :: String
defaultWaspVersionBounds = show (SV.backwardsCompatibleWith WV.waspVersion)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this file I added the notion of github:.... So removing it would mostly come down to delegint big part of this file.

TODO: I should remove github: support from there if we end up not going with it.

Original file line number Diff line number Diff line change
@@ -7,29 +7,50 @@ where
import Control.Arrow (left)
import Data.Foldable (find)
import Data.Function ((&))
import Data.List (isPrefixOf)
import Data.List (intercalate, isPrefixOf)
import Data.Maybe (fromMaybe)
import StrongPath (Dir', Path', Rel', parseRelDir, reldir)
import qualified System.FilePath.Posix as FP.Posix
import qualified Text.Parsec as P
import Wasp.Cli.Command (Command)
import Wasp.Cli.Command.CreateNewProject.Common (throwInvalidTemplateNameUsedError)
import Wasp.Cli.Command.CreateNewProject.Common (throwProjectCreationError)
import Wasp.Cli.Command.CreateNewProject.StarterTemplates.Common (styleText, waspVersionTemplateGitTag)
import qualified Wasp.Cli.Command.CreateNewProject.StarterTemplates.StarterTemplate as ST
import qualified Wasp.Cli.GithubRepo as GhRepo

-- | A way to uniquely reference a specific Wasp starter template.
data StarterTemplateId
= GhRepoTemplateUri !GhRepo.GithubRepoOwner !GhRepo.GithubRepoName !(Maybe (Path' Rel' Dir'))
| EmbeddedTemplateName !String
= -- | References one of the featured templates (templates that we know about here in Wasp CLI) by its name.
FeaturedTemplateName !String
| -- | References any template that is available as a github repo.
-- Most useful for referencing third-party repos that we don't know about, since featured ones
-- we can more simply reference by name.
GhRepoTemplateUri !GhRepo.GithubRepoOwner !GhRepo.GithubRepoName !(Maybe (Path' Rel' Dir'))

-- TODO: Comment (explain it will try to find it in featured templates or construct it based on id).
data StarterTemplateIdType
= IdTypeFeaturedTemplateName
| IdTypeGhRepoTemplateUri
deriving (Enum, Bounded)

-- TODO: Explain what is this type used for, and also ignore the warning about it not being used.
getStarterTemplateIdType :: StarterTemplateId -> StarterTemplateIdType
getStarterTemplateIdType = \case
FeaturedTemplateName {} -> IdTypeFeaturedTemplateName
GhRepoTemplateUri {} -> IdTypeGhRepoTemplateUri

getStarterTemplateIdTypeDescription :: StarterTemplateIdType -> String
getStarterTemplateIdTypeDescription = \case
IdTypeFeaturedTemplateName -> "a featured template name (e.g. \"saas\")"
IdTypeGhRepoTemplateUri -> "a github URI (github:<owner>/<repo>[/some/dir])"

-- | Given a template id (as string), it will obtain the information on the template that this id references.
-- It will throw if the id is invalid (can't be parsed, or information on the template can't be obtain based on it).
getStarterTemplateByIdOrThrow :: [ST.StarterTemplate] -> String -> Command ST.StarterTemplate
getStarterTemplateByIdOrThrow featuredTemplates templateId =
-- TODO: Refactor/rename throwInvalidTemplateNameUsedError? Yeah or probably just make another error function here, that is better suited for the situation (which is failed parsing of the template id.
(parseStarterTemplateId templateId & either (const throwInvalidTemplateNameUsedError) pure) >>= \case
EmbeddedTemplateName templateName ->
getStarterTemplateByIdOrThrow featuredTemplates templateIdString =
(parseStarterTemplateId templateIdString & either throwTemplateIdParsingError pure) >>= \case
FeaturedTemplateName templateName ->
findTemplateByName featuredTemplates templateName
& maybe throwInvalidTemplateNameUsedError pure
& maybe (throwInvalidTemplateNameUsedError templateName) pure
GhRepoTemplateUri repoOwner repoName maybeTmplDirPath ->
return $
ST.GhRepoStarterTemplate
@@ -46,16 +67,24 @@ getStarterTemplateByIdOrThrow featuredTemplates templateId =
]
}
)
where
throwTemplateIdParsingError errorMsg =
throwProjectCreationError $
"Failed to parse template id: " <> errorMsg <> "\n" <> expectedInputMessage

throwInvalidTemplateNameUsedError templateName =
throwProjectCreationError $
"There is no featured template with name " <> templateName <> ".\n" <> expectedInputMessage

-- TODO: Use getTemplateName here instead of `show`.
expectedInputMessage =
"Expected " <> intercalate " or " (getStarterTemplateIdTypeDescription <$> [minBound .. maxBound]) <> "."
<> (" Valid featured template names are " <> intercalate ", " (show <$> featuredTemplates) <> ".")

-- TODO: Write a comment here.
parseStarterTemplateId :: String -> Either String StarterTemplateId
parseStarterTemplateId = \case
templateId
| ghRepoTemplateIdPrefix `isPrefixOf` templateId ->
-- TODO: Do something with the parse error message or just let it through as I do now?
parseGhRepoTemplateUri templateId & left show
templateId ->
pure $ EmbeddedTemplateName templateId
templateId | ghRepoTemplateIdPrefix `isPrefixOf` templateId -> parseGhRepoTemplateUri templateId & left show
templateId -> pure $ FeaturedTemplateName templateId
where
-- Parses following format: github:<org_name>/<repo_name>[/<path_to_template_dir>] .
parseGhRepoTemplateUri :: String -> Either P.ParseError StarterTemplateId
@@ -68,8 +97,8 @@ parseStarterTemplateId = \case
maybeTmplDirStrongPath <-
P.optionMaybe (P.char FP.Posix.pathSeparator >> P.many1 P.anyChar) >>= \case
Nothing -> pure Nothing
-- NOTE: Even though parseRelDir returns System Path, it is able to parse both Posix and System
-- separators, which enables us to use it here even though we expect Posix.
-- Even though parseRelDir returns System Path, it is able to parse both Posix and System
-- separators, which enables us to use it here even though we expect Posix.
Just tmplDirFilePath -> either (fail . show) (pure . Just) $ parseRelDir tmplDirFilePath
return $ GhRepoTemplateUri repoOwner repoName maybeTmplDirStrongPath