elm-pages 3.0.0-beta.9 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/README.md +2 -2
  2. package/adapter/netlify.js +207 -0
  3. package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2731 -2939
  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 +4 -4
  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 +4 -4
  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 +143 -59
  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 +160 -102
  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 +320 -143
  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 +1 -3
  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 -82
  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 +112 -8
  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 +586 -768
  114. package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
  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 -546
  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 -1242
  162. package/src/Pages/Internal/Form.elm +0 -17
  163. package/src/Pages/Internal/Platform/Cli.elm.bak +0 -1276
  164. package/src/Pages/Msg.elm +0 -79
  165. package/src/Pages/Transition.elm +0 -70
@@ -6,40 +6,38 @@ module Pages.Internal.Platform.Cli exposing (Flags, Model, Msg(..), Program, cli
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)
26
27
  import Pages.Internal.Platform.CompatibilityKey
27
28
  import Pages.Internal.Platform.Effect as Effect exposing (Effect)
28
- import Pages.Internal.Platform.StaticResponses as StaticResponses exposing (StaticResponses)
29
+ import Pages.Internal.Platform.StaticResponses as StaticResponses
29
30
  import Pages.Internal.Platform.ToJsPayload as ToJsPayload
30
31
  import Pages.Internal.ResponseSketch as ResponseSketch
31
- import Pages.Internal.StaticHttpBody as StaticHttpBody
32
- import Pages.Msg
33
32
  import Pages.ProgramConfig exposing (ProgramConfig)
34
33
  import Pages.SiteConfig exposing (SiteConfig)
35
34
  import Pages.StaticHttp.Request
36
- import Pages.StaticHttpRequest as StaticHttpRequest
37
- import Path exposing (Path)
35
+ import PagesMsg exposing (PagesMsg)
38
36
  import RenderRequest exposing (RenderRequest)
39
37
  import RequestsAndPending exposing (RequestsAndPending)
40
- import Task
41
38
  import TerminalText as Terminal
42
39
  import Url exposing (Url)
40
+ import UrlPath exposing (UrlPath)
43
41
 
44
42
 
45
43
  {-| -}
@@ -55,10 +53,8 @@ currentCompatibilityKey =
55
53
 
56
54
  {-| -}
57
55
  type alias Model route =
58
- { staticResponses : StaticResponses
56
+ { staticResponses : BackendTask FatalError Effect
59
57
  , errors : List BuildError
60
- , allRawResponses : RequestsAndPending
61
- , unprocessedPages : List ( Path, route )
62
58
  , maybeRequestJson : RenderRequest route
63
59
  , isDevServer : Bool
64
60
  }
@@ -66,12 +62,7 @@ type alias Model route =
66
62
 
67
63
  {-| -}
68
64
  type Msg
69
- = GotDataBatch
70
- (List
71
- { request : Pages.StaticHttp.Request.Request
72
- , response : RequestsAndPending.Response
73
- }
74
- )
65
+ = GotDataBatch Decode.Value
75
66
  | GotBuildError BuildError
76
67
 
77
68
 
@@ -112,7 +103,7 @@ cliApplication config =
112
103
  |> Tuple.mapSecond (perform site renderRequest config)
113
104
  , update =
114
105
  \msg model ->
115
- update site config msg model
106
+ update msg model
116
107
  |> Tuple.mapSecond (perform site model.maybeRequestJson config)
117
108
  , subscriptions =
118
109
  \_ ->
@@ -159,35 +150,11 @@ cliApplication config =
159
150
  )
160
151
  |> mergeResult
161
152
  )
162
- , config.gotBatchSub
163
- |> Sub.map
164
- (\newBatch ->
165
- Decode.decodeValue batchDecoder newBatch
166
- |> Result.map GotDataBatch
167
- |> Result.mapError
168
- (\error ->
169
- ("From location 2: "
170
- ++ (error
171
- |> Decode.errorToString
172
- )
173
- )
174
- |> BuildError.internal
175
- |> GotBuildError
176
- )
177
- |> mergeResult
178
- )
153
+ , config.gotBatchSub |> Sub.map GotDataBatch
179
154
  ]
180
155
  }
181
156
 
182
157
 
183
- batchDecoder : Decode.Decoder (List { request : Pages.StaticHttp.Request.Request, response : RequestsAndPending.Response })
184
- batchDecoder =
185
- Decode.map2 (\request response -> { request = request, response = response })
186
- (Decode.field "request" requestDecoder)
187
- (Decode.field "response" RequestsAndPending.decoder)
188
- |> Decode.list
189
-
190
-
191
158
  mergeResult : Result a a -> a
192
159
  mergeResult r =
193
160
  case r of
@@ -244,61 +211,16 @@ perform site renderRequest config effect =
244
211
  Effect.Batch list ->
245
212
  flatten site renderRequest config list
246
213
 
