From harness-claude
Write Node.js ES modules correctly using import.meta.url, package.json type, and CJS interop. Covers setup, __dirname replacement, dynamic/JSON imports, top-level await, file extensions, TypeScript config for migration.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Write Node.js ES modules correctly using import.meta.url, package.json type, and CJS interop
Provides decision trees and references for JavaScript/Node.js async patterns, module systems, event loop, runtime internals, and ES2024+ features like Promise.withResolvers.
Writes, debugs, and refactors JavaScript using ES2023+ features, async/await, ESM modules, and Node.js APIs. For vanilla JS apps, browser/Node performance, Web Workers, and Fetch API.
Provides expertise in modern JavaScript with ES6+ features, async patterns like promises and async/await, event loops, Node.js APIs, and browser/Node compatibility. Use for building, debugging performance, and migrating legacy JS.
Share bugs, ideas, or general feedback.
Write Node.js ES modules correctly using import.meta.url, package.json type, and CJS interop
__dirname and __filename equivalents in ESMpackage.json:{ "type": "module" }
All .js files are now ESM. Use .cjs extension for CommonJS files.
__dirname and __filename:import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const configPath = join(__dirname, 'config.json');
const { default: chalk } = await import('chalk');
// Conditional imports
const db =
process.env.DB === 'postgres' ? await import('./db/postgres.js') : await import('./db/sqlite.js');
import config from './config.json' with { type: 'json' };
// Or dynamically
const pkg = await import('./package.json', { with: { type: 'json' } });
// Default import for CJS modules
import express from 'express';
// Named imports may not work for all CJS modules
// Use default import + destructure instead
import pkg from 'lodash';
const { pick, omit } = pkg;
// Works at module top level in ESM (not in CJS)
const config = await loadConfig();
const db = await connectDatabase(config.dbUrl);
export { db, config };
// CJS: works without extension
const { foo } = require('./utils');
// ESM: extension required
import { foo } from './utils.js'; // .js even for .ts files with Node16 module resolution
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022"
}
}
ES modules are the standard module system for JavaScript. Node.js supports them natively since v12 (stable since v14). ESM is statically analyzable, supports tree-shaking, and uses import/export syntax.
ESM vs CJS differences:
import/export, static analysis, async loading, strict mode by defaultrequire/module.exports, dynamic, synchronous, non-strict by defaultrequire() ESM (use dynamic import() instead)import.meta: ESM-only global with module metadata:
import.meta.url — file URL of the current module (file:///path/to/module.js)import.meta.resolve() — resolve a module specifier relative to the current moduleDual package publishing: Libraries can support both ESM and CJS consumers:
{
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
}
}
Trade-offs:
https://nodejs.org/api/esm.html