feat(compiler-cli): support for partial compilation of components (#39707)
This commit implements partial compilation of components, together with linking the partial declaration into its full AOT output. This commit does not yet enable accurate source maps into external templates. This requires additional work to account for escape sequences which is non-trivial. Inline templates that were represented using a string or template literal are transplated into the partial declaration output, so their source maps should be accurate. Note, however, that the accuracy of source maps is not currently verified in tests; this is also left as future work. The golden files of partial compilation output have been updated to reflect the generated code for components. Please note that the current output should not yet be considered stable. PR Close #39707
This commit is contained in:
@@ -18,7 +18,7 @@ export const NO_STATEMENTS: Readonly<any[]> = [] as const;
|
||||
* This class is responsible for linking all the partial declarations found in a single file.
|
||||
*/
|
||||
export class FileLinker<TConstantScope, TStatement, TExpression> {
|
||||
private linkerSelector = new PartialLinkerSelector<TExpression>();
|
||||
private linkerSelector = new PartialLinkerSelector<TExpression>(this.linkerEnvironment.options);
|
||||
private emitScopes = new Map<TConstantScope, EmitScope<TStatement, TExpression>>();
|
||||
|
||||
constructor(
|
||||
|
||||
+165
-3
@@ -5,20 +5,182 @@
|
||||
* 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 {ConstantPool} from '@angular/compiler';
|
||||
import {compileComponentFromMetadata, ConstantPool, DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig, makeBindingParser, parseTemplate, R3ComponentMetadata, R3UsedDirectiveMetadata} from '@angular/compiler';
|
||||
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/compiler/src/core';
|
||||
import * as o from '@angular/compiler/src/output/output_ast';
|
||||
|
||||
import {AstObject} from '../../ast/ast_value';
|
||||
import {Range} from '../../ast/ast_host';
|
||||
import {AstObject, AstValue} from '../../ast/ast_value';
|
||||
import {FatalLinkerError} from '../../fatal_linker_error';
|
||||
import {LinkerOptions} from '../linker_options';
|
||||
|
||||
import {toR3DirectiveMeta} from './partial_directive_linker_1';
|
||||
import {PartialLinker} from './partial_linker';
|
||||
|
||||
/**
|
||||
* A `PartialLinker` that is designed to process `ɵɵngDeclareComponent()` call expressions.
|
||||
*/
|
||||
export class PartialComponentLinkerVersion1<TExpression> implements PartialLinker<TExpression> {
|
||||
constructor(private readonly options: LinkerOptions) {}
|
||||
|
||||
linkPartialDeclaration(
|
||||
sourceUrl: string, code: string, constantPool: ConstantPool,
|
||||
metaObj: AstObject<TExpression>): o.Expression {
|
||||
throw new Error('Not implemented.');
|
||||
const meta = toR3ComponentMeta(metaObj, code, sourceUrl, this.options);
|
||||
const def = compileComponentFromMetadata(meta, constantPool, makeBindingParser());
|
||||
return def.expression;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function derives the `R3ComponentMetadata` from the provided AST object.
|
||||
*/
|
||||
export function toR3ComponentMeta<TExpression>(
|
||||
metaObj: AstObject<TExpression>, code: string, sourceUrl: string,
|
||||
options: LinkerOptions): R3ComponentMetadata {
|
||||
let interpolation = DEFAULT_INTERPOLATION_CONFIG;
|
||||
if (metaObj.has('interpolation')) {
|
||||
interpolation = InterpolationConfig.fromArray(
|
||||
metaObj.getArray('interpolation').map(entry => entry.getString()) as [string, string]);
|
||||
}
|
||||
const templateObj = metaObj.getObject('template');
|
||||
const templateSource = templateObj.getValue('source');
|
||||
const range = getTemplateRange(templateSource, code);
|
||||
const isInline = templateObj.getBoolean('isInline');
|
||||
|
||||
// We always normalize line endings if the template is inline.
|
||||
const i18nNormalizeLineEndingsInICUs = isInline || options.i18nNormalizeLineEndingsInICUs;
|
||||
|
||||
const template = parseTemplate(code, sourceUrl, {
|
||||
escapedString: true,
|
||||
interpolationConfig: interpolation,
|
||||
range,
|
||||
enableI18nLegacyMessageIdFormat: options.enableI18nLegacyMessageIdFormat,
|
||||
preserveWhitespaces:
|
||||
metaObj.has('preserveWhitespaces') ? metaObj.getBoolean('preserveWhitespaces') : false,
|
||||
i18nNormalizeLineEndingsInICUs,
|
||||
isInline,
|
||||
});
|
||||
if (template.errors !== null) {
|
||||
const errors = template.errors.map(err => err.toString()).join('\n');
|
||||
throw new FatalLinkerError(
|
||||
templateSource.expression, `Errors found in the template:\n${errors}`);
|
||||
}
|
||||
|
||||
let wrapDirectivesAndPipesInClosure = false;
|
||||
|
||||
const directives: R3UsedDirectiveMetadata[] = metaObj.has('directives') ?
|
||||
metaObj.getArray('directives').map(directive => {
|
||||
const directiveExpr = directive.getObject();
|
||||
const type = directiveExpr.getValue('type');
|
||||
const selector = directiveExpr.getString('selector');
|
||||
|
||||
let typeExpr = type.getOpaque();
|
||||
if (type.isFunction()) {
|
||||
typeExpr = type.getFunctionReturnValue().getOpaque();
|
||||
wrapDirectivesAndPipesInClosure = true;
|
||||
}
|
||||
return {
|
||||
type: typeExpr,
|
||||
selector: selector,
|
||||
inputs: directiveExpr.has('inputs') ?
|
||||
directiveExpr.getArray('inputs').map(input => input.getString()) :
|
||||
[],
|
||||
outputs: directiveExpr.has('outputs') ?
|
||||
directiveExpr.getArray('outputs').map(input => input.getString()) :
|
||||
[],
|
||||
exportAs: directiveExpr.has('exportAs') ?
|
||||
directiveExpr.getArray('exportAs').map(exportAs => exportAs.getString()) :
|
||||
null,
|
||||
};
|
||||
}) :
|
||||
[];
|
||||
|
||||
const pipes = metaObj.has('pipes') ? metaObj.getObject('pipes').toMap(value => {
|
||||
if (value.isFunction()) {
|
||||
wrapDirectivesAndPipesInClosure = true;
|
||||
return value.getFunctionReturnValue().getOpaque();
|
||||
} else {
|
||||
return value.getOpaque();
|
||||
}
|
||||
}) :
|
||||
new Map<string, o.Expression>();
|
||||
|
||||
return {
|
||||
...toR3DirectiveMeta(metaObj, code, sourceUrl),
|
||||
viewProviders: metaObj.has('viewProviders') ? metaObj.getOpaque('viewProviders') : null,
|
||||
template: {
|
||||
nodes: template.nodes,
|
||||
ngContentSelectors: template.ngContentSelectors,
|
||||
},
|
||||
wrapDirectivesAndPipesInClosure,
|
||||
styles: metaObj.has('styles') ? metaObj.getArray('styles').map(entry => entry.getString()) : [],
|
||||
encapsulation: metaObj.has('encapsulation') ?
|
||||
parseEncapsulation(metaObj.getValue('encapsulation')) :
|
||||
ViewEncapsulation.Emulated,
|
||||
interpolation,
|
||||
changeDetection: metaObj.has('changeDetection') ?
|
||||
parseChangeDetectionStrategy(metaObj.getValue('changeDetection')) :
|
||||
ChangeDetectionStrategy.Default,
|
||||
animations: metaObj.has('animations') ? metaObj.getOpaque('animations') : null,
|
||||
relativeContextFilePath: sourceUrl,
|
||||
i18nUseExternalIds: options.i18nUseExternalIds,
|
||||
pipes,
|
||||
directives,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the `ViewEncapsulation` mode from the AST value's symbol name.
|
||||
*/
|
||||
function parseEncapsulation<TExpression>(encapsulation: AstValue<TExpression>): ViewEncapsulation {
|
||||
const symbolName = encapsulation.getSymbolName();
|
||||
if (symbolName === null) {
|
||||
throw new FatalLinkerError(
|
||||
encapsulation.expression, 'Expected encapsulation to have a symbol name');
|
||||
}
|
||||
const enumValue = ViewEncapsulation[symbolName as keyof typeof ViewEncapsulation];
|
||||
if (enumValue === undefined) {
|
||||
throw new FatalLinkerError(encapsulation.expression, 'Unsupported encapsulation');
|
||||
}
|
||||
return enumValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the `ChangeDetectionStrategy` from the AST value's symbol name.
|
||||
*/
|
||||
function parseChangeDetectionStrategy<TExpression>(changeDetectionStrategy: AstValue<TExpression>):
|
||||
ChangeDetectionStrategy {
|
||||
const symbolName = changeDetectionStrategy.getSymbolName();
|
||||
if (symbolName === null) {
|
||||
throw new FatalLinkerError(
|
||||
changeDetectionStrategy.expression,
|
||||
'Expected change detection strategy to have a symbol name');
|
||||
}
|
||||
const enumValue = ChangeDetectionStrategy[symbolName as keyof typeof ChangeDetectionStrategy];
|
||||
if (enumValue === undefined) {
|
||||
throw new FatalLinkerError(
|
||||
changeDetectionStrategy.expression, 'Unsupported change detection strategy');
|
||||
}
|
||||
return enumValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the range to remove the start and end chars, which should be quotes around the template.
|
||||
*/
|
||||
function getTemplateRange<TExpression>(templateNode: AstValue<TExpression>, code: string): Range {
|
||||
const {startPos, endPos, startLine, startCol} = templateNode.getRange();
|
||||
|
||||
if (!/["'`]/.test(code[startPos]) || code[startPos] !== code[endPos - 1]) {
|
||||
throw new FatalLinkerError(
|
||||
templateNode.expression,
|
||||
`Expected the template string to be wrapped in quotes but got: ${
|
||||
code.substring(startPos, endPos)}`);
|
||||
}
|
||||
return {
|
||||
startPos: startPos + 1,
|
||||
endPos: endPos - 1,
|
||||
startLine,
|
||||
startCol: startCol + 1,
|
||||
};
|
||||
}
|
||||
|
||||
+5
-1
@@ -5,6 +5,8 @@
|
||||
* 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 {LinkerOptions} from '../linker_options';
|
||||
|
||||
import {PartialComponentLinkerVersion1} from './partial_component_linker_1';
|
||||
import {PartialDirectiveLinkerVersion1} from './partial_directive_linker_1';
|
||||
import {PartialLinker} from './partial_linker';
|
||||
@@ -15,10 +17,12 @@ export class PartialLinkerSelector<TExpression> {
|
||||
1: new PartialDirectiveLinkerVersion1(),
|
||||
},
|
||||
'ɵɵngDeclareComponent': {
|
||||
1: new PartialComponentLinkerVersion1(),
|
||||
1: new PartialComponentLinkerVersion1(this.options),
|
||||
},
|
||||
};
|
||||
|
||||
constructor(private options: LinkerOptions) {}
|
||||
|
||||
/**
|
||||
* Returns true if there are `PartialLinker` classes that can handle functions with this name.
|
||||
*/
|
||||
|
||||
+10
-3
@@ -6,15 +6,22 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {LinkerOptions} from '../../..';
|
||||
import {PartialComponentLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_component_linker_1';
|
||||
import {PartialDirectiveLinkerVersion1} from '../../../src/file_linker/partial_linkers/partial_directive_linker_1';
|
||||
import {PartialLinkerSelector} from '../../../src/file_linker/partial_linkers/partial_linker_selector';
|
||||
|
||||
describe('PartialLinkerSelector', () => {
|
||||
const options: LinkerOptions = {
|
||||
i18nNormalizeLineEndingsInICUs: true,
|
||||
enableI18nLegacyMessageIdFormat: false,
|
||||
i18nUseExternalIds: false,
|
||||
};
|
||||
|
||||
describe('supportsDeclaration()', () => {
|
||||
it('should return true if there is at least one linker that matches the given function name',
|
||||
() => {
|
||||
const selector = new PartialLinkerSelector();
|
||||
const selector = new PartialLinkerSelector(options);
|
||||
expect(selector.supportsDeclaration('ɵɵngDeclareDirective')).toBe(true);
|
||||
expect(selector.supportsDeclaration('ɵɵngDeclareComponent')).toBe(true);
|
||||
expect(selector.supportsDeclaration('$foo')).toBe(false);
|
||||
@@ -23,7 +30,7 @@ describe('PartialLinkerSelector', () => {
|
||||
|
||||
describe('getLinker()', () => {
|
||||
it('should return the linker that matches the name and version number', () => {
|
||||
const selector = new PartialLinkerSelector();
|
||||
const selector = new PartialLinkerSelector(options);
|
||||
expect(selector.getLinker('ɵɵngDeclareDirective', 1))
|
||||
.toBeInstanceOf(PartialDirectiveLinkerVersion1);
|
||||
expect(selector.getLinker('ɵɵngDeclareComponent', 1))
|
||||
@@ -31,7 +38,7 @@ describe('PartialLinkerSelector', () => {
|
||||
});
|
||||
|
||||
it('should throw an error if there is no linker that matches the given name or version', () => {
|
||||
const selector = new PartialLinkerSelector();
|
||||
const selector = new PartialLinkerSelector(options);
|
||||
expect(() => selector.getLinker('$foo', 1))
|
||||
.toThrowError('Unknown partial declaration function $foo.');
|
||||
expect(() => selector.getLinker('ɵɵngDeclareDirective', 2))
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {compileComponentFromMetadata, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, makeBindingParser, ParsedTemplate, ParseSourceFile, parseTemplate, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr} from '@angular/compiler';
|
||||
import {compileComponentFromMetadata, compileDeclareComponentFromMetadata, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, makeBindingParser, ParsedTemplate, ParseSourceFile, parseTemplate, R3ComponentDef, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, R3UsedDirectiveMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CycleAnalyzer} from '../../cycles';
|
||||
@@ -506,11 +506,15 @@ export class ComponentDecoratorHandler implements
|
||||
const bound = binder.bind({template: metadata.template.nodes});
|
||||
|
||||
// The BoundTarget knows which directives and pipes matched the template.
|
||||
const usedDirectives = bound.getUsedDirectives().map(directive => {
|
||||
type UsedDirective = R3UsedDirectiveMetadata&{ref: Reference};
|
||||
const usedDirectives: UsedDirective[] = bound.getUsedDirectives().map(directive => {
|
||||
return {
|
||||
selector: directive.selector,
|
||||
expression: this.refEmitter.emit(directive.ref, context),
|
||||
ref: directive.ref,
|
||||
type: this.refEmitter.emit(directive.ref, context),
|
||||
selector: directive.selector,
|
||||
inputs: directive.inputs.propertyNames,
|
||||
outputs: directive.outputs.propertyNames,
|
||||
exportAs: directive.exportAs,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -529,15 +533,14 @@ export class ComponentDecoratorHandler implements
|
||||
|
||||
// Scan through the directives/pipes actually used in the template and check whether any
|
||||
// import which needs to be generated would create a cycle.
|
||||
const cycleDetected =
|
||||
usedDirectives.some(dir => this._isCyclicImport(dir.expression, context)) ||
|
||||
const cycleDetected = usedDirectives.some(dir => this._isCyclicImport(dir.type, context)) ||
|
||||
usedPipes.some(pipe => this._isCyclicImport(pipe.expression, context));
|
||||
|
||||
if (!cycleDetected) {
|
||||
// No cycle was detected. Record the imports that need to be created in the cycle detector
|
||||
// so that future cyclic import checks consider their production.
|
||||
for (const {expression} of usedDirectives) {
|
||||
this._recordSyntheticImport(expression, context);
|
||||
for (const {type} of usedDirectives) {
|
||||
this._recordSyntheticImport(type, context);
|
||||
}
|
||||
for (const {expression} of usedPipes) {
|
||||
this._recordSyntheticImport(expression, context);
|
||||
@@ -548,7 +551,7 @@ export class ComponentDecoratorHandler implements
|
||||
// declared after this component.
|
||||
const wrapDirectivesAndPipesInClosure =
|
||||
usedDirectives.some(
|
||||
dir => isExpressionForwardReference(dir.expression, node.name, context)) ||
|
||||
dir => isExpressionForwardReference(dir.type, node.name, context)) ||
|
||||
usedPipes.some(
|
||||
pipe => isExpressionForwardReference(pipe.expression, node.name, context));
|
||||
|
||||
@@ -599,18 +602,35 @@ export class ComponentDecoratorHandler implements
|
||||
node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>,
|
||||
resolution: Readonly<ComponentResolutionData>, pool: ConstantPool): CompileResult[] {
|
||||
const meta: R3ComponentMetadata = {...analysis.meta, ...resolution};
|
||||
const res = compileComponentFromMetadata(meta, pool, makeBindingParser());
|
||||
const factoryRes = compileNgFactoryDefField(
|
||||
{...meta, injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Component});
|
||||
const def = compileComponentFromMetadata(meta, pool, makeBindingParser());
|
||||
return this.compileComponent(analysis, def);
|
||||
}
|
||||
|
||||
compilePartial(
|
||||
node: ClassDeclaration, analysis: Readonly<ComponentAnalysisData>,
|
||||
resolution: Readonly<ComponentResolutionData>): CompileResult[] {
|
||||
const meta: R3ComponentMetadata = {...analysis.meta, ...resolution};
|
||||
const def = compileDeclareComponentFromMetadata(meta, analysis.template);
|
||||
return this.compileComponent(analysis, def);
|
||||
}
|
||||
|
||||
private compileComponent(
|
||||
analysis: Readonly<ComponentAnalysisData>,
|
||||
{expression: initializer, type}: R3ComponentDef): CompileResult[] {
|
||||
const factoryRes = compileNgFactoryDefField({
|
||||
...analysis.meta,
|
||||
injectFn: Identifiers.directiveInject,
|
||||
target: R3FactoryTarget.Component,
|
||||
});
|
||||
if (analysis.metadataStmt !== null) {
|
||||
factoryRes.statements.push(analysis.metadataStmt);
|
||||
}
|
||||
return [
|
||||
factoryRes, {
|
||||
name: 'ɵcmp',
|
||||
initializer: res.expression,
|
||||
initializer,
|
||||
statements: [],
|
||||
type: res.type,
|
||||
type,
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -737,7 +757,8 @@ export class ComponentDecoratorHandler implements
|
||||
}
|
||||
|
||||
const template = this._parseTemplate(
|
||||
component, templateStr, sourceMapUrl(resourceUrl), /* templateRange */ undefined,
|
||||
component, templateStr, /* templateLiteral */ null, sourceMapUrl(resourceUrl),
|
||||
/* templateRange */ undefined,
|
||||
/* escapedString */ false);
|
||||
|
||||
return {
|
||||
@@ -763,6 +784,7 @@ export class ComponentDecoratorHandler implements
|
||||
const templateExpr = component.get('template')!;
|
||||
|
||||
let templateStr: string;
|
||||
let templateLiteral: ts.Node|null = null;
|
||||
let templateUrl: string = '';
|
||||
let templateRange: LexerRange|undefined = undefined;
|
||||
let sourceMapping: TemplateSourceMapping;
|
||||
@@ -774,6 +796,7 @@ export class ComponentDecoratorHandler implements
|
||||
// strip
|
||||
templateRange = getTemplateRange(templateExpr);
|
||||
templateStr = templateExpr.getSourceFile().text;
|
||||
templateLiteral = templateExpr;
|
||||
templateUrl = containingFile;
|
||||
escapedString = true;
|
||||
sourceMapping = {
|
||||
@@ -795,15 +818,16 @@ export class ComponentDecoratorHandler implements
|
||||
};
|
||||
}
|
||||
|
||||
const template =
|
||||
this._parseTemplate(component, templateStr, templateUrl, templateRange, escapedString);
|
||||
const template = this._parseTemplate(
|
||||
component, templateStr, templateLiteral, templateUrl, templateRange, escapedString);
|
||||
|
||||
return {...template, sourceMapping};
|
||||
}
|
||||
|
||||
private _parseTemplate(
|
||||
component: Map<string, ts.Expression>, templateStr: string, templateUrl: string,
|
||||
templateRange: LexerRange|undefined, escapedString: boolean): ParsedComponentTemplate {
|
||||
component: Map<string, ts.Expression>, templateStr: string, templateLiteral: ts.Node|null,
|
||||
templateUrl: string, templateRange: LexerRange|undefined,
|
||||
escapedString: boolean): ParsedComponentTemplate {
|
||||
let preserveWhitespaces: boolean = this.defaultPreserveWhitespaces;
|
||||
if (component.has('preserveWhitespaces')) {
|
||||
const expr = component.get('preserveWhitespaces')!;
|
||||
@@ -829,6 +853,7 @@ export class ComponentDecoratorHandler implements
|
||||
// We always normalize line endings if the template has been escaped (i.e. is inline).
|
||||
const i18nNormalizeLineEndingsInICUs = escapedString || this.i18nNormalizeLineEndingsInICUs;
|
||||
|
||||
const isInline = component.has('template');
|
||||
const parsedTemplate = parseTemplate(templateStr, templateUrl, {
|
||||
preserveWhitespaces,
|
||||
interpolationConfig,
|
||||
@@ -836,6 +861,7 @@ export class ComponentDecoratorHandler implements
|
||||
escapedString,
|
||||
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
|
||||
i18nNormalizeLineEndingsInICUs,
|
||||
isInline,
|
||||
});
|
||||
|
||||
// Unfortunately, the primary parse of the template above may not contain accurate source map
|
||||
@@ -859,14 +885,15 @@ export class ComponentDecoratorHandler implements
|
||||
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
|
||||
i18nNormalizeLineEndingsInICUs,
|
||||
leadingTriviaChars: [],
|
||||
isInline,
|
||||
});
|
||||
|
||||
return {
|
||||
...parsedTemplate,
|
||||
diagNodes,
|
||||
template: templateStr,
|
||||
template: templateLiteral !== null ? new WrappedNodeExpr(templateLiteral) : templateStr,
|
||||
templateUrl,
|
||||
isInline: component.has('template'),
|
||||
isInline,
|
||||
file: new ParseSourceFile(templateStr, templateUrl),
|
||||
};
|
||||
}
|
||||
|
||||
+19
-84
@@ -6,18 +6,7 @@ import * as i0 from "@angular/core";
|
||||
export class MyComponent {
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 5, vars: 0, consts: [["title", "Hello", 1, "my-app"], ["cx", "20", "cy", "30", "r", "50"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelementStart(0, "div", 0);
|
||||
i0.ɵɵnamespaceSVG();
|
||||
i0.ɵɵelementStart(1, "svg");
|
||||
i0.ɵɵelement(2, "circle", 1);
|
||||
i0.ɵɵelementEnd();
|
||||
i0.ɵɵnamespaceHTML();
|
||||
i0.ɵɵelementStart(3, "p");
|
||||
i0.ɵɵtext(4, "test");
|
||||
i0.ɵɵelementEnd();
|
||||
i0.ɵɵelementEnd();
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div class="my-app" title="Hello"><svg><circle cx="20" cy="30" r="50"/></svg><p>test</p></div>', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
@@ -56,18 +45,7 @@ import * as i0 from "@angular/core";
|
||||
export class MyComponent {
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 5, vars: 0, consts: [["title", "Hello", 1, "my-app"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelementStart(0, "div", 0);
|
||||
i0.ɵɵnamespaceMathML();
|
||||
i0.ɵɵelementStart(1, "math");
|
||||
i0.ɵɵelement(2, "infinity");
|
||||
i0.ɵɵelementEnd();
|
||||
i0.ɵɵnamespaceHTML();
|
||||
i0.ɵɵelementStart(3, "p");
|
||||
i0.ɵɵtext(4, "test");
|
||||
i0.ɵɵelementEnd();
|
||||
i0.ɵɵelementEnd();
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div class="my-app" title="Hello"><math><infinity/></math><p>test</p></div>', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
@@ -106,15 +84,7 @@ import * as i0 from "@angular/core";
|
||||
export class MyComponent {
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 5, vars: 0, consts: [["title", "Hello", 1, "my-app"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelementStart(0, "div", 0);
|
||||
i0.ɵɵtext(1, "Hello ");
|
||||
i0.ɵɵelementStart(2, "b");
|
||||
i0.ɵɵtext(3, "World");
|
||||
i0.ɵɵelementEnd();
|
||||
i0.ɵɵtext(4, "!");
|
||||
i0.ɵɵelementEnd();
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div class="my-app" title="Hello">Hello <b>World</b>!</div>', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
@@ -153,15 +123,7 @@ import * as i0 from "@angular/core";
|
||||
export class MyComponent {
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 5, vars: 0, consts: [[0, "xmlns", "foo", "http://someuri/foo", 0, "foo", "bar", "baz", "title", "Hello", 0, "foo", "qux", "quacks", 1, "my-app"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelementStart(0, "div", 0);
|
||||
i0.ɵɵtext(1, "Hello ");
|
||||
i0.ɵɵelementStart(2, "b");
|
||||
i0.ɵɵtext(3, "World");
|
||||
i0.ɵɵelementEnd();
|
||||
i0.ɵɵtext(4, "!");
|
||||
i0.ɵɵelementEnd();
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div xmlns:foo="http://someuri/foo" class="my-app" foo:bar="baz" title="Hello" foo:qux="quacks">Hello <b>World</b>!</div>', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
@@ -200,14 +162,7 @@ import * as i0 from "@angular/core";
|
||||
export class MyComponent {
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 4, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelementContainerStart(0);
|
||||
i0.ɵɵelementStart(1, "span");
|
||||
i0.ɵɵtext(2, "in a ");
|
||||
i0.ɵɵelementEnd();
|
||||
i0.ɵɵtext(3, "container");
|
||||
i0.ɵɵelementContainerEnd();
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<ng-container><span>in a </span>container</ng-container>', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
@@ -246,9 +201,7 @@ import * as i0 from "@angular/core";
|
||||
export class MyComponent {
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelementContainer(0);
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<ng-container></ng-container>', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{ selector: 'my-component', template: '<ng-container></ng-container>' }]
|
||||
@@ -287,11 +240,7 @@ export class MyComponent {
|
||||
}
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, vars: 1, consts: [[3, "id"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelement(0, "div", 0);
|
||||
} if (rf & 2) {
|
||||
i0.ɵɵproperty("id", ctx.id);
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div [id]="id"></div>', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{ selector: 'my-component', template: '<div [id]="id"></div>' }]
|
||||
@@ -325,20 +274,18 @@ export declare class MyModule {
|
||||
****************************************************************************************************/
|
||||
import { Component, NgModule } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
const _c0 = function (a0) { return [a0]; };
|
||||
const _c1 = function () { return [0]; };
|
||||
export class MyComponent {
|
||||
constructor() {
|
||||
this.id = 'one';
|
||||
}
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 2, vars: 15, consts: [[3, "ternary", "pipe", "and", "or"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelement(0, "div", 0);
|
||||
i0.ɵɵpipe(1, "pipe");
|
||||
} if (rf & 2) {
|
||||
i0.ɵɵproperty("ternary", ctx.cond ? i0.ɵɵpureFunction1(8, _c0, ctx.a) : i0.ɵɵpureFunction0(10, _c1))("pipe", i0.ɵɵpipeBind3(1, 4, ctx.value, 1, 2))("and", ctx.cond && i0.ɵɵpureFunction1(11, _c0, ctx.b))("or", ctx.cond || i0.ɵɵpureFunction1(13, _c0, ctx.c));
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: `<div
|
||||
[ternary]="cond ? [a] : [0]"
|
||||
[pipe]="value | pipe:1:2"
|
||||
[and]="cond && [b]"
|
||||
[or]="cond || [c]"
|
||||
></div>`, isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
@@ -380,20 +327,13 @@ export declare class MyModule {
|
||||
****************************************************************************************************/
|
||||
import { Component, Input, NgModule } from '@angular/core';
|
||||
import * as i0 from "@angular/core";
|
||||
const _c0 = function (a0, a1) { return { collapsedHeight: a0, expandedHeight: a1 }; };
|
||||
const _c1 = function (a0, a1) { return { value: a0, params: a1 }; };
|
||||
const _c2 = function (a0, a1) { return { collapsedWidth: a0, expandedWidth: a1 }; };
|
||||
export class MyComponent {
|
||||
getExpandedState() {
|
||||
return 'expanded';
|
||||
}
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], hostVars: 14, hostBindings: function MyComponent_HostBindings(rf, ctx) { if (rf & 2) {
|
||||
i0.ɵɵsyntheticHostProperty("@expansionHeight", i0.ɵɵpureFunction2(5, _c1, ctx.getExpandedState(), i0.ɵɵpureFunction2(2, _c0, ctx.collapsedHeight, ctx.expandedHeight)))("@expansionWidth", i0.ɵɵpureFunction2(11, _c1, ctx.getExpandedState(), i0.ɵɵpureFunction2(8, _c2, ctx.collapsedWidth, ctx.expandedWidth)));
|
||||
} }, inputs: { expandedHeight: "expandedHeight", collapsedHeight: "collapsedHeight", expandedWidth: "expandedWidth", collapsedWidth: "collapsedWidth" }, decls: 1, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵtext(0, "...");
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", inputs: { expandedHeight: "expandedHeight", collapsedHeight: "collapsedHeight", expandedWidth: "expandedWidth", collapsedWidth: "collapsedWidth" }, host: { properties: { "@expansionHeight": "{\n value: getExpandedState(),\n params: {\n collapsedHeight: collapsedHeight,\n expandedHeight: expandedHeight\n }\n }", "@expansionWidth": "{\n value: getExpandedState(),\n params: {\n collapsedWidth: collapsedWidth,\n expandedWidth: expandedWidth\n }\n }" } }, ngImport: i0, template: { source: '...', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
@@ -465,12 +405,7 @@ export class MyComponent {
|
||||
}
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, vars: 4, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelement(0, "div");
|
||||
} if (rf & 2) {
|
||||
i0.ɵɵstyleProp("background-color", ctx.color);
|
||||
i0.ɵɵclassProp("error", ctx.error);
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div [class.error]="error" [style.background-color]="color"></div>', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
@@ -511,10 +446,10 @@ import * as i0 from "@angular/core";
|
||||
export class MyComponent {
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 2, vars: 0, consts: [["title", "hi"]], template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelement(0, "div", 0);
|
||||
i0.ɵɵelement(1, "span", 0);
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: `
|
||||
<div title="hi"></div>
|
||||
<span title="hi"></span>
|
||||
`, isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
|
||||
+1
-5
@@ -9,11 +9,7 @@ export class MyApp {
|
||||
}
|
||||
}
|
||||
MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); };
|
||||
MyApp.ɵcmp = i0.ɵɵdefineComponent({ type: MyApp, selectors: [["my-app"]], decls: 1, vars: 9, template: function MyApp_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵtext(0);
|
||||
} if (rf & 2) {
|
||||
i0.ɵɵtextInterpolateV([" ", ctx.list[0], " ", ctx.list[1], " ", ctx.list[2], " ", ctx.list[3], " ", ctx.list[4], " ", ctx.list[5], " ", ctx.list[6], " ", ctx.list[7], " ", ctx.list[8], " "]);
|
||||
} }, encapsulation: 2 });
|
||||
MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyApp, selector: "my-app", ngImport: i0, template: { source: ' {{list[0]}} {{list[1]}} {{list[2]}} {{list[3]}} {{list[4]}} {{list[5]}} {{list[6]}} {{list[7]}} {{list[8]}} ', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyApp, [{
|
||||
type: Component,
|
||||
args: [{
|
||||
|
||||
+1
-3
@@ -14,9 +14,7 @@ I18nDirective.ɵdir = i0.ɵɵngDeclareDirective({ version: 1, type: I18nDirectiv
|
||||
export class MyComponent {
|
||||
}
|
||||
MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); };
|
||||
MyComponent.ɵcmp = i0.ɵɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], decls: 1, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵɵelement(0, "div");
|
||||
} }, encapsulation: 2 });
|
||||
MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: 1, type: MyComponent, selector: "my-component", ngImport: i0, template: { source: '<div i18n></div>', isInline: true } });
|
||||
/*@__PURE__*/ (function () { i0.ɵsetClassMetadata(MyComponent, [{
|
||||
type: Component,
|
||||
args: [{ selector: 'my-component', template: '<div i18n></div>' }]
|
||||
|
||||
Reference in New Issue
Block a user