Creating and Publishing NPM Packages

Share Your Code with the World

Becoming an NPM Author

Publishing your first NPM package is like opening your own store in a global marketplace. You're not just writing code anymore - you're creating tools that developers worldwide can use. Today, we'll learn how to package your code professionally, publish it to NPM, and maintain it like a pro!

The Package Creation Journey

graph TD A[Idea] --> B[Plan Package] B --> C[Create Structure] C --> D[Write Code] D --> E[Add Documentation] E --> F[Test Package] F --> G[Prepare for Publishing] G --> H[Publish to NPM] H --> I[Maintain & Update] style A fill:#f9f,stroke:#333 style H fill:#9f9,stroke:#333 style I fill:#ff9,stroke:#333

Planning Your Package

Before You Start Coding

Choosing a Package Name

// Check if name is available
npm info your-package-name

// Good package names:
"date-formatter"         // Clear and descriptive
"@myorg/utils"          // Scoped package
"react-carousel"        // Framework-specific
"express-validator"     // Purpose is obvious

// Bad package names:
"utils"                 // Too generic
"my-awesome-package"    // Not descriptive
"xYz123"               // Meaningless
"jQuery"               // Misleading/trademark issues

// Naming conventions:
// - Use lowercase
// - Use hyphens for spaces
// - Be descriptive but concise
// - Avoid trademarks
// - Consider SEO

Package Scope

// Unscoped packages (public namespace)
"lodash"
"express"
"react"

// Scoped packages (organizational namespace)
"@babel/core"
"@types/node"
"@testing-library/react"

// Creating a scoped package
npm init --scope=@myusername

// Benefits of scoped packages:
// - Namespace protection
// - Clear ownership
// - Grouping related packages
// - Private packages (paid feature)

Package Structure

Recommended Directory Structure

my-awesome-package/
├── src/                    # Source code
│   ├── index.js           # Main entry point
│   ├── utils/             # Utility functions
│   └── components/        # If applicable
├── test/                   # Test files
│   ├── index.test.js
│   └── utils.test.js
├── examples/               # Usage examples
│   └── basic-usage.js
├── docs/                   # Additional documentation
│   └── API.md
├── .github/               # GitHub specific files
│   └── workflows/         # CI/CD workflows
├── dist/                  # Built files (gitignored)
├── .gitignore
├── .npmignore             # NPM-specific ignore
├── .eslintrc.json         # Linting configuration
├── .prettierrc            # Code formatting
├── CHANGELOG.md           # Version history
├── CONTRIBUTING.md        # Contribution guidelines
├── LICENSE                # License file
├── README.md              # Main documentation
├── package.json           # Package configuration
└── tsconfig.json          # If using TypeScript

Essential Files

// .gitignore
node_modules/
dist/
coverage/
.env
.DS_Store
*.log

// .npmignore (if needed - otherwise uses .gitignore)
src/
test/
examples/
docs/
.github/
.eslintrc.json
.prettierrc
tsconfig.json
*.test.js

// Alternative: use "files" in package.json
{
  "files": [
    "dist/",
    "README.md",
    "LICENSE"
  ]
}

Writing Your Package Code

Entry Point (index.js)

// src/index.js - CommonJS
const { formatDate } = require('./utils/dateFormatter');
const { validateEmail } = require('./utils/validators');

module.exports = {
  formatDate,
  validateEmail,
  // Re-export other utilities
};

// src/index.js - ES Modules
export { formatDate } from './utils/dateFormatter.js';
export { validateEmail } from './utils/validators.js';
export { default as Logger } from './Logger.js';

// Supporting both CommonJS and ES Modules
// package.json
{
  "main": "dist/index.cjs",     // CommonJS entry
  "module": "dist/index.mjs",   // ES Module entry
  "exports": {
    ".": {
      "require": "./dist/index.cjs",
      "import": "./dist/index.mjs"
    }
  }
}

Example Package: String Utilities

// src/stringUtils.js
/**
 * Capitalizes the first letter of a string
 * @param {string} str - The input string
 * @returns {string} The capitalized string
 */
function capitalize(str) {
  if (typeof str !== 'string') {
    throw new TypeError('Input must be a string');
  }
  return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
 * Converts a string to camelCase
 * @param {string} str - The input string
 * @returns {string} The camelCased string
 */
function toCamelCase(str) {
  return str
    .toLowerCase()
    .replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase());
}

