An elm-pages Script is a way to execute an elm-pages BackendTask.
Read more about using the elm-pages CLI to run (or bundle) scripts, plus a brief tutorial, at https://elm-pages.com/docs/elm-pages-scripts.
Same as withoutCliOptions, but allows you to define a CLI Options Parser so the user can
pass in additional options for the script.
Uses https://package.elm-lang.org/packages/dillonkearns/elm-cli-options-parser/latest/.
Read more at https://elm-pages.com/docs/elm-pages-scripts/#adding-command-line-options.
Define a simple Script (no CLI Options).
module MyScript exposing (run)
import BackendTask
import Pages.Script as Script
run =
Script.withoutCliOptions
(Script.log "Hello!"
|> BackendTask.allowFatal
)
Write a file to the file system. Returns the absolute resolved path to the written file for chaining.
File paths are relative to the root of your elm-pages project (next to the elm.json file and src/ directory), or you can pass in absolute paths beginning with a /.
module MyScript exposing (run)
import BackendTask
import FilePath exposing (FilePath)
import Pages.Script as Script
run =
Script.withoutCliOptions
(Script.writeFile
{ body = """{ "message": "Hello, World!" }""" }
(FilePath.fromString "hello.json")
|> BackendTask.allowFatal
|> BackendTask.andThen
(\writtenPath ->
Script.log ("Wrote " ++ FilePath.toString writtenPath)
)
)
Delete a file. Silently succeeds if the file doesn't exist (like rm -f).
import FilePath exposing (FilePath)
Script.writeFile { body = "..." } (FilePath.fromString "temp.txt")
|> BackendTask.allowFatal
|> BackendTask.andThen (\_ -> Script.deleteFile (FilePath.fromString "temp.txt"))
Copy a single file. Auto-creates parent directories of the destination (matching writeFile behavior).
Returns the absolute resolved destination path for chaining.
import FilePath exposing (FilePath)
FilePath.fromString "src/config.json"
|> Script.copyFile { to = FilePath.fromString "dist/config.json" }
Move (rename) a file or directory. Atomic on the same filesystem. Auto-creates parent directories of the destination. Returns the absolute resolved destination path for chaining.
import FilePath exposing (FilePath)
FilePath.fromString "build/output.js"
|> Script.move { to = FilePath.fromString "dist/app.js" }
Create a directory. Returns the absolute resolved path.
The { recursive : Bool } flag controls whether parent directories are created (like mkdir -p).
import FilePath exposing (FilePath)
-- Create nested directories
Script.makeDirectory { recursive = True } (FilePath.fromString "dist/assets/images")
-- Create a single directory (parent must exist)
Script.makeDirectory { recursive = False } (FilePath.fromString "output")
Remove a directory. Silently succeeds if the directory doesn't exist.
The explicit { recursive : Bool } flag makes dangerous rm -rf behavior opt-in — you must consciously choose
recursive removal of non-empty directories.
import FilePath exposing (FilePath)
-- Remove a directory and all its contents
Script.removeDirectory { recursive = True } (FilePath.fromString "build")
-- Remove only if empty
Script.removeDirectory { recursive = False } (FilePath.fromString "empty-dir")
Create a temporary directory with a given prefix. Returns the absolute path to the created directory.
Pairs naturally with BackendTask.finally for cleanup:
import FilePath exposing (FilePath)
Script.makeTempDirectory "my-build-"
|> BackendTask.andThen
(\tmpDir ->
doWork tmpDir
|> BackendTask.finally
(Script.removeDirectory { recursive = True } tmpDir)
)
Run a single command and return stderr and stdout combined as a single String.
If you want to do more advanced things like piping together multiple commands in a pipeline, or piping in a file to a command, etc., see the Stream module.
module MyScript exposing (run)
import BackendTask
import Pages.Script as Script exposing (Script)
run : Script
run =
Script.withoutCliOptions
(Script.command "ls" []
|> BackendTask.andThen
(\files ->
Script.log ("Files: " ++ files)
)
)
Like command, but prints stderr and stdout to the console as the command runs instead of capturing them.
module MyScript exposing (run)
import BackendTask
import Pages.Script as Script exposing (Script)
run : Script
run =
Script.withoutCliOptions
(Script.exec "ls" [])
Log to stdout.
module MyScript exposing (run)
import BackendTask
import Pages.Script as Script
run =
Script.withoutCliOptions
(Script.log "Hello!"
|> BackendTask.allowFatal
)
Sleep for a number of milliseconds.
module MyScript exposing (run)
import BackendTask
import Pages.Script as Script
run =
Script.withoutCliOptions
(Script.log "Hello..."
|> Script.doThen
(Script.sleep 1000)
|> Script.doThen
(Script.log "World!")
)
Run a command with no output, then run another command.
module MyScript exposing (run)
import BackendTask
import Pages.Script as Script
run =
Script.withoutCliOptions
(Script.log "Hello!"
|> Script.doThen
(Script.log "World!")
)
Same as expectWhich, but returns Nothing if the command is not found instead of failing with a FatalError.
Check if a command is available on the system. If it is, return the full path to the command, otherwise fail with a FatalError.
module MyScript exposing (run)
import BackendTask
import Pages.Script as Script
run : Script
run =
Script.withoutCliOptions
(Script.expectWhich "elm-review"
|> BackendTask.andThen
(\path ->
Script.log ("The path to `elm-review` is: " ++ path)
)
)
If you run it with a command that is not available, you will see an error like this:
Script.expectWhich "hype-script"
-- COMMAND NOT FOUND ---------------
I expected to find `hype-script`, but it was not on your PATH. Make sure it is installed and included in your PATH.
module QuestionDemo exposing (run)
import BackendTask
run : Script
run =
Script.withoutCliOptions
(Script.question "What is your name? "
|> BackendTask.andThen
(\name ->
Script.log ("Hello, " ++ name ++ "!")
)
)
Read a single keypress from stdin without requiring Enter.
This is useful for interactive prompts where you want immediate response to a single key, like confirmation dialogs (y/n) or menu navigation.
module ConfirmDemo exposing (run)
import BackendTask
run : Script
run =
Script.withoutCliOptions
(Script.log "Approve this change? [y/n] "
|> BackendTask.andThen (\_ -> Script.readKey)
|> BackendTask.andThen
(\key ->
if key == "y" then
Script.log "Approved!"
else
Script.log "Rejected."
)
)
Note: Returns the raw key character. Control characters like Ctrl+C will terminate the process.
When not running in an interactive terminal (e.g., piped input or CI),
falls back to line-buffered input and returns the first character of the line.
This allows scripts to work both interactively and with piped input like
echo "y" | elm-pages run MyScript.elm.
Like readKey, but returns a default value when Enter is pressed.
Script.log "Continue? [Y/n] "
|> BackendTask.andThen (\_ -> Script.readKeyWithDefault "y")
|> BackendTask.andThen
(\key ->
if String.toLower key == "y" then
continue
else
abort
)
Useful for prompts where pressing Enter should accept a default option.
The recoverable error type for file writes. You can use BackendTask.allowFatal if you want to allow the program to crash
with an error message if a file write is unsuccessful.
The type for your
runfunction that can be executed byelm-pages run.