Skip to content

Commit 4dada44

Browse files
committed
feat: add ESM and CommonJS dual module support (v1.1.0)
1 parent ad1eede commit 4dada44

16 files changed

Lines changed: 273 additions & 37 deletions

.changeset/major-v1.md

Lines changed: 0 additions & 20 deletions
This file was deleted.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ node_modules
99

1010
# Compiled Files
1111
dist
12+
dist/cjs
13+
dist/esm
14+
dist/types
1215
benchmark/dist
1316

1417
# Logs and databases

CHANGELOG.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,69 @@
11
# Changelog
22

3+
## 2.0.0
4+
5+
### Major Changes
6+
7+
- ad1eede: Production-ready v1.0.0 release with enterprise features:
8+
- Job timeout & cancellation with AbortController support
9+
- Priority queue system (HIGH, NORMAL, LOW priorities)
10+
- Worker health monitoring and automatic restart on failure
11+
- Real-time metrics and performance monitoring
12+
- Auto-scaling thread pool with configurable thresholds
13+
- Strict mode security validation (enabled by default)
14+
- Comprehensive benchmark suite vs competitors (Piscina, Workerpool, Tinypool)
15+
- Full TypeScript strict mode with zero linter errors
16+
- 20+ new comprehensive tests covering all features
17+
- Complete documentation overhaul (README, SECURITY, CHANGELOG, CONTRIBUTING)
18+
- Advanced examples for all features
19+
- Event-driven pool closing (replaced inefficient polling)
20+
- Enhanced error handling with full stack trace preservation
21+
22+
### Minor Changes
23+
24+
- Add full ESM (ES Modules) and CommonJS dual module support:
25+
- Dual build system with separate CJS and ESM outputs
26+
- Proper package.json exports field with conditional exports
27+
- Module type hints in dist folders for better resolution
28+
- Universal worker path resolution for both module systems
29+
- Compatibility tests for both ESM and CJS
30+
- Tree-shaking support in ESM builds
31+
- Works seamlessly in modern bundlers (Vite, Rollup) and legacy tools (Webpack, Node CJS)
32+
- Fully backward compatible - existing CommonJS users unaffected
33+
34+
## 1.1.0
35+
36+
### Minor Changes
37+
38+
- **Full ESM (ES Modules) support**: Package now supports both ESM and CommonJS
39+
- **Dual build system**: Separate builds for CJS (`dist/cjs`) and ESM (`dist/esm`) with proper conditional exports
40+
- **Package.json exports field**: Proper conditional exports for modern tooling and tree-shaking
41+
- **Module type hints**: package.json files in dist folders for better module resolution
42+
- **Compatibility tests**: Dedicated ESM and CJS compatibility test suites
43+
- **Universal worker path resolution**: Works seamlessly in both ESM and CJS environments
44+
45+
### Changed
46+
47+
- Build system now outputs three separate builds: `dist/cjs`, `dist/esm`, `dist/types`
48+
- Worker path resolution improved to detect and handle both module systems automatically
49+
- Package exports configured for optimal tree-shaking in ESM
50+
- Documentation updated with both ESM and CJS usage examples
51+
52+
### Technical Details
53+
54+
- Main entry point (`require`): `./dist/cjs/index.js`
55+
- Module entry point (`import`): `./dist/esm/index.js`
56+
- TypeScript types: `./dist/types/index.d.ts`
57+
- Requires Node.js 12.20+ for conditional exports support
58+
59+
### Benefits
60+
61+
- ✅ Modern ESM support for Vite, Rollup, and modern bundlers
62+
- ✅ Tree-shaking support in ESM builds
63+
- ✅ Backward compatible with CommonJS projects
64+
- ✅ Future-proof module architecture
65+
- ✅ Works in both Next.js App Router (ESM) and Pages Router (CJS)
66+
367
## 1.0.0
468

569
### Major Changes

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ NodeSwarm is a high-performance, feature-rich library for managing worker thread
1212

1313
- **🚀 High Performance**: Optimized for throughput and latency
1414
- **⚡ Simple API**: Execute any function in a thread with one line
15+
- **📦 Dual Module Support**: Works with both ESM and CommonJS
1516
- **🎯 Priority Queue**: HIGH, NORMAL, LOW priority job scheduling
1617
- **⏱️ Timeout & Cancellation**: Built-in timeout and AbortController support
1718
- **📊 Metrics**: Real-time monitoring of pool performance
@@ -37,6 +38,8 @@ pnpm add nodeswarm
3738