/**
 * Truncates a string to a specified length
 * @param {string} str - The input string
 * @param {number} length - Maximum length
 * @param {string} [ending='...'] - String to append if truncated
 * @returns {string} The truncated string
 */
function truncate(str, length, ending = '...') {
  if (str.length <= length) return str;
  return str.slice(0, length - ending.length) + ending;
}

module.exports = {
  capitalize,
  toCamelCase,
  truncate
};

Adding TypeScript Support

TypeScript Configuration

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2018",
    "module": "CommonJS",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "test", "dist"]
}

// src/index.ts
export interface StringUtilOptions {
  preserveWhitespace?: boolean;
  caseSensitive?: boolean;
}

export function capitalize(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

export function truncate(
  str: string, 
  length: number, 
  ending: string = '...'
): string {
  if (str.length <= length) return str;
  return str.slice(0, length - ending.length) + ending;
}

// package.json for TypeScript package
{
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "prepare": "npm run build"
  }
}

Type Definitions for JavaScript

// index.d.ts - Type definitions for JavaScript package
declare module 'my-string-utils' {
  export function capitalize(str: string): string;
  
  export function toCamelCase(str: string): string;
  
  export function truncate(
    str: string, 
    length: number, 
    ending?: string
  ): string;
  
  export interface StringUtilOptions {
    preserveWhitespace?: boolean;
    caseSensitive?: boolean;
  }
}

// package.json reference
{
  "types": "index.d.ts",
  "files": [
    "dist/",
    "index.d.ts"
  ]
}

Writing Documentation

README.md Template

# Package Name

