diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 996abbbd5..b94a3914e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,10 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Use Node.js 14 + - name: Use Node.js 16 uses: actions/setup-node@v1 with: - node-version: 14 + node-version: 16 # Re-use node_modules between runs until package-lock.json changes. - name: Cache node_modules id: cache-node_modules @@ -105,10 +105,10 @@ jobs: runs-on: windows-latest steps: - uses: actions/checkout@v2 - - name: Use Node.js 14 + - name: Use Node.js 16 uses: actions/setup-node@v1 with: - node-version: 14 + node-version: 16 # Re-use node_modules between runs until package-lock.json changes. - name: Cache node_modules id: cache-node_modules diff --git a/examples/base-path/package-lock.json b/examples/base-path/package-lock.json index ade390607..63f302f80 100644 --- a/examples/base-path/package-lock.json +++ b/examples/base-path/package-lock.json @@ -22,7 +22,7 @@ "license": "BSD-3-Clause", "dependencies": { "chokidar": "3.5.2", - "commander": "^8.1.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", "devcert": "^1.2.0", @@ -1553,7 +1553,7 @@ "@types/node": "12.20.12", "@types/serve-static": "1.13.10", "chokidar": "3.5.2", - "commander": "^8.1.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", "cypress": "^8.3.0", diff --git a/examples/base-path/src/Page/Index.elm b/examples/base-path/src/Page/Index.elm index 62394b696..ee2e79195 100644 --- a/examples/base-path/src/Page/Index.elm +++ b/examples/base-path/src/Page/Index.elm @@ -16,7 +16,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = diff --git a/examples/base-path/src/Page/SubPage.elm b/examples/base-path/src/Page/SubPage.elm index 2fcef48ac..e654aca7b 100644 --- a/examples/base-path/src/Page/SubPage.elm +++ b/examples/base-path/src/Page/SubPage.elm @@ -17,7 +17,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = diff --git a/examples/docs/package-lock.json b/examples/docs/package-lock.json index 6090a4818..9a6bd9fcd 100644 --- a/examples/docs/package-lock.json +++ b/examples/docs/package-lock.json @@ -22,14 +22,15 @@ } }, "../..": { - "version": "2.1.9", + "version": "2.1.11", "dev": true, "license": "BSD-3-Clause", "dependencies": { "chokidar": "3.5.2", - "commander": "^8.1.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", + "devcert": "^1.2.0", "elm-doc-preview": "^5.0.5", "elm-hot": "^1.1.6", "fs-extra": "^10.0.0", @@ -1440,10 +1441,11 @@ "@types/node": "12.20.12", "@types/serve-static": "1.13.10", "chokidar": "3.5.2", - "commander": "^8.1.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", "cypress": "^8.3.0", + "devcert": "^1.2.0", "elm-doc-preview": "^5.0.5", "elm-hot": "^1.1.6", "elm-optimize-level-2": "^0.1.5", diff --git a/examples/docs/src/Page/Blog.elm b/examples/docs/src/Page/Blog.elm index bd7165ff8..0fdd34bd0 100644 --- a/examples/docs/src/Page/Blog.elm +++ b/examples/docs/src/Page/Blog.elm @@ -48,7 +48,7 @@ type alias RouteParams = type alias Model = - () + {} view : diff --git a/examples/docs/src/Page/Blog/Slug_.elm b/examples/docs/src/Page/Blog/Slug_.elm index fc6d743f6..7ca01da73 100644 --- a/examples/docs/src/Page/Blog/Slug_.elm +++ b/examples/docs/src/Page/Blog/Slug_.elm @@ -25,7 +25,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = @@ -41,13 +41,13 @@ page = Page.prerender { data = data , head = head - , routes = routes + , pages = pages } |> Page.buildNoState { view = view } -routes : DataSource.DataSource (List RouteParams) -routes = +pages : DataSource.DataSource (List RouteParams) +pages = Article.blogPostsGlob |> DataSource.map (List.map diff --git a/examples/docs/src/Page/Docs/Section__.elm b/examples/docs/src/Page/Docs/Section__.elm index 4d6670ffd..fb4fcdd45 100644 --- a/examples/docs/src/Page/Docs/Section__.elm +++ b/examples/docs/src/Page/Docs/Section__.elm @@ -30,7 +30,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = @@ -45,7 +45,7 @@ page : Page RouteParams Data page = Page.prerender { head = head - , routes = routes + , pages = pages , data = data } |> Page.buildNoState @@ -53,8 +53,8 @@ page = } -routes : DataSource (List RouteParams) -routes = +pages : DataSource (List RouteParams) +pages = DocsSection.all |> DataSource.map (List.map diff --git a/examples/docs/src/Page/Index.elm b/examples/docs/src/Page/Index.elm index 130972359..3ba2925ef 100644 --- a/examples/docs/src/Page/Index.elm +++ b/examples/docs/src/Page/Index.elm @@ -43,7 +43,7 @@ import View.CodeTab as CodeTab type alias Model = - () + {} type alias Msg = @@ -145,13 +145,13 @@ page : Page RouteParams Data page = Page.prerender { head = head - , routes = routes + , pages = pages , data = data } |> Page.buildNoState { view = view } -routes : DataSource (List RouteParams) -routes = +pages : DataSource (List RouteParams) +pages = DataSource.succeed [ { name = "elm-pages" } ] data : RouteParams -> DataSource Data diff --git a/examples/docs/src/Page/Showcase.elm b/examples/docs/src/Page/Showcase.elm index 8f43f0aaa..1bb31653e 100644 --- a/examples/docs/src/Page/Showcase.elm +++ b/examples/docs/src/Page/Showcase.elm @@ -18,7 +18,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = @@ -29,7 +29,7 @@ type alias RouteParams = {} -page : PageWithState RouteParams Data () Msg +page : PageWithState RouteParams Data Model Msg page = Page.single { head = head diff --git a/examples/end-to-end/package-lock.json b/examples/end-to-end/package-lock.json index c872ca1d4..5b115e136 100644 --- a/examples/end-to-end/package-lock.json +++ b/examples/end-to-end/package-lock.json @@ -16,14 +16,15 @@ } }, "../..": { - "version": "2.1.7", + "version": "2.1.11", "dev": true, "license": "BSD-3-Clause", "dependencies": { "chokidar": "3.5.2", - "commander": "8.0.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", + "devcert": "^1.2.0", "elm-doc-preview": "^5.0.5", "elm-hot": "^1.1.6", "fs-extra": "^10.0.0", @@ -34,8 +35,8 @@ "micromatch": "^4.0.4", "object-hash": "^2.2.0", "serve-static": "^1.14.1", - "terser": "5.7.1", - "undici": "4.2.1", + "terser": "^5.7.2", + "undici": "^4.4.7", "which": "^2.0.2" }, "bin": { @@ -44,7 +45,7 @@ "devDependencies": { "@types/cross-spawn": "^6.0.2", "@types/fs-extra": "9.0.12", - "@types/micromatch": "^4.0.1", + "@types/micromatch": "^4.0.2", "@types/node": "12.20.12", "@types/serve-static": "1.13.10", "cypress": "^8.3.0", @@ -54,7 +55,7 @@ "elm-tooling": "^1.3.0", "elm-verify-examples": "^5.0.0", "elmi-to-json": "^1.2.0", - "mocha": "^8.4.0", + "mocha": "^9.1.0", "typescript": "4.3.5" } }, @@ -1489,14 +1490,15 @@ "requires": { "@types/cross-spawn": "^6.0.2", "@types/fs-extra": "9.0.12", - "@types/micromatch": "^4.0.1", + "@types/micromatch": "^4.0.2", "@types/node": "12.20.12", "@types/serve-static": "1.13.10", "chokidar": "3.5.2", - "commander": "8.0.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", "cypress": "^8.3.0", + "devcert": "^1.2.0", "elm-doc-preview": "^5.0.5", "elm-hot": "^1.1.6", "elm-optimize-level-2": "^0.1.5", @@ -1511,12 +1513,12 @@ "jsesc": "^3.0.2", "kleur": "^4.1.4", "micromatch": "^4.0.4", - "mocha": "^8.4.0", + "mocha": "^9.1.0", "object-hash": "^2.2.0", "serve-static": "^1.14.1", - "terser": "5.7.1", + "terser": "^5.7.2", "typescript": "4.3.5", - "undici": "4.2.1", + "undici": "^4.4.7", "which": "^2.0.2" }, "dependencies": { diff --git a/examples/end-to-end/src/Page/FileData.elm b/examples/end-to-end/src/Page/FileData.elm index eaf97119d..1416f84ca 100644 --- a/examples/end-to-end/src/Page/FileData.elm +++ b/examples/end-to-end/src/Page/FileData.elm @@ -14,7 +14,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = diff --git a/examples/end-to-end/src/Page/Index.elm b/examples/end-to-end/src/Page/Index.elm index 97dd0fefa..59fb6125d 100644 --- a/examples/end-to-end/src/Page/Index.elm +++ b/examples/end-to-end/src/Page/Index.elm @@ -13,7 +13,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = diff --git a/examples/end-to-end/src/Page/Links.elm b/examples/end-to-end/src/Page/Links.elm index 82382adf7..de5c51ef9 100644 --- a/examples/end-to-end/src/Page/Links.elm +++ b/examples/end-to-end/src/Page/Links.elm @@ -13,7 +13,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = diff --git a/examples/escaping/package-lock.json b/examples/escaping/package-lock.json index c06ab018c..5b115e136 100644 --- a/examples/escaping/package-lock.json +++ b/examples/escaping/package-lock.json @@ -21,7 +21,7 @@ "license": "BSD-3-Clause", "dependencies": { "chokidar": "3.5.2", - "commander": "^8.1.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", "devcert": "^1.2.0", @@ -1494,7 +1494,7 @@ "@types/node": "12.20.12", "@types/serve-static": "1.13.10", "chokidar": "3.5.2", - "commander": "^8.1.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", "cypress": "^8.3.0", diff --git a/examples/escaping/src/Page/Escaping.elm b/examples/escaping/src/Page/Escaping.elm index 5bf827220..f9eda61bd 100644 --- a/examples/escaping/src/Page/Escaping.elm +++ b/examples/escaping/src/Page/Escaping.elm @@ -17,7 +17,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = diff --git a/examples/escaping/src/Page/Index.elm b/examples/escaping/src/Page/Index.elm index 8032cf638..c8692e97f 100644 --- a/examples/escaping/src/Page/Index.elm +++ b/examples/escaping/src/Page/Index.elm @@ -14,7 +14,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = diff --git a/examples/pokedex/src/Page/PokedexNumber_.elm b/examples/pokedex/src/Page/PokedexNumber_.elm index 9274b5964..3a903e28a 100644 --- a/examples/pokedex/src/Page/PokedexNumber_.elm +++ b/examples/pokedex/src/Page/PokedexNumber_.elm @@ -32,7 +32,7 @@ page : Page RouteParams Data page = Page.prerenderWithFallback { head = head - , routes = routes + , pages = pages , data = data , handleFallback = \{ pokedexNumber } -> @@ -47,8 +47,8 @@ page = |> Page.buildNoState { view = view } -routes : DataSource (List RouteParams) -routes = +pages : DataSource (List RouteParams) +pages = DataSource.succeed [] diff --git a/examples/routing/package-lock.json b/examples/routing/package-lock.json index c06ab018c..5b115e136 100644 --- a/examples/routing/package-lock.json +++ b/examples/routing/package-lock.json @@ -21,7 +21,7 @@ "license": "BSD-3-Clause", "dependencies": { "chokidar": "3.5.2", - "commander": "^8.1.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", "devcert": "^1.2.0", @@ -1494,7 +1494,7 @@ "@types/node": "12.20.12", "@types/serve-static": "1.13.10", "chokidar": "3.5.2", - "commander": "^8.1.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", "cypress": "^8.3.0", diff --git a/examples/routing/src/Page/Cats/Name__.elm b/examples/routing/src/Page/Cats/Name__.elm index 0f7be8fd3..579e93344 100644 --- a/examples/routing/src/Page/Cats/Name__.elm +++ b/examples/routing/src/Page/Cats/Name__.elm @@ -12,7 +12,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = @@ -27,14 +27,14 @@ page : Page RouteParams Data page = Page.prerender { head = head - , routes = routes + , pages = pages , data = data } |> Page.buildNoState { view = view } -routes : DataSource.DataSource (List RouteParams) -routes = +pages : DataSource.DataSource (List RouteParams) +pages = DataSource.succeed [ { name = Just "larry" } @@ -45,7 +45,7 @@ routes = data : RouteParams -> DataSource.DataSource Data data routeParams = - DataSource.succeed () + DataSource.succeed {} head : @@ -69,7 +69,7 @@ head static = type alias Data = - () + {} view : diff --git a/examples/routing/src/Page/Date/SPLAT_.elm b/examples/routing/src/Page/Date/SPLAT_.elm index 0c2de72c4..048323a4a 100644 --- a/examples/routing/src/Page/Date/SPLAT_.elm +++ b/examples/routing/src/Page/Date/SPLAT_.elm @@ -10,7 +10,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = @@ -25,7 +25,7 @@ page : Page RouteParams Data page = Page.prerender { head = head - , routes = routes + , pages = pages , data = data --, routeFound = \_ -> DataSource.succeed True @@ -33,8 +33,8 @@ page = |> Page.buildNoState { view = view } -routes : DataSource.DataSource (List RouteParams) -routes = +pages : DataSource.DataSource (List RouteParams) +pages = DataSource.succeed [ { splat = ( "2021", [ "04", "28" ] ) } @@ -45,7 +45,7 @@ routes = data : RouteParams -> DataSource.DataSource Data data routeParams = - DataSource.succeed () + DataSource.succeed {} head : @@ -56,7 +56,7 @@ head static = type alias Data = - () + {} view : diff --git a/examples/routing/src/Page/SPLAT__.elm b/examples/routing/src/Page/SPLAT__.elm index ffeb5ab68..af8456777 100644 --- a/examples/routing/src/Page/SPLAT__.elm +++ b/examples/routing/src/Page/SPLAT__.elm @@ -10,7 +10,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = @@ -25,7 +25,7 @@ page : Page RouteParams Data page = Page.prerender { head = head - , routes = DataSource.succeed [] + , pages = DataSource.succeed [] , data = data --, routeFound = \_ -> DataSource.succeed True @@ -35,7 +35,7 @@ page = data : RouteParams -> DataSource.DataSource Data data routeParams = - DataSource.succeed () + DataSource.succeed {} head : @@ -46,7 +46,7 @@ head static = type alias Data = - () + {} view : diff --git a/examples/routing/src/Page/Slide.elm b/examples/routing/src/Page/Slide.elm index e33145796..0c28ddd8c 100644 --- a/examples/routing/src/Page/Slide.elm +++ b/examples/routing/src/Page/Slide.elm @@ -11,7 +11,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = @@ -26,7 +26,7 @@ page : Page RouteParams Data page = Page.single { head = head - , data = DataSource.succeed () + , data = DataSource.succeed {} } |> Page.buildNoState { view = view } @@ -52,7 +52,7 @@ head static = type alias Data = - () + {} view : diff --git a/examples/routing/src/Page/Slide/Number_.elm b/examples/routing/src/Page/Slide/Number_.elm index 7c76c8f45..1216e658b 100644 --- a/examples/routing/src/Page/Slide/Number_.elm +++ b/examples/routing/src/Page/Slide/Number_.elm @@ -38,7 +38,7 @@ page : Page.PageWithState RouteParams Data Model Msg page = Page.prerender { head = head - , routes = + , pages = slideCount |> DataSource.map (\count -> @@ -87,7 +87,7 @@ page = _ -> ( model, Cmd.none ) , subscriptions = - \maybePageUrl routeParams path model -> + \maybePageUrl routeParams path sharedModel model -> Browser.Events.onKeyDown keyDecoder |> Sub.map OnKeyPress } diff --git a/examples/slides/src/Page/Cats/Name__.elm b/examples/slides/src/Page/Cats/Name__.elm index 5cfe40ed7..79ac47b34 100644 --- a/examples/slides/src/Page/Cats/Name__.elm +++ b/examples/slides/src/Page/Cats/Name__.elm @@ -26,14 +26,14 @@ page : Page RouteParams Data page = Page.prerender { head = head - , routes = routes + , pages = pages , data = data } |> Page.buildNoState { view = view } -routes : DataSource.DataSource (List RouteParams) -routes = +pages : DataSource.DataSource (List RouteParams) +pages = DataSource.succeed [ { name = Just "larry" } diff --git a/examples/slides/src/Page/Slide/Number_.elm b/examples/slides/src/Page/Slide/Number_.elm index 184b0a001..86cbc737b 100644 --- a/examples/slides/src/Page/Slide/Number_.elm +++ b/examples/slides/src/Page/Slide/Number_.elm @@ -36,7 +36,7 @@ page : Page.PageWithState RouteParams Data Model Msg page = Page.prerender { head = head - , routes = + , pages = slideCount |> DataSource.map (\count -> diff --git a/generator/src/Page.elm b/generator/src/Page.elm index a8fe59481..8d134b7af 100644 --- a/generator/src/Page.elm +++ b/generator/src/Page.elm @@ -1,47 +1,79 @@ module Page exposing - ( Builder(..) + ( Page, buildNoState , StaticPayload + , buildWithLocalState, buildWithSharedState , prerender, single - , Page, buildNoState - , PageWithState, buildWithLocalState, buildWithSharedState + , Builder(..) + , PageWithState --, serverless, prerenderWithFallback ) {-| -## Building a Page Module +## Stateless Page Modules -@docs Builder +The simplest Page Module you can build is one with no state. It still can use `DataSource`'s, but it has no `init`, `update`, or `subscriptions`. +It can read the `Shared.Model`, but it cannot send `Shared.Msg`'s to update the `Shared.Model`. If you need a `Model`, use `buildWithLocalState`. -## Static Data +If you need to _change_ Shared state, use `buildWithSharedState`. -Every template will have access to a `StaticPayload`. +@docs Page, buildNoState -@docs StaticPayload -Since this data is _static_, you have access to it before the user has loaded the page, including at build time. +## Accessing Static Data + +With `elm-pages`, you can have HTTP data available before a page is loaded, or read in a file, etc, using the DataSource API. Since the data +is available when the page is pre-rendered (as well as in the hydrated page), this is called Static Data. + An example of dynamic data would be keyboard input from the user, query params, or any other data that comes from the app running in the browser. -But before the user even requests the page, we have the following data: +We have the following data during pre-render: - - `path` - these paths are static. In other words, we know every single path when we build an elm-pages site. - - `metadata` - we have a decoded Elm value for the page's metadata. - - `sharedStatic` - we can access any shared data between pages. For example, you may have fetched the name of a blog ("Jane's Blog") from the API for a Content Management System (CMS). - - `static` - this is the static data for this specific page. If you use `noData`, then this will be `()`, meaning there is no page-specific static data. + - `path` - the current path is static. In other words, we know the current path when we build an elm-pages site. Note that we **do not** know query parameters, fragments, etc. That is dynamic data. Pre-rendering occurs for paths in our app, but we don't know what possible query paremters might be used when those paths are hit. + - `data` - this will be the resolved `DataSource` for our page. + - `sharedData` - we can access any shared data between pages. For example, you may have fetched the name of a blog ("Jane's Blog") from the API for a Content Management System (CMS). + - `routeParams` - this is the record that includes any Dynamic Route Segments for the given page (or an empty record if there are none) -@docs prerender, single +@docs StaticPayload -## Stateless Page Modules +## Stateful Page Modules -@docs Page, buildNoState +@docs buildWithLocalState, buildWithSharedState -## Stateful Page Modules +## Pre-Rendered Pages + +A `single` page is just a Route that has no Dynamic Route Segments. For example, `Page.About` will have `type alias RouteParams = {}`, whereas `Page.Blog.Slug_` has a Dynamic Segment slug, and `type alias RouteParams = { slug : String }`. + +When you run `elm-pages add About`, it will use `Page.single { ... }` because it has empty `RouteParams`. When you run `elm-pages add Blog.Slug_`, will will use `Page.prerender` because it has a Dynamic Route Segment. + +So `Page.single` is just a simplified version of `Page.prerender`. If there are no Dynamic Route Segments, then you don't need to define which pages to render so `Page.single` doesn't need a `pages` field. -@docs PageWithState, buildWithLocalState, buildWithSharedState +When there are Dynamic Route Segments, you need to tell `elm-pages` which pages to render. For example: + + page = + Page.prerender + { data = data + , pages = pages + , head = head + } + + pages = + DataSource.succeed + [ { slug = "blog-post1" } + , { slug = "blog-post2" } + ] + +@docs prerender, single + + +## Internals + +@docs Builder +@docs PageWithState -} @@ -59,21 +91,21 @@ import View exposing (View) {-| -} -type alias PageWithState routeParams templateData templateModel templateMsg = - { data : routeParams -> DataSource templateData +type alias PageWithState routeParams data model msg = + { data : routeParams -> DataSource data , staticRoutes : DataSource (List routeParams) , view : Maybe PageUrl -> Shared.Model - -> templateModel - -> StaticPayload templateData routeParams - -> View templateMsg + -> model + -> StaticPayload data routeParams + -> View msg , head : - StaticPayload templateData routeParams + StaticPayload data routeParams -> List Head.Tag - , init : Maybe PageUrl -> Shared.Model -> StaticPayload templateData routeParams -> ( templateModel, Cmd templateMsg ) - , update : PageUrl -> StaticPayload templateData routeParams -> Maybe Browser.Navigation.Key -> templateMsg -> templateModel -> Shared.Model -> ( templateModel, Cmd templateMsg, Maybe Shared.Msg ) - , subscriptions : Maybe PageUrl -> routeParams -> Path -> templateModel -> Shared.Model -> Sub templateMsg + , init : Maybe PageUrl -> Shared.Model -> StaticPayload data routeParams -> ( model, Cmd msg ) + , update : PageUrl -> StaticPayload data routeParams -> Maybe Browser.Navigation.Key -> msg -> model -> Shared.Model -> ( model, Cmd msg, Maybe Shared.Msg ) + , subscriptions : Maybe PageUrl -> routeParams -> Path -> model -> Shared.Model -> Sub msg , handleRoute : { moduleName : List String, routePattern : RoutePattern } -> (routeParams -> List ( String, String )) -> routeParams -> DataSource (Maybe NotFoundReason) , kind : String } @@ -81,7 +113,7 @@ type alias PageWithState routeParams templateData templateModel templateMsg = {-| -} type alias Page routeParams data = - PageWithState routeParams data () Never + PageWithState routeParams data {} Never {-| -} @@ -94,12 +126,12 @@ type alias StaticPayload data routeParams = {-| -} -type Builder routeParams templateData +type Builder routeParams data = WithData - { data : routeParams -> DataSource templateData + { data : routeParams -> DataSource data , staticRoutes : DataSource (List routeParams) , head : - StaticPayload templateData routeParams + StaticPayload data routeParams -> List Head.Tag , serverless : Bool , handleRoute : @@ -116,11 +148,11 @@ buildNoState : { view : Maybe PageUrl -> Shared.Model - -> StaticPayload templateData routeParams + -> StaticPayload data routeParams -> View Never } - -> Builder routeParams templateData - -> PageWithState routeParams templateData () Never + -> Builder routeParams data + -> PageWithState routeParams data {} Never buildNoState { view } builderState = case builderState of WithData record -> @@ -128,8 +160,8 @@ buildNoState { view } builderState = , head = record.head , data = record.data , staticRoutes = record.staticRoutes - , init = \_ _ _ -> ( (), Cmd.none ) - , update = \_ _ _ _ _ _ -> ( (), Cmd.none, Nothing ) + , init = \_ _ _ -> ( {}, Cmd.none ) + , update = \_ _ _ _ _ _ -> ( {}, Cmd.none, Nothing ) , subscriptions = \_ _ _ _ _ -> Sub.none , handleRoute = record.handleRoute , kind = record.kind @@ -141,15 +173,15 @@ buildWithLocalState : { view : Maybe PageUrl -> Shared.Model - -> templateModel - -> StaticPayload templateData routeParams - -> View templateMsg - , init : Maybe PageUrl -> Shared.Model -> StaticPayload templateData routeParams -> ( templateModel, Cmd templateMsg ) - , update : PageUrl -> Maybe Browser.Navigation.Key -> Shared.Model -> StaticPayload templateData routeParams -> templateMsg -> templateModel -> ( templateModel, Cmd templateMsg ) - , subscriptions : Maybe PageUrl -> routeParams -> Path -> templateModel -> Sub templateMsg + -> model + -> StaticPayload data routeParams + -> View msg + , init : Maybe PageUrl -> Shared.Model -> StaticPayload data routeParams -> ( model, Cmd msg ) + , update : PageUrl -> Maybe Browser.Navigation.Key -> Shared.Model -> StaticPayload data routeParams -> msg -> model -> ( model, Cmd msg ) + , subscriptions : Maybe PageUrl -> routeParams -> Path -> Shared.Model -> model -> Sub msg } - -> Builder routeParams templateData - -> PageWithState routeParams templateData templateModel templateMsg + -> Builder routeParams data + -> PageWithState routeParams data model msg buildWithLocalState config builderState = case builderState of WithData record -> @@ -161,7 +193,7 @@ buildWithLocalState config builderState = , staticRoutes = record.staticRoutes , init = config.init , update = - \pageUrl staticPayload navigationKey msg templateModel sharedModel -> + \pageUrl staticPayload navigationKey msg model sharedModel -> let ( updatedModel, cmd ) = config.update @@ -170,12 +202,12 @@ buildWithLocalState config builderState = sharedModel staticPayload msg - templateModel + model in ( updatedModel, cmd, Nothing ) , subscriptions = - \maybePageUrl routeParams path templateModel sharedModel -> - config.subscriptions maybePageUrl routeParams path templateModel + \maybePageUrl routeParams path model sharedModel -> + config.subscriptions maybePageUrl routeParams path sharedModel model , handleRoute = record.handleRoute , kind = record.kind } @@ -186,15 +218,15 @@ buildWithSharedState : { view : Maybe PageUrl -> Shared.Model - -> templateModel - -> StaticPayload templateData routeParams - -> View templateMsg - , init : Maybe PageUrl -> Shared.Model -> StaticPayload templateData routeParams -> ( templateModel, Cmd templateMsg ) - , update : PageUrl -> Maybe Browser.Navigation.Key -> Shared.Model -> StaticPayload templateData routeParams -> templateMsg -> templateModel -> ( templateModel, Cmd templateMsg, Maybe Shared.Msg ) - , subscriptions : Maybe PageUrl -> routeParams -> Path -> templateModel -> Shared.Model -> Sub templateMsg + -> model + -> StaticPayload data routeParams + -> View msg + , init : Maybe PageUrl -> Shared.Model -> StaticPayload data routeParams -> ( model, Cmd msg ) + , update : PageUrl -> Maybe Browser.Navigation.Key -> Shared.Model -> StaticPayload data routeParams -> msg -> model -> ( model, Cmd msg, Maybe Shared.Msg ) + , subscriptions : Maybe PageUrl -> routeParams -> Path -> Shared.Model -> model -> Sub msg } - -> Builder routeParams templateData - -> PageWithState routeParams templateData templateModel templateMsg + -> Builder routeParams data + -> PageWithState routeParams data model msg buildWithSharedState config builderState = case builderState of WithData record -> @@ -204,14 +236,16 @@ buildWithSharedState config builderState = , staticRoutes = record.staticRoutes , init = config.init , update = - \pageUrl staticPayload navigationKey msg templateModel sharedModel -> + \pageUrl staticPayload navigationKey msg model sharedModel -> config.update pageUrl navigationKey sharedModel staticPayload msg - templateModel - , subscriptions = config.subscriptions + model + , subscriptions = + \maybePageUrl routeParams path model sharedModel -> + config.subscriptions maybePageUrl routeParams path sharedModel model , handleRoute = record.handleRoute , kind = record.kind } @@ -237,19 +271,19 @@ single { data, head } = {-| -} prerender : { data : routeParams -> DataSource data - , routes : DataSource (List routeParams) + , pages : DataSource (List routeParams) , head : StaticPayload data routeParams -> List Head.Tag } -> Builder routeParams data -prerender { data, head, routes } = +prerender { data, head, pages } = WithData { data = data - , staticRoutes = routes + , staticRoutes = pages , head = head , serverless = False , handleRoute = \moduleContext toRecord routeParams -> - routes + pages |> DataSource.map (\allRoutes -> if allRoutes |> List.member routeParams then @@ -275,15 +309,15 @@ prerender { data, head, routes } = --{-| -} --prerenderWithFallback : -- { data : routeParams -> DataSource data --- , routes : DataSource (List routeParams) +-- , pages : DataSource (List routeParams) -- , handleFallback : routeParams -> DataSource Bool -- , head : StaticPayload data routeParams -> List Head.Tag -- } -- -> Builder routeParams data ---prerenderWithFallback { data, head, routes, handleFallback } = +--prerenderWithFallback { data, head, pages, handleFallback } = -- WithData -- { data = data --- , staticRoutes = routes +-- , staticRoutes = pages -- , head = head -- , serverless = False -- , handleRoute = @@ -301,7 +335,7 @@ prerender { data, head, routes } = -- -- between on-demand builders and the dev server -- -- we only need to match the pre-rendered routes in the dev server, -- -- not in on-demand builders --- routes +-- pages -- |> DataSource.map -- (\allRoutes -> -- if allRoutes |> List.member routeParams then diff --git a/generator/src/cli.js b/generator/src/cli.js index 74df96f0f..b176c7e47 100755 --- a/generator/src/cli.js +++ b/generator/src/cli.js @@ -9,6 +9,7 @@ const fs = require("fs"); const path = require("path"); const commander = require("commander"); +const Argument = commander.Argument; const packageVersion = require("../../package.json").version; @@ -58,9 +59,14 @@ async function main() { program .command("add ") + .addArgument( + new Argument("", "Generate Page Module with state") + .choices(["local", "shared"]) + .argOptional() + ) .description("create a new Page module") - .action(async (moduleName) => { - await generate.run({ moduleName }); + .action(async (moduleName, state) => { + await generate.run({ moduleName, withState: state }); }); program diff --git a/generator/src/codegen-template-module.js b/generator/src/codegen-template-module.js index 232df1004..e4deaf0e6 100755 --- a/generator/src/codegen-template-module.js +++ b/generator/src/codegen-template-module.js @@ -2,12 +2,12 @@ const fs = require("./dir-helpers.js"); const path = require("path"); const routeHelpers = require("./route-codegen-helpers"); -async function run({ moduleName }) { +async function run({ moduleName, withState }) { if (!moduleName.match(/[A-Z][A-Za-z0-9]+(\.[A-Z][A-Za-z0-9])*/)) { console.error("Invalid module name."); process.exit(1); } - const content = fileContent(moduleName); + const content = fileContent(moduleName, withState); const fullFilePath = path.join( `src/Page/`, moduleName.replace(/\./g, "/") + ".elm" @@ -18,19 +18,25 @@ async function run({ moduleName }) { /** * @param {string} pageModuleName + * @param {'local' | 'shared' | null} withState */ -function fileContent(pageModuleName) { - return routeHelpers.routeParams(pageModuleName.split(".")).length > 0 - ? fileContentWithParams(pageModuleName) - : fileContentWithoutParams(pageModuleName); +function fileContent(pageModuleName, withState) { + return fileContentWithParams( + pageModuleName, + routeHelpers.routeParams(pageModuleName.split(".")).length > 0, + withState + ); } /** * @param {string} pageModuleName + * @param {boolean} withParams + * @param {'local' | 'shared' | null} withState */ -function fileContentWithParams(pageModuleName) { +function fileContentWithParams(pageModuleName, withParams, withState) { return `module Page.${pageModuleName} exposing (Model, Msg, Data, page) +${withState ? "\nimport Browser.Navigation" : ""} import DataSource exposing (DataSource) import Head import Head.Seo as Seo @@ -38,117 +44,113 @@ import Page exposing (Page, PageWithState, StaticPayload) import Pages.PageUrl exposing (PageUrl) import Pages.Url import Shared +${withState ? "import Path exposing (Path)\n" : ""} import View exposing (View) type alias Model = - () + ${withState ? "{}" : "{}"} -type alias Msg = - Never +${ + withState + ? `type Msg + = NoOp` + : `type alias Msg = + Never` +} type alias RouteParams = ${routeHelpers.paramsRecord(pageModuleName.split("."))} -page : Page RouteParams Data +page : ${ + withState + ? "PageWithState RouteParams Data Model Msg" + : "Page RouteParams Data" + } page = - Page.prerender + ${ + withParams + ? `Page.prerender { head = head - , routes = routes + , pages = pages , data = data + }` + : `Page.single + { head = head + , data = data + }` + } + |> ${ + withState + ? `Page.buildWithLocalState + { view = view + , update = update + , subscriptions = subscriptions + , init = init + }` + : `Page.buildNoState { view = view }` } - |> Page.buildNoState { view = view } - - -routes : DataSource (List RouteParams) -routes = - DataSource.succeed [] - - -data : RouteParams -> DataSource Data -data routeParams = - DataSource.succeed () - - - -head : - StaticPayload Data RouteParams - -> List Head.Tag -head static = - 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 - - -type alias Data = - () - -view : +${ + withState + ? ` +init : Maybe PageUrl -> Shared.Model -> StaticPayload Data RouteParams - -> View Msg -view maybeUrl sharedModel static = - View.placeholder "${pageModuleName}" -`; -} + -> ( Model, Cmd Msg ) +init maybePageUrl sharedModel static = + ( {}, Cmd.none ) -/** - * @param {string} pageModuleName - */ -function fileContentWithoutParams(pageModuleName) { - return `module Page.${pageModuleName} exposing (Model, Msg, Data, page) - -import DataSource exposing (DataSource) -import Head -import Head.Seo as Seo -import Page exposing (Page, PageWithState, StaticPayload) -import Pages.PageUrl exposing (PageUrl) -import Pages.Url -import Shared -import View exposing (View) - - -type alias Model = - () - - -type alias Msg = - Never - -type alias RouteParams = - {} -page : Page RouteParams Data -page = - Page.single - { head = head - , data = data - } - |> Page.buildNoState { view = view } +update : + PageUrl + -> Maybe Browser.Navigation.Key + -> Shared.Model + -> StaticPayload Data RouteParams + -> Msg + -> Model + -> ${ + withState === "local" + ? "( Model, Cmd Msg )" + : "( Model, Cmd Msg, Maybe Shared.Msg )" + } +update pageUrl maybeNavigationKey sharedModel static msg model = + case msg of + NoOp -> + ${ + withState === "local" + ? "( model, Cmd.none )" + : "( model, Cmd.none, Nothing )" + } -type alias Data = - () +subscriptions : Maybe PageUrl -> RouteParams -> Path -> Model -> Sub Msg +subscriptions maybePageUrl routeParams path model = + Sub.none +` + : "" +} +${ + withParams + ? `pages : DataSource (List RouteParams) +pages = + DataSource.succeed [] -data : DataSource Data -data = - DataSource.succeed () +` + : "" +} +${ + withParams + ? `data : RouteParams -> DataSource Data +data routeParams =` + : `data : DataSource Data +data =` +} + DataSource.succeed {} head : StaticPayload Data RouteParams @@ -170,12 +172,27 @@ head static = |> Seo.website -view : +type alias Data = + {} + + +${ + withState + ? `view : + Maybe PageUrl + -> Shared.Model + -> templateModel + -> StaticPayload templateData routeParams + -> View templateMsg +view maybeUrl sharedModel model static = +` + : `view : Maybe PageUrl -> Shared.Model -> StaticPayload Data RouteParams -> View Msg -view maybeUrl sharedModel static = +view maybeUrl sharedModel static =` +} View.placeholder "${pageModuleName}" `; } diff --git a/generator/template/public/style.css b/generator/template/public/style.css index e69de29bb..6790161ed 100644 --- a/generator/template/public/style.css +++ b/generator/template/public/style.css @@ -0,0 +1,4 @@ +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, + sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; +} diff --git a/generator/template/src/Page/Index.elm b/generator/template/src/Page/Index.elm index 80ac7a5fb..661d90b5c 100644 --- a/generator/template/src/Page/Index.elm +++ b/generator/template/src/Page/Index.elm @@ -3,6 +3,8 @@ module Page.Index exposing (Data, Model, Msg, page) import DataSource exposing (DataSource) import Head import Head.Seo as Seo +import Html +import Html.Attributes as Attr import Page exposing (Page, StaticPayload) import Pages.PageUrl exposing (PageUrl) import Pages.Url @@ -11,7 +13,7 @@ import View exposing (View) type alias Model = - () + {} type alias Msg = @@ -22,6 +24,10 @@ type alias RouteParams = {} +type alias Data = + {} + + page : Page RouteParams Data page = Page.single @@ -33,7 +39,7 @@ page = data : DataSource Data data = - DataSource.succeed () + DataSource.succeed {} head : @@ -51,19 +57,30 @@ head static = } , description = "TODO" , locale = Nothing - , title = "TODO title" -- metadata.title -- TODO + , title = "elm-pages is running" } |> Seo.website -type alias Data = - () - - view : Maybe PageUrl -> Shared.Model -> StaticPayload Data RouteParams -> View Msg view maybeUrl sharedModel static = - View.placeholder "Index" + { 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" ] + ] + ] + ] + } diff --git a/package-lock.json b/package-lock.json index 33a45a9b2..d3d906c17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "license": "BSD-3-Clause", "dependencies": { "chokidar": "3.5.2", - "commander": "^8.1.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", "devcert": "^1.2.0", @@ -1149,9 +1149,9 @@ "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" }, "node_modules/commander": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.1.0.tgz", - "integrity": "sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "engines": { "node": ">= 12" } @@ -6965,9 +6965,9 @@ "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" }, "commander": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.1.0.tgz", - "integrity": "sha512-mf45ldcuHSYShkplHHGKWb4TrmwQadxOn7v4WuhDJy0ZVoY5JFajaRDKD0PNe5qXzBX0rhovjTnP6Kz9LETcuA==" + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" }, "common-tags": { "version": "1.8.0", diff --git a/package.json b/package.json index 9cbcd43da..53ac3d467 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "license": "BSD-3-Clause", "dependencies": { "chokidar": "3.5.2", - "commander": "^8.1.0", + "commander": "8.3.0", "connect": "^3.7.0", "cross-spawn": "7.0.3", "devcert": "^1.2.0",