A complete, source-based reference to the internals, configuration, operations, and security model of Yarn 1 ("Classic"), version 1.23.0-0 (commit c2dda50). This guide is written for operators, build engineers, platform architects, and contributors — anyone who needs to understand how Yarn works inside rather than just how to use it. For day-to-day command usage, see the companion User Guide.
Table of Contents
- Executive Summary
- System Architecture
- The Install Pipeline
- Repository Structure
- Module Reference
- The Lockfile Format
- Registries & Resolution
- Configuration Mastery
- Environment Variables
- Caching, Mirrors & Storage
- Security Analysis
- Operations & CI
- Building Yarn From Source
- Error Model & Exit Codes
- Code Quality & Conventions
- Potential Improvements
- Quick Reference
1. Executive Summary
Yarn is a command-line dependency manager for the JavaScript/Node.js ecosystem. Its job is to take a declarative manifest (package.json) and produce a deterministic, verified, on-disk dependency tree (node_modules) plus a lockfile (yarn.lock) that pins the exact result.
Key facts
| Property | Value |
|---|---|
| Project name | yarn (binaries: yarn, yarnpkg) |
| Version analyzed | 1.23.0-0 |
| License | BSD-2-Clause |
| Language | JavaScript (ES with Flow type annotations) |
| Runtime | Node.js (engines.node >= 4.0.0; supported set ^4.8.0 || ^5.7.0 || ^6.2.2 || >=8.0.0) |
| Entry point | bin/yarn.js → src/cli/index.js |
| Build system | Gulp (Babel transpile) + Webpack (single-file bundle) |
| Type checking | Flow |
| Tests | Jest |
| Default registry | https://registry.yarnpkg.com (a CDN proxy of the npm registry) |
| Lockfile version | 1 |
| Cache version | 6 |
Design goals. Yarn was built to solve three problems with early npm: determinism (a lockfile that pins the whole tree), speed (a global cache + parallel network and I/O), and security (integrity hashes on every package). Those three pillars are visible throughout the architecture: the resolver produces a fully pinned graph, the fetcher caches and hashes everything, and the lockfile records both.
Note
"Classic" Yarn (the 1.x line documented here) is a mature, stable codebase in maintenance mode. The actively developed successor, Yarn 2+ ("Berry"), is a separate repository with a plugin architecture and Plug'n'Play by default. Yarn 1 already ships an experimental Plug'n'Play implementation (see §7).
2. System Architecture
Yarn is a single Node process that orchestrates several cooperating subsystems. There is no daemon, no server, and no database — state lives entirely in files (package.json, yarn.lock, the cache, and .yarnrc/.npmrc).
bin/yarn.js (thin launcher; honors yarn-path)
│
▼
src/cli/index.js (main)
┌───────────────────────┼────────────────────────┐
│ parse global flags (commander.js) │
│ resolve command name (default = "install") │
│ build Reporter (Console | JSON) │
│ construct Config ◄─────── .yarnrc / .npmrc / env / DEFAULTS
│ acquire mutex (optional: file | network) │
└───────────────────────┬────────────────────────┘
│ command.run(config, reporter, flags, args)
▼
src/cli/commands/<command>.js (44 commands)
│
┌───────────────┬───────┴────────┬───────────────┬──────────────┐
▼ ▼ ▼ ▼ ▼
PackageResolver PackageFetcher PackageLinker PackageHoister PackageInstallScripts
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
Resolvers Fetchers node_modules hoisted tree lifecycle scripts
(registry + (tarball/git/ layout
8 exotics) copy + cache)
│ │
▼ ▼
Registries Global cache + offline mirror
(npm + yarn)
│
▼
RequestManager (HTTP, retries, concurrency, HAR capture)
Layering
| Layer | Responsibility | Key modules |
|---|---|---|
| CLI | Argument parsing, command dispatch, output | src/cli/index.js, src/cli/commands/* |
| Orchestration | The install lifecycle and its steps | src/cli/commands/install.js (the Install class) |
| Resolution | Turn version ranges into pinned packages | src/package-resolver.js, src/resolvers/* |
| Fetching | Download + cache + verify packages | src/package-fetcher.js, src/fetchers/* |
| Linking | Lay out node_modules, hoist, link bins |
src/package-linker.js, src/package-hoister.js |
| Registry I/O | Auth, config, registry protocol | src/registries/*, src/util/request-manager.js |
| Persistence | Lockfile + integrity | src/lockfile/*, src/integrity-checker.js |
| Reporting | Console/JSON output, progress, prompts | src/reporters/* |
| Config | Merge .yarnrc/.npmrc/env/defaults |
src/config.js, src/rc.js |
The launcher and yarn-path
src/cli/index.js's start() function checks for a yarn-path (or yarnPath) setting in the resolved .yarnrc. If present — and the YARN_IGNORE_PATH environment variable is not set — Yarn spawns the Yarn bundle at that path instead of running itself. This is the mechanism behind yarn policies set-version / yarn set version: a project can commit an exact Yarn release into .yarn/releases/ and pin every developer to it regardless of their globally installed Yarn.
process start
│
├─ read .yarnrc for yarn-path
│ └─ if set and !YARN_IGNORE_PATH → spawn that bundle (.cjs via node, else direct) → exit
│
└─ otherwise → main({startArgs, args, endArgs})
3. The Install Pipeline
The Install class in src/cli/commands/install.js (≈1,150 lines) is the heart of Yarn. yarn add, yarn remove, yarn upgrade, yarn import, yarn unplug, yarn check, yarn list, and others all subclass or reuse it.
Install.init() assembles an ordered list of steps, each wrapped in a hook (callThroughHook) so plugins/experimentalYarnHooks can intercept them, then runs them in sequence with progress reporting:
Install.init()
├─ checkUpdate() once-a-day self-update nag
├─ fetchRequestFromCwd() read manifests → dependency requests + patterns
├─ (optional) checkCompatibility engines/os/cpu checks
├─ STEP resolveStep → resolver.init(requests) Resolving packages...
│ └─ bailout() if integrity matches and lockfile is clean → done early
├─ STEP auditStep → audit.performAudit() (if --audit) Auditing...
├─ STEP fetchStep → fetcher.fetch(manifests) Fetching packages...
│ └─ compatibility.check() across the whole graph
├─ STEP linkStep → linker.init(patterns) Linking dependencies...
├─ STEP pnpStep → generatePnpMap() (if PnP enabled) write .pnp.js
├─ STEP buildStep → scripts.init() (unless --ignore-scripts) Building fresh packages...
├─ (optional) HAR save / autoclean
├─ saveLockfileAndIntegrity() write yarn.lock + .yarn-integrity
└─ persistChanges() write back package.json (for add/remove/upgrade)
The bailout optimization
Before doing any network work, Install.bailout() consults the integrity checker (src/integrity-checker.js). It compares the requested patterns, the lockfile cache, the flags, and the workspace layout against the stored .yarn-integrity file. If everything matches and a lockfile exists, Yarn prints success Already up-to-date. and exits immediately. This is why a no-op yarn install is nearly instantaneous.
Bailout is disabled when --audit, --force, --skip-integrity-check, or Plug'n'Play is in effect (PnP installs are fast enough that the check is not worthwhile). --frozen-lockfile additionally turns a needed-update into a hard error here.
Lifecycle script ordering
wrapLifecycle() (exported from install.js) frames the install with npm-compatible lifecycle scripts:
preinstall → [install work] → install → postinstall → (if !production) prepublish → prepare
yarn run adds pre<script>/post<script> wrapping for arbitrary scripts; yarn publish runs prepublish, prepare, prepublishOnly, prepack, then publish, postpublish; yarn pack runs prepack/postpack; yarn remove runs preuninstall/uninstall/postuninstall.
4. Repository Structure
The repository is large by file count (~5,200 files) but the source is concentrated — most files are test fixtures and a cached HTTP request fixture set under __tests__/fixtures/request-cache/. The triage that matters:
yarn/
├── bin/ Launcher scripts (yarn.js)
├── src/ ◄── ALL production source
│ ├── cli/
│ │ ├── index.js main(): flag parsing, dispatch, mutex, error handling
│ │ ├── aliases.js command-name aliases (upgrade-interactive, etc.)
│ │ └── commands/ 44 command modules (see §5)
│ ├── registries/ npm + yarn registry clients
│ ├── resolvers/ registry resolvers + 8 exotic resolvers
│ ├── fetchers/ tarball / git / copy fetchers
│ ├── lockfile/ lockfile parse / stringify
│ ├── reporters/ console / json / buffer / event reporters
│ │ └── lang/ localized message strings (en.js, ...)
│ ├── util/ ~40 utility modules (fs, git, crypto, ...)
│ │ └── normalize-manifest/ package.json normalization + validation
│ ├── config.js central configuration object
│ ├── constants.js URLs, filenames, versions, concurrency
│ ├── errors.js error class hierarchy
│ ├── package-resolver.js drives resolution of the whole graph
│ ├── package-fetcher.js drives fetching of all manifests
│ ├── package-linker.js lays out node_modules + bin links
│ ├── package-hoister.js hoisting / deduplication algorithm
│ ├── package-request.js a single dependency request
│ ├── package-reference.js a resolved package reference
│ ├── package-install-scripts.js runs install/build scripts in parallel
│ ├── package-compatibility.js engines/os/cpu checks
│ ├── integrity-checker.js decides whether reinstall is needed
│ ├── workspace-layout.js workspace graph model
│ ├── resolution-map.js selective "resolutions" field support
│ └── hoisted-tree-builder.js builds the tree audit/why/list consume
├── packages/
│ ├── lockfile/ @yarnpkg/lockfile (standalone parser package)
│ └── pkg-tests/ end-to-end behavioral test harness
├── __tests__/ Jest unit tests + fixtures
├── scripts/ build/release scripts (dist, deb, rpm, chocolatey, win)
├── resources/ installer resources (debian, winsetup, chocolatey)
└── package.json manifest (deps, scripts, jest config)
5. Module Reference
CLI commands (src/cli/commands/, 44 modules)
Each command module exports run, setFlags, and hasWrapper, and is registered in src/cli/commands/index.js. The dispatcher in index.js defaults to install when no command is given, and to run (script execution) when an unrecognized name is given.
Install family (subclass or reuse the Install class):
| Module | Command | Notes |
|---|---|---|
install.js |
install |
The base Install class + the install pipeline. |
add.js |
add |
Add extends Install; appends args as new requests, computes save ranges. |
remove.js |
remove |
Deletes from manifest + reinstalls to regenerate the lockfile. |
upgrade.js |
upgrade |
Computes outdated set, unlocks them in the lockfile, delegates to Add. |
upgrade-interactive.js |
upgrade-interactive |
Inquirer checkbox UI over the outdated set. |
import.js |
import |
Import extends Install; builds a graph from package-lock.json/node_modules. |
unplug.js |
unplug |
Marks packages for unplugging during a PnP install. |
check.js |
check |
Verifies tree vs. lockfile (--integrity, --verify-tree). |
list.js |
list |
Builds and prints the hoisted tree. |
outdated.js |
outdated |
Diffs current vs. wanted vs. latest. |
licenses.js |
licenses |
list + generate-disclaimer subcommands. |
audit.js |
audit |
Posts a gzip'd dependency tree to the registry audit endpoint. |
why.js |
why |
Traces dependency reasons and disk sizes. |
Project & scripts:
| Module | Command | Notes |
|---|---|---|
init.js |
init |
Interactive manifest creation; -2/--install delegate to policies set-version. |
run.js |
run |
Runs package.json scripts and .bin binaries; suggests on typos via leven. |
exec.js |
exec |
Runs an arbitrary command in Yarn's env. |
node.js |
node |
Runs Node with Yarn's version + PnP require. |
bin.js |
bin |
Prints .bin folder or a binary path. |
create.js |
create |
Global-installs and runs a create-* package. |
Registry / publishing:
| Module | Command | Notes |
|---|---|---|
publish.js |
publish |
Bump → login → pack → PUT to registry → revoke token. |
pack.js |
pack |
Builds the publishable tarball (honors files, .npmignore, bundledDependencies). |
version.js |
version |
Bumps version, runs preversion/version/postversion, git-tags. |
versions.js |
versions |
Reports yarn/node/component versions. |
login.js / logout.js |
login/logout |
Store/clear registry credentials; token acquisition (getToken). |
owner.js |
owner |
Add/remove/list package maintainers. |
tag.js |
tag |
Manage dist-tags. |
team.js |
team |
Manage org teams. |
info.js |
info |
Registry metadata for a package. |
access.js |
access |
Reserved — notYetImplemented. |
generate-lock-entry.js |
generate-lock-entry |
Print a lockfile entry for the current package. |
Global / linking / workspaces / config:
| Module | Command | Notes |
|---|---|---|
global.js |
global |
add/remove/list/upgrade/bin/dir; manages a side install + bin links. |
link.js / unlink.js |
link/unlink |
Create/remove dev symlinks in the global link registry. |
workspace.js |
workspace |
Run a command in one named workspace. |
workspaces.js |
workspaces |
info + run across all workspaces. |
config.js |
config |
get/set/delete/list/current against .yarnrc. |
policies.js |
policies set-version |
Download + pin a Yarn release (incl. berry, nightly). |
cache.js |
cache |
list/dir/clean on the global cache. |
autoclean.js |
autoclean |
Strip docs/tests/examples from deps via .yarnclean. |
help.js |
help |
Render help text. |
Helper modules: _build-sub-commands.js (factory for commands with subcommands like cache, config, global), _useless.js (factory for the deliberate no-op commands).
Note
Three commands are intentional no-ops built via
_useless.js:dedupe,prune, andlockfile. Each throws a message explaining thatyarn installalready performs the operation. This is documented behavior, not a bug.
Core engine modules (src/)
| Module | Role |
|---|---|
package-resolver.js |
Orchestrates resolution of the entire graph; deduplicates patterns; tracks patterns/patternsByPackage; integrates ResolutionMap. |
package-request.js |
Represents one dependency request; finds versions on the registry or via exotic resolvers; computes the outdated set (getOutdatedPackages). |
package-reference.js |
A resolved package instance with its requests, patterns, remote, permissions. |
package-fetcher.js |
Fetches all resolved manifests (parallel), populating the cache. |
package-linker.js |
Builds the flat hoisted tree, copies/symlinks packages, links bins, resolves peer dependencies. |
package-hoister.js |
The hoisting algorithm; HoistManifest, NohoistResolver for nohoist workspace rules. |
package-install-scripts.js |
Runs install/postinstall build scripts with bounded concurrency. |
package-compatibility.js |
Validates engines, os, cpu against the host. |
integrity-checker.js |
Computes/compares .yarn-integrity; the basis of the bailout optimization. |
resolution-map.js |
Implements the resolutions manifest field for forcing transitive versions. |
workspace-layout.js |
Models the workspace graph and the virtual aggregator manifest. |
config.js |
The central Config object: folders, registries, options, lifecycle script execution, manifest read/write. |
lockfile/ |
Custom lockfile tokenizer/parser + stringifier (see §6). |
6. The Lockfile Format
yarn.lock (lockfile version 1) is generated by src/lockfile/stringify.js and read by src/lockfile/parse.js. It is a custom, YAML-like, human-readable format — not JSON and not strictly YAML.
A representative entry:
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
lodash@^4.17.0, lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#hash"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvKw==
Key properties of the format:
- Multiple patterns share one entry.
lodash@^4.17.0, lodash@^4.17.21:means both ranges resolved to the same package; the body is written once. - Field ordering is stable.
stringify.jssorts fields by a fixed priority (name,version,uid,resolved,integrity,registry,dependencies) then alphabetically, so diffs stay minimal across runs. - Keys are quoted only when necessary (
maybeWrap/shouldWrapKey), keeping the file readable. npmregistry is the implicit default — entries omitregistrywhen it isnpm(implodeEntry).- Integrity is normalized with a stable sort of multi-hash strings (
serializeIntegrity).
The parser
parse.js implements a hand-written tokenizer (tokenise) and recursive-descent Parser. Notable behaviors:
- It enforces two-space indentation — odd indentation throws
Invalid number of spaces. - It rejects newer lockfiles: if a
# yarn lockfile v<N>comment declares a version greater thanLOCKFILE_VERSION(1), it errors and tells you to upgrade Yarn. - If the custom parse fails on a non-
.ymlfile, it falls back to a YAML safe-load (js-yamlFAILSAFE_SCHEMA). - It detects and resolves git merge conflicts automatically:
hasMergeConflicts()finds<<<<<<</=======/>>>>>>>markers,extractConflictVariants()splits the two sides, and both are parsed and merged. A successful merge reportsinfo Merge conflict detected in yarn.lock and successfully merged.; an unmergeable one returns aconflictresult.
Tip
Because Yarn auto-merges lockfile conflicts, the recommended resolution for a
yarn.lockgit conflict is simply to runyarn install— it will parse both variants, merge them, and rewrite a clean lockfile.
The standalone @yarnpkg/lockfile package (packages/lockfile/) exposes this parse/stringify pair for third-party tooling.
7. Registries & Resolution
Dual-registry model
src/registries/ defines two registry clients, both subclasses of BaseRegistry:
BaseRegistry
└─ NpmRegistry (folder: node_modules; filename: package.json)
└─ YarnRegistry (filename: yarn.json; holds Yarn's own defaults + ~/.yarnrc)
NpmRegistryspeaks the npm registry protocol: it resolves the registry URL per package/scope, handles scoped-package name escaping (@scope%2fpkg), enforces HTTPS foryarnpkg.com/npmjs.org, reads.npmrcfrom the standard cascade (cwd → home → global prefixetc), and assembles auth headers (_authToken→ Bearer,_auth→ Basic, username/_password→ Basic). It also handles 2FA: on aOneTimePasswordErrorit prompts for a one-time password and retries.YarnRegistrylayers Yarn-specific defaults on top. ItsDEFAULTSobject is the authoritative source of Yarn's built-in configuration values (see §8). ItsgetOption()resolves a key by checking the Yarn config, then an npm-name mapping (npmMap), then the npm config, thenDEFAULTS. It persists global settings to~/.yarnrcviasaveHomeConfig().
Resolvers
src/resolvers/index.js registers two registry resolvers (npm, yarn) and eight exotic resolvers for non-registry sources. getExoticResolver(pattern) tests each in turn:
| Exotic resolver | Pattern example |
|---|---|
git-resolver |
git+https://…, git+ssh://… |
tarball-resolver |
https://example.com/pkg.tgz |
github-resolver |
user/repo, github:user/repo |
gitlab-resolver |
gitlab:user/repo |
bitbucket-resolver |
bitbucket:user/repo |
gist-resolver |
gist:hash |
file-resolver |
file:../path |
link-resolver |
link:../path |
Each registry resolver is also exposed as an exotic via a generated ExoticRegistryResolver subclass, enabling npm:foo@1.0.0 / yarn:foo@1.0.0 protocol patterns.
Fetchers
src/fetchers/ mirrors the resolver families. BaseFetcher.fetch() creates the destination, calls the subclass _fetch(), normalizes the resulting manifest, and links any declared bin entries (via cmd-shim on Windows, symlinks elsewhere). Subclasses include a tarball fetcher (with a LocalTarballFetcher), a git fetcher, and a copy fetcher for file: dependencies. Fetched tarballs may be copied into the offline mirror for air-gapped reinstalls.
Plug'n'Play (experimental)
When --enable-pnp/--pnp is set (or installConfig.pnp in package.json), the install pipeline adds a pnpStep that writes a .pnp.js file mapping every dependency to its location in the cache, bypassing node_modules entirely. yarn node and yarn run auto---require this file. yarn unplug materializes a package out of the cache into .yarn/unplugged/ for debugging; it errors if PnP is disabled.
8. Configuration Mastery
Configuration sources & precedence
Yarn merges configuration from multiple sources. Highest precedence wins:
command-line flags
▼
environment variables (YARN_*, NPM_CONFIG_*, npm_config_*)
▼
project .yarnrc / .npmrc (searched cwd → upward)
▼
home ~/.yarnrc / ~/.npmrc
▼
global prefix etc/yarnrc / etc/npmrc
▼
YarnRegistry.DEFAULTS (built-in defaults)
.yarnrc is parsed with the lockfile parser; .npmrc is parsed as INI (ini). Disable auto-detection with --no-default-rc; supply an explicit file with --use-yarnrc <path>.
Built-in defaults (YarnRegistry.DEFAULTS)
These are the authoritative Yarn defaults from src/registries/yarn-registry.js:
| Key | Default | Meaning |
|---|---|---|
registry |
https://registry.yarnpkg.com |
Package registry. |
save-prefix |
^ |
Range prefix for yarn add. |
bin-links |
true |
Create .bin symlinks. |
ignore-scripts |
false |
Skip lifecycle scripts. |
ignore-optional |
false |
Skip optional dependencies. |
strict-ssl |
true |
Enforce valid TLS certs. |
init-version |
1.0.0 |
Default version for yarn init. |
init-license |
MIT |
Default license for yarn init. |
version-tag-prefix |
v |
Git tag prefix for yarn version. |
version-git-tag |
true |
Create a git tag on yarn version. |
version-git-sign |
false |
GPG-sign the version tag. |
version-commit-hooks |
true |
Run git commit hooks. |
version-git-message |
v%s |
Version commit message template. |
user-agent |
yarn/<v> npm/? node/<v> <platform> <arch> |
HTTP User-Agent. |
npm-key compatibility mapping
YarnRegistry maps several Yarn config names to their npm equivalents (npmMap), so npm config files keep working:
| Yarn key | npm key |
|---|---|
version-git-sign |
sign-git-tag |
version-tag-prefix |
tag-version-prefix |
version-git-tag |
git-tag-version |
version-commit-hooks |
commit-hooks |
version-git-message |
message |
Path-relative keys
These keys are resolved relative to the .yarnrc that defines them, and folder keys are auto-created: yarn-offline-mirror, cache-folder, global-folder, offline-cache-folder, yarn-path.
Other notable settings
| Setting | Effect |
|---|---|
yarn-path |
Spawn a pinned Yarn bundle instead of the global one. |
yarn-offline-mirror |
Directory to mirror downloaded tarballs into. |
yarn-offline-mirror-pruning |
Prune unused tarballs from the mirror on install. |
disable-self-update-check |
Suppress the once-a-day outdated-Yarn nag. |
script-shell |
Shell used to run scripts. |
scripts-prepend-node-path |
Prepend Node's dir to PATH in scripts. |
lastUpdateCheck |
Timestamp Yarn writes to throttle update checks. |
Important
Yarn reads both Yarn-prefixed (
YARN_*) and npm-prefixed (NPM_CONFIG_*,npm_config_*) environment variables. Environment variables are converted to config keys by lowercasing, stripping the prefix, turning__into.and_into-. This is how CI systems inject registry tokens (npm_config_//registry.example.com/:_authToken).
9. Environment Variables
Yarn reads these process environment variables directly (from src/constants.js, src/config.js, registry, and command modules):
| Variable | Used for |
|---|---|
YARN_SILENT |
Force silent mode (equivalent to --silent). |
YARN_IGNORE_PATH |
Ignore the yarn-path setting (prevents launcher recursion). |
YARN_WRAP_OUTPUT |
Toggle the header/footer output wrapper. |
YARN_AUTH_TOKEN / NPM_AUTH_TOKEN |
Bearer token for registry auth (yarn login/getToken). |
NPM_CONFIG_* / npm_config_* |
npm-style config injection (registry, auth, etc.). |
NODE_ENV / NPM_CONFIG_PRODUCTION / YARN_PRODUCTION |
Toggle production (skip devDependencies). |
CHILD_CONCURRENCY |
Override max concurrent child processes (default 5). |
NODE_OPTIONS |
Passed through to yarn node. |
PREFIX / DESTDIR / LOCALAPPDATA |
Global install prefix resolution. |
GITHUB_TOKEN |
Authenticated GitHub API calls in policies set-version. |
HOME / USERPROFILE / XDG_* |
Home, cache, config, and data directory resolution. |
TERM_PROGRAM |
Emoji auto-enable detection. |
PATH / Path |
Binary lookup (platform-aware key via getPathKey). |
10. Caching, Mirrors & Storage
Yarn keeps three distinct on-disk stores. Their locations are platform-dependent and derived in src/constants.js + src/util/user-dirs.js (XDG on Linux, AppData on Windows, etc.).
| Store | Constant / setting | Purpose |
|---|---|---|
| Global cache | PREFERRED_MODULE_CACHE_DIRECTORIES (XDG cache dir, then os.tmpdir()/.yarn-cache-<uid>) |
Every downloaded + extracted package, keyed by integrity. Cache format version 6. |
| Link registry | LINK_REGISTRY_DIRECTORY (<dataDir>/link) |
Symlinks created by yarn link. |
| Global modules | GLOBAL_MODULE_DIRECTORY (<dataDir>/global) |
Packages installed via yarn global add. |
| Offline mirror | yarn-offline-mirror (opt-in) |
Raw .tgz tarballs kept next to the project for offline installs. |
Inspect and manage the cache:
yarn cache dir # print cache location
yarn cache list # list cached packages
yarn cache list --pattern lo* # filter by name
yarn cache clean # wipe the whole cache
yarn cache clean lodash # remove one package
Tip
On CI, persist the global cache (
yarn cache dir) across builds rather thannode_modules. Combined with--frozen-lockfile, this gives fast, fully reproducible installs. For air-gapped builds, commit ayarn-offline-mirrorand install with--offline.
Yarn-managed sentinel files inside a project:
| File | Purpose |
|---|---|
yarn.lock |
The lockfile. |
.yarn-integrity |
Integrity snapshot powering the bailout optimization. |
.yarnclean |
Filter list for yarn autoclean. |
.pnp.js |
Plug'n'Play resolution map (when PnP enabled). |
.yarn-single-instance |
Mutex lock file (--mutex file). |
yarn-error.log |
Crash report written on unexpected errors. |
11. Security Analysis
Integrity verification
Every fetched package is verified against a cryptographic integrity hash (Subresource-Integrity style, sha512 preferred with sha1 fallback via the ssri library). The hash is recorded in yarn.lock (integrity field) and in .yarn-integrity. A package whose downloaded bytes do not match its recorded hash is rejected. This protects against tampered mirrors and corrupted downloads.
Transport security
NpmRegistry enforces HTTPS for the official registries: any http:// URL matching yarnpkg.com, npmjs.org, or npmjs.com is rewritten to https:// (REGEX_REGISTRY_ENFORCED_HTTPS). TLS certificate validation is on by default (strict-ssl: true). Proxies are supported via --proxy/--https-proxy.
Authentication
Credentials are never hard-coded. Auth is resolved per-registry from config in priority order: _authToken (Bearer) → _auth (Basic) → username + _password (Basic). Tokens can also come from YARN_AUTH_TOKEN/NPM_AUTH_TOKEN env vars. Two-factor auth is supported interactively or via --otp. yarn login stores only username/email in ~/.yarnrc; the password is requested per publish and used to mint a token that is revoked at the end of the operation (see publish/owner/tag/team flows).
Lifecycle script execution (the main risk surface)
Like all package managers, Yarn's largest attack surface is install scripts (preinstall/install/postinstall) — arbitrary code that runs during install. Mitigations available to operators:
--ignore-scripts(orignore-scripts: truein config) disables all lifecycle scripts.yarn audit(and--auditduring install) flags known-vulnerable packages against the registry advisory database.- The integrity system ensures the bytes of a package match what was locked, so a malicious change to an already-locked package is detected.
Caution
Running
yarn installexecutes install scripts from every dependency in the tree by default. In untrusted or high-security environments, install with--ignore-scriptsand audit scripts before enabling them. Pair this with a committedyarn.lockso the exact, reviewed package set is what gets installed.
Error classes with security meaning
src/errors.js defines SecurityError (a MessageError subclass) and OneTimePasswordError, used to surface integrity/auth failures distinctly from ordinary errors.
Note
Yarn 1 depends on the now-deprecated
requestHTTP library and uses the legacyurl.parseAPI (the source of the Node 24 deprecation warning, community issue #9210). These are maintenance/compatibility concerns rather than active vulnerabilities, but they are reasons the project recommends migrating to Yarn 2+ for new work.
12. Operations & CI
Recommended CI install
yarn install --frozen-lockfile --non-interactive
--frozen-lockfilefails the build ifyarn.lockis out of date — catching un-committed dependency changes.--non-interactiveprevents Yarn from ever blocking on a prompt (it would otherwise errorCan't answer a question unless a user TTY).
Deployment
yarn install --frozen-lockfile --production
Installs only runtime dependencies. Combine with NODE_ENV=production for the same effect via environment.
Concurrency tuning
| Knob | Default | How to change |
|---|---|---|
| Network concurrency | 8 | --network-concurrency <n> |
| Network timeout | 30000 ms | --network-timeout <ms> |
| Child-process concurrency | 5 | CHILD_CONCURRENCY env var |
Single-instance enforcement
yarn install --mutex file # lock via .yarn-single-instance
yarn install --mutex network:31997 # lock via a localhost port
Useful when multiple jobs might run Yarn in the same workspace concurrently. The default single-instance port is 31997.
Diagnostics
| Action | Command |
|---|---|
| Verbose internals | yarn <cmd> --verbose |
| Machine-readable logs | yarn <cmd> --json |
| Network capture | yarn install --har → yarn-install_<ts>.har |
| Verify install integrity | yarn check / yarn check --integrity |
| Crash report | auto-written to yarn-error.log (includes args, env PATH, versions, manifests, lockfile) |
Tip
When filing a bug, attach the auto-generated
yarn-error.log. It captures the command arguments, Yarn/Node versions, platform, full stack trace, and the relevant manifests and lockfile — everything a maintainer needs to reproduce.
Pinning Yarn for the whole team
yarn policies set-version 1.22.19 # or: latest, rc, nightly, berry
This writes .yarn/releases/yarn-<v>.cjs and sets yarn-path in .yarnrc. Commit both. Every subsequent yarn invocation in the repo runs that exact bundle, regardless of each developer's globally installed version.
13. Building Yarn From Source
Yarn is written in Flow-typed JavaScript and transpiled with Babel via Gulp; release artifacts are bundled with Webpack.
Key package.json scripts:
| Script | Command | Purpose |
|---|---|---|
build |
gulp build |
Transpile src/ → lib/. |
build-bundle |
node ./scripts/build-webpack.js |
Single-file Webpack bundle. |
build-dist |
bash ./scripts/build-dist.sh |
Full distributable build. |
build-deb |
./scripts/build-deb.sh |
Debian package. |
build-chocolatey |
powershell ./scripts/build-chocolatey.ps1 |
Windows Chocolatey package. |
build-win-installer |
scripts\build-windows-installer.bat |
Windows MSI. |
watch |
gulp watch |
Rebuild on change. |
lint |
eslint . && flow check |
Lint + type-check. |
test |
yarn lint && yarn test-only |
Full test suite. |
test-only |
node --max_old_space_size=4096 node_modules/jest/bin/jest.js --verbose |
Jest only. |
Testing:
- Unit tests live in
__tests__/, run by Jest, and use a committed HTTP request cache (__tests__/fixtures/request-cache/) so tests run offline and deterministically. - End-to-end tests live in
packages/pkg-tests/and exercise real install behavior against fixture packages.
CI matrices (AppVeyor / Azure Pipelines) test across Node 8/10/12/13 on Windows, Linux, and macOS.
Note
Yarn is self-hosting: its own CI uses Yarn to install Yarn's dependencies (
yarn install --frozen-lockfile), and the development bootstrap script adds the Yarn apt repository to install Yarn before building it.
14. Error Model & Exit Codes
Error class hierarchy (src/errors.js)
Error
├─ MessageError(msg, code) user-facing message; printed without a stack trace
│ ├─ ProcessSpawnError(.process) failed to spawn a child process
│ ├─ SecurityError integrity / security failures
│ └─ ProcessTermError(.EXIT_CODE) a child process terminated; carries its exit code
├─ ResponseError(.responseCode) HTTP-level error
└─ OneTimePasswordError 2FA OTP required/incorrect
MessageErrors are printed as clean one-line errors. Any other (unexpected) error triggers onUnexpectedError(), which writes a detailed yarn-error.log and prints a "report this" message. A ProcessTermError propagates the child's own exit code.
Audit exit codes
yarn audit returns a bitmask summing the severities present, so CI can gate on it:
| Severity | Bit value |
|---|---|
| info | 1 |
| low | 2 |
| moderate | 4 |
| high | 8 |
| critical | 16 |
A clean audit exits 0; an audit finding a high + a low vulnerability exits 10 (8 + 2).
15. Code Quality & Conventions
| Aspect | Convention |
|---|---|
| Types | Flow (/* @flow */ header required by lint rule flowtype/require-valid-file-annotation). |
| Formatting | Prettier — single quotes, trailing commas, no bracket spacing, 120-col print width. |
| Linting | ESLint extending eslint-config-fb-strict, plus a custom eslint-plugin-yarn-internal (e.g. warn-language to keep user-facing strings centralized). |
require-await, no-process-exit, no-return-await |
Enforced as errors. |
| i18n | All user-facing text flows through reporter.lang(key, ...args), resolved from src/reporters/lang/*. New strings must be added there, not inlined. |
| Command shape | Every command exports run, setFlags, hasWrapper; optional examples, requireLockfile, noArguments, shouldRunInCurrentCwd. |
| Subcommands | Built with the _build-sub-commands.js factory for a consistent yarn <cmd> <subcmd> UX. |
Architecture patterns worth noting:
- Class inheritance for command variants.
Add,GlobalAdd,ImportextendInstall, overriding hooks (prepareRequests,bailout,applyChanges) rather than duplicating the pipeline. - Step-and-hook pipeline. The install flow is a list of hookable async steps, enabling
experimentalYarnHooksinterception (resolveStep,fetchStep,linkStep,buildStep,pnpStep,auditStep,runScript). - Reporter abstraction. All output goes through a
Reporterinterface (ConsoleReporter,JSONReporter,BufferReporter,EventReporter,NoopReporter), so--jsonand silent modes work uniformly and commands can be driven programmatically.
16. Potential Improvements
Observations drawn from the source (Yarn 1 is in maintenance mode, so these are largely addressed in Yarn 2+):
- Deprecated HTTP stack. Yarn 1 depends on the unmaintained
requestlibrary and legacyurl.parse, producing deprecation warnings on modern Node (community issue #9210). A migration tofetch/undiciwould remove these. - Audit endpoint coupling.
yarn auditassumes the registry implements the npm audit API; custom registries return 410 and break it (community issue #9234). A graceful "audit unsupported" path would improve robustness. Note the source already disables the (npm-incompatible) automatic-fix action reporting. - PnP is experimental in 1.x. The Plug'n'Play implementation works but is not the default; Yarn 2+ promotes it to a first-class strategy.
node_modulescopying cost. The default linker copies packages intonode_modules;--link-duplicateshard-links duplicates, but the broader inefficiency is what PnP and Yarn 2+ address.- Self-update nag. The once-a-day "Yarn is outdated" check writes
lastUpdateCheckto home config; centrally managed installs should setdisable-self-update-check.
17. Quick Reference
Key paths & files
| Path | Role |
|---|---|
bin/yarn.js |
Launcher. |
src/cli/index.js |
main() — parse, dispatch, run. |
src/cli/commands/ |
44 command modules. |
src/config.js / src/constants.js |
Central config / canonical constants. |
src/registries/yarn-registry.js |
Built-in config defaults. |
src/lockfile/ |
Lockfile parse/stringify. |
yarn.lock |
The lockfile (v1). |
.yarn-integrity |
Bailout integrity snapshot. |
yarn-error.log |
Crash report. |
Canonical constants (src/constants.js)
| Constant | Value |
|---|---|
YARN_REGISTRY |
https://registry.yarnpkg.com |
SUPPORTED_NODE_VERSIONS |
^4.8.0 || ^5.7.0 || ^6.2.2 || >=8.0.0 |
LOCKFILE_VERSION |
1 |
CACHE_VERSION |
6 |
NETWORK_CONCURRENCY |
8 |
NETWORK_TIMEOUT |
30000 ms |
CHILD_CONCURRENCY |
5 |
SINGLE_INSTANCE_PORT |
31997 |
LOCKFILE_FILENAME |
yarn.lock |
INTEGRITY_FILENAME |
.yarn-integrity |
CLEAN_FILENAME |
.yarnclean |
PNP_FILENAME |
.pnp.js |
CI one-liners
# Reproducible install
yarn install --frozen-lockfile --non-interactive
# Production deploy
yarn install --frozen-lockfile --production
# Security gate (fails on high+ severity)
yarn audit --level high
# Offline / air-gapped
yarn install --offline
Install-pipeline cheat sheet
resolve → fetch → link → (pnp) → build → write lockfile + integrity → persist manifest
│ │ │ │
bailout cache hoist scripts