elm-pages 3.0.0-beta.8 → 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.
Files changed (164) hide show
  1. package/README.md +11 -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/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  5. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
  6. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
  7. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  8. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  9. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  10. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
  11. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1527 -422
  12. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +16840 -13653
  13. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  14. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +2 -2
  15. package/generator/dead-code-review/elm.json +9 -7
  16. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
  17. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
  18. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
  19. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
  20. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
  21. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
  22. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  23. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  24. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  25. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
  26. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1527 -422
  27. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25118 -21832
  28. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  29. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +2 -2
  30. package/generator/review/elm.json +10 -10
  31. package/generator/src/RouteBuilder.elm +93 -128
  32. package/generator/src/SharedTemplate.elm +8 -7
  33. package/generator/src/SiteConfig.elm +3 -2
  34. package/generator/src/basepath-middleware.js +3 -3
  35. package/generator/src/build.js +147 -63
  36. package/generator/src/cli.js +292 -88
  37. package/generator/src/codegen.js +29 -27
  38. package/generator/src/compatibility-key.js +3 -0
  39. package/generator/src/compile-elm.js +43 -26
  40. package/generator/src/config.js +2 -4
  41. package/generator/src/copy-dir.js +2 -2
  42. package/generator/src/dev-server.js +159 -92
  43. package/generator/src/dir-helpers.js +9 -26
  44. package/generator/src/elm-codegen.js +5 -4
  45. package/generator/src/elm-file-constants.js +2 -3
  46. package/generator/src/error-formatter.js +12 -11
  47. package/generator/src/file-helpers.js +3 -4
  48. package/generator/src/generate-template-module-connector.js +23 -23
  49. package/generator/src/init.js +9 -8
  50. package/generator/src/pre-render-html.js +10 -13
  51. package/generator/src/render-test.js +109 -0
  52. package/generator/src/render-worker.js +25 -28
  53. package/generator/src/render.js +321 -142
  54. package/generator/src/request-cache.js +265 -162
  55. package/generator/src/resolve-elm-module.js +64 -0
  56. package/generator/src/rewrite-client-elm-json.js +6 -5
  57. package/generator/src/rewrite-elm-json-help.js +56 -0
  58. package/generator/src/rewrite-elm-json.js +17 -7
  59. package/generator/src/route-codegen-helpers.js +16 -31
  60. package/generator/src/seo-renderer.js +12 -7
  61. package/generator/src/vite-utils.js +1 -2
  62. package/generator/static-code/elm-pages.js +10 -0
  63. package/generator/static-code/hmr.js +79 -13
  64. package/generator/template/app/Api.elm +3 -2
  65. package/generator/template/app/Effect.elm +155 -0
  66. package/generator/template/app/ErrorPage.elm +49 -6
  67. package/generator/template/app/Route/Blog/Slug_.elm +86 -0
  68. package/generator/template/app/Route/Greet.elm +107 -0
  69. package/generator/template/app/Route/Hello.elm +119 -0
  70. package/generator/template/app/Route/Index.elm +26 -25
  71. package/generator/template/app/Shared.elm +38 -39
  72. package/generator/template/app/Site.elm +4 -7
  73. package/generator/template/app/View.elm +9 -8
  74. package/generator/template/codegen/elm.codegen.json +18 -0
  75. package/generator/template/custom-backend-task.ts +3 -0
  76. package/generator/template/elm-pages.config.mjs +13 -0
  77. package/generator/template/elm-tooling.json +0 -3
  78. package/generator/template/elm.json +25 -20
  79. package/generator/template/index.ts +1 -2
  80. package/generator/template/netlify.toml +4 -1
  81. package/generator/template/package.json +10 -4
  82. package/generator/template/script/.elm-pages/compiled-ports/custom-backend-task.mjs +7 -0
  83. package/generator/template/script/custom-backend-task.ts +3 -0
  84. package/generator/template/script/elm.json +61 -0
  85. package/generator/template/script/src/AddRoute.elm +312 -0
  86. package/generator/template/script/src/Stars.elm +42 -0
  87. package/package.json +30 -27
  88. package/src/ApiRoute.elm +249 -85
  89. package/src/BackendTask/Custom.elm +325 -0
  90. package/src/BackendTask/Env.elm +90 -0
  91. package/src/{DataSource → BackendTask}/File.elm +171 -56
  92. package/src/{DataSource → BackendTask}/Glob.elm +136 -125
  93. package/src/BackendTask/Http.elm +679 -0
  94. package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
  95. package/src/BackendTask/Internal/Request.elm +69 -0
  96. package/src/BackendTask/Random.elm +79 -0
  97. package/src/BackendTask/Time.elm +47 -0
  98. package/src/BackendTask.elm +531 -0
  99. package/src/FatalError.elm +90 -0
  100. package/src/FormData.elm +21 -18
  101. package/src/Head/Seo.elm +4 -4
  102. package/src/Head.elm +237 -7
  103. package/src/Internal/ApiRoute.elm +7 -5
  104. package/src/Internal/Request.elm +84 -4
  105. package/src/PageServerResponse.elm +6 -1
  106. package/src/Pages/ConcurrentSubmission.elm +127 -0
  107. package/src/Pages/Form.elm +340 -0
  108. package/src/Pages/FormData.elm +19 -0
  109. package/src/Pages/GeneratorProgramConfig.elm +15 -0
  110. package/src/Pages/Internal/FatalError.elm +5 -0
  111. package/src/Pages/Internal/Msg.elm +93 -0
  112. package/src/Pages/Internal/NotFoundReason.elm +4 -4
  113. package/src/Pages/Internal/Platform/Cli.elm +629 -767
  114. package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
  115. package/src/Pages/Internal/Platform/Effect.elm +1 -2
  116. package/src/Pages/Internal/Platform/GeneratorApplication.elm +379 -0
  117. package/src/Pages/Internal/Platform/StaticResponses.elm +65 -276
  118. package/src/Pages/Internal/Platform/ToJsPayload.elm +6 -9
  119. package/src/Pages/Internal/Platform.elm +330 -203
  120. package/src/Pages/Internal/ResponseSketch.elm +2 -2
  121. package/src/Pages/Internal/Script.elm +17 -0
  122. package/src/Pages/Internal/StaticHttpBody.elm +35 -1
  123. package/src/Pages/Manifest.elm +52 -11
  124. package/src/Pages/Navigation.elm +85 -0
  125. package/src/Pages/PageUrl.elm +26 -12
  126. package/src/Pages/ProgramConfig.elm +32 -22
  127. package/src/Pages/Script.elm +166 -0
  128. package/src/Pages/SiteConfig.elm +3 -2
  129. package/src/Pages/StaticHttp/Request.elm +2 -2
  130. package/src/Pages/StaticHttpRequest.elm +23 -99
  131. package/src/Pages/Url.elm +3 -3
  132. package/src/PagesMsg.elm +88 -0
  133. package/src/QueryParams.elm +21 -172
  134. package/src/RenderRequest.elm +7 -7
  135. package/src/RequestsAndPending.elm +37 -20
  136. package/src/Result/Extra.elm +26 -0
  137. package/src/Scaffold/Form.elm +569 -0
  138. package/src/Scaffold/Route.elm +1431 -0
  139. package/src/Server/Request.elm +476 -1001
  140. package/src/Server/Response.elm +130 -36
  141. package/src/Server/Session.elm +181 -111
  142. package/src/Server/SetCookie.elm +80 -32
  143. package/src/Stub.elm +53 -0
  144. package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
  145. package/src/{Path.elm → UrlPath.elm} +33 -36
  146. package/generator/template/public/images/icon-png.png +0 -0
  147. package/src/DataSource/Env.elm +0 -38
  148. package/src/DataSource/Http.elm +0 -446
  149. package/src/DataSource/Internal/Request.elm +0 -20
  150. package/src/DataSource/Port.elm +0 -90
  151. package/src/DataSource.elm +0 -538
  152. package/src/Form/Field.elm +0 -717
  153. package/src/Form/FieldStatus.elm +0 -36
  154. package/src/Form/FieldView.elm +0 -417
  155. package/src/Form/FormData.elm +0 -22
  156. package/src/Form/Validation.elm +0 -391
  157. package/src/Form/Value.elm +0 -118
  158. package/src/Form.elm +0 -1683
  159. package/src/FormDecoder.elm +0 -102
  160. package/src/Pages/FormState.elm +0 -256
  161. package/src/Pages/Generate.elm +0 -1151
  162. package/src/Pages/Internal/Form.elm +0 -17
  163. package/src/Pages/Msg.elm +0 -79
  164. package/src/Pages/Transition.elm +0 -70
