elm-pages 3.0.0-beta.9 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/adapter/netlify.js +207 -0
- package/codegen/{elm-pages-codegen.js → elm-pages-codegen.cjs} +2731 -2939
- 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.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/o.dat +0 -0
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/elm.json +1 -1
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Reporter.elm.js +1527 -422
- package/generator/dead-code-review/elm-stuff/tests-0.19.1/js/Runner.elm.js +16840 -13653
- 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 +4 -4
- package/generator/dead-code-review/elm.json +9 -7
- package/generator/dead-code-review/src/Pages/Review/DeadCodeEliminateData.elm +59 -10
- package/generator/dead-code-review/tests/Pages/Review/DeadCodeEliminateDataTest.elm +52 -36
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Internal-RoutePattern.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmi +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/Pages-Review-NoContractViolations.elmo +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/d.dat +0 -0
- package/generator/review/elm-stuff/tests-0.19.1/elm-stuff/0.19.1/i.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/Reporter.elm.js +1527 -422
- package/generator/review/elm-stuff/tests-0.19.1/js/Runner.elm.js +25118 -21832
- 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 +4 -4
- package/generator/review/elm.json +10 -10
- package/generator/src/RouteBuilder.elm +93 -128
- package/generator/src/SharedTemplate.elm +8 -7
- package/generator/src/SiteConfig.elm +3 -2
- package/generator/src/basepath-middleware.js +3 -3
- package/generator/src/build.js +143 -59
- package/generator/src/cli.js +292 -88
- package/generator/src/codegen.js +29 -27
- package/generator/src/compatibility-key.js +3 -0
- package/generator/src/compile-elm.js +43 -26
- package/generator/src/config.js +2 -4
- package/generator/src/copy-dir.js +2 -2
- package/generator/src/dev-server.js +160 -102
- package/generator/src/dir-helpers.js +9 -26
- package/generator/src/elm-codegen.js +5 -4
- package/generator/src/elm-file-constants.js +2 -3
- package/generator/src/error-formatter.js +12 -11
- package/generator/src/file-helpers.js +3 -4
- package/generator/src/generate-template-module-connector.js +23 -23
- package/generator/src/init.js +9 -8
- package/generator/src/pre-render-html.js +10 -13
- package/generator/src/render-test.js +109 -0
- package/generator/src/render-worker.js +25 -28
- package/generator/src/render.js +320 -143
- package/generator/src/request-cache.js +265 -162
- package/generator/src/resolve-elm-module.js +64 -0
- package/generator/src/rewrite-client-elm-json.js +6 -5
- package/generator/src/rewrite-elm-json-help.js +56 -0
- package/generator/src/rewrite-elm-json.js +17 -7
- package/generator/src/route-codegen-helpers.js +16 -31
- package/generator/src/seo-renderer.js +1 -3
- package/generator/src/vite-utils.js +1 -2
- package/generator/static-code/elm-pages.js +10 -0
- package/generator/static-code/hmr.js +79 -13
- package/generator/template/app/Api.elm +3 -2
- package/generator/template/app/Effect.elm +155 -0
- package/generator/template/app/ErrorPage.elm +49 -6
- package/generator/template/app/Route/Blog/Slug_.elm +86 -0
- package/generator/template/app/Route/Greet.elm +107 -0
- package/generator/template/app/Route/Hello.elm +119 -0
- package/generator/template/app/Route/Index.elm +26 -25
- package/generator/template/app/Shared.elm +38 -39
- package/generator/template/app/Site.elm +4 -7
- package/generator/template/app/View.elm +9 -8
- package/generator/template/codegen/elm.codegen.json +18 -0
- package/generator/template/custom-backend-task.ts +3 -0
- package/generator/template/elm-pages.config.mjs +13 -0
- package/generator/template/elm-tooling.json +0 -3
- package/generator/template/elm.json +25 -20
- package/generator/template/index.ts +1 -2
- package/generator/template/netlify.toml +4 -1
- package/generator/template/package.json +10 -4
- package/generator/template/script/.elm-pages/compiled-ports/custom-backend-task.mjs +7 -0
- package/generator/template/script/custom-backend-task.ts +3 -0
- package/generator/template/script/elm.json +61 -0
- package/generator/template/script/src/AddRoute.elm +312 -0
- package/generator/template/script/src/Stars.elm +42 -0
- package/package.json +30 -27
- package/src/ApiRoute.elm +249 -82
- package/src/BackendTask/Custom.elm +325 -0
- package/src/BackendTask/Env.elm +90 -0
- package/src/{DataSource → BackendTask}/File.elm +171 -56
- package/src/{DataSource → BackendTask}/Glob.elm +136 -125
- package/src/BackendTask/Http.elm +679 -0
- package/src/{DataSource → BackendTask}/Internal/Glob.elm +1 -1
- package/src/BackendTask/Internal/Request.elm +69 -0
- package/src/BackendTask/Random.elm +79 -0
- package/src/BackendTask/Time.elm +47 -0
- package/src/BackendTask.elm +531 -0
- package/src/FatalError.elm +90 -0
- package/src/FormData.elm +21 -18
- package/src/Head/Seo.elm +4 -4
- package/src/Head.elm +112 -8
- package/src/Internal/ApiRoute.elm +7 -5
- package/src/Internal/Request.elm +84 -4
- package/src/PageServerResponse.elm +6 -1
- package/src/Pages/ConcurrentSubmission.elm +127 -0
- package/src/Pages/Form.elm +340 -0
- package/src/Pages/FormData.elm +19 -0
- package/src/Pages/GeneratorProgramConfig.elm +15 -0
- package/src/Pages/Internal/FatalError.elm +5 -0
- package/src/Pages/Internal/Msg.elm +93 -0
- package/src/Pages/Internal/NotFoundReason.elm +4 -4
- package/src/Pages/Internal/Platform/Cli.elm +586 -768
- package/src/Pages/Internal/Platform/CompatibilityKey.elm +1 -1
- package/src/Pages/Internal/Platform/Effect.elm +1 -2
- package/src/Pages/Internal/Platform/GeneratorApplication.elm +379 -0
- package/src/Pages/Internal/Platform/StaticResponses.elm +65 -276
- package/src/Pages/Internal/Platform/ToJsPayload.elm +6 -9
- package/src/Pages/Internal/Platform.elm +330 -203
- package/src/Pages/Internal/ResponseSketch.elm +2 -2
- package/src/Pages/Internal/Script.elm +17 -0
- package/src/Pages/Internal/StaticHttpBody.elm +35 -1
- package/src/Pages/Manifest.elm +52 -11
- package/src/Pages/Navigation.elm +85 -0
- package/src/Pages/PageUrl.elm +26 -12
- package/src/Pages/ProgramConfig.elm +32 -22
- package/src/Pages/Script.elm +166 -0
- package/src/Pages/SiteConfig.elm +3 -2
- package/src/Pages/StaticHttp/Request.elm +2 -2
- package/src/Pages/StaticHttpRequest.elm +23 -99
- package/src/Pages/Url.elm +3 -3
- package/src/PagesMsg.elm +88 -0
- package/src/QueryParams.elm +21 -172
- package/src/RenderRequest.elm +7 -7
- package/src/RequestsAndPending.elm +37 -20
- package/src/Result/Extra.elm +26 -0
- package/src/Scaffold/Form.elm +569 -0
- package/src/Scaffold/Route.elm +1431 -0
- package/src/Server/Request.elm +476 -1001
- package/src/Server/Response.elm +130 -36
- package/src/Server/Session.elm +181 -111
- package/src/Server/SetCookie.elm +80 -32
- package/src/Stub.elm +53 -0
- package/src/Test/Html/Internal/ElmHtml/ToString.elm +8 -9
- package/src/{Path.elm → UrlPath.elm} +33 -36
- package/generator/template/public/images/icon-png.png +0 -0
- package/src/DataSource/Env.elm +0 -38
- package/src/DataSource/Http.elm +0 -446
- package/src/DataSource/Internal/Request.elm +0 -20
- package/src/DataSource/Port.elm +0 -90
- package/src/DataSource.elm +0 -546
- package/src/Form/Field.elm +0 -717
- package/src/Form/FieldStatus.elm +0 -36
- package/src/Form/FieldView.elm +0 -417
- package/src/Form/FormData.elm +0 -22
- package/src/Form/Validation.elm +0 -391
- package/src/Form/Value.elm +0 -118
- package/src/Form.elm +0 -1683
- package/src/FormDecoder.elm +0 -102
- package/src/Pages/FormState.elm +0 -256
- package/src/Pages/Generate.elm +0 -1242
- package/src/Pages/Internal/Form.elm +0 -17
- package/src/Pages/Internal/Platform/Cli.elm.bak +0 -1276
- package/src/Pages/Msg.elm +0 -79
- package/src/Pages/Transition.elm +0 -70
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
module BackendTask.Custom exposing
|
|
2
|
+
( run
|
|
3
|
+
, Error(..)
|
|
4
|
+
, timeDecoder, dateDecoder
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
{-| 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.
|
|
8
|
+
|
|
9
|
+
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
|
|
10
|
+
lifecycle of `BackendTask`'s.
|
|
11
|
+
|
|
12
|
+
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.
|
|
13
|
+
|
|
14
|
+
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.
|
|
15
|
+
|
|
16
|
+
@docs run
|
|
17
|
+
|
|
18
|
+
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,
|
|
19
|
+
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)).
|
|
20
|
+
|
|
21
|
+
import BackendTask exposing (BackendTask)
|
|
22
|
+
import BackendTask.Custom
|
|
23
|
+
import Json.Encode
|
|
24
|
+
import OptimizedDecoder as Decode
|
|
25
|
+
|
|
26
|
+
data : BackendTask FatalError String
|
|
27
|
+
data =
|
|
28
|
+
BackendTask.Custom.run "environmentVariable"
|
|
29
|
+
(Json.Encode.string "EDITOR")
|
|
30
|
+
Decode.string
|
|
31
|
+
|> BackendTask.allowFatal
|
|
32
|
+
|
|
33
|
+
-- will resolve to "VIM" if you run `EDITOR=vim elm-pages dev`
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
// custom-backend-task.js
|
|
37
|
+
|
|
38
|
+
module.exports =
|
|
39
|
+
/**
|
|
40
|
+
* @param { unknown } fromElm
|
|
41
|
+
* @returns { Promise<unknown> }
|
|
42
|
+
*/
|
|
43
|
+
{
|
|
44
|
+
environmentVariable: async function (name) {
|
|
45
|
+
const result = process.env[name];
|
|
46
|
+
if (result) {
|
|
47
|
+
return result;
|
|
48
|
+
} else {
|
|
49
|
+
throw `No environment variable called ${name}
|
|
50
|
+
|
|
51
|
+
Available:
|
|
52
|
+
|
|
53
|
+
${Object.keys(process.env).join("\n")}
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## Performance
|
|
62
|
+
|
|
63
|
+
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` performs all `BackendTask`'s in parallel whenever possible.
|
|
64
|
+
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`,
|
|
65
|
+
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.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## Error Handling
|
|
69
|
+
|
|
70
|
+
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.
|
|
71
|
+
|
|
72
|
+
@docs Error
|
|
73
|
+
|
|
74
|
+
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`
|
|
75
|
+
to handle possible errors, but you can throw a JSON value and handle it in Elm in the `CustomBackendTaskException` call error.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
## Decoding JS Date Objects
|
|
79
|
+
|
|
80
|
+
These decoders are for use with decoding JS values of type `Date`. If you have control over the format, it may be better to
|
|
81
|
+
be more explicit with a [Rata Die](https://en.wikipedia.org/wiki/Rata_Die) number value or an ISO-8601 formatted date string instead.
|
|
82
|
+
But often JavaScript libraries and core APIs will give you JS Date objects, so this can be useful for working with those.
|
|
83
|
+
|
|
84
|
+
@docs timeDecoder, dateDecoder
|
|
85
|
+
|
|
86
|
+
-}
|
|
87
|
+
|
|
88
|
+
import BackendTask exposing (BackendTask)
|
|
89
|
+
import BackendTask.Http
|
|
90
|
+
import Date
|
|
91
|
+
import FatalError exposing (FatalError)
|
|
92
|
+
import Json.Decode as Decode exposing (Decoder)
|
|
93
|
+
import Json.Encode as Encode
|
|
94
|
+
import TerminalText
|
|
95
|
+
import Time
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
{-| -}
|
|
99
|
+
run :
|
|
100
|
+
String
|
|
101
|
+
-> Encode.Value
|
|
102
|
+
-> Decoder b
|
|
103
|
+
-> BackendTask { fatal : FatalError, recoverable : Error } b
|
|
104
|
+
run portName input decoder =
|
|
105
|
+
request
|
|
106
|
+
{ body =
|
|
107
|
+
Encode.object
|
|
108
|
+
[ ( "input", input )
|
|
109
|
+
, ( "portName", Encode.string portName )
|
|
110
|
+
]
|
|
111
|
+
|> BackendTask.Http.jsonBody
|
|
112
|
+
, expect =
|
|
113
|
+
Decode.field "elm-pages-internal-error" Decode.string
|
|
114
|
+
|> Decode.maybe
|
|
115
|
+
|> Decode.andThen
|
|
116
|
+
(\maybeInternalErrorCode ->
|
|
117
|
+
case maybeInternalErrorCode of
|
|
118
|
+
Just errorKind ->
|
|
119
|
+
--Decode.field "elm-pages-internal-error" Decode.string
|
|
120
|
+
-- |> Decode.andThen
|
|
121
|
+
(--\errorKind ->
|
|
122
|
+
if errorKind == "CustomBackendTaskNotDefined" then
|
|
123
|
+
FatalError.recoverable
|
|
124
|
+
{ title = "Custom BackendTask Error"
|
|
125
|
+
, body =
|
|
126
|
+
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.run. I expected to find a port named `"
|
|
127
|
+
, TerminalText.yellow portName
|
|
128
|
+
, TerminalText.text "` but I couldn't find it. Is the function exported in your custom-backend-task file?"
|
|
129
|
+
]
|
|
130
|
+
|> TerminalText.toString
|
|
131
|
+
}
|
|
132
|
+
(CustomBackendTaskNotDefined { name = portName })
|
|
133
|
+
|> Decode.succeed
|
|
134
|
+
|
|
135
|
+
else if errorKind == "ExportIsNotFunction" then
|
|
136
|
+
Decode.field "error" Decode.string
|
|
137
|
+
|> Decode.maybe
|
|
138
|
+
|> Decode.map (Maybe.withDefault "")
|
|
139
|
+
|> Decode.map
|
|
140
|
+
(\incorrectType ->
|
|
141
|
+
FatalError.recoverable
|
|
142
|
+
{ title = "Custom BackendTask Error"
|
|
143
|
+
, body =
|
|
144
|
+
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.run. I found an export called `"
|
|
145
|
+
, TerminalText.yellow portName
|
|
146
|
+
, TerminalText.text "` but I expected its type to be function, but instead its type was: "
|
|
147
|
+
, TerminalText.red incorrectType
|
|
148
|
+
]
|
|
149
|
+
|> TerminalText.toString
|
|
150
|
+
}
|
|
151
|
+
ExportIsNotFunction
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
else if errorKind == "MissingCustomBackendTaskFile" then
|
|
155
|
+
FatalError.recoverable
|
|
156
|
+
{ title = "Custom BackendTask Error"
|
|
157
|
+
, body =
|
|
158
|
+
[ 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."
|
|
159
|
+
]
|
|
160
|
+
|> TerminalText.toString
|
|
161
|
+
}
|
|
162
|
+
MissingCustomBackendTaskFile
|
|
163
|
+
|> Decode.succeed
|
|
164
|
+
|
|
165
|
+
else if errorKind == "ErrorInCustomBackendTaskFile" then
|
|
166
|
+
Decode.field "error" Decode.string
|
|
167
|
+
|> Decode.maybe
|
|
168
|
+
|> Decode.map (Maybe.withDefault "")
|
|
169
|
+
|> Decode.map
|
|
170
|
+
(\errorMessage ->
|
|
171
|
+
FatalError.recoverable
|
|
172
|
+
{ title = "Custom BackendTask Error"
|
|
173
|
+
, body =
|
|
174
|
+
[ 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"
|
|
175
|
+
, TerminalText.red errorMessage
|
|
176
|
+
, TerminalText.text "\n\nAre there syntax errors or exceptions thrown during import?"
|
|
177
|
+
]
|
|
178
|
+
|> TerminalText.toString
|
|
179
|
+
}
|
|
180
|
+
ErrorInCustomBackendTaskFile
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
else if errorKind == "CustomBackendTaskException" then
|
|
184
|
+
Decode.field "error" Decode.value
|
|
185
|
+
|> Decode.maybe
|
|
186
|
+
|> Decode.map (Maybe.withDefault Encode.null)
|
|
187
|
+
|> Decode.map
|
|
188
|
+
(\portCallError ->
|
|
189
|
+
FatalError.recoverable
|
|
190
|
+
{ title = "Custom BackendTask Error"
|
|
191
|
+
, body =
|
|
192
|
+
[ 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"
|
|
193
|
+
, TerminalText.red (Encode.encode 2 portCallError)
|
|
194
|
+
, TerminalText.text "\n\nYou could add a `try`/`catch` in your `custom-backend-task` JavaScript code to handle that error."
|
|
195
|
+
]
|
|
196
|
+
|> TerminalText.toString
|
|
197
|
+
}
|
|
198
|
+
(CustomBackendTaskException portCallError)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
else if errorKind == "NonJsonException" then
|
|
202
|
+
Decode.map2
|
|
203
|
+
(\exceptionMessage stackTrace ->
|
|
204
|
+
FatalError.recoverable
|
|
205
|
+
{ title = "Custom BackendTask Error"
|
|
206
|
+
, body =
|
|
207
|
+
[ 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"
|
|
208
|
+
, TerminalText.red exceptionMessage
|
|
209
|
+
, TerminalText.text ("\n\n" ++ (stackTrace |> Maybe.withDefault "\n"))
|
|
210
|
+
, TerminalText.text "\n\nYou could add a `try`/`catch` in your `custom-backend-task` JavaScript code to handle that error."
|
|
211
|
+
]
|
|
212
|
+
|> TerminalText.toString
|
|
213
|
+
}
|
|
214
|
+
(NonJsonException exceptionMessage)
|
|
215
|
+
)
|
|
216
|
+
(Decode.field "error" Decode.string)
|
|
217
|
+
(Decode.field "stack" (Decode.nullable Decode.string))
|
|
218
|
+
|
|
219
|
+
else
|
|
220
|
+
FatalError.recoverable
|
|
221
|
+
{ title = "Custom BackendTask Error"
|
|
222
|
+
, body =
|
|
223
|
+
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.run. I expected to find a port named `"
|
|
224
|
+
, TerminalText.yellow portName
|
|
225
|
+
, TerminalText.text "`."
|
|
226
|
+
]
|
|
227
|
+
|> TerminalText.toString
|
|
228
|
+
}
|
|
229
|
+
ErrorInCustomBackendTaskFile
|
|
230
|
+
|> Decode.succeed
|
|
231
|
+
)
|
|
232
|
+
|> Decode.map Err
|
|
233
|
+
|
|
234
|
+
Nothing ->
|
|
235
|
+
decoder |> Decode.map Ok
|
|
236
|
+
)
|
|
237
|
+
|> BackendTask.Http.expectJson
|
|
238
|
+
}
|
|
239
|
+
|> BackendTask.andThen BackendTask.fromResult
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
{-| -}
|
|
243
|
+
type Error
|
|
244
|
+
= Error
|
|
245
|
+
| ErrorInCustomBackendTaskFile
|
|
246
|
+
| MissingCustomBackendTaskFile
|
|
247
|
+
| CustomBackendTaskNotDefined { name : String }
|
|
248
|
+
| CustomBackendTaskException Decode.Value
|
|
249
|
+
| NonJsonException String
|
|
250
|
+
| ExportIsNotFunction
|
|
251
|
+
| DecodeError Decode.Error
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
{-| -}
|
|
255
|
+
timeDecoder : Decoder Time.Posix
|
|
256
|
+
timeDecoder =
|
|
257
|
+
Decode.field "__elm-pages-normalized__"
|
|
258
|
+
(Decode.field "kind" Decode.string
|
|
259
|
+
|> Decode.andThen
|
|
260
|
+
(\kind ->
|
|
261
|
+
if kind == "Date" then
|
|
262
|
+
Decode.field "value"
|
|
263
|
+
(Decode.int |> Decode.map Time.millisToPosix)
|
|
264
|
+
|
|
265
|
+
else
|
|
266
|
+
Decode.fail <| "I was running BackendTask.Custom.timeDecoder and expecting an elm-pages normalized Object with kind \"Date\", but got kind \"" ++ kind ++ "\"."
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
{-| The same as `timeDecoder`, but it converts the decoded `Time.Posix` value into a `Date` with `Date.fromPosix Time.utc`.
|
|
272
|
+
|
|
273
|
+
JavaScript `Date` objects don't distinguish between values with only a date vs. values with both a date and a time. So be sure
|
|
274
|
+
to use this decoder when you know the semantics represent a date with no associated time (or you're sure you don't care about the time).
|
|
275
|
+
|
|
276
|
+
-}
|
|
277
|
+
dateDecoder : Decoder Date.Date
|
|
278
|
+
dateDecoder =
|
|
279
|
+
Decode.field "__elm-pages-normalized__"
|
|
280
|
+
(Decode.field "kind" Decode.string
|
|
281
|
+
|> Decode.andThen
|
|
282
|
+
(\kind ->
|
|
283
|
+
if kind == "Date" then
|
|
284
|
+
Decode.field "value"
|
|
285
|
+
(Decode.int |> Decode.map (Time.millisToPosix >> Date.fromPosix Time.utc))
|
|
286
|
+
|
|
287
|
+
else
|
|
288
|
+
Decode.fail <| "I was running BackendTask.Custom.dateDecoder and expecting an elm-pages normalized Object with kind \"Date\", but got kind \"" ++ kind ++ "\"."
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
request :
|
|
294
|
+
{ body : BackendTask.Http.Body
|
|
295
|
+
, expect : BackendTask.Http.Expect a
|
|
296
|
+
}
|
|
297
|
+
-> BackendTask { fatal : FatalError, recoverable : Error } a
|
|
298
|
+
request { body, expect } =
|
|
299
|
+
-- elm-review: known-unoptimized-recursion
|
|
300
|
+
BackendTask.Http.request
|
|
301
|
+
{ url = "elm-pages-internal://port"
|
|
302
|
+
, method = "GET"
|
|
303
|
+
, headers = []
|
|
304
|
+
, body = body
|
|
305
|
+
, timeoutInMs = Nothing
|
|
306
|
+
, retries = Nothing
|
|
307
|
+
}
|
|
308
|
+
expect
|
|
309
|
+
|> BackendTask.onError
|
|
310
|
+
(\error ->
|
|
311
|
+
-- TODO avoid crash here, this should be handled as an internal error
|
|
312
|
+
--request params
|
|
313
|
+
case error.recoverable of
|
|
314
|
+
BackendTask.Http.BadBody (Just jsonError) _ ->
|
|
315
|
+
{ recoverable = DecodeError jsonError
|
|
316
|
+
, fatal = error.fatal
|
|
317
|
+
}
|
|
318
|
+
|> BackendTask.fail
|
|
319
|
+
|
|
320
|
+
_ ->
|
|
321
|
+
{ recoverable = Error
|
|
322
|
+
, fatal = error.fatal
|
|
323
|
+
}
|
|
324
|
+
|> BackendTask.fail
|
|
325
|
+
)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
module BackendTask.Env exposing
|
|
2
|
+
( get, expect
|
|
3
|
+
, Error(..)
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
{-| Because BackendTask's in `elm-pages` never run in the browser (see [the BackendTask docs](BackendTask)), you can access environment variables securely. As long as the environment variable isn't sent
|
|
7
|
+
down into the final `Data` value, it won't end up in the client!
|
|
8
|
+
|
|
9
|
+
import BackendTask exposing (BackendTask)
|
|
10
|
+
import BackendTask.Env
|
|
11
|
+
import FatalError exposing (FatalError)
|
|
12
|
+
|
|
13
|
+
type alias EnvVariables =
|
|
14
|
+
{ sendGridKey : String
|
|
15
|
+
, siteUrl : String
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
sendEmail : Email -> BackendTask FatalError ()
|
|
19
|
+
sendEmail email =
|
|
20
|
+
BackendTask.map2 EnvVariables
|
|
21
|
+
(BackendTask.Env.expect "SEND_GRID_KEY" |> BackendTask.allowFatal)
|
|
22
|
+
(BackendTask.Env.get "BASE_URL"
|
|
23
|
+
|> BackendTask.map (Maybe.withDefault "http://localhost:1234")
|
|
24
|
+
)
|
|
25
|
+
|> BackendTask.andThen (sendEmailBackendTask email)
|
|
26
|
+
|
|
27
|
+
sendEmailBackendTask : Email -> EnvVariables -> BackendTask FatalError ()
|
|
28
|
+
sendEmailBackendTask email envVariables =
|
|
29
|
+
Debug.todo "Not defined here"
|
|
30
|
+
|
|
31
|
+
@docs get, expect
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## Errors
|
|
35
|
+
|
|
36
|
+
@docs Error
|
|
37
|
+
|
|
38
|
+
-}
|
|
39
|
+
|
|
40
|
+
import BackendTask exposing (BackendTask)
|
|
41
|
+
import BackendTask.Http
|
|
42
|
+
import BackendTask.Internal.Request
|
|
43
|
+
import FatalError exposing (FatalError)
|
|
44
|
+
import Json.Decode as Decode
|
|
45
|
+
import Json.Encode as Encode
|
|
46
|
+
import TerminalText
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
{-| -}
|
|
50
|
+
type Error
|
|
51
|
+
= MissingEnvVariable String
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
{-| Get an environment variable, or Nothing if there is no environment variable matching that name. This `BackendTask`
|
|
55
|
+
will never fail, but instead will return `Nothing` if the environment variable is missing.
|
|
56
|
+
-}
|
|
57
|
+
get : String -> BackendTask error (Maybe String)
|
|
58
|
+
get envVariableName =
|
|
59
|
+
BackendTask.Internal.Request.request
|
|
60
|
+
{ name = "env"
|
|
61
|
+
, body = BackendTask.Http.jsonBody (Encode.string envVariableName)
|
|
62
|
+
, expect =
|
|
63
|
+
BackendTask.Http.expectJson
|
|
64
|
+
(Decode.nullable Decode.string)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
{-| Get an environment variable, or a BackendTask FatalError if there is no environment variable matching that name.
|
|
69
|
+
-}
|
|
70
|
+
expect : String -> BackendTask { fatal : FatalError, recoverable : Error } String
|
|
71
|
+
expect envVariableName =
|
|
72
|
+
envVariableName
|
|
73
|
+
|> get
|
|
74
|
+
|> BackendTask.andThen
|
|
75
|
+
(\maybeValue ->
|
|
76
|
+
maybeValue
|
|
77
|
+
|> Result.fromMaybe
|
|
78
|
+
(FatalError.recoverable
|
|
79
|
+
{ title = "Missing Env Variable"
|
|
80
|
+
, body =
|
|
81
|
+
[ TerminalText.text "BackendTask.Env.expect was expecting a variable `"
|
|
82
|
+
, TerminalText.yellow envVariableName
|
|
83
|
+
, TerminalText.text "` but couldn't find a variable with that name."
|
|
84
|
+
]
|
|
85
|
+
|> TerminalText.toString
|
|
86
|
+
}
|
|
87
|
+
(MissingEnvVariable envVariableName)
|
|
88
|
+
)
|
|
89
|
+
|> BackendTask.fromResult
|
|
90
|
+
)
|