elm-pages 3.0.0-beta.1 → 3.0.0-beta.12

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 (103) hide show
  1. package/README.md +10 -1
  2. package/codegen/elm-pages-codegen.js +803 -284
  3. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
  4. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
  5. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
  6. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  7. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  8. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  9. package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
  10. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1326 -121
  11. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +15368 -13272
  12. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  13. package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
  14. package/generator/dead-code-review/elm.json +6 -5
  15. package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +141 -17
  16. package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +218 -0
  17. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
  18. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
  19. package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
  20. package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
  21. package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1326 -121
  22. package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +14574 -12631
  23. package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
  24. package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
  25. package/generator/review/elm.json +6 -6
  26. package/generator/src/SharedTemplate.elm +1 -1
  27. package/generator/src/build.js +81 -51
  28. package/generator/src/cli.js +120 -42
  29. package/generator/src/codegen.js +11 -10
  30. package/generator/src/compatibility-key.js +1 -0
  31. package/generator/src/config.js +41 -0
  32. package/generator/src/dev-server.js +36 -56
  33. package/generator/src/elm-codegen.js +3 -0
  34. package/generator/src/generate-template-module-connector.js +0 -28
  35. package/generator/src/pre-render-html.js +31 -17
  36. package/generator/src/render-worker.js +1 -1
  37. package/generator/src/render.js +224 -37
  38. package/generator/src/request-cache.js +1 -0
  39. package/generator/src/rewrite-elm-json.js +3 -3
  40. package/generator/src/seo-renderer.js +11 -4
  41. package/generator/src/vite-utils.js +78 -0
  42. package/generator/template/app/Api.elm +1 -1
  43. package/generator/template/app/Site.elm +6 -1
  44. package/package.json +12 -13
  45. package/src/ApiRoute.elm +146 -11
  46. package/src/DataSource/Env.elm +27 -3
  47. package/src/DataSource/File.elm +1 -1
  48. package/src/DataSource/Internal/Request.elm +0 -5
  49. package/src/DataSource.elm +50 -53
  50. package/src/Form/Field.elm +1 -1
  51. package/src/Form.elm +33 -33
  52. package/src/Head/Seo.elm +16 -27
  53. package/src/Head.elm +237 -7
  54. package/src/HtmlPrinter.elm +7 -3
  55. package/src/MultiDict.elm +49 -0
  56. package/src/Pages/Generate.elm +548 -103
  57. package/src/Pages/GeneratorProgramConfig.elm +15 -0
  58. package/src/Pages/Internal/NotFoundReason.elm +3 -2
  59. package/src/Pages/Internal/Platform/Cli.elm +91 -27
  60. package/src/Pages/Internal/Platform/Cli.elm.bak +1276 -0
  61. package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
  62. package/src/Pages/Internal/Platform/GeneratorApplication.elm +455 -0
  63. package/src/Pages/Internal/Platform.elm +34 -27
  64. package/src/Pages/Manifest.elm +24 -0
  65. package/src/Pages/ProgramConfig.elm +6 -3
  66. package/src/Pages/Script.elm +100 -0
  67. package/src/PairingHeap.elm +137 -0
  68. package/src/Parser/Extra/String.elm +33 -0
  69. package/src/Parser/Extra.elm +69 -0
  70. package/src/ProgramTest/ComplexQuery.elm +360 -0
  71. package/src/ProgramTest/EffectSimulation.elm +122 -0
  72. package/src/ProgramTest/Failure.elm +367 -0
  73. package/src/ProgramTest/HtmlHighlighter.elm +116 -0
  74. package/src/ProgramTest/HtmlParserHacks.elm +58 -0
  75. package/src/ProgramTest/HtmlRenderer.elm +73 -0
  76. package/src/ProgramTest/Program.elm +30 -0
  77. package/src/ProgramTest/StringLines.elm +26 -0
  78. package/src/ProgramTest/TestHtmlHacks.elm +132 -0
  79. package/src/ProgramTest/TestHtmlParser.elm +201 -0
  80. package/src/ProgramTest.elm +2339 -0
  81. package/src/Query/Extra.elm +55 -0
  82. package/src/Result/Extra.elm +21 -0
  83. package/src/Server/Request.elm +2 -2
  84. package/src/Server/Session.elm +149 -83
  85. package/src/Server/SetCookie.elm +89 -31
  86. package/src/SimulatedEffect/Cmd.elm +69 -0
  87. package/src/SimulatedEffect/Http.elm +330 -0
  88. package/src/SimulatedEffect/Navigation.elm +69 -0
  89. package/src/SimulatedEffect/Ports.elm +62 -0
  90. package/src/SimulatedEffect/Process.elm +24 -0
  91. package/src/SimulatedEffect/Sub.elm +48 -0
  92. package/src/SimulatedEffect/Task.elm +252 -0
  93. package/src/SimulatedEffect/Time.elm +25 -0
  94. package/src/SimulatedEffect.elm +42 -0
  95. package/src/String/Extra.elm +6 -0
  96. package/src/Test/Http.elm +145 -0
  97. package/src/TestResult.elm +35 -0
  98. package/src/TestState.elm +305 -0
  99. package/src/Url/Extra.elm +100 -0
  100. package/src/Vendored/Diff.elm +321 -0
  101. package/src/Vendored/Failure.elm +217 -0
  102. package/src/Vendored/FormatMonochrome.elm +44 -0
  103. package/src/Vendored/Highlightable.elm +53 -0
