Yarn 06 ๐งถ Running Scripts and Executing Binaries
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
| Priority | Source | Example |
|---|---|---|
| 1 | scripts in package.json | yarn test runs jest |
| 2 | Taskfile task | yarn build runs a taskfile task |
| 3 | Dependency binary | yarn tsc runs node_modules/.bin/tsc |
yarn run Flags
| Flag | Purpose |
|---|---|
-T, --top-level | Resolve from root workspace |
-B, --binaries-only | Skip scripts and tasks |
--inspect | Forward --inspect to Node.js |
--inspect-brk | Forward --inspect-brk to Node.js |
--require | Preload a module via Node.js |
yarn dlx Flags
| Flag | Purpose |
|---|---|
-p, --package | Specify package(s) to install |
-q, --quiet | Suppress 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
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Wrong tool executed | A script shadows a binary name | Use --binaries-only to force binary resolution |
| Script runs from wrong directory | Monorepo workspace context | Use --top-level for root scripts |
| Arguments not forwarded | Shell interprets flags before Yarn | Quote flags if they contain special characters |
| dlx package not found | -p flag not used for differently-named binary | Specify the package with -p |
| Node.js flag ignored | Target 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
| Item | Value |
|---|---|
| Script execution | yarn <script> or yarn run <script> |
| Resolution order | Script โ 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 execution | yarn dlx <package> |
| Dlx package specification | -p or --package |
| Dlx persistence | None; not recorded |
Key takeaways:
- The
runkeyword is optional in Yarn Berry.yarn testandyarn run testare identical. The resolution order is the same regardless . - Scripts take precedence over binaries. A script named
testshadows a dependency binary namedtest. The--binaries-onlyflag 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-levelflag 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 dlxis 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 .dlxis not a replacement foradd. Usingdlxfor 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.
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!