From de37095a7a7786e575654f62f7bb7f0afabf2956 Mon Sep 17 00:00:00 2001 From: Jason Aden Date: Mon, 19 Mar 2018 16:37:43 -0700 Subject: [PATCH] feat(compat): add next rxjs version's exports for forward compatibility * Also adds throwIfEmpty operator --- .make-packages.js | 6 +- spec/operators/throwIfEmpty-spec.ts | 122 ++++++++++++++++++++++++++++ src/Notification.ts | 9 +- src/Rx.ts | 7 ++ src/add/operator/throwIfEmpty.ts | 11 +++ src/ajax.ts | 2 + src/operator/throwIfEmpty.ts | 7 ++ src/operators.ts | 1 + src/operators/throwIfEmpty.ts | 43 ++++++++++ src/testing.ts | 1 + src/websocket.ts | 1 + 11 files changed, 205 insertions(+), 5 deletions(-) create mode 100644 spec/operators/throwIfEmpty-spec.ts create mode 100644 src/add/operator/throwIfEmpty.ts create mode 100644 src/ajax.ts create mode 100644 src/operator/throwIfEmpty.ts create mode 100644 src/operators/throwIfEmpty.ts create mode 100644 src/testing.ts create mode 100644 src/websocket.ts diff --git a/.make-packages.js b/.make-packages.js index 888ac9ac09..5f7e16608b 100644 --- a/.make-packages.js +++ b/.make-packages.js @@ -39,8 +39,10 @@ fs.removeSync(PKG_ROOT); let rootPackageJson = Object.assign({}, pkg, { name: 'rxjs', - main: './Rx.js', - typings: './Rx.d.ts' + main: './index.js', + module: './_esm5/index.js', + es2015: './_esm2015/index.js', + typings: './index.d.ts' }); // Get a list of the file names. Sort in reverse order so re-export files diff --git a/spec/operators/throwIfEmpty-spec.ts b/spec/operators/throwIfEmpty-spec.ts new file mode 100644 index 0000000000..3e472029ec --- /dev/null +++ b/spec/operators/throwIfEmpty-spec.ts @@ -0,0 +1,122 @@ +import { expect } from 'chai'; +import { cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; +import * as Rx from '../../dist/package/Rx'; + +/** @test {timeout} */ +describe('throwIfEmpty', () => { + describe('with errorFactory', () => { + it('should throw if empty', () => { + const error = new Error('So empty inside'); + let thrown: any; + + Rx.Observable.empty().throwIfEmpty(() => error) + .subscribe({ + error(err) { + thrown = err; + } + }); + + expect(thrown).to.equal(error); + }); + + it('should NOT throw if NOT empty', () => { + const error = new Error('So empty inside'); + let thrown: any; + + Rx.Observable.of('test').throwIfEmpty(() => error) + .subscribe({ + error(err) { + thrown = err; + } + }); + + // tslint:disable-next-line:no-unused-expression + expect(thrown).to.be.undefined; + }); + + it('should pass values through', () => { + const source = cold('----a---b---c---|'); + const sub1 = '^ !'; + const expected = '----a---b---c---|'; + expectObservable( + source.throwIfEmpty(() => new Error('test')) + ).toBe(expected); + expectSubscriptions(source.subscriptions).toBe([sub1]); + }); + + it('should never when never', () => { + const source = cold('-'); + const sub1 = '^'; + const expected = '-'; + expectObservable( + source.throwIfEmpty(() => new Error('test')) + ).toBe(expected); + expectSubscriptions(source.subscriptions).toBe([sub1]); + }); + + it('should error when empty', () => { + const source = cold('----|'); + const sub1 = '^ !'; + const expected = '----#'; + expectObservable( + source.throwIfEmpty(() => new Error('test')) + ).toBe(expected, undefined, new Error('test')); + expectSubscriptions(source.subscriptions).toBe([sub1]); + }); + }); + + describe('without errorFactory', () => { + it('should throw EmptyError if empty', () => { + let thrown: any; + + Rx.Observable.empty().throwIfEmpty() + .subscribe({ + error(err) { + thrown = err; + } + }); + + expect(thrown).to.be.instanceof(Rx.EmptyError); + }); + + it('should NOT throw if NOT empty', () => { + let thrown: any; + + Rx.Observable.of('test').throwIfEmpty() + .subscribe({ + error(err) { + thrown = err; + } + }); + + // tslint:disable-next-line:no-unused-expression + expect(thrown).to.be.undefined; + }); + + it('should pass values through', () => { + const source = cold('----a---b---c---|'); + const sub1 = '^ !'; + const expected = '----a---b---c---|'; + expectObservable(source.throwIfEmpty()).toBe(expected); + expectSubscriptions(source.subscriptions).toBe([sub1]); + }); + + it('should never when never', () => { + const source = cold('-'); + const sub1 = '^'; + const expected = '-'; + expectObservable(source.throwIfEmpty()).toBe(expected); + expectSubscriptions(source.subscriptions).toBe([sub1]); + }); + + it('should error when empty', () => { + const source = cold('----|'); + const sub1 = '^ !'; + const expected = '----#'; + expectObservable( + source.throwIfEmpty() + ).toBe(expected, undefined, new Rx.EmptyError()); + expectSubscriptions(source.subscriptions).toBe([sub1]); + }); + }); +}); diff --git a/src/Notification.ts b/src/Notification.ts index 88749c9c0a..d288079ba6 100644 --- a/src/Notification.ts +++ b/src/Notification.ts @@ -1,5 +1,8 @@ import { PartialObserver } from './Observer'; import { Observable } from './Observable'; +import { of } from './observable/of'; +import { empty } from './observable/empty'; +import { _throw } from './observable/throw'; /** * Represents a push-based event or value that an {@link Observable} can emit. @@ -84,11 +87,11 @@ export class Notification { const kind = this.kind; switch (kind) { case 'N': - return Observable.of(this.value); + return of(this.value); case 'E': - return Observable.throw(this.error); + return _throw(this.error); case 'C': - return Observable.empty(); + return empty(); } throw new Error('unexpected notification kind value'); } diff --git a/src/Rx.ts b/src/Rx.ts index 49b2ed7af3..5338b8fced 100644 --- a/src/Rx.ts +++ b/src/Rx.ts @@ -128,6 +128,7 @@ import './add/operator/takeUntil'; import './add/operator/takeWhile'; import './add/operator/throttle'; import './add/operator/throttleTime'; +import './add/operator/throwIfEmpty'; import './add/operator/timeInterval'; import './add/operator/timeout'; import './add/operator/timeoutWith'; @@ -177,9 +178,15 @@ import { rxSubscriber } from './symbol/rxSubscriber'; import { iterator } from './symbol/iterator'; import { observable } from './symbol/observable'; +import * as _ajax from './ajax'; import * as _operators from './operators'; +import * as _testing from './testing'; +import * as _websocket from './websocket'; +export const ajax = _ajax; export const operators = _operators; +export const testing = _testing; +export const websocket = _websocket; /* tslint:enable:no-unused-variable */ diff --git a/src/add/operator/throwIfEmpty.ts b/src/add/operator/throwIfEmpty.ts new file mode 100644 index 0000000000..74d1e264c4 --- /dev/null +++ b/src/add/operator/throwIfEmpty.ts @@ -0,0 +1,11 @@ + +import { Observable } from '../../Observable'; +import { throwIfEmpty } from '../../operator/throwIfEmpty'; + +Observable.prototype.throwIfEmpty = throwIfEmpty; + +declare module '../../Observable' { + interface Observable { + throwIfEmpty: typeof throwIfEmpty; + } +} \ No newline at end of file diff --git a/src/ajax.ts b/src/ajax.ts new file mode 100644 index 0000000000..ea42c3f093 --- /dev/null +++ b/src/ajax.ts @@ -0,0 +1,2 @@ +export { ajax } from './observable/dom/ajax'; +export { AjaxRequest, AjaxResponse, AjaxError, AjaxTimeoutError } from './observable/dom/AjaxObservable'; diff --git a/src/operator/throwIfEmpty.ts b/src/operator/throwIfEmpty.ts new file mode 100644 index 0000000000..0c7353b453 --- /dev/null +++ b/src/operator/throwIfEmpty.ts @@ -0,0 +1,7 @@ +import { Observable } from '../Observable'; +import { throwIfEmpty as higherOrder, defaultErrorFactory } from '../operators/throwIfEmpty'; + +export function throwIfEmpty(this: Observable, + errorFactory: (() => any) = defaultErrorFactory): Observable { + return higherOrder(errorFactory)(this) as Observable; +} diff --git a/src/operators.ts b/src/operators.ts index 798e525000..d0c5af9491 100644 --- a/src/operators.ts +++ b/src/operators.ts @@ -93,6 +93,7 @@ export { takeWhile } from './operators/takeWhile'; export { tap } from './operators/tap'; export { throttle } from './operators/throttle'; export { throttleTime } from './operators/throttleTime'; +export { throwIfEmpty } from './operators/throwIfEmpty'; export { timeInterval } from './operators/timeInterval'; export { timeout } from './operators/timeout'; export { timeoutWith } from './operators/timeoutWith'; diff --git a/src/operators/throwIfEmpty.ts b/src/operators/throwIfEmpty.ts new file mode 100644 index 0000000000..5c18ee7bcd --- /dev/null +++ b/src/operators/throwIfEmpty.ts @@ -0,0 +1,43 @@ +import { tap } from './tap'; +import { EmptyError } from '../util/EmptyError'; +/* tslint:disable:no-unused-variable */ +import { Observable } from '../Observable'; +/* tslint:enable:no-unused-variable */ + +/** + * If the source observable completes without emitting a value, it will emit + * an error. The error will be created at that time by the optional + * `errorFactory` argument, otherwise, the error will be {@link ErrorEmpty}. + * + * @example + * + * const click$ = fromEvent(button, 'click'); + * + * clicks$.pipe( + * takeUntil(timer(1000)), + * throwIfEmpty( + * () => new Error('the button was not clicked within 1 second') + * ), + * ) + * .subscribe({ + * next() { console.log('The button was clicked'); }, + * error(err) { console.error(err); }, + * }); + * @param {Function} [errorFactory] A factory function called to produce the + * error to be thrown when the source observable completes without emitting a + * value. + */ +export const throwIfEmpty = + (errorFactory: (() => any) = defaultErrorFactory) => tap({ + hasValue: false, + next(this: any) { this.hasValue = true; }, + complete(this: any) { + if (!this.hasValue) { + throw errorFactory(); + } + } + } as any); + +export function defaultErrorFactory() { + return new EmptyError(); +} diff --git a/src/testing.ts b/src/testing.ts new file mode 100644 index 0000000000..ef68e4bd62 --- /dev/null +++ b/src/testing.ts @@ -0,0 +1 @@ +export { TestScheduler } from './testing/TestScheduler'; diff --git a/src/websocket.ts b/src/websocket.ts new file mode 100644 index 0000000000..116a1d29eb --- /dev/null +++ b/src/websocket.ts @@ -0,0 +1 @@ +export { webSocket as websocket } from './observable/dom/webSocket';