+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 97 of 355

๐Ÿ–ฅ ๏ธ CommonJS Modules: Node.js Compatibility Mastery

Master CommonJS modules in TypeScript for seamless Node.js compatibility, legacy code integration, and robust backend development ๐Ÿš€

๐Ÿš€Intermediate
22 min read

Prerequisites

  • ES6 modules and import/export patterns ๐Ÿ“ฆ
  • Basic Node.js understanding ๐Ÿ–ฅ๏ธ
  • TypeScript compilation basics โšก

What you'll learn

  • Master CommonJS require() and module.exports patterns ๐Ÿ–ฅ๏ธ
  • Configure TypeScript for Node.js compatibility ๐Ÿ› ๏ธ
  • Bridge ES modules and CommonJS seamlessly ๐ŸŒ‰
  • Handle legacy Node.js libraries with confidence โœจ

๐ŸŽฏ Introduction

Welcome to the world of CommonJS modules in TypeScript! ๐ŸŽ‰ In this guide, weโ€™ll explore how to work with Node.jsโ€™s traditional module system while maintaining TypeScriptโ€™s powerful type safety.

Youโ€™ll discover how to bridge the gap between modern ES6 modules and the CommonJS ecosystem that powers millions of Node.js applications. Whether youโ€™re working with legacy codebases ๐Ÿš๏ธ, integrating third-party libraries ๐Ÿ“š, or building robust backend services ๐Ÿ–ฅ๏ธ, mastering CommonJS compatibility is essential for real-world TypeScript development.

By the end of this tutorial, youโ€™ll be seamlessly switching between module systems like a true Node.js wizard! ๐Ÿง™โ€โ™‚๏ธ Letโ€™s dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š Understanding CommonJS

๐Ÿค” What is CommonJS?

CommonJS is like the original blueprint for JavaScript modules ๐Ÿ“‹. Think of it as the foundation that Node.js was built on - before ES6 modules existed, CommonJS was the way to organize and share code in JavaScript environments.

In Node.js terms, CommonJS provides:

  • โœจ Synchronous loading - modules load immediately when required
  • ๐Ÿš€ require() function - the classic way to import modules
  • ๐Ÿ›ก๏ธ module.exports - the standard way to export functionality
  • ๐Ÿ“ฆ Automatic wrapping - each file gets its own scope

๐Ÿ’ก Why Learn CommonJS with TypeScript?

Hereโ€™s why CommonJS knowledge is crucial:

  1. Legacy Compatibility ๐Ÿ”ง: Millions of Node.js packages use CommonJS
  2. Enterprise Codebases ๐Ÿข: Many production systems rely on CommonJS
  3. Library Integration ๐Ÿ“š: Many npm packages are still CommonJS-only
  4. Migration Scenarios ๐Ÿ”„: Converting from CommonJS to ES modules
  5. Universal Libraries ๐ŸŒ: Building packages that work everywhere

Real-world example: When building a Node.js API that uses Express.js ๐Ÿš‚, youโ€™ll often need to integrate with CommonJS libraries while writing modern TypeScript code!

๐Ÿ”ง Basic CommonJS Patterns

๐Ÿ“ require() and module.exports

Letโ€™s start with the fundamental CommonJS patterns:

// ๐Ÿ“ utils/math.ts - CommonJS exports
// ๐Ÿงฎ Traditional CommonJS export patterns

// Pattern 1: Direct assignment to module.exports
module.exports = {
  PI: 3.14159,
  E: 2.71828,
  
  // โž• Basic math operations
  add: (a: number, b: number): number => {
    return a + b;
  },
  
  // โœ–๏ธ Multiplication with logging
  multiply: (a: number, b: number): number => {
    console.log(`๐Ÿ”ข Multiplying ${a} ร— ${b}`);
    return a * b;
  }
};

// Pattern 2: Individual property exports
module.exports.subtract = (a: number, b: number): number => {
  return a - b;
};

module.exports.divide = (a: number, b: number): number => {
  if (b === 0) {
    throw new Error("Division by zero! ๐Ÿšซ");
  }
  return a / b;
};
// ๐Ÿ“ services/calculator.ts - CommonJS imports
// ๐ŸŽฏ CommonJS require syntax
const math = require('../utils/math');

class Calculator {
  // โž• Using imported functions
  calculate(operation: string, a: number, b: number): number {
    switch (operation) {
      case 'add':
        return math.add(a, b);
      case 'multiply':
        return math.multiply(a, b);
      case 'subtract':
        return math.subtract(a, b);
      case 'divide':
        return math.divide(a, b);
      default:
        throw new Error(`Unknown operation: ${operation} ๐Ÿคทโ€โ™‚๏ธ`);
    }
  }
  
  // ๐Ÿ”ต Circle area using imported constants
  getCircleArea(radius: number): number {
    return math.PI * math.multiply(radius, radius);
  }
}

// ๐Ÿš€ Export the calculator class
module.exports = Calculator;

๐Ÿ’ก Explanation: CommonJS uses require() for imports and module.exports for exports. Notice how we access properties from the exported object!

๐ŸŽฏ TypeScript Configuration for CommonJS

Configure TypeScript to work seamlessly with CommonJS:

