elm-pages 3.0.0-beta.9 → 3.0.1

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.
Files changed (208) hide show
  1. package/README.md +2 -2
  2. package/adapter/netlify.js +207 -0
  3. package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2730 -2938
  4. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/dependencies.c59ecaf7fa8379e3a2d0f793fe9784e3060cb64a6d1fe22b8f6a054502021dbe.json +1 -0
  5. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Console-Text.elmi +0 -0
  6. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Console-Text.elmo +0 -0
  7. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  8. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
  9. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmi +0 -0
  10. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
  11. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Generated-Main.elmi +0 -0
  12. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Generated-Main.elmo +0 -0
  13. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Console-Format-Color.elmi +0 -0
  14. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Console-Format-Color.elmo +0 -0
  15. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Console-Format-Monochrome.elmi +0 -0
  16. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Console-Format-Monochrome.elmo +0 -0
  17. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Console-Format.elmi +0 -0
  18. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Console-Format.elmo +0 -0
  19. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Console.elmi +0 -0
  20. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Console.elmo +0 -0
  21. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Highlightable.elmi +0 -0
  22. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Highlightable.elmo +0 -0
  23. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-JUnit.elmi +0 -0
  24. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-JUnit.elmo +0 -0
  25. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Json.elmi +0 -0
  26. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Json.elmo +0 -0
  27. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Reporter.elmi +0 -0
  28. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-Reporter.elmo +0 -0
  29. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-TestResults.elmi +0 -0
  30. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Reporter-TestResults.elmo +0 -0
  31. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Runner-JsMessage.elmi +0 -0
  32. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Runner-JsMessage.elmo +0 -0
  33. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Runner-Node-Vendor-Console.elmi +0 -0
  34. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Runner-Node-Vendor-Console.elmo +0 -0
  35. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Runner-Node-Vendor-Diff.elmi +0 -0
  36. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Runner-Node-Vendor-Diff.elmo +0 -0
  37. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Runner-Node.elmi +0 -0
  38. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/Test-Runner-Node.elmo +0 -0
  39. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/d.dat +0 -0
  40. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/i.dat +0 -0
  41. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/lock +0 -0
  42. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm-stuff/0.19.1/o.dat +0 -0
  43. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elm.json +38 -0
  44. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/elmTestOutput.js +30883 -0
  45. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/package.json +1 -0
  46. package/generator/dead-code-review/elm-stuff/generated-code/elm-community/elm-test/0.19.1-revision9/src/Test/Generated/Main.elm +27 -0
  47. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  48. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
  49. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
  50. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  51. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  52. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  53. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
  54. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1527 -422
  55. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +17042 -13855
  56. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  57. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +2 -2
  58. package/generator/dead-code-review/elm.json +9 -7
  59. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +64 -13
  60. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +66 -50
  61. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
  62. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
  63. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
  64. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
  65. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  66. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  67. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  68. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
  69. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1527 -422
  70. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25118 -21832
  71. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  72. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +2 -2
  73. package/generator/review/elm.json +10 -10
  74. package/generator/src/RouteBuilder.elm +93 -128
  75. package/generator/src/SharedTemplate.elm +8 -7
  76. package/generator/src/SiteConfig.elm +3 -2
  77. package/generator/src/basepath-middleware.js +3 -3
  78. package/generator/src/build.js +143 -59
  79. package/generator/src/cli.js +292 -88
  80. package/generator/src/codegen.js +29 -27
  81. package/generator/src/compatibility-key.js +3 -0
  82. package/generator/src/compile-elm.js +43 -26
  83. package/generator/src/config.js +2 -4
  84. package/generator/src/copy-dir.js +2 -2
  85. package/generator/src/dev-server.js +160 -102
  86. package/generator/src/dir-helpers.js +9 -26
  87. package/generator/src/elm-codegen.js +5 -4
  88. package/generator/src/elm-file-constants.js +2 -3
  89. package/generator/src/error-formatter.js +12 -11
  90. package/generator/src/file-helpers.js +3 -4
  91. package/generator/src/generate-template-module-connector.js +23 -23
  92. package/generator/src/init.js +9 -8
  93. package/generator/src/pre-render-html.js +10 -13
  94. package/generator/src/render-test.js +109 -0
  95. package/generator/src/render-worker.js +25 -28
  96. package/generator/src/render.js +320 -143
  97. package/generator/src/request-cache.js +265 -162
  98. package/generator/src/resolve-elm-module.js +64 -0
  99. package/generator/src/rewrite-client-elm-json.js +6 -5
  100. package/generator/src/rewrite-elm-json-help.js +56 -0
  101. package/generator/src/rewrite-elm-json.js +17 -7
  102. package/generator/src/route-codegen-helpers.js +16 -31
  103. package/generator/src/seo-renderer.js +1 -3
  104. package/generator/src/vite-utils.js +1 -2
  105. package/generator/static-code/elm-pages.js +10 -0
  106. package/generator/static-code/hmr.js +79 -13
  107. package/generator/template/app/Api.elm +3 -2
  108. package/generator/template/app/Effect.elm +155 -0
  109. package/generator/template/app/ErrorPage.elm +49 -6
  110. package/generator/template/app/Route/Blog/Slug_.elm +86 -0
  111. package/generator/template/app/Route/Greet.elm +107 -0
  112. package/generator/template/app/Route/Hello.elm +119 -0
  113. package/generator/template/app/Route/Index.elm +26 -25
  114. package/generator/template/app/Shared.elm +38 -39
  115. package/generator/template/app/Site.elm +4 -7
  116. package/generator/template/app/View.elm +9 -8
  117. package/generator/template/codegen/elm.codegen.json +18 -0
  118. package/generator/template/custom-backend-task.ts +3 -0
  119. package/generator/template/elm-pages.config.mjs +13 -0
  120. package/generator/template/elm-tooling.json +0 -3
  121. package/generator/template/elm.json +34 -25
  122. package/generator/template/index.ts +1 -2
  123. package/generator/template/netlify.toml +4 -1
  124. package/generator/template/package.json +10 -4
  125. package/generator/template/script/.elm-pages/compiled-ports/custom-backend-task.mjs +7 -0
  126. package/generator/template/script/custom-backend-task.ts +3 -0
  127. package/generator/template/script/elm.json +61 -0
  128. package/generator/template/script/src/AddRoute.elm +312 -0
  129. package/generator/template/script/src/Stars.elm +42 -0
  130. package/package.json +30 -27
  131. package/src/ApiRoute.elm +249 -82
  132. package/src/BackendTask/Custom.elm +325 -0
  133. package/src/BackendTask/Env.elm +90 -0
  134. package/src/{DataSource → BackendTask}/File.elm +171 -56
  135. package/src/{DataSource → BackendTask}/Glob.elm +136 -125
  136. package/src/BackendTask/Http.elm +679 -0
  137. package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
  138. package/src/BackendTask/Internal/Request.elm +69 -0
  139. package/src/BackendTask/Random.elm +79 -0
  140. package/src/BackendTask/Time.elm +47 -0
  141. package/src/BackendTask.elm +531 -0
  142. package/src/FatalError.elm +90 -0
  143. package/src/FormData.elm +21 -18
  144. package/src/Head/Seo.elm +4 -4
  145. package/src/Head.elm +112 -8
  146. package/src/Internal/ApiRoute.elm +7 -5
  147. package/src/Internal/Request.elm +84 -4
  148. package/src/PageServerResponse.elm +6 -1
  149. package/src/Pages/ConcurrentSubmission.elm +127 -0
  150. package/src/Pages/Form.elm +340 -0
  151. package/src/Pages/FormData.elm +19 -0
  152. package/src/Pages/GeneratorProgramConfig.elm +15 -0
  153. package/src/Pages/Internal/FatalError.elm +5 -0
  154. package/src/Pages/Internal/Msg.elm +93 -0
  155. package/src/Pages/Internal/NotFoundReason.elm +4 -4
  156. package/src/Pages/Internal/Platform/Cli.elm +586 -768
  157. package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
  158. package/src/Pages/Internal/Platform/Effect.elm +1 -2
  159. package/src/Pages/Internal/Platform/GeneratorApplication.elm +379 -0
  160. package/src/Pages/Internal/Platform/StaticResponses.elm +65 -276
  161. package/src/Pages/Internal/Platform/ToJsPayload.elm +6 -9
  162. package/src/Pages/Internal/Platform.elm +330 -203
  163. package/src/Pages/Internal/ResponseSketch.elm +2 -2
  164. package/src/Pages/Internal/Script.elm +17 -0
  165. package/src/Pages/Internal/StaticHttpBody.elm +35 -1
  166. package/src/Pages/Manifest.elm +52 -11
  167. package/src/Pages/Navigation.elm +85 -0
  168. package/src/Pages/PageUrl.elm +26 -12
  169. package/src/Pages/ProgramConfig.elm +32 -22
  170. package/src/Pages/Script.elm +166 -0
  171. package/src/Pages/SiteConfig.elm +3 -2
  172. package/src/Pages/StaticHttp/Request.elm +2 -2
  173. package/src/Pages/StaticHttpRequest.elm +23 -99
  174. package/src/Pages/Url.elm +3 -3
  175. package/src/PagesMsg.elm +88 -0
  176. package/src/QueryParams.elm +21 -172
  177. package/src/RenderRequest.elm +7 -7
  178. package/src/RequestsAndPending.elm +37 -20
  179. package/src/Result/Extra.elm +26 -0
  180. package/src/Scaffold/Form.elm +569 -0
  181. package/src/Scaffold/Route.elm +1431 -0
  182. package/src/Server/Request.elm +476 -1001
  183. package/src/Server/Response.elm +130 -36
  184. package/src/Server/Session.elm +181 -111
  185. package/src/Server/SetCookie.elm +80 -32
  186. package/src/Stub.elm +53 -0
  187. package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
  188. package/src/{Path.elm → UrlPath.elm} +33 -36
  189. package/generator/template/public/images/icon-png.png +0 -0
  190. package/src/DataSource/Env.elm +0 -38
  191. package/src/DataSource/Http.elm +0 -446
  192. package/src/DataSource/Internal/Request.elm +0 -20
  193. package/src/DataSource/Port.elm +0 -90
  194. package/src/DataSource.elm +0 -546
  195. package/src/Form/Field.elm +0 -717
  196. package/src/Form/FieldStatus.elm +0 -36
  197. package/src/Form/FieldView.elm +0 -417
  198. package/src/Form/FormData.elm +0 -22
  199. package/src/Form/Validation.elm +0 -391
  200. package/src/Form/Value.elm +0 -118
  201. package/src/Form.elm +0 -1683
  202. package/src/FormDecoder.elm +0 -102
  203. package/src/Pages/FormState.elm +0 -256
  204. package/src/Pages/Generate.elm +0 -1242
  205. package/src/Pages/Internal/Form.elm +0 -17
  206. package/src/Pages/Internal/Platform/Cli.elm.bak +0 -1276
  207. package/src/Pages/Msg.elm +0 -79
  208. package/src/Pages/Transition.elm +0 -70
