feat(ivy): bridge component styles into the component renderer (#25255)

PR Close #25255
This commit is contained in:
Matias Niemelä
2018-07-31 11:14:06 -07:00
parent a59d4da304
commit a37bcc3bfe
26 changed files with 3557 additions and 24 deletions

View File

@ -188,6 +188,7 @@ export class CompileStylesheetMetadata {
export interface CompileTemplateSummary {
ngContentSelectors: string[];
encapsulation: ViewEncapsulation|null;
styles: string[];
}
/**
@ -243,6 +244,7 @@ export class CompileTemplateMetadata {
return {
ngContentSelectors: this.ngContentSelectors,
encapsulation: this.encapsulation,
styles: this.styles
};
}
}

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ViewEncapsulation} from '../../core';
import * as o from '../../output/output_ast';
import {ParseSourceSpan} from '../../parse_util';
import * as t from '../r3_ast';
@ -151,6 +152,22 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
* This is done when the directives contain forward references.
*/
wrapDirectivesInClosure: boolean;
/**
* A collection of styling data that will be applied and scoped to the component.
*/
styles: string[];
/**
* An encapsulation policy for the template and CSS styles. One of:
* - `ViewEncapsulation.Native`: Use shadow roots. This works only if natively available on the
* platform (note that this is marked the as the "deprecated shadow DOM" as of Angular v6.1.
* - `ViewEncapsulation.Emulated`: Use shimmed CSS that emulates the native behavior.
* - `ViewEncapsulation.None`: Use global CSS without any encapsulation.
* - `ViewEncapsulation.ShadowDom`: Use the latest ShadowDOM API to natively encapsulate styles
* into a shadow root.
*/
encapsulation: ViewEncapsulation;
}
/**

View File

@ -16,6 +16,8 @@ import {LifecycleHooks} from '../../lifecycle_reflector';
import * as o from '../../output/output_ast';
import {typeSourceSpan} from '../../parse_util';
import {CssSelector, SelectorMatcher} from '../../selector';
import {ShadowCss} from '../../shadow_css';
import {CONTENT_ATTR, HOST_ATTR} from '../../style_compiler';
import {BindingParser} from '../../template_parser/binding_parser';
import {OutputContext, error} from '../../util';
import {compileFactoryFunction, dependenciesFromGlobalMetadata} from '../r3_factory';
@ -27,6 +29,8 @@ import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata
import {BindingScope, TemplateDefinitionBuilder, renderFlagCheckIfStmt} from './template';
import {CONTEXT_NAME, DefinitionMap, RENDER_FLAGS, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator} from './util';
const EMPTY_ARRAY: any[] = [];
function baseDirectiveFields(
meta: R3DirectiveMetadata, constantPool: ConstantPool,
bindingParser: BindingParser): {definitionMap: DefinitionMap, statements: o.Statement[]} {
@ -218,6 +222,15 @@ export function compileComponentFromMetadata(
definitionMap.set('pipes', o.literalArr(Array.from(pipesUsed)));
}
// e.g. `styles: [str1, str2]`
if (meta.styles && meta.styles.length) {
const styleValues = meta.encapsulation == core.ViewEncapsulation.Emulated ?
compileStyles(meta.styles, CONTENT_ATTR, HOST_ATTR) :
meta.styles;
const strings = styleValues.map(str => o.literal(str));
definitionMap.set('styles', o.literalArr(strings));
}
// On the type side, remove newlines from the selector as it will need to fit into a TypeScript
// string literal, which must be on one line.
const selectorForType = (meta.selector || '').replace(/\n/g, '');
@ -287,6 +300,9 @@ export function compileComponentFromRender2(
pipes: typeMapToExpressionMap(pipeTypeByName, outputCtx),
viewQueries: queriesFromGlobalMetadata(component.viewQueries, outputCtx),
wrapDirectivesInClosure: false,
styles: (summary.template && summary.template.styles) || EMPTY_ARRAY,
encapsulation:
(summary.template && summary.template.encapsulation) || core.ViewEncapsulation.Emulated
};
const res = compileComponentFromMetadata(meta, outputCtx.constantPool, bindingParser);
@ -624,3 +640,8 @@ export function parseHostBindings(host: {[key: string]: string}): {
return {attributes, listeners, properties, animations};
}
function compileStyles(styles: string[], selector: string, hostSelector: string): string[] {
const shadowCss = new ShadowCss();
return styles.map(style => { return shadowCss !.shimCssText(style, selector, hostSelector); });
}

View File

@ -14,8 +14,8 @@ import {UrlResolver} from './url_resolver';
import {OutputContext} from './util';
const COMPONENT_VARIABLE = '%COMP%';
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
export const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
export const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
export class StylesCompileDependency {
constructor(