javascript

exercises

exercises.js⚔
// ╔══════════════════════════════════════════════════════════════════════════════╗
// ā•‘                     COMMONJS & AMD - EXERCISES                                ā•‘
// ā•‘                  Practice Legacy Module System Patterns                        ā•‘
// ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•

/*
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                              EXERCISE OVERVIEW                                   │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                                                  │
│  Exercise 1: CommonJS Module - Create a utility module                          │
│  Exercise 2: CommonJS Exports - Practice different export patterns              │
│  Exercise 3: AMD Module - Create modules with dependencies                      │
│  Exercise 4: UMD Pattern - Make a universal module                              │
│  Exercise 5: Module Conversion - Convert between formats                        │
│                                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
*/

// ════════════════════════════════════════════════════════════════════════════════
// HELPER: Module System Simulators
// ════════════════════════════════════════════════════════════════════════════════

// CommonJS Simulator
const CommonJS = (function () {
  const cache = new Map();

  return {
    createModule(factory) {
      const module = { exports: {} };
      const exports = module.exports;
      factory(module, exports, this.createModule.bind(this));
      return module.exports;
    },

    require(name, factory) {
      if (cache.has(name)) {
        return cache.get(name);
      }
      const exports = this.createModule(factory);
      cache.set(name, exports);
      return exports;
    },

    clearCache() {
      cache.clear();
    },
  };
})();

// AMD Simulator
const AMD = (function () {
  const modules = new Map();

  return {
    define(name, deps, factory) {
      if (typeof name !== 'string') {
        factory = deps;
        deps = name;
        name = 'anonymous_' + Date.now();
      }
      if (!Array.isArray(deps)) {
        factory = deps;
        deps = [];
      }
      if (typeof factory !== 'function') {
        const obj = factory;
        factory = () => obj;
      }

      modules.set(name, { deps, factory, resolved: null });
      return name;
    },

    require(deps, callback) {
      if (typeof deps === 'string') deps = [deps];

      const resolved = deps.map((dep) => {
        const mod = modules.get(dep);
        if (!mod) throw new Error(`Module not found: ${dep}`);

        if (!mod.resolved) {
          const depsResolved = mod.deps.map((d) => {
            const m = modules.get(d);
            return m ? m.resolved || m.factory() : undefined;
          });
          mod.resolved = mod.factory(...depsResolved);
        }
        return mod.resolved;
      });

      if (callback) callback(...resolved);
      return resolved[0];
    },

    clear() {
      modules.clear();
    },
  };
})();

// ════════════════════════════════════════════════════════════════════════════════
// EXERCISE 1: CommonJS String Utilities Module
// ════════════════════════════════════════════════════════════════════════════════

/*
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Create a CommonJS string utilities module                                      │
│                                                                                  │
│  Requirements:                                                                  │
│  1. Export a 'capitalize' function that capitalizes first letter                │
│  2. Export a 'camelCase' function that converts to camelCase                    │
│  3. Export a 'snakeCase' function that converts to snake_case                   │
│  4. Export a 'kebabCase' function that converts to kebab-case                   │
│  5. Export a 'truncate' function with length and suffix parameters              │
│  6. Export a 'wordCount' function that counts words                             │
│                                                                                  │
│  Use module.exports = { ... } pattern                                           │
│                                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
*/

// Your solution here:

const stringUtils = CommonJS.createModule((module, exports) => {
  // TODO: Implement the string utilities

  // Solution:

  function capitalize(str) {
    if (!str) return '';
    return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
  }

  function camelCase(str) {
    return str
      .toLowerCase()
      .replace(/[^a-zA-Z0-9]+(.)/g, (match, char) => char.toUpperCase());
  }

  function snakeCase(str) {
    return str
      .replace(/([a-z])([A-Z])/g, '$1_$2')
      .replace(/[\s-]+/g, '_')
      .toLowerCase();
  }

  function kebabCase(str) {
    return str
      .replace(/([a-z])([A-Z])/g, '$1-$2')
      .replace(/[\s_]+/g, '-')
      .toLowerCase();
  }

  function truncate(str, length, suffix = '...') {
    if (str.length <= length) return str;
    return str.slice(0, length - suffix.length) + suffix;
  }

  function wordCount(str) {
    return str.trim().split(/\s+/).filter(Boolean).length;
  }

  // Export using module.exports
  module.exports = {
    capitalize,
    camelCase,
    snakeCase,
    kebabCase,
    truncate,
    wordCount,
  };
});