@@ -0,0 +1,6 @@
1
+ module Pages.Internal.Platform.CompatibilityKey exposing (currentCompatibilityKey)
2
+
3
+
4
+ currentCompatibilityKey : Int
5
+ currentCompatibilityKey =
6
+ 2
@@ -0,0 +1,455 @@
1
+ module Pages.Internal.Platform.GeneratorApplication exposing (Flags, Model, Msg(..), init, requestDecoder, update, app)
2
+
3
+ {-| Exposed for internal use only (used in generated code).
4
+
5
+ @docs Flags, Model, Msg, init, requestDecoder, update, app
6
+
7
+ -}
8
+
9
+ import BuildError exposing (BuildError)
10
+ import Cli.Program as Program exposing (FlagsIncludingArgv)
11
+ import Codec
12
+ import DataSource exposing (DataSource)
13
+ import Dict
14
+ import HtmlPrinter
15
+ import Json.Decode as Decode
16
+ import Json.Encode
17
+ import Pages.GeneratorProgramConfig exposing (GeneratorProgramConfig)
18
+ import Pages.Internal.Platform.CompatibilityKey
19
+ import Pages.Internal.Platform.Effect as Effect exposing (Effect)
20
+ import Pages.Internal.Platform.StaticResponses as StaticResponses exposing (StaticResponses)
21
+ import Pages.Internal.Platform.ToJsPayload as ToJsPayload
22
+ import Pages.Script exposing (Script(..))
23
+ import Pages.StaticHttp.Request
24
+ import Pages.StaticHttpRequest as StaticHttpRequest
25
+ import RequestsAndPending exposing (RequestsAndPending)
26
+ import TerminalText as Terminal
27
+
28
+
29
+ {-| -}
30
+ type alias Flags =
31
+ { compatibilityKey : Int
32
+ }
33
+
34
+
35
+ {-| -}
36
+ type alias Model =
37
+ { staticResponses : StaticResponses
38
+ , errors : List BuildError
39
+ , allRawResponses : RequestsAndPending
40
+ , done : Bool
41
+ }
42
+
43
+
44
+ {-| -}
45
+ type Msg
46
+ = GotDataBatch
47
+ (List
48
+ { request : Pages.StaticHttp.Request.Request
49
+ , response : RequestsAndPending.Response
50
+ }
51
+ )
52
+ | GotBuildError BuildError
53
+
54
+
55
+ {-| -}
56
+ app :
57
+ GeneratorProgramConfig
58
+ -> Program.StatefulProgram Model Msg (DataSource ()) Flags
59
+ app config =
60
+ let
61
+ cliConfig : Program.Config (DataSource ())
62
+ cliConfig =
63
+ case config.data of
64
+ Generator theCliConfig ->
65
+ theCliConfig HtmlPrinter.htmlToString
66
+ in
67
+ Program.stateful
68
+ { init =
69
+ \flags cliOptions ->
70
+ init cliOptions flags
71
+ |> Tuple.mapSecond (perform config)
72
+ , update =
73
+ \cliOptions msg model ->
74
+ update cliOptions msg model
75
+ |> Tuple.mapSecond (perform config)
76
+ , subscriptions =
77
+ \_ ->
78
+ Sub.batch
79
+ [ config.fromJsPort
80
+ |> Sub.map
81
+ (\jsonValue ->
82
+ let
83
+ decoder : Decode.Decoder Msg
84
+ decoder =
85
+ Decode.field "tag" Decode.string
86
+ |> Decode.andThen
87
+ (\tag ->
88
+ case tag of
89
+ "BuildError" ->
90
+ Decode.field "data"
91
+ (Decode.map2
92
+ (\message title ->
93
+ { title = title
94
+ , message = message
95
+ , fatal = True
96
+ , path = "" -- TODO wire in current path here
97
+ }
98
+ )
99
+ (Decode.field "message" Decode.string |> Decode.map Terminal.fromAnsiString)
100
+ (Decode.field "title" Decode.string)
101
+ )
102
+ |> Decode.map GotBuildError
103
+
104
+ _ ->
105
+ Decode.fail "Unhandled msg"
106
+ )
107
+ in
108
+ Decode.decodeValue decoder jsonValue
109
+ |> Result.mapError
110
+ (\error ->
111
+ ("From location 1: "
112
+ ++ (error
113
+ |> Decode.errorToString
114
+ )
115
+ )
116
+ |> BuildError.internal
117
+ |> GotBuildError
118
+ )
119
+ |> mergeResult
120
+ )
121
+ , config.gotBatchSub
122
+ |> Sub.map
123
+ (\newBatch ->
124
+ Decode.decodeValue batchDecoder newBatch
125
+ |> Result.map GotDataBatch
126
+ |> Result.mapError
127
+ (\error ->
128
+ ("From location 2: "
129
+ ++ (error
130
+ |> Decode.errorToString
131
+ )
132
+ )
133
+ |> BuildError.internal
134
+ |> GotBuildError
135
+ )
136
+ |> mergeResult
137
+ )
138
+ ]
139
+ , config = cliConfig
140
+ , printAndExitFailure =
141
+ \string ->
142
+ ToJsPayload.Errors
143
+ [ { title = "Invalid CLI arguments"
144
+ , path = ""
145
+ , message =
146
+ [ Terminal.text string
147
+ ]
148
+ , fatal = True
149
+ }
150
+ ]
151
+ |> Codec.encodeToValue (ToJsPayload.successCodecNew2 "" "")
152
+ |> config.toJsPort
153
+ |> Cmd.map never
154
+ , printAndExitSuccess = \string -> config.toJsPort (Json.Encode.string string) |> Cmd.map never
155
+ }
156
+
157
+
158
+ batchDecoder : Decode.Decoder (List { request : Pages.StaticHttp.Request.Request, response : RequestsAndPending.Response })
159
+ batchDecoder =
160
+ Decode.map2 (\request response -> { request = request, response = response })
161
+ (Decode.field "request" requestDecoder)
162
+ (Decode.field "response" RequestsAndPending.decoder)
163
+ |> Decode.list
164
+
165
+
166
+ mergeResult : Result a a -> a
167
+ mergeResult r =
168
+ case r of
169
+ Ok rr ->
170
+ rr
171
+
172
+ Err rr ->
173
+ rr
174
+
175
+
176
+ {-| -}
177
+ requestDecoder : Decode.Decoder Pages.StaticHttp.Request.Request
178
+ requestDecoder =
179
+ Pages.StaticHttp.Request.codec
180
+ |> Codec.decoder
181
+
182
+
183
+ flatten : GeneratorProgramConfig -> List Effect -> Cmd Msg
184
+ flatten config list =
185
+ Cmd.batch (flattenHelp [] config list)
186
+
187
+
188
+ flattenHelp : List (Cmd Msg) -> GeneratorProgramConfig -> List Effect -> List (Cmd Msg)
189
+ flattenHelp soFar config list =
190
+ case list of
191
+ first :: rest ->
192
+ flattenHelp
193
+ (perform config first :: soFar)
194
+ config
195
+ rest
196
+
197
+ [] ->
198
+ soFar
199
+
200
+
201
+ perform :
202
+ GeneratorProgramConfig
203
+ -> Effect
204
+ -> Cmd Msg
205
+ perform config effect =
206
+ let
207
+ canonicalSiteUrl : String
208
+ canonicalSiteUrl =
209
+ ""
210
+ in
211
+ case effect of
212
+ Effect.NoEffect ->
213
+ Cmd.none
214
+
215
+ Effect.Batch list ->
216
+ flatten config list
217
+
218
+ Effect.FetchHttp unmasked ->
219
+ ToJsPayload.DoHttp unmasked unmasked.useCache
220
+ |> Codec.encoder (ToJsPayload.successCodecNew2 canonicalSiteUrl "")
221
+ |> config.toJsPort
222
+ |> Cmd.map never
223
+
224
+ Effect.SendSinglePage info ->
225
+ let
226
+ currentPagePath : String
227
+ currentPagePath =
228
+ case info of
229
+ ToJsPayload.PageProgress toJsSuccessPayloadNew ->
230
+ toJsSuccessPayloadNew.route
231
+
232
+ _ ->
233
+ ""
234
+ in
235
+ info
236
+ |> Codec.encoder (ToJsPayload.successCodecNew2 canonicalSiteUrl currentPagePath)
237
+ |> config.toJsPort
238
+ |> Cmd.map never
239
+
240
+ Effect.SendSinglePageNew rawBytes info ->
241
+ let
242
+ currentPagePath : String
243
+ currentPagePath =
244
+ case info of
245
+ ToJsPayload.PageProgress toJsSuccessPayloadNew ->
246
+ toJsSuccessPayloadNew.route
247
+
248
+ _ ->
249
+ ""
250
+ in
251
+ { oldThing =
252
+ info
253
+ |> Codec.encoder (ToJsPayload.successCodecNew2 canonicalSiteUrl currentPagePath)
254
+ , binaryPageData = rawBytes
255
+ }
256
+ |> config.sendPageData
257
+ |> Cmd.map never
258
+
259
+ Effect.Continue ->
260
+ Cmd.none
261
+
262
+
263
+
264
+ -- TODO use Json.Decode.Value for flagsDecoder instead of hardcoded record flags
265
+ --flagsDecoder :
266
+ -- Decode.Decoder
267
+ -- { staticHttpCache : RequestsAndPending
268
+ -- , compatibilityKey : Int
269
+ -- }
270
+ --flagsDecoder =
271
+ -- Decode.map3
272
+ -- (\staticHttpCache compatibilityKey ->
273
+ -- { staticHttpCache = staticHttpCache
274
+ -- , isDevServer = isDevServer
275
+ -- , compatibilityKey = compatibilityKey
276
+ -- }
277
+ -- )
278
+ -- (Decode.succeed Dict.empty)
279
+ -- (Decode.field "compatibilityKey" Decode.int)
280
+
281
+
282
+ {-| -}
283
+ init :
284
+ DataSource ()
285
+ -> FlagsIncludingArgv Flags
286
+ -> ( Model, Effect )
287
+ init execute flags =
288
+ if flags.compatibilityKey == Pages.Internal.Platform.CompatibilityKey.currentCompatibilityKey then
289
+ initLegacy execute { staticHttpCache = Dict.empty }
290
+
291
+ else
292
+ let
293
+ elmPackageAheadOfNpmPackage : Bool
294
+ elmPackageAheadOfNpmPackage =
295
+ Pages.Internal.Platform.CompatibilityKey.currentCompatibilityKey > flags.compatibilityKey
296
+
297
+ message : String
298
+ message =
299
+ "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"
300
+ ++ (if elmPackageAheadOfNpmPackage then
301
+ "The elm-pages Elm package is ahead of the elm-pages NPM package. Try updating the elm-pages NPM package?"
302
+
303
+ else
304
+ "The elm-pages NPM package is ahead of the elm-pages Elm package. Try updating the elm-pages Elm package?"
305
+ )
306
+ in
307
+ updateAndSendPortIfDone execute
308
+ { staticResponses = StaticResponses.empty
309
+ , errors =
310
+ [ { title = "Incompatible NPM and Elm package versions"
311
+ , message = [ Terminal.text <| message ]
312
+ , fatal = True
313
+ , path = ""
314
+ }
315
+ ]
316
+ , allRawResponses = Dict.empty
317
+ , done = False
318
+ }
319
+
320
+
321
+ initLegacy :
322
+ DataSource ()
323
+ -> { staticHttpCache : RequestsAndPending }
324
+ -> ( Model, Effect )
325
+ initLegacy execute { staticHttpCache } =
326
+ let
327
+ staticResponses : StaticResponses
328
+ staticResponses =
329
+ StaticResponses.renderApiRequest execute
330
+
331
+ initialModel : Model
332
+ initialModel =
333
+ { staticResponses = staticResponses
334
+ , errors = []
335
+ , allRawResponses = staticHttpCache
336
+ , done = False
337
+ }
338
+ in
339
+ StaticResponses.nextStep initialModel Nothing
340
+ |> nextStepToEffect execute
341
+ initialModel
342
+
343
+
344
+ updateAndSendPortIfDone :
345
+ DataSource ()
346
+ -> Model
347
+ -> ( Model, Effect )
348
+ updateAndSendPortIfDone execute model =
349
+ StaticResponses.nextStep
350
+ model
351
+ Nothing
352
+ |> nextStepToEffect execute model
353
+
354
+
355
+ {-| -}
356
+ update :
357
+ DataSource ()
358
+ -> Msg
359
+ -> Model
360
+ -> ( Model, Effect )
361
+ update execute msg model =
362
+ case msg of
363
+ GotDataBatch batch ->
364
+ let
365
+ updatedModel : Model
366
+ updatedModel =
367
+ model
368
+ |> StaticResponses.batchUpdate batch
369
+ in
370
+ StaticResponses.nextStep
371
+ updatedModel
372
+ Nothing
373
+ |> nextStepToEffect execute updatedModel
374
+
375
+ GotBuildError buildError ->
376
+ let
377
+ updatedModel : Model
378
+ updatedModel =
379
+ { model
380
+ | errors =
381
+ buildError :: model.errors
382
+ }
383
+ in
384
+ StaticResponses.nextStep
385
+ updatedModel
386
+ Nothing
387
+ |> nextStepToEffect execute updatedModel
388
+
389
+
390
+ nextStepToEffect :
391
+ DataSource ()
392
+ -> Model
393
+ -> ( StaticResponses, StaticResponses.NextStep route )
394
+ -> ( Model, Effect )
395
+ nextStepToEffect execute model ( updatedStaticResponsesModel, nextStep ) =
396
+ case nextStep of
397
+ StaticResponses.Continue updatedAllRawResponses httpRequests _ ->
398
+ let
399
+ updatedModel : Model
400
+ updatedModel =
401
+ { model
402
+ | allRawResponses = updatedAllRawResponses
403
+ , staticResponses = updatedStaticResponsesModel
404
+ }
405
+ in
406
+ if List.isEmpty httpRequests then
407
+ nextStepToEffect execute
408
+ updatedModel
409
+ (StaticResponses.nextStep
410
+ updatedModel
411
+ Nothing
412
+ )
413
+
414
+ else
415
+ ( updatedModel
416
+ , (httpRequests
417
+ |> List.map Effect.FetchHttp
418
+ )
419
+ |> Effect.Batch
420
+ )
421
+
422
+ StaticResponses.Finish toJsPayload ->
423
+ case toJsPayload of
424
+ StaticResponses.ApiResponse ->
425
+ let
426
+ apiResponse : Effect
427
+ apiResponse =
428
+ StaticHttpRequest.resolve
429
+ execute
430
+ model.allRawResponses
431
+ |> Result.mapError (StaticHttpRequest.toBuildError "TODO - path from request")
432
+ |> (\response ->
433
+ case response of
434
+ Ok () ->
435
+ { body = Json.Encode.null
436
+ , staticHttpCache = Dict.empty
437
+ , statusCode = 200
438
+ }
439
+ |> ToJsPayload.SendApiResponse
440
+ |> Effect.SendSinglePage
441
+
442
+ Err error ->
443
+ [ error ]
444
+ |> ToJsPayload.Errors
445
+ |> Effect.SendSinglePage
446
+ )
447
+ in
448
+ ( model
449
+ , apiResponse
450
+ )
451
+
452
+ StaticResponses.Errors errors ->
453
+ ( model
454
+ , errors |> ToJsPayload.Errors |> Effect.SendSinglePage
455
+ )
@@ -58,7 +58,7 @@ type alias Program userModel userMsg pageData actionData sharedData errorPage =
58
58
  mainView :
