/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/compiler/src/property_mapping.ts
211 строк
7 KB
Kristiyan Kostadinov
refactor(compiler-cli): move ClassPropertyMapping into compiler
03 апр 2026, 19:44
03 апр 2026, 19:44
d15ceff
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ /** * The name of a class property that backs an input or output declared by a directive or component. * * This type exists for documentation only. */ export type ClassPropertyName = string; /** * The name by which an input or output of a directive or component is bound in an Angular template. * * This type exists for documentation only. */ export type BindingPropertyName = string; /** * An input or output of a directive that has both a named JavaScript class property on a component * or directive class, as well as an Angular template property name used for binding. */ export interface InputOrOutput { /** * The name of the JavaScript property on the component or directive instance for this input or * output. */ readonly classPropertyName: ClassPropertyName; /** * The property name used to bind this input or output in an Angular template. */ readonly bindingPropertyName: BindingPropertyName; /** Whether the input or output is signal based. */ readonly isSignal: boolean; } /** * A mapping of component property and template binding property names, for example containing the * inputs of a particular directive or component. * * A single component property has exactly one input/output annotation (and therefore one binding * property name) associated with it, but the same binding property name may be shared across many * component property names. * * Allows bidirectional querying of the mapping - looking up all inputs/outputs with a given * property name, or mapping from a specific class property to its binding property name. */ export class ClassPropertyMapping<T extends InputOrOutput = InputOrOutput> { /** * Mapping from class property names to the single `InputOrOutput` for that class property. */ private forwardMap: Map<ClassPropertyName, T>; /** * Mapping from property names to one or more `InputOrOutput`s which share that name. */ private reverseMap: Map<BindingPropertyName, T[]>; private constructor(forwardMap: Map<ClassPropertyName, T>) { this.forwardMap = forwardMap; this.reverseMap = reverseMapFromForwardMap(forwardMap); } /** * Construct a `ClassPropertyMapping` with no entries. */ static empty<T extends InputOrOutput>(): ClassPropertyMapping<T> { return new ClassPropertyMapping(new Map()); } /** * Construct a `ClassPropertyMapping` from a primitive JS object which maps class property names * to either binding property names or an array that contains both names, which is used in on-disk * metadata formats (e.g. in .d.ts files). */ static fromMappedObject<T extends InputOrOutput>(obj: { [classPropertyName: string]: BindingPropertyName | T; }): ClassPropertyMapping<T> { const forwardMap = new Map<ClassPropertyName, T>(); for (const classPropertyName of Object.keys(obj)) { const value = obj[classPropertyName]; let inputOrOutput: InputOrOutput; if (typeof value === 'string') { inputOrOutput = { classPropertyName, bindingPropertyName: value, // Inputs/outputs not captured via an explicit `InputOrOutput` mapping // value are always considered non-signal. This is the string shorthand. isSignal: false, }; } else { inputOrOutput = value; } forwardMap.set(classPropertyName, inputOrOutput as T); } return new ClassPropertyMapping(forwardMap); } /** * Merge two mappings into one, with class properties from `b` taking precedence over class * properties from `a`. */ static merge<T extends InputOrOutput>( a: ClassPropertyMapping<T>, b: ClassPropertyMapping<T>, ): ClassPropertyMapping<T> { const forwardMap = new Map<ClassPropertyName, T>(a.forwardMap.entries()); for (const [classPropertyName, inputOrOutput] of b.forwardMap) { forwardMap.set(classPropertyName, inputOrOutput); } return new ClassPropertyMapping(forwardMap); } /** * All class property names mapped in this mapping. */ get classPropertyNames(): ClassPropertyName[] { return Array.from(this.forwardMap.keys()); } /** * All binding property names mapped in this mapping. */ get propertyNames(): BindingPropertyName[] { return Array.from(this.reverseMap.keys()); } /** * Check whether a mapping for the given property name exists. */ hasBindingPropertyName(propertyName: BindingPropertyName): boolean { return this.reverseMap.has(propertyName); } /** * Lookup all `InputOrOutput`s that use this `propertyName`. */ getByBindingPropertyName(propertyName: string): ReadonlyArray<T> | null { return this.reverseMap.has(propertyName) ? this.reverseMap.get(propertyName)! : null; } /** * Lookup the `InputOrOutput` associated with a `classPropertyName`. */ getByClassPropertyName(classPropertyName: string): T | null { return this.forwardMap.has(classPropertyName) ? this.forwardMap.get(classPropertyName)! : null; } /** * Convert this mapping to a primitive JS object which maps each class property directly to the * binding property name associated with it. */ toDirectMappedObject(): {[classPropertyName: string]: BindingPropertyName} { const obj: {[classPropertyName: string]: BindingPropertyName} = {}; for (const [classPropertyName, inputOrOutput] of this.forwardMap) { obj[classPropertyName] = inputOrOutput.bindingPropertyName; } return obj; } /** * Convert this mapping to a primitive JS object which maps each class property either to itself * (for cases where the binding property name is the same) or to an array which contains both * names if they differ. * * This object format is used when mappings are serialized (for example into .d.ts files). * @param transform Function used to transform the values of the generated map. */ toJointMappedObject<O = T>(transform: (value: T) => O): {[classPropertyName: string]: O} { const obj: {[classPropertyName: string]: O} = {}; for (const [classPropertyName, inputOrOutput] of this.forwardMap) { obj[classPropertyName] = transform(inputOrOutput); } return obj; } /** * Implement the iterator protocol and return entry objects which contain the class and binding * property names (and are useful for destructuring). */ *[Symbol.iterator](): IterableIterator<T> { for (const inputOrOutput of this.forwardMap.values()) { yield inputOrOutput; } } } function reverseMapFromForwardMap<T extends InputOrOutput>( forwardMap: Map<ClassPropertyName, T>, ): Map<BindingPropertyName, T[]> { const reverseMap = new Map<BindingPropertyName, T[]>(); for (const [_, inputOrOutput] of forwardMap) { if (!reverseMap.has(inputOrOutput.bindingPropertyName)) { reverseMap.set(inputOrOutput.bindingPropertyName, []); } reverseMap.get(inputOrOutput.bindingPropertyName)!.push(inputOrOutput); } return reverseMap; }