// Test
console.log('Exercise 1 - CommonJS String Utilities:');
console.log('  capitalize("hello"):', stringUtils.capitalize('hello'));
console.log(
  '  camelCase("hello world"):',
  stringUtils.camelCase('hello world')
);
console.log('  snakeCase("helloWorld"):', stringUtils.snakeCase('helloWorld'));
console.log(
  '  kebabCase("hello_world"):',
  stringUtils.kebabCase('hello_world')
);
console.log(
  '  truncate("Hello World", 8):',
  stringUtils.truncate('Hello World', 8)
);
console.log(
  '  wordCount("The quick brown fox"):',
  stringUtils.wordCount('The quick brown fox')
);
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// EXERCISE 2: CommonJS Export Patterns
// ════════════════════════════════════════════════════════════════════════════════

/*
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Practice different CommonJS export patterns                                    │
│                                                                                  │
│  Task A: Export a single function (factory pattern)                             │
│  - Create a 'createCounter' module that exports a factory function              │
│  - Factory returns an object with: increment, decrement, getCount, reset        │
│                                                                                  │
│  Task B: Export a class                                                         │
│  - Create an 'EventEmitter' module that exports a class                         │
│  - Class has: on(event, handler), emit(event, data), off(event, handler)        │
│                                                                                  │
│  Task C: Export using 'exports' shorthand                                       │
│  - Create a 'validators' module using exports.xxx = ...                         │
│  - Include: isEmail, isUrl, isPhone, isEmpty                                    │
│                                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
*/

// Task A: Factory function export

const counterModule = CommonJS.createModule((module, exports) => {
  // TODO: Export createCounter factory function

  // Solution:
  function createCounter(initialValue = 0) {
    let count = initialValue;

    return {
      increment(amount = 1) {
        count += amount;
        return count;
      },

      decrement(amount = 1) {
        count -= amount;
        return count;
      },

      getCount() {
        return count;
      },

      reset() {
        count = initialValue;
        return count;
      },
    };
  }

  // Export single function
  module.exports = createCounter;
});

console.log('Exercise 2A - Factory Function Export:');
const counter = counterModule(10);
console.log('  Initial count:', counter.getCount());
console.log('  After increment:', counter.increment());
console.log('  After increment(5):', counter.increment(5));
console.log('  After decrement:', counter.decrement());
console.log('  After reset:', counter.reset());
console.log('');

// Task B: Class export

const eventEmitterModule = CommonJS.createModule((module, exports) => {
  // TODO: Export EventEmitter class

  // Solution:
  class EventEmitter {
    constructor() {
      this.events = new Map();
    }

    on(event, handler) {
      if (!this.events.has(event)) {
        this.events.set(event, new Set());
      }
      this.events.get(event).add(handler);
      return this;
    }

    emit(event, data) {
      const handlers = this.events.get(event);
      if (handlers) {
        handlers.forEach((handler) => handler(data));
      }
      return this;
    }

    off(event, handler) {
      const handlers = this.events.get(event);
      if (handlers) {
        handlers.delete(handler);
      }
      return this;
    }

    once(event, handler) {
      const onceHandler = (data) => {
        this.off(event, onceHandler);
        handler(data);
      };
      return this.on(event, onceHandler);
    }
  }

  // Export class
  module.exports = EventEmitter;
});

console.log('Exercise 2B - Class Export:');
const emitter = new eventEmitterModule();
emitter.on('greet', (name) => console.log(`  Hello, ${name}!`));
emitter.emit('greet', 'World');
console.log('');

// Task C: exports shorthand

const validators = CommonJS.createModule((module, exports) => {
  // TODO: Use exports.xxx = ... pattern

  // Solution:
  exports.isEmail = function (str) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
  };

  exports.isUrl = function (str) {
    try {
      new URL(str);
      return true;
    } catch {
      return false;
    }
  };

  exports.isPhone = function (str) {
    return /^[\+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/.test(str);
  };

  exports.isEmpty = function (value) {
    if (value === null || value === undefined) return true;
    if (typeof value === 'string') return value.trim() === '';
    if (Array.isArray(value)) return value.length === 0;
    if (typeof value === 'object') return Object.keys(value).length === 0;
    return false;
  };

  exports.isNumber = function (value) {
    return typeof value === 'number' && !isNaN(value);
  };
});

