| |

Yarn 06 ๐Ÿงถ Running Scripts and Executing Binaries

โฑ๏ธ Estimated read: 9 min | |

A project’s package.json is not just a dependency manifest. It is also a command registry. The scripts field defines named commands that can be invoked with a short alias, and Yarn provides a resolution layer that decides whether a given name refers to a script, a taskfile, or a binary from a dependency. In Yarn Berry (2+), the run keyword is optionalโ€”yarn test is the same as yarn run testโ€”and the resolution order is explicit and documented. Understanding this order is the difference between a command that works and one that silently executes the wrong tool .

Key point: yarn run <name> resolves the name in a fixed order: local script, taskfile task, dependency binary. If you omit run, Yarn still performs the same resolution. The --binaries-only flag skips scripts and tasks entirely.


Why script execution needs a resolution layer

The naming collision problem. A project may have a script called test and a dependency that exposes a binary called test. The scripts field in package.json takes precedence, but this is a convention, not a universal rule. Without an explicit resolution order, invoking yarn test would be ambiguous. Yarn documents the order to remove the ambiguity: local scripts first, then taskfiles, then binaries .

The workspace context problem. In a monorepo, a script defined in a workspace runs from the directory of that workspace, not from the repository root. This is deliberate: scripts often expect to read files relative to their own package. The --top-level flag overrides this behavior when the root workspace’s scripts are needed .

The binary forwarding problem. A dependency binary may need Node.js-specific flags. Yarn provides --inspect, --inspect-brk, and --require to forward these flags to the Node.js process running the binary. Without this, debugging a binary installed as a dependency would require knowing its exact path and invoking Node.js manually .

The one-off execution problem. Some binaries are needed only onceโ€”a scaffolding tool, a code generator, a converter. Installing them as a project dependency pollutes the manifest and the lockfile. yarn dlx installs the package in a temporary environment, runs the binary, and discards it. The package is never recorded in package.json or yarn.lock .

The trade-off. Scripts are fast because they resolve locally. dlx is slower because it installs a package in a temporary directory. The resolution order is rigid, which means a script that shadows a binary name cannot be bypassed without the --binaries-only flag. These are deliberate constraints, not limitations.


a. Resolving scripts and binaries with yarn run

yarn run <name> resolves the name against three sources in order. First, it checks the scripts field of the current workspace’s package.json. If a matching script exists, Yarn executes it. Second, it checks for a taskfile in the workspace and looks for a task with the matching name. Third, it checks whether any dependency of the local workspace exposes a binary with the matching name .

# These two commands are identical
yarn run build
yarn build

The script runs from the directory of the workspace that declares it. If the workspace is a sub-package in a monorepo, the current working directory during execution is that sub-package’s directory, not the repository root. This is important for scripts that reference relative paths .

The --top-level (or -T) flag changes the resolution scope. It checks the top-level workspace instead of the current one. This is useful when a root-level script orchestrates work across the entire monorepo .

The --binaries-only (or -B) flag skips the script and taskfile checks entirely. It resolves the name only against dependency binaries. This is the escape hatch when a script name shadows a binary name and the binary is the intended target .


b. Forwarding flags and passing arguments

Arguments after the script name are passed to the script. For a script that invokes Jest, yarn test -o --watch runs jest -o --watch. In Yarn Berry, the -- separator is not required for argument forwarding; arguments after the script name are passed through .

# Arguments are forwarded to the script
yarn test --coverage --watchAll=false

Node.js debugging flags are forwarded when the target is a Node binary. The --inspect flag activates the inspector, --inspect-brk pauses execution until a debugger attaches, and --require preloads a module. These are applied to the Node.js process that runs the binary, not to Yarn itself .

# Forward --inspect-brk to the Node process running webpack
yarn run --inspect-brk webpack

c. One-off execution with yarn dlx

yarn dlx installs a package in a temporary project and runs a binary from that installation. The binary runs from the current working directory, so it can read project files, but the package itself is not added to the project’s dependencies .

# Run create-vite without installing it
yarn dlx create-vite

# Install multiple packages for a single command
yarn dlx -p typescript -p ts-node ts-node --transpile-only -e "console.log('hello')"

The -p (or --package) flag specifies the package to install when the binary name differs from the package name. The -q (or --quiet) flag suppresses install logs .

Using yarn dlx as a replacement for yarn add is explicitly discouraged. The packages installed by dlx are not tracked in the manifest or the lockfile, making the project non-deterministic. dlx is for one-off commands, not for dependencies the project relies on .


Complete Example Session

# ============================================
# PART 1: DEFINING SCRIPTS IN PACKAGE.JSON
# ============================================

# scripts field defines the command registry.
{
  "scripts": {
    "build": "tsc",
    "test": "jest",
    "lint": "eslint src/"
  }
}
# ============================================
# PART 2: RUNNING A SCRIPT
# ============================================

yarn build
# Executes "tsc" from the workspace directory.
# ============================================
# PART 3: OMITTING THE RUN KEYWORD
# ============================================

# In Yarn Berry, "run" is optional.
yarn test
# Identical to: yarn run test
# ============================================
# PART 4: PASSING ARGUMENTS
# ============================================

yarn test --coverage
# Executes: jest --coverage
# ============================================
# PART 5: RUNNING A DEPENDENCY BINARY
# ============================================

# If "tsc" is not a script but is a binary from TypeScript:
yarn tsc --version
# Resolves to the binary in node_modules/.bin
# ============================================
# PART 6: FORCING BINARY RESOLUTION
# ============================================

# Skip scripts and resolve only binaries.
yarn run --binaries-only tsc
# Even if a "tsc" script exists, the binary runs.
# ============================================
# PART 7: TOP-LEVEL RESOLUTION
# ============================================

