elm-pages 3.0.0-beta.9 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/adapter/netlify.js +207 -0
- package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2731 -2939
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1527 -422
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +16840 -13653
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
- package/generator/dead-code-review/elm.json +9 -7
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1527 -422
- package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25118 -21832
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
- package/generator/review/elm.json +10 -10
- package/generator/src/RouteBuilder.elm +93 -128
- package/generator/src/SharedTemplate.elm +8 -7
- package/generator/src/SiteConfig.elm +3 -2
- package/generator/src/basepath-middleware.js +3 -3
- package/generator/src/build.js +143 -59
- package/generator/src/cli.js +292 -88
- package/generator/src/codegen.js +29 -27
- package/generator/src/compatibility-key.js +3 -0
- package/generator/src/compile-elm.js +43 -26
- package/generator/src/config.js +2 -4
- package/generator/src/copy-dir.js +2 -2
- package/generator/src/dev-server.js +160 -102
- package/generator/src/dir-helpers.js +9 -26
- package/generator/src/elm-codegen.js +5 -4
- package/generator/src/elm-file-constants.js +2 -3
- package/generator/src/error-formatter.js +12 -11
- package/generator/src/file-helpers.js +3 -4
- package/generator/src/generate-template-module-connector.js +23 -23
- package/generator/src/init.js +9 -8
- package/generator/src/pre-render-html.js +10 -13
- package/generator/src/render-test.js +109 -0
- package/generator/src/render-worker.js +25 -28
- package/generator/src/render.js +320 -143
- package/generator/src/request-cache.js +265 -162
- package/generator/src/resolve-elm-module.js +64 -0
- package/generator/src/rewrite-client-elm-json.js +6 -5
- package/generator/src/rewrite-elm-json-help.js +56 -0
- package/generator/src/rewrite-elm-json.js +17 -7
- package/generator/src/route-codegen-helpers.js +16 -31
- package/generator/src/seo-renderer.js +1 -3
- package/generator/src/vite-utils.js +1 -2
- package/generator/static-code/elm-pages.js +10 -0
- package/generator/static-code/hmr.js +79 -13
- package/generator/template/app/Api.elm +3 -2
- package/generator/template/app/Effect.elm +155 -0
- package/generator/template/app/ErrorPage.elm +49 -6
- package/generator/template/app/Route/Blog/Slug_.elm +86 -0
- package/generator/template/app/Route/Greet.elm +107 -0
- package/generator/template/app/Route/Hello.elm +119 -0
- package/generator/template/app/Route/Index.elm +26 -25
- package/generator/template/app/Shared.elm +38 -39
- package/generator/template/app/Site.elm +4 -7
- package/generator/template/app/View.elm +9 -8
- package/generator/template/codegen/elm.codegen.json +18 -0
- package/generator/template/custom-backend-task.ts +3 -0
- package/generator/template/elm-pages.config.mjs +13 -0
- package/generator/template/elm-tooling.json +0 -3
- package/generator/template/elm.json +25 -20
- package/generator/template/index.ts +1 -2
- package/generator/template/netlify.toml +4 -1
- package/generator/template/package.json +10 -4
- package/generator/template/script/.elm-pages/compiled-ports/custom-backend-task.mjs +7 -0
- package/generator/template/script/custom-backend-task.ts +3 -0
- package/generator/template/script/elm.json +61 -0
- package/generator/template/script/src/AddRoute.elm +312 -0
- package/generator/template/script/src/Stars.elm +42 -0
- package/package.json +30 -27
- package/src/ApiRoute.elm +249 -82
- package/src/BackendTask/Custom.elm +325 -0
- package/src/BackendTask/Env.elm +90 -0
- package/src/{DataSource → BackendTask}/File.elm +171 -56
- package/src/{DataSource → BackendTask}/Glob.elm +136 -125
- package/src/BackendTask/Http.elm +679 -0
- package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
- package/src/BackendTask/Internal/Request.elm +69 -0
- package/src/BackendTask/Random.elm +79 -0
- package/src/BackendTask/Time.elm +47 -0
- package/src/BackendTask.elm +531 -0
- package/src/FatalError.elm +90 -0
- package/src/FormData.elm +21 -18
- package/src/Head/Seo.elm +4 -4
- package/src/Head.elm +112 -8
- package/src/Internal/ApiRoute.elm +7 -5
- package/src/Internal/Request.elm +84 -4
- package/src/PageServerResponse.elm +6 -1
- package/src/Pages/ConcurrentSubmission.elm +127 -0
- package/src/Pages/Form.elm +340 -0
- package/src/Pages/FormData.elm +19 -0
- package/src/Pages/GeneratorProgramConfig.elm +15 -0
- package/src/Pages/Internal/FatalError.elm +5 -0
- package/src/Pages/Internal/Msg.elm +93 -0
- package/src/Pages/Internal/NotFoundReason.elm +4 -4
- package/src/Pages/Internal/Platform/Cli.elm +586 -768
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
- package/src/Pages/Internal/Platform/Effect.elm +1 -2
- package/src/Pages/Internal/Platform/GeneratorApplication.elm +379 -0
- package/src/Pages/Internal/Platform/StaticResponses.elm +65 -276
- package/src/Pages/Internal/Platform/ToJsPayload.elm +6 -9
- package/src/Pages/Internal/Platform.elm +330 -203
- package/src/Pages/Internal/ResponseSketch.elm +2 -2
- package/src/Pages/Internal/Script.elm +17 -0
- package/src/Pages/Internal/StaticHttpBody.elm +35 -1
- package/src/Pages/Manifest.elm +52 -11
- package/src/Pages/Navigation.elm +85 -0
- package/src/Pages/PageUrl.elm +26 -12
- package/src/Pages/ProgramConfig.elm +32 -22
- package/src/Pages/Script.elm +166 -0
- package/src/Pages/SiteConfig.elm +3 -2
- package/src/Pages/StaticHttp/Request.elm +2 -2
- package/src/Pages/StaticHttpRequest.elm +23 -99
- package/src/Pages/Url.elm +3 -3
- package/src/PagesMsg.elm +88 -0
- package/src/QueryParams.elm +21 -172
- package/src/RenderRequest.elm +7 -7
- package/src/RequestsAndPending.elm +37 -20
- package/src/Result/Extra.elm +26 -0
- package/src/Scaffold/Form.elm +569 -0
- package/src/Scaffold/Route.elm +1431 -0
- package/src/Server/Request.elm +476 -1001
- package/src/Server/Response.elm +130 -36
- package/src/Server/Session.elm +181 -111
- package/src/Server/SetCookie.elm +80 -32
- package/src/Stub.elm +53 -0
- package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
- package/src/{Path.elm → UrlPath.elm} +33 -36
- package/generator/template/public/images/icon-png.png +0 -0
- package/src/DataSource/Env.elm +0 -38
- package/src/DataSource/Http.elm +0 -446
- package/src/DataSource/Internal/Request.elm +0 -20
- package/src/DataSource/Port.elm +0 -90
- package/src/DataSource.elm +0 -546
- package/src/Form/Field.elm +0 -717
- package/src/Form/FieldStatus.elm +0 -36
- package/src/Form/FieldView.elm +0 -417
- package/src/Form/FormData.elm +0 -22
- package/src/Form/Validation.elm +0 -391
- package/src/Form/Value.elm +0 -118
- package/src/Form.elm +0 -1683
- package/src/FormDecoder.elm +0 -102
- package/src/Pages/FormState.elm +0 -256
- package/src/Pages/Generate.elm +0 -1242
- package/src/Pages/Internal/Form.elm +0 -17
- package/src/Pages/Internal/Platform/Cli.elm.bak +0 -1276
- package/src/Pages/Msg.elm +0 -79
- package/src/Pages/Transition.elm +0 -70
package/src/ApiRoute.elm
CHANGED
|
@@ -1,29 +1,166 @@
|
|
|
1
1
|
module ApiRoute exposing
|
|
2
|
-
(
|
|
2
|
+
( single, preRender
|
|
3
|
+
, serverRender
|
|
4
|
+
, preRenderWithFallback
|
|
5
|
+
, ApiRoute, ApiRouteBuilder, Response
|
|
3
6
|
, capture, literal, slash, succeed
|
|
4
|
-
, single, preRender
|
|
5
|
-
, preRenderWithFallback, serverRender
|
|
6
7
|
, withGlobalHeadTags
|
|
7
|
-
, toJson, getBuildTimeRoutes,
|
|
8
|
+
, toJson, getBuildTimeRoutes, getGlobalHeadTagsBackendTask
|
|
8
9
|
)
|
|
9
10
|
|
|
10
|
-
{-| ApiRoute's are defined in `src/Api.elm` and are a way to generate files
|
|
11
|
-
|
|
12
|
-
the DataSource for your ApiRoutes, and it won't effect the payload size. Instead, the size of an ApiRoute is just the content you output for that route.
|
|
11
|
+
{-| ApiRoute's are defined in `src/Api.elm` and are a way to generate files either statically pre-rendered at build-time (like RSS feeds, sitemaps, or any text-based file that you output with an Elm function),
|
|
12
|
+
or server-rendered at runtime (like a JSON API endpoint, or an RSS feed that gives you fresh data without rebuilding your site).
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Your ApiRoute's get access to a [`BackendTask`](BackendTask) so you can pull in HTTP data, etc. Because ApiRoutes don't hydrate into Elm apps (like pages in elm-pages do), you can pull in as much data as you want in
|
|
15
|
+
the BackendTask for your ApiRoutes and it won't effect the payload size. Instead, the size of an ApiRoute is just the content you output for that route.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
Similar to your elm-pages Route Modules, ApiRoute's can be either server-rendered or pre-rendered. Let's compare the differences between pre-rendered and server-rendered ApiRoutes, and the different
|
|
18
|
+
use cases they support.
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
## Pre-Rendering
|
|
20
22
|
|
|
23
|
+
A pre-rendered ApiRoute is just a generated file. For example:
|
|
24
|
+
|
|
25
|
+
- [An RSS feed](https://github.com/dillonkearns/elm-pages/blob/131f7b750cdefb2ba7a34a06be06dfbfafc79a86/examples/docs/app/Api.elm#L77-L84) ([Output file](https://elm-pages.com/blog/feed.xml))
|
|
26
|
+
- [A calendar feed in the ical format](https://github.com/dillonkearns/incrementalelm.com/blob/d4934d899d06232dc66dcf9f4b5eccc74bbc60d3/src/Api.elm#L51-L60) ([Output file](https://incrementalelm.com/live.ics))
|
|
27
|
+
- A redirect file for a hosting provider like Netlify
|
|
28
|
+
|
|
29
|
+
You could even generate a JavaScript file, an Elm file, or any file with a String body! It's really just a way to generate files, which are typically used to serve files to a user or Browser, but you execute them, copy them, etc. The only limit is your imagination!
|
|
30
|
+
The beauty is that you have a way to 1) pull in type-safe data using BackendTask's, and 2) write those files, and all in pure Elm!
|
|
31
|
+
|
|
21
32
|
@docs single, preRender
|
|
22
33
|
|
|
23
34
|
|
|
24
35
|
## Server Rendering
|
|
25
36
|
|
|
26
|
-
|
|
37
|
+
You could use server-rendered ApiRoutes to do a lot of similar things, the main difference being that it will be served up through a URL and generated on-demand when that URL is requested.
|
|
38
|
+
So for example, for an RSS feed or ical calendar feed like in the pre-rendered examples, you could build the same routes, but you would be pulling in the list of posts or calendar events on-demand rather
|
|
39
|
+
than upfront at build-time. That means you can hit your database and serve up always-up-to-date data.
|
|
40
|
+
|
|
41
|
+
Not only that, but your server-rendered ApiRoutes have access to the incoming HTTP request payload just like your server-rendered Route Modules do. Just as with server-rendered Route Modules,
|
|
42
|
+
a server-rendered ApiRoute accesses the incoming HTTP request through a [Server.Request.Parser](Server-Request). Consider the use cases that this opens up:
|
|
43
|
+
|
|
44
|
+
- Serve up protected assets. For example, gated content, like a paid subscriber feed for a podcast that checks authentication information in a query parameter to authenticate that a user has an active paid subscription before serving up the Pro RSS feed.
|
|
45
|
+
- Serve up user-specific content, either through a cookie or other means of authentication
|
|
46
|
+
- Look at the [accepted content-type in the request headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) and use that to choose a response format, like XML or JSON ([full example](https://github.com/dillonkearns/elm-pages/blob/131f7b750cdefb2ba7a34a06be06dfbfafc79a86/examples/end-to-end/app/Api.elm#L76-L107)).
|
|
47
|
+
- Look at the [accepted language in the request headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language) and use that to choose a language for the response data.
|
|
48
|
+
|
|
49
|
+
@docs serverRender
|
|
50
|
+
|
|
51
|
+
You can also do a hybrid approach using `preRenderWithFallback`. This allows you to pre-render a set of routes at build-time, but build additional routes that weren't rendered at build-time on the fly on the server.
|
|
52
|
+
Conceptually, this is just a delayed version of a pre-rendered route. Because of that, you _do not_ have access to the incoming HTTP request (no `Server.Request.Parser` like in server-rendered ApiRoute's).
|
|
53
|
+
The strategy used to build these routes will differ depending on your hosting provider and the elm-pages adapter you have setup, but generally ApiRoute's that use `preRenderWithFallback` will be cached on the server
|
|
54
|
+
so within a certain time interval (or in the case of [Netlify's DPR](https://www.netlify.com/blog/2021/04/14/distributed-persistent-rendering-a-new-jamstack-approach-for-faster-builds/), until a new build is done)
|
|
55
|
+
that asset will be served up if that URL was already served up by the server.
|
|
56
|
+
|
|
57
|
+
@docs preRenderWithFallback
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## Defining ApiRoute's
|
|
61
|
+
|
|
62
|
+
You define your ApiRoute's in `app/Api.elm`. Here's a simple example:
|
|
63
|
+
|
|
64
|
+
module Api exposing (routes)
|
|
65
|
+
|
|
66
|
+
import ApiRoute
|
|
67
|
+
import BackendTask exposing (BackendTask)
|
|
68
|
+
import Server.Request
|
|
69
|
+
|
|
70
|
+
routes :
|
|
71
|
+
BackendTask (List Route)
|
|
72
|
+
-> (Maybe { indent : Int, newLines : Bool } -> Html Never -> String)
|
|
73
|
+
-> List (ApiRoute.ApiRoute ApiRoute.Response)
|
|
74
|
+
routes getStaticRoutes htmlToString =
|
|
75
|
+
[ preRenderedExample
|
|
76
|
+
, requestPrinterExample
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
{-| Generates the following files when you
|
|
80
|
+
run `elm-pages build`:
|
|
81
|
+
|
|
82
|
+
- `dist/users/1.json`
|
|
83
|
+
- `dist/users/2.json`
|
|
84
|
+
- `dist/users/3.json`
|
|
85
|
+
|
|
86
|
+
When you host it, these static assets will
|
|
87
|
+
be served at `/users/1.json`, etc.
|
|
88
|
+
|
|
89
|
+
-}
|
|
90
|
+
preRenderedExample : ApiRoute.ApiRoute ApiRoute.Response
|
|
91
|
+
preRenderedExample =
|
|
92
|
+
ApiRoute.succeed
|
|
93
|
+
(\userId ->
|
|
94
|
+
BackendTask.succeed
|
|
95
|
+
(Json.Encode.object
|
|
96
|
+
[ ( "id", Json.Encode.string userId )
|
|
97
|
+
, ( "name", "Data for user " ++ userId |> Json.Encode.string )
|
|
98
|
+
]
|
|
99
|
+
|> Json.Encode.encode 2
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
|> ApiRoute.literal "users"
|
|
103
|
+
|> ApiRoute.slash
|
|
104
|
+
|> ApiRoute.capture
|
|
105
|
+
|> ApiRoute.literal ".json"
|
|
106
|
+
|> ApiRoute.preRender
|
|
107
|
+
(\route ->
|
|
108
|
+
BackendTask.succeed
|
|
109
|
+
[ route "1"
|
|
110
|
+
, route "2"
|
|
111
|
+
, route "3"
|
|
112
|
+
]
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
{-| This returns a JSON response that prints information about the incoming
|
|
116
|
+
HTTP request. In practice you'd want to do something useful with that data,
|
|
117
|
+
and use more of the high-level helpers from the Server.Request API.
|
|
118
|
+
-}
|
|
119
|
+
requestPrinterExample : ApiRoute ApiRoute.Response
|
|
120
|
+
requestPrinterExample =
|
|
121
|
+
ApiRoute.succeed
|
|
122
|
+
(Server.Request.map4
|
|
123
|
+
(\rawBody method cookies queryParams ->
|
|
124
|
+
Encode.object
|
|
125
|
+
[ ( "rawBody"
|
|
126
|
+
, rawBody
|
|
127
|
+
|> Maybe.map Encode.string
|
|
128
|
+
|> Maybe.withDefault Encode.null
|
|
129
|
+
)
|
|
130
|
+
, ( "method"
|
|
131
|
+
, method
|
|
132
|
+
|> Server.Request.methodToString
|
|
133
|
+
|> Encode.string
|
|
134
|
+
)
|
|
135
|
+
, ( "cookies"
|
|
136
|
+
, cookies
|
|
137
|
+
|> Encode.dict
|
|
138
|
+
identity
|
|
139
|
+
Encode.string
|
|
140
|
+
)
|
|
141
|
+
, ( "queryParams"
|
|
142
|
+
, queryParams
|
|
143
|
+
|> Encode.dict
|
|
144
|
+
identity
|
|
145
|
+
(Encode.list Encode.string)
|
|
146
|
+
)
|
|
147
|
+
]
|
|
148
|
+
|> Response.json
|
|
149
|
+
|> BackendTask.succeed
|
|
150
|
+
)
|
|
151
|
+
Server.Request.rawBody
|
|
152
|
+
Server.Request.method
|
|
153
|
+
Server.Request.allCookies
|
|
154
|
+
Server.Request.queryParams
|
|
155
|
+
)
|
|
156
|
+
|> ApiRoute.literal "api"
|
|
157
|
+
|> ApiRoute.slash
|
|
158
|
+
|> ApiRoute.literal "request-test"
|
|
159
|
+
|> ApiRoute.serverRender
|
|
160
|
+
|
|
161
|
+
@docs ApiRoute, ApiRouteBuilder, Response
|
|
162
|
+
|
|
163
|
+
@docs capture, literal, slash, succeed
|
|
27
164
|
|
|
28
165
|
|
|
29
166
|
## Including Head Tags
|
|
@@ -33,15 +170,15 @@ the DataSource for your ApiRoutes, and it won't effect the payload size. Instead
|
|
|
33
170
|
|
|
34
171
|
## Internals
|
|
35
172
|
|
|
36
|
-
@docs toJson, getBuildTimeRoutes,
|
|
173
|
+
@docs toJson, getBuildTimeRoutes, getGlobalHeadTagsBackendTask
|
|
37
174
|
|
|
38
175
|
-}
|
|
39
176
|
|
|
40
|
-
import
|
|
41
|
-
import
|
|
177
|
+
import BackendTask exposing (BackendTask)
|
|
178
|
+
import FatalError exposing (FatalError)
|
|
42
179
|
import Head
|
|
43
180
|
import Internal.ApiRoute exposing (ApiRoute(..), ApiRouteBuilder(..))
|
|
44
|
-
import
|
|
181
|
+
import Internal.Request
|
|
45
182
|
import Json.Encode
|
|
46
183
|
import Pattern
|
|
47
184
|
import Regex
|
|
@@ -54,52 +191,34 @@ type alias ApiRoute response =
|
|
|
54
191
|
Internal.ApiRoute.ApiRoute response
|
|
55
192
|
|
|
56
193
|
|
|
57
|
-
{-|
|
|
58
|
-
|
|
194
|
+
{-| Same as [`preRender`](#preRender), but for an ApiRoute that has no dynamic segments. This is just a bit simpler because
|
|
195
|
+
since there are no dynamic segments, you don't need to provide a BackendTask with the list of dynamic segments to pre-render because there is only a single possible route.
|
|
196
|
+
-}
|
|
197
|
+
single : ApiRouteBuilder (BackendTask FatalError String) (List String) -> ApiRoute Response
|
|
59
198
|
single handler =
|
|
60
199
|
handler
|
|
61
|
-
|> preRender (\constructor ->
|
|
200
|
+
|> preRender (\constructor -> BackendTask.succeed [ constructor ])
|
|
62
201
|
|
|
63
202
|
|
|
64
203
|
{-| -}
|
|
65
|
-
serverRender : ApiRouteBuilder (Server.Request.
|
|
204
|
+
serverRender : ApiRouteBuilder (Server.Request.Request -> BackendTask FatalError (Server.Response.Response Never Never)) constructor -> ApiRoute Response
|
|
66
205
|
serverRender ((ApiRouteBuilder patterns pattern _ _ _) as fullHandler) =
|
|
67
206
|
ApiRoute
|
|
68
207
|
{ regex = Regex.fromString ("^" ++ pattern ++ "$") |> Maybe.withDefault Regex.never
|
|
69
208
|
, matchesToResponse =
|
|
70
|
-
\path ->
|
|
209
|
+
\serverRequest path ->
|
|
71
210
|
Internal.ApiRoute.tryMatch path fullHandler
|
|
72
211
|
|> Maybe.map
|
|
73
|
-
(\
|
|
74
|
-
|
|
75
|
-
"$$elm-pages$$headers"
|
|
76
|
-
(toDataSource |> Server.Request.getDecoder |> Decode.map Just)
|
|
77
|
-
|> DataSource.andThen
|
|
78
|
-
(\rendered ->
|
|
79
|
-
case rendered of
|
|
80
|
-
Just (Ok okRendered) ->
|
|
81
|
-
okRendered
|
|
82
|
-
|
|
83
|
-
Just (Err errors) ->
|
|
84
|
-
errors
|
|
85
|
-
|> Server.Request.errorsToString
|
|
86
|
-
|> Server.Response.plainText
|
|
87
|
-
|> Server.Response.withStatusCode 400
|
|
88
|
-
|> DataSource.succeed
|
|
89
|
-
|
|
90
|
-
Nothing ->
|
|
91
|
-
Server.Response.plainText "No matching request handler"
|
|
92
|
-
|> Server.Response.withStatusCode 400
|
|
93
|
-
|> DataSource.succeed
|
|
94
|
-
)
|
|
212
|
+
(\toBackendTask ->
|
|
213
|
+
toBackendTask (Internal.Request.toRequest serverRequest)
|
|
95
214
|
)
|
|
96
|
-
|> Maybe.map (
|
|
215
|
+
|> Maybe.map (BackendTask.map (Server.Response.toJson >> Just))
|
|
97
216
|
|> Maybe.withDefault
|
|
98
|
-
(
|
|
99
|
-
, buildTimeRoutes =
|
|
217
|
+
(BackendTask.succeed Nothing)
|
|
218
|
+
, buildTimeRoutes = BackendTask.succeed []
|
|
100
219
|
, handleRoute =
|
|
101
220
|
\path ->
|
|
102
|
-
|
|
221
|
+
BackendTask.succeed
|
|
103
222
|
(case Internal.ApiRoute.tryMatch path fullHandler of
|
|
104
223
|
Just _ ->
|
|
105
224
|
True
|
|
@@ -114,26 +233,26 @@ serverRender ((ApiRouteBuilder patterns pattern _ _ _) as fullHandler) =
|
|
|
114
233
|
|
|
115
234
|
|
|
116
235
|
{-| -}
|
|
117
|
-
preRenderWithFallback : (constructor ->
|
|
236
|
+
preRenderWithFallback : (constructor -> BackendTask FatalError (List (List String))) -> ApiRouteBuilder (BackendTask FatalError (Server.Response.Response Never Never)) constructor -> ApiRoute Response
|
|
118
237
|
preRenderWithFallback buildUrls ((ApiRouteBuilder patterns pattern _ toString constructor) as fullHandler) =
|
|
119
238
|
let
|
|
120
|
-
buildTimeRoutes__ :
|
|
239
|
+
buildTimeRoutes__ : BackendTask FatalError (List String)
|
|
121
240
|
buildTimeRoutes__ =
|
|
122
241
|
buildUrls (constructor [])
|
|
123
|
-
|>
|
|
242
|
+
|> BackendTask.map (List.map toString)
|
|
124
243
|
in
|
|
125
244
|
ApiRoute
|
|
126
245
|
{ regex = Regex.fromString ("^" ++ pattern ++ "$") |> Maybe.withDefault Regex.never
|
|
127
246
|
, matchesToResponse =
|
|
128
|
-
\path ->
|
|
247
|
+
\_ path ->
|
|
129
248
|
Internal.ApiRoute.tryMatch path fullHandler
|
|
130
|
-
|> Maybe.map (
|
|
249
|
+
|> Maybe.map (BackendTask.map (Server.Response.toJson >> Just))
|
|
131
250
|
|> Maybe.withDefault
|
|
132
|
-
(
|
|
251
|
+
(BackendTask.succeed Nothing)
|
|
133
252
|
, buildTimeRoutes = buildTimeRoutes__
|
|
134
253
|
, handleRoute =
|
|
135
254
|
\path ->
|
|
136
|
-
|
|
255
|
+
BackendTask.succeed
|
|
137
256
|
(case Internal.ApiRoute.tryMatch path fullHandler of
|
|
138
257
|
Just _ ->
|
|
139
258
|
True
|
|
@@ -155,43 +274,90 @@ encodeStaticFileBody fileBody =
|
|
|
155
274
|
]
|
|
156
275
|
|
|
157
276
|
|
|
158
|
-
{-| -
|
|
159
|
-
|
|
277
|
+
{-| Pre-render files for a given route pattern statically at build-time. If you only need to serve a single file, you can use [`single`](#single) instead.
|
|
278
|
+
|
|
279
|
+
import ApiRoute
|
|
280
|
+
import BackendTask
|
|
281
|
+
import BackendTask.Http
|
|
282
|
+
import Json.Decode as Decode
|
|
283
|
+
import Json.Encode as Encode
|
|
284
|
+
|
|
285
|
+
starsApi : ApiRoute ApiRoute.Response
|
|
286
|
+
starsApi =
|
|
287
|
+
ApiRoute.succeed
|
|
288
|
+
(\user repoName ->
|
|
289
|
+
BackendTask.Http.getJson
|
|
290
|
+
("https://api.github.com/repos/" ++ user ++ "/" ++ repoName)
|
|
291
|
+
(Decode.field "stargazers_count" Decode.int)
|
|
292
|
+
|> BackendTask.allowFatal
|
|
293
|
+
|> BackendTask.map
|
|
294
|
+
(\stars ->
|
|
295
|
+
Encode.object
|
|
296
|
+
[ ( "repo", Encode.string repoName )
|
|
297
|
+
, ( "stars", Encode.int stars )
|
|
298
|
+
]
|
|
299
|
+
|> Encode.encode 2
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
|> ApiRoute.literal "repo"
|
|
303
|
+
|> ApiRoute.slash
|
|
304
|
+
|> ApiRoute.capture
|
|
305
|
+
|> ApiRoute.slash
|
|
306
|
+
|> ApiRoute.capture
|
|
307
|
+
|> ApiRoute.slash
|
|
308
|
+
|> ApiRoute.literal ".json"
|
|
309
|
+
|> ApiRoute.preRender
|
|
310
|
+
(\route ->
|
|
311
|
+
BackendTask.succeed
|
|
312
|
+
[ route "dillonkearns" "elm-graphql"
|
|
313
|
+
, route "dillonkearns" "elm-pages"
|
|
314
|
+
]
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
You can view these files in the dev server at <http://localhost:1234/repo/dillonkearns/elm-graphql.json>, and when you run `elm-pages build` this will result in the following files being generated:
|
|
318
|
+
|
|
319
|
+
- `dist/repo/dillonkearns/elm-graphql.json`
|
|
320
|
+
- `dist/repo/dillonkearns/elm-pages.json`
|
|
321
|
+
|
|
322
|
+
Note: `dist` is the output folder for `elm-pages build`, so this will be accessible in your hosted site at `/repo/dillonkearns/elm-graphql.json` and `/repo/dillonkearns/elm-pages.json`.
|
|
323
|
+
|
|
324
|
+
-}
|
|
325
|
+
preRender : (constructor -> BackendTask FatalError (List (List String))) -> ApiRouteBuilder (BackendTask FatalError String) constructor -> ApiRoute Response
|
|
160
326
|
preRender buildUrls ((ApiRouteBuilder patterns pattern _ toString constructor) as fullHandler) =
|
|
161
327
|
let
|
|
162
|
-
buildTimeRoutes__ :
|
|
328
|
+
buildTimeRoutes__ : BackendTask FatalError (List String)
|
|
163
329
|
buildTimeRoutes__ =
|
|
164
330
|
buildUrls (constructor [])
|
|
165
|
-
|>
|
|
331
|
+
|> BackendTask.map (List.map toString)
|
|
166
332
|
|
|
167
|
-
preBuiltMatches :
|
|
333
|
+
preBuiltMatches : BackendTask FatalError (List (List String))
|
|
168
334
|
preBuiltMatches =
|
|
169
335
|
buildUrls (constructor [])
|
|
170
336
|
in
|
|
171
337
|
ApiRoute
|
|
172
338
|
{ regex = Regex.fromString ("^" ++ pattern ++ "$") |> Maybe.withDefault Regex.never
|
|
173
339
|
, matchesToResponse =
|
|
174
|
-
\path ->
|
|
340
|
+
\_ path ->
|
|
175
341
|
let
|
|
176
342
|
matches : List String
|
|
177
343
|
matches =
|
|
178
344
|
Internal.ApiRoute.pathToMatches path fullHandler
|
|
179
345
|
|
|
180
|
-
routeFound :
|
|
346
|
+
routeFound : BackendTask FatalError Bool
|
|
181
347
|
routeFound =
|
|
182
348
|
preBuiltMatches
|
|
183
|
-
|>
|
|
349
|
+
|> BackendTask.map (List.member matches)
|
|
184
350
|
in
|
|
185
351
|
routeFound
|
|
186
|
-
|>
|
|
352
|
+
|> BackendTask.andThen
|
|
187
353
|
(\found ->
|
|
188
354
|
if found then
|
|
189
355
|
Internal.ApiRoute.tryMatch path fullHandler
|
|
190
|
-
|> Maybe.map (
|
|
191
|
-
|> Maybe.withDefault (
|
|
356
|
+
|> Maybe.map (BackendTask.map (encodeStaticFileBody >> Just))
|
|
357
|
+
|> Maybe.withDefault (BackendTask.succeed Nothing)
|
|
192
358
|
|
|
193
359
|
else
|
|
194
|
-
|
|
360
|
+
BackendTask.succeed Nothing
|
|
195
361
|
)
|
|
196
362
|
, buildTimeRoutes = buildTimeRoutes__
|
|
197
363
|
, handleRoute =
|
|
@@ -202,24 +368,27 @@ preRender buildUrls ((ApiRouteBuilder patterns pattern _ toString constructor) a
|
|
|
202
368
|
Internal.ApiRoute.pathToMatches path fullHandler
|
|
203
369
|
in
|
|
204
370
|
preBuiltMatches
|
|
205
|
-
|>
|
|
371
|
+
|> BackendTask.map (List.member matches)
|
|
206
372
|
, pattern = patterns
|
|
207
373
|
, kind = "prerender"
|
|
208
374
|
, globalHeadTags = Nothing
|
|
209
375
|
}
|
|
210
376
|
|
|
211
377
|
|
|
212
|
-
{-|
|
|
378
|
+
{-| The intermediary value while building an ApiRoute definition.
|
|
379
|
+
-}
|
|
213
380
|
type alias ApiRouteBuilder a constructor =
|
|
214
381
|
Internal.ApiRoute.ApiRouteBuilder a constructor
|
|
215
382
|
|
|
216
383
|
|
|
217
|
-
{-|
|
|
384
|
+
{-| The final value from defining an ApiRoute.
|
|
385
|
+
-}
|
|
218
386
|
type alias Response =
|
|
219
387
|
Json.Encode.Value
|
|
220
388
|
|
|
221
389
|
|
|
222
|
-
{-|
|
|
390
|
+
{-| Starts the definition of a route with any captured segments.
|
|
391
|
+
-}
|
|
223
392
|
succeed : a -> ApiRouteBuilder a (List String)
|
|
224
393
|
succeed a =
|
|
225
394
|
ApiRouteBuilder Pattern.empty "" (\_ -> a) (\_ -> "") (\list -> list)
|
|
@@ -235,7 +404,8 @@ toJson ((ApiRoute { kind }) as apiRoute) =
|
|
|
235
404
|
]
|
|
236
405
|
|
|
237
406
|
|
|
238
|
-
{-|
|
|
407
|
+
{-| A literal String segment of a route.
|
|
408
|
+
-}
|
|
239
409
|
literal : String -> ApiRouteBuilder a constructor -> ApiRouteBuilder a constructor
|
|
240
410
|
literal segment (ApiRouteBuilder patterns pattern handler toString constructor) =
|
|
241
411
|
ApiRouteBuilder
|
|
@@ -246,13 +416,15 @@ literal segment (ApiRouteBuilder patterns pattern handler toString constructor)
|
|
|
246
416
|
constructor
|
|
247
417
|
|
|
248
418
|
|
|
249
|
-
{-|
|
|
419
|
+
{-| A path separator within the route.
|
|
420
|
+
-}
|
|
250
421
|
slash : ApiRouteBuilder a constructor -> ApiRouteBuilder a constructor
|
|
251
422
|
slash (ApiRouteBuilder patterns pattern handler toString constructor) =
|
|
252
423
|
ApiRouteBuilder (patterns |> Pattern.addSlash) (pattern ++ "/") handler (\arg -> toString arg ++ "/") constructor
|
|
253
424
|
|
|
254
425
|
|
|
255
|
-
{-|
|
|
426
|
+
{-| Captures a dynamic segment from the route.
|
|
427
|
+
-}
|
|
256
428
|
capture :
|
|
257
429
|
ApiRouteBuilder (String -> a) constructor
|
|
258
430
|
-> ApiRouteBuilder a (String -> constructor)
|
|
@@ -284,25 +456,20 @@ capture (ApiRouteBuilder patterns pattern previousHandler toString constructor)
|
|
|
284
456
|
|
|
285
457
|
{-| For internal use by generated code. Not so useful in user-land.
|
|
286
458
|
-}
|
|
287
|
-
getBuildTimeRoutes : ApiRoute response ->
|
|
459
|
+
getBuildTimeRoutes : ApiRoute response -> BackendTask FatalError (List String)
|
|
288
460
|
getBuildTimeRoutes (ApiRoute handler) =
|
|
289
461
|
handler.buildTimeRoutes
|
|
290
462
|
|
|
291
463
|
|
|
292
464
|
{-| Include head tags on every page's HTML.
|
|
293
465
|
-}
|
|
294
|
-
withGlobalHeadTags :
|
|
466
|
+
withGlobalHeadTags : BackendTask FatalError (List Head.Tag) -> ApiRoute response -> ApiRoute response
|
|
295
467
|
withGlobalHeadTags globalHeadTags (ApiRoute handler) =
|
|
296
468
|
ApiRoute { handler | globalHeadTags = Just globalHeadTags }
|
|
297
469
|
|
|
298
470
|
|
|
299
|
-
{-|
|
|
300
|
-
|
|
301
|
-
|
|
471
|
+
{-| For internal use.
|
|
472
|
+
-}
|
|
473
|
+
getGlobalHeadTagsBackendTask : ApiRoute response -> Maybe (BackendTask FatalError (List Head.Tag))
|
|
474
|
+
getGlobalHeadTagsBackendTask (ApiRoute handler) =
|
|
302
475
|
handler.globalHeadTags
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
--captureRest : ApiRouteBuilder (List String -> a) b -> ApiRouteBuilder a b
|
|
307
|
-
--captureRest previousHandler =
|
|
308
|
-
-- Debug.todo ""
|