fix(service-worker): ensure initialised in browser only (#20782)

closes #20360

PR Close #20782
This commit is contained in:
Fabian Wiles 2017-11-12 12:39:27 +13:00 committed by Igor Minar
parent da3563ce19
commit 7cabaa0ae7
7 changed files with 39 additions and 18 deletions

View File

@ -12,7 +12,8 @@
"tslib": "^1.7.1" "tslib": "^1.7.1"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "0.0.0-PLACEHOLDER" "@angular/core": "0.0.0-PLACEHOLDER",
"@angular/common": "0.0.0-PLACEHOLDER"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -11,6 +11,7 @@ const sourcemaps = require('rollup-plugin-sourcemaps');
const globals = { const globals = {
'@angular/core': 'ng.core', '@angular/core': 'ng.core',
'@angular/common': 'ng.common',
'rxjs/BehaviorSubject': 'Rx', 'rxjs/BehaviorSubject': 'Rx',
'rxjs/ConnectableObservable': 'Rx', 'rxjs/ConnectableObservable': 'Rx',

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Injectable} from '@angular/core'; import {isPlatformBrowser} from '@angular/common';
import {Inject, Injectable, PLATFORM_ID} from '@angular/core';
import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import {ConnectableObservable} from 'rxjs/observable/ConnectableObservable'; import {ConnectableObservable} from 'rxjs/observable/ConnectableObservable';
@ -86,8 +87,11 @@ export class NgswCommChannel {
*/ */
readonly events: Observable<IncomingEvent>; readonly events: Observable<IncomingEvent>;
constructor(private serviceWorker: ServiceWorkerContainer|undefined) { constructor(
if (!serviceWorker) { private serviceWorker: ServiceWorkerContainer|undefined,
@Inject(PLATFORM_ID) platformId: string) {
if (!serviceWorker || !isPlatformBrowser(platformId)) {
this.serviceWorker = undefined;
this.worker = this.events = this.registration = errorObservable(ERR_SW_NOT_SUPPORTED); this.worker = this.events = this.registration = errorObservable(ERR_SW_NOT_SUPPORTED);
} else { } else {
const controllerChangeEvents = const controllerChangeEvents =

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {APP_INITIALIZER, ApplicationRef, Inject, InjectionToken, Injector, ModuleWithProviders, NgModule} from '@angular/core'; import {isPlatformBrowser} from '@angular/common';
import {APP_INITIALIZER, ApplicationRef, Inject, InjectionToken, Injector, ModuleWithProviders, NgModule, PLATFORM_ID} from '@angular/core';
import {Observable} from 'rxjs/Observable'; import {Observable} from 'rxjs/Observable';
import {filter as op_filter} from 'rxjs/operator/filter'; import {filter as op_filter} from 'rxjs/operator/filter';
import {take as op_take} from 'rxjs/operator/take'; import {take as op_take} from 'rxjs/operator/take';
@ -24,10 +25,12 @@ export abstract class RegistrationOptions {
export const SCRIPT = new InjectionToken<string>('NGSW_REGISTER_SCRIPT'); export const SCRIPT = new InjectionToken<string>('NGSW_REGISTER_SCRIPT');
export function ngswAppInitializer( export function ngswAppInitializer(
injector: Injector, script: string, options: RegistrationOptions): Function { injector: Injector, script: string, options: RegistrationOptions,
platformId: string): Function {
const initializer = () => { const initializer = () => {
const app = injector.get<ApplicationRef>(ApplicationRef); const app = injector.get<ApplicationRef>(ApplicationRef);
if (!('serviceWorker' in navigator) || options.enabled === false) { if (!(isPlatformBrowser(platformId) && ('serviceWorker' in navigator) &&
options.enabled !== false)) {
return; return;
} }
const onStable = const onStable =
@ -51,8 +54,10 @@ export function ngswAppInitializer(
return initializer; return initializer;
} }
export function ngswCommChannelFactory(opts: RegistrationOptions): NgswCommChannel { export function ngswCommChannelFactory(
return new NgswCommChannel(opts.enabled !== false ? navigator.serviceWorker : undefined); opts: RegistrationOptions, platformId: string): NgswCommChannel {
return new NgswCommChannel(
opts.enabled !== false ? navigator.serviceWorker : undefined, platformId);
} }
/** /**
@ -75,11 +80,15 @@ export class ServiceWorkerModule {
providers: [ providers: [
{provide: SCRIPT, useValue: script}, {provide: SCRIPT, useValue: script},
{provide: RegistrationOptions, useValue: opts}, {provide: RegistrationOptions, useValue: opts},
{provide: NgswCommChannel, useFactory: ngswCommChannelFactory, deps: [RegistrationOptions]}, {
provide: NgswCommChannel,
useFactory: ngswCommChannelFactory,
deps: [RegistrationOptions, PLATFORM_ID]
},
{ {
provide: APP_INITIALIZER, provide: APP_INITIALIZER,
useFactory: ngswAppInitializer, useFactory: ngswAppInitializer,
deps: [Injector, SCRIPT, RegistrationOptions], deps: [Injector, SCRIPT, RegistrationOptions, PLATFORM_ID],
multi: true, multi: true,
}, },
], ],

View File

@ -20,12 +20,12 @@ export function main() {
let comm: NgswCommChannel; let comm: NgswCommChannel;
beforeEach(() => { beforeEach(() => {
mock = new MockServiceWorkerContainer(); mock = new MockServiceWorkerContainer();
comm = new NgswCommChannel(mock as any); comm = new NgswCommChannel(mock as any, 'browser');
}); });
describe('NgswCommsChannel', () => { describe('NgswCommsChannel', () => {
it('can access the registration when it comes before subscription', (done: DoneFn) => { it('can access the registration when it comes before subscription', (done: DoneFn) => {
const mock = new MockServiceWorkerContainer(); const mock = new MockServiceWorkerContainer();
const comm = new NgswCommChannel(mock as any); const comm = new NgswCommChannel(mock as any, 'browser');
const regPromise = mock.getRegistration() as any as MockServiceWorkerRegistration; const regPromise = mock.getRegistration() as any as MockServiceWorkerRegistration;
mock.setupSw(); mock.setupSw();
@ -34,13 +34,18 @@ export function main() {
}); });
it('can access the registration when it comes after subscription', (done: DoneFn) => { it('can access the registration when it comes after subscription', (done: DoneFn) => {
const mock = new MockServiceWorkerContainer(); const mock = new MockServiceWorkerContainer();
const comm = new NgswCommChannel(mock as any); const comm = new NgswCommChannel(mock as any, 'browser');
const regPromise = mock.getRegistration() as any as MockServiceWorkerRegistration; const regPromise = mock.getRegistration() as any as MockServiceWorkerRegistration;
comm.registration.subscribe(reg => { done(); }); comm.registration.subscribe(reg => { done(); });
mock.setupSw(); mock.setupSw();
}); });
it('is disabled for platform-server', () => {
const mock = new MockServiceWorkerContainer();
const comm = new NgswCommChannel(mock as any, 'server');
expect(comm.isEnabled).toEqual(false);
});
}); });
describe('SwPush', () => { describe('SwPush', () => {
let push: SwPush; let push: SwPush;
@ -72,7 +77,7 @@ export function main() {
expect(() => TestBed.get(SwPush)).not.toThrow(); expect(() => TestBed.get(SwPush)).not.toThrow();
}); });
describe('with no SW', () => { describe('with no SW', () => {
beforeEach(() => { comm = new NgswCommChannel(undefined); }); beforeEach(() => { comm = new NgswCommChannel(undefined, 'browser'); });
it('can be instantiated', () => { push = new SwPush(comm); }); it('can be instantiated', () => { push = new SwPush(comm); });
it('does not crash on subscription to observables', () => { it('does not crash on subscription to observables', () => {
push = new SwPush(comm); push = new SwPush(comm);
@ -166,7 +171,7 @@ export function main() {
expect(() => TestBed.get(SwUpdate)).not.toThrow(); expect(() => TestBed.get(SwUpdate)).not.toThrow();
}); });
describe('with no SW', () => { describe('with no SW', () => {
beforeEach(() => { comm = new NgswCommChannel(undefined); }); beforeEach(() => { comm = new NgswCommChannel(undefined, 'browser'); });
it('can be instantiated', () => { update = new SwUpdate(comm); }); it('can be instantiated', () => { update = new SwUpdate(comm); });
it('does not crash on subscription to observables', () => { it('does not crash on subscription to observables', () => {
update = new SwUpdate(comm); update = new SwUpdate(comm);

View File

@ -80,7 +80,7 @@ export function main() {
async_beforeEach(async() => { async_beforeEach(async() => {
// Fire up the client. // Fire up the client.
mock = new MockServiceWorkerContainer(); mock = new MockServiceWorkerContainer();
comm = new NgswCommChannel(mock as any); comm = new NgswCommChannel(mock as any, 'browser');
scope = new SwTestHarnessBuilder().withServerState(server).build(); scope = new SwTestHarnessBuilder().withServerState(server).build();
driver = new Driver(scope, scope, new CacheDatabase(scope, scope)); driver = new Driver(scope, scope, new CacheDatabase(scope, scope));

View File

@ -5,7 +5,8 @@
"baseUrl": ".", "baseUrl": ".",
"rootDir": ".", "rootDir": ".",
"paths": { "paths": {
"@angular/core": ["../../dist/packages/core"] "@angular/core": ["../../dist/packages/core"],
"@angular/common": ["../../dist/packages/common"]
}, },
"outDir": "../../dist/packages/service-worker" "outDir": "../../dist/packages/service-worker"
}, },