59
59
  ProgramConfig userMsg userModel route pageData actionData sharedData effect (Msg userMsg pageData actionData sharedData errorPage) errorPage
60
60
  -> Model userModel pageData actionData sharedData
61
- -> { title : String, body : Html (Pages.Msg.Msg userMsg) }
61
+ -> { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
62
62
  mainView config model =
63
63
  case model.notFound of
64
64
  Just info ->
@@ -95,7 +95,7 @@ mainView config model =
95
95
  Err error ->
96
96
  { title = "Page Data Error"
97
97
  , body =
98
- Html.div [] [ Html.text error ]
98
+ [ Html.div [] [ Html.text error ] ]
99
99
  }
100
100
 
101
101
 
@@ -124,9 +124,9 @@ view config model =
124
124
  { title = title
125
125
  , body =
126
126
  [ onViewChangeElement model.url
127
- , body |> Html.map UserMsg
128
127
  , AriaLiveAnnouncer.view model.ariaNavigationAnnouncement
129
128
  ]
129
+ ++ List.map (Html.map UserMsg) body
130
130
  }
131
131
 
132
132
 
@@ -423,9 +423,7 @@ update config appMsg model =
423
423
  )
424
424
 
425
425
  else
426
- ( { model
427
- | url = url
428
- }
426
+ ( model
429
427
  , NoEffect
430
428
  )
431
429
  -- TODO is it reasonable to always re-fetch route data if you re-navigate to the current route? Might be a good
@@ -605,27 +603,32 @@ update config appMsg model =
605
603
  , actionData = newActionData
606
604
  }
