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
package/src/Head.elm
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
module Head exposing
|
|
2
|
+
( Tag, metaName, metaProperty, metaRedirect
|
|
3
|
+
, rssLink, sitemapLink, rootLanguage
|
|
4
|
+
, structuredData
|
|
5
|
+
, AttributeValue
|
|
6
|
+
, currentPageFullUrl, urlAttribute, raw
|
|
7
|
+
, appleTouchIcon, icon
|
|
8
|
+
, toJson, canonicalLink
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
{-| This module contains low-level functions for building up
|
|
12
|
+
values that will be rendered into the page's `<head>` tag
|
|
13
|
+
when you run `elm-pages build`. Most likely the `Head.Seo` module
|
|
14
|
+
will do everything you need out of the box, and you will just need to import `Head`
|
|
15
|
+
so you can use the `Tag` type in your type annotations.
|
|
16
|
+
|
|
17
|
+
But this module might be useful if you have a special use case, or if you are
|
|
18
|
+
writing a plugin package to extend `elm-pages`.
|
|
19
|
+
|
|
20
|
+
@docs Tag, metaName, metaProperty, metaRedirect
|
|
21
|
+
@docs rssLink, sitemapLink, rootLanguage
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Structured Data
|
|
25
|
+
|
|
26
|
+
@docs structuredData
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## `AttributeValue`s
|
|
30
|
+
|
|
31
|
+
@docs AttributeValue
|
|
32
|
+
@docs currentPageFullUrl, urlAttribute, raw
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## Icons
|
|
36
|
+
|
|
37
|
+
@docs appleTouchIcon, icon
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## Functions for use by generated code
|
|
41
|
+
|
|
42
|
+
@docs toJson, canonicalLink
|
|
43
|
+
|
|
44
|
+
-}
|
|
45
|
+
|
|
46
|
+
import Json.Encode
|
|
47
|
+
import LanguageTag exposing (LanguageTag)
|
|
48
|
+
import MimeType
|
|
49
|
+
import Pages.Internal.String as String
|
|
50
|
+
import Pages.Url
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
{-| Values that can be passed to the generated `Pages.application` config
|
|
54
|
+
through the `head` function.
|
|
55
|
+
-}
|
|
56
|
+
type Tag
|
|
57
|
+
= Tag Details
|
|
58
|
+
| StructuredData Json.Encode.Value
|
|
59
|
+
| RootModifier String String
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
type alias Details =
|
|
63
|
+
{ name : String
|
|
64
|
+
, attributes : List ( String, AttributeValue )
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
{-| You can learn more about structured data in [Google's intro to structured data](https://developers.google.com/search/docs/guides/intro-structured-data).
|
|
69
|
+
|
|
70
|
+
When you add a `structuredData` item to one of your pages in `elm-pages`, it will add `json-ld` data to your document that looks like this:
|
|
71
|
+
|
|
72
|
+
```html
|
|
73
|
+
<script type="application/ld+json">
|
|
74
|
+
{
|
|
75
|
+
"@context":"http://schema.org/",
|
|
76
|
+
"@type":"Article",
|
|
77
|
+
"headline":"Extensible Markdown Parsing in Pure Elm",
|
|
78
|
+
"description":"Introducing a new parser that extends your palette with no additional syntax",
|
|
79
|
+
"image":"https://elm-pages.com/images/article-covers/extensible-markdown-parsing.jpg",
|
|
80
|
+
"author":{
|
|
81
|
+
"@type":"Person",
|
|
82
|
+
"name":"Dillon Kearns"
|
|
83
|
+
},
|
|
84
|
+
"publisher":{
|
|
85
|
+
"@type":"Person",
|
|
86
|
+
"name":"Dillon Kearns"
|
|
87
|
+
},
|
|
88
|
+
"url":"https://elm-pages.com/blog/extensible-markdown-parsing-in-elm",
|
|
89
|
+
"datePublished":"2019-10-08",
|
|
90
|
+
"mainEntityOfPage":{
|
|
91
|
+
"@type":"SoftwareSourceCode",
|
|
92
|
+
"codeRepository":"https://github.com/dillonkearns/elm-pages",
|
|
93
|
+
"description":"A statically typed site generator for Elm.",
|
|
94
|
+
"author":"Dillon Kearns",
|
|
95
|
+
"programmingLanguage":{
|
|
96
|
+
"@type":"ComputerLanguage",
|
|
97
|
+
"url":"http://elm-lang.org/",
|
|
98
|
+
"name":"Elm",
|
|
99
|
+
"image":"http://elm-lang.org/",
|
|
100
|
+
"identifier":"http://elm-lang.org/"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
</script>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
To get that data, you would write this in your `elm-pages` head tags:
|
|
108
|
+
|
|
109
|
+
import Json.Encode as Encode
|
|
110
|
+
|
|
111
|
+
{-| <https://schema.org/Article>
|
|
112
|
+
-}
|
|
113
|
+
encodeArticle :
|
|
114
|
+
{ title : String
|
|
115
|
+
, description : String
|
|
116
|
+
, author : StructuredDataHelper { authorMemberOf | personOrOrganization : () } authorPossibleFields
|
|
117
|
+
, publisher : StructuredDataHelper { publisherMemberOf | personOrOrganization : () } publisherPossibleFields
|
|
118
|
+
, url : String
|
|
119
|
+
, imageUrl : String
|
|
120
|
+
, datePublished : String
|
|
121
|
+
, mainEntityOfPage : Encode.Value
|
|
122
|
+
}
|
|
123
|
+
-> Head.Tag
|
|
124
|
+
encodeArticle info =
|
|
125
|
+
Encode.object
|
|
126
|
+
[ ( "@context", Encode.string "http://schema.org/" )
|
|
127
|
+
, ( "@type", Encode.string "Article" )
|
|
128
|
+
, ( "headline", Encode.string info.title )
|
|
129
|
+
, ( "description", Encode.string info.description )
|
|
130
|
+
, ( "image", Encode.string info.imageUrl )
|
|
131
|
+
, ( "author", encode info.author )
|
|
132
|
+
, ( "publisher", encode info.publisher )
|
|
133
|
+
, ( "url", Encode.string info.url )
|
|
134
|
+
, ( "datePublished", Encode.string info.datePublished )
|
|
135
|
+
, ( "mainEntityOfPage", info.mainEntityOfPage )
|
|
136
|
+
]
|
|
137
|
+
|> Head.structuredData
|
|
138
|
+
|
|
139
|
+
Take a look at this [Google Search Gallery](https://developers.google.com/search/docs/guides/search-gallery)
|
|
140
|
+
to see some examples of how structured data can be used by search engines to give rich search results. It can help boost
|
|
141
|
+
your rankings, get better engagement for your content, and also make your content more accessible. For example,
|
|
142
|
+
voice assistant devices can make use of structured data. If you're hosting a conference and want to make the event
|
|
143
|
+
date and location easy for attendees to find, this can make that information more accessible.
|
|
144
|
+
|
|
145
|
+
For the current version of API, you'll need to make sure that the format is correct and contains the required and recommended
|
|
146
|
+
structure.
|
|
147
|
+
|
|
148
|
+
Check out <https://schema.org> for a comprehensive listing of possible data types and fields. And take a look at
|
|
149
|
+
Google's [Structured Data Testing Tool](https://search.google.com/structured-data/testing-tool)
|
|
150
|
+
too make sure that your structured data is valid and includes the recommended values.
|
|
151
|
+
|
|
152
|
+
In the future, `elm-pages` will likely support a typed API, but schema.org is a massive spec, and changes frequently.
|
|
153
|
+
And there are multiple sources of information on the possible and recommended structure. So it will take some time
|
|
154
|
+
for the right API design to evolve. In the meantime, this allows you to make use of this for SEO purposes.
|
|
155
|
+
|
|
156
|
+
-}
|
|
157
|
+
structuredData : Json.Encode.Value -> Tag
|
|
158
|
+
structuredData value =
|
|
159
|
+
StructuredData value
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
{-| Create a raw `AttributeValue` (as opposed to some kind of absolute URL).
|
|
163
|
+
-}
|
|
164
|
+
raw : String -> AttributeValue
|
|
165
|
+
raw value =
|
|
166
|
+
Raw value
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
{-| Create an `AttributeValue` from an `ImagePath`.
|
|
170
|
+
-}
|
|
171
|
+
urlAttribute : Pages.Url.Url -> AttributeValue
|
|
172
|
+
urlAttribute value =
|
|
173
|
+
FullUrl value
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
{-| Create an `AttributeValue` representing the current page's full url.
|
|
177
|
+
-}
|
|
178
|
+
currentPageFullUrl : AttributeValue
|
|
179
|
+
currentPageFullUrl =
|
|
180
|
+
FullUrlToCurrentPage
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
{-| Values, such as between the `<>`'s here:
|
|
184
|
+
|
|
185
|
+
```html
|
|
186
|
+
<meta name="<THIS IS A VALUE>" content="<THIS IS A VALUE>" />
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
-}
|
|
190
|
+
type AttributeValue
|
|
191
|
+
= Raw String
|
|
192
|
+
| FullUrl Pages.Url.Url
|
|
193
|
+
| FullUrlToCurrentPage
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
{-| It's recommended that you use the `Seo` module helpers, which will provide this
|
|
197
|
+
for you, rather than directly using this.
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
|
|
201
|
+
Head.canonicalLink "https://elm-pages.com"
|
|
202
|
+
|
|
203
|
+
-}
|
|
204
|
+
canonicalLink : Maybe String -> Tag
|
|
205
|
+
canonicalLink maybePath =
|
|
206
|
+
node "link"
|
|
207
|
+
[ ( "rel", raw "canonical" )
|
|
208
|
+
, ( "href"
|
|
209
|
+
, maybePath |> Maybe.map raw |> Maybe.withDefault currentPageFullUrl
|
|
210
|
+
)
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
{-| Add a link to the site's RSS feed.
|
|
215
|
+
|
|
216
|
+
Example:
|
|
217
|
+
|
|
218
|
+
rssLink "/feed.xml"
|
|
219
|
+
|
|
220
|
+
```html
|
|
221
|
+
<link rel="alternate" type="application/rss+xml" href="/rss.xml">
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
-}
|
|
225
|
+
rssLink : String -> Tag
|
|
226
|
+
rssLink url =
|
|
227
|
+
node "link"
|
|
228
|
+
[ ( "rel", raw "alternate" )
|
|
229
|
+
, ( "type", raw "application/rss+xml" )
|
|
230
|
+
, ( "href", raw url )
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
{-| -}
|
|
235
|
+
icon : List ( Int, Int ) -> MimeType.MimeImage -> Pages.Url.Url -> Tag
|
|
236
|
+
icon sizes imageMimeType imageUrl =
|
|
237
|
+
-- TODO allow "any" for sizes value
|
|
238
|
+
[ ( "rel", raw "icon" |> Just )
|
|
239
|
+
, ( "sizes"
|
|
240
|
+
, sizes
|
|
241
|
+
|> nonEmptyList
|
|
242
|
+
|> Maybe.map sizesToString
|
|
243
|
+
|> Maybe.map raw
|
|
244
|
+
)
|
|
245
|
+
, ( "type", imageMimeType |> MimeType.Image |> MimeType.toString |> raw |> Just )
|
|
246
|
+
, ( "href", urlAttribute imageUrl |> Just )
|
|
247
|
+
]
|
|
248
|
+
|> filterMaybeValues
|
|
249
|
+
|> node "link"
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
nonEmptyList : List a -> Maybe (List a)
|
|
253
|
+
nonEmptyList list =
|
|
254
|
+
if List.isEmpty list then
|
|
255
|
+
Nothing
|
|
256
|
+
|
|
257
|
+
else
|
|
258
|
+
Just list
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
{-| Note: the type must be png.
|
|
262
|
+
See <https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html>.
|
|
263
|
+
|
|
264
|
+
If a size is provided, it will be turned into square dimensions as per the recommendations here: <https://developers.google.com/web/fundamentals/design-and-ux/browser-customization/#safari>
|
|
265
|
+
|
|
266
|
+
Images must be png's, and non-transparent images are recommended. Current recommended dimensions are 180px and 192px.
|
|
267
|
+
|
|
268
|
+
-}
|
|
269
|
+
appleTouchIcon : Maybe Int -> Pages.Url.Url -> Tag
|
|
270
|
+
appleTouchIcon maybeSize imageUrl =
|
|
271
|
+
[ ( "rel", raw "apple-touch-icon" |> Just )
|
|
272
|
+
, ( "sizes"
|
|
273
|
+
, maybeSize
|
|
274
|
+
|> Maybe.map (\size -> sizesToString [ ( size, size ) ])
|
|
275
|
+
|> Maybe.map raw
|
|
276
|
+
)
|
|
277
|
+
, ( "href", urlAttribute imageUrl |> Just )
|
|
278
|
+
]
|
|
279
|
+
|> filterMaybeValues
|
|
280
|
+
|> node "link"
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
{-| Set the language for a page.
|
|
284
|
+
|
|
285
|
+
<https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang>
|
|
286
|
+
|
|
287
|
+
import Head
|
|
288
|
+
import LanguageTag
|
|
289
|
+
import LanguageTag.Language
|
|
290
|
+
|
|
291
|
+
LanguageTag.Language.de -- sets the page's language to German
|
|
292
|
+
|> LanguageTag.build LanguageTag.emptySubtags
|
|
293
|
+
|> Head.rootLanguage
|
|
294
|
+
|
|
295
|
+
This results pre-rendered HTML with a global lang tag set.
|
|
296
|
+
|
|
297
|
+
```html
|
|
298
|
+
<html lang="no">
|
|
299
|
+
...
|
|
300
|
+
</html>
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
-}
|
|
304
|
+
rootLanguage : LanguageTag -> Tag
|
|
305
|
+
rootLanguage languageTag =
|
|
306
|
+
languageTag
|
|
307
|
+
|> LanguageTag.toString
|
|
308
|
+
|> RootModifier "lang"
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
-- TODO rootDirection for
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
filterMaybeValues : List ( String, Maybe a ) -> List ( String, a )
|
|
316
|
+
filterMaybeValues list =
|
|
317
|
+
list
|
|
318
|
+
|> List.filterMap
|
|
319
|
+
(\( key, maybeValue ) ->
|
|
320
|
+
case maybeValue of
|
|
321
|
+
Just value ->
|
|
322
|
+
Just ( key, value )
|
|
323
|
+
|
|
324
|
+
Nothing ->
|
|
325
|
+
Nothing
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
sizesToString : List ( Int, Int ) -> String
|
|
330
|
+
sizesToString sizes =
|
|
331
|
+
sizes
|
|
332
|
+
|> List.map (\( x, y ) -> String.fromInt x ++ "x" ++ String.fromInt y)
|
|
333
|
+
|> String.join " "
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
{-| Add a link to the site's RSS feed.
|
|
337
|
+
|
|
338
|
+
Example:
|
|
339
|
+
|
|
340
|
+
sitemapLink "/feed.xml"
|
|
341
|
+
|
|
342
|
+
```html
|
|
343
|
+
<link rel="sitemap" type="application/xml" href="/sitemap.xml">
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
-}
|
|
347
|
+
sitemapLink : String -> Tag
|
|
348
|
+
sitemapLink url =
|
|
349
|
+
node "link"
|
|
350
|
+
[ ( "rel", raw "sitemap" )
|
|
351
|
+
, ( "type", raw "application/xml" )
|
|
352
|
+
, ( "href", raw url )
|
|
353
|
+
]
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
{-| Example:
|
|
357
|
+
|
|
358
|
+
Head.metaProperty "fb:app_id" (Head.raw "123456789")
|
|
359
|
+
|
|
360
|
+
Results in `<meta property="fb:app_id" content="123456789" />`
|
|
361
|
+
|
|
362
|
+
-}
|
|
363
|
+
metaProperty : String -> AttributeValue -> Tag
|
|
364
|
+
metaProperty property content =
|
|
365
|
+
node "meta"
|
|
366
|
+
[ ( "property", raw property )
|
|
367
|
+
, ( "content", content )
|
|
368
|
+
]
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
{-| Example:
|
|
372
|
+
|
|
373
|
+
Head.metaName "twitter:card" (Head.raw "summary_large_image")
|
|
374
|
+
|
|
375
|
+
Results in `<meta name="twitter:card" content="summary_large_image" />`
|
|
376
|
+
|
|
377
|
+
-}
|
|
378
|
+
metaName : String -> AttributeValue -> Tag
|
|
379
|
+
metaName name content =
|
|
380
|
+
node "meta"
|
|
381
|
+
[ ( "name", Raw name )
|
|
382
|
+
, ( "content", content )
|
|
383
|
+
]
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
{-| Example:
|
|
387
|
+
|
|
388
|
+
metaRedirect (Raw "0; url=https://google.com")
|
|
389
|
+
|
|
390
|
+
Results in `<meta http-equiv="refresh" content="0; url=https://google.com" />`
|
|
391
|
+
|
|
392
|
+
-}
|
|
393
|
+
metaRedirect : AttributeValue -> Tag
|
|
394
|
+
metaRedirect content =
|
|
395
|
+
node "meta"
|
|
396
|
+
[ ( "http-equiv", Raw "refresh" )
|
|
397
|
+
, ( "content", content )
|
|
398
|
+
]
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
{-| Low-level function for creating a tag for the HTML document's `<head>`.
|
|
402
|
+
-}
|
|
403
|
+
node : String -> List ( String, AttributeValue ) -> Tag
|
|
404
|
+
node name attributes =
|
|
405
|
+
Tag
|
|
406
|
+
{ name = name
|
|
407
|
+
, attributes = attributes
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
{-| Feel free to use this, but in 99% of cases you won't need it. The generated
|
|
412
|
+
code will run this for you to generate your `manifest.json` file automatically!
|
|
413
|
+
-}
|
|
414
|
+
toJson : String -> String -> Tag -> Json.Encode.Value
|
|
415
|
+
toJson canonicalSiteUrl currentPagePath tag =
|
|
416
|
+
case tag of
|
|
417
|
+
Tag headTag ->
|
|
418
|
+
Json.Encode.object
|
|
419
|
+
[ ( "name", Json.Encode.string headTag.name )
|
|
420
|
+
, ( "attributes", Json.Encode.list (encodeProperty canonicalSiteUrl currentPagePath) headTag.attributes )
|
|
421
|
+
, ( "type", Json.Encode.string "head" )
|
|
422
|
+
]
|
|
423
|
+
|
|
424
|
+
StructuredData value ->
|
|
425
|
+
Json.Encode.object
|
|
426
|
+
[ ( "contents", value )
|
|
427
|
+
, ( "type", Json.Encode.string "json-ld" )
|
|
428
|
+
]
|
|
429
|
+
|
|
430
|
+
RootModifier key value ->
|
|
431
|
+
Json.Encode.object
|
|
432
|
+
[ ( "type", Json.Encode.string "root" )
|
|
433
|
+
, ( "keyValuePair", Json.Encode.list Json.Encode.string [ key, value ] )
|
|
434
|
+
]
|
|
435
|
+
|
|
436
|
+
|
|
437
|
+
encodeProperty : String -> String -> ( String, AttributeValue ) -> Json.Encode.Value
|
|
438
|
+
encodeProperty canonicalSiteUrl currentPagePath ( name, value ) =
|
|
439
|
+
case value of
|
|
440
|
+
Raw rawValue ->
|
|
441
|
+
Json.Encode.list Json.Encode.string [ name, rawValue ]
|
|
442
|
+
|
|
443
|
+
FullUrlToCurrentPage ->
|
|
444
|
+
Json.Encode.list Json.Encode.string [ name, joinPaths canonicalSiteUrl currentPagePath ]
|
|
445
|
+
|
|
446
|
+
FullUrl url ->
|
|
447
|
+
Json.Encode.list Json.Encode.string [ name, Pages.Url.toAbsoluteUrl canonicalSiteUrl url ]
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
joinPaths : String -> String -> String
|
|
451
|
+
joinPaths base path =
|
|
452
|
+
String.chopEnd "/" base ++ "/" ++ String.chopStart "/" path
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module HtmlPrinter exposing (htmlToString)
|
|
2
|
+
|
|
3
|
+
import Html exposing (Html)
|
|
4
|
+
import Json.Decode as Decode
|
|
5
|
+
import Json.Encode
|
|
6
|
+
import Test.Html.Internal.ElmHtml.InternalTypes exposing (decodeElmHtml)
|
|
7
|
+
import Test.Html.Internal.ElmHtml.ToString exposing (defaultFormatOptions, nodeToStringWithOptions)
|
|
8
|
+
import VirtualDom
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
htmlToString : Html msg -> String
|
|
12
|
+
htmlToString viewHtml =
|
|
13
|
+
case
|
|
14
|
+
Decode.decodeValue
|
|
15
|
+
(decodeElmHtml (\_ _ -> VirtualDom.Normal (Decode.succeed ())))
|
|
16
|
+
(asJsonView viewHtml)
|
|
17
|
+
of
|
|
18
|
+
Ok str ->
|
|
19
|
+
nodeToStringWithOptions defaultFormatOptions str
|
|
20
|
+
|
|
21
|
+
Err err ->
|
|
22
|
+
"Error: " ++ Decode.errorToString err
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
asJsonView : Html msg -> Decode.Value
|
|
26
|
+
asJsonView x =
|
|
27
|
+
Json.Encode.string "REPLACE_ME_WITH_JSON_STRINGIFY"
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Internal.ApiRoute exposing
|
|
2
|
+
( ApiRoute(..)
|
|
3
|
+
, ApiRouteBuilder(..)
|
|
4
|
+
, firstMatch
|
|
5
|
+
, pathToMatches
|
|
6
|
+
, tryMatch
|
|
7
|
+
, withRoutes
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
import DataSource exposing (DataSource)
|
|
11
|
+
import Regex exposing (Regex)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
{-| -}
|
|
15
|
+
firstMatch : String -> List (ApiRoute response) -> Maybe (ApiRoute response)
|
|
16
|
+
firstMatch path handlers =
|
|
17
|
+
case handlers of
|
|
18
|
+
[] ->
|
|
19
|
+
Nothing
|
|
20
|
+
|
|
21
|
+
first :: rest ->
|
|
22
|
+
case tryMatchDone path first of
|
|
23
|
+
Just response ->
|
|
24
|
+
Just response
|
|
25
|
+
|
|
26
|
+
Nothing ->
|
|
27
|
+
firstMatch path rest
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
{-| -}
|
|
31
|
+
tryMatchDone : String -> ApiRoute response -> Maybe (ApiRoute response)
|
|
32
|
+
tryMatchDone path (ApiRoute handler) =
|
|
33
|
+
if Regex.contains handler.regex path then
|
|
34
|
+
Just (ApiRoute handler)
|
|
35
|
+
|
|
36
|
+
else
|
|
37
|
+
Nothing
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
{-| -}
|
|
41
|
+
type ApiRoute response
|
|
42
|
+
= ApiRoute
|
|
43
|
+
{ regex : Regex
|
|
44
|
+
, matchesToResponse : String -> DataSource (Maybe response)
|
|
45
|
+
, buildTimeRoutes : DataSource (List String)
|
|
46
|
+
, handleRoute : String -> DataSource Bool
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
{-| -}
|
|
51
|
+
pathToMatches : String -> ApiRouteBuilder a constructor -> List String
|
|
52
|
+
pathToMatches path (ApiRouteBuilder pattern _ _ _) =
|
|
53
|
+
Regex.find
|
|
54
|
+
(Regex.fromString pattern
|
|
55
|
+
|> Maybe.withDefault Regex.never
|
|
56
|
+
)
|
|
57
|
+
path
|
|
58
|
+
|> List.concatMap .submatches
|
|
59
|
+
|> List.filterMap identity
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
{-| -}
|
|
63
|
+
withRoutes : (constructor -> List (List String)) -> ApiRouteBuilder a constructor -> List String
|
|
64
|
+
withRoutes buildUrls (ApiRouteBuilder _ _ toString constructor) =
|
|
65
|
+
buildUrls (constructor [])
|
|
66
|
+
|> List.map toString
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
{-| -}
|
|
70
|
+
tryMatch : String -> ApiRouteBuilder response constructor -> Maybe response
|
|
71
|
+
tryMatch path (ApiRouteBuilder pattern handler _ _) =
|
|
72
|
+
let
|
|
73
|
+
matches : List String
|
|
74
|
+
matches =
|
|
75
|
+
Regex.find
|
|
76
|
+
(Regex.fromString pattern
|
|
77
|
+
|> Maybe.withDefault Regex.never
|
|
78
|
+
)
|
|
79
|
+
path
|
|
80
|
+
|> List.concatMap .submatches
|
|
81
|
+
|> List.filterMap identity
|
|
82
|
+
in
|
|
83
|
+
handler matches
|
|
84
|
+
|> Just
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
{-| -}
|
|
88
|
+
type ApiRouteBuilder a constructor
|
|
89
|
+
= ApiRouteBuilder String (List String -> a) (List String -> String) (List String -> constructor)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Internal.OptimizedDecoder exposing (OptimizedDecoder(..), jd, jde)
|
|
2
|
+
|
|
3
|
+
import Json.Decode
|
|
4
|
+
import Json.Decode.Exploration
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
type OptimizedDecoder a
|
|
8
|
+
= OptimizedDecoder (Json.Decode.Decoder a) (Json.Decode.Exploration.Decoder a)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
jd : OptimizedDecoder a -> Json.Decode.Decoder a
|
|
12
|
+
jd (OptimizedDecoder jd_ _) =
|
|
13
|
+
jd_
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
jde : OptimizedDecoder a -> Json.Decode.Exploration.Decoder a
|
|
17
|
+
jde (OptimizedDecoder _ jde_) =
|
|
18
|
+
jde_
|