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.
Files changed (75) hide show
  1. package/generator/review/elm.json +34 -0
  2. package/generator/review/src/ReviewConfig.elm +10 -0
  3. package/generator/src/basepath-middleware.js +15 -9
  4. package/generator/src/build.js +77 -4
  5. package/generator/src/cli.js +13 -9
  6. package/generator/src/compile-elm.js +43 -0
  7. package/generator/src/dev-server.js +63 -11
  8. package/generator/src/error-formatter.js +62 -9
  9. package/generator/src/generate-template-module-connector.js +17 -4
  10. package/generator/src/init.js +4 -0
  11. package/generator/src/pre-render-html.js +19 -12
  12. package/generator/src/render-worker.js +0 -1
  13. package/generator/src/render.js +1 -2
  14. package/generator/src/seo-renderer.js +21 -2
  15. package/generator/static-code/hmr.js +43 -6
  16. package/generator/template/elm.json +13 -5
  17. package/generator/template/package.json +3 -2
  18. package/package.json +14 -8
  19. package/src/ApiRoute.elm +178 -0
  20. package/src/AriaLiveAnnouncer.elm +36 -0
  21. package/src/BuildError.elm +60 -0
  22. package/src/DataSource/File.elm +288 -0
  23. package/src/DataSource/Glob.elm +1050 -0
  24. package/src/DataSource/Http.elm +467 -0
  25. package/src/DataSource/Internal/Glob.elm +74 -0
  26. package/src/DataSource/Port.elm +87 -0
  27. package/src/DataSource/ServerRequest.elm +60 -0
  28. package/src/DataSource.elm +801 -0
  29. package/src/Head/Seo.elm +516 -0
  30. package/src/Head/Twitter.elm +109 -0
  31. package/src/Head.elm +452 -0
  32. package/src/HtmlPrinter.elm +27 -0
  33. package/src/Internal/ApiRoute.elm +89 -0
  34. package/src/Internal/OptimizedDecoder.elm +18 -0
  35. package/src/KeepOrDiscard.elm +6 -0
  36. package/src/OptimizedDecoder/Pipeline.elm +335 -0
  37. package/src/OptimizedDecoder.elm +818 -0
  38. package/src/Pages/ContentCache.elm +248 -0
  39. package/src/Pages/Flags.elm +26 -0
  40. package/src/Pages/Http.elm +10 -0
  41. package/src/Pages/Internal/ApplicationType.elm +6 -0
  42. package/src/Pages/Internal/NotFoundReason.elm +256 -0
  43. package/src/Pages/Internal/Platform/Cli.elm +1015 -0
  44. package/src/Pages/Internal/Platform/Effect.elm +14 -0
  45. package/src/Pages/Internal/Platform/StaticResponses.elm +540 -0
  46. package/src/Pages/Internal/Platform/ToJsPayload.elm +138 -0
  47. package/src/Pages/Internal/Platform.elm +745 -0
  48. package/src/Pages/Internal/RoutePattern.elm +122 -0
  49. package/src/Pages/Internal/Router.elm +116 -0
  50. package/src/Pages/Internal/StaticHttpBody.elm +54 -0
  51. package/src/Pages/Internal/String.elm +39 -0
  52. package/src/Pages/Manifest/Category.elm +240 -0
  53. package/src/Pages/Manifest.elm +412 -0
  54. package/src/Pages/PageUrl.elm +38 -0
  55. package/src/Pages/ProgramConfig.elm +73 -0
  56. package/src/Pages/Review/NoContractViolations.elm +397 -0
  57. package/src/Pages/Secrets.elm +83 -0
  58. package/src/Pages/SiteConfig.elm +13 -0
  59. package/src/Pages/StaticHttp/Request.elm +42 -0
  60. package/src/Pages/StaticHttpRequest.elm +320 -0
  61. package/src/Pages/Url.elm +60 -0
  62. package/src/Path.elm +96 -0
  63. package/src/QueryParams.elm +216 -0
  64. package/src/RenderRequest.elm +163 -0
  65. package/src/RequestsAndPending.elm +20 -0
  66. package/src/Secrets.elm +111 -0
  67. package/src/SecretsDict.elm +45 -0
  68. package/src/StructuredData.elm +236 -0
  69. package/src/TerminalText.elm +242 -0
  70. package/src/Test/Html/Internal/ElmHtml/Constants.elm +53 -0
  71. package/src/Test/Html/Internal/ElmHtml/Helpers.elm +17 -0
  72. package/src/Test/Html/Internal/ElmHtml/InternalTypes.elm +529 -0
  73. package/src/Test/Html/Internal/ElmHtml/Markdown.elm +56 -0
  74. package/src/Test/Html/Internal/ElmHtml/ToString.elm +197 -0
  75. package/src/Test/Internal/KernelConstants.elm +34 -0
