Manage Apple Reminders via the `remindctl` CLI on macOS (list, add, edit, complete, delete)....
npx skills add jon23d/skillz --skill "esm"
Install specific skill from multi-skill repository
# Description
Use when writing JavaScript or TypeScript modules, creating Node.js projects, adding imports or exports, setting up package.json, tsconfig, or resolving file paths β ensure modern ESM is used instead of CommonJS.
# SKILL.md
name: esm
description: Use when writing JavaScript or TypeScript modules, creating Node.js projects, adding imports or exports, setting up package.json, tsconfig, or resolving file paths β ensure modern ESM is used instead of CommonJS.
ESM: Modern JavaScript Modules
Overview
Always use ECMAScript Modules (ESM). Never use CommonJS (require, module.exports, __dirname, __filename). This applies to all new files and to any existing file you edit β migrate it to ESM in the same pass.
Rules β no exceptions
Imports and exports:
- Use import / export β never require() or module.exports
- Always include the .js extension on local imports (even when the source is .ts)
- Use named exports by default; use default exports only when the module has a single obvious export
package.json:
- Always include "type": "module" in every package.json
- Never omit it, even for small scripts or one-off utilities
Path resolution (__dirname / __filename):
- These globals do not exist in ESM. Use this pattern instead:
js
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
- Prefer import.meta.url directly when only a URL is needed (e.g. new URL('../data.json', import.meta.url))
Node.js built-ins:
- Use the node: protocol prefix: import fs from 'node:fs', import path from 'node:path'
Dynamic imports:
- Use await import('./module.js') when lazy-loading is needed β not require()
TypeScript
Use "module": "NodeNext" and "moduleResolution": "NodeNext" in tsconfig.json. This is the only correct configuration for ESM TypeScript targeting Node.js.
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"outDir": "dist"
}
}
- Write
.jsextensions in import paths even in.tsfiles β TypeScript resolves the source but Node.js loads the compiled output import typefor type-only imports to avoid runtime overhead
// Correct in a .ts file
import { formatDate } from './utils.js';
import type { Config } from './config.js';
Migrating existing CJS files
When you edit any file that uses CommonJS, convert the whole file to ESM in the same edit:
- Replace all require() with import
- Replace all module.exports = ... / exports.x = ... with export
- Replace __dirname / __filename with the fileURLToPath pattern
- Add or confirm "type": "module" in package.json
Common mistakes
- Missing
"type": "module"β Node.js defaults to CJS, breaking allimportstatements. Fix: add"type": "module"topackage.json. - Missing
.jsextension on local imports β ESM does not auto-resolve extensions.import './utils'fails;import './utils.js'works. - Using
__dirnamedirectly β Not defined in ESM. Use thefileURLToPathpattern above. require()for JSON files β UseJSON.parse(await fs.readFile(new URL('./data.json', import.meta.url), 'utf8'))or the import assertion syntax if supported.- Mixing CJS and ESM in the same project β Pick ESM. CJS dependencies can still be imported via
importβ CJS interop works one-way (ESM can import CJS; CJS cannotrequireESM). "module": "CommonJS"in tsconfig β Incompatible with ESM output. Use"NodeNext".
Quick reference
// package.json
{ "type": "module" }
// Named export
export function formatDate(date) { ... }
export const config = { ... };
// Default export
export default class Server { ... }
// Import local module (extension required)
import { formatDate, slugify } from './utils.js';
// Import node built-in
import { readFile } from 'node:fs/promises';
import { join, dirname } from 'node:path';
// __dirname equivalent
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
// JSON file
const data = JSON.parse(
await readFile(new URL('./data.json', import.meta.url), 'utf8')
);
// Dynamic import
const { default: mod } = await import('./plugin.js');
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.