@@ -1,44 +1,43 @@
1
- module Pages.Internal.Platform.Cli exposing (Flags, Model, Msg(..), Program, cliApplication, init, requestDecoder, update)
1
+ module Pages.Internal.Platform.Cli exposing (Flags, Model, Msg(..), Program, cliApplication, init, requestDecoder, update, currentCompatibilityKey)
2
2
 
3
3
  {-| Exposed for internal use only (used in generated code).
4
4
 
5
- @docs Flags, Model, Msg, Program, cliApplication, init, requestDecoder, update
5
+ @docs Flags, Model, Msg, Program, cliApplication, init, requestDecoder, update, currentCompatibilityKey
6
6
 
7
7
  -}
8
8
 
9
- import ApiRoute
9
+ import BackendTask exposing (BackendTask)
10
10
  import BuildError exposing (BuildError)
11
11
  import Bytes exposing (Bytes)
12
12
  import Bytes.Encode
13
13
  import Codec
14
- import DataSource exposing (DataSource)
15
- import Dict
16
- import Head
14
+ import Dict exposing (Dict)
15
+ import FatalError exposing (FatalError)
16
+ import Head exposing (Tag)
17
17
  import Html exposing (Html)
18
18
  import HtmlPrinter
19
19
  import Internal.ApiRoute exposing (ApiRoute(..))
20
+ import Internal.Request
20
21
  import Json.Decode as Decode
21
22
  import Json.Encode
22
23
  import PageServerResponse exposing (PageServerResponse)
23
24
  import Pages.Flags
24
- import Pages.Http
25
+ import Pages.Internal.FatalError
25
26
  import Pages.Internal.NotFoundReason as NotFoundReason exposing (NotFoundReason)
27
+ import Pages.Internal.Platform.CompatibilityKey
26
28
  import Pages.Internal.Platform.Effect as Effect exposing (Effect)
27
- import Pages.Internal.Platform.StaticResponses as StaticResponses exposing (StaticResponses)
29
+ import Pages.Internal.Platform.StaticResponses as StaticResponses
28
30
  import Pages.Internal.Platform.ToJsPayload as ToJsPayload
29
31
  import Pages.Internal.ResponseSketch as ResponseSketch
30
- import Pages.Internal.StaticHttpBody as StaticHttpBody
31
- import Pages.Msg
32
32
  import Pages.ProgramConfig exposing (ProgramConfig)
33
33
  import Pages.SiteConfig exposing (SiteConfig)
34
34
  import Pages.StaticHttp.Request
35
- import Pages.StaticHttpRequest as StaticHttpRequest
36
- import Path exposing (Path)
35
+ import PagesMsg exposing (PagesMsg)
37
36
  import RenderRequest exposing (RenderRequest)
38
37
  import RequestsAndPending exposing (RequestsAndPending)
39
- import Task
40
38
  import TerminalText as Terminal
41
39
  import Url exposing (Url)
40
+ import UrlPath exposing (UrlPath)
42
41
 
43
42
 
44
43
  {-| -}
@@ -46,12 +45,16 @@ type alias Flags =
46
45
  Decode.Value
47
46
 
48
47
 
48
+ {-| -}
49
+ currentCompatibilityKey : Int
50
+ currentCompatibilityKey =
51
+ Pages.Internal.Platform.CompatibilityKey.currentCompatibilityKey
52
+
53
+
49
54
  {-| -}
50
55
  type alias Model route =
51
- { staticResponses : StaticResponses
56
+ { staticResponses : BackendTask FatalError Effect
52
57
  , errors : List BuildError
53
- , allRawResponses : RequestsAndPending
54
- , unprocessedPages : List ( Path, route )
55
58
  , maybeRequestJson : RenderRequest route
56
59
  , isDevServer : Bool
57
60
  }
@@ -59,12 +62,7 @@ type alias Model route =
59
62
 
60
63
  {-| -}
61
64
  type Msg
62
- = GotDataBatch
63
- (List
64
- { request : Pages.StaticHttp.Request.Request
65
- , response : RequestsAndPending.Response
66
- }
67
- )
65
+ = GotDataBatch Decode.Value
68
66
  | GotBuildError BuildError
69
67
 
70
68
 
@@ -105,7 +103,7 @@ cliApplication config =
105
103
  |> Tuple.mapSecond (perform site renderRequest config)
106
104
  , update =
107
105
  \msg model ->
108
- update site config msg model
106
+ update msg model
109
107
  |> Tuple.mapSecond (perform site model.maybeRequestJson config)
110
108
  , subscriptions =
111
109
  \_ ->
@@ -152,35 +150,11 @@ cliApplication config =
152
150
  )
153
151
  |> mergeResult
154
152
  )
155
- , config.gotBatchSub
156
- |> Sub.map
157
- (\newBatch ->
158
- Decode.decodeValue batchDecoder newBatch
159
- |> Result.map GotDataBatch
160
- |> Result.mapError
161
- (\error ->
162
- ("From location 2: "
163
- ++ (error
164
- |> Decode.errorToString
165
- )
166
- )
167
- |> BuildError.internal
168
- |> GotBuildError
169
- )
170
- |> mergeResult
171
- )
153
+ , config.gotBatchSub |> Sub.map GotDataBatch
172
154
  ]
173
155
  }
174
156
 
175
157
 
176
- batchDecoder : Decode.Decoder (List { request : Pages.StaticHttp.Request.Request, response : RequestsAndPending.Response })
177
- batchDecoder =
178
- Decode.map2 (\request response -> { request = request, response = response })
179
- (Decode.field "request" requestDecoder)
180
- (Decode.field "response" RequestsAndPending.decoder)
181
- |> Decode.list
182
-
183
-
184
158
  mergeResult : Result a a -> a
185
159
  mergeResult r =
186
160
  case r of
@@ -237,61 +211,16 @@ perform site renderRequest config effect =
237
211
  Effect.Batch list ->
238
212
  flatten site renderRequest config list
239
213
 
240
- Effect.FetchHttp unmasked ->
241
- if unmasked.url == "$$elm-pages$$headers" then
242
- case
243
- renderRequest
244
- |> RenderRequest.maybeRequestPayload
245
- |> Maybe.map (\json -> RequestsAndPending.Response Nothing (RequestsAndPending.JsonBody json))
246
- |> Result.fromMaybe (Pages.Http.BadUrl "$$elm-pages$$headers is only available on server-side request (not on build).")
247
- of
248
- Ok okResponse ->
249
- Task.succeed
250
- [ { request = unmasked
251
- , response = okResponse
252
- }
253
- ]
254
- |> Task.perform GotDataBatch
255
-
256
- Err error ->
257
- { title = "Static HTTP Error"
258
- , message =
259
- [ Terminal.text "I got an error making an HTTP request to this URL: "
260
-
261
- -- TODO include HTTP method, headers, and body
262
- , Terminal.yellow unmasked.url
263
- , Terminal.text <| Json.Encode.encode 2 <| StaticHttpBody.encode unmasked.body
264
- , Terminal.text "\n\n"
265
- , case error of
266
- Pages.Http.BadStatus metadata body ->
267
- Terminal.text <|
268
- String.join "\n"
269
- [ "Bad status: " ++ String.fromInt metadata.statusCode
270
- , "Status message: " ++ metadata.statusText
271
- , "Body: " ++ body
272
- ]
273
-
274
- Pages.Http.BadUrl _ ->
275
- -- TODO include HTTP method, headers, and body
276
- Terminal.text <| "Invalid url: " ++ unmasked.url
277
-
278
- Pages.Http.Timeout ->
279
- Terminal.text "Timeout"
280
-
281
- Pages.Http.NetworkError ->
282
- Terminal.text "Network error"
283
- ]
284
- , fatal = True
285
- , path = "" -- TODO wire in current path here
286
- }
287
- |> Task.succeed
288
- |> Task.perform GotBuildError
289
-
290
- else
291
- ToJsPayload.DoHttp unmasked unmasked.useCache
292
- |> Codec.encoder (ToJsPayload.successCodecNew2 canonicalSiteUrl "")
293
- |> config.toJsPort
294
- |> Cmd.map never
214
+ Effect.FetchHttp requests ->
215
+ requests
216
+ |> List.map
217
+ (\request ->
218
+ ( Pages.StaticHttp.Request.hash request, request )
219
+ )
220
+ |> ToJsPayload.DoHttp
221
+ |> Codec.encoder (ToJsPayload.successCodecNew2 canonicalSiteUrl "")
222
+ |> config.toJsPort
223
+ |> Cmd.map never
295
224
 
296
225
  Effect.SendSinglePage info ->
297
226
  let
@@ -328,32 +257,25 @@ perform site renderRequest config effect =
328
257
  |> config.sendPageData
329
258
  |> Cmd.map never
330
259
 
331
- Effect.Continue ->
332
- Cmd.none
333
-
334
260
 
335
261
  flagsDecoder :
336
262
  Decode.Decoder