![npm version](https://img.shields.io/npm/v/package-name)
![npm downloads](https://img.shields.io/npm/dm/package-name)
![license](https://img.shields.io/npm/l/package-name)

A brief description of what your package does and why it's useful.

## Features

- ✨ Feature 1
- 🚀 Feature 2
- 💪 Feature 3

## Installation

```bash
npm install package-name
# or
yarn add package-name
```

## Quick Start

```javascript
const { capitalize, truncate } = require('package-name');

const result = capitalize('hello world');
console.log(result); // 'Hello world'

const shortened = truncate('This is a long string', 10);
console.log(shortened); // 'This is...'
```

## API Reference

### `capitalize(str: string): string`

Capitalizes the first letter of a string.

**Parameters:**
- `str` (string): The input string

**Returns:** string - The capitalized string

**Example:**
```javascript
capitalize('hello'); // 'Hello'
```

### `truncate(str: string, length: number, ending?: string): string`

Truncates a string to specified length.

**Parameters:**
- `str` (string): The input string
- `length` (number): Maximum length
- `ending` (string, optional): String to append if truncated. Default: '...'

**Returns:** string - The truncated string

**Example:**
```javascript
truncate('Hello world', 5); // 'He...'
truncate('Hello world', 5, '…'); // 'Hell…'
```

## Configuration

```javascript
const utils = require('package-name');

// Configure globally
utils.configure({
  defaultEncoding: 'utf-8'
});
```

## Examples

See the [examples](./examples) directory for more usage examples.

## Contributing

Contributions are welcome! Please read our [contributing guidelines](CONTRIBUTING.md) first.

## License

MIT © [Your Name](https://github.com/username)

CHANGELOG.md

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- New `slugify` function for URL-friendly strings

### Changed
- Improved performance of `toCamelCase` function

## [1.1.0] - 2024-01-15

### Added
- TypeScript support with type definitions
- New `truncate` function with custom endings

### Fixed
- Bug in `capitalize` with empty strings

## [1.0.1] - 2024-01-01

### Fixed
- Documentation typos
- Edge case in `toCamelCase` with numbers

## [1.0.0] - 2023-12-15

### Added
- Initial release
- `capitalize` function
- `toCamelCase` function
- Basic documentation

Testing Your Package

Unit Tests with Jest

// test/stringUtils.test.js
const { capitalize, toCamelCase, truncate } = require('../src/stringUtils');

describe('capitalize', () => {
  test('capitalizes first letter', () => {
    expect(capitalize('hello')).toBe('Hello');
  });

  test('handles empty string', () => {
    expect(capitalize('')).toBe('');
  });

  test('throws on non-string input', () => {
    expect(() => capitalize(123)).toThrow(TypeError);
  });
});

describe('toCamelCase', () => {
  test('converts to camelCase', () => {
    expect(toCamelCase('hello world')).toBe('helloWorld');
    expect(toCamelCase('hello-world')).toBe('helloWorld');
    expect(toCamelCase('hello_world')).toBe('helloWorld');
  });
});

describe('truncate', () => {
  test('truncates long strings', () => {
    expect(truncate('Hello world', 5)).toBe('He...');
  });

  test('preserves short strings', () => {
    expect(truncate('Hi', 5)).toBe('Hi');
  });

  test('custom ending', () => {
    expect(truncate('Hello world', 6, '…')).toBe('Hello…');
  });
});

// package.json test configuration
{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  },
  "jest": {
    "testEnvironment": "node",
    "coverageDirectory": "coverage",
    "collectCoverageFrom": [
      "src/**/*.js",
      "!src/index.js"
    ]
  }
}

Integration Tests

// test/integration.test.js
const myPackage = require('../dist');

describe('Package Integration', () => {
  test('exports all functions', () => {
    expect(myPackage.capitalize).toBeDefined();
    expect(myPackage.toCamelCase).toBeDefined();
    expect(myPackage.truncate).toBeDefined();
  });

  test('functions work together', () => {
    const input = 'hello world example';
    const capitalized = myPackage.capitalize(input);
    const camelCased = myPackage.toCamelCase(capitalized);
    
    expect(camelCased).toBe('helloWorldExample');
  });
});

// Test package installation
// test/install.test.js
const { execSync } = require('child_process');
const path = require('path');
const fs = require('fs');

describe('Package Installation', () => {
  const testDir = path.join(__dirname, 'test-install');
  
  beforeAll(() => {
    // Create test directory
    fs.mkdirSync(testDir, { recursive: true });
    
    // Pack the package
    execSync('npm pack', { cwd: path.join(__dirname, '..') });
    
    // Install in test directory
    const packageFile = fs.readdirSync(path.join(__dirname, '..'))
      .find(file => file.endsWith('.tgz'));
    
    execSync(`npm init -y`, { cwd: testDir });
    execSync(`npm install ../${packageFile}`, { cwd: testDir });
  });

  test('package installs correctly', () => {
    const packageJson = require(path.join(testDir, 'package.json'));
    expect(packageJson.dependencies).toHaveProperty('your-package-name');
  });

  afterAll(() => {
    // Cleanup
    fs.rmSync(testDir, { recursive: true, force: true });
  });
});

Preparing for Publication

Pre-publish Checklist

// package.json essential fields
{
  "name": "@myorg/string-utils",
  "version": "1.0.0",
  "description": "Utility functions for string manipulation",
  "keywords": ["string", "utils", "manipulation", "format"],
  "homepage": "https://github.com/myorg/string-utils#readme",
  "bugs": {
    "url": "https://github.com/myorg/string-utils/issues"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/myorg/string-utils.git"
  },
  "license": "MIT",
  "author": "Your Name ",
  "main": "dist/index.js",
  "module": "dist/index.esm.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist/",
    "README.md",
    "LICENSE"
  ],
  "scripts": {
    "build": "rollup -c",
    "test": "jest",
    "lint": "eslint src",
    "prepublishOnly": "npm run test && npm run build",
    "prepare": "npm run build"
  },
  "engines": {
    "node": ">=12.0.0"
  }
}

// Build script (rollup.config.js)
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';

export default {
  input: 'src/index.js',
  output: [
    {
      file: 'dist/index.js',
      format: 'cjs'
    },
    {
      file: 'dist/index.esm.js',
      format: 'es'
    },
    {
      file: 'dist/index.min.js',
      format: 'umd',
      name: 'StringUtils',
      plugins: [terser()]
    }
  ],
  plugins: [resolve(), commonjs()]
};

License Selection

// MIT License (most permissive)
MIT License

Copyright (c) 2024 Your Name

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

[Full license text...]

// Choose license based on needs:
// - MIT: Very permissive, commercial use allowed
// - Apache 2.0: Similar to MIT with patent protection
// - GPL-3.0: Copyleft, requires derivative works to be open source
// - ISC: Simpler alternative to MIT
// - BSD-3-Clause: Similar to MIT with no endorsement clause

Publishing Process

flowchart TD A[npm login] --> B[Verify package.json] B --> C[Run tests] C --> D[Build package] D --> E[Check package contents] E --> F[npm publish] F --> G[Verify on npmjs.com] H[npm version patch/minor/major] --> I[Git tag created] I --> F style F fill:#9f9,stroke:#333,stroke-width:2px

Publishing Commands

# Login to npm
npm login
# Enter username, password, email, and 2FA code

# Verify you're logged in
npm whoami

# Check what will be published
npm pack --dry-run
# or
npm publish --dry-run

# View package contents
tar -tf package-name-1.0.0.tgz

# Publish package
npm publish

# Publish scoped package publicly
npm publish --access=public

# Update version and publish
npm version patch  # 1.0.0 -> 1.0.1
npm version minor  # 1.0.0 -> 1.1.0
npm version major  # 1.0.0 -> 2.0.0
npm publish

# Publish with tag (e.g., beta)
npm publish --tag beta

# Set latest tag
npm dist-tag add package-name@1.0.0 latest

Two-Factor Authentication

# Enable 2FA for auth and publish
npm profile enable-2fa auth-and-writes

# Enable 2FA for auth only
npm profile enable-2fa auth-only

# Publish with 2FA
npm publish --otp=123456

# Automate with environment variable
NPM_CONFIG_OTP=123456 npm publish

Post-Publication Tasks

Verify Publication

# Check package on npm
npm info your-package-name

# View all versions
npm view your-package-name versions

# Install and test
mkdir test-install
cd test-install
npm init -y
npm install your-package-name

// Test file
const package = require('your-package-name');
console.log(package.someFunction());

Documentation and Promotion

// Update GitHub repository
git push origin main --tags

// Create GitHub release
# 1. Go to GitHub repository
# 2. Click "Releases"
# 3. Click "Draft a new release"
# 4. Select the version tag
# 5. Add release notes from CHANGELOG.md

// Update documentation site
npm run docs:build
npm run docs:deploy

// Announce on social media
"🚀 Just published my-package v1.0.0!

✨ Features:
- Feature 1
- Feature 2

📦 npm install my-package
📚 Docs: https://link-to-docs

#JavaScript #OpenSource #NPM"

Maintaining Your Package

Version Management

// Semantic versioning rules
// MAJOR.MINOR.PATCH

// Patch version (bug fixes)
npm version patch -m "Fix: %s"
// 1.0.0 -> 1.0.1

// Minor version (new features, backward compatible)
npm version minor -m "Feat: %s"
// 1.0.0 -> 1.1.0

// Major version (breaking changes)
npm version major -m "Breaking: %s"
// 1.0.0 -> 2.0.0

// Pre-release versions
npm version prerelease --preid=beta
// 1.0.0 -> 1.0.1-beta.0

npm version prerelease
// 1.0.1-beta.0 -> 1.0.1-beta.1

// Custom version
npm version 1.2.3-beta.4

Deprecating Versions

# Deprecate a specific version
npm deprecate package-name@1.0.0 "Critical bug, please upgrade to 1.0.1"

# Deprecate a range of versions
npm deprecate package-name@"< 2.0.0" "Version 1.x is no longer supported"

# Un-deprecate a version
npm deprecate package-name@1.0.0 ""

# Deprecate entire package
npm deprecate package-name "This package is no longer maintained"

Handling Issues and PRs

// GitHub issue template (.github/ISSUE_TEMPLATE/bug_report.md)
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---

**Describe the bug**
A clear description of the bug.

**To Reproduce**
Steps to reproduce:
1. ...
2. ...

**Expected behavior**
What you expected to happen.

**Environment:**
 - Node version: [e.g. 16.0.0]
 - Package version: [e.g. 1.0.0]
 - OS: [e.g. macOS, Windows]

**Additional context**
Add any other context here.

// Pull request template (.github/PULL_REQUEST_TEMPLATE.md)
## Description
Brief description of changes.

## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Checklist
- [ ] Tests pass
- [ ] Documentation updated
- [ ] CHANGELOG.md updated
- [ ] Version bumped appropriately

Security Considerations

Security Best Practices

// 1. Never publish secrets
// .npmignore or "files" in package.json
.env
.env.*
config/secrets.js
*.pem
*.key

// 2. Use npm audit regularly
npm audit
npm audit fix

// 3. Enable 2FA
npm profile enable-2fa auth-and-writes

// 4. Use .npmrc for security settings
# .npmrc
audit-level=moderate
fund=false
save-exact=true

// 5. Validate input in your package
function processUserInput(input) {
  // Sanitize and validate
  if (typeof input !== 'string') {
    throw new TypeError('Input must be a string');
  }
  
  // Prevent command injection
  const sanitized = input.replace(/[;&|`<>]/g, '');
  
  return sanitized;
}

// 6. Regular dependency updates
npm outdated
npm update

// 7. Use lockfile for CI/CD
npm ci  # Instead of npm install

Security Vulnerability Reporting

// SECURITY.md
# Security Policy

## Supported Versions

| Version | Supported          |
| ------- | ------------------ |
| 2.x.x   | :white_check_mark: |
| 1.x.x   | :x:                |

## Reporting a Vulnerability

Please report security vulnerabilities to security@example.com

You can expect:
1. Acknowledgment within 24 hours
2. Regular updates on progress
3. Credit in the security advisory (if desired)

Please do not:
- Open public issues for security vulnerabilities
- Disclose the vulnerability publicly before a fix

## Security Update Process

1. Vulnerability reported
2. Confirm and reproduce
3. Develop fix
4. Release patch version
5. Publish security advisory

Continuous Integration

GitHub Actions Workflow

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [14.x, 16.x, 18.x]
    
    steps:
    - uses: actions/checkout@v3
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v3
      with:
        node-version: ${{ matrix.node-version }}
    
    - run: npm ci
    - run: npm run lint
    - run: npm test
    - run: npm run build

  publish:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
    
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-node@v3
      with:
        node-version: '16.x'
        registry-url: 'https://registry.npmjs.org'
    
    - run: npm ci
    - run: npm run build
    - run: npm publish
      env:
        NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Automated Releases

// Release workflow with semantic-release
# .github/workflows/release.yml
name: Release

on:
  push:
    branches:
      - main

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '16.x'
      
      - run: npm ci
      - run: npm run build
      - run: npm test
      
      - name: Semantic Release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: npx semantic-release

// .releaserc.json
{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    "@semantic-release/changelog",
    "@semantic-release/npm",
    "@semantic-release/github",
    "@semantic-release/git"
  ]
}

Package Analytics

Monitoring Package Usage

// Check download statistics
npm view your-package-name

// Weekly downloads
https://www.npmjs.com/package/your-package-name

// Detailed analytics
https://npm-stat.com/charts.html?package=your-package-name

// Package quality metrics
https://snyk.io/advisor/npm-package/your-package-name

// Dependencies using your package
https://www.npmjs.com/browse/depended/your-package-name

// GitHub insights
- Stars and forks
- Issues and PRs
- Contributors
- Traffic data

User Feedback Collection

// Add feedback section to README
## Feedback

We'd love to hear your feedback! Please:
- ⭐ Star this repo if you find it useful
- 🐛 Report bugs by creating an issue
- 💡 Request features through discussions
- 🤝 Contribute improvements via pull requests

// Create GitHub discussions template
# .github/DISCUSSION_TEMPLATE/ideas.yml
title: "[IDEA] "
labels: ["enhancement"]
body:
  - type: textarea
    attributes:
      label: Description
      description: Describe your idea
    validations:
      required: true
  - type: textarea
    attributes:
      label: Use Case
      description: How would this be used?
    validations:
      required: true
      
// Add analytics to your package (optional)
const analytics = require('simple-analytics');

function trackUsage(functionName) {
  if (process.env.NODE_ENV === 'production') {
    analytics.track('function-usage', {
      function: functionName,
      version: require('./package.json').version
    });
  }
}

// Usage
function capitalize(str) {
  trackUsage('capitalize');
  return str.charAt(0).toUpperCase() + str.slice(1);
}

Common Publishing Mistakes

Mistakes to Avoid

graph TD A[Common Mistakes] --> B[Secrets in Code] A --> C[Missing Documentation] A --> D[No Tests] A --> E[Wrong Files Published] A --> F[Breaking Changes in Minor] A --> G[Forgetting Build Step] B --> B1[Use .npmignore] C --> C1[Write good README] D --> D1[Add test suite] E --> E1[Use files field] F --> F1[Follow SemVer] G --> G1[Use prepare script] style A fill:#f96,stroke:#333

Pre-publish Checklist

// Create a pre-publish checklist
#!/bin/bash
# prepublish-check.sh

echo "🔍 Pre-publish checks..."

# Check for console.logs
if grep -r "console.log" src/; then
  echo "❌ Found console.log statements"
  exit 1
fi

# Check for sensitive data
if grep -r "password\|secret\|key\|token" src/; then
  echo "❌ Possible sensitive data found"
  exit 1
fi

# Ensure tests pass
npm test || exit 1

# Check documentation
if [ ! -f "README.md" ]; then
  echo "❌ README.md is missing"
  exit 1
fi

# Verify build
npm run build || exit 1

# Check package size
size=$(npm pack --dry-run 2>&1 | grep "package size:" | cut -d: -f2)
echo "📦 Package size: $size"

echo "✅ All checks passed!"

Real-World Package Example

Complete Package Structure

date-utils/
├── .github/
│   ├── workflows/
│   │   ├── ci.yml
│   │   └── release.yml
│   └── ISSUE_TEMPLATE/
│       ├── bug_report.md
│       └── feature_request.md
├── src/
│   ├── index.js
│   ├── formatters/
│   │   ├── date.js
│   │   └── time.js
│   └── parsers/
│       ├── date.js
│       └── time.js
├── test/
│   ├── formatters.test.js
│   └── parsers.test.js
├── examples/
│   ├── basic.js
│   └── advanced.js
├── docs/
│   ├── API.md
│   └── EXAMPLES.md
├── .eslintrc.json
├── .gitignore
├── .npmignore
├── .prettierrc
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── package.json
└── rollup.config.js

Complete package.json Example

{
  "name": "@myorg/date-utils",
  "version": "1.0.0",
  "description": "Lightweight date manipulation utilities",
  "keywords": [
    "date",
    "time",
    "format",
    "parse",
    "utility"
  ],
  "homepage": "https://github.com/myorg/date-utils#readme",
  "bugs": {
    "url": "https://github.com/myorg/date-utils/issues"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/myorg/date-utils.git"
  },
  "license": "MIT",
  "author": "Your Name ",
  "sideEffects": false,
  "main": "dist/index.js",
  "module": "dist/index.esm.js",
  "browser": "dist/index.umd.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist",
    "src",
    "README.md",
    "LICENSE"
  ],
  "scripts": {
    "build": "rollup -c",
    "test": "jest",
    "test:coverage": "jest --coverage",
    "lint": "eslint src test",
    "format": "prettier --write \"{src,test}/**/*.js\"",
    "prepare": "npm run build",
    "prepublishOnly": "npm test && npm run lint",
    "version": "npm run format && git add -A src",
    "postversion": "git push && git push --tags"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@babel/preset-env": "^7.20.0",
    "@rollup/plugin-babel": "^6.0.0",
    "@rollup/plugin-commonjs": "^24.0.0",
    "@rollup/plugin-node-resolve": "^15.0.0",
    "eslint": "^8.0.0",
    "jest": "^29.0.0",
    "prettier": "^2.8.0",
    "rollup": "^3.0.0"
  },
  "engines": {
    "node": ">=14.0.0"
  },
  "publishConfig": {
    "access": "public"
  }
}

Practice Exercises

Exercise 1: Create Your First Package

// Task: Create a simple string utility package
// Requirements:
// 1. Create a package called "string-helpers"
// 2. Implement these functions:
//    - reverseString(str)
//    - countWords(str)
//    - toTitleCase(str)
// 3. Add proper documentation
// 4. Include unit tests
// 5. Prepare for publishing (don't actually publish)

// Steps:
// 1. Initialize project: npm init
// 2. Create src/index.js with functions
// 3. Write tests in test/
// 4. Add README.md with examples
// 5. Configure package.json properly
// 6. Run npm pack to test package

Exercise 2: Package Maintenance

// Scenario: You've published version 1.0.0 of your package
// Now you need to:
// 1. Fix a bug in the countWords function
// 2. Add a new feature: isPalindrome function
// 3. Update documentation
// 4. Decide on version number (patch, minor, or major?)
// 5. Publish the update

// Questions to consider:
// - Is the bug fix backward compatible?
// - Does the new feature break existing code?
// - How should you update the CHANGELOG?
// - What commands will you use to publish?

Key Takeaways

Next Steps

Additional Resources