3839
## Quick Start
3940

41+
### ESM (Modern)
42+
4043
```typescript
4144
import { ThreadPool } from "nodeswarm";
4245

@@ -49,6 +52,21 @@ console.log(result); // 15
4952
await pool.close();
5053
```
5154

55+
### CommonJS (Legacy)
56+
57+
```javascript
58+
const { ThreadPool } = require("nodeswarm");
59+
60+
(async () => {
61+
const pool = new ThreadPool();
62+
63+
const result = await pool.thread((a, b) => a + b, 5, 10);
64+
console.log(result); // 15
65+
66+
await pool.close();
67+
})();
68+
```
69+
5270
## Performance
5371

5472
NodeSwarm delivers excellent performance across different workload types:

package.json

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,49 @@
11
{
22
"name": "nodeswarm",
3-
"version": "1.0.0",
3+
"version": "2.0.0",
44
"description": "Lightweight library for managing worker threads and parallel execution of tasks in Node.js.",
5-
"main": "dist/index.js",
6-
"types": "dist/index.d.ts",
5+
"type": "commonjs",
6+
"main": "./dist/cjs/index.js",
7+
"module": "./dist/esm/index.js",
8+
"types": "./dist/types/index.d.ts",
9+
"exports": {
10+
".": {
11+
"import": {
12+
"types": "./dist/types/index.d.ts",
13+
"default": "./dist/esm/index.js"
14+
},
15+
"require": {
16+
"types": "./dist/types/index.d.ts",
17+
"default": "./dist/cjs/index.js"
18+
}
19+
},
20+
"./package.json": "./package.json"
21+
},
22+
"files": [
23+
"dist",
24+
"README.md",
25+
"LICENSE",
26+
"CHANGELOG.md",
27+
"SECURITY.md"
28+
],
729
"repository": "git@github.com:dwekat/nodeswarm.git",
830
"bugs": {
931
"url": "https://github.com/dwekat/nodeswarm/issues"
1032
},
1133
"homepage": "https://github.com/dwekat/nodeswarm#readme",
1234
"scripts": {
13-
"build": "rimraf dist && tsc",
35+
"build": "npm run build:clean && npm run build:cjs && npm run build:fix-cjs && npm run build:esm && npm run build:types && npm run build:hints",
36+
"build:clean": "rimraf dist",
37+
"build:cjs": "tsc -p tsconfig.cjs.json",
38+
"build:fix-cjs": "node scripts/fix-cjs-imports.js",
39+
"build:esm": "tsc -p tsconfig.esm.json",
40+
"build:types": "tsc -p tsconfig.types.json",
41+
"build:hints": "node scripts/create-package-hints.js",
1442
"build:benchmark": "tsc -p benchmark/tsconfig.json",
1543
"pretest": "rimraf test_tmp && tsc -p tsconfig.worker.json",
1644
"posttest": "rimraf test_tmp",
1745
"test": "jest",
46+
"test:compat": "node test/cjs-compat.test.cjs && node test/esm-compat.test.mjs",
1847
"benchmark": "npm run build && npm run build:benchmark && node benchmark/dist/index.js",
1948
"benchmark:compare": "npm run build && npm run build:benchmark && node benchmark/dist/compare.js",
2049
"prepublishOnly": "npm run build",

scripts/create-package-hints.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Create package.json type hints in dist folders
4+
*/
5+
const fs = require('fs');
6+
const path = require('path');
7+
8+
const cjsPackage = { type: 'commonjs' };
9+
const esmPackage = { type: 'module' };
10+
11+
const cjsPath = path.join(__dirname, '../dist/cjs/package.json');
12+
const esmPath = path.join(__dirname, '../dist/esm/package.json');
13+
14+
// Ensure directories exist
15+
fs.mkdirSync(path.dirname(cjsPath), { recursive: true });
16+
fs.mkdirSync(path.dirname(esmPath), { recursive: true });
17+
18+
// Write package.json files
19+
fs.writeFileSync(cjsPath, JSON.stringify(cjsPackage, null, 2) + '\n');
20+
fs.writeFileSync(esmPath, JSON.stringify(esmPackage, null, 2) + '\n');
21+
22+
console.log('✓ Created package.json type hints in dist folders');
23+

scripts/fix-cjs-imports.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Fix import.meta references in CJS build
4+
* Replaces the ESM path code with a throw statement since it's unreachable
5+
*/
6+
const fs = require('fs');
7+
const path = require('path');
8+
9+
const threadPoolPath = path.join(__dirname, '../dist/cjs/ThreadPool.js');
10+
11+
let content = fs.readFileSync(threadPoolPath, 'utf8');
12+
13+
// Replace the import.meta.url line with a throw
14+
// This code is unreachable in CJS (guarded by __dirname check)
15+
// but Node still parses it and throws syntax error
16+
content = content.replace(
17+
/const url = new URL\("\.\/worker\.js", import\.meta\.url\);/g,
18+
'throw new Error("This code path should never execute in CommonJS");'
19+
);
20+
21+
fs.writeFileSync(threadPoolPath, content, 'utf8');
22+
console.log('✓ Fixed import.meta references in CJS build');
23+

src/ThreadPool.ts

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import os from "os";
2-
import { resolve } from "path";
2+
import { resolve, dirname } from "path";
3+
import { fileURLToPath } from "url";
34
import { Worker } from "worker_threads";
4-
import { PriorityQueue } from "./priorityQueue";
5-
import { MetricsTracker } from "./metrics";
6-
import { validateFunction, validateArguments } from "./validation";
5+
import { PriorityQueue } from "./priorityQueue.js";
6+
import { MetricsTracker } from "./metrics.js";
7+
import { validateFunction, validateArguments } from "./validation.js";
78
import {
89
ThreadPoolConfig,
910
ThreadOptions,
@@ -12,10 +13,38 @@ import {
1213
WorkerMessage,
1314
ThreadPoolMetrics,
1415
WorkerState,
15-
} from "./types";
16+
} from "./types.js";
1617

1718
// Re-export WorkerMessage for worker.ts
18-
export type { WorkerMessage } from "./types";
19+
export type { WorkerMessage } from "./types.js";
20+
21+
/**
22+
* Get worker path - resolved at module load time
23+
* In CJS: uses __dirname which is always defined
24+
* In ESM: uses import.meta.url
25+
*
26+
* Note: The __dirname check returns early in CJS builds, so the import.meta
27+
* code is never executed in CJS (avoiding syntax errors).
28+
*/
29+
let cachedWorkerPath: string | null = null;
30+
31+
function getWorkerPath(): string {
32+
if (cachedWorkerPath) return cachedWorkerPath;
33+
34+
// CommonJS: __dirname is defined
35+
// This check succeeds and returns in CJS, so import.meta below never runs
36+
if (typeof __dirname !== "undefined") {
37+
cachedWorkerPath = resolve(__dirname, "./worker.js");
38+
return cachedWorkerPath;
39+
}
40+
41+
// ESM: __dirname is not defined, use import.meta.url
42+
// This code only executes in ESM builds where import.meta is available
43+
// @ts-ignore - TypeScript may not recognize import.meta in all contexts
44+
const url = new URL("./worker.js", import.meta.url);
45+
cachedWorkerPath = fileURLToPath(url);
46+
return cachedWorkerPath;
47+
}
1948

2049
/**
2150
* ThreadPool class manages a pool of worker threads and schedules jobs
@@ -31,7 +60,7 @@ export class ThreadPool {
3160
private metrics: MetricsTracker;
3261
private healthCheckInterval?: NodeJS.Timeout;
3362

34-
private static readonly workerPath = resolve(__dirname, "./worker.js");
63+
private static readonly workerPath = getWorkerPath();
3564
private readonly config: Required<ThreadPoolConfig>;
3665

3766
/**

src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export * from "./ThreadPool";
2-
export * from "./types";
3-
export { PriorityQueue } from "./priorityQueue";
4-
export { MetricsTracker } from "./metrics";
1+
export * from "./ThreadPool.js";
2+
export * from "./types.js";
3+
export { PriorityQueue } from "./priorityQueue.js";
4+
export { MetricsTracker } from "./metrics.js";

src/priorityQueue.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Job, Priority } from "./types";
1+
import { Job, Priority } from "./types.js";
22

33
/**
44
* Priority queue implementation for job scheduling

0 commit comments

Comments
 (0)