package/src/ApiRoute.elm CHANGED
@@ -1,29 +1,166 @@
1
1
  module ApiRoute exposing
2
- ( ApiRoute, ApiRouteBuilder, Response
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, getGlobalHeadTagsDataSource
8
+ , toJson, getBuildTimeRoutes, getGlobalHeadTagsBackendTask
8
9
  )
9
10
 
10
- {-| ApiRoute's are defined in `src/Api.elm` and are a way to generate files, like RSS feeds, sitemaps, or any text-based file that you output with an Elm function! You get access
11
- to a DataSource 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
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
- @docs ApiRoute, ApiRouteBuilder, Response
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
- @docs capture, literal, slash, succeed
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
- @docs preRenderWithFallback, serverRender
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, getGlobalHeadTagsDataSource
173
+ @docs toJson, getBuildTimeRoutes, getGlobalHeadTagsBackendTask
37
174
 
38
175
  -}
39
176
 
40
- import DataSource exposing (DataSource)
41
- import DataSource.Http
177
+ import BackendTask exposing (BackendTask)
178
+ import FatalError exposing (FatalError)
42
179
  import Head
43
180
  import Internal.ApiRoute exposing (ApiRoute(..), ApiRouteBuilder(..))
44
- import Json.Decode as Decode
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
- single : ApiRouteBuilder (DataSource String) (List String) -> ApiRoute Response
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 -> DataSource.succeed [ constructor ])
200
+ |> preRender (\constructor -> BackendTask.succeed [ constructor ])
62
201
 