607
605
 
608
- ( userModel, _ ) =
606
+ ( userModel, userEffect ) =
609
607
  -- TODO if urlWithoutRedirectResolution is different from the url with redirect resolution, then
610
608
  -- instead of calling update, call pushUrl (I think?)
611
609
  -- TODO include user Cmd
612
- config.update model.pageFormState
613
- (model.inFlightFetchers |> toFetcherState)
614
- (model.transition |> Maybe.map Tuple.second)
615
- newSharedData
616
- newPageData
617
- model.key
618
- (config.onPageChange
619
- { protocol = model.url.protocol
620
- , host = model.url.host
621
- , port_ = model.url.port_
622
- , path = urlPathToPath urlWithoutRedirectResolution
623
- , query = urlWithoutRedirectResolution.query
624
- , fragment = urlWithoutRedirectResolution.fragment
625
- , metadata = config.urlToRoute urlWithoutRedirectResolution
626
- }
627
- )
628
- previousPageData.userModel
610
+ if stayingOnSamePath then
611
+ ( previousPageData.userModel, NoEffect )
612
+
613
+ else
614
+ config.update model.pageFormState
615
+ (model.inFlightFetchers |> toFetcherState)
616
+ (model.transition |> Maybe.map Tuple.second)
617
+ newSharedData
618
+ newPageData
619
+ model.key
620
+ (config.onPageChange
621
+ { protocol = model.url.protocol
622
+ , host = model.url.host
623
+ , port_ = model.url.port_
624
+ , path = urlPathToPath urlWithoutRedirectResolution
625
+ , query = urlWithoutRedirectResolution.query
626
+ , fragment = urlWithoutRedirectResolution.fragment
627
+ , metadata = config.urlToRoute urlWithoutRedirectResolution
628
+ }
629
+ )
630
+ previousPageData.userModel
631
+ |> Tuple.mapSecond UserCmd
629
632
 
