Update docs and rename a BackendTask.Port Error variant.

This commit is contained in:
Dillon Kearns 2023-01-13 10:03:39 -08:00
parent 5ab296f91c
commit be32cbd2be
3 changed files with 48 additions and 45 deletions

File diff suppressed because one or more lines are too long

View File

@ -83,7 +83,7 @@ function lookupOrPerform(portsFile, mode, rawRequest, hasFsAccess, useCache) {
resolve({
kind: "response-json",
value: jsonResponse({
"elm-pages-internal-error": "PortCallError",
"elm-pages-internal-error": "PortCallException",
error: portCallError,
}),
});

View File

@ -3,30 +3,19 @@ module BackendTask.Port exposing
, Error(..)
)
{-|
{-| 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.
With `BackendTask.Port`, you send and receive JSON to JavaScript running in NodeJS. As with any `BackendTask`, Port 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
lifecycle of `BackendTask`'s.
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.
A `BackendTask.Port` will call an async JavaScript function with the given name from the definition in a file called `port-data-source.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.
@docs get
@docs Error
-}
import BackendTask
import BackendTask.Http
import BackendTask.Internal.Request
import Exception exposing (Exception)
import Json.Decode as Decode exposing (Decoder)
import Json.Encode as Encode
import TerminalText
{-| 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.
With `BackendTask.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.
A `BackendTask.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.
Here is the Elm code and corresponding JavaScript definition for getting an environment variable (or a build error if it isn't found).
Here is the Elm code and corresponding JavaScript definition for getting an environment variable (or an `Exception BackendTask.Port.Error` if it isn't found). In this example,
we're using `BackendTask.throw` to let the framework treat that as an unexpected exception, but we could also handle the possible failures of the `Exception` (see [`Exception`](Exception)).
import BackendTask exposing (BackendTask)
import BackendTask.Port
@ -43,8 +32,7 @@ Here is the Elm code and corresponding JavaScript definition for getting an envi
-- will resolve to "VIM" if you run `EDITOR=vim elm-pages dev`
```javascript
const kleur = require("kleur");
// port-data-source.js
module.exports =
/**
@ -57,31 +45,46 @@ module.exports =
if (result) {
return result;
} else {
throw `No environment variable called ${kleur
.yellow()
.underline(name)}\n\nAvailable:\n\n${Object.keys(process.env).join(
"\n"
)}`;
throw `No environment variable called ${name}
Available:
${Object.keys(process.env).join("\n")}
`;
}
},
}
```
## Error Handling
`port-data-source.js`
Any time you throw an exception from a BackendTask.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
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
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.
## Performance
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.
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.
So if you do `BackendTask.map2 Tuple.pair myHttpBackendTask myPortBackendTask`, 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`,
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 Port definitions.
## Error Handling
There are a few different things that can go wrong when running a port-data-source. These possible errors are captured in the `BackendTask.Port.Error` type.
@docs Error
Any time you throw a JavaScript exception from a BackendTask.Port definition, it will give you a `PortCallException`. It's usually easier to add a `try`/`catch` in your JavaScript code in `port-data-source.js`
to handle possible errors, but you can throw a JSON value and handle it in Elm in the `PortCallException` call error.
-}
import BackendTask
import BackendTask.Http
import BackendTask.Internal.Request
import Exception exposing (Exception)
import Json.Decode as Decode exposing (Decoder)
import Json.Encode as Encode
import TerminalText
{-| -}
get : String -> Encode.Value -> Decoder b -> BackendTask.BackendTask (Exception Error) b
get portName input decoder =
BackendTask.Internal.Request.request
@ -155,19 +158,19 @@ get portName input decoder =
}
)
else if errorKind == "PortCallError" then
else if errorKind == "PortCallException" then
Decode.field "error" Decode.value
|> Decode.maybe
|> Decode.map (Maybe.withDefault Encode.null)
|> Decode.map
(\portCallError ->
Exception.Exception
(PortCallError portCallError)
(PortCallException portCallError)
{ title = "Port Error"
, body =
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I couldn't import the port definitions file, because of this exception:\n\n"
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I was able to import the port definitions file, but when running it I encountered this exception:\n\n"
, TerminalText.red (Encode.encode 2 portCallError)
, TerminalText.text "\n\nAre there syntax errors or exceptions thrown during import?"
, TerminalText.text "\n\nYou could add a `try`/`catch` in your `port-data-source` JavaScript code to handle that error."
]
|> TerminalText.toString
}
@ -199,5 +202,5 @@ type Error
| ErrorInPortsFile
| MissingPortsFile
| PortNotDefined { name : String }
| PortCallError Decode.Value
| PortCallException Decode.Value
| ExportIsNotFunction