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
- Does this package already exist?
- What problem does it solve?
- Who is your target audience?
- What will make it stand out?
- What's the scope of functionality?
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



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
- Plan your package structure before coding
- Write comprehensive documentation
- Include tests for reliability
- Follow semantic versioning strictly
- Use proper build tools for distribution
- Secure your package with 2FA and audits
- Automate with CI/CD pipelines
- Maintain your package actively
- Listen to user feedback and iterate
- Consider the developer experience
Next Steps
- Create and publish your first package
- Set up automated testing and deployment
- Build a documentation website
- Start contributing to existing packages
- Learn about monorepo management
- Explore package bundling optimization
- Study popular packages for best practices