@@ -0,0 +1,467 @@
1
+ module DataSource.Http exposing
2
+ ( RequestDetails
3
+ , get, request
4
+ , Body, emptyBody, stringBody, jsonBody
5
+ , unoptimizedRequest
6
+ , Expect, expectString, expectUnoptimizedJson
7
+ )
8
+
9
+ {-| `DataSource.Http` requests are an alternative to doing Elm HTTP requests the traditional way using the `elm/http` package.
10
+
11
+ The key differences are:
12
+
13
+ - `DataSource.Http.Request`s are performed once at build time (`Http.Request`s are performed at runtime, at whenever point you perform them)
14
+ - `DataSource.Http.Request`s strip out unused JSON data from the data your decoder doesn't touch to minimize the JSON payload
15
+ - `DataSource.Http.Request`s can use [`Pages.Secrets`](Pages.Secrets) to securely use credentials from your environment variables which are completely masked in the production assets.
16
+ - `DataSource.Http.Request`s have a built-in `DataSource.andThen` that allows you to perform follow-up requests without using tasks
17
+
18
+
19
+ ## Scenarios where DataSource.Http is a good fit
20
+
21
+ If you need data that is refreshed often you may want to do a traditional HTTP request with the `elm/http` package.
22
+ The kinds of situations that are served well by static HTTP are with data that updates moderately frequently or infrequently (or never).
23
+ A common pattern is to trigger a new build when data changes. Many JAMstack services
24
+ allow you to send a WebHook to your host (for example, Netlify is a good static file host that supports triggering builds with webhooks). So
25
+ you may want to have your site rebuild everytime your calendar feed has an event added, or whenever a page or article is added
26
+ or updated on a CMS service like Contentful.
27
+
28
+ In scenarios like this, you can serve data that is just as up-to-date as it would be using `elm/http`, but you get the performance
29
+ gains of using `DataSource.Http.Request`s as well as the simplicity and robustness that comes with it. Read more about these benefits
30
+ in [this article introducing DataSource.Http requests and some concepts around it](https://elm-pages.com/blog/static-http).
31
+
32
+
33
+ ## Scenarios where DataSource.Http is not a good fit
34
+
35
+ - Data that is specific to the logged-in user
36
+ - Data that needs to be the very latest and changes often (for example, sports scores)
37
+
38
+ @docs RequestDetails
39
+ @docs get, request
40
+
41
+
42
+ ## Building a DataSource.Http Request Body
43
+
44
+ The way you build a body is analogous to the `elm/http` package. Currently, only `emptyBody` and
45
+ `stringBody` are supported. If you have a use case that calls for a different body type, please open a Github issue
46
+ and describe your use case!
47
+
48
+ @docs Body, emptyBody, stringBody, jsonBody
49
+
50
+
51
+ ## Unoptimized Requests
52
+
53
+ Warning - use these at your own risk! It's highly recommended that you use the other request functions that make use of
54
+ `zwilias/json-decode-exploration` in order to allow you to reduce down your JSON to only the values that are used by
55
+ your decoders. This can significantly reduce download sizes for your DataSource.Http requests.
56
+
57
+ @docs unoptimizedRequest
58
+
59
+
60
+ ### Expect for unoptimized requests
61
+
62
+ @docs Expect, expectString, expectUnoptimizedJson
63
+
64
+ -}
65
+
66
+ import DataSource exposing (DataSource)
67
+ import Dict
68
+ import Internal.OptimizedDecoder
69
+ import Json.Decode
70
+ import Json.Decode.Exploration
71
+ import Json.Encode as Encode
72
+ import KeepOrDiscard
73
+ import OptimizedDecoder as Decode exposing (Decoder)
74
+ import Pages.Internal.ApplicationType as ApplicationType
75
+ import Pages.Internal.StaticHttpBody as Body
76
+ import Pages.Secrets
77
+ import Pages.StaticHttp.Request as HashRequest
78
+ import Pages.StaticHttpRequest exposing (RawRequest(..))
79
+ import RequestsAndPending
80
+ import Secrets
81
+
82
+
83
+ {-| Build an empty body for a DataSource.Http request. See [elm/http's `Http.emptyBody`](https://package.elm-lang.org/packages/elm/http/latest/Http#emptyBody).
84
+ -}
85
+ emptyBody : Body
86
+ emptyBody =
87
+ Body.EmptyBody
88
+
89
+
90
+ {-| Builds a string body for a DataSource.Http request. See [elm/http's `Http.stringBody`](https://package.elm-lang.org/packages/elm/http/latest/Http#stringBody).
91
+
92
+ Note from the `elm/http` docs:
93
+
94
+ > The first argument is a [MIME type](https://en.wikipedia.org/wiki/Media_type) of the body. Some servers are strict about this!
95
+
96
+ -}
97
+ stringBody : String -> String -> Body
98
+ stringBody contentType content =
99
+ Body.StringBody contentType content
100
+
101
+
102
+ {-| Builds a JSON body for a DataSource.Http request. See [elm/http's `Http.jsonBody`](https://package.elm-lang.org/packages/elm/http/latest/Http#jsonBody).
103
+ -}
104
+ jsonBody : Encode.Value -> Body
105
+ jsonBody content =
106
+ Body.JsonBody content
107
+
108
+
109
+ {-| A body for a DataSource.Http request.
110
+ -}
111
+ type alias Body =
112
+ Body.Body
113
+
114
+
115
+ {-| A simplified helper around [`DataSource.Http.request`](#request), which builds up a DataSource.Http GET request.
116
+
117
+ import DataSource
118
+ import DataSource.Http
119
+ import Json.Decode as Decode exposing (Decoder)
120
+
121
+ getRequest : DataSource Int
122
+ getRequest =
123
+ DataSource.Http.get
124
+ (Secrets.succeed "https://api.github.com/repos/dillonkearns/elm-pages")
125
+ (Decode.field "stargazers_count" Decode.int)
126
+
127
+ -}
128
+ get :
129
+ Pages.Secrets.Value String
130
+ -> Decoder a
131
+ -> DataSource a
132
+ get url decoder =
133
+ request
134
+ (Secrets.map
135
+ (\okUrl ->
136
+ -- wrap in new variant
137
+ { url = okUrl
138
+ , method = "GET"
139
+ , headers = []
140
+ , body = emptyBody
141
+ }
142
+ )
143
+ url
144
+ )
145
+ decoder
146
+
147
+
148
+ {-| The full details to perform a DataSource.Http request.
149
+ -}
150
+ type alias RequestDetails =
151
+ { url : String
152
+ , method : String
153
+ , headers : List ( String, String )
154
+ , body : Body
155
+ }
156
+
157
+
158
+ requestToString : RequestDetails -> String
159
+ requestToString requestDetails =
160
+ requestDetails.url
161
+
162
+
163
+ {-| Build a `DataSource.Http` request (analagous to [Http.request](https://package.elm-lang.org/packages/elm/http/latest/Http#request)).
164
+ This function takes in all the details to build a `DataSource.Http` request, but you can build your own simplified helper functions
165
+ with this as a low-level detail, or you can use functions like [DataSource.Http.get](#get).
166
+ -}
167
+ request :
168
+ Pages.Secrets.Value RequestDetails
169
+ -> Decoder a
170
+ -> DataSource a
171
+ request urlWithSecrets decoder =
172
+ unoptimizedRequest urlWithSecrets (ExpectJson decoder)
173
+
174
+
175
+ {-| Analgous to the `Expect` type in the `elm/http` package. This represents how you will process the data that comes
176
+ back in your DataSource.Http request.
177
+
178
+ You can derive `ExpectUnoptimizedJson` from `ExpectString`. Or you could build your own helper to process the String
179
+ as XML, for example, or give an `elm-pages` build error if the response can't be parsed as XML.
180
+
181
+ -}
182
+ type Expect value
183
+ = ExpectUnoptimizedJson (Json.Decode.Decoder value)
184
+ | ExpectJson (Decoder value)
185
+ | ExpectString (String -> Result String value)
186
+
187
+
188
+ {-| Request a raw String. You can validate the String if you need to check the formatting, or try to parse it
189
+ in something besides JSON. Be sure to use the `DataSource.Http.request` function if you want an optimized request that
190
+ strips out unused JSON to optimize your asset size.
191
+
192
+ If the function you pass to `expectString` yields an `Err`, then you will get a build error that will
193
+ fail your `elm-pages` build and print out the String from the `Err`.
194
+
195
+ request =
196
+ DataSource.Http.unoptimizedRequest
197
+ (Secrets.succeed
198
+ { url = "https://example.com/file.txt"
199
+ , method = "GET"
200
+ , headers = []
201
+ , body = DataSource.Http.emptyBody
202
+ }
203
+ )
204
+ (DataSource.Http.expectString
205
+ (\string ->
206
+ if String.toUpper string == string then
207
+ Ok string
208
+
209
+ else
210
+ Err "String was not uppercased"
211
+ )
212
+ )
213
+
214
+ -}
215
+ expectString : (String -> Result String value) -> Expect value
216
+ expectString =
217
+ ExpectString
218
+
219
+
220
+ {-| Handle the incoming response as JSON and don't optimize the asset and strip out unused values.
221
+ Be sure to use the `DataSource.Http.request` function if you want an optimized request that
222
+ strips out unused JSON to optimize your asset size. This function makes sense to use for things like a GraphQL request
223
+ where the JSON payload is already trimmed down to the data you explicitly requested.
224
+
225
+ If the function you pass to `expectString` yields an `Err`, then you will get a build error that will
226
+ fail your `elm-pages` build and print out the String from the `Err`.
227
+
228
+ -}
229
+ expectUnoptimizedJson : Json.Decode.Decoder value -> Expect value
230
+ expectUnoptimizedJson =
231
+ ExpectUnoptimizedJson
232
+
233
+
234
+ {-| This is an alternative to the other request functions in this module that doesn't perform any optimizations on the
235
+ asset. Be sure to use the optimized versions, like `DataSource.Http.request`, if you can. Using those can significantly reduce
236
+ your asset sizes by removing all unused fields from your JSON.
237
+
238
+ You may want to use this function instead if you need XML data or plaintext. Or maybe you're hitting a GraphQL API,
239
+ so you don't need any additional optimization as the payload is already reduced down to exactly what you requested.
240
+
241
+ -}
242
+ unoptimizedRequest :
243
+ Pages.Secrets.Value RequestDetails
244
+ -> Expect a
245
+ -> DataSource a
246
+ unoptimizedRequest requestWithSecrets expect =
247
+ case expect of
248
+ ExpectJson decoder ->
249
+ Request Dict.empty
250
+ ( [ requestWithSecrets ]
251
+ , \keepOrDiscard appType rawResponseDict ->
252
+ case appType of
253
+ ApplicationType.Cli ->
254
+ rawResponseDict
255
+ |> RequestsAndPending.get (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash)
256
+ |> (\maybeResponse ->
257
+ case maybeResponse of
258
+ Just rawResponse ->
259
+ Ok
260
+ ( Dict.singleton (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash)
261
+ (case keepOrDiscard of
262
+ KeepOrDiscard.Keep ->
263
+ Pages.StaticHttpRequest.StripResponse
264
+ (Decode.map (\_ -> ()) decoder)
265
+
266
+ KeepOrDiscard.Discard ->
267
+ Pages.StaticHttpRequest.CliOnly
268
+ )
269
+ , rawResponse
270
+ )
271
+
272
+ Nothing ->
273
+ Err
274
+ (Pages.StaticHttpRequest.MissingHttpResponse
275
+ (requestToString (Secrets.maskedLookup requestWithSecrets))
276
+ [ requestWithSecrets ]
277
+ )
278
+ )
279
+ |> Result.andThen
280
+ (\( strippedResponses, rawResponse ) ->
281
+ rawResponse
282
+ |> Json.Decode.Exploration.decodeString (decoder |> Internal.OptimizedDecoder.jde)
283
+ |> (\decodeResult ->
284
+ case decodeResult of
285
+ Json.Decode.Exploration.BadJson ->
286
+ Pages.StaticHttpRequest.DecoderError ("Payload sent back invalid JSON\n" ++ rawResponse) |> Err
287
+
288
+ Json.Decode.Exploration.Errors errors ->
289
+ errors
290
+ |> Json.Decode.Exploration.errorsToString
291
+ |> Pages.StaticHttpRequest.DecoderError
292
+ |> Err
293
+
294
+ Json.Decode.Exploration.WithWarnings _ a ->
295
+ Ok a
296
+
297
+ Json.Decode.Exploration.Success a ->
298
+ Ok a
299
+ )
300
+ |> Result.map
301
+ (\finalRequest ->
302
+ ( case keepOrDiscard of
303
+ KeepOrDiscard.Keep ->
304
+ strippedResponses
305
+ |> Dict.insert
306
+ (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash)
307
+ (Pages.StaticHttpRequest.StripResponse
308
+ (Decode.map (\_ -> ()) decoder)
309
+ )
310
+
311
+ KeepOrDiscard.Discard ->
312
+ strippedResponses
313
+ |> Dict.insert
314
+ (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash)
315
+ Pages.StaticHttpRequest.CliOnly
316
+ , finalRequest
317
+ )
318
+ )
319
+ )
320
+ |> toResult
321
+
322
+ ApplicationType.Browser ->
323
+ rawResponseDict
324
+ |> RequestsAndPending.get (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash)
325
+ |> (\maybeResponse ->
326
+ case maybeResponse of
327
+ Just rawResponse ->
328
+ Ok
329
+ ( -- TODO should this be an empty Dict? Shouldn't matter in the browser.
330
+ Dict.singleton (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash)
331
+ Pages.StaticHttpRequest.UseRawResponse
332
+ , rawResponse
333
+ )
334
+
335
+ Nothing ->
336
+ Err
337
+ (Pages.StaticHttpRequest.MissingHttpResponse (requestToString (Secrets.maskedLookup requestWithSecrets))
338
+ [ requestWithSecrets ]
339
+ )
340
+ )
341
+ |> Result.andThen
342
+ (\( strippedResponses, rawResponse ) ->
343
+ rawResponse
344
+ |> Json.Decode.decodeString (decoder |> Internal.OptimizedDecoder.jd)
345
+ |> (\decodeResult ->
346
+ case decodeResult of
347
+ Err error ->
348
+ Pages.StaticHttpRequest.DecoderError
349
+ ("Payload sent back invalid JSON\n"
350
+ ++ rawResponse
351
+ ++ "\n KEYS"
352
+ ++ (Dict.keys strippedResponses |> String.join " - ")
353
+ ++ Json.Decode.errorToString error
354
+ )
355
+ |> Err
356
+
357
+ Ok a ->
358
+ Ok a
359
+ )
360
+ |> Result.map
361
+ (\finalRequest ->
362
+ ( strippedResponses
363
+ , finalRequest
364
+ )
365
+ )
366
+ )
367
+ |> toResult
368
+ )
369
+
370
+ ExpectUnoptimizedJson decoder ->
371
+ Request Dict.empty
372
+ ( [ requestWithSecrets ]
373
+ , \_ _ rawResponseDict ->
374
+ rawResponseDict
375
+ |> RequestsAndPending.get (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash)
376
+ |> (\maybeResponse ->
377
+ case maybeResponse of
378
+ Just rawResponse ->
379
+ Ok
380
+ ( -- TODO check keepOrDiscard
381
+ Dict.singleton (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash)
382
+ Pages.StaticHttpRequest.UseRawResponse
383
+ , rawResponse
384
+ )
385
+
386
+ Nothing ->
387
+ Err
388
+ (Pages.StaticHttpRequest.MissingHttpResponse (requestToString (Secrets.maskedLookup requestWithSecrets))
389
+ [ requestWithSecrets ]
390
+ )
391
+ )
392
+ |> Result.andThen
393
+ (\( strippedResponses, rawResponse ) ->
394
+ rawResponse
395
+ |> Json.Decode.decodeString decoder
396
+ |> (\decodeResult ->
397
+ case decodeResult of
398
+ Err error ->
399
+ error
400
+ |> Decode.errorToString
401
+ |> Pages.StaticHttpRequest.DecoderError
402
+ |> Err
403
+
404
+ Ok a ->
405
+ Ok a
406
+ )
407
+ |> Result.map
408
+ (\finalRequest ->
409
+ ( -- TODO check keepOrDiscard
410
+ strippedResponses
411
+ |> Dict.insert
412
+ (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash)
413
+ Pages.StaticHttpRequest.UseRawResponse
414
+ , finalRequest
415
+ )
416
+ )
417
+ )
418
+ |> toResult
419
+ )
420
+
421
+ ExpectString mapStringFn ->
422
+ Request Dict.empty
423
+ ( [ requestWithSecrets ]
424
+ , \_ _ rawResponseDict ->
425
+ rawResponseDict
426
+ |> RequestsAndPending.get (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash)
427
+ |> (\maybeResponse ->
428
+ case maybeResponse of
429
+ Just rawResponse ->
430
+ Ok
431
+ ( -- TODO check keepOrDiscard
432
+ Dict.singleton (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash) Pages.StaticHttpRequest.UseRawResponse
433
+ , rawResponse
434
+ )
435
+
436
+ Nothing ->
437
+ Err
438
+ (Pages.StaticHttpRequest.MissingHttpResponse (requestToString (Secrets.maskedLookup requestWithSecrets))
439
+ [ requestWithSecrets ]
440
+ )
441
+ )
442
+ |> Result.andThen
443
+ (\( strippedResponses, rawResponse ) ->
444
+ rawResponse
445
+ |> mapStringFn
446
+ |> Result.mapError Pages.StaticHttpRequest.DecoderError
447
+ |> Result.map
448
+ (\finalRequest ->
449
+ ( -- TODO check keepOrDiscard
450
+ strippedResponses
451
+ |> Dict.insert (Secrets.maskedLookup requestWithSecrets |> HashRequest.hash) Pages.StaticHttpRequest.UseRawResponse
452
+ , finalRequest
453
+ )
454
+ )
455
+ )
456
+ |> toResult
457
+ )
458
+
459
+
460
+ toResult : Result Pages.StaticHttpRequest.Error ( Dict.Dict String Pages.StaticHttpRequest.WhatToDo, b ) -> RawRequest b
461
+ toResult result =
462
+ case result of
463
+ Err error ->
464
+ RequestError error
465
+
466
+ Ok ( stripped, okValue ) ->
467
+ ApiRoute stripped okValue
@@ -0,0 +1,74 @@
1
+ module DataSource.Internal.Glob exposing
2
+ ( Glob(..)
3
+ , extractMatches
4
+ , run
5
+ , toPattern
6
+ )
7
+
8
+ import List.Extra
9
+ import Regex exposing (Regex)
10
+
11
+
12
+ {-| -}
13
+ type Glob a
14
+ = Glob String String (String -> List String -> ( a, List String ))
15
+
16
+
17
+ run : String -> Glob a -> { match : a, pattern : String }
18
+ run rawInput (Glob pattern regex applyCapture) =
19
+ let
20
+ fullRegex : String
21
+ fullRegex =
22
+ "^" ++ regex ++ "$"
23
+
24
+ regexCaptures : List String
25
+ regexCaptures =
26
+ Regex.find parsedRegex rawInput
27
+ |> List.concatMap .submatches
28
+ |> List.map (Maybe.withDefault "")
29
+
30
+ parsedRegex : Regex
31
+ parsedRegex =
32
+ Regex.fromString fullRegex |> Maybe.withDefault Regex.never
33
+ in
34
+ { match =
35
+ regexCaptures
36
+ |> List.reverse
37
+ |> applyCapture rawInput
38
+ |> Tuple.first
39
+ , pattern = pattern
40
+ }
41
+
42
+
43
+ {-| -}
44
+ toPattern : Glob a -> String
45
+ toPattern (Glob pattern _ _) =
46
+ pattern
47
+
48
+
49
+ {-| -}
50
+ extractMatches : a -> List ( String, a ) -> String -> List a
51
+ extractMatches defaultValue list string =
52
+ extractMatchesHelp defaultValue list string []
53
+
54
+
55
+ extractMatchesHelp : a -> List ( String, a ) -> String -> List a -> List a
56
+ extractMatchesHelp defaultValue list string soFar =
57
+ if string == "" then
58
+ List.reverse soFar
59
+
60
+ else
61
+ let
62
+ ( matchedValue, updatedString ) =
63
+ List.Extra.findMap
64
+ (\( literalString, value ) ->
65
+ if string |> String.startsWith literalString then
66
+ Just ( value, string |> String.dropLeft (String.length literalString) )
67
+
68
+ else
69
+ Nothing
70
+ )
71
+ list
72
+ |> Maybe.withDefault ( defaultValue, "" )
73
+ in
74
+ extractMatchesHelp defaultValue list updatedString (matchedValue :: soFar)
@@ -0,0 +1,87 @@
1
+ module DataSource.Port exposing (get)
2
+
3
+ {-|
4
+
5
+ @docs get
6
+
7
+ -}
8
+
9
+ import DataSource
10
+ import DataSource.Http
11
+ import Json.Encode
12
+ import OptimizedDecoder exposing (Decoder)
13
+ import Secrets
14
+
15
+
16
+ {-| In a vanilla Elm application, ports let you either send or receive JSON data between your Elm application and the JavaScript context in the user's browser at runtime.
17
+
18
+ With `DataSource.Port`, you send and receive JSON to JavaScript running in NodeJS during build-time. This means that you can call shell scripts, or run NPM packages that are installed, or anything else you could do with NodeJS.
19
+
20
+ A `DataSource.Port` will call an async JavaScript function with the given name. The function receives the input JSON value, and the Decoder is used to decode the return value of the async function.
21
+
22
+ Here is the Elm code and corresponding JavaScript definition for getting an environment variable (or a build error if it isn't found).
23
+
24
+ import DataSource exposing (DataSource)
25
+ import DataSource.Port
26
+ import Json.Encode
27
+ import OptimizedDecoder as Decode
28
+
29
+ data : DataSource String
30
+ data =
31
+ DataSource.Port.get "environmentVariable"
32
+ (Json.Encode.string "EDITOR")
33
+ Decode.string
34
+
35
+ -- will resolve to "VIM" if you run `EDITOR=vim elm-pages dev`
36
+
37
+ ```javascript
38
+ const kleur = require("kleur");
39
+
40
+
41
+ module.exports =
42
+ /**
43
+ * @param { unknown } fromElm
44
+ * @returns { Promise<unknown> }
45
+ */
46
+ {
47
+ environmentVariable: async function (name) {
48
+ const result = process.env[name];
49
+ if (result) {
50
+ return result;
51
+ } else {
52
+ throw `No environment variable called ${kleur
53
+ .yellow()
54
+ .underline(name)}\n\nAvailable:\n\n${Object.keys(process.env).join(
55
+ "\n"
56
+ )}`;
57
+ }
58
+ },
59
+ }
60
+ ```
61
+
62
+
63
+ ## Error Handling
64
+
65
+ `port-data-source.js`
66
+
67
+ Any time you throw an exception from a DataaSource.Port definition, it will result in a build error in your `elm-pages build` or dev server. In the example above, if the environment variable
68
+ is not found it will result in a build failure. Notice that the NPM package `kleur` is being used in this example to add color to the output for that build error. You can use any tool you
69
+ prefer to add ANSI color codes within the error string in an exception and it will show up with color output in the build output and dev server.
70
+
71
+
72
+ ## Performance
73
+
74
+ As with any JavaScript or NodeJS code, avoid doing blocking IO operations. For example, avoid using `fs.readFileSync`, because blocking IO can slow down your elm-pages builds and dev server.
75
+
76
+ -}
77
+ get : String -> Json.Encode.Value -> Decoder b -> DataSource.DataSource b
78
+ get portName input decoder =
79
+ DataSource.Http.request
80
+ (Secrets.succeed
81
+ { url = "port://" ++ portName
82
+ , method = "GET"
83
+ , headers = []
84
+ , body = DataSource.Http.jsonBody input
85
+ }
86
+ )
87
+ decoder
@@ -0,0 +1,60 @@
1
+ module DataSource.ServerRequest exposing (ServerRequest, expectHeader, init, optionalHeader, staticData, toStaticHttp)
2
+
3
+ {-|
4
+
5
+ @docs ServerRequest, expectHeader, init, optionalHeader, staticData, toStaticHttp
6
+
7
+ -}
8
+
9
+ import DataSource
10
+ import DataSource.Http
11
+ import OptimizedDecoder
12
+ import Secrets
13
+
14
+
15
+ {-| -}
16
+ type ServerRequest decodesTo
17
+ = ServerRequest (OptimizedDecoder.Decoder decodesTo)
18
+
19
+
20
+ {-| -}
21
+ init : constructor -> ServerRequest constructor
22
+ init constructor =
23
+ ServerRequest (OptimizedDecoder.succeed constructor)
24
+
25
+
26
+ {-| -}
27
+ staticData : DataSource.DataSource String
28
+ staticData =
29
+ DataSource.Http.get (Secrets.succeed "$$elm-pages$$headers")
30
+ (OptimizedDecoder.field "headers"
31
+ (OptimizedDecoder.field "accept-language" OptimizedDecoder.string)
32
+ )
33
+
34
+
35
+ {-| -}
36
+ toStaticHttp : ServerRequest decodesTo -> DataSource.DataSource decodesTo
37
+ toStaticHttp (ServerRequest decoder) =
38
+ DataSource.Http.get (Secrets.succeed "$$elm-pages$$headers") decoder
39
+
40
+
41
+ {-| -}
42
+ expectHeader : String -> ServerRequest (String -> value) -> ServerRequest value
43
+ expectHeader headerName (ServerRequest decoder) =
44
+ decoder
45
+ |> OptimizedDecoder.andMap
46
+ (OptimizedDecoder.field headerName OptimizedDecoder.string
47
+ |> OptimizedDecoder.field "headers"
48
+ )
49
+ |> ServerRequest
50
+
51
+
52
+ {-| -}
53
+ optionalHeader : String -> ServerRequest (Maybe String -> value) -> ServerRequest value
54
+ optionalHeader headerName (ServerRequest decoder) =
55
+ decoder
56
+ |> OptimizedDecoder.andMap
57
+ (OptimizedDecoder.optionalField headerName OptimizedDecoder.string
58
+ |> OptimizedDecoder.field "headers"
59
+ )
60
+ |> ServerRequest