angular/packages/compiler/src/compiler_facade_interface.ts
Kristiyan Kostadinov f6da091228 refactor(compiler): introduce compiler infrastructure for input transforms (#50225)
Adds the necessary compiler changes to support input transform functions. The compiler output has changed in the following ways:

### Directive handler
The directive handler now extracts a reference to the input transform function and it resolves the type of its first parameter. It also asserts that the type can be referenced in the compiled output and that it doesn't clash with any pre-existing `ngAcceptInputType_` members.

### .d.ts
In the generated declaration files the compiler now inserts an `ngAcceptInputType_` member for each input with a `transform` function. The member's type corresponds to the type of the first parameter of the function, e.g.

```typescript
// foo.directive.ts
@Directive()
export class Foo {
  @Input({transform: (incomingValue: string) => parseInt(incomingValue)}) value: number;
}

// foo.directive.d.ts
export class Foo {
  value: number;
  static ngAcceptInputType_value: string;
}
```

### Type check block
If an input has `transform` function, the TCB will use the type of its first parameter for the setter type. This uses the same infrastructure as the `ngAcceptInputType_` members.

### Directive declaration
The generated runtime directive declaration call now includes the `transform` function in the `inputs` map, if the input is being transformed. The function will be picked up by the runtime in the next commit to do the actual transformation.

```typescript
// foo.directive.ts
@Directive()
export class Foo {
  @Input({transform: (incomingValue: string) => parseInt(incomingValue)}) value: number;
}

// foo.directive.js
export class Foo {
  ɵdir = ɵɵdefineDirective({
    inputs: {
      value: ['value', 'value', incomingValue => parseInt(incomingValue)]
    }
  });
}
```

PR Close #50225
2023-05-22 14:48:02 +00:00

358 lines
9.9 KiB
TypeScript

/**
* @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.io/license
*/
/**
* A set of interfaces which are shared between `@angular/core` and `@angular/compiler` to allow
* for late binding of `@angular/compiler` for JIT purposes.
*
* This file has two copies. Please ensure that they are in sync:
* - packages/compiler/src/compiler_facade_interface.ts (main)
* - packages/core/src/compiler/compiler_facade_interface.ts (replica)
*
* Please ensure that the two files are in sync using this command:
* ```
* cp packages/compiler/src/compiler_facade_interface.ts \
* packages/core/src/compiler/compiler_facade_interface.ts
* ```
*/
export interface ExportedCompilerFacade {
ɵcompilerFacade: CompilerFacade;
}
export interface CompilerFacade {
compilePipe(angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3PipeMetadataFacade):
any;
compilePipeDeclaration(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, declaration: R3DeclarePipeFacade): any;
compileInjectable(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3InjectableMetadataFacade): any;
compileInjectableDeclaration(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3DeclareInjectableFacade): any;
compileInjector(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3InjectorMetadataFacade): any;
compileInjectorDeclaration(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
declaration: R3DeclareInjectorFacade): any;
compileNgModule(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3NgModuleMetadataFacade): any;
compileNgModuleDeclaration(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
declaration: R3DeclareNgModuleFacade): any;
compileDirective(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3DirectiveMetadataFacade): any;
compileDirectiveDeclaration(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
declaration: R3DeclareDirectiveFacade): any;
compileComponent(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3ComponentMetadataFacade): any;
compileComponentDeclaration(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
declaration: R3DeclareComponentFacade): any;
compileFactory(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3FactoryDefMetadataFacade): any;
compileFactoryDeclaration(
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3DeclareFactoryFacade): any;
createParseSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan;
FactoryTarget: typeof FactoryTarget;
// Note that we do not use `{new(): ResourceLoader}` here because
// the resource loader class is abstract and not constructable.
ResourceLoader: Function&{prototype: ResourceLoader};
}
export interface CoreEnvironment {
[name: string]: Function;
}
export type ResourceLoader = {
get(url: string): Promise<string>|string;
};
export type InputMap = {
[key: string]: {
bindingPropertyName: string,
classPropertyName: string,
required: boolean,
transformFunction: InputTransformFunction,
};
};
export type Provider = unknown;
export type Type = Function;
export type OpaqueValue = unknown;
export type InputTransformFunction = any;
export enum FactoryTarget {
Directive = 0,
Component = 1,
Injectable = 2,
Pipe = 3,
NgModule = 4,
}
export interface R3DependencyMetadataFacade {
token: OpaqueValue;
attribute: string|null;
host: boolean;
optional: boolean;
self: boolean;
skipSelf: boolean;
}
export interface R3DeclareDependencyMetadataFacade {
token: OpaqueValue;
attribute?: boolean;
host?: boolean;
optional?: boolean;
self?: boolean;
skipSelf?: boolean;
}
export interface R3PipeMetadataFacade {
name: string;
type: Type;
pipeName: string;
pure: boolean;
isStandalone: boolean;
}
export interface R3InjectableMetadataFacade {
name: string;
type: Type;
typeArgumentCount: number;
providedIn?: Type|'root'|'platform'|'any'|null;
useClass?: OpaqueValue;
useFactory?: OpaqueValue;
useExisting?: OpaqueValue;
useValue?: OpaqueValue;
deps?: R3DependencyMetadataFacade[];
}
export interface R3NgModuleMetadataFacade {
type: Type;
bootstrap: Function[];
declarations: Function[];
imports: Function[];
exports: Function[];
schemas: {name: string}[]|null;
id: string|null;
}
export interface R3InjectorMetadataFacade {
name: string;
type: Type;
providers: Provider[];
imports: OpaqueValue[];
}
export interface R3HostDirectiveMetadataFacade {
directive: Type;
inputs?: string[];
outputs?: string[];
}
export interface R3DirectiveMetadataFacade {
name: string;
type: Type;
typeSourceSpan: ParseSourceSpan;
selector: string|null;
queries: R3QueryMetadataFacade[];
host: {[key: string]: string};
propMetadata: {[key: string]: OpaqueValue[]};
lifecycle: {usesOnChanges: boolean;};
inputs: (string|{name: string, alias?: string, required?: boolean})[];
outputs: string[];
usesInheritance: boolean;
exportAs: string[]|null;
providers: Provider[]|null;
viewQueries: R3QueryMetadataFacade[];
isStandalone: boolean;
hostDirectives: R3HostDirectiveMetadataFacade[]|null;
isSignal: boolean;
}
export interface R3ComponentMetadataFacade extends R3DirectiveMetadataFacade {
template: string;
preserveWhitespaces: boolean;
animations: OpaqueValue[]|undefined;
declarations: R3TemplateDependencyFacade[];
styles: string[];
encapsulation: ViewEncapsulation;
viewProviders: Provider[]|null;
interpolation?: [string, string];
changeDetection?: ChangeDetectionStrategy;
}
export interface R3DeclareDirectiveFacade {
selector?: string;
type: Type;
inputs?: {
[classPropertyName: string]: string|
[bindingPropertyName: string,
classPropertyName: string, transformFunction?: InputTransformFunction]
};
outputs?: {[classPropertyName: string]: string};
host?: {
attributes?: {[key: string]: OpaqueValue};
listeners?: {[key: string]: string};
properties?: {[key: string]: string};
classAttribute?: string;
styleAttribute?: string;
};
queries?: R3DeclareQueryMetadataFacade[];
viewQueries?: R3DeclareQueryMetadataFacade[];
providers?: OpaqueValue;
exportAs?: string[];
usesInheritance?: boolean;
usesOnChanges?: boolean;
isStandalone?: boolean;
isSignal?: boolean;
hostDirectives?: R3HostDirectiveMetadataFacade[]|null;
}
export interface R3DeclareComponentFacade extends R3DeclareDirectiveFacade {
template: string;
isInline?: boolean;
styles?: string[];
// Post-standalone libraries use a unified dependencies field.
dependencies?: R3DeclareTemplateDependencyFacade[];
// Pre-standalone libraries have separate component/directive/pipe fields:
components?: R3DeclareDirectiveDependencyFacade[];
directives?: R3DeclareDirectiveDependencyFacade[];
pipes?: {[pipeName: string]: OpaqueValue|(() => OpaqueValue)};
viewProviders?: OpaqueValue;
animations?: OpaqueValue;
changeDetection?: ChangeDetectionStrategy;
encapsulation?: ViewEncapsulation;
interpolation?: [string, string];
preserveWhitespaces?: boolean;
}
export type R3DeclareTemplateDependencyFacade = {
kind: string
}&(R3DeclareDirectiveDependencyFacade|R3DeclarePipeDependencyFacade|
R3DeclareNgModuleDependencyFacade);
export interface R3DeclareDirectiveDependencyFacade {
kind?: 'directive'|'component';
selector: string;
type: OpaqueValue|(() => OpaqueValue);
inputs?: string[];
outputs?: string[];
exportAs?: string[];
}
export interface R3DeclarePipeDependencyFacade {
kind?: 'pipe';
name: string;
type: OpaqueValue|(() => OpaqueValue);
}
export interface R3DeclareNgModuleDependencyFacade {
kind: 'ngmodule';
type: OpaqueValue|(() => OpaqueValue);
}
export enum R3TemplateDependencyKind {
Directive = 0,
Pipe = 1,
NgModule = 2,
}
export interface R3TemplateDependencyFacade {
kind: R3TemplateDependencyKind;
type: OpaqueValue|(() => OpaqueValue);
}
export interface R3FactoryDefMetadataFacade {
name: string;
type: Type;
typeArgumentCount: number;
deps: R3DependencyMetadataFacade[]|null;
target: FactoryTarget;
}
export interface R3DeclareFactoryFacade {
type: Type;
deps: R3DeclareDependencyMetadataFacade[]|'invalid'|null;
target: FactoryTarget;
}
export interface R3DeclareInjectableFacade {
type: Type;
providedIn?: Type|'root'|'platform'|'any'|null;
useClass?: OpaqueValue;
useFactory?: OpaqueValue;
useExisting?: OpaqueValue;
useValue?: OpaqueValue;
deps?: R3DeclareDependencyMetadataFacade[];
}
export enum ViewEncapsulation {
Emulated = 0,
// Historically the 1 value was for `Native` encapsulation which has been removed as of v11.
None = 2,
ShadowDom = 3
}
export type ChangeDetectionStrategy = number;
export interface R3QueryMetadataFacade {
propertyName: string;
first: boolean;
predicate: OpaqueValue|string[];
descendants: boolean;
emitDistinctChangesOnly: boolean;
read: OpaqueValue|null;
static: boolean;
}
export interface R3DeclareQueryMetadataFacade {
propertyName: string;
first?: boolean;
predicate: OpaqueValue|string[];
descendants?: boolean;
read?: OpaqueValue;
static?: boolean;
emitDistinctChangesOnly?: boolean;
}
export interface R3DeclareInjectorFacade {
type: Type;
imports?: OpaqueValue[];
providers?: OpaqueValue[];
}
export interface R3DeclareNgModuleFacade {
type: Type;
bootstrap?: OpaqueValue[]|(() => OpaqueValue[]);
declarations?: OpaqueValue[]|(() => OpaqueValue[]);
imports?: OpaqueValue[]|(() => OpaqueValue[]);
exports?: OpaqueValue[]|(() => OpaqueValue[]);
schemas?: OpaqueValue[];
id?: OpaqueValue;
}
export interface R3DeclarePipeFacade {
type: Type;
name: string;
pure?: boolean;
isStandalone?: boolean;
}
export interface ParseSourceSpan {
start: any;
end: any;
details: any;
fullStart: any;
}