247
- Effect.FetchHttp unmasked ->
248
- if unmasked.url == "$$elm-pages$$headers" then
249
- case
250
- renderRequest
251
- |> RenderRequest.maybeRequestPayload
252
- |> Maybe.map (\json -> RequestsAndPending.Response Nothing (RequestsAndPending.JsonBody json))
253
- |> Result.fromMaybe (Pages.Http.BadUrl "$$elm-pages$$headers is only available on server-side request (not on build).")
254
- of
255
- Ok okResponse ->
256
- Task.succeed
257
- [ { request = unmasked
258
- , response = okResponse
259
- }
260
- ]
261
- |> Task.perform GotDataBatch
262
-
263
- Err error ->
264
- { title = "Static HTTP Error"
265
- , message =
266
- [ Terminal.text "I got an error making an HTTP request to this URL: "
267
-
268
- -- TODO include HTTP method, headers, and body
269
- , Terminal.yellow unmasked.url
270
- , Terminal.text <| Json.Encode.encode 2 <| StaticHttpBody.encode unmasked.body
271
- , Terminal.text "\n\n"
272
- , case error of
273
- Pages.Http.BadStatus metadata body ->
274
- Terminal.text <|
275
- String.join "\n"
276
- [ "Bad status: " ++ String.fromInt metadata.statusCode
277
- , "Status message: " ++ metadata.statusText
278
- , "Body: " ++ body
279
- ]
280
-
281
- Pages.Http.BadUrl _ ->
282
- -- TODO include HTTP method, headers, and body
283
- Terminal.text <| "Invalid url: " ++ unmasked.url
284
-
285
- Pages.Http.Timeout ->
286
- Terminal.text "Timeout"
287
-
288
- Pages.Http.NetworkError ->
289
- Terminal.text "Network error"
290
- ]
291
- , fatal = True
292
- , path = "" -- TODO wire in current path here
293
- }
294
- |> Task.succeed
295
- |> Task.perform GotBuildError
296
-
297
- else
298
- ToJsPayload.DoHttp unmasked unmasked.useCache
299
- |> Codec.encoder (ToJsPayload.successCodecNew2 canonicalSiteUrl "")
300
- |> config.toJsPort
301
- |> 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
302
224
 
303
225
  Effect.SendSinglePage info ->
304
226
  let
@@ -335,9 +257,6 @@ perform site renderRequest config effect =
335
257
  |> config.sendPageData
336
258
  |> Cmd.map never
337
259
 
338
- Effect.Continue ->
339
- Cmd.none
340
-
341
260
 
342
261
  flagsDecoder :
343
262
  Decode.Decoder
@@ -353,15 +272,8 @@ flagsDecoder =
353
272
  , compatibilityKey = compatibilityKey
354
273
  }
355
274
  )
356
- --(Decode.field "staticHttpCache"
357
- -- (Decode.dict
358
- -- (Decode.string
359
- -- |> Decode.map Just
360
- -- )
361
- -- )
362
- --)
363
275
  -- TODO remove hardcoding and decode staticHttpCache here
364
- (Decode.succeed Dict.empty)
276
+ (Decode.succeed (Json.Encode.object []))
365
277
  (Decode.field "mode" Decode.string |> Decode.map (\mode -> mode == "dev-server"))
366
278
  (Decode.field "compatibilityKey" Decode.int)
367
279
 
@@ -375,9 +287,9 @@ init :
375
287
  -> ( Model route, Effect )
376
288
  init site renderRequest config flags =
377
289
  case Decode.decodeValue flagsDecoder flags of
378
- Ok { staticHttpCache, isDevServer, compatibilityKey } ->
290
+ Ok { isDevServer, compatibilityKey } ->
379
291
  if compatibilityKey == currentCompatibilityKey then
380
- initLegacy site renderRequest { staticHttpCache = staticHttpCache, isDevServer = isDevServer } config
292
+ initLegacy site renderRequest { isDevServer = isDevServer } config
381
293
 
382
294
  else
383
295
  let
@@ -396,9 +308,7 @@ init site renderRequest config flags =
396
308
  )
397
309
  in
398
310
  updateAndSendPortIfDone
