elm-pages 3.0.0-beta.15 → 3.0.0-beta.17
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 +1 -1
- package/codegen/elm-pages-codegen.js +66 -115
- 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.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/js/Runner.elm.js +20 -20
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_runner.js +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +5 -5
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +21 -21
- 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/js/node_runner.js +1 -1
- package/generator/review/elm-stuff/tests-0.19.1/js/node_supervisor.js +1 -1
- package/generator/src/RouteBuilder.elm +23 -23
- package/generator/src/SharedTemplate.elm +2 -2
- package/generator/src/SiteConfig.elm +2 -2
- package/generator/src/build.js +7 -7
- package/generator/src/cli.js +11 -8
- package/generator/src/compatibility-key.js +1 -1
- package/generator/src/dev-server.js +6 -6
- package/generator/src/render-test.js +1 -1
- package/generator/src/render.js +5 -5
- package/generator/src/request-cache.js +5 -5
- package/package.json +1 -1
- package/src/ApiRoute.elm +13 -13
- package/src/BackendTask/{Port.elm → Custom.elm} +60 -52
- package/src/BackendTask/Env.elm +9 -8
- package/src/BackendTask/File.elm +49 -10
- package/src/BackendTask/Glob.elm +6 -6
- package/src/BackendTask/Http.elm +12 -12
- package/src/BackendTask.elm +9 -23
- package/src/{Exception.elm → FatalError.elm} +37 -31
- package/src/Form.elm +3 -3
- package/src/Internal/ApiRoute.elm +5 -5
- package/src/Pages/Generate.elm +1 -1
- package/src/Pages/Internal/FatalError.elm +5 -0
- package/src/Pages/Internal/Platform/Cli.elm +4 -4
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
- package/src/Pages/Internal/Platform/GeneratorApplication.elm +7 -7
- package/src/Pages/Internal/Platform/StaticResponses.elm +10 -9
- package/src/Pages/Internal/Script.elm +2 -2
- package/src/Pages/Manifest.elm +2 -2
- package/src/Pages/ProgramConfig.elm +7 -7
- package/src/Pages/Script.elm +4 -4
- package/src/Pages/SiteConfig.elm +2 -2
- package/src/Server/Request.elm +3 -3
package/src/ApiRoute.elm
CHANGED
|
@@ -173,7 +173,7 @@ You define your ApiRoute's in `app/Api.elm`. Here's a simple example:
|
|
|
173
173
|
-}
|
|
174
174
|
|
|
175
175
|
import BackendTask exposing (BackendTask)
|
|
176
|
-
import
|
|
176
|
+
import FatalError exposing (FatalError)
|
|
177
177
|
import Head
|
|
178
178
|
import Internal.ApiRoute exposing (ApiRoute(..), ApiRouteBuilder(..))
|
|
179
179
|
import Json.Decode as Decode
|
|
@@ -192,14 +192,14 @@ type alias ApiRoute response =
|
|
|
192
192
|
{-| Same as [`preRender`](#preRender), but for an ApiRoute that has no dynamic segments. This is just a bit simpler because
|
|
193
193
|
since there are no dynamic segments, you don't need to provide a BackendTask with the list of dynamic segments to pre-render because there is only a single possible route.
|
|
194
194
|
-}
|
|
195
|
-
single : ApiRouteBuilder (BackendTask
|
|
195
|
+
single : ApiRouteBuilder (BackendTask FatalError String) (List String) -> ApiRoute Response
|
|
196
196
|
single handler =
|
|
197
197
|
handler
|
|
198
198
|
|> preRender (\constructor -> BackendTask.succeed [ constructor ])
|
|
199
199
|
|
|
200
200
|
|
|
201
201
|
{-| -}
|
|
202
|
-
serverRender : ApiRouteBuilder (Server.Request.Parser (BackendTask
|
|
202
|
+
serverRender : ApiRouteBuilder (Server.Request.Parser (BackendTask FatalError (Server.Response.Response Never Never))) constructor -> ApiRoute Response
|
|
203
203
|
serverRender ((ApiRouteBuilder patterns pattern _ _ _) as fullHandler) =
|
|
204
204
|
ApiRoute
|
|
205
205
|
{ regex = Regex.fromString ("^" ++ pattern ++ "$") |> Maybe.withDefault Regex.never
|
|
@@ -218,7 +218,7 @@ serverRender ((ApiRouteBuilder patterns pattern _ _ _) as fullHandler) =
|
|
|
218
218
|
|> BackendTask.onError
|
|
219
219
|
(\stringError ->
|
|
220
220
|
-- TODO make error with title and better context/formatting
|
|
221
|
-
|
|
221
|
+
FatalError.fromString stringError |> BackendTask.fail
|
|
222
222
|
)
|
|
223
223
|
|> BackendTask.andThen
|
|
224
224
|
(\rendered ->
|
|
@@ -260,10 +260,10 @@ serverRender ((ApiRouteBuilder patterns pattern _ _ _) as fullHandler) =
|
|
|
260
260
|
|
|
261
261
|
|
|
262
262
|
{-| -}
|
|
263
|
-
preRenderWithFallback : (constructor -> BackendTask
|
|
263
|
+
preRenderWithFallback : (constructor -> BackendTask FatalError (List (List String))) -> ApiRouteBuilder (BackendTask FatalError (Server.Response.Response Never Never)) constructor -> ApiRoute Response
|
|
264
264
|
preRenderWithFallback buildUrls ((ApiRouteBuilder patterns pattern _ toString constructor) as fullHandler) =
|
|
265
265
|
let
|
|
266
|
-
buildTimeRoutes__ : BackendTask
|
|
266
|
+
buildTimeRoutes__ : BackendTask FatalError (List String)
|
|
267
267
|
buildTimeRoutes__ =
|
|
268
268
|
buildUrls (constructor [])
|
|
269
269
|
|> BackendTask.map (List.map toString)
|
|
@@ -302,15 +302,15 @@ encodeStaticFileBody fileBody =
|
|
|
302
302
|
|
|
303
303
|
|
|
304
304
|
{-| -}
|
|
305
|
-
preRender : (constructor -> BackendTask
|
|
305
|
+
preRender : (constructor -> BackendTask FatalError (List (List String))) -> ApiRouteBuilder (BackendTask FatalError String) constructor -> ApiRoute Response
|
|
306
306
|
preRender buildUrls ((ApiRouteBuilder patterns pattern _ toString constructor) as fullHandler) =
|
|
307
307
|
let
|
|
308
|
-
buildTimeRoutes__ : BackendTask
|
|
308
|
+
buildTimeRoutes__ : BackendTask FatalError (List String)
|
|
309
309
|
buildTimeRoutes__ =
|
|
310
310
|
buildUrls (constructor [])
|
|
311
311
|
|> BackendTask.map (List.map toString)
|
|
312
312
|
|
|
313
|
-
preBuiltMatches : BackendTask
|
|
313
|
+
preBuiltMatches : BackendTask FatalError (List (List String))
|
|
314
314
|
preBuiltMatches =
|
|
315
315
|
buildUrls (constructor [])
|
|
316
316
|
in
|
|
@@ -323,7 +323,7 @@ preRender buildUrls ((ApiRouteBuilder patterns pattern _ toString constructor) a
|
|
|
323
323
|
matches =
|
|
324
324
|
Internal.ApiRoute.pathToMatches path fullHandler
|
|
325
325
|
|
|
326
|
-
routeFound : BackendTask
|
|
326
|
+
routeFound : BackendTask FatalError Bool
|
|
327
327
|
routeFound =
|
|
328
328
|
preBuiltMatches
|
|
329
329
|
|> BackendTask.map (List.member matches)
|
|
@@ -431,19 +431,19 @@ capture (ApiRouteBuilder patterns pattern previousHandler toString constructor)
|
|
|
431
431
|
|
|
432
432
|
{-| For internal use by generated code. Not so useful in user-land.
|
|
433
433
|
-}
|
|
434
|
-
getBuildTimeRoutes : ApiRoute response -> BackendTask
|
|
434
|
+
getBuildTimeRoutes : ApiRoute response -> BackendTask FatalError (List String)
|
|
435
435
|
getBuildTimeRoutes (ApiRoute handler) =
|
|
436
436
|
handler.buildTimeRoutes
|
|
437
437
|
|
|
438
438
|
|
|
439
439
|
{-| Include head tags on every page's HTML.
|
|
440
440
|
-}
|
|
441
|
-
withGlobalHeadTags : BackendTask
|
|
441
|
+
withGlobalHeadTags : BackendTask FatalError (List Head.Tag) -> ApiRoute response -> ApiRoute response
|
|
442
442
|
withGlobalHeadTags globalHeadTags (ApiRoute handler) =
|
|
443
443
|
ApiRoute { handler | globalHeadTags = Just globalHeadTags }
|
|
444
444
|
|
|
445
445
|
|
|
446
446
|
{-| -}
|
|
447
|
-
getGlobalHeadTagsBackendTask : ApiRoute response -> Maybe (BackendTask
|
|
447
|
+
getGlobalHeadTagsBackendTask : ApiRoute response -> Maybe (BackendTask FatalError (List Head.Tag))
|
|
448
448
|
getGlobalHeadTagsBackendTask (ApiRoute handler) =
|
|
449
449
|
handler.globalHeadTags
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
module BackendTask.
|
|
2
|
-
(
|
|
1
|
+
module BackendTask.Custom exposing
|
|
2
|
+
( run
|
|
3
3
|
, Error(..)
|
|
4
4
|
)
|
|
5
5
|
|
|
6
6
|
{-| 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.
|
|
7
7
|
|
|
8
|
-
With `BackendTask.
|
|
8
|
+
With `BackendTask.Custom`, you send and receive JSON to JavaScript running in NodeJS. As with any `BackendTask`, Custom BackendTask's are either run at build-time (for pre-rendered routes) or at request-time (for server-rendered routes). See [`BackendTask`](BackendTask) for more about the
|
|
9
9
|
lifecycle of `BackendTask`'s.
|
|
10
10
|
|
|
11
11
|
This means that you can call shell scripts, run NPM packages that are installed, or anything else you could do with NodeJS to perform custom side-effects, get some data, or both.
|
|
12
12
|
|
|
13
|
-
A `BackendTask.
|
|
13
|
+
A `BackendTask.Custom` will call an async JavaScript function with the given name from the definition in a file called `custom-backend-task.js` in your project's root directory. The function receives the input JSON value, and the Decoder is used to decode the return value of the async function.
|
|
14
14
|
|
|
15
|
-
@docs
|
|
15
|
+
@docs run
|
|
16
16
|
|
|
17
|
-
Here is the Elm code and corresponding JavaScript definition for getting an environment variable (or an `
|
|
18
|
-
we're using `BackendTask.
|
|
17
|
+
Here is the Elm code and corresponding JavaScript definition for getting an environment variable (or an `FatalError BackendTask.Custom.Error` if it isn't found). In this example,
|
|
18
|
+
we're using `BackendTask.allowFatal` to let the framework treat that as an unexpected exception, but we could also handle the possible failures of the `FatalError` (see [`FatalError`](FatalError)).
|
|
19
19
|
|
|
20
20
|
import BackendTask exposing (BackendTask)
|
|
21
|
-
import BackendTask.
|
|
21
|
+
import BackendTask.Custom
|
|
22
22
|
import Json.Encode
|
|
23
23
|
import OptimizedDecoder as Decode
|
|
24
24
|
|
|
25
|
-
data : BackendTask
|
|
25
|
+
data : BackendTask FatalError String
|
|
26
26
|
data =
|
|
27
|
-
BackendTask.
|
|
27
|
+
BackendTask.Custom.run "environmentVariable"
|
|
28
28
|
(Json.Encode.string "EDITOR")
|
|
29
29
|
Decode.string
|
|
30
|
-
|> BackendTask.
|
|
30
|
+
|> BackendTask.allowFatal
|
|
31
31
|
|
|
32
32
|
-- will resolve to "VIM" if you run `EDITOR=vim elm-pages dev`
|
|
33
33
|
|
|
34
34
|
```javascript
|
|
35
|
-
//
|
|
35
|
+
// custom-backend-task.js
|
|
36
36
|
|
|
37
37
|
module.exports =
|
|
38
38
|
/**
|
|
@@ -60,33 +60,37 @@ ${Object.keys(process.env).join("\n")}
|
|
|
60
60
|
## Performance
|
|
61
61
|
|
|
62
62
|
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. `elm-pages` performances all `BackendTask`'s in parallel whenever possible.
|
|
63
|
-
So if you do `BackendTask.map2 Tuple.pair myHttpBackendTask
|
|
64
|
-
it will need to resolve them in sequence rather than in parallel, but it's still best to avoid blocking IO operations in your BackendTask
|
|
63
|
+
So if you do `BackendTask.map2 Tuple.pair myHttpBackendTask myCustomBackendTask`, it will resolve those two in parallel. NodeJS performs best when you take advantage of its ability to do non-blocking I/O (file reads, HTTP requests, etc.). If you use `BackendTask.andThen`,
|
|
64
|
+
it will need to resolve them in sequence rather than in parallel, but it's still best to avoid blocking IO operations in your Custom BackendTask definitions.
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
## Error Handling
|
|
68
68
|
|
|
69
|
-
There are a few different things that can go wrong when running a
|
|
69
|
+
There are a few different things that can go wrong when running a custom-backend-task. These possible errors are captured in the `BackendTask.Custom.Error` type.
|
|
70
70
|
|
|
71
71
|
@docs Error
|
|
72
72
|
|
|
73
|
-
Any time you throw a JavaScript exception from a BackendTask.
|
|
74
|
-
to handle possible errors, but you can throw a JSON value and handle it in Elm in the `
|
|
73
|
+
Any time you throw a JavaScript exception from a BackendTask.Custom definition, it will give you a `CustomBackendTaskException`. It's usually easier to add a `try`/`catch` in your JavaScript code in `custom-backend-task.js`
|
|
74
|
+
to handle possible errors, but you can throw a JSON value and handle it in Elm in the `CustomBackendTaskException` call error.
|
|
75
75
|
|
|
76
76
|
-}
|
|
77
77
|
|
|
78
78
|
import BackendTask
|
|
79
79
|
import BackendTask.Http
|
|
80
80
|
import BackendTask.Internal.Request
|
|
81
|
-
import
|
|
81
|
+
import FatalError exposing (FatalError)
|
|
82
82
|
import Json.Decode as Decode exposing (Decoder)
|
|
83
83
|
import Json.Encode as Encode
|
|
84
84
|
import TerminalText
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
{-| -}
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
run :
|
|
89
|
+
String
|
|
90
|
+
-> Encode.Value
|
|
91
|
+
-> Decoder b
|
|
92
|
+
-> BackendTask.BackendTask { fatal : FatalError, recoverable : Error } b
|
|
93
|
+
run portName input decoder =
|
|
90
94
|
BackendTask.Internal.Request.request
|
|
91
95
|
{ name = "port"
|
|
92
96
|
, body =
|
|
@@ -100,16 +104,17 @@ get portName input decoder =
|
|
|
100
104
|
[ Decode.field "elm-pages-internal-error" Decode.string
|
|
101
105
|
|> Decode.andThen
|
|
102
106
|
(\errorKind ->
|
|
103
|
-
if errorKind == "
|
|
104
|
-
|
|
105
|
-
{ title = "
|
|
107
|
+
if errorKind == "CustomBackendTaskNotDefined" then
|
|
108
|
+
FatalError.recoverable
|
|
109
|
+
{ title = "Custom BackendTask Error"
|
|
106
110
|
, body =
|
|
107
|
-
[ TerminalText.text "Something went wrong in a call to BackendTask.
|
|
111
|
+
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.run. I expected to find a port named `"
|
|
108
112
|
, TerminalText.yellow portName
|
|
109
|
-
, TerminalText.text "` but I couldn't find it. Is the function exported in your
|
|
113
|
+
, TerminalText.text "` but I couldn't find it. Is the function exported in your custom-backend-task file?"
|
|
110
114
|
]
|
|
111
115
|
|> TerminalText.toString
|
|
112
116
|
}
|
|
117
|
+
(CustomBackendTaskNotDefined { name = portName })
|
|
113
118
|
|> Decode.succeed
|
|
114
119
|
|
|
115
120
|
else if errorKind == "ExportIsNotFunction" then
|
|
@@ -117,75 +122,78 @@ get portName input decoder =
|
|
|
117
122
|
|> Decode.maybe
|
|
118
123
|
|> Decode.map (Maybe.withDefault "")
|
|
119
124
|
|> Decode.map
|
|
120
|
-
(\
|
|
121
|
-
|
|
122
|
-
{ title = "
|
|
125
|
+
(\incorrectType ->
|
|
126
|
+
FatalError.recoverable
|
|
127
|
+
{ title = "Custom BackendTask Error"
|
|
123
128
|
, body =
|
|
124
|
-
[ TerminalText.text "Something went wrong in a call to BackendTask.
|
|
129
|
+
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.run. I found an export called `"
|
|
125
130
|
, TerminalText.yellow portName
|
|
126
131
|
, TerminalText.text "` but I expected its type to be function, but instead its type was: "
|
|
127
|
-
, TerminalText.red
|
|
132
|
+
, TerminalText.red incorrectType
|
|
128
133
|
]
|
|
129
134
|
|> TerminalText.toString
|
|
130
135
|
}
|
|
136
|
+
ExportIsNotFunction
|
|
131
137
|
)
|
|
132
138
|
|
|
133
|
-
else if errorKind == "
|
|
134
|
-
|
|
135
|
-
{ title = "
|
|
139
|
+
else if errorKind == "MissingCustomBackendTaskFile" then
|
|
140
|
+
FatalError.recoverable
|
|
141
|
+
{ title = "Custom BackendTask Error"
|
|
136
142
|
, body =
|
|
137
|
-
[ TerminalText.text "Something went wrong in a call to BackendTask.
|
|
143
|
+
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.run. I couldn't find your custom-backend-task file. Be sure to create a 'custom-backend-task.ts' or 'custom-backend-task.js' file."
|
|
138
144
|
]
|
|
139
145
|
|> TerminalText.toString
|
|
140
146
|
}
|
|
147
|
+
MissingCustomBackendTaskFile
|
|
141
148
|
|> Decode.succeed
|
|
142
149
|
|
|
143
|
-
else if errorKind == "
|
|
150
|
+
else if errorKind == "ErrorInCustomBackendTaskFile" then
|
|
144
151
|
Decode.field "error" Decode.string
|
|
145
152
|
|> Decode.maybe
|
|
146
153
|
|> Decode.map (Maybe.withDefault "")
|
|
147
154
|
|> Decode.map
|
|
148
155
|
(\errorMessage ->
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
{ title = "Port Error"
|
|
156
|
+
FatalError.recoverable
|
|
157
|
+
{ title = "Custom BackendTask Error"
|
|
152
158
|
, body =
|
|
153
|
-
[ TerminalText.text "Something went wrong in a call to BackendTask.
|
|
159
|
+
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.run. I couldn't import the port definitions file, because of this exception:\n\n"
|
|
154
160
|
, TerminalText.red errorMessage
|
|
155
161
|
, TerminalText.text "\n\nAre there syntax errors or exceptions thrown during import?"
|
|
156
162
|
]
|
|
157
163
|
|> TerminalText.toString
|
|
158
164
|
}
|
|
165
|
+
ErrorInCustomBackendTaskFile
|
|
159
166
|
)
|
|
160
167
|
|
|
161
|
-
else if errorKind == "
|
|
168
|
+
else if errorKind == "CustomBackendTaskException" then
|
|
162
169
|
Decode.field "error" Decode.value
|
|
163
170
|
|> Decode.maybe
|
|
164
171
|
|> Decode.map (Maybe.withDefault Encode.null)
|
|
165
172
|
|> Decode.map
|
|
166
173
|
(\portCallError ->
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
{ title = "Port Error"
|
|
174
|
+
FatalError.recoverable
|
|
175
|
+
{ title = "Custom BackendTask Error"
|
|
170
176
|
, body =
|
|
171
|
-
[ TerminalText.text "Something went wrong in a call to BackendTask.
|
|
177
|
+
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.run. I was able to import the port definitions file, but when running it I encountered this exception:\n\n"
|
|
172
178
|
, TerminalText.red (Encode.encode 2 portCallError)
|
|
173
|
-
, TerminalText.text "\n\nYou could add a `try`/`catch` in your `
|
|
179
|
+
, TerminalText.text "\n\nYou could add a `try`/`catch` in your `custom-backend-task` JavaScript code to handle that error."
|
|
174
180
|
]
|
|
175
181
|
|> TerminalText.toString
|
|
176
182
|
}
|
|
183
|
+
(CustomBackendTaskException portCallError)
|
|
177
184
|
)
|
|
178
185
|
|
|
179
186
|
else
|
|
180
|
-
|
|
181
|
-
{ title = "
|
|
187
|
+
FatalError.recoverable
|
|
188
|
+
{ title = "Custom BackendTask Error"
|
|
182
189
|
, body =
|
|
183
|
-
[ TerminalText.text "Something went wrong in a call to BackendTask.
|
|
190
|
+
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.run. I expected to find a port named `"
|
|
184
191
|
, TerminalText.yellow portName
|
|
185
192
|
, TerminalText.text "`."
|
|
186
193
|
]
|
|
187
194
|
|> TerminalText.toString
|
|
188
195
|
}
|
|
196
|
+
ErrorInCustomBackendTaskFile
|
|
189
197
|
|> Decode.succeed
|
|
190
198
|
)
|
|
191
199
|
|> Decode.map Err
|
|
@@ -199,8 +207,8 @@ get portName input decoder =
|
|
|
199
207
|
{-| -}
|
|
200
208
|
type Error
|
|
201
209
|
= Error
|
|
202
|
-
|
|
|
203
|
-
|
|
|
204
|
-
|
|
|
205
|
-
|
|
|
210
|
+
| ErrorInCustomBackendTaskFile
|
|
211
|
+
| MissingCustomBackendTaskFile
|
|
212
|
+
| CustomBackendTaskNotDefined { name : String }
|
|
213
|
+
| CustomBackendTaskException Decode.Value
|
|
206
214
|
| ExportIsNotFunction
|
package/src/BackendTask/Env.elm
CHANGED
|
@@ -8,23 +8,23 @@ down into the final `Data` value, it won't end up in the client!
|
|
|
8
8
|
|
|
9
9
|
import BackendTask exposing (BackendTask)
|
|
10
10
|
import BackendTask.Env
|
|
11
|
-
import
|
|
11
|
+
import FatalError exposing (FatalError)
|
|
12
12
|
|
|
13
13
|
type alias EnvVariables =
|
|
14
14
|
{ sendGridKey : String
|
|
15
15
|
, siteUrl : String
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
sendEmail : Email -> BackendTask
|
|
18
|
+
sendEmail : Email -> BackendTask FatalError ()
|
|
19
19
|
sendEmail email =
|
|
20
20
|
BackendTask.map2 EnvVariables
|
|
21
|
-
(BackendTask.Env.expect "SEND_GRID_KEY" |> BackendTask.
|
|
21
|
+
(BackendTask.Env.expect "SEND_GRID_KEY" |> BackendTask.allowFatal)
|
|
22
22
|
(BackendTask.Env.get "BASE_URL"
|
|
23
23
|
|> BackendTask.map (Maybe.withDefault "http://localhost:1234")
|
|
24
24
|
)
|
|
25
25
|
|> BackendTask.andThen (sendEmailBackendTask email)
|
|
26
26
|
|
|
27
|
-
sendEmailBackendTask : Email -> EnvVariables -> BackendTask
|
|
27
|
+
sendEmailBackendTask : Email -> EnvVariables -> BackendTask FatalError ()
|
|
28
28
|
sendEmailBackendTask email envVariables =
|
|
29
29
|
Debug.todo "Not defined here"
|
|
30
30
|
|
|
@@ -40,7 +40,7 @@ down into the final `Data` value, it won't end up in the client!
|
|
|
40
40
|
import BackendTask exposing (BackendTask)
|
|
41
41
|
import BackendTask.Http
|
|
42
42
|
import BackendTask.Internal.Request
|
|
43
|
-
import
|
|
43
|
+
import FatalError exposing (FatalError)
|
|
44
44
|
import Json.Decode as Decode
|
|
45
45
|
import Json.Encode as Encode
|
|
46
46
|
import TerminalText
|
|
@@ -65,9 +65,9 @@ get envVariableName =
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
|
|
68
|
-
{-| Get an environment variable, or a BackendTask
|
|
68
|
+
{-| Get an environment variable, or a BackendTask FatalError if there is no environment variable matching that name.
|
|
69
69
|
-}
|
|
70
|
-
expect : String -> BackendTask
|
|
70
|
+
expect : String -> BackendTask { fatal : FatalError, recoverable : Error } String
|
|
71
71
|
expect envVariableName =
|
|
72
72
|
envVariableName
|
|
73
73
|
|> get
|
|
@@ -75,7 +75,7 @@ expect envVariableName =
|
|
|
75
75
|
(\maybeValue ->
|
|
76
76
|
maybeValue
|
|
77
77
|
|> Result.fromMaybe
|
|
78
|
-
(
|
|
78
|
+
(FatalError.recoverable
|
|
79
79
|
{ title = "Missing Env Variable"
|
|
80
80
|
, body =
|
|
81
81
|
[ TerminalText.text "BackendTask.Env.expect was expecting a variable `"
|
|
@@ -84,6 +84,7 @@ expect envVariableName =
|
|
|
84
84
|
]
|
|
85
85
|
|> TerminalText.toString
|
|
86
86
|
}
|
|
87
|
+
(MissingEnvVariable envVariableName)
|
|
87
88
|
)
|
|
88
89
|
|> BackendTask.fromResult
|
|
89
90
|
)
|
package/src/BackendTask/File.elm
CHANGED
|
@@ -51,7 +51,7 @@ plain old JSON in Elm.
|
|
|
51
51
|
import BackendTask exposing (BackendTask)
|
|
52
52
|
import BackendTask.Http
|
|
53
53
|
import BackendTask.Internal.Request
|
|
54
|
-
import
|
|
54
|
+
import FatalError exposing (FatalError)
|
|
55
55
|
import Json.Decode as Decode exposing (Decoder)
|
|
56
56
|
import TerminalText
|
|
57
57
|
|
|
@@ -141,7 +141,15 @@ It's common to parse the body with a markdown parser or other format.
|
|
|
141
141
|
)
|
|
142
142
|
|
|
143
143
|
-}
|
|
144
|
-
bodyWithFrontmatter :
|
|
144
|
+
bodyWithFrontmatter :
|
|
145
|
+
(String -> Decoder frontmatter)
|
|
146
|
+
-> String
|
|
147
|
+
->
|
|
148
|
+
BackendTask
|
|
149
|
+
{ fatal : FatalError
|
|
150
|
+
, recoverable : FileReadError Decode.Error
|
|
151
|
+
}
|
|
152
|
+
frontmatter
|
|
145
153
|
bodyWithFrontmatter frontmatterDecoder filePath =
|
|
146
154
|
read filePath
|
|
147
155
|
(body
|
|
@@ -213,7 +221,15 @@ the [`BackendTask`](BackendTask) API along with [`BackendTask.Glob`](BackendTask
|
|
|
213
221
|
|> BackendTask.resolve
|
|
214
222
|
|
|
215
223
|
-}
|
|
216
|
-
onlyFrontmatter :
|
|
224
|
+
onlyFrontmatter :
|
|
225
|
+
Decoder frontmatter
|
|
226
|
+
-> String
|
|
227
|
+
->
|
|
228
|
+
BackendTask
|
|
229
|
+
{ fatal : FatalError
|
|
230
|
+
, recoverable : FileReadError Decode.Error
|
|
231
|
+
}
|
|
232
|
+
frontmatter
|
|
217
233
|
onlyFrontmatter frontmatterDecoder filePath =
|
|
218
234
|
read filePath
|
|
219
235
|
(frontmatter frontmatterDecoder)
|
|
@@ -240,7 +256,14 @@ Hey there! This is my first post :)
|
|
|
240
256
|
Then data will yield the value `"Hey there! This is my first post :)"`.
|
|
241
257
|
|
|
242
258
|
-}
|
|
243
|
-
bodyWithoutFrontmatter :
|
|
259
|
+
bodyWithoutFrontmatter :
|
|
260
|
+
String
|
|
261
|
+
->
|
|
262
|
+
BackendTask
|
|
263
|
+
{ fatal : FatalError
|
|
264
|
+
, recoverable : FileReadError decoderError
|
|
265
|
+
}
|
|
266
|
+
String
|
|
244
267
|
bodyWithoutFrontmatter filePath =
|
|
245
268
|
read filePath
|
|
246
269
|
body
|
|
@@ -264,7 +287,7 @@ You could read a file called `hello.txt` in your root project directory like thi
|
|
|
264
287
|
File.rawFile "hello.txt"
|
|
265
288
|
|
|
266
289
|
-}
|
|
267
|
-
rawFile : String -> BackendTask
|
|
290
|
+
rawFile : String -> BackendTask { fatal : FatalError, recoverable : FileReadError decoderError } String
|
|
268
291
|
rawFile filePath =
|
|
269
292
|
read filePath (Decode.field "rawFile" Decode.string)
|
|
270
293
|
|
|
@@ -286,7 +309,15 @@ The Decode will strip off any unused JSON data.
|
|
|
286
309
|
"elm.json"
|
|
287
310
|
|
|
288
311
|
-}
|
|
289
|
-
jsonFile :
|
|
312
|
+
jsonFile :
|
|
313
|
+
Decoder a
|
|
314
|
+
-> String
|
|
315
|
+
->
|
|
316
|
+
BackendTask
|
|
317
|
+
{ fatal : FatalError
|
|
318
|
+
, recoverable : FileReadError Decode.Error
|
|
319
|
+
}
|
|
320
|
+
a
|
|
290
321
|
jsonFile jsonFileDecoder filePath =
|
|
291
322
|
rawFile filePath
|
|
292
323
|
|> BackendTask.andThen
|
|
@@ -295,13 +326,14 @@ jsonFile jsonFileDecoder filePath =
|
|
|
295
326
|
|> Decode.decodeString jsonFileDecoder
|
|
296
327
|
|> Result.mapError
|
|
297
328
|
(\jsonDecodeError ->
|
|
298
|
-
|
|
329
|
+
FatalError.recoverable
|
|
299
330
|
{ title = "JSON Decoding Error"
|
|
300
331
|
, body =
|
|
301
332
|
[ TerminalText.text (Decode.errorToString jsonDecodeError)
|
|
302
333
|
]
|
|
303
334
|
|> TerminalText.toString
|
|
304
335
|
}
|
|
336
|
+
(DecodingError jsonDecodeError)
|
|
305
337
|
)
|
|
306
338
|
|> BackendTask.fromResult
|
|
307
339
|
)
|
|
@@ -314,7 +346,7 @@ body =
|
|
|
314
346
|
Decode.field "withoutFrontmatter" Decode.string
|
|
315
347
|
|
|
316
348
|
|
|
317
|
-
read : String -> Decoder a -> BackendTask
|
|
349
|
+
read : String -> Decoder a -> BackendTask { fatal : FatalError, recoverable : FileReadError error } a
|
|
318
350
|
read filePath decoder =
|
|
319
351
|
BackendTask.Internal.Request.request
|
|
320
352
|
{ name = "read-file"
|
|
@@ -330,10 +362,16 @@ read filePath decoder =
|
|
|
330
362
|
|> BackendTask.andThen BackendTask.fromResult
|
|
331
363
|
|
|
332
364
|
|
|
333
|
-
errorDecoder :
|
|
365
|
+
errorDecoder :
|
|
366
|
+
String
|
|
367
|
+
->
|
|
368
|
+
Decoder
|
|
369
|
+
{ fatal : FatalError
|
|
370
|
+
, recoverable : FileReadError decoding
|
|
371
|
+
}
|
|
334
372
|
errorDecoder filePath =
|
|
335
373
|
Decode.succeed
|
|
336
|
-
(
|
|
374
|
+
(FatalError.recoverable
|
|
337
375
|
{ title = "File Doesn't Exist"
|
|
338
376
|
, body =
|
|
339
377
|
[ TerminalText.text "Couldn't find file at path `"
|
|
@@ -342,4 +380,5 @@ errorDecoder filePath =
|
|
|
342
380
|
]
|
|
343
381
|
|> TerminalText.toString
|
|
344
382
|
}
|
|
383
|
+
FileDoesntExist
|
|
345
384
|
)
|
package/src/BackendTask/Glob.elm
CHANGED
|
@@ -229,7 +229,7 @@ import BackendTask exposing (BackendTask)
|
|
|
229
229
|
import BackendTask.Http
|
|
230
230
|
import BackendTask.Internal.Glob exposing (Glob(..))
|
|
231
231
|
import BackendTask.Internal.Request
|
|
232
|
-
import
|
|
232
|
+
import FatalError exposing (FatalError, Recoverable)
|
|
233
233
|
import Json.Decode as Decode
|
|
234
234
|
import Json.Encode as Encode
|
|
235
235
|
import List.Extra
|
|
@@ -1054,7 +1054,7 @@ so it's ideal to make this kind of assertion rather than having fallback behavio
|
|
|
1054
1054
|
issues (like if we had instead ignored the case where there are two or more matching blog post files).
|
|
1055
1055
|
|
|
1056
1056
|
-}
|
|
1057
|
-
expectUniqueMatch : Glob a -> BackendTask (
|
|
1057
|
+
expectUniqueMatch : Glob a -> BackendTask (Recoverable String) a
|
|
1058
1058
|
expectUniqueMatch glob =
|
|
1059
1059
|
glob
|
|
1060
1060
|
|> toBackendTask
|
|
@@ -1066,14 +1066,14 @@ expectUniqueMatch glob =
|
|
|
1066
1066
|
|
|
1067
1067
|
[] ->
|
|
1068
1068
|
BackendTask.fail <|
|
|
1069
|
-
|
|
1070
|
-
|
|
1069
|
+
FatalError.recoverable
|
|
1070
|
+
{ title = "Non-Unique Glob", body = "No files matched the pattern: " ++ toPatternString glob }
|
|
1071
1071
|
("No files matched the pattern: " ++ toPatternString glob)
|
|
1072
1072
|
|
|
1073
1073
|
_ ->
|
|
1074
1074
|
BackendTask.fail <|
|
|
1075
|
-
|
|
1076
|
-
"
|
|
1075
|
+
FatalError.recoverable
|
|
1076
|
+
{ title = "Non-Unique Glob", body = "Expected a unique match, but more than one file matched." }
|
|
1077
1077
|
"More than one file matched."
|
|
1078
1078
|
)
|
|
1079
1079
|
|