diff --git a/packages/compiler/src/render3/r3_view_compiler.ts b/packages/compiler/src/render3/r3_view_compiler.ts index 4790b157dd..587b4bb626 100644 --- a/packages/compiler/src/render3/r3_view_compiler.ts +++ b/packages/compiler/src/render3/r3_view_compiler.ts @@ -471,20 +471,31 @@ class TemplateDefinitionBuilder implements TemplateAstVisitor, LocalResolver { if (this.ngContentSelectors && this.ngContentSelectors.length > 0) { const contentProjections = getContentProjection(nodes, this.ngContentSelectors); this._contentProjections = contentProjections; + if (contentProjections.size > 0) { - const infos: R3CssSelectorList[] = []; + const selectors: string[] = []; + Array.from(contentProjections.values()).forEach(info => { if (info.selector) { - infos[info.index - 1] = info.selector; + selectors[info.index - 1] = info.selector; } }); + const projectionIndex = this._projectionDefinitionIndex = this.allocateDataSlot(); const parameters: o.Expression[] = [o.literal(projectionIndex)]; - !infos.some(value => !value) || error(`content project information skipped an index`); - if (infos.length > 1) { - parameters.push(this.outputCtx.constantPool.getConstLiteral( - asLiteral(infos), /* forceShared */ true)); + + if (selectors.some(value => !value)) { + error(`content project information skipped an index`); } + + if (selectors.length > 1) { + const r3Selectors = selectors.map(s => parseSelectorToR3Selector(s)); + // `projectionDef` needs both the parsed and raw value of the selectors + const parsed = this.outputCtx.constantPool.getConstLiteral(asLiteral(r3Selectors), true); + const unParsed = this.outputCtx.constantPool.getConstLiteral(asLiteral(selectors), true); + parameters.push(parsed, unParsed); + } + this.instruction(this._creationMode, null, R3.projectionDef, ...parameters); } } @@ -1016,7 +1027,7 @@ type HostBindings = { // Turn a directive selector into an R3-compatible selector for directive def function createDirectiveSelector(selector: string): o.Expression { - return asLiteral(parseSelectorsToR3Selector(CssSelector.parse(selector))); + return asLiteral(parseSelectorToR3Selector(selector)); } function createHostAttributesArray( @@ -1186,7 +1197,7 @@ function invalid(arg: o.Expression | o.Statement | TemplateAst): never { interface NgContentInfo { index: number; - selector?: R3CssSelectorList; + selector?: string; } class ContentProjectionVisitor extends RecursiveTemplateAstVisitor { @@ -1198,15 +1209,15 @@ class ContentProjectionVisitor extends RecursiveTemplateAstVisitor { } visitNgContent(ngContent: NgContentAst) { - const selectorText = this.ngContentSelectors[ngContent.index]; - selectorText != null || - error(`could not find selector for index ${ngContent.index} in ${ngContent}`); - if (!selectorText || selectorText === '*') { + const selector = this.ngContentSelectors[ngContent.index]; + if (selector == null) { + error(`could not find selector for index ${ngContent.index} in ${ngContent}`); + } + + if (!selector || selector === '*') { this.projectionMap.set(ngContent, {index: 0}); } else { - const cssSelectors = CssSelector.parse(selectorText); - this.projectionMap.set( - ngContent, {index: this.index++, selector: parseSelectorsToR3Selector(cssSelectors)}); + this.projectionMap.set(ngContent, {index: this.index++, selector}); } } } @@ -1278,7 +1289,8 @@ function parserSelectorToR3Selector(selector: CssSelector): R3CssSelector { return positive.concat(...negative); } -function parseSelectorsToR3Selector(selectors: CssSelector[]): R3CssSelectorList { +function parseSelectorToR3Selector(selector: string): R3CssSelectorList { + const selectors = CssSelector.parse(selector); return selectors.map(parserSelectorToR3Selector); } diff --git a/packages/compiler/test/render3/r3_compiler_compliance_spec.ts b/packages/compiler/test/render3/r3_compiler_compliance_spec.ts index 6c96915232..2c38538185 100644 --- a/packages/compiler/test/render3/r3_compiler_compliance_spec.ts +++ b/packages/compiler/test/render3/r3_compiler_compliance_spec.ts @@ -147,7 +147,7 @@ describe('compiler compliance', () => { @Directive({selector: 'div.foo[some-directive]:not([title]):not(.baz)'}) export class SomeDirective {} - + @Directive({selector: ':not(span[title]):not(.baz)'}) export class OtherDirective {} @@ -601,8 +601,9 @@ describe('compiler compliance', () => { const ComplexComponentDefinition = ` const $c1$ = [[['span', 'title', 'tofirst']], [['span', 'title', 'tosecond']]]; - const $c2$ = ['id','first']; - const $c3$ = ['id','second']; + const $c2$ = ['span[title=toFirst]', 'span[title=toSecond]']; + const $c3$ = ['id','first']; + const $c4$ = ['id','second']; … static ngComponentDef = $r3$.ɵdefineComponent({ type: ComplexComponent, @@ -610,11 +611,11 @@ describe('compiler compliance', () => { factory: function ComplexComponent_Factory() { return new ComplexComponent(); }, template: function ComplexComponent_Template(rf: IDENT, ctx: IDENT) { if (rf & 1) { - $r3$.ɵpD(0, $c1$); - $r3$.ɵE(1, 'div', $c2$); + $r3$.ɵpD(0, $c1$, $c2$); + $r3$.ɵE(1, 'div', $c3$); $r3$.ɵP(2, 0, 1); $r3$.ɵe(); - $r3$.ɵE(3, 'div', $c3$); + $r3$.ɵE(3, 'div', $c4$); $r3$.ɵP(4, 0, 2); $r3$.ɵe(); } @@ -783,7 +784,7 @@ describe('compiler compliance', () => { } @Component({ - selector: 'my-app', + selector: 'my-app', template: '{{name | myPipe:size | myPurePipe:size }}

{{ name | myPurePipe:size }}

' }) export class MyApp { @@ -805,7 +806,7 @@ describe('compiler compliance', () => { const MyPurePipeDefinition = ` static ngPipeDef = $r3$.ɵdefinePipe({ - name: 'myPurePipe', + name: 'myPurePipe', type: MyPurePipe, factory: function MyPurePipe_Factory() { return new MyPurePipe(); }, pure: true