Yarn 03 ๐งถ Initializing Projects and package.json
Every Yarn project begins with a single file. Before dependencies are installed, before scripts are run, and before lockfiles are generated, there is package.json. This file is the manifestโthe contract between your project, the package manager, and anyone who clones the repository. Running yarn init is the first command in any new project, and understanding what it creates (and what it deliberately leaves out) determines whether the project starts on solid footing or accumulates configuration debt from the first commit.
Key point: package.json is not just a list of dependencies. It is the identity of the project, the entry point for tooling, and the metadata that decides whether the project can be published, installed, and understood by others.
Why package.json exists
The identity problem. A directory of JavaScript files has no name, no version, and no declared purpose. package.json gives the project an identity: name, version, description, and license. Without these fields, the project is anonymousโit cannot be published to a registry, it cannot be depended upon by another package, and its provenance is undocumented.
The dependency problem. Node.js resolves imports from node_modules, but nothing tells the system which packages belong there. dependencies, devDependencies, peerDependencies, and optionalDependencies declare exactly what the project needs, at what version range, and under what conditions. Without these fields, every install would be manual and unreproducible.
The entry point problem. When another package imports your library, Node.js needs to know which file to load. The main field (and the newer exports field) declares the entry point. When a user runs a command-line tool, the bin field maps command names to executable files. Without these, the code exists but cannot be consumed.
The tooling problem. Scripts, workspaces, package manager version pins, and platform constraints all live in package.json. The file is not just metadataโit is the configuration hub that every tool in the JavaScript ecosystem reads.
The trade-off. A minimal package.json is easy to understand but leaves the project under-specified. A comprehensive one documents everything but requires maintenance. The resolution is to include what the project needs to function and what others need to use itโnothing more.
a. Running yarn init: interactive versus non-interactive
The yarn init command creates a package.json in the current directory. In Yarn Berry (2+), the behavior is streamlined compared to Classic. The command generates the manifest, initializes a Git repository if none exists, and creates an .editorconfig file . It does not ask a series of interactive questions by default in the modern versionโthat interactive flow was a Yarn 1.x feature .
# Create a package.json in the current directory
yarn init
# Create a private package (adds "private": true)
yarn init -p
# Create a workspace root with a packages/ directory
yarn init -w
# Initialize with a specific Yarn version locked in the project
yarn init -i=latest
The -p flag is important for any project that will not be published to a registry. Setting "private": true prevents accidental publication and unlocks workspace features that are meaningless for published packages . The -w flag configures the project as a monorepo root, expecting workspaces under packages/ .
For a new project that will be published, omit -p. For an application, always include it.
b. The essential fields: identity and entry points
The name and version fields form the unique identifier for a package. Without them, the package cannot be installed by another project. The name must be lowercase, URL-safe, and not start with a dot or underscore . Scoped names like @myorg/utils are valid and namespace the package under an organization.
The main field declares the entry point that Node.js resolves when the package is imported. If omitted, Node.js defaults to index.js. For modern packages, the exports field is preferredโit provides conditional exports for different environments (CommonJS, ESM, browser) and is the official replacement for main .
{
"name": "my-package",
"version": "1.0.0",
"main": "./dist/index.js",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
The license field is not optional in practice. Without it, the default copyright appliesโno one has legal permission to use the code. An SPDX identifier like "MIT" or "Apache-2.0" removes that ambiguity .
c. Dependency fields and behavioral metadata
Yarn distinguishes between four types of dependencies, each with a specific purpose. dependencies are required for the package to function at runtime. devDependencies are required only during developmentโtesting frameworks, build tools, linters. peerDependencies declare packages that the consumer must provide, typically for plugins and shared runtime libraries. optionalDependencies are installed only if the platform matches the os, cpu, and libc constraints .
{
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"jest": "^29.0.0"
},
"peerDependencies": {
"react": "^19.0.0"
},
"optionalDependencies": {
"fsevents": "^2.3.0"
}
}
The scripts field defines commands runnable via yarn run. Yarn executes scripts in a POSIX-like shell that works across Linux, macOS, and Windows, making environment variable assignment and one-liners portable . The bin field exposes executable files to the shell, mapping command names to JavaScript entry points .
Complete Example Session
# ============================================
# PART 1: INITIALIZING A PRIVATE APPLICATION
# ============================================
# Applications should never be published.
# The -p flag sets "private": true automatically.
mkdir my-app && cd my-app
yarn init -p
# ============================================
# PART 2: THE GENERATED package.json
# ============================================
# Yarn generates a minimal manifest.
# name defaults to the directory name.
cat package.json
# {
# "name": "my-app",
# "version": "0.0.0",
# "private": true
# }
# ============================================
# PART 3: ADDING IDENTITY FIELDS
# ============================================
# Open package.json and add descriptive metadata.
# These fields help others understand the project.
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"description": "A task management API with persistent storage",
"license": "MIT",
"author": "Alex Chen <alex@example.com>"
}
# ============================================
# PART 4: DECLARING THE ENTRY POINT
# ============================================
# For an application, main may not be needed.
# For a library, it declares what importers receive.
{
"main": "./dist/index.js",
"exports": {
".": "./dist/index.js"
}
}
# ============================================
# PART 5: ADDING SCRIPTS
# ============================================
# Scripts are the primary interface for running tasks.
# Yarn provides a cross-platform shell.
{
"scripts": {
"dev": "node src/server.js",
"build": "tsc",
"test": "jest",
"lint": "eslint src/"
}
}
# ============================================
# PART 6: INSTALLING DEPENDENCIES
# ============================================
# yarn add modifies package.json and installs.
yarn add express
# yarn add -D adds to devDependencies.
yarn add -D typescript jest @types/node
# ============================================
# PART 7: THE UPDATED MANIFEST
# ============================================
# Yarn writes exact ranges to package.json.
# The lockfile pins exact resolved versions.
cat package.json
# {
# "dependencies": {
# "express": "^4.18.2"
# },
# "devDependencies": {
# "typescript": "^5.4.0",
# "jest": "^29.7.0"
# }
# }
# ============================================
# PART 8: WORKSPACE ROOT INITIALIZATION
# ============================================
# For a monorepo, the -w flag creates a root manifest.
mkdir my-monorepo && cd my-monorepo
yarn init -w
# This creates packages/ and configures workspaces.
cat package.json
# {
# "name": "my-monorepo",
# "private": true,
# "workspaces": ["packages/*"]
# }
# ============================================
# PART 9: LOCKING THE PACKAGE MANAGER
# ============================================
# The packageManager field pins the Yarn version.
# Corepack uses this to route to the correct binary.
{
"packageManager": "yarn@4.5.0"
}
The nine parts above trace the lifecycle of package.json: initialization with privacy enforced, identity fields added, entry points declared, scripts configured, dependencies installed and recorded, workspace setup for monorepos, and the package manager version locked. Each step writes to the same file, building a complete manifest.
Quick Reference
yarn init Options
| Option | Effect |
|---|---|
yarn init | Create a public package |
yarn init -p | Create a private package ("private": true) |
yarn init -w | Create a workspace root with packages/ |
yarn init -i=latest | Lock the Yarn version in the project |
yarn init -n my-name | Set the package name explicitly |
Essential Manifest Fields
| Field | Purpose |
|---|---|
name | Package identifier, lowercase, URL-safe |
version | Semver version, required for publishing |
private | Prevents accidental publication |
license | SPDX identifier, legal permission |
main | Entry point for Node.js resolution |
exports | Modern conditional entry points |
scripts | Commands runnable via yarn run |
bin | Expose executables to the shell |
Dependency Types
| Field | When Used |
|---|---|
dependencies | Runtime requirements |
devDependencies | Build, test, lint tools |
peerDependencies | Consumer-provided packages |
optionalDependencies | Platform-specific, may fail |
Best Practices
โ Do This:
yarn init -p
<!-- Applications should always be private --> # โ
{
"license": "MIT"
}
<!-- Legal clarity from the first commit --> # โ
{
"exports": {
".": "./dist/index.js"
}
}
<!-- Modern entry point declaration --> # โ
{
"packageManager": "yarn@4.5.0"
}
<!-- Pin the package manager version --> # โ
โ Don’t Do This:
{
"name": "My Package"
}
<!-- Uppercase and spaces break resolution --> # โ
{
"version": "latest"
}
<!-- Version must be semver, not a tag --> # โ
{
"dependencies": {
"typescript": "^5.0.0"
}
}
<!-- Build tools belong in devDependencies --> # โ
{
"private": false
}
<!-- Published by mistake if flag omitted --> # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Package cannot be published | name has uppercase or spaces | Use lowercase, URL-safe characters |
| Import fails in consumer | Missing main or exports | Declare entry point explicitly |
| Dev tools installed in production | Build tools in dependencies | Move to devDependencies |
| Accidental publication | private not set | Add "private": true for apps |
| Workspace resolution fails | workspaces field missing | Use yarn init -w or add manually |
Real-World Examples
1. Private Application
{
"name": "my-app",
"version": "0.1.0",
"private": true
}
2. Published Library
{
"name": "@myorg/utils",
"version": "1.0.0",
"license": "MIT",
"main": "./dist/index.js"
}
3. CLI Tool
{
"name": "my-cli",
"bin": {
"my-command": "./bin/cli.js"
}
}
4. Workspace Root
{
"private": true,
"workspaces": ["packages/*"]
}
5. Conditional Exports
{
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
6. Peer Dependency
{
"peerDependencies": {
"react": "^19.0.0"
}
}
7. Scripts
{
"scripts": {
"dev": "node src/server.js",
"test": "jest"
}
}
8. Platform Constraint
{
"os": ["linux", "darwin"],
"cpu": ["x64", "arm64"]
}
9. Package Manager Lock
{
"packageManager": "yarn@4.5.0"
}
10. Author Metadata
{
"author": "Alex Chen <alex@example.com>"
}
Visual
The Manifest as Configuration Hub
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ package.json โ
โ โ
โ Identity โ name, version, license โ
โ Entry Points โ main, exports, bin โ
โ Dependencies โ deps, devDeps, peers โ
โ Behavior โ scripts, type, os/cpu โ
โ Tooling โ packageManager, workspacesโ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Every tool reads this file โ
โ โ
โ Node.js โ main/exports for resolution โ
โ Yarn โ deps, scripts, workspaces โ
โ Corepack โ packageManager version โ
โ CI โ scripts, engines โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The manifest is not just for Yarn. It is the single source of truth that every tool in the JavaScript ecosystem consults.
Initialization Decision Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ What type of project? โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโดโโโโโโโโโโโ
โ โ
โผ โผ
โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ
โ Application โ โ Library โ
โ (not โ โ (will be โ
โ published) โ โ published) โ
โโโโโโโโโฌโโโโโโโโ โโโโโโโโโฌโโโโโโโโ
โ โ
โผ โผ
โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ
โ yarn init -p โ โ yarn init โ
โ โ โ โ
โ private: true โ โ license: MIT โ
โ โ โ main: index.jsโ
โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโ
The -p flag is the single most important decision at initialization. It encodes the project’s intent permanently.
Summary
| Item | Value |
|---|---|
| Init command | yarn init |
| Private flag | yarn init -p |
| Workspace flag | yarn init -w |
| Version lock | yarn init -i=latest |
| Required fields | name, version |
| Entry point | main or exports |
| Legal clarity | license |
| Publication guard | private |
| Modern resolution | exports over main |
Key takeaways:
yarn initcreates the manifest, not the project structure. It generatespackage.jsonand an.editorconfig, but no source files or directories .- The
-pflag is essential for applications. It sets"private": true, preventing accidental publication and enabling workspace features . nameandversionare the only truly required fields. Without them, the package cannot be installed as a dependency .exportsreplacesmainfor modern packages. It provides conditional resolution and is the official entry point mechanism in Node.js .- Dependency fields have distinct semantics. Runtime code goes in
dependencies, build tooling indevDependencies, and shared runtime contracts inpeerDependencies. packageManagerpins the Yarn version. Corepack reads this field to route commands to the correct binary, ensuring reproducibility across machines .
Remember: package.json is the contract. It tells Yarn what to install, tells Node.js what to load, tells Corepack which version to run, and tells other developers what the project is and how to use it. Running yarn init is the first step, but the decisions made in those first fieldsโprivate or public, library or applicationโshape every subsequent configuration choice.
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!