elm-pages 3.0.0-beta.0 → 3.0.0-beta.10
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/README.md +10 -1
- package/codegen/elm-pages-codegen.js +39026 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateData.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-DeadCodeEliminateDataTest.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Reporter.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmi +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Runner.elmo +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/lock +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/o.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +6795 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25835 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +110 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +187 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/package.json +1 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/src/Reporter.elm +26 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/src/Runner.elm +62 -0
- package/generator/dead-code-review/elm.json +35 -0
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +304 -0
- package/generator/dead-code-review/src/ReviewConfig.elm +9 -0
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +673 -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/o.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/src/SharedTemplate.elm +1 -1
- package/generator/src/build.js +75 -42
- package/generator/src/compatibility-key.js +1 -0
- package/generator/src/config.js +41 -0
- package/generator/src/dev-server.js +36 -56
- package/generator/src/generate-template-module-connector.js +2 -28
- package/generator/src/pre-render-html.js +31 -17
- package/generator/src/render.js +2 -0
- package/generator/src/seo-renderer.js +11 -4
- package/generator/src/vite-utils.js +78 -0
- package/generator/template/app/Api.elm +1 -1
- package/generator/template/app/Site.elm +6 -1
- package/package.json +5 -3
- package/src/ApiRoute.elm +0 -3
- package/src/DataSource/File.elm +1 -1
- package/src/DataSource/Internal/Request.elm +0 -5
- package/src/DataSource.elm +39 -31
- package/src/Form/Field.elm +1 -1
- package/src/Form.elm +1 -1
- package/src/Head/Seo.elm +16 -27
- package/src/Head.elm +126 -0
- package/src/HtmlPrinter.elm +7 -3
- package/src/Pages/Generate.elm +544 -102
- package/src/Pages/Internal/NotFoundReason.elm +3 -2
- package/src/Pages/Internal/Platform/Cli.elm +91 -27
- package/src/Pages/Internal/Platform/Cli.elm.bak +1276 -0
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +6 -0
- package/src/Pages/Internal/Platform.elm +34 -27
- package/src/Pages/ProgramConfig.elm +6 -3
- package/src/Server/Session.elm +149 -83
- package/src/Server/SetCookie.elm +89 -31
package/src/ApiRoute.elm
CHANGED
|
@@ -11,9 +11,6 @@ module ApiRoute exposing
|
|
|
11
11
|
to a DataSource so you can pull in HTTP data, etc. Because ApiRoutes don't hydrate into Elm apps (like pages in elm-pages do), you can pull in as much data as you want in
|
|
12
12
|
the DataSource for your ApiRoutes, and it won't effect the payload size. Instead, the size of an ApiRoute is just the content you output for that route.
|
|
13
13
|
|
|
14
|
-
In a future release, ApiRoutes may be able to run at request-time in a serverless function, allowing you to use pure Elm code to create dynamic APIs, and even pulling in data from
|
|
15
|
-
DataSources dynamically.
|
|
16
|
-
|
|
17
14
|
@docs ApiRoute, ApiRouteBuilder, Response
|
|
18
15
|
|
|
19
16
|
@docs capture, literal, slash, succeed
|
package/src/DataSource/File.elm
CHANGED
|
@@ -171,7 +171,7 @@ just the metadata.
|
|
|
171
171
|
(Decode.field "tags" (Decode.list Decode.string))
|
|
172
172
|
|
|
173
173
|
If you wanted to use this to get this metadata for all blog posts in a folder, you could use
|
|
174
|
-
the [`DataSource`](DataSource) API along with [`DataSource.Glob`](DataSource
|
|
174
|
+
the [`DataSource`](DataSource) API along with [`DataSource.Glob`](DataSource-Glob).
|
|
175
175
|
|
|
176
176
|
import DataSource exposing (DataSource)
|
|
177
177
|
import DataSource.File as File
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
module DataSource.Internal.Request exposing (request)
|
|
2
2
|
|
|
3
|
-
{-| Build a `DataSource.Http` request (analogous to [Http.request](https://package.elm-lang.org/packages/elm/http/latest/Http#request)).
|
|
4
|
-
This function takes in all the details to build a `DataSource.Http` request, but you can build your own simplified helper functions
|
|
5
|
-
with this as a low-level detail, or you can use functions like [DataSource.Http.get](#get).
|
|
6
|
-
-}
|
|
7
|
-
|
|
8
3
|
import DataSource exposing (DataSource)
|
|
9
4
|
import DataSource.Http exposing (Body, Expect)
|
|
10
5
|
|
package/src/DataSource.elm
CHANGED
|
@@ -7,7 +7,7 @@ module DataSource exposing
|
|
|
7
7
|
, map2, map3, map4, map5, map6, map7, map8, map9
|
|
8
8
|
)
|
|
9
9
|
|
|
10
|
-
{-| In an `elm-pages` app, each
|
|
10
|
+
{-| In an `elm-pages` app, each Route Module can define a value `data` which is a `DataSource` that will be resolved **before** `init` is called. That means it is also available
|
|
11
11
|
when the page's HTML is pre-rendered during the build step. You can also access the resolved data in `head` to use it for the page's SEO meta tags.
|
|
12
12
|
|
|
13
13
|
A `DataSource` lets you pull in data from:
|
|
@@ -20,9 +20,17 @@ A `DataSource` lets you pull in data from:
|
|
|
20
20
|
- Or any combination of the above, using `DataSource.map2`, `DataSource.andThen`, or other combining/continuing helpers from this module
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## DataSource's vs. Effect's/Cmd's
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
DataSource's are always resolved before the page is rendered and sent to the browser. A DataSource is never executed
|
|
26
|
+
in the Browser. Instead, the resolved data from the DataSource is passed down to the Browser - it has been resolved
|
|
27
|
+
before any client-side JavaScript ever executes. In the case of a pre-rendered route, this is during the CLI build phase,
|
|
28
|
+
and for server-rendered routes its DataSource is resolved on the server.
|
|
29
|
+
|
|
30
|
+
Effect's/Cmd's are never executed on the CLI or server, they are only executed in the Browser. The data from a Route Module's
|
|
31
|
+
`init` function is used to render the initial HTML on the server or build step, but the Effect isn't executed and `update` is never called
|
|
32
|
+
before the page is hydrated in the Browser. This gives a deterministic mental model of what the first render will look like,
|
|
33
|
+
and a nicely typed way to define the initial `Data` you have to render your initial view.
|
|
26
34
|
|
|
27
35
|
Because `elm-pages` hydrates into a full Elm single-page app, it does need the data in order to initialize the Elm app.
|
|
28
36
|
So why not just get the data the old-fashioned way, with `elm/http`, for example?
|
|
@@ -123,16 +131,16 @@ A common use for this is to map your data into your elm-pages view:
|
|
|
123
131
|
map : (a -> b) -> DataSource a -> DataSource b
|
|
124
132
|
map fn requestInfo =
|
|
125
133
|
case requestInfo of
|
|
126
|
-
|
|
127
|
-
|
|
134
|
+
ApiRoute value ->
|
|
135
|
+
ApiRoute (fn value)
|
|
128
136
|
|
|
129
137
|
Request urls lookupFn ->
|
|
130
138
|
Request
|
|
131
139
|
urls
|
|
132
140
|
(mapLookupFn fn lookupFn)
|
|
133
141
|
|
|
134
|
-
|
|
135
|
-
|
|
142
|
+
RequestError error ->
|
|
143
|
+
RequestError error
|
|
136
144
|
|
|
137
145
|
|
|
138
146
|
mapLookupFn : (a -> b) -> (d -> c -> DataSource a) -> d -> c -> DataSource b
|
|
@@ -182,8 +190,8 @@ resolve =
|
|
|
182
190
|
|
|
183
191
|
-}
|
|
184
192
|
combine : List (DataSource value) -> DataSource (List value)
|
|
185
|
-
combine =
|
|
186
|
-
List.
|
|
193
|
+
combine items =
|
|
194
|
+
List.foldl (map2 (::)) (succeed []) items |> map List.reverse
|
|
187
195
|
|
|
188
196
|
|
|
189
197
|
{-| Like map, but it takes in two `Request`s.
|
|
@@ -212,11 +220,8 @@ combine =
|
|
|
212
220
|
map2 : (a -> b -> c) -> DataSource a -> DataSource b -> DataSource c
|
|
213
221
|
map2 fn request1 request2 =
|
|
214
222
|
case ( request1, request2 ) of
|
|
215
|
-
(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
( _, RequestError error ) ->
|
|
219
|
-
RequestError error
|
|
223
|
+
( ApiRoute value1, ApiRoute value2 ) ->
|
|
224
|
+
ApiRoute (fn value1 value2)
|
|
220
225
|
|
|
221
226
|
( Request urls1 lookupFn1, Request urls2 lookupFn2 ) ->
|
|
222
227
|
Request
|
|
@@ -233,8 +238,11 @@ map2 fn request1 request2 =
|
|
|
233
238
|
urls1
|
|
234
239
|
(mapReq fn (\_ _ -> ApiRoute value2) lookupFn1)
|
|
235
240
|
|
|
236
|
-
(
|
|
237
|
-
|
|
241
|
+
( RequestError error, _ ) ->
|
|
242
|
+
RequestError error
|
|
243
|
+
|
|
244
|
+
( _, RequestError error ) ->
|
|
245
|
+
RequestError error
|
|
238
246
|
|
|
239
247
|
|
|
240
248
|
mapReq : (a -> b -> c) -> (e -> d -> DataSource a) -> (e -> d -> DataSource b) -> e -> d -> DataSource c
|
|
@@ -247,9 +255,6 @@ mapReq fn lookupFn1 lookupFn2 maybeMock rawResponses =
|
|
|
247
255
|
lookup : Maybe Pages.StaticHttpRequest.MockResolver -> DataSource value -> RequestsAndPending -> Result Pages.StaticHttpRequest.Error value
|
|
248
256
|
lookup maybeMockResolver requestInfo rawResponses =
|
|
249
257
|
case requestInfo of
|
|
250
|
-
RequestError error ->
|
|
251
|
-
Err error
|
|
252
|
-
|
|
253
258
|
Request urls lookupFn ->
|
|
254
259
|
lookup maybeMockResolver
|
|
255
260
|
(addUrls urls (lookupFn maybeMockResolver rawResponses))
|
|
@@ -258,18 +263,21 @@ lookup maybeMockResolver requestInfo rawResponses =
|
|
|
258
263
|
ApiRoute value ->
|
|
259
264
|
Ok value
|
|
260
265
|
|
|
266
|
+
RequestError error ->
|
|
267
|
+
Err error
|
|
268
|
+
|
|
261
269
|
|
|
262
270
|
addUrls : List HashRequest.Request -> DataSource value -> DataSource value
|
|
263
271
|
addUrls urlsToAdd requestInfo =
|
|
264
272
|
case requestInfo of
|
|
265
|
-
|
|
266
|
-
|
|
273
|
+
ApiRoute value ->
|
|
274
|
+
ApiRoute value
|
|
267
275
|
|
|
268
276
|
Request initialUrls function ->
|
|
269
277
|
Request (initialUrls ++ urlsToAdd) function
|
|
270
278
|
|
|
271
|
-
|
|
272
|
-
|
|
279
|
+
RequestError error ->
|
|
280
|
+
RequestError error
|
|
273
281
|
|
|
274
282
|
|
|
275
283
|
{-| The full details to perform a StaticHttp request.
|
|
@@ -286,14 +294,14 @@ type alias RequestDetails =
|
|
|
286
294
|
lookupUrls : DataSource value -> List RequestDetails
|
|
287
295
|
lookupUrls requestInfo =
|
|
288
296
|
case requestInfo of
|
|
289
|
-
|
|
290
|
-
-- TODO should this have URLs passed through?
|
|
297
|
+
ApiRoute _ ->
|
|
291
298
|
[]
|
|
292
299
|
|
|
293
300
|
Request urls _ ->
|
|
294
301
|
urls
|
|
295
302
|
|
|
296
|
-
|
|
303
|
+
RequestError _ ->
|
|
304
|
+
-- TODO should this have URLs passed through?
|
|
297
305
|
[]
|
|
298
306
|
|
|
299
307
|
|
|
@@ -324,19 +332,19 @@ andThen fn requestInfo =
|
|
|
324
332
|
rawResponses
|
|
325
333
|
|> (\result ->
|
|
326
334
|
case result of
|
|
327
|
-
Err error ->
|
|
328
|
-
RequestError error
|
|
329
|
-
|
|
330
335
|
Ok value ->
|
|
331
336
|
case fn value of
|
|
337
|
+
ApiRoute finalValue ->
|
|
338
|
+
ApiRoute finalValue
|
|
339
|
+
|
|
332
340
|
Request values function ->
|
|
333
341
|
Request values function
|
|
334
342
|
|
|
335
343
|
RequestError error ->
|
|
336
344
|
RequestError error
|
|
337
345
|
|
|
338
|
-
|
|
339
|
-
|
|
346
|
+
Err error ->
|
|
347
|
+
RequestError error
|
|
340
348
|
)
|
|
341
349
|
)
|
|
342
350
|
|
package/src/Form/Field.elm
CHANGED
package/src/Form.elm
CHANGED
|
@@ -575,7 +575,7 @@ field name (Field fieldParser kind) (Form definitions parseFn toInitialValues) =
|
|
|
575
575
|
|
|
576
576
|
{-| Declare a hidden field for the form.
|
|
577
577
|
|
|
578
|
-
Unlike [`field`](#field) declarations which are rendered using [`Form.
|
|
578
|
+
Unlike [`field`](#field) declarations which are rendered using [`Form.FieldView`](Form-FieldView)
|
|
579
579
|
functions, `hiddenField` inputs are automatically inserted into the form when you render it.
|
|
580
580
|
|
|
581
581
|
You define the field's validations the same way as for `field`, with the
|
package/src/Head/Seo.elm
CHANGED
|
@@ -49,10 +49,12 @@ with the `head` function that you pass to your Pages config (`Pages.application`
|
|
|
49
49
|
|
|
50
50
|
-}
|
|
51
51
|
|
|
52
|
+
import DateOrDateTime exposing (DateOrDateTime)
|
|
52
53
|
import Head
|
|
53
54
|
import Head.Twitter as Twitter
|
|
54
55
|
import LanguageTag.Country
|
|
55
56
|
import LanguageTag.Language
|
|
57
|
+
import MimeType exposing (MimeType)
|
|
56
58
|
import Pages.Url
|
|
57
59
|
|
|
58
60
|
|
|
@@ -235,9 +237,9 @@ website common =
|
|
|
235
237
|
article :
|
|
236
238
|
{ tags : List String
|
|
237
239
|
, section : Maybe String
|
|
238
|
-
, publishedTime : Maybe
|
|
239
|
-
, modifiedTime : Maybe
|
|
240
|
-
, expirationTime : Maybe
|
|
240
|
+
, publishedTime : Maybe DateOrDateTime
|
|
241
|
+
, modifiedTime : Maybe DateOrDateTime
|
|
242
|
+
, expirationTime : Maybe DateOrDateTime
|
|
241
243
|
}
|
|
242
244
|
-> Common
|
|
243
245
|
-> List Head.Tag
|
|
@@ -252,7 +254,7 @@ book :
|
|
|
252
254
|
->
|
|
253
255
|
{ tags : List String
|
|
254
256
|
, isbn : Maybe String
|
|
255
|
-
, releaseDate : Maybe
|
|
257
|
+
, releaseDate : Maybe DateOrDateTime
|
|
256
258
|
}
|
|
257
259
|
-> List Head.Tag
|
|
258
260
|
book common details =
|
|
@@ -354,7 +356,7 @@ tagsForAudio : Audio -> List ( String, Maybe Head.AttributeValue )
|
|
|
354
356
|
tagsForAudio audio =
|
|
355
357
|
[ ( "og:audio", audio.url |> Head.raw |> Just )
|
|
356
358
|
, ( "og:audio:secure_url", audio.url |> Head.raw |> Just )
|
|
357
|
-
, ( "og:audio:type", audio.mimeType |> Maybe.map Head.raw )
|
|
359
|
+
, ( "og:audio:type", audio.mimeType |> Maybe.map (MimeType.toString >> Head.raw) )
|
|
358
360
|
]
|
|
359
361
|
|
|
360
362
|
|
|
@@ -371,14 +373,14 @@ type ContentDetails
|
|
|
371
373
|
| Article
|
|
372
374
|
{ tags : List String
|
|
373
375
|
, section : Maybe String
|
|
374
|
-
, publishedTime : Maybe
|
|
375
|
-
, modifiedTime : Maybe
|
|
376
|
-
, expirationTime : Maybe
|
|
376
|
+
, publishedTime : Maybe DateOrDateTime
|
|
377
|
+
, modifiedTime : Maybe DateOrDateTime
|
|
378
|
+
, expirationTime : Maybe DateOrDateTime
|
|
377
379
|
}
|
|
378
380
|
| Book
|
|
379
381
|
{ tags : List String
|
|
380
382
|
, isbn : Maybe String
|
|
381
|
-
, releaseDate : Maybe
|
|
383
|
+
, releaseDate : Maybe DateOrDateTime
|
|
382
384
|
}
|
|
383
385
|
| Song
|
|
384
386
|
{-
|
|
@@ -399,20 +401,6 @@ type ContentDetails
|
|
|
399
401
|
}
|
|
400
402
|
|
|
401
403
|
|
|
402
|
-
{-| <https://en.wikipedia.org/wiki/ISO_8601>
|
|
403
|
-
-}
|
|
404
|
-
type alias Iso8601DateTime =
|
|
405
|
-
-- TODO should be more type-safe here
|
|
406
|
-
String
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
{-| <https://en.wikipedia.org/wiki/Media_type>
|
|
410
|
-
-}
|
|
411
|
-
type alias MimeType =
|
|
412
|
-
-- TODO should be more type-safe here
|
|
413
|
-
String
|
|
414
|
-
|
|
415
|
-
|
|
416
404
|
{-| See <https://ogp.me/#structured>
|
|
417
405
|
-}
|
|
418
406
|
type alias Image =
|
|
@@ -448,6 +436,7 @@ tagsForVideo video =
|
|
|
448
436
|
, ( "og:video:secure_url", video.url |> Head.raw |> Just )
|
|
449
437
|
, ( "og:video:width", video.dimensions |> Maybe.map .width |> Maybe.map String.fromInt |> Maybe.map Head.raw )
|
|
450
438
|
, ( "og:video:height", video.dimensions |> Maybe.map .height |> Maybe.map String.fromInt |> Maybe.map Head.raw )
|
|
439
|
+
, ( "og:video:type", video.mimeType |> Maybe.map (MimeType.toString >> Head.raw) )
|
|
451
440
|
]
|
|
452
441
|
|
|
453
442
|
|
|
@@ -466,9 +455,9 @@ tags (Content common details) =
|
|
|
466
455
|
-}
|
|
467
456
|
[ ( "og:type", "article" |> Head.raw |> Just )
|
|
468
457
|
, ( "article:section", articleDetails.section |> Maybe.map Head.raw )
|
|
469
|
-
, ( "article:published_time", articleDetails.publishedTime |> Maybe.map Head.raw )
|
|
470
|
-
, ( "article:modified_time", articleDetails.modifiedTime |> Maybe.map Head.raw )
|
|
471
|
-
, ( "article:expiration_time", articleDetails.expirationTime |> Maybe.map Head.raw )
|
|
458
|
+
, ( "article:published_time", articleDetails.publishedTime |> Maybe.map (DateOrDateTime.toIso8601 >> Head.raw) )
|
|
459
|
+
, ( "article:modified_time", articleDetails.modifiedTime |> Maybe.map (DateOrDateTime.toIso8601 >> Head.raw) )
|
|
460
|
+
, ( "article:expiration_time", articleDetails.expirationTime |> Maybe.map (DateOrDateTime.toIso8601 >> Head.raw) )
|
|
472
461
|
]
|
|
473
462
|
++ List.map
|
|
474
463
|
(\tag -> ( "article:tag", tag |> Head.raw |> Just ))
|
|
@@ -477,7 +466,7 @@ tags (Content common details) =
|
|
|
477
466
|
Book bookDetails ->
|
|
478
467
|
[ ( "og:type", "book" |> Head.raw |> Just )
|
|
479
468
|
, ( "og:isbn", bookDetails.isbn |> Maybe.map Head.raw )
|
|
480
|
-
, ( "og:release_date", bookDetails.releaseDate |> Maybe.map Head.raw )
|
|
469
|
+
, ( "og:release_date", bookDetails.releaseDate |> Maybe.map (DateOrDateTime.toIso8601 >> Head.raw) )
|
|
481
470
|
]
|
|
482
471
|
++ List.map
|
|
483
472
|
(\tag -> ( "book:tag", tag |> Head.raw |> Just ))
|
package/src/Head.elm
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module Head exposing
|
|
2
2
|
( Tag, metaName, metaProperty, metaRedirect
|
|
3
3
|
, rssLink, sitemapLink, rootLanguage, manifestLink
|
|
4
|
+
, nonLoadingNode
|
|
4
5
|
, structuredData
|
|
5
6
|
, AttributeValue
|
|
6
7
|
, currentPageFullUrl, urlAttribute, raw
|
|
@@ -20,6 +21,8 @@ writing a plugin package to extend `elm-pages`.
|
|
|
20
21
|
@docs Tag, metaName, metaProperty, metaRedirect
|
|
21
22
|
@docs rssLink, sitemapLink, rootLanguage, manifestLink
|
|
22
23
|
|
|
24
|
+
@docs nonLoadingNode
|
|
25
|
+
|
|
23
26
|
|
|
24
27
|
## Structured Data
|
|
25
28
|
|
|
@@ -45,9 +48,11 @@ writing a plugin package to extend `elm-pages`.
|
|
|
45
48
|
|
|
46
49
|
import Json.Encode
|
|
47
50
|
import LanguageTag exposing (LanguageTag)
|
|
51
|
+
import List.Extra
|
|
48
52
|
import MimeType
|
|
49
53
|
import Pages.Internal.String as String
|
|
50
54
|
import Pages.Url
|
|
55
|
+
import Regex
|
|
51
56
|
|
|
52
57
|
|
|
53
58
|
{-| Values that can be passed to the generated `Pages.application` config
|
|
@@ -57,6 +62,7 @@ type Tag
|
|
|
57
62
|
= Tag Details
|
|
58
63
|
| StructuredData Json.Encode.Value
|
|
59
64
|
| RootModifier String String
|
|
65
|
+
| Stripped String
|
|
60
66
|
|
|
61
67
|
|
|
62
68
|
type alias Details =
|
|
@@ -211,6 +217,120 @@ canonicalLink maybePath =
|
|
|
211
217
|
]
|
|
212
218
|
|
|
213
219
|
|
|
220
|
+
{-| Escape hatch for any head tags that don't have high-level helpers. This lets you build arbitrary head nodes as long as they
|
|
221
|
+
are not loading or preloading directives.
|
|
222
|
+
|
|
223
|
+
Tags that do loading/pre-loading will not work from this function. `elm-pages` uses ViteJS for loading assets like
|
|
224
|
+
script tags, stylesheets, fonts, etc., and allows you to customize which assets to preload and how through the elm-pages.config.mjs file.
|
|
225
|
+
See the full discussion of the design in [#339](https://github.com/dillonkearns/elm-pages/discussions/339).
|
|
226
|
+
|
|
227
|
+
So for example the following tags would _not_ load if defined through `nonLoadingNode`, and would instead need to be registered through Vite:
|
|
228
|
+
|
|
229
|
+
- `<script src="...">`
|
|
230
|
+
- `<link href="/style.css">`
|
|
231
|
+
- `<link rel="preload">`
|
|
232
|
+
|
|
233
|
+
The following tag would successfully render as it is a non-loading tag:
|
|
234
|
+
|
|
235
|
+
Head.nonLoadingNode "link"
|
|
236
|
+
[ ( "rel", Head.raw "alternate" )
|
|
237
|
+
, ( "type", Head.raw "application/atom+xml" )
|
|
238
|
+
, ( "title", Head.raw "News" )
|
|
239
|
+
, ( "href", Head.raw "/news/atom" )
|
|
240
|
+
]
|
|
241
|
+
|
|
242
|
+
Renders the head tag:
|
|
243
|
+
|
|
244
|
+
`<link rel="alternate" type="application/atom+xml" title="News" href="/news/atom">`
|
|
245
|
+
|
|
246
|
+
-}
|
|
247
|
+
nonLoadingNode : String -> List ( String, AttributeValue ) -> Tag
|
|
248
|
+
nonLoadingNode nodeName attributes =
|
|
249
|
+
let
|
|
250
|
+
relTag : Maybe AttributeValue
|
|
251
|
+
relTag =
|
|
252
|
+
attributes
|
|
253
|
+
|> List.Extra.find (\( key, _ ) -> key == "rel")
|
|
254
|
+
|> Maybe.map Tuple.second
|
|
255
|
+
|
|
256
|
+
isPreloadDirective : Bool
|
|
257
|
+
isPreloadDirective =
|
|
258
|
+
case relTag of
|
|
259
|
+
Just (Raw rel) ->
|
|
260
|
+
let
|
|
261
|
+
isLinkTag : Bool
|
|
262
|
+
isLinkTag =
|
|
263
|
+
nodeName |> matchesKeyword "link"
|
|
264
|
+
in
|
|
265
|
+
isLinkTag
|
|
266
|
+
&& (rel
|
|
267
|
+
|> matchesOneKeyword
|
|
268
|
+
[ "preload"
|
|
269
|
+
, "modulepreload"
|
|
270
|
+
, "preconnect"
|
|
271
|
+
, "dns-prefetch"
|
|
272
|
+
, "stylesheet"
|
|
273
|
+
]
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
_ ->
|
|
277
|
+
False
|
|
278
|
+
in
|
|
279
|
+
if
|
|
280
|
+
(nodeName |> matchesKeyword "script")
|
|
281
|
+
|| isPreloadDirective
|
|
282
|
+
then
|
|
283
|
+
Stripped
|
|
284
|
+
("<"
|
|
285
|
+
++ nodeName
|
|
286
|
+
++ " "
|
|
287
|
+
++ (attributes
|
|
288
|
+
|> List.map
|
|
289
|
+
(\( name, value ) ->
|
|
290
|
+
name
|
|
291
|
+
++ "=\""
|
|
292
|
+
++ ((case value of
|
|
293
|
+
Raw rawValue ->
|
|
294
|
+
rawValue
|
|
295
|
+
|
|
296
|
+
_ ->
|
|
297
|
+
"<internals>"
|
|
298
|
+
)
|
|
299
|
+
++ "\""
|
|
300
|
+
)
|
|
301
|
+
)
|
|
302
|
+
|> String.join " "
|
|
303
|
+
)
|
|
304
|
+
++ " />"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
else
|
|
308
|
+
node nodeName attributes
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
matchesOneKeyword : List String -> String -> Bool
|
|
312
|
+
matchesOneKeyword keywords string =
|
|
313
|
+
List.any
|
|
314
|
+
(\keyword -> string |> matchesKeyword keyword)
|
|
315
|
+
keywords
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
matchesKeyword : String -> String -> Bool
|
|
319
|
+
matchesKeyword regex string =
|
|
320
|
+
string |> matchesRegex ("^\\s*" ++ regex ++ "\\s*$")
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
matchesRegex : String -> String -> Bool
|
|
324
|
+
matchesRegex regex string =
|
|
325
|
+
string
|
|
326
|
+
|> Regex.contains
|
|
327
|
+
(Regex.fromStringWith
|
|
328
|
+
{ caseInsensitive = True, multiline = True }
|
|
329
|
+
regex
|
|
330
|
+
|> Maybe.withDefault Regex.never
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
214
334
|
{-| Let's you link to your manifest.json file, see <https://developer.mozilla.org/en-US/docs/Web/Manifest#deploying_a_manifest>.
|
|
215
335
|
-}
|
|
216
336
|
manifestLink : String -> Tag
|
|
@@ -443,6 +563,12 @@ toJson canonicalSiteUrl currentPagePath tag =
|
|
|
443
563
|
, ( "keyValuePair", Json.Encode.list Json.Encode.string [ key, value ] )
|
|
444
564
|
]
|
|
445
565
|
|
|
566
|
+
Stripped message ->
|
|
567
|
+
Json.Encode.object
|
|
568
|
+
[ ( "type", Json.Encode.string "stripped" )
|
|
569
|
+
, ( "message", Json.Encode.string message )
|
|
570
|
+
]
|
|
571
|
+
|
|
446
572
|
|
|
447
573
|
encodeProperty : String -> String -> ( String, AttributeValue ) -> Json.Encode.Value
|
|
448
574
|
encodeProperty canonicalSiteUrl currentPagePath ( name, value ) =
|
package/src/HtmlPrinter.elm
CHANGED
|
@@ -8,15 +8,19 @@ import Test.Html.Internal.ElmHtml.ToString exposing (defaultFormatOptions, nodeT
|
|
|
8
8
|
import VirtualDom
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
htmlToString : Html msg -> String
|
|
12
|
-
htmlToString viewHtml =
|
|
11
|
+
htmlToString : Maybe { indent : Int, newLines : Bool } -> Html msg -> String
|
|
12
|
+
htmlToString formatOptions viewHtml =
|
|
13
13
|
case
|
|
14
14
|
Decode.decodeValue
|
|
15
15
|
(decodeElmHtml (\_ _ -> VirtualDom.Normal (Decode.succeed ())))
|
|
16
16
|
(asJsonView viewHtml)
|
|
17
17
|
of
|
|
18
18
|
Ok str ->
|
|
19
|
-
nodeToStringWithOptions
|
|
19
|
+
nodeToStringWithOptions
|
|
20
|
+
(formatOptions
|
|
21
|
+
|> Maybe.withDefault defaultFormatOptions
|
|
22
|
+
)
|
|
23
|
+
str
|
|
20
24
|
|
|
21
25
|
Err err ->
|
|
22
26
|
"Error pre-rendering HTML in HtmlPrinter.elm: " ++ Decode.errorToString err
|