console.log('Exercise 2C - exports Shorthand:');
console.log(
  '  isEmail("test@email.com"):',
  validators.isEmail('test@email.com')
);
console.log('  isEmail("invalid"):', validators.isEmail('invalid'));
console.log(
  '  isUrl("https://example.com"):',
  validators.isUrl('https://example.com')
);
console.log('  isPhone("123-456-7890"):', validators.isPhone('123-456-7890'));
console.log('  isEmpty(""):', validators.isEmpty(''));
console.log('  isEmpty([1,2,3]):', validators.isEmpty([1, 2, 3]));
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// EXERCISE 3: AMD Modules with Dependencies
// ════════════════════════════════════════════════════════════════════════════════

/*
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Create AMD modules with dependencies                                           │
│                                                                                  │
│  Requirements:                                                                  │
│  1. Create 'constants' module with PI, E, GOLDEN_RATIO                          │
│  2. Create 'mathOps' module depending on 'constants'                            │
│     - circleArea(r), sphereVolume(r), goldenRectangle(width)                   │
│  3. Create 'formatter' module (no dependencies)                                 │
│     - formatNumber(n, decimals), formatPercent(n)                               │
│  4. Create 'calculator' module depending on 'mathOps' and 'formatter'           │
│     - calculateAndFormat methods combining both                                  │
│                                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
*/

// Clear AMD registry for clean start
AMD.clear();

// TODO: Define the AMD modules

// Solution:

// 1. Constants module (no dependencies)
AMD.define('constants', [], function () {
  return {
    PI: 3.14159265359,
    E: 2.71828182846,
    GOLDEN_RATIO: 1.61803398875,
  };
});

// 2. Math operations module (depends on constants)
AMD.define('mathOps', ['constants'], function (constants) {
  return {
    circleArea(radius) {
      return constants.PI * radius * radius;
    },

    circleCircumference(radius) {
      return 2 * constants.PI * radius;
    },

    sphereVolume(radius) {
      return (4 / 3) * constants.PI * Math.pow(radius, 3);
    },

    goldenRectangle(width) {
      return {
        width: width,
        height: width / constants.GOLDEN_RATIO,
      };
    },

    exponential(base, power) {
      return Math.pow(constants.E, power) * base;
    },
  };
});

// 3. Formatter module (no dependencies)
AMD.define('formatter', [], function () {
  return {
    formatNumber(num, decimals = 2) {
      return num.toFixed(decimals);
    },

    formatPercent(num, decimals = 1) {
      return (num * 100).toFixed(decimals) + '%';
    },

    formatCurrency(num, currency = '$') {
      return currency + num.toFixed(2);
    },

    formatWithCommas(num) {
      return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
    },
  };
});

// 4. Calculator module (depends on mathOps and formatter)
AMD.define(
  'calculator',
  ['mathOps', 'formatter'],
  function (mathOps, formatter) {
    return {
      calculateCircle(radius) {
        const area = mathOps.circleArea(radius);
        const circumference = mathOps.circleCircumference(radius);

        return {
          radius: radius,
          area: formatter.formatNumber(area),
          circumference: formatter.formatNumber(circumference),
        };
      },

      calculateSphere(radius) {
        const volume = mathOps.sphereVolume(radius);

        return {
          radius: radius,
          volume: formatter.formatNumber(volume),
        };
      },

      calculateGoldenRect(width) {
        const rect = mathOps.goldenRectangle(width);

        return {
          width: formatter.formatNumber(rect.width),
          height: formatter.formatNumber(rect.height),
          ratio: formatter.formatNumber(rect.width / rect.height),
        };
      },
    };
  }
);

// Test
console.log('Exercise 3 - AMD Modules with Dependencies:');

AMD.require(['calculator'], function (calc) {
  console.log('  Circle (r=5):', calc.calculateCircle(5));
  console.log('  Sphere (r=3):', calc.calculateSphere(3));
  console.log('  Golden Rectangle (w=100):', calc.calculateGoldenRect(100));
});

