elm-pages 2.1.11 → 3.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolationsTest.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolationsTest.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/{template/public/style.css → review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/lock} +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +6795 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +27617 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +110 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +187 -0
- package/generator/review/elm-stuff/tests-0.19.1/js/package.json +1 -0
- package/generator/review/elm-stuff/tests-0.19.1/src/Reporter.elm +26 -0
- package/generator/review/elm-stuff/tests-0.19.1/src/Runner.elm +62 -0
- package/generator/review/elm.json +13 -4
- package/{src → generator/review/src}/Pages/Review/NoContractViolations.elm +148 -148
- package/generator/review/tests/Pages/Review/NoContractViolationsTest.elm +331 -0
- package/generator/src/RouteBuilder.elm +420 -0
- package/generator/src/SharedTemplate.elm +4 -5
- package/generator/src/SiteConfig.elm +3 -9
- package/generator/src/build.js +308 -95
- package/generator/src/cli.js +103 -8
- package/generator/src/codegen.js +192 -35
- package/generator/src/compile-elm.js +183 -31
- package/generator/src/dev-server.js +353 -96
- package/generator/src/elm-application.json +3 -1
- package/generator/src/elm-codegen.js +34 -0
- package/generator/src/elm-file-constants.js +2 -0
- package/generator/src/error-formatter.js +20 -1
- package/generator/src/generate-template-module-connector.js +120 -924
- package/generator/src/hello.ts +5 -0
- package/generator/src/pre-render-html.js +58 -104
- package/generator/src/render-worker.js +27 -13
- package/generator/src/render.js +252 -197
- package/generator/src/request-cache-fs.js +18 -0
- package/generator/src/request-cache.js +128 -56
- package/generator/src/rewrite-client-elm-json.js +49 -0
- package/generator/src/route-codegen-helpers.js +62 -1
- package/generator/static-code/dev-style.css +22 -0
- package/generator/static-code/elm-pages.js +43 -39
- package/generator/static-code/hmr.js +98 -88
- package/generator/template/app/Api.elm +25 -0
- package/generator/template/app/ErrorPage.elm +38 -0
- package/generator/template/app/Route/Index.elm +87 -0
- package/generator/template/{src → app}/Shared.elm +34 -13
- package/generator/template/app/Site.elm +19 -0
- package/generator/template/{src → app}/View.elm +0 -0
- package/generator/template/elm-pages.config.mjs +5 -0
- package/generator/template/elm.json +1 -0
- package/generator/template/{public/index.js → index.ts} +7 -3
- package/generator/template/package.json +4 -4
- package/generator/template/public/favicon.ico +0 -0
- package/generator/template/public/images/icon-png.png +0 -0
- package/generator/template/src/.gitkeep +0 -0
- package/generator/template/style.css +4 -0
- package/package.json +30 -23
- package/src/ApiRoute.elm +176 -43
- package/src/BuildError.elm +10 -1
- package/src/CookieParser.elm +84 -0
- package/src/DataSource/Env.elm +38 -0
- package/src/DataSource/File.elm +27 -16
- package/src/DataSource/Glob.elm +126 -80
- package/src/DataSource/Http.elm +283 -304
- package/src/DataSource/Internal/Glob.elm +5 -21
- package/src/DataSource/Internal/Request.elm +25 -0
- package/src/DataSource/Port.elm +17 -14
- package/src/DataSource.elm +55 -318
- package/src/Form/Field.elm +717 -0
- package/src/Form/FieldStatus.elm +36 -0
- package/src/Form/FieldView.elm +417 -0
- package/src/Form/FormData.elm +22 -0
- package/src/Form/Validation.elm +391 -0
- package/src/Form/Value.elm +118 -0
- package/src/Form.elm +1683 -0
- package/src/FormData.elm +58 -0
- package/src/FormDecoder.elm +102 -0
- package/src/Head/Seo.elm +12 -4
- package/src/Head.elm +12 -2
- package/src/HtmlPrinter.elm +1 -1
- package/src/Internal/ApiRoute.elm +17 -4
- package/src/Internal/Request.elm +7 -0
- package/src/PageServerResponse.elm +68 -0
- package/src/Pages/ContentCache.elm +1 -229
- package/src/Pages/Fetcher.elm +58 -0
- package/src/Pages/FormState.elm +256 -0
- package/src/Pages/Generate.elm +800 -0
- package/src/Pages/Internal/Form.elm +17 -0
- package/src/Pages/Internal/NotFoundReason.elm +3 -55
- package/src/Pages/Internal/Platform/Cli.elm +777 -579
- package/src/Pages/Internal/Platform/Effect.elm +5 -5
- package/src/Pages/Internal/Platform/StaticResponses.elm +178 -394
- package/src/Pages/Internal/Platform/ToJsPayload.elm +24 -23
- package/src/Pages/Internal/Platform.elm +1244 -504
- package/src/Pages/Internal/ResponseSketch.elm +19 -0
- package/src/Pages/Internal/RoutePattern.elm +596 -45
- package/src/Pages/Manifest.elm +26 -0
- package/src/Pages/Msg.elm +79 -0
- package/src/Pages/ProgramConfig.elm +67 -14
- package/src/Pages/SiteConfig.elm +3 -6
- package/src/Pages/StaticHttp/Request.elm +4 -2
- package/src/Pages/StaticHttpRequest.elm +50 -215
- package/src/Pages/Transition.elm +70 -0
- package/src/Path.elm +1 -0
- package/src/Pattern.elm +98 -0
- package/src/RenderRequest.elm +2 -2
- package/src/RequestsAndPending.elm +111 -9
- package/src/Server/Request.elm +1253 -0
- package/src/Server/Response.elm +292 -0
- package/src/Server/Session.elm +316 -0
- package/src/Server/SetCookie.elm +169 -0
- package/src/TerminalText.elm +1 -1
- package/src/Test/Html/Internal/ElmHtml/Markdown.elm +0 -1
- package/src/Test/Html/Internal/ElmHtml/ToString.elm +1 -1
- package/generator/src/Page.elm +0 -359
- package/generator/src/codegen-template-module.js +0 -183
- package/generator/src/elm-pages-js-minified.js +0 -1
- package/generator/template/src/Api.elm +0 -14
- package/generator/template/src/Page/Index.elm +0 -69
- package/generator/template/src/Site.elm +0 -41
- package/src/DataSource/ServerRequest.elm +0 -60
- package/src/Internal/OptimizedDecoder.elm +0 -18
- package/src/KeepOrDiscard.elm +0 -6
- package/src/OptimizedDecoder/Pipeline.elm +0 -335
- package/src/OptimizedDecoder.elm +0 -818
- package/src/Pages/Internal/ApplicationType.elm +0 -6
- package/src/Pages/Secrets.elm +0 -83
- package/src/Secrets.elm +0 -111
- package/src/SecretsDict.elm +0 -45
package/src/FormData.elm
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module FormData exposing (encode, parse)
|
|
2
|
+
|
|
3
|
+
import Dict exposing (Dict)
|
|
4
|
+
import List.NonEmpty exposing (NonEmpty)
|
|
5
|
+
import Url
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
parse : String -> Dict String ( String, List String )
|
|
9
|
+
parse rawString =
|
|
10
|
+
rawString
|
|
11
|
+
|> String.split "&"
|
|
12
|
+
|> List.foldl
|
|
13
|
+
(\entry soFar ->
|
|
14
|
+
case entry |> String.split "=" of
|
|
15
|
+
[ key, value ] ->
|
|
16
|
+
let
|
|
17
|
+
newValue : String
|
|
18
|
+
newValue =
|
|
19
|
+
value |> decode
|
|
20
|
+
in
|
|
21
|
+
Dict.update (decode key)
|
|
22
|
+
(\maybeExistingList ->
|
|
23
|
+
maybeExistingList
|
|
24
|
+
|> Maybe.map (\( first, rest ) -> ( first, rest ++ [ newValue ] ))
|
|
25
|
+
|> Maybe.withDefault ( newValue, [] )
|
|
26
|
+
|> Just
|
|
27
|
+
)
|
|
28
|
+
soFar
|
|
29
|
+
|
|
30
|
+
_ ->
|
|
31
|
+
--( entry |> Url.percentDecode |> Maybe.withDefault entry, ( "", [] ) )
|
|
32
|
+
soFar
|
|
33
|
+
)
|
|
34
|
+
Dict.empty
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
decode : String -> String
|
|
38
|
+
decode string =
|
|
39
|
+
string
|
|
40
|
+
|> String.replace "+" " "
|
|
41
|
+
|> Url.percentDecode
|
|
42
|
+
|> Maybe.withDefault ""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
encode : Dict String (NonEmpty String) -> String
|
|
46
|
+
encode dict =
|
|
47
|
+
dict
|
|
48
|
+
|> Dict.toList
|
|
49
|
+
|> List.concatMap
|
|
50
|
+
(\( key, values ) ->
|
|
51
|
+
values
|
|
52
|
+
|> List.NonEmpty.toList
|
|
53
|
+
|> List.map
|
|
54
|
+
(\value ->
|
|
55
|
+
Url.percentEncode key ++ "=" ++ Url.percentEncode value
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|> String.join "&"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
module FormDecoder exposing (encodeFormData, formDataOnSubmit, methodToString)
|
|
2
|
+
|
|
3
|
+
import Form.FormData as FormData exposing (FormData)
|
|
4
|
+
import Html
|
|
5
|
+
import Html.Events
|
|
6
|
+
import Json.Decode as Decode
|
|
7
|
+
import Json.Encode
|
|
8
|
+
import Url
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
formDataOnSubmit : Html.Attribute FormData
|
|
12
|
+
formDataOnSubmit =
|
|
13
|
+
Html.Events.preventDefaultOn "submit"
|
|
14
|
+
(Decode.map4 FormData
|
|
15
|
+
(Decode.value
|
|
16
|
+
|> Decode.andThen
|
|
17
|
+
(\decodeValue ->
|
|
18
|
+
case Decode.decodeValue tuplesDecoder (decoder decodeValue) of
|
|
19
|
+
Ok decoded ->
|
|
20
|
+
Decode.succeed decoded
|
|
21
|
+
|
|
22
|
+
Err error ->
|
|
23
|
+
Decode.succeed
|
|
24
|
+
[ ( "error"
|
|
25
|
+
, Decode.errorToString error
|
|
26
|
+
)
|
|
27
|
+
]
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
(currentForm "method" methodDecoder)
|
|
31
|
+
(currentForm "action" Decode.string)
|
|
32
|
+
(currentForm "id" (Decode.nullable Decode.string))
|
|
33
|
+
|> Decode.map alwaysPreventDefault
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
currentForm : String -> Decode.Decoder a -> Decode.Decoder a
|
|
38
|
+
currentForm field decoder_ =
|
|
39
|
+
Decode.oneOf
|
|
40
|
+
[ Decode.at [ "submitter", "form" ] decoder_
|
|
41
|
+
, Decode.at [ "currentTarget", field ] decoder_
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
methodDecoder : Decode.Decoder FormData.Method
|
|
46
|
+
methodDecoder =
|
|
47
|
+
Decode.string
|
|
48
|
+
|> Decode.map
|
|
49
|
+
(\methodString ->
|
|
50
|
+
case methodString |> String.toUpper of
|
|
51
|
+
"GET" ->
|
|
52
|
+
FormData.Get
|
|
53
|
+
|
|
54
|
+
"POST" ->
|
|
55
|
+
FormData.Post
|
|
56
|
+
|
|
57
|
+
_ ->
|
|
58
|
+
-- TODO what about "dialog" method? Is it okay for that to be interpreted as GET,
|
|
59
|
+
-- or should there be a variant for that?
|
|
60
|
+
FormData.Get
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
decoder : Decode.Value -> Decode.Value
|
|
65
|
+
decoder event =
|
|
66
|
+
Json.Encode.string "REPLACE_ME_WITH_FORM_TO_STRING"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
alwaysPreventDefault : msg -> ( msg, Bool )
|
|
70
|
+
alwaysPreventDefault msg =
|
|
71
|
+
( msg, True )
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
tuplesDecoder : Decode.Decoder (List ( String, String ))
|
|
75
|
+
tuplesDecoder =
|
|
76
|
+
Decode.list
|
|
77
|
+
(Decode.map2 Tuple.pair
|
|
78
|
+
(Decode.index 0 Decode.string)
|
|
79
|
+
(Decode.index 1 Decode.string)
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
methodToString : FormData.Method -> String
|
|
84
|
+
methodToString method =
|
|
85
|
+
case method of
|
|
86
|
+
FormData.Get ->
|
|
87
|
+
"GET"
|
|
88
|
+
|
|
89
|
+
FormData.Post ->
|
|
90
|
+
"POST"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
encodeFormData :
|
|
94
|
+
FormData
|
|
95
|
+
-> String
|
|
96
|
+
encodeFormData data =
|
|
97
|
+
data.fields
|
|
98
|
+
|> List.map
|
|
99
|
+
(\( name, value ) ->
|
|
100
|
+
Url.percentEncode name ++ "=" ++ Url.percentEncode value
|
|
101
|
+
)
|
|
102
|
+
|> String.join "&"
|
package/src/Head/Seo.elm
CHANGED
|
@@ -51,6 +51,8 @@ with the `head` function that you pass to your Pages config (`Pages.application`
|
|
|
51
51
|
|
|
52
52
|
import Head
|
|
53
53
|
import Head.Twitter as Twitter
|
|
54
|
+
import LanguageTag.Country
|
|
55
|
+
import LanguageTag.Language
|
|
54
56
|
import Pages.Url
|
|
55
57
|
|
|
56
58
|
|
|
@@ -307,6 +309,13 @@ type alias Common =
|
|
|
307
309
|
}
|
|
308
310
|
|
|
309
311
|
|
|
312
|
+
localeToString : Locale -> String
|
|
313
|
+
localeToString ( language, territory ) =
|
|
314
|
+
LanguageTag.Language.toCodeString language
|
|
315
|
+
++ "_"
|
|
316
|
+
++ LanguageTag.Country.toCodeString territory
|
|
317
|
+
|
|
318
|
+
|
|
310
319
|
tagsForCommon : Common -> List ( String, Maybe Head.AttributeValue )
|
|
311
320
|
tagsForCommon common =
|
|
312
321
|
tagsForImage common.image
|
|
@@ -316,12 +325,12 @@ tagsForCommon common =
|
|
|
316
325
|
, ( "og:url", common.canonicalUrlOverride |> Maybe.map Head.raw |> Maybe.withDefault Head.currentPageFullUrl |> Just )
|
|
317
326
|
, ( "og:description", Just (Head.raw common.description) )
|
|
318
327
|
, ( "og:site_name", Just (Head.raw common.siteName) )
|
|
319
|
-
, ( "og:locale", common.locale |> Maybe.map Head.raw )
|
|
328
|
+
, ( "og:locale", common.locale |> Maybe.map localeToString |> Maybe.map Head.raw )
|
|
320
329
|
]
|
|
321
330
|
++ (common.alternateLocales
|
|
322
331
|
|> List.map
|
|
323
332
|
(\alternateLocale ->
|
|
324
|
-
( "og:locale:alternate", alternateLocale |> Head.raw |> Just )
|
|
333
|
+
( "og:locale:alternate", alternateLocale |> localeToString |> Head.raw |> Just )
|
|
325
334
|
)
|
|
326
335
|
)
|
|
327
336
|
++ Twitter.rawTags common.twitterCard
|
|
@@ -350,8 +359,7 @@ tagsForAudio audio =
|
|
|
350
359
|
|
|
351
360
|
|
|
352
361
|
type alias Locale =
|
|
353
|
-
|
|
354
|
-
String
|
|
362
|
+
( LanguageTag.Language.Language, LanguageTag.Country.Country )
|
|
355
363
|
|
|
356
364
|
|
|
357
365
|
type Content
|
package/src/Head.elm
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Head exposing
|
|
2
2
|
( Tag, metaName, metaProperty, metaRedirect
|
|
3
|
-
, rssLink, sitemapLink, rootLanguage
|
|
3
|
+
, rssLink, sitemapLink, rootLanguage, manifestLink
|
|
4
4
|
, structuredData
|
|
5
5
|
, AttributeValue
|
|
6
6
|
, currentPageFullUrl, urlAttribute, raw
|
|
@@ -18,7 +18,7 @@ But this module might be useful if you have a special use case, or if you are
|
|
|
18
18
|
writing a plugin package to extend `elm-pages`.
|
|
19
19
|
|
|
20
20
|
@docs Tag, metaName, metaProperty, metaRedirect
|
|
21
|
-
@docs rssLink, sitemapLink, rootLanguage
|
|
21
|
+
@docs rssLink, sitemapLink, rootLanguage, manifestLink
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
## Structured Data
|
|
@@ -211,6 +211,16 @@ canonicalLink maybePath =
|
|
|
211
211
|
]
|
|
212
212
|
|
|
213
213
|
|
|
214
|
+
{-| Let's you link to your manifest.json file, see <https://developer.mozilla.org/en-US/docs/Web/Manifest#deploying_a_manifest>.
|
|
215
|
+
-}
|
|
216
|
+
manifestLink : String -> Tag
|
|
217
|
+
manifestLink path =
|
|
218
|
+
node "link"
|
|
219
|
+
[ ( "rel", raw "manifest" )
|
|
220
|
+
, ( "href", raw path )
|
|
221
|
+
]
|
|
222
|
+
|
|
223
|
+
|
|
214
224
|
{-| Add a link to the site's RSS feed.
|
|
215
225
|
|
|
216
226
|
Example:
|
package/src/HtmlPrinter.elm
CHANGED
|
@@ -3,11 +3,14 @@ module Internal.ApiRoute exposing
|
|
|
3
3
|
, ApiRouteBuilder(..)
|
|
4
4
|
, firstMatch
|
|
5
5
|
, pathToMatches
|
|
6
|
+
, toPattern
|
|
6
7
|
, tryMatch
|
|
7
8
|
, withRoutes
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
import DataSource exposing (DataSource)
|
|
12
|
+
import Head
|
|
13
|
+
import Pattern exposing (Pattern)
|
|
11
14
|
import Regex exposing (Regex)
|
|
12
15
|
|
|
13
16
|
|
|
@@ -44,12 +47,20 @@ type ApiRoute response
|
|
|
44
47
|
, matchesToResponse : String -> DataSource (Maybe response)
|
|
45
48
|
, buildTimeRoutes : DataSource (List String)
|
|
46
49
|
, handleRoute : String -> DataSource Bool
|
|
50
|
+
, pattern : Pattern
|
|
51
|
+
, kind : String
|
|
52
|
+
, globalHeadTags : Maybe (DataSource (List Head.Tag))
|
|
47
53
|
}
|
|
48
54
|
|
|
49
55
|
|
|
56
|
+
toPattern : ApiRoute response -> Pattern
|
|
57
|
+
toPattern (ApiRoute { pattern }) =
|
|
58
|
+
pattern
|
|
59
|
+
|
|
60
|
+
|
|
50
61
|
{-| -}
|
|
51
62
|
pathToMatches : String -> ApiRouteBuilder a constructor -> List String
|
|
52
|
-
pathToMatches path (ApiRouteBuilder pattern _ _ _) =
|
|
63
|
+
pathToMatches path (ApiRouteBuilder _ pattern _ _ _) =
|
|
53
64
|
Regex.find
|
|
54
65
|
(Regex.fromString pattern
|
|
55
66
|
|> Maybe.withDefault Regex.never
|
|
@@ -57,18 +68,19 @@ pathToMatches path (ApiRouteBuilder pattern _ _ _) =
|
|
|
57
68
|
path
|
|
58
69
|
|> List.concatMap .submatches
|
|
59
70
|
|> List.filterMap identity
|
|
71
|
+
|> List.reverse
|
|
60
72
|
|
|
61
73
|
|
|
62
74
|
{-| -}
|
|
63
75
|
withRoutes : (constructor -> List (List String)) -> ApiRouteBuilder a constructor -> List String
|
|
64
|
-
withRoutes buildUrls (ApiRouteBuilder _ _ toString constructor) =
|
|
76
|
+
withRoutes buildUrls (ApiRouteBuilder _ _ _ toString constructor) =
|
|
65
77
|
buildUrls (constructor [])
|
|
66
78
|
|> List.map toString
|
|
67
79
|
|
|
68
80
|
|
|
69
81
|
{-| -}
|
|
70
82
|
tryMatch : String -> ApiRouteBuilder response constructor -> Maybe response
|
|
71
|
-
tryMatch path (ApiRouteBuilder pattern handler _ _) =
|
|
83
|
+
tryMatch path (ApiRouteBuilder _ pattern handler _ _) =
|
|
72
84
|
let
|
|
73
85
|
matches : List String
|
|
74
86
|
matches =
|
|
@@ -79,6 +91,7 @@ tryMatch path (ApiRouteBuilder pattern handler _ _) =
|
|
|
79
91
|
path
|
|
80
92
|
|> List.concatMap .submatches
|
|
81
93
|
|> List.filterMap identity
|
|
94
|
+
|> List.reverse
|
|
82
95
|
in
|
|
83
96
|
handler matches
|
|
84
97
|
|> Just
|
|
@@ -86,4 +99,4 @@ tryMatch path (ApiRouteBuilder pattern handler _ _) =
|
|
|
86
99
|
|
|
87
100
|
{-| -}
|
|
88
101
|
type ApiRouteBuilder a constructor
|
|
89
|
-
= ApiRouteBuilder String (List String -> a) (List String -> String) (List String -> constructor)
|
|
102
|
+
= ApiRouteBuilder Pattern String (List String -> a) (List String -> String) (List String -> constructor)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module PageServerResponse exposing (PageServerResponse(..), Response, toJson, toRedirect)
|
|
2
|
+
|
|
3
|
+
import Dict
|
|
4
|
+
import Json.Encode
|
|
5
|
+
import List.Extra
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
type PageServerResponse data error
|
|
9
|
+
= RenderPage
|
|
10
|
+
{ statusCode : Int
|
|
11
|
+
, headers : List ( String, String )
|
|
12
|
+
}
|
|
13
|
+
data
|
|
14
|
+
| ServerResponse Response
|
|
15
|
+
| ErrorPage error { headers : List ( String, String ) }
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
toRedirect : Response -> Maybe { statusCode : Int, location : String }
|
|
19
|
+
toRedirect response =
|
|
20
|
+
response.headers
|
|
21
|
+
|> Dict.fromList
|
|
22
|
+
|> Dict.get "Location"
|
|
23
|
+
|> Maybe.andThen
|
|
24
|
+
(\location ->
|
|
25
|
+
if response.statusCode == 302 then
|
|
26
|
+
Just { statusCode = 302, location = location }
|
|
27
|
+
|
|
28
|
+
else
|
|
29
|
+
Nothing
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
type alias Response =
|
|
34
|
+
{ statusCode : Int
|
|
35
|
+
, headers : List ( String, String )
|
|
36
|
+
, body : Maybe String
|
|
37
|
+
, isBase64Encoded : Bool
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
toJson : Response -> Json.Encode.Value
|
|
42
|
+
toJson serverResponse =
|
|
43
|
+
Json.Encode.object
|
|
44
|
+
[ ( "body", serverResponse.body |> Maybe.map Json.Encode.string |> Maybe.withDefault Json.Encode.null )
|
|
45
|
+
, ( "statusCode", serverResponse.statusCode |> Json.Encode.int )
|
|
46
|
+
, ( "headers"
|
|
47
|
+
, serverResponse.headers
|
|
48
|
+
|> collectMultiValueHeaders
|
|
49
|
+
|> List.map (Tuple.mapSecond (Json.Encode.list Json.Encode.string))
|
|
50
|
+
|> Json.Encode.object
|
|
51
|
+
)
|
|
52
|
+
, ( "kind", Json.Encode.string "server-response" )
|
|
53
|
+
, ( "isBase64Encoded", Json.Encode.bool serverResponse.isBase64Encoded )
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
collectMultiValueHeaders : List ( String, String ) -> List ( String, List String )
|
|
58
|
+
collectMultiValueHeaders headers =
|
|
59
|
+
headers
|
|
60
|
+
|> List.Extra.groupWhile
|
|
61
|
+
(\( key1, _ ) ( key2, _ ) -> key1 == key2)
|
|
62
|
+
|> List.map
|
|
63
|
+
(\( ( key, firstValue ), otherValues ) ->
|
|
64
|
+
( key
|
|
65
|
+
, firstValue
|
|
66
|
+
:: (otherValues |> List.map Tuple.second)
|
|
67
|
+
)
|
|
68
|
+
)
|
|
@@ -1,212 +1,16 @@
|
|
|
1
1
|
module Pages.ContentCache exposing
|
|
2
|
-
(
|
|
3
|
-
, ContentJson
|
|
4
|
-
, Entry(..)
|
|
5
|
-
, Path
|
|
6
|
-
, contentJsonDecoder
|
|
7
|
-
, init
|
|
8
|
-
, is404
|
|
9
|
-
, lazyLoad
|
|
10
|
-
, notFoundReason
|
|
2
|
+
( Path
|
|
11
3
|
, pathForUrl
|
|
12
4
|
)
|
|
13
5
|
|
|
14
|
-
import BuildError exposing (BuildError)
|
|
15
|
-
import Codec
|
|
16
|
-
import Dict exposing (Dict)
|
|
17
|
-
import Html exposing (Html)
|
|
18
|
-
import Http
|
|
19
|
-
import Json.Decode as Decode
|
|
20
|
-
import Pages.Internal.NotFoundReason
|
|
21
6
|
import Pages.Internal.String as String
|
|
22
|
-
import RequestsAndPending exposing (RequestsAndPending)
|
|
23
|
-
import Task exposing (Task)
|
|
24
7
|
import Url exposing (Url)
|
|
25
8
|
|
|
26
9
|
|
|
27
|
-
type alias Content =
|
|
28
|
-
List ( List String, { extension : String, frontMatter : String, body : Maybe String } )
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
type alias ContentCache =
|
|
32
|
-
Dict Path Entry
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
type alias Errors =
|
|
36
|
-
List ( Html Never, BuildError )
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
type alias ContentCacheInner =
|
|
40
|
-
Dict Path Entry
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
type Entry
|
|
44
|
-
= Parsed ContentJson
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
type alias ParseError =
|
|
48
|
-
String
|
|
49
|
-
|
|
50
|
-
|
|
51
10
|
type alias Path =
|
|
52
11
|
List String
|
|
53
12
|
|
|
54
13
|
|
|
55
|
-
init :
|
|
56
|
-
Maybe ( Path, ContentJson )
|
|
57
|
-
-> ContentCache
|
|
58
|
-
init maybeInitialPageContent =
|
|
59
|
-
case maybeInitialPageContent of
|
|
60
|
-
Nothing ->
|
|
61
|
-
Dict.empty
|
|
62
|
-
|
|
63
|
-
Just ( urls, contentJson ) ->
|
|
64
|
-
Dict.singleton urls (Parsed contentJson)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
{-| Get from the Cache... if it's not already parsed, it will
|
|
68
|
-
parse it before returning it and store the parsed version in the Cache
|
|
69
|
-
-}
|
|
70
|
-
lazyLoad :
|
|
71
|
-
{ currentUrl : Url, basePath : List String }
|
|
72
|
-
-> ContentCache
|
|
73
|
-
-> Task Http.Error ( Url, ContentJson, ContentCache )
|
|
74
|
-
lazyLoad urls cache =
|
|
75
|
-
case Dict.get (pathForUrl urls) cache of
|
|
76
|
-
Just (Parsed contentJson) ->
|
|
77
|
-
Task.succeed
|
|
78
|
-
( urls.currentUrl
|
|
79
|
-
, contentJson
|
|
80
|
-
, cache
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
Nothing ->
|
|
84
|
-
urls.currentUrl
|
|
85
|
-
|> httpTask
|
|
86
|
-
|> Task.map
|
|
87
|
-
(\downloadedContent ->
|
|
88
|
-
( urls.currentUrl
|
|
89
|
-
, downloadedContent
|
|
90
|
-
, update
|
|
91
|
-
cache
|
|
92
|
-
urls
|
|
93
|
-
downloadedContent
|
|
94
|
-
)
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
httpTask : Url -> Task Http.Error ContentJson
|
|
99
|
-
httpTask url =
|
|
100
|
-
Http.task
|
|
101
|
-
{ method = "GET"
|
|
102
|
-
, headers = []
|
|
103
|
-
, url =
|
|
104
|
-
url.path
|
|
105
|
-
|> String.chopForwardSlashes
|
|
106
|
-
|> String.split "/"
|
|
107
|
-
|> List.filter ((/=) "")
|
|
108
|
-
|> (\l -> l ++ [ "content.json" ])
|
|
109
|
-
|> String.join "/"
|
|
110
|
-
|> String.append "/"
|
|
111
|
-
, body = Http.emptyBody
|
|
112
|
-
, resolver =
|
|
113
|
-
Http.stringResolver
|
|
114
|
-
(\response ->
|
|
115
|
-
case response of
|
|
116
|
-
Http.BadUrl_ url_ ->
|
|
117
|
-
Err (Http.BadUrl url_)
|
|
118
|
-
|
|
119
|
-
Http.Timeout_ ->
|
|
120
|
-
Err Http.Timeout
|
|
121
|
-
|
|
122
|
-
Http.NetworkError_ ->
|
|
123
|
-
Err Http.NetworkError
|
|
124
|
-
|
|
125
|
-
Http.BadStatus_ metadata _ ->
|
|
126
|
-
Err (Http.BadStatus metadata.statusCode)
|
|
127
|
-
|
|
128
|
-
Http.GoodStatus_ _ body ->
|
|
129
|
-
body
|
|
130
|
-
|> Decode.decodeString contentJsonDecoder
|
|
131
|
-
|> Result.mapError (\err -> Http.BadBody (Decode.errorToString err))
|
|
132
|
-
)
|
|
133
|
-
, timeout = Nothing
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
type alias ContentJson =
|
|
138
|
-
{ staticData : RequestsAndPending
|
|
139
|
-
, is404 : Bool
|
|
140
|
-
, path : Maybe String
|
|
141
|
-
, notFoundReason : Maybe Pages.Internal.NotFoundReason.Payload
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
contentJsonDecoder : Decode.Decoder ContentJson
|
|
146
|
-
contentJsonDecoder =
|
|
147
|
-
Decode.field "is404" Decode.bool
|
|
148
|
-
|> Decode.andThen
|
|
149
|
-
(\is404Value ->
|
|
150
|
-
if is404Value then
|
|
151
|
-
Decode.map4 ContentJson
|
|
152
|
-
(Decode.succeed Dict.empty)
|
|
153
|
-
(Decode.succeed is404Value)
|
|
154
|
-
(Decode.field "path" Decode.string |> Decode.map Just)
|
|
155
|
-
(Decode.at [ "staticData", "notFoundReason" ]
|
|
156
|
-
(Decode.string
|
|
157
|
-
|> Decode.andThen
|
|
158
|
-
(\jsonString ->
|
|
159
|
-
case
|
|
160
|
-
Decode.decodeString
|
|
161
|
-
(Codec.decoder Pages.Internal.NotFoundReason.codec
|
|
162
|
-
|> Decode.map Just
|
|
163
|
-
)
|
|
164
|
-
jsonString
|
|
165
|
-
of
|
|
166
|
-
Ok okValue ->
|
|
167
|
-
Decode.succeed okValue
|
|
168
|
-
|
|
169
|
-
Err error ->
|
|
170
|
-
Decode.fail
|
|
171
|
-
(Decode.errorToString error)
|
|
172
|
-
)
|
|
173
|
-
)
|
|
174
|
-
)
|
|
175
|
-
|
|
176
|
-
else
|
|
177
|
-
Decode.map4 ContentJson
|
|
178
|
-
(Decode.field "staticData" RequestsAndPending.decoder)
|
|
179
|
-
(Decode.succeed is404Value)
|
|
180
|
-
(Decode.succeed Nothing)
|
|
181
|
-
(Decode.succeed Nothing)
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
update :
|
|
186
|
-
ContentCache
|
|
187
|
-
-> { currentUrl : Url, basePath : List String }
|
|
188
|
-
-> ContentJson
|
|
189
|
-
-> ContentCache
|
|
190
|
-
update cache urls rawContent =
|
|
191
|
-
Dict.update
|
|
192
|
-
(pathForUrl urls)
|
|
193
|
-
(\entry ->
|
|
194
|
-
case entry of
|
|
195
|
-
Just (Parsed _) ->
|
|
196
|
-
entry
|
|
197
|
-
|
|
198
|
-
Nothing ->
|
|
199
|
-
{ staticData = rawContent.staticData
|
|
200
|
-
, is404 = rawContent.is404
|
|
201
|
-
, path = rawContent.path
|
|
202
|
-
, notFoundReason = rawContent.notFoundReason
|
|
203
|
-
}
|
|
204
|
-
|> Parsed
|
|
205
|
-
|> Just
|
|
206
|
-
)
|
|
207
|
-
cache
|
|
208
|
-
|
|
209
|
-
|
|
210
14
|
pathForUrl : { currentUrl : Url, basePath : List String } -> Path
|
|
211
15
|
pathForUrl { currentUrl, basePath } =
|
|
212
16
|
currentUrl.path
|
|
@@ -214,35 +18,3 @@ pathForUrl { currentUrl, basePath } =
|
|
|
214
18
|
|> String.split "/"
|
|
215
19
|
|> List.filter ((/=) "")
|
|
216
20
|
|> List.drop (List.length basePath)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
is404 :
|
|
220
|
-
ContentCache
|
|
221
|
-
-> { currentUrl : Url, basePath : List String }
|
|
222
|
-
-> Bool
|
|
223
|
-
is404 dict urls =
|
|
224
|
-
dict
|
|
225
|
-
|> Dict.get (pathForUrl urls)
|
|
226
|
-
|> Maybe.map
|
|
227
|
-
(\entry ->
|
|
228
|
-
case entry of
|
|
229
|
-
Parsed data ->
|
|
230
|
-
data.is404
|
|
231
|
-
)
|
|
232
|
-
|> Maybe.withDefault True
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
notFoundReason :
|
|
236
|
-
ContentCache
|
|
237
|
-
-> { currentUrl : Url, basePath : List String }
|
|
238
|
-
-> Maybe Pages.Internal.NotFoundReason.Payload
|
|
239
|
-
notFoundReason dict urls =
|
|
240
|
-
dict
|
|
241
|
-
|> Dict.get (pathForUrl urls)
|
|
242
|
-
|> Maybe.map
|
|
243
|
-
(\entry ->
|
|
244
|
-
case entry of
|
|
245
|
-
Parsed data ->
|
|
246
|
-
data.notFoundReason
|
|
247
|
-
)
|
|
248
|
-
|> Maybe.withDefault Nothing
|