63
202
 
64
203
  {-| -}
65
- serverRender : ApiRouteBuilder (Server.Request.Parser (DataSource (Server.Response.Response Never Never))) constructor -> ApiRoute Response
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
- (\toDataSource ->
74
- DataSource.Http.get
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 (DataSource.map (Server.Response.toJson >> Just))
215
+ |> Maybe.map (BackendTask.map (Server.Response.toJson >> Just))
97
216
  |> Maybe.withDefault
98
- (DataSource.succeed Nothing)
99
- , buildTimeRoutes = DataSource.succeed []
217
+ (BackendTask.succeed Nothing)
218
+ , buildTimeRoutes = BackendTask.succeed []
100
219
  , handleRoute =
101
220
  \path ->
102
- DataSource.succeed
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 -> DataSource (List (List String))) -> ApiRouteBuilder (DataSource (Server.Response.Response Never Never)) constructor -> ApiRoute Response
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__ : DataSource (List String)
239
+ buildTimeRoutes__ : BackendTask FatalError (List String)
121
240
  buildTimeRoutes__ =
122
241
  buildUrls (constructor [])
123
- |> DataSource.map (List.map toString)
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 (DataSource.map (Server.Response.toJson >> Just))
249
+ |> Maybe.map (BackendTask.map (Server.Response.toJson >> Just))
131
250
  |> Maybe.withDefault
