diff --git a/packages/compiler-cli/linker/babel/src/ast/babel_ast_host.ts b/packages/compiler-cli/linker/babel/src/ast/babel_ast_host.ts index 4e826d8028..5f9426847a 100644 --- a/packages/compiler-cli/linker/babel/src/ast/babel_ast_host.ts +++ b/packages/compiler-cli/linker/babel/src/ast/babel_ast_host.ts @@ -100,6 +100,21 @@ export class BabelAstHost implements AstHost { return stmt.argument; } + isCallExpression = t.isCallExpression; + parseCallee(call: t.Expression): t.Expression { + assert(call, t.isCallExpression, 'a call expression'); + assert(call.callee, t.isExpression, 'an expression'); + return call.callee; + } + parseArguments(call: t.Expression): t.Expression[] { + assert(call, t.isCallExpression, 'a call expression'); + return call.arguments.map(arg => { + assert(arg, isNotSpreadArgument, 'argument not to use spread syntax'); + assert(arg, t.isExpression, 'argument to be an expression'); + return arg; + }); + } + getRange(node: t.Expression): Range { if (node.loc == null || node.start === null || node.end === null) { throw new FatalLinkerError( @@ -138,3 +153,15 @@ function isNotSpreadElement(e: t.Expression|t.SpreadElement): e is t.Expression function isPropertyName(e: t.Expression): e is t.Identifier|t.StringLiteral|t.NumericLiteral { return t.isIdentifier(e) || t.isStringLiteral(e) || t.isNumericLiteral(e); } + +/** + * The declared type of an argument to a call expression. + */ +type ArgumentType = t.CallExpression['arguments'][number]; + +/** + * Return true if the argument is not a spread element. + */ +function isNotSpreadArgument(arg: ArgumentType): arg is Exclude { + return !t.isSpreadElement(arg); +} diff --git a/packages/compiler-cli/linker/babel/test/ast/babel_ast_host_spec.ts b/packages/compiler-cli/linker/babel/test/ast/babel_ast_host_spec.ts index 746f6cd3ae..81b4571e93 100644 --- a/packages/compiler-cli/linker/babel/test/ast/babel_ast_host_spec.ts +++ b/packages/compiler-cli/linker/babel/test/ast/babel_ast_host_spec.ts @@ -263,6 +263,55 @@ describe('BabelAstHost', () => { }); }); + describe('isCallExpression()', () => { + it('should return true if the expression is a call expression', () => { + expect(host.isCallExpression(expr('foo()'))).toBe(true); + expect(host.isCallExpression(expr('foo.bar()'))).toBe(true); + expect(host.isCallExpression(expr('(foo)(1)'))).toBe(true); + }); + + it('should return false if the expression is not a call expression', () => { + expect(host.isCallExpression(expr('[]'))).toBe(false); + expect(host.isCallExpression(expr('"moo"'))).toBe(false); + expect(host.isCallExpression(expr('\'moo\''))).toBe(false); + expect(host.isCallExpression(expr('someIdentifier'))).toBe(false); + expect(host.isCallExpression(expr('42'))).toBe(false); + expect(host.isCallExpression(expr('{}'))).toBe(false); + expect(host.isCallExpression(expr('null'))).toBe(false); + expect(host.isCallExpression(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isCallExpression(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseCallee()', () => { + it('should return the callee expression', () => { + expect(host.parseCallee(expr('foo()'))).toEqual(expr('foo')); + expect(host.parseCallee(expr('foo.bar()'))).toEqual(expr('foo.bar')); + }); + + it('should error if the node is not a call expression', () => { + expect(() => host.parseCallee(expr('[]'))) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + }); + + describe('parseArguments()', () => { + it('should return the arguments as an array of expressions', () => { + expect(host.parseArguments(expr('foo(12, [])'))).toEqual([expr('12'), expr('[]')]); + expect(host.parseArguments(expr('foo.bar()'))).toEqual([]); + }); + + it('should error if the node is not a call expression', () => { + expect(() => host.parseArguments(expr('[]'))) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + + it('should error if an argument uses spread syntax', () => { + expect(() => host.parseArguments(expr('foo(1, ...[])'))) + .toThrowError('Unsupported syntax, expected argument not to use spread syntax.'); + }); + }); + describe('getRange()', () => { it('should extract the range from the expression', () => { const file = parse('// preamble\nx = \'moo\';'); diff --git a/packages/compiler-cli/linker/src/ast/ast_host.ts b/packages/compiler-cli/linker/src/ast/ast_host.ts index 70f55bb5d7..11907f6115 100644 --- a/packages/compiler-cli/linker/src/ast/ast_host.ts +++ b/packages/compiler-cli/linker/src/ast/ast_host.ts @@ -74,6 +74,21 @@ export interface AstHost { */ parseReturnValue(fn: TExpression): TExpression; + /** + * Return true if the given expression is a call expression, or false otherwise. + */ + isCallExpression(node: TExpression): boolean; + /** + * Returns the expression that is called in the provided call expression, or throw if it is not + * a call expression. + */ + parseCallee(call: TExpression): TExpression; + /** + * Returns the argument expressions for the provided call expression, or throw if it is not + * a call expression. + */ + parseArguments(call: TExpression): TExpression[]; + /** * Compute the location range of the expression in the source file, to be used for source-mapping. */ diff --git a/packages/compiler-cli/linker/src/ast/ast_value.ts b/packages/compiler-cli/linker/src/ast/ast_value.ts index 51f8e4bdef..3e3de6943c 100644 --- a/packages/compiler-cli/linker/src/ast/ast_value.ts +++ b/packages/compiler-cli/linker/src/ast/ast_value.ts @@ -295,6 +295,19 @@ export class AstValue { return new AstValue(this.host.parseReturnValue(this.expression), this.host); } + isCallExpression(): boolean { + return this.host.isCallExpression(this.expression); + } + + getCallee(): AstValue { + return new AstValue(this.host.parseCallee(this.expression), this.host); + } + + getArguments(): AstValue[] { + const args = this.host.parseArguments(this.expression); + return args.map(arg => new AstValue(arg, this.host)); + } + /** * Return the `TExpression` of this value wrapped in a `WrappedNodeExpr`. */ diff --git a/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts b/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts index 582f5823a0..f23223d0b8 100644 --- a/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts +++ b/packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts @@ -109,6 +109,21 @@ export class TypeScriptAstHost implements AstHost { return stmt.expression; } + isCallExpression = ts.isCallExpression; + + parseCallee(call: ts.Expression): ts.Expression { + assert(call, ts.isCallExpression, 'a call expression'); + return call.expression; + } + + parseArguments(call: ts.Expression): ts.Expression[] { + assert(call, ts.isCallExpression, 'a call expression'); + return call.arguments.map(arg => { + assert(arg, isNotSpreadElement, 'argument not to use spread syntax'); + return arg; + }); + } + getRange(node: ts.Expression): Range { const file = node.getSourceFile(); if (file === undefined) { diff --git a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts index 254107b7fa..83f9a5b183 100644 --- a/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts +++ b/packages/compiler-cli/linker/src/file_linker/partial_linkers/partial_component_linker_1.ts @@ -73,10 +73,12 @@ export function toR3ComponentMeta( const selector = directiveExpr.getString('selector'); let typeExpr = type.getOpaque(); - if (type.isFunction()) { - typeExpr = type.getFunctionReturnValue().getOpaque(); + const forwardRefType = extractForwardRef(type); + if (forwardRefType !== null) { + typeExpr = forwardRefType; wrapDirectivesAndPipesInClosure = true; } + return { type: typeExpr, selector: selector, @@ -95,12 +97,13 @@ export function toR3ComponentMeta( let pipes = new Map(); if (metaObj.has('pipes')) { - pipes = metaObj.getObject('pipes').toMap(value => { - if (value.isFunction()) { + pipes = metaObj.getObject('pipes').toMap(pipe => { + const forwardRefType = extractForwardRef(pipe); + if (forwardRefType !== null) { wrapDirectivesAndPipesInClosure = true; - return value.getFunctionReturnValue().getOpaque(); + return forwardRefType; } else { - return value.getOpaque(); + return pipe.getOpaque(); } }); } @@ -205,3 +208,35 @@ function getTemplateRange( startCol: startCol + 1, }; } + +/** + * Extract the type reference expression from a `forwardRef` function call. For example, the + * expression `forwardRef(function() { return FooDir; })` returns `FooDir`. Note that this + * expression is required to be wrapped in a closure, as otherwise the forward reference would be + * resolved before initialization. + */ +function extractForwardRef(expr: AstValue): + o.WrappedNodeExpr|null { + if (!expr.isCallExpression()) { + return null; + } + + const callee = expr.getCallee(); + if (callee.getSymbolName() !== 'forwardRef') { + throw new FatalLinkerError( + callee.expression, 'Unsupported directive type, expected forwardRef or a type reference'); + } + + const args = expr.getArguments(); + if (args.length !== 1) { + throw new FatalLinkerError(expr, 'Unsupported forwardRef call, expected a single argument'); + } + + const wrapperFn = args[0] as AstValue; + if (!wrapperFn.isFunction()) { + throw new FatalLinkerError( + wrapperFn, 'Unsupported forwardRef call, expected a function argument'); + } + + return wrapperFn.getFunctionReturnValue().getOpaque(); +} diff --git a/packages/compiler-cli/linker/test/ast/ast_value_spec.ts b/packages/compiler-cli/linker/test/ast/ast_value_spec.ts index d18dd143cd..08f11ac3a7 100644 --- a/packages/compiler-cli/linker/test/ast/ast_value_spec.ts +++ b/packages/compiler-cli/linker/test/ast/ast_value_spec.ts @@ -359,6 +359,52 @@ describe('AstValue', () => { }); }); + describe('isCallExpression', () => { + it('should return true if the value represents a call expression', () => { + const callExpr = factory.createCallExpression(factory.createIdentifier('foo'), [], false); + expect(createAstValue(callExpr).isCallExpression()).toBe(true); + }); + + it('should return false if the value does not represent a call expression', () => { + const fooExpr = factory.createIdentifier('foo'); + expect(createAstValue(fooExpr).isCallExpression()).toBe(false); + }); + }); + + describe('getCallee', () => { + it('should return the callee expression as a value', () => { + const callExpr = factory.createCallExpression(factory.createIdentifier('foo'), [], false); + expect(createAstValue(callExpr).getCallee()) + .toEqual(createAstValue(factory.createIdentifier('foo'))); + }); + + it('should throw an error if the value is not a call expression', () => { + expect(() => createAstValue(factory.createLiteral(42)).getCallee()) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + }); + + describe('getArguments', () => { + it('should return the arguments as an array of values', () => { + const callExpr = factory.createCallExpression( + factory.createIdentifier('foo'), + [ + factory.createLiteral(1), + factory.createLiteral(2), + ], + false); + expect(createAstValue(callExpr).getArguments()).toEqual([ + createAstValue(factory.createLiteral(1)), + createAstValue(factory.createLiteral(2)), + ]); + }); + + it('should throw an error if the value is not a call expression', () => { + expect(() => createAstValue(factory.createLiteral(42)).getArguments()) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + }); + describe('getOpaque()', () => { it('should return the value wrapped in a `WrappedNodeExpr`', () => { expect(createAstValue(factory.createLiteral(42)).getOpaque()) diff --git a/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts b/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts index 68dd6ad9b4..c904b50a4c 100644 --- a/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts +++ b/packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts @@ -257,6 +257,59 @@ describe('TypeScriptAstHost', () => { }); }); + describe('isCallExpression()', () => { + it('should return true if the expression is a call expression', () => { + expect(host.isCallExpression(expr('foo()'))).toBe(true); + expect(host.isCallExpression(expr('foo.bar()'))).toBe(true); + expect(host.isCallExpression(expr('(foo)(1)'))).toBe(true); + }); + + it('should return false if the expression is not a call expression', () => { + expect(host.isCallExpression(expr('[]'))).toBe(false); + expect(host.isCallExpression(expr('"moo"'))).toBe(false); + expect(host.isCallExpression(expr('\'moo\''))).toBe(false); + expect(host.isCallExpression(expr('someIdentifier'))).toBe(false); + expect(host.isCallExpression(expr('42'))).toBe(false); + expect(host.isCallExpression(rhs('x = {}'))).toBe(false); + expect(host.isCallExpression(expr('null'))).toBe(false); + expect(host.isCallExpression(expr('\'a\' + \'b\''))).toBe(false); + expect(host.isCallExpression(expr('\`moo\`'))).toBe(false); + }); + }); + + describe('parseCallee()', () => { + it('should return the callee expression', () => { + const foo = jasmine.objectContaining({text: 'foo', kind: ts.SyntaxKind.Identifier}); + const bar = jasmine.objectContaining({kind: ts.SyntaxKind.PropertyAccessExpression}); + expect(host.parseCallee(expr('foo()'))).toEqual(foo); + expect(host.parseCallee(expr('foo.bar()'))).toEqual(bar); + }); + + it('should error if the node is not a call expression', () => { + expect(() => host.parseCallee(expr('[]'))) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + }); + + describe('parseArguments()', () => { + it('should return the arguments as an array of expressions', () => { + const arg1 = jasmine.objectContaining({text: '12', kind: ts.SyntaxKind.NumericLiteral}); + const arg2 = jasmine.objectContaining({kind: ts.SyntaxKind.ArrayLiteralExpression}); + expect(host.parseArguments(expr('foo(12, [])'))).toEqual([arg1, arg2]); + expect(host.parseArguments(expr('foo.bar()'))).toEqual([]); + }); + + it('should error if the node is not a call expression', () => { + expect(() => host.parseArguments(expr('[]'))) + .toThrowError('Unsupported syntax, expected a call expression.'); + }); + + it('should error if an argument uses spread syntax', () => { + expect(() => host.parseArguments(expr('foo(1, ...[])'))) + .toThrowError('Unsupported syntax, expected argument not to use spread syntax.'); + }); + }); + describe('getRange()', () => { it('should extract the range from the expression', () => { const moo = rhs('// preamble\nx = \'moo\';'); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/GOLDEN_PARTIAL.js index 16214e17fe..d964225342 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/GOLDEN_PARTIAL.js @@ -8,7 +8,7 @@ export class HostBindingComp { HostBindingComp.ɵfac = function HostBindingComp_Factory(t) { return new (t || HostBindingComp)(); }; HostBindingComp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: HostBindingComp, selector: "host-binding-comp", ngImport: i0, template: { source: ` - `, isInline: true }, directives: [{ type: function () { return MyForwardDirective; }, selector: "my-forward-directive" }] }); + `, isInline: true }, directives: [{ type: i0.forwardRef(function () { return MyForwardDirective; }), selector: "my-forward-directive" }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(HostBindingComp, [{ type: Component, args: [{ @@ -59,7 +59,7 @@ export class HostBindingComp { HostBindingComp.ɵfac = function HostBindingComp_Factory(t) { return new (t || HostBindingComp)(); }; HostBindingComp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: HostBindingComp, selector: "host-binding-comp", ngImport: i0, template: { source: `
...
- `, isInline: true }, pipes: { "my_forward_pipe": function () { return MyForwardPipe; } } }); + `, isInline: true }, pipes: { "my_forward_pipe": i0.forwardRef(function () { return MyForwardPipe; }) } }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(HostBindingComp, [{ type: Component, args: [{ diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/GOLDEN_PARTIAL.js index 212fac45a2..01668bdf44 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/queries/GOLDEN_PARTIAL.js @@ -34,7 +34,7 @@ export class ViewQueryComponent { ViewQueryComponent.ɵfac = function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); }; ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true }, { propertyName: "someDirs", predicate: SomeDirective, descendants: true }], ngImport: i0, template: { source: `
- `, isInline: true }, directives: [{ type: function () { return SomeDirective; }, selector: "[someDir]" }] }); + `, isInline: true }, directives: [{ type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewQueryComponent, [{ type: Component, args: [{ @@ -168,7 +168,7 @@ export class ViewQueryComponent { ViewQueryComponent.ɵfac = function ViewQueryComponent_Factory(t) { return new (t || ViewQueryComponent)(); }; ViewQueryComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: ViewQueryComponent, selector: "view-query-component", viewQueries: [{ propertyName: "someDir", first: true, predicate: SomeDirective, descendants: true, static: true }, { propertyName: "foo", first: true, predicate: ["foo"], descendants: true }], ngImport: i0, template: { source: `
- `, isInline: true }, directives: [{ type: function () { return SomeDirective; }, selector: "[someDir]" }] }); + `, isInline: true }, directives: [{ type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ViewQueryComponent, [{ type: Component, args: [{ @@ -361,7 +361,7 @@ MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: My
- `, isInline: true }, directives: [{ type: function () { return ContentQueryComponent; }, selector: "content-query-component" }, { type: function () { return SomeDirective; }, selector: "[someDir]" }] }); + `, isInline: true }, directives: [{ type: i0.forwardRef(function () { return ContentQueryComponent; }), selector: "content-query-component" }, { type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{ type: Component, args: [{ @@ -518,7 +518,7 @@ MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: My
- `, isInline: true }, directives: [{ type: function () { return ContentQueryComponent; }, selector: "content-query-component" }, { type: function () { return SomeDirective; }, selector: "[someDir]" }] }); + `, isInline: true }, directives: [{ type: i0.forwardRef(function () { return ContentQueryComponent; }), selector: "content-query-component" }, { type: i0.forwardRef(function () { return SomeDirective; }), selector: "[someDir]" }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{ type: Component, args: [{ diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/template_variables/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/template_variables/GOLDEN_PARTIAL.js index 3ef7aed51e..944dbd1a92 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/template_variables/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_compiler_compliance/components_and_directives/template_variables/GOLDEN_PARTIAL.js @@ -53,7 +53,7 @@ export class MyComponent { } } MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ``, isInline: true }, directives: [{ type: function () { return ForOfDirective; }, selector: "[forOf]", inputs: ["forOf"] }] }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: { source: ``, isInline: true }, directives: [{ type: i0.forwardRef(function () { return ForOfDirective; }), selector: "[forOf]", inputs: ["forOf"] }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ @@ -143,7 +143,7 @@ export class MyComponent { } } MyComponent.ɵfac = function MyComponent_Factory(t) { return new (t || MyComponent)(); }; -MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: { source: `
  • {{item.name}}
`, isInline: true }, directives: [{ type: function () { return ForOfDirective; }, selector: "[forOf]", inputs: ["forOf"] }] }); +MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyComponent, selector: "my-component", ngImport: i0, template: { source: `
  • {{item.name}}
`, isInline: true }, directives: [{ type: i0.forwardRef(function () { return ForOfDirective; }), selector: "[forOf]", inputs: ["forOf"] }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ @@ -246,7 +246,7 @@ MyComponent.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", ty - `, isInline: true }, directives: [{ type: function () { return ForOfDirective; }, selector: "[forOf]", inputs: ["forOf"] }] }); + `, isInline: true }, directives: [{ type: i0.forwardRef(function () { return ForOfDirective; }), selector: "[forOf]", inputs: ["forOf"] }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{ type: Component, args: [{ diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/GOLDEN_PARTIAL.js index 2b42ec9bea..934c6604e8 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler/GOLDEN_PARTIAL.js @@ -9,7 +9,7 @@ export class MyApp { } } MyApp.ɵfac = function MyApp_Factory(t) { return new (t || MyApp)(); }; -MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: { source: '', isInline: true }, directives: [{ type: function () { return TodoComponent; }, selector: "todo", inputs: ["data"] }] }); +MyApp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: MyApp, selector: "my-app", ngImport: i0, template: { source: '', isInline: true }, directives: [{ type: i0.forwardRef(function () { return TodoComponent; }), selector: "todo", inputs: ["data"] }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyApp, [{ type: Component, args: [{ selector: 'my-app', template: '' }] diff --git a/packages/compiler-cli/test/compliance/test_cases/source_mapping/inline_templates/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/source_mapping/inline_templates/GOLDEN_PARTIAL.js index d188813433..19647943f6 100644 --- a/packages/compiler-cli/test/compliance/test_cases/source_mapping/inline_templates/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/source_mapping/inline_templates/GOLDEN_PARTIAL.js @@ -334,7 +334,7 @@ import * as i0 from "@angular/core"; export class TestCmp { } TestCmp.ɵfac = function TestCmp_Factory(t) { return new (t || TestCmp)(); }; -TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: '
{{200.3 | percent : 2 }}
', isInline: true }, pipes: { "percent": function () { return PercentPipe; } } }); +TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: '
{{200.3 | percent : 2 }}
', isInline: true }, pipes: { "percent": i0.forwardRef(function () { return PercentPipe; }) } }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestCmp, [{ type: Component, args: [{ @@ -364,7 +364,7 @@ AppModule.ɵinj = i0.ɵɵdefineInjector({ factory: function AppModule_Factory(t) /**************************************************************************************************** * PARTIAL FILE: interpolation_with_pipe.js.map ****************************************************************************************************/ -{"version":3,"file":"interpolation_with_pipe.js","sourceRoot":"","sources":["../interpolation_with_pipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAgB,MAAM,eAAe,CAAC;;AAMvE,MAAM,OAAO,OAAO;;8DAAP,OAAO;6EAAP,OAAO,0DAFR,qCAAqC,6DAMpC,WAAW;uFAJX,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,qCAAqC;aAChD;;AAKD,MAAM,OAAO,WAAW;IACtB,SAAS,KAAI,CAAC;;sEADH,WAAW;6DAAX,WAAW;uFAAX,WAAW;cADvB,IAAI;eAAC,EAAC,IAAI,EAAE,SAAS,EAAC;;AAMvB,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBATT,OAAO,EAIP,WAAW;uFAKX,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,EAAC"} +{"version":3,"file":"interpolation_with_pipe.js","sourceRoot":"","sources":["../interpolation_with_pipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAgB,MAAM,eAAe,CAAC;;AAMvE,MAAM,OAAO,OAAO;;8DAAP,OAAO;6EAAP,OAAO,0DAFR,qCAAqC,2EAMpC,WAAW;uFAJX,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,qCAAqC;aAChD;;AAKD,MAAM,OAAO,WAAW;IACtB,SAAS,KAAI,CAAC;;sEADH,WAAW;6DAAX,WAAW;uFAAX,WAAW;cADvB,IAAI;eAAC,EAAC,IAAI,EAAE,SAAS,EAAC;;AAMvB,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBATT,OAAO,EAIP,WAAW;uFAKX,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,EAAC"} /**************************************************************************************************** * PARTIAL FILE: interpolation_with_pipe.d.ts ****************************************************************************************************/ @@ -392,7 +392,7 @@ import * as i0 from "@angular/core"; export class TestCmp { } TestCmp.ɵfac = function TestCmp_Factory(t) { return new (t || TestCmp)(); }; -TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: '
{{200.3 | percent : 2 }}
', isInline: true }, pipes: { "percent": function () { return PercentPipe; } } }); +TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: '
{{200.3 | percent : 2 }}
', isInline: true }, pipes: { "percent": i0.forwardRef(function () { return PercentPipe; }) } }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestCmp, [{ type: Component, args: [{ @@ -422,7 +422,7 @@ AppModule.ɵinj = i0.ɵɵdefineInjector({ factory: function AppModule_Factory(t) /**************************************************************************************************** * PARTIAL FILE: interpolation_with_pipe.js.map ****************************************************************************************************/ -{"version":3,"file":"interpolation_with_pipe.js","sourceRoot":"","sources":["../interpolation_with_pipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAgB,MAAM,eAAe,CAAC;;AAMvE,MAAM,OAAO,OAAO;;8DAAP,OAAO;6EAAP,OAAO,0DAFR,qCAAqC,6DAMpC,WAAW;uFAJX,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,qCAAqC;aAChD;;AAKD,MAAM,OAAO,WAAW;IACtB,SAAS,KAAI,CAAC;;sEADH,WAAW;6DAAX,WAAW;uFAAX,WAAW;cADvB,IAAI;eAAC,EAAC,IAAI,EAAE,SAAS,EAAC;;AAMvB,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBATT,OAAO,EAIP,WAAW;uFAKX,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,EAAC"} +{"version":3,"file":"interpolation_with_pipe.js","sourceRoot":"","sources":["../interpolation_with_pipe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAgB,MAAM,eAAe,CAAC;;AAMvE,MAAM,OAAO,OAAO;;8DAAP,OAAO;6EAAP,OAAO,0DAFR,qCAAqC,2EAMpC,WAAW;uFAJX,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,qCAAqC;aAChD;;AAKD,MAAM,OAAO,WAAW;IACtB,SAAS,KAAI,CAAC;;sEADH,WAAW;6DAAX,WAAW;uFAAX,WAAW;cADvB,IAAI;eAAC,EAAC,IAAI,EAAE,SAAS,EAAC;;AAMvB,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBATT,OAAO,EAIP,WAAW;uFAKX,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,EAAC"} /**************************************************************************************************** * PARTIAL FILE: interpolation_with_pipe.d.ts ****************************************************************************************************/ @@ -857,7 +857,7 @@ export class TestCmp { } } TestCmp.ɵfac = function TestCmp_Factory(t) { return new (t || TestCmp)(); }; -TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: 'Name: ', isInline: true }, directives: [{ type: function () { return NgModelDirective; }, selector: "[ngModel]", inputs: ["ngModel"], outputs: ["ngModelChanges"] }] }); +TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: 'Name: ', isInline: true }, directives: [{ type: i0.forwardRef(function () { return NgModelDirective; }), selector: "[ngModel]", inputs: ["ngModel"], outputs: ["ngModelChanges"] }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestCmp, [{ type: Component, args: [{ @@ -894,7 +894,7 @@ AppModule.ɵinj = i0.ɵɵdefineInjector({ factory: function AppModule_Factory(t) /**************************************************************************************************** * PARTIAL FILE: two_way_binding_simple.js.map ****************************************************************************************************/ -{"version":3,"file":"two_way_binding_simple.js","sourceRoot":"","sources":["../two_way_binding_simple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;;AAM1F,MAAM,OAAO,OAAO;IAJpB;QAKE,SAAI,GAAW,EAAE,CAAC;KACnB;;8DAFY,OAAO;6EAAP,OAAO,0DAFR,kCAAkC,8DAOjC,gBAAgB;uFALhB,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,kCAAkC;aAC7C;;AAMD,MAAM,OAAO,gBAAgB;IAD7B;QAEW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;gFAHY,gBAAgB;sFAAhB,gBAAgB;uFAAhB,gBAAgB;cAD5B,SAAS;eAAC,EAAC,QAAQ,EAAE,WAAW,EAAC;gBAEvB,OAAO;kBAAf,KAAK;YACI,cAAc;kBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBAXT,OAAO,EAKP,gBAAgB;uFAMhB,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} +{"version":3,"file":"two_way_binding_simple.js","sourceRoot":"","sources":["../two_way_binding_simple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;;AAM1F,MAAM,OAAO,OAAO;IAJpB;QAKE,SAAI,GAAW,EAAE,CAAC;KACnB;;8DAFY,OAAO;6EAAP,OAAO,0DAFR,kCAAkC,4EAOjC,gBAAgB;uFALhB,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,kCAAkC;aAC7C;;AAMD,MAAM,OAAO,gBAAgB;IAD7B;QAEW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;gFAHY,gBAAgB;sFAAhB,gBAAgB;uFAAhB,gBAAgB;cAD5B,SAAS;eAAC,EAAC,QAAQ,EAAE,WAAW,EAAC;gBAEvB,OAAO;kBAAf,KAAK;YACI,cAAc;kBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBAXT,OAAO,EAKP,gBAAgB;uFAMhB,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} /**************************************************************************************************** * PARTIAL FILE: two_way_binding_simple.d.ts ****************************************************************************************************/ @@ -927,7 +927,7 @@ export class TestCmp { } } TestCmp.ɵfac = function TestCmp_Factory(t) { return new (t || TestCmp)(); }; -TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: 'Name: ', isInline: true }, directives: [{ type: function () { return NgModelDirective; }, selector: "[ngModel]", inputs: ["ngModel"], outputs: ["ngModelChanges"] }] }); +TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: 'Name: ', isInline: true }, directives: [{ type: i0.forwardRef(function () { return NgModelDirective; }), selector: "[ngModel]", inputs: ["ngModel"], outputs: ["ngModelChanges"] }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestCmp, [{ type: Component, args: [{ @@ -964,7 +964,7 @@ AppModule.ɵinj = i0.ɵɵdefineInjector({ factory: function AppModule_Factory(t) /**************************************************************************************************** * PARTIAL FILE: two_way_binding_simple.js.map ****************************************************************************************************/ -{"version":3,"file":"two_way_binding_simple.js","sourceRoot":"","sources":["../two_way_binding_simple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;;AAM1F,MAAM,OAAO,OAAO;IAJpB;QAKE,SAAI,GAAW,EAAE,CAAC;KACnB;;8DAFY,OAAO;6EAAP,OAAO,0DAFR,kCAAkC,8DAOjC,gBAAgB;uFALhB,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,kCAAkC;aAC7C;;AAMD,MAAM,OAAO,gBAAgB;IAD7B;QAEW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;gFAHY,gBAAgB;sFAAhB,gBAAgB;uFAAhB,gBAAgB;cAD5B,SAAS;eAAC,EAAC,QAAQ,EAAE,WAAW,EAAC;gBAEvB,OAAO;kBAAf,KAAK;YACI,cAAc;kBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBAXT,OAAO,EAKP,gBAAgB;uFAMhB,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} +{"version":3,"file":"two_way_binding_simple.js","sourceRoot":"","sources":["../two_way_binding_simple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;;AAM1F,MAAM,OAAO,OAAO;IAJpB;QAKE,SAAI,GAAW,EAAE,CAAC;KACnB;;8DAFY,OAAO;6EAAP,OAAO,0DAFR,kCAAkC,4EAOjC,gBAAgB;uFALhB,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,kCAAkC;aAC7C;;AAMD,MAAM,OAAO,gBAAgB;IAD7B;QAEW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;gFAHY,gBAAgB;sFAAhB,gBAAgB;uFAAhB,gBAAgB;cAD5B,SAAS;eAAC,EAAC,QAAQ,EAAE,WAAW,EAAC;gBAEvB,OAAO;kBAAf,KAAK;YACI,cAAc;kBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBAXT,OAAO,EAKP,gBAAgB;uFAMhB,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} /**************************************************************************************************** * PARTIAL FILE: two_way_binding_simple.d.ts ****************************************************************************************************/ @@ -997,7 +997,7 @@ export class TestCmp { } } TestCmp.ɵfac = function TestCmp_Factory(t) { return new (t || TestCmp)(); }; -TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: 'Name: ', isInline: true }, directives: [{ type: function () { return NgModelDirective; }, selector: "[ngModel]", inputs: ["ngModel"], outputs: ["ngModelChanges"] }] }); +TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: 'Name: ', isInline: true }, directives: [{ type: i0.forwardRef(function () { return NgModelDirective; }), selector: "[ngModel]", inputs: ["ngModel"], outputs: ["ngModelChanges"] }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestCmp, [{ type: Component, args: [{ @@ -1034,7 +1034,7 @@ AppModule.ɵinj = i0.ɵɵdefineInjector({ factory: function AppModule_Factory(t) /**************************************************************************************************** * PARTIAL FILE: two_way_binding_longhand.js.map ****************************************************************************************************/ -{"version":3,"file":"two_way_binding_longhand.js","sourceRoot":"","sources":["../two_way_binding_longhand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;;AAM1F,MAAM,OAAO,OAAO;IAJpB;QAKE,SAAI,GAAW,EAAE,CAAC;KACnB;;8DAFY,OAAO;6EAAP,OAAO,0DAFR,qCAAqC,8DAOpC,gBAAgB;uFALhB,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,qCAAqC;aAChD;;AAMD,MAAM,OAAO,gBAAgB;IAD7B;QAEW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;gFAHY,gBAAgB;sFAAhB,gBAAgB;uFAAhB,gBAAgB;cAD5B,SAAS;eAAC,EAAC,QAAQ,EAAE,WAAW,EAAC;gBAEvB,OAAO;kBAAf,KAAK;YACI,cAAc;kBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBAXT,OAAO,EAKP,gBAAgB;uFAMhB,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} +{"version":3,"file":"two_way_binding_longhand.js","sourceRoot":"","sources":["../two_way_binding_longhand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;;AAM1F,MAAM,OAAO,OAAO;IAJpB;QAKE,SAAI,GAAW,EAAE,CAAC;KACnB;;8DAFY,OAAO;6EAAP,OAAO,0DAFR,qCAAqC,4EAOpC,gBAAgB;uFALhB,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,qCAAqC;aAChD;;AAMD,MAAM,OAAO,gBAAgB;IAD7B;QAEW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;gFAHY,gBAAgB;sFAAhB,gBAAgB;uFAAhB,gBAAgB;cAD5B,SAAS;eAAC,EAAC,QAAQ,EAAE,WAAW,EAAC;gBAEvB,OAAO;kBAAf,KAAK;YACI,cAAc;kBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBAXT,OAAO,EAKP,gBAAgB;uFAMhB,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} /**************************************************************************************************** * PARTIAL FILE: two_way_binding_longhand.d.ts ****************************************************************************************************/ @@ -1067,7 +1067,7 @@ export class TestCmp { } } TestCmp.ɵfac = function TestCmp_Factory(t) { return new (t || TestCmp)(); }; -TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: 'Name: ', isInline: true }, directives: [{ type: function () { return NgModelDirective; }, selector: "[ngModel]", inputs: ["ngModel"], outputs: ["ngModelChanges"] }] }); +TestCmp.ɵcmp = i0.ɵɵngDeclareComponent({ version: "0.0.0-PLACEHOLDER", type: TestCmp, selector: "test-cmp", ngImport: i0, template: { source: 'Name: ', isInline: true }, directives: [{ type: i0.forwardRef(function () { return NgModelDirective; }), selector: "[ngModel]", inputs: ["ngModel"], outputs: ["ngModelChanges"] }] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestCmp, [{ type: Component, args: [{ @@ -1104,7 +1104,7 @@ AppModule.ɵinj = i0.ɵɵdefineInjector({ factory: function AppModule_Factory(t) /**************************************************************************************************** * PARTIAL FILE: two_way_binding_longhand.js.map ****************************************************************************************************/ -{"version":3,"file":"two_way_binding_longhand.js","sourceRoot":"","sources":["../two_way_binding_longhand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;;AAM1F,MAAM,OAAO,OAAO;IAJpB;QAKE,SAAI,GAAW,EAAE,CAAC;KACnB;;8DAFY,OAAO;6EAAP,OAAO,0DAFR,qCAAqC,8DAOpC,gBAAgB;uFALhB,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,qCAAqC;aAChD;;AAMD,MAAM,OAAO,gBAAgB;IAD7B;QAEW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;gFAHY,gBAAgB;sFAAhB,gBAAgB;uFAAhB,gBAAgB;cAD5B,SAAS;eAAC,EAAC,QAAQ,EAAE,WAAW,EAAC;gBAEvB,OAAO;kBAAf,KAAK;YACI,cAAc;kBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBAXT,OAAO,EAKP,gBAAgB;uFAMhB,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} +{"version":3,"file":"two_way_binding_longhand.js","sourceRoot":"","sources":["../two_way_binding_longhand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;;AAM1F,MAAM,OAAO,OAAO;IAJpB;QAKE,SAAI,GAAW,EAAE,CAAC;KACnB;;8DAFY,OAAO;6EAAP,OAAO,0DAFR,qCAAqC,4EAOpC,gBAAgB;uFALhB,OAAO;cAJnB,SAAS;eAAC;gBACT,QAAQ,EAAE,UAAU;gBACpB,QAAQ,EAAE,qCAAqC;aAChD;;AAMD,MAAM,OAAO,gBAAgB;IAD7B;QAEW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;gFAHY,gBAAgB;sFAAhB,gBAAgB;uFAAhB,gBAAgB;cAD5B,SAAS;eAAC,EAAC,QAAQ,EAAE,WAAW,EAAC;gBAEvB,OAAO;kBAAf,KAAK;YACI,cAAc;kBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;6CAAT,SAAS;iGAAT,SAAS;wFAAT,SAAS,mBAXT,OAAO,EAKP,gBAAgB;uFAMhB,SAAS;cADrB,QAAQ;eAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} /**************************************************************************************************** * PARTIAL FILE: two_way_binding_longhand.d.ts ****************************************************************************************************/ diff --git a/packages/compiler/src/render3/partial/api.ts b/packages/compiler/src/render3/partial/api.ts index 501961883a..836685b591 100644 --- a/packages/compiler/src/render3/partial/api.ts +++ b/packages/compiler/src/render3/partial/api.ts @@ -155,7 +155,8 @@ export interface R3DeclareComponentMetadata extends R3DeclareDirectiveMetadata { selector: string; /** - * Reference to the directive class (possibly a forward reference). + * Reference to the directive class (possibly a forward reference wrapped in a `forwardRef` + * invocation). */ type: o.Expression | (() => o.Expression); @@ -176,8 +177,8 @@ export interface R3DeclareComponentMetadata extends R3DeclareDirectiveMetadata { }[]; /** - * A map of pipe names to an expression referencing the pipe type (possibly a forward reference) - * which are used in the template. + * A map of pipe names to an expression referencing the pipe type (possibly a forward reference + * wrapped in a `forwardRef` invocation) which are used in the template. */ pipes?: {[pipeName: string]: o.Expression|(() => o.Expression)}; diff --git a/packages/compiler/src/render3/partial/component.ts b/packages/compiler/src/render3/partial/component.ts index a19f0020bc..271a836fa8 100644 --- a/packages/compiler/src/render3/partial/component.ts +++ b/packages/compiler/src/render3/partial/component.ts @@ -91,9 +91,8 @@ function compileTemplateDefinition(template: ParsedTemplate): o.LiteralMapExpr { * individual directives. If the component does not use any directives, then null is returned. */ function compileUsedDirectiveMetadata(meta: R3ComponentMetadata): o.LiteralArrayExpr|null { - const wrapType = meta.wrapDirectivesAndPipesInClosure ? - (expr: o.Expression) => o.fn([], [new o.ReturnStatement(expr)]) : - (expr: o.Expression) => expr; + const wrapType = + meta.wrapDirectivesAndPipesInClosure ? generateForwardRef : (expr: o.Expression) => expr; return toOptionalLiteralArray(meta.directives, directive => { const dirMeta = new DefinitionMap(); @@ -116,9 +115,8 @@ function compileUsedPipeMetadata(meta: R3ComponentMetadata): o.LiteralMapExpr|nu return null; } - const wrapType = meta.wrapDirectivesAndPipesInClosure ? - (expr: o.Expression) => o.fn([], [new o.ReturnStatement(expr)]) : - (expr: o.Expression) => expr; + const wrapType = + meta.wrapDirectivesAndPipesInClosure ? generateForwardRef : (expr: o.Expression) => expr; const entries = []; for (const [name, pipe] of meta.pipes) { @@ -126,3 +124,7 @@ function compileUsedPipeMetadata(meta: R3ComponentMetadata): o.LiteralMapExpr|nu } return o.literalMap(entries); } + +function generateForwardRef(expr: o.Expression): o.Expression { + return o.importExpr(R3.forwardRef).callFn([o.fn([], [new o.ReturnStatement(expr)])]); +} diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index 7f757c13dd..6e3d2f7ae2 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -229,6 +229,8 @@ export class Identifiers { static templateRefExtractor: o.ExternalReference = {name: 'ɵɵtemplateRefExtractor', moduleName: CORE}; + static forwardRef: o.ExternalReference = {name: 'forwardRef', moduleName: CORE}; + static resolveWindow: o.ExternalReference = {name: 'ɵɵresolveWindow', moduleName: CORE}; static resolveDocument: o.ExternalReference = {name: 'ɵɵresolveDocument', moduleName: CORE}; static resolveBody: o.ExternalReference = {name: 'ɵɵresolveBody', moduleName: CORE}; diff --git a/packages/core/src/render3/jit/environment.ts b/packages/core/src/render3/jit/environment.ts index be4aae16ad..4556e25dd8 100644 --- a/packages/core/src/render3/jit/environment.ts +++ b/packages/core/src/render3/jit/environment.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {forwardRef} from '../../di/forward_ref'; import {ɵɵinject, ɵɵinvalidFactoryDep} from '../../di/injector_compatibility'; import {ɵɵdefineInjectable, ɵɵdefineInjector} from '../../di/interface/defs'; import * as sanitization from '../../sanitization/sanitization'; @@ -167,4 +168,6 @@ export const angularCoreEnv: {[name: string]: Function} = 'ɵɵsanitizeUrlOrResourceUrl': sanitization.ɵɵsanitizeUrlOrResourceUrl, 'ɵɵtrustConstantHtml': sanitization.ɵɵtrustConstantHtml, 'ɵɵtrustConstantResourceUrl': sanitization.ɵɵtrustConstantResourceUrl, + + 'forwardRef': forwardRef, }))();