fix(ivy): allow abstract directives to have an invalid constructor (#32987)
For abstract directives, i.e. directives without a selector, it may happen that their constructor is called explicitly from a subclass, hence its parameters are not required to be valid for Angular's DI purposes. Prior to this commit however, having an abstract directive with a constructor that has parameters that are not eligible for Angular's DI would produce a compilation error. A similar scenario may occur for `@Injectable`s, where an explicit `use*` definition allows for the constructor to be irrelevant. For example, the situation where `useFactory` is specified allows for the constructor to be called explicitly with any value, so its constructor parameters are not required to be valid. For `@Injectable`s this is handled by generating a DI factory function that throws. This commit implements the same solution for abstract directives, such that a compilation error is avoided while still producing an error at runtime if the type is instantiated implicitly by Angular's DI mechanism. Fixes #32981 PR Close #32987
This commit is contained in:
parent
e4e8dbdee0
commit
8d15bfa6ee
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, ParseError, ParseSourceFile, ParseTemplateOptions, R3ComponentMetadata, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
|
import {ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, ParseError, ParseSourceFile, ParseTemplateOptions, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {CycleAnalyzer} from '../../cycles';
|
import {CycleAnalyzer} from '../../cycles';
|
||||||
|
@ -490,7 +490,8 @@ export class ComponentDecoratorHandler implements
|
||||||
CompileResult[] {
|
CompileResult[] {
|
||||||
const meta = analysis.meta;
|
const meta = analysis.meta;
|
||||||
const res = compileComponentFromMetadata(meta, pool, makeBindingParser());
|
const res = compileComponentFromMetadata(meta, pool, makeBindingParser());
|
||||||
const factoryRes = compileNgFactoryDefField({...meta, injectFn: Identifiers.directiveInject});
|
const factoryRes = compileNgFactoryDefField(
|
||||||
|
{...meta, injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Component});
|
||||||
if (analysis.metadataStmt !== null) {
|
if (analysis.metadataStmt !== null) {
|
||||||
factoryRes.statements.push(analysis.metadataStmt);
|
factoryRes.statements.push(analysis.metadataStmt);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ConstantPool, EMPTY_SOURCE_SPAN, Expression, Identifiers, ParseError, ParsedHostBindings, R3DirectiveMetadata, R3QueryMetadata, Statement, WrappedNodeExpr, compileDirectiveFromMetadata, makeBindingParser, parseHostBindings, verifyHostBindings} from '@angular/compiler';
|
import {ConstantPool, EMPTY_SOURCE_SPAN, Expression, Identifiers, ParseError, ParsedHostBindings, R3DependencyMetadata, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, Statement, WrappedNodeExpr, compileDirectiveFromMetadata, makeBindingParser, parseHostBindings, verifyHostBindings} from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
||||||
|
@ -19,7 +19,7 @@ import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFl
|
||||||
|
|
||||||
import {compileNgFactoryDefField} from './factory';
|
import {compileNgFactoryDefField} from './factory';
|
||||||
import {generateSetClassMetadataCall} from './metadata';
|
import {generateSetClassMetadataCall} from './metadata';
|
||||||
import {findAngularDecorator, getValidConstructorDependencies, readBaseClass, unwrapExpression, unwrapForwardRef} from './util';
|
import {findAngularDecorator, getConstructorDependencies, readBaseClass, unwrapConstructorDependencies, unwrapExpression, unwrapForwardRef, validateConstructorDependencies} from './util';
|
||||||
|
|
||||||
const EMPTY_OBJECT: {[key: string]: string} = {};
|
const EMPTY_OBJECT: {[key: string]: string} = {};
|
||||||
|
|
||||||
|
@ -89,7 +89,8 @@ export class DirectiveDecoratorHandler implements
|
||||||
CompileResult[] {
|
CompileResult[] {
|
||||||
const meta = analysis.meta;
|
const meta = analysis.meta;
|
||||||
const res = compileDirectiveFromMetadata(meta, pool, makeBindingParser());
|
const res = compileDirectiveFromMetadata(meta, pool, makeBindingParser());
|
||||||
const factoryRes = compileNgFactoryDefField({...meta, injectFn: Identifiers.directiveInject});
|
const factoryRes = compileNgFactoryDefField(
|
||||||
|
{...meta, injectFn: Identifiers.directiveInject, target: R3FactoryTarget.Directive});
|
||||||
if (analysis.metadataStmt !== null) {
|
if (analysis.metadataStmt !== null) {
|
||||||
factoryRes.statements.push(analysis.metadataStmt);
|
factoryRes.statements.push(analysis.metadataStmt);
|
||||||
}
|
}
|
||||||
|
@ -228,11 +229,23 @@ export function extractDirectiveMetadata(
|
||||||
exportAs = resolved.split(',').map(part => part.trim());
|
exportAs = resolved.split(',').map(part => part.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
|
||||||
|
let ctorDeps: R3DependencyMetadata[]|'invalid'|null;
|
||||||
|
|
||||||
|
// Non-abstract directives (those with a selector) require valid constructor dependencies, whereas
|
||||||
|
// abstract directives are allowed to have invalid dependencies, given that a subclass may call
|
||||||
|
// the constructor explicitly.
|
||||||
|
if (selector !== null) {
|
||||||
|
ctorDeps = validateConstructorDependencies(clazz, rawCtorDeps);
|
||||||
|
} else {
|
||||||
|
ctorDeps = unwrapConstructorDependencies(rawCtorDeps);
|
||||||
|
}
|
||||||
|
|
||||||
// Detect if the component inherits from another class
|
// Detect if the component inherits from another class
|
||||||
const usesInheritance = reflector.hasBaseClass(clazz);
|
const usesInheritance = reflector.hasBaseClass(clazz);
|
||||||
const metadata: R3DirectiveMetadata = {
|
const metadata: R3DirectiveMetadata = {
|
||||||
name: clazz.name.text,
|
name: clazz.name.text,
|
||||||
deps: getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore), host,
|
deps: ctorDeps, host,
|
||||||
lifecycle: {
|
lifecycle: {
|
||||||
usesOnChanges,
|
usesOnChanges,
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {R3FactoryDefMetadata, compileFactoryFromMetadata} from '@angular/compiler';
|
import {R3FactoryMetadata, compileFactoryFunction} from '@angular/compiler';
|
||||||
|
|
||||||
import {CompileResult} from '../../transform';
|
import {CompileResult} from '../../transform';
|
||||||
|
|
||||||
export function compileNgFactoryDefField(metadata: R3FactoryDefMetadata): CompileResult {
|
export function compileNgFactoryDefField(metadata: R3FactoryMetadata): CompileResult {
|
||||||
const res = compileFactoryFromMetadata(metadata);
|
const res = compileFactoryFunction(metadata);
|
||||||
return {name: 'ɵfac', initializer: res.factory, statements: res.statements, type: res.type};
|
return {name: 'ɵfac', initializer: res.factory, statements: res.statements, type: res.type};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr, compileInjectable as compileIvyInjectable} from '@angular/compiler';
|
import {Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3FactoryTarget, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr, compileInjectable as compileIvyInjectable} from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
||||||
|
@ -16,7 +16,7 @@ import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerPr
|
||||||
|
|
||||||
import {compileNgFactoryDefField} from './factory';
|
import {compileNgFactoryDefField} from './factory';
|
||||||
import {generateSetClassMetadataCall} from './metadata';
|
import {generateSetClassMetadataCall} from './metadata';
|
||||||
import {findAngularDecorator, getConstructorDependencies, getValidConstructorDependencies, isAngularCore, unwrapForwardRef, validateConstructorDependencies} from './util';
|
import {findAngularDecorator, getConstructorDependencies, getValidConstructorDependencies, isAngularCore, unwrapConstructorDependencies, unwrapForwardRef, validateConstructorDependencies} from './util';
|
||||||
|
|
||||||
export interface InjectableHandlerData {
|
export interface InjectableHandlerData {
|
||||||
meta: R3InjectableMetadata;
|
meta: R3InjectableMetadata;
|
||||||
|
@ -83,7 +83,8 @@ export class InjectableDecoratorHandler implements
|
||||||
type: meta.type,
|
type: meta.type,
|
||||||
typeArgumentCount: meta.typeArgumentCount,
|
typeArgumentCount: meta.typeArgumentCount,
|
||||||
deps: analysis.ctorDeps,
|
deps: analysis.ctorDeps,
|
||||||
injectFn: Identifiers.inject
|
injectFn: Identifiers.inject,
|
||||||
|
target: R3FactoryTarget.Injectable,
|
||||||
});
|
});
|
||||||
if (analysis.metadataStmt !== null) {
|
if (analysis.metadataStmt !== null) {
|
||||||
factoryRes.statements.push(analysis.metadataStmt);
|
factoryRes.statements.push(analysis.metadataStmt);
|
||||||
|
@ -216,45 +217,25 @@ function extractInjectableCtorDeps(
|
||||||
// Angular's DI.
|
// Angular's DI.
|
||||||
//
|
//
|
||||||
// To deal with this, @Injectable() without an argument is more lenient, and if the
|
// To deal with this, @Injectable() without an argument is more lenient, and if the
|
||||||
// constructor signature does not work for DI then a provider def (ɵprov) that throws.
|
// constructor signature does not work for DI then a factory definition (ɵfac) that throws is
|
||||||
|
// generated.
|
||||||
if (strictCtorDeps) {
|
if (strictCtorDeps) {
|
||||||
ctorDeps = getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
|
ctorDeps = getValidConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
|
||||||
} else {
|
} else {
|
||||||
const possibleCtorDeps =
|
ctorDeps = unwrapConstructorDependencies(
|
||||||
getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
|
getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore));
|
||||||
if (possibleCtorDeps !== null) {
|
|
||||||
if (possibleCtorDeps.deps !== null) {
|
|
||||||
// This use of @Injectable has valid constructor dependencies.
|
|
||||||
ctorDeps = possibleCtorDeps.deps;
|
|
||||||
} else {
|
|
||||||
// This use of @Injectable is technically invalid. Generate a factory function which
|
|
||||||
// throws
|
|
||||||
// an error.
|
|
||||||
// TODO(alxhub): log warnings for the bad use of @Injectable.
|
|
||||||
ctorDeps = 'invalid';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctorDeps;
|
return ctorDeps;
|
||||||
} else if (decorator.args.length === 1) {
|
} else if (decorator.args.length === 1) {
|
||||||
const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
|
const rawCtorDeps = getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore);
|
||||||
|
|
||||||
// rawCtorDeps will be null if the class has no constructor.
|
if (strictCtorDeps && meta.useValue === undefined && meta.useExisting === undefined &&
|
||||||
if (rawCtorDeps !== null) {
|
meta.useClass === undefined && meta.useFactory === undefined) {
|
||||||
if (rawCtorDeps.deps !== null) {
|
|
||||||
// A constructor existed and had valid dependencies.
|
|
||||||
ctorDeps = rawCtorDeps.deps;
|
|
||||||
} else {
|
|
||||||
// A constructor existed but had invalid dependencies.
|
|
||||||
ctorDeps = 'invalid';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strictCtorDeps && !meta.useValue && !meta.useExisting && !meta.useClass &&
|
|
||||||
!meta.useFactory) {
|
|
||||||
// Since use* was not provided, validate the deps according to strictCtorDeps.
|
// Since use* was not provided, validate the deps according to strictCtorDeps.
|
||||||
validateConstructorDependencies(clazz, rawCtorDeps);
|
ctorDeps = validateConstructorDependencies(clazz, rawCtorDeps);
|
||||||
|
} else {
|
||||||
|
ctorDeps = unwrapConstructorDependencies(rawCtorDeps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Identifiers, R3PipeMetadata, Statement, WrappedNodeExpr, compilePipeFromMetadata} from '@angular/compiler';
|
import {Identifiers, R3FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr, compilePipeFromMetadata} from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
||||||
|
@ -114,7 +114,7 @@ export class PipeDecoratorHandler implements DecoratorHandler<PipeHandlerData, D
|
||||||
const factoryRes = compileNgFactoryDefField({
|
const factoryRes = compileNgFactoryDefField({
|
||||||
...meta,
|
...meta,
|
||||||
injectFn: Identifiers.directiveInject,
|
injectFn: Identifiers.directiveInject,
|
||||||
isPipe: true,
|
target: R3FactoryTarget.Pipe,
|
||||||
});
|
});
|
||||||
if (analysis.metadataStmt !== null) {
|
if (analysis.metadataStmt !== null) {
|
||||||
factoryRes.statements.push(analysis.metadataStmt);
|
factoryRes.statements.push(analysis.metadataStmt);
|
||||||
|
|
|
@ -134,6 +134,25 @@ export function valueReferenceToExpression(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert `ConstructorDeps` into the `R3DependencyMetadata` array for those deps if they're valid,
|
||||||
|
* or into an `'invalid'` signal if they're not.
|
||||||
|
*
|
||||||
|
* This is a companion function to `validateConstructorDependencies` which accepts invalid deps.
|
||||||
|
*/
|
||||||
|
export function unwrapConstructorDependencies(deps: ConstructorDeps | null): R3DependencyMetadata[]|
|
||||||
|
'invalid'|null {
|
||||||
|
if (deps === null) {
|
||||||
|
return null;
|
||||||
|
} else if (deps.deps !== null) {
|
||||||
|
// These constructor dependencies are valid.
|
||||||
|
return deps.deps;
|
||||||
|
} else {
|
||||||
|
// These deps are invalid.
|
||||||
|
return 'invalid';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function getValidConstructorDependencies(
|
export function getValidConstructorDependencies(
|
||||||
clazz: ClassDeclaration, reflector: ReflectionHost,
|
clazz: ClassDeclaration, reflector: ReflectionHost,
|
||||||
defaultImportRecorder: DefaultImportRecorder, isCore: boolean): R3DependencyMetadata[]|null {
|
defaultImportRecorder: DefaultImportRecorder, isCore: boolean): R3DependencyMetadata[]|null {
|
||||||
|
@ -141,6 +160,13 @@ export function getValidConstructorDependencies(
|
||||||
clazz, getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore));
|
clazz, getConstructorDependencies(clazz, reflector, defaultImportRecorder, isCore));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that `ConstructorDeps` does not have any invalid dependencies and convert them into the
|
||||||
|
* `R3DependencyMetadata` array if so, or raise a diagnostic if some deps are invalid.
|
||||||
|
*
|
||||||
|
* This is a companion function to `unwrapConstructorDependencies` which does not accept invalid
|
||||||
|
* deps.
|
||||||
|
*/
|
||||||
export function validateConstructorDependencies(
|
export function validateConstructorDependencies(
|
||||||
clazz: ClassDeclaration, deps: ConstructorDeps | null): R3DependencyMetadata[]|null {
|
clazz: ClassDeclaration, deps: ConstructorDeps | null): R3DependencyMetadata[]|null {
|
||||||
if (deps === null) {
|
if (deps === null) {
|
||||||
|
|
|
@ -66,8 +66,7 @@ export interface CompilerOptions extends ts.CompilerOptions {
|
||||||
// be determined. When this value option is not provided or is `false`, constructor
|
// be determined. When this value option is not provided or is `false`, constructor
|
||||||
// parameters of classes marked with `@Injectable` whose type cannot be resolved will
|
// parameters of classes marked with `@Injectable` whose type cannot be resolved will
|
||||||
// produce a warning. With this option `true`, they produce an error. When this option is
|
// produce a warning. With this option `true`, they produce an error. When this option is
|
||||||
// not provided is treated as if it were `false`. In Angular 6.0, if this option is not
|
// not provided is treated as if it were `false`.
|
||||||
// provided, it will be treated as `true`.
|
|
||||||
strictInjectionParameters?: boolean;
|
strictInjectionParameters?: boolean;
|
||||||
|
|
||||||
// Whether to generate a flat module index of the given name and the corresponding
|
// Whether to generate a flat module index of the given name and the corresponding
|
||||||
|
|
|
@ -1336,7 +1336,7 @@ runInEachFileSystem(os => {
|
||||||
env.write('test.ts', `
|
env.write('test.ts', `
|
||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable({providedIn: 'root'})
|
||||||
export class Test {
|
export class Test {
|
||||||
constructor(private notInjectable: string) {}
|
constructor(private notInjectable: string) {}
|
||||||
}
|
}
|
||||||
|
@ -1364,7 +1364,75 @@ runInEachFileSystem(os => {
|
||||||
|
|
||||||
env.driveMain();
|
env.driveMain();
|
||||||
const jsContents = env.getContents('test.js');
|
const jsContents = env.getContents('test.js');
|
||||||
expect(jsContents).toMatch(/function Test_Factory\(t\) { throw new Error\(/ms);
|
expect(jsContents)
|
||||||
|
.toMatch(/function Test_Factory\(t\) { i0\.ɵɵinvalidFactory\(\)/ms);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not give a compile-time error if an invalid @Injectable is used with useFactory',
|
||||||
|
() => {
|
||||||
|
env.tsconfig({strictInjectionParameters: true});
|
||||||
|
env.write('test.ts', `
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
useFactory: () => '42',
|
||||||
|
})
|
||||||
|
export class Test {
|
||||||
|
constructor(private notInjectable: string) {}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
env.driveMain();
|
||||||
|
const jsContents = env.getContents('test.js');
|
||||||
|
expect(jsContents)
|
||||||
|
.toMatch(/function Test_Factory\(t\) { i0\.ɵɵinvalidFactory\(\)/ms);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not give a compile-time error if an invalid @Injectable is used with useExisting',
|
||||||
|
() => {
|
||||||
|
env.tsconfig({strictInjectionParameters: true});
|
||||||
|
env.write('test.ts', `
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
|
export class MyService {}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
useExisting: MyService,
|
||||||
|
})
|
||||||
|
export class Test {
|
||||||
|
constructor(private notInjectable: string) {}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
env.driveMain();
|
||||||
|
const jsContents = env.getContents('test.js');
|
||||||
|
expect(jsContents)
|
||||||
|
.toMatch(/function Test_Factory\(t\) { i0\.ɵɵinvalidFactory\(\)/ms);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not give a compile-time error if an invalid @Injectable is used with useClass',
|
||||||
|
() => {
|
||||||
|
env.tsconfig({strictInjectionParameters: true});
|
||||||
|
env.write('test.ts', `
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
|
export class MyService {}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
useClass: MyService,
|
||||||
|
})
|
||||||
|
export class Test {
|
||||||
|
constructor(private notInjectable: string) {}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
env.driveMain();
|
||||||
|
const jsContents = env.getContents('test.js');
|
||||||
|
expect(jsContents)
|
||||||
|
.toMatch(/function Test_Factory\(t\) { i0\.ɵɵinvalidFactory\(\)/ms);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1382,7 +1450,8 @@ runInEachFileSystem(os => {
|
||||||
|
|
||||||
env.driveMain();
|
env.driveMain();
|
||||||
const jsContents = env.getContents('test.js');
|
const jsContents = env.getContents('test.js');
|
||||||
expect(jsContents).toContain('Test.ɵfac = function Test_Factory(t) { throw new Error(');
|
expect(jsContents)
|
||||||
|
.toContain('Test.ɵfac = function Test_Factory(t) { i0.ɵɵinvalidFactory()');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should compile an @Injectable provided in the root on a class with a non-injectable constructor',
|
it('should compile an @Injectable provided in the root on a class with a non-injectable constructor',
|
||||||
|
@ -1399,12 +1468,69 @@ runInEachFileSystem(os => {
|
||||||
env.driveMain();
|
env.driveMain();
|
||||||
const jsContents = env.getContents('test.js');
|
const jsContents = env.getContents('test.js');
|
||||||
expect(jsContents)
|
expect(jsContents)
|
||||||
.toContain('Test.ɵfac = function Test_Factory(t) { throw new Error(');
|
.toContain('Test.ɵfac = function Test_Factory(t) { i0.ɵɵinvalidFactory()');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('compiling invalid @Directives', () => {
|
||||||
|
describe('directives with a selector', () => {
|
||||||
|
it('should give a compile-time error if an invalid constructor is used', () => {
|
||||||
|
env.tsconfig({strictInjectionParameters: true});
|
||||||
|
env.write('test.ts', `
|
||||||
|
import {Directive} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({selector: 'app-test'})
|
||||||
|
export class Test {
|
||||||
|
constructor(private notInjectable: string) {}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const errors = env.driveDiagnostics();
|
||||||
|
expect(errors.length).toBe(1);
|
||||||
|
expect(errors[0].messageText).toContain('No suitable injection token for parameter');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('abstract directives', () => {
|
||||||
|
it('should generate a factory function that throws', () => {
|
||||||
|
env.tsconfig({strictInjectionParameters: false});
|
||||||
|
env.write('test.ts', `
|
||||||
|
import {Directive} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
export class Test {
|
||||||
|
constructor(private notInjectable: string) {}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
env.driveMain();
|
||||||
|
const jsContents = env.getContents('test.js');
|
||||||
|
expect(jsContents)
|
||||||
|
.toContain('Test.ɵfac = function Test_Factory(t) { i0.ɵɵinvalidFactory()');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should generate a factory function that throws, even under strictInjectionParameters',
|
||||||
|
() => {
|
||||||
|
env.tsconfig({strictInjectionParameters: true});
|
||||||
|
env.write('test.ts', `
|
||||||
|
import {Directive} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
export class Test {
|
||||||
|
constructor(private notInjectable: string) {}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
env.driveMain();
|
||||||
|
const jsContents = env.getContents('test.js');
|
||||||
|
expect(jsContents)
|
||||||
|
.toContain('Test.ɵfac = function Test_Factory(t) { i0.ɵɵinvalidFactory()');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('templateUrl and styleUrls processing', () => {
|
describe('templateUrl and styleUrls processing', () => {
|
||||||
const testsForResource = (resource: string) => [
|
const testsForResource = (resource: string) => [
|
||||||
// [component location, resource location, resource reference]
|
// [component location, resource location, resource reference]
|
||||||
|
|
|
@ -96,7 +96,7 @@ export {BoundAttribute as TmplAstBoundAttribute, BoundEvent as TmplAstBoundEvent
|
||||||
export * from './render3/view/t2_api';
|
export * from './render3/view/t2_api';
|
||||||
export * from './render3/view/t2_binder';
|
export * from './render3/view/t2_binder';
|
||||||
export {Identifiers as R3Identifiers} from './render3/r3_identifiers';
|
export {Identifiers as R3Identifiers} from './render3/r3_identifiers';
|
||||||
export {R3DependencyMetadata, R3FactoryDefMetadata, R3ResolvedDependencyType, compileFactoryFromMetadata, R3FactoryMetadata} from './render3/r3_factory';
|
export {R3DependencyMetadata, R3ResolvedDependencyType, compileFactoryFunction, R3FactoryMetadata, R3FactoryTarget} from './render3/r3_factory';
|
||||||
export {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata} from './render3/r3_module_compiler';
|
export {compileInjector, compileNgModule, R3InjectorMetadata, R3NgModuleMetadata} from './render3/r3_module_compiler';
|
||||||
export {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
|
export {compilePipeFromMetadata, R3PipeMetadata} from './render3/r3_pipe_compiler';
|
||||||
export {makeBindingParser, parseTemplate, ParseTemplateOptions} from './render3/view/template';
|
export {makeBindingParser, parseTemplate, ParseTemplateOptions} from './render3/view/template';
|
||||||
|
|
|
@ -45,6 +45,7 @@ export interface CompilerFacade {
|
||||||
createParseSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan;
|
createParseSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan;
|
||||||
|
|
||||||
R3ResolvedDependencyType: typeof R3ResolvedDependencyType;
|
R3ResolvedDependencyType: typeof R3ResolvedDependencyType;
|
||||||
|
R3FactoryTarget: typeof R3FactoryTarget;
|
||||||
ResourceLoader: {new (): ResourceLoader};
|
ResourceLoader: {new (): ResourceLoader};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +71,14 @@ export enum R3ResolvedDependencyType {
|
||||||
ChangeDetectorRef = 2,
|
ChangeDetectorRef = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum R3FactoryTarget {
|
||||||
|
Directive = 0,
|
||||||
|
Component = 1,
|
||||||
|
Injectable = 2,
|
||||||
|
Pipe = 3,
|
||||||
|
NgModule = 4,
|
||||||
|
}
|
||||||
|
|
||||||
export interface R3DependencyMetadataFacade {
|
export interface R3DependencyMetadataFacade {
|
||||||
token: any;
|
token: any;
|
||||||
resolved: R3ResolvedDependencyType;
|
resolved: R3ResolvedDependencyType;
|
||||||
|
@ -167,7 +176,7 @@ export interface R3FactoryDefMetadataFacade {
|
||||||
typeArgumentCount: number;
|
typeArgumentCount: number;
|
||||||
deps: R3DependencyMetadataFacade[]|null;
|
deps: R3DependencyMetadataFacade[]|null;
|
||||||
injectFn: 'directiveInject'|'inject';
|
injectFn: 'directiveInject'|'inject';
|
||||||
isPipe: boolean;
|
target: R3FactoryTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ViewEncapsulation = number;
|
export type ViewEncapsulation = number;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import {Identifiers} from './identifiers';
|
import {Identifiers} from './identifiers';
|
||||||
import * as o from './output/output_ast';
|
import * as o from './output/output_ast';
|
||||||
import {R3DependencyMetadata, R3FactoryDelegateType, compileFactoryFunction} from './render3/r3_factory';
|
import {R3DependencyMetadata, R3FactoryDelegateType, R3FactoryTarget, compileFactoryFunction} from './render3/r3_factory';
|
||||||
import {mapToMapExpression, typeWithParameters} from './render3/util';
|
import {mapToMapExpression, typeWithParameters} from './render3/util';
|
||||||
|
|
||||||
export interface InjectableDef {
|
export interface InjectableDef {
|
||||||
|
@ -38,6 +38,7 @@ export function compileInjectable(meta: R3InjectableMetadata): InjectableDef {
|
||||||
typeArgumentCount: meta.typeArgumentCount,
|
typeArgumentCount: meta.typeArgumentCount,
|
||||||
deps: [],
|
deps: [],
|
||||||
injectFn: Identifiers.inject,
|
injectFn: Identifiers.inject,
|
||||||
|
target: R3FactoryTarget.Injectable,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (meta.useClass !== undefined) {
|
if (meta.useClass !== undefined) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from './ml_parser/int
|
||||||
import {DeclareVarStmt, Expression, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast';
|
import {DeclareVarStmt, Expression, LiteralExpr, Statement, StmtModifier, WrappedNodeExpr} from './output/output_ast';
|
||||||
import {JitEvaluator} from './output/output_jit';
|
import {JitEvaluator} from './output/output_jit';
|
||||||
import {ParseError, ParseSourceSpan, r3JitTypeSourceSpan} from './parse_util';
|
import {ParseError, ParseSourceSpan, r3JitTypeSourceSpan} from './parse_util';
|
||||||
import {R3DependencyMetadata, R3ResolvedDependencyType, compileFactoryFromMetadata} from './render3/r3_factory';
|
import {R3DependencyMetadata, R3FactoryTarget, R3ResolvedDependencyType, compileFactoryFunction} from './render3/r3_factory';
|
||||||
import {R3JitReflector} from './render3/r3_jit';
|
import {R3JitReflector} from './render3/r3_jit';
|
||||||
import {R3InjectorMetadata, R3NgModuleMetadata, compileInjector, compileNgModule} from './render3/r3_module_compiler';
|
import {R3InjectorMetadata, R3NgModuleMetadata, compileInjector, compileNgModule} from './render3/r3_module_compiler';
|
||||||
import {compilePipeFromMetadata} from './render3/r3_pipe_compiler';
|
import {compilePipeFromMetadata} from './render3/r3_pipe_compiler';
|
||||||
|
@ -29,6 +29,7 @@ import {DomElementSchemaRegistry} from './schema/dom_element_schema_registry';
|
||||||
|
|
||||||
export class CompilerFacadeImpl implements CompilerFacade {
|
export class CompilerFacadeImpl implements CompilerFacade {
|
||||||
R3ResolvedDependencyType = R3ResolvedDependencyType as any;
|
R3ResolvedDependencyType = R3ResolvedDependencyType as any;
|
||||||
|
R3FactoryTarget = R3FactoryTarget as any;
|
||||||
ResourceLoader = ResourceLoader;
|
ResourceLoader = ResourceLoader;
|
||||||
private elementSchemaRegistry = new DomElementSchemaRegistry();
|
private elementSchemaRegistry = new DomElementSchemaRegistry();
|
||||||
|
|
||||||
|
@ -155,14 +156,14 @@ export class CompilerFacadeImpl implements CompilerFacade {
|
||||||
|
|
||||||
compileFactory(
|
compileFactory(
|
||||||
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3FactoryDefMetadataFacade) {
|
angularCoreEnv: CoreEnvironment, sourceMapUrl: string, meta: R3FactoryDefMetadataFacade) {
|
||||||
const factoryRes = compileFactoryFromMetadata({
|
const factoryRes = compileFactoryFunction({
|
||||||
name: meta.name,
|
name: meta.name,
|
||||||
type: new WrappedNodeExpr(meta.type),
|
type: new WrappedNodeExpr(meta.type),
|
||||||
typeArgumentCount: meta.typeArgumentCount,
|
typeArgumentCount: meta.typeArgumentCount,
|
||||||
deps: convertR3DependencyMetadataArray(meta.deps),
|
deps: convertR3DependencyMetadataArray(meta.deps),
|
||||||
injectFn: meta.injectFn === 'directiveInject' ? Identifiers.directiveInject :
|
injectFn: meta.injectFn === 'directiveInject' ? Identifiers.directiveInject :
|
||||||
Identifiers.inject,
|
Identifiers.inject,
|
||||||
isPipe: meta.isPipe
|
target: meta.target,
|
||||||
});
|
});
|
||||||
return this.jitExpression(
|
return this.jitExpression(
|
||||||
factoryRes.factory, angularCoreEnv, sourceMapUrl, factoryRes.statements);
|
factoryRes.factory, angularCoreEnv, sourceMapUrl, factoryRes.statements);
|
||||||
|
|
|
@ -56,6 +56,11 @@ export interface R3ConstructorFactoryMetadata {
|
||||||
* function could be different, and other options control how it will be invoked.
|
* function could be different, and other options control how it will be invoked.
|
||||||
*/
|
*/
|
||||||
injectFn: o.ExternalReference;
|
injectFn: o.ExternalReference;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of the target being created by the factory.
|
||||||
|
*/
|
||||||
|
target: R3FactoryTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum R3FactoryDelegateType {
|
export enum R3FactoryDelegateType {
|
||||||
|
@ -82,13 +87,12 @@ export interface R3ExpressionFactoryMetadata extends R3ConstructorFactoryMetadat
|
||||||
export type R3FactoryMetadata = R3ConstructorFactoryMetadata | R3DelegatedFactoryMetadata |
|
export type R3FactoryMetadata = R3ConstructorFactoryMetadata | R3DelegatedFactoryMetadata |
|
||||||
R3DelegatedFnOrClassMetadata | R3ExpressionFactoryMetadata;
|
R3DelegatedFnOrClassMetadata | R3ExpressionFactoryMetadata;
|
||||||
|
|
||||||
export interface R3FactoryDefMetadata {
|
export enum R3FactoryTarget {
|
||||||
name: string;
|
Directive = 0,
|
||||||
type: o.Expression;
|
Component = 1,
|
||||||
typeArgumentCount: number;
|
Injectable = 2,
|
||||||
deps: R3DependencyMetadata[]|'invalid'|null;
|
Pipe = 3,
|
||||||
injectFn: o.ExternalReference;
|
NgModule = 4,
|
||||||
isPipe?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,7 +167,7 @@ export interface R3FactoryFn {
|
||||||
/**
|
/**
|
||||||
* Construct a factory function expression for the given `R3FactoryMetadata`.
|
* Construct a factory function expression for the given `R3FactoryMetadata`.
|
||||||
*/
|
*/
|
||||||
export function compileFactoryFunction(meta: R3FactoryMetadata, isPipe = false): R3FactoryFn {
|
export function compileFactoryFunction(meta: R3FactoryMetadata): R3FactoryFn {
|
||||||
const t = o.variable('t');
|
const t = o.variable('t');
|
||||||
const statements: o.Statement[] = [];
|
const statements: o.Statement[] = [];
|
||||||
|
|
||||||
|
@ -179,8 +183,9 @@ export function compileFactoryFunction(meta: R3FactoryMetadata, isPipe = false):
|
||||||
if (meta.deps !== null) {
|
if (meta.deps !== null) {
|
||||||
// There is a constructor (either explicitly or implicitly defined).
|
// There is a constructor (either explicitly or implicitly defined).
|
||||||
if (meta.deps !== 'invalid') {
|
if (meta.deps !== 'invalid') {
|
||||||
ctorExpr =
|
ctorExpr = new o.InstantiateExpr(
|
||||||
new o.InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.injectFn, isPipe));
|
typeForCtor,
|
||||||
|
injectDependencies(meta.deps, meta.injectFn, meta.target === R3FactoryTarget.Pipe));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const baseFactory = o.variable(`ɵ${meta.name}_BaseFactory`);
|
const baseFactory = o.variable(`ɵ${meta.name}_BaseFactory`);
|
||||||
|
@ -206,7 +211,7 @@ export function compileFactoryFunction(meta: R3FactoryMetadata, isPipe = false):
|
||||||
if (ctorExprFinal !== null) {
|
if (ctorExprFinal !== null) {
|
||||||
ctorStmt = r.set(ctorExprFinal).toStmt();
|
ctorStmt = r.set(ctorExprFinal).toStmt();
|
||||||
} else {
|
} else {
|
||||||
ctorStmt = makeErrorStmt(meta.name);
|
ctorStmt = o.importExpr(R3.invalidFactory).callFn([]).toStmt();
|
||||||
}
|
}
|
||||||
body.push(o.ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()]));
|
body.push(o.ifStmt(t, [ctorStmt], [r.set(nonCtorExpr).toStmt()]));
|
||||||
return r;
|
return r;
|
||||||
|
@ -228,8 +233,9 @@ export function compileFactoryFunction(meta: R3FactoryMetadata, isPipe = false):
|
||||||
} else if (isDelegatedMetadata(meta)) {
|
} else if (isDelegatedMetadata(meta)) {
|
||||||
// This type is created with a delegated factory. If a type parameter is not specified, call
|
// This type is created with a delegated factory. If a type parameter is not specified, call
|
||||||
// the factory instead.
|
// the factory instead.
|
||||||
const delegateArgs = injectDependencies(meta.delegateDeps, meta.injectFn, isPipe);
|
const delegateArgs =
|
||||||
// Either call `new delegate(...)` or `delegate(...)` depending on meta.useNewForDelegate.
|
injectDependencies(meta.delegateDeps, meta.injectFn, meta.target === R3FactoryTarget.Pipe);
|
||||||
|
// Either call `new delegate(...)` or `delegate(...)` depending on meta.delegateType.
|
||||||
const factoryExpr = new (
|
const factoryExpr = new (
|
||||||
meta.delegateType === R3FactoryDelegateType.Class ?
|
meta.delegateType === R3FactoryDelegateType.Class ?
|
||||||
o.InstantiateExpr :
|
o.InstantiateExpr :
|
||||||
|
@ -245,7 +251,7 @@ export function compileFactoryFunction(meta: R3FactoryMetadata, isPipe = false):
|
||||||
if (retExpr !== null) {
|
if (retExpr !== null) {
|
||||||
body.push(new o.ReturnStatement(retExpr));
|
body.push(new o.ReturnStatement(retExpr));
|
||||||
} else {
|
} else {
|
||||||
body.push(makeErrorStmt(meta.name));
|
body.push(o.importExpr(R3.invalidFactory).callFn([]).toStmt());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -258,21 +264,6 @@ export function compileFactoryFunction(meta: R3FactoryMetadata, isPipe = false):
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the factory def (`ɵfac`) from directive/component/pipe metadata.
|
|
||||||
*/
|
|
||||||
export function compileFactoryFromMetadata(meta: R3FactoryDefMetadata): R3FactoryFn {
|
|
||||||
return compileFactoryFunction(
|
|
||||||
{
|
|
||||||
name: meta.name,
|
|
||||||
type: meta.type,
|
|
||||||
deps: meta.deps,
|
|
||||||
typeArgumentCount: meta.typeArgumentCount,
|
|
||||||
injectFn: meta.injectFn,
|
|
||||||
},
|
|
||||||
meta.isPipe);
|
|
||||||
}
|
|
||||||
|
|
||||||
function injectDependencies(
|
function injectDependencies(
|
||||||
deps: R3DependencyMetadata[], injectFn: o.ExternalReference, isPipe: boolean): o.Expression[] {
|
deps: R3DependencyMetadata[], injectFn: o.ExternalReference, isPipe: boolean): o.Expression[] {
|
||||||
return deps.map(dep => compileInjectDependency(dep, injectFn, isPipe));
|
return deps.map(dep => compileInjectDependency(dep, injectFn, isPipe));
|
||||||
|
@ -358,13 +349,6 @@ export function dependenciesFromGlobalMetadata(
|
||||||
return deps;
|
return deps;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeErrorStmt(name: string): o.Statement {
|
|
||||||
return new o.ThrowStmt(new o.InstantiateExpr(new o.ReadVarExpr('Error'), [
|
|
||||||
o.literal(
|
|
||||||
`${name} has a constructor which is not compatible with Dependency Injection. It should probably not be @Injectable().`)
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDelegatedMetadata(meta: R3FactoryMetadata): meta is R3DelegatedFactoryMetadata|
|
function isDelegatedMetadata(meta: R3FactoryMetadata): meta is R3DelegatedFactoryMetadata|
|
||||||
R3DelegatedFnOrClassMetadata {
|
R3DelegatedFnOrClassMetadata {
|
||||||
return (meta as any).delegateType !== undefined;
|
return (meta as any).delegateType !== undefined;
|
||||||
|
|
|
@ -211,6 +211,7 @@ export class Identifiers {
|
||||||
o.ExternalReference = {name: 'ɵɵinjectPipeChangeDetectorRef', moduleName: CORE};
|
o.ExternalReference = {name: 'ɵɵinjectPipeChangeDetectorRef', moduleName: CORE};
|
||||||
|
|
||||||
static directiveInject: o.ExternalReference = {name: 'ɵɵdirectiveInject', moduleName: CORE};
|
static directiveInject: o.ExternalReference = {name: 'ɵɵdirectiveInject', moduleName: CORE};
|
||||||
|
static invalidFactory: o.ExternalReference = {name: 'ɵɵinvalidFactory', moduleName: CORE};
|
||||||
|
|
||||||
static templateRefExtractor:
|
static templateRefExtractor:
|
||||||
o.ExternalReference = {name: 'ɵɵtemplateRefExtractor', moduleName: CORE};
|
o.ExternalReference = {name: 'ɵɵtemplateRefExtractor', moduleName: CORE};
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {mapLiteral} from '../output/map_util';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {OutputContext} from '../util';
|
import {OutputContext} from '../util';
|
||||||
|
|
||||||
import {R3DependencyMetadata, compileFactoryFunction} from './r3_factory';
|
import {R3DependencyMetadata, R3FactoryTarget, compileFactoryFunction} from './r3_factory';
|
||||||
import {Identifiers as R3} from './r3_identifiers';
|
import {Identifiers as R3} from './r3_identifiers';
|
||||||
import {R3Reference, convertMetaToOutput, mapToMapExpression} from './util';
|
import {R3Reference, convertMetaToOutput, mapToMapExpression} from './util';
|
||||||
|
|
||||||
|
@ -210,6 +210,7 @@ export function compileInjector(meta: R3InjectorMetadata): R3InjectorDef {
|
||||||
typeArgumentCount: 0,
|
typeArgumentCount: 0,
|
||||||
deps: meta.deps,
|
deps: meta.deps,
|
||||||
injectFn: R3.inject,
|
injectFn: R3.inject,
|
||||||
|
target: R3FactoryTarget.NgModule,
|
||||||
});
|
});
|
||||||
const definitionMap = {
|
const definitionMap = {
|
||||||
factory: result.factory,
|
factory: result.factory,
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {DefinitionKind} from '../constant_pool';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
import {OutputContext, error} from '../util';
|
import {OutputContext, error} from '../util';
|
||||||
|
|
||||||
import {R3DependencyMetadata, compileFactoryFromMetadata, compileFactoryFunction, dependenciesFromGlobalMetadata} from './r3_factory';
|
import {R3DependencyMetadata, R3FactoryTarget, compileFactoryFunction, dependenciesFromGlobalMetadata} from './r3_factory';
|
||||||
import {Identifiers as R3} from './r3_identifiers';
|
import {Identifiers as R3} from './r3_identifiers';
|
||||||
import {typeWithParameters} from './util';
|
import {typeWithParameters} from './util';
|
||||||
|
|
||||||
|
@ -89,8 +89,8 @@ export function compilePipeFromRender2(
|
||||||
pure: pipe.pure,
|
pure: pipe.pure,
|
||||||
};
|
};
|
||||||
const res = compilePipeFromMetadata(metadata);
|
const res = compilePipeFromMetadata(metadata);
|
||||||
const factoryRes =
|
const factoryRes = compileFactoryFunction(
|
||||||
compileFactoryFromMetadata({...metadata, injectFn: R3.directiveInject, isPipe: true});
|
{...metadata, injectFn: R3.directiveInject, target: R3FactoryTarget.Pipe});
|
||||||
const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Pipe);
|
const definitionField = outputCtx.constantPool.propertyNameOf(DefinitionKind.Pipe);
|
||||||
const ngFactoryDefStatement = new o.ClassStmt(
|
const ngFactoryDefStatement = new o.ClassStmt(
|
||||||
/* name */ name,
|
/* name */ name,
|
||||||
|
|
|
@ -41,7 +41,7 @@ export interface R3DirectiveMetadata {
|
||||||
/**
|
/**
|
||||||
* Dependencies of the directive's constructor.
|
* Dependencies of the directive's constructor.
|
||||||
*/
|
*/
|
||||||
deps: R3DependencyMetadata[]|null;
|
deps: R3DependencyMetadata[]|'invalid'|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unparsed selector of the directive, or `null` if there was no selector.
|
* Unparsed selector of the directive, or `null` if there was no selector.
|
||||||
|
|
|
@ -12,17 +12,17 @@ import {CompileReflector} from '../../compile_reflector';
|
||||||
import {BindingForm, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
import {BindingForm, convertPropertyBinding} from '../../compiler_util/expression_converter';
|
||||||
import {ConstantPool, DefinitionKind} from '../../constant_pool';
|
import {ConstantPool, DefinitionKind} from '../../constant_pool';
|
||||||
import * as core from '../../core';
|
import * as core from '../../core';
|
||||||
import {AST, Interpolation, ParsedEvent, ParsedEventType, ParsedProperty} from '../../expression_parser/ast';
|
import {AST, ParsedEvent, ParsedEventType, ParsedProperty} from '../../expression_parser/ast';
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||||
import * as o from '../../output/output_ast';
|
import * as o from '../../output/output_ast';
|
||||||
import {ParseError, ParseSourceSpan, typeSourceSpan} from '../../parse_util';
|
import {ParseError, ParseSourceSpan} from '../../parse_util';
|
||||||
import {CssSelector, SelectorMatcher} from '../../selector';
|
import {CssSelector, SelectorMatcher} from '../../selector';
|
||||||
import {ShadowCss} from '../../shadow_css';
|
import {ShadowCss} from '../../shadow_css';
|
||||||
import {CONTENT_ATTR, HOST_ATTR} from '../../style_compiler';
|
import {CONTENT_ATTR, HOST_ATTR} from '../../style_compiler';
|
||||||
import {BindingParser} from '../../template_parser/binding_parser';
|
import {BindingParser} from '../../template_parser/binding_parser';
|
||||||
import {OutputContext, error} from '../../util';
|
import {OutputContext, error} from '../../util';
|
||||||
import {BoundEvent} from '../r3_ast';
|
import {BoundEvent} from '../r3_ast';
|
||||||
import {compileFactoryFromMetadata, compileFactoryFunction, dependenciesFromGlobalMetadata} from '../r3_factory';
|
import {R3FactoryTarget, compileFactoryFunction} from '../r3_factory';
|
||||||
import {Identifiers as R3} from '../r3_identifiers';
|
import {Identifiers as R3} from '../r3_identifiers';
|
||||||
import {Render3ParseResult} from '../r3_template_transform';
|
import {Render3ParseResult} from '../r3_template_transform';
|
||||||
import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
|
import {prepareSyntheticListenerFunctionName, prepareSyntheticPropertyName, typeWithParameters} from '../util';
|
||||||
|
@ -330,7 +330,8 @@ export function compileDirectiveFromRender2(
|
||||||
|
|
||||||
const meta = directiveMetadataFromGlobalMetadata(directive, outputCtx, reflector);
|
const meta = directiveMetadataFromGlobalMetadata(directive, outputCtx, reflector);
|
||||||
const res = compileDirectiveFromMetadata(meta, outputCtx.constantPool, bindingParser);
|
const res = compileDirectiveFromMetadata(meta, outputCtx.constantPool, bindingParser);
|
||||||
const factoryRes = compileFactoryFromMetadata({...meta, injectFn: R3.directiveInject});
|
const factoryRes = compileFactoryFunction(
|
||||||
|
{...meta, injectFn: R3.directiveInject, target: R3FactoryTarget.Directive});
|
||||||
const ngFactoryDefStatement = new o.ClassStmt(
|
const ngFactoryDefStatement = new o.ClassStmt(
|
||||||
name, null,
|
name, null,
|
||||||
[new o.ClassField('ɵfac', o.INFERRED_TYPE, [o.StmtModifier.Static], factoryRes.factory)], [],
|
[new o.ClassField('ɵfac', o.INFERRED_TYPE, [o.StmtModifier.Static], factoryRes.factory)], [],
|
||||||
|
@ -382,7 +383,8 @@ export function compileComponentFromRender2(
|
||||||
i18nUseExternalIds: true,
|
i18nUseExternalIds: true,
|
||||||
};
|
};
|
||||||
const res = compileComponentFromMetadata(meta, outputCtx.constantPool, bindingParser);
|
const res = compileComponentFromMetadata(meta, outputCtx.constantPool, bindingParser);
|
||||||
const factoryRes = compileFactoryFromMetadata({...meta, injectFn: R3.directiveInject});
|
const factoryRes = compileFactoryFunction(
|
||||||
|
{...meta, injectFn: R3.directiveInject, target: R3FactoryTarget.Directive});
|
||||||
const ngFactoryDefStatement = new o.ClassStmt(
|
const ngFactoryDefStatement = new o.ClassStmt(
|
||||||
name, null,
|
name, null,
|
||||||
[new o.ClassField('ɵfac', o.INFERRED_TYPE, [o.StmtModifier.Static], factoryRes.factory)], [],
|
[new o.ClassField('ɵfac', o.INFERRED_TYPE, [o.StmtModifier.Static], factoryRes.factory)], [],
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as core from '../../core/src/compiler/compiler_facade_interface';
|
import * as core from '../../core/src/compiler/compiler_facade_interface';
|
||||||
import {R3ResolvedDependencyType} from '../public_api';
|
import {R3FactoryTarget, R3ResolvedDependencyType} from '../public_api';
|
||||||
import * as compiler from '../src/compiler_facade_interface';
|
import * as compiler from '../src/compiler_facade_interface';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,6 +60,15 @@ const coreR3ResolvedDependencyType3: core.R3ResolvedDependencyType =
|
||||||
const compilerR3ResolvedDependencyType3: compiler.R3ResolvedDependencyType =
|
const compilerR3ResolvedDependencyType3: compiler.R3ResolvedDependencyType =
|
||||||
null !as R3ResolvedDependencyType;
|
null !as R3ResolvedDependencyType;
|
||||||
|
|
||||||
|
const coreR3FactoryTarget: core.R3FactoryTarget = null !as compiler.R3FactoryTarget;
|
||||||
|
const compilerR3FactoryTarget: compiler.R3FactoryTarget = null !as core.R3FactoryTarget;
|
||||||
|
|
||||||
|
const coreR3FactoryTarget2: R3FactoryTarget = null !as core.R3FactoryTarget;
|
||||||
|
const compilerR3FactoryTarget2: R3FactoryTarget = null !as core.R3FactoryTarget;
|
||||||
|
|
||||||
|
const coreR3FactoryTarget3: core.R3FactoryTarget = null !as R3FactoryTarget;
|
||||||
|
const compilerR3FactoryTarget3: compiler.R3FactoryTarget = null !as R3FactoryTarget;
|
||||||
|
|
||||||
const coreR3DependencyMetadataFacade: core.R3DependencyMetadataFacade =
|
const coreR3DependencyMetadataFacade: core.R3DependencyMetadataFacade =
|
||||||
null !as compiler.R3DependencyMetadataFacade;
|
null !as compiler.R3DependencyMetadataFacade;
|
||||||
const compilerR3DependencyMetadataFacade: compiler.R3DependencyMetadataFacade =
|
const compilerR3DependencyMetadataFacade: compiler.R3DependencyMetadataFacade =
|
||||||
|
|
|
@ -45,6 +45,7 @@ export interface CompilerFacade {
|
||||||
createParseSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan;
|
createParseSourceSpan(kind: string, typeName: string, sourceUrl: string): ParseSourceSpan;
|
||||||
|
|
||||||
R3ResolvedDependencyType: typeof R3ResolvedDependencyType;
|
R3ResolvedDependencyType: typeof R3ResolvedDependencyType;
|
||||||
|
R3FactoryTarget: typeof R3FactoryTarget;
|
||||||
ResourceLoader: {new (): ResourceLoader};
|
ResourceLoader: {new (): ResourceLoader};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +71,14 @@ export enum R3ResolvedDependencyType {
|
||||||
ChangeDetectorRef = 2,
|
ChangeDetectorRef = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum R3FactoryTarget {
|
||||||
|
Directive = 0,
|
||||||
|
Component = 1,
|
||||||
|
Injectable = 2,
|
||||||
|
Pipe = 3,
|
||||||
|
NgModule = 4,
|
||||||
|
}
|
||||||
|
|
||||||
export interface R3DependencyMetadataFacade {
|
export interface R3DependencyMetadataFacade {
|
||||||
token: any;
|
token: any;
|
||||||
resolved: R3ResolvedDependencyType;
|
resolved: R3ResolvedDependencyType;
|
||||||
|
@ -167,7 +176,7 @@ export interface R3FactoryDefMetadataFacade {
|
||||||
typeArgumentCount: number;
|
typeArgumentCount: number;
|
||||||
deps: R3DependencyMetadataFacade[]|null;
|
deps: R3DependencyMetadataFacade[]|null;
|
||||||
injectFn: 'directiveInject'|'inject';
|
injectFn: 'directiveInject'|'inject';
|
||||||
isPipe: boolean;
|
target: R3FactoryTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ViewEncapsulation = number;
|
export type ViewEncapsulation = number;
|
||||||
|
|
|
@ -34,6 +34,7 @@ export {
|
||||||
ɵɵdirectiveInject,
|
ɵɵdirectiveInject,
|
||||||
ɵɵinjectAttribute,
|
ɵɵinjectAttribute,
|
||||||
ɵɵinjectPipeChangeDetectorRef,
|
ɵɵinjectPipeChangeDetectorRef,
|
||||||
|
ɵɵinvalidFactory,
|
||||||
ɵɵgetFactoryOf,
|
ɵɵgetFactoryOf,
|
||||||
ɵɵgetInheritedFactory,
|
ɵɵgetInheritedFactory,
|
||||||
ɵɵsetComponentScope,
|
ɵɵsetComponentScope,
|
||||||
|
|
|
@ -48,14 +48,14 @@ export function compileInjectable(type: Type<any>, srcMeta?: Injectable): void {
|
||||||
get: () => {
|
get: () => {
|
||||||
if (ngFactoryDef === null) {
|
if (ngFactoryDef === null) {
|
||||||
const metadata = getInjectableMetadata(type, srcMeta);
|
const metadata = getInjectableMetadata(type, srcMeta);
|
||||||
ngFactoryDef =
|
const compiler = getCompilerFacade();
|
||||||
getCompilerFacade().compileFactory(angularCoreDiEnv, `ng:///${type.name}/ɵfac.js`, {
|
ngFactoryDef = compiler.compileFactory(angularCoreDiEnv, `ng:///${type.name}/ɵfac.js`, {
|
||||||
name: metadata.name,
|
name: metadata.name,
|
||||||
type: metadata.type,
|
type: metadata.type,
|
||||||
typeArgumentCount: metadata.typeArgumentCount,
|
typeArgumentCount: metadata.typeArgumentCount,
|
||||||
deps: reflectDependencies(type),
|
deps: reflectDependencies(type),
|
||||||
injectFn: 'inject',
|
injectFn: 'inject',
|
||||||
isPipe: false
|
target: compiler.R3FactoryTarget.Pipe
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return ngFactoryDef;
|
return ngFactoryDef;
|
||||||
|
|
|
@ -56,6 +56,7 @@ export {
|
||||||
ɵɵcontainerRefreshStart,
|
ɵɵcontainerRefreshStart,
|
||||||
|
|
||||||
ɵɵdirectiveInject,
|
ɵɵdirectiveInject,
|
||||||
|
ɵɵinvalidFactory,
|
||||||
|
|
||||||
ɵɵelement,
|
ɵɵelement,
|
||||||
ɵɵelementContainer,
|
ɵɵelementContainer,
|
||||||
|
|
|
@ -59,3 +59,21 @@ export function ɵɵdirectiveInject<T>(
|
||||||
export function ɵɵinjectAttribute(attrNameToInject: string): string|null {
|
export function ɵɵinjectAttribute(attrNameToInject: string): string|null {
|
||||||
return injectAttributeImpl(getPreviousOrParentTNode(), attrNameToInject);
|
return injectAttributeImpl(getPreviousOrParentTNode(), attrNameToInject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error indicating that a factory function could not be generated by the compiler for a
|
||||||
|
* particular class.
|
||||||
|
*
|
||||||
|
* This instruction allows the actual error message to be optimized away when ngDevMode is turned
|
||||||
|
* off, saving bytes of generated code while still providing a good experience in dev mode.
|
||||||
|
*
|
||||||
|
* The name of the class is not mentioned here, but will be in the generated factory function name
|
||||||
|
* and thus in the stack trace.
|
||||||
|
*
|
||||||
|
* @codeGenApi
|
||||||
|
*/
|
||||||
|
export function ɵɵinvalidFactory(): never {
|
||||||
|
const msg =
|
||||||
|
ngDevMode ? `This constructor was not compatible with Dependency Injection.` : 'invalid';
|
||||||
|
throw new Error(msg);
|
||||||
|
}
|
||||||
|
|
|
@ -165,9 +165,12 @@ function addDirectiveFactoryDef(type: Type<any>, metadata: Directive | Component
|
||||||
get: () => {
|
get: () => {
|
||||||
if (ngFactoryDef === null) {
|
if (ngFactoryDef === null) {
|
||||||
const meta = getDirectiveMetadata(type, metadata);
|
const meta = getDirectiveMetadata(type, metadata);
|
||||||
ngFactoryDef = getCompilerFacade().compileFactory(
|
const compiler = getCompilerFacade();
|
||||||
angularCoreEnv, `ng:///${type.name}/ɵfac.js`,
|
ngFactoryDef = compiler.compileFactory(angularCoreEnv, `ng:///${type.name}/ɵfac.js`, {
|
||||||
{...meta.metadata, injectFn: 'directiveInject', isPipe: false});
|
...meta.metadata,
|
||||||
|
injectFn: 'directiveInject',
|
||||||
|
target: compiler.R3FactoryTarget.Directive
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return ngFactoryDef;
|
return ngFactoryDef;
|
||||||
},
|
},
|
||||||
|
|
|
@ -42,6 +42,7 @@ export const angularCoreEnv: {[name: string]: Function} =
|
||||||
'ɵɵgetInheritedFactory': r3.ɵɵgetInheritedFactory,
|
'ɵɵgetInheritedFactory': r3.ɵɵgetInheritedFactory,
|
||||||
'ɵɵinject': ɵɵinject,
|
'ɵɵinject': ɵɵinject,
|
||||||
'ɵɵinjectAttribute': r3.ɵɵinjectAttribute,
|
'ɵɵinjectAttribute': r3.ɵɵinjectAttribute,
|
||||||
|
'ɵɵinvalidFactory': r3.ɵɵinvalidFactory,
|
||||||
'ɵɵinjectPipeChangeDetectorRef': r3.ɵɵinjectPipeChangeDetectorRef,
|
'ɵɵinjectPipeChangeDetectorRef': r3.ɵɵinjectPipeChangeDetectorRef,
|
||||||
'ɵɵtemplateRefExtractor': r3.ɵɵtemplateRefExtractor,
|
'ɵɵtemplateRefExtractor': r3.ɵɵtemplateRefExtractor,
|
||||||
'ɵɵNgOnChangesFeature': r3.ɵɵNgOnChangesFeature,
|
'ɵɵNgOnChangesFeature': r3.ɵɵNgOnChangesFeature,
|
||||||
|
|
|
@ -22,9 +22,10 @@ export function compilePipe(type: Type<any>, meta: Pipe): void {
|
||||||
get: () => {
|
get: () => {
|
||||||
if (ngFactoryDef === null) {
|
if (ngFactoryDef === null) {
|
||||||
const metadata = getPipeMetadata(type, meta);
|
const metadata = getPipeMetadata(type, meta);
|
||||||
ngFactoryDef = getCompilerFacade().compileFactory(
|
const compiler = getCompilerFacade();
|
||||||
|
ngFactoryDef = compiler.compileFactory(
|
||||||
angularCoreEnv, `ng:///${metadata.name}/ɵfac.js`,
|
angularCoreEnv, `ng:///${metadata.name}/ɵfac.js`,
|
||||||
{...metadata, injectFn: 'directiveInject', isPipe: true});
|
{...metadata, injectFn: 'directiveInject', target: compiler.R3FactoryTarget.Pipe});
|
||||||
}
|
}
|
||||||
return ngFactoryDef;
|
return ngFactoryDef;
|
||||||
},
|
},
|
||||||
|
|
|
@ -921,6 +921,8 @@ export interface ɵɵInjectorDef<T> {
|
||||||
|
|
||||||
export declare function ɵɵinjectPipeChangeDetectorRef(flags?: InjectFlags): ChangeDetectorRef | null;
|
export declare function ɵɵinjectPipeChangeDetectorRef(flags?: InjectFlags): ChangeDetectorRef | null;
|
||||||
|
|
||||||
|
export declare function ɵɵinvalidFactory(): never;
|
||||||
|
|
||||||
export declare function ɵɵlistener(eventName: string, listenerFn: (e?: any) => any, useCapture?: boolean, eventTargetResolver?: GlobalTargetResolver): void;
|
export declare function ɵɵlistener(eventName: string, listenerFn: (e?: any) => any, useCapture?: boolean, eventTargetResolver?: GlobalTargetResolver): void;
|
||||||
|
|
||||||
export declare function ɵɵloadQuery<T>(): QueryList<T>;
|
export declare function ɵɵloadQuery<T>(): QueryList<T>;
|
||||||
|
|
Loading…
Reference in New Issue