From ec2dbe7fb481bf9f6eb69291d539066b2e17c617 Mon Sep 17 00:00:00 2001 From: JoostK Date: Fri, 11 Sep 2020 15:17:34 +0200 Subject: [PATCH] fix(compiler): detect pipes in ICUs in template binder (#38810) Recent work on compiler internals in #38539 led to an unexpected failure, where a pipe used exclusively inside of an ICU would no longer be emitted into the compilation output. This caused runtime errors due to missing pipes. The issue occurred because the change in #38539 would determine the set of used pipes up-front, independent from the template compilation using the `R3TargetBinder`. However, `R3TargetBinder` did not consider expressions within ICUs, so any pipe usages within those expressions would not be detected. This fix unblocks #38539 and also concerns upcoming linker work, given that prelink compilations would not go through full template compilation but only `R3TargetBinder`. PR Close #38810 --- .../compiler/src/render3/view/t2_binder.ts | 5 ++- .../test/render3/view/binding_spec.ts | 34 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/compiler/src/render3/view/t2_binder.ts b/packages/compiler/src/render3/view/t2_binder.ts index 53b363afcc172..f4abcb6a1c6ad 100644 --- a/packages/compiler/src/render3/view/t2_binder.ts +++ b/packages/compiler/src/render3/view/t2_binder.ts @@ -434,7 +434,10 @@ class TemplateBinder extends RecursiveAstVisitor implements Visitor { visitText(text: Text) {} visitContent(content: Content) {} visitTextAttribute(attribute: TextAttribute) {} - visitIcu(icu: Icu): void {} + visitIcu(icu: Icu): void { + Object.keys(icu.vars).forEach(key => icu.vars[key].visit(this)); + Object.keys(icu.placeholders).forEach(key => icu.placeholders[key].visit(this)); + } // The remaining visitors are concerned with processing AST expressions within template bindings diff --git a/packages/compiler/test/render3/view/binding_spec.ts b/packages/compiler/test/render3/view/binding_spec.ts index a1c51254544d2..6afb9c011ddae 100644 --- a/packages/compiler/test/render3/view/binding_spec.ts +++ b/packages/compiler/test/render3/view/binding_spec.ts @@ -194,4 +194,38 @@ describe('t2 binding', () => { expect(consumer).toEqual(el); }); }); + + describe('used pipes', () => { + it('should record pipes used in interpolations', () => { + const template = parseTemplate('{{value|date}}', '', {}); + const binder = new R3TargetBinder(makeSelectorMatcher()); + const res = binder.bind({template: template.nodes}); + expect(res.getUsedPipes()).toEqual(['date']); + }); + + it('should record pipes used in bound attributes', () => { + const template = parseTemplate('', '', {}); + const binder = new R3TargetBinder(makeSelectorMatcher()); + const res = binder.bind({template: template.nodes}); + expect(res.getUsedPipes()).toEqual(['number']); + }); + + it('should record pipes used in bound template attributes', () => { + const template = parseTemplate('', '', {}); + const binder = new R3TargetBinder(makeSelectorMatcher()); + const res = binder.bind({template: template.nodes}); + expect(res.getUsedPipes()).toEqual(['async']); + }); + + it('should record pipes used in ICUs', () => { + const template = parseTemplate( + `{count|number, plural, + =1 { {{value|date}} } + }`, + '', {}); + const binder = new R3TargetBinder(makeSelectorMatcher()); + const res = binder.bind({template: template.nodes}); + expect(res.getUsedPipes()).toEqual(['number', 'date']); + }); + }); });