AMD.require(['constants', 'formatter'], function (constants, formatter) {
  console.log('  PI formatted:', formatter.formatNumber(constants.PI, 5));
  console.log(
    '  Golden Ratio:',
    formatter.formatNumber(constants.GOLDEN_RATIO, 5)
  );
});
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// EXERCISE 4: UMD Pattern Implementation
// ════════════════════════════════════════════════════════════════════════════════

/*
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Create a UMD (Universal Module Definition) module                              │
│                                                                                  │
│  Requirements:                                                                  │
│  1. Create a 'DateHelper' utility module                                        │
│  2. Should work in: AMD, CommonJS, and browser globals                          │
│  3. Include methods:                                                            │
│     - format(date, pattern) - format date as string                             │
│     - parse(str) - parse string to date                                         │
│     - addDays(date, days) - add days to date                                    │
│     - diffDays(date1, date2) - difference in days                               │
│     - isToday(date), isFuture(date), isPast(date)                               │
│                                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
*/

// TODO: Implement UMD module

// Solution:
(function (root, factory) {
  // AMD
  if (typeof AMD !== 'undefined' && AMD.define) {
    AMD.define('dateHelper', [], factory);
  }
  // CommonJS
  else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
  }
  // Browser globals
  else {
    root.DateHelper = factory();
  }
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
  // Private helpers
  function padZero(num) {
    return String(num).padStart(2, '0');
  }

  function toDate(value) {
    if (value instanceof Date) return value;
    return new Date(value);
  }

  // Public API
  return {
    format(date, pattern = 'YYYY-MM-DD') {
      const d = toDate(date);
      const year = d.getFullYear();
      const month = padZero(d.getMonth() + 1);
      const day = padZero(d.getDate());
      const hours = padZero(d.getHours());
      const minutes = padZero(d.getMinutes());
      const seconds = padZero(d.getSeconds());

      return pattern
        .replace('YYYY', year)
        .replace('MM', month)
        .replace('DD', day)
        .replace('HH', hours)
        .replace('mm', minutes)
        .replace('ss', seconds);
    },

    parse(str) {
      return new Date(str);
    },

    addDays(date, days) {
      const result = toDate(date);
      result.setDate(result.getDate() + days);
      return result;
    },

    subtractDays(date, days) {
      return this.addDays(date, -days);
    },

    diffDays(date1, date2) {
      const d1 = toDate(date1);
      const d2 = toDate(date2);
      const diffTime = Math.abs(d2 - d1);
      return Math.ceil(diffTime / (1000 * 60 * 60 * 24));
    },

    isToday(date) {
      const d = toDate(date);
      const today = new Date();
      return d.toDateString() === today.toDateString();
    },

    isFuture(date) {
      return toDate(date) > new Date();
    },

    isPast(date) {
      return toDate(date) < new Date();
    },

    isWeekend(date) {
      const day = toDate(date).getDay();
      return day === 0 || day === 6;
    },

    startOfDay(date) {
      const d = toDate(date);
      d.setHours(0, 0, 0, 0);
      return d;
    },

    endOfDay(date) {
      const d = toDate(date);
      d.setHours(23, 59, 59, 999);
      return d;
    },
  };
});

// Test via AMD
console.log('Exercise 4 - UMD Module:');
AMD.require(['dateHelper'], function (dateHelper) {
  const now = new Date();
  console.log('  format(now):', dateHelper.format(now));
  console.log(
    '  format(now, "DD/MM/YYYY"):',
    dateHelper.format(now, 'DD/MM/YYYY')
  );
  console.log(
    '  addDays(now, 7):',
    dateHelper.format(dateHelper.addDays(now, 7))
  );
  console.log('  isToday(now):', dateHelper.isToday(now));
  console.log(
    '  isFuture(tomorrow):',
    dateHelper.isFuture(dateHelper.addDays(now, 1))
  );
  console.log('  isWeekend(now):', dateHelper.isWeekend(now));
  console.log(
    '  diffDays between dates:',
    dateHelper.diffDays('2024-01-01', '2024-01-15'),
    'days'
  );
});
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// EXERCISE 5: Module Format Conversion
// ════════════════════════════════════════════════════════════════════════════════