132
- (DataSource.succeed Nothing)
251
+ (BackendTask.succeed Nothing)
133
252
  , buildTimeRoutes = buildTimeRoutes__
134
253
  , handleRoute =
135
254
  \path ->
136
- DataSource.succeed
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
- preRender : (constructor -> DataSource (List (List String))) -> ApiRouteBuilder (DataSource String) constructor -> ApiRoute Response
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__ : DataSource (List String)
328
+ buildTimeRoutes__ : BackendTask FatalError (List String)
163
329
  buildTimeRoutes__ =
164
330
  buildUrls (constructor [])
165
- |> DataSource.map (List.map toString)
331
+ |> BackendTask.map (List.map toString)
166
332
 
167
- preBuiltMatches : DataSource (List (List String))
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 : DataSource Bool
346
+ routeFound : BackendTask FatalError Bool
181
347
  routeFound =
182
348
  preBuiltMatches
183
- |> DataSource.map (List.member matches)
349
+ |> BackendTask.map (List.member matches)
184
350
  in
185
351
  routeFound
186
- |> DataSource.andThen
352
+ |> BackendTask.andThen
187
353
  (\found ->
188
354
  if found then
189
355
  Internal.ApiRoute.tryMatch path fullHandler
190
- |> Maybe.map (DataSource.map (encodeStaticFileBody >> Just))
191
- |> Maybe.withDefault (DataSource.succeed Nothing)
356
+ |> Maybe.map (BackendTask.map (encodeStaticFileBody >> Just))
357
+ |> Maybe.withDefault (BackendTask.succeed Nothing)
192
358
 
193
359
  else
194
- DataSource.succeed Nothing
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
- |> DataSource.map (List.member matches)
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 -> DataSource (List String)
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 : DataSource (List Head.Tag) -> ApiRoute response -> ApiRoute response
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
- getGlobalHeadTagsDataSource : ApiRoute response -> Maybe (DataSource (List Head.Tag))
301
- getGlobalHeadTagsDataSource (ApiRoute handler) =
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 ""