From 59e4c3d455bdba98822ec6b986033abf4e748020 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 17 Sep 2025 14:06:25 +0200 Subject: [PATCH 1/3] refactor(compiler-cli): track member metadata using output AST (#63957) Reworks the logic that tracks the decorator metadata for members to do so using the output AST, rather than wrapping the TypeScript AST. This makes it easier to programmatically generate new members that weren't part of the TypeScript AST before. PR Close #63957 --- .../ngtsc/annotations/common/src/metadata.ts | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/common/src/metadata.ts b/packages/compiler-cli/src/ngtsc/annotations/common/src/metadata.ts index e6d782d461cd..6299337a7456 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/common/src/metadata.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/common/src/metadata.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import {ErrorCode, FatalDiagnosticError, makeRelatedInformation} from '../../../diagnostics'; +import {ErrorCode, FatalDiagnosticError} from '../../../diagnostics'; import { ArrowFunctionExpr, Expression, @@ -19,6 +19,7 @@ import { import ts from 'typescript'; import { + ClassMember, ClassMemberAccessLevel, CtorParameter, DeclarationNode, @@ -88,15 +89,32 @@ export function extractClassMetadata( const classMembers = reflection.getMembersOfClass(clazz).filter( (member) => !member.isStatic && - member.decorators !== null && - member.decorators.length > 0 && // Private fields are not supported in the metadata emit member.accessLevel !== ClassMemberAccessLevel.EcmaScriptPrivate, ); - const duplicateDecoratedMembers = classMembers.filter( - (member, i, arr) => arr.findIndex((arrayMember) => arrayMember.name === member.name) < i, - ); - if (duplicateDecoratedMembers.length > 0) { + + const decoratedMembers: {key: string; value: Expression; quoted: boolean}[] = []; + const seenMemberNames = new Set(); + let duplicateDecoratedMembers: ClassMember[] | null = null; + + for (const member of classMembers) { + if (member.decorators !== null && member.decorators.length > 0) { + decoratedMembers.push({ + key: member.name, + quoted: false, + value: decoratedClassMemberToMetadata(member.decorators!, isCore), + }); + + if (seenMemberNames.has(member.name)) { + duplicateDecoratedMembers ??= []; + duplicateDecoratedMembers.push(member); + } else { + seenMemberNames.add(member.name); + } + } + } + + if (duplicateDecoratedMembers !== null) { // This should theoretically never happen, because the only way to have duplicate instance // member names is getter/setter pairs and decorators cannot appear in both a getter and the // corresponding setter. @@ -107,13 +125,9 @@ export function extractClassMetadata( duplicateDecoratedMembers.map((member) => member.name).join(', '), ); } - const decoratedMembers = classMembers.map((member) => - classMemberToMetadata(member.nameNode ?? member.name, member.decorators!, isCore), - ); + if (decoratedMembers.length > 0) { - metaPropDecorators = new WrappedNodeExpr( - ts.factory.createObjectLiteralExpression(decoratedMembers), - ); + metaPropDecorators = literalMap(decoratedMembers); } return { @@ -153,16 +167,14 @@ function ctorParameterToMetadata(param: CtorParameter, isCore: boolean): Express /** * Convert a reflected class member to metadata. */ -function classMemberToMetadata( - name: ts.PropertyName | string, +function decoratedClassMemberToMetadata( decorators: Decorator[], isCore: boolean, -): ts.PropertyAssignment { +): LiteralArrayExpr { const ngDecorators = decorators .filter((dec) => isAngularDecorator(dec, isCore)) - .map((decorator: Decorator) => decoratorToMetadata(decorator)); - const decoratorMeta = ts.factory.createArrayLiteralExpression(ngDecorators); - return ts.factory.createPropertyAssignment(name, decoratorMeta); + .map((decorator: Decorator) => new WrappedNodeExpr(decoratorToMetadata(decorator))); + return new LiteralArrayExpr(ngDecorators); } /** From 674e9911bc969fdc9b17fcd9bf80eaa053fb9ed1 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 18 Sep 2025 11:27:19 +0200 Subject: [PATCH 2/3] refactor(compiler): expose member decorator identifiers (#63957) Adds the member decorators to the map of exposed compiler symbols. PR Close #63957 --- packages/compiler/src/render3/r3_identifiers.ts | 11 +++++++++++ packages/core/test/render3/jit_environment_spec.ts | 6 ++++++ 2 files changed, 17 insertions(+) diff --git a/packages/compiler/src/render3/r3_identifiers.ts b/packages/compiler/src/render3/r3_identifiers.ts index 3c48d64de48d..c2cf7044785c 100644 --- a/packages/compiler/src/render3/r3_identifiers.ts +++ b/packages/compiler/src/render3/r3_identifiers.ts @@ -477,6 +477,17 @@ export class Identifiers { moduleName: CORE, }; + // Decorators + static inputDecorator: o.ExternalReference = {name: 'Input', moduleName: CORE}; + static outputDecorator: o.ExternalReference = {name: 'Output', moduleName: CORE}; + static viewChildDecorator: o.ExternalReference = {name: 'ViewChild', moduleName: CORE}; + static viewChildrenDecorator: o.ExternalReference = {name: 'ViewChildren', moduleName: CORE}; + static contentChildDecorator: o.ExternalReference = {name: 'ContentChild', moduleName: CORE}; + static contentChildrenDecorator: o.ExternalReference = { + name: 'ContentChildren', + moduleName: CORE, + }; + // type-checking static InputSignalBrandWriteType = {name: 'ɵINPUT_SIGNAL_BRAND_WRITE_TYPE', moduleName: CORE}; static UnwrapDirectiveSignalInputs = {name: 'ɵUnwrapDirectiveSignalInputs', moduleName: CORE}; diff --git a/packages/core/test/render3/jit_environment_spec.ts b/packages/core/test/render3/jit_environment_spec.ts index 87db419d252c..1184eaa4920b 100644 --- a/packages/core/test/render3/jit_environment_spec.ts +++ b/packages/core/test/render3/jit_environment_spec.ts @@ -29,6 +29,12 @@ const INTERFACE_EXCEPTIONS = new Set([ const AOT_ONLY = new Set([ 'ɵsetClassMetadata', 'ɵsetClassMetadataAsync', + 'Input', + 'Output', + 'ViewChild', + 'ViewChildren', + 'ContentChild', + 'ContentChildren', // used in type-checking. 'ɵINPUT_SIGNAL_BRAND_WRITE_TYPE', From 2c0597789a9272dd12c74379809e8bbcc973f000 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Thu, 18 Sep 2025 20:25:27 +0200 Subject: [PATCH 3/3] fix(compiler-cli): capture metadata for undecorated fields (#63957) Currently if `TestBed.overrideComponent` is used on a class that uses initializer APIs (e.g. `input()`), the initializer metadata will be wiped out, because `overrideComponent` re-compiles the class with the information set by `setClassMetadata`. `setClassMetadata` only captures decorated members at the moment. These changes introduce some logic to capture the new initializer-based APIs in `setClassMetadata` as well. Fixes #57944. PR Close #63957 --- .../ngtsc/annotations/common/src/metadata.ts | 18 ++- .../annotations/common/test/metadata_spec.ts | 2 +- .../annotations/component/src/handler.ts | 9 ++ .../annotations/directive/src/handler.ts | 25 +++- .../ngtsc/annotations/directive/src/shared.ts | 140 ++++++++++++++++++ .../test_cases/model_inputs/GOLDEN_PARTIAL.js | 6 +- .../output_function/GOLDEN_PARTIAL.js | 6 +- .../host_bindings/GOLDEN_PARTIAL.js | 6 +- .../GOLDEN_PARTIAL.js | 4 +- .../signal_inputs/GOLDEN_PARTIAL.js | 10 +- .../signal_queries/GOLDEN_PARTIAL.js | 8 +- .../inline_templates/GOLDEN_PARTIAL.js | 8 +- .../test/ngtsc/authoring_inputs_spec.ts | 20 +++ .../test/ngtsc/authoring_models_spec.ts | 25 ++++ .../test/ngtsc/authoring_outputs_spec.ts | 21 +++ .../test/ngtsc/authoring_queries_spec.ts | 140 ++++++++++++++++++ 16 files changed, 419 insertions(+), 29 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/annotations/common/src/metadata.ts b/packages/compiler-cli/src/ngtsc/annotations/common/src/metadata.ts index 6299337a7456..98903c398ef6 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/common/src/metadata.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/common/src/metadata.ts @@ -30,6 +30,9 @@ import { import {valueReferenceToExpression, wrapFunctionExpressionsInParens} from './util'; +/** Function that extracts metadata from an undercorated class member. */ +export type UndecoratedMetadataExtractor = (member: ClassMember) => LiteralArrayExpr | null; + /** * Given a class declaration, generate a call to `setClassMetadata` with the Angular metadata * present on the class or its member fields. An ngDevMode guard is used to allow the call to be @@ -44,6 +47,7 @@ export function extractClassMetadata( isCore: boolean, annotateForClosureCompiler?: boolean, angularDecoratorTransform: (dec: Decorator) => Decorator = (dec) => dec, + undecoratedMetadataExtractor: UndecoratedMetadataExtractor = () => null, ): R3ClassMetadata | null { if (!reflection.isClass(clazz)) { return null; @@ -98,10 +102,12 @@ export function extractClassMetadata( let duplicateDecoratedMembers: ClassMember[] | null = null; for (const member of classMembers) { + const shouldQuoteName = member.nameNode !== null && ts.isStringLiteralLike(member.nameNode); + if (member.decorators !== null && member.decorators.length > 0) { decoratedMembers.push({ key: member.name, - quoted: false, + quoted: shouldQuoteName, value: decoratedClassMemberToMetadata(member.decorators!, isCore), }); @@ -111,6 +117,16 @@ export function extractClassMetadata( } else { seenMemberNames.add(member.name); } + } else { + const undecoratedMetadata = undecoratedMetadataExtractor(member); + + if (undecoratedMetadata !== null) { + decoratedMembers.push({ + key: member.name, + quoted: shouldQuoteName, + value: undecoratedMetadata, + }); + } } } diff --git a/packages/compiler-cli/src/ngtsc/annotations/common/test/metadata_spec.ts b/packages/compiler-cli/src/ngtsc/annotations/common/test/metadata_spec.ts index 5814cf434d80..e117844ca449 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/common/test/metadata_spec.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/common/test/metadata_spec.ts @@ -108,7 +108,7 @@ runInEachFileSystem(() => { } `); expect(res).toContain( - `{ 'has-dashes-in-name': [{ type: Input }], noDashesInName: [{ type: Input }] })`, + `{ "has-dashes-in-name": [{ type: Input }], noDashesInName: [{ type: Input }] })`, ); }); diff --git a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts index b3d350493e7d..7a7f4378d1ec 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts @@ -155,12 +155,14 @@ import { ResourceLoader, toFactoryMetadata, tryUnwrapForwardRef, + UndecoratedMetadataExtractor, validateHostDirectives, wrapFunctionExpressionsInParens, } from '../../common'; import { extractDirectiveMetadata, extractHostBindingResources, + getDirectiveUndecoratedMetadataExtractor, parseDirectiveStyles, } from '../../directive'; import {createModuleWithProvidersResolver, NgModuleSymbol} from '../../ng_module'; @@ -291,6 +293,11 @@ export class ComponentDecoratorHandler preserveSignificantWhitespace: this.i18nPreserveSignificantWhitespace, }; + this.undecoratedMetadataExtractor = getDirectiveUndecoratedMetadataExtractor( + reflector, + importTracker, + ); + // Dependencies can't be deferred during HMR, because the HMR update module can't have // dynamic imports and its dependencies need to be passed in directly. If dependencies // are deferred, their imports will be deleted so we may lose the reference to them. @@ -299,6 +306,7 @@ export class ComponentDecoratorHandler private literalCache = new Map(); private elementSchemaRegistry = new DomElementSchemaRegistry(); + private readonly undecoratedMetadataExtractor: UndecoratedMetadataExtractor; /** * During the asynchronous preanalyze phase, it's necessary to parse the template to extract @@ -971,6 +979,7 @@ export class ComponentDecoratorHandler this.isCore, this.annotateForClosureCompiler, (dec) => transformDecoratorResources(dec, component, styles, template), + this.undecoratedMetadataExtractor, ) : null, classDebugInfo: extractClassDebugInfo( diff --git a/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts b/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts index c97a5c8f9879..875308dadc03 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/directive/src/handler.ts @@ -74,10 +74,16 @@ import { ReferencesRegistry, resolveProvidersRequiringFactory, toFactoryMetadata, + UndecoratedMetadataExtractor, validateHostDirectives, } from '../../common'; -import {extractDirectiveMetadata, extractHostBindingResources, HostBindingNodes} from './shared'; +import { + extractDirectiveMetadata, + extractHostBindingResources, + getDirectiveUndecoratedMetadataExtractor, + HostBindingNodes, +} from './shared'; import {DirectiveSymbol} from './symbol'; import {JitDeclarationRegistry} from '../../common/src/jit_declaration_registry'; import { @@ -154,10 +160,16 @@ export class DirectiveDecoratorHandler private readonly usePoisonedData: boolean, private readonly typeCheckHostBindings: boolean, private readonly emitDeclarationOnly: boolean, - ) {} + ) { + this.undecoratedMetadataExtractor = getDirectiveUndecoratedMetadataExtractor( + reflector, + importTracker, + ); + } readonly precedence = HandlerPrecedence.PRIMARY; readonly name = 'DirectiveDecoratorHandler'; + private readonly undecoratedMetadataExtractor: UndecoratedMetadataExtractor; detect( node: ClassDeclaration, @@ -240,7 +252,14 @@ export class DirectiveDecoratorHandler hostDirectives: directiveResult.hostDirectives, rawHostDirectives: directiveResult.rawHostDirectives, classMetadata: this.includeClassMetadata - ? extractClassMetadata(node, this.reflector, this.isCore, this.annotateForClosureCompiler) + ? extractClassMetadata( + node, + this.reflector, + this.isCore, + this.annotateForClosureCompiler, + undefined, + this.undecoratedMetadataExtractor, + ) : null, baseClass: readBaseClass(node, this.reflector, this.evaluator), typeCheckMeta: extractDirectiveTypeCheckMeta(node, directiveResult.inputs, this.reflector), diff --git a/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts b/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts index 65830fd6bb42..f5ecd7265b3e 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/directive/src/shared.ts @@ -14,6 +14,8 @@ import { ExternalReference, ForwardRefHandling, getSafePropertyAccessString, + LiteralArrayExpr, + literalMap, MaybeForwardRefExpression, ParsedHostBindings, ParseError, @@ -24,7 +26,10 @@ import { R3QueryMetadata, R3Reference, verifyHostBindings, + R3Identifiers, + ArrowFunctionExpr, WrappedNodeExpr, + literal, } from '@angular/compiler'; import ts from 'typescript'; @@ -76,6 +81,7 @@ import { ReferencesRegistry, toR3Reference, tryUnwrapForwardRef, + UndecoratedMetadataExtractor, unwrapConstructorDependencies, unwrapExpression, validateConstructorDependencies, @@ -855,6 +861,140 @@ export function parseFieldStringArrayValue( return value; } +/** + * Returns a function that can be used to extract data for the `setClassMetadata` + * calls from undecorated directive class members. + */ +export function getDirectiveUndecoratedMetadataExtractor( + reflector: ReflectionHost, + importTracker: ImportedSymbolsTracker, +): UndecoratedMetadataExtractor { + return (member: ClassMember): LiteralArrayExpr | null => { + const input = tryParseSignalInputMapping(member, reflector, importTracker); + if (input !== null) { + return getDecoratorMetaArray([ + [new ExternalExpr(R3Identifiers.inputDecorator), memberMetadataFromSignalInput(input)], + ]); + } + + const output = tryParseInitializerBasedOutput(member, reflector, importTracker); + if (output !== null) { + return getDecoratorMetaArray([ + [ + new ExternalExpr(R3Identifiers.outputDecorator), + memberMetadataFromInitializerOutput(output.metadata), + ], + ]); + } + + const model = tryParseSignalModelMapping(member, reflector, importTracker); + if (model !== null) { + return getDecoratorMetaArray([ + [ + new ExternalExpr(R3Identifiers.inputDecorator), + memberMetadataFromSignalInput(model.input), + ], + [ + new ExternalExpr(R3Identifiers.outputDecorator), + memberMetadataFromInitializerOutput(model.output), + ], + ]); + } + + const query = tryParseSignalQueryFromInitializer(member, reflector, importTracker); + if (query !== null) { + let identifier: ExternalReference; + if (query.name === 'viewChild') { + identifier = R3Identifiers.viewChildDecorator; + } else if (query.name === 'viewChildren') { + identifier = R3Identifiers.viewChildrenDecorator; + } else if (query.name === 'contentChild') { + identifier = R3Identifiers.contentChildDecorator; + } else if (query.name === 'contentChildren') { + identifier = R3Identifiers.contentChildrenDecorator; + } else { + return null; + } + + return getDecoratorMetaArray([ + [new ExternalExpr(identifier), memberMetadataFromSignalQuery(query.call)], + ]); + } + + return null; + }; +} + +function getDecoratorMetaArray( + decorators: [type: ExternalExpr, args: LiteralArrayExpr][], +): LiteralArrayExpr { + return new LiteralArrayExpr( + decorators.map(([type, args]) => + literalMap([ + {key: 'type', value: type, quoted: false}, + {key: 'args', value: args, quoted: false}, + ]), + ), + ); +} + +function memberMetadataFromSignalInput(input: InputMapping): LiteralArrayExpr { + // Note that for signal inputs the transform is captured in the signal + // initializer so we don't need to capture it here. + return new LiteralArrayExpr([ + literalMap([ + { + key: 'isSignal', + value: literal(true), + quoted: false, + }, + { + key: 'alias', + value: literal(input.bindingPropertyName), + quoted: false, + }, + { + key: 'required', + value: literal(input.required), + quoted: false, + }, + ]), + ]); +} + +function memberMetadataFromInitializerOutput(output: InputOrOutput): LiteralArrayExpr { + return new LiteralArrayExpr([literal(output.bindingPropertyName)]); +} + +function memberMetadataFromSignalQuery(call: ts.CallExpression): LiteralArrayExpr { + const firstArg = call.arguments[0]; + const firstArgMeta = + ts.isStringLiteralLike(firstArg) || ts.isCallExpression(firstArg) + ? new WrappedNodeExpr(firstArg) + : // If the first argument is a class reference, we need to wrap it in a `forwardRef` + // because the reference might occur after the current class. This wouldn't be flagged + // on the query initializer, because it executes after the class is initialized, whereas + // `setClassMetadata` runs immediately. + new ExternalExpr(R3Identifiers.forwardRef).callFn([ + new ArrowFunctionExpr([], new WrappedNodeExpr(firstArg)), + ]); + + const entries: Expression[] = [ + // We use wrapped nodes here, because the output AST doesn't support spread assignments. + firstArgMeta, + new WrappedNodeExpr( + ts.factory.createObjectLiteralExpression([ + ...(call.arguments.length > 1 + ? [ts.factory.createSpreadAssignment(call.arguments[1])] + : []), + ts.factory.createPropertyAssignment('isSignal', ts.factory.createTrue()), + ]), + ), + ]; + + return new LiteralArrayExpr(entries); +} + function isStringArrayOrDie(value: any, name: string, node: ts.Expression): value is string[] { if (!Array.isArray(value)) { return false; diff --git a/packages/compiler-cli/test/compliance/test_cases/model_inputs/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/model_inputs/GOLDEN_PARTIAL.js index 9dc3dd4539ec..33d14ee9b3b6 100644 --- a/packages/compiler-cli/test/compliance/test_cases/model_inputs/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/model_inputs/GOLDEN_PARTIAL.js @@ -14,7 +14,7 @@ TestDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "0.0. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, decorators: [{ type: Directive, args: [{}] - }] }); + }], propDecorators: { counter: [{ type: i0.Input, args: [{ isSignal: true, alias: "counter", required: false }] }, { type: i0.Output, args: ["counterChange"] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }, { type: i0.Output, args: ["nameChange"] }] } }); /**************************************************************************************************** * PARTIAL FILE: model_directive_definition.d.ts @@ -45,7 +45,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE args: [{ template: 'Works', }] - }] }); + }], propDecorators: { counter: [{ type: i0.Input, args: [{ isSignal: true, alias: "counter", required: false }] }, { type: i0.Output, args: ["counterChange"] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }, { type: i0.Output, args: ["nameChange"] }] } }); /**************************************************************************************************** * PARTIAL FILE: model_component_definition.d.ts @@ -80,7 +80,7 @@ TestDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "0.0. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, decorators: [{ type: Directive, args: [{}] - }], propDecorators: { decoratorInput: [{ + }], propDecorators: { counter: [{ type: i0.Input, args: [{ isSignal: true, alias: "counter", required: false }] }, { type: i0.Output, args: ["counterChange"] }], modelWithAlias: [{ type: i0.Input, args: [{ isSignal: true, alias: "alias", required: false }] }, { type: i0.Output, args: ["aliasChange"] }], decoratorInput: [{ type: Input }], decoratorInputWithAlias: [{ type: Input, diff --git a/packages/compiler-cli/test/compliance/test_cases/output_function/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/output_function/GOLDEN_PARTIAL.js index bfb7ec07b0cf..724a4967fce1 100644 --- a/packages/compiler-cli/test/compliance/test_cases/output_function/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/output_function/GOLDEN_PARTIAL.js @@ -18,7 +18,7 @@ TestDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "0.0. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, decorators: [{ type: Directive, args: [{}] - }] }); + }], propDecorators: { a: [{ type: i0.Output, args: ["a"] }], b: [{ type: i0.Output, args: ["b"] }], c: [{ type: i0.Output, args: ["cPublic"] }], d: [{ type: i0.Output, args: ["d"] }], e: [{ type: i0.Output, args: ["e"] }] } }); /**************************************************************************************************** * PARTIAL FILE: output_in_directive.d.ts @@ -56,7 +56,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE args: [{ template: 'Works', }] - }] }); + }], propDecorators: { a: [{ type: i0.Output, args: ["a"] }], b: [{ type: i0.Output, args: ["b"] }], c: [{ type: i0.Output, args: ["cPublic"] }], d: [{ type: i0.Output, args: ["d"] }], e: [{ type: i0.Output, args: ["e"] }] } }); /**************************************************************************************************** * PARTIAL FILE: output_in_component.d.ts @@ -97,7 +97,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE args: [{ standalone: true, }] - }], propDecorators: { clickDecorator1: [{ + }], propDecorators: { click1: [{ type: i0.Output, args: ["click1"] }], click2: [{ type: i0.Output, args: ["click2"] }], click3: [{ type: i0.Output, args: ["click3"] }], _bla: [{ type: i0.Output, args: ["decoratorPublicName"] }], _bla2: [{ type: i0.Output, args: ["decoratorPublicName2"] }], clickDecorator1: [{ type: Output }], clickDecorator2: [{ type: Output diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js index 2831f0d23ba7..b4805ea15ef0 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_bindings/host_bindings/GOLDEN_PARTIAL.js @@ -833,13 +833,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE selector: '[hostBindingDir]', standalone: false }] - }], propDecorators: { 'is-a': [{ + }], propDecorators: { "is-a": [{ type: HostBinding, args: ['class.a'] - }], 'is-"b"': [{ + }], "is-\"b\"": [{ type: HostBinding, args: ['class.b'] - }], '"is-c"': [{ + }], "\"is-c\"": [{ type: HostBinding, args: ['class.c'] }] } }); diff --git a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js index 166e20b604ae..4b1957b46610 100644 --- a/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_listener/GOLDEN_PARTIAL.js @@ -977,7 +977,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE args: [{ selector: '[ngModel]', }] - }] }); + }], propDecorators: { ngModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngModel", required: true }] }, { type: i0.Output, args: ["ngModelChange"] }] } }); export class TestCmp { constructor() { this.names = [signal('Angular')]; @@ -1031,7 +1031,7 @@ NgModelDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", versi i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: NgModelDirective, decorators: [{ type: Directive, args: [{ selector: '[ngModel]' }] - }] }); + }], propDecorators: { ngModel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ngModel", required: false }] }, { type: i0.Output, args: ["ngModelChange"] }] } }); export class TestCmp { constructor() { this.value = 123; diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js index c403b60b196c..57031f20c3f4 100644 --- a/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/signal_inputs/GOLDEN_PARTIAL.js @@ -14,7 +14,7 @@ TestDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "0.0. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, decorators: [{ type: Directive, args: [{}] - }] }); + }], propDecorators: { counter: [{ type: i0.Input, args: [{ isSignal: true, alias: "counter", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }] } }); /**************************************************************************************************** * PARTIAL FILE: input_directive_definition.d.ts @@ -45,7 +45,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE args: [{ template: 'Works', }] - }] }); + }], propDecorators: { counter: [{ type: i0.Input, args: [{ isSignal: true, alias: "counter", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }] } }); /**************************************************************************************************** * PARTIAL FILE: input_component_definition.d.ts @@ -81,7 +81,7 @@ TestDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "0.0. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, decorators: [{ type: Directive, args: [{}] - }], propDecorators: { decoratorInput: [{ + }], propDecorators: { counter: [{ type: i0.Input, args: [{ isSignal: true, alias: "counter", required: false }] }], signalWithTransform: [{ type: i0.Input, args: [{ isSignal: true, alias: "signalWithTransform", required: false }] }], signalWithTransformAndAlias: [{ type: i0.Input, args: [{ isSignal: true, alias: "publicNameSignal", required: false }] }], decoratorInput: [{ type: Input }], decoratorInputWithAlias: [{ type: Input, @@ -127,7 +127,7 @@ TestDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "0.0. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, decorators: [{ type: Directive, args: [{}] - }] }); + }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }] } }); /**************************************************************************************************** * PARTIAL FILE: transform_not_captured.d.ts @@ -163,7 +163,7 @@ TestDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "0.0. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, decorators: [{ type: Directive, args: [{}] - }] }); + }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], name2: [{ type: i0.Input, args: [{ isSignal: true, alias: "name2", required: true }] }], genericTransform: [{ type: i0.Input, args: [{ isSignal: true, alias: "genericTransform", required: true }] }], genericTransform2: [{ type: i0.Input, args: [{ isSignal: true, alias: "genericTransform2", required: true }] }] } }); /**************************************************************************************************** * PARTIAL FILE: complex_transform_functions.d.ts diff --git a/packages/compiler-cli/test/compliance/test_cases/signal_queries/GOLDEN_PARTIAL.js b/packages/compiler-cli/test/compliance/test_cases/signal_queries/GOLDEN_PARTIAL.js index 33d4b3d20ff3..3229468d1dba 100644 --- a/packages/compiler-cli/test/compliance/test_cases/signal_queries/GOLDEN_PARTIAL.js +++ b/packages/compiler-cli/test/compliance/test_cases/signal_queries/GOLDEN_PARTIAL.js @@ -24,7 +24,7 @@ TestDir.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "0.0. i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDER", ngImport: i0, type: TestDir, decorators: [{ type: Directive, args: [{}] - }] }); + }], propDecorators: { query1: [{ type: i0.ViewChild, args: ['locatorA', { isSignal: true }] }], query2: [{ type: i0.ViewChildren, args: ['locatorB', { isSignal: true }] }], query3: [{ type: i0.ContentChild, args: ['locatorC', { isSignal: true }] }], query4: [{ type: i0.ContentChildren, args: ['locatorD', { isSignal: true }] }], query5: [{ type: i0.ViewChild, args: [forwardRef(() => SomeToken), { isSignal: true }] }], query6: [{ type: i0.ViewChildren, args: [i0.forwardRef(() => SomeToken), { isSignal: true }] }], query7: [{ type: i0.ViewChild, args: ['locatorE', Object.assign({ read: SomeToken }, { isSignal: true })] }], query8: [{ type: i0.ContentChildren, args: ['locatorF, locatorG', Object.assign({ descendants: true }, { isSignal: true })] }], query9: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => nonAnalyzableRefersToString), Object.assign({ descendants: true }, { isSignal: true })] }] } }); /**************************************************************************************************** * PARTIAL FILE: query_in_directive.d.ts @@ -66,7 +66,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE args: [{ template: 'Works', }] - }] }); + }], propDecorators: { query1: [{ type: i0.ViewChild, args: ['locatorA', { isSignal: true }] }], query2: [{ type: i0.ViewChildren, args: ['locatorB', { isSignal: true }] }], query3: [{ type: i0.ContentChild, args: ['locatorC', { isSignal: true }] }], query4: [{ type: i0.ContentChildren, args: ['locatorD', { isSignal: true }] }] } }); /**************************************************************************************************** * PARTIAL FILE: query_in_component.d.ts @@ -100,10 +100,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE }], propDecorators: { decoratorViewChild: [{ type: ViewChild, args: ['locator1'] - }], decoratorContentChild: [{ + }], signalViewChild: [{ type: i0.ViewChild, args: ['locator1', { isSignal: true }] }], decoratorContentChild: [{ type: ContentChild, args: ['locator2'] - }] } }); + }], signalContentChild: [{ type: i0.ContentChild, args: ['locator2', { isSignal: true }] }] } }); /**************************************************************************************************** * PARTIAL FILE: mixed_query_variants.d.ts 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 37f60fb068ca..4dc29dd6c109 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 @@ -930,7 +930,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE /**************************************************************************************************** * 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;;AAO1F,MAAM,OAAO,OAAO;IALpB;QAME,SAAI,GAAW,EAAE,CAAC;KACnB;;+GAFY,OAAO;mGAAP,OAAO,qEAHN,kCAAkC,gFAWnC,gBAAgB;sGARhB,OAAO;kBALnB,SAAS;mBAAC;oBACP,QAAQ,EAAE,UAAU;oBACpB,QAAQ,EAAE,kCAAkC;oBAC5C,UAAU,EAAE,KAAK;iBACpB;;AASD,MAAM,OAAO,gBAAgB;IAJ7B;QAKW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;wHAHY,gBAAgB;4GAAhB,gBAAgB;sGAAhB,gBAAgB;kBAJ5B,SAAS;mBAAC;oBACP,QAAQ,EAAE,WAAW;oBACrB,UAAU,EAAE,KAAK;iBACpB;8BAEU,OAAO;sBAAf,KAAK;gBACI,cAAc;sBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;iHAAT,SAAS;kHAAT,SAAS,iBAdT,OAAO,EAQP,gBAAgB;kHAMhB,SAAS;sGAAT,SAAS;kBADrB,QAAQ;mBAAC,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;;AAO1F,MAAM,OAAO,OAAO;IALpB;QAME,SAAI,GAAW,EAAE,CAAC;KACnB;;+GAFY,OAAO;mGAAP,OAAO,qEAHN,kCAAkC,gFAWnC,gBAAgB;sGARhB,OAAO;kBALnB,SAAS;mBAAC;oBACP,QAAQ,EAAE,UAAU;oBACpB,QAAQ,EAAE,kCAAkC;oBAC5C,UAAU,EAAE,KAAK;iBACpB;;AASD,MAAM,OAAO,gBAAgB;IAJ7B;QAKW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;wHAHY,gBAAgB;4GAAhB,gBAAgB;sGAAhB,gBAAgB;kBAJ5B,SAAS;mBAAC;oBACP,QAAQ,EAAE,WAAW;oBACrB,UAAU,EAAE,KAAK;iBACpB;;sBAEE,KAAK;;sBACL,MAAM;;AAIT,MAAM,OAAO,SAAS;;iHAAT,SAAS;kHAAT,SAAS,iBAdT,OAAO,EAQP,gBAAgB;kHAMhB,SAAS;sGAAT,SAAS;kBADrB,QAAQ;mBAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} /**************************************************************************************************** * PARTIAL FILE: two_way_binding_simple.d.ts ****************************************************************************************************/ @@ -1005,7 +1005,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE /**************************************************************************************************** * 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;;AAO1F,MAAM,OAAO,OAAO;IALpB;QAME,SAAI,GAAW,EAAE,CAAC;KACnB;;+GAFY,OAAO;mGAAP,OAAO,qEAHN,kCAAkC,gFAWnC,gBAAgB;sGARhB,OAAO;kBALnB,SAAS;mBAAC;oBACP,QAAQ,EAAE,UAAU;oBACpB,QAAQ,EAAE,kCAAkC;oBAC5C,UAAU,EAAE,KAAK;iBACpB;;AASD,MAAM,OAAO,gBAAgB;IAJ7B;QAKW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;wHAHY,gBAAgB;4GAAhB,gBAAgB;sGAAhB,gBAAgB;kBAJ5B,SAAS;mBAAC;oBACP,QAAQ,EAAE,WAAW;oBACrB,UAAU,EAAE,KAAK;iBACpB;8BAEU,OAAO;sBAAf,KAAK;gBACI,cAAc;sBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;iHAAT,SAAS;kHAAT,SAAS,iBAdT,OAAO,EAQP,gBAAgB;kHAMhB,SAAS;sGAAT,SAAS;kBADrB,QAAQ;mBAAC,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;;AAO1F,MAAM,OAAO,OAAO;IALpB;QAME,SAAI,GAAW,EAAE,CAAC;KACnB;;+GAFY,OAAO;mGAAP,OAAO,qEAHN,kCAAkC,gFAWnC,gBAAgB;sGARhB,OAAO;kBALnB,SAAS;mBAAC;oBACP,QAAQ,EAAE,UAAU;oBACpB,QAAQ,EAAE,kCAAkC;oBAC5C,UAAU,EAAE,KAAK;iBACpB;;AASD,MAAM,OAAO,gBAAgB;IAJ7B;QAKW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;wHAHY,gBAAgB;4GAAhB,gBAAgB;sGAAhB,gBAAgB;kBAJ5B,SAAS;mBAAC;oBACP,QAAQ,EAAE,WAAW;oBACrB,UAAU,EAAE,KAAK;iBACpB;;sBAEE,KAAK;;sBACL,MAAM;;AAIT,MAAM,OAAO,SAAS;;iHAAT,SAAS;kHAAT,SAAS,iBAdT,OAAO,EAQP,gBAAgB;kHAMhB,SAAS;sGAAT,SAAS;kBADrB,QAAQ;mBAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} /**************************************************************************************************** * PARTIAL FILE: two_way_binding_simple.d.ts ****************************************************************************************************/ @@ -1080,7 +1080,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE /**************************************************************************************************** * 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;;AAO1F,MAAM,OAAO,OAAO;IALpB;QAME,SAAI,GAAW,EAAE,CAAC;KACnB;;+GAFY,OAAO;mGAAP,OAAO,qEAHN,qCAAqC,gFAWtC,gBAAgB;sGARhB,OAAO;kBALnB,SAAS;mBAAC;oBACP,QAAQ,EAAE,UAAU;oBACpB,QAAQ,EAAE,qCAAqC;oBAC/C,UAAU,EAAE,KAAK;iBACpB;;AASD,MAAM,OAAO,gBAAgB;IAJ7B;QAKW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;wHAHY,gBAAgB;4GAAhB,gBAAgB;sGAAhB,gBAAgB;kBAJ5B,SAAS;mBAAC;oBACP,QAAQ,EAAE,WAAW;oBACrB,UAAU,EAAE,KAAK;iBACpB;8BAEU,OAAO;sBAAf,KAAK;gBACI,cAAc;sBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;iHAAT,SAAS;kHAAT,SAAS,iBAdT,OAAO,EAQP,gBAAgB;kHAMhB,SAAS;sGAAT,SAAS;kBADrB,QAAQ;mBAAC,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;;AAO1F,MAAM,OAAO,OAAO;IALpB;QAME,SAAI,GAAW,EAAE,CAAC;KACnB;;+GAFY,OAAO;mGAAP,OAAO,qEAHN,qCAAqC,gFAWtC,gBAAgB;sGARhB,OAAO;kBALnB,SAAS;mBAAC;oBACP,QAAQ,EAAE,UAAU;oBACpB,QAAQ,EAAE,qCAAqC;oBAC/C,UAAU,EAAE,KAAK;iBACpB;;AASD,MAAM,OAAO,gBAAgB;IAJ7B;QAKW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;wHAHY,gBAAgB;4GAAhB,gBAAgB;sGAAhB,gBAAgB;kBAJ5B,SAAS;mBAAC;oBACP,QAAQ,EAAE,WAAW;oBACrB,UAAU,EAAE,KAAK;iBACpB;;sBAEE,KAAK;;sBACL,MAAM;;AAIT,MAAM,OAAO,SAAS;;iHAAT,SAAS;kHAAT,SAAS,iBAdT,OAAO,EAQP,gBAAgB;kHAMhB,SAAS;sGAAT,SAAS;kBADrB,QAAQ;mBAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} /**************************************************************************************************** * PARTIAL FILE: two_way_binding_longhand.d.ts ****************************************************************************************************/ @@ -1155,7 +1155,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "0.0.0-PLACEHOLDE /**************************************************************************************************** * 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;;AAO1F,MAAM,OAAO,OAAO;IALpB;QAME,SAAI,GAAW,EAAE,CAAC;KACnB;;+GAFY,OAAO;mGAAP,OAAO,qEAHN,qCAAqC,gFAWtC,gBAAgB;sGARhB,OAAO;kBALnB,SAAS;mBAAC;oBACP,QAAQ,EAAE,UAAU;oBACpB,QAAQ,EAAE,qCAAqC;oBAC/C,UAAU,EAAE,KAAK;iBACpB;;AASD,MAAM,OAAO,gBAAgB;IAJ7B;QAKW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;wHAHY,gBAAgB;4GAAhB,gBAAgB;sGAAhB,gBAAgB;kBAJ5B,SAAS;mBAAC;oBACP,QAAQ,EAAE,WAAW;oBACrB,UAAU,EAAE,KAAK;iBACpB;8BAEU,OAAO;sBAAf,KAAK;gBACI,cAAc;sBAAvB,MAAM;;AAIT,MAAM,OAAO,SAAS;;iHAAT,SAAS;kHAAT,SAAS,iBAdT,OAAO,EAQP,gBAAgB;kHAMhB,SAAS;sGAAT,SAAS;kBADrB,QAAQ;mBAAC,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;;AAO1F,MAAM,OAAO,OAAO;IALpB;QAME,SAAI,GAAW,EAAE,CAAC;KACnB;;+GAFY,OAAO;mGAAP,OAAO,qEAHN,qCAAqC,gFAWtC,gBAAgB;sGARhB,OAAO;kBALnB,SAAS;mBAAC;oBACP,QAAQ,EAAE,UAAU;oBACpB,QAAQ,EAAE,qCAAqC;oBAC/C,UAAU,EAAE,KAAK;iBACpB;;AASD,MAAM,OAAO,gBAAgB;IAJ7B;QAKW,YAAO,GAAW,EAAE,CAAC;QACpB,mBAAc,GAAyB,IAAI,YAAY,EAAE,CAAC;KACrE;;wHAHY,gBAAgB;4GAAhB,gBAAgB;sGAAhB,gBAAgB;kBAJ5B,SAAS;mBAAC;oBACP,QAAQ,EAAE,WAAW;oBACrB,UAAU,EAAE,KAAK;iBACpB;;sBAEE,KAAK;;sBACL,MAAM;;AAIT,MAAM,OAAO,SAAS;;iHAAT,SAAS;kHAAT,SAAS,iBAdT,OAAO,EAQP,gBAAgB;kHAMhB,SAAS;sGAAT,SAAS;kBADrB,QAAQ;mBAAC,EAAC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,CAAC,EAAC"} /**************************************************************************************************** * PARTIAL FILE: two_way_binding_longhand.d.ts ****************************************************************************************************/ diff --git a/packages/compiler-cli/test/ngtsc/authoring_inputs_spec.ts b/packages/compiler-cli/test/ngtsc/authoring_inputs_spec.ts index 34831b88d8b6..379a3130a75a 100644 --- a/packages/compiler-cli/test/ngtsc/authoring_inputs_spec.ts +++ b/packages/compiler-cli/test/ngtsc/authoring_inputs_spec.ts @@ -467,5 +467,25 @@ runInEachFileSystem(() => { const js = env.getContents('test.js'); expect(js).toContain('inputs: { data: [1, "data"] }'); }); + + it('should capture signal inputs in the setClassMetadata call', () => { + env.write( + 'test.ts', + ` + import {Directive, input} from '@angular/core'; + + @Directive() + export class TestDir { + data = input('test'); + } + `, + ); + env.driveMain(); + const js = env.getContents('test.js'); + expect(js).toContain('import * as i0 from "@angular/core";'); + expect(js).toContain(`i0.ɵsetClassMetadata(TestDir, [{ + type: Directive + }], null, { data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }] });`); + }); }); }); diff --git a/packages/compiler-cli/test/ngtsc/authoring_models_spec.ts b/packages/compiler-cli/test/ngtsc/authoring_models_spec.ts index 13fe7023145a..f36d1d91bbc2 100644 --- a/packages/compiler-cli/test/ngtsc/authoring_models_spec.ts +++ b/packages/compiler-cli/test/ngtsc/authoring_models_spec.ts @@ -776,5 +776,30 @@ runInEachFileSystem(() => { expect(diagnostics.length).toBe(0); }); }); + + it('should capture model input/output pair in the setClassMetadata call', () => { + env.write( + 'test.ts', + ` + import {Directive, model} from '@angular/core'; + + @Directive() + export class TestDir { + value = model(1); + } + `, + ); + env.driveMain(); + + const js = env.getContents('test.js'); + expect(js).toContain('import * as i0 from "@angular/core";'); + expect(js).toContain( + `i0.ɵsetClassMetadata(TestDir, [{ + type: Directive + }], null, { value: [` + + `{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, ` + + `{ type: i0.Output, args: ["valueChange"] }] });`, + ); + }); }); }); diff --git a/packages/compiler-cli/test/ngtsc/authoring_outputs_spec.ts b/packages/compiler-cli/test/ngtsc/authoring_outputs_spec.ts index b3eb034ea66d..48a3dad4dad5 100644 --- a/packages/compiler-cli/test/ngtsc/authoring_outputs_spec.ts +++ b/packages/compiler-cli/test/ngtsc/authoring_outputs_spec.ts @@ -324,5 +324,26 @@ runInEachFileSystem(() => { expect(diagnostics.length).toBe(0); }); }); + + it('should capture initializer-based outputs in the setClassMetadata call', () => { + env.write( + 'test.ts', + ` + import {Component, output} from '@angular/core'; + + @Component({selector: 'test', template: ''}) + export class TestDir { + click = output(); + } + `, + ); + env.driveMain(); + const js = env.getContents('test.js'); + expect(js).toContain('import * as i0 from "@angular/core";'); + expect(js).toContain(`i0.ɵsetClassMetadata(TestDir, [{ + type: Component, + args: [{ selector: 'test', template: '' }] + }], null, { click: [{ type: i0.Output, args: ["click"] }] });`); + }); }); }); diff --git a/packages/compiler-cli/test/ngtsc/authoring_queries_spec.ts b/packages/compiler-cli/test/ngtsc/authoring_queries_spec.ts index 6a9ca68e80af..1835642d7730 100644 --- a/packages/compiler-cli/test/ngtsc/authoring_queries_spec.ts +++ b/packages/compiler-cli/test/ngtsc/authoring_queries_spec.ts @@ -389,6 +389,146 @@ runInEachFileSystem(() => { const diagnostics = env.driveDiagnostics(); expect(diagnostics.length).toBe(0); }); + + it('should capture a viewChild query in the setClasMetadata call', () => { + env.write( + 'test.ts', + ` + import {Component, viewChild} from '@angular/core'; + + @Component({selector: 'test', template: ''}) + export class TestDir { + el = viewChild('myLocator'); + } + `, + ); + env.driveMain(); + + const js = env.getContents('test.js'); + expect(js).toContain('import * as i0 from "@angular/core";'); + expect(js).toContain(`i0.ɵsetClassMetadata(TestDir, [{ + type: Component, + args: [{ selector: 'test', template: '' }] + }], null, { el: [{ type: i0.ViewChild, args: ['myLocator', { isSignal: true }] }] });`); + }); + + it('should capture a viewChildren query in the setClasMetadata call', () => { + env.write( + 'test.ts', + ` + import {Component, viewChildren} from '@angular/core'; + + @Component({selector: 'test', template: ''}) + export class TestDir { + el = viewChildren('myLocator'); + } + `, + ); + env.driveMain(); + + const js = env.getContents('test.js'); + expect(js).toContain('import * as i0 from "@angular/core";'); + expect(js).toContain(`i0.ɵsetClassMetadata(TestDir, [{ + type: Component, + args: [{ selector: 'test', template: '' }] + }], null, { el: [{ type: i0.ViewChildren, args: ['myLocator', { isSignal: true }] }] });`); + }); + + it('should capture a contentChild query in the setClasMetadata call', () => { + env.write( + 'test.ts', + ` + import {Component, contentChild} from '@angular/core'; + + @Component({selector: 'test', template: ''}) + export class TestDir { + el = contentChild('myLocator'); + } + `, + ); + env.driveMain(); + + const js = env.getContents('test.js'); + expect(js).toContain('import * as i0 from "@angular/core";'); + expect(js).toContain(`i0.ɵsetClassMetadata(TestDir, [{ + type: Component, + args: [{ selector: 'test', template: '' }] + }], null, { el: [{ type: i0.ContentChild, args: ['myLocator', { isSignal: true }] }] });`); + }); + + it('should capture a contentChildren query in the setClasMetadata call', () => { + env.write( + 'test.ts', + ` + import {Component, contentChildren} from '@angular/core'; + + @Component({selector: 'test', template: ''}) + export class TestDir { + el = contentChildren('myLocator'); + } + `, + ); + env.driveMain(); + + const js = env.getContents('test.js'); + expect(js).toContain('import * as i0 from "@angular/core";'); + expect(js).toContain(`i0.ɵsetClassMetadata(TestDir, [{ + type: Component, + args: [{ selector: 'test', template: '' }] + }], null, { el: [{ type: i0.ContentChildren, args: ['myLocator', { isSignal: true }] }] });`); + }); + + it('should capture a query with options in a setClassMetadata call', () => { + env.write( + 'test.ts', + ` + import {Component, viewChild, ElementRef} from '@angular/core'; + + @Component({selector: 'test', template: ''}) + export class TestDir { + el = viewChild('myLocator', {read: ElementRef}); + } + `, + ); + env.driveMain(); + + const js = env.getContents('test.js'); + expect(js).toContain('import * as i0 from "@angular/core";'); + expect(js).toContain( + `i0.ɵsetClassMetadata(TestDir, [{ + type: Component, + args: [{ selector: 'test', template: '' }] + }], null, { el: [{ type: i0.ViewChild, ` + + `args: ['myLocator', Object.assign({ read: ElementRef }, { isSignal: true })] }] });`, + ); + }); + + it('should wrap reference in query as a forwardRef in the setClassMetadata call', () => { + env.write( + 'test.ts', + ` + import {Component, Directive, viewChild} from '@angular/core'; + + @Component({selector: 'test', template: ''}) + export class TestDir { + el = viewChild(Dep); + } + + @Directive({selector: '[dep]'}) + export class Dep {} + `, + ); + env.driveMain(); + + const js = env.getContents('test.js'); + expect(js).toContain('import * as i0 from "@angular/core";'); + expect(js).toContain( + `i0.ɵsetClassMetadata(TestDir, [{ + type: Component, + args: [{ selector: 'test', template: '' }] + }], null, { el: [{ type: i0.ViewChild, args: [i0.forwardRef(() => Dep), { isSignal: true }] }] });`, + ); + }); }); }); });