| |

Yarn 03 ๐Ÿงถ Initializing Projects and package.json

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

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

OptionEffect
yarn initCreate a public package
yarn init -pCreate a private package ("private": true)
yarn init -wCreate a workspace root with packages/
yarn init -i=latestLock the Yarn version in the project
yarn init -n my-nameSet the package name explicitly

Essential Manifest Fields

FieldPurpose
namePackage identifier, lowercase, URL-safe
versionSemver version, required for publishing
privatePrevents accidental publication
licenseSPDX identifier, legal permission
mainEntry point for Node.js resolution
exportsModern conditional entry points
scriptsCommands runnable via yarn run
binExpose executables to the shell

Dependency Types

FieldWhen Used
dependenciesRuntime requirements
devDependenciesBuild, test, lint tools
peerDependenciesConsumer-provided packages
optionalDependenciesPlatform-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

PitfallWhy It HappensFix
Package cannot be publishedname has uppercase or spacesUse lowercase, URL-safe characters
Import fails in consumerMissing main or exportsDeclare entry point explicitly
Dev tools installed in productionBuild tools in dependenciesMove to devDependencies
Accidental publicationprivate not setAdd "private": true for apps
Workspace resolution failsworkspaces field missingUse 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

ItemValue
Init commandyarn init
Private flagyarn init -p
Workspace flagyarn init -w
Version lockyarn init -i=latest
Required fieldsname, version
Entry pointmain or exports
Legal claritylicense
Publication guardprivate
Modern resolutionexports over main

Key takeaways:

  • yarn init creates the manifest, not the project structure. It generates package.json and an .editorconfig, but no source files or directories .
  • The -p flag is essential for applications. It sets "private": true, preventing accidental publication and enabling workspace features .
  • name and version are the only truly required fields. Without them, the package cannot be installed as a dependency .
  • exports replaces main for 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 in devDependencies, and shared runtime contracts in peerDependencies .
  • packageManager pins 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.

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!