/*
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Practice converting between module formats                                     │
│                                                                                  │
│  Given this ES Module code:                                                     │
│                                                                                  │
│  export const VERSION = '1.0.0';                                                │
│                                                                                  │
│  export function greet(name) {                                                  │
│      return `Hello, ${name}!`;                                                  │
│  }                                                                              │
│                                                                                  │
│  export default class Greeter {                                                 │
│      constructor(greeting = 'Hello') {                                          │
│          this.greeting = greeting;                                              │
│      }                                                                          │
│      greet(name) {                                                              │
│          return `${this.greeting}, ${name}!`;                                   │
│      }                                                                          │
│  }                                                                              │
│                                                                                  │
│  Task: Convert to CommonJS, AMD, and UMD formats                                │
│                                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
*/

console.log('Exercise 5 - Module Format Conversion:');

// A) CommonJS Version
console.log('\n  A) CommonJS Version:');

const greeterCJS = CommonJS.createModule((module, exports) => {
  // Named exports
  exports.VERSION = '1.0.0';

  exports.greet = function (name) {
    return `Hello, ${name}!`;
  };

  // Default export (as main export or separate)
  class Greeter {
    constructor(greeting = 'Hello') {
      this.greeting = greeting;
    }
    greet(name) {
      return `${this.greeting}, ${name}!`;
    }
  }

  // Option 1: Export class as default
  module.exports = Greeter;

  // Attach named exports to class
  module.exports.VERSION = '1.0.0';
  module.exports.greet = function (name) {
    return `Hello, ${name}!`;
  };
});

const GreeterCJS = greeterCJS;
const greeterInstance = new GreeterCJS('Hi');
console.log('    greet("World"):', GreeterCJS.greet('World'));
console.log(
  '    new Greeter().greet("World"):',
  greeterInstance.greet('World')
);
console.log('    VERSION:', GreeterCJS.VERSION);

// B) AMD Version
console.log('\n  B) AMD Version:');

AMD.define('greeterAMD', [], function () {
  var VERSION = '1.0.0';

  function greet(name) {
    return 'Hello, ' + name + '!';
  }

  function Greeter(greeting) {
    this.greeting = greeting || 'Hello';
  }

  Greeter.prototype.greet = function (name) {
    return this.greeting + ', ' + name + '!';
  };

  // Return object with all exports
  return {
    VERSION: VERSION,
    greet: greet,
    default: Greeter,
    Greeter: Greeter, // Also as named export
  };
});

AMD.require(['greeterAMD'], function (greeterModule) {
  console.log('    greet("World"):', greeterModule.greet('World'));
  var g = new greeterModule.Greeter('Hey');
  console.log('    new Greeter("Hey").greet("World"):', g.greet('World'));
  console.log('    VERSION:', greeterModule.VERSION);
});

// C) UMD Version
console.log('\n  C) UMD Version:');

(function (root, factory) {
  if (typeof AMD !== 'undefined' && AMD.define) {
    AMD.define('greeterUMD', [], factory);
  } else if (typeof module === 'object' && module.exports) {
    module.exports = factory();
  } else {
    root.GreeterModule = factory();
  }
})(typeof globalThis !== 'undefined' ? globalThis : this, function () {
  var VERSION = '1.0.0';

  function greet(name) {
    return 'Hello, ' + name + '!';
  }

  function Greeter(greeting) {
    this.greeting = greeting || 'Hello';
  }

  Greeter.prototype.greet = function (name) {
    return this.greeting + ', ' + name + '!';
  };

  // For UMD, typically export the main thing and attach extras
  Greeter.VERSION = VERSION;
  Greeter.greet = greet;

  return Greeter;
});

AMD.require(['greeterUMD'], function (Greeter) {
  console.log('    static greet("World"):', Greeter.greet('World'));
  var g = new Greeter('Howdy');
  console.log('    new Greeter("Howdy").greet("World"):', g.greet('World'));
  console.log('    VERSION:', Greeter.VERSION);
});

console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// BONUS EXERCISE: Plugin System
// ════════════════════════════════════════════════════════════════════════════════

/*
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Create a CommonJS-style plugin system                                          │
│                                                                                  │
│  Requirements:                                                                  │
│  1. Create a PluginManager that can:                                            │
│     - register(name, plugin) - add a plugin                                     │
│     - unregister(name) - remove a plugin                                        │
│     - execute(name, ...args) - run a plugin                                     │
│     - list() - list all plugins                                                 │
│  2. Plugins are modules that export an 'execute' function                       │
│  3. Create 2-3 sample plugins                                                   │
│                                                                                  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
*/

