📦 Package Configuration: Your Project’s Recipe Book
Imagine you’re a chef with a secret recipe book. This book tells everyone:
- What ingredients you need
- How to cook the dish
- What the final meal looks like
In Node.js, package.json is exactly that recipe book for your project!
🏠The package.json Structure
Think of package.json as your project’s ID card. Just like your ID has your name, photo, and address, package.json tells Node.js everything about your project.
The Basic Recipe Card
{
"name": "my-awesome-app",
"version": "1.0.0",
"description": "A cool app",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"express": "^4.18.0"
}
}
What each part means:
name→ Your project’s name (like “Chocolate Cake”)version→ Which version (1.0.0 = first release!)description→ What it doesmain→ The starting point (front door)scripts→ Commands you can rundependencies→ Ingredients you need
🚪 Entry Points Configuration
Imagine your house has multiple doors:
- Front door for guests
- Back door for family
- Garage door for cars
Your package can have different entry points for different situations!
The main Field (Front Door)
{
"main": "dist/index.js"
}
This is the default door. When someone writes:
const myPackage = require('my-package');
Node.js opens dist/index.js.
The module Field (Modern Door)
{
"main": "dist/index.cjs",
"module": "dist/index.mjs"
}
Some tools prefer the modern ESM format. The module field points them there!
⚡ Type Field for ESM
Here’s a big question: Does your project speak old JavaScript or modern JavaScript?
The Two Languages
| Old Style (CommonJS) | Modern Style (ESM) |
|---|---|
require() |
import |
module.exports |
export |
.js or .cjs |
.mjs or .js |
Choosing Your Language
{
"type": "module"
}
When you add "type": "module":
- All
.jsfiles use modern imports - Use
.cjsfor old-style files
{
"type": "commonjs"
}
When you add "type": "commonjs" (or leave it out):
- All
.jsfiles use require() - Use
.mjsfor modern files
Quick Visual
graph TD A[package.json] --> B{type field?} B -->|module| C[.js = ESM] B -->|commonjs| D[.js = CommonJS] C --> E[Use .cjs for CommonJS] D --> F[Use .mjs for ESM]
🏷️ The node: Prefix for Built-ins
Node.js comes with free tools built right in! Things like:
- Reading files (
fs) - Working with paths (
path) - Making web requests (
http)
The Problem
What if someone creates a package also called fs? Confusion!
The Solution: node: prefix
// Old way (works but risky)
import fs from 'fs';
// New way (crystal clear!)
import fs from 'node:fs';
The node: prefix is like saying:
“I want the REAL fs from Node.js, not some impostor!”
Common Built-in Modules
import fs from 'node:fs';
import path from 'node:path';
import http from 'node:http';
import crypto from 'node:crypto';
import url from 'node:url';
Pro tip: Always use node: prefix. It’s clearer and slightly faster!
📤 Package Exports Field
The exports field is like a fancy reception desk. It controls exactly what parts of your package visitors can access.
Basic Exports
{
"name": "my-library",
"exports": "./dist/index.js"
}
This is simple: one door for everyone.
Multiple Exports (Multiple Doors)
{
"exports": {
".": "./dist/main.js",
"./utils": "./dist/utils.js",
"./helpers": "./dist/helpers.js"
}
}
Now users can import specific parts:
import main from 'my-library';
import utils from 'my-library/utils';
import helpers from 'my-library/helpers';
Blocking Unwanted Access
{
"exports": {
".": "./dist/public.js",
"./internal/*": null
}
}
The null is like a “No Entry” sign!
📥 Package Imports Field
While exports controls what others can take from you, imports creates shortcuts for yourself!
The Problem
Long import paths are annoying:
// Yuck! So long!
import config from '../../../config/settings.js';
import utils from '../../../lib/utils/helpers.js';
The Solution: Import Maps
{
"imports": {
"#config": "./src/config/settings.js",
"#utils/*": "./src/lib/utils/*.js"
}
}
Now your imports are clean:
// Beautiful!
import config from '#config';
import helpers from '#utils/helpers';
Rules for Imports
- Must start with
#- This is required! - Only works inside your package - Private shortcuts
- Wildcards allowed - Use
*for patterns
graph LR A["#config"] -->|maps to| B["./src/config/settings.js"] C["#utils/math"] -->|maps to| D["./src/lib/utils/math.js"]
🎠Conditional Exports
This is where the magic happens! Conditional exports let your package shape-shift based on who’s using it.
The Conditions
| Condition | When It Activates |
|---|---|
import |
ESM import statement |
require |
CommonJS require() |
node |
Running in Node.js |
browser |
Running in browser |
default |
Fallback option |
Dual Package (ESM + CommonJS)
{
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"default": "./dist/index.cjs"
}
}
}
What happens:
import pkg from 'pkg'→ Gets.mjs(modern)require('pkg')→ Gets.cjs(classic)
Node vs Browser
{
"exports": {
".": {
"node": "./dist/node.js",
"browser": "./dist/browser.js",
"default": "./dist/node.js"
}
}
}
Combining Conditions
{
"exports": {
".": {
"node": {
"import": "./dist/node.mjs",
"require": "./dist/node.cjs"
},
"browser": {
"import": "./dist/browser.mjs",
"default": "./dist/browser.js"
}
}
}
}
graph TD A[Someone imports your package] --> B{Environment?} B -->|Node.js| C{Import type?} B -->|Browser| D{Import type?} C -->|import| E[node.mjs] C -->|require| F[node.cjs] D -->|import| G[browser.mjs] D -->|default| H[browser.js]
🎯 Putting It All Together
Here’s a complete professional package.json:
{
"name": "super-library",
"version": "2.0.0",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
},
"./utils": {
"import": "./dist/utils.mjs",
"require": "./dist/utils.cjs"
}
},
"imports": {
"#internal/*": "./src/internal/*.js"
}
}
🌟 Key Takeaways
- package.json = Your project’s recipe book
- type: “module” = Enables modern imports
- node: prefix = Always use for built-ins
- exports = Control what others can import
- imports = Create shortcuts for yourself
- Conditional exports = Serve different code to different users
You’re now a package configuration pro! 🎉