Package Configuration

Loading concept...

📦 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 does
  • main → The starting point (front door)
  • scripts → Commands you can run
  • dependencies → 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 .js files use modern imports
  • Use .cjs for old-style files
{
  "type": "commonjs"
}

When you add "type": "commonjs" (or leave it out):

  • All .js files use require()
  • Use .mjs for 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

  1. Must start with # - This is required!
  2. Only works inside your package - Private shortcuts
  3. 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

  1. package.json = Your project’s recipe book
  2. type: “module” = Enables modern imports
  3. node: prefix = Always use for built-ins
  4. exports = Control what others can import
  5. imports = Create shortcuts for yourself
  6. Conditional exports = Serve different code to different users

You’re now a package configuration pro! 🎉

Loading story...

No Story Available

This concept doesn't have a story yet.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

Interactive Preview

Interactive - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Interactive Content

This concept doesn't have interactive content yet.

Cheatsheet Preview

Cheatsheet - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Cheatsheet Available

This concept doesn't have a cheatsheet yet.

Quiz Preview

Quiz - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.

No Quiz Available

This concept doesn't have a quiz yet.