630
633
  updatedModel : Model userModel pageData actionData sharedData
631
634
  updatedModel =
@@ -656,10 +659,13 @@ update config appMsg model =
656
659
  , currentPath = newUrl.path
657
660
  }
658
661
  , if not stayingOnSamePath && scrollToTopWhenDone then
659
- ScrollToTop
662
+ Batch
663
+ [ ScrollToTop
664
+ , userEffect
665
+ ]
660
666
 
661
667
  else
662
- NoEffect
668
+ userEffect
663
669
  )
664
670
  |> (case maybeUserMsg of
665
671
  Just userMsg ->
@@ -1392,7 +1398,8 @@ loadDataAndUpdateUrl ( newPageData, newSharedData, newActionData ) maybeUserMsg
1392
1398
  , actionData = newActionData
1393
1399
  }
1394
1400
 
1395
- ( userModel, _ ) =
1401
+ -- TODO use userEffect here?
1402
+ ( userModel, userEffect ) =
1396
1403
  -- TODO if urlWithoutRedirectResolution is different from the url with redirect resolution, then
1397
1404
  -- instead of calling update, call pushUrl (I think?)
1398
1405
  -- TODO include user Cmd
@@ -2,6 +2,7 @@ module Pages.Manifest exposing
2
2
  ( Config, Icon
3
3
  , init
4
4
  , withBackgroundColor, withCategories, withDisplayMode, withIarcRatingId, withLang, withOrientation, withShortName, withThemeColor
5
+ , withField
5
6
  , DisplayMode(..), Orientation(..), IconPurpose(..)
6
7
  , generator
7
8
  , toJson
@@ -41,6 +42,11 @@ You pass your `Pages.Manifest.Config` record into the `Pages.application` functi
41
42
  @docs withBackgroundColor, withCategories, withDisplayMode, withIarcRatingId, withLang, withOrientation, withShortName, withThemeColor
42
43
 
43
44
 
45
+ ## Arbitrary Fields Escape Hatch
46
+
47
+ @docs withField
48
+
49
+
44
50
  ## Config options
45
51
 
46
52
  @docs DisplayMode, Orientation, IconPurpose
@@ -61,6 +67,7 @@ import ApiRoute
61
67
  import Color exposing (Color)
62
68
  import Color.Convert
63
69
  import DataSource exposing (DataSource)
70
+ import Dict exposing (Dict)
64
71
  import Head
65
72
  import Json.Encode as Encode
66
73
  import LanguageTag exposing (LanguageTag, emptySubtags)
@@ -123,6 +130,7 @@ init options =
123
130
  , shortName = Nothing
124
131
  , icons = options.icons
125
132
  , lang = usEnglish
133
+ , otherFields = Dict.empty
126
134
  }
