Skip to main content

Module Development

Guide to creating new utility modules.

Creating a New Module​

1. Choose Location​

Place your module in the appropriate utils/ subdirectory:

utils/
β”œβ”€β”€ formatting/ # Data formatters
β”œβ”€β”€ maps/ # Map functionality
β”œβ”€β”€ charts/ # Chart functionality
β”œβ”€β”€ state/ # State management
β”œβ”€β”€ files/ # File operations
β”œβ”€β”€ ui/ # UI components
└── your-category/

2. Create Module File​

// utils/formatting/formatPower.js

/**
* Power formatting utilities.
*
* @module formatPower
*/

/**
* Formats power value for display.
*
* @example
* formatPower(250); // "250 W"
* formatPower(1500); // "1.5 kW"
*
* @param {number} watts - Power in watts
* @param {Object} [options] - Formatting options
* @param {number} [options.decimals=0] - Decimal places. Default is `0`
*
* @returns {string} Formatted power string
*/
export function formatPower(watts, options = {}) {
const { decimals = 0 } = options;

if (watts >= 1000) {
return `${(watts / 1000).toFixed(1)} kW`;
}

return `${watts.toFixed(decimals)} W`;
}

/**
* Converts power to watts per kilogram.
*
* @param {number} watts - Power in watts
* @param {number} weightKg - Weight in kilograms
*
* @returns {number} Watts per kilogram
*/
export function powerToWkg(watts, weightKg) {
if (weightKg <= 0) {
throw new Error("Weight must be positive");
}
return watts / weightKg;
}

3. Add Tests​

// tests/unit/formatPower.test.js

import { describe, it, expect } from "vitest";
import { formatPower, powerToWkg } from "../../utils/formatting/formatPower.js";

describe("formatPower", () => {
it("should format watts", () => {
expect(formatPower(250)).toBe("250 W");
});

it("should format kilowatts for large values", () => {
expect(formatPower(1500)).toBe("1.5 kW");
});

it("should handle zero", () => {
expect(formatPower(0)).toBe("0 W");
});
});

describe("powerToWkg", () => {
it("should calculate watts per kg", () => {
expect(powerToWkg(300, 75)).toBe(4);
});

it("should throw for zero weight", () => {
expect(() => powerToWkg(300, 0)).toThrow();
});
});

4. Export from Index​

If your category has an index file:

// utils/formatting/index.js
export { formatDistance } from "./formatDistance.js";
export { formatDuration } from "./formatDuration.js";
export { formatPower, powerToWkg } from "./formatPower.js"; // Add

5. Import and Use​

// In other modules
import { formatPower } from "../utils/formatting/formatPower.js";

// Or from index
import { formatPower } from "../utils/formatting/index.js";

Module Template​

/**
* [Module Description]
*
* @module {undefined} moduleName
*/

// Constants
const DEFAULT_OPTIONS = {
// Default values
};

/**
* [Function description]
*
* @example
* // Usage example
*
* @param {type} paramName - Parameter description
* @param {Object} [options] - Options object
*
* @returns {type} Return description
*/
export function functionName(param, options = {}) {
// Merge options with defaults
const opts = { ...DEFAULT_OPTIONS, ...options };

// Validate inputs
if (!isValid(param)) {
throw new Error("Invalid parameter");
}

// Implementation
return result;
}

Best Practices​

Do​

  • βœ… Single responsibility
  • βœ… Clear function names
  • βœ… Comprehensive JSDoc
  • βœ… Input validation
  • βœ… Error handling
  • βœ… Unit tests

Don't​

  • ❌ Side effects
  • ❌ Global state
  • ❌ Circular dependencies
  • ❌ DOM manipulation (in utility modules)
  • ❌ Console.log in production

Module Categories​

Formatting Modules​

Transform data for display:

export function formatValue(value) {
// Transform to display string
return displayString;
}

Utility Modules​

General-purpose helpers:

export function calculateMetric(data) {
// Compute and return result
return result;
}

UI Modules​

Handle user interface:

export function setupComponent(element) {
// Attach event listeners
// Update DOM
}

Next: Testing β†’