elm-pages 3.0.0-beta.3 → 3.0.0-beta.30

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 (123) hide show
  1. package/README.md +10 -1
  2. package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2864 -2589
  3. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  4. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
  5. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
  6. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  7. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  8. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  9. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
  10. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1327 -122
  11. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +15295 -13271
  12. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  13. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  14. package/generator/dead-code-review/elm.json +8 -6
  15. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
  16. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
  17. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  18. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  19. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  20. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
  21. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1327 -122
  22. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +14621 -12637
  23. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  24. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +4 -4
  25. package/generator/review/elm.json +8 -8
  26. package/generator/src/RouteBuilder.elm +113 -107
  27. package/generator/src/SharedTemplate.elm +3 -2
  28. package/generator/src/SiteConfig.elm +3 -2
  29. package/generator/src/basepath-middleware.js +3 -3
  30. package/generator/src/build.js +123 -87
  31. package/generator/src/cli.js +256 -77
  32. package/generator/src/codegen.js +29 -27
  33. package/generator/src/compatibility-key.js +3 -0
  34. package/generator/src/compile-elm.js +25 -25
  35. package/generator/src/config.js +39 -0
  36. package/generator/src/copy-dir.js +2 -2
  37. package/generator/src/dev-server.js +150 -133
  38. package/generator/src/dir-helpers.js +9 -26
  39. package/generator/src/elm-codegen.js +5 -4
  40. package/generator/src/elm-file-constants.js +2 -3
  41. package/generator/src/error-formatter.js +12 -11
  42. package/generator/src/file-helpers.js +3 -4
  43. package/generator/src/generate-template-module-connector.js +23 -22
  44. package/generator/src/init.js +9 -8
  45. package/generator/src/pre-render-html.js +39 -28
  46. package/generator/src/render-test.js +109 -0
  47. package/generator/src/render-worker.js +25 -28
  48. package/generator/src/render.js +322 -142
  49. package/generator/src/request-cache.js +252 -163
  50. package/generator/src/rewrite-client-elm-json.js +5 -5
  51. package/generator/src/rewrite-elm-json.js +7 -7
  52. package/generator/src/route-codegen-helpers.js +16 -31
  53. package/generator/src/seo-renderer.js +12 -7
  54. package/generator/src/vite-utils.js +77 -0
  55. package/generator/static-code/hmr.js +79 -13
  56. package/generator/template/app/Api.elm +6 -5
  57. package/generator/template/app/Effect.elm +123 -0
  58. package/generator/template/app/ErrorPage.elm +37 -6
  59. package/generator/template/app/Route/Index.elm +17 -10
  60. package/generator/template/app/Shared.elm +24 -47
  61. package/generator/template/app/Site.elm +19 -6
  62. package/generator/template/app/View.elm +1 -8
  63. package/generator/template/elm-tooling.json +0 -3
  64. package/generator/template/elm.json +34 -25
  65. package/generator/template/package.json +10 -4
  66. package/package.json +23 -22
  67. package/src/ApiRoute.elm +199 -61
  68. package/src/BackendTask/Custom.elm +325 -0
  69. package/src/BackendTask/Env.elm +90 -0
  70. package/src/{DataSource → BackendTask}/File.elm +128 -43
  71. package/src/{DataSource → BackendTask}/Glob.elm +136 -125
  72. package/src/BackendTask/Http.elm +673 -0
  73. package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
  74. package/src/BackendTask/Internal/Request.elm +28 -0
  75. package/src/BackendTask/Random.elm +79 -0
  76. package/src/BackendTask/Time.elm +47 -0
  77. package/src/BackendTask.elm +537 -0
  78. package/src/FatalError.elm +89 -0
  79. package/src/Form/Field.elm +21 -9
  80. package/src/Form/FieldView.elm +94 -14
  81. package/src/Form.elm +275 -400
  82. package/src/Head.elm +237 -7
  83. package/src/HtmlPrinter.elm +7 -3
  84. package/src/Internal/ApiRoute.elm +7 -5
  85. package/src/PageServerResponse.elm +6 -1
  86. package/src/Pages/FormState.elm +6 -5
  87. package/src/Pages/GeneratorProgramConfig.elm +15 -0
  88. package/src/Pages/Internal/FatalError.elm +5 -0
  89. package/src/Pages/Internal/Form.elm +21 -1
  90. package/src/Pages/{Msg.elm → Internal/Msg.elm} +26 -16
  91. package/src/Pages/Internal/Platform/Cli.elm +507 -763
  92. package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
  93. package/src/Pages/Internal/Platform/Effect.elm +1 -2
  94. package/src/Pages/Internal/Platform/GeneratorApplication.elm +373 -0
  95. package/src/Pages/Internal/Platform/StaticResponses.elm +73 -270
  96. package/src/Pages/Internal/Platform/ToJsPayload.elm +4 -7
  97. package/src/Pages/Internal/Platform.elm +215 -102
  98. package/src/Pages/Internal/Script.elm +17 -0
  99. package/src/Pages/Internal/StaticHttpBody.elm +35 -1
  100. package/src/Pages/Manifest.elm +29 -4
  101. package/src/Pages/PageUrl.elm +23 -9
  102. package/src/Pages/ProgramConfig.elm +14 -10
  103. package/src/Pages/Script.elm +109 -0
  104. package/src/Pages/SiteConfig.elm +3 -2
  105. package/src/Pages/StaticHttp/Request.elm +2 -2
  106. package/src/Pages/StaticHttpRequest.elm +23 -98
  107. package/src/PagesMsg.elm +92 -0
  108. package/src/Path.elm +16 -19
  109. package/src/QueryParams.elm +21 -172
  110. package/src/RequestsAndPending.elm +8 -19
  111. package/src/Result/Extra.elm +26 -0
  112. package/src/Scaffold/Form.elm +484 -0
  113. package/src/Scaffold/Route.elm +1376 -0
  114. package/src/Server/Request.elm +43 -37
  115. package/src/Server/Session.elm +34 -34
  116. package/src/Server/SetCookie.elm +1 -1
  117. package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
  118. package/src/DataSource/Env.elm +0 -38
  119. package/src/DataSource/Http.elm +0 -446
  120. package/src/DataSource/Internal/Request.elm +0 -20
  121. package/src/DataSource/Port.elm +0 -90
  122. package/src/DataSource.elm +0 -538
  123. package/src/Pages/Generate.elm +0 -800
