2018-10-24 19:02:25 -04:00
|
|
|
/**
|
|
|
|
* @license
|
2020-05-19 15:08:49 -04:00
|
|
|
* Copyright Google LLC All Rights Reserved.
|
2018-10-24 19:02:25 -04:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2020-12-13 14:25:20 -05:00
|
|
|
import {CompilerFacade, CoreEnvironment, ExportedCompilerFacade, OpaqueValue, R3ComponentMetadataFacade, R3DeclareDirectiveFacade, R3DeclareQueryMetadataFacade, R3DependencyMetadataFacade, R3DirectiveMetadataFacade, R3FactoryDefMetadataFacade, R3InjectableMetadataFacade, R3InjectorMetadataFacade, R3NgModuleMetadataFacade, R3PipeMetadataFacade, R3QueryMetadataFacade, StringMap, StringMapWithRename} from './compiler_facade_interface';
|
2018-10-24 19:02:25 -04:00
|
|
|
import {ConstantPool} from './constant_pool';
|
|
|
|
import {HostBinding, HostListener, Input, Output, Type} from './core';
|
2019-09-01 06:26:04 -04:00
|
|
|
import {Identifiers} from './identifiers';
|
2018-10-24 19:02:25 -04:00
|
|
|
import {compileInjectable} from './injectable_compiler_2';
|
2018-11-29 19:21:16 -05:00
|
|
|
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './ml_parser/interpolation_config';
|
2019-02-08 17:10:19 -05:00
|
|
|
import {DeclareVarStmt, Expression, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast';
|
|
|
|
import {JitEvaluator} from './output/output_jit';
|
2019-01-24 20:25:46 -05:00
|
|
|
import {ParseError, ParseSourceSpan, r3JitTypeSourceSpan} from './parse_util';
|
2020-04-08 13:14:18 -04:00
|
|
|
import {compileFactoryFunction, R3DependencyMetadata, R3FactoryTarget, R3ResolvedDependencyType} from './render3/r3_factory';
|
2019-02-08 17:10:19 -05:00
|
|
|
import {R3JitReflector} from './render3/r3_jit';
|
2020-04-08 13:14:18 -04:00
|
|
|
import {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata} from './render3/r3_module_compiler';
|
|
|
|
import {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
|
2018-10-24 19:02:25 -04:00
|
|
|
import {R3Reference} from './render3/util';
|
2020-12-13 14:25:20 -05:00
|
|
|
import {R3ComponentMetadata, R3DirectiveMetadata, R3HostMetadata, R3QueryMetadata} from './render3/view/api';
|
2020-04-08 13:14:18 -04:00
|
|
|
import {compileComponentFromMetadata, compileDirectiveFromMetadata, ParsedHostBindings, parseHostBindings, verifyHostBindings} from './render3/view/compiler';
|
2018-10-24 19:02:25 -04:00
|
|
|
import {makeBindingParser, parseTemplate} from './render3/view/template';
|
2019-03-03 12:19:27 -05:00
|
|
|
import {ResourceLoader} from './resource_loader';
|
2018-11-22 09:38:28 -05:00
|
|
|
import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry';
|
2018-10-24 19:02:25 -04:00
|
|
|
|
|
|
|
export class CompilerFacadeImpl implements CompilerFacade {
|
|
|
|
R3ResolvedDependencyType = R3ResolvedDependencyType as any;
|
2019-10-03 15:54:49 -04:00
|
|
|
R3FactoryTarget = R3FactoryTarget as any;
|
2019-03-03 12:19:27 -05:00
|
|
|
ResourceLoader = ResourceLoader;
|
2018-11-22 09:38:28 -05:00
|
|
|
private elementSchemaRegistry = new DomElementSchemaRegistry();
|
2019-02-08 17:10:20 -05:00
|
|
|
|
|
|
|
constructor(private jitEvaluator = new JitEvaluator()) {}
|
2018-10-24 19:02:25 -04:00
|
|
|
|
|
|
|
compilePipe(angularCoreEnv: CoreEnvironment, sourceMapUrl: string, facade: R3PipeMetadataFacade):
|
|
|
|
any {
|
refactor(ivy): split `type` into `type`, `internalType` and `adjacentType` (#33533)
When compiling an Angular decorator (e.g. Directive), @angular/compiler
generates an 'expression' to be added as a static definition field
on the class, a 'type' which will be added for that field to the .d.ts
file, and a statement adjacent to the class that calls `setClassMetadata()`.
Previously, the same WrappedNodeExpr of the class' ts.Identifier was used
within each of this situations.
In the ngtsc case, this is proper. In the ngcc case, if the class being
compiled is within an ES5 IIFE, the outer name of the class may have
changed. Thus, the class has both an inner and outer name. The outer name
should continue to be used elsewhere in the compiler and in 'type'.
The 'expression' will live within the IIFE, the `internalType` should be used.
The adjacent statement will also live within the IIFE, the `adjacentType` should be used.
This commit introduces `ReflectionHost.getInternalNameOfClass()` and
`ReflectionHost.getAdjacentNameOfClass()`, which the compiler can use to
query for the correct name to use.
PR Close #33533
2019-11-01 12:55:09 -04:00
|
|
|
const metadata: R3PipeMetadata = {
|
2018-10-24 19:02:25 -04:00
|
|
|
name: facade.name,
|
2019-12-18 09:03:05 -05:00
|
|
|
type: wrapReference(facade.type),
|
refactor(ivy): split `type` into `type`, `internalType` and `adjacentType` (#33533)
When compiling an Angular decorator (e.g. Directive), @angular/compiler
generates an 'expression' to be added as a static definition field
on the class, a 'type' which will be added for that field to the .d.ts
file, and a statement adjacent to the class that calls `setClassMetadata()`.
Previously, the same WrappedNodeExpr of the class' ts.Identifier was used
within each of this situations.
In the ngtsc case, this is proper. In the ngcc case, if the class being
compiled is within an ES5 IIFE, the outer name of the class may have
changed. Thus, the class has both an inner and outer name. The outer name
should continue to be used elsewhere in the compiler and in 'type'.
The 'expression' will live within the IIFE, the `internalType` should be used.
The adjacent statement will also live within the IIFE, the `adjacentType` should be used.
This commit introduces `ReflectionHost.getInternalNameOfClass()` and
`ReflectionHost.getAdjacentNameOfClass()`, which the compiler can use to
query for the correct name to use.
PR Close #33533
2019-11-01 12:55:09 -04:00
|
|
|
internalType: new WrappedNodeExpr(facade.type),
|
2019-03-19 16:22:03 -04:00
|
|
|
typeArgumentCount: facade.typeArgumentCount,
|
2018-10-24 19:02:25 -04:00
|
|
|
deps: convertR3DependencyMetadataArray(facade.deps),
|
|
|
|
pipeName: facade.pipeName,
|
|
|
|
pure: facade.pure,
|
2019-08-12 02:26:20 -04:00
|
|
|
};
|
|
|
|
const res = compilePipeFromMetadata(metadata);
|
|
|
|
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
2018-10-24 19:02:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
compileInjectable(
|
|
|
|
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
|
|
|
|
facade: R3InjectableMetadataFacade): any {
|
|
|
|
const {expression, statements} = compileInjectable({
|
|
|
|
name: facade.name,
|
2019-12-18 09:03:05 -05:00
|
|
|
type: wrapReference(facade.type),
|
refactor(ivy): split `type` into `type`, `internalType` and `adjacentType` (#33533)
When compiling an Angular decorator (e.g. Directive), @angular/compiler
generates an 'expression' to be added as a static definition field
on the class, a 'type' which will be added for that field to the .d.ts
file, and a statement adjacent to the class that calls `setClassMetadata()`.
Previously, the same WrappedNodeExpr of the class' ts.Identifier was used
within each of this situations.
In the ngtsc case, this is proper. In the ngcc case, if the class being
compiled is within an ES5 IIFE, the outer name of the class may have
changed. Thus, the class has both an inner and outer name. The outer name
should continue to be used elsewhere in the compiler and in 'type'.
The 'expression' will live within the IIFE, the `internalType` should be used.
The adjacent statement will also live within the IIFE, the `adjacentType` should be used.
This commit introduces `ReflectionHost.getInternalNameOfClass()` and
`ReflectionHost.getAdjacentNameOfClass()`, which the compiler can use to
query for the correct name to use.
PR Close #33533
2019-11-01 12:55:09 -04:00
|
|
|
internalType: new WrappedNodeExpr(facade.type),
|
2018-11-09 20:58:33 -05:00
|
|
|
typeArgumentCount: facade.typeArgumentCount,
|
2018-10-24 19:02:25 -04:00
|
|
|
providedIn: computeProvidedIn(facade.providedIn),
|
|
|
|
useClass: wrapExpression(facade, USE_CLASS),
|
|
|
|
useFactory: wrapExpression(facade, USE_FACTORY),
|
|
|
|
useValue: wrapExpression(facade, USE_VALUE),
|
|
|
|
useExisting: wrapExpression(facade, USE_EXISTING),
|
|
|
|
userDeps: convertR3DependencyMetadataArray(facade.userDeps) || undefined,
|
|
|
|
});
|
|
|
|
|
2019-02-08 17:10:19 -05:00
|
|
|
return this.jitExpression(expression, angularCoreEnv, sourceMapUrl, statements);
|
2018-10-24 19:02:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
compileInjector(
|
|
|
|
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
|
|
|
|
facade: R3InjectorMetadataFacade): any {
|
|
|
|
const meta: R3InjectorMetadata = {
|
|
|
|
name: facade.name,
|
2019-12-18 09:03:05 -05:00
|
|
|
type: wrapReference(facade.type),
|
refactor(ivy): split `type` into `type`, `internalType` and `adjacentType` (#33533)
When compiling an Angular decorator (e.g. Directive), @angular/compiler
generates an 'expression' to be added as a static definition field
on the class, a 'type' which will be added for that field to the .d.ts
file, and a statement adjacent to the class that calls `setClassMetadata()`.
Previously, the same WrappedNodeExpr of the class' ts.Identifier was used
within each of this situations.
In the ngtsc case, this is proper. In the ngcc case, if the class being
compiled is within an ES5 IIFE, the outer name of the class may have
changed. Thus, the class has both an inner and outer name. The outer name
should continue to be used elsewhere in the compiler and in 'type'.
The 'expression' will live within the IIFE, the `internalType` should be used.
The adjacent statement will also live within the IIFE, the `adjacentType` should be used.
This commit introduces `ReflectionHost.getInternalNameOfClass()` and
`ReflectionHost.getAdjacentNameOfClass()`, which the compiler can use to
query for the correct name to use.
PR Close #33533
2019-11-01 12:55:09 -04:00
|
|
|
internalType: new WrappedNodeExpr(facade.type),
|
2018-10-24 19:02:25 -04:00
|
|
|
deps: convertR3DependencyMetadataArray(facade.deps),
|
|
|
|
providers: new WrappedNodeExpr(facade.providers),
|
2019-03-30 08:09:45 -04:00
|
|
|
imports: facade.imports.map(i => new WrappedNodeExpr(i)),
|
2018-10-24 19:02:25 -04:00
|
|
|
};
|
|
|
|
const res = compileInjector(meta);
|
2019-02-08 17:10:19 -05:00
|
|
|
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, res.statements);
|
2018-10-24 19:02:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
compileNgModule(
|
|
|
|
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
|
|
|
|
facade: R3NgModuleMetadataFacade): any {
|
|
|
|
const meta: R3NgModuleMetadata = {
|
2019-12-18 09:03:05 -05:00
|
|
|
type: wrapReference(facade.type),
|
refactor(ivy): split `type` into `type`, `internalType` and `adjacentType` (#33533)
When compiling an Angular decorator (e.g. Directive), @angular/compiler
generates an 'expression' to be added as a static definition field
on the class, a 'type' which will be added for that field to the .d.ts
file, and a statement adjacent to the class that calls `setClassMetadata()`.
Previously, the same WrappedNodeExpr of the class' ts.Identifier was used
within each of this situations.
In the ngtsc case, this is proper. In the ngcc case, if the class being
compiled is within an ES5 IIFE, the outer name of the class may have
changed. Thus, the class has both an inner and outer name. The outer name
should continue to be used elsewhere in the compiler and in 'type'.
The 'expression' will live within the IIFE, the `internalType` should be used.
The adjacent statement will also live within the IIFE, the `adjacentType` should be used.
This commit introduces `ReflectionHost.getInternalNameOfClass()` and
`ReflectionHost.getAdjacentNameOfClass()`, which the compiler can use to
query for the correct name to use.
PR Close #33533
2019-11-01 12:55:09 -04:00
|
|
|
internalType: new WrappedNodeExpr(facade.type),
|
|
|
|
adjacentType: new WrappedNodeExpr(facade.type),
|
2018-10-24 19:02:25 -04:00
|
|
|
bootstrap: facade.bootstrap.map(wrapReference),
|
|
|
|
declarations: facade.declarations.map(wrapReference),
|
|
|
|
imports: facade.imports.map(wrapReference),
|
|
|
|
exports: facade.exports.map(wrapReference),
|
|
|
|
emitInline: true,
|
2019-03-08 20:57:34 -05:00
|
|
|
containsForwardDecls: false,
|
2019-02-11 18:03:04 -05:00
|
|
|
schemas: facade.schemas ? facade.schemas.map(wrapReference) : null,
|
2019-05-07 22:57:55 -04:00
|
|
|
id: facade.id ? new WrappedNodeExpr(facade.id) : null,
|
2018-10-24 19:02:25 -04:00
|
|
|
};
|
|
|
|
const res = compileNgModule(meta);
|
2019-02-08 17:10:19 -05:00
|
|
|
return this.jitExpression(res.expression, angularCoreEnv, sourceMapUrl, []);
|
2018-10-24 19:02:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
compileDirective(
|
|
|
|
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
|
|
|
|
facade: R3DirectiveMetadataFacade): any {
|
2020-12-13 14:25:20 -05:00
|
|
|
const meta: R3DirectiveMetadata = convertDirectiveFacadeToMetadata(facade);
|
|
|
|
return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
|
|
|
}
|
|
|
|
|
|
|
|
compileDirectiveDeclaration(
|
|
|
|
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
|
|
|
|
declaration: R3DeclareDirectiveFacade): any {
|
|
|
|
const typeSourceSpan =
|
|
|
|
this.createParseSourceSpan('Directive', declaration.type.name, sourceMapUrl);
|
|
|
|
const meta = convertDeclareDirectiveFacadeToMetadata(declaration, typeSourceSpan);
|
|
|
|
return this.compileDirectiveFromMeta(angularCoreEnv, sourceMapUrl, meta);
|
|
|
|
}
|
|
|
|
|
|
|
|
private compileDirectiveFromMeta(
|
|
|
|
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3DirectiveMetadata): any {
|
2018-10-24 19:02:25 -04:00
|
|
|
const constantPool = new ConstantPool();
|
|
|
|
const bindingParser = makeBindingParser();
|
|
|
|
const res = compileDirectiveFromMetadata(meta, constantPool, bindingParser);
|
2019-08-12 02:26:20 -04:00
|
|
|
return this.jitExpression(
|
|
|
|
res.expression, angularCoreEnv, sourceMapUrl, constantPool.statements);
|
2018-10-24 19:02:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
compileComponent(
|
|
|
|
angularCoreEnv: CoreEnvironment, sourceMapUrl: string,
|
|
|
|
facade: R3ComponentMetadataFacade): any {
|
|
|
|
// The ConstantPool is a requirement of the JIT'er.
|
|
|
|
const constantPool = new ConstantPool();
|
|
|
|
|
2018-11-29 19:21:16 -05:00
|
|
|
const interpolationConfig = facade.interpolation ?
|
|
|
|
InterpolationConfig.fromArray(facade.interpolation) :
|
|
|
|
DEFAULT_INTERPOLATION_CONFIG;
|
2018-10-24 19:02:25 -04:00
|
|
|
// Parse the template and check for errors.
|
2018-11-29 19:21:16 -05:00
|
|
|
const template = parseTemplate(
|
|
|
|
facade.template, sourceMapUrl,
|
2019-02-08 17:10:19 -05:00
|
|
|
{preserveWhitespaces: facade.preserveWhitespaces, interpolationConfig});
|
2020-08-26 12:10:04 -04:00
|
|
|
if (template.errors !== null) {
|
2018-10-24 19:02:25 -04:00
|
|
|
const errors = template.errors.map(err => err.toString()).join(', ');
|
|
|
|
throw new Error(`Errors during JIT compilation of template for ${facade.name}: ${errors}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compile the component metadata, including template, into an expression.
|
|
|
|
// TODO(alxhub): implement inputs, outputs, queries, etc.
|
2020-11-16 12:24:37 -05:00
|
|
|
const metadata: R3ComponentMetadata = {
|
2019-08-12 02:26:20 -04:00
|
|
|
...facade as R3ComponentMetadataFacadeNoPropAndWhitespace,
|
|
|
|
...convertDirectiveFacadeToMetadata(facade),
|
|
|
|
selector: facade.selector || this.elementSchemaRegistry.getDefaultComponentElementName(),
|
|
|
|
template,
|
|
|
|
wrapDirectivesAndPipesInClosure: false,
|
2019-11-23 18:36:00 -05:00
|
|
|
styles: [...facade.styles, ...template.styles],
|
2019-08-12 02:26:20 -04:00
|
|
|
encapsulation: facade.encapsulation as any,
|
|
|
|
interpolation: interpolationConfig,
|
|
|
|
changeDetection: facade.changeDetection,
|
|
|
|
animations: facade.animations != null ? new WrappedNodeExpr(facade.animations) : null,
|
|
|
|
viewProviders: facade.viewProviders != null ? new WrappedNodeExpr(facade.viewProviders) :
|
|
|
|
null,
|
|
|
|
relativeContextFilePath: '',
|
|
|
|
i18nUseExternalIds: true,
|
|
|
|
};
|
2018-10-24 19:02:25 -04:00
|
|
|
const res = compileComponentFromMetadata(
|
2019-08-12 02:26:20 -04:00
|
|
|
metadata, constantPool, makeBindingParser(interpolationConfig));
|
|
|
|
const jitExpressionSourceMap = `ng:///${facade.name}.js`;
|
|
|
|
return this.jitExpression(
|
|
|
|
res.expression, angularCoreEnv, jitExpressionSourceMap, constantPool.statements);
|
|
|
|
}
|
|
|
|
|
|
|
|
compileFactory(
|
2019-09-01 06:26:04 -04:00
|
|
|
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3FactoryDefMetadataFacade) {
|
2019-10-03 15:54:49 -04:00
|
|
|
const factoryRes = compileFactoryFunction({
|
2019-08-12 02:26:20 -04:00
|
|
|
name: meta.name,
|
2019-12-18 09:03:05 -05:00
|
|
|
type: wrapReference(meta.type),
|
refactor(ivy): split `type` into `type`, `internalType` and `adjacentType` (#33533)
When compiling an Angular decorator (e.g. Directive), @angular/compiler
generates an 'expression' to be added as a static definition field
on the class, a 'type' which will be added for that field to the .d.ts
file, and a statement adjacent to the class that calls `setClassMetadata()`.
Previously, the same WrappedNodeExpr of the class' ts.Identifier was used
within each of this situations.
In the ngtsc case, this is proper. In the ngcc case, if the class being
compiled is within an ES5 IIFE, the outer name of the class may have
changed. Thus, the class has both an inner and outer name. The outer name
should continue to be used elsewhere in the compiler and in 'type'.
The 'expression' will live within the IIFE, the `internalType` should be used.
The adjacent statement will also live within the IIFE, the `adjacentType` should be used.
This commit introduces `ReflectionHost.getInternalNameOfClass()` and
`ReflectionHost.getAdjacentNameOfClass()`, which the compiler can use to
query for the correct name to use.
PR Close #33533
2019-11-01 12:55:09 -04:00
|
|
|
internalType: new WrappedNodeExpr(meta.type),
|
2019-08-12 02:26:20 -04:00
|
|
|
typeArgumentCount: meta.typeArgumentCount,
|
2019-09-01 06:26:04 -04:00
|
|
|
deps: convertR3DependencyMetadataArray(meta.deps),
|
|
|
|
injectFn: meta.injectFn === 'directiveInject' ? Identifiers.directiveInject :
|
|
|
|
Identifiers.inject,
|
2019-10-03 15:54:49 -04:00
|
|
|
target: meta.target,
|
2019-08-12 02:26:20 -04:00
|
|
|
});
|
2019-02-08 17:10:19 -05:00
|
|
|
return this.jitExpression(
|
2019-08-12 02:26:20 -04:00
|
|
|
factoryRes.factory, angularCoreEnv, sourceMapUrl, factoryRes.statements);
|
2018-10-24 19:02:25 -04:00
|
|
|
}
|
2019-01-24 20:25:46 -05:00
|
|
|
|
|
|
|
createParseSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan {
|
|
|
|
return r3JitTypeSourceSpan(kind, typeName, sourceUrl);
|
|
|
|
}
|
2019-02-08 17:10:19 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* JIT compiles an expression and returns the result of executing that expression.
|
|
|
|
*
|
|
|
|
* @param def the definition which will be compiled and executed to get the value to patch
|
|
|
|
* @param context an object map of @angular/core symbol names to symbols which will be available
|
|
|
|
* in the context of the compiled expression
|
|
|
|
* @param sourceUrl a URL to use for the source map of the compiled expression
|
|
|
|
* @param preStatements a collection of statements that should be evaluated before the expression.
|
|
|
|
*/
|
|
|
|
private jitExpression(
|
|
|
|
def: Expression, context: {[key: string]: any}, sourceUrl: string,
|
|
|
|
preStatements: Statement[]): any {
|
|
|
|
// The ConstantPool may contain Statements which declare variables used in the final expression.
|
|
|
|
// Therefore, its statements need to precede the actual JIT operation. The final statement is a
|
|
|
|
// declaration of $def which is set to the expression being compiled.
|
|
|
|
const statements: Statement[] = [
|
|
|
|
...preStatements,
|
|
|
|
new DeclareVarStmt('$def', def, undefined, [StmtModifier.Exported]),
|
|
|
|
];
|
|
|
|
|
|
|
|
const res = this.jitEvaluator.evaluateStatements(
|
|
|
|
sourceUrl, statements, new R3JitReflector(context), /* enableSourceMaps */ true);
|
|
|
|
return res['$def'];
|
|
|
|
}
|
2018-10-24 19:02:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// This seems to be needed to placate TS v3.0 only
|
|
|
|
type R3ComponentMetadataFacadeNoPropAndWhitespace = Pick<
|
|
|
|
R3ComponentMetadataFacade,
|
|
|
|
Exclude<Exclude<keyof R3ComponentMetadataFacade, 'preserveWhitespaces'>, 'propMetadata'>>;
|
|
|
|
|
|
|
|
const USE_CLASS = Object.keys({useClass: null})[0];
|
|
|
|
const USE_FACTORY = Object.keys({useFactory: null})[0];
|
|
|
|
const USE_VALUE = Object.keys({useValue: null})[0];
|
|
|
|
const USE_EXISTING = Object.keys({useExisting: null})[0];
|
|
|
|
|
2019-06-14 03:28:04 -04:00
|
|
|
const wrapReference = function(value: any): R3Reference {
|
2018-10-24 19:02:25 -04:00
|
|
|
const wrapped = new WrappedNodeExpr(value);
|
|
|
|
return {value: wrapped, type: wrapped};
|
|
|
|
};
|
|
|
|
|
|
|
|
function convertToR3QueryMetadata(facade: R3QueryMetadataFacade): R3QueryMetadata {
|
|
|
|
return {
|
|
|
|
...facade,
|
|
|
|
predicate: Array.isArray(facade.predicate) ? facade.predicate :
|
|
|
|
new WrappedNodeExpr(facade.predicate),
|
|
|
|
read: facade.read ? new WrappedNodeExpr(facade.read) : null,
|
2019-02-18 20:33:59 -05:00
|
|
|
static: facade.static
|
2018-10-24 19:02:25 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-12-13 14:25:20 -05:00
|
|
|
function convertQueryDeclarationToMetadata(declaration: R3DeclareQueryMetadataFacade):
|
|
|
|
R3QueryMetadata {
|
|
|
|
return {
|
|
|
|
propertyName: declaration.propertyName,
|
|
|
|
first: declaration.first ?? false,
|
|
|
|
predicate: Array.isArray(declaration.predicate) ? declaration.predicate :
|
|
|
|
new WrappedNodeExpr(declaration.predicate),
|
|
|
|
descendants: declaration.descendants ?? false,
|
|
|
|
read: declaration.read ? new WrappedNodeExpr(declaration.read) : null,
|
|
|
|
static: declaration.static ?? false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-10-24 19:02:25 -04:00
|
|
|
function convertDirectiveFacadeToMetadata(facade: R3DirectiveMetadataFacade): R3DirectiveMetadata {
|
|
|
|
const inputsFromMetadata = parseInputOutputs(facade.inputs || []);
|
|
|
|
const outputsFromMetadata = parseInputOutputs(facade.outputs || []);
|
|
|
|
const propMetadata = facade.propMetadata;
|
|
|
|
const inputsFromType: StringMapWithRename = {};
|
|
|
|
const outputsFromType: StringMap = {};
|
|
|
|
for (const field in propMetadata) {
|
|
|
|
if (propMetadata.hasOwnProperty(field)) {
|
|
|
|
propMetadata[field].forEach(ann => {
|
|
|
|
if (isInput(ann)) {
|
|
|
|
inputsFromType[field] =
|
|
|
|
ann.bindingPropertyName ? [ann.bindingPropertyName, field] : field;
|
|
|
|
} else if (isOutput(ann)) {
|
|
|
|
outputsFromType[field] = ann.bindingPropertyName || field;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
...facade as R3DirectiveMetadataFacadeNoPropAndWhitespace,
|
2019-01-24 20:25:46 -05:00
|
|
|
typeSourceSpan: facade.typeSourceSpan,
|
2019-12-18 09:03:05 -05:00
|
|
|
type: wrapReference(facade.type),
|
refactor(ivy): split `type` into `type`, `internalType` and `adjacentType` (#33533)
When compiling an Angular decorator (e.g. Directive), @angular/compiler
generates an 'expression' to be added as a static definition field
on the class, a 'type' which will be added for that field to the .d.ts
file, and a statement adjacent to the class that calls `setClassMetadata()`.
Previously, the same WrappedNodeExpr of the class' ts.Identifier was used
within each of this situations.
In the ngtsc case, this is proper. In the ngcc case, if the class being
compiled is within an ES5 IIFE, the outer name of the class may have
changed. Thus, the class has both an inner and outer name. The outer name
should continue to be used elsewhere in the compiler and in 'type'.
The 'expression' will live within the IIFE, the `internalType` should be used.
The adjacent statement will also live within the IIFE, the `adjacentType` should be used.
This commit introduces `ReflectionHost.getInternalNameOfClass()` and
`ReflectionHost.getAdjacentNameOfClass()`, which the compiler can use to
query for the correct name to use.
PR Close #33533
2019-11-01 12:55:09 -04:00
|
|
|
internalType: new WrappedNodeExpr(facade.type),
|
2018-10-24 19:02:25 -04:00
|
|
|
deps: convertR3DependencyMetadataArray(facade.deps),
|
2019-04-27 03:33:10 -04:00
|
|
|
host: extractHostBindings(facade.propMetadata, facade.typeSourceSpan, facade.host),
|
2018-10-24 19:02:25 -04:00
|
|
|
inputs: {...inputsFromMetadata, ...inputsFromType},
|
|
|
|
outputs: {...outputsFromMetadata, ...outputsFromType},
|
2018-11-12 21:02:47 -05:00
|
|
|
queries: facade.queries.map(convertToR3QueryMetadata),
|
2018-10-24 19:02:25 -04:00
|
|
|
providers: facade.providers != null ? new WrappedNodeExpr(facade.providers) : null,
|
2019-03-13 14:30:38 -04:00
|
|
|
viewQueries: facade.viewQueries.map(convertToR3QueryMetadata),
|
feat(ngcc): add a migration for undecorated child classes (#33362)
In Angular View Engine, there are two kinds of decorator inheritance:
1) both the parent and child classes have decorators
This case is supported by InheritDefinitionFeature, which merges some fields
of the definitions (such as the inputs or queries).
2) only the parent class has a decorator
If the child class is missing a decorator, the compiler effectively behaves
as if the parent class' decorator is applied to the child class as well.
This is the "undecorated child" scenario, and this commit adds a migration
to ngcc to support this pattern in Ivy.
This migration has 2 phases. First, the NgModules of the application are
scanned for classes in 'declarations' which are missing decorators, but
whose base classes do have decorators. These classes are the undecorated
children. This scan is performed recursively, so even if a declared class
has a base class that itself inherits a decorator, this case is handled.
Next, a synthetic decorator (either @Component or @Directive) is created
on the child class. This decorator copies some critical information such
as 'selector' and 'exportAs', as well as supports any decorated fields
(@Input, etc). A flag is passed to the decorator compiler which causes a
special feature `CopyDefinitionFeature` to be included on the compiled
definition. This feature copies at runtime the remaining aspects of the
parent definition which `InheritDefinitionFeature` does not handle,
completing the "full" inheritance of the child class' decorator from its
parent class.
PR Close #33362
2019-10-23 15:00:49 -04:00
|
|
|
fullInheritance: false,
|
2018-10-24 19:02:25 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-12-13 14:25:20 -05:00
|
|
|
function convertDeclareDirectiveFacadeToMetadata(
|
|
|
|
declaration: R3DeclareDirectiveFacade, typeSourceSpan: ParseSourceSpan): R3DirectiveMetadata {
|
|
|
|
return {
|
|
|
|
name: declaration.type.name,
|
|
|
|
type: wrapReference(declaration.type),
|
|
|
|
typeSourceSpan,
|
|
|
|
internalType: new WrappedNodeExpr(declaration.type),
|
|
|
|
selector: declaration.selector ?? null,
|
|
|
|
inputs: declaration.inputs ?? {},
|
|
|
|
outputs: declaration.outputs ?? {},
|
|
|
|
host: convertHostDeclarationToMetadata(declaration.host),
|
|
|
|
queries: (declaration.queries ?? []).map(convertQueryDeclarationToMetadata),
|
|
|
|
viewQueries: (declaration.viewQueries ?? []).map(convertQueryDeclarationToMetadata),
|
|
|
|
providers: declaration.providers !== undefined ? new WrappedNodeExpr(declaration.providers) :
|
|
|
|
null,
|
|
|
|
exportAs: declaration.exportAs ?? null,
|
|
|
|
usesInheritance: declaration.usesInheritance ?? false,
|
|
|
|
lifecycle: {usesOnChanges: declaration.usesOnChanges ?? false},
|
|
|
|
deps: null,
|
|
|
|
typeArgumentCount: 0,
|
|
|
|
fullInheritance: false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function convertHostDeclarationToMetadata(host: R3DeclareDirectiveFacade['host'] = {}):
|
|
|
|
R3HostMetadata {
|
|
|
|
return {
|
|
|
|
attributes: convertOpaqueValuesToExpressions(host.attributes ?? {}),
|
|
|
|
listeners: host.listeners ?? {},
|
|
|
|
properties: host.properties ?? {},
|
|
|
|
specialAttributes: {
|
|
|
|
classAttr: host.classAttribute,
|
|
|
|
styleAttr: host.styleAttribute,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function convertOpaqueValuesToExpressions(obj: {[key: string]: OpaqueValue}):
|
|
|
|
{[key: string]: WrappedNodeExpr<unknown>} {
|
|
|
|
const result: {[key: string]: WrappedNodeExpr<unknown>} = {};
|
|
|
|
for (const key of Object.keys(obj)) {
|
|
|
|
result[key] = new WrappedNodeExpr(obj[key]);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-10-24 19:02:25 -04:00
|
|
|
// This seems to be needed to placate TS v3.0 only
|
|
|
|
type R3DirectiveMetadataFacadeNoPropAndWhitespace =
|
|
|
|
Pick<R3DirectiveMetadataFacade, Exclude<keyof R3DirectiveMetadataFacade, 'propMetadata'>>;
|
|
|
|
|
|
|
|
function wrapExpression(obj: any, property: string): WrappedNodeExpr<any>|undefined {
|
|
|
|
if (obj.hasOwnProperty(property)) {
|
|
|
|
return new WrappedNodeExpr(obj[property]);
|
|
|
|
} else {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-08 13:14:18 -04:00
|
|
|
function computeProvidedIn(providedIn: Type|string|null|undefined): Expression {
|
2018-10-24 19:02:25 -04:00
|
|
|
if (providedIn == null || typeof providedIn === 'string') {
|
|
|
|
return new LiteralExpr(providedIn);
|
|
|
|
} else {
|
|
|
|
return new WrappedNodeExpr(providedIn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function convertR3DependencyMetadata(facade: R3DependencyMetadataFacade): R3DependencyMetadata {
|
|
|
|
let tokenExpr;
|
|
|
|
if (facade.token === null) {
|
|
|
|
tokenExpr = new LiteralExpr(null);
|
|
|
|
} else if (facade.resolved === R3ResolvedDependencyType.Attribute) {
|
|
|
|
tokenExpr = new LiteralExpr(facade.token);
|
|
|
|
} else {
|
|
|
|
tokenExpr = new WrappedNodeExpr(facade.token);
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
token: tokenExpr,
|
feat(compiler): add dependency info and ng-content selectors to metadata (#35695)
This commit augments the `FactoryDef` declaration of Angular decorated
classes to contain information about the parameter decorators used in
the constructor. If no constructor is present, or none of the parameters
have any Angular decorators, then this will be represented using the
`null` type. Otherwise, a tuple type is used where the entry at index `i`
corresponds with parameter `i`. Each tuple entry can be one of two types:
1. If the associated parameter does not have any Angular decorators,
the tuple entry will be the `null` type.
2. Otherwise, a type literal is used that may declare at least one of
the following properties:
- "attribute": if `@Attribute` is present. The injected attribute's
name is used as string literal type, or the `unknown` type if the
attribute name is not a string literal.
- "self": if `@Self` is present, always of type `true`.
- "skipSelf": if `@SkipSelf` is present, always of type `true`.
- "host": if `@Host` is present, always of type `true`.
- "optional": if `@Optional` is present, always of type `true`.
A property is only present if the corresponding decorator is used.
Note that the `@Inject` decorator is currently not included, as it's
non-trivial to properly convert the token's value expression to a
type that is valid in a declaration file.
Additionally, the `ComponentDefWithMeta` declaration that is created for
Angular components has been extended to include all selectors on
`ng-content` elements within the component's template.
This additional metadata is useful for tooling such as the Angular
Language Service, as it provides the ability to offer suggestions for
directives/components defined in libraries. At the moment, such
tooling extracts the necessary information from the _metadata.json_
manifest file as generated by ngc, however this metadata representation
is being replaced by the information emitted into the declaration files.
Resolves FW-1870
PR Close #35695
2020-02-26 16:05:44 -05:00
|
|
|
attribute: null,
|
2018-10-24 19:02:25 -04:00
|
|
|
resolved: facade.resolved,
|
|
|
|
host: facade.host,
|
|
|
|
optional: facade.optional,
|
|
|
|
self: facade.self,
|
feat(compiler): add dependency info and ng-content selectors to metadata (#35695)
This commit augments the `FactoryDef` declaration of Angular decorated
classes to contain information about the parameter decorators used in
the constructor. If no constructor is present, or none of the parameters
have any Angular decorators, then this will be represented using the
`null` type. Otherwise, a tuple type is used where the entry at index `i`
corresponds with parameter `i`. Each tuple entry can be one of two types:
1. If the associated parameter does not have any Angular decorators,
the tuple entry will be the `null` type.
2. Otherwise, a type literal is used that may declare at least one of
the following properties:
- "attribute": if `@Attribute` is present. The injected attribute's
name is used as string literal type, or the `unknown` type if the
attribute name is not a string literal.
- "self": if `@Self` is present, always of type `true`.
- "skipSelf": if `@SkipSelf` is present, always of type `true`.
- "host": if `@Host` is present, always of type `true`.
- "optional": if `@Optional` is present, always of type `true`.
A property is only present if the corresponding decorator is used.
Note that the `@Inject` decorator is currently not included, as it's
non-trivial to properly convert the token's value expression to a
type that is valid in a declaration file.
Additionally, the `ComponentDefWithMeta` declaration that is created for
Angular components has been extended to include all selectors on
`ng-content` elements within the component's template.
This additional metadata is useful for tooling such as the Angular
Language Service, as it provides the ability to offer suggestions for
directives/components defined in libraries. At the moment, such
tooling extracts the necessary information from the _metadata.json_
manifest file as generated by ngc, however this metadata representation
is being replaced by the information emitted into the declaration files.
Resolves FW-1870
PR Close #35695
2020-02-26 16:05:44 -05:00
|
|
|
skipSelf: facade.skipSelf,
|
2018-10-24 19:02:25 -04:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-04-08 13:14:18 -04:00
|
|
|
function convertR3DependencyMetadataArray(facades: R3DependencyMetadataFacade[]|null|
|
|
|
|
undefined): R3DependencyMetadata[]|null {
|
2018-10-24 19:02:25 -04:00
|
|
|
return facades == null ? null : facades.map(convertR3DependencyMetadata);
|
|
|
|
}
|
|
|
|
|
2019-01-24 20:25:46 -05:00
|
|
|
function extractHostBindings(
|
2019-04-27 03:33:10 -04:00
|
|
|
propMetadata: {[key: string]: any[]}, sourceSpan: ParseSourceSpan,
|
|
|
|
host?: {[key: string]: string}): ParsedHostBindings {
|
2018-10-24 19:02:25 -04:00
|
|
|
// First parse the declarations from the metadata.
|
2019-01-24 20:25:46 -05:00
|
|
|
const bindings = parseHostBindings(host || {});
|
|
|
|
|
|
|
|
// After that check host bindings for errors
|
|
|
|
const errors = verifyHostBindings(bindings, sourceSpan);
|
|
|
|
if (errors.length) {
|
|
|
|
throw new Error(errors.map((error: ParseError) => error.msg).join('\n'));
|
|
|
|
}
|
2018-10-24 19:02:25 -04:00
|
|
|
|
|
|
|
// Next, loop over the properties of the object, looking for @HostBinding and @HostListener.
|
|
|
|
for (const field in propMetadata) {
|
|
|
|
if (propMetadata.hasOwnProperty(field)) {
|
|
|
|
propMetadata[field].forEach(ann => {
|
|
|
|
if (isHostBinding(ann)) {
|
2019-01-24 20:25:46 -05:00
|
|
|
bindings.properties[ann.hostPropertyName || field] = field;
|
2018-10-24 19:02:25 -04:00
|
|
|
} else if (isHostListener(ann)) {
|
2019-01-24 20:25:46 -05:00
|
|
|
bindings.listeners[ann.eventName || field] = `${field}(${(ann.args || []).join(',')})`;
|
2018-10-24 19:02:25 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-24 20:25:46 -05:00
|
|
|
return bindings;
|
2018-10-24 19:02:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function isHostBinding(value: any): value is HostBinding {
|
|
|
|
return value.ngMetadataName === 'HostBinding';
|
|
|
|
}
|
|
|
|
|
|
|
|
function isHostListener(value: any): value is HostListener {
|
|
|
|
return value.ngMetadataName === 'HostListener';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function isInput(value: any): value is Input {
|
|
|
|
return value.ngMetadataName === 'Input';
|
|
|
|
}
|
|
|
|
|
|
|
|
function isOutput(value: any): value is Output {
|
|
|
|
return value.ngMetadataName === 'Output';
|
|
|
|
}
|
|
|
|
|
|
|
|
function parseInputOutputs(values: string[]): StringMap {
|
2020-04-08 13:14:18 -04:00
|
|
|
return values.reduce((map, value) => {
|
|
|
|
const [field, property] = value.split(',').map(piece => piece.trim());
|
|
|
|
map[field] = property || field;
|
|
|
|
return map;
|
|
|
|
}, {} as StringMap);
|
2018-10-24 19:02:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export function publishFacade(global: any) {
|
|
|
|
const ng: ExportedCompilerFacade = global.ng || (global.ng = {});
|
|
|
|
ng.ɵcompilerFacade = new CompilerFacadeImpl();
|
2018-11-12 21:02:47 -05:00
|
|
|
}
|