# Run a script from the root workspace.
yarn run --top-level build
# Resolves "build" from the root package.json.
# ============================================
# PART 8: NODE.JS FLAG FORWARDING
# ============================================

# Debug a binary with the inspector.
yarn run --inspect-brk webpack
# Node.js runs webpack with --inspect-brk.
# ============================================
# PART 9: ONE-OFF EXECUTION WITH DLX
# ============================================

# Scaffold a project without installing create-vite.
yarn dlx create-vite my-app
# The package is installed temporarily and discarded.

The nine parts trace the execution model: defining scripts, running them with and without the run keyword, passing arguments, resolving binaries, forcing binary-only resolution, using top-level scope, forwarding Node.js flags, and executing one-off tools with dlx.


Quick Reference

Resolution Order

PrioritySourceExample
1scripts in package.jsonyarn test runs jest
2Taskfile taskyarn build runs a taskfile task
3Dependency binaryyarn tsc runs node_modules/.bin/tsc

yarn run Flags

FlagPurpose
-T, --top-levelResolve from root workspace
-B, --binaries-onlySkip scripts and tasks
--inspectForward --inspect to Node.js
--inspect-brkForward --inspect-brk to Node.js
--requirePreload a module via Node.js

yarn dlx Flags

FlagPurpose
-p, --packageSpecify package(s) to install
-q, --quietSuppress install logs

Best Practices

โœ… Do This:

yarn build
<!-- Omit "run" for brevity in Yarn Berry -->       # โœ…
yarn test --coverage
<!-- Arguments after the script name are forwarded --> # โœ…
yarn dlx create-vite
<!-- Use dlx for one-off scaffolding tools -->       # โœ…

โŒ Don’t Do This:

yarn add create-vite
<!-- One-off tools belong in dlx, not dependencies --> # โŒ
yarn run --binaries-only test
<!-- Skips the "test" script if one exists -->        # โŒ
# Rely on yarn dlx for project dependencies
<!-- dlx packages are not tracked in the manifest --> # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Wrong tool executedA script shadows a binary nameUse --binaries-only to force binary resolution
Script runs from wrong directoryMonorepo workspace contextUse --top-level for root scripts
Arguments not forwardedShell interprets flags before YarnQuote flags if they contain special characters
dlx package not found-p flag not used for differently-named binarySpecify the package with -p
Node.js flag ignoredTarget is not a Node binary--inspect only applies to Node.js execution

Real-World Examples

1. Run a Script

yarn build

2. Run with Arguments

yarn test --watch

3. Force Binary Resolution

yarn run --binaries-only eslint

4. Top-Level Script

yarn run -T lint

5. Forward Inspector

yarn run --inspect-brk webpack

6. One-Off Scaffold

yarn dlx create-vite my-app

7. Multiple Packages for Dlx

yarn dlx -p typescript -p ts-node ts-node script.ts

8. Quiet Dlx

yarn dlx -q prettier --write .

9. Check Available Scripts

yarn run
# Lists all scripts in the current workspace.

10. Run a Binary Directly

yarn tsc --noEmit

Visual

Resolution Order

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  yarn <name>                                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  1. Is <name> in package.json "scripts"?     โ”‚
โ”‚     โ”œโ”€ YES โ†’ Execute the script              โ”‚
โ”‚     โ””โ”€ NO  โ†’ Continue                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  2. Is <name> a task in a taskfile?          โ”‚
โ”‚     โ”œโ”€ YES โ†’ Execute the task                โ”‚
โ”‚     โ””โ”€ NO  โ†’ Continue                        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  3. Does a dependency expose a binary?       โ”‚
โ”‚     โ”œโ”€ YES โ†’ Execute the binary              โ”‚
โ”‚     โ””โ”€ NO  โ†’ Error                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The order is fixed. The --binaries-only flag skips steps 1 and 2.

Dlx vs Add

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  yarn dlx create-vite                        โ”‚
โ”‚                                              โ”‚
โ”‚  Temporary project โ†’ install โ†’ run โ†’ discard โ”‚
โ”‚  Not recorded in package.json or yarn.lock   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  yarn add create-vite                        โ”‚
โ”‚                                              โ”‚
โ”‚  Project dependencies โ†’ install โ†’ recorded   โ”‚
โ”‚  Available for future commands               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

dlx is for one-off tools. add is for ongoing dependencies.


Summary

ItemValue
Script executionyarn <script> or yarn run <script>
Resolution orderScript โ†’ taskfile โ†’ binary
Binary-only flag--binaries-only or -B
Top-level flag--top-level or -T
Node.js flag forwarding--inspect, --inspect-brk, --require
One-off executionyarn dlx <package>
Dlx package specification-p or --package
Dlx persistenceNone; not recorded

Key takeaways:

  • The run keyword is optional in Yarn Berry. yarn test and yarn run test are identical. The resolution order is the same regardless .
  • Scripts take precedence over binaries. A script named test shadows a dependency binary named test. The --binaries-only flag forces binary resolution when needed .
  • Scripts run from the workspace directory. In a monorepo, a sub-package’s script runs from that sub-package’s directory, not from the root. The --top-level flag changes this .
  • Arguments are forwarded to the script. Flags after the script name are passed through to the underlying command without requiring a -- separator .
  • yarn dlx is for one-off tools. It installs a package temporarily, runs its binary, and discards it. The package is not recorded in the manifest or lockfile .
  • dlx is not a replacement for add. Using dlx for dependencies makes the project non-deterministic because the installed packages are not tracked .

Remember: yarn run is a resolver, not just a script runner. It checks scripts first, then taskfiles, then binaries. The --binaries-only flag skips to the binary layer. The --top-level flag changes the workspace context. And yarn dlx is the escape hatch for tools that do not belong in the project’s dependency graph.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!