console.log('Bonus - Plugin System:');

const PluginManager = CommonJS.createModule((module, exports) => {
  const plugins = new Map();

  module.exports = {
    register(name, plugin) {
      if (typeof plugin.execute !== 'function') {
        throw new Error(`Plugin ${name} must have an execute function`);
      }
      plugins.set(name, plugin);
      console.log(`    [PluginManager] Registered: ${name}`);
      return this;
    },

    unregister(name) {
      plugins.delete(name);
      console.log(`    [PluginManager] Unregistered: ${name}`);
      return this;
    },

    execute(name, ...args) {
      const plugin = plugins.get(name);
      if (!plugin) {
        throw new Error(`Plugin not found: ${name}`);
      }
      return plugin.execute(...args);
    },

    list() {
      return Array.from(plugins.keys());
    },

    has(name) {
      return plugins.has(name);
    },
  };
});

// Sample plugins
const uppercasePlugin = CommonJS.createModule((module) => {
  module.exports = {
    name: 'uppercase',
    version: '1.0.0',
    execute(text) {
      return text.toUpperCase();
    },
  };
});

const reversePlugin = CommonJS.createModule((module) => {
  module.exports = {
    name: 'reverse',
    version: '1.0.0',
    execute(text) {
      return text.split('').reverse().join('');
    },
  };
});

const repeatPlugin = CommonJS.createModule((module) => {
  module.exports = {
    name: 'repeat',
    version: '1.0.0',
    execute(text, times = 2) {
      return text.repeat(times);
    },
  };
});

// Use the plugin system
PluginManager.register('uppercase', uppercasePlugin);
PluginManager.register('reverse', reversePlugin);
PluginManager.register('repeat', repeatPlugin);

console.log('  Available plugins:', PluginManager.list());
console.log(
  '  uppercase("hello"):',
  PluginManager.execute('uppercase', 'hello')
);
console.log('  reverse("hello"):', PluginManager.execute('reverse', 'hello'));
console.log('  repeat("hi", 3):', PluginManager.execute('repeat', 'hi', 3));
console.log('');

// ════════════════════════════════════════════════════════════════════════════════
// SUMMARY
// ════════════════════════════════════════════════════════════════════════════════

console.log(`
╔══════════════════════════════════════════════════════════════════════════════╗
ā•‘                 COMMONJS & AMD EXERCISES - COMPLETE                          ā•‘
╠══════════════════════════════════════════════════════════════════════════════╣
ā•‘                                                                              ā•‘
ā•‘  Completed Exercises:                                                        ā•‘
ā•‘  āœ“ Exercise 1: CommonJS String Utilities                                    ā•‘
ā•‘  āœ“ Exercise 2: CommonJS Export Patterns (factory, class, exports)           ā•‘
ā•‘  āœ“ Exercise 3: AMD Modules with Dependencies                                ā•‘
ā•‘  āœ“ Exercise 4: UMD Pattern Implementation                                   ā•‘
ā•‘  āœ“ Exercise 5: Module Format Conversion                                     ā•‘
ā•‘  āœ“ Bonus: Plugin System                                                     ā•‘
ā•‘                                                                              ā•‘
ā•‘  Key Patterns:                                                               ā•‘
ā•‘  • CommonJS: module.exports = value, require('module')                       ā•‘
ā•‘  • AMD: define(name, deps, factory), require(deps, callback)                 ā•‘
ā•‘  • UMD: Check for AMD, then CommonJS, then global                           ā•‘
ā•‘                                                                              ā•‘
ā•‘  Best Practices:                                                             ā•‘
ā•‘  • Use module.exports for single exports                                     ā•‘
ā•‘  • Use exports.x for multiple named exports                                  ā•‘
ā•‘  • Never reassign exports directly                                           ā•‘
ā•‘  • Declare dependencies explicitly in AMD                                    ā•‘
ā•‘  • Use UMD for library distribution                                          ā•‘
ā•‘                                                                              ā•‘
ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
`);
Exercises - JavaScript Tutorial | DeepML