refactor(core): separate lazy load & content insertion

pull/3453/head
Arman Ozak 6 years ago
parent 21095025a9
commit 7da8011eee

@ -1,7 +1,6 @@
import { Observable, of } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { fromLazyLoad } from '../utils';
import { ContentSecurityStrategy, CONTENT_SECURITY_STRATEGY } from './content-security.strategy';
import { CrossOriginStrategy, CROSS_ORIGIN_STRATEGY } from './cross-origin.strategy';
import { DomStrategy, DOM_STRATEGY } from './dom.strategy';
@ -10,7 +9,6 @@ export abstract class LoadingStrategy<T extends HTMLScriptElement | HTMLLinkElem
public path: string,
protected domStrategy: DomStrategy = DOM_STRATEGY.AppendToHead(),
protected crossOriginStrategy: CrossOriginStrategy = CROSS_ORIGIN_STRATEGY.Anonymous(),
protected contentSecurityStrategy: ContentSecurityStrategy = CONTENT_SECURITY_STRATEGY.Loose(),
) {}
abstract createElement(): T;
@ -18,25 +16,15 @@ export abstract class LoadingStrategy<T extends HTMLScriptElement | HTMLLinkElem
createStream<E extends Event>(): Observable<E> {
return of(null).pipe(
switchMap(() =>
fromLazyLoad<E>(
this.createElement(),
this.domStrategy,
this.crossOriginStrategy,
this.contentSecurityStrategy,
),
fromLazyLoad<E>(this.createElement(), this.domStrategy, this.crossOriginStrategy),
),
);
}
}
export class ScriptLoadingStrategy extends LoadingStrategy<HTMLScriptElement> {
constructor(
src: string,
domStrategy?: DomStrategy,
crossOriginStrategy?: CrossOriginStrategy,
contentSecurityStrategy?: ContentSecurityStrategy,
) {
super(src, domStrategy, crossOriginStrategy, contentSecurityStrategy);
constructor(src: string, domStrategy?: DomStrategy, crossOriginStrategy?: CrossOriginStrategy) {
super(src, domStrategy, crossOriginStrategy);
}
createElement(): HTMLScriptElement {
@ -48,13 +36,8 @@ export class ScriptLoadingStrategy extends LoadingStrategy<HTMLScriptElement> {
}
export class StyleLoadingStrategy extends LoadingStrategy<HTMLLinkElement> {
constructor(
href: string,
domStrategy?: DomStrategy,
crossOriginStrategy?: CrossOriginStrategy,
contentSecurityStrategy?: ContentSecurityStrategy,
) {
super(href, domStrategy, crossOriginStrategy, contentSecurityStrategy);
constructor(href: string, domStrategy?: DomStrategy, crossOriginStrategy?: CrossOriginStrategy) {
super(href, domStrategy, crossOriginStrategy);
}
createElement(): HTMLLinkElement {

@ -1,9 +1,4 @@
import {
ContentSecurityStrategy,
CONTENT_SECURITY_STRATEGY,
DomStrategy,
DOM_STRATEGY,
} from '../strategies';
import { DomStrategy, DOM_STRATEGY } from '../strategies';
import { CrossOriginStrategy, CROSS_ORIGIN_STRATEGY } from '../strategies/cross-origin.strategy';
import { uuid } from '../utils';
import { fromLazyLoad } from '../utils/lazy-load-utils';
@ -57,24 +52,6 @@ describe('Lazy Load Utils', () => {
expect(element.getAttribute('integrity')).toBe(integrity);
});
it('should not set nonce by default', () => {
const element = document.createElement('link');
fromLazyLoad(element);
expect(element.getAttribute('nonce')).toBeNull();
});
it('should allow setting a content security strategy', () => {
const element = document.createElement('link');
const nonce = uuid();
fromLazyLoad(element, undefined, undefined, CONTENT_SECURITY_STRATEGY.Strict(nonce));
expect(element.getAttribute('nonce')).toBe(nonce);
});
it('should emit error event on fail and clear callbacks', done => {
const error = new CustomEvent('error');
const parentNode = { removeChild: jest.fn() };
@ -94,9 +71,6 @@ describe('Lazy Load Utils', () => {
{
setCrossOrigin(_: HTMLLinkElement) {},
} as CrossOriginStrategy,
{
applyCSP(_: HTMLLinkElement) {},
} as ContentSecurityStrategy,
).subscribe({
error: value => {
expect(value).toBe(error);
@ -126,9 +100,6 @@ describe('Lazy Load Utils', () => {
{
setCrossOrigin(_: HTMLLinkElement) {},
} as CrossOriginStrategy,
{
applyCSP(_: HTMLLinkElement) {},
} as ContentSecurityStrategy,
).subscribe({
next: value => {
expect(value).toBe(success);

@ -1,15 +1,12 @@
import {
CONTENT_SECURITY_STRATEGY,
CROSS_ORIGIN_STRATEGY,
DOM_STRATEGY,
LOADING_STRATEGY,
ScriptLoadingStrategy,
StyleLoadingStrategy,
} from '../strategies';
import { uuid } from '../utils';
const path = 'http://example.com/';
const nonce = uuid();
describe('ScriptLoadingStrategy', () => {
describe('#createElement', () => {
@ -26,7 +23,6 @@ describe('ScriptLoadingStrategy', () => {
it('should use given dom and cross-origin strategies', done => {
const domStrategy = DOM_STRATEGY.PrependToHead();
const crossOriginStrategy = CROSS_ORIGIN_STRATEGY.UseCredentials();
const contentSecurityStrategy = CONTENT_SECURITY_STRATEGY.Strict(nonce);
domStrategy.insertElement = jest.fn((el: HTMLScriptElement) => {
setTimeout(() => {
@ -34,23 +30,16 @@ describe('ScriptLoadingStrategy', () => {
new CustomEvent('success', {
detail: {
crossOrigin: el.crossOrigin,
nonce: el.getAttribute('nonce'),
},
}),
);
}, 0);
}) as any;
const strategy = new ScriptLoadingStrategy(
path,
domStrategy,
crossOriginStrategy,
contentSecurityStrategy,
);
const strategy = new ScriptLoadingStrategy(path, domStrategy, crossOriginStrategy);
strategy.createStream<CustomEvent>().subscribe(event => {
expect(event.detail.crossOrigin).toBe('use-credentials');
expect(event.detail.nonce).toBe(nonce);
done();
});
});
@ -73,7 +62,6 @@ describe('StyleLoadingStrategy', () => {
it('should use given dom and cross-origin strategies', done => {
const domStrategy = DOM_STRATEGY.PrependToHead();
const crossOriginStrategy = CROSS_ORIGIN_STRATEGY.UseCredentials();
const contentSecurityStrategy = CONTENT_SECURITY_STRATEGY.Strict(nonce);
domStrategy.insertElement = jest.fn((el: HTMLLinkElement) => {
setTimeout(() => {
@ -81,23 +69,16 @@ describe('StyleLoadingStrategy', () => {
new CustomEvent('success', {
detail: {
crossOrigin: el.crossOrigin,
nonce: el.getAttribute('nonce'),
},
}),
);
}, 0);
}) as any;
const strategy = new StyleLoadingStrategy(
path,
domStrategy,
crossOriginStrategy,
contentSecurityStrategy,
);
const strategy = new StyleLoadingStrategy(path, domStrategy, crossOriginStrategy);
strategy.createStream<CustomEvent>().subscribe(event => {
expect(event.detail.crossOrigin).toBe('use-credentials');
expect(event.detail.nonce).toBe(nonce);
done();
});
});

@ -1,8 +1,4 @@
import { Observable, Observer } from 'rxjs';
import {
ContentSecurityStrategy,
CONTENT_SECURITY_STRATEGY,
} from '../strategies/content-security.strategy';
import { CrossOriginStrategy, CROSS_ORIGIN_STRATEGY } from '../strategies/cross-origin.strategy';
import { DomStrategy, DOM_STRATEGY } from '../strategies/dom.strategy';
@ -10,10 +6,8 @@ export function fromLazyLoad<T extends Event>(
element: HTMLScriptElement | HTMLLinkElement,
domStrategy: DomStrategy = DOM_STRATEGY.AppendToHead(),
crossOriginStrategy: CrossOriginStrategy = CROSS_ORIGIN_STRATEGY.Anonymous(),
contentSecurityStrategy: ContentSecurityStrategy = CONTENT_SECURITY_STRATEGY.Loose(),
): Observable<T> {
crossOriginStrategy.setCrossOrigin(element);
contentSecurityStrategy.applyCSP(element);
domStrategy.insertElement(element);
return new Observable((observer: Observer<T>) => {

Loading…
Cancel
Save