From f065f7a6cbe1dfdf46bcf4ec41aecbee22bf4c39 Mon Sep 17 00:00:00 2001 From: Dillon Kearns Date: Tue, 30 May 2023 16:22:28 -0700 Subject: [PATCH] Update init command. --- generator/template/app/Api.elm | 4 +- generator/template/app/Effect.elm | 34 +- generator/template/app/ErrorPage.elm | 24 +- generator/template/app/Route/Blog/Slug_.elm | 86 +++++ generator/template/app/Route/Greet.elm | 107 ++++++ generator/template/app/Route/Hello.elm | 119 +++++++ generator/template/app/Route/Index.elm | 44 ++- generator/template/app/Shared.elm | 54 ++- generator/template/app/Site.elm | 15 +- generator/template/app/View.elm | 8 + generator/template/codegen/.gitignore | 2 + generator/template/codegen/elm.codegen.json | 18 + generator/template/custom-backend-task.ts | 3 + generator/template/elm-pages.config.mjs | 13 + generator/template/elm.json | 55 ++- generator/template/netlify.toml | 5 +- generator/template/package.json | 12 +- generator/template/public/images/icon-png.png | Bin 976 -> 0 bytes .../compiled-ports/custom-backend-task.mjs | 7 + .../template/script/custom-backend-task.ts | 3 + generator/template/script/elm.json | 61 ++++ generator/template/script/src/AddRoute.elm | 312 ++++++++++++++++++ generator/template/script/src/Stars.elm | 42 +++ 23 files changed, 929 insertions(+), 99 deletions(-) create mode 100644 generator/template/app/Route/Blog/Slug_.elm create mode 100644 generator/template/app/Route/Greet.elm create mode 100644 generator/template/app/Route/Hello.elm create mode 100644 generator/template/codegen/.gitignore create mode 100644 generator/template/codegen/elm.codegen.json create mode 100644 generator/template/custom-backend-task.ts delete mode 100644 generator/template/public/images/icon-png.png create mode 100644 generator/template/script/.elm-pages/compiled-ports/custom-backend-task.mjs create mode 100644 generator/template/script/custom-backend-task.ts create mode 100644 generator/template/script/elm.json create mode 100644 generator/template/script/src/AddRoute.elm create mode 100644 generator/template/script/src/Stars.elm diff --git a/generator/template/app/Api.elm b/generator/template/app/Api.elm index 75bc7da1..86aa52ba 100644 --- a/generator/template/app/Api.elm +++ b/generator/template/app/Api.elm @@ -1,6 +1,6 @@ module Api exposing (routes) -import ApiRoute +import ApiRoute exposing (ApiRoute) import BackendTask exposing (BackendTask) import FatalError exposing (FatalError) import Html exposing (Html) @@ -11,7 +11,7 @@ import Route exposing (Route) routes : BackendTask FatalError (List Route) -> (Maybe { indent : Int, newLines : Bool } -> Html Never -> String) - -> List (ApiRoute.ApiRoute ApiRoute.Response) + -> List (ApiRoute ApiRoute.Response) routes getStaticRoutes htmlToString = [] diff --git a/generator/template/app/Effect.elm b/generator/template/app/Effect.elm index ee89c0a1..a4b89795 100644 --- a/generator/template/app/Effect.elm +++ b/generator/template/app/Effect.elm @@ -1,17 +1,25 @@ module Effect exposing (Effect(..), batch, fromCmd, map, none, perform) +{-| + +@docs Effect, batch, fromCmd, map, none, perform + +-} + import Browser.Navigation -import Form.FormData exposing (FormData) +import Form import Http import Json.Decode as Decode import Pages.Fetcher import Url exposing (Url) +{-| -} type Effect msg = None | Cmd (Cmd msg) | Batch (List (Effect msg)) + | GetStargazers (Result Http.Error Int -> msg) | SetField { formId : String, name : String, value : String } | FetchRouteData { data : Maybe FormData @@ -24,27 +32,32 @@ type Effect msg | SubmitFetcher (Pages.Fetcher.Fetcher msg) +{-| -} type alias RequestInfo = { contentType : String , body : String } +{-| -} none : Effect msg none = None +{-| -} batch : List (Effect msg) -> Effect msg batch = Batch +{-| -} fromCmd : Cmd msg -> Effect msg fromCmd = Cmd +{-| -} map : (a -> b) -> Effect a -> Effect b map fn effect = case effect of @@ -57,6 +70,9 @@ map fn effect = Batch list -> Batch (List.map (map fn) list) + GetStargazers toMsg -> + GetStargazers (toMsg >> fn) + FetchRouteData fetchInfo -> FetchRouteData { data = fetchInfo.data @@ -78,6 +94,7 @@ map fn effect = |> SubmitFetcher +{-| -} perform : { fetchRouteData : { data : Maybe FormData @@ -112,6 +129,13 @@ perform ({ fromPageMsg, key } as helpers) effect = Batch list -> Cmd.batch (List.map (perform helpers) list) + GetStargazers toMsg -> + Http.get + { url = + "https://api.github.com/repos/dillonkearns/elm-pages" + , expect = Http.expectJson (toMsg >> fromPageMsg) (Decode.field "stargazers_count" Decode.int) + } + FetchRouteData fetchInfo -> helpers.fetchRouteData fetchInfo @@ -121,3 +145,11 @@ perform ({ fromPageMsg, key } as helpers) effect = SubmitFetcher record -> helpers.runFetcher record + + +type alias FormData = + { fields : List ( String, String ) + , method : Form.Method + , action : String + , id : Maybe String + } diff --git a/generator/template/app/ErrorPage.elm b/generator/template/app/ErrorPage.elm index 38ad1681..69a0127c 100644 --- a/generator/template/app/ErrorPage.elm +++ b/generator/template/app/ErrorPage.elm @@ -3,20 +3,22 @@ module ErrorPage exposing (ErrorPage(..), Model, Msg, head, init, internalError, import Effect exposing (Effect) import Head import Html exposing (Html) +import Html.Events exposing (onClick) import View exposing (View) type Msg - = NoOp + = Increment type alias Model = - {} + { count : Int + } init : ErrorPage -> ( Model, Effect Msg ) init errorPage = - ( {} + ( { count = 0 } , Effect.none ) @@ -24,8 +26,8 @@ init errorPage = update : ErrorPage -> Msg -> Model -> ( Model, Effect Msg ) update errorPage msg model = case msg of - NoOp -> - ( {}, Effect.none ) + Increment -> + ( { model | count = model.count + 1 }, Effect.none ) head : ErrorPage -> List Head.Tag @@ -53,9 +55,19 @@ view error model = { body = [ Html.div [] [ Html.p [] [ Html.text "Page not found. Maybe try another URL?" ] + , Html.div [] + [ Html.button + [ onClick Increment + ] + [ Html.text + (model.count + |> String.fromInt + ) + ] + ] ] ] - , title = "Page Not Found" + , title = "This is a NotFound Error" } diff --git a/generator/template/app/Route/Blog/Slug_.elm b/generator/template/app/Route/Blog/Slug_.elm new file mode 100644 index 00000000..bfbedcff --- /dev/null +++ b/generator/template/app/Route/Blog/Slug_.elm @@ -0,0 +1,86 @@ +module Route.Blog.Slug_ exposing (ActionData, Data, Model, Msg, route) + +import BackendTask exposing (BackendTask) +import FatalError exposing (FatalError) +import Head +import Head.Seo as Seo +import Html +import Pages.Url +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App, StatelessRoute) +import Shared +import View exposing (View) + + +type alias Model = + {} + + +type alias Msg = + () + + +type alias RouteParams = + { slug : String } + + +route : StatelessRoute RouteParams Data ActionData +route = + RouteBuilder.preRender + { head = head + , pages = pages + , data = data + } + |> RouteBuilder.buildNoState { view = view } + + +pages : BackendTask FatalError (List RouteParams) +pages = + BackendTask.succeed + [ { slug = "hello" } + ] + + +type alias Data = + { something : String + } + + +type alias ActionData = + {} + + +data : RouteParams -> BackendTask FatalError Data +data routeParams = + BackendTask.map Data + (BackendTask.succeed "Hi") + + +head : + App Data ActionData RouteParams + -> List Head.Tag +head app = + Seo.summary + { canonicalUrlOverride = Nothing + , siteName = "elm-pages" + , image = + { url = Pages.Url.external "TODO" + , alt = "elm-pages logo" + , dimensions = Nothing + , mimeType = Nothing + } + , description = "TODO" + , locale = Nothing + , title = "TODO title" -- metadata.title -- TODO + } + |> Seo.website + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> View (PagesMsg Msg) +view app sharedModel = + { title = "Placeholder - Blog.Slug_" + , body = [ Html.text "You're on the page Blog.Slug_" ] + } diff --git a/generator/template/app/Route/Greet.elm b/generator/template/app/Route/Greet.elm new file mode 100644 index 00000000..5434b626 --- /dev/null +++ b/generator/template/app/Route/Greet.elm @@ -0,0 +1,107 @@ +module Route.Greet exposing (ActionData, Data, Model, Msg, route) + +import BackendTask exposing (BackendTask) +import BackendTask.Http +import ErrorPage exposing (ErrorPage) +import FatalError exposing (FatalError) +import Head +import Head.Seo as Seo +import Html +import Json.Decode as Decode +import Pages.Url +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App, StatefulRoute, StatelessRoute) +import Server.Request as Request exposing (Request) +import Server.Response as Response exposing (Response) +import Shared +import View exposing (View) + + +type alias Model = + {} + + +type alias Msg = + () + + +type alias RouteParams = + {} + + +route : StatelessRoute RouteParams Data ActionData +route = + RouteBuilder.serverRender + { head = head + , data = data + , action = \_ _ -> BackendTask.fail (FatalError.fromString "No action.") + } + |> RouteBuilder.buildNoState { view = view } + + +type alias Data = + { name : Maybe String + } + + +type alias ActionData = + {} + + +data : RouteParams -> Request -> BackendTask FatalError (Response Data ErrorPage) +data routeParams request = + case request |> Request.queryParam "name" of + Just name -> + BackendTask.Http.getJson "http://worldtimeapi.org/api/timezone/America/Los_Angeles" + (Decode.field "utc_datetime" Decode.string) + |> BackendTask.allowFatal + |> BackendTask.map + (\dateTimeString -> + Response.render + { name = Just dateTimeString } + ) + + Nothing -> + BackendTask.succeed + (Response.render + { name = Nothing } + ) + + +head : + App Data ActionData RouteParams + -> List Head.Tag +head app = + Seo.summary + { canonicalUrlOverride = Nothing + , siteName = "elm-pages" + , image = + { url = Pages.Url.external "TODO" + , alt = "elm-pages logo" + , dimensions = Nothing + , mimeType = Nothing + } + , description = "TODO" + , locale = Nothing + , title = "TODO title" -- metadata.title -- TODO + } + |> Seo.website + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> View (PagesMsg Msg) +view app shared = + { title = "Greetings" + , body = + [ Html.div [] + [ case app.data.name of + Just name -> + Html.text ("Hello " ++ name) + + Nothing -> + Html.text "Hello, I didn't find your name" + ] + ] + } diff --git a/generator/template/app/Route/Hello.elm b/generator/template/app/Route/Hello.elm new file mode 100644 index 00000000..3119a43d --- /dev/null +++ b/generator/template/app/Route/Hello.elm @@ -0,0 +1,119 @@ +module Route.Hello exposing (ActionData, Data, Model, Msg(..), RouteParams, action, data, route) + +import BackendTask exposing (BackendTask) +import BackendTask.Http +import Effect exposing (Effect) +import ErrorPage exposing (ErrorPage) +import FatalError exposing (FatalError) +import Head +import Html +import Json.Decode as Decode +import PagesMsg exposing (PagesMsg) +import RouteBuilder exposing (App) +import Server.Request exposing (Request) +import Server.Response +import Shared +import UrlPath exposing (UrlPath) +import View exposing (View) + + +type alias Model = + {} + + +type Msg + = NoOp + + +type alias RouteParams = + {} + + +route = + RouteBuilder.serverRender { data = data, action = action, head = head } + |> RouteBuilder.buildWithLocalState + { view = view + , subscriptions = subscriptions + , update = update + , init = init + } + + +init : + App Data ActionData RouteParams + -> Shared.Model + -> ( Model, Effect Msg ) +init app shared = + ( {}, Effect.none ) + + +update : + App Data ActionData RouteParams + -> Shared.Model + -> Msg + -> Model + -> ( Model, Effect Msg ) +update app shared msg model = + case msg of + NoOp -> + ( model, Effect.none ) + + +subscriptions : + RouteParams + -> UrlPath + -> Shared.Model + -> Model + -> Sub Msg +subscriptions routeParams path shared model = + Sub.none + + +type alias Data = + { stars : Int + } + + +type alias ActionData = + {} + + +data : + RouteParams + -> Request + -> BackendTask FatalError (Server.Response.Response Data ErrorPage) +data routeParams request = + BackendTask.Http.getWithOptions + { url = "https://api.github.com/repos/dillonkearns/elm-pages" + , expect = BackendTask.Http.expectJson (Decode.field "stargazers_count" Decode.int) + , headers = [] + , cacheStrategy = Just BackendTask.Http.IgnoreCache + , retries = Nothing + , timeoutInMs = Nothing + , cachePath = Nothing + } + |> BackendTask.allowFatal + |> BackendTask.map + (\stars -> Server.Response.render { stars = stars }) + + +head : App Data ActionData RouteParams -> List Head.Tag +head app = + [] + + +view : + App Data ActionData RouteParams + -> Shared.Model + -> Model + -> View (PagesMsg Msg) +view app shared model = + { title = "Hello", body = [ Html.text (String.fromInt app.data.stars) ] } + + +action : + RouteParams + -> Request + -> BackendTask.BackendTask FatalError.FatalError (Server.Response.Response ActionData ErrorPage.ErrorPage) +action routeParams request = + BackendTask.succeed (Server.Response.render {}) diff --git a/generator/template/app/Route/Index.elm b/generator/template/app/Route/Index.elm index 91d67c34..e930c858 100644 --- a/generator/template/app/Route/Index.elm +++ b/generator/template/app/Route/Index.elm @@ -5,13 +5,11 @@ import FatalError exposing (FatalError) import Head import Head.Seo as Seo import Html -import Html.Styled.Attributes as Attr -import PagesMsg exposing (PagesMsg) -import Pages.PageUrl exposing (PageUrl) import Pages.Url -import Path +import PagesMsg exposing (PagesMsg) +import UrlPath import Route -import RouteBuilder exposing (StatefulRoute, StatelessRoute, App) +import RouteBuilder exposing (App, StatelessRoute) import Shared import View exposing (View) @@ -28,6 +26,11 @@ type alias RouteParams = {} +type alias Data = + { message : String + } + + type alias ActionData = {} @@ -41,24 +44,22 @@ route = |> RouteBuilder.buildNoState { view = view } -type alias Data = - () - - data : BackendTask FatalError Data data = - BackendTask.succeed () + BackendTask.succeed Data + |> BackendTask.andMap + (BackendTask.succeed "Hello!") head : App Data ActionData RouteParams -> List Head.Tag -head static = +head app = Seo.summary { canonicalUrlOverride = Nothing , siteName = "elm-pages" , image = - { url = [ "images", "icon-png.png" ] |> Path.join |> Pages.Url.fromPath + { url = [ "images", "icon-png.png" ] |> UrlPath.join |> Pages.Url.fromPath , alt = "elm-pages logo" , dimensions = Nothing , mimeType = Nothing @@ -71,24 +72,17 @@ head static = view : - Maybe PageUrl + App Data ActionData RouteParams -> Shared.Model - -> App Data ActionData RouteParams -> View (PagesMsg Msg) -view maybeUrl sharedModel static = +view app shared = { title = "elm-pages is running" , body = [ Html.h1 [] [ Html.text "elm-pages is up and running!" ] - , Html.h2 [] [ Html.text "Learn more" ] - , Html.ul - [] - [ Html.li [] - [ Html.a [ Attr.href "https://elm-pages.com/docs/" ] [ Html.text "Framework documentation" ] - ] - , Html.li - [] - [ Html.a [ Attr.href "https://package.elm-lang.org/packages/dillonkearns/elm-pages/latest/" ] [ Html.text "Elm package documentation" ] - ] + , Html.p [] + [ Html.text <| "The message is: " ++ app.data.message ] + , Route.Blog__Slug_ { slug = "hello" } + |> Route.link [] [ Html.text "My blog post" ] ] } diff --git a/generator/template/app/Shared.elm b/generator/template/app/Shared.elm index 7af1052e..b5672551 100644 --- a/generator/template/app/Shared.elm +++ b/generator/template/app/Shared.elm @@ -4,9 +4,10 @@ import BackendTask exposing (BackendTask) import Effect exposing (Effect) import FatalError exposing (FatalError) import Html exposing (Html) +import Html.Events import Pages.Flags import Pages.PageUrl exposing (PageUrl) -import Path exposing (Path) +import UrlPath exposing (UrlPath) import Route exposing (Route) import SharedTemplate exposing (SharedTemplate) import View exposing (View) @@ -19,16 +20,13 @@ template = , view = view , data = data , subscriptions = subscriptions - , onPageChange = Just OnPageChange + , onPageChange = Nothing } type Msg - = OnPageChange - { path : Path - , query : Maybe String - , fragment : Maybe String - } + = SharedMsg SharedMsg + | MenuClicked type alias Data = @@ -40,7 +38,7 @@ type SharedMsg type alias Model = - { showMobileMenu : Bool + { showMenu : Bool } @@ -49,7 +47,7 @@ init : -> Maybe { path : - { path : Path + { path : UrlPath , query : Maybe String , fragment : Maybe String } @@ -58,7 +56,7 @@ init : } -> ( Model, Effect Msg ) init flags maybePagePath = - ( { showMobileMenu = False } + ( { showMenu = False } , Effect.none ) @@ -66,11 +64,14 @@ init flags maybePagePath = update : Msg -> Model -> ( Model, Effect Msg ) update msg model = case msg of - OnPageChange _ -> - ( { model | showMobileMenu = False }, Effect.none ) + SharedMsg globalMsg -> + ( model, Effect.none ) + + MenuClicked -> + ( { model | showMenu = not model.showMenu }, Effect.none ) -subscriptions : Path -> Model -> Sub Msg +subscriptions : UrlPath -> Model -> Sub Msg subscriptions _ _ = Sub.none @@ -83,16 +84,37 @@ data = view : Data -> - { path : Path + { path : UrlPath , route : Maybe Route } -> Model -> (Msg -> msg) -> View msg -> { body : List (Html msg), title : String } -view stars page model toMsg pageView = +view sharedData page model toMsg pageView = { body = - [ Html.div [] pageView.body + [ Html.nav [] + [ Html.button + [ Html.Events.onClick MenuClicked ] + [ Html.text + (if model.showMenu then + "Close Menu" + + else + "Open Menu" + ) + ] + , if model.showMenu then + Html.ul [] + [ Html.li [] [ Html.text "Menu item 1" ] + , Html.li [] [ Html.text "Menu item 2" ] + ] + + else + Html.text "" + ] + |> Html.map toMsg + , Html.main_ [] pageView.body ] , title = pageView.title } diff --git a/generator/template/app/Site.elm b/generator/template/app/Site.elm index f7fb734d..7d6af63e 100644 --- a/generator/template/app/Site.elm +++ b/generator/template/app/Site.elm @@ -1,16 +1,14 @@ -module Site exposing (canonicalUrl, config) +module Site exposing (config) import BackendTask exposing (BackendTask) import FatalError exposing (FatalError) import Head -import MimeType -import Pages.Url import SiteConfig exposing (SiteConfig) config : SiteConfig config = - { canonicalUrl = canonicalUrl + { canonicalUrl = "https://elm-pages.com" , head = head } @@ -18,15 +16,6 @@ config = head : BackendTask FatalError (List Head.Tag) head = [ Head.metaName "viewport" (Head.raw "width=device-width,initial-scale=1") - , Head.metaName "mobile-web-app-capable" (Head.raw "yes") - , Head.metaName "theme-color" (Head.raw "#ffffff") - , Head.metaName "apple-mobile-web-app-capable" (Head.raw "yes") - , Head.metaName "apple-mobile-web-app-status-bar-style" (Head.raw "black-translucent") , Head.sitemapLink "/sitemap.xml" ] |> BackendTask.succeed - - -canonicalUrl : String -canonicalUrl = - "https://elm-pages.com" diff --git a/generator/template/app/View.elm b/generator/template/app/View.elm index 54c7322b..3a39ad0a 100644 --- a/generator/template/app/View.elm +++ b/generator/template/app/View.elm @@ -1,14 +1,22 @@ module View exposing (View, map) +{-| + +@docs View, map + +-} + import Html exposing (Html) +{-| -} type alias View msg = { title : String , body : List (Html msg) } +{-| -} map : (msg1 -> msg2) -> View msg1 -> View msg2 map fn doc = { title = doc.title diff --git a/generator/template/codegen/.gitignore b/generator/template/codegen/.gitignore new file mode 100644 index 00000000..3dfc6adc --- /dev/null +++ b/generator/template/codegen/.gitignore @@ -0,0 +1,2 @@ +Gen/ + diff --git a/generator/template/codegen/elm.codegen.json b/generator/template/codegen/elm.codegen.json new file mode 100644 index 00000000..329af994 --- /dev/null +++ b/generator/template/codegen/elm.codegen.json @@ -0,0 +1,18 @@ +{ + "elm-codegen-version": "0.2.0", + "codegen-helpers": { + "packages": { + "elm/core": "1.0.5", + "dillonkearns/elm-form": "3.0.0", + "elm/html": "1.0.0", + "rtfeldman/elm-css": "18.0.0", + "dillonkearns/elm-pages-v3-beta": "21.0.0", + "elm/json": "1.1.3" + }, + "local": [ + ".elm-pages/", + "app/", + "src/" + ] + } +} \ No newline at end of file diff --git a/generator/template/custom-backend-task.ts b/generator/template/custom-backend-task.ts new file mode 100644 index 00000000..8a59bea6 --- /dev/null +++ b/generator/template/custom-backend-task.ts @@ -0,0 +1,3 @@ +export async function hello(name) { + return `Hello ${name}!`; +} diff --git a/generator/template/elm-pages.config.mjs b/generator/template/elm-pages.config.mjs index fc9d07e7..8982a8d4 100644 --- a/generator/template/elm-pages.config.mjs +++ b/generator/template/elm-pages.config.mjs @@ -1,5 +1,18 @@ import { defineConfig } from "vite"; +import adapter from "elm-pages/adapter/netlify.js"; export default { vite: defineConfig({}), + adapter, + headTagsTemplate(context) { + return ` + + +`; + }, + preloadTagForFile(file) { + // add preload directives for JS assets and font assets, etc., skip for CSS files + // this function will be called with each file that is procesed by Vite, including any files in your headTagsTemplate in your config + return !file.endsWith(".css"); + }, }; diff --git a/generator/template/elm.json b/generator/template/elm.json index 65bcca98..e8676485 100644 --- a/generator/template/elm.json +++ b/generator/template/elm.json @@ -1,60 +1,57 @@ { "type": "application", "source-directories": [ - "app", "src", - ".elm-pages" + "../codegen" ], "elm-version": "0.19.1", "dependencies": { "direct": { + "dillonkearns/elm-cli-options-parser": "3.2.0", + "dillonkearns/elm-pages-v3-beta": "21.0.0", + "elm/bytes": "1.0.8", + "elm/core": "1.0.5", + "elm/html": "1.0.0", + "elm/json": "1.1.3", + "mdgriffith/elm-codegen": "3.0.0" + }, + "indirect": { + "Chadtech/elm-bool-extra": "2.4.2", "avh4/elm-color": "1.0.0", "danfishgold/base64-bytes": "1.1.0", "danyx23/elm-mimetype": "4.0.1", "dillonkearns/elm-bcp47-language-tag": "1.0.1", - "dillonkearns/elm-markdown": "7.0.1", - "dillonkearns/elm-pages-v3-beta": "12.0.0", + "dillonkearns/elm-date-or-date-time": "2.0.0", + "dillonkearns/elm-form": "3.0.0", "elm/browser": "1.0.2", - "elm/bytes": "1.0.8", - "elm/core": "1.0.5", - "elm/html": "1.0.0", + "elm/file": "1.0.5", "elm/http": "2.0.0", - "elm/json": "1.1.3", "elm/parser": "1.1.0", + "elm/random": "1.0.0", "elm/regex": "1.0.0", "elm/time": "1.0.0", "elm/url": "1.0.0", "elm/virtual-dom": "1.0.3", - "elm-community/dict-extra": "2.4.0", - "elm-community/list-extra": "8.7.0", - "elm-community/result-extra": "2.4.0", - "jluckyiv/elm-utc-date-strings": "1.0.0", - "justinmimbs/date": "4.0.1", - "mdgriffith/elm-codegen": "2.0.0", - "miniBill/elm-codec": "2.0.0", - "noahzgordon/elm-color-extra": "1.0.2", - "robinheghan/fnv1a": "1.0.0", - "rtfeldman/elm-css": "18.0.0", - "the-sett/elm-syntax-dsl": "6.0.2", - "turboMaCk/non-empty-list-alias": "1.3.1", - "vito/elm-ansi": "10.0.1" - }, - "indirect": { - "Chadtech/elm-bool-extra": "2.4.2", - "dillonkearns/elm-cli-options-parser": "3.2.0", - "dillonkearns/elm-date-or-date-time": "2.0.0", - "elm/file": "1.0.5", - "elm/random": "1.0.0", "elm-community/basics-extra": "4.1.0", + "elm-community/list-extra": "8.7.0", "elm-community/maybe-extra": "5.3.0", "fredcy/elm-parseint": "2.0.1", + "jluckyiv/elm-utc-date-strings": "1.0.0", + "justinmimbs/date": "4.0.1", + "miniBill/elm-codec": "2.0.0", "miniBill/elm-unicode": "1.0.3", + "noahzgordon/elm-color-extra": "1.0.2", + "robinheghan/fnv1a": "1.0.0", "robinheghan/murmur3": "1.0.0", + "rtfeldman/elm-css": "18.0.0", "rtfeldman/elm-hex": "1.0.0", "rtfeldman/elm-iso8601-date-strings": "1.1.4", "stil4m/elm-syntax": "7.2.9", "stil4m/structured-writer": "1.0.3", - "the-sett/elm-pretty-printer": "3.0.0" + "the-sett/elm-pretty-printer": "3.0.0", + "the-sett/elm-syntax-dsl": "6.0.2", + "turboMaCk/non-empty-list-alias": "1.3.1", + "vito/elm-ansi": "10.0.1" } }, "test-dependencies": { diff --git a/generator/template/netlify.toml b/generator/template/netlify.toml index 42cb0bc4..79202277 100644 --- a/generator/template/netlify.toml +++ b/generator/template/netlify.toml @@ -1,10 +1,13 @@ [build] functions = "functions/" publish = "dist/" - command = "export ELM_HOME=\"$NETLIFY_BUILD_BASE/cache/elm\" && npm install --no-optional && npm run build" + command = "mkdir bin && export PATH=\"/opt/build/repo/bin:$PATH\" && echo $PATH && curl https://static.lamdera.com/bin/linux/lamdera -o bin/lamdera && chmod a+x bin/lamdera && export ELM_HOME=\"$NETLIFY_BUILD_BASE/cache/elm\" && npm install && npm run build" [dev] command = "npm start" targetPort = 1234 autoLaunch = true framework = "#custom" + +[functions] + node_bundler = "esbuild" diff --git a/generator/template/package.json b/generator/template/package.json index 5436ca03..f109b644 100644 --- a/generator/template/package.json +++ b/generator/template/package.json @@ -7,14 +7,14 @@ "build": "elm-pages build" }, "devDependencies": { - "elm-codegen": "^0.2.0", + "elm-codegen": "^0.3.0", "elm-optimize-level-2": "^0.3.5", - "elm-pages": "^3.0.0-beta.20", - "elm-review": "2.8.5", - "elm-tooling": "^1.10.0", - "vite": "^3.1.8" + "elm-pages": "^3.0.0-beta.42", + "elm-review": "^2.10.2", + "elm-tooling": "^1.14.0", + "vite": "^4.3.5" }, "dependencies": { - "@netlify/functions": "^1.2.0" + "@netlify/functions": "^1.4.0" } } diff --git a/generator/template/public/images/icon-png.png b/generator/template/public/images/icon-png.png deleted file mode 100644 index 1e514b4bdb55f895dd68bc1d7d43a0af92c57119..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 976 zcmV;>126oEP)U*Ls*iFe)z5v?GWmQwtk*E+56q7V@_ncnrOm=p5%+9Xv zIwR+W!!Ym6!2D)+=Xstz2W;80Rj4Q(WytbP zZrgxV4eQt@4jvT2Y~s8LDZU^IJ?igT?0<3GE@oU-*QIL%zrBY>NV;Vkrni{m8|mFc2Lko**8~r6ZAH;tK*WltV5bhJkKB%+)ZM zc!K~8^&wXdL16)Ay9?8iED#UomxDo=YkrtyfB+2lA-^4fLRTnEazFrvmm59AhJUOy%i1Ym6+j4k)}oK&ONn`igYf1U*; z0ZT5B&DPrV-fq;f!;o=W%GEFkubyL`+y<>91_gm#+hJuPULdmJds6GiK+jd@3Mpdd z-nbbwE?NOD?Bu{7$SK(VnHm31+gF&#DZ853zDpAJdeD1 zgq|2LKY}RJ3Y}?4r$BVbabHgv$aTSqZ=qA#;C`P!#N3-`*^`y;Mqyo{^&qlw7pZld zF~#ZX>>=7`-z#~Q4OxX5>2J#RH@5e-W^;iqq!db~~iLH7W2 z_h##KWNf58I-6|A5l7FDU=T65vS@rM2G80t_yznx=lLryVFC8etUdR{AX&(UQcH#J+a yO}LBvKZmc@ + cliOptions + |> createFile + |> Script.writeFile + |> BackendTask.allowFatal + ) + + +program : Program.Config CliOptions +program = + Program.config + |> Program.add + (OptionsParser.build CliOptions + |> OptionsParser.with (Option.requiredPositionalArg "module" |> Scaffold.Route.moduleNameCliArg) + |> OptionsParser.withRestArgs Scaffold.Form.restArgsParser + ) + + +createFile : CliOptions -> { path : String, body : String } +createFile { moduleName, fields } = + let + formHelpers : + Maybe + { formHandlers : Elm.Expression + , form : Elm.Expression + , declarations : List Elm.Declaration + } + formHelpers = + Scaffold.Form.provide + { fields = fields + , elmCssView = False + , view = + \{ formState, params } -> + Elm.Let.letIn + (\fieldView -> + Elm.list + ((params + |> List.map + (\{ name, kind, param } -> + fieldView (Elm.string name) param + ) + ) + ++ [ Elm.ifThen formState.submitting + (Html.button + [ Attr.disabled True + ] + [ Html.text "Submitting..." + ] + ) + (Html.button [] + [ Html.text "Submit" + ] + ) + ] + ) + ) + |> Elm.Let.fn2 "fieldView" + ( "label", Type.string |> Just ) + ( "field", Nothing ) + (\label field -> + Html.div [] + [ Html.label [] + [ Html.call_.text (Elm.Op.append label (Elm.string " ")) + , field |> FieldView.input [] + , errorsView.call formState.errors field + ] + ] + ) + |> Elm.Let.toExpression + } + in + Scaffold.Route.serverRender + { moduleName = moduleName + , action = + ( Alias + (Type.record + (case formHelpers of + Just _ -> + [ ( "errors", Type.namedWith [ "Form" ] "ServerResponse" [ Type.string ] ) + ] + + Nothing -> + [] + ) + ) + , \routeParams request -> + formHelpers + |> Maybe.map + (\justFormHelp -> + Request.formData justFormHelp.formHandlers request + |> Gen.Maybe.call_.map + (Elm.fn ( "formData", Nothing ) + (\formData -> + Elm.Case.tuple formData + "response" + "parsedForm" + (\response parsedForm -> + Elm.Case.custom parsedForm + Type.int + [ Elm.Case.branch1 "Form.Valid" + ( "validatedForm", Type.int ) + (\validatedForm -> + Elm.Case.custom validatedForm + Type.int + [ Elm.Case.branch1 "Action" + ( "parsed", Type.int ) + (\parsed -> + Scaffold.Form.recordEncoder parsed fields + |> Gen.Json.Encode.encode 2 + |> Gen.Pages.Script.call_.log + |> Gen.BackendTask.call_.map + (Elm.fn ( "_", Nothing ) + (\_ -> + Response.render + (Elm.record + [ ( "errors", response ) + ] + ) + ) + ) + ) + ] + ) + , Elm.Case.branch2 "Form.Invalid" + ( "parsed", Type.int ) + ( "error", Type.int ) + (\_ _ -> + "Form validations did not succeed!" + |> Gen.Pages.Script.log + |> Gen.BackendTask.call_.map + (Elm.fn ( "_", Nothing ) + (\_ -> + Response.render + (Elm.record + [ ( "errors", response ) + ] + ) + ) + ) + ) + ] + ) + ) + ) + |> Gen.Maybe.withDefault + (Gen.BackendTask.fail + (Gen.FatalError.fromString "Expected form post") + ) + ) + |> Maybe.withDefault + (Gen.BackendTask.succeed + (Response.render + (Elm.record []) + ) + ) + ) + , data = + ( Alias (Type.record []) + , \routeParams request -> + Gen.BackendTask.succeed + (Response.render + (Elm.record []) + ) + ) + , head = \app -> Elm.list [] + } + |> Scaffold.Route.addDeclarations + (formHelpers + |> Maybe.map .declarations + |> Maybe.map ((::) errorsView.declaration) + |> Maybe.withDefault [] + ) + |> Scaffold.Route.buildWithLocalState + { view = + \{ shared, model, app } -> + Gen.View.make_.view + { title = moduleName |> String.join "." |> Elm.string + , body = + Elm.list + (case formHelpers of + Just justFormHelp -> + [ Html.h2 [] [ Html.text "Form" ] + , justFormHelp.form + |> PagesForm.call_.renderHtml + (Elm.list []) + (Form.options "form" + |> Form.withServerResponse + (app + |> Elm.get "action" + |> Gen.Maybe.map (Elm.get "errors") + ) + ) + app + ] + + Nothing -> + [ Html.h2 [] [ Html.text "New Page" ] + ] + ) + } + , update = + \{ shared, app, msg, model } -> + Elm.Case.custom msg + (Type.named [] "Msg") + [ Elm.Case.branch0 "NoOp" + (Elm.tuple model + Effect.none + ) + ] + , init = + \{ shared, app } -> + Elm.tuple (Elm.record []) Effect.none + , subscriptions = + \{ routeParams, path, shared, model } -> + Elm.val "Sub.none" + , model = + Alias (Type.record []) + , msg = + Custom [ Elm.variant "NoOp" ] + } + + +errorsView : + { declaration : Elm.Declaration + , call : Elm.Expression -> Elm.Expression -> Elm.Expression + , callFrom : List String -> Elm.Expression -> Elm.Expression -> Elm.Expression + , value : List String -> Elm.Expression + } +errorsView = + Elm.Declare.fn2 "errorsView" + ( "errors", Type.namedWith [ "Form" ] "Errors" [ Type.string ] |> Just ) + ( "field" + , Type.namedWith [ "Form", "Validation" ] + "Field" + [ Type.string + , Type.var "parsed" + , Type.var "kind" + ] + |> Just + ) + (\errors field -> + Elm.ifThen + (Gen.List.call_.isEmpty (Form.errorsForField field errors)) + (Html.div [] []) + (Html.div + [] + [ Html.call_.ul (Elm.list []) + (Gen.List.call_.map + (Elm.fn ( "error", Nothing ) + (\error -> + Html.li + [ Attr.style "color" "red" + ] + [ Html.call_.text error + ] + ) + ) + (Form.errorsForField field errors) + ) + ] + ) + |> Elm.withType + (Type.namedWith [ "Html" ] + "Html" + [ Type.namedWith + [ "PagesMsg" ] + "PagesMsg" + [ Type.named [] "Msg" ] + ] + ) + ) diff --git a/generator/template/script/src/Stars.elm b/generator/template/script/src/Stars.elm new file mode 100644 index 00000000..80ecee86 --- /dev/null +++ b/generator/template/script/src/Stars.elm @@ -0,0 +1,42 @@ +module Stars exposing (run) + +import BackendTask exposing (BackendTask) +import BackendTask.Http +import Cli.Option as Option +import Cli.OptionsParser as OptionsParser +import Cli.Program as Program +import Json.Decode as Decode +import Pages.Script as Script exposing (Script) + + +run : Script +run = + Script.withCliOptions program + (\{ username, repo } -> + BackendTask.Http.getJson + ("https://api.github.com/repos/dillonkearns/" ++ repo) + (Decode.field "stargazers_count" Decode.int) + |> BackendTask.allowFatal + |> BackendTask.andThen + (\stars -> + Script.log (String.fromInt stars) + ) + ) + + +type alias CliOptions = + { username : String + , repo : String + } + + +program : Program.Config CliOptions +program = + Program.config + |> Program.add + (OptionsParser.build CliOptions + |> OptionsParser.with + (Option.optionalKeywordArg "username" |> Option.withDefault "dillonkearns") + |> OptionsParser.with + (Option.optionalKeywordArg "repo" |> Option.withDefault "elm-pages") + )