Skip to content

Commit

Permalink
Merge pull request #3451 from jasonaden/v6_forward_compat
Browse files Browse the repository at this point in the history
V6 forward compatibility
  • Loading branch information
benlesh committed Mar 21, 2018
2 parents 4cbd91c + de37095 commit 4f5369e
Show file tree
Hide file tree
Showing 17 changed files with 361 additions and 18 deletions.
6 changes: 4 additions & 2 deletions .make-packages.js
Expand Up @@ -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
Expand Down
122 changes: 122 additions & 0 deletions 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]);
});
});
});
9 changes: 6 additions & 3 deletions 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.
Expand Down Expand Up @@ -84,11 +87,11 @@ export class Notification<T> {
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<T>();
return empty<T>();
}
throw new Error('unexpected notification kind value');
}
Expand Down
20 changes: 10 additions & 10 deletions src/Observable.ts
Expand Up @@ -289,16 +289,16 @@ export class Observable<T> implements Subscribable<T> {
}

/* tslint:disable:max-line-length */
pipe(): Observable<T>
pipe<A>(op1: OperatorFunction<T, A>): Observable<A>
pipe<A, B>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>): Observable<B>
pipe<A, B, C>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>): Observable<C>
pipe<A, B, C, D>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>): Observable<D>
pipe<A, B, C, D, E>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>): Observable<E>
pipe<A, B, C, D, E, F>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>): Observable<F>
pipe<A, B, C, D, E, F, G>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>): Observable<G>
pipe<A, B, C, D, E, F, G, H>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>, op8: OperatorFunction<G, H>): Observable<H>
pipe<A, B, C, D, E, F, G, H, I>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>, op8: OperatorFunction<G, H>, op9: OperatorFunction<H, I>): Observable<I>
pipe(): Observable<T>;
pipe<A>(op1: OperatorFunction<T, A>): Observable<A>;
pipe<A, B>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>): Observable<B>;
pipe<A, B, C>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>): Observable<C>;
pipe<A, B, C, D>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>): Observable<D>;
pipe<A, B, C, D, E>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>): Observable<E>;
pipe<A, B, C, D, E, F>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>): Observable<F>;
pipe<A, B, C, D, E, F, G>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>): Observable<G>;
pipe<A, B, C, D, E, F, G, H>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>, op8: OperatorFunction<G, H>): Observable<H>;
pipe<A, B, C, D, E, F, G, H, I>(op1: OperatorFunction<T, A>, op2: OperatorFunction<A, B>, op3: OperatorFunction<B, C>, op4: OperatorFunction<C, D>, op5: OperatorFunction<D, E>, op6: OperatorFunction<E, F>, op7: OperatorFunction<F, G>, op8: OperatorFunction<G, H>, op9: OperatorFunction<H, I>): Observable<I>;
/* tslint:enable:max-line-length */

/**
Expand Down
8 changes: 8 additions & 0 deletions src/Rx.ts
@@ -1,3 +1,4 @@

/* tslint:disable:no-unused-variable */
// Subject imported before Observable to bypass circular dependency issue since
// Subject extends Observable and Observable references Subject in it's
Expand Down Expand Up @@ -127,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';
Expand Down Expand Up @@ -176,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 */

