elm-pages 2.1.7 → 2.1.11
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.
- package/generator/review/elm.json +34 -0
- package/generator/review/src/ReviewConfig.elm +10 -0
- package/generator/src/basepath-middleware.js +15 -9
- package/generator/src/build.js +77 -4
- package/generator/src/cli.js +13 -9
- package/generator/src/compile-elm.js +43 -0
- package/generator/src/dev-server.js +63 -11
- package/generator/src/error-formatter.js +62 -9
- package/generator/src/generate-template-module-connector.js +17 -4
- package/generator/src/init.js +4 -0
- package/generator/src/pre-render-html.js +19 -12
- package/generator/src/render-worker.js +0 -1
- package/generator/src/render.js +1 -2
- package/generator/src/seo-renderer.js +21 -2
- package/generator/static-code/hmr.js +43 -6
- package/generator/template/elm.json +13 -5
- package/generator/template/package.json +3 -2
- package/package.json +14 -8
- package/src/ApiRoute.elm +178 -0
- package/src/AriaLiveAnnouncer.elm +36 -0
- package/src/BuildError.elm +60 -0
- package/src/DataSource/File.elm +288 -0
- package/src/DataSource/Glob.elm +1050 -0
- package/src/DataSource/Http.elm +467 -0
- package/src/DataSource/Internal/Glob.elm +74 -0
- package/src/DataSource/Port.elm +87 -0
- package/src/DataSource/ServerRequest.elm +60 -0
- package/src/DataSource.elm +801 -0
- package/src/Head/Seo.elm +516 -0
- package/src/Head/Twitter.elm +109 -0
- package/src/Head.elm +452 -0
- package/src/HtmlPrinter.elm +27 -0
- package/src/Internal/ApiRoute.elm +89 -0
- package/src/Internal/OptimizedDecoder.elm +18 -0
- package/src/KeepOrDiscard.elm +6 -0
- package/src/OptimizedDecoder/Pipeline.elm +335 -0
- package/src/OptimizedDecoder.elm +818 -0
- package/src/Pages/ContentCache.elm +248 -0
- package/src/Pages/Flags.elm +26 -0
- package/src/Pages/Http.elm +10 -0
- package/src/Pages/Internal/ApplicationType.elm +6 -0
- package/src/Pages/Internal/NotFoundReason.elm +256 -0
- package/src/Pages/Internal/Platform/Cli.elm +1015 -0
- package/src/Pages/Internal/Platform/Effect.elm +14 -0
- package/src/Pages/Internal/Platform/StaticResponses.elm +540 -0
- package/src/Pages/Internal/Platform/ToJsPayload.elm +138 -0
- package/src/Pages/Internal/Platform.elm +745 -0
- package/src/Pages/Internal/RoutePattern.elm +122 -0
- package/src/Pages/Internal/Router.elm +116 -0
- package/src/Pages/Internal/StaticHttpBody.elm +54 -0
- package/src/Pages/Internal/String.elm +39 -0
- package/src/Pages/Manifest/Category.elm +240 -0
- package/src/Pages/Manifest.elm +412 -0
- package/src/Pages/PageUrl.elm +38 -0
- package/src/Pages/ProgramConfig.elm +73 -0
- package/src/Pages/Review/NoContractViolations.elm +397 -0
- package/src/Pages/Secrets.elm +83 -0
- package/src/Pages/SiteConfig.elm +13 -0
- package/src/Pages/StaticHttp/Request.elm +42 -0
- package/src/Pages/StaticHttpRequest.elm +320 -0
- package/src/Pages/Url.elm +60 -0
- package/src/Path.elm +96 -0
- package/src/QueryParams.elm +216 -0
- package/src/RenderRequest.elm +163 -0
- package/src/RequestsAndPending.elm +20 -0
- package/src/Secrets.elm +111 -0
- package/src/SecretsDict.elm +45 -0
- package/src/StructuredData.elm +236 -0
- package/src/TerminalText.elm +242 -0
- package/src/Test/Html/Internal/ElmHtml/Constants.elm +53 -0
- package/src/Test/Html/Internal/ElmHtml/Helpers.elm +17 -0
- package/src/Test/Html/Internal/ElmHtml/InternalTypes.elm +529 -0
- package/src/Test/Html/Internal/ElmHtml/Markdown.elm +56 -0
- package/src/Test/Html/Internal/ElmHtml/ToString.elm +197 -0
- package/src/Test/Internal/KernelConstants.elm +34 -0
|
@@ -0,0 +1,745 @@
|
|
|
1
|
+
module Pages.Internal.Platform exposing (Flags, Model, Msg, Program, application)
|
|
2
|
+
|
|
3
|
+
{-| Exposed for internal use only (used in generated code).
|
|
4
|
+
|
|
5
|
+
@docs Flags, Model, Msg, Program, application
|
|
6
|
+
|
|
7
|
+
-}
|
|
8
|
+
|
|
9
|
+
import AriaLiveAnnouncer
|
|
10
|
+
import Browser
|
|
11
|
+
import Browser.Dom as Dom
|
|
12
|
+
import Browser.Navigation
|
|
13
|
+
import BuildError exposing (BuildError)
|
|
14
|
+
import Html exposing (Html)
|
|
15
|
+
import Html.Attributes as Attr
|
|
16
|
+
import Http
|
|
17
|
+
import Json.Decode as Decode
|
|
18
|
+
import Json.Encode
|
|
19
|
+
import Pages.ContentCache as ContentCache exposing (ContentCache, ContentJson, contentJsonDecoder)
|
|
20
|
+
import Pages.Flags
|
|
21
|
+
import Pages.Internal.ApplicationType as ApplicationType
|
|
22
|
+
import Pages.Internal.NotFoundReason
|
|
23
|
+
import Pages.Internal.String as String
|
|
24
|
+
import Pages.ProgramConfig exposing (ProgramConfig)
|
|
25
|
+
import Pages.StaticHttpRequest as StaticHttpRequest
|
|
26
|
+
import Path exposing (Path)
|
|
27
|
+
import QueryParams
|
|
28
|
+
import Task
|
|
29
|
+
import Url exposing (Url)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
{-| -}
|
|
33
|
+
type alias Program userModel userMsg pageData sharedData =
|
|
34
|
+
Platform.Program Flags (Model userModel pageData sharedData) (Msg userMsg)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
mainView :
|
|
38
|
+
ProgramConfig userMsg userModel route siteData pageData sharedData
|
|
39
|
+
-> Model userModel pageData sharedData
|
|
40
|
+
-> { title : String, body : Html userMsg }
|
|
41
|
+
mainView config model =
|
|
42
|
+
let
|
|
43
|
+
urls : { currentUrl : Url, basePath : List String }
|
|
44
|
+
urls =
|
|
45
|
+
{ currentUrl = model.url
|
|
46
|
+
, basePath = config.basePath
|
|
47
|
+
}
|
|
48
|
+
in
|
|
49
|
+
case ContentCache.notFoundReason model.contentCache urls of
|
|
50
|
+
Just notFoundReason ->
|
|
51
|
+
Pages.Internal.NotFoundReason.document config.pathPatterns notFoundReason
|
|
52
|
+
|
|
53
|
+
Nothing ->
|
|
54
|
+
case model.pageData of
|
|
55
|
+
Ok pageData ->
|
|
56
|
+
(config.view
|
|
57
|
+
{ path = ContentCache.pathForUrl urls |> Path.join
|
|
58
|
+
, route = config.urlToRoute model.url
|
|
59
|
+
}
|
|
60
|
+
Nothing
|
|
61
|
+
pageData.sharedData
|
|
62
|
+
pageData.pageData
|
|
63
|
+
|> .view
|
|
64
|
+
)
|
|
65
|
+
pageData.userModel
|
|
66
|
+
|
|
67
|
+
Err error ->
|
|
68
|
+
{ title = "Page Data Error"
|
|
69
|
+
, body =
|
|
70
|
+
Html.div [] [ Html.text error ]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
urlsToPagePath :
|
|
75
|
+
{ currentUrl : Url, basePath : List String }
|
|
76
|
+
-> Path
|
|
77
|
+
urlsToPagePath urls =
|
|
78
|
+
urls.currentUrl.path
|
|
79
|
+
|> String.chopForwardSlashes
|
|
80
|
+
|> String.split "/"
|
|
81
|
+
|> List.filter ((/=) "")
|
|
82
|
+
|> List.drop (List.length urls.basePath)
|
|
83
|
+
|> Path.join
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
view :
|
|
87
|
+
ProgramConfig userMsg userModel route siteData pageData sharedData
|
|
88
|
+
-> Model userModel pageData sharedData
|
|
89
|
+
-> Browser.Document (Msg userMsg)
|
|
90
|
+
view config model =
|
|
91
|
+
let
|
|
92
|
+
{ title, body } =
|
|
93
|
+
mainView config model
|
|
94
|
+
in
|
|
95
|
+
{ title = title
|
|
96
|
+
, body =
|
|
97
|
+
[ onViewChangeElement model.url
|
|
98
|
+
, body |> Html.map UserMsg
|
|
99
|
+
, AriaLiveAnnouncer.view model.ariaNavigationAnnouncement
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
onViewChangeElement : Url -> Html msg
|
|
105
|
+
onViewChangeElement currentUrl =
|
|
106
|
+
-- this is a hidden tag
|
|
107
|
+
-- it is used from the JS-side to reliably
|
|
108
|
+
-- check when Elm has changed pages
|
|
109
|
+
-- (and completed rendering the view)
|
|
110
|
+
Html.div
|
|
111
|
+
[ Attr.attribute "data-url" (Url.toString currentUrl)
|
|
112
|
+
, Attr.attribute "display" "none"
|
|
113
|
+
]
|
|
114
|
+
[]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
{-| -}
|
|
118
|
+
type alias Flags =
|
|
119
|
+
Decode.Value
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
{-| -}
|
|
123
|
+
init :
|
|
124
|
+
ProgramConfig userMsg userModel route staticData pageData sharedData
|
|
125
|
+
-> Flags
|
|
126
|
+
-> Url
|
|
127
|
+
-> Browser.Navigation.Key
|
|
128
|
+
-> ( Model userModel pageData sharedData, Cmd (Msg userMsg) )
|
|
129
|
+
init config flags url key =
|
|
130
|
+
let
|
|
131
|
+
contentCache : ContentCache
|
|
132
|
+
contentCache =
|
|
133
|
+
ContentCache.init
|
|
134
|
+
(Maybe.map
|
|
135
|
+
(\cj ->
|
|
136
|
+
( currentPath
|
|
137
|
+
, cj
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
contentJson
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
currentPath : List String
|
|
144
|
+
currentPath =
|
|
145
|
+
flags
|
|
146
|
+
|> Decode.decodeValue
|
|
147
|
+
(Decode.at [ "contentJson", "path" ]
|
|
148
|
+
(Decode.string
|
|
149
|
+
|> Decode.map Path.fromString
|
|
150
|
+
|> Decode.map Path.toSegments
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
|> Result.mapError Decode.errorToString
|
|
154
|
+
|> Result.toMaybe
|
|
155
|
+
|> Maybe.withDefault []
|
|
156
|
+
|
|
157
|
+
contentJson : Maybe ContentJson
|
|
158
|
+
contentJson =
|
|
159
|
+
flags
|
|
160
|
+
|> Decode.decodeValue (Decode.field "contentJson" contentJsonDecoder)
|
|
161
|
+
|> Result.toMaybe
|
|
162
|
+
|
|
163
|
+
urls : { currentUrl : Url, basePath : List String }
|
|
164
|
+
urls =
|
|
165
|
+
{ currentUrl = url
|
|
166
|
+
, basePath = config.basePath
|
|
167
|
+
}
|
|
168
|
+
in
|
|
169
|
+
case contentJson |> Maybe.map .staticData of
|
|
170
|
+
Just justContentJson ->
|
|
171
|
+
let
|
|
172
|
+
pageDataResult : Result BuildError pageData
|
|
173
|
+
pageDataResult =
|
|
174
|
+
StaticHttpRequest.resolve ApplicationType.Browser
|
|
175
|
+
(config.data (config.urlToRoute url))
|
|
176
|
+
justContentJson
|
|
177
|
+
|> Result.mapError (StaticHttpRequest.toBuildError url.path)
|
|
178
|
+
|
|
179
|
+
sharedDataResult : Result BuildError sharedData
|
|
180
|
+
sharedDataResult =
|
|
181
|
+
StaticHttpRequest.resolve ApplicationType.Browser
|
|
182
|
+
config.sharedData
|
|
183
|
+
justContentJson
|
|
184
|
+
|> Result.mapError (StaticHttpRequest.toBuildError url.path)
|
|
185
|
+
|
|
186
|
+
pagePath : Path
|
|
187
|
+
pagePath =
|
|
188
|
+
urlsToPagePath urls
|
|
189
|
+
in
|
|
190
|
+
case Result.map2 Tuple.pair sharedDataResult pageDataResult of
|
|
191
|
+
Ok ( sharedData, pageData ) ->
|
|
192
|
+
let
|
|
193
|
+
userFlags : Pages.Flags.Flags
|
|
194
|
+
userFlags =
|
|
195
|
+
flags
|
|
196
|
+
|> Decode.decodeValue
|
|
197
|
+
(Decode.field "userFlags" Decode.value)
|
|
198
|
+
|> Result.withDefault Json.Encode.null
|
|
199
|
+
|> Pages.Flags.BrowserFlags
|
|
200
|
+
|
|
201
|
+
( userModel, userCmd ) =
|
|
202
|
+
Just
|
|
203
|
+
{ path =
|
|
204
|
+
{ path = pagePath
|
|
205
|
+
, query = url.query
|
|
206
|
+
, fragment = url.fragment
|
|
207
|
+
}
|
|
208
|
+
, metadata = config.urlToRoute url
|
|
209
|
+
, pageUrl =
|
|
210
|
+
Just
|
|
211
|
+
{ protocol = url.protocol
|
|
212
|
+
, host = url.host
|
|
213
|
+
, port_ = url.port_
|
|
214
|
+
, path = pagePath
|
|
215
|
+
, query = url.query |> Maybe.map QueryParams.fromString
|
|
216
|
+
, fragment = url.fragment
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|> config.init userFlags sharedData pageData (Just key)
|
|
220
|
+
|
|
221
|
+
cmd : Cmd (Msg userMsg)
|
|
222
|
+
cmd =
|
|
223
|
+
[ userCmd
|
|
224
|
+
|> Cmd.map UserMsg
|
|
225
|
+
|> Just
|
|
226
|
+
, contentCache
|
|
227
|
+
|> ContentCache.lazyLoad urls
|
|
228
|
+
|> Task.attempt UpdateCache
|
|
229
|
+
|> Just
|
|
230
|
+
]
|
|
231
|
+
|> List.filterMap identity
|
|
232
|
+
|> Cmd.batch
|
|
233
|
+
|
|
234
|
+
initialModel : Model userModel pageData sharedData
|
|
235
|
+
initialModel =
|
|
236
|
+
{ key = key
|
|
237
|
+
, url = url
|
|
238
|
+
, contentCache = contentCache
|
|
239
|
+
, pageData =
|
|
240
|
+
Ok
|
|
241
|
+
{ pageData = pageData
|
|
242
|
+
, sharedData = sharedData
|
|
243
|
+
, userModel = userModel
|
|
244
|
+
}
|
|
245
|
+
, ariaNavigationAnnouncement = ""
|
|
246
|
+
, userFlags = flags
|
|
247
|
+
}
|
|
248
|
+
in
|
|
249
|
+
( { initialModel
|
|
250
|
+
| ariaNavigationAnnouncement = mainView config initialModel |> .title
|
|
251
|
+
}
|
|
252
|
+
, cmd
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
Err error ->
|
|
256
|
+
( { key = key
|
|
257
|
+
, url = url
|
|
258
|
+
, contentCache = contentCache
|
|
259
|
+
, pageData = BuildError.errorToString error |> Err
|
|
260
|
+
, ariaNavigationAnnouncement = "Error"
|
|
261
|
+
, userFlags = flags
|
|
262
|
+
}
|
|
263
|
+
, Cmd.none
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
Nothing ->
|
|
267
|
+
( { key = key
|
|
268
|
+
, url = url
|
|
269
|
+
, contentCache = contentCache
|
|
270
|
+
, pageData = Err "TODO"
|
|
271
|
+
, ariaNavigationAnnouncement = "Error"
|
|
272
|
+
, userFlags = flags
|
|
273
|
+
}
|
|
274
|
+
, Cmd.none
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
{-| -}
|
|
279
|
+
type Msg userMsg
|
|
280
|
+
= LinkClicked Browser.UrlRequest
|
|
281
|
+
| UrlChanged Url
|
|
282
|
+
| UserMsg userMsg
|
|
283
|
+
| UpdateCache (Result Http.Error ( Url, ContentJson, ContentCache ))
|
|
284
|
+
| UpdateCacheAndUrl Url (Result Http.Error ( Url, ContentJson, ContentCache ))
|
|
285
|
+
| PageScrollComplete
|
|
286
|
+
| HotReloadComplete ContentJson
|
|
287
|
+
| NoOp
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
{-| -}
|
|
291
|
+
type alias Model userModel pageData sharedData =
|
|
292
|
+
{ key : Browser.Navigation.Key
|
|
293
|
+
, url : Url
|
|
294
|
+
, contentCache : ContentCache
|
|
295
|
+
, ariaNavigationAnnouncement : String
|
|
296
|
+
, pageData :
|
|
297
|
+
Result
|
|
298
|
+
String
|
|
299
|
+
{ userModel : userModel
|
|
300
|
+
, pageData : pageData
|
|
301
|
+
, sharedData : sharedData
|
|
302
|
+
}
|
|
303
|
+
, userFlags : Decode.Value
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
{-| -}
|
|
308
|
+
update :
|
|
309
|
+
ProgramConfig userMsg userModel route siteData pageData sharedData
|
|
310
|
+
-> Msg userMsg
|
|
311
|
+
-> Model userModel pageData sharedData
|
|
312
|
+
-> ( Model userModel pageData sharedData, Cmd (Msg userMsg) )
|
|
313
|
+
update config appMsg model =
|
|
314
|
+
case appMsg of
|
|
315
|
+
LinkClicked urlRequest ->
|
|
316
|
+
case urlRequest of
|
|
317
|
+
Browser.Internal url ->
|
|
318
|
+
let
|
|
319
|
+
navigatingToSamePage : Bool
|
|
320
|
+
navigatingToSamePage =
|
|
321
|
+
(url.path == model.url.path) && (url /= model.url)
|
|
322
|
+
in
|
|
323
|
+
if navigatingToSamePage then
|
|
324
|
+
-- this is a workaround for an issue with anchor fragment navigation
|
|
325
|
+
-- see https://github.com/elm/browser/issues/39
|
|
326
|
+
( model, Browser.Navigation.load (Url.toString url) )
|
|
327
|
+
|
|
328
|
+
else
|
|
329
|
+
( model
|
|
330
|
+
, Browser.Navigation.pushUrl model.key (Url.toString url)
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
Browser.External href ->
|
|
334
|
+
( model, Browser.Navigation.load href )
|
|
335
|
+
|
|
336
|
+
UrlChanged url ->
|
|
337
|
+
let
|
|
338
|
+
navigatingToSamePage : Bool
|
|
339
|
+
navigatingToSamePage =
|
|
340
|
+
(url.path == model.url.path) && (url /= model.url)
|
|
341
|
+
|
|
342
|
+
urls : { currentUrl : Url, basePath : List String }
|
|
343
|
+
urls =
|
|
344
|
+
{ currentUrl = url
|
|
345
|
+
, basePath = config.basePath
|
|
346
|
+
}
|
|
347
|
+
in
|
|
348
|
+
if navigatingToSamePage then
|
|
349
|
+
-- this saves a few CPU cycles, but also
|
|
350
|
+
-- makes sure we don't send an UpdateCacheAndUrl
|
|
351
|
+
-- which scrolls to the top after page changes.
|
|
352
|
+
-- This is important because we may have just scrolled
|
|
353
|
+
-- to a specific page location for an anchor link.
|
|
354
|
+
model.pageData
|
|
355
|
+
|> Result.map
|
|
356
|
+
(\pageData ->
|
|
357
|
+
let
|
|
358
|
+
updatedPageData : Result String { userModel : userModel, sharedData : sharedData, pageData : pageData }
|
|
359
|
+
updatedPageData =
|
|
360
|
+
Ok
|
|
361
|
+
{ userModel = userModel
|
|
362
|
+
, sharedData = pageData.sharedData
|
|
363
|
+
, pageData = pageData.pageData
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
( userModel, userCmd ) =
|
|
367
|
+
config.update
|
|
368
|
+
pageData.sharedData
|
|
369
|
+
pageData.pageData
|
|
370
|
+
(Just model.key)
|
|
371
|
+
(config.onPageChange
|
|
372
|
+
{ protocol = model.url.protocol
|
|
373
|
+
, host = model.url.host
|
|
374
|
+
, port_ = model.url.port_
|
|
375
|
+
, path = urlPathToPath config urls.currentUrl
|
|
376
|
+
, query = url.query
|
|
377
|
+
, fragment = url.fragment
|
|
378
|
+
, metadata = config.urlToRoute url
|
|
379
|
+
}
|
|
380
|
+
)
|
|
381
|
+
pageData.userModel
|
|
382
|
+
in
|
|
383
|
+
( { model
|
|
384
|
+
| url = url
|
|
385
|
+
, pageData = updatedPageData
|
|
386
|
+
}
|
|
387
|
+
, Cmd.none
|
|
388
|
+
--Cmd.batch
|
|
389
|
+
-- [ userCmd |> Cmd.map UserMsg
|
|
390
|
+
-- , Task.perform (\_ -> PageScrollComplete) (Dom.setViewport 0 0)
|
|
391
|
+
-- ]
|
|
392
|
+
)
|
|
393
|
+
)
|
|
394
|
+
|> Result.withDefault ( model, Cmd.none )
|
|
395
|
+
|
|
396
|
+
else
|
|
397
|
+
( model
|
|
398
|
+
, model.contentCache
|
|
399
|
+
|> ContentCache.lazyLoad urls
|
|
400
|
+
|> Task.attempt (UpdateCacheAndUrl url)
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
UserMsg userMsg ->
|
|
404
|
+
case model.pageData of
|
|
405
|
+
Ok pageData ->
|
|
406
|
+
let
|
|
407
|
+
( userModel, userCmd ) =
|
|
408
|
+
config.update pageData.sharedData pageData.pageData (Just model.key) userMsg pageData.userModel
|
|
409
|
+
|
|
410
|
+
updatedPageData : Result error { userModel : userModel, pageData : pageData, sharedData : sharedData }
|
|
411
|
+
updatedPageData =
|
|
412
|
+
Ok { pageData | userModel = userModel }
|
|
413
|
+
in
|
|
414
|
+
( { model | pageData = updatedPageData }, userCmd |> Cmd.map UserMsg )
|
|
415
|
+
|
|
416
|
+
Err error ->
|
|
417
|
+
( model, Cmd.none )
|
|
418
|
+
|
|
419
|
+
UpdateCache cacheUpdateResult ->
|
|
420
|
+
case cacheUpdateResult of
|
|
421
|
+
-- TODO can there be race conditions here? Might need to set something in the model
|
|
422
|
+
-- to keep track of the last url change
|
|
423
|
+
Ok ( url, contentJson, updatedCache ) ->
|
|
424
|
+
( { model | contentCache = updatedCache }
|
|
425
|
+
, Cmd.none
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
Err _ ->
|
|
429
|
+
-- TODO handle error
|
|
430
|
+
( model, Cmd.none )
|
|
431
|
+
|
|
432
|
+
UpdateCacheAndUrl url cacheUpdateResult ->
|
|
433
|
+
case
|
|
434
|
+
Result.map2 Tuple.pair (cacheUpdateResult |> Result.mapError (\error -> "Http error")) model.pageData
|
|
435
|
+
of
|
|
436
|
+
-- TODO can there be race conditions here? Might need to set something in the model
|
|
437
|
+
-- to keep track of the last url change
|
|
438
|
+
Ok ( ( _, contentJson, updatedCache ), pageData ) ->
|
|
439
|
+
let
|
|
440
|
+
updatedPageData : Result String { userModel : userModel, sharedData : sharedData, pageData : pageData }
|
|
441
|
+
updatedPageData =
|
|
442
|
+
updatedPageStaticData
|
|
443
|
+
|> Result.map
|
|
444
|
+
(\pageStaticData ->
|
|
445
|
+
{ userModel = userModel
|
|
446
|
+
, sharedData = pageData.sharedData
|
|
447
|
+
, pageData = pageStaticData
|
|
448
|
+
}
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
updatedPageStaticData : Result String pageData
|
|
452
|
+
updatedPageStaticData =
|
|
453
|
+
StaticHttpRequest.resolve ApplicationType.Browser
|
|
454
|
+
(config.data (config.urlToRoute url))
|
|
455
|
+
contentJson.staticData
|
|
456
|
+
|> Result.mapError
|
|
457
|
+
(\error ->
|
|
458
|
+
error
|
|
459
|
+
|> StaticHttpRequest.toBuildError ""
|
|
460
|
+
|> BuildError.errorToString
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
( userModel, userCmd ) =
|
|
464
|
+
config.update
|
|
465
|
+
pageData.sharedData
|
|
466
|
+
(updatedPageStaticData |> Result.withDefault pageData.pageData)
|
|
467
|
+
(Just model.key)
|
|
468
|
+
(config.onPageChange
|
|
469
|
+
{ protocol = model.url.protocol
|
|
470
|
+
, host = model.url.host
|
|
471
|
+
, port_ = model.url.port_
|
|
472
|
+
, path = url |> urlPathToPath config
|
|
473
|
+
, query = url.query
|
|
474
|
+
, fragment = url.fragment
|
|
475
|
+
, metadata = config.urlToRoute url
|
|
476
|
+
}
|
|
477
|
+
)
|
|
478
|
+
pageData.userModel
|
|
479
|
+
|
|
480
|
+
updatedModel : Model userModel pageData sharedData
|
|
481
|
+
updatedModel =
|
|
482
|
+
{ model
|
|
483
|
+
| url = url
|
|
484
|
+
, contentCache = updatedCache
|
|
485
|
+
, pageData = updatedPageData
|
|
486
|
+
}
|
|
487
|
+
in
|
|
488
|
+
( { updatedModel
|
|
489
|
+
| ariaNavigationAnnouncement = mainView config updatedModel |> .title
|
|
490
|
+
}
|
|
491
|
+
, Cmd.batch
|
|
492
|
+
[ userCmd |> Cmd.map UserMsg
|
|
493
|
+
, Task.perform (\_ -> PageScrollComplete) (Dom.setViewport 0 0)
|
|
494
|
+
]
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
Err error ->
|
|
498
|
+
{-
|
|
499
|
+
When there is an error loading the content.json, we are either
|
|
500
|
+
1) in the dev server, and should show the relevant DataSource error for the page
|
|
501
|
+
we're navigating to. This could be done more cleanly, but it's simplest to just
|
|
502
|
+
do a fresh page load and use the code path for presenting an error for a fresh page.
|
|
503
|
+
2) In a production app. That means we had a successful build, so there were no DataSource failures,
|
|
504
|
+
so the app must be stale (unless it's in some unexpected state from a bug). In the future,
|
|
505
|
+
it probably makes sense to include some sort of hash of the app version we are fetching, match
|
|
506
|
+
it with the current version that's running, and perform this logic when we see there is a mismatch.
|
|
507
|
+
But for now, if there is any error we do a full page load (not a single-page navigation), which
|
|
508
|
+
gives us a fresh version of the app to make sure things are in sync.
|
|
509
|
+
|
|
510
|
+
-}
|
|
511
|
+
( model
|
|
512
|
+
, url
|
|
513
|
+
|> Url.toString
|
|
514
|
+
|> Browser.Navigation.load
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
PageScrollComplete ->
|
|
518
|
+
( model, Cmd.none )
|
|
519
|
+
|
|
520
|
+
HotReloadComplete contentJson ->
|
|
521
|
+
let
|
|
522
|
+
urls : { currentUrl : Url, basePath : List String }
|
|
523
|
+
urls =
|
|
524
|
+
{ currentUrl = model.url
|
|
525
|
+
, basePath = config.basePath
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
pageDataResult : Result BuildError pageData
|
|
529
|
+
pageDataResult =
|
|
530
|
+
StaticHttpRequest.resolve ApplicationType.Browser
|
|
531
|
+
(config.data (config.urlToRoute model.url))
|
|
532
|
+
contentJson.staticData
|
|
533
|
+
|> Result.mapError (StaticHttpRequest.toBuildError model.url.path)
|
|
534
|
+
|
|
535
|
+
sharedDataResult : Result BuildError sharedData
|
|
536
|
+
sharedDataResult =
|
|
537
|
+
StaticHttpRequest.resolve ApplicationType.Browser
|
|
538
|
+
config.sharedData
|
|
539
|
+
contentJson.staticData
|
|
540
|
+
|> Result.mapError (StaticHttpRequest.toBuildError model.url.path)
|
|
541
|
+
|
|
542
|
+
from404ToNon404 : Bool
|
|
543
|
+
from404ToNon404 =
|
|
544
|
+
not contentJson.is404
|
|
545
|
+
&& was404
|
|
546
|
+
|
|
547
|
+
was404 : Bool
|
|
548
|
+
was404 =
|
|
549
|
+
ContentCache.is404 model.contentCache urls
|
|
550
|
+
in
|
|
551
|
+
case Result.map2 Tuple.pair sharedDataResult pageDataResult of
|
|
552
|
+
Ok ( sharedData, pageData ) ->
|
|
553
|
+
let
|
|
554
|
+
updateResult : Maybe ( userModel, Cmd userMsg )
|
|
555
|
+
updateResult =
|
|
556
|
+
if from404ToNon404 then
|
|
557
|
+
case model.pageData of
|
|
558
|
+
Ok pageData_ ->
|
|
559
|
+
config.update
|
|
560
|
+
sharedData
|
|
561
|
+
pageData
|
|
562
|
+
(Just model.key)
|
|
563
|
+
(config.onPageChange
|
|
564
|
+
{ protocol = model.url.protocol
|
|
565
|
+
, host = model.url.host
|
|
566
|
+
, port_ = model.url.port_
|
|
567
|
+
, path = model.url |> urlPathToPath config
|
|
568
|
+
, query = model.url.query
|
|
569
|
+
, fragment = model.url.fragment
|
|
570
|
+
, metadata = config.urlToRoute model.url
|
|
571
|
+
}
|
|
572
|
+
)
|
|
573
|
+
pageData_.userModel
|
|
574
|
+
|> Just
|
|
575
|
+
|
|
576
|
+
Err error ->
|
|
577
|
+
Nothing
|
|
578
|
+
|
|
579
|
+
else
|
|
580
|
+
Nothing
|
|
581
|
+
in
|
|
582
|
+
case updateResult of
|
|
583
|
+
Just ( userModel, userCmd ) ->
|
|
584
|
+
( { model
|
|
585
|
+
| contentCache =
|
|
586
|
+
ContentCache.init
|
|
587
|
+
(Just
|
|
588
|
+
( urls.currentUrl
|
|
589
|
+
|> config.urlToRoute
|
|
590
|
+
|> config.routeToPath
|
|
591
|
+
, contentJson
|
|
592
|
+
)
|
|
593
|
+
)
|
|
594
|
+
, pageData =
|
|
595
|
+
Ok
|
|
596
|
+
{ pageData = pageData
|
|
597
|
+
, sharedData = sharedData
|
|
598
|
+
, userModel = userModel
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
, Cmd.batch
|
|
602
|
+
[ userCmd |> Cmd.map UserMsg
|
|
603
|
+
]
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
Nothing ->
|
|
607
|
+
let
|
|
608
|
+
pagePath : Path
|
|
609
|
+
pagePath =
|
|
610
|
+
urlsToPagePath urls
|
|
611
|
+
|
|
612
|
+
userFlags : Pages.Flags.Flags
|
|
613
|
+
userFlags =
|
|
614
|
+
model.userFlags
|
|
615
|
+
|> Decode.decodeValue
|
|
616
|
+
(Decode.field "userFlags" Decode.value)
|
|
617
|
+
|> Result.withDefault Json.Encode.null
|
|
618
|
+
|> Pages.Flags.BrowserFlags
|
|
619
|
+
|
|
620
|
+
( userModel, userCmd ) =
|
|
621
|
+
Just
|
|
622
|
+
{ path =
|
|
623
|
+
{ path = pagePath
|
|
624
|
+
, query = model.url.query
|
|
625
|
+
, fragment = model.url.fragment
|
|
626
|
+
}
|
|
627
|
+
, metadata = config.urlToRoute model.url
|
|
628
|
+
, pageUrl =
|
|
629
|
+
Just
|
|
630
|
+
{ protocol = model.url.protocol
|
|
631
|
+
, host = model.url.host
|
|
632
|
+
, port_ = model.url.port_
|
|
633
|
+
, path = pagePath
|
|
634
|
+
, query = model.url.query |> Maybe.map QueryParams.fromString
|
|
635
|
+
, fragment = model.url.fragment
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|> config.init userFlags sharedData pageData (Just model.key)
|
|
639
|
+
in
|
|
640
|
+
( { model
|
|
641
|
+
| contentCache =
|
|
642
|
+
ContentCache.init
|
|
643
|
+
(Just
|
|
644
|
+
( urls.currentUrl
|
|
645
|
+
|> config.urlToRoute
|
|
646
|
+
|> config.routeToPath
|
|
647
|
+
, contentJson
|
|
648
|
+
)
|
|
649
|
+
)
|
|
650
|
+
, pageData =
|
|
651
|
+
model.pageData
|
|
652
|
+
|> Result.map
|
|
653
|
+
(\previousPageData ->
|
|
654
|
+
{ pageData = pageData
|
|
655
|
+
, sharedData = sharedData
|
|
656
|
+
, userModel = previousPageData.userModel
|
|
657
|
+
}
|
|
658
|
+
)
|
|
659
|
+
|> Result.withDefault
|
|
660
|
+
{ pageData = pageData
|
|
661
|
+
, sharedData = sharedData
|
|
662
|
+
, userModel = userModel
|
|
663
|
+
}
|
|
664
|
+
|> Ok
|
|
665
|
+
}
|
|
666
|
+
, userCmd |> Cmd.map UserMsg
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
Err error ->
|
|
670
|
+
( { model
|
|
671
|
+
| contentCache =
|
|
672
|
+
ContentCache.init
|
|
673
|
+
(Just
|
|
674
|
+
( urls.currentUrl
|
|
675
|
+
|> config.urlToRoute
|
|
676
|
+
|> config.routeToPath
|
|
677
|
+
, contentJson
|
|
678
|
+
)
|
|
679
|
+
)
|
|
680
|
+
}
|
|
681
|
+
, Cmd.none
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
NoOp ->
|
|
685
|
+
( model, Cmd.none )
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
{-| -}
|
|
689
|
+
application :
|
|
690
|
+
ProgramConfig userMsg userModel route staticData pageData sharedData
|
|
691
|
+
-> Platform.Program Flags (Model userModel pageData sharedData) (Msg userMsg)
|
|
692
|
+
application config =
|
|
693
|
+
Browser.application
|
|
694
|
+
{ init =
|
|
695
|
+
\flags url key ->
|
|
696
|
+
init config flags url key
|
|
697
|
+
, view = view config
|
|
698
|
+
, update = update config
|
|
699
|
+
, subscriptions =
|
|
700
|
+
\model ->
|
|
701
|
+
let
|
|
702
|
+
urls : { currentUrl : Url }
|
|
703
|
+
urls =
|
|
704
|
+
{ currentUrl = model.url }
|
|
705
|
+
in
|
|
706
|
+
case model.pageData of
|
|
707
|
+
Ok pageData ->
|
|
708
|
+
Sub.batch
|
|
709
|
+
[ config.subscriptions (model.url |> config.urlToRoute)
|
|
710
|
+
(urls.currentUrl |> config.urlToRoute |> config.routeToPath |> Path.join)
|
|
711
|
+
pageData.userModel
|
|
712
|
+
|> Sub.map UserMsg
|
|
713
|
+
, config.fromJsPort
|
|
714
|
+
|> Sub.map
|
|
715
|
+
(\decodeValue ->
|
|
716
|
+
case decodeValue |> Decode.decodeValue (Decode.field "contentJson" contentJsonDecoder) of
|
|
717
|
+
Ok contentJson ->
|
|
718
|
+
HotReloadComplete contentJson
|
|
719
|
+
|
|
720
|
+
Err _ ->
|
|
721
|
+
-- TODO should be no message here
|
|
722
|
+
NoOp
|
|
723
|
+
)
|
|
724
|
+
]
|
|
725
|
+
|
|
726
|
+
Err _ ->
|
|
727
|
+
config.fromJsPort
|
|
728
|
+
|> Sub.map
|
|
729
|
+
(\decodeValue ->
|
|
730
|
+
case decodeValue |> Decode.decodeValue (Decode.field "contentJson" contentJsonDecoder) of
|
|
731
|
+
Ok contentJson ->
|
|
732
|
+
HotReloadComplete contentJson
|
|
733
|
+
|
|
734
|
+
Err _ ->
|
|
735
|
+
-- TODO should be no message here
|
|
736
|
+
NoOp
|
|
737
|
+
)
|
|
738
|
+
, onUrlChange = UrlChanged
|
|
739
|
+
, onUrlRequest = LinkClicked
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
urlPathToPath : ProgramConfig userMsg userModel route siteData pageData sharedData -> Url -> Path
|
|
744
|
+
urlPathToPath config urls =
|
|
745
|
+
urls.path |> Path.fromString
|