127
135
 
128
136
 
@@ -191,6 +199,17 @@ withLang languageTag config =
191
199
  { config | lang = languageTag }
192
200
 
193
201
 
202
+ {-| Escape hatch for specifying fields that aren't exposed through this module otherwise. The possible supported properties
203
+ in a manifest file can change over time, so see [MDN manifest.json docs](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json)
204
+ for a full listing of the current supported properties.
205
+ -}
206
+ withField : String -> Encode.Value -> Config -> Config
207
+ withField name value config =
208
+ { config
209
+ | otherFields = config.otherFields |> Dict.insert name value
210
+ }
211
+
212
+
194
213
  orientationToString : Orientation -> String
195
214
  orientationToString orientation =
196
215
  case orientation of
@@ -239,6 +258,7 @@ type alias Config =
239
258
  , shortName : Maybe String
240
259
  , icons : List Icon
241
260
  , lang : LanguageTag
261
+ , otherFields : Dict String Encode.Value
242
262
  }
243
263
 
244
264
 
@@ -420,6 +440,10 @@ toJson canonicalSiteUrl config =
420
440
  , Encode.string "/" |> Just
421
441
  )
422
442
  ]
443
+ ++ (config.otherFields
444
+ |> Dict.toList
445
+ |> List.map (Tuple.mapSecond Just)
446
+ )
423
447
  |> encodeMaybeObject