@@ -0,0 +1,28 @@
1
+ module BackendTask.Internal.Request exposing (request)
2
+
3
+ import BackendTask exposing (BackendTask)
4
+ import BackendTask.Http exposing (Body, Expect)
5
+
6
+
7
+ request :
8
+ { name : String
9
+ , body : Body
10
+ , expect : Expect a
11
+ }
12
+ -> BackendTask error a
13
+ request ({ name, body, expect } as params) =
14
+ -- elm-review: known-unoptimized-recursion
15
+ BackendTask.Http.request
16
+ { url = "elm-pages-internal://" ++ name
17
+ , method = "GET"
18
+ , headers = []
19
+ , body = body
20
+ , timeoutInMs = Nothing
21
+ , retries = Nothing
22
+ }
23
+ expect
24
+ |> BackendTask.onError
25
+ (\_ ->
26
+ -- TODO avoid crash here, this should be handled as an internal error
27
+ request params
28
+ )
@@ -0,0 +1,79 @@
1
+ module BackendTask.Random exposing
2
+ ( generate
3
+ , int32
4
+ )
5
+
6
+ {-|
7
+
8
+ @docs generate
9
+
10
+ @docs int32
11
+
12
+ -}
13
+
14
+ import BackendTask exposing (BackendTask)
15
+ import BackendTask.Http
16
+ import BackendTask.Internal.Request
17
+ import Json.Decode as Decode
18
+ import Json.Encode as Encode
19
+ import Random
20
+
21
+
22
+ {-| Takes an `elm/random` `Random.Generator` and runs it using a randomly generated initial seed.
23
+
24
+ type alias Data =
25
+ { randomData : ( Int, Float )
26
+ }
27
+
28
+ data : BackendTask FatalError Data
29
+ data =
30
+ BackendTask.map Data
31
+ (BackendTask.Random.generate generator)
32
+
33
+ generator : Random.Generator ( Int, Float )
34
+ generator =
35
+ Random.map2 Tuple.pair (Random.int 0 100) (Random.float 0 100)
36
+
37
+ The random initial seed is generated using <https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues>
38
+ to generate a single 32-bit Integer. That 32-bit Integer is then used with `Random.initialSeed` to create an Elm Random.Seed value.
39
+ Then that `Seed` used to run the `Generator`.
40
+
41
+ Note that this is different than `elm/random`'s `Random.generate`. This difference shouldn't be problematic, and in fact the `BackendTask`
42
+ random seed generation is more cryptographically independent because you can't determine the
43
+ random seed based solely on the time at which it is run. Each time you call `BackendTask.generate` it uses a newly
44
+ generated random seed to run the `Random.Generator` that is passed in. In contrast, `elm/random`'s `Random.generate`
45
+ generates an initial seed using `Time.now`, and then continues with that same seed using using [`Random.step`](https://package.elm-lang.org/packages/elm/random/latest/Random#step)
46
+ to get new random values after that. You can [see the implementation here](https://github.com/elm/random/blob/c1c9da4d861363cee1c93382d2687880279ed0dd/src/Random.elm#L865-L896).
47
+ However, `elm/random` is still not suitable in general for cryptographic uses of random because it uses 32-bits for when it
48
+ steps through new seeds while running a single `Random.Generator`.
49
+
50
+ -}
51
+ generate : Random.Generator value -> BackendTask error value
52
+ generate generator =
53
+ int32
54
+ |> BackendTask.map
55
+ (Random.initialSeed
56
+ >> Random.step generator
57
+ >> Tuple.first
58
+ )
59
+
60
+
61
+ {-| Gives a random 32-bit Int. This can be useful if you want to do low-level things with a cryptographically sound
62
+ random 32-bit integer.
63
+
64
+ The value comes from running this code in Node using <https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues>:
65
+
66
+ ```js
67
+ import * as crypto from "node:crypto";
68
+
69
+ crypto.getRandomValues(new Uint32Array(1))[0]
70
+ ```
71
+
72
+ -}
73
+ int32 : BackendTask error Int
74
+ int32 =
75
+ BackendTask.Internal.Request.request
76
+ { name = "randomSeed"
77
+ , body = BackendTask.Http.jsonBody Encode.null
78
+ , expect = BackendTask.Http.expectJson Decode.int
79
+ }
@@ -0,0 +1,47 @@
1
+ module BackendTask.Time exposing (now)
2
+
3
+ {-|
4
+
5
+ @docs now
6
+
7
+ -}
8
+
9
+ import BackendTask exposing (BackendTask)
10
+ import BackendTask.Http
11
+ import BackendTask.Internal.Request
12
+ import Json.Decode as Decode
13
+ import Json.Encode as Encode
14
+ import Time
15
+
16
+
17
+ {-| Gives a `Time.Posix` of when the `BackendTask` executes.
18
+
19
+ type alias Data =
20
+ { time : Time.Posix
21
+ }
22
+
23
+ data : BackendTask FatalError Data
24
+ data =
25
+ BackendTask.map Data
26
+ BackendTask.Time.now
27
+
28
+ It's better to use [`Server.Request.requestTime`](Server-Request#requestTime) or `Pages.builtAt` when those are the semantics
29
+ you are looking for. `requestTime` gives you a single reliable and consistent time for when the incoming HTTP request was received in
30
+ a server-rendered Route or server-rendered API Route. `Pages.builtAt` gives a single reliable and consistent time when the
31
+ site was built.
32
+
33
+ `BackendTask.Time.now` gives you the time that it happened to execute, which might give you what you need, but be
34
+ aware that the time you get is dependent on how BackendTask's are scheduled and executed internally in elm-pages, and
35
+ its best to avoid depending on that variation when possible.
36
+
37
+ -}
38
+ now : BackendTask error Time.Posix
39
+ now =
40
+ BackendTask.Internal.Request.request
41
+ { name = "now"
42
+ , body =
43
+ BackendTask.Http.jsonBody Encode.null
44
+ , expect =
45
+ BackendTask.Http.expectJson
46
+ (Decode.int |> Decode.map Time.millisToPosix)
47
+ }
@@ -0,0 +1,537 @@
1
+ module BackendTask exposing
2
+ ( BackendTask
3
+ , map, succeed, fail
4
+ , fromResult
5
+ , andThen, resolve, combine
6
+ , andMap
7
+ , map2, map3, map4, map5, map6, map7, map8, map9
8
+ , allowFatal, mapError, onError, toResult
9
+ )
10
+
11
+ {-| In an `elm-pages` app, each Route Module can define a value `data` which is a `BackendTask` that will be resolved **before** `init` is called. That means it is also available
12
+ when the page's HTML is pre-rendered during the build step. You can also access the resolved data in `head` to use it for the page's SEO meta tags.
13
+
14
+ A `BackendTask` lets you pull in data from:
15
+
16
+ - Local files ([`BackendTask.File`](BackendTask-File))
17
+ - HTTP requests ([`BackendTask.Http`](BackendTask-Http))
18
+ - Globs, i.e. listing out local files based on a pattern like `content/*.txt` ([`BackendTask.Glob`](BackendTask-Glob))
19
+ - Ports, i.e. getting JSON data from running custom NodeJS, similar to a port in a vanilla Elm app except run at build-time in NodeJS, rather than at run-time in the browser ([`BackendTask.Custom`](BackendTask-Custom))
20
+ - Hardcoded data (`BackendTask.succeed "Hello!"`)
21
+ - Or any combination of the above, using `BackendTask.map2`, `BackendTask.andThen`, or other combining/continuing helpers from this module
22
+
23
+
24
+ ## BackendTask's vs. Effect's/Cmd's
25
+
26
+ BackendTask's are always resolved before the page is rendered and sent to the browser. A BackendTask is never executed
27
+ in the Browser. Instead, the resolved data from the BackendTask is passed down to the Browser - it has been resolved
28
+ before any client-side JavaScript ever executes. In the case of a pre-rendered route, this is during the CLI build phase,
29
+ and for server-rendered routes its BackendTask is resolved on the server.
30
+
31
+ Effect's/Cmd's are never executed on the CLI or server, they are only executed in the Browser. The data from a Route Module's
32
+ `init` function is used to render the initial HTML on the server or build step, but the Effect isn't executed and `update` is never called
33
+ before the page is hydrated in the Browser. This gives a deterministic mental model of what the first render will look like,
34
+ and a nicely typed way to define the initial `Data` you have to render your initial view.
35
+
36
+ Because `elm-pages` hydrates into a full Elm single-page app, it does need the data in order to initialize the Elm app.
37
+ So why not just get the data the old-fashioned way, with `elm/http`, for example?
38
+
39
+ A few reasons:
40
+
41
+ 1. BackendTask's allow you to pull in data that you wouldn't normally be able to access from an Elm app, like local files, or listings of files in a folder. Not only that, but the dev server knows to automatically hot reload the data when the files it depends on change, so you can edit the files you used in your BackendTask and see the page hot reload as you save!
42
+ 2. You can pre-render HTML for your pages, including the SEO meta tags, with all that rich, well-typed Elm data available! That's something you can't accomplish with a vanilla Elm app, and it's one of the main use cases for elm-pages.
43
+ 3. Because `elm-pages` has a build step, you know that your `BackendTask.Http` requests succeeded, your decoders succeeded, your custom BackendTask validations succeeded, and everything went smoothly. If something went wrong, you get a build failure and can deal with the issues before the site goes live. That means your users won't see those errors, and as a developer you don't need to handle those error cases in your code! Think of it as "parse, don't validate", but for your entire build. In the case of server-rendered routes, a BackendTask failure will render a 500 page, so more care needs to be taken to make sure all common errors are handled properly, but the tradeoff is that you can use BackendTask's to pull in highly dynamic data and even render user-specific pages.
44
+ 4. For static routes, you don't have to worry about an API being down, or hitting it repeatedly. You can build in data and it will end up as optimized binary-encoded data served up with all the other assets of your site. If your CDN (static site host) is down, then the rest of your site is probably down anyway. If your site host is up, then so is all of your `BackendTask` data. Also, it will be served up extremely quickly without needing to wait for any database queries to be performed, `andThen` requests to be resolved, etc., because all of that work and waiting was done at build-time!
45
+
46
+
47
+ ## Mental Model
48
+
49
+ You can think of a BackendTask as a declarative (not imperative) definition of data. It represents where to get the data from, and how to transform it (map, combine with other BackendTasks, etc.).
50
+
51
+
52
+ ## How do I actually use a BackendTask?
53
+
54
+ This is very similar to Cmd's in Elm. You don't perform a Cmd just by running that code, as you might in a language like JavaScript. Instead, a Cmd _will not do anything_ unless you pass it to The Elm Architecture to have it perform it for you.
55
+ You pass a Cmd to The Elm Architecture by returning it in `init` or `update`. So actually a `Cmd` is just data describing a side-effect that the Elm runtime can perform, and how to build a `Msg` once it's done.
56
+
57
+ `BackendTask`'s are very similar. A `BackendTask` doesn't do anything just by "running" it. Just like a `Cmd`, it's only data that describes a side-effect to perform. Specifically, it describes a side-effect that the _elm-pages runtime_ can perform.
58
+ There are a few places where we can pass a `BackendTask` to the `elm-pages` runtime so it can perform it. Most commonly, you give a field called `data` in your Route Module's definition. Instead of giving a `Msg` when the side-effects are complete,
59
+ the page will render once all of the side-effects have run and all the data is resolved. `elm-pages` makes the resolved data available your Route Module's `init`, `view`, `update`, and `head` functions, similar to how a regular Elm app passes `Msg`'s in
60
+ to `update`.
61
+
62
+ Any place in your `elm-pages` app where the framework lets you pass in a value of type `BackendTask` is a place where you can give `elm-pages` a BackendTask to perform (for example, `Site.head` where you define global head tags for your site).
63
+
64
+
65
+ ## Basics
66
+
67
+ @docs BackendTask
68
+
69
+ @docs map, succeed, fail
70
+
71
+ @docs fromResult
72
+
73
+
74
+ ## Chaining Requests
75
+
76
+ @docs andThen, resolve, combine
77
+
78
+ @docs andMap
79
+
80
+ @docs map2, map3, map4, map5, map6, map7, map8, map9
81
+
82
+
83
+ ## FatalError Handling
84
+
85
+ @docs allowFatal, mapError, onError, toResult
86
+
87
+ -}
88
+
89
+ import FatalError exposing (FatalError)
90
+ import Json.Encode
91
+ import Pages.StaticHttpRequest exposing (RawRequest(..))
92
+
93
+
94
+ {-| A BackendTask represents data that will be gathered at build time. Multiple `BackendTask`s can be combined together using the `mapN` functions,
95
+ very similar to how you can manipulate values with Json Decoders in Elm.
96
+ -}
97
+ type alias BackendTask error value =
98
+ RawRequest error value
99
+
100
+
101
+ {-| Transform a request into an arbitrary value. The same underlying HTTP requests will be performed during the build
102
+ step, but mapping allows you to change the resulting values by applying functions to the results.
103
+
104
+ A common use for this is to map your data into your elm-pages view:
105
+
106
+ import BackendTask
107
+ import Json.Decode as Decode exposing (Decoder)
108
+
109
+ view =
110
+ BackendTask.Http.get
111
+ (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
112
+ (Decode.field "stargazers_count" Decode.int)
113
+ |> BackendTask.map
114
+ (\stars ->
115
+ { view =
116
+ \model viewForPage ->
117
+ { title = "Current stars: " ++ String.fromInt stars
118
+ , body = Html.text <| "⭐️ " ++ String.fromInt stars
119
+ , head = []
120
+ }
121
+ }
122
+ )
123
+
124
+ -}
125
+ map : (a -> b) -> BackendTask error a -> BackendTask error b
126
+ map fn requestInfo =
127
+ case requestInfo of
128
+ ApiRoute value ->
129
+ ApiRoute (Result.map fn value)
130
+
131
+ Request urls lookupFn ->
132
+ Request
133
+ urls
134
+ (mapLookupFn fn lookupFn)
135
+
136
+
137
+ mapLookupFn : (a -> b) -> (d -> c -> BackendTask error a) -> d -> c -> BackendTask error b
138
+ mapLookupFn fn lookupFn maybeMock requests =
139
+ map fn (lookupFn maybeMock requests)
140
+
141
+
142
+ {-| Helper to remove an inner layer of Request wrapping.
143
+ -}
144
+ resolve : BackendTask error (List (BackendTask error value)) -> BackendTask error (List value)
145
+ resolve =
146
+ andThen combine
147
+
148
+
149
+ {-| Turn a list of `StaticHttp.Request`s into a single one.
150
+
151
+ import BackendTask
152
+ import Json.Decode as Decode exposing (Decoder)
153
+
154
+ type alias Pokemon =
155
+ { name : String
156
+ , sprite : String
157
+ }
158
+
159
+ pokemonDetailRequest : StaticHttp.Request (List Pokemon)
160
+ pokemonDetailRequest =
161
+ StaticHttp.get
162
+ (Secrets.succeed "https://pokeapi.co/api/v2/pokemon/?limit=3")
163
+ (Decode.field "results"
164
+ (Decode.list
165
+ (Decode.map2 Tuple.pair
166
+ (Decode.field "name" Decode.string)
167
+ (Decode.field "url" Decode.string)
168
+ |> Decode.map
169
+ (\( name, url ) ->
170
+ StaticHttp.get (Secrets.succeed url)
171
+ (Decode.at
172
+ [ "sprites", "front_default" ]
173
+ Decode.string
174
+ |> Decode.map (Pokemon name)
175
+ )
176
+ )
177
+ )
178
+ )
179
+ )
180
+ |> StaticHttp.andThen StaticHttp.combine
181
+
182
+ -}
183
+ combine : List (BackendTask error value) -> BackendTask error (List value)
184
+ combine items =
185
+ List.foldl (map2 (::)) (succeed []) items |> map List.reverse
186
+
187
+
188
+ {-| Like map, but it takes in two `Request`s.
189
+
190
+ view siteMetadata page =
191
+ StaticHttp.map2
192
+ (\elmPagesStars elmMarkdownStars ->
193
+ { view =
194
+ \model viewForPage ->
195
+ { title = "Repo Stargazers"
196
+ , body = starsView elmPagesStars elmMarkdownStars
197
+ }
198
+ , head = head elmPagesStars elmMarkdownStars
199
+ }
200
+ )
201
+ (get
202
+ (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
203
+ (Decode.field "stargazers_count" Decode.int)
204
+ )
205
+ (get
206
+ (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-markdown")
207
+ (Decode.field "stargazers_count" Decode.int)
208
+ )
209
+
210
+ -}
211
+ map2 : (a -> b -> c) -> BackendTask error a -> BackendTask error b -> BackendTask error c
212
+ map2 fn request1 request2 =
213
+ -- elm-review: known-unoptimized-recursion
214
+ -- TODO try to find a way to optimize tail-call recursion here
215
+ case ( request1, request2 ) of
216
+ ( ApiRoute value1, ApiRoute value2 ) ->
217
+ ApiRoute (Result.map2 fn value1 value2)
218
+
219
+ ( Request urls1 lookupFn1, Request urls2 lookupFn2 ) ->
220
+ Request
221
+ (urls1 ++ urls2)
222
+ (\resolver responses ->
223
+ map2 fn
224
+ (lookupFn1 resolver responses)
225
+ (lookupFn2 resolver responses)
226
+ )
227
+
228
+ ( Request urls1 lookupFn1, ApiRoute value2 ) ->
229
+ Request
230
+ urls1
231
+ (\resolver responses ->
232
+ map2 fn
233
+ (lookupFn1 resolver responses)
234
+ (ApiRoute value2)
235
+ )
236
+
237
+ ( ApiRoute value2, Request urls1 lookupFn1 ) ->
238
+ Request
239
+ urls1
240
+ (\resolver responses ->
241
+ map2 fn
242
+ (ApiRoute value2)
243
+ (lookupFn1 resolver responses)
244
+ )
245
+
246
+
247
+ {-| Build off of the response from a previous `BackendTask` request to build a follow-up request. You can use the data
248
+ from the previous response to build up the URL, headers, etc. that you send to the subsequent request.
249
+
250
+ import BackendTask
251
+ import Json.Decode as Decode exposing (Decoder)
252
+
253
+ licenseData : BackendTask String
254
+ licenseData =
255
+ BackendTask.Http.get
256
+ (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
257
+ (Decode.at [ "license", "url" ] Decode.string)
258
+ |> BackendTask.andThen
259
+ (\licenseUrl ->
260
+ BackendTask.Http.get (Secrets.succeed licenseUrl) (Decode.field "description" Decode.string)
261
+ )
262
+
263
+ -}
264
+ andThen : (a -> BackendTask error b) -> BackendTask error a -> BackendTask error b
265
+ andThen fn requestInfo =
266
+ -- elm-review: known-unoptimized-recursion
267
+ -- TODO try to find a way to optimize recursion here
268
+ case requestInfo of
269
+ ApiRoute a ->
270
+ case a of
271
+ Ok okA ->
272
+ fn okA
273
+
274
+ Err errA ->
275
+ fail errA
276
+
277
+ Request urls lookupFn ->
278
+ if List.isEmpty urls then
279
+ andThen fn (lookupFn Nothing (Json.Encode.object []))
280
+
281
+ else
282
+ Request urls
283
+ (\maybeMockResolver responses ->
284
+ lookupFn maybeMockResolver responses
285
+ |> andThen fn
286
+ )
287
+
288
+
289
+ {-| -}
290
+ onError : (error -> BackendTask mappedError value) -> BackendTask error value -> BackendTask mappedError value
291
+ onError fromError backendTask =
292
+ -- elm-review: known-unoptimized-recursion
293
+ case backendTask of
294
+ ApiRoute a ->
295
+ case a of
296
+ Ok okA ->
297
+ succeed okA
298
+
299
+ Err errA ->
300
+ fromError errA
301
+
302
+ Request urls lookupFn ->
303
+ if List.isEmpty urls then
304
+ onError fromError (lookupFn Nothing (Json.Encode.object []))
305
+
306
+ else
307
+ Request urls
308
+ (\maybeMockResolver responses ->
309
+ lookupFn maybeMockResolver responses
310
+ |> onError fromError
311
+ )
312
+
313
+
314
+ {-| A helper for combining `BackendTask`s in pipelines.
315
+ -}
316
+ andMap : BackendTask error a -> BackendTask error (a -> b) -> BackendTask error b
317
+ andMap =
318
+ map2 (|>)
319
+
320
+
321
+ {-| This is useful for prototyping with some hardcoded data, or for having a view that doesn't have any StaticHttp data.
322
+
323
+ import BackendTask
324
+
325
+ view :
326
+ List ( PagePath, Metadata )
327
+ ->
328
+ { path : PagePath
329
+ , frontmatter : Metadata
330
+ }
331
+ ->
332
+ StaticHttp.Request
333
+ { view : Model -> View -> { title : String, body : Html Msg }
334
+ , head : List (Head.Tag Pages.PathKey)
335
+ }
336
+ view siteMetadata page =
337
+ StaticHttp.succeed
338
+ { view =
339
+ \model viewForPage ->
340
+ mainView model viewForPage
341
+ , head = head page.frontmatter
342
+ }
343
+
344
+ -}
345
+ succeed : a -> BackendTask error a
346
+ succeed value =
347
+ ApiRoute (Ok value)
348
+
349
+
350
+ {-| -}
351
+ fail : error -> BackendTask error a
352
+ fail error =
353
+ ApiRoute (Err error)
354
+
355
+
356
+ {-| Turn an Err into a BackendTask failure.
357
+ -}
358
+ fromResult : Result error value -> BackendTask error value
359
+ fromResult result =
360
+ case result of
361
+ Ok okValue ->
362
+ succeed okValue
363
+
364
+ Err error ->
365
+ fail error
366
+
367
+
368
+ {-| -}
369
+ mapError : (error -> errorMapped) -> BackendTask error value -> BackendTask errorMapped value
370
+ mapError mapFn requestInfo =
371
+ case requestInfo of
372
+ ApiRoute value ->
373
+ ApiRoute (Result.mapError mapFn value)
374
+
375
+ Request urls lookupFn ->
376
+ Request
377
+ urls
378
+ (mapLookupFnError mapFn lookupFn)
379
+
380
+
381
+ mapLookupFnError : (error -> errorMapped) -> (d -> c -> BackendTask error a) -> d -> c -> BackendTask errorMapped a
382
+ mapLookupFnError fn lookupFn maybeMock requests =
383
+ mapError fn (lookupFn maybeMock requests)
384
+
385
+
386
+ {-| -}
387
+ map3 :
388
+ (value1 -> value2 -> value3 -> valueCombined)
389
+ -> BackendTask error value1
390
+ -> BackendTask error value2
391
+ -> BackendTask error value3
392
+ -> BackendTask error valueCombined
393
+ map3 combineFn request1 request2 request3 =
394
+ succeed combineFn
395
+ |> map2 (|>) request1
396
+ |> map2 (|>) request2
397
+ |> map2 (|>) request3
398
+
399
+
400
+ {-| -}
401
+ map4 :
402
+ (value1 -> value2 -> value3 -> value4 -> valueCombined)
403
+ -> BackendTask error value1
404
+ -> BackendTask error value2
405
+ -> BackendTask error value3
406
+ -> BackendTask error value4
407
+ -> BackendTask error valueCombined
408
+ map4 combineFn request1 request2 request3 request4 =
409
+ succeed combineFn
410
+ |> map2 (|>) request1
411
+ |> map2 (|>) request2
412
+ |> map2 (|>) request3
413
+ |> map2 (|>) request4
414
+
415
+
416
+ {-| -}
417
+ map5 :
418
+ (value1 -> value2 -> value3 -> value4 -> value5 -> valueCombined)
419
+ -> BackendTask error value1
420
+ -> BackendTask error value2
421
+ -> BackendTask error value3
422
+ -> BackendTask error value4
423
+ -> BackendTask error value5
424
+ -> BackendTask error valueCombined
425
+ map5 combineFn request1 request2 request3 request4 request5 =
426
+ succeed combineFn
427
+ |> map2 (|>) request1
428
+ |> map2 (|>) request2
429
+ |> map2 (|>) request3
430
+ |> map2 (|>) request4
431
+ |> map2 (|>) request5
432
+
433
+
434
+ {-| -}
435
+ map6 :
436
+ (value1 -> value2 -> value3 -> value4 -> value5 -> value6 -> valueCombined)
437
+ -> BackendTask error value1
438
+ -> BackendTask error value2
439
+ -> BackendTask error value3
440
+ -> BackendTask error value4
441
+ -> BackendTask error value5
442
+ -> BackendTask error value6
443
+ -> BackendTask error valueCombined
444
+ map6 combineFn request1 request2 request3 request4 request5 request6 =
445
+ succeed combineFn
446
+ |> map2 (|>) request1
447
+ |> map2 (|>) request2
448
+ |> map2 (|>) request3
449
+ |> map2 (|>) request4
450
+ |> map2 (|>) request5
451
+ |> map2 (|>) request6
452
+
453
+
454
+ {-| -}
455
+ map7 :
456
+ (value1 -> value2 -> value3 -> value4 -> value5 -> value6 -> value7 -> valueCombined)
457
+ -> BackendTask error value1
458
+ -> BackendTask error value2
459
+ -> BackendTask error value3
460
+ -> BackendTask error value4
461
+ -> BackendTask error value5
462
+ -> BackendTask error value6
463
+ -> BackendTask error value7
464
+ -> BackendTask error valueCombined
465
+ map7 combineFn request1 request2 request3 request4 request5 request6 request7 =
466
+ succeed combineFn
467
+ |> map2 (|>) request1
468
+ |> map2 (|>) request2
469
+ |> map2 (|>) request3
470
+ |> map2 (|>) request4
471
+ |> map2 (|>) request5
472
+ |> map2 (|>) request6
473
+ |> map2 (|>) request7
474
+
475
+
476
+ {-| -}
477
+ map8 :
478
+ (value1 -> value2 -> value3 -> value4 -> value5 -> value6 -> value7 -> value8 -> valueCombined)
479
+ -> BackendTask error value1
480
+ -> BackendTask error value2
481
+ -> BackendTask error value3
482
+ -> BackendTask error value4
483
+ -> BackendTask error value5
484
+ -> BackendTask error value6
485
+ -> BackendTask error value7
486
+ -> BackendTask error value8
487
+ -> BackendTask error valueCombined
488
+ map8 combineFn request1 request2 request3 request4 request5 request6 request7 request8 =
489
+ succeed combineFn
490
+ |> map2 (|>) request1
491
+ |> map2 (|>) request2
492
+ |> map2 (|>) request3
493
+ |> map2 (|>) request4
494
+ |> map2 (|>) request5
495
+ |> map2 (|>) request6
496
+ |> map2 (|>) request7
497
+ |> map2 (|>) request8
498
+
499
+
500
+ {-| -}
501
+ map9 :
502
+ (value1 -> value2 -> value3 -> value4 -> value5 -> value6 -> value7 -> value8 -> value9 -> valueCombined)
503
+ -> BackendTask error value1
504
+ -> BackendTask error value2
505
+ -> BackendTask error value3
506
+ -> BackendTask error value4
507
+ -> BackendTask error value5
508
+ -> BackendTask error value6
509
+ -> BackendTask error value7
510
+ -> BackendTask error value8
511
+ -> BackendTask error value9
512
+ -> BackendTask error valueCombined
513
+ map9 combineFn request1 request2 request3 request4 request5 request6 request7 request8 request9 =
514
+ succeed combineFn
515
+ |> map2 (|>) request1
516
+ |> map2 (|>) request2
517
+ |> map2 (|>) request3
518
+ |> map2 (|>) request4
519
+ |> map2 (|>) request5
520
+ |> map2 (|>) request6
521
+ |> map2 (|>) request7
522
+ |> map2 (|>) request8
523
+ |> map2 (|>) request9
524
+
525
+
526
+ {-| -}
527
+ allowFatal : BackendTask { error | fatal : FatalError } data -> BackendTask FatalError data
528
+ allowFatal backendTask =
529
+ mapError .fatal backendTask
530
+
531
+
532
+ {-| -}
533
+ toResult : BackendTask error data -> BackendTask noError (Result error data)
534
+ toResult backendTask =
535
+ backendTask
536
+ |> andThen (Ok >> succeed)
537
+ |> onError (Err >> succeed)