/** * @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 {Component} from './directives'; /** * Used to resolve resource URLs on `@Component` when used with JIT compilation. * * Example: * ``` * @Component({ * selector: 'my-comp', * templateUrl: 'my-comp.html', // This requires asynchronous resolution * }) * class MyComponnent{ * } * * // Calling `renderComponent` will fail because `MyComponent`'s `@Compenent.templateUrl` * // needs to be resolved because `renderComponent` is synchronous process. * // renderComponent(MyComponent); * * // Calling `resolveComponentResources` will resolve `@Compenent.templateUrl` into * // `@Compenent.template`, which would allow `renderComponent` to proceed in synchronous manner. * // Use browser's `fetch` function as the default resource resolution strategy. * resolveComponentResources(fetch).then(() => { * // After resolution all URLs have been converted into strings. * renderComponent(MyComponent); * }); * * ``` * * NOTE: In AOT the resolution happens during compilation, and so there should be no need * to call this method outside JIT mode. * * @param resourceResolver a function which is responsible to returning a `Promise` of the resolved * URL. Browser's `fetch` method is a good default implementation. */ export function resolveComponentResources( resourceResolver: (url: string) => (Promise}>)): Promise { // Store all promises which are fetching the resources. const urlFetches: Promise[] = []; // Cache so that we don't fetch the same resource more than once. const urlMap = new Map>(); function cachedResourceResolve(url: string): Promise { let promise = urlMap.get(url); if (!promise) { const resp = resourceResolver(url); urlMap.set(url, promise = resp.then(unwrapResponse)); urlFetches.push(promise); } return promise; } componentResourceResolutionQueue.forEach((component: Component) => { if (component.templateUrl) { cachedResourceResolve(component.templateUrl).then((template) => { component.template = template; component.templateUrl = undefined; }); } const styleUrls = component.styleUrls; const styles = component.styles || (component.styles = []); const styleOffset = component.styles.length; styleUrls && styleUrls.forEach((styleUrl, index) => { styles.push(''); // pre-allocate array. cachedResourceResolve(styleUrl).then((style) => { styles[styleOffset + index] = style; styleUrls.splice(styleUrls.indexOf(styleUrl), 1); if (styleUrls.length == 0) { component.styleUrls = undefined; } }); }); }); componentResourceResolutionQueue.clear(); return Promise.all(urlFetches).then(() => null); } const componentResourceResolutionQueue: Set = new Set(); export function maybeQueueResolutionOfComponentResources(metadata: Component) { if (componentNeedsResolution(metadata)) { componentResourceResolutionQueue.add(metadata); } } export function componentNeedsResolution(component: Component) { return component.templateUrl || component.styleUrls && component.styleUrls.length; } export function clearResolutionOfComponentResourcesQueue() { componentResourceResolutionQueue.clear(); } function unwrapResponse(response: string | {text(): Promise}): string|Promise { return typeof response == 'string' ? response : response.text(); }