Expand Down
2 changes: 1 addition & 1 deletion src/Subscription.ts
Expand Up @@ -29,7 +29,7 @@ export interface ISubscription extends AnonymousSubscription {
* @class Subscription
*/
export class Subscription implements ISubscription {
public static EMPTY: Subscription = (function(empty: any){
public static EMPTY: Subscription = (function(empty: any) {
empty.closed = true;
return empty;
}(new Subscription()));
Expand Down
11 changes: 11 additions & 0 deletions 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<T> {
throwIfEmpty: typeof throwIfEmpty;
}
}
2 changes: 2 additions & 0 deletions src/ajax.ts
@@ -0,0 +1,2 @@
export { ajax } from './observable/dom/ajax';
export { AjaxRequest, AjaxResponse, AjaxError, AjaxTimeoutError } from './observable/dom/AjaxObservable';
72 changes: 72 additions & 0 deletions src/index.ts
@@ -0,0 +1,72 @@
/* Observable */
export { Observable } from './Observable';
export { ConnectableObservable } from './observable/ConnectableObservable';
export { Operator } from './Operator';
export { observable } from './symbol/observable';

/* Subjects */
export { Subject } from './Subject';
export { BehaviorSubject } from './BehaviorSubject';
export { ReplaySubject } from './ReplaySubject';
export { AsyncSubject } from './AsyncSubject';

/* Schedulers */
export { asap as asapScheduler } from './scheduler/asap';
export { async as asyncScheduler } from './scheduler/async';
export { queue as queueScheduler } from './scheduler/queue';
export { animationFrame as animationFrameScheduler } from './scheduler/animationFrame';
export { VirtualTimeScheduler, VirtualAction } from './scheduler/VirtualTimeScheduler';

/* Subscription */
export { Subscription } from './Subscription';
export { Subscriber } from './Subscriber';

/* Notification */
export { Notification } from './Notification';

/* Utils */
export { pipe } from './util/pipe';
export { noop } from './util/noop';
export { identity } from './util/identity';

/* Error types */
export { ArgumentOutOfRangeError } from './util/ArgumentOutOfRangeError';
export { EmptyError } from './util/EmptyError';
export { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';
export { UnsubscriptionError } from './util/UnsubscriptionError';
export { TimeoutError } from './util/TimeoutError';

/* Static observable creation exports */
export { bindCallback } from './observable/bindCallback';
export { bindNodeCallback } from './observable/bindNodeCallback';
export { combineLatest } from './observable/combineLatest';
export { concat } from './observable/concat';
export { defer } from './observable/defer';
export { empty } from './observable/empty';
export { forkJoin } from './observable/forkJoin';
export { from } from './observable/from';
export { fromEvent } from './observable/fromEvent';
export { fromEventPattern } from './observable/fromEventPattern';
export { generate } from './observable/generate';
export { _if as iif } from './observable/if';
export { interval } from './observable/interval';
export { merge } from './observable/merge';
export { of } from './observable/of';
export { onErrorResumeNext } from './observable/onErrorResumeNext';
export { pairs } from './observable/pairs';
export { race } from './observable/race';
export { range } from './observable/range';
export { _throw as throwError } from './observable/throw';
export { timer } from './observable/timer';
export { using } from './observable/using';
export { zip } from './observable/zip';

/* Constants */
export { EMPTY } from './observable/empty';
export { NEVER } from './observable/never';

/* Types */
export * from './types';

/* Config */
// export { config } from './config';
10 changes: 9 additions & 1 deletion src/observable/empty.ts
@@ -1,3 +1,11 @@
import { EmptyObservable } from './EmptyObservable';
import { Observable } from '../Observable';
import { EmptyObservable } from './EmptyObservable';

/**
* Observable instance always typed to `never` to be forward compatible with RxJS v6. Simlar
* to calling {@link empty} without a {@link Scheduler}. It is preferrable to use this over
* `empty()`.
*/
export const EMPTY = new Observable<never>();

export const empty = EmptyObservable.create;
30 changes: 29 additions & 1 deletion src/observable/never.ts
@@ -1,3 +1,31 @@
import { NeverObservable } from './NeverObservable';
import { Observable } from '../Observable';
import { NeverObservable } from './NeverObservable';
import { noop } from '../util/noop';

/**
* An Observable that emits no items to the Observer and never completes. Compatible with
* RxJS v6 API.
*
* <img src="./img/never.png" width="100%">
*
* A simple Observable that emits neither values nor errors nor the completion
* notification. It can be used for testing purposes or for composing with other
* Observables. Please note that by never emitting a complete notification, this
* Observable keeps the subscription from being disposed automatically.
* Subscriptions need to be manually disposed.
*
* @example <caption>Emit the number 7, then never emit anything else (not even complete).</caption>
* function info() {
* console.log('Will not be called');
* }
* var result = NEVER.startWith(7);
* result.subscribe(x => console.log(x), info, info);
*
* @see {@link create}
* @see {@link EMPTY}
* @see {@link of}
* @see {@link throwError}
*/
export const NEVER = new Observable<never>(noop);

export const never = NeverObservable.create;
7 changes: 7 additions & 0 deletions src/operator/throwIfEmpty.ts
@@ -0,0 +1,7 @@
import { Observable } from '../Observable';
import { throwIfEmpty as higherOrder, defaultErrorFactory } from '../operators/throwIfEmpty';

export function throwIfEmpty<T>(this: Observable<T>,
errorFactory: (() => any) = defaultErrorFactory): Observable<T> {
return higherOrder(errorFactory)(this) as Observable<T>;
}
1 change: 1 addition & 0 deletions src/operators.ts
Expand Up @@ -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';
Expand Down

0 comments on commit 4f5369e

Please sign in to comment.