refactor(ivy): align compiler with runtime (#22921)
Remove `containerRefreshStart` and `containerRefreshEnd` instruction from the output. Generate directives as a list in `componentDef` rather than inline into instructions. This is consistent in making selector resolution runtime so that translation of templates can follow locality. PR Close #22921
This commit is contained in:

committed by
Alex Rickabaugh

parent
5266ffe04a
commit
60065935be
@ -33,10 +33,6 @@ export class Identifiers {
|
||||
|
||||
static containerEnd: o.ExternalReference = {name: 'ɵc', moduleName: CORE};
|
||||
|
||||
static containerRefreshStart: o.ExternalReference = {name: 'ɵcR', moduleName: CORE};
|
||||
|
||||
static containerRefreshEnd: o.ExternalReference = {name: 'ɵcr', moduleName: CORE};
|
||||
|
||||
static directiveCreate: o.ExternalReference = {name: 'ɵD', moduleName: CORE};
|
||||
|
||||
static text: o.ExternalReference = {name: 'ɵT', moduleName: CORE};
|
||||
|
@ -120,6 +120,23 @@ export function compileComponent(
|
||||
addDependencyToComponent(outputCtx, summary, pipeSet, pipeExps);
|
||||
}
|
||||
|
||||
const directiveExps: o.Expression[] = [];
|
||||
const directiveMap = new Set<string>();
|
||||
/**
|
||||
* This function gets called every time a directive dependency needs to be added to the template.
|
||||
* Its job is to remove duplicates from the list. (Only have single dependency no matter how many
|
||||
* times the dependency is used.)
|
||||
*/
|
||||
function addDirectiveDependency(ast: DirectiveAst) {
|
||||
const importExpr = outputCtx.importExpr(ast.directive.type.reference) as o.ExternalExpr;
|
||||
const uniqueKey = importExpr.value.moduleName + ':' + importExpr.value.name;
|
||||
|
||||
if (!directiveMap.has(uniqueKey)) {
|
||||
directiveMap.add(uniqueKey);
|
||||
directiveExps.push(importExpr);
|
||||
}
|
||||
}
|
||||
|
||||
const field = (key: string, value: o.Expression | null) => {
|
||||
if (value) {
|
||||
definitionMapValues.push({key, value, quoted: false});
|
||||
@ -162,10 +179,13 @@ export function compileComponent(
|
||||
new TemplateDefinitionBuilder(
|
||||
outputCtx, outputCtx.constantPool, reflector, CONTEXT_NAME, ROOT_SCOPE.nestedScope(), 0,
|
||||
component.template !.ngContentSelectors, templateTypeName, templateName, pipeMap,
|
||||
component.viewQueries, addPipeDependency)
|
||||
component.viewQueries, addDirectiveDependency, addPipeDependency)
|
||||
.buildTemplateFunction(template, []);
|
||||
|
||||
field('template', templateFunctionExpression);
|
||||
if (directiveExps.length) {
|
||||
field('directives', o.literalArr(directiveExps));
|
||||
}
|
||||
|
||||
// e.g. `pipes: [MyPipe]`
|
||||
if (pipeExps.length) {
|
||||
@ -373,6 +393,7 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
private bindingScope: BindingScope, private level = 0, private ngContentSelectors: string[],
|
||||
private contextName: string|null, private templateName: string|null,
|
||||
private pipes: Map<string, CompilePipeSummary>, private viewQueries: CompileQueryMetadata[],
|
||||
private addDirectiveDependency: (ast: DirectiveAst) => void,
|
||||
private addPipeDependency: (summary: CompilePipeSummary) => void) {
|
||||
this._valueConverter = new ValueConverter(
|
||||
outputCtx, () => this.allocateDataSlot(), (name, localName, slot, value) => {
|
||||
@ -504,23 +525,9 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
this.instruction(this._creationMode, ast.sourceSpan, R3.projection, ...parameters);
|
||||
}
|
||||
|
||||
private _computeDirectivesArray(directives: DirectiveAst[]) {
|
||||
const directiveExpressions: o.Expression[] =
|
||||
directives.filter(directive => !directive.directive.isComponent).map(directive => {
|
||||
this.allocateDataSlot(); // Allocate space for the directive
|
||||
return this.typeReference(directive.directive.type.reference);
|
||||
});
|
||||
return directiveExpressions.length ?
|
||||
this.constantPool.getConstLiteral(
|
||||
o.literalArr(directiveExpressions), /* forceShared */ true) :
|
||||
o.literal(null, o.INFERRED_TYPE);
|
||||
;
|
||||
}
|
||||
|
||||
// TemplateAstVisitor
|
||||
visitElement(element: ElementAst) {
|
||||
const elementIndex = this.allocateDataSlot();
|
||||
let componentIndex: number|undefined = undefined;
|
||||
const referenceDataSlots = new Map<string, number>();
|
||||
const wasInI18nSection = this._inI18nSection;
|
||||
|
||||
@ -563,13 +570,11 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
const nullNode = o.literal(null, o.INFERRED_TYPE);
|
||||
const parameters: o.Expression[] = [o.literal(elementIndex)];
|
||||
|
||||
// Add component type or element tag
|
||||
if (component) {
|
||||
parameters.push(this.typeReference(component.directive.type.reference));
|
||||
componentIndex = this.allocateDataSlot();
|
||||
} else {
|
||||
parameters.push(o.literal(element.name));
|
||||
this.addDirectiveDependency(component);
|
||||
}
|
||||
element.directives.forEach(this.addDirectiveDependency);
|
||||
parameters.push(o.literal(element.name));
|
||||
|
||||
// Add the attributes
|
||||
const i18nMessages: o.Statement[] = [];
|
||||
@ -598,10 +603,6 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
|
||||
parameters.push(attrArg);
|
||||
|
||||
// Add directives array
|
||||
const directivesArray = this._computeDirectivesArray(element.directives);
|
||||
parameters.push(directivesArray);
|
||||
|
||||
if (element.references && element.references.length > 0) {
|
||||
const references =
|
||||
flatten(element.references.map(reference => {
|
||||
@ -621,16 +622,12 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
parameters.push(nullNode);
|
||||
}
|
||||
|
||||
// Remove trailing null nodes as they are implied.
|
||||
while (o.isNull(parameters[parameters.length - 1])) {
|
||||
parameters.pop();
|
||||
}
|
||||
|
||||
// Generate the instruction create element instruction
|
||||
if (i18nMessages.length > 0) {
|
||||
this._creationMode.push(...i18nMessages);
|
||||
}
|
||||
this.instruction(this._creationMode, element.sourceSpan, R3.createElement, ...parameters);
|
||||
this.instruction(
|
||||
this._creationMode, element.sourceSpan, R3.createElement, ...trimTrailingNulls(parameters));
|
||||
|
||||
const implicit = o.variable(this.contextParameter);
|
||||
|
||||
@ -725,28 +722,41 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
contextName ? `${contextName}_Template_${templateIndex}` : `Template_${templateIndex}`;
|
||||
const templateContext = `ctx${this.level}`;
|
||||
|
||||
const directivesArray = this._computeDirectivesArray(ast.directives);
|
||||
const parameters: o.Expression[] = [o.variable(templateName), o.literal(null, o.INFERRED_TYPE)];
|
||||
const attributeNames: o.Expression[] = [];
|
||||
ast.directives.forEach((directiveAst: DirectiveAst) => {
|
||||
this.addDirectiveDependency(directiveAst);
|
||||
CssSelector.parse(directiveAst.directive.selector !).forEach(selector => {
|
||||
selector.attrs.forEach((value) => {
|
||||
// Convert '' (falsy) strings into `null`. This is needed because we want
|
||||
// to communicate to runtime that these attributes are present for
|
||||
// selector matching, but should not actually be added to the DOM.
|
||||
// attributeNames.push(o.literal(value ? value : null));
|
||||
|
||||
// TODO(misko): make the above comment true, for now just write to DOM because
|
||||
// the runtime selectors have not been updated.
|
||||
attributeNames.push(o.literal(value));
|
||||
});
|
||||
});
|
||||
});
|
||||
if (attributeNames.length) {
|
||||
parameters.push(
|
||||
this.constantPool.getConstLiteral(o.literalArr(attributeNames), /* forcedShared */ true));
|
||||
}
|
||||
|
||||
// e.g. C(1, C1Template)
|
||||
this.instruction(
|
||||
this._creationMode, ast.sourceSpan, R3.containerCreate, o.literal(templateIndex),
|
||||
directivesArray, o.variable(templateName));
|
||||
|
||||
// e.g. Cr(1)
|
||||
this.instruction(
|
||||
this._refreshMode, ast.sourceSpan, R3.containerRefreshStart, o.literal(templateIndex));
|
||||
...trimTrailingNulls(parameters));
|
||||
|
||||
// Generate directives
|
||||
this._visitDirectives(ast.directives, o.variable(this.contextParameter), templateIndex);
|
||||
|
||||
// e.g. cr();
|
||||
this.instruction(this._refreshMode, ast.sourceSpan, R3.containerRefreshEnd);
|
||||
|
||||
// Create the template function
|
||||
const templateVisitor = new TemplateDefinitionBuilder(
|
||||
this.outputCtx, this.constantPool, this.reflector, templateContext,
|
||||
this.bindingScope.nestedScope(), this.level + 1, this.ngContentSelectors, contextName,
|
||||
templateName, this.pipes, [], this.addPipeDependency);
|
||||
templateName, this.pipes, [], this.addDirectiveDependency, this.addPipeDependency);
|
||||
const templateFunctionExpr = templateVisitor.buildTemplateFunction(ast.children, ast.variables);
|
||||
this._postfix.push(templateFunctionExpr.toDeclStmt(templateName, null));
|
||||
}
|
||||
@ -811,8 +821,6 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver {
|
||||
statements.push(o.importExpr(reference, null, span).callFn(params, span).toStmt());
|
||||
}
|
||||
|
||||
private typeReference(type: any): o.Expression { return this.outputCtx.importExpr(type); }
|
||||
|
||||
private definitionOf(type: any, kind: DefinitionKind): o.Expression {
|
||||
return this.constantPool.getDefinition(type, kind, this.outputCtx);
|
||||
}
|
||||
@ -923,6 +931,16 @@ export function createFactory(
|
||||
type.reference.name ? `${type.reference.name}_Factory` : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove trailing null nodes as they are implied.
|
||||
*/
|
||||
function trimTrailingNulls(parameters: o.Expression[]): o.Expression[] {
|
||||
while (o.isNull(parameters[parameters.length - 1])) {
|
||||
parameters.pop();
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
type HostBindings = {
|
||||
[key: string]: string
|
||||
};
|
||||
|
@ -28,6 +28,17 @@ const _SELECTOR_REGEXP = new RegExp(
|
||||
export class CssSelector {
|
||||
element: string|null = null;
|
||||
classNames: string[] = [];
|
||||
/**
|
||||
* The selectors are encoded in pairs where:
|
||||
* - even locations are attribute names
|
||||
* - odd locations are attribute values.
|
||||
*
|
||||
* Example:
|
||||
* Selector: `[key1=value1][key2]` would parse to:
|
||||
* ```
|
||||
* ['key1', 'value1', 'key2', '']
|
||||
* ```
|
||||
*/
|
||||
attrs: string[] = [];
|
||||
notSelectors: CssSelector[] = [];
|
||||
|
||||
|
@ -113,7 +113,6 @@ describe('compiler compliance', () => {
|
||||
// MyComponent definition should be:
|
||||
const MyComponentDefinition = `
|
||||
const $c1$ = ['some-directive', ''];
|
||||
const $c2$ = [SomeDirective];
|
||||
…
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
@ -121,11 +120,12 @@ describe('compiler compliance', () => {
|
||||
factory: function MyComponent_Factory() { return new MyComponent(); },
|
||||
template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, ChildComponent, IDENT, IDENT);
|
||||
$r3$.ɵE(0, 'child', $c1$);
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵT(3, '!');
|
||||
$r3$.ɵT(1, '!');
|
||||
}
|
||||
}
|
||||
},
|
||||
directives: [ChildComponent, SomeDirective]
|
||||
});
|
||||
`;
|
||||
|
||||
@ -249,7 +249,7 @@ describe('compiler compliance', () => {
|
||||
});`;
|
||||
const MyComponentDefinition = `
|
||||
const $c1$ = ['foo', ''];
|
||||
const $c2$ = [IfDirective];
|
||||
const $c2$ = ['if', ''];
|
||||
…
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
@ -257,13 +257,11 @@ describe('compiler compliance', () => {
|
||||
factory: function MyComponent_Factory() { return new MyComponent(); },
|
||||
template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, 'ul', null, null, $c1$);
|
||||
$r3$.ɵC(2, $c2$, MyComponent_IfDirective_Template_2);
|
||||
$r3$.ɵE(0, 'ul', null, $c1$);
|
||||
$r3$.ɵC(2, MyComponent_IfDirective_Template_2, null, $c2$);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
const $foo$ = $r3$.ɵld(1);
|
||||
$r3$.ɵcR(2);
|
||||
$r3$.ɵcr();
|
||||
|
||||
function MyComponent_IfDirective_Template_2(ctx0: IDENT, cm: IDENT) {
|
||||
if (cm) {
|
||||
@ -273,7 +271,8 @@ describe('compiler compliance', () => {
|
||||
}
|
||||
$r3$.ɵt(1, $r3$.ɵi2('', ctx.salutation, ' ', $foo$, ''));
|
||||
}
|
||||
}
|
||||
},
|
||||
directives: [IfDirective]
|
||||
});`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
@ -327,11 +326,12 @@ describe('compiler compliance', () => {
|
||||
factory: function MyApp_Factory() { return new MyApp(); },
|
||||
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, MyComp);
|
||||
$r3$.ɵE(0, 'my-comp');
|
||||
$r3$.ɵe();
|
||||
}
|
||||
$r3$.ɵp(0, 'names', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.customName)));
|
||||
}
|
||||
},
|
||||
directives: [MyComp]
|
||||
});
|
||||
`;
|
||||
|
||||
@ -403,13 +403,14 @@ describe('compiler compliance', () => {
|
||||
factory: function MyApp_Factory() { return new MyApp(); },
|
||||
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, MyComp);
|
||||
$r3$.ɵE(0, 'my-comp');
|
||||
$r3$.ɵe();
|
||||
}
|
||||
$r3$.ɵp(
|
||||
0, 'names',
|
||||
$r3$.ɵb($r3$.ɵfV($e0_ff$, ctx.n0, ctx.n1, ctx.n2, ctx.n3, ctx.n4, ctx.n5, ctx.n6, ctx.n7, ctx.n8)));
|
||||
}
|
||||
},
|
||||
directives: [MyComp]
|
||||
});
|
||||
`;
|
||||
|
||||
@ -461,11 +462,12 @@ describe('compiler compliance', () => {
|
||||
factory: function MyApp_Factory() { return new MyApp(); },
|
||||
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, ObjectComp);
|
||||
$r3$.ɵE(0, 'object-comp');
|
||||
$r3$.ɵe();
|
||||
}
|
||||
$r3$.ɵp(0, 'config', $r3$.ɵb($r3$.ɵf1($e0_ff$, ctx.name)));
|
||||
}
|
||||
},
|
||||
directives: [ObjectComp]
|
||||
});
|
||||
`;
|
||||
|
||||
@ -523,14 +525,15 @@ describe('compiler compliance', () => {
|
||||
factory: function MyApp_Factory() { return new MyApp(); },
|
||||
template: function MyApp_Template(ctx: $MyApp$, cm: $boolean$) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, NestedComp);
|
||||
$r3$.ɵE(0, 'nested-comp');
|
||||
$r3$.ɵe();
|
||||
}
|
||||
$r3$.ɵp(
|
||||
0, 'config',
|
||||
$r3$.ɵb($r3$.ɵf2(
|
||||
$e0_ff_2$, ctx.name, $r3$.ɵf1($e0_ff_1$, $r3$.ɵf1($e0_ff$, ctx.duration)))));
|
||||
}
|
||||
},
|
||||
directives: [NestedComp]
|
||||
});
|
||||
`;
|
||||
|
||||
@ -655,7 +658,6 @@ describe('compiler compliance', () => {
|
||||
|
||||
const ViewQueryComponentDefinition = `
|
||||
const $e0_attrs$ = ['someDir',''];
|
||||
const $e1_dirs$ = [SomeDirective];
|
||||
…
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: ViewQueryComponent,
|
||||
@ -665,11 +667,12 @@ describe('compiler compliance', () => {
|
||||
var $tmp$: $any$;
|
||||
if (cm) {
|
||||
$r3$.ɵQ(0, SomeDirective, true);
|
||||
$r3$.ɵE(1, 'div', $e0_attrs$, $e1_dirs$);
|
||||
$r3$.ɵE(1, 'div', $e0_attrs$);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
($r3$.ɵqR(($tmp$ = $r3$.ɵld(0))) && (ctx.someDir = $tmp$.first));
|
||||
}
|
||||
},
|
||||
directives:[SomeDirective]
|
||||
});`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
@ -851,7 +854,7 @@ describe('compiler compliance', () => {
|
||||
factory: function MyComponent_Factory() { return new MyComponent(); },
|
||||
template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, 'input', null, null, $c1$);
|
||||
$r3$.ɵE(0, 'input', null, $c1$);
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵT(2);
|
||||
}
|
||||
@ -929,14 +932,15 @@ describe('compiler compliance', () => {
|
||||
factory: function SimpleLayout_Factory() { return new SimpleLayout(); },
|
||||
template: function SimpleLayout_Template(ctx: IDENT, cm: IDENT) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, LifecycleComp);
|
||||
$r3$.ɵE(0, 'lifecycle-comp');
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵE(2, LifecycleComp);
|
||||
$r3$.ɵE(1, 'lifecycle-comp');
|
||||
$r3$.ɵe();
|
||||
}
|
||||
$r3$.ɵp(0, 'name', $r3$.ɵb(ctx.name1));
|
||||
$r3$.ɵp(2, 'name', $r3$.ɵb(ctx.name2));
|
||||
}
|
||||
$r3$.ɵp(1, 'name', $r3$.ɵb(ctx.name2));
|
||||
},
|
||||
directives: [LifecycleComp]
|
||||
});`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
@ -1039,7 +1043,7 @@ describe('compiler compliance', () => {
|
||||
`;
|
||||
|
||||
const MyComponentDefinition = `
|
||||
const $c1$ = [ForOfDirective];
|
||||
const $_c0$ = ['forOf',''];
|
||||
…
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
@ -1048,12 +1052,10 @@ describe('compiler compliance', () => {
|
||||
template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, 'ul');
|
||||
$r3$.ɵC(1, $c1$, MyComponent_ForOfDirective_Template_1);
|
||||
$r3$.ɵC(1, MyComponent_ForOfDirective_Template_1, null, $_c0$);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
$r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items));
|
||||
$r3$.ɵcR(1);
|
||||
$r3$.ɵcr();
|
||||
|
||||
function MyComponent_ForOfDirective_Template_1(ctx0: IDENT, cm: IDENT) {
|
||||
if (cm) {
|
||||
@ -1064,7 +1066,8 @@ describe('compiler compliance', () => {
|
||||
const $item$ = ctx0.$implicit;
|
||||
$r3$.ɵt(1, $r3$.ɵi1('', $item$.name, ''));
|
||||
}
|
||||
}
|
||||
},
|
||||
directives: [ForOfDirective]
|
||||
});
|
||||
`;
|
||||
|
||||
@ -1114,7 +1117,7 @@ describe('compiler compliance', () => {
|
||||
};
|
||||
|
||||
const MyComponentDefinition = `
|
||||
const $c1$ = [ForOfDirective];
|
||||
const $c1$ = ['forOf', ''];
|
||||
…
|
||||
static ngComponentDef = $r3$.ɵdefineComponent({
|
||||
type: MyComponent,
|
||||
@ -1123,12 +1126,10 @@ describe('compiler compliance', () => {
|
||||
template: function MyComponent_Template(ctx: IDENT, cm: IDENT) {
|
||||
if (cm) {
|
||||
$r3$.ɵE(0, 'ul');
|
||||
$r3$.ɵC(1, $c1$, MyComponent_ForOfDirective_Template_1);
|
||||
$r3$.ɵC(1, MyComponent_ForOfDirective_Template_1, null, $c1$);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
$r3$.ɵp(1, 'forOf', $r3$.ɵb(ctx.items));
|
||||
$r3$.ɵcR(1);
|
||||
$r3$.ɵcr();
|
||||
|
||||
function MyComponent_ForOfDirective_Template_1(ctx0: IDENT, cm: IDENT) {
|
||||
if (cm) {
|
||||
@ -1137,15 +1138,13 @@ describe('compiler compliance', () => {
|
||||
$r3$.ɵT(2);
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵE(3, 'ul');
|
||||
$r3$.ɵC(4, $c1$, MyComponent_ForOfDirective_ForOfDirective_Template_4);
|
||||
$r3$.ɵC(4, MyComponent_ForOfDirective_ForOfDirective_Template_4, null, $c1$);
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵe();
|
||||
}
|
||||
const $item$ = ctx0.$implicit;
|
||||
$r3$.ɵp(4, 'forOf', $r3$.ɵb(IDENT.infos));
|
||||
$r3$.ɵt(2, $r3$.ɵi1('', IDENT.name, ''));
|
||||
$r3$.ɵcR(4);
|
||||
$r3$.ɵcr();
|
||||
|
||||
function MyComponent_ForOfDirective_ForOfDirective_Template_4(
|
||||
ctx1: IDENT, cm: IDENT) {
|
||||
@ -1158,7 +1157,8 @@ describe('compiler compliance', () => {
|
||||
$r3$.ɵt(1, $r3$.ɵi2(' ', $item$.name, ': ', $info$.description, ' '));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
directives: [ForOfDirective]
|
||||
});`;
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
|
Reference in New Issue
Block a user