From bb049ea9a90550906ad046663aad401af694df26 Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Sun, 15 Dec 2024 20:02:35 +0800 Subject: [PATCH] feat: merge Ready and EventEmitter (#5) ## Summary by CodeRabbit - **New Features** - Added support for Node.js version 22 in CI workflows. - Introduced a new `ReadyEventEmitter` class for event-driven readiness handling. - Added `ReadyEventClass` to manage 'ready-event' emissions. - Enhanced documentation with a new badge for Node.js version and a section on `ReadyEventEmitter`. - **Bug Fixes** - Improved promise handling and method documentation in the `Ready` class. - **Chores** - Updated dependency versions in `package.json` and removed unnecessary dependencies. - Removed explicit test check during the release job in CI workflows. --- .github/workflows/nodejs.yml | 5 ++-- .github/workflows/release.yml | 2 -- README.md | 20 +++++++++----- package.json | 8 +++--- src/index.ts | 29 +++++++++++++++++---- test/index.test.ts | 49 ++++++++++++++++++++++++++++++----- 6 files changed, 86 insertions(+), 27 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 0298d9f..619ea9e 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 ] @@ -12,5 +11,7 @@ jobs: name: Node.js uses: node-modules/github-actions/.github/workflows/node-test.yml@master with: - version: '16.17.0, 16, 18, 20' + version: '16.17.0, 16, 18, 20, 22' os: 'ubuntu-latest' + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a4e1158..1c6cbb1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,5 +11,3 @@ jobs: secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} GIT_TOKEN: ${{ secrets.GIT_TOKEN }} - with: - checkTest: false diff --git a/README.md b/README.md index 7b47975..e681e00 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![CI](https://github.com/node-modules/get-ready/actions/workflows/nodejs.yml/badge.svg)](https://github.com/node-modules/get-ready/actions/workflows/nodejs.yml) [![Test coverage][codecov-image]][codecov-url] [![npm download][download-image]][download-url] +[![Node.js Version](https://img.shields.io/node/v/get-ready.svg?style=flat)](https://nodejs.org/en/download/) [npm-image]: https://img.shields.io/npm/v/get-ready.svg?style=flat-square [npm-url]: https://npmjs.org/package/get-ready @@ -51,6 +52,16 @@ obj.ready(true); obj.ready().then(() => console.log('ready')); ``` +### ReadyEventEmitter + +```ts +import { ReadyEventEmitter } from 'get-ready'; + +class MyClass extends ReadyEventEmitter { + // your handler here +} +``` + **Warning: the callback is called after nextTick** ### Emit @@ -79,13 +90,8 @@ obj.ready(new Error('err')); [MIT](LICENSE) - - ## Contributors -|[
supershabam](https://github.com/supershabam)
|[
fengmk2](https://github.com/fengmk2)
|[
popomore](https://github.com/popomore)
|[
dead-horse](https://github.com/dead-horse)
|[
semantic-release-bot](https://github.com/semantic-release-bot)
| -| :---: | :---: | :---: | :---: | :---: | - -This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Mon Jun 05 2023 14:06:50 GMT+0800`. +[![Contributors](https://contrib.rocks/image?repo=node-modules/get-ready)](https://github.com/node-modules/get-ready/graphs/contributors) - +Made with [contributors-img](https://contrib.rocks). diff --git a/package.json b/package.json index 0619d60..8d656f3 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,7 @@ "egg-bin": "^6.4.1", "eslint": "^8.51.0", "eslint-config-egg": "^13.0.0", - "git-contributor": "^2.1.5", - "tshy": "^1.2.2", + "tshy": "3", "tshy-after": "^1.0.0", "typescript": "^5.2.2" }, @@ -23,7 +22,6 @@ "node": ">= 16.13.0" }, "scripts": { - "contributor": "git-contributor", "lint": "eslint src test --ext ts", "test": "npm run lint && egg-bin test", "ci": "egg-bin cov && npm run prepublishOnly && npm pack", @@ -65,5 +63,7 @@ } }, "type": "module", - "types": "./dist/commonjs/index.d.ts" + "types": "./dist/commonjs/index.d.ts", + "main": "./dist/commonjs/index.js", + "module": "./dist/esm/index.js" } diff --git a/src/index.ts b/src/index.ts index 7db2637..71a2291 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,7 @@ +import { EventEmitter } from 'node:events'; + export type CallbackFunction = (err?: Error) => void; -export type ReadyFunctionArg = boolean | Error | CallbackFunction | undefined; +export type ReadyFunctionArg = boolean | Error | CallbackFunction; export class Ready { #isReady: boolean; @@ -11,6 +13,8 @@ export class Ready { this.#readyCallbacks = []; } + ready(): Promise; + ready(flagOrFunction: ReadyFunctionArg): void; ready(flagOrFunction?: ReadyFunctionArg) { // register a callback if (flagOrFunction === undefined || typeof flagOrFunction === 'function') { @@ -25,7 +29,7 @@ export class Ready { * It will return promise when no argument passing. */ #register(func?: CallbackFunction) { - // support `this.ready().then(onready);` and `await this.ready()`; + // support `this.ready().then(onReady);` and `await this.ready()`; if (!func) { return new Promise((resolve, reject) => { function func(err?: Error) { @@ -51,8 +55,8 @@ export class Ready { } /** - * Call the callbacks that has been registerd, and clean the callback stack. - * If the flag is not false, it will be marked as ready. Then the callbacks will be called immediatly when register. + * Call the callbacks that has been registered, and clean the callback stack. + * If the flag is not false, it will be marked as ready. Then the callbacks will be called immediately when register. * @param {Boolean|Error} flag - Set a flag whether it had been ready. If the flag is an error, it's also ready, but the callback will be called with argument `error` */ #emit(flag: boolean | Error) { @@ -76,8 +80,23 @@ export class Ready { if (!obj) return; const ready = new Ready(); // delegate method - obj.ready = (flagOrFunction: any) => ready.ready(flagOrFunction); + obj.ready = (flagOrFunction: any) => { + return ready.ready(flagOrFunction); + }; } } export default Ready; + +export class ReadyEventEmitter extends EventEmitter { + #readyObj = new Ready(); + + ready(): Promise; + ready(flagOrFunction: ReadyFunctionArg): void; + ready(flagOrFunction?: ReadyFunctionArg) { + if (flagOrFunction === undefined) { + return this.#readyObj.ready(); + } + this.#readyObj.ready(flagOrFunction); + } +} diff --git a/test/index.test.ts b/test/index.test.ts index 234793e..c46fe80 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,5 +1,5 @@ import { strict as assert } from 'node:assert'; -import Ready, { ReadyFunctionArg, Ready as ReadyBase } from '../src/index.js'; +import Ready, { ReadyFunctionArg, Ready as ReadyBase, ReadyEventEmitter } from '../src/index.js'; class SomeClass { property: string; @@ -10,8 +10,13 @@ class SomeClass { this.#readyObject = new Ready(); } + ready(): Promise; + ready(arg: ReadyFunctionArg): void; ready(arg?: ReadyFunctionArg) { - return this.#readyObject.ready(arg); + if (arg === undefined) { + return this.#readyObject.ready(); + } + this.#readyObject.ready(arg); } method() { @@ -32,6 +37,22 @@ class ReadySubClass extends ReadyBase { } } +class ReadyEventClass extends ReadyEventEmitter { + property: string; + + constructor() { + super(); + this.property = 'value'; + this.ready(() => { + this.emit('ready-event'); + }); + } + + method() { + return 'method'; + } +} + describe('Ready.mixin', () => { it('should exports mixin', () => { assert(Ready.mixin); @@ -88,7 +109,7 @@ describe('new Ready()', () => { assert.deepEqual(arr, [ 1, 2 ]); }); - it('should immediatly call callback when already ready', done => { + it('should immediately call callback when already ready', done => { const someClass = new SomeClass(); someClass.ready(true); someClass.ready(done); @@ -113,11 +134,25 @@ describe('new Ready()', () => { }); }); +describe('new ReadyEventClass()', () => { + it('should have Ready properties', async () => { + const someClass = new ReadyEventClass(); + assert('ready' in someClass); + let gotReadyEvent = false; + someClass.on('ready-event', () => { + gotReadyEvent = true; + }); + someClass.ready(true); + await someClass.ready(); + assert.equal(gotReadyEvent, true); + }); +}); + describe('promise', () => { it('should resolve after ready', done => { const someClass = new SomeClass(); - someClass.ready()!.then(() => { - someClass.ready()!.then(done); + someClass.ready().then(() => { + someClass.ready().then(done); }); someClass.ready(true); }); @@ -144,7 +179,7 @@ describe('error', () => { it('should get error in promise', done => { const someClass = new SomeClass(); - someClass.ready()!.catch(err => { + someClass.ready().catch(err => { assert(err); assert(err.message === 'error'); done(); @@ -165,7 +200,7 @@ describe('error', () => { it('should get error after ready in promise', done => { const someClass = new SomeClass(); someClass.ready(new Error('error')); - someClass.ready()!.catch(err => { + someClass.ready().catch(err => { assert(err); assert(err.message === 'error'); done();