diff --git a/package.json b/package.json index bef9d432aa..78d3342ed1 100644 --- a/package.json +++ b/package.json @@ -13,11 +13,11 @@ }, "scripts": { "build_all": "npm run build_es6 && npm run build_amd && npm run build_cjs && npm run build_global && npm run generate_packages", - "build_amd": "rm -rf dist/amd && tsc typings/es6-shim/es6-shim.d.ts src/Rx.ts -m amd --outDir dist/amd --sourcemap --target ES5 --diagnostics --pretty", - "build_cjs": "rm -rf dist/cjs && tsc typings/es6-shim/es6-shim.d.ts src/Rx.ts src/Rx.KitchenSink.ts -m commonjs --outDir dist/cjs --sourcemap --target ES5 -d --diagnostics --pretty", - "build_es6": "rm -rf dist/es6 && tsc src/Rx.ts src/Rx.KitchenSink.ts --outDir dist/es6 --sourceMap --target ES6 -d --diagnostics --pretty", + "build_amd": "npm run build_operators && rm -rf dist/amd && tsc typings/es6-shim/es6-shim.d.ts src/Rx.ts -m amd --outDir dist/amd --sourcemap --target ES5 --diagnostics --pretty", + "build_cjs": "npm run build_operators && rm -rf dist/cjs && tsc typings/es6-shim/es6-shim.d.ts src/Rx.ts src/Rx.KitchenSink.ts -m commonjs --outDir dist/cjs --sourcemap --target ES5 -d --diagnostics --pretty", + "build_es6": "npm run build_operators && rm -rf dist/es6 && tsc src/Rx.ts src/Rx.KitchenSink.ts --outDir dist/es6 --sourceMap --target ES6 -d --diagnostics --pretty", "build_closure": "java -jar ./node_modules/google-closure-compiler/compiler.jar ./dist/global/Rx.js --language_in ECMASCRIPT5 --create_source_map ./dist/global/Rx.min.js.map --js_output_file ./dist/global/Rx.min.js", - "build_global": "rm -rf dist/global && mkdir \"dist/global\" && browserify src/Rx.global.js --outfile dist/global/Rx.js && npm run build_closure", + "build_global": "npm run build_operators && rm -rf dist/global && mkdir \"dist/global\" && browserify src/Rx.global.js --outfile dist/global/Rx.js && npm run build_closure", "build_perf": "npm run build_cjs && npm run build_global && webdriver-manager update && npm run perf", "build_test": "rm -rf dist/ && npm run lint && npm run build_cjs && jasmine", "build_cover": "rm -rf dist/ && npm run lint && npm run build_cjs && npm run cover", @@ -37,7 +37,8 @@ "generate_packages": "node .make-packages.js", "generate_operator_patches": "tsc -outDir dist/ ./tools/generate-operator-patches.ts && node ./dist/generate-operator-patches.js --exec", "commit": "git-cz", - "check_circular_dependencies": "madge ./dist/cjs --circular" + "check_circular_dependencies": "madge ./dist/cjs --circular", + "build_operators": "node typingsgen.js" }, "repository": { "type": "git", diff --git a/src/CoreOperators.ts b/src/CoreOperators.ts index 60e595de1a..3b2b869b4f 100644 --- a/src/CoreOperators.ts +++ b/src/CoreOperators.ts @@ -4,6 +4,9 @@ import {ConnectableObservable} from './observable/ConnectableObservable'; import {Subject} from './Subject'; import {GroupedObservable} from './operator/groupBy-support'; import {Notification} from './Notification'; +/* tslint:disable */ +import * as operator from './operator-typings'; +/* tslint:enable */ export interface CoreOperators { buffer?: (closingNotifier: Observable) => Observable; @@ -13,7 +16,7 @@ export interface CoreOperators { bufferWhen?: (closingSelector: () => Observable) => Observable; catch?: (selector: (err: any, source: Observable, caught: Observable) => Observable) => Observable; combineAll?: (project?: (...values: Array) => R) => Observable; - combineLatest?: (...observables: Array | ((...values: Array) => R)>) => Observable; + combineLatest: operator.operator_proto_combineLatest; concat?: (...observables: (Observable | Scheduler)[]) => Observable; concatAll?: () => Observable; concatMap?: (project: ((x: T, ix: number) => Observable), projectResult?: (x: T, y: any, ix: number, iy: number) => R) => Observable; diff --git a/src/Observable.ts b/src/Observable.ts index 218cbb4338..b0f414de6b 100644 --- a/src/Observable.ts +++ b/src/Observable.ts @@ -12,6 +12,28 @@ import {Subject} from './Subject'; import {Notification} from './Notification'; import {rxSubscriber} from'./symbol/rxSubscriber'; +/* tslint:disable */ +import * as operator from './operator-typings'; +/* tslint:enable */ +import {combineLatest as combineLatestStatic} from './operator/combineLatest-static'; +import {concat as concatStatic} from './operator/concat-static'; +import {merge as mergeStatic} from './operator/merge-static'; +import {zip as zipStatic} from './operator/zip-static'; +import {DeferObservable} from './observable/defer'; +import {EmptyObservable} from './observable/empty'; +import {ForkJoinObservable} from './observable/forkJoin'; +import {FromObservable} from './observable/from'; +import {CallbackObservable} from './observable/fromCallback'; +import {ArrayObservable} from './observable/fromArray'; +import {FromEventObservable} from './observable/fromEvent'; +import {FromEventPatternObservable} from './observable/fromEventPattern'; +import {PromiseObservable} from './observable/fromPromise'; +import {IntervalObservable} from './observable/interval'; +import {TimerObservable} from './observable/timer'; +import {RangeObservable} from './observable/range'; +import {InfiniteObservable} from './observable/never'; +import {ErrorObservable} from './observable/throw'; + /** * A representation of any set of values over any amount of time. This the most basic building block * of RxJS. @@ -156,33 +178,25 @@ export class Observable implements CoreOperators { } // static method stubs - static bindCallback: (callbackFunc: Function, selector?: Function, scheduler?: Scheduler) => Function; - static combineLatest: (...observables: Array | - Array> | - ((...values: Array) => T) | - Scheduler>) => Observable; - static concat: (...observables: Array | Scheduler>) => Observable; - static defer: (observableFactory: () => Observable) => Observable; - static empty: (scheduler?: Scheduler) => Observable; - static forkJoin: (...sources: Array | - Array> | - Promise | - ((...values: Array) => any)>) => Observable; - static from: (iterable: any, scheduler?: Scheduler) => Observable; - static fromArray: (array: T[], scheduler?: Scheduler) => Observable; - static fromEvent: (element: any, eventName: string, selector?: (...args: Array) => T) => Observable; - static fromEventPattern: (addHandler: (handler: Function) => void, - removeHandler: (handler: Function) => void, - selector?: (...args: Array) => T) => Observable; - static fromPromise: (promise: Promise, scheduler?: Scheduler) => Observable; - static interval: (interval: number, scheduler?: Scheduler) => Observable; - static merge: (...observables: Array | Scheduler | number>) => Observable; - static never: () => Observable; - static of: (...values: Array) => Observable; - static range: (start: number, end: number, scheduler?: Scheduler) => Observable; - static throw: (error: T) => Observable; - static timer: (dueTime?: number | Date, period?: number | Scheduler, scheduler?: Scheduler) => Observable; - static zip: (...observables: Array | ((...values: Array) => T)>) => Observable; + static bindCallback: typeof CallbackObservable.create; + static combineLatest: typeof combineLatestStatic; + static concat: typeof concatStatic; + static defer: typeof DeferObservable.create; + static empty: typeof EmptyObservable.create; + static forkJoin: typeof ForkJoinObservable.create; + static from: typeof FromObservable.create; + static fromArray: typeof ArrayObservable.create; + static fromEvent: typeof FromEventObservable.create; + static fromEventPattern: typeof FromEventPatternObservable.create; + static fromPromise: typeof PromiseObservable.create; + static interval: typeof IntervalObservable.create; + static merge: typeof mergeStatic; + static never: typeof InfiniteObservable.create; + static of: typeof ArrayObservable.of; + static range: typeof RangeObservable.create; + static throw: typeof ErrorObservable.create; + static timer: typeof TimerObservable.create; + static zip: typeof zipStatic; // core operators buffer: (closingNotifier: Observable) => Observable; @@ -192,9 +206,7 @@ export class Observable implements CoreOperators { bufferWhen: (closingSelector: () => Observable) => Observable; catch: (selector: (err: any, source: Observable, caught: Observable) => Observable) => Observable; combineAll: (project?: (...values: Array) => R) => Observable; - combineLatest: (...observables: Array | - Array> | - ((...values: Array) => R)>) => Observable; + combineLatest: operator.operator_proto_combineLatest; concat: (...observables: (Observable | Scheduler)[]) => Observable; concatAll: () => Observable; concatMap: (project: ((x: T, ix: number) => Observable), projectResult?: (x: T, y: any, ix: number, iy: number) => R) => Observable; @@ -255,9 +267,9 @@ export class Observable implements CoreOperators { startWith: (x: T) => Observable; subscribeOn: (scheduler: Scheduler, delay?: number) => Observable; switch: () => Observable; - exhaust: () => Observable; + switchFirst: () => Observable; switchMap: (project: ((x: T, ix: number) => Observable), projectResult?: (x: T, y: any, ix: number, iy: number) => R) => Observable; - exhaustMap: (project: (x: T, ix: number) => Observable, rSelector?: (x: T, y: R, ix: number, iy: number) => R2) => Observable; + switchFirstMap: (project: (x: T, ix: number) => Observable, rSelector?: (x: T, y: R, ix: number, iy: number) => R2) => Observable; switchMapTo: (observable: Observable, projectResult?: (x: T, y: any, ix: number, iy: number) => R) => Observable; take: (count: number) => Observable; takeUntil: (notifier: Observable) => Observable; diff --git a/src/operator-typings.ts b/src/operator-typings.ts new file mode 100644 index 0000000000..e563da1a4e --- /dev/null +++ b/src/operator-typings.ts @@ -0,0 +1,41 @@ +/* tslint:disable:class-name */ /* tslint:disable:no-unused-variable */ /* tslint:disable:max-line-length */ +import {Observable} from './Observable'; +import {ConnectableObservable} from './observable/ConnectableObservable'; +import {Scheduler} from './Scheduler'; +import {Notification} from './Notification'; +import {Subject} from './Subject'; +import {Observer} from './Observer'; +import {GroupedObservable} from './operator/groupBy-support'; +import {GroupByObservable} from './operator/groupBy'; +import {TimeInterval} from './operator/extended/timeInterval'; +import {ObservableInput, ObservableOrPromise, ArrayOrIterator, _Selector, _IndexSelector, _SwitchMapResultSelector, _ObservableMergeMapProjector, _IteratorMergeMapProjector, _Predicate, _PredicateObservable, _Comparer, _Accumulator, _MergeAccumulator} from './types'; + +/* ||| MARKER ||| */ +export interface operator_proto_combineLatest { + (project: (v1: T) => TResult): Observable; + (project: (v1: T) => TResult): Observable; + (v2: ObservableInput): Observable<[T, T2]>; + (v2: ObservableInput, v3: ObservableInput): Observable<[T, T2, T3]>; + (v2: ObservableInput, v3: ObservableInput, v4: ObservableInput): Observable<[T, T2, T3, T4]>; + (v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput): Observable<[T, T2, T3, T4, T5]>; + (v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput): Observable<[T, T2, T3, T4, T5, T6]>; + (array: [ObservableInput]): Observable<[T, T2]>; + (array: [ObservableInput, ObservableInput]): Observable<[T, T2, T3]>; + (array: [ObservableInput, ObservableInput, ObservableInput]): Observable<[T, T2, T3, T4]>; + (array: [ObservableInput, ObservableInput, ObservableInput, ObservableInput]): Observable<[T, T2, T3, T4, T5]>; + (array: [ObservableInput, ObservableInput, ObservableInput, ObservableInput, ObservableInput]): Observable<[T, T2, T3, T4, T5, T6]>; + (v2: ObservableInput, project: (v1: T, v2: T2) => TResult): Observable; + (v2: ObservableInput, v3: ObservableInput, project: (v1: T, v2: T2, v3: T3) => TResult): Observable; + (v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, project: (v1: T, v2: T2, v3: T3, v4: T4) => TResult): Observable; + (v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, project: (v1: T, v2: T2, v3: T3, v4: T4, v5: T5) => TResult): Observable; + (v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput, project: (v1: T, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6) => TResult): Observable; + (array: [ObservableInput], project: (v1: T, v2: T2) => TResult): Observable; + (array: [ObservableInput, ObservableInput], project: (v1: T, v2: T2, v3: T3) => TResult): Observable; + (array: [ObservableInput, ObservableInput, ObservableInput], project: (v1: T, v2: T2, v3: T3, v4: T4) => TResult): Observable; + (array: [ObservableInput, ObservableInput, ObservableInput, ObservableInput], project: (v1: T, v2: T2, v3: T3, v4: T4, v5: T5) => TResult): Observable; + (array: [ObservableInput, ObservableInput, ObservableInput, ObservableInput, ObservableInput], project: (v1: T, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6) => TResult): Observable; + (array: ObservableInput[], project?: Function): Observable; + (...observables: Array>): Observable; + (...observables: Array | ((...values: Array) => R)>): Observable; +} +/* ||| MARKER ||| */ diff --git a/src/operator/combineLatest.ts b/src/operator/combineLatest.ts index 08b77ff125..78b1575d98 100644 --- a/src/operator/combineLatest.ts +++ b/src/operator/combineLatest.ts @@ -2,6 +2,7 @@ import {Observable} from '../Observable'; import {ArrayObservable} from '../observable/fromArray'; import {CombineLatestOperator} from './combineLatest-support'; import {isArray} from '../util/isArray'; +import {ObservableInput} from '../types'; /** * Combines the values from this observable with values from observables passed as arguments. This is done by subscribing @@ -13,10 +14,21 @@ import {isArray} from '../util/isArray'; * @returns {Observable} an observable of other projected values from the most recent values from each observable, or an array of each of * the most recent values from each observable. */ -export function combineLatest(...observables: Array | +export function combineLatest(project: (v1: T) => TResult): Observable; +export function combineLatest(project: (v1: T) => TResult): Observable; +/*-- *compute 2-6* export function combineLatest({|v|: ObservableInput<|X|>}): Observable<[T, {|X|}]>; --*/ +/*-- *compute 2-6* export function combineLatest(array: [{ObservableInput<|X|>}]): Observable<[T, {|X|}]>; --*/ +/*-- *compute 2-6* export function combineLatest({|v|: ObservableInput<|X|>}, + project: (v1: T, {|v|: |X|}) => TResult): Observable; --*/ +/*-- *compute 2-6* export function combineLatest(array: [{ObservableInput<|X|>}], + project: (v1: T, {|v|: |X|}) => TResult): Observable; --*/ +export function combineLatest(array: ObservableInput[], project?: Function): Observable; +export function combineLatest(...observables: Array>): Observable; +export function combineLatest(...observables: Array | ((...values: Array) => R)>): Observable; +export function combineLatest(...observables: Array | Array> | ((...values: Array) => R)>): Observable { - let project: (...values: Array) => R = null; + let project: (...values: Array) => R = null; if (typeof observables[observables.length - 1] === 'function') { project = <(...values: Array) => R>observables.pop(); } @@ -24,7 +36,7 @@ export function combineLatest(...observables: Array | // if the first and only other argument besides the resultSelector is an array // assume it's been called with `combineLatest([obs1, obs2, obs3], project)` if (observables.length === 1 && isArray(observables[0])) { - observables = >>observables[0]; + observables = observables[0]; } observables.unshift(this); diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000000..5791b75c03 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,17 @@ +import {Observable} from './Observable'; +export type ObservableOrPromise = Observable | Promise; +export type ArrayOrIterator = Iterator | ArrayLike | Array; +export type ObservableInput = Observable | Promise | Iterator | ArrayLike; + +export type _Selector = (value: T) => TResult; +export type _IndexSelector = (value: T, index: number) => TResult; +export type _SwitchMapResultSelector = (outerValue: T1, innerValue: T2, outerIndex: number, innerIndex: number) => TResult; +export type _ObservableMergeMapProjector = (value: T, index: number) => ObservableOrPromise; +export type _IteratorMergeMapProjector = (value: T, index: number) => ArrayOrIterator; + +export type _Predicate = _Selector; +export type _PredicateObservable = (value: T, index: number, observable: Observable) => boolean; + +export type _Comparer = (value1: T, value2: T) => TResult; +export type _Accumulator = (acc: TAcc, value: T) => TAcc; +export type _MergeAccumulator = (acc: TAcc, value: T) => Observable; diff --git a/tsconfig.json b/tsconfig.json index 9c90f016a6..bb438d1ca2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,8 +11,9 @@ "indentSize": 2, "tabSize": 2 }, - "files": [ - "src/Rx.ts", - "src/Rx.KitchenSink.ts" - ] + "files": [ + "typings/es6-build-shim.d.ts", + "src/Rx.ts", + "src/Rx.KitchenSink.ts" + ] } diff --git a/typings/es6-build-shim.d.ts b/typings/es6-build-shim.d.ts new file mode 100644 index 0000000000..ed0410dcbf --- /dev/null +++ b/typings/es6-build-shim.d.ts @@ -0,0 +1 @@ +declare type IterableShim = Iterable; diff --git a/typingsgen.js b/typingsgen.js new file mode 100644 index 0000000000..c8778b40e4 --- /dev/null +++ b/typingsgen.js @@ -0,0 +1,118 @@ +var fs = require('fs'); +var regex = /export interface .*?Operators \{([\S|\s]*)\}/; + +var core = fs.readFileSync('./src/CoreOperators.ts').toString(); +var kitchenSink = fs.readFileSync('./src/Rx.KitchenSink.ts').toString(); +var combinedMethods = core.match(regex)[1].trim() + '\n' + kitchenSink.match(regex)[1].trim(); +var contents = combinedMethods.split('\n'); + +var operators = {}; +var fileResult = ''; + +for (var i = 0; i < contents.length; i++) { + var item = contents[i].trim(); + if (item) { + var file = item.match(/(.*?)\: operator.operator_proto_(.*?);/); + if (!file) { + continue; + } + + var filename = file[2].trim(); + var fileContent; + + if (fs.existsSync('./src/operator/' + filename + '.ts')) { + fileContent = fs.readFileSync('./src/operator/' + filename + '.ts').toString('utf8'); + } else { + fileContent = fs.readFileSync('./src/operator/extended/' + filename + '.ts').toString('utf8'); + } + + fileContent = computeTypingsFor(fileContent); + + var methods = []; + + var r = new RegExp('export function [_]?' + filename + '([\\s|\\S]*?[\\;\\{])', 'g'); + + do { + var result = r.exec(fileContent); + if (result) { + var method = result[1].trim(); + if (methods.length > 0 && method.indexOf('{') > -1) { + continue; + } + + method = method.split(/\n/g) + .filter(function (x) { return !!x; }) + .map(function (x) { return ('' + x).trim(); }) + .join(' ') + .replace(/ = .*?([\,|\)])/g, '$1'); + + if (method[method.length - 1] === ';' || method[method.length - 1] === '{') { + method = method.substr(0, method.length - 1).trim(); + } + + method = method.replace(/^/, '').replace(/^ {\n ' + methods.join(';\n ') + ';\n}\n'; + } + } +} + +var typingsContent = fs.readFileSync('./src/operator-typings.ts').toString(); +fileResult = '/* ||| MARKER ||| */\n' + fileResult + '/* ||| MARKER ||| */'; +typingsContent = typingsContent.replace(/(\/\* \|\|\| MARKER \|\|\| \*\/[\s|\S]*?\/\* \|\|\| MARKER \|\|\| \*\/)/, fileResult); +fs.writeFileSync('./src/operator-typings.ts', typingsContent); + + +function computeTypingsFor(s) { + var captureRegex = /\/\*\-\-([\s|\S]*?)-\-\*\//g; + var computeNumberRegex = /\*compute (\d.*?)?\*/; + var tokenRegex = /\{.*?\}/g; + + s = s.replace(captureRegex, function(capture) { + capture = capture.trim(); + capture = capture.substr(3, capture.length - 3 * 2); + var compute = computeNumberRegex.exec(capture); + if (compute) { + compute = compute[1] || '6'; + } else { + compute = '6'; + } + var range = compute.split('-'); + if (range.length === 1) { + var start = 1; + var end = range[0]; + } else { + var start = range[0]; + var end = range[1]; + } + + capture = capture.replace(computeNumberRegex, '').trim(); + + var tokenResult; + var results = []; + for (var number = start; number <= end; number++) { + var res = capture.replace(tokenRegex, function(capture, index, str) { + var items = []; + capture = capture.substr(1, capture.length - 2); + for (var i = start; i <= number; i++) { + var typeName = 'T' + (i === 1 ? '' : i); + items.push(capture.replace(/\|X\|/g, typeName).replace(/\|v\|/g, 'v' + i)); + } + + return items.join(', '); + }); + results.push(res); + } + + return results.join('\n'); + }); + + return s; +} + +