refactor: move angular source to /packages rather than modules/@angular

This commit is contained in:
Jason Aden
2017-03-02 10:48:42 -08:00
parent 5ad5301a3e
commit 3e51a19983
1051 changed files with 18 additions and 18 deletions

View File

@ -0,0 +1,11 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken} from '@angular/core';
export const ON_WEB_WORKER = new InjectionToken<boolean>('WebWorker.onWebWorker');

View File

@ -0,0 +1,168 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {EventEmitter, Injectable, Type, ɵstringify as stringify} from '@angular/core';
import {MessageBus} from './message_bus';
import {Serializer, SerializerTypes} from './serializer';
/**
* @experimental WebWorker support in Angular is experimental.
*/
export abstract class ClientMessageBrokerFactory {
/**
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
*/
abstract createMessageBroker(channel: string, runInZone?: boolean): ClientMessageBroker;
}
@Injectable()
export class ClientMessageBrokerFactory_ extends ClientMessageBrokerFactory {
/** @internal */
_serializer: Serializer;
constructor(private _messageBus: MessageBus, _serializer: Serializer) {
super();
this._serializer = _serializer;
}
/**
* Initializes the given channel and attaches a new {@link ClientMessageBroker} to it.
*/
createMessageBroker(channel: string, runInZone: boolean = true): ClientMessageBroker {
this._messageBus.initChannel(channel, runInZone);
return new ClientMessageBroker_(this._messageBus, this._serializer, channel);
}
}
/**
* @experimental WebWorker support in Angular is experimental.
*/
export abstract class ClientMessageBroker {
abstract runOnService(args: UiArguments, returnType: Type<any>|SerializerTypes): Promise<any>;
}
interface PromiseCompleter {
resolve: (result: any) => void;
reject: (err: any) => void;
}
export class ClientMessageBroker_ extends ClientMessageBroker {
private _pending = new Map<string, PromiseCompleter>();
private _sink: EventEmitter<any>;
/** @internal */
public _serializer: Serializer;
constructor(messageBus: MessageBus, _serializer: Serializer, public channel: any) {
super();
this._sink = messageBus.to(channel);
this._serializer = _serializer;
const source = messageBus.from(channel);
source.subscribe({next: (message: ResponseMessageData) => this._handleMessage(message)});
}
private _generateMessageId(name: string): string {
const time: string = stringify(new Date().getTime());
let iteration: number = 0;
let id: string = name + time + stringify(iteration);
while (this._pending.has(id)) {
id = `${name}${time}${iteration}`;
iteration++;
}
return id;
}
runOnService(args: UiArguments, returnType: Type<any>|SerializerTypes): Promise<any> {
const fnArgs: any[] = [];
if (args.args) {
args.args.forEach(argument => {
if (argument.type != null) {
fnArgs.push(this._serializer.serialize(argument.value, argument.type));
} else {
fnArgs.push(argument.value);
}
});
}
let promise: Promise<any>;
let id: string = null;
if (returnType != null) {
let completer: PromiseCompleter;
promise = new Promise((resolve, reject) => { completer = {resolve, reject}; });
id = this._generateMessageId(args.method);
this._pending.set(id, completer);
promise.catch((err) => {
if (console && console.error) {
// tslint:disable-next-line:no-console
console.error(err);
}
completer.reject(err);
});
promise = promise.then(
(v: any) => this._serializer ? this._serializer.deserialize(v, returnType) : v);
} else {
promise = null;
}
const message: RequestMessageData = {
'method': args.method,
'args': fnArgs,
};
if (id != null) {
message['id'] = id;
}
this._sink.emit(message);
return promise;
}
private _handleMessage(message: ResponseMessageData): void {
if (message.type === 'result' || message.type === 'error') {
const id = message.id;
if (this._pending.has(id)) {
if (message.type === 'result') {
this._pending.get(id).resolve(message.value);
} else {
this._pending.get(id).reject(message.value);
}
this._pending.delete(id);
}
}
}
}
interface RequestMessageData {
method: string;
args?: any[];
id?: string;
}
interface ResponseMessageData {
type: 'result'|'error';
value?: any;
id?: string;
}
/**
* @experimental WebWorker support in Angular is experimental.
*/
export class FnArg {
constructor(
public value: any, public type: Type<any>|SerializerTypes = SerializerTypes.PRIMITIVE) {}
}
/**
* @experimental WebWorker support in Angular is experimental.
*/
export class UiArguments {
constructor(public method: string, public args?: FnArg[]) {}
}