// ๐Ÿ“ tsconfig.json - CommonJS configuration
{
  "compilerOptions": {
    "target": "ES2020",                    // ๐ŸŽฏ Modern JavaScript target
    "module": "CommonJS",                  // ๐Ÿ“ฆ Use CommonJS modules
    "moduleResolution": "node",            // ๐Ÿ” Node.js module resolution
    "esModuleInterop": true,              // ๐ŸŒ‰ Allow ES/CommonJS interop
    "allowSyntheticDefaultImports": true,  // โœจ Synthetic default imports
    "strict": true,                        // ๐Ÿ›ก๏ธ Maximum type safety
    "skipLibCheck": true,                  // โšก Skip lib type checking
    "forceConsistentCasingInFileNames": true, // ๐Ÿ“ Consistent file names
    "outDir": "./dist",                    // ๐Ÿ“ Output directory
    "rootDir": "./src",                    // ๐Ÿ“ Source directory
    "declaration": true,                   // ๐Ÿ“‹ Generate .d.ts files
    "declarationMap": true,                // ๐Ÿ—บ๏ธ Source maps for declarations
    "sourceMap": true                      // ๐Ÿ—บ๏ธ Source maps for debugging
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
// ๐Ÿ“ package.json - Node.js package configuration
{
  "name": "typescript-commonjs-example",
  "version": "1.0.0",
  "type": "commonjs",                    // ๐Ÿ“ฆ Explicit CommonJS declaration
  "main": "dist/index.js",               // ๐ŸŽฏ Entry point
  "types": "dist/index.d.ts",            // ๐Ÿ“‹ TypeScript definitions
  "scripts": {
    "build": "tsc",                      // ๐Ÿ”จ Build TypeScript
    "start": "node dist/index.js",       // ๐Ÿš€ Start application
    "dev": "ts-node src/index.ts",       // โšก Development mode
    "watch": "tsc --watch"               // ๐Ÿ‘€ Watch mode
  },
  "dependencies": {
    "express": "^4.18.2"                // ๐Ÿš‚ Example dependency
  },
  "devDependencies": {
    "@types/node": "^20.0.0",            // ๐Ÿ–ฅ๏ธ Node.js types
    "@types/express": "^4.17.17",        // ๐Ÿš‚ Express types
    "typescript": "^5.0.0",              // ๐Ÿ“˜ TypeScript compiler
    "ts-node": "^10.9.0"                 // โšก Development runner
  }
}

๐Ÿ”„ Mixed Module Patterns

Combining different export styles:

// ๐Ÿ“ database/connection.ts - Mixed CommonJS exports
import { Pool, PoolConfig } from 'pg'; // ๐Ÿ˜ PostgreSQL types

// ๐Ÿ—๏ธ Database configuration interface
interface DatabaseConfig extends PoolConfig {
  retryAttempts?: number;
  retryDelay?: number;
}

// ๐Ÿ“Š Connection pool class
class DatabaseConnection {
  private pool: Pool;
  private config: DatabaseConfig;
  
  constructor(config: DatabaseConfig) {
    this.config = {
      retryAttempts: 3,
      retryDelay: 1000,
      ...config
    };
    
    this.pool = new Pool(this.config);
    console.log('๐Ÿ”— Database connection pool created');
  }
  
  // ๐Ÿ” Execute query with type safety
  async query<T = any>(text: string, params?: any[]): Promise<T[]> {
    try {
      console.log(`๐Ÿ“ Executing query: ${text.substring(0, 50)}...`);
      const result = await this.pool.query(text, params);
      return result.rows;
    } catch (error) {
      console.error('โŒ Database query failed:', error);
      throw error;
    }
  }
  
  // ๐Ÿงน Clean shutdown
  async close(): Promise<void> {
    await this.pool.end();
    console.log('๐Ÿ‘‹ Database connection pool closed');
  }
}

// ๐Ÿ“ฆ CommonJS exports - multiple patterns
module.exports = DatabaseConnection;           // Default export
module.exports.DatabaseConnection = DatabaseConnection; // Named export
module.exports.createConnection = (config: DatabaseConfig) => {
  return new DatabaseConnection(config);       // Factory function
};

// ๐ŸŽฏ Export types for TypeScript users
export type { DatabaseConfig };

๐Ÿ’ก Practical Examples

๐Ÿ–ฅ๏ธ Example 1: Express.js Server with TypeScript

Letโ€™s build a real-world Express server using CommonJS patterns:

// ๐Ÿ“ server/app.ts - Express application setup
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');

// ๐Ÿ—๏ธ Import our custom modules
const userRoutes = require('./routes/users');
const authMiddleware = require('./middleware/auth');
const errorHandler = require('./middleware/errorHandler');

// ๐Ÿ“Š Application configuration interface
interface AppConfig {
  port: number;
  cors: {
    origin: string[];
    credentials: boolean;
  };
  rateLimit: {
    windowMs: number;
    max: number;
  };
}

// ๐Ÿš€ Express application class
class ExpressApp {
  private app: any; // Express app instance
  private config: AppConfig;
  
  constructor(config: AppConfig) {
    this.config = config;
    this.app = express();
    this.setupMiddleware();
    this.setupRoutes();
    this.setupErrorHandling();
  }
  
  // ๐Ÿ› ๏ธ Configure middleware
  private setupMiddleware(): void {
    // ๐Ÿ›ก๏ธ Security middleware
    this.app.use(helmet({
      contentSecurityPolicy: {
        directives: {
          defaultSrc: ["'self'"],
          styleSrc: ["'self'", "'unsafe-inline'"]
        }
      }
    }));
    
    // ๐ŸŒ CORS configuration
    this.app.use(cors({
      origin: this.config.cors.origin,
      credentials: this.config.cors.credentials
    }));
    
    // ๐Ÿ“ Request parsing
    this.app.use(express.json({ limit: '10mb' }));
    this.app.use(express.urlencoded({ extended: true }));
    
    console.log('๐Ÿ”ง Middleware configured successfully');
  }
  
  // ๐Ÿ›ฃ๏ธ Setup application routes
  private setupRoutes(): void {
    // ๐Ÿ  Health check endpoint
    this.app.get('/health', (req: any, res: any) => {
      res.json({ 
        status: 'healthy',
        timestamp: new Date().toISOString(),
        uptime: process.uptime(),
        emoji: 'โœ…'
      });
    });
    
    // ๐Ÿ‘ค User routes with authentication
    this.app.use('/api/users', authMiddleware, userRoutes);
    
    // ๐Ÿ“Š API information endpoint
    this.app.get('/api/info', (req: any, res: any) => {
      res.json({
        name: 'TypeScript CommonJS API',
        version: '1.0.0',
        description: 'Example API using CommonJS modules ๐Ÿš€',
        endpoints: [
          'GET /health',
          'GET /api/info',
          'GET /api/users',
          'POST /api/users'
        ]
      });
    });
    
    console.log('๐Ÿ›ฃ๏ธ Routes configured successfully');
  }
  
  // ๐Ÿšจ Error handling setup
  private setupErrorHandling(): void {
    // 404 handler
    this.app.use((req: any, res: any) => {
      res.status(404).json({
        error: 'Not Found',
        message: `Route ${req.method} ${req.path} not found ๐Ÿ”`,
        timestamp: new Date().toISOString()
      });
    });
    
    // Global error handler
    this.app.use(errorHandler);
    
    console.log('๐Ÿšจ Error handling configured');
  }
  
  // ๐Ÿš€ Start the server
  start(): void {
    this.app.listen(this.config.port, () => {
      console.log(`๐Ÿš€ Server running on port ${this.config.port}`);
      console.log(`๐Ÿ“Š Health check: http://localhost:${this.config.port}/health`);
      console.log(`๐Ÿ“– API info: http://localhost:${this.config.port}/api/info`);
    });
  }
  
  // ๐Ÿ“ฑ Get express app instance
  getApp(): any {
    return this.app;
  }
}

// ๐Ÿ“ฆ CommonJS export
module.exports = ExpressApp;
// ๐Ÿ“ routes/users.ts - User routes module
const express = require('express');
const router = express.Router();

// ๐ŸŽฏ User interface
interface User {
  id: string;
  name: string;
  email: string;
  createdAt: Date;
  role: 'admin' | 'user' | 'moderator';
}

// ๐Ÿ“Š Mock user database
const users: User[] = [
  {
    id: '1',
    name: 'Alice Johnson',
    email: '[email protected]',
    createdAt: new Date('2024-01-15'),
    role: 'admin'
  },
  {
    id: '2', 
    name: 'Bob Smith',
    email: '[email protected]',
    createdAt: new Date('2024-02-20'),
    role: 'user'
  },
  {
    id: '3',
    name: 'Charlie Brown',
    email: '[email protected]', 
    createdAt: new Date('2024-03-10'),
    role: 'moderator'
  }
];

// ๐Ÿ“‹ GET /api/users - List all users
router.get('/', (req: any, res: any) => {
  try {
    const { role, limit = '10' } = req.query;
    let filteredUsers = users;
    
    // ๐Ÿ” Filter by role if specified
    if (role) {
      filteredUsers = users.filter(user => user.role === role);
    }
    
    // ๐Ÿ“ Apply limit
    const limitNum = parseInt(limit as string, 10);
    const result = filteredUsers.slice(0, limitNum);
    
    res.json({
      users: result,
      total: filteredUsers.length,
      showing: result.length,
      message: `Found ${result.length} users ๐Ÿ‘ฅ`
    });
    
    console.log(`๐Ÿ“‹ Retrieved ${result.length} users`);
  } catch (error) {
    console.error('โŒ Error fetching users:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

// ๐Ÿ‘ค GET /api/users/:id - Get specific user
router.get('/:id', (req: any, res: any) => {
  try {
    const { id } = req.params;
    const user = users.find(u => u.id === id);
    
    if (!user) {
      return res.status(404).json({
        error: 'User not found',
        message: `User with ID ${id} does not exist ๐Ÿ”`
      });
    }
    
    res.json({
      user,
      message: `User ${user.name} retrieved successfully โœ…`
    });
    
    console.log(`๐Ÿ‘ค Retrieved user: ${user.name}`);
  } catch (error) {
    console.error('โŒ Error fetching user:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

// โž• POST /api/users - Create new user
router.post('/', (req: any, res: any) => {
  try {
    const { name, email, role = 'user' } = req.body;
    
    // โœ… Validation
    if (!name || !email) {
      return res.status(400).json({
        error: 'Validation failed',
        message: 'Name and email are required ๐Ÿ“'
      });
    }
    
    // ๐Ÿ” Check for duplicate email
    const existingUser = users.find(u => u.email === email);
    if (existingUser) {
      return res.status(409).json({
        error: 'Conflict',
        message: 'User with this email already exists ๐Ÿ“ง'
      });
    }
    
    // ๐ŸŽฏ Create new user
    const newUser: User = {
      id: (users.length + 1).toString(),
      name,
      email,
      createdAt: new Date(),
      role: role as User['role']
    };
    
    users.push(newUser);
    
    res.status(201).json({
      user: newUser,
      message: `User ${newUser.name} created successfully ๐ŸŽ‰`
    });
    
    console.log(`โž• Created user: ${newUser.name}`);
  } catch (error) {
    console.error('โŒ Error creating user:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
});

// ๐Ÿ“ฆ Export the router
module.exports = router;
// ๐Ÿ“ middleware/auth.ts - Authentication middleware
// ๐Ÿ” Simple authentication middleware example

interface AuthRequest {
  headers: {
    authorization?: string;
    [key: string]: any;
  };
  user?: {
    id: string;
    role: string;
  };
}

// ๐Ÿ›ก๏ธ Authentication middleware function
const authMiddleware = (req: AuthRequest, res: any, next: any) => {
  try {
    const authHeader = req.headers.authorization;
    
    if (!authHeader) {
      return res.status(401).json({
        error: 'Unauthorized',
        message: 'No authorization header provided ๐Ÿ”’'
      });
    }
    
    // ๐ŸŽฏ Simple token validation (in real app, verify JWT)
    const token = authHeader.replace('Bearer ', '');
    
    if (token === 'valid-token-123') {
      // โœ… Valid token - attach user info
      req.user = {
        id: 'user123',
        role: 'admin'
      };
      
      console.log('โœ… User authenticated successfully');
      next();
    } else {
      return res.status(401).json({
        error: 'Unauthorized', 
        message: 'Invalid token provided ๐Ÿšซ'
      });
    }
    
  } catch (error) {
    console.error('โŒ Authentication error:', error);
    res.status(500).json({ error: 'Internal server error' });
  }
};

// ๐Ÿ“ฆ Export the middleware
module.exports = authMiddleware;

๐ŸŽฏ Try it yourself: Add user update and delete endpoints, plus role-based access control!

๐Ÿ“ฆ Example 2: npm Package with CommonJS and ES Module Support

Create a dual-mode package that works with both systems:

// ๐Ÿ“ src/index.ts - Main package entry point
// ๐ŸŒ‰ Universal package that supports both CommonJS and ES modules

// ๐ŸŽฏ Core validation functions
export interface ValidationRule {
  type: 'required' | 'email' | 'minLength' | 'maxLength' | 'pattern';
  value?: any;
  message?: string;
}

export interface ValidationResult {
  isValid: boolean;
  errors: string[];
  field?: string;
}

// โœ… Email validation
export const validateEmail = (email: string): ValidationResult => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const isValid = emailRegex.test(email);
  
  return {
    isValid,
    errors: isValid ? [] : ['Invalid email format ๐Ÿ“ง'],
    field: 'email'
  };
};

// ๐Ÿ“ Length validation
export const validateLength = (
  value: string, 
  min: number, 
  max: number
): ValidationResult => {
  const length = value.length;
  const errors: string[] = [];
  
  if (length < min) {
    errors.push(`Must be at least ${min} characters long ๐Ÿ“`);
  }
  
  if (length > max) {
    errors.push(`Must be no more than ${max} characters long โœ‚๏ธ`);
  }
  
  return {
    isValid: errors.length === 0,
    errors,
    field: 'length'
  };
};

// ๐Ÿ” Pattern validation
export const validatePattern = (
  value: string, 
  pattern: RegExp, 
  message: string = 'Invalid format'
): ValidationResult => {
  const isValid = pattern.test(value);
  
  return {
    isValid,
    errors: isValid ? [] : [`${message} ๐Ÿ”`],
    field: 'pattern'
  };
};

// ๐ŸŽฏ Comprehensive validator class
export class Validator {
  private rules: Map<string, ValidationRule[]> = new Map();
  
  // โž• Add validation rule
  addRule(field: string, rule: ValidationRule): void {
    if (!this.rules.has(field)) {
      this.rules.set(field, []);
    }
    this.rules.get(field)!.push(rule);
  }
  
  // โœ… Validate all fields
  validate(data: Record<string, any>): Record<string, ValidationResult> {
    const results: Record<string, ValidationResult> = {};
    
    for (const [field, rules] of this.rules) {
      const value = data[field];
      const errors: string[] = [];
      
      for (const rule of rules) {
        switch (rule.type) {
          case 'required':
            if (!value || (typeof value === 'string' && value.trim() === '')) {
              errors.push(rule.message || `${field} is required ๐Ÿ“`);
            }
            break;
            
          case 'email':
            if (value && !validateEmail(value).isValid) {
              errors.push(rule.message || 'Invalid email format ๐Ÿ“ง');
            }
            break;
            
          case 'minLength':
            if (value && value.length < rule.value) {
              errors.push(rule.message || `Must be at least ${rule.value} characters ๐Ÿ“`);
            }
            break;
            
          case 'maxLength':
            if (value && value.length > rule.value) {
              errors.push(rule.message || `Must be no more than ${rule.value} characters โœ‚๏ธ`);
            }
            break;
            
          case 'pattern':
            if (value && !rule.value.test(value)) {
              errors.push(rule.message || 'Invalid format ๐Ÿ”');
            }
            break;
        }
      }
      
      results[field] = {
        isValid: errors.length === 0,
        errors,
        field
      };
    }
    
    return results;
  }
  
  // ๐Ÿ” Check if all validations pass
  isValid(data: Record<string, any>): boolean {
    const results = this.validate(data);
    return Object.values(results).every(result => result.isValid);
  }
}

// ๐ŸŽฏ Convenient factory function
export const createValidator = (): Validator => {
  return new Validator();
};

// ๐Ÿ“ฆ Default export for CommonJS compatibility
const validatorPackage = {
  validateEmail,
  validateLength,
  validatePattern,
  Validator,
  createValidator
};

// ๐ŸŒ‰ Dual export support
export default validatorPackage;

// CommonJS compatibility
if (typeof module !== 'undefined' && module.exports) {
  module.exports = validatorPackage;
  module.exports.default = validatorPackage;
  module.exports.validateEmail = validateEmail;
  module.exports.validateLength = validateLength;
  module.exports.validatePattern = validatePattern;
  module.exports.Validator = Validator;
  module.exports.createValidator = createValidator;
}
// ๐Ÿ“ package.json - Dual module support configuration
{
  "name": "typescript-validator-universal",
  "version": "1.0.0",
  "description": "Universal validation library supporting both CommonJS and ES modules ๐ŸŒ‰",
  "main": "dist/cjs/index.js",           // ๐Ÿ“ฆ CommonJS entry point
  "module": "dist/esm/index.js",         // ๐Ÿ“ฆ ES Module entry point
  "types": "dist/types/index.d.ts",      // ๐Ÿ“‹ TypeScript definitions
  "exports": {
    ".": {
      "require": "./dist/cjs/index.js",  // ๐ŸŽฏ CommonJS
      "import": "./dist/esm/index.js",   // ๐ŸŽฏ ES Module
      "types": "./dist/types/index.d.ts" // ๐Ÿ“‹ Types
    }
  },
  "files": [
    "dist/",
    "README.md"
  ],
  "scripts": {
    "build": "npm run build:cjs && npm run build:esm && npm run build:types",
    "build:cjs": "tsc -p tsconfig.cjs.json",
    "build:esm": "tsc -p tsconfig.esm.json", 
    "build:types": "tsc -p tsconfig.types.json",
    "test": "jest",
    "lint": "eslint src/**/*.ts"
  },
  "keywords": ["typescript", "validation", "commonjs", "esm", "universal"],
  "author": "Your Name",
  "license": "MIT"
}

๐Ÿš€ Advanced CommonJS Patterns

๐Ÿง™โ€โ™‚๏ธ Dynamic Requires and Conditional Loading

Advanced patterns for dynamic module loading:

// ๐Ÿ“ utils/module-loader.ts - Dynamic module loading utilities

interface ModuleCache {
  [key: string]: any;
}

// ๐Ÿ“ฆ Module loader with caching
class DynamicModuleLoader {
  private cache: ModuleCache = {};
  
  // ๐Ÿš€ Load module with caching
  loadModule<T = any>(modulePath: string): T {
    if (this.cache[modulePath]) {
      console.log(`๐Ÿ“ฆ Using cached module: ${modulePath}`);
      return this.cache[modulePath];
    }
    
    try {
      console.log(`โณ Loading module: ${modulePath}`);
      const module = require(modulePath);
      this.cache[modulePath] = module;
      console.log(`โœ… Module loaded: ${modulePath}`);
      return module;
    } catch (error) {
      console.error(`โŒ Failed to load module: ${modulePath}`, error);
      throw new Error(`Module loading failed: ${modulePath}`);
    }
  }
  
  // ๐Ÿ”„ Conditional loading based on environment
  loadEnvironmentModule(basePath: string): any {
    const env = process.env.NODE_ENV || 'development';
    const modulePath = `${basePath}.${env}`;
    
    try {
      return this.loadModule(modulePath);
    } catch (error) {
      console.log(`โš ๏ธ Environment module not found: ${modulePath}, falling back to base`);
      return this.loadModule(basePath);
    }
  }
  
  // ๐Ÿ” Check if module exists
  moduleExists(modulePath: string): boolean {
    try {
      require.resolve(modulePath);
      return true;
    } catch (error) {
      return false;
    }
  }
  
  // ๐Ÿงน Clear module cache
  clearCache(modulePath?: string): void {
    if (modulePath) {
      delete this.cache[modulePath];
      console.log(`๐Ÿงน Cleared cache for: ${modulePath}`);
    } else {
      this.cache = {};
      console.log('๐Ÿงน Cleared entire module cache');
    }
  }
  
  // ๐Ÿ“Š Get cache statistics
  getCacheStats(): { total: number; modules: string[] } {
    const modules = Object.keys(this.cache);
    return {
      total: modules.length,
      modules
    };
  }
}

// ๐ŸŽฏ Plugin system using dynamic loading
class PluginManager {
  private plugins: Map<string, any> = new Map();
  private loader = new DynamicModuleLoader();
  
  // ๐Ÿ”Œ Load plugin by name
  loadPlugin(name: string, pluginPath?: string): void {
    const path = pluginPath || `./plugins/${name}`;
    
    try {
      const plugin = this.loader.loadModule(path);
      
      // โœ… Validate plugin interface
      if (typeof plugin.init !== 'function') {
        throw new Error('Plugin must have an init() function');
      }
      
      // ๐Ÿš€ Initialize plugin
      plugin.init();
      this.plugins.set(name, plugin);
      
      console.log(`๐Ÿ”Œ Plugin loaded: ${name}`);
    } catch (error) {
      console.error(`โŒ Failed to load plugin: ${name}`, error);
    }
  }
  
  // ๐ŸŽฎ Execute plugin method
  executePlugin(name: string, method: string, ...args: any[]): any {
    const plugin = this.plugins.get(name);
    
    if (!plugin) {
      throw new Error(`Plugin not found: ${name}`);
    }
    
    if (typeof plugin[method] !== 'function') {
      throw new Error(`Method ${method} not found in plugin ${name}`);
    }
    
    console.log(`๐ŸŽฎ Executing ${name}.${method}()`);
    return plugin[method](...args);
  }
  
  // ๐Ÿ“‹ List loaded plugins
  listPlugins(): string[] {
    return Array.from(this.plugins.keys());
  }
}

// ๐Ÿ“ฆ Export utilities
module.exports = {
  DynamicModuleLoader,
  PluginManager,
  
  // ๐ŸŽฏ Convenience functions
  loadModule: (path: string) => new DynamicModuleLoader().loadModule(path),
  createPluginManager: () => new PluginManager()
};

๐Ÿ—๏ธ Module Wrapping and Namespace Creation

Advanced patterns for organizing CommonJS modules:

// ๐Ÿ“ core/namespace.ts - Namespace creation utilities

// ๐Ÿ—๏ธ Namespace builder for organizing modules
class NamespaceBuilder {
  private namespace: any = {};
  
  // โž• Add module to namespace
  addModule(name: string, module: any): this {
    const parts = name.split('.');
    let current = this.namespace;
    
    // ๐Ÿ—๏ธ Create nested structure
    for (let i = 0; i < parts.length - 1; i++) {
      const part = parts[i];
      if (!current[part]) {
        current[part] = {};
      }
      current = current[part];
    }
    
    // ๐Ÿ“ฆ Add the module
    current[parts[parts.length - 1]] = module;
    console.log(`๐Ÿ“ฆ Added module to namespace: ${name}`);
    
    return this;
  }
  
  // ๐Ÿ”Œ Auto-load modules from directory
  autoLoad(basePath: string, pattern: RegExp = /\.js$/): this {
    const fs = require('fs');
    const path = require('path');
    
    try {
      const files = fs.readdirSync(basePath);
      
      for (const file of files) {
        if (pattern.test(file)) {
          const moduleName = path.basename(file, path.extname(file));
          const modulePath = path.join(basePath, file);
          
          try {
            const module = require(modulePath);
            this.addModule(moduleName, module);
          } catch (error) {
            console.error(`โŒ Failed to auto-load: ${modulePath}`, error);
          }
        }
      }
    } catch (error) {
      console.error(`โŒ Failed to read directory: ${basePath}`, error);
    }
    
    return this;
  }
  
  // ๐ŸŽฏ Get the built namespace
  build(): any {
    return this.namespace;
  }
  
  // ๐Ÿ” Get module from namespace
  get(path: string): any {
    const parts = path.split('.');
    let current = this.namespace;
    
    for (const part of parts) {
      if (!current[part]) {
        return undefined;
      }
      current = current[part];
    }
    
    return current;
  }
}

// ๐ŸŽฏ Module wrapper for consistent interface
const wrapModule = (module: any, metadata: any = {}) => {
  return {
    // ๐Ÿ“Š Module metadata
    _meta: {
      name: metadata.name || 'unknown',
      version: metadata.version || '1.0.0',
      author: metadata.author || 'unknown',
      loadedAt: new Date(),
      ...metadata
    },
    
    // ๐Ÿ“ฆ Original module
    ...module,
    
    // ๐Ÿ”ง Utility methods
    getInfo: () => {
      return {
        name: metadata.name,
        version: metadata.version,
        exports: Object.keys(module),
        loadedAt: new Date()
      };
    }
  };
};

// ๐Ÿ“ฆ Export the namespace utilities
module.exports = {
  NamespaceBuilder,
  wrapModule,
  
  // ๐ŸŽฏ Convenience functions
  createNamespace: () => new NamespaceBuilder(),
  
  // ๐Ÿ—๏ธ Quick namespace creation
  buildNamespace: (modules: Record<string, any>) => {
    const builder = new NamespaceBuilder();
    for (const [name, module] of Object.entries(modules)) {
      builder.addModule(name, module);
    }
    return builder.build();
  }
};

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Mixed Module Systems Confusion

// โŒ Wrong way - mixing import/require inconsistently
import express from 'express'; // ES6 import
const cors = require('cors');   // CommonJS require
// This creates confusion and potential issues!

// ๐Ÿ’ฅ TypeScript compilation errors
export default function createApp() {
  // ...
}
module.exports = createApp; // Mixing export styles!
// โœ… Correct way - consistent CommonJS approach
const express = require('express');
const cors = require('cors');

// ๐ŸŽฏ Consistent CommonJS exports
function createApp() {
  const app = express();
  
  app.use(cors());
  app.use(express.json());
  
  return app;
}

// ๐Ÿ“ฆ Single export style
module.exports = createApp;
module.exports.createApp = createApp; // Named export for flexibility

๐Ÿคฏ Pitfall 2: TypeScript Configuration Issues

// โŒ Wrong configuration - causes import issues
// tsconfig.json with problematic settings
{
  "compilerOptions": {
    "module": "ES2020",           // ๐Ÿ’ฅ Conflicts with CommonJS usage
    "moduleResolution": "classic", // ๐Ÿ’ฅ Wrong resolution strategy
    "esModuleInterop": false      // ๐Ÿ’ฅ Breaks interop
  }
}
// โœ… Correct configuration for CommonJS
{
  "compilerOptions": {
    "module": "CommonJS",              // ๐ŸŽฏ Consistent with runtime
    "moduleResolution": "node",        // โœ… Node.js resolution
    "esModuleInterop": true,          // ๐ŸŒ‰ Enable ES/CommonJS interop
    "allowSyntheticDefaultImports": true, // โœ… Synthetic defaults
    "target": "ES2020",               // ๐ŸŽฏ Modern JS features
    "strict": true,                   // ๐Ÿ›ก๏ธ Type safety
    "skipLibCheck": true              // โšก Performance
  }
}

๐Ÿ”ฅ Pitfall 3: require() vs import Type Confusion

// โŒ Wrong way - losing type information
const someLibrary = require('some-library'); // ๐Ÿ’ฅ No type checking!
// someLibrary. <-- No autocomplete, no type safety

// Using any types everywhere
const result: any = someLibrary.doSomething();
// โœ… Correct way - maintaining types with CommonJS
// Option 1: Import types separately
import type { SomeLibraryType } from 'some-library';
const someLibrary = require('some-library') as SomeLibraryType;

// Option 2: Use proper type declarations
interface SomeLibrary {
  doSomething(): string;
  configure(options: any): void;
}

const someLibrary: SomeLibrary = require('some-library');

// Option 3: Create typed wrapper
const createTypedRequire = <T>(modulePath: string): T => {
  return require(modulePath);
};

const typedLibrary = createTypedRequire<SomeLibrary>('some-library');

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Use Consistent Module Systems: Pick CommonJS or ES modules per project
  2. ๐Ÿ“ Configure TypeScript Properly: Set correct module and resolution options
  3. ๐Ÿ›ก๏ธ Maintain Type Safety: Donโ€™t lose types when using require()
  4. ๐ŸŽจ Use esModuleInterop: Enable seamless ES/CommonJS compatibility
  5. โœจ Cache Modules Wisely: Use require() caching effectively
  6. ๐Ÿ”’ Handle Errors Gracefully: Wrap require() calls in try/catch
  7. ๐Ÿ“ฆ Export Consistently: Use the same export pattern throughout

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Plugin-Based Logger System

Create a modular logging system that supports dynamic plugin loading:

๐Ÿ“‹ Requirements:

  • ๐Ÿ“ Core logger with multiple log levels (info, warn, error, debug)
  • ๐Ÿ”Œ Plugin system for different output formats (console, file, database)
  • โš™๏ธ Configuration system using CommonJS modules
  • ๐Ÿ”„ Dynamic plugin loading and unloading
  • ๐Ÿ“Š Log aggregation and filtering capabilities
  • ๐ŸŽจ TypeScript types for all plugins and configurations!

๐Ÿš€ Bonus Points:

  • Add log rotation for file outputs
  • Implement log streaming to external services
  • Create performance monitoring plugins

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐Ÿ“ core/logger.ts - Main logger implementation
interface LogLevel {
  name: string;
  level: number;
  color?: string;
}

interface LogEntry {
  timestamp: Date;
  level: LogLevel;
  message: string;
  metadata?: Record<string, any>;
  source?: string;
}

interface LoggerPlugin {
  name: string;
  version: string;
  init(config?: any): void;
  log(entry: LogEntry): void;
  destroy?(): void;
}

class PluginLogger {
  private levels: Map<string, LogLevel> = new Map();
  private plugins: Map<string, LoggerPlugin> = new Map();
  private currentLevel: number = 0;
  
  constructor() {
    // ๐Ÿ“Š Setup default log levels
    this.addLevel('debug', 0, '\x1b[36m');
    this.addLevel('info', 1, '\x1b[32m');
    this.addLevel('warn', 2, '\x1b[33m');
    this.addLevel('error', 3, '\x1b[31m');
  }
  
  // ๐Ÿ“Š Add log level
  addLevel(name: string, level: number, color?: string): void {
    this.levels.set(name, { name, level, color });
    console.log(`๐Ÿ“Š Added log level: ${name} (${level})`);
  }
  
  // ๐Ÿ”Œ Load plugin
  loadPlugin(name: string, pluginPath: string, config?: any): void {
    try {
      const PluginClass = require(pluginPath);
      const plugin: LoggerPlugin = new PluginClass();
      
      plugin.init(config);
      this.plugins.set(name, plugin);
      
      console.log(`๐Ÿ”Œ Plugin loaded: ${name} v${plugin.version}`);
    } catch (error) {
      console.error(`โŒ Failed to load plugin: ${name}`, error);
    }
  }
  
  // ๐Ÿ“ Core logging method
  private logInternal(levelName: string, message: string, metadata?: any): void {
    const level = this.levels.get(levelName);
    if (!level || level.level < this.currentLevel) {
      return; // Level too low
    }
    
    const entry: LogEntry = {
      timestamp: new Date(),
      level,
      message,
      metadata,
      source: this.getCallerInfo()
    };
    
    // ๐Ÿ“ก Send to all plugins
    for (const plugin of this.plugins.values()) {
      try {
        plugin.log(entry);
      } catch (error) {
        console.error(`โŒ Plugin error: ${plugin.name}`, error);
      }
    }
  }
  
  // ๐Ÿ” Get caller information
  private getCallerInfo(): string {
    const stack = new Error().stack;
    if (stack) {
      const lines = stack.split('\n');
      return lines[4]?.trim() || 'unknown';
    }
    return 'unknown';
  }
  
  // ๐Ÿ“ Public logging methods
  debug(message: string, metadata?: any): void {
    this.logInternal('debug', message, metadata);
  }
  
  info(message: string, metadata?: any): void {
    this.logInternal('info', message, metadata);
  }
  
  warn(message: string, metadata?: any): void {
    this.logInternal('warn', message, metadata);
  }
  
  error(message: string, metadata?: any): void {
    this.logInternal('error', message, metadata);
  }
  
  // โš™๏ธ Set minimum log level
  setLevel(levelName: string): void {
    const level = this.levels.get(levelName);
    if (level) {
      this.currentLevel = level.level;
      console.log(`โš™๏ธ Log level set to: ${levelName}`);
    }
  }
}

// ๐Ÿ“ฆ Export logger
module.exports = PluginLogger;
// ๐Ÿ“ plugins/console-plugin.ts - Console output plugin
const ConsolePlugin = class implements LoggerPlugin {
  name = 'console';
  version = '1.0.0';
  private showColors = true;
  
  init(config: { colors?: boolean } = {}): void {
    this.showColors = config.colors !== false;
    console.log(`๐Ÿ–ฅ๏ธ Console plugin initialized (colors: ${this.showColors})`);
  }
  
  log(entry: LogEntry): void {
    const timestamp = entry.timestamp.toISOString();
    const level = entry.level.name.toUpperCase();
    
    let output = `[${timestamp}] ${level}: ${entry.message}`;
    
    if (entry.metadata) {
      output += ` ${JSON.stringify(entry.metadata)}`;
    }
    
    if (this.showColors && entry.level.color) {
      output = `${entry.level.color}${output}\x1b[0m`;
    }
    
    console.log(output);
  }
};

module.exports = ConsolePlugin;
// ๐Ÿ“ plugins/file-plugin.ts - File output plugin
const fs = require('fs');
const path = require('path');

const FilePlugin = class implements LoggerPlugin {
  name = 'file';
  version = '1.0.0';
  private logDir = './logs';
  private maxSize = 10 * 1024 * 1024; // 10MB
  
  init(config: { logDir?: string; maxSize?: number } = {}): void {
    this.logDir = config.logDir || this.logDir;
    this.maxSize = config.maxSize || this.maxSize;
    
    // ๐Ÿ“ Ensure log directory exists
    if (!fs.existsSync(this.logDir)) {
      fs.mkdirSync(this.logDir, { recursive: true });
    }
    
    console.log(`๐Ÿ“ File plugin initialized (dir: ${this.logDir})`);
  }
  
  log(entry: LogEntry): void {
    const date = entry.timestamp.toISOString().split('T')[0];
    const filename = path.join(this.logDir, `app-${date}.log`);
    
    const logLine = JSON.stringify({
      timestamp: entry.timestamp.toISOString(),
      level: entry.level.name,
      message: entry.message,
      metadata: entry.metadata,
      source: entry.source
    }) + '\n';
    
    // ๐Ÿ”„ Check file size and rotate if needed
    this.rotateIfNeeded(filename);
    
    fs.appendFileSync(filename, logLine);
  }
  
  private rotateIfNeeded(filename: string): void {
    if (fs.existsSync(filename)) {
      const stats = fs.statSync(filename);
      if (stats.size > this.maxSize) {
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const rotated = `${filename}.${timestamp}`;
        fs.renameSync(filename, rotated);
        console.log(`๐Ÿ”„ Rotated log file: ${rotated}`);
      }
    }
  }
};

module.exports = FilePlugin;
// ๐Ÿ“ config/logger-config.ts - Logger configuration
const loggerConfig = {
  level: 'info',
  plugins: [
    {
      name: 'console',
      path: './plugins/console-plugin',
      config: {
        colors: true
      }
    },
    {
      name: 'file',
      path: './plugins/file-plugin', 
      config: {
        logDir: './logs',
        maxSize: 5 * 1024 * 1024 // 5MB
      }
    }
  ]
};

module.exports = loggerConfig;
// ๐Ÿ“ index.ts - Usage example
const PluginLogger = require('./core/logger');
const config = require('./config/logger-config');

// ๐Ÿš€ Initialize logger
const logger = new PluginLogger();

// ๐Ÿ”Œ Load plugins from config
for (const pluginConfig of config.plugins) {
  logger.loadPlugin(
    pluginConfig.name,
    pluginConfig.path,
    pluginConfig.config
  );
}

// โš™๏ธ Set log level
logger.setLevel(config.level);

// ๐Ÿ“ Test logging
logger.info('Logger system initialized! ๐Ÿš€');
logger.debug('Debug information', { userId: 123, action: 'login' });
logger.warn('This is a warning โš ๏ธ', { component: 'auth' });
logger.error('An error occurred! โŒ', { error: 'Connection failed' });

// ๐ŸŽฏ Export configured logger
module.exports = logger;

๐ŸŽ“ Key Takeaways

Youโ€™ve mastered CommonJS modules in TypeScript! Hereโ€™s what you can now do:

  • โœ… Use require() and module.exports with full TypeScript support ๐Ÿ’ช
  • โœ… Configure TypeScript for seamless Node.js compatibility ๐Ÿ›ก๏ธ
  • โœ… Bridge ES modules and CommonJS without losing type safety ๐ŸŽฏ
  • โœ… Build universal packages that work in any environment ๐Ÿ”ง
  • โœ… Handle legacy codebases with confidence and skill ๐Ÿš€

Remember: CommonJS is still the backbone of the Node.js ecosystem. Mastering it opens doors to countless libraries and legacy systems! ๐Ÿค

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered CommonJS modules in TypeScript!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Practice with the plugin logger exercise above
  2. ๐Ÿ—๏ธ Convert an existing ES module project to CommonJS
  3. ๐Ÿ“š Move on to our next tutorial: Promises in TypeScript: Type-Safe Asynchronous Code
  4. ๐ŸŒŸ Share your universal packages with the community!

Remember: The best developers understand both old and new technologies. Keep building bridges! ๐ŸŒ‰


Happy CommonJS coding! ๐ŸŽ‰๐Ÿ–ฅ๏ธโœจ