fix(compiler): only warn for `@Injectable` classes with invalid args.
In v2.x, users had to annotate classes that they intended to use as tokens with `@Injectable`. This is no longer required in v4.x for tokens, and we now require the constructor parameters of classes annotated with `@Injectable` to be statically analyzable by ngc. This commit reduces the error into a warning if the constructor parameters do not meet this condition. DEPRECATION: - Classes annotated with `@Injectable` but whose constructor’s parameter types are not statically analyzable by ngc will produce a warning. Closes #15003
This commit is contained in:
parent
50ab06e29d
commit
5c34066058
|
@ -67,7 +67,7 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new CompileMetadataResolver(
|
||||||
config, new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
config, new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||||
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
||||||
symbolCache, staticReflector);
|
console, symbolCache, staticReflector);
|
||||||
// TODO(vicb): do not pass options.i18nFormat here
|
// TODO(vicb): do not pass options.i18nFormat here
|
||||||
const importResolver = {
|
const importResolver = {
|
||||||
getImportAs: (symbol: StaticSymbol) => symbolResolver.getImportAs(symbol),
|
getImportAs: (symbol: StaticSymbol) => symbolResolver.getImportAs(symbol),
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
/**
|
/**
|
||||||
* Extract i18n messages from source code
|
* Extract i18n messages from source code
|
||||||
*/
|
*/
|
||||||
import {ViewEncapsulation} from '@angular/core';
|
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
||||||
|
|
||||||
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
|
import {analyzeAndValidateNgModules, extractProgramSymbols} from '../aot/compiler';
|
||||||
import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities';
|
import {StaticAndDynamicReflectionCapabilities} from '../aot/static_reflection_capabilities';
|
||||||
|
@ -106,7 +106,7 @@ export class Extractor {
|
||||||
const resolver = new CompileMetadataResolver(
|
const resolver = new CompileMetadataResolver(
|
||||||
config, new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
config, new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
|
||||||
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
|
||||||
symbolCache, staticReflector);
|
new Console(), symbolCache, staticReflector);
|
||||||
|
|
||||||
// TODO(vicb): implicit tags & attributes
|
// TODO(vicb): implicit tags & attributes
|
||||||
const messageBundle = new MessageBundle(htmlParser, [], {}, locale);
|
const messageBundle = new MessageBundle(htmlParser, [], {}, locale);
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, RendererType2, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef, ɵERROR_COMPONENT_TYPE, ɵLIFECYCLE_HOOKS_VALUES, ɵReflectorReader, ɵccf as createComponentFactory, ɵreflector, ɵstringify as stringify} from '@angular/core';
|
import {Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, RendererType2, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef, ɵConsole as Console, ɵERROR_COMPONENT_TYPE, ɵLIFECYCLE_HOOKS_VALUES, ɵReflectorReader, ɵccf as createComponentFactory, ɵreflector, ɵstringify as stringify} from '@angular/core';
|
||||||
|
|
||||||
import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
|
import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
|
||||||
import {ngfactoryFilePath} from './aot/util';
|
import {ngfactoryFilePath} from './aot/util';
|
||||||
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
||||||
|
@ -49,7 +50,7 @@ export class CompileMetadataResolver {
|
||||||
private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver,
|
private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver,
|
||||||
private _summaryResolver: SummaryResolver<any>,
|
private _summaryResolver: SummaryResolver<any>,
|
||||||
private _schemaRegistry: ElementSchemaRegistry,
|
private _schemaRegistry: ElementSchemaRegistry,
|
||||||
private _directiveNormalizer: DirectiveNormalizer,
|
private _directiveNormalizer: DirectiveNormalizer, private _console: Console,
|
||||||
@Optional() private _staticSymbolCache: StaticSymbolCache,
|
@Optional() private _staticSymbolCache: StaticSymbolCache,
|
||||||
private _reflector: ɵReflectorReader = ɵreflector,
|
private _reflector: ɵReflectorReader = ɵreflector,
|
||||||
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {}
|
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {}
|
||||||
|
@ -665,7 +666,10 @@ export class CompileMetadataResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
getInjectableSummary(type: any): cpl.CompileTypeSummary {
|
getInjectableSummary(type: any): cpl.CompileTypeSummary {
|
||||||
return {summaryKind: cpl.CompileSummaryKind.Injectable, type: this._getTypeMetadata(type)};
|
return {
|
||||||
|
summaryKind: cpl.CompileSummaryKind.Injectable,
|
||||||
|
type: this._getTypeMetadata(type, null, false)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getInjectableMetadata(type: Type<any>, dependencies: any[] = null):
|
private _getInjectableMetadata(type: Type<any>, dependencies: any[] = null):
|
||||||
|
@ -677,11 +681,12 @@ export class CompileMetadataResolver {
|
||||||
return this._getTypeMetadata(type, dependencies);
|
return this._getTypeMetadata(type, dependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getTypeMetadata(type: Type<any>, dependencies: any[] = null): cpl.CompileTypeMetadata {
|
private _getTypeMetadata(type: Type<any>, dependencies: any[] = null, throwOnUnknownDeps = true):
|
||||||
|
cpl.CompileTypeMetadata {
|
||||||
const identifier = this._getIdentifierMetadata(type);
|
const identifier = this._getIdentifierMetadata(type);
|
||||||
return {
|
return {
|
||||||
reference: identifier.reference,
|
reference: identifier.reference,
|
||||||
diDeps: this._getDependenciesMetadata(identifier.reference, dependencies),
|
diDeps: this._getDependenciesMetadata(identifier.reference, dependencies, throwOnUnknownDeps),
|
||||||
lifecycleHooks:
|
lifecycleHooks:
|
||||||
ɵLIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, identifier.reference)),
|
ɵLIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, identifier.reference)),
|
||||||
};
|
};
|
||||||
|
@ -742,8 +747,9 @@ export class CompileMetadataResolver {
|
||||||
return pipeMeta;
|
return pipeMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getDependenciesMetadata(typeOrFunc: Type<any>|Function, dependencies: any[]):
|
private _getDependenciesMetadata(
|
||||||
cpl.CompileDiDependencyMetadata[] {
|
typeOrFunc: Type<any>|Function, dependencies: any[],
|
||||||
|
throwOnUnknownDeps = true): cpl.CompileDiDependencyMetadata[] {
|
||||||
let hasUnknownDeps = false;
|
let hasUnknownDeps = false;
|
||||||
const params = dependencies || this._reflector.parameters(typeOrFunc) || [];
|
const params = dependencies || this._reflector.parameters(typeOrFunc) || [];
|
||||||
|
|
||||||
|
@ -797,10 +803,13 @@ export class CompileMetadataResolver {
|
||||||
if (hasUnknownDeps) {
|
if (hasUnknownDeps) {
|
||||||
const depsTokens =
|
const depsTokens =
|
||||||
dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', ');
|
dependenciesMetadata.map((dep) => dep ? stringifyType(dep.token) : '?').join(', ');
|
||||||
this._reportError(
|
const message =
|
||||||
syntaxError(
|
`Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`;
|
||||||
`Can't resolve all parameters for ${stringifyType(typeOrFunc)}: (${depsTokens}).`),
|
if (throwOnUnknownDeps) {
|
||||||
typeOrFunc);
|
this._reportError(syntaxError(message), typeOrFunc);
|
||||||
|
} else {
|
||||||
|
this._console.warn(`Warning: ${message} This will become an error in Angular v5.x`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dependenciesMetadata;
|
return dependenciesMetadata;
|
||||||
|
|
|
@ -219,6 +219,35 @@ describe('compiler (unbundled Angular)', () => {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('errors', () => {
|
||||||
|
it('should only warn if not all arguments of an @Injectable class can be resolved',
|
||||||
|
fakeAsync(() => {
|
||||||
|
const FILES: MockData = {
|
||||||
|
app: {
|
||||||
|
'app.ts': `
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MyService {
|
||||||
|
constructor(a: boolean) {}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const host = new MockCompilerHost(['/app/app.ts'], FILES, angularFiles);
|
||||||
|
const aotHost = new MockAotCompilerHost(host);
|
||||||
|
let ok = false;
|
||||||
|
const warnSpy = spyOn(console, 'warn');
|
||||||
|
compile(host, aotHost, expectNoDiagnostics).then(() => ok = true);
|
||||||
|
|
||||||
|
tick();
|
||||||
|
|
||||||
|
expect(ok).toBe(true);
|
||||||
|
expect(warnSpy).toHaveBeenCalledWith(
|
||||||
|
`Warning: Can't resolve all parameters for MyService in /app/app.ts: (?). This will become an error in Angular v5.x`);
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('compiler (bundled Angular)', () => {
|
describe('compiler (bundled Angular)', () => {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AotSummaryResolver, CompileDirectiveMetadata, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, InterpolationConfig, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, SummaryResolver, UrlResolver, analyzeNgModules, componentModuleUrl, createOfflineCompileUrlResolver, extractProgramSymbols} from '@angular/compiler';
|
import {AotSummaryResolver, CompileDirectiveMetadata, CompileMetadataResolver, CompilerConfig, DEFAULT_INTERPOLATION_CONFIG, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, InterpolationConfig, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, SummaryResolver, UrlResolver, analyzeNgModules, componentModuleUrl, createOfflineCompileUrlResolver, extractProgramSymbols} from '@angular/compiler';
|
||||||
import {Type, ViewEncapsulation} from '@angular/core';
|
import {Type, ViewEncapsulation, ɵConsole as Console} from '@angular/core';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
@ -115,8 +115,8 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||||
|
|
||||||
result = this._resolver = new CompileMetadataResolver(
|
result = this._resolver = new CompileMetadataResolver(
|
||||||
config, moduleResolver, directiveResolver, pipeResolver, new SummaryResolver(),
|
config, moduleResolver, directiveResolver, pipeResolver, new SummaryResolver(),
|
||||||
elementSchemaRegistry, directiveNormalizer, this._staticSymbolCache, this.reflector,
|
elementSchemaRegistry, directiveNormalizer, new Console(), this._staticSymbolCache,
|
||||||
(error, type) => this.collectError(error, type && type.filePath));
|
this.reflector, (error, type) => this.collectError(error, type && type.filePath));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue