JavaScript Modules - Exercise 3

Move shared values or operations into a third module so the original modules no longer import each other directly.

// shared.js
export const roles = { admin: 'Admin' };
// user.js and format.js can both import roles.js.

Each module may be evaluated before the other has finished initializing its exports. Move work behind a function or remove the cycle.

// Prefer calling after initialization:
export function getLabel() {
  return formatUser(user);
}

Use the module system required by the runtime and project configuration. ESM uses static import/export; CommonJS uses require/module.exports.

// ESM
export { add };
import { add } from './math.js';

// CommonJS
module.exports = { add };
const { add } = require('./math');

Use a dynamic import inside the event or condition that needs the feature.

button.addEventListener('click', async () => {
  const { openEditor } = await import('./editor.js');
  openEditor();
});

Dynamic imports return Promises, so handle failures with try...catch.

try {
  const feature = await import('./optional-feature.js');
  feature.start();
} catch (error) {
  showUnavailableMessage(error);
}

A regular top-level import has a literal module path, allowing tools to build the dependency graph before execution.

import { format } from './format.js';
// The path is known during build analysis.

Re-export only the public API from an entry module and keep internal files private to the feature.

// users/index.js
export { createUser } from './create-user.js';
export { findUser } from './find-user.js';

Keep modules focused, export pure functions where possible, and inject external dependencies instead of hiding them in module-level state.

export function total(items, taxRate) {
  return items.reduce((sum, item) => sum + item.price, 0) * (1 + taxRate);
}