View File

@ -0,0 +1,103 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {EventEmitter, NgZone} from '@angular/core';
/**
* Message Bus is a low level API used to communicate between the UI and the background.
* Communication is based on a channel abstraction. Messages published in a
* given channel to one MessageBusSink are received on the same channel
* by the corresponding MessageBusSource.
*
* @experimental WebWorker support in Angular is currenlty experimental.
*/
export abstract class MessageBus implements MessageBusSource, MessageBusSink {
/**
* Sets up a new channel on the MessageBus.
* MUST be called before calling from or to on the channel.
* If runInZone is true then the source will emit events inside the angular zone
* and the sink will buffer messages and send only once the zone exits.
* if runInZone is false then the source will emit events inside the global zone
* and the sink will send messages immediately.
*/
abstract initChannel(channel: string, runInZone?: boolean): void;
/**
* Assigns this bus to the given zone.
* Any callbacks attached to channels where runInZone was set to true on initialization
* will be executed in the given zone.
*/
abstract attachToZone(zone: NgZone): void;
/**
* Returns an {@link EventEmitter} that emits every time a message
* is received on the given channel.
*/
abstract from(channel: string): EventEmitter<any>;
/**
* Returns an {@link EventEmitter} for the given channel
* To publish methods to that channel just call next on the returned emitter
*/
abstract to(channel: string): EventEmitter<any>;
}
/**
* @experimental WebWorker support in Angular is currenlty experimental.
*/
export interface MessageBusSource {
/**
* Sets up a new channel on the MessageBusSource.
* MUST be called before calling from on the channel.
* If runInZone is true then the source will emit events inside the angular zone.
* if runInZone is false then the source will emit events inside the global zone.
*/
initChannel(channel: string, runInZone: boolean): void;
/**
* Assigns this source to the given zone.
* Any channels which are initialized with runInZone set to true will emit events that will be
* executed within the given zone.
*/
attachToZone(zone: NgZone): void;
/**
* Returns an {@link EventEmitter} that emits every time a message
* is received on the given channel.
*/
from(channel: string): EventEmitter<any>;
}
/**
* @experimental WebWorker support in Angular is currenlty experimental.
*/
export interface MessageBusSink {
/**
* Sets up a new channel on the MessageBusSink.
* MUST be called before calling to on the channel.
* If runInZone is true the sink will buffer messages and send only once the zone exits.
* if runInZone is false the sink will send messages immediatly.
*/
initChannel(channel: string, runInZone: boolean): void;
/**
* Assigns this sink to the given zone.
* Any channels which are initialized with runInZone set to true will wait for the given zone
* to exit before sending messages.
*/
attachToZone(zone: NgZone): void;
/**
* Returns an {@link EventEmitter} for the given channel
* To publish methods to that channel just call next on the returned emitter
*/
to(channel: string): EventEmitter<any>;
}

View File

@ -0,0 +1,16 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/**
* All channels used by angular's WebWorker components are listed here.
* You should not use these channels in your application code.
*/
export const RENDERER_2_CHANNEL = 'v2.ng-Renderer';
export const EVENT_2_CHANNEL = 'v2.ng-Events';
export const ROUTER_CHANNEL = 'ng-Router';

View File

