Update init command.

This commit is contained in:
Dillon Kearns 2023-05-30 16:22:28 -07:00
parent 79fe88b2bc
commit f065f7a6cb
23 changed files with 929 additions and 99 deletions

View File

@ -1,6 +1,6 @@
module Api exposing (routes) module Api exposing (routes)
import ApiRoute import ApiRoute exposing (ApiRoute)
import BackendTask exposing (BackendTask) import BackendTask exposing (BackendTask)
import FatalError exposing (FatalError) import FatalError exposing (FatalError)
import Html exposing (Html) import Html exposing (Html)
@ -11,7 +11,7 @@ import Route exposing (Route)
routes : routes :
BackendTask FatalError (List Route) BackendTask FatalError (List Route)
-> (Maybe { indent : Int, newLines : Bool } -> Html Never -> String) -> (Maybe { indent : Int, newLines : Bool } -> Html Never -> String)
-> List (ApiRoute.ApiRoute ApiRoute.Response) -> List (ApiRoute ApiRoute.Response)
routes getStaticRoutes htmlToString = routes getStaticRoutes htmlToString =
[] []

View File

@ -1,17 +1,25 @@
module Effect exposing (Effect(..), batch, fromCmd, map, none, perform) module Effect exposing (Effect(..), batch, fromCmd, map, none, perform)
{-|
@docs Effect, batch, fromCmd, map, none, perform
-}
import Browser.Navigation import Browser.Navigation
import Form.FormData exposing (FormData) import Form
import Http import Http
import Json.Decode as Decode import Json.Decode as Decode
import Pages.Fetcher import Pages.Fetcher
import Url exposing (Url) import Url exposing (Url)
{-| -}
type Effect msg type Effect msg
= None = None
| Cmd (Cmd msg) | Cmd (Cmd msg)
| Batch (List (Effect msg)) | Batch (List (Effect msg))
| GetStargazers (Result Http.Error Int -> msg)
| SetField { formId : String, name : String, value : String } | SetField { formId : String, name : String, value : String }
| FetchRouteData | FetchRouteData
{ data : Maybe FormData { data : Maybe FormData
@ -24,27 +32,32 @@ type Effect msg
| SubmitFetcher (Pages.Fetcher.Fetcher msg) | SubmitFetcher (Pages.Fetcher.Fetcher msg)
{-| -}
type alias RequestInfo = type alias RequestInfo =
{ contentType : String { contentType : String
, body : String , body : String
} }
{-| -}
none : Effect msg none : Effect msg
none = none =
None None
{-| -}
batch : List (Effect msg) -> Effect msg batch : List (Effect msg) -> Effect msg
batch = batch =
Batch Batch
{-| -}
fromCmd : Cmd msg -> Effect msg fromCmd : Cmd msg -> Effect msg
fromCmd = fromCmd =
Cmd Cmd
{-| -}
map : (a -> b) -> Effect a -> Effect b map : (a -> b) -> Effect a -> Effect b
map fn effect = map fn effect =
case effect of case effect of
@ -57,6 +70,9 @@ map fn effect =
Batch list -> Batch list ->
Batch (List.map (map fn) list) Batch (List.map (map fn) list)
GetStargazers toMsg ->
GetStargazers (toMsg >> fn)
FetchRouteData fetchInfo -> FetchRouteData fetchInfo ->
FetchRouteData FetchRouteData
{ data = fetchInfo.data { data = fetchInfo.data
@ -78,6 +94,7 @@ map fn effect =
|> SubmitFetcher |> SubmitFetcher
{-| -}
perform : perform :
{ fetchRouteData : { fetchRouteData :
{ data : Maybe FormData { data : Maybe FormData
@ -112,6 +129,13 @@ perform ({ fromPageMsg, key } as helpers) effect =
Batch list -> Batch list ->
Cmd.batch (List.map (perform helpers) 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 -> FetchRouteData fetchInfo ->
helpers.fetchRouteData helpers.fetchRouteData
fetchInfo fetchInfo
@ -121,3 +145,11 @@ perform ({ fromPageMsg, key } as helpers) effect =
SubmitFetcher record -> SubmitFetcher record ->
helpers.runFetcher record helpers.runFetcher record
type alias FormData =
{ fields : List ( String, String )
, method : Form.Method
, action : String
, id : Maybe String
}

View File

@ -3,20 +3,22 @@ module ErrorPage exposing (ErrorPage(..), Model, Msg, head, init, internalError,
import Effect exposing (Effect) import Effect exposing (Effect)
import Head import Head
import Html exposing (Html) import Html exposing (Html)
import Html.Events exposing (onClick)
import View exposing (View) import View exposing (View)
type Msg type Msg
= NoOp = Increment
type alias Model = type alias Model =
{} { count : Int
}
init : ErrorPage -> ( Model, Effect Msg ) init : ErrorPage -> ( Model, Effect Msg )
init errorPage = init errorPage =
( {} ( { count = 0 }
, Effect.none , Effect.none
) )
@ -24,8 +26,8 @@ init errorPage =
update : ErrorPage -> Msg -> Model -> ( Model, Effect Msg ) update : ErrorPage -> Msg -> Model -> ( Model, Effect Msg )
update errorPage msg model = update errorPage msg model =
case msg of case msg of
NoOp -> Increment ->
( {}, Effect.none ) ( { model | count = model.count + 1 }, Effect.none )
head : ErrorPage -> List Head.Tag head : ErrorPage -> List Head.Tag
@ -53,9 +55,19 @@ view error model =
{ body = { body =
[ Html.div [] [ Html.div []
[ Html.p [] [ Html.text "Page not found. Maybe try another URL?" ] [ 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"
} }

View File

@ -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_" ]
}

View File

@ -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"
]
]
}

View File

@ -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 {})

View File

@ -5,13 +5,11 @@ import FatalError exposing (FatalError)
import Head import Head
import Head.Seo as Seo import Head.Seo as Seo
import Html import Html
import Html.Styled.Attributes as Attr
import PagesMsg exposing (PagesMsg)
import Pages.PageUrl exposing (PageUrl)
import Pages.Url import Pages.Url
import Path import PagesMsg exposing (PagesMsg)
import UrlPath
import Route import Route
import RouteBuilder exposing (StatefulRoute, StatelessRoute, App) import RouteBuilder exposing (App, StatelessRoute)
import Shared import Shared
import View exposing (View) import View exposing (View)
@ -28,6 +26,11 @@ type alias RouteParams =
{} {}
type alias Data =
{ message : String
}
type alias ActionData = type alias ActionData =
{} {}
@ -41,24 +44,22 @@ route =
|> RouteBuilder.buildNoState { view = view } |> RouteBuilder.buildNoState { view = view }
type alias Data =
()
data : BackendTask FatalError Data data : BackendTask FatalError Data
data = data =
BackendTask.succeed () BackendTask.succeed Data
|> BackendTask.andMap
(BackendTask.succeed "Hello!")
head : head :
App Data ActionData RouteParams App Data ActionData RouteParams
-> List Head.Tag -> List Head.Tag
head static = head app =
Seo.summary Seo.summary
{ canonicalUrlOverride = Nothing { canonicalUrlOverride = Nothing
, siteName = "elm-pages" , siteName = "elm-pages"
, image = , 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" , alt = "elm-pages logo"
, dimensions = Nothing , dimensions = Nothing
, mimeType = Nothing , mimeType = Nothing
@ -71,24 +72,17 @@ head static =
view : view :
Maybe PageUrl App Data ActionData RouteParams
-> Shared.Model -> Shared.Model
-> App Data ActionData RouteParams
-> View (PagesMsg Msg) -> View (PagesMsg Msg)
view maybeUrl sharedModel static = view app shared =
{ title = "elm-pages is running" { title = "elm-pages is running"
, body = , body =
[ Html.h1 [] [ Html.text "elm-pages is up and running!" ] [ Html.h1 [] [ Html.text "elm-pages is up and running!" ]
, Html.h2 [] [ Html.text "Learn more" ] , Html.p []
, Html.ul [ Html.text <| "The message is: " ++ app.data.message
[]
[ 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" ]
]
] ]
, Route.Blog__Slug_ { slug = "hello" }
|> Route.link [] [ Html.text "My blog post" ]
] ]
} }

View File

@ -4,9 +4,10 @@ import BackendTask exposing (BackendTask)
import Effect exposing (Effect) import Effect exposing (Effect)
import FatalError exposing (FatalError) import FatalError exposing (FatalError)
import Html exposing (Html) import Html exposing (Html)
import Html.Events
import Pages.Flags import Pages.Flags
import Pages.PageUrl exposing (PageUrl) import Pages.PageUrl exposing (PageUrl)
import Path exposing (Path) import UrlPath exposing (UrlPath)
import Route exposing (Route) import Route exposing (Route)
import SharedTemplate exposing (SharedTemplate) import SharedTemplate exposing (SharedTemplate)
import View exposing (View) import View exposing (View)
@ -19,16 +20,13 @@ template =
, view = view , view = view
, data = data , data = data
, subscriptions = subscriptions , subscriptions = subscriptions
, onPageChange = Just OnPageChange , onPageChange = Nothing
} }
type Msg type Msg
= OnPageChange = SharedMsg SharedMsg
{ path : Path | MenuClicked
, query : Maybe String
, fragment : Maybe String
}
type alias Data = type alias Data =
@ -40,7 +38,7 @@ type SharedMsg
type alias Model = type alias Model =
{ showMobileMenu : Bool { showMenu : Bool
} }
@ -49,7 +47,7 @@ init :
-> ->
Maybe Maybe
{ path : { path :
{ path : Path { path : UrlPath
, query : Maybe String , query : Maybe String
, fragment : Maybe String , fragment : Maybe String
} }
@ -58,7 +56,7 @@ init :
} }
-> ( Model, Effect Msg ) -> ( Model, Effect Msg )
init flags maybePagePath = init flags maybePagePath =
( { showMobileMenu = False } ( { showMenu = False }
, Effect.none , Effect.none
) )
@ -66,11 +64,14 @@ init flags maybePagePath =
update : Msg -> Model -> ( Model, Effect Msg ) update : Msg -> Model -> ( Model, Effect Msg )
update msg model = update msg model =
case msg of case msg of
OnPageChange _ -> SharedMsg globalMsg ->
( { model | showMobileMenu = False }, Effect.none ) ( model, Effect.none )
MenuClicked ->
( { model | showMenu = not model.showMenu }, Effect.none )
subscriptions : Path -> Model -> Sub Msg subscriptions : UrlPath -> Model -> Sub Msg
subscriptions _ _ = subscriptions _ _ =
Sub.none Sub.none
@ -83,16 +84,37 @@ data =
view : view :
Data Data
-> ->
{ path : Path { path : UrlPath
, route : Maybe Route , route : Maybe Route
} }
-> Model -> Model
-> (Msg -> msg) -> (Msg -> msg)
-> View msg -> View msg
-> { body : List (Html msg), title : String } -> { body : List (Html msg), title : String }
view stars page model toMsg pageView = view sharedData page model toMsg pageView =
{ body = { 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 , title = pageView.title
} }

View File

@ -1,16 +1,14 @@
module Site exposing (canonicalUrl, config) module Site exposing (config)
import BackendTask exposing (BackendTask) import BackendTask exposing (BackendTask)
import FatalError exposing (FatalError) import FatalError exposing (FatalError)
import Head import Head
import MimeType
import Pages.Url
import SiteConfig exposing (SiteConfig) import SiteConfig exposing (SiteConfig)
config : SiteConfig config : SiteConfig
config = config =
{ canonicalUrl = canonicalUrl { canonicalUrl = "https://elm-pages.com"
, head = head , head = head
} }
@ -18,15 +16,6 @@ config =
head : BackendTask FatalError (List Head.Tag) head : BackendTask FatalError (List Head.Tag)
head = head =
[ Head.metaName "viewport" (Head.raw "width=device-width,initial-scale=1") [ 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" , Head.sitemapLink "/sitemap.xml"
] ]
|> BackendTask.succeed |> BackendTask.succeed
canonicalUrl : String
canonicalUrl =
"https://elm-pages.com"

View File

@ -1,14 +1,22 @@
module View exposing (View, map) module View exposing (View, map)
{-|
@docs View, map
-}
import Html exposing (Html) import Html exposing (Html)
{-| -}
type alias View msg = type alias View msg =
{ title : String { title : String
, body : List (Html msg) , body : List (Html msg)
} }
{-| -}
map : (msg1 -> msg2) -> View msg1 -> View msg2 map : (msg1 -> msg2) -> View msg1 -> View msg2
map fn doc = map fn doc =
{ title = doc.title { title = doc.title

2
generator/template/codegen/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
Gen/

View File

@ -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/"
]
}
}

View File

@ -0,0 +1,3 @@
export async function hello(name) {
return `Hello ${name}!`;
}

View File

@ -1,5 +1,18 @@
import { defineConfig } from "vite"; import { defineConfig } from "vite";
import adapter from "elm-pages/adapter/netlify.js";
export default { export default {
vite: defineConfig({}), vite: defineConfig({}),
adapter,
headTagsTemplate(context) {
return `
<link rel="stylesheet" href="/style.css" />
<meta name="generator" content="elm-pages v${context.cliVersion}" />
`;
},
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");
},
}; };

View File

@ -1,60 +1,57 @@
{ {
"type": "application", "type": "application",
"source-directories": [ "source-directories": [
"app",
"src", "src",
".elm-pages" "../codegen"
], ],
"elm-version": "0.19.1", "elm-version": "0.19.1",
"dependencies": { "dependencies": {
"direct": { "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", "avh4/elm-color": "1.0.0",
"danfishgold/base64-bytes": "1.1.0", "danfishgold/base64-bytes": "1.1.0",
"danyx23/elm-mimetype": "4.0.1", "danyx23/elm-mimetype": "4.0.1",
"dillonkearns/elm-bcp47-language-tag": "1.0.1", "dillonkearns/elm-bcp47-language-tag": "1.0.1",
"dillonkearns/elm-markdown": "7.0.1", "dillonkearns/elm-date-or-date-time": "2.0.0",
"dillonkearns/elm-pages-v3-beta": "12.0.0", "dillonkearns/elm-form": "3.0.0",
"elm/browser": "1.0.2", "elm/browser": "1.0.2",
"elm/bytes": "1.0.8", "elm/file": "1.0.5",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
"elm/http": "2.0.0", "elm/http": "2.0.0",
"elm/json": "1.1.3",
"elm/parser": "1.1.0", "elm/parser": "1.1.0",
"elm/random": "1.0.0",
"elm/regex": "1.0.0", "elm/regex": "1.0.0",
"elm/time": "1.0.0", "elm/time": "1.0.0",
"elm/url": "1.0.0", "elm/url": "1.0.0",
"elm/virtual-dom": "1.0.3", "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/basics-extra": "4.1.0",
"elm-community/list-extra": "8.7.0",
"elm-community/maybe-extra": "5.3.0", "elm-community/maybe-extra": "5.3.0",
"fredcy/elm-parseint": "2.0.1", "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", "miniBill/elm-unicode": "1.0.3",
"noahzgordon/elm-color-extra": "1.0.2",
"robinheghan/fnv1a": "1.0.0",
"robinheghan/murmur3": "1.0.0", "robinheghan/murmur3": "1.0.0",
"rtfeldman/elm-css": "18.0.0",
"rtfeldman/elm-hex": "1.0.0", "rtfeldman/elm-hex": "1.0.0",
"rtfeldman/elm-iso8601-date-strings": "1.1.4", "rtfeldman/elm-iso8601-date-strings": "1.1.4",
"stil4m/elm-syntax": "7.2.9", "stil4m/elm-syntax": "7.2.9",
"stil4m/structured-writer": "1.0.3", "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": { "test-dependencies": {

View File

@ -1,10 +1,13 @@
[build] [build]
functions = "functions/" functions = "functions/"
publish = "dist/" 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] [dev]
command = "npm start" command = "npm start"
targetPort = 1234 targetPort = 1234
autoLaunch = true autoLaunch = true
framework = "#custom" framework = "#custom"
[functions]
node_bundler = "esbuild"

View File

@ -7,14 +7,14 @@
"build": "elm-pages build" "build": "elm-pages build"
}, },
"devDependencies": { "devDependencies": {
"elm-codegen": "^0.2.0", "elm-codegen": "^0.3.0",
"elm-optimize-level-2": "^0.3.5", "elm-optimize-level-2": "^0.3.5",
"elm-pages": "^3.0.0-beta.20", "elm-pages": "^3.0.0-beta.42",
"elm-review": "2.8.5", "elm-review": "^2.10.2",
"elm-tooling": "^1.10.0", "elm-tooling": "^1.14.0",
"vite": "^3.1.8" "vite": "^4.3.5"
}, },
"dependencies": { "dependencies": {
"@netlify/functions": "^1.2.0" "@netlify/functions": "^1.4.0"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 B

View File

@ -0,0 +1,7 @@
// script/custom-backend-task.ts
async function hello(name) {
return `Hello ${name}!`;
}
export {
hello
};

View File

@ -0,0 +1,3 @@
export async function hello(name) {
return `Hello ${name}!`;
}

View File

@ -0,0 +1,61 @@
{
"type": "application",
"source-directories": [
"src",
"../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-date-or-date-time": "2.0.0",
"dillonkearns/elm-form": "3.0.0",
"elm/browser": "1.0.2",
"elm/file": "1.0.5",
"elm/http": "2.0.0",
"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/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-syntax-dsl": "6.0.2",
"turboMaCk/non-empty-list-alias": "1.3.1",
"vito/elm-ansi": "10.0.1"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

View File

@ -0,0 +1,312 @@
module AddRoute exposing (run)
import BackendTask
import Cli.Option as Option
import Cli.OptionsParser as OptionsParser
import Cli.Program as Program
import Elm
import Elm.Annotation as Type
import Elm.Case
import Elm.Declare
import Elm.Let
import Elm.Op
import Gen.BackendTask
import Gen.Effect as Effect
import Gen.FatalError
import Gen.Form as Form
import Gen.Form.FieldView as FieldView
import Gen.Html as Html
import Gen.Html.Attributes as Attr
import Gen.Json.Encode
import Gen.List
import Gen.Maybe
import Gen.Pages.Form as PagesForm
import Gen.Pages.Script
import Gen.Server.Request as Request
import Gen.Server.Response as Response
import Gen.View
import Pages.Script as Script exposing (Script)
import Scaffold.Form
import Scaffold.Route exposing (Type(..))
type alias CliOptions =
{ moduleName : List String
, fields : List ( String, Scaffold.Form.Kind )
}
run : Script
run =
Script.withCliOptions program
(\cliOptions ->
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" ]
]
)
)

View File

@ -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")
)