Rename BackendTask.Port -> BackendTask.Custom.

This commit is contained in:
Dillon Kearns 2023-01-17 07:04:18 -08:00
parent b773557245
commit ea29d1bbb3
14 changed files with 68 additions and 68 deletions

File diff suppressed because one or more lines are too long

View File

@ -17,7 +17,7 @@
"BackendTask.Glob",
"BackendTask.Http",
"BackendTask.File",
"BackendTask.Port",
"BackendTask.Custom",
"BackendTask.Env",
"Server.Request",
"Server.Session",

File diff suppressed because one or more lines are too long

View File

@ -76,7 +76,7 @@ routes =
(Decode.list (blogPostDecoder |> Decode.map .slug |> Decode.map RouteParams))
```
### BackendTask.Port
### BackendTask.Custom
The core built-in `BackendTask` modules let you pull in
@ -88,12 +88,12 @@ The core built-in `BackendTask` modules let you pull in
If that isn't enough to get you the data you need to pull in to your site, then there's an additional module that lets you build your own custom `BackendTask`.
[`BackendTask.Port`](BackendTask-Port) lets you decode JSON data that you call from custom NodeJS functions. As with any `BackendTask`, you get this data in the build step and then it gets built in to your site, so these NodeJS functions, HTTP requests, file reads, etc. are not happening when a user opens a page in your live site that you built with `elm-pages build`.
[`BackendTask.Custom`](BackendTask-Custom) lets you decode JSON data that you call from custom NodeJS functions. As with any `BackendTask`, you get this data in the build step and then it gets built in to your site, so these NodeJS functions, HTTP requests, file reads, etc. are not happening when a user opens a page in your live site that you built with `elm-pages build`.
```elm
data : BackendTask String
data =
BackendTask.Port.get "environmentVariable"
BackendTask.Custom.get "environmentVariable"
(Json.Encode.string "EDITOR")
Decode.string
```

View File

@ -3,7 +3,7 @@ module Route.Fetcher exposing (ActionData, Data, Model, Msg, RouteParams, route)
{-| -}
import BackendTask exposing (BackendTask)
import BackendTask.Port
import BackendTask.Custom
import Dict
import Effect
import ErrorPage
@ -97,7 +97,7 @@ data :
-> Server.Request.Parser (BackendTask FatalError (Server.Response.Response Data ErrorPage.ErrorPage))
data routeParams =
Server.Request.succeed
(BackendTask.Port.get "getItems"
(BackendTask.Custom.get "getItems"
Encode.null
(Decode.list Decode.string)
|> BackendTask.allowFatal
@ -125,7 +125,7 @@ action routeParams =
(\formPost ->
case formPost of
Ok (AddItem newItem) ->
BackendTask.Port.get "addItem"
BackendTask.Custom.get "addItem"
(Encode.string newItem)
(Decode.list Decode.string)
|> BackendTask.allowFatal
@ -135,7 +135,7 @@ action routeParams =
)
Ok DeleteAll ->
BackendTask.Port.get "deleteAllItems"
BackendTask.Custom.get "deleteAllItems"
Encode.null
(Decode.list Decode.string)
|> BackendTask.allowFatal

View File

@ -1,8 +1,8 @@
module Route.Index exposing (ActionData, Data, Model, Msg, route)
import BackendTask exposing (BackendTask)
import BackendTask.Custom
import BackendTask.File
import BackendTask.Port
import FatalError exposing (FatalError)
import Head
import Head.Seo as Seo
@ -53,7 +53,7 @@ data : BackendTask FatalError Data
data =
BackendTask.map2 Data
(BackendTask.File.rawFile "greeting.txt" |> BackendTask.allowFatal)
(BackendTask.Port.get "hello" (Encode.string "Jane") Decode.string |> BackendTask.allowFatal)
(BackendTask.Custom.get "hello" (Encode.string "Jane") Decode.string |> BackendTask.allowFatal)
head :

View File

@ -2,7 +2,7 @@ module Route.Feed__ exposing (ActionData, Data, Model, Msg, route)
import BackendTask exposing (BackendTask)
import BackendTask.Http
import BackendTask.Port
import BackendTask.Custom
import Effect exposing (Effect)
import ErrorPage exposing (ErrorPage)
import Head

View File

@ -1,7 +1,7 @@
module Route.PortTest exposing (ActionData, Data, Model, Msg, route)
import BackendTask exposing (BackendTask)
import BackendTask.Port
import BackendTask.Custom
import FatalError exposing (FatalError)
import Head
import Head.Seo as Seo
@ -49,7 +49,7 @@ type alias ActionData =
data : BackendTask FatalError Data
data =
BackendTask.succeed Data
|> BackendTask.andMap (BackendTask.Port.get "hello" (Encode.string "Jane") Decode.string |> BackendTask.allowFatal)
|> BackendTask.andMap (BackendTask.Custom.get "hello" (Encode.string "Jane") Decode.string |> BackendTask.allowFatal)
head :

View File

@ -3,7 +3,7 @@ module Route.Login exposing (ActionData, Data, Model, Msg, route)
import Api.Scalar exposing (Uuid(..))
import Data.User
import BackendTask exposing (BackendTask)
import BackendTask.Port
import BackendTask.Custom
import Dict exposing (Dict)
import ErrorPage exposing (ErrorPage)
import Form
@ -99,7 +99,7 @@ form =
attemptLogIn : String -> String -> BackendTask (Maybe Uuid)
attemptLogIn username password =
BackendTask.Port.get "hashPassword"
BackendTask.Custom.get "hashPassword"
(Json.Encode.string password)
Json.Decode.string
|> BackendTask.andThen

View File

@ -2,9 +2,9 @@ module Route.Login exposing (ActionData, Data, Model, Msg, route)
import Api.Scalar exposing (Uuid(..))
import BackendTask exposing (BackendTask)
import BackendTask.Custom
import BackendTask.Env
import BackendTask.Http
import BackendTask.Port
import Data.Session
import Dict exposing (Dict)
import EmailAddress exposing (EmailAddress)
@ -61,7 +61,7 @@ route =
now : BackendTask Time.Posix
now =
BackendTask.Port.get "now"
BackendTask.Custom.get "now"
Encode.null
(Decode.int |> Decode.map Time.millisToPosix)
@ -71,7 +71,7 @@ emailToMagicLink email baseUrl =
now
|> BackendTask.andThen
(\now_ ->
BackendTask.Port.get "encrypt"
BackendTask.Custom.get "encrypt"
(Encode.object
[ ( "text", Encode.string (EmailAddress.toString email) )
, ( "expiresAt", (Time.posixToMillis now_ + (1000 * 60 * 30)) |> Encode.int )
@ -497,7 +497,7 @@ sendEmail apiKey_ email_ =
parseMagicHash : String -> BackendTask ( String, Time.Posix )
parseMagicHash magicHash =
BackendTask.Port.get "decrypt"
BackendTask.Custom.get "decrypt"
(Encode.string magicHash)
(Decode.string
|> Decode.map
@ -533,6 +533,6 @@ parseMagicHashIfNotExpired magicHash =
log : String -> BackendTask ()
log message =
BackendTask.Port.get "log"
BackendTask.Custom.get "log"
(Encode.string message)
(Decode.succeed ())

View File

@ -43,21 +43,21 @@ function lookupOrPerform(portsFile, mode, rawRequest, hasFsAccess, useCache) {
resolve({
kind: "response-json",
value: jsonResponse({
"elm-pages-internal-error": "PortNotDefined",
"elm-pages-internal-error": "CustomBackendTaskNotDefined",
}),
});
} else if (portBackendTaskImportError === "missing") {
resolve({
kind: "response-json",
value: jsonResponse({
"elm-pages-internal-error": "MissingPortsFile",
"elm-pages-internal-error": "MissingCustomBackendTaskFile",
}),
});
} else {
resolve({
kind: "response-json",
value: jsonResponse({
"elm-pages-internal-error": "ErrorInPortsFile",
"elm-pages-internal-error": "ErrorInCustomBackendTaskFile",
error:
(portBackendTaskImportError &&
portBackendTaskImportError.stack) ||
@ -83,7 +83,7 @@ function lookupOrPerform(portsFile, mode, rawRequest, hasFsAccess, useCache) {
resolve({
kind: "response-json",
value: jsonResponse({
"elm-pages-internal-error": "PortCallException",
"elm-pages-internal-error": "CustomBackendTaskException",
error: portCallError,
}),
});
@ -92,7 +92,7 @@ function lookupOrPerform(portsFile, mode, rawRequest, hasFsAccess, useCache) {
} catch (error) {
console.trace(error);
reject({
title: "BackendTask.Port Error",
title: "BackendTask.Custom Error",
message: error.toString(),
});
}

View File

@ -1,7 +1,7 @@
module Timestamps exposing (Timestamps, data, format)
import BackendTask exposing (BackendTask)
import BackendTask.Port
import BackendTask.Custom
import DateFormat
import Json.Decode as Decode
import Json.Decode.Extra
@ -19,7 +19,7 @@ type alias Timestamps =
data : String -> BackendTask Timestamps
data filePath =
BackendTask.Port.get "gitTimestamps"
BackendTask.Custom.get "gitTimestamps"
(Json.Encode.string filePath)
(Decode.string
|> Decode.map (String.trim >> String.split "\n")

View File

@ -16,7 +16,7 @@ A `BackendTask` lets you pull in data from:
- Local files ([`BackendTask.File`](BackendTask-File))
- HTTP requests ([`BackendTask.Http`](BackendTask-Http))
- Globs, i.e. listing out local files based on a pattern like `content/*.txt` ([`BackendTask.Glob`](BackendTask-Glob))
- Ports, i.e. getting JSON data from running custom NodeJS, similar to a port in a vanilla Elm app except run at build-time in NodeJS, rather than at run-time in the browser ([`BackendTask.Port`](BackendTask-Port))
- Ports, i.e. getting JSON data from running custom NodeJS, similar to a port in a vanilla Elm app except run at build-time in NodeJS, rather than at run-time in the browser ([`BackendTask.Custom`](BackendTask-Custom))
- Hardcoded data (`BackendTask.succeed "Hello!"`)
- Or any combination of the above, using `BackendTask.map2`, `BackendTask.andThen`, or other combining/continuing helpers from this module

View File

@ -1,30 +1,30 @@
module BackendTask.Port exposing
module BackendTask.Custom exposing
( get
, 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
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
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.
A `BackendTask.Custom` 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
Here is the Elm code and corresponding JavaScript definition for getting an environment variable (or an `FatalError BackendTask.Port.Error` if it isn't found). In this example,
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,
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)).
import BackendTask exposing (BackendTask)
import BackendTask.Port
import BackendTask.Custom
import Json.Encode
import OptimizedDecoder as Decode
data : BackendTask FatalError String
data =
BackendTask.Port.get "environmentVariable"
BackendTask.Custom.get "environmentVariable"
(Json.Encode.string "EDITOR")
Decode.string
|> BackendTask.allowFatal
@ -60,18 +60,18 @@ ${Object.keys(process.env).join("\n")}
## 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. `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.
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`,
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.
## 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.
There are a few different things that can go wrong when running a port-data-source. These possible errors are captured in the `BackendTask.Custom.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.
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 `port-data-source.js`
to handle possible errors, but you can throw a JSON value and handle it in Elm in the `CustomBackendTaskException` call error.
-}
@ -104,17 +104,17 @@ get portName input decoder =
[ Decode.field "elm-pages-internal-error" Decode.string
|> Decode.andThen
(\errorKind ->
if errorKind == "PortNotDefined" then
if errorKind == "CustomBackendTaskNotDefined" then
FatalError.recoverable
{ title = "Port Error"
{ title = "Custom BackendTask Error"
, body =
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I expected to find a port named `"
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.get. I expected to find a port named `"
, TerminalText.yellow portName
, TerminalText.text "` but I couldn't find it. Is the function exported in your port-data-source file?"
]
|> TerminalText.toString
}
(PortNotDefined { name = portName })
(CustomBackendTaskNotDefined { name = portName })
|> Decode.succeed
else if errorKind == "ExportIsNotFunction" then
@ -122,78 +122,78 @@ get portName input decoder =
|> Decode.maybe
|> Decode.map (Maybe.withDefault "")
|> Decode.map
(\incorrectPortType ->
(\incorrectType ->
FatalError.recoverable
{ title = "Port Error"
{ title = "Custom BackendTask Error"
, body =
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I found an export called `"
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.get. I found an export called `"
, TerminalText.yellow portName
, TerminalText.text "` but I expected its type to be function, but instead its type was: "
, TerminalText.red incorrectPortType
, TerminalText.red incorrectType
]
|> TerminalText.toString
}
ExportIsNotFunction
)
else if errorKind == "MissingPortsFile" then
else if errorKind == "MissingCustomBackendTaskFile" then
FatalError.recoverable
{ title = "Port Error"
{ title = "Custom BackendTask Error"
, body =
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I couldn't find your port-data-source file. Be sure to create a 'port-data-source.ts' or 'port-data-source.js' file."
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.get. I couldn't find your port-data-source file. Be sure to create a 'port-data-source.ts' or 'port-data-source.js' file."
]
|> TerminalText.toString
}
MissingPortsFile
MissingCustomBackendTaskFile
|> Decode.succeed
else if errorKind == "ErrorInPortsFile" then
else if errorKind == "ErrorInCustomBackendTaskFile" then
Decode.field "error" Decode.string
|> Decode.maybe
|> Decode.map (Maybe.withDefault "")
|> Decode.map
(\errorMessage ->
FatalError.recoverable
{ title = "Port Error"
{ title = "Custom BackendTask 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.Custom.get. I couldn't import the port definitions file, because of this exception:\n\n"
, TerminalText.red errorMessage
, TerminalText.text "\n\nAre there syntax errors or exceptions thrown during import?"
]
|> TerminalText.toString
}
ErrorInPortsFile
ErrorInCustomBackendTaskFile
)
else if errorKind == "PortCallException" then
else if errorKind == "CustomBackendTaskException" then
Decode.field "error" Decode.value
|> Decode.maybe
|> Decode.map (Maybe.withDefault Encode.null)
|> Decode.map
(\portCallError ->
FatalError.recoverable
{ title = "Port Error"
{ title = "Custom BackendTask Error"
, body =
[ 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.text "Something went wrong in a call to BackendTask.Custom.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\nYou could add a `try`/`catch` in your `port-data-source` JavaScript code to handle that error."
]
|> TerminalText.toString
}
(PortCallException portCallError)
(CustomBackendTaskException portCallError)
)
else
FatalError.recoverable
{ title = "Port Error"
{ title = "Custom BackendTask Error"
, body =
[ TerminalText.text "Something went wrong in a call to BackendTask.Port.get. I expected to find a port named `"
[ TerminalText.text "Something went wrong in a call to BackendTask.Custom.get. I expected to find a port named `"
, TerminalText.yellow portName
, TerminalText.text "`."
]
|> TerminalText.toString
}
ErrorInPortsFile
ErrorInCustomBackendTaskFile
|> Decode.succeed
)
|> Decode.map Err
@ -207,8 +207,8 @@ get portName input decoder =
{-| -}
type Error
= Error
| ErrorInPortsFile
| MissingPortsFile
| PortNotDefined { name : String }
| PortCallException Decode.Value
| ErrorInCustomBackendTaskFile
| MissingCustomBackendTaskFile
| CustomBackendTaskNotDefined { name : String }
| CustomBackendTaskException Decode.Value
| ExportIsNotFunction