399
- site
400
- config
401
- { staticResponses = StaticResponses.empty
311
+ { staticResponses = StaticResponses.empty Effect.NoEffect
402
312
  , errors =
403
313
  [ { title = "Incompatible NPM and Elm package versions"
404
314
  , message = [ Terminal.text <| message ]
@@ -406,17 +316,13 @@ init site renderRequest config flags =
406
316
  , path = ""
407
317
  }
408
318
  ]
409
- , allRawResponses = Dict.empty
410
- , unprocessedPages = []
411
319
  , maybeRequestJson = renderRequest
412
320
  , isDevServer = False
413
321
  }
414
322
 
415
323
  Err error ->
416
324
  updateAndSendPortIfDone
417
- site
418
- config
419
- { staticResponses = StaticResponses.empty
325
+ { staticResponses = StaticResponses.empty Effect.NoEffect
420
326
  , errors =
421
327
  [ { title = "Internal Error"
422
328
  , message = [ Terminal.text <| "Failed to parse flags: " ++ Decode.errorToString error ]
@@ -424,8 +330,6 @@ init site renderRequest config flags =
424
330
  , path = ""
425
331
  }
426
332
  ]
427
- , allRawResponses = Dict.empty
428
- , unprocessedPages = []
429
333
  , maybeRequestJson = renderRequest
430
334
  , isDevServer = False
431
335
  }
@@ -474,140 +378,506 @@ isActionDecoder =
474
378
  initLegacy :
475
379
  SiteConfig
476
380
  -> RenderRequest route
477
- -> { staticHttpCache : RequestsAndPending, isDevServer : Bool }
381
+ -> { isDevServer : Bool }
478
382
  -> ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
479
383
  -> ( Model route, Effect )
480
- initLegacy site renderRequest { staticHttpCache, isDevServer } config =
384
+ initLegacy site ((RenderRequest.SinglePage includeHtml singleRequest _) as renderRequest) { isDevServer } config =
481
385
  let
482
- staticResponses : StaticResponses
483
- staticResponses =
484
- case renderRequest of
485
- 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 ->
486
394
  let
487
- globalHeadTags : DataSource (List Head.Tag)
488
- globalHeadTags =
489
- (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
+ }
490
410
  in
491
- case singleRequest of
492
- RenderRequest.Page serverRequestPayload ->
493
- let
494
- isAction : Maybe ActionRequest
495
- isAction =
496
- renderRequest
497
- |> RenderRequest.maybeRequestPayload
498
- |> Maybe.andThen (Decode.decodeValue isActionDecoder >> Result.withDefault Nothing)
499
- in
500
- StaticResponses.renderSingleRoute
501
- (case isAction of
502
- Just _ ->
503
- config.action serverRequestPayload.frontmatter
504
- |> DataSource.andThen
505
- (\something ->
506
- case something of
507
- PageServerResponse.ErrorPage _ _ ->
508
- DataSource.succeed something
509
- |> DataSource.map (\_ -> ())
510
-
511
- PageServerResponse.RenderPage _ _ ->
512
- DataSource.map3 (\_ _ _ -> ())
513
- (config.data serverRequestPayload.frontmatter)
514
- config.sharedData
515
- globalHeadTags
516
-
517
- PageServerResponse.ServerResponse _ ->
518
- DataSource.succeed something
519
- |> DataSource.map (\_ -> ())
520
- )
411
+ --case isAction of
412
+ -- Just actionRequest ->
413
+ (if isDevServer then
414
+ config.handleRoute serverRequestPayload.frontmatter
521
415
 
416
+ else
417
+ BackendTask.succeed Nothing
418
+ )
419
+ |> BackendTask.andThen
420
+ (\pageFound ->
421
+ case pageFound of
522
422
  Nothing ->
523
- DataSource.map3 (\_ _ _ -> ())
524
- (config.data serverRequestPayload.frontmatter)
525
- config.sharedData
526
- globalHeadTags
527
- )
528
- (if isDevServer then
529
- 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
530
433
 
531
- else
532
- DataSource.succeed Nothing
533
- )
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
+ }
534
451
 
535
- RenderRequest.Api ( path, ApiRoute apiRequest ) ->
536
- StaticResponses.renderApiRequest
537
- (DataSource.map2 (\_ _ -> ())
538
- (apiRequest.matchesToResponse path)
539
- globalHeadTags
540
- )
452
+ _ ->
453
+ Nothing
541
454
 
542
- RenderRequest.NotFound _ ->
543
- StaticResponses.renderApiRequest
544
- (DataSource.map2 (\_ _ -> ())
545
- (DataSource.succeed [])
546
- globalHeadTags
547
- )
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
548
471
 
549
- unprocessedPages : List ( Path, route )
550
- unprocessedPages =
551
- case renderRequest of
552
- RenderRequest.SinglePage _ serverRequestPayload _ ->
553
- case serverRequestPayload of
554
- RenderRequest.Page pageData ->
555
- [ ( 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
556
739
 
557
- RenderRequest.Api _ ->
558
- []
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
+ )
784
+
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
+ )
559
795
 
560
- RenderRequest.NotFound _ ->
561
- []
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
+ )
562
845
 
563
846
  initialModel : Model route
564
847
  initialModel =
565
- { staticResponses = staticResponses
848
+ { staticResponses = staticResponsesNew
566
849
  , errors = []
567
- , allRawResponses = staticHttpCache
568
- , unprocessedPages = unprocessedPages
569
850
  , maybeRequestJson = renderRequest
570
851
  , isDevServer = isDevServer
571
852
  }
572
853
  in
573
- StaticResponses.nextStep initialModel Nothing
574
- |> nextStepToEffect site
575
- config
854
+ StaticResponses.nextStep (Json.Encode.object []) initialModel.staticResponses initialModel
855
+ |> nextStepToEffect
576
856
  initialModel
577
857
 
578
858
 
579
859
  updateAndSendPortIfDone :
580
- SiteConfig
581
- -> ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
582
- -> Model route
860
+ Model route
583
861
  -> ( Model route, Effect )
584
- updateAndSendPortIfDone site config model =
585
- StaticResponses.nextStep
862
+ updateAndSendPortIfDone model =
863
+ StaticResponses.nextStep (Json.Encode.object [])
864
+ model.staticResponses
586
865
  model
587
- Nothing
588
- |> nextStepToEffect site config model
866
+ |> nextStepToEffect model
589
867
 
590
868
 
591
869
  {-| -}
592
870
  update :
593
- SiteConfig
594
- -> ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
595
- -> Msg
871
+ Msg
596
872
  -> Model route
597
873
  -> ( Model route, Effect )
598
- update site config msg model =
874
+ update msg model =
599
875
  case msg of
600
876
  GotDataBatch batch ->
601
- let
602
- updatedModel : Model route
603
- updatedModel =
604
- model
605
- |> StaticResponses.batchUpdate batch
606
- in
607
- StaticResponses.nextStep
608
- updatedModel
609
- Nothing
610
- |> nextStepToEffect site config updatedModel
877
+ StaticResponses.nextStep batch
878
+ model.staticResponses
879
+ model
880
+ |> nextStepToEffect model
611
881
 
612
882
  GotBuildError buildError ->
613
883
  let
@@ -618,560 +888,45 @@ update site config msg model =
618
888
  buildError :: model.errors
619
889
  }
620
890
  in
621
- StaticResponses.nextStep
891
+ StaticResponses.nextStep (Json.Encode.object [])
892
+ updatedModel.staticResponses
622
893
  updatedModel
623
- Nothing
624
- |> nextStepToEffect site config updatedModel
894
+ |> nextStepToEffect updatedModel
625
895
 
626
896
 
627
897
  nextStepToEffect :
628
- SiteConfig
629
- -> ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
630
- -> Model route
631
- -> ( StaticResponses, StaticResponses.NextStep route )
898
+ Model route
899
+ -> StaticResponses.NextStep route Effect
632
900
  -> ( Model route, Effect )
633
- nextStepToEffect site config model ( updatedStaticResponsesModel, nextStep ) =
901
+ nextStepToEffect model nextStep =
634
902
  case nextStep of
635
- StaticResponses.Continue updatedAllRawResponses httpRequests maybeRoutes ->
636
- let
637
- updatedUnprocessedPages : List ( Path, route )
638
- updatedUnprocessedPages =
639
- case maybeRoutes of
640
- Just newRoutes ->
641
- newRoutes
642
- |> List.map
643
- (\route ->
644
- ( Path.join (config.routeToPath route)
645
- , route
646
- )
647
- )
648
-
649
- Nothing ->
650
- model.unprocessedPages
651
-
652
- updatedModel : Model route
653
- updatedModel =
654
- { model
655
- | allRawResponses = updatedAllRawResponses
656
- , staticResponses = updatedStaticResponsesModel
657
- , unprocessedPages = updatedUnprocessedPages
658
- }
659
- in
660
- if List.isEmpty httpRequests then
661
- nextStepToEffect site
662
- config
663
- updatedModel
664
- (StaticResponses.nextStep
665
- updatedModel
666
- Nothing
667
- )
668
-
669
- else
670
- ( updatedModel
671
- , (httpRequests
672
- |> List.map Effect.FetchHttp
673
- )
674
- |> Effect.Batch
675
- )
676
-
677
- StaticResponses.Finish toJsPayload ->
678
- case toJsPayload of
679
- StaticResponses.ApiResponse ->
680
- let
681
- apiResponse : Effect
682
- apiResponse =
683
- case model.maybeRequestJson of
684
- RenderRequest.SinglePage _ requestPayload _ ->
685
- let
686
- sharedDataResult : Result BuildError sharedData
687
- sharedDataResult =
688
- StaticHttpRequest.resolve
689
- config.sharedData
690
- model.allRawResponses
691
- |> Result.mapError (StaticHttpRequest.toBuildError "")
692
- in
693
- case requestPayload of
694
- RenderRequest.Api ( path, ApiRoute apiHandler ) ->
695
- let
696
- thing : DataSource (Maybe ApiRoute.Response)
697
- thing =
698
- apiHandler.matchesToResponse path
699
- in
700
- StaticHttpRequest.resolve
701
- thing
702
- model.allRawResponses
703
- |> Result.mapError (StaticHttpRequest.toBuildError "TODO - path from request")
704
- |> (\response ->
705
- case response of
706
- Ok (Just okResponse) ->
707
- { body = okResponse
708
- , staticHttpCache = Dict.empty -- TODO do I need to serialize the full cache here, or can I handle that from the JS side?
709
-
710
- -- model.allRawResponses |> Dict.Extra.filterMap (\_ v -> v)
711
- , statusCode = 200
712
- }
713
- |> ToJsPayload.SendApiResponse
714
- |> Effect.SendSinglePage
715
-
716
- Ok Nothing ->
717
- render404Page config (Result.toMaybe sharedDataResult) model (Path.fromString path) NotFoundReason.NoMatchingRoute
718
-
719
- Err error ->
720
- [ error ]
721
- |> ToJsPayload.Errors
722
- |> Effect.SendSinglePage
723
- )
724
-
725
- RenderRequest.Page payload ->
726
- let
727
- pageFoundResult : Result BuildError (Maybe NotFoundReason)
728
- pageFoundResult =
729
- StaticHttpRequest.resolve
730
- (if model.isDevServer then
731
- -- TODO OPTIMIZATION this is redundant
732
- config.handleRoute payload.frontmatter
733
-
734
- else
735
- DataSource.succeed Nothing
736
- )
737
- model.allRawResponses
738
- |> Result.mapError (StaticHttpRequest.toBuildError (payload.path |> Path.toAbsolute))
739
- in
740
- case pageFoundResult of
741
- Ok Nothing ->
742
- sendSinglePageProgress site model.allRawResponses config model payload
743
-
744
- Ok (Just notFoundReason) ->
745
- render404Page config
746
- --Nothing
747
- (Result.toMaybe sharedDataResult)
748
- model
749
- payload.path
750
- notFoundReason
751
-
752
- Err error ->
753
- [ error ] |> ToJsPayload.Errors |> Effect.SendSinglePage
754
-
755
- RenderRequest.NotFound path ->
756
- render404Page config
757
- --Nothing
758
- (Result.toMaybe sharedDataResult)
759
- model
760
- path
761
- NotFoundReason.NoMatchingRoute
762
- in
763
- ( model
764
- , apiResponse
765
- )
766
-
767
- StaticResponses.Errors errors ->
768
- ( model
769
- , errors |> ToJsPayload.Errors |> Effect.SendSinglePage
770
- )
771
-
772
-
773
- sendSinglePageProgress :
774
- SiteConfig
775
- -> RequestsAndPending
776
- -> ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
777
- -> Model route
778
- -> { path : Path, frontmatter : route }
779
- -> Effect
780
- sendSinglePageProgress site contentJson config model info =
781
- let
782
- ( page, route ) =
783
- ( info.path, info.frontmatter )
784
- in
785
- case model.maybeRequestJson of
786
- RenderRequest.SinglePage includeHtml _ _ ->
787
- let
788
- isAction : Maybe ActionRequest
789
- isAction =
790
- model.maybeRequestJson
791
- |> RenderRequest.maybeRequestPayload
792
- |> Maybe.andThen (Decode.decodeValue isActionDecoder >> Result.withDefault Nothing)
793
-
794
- pageFoundResult : Result BuildError (Maybe NotFoundReason)
795
- pageFoundResult =
796
- -- TODO OPTIMIZATION this is redundant
797
- StaticHttpRequest.resolve
798
- (if model.isDevServer then
799
- config.handleRoute route
800
-
801
- else
802
- DataSource.succeed Nothing
803
- )
804
- model.allRawResponses
805
- |> Result.mapError (StaticHttpRequest.toBuildError currentUrl.path)
806
-
807
- renderedResult : Result BuildError (PageServerResponse { head : List Head.Tag, view : String, title : String } errorPage)
808
- renderedResult =
809
- case includeHtml of
810
- RenderRequest.OnlyJson ->
811
- pageDataResult
812
- |> Result.map
813
- (\okPageData ->
814
- case okPageData of
815
- PageServerResponse.RenderPage responseInfo _ ->
816
- PageServerResponse.RenderPage
817
- { statusCode = responseInfo.statusCode
818
- , headers = responseInfo.headers
819
- }
820
- { head = []
821
- , view = "This page was not rendered because it is a JSON-only request."
822
- , title = "This page was not rendered because it is a JSON-only request."
823
- }
824
-
825
- PageServerResponse.ServerResponse serverResponse ->
826
- PageServerResponse.ServerResponse serverResponse
827
-
828
- PageServerResponse.ErrorPage error record ->
829
- PageServerResponse.ErrorPage error record
830
- )
831
-
832
- RenderRequest.HtmlAndJson ->
833
- Result.map2 Tuple.pair pageDataResult sharedDataResult
834
- |> Result.map
835
- (\( pageData_, sharedData ) ->
836
- case pageData_ of
837
- PageServerResponse.RenderPage responseInfo pageData ->
838
- let
839
- currentPage : { path : Path, route : route }
840
- currentPage =
841
- { path = page, route = urlToRoute config currentUrl }
842
-
843
- maybeActionData : Maybe actionData
844
- maybeActionData =
845
- case isAction of
846
- Just _ ->
847
- case actionDataResult of
848
- Ok (PageServerResponse.RenderPage _ actionData) ->
849
- Just actionData
850
-
851
- _ ->
852
- Nothing
853
-
854
- Nothing ->
855
- Nothing
856
-
857
- pageModel : userModel
858
- pageModel =
859
- config.init
860
- Pages.Flags.PreRenderFlags
861
- sharedData
862
- pageData
863
- maybeActionData
864
- (Just
865
- { path =
866
- { path = currentPage.path
867
- , query = Nothing
868
- , fragment = Nothing
869
- }
870
- , metadata = currentPage.route
871
- , pageUrl = Nothing
872
- }
873
- )
874
- |> Tuple.first
875
-
876
- viewValue : { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
877
- viewValue =
878
- (config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData maybeActionData |> .view) pageModel
879
- in
880
- PageServerResponse.RenderPage responseInfo
881
- { head = config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData maybeActionData |> .head
882
- , view = viewValue.body |> bodyToString
883
- , title = viewValue.title
884
- }
885
-
886
- PageServerResponse.ServerResponse serverResponse ->
887
- PageServerResponse.ServerResponse serverResponse
888
-
889
- PageServerResponse.ErrorPage error record ->
890
- let
891
- currentPage : { path : Path, route : route }
892
- currentPage =
893
- { path = page, route = urlToRoute config currentUrl }
894
-
895
- pageModel : userModel
896
- pageModel =
897
- config.init
898
- Pages.Flags.PreRenderFlags
899
- sharedData
900
- pageData
901
- Nothing
902
- (Just
903
- { path =
904
- { path = currentPage.path
905
- , query = Nothing
906
- , fragment = Nothing
907
- }
908
- , metadata = currentPage.route
909
- , pageUrl = Nothing
910
- }
911
- )
912
- |> Tuple.first
913
-
914
- pageData : pageData
915
- pageData =
916
- config.errorPageToData error
917
-
918
- viewValue : { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
919
- viewValue =
920
- (config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData Nothing |> .view) pageModel
921
- in
922
- PageServerResponse.RenderPage
923
- { statusCode = config.errorStatusCode error
924
- , headers = record.headers
925
- }
926
- { head = config.view Dict.empty Dict.empty Nothing currentPage Nothing sharedData pageData Nothing |> .head
927
- , view = viewValue.body |> List.map (HtmlPrinter.htmlToString Nothing) |> String.join "\n"
928
- , title = viewValue.title
929
- }
930
- )
931
-
932
- currentUrl : Url
933
- currentUrl =
934
- { protocol = Url.Https
935
- , host = site.canonicalUrl
936
- , port_ = Nothing
937
- , path = page |> Path.toRelative
938
- , query = Nothing
939
- , fragment = Nothing
940
- }
941
-
942
- pageDataResult : Result BuildError (PageServerResponse pageData errorPage)
943
- pageDataResult =
944
- -- TODO OPTIMIZATION can these three be included in StaticResponses.Finish?
945
- StaticHttpRequest.resolve
946
- (case isAction of
947
- Just _ ->
948
- config.action (urlToRoute config currentUrl)
949
- |> DataSource.andThen
950
- (\something ->
951
- case something of
952
- PageServerResponse.ErrorPage a b ->
953
- PageServerResponse.ErrorPage a b
954
- |> DataSource.succeed
955
-
956
- PageServerResponse.RenderPage _ _ ->
957
- -- TODO the headers/response codes are ignored from the action here
958
- -- is that okay? Should you always do a redirect or another kind of
959
- -- server response if you want to control the headers/response code for an action (like logout & redirect, for example)?
960
- config.data (urlToRoute config currentUrl)
961
-
962
- PageServerResponse.ServerResponse a ->
963
- PageServerResponse.ServerResponse a
964
- |> DataSource.succeed
965
- )
966
-
967
- Nothing ->
968
- config.data (urlToRoute config currentUrl)
969
- )
970
- contentJson
971
- |> Result.mapError (StaticHttpRequest.toBuildError currentUrl.path)
972
-
973
- actionDataResult : Result BuildError (PageServerResponse actionData errorPage)
974
- actionDataResult =
975
- -- TODO OPTIMIZATION can these three be included in StaticResponses.Finish?
976
- StaticHttpRequest.resolve
977
- (config.action (urlToRoute config currentUrl))
978
- contentJson
979
- |> Result.mapError (StaticHttpRequest.toBuildError currentUrl.path)
980
-
981
- sharedDataResult : Result BuildError sharedData
982
- sharedDataResult =
983
- StaticHttpRequest.resolve
984
- config.sharedData
985
- contentJson
986
- |> Result.mapError (StaticHttpRequest.toBuildError currentUrl.path)
987
-
988
- globalHeadTags : DataSource (List Head.Tag)
989
- globalHeadTags =
990
- (config.globalHeadTags |> Maybe.withDefault (\_ -> DataSource.succeed [])) HtmlPrinter.htmlToString
991
-
992
- siteDataResult : Result BuildError (List Head.Tag)
993
- siteDataResult =
994
- StaticHttpRequest.resolve
995
- globalHeadTags
996
- model.allRawResponses
997
- |> Result.mapError (StaticHttpRequest.toBuildError "Site.elm")
998
- in
999
- case Result.map3 (\a b c -> ( a, b, c )) pageFoundResult renderedResult siteDataResult of
1000
- Ok ( maybeNotFoundReason, renderedOrApiResponse, siteData ) ->
1001
- case maybeNotFoundReason of
1002
- Nothing ->
1003
- let
1004
- ( actionHeaders, byteEncodedPageData ) =
1005
- case pageDataResult of
1006
- Ok pageServerResponse ->
1007
- case pageServerResponse of
1008
- PageServerResponse.RenderPage ignored1 pageData ->
1009
- -- TODO want to encode both shared and page data in dev server and HTML-embedded data
1010
- -- but not for writing out the content.dat files - would be good to optimize this redundant data out
1011
- --if model.isDevServer then
1012
- case isAction of
1013
- Just actionRequestKind ->
1014
- case actionDataResult of
1015
- Ok (PageServerResponse.RenderPage ignored2 actionData) ->
1016
- case actionRequestKind of
1017
- ActionResponseRequest ->
1018
- ( ignored2.headers
1019
- , sharedDataResult
1020
- |> Result.map (\sharedData -> ResponseSketch.HotUpdate pageData sharedData (Just actionData))
1021
- |> Result.withDefault (ResponseSketch.RenderPage pageData (Just actionData))
1022
- |> config.encodeResponse
1023
- |> Bytes.Encode.encode
1024
- )
1025
-
1026
- ActionOnlyRequest ->
1027
- ---- TODO need to encode action data when only that is requested (not ResponseSketch?)
1028
- ( ignored2.headers
1029
- , actionData
1030
- |> config.encodeAction
1031
- |> Bytes.Encode.encode
1032
- )
1033
-
1034
- _ ->
1035
- ( ignored1.headers
1036
- , Bytes.Encode.encode (Bytes.Encode.unsignedInt8 0)
1037
- )
1038
-
1039
- Nothing ->
1040
- ( ignored1.headers
1041
- , sharedDataResult
1042
- |> Result.map (\something -> ResponseSketch.HotUpdate pageData something Nothing)
1043
- |> Result.withDefault (ResponseSketch.RenderPage pageData Nothing)
1044
- |> config.encodeResponse
1045
- |> Bytes.Encode.encode
1046
- )
1047
-
1048
- --else
1049
- -- pageData
1050
- -- |> ResponseSketch.RenderPage
1051
- -- |> config.encodeResponse
1052
- -- |> Bytes.Encode.encode
1053
- PageServerResponse.ServerResponse serverResponse ->
1054
- -- TODO handle error?
1055
- ( serverResponse.headers
1056
- , PageServerResponse.toRedirect serverResponse
1057
- |> Maybe.map
1058
- (\{ location } ->
1059
- location
1060
- |> ResponseSketch.Redirect
1061
- |> config.encodeResponse
1062
- )
1063
- -- TODO handle other cases besides redirects?
1064
- |> Maybe.withDefault (Bytes.Encode.unsignedInt8 0)
1065
- |> Bytes.Encode.encode
1066
- )
1067
-
1068
- PageServerResponse.ErrorPage error { headers } ->
1069
- -- TODO this case should never happen
1070
- ( headers
1071
- , sharedDataResult
1072
- |> Result.map
1073
- (\sharedData ->
1074
- ResponseSketch.HotUpdate (config.errorPageToData error)
1075
- sharedData
1076
- Nothing
1077
- )
1078
- |> Result.map config.encodeResponse
1079
- |> Result.map Bytes.Encode.encode
1080
- |> Result.withDefault (Bytes.Encode.encode (Bytes.Encode.unsignedInt8 0))
1081
- )
1082
-
1083
- _ ->
1084
- -- TODO handle error?
1085
- ( []
1086
- , Bytes.Encode.encode (Bytes.Encode.unsignedInt8 0)
1087
- )
1088
- in
1089
- case renderedOrApiResponse of
1090
- PageServerResponse.RenderPage responseInfo rendered ->
1091
- { route = page |> Path.toRelative
1092
- , contentJson = Dict.empty
1093
- , html = rendered.view
1094
- , errors = []
1095
- , head = rendered.head ++ siteData
1096
- , title = rendered.title
1097
- , staticHttpCache = Dict.empty
1098
- , is404 = False
1099
- , statusCode = responseInfo.statusCode
1100
- , headers =
1101
- -- 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?
1102
- actionHeaders
1103
- }
1104
- |> ToJsPayload.PageProgress
1105
- |> Effect.SendSinglePageNew byteEncodedPageData
1106
-
1107
- PageServerResponse.ServerResponse serverResponse ->
1108
- PageServerResponse.toRedirect serverResponse
1109
- |> Maybe.map
1110
- (\_ ->
1111
- { route = page |> Path.toRelative
1112
- , contentJson = Dict.empty
1113
- , html = "This is intentionally blank HTML"
1114
- , errors = []
1115
- , head = []
1116
- , title = "This is an intentionally blank title"
1117
- , staticHttpCache = Dict.empty
1118
- , is404 = False
1119
- , statusCode =
1120
- case includeHtml of
1121
- RenderRequest.OnlyJson ->
1122
- -- 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)
1123
- 200
1124
-
1125
- RenderRequest.HtmlAndJson ->
1126
- serverResponse.statusCode
1127
- , headers = serverResponse.headers
1128
- }
1129
- |> ToJsPayload.PageProgress
1130
- |> Effect.SendSinglePageNew byteEncodedPageData
1131
- )
1132
- |> Maybe.withDefault
1133
- ({ body = serverResponse |> PageServerResponse.toJson
1134
- , staticHttpCache = Dict.empty
1135
- , statusCode = serverResponse.statusCode
1136
- }
1137
- |> ToJsPayload.SendApiResponse
1138
- |> Effect.SendSinglePage
1139
- )
1140
-
1141
- PageServerResponse.ErrorPage error responseInfo ->
1142
- -- TODO this case should never happen
1143
- { route = page |> Path.toRelative
1144
- , contentJson = Dict.empty
1145
- , html = "UNEXPECTED!" --HtmlPrinter.htmlToString rendered.body
1146
- , errors = []
1147
- , head = [] -- rendered.head ++ siteData -- TODO this should call ErrorPage.head maybe?
1148
- , title = "UNEXPECTED CASE" --rendered.title
1149
- , staticHttpCache = Dict.empty
1150
- , is404 = False
1151
- , statusCode = config.errorStatusCode error
1152
- , headers = responseInfo.headers
1153
- }
1154
- |> ToJsPayload.PageProgress
1155
- |> Effect.SendSinglePageNew byteEncodedPageData
903
+ StaticResponses.Continue httpRequests updatedStaticResponsesModel ->
904
+ ( { model
905
+ | staticResponses = updatedStaticResponsesModel
906
+ }
907
+ , Effect.FetchHttp httpRequests
908
+ )
1156
909
 
1157
- Just notFoundReason ->
1158
- render404Page config (Result.toMaybe sharedDataResult) model page notFoundReason
910
+ StaticResponses.FinishedWithErrors errors ->
911
+ ( model
912
+ , errors |> ToJsPayload.Errors |> Effect.SendSinglePage
913
+ )
1159
914
 
1160
- Err error ->
1161
- [ error ]
1162
- |> ToJsPayload.Errors
1163
- |> Effect.SendSinglePage
915
+ StaticResponses.Finish finalValue ->
916
+ ( model
917
+ , finalValue
918
+ )
1164
919
 
1165
920
 
1166
921
  render404Page :
1167
922
  ProgramConfig userMsg userModel route pageData actionData sharedData effect mappedMsg errorPage
1168
923
  -> Maybe sharedData
1169
- -> Model route
1170
- -> Path
924
+ -> Bool
925
+ -> UrlPath
1171
926
  -> NotFoundReason
1172
927
  -> Effect
1173
- render404Page config sharedData model path notFoundReason =
1174
- case ( model.isDevServer, sharedData ) of
928
+ render404Page config sharedData isDevServer path notFoundReason =
929
+ case ( isDevServer, sharedData ) of
1175
930
  ( False, Just justSharedData ) ->
1176
931
  let
1177
932
  byteEncodedPageData : Bytes
@@ -1198,11 +953,11 @@ render404Page config sharedData model path notFoundReason =
1198
953
  pageData =
1199
954
  config.errorPageToData config.notFoundPage
1200
955
 
1201
- pathAndRoute : { path : Path, route : route }
956
+ pathAndRoute : { path : UrlPath, route : route }
1202
957
  pathAndRoute =
1203
958
  { path = path, route = config.notFoundRoute }
1204
959
 
1205
- viewValue : { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
960
+ viewValue : { title : String, body : List (Html (PagesMsg userMsg)) }
1206
961
  viewValue =
1207
962
  (config.view Dict.empty
1208
963
  Dict.empty
@@ -1216,7 +971,7 @@ render404Page config sharedData model path notFoundReason =
1216
971
  )
1217
972
  pageModel
1218
973
  in
1219
- { route = Path.toAbsolute path
974
+ { route = UrlPath.toAbsolute path
1220
975
  , contentJson = Dict.empty
1221
976
  , html = viewValue.body |> bodyToString
1222
977
  , errors = []
@@ -1225,7 +980,7 @@ render404Page config sharedData model path notFoundReason =
1225
980
  , staticHttpCache = Dict.empty
1226
981
  , is404 = True
1227
982
  , statusCode = 404
1228
- , headers = []
983
+ , headers = Dict.empty
1229
984
  }
1230
985
  |> ToJsPayload.PageProgress
1231
986
  |> Effect.SendSinglePageNew byteEncodedPageData
@@ -1245,7 +1000,7 @@ render404Page config sharedData model path notFoundReason =
1245
1000
  }
1246
1001
  |> NotFoundReason.document config.pathPatterns
1247
1002
  in
1248
- { route = Path.toAbsolute path
1003
+ { route = UrlPath.toAbsolute path
1249
1004
  , contentJson = Dict.empty
1250
1005
  , html = bodyToString notFoundDocument.body
1251
1006
  , errors = []
@@ -1257,7 +1012,7 @@ render404Page config sharedData model path notFoundReason =
1257
1012
  --model.allRawResponses |> Dict.Extra.filterMap (\_ v -> v)
1258
1013
  , is404 = True
1259
1014
  , statusCode = 404
1260
- , headers = []
1015
+ , headers = Dict.empty
1261
1016
  }
1262
1017
  |> ToJsPayload.PageProgress
1263
1018
  |> Effect.SendSinglePageNew byteEncodedPageData
@@ -1275,3 +1030,66 @@ urlToRoute config url =
1275
1030
 
1276
1031
  else
1277
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