@ -0,0 +1,152 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {EventEmitter, Injectable, NgZone} from '@angular/core';
import {MessageBus, MessageBusSink, MessageBusSource} from './message_bus';
// TODO(jteplitz602) Replace this with the definition in lib.webworker.d.ts(#3492)
export interface PostMessageTarget {
postMessage: (message: any, transfer?: [ArrayBuffer]) => void;
}
export class PostMessageBusSink implements MessageBusSink {
private _zone: NgZone;
private _channels: {[key: string]: _Channel} = {};
private _messageBuffer: Array<Object> = [];
constructor(private _postMessageTarget: PostMessageTarget) {}
attachToZone(zone: NgZone): void {
this._zone = zone;
this._zone.runOutsideAngular(
() => { this._zone.onStable.subscribe({next: () => { this._handleOnEventDone(); }}); });
}
initChannel(channel: string, runInZone: boolean = true): void {
if (this._channels.hasOwnProperty(channel)) {
throw new Error(`${channel} has already been initialized`);
}
const emitter = new EventEmitter(false);
const channelInfo = new _Channel(emitter, runInZone);
this._channels[channel] = channelInfo;
emitter.subscribe((data: Object) => {
const message = {channel: channel, message: data};
if (runInZone) {
this._messageBuffer.push(message);
} else {
this._sendMessages([message]);
}
});
}
to(channel: string): EventEmitter<any> {
if (this._channels.hasOwnProperty(channel)) {
return this._channels[channel].emitter;
} else {
throw new Error(`${channel} is not set up. Did you forget to call initChannel?`);
}
}
private _handleOnEventDone() {
if (this._messageBuffer.length > 0) {
this._sendMessages(this._messageBuffer);
this._messageBuffer = [];
}
}
private _sendMessages(messages: Array<Object>) { this._postMessageTarget.postMessage(messages); }
}
export class PostMessageBusSource implements MessageBusSource {
private _zone: NgZone;
private _channels: {[key: string]: _Channel} = {};
constructor(eventTarget?: EventTarget) {
if (eventTarget) {
eventTarget.addEventListener('message', (ev: MessageEvent) => this._handleMessages(ev));
} else {
// if no eventTarget is given we assume we're in a WebWorker and listen on the global scope
const workerScope = <EventTarget>self;
workerScope.addEventListener('message', (ev: MessageEvent) => this._handleMessages(ev));
}
}
attachToZone(zone: NgZone) { this._zone = zone; }
initChannel(channel: string, runInZone: boolean = true) {
if (this._channels.hasOwnProperty(channel)) {
throw new Error(`${channel} has already been initialized`);
}
const emitter = new EventEmitter(false);
const channelInfo = new _Channel(emitter, runInZone);
this._channels[channel] = channelInfo;
}
from(channel: string): EventEmitter<any> {
if (this._channels.hasOwnProperty(channel)) {
return this._channels[channel].emitter;
} else {
throw new Error(`${channel} is not set up. Did you forget to call initChannel?`);
}
}
private _handleMessages(ev: MessageEvent): void {
const messages = ev.data;
for (let i = 0; i < messages.length; i++) {
this._handleMessage(messages[i]);
}
}
private _handleMessage(data: any): void {
const channel = data.channel;
if (this._channels.hasOwnProperty(channel)) {
const channelInfo = this._channels[channel];
if (channelInfo.runInZone) {
this._zone.run(() => { channelInfo.emitter.emit(data.message); });
} else {
channelInfo.emitter.emit(data.message);
}
}
}
}
/**
* A TypeScript implementation of {@link MessageBus} for communicating via JavaScript's
* postMessage API.
*/
@Injectable()
export class PostMessageBus implements MessageBus {
constructor(public sink: PostMessageBusSink, public source: PostMessageBusSource) {}
attachToZone(zone: NgZone): void {
this.source.attachToZone(zone);
this.sink.attachToZone(zone);
}
initChannel(channel: string, runInZone: boolean = true): void {
this.source.initChannel(channel, runInZone);
this.sink.initChannel(channel, runInZone);
}
from(channel: string): EventEmitter<any> { return this.source.from(channel); }
to(channel: string): EventEmitter<any> { return this.sink.to(channel); }
}
/**
* Helper class that wraps a channel's {@link EventEmitter} and
* keeps track of if it should run in the zone.
*/
class _Channel {
constructor(public emitter: EventEmitter<any>, public runInZone: boolean) {}
}

View File