337
263
  { staticHttpCache : RequestsAndPending
338
264
  , isDevServer : Bool
265
+ , compatibilityKey : Int
339
266
  }
340
267
  flagsDecoder =
341
- Decode.map2
342
- (\staticHttpCache isDevServer ->
268
+ Decode.map3
269
+ (\staticHttpCache isDevServer compatibilityKey ->
343
270
  { staticHttpCache = staticHttpCache
344
271
  , isDevServer = isDevServer
272
+ , compatibilityKey = compatibilityKey
345
273
  }
346
274
  )
347
- --(Decode.field "staticHttpCache"
348
- -- (Decode.dict
349
- -- (Decode.string
350
- -- |> Decode.map Just
351
- -- )
352
- -- )
353
- --)
354
275
  -- TODO remove hardcoding and decode staticHttpCache here
355
- (Decode.succeed Dict.empty)
276
+ (Decode.succeed (Json.Encode.object []))
356
277
  (Decode.field "mode" Decode.string |> Decode.map (\mode -> mode == "dev-server"))
278
+ (Decode.field "compatibilityKey" Decode.int)
357
279
 
358
280
 
359
281
  {-| -}
@@ -365,14 +287,42 @@ init :
365
287
  -> ( Model route, Effect )
366
288
  init site renderRequest config flags =
367
289
  case Decode.decodeValue flagsDecoder flags of
368
- Ok { staticHttpCache, isDevServer } ->
369
- initLegacy site renderRequest { staticHttpCache = staticHttpCache, isDevServer = isDevServer } config
290
+ Ok { isDevServer, compatibilityKey } ->
291
+ if compatibilityKey == currentCompatibilityKey then
292
+ initLegacy site renderRequest { isDevServer = isDevServer } config
293
+
294
+ else
295
+ let
296
+ elmPackageAheadOfNpmPackage : Bool
297
+ elmPackageAheadOfNpmPackage =
298
+ currentCompatibilityKey > compatibilityKey
299
+
300
+ message : String
301
+ message =
302
+ "The NPM package and Elm package you have installed are incompatible. If you are updating versions, be sure to update both the elm-pages Elm and NPM package.\n\n"
303
+ ++ (if elmPackageAheadOfNpmPackage then
304
+ "The elm-pages Elm package is ahead of the elm-pages NPM package. Try updating the elm-pages NPM package?"
305
+
306
+ else
307
+ "The elm-pages NPM package is ahead of the elm-pages Elm package. Try updating the elm-pages Elm package?"
308
+ )
309
+ in
310
+ updateAndSendPortIfDone
311
+ { staticResponses = StaticResponses.empty Effect.NoEffect
312
+ , errors =
313
+ [ { title = "Incompatible NPM and Elm package versions"
314
+ , message = [ Terminal.text <| message ]
315
+ , fatal = True
316
+ , path = ""
317
+ }
318
+ ]
319
+ , maybeRequestJson = renderRequest
320
+ , isDevServer = False
321
+ }
370
322
 
371
323
  Err error ->
372
324
  updateAndSendPortIfDone
373
- site
374
- config
375
- { staticResponses = StaticResponses.empty
325
+ { staticResponses = StaticResponses.empty Effect.NoEffect
376
326
  , errors =
377
327
  [ { title = "Internal Error"
378
328
  , message = [ Terminal.text <| "Failed to parse flags: " ++ Decode.errorToString error ]
@@ -380,8 +330,6 @@ init site renderRequest config flags =
380
330
  , path = ""
381
331
  }
382
332
  ]
383
- , allRawResponses = Dict.empty
384
- , unprocessedPages = []
385
333
  , maybeRequestJson = renderRequest
386
334
  , isDevServer = False
387
335
  }
@@ -430,140 +378,506 @@ isActionDecoder =
430
378
  initLegacy :
431
379
  SiteConfig
432
380
  -> RenderRequest route
433
- -> { staticHttpCache : RequestsAndPending, isDevServer : Bool }
381
+ -> { isDevServer : Bool }
434
382
  -> ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
435
383
  -> ( Model route, Effect )
436
- initLegacy site renderRequest { staticHttpCache, isDevServer } config =
384
+ initLegacy site ((RenderRequest.SinglePage includeHtml singleRequest _) as renderRequest) { isDevServer } config =
437
385
  let
438
- staticResponses : StaticResponses
439
- staticResponses =
440
- case renderRequest of
441
- RenderRequest.SinglePage _ singleRequest _ ->
386
+ globalHeadTags : BackendTask FatalError (List Tag)
387
+ globalHeadTags =
388
+ (config.globalHeadTags |> Maybe.withDefault (\_ -> BackendTask.succeed [])) HtmlPrinter.htmlToString
389
+
390
+ staticResponsesNew : BackendTask FatalError Effect
391
+ staticResponsesNew =
392
+ case singleRequest of
393
+ RenderRequest.Page serverRequestPayload ->
442
394
  let
443
- globalHeadTags : DataSource (List Head.Tag)
444
- globalHeadTags =
445
- (config.globalHeadTags |> Maybe.withDefault (\_ -> DataSource.succeed [])) HtmlPrinter.htmlToString
395
+ isAction : Maybe ActionRequest
396
+ isAction =
397
+ renderRequest
398
+ |> RenderRequest.maybeRequestPayload
399
+ |> Maybe.andThen (Decode.decodeValue isActionDecoder >> Result.withDefault Nothing)
400
+
401
+ currentUrl : Url
402
+ currentUrl =
403
+ { protocol = Url.Https
404
+ , host = site.canonicalUrl
405
+ , port_ = Nothing
406
+ , path = serverRequestPayload.path |> UrlPath.toRelative
407
+ , query = Nothing
408
+ , fragment = Nothing
409
+ }
446
410
  in
447
- case singleRequest of
448
- RenderRequest.Page serverRequestPayload ->
449
- let
450
- isAction : Maybe ActionRequest
451
- isAction =
452
- renderRequest
453
- |> RenderRequest.maybeRequestPayload
454
- |> Maybe.andThen (Decode.decodeValue isActionDecoder >> Result.withDefault Nothing)
455
- in
456
- StaticResponses.renderSingleRoute
457
- (case isAction of
458
- Just _ ->
459
- config.action serverRequestPayload.frontmatter
460
- |> DataSource.andThen
461
- (\something ->
462
- case something of
463
- PageServerResponse.ErrorPage _ _ ->
464
- DataSource.succeed something
465
- |> DataSource.map (\_ -> ())
466
-
467
- PageServerResponse.RenderPage _ _ ->
468
- DataSource.map3 (\_ _ _ -> ())
469
- (config.data serverRequestPayload.frontmatter)
470
- config.sharedData
471
- globalHeadTags
472
-
473
- PageServerResponse.ServerResponse _ ->
474
- DataSource.succeed something
475
- |> DataSource.map (\_ -> ())
476
- )
411
+ --case isAction of
412
+ -- Just actionRequest ->
413
+ (if isDevServer then
414
+ config.handleRoute serverRequestPayload.frontmatter
477
415
 
416
+ else
417
+ BackendTask.succeed Nothing
418
+ )
419
+ |> BackendTask.andThen
420
+ (\pageFound ->
421
+ case pageFound of
478
422
  Nothing ->
479
- DataSource.map3 (\_ _ _ -> ())
480
- (config.data serverRequestPayload.frontmatter)
481
- config.sharedData
482
- globalHeadTags
483
- )
484
- (if isDevServer then
485
- config.handleRoute serverRequestPayload.frontmatter
423
+ --sendSinglePageProgress site model.allRawResponses config model payload
424
+ (case isAction of
425
+ Just _ ->
426
+ config.action
427
+ (RenderRequest.maybeRequestPayload renderRequest
428
+ |> Maybe.map Internal.Request.toRequest
429
+ |> Maybe.withDefault Internal.Request.fakeRequest
430
+ )
431
+ serverRequestPayload.frontmatter
432
+ |> BackendTask.map Just
486
433
 
487
- else
488
- DataSource.succeed Nothing
489
- )
434
+ Nothing ->
435
+ BackendTask.succeed Nothing
436
+ )
437
+ |> BackendTask.andThen
438
+ (\something ->
439
+ let
440
+ actionHeaders2 : Maybe { statusCode : Int, headers : List ( String, String ) }
441
+ actionHeaders2 =
442
+ case something of
443
+ Just (PageServerResponse.RenderPage responseThing _) ->
444
+ Just responseThing
445
+
446
+ Just (PageServerResponse.ServerResponse responseThing) ->
447
+ Just
448
+ { headers = responseThing.headers
449
+ , statusCode = responseThing.statusCode
450
+ }
490
451
 
491
- RenderRequest.Api ( path, ApiRoute apiRequest ) ->
492
- StaticResponses.renderApiRequest
493
- (DataSource.map2 (\_ _ -> ())
494
- (apiRequest.matchesToResponse path)
495
- globalHeadTags
496
- )
452
+ _ ->
453
+ Nothing
497
454
 
498
- RenderRequest.NotFound _ ->
499
- StaticResponses.renderApiRequest
500
- (DataSource.map2 (\_ _ -> ())
501
- (DataSource.succeed [])
502
- globalHeadTags
503
- )
455
+ maybeRedirectResponse : Maybe Effect
456
+ maybeRedirectResponse =
457
+ actionHeaders2
458
+ |> Maybe.andThen
459
+ (\responseMetadata ->
460
+ toRedirectResponse config
461
+ serverRequestPayload
462
+ includeHtml
463
+ responseMetadata
464
+ responseMetadata
465
+ )
466
+ in
467
+ case maybeRedirectResponse of
468
+ Just redirectResponse ->
469
+ redirectResponse
470
+ |> BackendTask.succeed
504
471
 
505
- unprocessedPages : List ( Path, route )
506
- unprocessedPages =
507
- case renderRequest of
508
- RenderRequest.SinglePage _ serverRequestPayload _ ->
509
- case serverRequestPayload of
510
- RenderRequest.Page pageData ->
511
- [ ( pageData.path, pageData.frontmatter ) ]
472
+ Nothing ->
473
+ BackendTask.map3
474
+ (\pageData sharedData tags ->
475
+ let
476
+ renderedResult : Effect
477
+ renderedResult =
478
+ case pageData of
479
+ PageServerResponse.RenderPage responseInfo pageData_ ->
480
+ let
481
+ currentPage : { path : UrlPath, route : route }
482
+ currentPage =
483
+ { path = serverRequestPayload.path, route = urlToRoute config currentUrl }
484
+
485
+ maybeActionData : Maybe actionData
486
+ maybeActionData =
487
+ case something of
488
+ Just (PageServerResponse.RenderPage _ actionThing) ->
489
+ Just actionThing
490
+
491
+ _ ->
492
+ Nothing
493
+
494
+ pageModel : userModel
495
+ pageModel =
496
+ config.init
497
+ Pages.Flags.PreRenderFlags
498
+ sharedData
499
+ pageData_
500
+ maybeActionData
501
+ (Just
502
+ { path =
503
+ { path = currentPage.path
504
+ , query = Nothing
505
+ , fragment = Nothing
506
+ }
507
+ , metadata = currentPage.route
508
+ , pageUrl = Nothing
509
+ }
510
+ )
511
+ |> Tuple.first
512
+
513
+ viewValue : { title : String, body : List (Html (PagesMsg userMsg)) }
514
+ viewValue =
515
+ (config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData_ maybeActionData |> .view) pageModel
516
+
517
+ responseMetadata : { statusCode : Int, headers : List ( String, String ) }
518
+ responseMetadata =
519
+ actionHeaders2 |> Maybe.withDefault responseInfo
520
+ in
521
+ (case isAction of
522
+ Just actionRequestKind ->
523
+ let
524
+ actionDataResult : Maybe (PageServerResponse actionData errorPage)
525
+ actionDataResult =
526
+ something
527
+ in
528
+ case actionDataResult of
529
+ Just (PageServerResponse.RenderPage ignored2 actionData_) ->
530
+ case actionRequestKind of
531
+ ActionResponseRequest ->
532
+ ( ignored2.headers
533
+ , ResponseSketch.HotUpdate pageData_ sharedData (Just actionData_)
534
+ |> config.encodeResponse
535
+ |> Bytes.Encode.encode
536
+ )
537
+
538
+ ActionOnlyRequest ->
539
+ ---- TODO need to encode action data when only that is requested (not ResponseSketch?)
540
+ ( ignored2.headers
541
+ , actionData_
542
+ |> config.encodeAction
543
+ |> Bytes.Encode.encode
544
+ )
545
+
546
+ _ ->
547
+ ( responseMetadata.headers
548
+ , Bytes.Encode.encode (Bytes.Encode.unsignedInt8 0)
549
+ )
550
+
551
+ Nothing ->
552
+ ( responseMetadata.headers
553
+ , ResponseSketch.HotUpdate pageData_ sharedData Nothing
554
+ |> config.encodeResponse
555
+ |> Bytes.Encode.encode
556
+ )
557
+ )
558
+ |> (\( actionHeaders, byteEncodedPageData ) ->
559
+ let
560
+ rendered : { view : userModel -> { title : String, body : List (Html (PagesMsg userMsg)) }, head : List Tag }
561
+ rendered =
562
+ config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData_ maybeActionData
563
+ in
564
+ PageServerResponse.toRedirect responseMetadata
565
+ |> Maybe.map
566
+ (\{ location } ->
567
+ location
568
+ |> ResponseSketch.Redirect
569
+ |> config.encodeResponse
570
+ |> Bytes.Encode.encode
571
+ )
572
+ -- TODO handle other cases besides redirects?
573
+ |> Maybe.withDefault byteEncodedPageData
574
+ |> (\encodedData ->
575
+ { route = currentPage.path |> UrlPath.toRelative
576
+ , contentJson = Dict.empty
577
+ , html = viewValue.body |> bodyToString
578
+ , errors = []
579
+ , head = rendered.head ++ tags
580
+ , title = viewValue.title
581
+ , staticHttpCache = Dict.empty
582
+ , is404 = False
583
+ , statusCode =
584
+ case includeHtml of
585
+ RenderRequest.OnlyJson ->
586
+ 200
587
+
588
+ RenderRequest.HtmlAndJson ->
589
+ responseMetadata.statusCode
590
+ , headers =
591
+ -- TODO should `responseInfo.headers` be used? Is there a problem in the case where there is both an action and data response in one? Do we need to make sure it is performed as two separate HTTP requests to ensure that the cookies are set correctly in that case?
592
+ actionHeaders
593
+ |> combineHeaders
594
+ }
595
+ |> ToJsPayload.PageProgress
596
+ |> Effect.SendSinglePageNew encodedData
597
+ )
598
+ )
599
+
600
+ PageServerResponse.ServerResponse serverResponse ->
601
+ --PageServerResponse.ServerResponse serverResponse
602
+ -- TODO handle error?
603
+ let
604
+ responseMetadata : PageServerResponse.Response
605
+ responseMetadata =
606
+ case something of
607
+ Just (PageServerResponse.ServerResponse responseThing) ->
608
+ responseThing
609
+
610
+ _ ->
611
+ serverResponse
612
+ in
613
+ toRedirectResponse config serverRequestPayload includeHtml serverResponse responseMetadata
614
+ |> Maybe.withDefault
615
+ ({ body = serverResponse |> PageServerResponse.toJson
616
+ , staticHttpCache = Dict.empty
617
+ , statusCode = serverResponse.statusCode
618
+ }
619
+ |> ToJsPayload.SendApiResponse
620
+ |> Effect.SendSinglePage
621
+ )
622
+
623
+ PageServerResponse.ErrorPage error record ->
624
+ let
625
+ currentPage : { path : UrlPath, route : route }
626
+ currentPage =
627
+ { path = serverRequestPayload.path, route = urlToRoute config currentUrl }
628
+
629
+ pageModel : userModel
630
+ pageModel =
631
+ config.init
632
+ Pages.Flags.PreRenderFlags
633
+ sharedData
634
+ pageData2
635
+ Nothing
636
+ (Just
637
+ { path =
638
+ { path = currentPage.path
639
+ , query = Nothing
640
+ , fragment = Nothing
641
+ }
642
+ , metadata = currentPage.route
643
+ , pageUrl = Nothing
644
+ }
645
+ )
646
+ |> Tuple.first
647
+
648
+ pageData2 : pageData
649
+ pageData2 =
650
+ config.errorPageToData error
651
+
652
+ viewValue : { title : String, body : List (Html (PagesMsg userMsg)) }
653
+ viewValue =
654
+ (config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData2 Nothing |> .view) pageModel
655
+ in
656
+ (ResponseSketch.HotUpdate pageData2 sharedData Nothing
657
+ |> config.encodeResponse
658
+ |> Bytes.Encode.encode
659
+ )
660
+ |> (\encodedData ->
661
+ { route = currentPage.path |> UrlPath.toRelative
662
+ , contentJson = Dict.empty
663
+ , html = viewValue.body |> bodyToString
664
+ , errors = []
665
+ , head = tags
666
+ , title = viewValue.title
667
+ , staticHttpCache = Dict.empty
668
+ , is404 = False
669
+ , statusCode =
670
+ case includeHtml of
671
+ RenderRequest.OnlyJson ->
672
+ 200
673
+
674
+ RenderRequest.HtmlAndJson ->
675
+ config.errorStatusCode error
676
+ , headers = record.headers |> combineHeaders
677
+ }
678
+ |> ToJsPayload.PageProgress
679
+ |> Effect.SendSinglePageNew encodedData
680
+ )
681
+ in
682
+ renderedResult
683
+ )
684
+ (config.data
685
+ (RenderRequest.maybeRequestPayload renderRequest
686
+ |> Maybe.map Internal.Request.toRequest
687
+ |> Maybe.withDefault Internal.Request.fakeRequest
688
+ )
689
+ serverRequestPayload.frontmatter
690
+ )
691
+ config.sharedData
692
+ globalHeadTags
693
+ )
694
+ |> BackendTask.onError
695
+ (\((Pages.Internal.FatalError.FatalError fatalError) as error) ->
696
+ let
697
+ isPreRendered : Bool
698
+ isPreRendered =
699
+ let
700
+ keys : Int
701
+ keys =
702
+ RenderRequest.maybeRequestPayload renderRequest |> Maybe.map (Decode.decodeValue (Decode.keyValuePairs Decode.value)) |> Maybe.withDefault (Ok []) |> Result.withDefault [] |> List.map Tuple.first |> List.length
703
+ in
704
+ -- TODO this is a bit hacky, would be nice to clean up the way of checking whether this is server-rendered or pre-rendered
705
+ keys <= 1
706
+ in
707
+ if isDevServer || isPreRendered then
708
+ -- we want to stop the build for pre-rendered routes, and give a dev server error popup in the dev server
709
+ BackendTask.fail error
710
+
711
+ else
712
+ --only render the production ErrorPage in production server-rendered Routes
713
+ config.sharedData
714
+ |> BackendTask.andThen
715
+ (\justSharedData ->
716
+ let
717
+ errorPage : errorPage
718
+ errorPage =
719
+ config.internalError fatalError.body
720
+
721
+ dataThing : pageData
722
+ dataThing =
723
+ errorPage
724
+ |> config.errorPageToData
725
+
726
+ statusCode : Int
727
+ statusCode =
728
+ config.errorStatusCode errorPage
729
+
730
+ byteEncodedPageData : Bytes
731
+ byteEncodedPageData =
732
+ ResponseSketch.HotUpdate
733
+ dataThing
734
+ justSharedData
735
+ -- TODO remove shared action data
736
+ Nothing
737
+ |> config.encodeResponse
738
+ |> Bytes.Encode.encode
512
739
 
513
- RenderRequest.Api _ ->
514
- []
740
+ pageModel : userModel
741
+ pageModel =
742
+ config.init
743
+ Pages.Flags.PreRenderFlags
744
+ justSharedData
745
+ dataThing
746
+ Nothing
747
+ (Just
748
+ { path =
749
+ { path = currentPage.path
750
+ , query = Nothing
751
+ , fragment = Nothing
752
+ }
753
+ , metadata = currentPage.route
754
+ , pageUrl = Nothing
755
+ }
756
+ )
757
+ |> Tuple.first
758
+
759
+ currentPage : { path : UrlPath, route : route }
760
+ currentPage =
761
+ { path = serverRequestPayload.path, route = urlToRoute config currentUrl }
762
+
763
+ viewValue : { title : String, body : List (Html (PagesMsg userMsg)) }
764
+ viewValue =
765
+ (config.view Dict.empty Dict.empty Nothing currentPage Nothing justSharedData dataThing Nothing |> .view)
766
+ pageModel
767
+ in
768
+ { route = UrlPath.toAbsolute currentPage.path
769
+ , contentJson = Dict.empty
770
+ , html = viewValue.body |> bodyToString
771
+ , errors = []
772
+ , head = [] -- TODO render head tags --config.view Dict.empty Dict.empty Nothing pathAndRoute Nothing justSharedData pageData Nothing |> .head
773
+ , title = viewValue.title
774
+ , staticHttpCache = Dict.empty
775
+ , is404 = False
776
+ , statusCode = statusCode
777
+ , headers = Dict.empty
778
+ }
779
+ |> ToJsPayload.PageProgress
780
+ |> Effect.SendSinglePageNew byteEncodedPageData
781
+ |> BackendTask.succeed
782
+ )
783
+ )
515
784
 
516
- RenderRequest.NotFound _ ->
517
- []
785
+ Just notFoundReason ->
786
+ render404Page config
787
+ Nothing
788
+ -- TODO do I need sharedDataResult?
789
+ --(Result.toMaybe sharedDataResult)
790
+ isDevServer
791
+ serverRequestPayload.path
792
+ notFoundReason
793
+ |> BackendTask.succeed
794
+ )
795
+
796
+ RenderRequest.Api ( path, ApiRoute apiHandler ) ->
797
+ BackendTask.map2
798
+ (\response _ ->
799
+ case response of
800
+ Just okResponse ->
801
+ { body = okResponse
802
+ , staticHttpCache = Dict.empty -- TODO do I need to serialize the full cache here, or can I handle that from the JS side?
803
+ , statusCode = 200
804
+ }
805
+ |> ToJsPayload.SendApiResponse
806
+ |> Effect.SendSinglePage
807
+
808
+ Nothing ->
809
+ render404Page config
810
+ -- TODO do I need sharedDataResult here?
811
+ Nothing
812
+ isDevServer
813
+ (UrlPath.fromString path)
814
+ NotFoundReason.NoMatchingRoute
815
+ --Err error ->
816
+ -- [ error ]
817
+ -- |> ToJsPayload.Errors
818
+ -- |> Effect.SendSinglePage
819
+ )
820
+ (apiHandler.matchesToResponse
821
+ (renderRequest
822
+ |> RenderRequest.maybeRequestPayload
823
+ |> Maybe.withDefault Json.Encode.null
824
+ )
825
+ path
826
+ )
827
+ globalHeadTags
828
+
829
+ RenderRequest.NotFound notFoundPath ->
830
+ (BackendTask.map2
831
+ (\_ _ ->
832
+ render404Page config
833
+ Nothing
834
+ --(Result.toMaybe sharedDataResult)
835
+ --model
836
+ isDevServer
837
+ notFoundPath
838
+ NotFoundReason.NoMatchingRoute
839
+ )
840
+ (BackendTask.succeed [])
841
+ globalHeadTags
842
+ -- TODO is there a way to resolve sharedData but get it as a Result if it fails?
843
+ --config.sharedData
844
+ )
518
845
 
519
846
  initialModel : Model route
520
847
  initialModel =
521
- { staticResponses = staticResponses
848
+ { staticResponses = staticResponsesNew
522
849
  , errors = []
523
- , allRawResponses = staticHttpCache
524
- , unprocessedPages = unprocessedPages
525
850
  , maybeRequestJson = renderRequest
526
851
  , isDevServer = isDevServer
527
852
  }
528
853
  in
529
- StaticResponses.nextStep initialModel Nothing
530
- |> nextStepToEffect site
531
- config
854
+ StaticResponses.nextStep (Json.Encode.object []) initialModel.staticResponses initialModel
855
+ |> nextStepToEffect
532
856
  initialModel
533
857
 
534
858
 
535
859
  updateAndSendPortIfDone :
536
- SiteConfig
537
- -> ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
538
- -> Model route
860
+ Model route
539
861
  -> ( Model route, Effect )
540
- updateAndSendPortIfDone site config model =
541
- StaticResponses.nextStep
862
+ updateAndSendPortIfDone model =
863
+ StaticResponses.nextStep (Json.Encode.object [])
864
+ model.staticResponses
542
865
  model
543
- Nothing
544
- |> nextStepToEffect site config model
866
+ |> nextStepToEffect model
545
867
 
546
868
 
547
869
  {-| -}
548
870
  update :
549
- SiteConfig
550
- -> ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
551
- -> Msg
871
+ Msg
552
872
  -> Model route
553
873
  -> ( Model route, Effect )
554
- update site config msg model =
874
+ update msg model =
555
875
  case msg of
556
876
  GotDataBatch batch ->
557
- let
558
- updatedModel : Model route
559
- updatedModel =
560
- model
561
- |> StaticResponses.batchUpdate batch
562
- in
563
- StaticResponses.nextStep
564
- updatedModel
565
- Nothing
566
- |> nextStepToEffect site config updatedModel
877
+ StaticResponses.nextStep batch
878
+ model.staticResponses
879
+ model
880
+ |> nextStepToEffect model
567
881
 
568
882
  GotBuildError buildError ->
569
883
  let
@@ -574,560 +888,45 @@ update site config msg model =
574
888
  buildError :: model.errors
575
889
  }
576
890
  in
577
- StaticResponses.nextStep
891
+ StaticResponses.nextStep (Json.Encode.object [])
892
+ updatedModel.staticResponses
578
893
  updatedModel
579
- Nothing
580
- |> nextStepToEffect site config updatedModel
894
+ |> nextStepToEffect updatedModel
581
895
 
582
896
 
583
897
  nextStepToEffect :
584
- SiteConfig
585
- -> ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
586
- -> Model route
587
- -> ( StaticResponses, StaticResponses.NextStep route )
898
+ Model route
899
+ -> StaticResponses.NextStep route Effect
588
900
  -> ( Model route, Effect )
589
- nextStepToEffect site config model ( updatedStaticResponsesModel, nextStep ) =
901
+ nextStepToEffect model nextStep =
590
902
  case nextStep of
591
- StaticResponses.Continue updatedAllRawResponses httpRequests maybeRoutes ->
592
- let
593
- updatedUnprocessedPages : List ( Path, route )
594
- updatedUnprocessedPages =
595
- case maybeRoutes of
596
- Just newRoutes ->
597
- newRoutes
598
- |> List.map
599
- (\route ->
600
- ( Path.join (config.routeToPath route)
601
- , route
602
- )
603
- )
604
-
605
- Nothing ->
606
- model.unprocessedPages
607
-
608
- updatedModel : Model route
609
- updatedModel =
610
- { model
611
- | allRawResponses = updatedAllRawResponses
612
- , staticResponses = updatedStaticResponsesModel
613
- , unprocessedPages = updatedUnprocessedPages
614
- }
615
- in
616
- if List.isEmpty httpRequests then
617
- nextStepToEffect site
618
- config
619
- updatedModel
620
- (StaticResponses.nextStep
621
- updatedModel
622
- Nothing
623
- )
624
-
625
- else
626
- ( updatedModel
627
- , (httpRequests
628
- |> List.map Effect.FetchHttp
629
- )
630
- |> Effect.Batch
631
- )
632
-
633
- StaticResponses.Finish toJsPayload ->
634
- case toJsPayload of
635
- StaticResponses.ApiResponse ->
636
- let
637
- apiResponse : Effect
638
- apiResponse =
639
- case model.maybeRequestJson of
640
- RenderRequest.SinglePage _ requestPayload _ ->
641
- let
642
- sharedDataResult : Result BuildError sharedData
643
- sharedDataResult =
644
- StaticHttpRequest.resolve
645
- config.sharedData
646
- model.allRawResponses
647
- |> Result.mapError (StaticHttpRequest.toBuildError "")
648
- in
649
- case requestPayload of
650
- RenderRequest.Api ( path, ApiRoute apiHandler ) ->
651
- let
652
- thing : DataSource (Maybe ApiRoute.Response)
653
- thing =
654
- apiHandler.matchesToResponse path
655
- in
656
- StaticHttpRequest.resolve
657
- thing
658
- model.allRawResponses
659
- |> Result.mapError (StaticHttpRequest.toBuildError "TODO - path from request")
660
- |> (\response ->
661
- case response of
662
- Ok (Just okResponse) ->
663
- { body = okResponse
664
- , staticHttpCache = Dict.empty -- TODO do I need to serialize the full cache here, or can I handle that from the JS side?
665
-
666
- -- model.allRawResponses |> Dict.Extra.filterMap (\_ v -> v)
667
- , statusCode = 200
668
- }
669
- |> ToJsPayload.SendApiResponse
670
- |> Effect.SendSinglePage
671
-
672
- Ok Nothing ->
673
- render404Page config (Result.toMaybe sharedDataResult) model (Path.fromString path) NotFoundReason.NoMatchingRoute
674
-
675
- Err error ->
676
- [ error ]
677
- |> ToJsPayload.Errors
678
- |> Effect.SendSinglePage
679
- )
680
-
681
- RenderRequest.Page payload ->
682
- let
683
- pageFoundResult : Result BuildError (Maybe NotFoundReason)
684
- pageFoundResult =
685
- StaticHttpRequest.resolve
686
- (if model.isDevServer then
687
- -- TODO OPTIMIZATION this is redundant
688
- config.handleRoute payload.frontmatter
689
-
690
- else
691
- DataSource.succeed Nothing
692
- )
693
- model.allRawResponses
694
- |> Result.mapError (StaticHttpRequest.toBuildError (payload.path |> Path.toAbsolute))
695
- in
696
- case pageFoundResult of
697
- Ok Nothing ->
698
- sendSinglePageProgress site model.allRawResponses config model payload
699
-
700
- Ok (Just notFoundReason) ->
701
- render404Page config
702
- --Nothing
703
- (Result.toMaybe sharedDataResult)
704
- model
705
- payload.path
706
- notFoundReason
707
-
708
- Err error ->
709
- [ error ] |> ToJsPayload.Errors |> Effect.SendSinglePage
710
-
711
- RenderRequest.NotFound path ->
712
- render404Page config
713
- --Nothing
714
- (Result.toMaybe sharedDataResult)
715
- model
716
- path
717
- NotFoundReason.NoMatchingRoute
718
- in
719
- ( model
720
- , apiResponse
721
- )
722
-
723
- StaticResponses.Errors errors ->
724
- ( model
725
- , errors |> ToJsPayload.Errors |> Effect.SendSinglePage
726
- )
727
-
728
-
729
- sendSinglePageProgress :
730
- SiteConfig
731
- -> RequestsAndPending
732
- -> ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
733
- -> Model route
734
- -> { path : Path, frontmatter : route }
735
- -> Effect
736
- sendSinglePageProgress site contentJson config model info =
737
- let
738
- ( page, route ) =
739
- ( info.path, info.frontmatter )
740
- in
741
- case model.maybeRequestJson of
742
- RenderRequest.SinglePage includeHtml _ _ ->
743
- let
744
- isAction : Maybe ActionRequest
745
- isAction =
746
- model.maybeRequestJson
747
- |> RenderRequest.maybeRequestPayload
748
- |> Maybe.andThen (Decode.decodeValue isActionDecoder >> Result.withDefault Nothing)
749
-
750
- pageFoundResult : Result BuildError (Maybe NotFoundReason)
751
- pageFoundResult =
752
- -- TODO OPTIMIZATION this is redundant
753
- StaticHttpRequest.resolve
754
- (if model.isDevServer then
755
- config.handleRoute route
756
-
757
- else
758
- DataSource.succeed Nothing
759
- )
760
- model.allRawResponses
761
- |> Result.mapError (StaticHttpRequest.toBuildError currentUrl.path)
762
-
763
- renderedResult : Result BuildError (PageServerResponse { head : List Head.Tag, view : String, title : String } errorPage)
764
- renderedResult =
765
- case includeHtml of
766
- RenderRequest.OnlyJson ->
767
- pageDataResult
768
- |> Result.map
769
- (\okPageData ->
770
- case okPageData of
771
- PageServerResponse.RenderPage responseInfo _ ->
772
- PageServerResponse.RenderPage
773
- { statusCode = responseInfo.statusCode
774
- , headers = responseInfo.headers
775
- }
776
- { head = []
777
- , view = "This page was not rendered because it is a JSON-only request."
778
- , title = "This page was not rendered because it is a JSON-only request."
779
- }
780
-
781
- PageServerResponse.ServerResponse serverResponse ->
782
- PageServerResponse.ServerResponse serverResponse
783
-
784
- PageServerResponse.ErrorPage error record ->
785
- PageServerResponse.ErrorPage error record
786
- )
787
-
788
- RenderRequest.HtmlAndJson ->
789
- Result.map2 Tuple.pair pageDataResult sharedDataResult
790
- |> Result.map
791
- (\( pageData_, sharedData ) ->
792
- case pageData_ of
793
- PageServerResponse.RenderPage responseInfo pageData ->
794
- let
795
- currentPage : { path : Path, route : route }
796
- currentPage =
797
- { path = page, route = urlToRoute config currentUrl }
798
-
799
- maybeActionData : Maybe actionData
800
- maybeActionData =
801
- case isAction of
802
- Just _ ->
803
- case actionDataResult of
804
- Ok (PageServerResponse.RenderPage _ actionData) ->
805
- Just actionData
806
-
807
- _ ->
808
- Nothing
809
-
810
- Nothing ->
811
- Nothing
812
-
813
- pageModel : userModel
814
- pageModel =
815
- config.init
816
- Pages.Flags.PreRenderFlags
817
- sharedData
818
- pageData
819
- maybeActionData
820
- (Just
821
- { path =
822
- { path = currentPage.path
823
- , query = Nothing
824
- , fragment = Nothing
825
- }
826
- , metadata = currentPage.route
827
- , pageUrl = Nothing
828
- }
829
- )
830
- |> Tuple.first
831
-
832
- viewValue : { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
833
- viewValue =
834
- (config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData maybeActionData |> .view) pageModel
835
- in
836
- PageServerResponse.RenderPage responseInfo
837
- { head = config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData maybeActionData |> .head
838
- , view = viewValue.body |> bodyToString
839
- , title = viewValue.title
840
- }
841
-
842
- PageServerResponse.ServerResponse serverResponse ->
843
- PageServerResponse.ServerResponse serverResponse
844
-
845
- PageServerResponse.ErrorPage error record ->
846
- let
847
- currentPage : { path : Path, route : route }
848
- currentPage =
849
- { path = page, route = urlToRoute config currentUrl }
850
-
851
- pageModel : userModel
852
- pageModel =
853
- config.init
854
- Pages.Flags.PreRenderFlags
855
- sharedData
856
- pageData
857
- Nothing
858
- (Just
859
- { path =
860
- { path = currentPage.path
861
- , query = Nothing
862
- , fragment = Nothing
863
- }
864
- , metadata = currentPage.route
865
- , pageUrl = Nothing
866
- }
867
- )
868
- |> Tuple.first
869
-
870
- pageData : pageData
871
- pageData =
872
- config.errorPageToData error
873
-
874
- viewValue : { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
875
- viewValue =
876
- (config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData Nothing |> .view) pageModel
877
- in
878
- PageServerResponse.RenderPage
879
- { statusCode = config.errorStatusCode error
880
- , headers = record.headers
881
- }
882
- { head = config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData Nothing |> .head
883
- , view = viewValue.body |> List.map (HtmlPrinter.htmlToString Nothing) |> String.join "\n"
884
- , title = viewValue.title
885
- }
886
- )
887
-
888
- currentUrl : Url
889
- currentUrl =
890
- { protocol = Url.Https
891
- , host = site.canonicalUrl
892
- , port_ = Nothing
893
- , path = page |> Path.toRelative
894
- , query = Nothing
895
- , fragment = Nothing
896
- }
897
-
898
- pageDataResult : Result BuildError (PageServerResponse pageData errorPage)
899
- pageDataResult =
900
- -- TODO OPTIMIZATION can these three be included in StaticResponses.Finish?
901
- StaticHttpRequest.resolve
902
- (case isAction of
903
- Just _ ->
904
- config.action (urlToRoute config currentUrl)
905
- |> DataSource.andThen
906
- (\something ->
907
- case something of
908
- PageServerResponse.ErrorPage a b ->
909
- PageServerResponse.ErrorPage a b
910
- |> DataSource.succeed
911
-
912
- PageServerResponse.RenderPage _ _ ->
913
- -- TODO the headers/response codes are ignored from the action here
914
- -- is that okay? Should you always do a redirect or another kind of
915
- -- server response if you want to control the headers/response code for an action (like logout & redirect, for example)?
916
- config.data (urlToRoute config currentUrl)
917
-
918
- PageServerResponse.ServerResponse a ->
919
- PageServerResponse.ServerResponse a
920
- |> DataSource.succeed
921
- )
922
-
923
- Nothing ->
924
- config.data (urlToRoute config currentUrl)
925
- )
926
- contentJson
927
- |> Result.mapError (StaticHttpRequest.toBuildError currentUrl.path)
928
-
929
- actionDataResult : Result BuildError (PageServerResponse actionData errorPage)
930
- actionDataResult =
931
- -- TODO OPTIMIZATION can these three be included in StaticResponses.Finish?
932
- StaticHttpRequest.resolve
933
- (config.action (urlToRoute config currentUrl))
934
- contentJson
935
- |> Result.mapError (StaticHttpRequest.toBuildError currentUrl.path)
936
-
937
- sharedDataResult : Result BuildError sharedData
938
- sharedDataResult =
939
- StaticHttpRequest.resolve
940
- config.sharedData
941
- contentJson
942
- |> Result.mapError (StaticHttpRequest.toBuildError currentUrl.path)
943
-
944
- globalHeadTags : DataSource (List Head.Tag)
945
- globalHeadTags =
946
- (config.globalHeadTags |> Maybe.withDefault (\_ -> DataSource.succeed [])) HtmlPrinter.htmlToString
947
-
948
- siteDataResult : Result BuildError (List Head.Tag)
949
- siteDataResult =
950
- StaticHttpRequest.resolve
951
- globalHeadTags
952
- model.allRawResponses
953
- |> Result.mapError (StaticHttpRequest.toBuildError "Site.elm")
954
- in
955
- case Result.map3 (\a b c -> ( a, b, c )) pageFoundResult renderedResult siteDataResult of
956
- Ok ( maybeNotFoundReason, renderedOrApiResponse, siteData ) ->
957
- case maybeNotFoundReason of
958
- Nothing ->
959
- let
960
- ( actionHeaders, byteEncodedPageData ) =
961
- case pageDataResult of
962
- Ok pageServerResponse ->
963
- case pageServerResponse of
964
- PageServerResponse.RenderPage ignored1 pageData ->
965
- -- TODO want to encode both shared and page data in dev server and HTML-embedded data
966
- -- but not for writing out the content.dat files - would be good to optimize this redundant data out
967
- --if model.isDevServer then
968
- case isAction of
969
- Just actionRequestKind ->
970
- case actionDataResult of
971
- Ok (PageServerResponse.RenderPage ignored2 actionData) ->
972
- case actionRequestKind of
973
- ActionResponseRequest ->
974
- ( ignored2.headers
975
- , sharedDataResult
976
- |> Result.map (\sharedData -> ResponseSketch.HotUpdate pageData sharedData (Just actionData))
977
- |> Result.withDefault (ResponseSketch.RenderPage pageData (Just actionData))
978
- |> config.encodeResponse
979
- |> Bytes.Encode.encode
980
- )
981
-
982
- ActionOnlyRequest ->
983
- ---- TODO need to encode action data when only that is requested (not ResponseSketch?)
984
- ( ignored2.headers
985
- , actionData
986
- |> config.encodeAction
987
- |> Bytes.Encode.encode
988
- )
989
-
990
- _ ->
991
- ( ignored1.headers
992
- , Bytes.Encode.encode (Bytes.Encode.unsignedInt8 0)
993
- )
994
-
995
- Nothing ->
996
- ( ignored1.headers
997
- , sharedDataResult
998
- |> Result.map (\something -> ResponseSketch.HotUpdate pageData something Nothing)
999
- |> Result.withDefault (ResponseSketch.RenderPage pageData Nothing)
1000
- |> config.encodeResponse
1001
- |> Bytes.Encode.encode
1002
- )
1003
-
1004
- --else
1005
- -- pageData
1006
- -- |> ResponseSketch.RenderPage
1007
- -- |> config.encodeResponse
1008
- -- |> Bytes.Encode.encode
1009
- PageServerResponse.ServerResponse serverResponse ->
1010
- -- TODO handle error?
1011
- ( serverResponse.headers
1012
- , PageServerResponse.toRedirect serverResponse
1013
- |> Maybe.map
1014
- (\{ location } ->
1015
- location
1016
- |> ResponseSketch.Redirect
1017
- |> config.encodeResponse
1018
- )
1019
- -- TODO handle other cases besides redirects?
1020
- |> Maybe.withDefault (Bytes.Encode.unsignedInt8 0)
1021
- |> Bytes.Encode.encode
1022
- )
1023
-
1024
- PageServerResponse.ErrorPage error { headers } ->
1025
- -- TODO this case should never happen
1026
- ( headers
1027
- , sharedDataResult
1028
- |> Result.map
1029
- (\sharedData ->
1030
- ResponseSketch.HotUpdate (config.errorPageToData error)
1031
- sharedData
1032
- Nothing
1033
- )
1034
- |> Result.map config.encodeResponse
1035
- |> Result.map Bytes.Encode.encode
1036
- |> Result.withDefault (Bytes.Encode.encode (Bytes.Encode.unsignedInt8 0))
1037
- )
1038
-
1039
- _ ->
1040
- -- TODO handle error?
1041
- ( []
1042
- , Bytes.Encode.encode (Bytes.Encode.unsignedInt8 0)
1043
- )
1044
- in
1045
- case renderedOrApiResponse of
1046
- PageServerResponse.RenderPage responseInfo rendered ->
1047
- { route = page |> Path.toRelative
1048
- , contentJson = Dict.empty
1049
- , html = rendered.view
1050
- , errors = []
1051
- , head = rendered.head ++ siteData
1052
- , title = rendered.title
1053
- , staticHttpCache = Dict.empty
1054
- , is404 = False
1055
- , statusCode = responseInfo.statusCode
1056
- , headers =
1057
- -- TODO should `responseInfo.headers` be used? Is there a problem in the case where there is both an action and data response in one? Do we need to make sure it is performed as two separate HTTP requests to ensure that the cookies are set correctly in that case?
1058
- actionHeaders
1059
- }
1060
- |> ToJsPayload.PageProgress
1061
- |> Effect.SendSinglePageNew byteEncodedPageData
1062
-
1063
- PageServerResponse.ServerResponse serverResponse ->
1064
- PageServerResponse.toRedirect serverResponse
1065
- |> Maybe.map
1066
- (\_ ->
1067
- { route = page |> Path.toRelative
1068
- , contentJson = Dict.empty
1069
- , html = "This is intentionally blank HTML"
1070
- , errors = []
1071
- , head = []
1072
- , title = "This is an intentionally blank title"
1073
- , staticHttpCache = Dict.empty
1074
- , is404 = False
1075
- , statusCode =
1076
- case includeHtml of
1077
- RenderRequest.OnlyJson ->
1078
- -- if this is a redirect for a `content.dat`, we don't want to send an *actual* redirect status code because the redirect needs to be handled in Elm (not by the Browser)
1079
- 200
1080
-
1081
- RenderRequest.HtmlAndJson ->
1082
- serverResponse.statusCode
1083
- , headers = serverResponse.headers
1084
- }
1085
- |> ToJsPayload.PageProgress
1086
- |> Effect.SendSinglePageNew byteEncodedPageData
1087
- )
1088
- |> Maybe.withDefault
1089
- ({ body = serverResponse |> PageServerResponse.toJson
1090
- , staticHttpCache = Dict.empty
1091
- , statusCode = serverResponse.statusCode
1092
- }
1093
- |> ToJsPayload.SendApiResponse
1094
- |> Effect.SendSinglePage
1095
- )
1096
-
1097
- PageServerResponse.ErrorPage error responseInfo ->
1098
- -- TODO this case should never happen
1099
- { route = page |> Path.toRelative
1100
- , contentJson = Dict.empty
1101
- , html = "UNEXPECTED!" --HtmlPrinter.htmlToString rendered.body
1102
- , errors = []
1103
- , head = [] -- rendered.head ++ siteData -- TODO this should call ErrorPage.head maybe?
1104
- , title = "UNEXPECTED CASE" --rendered.title
1105
- , staticHttpCache = Dict.empty
1106
- , is404 = False
1107
- , statusCode = config.errorStatusCode error
1108
- , headers = responseInfo.headers
1109
- }
1110
- |> ToJsPayload.PageProgress
1111
- |> Effect.SendSinglePageNew byteEncodedPageData
903
+ StaticResponses.Continue httpRequests updatedStaticResponsesModel ->
904
+ ( { model
905
+ | staticResponses = updatedStaticResponsesModel
906
+ }
907
+ , Effect.FetchHttp httpRequests
908
+ )
1112
909
 
1113
- Just notFoundReason ->
1114
- render404Page config (Result.toMaybe sharedDataResult) model page notFoundReason
910
+ StaticResponses.FinishedWithErrors errors ->
911
+ ( model
912
+ , errors |> ToJsPayload.Errors |> Effect.SendSinglePage
913
+ )
1115
914
 
1116
- Err error ->
1117
- [ error ]
1118
- |> ToJsPayload.Errors
1119
- |> Effect.SendSinglePage
915
+ StaticResponses.Finish finalValue ->
916
+ ( model
917
+ , finalValue
918
+ )
1120
919
 
1121
920
 
1122
921
  render404Page :
1123
922
  ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
1124
923
  -> Maybe sharedData
1125
- -> Model route
1126
- -> Path
924
+ -> Bool
925
+ -> UrlPath
1127
926
  -> NotFoundReason
1128
927
  -> Effect
1129
- render404Page config sharedData model path notFoundReason =
1130
- case ( model.isDevServer, sharedData ) of
928
+ render404Page config sharedData isDevServer path notFoundReason =
929
+ case ( isDevServer, sharedData ) of
1131
930
  ( False, Just justSharedData ) ->
1132
931
  let
1133
932
  byteEncodedPageData : Bytes
@@ -1154,11 +953,11 @@ render404Page config sharedData model path notFoundReason =
1154
953
  pageData =
1155
954
  config.errorPageToData config.notFoundPage
1156
955
 
1157
- pathAndRoute : { path : Path, route : route }
956
+ pathAndRoute : { path : UrlPath, route : route }
1158
957
  pathAndRoute =
1159
958
  { path = path, route = config.notFoundRoute }
1160
959
 
1161
- viewValue : { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
960
+ viewValue : { title : String, body : List (Html (PagesMsg userMsg)) }
1162
961
  viewValue =
1163
962
  (config.view Dict.empty
1164
963
  Dict.empty
@@ -1172,7 +971,7 @@ render404Page config sharedData model path notFoundReason =
1172
971
  )
1173
972
  pageModel
1174
973
  in
1175
- { route = Path.toAbsolute path
974
+ { route = UrlPath.toAbsolute path
1176
975
  , contentJson = Dict.empty
1177
976
  , html = viewValue.body |> bodyToString
1178
977
  , errors = []
@@ -1181,7 +980,7 @@ render404Page config sharedData model path notFoundReason =
1181
980
  , staticHttpCache = Dict.empty
1182
981
  , is404 = True
1183
982
  , statusCode = 404
1184
- , headers = []
983
+ , headers = Dict.empty
1185
984
  }
1186
985
  |> ToJsPayload.PageProgress
1187
986
  |> Effect.SendSinglePageNew byteEncodedPageData
@@ -1201,7 +1000,7 @@ render404Page config sharedData model path notFoundReason =
1201
1000
  }
1202
1001
  |> NotFoundReason.document config.pathPatterns
1203
1002
  in
1204
- { route = Path.toAbsolute path
1003
+ { route = UrlPath.toAbsolute path
1205
1004
  , contentJson = Dict.empty
1206
1005
  , html = bodyToString notFoundDocument.body
1207
1006
  , errors = []
@@ -1213,7 +1012,7 @@ render404Page config sharedData model path notFoundReason =
1213
1012
  --model.allRawResponses |> Dict.Extra.filterMap (\_ v -> v)
1214
1013
  , is404 = True
1215
1014
  , statusCode = 404
1216
- , headers = []
1015
+ , headers = Dict.empty
1217
1016
  }
1218
1017
  |> ToJsPayload.PageProgress
1219
1018
  |> Effect.SendSinglePageNew byteEncodedPageData
@@ -1231,3 +1030,66 @@ urlToRoute config url =
1231
1030
 
1232
1031
  else
1233
1032
  config.urlToRoute url
1033
+
1034
+
1035
+ toRedirectResponse :
1036
+ ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
1037
+ -> { b | path : UrlPath }
1038
+ -> RenderRequest.IncludeHtml
1039
+ -> { c | headers : List ( String, String ), statusCode : Int }
1040
+ -> { response | statusCode : Int, headers : List ( String, String ) }
1041
+ -> Maybe Effect
1042
+ toRedirectResponse config serverRequestPayload includeHtml serverResponse responseMetadata =
1043
+ PageServerResponse.toRedirect responseMetadata
1044
+ |> Maybe.map
1045
+ (\_ ->
1046
+ let
1047
+ ( _, byteEncodedPageData ) =
1048
+ ( serverResponse.headers
1049
+ , PageServerResponse.toRedirect serverResponse
1050
+ |> Maybe.map
1051
+ (\{ location } ->
1052
+ location
1053
+ |> ResponseSketch.Redirect
1054
+ |> config.encodeResponse
1055
+ )
1056
+ |> Maybe.withDefault (Bytes.Encode.unsignedInt8 0)
1057
+ |> Bytes.Encode.encode
1058
+ )
1059
+ in
1060
+ { route = serverRequestPayload.path |> UrlPath.toRelative
1061
+ , contentJson = Dict.empty
1062
+ , html = "This is intentionally blank HTML"
1063
+ , errors = []
1064
+ , head = []
1065
+ , title = "This is an intentionally blank title"
1066
+ , staticHttpCache = Dict.empty
1067
+ , is404 = False
1068
+ , statusCode =
1069
+ case includeHtml of
1070
+ RenderRequest.OnlyJson ->
1071
+ -- if this is a redirect for a `content.dat`, we don't want to send an *actual* redirect status code because the redirect needs to be handled in Elm (not by the Browser)
1072
+ 200
1073
+
1074
+ RenderRequest.HtmlAndJson ->
1075
+ responseMetadata.statusCode
1076
+ , headers = responseMetadata.headers |> combineHeaders
1077
+ }
1078
+ |> ToJsPayload.PageProgress
1079
+ |> Effect.SendSinglePageNew byteEncodedPageData
1080
+ )
1081
+
1082
+
1083
+ combineHeaders : List ( String, String ) -> Dict String (List String)
1084
+ combineHeaders headers =
1085
+ headers
1086
+ |> List.foldl
1087
+ (\( key, value ) dict ->
1088
+ Dict.update key
1089
+ (Maybe.map ((::) value)
1090
+ >> Maybe.withDefault [ value ]
1091
+ >> Just
1092
+ )
1093
+ dict
1094
+ )
1095
+ Dict.empty