diff --git a/CHANGELOG.md b/CHANGELOG.md index e6d6519..ff2c2f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ > - 🏠 Internal > - 💅 Polish +## Unreleased + +* 🐛 Fix `ReadableStream` to match TypeScript's `AsyncIterable` type. ([#141](https://github.com/MattiasBuelens/web-streams-polyfill/issues/141), [#142](https://github.com/MattiasBuelens/web-streams-polyfill/pull/142)) + ## 3.3.2 (2024-01-04) * 🐛 Fix bad publish to npm. diff --git a/dist/types/ts3.6/polyfill.d.ts b/dist/types/ts3.6/polyfill.d.ts index 93ea710..1e24288 100644 --- a/dist/types/ts3.6/polyfill.d.ts +++ b/dist/types/ts3.6/polyfill.d.ts @@ -6,7 +6,7 @@ import type { ReadableStreamAsyncIterator, ReadableStreamIteratorOptions } from export * from './ponyfill'; declare global { - interface ReadableStream { + interface ReadableStream extends AsyncIterable { /** * Asynchronously iterates over the chunks in the stream's internal queue. * @@ -23,7 +23,6 @@ declare global { /** * {@inheritDoc ReadableStream.values} */ - [Symbol.asyncIterator]: (options?: ReadableStreamIteratorOptions) => ReadableStreamAsyncIterator; + [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; } } - diff --git a/etc/web-streams-polyfill.api.md b/etc/web-streams-polyfill.api.md index e269ec8..44563e1 100644 --- a/etc/web-streams-polyfill.api.md +++ b/etc/web-streams-polyfill.api.md @@ -52,8 +52,8 @@ export class ReadableByteStreamController { } // @public -export class ReadableStream { - [Symbol.asyncIterator]: (options?: ReadableStreamIteratorOptions) => ReadableStreamAsyncIterator; +export class ReadableStream implements AsyncIterable { + [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; constructor(underlyingSource: UnderlyingByteSource, strategy?: { highWaterMark?: number; size?: undefined; @@ -76,7 +76,7 @@ export class ReadableStream { } // @public -export interface ReadableStreamAsyncIterator extends AsyncIterator { +export interface ReadableStreamAsyncIterator extends AsyncIterableIterator { // (undocumented) next(): Promise>; // (undocumented) diff --git a/rollup.config.mjs b/rollup.config.mjs index 51139c2..4293de9 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -69,13 +69,13 @@ function bundle(entry, { esm = false, minify = false, target = 'es5' } = {}) { declaration: false, declarationMap: false }), - inject({ + target === 'es5' ? inject({ include: 'src/**/*.ts', exclude: 'src/stub/symbol.ts', modules: { Symbol: path.resolve(dirname, './src/stub/symbol.ts') } - }), + }) : undefined, replace({ include: 'src/**/*.ts', preventAssignment: true, diff --git a/src/lib/abstract-ops/ecmascript.ts b/src/lib/abstract-ops/ecmascript.ts index 546fc19..6a07db4 100644 --- a/src/lib/abstract-ops/ecmascript.ts +++ b/src/lib/abstract-ops/ecmascript.ts @@ -110,6 +110,12 @@ export function CreateAsyncFromSyncIterator(syncIteratorRecord: SyncIteratorR return { iterator: asyncIterator, nextMethod, done: false }; } +// Aligns with core-js/modules/es.symbol.async-iterator.js +export const SymbolAsyncIterator: (typeof Symbol)['asyncIterator'] = + Symbol.asyncIterator ?? + Symbol.for?.('Symbol.asyncIterator') ?? + '@@asyncIterator'; + export type SyncOrAsyncIterable = Iterable | AsyncIterable; export type SyncOrAsyncIteratorMethod = () => (Iterator | AsyncIterator); @@ -131,7 +137,7 @@ function GetIterator( assert(hint === 'sync' || hint === 'async'); if (method === undefined) { if (hint === 'async') { - method = GetMethod(obj as AsyncIterable, Symbol.asyncIterator); + method = GetMethod(obj as AsyncIterable, SymbolAsyncIterator); if (method === undefined) { const syncMethod = GetMethod(obj as Iterable, Symbol.iterator); const syncIteratorRecord = GetIterator(obj as Iterable, 'sync', syncMethod); diff --git a/src/lib/readable-stream.ts b/src/lib/readable-stream.ts index 74446a4..dbfbb58 100644 --- a/src/lib/readable-stream.ts +++ b/src/lib/readable-stream.ts @@ -49,7 +49,7 @@ import type { } from './readable-stream/underlying-source'; import { noop } from '../utils'; import { setFunctionName, typeIsObject } from './helpers/miscellaneous'; -import { CreateArrayFromList } from './abstract-ops/ecmascript'; +import { CreateArrayFromList, SymbolAsyncIterator } from './abstract-ops/ecmascript'; import { CancelSteps } from './abstract-ops/internal-methods'; import { IsNonNegativeNumber } from './abstract-ops/miscellaneous'; import { assertObject, assertRequiredArgument } from './validators/basic'; @@ -85,7 +85,7 @@ type ReadableStreamState = 'readable' | 'closed' | 'errored'; * * @public */ -export class ReadableStream { +export class ReadableStream implements AsyncIterable { /** @internal */ _state!: ReadableStreamState; /** @internal */ @@ -329,7 +329,12 @@ export class ReadableStream { /** * {@inheritDoc ReadableStream.values} */ - [Symbol.asyncIterator]!: (options?: ReadableStreamIteratorOptions) => ReadableStreamAsyncIterator; + [Symbol.asyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator; + + [SymbolAsyncIterator](options?: ReadableStreamIteratorOptions): ReadableStreamAsyncIterator { + // Stub implementation, overridden below + return this.values(options); + } /** * Creates a new ReadableStream wrapping the provided iterable or async iterable. @@ -367,13 +372,11 @@ if (typeof Symbol.toStringTag === 'symbol') { configurable: true }); } -if (typeof Symbol.asyncIterator === 'symbol') { - Object.defineProperty(ReadableStream.prototype, Symbol.asyncIterator, { - value: ReadableStream.prototype.values, - writable: true, - configurable: true - }); -} +Object.defineProperty(ReadableStream.prototype, SymbolAsyncIterator, { + value: ReadableStream.prototype.values, + writable: true, + configurable: true +}); export type { ReadableStreamAsyncIterator, diff --git a/src/lib/readable-stream/async-iterator.ts b/src/lib/readable-stream/async-iterator.ts index e504ded..b7abf8d 100644 --- a/src/lib/readable-stream/async-iterator.ts +++ b/src/lib/readable-stream/async-iterator.ts @@ -25,7 +25,7 @@ import { * * @public */ -export interface ReadableStreamAsyncIterator extends AsyncIterator { +export interface ReadableStreamAsyncIterator extends AsyncIterableIterator { next(): Promise>; return(value?: any): Promise>; @@ -140,9 +140,7 @@ const ReadableStreamAsyncIteratorPrototype: ReadableStreamAsyncIteratorInstance< return this._asyncIteratorImpl.return(value); } } as any; -if (AsyncIteratorPrototype !== undefined) { - Object.setPrototypeOf(ReadableStreamAsyncIteratorPrototype, AsyncIteratorPrototype); -} +Object.setPrototypeOf(ReadableStreamAsyncIteratorPrototype, AsyncIteratorPrototype); // Abstract operations for the ReadableStream. diff --git a/src/target/es2018/stub/async-iterator-prototype.ts b/src/target/es2018/stub/async-iterator-prototype.ts index c8f11d8..b25f071 100644 --- a/src/target/es2018/stub/async-iterator-prototype.ts +++ b/src/target/es2018/stub/async-iterator-prototype.ts @@ -1,5 +1,5 @@ /// /* eslint-disable @typescript-eslint/no-empty-function */ -export const AsyncIteratorPrototype: AsyncIterable | undefined = +export const AsyncIteratorPrototype: AsyncIterable = Object.getPrototypeOf(Object.getPrototypeOf(async function* (): AsyncIterableIterator {}).prototype); diff --git a/src/target/es5/stub/async-iterator-prototype.ts b/src/target/es5/stub/async-iterator-prototype.ts index 4839a2b..6455bd3 100644 --- a/src/target/es5/stub/async-iterator-prototype.ts +++ b/src/target/es5/stub/async-iterator-prototype.ts @@ -1,16 +1,13 @@ /// -export let AsyncIteratorPrototype: AsyncIterable | undefined; +import { SymbolAsyncIterator } from '../../../lib/abstract-ops/ecmascript'; -if (typeof Symbol.asyncIterator === 'symbol') { - // We're running inside a ES2018+ environment, but we're compiling to an older syntax. - // We cannot access %AsyncIteratorPrototype% without non-ES2018 syntax, but we can re-create it. - AsyncIteratorPrototype = { - // 25.1.3.1 %AsyncIteratorPrototype% [ @@asyncIterator ] ( ) - // https://tc39.github.io/ecma262/#sec-asynciteratorprototype-asynciterator - [Symbol.asyncIterator](this: AsyncIterator) { - return this; - } - }; - Object.defineProperty(AsyncIteratorPrototype, Symbol.asyncIterator, { enumerable: false }); -} +// We cannot access %AsyncIteratorPrototype% without non-ES2018 syntax, but we can re-create it. +export const AsyncIteratorPrototype: AsyncIterable = { + // 25.1.3.1 %AsyncIteratorPrototype% [ @@asyncIterator ] ( ) + // https://tc39.github.io/ecma262/#sec-asynciteratorprototype-asynciterator + [SymbolAsyncIterator](this: AsyncIterator) { + return this; + } +}; +Object.defineProperty(AsyncIteratorPrototype, SymbolAsyncIterator, { enumerable: false });