@ -0,0 +1,38 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable} from '@angular/core';
@Injectable()
export class RenderStore {
private _nextIndex = 0;
private _lookupById = new Map<number, any>();
private _lookupByObject = new Map<any, number>();
allocateId(): number { return this._nextIndex++; }
store(obj: any, id: number): void {
if (id == null) return;
this._lookupById.set(id, obj);
this._lookupByObject.set(obj, id);
}
remove(obj: any): void {
const index = this._lookupByObject.get(obj);
if (index != null) {
this._lookupByObject.delete(obj);
this._lookupById.delete(index);
}
}
deserialize(id: number): any {
return this._lookupById.has(id) ? this._lookupById.get(id) : null;
}
serialize(obj: any): number { return obj == null ? null : this._lookupByObject.get(obj); }
}

View File

@ -0,0 +1,142 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Injectable, RenderComponentType, RendererType2, Type, ɵstringify as stringify} from '@angular/core';
import {RenderStore} from './render_store';
/**
* Any type that does not need to be serialized (string, number, boolean)
*
* @experimental WebWorker support in Angular is currently experimental.
* @deprecated in v4. Use SerializerTypes.PRIMITIVE instead
*/
export const PRIMITIVE = SerializerTypes.PRIMITIVE;
export class LocationType {
constructor(
public href: string, public protocol: string, public host: string, public hostname: string,
public port: string, public pathname: string, public search: string, public hash: string,
public origin: string) {}
}
/**
* @experimental WebWorker support in Angular is currently experimental.
*/
export const enum SerializerTypes {
// RendererType2
RENDERER_TYPE_2,
// Primitive types
PRIMITIVE,
// An object stored in a RenderStore
RENDER_STORE_OBJECT,
}
@Injectable()
export class Serializer {
constructor(private _renderStore: RenderStore) {}
serialize(obj: any, type: Type<any>|SerializerTypes = SerializerTypes.PRIMITIVE): Object {
if (obj == null || type === SerializerTypes.PRIMITIVE) {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(v => this.serialize(v, type));
}
if (type === SerializerTypes.RENDER_STORE_OBJECT) {
return this._renderStore.serialize(obj);
}
if (type === RenderComponentType) {
return this._serializeRenderComponentType(obj);
}
if (type === SerializerTypes.RENDERER_TYPE_2) {
return this._serializeRendererType2(obj);
}
if (type === LocationType) {
return this._serializeLocation(obj);
}
throw new Error(`No serializer for type ${stringify(type)}`);
}
deserialize(map: any, type: Type<any>|SerializerTypes = SerializerTypes.PRIMITIVE, data?: any):
any {
if (map == null || type === SerializerTypes.PRIMITIVE) {
return map;
}
if (Array.isArray(map)) {
return map.map(val => this.deserialize(val, type, data));
}
if (type === SerializerTypes.RENDER_STORE_OBJECT) {
return this._renderStore.deserialize(map);
}
if (type === RenderComponentType) {
return this._deserializeRenderComponentType(map);
}
if (type === SerializerTypes.RENDERER_TYPE_2) {
return this._deserializeRendererType2(map);
}
if (type === LocationType) {
return this._deserializeLocation(map);
}
throw new Error(`No deserializer for type ${stringify(type)}`);
}
private _serializeLocation(loc: LocationType): Object {
return {
'href': loc.href,
'protocol': loc.protocol,
'host': loc.host,
'hostname': loc.hostname,
'port': loc.port,
'pathname': loc.pathname,
'search': loc.search,
'hash': loc.hash,
'origin': loc.origin,
};
}
private _deserializeLocation(loc: {[key: string]: any}): LocationType {
return new LocationType(
loc['href'], loc['protocol'], loc['host'], loc['hostname'], loc['port'], loc['pathname'],
loc['search'], loc['hash'], loc['origin']);
}
private _serializeRenderComponentType(type: RenderComponentType): Object {
return {
'id': type.id,
'templateUrl': type.templateUrl,
'slotCount': type.slotCount,
'encapsulation': this.serialize(type.encapsulation),
'styles': this.serialize(type.styles),
};
}
private _deserializeRenderComponentType(props: {[key: string]: any}): RenderComponentType {
return new RenderComponentType(
props['id'], props['templateUrl'], props['slotCount'],
this.deserialize(props['encapsulation']), this.deserialize(props['styles']), {});
}
private _serializeRendererType2(type: RendererType2): {[key: string]: any} {
return {
'id': type.id,
'encapsulation': this.serialize(type.encapsulation),
'styles': this.serialize(type.styles),
'data': this.serialize(type.data),
};
}
private _deserializeRendererType2(props: {[key: string]: any}): RendererType2 {
return {
id: props['id'],
encapsulation: props['encapsulation'],
styles: this.deserialize(props['styles']),
data: this.deserialize(props['data'])
};
}
}