424
448
 
425
449
 
@@ -65,7 +65,7 @@ type alias ProgramConfig userMsg userModel route pageData actionData sharedData
65
65
  -> pageData
66
66
  -> Maybe actionData
67
67
  ->
68
- { view : userModel -> { title : String, body : Html (Pages.Msg.Msg userMsg) }
68
+ { view : userModel -> { title : String, body : List (Html (Pages.Msg.Msg userMsg)) }
69
69
  , head : List Head.Tag
70
70
  }
71
71
  , handleRoute : route -> DataSource (Maybe NotFoundReason)
@@ -88,7 +88,10 @@ type alias ProgramConfig userMsg userModel route pageData actionData sharedData
88
88
  }
89
89
  -> userMsg
90
90
  , apiRoutes :
91
- (Html Never -> String)
91
+ (Maybe { indent : Int, newLines : Bool }
92
+ -> Html Never
93
+ -> String
94
+ )
92
95
  -> List (ApiRoute.ApiRoute ApiRoute.Response)
93
96
  , pathPatterns : List RoutePattern
94
97
  , basePath : List String
@@ -98,7 +101,7 @@ type alias ProgramConfig userMsg userModel route pageData actionData sharedData
98
101
  , encodeResponse : ResponseSketch pageData actionData sharedData -> Bytes.Encode.Encoder
99
102
  , encodeAction : actionData -> Bytes.Encode.Encoder
100
103
  , decodeResponse : Bytes.Decode.Decoder (ResponseSketch pageData actionData sharedData)
101
- , globalHeadTags : Maybe ((Html Never -> String) -> DataSource (List Head.Tag))
104
+ , globalHeadTags : Maybe ((Maybe { indent : Int, newLines : Bool } -> Html Never -> String) -> DataSource (List Head.Tag))
102
105
  , cmdToEffect : Cmd userMsg -> effect
103
106
  , perform :
104
107
  { fetchRouteData :