diff --git a/.eslintrc b/.eslintrc index 8f6f5f7..9bcdb46 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,6 @@ { "extends": [ - "eslint-config-egg" + "eslint-config-egg/typescript", + "eslint-config-egg/lib/rules/enforce-node-prefix" ] } diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 4b0c095..c70132d 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -3,7 +3,6 @@ name: CI on: push: branches: [ master ] - pull_request: branches: [ master ] @@ -13,4 +12,6 @@ jobs: uses: node-modules/github-actions/.github/workflows/node-test.yml@master with: os: 'ubuntu-latest, macos-latest, windows-latest' - version: '14, 16, 18, 20' + version: '18.19.0, 18, 20, 22' + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 996d63b..3263c70 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,5 +10,3 @@ jobs: secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GIT_TOKEN: ${{ secrets.GIT_TOKEN }} - with: - checkTest: false diff --git a/.gitignore b/.gitignore index c0a6490..aad1f5f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ results node_modules npm-debug.log coverage/ +.tshy* +.eslintcache +dist diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e8c6521 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2015-present node-modules and other contributors. +Copyright (c) 2014 - 2015 fengmk2 and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index bf6e10f..2573058 100644 --- a/README.md +++ b/README.md @@ -25,84 +25,45 @@ npm install sendmessage --save ### master.js -```js -var childprocess = require('child_process'); -var sendmessage = require('sendmessage'); +```ts +import { fork } from 'node:child_process'; +import sendmessage from 'sendmessage'; + +const worker = fork('./worker.js'); -var worker = childprocess.fork('./worker.js'); -sendmessage(worker, {hi: 'this is a message to worker'}); +sendmessage(worker, { hi: 'this is a message to worker' }); ``` ### worker.js -```js -var sendmessage = require('sendmessage'); +```ts +import sendmessage from 'sendmessage'; -sendmessage(process, {hello: 'this is a message to master'}); +sendmessage(process, { hello: 'this is a message to master' }); ``` ## API -### #sendmessage(childprocess, message) +### #sendmessage(childProcess, message) Send a cross process message. If a process is not child process, this will just call `process.emit('message', message)` instead. -- childprocess: child process instance +- childProcess: child process instance - message: the message need to send ```js -sendmessage(process, {hello: 'this is a message to master'}); +sendmessage(process, { hello: 'this is a message to master' }); ``` You can switch to `process.emit('message', message)` using `process.env.SENDMESSAGE_ONE_PROCESS` -## Test - -```bash -npm install -npm test -``` - -### Coverage - -```bash -npm run ci -``` - ## License -(The MIT License) - -Copyright (c) 2014 - 2015 fengmk2 and other contributors - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - +[MIT](LICENSE) ## Contributors -|[
fengmk2](https://github.com/fengmk2)
|[
popomore](https://github.com/popomore)
|[
semantic-release-bot](https://github.com/semantic-release-bot)
|[
sjfkai](https://github.com/sjfkai)
| -| :---: | :---: | :---: | :---: | - - -This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Tue Jun 13 2023 20:42:15 GMT+0800`. +[![Contributors](https://contrib.rocks/image?repo=node-modules/sendmessage)](https://github.com/node-modules/sendmessage/graphs/contributors) - +Made with [contributors-img](https://contrib.rocks). diff --git a/package.json b/package.json index a74ea8a..4a44a7b 100644 --- a/package.json +++ b/package.json @@ -1,32 +1,36 @@ { "name": "sendmessage", "version": "2.0.0", + "engines": { + "node": ">= 18.19.0" + }, "description": "Send a cross process message if message channel is connected.", - "main": "index.js", - "files": [ - "index.js" - ], "scripts": { - "test": "mocha --exit -t 5000 test/*.test.js", + "lint": "eslint --cache src --ext .ts", + "pretest": "npm run prepublishOnly && attw --pack", + "test": "npm run lint && mocha --exit -t 5000 test/*.test.js", "ci": "c8 -r lcov -r text -r text-summary npm test", - "lint": "eslint .", - "contributors": "git-contributor" + "prepublishOnly": "tshy && tshy-after" }, "dependencies": {}, "devDependencies": { - "c8": "^7.14.0", - "eslint": "^8.42.0", - "eslint-config-egg": "^12.2.1", - "git-contributor": "^2.1.5", - "mm": "^3.3.0", - "mocha": "^10.2.0", - "should": "*" + "@arethetypeswrong/cli": "^0.15.3", + "@eggjs/tsconfig": "1", + "@types/mocha": "10", + "@types/node": "20", + "c8": "^10.1.2", + "eslint": "8", + "eslint-config-egg": "13", + "mm": "3", + "mocha": "^10.4.0", + "tshy": "1", + "tshy-after": "1", + "typescript": "5" }, "homepage": "https://github.com/node-modules/sendmessage", "repository": { "type": "git", - "url": "git://github.com/node-modules/sendmessage.git", - "web": "https://github.com/node-modules/sendmessage" + "url": "git://github.com/node-modules/sendmessage.git" }, "bugs": { "url": "https://github.com/node-modules/sendmessage/issues" @@ -38,9 +42,34 @@ "message", "channel closed" ], - "engines": { - "node": ">= 14.17.0" - }, "author": "fengmk2 (https://github.com/fengmk2)", - "license": "MIT" + "license": "MIT", + "type": "module", + "tshy": { + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json" + } + }, + "exports": { + ".": { + "import": { + "source": "./src/index.ts", + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "source": "./src/index.ts", + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + }, + "./package.json": "./package.json" + }, + "files": [ + "dist", + "src" + ], + "types": "./dist/commonjs/index.d.ts", + "main": "./dist/commonjs/index.js" } diff --git a/index.js b/src/index.ts similarity index 53% rename from index.js rename to src/index.ts index 6fd363b..c8ae5aa 100644 --- a/index.js +++ b/src/index.ts @@ -1,16 +1,35 @@ -const { isMainThread, parentPort } = require('worker_threads'); +import { debuglog } from 'node:util'; +import { isMainThread, parentPort } from 'node:worker_threads'; +import { EventEmitter } from 'node:events'; + +const debug = debuglog('sendmessage'); let IS_NODE_DEV_RUNNER = /node\-dev$/.test(process.env._ || ''); if (!IS_NODE_DEV_RUNNER && process.env.IS_NODE_DEV_RUNNER) { IS_NODE_DEV_RUNNER = true; } +debug('IS_NODE_DEV_RUNNER: %s', IS_NODE_DEV_RUNNER); + +export interface ChildProcessOrWorker extends EventEmitter { + // Worker + postMessage?(message: unknown): void; + // ChildProcess + send?(message: unknown): boolean; + connected?: boolean; + pid?: number; + process?: { + connected?: boolean; + pid?: number; + }; +} -module.exports = function send(child, message) { +export default function sendmessage(child: ChildProcessOrWorker, message: unknown) { if ( isMainThread // not in worker thread && typeof child.postMessage !== 'function' // child is not worker && typeof child.send !== 'function' ) { + debug('child is master process, emit message: %j', message); // not a child process return setImmediate(child.emit.bind(child, 'message', message)); } @@ -18,16 +37,20 @@ module.exports = function send(child, message) { if (IS_NODE_DEV_RUNNER || process.env.SENDMESSAGE_ONE_PROCESS) { // run with node-dev, only one process // https://github.com/node-modules/sendmessage/issues/1 + debug('node-dev: %s or SENDMESSAGE_ONE_PROCESS: %s, emit message: %j', + IS_NODE_DEV_RUNNER, process.env.SENDMESSAGE_ONE_PROCESS, message); return setImmediate(child.emit.bind(child, 'message', message)); } // child is worker if (typeof child.postMessage === 'function') { + debug('child is worker, postMessage: %j', message); return child.postMessage(message); } // in worker thread if (!isMainThread) { - return parentPort.postMessage(message); + debug('in worker thread, parentPort.postMessage: %j', message); + return parentPort!.postMessage(message); } // cluster.fork(): child.process is process @@ -35,12 +58,13 @@ module.exports = function send(child, message) { const connected = child.process ? child.process.connected : child.connected; if (connected) { - return child.send(message); + debug('child is process, send: %j', message); + return child.send!(message); } - // just log warnning message + // just log warning message const pid = child.process ? child.process.pid : child.pid; const err = new Error('channel closed'); console.warn('[%s][sendmessage] WARN pid#%s channel closed, nothing send\nstack: %s', Date(), pid, err.stack); -}; +} diff --git a/test/child.js b/test/child.js index f6cf520..4f0443e 100644 --- a/test/child.js +++ b/test/child.js @@ -1,5 +1,5 @@ -const { isMainThread, parentPort } = require('worker_threads'); -const sendmessage = require('..'); +import { isMainThread, parentPort } from 'node:worker_threads'; +import sendmessage from '../dist/esm/index.js'; const listener = function(message) { if (message.disconnect) { @@ -11,7 +11,9 @@ const listener = function(message) { got: message, }); }; + process.on('message', listener); + if (!isMainThread) { // worker thread parentPort.on('message', listener); diff --git a/test/sendmessage.test.js b/test/sendmessage.test.js index 43c4e61..194f5ca 100644 --- a/test/sendmessage.test.js +++ b/test/sendmessage.test.js @@ -1,18 +1,23 @@ -const path = require('path'); -require('should'); -const childprocess = require('child_process'); -const cluster = require('cluster'); -const workerThreads = require('worker_threads'); -const mm = require('mm'); -const sendmessage = require('../'); - -describe('sendmessage.test.js', function() { +import { strict as assert } from 'node:assert'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import childprocess from 'node:child_process'; +import cluster from 'node:cluster'; +import workerThreads from 'node:worker_threads'; +import mm from 'mm'; +import sendmessage from '../dist/esm/index.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const childFile = path.join(__dirname, 'child.js'); + +describe('sendmessage.test.js', () => { afterEach(mm.restore); - describe('single process', function() { - it('should emit message when process is not child process', function(done) { - process.once('message', function(message) { - message.should.eql({ + describe('single process', () => { + it('should emit message when process is not child process', done => { + process.once('message', message => { + assert.deepEqual(message, { foo: 'bar', }); done(); @@ -21,12 +26,11 @@ describe('sendmessage.test.js', function() { }); }); - describe('child_process.fork()', function() { - it('should send cross process message', function(done) { - const childfile = path.join(__dirname, 'child.js'); - const child = childprocess.fork(childfile); + describe('child_process.fork()', () => { + it('should send cross process message', done => { + const child = childprocess.fork(childFile); child.once('message', function(message) { - message.should.eql({ + assert.deepEqual(message, { from: 'child', hi: 'this is a message send to master', }); @@ -37,7 +41,7 @@ describe('sendmessage.test.js', function() { }); child.once('message', function(message) { - message.should.eql({ + assert.deepEqual(message, { from: 'child', got: { from: 'master', @@ -49,11 +53,10 @@ describe('sendmessage.test.js', function() { }); }); - it('should show warnning message when channel closed', function(done) { - const childfile = path.join(__dirname, 'child.js'); - const child = childprocess.fork(childfile); + it('should show warning message when channel closed', done => { + const child = childprocess.fork(childFile); child.once('message', function(message) { - message.should.eql({ + assert.deepEqual(message, { from: 'child', hi: 'this is a message send to master', }); @@ -63,7 +66,7 @@ describe('sendmessage.test.js', function() { disconnect: true, }); - child.once('disconnect', function() { + child.once('disconnect', () => { console.log('child#%d disconnected', child.pid); sendmessage(child, { from: 'master', @@ -75,15 +78,14 @@ describe('sendmessage.test.js', function() { }); }); - describe('cluster.fork()', function() { - it('should send cross process message', function(done) { - const childfile = path.join(__dirname, 'child.js'); - cluster.setupMaster({ - exec: childfile, + describe('cluster.fork()', () => { + it('should send cross process message', done => { + cluster.setupPrimary({ + exec: childFile, }); const child = cluster.fork(); child.once('message', function(message) { - message.should.eql({ + assert.deepEqual(message, { from: 'child', hi: 'this is a message send to master', }); @@ -94,7 +96,7 @@ describe('sendmessage.test.js', function() { }); child.once('message', function(message) { - message.should.eql({ + assert.deepEqual(message, { from: 'child', got: { from: 'master', @@ -106,14 +108,13 @@ describe('sendmessage.test.js', function() { }); }); - it('should show warnning message when channel closed', function(done) { - const childfile = path.join(__dirname, 'child.js'); - cluster.setupMaster({ - exec: childfile, + it('should show warning message when channel closed', done => { + cluster.setupPrimary({ + exec: childFile, }); const child = cluster.fork(); child.once('message', function(message) { - message.should.eql({ + assert.deepEqual(message, { from: 'child', hi: 'this is a message send to master', }); @@ -123,7 +124,7 @@ describe('sendmessage.test.js', function() { disconnect: true, }); - child.once('disconnect', function() { + child.once('disconnect', () => { console.log('child#%d disconnected', child.process.pid); sendmessage(child, { from: 'master', @@ -135,12 +136,11 @@ describe('sendmessage.test.js', function() { }); }); - describe('worker_threads', function() { - it('should send cross process message', function(done) { - const childfile = path.join(__dirname, 'child.js'); - const worker = new workerThreads.Worker(childfile); + describe('worker_threads', () => { + it('should send cross process message', done => { + const worker = new workerThreads.Worker(childFile); worker.once('message', function(message) { - message.should.eql({ + assert.deepEqual(message, { from: 'child', hi: 'this is a message send to master', }); @@ -151,7 +151,7 @@ describe('sendmessage.test.js', function() { }); worker.once('message', function(message) { - message.should.eql({ + assert.deepEqual(message, { from: 'child', got: { from: 'master', @@ -165,12 +165,11 @@ describe('sendmessage.test.js', function() { }); describe('SENDMESSAGE_ONE_PROCESS', () => { - it('should emit when SENDMESSAGE_ONE_PROCESS = true', function(done) { - const childfile = path.join(__dirname, 'child.js'); - const child = childprocess.fork(childfile); + it('should emit when SENDMESSAGE_ONE_PROCESS = true', done => { + const child = childprocess.fork(childFile); mm(process.env, 'SENDMESSAGE_ONE_PROCESS', 'true'); child.once('message', function(message) { - message.should.eql({ + assert.deepEqual(message, { from: 'child', hi: 'this is a message send to master', }); @@ -181,7 +180,7 @@ describe('sendmessage.test.js', function() { }); child.once('message', function(msg) { - msg.should.eql({ + assert.deepEqual(msg, { from: 'master', reply: 'this is a reply message send to child', }); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ff41b73 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@eggjs/tsconfig", + "compilerOptions": { + "strict": true, + "noImplicitAny": true, + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext" + } +}