View File

@ -0,0 +1,110 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {EventEmitter, Injectable, Type} from '@angular/core';
import {MessageBus} from '../shared/message_bus';
import {Serializer, SerializerTypes} from '../shared/serializer';
/**
* @experimental WebWorker support in Angular is currently experimental.
*/
export abstract class ServiceMessageBrokerFactory {
/**
* Initializes the given channel and attaches a new {@link ServiceMessageBroker} to it.
*/
abstract createMessageBroker(channel: string, runInZone?: boolean): ServiceMessageBroker;
}
@Injectable()
export class ServiceMessageBrokerFactory_ extends ServiceMessageBrokerFactory {
/** @internal */
_serializer: Serializer;
constructor(private _messageBus: MessageBus, _serializer: Serializer) {
super();
this._serializer = _serializer;
}
createMessageBroker(channel: string, runInZone: boolean = true): ServiceMessageBroker {
this._messageBus.initChannel(channel, runInZone);
return new ServiceMessageBroker_(this._messageBus, this._serializer, channel);
}
}
/**
* Helper class for UIComponents that allows components to register methods.
* If a registered method message is received from the broker on the worker,
* the UIMessageBroker deserializes its arguments and calls the registered method.
* If that method returns a promise, the UIMessageBroker returns the result to the worker.
*
* @experimental WebWorker support in Angular is currently experimental.
*/
export abstract class ServiceMessageBroker {
abstract registerMethod(
methodName: string, signature: Array<Type<any>|SerializerTypes>, method: Function,
returnType?: Type<any>|SerializerTypes): void;
}
export class ServiceMessageBroker_ extends ServiceMessageBroker {
private _sink: EventEmitter<any>;
private _methods = new Map<string, Function>();
constructor(messageBus: MessageBus, private _serializer: Serializer, public channel: string) {
super();
this._sink = messageBus.to(channel);
const source = messageBus.from(channel);
source.subscribe({next: (message: any) => this._handleMessage(message)});
}
registerMethod(
methodName: string, signature: Array<Type<any>|SerializerTypes>,
method: (..._: any[]) => Promise<any>| void, returnType?: Type<any>|SerializerTypes): void {
this._methods.set(methodName, (message: ReceivedMessage) => {
const serializedArgs = message.args;
const numArgs = signature ? signature.length : 0;
const deserializedArgs = new Array(numArgs);
for (let i = 0; i < numArgs; i++) {
const serializedArg = serializedArgs[i];
deserializedArgs[i] = this._serializer.deserialize(serializedArg, signature[i]);
}
const promise = method(...deserializedArgs);
if (returnType && promise) {
this._wrapWebWorkerPromise(message.id, promise, returnType);
}
});
}
private _handleMessage(message: ReceivedMessage): void {
if (this._methods.has(message.method)) {
this._methods.get(message.method)(message);
}
}
private _wrapWebWorkerPromise(id: string, promise: Promise<any>, type: Type<any>|SerializerTypes):
void {
promise.then((result: any) => {
this._sink.emit({
'type': 'result',
'value': this._serializer.serialize(result, type),
'id': id,
});
});
}
}
/**
* @experimental WebWorker support in Angular is currently experimental.
*/
export interface ReceivedMessage {
method: string;
args: any[];
id: string;
type: string;
}