refactor(compiler-cli): emit `forwardRef` invocation for forward type references (#40117)

The types of directives and pipes that are used in a component's
template may be emitted into the partial declaration wrapped inside a
closure, which is needed when the type is declared later in the module.
This poses a problem for JIT compilation of partial declarations, as
this closure is indistinguishable from a class reference itself. To mark
the forward reference function as such, this commit changes the partial
declaration codegen to emit a `forwardRef` invocation wrapped around
the closure, which ensures that the closure is properly tagged as a
forward reference. This allows the forward reference to be treated as
such during JIT compilation.

PR Close #40117
This commit is contained in:
JoostK 2020-12-14 22:35:35 +01:00 committed by Joey Perrott
parent fc1cd07eb0
commit e23fd1f382
17 changed files with 298 additions and 37 deletions

View File

@ -100,6 +100,21 @@ export class BabelAstHost implements AstHost<t.Expression> {
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<ArgumentType, t.SpreadElement> {
return !t.isSpreadElement(arg);
}

View File

@ -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\';');

View File

@ -74,6 +74,21 @@ export interface AstHost<TExpression> {
*/
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.
*/

View File

@ -295,6 +295,19 @@ export class AstValue<T, TExpression> {
return new AstValue(this.host.parseReturnValue(this.expression), this.host);
}
isCallExpression(): boolean {
return this.host.isCallExpression(this.expression);
}
getCallee(): AstValue<unknown, TExpression> {
return new AstValue(this.host.parseCallee(this.expression), this.host);
}
getArguments(): AstValue<unknown, TExpression>[] {
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`.
*/

View File

@ -109,6 +109,21 @@ export class TypeScriptAstHost implements AstHost<ts.Expression> {
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) {

View File

@ -73,10 +73,12 @@ export function toR3ComponentMeta<TExpression>(
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<TExpression>(
let pipes = new Map<string, o.Expression>();
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<TExpression>(
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<TExpression>(expr: AstValue<unknown, TExpression>):
o.WrappedNodeExpr<TExpression>|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<Function, TExpression>;
if (!wrapperFn.isFunction()) {
throw new FatalLinkerError(
wrapperFn, 'Unsupported forwardRef call, expected a function argument');
}
return wrapperFn.getFunctionReturnValue().getOpaque();
}

View File

@ -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<Function>(callExpr).isCallExpression()).toBe(true);
});
it('should return false if the value does not represent a call expression', () => {
const fooExpr = factory.createIdentifier('foo');
expect(createAstValue<Function>(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<Function>(callExpr).getCallee())
.toEqual(createAstValue(factory.createIdentifier('foo')));
});
it('should throw an error if the value is not a call expression', () => {
expect(() => createAstValue<number>(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<Function>(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<number>(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())

View File

@ -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\';');

View File

@ -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: `
<my-forward-directive></my-forward-directive>
`, 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: `
<div [attr.style]="{} | my_forward_pipe">...</div>
`, 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: [{

View File

@ -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: `
<div someDir></div>
`, 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: `
<div someDir></div>
`, 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
<content-query-component>
<div someDir></div>
</content-query-component>
`, 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
<content-query-component>
<div someDir></div>
</content-query-component>
`, 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: [{

View File

@ -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: `<svg><g *for="let item of items"><circle></circle></g></svg>`, 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: `<svg><g *for="let item of items"><circle></circle></g></svg>`, 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: `<ul><li *for="let item of items">{{item.name}}</li></ul>`, 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: `<ul><li *for="let item of items">{{item.name}}</li></ul>`, 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
</li>
</ul>
</li>
</ul>`, isInline: true }, directives: [{ type: function () { return ForOfDirective; }, selector: "[forOf]", inputs: ["forOf"] }] });
</ul>`, isInline: true }, directives: [{ type: i0.forwardRef(function () { return ForOfDirective; }), selector: "[forOf]", inputs: ["forOf"] }] });
(function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MyComponent, [{
type: Component,
args: [{

View File

@ -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: '<todo [data]="list"></todo>', 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: '<todo [data]="list"></todo>', 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: '<todo [data]="list"></todo>' }]

View File

@ -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: '<div>{{200.3 | percent : 2 }}</div>', 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: '<div>{{200.3 | percent : 2 }}</div>', 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: '<div>{{200.3 | percent : 2 }}</div>', 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: '<div>{{200.3 | percent : 2 }}</div>', 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: <input [(ngModel)]="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: <input [(ngModel)]="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: <input [(ngModel)]="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: <input [(ngModel)]="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: <input bindon-ngModel="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: <input bindon-ngModel="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: <input bindon-ngModel="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: <input bindon-ngModel="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
****************************************************************************************************/

View File

@ -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)};

View File

@ -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<R3UsedDirectiveMetadata>();
@ -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)])]);
}

View File

@ -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};

View File

@ -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,
}))();