Component defs are not considered public API, so the property
that contains them should be prefixed with Angular's marker
for "private" ('ɵ') to discourage apps from relying on def
APIs directly.
This commit adds the prefix and shortens the name from
`ngComponentDef` to `cmp`. This is because property names
cannot be minified by Uglify without turning on property
mangling (which most apps have turned off) and are thus
size-sensitive.
Note that the other "defs" (ngDirectiveDef, etc) will be
prefixed and shortened in follow-up PRs, in an attempt to
limit how large and conflict-y this change is.
PR Close #33088
132 lines
4.8 KiB
TypeScript
132 lines
4.8 KiB
TypeScript
/**
|
|
* @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 {Type} from '../interface/type';
|
|
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 MyComponent{
|
|
* }
|
|
*
|
|
* // Calling `renderComponent` will fail because `renderComponent` is a synchronous process
|
|
* // and `MyComponent`'s `@Component.templateUrl` needs to be resolved asynchronously.
|
|
*
|
|
* // Calling `resolveComponentResources()` will resolve `@Component.templateUrl` into
|
|
* // `@Component.template`, which allows `renderComponent` to proceed in a synchronous manner.
|
|
*
|
|
* // Use browser's `fetch()` function as the default resource resolution strategy.
|
|
* resolveComponentResources(fetch).then(() => {
|
|
* // After resolution all URLs have been converted into `template` 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 for returning a `Promise` to the
|
|
* contents of the resolved URL. Browser's `fetch()` method is a good default implementation.
|
|
*/
|
|
export function resolveComponentResources(
|
|
resourceResolver: (url: string) => (Promise<string|{text(): Promise<string>}>)): Promise<void> {
|
|
// Store all promises which are fetching the resources.
|
|
const componentResolved: Promise<void>[] = [];
|
|
|
|
// Cache so that we don't fetch the same resource more than once.
|
|
const urlMap = new Map<string, Promise<string>>();
|
|
function cachedResourceResolve(url: string): Promise<string> {
|
|
let promise = urlMap.get(url);
|
|
if (!promise) {
|
|
const resp = resourceResolver(url);
|
|
urlMap.set(url, promise = resp.then(unwrapResponse));
|
|
}
|
|
return promise;
|
|
}
|
|
|
|
componentResourceResolutionQueue.forEach((component: Component, type: Type<any>) => {
|
|
const promises: Promise<void>[] = [];
|
|
if (component.templateUrl) {
|
|
promises.push(cachedResourceResolve(component.templateUrl).then((template) => {
|
|
component.template = template;
|
|
}));
|
|
}
|
|
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.
|
|
promises.push(cachedResourceResolve(styleUrl).then((style) => {
|
|
styles[styleOffset + index] = style;
|
|
styleUrls.splice(styleUrls.indexOf(styleUrl), 1);
|
|
if (styleUrls.length == 0) {
|
|
component.styleUrls = undefined;
|
|
}
|
|
}));
|
|
});
|
|
const fullyResolved = Promise.all(promises).then(() => componentDefResolved(type));
|
|
componentResolved.push(fullyResolved);
|
|
});
|
|
clearResolutionOfComponentResourcesQueue();
|
|
return Promise.all(componentResolved).then(() => undefined);
|
|
}
|
|
|
|
let componentResourceResolutionQueue = new Map<Type<any>, Component>();
|
|
|
|
// Track when existing ɵcmp for a Type is waiting on resources.
|
|
const componentDefPendingResolution = new Set<Type<any>>();
|
|
|
|
export function maybeQueueResolutionOfComponentResources(type: Type<any>, metadata: Component) {
|
|
if (componentNeedsResolution(metadata)) {
|
|
componentResourceResolutionQueue.set(type, metadata);
|
|
componentDefPendingResolution.add(type);
|
|
}
|
|
}
|
|
|
|
export function isComponentDefPendingResolution(type: Type<any>): boolean {
|
|
return componentDefPendingResolution.has(type);
|
|
}
|
|
|
|
export function componentNeedsResolution(component: Component): boolean {
|
|
return !!(
|
|
(component.templateUrl && !component.hasOwnProperty('template')) ||
|
|
component.styleUrls && component.styleUrls.length);
|
|
}
|
|
export function clearResolutionOfComponentResourcesQueue(): Map<Type<any>, Component> {
|
|
const old = componentResourceResolutionQueue;
|
|
componentResourceResolutionQueue = new Map();
|
|
return old;
|
|
}
|
|
|
|
export function restoreComponentResolutionQueue(queue: Map<Type<any>, Component>): void {
|
|
componentDefPendingResolution.clear();
|
|
queue.forEach((_, type) => componentDefPendingResolution.add(type));
|
|
componentResourceResolutionQueue = queue;
|
|
}
|
|
|
|
export function isComponentResourceResolutionQueueEmpty() {
|
|
return componentResourceResolutionQueue.size === 0;
|
|
}
|
|
|
|
function unwrapResponse(response: string | {text(): Promise<string>}): string|Promise<string> {
|
|
return typeof response == 'string' ? response : response.text();
|
|
}
|
|
|
|
function componentDefResolved(type: Type<any>): void {
|
|
componentDefPendingResolution.delete(type);
|
|
}
|