On this page
- Description
- Running a task only if it exists
- Specifying the current working directory
- Getting directory deno task was run from
- Wildcard matching of tasks
- Loading environment variables from a file
- Task dependencies
- Caching task results
- Node and npx binary support
- Workspace support
- Syntax
- Built-in commands
- package.json support
- Command Resolution
- Options
- Dependency management options
deno task
Description Jump to heading
deno task provides a cross-platform way to define and execute custom commands
specific to a codebase.
To get started, define your commands in your codebase's
Deno configuration file under a
"tasks" key.
For example:
{
"tasks": {
"data": "deno task collect && deno task analyze",
"collect": "deno run --allow-read=. --allow-write=. scripts/collect.js",
"analyze": {
"description": "Run analysis script",
"command": "deno run --allow-read=. scripts/analyze.js"
}
}
}
Running a task only if it exists Jump to heading
deno task <name> exits with a non-zero code when the named task is not
defined. To make a task optional, pass --if-present. Deno then exits with code
0 and prints nothing when the task is missing, which is useful for shared CI
scripts that call a task only some packages define:
deno task --if-present build
The flag only suppresses the not-found case for the named task. Listing tasks
with a bare deno task, running a task that does exist, and real errors such as
a missing task dependency are unaffected.
Specifying the current working directory Jump to heading
By default, deno task executes commands with the directory of the Deno
configuration file (ex. deno.json) as the current working directory. This
allows tasks to use relative paths and continue to work regardless of where in
the directory tree you happen to execute the deno task from. In some scenarios,
this may not be desired and this behavior can be overridden with the INIT_CWD
environment variable.
INIT_CWD will be set with the full path to the directory the task was run in,
if not already set. This aligns with the same behavior as npm run.
For example, the following task will change the current working directory of the
task to be in the same directory the user ran the task from and then output the
current working directory which is now that directory (remember, this works on
Windows too because deno task is cross-platform).
{
"tasks": {
"my_task": "cd $INIT_CWD && pwd"
}
}
Getting directory deno task was run from Jump to heading
Since tasks are run using the directory of the Deno configuration file as the
current working directory, it may be useful to know the directory the
deno task was executed from instead. This is possible by using the INIT_CWD
environment variable in a task or script launched from deno task (works the
same way as in npm run, but in a cross-platform way).
For example, to provide this directory to a script in a task, do the following (note the directory is surrounded in double quotes to keep it as a single argument in case it contains spaces):
{
"tasks": {
"start": "deno run main.ts \"$INIT_CWD\""
}
}
Wildcard matching of tasks Jump to heading
The deno task command can run multiple tasks in parallel by passing a wildcard
pattern. A wildcard pattern is specified with the * character.
{
"tasks": {
"build:client": "deno run -RW client/build.ts",
"build:server": "deno run -RW server/build.ts"
}
}
Running deno task "build:*" will run both build:client and build:server
tasks.
For multi-word task names, we recommend using : as the separator (e.g.
build:client, test:unit, lint:fix) to match the convention used in the npm
ecosystem and to group related tasks for wildcard matching.
When using a wildcard make sure to quote the task name (eg. "build:*"),
otherwise your shell might try to expand the wildcard character, leading to
surprising errors.
Exclude tasks from a wildcard match by adding an exclusion group (!a|b|c) to
the end of the pattern. Each listed value is matched against what the *
captured. For example, given test:unit, test:integration, test:e2e, and
test:interactive tasks:
deno task "test:*(!e2e|interactive)"
runs test:unit and test:integration but skips test:e2e and
test:interactive. A pattern that has an exclusion group but no * is
rejected, since there is nothing to exclude from.
Loading environment variables from a file Jump to heading
Pass --env-file to load variables from a dotenv file into the task's shell
environment, so every command in the task body inherits them:
# Load .env
deno task --env-file start
# Load a specific file
deno task --env-file=.env.production start
The flag can be given more than once to load multiple files, with later files
taking precedence. With no value it defaults to .env.
Task dependencies Jump to heading
You can specify dependencies for a task:
{
"tasks": {
"build": "deno run -RW build.ts",
"generate": "deno run -RW generate.ts",
"serve": {
"command": "deno run -RN server.ts",
"dependencies": ["build", "generate"]
}
}
}
In the above example, running deno task serve will first execute build and
generate tasks in parallel, and once both of them finish successfully the
serve task will be executed:
deno task serve
Task build deno run -RW build.ts
Task generate deno run -RW generate.ts
Generating data...
Starting the build...
Build finished
Data generated
Task serve deno run -RN server.ts
Listening on http://localhost:8000/
Dependency tasks are executed in parallel, with the default parallel limit being
equal to number of cores on your machine. To change this limit for a single
invocation, pass --jobs (short -j, also spelled --concurrency); to set it
for the environment, use the DENO_JOBS environment variable. The flag takes
precedence:
# Run workspace tasks fully sequentially
deno task --recursive --jobs 1 build
When tasks run in parallel, each output line is prefixed with the task name that
produced it (color-coded per task). Prefixes stay attached even when a task
forks subprocesses, so a parallel build + test + lint run stays legible
without an external multiplexer.
Dependencies are tracked and if multiple tasks depend on the same task, that task will only be run once:
{
// a
// / \
// b c
// \ /
// d
"tasks": {
"a": {
"command": "deno run a.js",
"dependencies": ["b", "c"]
},
"b": {
"command": "deno run b.js",
"dependencies": ["d"]
},
"c": {
"command": "deno run c.js",
"dependencies": ["d"]
},
"d": "deno run d.js"
}
}
deno task a
Task d deno run d.js
Running d
Task c deno run c.js
Running c
Task b deno run b.js
Running b
Task a deno run a.js
Running a
If a cycle between dependencies is discovered, an error will be returned:
{
"tasks": {
"a": {
"command": "deno run a.js",
"dependencies": ["b"]
},
"b": {
"command": "deno run b.js",
"dependencies": ["a"]
}
}
}
deno task a
Task cycle detected: a -> b -> a
You can also specify a task that has dependencies but no command. This is
useful to logically group several tasks together:
{
"tasks": {
"dev:client": "deno run --watch client/mod.ts",
"dev:server": "deno run --watch server/mod.ts",
"dev": {
"dependencies": ["dev:client", "dev:server"]
}
}
}
Running deno task dev will run both dev:client and dev:server in parallel.
Caching task results Jump to heading
A task can skip work when none of its inputs have changed. Add a files field
listing the input globs the task reads, and Deno fingerprints the command, its
appended arguments, the contents of the matching files, and the values of any
listed env vars, then skips the task on the next run when none of them changed.
Caching is opt-in: a task with no files field always runs.
{
"tasks": {
"build": {
"command": "deno run -RW build.ts",
"files": ["src/**/*.ts", "deno.json"],
"output": ["dist/"],
"env": ["NODE_ENV"]
}
}
}
fileslists the input globs that make up the cache key. Declaring them is what turns on caching for the task.outputlists the globs the task produces. On a cache hit they are restored from the cache, so deletingdist/and re-running regenerates it.envlists environment variable names whose values are part of the cache key, so the task re-runs when one of them changes.
A task's dependency fingerprints are folded into its own cache key, so a task re-runs whenever an upstream one did.
Node and npx binary support Jump to heading
By default, deno task will execute commands with the deno binary. If you
need to ensure that a command is run with the npm or npx binary, you can do
so by invoking the npm or npx run command respectively. For example:
{
"tasks": {
"test:node": "npm run test"
}
}
Workspace support Jump to heading
deno task can be used in workspaces, to run tasks from multiple member
directories in parallel. To execute dev tasks from all workspace members use
--recursive flag:
{
"workspace": [
"client",
"server"
]
}
{
"name": "@scope/client",
"tasks": {
"dev": "deno run -RN build.ts"
}
}
{
"name": "@scope/server",
"tasks": {
"dev": "deno run -RN server.ts"
}
}
deno task --recursive dev
Task dev deno run -RN build.ts
Task dev deno run -RN server.ts
Bundling project...
Listening on http://localhost:8000/
Project bundled
Tasks to run can be filtered based on the workspace members:
deno task --filter "client" dev
Task dev deno run -RN build.ts
Bundling project...
Project bundled
Note that the filter matches against the workspace member names as specified in
the name field of each member's
deno.json file.
Syntax Jump to heading
deno task uses a cross-platform shell that's a subset of sh/bash to execute
defined tasks.
Boolean lists Jump to heading
Boolean lists provide a way to execute additional commands based on the exit
code of the initial command. They separate commands using the && and ||
operators.
The && operator provides a way to execute a command and if it succeeds (has
an exit code of 0) it will execute the next command:
deno run --allow-read=. --allow-write=. collect.ts && deno run --allow-read=. analyze.ts
The || operator is the opposite. It provides a way to execute a command and
only if it fails (has a non-zero exit code) it will execute the next command:
deno run --allow-read=. --allow-write=. collect.ts || deno run play_sad_music.ts
Sequential lists Jump to heading
Sequential lists are similar to boolean lists, but execute regardless of whether
the previous command in the list passed or failed. Commands are separated with a
semi-colon (;).
deno run output_data.ts ; deno run --allow-net server.ts
Async commands Jump to heading
Async commands provide a way to make a command execute asynchronously. This can
be useful when starting multiple processes. To make a command asynchronous, add
an & to the end of it. For example the following would execute
sleep 1 && deno run --allow-net server.ts and deno run --allow-net client.ts
at the same time:
sleep 1 && deno run --allow-net server.ts & deno run --allow-net client.ts
Unlike in most shells, the first async command to fail will cause all the other
commands to fail immediately. In the example above, this would mean that if the
server command fails then the client command will also fail and exit. You can
opt out of this behavior by adding || true to the end of a command, which will
force a 0 exit code. For example:
deno run --allow-net server.ts || true & deno run --allow-net client.ts || true
Environment variables Jump to heading
Environment variables are defined like the following:
export VAR_NAME=value
Here's an example of using one in a task with shell variable substitution and then with it being exported as part of the environment of the spawned Deno process (note that in the JSON configuration file the double quotes would need to be escaped with backslashes):
export VAR=hello && echo $VAR && deno eval "console.log('Deno: ' + Deno.env.get('VAR'))"
Would output:
hello
Deno: hello
Setting environment variables for a command Jump to heading
To specify environment variable(s) before a command, list them like so:
VAR=hello VAR2=bye deno run main.ts
This will use those environment variables specifically for the following command.
Shell variables Jump to heading
Shell variables are similar to environment variables, but won't be exported to spawned commands. They are defined with the following syntax:
VAR_NAME=value
If we use a shell variable instead of an environment variable in a similar example to what's shown in the previous "Environment variables" section:
VAR=hello && echo $VAR && deno eval "console.log('Deno: ' + Deno.env.get('VAR'))"
We will get the following output:
hello
Deno: undefined
Shell variables can be useful when we want to reuse a value, but don't want it available in any spawned processes.
Exit status variable Jump to heading
The exit code of the previously run command is available in the $? variable.
# outputs 10
deno eval 'Deno.exit(10)' || echo $?
Pipelines Jump to heading
Pipelines provide a way to pipe the output of one command to another.
The following command pipes the stdout output "Hello" to the stdin of the spawned Deno process:
echo Hello | deno run main.ts
To pipe stdout and stderr, use |& instead:
deno eval 'console.log(1); console.error(2);' |& deno run main.ts
Command substitution Jump to heading
The $(command) syntax provides a way to use the output of a command in other
commands that get executed.
For example, to provide the output of getting the latest git revision to another command you could do the following:
deno run main.ts $(git rev-parse HEAD)
Another example using a shell variable:
REV=$(git rev-parse HEAD) && deno run main.ts $REV && echo $REV
Negate exit code Jump to heading
To negate the exit code, add an exclamation point and space before a command:
# change the exit code from 1 to 0
! deno eval 'Deno.exit(1);'
Redirects Jump to heading
Redirects provide a way to pipe stdout and/or stderr to a file.
For example, the following redirects stdout of deno run main.ts to a file
called file.txt on the file system:
deno run main.ts > file.txt
To instead redirect stderr, use 2>:
deno run main.ts 2> file.txt
To redirect both stdout and stderr, use &>:
deno run main.ts &> file.txt
To append to a file, instead of overwriting an existing one, use two right angle brackets instead of one:
deno run main.ts >> file.txt
Suppressing either stdout, stderr, or both of a command is possible by
redirecting to /dev/null. This works in a cross-platform way including on
Windows.
# suppress stdout
deno run main.ts > /dev/null
# suppress stderr
deno run main.ts 2> /dev/null
# suppress both stdout and stderr
deno run main.ts &> /dev/null
Or redirecting stdout to stderr and vice-versa:
# redirect stdout to stderr
deno run main.ts >&2
# redirect stderr to stdout
deno run main.ts 2>&1
Input redirects are also supported:
# redirect file.txt to the stdin of gzip
gzip < file.txt
Note that redirecting multiple redirects is currently not supported.
Cross-platform shebang Jump to heading
Starting in Deno 1.42, deno task will execute scripts that start with
#!/usr/bin/env -S the same way on all platforms.
For example:
#!/usr/bin/env -S deno run
console.log("Hello there!");
{
"tasks": {
"hi": "./script.ts"
}
}
Then on a Windows machine:
> pwd
C:\Users\david\dev\my_project
> deno task hi
Hello there!
Glob expansion Jump to heading
Glob expansion is supported in Deno 1.34 and above. This allows for specifying globs to match files in a cross-platform way.
# match .ts files in the current and descendant directories
echo **/*.ts
# match .ts files in the current directory
echo *.ts
# match files that start with "data", have a single number, then end with .csv
echo data[0-9].csv
The supported glob characters are *, ?, and [/].
Because globstar is enabled by default (see Shell options),
** recurses into all descendant directories, including node_modules.
This differs from some interactive shells where ** is not recursive unless
explicitly enabled, so a task like deno check **/*.ts can expand to far more
files than the same command typed in your terminal, sweeping in dependency
files. That can lead to errors such as Argument list too long or type-checking
files you didn't intend to.
For deno check and deno fmt, prefer running them without glob arguments and
using the exclude option
in your deno.json (node_modules is excluded by default), or disable
recursion for the task with shopt -u globstar.
Shell options Jump to heading
deno task supports shell options in Deno 2.6.6 and above to control glob
expansion and pipeline behavior. By default, failglob and globstar are
enabled.
- failglob - When enabled, globs that don't match any files will cause an
error. Disable with
shopt -u failglob. - globstar - When enabled,
**matches zero or more directories. Disable withshopt -u globstar. - nullglob - When enabled, globs that don't match any files expand to
nothing instead of the literal glob pattern. Enable with
shopt -s nullglob. - pipefail - When enabled, the exit code of a pipeline is the exit code of
the last command to exit with a non-zero status, or zero if all commands exit
successfully. Enable with
set -o pipefail. - errexit (Deno 2.8+) - When enabled, a sequential list aborts on the first
command that exits non-zero. Enable with
set -eorset -o errexit; disable again withset +eorset +o errexit. Useful when porting a shell script that relies onset -esemantics into atasksblock.
Examples:
{
"tasks": {
// disable failglob
"task1": "shopt -u failglob && rm -rf *.ts",
// disable failglob and enable nullglob
"task2": "shopt -u failglob && shopt -s nullglob && rm -rf *.ts",
// disable globstar
"task3": "shopt -u globstar && echo **/*.ts",
// enable pipefail
"task4": "set -o pipefail && cat missing.txt | echo 'hello'",
// abort the sequential list on the first failing command
"task5": "set -e; build_step_one; build_step_two; build_step_three"
}
}
Shell options do not propagate to deno task subprocesses. Each deno task
invocation starts with the default options.
Built-in commands Jump to heading
deno task ships with several built-in commands that work the same out of the
box on Windows, Mac, and Linux.
cp- Copies files.mv- Moves files.rm- Remove files or directories.- Ex:
rm -rf [FILE]...- Commonly used to recursively delete files or directories.
- Ex:
mkdir- Makes directories.- Ex.
mkdir -p DIRECTORY...- Commonly used to make a directory and all its parents with no error if it exists.
- Ex.
pwd- Prints the name of the current/working directory.sleep- Delays for a specified amount of time.- Ex.
sleep 1to sleep for 1 second,sleep 0.5to sleep for half a second, orsleep 1mto sleep a minute
- Ex.
echo- Displays a line of text.cat- Concatenates files and outputs them on stdout. When no arguments are provided it reads and outputs stdin.exit- Causes the shell to exit.head- Output the first part of a file.export- Sets environment variables and exports them to spawned commands.unset- Unsets environment variables.xargs- Builds arguments from stdin and executes a command.:- The POSIX null command. Does nothing and always exits with status0(Deno 2.8+). Handy as a no-op placeholder in conditionals or for parameter-expansion side effects.
If you find a useful flag missing on a command or have any suggestions for additional commands that should be supported out of the box, then please open an issue on the deno_task_shell repo.
Note that if you wish to execute any of these commands in a non-cross-platform
way on Mac or Linux, then you may do so by running it through sh:
sh -c <command> (ex. sh -c cp source destination).
package.json support Jump to heading
deno task falls back to reading from the "scripts" entries in a package.json
file if it is discovered. Note that Deno does not respect or support any npm
life cycle events like preinstall or postinstall—you must explicitly run the
script entries you want to run (ex.
deno install --entrypoint main.ts && deno task postinstall).
When deno task runs a package.json script, it sets the npm_* environment
variables that npm exposes, so scripts that read them keep working. These
include npm_package_name, npm_package_version, npm_lifecycle_event (the
script name), npm_lifecycle_script (its command string), and
npm_config_user_agent, along with npm_execpath and npm_node_execpath (both
set to the path of the running deno executable) and npm_command (set to
run-script). These variables are set only for package.json scripts. Tasks
defined in deno.json do not receive them.
Command Resolution Jump to heading
When a task command references a binary (e.g., ohm, tsc, eslint), Deno
resolves it using the following order:
-
node_modules/.bin/- if the task's directory or a parent directory has anode_modules/.bin/folder, Deno looks there first. Note thatdeno add npm:<pkg>updatesdeno.jsonimports anddeno.lockbut does not create anode_modulesdirectory. Thenode_modulesdirectory is only created when usingdeno installor other npm-compatible tooling. -
package.jsonbinfield - when a dependency defines abinfield in itspackage.json, Deno automatically makes those commands available within task scripts through its npm compatibility layer. -
System
PATH- if the command is not found above, Deno falls back to searching the systemPATH.
Example Jump to heading
Given a package.json with:
{
"dependencies": {
"@ohm-js/cli": "^2.0.0"
}
}
And a deno.json with:
{
"tasks": {
"generate": "ohm src/grammar.ohm -o src/grammar.js"
}
}
Running deno task generate will:
- Look for
ohminnode_modules/.bin/ohm - If found, execute it using Deno's Node.js compatibility layer
- The command runs under Deno's runtime, not Node.js.
deno task [OPTIONS] [TASK]Run a task defined in the configuration file:
deno task build
List all available tasks (from config files in the current and ancestor directories):
deno task
Evaluate a task from string:
deno task --eval "echo $(pwd)"
Options Jump to heading
Configure different aspects of deno including TypeScript, linting, and code formatting.
Typically the configuration file will be called deno.json or deno.jsonc and
automatically detected; in that case this flag is not necessary.
--cwd<DIR>Specify the directory to run the task in.
--env-file<FILE>optionalLoad environment variables from local file Only the first environment variable with a given key is used. Existing process environment variables are not overwritten, so if variables with the same names already exist in the environment, their values will be preserved. Where multiple declarations for the same environment variable exist in your .env file, the first one encountered is applied. This is determined by the order of the files you pass as arguments.
--evalEvaluate the passed value as if it was a task in a configuration file.
--filter, -f<filter>Filter members of the workspace by name, implies --recursive flag.
--if-presentExit with code 0 instead of an error when the task is not found.
--jobs, -j<NUMBER>Maximum number of tasks to run concurrently. Overrides the DENO_JOBS environment variable; defaults to the number of available CPUs. Use 1 to force sequential execution. Only affects runs where multiple tasks can run concurrently (workspace runs, or a task with parallelizable dependencies).
--no-prefixDisable prefixing the output of concurrently-executing tasks with the task name.
--recursive, -rRun the task in all projects in the workspace.
--tunnel, -t<tunnel>optionalExecute tasks with a tunnel to Deno Deploy.
Create a secure connection between your local machine and Deno Deploy, providing access to centralised environment variables, logging, and serving from your local environment to the public internet.
Dependency management options Jump to heading
--frozen<BOOLEAN>optionalError out if lockfile is out of date.
--lock<FILE>optionalCheck the specified lock file. (If value is not provided, defaults to "./deno.lock").
--no-lockDisable auto discovery of the lock file.
--node-modules-dir<MODE>optionalSelects the node_modules directory mode for npm packages (not a path). One of: auto (create a local node_modules directory and install npm packages into it), manual (use the existing local node_modules directory, do not modify it), none (do not use a local node_modules directory; resolve npm packages from the global cache). Defaults to auto when the flag is passed without a value.
--node-modules-linker<MODE>Sets the linker mode for npm packages (isolated or hoisted).
