elm-pages 3.0.0-beta.26 → 3.0.0-beta.28

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