diff --git a/src/index.ts b/src/index.ts index ac1003d..4c14a1a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import * as clipboardItem from './clipboarditem.js' import * as elementCheckVisibility from './element-checkvisibility.js' import * as navigatorClipboard from './navigator-clipboard.js' +import * as withResolvers from './promise-withResolvers.js' import * as requestIdleCallback from './requestidlecallback.js' let supportsModalPseudo = false @@ -47,6 +48,7 @@ export const polyfills = { elementCheckVisibility, navigatorClipboard, requestIdleCallback, + withResolvers, } export function isSupported() { diff --git a/src/promise-withResolvers.ts b/src/promise-withResolvers.ts new file mode 100644 index 0000000..04be228 --- /dev/null +++ b/src/promise-withResolvers.ts @@ -0,0 +1,30 @@ +/*#__PURE__*/ +export function withResolvers(this: PromiseConstructor) { + const out = {} as { + promise: Promise + resolve: (value: T | PromiseLike) => void + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reject: (reason?: any) => void + } + out.promise = new Promise((resolve, reject) => { + out.resolve = resolve + out.reject = reject + }) + return out +} + +/*#__PURE__*/ +export function isSupported(): boolean { + return 'withResolvers' in Promise && typeof Promise.withResolvers === 'function' +} + +/*#__PURE__*/ +export function isPolyfilled(): boolean { + return 'withResolvers' in Promise && Promise.withResolvers === withResolvers +} + +export function apply(): void { + if (!isSupported()) { + Object.assign(Promise, {withResolvers}) + } +} diff --git a/test/navigator-clipboard.js b/test/navigator-clipboard.js index e20754e..82641c1 100644 --- a/test/navigator-clipboard.js +++ b/test/navigator-clipboard.js @@ -16,7 +16,7 @@ describe('navigator clipboard', () => { expect(arr).to.have.lengthOf(1) expect(arr[0]).to.be.an.instanceof(globalThis.ClipboardItem) expect(arr[0].types).to.eql(['text/plain']) - expect(await arr[0].getType('text/plain')).to.eql('foo') + expect(await (await arr[0].getType('text/plain')).text()).to.eql('foo') }) }) diff --git a/test/promise-withResolvers.js b/test/promise-withResolvers.js new file mode 100644 index 0000000..a817cf9 --- /dev/null +++ b/test/promise-withResolvers.js @@ -0,0 +1,40 @@ +import {apply, isPolyfilled, isSupported, withResolvers} from '../lib/promise-withResolvers.js' + +describe('withResolvers', () => { + it('has standard isSupported, isPolyfilled, apply API', () => { + expect(isSupported).to.be.a('function') + expect(isPolyfilled).to.be.a('function') + expect(apply).to.be.a('function') + expect(isSupported()).to.be.a('boolean') + expect(isPolyfilled()).to.equal(false) + }) + + it('resolves to first resolving value', async () => { + const arg = withResolvers() + expect(Object.keys(arg).sort()).to.eql(['promise', 'reject', 'resolve']) + expect(arg).to.have.property('promise').to.be.a('promise') + expect(arg).to.have.property('resolve').to.be.a('function') + expect(arg).to.have.property('reject').to.be.a('function') + + arg.resolve(1) + expect(await arg.promise).to.be.eql(1) + }) + + it('rejects to first rejecting reason', async () => { + const arg = withResolvers() + expect(Object.keys(arg).sort()).to.eql(['promise', 'reject', 'resolve']) + expect(arg).to.have.property('promise').to.be.a('promise') + expect(arg).to.have.property('resolve').to.be.a('function') + expect(arg).to.have.property('reject').to.be.a('function') + + const err = new Error('rejected') + + try { + arg.reject(err) + await arg.promise + expect.fail('should fail') + } catch (e) { + expect(e).to.be.eql(err) + } + }) +}) diff --git a/test/requestidlecallback.js b/test/requestidlecallback.js index dd78f8c..07769a4 100644 --- a/test/requestidlecallback.js +++ b/test/requestidlecallback.js @@ -14,6 +14,6 @@ describe('requestIdleCallback', () => { expect(Object.keys(arg)).to.eql(['didTimeout', 'timeRemaining']) expect(arg).to.have.property('didTimeout').to.be.a('boolean') expect(arg).to.have.property('timeRemaining').to.be.a('function') - expect(arg.timeRemaining()).to.be.a('number').lessThan(50).greaterThanOrEqual(0) + expect(arg.timeRemaining()).to.be.a('number').lessThanOrEqual(50).greaterThanOrEqual(0) }) })