style(compiler-cli): reformat of codebase with new clang-format version (#36520)
This commit reformats the packages/compiler-cli tree using the new version of clang-format. PR Close #36520
This commit is contained in:
@ -35,7 +35,8 @@ export function translateDiagnostics(
|
||||
const fileName = span.start.file.url;
|
||||
ng.push({
|
||||
messageText: diagnosticMessageToString(diagnostic.messageText),
|
||||
category: diagnostic.category, span,
|
||||
category: diagnostic.category,
|
||||
span,
|
||||
source: SOURCE,
|
||||
code: DEFAULT_ERROR_CODE
|
||||
});
|
||||
@ -53,6 +54,6 @@ function sourceSpanOf(host: TypeCheckHost, source: ts.SourceFile, start: number)
|
||||
return host.parseSourceSpanOf(source.fileName, line, character);
|
||||
}
|
||||
|
||||
function diagnosticMessageToString(message: ts.DiagnosticMessageChain | string): string {
|
||||
function diagnosticMessageToString(message: ts.DiagnosticMessageChain|string): string {
|
||||
return ts.flattenDiagnosticMessageText(message, '\n');
|
||||
}
|
||||
|
@ -16,4 +16,4 @@ to the language service.
|
||||
*/
|
||||
export {MetadataCollector, ModuleMetadata} from './metadata';
|
||||
export {CompilerOptions} from './transformers/api';
|
||||
export {MetadataReaderCache, MetadataReaderHost, createMetadataReaderCache, readMetadata} from './transformers/metadata_reader';
|
||||
export {createMetadataReaderCache, MetadataReaderCache, MetadataReaderHost, readMetadata} from './transformers/metadata_reader';
|
||||
|
@ -24,9 +24,9 @@ import {NodeJSFileSystem, setFileSystem} from './ngtsc/file_system';
|
||||
export function main(
|
||||
args: string[], consoleError: (s: string) => void = console.error,
|
||||
config?: NgcParsedConfiguration, customTransformers?: api.CustomTransformers, programReuse?: {
|
||||
program: api.Program | undefined,
|
||||
program: api.Program|undefined,
|
||||
},
|
||||
modifiedResourceFiles?: Set<string>| null): number {
|
||||
modifiedResourceFiles?: Set<string>|null): number {
|
||||
let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
|
||||
config || readNgcCommandLineAndConfiguration(args);
|
||||
if (configErrors.length) {
|
||||
@ -47,7 +47,9 @@ export function main(
|
||||
options,
|
||||
emitFlags,
|
||||
oldProgram,
|
||||
emitCallback: createEmitCallback(options), customTransformers, modifiedResourceFiles
|
||||
emitCallback: createEmitCallback(options),
|
||||
customTransformers,
|
||||
modifiedResourceFiles
|
||||
});
|
||||
if (programReuse !== undefined) {
|
||||
programReuse.program = program;
|
||||
@ -57,8 +59,8 @@ export function main(
|
||||
|
||||
export function mainDiagnosticsForTest(
|
||||
args: string[], config?: NgcParsedConfiguration,
|
||||
programReuse?: {program: api.Program | undefined},
|
||||
modifiedResourceFiles?: Set<string>| null): ReadonlyArray<ts.Diagnostic|api.Diagnostic> {
|
||||
programReuse?: {program: api.Program|undefined},
|
||||
modifiedResourceFiles?: Set<string>|null): ReadonlyArray<ts.Diagnostic|api.Diagnostic> {
|
||||
let {project, rootNames, options, errors: configErrors, watch, emitFlags} =
|
||||
config || readNgcCommandLineAndConfiguration(args);
|
||||
if (configErrors.length) {
|
||||
@ -100,9 +102,10 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
|
||||
options.emitDecoratorMetadata = true;
|
||||
}
|
||||
const tsickleHost: Pick<
|
||||
tsickle.TsickleHost, 'shouldSkipTsickleProcessing'|'pathToModuleName'|
|
||||
'shouldIgnoreWarningsForPath'|'fileNameToModuleId'|'googmodule'|'untyped'|
|
||||
'convertIndexImportShorthand'|'transformDecorators'|'transformTypesToClosure'> = {
|
||||
tsickle.TsickleHost,
|
||||
'shouldSkipTsickleProcessing'|'pathToModuleName'|'shouldIgnoreWarningsForPath'|
|
||||
'fileNameToModuleId'|'googmodule'|'untyped'|'convertIndexImportShorthand'|
|
||||
'transformDecorators'|'transformTypesToClosure'> = {
|
||||
shouldSkipTsickleProcessing: (fileName) => /\.d\.ts$/.test(fileName) ||
|
||||
// View Engine's generated files were never intended to be processed with tsickle.
|
||||
(!options.enableIvy && GENERATED_FILES.test(fileName)),
|
||||
@ -111,7 +114,9 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
|
||||
fileNameToModuleId: (fileName) => fileName,
|
||||
googmodule: false,
|
||||
untyped: true,
|
||||
convertIndexImportShorthand: false, transformDecorators, transformTypesToClosure,
|
||||
convertIndexImportShorthand: false,
|
||||
transformDecorators,
|
||||
transformTypesToClosure,
|
||||
};
|
||||
|
||||
if (options.annotateForClosureCompiler || options.annotationsAs === 'static fields') {
|
||||
@ -147,7 +152,9 @@ function createEmitCallback(options: api.CompilerOptions): api.TsEmitCallback|un
|
||||
}
|
||||
}
|
||||
|
||||
export interface NgcParsedConfiguration extends ParsedConfiguration { watch?: boolean; }
|
||||
export interface NgcParsedConfiguration extends ParsedConfiguration {
|
||||
watch?: boolean;
|
||||
}
|
||||
|
||||
export function readNgcCommandLineAndConfiguration(args: string[]): NgcParsedConfiguration {
|
||||
const options: api.CompilerOptions = {};
|
||||
@ -194,7 +201,8 @@ export function readCommandLineAndConfiguration(
|
||||
}
|
||||
return {
|
||||
project,
|
||||
rootNames: config.rootNames, options,
|
||||
rootNames: config.rootNames,
|
||||
options,
|
||||
errors: config.errors,
|
||||
emitFlags: config.emitFlags
|
||||
};
|
||||
@ -237,7 +245,7 @@ export function watchMode(
|
||||
|
||||
function printDiagnostics(
|
||||
diagnostics: ReadonlyArray<ts.Diagnostic|api.Diagnostic>,
|
||||
options: api.CompilerOptions | undefined, consoleError: (s: string) => void): void {
|
||||
options: api.CompilerOptions|undefined, consoleError: (s: string) => void): void {
|
||||
if (diagnostics.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -47,8 +47,7 @@ function createSyntheticIndexHost<H extends ts.CompilerHost>(
|
||||
|
||||
newHost.writeFile =
|
||||
(fileName: string, data: string, writeByteOrderMark: boolean,
|
||||
onError: ((message: string) => void) | undefined,
|
||||
sourceFiles: Readonly<ts.SourceFile>[]) => {
|
||||
onError: ((message: string) => void)|undefined, sourceFiles: Readonly<ts.SourceFile>[]) => {
|
||||
delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
||||
if (fileName.match(DTS) && sourceFiles && sourceFiles.length == 1 &&
|
||||
path.normalize(sourceFiles[0].fileName) === normalSyntheticIndexName) {
|
||||
@ -103,7 +102,7 @@ export function createBundleIndexHost<H extends ts.CompilerHost>(
|
||||
// contents of the flat module index. The bundle produced during emit does use the metadata cache
|
||||
// with associated transforms, so the metadata will have lowered expressions, resource inlining,
|
||||
// etc.
|
||||
const getMetadataBundle = (cache: MetadataCache | null) => {
|
||||
const getMetadataBundle = (cache: MetadataCache|null) => {
|
||||
const bundler = new MetadataBundler(
|
||||
indexModule, ngOptions.flatModuleId, new CompilerHostAdapter(host, cache, ngOptions),
|
||||
ngOptions.flatModulePrivateSymbolPrefix);
|
||||
@ -113,7 +112,7 @@ export function createBundleIndexHost<H extends ts.CompilerHost>(
|
||||
// First, produce the bundle with no MetadataCache.
|
||||
const metadataBundle = getMetadataBundle(/* MetadataCache */ null);
|
||||
const name =
|
||||
path.join(path.dirname(indexModule), ngOptions.flatModuleOutFile !.replace(JS_EXT, '.ts'));
|
||||
path.join(path.dirname(indexModule), ngOptions.flatModuleOutFile!.replace(JS_EXT, '.ts'));
|
||||
const libraryIndex = `./${path.basename(indexModule)}`;
|
||||
const content = privateEntriesToIndex(libraryIndex, metadataBundle.privates);
|
||||
|
||||
|
@ -11,7 +11,7 @@ import * as ts from 'typescript';
|
||||
import {MetadataCache} from '../transformers/metadata_cache';
|
||||
|
||||
import {MetadataCollector} from './collector';
|
||||
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, METADATA_VERSION, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicCallExpression, isMetadataSymbolicExpression, isMethodMetadata} from './schema';
|
||||
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicCallExpression, isMetadataSymbolicExpression, isMethodMetadata, MemberMetadata, METADATA_VERSION, MetadataEntry, MetadataError, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata} from './schema';
|
||||
|
||||
|
||||
|
||||
@ -59,7 +59,9 @@ interface Symbol {
|
||||
privateName?: string;
|
||||
}
|
||||
|
||||
export interface BundleEntries { [name: string]: MetadataEntry; }
|
||||
export interface BundleEntries {
|
||||
[name: string]: MetadataEntry;
|
||||
}
|
||||
|
||||
export interface BundlePrivateEntry {
|
||||
privateName: string;
|
||||
@ -77,7 +79,7 @@ export interface MetadataBundlerHost {
|
||||
}
|
||||
|
||||
type StaticsMetadata = {
|
||||
[name: string]: MetadataValue | FunctionMetadata;
|
||||
[name: string]: MetadataValue|FunctionMetadata;
|
||||
};
|
||||
|
||||
export class MetadataBundler {
|
||||
@ -87,7 +89,7 @@ export class MetadataBundler {
|
||||
private rootModule: string;
|
||||
private privateSymbolPrefix: string;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
private exported !: Set<Symbol>;
|
||||
private exported!: Set<Symbol>;
|
||||
|
||||
constructor(
|
||||
private root: string, private importAs: string|undefined, private host: MetadataBundlerHost,
|
||||
@ -106,14 +108,14 @@ export class MetadataBundler {
|
||||
const privates = Array.from(this.symbolMap.values())
|
||||
.filter(s => s.referenced && s.isPrivate)
|
||||
.map(s => ({
|
||||
privateName: s.privateName !,
|
||||
name: s.declaration !.name,
|
||||
module: s.declaration !.module
|
||||
privateName: s.privateName!,
|
||||
name: s.declaration!.name,
|
||||
module: s.declaration!.module
|
||||
}));
|
||||
const origins = Array.from(this.symbolMap.values())
|
||||
.filter(s => s.referenced && !s.reexport)
|
||||
.reduce<{[name: string]: string}>((p, s) => {
|
||||
p[s.isPrivate ? s.privateName ! : s.name] = s.declaration !.module;
|
||||
p[s.isPrivate ? s.privateName! : s.name] = s.declaration!.module;
|
||||
return p;
|
||||
}, {});
|
||||
const exports = this.getReExports(exportedSymbols);
|
||||
@ -121,8 +123,10 @@ export class MetadataBundler {
|
||||
metadata: {
|
||||
__symbolic: 'module',
|
||||
version: METADATA_VERSION,
|
||||
exports: exports.length ? exports : undefined, metadata, origins,
|
||||
importAs: this.importAs !
|
||||
exports: exports.length ? exports : undefined,
|
||||
metadata,
|
||||
origins,
|
||||
importAs: this.importAs!
|
||||
},
|
||||
privates
|
||||
};
|
||||
@ -156,7 +160,7 @@ export class MetadataBundler {
|
||||
|
||||
const exportSymbol = (exportedSymbol: Symbol, exportAs: string) => {
|
||||
const symbol = this.symbolOf(moduleName, exportAs);
|
||||
result !.push(symbol);
|
||||
result!.push(symbol);
|
||||
exportedSymbol.reexportedAs = symbol;
|
||||
symbol.exports = exportedSymbol;
|
||||
};
|
||||
@ -276,18 +280,18 @@ export class MetadataBundler {
|
||||
Array.from(this.symbolMap.values()).forEach(symbol => {
|
||||
if (symbol.referenced && !symbol.reexport) {
|
||||
let name = symbol.name;
|
||||
const identifier = `${symbol.declaration!.module}:${symbol.declaration !.name}`;
|
||||
const identifier = `${symbol.declaration!.module}:${symbol.declaration!.name}`;
|
||||
if (symbol.isPrivate && !symbol.privateName) {
|
||||
name = newPrivateName(this.privateSymbolPrefix);
|
||||
symbol.privateName = name;
|
||||
}
|
||||
if (symbolsMap.has(identifier)) {
|
||||
const names = symbolsMap.get(identifier);
|
||||
names !.push(name);
|
||||
names!.push(name);
|
||||
} else {
|
||||
symbolsMap.set(identifier, [name]);
|
||||
}
|
||||
result[name] = symbol.value !;
|
||||
result[name] = symbol.value!;
|
||||
}
|
||||
});
|
||||
|
||||
@ -320,9 +324,9 @@ export class MetadataBundler {
|
||||
for (const symbol of exportedSymbols) {
|
||||
if (symbol.reexport) {
|
||||
// symbol.declaration is guaranteed to be defined during the phase this method is called.
|
||||
const declaration = symbol.declaration !;
|
||||
const declaration = symbol.declaration!;
|
||||
const module = declaration.module;
|
||||
if (declaration !.name == '*') {
|
||||
if (declaration!.name == '*') {
|
||||
// Reexport all the symbols.
|
||||
exportAlls.add(declaration.module);
|
||||
} else {
|
||||
@ -346,12 +350,12 @@ export class MetadataBundler {
|
||||
|
||||
private convertSymbol(symbol: Symbol) {
|
||||
// canonicalSymbol is ensured to be defined before this is called.
|
||||
const canonicalSymbol = symbol.canonicalSymbol !;
|
||||
const canonicalSymbol = symbol.canonicalSymbol!;
|
||||
|
||||
if (!canonicalSymbol.referenced) {
|
||||
canonicalSymbol.referenced = true;
|
||||
// declaration is ensured to be definded before this method is called.
|
||||
const declaration = canonicalSymbol.declaration !;
|
||||
const declaration = canonicalSymbol.declaration!;
|
||||
const module = this.getMetadata(declaration.module);
|
||||
if (module) {
|
||||
const value = module.metadata[declaration.name];
|
||||
@ -399,11 +403,11 @@ export class MetadataBundler {
|
||||
private convertMember(moduleName: string, member: MemberMetadata) {
|
||||
const result: MemberMetadata = {__symbolic: member.__symbolic};
|
||||
result.decorators =
|
||||
member.decorators && member.decorators.map(d => this.convertExpression(moduleName, d) !);
|
||||
member.decorators && member.decorators.map(d => this.convertExpression(moduleName, d)!);
|
||||
if (isMethodMetadata(member)) {
|
||||
(result as MethodMetadata).parameterDecorators = member.parameterDecorators &&
|
||||
member.parameterDecorators.map(
|
||||
d => d && d.map(p => this.convertExpression(moduleName, p) !));
|
||||
d => d && d.map(p => this.convertExpression(moduleName, p)!));
|
||||
if (isConstructorMetadata(member)) {
|
||||
if (member.parameters) {
|
||||
(result as ConstructorMetadata).parameters =
|
||||
@ -450,7 +454,7 @@ export class MetadataBundler {
|
||||
return this.convertError(moduleName, value);
|
||||
}
|
||||
if (isMetadataSymbolicExpression(value)) {
|
||||
return this.convertExpression(moduleName, value) !;
|
||||
return this.convertExpression(moduleName, value)!;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(v => this.convertValue(moduleName, v));
|
||||
@ -466,8 +470,8 @@ export class MetadataBundler {
|
||||
}
|
||||
|
||||
private convertExpression(
|
||||
moduleName: string, value: MetadataSymbolicExpression|MetadataError|null|
|
||||
undefined): MetadataSymbolicExpression|MetadataError|undefined|null {
|
||||
moduleName: string, value: MetadataSymbolicExpression|MetadataError|null|undefined):
|
||||
MetadataSymbolicExpression|MetadataError|undefined|null {
|
||||
if (value) {
|
||||
switch (value.__symbolic) {
|
||||
case 'error':
|
||||
@ -487,14 +491,15 @@ export class MetadataBundler {
|
||||
message: value.message,
|
||||
line: value.line,
|
||||
character: value.character,
|
||||
context: value.context, module
|
||||
context: value.context,
|
||||
module
|
||||
};
|
||||
}
|
||||
|
||||
private convertReference(moduleName: string, value: MetadataSymbolicReferenceExpression):
|
||||
MetadataSymbolicReferenceExpression|MetadataError|undefined {
|
||||
const createReference = (symbol: Symbol): MetadataSymbolicReferenceExpression => {
|
||||
const declaration = symbol.declaration !;
|
||||
const declaration = symbol.declaration!;
|
||||
if (declaration.module.startsWith('.')) {
|
||||
// Reference to a symbol defined in the module. Ensure it is converted then return a
|
||||
// references to the final symbol.
|
||||
@ -503,11 +508,11 @@ export class MetadataBundler {
|
||||
__symbolic: 'reference',
|
||||
get name() {
|
||||
// Resolved lazily because private names are assigned late.
|
||||
const canonicalSymbol = symbol.canonicalSymbol !;
|
||||
const canonicalSymbol = symbol.canonicalSymbol!;
|
||||
if (canonicalSymbol.isPrivate == null) {
|
||||
throw Error('Invalid state: isPrivate was not initialized');
|
||||
}
|
||||
return canonicalSymbol.isPrivate ? canonicalSymbol.privateName ! : canonicalSymbol.name;
|
||||
return canonicalSymbol.isPrivate ? canonicalSymbol.privateName! : canonicalSymbol.name;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
@ -584,7 +589,7 @@ export class MetadataBundler {
|
||||
|
||||
private convertExpressionNode(moduleName: string, value: MetadataSymbolicExpression):
|
||||
MetadataSymbolicExpression {
|
||||
const result: MetadataSymbolicExpression = { __symbolic: value.__symbolic } as any;
|
||||
const result: MetadataSymbolicExpression = {__symbolic: value.__symbolic} as any;
|
||||
for (const key in value) {
|
||||
(result as any)[key] = this.convertValue(moduleName, (value as any)[key]);
|
||||
}
|
||||
|
@ -8,8 +8,8 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Evaluator, errorSymbol, recordMapEntry} from './evaluator';
|
||||
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, InterfaceMetadata, METADATA_VERSION, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema';
|
||||
import {errorSymbol, Evaluator, recordMapEntry} from './evaluator';
|
||||
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, InterfaceMetadata, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata, MemberMetadata, METADATA_VERSION, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata} from './schema';
|
||||
import {Symbols} from './symbols';
|
||||
|
||||
const isStatic = (node: ts.Declaration) =>
|
||||
@ -60,12 +60,12 @@ export class MetadataCollector {
|
||||
new Map<MetadataValue|ClassMetadata|InterfaceMetadata|FunctionMetadata, ts.Node>();
|
||||
const composedSubstituter = substituteExpression && this.options.substituteExpression ?
|
||||
(value: MetadataValue, node: ts.Node) =>
|
||||
this.options.substituteExpression !(substituteExpression(value, node), node) :
|
||||
this.options.substituteExpression!(substituteExpression(value, node), node) :
|
||||
substituteExpression;
|
||||
const evaluatorOptions = substituteExpression ?
|
||||
{...this.options, substituteExpression: composedSubstituter} :
|
||||
this.options;
|
||||
let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined;
|
||||
let metadata: {[name: string]: MetadataValue|ClassMetadata|FunctionMetadata}|undefined;
|
||||
const evaluator = new Evaluator(locals, nodeMap, evaluatorOptions, (name, value) => {
|
||||
if (!metadata) metadata = {};
|
||||
metadata[name] = value;
|
||||
@ -88,9 +88,9 @@ export class MetadataCollector {
|
||||
return errorSymbol(message, node, context, sourceFile);
|
||||
}
|
||||
|
||||
function maybeGetSimpleFunction(
|
||||
functionDeclaration: ts.FunctionDeclaration |
|
||||
ts.MethodDeclaration): {func: FunctionMetadata, name: string}|undefined {
|
||||
function maybeGetSimpleFunction(functionDeclaration: ts.FunctionDeclaration|
|
||||
ts.MethodDeclaration): {func: FunctionMetadata, name: string}|
|
||||
undefined {
|
||||
if (functionDeclaration.name && functionDeclaration.name.kind == ts.SyntaxKind.Identifier) {
|
||||
const nameNode = <ts.Identifier>functionDeclaration.name;
|
||||
const functionName = nameNode.text;
|
||||
@ -119,8 +119,8 @@ export class MetadataCollector {
|
||||
function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata {
|
||||
const result: ClassMetadata = {__symbolic: 'class'};
|
||||
|
||||
function getDecorators(decorators: ReadonlyArray<ts.Decorator>| undefined):
|
||||
MetadataSymbolicExpression[]|undefined {
|
||||
function getDecorators(decorators: ReadonlyArray<ts.Decorator>|
|
||||
undefined): MetadataSymbolicExpression[]|undefined {
|
||||
if (decorators && decorators.length)
|
||||
return decorators.map(decorator => objFromDecorator(decorator));
|
||||
return undefined;
|
||||
@ -167,8 +167,8 @@ export class MetadataCollector {
|
||||
}
|
||||
|
||||
// static member
|
||||
let statics: {[name: string]: MetadataValue | FunctionMetadata}|null = null;
|
||||
function recordStaticMember(name: string, value: MetadataValue | FunctionMetadata) {
|
||||
let statics: {[name: string]: MetadataValue|FunctionMetadata}|null = null;
|
||||
function recordStaticMember(name: string, value: MetadataValue|FunctionMetadata) {
|
||||
if (!statics) statics = {};
|
||||
statics[name] = value;
|
||||
}
|
||||
@ -189,11 +189,10 @@ export class MetadataCollector {
|
||||
}
|
||||
const methodDecorators = getDecorators(method.decorators);
|
||||
const parameters = method.parameters;
|
||||
const parameterDecoratorData:
|
||||
((MetadataSymbolicExpression | MetadataError)[] | undefined)[] = [];
|
||||
const parametersData:
|
||||
(MetadataSymbolicReferenceExpression | MetadataError |
|
||||
MetadataSymbolicSelectExpression | null)[] = [];
|
||||
const parameterDecoratorData: ((MetadataSymbolicExpression | MetadataError)[]|
|
||||
undefined)[] = [];
|
||||
const parametersData: (MetadataSymbolicReferenceExpression|MetadataError|
|
||||
MetadataSymbolicSelectExpression|null)[] = [];
|
||||
let hasDecoratorData: boolean = false;
|
||||
let hasParameterData: boolean = false;
|
||||
for (const parameter of parameters) {
|
||||
@ -282,15 +281,14 @@ export class MetadataCollector {
|
||||
ts.getCombinedModifierFlags(node as ts.Declaration) & ts.ModifierFlags.Export;
|
||||
const isExportedIdentifier = (identifier?: ts.Identifier) =>
|
||||
identifier && exportMap.has(identifier.text);
|
||||
const isExported =
|
||||
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.TypeAliasDeclaration |
|
||||
ts.InterfaceDeclaration | ts.EnumDeclaration) =>
|
||||
isExport(node) || isExportedIdentifier(node.name);
|
||||
const isExported = (node: ts.FunctionDeclaration|ts.ClassDeclaration|ts.TypeAliasDeclaration|
|
||||
ts.InterfaceDeclaration|ts.EnumDeclaration) =>
|
||||
isExport(node) || isExportedIdentifier(node.name);
|
||||
const exportedIdentifierName = (identifier?: ts.Identifier) =>
|
||||
identifier && (exportMap.get(identifier.text) || identifier.text);
|
||||
const exportedName =
|
||||
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration |
|
||||
ts.TypeAliasDeclaration | ts.EnumDeclaration) => exportedIdentifierName(node.name);
|
||||
const exportedName = (node: ts.FunctionDeclaration|ts.ClassDeclaration|
|
||||
ts.InterfaceDeclaration|ts.TypeAliasDeclaration|ts.EnumDeclaration) =>
|
||||
exportedIdentifierName(node.name);
|
||||
|
||||
|
||||
// Pre-declare classes and functions
|
||||
@ -419,8 +417,8 @@ export class MetadataCollector {
|
||||
if (name) {
|
||||
if (!metadata) metadata = {};
|
||||
// TODO(alxhub): The literal here is not valid FunctionMetadata.
|
||||
metadata[name] = maybeFunc ? recordEntry(maybeFunc.func, node) :
|
||||
({ __symbolic: 'function' } as any);
|
||||
metadata[name] =
|
||||
maybeFunc ? recordEntry(maybeFunc.func, node) : ({__symbolic: 'function'} as any);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -456,7 +454,8 @@ export class MetadataCollector {
|
||||
operator: '+',
|
||||
left: {
|
||||
__symbolic: 'select',
|
||||
expression: recordEntry({__symbolic: 'reference', name: enumName}, node), name
|
||||
expression: recordEntry({__symbolic: 'reference', name: enumName}, node),
|
||||
name
|
||||
},
|
||||
} as any;
|
||||
} else {
|
||||
@ -555,7 +554,8 @@ export class MetadataCollector {
|
||||
}
|
||||
const result: ModuleMetadata = {
|
||||
__symbolic: 'module',
|
||||
version: this.options.version || METADATA_VERSION, metadata
|
||||
version: this.options.version || METADATA_VERSION,
|
||||
metadata
|
||||
};
|
||||
if (sourceFile.moduleName) result.importAs = sourceFile.moduleName;
|
||||
if (exports) result.exports = exports;
|
||||
@ -570,8 +570,7 @@ function validateMetadata(
|
||||
metadata: {[name: string]: MetadataEntry}) {
|
||||
let locals: Set<string> = new Set(['Array', 'Object', 'Set', 'Map', 'string', 'number', 'any']);
|
||||
|
||||
function validateExpression(
|
||||
expression: MetadataValue | MetadataSymbolicExpression | MetadataError) {
|
||||
function validateExpression(expression: MetadataValue|MetadataSymbolicExpression|MetadataError) {
|
||||
if (!expression) {
|
||||
return;
|
||||
} else if (Array.isArray(expression)) {
|
||||
@ -648,11 +647,11 @@ function validateMetadata(
|
||||
}
|
||||
if (classData.members) {
|
||||
Object.getOwnPropertyNames(classData.members)
|
||||
.forEach(name => classData.members ![name].forEach((m) => validateMember(classData, m)));
|
||||
.forEach(name => classData.members![name].forEach((m) => validateMember(classData, m)));
|
||||
}
|
||||
if (classData.statics) {
|
||||
Object.getOwnPropertyNames(classData.statics).forEach(name => {
|
||||
const staticMember = classData.statics ![name];
|
||||
const staticMember = classData.statics![name];
|
||||
if (isFunctionMetadata(staticMember)) {
|
||||
validateExpression(staticMember.value);
|
||||
} else {
|
||||
@ -675,7 +674,7 @@ function validateMetadata(
|
||||
}
|
||||
}
|
||||
|
||||
function shouldReportNode(node: ts.Node | undefined) {
|
||||
function shouldReportNode(node: ts.Node|undefined) {
|
||||
if (node) {
|
||||
const nodeStart = node.getStart();
|
||||
return !(
|
||||
@ -688,12 +687,13 @@ function validateMetadata(
|
||||
function reportError(error: MetadataError) {
|
||||
const node = nodeMap.get(error);
|
||||
if (shouldReportNode(node)) {
|
||||
const lineInfo = error.line != undefined ?
|
||||
error.character != undefined ? `:${error.line + 1}:${error.character + 1}` :
|
||||
`:${error.line + 1}` :
|
||||
'';
|
||||
throw new Error(
|
||||
`${sourceFile.fileName}${lineInfo}: Metadata collected contains an error that will be reported at runtime: ${expandedMessage(error)}.\n ${JSON.stringify(error)}`);
|
||||
const lineInfo = error.line != undefined ? error.character != undefined ?
|
||||
`:${error.line + 1}:${error.character + 1}` :
|
||||
`:${error.line + 1}` :
|
||||
'';
|
||||
throw new Error(`${sourceFile.fileName}${
|
||||
lineInfo}: Metadata collected contains an error that will be reported at runtime: ${
|
||||
expandedMessage(error)}.\n ${JSON.stringify(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -708,8 +708,9 @@ function validateMetadata(
|
||||
if (shouldReportNode(node)) {
|
||||
if (node) {
|
||||
const {line, character} = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
||||
throw new Error(
|
||||
`${sourceFile.fileName}:${line + 1}:${character + 1}: Error encountered in metadata generated for exported symbol '${name}': \n ${e.message}`);
|
||||
throw new Error(`${sourceFile.fileName}:${line + 1}:${
|
||||
character + 1}: Error encountered in metadata generated for exported symbol '${
|
||||
name}': \n ${e.message}`);
|
||||
}
|
||||
throw new Error(
|
||||
`Error encountered in metadata generated for exported symbol ${name}: \n ${e.message}`);
|
||||
@ -722,7 +723,7 @@ function validateMetadata(
|
||||
function namesOf(parameters: ts.NodeArray<ts.ParameterDeclaration>): string[] {
|
||||
const result: string[] = [];
|
||||
|
||||
function addNamesOf(name: ts.Identifier | ts.BindingPattern) {
|
||||
function addNamesOf(name: ts.Identifier|ts.BindingPattern) {
|
||||
if (name.kind == ts.SyntaxKind.Identifier) {
|
||||
const identifier = <ts.Identifier>name;
|
||||
result.push(identifier.text);
|
||||
@ -752,7 +753,8 @@ function expandedMessage(error: any): string {
|
||||
switch (error.message) {
|
||||
case 'Reference to non-exported class':
|
||||
if (error.context && error.context.className) {
|
||||
return `Reference to a non-exported class ${error.context.className}. Consider exporting the class`;
|
||||
return `Reference to a non-exported class ${
|
||||
error.context.className}. Consider exporting the class`;
|
||||
}
|
||||
break;
|
||||
case 'Variable not initialized':
|
||||
@ -771,7 +773,8 @@ function expandedMessage(error: any): string {
|
||||
'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function';
|
||||
case 'Reference to a local symbol':
|
||||
if (error.context && error.context.name) {
|
||||
return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`;
|
||||
return `Reference to a local (non-exported) symbol '${
|
||||
error.context.name}'. Consider exporting the symbol`;
|
||||
}
|
||||
}
|
||||
return error.message;
|
||||
|
@ -9,7 +9,7 @@
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CollectorOptions} from './collector';
|
||||
import {ClassMetadata, FunctionMetadata, InterfaceMetadata, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataSourceLocationInfo, MetadataSymbolicCallExpression, MetadataValue, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression} from './schema';
|
||||
import {ClassMetadata, FunctionMetadata, InterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataSourceLocationInfo, MetadataSymbolicCallExpression, MetadataValue} from './schema';
|
||||
import {Symbols} from './symbols';
|
||||
|
||||
|
||||
@ -46,8 +46,9 @@ export function recordMapEntry<T extends MetadataEntry>(
|
||||
sourceFile?: ts.SourceFile) {
|
||||
if (!nodeMap.has(entry)) {
|
||||
nodeMap.set(entry, node);
|
||||
if (node && (isMetadataImportedSymbolReferenceExpression(entry) ||
|
||||
isMetadataImportDefaultReference(entry)) &&
|
||||
if (node &&
|
||||
(isMetadataImportedSymbolReferenceExpression(entry) ||
|
||||
isMetadataImportDefaultReference(entry)) &&
|
||||
entry.line == null) {
|
||||
const info = sourceInfo(node, sourceFile);
|
||||
if (info.line != null) entry.line = info.line;
|
||||
@ -88,7 +89,7 @@ export interface ImportMetadata {
|
||||
}
|
||||
|
||||
|
||||
function getSourceFileOfNode(node: ts.Node | undefined): ts.SourceFile {
|
||||
function getSourceFileOfNode(node: ts.Node|undefined): ts.SourceFile {
|
||||
while (node && node.kind != ts.SyntaxKind.SourceFile) {
|
||||
node = node.parent;
|
||||
}
|
||||
@ -97,7 +98,7 @@ function getSourceFileOfNode(node: ts.Node | undefined): ts.SourceFile {
|
||||
|
||||
/* @internal */
|
||||
export function sourceInfo(
|
||||
node: ts.Node | undefined, sourceFile: ts.SourceFile | undefined): MetadataSourceLocationInfo {
|
||||
node: ts.Node|undefined, sourceFile: ts.SourceFile|undefined): MetadataSourceLocationInfo {
|
||||
if (node) {
|
||||
sourceFile = sourceFile || getSourceFileOfNode(node);
|
||||
if (sourceFile) {
|
||||
@ -435,7 +436,7 @@ export class Evaluator {
|
||||
case ts.SyntaxKind.TypeReference:
|
||||
const typeReferenceNode = <ts.TypeReferenceNode>node;
|
||||
const typeNameNode = typeReferenceNode.typeName;
|
||||
const getReference: (typeNameNode: ts.Identifier | ts.QualifiedName) => MetadataValue =
|
||||
const getReference: (typeNameNode: ts.Identifier|ts.QualifiedName) => MetadataValue =
|
||||
node => {
|
||||
if (typeNameNode.kind === ts.SyntaxKind.QualifiedName) {
|
||||
const qualifiedName = <ts.QualifiedName>node;
|
||||
@ -691,6 +692,6 @@ function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment {
|
||||
|
||||
const empty = ts.createNodeArray<any>();
|
||||
|
||||
function arrayOrEmpty<T extends ts.Node>(v: ts.NodeArray<T>| undefined): ts.NodeArray<T> {
|
||||
function arrayOrEmpty<T extends ts.Node>(v: ts.NodeArray<T>|undefined): ts.NodeArray<T> {
|
||||
return v || empty;
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
|
||||
export const METADATA_VERSION = 4;
|
||||
|
||||
export type MetadataEntry = ClassMetadata | InterfaceMetadata | FunctionMetadata | MetadataValue;
|
||||
export type MetadataEntry = ClassMetadata|InterfaceMetadata|FunctionMetadata|MetadataValue;
|
||||
|
||||
export interface ModuleMetadata {
|
||||
__symbolic: 'module';
|
||||
@ -43,18 +43,22 @@ export interface ClassMetadata {
|
||||
arity?: number;
|
||||
decorators?: (MetadataSymbolicExpression|MetadataError)[];
|
||||
members?: MetadataMap;
|
||||
statics?: {[name: string]: MetadataValue | FunctionMetadata};
|
||||
statics?: {[name: string]: MetadataValue|FunctionMetadata};
|
||||
}
|
||||
export function isClassMetadata(value: any): value is ClassMetadata {
|
||||
return value && value.__symbolic === 'class';
|
||||
}
|
||||
|
||||
export interface InterfaceMetadata { __symbolic: 'interface'; }
|
||||
export interface InterfaceMetadata {
|
||||
__symbolic: 'interface';
|
||||
}
|
||||
export function isInterfaceMetadata(value: any): value is InterfaceMetadata {
|
||||
return value && value.__symbolic === 'interface';
|
||||
}
|
||||
|
||||
export interface MetadataMap { [name: string]: MemberMetadata[]; }
|
||||
export interface MetadataMap {
|
||||
[name: string]: MemberMetadata[];
|
||||
}
|
||||
|
||||
export interface MemberMetadata {
|
||||
__symbolic: 'constructor'|'method'|'property';
|
||||
@ -99,24 +103,26 @@ export function isFunctionMetadata(value: any): value is FunctionMetadata {
|
||||
return value && value.__symbolic === 'function';
|
||||
}
|
||||
|
||||
export type MetadataValue = string | number | boolean | undefined | null | MetadataObject |
|
||||
MetadataArray | MetadataSymbolicExpression | MetadataSymbolicReferenceExpression |
|
||||
MetadataSymbolicBinaryExpression | MetadataSymbolicIndexExpression |
|
||||
MetadataSymbolicCallExpression | MetadataSymbolicPrefixExpression |
|
||||
MetadataSymbolicIfExpression | MetadataSymbolicSpreadExpression |
|
||||
MetadataSymbolicSelectExpression | MetadataError;
|
||||
export type MetadataValue = string|number|boolean|undefined|null|MetadataObject|MetadataArray|
|
||||
MetadataSymbolicExpression|MetadataSymbolicReferenceExpression|MetadataSymbolicBinaryExpression|
|
||||
MetadataSymbolicIndexExpression|MetadataSymbolicCallExpression|MetadataSymbolicPrefixExpression|
|
||||
MetadataSymbolicIfExpression|MetadataSymbolicSpreadExpression|MetadataSymbolicSelectExpression|
|
||||
MetadataError;
|
||||
|
||||
export interface MetadataObject { [name: string]: MetadataValue; }
|
||||
export interface MetadataObject {
|
||||
[name: string]: MetadataValue;
|
||||
}
|
||||
|
||||
export interface MetadataArray { [name: number]: MetadataValue; }
|
||||
export interface MetadataArray {
|
||||
[name: number]: MetadataValue;
|
||||
}
|
||||
|
||||
export type MetadataSymbolicExpression = MetadataSymbolicBinaryExpression |
|
||||
MetadataSymbolicIndexExpression | MetadataSymbolicIndexExpression |
|
||||
MetadataSymbolicCallExpression | MetadataSymbolicCallExpression |
|
||||
MetadataSymbolicPrefixExpression | MetadataSymbolicIfExpression |
|
||||
MetadataGlobalReferenceExpression | MetadataModuleReferenceExpression |
|
||||
MetadataImportedSymbolReferenceExpression | MetadataImportedDefaultReferenceExpression |
|
||||
MetadataSymbolicSelectExpression | MetadataSymbolicSpreadExpression;
|
||||
export type MetadataSymbolicExpression = MetadataSymbolicBinaryExpression|
|
||||
MetadataSymbolicIndexExpression|MetadataSymbolicIndexExpression|MetadataSymbolicCallExpression|
|
||||
MetadataSymbolicCallExpression|MetadataSymbolicPrefixExpression|MetadataSymbolicIfExpression|
|
||||
MetadataGlobalReferenceExpression|MetadataModuleReferenceExpression|
|
||||
MetadataImportedSymbolReferenceExpression|MetadataImportedDefaultReferenceExpression|
|
||||
MetadataSymbolicSelectExpression|MetadataSymbolicSpreadExpression;
|
||||
|
||||
export function isMetadataSymbolicExpression(value: any): value is MetadataSymbolicExpression {
|
||||
if (value) {
|
||||
@ -234,18 +240,17 @@ export function isMetadataImportedSymbolReferenceExpression(value: any):
|
||||
export interface MetadataImportedDefaultReferenceExpression extends MetadataSourceLocationInfo {
|
||||
__symbolic: 'reference';
|
||||
module: string;
|
||||
default:
|
||||
boolean;
|
||||
arguments?: MetadataValue[];
|
||||
default: boolean;
|
||||
arguments?: MetadataValue[];
|
||||
}
|
||||
export function isMetadataImportDefaultReference(value: any):
|
||||
value is MetadataImportedDefaultReferenceExpression {
|
||||
return value && value.module && value.default && isMetadataSymbolicReferenceExpression(value);
|
||||
}
|
||||
|
||||
export type MetadataSymbolicReferenceExpression = MetadataGlobalReferenceExpression |
|
||||
MetadataModuleReferenceExpression | MetadataImportedSymbolReferenceExpression |
|
||||
MetadataImportedDefaultReferenceExpression;
|
||||
export type MetadataSymbolicReferenceExpression =
|
||||
MetadataGlobalReferenceExpression|MetadataModuleReferenceExpression|
|
||||
MetadataImportedSymbolReferenceExpression|MetadataImportedDefaultReferenceExpression;
|
||||
export function isMetadataSymbolicReferenceExpression(value: any):
|
||||
value is MetadataSymbolicReferenceExpression {
|
||||
return value && value.__symbolic === 'reference';
|
||||
|
@ -12,7 +12,7 @@ import {MetadataSymbolicReferenceExpression, MetadataValue} from './schema';
|
||||
|
||||
export class Symbols {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
private _symbols !: Map<string, MetadataValue>;
|
||||
private _symbols!: Map<string, MetadataValue>;
|
||||
private references = new Map<string, MetadataSymbolicReferenceExpression>();
|
||||
|
||||
constructor(private sourceFile: ts.SourceFile) {}
|
||||
@ -21,12 +21,16 @@ export class Symbols {
|
||||
return (preferReference && this.references.get(name)) || this.symbols.get(name);
|
||||
}
|
||||
|
||||
define(name: string, value: MetadataValue) { this.symbols.set(name, value); }
|
||||
define(name: string, value: MetadataValue) {
|
||||
this.symbols.set(name, value);
|
||||
}
|
||||
defineReference(name: string, value: MetadataSymbolicReferenceExpression) {
|
||||
this.references.set(name, value);
|
||||
}
|
||||
|
||||
has(name: string): boolean { return this.symbols.has(name); }
|
||||
has(name: string): boolean {
|
||||
return this.symbols.has(name);
|
||||
}
|
||||
|
||||
private get symbols(): Map<string, MetadataValue> {
|
||||
let result = this._symbols;
|
||||
|
@ -6,7 +6,7 @@
|
||||
* 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, R3FactoryTarget, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr, compileComponentFromMetadata, makeBindingParser, parseTemplate} from '@angular/compiler';
|
||||
import {compileComponentFromMetadata, ConstantPool, CssSelector, DEFAULT_INTERPOLATION_CONFIG, DomElementSchemaRegistry, Expression, ExternalExpr, Identifiers, InterpolationConfig, LexerRange, makeBindingParser, ParseError, ParseSourceFile, parseTemplate, ParseTemplateOptions, R3ComponentMetadata, R3FactoryTarget, R3TargetBinder, SchemaMetadata, SelectorMatcher, Statement, TmplAstNode, WrappedNodeExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {CycleAnalyzer} from '../../cycles';
|
||||
@ -15,7 +15,7 @@ import {absoluteFrom, relative} from '../../file_system';
|
||||
import {DefaultImportRecorder, ModuleResolver, Reference, ReferenceEmitter} from '../../imports';
|
||||
import {DependencyTracker} from '../../incremental/api';
|
||||
import {IndexingContext} from '../../indexer';
|
||||
import {DirectiveMeta, InjectableClassRegistry, MetadataReader, MetadataRegistry, extractDirectiveGuards} from '../../metadata';
|
||||
import {DirectiveMeta, extractDirectiveGuards, InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata';
|
||||
import {flattenInheritedDirectiveMetadata} from '../../metadata/src/inheritance';
|
||||
import {EnumValue, PartialEvaluator} from '../../partial_evaluator';
|
||||
import {ClassDeclaration, Decorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
|
||||
@ -200,7 +200,7 @@ export class ComponentDecoratorHandler implements
|
||||
} else {
|
||||
return previous;
|
||||
}
|
||||
}, undefined) !;
|
||||
}, undefined)!;
|
||||
|
||||
|
||||
// Note that we could technically combine the `viewProvidersRequiringFactory` and
|
||||
@ -211,7 +211,7 @@ export class ComponentDecoratorHandler implements
|
||||
let wrappedViewProviders: Expression|null = null;
|
||||
|
||||
if (component.has('viewProviders')) {
|
||||
const viewProviders = component.get('viewProviders') !;
|
||||
const viewProviders = component.get('viewProviders')!;
|
||||
viewProvidersRequiringFactory =
|
||||
resolveProvidersRequiringFactory(viewProviders, this.reflector, this.evaluator);
|
||||
wrappedViewProviders = new WrappedNodeExpr(
|
||||
@ -221,7 +221,7 @@ export class ComponentDecoratorHandler implements
|
||||
|
||||
if (component.has('providers')) {
|
||||
providersRequiringFactory = resolveProvidersRequiringFactory(
|
||||
component.get('providers') !, this.reflector, this.evaluator);
|
||||
component.get('providers')!, this.reflector, this.evaluator);
|
||||
}
|
||||
|
||||
// Parse the template.
|
||||
@ -232,14 +232,14 @@ export class ComponentDecoratorHandler implements
|
||||
let template: ParsedTemplateWithSource;
|
||||
if (this.preanalyzeTemplateCache.has(node)) {
|
||||
// The template was parsed in preanalyze. Use it and delete it to save memory.
|
||||
const preanalyzed = this.preanalyzeTemplateCache.get(node) !;
|
||||
const preanalyzed = this.preanalyzeTemplateCache.get(node)!;
|
||||
this.preanalyzeTemplateCache.delete(node);
|
||||
|
||||
template = preanalyzed;
|
||||
} else {
|
||||
// The template was not already parsed. Either there's a templateUrl, or an inline template.
|
||||
if (component.has('templateUrl')) {
|
||||
const templateUrlExpr = component.get('templateUrl') !;
|
||||
const templateUrlExpr = component.get('templateUrl')!;
|
||||
const templateUrl = this.evaluator.evaluate(templateUrlExpr);
|
||||
if (typeof templateUrl !== 'string') {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -303,7 +303,7 @@ export class ComponentDecoratorHandler implements
|
||||
|
||||
let animations: Expression|null = null;
|
||||
if (component.has('animations')) {
|
||||
animations = new WrappedNodeExpr(component.get('animations') !);
|
||||
animations = new WrappedNodeExpr(component.get('animations')!);
|
||||
}
|
||||
|
||||
const output: AnalysisOutput<ComponentAnalysisData> = {
|
||||
@ -323,7 +323,8 @@ export class ComponentDecoratorHandler implements
|
||||
// analyzed and the full compilation scope for the component can be realized.
|
||||
animations,
|
||||
viewProviders: wrappedViewProviders,
|
||||
i18nUseExternalIds: this.i18nUseExternalIds, relativeContextFilePath,
|
||||
i18nUseExternalIds: this.i18nUseExternalIds,
|
||||
relativeContextFilePath,
|
||||
},
|
||||
guards: extractDirectiveGuards(node, this.reflector),
|
||||
metadataStmt: generateSetClassMetadataCall(
|
||||
@ -335,7 +336,7 @@ export class ComponentDecoratorHandler implements
|
||||
},
|
||||
};
|
||||
if (changeDetection !== null) {
|
||||
output.analysis !.meta.changeDetection = changeDetection;
|
||||
output.analysis!.meta.changeDetection = changeDetection;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
@ -353,7 +354,8 @@ export class ComponentDecoratorHandler implements
|
||||
outputs: analysis.meta.outputs,
|
||||
queries: analysis.meta.queries.map(query => query.propertyName),
|
||||
isComponent: true,
|
||||
baseClass: analysis.baseClass, ...analysis.guards,
|
||||
baseClass: analysis.baseClass,
|
||||
...analysis.guards,
|
||||
});
|
||||
|
||||
this.injectableRegistry.registerInjectable(node);
|
||||
@ -415,8 +417,8 @@ export class ComponentDecoratorHandler implements
|
||||
}
|
||||
for (const {name, ref} of scope.compilation.pipes) {
|
||||
if (!ts.isClassDeclaration(ref.node)) {
|
||||
throw new Error(
|
||||
`Unexpected non-class declaration ${ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`);
|
||||
throw new Error(`Unexpected non-class declaration ${
|
||||
ts.SyntaxKind[ref.node.kind]} for pipe ${ref.debugName}`);
|
||||
}
|
||||
pipes.set(name, ref as Reference<ClassDeclaration<ts.ClassDeclaration>>);
|
||||
}
|
||||
@ -491,7 +493,7 @@ export class ComponentDecoratorHandler implements
|
||||
|
||||
// The BoundTarget knows which directives and pipes matched the template.
|
||||
const usedDirectives = bound.getUsedDirectives();
|
||||
const usedPipes = bound.getUsedPipes().map(name => pipes.get(name) !);
|
||||
const usedPipes = bound.getUsedPipes().map(name => pipes.get(name)!);
|
||||
|
||||
// Scan through the directives/pipes actually used in the template and check whether any
|
||||
// import which needs to be generated would create a cycle.
|
||||
@ -539,7 +541,7 @@ export class ComponentDecoratorHandler implements
|
||||
if (analysis.providersRequiringFactory !== null &&
|
||||
analysis.meta.providers instanceof WrappedNodeExpr) {
|
||||
const providerDiagnostics = getProviderDiagnostics(
|
||||
analysis.providersRequiringFactory, analysis.meta.providers !.node,
|
||||
analysis.providersRequiringFactory, analysis.meta.providers!.node,
|
||||
this.injectableRegistry);
|
||||
diagnostics.push(...providerDiagnostics);
|
||||
}
|
||||
@ -547,7 +549,7 @@ export class ComponentDecoratorHandler implements
|
||||
if (analysis.viewProvidersRequiringFactory !== null &&
|
||||
analysis.meta.viewProviders instanceof WrappedNodeExpr) {
|
||||
const viewProviderDiagnostics = getProviderDiagnostics(
|
||||
analysis.viewProvidersRequiringFactory, analysis.meta.viewProviders !.node,
|
||||
analysis.viewProvidersRequiringFactory, analysis.meta.viewProviders!.node,
|
||||
this.injectableRegistry);
|
||||
diagnostics.push(...viewProviderDiagnostics);
|
||||
}
|
||||
@ -587,7 +589,7 @@ export class ComponentDecoratorHandler implements
|
||||
|
||||
private _resolveLiteral(decorator: Decorator): ts.ObjectLiteralExpression {
|
||||
if (this.literalCache.has(decorator)) {
|
||||
return this.literalCache.get(decorator) !;
|
||||
return this.literalCache.get(decorator)!;
|
||||
}
|
||||
if (decorator.args === null || decorator.args.length !== 1) {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -609,7 +611,7 @@ export class ComponentDecoratorHandler implements
|
||||
component: Map<string, ts.Expression>, field: string, enumSymbolName: string): number|null {
|
||||
let resolved: number|null = null;
|
||||
if (component.has(field)) {
|
||||
const expr = component.get(field) !;
|
||||
const expr = component.get(field)!;
|
||||
const value = this.evaluator.evaluate(expr) as any;
|
||||
if (value instanceof EnumValue && isAngularCoreReference(value.enumRef, enumSymbolName)) {
|
||||
resolved = value.resolved as number;
|
||||
@ -628,7 +630,7 @@ export class ComponentDecoratorHandler implements
|
||||
return extraUrls.length > 0 ? extraUrls : null;
|
||||
}
|
||||
|
||||
const styleUrlsExpr = component.get('styleUrls') !;
|
||||
const styleUrlsExpr = component.get('styleUrls')!;
|
||||
const styleUrls = this.evaluator.evaluate(styleUrlsExpr);
|
||||
if (!Array.isArray(styleUrls) || !styleUrls.every(url => typeof url === 'string')) {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -643,7 +645,7 @@ export class ComponentDecoratorHandler implements
|
||||
containingFile: string): Promise<ParsedTemplate|null> {
|
||||
if (component.has('templateUrl')) {
|
||||
// Extract the templateUrl and preload it.
|
||||
const templateUrlExpr = component.get('templateUrl') !;
|
||||
const templateUrlExpr = component.get('templateUrl')!;
|
||||
const templateUrl = this.evaluator.evaluate(templateUrlExpr);
|
||||
if (typeof templateUrl !== 'string') {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -703,7 +705,7 @@ export class ComponentDecoratorHandler implements
|
||||
ErrorCode.COMPONENT_MISSING_TEMPLATE, Decorator.nodeForError(decorator),
|
||||
'component is missing a template');
|
||||
}
|
||||
const templateExpr = component.get('template') !;
|
||||
const templateExpr = component.get('template')!;
|
||||
|
||||
let templateStr: string;
|
||||
let templateUrl: string = '';
|
||||
@ -721,7 +723,7 @@ export class ComponentDecoratorHandler implements
|
||||
escapedString = true;
|
||||
sourceMapping = {
|
||||
type: 'direct',
|
||||
node: templateExpr as(ts.StringLiteral | ts.NoSubstitutionTemplateLiteral),
|
||||
node: templateExpr as (ts.StringLiteral | ts.NoSubstitutionTemplateLiteral),
|
||||
};
|
||||
} else {
|
||||
const resolvedTemplate = this.evaluator.evaluate(templateExpr);
|
||||
@ -749,7 +751,7 @@ export class ComponentDecoratorHandler implements
|
||||
templateRange: LexerRange|undefined, escapedString: boolean): ParsedTemplate {
|
||||
let preserveWhitespaces: boolean = this.defaultPreserveWhitespaces;
|
||||
if (component.has('preserveWhitespaces')) {
|
||||
const expr = component.get('preserveWhitespaces') !;
|
||||
const expr = component.get('preserveWhitespaces')!;
|
||||
const value = this.evaluator.evaluate(expr);
|
||||
if (typeof value !== 'boolean') {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -760,7 +762,7 @@ export class ComponentDecoratorHandler implements
|
||||
|
||||
let interpolation: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG;
|
||||
if (component.has('interpolation')) {
|
||||
const expr = component.get('interpolation') !;
|
||||
const expr = component.get('interpolation')!;
|
||||
const value = this.evaluator.evaluate(expr);
|
||||
if (!Array.isArray(value) || value.length !== 2 ||
|
||||
!value.every(element => typeof element === 'string')) {
|
||||
@ -768,14 +770,15 @@ export class ComponentDecoratorHandler implements
|
||||
ErrorCode.VALUE_HAS_WRONG_TYPE, expr,
|
||||
'interpolation must be an array with 2 elements of string type');
|
||||
}
|
||||
interpolation = InterpolationConfig.fromArray(value as[string, string]);
|
||||
interpolation = InterpolationConfig.fromArray(value as [string, string]);
|
||||
}
|
||||
|
||||
const {errors, nodes: emitNodes, styleUrls, styles, ngContentSelectors} =
|
||||
parseTemplate(templateStr, templateUrl, {
|
||||
preserveWhitespaces,
|
||||
interpolationConfig: interpolation,
|
||||
range: templateRange, escapedString,
|
||||
range: templateRange,
|
||||
escapedString,
|
||||
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
|
||||
});
|
||||
|
||||
@ -795,7 +798,8 @@ export class ComponentDecoratorHandler implements
|
||||
const {nodes: diagNodes} = parseTemplate(templateStr, templateUrl, {
|
||||
preserveWhitespaces: true,
|
||||
interpolationConfig: interpolation,
|
||||
range: templateRange, escapedString,
|
||||
range: templateRange,
|
||||
escapedString,
|
||||
enableI18nLegacyMessageIdFormat: this.enableI18nLegacyMessageIdFormat,
|
||||
leadingTriviaChars: [],
|
||||
});
|
||||
@ -808,7 +812,8 @@ export class ComponentDecoratorHandler implements
|
||||
styles,
|
||||
ngContentSelectors,
|
||||
errors,
|
||||
template: templateStr, templateUrl,
|
||||
template: templateStr,
|
||||
templateUrl,
|
||||
isInline: component.has('template'),
|
||||
file: new ParseSourceFile(templateStr, templateUrl),
|
||||
};
|
||||
@ -820,7 +825,7 @@ export class ComponentDecoratorHandler implements
|
||||
}
|
||||
|
||||
// Figure out what file is being imported.
|
||||
return this.moduleResolver.resolveModule(expr.value.moduleName !, origin.fileName);
|
||||
return this.moduleResolver.resolveModule(expr.value.moduleName!, origin.fileName);
|
||||
}
|
||||
|
||||
private _isCyclicImport(expr: Expression, origin: ts.SourceFile): boolean {
|
||||
|
@ -36,9 +36,13 @@ export function getProviderDiagnostics(
|
||||
const contextNode = provider.getOriginForDiagnostics(providersDeclaration);
|
||||
diagnostics.push(makeDiagnostic(
|
||||
ErrorCode.UNDECORATED_PROVIDER, contextNode,
|
||||
`The class '${provider.node.name.text}' cannot be created via dependency injection, as it does not have an Angular decorator. This will result in an error at runtime.
|
||||
`The class '${
|
||||
provider.node.name
|
||||
.text}' cannot be created via dependency injection, as it does not have an Angular decorator. This will result in an error at runtime.
|
||||
|
||||
Either add the @Injectable() decorator to '${provider.node.name.text}', or configure a different provider (such as a provider with 'useFactory').
|
||||
Either add the @Injectable() decorator to '${
|
||||
provider.node.name
|
||||
.text}', or configure a different provider (such as a provider with 'useFactory').
|
||||
`,
|
||||
[{node: provider.node, messageText: `'${provider.node.name.text}' is declared here.`}]));
|
||||
}
|
||||
@ -52,7 +56,7 @@ export function getDirectiveDiagnostics(
|
||||
kind: string): ts.Diagnostic[]|null {
|
||||
let diagnostics: ts.Diagnostic[]|null = [];
|
||||
|
||||
const addDiagnostics = (more: ts.Diagnostic | ts.Diagnostic[] | null) => {
|
||||
const addDiagnostics = (more: ts.Diagnostic|ts.Diagnostic[]|null) => {
|
||||
if (more === null) {
|
||||
return;
|
||||
} else if (diagnostics === null) {
|
||||
@ -121,14 +125,16 @@ export function checkInheritanceOfDirective(
|
||||
|
||||
function getInheritedUndecoratedCtorDiagnostic(
|
||||
node: ClassDeclaration, baseClass: Reference, reader: MetadataReader) {
|
||||
const subclassMeta = reader.getDirectiveMetadata(new Reference(node)) !;
|
||||
const subclassMeta = reader.getDirectiveMetadata(new Reference(node))!;
|
||||
const dirOrComp = subclassMeta.isComponent ? 'Component' : 'Directive';
|
||||
const baseClassName = baseClass.debugName;
|
||||
|
||||
return makeDiagnostic(
|
||||
ErrorCode.DIRECTIVE_INHERITS_UNDECORATED_CTOR, node.name,
|
||||
`The ${dirOrComp.toLowerCase()} ${node.name.text} inherits its constructor from ${baseClassName}, ` +
|
||||
`The ${dirOrComp.toLowerCase()} ${node.name.text} inherits its constructor from ${
|
||||
baseClassName}, ` +
|
||||
`but the latter does not have an Angular decorator of its own. Dependency injection will not be able to ` +
|
||||
`resolve the parameters of ${baseClassName}'s constructor. Either add a @Directive decorator ` +
|
||||
`resolve the parameters of ${
|
||||
baseClassName}'s constructor. Either add a @Directive decorator ` +
|
||||
`to ${baseClassName}, or add an explicit constructor to ${node.name.text}.`);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ConstantPool, Expression, Identifiers, ParseError, ParsedHostBindings, R3DependencyMetadata, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, Statement, WrappedNodeExpr, compileDirectiveFromMetadata, makeBindingParser, parseHostBindings, verifyHostBindings} from '@angular/compiler';
|
||||
import {compileDirectiveFromMetadata, ConstantPool, Expression, Identifiers, makeBindingParser, ParsedHostBindings, ParseError, parseHostBindings, R3DependencyMetadata, R3DirectiveMetadata, R3FactoryTarget, R3QueryMetadata, Statement, verifyHostBindings, WrappedNodeExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
||||
@ -14,7 +14,7 @@ import {DefaultImportRecorder, Reference} from '../../imports';
|
||||
import {InjectableClassRegistry, MetadataReader, MetadataRegistry} from '../../metadata';
|
||||
import {extractDirectiveGuards} from '../../metadata/src/util';
|
||||
import {DynamicValue, EnumValue, PartialEvaluator} from '../../partial_evaluator';
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, Decorator, ReflectionHost, filterToMembersWithDecorator, reflectObjectLiteral} from '../../reflection';
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, Decorator, filterToMembersWithDecorator, ReflectionHost, reflectObjectLiteral} from '../../reflection';
|
||||
import {LocalModuleScopeRegistry} from '../../scope';
|
||||
import {AnalysisOutput, CompileResult, DecoratorHandler, DetectResult, HandlerFlags, HandlerPrecedence, ResolveResult} from '../../transform';
|
||||
|
||||
@ -92,7 +92,7 @@ export class DirectiveDecoratorHandler implements
|
||||
let providersRequiringFactory: Set<Reference<ClassDeclaration>>|null = null;
|
||||
if (directiveResult !== undefined && directiveResult.decorator.has('providers')) {
|
||||
providersRequiringFactory = resolveProvidersRequiringFactory(
|
||||
directiveResult.decorator.get('providers') !, this.reflector, this.evaluator);
|
||||
directiveResult.decorator.get('providers')!, this.reflector, this.evaluator);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -102,7 +102,8 @@ export class DirectiveDecoratorHandler implements
|
||||
node, this.reflector, this.defaultImportRecorder, this.isCore,
|
||||
this.annotateForClosureCompiler),
|
||||
baseClass: readBaseClass(node, this.reflector, this.evaluator),
|
||||
guards: extractDirectiveGuards(node, this.reflector), providersRequiringFactory
|
||||
guards: extractDirectiveGuards(node, this.reflector),
|
||||
providersRequiringFactory
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -120,7 +121,8 @@ export class DirectiveDecoratorHandler implements
|
||||
outputs: analysis.meta.outputs,
|
||||
queries: analysis.meta.queries.map(query => query.propertyName),
|
||||
isComponent: false,
|
||||
baseClass: analysis.baseClass, ...analysis.guards,
|
||||
baseClass: analysis.baseClass,
|
||||
...analysis.guards,
|
||||
});
|
||||
|
||||
this.injectableRegistry.registerInjectable(node);
|
||||
@ -132,7 +134,7 @@ export class DirectiveDecoratorHandler implements
|
||||
if (analysis.providersRequiringFactory !== null &&
|
||||
analysis.meta.providers instanceof WrappedNodeExpr) {
|
||||
const providerDiagnostics = getProviderDiagnostics(
|
||||
analysis.providersRequiringFactory, analysis.meta.providers !.node,
|
||||
analysis.providersRequiringFactory, analysis.meta.providers!.node,
|
||||
this.injectableRegistry);
|
||||
diagnostics.push(...providerDiagnostics);
|
||||
}
|
||||
@ -176,9 +178,8 @@ export class DirectiveDecoratorHandler implements
|
||||
export function extractDirectiveMetadata(
|
||||
clazz: ClassDeclaration, decorator: Readonly<Decorator|null>, reflector: ReflectionHost,
|
||||
evaluator: PartialEvaluator, defaultImportRecorder: DefaultImportRecorder, isCore: boolean,
|
||||
flags: HandlerFlags, annotateForClosureCompiler: boolean,
|
||||
defaultSelector: string | null =
|
||||
null): {decorator: Map<string, ts.Expression>, metadata: R3DirectiveMetadata}|undefined {
|
||||
flags: HandlerFlags, annotateForClosureCompiler: boolean, defaultSelector: string|null = null):
|
||||
{decorator: Map<string, ts.Expression>, metadata: R3DirectiveMetadata}|undefined {
|
||||
let directive: Map<string, ts.Expression>;
|
||||
if (decorator === null || decorator.args === null || decorator.args.length === 0) {
|
||||
directive = new Map<string, ts.Expression>();
|
||||
@ -220,9 +221,10 @@ export function extractDirectiveMetadata(
|
||||
|
||||
// And outputs.
|
||||
const outputsFromMeta = parseFieldToPropertyMapping(directive, 'outputs', evaluator);
|
||||
const outputsFromFields = parseDecoratedFields(
|
||||
filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), evaluator,
|
||||
resolveOutput) as{[field: string]: string};
|
||||
const outputsFromFields =
|
||||
parseDecoratedFields(
|
||||
filterToMembersWithDecorator(decoratedElements, 'Output', coreModule), evaluator,
|
||||
resolveOutput) as {[field: string]: string};
|
||||
// Construct the list of queries.
|
||||
const contentChildFromFields = queriesFromFields(
|
||||
filterToMembersWithDecorator(decoratedElements, 'ContentChild', coreModule), reflector,
|
||||
@ -244,7 +246,7 @@ export function extractDirectiveMetadata(
|
||||
|
||||
if (directive.has('queries')) {
|
||||
const queriesFromDecorator =
|
||||
extractQueriesFromDecorator(directive.get('queries') !, reflector, evaluator, isCore);
|
||||
extractQueriesFromDecorator(directive.get('queries')!, reflector, evaluator, isCore);
|
||||
queries.push(...queriesFromDecorator.content);
|
||||
viewQueries.push(...queriesFromDecorator.view);
|
||||
}
|
||||
@ -252,7 +254,7 @@ export function extractDirectiveMetadata(
|
||||
// Parse the selector.
|
||||
let selector = defaultSelector;
|
||||
if (directive.has('selector')) {
|
||||
const expr = directive.get('selector') !;
|
||||
const expr = directive.get('selector')!;
|
||||
const resolved = evaluator.evaluate(expr);
|
||||
if (typeof resolved !== 'string') {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -272,8 +274,8 @@ export function extractDirectiveMetadata(
|
||||
const providers: Expression|null = directive.has('providers') ?
|
||||
new WrappedNodeExpr(
|
||||
annotateForClosureCompiler ?
|
||||
wrapFunctionExpressionsInParens(directive.get('providers') !) :
|
||||
directive.get('providers') !) :
|
||||
wrapFunctionExpressionsInParens(directive.get('providers')!) :
|
||||
directive.get('providers')!) :
|
||||
null;
|
||||
|
||||
// Determine if `ngOnChanges` is a lifecycle hook defined on the component.
|
||||
@ -284,7 +286,7 @@ export function extractDirectiveMetadata(
|
||||
// Parse exportAs.
|
||||
let exportAs: string[]|null = null;
|
||||
if (directive.has('exportAs')) {
|
||||
const expr = directive.get('exportAs') !;
|
||||
const expr = directive.get('exportAs')!;
|
||||
const resolved = evaluator.evaluate(expr);
|
||||
if (typeof resolved !== 'string') {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -312,15 +314,24 @@ export function extractDirectiveMetadata(
|
||||
|
||||
const metadata: R3DirectiveMetadata = {
|
||||
name: clazz.name.text,
|
||||
deps: ctorDeps, host,
|
||||
deps: ctorDeps,
|
||||
host,
|
||||
lifecycle: {
|
||||
usesOnChanges,
|
||||
usesOnChanges,
|
||||
},
|
||||
inputs: {...inputsFromMeta, ...inputsFromFields},
|
||||
outputs: {...outputsFromMeta, ...outputsFromFields}, queries, viewQueries, selector,
|
||||
fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE), type, internalType,
|
||||
outputs: {...outputsFromMeta, ...outputsFromFields},
|
||||
queries,
|
||||
viewQueries,
|
||||
selector,
|
||||
fullInheritance: !!(flags & HandlerFlags.FULL_INHERITANCE),
|
||||
type,
|
||||
internalType,
|
||||
typeArgumentCount: reflector.getGenericArityOfClass(clazz) || 0,
|
||||
typeSourceSpan: createSourceSpan(clazz.name), usesInheritance, exportAs, providers
|
||||
typeSourceSpan: createSourceSpan(clazz.name),
|
||||
usesInheritance,
|
||||
exportAs,
|
||||
providers
|
||||
};
|
||||
return {decorator: directive, metadata};
|
||||
}
|
||||
@ -366,11 +377,11 @@ export function extractQueryMetadata(
|
||||
}
|
||||
const options = reflectObjectLiteral(optionsExpr);
|
||||
if (options.has('read')) {
|
||||
read = new WrappedNodeExpr(options.get('read') !);
|
||||
read = new WrappedNodeExpr(options.get('read')!);
|
||||
}
|
||||
|
||||
if (options.has('descendants')) {
|
||||
const descendantsExpr = options.get('descendants') !;
|
||||
const descendantsExpr = options.get('descendants')!;
|
||||
const descendantsValue = evaluator.evaluate(descendantsExpr);
|
||||
if (typeof descendantsValue !== 'boolean') {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -381,7 +392,7 @@ export function extractQueryMetadata(
|
||||
}
|
||||
|
||||
if (options.has('static')) {
|
||||
const staticValue = evaluator.evaluate(options.get('static') !);
|
||||
const staticValue = evaluator.evaluate(options.get('static')!);
|
||||
if (typeof staticValue !== 'boolean') {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.VALUE_HAS_WRONG_TYPE, node, `@${name} options.static must be a boolean`);
|
||||
@ -466,7 +477,7 @@ export function parseFieldArrayValue(
|
||||
}
|
||||
|
||||
// Resolve the field of interest from the directive metadata to a string[].
|
||||
const expression = directive.get(field) !;
|
||||
const expression = directive.get(field)!;
|
||||
const value = evaluator.evaluate(expression);
|
||||
if (!isStringArrayOrDie(value, field, expression)) {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -489,15 +500,13 @@ function parseFieldToPropertyMapping(
|
||||
return EMPTY_OBJECT;
|
||||
}
|
||||
|
||||
return metaValues.reduce(
|
||||
(results, value) => {
|
||||
// Either the value is 'field' or 'field: property'. In the first case, `property` will
|
||||
// be undefined, in which case the field name should also be used as the property name.
|
||||
const [field, property] = value.split(':', 2).map(str => str.trim());
|
||||
results[field] = property || field;
|
||||
return results;
|
||||
},
|
||||
{} as{[field: string]: string});
|
||||
return metaValues.reduce((results, value) => {
|
||||
// Either the value is 'field' or 'field: property'. In the first case, `property` will
|
||||
// be undefined, in which case the field name should also be used as the property name.
|
||||
const [field, property] = value.split(':', 2).map(str => str.trim());
|
||||
results[field] = property || field;
|
||||
return results;
|
||||
}, {} as {[field: string]: string});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -507,33 +516,32 @@ function parseFieldToPropertyMapping(
|
||||
function parseDecoratedFields(
|
||||
fields: {member: ClassMember, decorators: Decorator[]}[], evaluator: PartialEvaluator,
|
||||
mapValueResolver: (publicName: string, internalName: string) =>
|
||||
string | [string, string]): {[field: string]: string | [string, string]} {
|
||||
return fields.reduce(
|
||||
(results, field) => {
|
||||
const fieldName = field.member.name;
|
||||
field.decorators.forEach(decorator => {
|
||||
// The decorator either doesn't have an argument (@Input()) in which case the property
|
||||
// name is used, or it has one argument (@Output('named')).
|
||||
if (decorator.args == null || decorator.args.length === 0) {
|
||||
results[fieldName] = fieldName;
|
||||
} else if (decorator.args.length === 1) {
|
||||
const property = evaluator.evaluate(decorator.args[0]);
|
||||
if (typeof property !== 'string') {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator),
|
||||
`@${decorator.name} decorator argument must resolve to a string`);
|
||||
}
|
||||
results[fieldName] = mapValueResolver(property, fieldName);
|
||||
} else {
|
||||
// Too many arguments.
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator),
|
||||
`@${decorator.name} can have at most one argument, got ${decorator.args.length} argument(s)`);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
},
|
||||
{} as{[field: string]: string | [string, string]});
|
||||
string | [string, string]): {[field: string]: string|[string, string]} {
|
||||
return fields.reduce((results, field) => {
|
||||
const fieldName = field.member.name;
|
||||
field.decorators.forEach(decorator => {
|
||||
// The decorator either doesn't have an argument (@Input()) in which case the property
|
||||
// name is used, or it has one argument (@Output('named')).
|
||||
if (decorator.args == null || decorator.args.length === 0) {
|
||||
results[fieldName] = fieldName;
|
||||
} else if (decorator.args.length === 1) {
|
||||
const property = evaluator.evaluate(decorator.args[0]);
|
||||
if (typeof property !== 'string') {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator),
|
||||
`@${decorator.name} decorator argument must resolve to a string`);
|
||||
}
|
||||
results[fieldName] = mapValueResolver(property, fieldName);
|
||||
} else {
|
||||
// Too many arguments.
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator),
|
||||
`@${decorator.name} can have at most one argument, got ${
|
||||
decorator.args.length} argument(s)`);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
}, {} as {[field: string]: string | [string, string]});
|
||||
}
|
||||
|
||||
function resolveInput(publicName: string, internalName: string): [string, string] {
|
||||
@ -552,7 +560,7 @@ export function queriesFromFields(
|
||||
const node = member.node || Decorator.nodeForError(decorator);
|
||||
|
||||
// Throw in case of `@Input() @ContentChild('foo') foo: any`, which is not supported in Ivy
|
||||
if (member.decorators !.some(v => v.name === 'Input')) {
|
||||
if (member.decorators!.some(v => v.name === 'Input')) {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.DECORATOR_COLLISION, node,
|
||||
'Cannot combine @Input decorators with query decorators');
|
||||
@ -626,38 +634,40 @@ function evaluateHostExpressionBindings(
|
||||
}
|
||||
|
||||
export function extractHostBindings(
|
||||
members: ClassMember[], evaluator: PartialEvaluator, coreModule: string | undefined,
|
||||
members: ClassMember[], evaluator: PartialEvaluator, coreModule: string|undefined,
|
||||
metadata?: Map<string, ts.Expression>): ParsedHostBindings {
|
||||
let bindings: ParsedHostBindings;
|
||||
if (metadata && metadata.has('host')) {
|
||||
bindings = evaluateHostExpressionBindings(metadata.get('host') !, evaluator);
|
||||
bindings = evaluateHostExpressionBindings(metadata.get('host')!, evaluator);
|
||||
} else {
|
||||
bindings = parseHostBindings({});
|
||||
}
|
||||
|
||||
filterToMembersWithDecorator(members, 'HostBinding', coreModule).forEach(({member, decorators}) => {
|
||||
decorators.forEach(decorator => {
|
||||
let hostPropertyName: string = member.name;
|
||||
if (decorator.args !== null && decorator.args.length > 0) {
|
||||
if (decorator.args.length !== 1) {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator),
|
||||
`@HostBinding can have at most one argument, got ${decorator.args.length} argument(s)`);
|
||||
}
|
||||
filterToMembersWithDecorator(members, 'HostBinding', coreModule)
|
||||
.forEach(({member, decorators}) => {
|
||||
decorators.forEach(decorator => {
|
||||
let hostPropertyName: string = member.name;
|
||||
if (decorator.args !== null && decorator.args.length > 0) {
|
||||
if (decorator.args.length !== 1) {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.DECORATOR_ARITY_WRONG, Decorator.nodeForError(decorator),
|
||||
`@HostBinding can have at most one argument, got ${
|
||||
decorator.args.length} argument(s)`);
|
||||
}
|
||||
|
||||
const resolved = evaluator.evaluate(decorator.args[0]);
|
||||
if (typeof resolved !== 'string') {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator),
|
||||
`@HostBinding's argument must be a string`);
|
||||
}
|
||||
const resolved = evaluator.evaluate(decorator.args[0]);
|
||||
if (typeof resolved !== 'string') {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.VALUE_HAS_WRONG_TYPE, Decorator.nodeForError(decorator),
|
||||
`@HostBinding's argument must be a string`);
|
||||
}
|
||||
|
||||
hostPropertyName = resolved;
|
||||
}
|
||||
hostPropertyName = resolved;
|
||||
}
|
||||
|
||||
bindings.properties[hostPropertyName] = member.name;
|
||||
});
|
||||
});
|
||||
bindings.properties[hostPropertyName] = member.name;
|
||||
});
|
||||
});
|
||||
|
||||
filterToMembersWithDecorator(members, 'HostListener', coreModule)
|
||||
.forEach(({member, decorators}) => {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {R3FactoryMetadata, compileFactoryFunction} from '@angular/compiler';
|
||||
import {compileFactoryFunction, R3FactoryMetadata} from '@angular/compiler';
|
||||
|
||||
import {CompileResult} from '../../transform';
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3FactoryTarget, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr, compileInjectable as compileIvyInjectable} from '@angular/compiler';
|
||||
import {compileInjectable as compileIvyInjectable, Expression, Identifiers, LiteralExpr, R3DependencyMetadata, R3FactoryTarget, R3InjectableMetadata, R3ResolvedDependencyType, Statement, WrappedNodeExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
||||
@ -83,7 +83,9 @@ export class InjectableDecoratorHandler implements
|
||||
};
|
||||
}
|
||||
|
||||
register(node: ClassDeclaration): void { this.injectableRegistry.registerInjectable(node); }
|
||||
register(node: ClassDeclaration): void {
|
||||
this.injectableRegistry.registerInjectable(node);
|
||||
}
|
||||
|
||||
compile(node: ClassDeclaration, analysis: Readonly<InjectableHandlerData>): CompileResult[] {
|
||||
const res = compileIvyInjectable(analysis.meta);
|
||||
@ -165,12 +167,12 @@ function extractInjectableMetadata(
|
||||
const meta = reflectObjectLiteral(metaNode);
|
||||
let providedIn: Expression = new LiteralExpr(null);
|
||||
if (meta.has('providedIn')) {
|
||||
providedIn = new WrappedNodeExpr(meta.get('providedIn') !);
|
||||
providedIn = new WrappedNodeExpr(meta.get('providedIn')!);
|
||||
}
|
||||
|
||||
let userDeps: R3DependencyMetadata[]|undefined = undefined;
|
||||
if ((meta.has('useClass') || meta.has('useFactory')) && meta.has('deps')) {
|
||||
const depsExpr = meta.get('deps') !;
|
||||
const depsExpr = meta.get('deps')!;
|
||||
if (!ts.isArrayLiteralExpression(depsExpr)) {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.VALUE_NOT_LITERAL, depsExpr,
|
||||
@ -186,7 +188,7 @@ function extractInjectableMetadata(
|
||||
typeArgumentCount,
|
||||
internalType,
|
||||
providedIn,
|
||||
useValue: new WrappedNodeExpr(unwrapForwardRef(meta.get('useValue') !, reflector)),
|
||||
useValue: new WrappedNodeExpr(unwrapForwardRef(meta.get('useValue')!, reflector)),
|
||||
};
|
||||
} else if (meta.has('useExisting')) {
|
||||
return {
|
||||
@ -195,7 +197,7 @@ function extractInjectableMetadata(
|
||||
typeArgumentCount,
|
||||
internalType,
|
||||
providedIn,
|
||||
useExisting: new WrappedNodeExpr(unwrapForwardRef(meta.get('useExisting') !, reflector)),
|
||||
useExisting: new WrappedNodeExpr(unwrapForwardRef(meta.get('useExisting')!, reflector)),
|
||||
};
|
||||
} else if (meta.has('useClass')) {
|
||||
return {
|
||||
@ -204,19 +206,20 @@ function extractInjectableMetadata(
|
||||
typeArgumentCount,
|
||||
internalType,
|
||||
providedIn,
|
||||
useClass: new WrappedNodeExpr(unwrapForwardRef(meta.get('useClass') !, reflector)),
|
||||
useClass: new WrappedNodeExpr(unwrapForwardRef(meta.get('useClass')!, reflector)),
|
||||
userDeps,
|
||||
};
|
||||
} else if (meta.has('useFactory')) {
|
||||
// useFactory is special - the 'deps' property must be analyzed.
|
||||
const factory = new WrappedNodeExpr(meta.get('useFactory') !);
|
||||
const factory = new WrappedNodeExpr(meta.get('useFactory')!);
|
||||
return {
|
||||
name,
|
||||
type,
|
||||
typeArgumentCount,
|
||||
internalType,
|
||||
providedIn,
|
||||
useFactory: factory, userDeps,
|
||||
useFactory: factory,
|
||||
userDeps,
|
||||
};
|
||||
} else {
|
||||
return {name, type, typeArgumentCount, internalType, providedIn};
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Expression, ExternalExpr, FunctionExpr, Identifiers, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NONE_TYPE, ReturnStatement, Statement, WrappedNodeExpr, literalMap} from '@angular/compiler';
|
||||
import {Expression, ExternalExpr, FunctionExpr, Identifiers, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, literalMap, NONE_TYPE, ReturnStatement, Statement, WrappedNodeExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {DefaultImportRecorder} from '../../imports';
|
||||
@ -71,7 +71,7 @@ export function generateSetClassMetadataCall(
|
||||
duplicateDecoratedMemberNames.join(', '));
|
||||
}
|
||||
const decoratedMembers =
|
||||
classMembers.map(member => classMemberToMetadata(member.name, member.decorators !, isCore));
|
||||
classMembers.map(member => classMemberToMetadata(member.name, member.decorators!, isCore));
|
||||
if (decoratedMembers.length > 0) {
|
||||
metaPropDecorators = ts.createObjectLiteral(decoratedMembers);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CUSTOM_ELEMENTS_SCHEMA, Expression, ExternalExpr, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NO_ERRORS_SCHEMA, R3Identifiers, R3InjectorMetadata, R3NgModuleMetadata, R3Reference, STRING_TYPE, SchemaMetadata, Statement, WrappedNodeExpr, compileInjector, compileNgModule} from '@angular/compiler';
|
||||
import {compileInjector, compileNgModule, CUSTOM_ELEMENTS_SCHEMA, Expression, ExternalExpr, InvokeFunctionExpr, LiteralArrayExpr, LiteralExpr, NO_ERRORS_SCHEMA, R3Identifiers, R3InjectorMetadata, R3NgModuleMetadata, R3Reference, SchemaMetadata, Statement, STRING_TYPE, WrappedNodeExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ErrorCode, FatalDiagnosticError, makeDiagnostic} from '../../diagnostics';
|
||||
@ -40,7 +40,9 @@ export interface NgModuleAnalysis {
|
||||
providers: ts.Expression|null;
|
||||
}
|
||||
|
||||
export interface NgModuleResolution { injectorImports: Expression[]; }
|
||||
export interface NgModuleResolution {
|
||||
injectorImports: Expression[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles @NgModule annotations to ngModuleDef fields.
|
||||
@ -116,7 +118,7 @@ export class NgModuleDecoratorHandler implements
|
||||
let declarationRefs: Reference<ClassDeclaration>[] = [];
|
||||
let rawDeclarations: ts.Expression|null = null;
|
||||
if (ngModule.has('declarations')) {
|
||||
rawDeclarations = ngModule.get('declarations') !;
|
||||
rawDeclarations = ngModule.get('declarations')!;
|
||||
const declarationMeta = this.evaluator.evaluate(rawDeclarations, forwardRefResolver);
|
||||
declarationRefs =
|
||||
this.resolveTypeList(rawDeclarations, declarationMeta, name, 'declarations');
|
||||
@ -128,7 +130,9 @@ export class NgModuleDecoratorHandler implements
|
||||
|
||||
diagnostics.push(makeDiagnostic(
|
||||
ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode,
|
||||
`Cannot declare '${ref.node.name.text}' in an NgModule as it's not a part of the current compilation.`,
|
||||
`Cannot declare '${
|
||||
ref.node.name
|
||||
.text}' in an NgModule as it's not a part of the current compilation.`,
|
||||
[{
|
||||
node: ref.node.name,
|
||||
messageText: `'${ref.node.name.text}' is declared here.`,
|
||||
@ -144,28 +148,28 @@ export class NgModuleDecoratorHandler implements
|
||||
let importRefs: Reference<ClassDeclaration>[] = [];
|
||||
let rawImports: ts.Expression|null = null;
|
||||
if (ngModule.has('imports')) {
|
||||
rawImports = ngModule.get('imports') !;
|
||||
rawImports = ngModule.get('imports')!;
|
||||
const importsMeta = this.evaluator.evaluate(rawImports, moduleResolvers);
|
||||
importRefs = this.resolveTypeList(rawImports, importsMeta, name, 'imports');
|
||||
}
|
||||
let exportRefs: Reference<ClassDeclaration>[] = [];
|
||||
let rawExports: ts.Expression|null = null;
|
||||
if (ngModule.has('exports')) {
|
||||
rawExports = ngModule.get('exports') !;
|
||||
rawExports = ngModule.get('exports')!;
|
||||
const exportsMeta = this.evaluator.evaluate(rawExports, moduleResolvers);
|
||||
exportRefs = this.resolveTypeList(rawExports, exportsMeta, name, 'exports');
|
||||
this.referencesRegistry.add(node, ...exportRefs);
|
||||
}
|
||||
let bootstrapRefs: Reference<ClassDeclaration>[] = [];
|
||||
if (ngModule.has('bootstrap')) {
|
||||
const expr = ngModule.get('bootstrap') !;
|
||||
const expr = ngModule.get('bootstrap')!;
|
||||
const bootstrapMeta = this.evaluator.evaluate(expr, forwardRefResolver);
|
||||
bootstrapRefs = this.resolveTypeList(expr, bootstrapMeta, name, 'bootstrap');
|
||||
}
|
||||
|
||||
const schemas: SchemaMetadata[] = [];
|
||||
if (ngModule.has('schemas')) {
|
||||
const rawExpr = ngModule.get('schemas') !;
|
||||
const rawExpr = ngModule.get('schemas')!;
|
||||
const result = this.evaluator.evaluate(rawExpr);
|
||||
if (!Array.isArray(result)) {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -203,7 +207,7 @@ export class NgModuleDecoratorHandler implements
|
||||
}
|
||||
|
||||
const id: Expression|null =
|
||||
ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id') !) : null;
|
||||
ngModule.has('id') ? new WrappedNodeExpr(ngModule.get('id')!) : null;
|
||||
const valueContext = node.getSourceFile();
|
||||
|
||||
let typeContext = valueContext;
|
||||
@ -220,7 +224,7 @@ export class NgModuleDecoratorHandler implements
|
||||
const exports = exportRefs.map(exp => this._toR3Reference(exp, valueContext, typeContext));
|
||||
|
||||
const isForwardReference = (ref: R3Reference) =>
|
||||
isExpressionForwardReference(ref.value, node.name !, valueContext);
|
||||
isExpressionForwardReference(ref.value, node.name!, valueContext);
|
||||
const containsForwardDecls = bootstrap.some(isForwardReference) ||
|
||||
declarations.some(isForwardReference) || imports.some(isForwardReference) ||
|
||||
exports.some(isForwardReference);
|
||||
@ -244,7 +248,7 @@ export class NgModuleDecoratorHandler implements
|
||||
schemas: [],
|
||||
};
|
||||
|
||||
const rawProviders = ngModule.has('providers') ? ngModule.get('providers') ! : null;
|
||||
const rawProviders = ngModule.has('providers') ? ngModule.get('providers')! : null;
|
||||
const wrapperProviders = rawProviders !== null ?
|
||||
new WrappedNodeExpr(
|
||||
this.annotateForClosureCompiler ? wrapFunctionExpressionsInParens(rawProviders) :
|
||||
@ -256,7 +260,7 @@ export class NgModuleDecoratorHandler implements
|
||||
// and pipes from the module exports.
|
||||
const injectorImports: WrappedNodeExpr<ts.Expression>[] = [];
|
||||
if (ngModule.has('imports')) {
|
||||
injectorImports.push(new WrappedNodeExpr(ngModule.get('imports') !));
|
||||
injectorImports.push(new WrappedNodeExpr(ngModule.get('imports')!));
|
||||
}
|
||||
|
||||
if (this.routeAnalyzer !== null) {
|
||||
@ -279,7 +283,8 @@ export class NgModuleDecoratorHandler implements
|
||||
schemas: schemas,
|
||||
mod: ngModuleDef,
|
||||
inj: ngInjectorDef,
|
||||
declarations: declarationRefs, rawDeclarations,
|
||||
declarations: declarationRefs,
|
||||
rawDeclarations,
|
||||
imports: importRefs,
|
||||
exports: exportRefs,
|
||||
providers: rawProviders,
|
||||
@ -326,7 +331,7 @@ export class NgModuleDecoratorHandler implements
|
||||
|
||||
if (analysis.providersRequiringFactory !== null) {
|
||||
const providerDiagnostics = getProviderDiagnostics(
|
||||
analysis.providersRequiringFactory, analysis.providers !, this.injectableRegistry);
|
||||
analysis.providersRequiringFactory, analysis.providers!, this.injectableRegistry);
|
||||
diagnostics.push(...providerDiagnostics);
|
||||
}
|
||||
|
||||
@ -396,7 +401,7 @@ export class NgModuleDecoratorHandler implements
|
||||
const pipes = scope.compilation.pipes.map(pipe => this.refEmitter.emit(pipe.ref, context));
|
||||
const directiveArray = new LiteralArrayExpr(directives);
|
||||
const pipesArray = new LiteralArrayExpr(pipes);
|
||||
const declExpr = this.refEmitter.emit(decl, context) !;
|
||||
const declExpr = this.refEmitter.emit(decl, context)!;
|
||||
const setComponentScope = new ExternalExpr(R3Identifiers.setComponentScope);
|
||||
const callExpr =
|
||||
new InvokeFunctionExpr(setComponentScope, [declExpr, directiveArray, pipesArray]);
|
||||
@ -472,8 +477,9 @@ export class NgModuleDecoratorHandler implements
|
||||
return null;
|
||||
}
|
||||
|
||||
const typeName = type && (ts.isIdentifier(type.typeName) && type.typeName ||
|
||||
ts.isQualifiedName(type.typeName) && type.typeName.right) ||
|
||||
const typeName = type &&
|
||||
(ts.isIdentifier(type.typeName) && type.typeName ||
|
||||
ts.isQualifiedName(type.typeName) && type.typeName.right) ||
|
||||
null;
|
||||
if (typeName === null) {
|
||||
return null;
|
||||
@ -559,7 +565,7 @@ export class NgModuleDecoratorHandler implements
|
||||
// Unwrap ModuleWithProviders for modules that are locally declared (and thus static
|
||||
// resolution was able to descend into the function and return an object literal, a Map).
|
||||
if (entry instanceof Map && entry.has('ngModule')) {
|
||||
entry = entry.get('ngModule') !;
|
||||
entry = entry.get('ngModule')!;
|
||||
}
|
||||
|
||||
if (Array.isArray(entry)) {
|
||||
@ -569,14 +575,16 @@ export class NgModuleDecoratorHandler implements
|
||||
if (!this.isClassDeclarationReference(entry)) {
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.VALUE_HAS_WRONG_TYPE, entry.node,
|
||||
`Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a class`);
|
||||
`Value at position ${idx} in the NgModule.${arrayName} of ${
|
||||
className} is not a class`);
|
||||
}
|
||||
refList.push(entry);
|
||||
} else {
|
||||
// TODO(alxhub): Produce a better diagnostic here - the array index may be an inner array.
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.VALUE_HAS_WRONG_TYPE, expr,
|
||||
`Value at position ${idx} in the NgModule.${arrayName} of ${className} is not a reference: ${entry}`);
|
||||
`Value at position ${idx} in the NgModule.${arrayName} of ${
|
||||
className} is not a reference: ${entry}`);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {Identifiers, R3FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr, compilePipeFromMetadata} from '@angular/compiler';
|
||||
import {compilePipeFromMetadata, Identifiers, R3FactoryTarget, R3PipeMetadata, Statement, WrappedNodeExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ErrorCode, FatalDiagnosticError} from '../../diagnostics';
|
||||
@ -79,7 +79,7 @@ export class PipeDecoratorHandler implements DecoratorHandler<Decorator, PipeHan
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.PIPE_MISSING_NAME, meta, `@Pipe decorator is missing name field`);
|
||||
}
|
||||
const pipeNameExpr = pipe.get('name') !;
|
||||
const pipeNameExpr = pipe.get('name')!;
|
||||
const pipeName = this.evaluator.evaluate(pipeNameExpr);
|
||||
if (typeof pipeName !== 'string') {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -88,7 +88,7 @@ export class PipeDecoratorHandler implements DecoratorHandler<Decorator, PipeHan
|
||||
|
||||
let pure = true;
|
||||
if (pipe.has('pure')) {
|
||||
const expr = pipe.get('pure') !;
|
||||
const expr = pipe.get('pure')!;
|
||||
const pureValue = this.evaluator.evaluate(expr);
|
||||
if (typeof pureValue !== 'boolean') {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -103,7 +103,8 @@ export class PipeDecoratorHandler implements DecoratorHandler<Decorator, PipeHan
|
||||
name,
|
||||
type,
|
||||
internalType,
|
||||
typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0, pipeName,
|
||||
typeArgumentCount: this.reflector.getGenericArityOfClass(clazz) || 0,
|
||||
pipeName,
|
||||
deps: getValidConstructorDependencies(
|
||||
clazz, this.reflector, this.defaultImportRecorder, this.isCore),
|
||||
pure,
|
||||
|
@ -12,7 +12,7 @@ import * as ts from 'typescript';
|
||||
import {ErrorCode, FatalDiagnosticError, makeDiagnostic} from '../../diagnostics';
|
||||
import {DefaultImportRecorder, ImportFlags, Reference, ReferenceEmitter} from '../../imports';
|
||||
import {ForeignFunctionResolver, PartialEvaluator} from '../../partial_evaluator';
|
||||
import {ClassDeclaration, CtorParameter, Decorator, Import, ReflectionHost, TypeValueReference, isNamedClassDeclaration} from '../../reflection';
|
||||
import {ClassDeclaration, CtorParameter, Decorator, Import, isNamedClassDeclaration, ReflectionHost, TypeValueReference} from '../../reflection';
|
||||
import {DeclarationData} from '../../scope';
|
||||
|
||||
export enum ConstructorDepErrorKind {
|
||||
@ -21,8 +21,7 @@ export enum ConstructorDepErrorKind {
|
||||
|
||||
export type ConstructorDeps = {
|
||||
deps: R3DependencyMetadata[];
|
||||
} |
|
||||
{
|
||||
}|{
|
||||
deps: null;
|
||||
errors: ConstructorDepError[];
|
||||
};
|
||||
@ -53,7 +52,7 @@ export function getConstructorDependencies(
|
||||
let resolved = R3ResolvedDependencyType.Token;
|
||||
|
||||
(param.decorators || []).filter(dec => isCore || isAngularCore(dec)).forEach(dec => {
|
||||
const name = isCore || dec.import === null ? dec.name : dec.import !.name;
|
||||
const name = isCore || dec.import === null ? dec.name : dec.import!.name;
|
||||
if (name === 'Inject') {
|
||||
if (dec.args === null || dec.args.length !== 1) {
|
||||
throw new FatalDiagnosticError(
|
||||
@ -97,7 +96,8 @@ export function getConstructorDependencies(
|
||||
if (token === null) {
|
||||
errors.push({
|
||||
index: idx,
|
||||
kind: ConstructorDepErrorKind.NO_SUITABLE_TOKEN, param,
|
||||
kind: ConstructorDepErrorKind.NO_SUITABLE_TOKEN,
|
||||
param,
|
||||
});
|
||||
} else {
|
||||
deps.push({token, attribute, optional, self, skipSelf, host, resolved});
|
||||
@ -122,10 +122,10 @@ export function valueReferenceToExpression(
|
||||
export function valueReferenceToExpression(
|
||||
valueRef: null, defaultImportRecorder: DefaultImportRecorder): null;
|
||||
export function valueReferenceToExpression(
|
||||
valueRef: TypeValueReference | null, defaultImportRecorder: DefaultImportRecorder): Expression|
|
||||
valueRef: TypeValueReference|null, defaultImportRecorder: DefaultImportRecorder): Expression|
|
||||
null;
|
||||
export function valueReferenceToExpression(
|
||||
valueRef: TypeValueReference | null, defaultImportRecorder: DefaultImportRecorder): Expression|
|
||||
valueRef: TypeValueReference|null, defaultImportRecorder: DefaultImportRecorder): Expression|
|
||||
null {
|
||||
if (valueRef === null) {
|
||||
return null;
|
||||
@ -138,7 +138,7 @@ export function valueReferenceToExpression(
|
||||
return new WrappedNodeExpr(valueRef.expression);
|
||||
} else {
|
||||
// TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here.
|
||||
return new ExternalExpr(valueRef as{moduleName: string, name: string});
|
||||
return new ExternalExpr(valueRef as {moduleName: string, name: string});
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,7 +148,7 @@ export function valueReferenceToExpression(
|
||||
*
|
||||
* This is a companion function to `validateConstructorDependencies` which accepts invalid deps.
|
||||
*/
|
||||
export function unwrapConstructorDependencies(deps: ConstructorDeps | null): R3DependencyMetadata[]|
|
||||
export function unwrapConstructorDependencies(deps: ConstructorDeps|null): R3DependencyMetadata[]|
|
||||
'invalid'|null {
|
||||
if (deps === null) {
|
||||
return null;
|
||||
@ -176,18 +176,19 @@ export function getValidConstructorDependencies(
|
||||
* deps.
|
||||
*/
|
||||
export function validateConstructorDependencies(
|
||||
clazz: ClassDeclaration, deps: ConstructorDeps | null): R3DependencyMetadata[]|null {
|
||||
clazz: ClassDeclaration, deps: ConstructorDeps|null): R3DependencyMetadata[]|null {
|
||||
if (deps === null) {
|
||||
return null;
|
||||
} else if (deps.deps !== null) {
|
||||
return deps.deps;
|
||||
} else {
|
||||
// TODO(alxhub): this cast is necessary because the g3 typescript version doesn't narrow here.
|
||||
const {param, index} = (deps as{errors: ConstructorDepError[]}).errors[0];
|
||||
const {param, index} = (deps as {errors: ConstructorDepError[]}).errors[0];
|
||||
// There is at least one error.
|
||||
throw new FatalDiagnosticError(
|
||||
ErrorCode.PARAM_MISSING_TOKEN, param.nameNode,
|
||||
`No suitable injection token for parameter '${param.name || index}' of class '${clazz.name.text}'.\n` +
|
||||
`No suitable injection token for parameter '${param.name || index}' of class '${
|
||||
clazz.name.text}'.\n` +
|
||||
(param.typeNode !== null ? `Found ${param.typeNode.getText()}` :
|
||||
'no type or decorator'));
|
||||
}
|
||||
@ -319,8 +320,7 @@ export function forwardRefResolver(
|
||||
*/
|
||||
export function combineResolvers(resolvers: ForeignFunctionResolver[]): ForeignFunctionResolver {
|
||||
return (ref: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>,
|
||||
args: ReadonlyArray<ts.Expression>): ts.Expression |
|
||||
null => {
|
||||
args: ReadonlyArray<ts.Expression>): ts.Expression|null => {
|
||||
for (const resolver of resolvers) {
|
||||
const resolved = resolver(ref, args);
|
||||
if (resolved !== null) {
|
||||
@ -406,8 +406,8 @@ export function makeDuplicateDeclarationError(
|
||||
const contextNode = decl.ref.getOriginForDiagnostics(decl.rawDeclarations, decl.ngModule.name);
|
||||
context.push({
|
||||
node: contextNode,
|
||||
messageText:
|
||||
`'${node.name.text}' is listed in the declarations of the NgModule '${decl.ngModule.name.text}'.`,
|
||||
messageText: `'${node.name.text}' is listed in the declarations of the NgModule '${
|
||||
decl.ngModule.name.text}'.`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -441,7 +441,7 @@ export function resolveProvidersRequiringFactory(
|
||||
} else if (provider instanceof Reference) {
|
||||
tokenClass = provider;
|
||||
} else if (provider instanceof Map && provider.has('useClass') && !provider.has('deps')) {
|
||||
const useExisting = provider.get('useClass') !;
|
||||
const useExisting = provider.get('useClass')!;
|
||||
if (useExisting instanceof Reference) {
|
||||
tokenClass = useExisting;
|
||||
}
|
||||
|
@ -12,17 +12,23 @@ import {runInEachFileSystem} from '../../file_system/testing';
|
||||
import {ModuleResolver, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports';
|
||||
import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata';
|
||||
import {PartialEvaluator} from '../../partial_evaluator';
|
||||
import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection';
|
||||
import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
|
||||
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
|
||||
import {getDeclaration, makeProgram} from '../../testing';
|
||||
import {ResourceLoader} from '../src/api';
|
||||
import {ComponentDecoratorHandler} from '../src/component';
|
||||
|
||||
export class NoopResourceLoader implements ResourceLoader {
|
||||
resolve(): string { throw new Error('Not implemented.'); }
|
||||
resolve(): string {
|
||||
throw new Error('Not implemented.');
|
||||
}
|
||||
canPreload = false;
|
||||
load(): string { throw new Error('Not implemented'); }
|
||||
preload(): Promise<void>|undefined { throw new Error('Not implemented'); }
|
||||
load(): string {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
preload(): Promise<void>|undefined {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
}
|
||||
runInEachFileSystem(() => {
|
||||
describe('ComponentDecoratorHandler', () => {
|
||||
@ -83,10 +89,12 @@ runInEachFileSystem(() => {
|
||||
const diag = err.toDiagnostic();
|
||||
expect(diag.code).toEqual(ivyCode(ErrorCode.DECORATOR_ARG_NOT_LITERAL));
|
||||
expect(diag.file.fileName.endsWith('entry.ts')).toBe(true);
|
||||
expect(diag.start).toBe(detected.metadata.args ![0].getStart());
|
||||
expect(diag.start).toBe(detected.metadata.args![0].getStart());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function ivyCode(code: ErrorCode): number { return Number('-99' + code.valueOf()); }
|
||||
function ivyCode(code: ErrorCode): number {
|
||||
return Number('-99' + code.valueOf());
|
||||
}
|
||||
});
|
||||
|
@ -6,12 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {absoluteFrom} from '../../file_system';
|
||||
import {runInEachFileSystem} from '../../file_system/testing';
|
||||
import {NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports';
|
||||
import {DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata';
|
||||
import {PartialEvaluator} from '../../partial_evaluator';
|
||||
import {ClassDeclaration, TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection';
|
||||
import {ClassDeclaration, isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
|
||||
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
|
||||
import {getDeclaration, makeProgram} from '../../testing';
|
||||
import {DirectiveDecoratorHandler} from '../src/directive';
|
||||
@ -77,9 +78,13 @@ runInEachFileSystem(() => {
|
||||
// Helpers
|
||||
function analyzeDirective(program: ts.Program, dirName: string, hasBaseClass: boolean = false) {
|
||||
class TestReflectionHost extends TypeScriptReflectionHost {
|
||||
constructor(checker: ts.TypeChecker) { super(checker); }
|
||||
constructor(checker: ts.TypeChecker) {
|
||||
super(checker);
|
||||
}
|
||||
|
||||
hasBaseClass(_class: ClassDeclaration): boolean { return hasBaseClass; }
|
||||
hasBaseClass(_class: ClassDeclaration): boolean {
|
||||
return hasBaseClass;
|
||||
}
|
||||
}
|
||||
|
||||
const checker = program.getTypeChecker();
|
||||
|
@ -10,7 +10,7 @@ import {absoluteFrom} from '../../file_system';
|
||||
import {runInEachFileSystem} from '../../file_system/testing';
|
||||
import {NOOP_DEFAULT_IMPORT_RECORDER} from '../../imports';
|
||||
import {InjectableClassRegistry} from '../../metadata';
|
||||
import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection';
|
||||
import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
|
||||
import {getDeclaration, makeProgram} from '../../testing';
|
||||
import {InjectableDecoratorHandler} from '../src/injectable';
|
||||
|
||||
@ -31,7 +31,7 @@ runInEachFileSystem(() => {
|
||||
const diag = err.toDiagnostic();
|
||||
expect(diag.code).toEqual(ngErrorCode(ErrorCode.INJECTABLE_DUPLICATE_PROV));
|
||||
expect(diag.file.fileName.endsWith('entry.ts')).toBe(true);
|
||||
expect(diag.start).toBe(ɵprov.nameNode !.getStart());
|
||||
expect(diag.start).toBe(ɵprov.nameNode!.getStart());
|
||||
}
|
||||
});
|
||||
|
||||
@ -43,7 +43,6 @@ runInEachFileSystem(() => {
|
||||
expect(res).not.toContain(jasmine.objectContaining({name: 'ɵprov'}));
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -6,8 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {absoluteFrom, getSourceFileOrError} from '../../file_system';
|
||||
import {TestFile, runInEachFileSystem} from '../../file_system/testing';
|
||||
import {runInEachFileSystem, TestFile} from '../../file_system/testing';
|
||||
import {NOOP_DEFAULT_IMPORT_RECORDER, NoopImportRewriter} from '../../imports';
|
||||
import {TypeScriptReflectionHost} from '../../reflection';
|
||||
import {getDeclaration, makeProgram} from '../../testing';
|
||||
|
@ -14,7 +14,7 @@ import {runInEachFileSystem} from '../../file_system/testing';
|
||||
import {LocalIdentifierStrategy, NOOP_DEFAULT_IMPORT_RECORDER, ReferenceEmitter} from '../../imports';
|
||||
import {CompoundMetadataReader, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry} from '../../metadata';
|
||||
import {PartialEvaluator} from '../../partial_evaluator';
|
||||
import {TypeScriptReflectionHost, isNamedClassDeclaration} from '../../reflection';
|
||||
import {isNamedClassDeclaration, TypeScriptReflectionHost} from '../../reflection';
|
||||
import {LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
|
||||
import {getDeclaration, makeProgram} from '../../testing';
|
||||
import {NgModuleDecoratorHandler} from '../src/ng_module';
|
||||
@ -79,7 +79,7 @@ runInEachFileSystem(() => {
|
||||
if (detected === undefined) {
|
||||
return fail('Failed to recognize @NgModule');
|
||||
}
|
||||
const moduleDef = handler.analyze(TestModule, detected.metadata).analysis !.mod;
|
||||
const moduleDef = handler.analyze(TestModule, detected.metadata).analysis!.mod;
|
||||
|
||||
expect(getReferenceIdentifierTexts(moduleDef.declarations)).toEqual(['TestComp']);
|
||||
expect(getReferenceIdentifierTexts(moduleDef.exports)).toEqual(['TestComp']);
|
||||
|
@ -13,8 +13,9 @@ import {unwrapExpression} from '../src/util';
|
||||
describe('ngtsc annotation utilities', () => {
|
||||
describe('unwrapExpression', () => {
|
||||
const obj = ts.createObjectLiteral();
|
||||
it('should pass through an ObjectLiteralExpression',
|
||||
() => { expect(unwrapExpression(obj)).toBe(obj); });
|
||||
it('should pass through an ObjectLiteralExpression', () => {
|
||||
expect(unwrapExpression(obj)).toBe(obj);
|
||||
});
|
||||
|
||||
it('should unwrap an ObjectLiteralExpression in parentheses', () => {
|
||||
const wrapped = ts.createParen(obj);
|
||||
|
@ -56,7 +56,7 @@ export interface ResourceHost {
|
||||
* core interface.
|
||||
*/
|
||||
export interface ExtendedTsCompilerHost extends ts.CompilerHost, Partial<ResourceHost>,
|
||||
Partial<UnifiedModulesHost> {}
|
||||
Partial<UnifiedModulesHost> {}
|
||||
|
||||
export interface LazyRoute {
|
||||
route: string;
|
||||
|
@ -36,7 +36,8 @@ export interface TestOnlyOptions {
|
||||
*/
|
||||
ivyTemplateTypeCheck?: boolean;
|
||||
|
||||
/** An option to enable ngtsc's internal performance tracing.
|
||||
/**
|
||||
* An option to enable ngtsc's internal performance tracing.
|
||||
*
|
||||
* This should be a path to a JSON file where trace information will be written. An optional 'ts:'
|
||||
* prefix will cause the trace to be written via the TS host instead of directly to the filesystem
|
||||
@ -54,4 +55,5 @@ export interface TestOnlyOptions {
|
||||
* Also includes a few miscellaneous options.
|
||||
*/
|
||||
export interface NgCompilerOptions extends ts.CompilerOptions, LegacyNgcOptions, BazelAndG3Options,
|
||||
NgcCompatibilityOptions, StrictTemplateOptions, TestOnlyOptions, I18nOptions, MiscOptions {}
|
||||
NgcCompatibilityOptions, StrictTemplateOptions,
|
||||
TestOnlyOptions, I18nOptions, MiscOptions {}
|
@ -12,23 +12,23 @@ import * as ts from 'typescript';
|
||||
import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecoratorHandler, NgModuleDecoratorHandler, NoopReferencesRegistry, PipeDecoratorHandler, ReferencesRegistry} from '../../annotations';
|
||||
import {CycleAnalyzer, ImportGraph} from '../../cycles';
|
||||
import {ErrorCode, ngErrorCode} from '../../diagnostics';
|
||||
import {ReferenceGraph, checkForPrivateExports} from '../../entry_point';
|
||||
import {LogicalFileSystem, getSourceFileOrError} from '../../file_system';
|
||||
import {AbsoluteModuleStrategy, AliasStrategy, AliasingHost, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports';
|
||||
import {checkForPrivateExports, ReferenceGraph} from '../../entry_point';
|
||||
import {getSourceFileOrError, LogicalFileSystem} from '../../file_system';
|
||||
import {AbsoluteModuleStrategy, AliasingHost, AliasStrategy, DefaultImportTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports';
|
||||
import {IncrementalDriver} from '../../incremental';
|
||||
import {IndexedComponent, IndexingContext, generateAnalysis} from '../../indexer';
|
||||
import {generateAnalysis, IndexedComponent, IndexingContext} from '../../indexer';
|
||||
import {CompoundMetadataReader, CompoundMetadataRegistry, DtsMetadataReader, InjectableClassRegistry, LocalMetadataRegistry, MetadataReader} from '../../metadata';
|
||||
import {ModuleWithProvidersScanner} from '../../modulewithproviders';
|
||||
import {PartialEvaluator} from '../../partial_evaluator';
|
||||
import {NOOP_PERF_RECORDER, PerfRecorder} from '../../perf';
|
||||
import {TypeScriptReflectionHost} from '../../reflection';
|
||||
import {HostResourceLoader} from '../../resource';
|
||||
import {NgModuleRouteAnalyzer, entryPointKeyFor} from '../../routing';
|
||||
import {entryPointKeyFor, NgModuleRouteAnalyzer} from '../../routing';
|
||||
import {ComponentScopeReader, LocalModuleScopeRegistry, MetadataDtsModuleScopeResolver} from '../../scope';
|
||||
import {generatedFactoryTransform} from '../../shims';
|
||||
import {ivySwitchTransform} from '../../switch';
|
||||
import {DecoratorHandler, DtsTransformRegistry, TraitCompiler, aliasTransformFactory, declarationTransformFactory, ivyTransformFactory} from '../../transform';
|
||||
import {TypeCheckContext, TypeCheckingConfig, isTemplateDiagnostic} from '../../typecheck';
|
||||
import {aliasTransformFactory, declarationTransformFactory, DecoratorHandler, DtsTransformRegistry, ivyTransformFactory, TraitCompiler} from '../../transform';
|
||||
import {isTemplateDiagnostic, TypeCheckContext, TypeCheckingConfig} from '../../typecheck';
|
||||
import {getSourceFileOrNull, isDtsPath, resolveModuleName} from '../../util/src/typescript';
|
||||
import {LazyRoute, NgCompilerOptions} from '../api';
|
||||
|
||||
@ -191,7 +191,9 @@ export class NgCompiler {
|
||||
/**
|
||||
* Get all setup-related diagnostics for this compilation.
|
||||
*/
|
||||
getOptionDiagnostics(): ts.Diagnostic[] { return this.constructionDiagnostics; }
|
||||
getOptionDiagnostics(): ts.Diagnostic[] {
|
||||
return this.constructionDiagnostics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `ts.Program` to use as a starting point when spawning a subsequent incremental
|
||||
@ -202,7 +204,9 @@ export class NgCompiler {
|
||||
* operation, the consumer's `ts.Program` is no longer usable for starting a new incremental
|
||||
* compilation. `getNextProgram` retrieves the `ts.Program` which can be used instead.
|
||||
*/
|
||||
getNextProgram(): ts.Program { return this.nextProgram; }
|
||||
getNextProgram(): ts.Program {
|
||||
return this.nextProgram;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform Angular's analysis step (as a precursor to `getDiagnostics` or `prepareEmit`)
|
||||
@ -262,8 +266,8 @@ export class NgCompiler {
|
||||
|
||||
// Relative entry paths are disallowed.
|
||||
if (entryRoute.startsWith('.')) {
|
||||
throw new Error(
|
||||
`Failed to list lazy routes: Resolution of relative paths (${entryRoute}) is not supported.`);
|
||||
throw new Error(`Failed to list lazy routes: Resolution of relative paths (${
|
||||
entryRoute}) is not supported.`);
|
||||
}
|
||||
|
||||
// Non-relative entry paths fall into one of the following categories:
|
||||
@ -349,7 +353,7 @@ export class NgCompiler {
|
||||
if (this.compilation === null) {
|
||||
this.analyzeSync();
|
||||
}
|
||||
return this.compilation !;
|
||||
return this.compilation!;
|
||||
}
|
||||
|
||||
private analyzeSync(): void {
|
||||
@ -482,7 +486,7 @@ export class NgCompiler {
|
||||
// Execute the typeCheck phase of each decorator in the program.
|
||||
const prepSpan = this.perfRecorder.start('typeCheckPrep');
|
||||
const ctx = new TypeCheckContext(
|
||||
typeCheckingConfig, compilation.refEmitter !, compilation.reflector, host.typeCheckFile);
|
||||
typeCheckingConfig, compilation.refEmitter!, compilation.reflector, host.typeCheckFile);
|
||||
compilation.traitCompiler.typeCheck(ctx);
|
||||
this.perfRecorder.stop(prepSpan);
|
||||
|
||||
@ -505,7 +509,7 @@ export class NgCompiler {
|
||||
const recordSpan = this.perfRecorder.start('recordDependencies');
|
||||
const depGraph = this.incrementalDriver.depGraph;
|
||||
|
||||
for (const scope of this.compilation !.scopeRegistry !.getCompilationScopes()) {
|
||||
for (const scope of this.compilation!.scopeRegistry!.getCompilationScopes()) {
|
||||
const file = scope.declaration.getSourceFile();
|
||||
const ngModuleFile = scope.ngModule.getSourceFile();
|
||||
|
||||
@ -517,7 +521,7 @@ export class NgCompiler {
|
||||
depGraph.addDependency(file, ngModuleFile);
|
||||
|
||||
const meta =
|
||||
this.compilation !.metaReader.getDirectiveMetadata(new Reference(scope.declaration));
|
||||
this.compilation!.metaReader.getDirectiveMetadata(new Reference(scope.declaration));
|
||||
if (meta !== null && meta.isComponent) {
|
||||
// If a component's template changes, it might have affected the import graph, and thus the
|
||||
// remote scoping feature which is activated in the event of potential import cycles. Thus,
|
||||
@ -543,12 +547,11 @@ export class NgCompiler {
|
||||
}
|
||||
|
||||
private scanForMwp(sf: ts.SourceFile): void {
|
||||
this.compilation !.mwpScanner.scan(sf, {
|
||||
this.compilation!.mwpScanner.scan(sf, {
|
||||
addTypeReplacement: (node: ts.Declaration, type: Type): void => {
|
||||
// Only obtain the return type transform for the source file once there's a type to replace,
|
||||
// so that no transform is allocated when there's nothing to do.
|
||||
this.compilation !.dtsTransforms !.getReturnTypeTransform(sf).addTypeReplacement(
|
||||
node, type);
|
||||
this.compilation!.dtsTransforms!.getReturnTypeTransform(sf).addTypeReplacement(node, type);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -686,9 +689,18 @@ export class NgCompiler {
|
||||
this.options.compileNonExportedClasses !== false, dtsTransforms);
|
||||
|
||||
return {
|
||||
isCore, traitCompiler, reflector, scopeRegistry,
|
||||
dtsTransforms, exportReferenceGraph, routeAnalyzer, mwpScanner,
|
||||
metaReader, defaultImportTracker, aliasingHost, refEmitter,
|
||||
isCore,
|
||||
traitCompiler,
|
||||
reflector,
|
||||
scopeRegistry,
|
||||
dtsTransforms,
|
||||
exportReferenceGraph,
|
||||
routeAnalyzer,
|
||||
mwpScanner,
|
||||
metaReader,
|
||||
defaultImportTracker,
|
||||
aliasingHost,
|
||||
refEmitter,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ErrorCode, ngErrorCode} from '../../diagnostics';
|
||||
import {FlatIndexGenerator, findFlatIndexEntryPoint} from '../../entry_point';
|
||||
import {findFlatIndexEntryPoint, FlatIndexGenerator} from '../../entry_point';
|
||||
import {AbsoluteFsPath, resolve} from '../../file_system';
|
||||
import {FactoryGenerator, FactoryTracker, ShimGenerator, SummaryGenerator, TypeCheckShimGenerator} from '../../shims';
|
||||
import {typeCheckFilePath} from '../../typecheck';
|
||||
@ -88,8 +88,7 @@ export class DelegatingCompilerHost implements
|
||||
* `ExtendedTsCompilerHost` methods whenever present.
|
||||
*/
|
||||
export class NgCompilerHost extends DelegatingCompilerHost implements
|
||||
RequiredCompilerHostDelegations,
|
||||
ExtendedTsCompilerHost {
|
||||
RequiredCompilerHostDelegations, ExtendedTsCompilerHost {
|
||||
readonly factoryTracker: FactoryTracker|null = null;
|
||||
readonly entryPoint: AbsoluteFsPath|null = null;
|
||||
readonly diagnostics: ts.Diagnostic[];
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {FileSystem, NgtscCompilerHost, absoluteFrom as _, getFileSystem, getSourceFileOrError, setFileSystem} from '../../file_system';
|
||||
import {absoluteFrom as _, FileSystem, getFileSystem, getSourceFileOrError, NgtscCompilerHost, setFileSystem} from '../../file_system';
|
||||
import {runInEachFileSystem} from '../../file_system/testing';
|
||||
import {NgCompilerOptions} from '../api';
|
||||
import {NgCompiler} from '../src/compiler';
|
||||
@ -16,7 +16,6 @@ import {NgCompilerHost} from '../src/host';
|
||||
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
|
||||
describe('NgCompiler', () => {
|
||||
let fs: FileSystem;
|
||||
|
||||
|
@ -30,7 +30,7 @@ export class ImportGraph {
|
||||
if (!this.map.has(sf)) {
|
||||
this.map.set(sf, this.scanImports(sf));
|
||||
}
|
||||
return this.map.get(sf) !;
|
||||
return this.map.get(sf)!;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,7 +47,9 @@ export class ImportGraph {
|
||||
return;
|
||||
}
|
||||
results.add(sf);
|
||||
this.importsOf(sf).forEach(imported => { this.transitiveImportsOfHelper(imported, results); });
|
||||
this.importsOf(sf).forEach(imported => {
|
||||
this.transitiveImportsOfHelper(imported, results);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,7 +33,8 @@ export function makeDiagnostic(code: ErrorCode, node: ts.Node, messageText: stri
|
||||
code: Number('-99' + code.valueOf()),
|
||||
file: ts.getOriginalNode(node).getSourceFile(),
|
||||
start: node.getStart(undefined, false),
|
||||
length: node.getWidth(), messageText,
|
||||
length: node.getWidth(),
|
||||
messageText,
|
||||
};
|
||||
if (relatedInfo !== undefined) {
|
||||
diag.relatedInformation = relatedInfo.map(info => {
|
||||
|
@ -24,7 +24,9 @@ export class FlatIndexGenerator implements ShimGenerator {
|
||||
join(dirname(entryPoint), relativeFlatIndexPath).replace(/\.js$/, '') + '.ts';
|
||||
}
|
||||
|
||||
recognize(fileName: string): boolean { return fileName === this.flatIndexPath; }
|
||||
recognize(fileName: string): boolean {
|
||||
return fileName === this.flatIndexPath;
|
||||
}
|
||||
|
||||
generate(): ts.SourceFile {
|
||||
const relativeEntryPoint = relativePathBetween(this.flatIndexPath, this.entryPoint);
|
||||
|
@ -95,9 +95,11 @@ export function checkForPrivateExports(
|
||||
const diagnostic: ts.Diagnostic = {
|
||||
category: ts.DiagnosticCategory.Error,
|
||||
code: ngErrorCode(ErrorCode.SYMBOL_NOT_EXPORTED),
|
||||
file: transitiveReference.getSourceFile(), ...getPosOfDeclaration(transitiveReference),
|
||||
messageText:
|
||||
`Unsupported private ${descriptor} ${name}. This ${descriptor} is visible to consumers via ${visibleVia}, but is not exported from the top-level library entrypoint.`,
|
||||
file: transitiveReference.getSourceFile(),
|
||||
...getPosOfDeclaration(transitiveReference),
|
||||
messageText: `Unsupported private ${descriptor} ${name}. This ${
|
||||
descriptor} is visible to consumers via ${
|
||||
visibleVia}, but is not exported from the top-level library entrypoint.`,
|
||||
};
|
||||
|
||||
diagnostics.push(diagnostic);
|
||||
|
@ -15,7 +15,7 @@ export class ReferenceGraph<T = ts.Declaration> {
|
||||
if (!this.references.has(from)) {
|
||||
this.references.set(from, new Set());
|
||||
}
|
||||
this.references.get(from) !.add(to);
|
||||
this.references.get(from)!.add(to);
|
||||
}
|
||||
|
||||
transitiveReferencesOf(target: T): Set<T> {
|
||||
@ -47,7 +47,7 @@ export class ReferenceGraph<T = ts.Declaration> {
|
||||
// Look through the outgoing edges of `source`.
|
||||
// TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
|
||||
let candidatePath: T[]|null = null;
|
||||
this.references.get(source) !.forEach(edge => {
|
||||
this.references.get(source)!.forEach(edge => {
|
||||
// Early exit if a path has already been found.
|
||||
if (candidatePath !== null) {
|
||||
return;
|
||||
@ -67,7 +67,7 @@ export class ReferenceGraph<T = ts.Declaration> {
|
||||
private collectTransitiveReferences(set: Set<T>, decl: T): void {
|
||||
if (this.references.has(decl)) {
|
||||
// TODO(alxhub): use proper iteration when the legacy build is removed. (#27762)
|
||||
this.references.get(decl) !.forEach(ref => {
|
||||
this.references.get(decl)!.forEach(ref => {
|
||||
if (!set.has(ref)) {
|
||||
set.add(ref);
|
||||
this.collectTransitiveReferences(set, ref);
|
||||
|
@ -16,9 +16,9 @@ runInEachFileSystem(() => {
|
||||
beforeEach(() => _ = absoluteFrom);
|
||||
|
||||
describe('findFlatIndexEntryPoint', () => {
|
||||
|
||||
it('should use the only source file if only a single one is specified',
|
||||
() => { expect(findFlatIndexEntryPoint([_('/src/index.ts')])).toBe(_('/src/index.ts')); });
|
||||
it('should use the only source file if only a single one is specified', () => {
|
||||
expect(findFlatIndexEntryPoint([_('/src/index.ts')])).toBe(_('/src/index.ts'));
|
||||
});
|
||||
|
||||
it('should use the shortest source file ending with "index.ts" for multiple files', () => {
|
||||
expect(findFlatIndexEntryPoint([
|
||||
|
@ -12,8 +12,9 @@ import {ReferenceGraph} from '../src/reference_graph';
|
||||
describe('entry_point reference graph', () => {
|
||||
let graph: ReferenceGraph<string>;
|
||||
|
||||
const refs =
|
||||
(target: string) => { return Array.from(graph.transitiveReferencesOf(target)).sort(); };
|
||||
const refs = (target: string) => {
|
||||
return Array.from(graph.transitiveReferencesOf(target)).sort();
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
graph = new ReferenceGraph();
|
||||
@ -45,6 +46,7 @@ describe('entry_point reference graph', () => {
|
||||
expect(graph.pathFrom('beta', 'alpha')).toEqual(['beta', 'delta', 'alpha']);
|
||||
});
|
||||
|
||||
it('should not report a path that doesn\'t exist',
|
||||
() => { expect(graph.pathFrom('gamma', 'beta')).toBeNull(); });
|
||||
it('should not report a path that doesn\'t exist', () => {
|
||||
expect(graph.pathFrom('gamma', 'beta')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
@ -25,7 +25,7 @@ export class CachedFileSystem implements FileSystem {
|
||||
if (!this.existsCache.has(path)) {
|
||||
this.existsCache.set(path, this.delegate.exists(path));
|
||||
}
|
||||
return this.existsCache.get(path) !;
|
||||
return this.existsCache.get(path)!;
|
||||
}
|
||||
|
||||
invalidateCaches(path: AbsoluteFsPath) {
|
||||
@ -131,15 +131,33 @@ export class CachedFileSystem implements FileSystem {
|
||||
}
|
||||
|
||||
// The following methods simply call through to the delegate.
|
||||
readdir(path: AbsoluteFsPath): PathSegment[] { return this.delegate.readdir(path); }
|
||||
pwd(): AbsoluteFsPath { return this.delegate.pwd(); }
|
||||
chdir(path: AbsoluteFsPath): void { this.delegate.chdir(path); }
|
||||
extname(path: AbsoluteFsPath|PathSegment): string { return this.delegate.extname(path); }
|
||||
isCaseSensitive(): boolean { return this.delegate.isCaseSensitive(); }
|
||||
isRoot(path: AbsoluteFsPath): boolean { return this.delegate.isRoot(path); }
|
||||
isRooted(path: string): boolean { return this.delegate.isRooted(path); }
|
||||
resolve(...paths: string[]): AbsoluteFsPath { return this.delegate.resolve(...paths); }
|
||||
dirname<T extends PathString>(file: T): T { return this.delegate.dirname(file); }
|
||||
readdir(path: AbsoluteFsPath): PathSegment[] {
|
||||
return this.delegate.readdir(path);
|
||||
}
|
||||
pwd(): AbsoluteFsPath {
|
||||
return this.delegate.pwd();
|
||||
}
|
||||
chdir(path: AbsoluteFsPath): void {
|
||||
this.delegate.chdir(path);
|
||||
}
|
||||
extname(path: AbsoluteFsPath|PathSegment): string {
|
||||
return this.delegate.extname(path);
|
||||
}
|
||||
isCaseSensitive(): boolean {
|
||||
return this.delegate.isCaseSensitive();
|
||||
}
|
||||
isRoot(path: AbsoluteFsPath): boolean {
|
||||
return this.delegate.isRoot(path);
|
||||
}
|
||||
isRooted(path: string): boolean {
|
||||
return this.delegate.isRooted(path);
|
||||
}
|
||||
resolve(...paths: string[]): AbsoluteFsPath {
|
||||
return this.delegate.resolve(...paths);
|
||||
}
|
||||
dirname<T extends PathString>(file: T): T {
|
||||
return this.delegate.dirname(file);
|
||||
}
|
||||
join<T extends PathString>(basePath: T, ...paths: string[]): T {
|
||||
return this.delegate.join(basePath, ...paths);
|
||||
}
|
||||
@ -149,7 +167,13 @@ export class CachedFileSystem implements FileSystem {
|
||||
basename(filePath: string, extension?: string|undefined): PathSegment {
|
||||
return this.delegate.basename(filePath, extension);
|
||||
}
|
||||
realpath(filePath: AbsoluteFsPath): AbsoluteFsPath { return this.delegate.realpath(filePath); }
|
||||
getDefaultLibLocation(): AbsoluteFsPath { return this.delegate.getDefaultLibLocation(); }
|
||||
normalize<T extends PathString>(path: T): T { return this.delegate.normalize(path); }
|
||||
realpath(filePath: AbsoluteFsPath): AbsoluteFsPath {
|
||||
return this.delegate.realpath(filePath);
|
||||
}
|
||||
getDefaultLibLocation(): AbsoluteFsPath {
|
||||
return this.delegate.getDefaultLibLocation();
|
||||
}
|
||||
normalize<T extends PathString>(path: T): T {
|
||||
return this.delegate.normalize(path);
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,9 @@ export class NgtscCompilerHost implements ts.CompilerHost {
|
||||
return this.fs.join(this.getDefaultLibLocation(), ts.getDefaultLibFileName(options));
|
||||
}
|
||||
|
||||
getDefaultLibLocation(): string { return this.fs.getDefaultLibLocation(); }
|
||||
getDefaultLibLocation(): string {
|
||||
return this.fs.getDefaultLibLocation();
|
||||
}
|
||||
|
||||
writeFile(
|
||||
fileName: string, data: string, writeByteOrderMark: boolean,
|
||||
@ -37,13 +39,17 @@ export class NgtscCompilerHost implements ts.CompilerHost {
|
||||
this.fs.writeFile(path, data);
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string { return this.fs.pwd(); }
|
||||
getCurrentDirectory(): string {
|
||||
return this.fs.pwd();
|
||||
}
|
||||
|
||||
getCanonicalFileName(fileName: string): string {
|
||||
return this.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
|
||||
}
|
||||
|
||||
useCaseSensitiveFileNames(): boolean { return this.fs.isCaseSensitive(); }
|
||||
useCaseSensitiveFileNames(): boolean {
|
||||
return this.fs.isCaseSensitive();
|
||||
}
|
||||
|
||||
getNewLine(): string {
|
||||
switch (this.options.newLine) {
|
||||
|
@ -16,32 +16,84 @@ import {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './
|
||||
* the `FileSystem` under the hood.
|
||||
*/
|
||||
export class InvalidFileSystem implements FileSystem {
|
||||
exists(path: AbsoluteFsPath): boolean { throw makeError(); }
|
||||
readFile(path: AbsoluteFsPath): string { throw makeError(); }
|
||||
writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void { throw makeError(); }
|
||||
removeFile(path: AbsoluteFsPath): void { throw makeError(); }
|
||||
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { throw makeError(); }
|
||||
readdir(path: AbsoluteFsPath): PathSegment[] { throw makeError(); }
|
||||
lstat(path: AbsoluteFsPath): FileStats { throw makeError(); }
|
||||
stat(path: AbsoluteFsPath): FileStats { throw makeError(); }
|
||||
pwd(): AbsoluteFsPath { throw makeError(); }
|
||||
chdir(path: AbsoluteFsPath): void { throw makeError(); }
|
||||
extname(path: AbsoluteFsPath|PathSegment): string { throw makeError(); }
|
||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { throw makeError(); }
|
||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { throw makeError(); }
|
||||
ensureDir(path: AbsoluteFsPath): void { throw makeError(); }
|
||||
removeDeep(path: AbsoluteFsPath): void { throw makeError(); }
|
||||
isCaseSensitive(): boolean { throw makeError(); }
|
||||
resolve(...paths: string[]): AbsoluteFsPath { throw makeError(); }
|
||||
dirname<T extends PathString>(file: T): T { throw makeError(); }
|
||||
join<T extends PathString>(basePath: T, ...paths: string[]): T { throw makeError(); }
|
||||
isRoot(path: AbsoluteFsPath): boolean { throw makeError(); }
|
||||
isRooted(path: string): boolean { throw makeError(); }
|
||||
relative<T extends PathString>(from: T, to: T): PathSegment { throw makeError(); }
|
||||
basename(filePath: string, extension?: string): PathSegment { throw makeError(); }
|
||||
realpath(filePath: AbsoluteFsPath): AbsoluteFsPath { throw makeError(); }
|
||||
getDefaultLibLocation(): AbsoluteFsPath { throw makeError(); }
|
||||
normalize<T extends PathString>(path: T): T { throw makeError(); }
|
||||
exists(path: AbsoluteFsPath): boolean {
|
||||
throw makeError();
|
||||
}
|
||||
readFile(path: AbsoluteFsPath): string {
|
||||
throw makeError();
|
||||
}
|
||||
writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void {
|
||||
throw makeError();
|
||||
}
|
||||
removeFile(path: AbsoluteFsPath): void {
|
||||
throw makeError();
|
||||
}
|
||||
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void {
|
||||
throw makeError();
|
||||
}
|
||||
readdir(path: AbsoluteFsPath): PathSegment[] {
|
||||
throw makeError();
|
||||
}
|
||||
lstat(path: AbsoluteFsPath): FileStats {
|
||||
throw makeError();
|
||||
}
|
||||
stat(path: AbsoluteFsPath): FileStats {
|
||||
throw makeError();
|
||||
}
|
||||
pwd(): AbsoluteFsPath {
|
||||
throw makeError();
|
||||
}
|
||||
chdir(path: AbsoluteFsPath): void {
|
||||
throw makeError();
|
||||
}
|
||||
extname(path: AbsoluteFsPath|PathSegment): string {
|
||||
throw makeError();
|
||||
}
|
||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
|
||||
throw makeError();
|
||||
}
|
||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
|
||||
throw makeError();
|
||||
}
|
||||
ensureDir(path: AbsoluteFsPath): void {
|
||||
throw makeError();
|
||||
}
|
||||
removeDeep(path: AbsoluteFsPath): void {
|
||||
throw makeError();
|
||||
}
|
||||
isCaseSensitive(): boolean {
|
||||
throw makeError();
|
||||
}
|
||||
resolve(...paths: string[]): AbsoluteFsPath {
|
||||
throw makeError();
|
||||
}
|
||||
dirname<T extends PathString>(file: T): T {
|
||||
throw makeError();
|
||||
}
|
||||
join<T extends PathString>(basePath: T, ...paths: string[]): T {
|
||||
throw makeError();
|
||||
}
|
||||
isRoot(path: AbsoluteFsPath): boolean {
|
||||
throw makeError();
|
||||
}
|
||||
isRooted(path: string): boolean {
|
||||
throw makeError();
|
||||
}
|
||||
relative<T extends PathString>(from: T, to: T): PathSegment {
|
||||
throw makeError();
|
||||
}
|
||||
basename(filePath: string, extension?: string): PathSegment {
|
||||
throw makeError();
|
||||
}
|
||||
realpath(filePath: AbsoluteFsPath): AbsoluteFsPath {
|
||||
throw makeError();
|
||||
}
|
||||
getDefaultLibLocation(): AbsoluteFsPath {
|
||||
throw makeError();
|
||||
}
|
||||
normalize<T extends PathString>(path: T): T {
|
||||
throw makeError();
|
||||
}
|
||||
}
|
||||
|
||||
function makeError() {
|
||||
|
@ -91,7 +91,7 @@ export class LogicalFileSystem {
|
||||
}
|
||||
this.cache.set(physicalFile, logicalFile);
|
||||
}
|
||||
return this.cache.get(physicalFile) !;
|
||||
return this.cache.get(physicalFile)!;
|
||||
}
|
||||
|
||||
private createLogicalProjectPath(file: AbsoluteFsPath, rootDir: AbsoluteFsPath):
|
||||
|
@ -17,20 +17,42 @@ import {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './
|
||||
*/
|
||||
export class NodeJSFileSystem implements FileSystem {
|
||||
private _caseSensitive: boolean|undefined = undefined;
|
||||
exists(path: AbsoluteFsPath): boolean { return fs.existsSync(path); }
|
||||
readFile(path: AbsoluteFsPath): string { return fs.readFileSync(path, 'utf8'); }
|
||||
exists(path: AbsoluteFsPath): boolean {
|
||||
return fs.existsSync(path);
|
||||
}
|
||||
readFile(path: AbsoluteFsPath): string {
|
||||
return fs.readFileSync(path, 'utf8');
|
||||
}
|
||||
writeFile(path: AbsoluteFsPath, data: string, exclusive: boolean = false): void {
|
||||
fs.writeFileSync(path, data, exclusive ? {flag: 'wx'} : undefined);
|
||||
}
|
||||
removeFile(path: AbsoluteFsPath): void { fs.unlinkSync(path); }
|
||||
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { fs.symlinkSync(target, path); }
|
||||
readdir(path: AbsoluteFsPath): PathSegment[] { return fs.readdirSync(path) as PathSegment[]; }
|
||||
lstat(path: AbsoluteFsPath): FileStats { return fs.lstatSync(path); }
|
||||
stat(path: AbsoluteFsPath): FileStats { return fs.statSync(path); }
|
||||
pwd(): AbsoluteFsPath { return this.normalize(process.cwd()) as AbsoluteFsPath; }
|
||||
chdir(dir: AbsoluteFsPath): void { process.chdir(dir); }
|
||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { fs.copyFileSync(from, to); }
|
||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void { fs.renameSync(from, to); }
|
||||
removeFile(path: AbsoluteFsPath): void {
|
||||
fs.unlinkSync(path);
|
||||
}
|
||||
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void {
|
||||
fs.symlinkSync(target, path);
|
||||
}
|
||||
readdir(path: AbsoluteFsPath): PathSegment[] {
|
||||
return fs.readdirSync(path) as PathSegment[];
|
||||
}
|
||||
lstat(path: AbsoluteFsPath): FileStats {
|
||||
return fs.lstatSync(path);
|
||||
}
|
||||
stat(path: AbsoluteFsPath): FileStats {
|
||||
return fs.statSync(path);
|
||||
}
|
||||
pwd(): AbsoluteFsPath {
|
||||
return this.normalize(process.cwd()) as AbsoluteFsPath;
|
||||
}
|
||||
chdir(dir: AbsoluteFsPath): void {
|
||||
process.chdir(dir);
|
||||
}
|
||||
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
|
||||
fs.copyFileSync(from, to);
|
||||
}
|
||||
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void {
|
||||
fs.renameSync(from, to);
|
||||
}
|
||||
ensureDir(path: AbsoluteFsPath): void {
|
||||
const parents: AbsoluteFsPath[] = [];
|
||||
while (!this.isRoot(path) && !this.exists(path)) {
|
||||
@ -38,10 +60,12 @@ export class NodeJSFileSystem implements FileSystem {
|
||||
path = this.dirname(path);
|
||||
}
|
||||
while (parents.length) {
|
||||
this.safeMkdir(parents.pop() !);
|
||||
this.safeMkdir(parents.pop()!);
|
||||
}
|
||||
}
|
||||
removeDeep(path: AbsoluteFsPath): void { fsExtra.removeSync(path); }
|
||||
removeDeep(path: AbsoluteFsPath): void {
|
||||
fsExtra.removeSync(path);
|
||||
}
|
||||
isCaseSensitive(): boolean {
|
||||
if (this._caseSensitive === undefined) {
|
||||
this._caseSensitive = this.exists(togglePathCase(__filename));
|
||||
@ -52,20 +76,30 @@ export class NodeJSFileSystem implements FileSystem {
|
||||
return this.normalize(p.resolve(...paths)) as AbsoluteFsPath;
|
||||
}
|
||||
|
||||
dirname<T extends string>(file: T): T { return this.normalize(p.dirname(file)) as T; }
|
||||
dirname<T extends string>(file: T): T {
|
||||
return this.normalize(p.dirname(file)) as T;
|
||||
}
|
||||
join<T extends string>(basePath: T, ...paths: string[]): T {
|
||||
return this.normalize(p.join(basePath, ...paths)) as T;
|
||||
}
|
||||
isRoot(path: AbsoluteFsPath): boolean { return this.dirname(path) === this.normalize(path); }
|
||||
isRooted(path: string): boolean { return p.isAbsolute(path); }
|
||||
isRoot(path: AbsoluteFsPath): boolean {
|
||||
return this.dirname(path) === this.normalize(path);
|
||||
}
|
||||
isRooted(path: string): boolean {
|
||||
return p.isAbsolute(path);
|
||||
}
|
||||
relative<T extends PathString>(from: T, to: T): PathSegment {
|
||||
return relativeFrom(this.normalize(p.relative(from, to)));
|
||||
}
|
||||
basename(filePath: string, extension?: string): PathSegment {
|
||||
return p.basename(filePath, extension) as PathSegment;
|
||||
}
|
||||
extname(path: AbsoluteFsPath|PathSegment): string { return p.extname(path); }
|
||||
realpath(path: AbsoluteFsPath): AbsoluteFsPath { return this.resolve(fs.realpathSync(path)); }
|
||||
extname(path: AbsoluteFsPath|PathSegment): string {
|
||||
return p.extname(path);
|
||||
}
|
||||
realpath(path: AbsoluteFsPath): AbsoluteFsPath {
|
||||
return this.resolve(fs.realpathSync(path));
|
||||
}
|
||||
getDefaultLibLocation(): AbsoluteFsPath {
|
||||
return this.resolve(require.resolve('typescript'), '..');
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
* A `string` is not assignable to a `BrandedPath`, but a `BrandedPath` is assignable to a `string`.
|
||||
* Two `BrandedPath`s with different brands are not mutually assignable.
|
||||
*/
|
||||
export type BrandedPath<B extends string> = string & {
|
||||
export type BrandedPath<B extends string> = string&{
|
||||
_brand: B;
|
||||
};
|
||||
|
||||
@ -63,7 +63,7 @@ export interface FileSystem {
|
||||
normalize<T extends PathString>(path: T): T;
|
||||
}
|
||||
|
||||
export type PathString = string | AbsoluteFsPath | PathSegment;
|
||||
export type PathString = string|AbsoluteFsPath|PathSegment;
|
||||
|
||||
/**
|
||||
* Information about an object in the FileSystem.
|
||||
|
@ -28,8 +28,8 @@ export function stripExtension(path: string): string {
|
||||
export function getSourceFileOrError(program: ts.Program, fileName: AbsoluteFsPath): ts.SourceFile {
|
||||
const sf = program.getSourceFile(fileName);
|
||||
if (sf === undefined) {
|
||||
throw new Error(
|
||||
`Program does not contain "${fileName}" - available files are ${program.getSourceFiles().map(sf => sf.fileName).join(', ')}`);
|
||||
throw new Error(`Program does not contain "${fileName}" - available files are ${
|
||||
program.getSourceFiles().map(sf => sf.fileName).join(', ')}`);
|
||||
}
|
||||
return sf;
|
||||
}
|
||||
|
@ -11,37 +11,49 @@ import {absoluteFrom, relativeFrom, setFileSystem} from '../src/helpers';
|
||||
import {NodeJSFileSystem} from '../src/node_js_file_system';
|
||||
|
||||
describe('path types', () => {
|
||||
beforeEach(() => { setFileSystem(new NodeJSFileSystem()); });
|
||||
beforeEach(() => {
|
||||
setFileSystem(new NodeJSFileSystem());
|
||||
});
|
||||
|
||||
describe('absoluteFrom', () => {
|
||||
it('should not throw when creating one from an absolute path',
|
||||
() => { expect(() => absoluteFrom('/test.txt')).not.toThrow(); });
|
||||
it('should not throw when creating one from an absolute path', () => {
|
||||
expect(() => absoluteFrom('/test.txt')).not.toThrow();
|
||||
});
|
||||
|
||||
if (os.platform() === 'win32') {
|
||||
it('should not throw when creating one from a windows absolute path',
|
||||
() => { expect(absoluteFrom('C:\\test.txt')).toEqual('C:/test.txt'); });
|
||||
it('should not throw when creating one from a windows absolute path', () => {
|
||||
expect(absoluteFrom('C:\\test.txt')).toEqual('C:/test.txt');
|
||||
});
|
||||
it('should not throw when creating one from a windows absolute path with POSIX separators',
|
||||
() => { expect(absoluteFrom('C:/test.txt')).toEqual('C:/test.txt'); });
|
||||
it('should support windows drive letters',
|
||||
() => { expect(absoluteFrom('D:\\foo\\test.txt')).toEqual('D:/foo/test.txt'); });
|
||||
it('should convert Windows path separators to POSIX separators',
|
||||
() => { expect(absoluteFrom('C:\\foo\\test.txt')).toEqual('C:/foo/test.txt'); });
|
||||
() => {
|
||||
expect(absoluteFrom('C:/test.txt')).toEqual('C:/test.txt');
|
||||
});
|
||||
it('should support windows drive letters', () => {
|
||||
expect(absoluteFrom('D:\\foo\\test.txt')).toEqual('D:/foo/test.txt');
|
||||
});
|
||||
it('should convert Windows path separators to POSIX separators', () => {
|
||||
expect(absoluteFrom('C:\\foo\\test.txt')).toEqual('C:/foo/test.txt');
|
||||
});
|
||||
}
|
||||
|
||||
it('should throw when creating one from a non-absolute path',
|
||||
() => { expect(() => absoluteFrom('test.txt')).toThrow(); });
|
||||
it('should throw when creating one from a non-absolute path', () => {
|
||||
expect(() => absoluteFrom('test.txt')).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('relativeFrom', () => {
|
||||
it('should not throw when creating one from a relative path',
|
||||
() => { expect(() => relativeFrom('a/b/c.txt')).not.toThrow(); });
|
||||
it('should not throw when creating one from a relative path', () => {
|
||||
expect(() => relativeFrom('a/b/c.txt')).not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw when creating one from an absolute path',
|
||||
() => { expect(() => relativeFrom('/a/b/c.txt')).toThrow(); });
|
||||
it('should throw when creating one from an absolute path', () => {
|
||||
expect(() => relativeFrom('/a/b/c.txt')).toThrow();
|
||||
});
|
||||
|
||||
if (os.platform() === 'win32') {
|
||||
it('should throw when creating one from a Windows absolute path',
|
||||
() => { expect(() => relativeFrom('C:/a/b/c.txt')).toThrow(); });
|
||||
it('should throw when creating one from a Windows absolute path', () => {
|
||||
expect(() => relativeFrom('C:/a/b/c.txt')).toThrow();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -10,4 +10,4 @@ export {Folder, MockFileSystem} from './src/mock_file_system';
|
||||
export {MockFileSystemNative} from './src/mock_file_system_native';
|
||||
export {MockFileSystemPosix} from './src/mock_file_system_posix';
|
||||
export {MockFileSystemWindows} from './src/mock_file_system_windows';
|
||||
export {TestFile, initMockFileSystem, runInEachFileSystem} from './src/test_helper';
|
||||
export {initMockFileSystem, runInEachFileSystem, TestFile} from './src/test_helper';
|
||||
|
@ -21,9 +21,13 @@ export abstract class MockFileSystem implements FileSystem {
|
||||
this._cwd = this.normalize(cwd);
|
||||
}
|
||||
|
||||
isCaseSensitive() { return this._isCaseSensitive; }
|
||||
isCaseSensitive() {
|
||||
return this._isCaseSensitive;
|
||||
}
|
||||
|
||||
exists(path: AbsoluteFsPath): boolean { return this.findFromPath(path).entity !== null; }
|
||||
exists(path: AbsoluteFsPath): boolean {
|
||||
return this.findFromPath(path).entity !== null;
|
||||
}
|
||||
|
||||
readFile(path: AbsoluteFsPath): string {
|
||||
const {entity} = this.findFromPath(path);
|
||||
@ -142,7 +146,9 @@ export abstract class MockFileSystem implements FileSystem {
|
||||
delete entity[basename];
|
||||
}
|
||||
|
||||
isRoot(path: AbsoluteFsPath): boolean { return this.dirname(path) === path; }
|
||||
isRoot(path: AbsoluteFsPath): boolean {
|
||||
return this.dirname(path) === path;
|
||||
}
|
||||
|
||||
extname(path: AbsoluteFsPath|PathSegment): string {
|
||||
const match = /.+(\.[^.]*)$/.exec(path);
|
||||
@ -159,9 +165,13 @@ export abstract class MockFileSystem implements FileSystem {
|
||||
}
|
||||
}
|
||||
|
||||
pwd(): AbsoluteFsPath { return this._cwd; }
|
||||
pwd(): AbsoluteFsPath {
|
||||
return this._cwd;
|
||||
}
|
||||
|
||||
chdir(path: AbsoluteFsPath): void { this._cwd = this.normalize(path); }
|
||||
chdir(path: AbsoluteFsPath): void {
|
||||
this._cwd = this.normalize(path);
|
||||
}
|
||||
|
||||
getDefaultLibLocation(): AbsoluteFsPath {
|
||||
// Mimic the node module resolution algorithm and start in the current directory, then look
|
||||
@ -201,8 +211,12 @@ export abstract class MockFileSystem implements FileSystem {
|
||||
abstract normalize<T extends PathString>(path: T): T;
|
||||
protected abstract splitPath<T extends PathString>(path: T): string[];
|
||||
|
||||
dump(): Folder { return cloneFolder(this._fileTree); }
|
||||
init(folder: Folder): void { this._fileTree = cloneFolder(folder); }
|
||||
dump(): Folder {
|
||||
return cloneFolder(this._fileTree);
|
||||
}
|
||||
init(folder: Folder): void {
|
||||
this._fileTree = cloneFolder(folder);
|
||||
}
|
||||
|
||||
protected findFromPath(path: AbsoluteFsPath, options?: {followSymLinks: boolean}): FindResult {
|
||||
const followSymLinks = !!options && options.followSymLinks;
|
||||
@ -215,7 +229,7 @@ export abstract class MockFileSystem implements FileSystem {
|
||||
segments[0] = '';
|
||||
let current: Entity|null = this._fileTree;
|
||||
while (segments.length) {
|
||||
current = current[segments.shift() !];
|
||||
current = current[segments.shift()!];
|
||||
if (current === undefined) {
|
||||
return {path, entity: null};
|
||||
}
|
||||
@ -239,7 +253,7 @@ export abstract class MockFileSystem implements FileSystem {
|
||||
|
||||
protected splitIntoFolderAndFile(path: AbsoluteFsPath): [AbsoluteFsPath, string] {
|
||||
const segments = this.splitPath(path);
|
||||
const file = segments.pop() !;
|
||||
const file = segments.pop()!;
|
||||
return [path.substring(0, path.length - file.length - 1) as AbsoluteFsPath, file];
|
||||
}
|
||||
}
|
||||
@ -247,8 +261,10 @@ export interface FindResult {
|
||||
path: AbsoluteFsPath;
|
||||
entity: Entity|null;
|
||||
}
|
||||
export type Entity = Folder | File | SymLink;
|
||||
export interface Folder { [pathSegments: string]: Entity; }
|
||||
export type Entity = Folder|File|SymLink;
|
||||
export interface Folder {
|
||||
[pathSegments: string]: Entity;
|
||||
}
|
||||
export type File = string;
|
||||
export class SymLink {
|
||||
constructor(public path: AbsoluteFsPath) {}
|
||||
@ -256,24 +272,32 @@ export class SymLink {
|
||||
|
||||
class MockFileStats implements FileStats {
|
||||
constructor(private entity: Entity) {}
|
||||
isFile(): boolean { return isFile(this.entity); }
|
||||
isDirectory(): boolean { return isFolder(this.entity); }
|
||||
isSymbolicLink(): boolean { return isSymLink(this.entity); }
|
||||
isFile(): boolean {
|
||||
return isFile(this.entity);
|
||||
}
|
||||
isDirectory(): boolean {
|
||||
return isFolder(this.entity);
|
||||
}
|
||||
isSymbolicLink(): boolean {
|
||||
return isSymLink(this.entity);
|
||||
}
|
||||
}
|
||||
|
||||
class MockFileSystemError extends Error {
|
||||
constructor(public code: string, public path: string, message: string) { super(message); }
|
||||
constructor(public code: string, public path: string, message: string) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function isFile(item: Entity | null): item is File {
|
||||
export function isFile(item: Entity|null): item is File {
|
||||
return typeof item === 'string';
|
||||
}
|
||||
|
||||
export function isSymLink(item: Entity | null): item is SymLink {
|
||||
export function isSymLink(item: Entity|null): item is SymLink {
|
||||
return item instanceof SymLink;
|
||||
}
|
||||
|
||||
export function isFolder(item: Entity | null): item is Folder {
|
||||
export function isFolder(item: Entity|null): item is Folder {
|
||||
return item !== null && !isFile(item) && !isSymLink(item);
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,9 @@ import {MockFileSystem} from './mock_file_system';
|
||||
const isWindows = os.platform() === 'win32';
|
||||
|
||||
export class MockFileSystemNative extends MockFileSystem {
|
||||
constructor(cwd: AbsoluteFsPath = '/' as AbsoluteFsPath) { super(undefined, cwd); }
|
||||
constructor(cwd: AbsoluteFsPath = '/' as AbsoluteFsPath) {
|
||||
super(undefined, cwd);
|
||||
}
|
||||
|
||||
// Delegate to the real NodeJSFileSystem for these path related methods
|
||||
|
||||
@ -36,9 +38,13 @@ export class MockFileSystemNative extends MockFileSystem {
|
||||
return NodeJSFileSystem.prototype.basename.call(this, filePath, extension);
|
||||
}
|
||||
|
||||
isCaseSensitive() { return NodeJSFileSystem.prototype.isCaseSensitive.call(this); }
|
||||
isCaseSensitive() {
|
||||
return NodeJSFileSystem.prototype.isCaseSensitive.call(this);
|
||||
}
|
||||
|
||||
isRooted(path: string): boolean { return NodeJSFileSystem.prototype.isRooted.call(this, path); }
|
||||
isRooted(path: string): boolean {
|
||||
return NodeJSFileSystem.prototype.isRooted.call(this, path);
|
||||
}
|
||||
|
||||
isRoot(path: AbsoluteFsPath): boolean {
|
||||
return NodeJSFileSystem.prototype.isRoot.call(this, path);
|
||||
@ -57,5 +63,7 @@ export class MockFileSystemNative extends MockFileSystem {
|
||||
return NodeJSFileSystem.prototype.normalize.call(this, path) as T;
|
||||
}
|
||||
|
||||
protected splitPath<T>(path: string): string[] { return path.split(/[\\\/]/); }
|
||||
protected splitPath<T>(path: string): string[] {
|
||||
return path.split(/[\\\/]/);
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,9 @@ export class MockFileSystemPosix extends MockFileSystem {
|
||||
return this.normalize(resolved) as AbsoluteFsPath;
|
||||
}
|
||||
|
||||
dirname<T extends string>(file: T): T { return this.normalize(p.posix.dirname(file)) as T; }
|
||||
dirname<T extends string>(file: T): T {
|
||||
return this.normalize(p.posix.dirname(file)) as T;
|
||||
}
|
||||
|
||||
join<T extends string>(basePath: T, ...paths: string[]): T {
|
||||
return this.normalize(p.posix.join(basePath, ...paths)) as T;
|
||||
@ -31,9 +33,13 @@ export class MockFileSystemPosix extends MockFileSystem {
|
||||
return p.posix.basename(filePath, extension) as PathSegment;
|
||||
}
|
||||
|
||||
isRooted(path: string): boolean { return path.startsWith('/'); }
|
||||
isRooted(path: string): boolean {
|
||||
return path.startsWith('/');
|
||||
}
|
||||
|
||||
protected splitPath<T extends PathString>(path: T): string[] { return path.split('/'); }
|
||||
protected splitPath<T extends PathString>(path: T): string[] {
|
||||
return path.split('/');
|
||||
}
|
||||
|
||||
normalize<T extends PathString>(path: T): T {
|
||||
return path.replace(/^[a-z]:\//i, '/').replace(/\\/g, '/') as T;
|
||||
|
@ -17,7 +17,9 @@ export class MockFileSystemWindows extends MockFileSystem {
|
||||
return this.normalize(resolved as AbsoluteFsPath);
|
||||
}
|
||||
|
||||
dirname<T extends string>(path: T): T { return this.normalize(p.win32.dirname(path) as T); }
|
||||
dirname<T extends string>(path: T): T {
|
||||
return this.normalize(p.win32.dirname(path) as T);
|
||||
}
|
||||
|
||||
join<T extends string>(basePath: T, ...paths: string[]): T {
|
||||
return this.normalize(p.win32.join(basePath, ...paths)) as T;
|
||||
@ -31,9 +33,13 @@ export class MockFileSystemWindows extends MockFileSystem {
|
||||
return p.win32.basename(filePath, extension) as PathSegment;
|
||||
}
|
||||
|
||||
isRooted(path: string): boolean { return /^([A-Z]:)?([\\\/]|$)/i.test(path); }
|
||||
isRooted(path: string): boolean {
|
||||
return /^([A-Z]:)?([\\\/]|$)/i.test(path);
|
||||
}
|
||||
|
||||
protected splitPath<T extends PathString>(path: T): string[] { return path.split(/[\\\/]/); }
|
||||
protected splitPath<T extends PathString>(path: T): string[] {
|
||||
return path.split(/[\\\/]/);
|
||||
}
|
||||
|
||||
normalize<T extends PathString>(path: T): T {
|
||||
return path.replace(/^[\/\\]/i, 'C:/').replace(/\\/g, '/') as T;
|
||||
|
@ -47,7 +47,9 @@ function runInFileSystem(os: string, callback: (os: string) => void, error: bool
|
||||
afterEach(() => setFileSystem(new InvalidFileSystem()));
|
||||
callback(os);
|
||||
if (error) {
|
||||
afterAll(() => { throw new Error(`runInFileSystem limited to ${os}, cannot pass`); });
|
||||
afterAll(() => {
|
||||
throw new Error(`runInFileSystem limited to ${os}, cannot pass`);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -125,14 +127,16 @@ function monkeyPatchTypeScript(os: string, fs: MockFileSystem) {
|
||||
return {files, directories};
|
||||
}
|
||||
|
||||
function realPath(path: string): string { return fs.realpath(fs.resolve(path)); }
|
||||
function realPath(path: string): string {
|
||||
return fs.realpath(fs.resolve(path));
|
||||
}
|
||||
|
||||
// Rather than completely re-implementing we are using the `ts.matchFiles` function,
|
||||
// which is internal to the `ts` namespace.
|
||||
const tsMatchFiles: (
|
||||
path: string, extensions: ReadonlyArray<string>| undefined,
|
||||
excludes: ReadonlyArray<string>| undefined, includes: ReadonlyArray<string>| undefined,
|
||||
useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number | undefined,
|
||||
path: string, extensions: ReadonlyArray<string>|undefined,
|
||||
excludes: ReadonlyArray<string>|undefined, includes: ReadonlyArray<string>|undefined,
|
||||
useCaseSensitiveFileNames: boolean, currentDirectory: string, depth: number|undefined,
|
||||
getFileSystemEntries: (path: string) => FileSystemEntries,
|
||||
realpath: (path: string) => string) => string[] = (ts as any).matchFiles;
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
export {AliasStrategy, AliasingHost, PrivateExportAliasingHost, UnifiedModulesAliasingHost} from './src/alias';
|
||||
export {AliasingHost, AliasStrategy, PrivateExportAliasingHost, UnifiedModulesAliasingHost} from './src/alias';
|
||||
export {ImportRewriter, NoopImportRewriter, R3SymbolsImportRewriter, validateAndRewriteCoreSymbol} from './src/core';
|
||||
export {DefaultImportRecorder, DefaultImportTracker, NOOP_DEFAULT_IMPORT_RECORDER} from './src/default';
|
||||
export {AbsoluteModuleStrategy, ImportFlags, LocalIdentifierStrategy, LogicalProjectStrategy, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesStrategy} from './src/emitter';
|
||||
|
@ -10,7 +10,7 @@ import {Expression, ExternalExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {UnifiedModulesHost} from '../../core/api';
|
||||
import {ClassDeclaration, ReflectionHost, isNamedClassDeclaration} from '../../reflection';
|
||||
import {ClassDeclaration, isNamedClassDeclaration, ReflectionHost} from '../../reflection';
|
||||
|
||||
import {ImportFlags, ReferenceEmitStrategy} from './emitter';
|
||||
import {Reference} from './references';
|
||||
@ -203,7 +203,9 @@ export class PrivateExportAliasingHost implements AliasingHost {
|
||||
*
|
||||
* Thus, `getAliasIn` always returns `null`.
|
||||
*/
|
||||
getAliasIn(): null { return null; }
|
||||
getAliasIn(): null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,11 +36,17 @@ export interface ImportRewriter {
|
||||
* `ImportRewriter` that does no rewriting.
|
||||
*/
|
||||
export class NoopImportRewriter implements ImportRewriter {
|
||||
shouldImportSymbol(symbol: string, specifier: string): boolean { return true; }
|
||||
shouldImportSymbol(symbol: string, specifier: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
rewriteSymbol(symbol: string, specifier: string): string { return symbol; }
|
||||
rewriteSymbol(symbol: string, specifier: string): string {
|
||||
return symbol;
|
||||
}
|
||||
|
||||
rewriteSpecifier(specifier: string, inContextOfFile: string): string { return specifier; }
|
||||
rewriteSpecifier(specifier: string, inContextOfFile: string): string {
|
||||
return specifier;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,7 +76,9 @@ const CORE_MODULE = '@angular/core';
|
||||
export class R3SymbolsImportRewriter implements ImportRewriter {
|
||||
constructor(private r3SymbolsPath: string) {}
|
||||
|
||||
shouldImportSymbol(symbol: string, specifier: string): boolean { return true; }
|
||||
shouldImportSymbol(symbol: string, specifier: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
rewriteSymbol(symbol: string, specifier: string): string {
|
||||
if (specifier !== CORE_MODULE) {
|
||||
@ -89,8 +97,8 @@ export class R3SymbolsImportRewriter implements ImportRewriter {
|
||||
|
||||
const relativePathToR3Symbols = relativePathBetween(inContextOfFile, this.r3SymbolsPath);
|
||||
if (relativePathToR3Symbols === null) {
|
||||
throw new Error(
|
||||
`Failed to rewrite import inside ${CORE_MODULE}: ${inContextOfFile} -> ${this.r3SymbolsPath}`);
|
||||
throw new Error(`Failed to rewrite import inside ${CORE_MODULE}: ${inContextOfFile} -> ${
|
||||
this.r3SymbolsPath}`);
|
||||
}
|
||||
|
||||
return relativePathToR3Symbols;
|
||||
@ -101,5 +109,5 @@ export function validateAndRewriteCoreSymbol(name: string): string {
|
||||
if (!CORE_SUPPORTED_SYMBOLS.has(name)) {
|
||||
throw new Error(`Importing unexpected symbol ${name} while compiling ${CORE_MODULE}`);
|
||||
}
|
||||
return CORE_SUPPORTED_SYMBOLS.get(name) !;
|
||||
return CORE_SUPPORTED_SYMBOLS.get(name)!;
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
@ -96,7 +96,7 @@ export class DefaultImportTracker implements DefaultImportRecorder {
|
||||
if (!this.sourceFileToImportMap.has(sf)) {
|
||||
this.sourceFileToImportMap.set(sf, new Map<ts.Identifier, ts.ImportDeclaration>());
|
||||
}
|
||||
this.sourceFileToImportMap.get(sf) !.set(id, decl);
|
||||
this.sourceFileToImportMap.get(sf)!.set(id, decl);
|
||||
}
|
||||
|
||||
recordUsedIdentifier(id: ts.Identifier): void {
|
||||
@ -105,18 +105,18 @@ export class DefaultImportTracker implements DefaultImportRecorder {
|
||||
// The identifier's source file has no registered default imports at all.
|
||||
return;
|
||||
}
|
||||
const identiferToDeclaration = this.sourceFileToImportMap.get(sf) !;
|
||||
const identiferToDeclaration = this.sourceFileToImportMap.get(sf)!;
|
||||
if (!identiferToDeclaration.has(id)) {
|
||||
// The identifier isn't from a registered default import.
|
||||
return;
|
||||
}
|
||||
const decl = identiferToDeclaration.get(id) !;
|
||||
const decl = identiferToDeclaration.get(id)!;
|
||||
|
||||
// Add the default import declaration to the set of used import declarations for the file.
|
||||
if (!this.sourceFileToUsedImports.has(sf)) {
|
||||
this.sourceFileToUsedImports.set(sf, new Set<ts.ImportDeclaration>());
|
||||
}
|
||||
this.sourceFileToUsedImports.get(sf) !.add(decl);
|
||||
this.sourceFileToUsedImports.get(sf)!.add(decl);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -127,7 +127,9 @@ export class DefaultImportTracker implements DefaultImportRecorder {
|
||||
*/
|
||||
importPreservingTransformer(): ts.TransformerFactory<ts.SourceFile> {
|
||||
return (context: ts.TransformationContext) => {
|
||||
return (sf: ts.SourceFile) => { return this.transformSourceFile(sf); };
|
||||
return (sf: ts.SourceFile) => {
|
||||
return this.transformSourceFile(sf);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@ -142,7 +144,7 @@ export class DefaultImportTracker implements DefaultImportRecorder {
|
||||
}
|
||||
|
||||
// There are declarations that need to be preserved.
|
||||
const importsToPreserve = this.sourceFileToUsedImports.get(originalSf) !;
|
||||
const importsToPreserve = this.sourceFileToUsedImports.get(originalSf)!;
|
||||
|
||||
// Generate a new statement list which preserves any imports present in `importsToPreserve`.
|
||||
const statements = sf.statements.map(stmt => {
|
||||
|
@ -9,7 +9,7 @@ import {Expression, ExternalExpr, ExternalReference, WrappedNodeExpr} from '@ang
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {UnifiedModulesHost} from '../../core/api';
|
||||
import {LogicalFileSystem, LogicalProjectPath, PathSegment, absoluteFromSourceFile, dirname, relative} from '../../file_system';
|
||||
import {absoluteFromSourceFile, dirname, LogicalFileSystem, LogicalProjectPath, PathSegment, relative} from '../../file_system';
|
||||
import {stripExtension} from '../../file_system/src/util';
|
||||
import {ReflectionHost} from '../../reflection';
|
||||
import {getSourceFile, isDeclaration, isTypeDeclaration, nodeNameForError} from '../../util/src/typescript';
|
||||
@ -93,8 +93,8 @@ export class ReferenceEmitter {
|
||||
return emitted;
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`Unable to write a reference to ${nodeNameForError(ref.node)} in ${ref.node.getSourceFile().fileName} from ${context.fileName}`);
|
||||
throw new Error(`Unable to write a reference to ${nodeNameForError(ref.node)} in ${
|
||||
ref.node.getSourceFile().fileName} from ${context.fileName}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,11 +149,11 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
|
||||
return null;
|
||||
} else if (!isDeclaration(ref.node)) {
|
||||
// It's not possible to import something which isn't a declaration.
|
||||
throw new Error(
|
||||
`Debug assert: unable to import a Reference to non-declaration of type ${ts.SyntaxKind[ref.node.kind]}.`);
|
||||
throw new Error(`Debug assert: unable to import a Reference to non-declaration of type ${
|
||||
ts.SyntaxKind[ref.node.kind]}.`);
|
||||
} else if ((importFlags & ImportFlags.AllowTypeImports) === 0 && isTypeDeclaration(ref.node)) {
|
||||
throw new Error(
|
||||
`Importing a type-only declaration of type ${ts.SyntaxKind[ref.node.kind]} in a value position is not allowed.`);
|
||||
throw new Error(`Importing a type-only declaration of type ${
|
||||
ts.SyntaxKind[ref.node.kind]} in a value position is not allowed.`);
|
||||
}
|
||||
|
||||
// Try to find the exported name of the declaration, if one is available.
|
||||
@ -162,8 +162,9 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
|
||||
if (symbolName === null) {
|
||||
// TODO(alxhub): make this error a ts.Diagnostic pointing at whatever caused this import to be
|
||||
// triggered.
|
||||
throw new Error(
|
||||
`Symbol ${ref.debugName} declared in ${getSourceFile(ref.node).fileName} is not exported from ${specifier} (import into ${context.fileName})`);
|
||||
throw new Error(`Symbol ${ref.debugName} declared in ${
|
||||
getSourceFile(ref.node).fileName} is not exported from ${specifier} (import into ${
|
||||
context.fileName})`);
|
||||
}
|
||||
|
||||
return new ExternalExpr(new ExternalReference(specifier, symbolName));
|
||||
@ -173,7 +174,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
|
||||
|null {
|
||||
const exports = this.getExportsOfModule(moduleName, fromFile);
|
||||
if (exports !== null && exports.has(target)) {
|
||||
return exports.get(target) !;
|
||||
return exports.get(target)!;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -184,7 +185,7 @@ export class AbsoluteModuleStrategy implements ReferenceEmitStrategy {
|
||||
if (!this.moduleExportsCache.has(moduleName)) {
|
||||
this.moduleExportsCache.set(moduleName, this.enumerateExportsOfModule(moduleName, fromFile));
|
||||
}
|
||||
return this.moduleExportsCache.get(moduleName) !;
|
||||
return this.moduleExportsCache.get(moduleName)!;
|
||||
}
|
||||
|
||||
protected enumerateExportsOfModule(specifier: string, fromFile: string):
|
||||
|
@ -80,7 +80,9 @@ export class Reference<T extends ts.Node = ts.Node> {
|
||||
*
|
||||
* See `bestGuessOwningModule`.
|
||||
*/
|
||||
get hasOwningModuleGuess(): boolean { return this.bestGuessOwningModule !== null; }
|
||||
get hasOwningModuleGuess(): boolean {
|
||||
return this.bestGuessOwningModule !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A name for the node, if one is available.
|
||||
@ -93,14 +95,18 @@ export class Reference<T extends ts.Node = ts.Node> {
|
||||
return id !== null ? id.text : null;
|
||||
}
|
||||
|
||||
get alias(): Expression|null { return this._alias; }
|
||||
get alias(): Expression|null {
|
||||
return this._alias;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Record a `ts.Identifier` by which it's valid to refer to this node, within the context of this
|
||||
* `Reference`.
|
||||
*/
|
||||
addIdentifier(identifier: ts.Identifier): void { this.identifiers.push(identifier); }
|
||||
addIdentifier(identifier: ts.Identifier): void {
|
||||
this.identifiers.push(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a `ts.Identifier` within this `Reference` that can be used to refer within the context of a
|
||||
|
@ -39,7 +39,7 @@ runInEachFileSystem(() => {
|
||||
module: ts.ModuleKind.ES2015,
|
||||
});
|
||||
const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause);
|
||||
const fooId = fooClause.name !;
|
||||
const fooId = fooClause.name!;
|
||||
const fooDecl = fooClause.parent;
|
||||
|
||||
const tracker = new DefaultImportTracker();
|
||||
@ -48,7 +48,7 @@ runInEachFileSystem(() => {
|
||||
program.emit(undefined, undefined, undefined, undefined, {
|
||||
before: [tracker.importPreservingTransformer()],
|
||||
});
|
||||
const testContents = host.readFile('/test.js') !;
|
||||
const testContents = host.readFile('/test.js')!;
|
||||
expect(testContents).toContain(`import Foo from './dep';`);
|
||||
|
||||
// The control should have the import elided.
|
||||
@ -69,7 +69,7 @@ runInEachFileSystem(() => {
|
||||
module: ts.ModuleKind.CommonJS,
|
||||
});
|
||||
const fooClause = getDeclaration(program, _('/test.ts'), 'Foo', ts.isImportClause);
|
||||
const fooId = ts.updateIdentifier(fooClause.name !);
|
||||
const fooId = ts.updateIdentifier(fooClause.name!);
|
||||
const fooDecl = fooClause.parent;
|
||||
|
||||
const tracker = new DefaultImportTracker();
|
||||
@ -81,7 +81,7 @@ runInEachFileSystem(() => {
|
||||
tracker.importPreservingTransformer(),
|
||||
],
|
||||
});
|
||||
const testContents = host.readFile('/test.js') !;
|
||||
const testContents = host.readFile('/test.js')!;
|
||||
expect(testContents).toContain(`var dep_1 = require("./dep");`);
|
||||
expect(testContents).toContain(`var ref = dep_1["default"];`);
|
||||
});
|
||||
|
@ -8,8 +8,8 @@
|
||||
import {ExternalExpr} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {LogicalFileSystem, absoluteFrom as _} from '../../file_system';
|
||||
import {TestFile, runInEachFileSystem} from '../../file_system/testing';
|
||||
import {absoluteFrom as _, LogicalFileSystem} from '../../file_system';
|
||||
import {runInEachFileSystem, TestFile} from '../../file_system/testing';
|
||||
import {Declaration, TypeScriptReflectionHost} from '../../reflection';
|
||||
import {getDeclaration, makeProgram} from '../../testing';
|
||||
import {AbsoluteModuleStrategy, ImportFlags, LogicalProjectStrategy} from '../src/emitter';
|
||||
@ -42,7 +42,7 @@ runInEachFileSystem(() => {
|
||||
]);
|
||||
const decl =
|
||||
getDeclaration(program, _('/node_modules/external.d.ts'), 'Foo', ts.isClassDeclaration);
|
||||
const context = program.getSourceFile(_('/context.ts')) !;
|
||||
const context = program.getSourceFile(_('/context.ts'))!;
|
||||
|
||||
const reference = new Reference(decl);
|
||||
const emitted = strategy.emit(reference, context, ImportFlags.None);
|
||||
@ -65,7 +65,7 @@ runInEachFileSystem(() => {
|
||||
]);
|
||||
const decl =
|
||||
getDeclaration(program, _('/node_modules/external.d.ts'), 'Foo', ts.isClassDeclaration);
|
||||
const context = program.getSourceFile(_('/context.ts')) !;
|
||||
const context = program.getSourceFile(_('/context.ts'))!;
|
||||
|
||||
const reference = new Reference(decl, {
|
||||
specifier: 'external',
|
||||
@ -92,7 +92,7 @@ runInEachFileSystem(() => {
|
||||
]);
|
||||
const decl = getDeclaration(
|
||||
program, _('/node_modules/external.d.ts'), 'Foo', ts.isInterfaceDeclaration);
|
||||
const context = program.getSourceFile(_('/context.ts')) !;
|
||||
const context = program.getSourceFile(_('/context.ts'))!;
|
||||
|
||||
const reference = new Reference(decl, {
|
||||
specifier: 'external',
|
||||
@ -116,7 +116,7 @@ runInEachFileSystem(() => {
|
||||
]);
|
||||
const decl = getDeclaration(
|
||||
program, _('/node_modules/external.d.ts'), 'Foo', ts.isInterfaceDeclaration);
|
||||
const context = program.getSourceFile(_('/context.ts')) !;
|
||||
const context = program.getSourceFile(_('/context.ts'))!;
|
||||
|
||||
const reference =
|
||||
new Reference(decl, {specifier: 'external', resolutionContext: context.fileName});
|
||||
@ -139,7 +139,9 @@ runInEachFileSystem(() => {
|
||||
return null;
|
||||
}
|
||||
const fakeExports = new Map<string, Declaration>();
|
||||
realExports.forEach((decl, name) => { fakeExports.set(`test${name}`, decl); });
|
||||
realExports.forEach((decl, name) => {
|
||||
fakeExports.set(`test${name}`, decl);
|
||||
});
|
||||
return fakeExports;
|
||||
}
|
||||
}
|
||||
@ -158,12 +160,12 @@ runInEachFileSystem(() => {
|
||||
const logicalFs = new LogicalFileSystem([_('/')]);
|
||||
const strategy = new LogicalProjectStrategy(new TestHost(checker), logicalFs);
|
||||
const decl = getDeclaration(program, _('/index.ts'), 'Foo', ts.isClassDeclaration);
|
||||
const context = program.getSourceFile(_('/context.ts')) !;
|
||||
const context = program.getSourceFile(_('/context.ts'))!;
|
||||
const ref = strategy.emit(new Reference(decl), context);
|
||||
expect(ref).not.toBeNull();
|
||||
|
||||
// Expect the prefixed name from the TestHost.
|
||||
expect((ref !as ExternalExpr).value.name).toEqual('testFoo');
|
||||
expect((ref! as ExternalExpr).value.name).toEqual('testFoo');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -25,7 +25,7 @@ export interface IncrementalBuild<W> {
|
||||
/**
|
||||
* Tracks dependencies between source files or resources in the application.
|
||||
*/
|
||||
export interface DependencyTracker<T extends{fileName: string} = ts.SourceFile> {
|
||||
export interface DependencyTracker<T extends {fileName: string} = ts.SourceFile> {
|
||||
/**
|
||||
* Record that the file `from` depends on the file `on`.
|
||||
*/
|
||||
|
@ -23,11 +23,13 @@ import {DependencyTracker} from '../api';
|
||||
* 2. One of its dependencies has physically changed.
|
||||
* 3. One of its resource dependencies has physically changed.
|
||||
*/
|
||||
export class FileDependencyGraph<T extends{fileName: string} = ts.SourceFile> implements
|
||||
export class FileDependencyGraph<T extends {fileName: string} = ts.SourceFile> implements
|
||||
DependencyTracker<T> {
|
||||
private nodes = new Map<T, FileNode>();
|
||||
|
||||
addDependency(from: T, on: T): void { this.nodeFor(from).dependsOn.add(on.fileName); }
|
||||
addDependency(from: T, on: T): void {
|
||||
this.nodeFor(from).dependsOn.add(on.fileName);
|
||||
}
|
||||
|
||||
addResourceDependency(from: T, resource: AbsoluteFsPath): void {
|
||||
this.nodeFor(from).usesResources.add(resource);
|
||||
@ -103,7 +105,7 @@ export class FileDependencyGraph<T extends{fileName: string} = ts.SourceFile> im
|
||||
usesResources: new Set<AbsoluteFsPath>(),
|
||||
});
|
||||
}
|
||||
return this.nodes.get(sf) !;
|
||||
return this.nodes.get(sf)!;
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +113,7 @@ export class FileDependencyGraph<T extends{fileName: string} = ts.SourceFile> im
|
||||
* Determine whether `sf` has logically changed, given its dependencies and the set of physically
|
||||
* changed files and resources.
|
||||
*/
|
||||
function isLogicallyChanged<T extends{fileName: string}>(
|
||||
function isLogicallyChanged<T extends {fileName: string}>(
|
||||
sf: T, node: FileNode, changedTsPaths: ReadonlySet<string>, deletedTsPaths: ReadonlySet<string>,
|
||||
changedResources: ReadonlySet<AbsoluteFsPath>): boolean {
|
||||
// A file is logically changed if it has physically changed itself (including being deleted).
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath, absoluteFrom} from '../../file_system';
|
||||
import {absoluteFrom, AbsoluteFsPath} from '../../file_system';
|
||||
import {ClassRecord, TraitCompiler} from '../../transform';
|
||||
import {IncrementalBuild} from '../api';
|
||||
|
||||
@ -194,9 +194,13 @@ export class IncrementalDriver implements IncrementalBuild<ClassRecord> {
|
||||
};
|
||||
}
|
||||
|
||||
recordSuccessfulEmit(sf: ts.SourceFile): void { this.state.pendingEmit.delete(sf.fileName); }
|
||||
recordSuccessfulEmit(sf: ts.SourceFile): void {
|
||||
this.state.pendingEmit.delete(sf.fileName);
|
||||
}
|
||||
|
||||
safeToSkipEmit(sf: ts.SourceFile): boolean { return !this.state.pendingEmit.has(sf.fileName); }
|
||||
safeToSkipEmit(sf: ts.SourceFile): boolean {
|
||||
return !this.state.pendingEmit.has(sf.fileName);
|
||||
}
|
||||
|
||||
priorWorkFor(sf: ts.SourceFile): ClassRecord[]|null {
|
||||
if (this.state.lastGood === null || this.logicalChanges === null) {
|
||||
@ -212,7 +216,7 @@ export class IncrementalDriver implements IncrementalBuild<ClassRecord> {
|
||||
}
|
||||
}
|
||||
|
||||
type BuildState = PendingBuildState | AnalyzedBuildState;
|
||||
type BuildState = PendingBuildState|AnalyzedBuildState;
|
||||
|
||||
enum BuildStateKind {
|
||||
Pending,
|
||||
|
@ -43,13 +43,19 @@ interface ExpressionIdentifier extends TemplateIdentifier {
|
||||
}
|
||||
|
||||
/** Describes a property accessed in a template. */
|
||||
export interface PropertyIdentifier extends ExpressionIdentifier { kind: IdentifierKind.Property; }
|
||||
export interface PropertyIdentifier extends ExpressionIdentifier {
|
||||
kind: IdentifierKind.Property;
|
||||
}
|
||||
|
||||
/** Describes a method accessed in a template. */
|
||||
export interface MethodIdentifier extends ExpressionIdentifier { kind: IdentifierKind.Method; }
|
||||
export interface MethodIdentifier extends ExpressionIdentifier {
|
||||
kind: IdentifierKind.Method;
|
||||
}
|
||||
|
||||
/** Describes an element attribute in a template. */
|
||||
export interface AttributeIdentifier extends TemplateIdentifier { kind: IdentifierKind.Attribute; }
|
||||
export interface AttributeIdentifier extends TemplateIdentifier {
|
||||
kind: IdentifierKind.Attribute;
|
||||
}
|
||||
|
||||
/** A reference to a directive node and its selector. */
|
||||
interface DirectiveReference {
|
||||
@ -85,7 +91,7 @@ export interface ReferenceIdentifier extends TemplateIdentifier {
|
||||
/** The target of this reference. If the target is not known, this is `null`. */
|
||||
target: {
|
||||
/** The template AST node that the reference targets. */
|
||||
node: ElementIdentifier | TemplateIdentifier;
|
||||
node: ElementIdentifier|TemplateIdentifier;
|
||||
|
||||
/**
|
||||
* The directive on `node` that the reference targets. If no directive is targeted, this is
|
||||
@ -96,14 +102,16 @@ export interface ReferenceIdentifier extends TemplateIdentifier {
|
||||
}
|
||||
|
||||
/** Describes a template variable like "foo" in `<div *ngFor="let foo of foos"></div>`. */
|
||||
export interface VariableIdentifier extends TemplateIdentifier { kind: IdentifierKind.Variable; }
|
||||
export interface VariableIdentifier extends TemplateIdentifier {
|
||||
kind: IdentifierKind.Variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifiers recorded at the top level of the template, without any context about the HTML nodes
|
||||
* they were discovered in.
|
||||
*/
|
||||
export type TopLevelIdentifier = PropertyIdentifier | MethodIdentifier | ElementIdentifier |
|
||||
TemplateNodeIdentifier | ReferenceIdentifier | VariableIdentifier;
|
||||
export type TopLevelIdentifier = PropertyIdentifier|MethodIdentifier|ElementIdentifier|
|
||||
TemplateNodeIdentifier|ReferenceIdentifier|VariableIdentifier;
|
||||
|
||||
/**
|
||||
* Describes the absolute byte offsets of a text anchor in a source code.
|
||||
|
@ -56,5 +56,7 @@ export class IndexingContext {
|
||||
/**
|
||||
* Adds a component to the context.
|
||||
*/
|
||||
addComponent(info: ComponentInfo) { this.components.add(info); }
|
||||
addComponent(info: ComponentInfo) {
|
||||
this.components.add(info);
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,9 @@ interface HTMLNode extends TmplAstNode {
|
||||
name?: string;
|
||||
}
|
||||
|
||||
type ExpressionIdentifier = PropertyIdentifier | MethodIdentifier;
|
||||
type TmplTarget = TmplAstReference | TmplAstVariable;
|
||||
type TargetIdentifier = ReferenceIdentifier | VariableIdentifier;
|
||||
type ExpressionIdentifier = PropertyIdentifier|MethodIdentifier;
|
||||
type TmplTarget = TmplAstReference|TmplAstVariable;
|
||||
type TargetIdentifier = ReferenceIdentifier|VariableIdentifier;
|
||||
type TargetIdentifierMap = Map<TmplTarget, TargetIdentifier>;
|
||||
|
||||
/**
|
||||
@ -62,7 +62,9 @@ class ExpressionVisitor extends RecursiveAstVisitor {
|
||||
return visitor.identifiers;
|
||||
}
|
||||
|
||||
visit(ast: AST) { ast.visit(this); }
|
||||
visit(ast: AST) {
|
||||
ast.visit(this);
|
||||
}
|
||||
|
||||
visitMethodCall(ast: MethodCall, context: {}) {
|
||||
this.visitIdentifier(ast, IdentifierKind.Method);
|
||||
@ -144,16 +146,22 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
|
||||
*
|
||||
* @param boundTemplate bound template target
|
||||
*/
|
||||
constructor(private boundTemplate: BoundTarget<ComponentMeta>) { super(); }
|
||||
constructor(private boundTemplate: BoundTarget<ComponentMeta>) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits a node in the template.
|
||||
*
|
||||
* @param node node to visit
|
||||
*/
|
||||
visit(node: HTMLNode) { node.visit(this); }
|
||||
visit(node: HTMLNode) {
|
||||
node.visit(this);
|
||||
}
|
||||
|
||||
visitAll(nodes: TmplAstNode[]) { nodes.forEach(node => this.visit(node)); }
|
||||
visitAll(nodes: TmplAstNode[]) {
|
||||
nodes.forEach(node => this.visit(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an identifier for an HTML element and visit its children recursively.
|
||||
@ -204,8 +212,12 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
|
||||
this.targetToIdentifier.bind(this));
|
||||
identifiers.forEach(id => this.identifiers.add(id));
|
||||
}
|
||||
visitBoundEvent(attribute: TmplAstBoundEvent) { this.visitExpression(attribute.handler); }
|
||||
visitBoundText(text: TmplAstBoundText) { this.visitExpression(text.value); }
|
||||
visitBoundEvent(attribute: TmplAstBoundEvent) {
|
||||
this.visitExpression(attribute.handler);
|
||||
}
|
||||
visitBoundText(text: TmplAstBoundText) {
|
||||
this.visitExpression(text.value);
|
||||
}
|
||||
visitReference(reference: TmplAstReference) {
|
||||
const referenceIdentifer = this.targetToIdentifier(reference);
|
||||
|
||||
@ -222,7 +234,7 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
|
||||
|TemplateNodeIdentifier {
|
||||
// If this node has already been seen, return the cached result.
|
||||
if (this.elementAndTemplateIdentifierCache.has(node)) {
|
||||
return this.elementAndTemplateIdentifierCache.get(node) !;
|
||||
return this.elementAndTemplateIdentifierCache.get(node)!;
|
||||
}
|
||||
|
||||
let name: string;
|
||||
@ -254,7 +266,8 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
|
||||
|
||||
const identifier = {
|
||||
name,
|
||||
span: absoluteSpan, kind,
|
||||
span: absoluteSpan,
|
||||
kind,
|
||||
attributes: new Set(attributes),
|
||||
usedDirectives: new Set(usedDirectives.map(dir => {
|
||||
return {
|
||||
@ -274,7 +287,7 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
|
||||
private targetToIdentifier(node: TmplAstReference|TmplAstVariable): TargetIdentifier {
|
||||
// If this node has already been seen, return the cached result.
|
||||
if (this.targetIdentifierCache.has(node)) {
|
||||
return this.targetIdentifierCache.get(node) !;
|
||||
return this.targetIdentifierCache.get(node)!;
|
||||
}
|
||||
|
||||
const {name, sourceSpan} = node;
|
||||
@ -304,7 +317,8 @@ class TemplateVisitor extends TmplAstRecursiveVisitor {
|
||||
identifier = {
|
||||
name,
|
||||
span,
|
||||
kind: IdentifierKind.Reference, target,
|
||||
kind: IdentifierKind.Reference,
|
||||
target,
|
||||
};
|
||||
} else {
|
||||
identifier = {
|
||||
|
@ -19,7 +19,8 @@ runInEachFileSystem(() => {
|
||||
|
||||
context.addComponent({
|
||||
declaration,
|
||||
selector: 'c-selector', boundTemplate,
|
||||
selector: 'c-selector',
|
||||
boundTemplate,
|
||||
templateMeta: {
|
||||
isInline: false,
|
||||
file: new ParseSourceFile('<div></div>', util.getTestFilePath()),
|
||||
@ -29,7 +30,8 @@ runInEachFileSystem(() => {
|
||||
expect(context.components).toEqual(new Set([
|
||||
{
|
||||
declaration,
|
||||
selector: 'c-selector', boundTemplate,
|
||||
selector: 'c-selector',
|
||||
boundTemplate,
|
||||
templateMeta: {
|
||||
isInline: false,
|
||||
file: new ParseSourceFile('<div></div>', util.getTestFilePath()),
|
||||
|
@ -219,11 +219,11 @@ runInEachFileSystem(() => {
|
||||
|
||||
const refArr = Array.from(refs);
|
||||
expect(refArr).toEqual(jasmine.arrayContaining([{
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Property,
|
||||
span: new AbsoluteSourceSpan(20, 23),
|
||||
target: null,
|
||||
}] as TopLevelIdentifier[]));
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Property,
|
||||
span: new AbsoluteSourceSpan(20, 23),
|
||||
target: null,
|
||||
}] as TopLevelIdentifier[]));
|
||||
});
|
||||
|
||||
it('should ignore property writes that are not implicitly received by the template', () => {
|
||||
@ -314,12 +314,13 @@ runInEachFileSystem(() => {
|
||||
};
|
||||
|
||||
const refArray = Array.from(refs);
|
||||
expect(refArray).toEqual(jasmine.arrayContaining([{
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Reference,
|
||||
span: new AbsoluteSourceSpan(6, 9),
|
||||
target: {node: elementReference, directive: null},
|
||||
}] as TopLevelIdentifier[]));
|
||||
expect(refArray).toEqual(
|
||||
jasmine.arrayContaining([{
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Reference,
|
||||
span: new AbsoluteSourceSpan(6, 9),
|
||||
target: {node: elementReference, directive: null},
|
||||
}] as TopLevelIdentifier[]));
|
||||
});
|
||||
|
||||
it('should discover nested references', () => {
|
||||
@ -334,12 +335,13 @@ runInEachFileSystem(() => {
|
||||
};
|
||||
|
||||
const refArray = Array.from(refs);
|
||||
expect(refArray).toEqual(jasmine.arrayContaining([{
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Reference,
|
||||
span: new AbsoluteSourceSpan(12, 15),
|
||||
target: {node: elementReference, directive: null},
|
||||
}] as TopLevelIdentifier[]));
|
||||
expect(refArray).toEqual(
|
||||
jasmine.arrayContaining([{
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Reference,
|
||||
span: new AbsoluteSourceSpan(12, 15),
|
||||
target: {node: elementReference, directive: null},
|
||||
}] as TopLevelIdentifier[]));
|
||||
});
|
||||
|
||||
it('should discover references to references', () => {
|
||||
@ -409,14 +411,14 @@ runInEachFileSystem(() => {
|
||||
const refArr = Array.from(refs);
|
||||
let fooRef = refArr.find(id => id.name === 'foo');
|
||||
expect(fooRef).toBeDefined();
|
||||
expect(fooRef !.kind).toBe(IdentifierKind.Reference);
|
||||
expect(fooRef!.kind).toBe(IdentifierKind.Reference);
|
||||
|
||||
fooRef = fooRef as ReferenceIdentifier;
|
||||
expect(fooRef.target).toBeDefined();
|
||||
expect(fooRef.target !.node.kind).toBe(IdentifierKind.Element);
|
||||
expect(fooRef.target !.node.name).toBe('div');
|
||||
expect(fooRef.target !.node.span).toEqual(new AbsoluteSourceSpan(1, 4));
|
||||
expect(fooRef.target !.directive).toEqual(declB);
|
||||
expect(fooRef.target!.node.kind).toBe(IdentifierKind.Element);
|
||||
expect(fooRef.target!.node.name).toBe('div');
|
||||
expect(fooRef.target!.node.span).toEqual(new AbsoluteSourceSpan(1, 4));
|
||||
expect(fooRef.target!.directive).toEqual(declB);
|
||||
});
|
||||
|
||||
it('should discover references to references', () => {
|
||||
@ -455,10 +457,10 @@ runInEachFileSystem(() => {
|
||||
|
||||
const refArray = Array.from(refs);
|
||||
expect(refArray).toEqual(jasmine.arrayContaining([{
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Variable,
|
||||
span: new AbsoluteSourceSpan(17, 20),
|
||||
}] as TopLevelIdentifier[]));
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Variable,
|
||||
span: new AbsoluteSourceSpan(17, 20),
|
||||
}] as TopLevelIdentifier[]));
|
||||
});
|
||||
|
||||
it('should discover variables with let- syntax', () => {
|
||||
@ -467,10 +469,10 @@ runInEachFileSystem(() => {
|
||||
|
||||
const refArray = Array.from(refs);
|
||||
expect(refArray).toEqual(jasmine.arrayContaining([{
|
||||
name: 'var',
|
||||
kind: IdentifierKind.Variable,
|
||||
span: new AbsoluteSourceSpan(17, 20),
|
||||
}] as TopLevelIdentifier[]));
|
||||
name: 'var',
|
||||
kind: IdentifierKind.Variable,
|
||||
span: new AbsoluteSourceSpan(17, 20),
|
||||
}] as TopLevelIdentifier[]));
|
||||
});
|
||||
|
||||
it('should discover nested variables', () => {
|
||||
@ -479,10 +481,10 @@ runInEachFileSystem(() => {
|
||||
|
||||
const refArray = Array.from(refs);
|
||||
expect(refArray).toEqual(jasmine.arrayContaining([{
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Variable,
|
||||
span: new AbsoluteSourceSpan(23, 26),
|
||||
}] as TopLevelIdentifier[]));
|
||||
name: 'foo',
|
||||
kind: IdentifierKind.Variable,
|
||||
span: new AbsoluteSourceSpan(23, 26),
|
||||
}] as TopLevelIdentifier[]));
|
||||
});
|
||||
|
||||
it('should discover references to variables', () => {
|
||||
|
@ -68,7 +68,7 @@ runInEachFileSystem(() => {
|
||||
|
||||
const info = analysis.get(decl);
|
||||
expect(info).toBeDefined();
|
||||
expect(info !.template.file)
|
||||
expect(info!.template.file)
|
||||
.toEqual(new ParseSourceFile('class C {}', util.getTestFilePath()));
|
||||
});
|
||||
|
||||
@ -83,7 +83,7 @@ runInEachFileSystem(() => {
|
||||
|
||||
const info = analysis.get(decl);
|
||||
expect(info).toBeDefined();
|
||||
expect(info !.template.file)
|
||||
expect(info!.template.file)
|
||||
.toEqual(new ParseSourceFile('<div>{{foo}}</div>', util.getTestFilePath()));
|
||||
});
|
||||
|
||||
@ -110,11 +110,11 @@ runInEachFileSystem(() => {
|
||||
|
||||
const infoA = analysis.get(declA);
|
||||
expect(infoA).toBeDefined();
|
||||
expect(infoA !.template.usedComponents).toEqual(new Set([declB]));
|
||||
expect(infoA!.template.usedComponents).toEqual(new Set([declB]));
|
||||
|
||||
const infoB = analysis.get(declB);
|
||||
expect(infoB).toBeDefined();
|
||||
expect(infoB !.template.usedComponents).toEqual(new Set([declA]));
|
||||
expect(infoB!.template.usedComponents).toEqual(new Set([declA]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -6,9 +6,10 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {BoundTarget, CssSelector, ParseTemplateOptions, R3TargetBinder, SelectorMatcher, parseTemplate} from '@angular/compiler';
|
||||
import {BoundTarget, CssSelector, parseTemplate, ParseTemplateOptions, R3TargetBinder, SelectorMatcher} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
import {AbsoluteFsPath, absoluteFrom} from '../../file_system';
|
||||
|
||||
import {absoluteFrom, AbsoluteFsPath} from '../../file_system';
|
||||
import {Reference} from '../../imports';
|
||||
import {ClassDeclaration} from '../../reflection';
|
||||
import {getDeclaration, makeProgram} from '../../testing';
|
||||
|
@ -9,7 +9,7 @@
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Reference} from '../../imports';
|
||||
import {ClassDeclaration, ReflectionHost, isNamedClassDeclaration} from '../../reflection';
|
||||
import {ClassDeclaration, isNamedClassDeclaration, ReflectionHost} from '../../reflection';
|
||||
|
||||
import {DirectiveMeta, MetadataReader, NgModuleMeta, PipeMeta} from './api';
|
||||
import {extractDirectiveGuards, extractReferencesFromType, readStringArrayType, readStringMapType, readStringType} from './util';
|
||||
|
@ -25,7 +25,7 @@ export function flattenInheritedDirectiveMetadata(
|
||||
throw new Error(`Metadata not found for directive: ${dir.debugName}`);
|
||||
}
|
||||
|
||||
let inputs: {[key: string]: string | [string, string]} = {};
|
||||
let inputs: {[key: string]: string|[string, string]} = {};
|
||||
let outputs: {[key: string]: string} = {};
|
||||
let coercedInputFields = new Set<string>();
|
||||
let isDynamic = false;
|
||||
|
@ -22,18 +22,24 @@ export class LocalMetadataRegistry implements MetadataRegistry, MetadataReader {
|
||||
private pipes = new Map<ClassDeclaration, PipeMeta>();
|
||||
|
||||
getDirectiveMetadata(ref: Reference<ClassDeclaration>): DirectiveMeta|null {
|
||||
return this.directives.has(ref.node) ? this.directives.get(ref.node) ! : null;
|
||||
return this.directives.has(ref.node) ? this.directives.get(ref.node)! : null;
|
||||
}
|
||||
getNgModuleMetadata(ref: Reference<ClassDeclaration>): NgModuleMeta|null {
|
||||
return this.ngModules.has(ref.node) ? this.ngModules.get(ref.node) ! : null;
|
||||
return this.ngModules.has(ref.node) ? this.ngModules.get(ref.node)! : null;
|
||||
}
|
||||
getPipeMetadata(ref: Reference<ClassDeclaration>): PipeMeta|null {
|
||||
return this.pipes.has(ref.node) ? this.pipes.get(ref.node) ! : null;
|
||||
return this.pipes.has(ref.node) ? this.pipes.get(ref.node)! : null;
|
||||
}
|
||||
|
||||
registerDirectiveMetadata(meta: DirectiveMeta): void { this.directives.set(meta.ref.node, meta); }
|
||||
registerNgModuleMetadata(meta: NgModuleMeta): void { this.ngModules.set(meta.ref.node, meta); }
|
||||
registerPipeMetadata(meta: PipeMeta): void { this.pipes.set(meta.ref.node, meta); }
|
||||
registerDirectiveMetadata(meta: DirectiveMeta): void {
|
||||
this.directives.set(meta.ref.node, meta);
|
||||
}
|
||||
registerNgModuleMetadata(meta: NgModuleMeta): void {
|
||||
this.ngModules.set(meta.ref.node, meta);
|
||||
}
|
||||
registerPipeMetadata(meta: PipeMeta): void {
|
||||
this.pipes.set(meta.ref.node, meta);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,7 +76,9 @@ export class InjectableClassRegistry {
|
||||
|
||||
constructor(private host: ReflectionHost) {}
|
||||
|
||||
registerInjectable(declaration: ClassDeclaration): void { this.classes.add(declaration); }
|
||||
registerInjectable(declaration: ClassDeclaration): void {
|
||||
this.classes.add(declaration);
|
||||
}
|
||||
|
||||
isInjectable(declaration: ClassDeclaration): boolean {
|
||||
// Figure out whether the class is injectable based on the registered classes, otherwise
|
||||
|
@ -9,13 +9,13 @@
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {Reference} from '../../imports';
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, ReflectionHost, isNamedClassDeclaration, reflectTypeEntityToDeclaration} from '../../reflection';
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, isNamedClassDeclaration, ReflectionHost, reflectTypeEntityToDeclaration} from '../../reflection';
|
||||
import {nodeDebugInfo} from '../../util/src/typescript';
|
||||
|
||||
import {DirectiveMeta, MetadataReader, NgModuleMeta, PipeMeta, TemplateGuardMeta} from './api';
|
||||
|
||||
export function extractReferencesFromType(
|
||||
checker: ts.TypeChecker, def: ts.TypeNode, ngModuleImportedFrom: string | null,
|
||||
checker: ts.TypeChecker, def: ts.TypeNode, ngModuleImportedFrom: string|null,
|
||||
resolutionContext: string): Reference<ClassDeclaration>[] {
|
||||
if (!ts.isTupleTypeNode(def)) {
|
||||
return [];
|
||||
@ -122,7 +122,7 @@ function extractTemplateGuard(member: ClassMember): TemplateGuardMeta|null {
|
||||
|
||||
function extractCoercedInput(member: ClassMember): string|null {
|
||||
if (member.kind !== ClassMemberKind.Property || !member.name.startsWith('ngAcceptInputType_')) {
|
||||
return null !;
|
||||
return null!;
|
||||
}
|
||||
return afterUnderscore(member.name);
|
||||
}
|
||||
|
@ -13,7 +13,9 @@ import {ImportFlags, Reference, ReferenceEmitter} from '../../imports';
|
||||
import {PartialEvaluator, ResolvedValueMap} from '../../partial_evaluator';
|
||||
import {ReflectionHost} from '../../reflection';
|
||||
|
||||
export interface DtsHandler { addTypeReplacement(node: ts.Declaration, type: Type): void; }
|
||||
export interface DtsHandler {
|
||||
addTypeReplacement(node: ts.Declaration, type: Type): void;
|
||||
}
|
||||
|
||||
export class ModuleWithProvidersScanner {
|
||||
constructor(
|
||||
|
@ -12,9 +12,11 @@ import {PerfRecorder} from './api';
|
||||
|
||||
export const NOOP_PERF_RECORDER: PerfRecorder = {
|
||||
enabled: false,
|
||||
mark: (name: string, node: ts.SourceFile | ts.Declaration, category?: string, detail?: string):
|
||||
void => {},
|
||||
start: (name: string, node: ts.SourceFile | ts.Declaration, category?: string, detail?: string):
|
||||
number => { return 0;},
|
||||
stop: (span: number | false): void => {},
|
||||
mark: (name: string, node: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
|
||||
void => {},
|
||||
start: (name: string, node: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
|
||||
number => {
|
||||
return 0;
|
||||
},
|
||||
stop: (span: number|false): void => {},
|
||||
};
|
||||
|
@ -20,7 +20,9 @@ export class PerfTracker implements PerfRecorder {
|
||||
|
||||
private constructor(private zeroTime: HrTime) {}
|
||||
|
||||
static zeroedToNow(): PerfTracker { return new PerfTracker(mark()); }
|
||||
static zeroedToNow(): PerfTracker {
|
||||
return new PerfTracker(mark());
|
||||
}
|
||||
|
||||
mark(name: string, node?: ts.SourceFile|ts.Declaration, category?: string, detail?: string):
|
||||
void {
|
||||
@ -73,7 +75,9 @@ export class PerfTracker implements PerfRecorder {
|
||||
return msg;
|
||||
}
|
||||
|
||||
asJson(): unknown { return this.log; }
|
||||
asJson(): unknown {
|
||||
return this.log;
|
||||
}
|
||||
|
||||
serializeToFile(target: string, host: ts.CompilerHost): void {
|
||||
const json = JSON.stringify(this.log, null, 2);
|
||||
|
@ -77,7 +77,9 @@ export class NgtscProgram implements api.Program {
|
||||
new NgCompiler(this.host, options, this.tsProgram, reuseProgram, this.perfRecorder);
|
||||
}
|
||||
|
||||
getTsProgram(): ts.Program { return this.tsProgram; }
|
||||
getTsProgram(): ts.Program {
|
||||
return this.tsProgram;
|
||||
}
|
||||
|
||||
getTsOptionDiagnostics(cancellationToken?: ts.CancellationToken|
|
||||
undefined): readonly ts.Diagnostic[] {
|
||||
@ -137,8 +139,8 @@ export class NgtscProgram implements api.Program {
|
||||
}
|
||||
|
||||
getNgSemanticDiagnostics(
|
||||
fileName?: string|undefined, cancellationToken?: ts.CancellationToken|
|
||||
undefined): readonly(ts.Diagnostic|api.Diagnostic)[] {
|
||||
fileName?: string|undefined, cancellationToken?: ts.CancellationToken|undefined):
|
||||
readonly(ts.Diagnostic|api.Diagnostic)[] {
|
||||
let sf: ts.SourceFile|undefined = undefined;
|
||||
if (fileName !== undefined) {
|
||||
sf = this.tsProgram.getSourceFile(fileName);
|
||||
@ -161,14 +163,17 @@ export class NgtscProgram implements api.Program {
|
||||
* This is used by the Angular CLI to allow for spawning (async) child compilations for things
|
||||
* like SASS files used in `styleUrls`.
|
||||
*/
|
||||
loadNgStructureAsync(): Promise<void> { return this.compiler.analyzeAsync(); }
|
||||
loadNgStructureAsync(): Promise<void> {
|
||||
return this.compiler.analyzeAsync();
|
||||
}
|
||||
|
||||
listLazyRoutes(entryRoute?: string|undefined): api.LazyRoute[] {
|
||||
return this.compiler.listLazyRoutes(entryRoute);
|
||||
}
|
||||
|
||||
emit(opts?: {
|
||||
emitFlags?: api.EmitFlags | undefined; cancellationToken?: ts.CancellationToken | undefined;
|
||||
emitFlags?: api.EmitFlags|undefined;
|
||||
cancellationToken?: ts.CancellationToken | undefined;
|
||||
customTransformers?: api.CustomTransformers | undefined;
|
||||
emitCallback?: api.TsEmitCallback | undefined;
|
||||
mergeEmitResultsCallback?: api.TsMergeEmitResultsCallback | undefined;
|
||||
@ -179,8 +184,8 @@ export class NgtscProgram implements api.Program {
|
||||
|
||||
const writeFile: ts.WriteFileCallback =
|
||||
(fileName: string, data: string, writeByteOrderMark: boolean,
|
||||
onError: ((message: string) => void) | undefined,
|
||||
sourceFiles: ReadonlyArray<ts.SourceFile>| undefined) => {
|
||||
onError: ((message: string) => void)|undefined,
|
||||
sourceFiles: ReadonlyArray<ts.SourceFile>|undefined) => {
|
||||
if (sourceFiles !== undefined) {
|
||||
// Record successful writes for any `ts.SourceFile` (that's not a declaration file)
|
||||
// that's an input to this write.
|
||||
@ -221,7 +226,8 @@ export class NgtscProgram implements api.Program {
|
||||
program: this.tsProgram,
|
||||
host: this.host,
|
||||
options: this.options,
|
||||
emitOnlyDtsFiles: false, writeFile,
|
||||
emitOnlyDtsFiles: false,
|
||||
writeFile,
|
||||
customTransformers: {
|
||||
before: beforeTransforms,
|
||||
after: customTransforms && customTransforms.afterTs,
|
||||
@ -257,11 +263,16 @@ export class NgtscProgram implements api.Program {
|
||||
}
|
||||
}
|
||||
|
||||
const defaultEmitCallback: api.TsEmitCallback =
|
||||
({program, targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles,
|
||||
customTransformers}) =>
|
||||
program.emit(
|
||||
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
||||
const defaultEmitCallback: api.TsEmitCallback = ({
|
||||
program,
|
||||
targetSourceFile,
|
||||
writeFile,
|
||||
cancellationToken,
|
||||
emitOnlyDtsFiles,
|
||||
customTransformers
|
||||
}) =>
|
||||
program.emit(
|
||||
targetSourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers);
|
||||
|
||||
function mergeEmitResults(emitResults: ts.EmitResult[]): ts.EmitResult {
|
||||
const diagnostics: ts.Diagnostic[] = [];
|
||||
|
@ -12,7 +12,7 @@ import * as ts from 'typescript';
|
||||
* Metadata extracted from an instance of a decorator on another declaration, or synthesized from
|
||||
* other information about a class.
|
||||
*/
|
||||
export type Decorator = ConcreteDecorator | SyntheticDecorator;
|
||||
export type Decorator = ConcreteDecorator|SyntheticDecorator;
|
||||
|
||||
export interface BaseDecorator {
|
||||
/**
|
||||
@ -28,11 +28,11 @@ export interface BaseDecorator {
|
||||
*/
|
||||
identifier: DecoratorIdentifier|null;
|
||||
|
||||
/**
|
||||
* `Import` by which the decorator was brought into the module in which it was invoked, or `null`
|
||||
* if the decorator was declared in the same module and not imported.
|
||||
*/
|
||||
import : Import | null;
|
||||
/**
|
||||
* `Import` by which the decorator was brought into the module in which it was invoked, or `null`
|
||||
* if the decorator was declared in the same module and not imported.
|
||||
*/
|
||||
import: Import|null;
|
||||
|
||||
/**
|
||||
* TypeScript reference to the decorator itself, or `null` if the decorator is synthesized (e.g.
|
||||
@ -87,8 +87,8 @@ export const Decorator = {
|
||||
* A decorator is identified by either a simple identifier (e.g. `Decorator`) or, in some cases,
|
||||
* a namespaced property access (e.g. `core.Decorator`).
|
||||
*/
|
||||
export type DecoratorIdentifier = ts.Identifier | NamespacedIdentifier;
|
||||
export type NamespacedIdentifier = ts.PropertyAccessExpression & {
|
||||
export type DecoratorIdentifier = ts.Identifier|NamespacedIdentifier;
|
||||
export type NamespacedIdentifier = ts.PropertyAccessExpression&{
|
||||
expression: ts.Identifier;
|
||||
name: ts.Identifier
|
||||
};
|
||||
@ -113,7 +113,7 @@ export function isDecoratorIdentifier(exp: ts.Expression): exp is DecoratorIdent
|
||||
* For `ReflectionHost` purposes, a class declaration should always have a `name` identifier,
|
||||
* because we need to be able to reference it in other parts of the program.
|
||||
*/
|
||||
export type ClassDeclaration<T extends ts.Declaration = ts.Declaration> = T & {name: ts.Identifier};
|
||||
export type ClassDeclaration<T extends ts.Declaration = ts.Declaration> = T&{name: ts.Identifier};
|
||||
|
||||
/**
|
||||
* An enumeration of possible kinds of class members.
|
||||
@ -241,8 +241,7 @@ export interface ClassMember {
|
||||
*/
|
||||
export type TypeValueReference = {
|
||||
local: true; expression: ts.Expression; defaultImportStatement: ts.ImportDeclaration | null;
|
||||
} |
|
||||
{
|
||||
}|{
|
||||
local: false;
|
||||
name: string;
|
||||
moduleName: string;
|
||||
@ -447,7 +446,7 @@ export interface InlineDeclaration extends BaseDeclaration {
|
||||
* downlevelings to a `ts.Expression` instead.
|
||||
*/
|
||||
export type Declaration<T extends ts.Declaration = ts.Declaration> =
|
||||
ConcreteDeclaration<T>| InlineDeclaration;
|
||||
ConcreteDeclaration<T>|InlineDeclaration;
|
||||
|
||||
/**
|
||||
* Abstracts reflection operations on a TypeScript AST.
|
||||
|
@ -18,7 +18,7 @@ import {TypeValueReference} from './host';
|
||||
* declaration, or if it is not possible to statically understand.
|
||||
*/
|
||||
export function typeToValue(
|
||||
typeNode: ts.TypeNode | null, checker: ts.TypeChecker): TypeValueReference|null {
|
||||
typeNode: ts.TypeNode|null, checker: ts.TypeChecker): TypeValueReference|null {
|
||||
// It's not possible to get a value expression if the parameter doesn't even have a type.
|
||||
if (typeNode === null || !ts.isTypeReferenceNode(typeNode)) {
|
||||
return null;
|
||||
@ -95,7 +95,7 @@ export function typeNodeToValueExpr(node: ts.TypeNode): ts.Expression|null {
|
||||
* give the identifier name within the current file by which the import is known.
|
||||
*/
|
||||
function resolveTypeSymbols(typeRef: ts.TypeReferenceNode, checker: ts.TypeChecker):
|
||||
{local: ts.Symbol, decl: ts.Symbol, importName: string | null}|null {
|
||||
{local: ts.Symbol, decl: ts.Symbol, importName: string|null}|null {
|
||||
const typeName = typeRef.typeName;
|
||||
// typeRefSymbol is the ts.Symbol of the entire type reference.
|
||||
const typeRefSymbol: ts.Symbol|undefined = checker.getSymbolAtLocation(typeName);
|
||||
@ -156,8 +156,8 @@ function isImportSource(node: ts.Declaration): node is(ts.ImportSpecifier | ts.N
|
||||
}
|
||||
|
||||
function extractModuleAndNameFromImport(
|
||||
node: ts.ImportSpecifier | ts.NamespaceImport | ts.ImportClause,
|
||||
localName: string | null): {name: string, moduleName: string} {
|
||||
node: ts.ImportSpecifier|ts.NamespaceImport|ts.ImportClause,
|
||||
localName: string|null): {name: string, moduleName: string} {
|
||||
let name: string;
|
||||
let moduleSpecifier: ts.Expression;
|
||||
switch (node.kind) {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, ReflectionHost, isDecoratorIdentifier} from './host';
|
||||
import {ClassDeclaration, ClassMember, ClassMemberKind, CtorParameter, Declaration, Decorator, FunctionDefinition, Import, isDecoratorIdentifier, ReflectionHost} from './host';
|
||||
import {typeToValue} from './type_to_value';
|
||||
|
||||
/**
|
||||
@ -76,8 +76,10 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
||||
|
||||
return {
|
||||
name,
|
||||
nameNode: node.name, typeValueReference,
|
||||
typeNode: originalTypeNode, decorators,
|
||||
nameNode: node.name,
|
||||
typeValueReference,
|
||||
typeNode: originalTypeNode,
|
||||
decorators,
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -183,11 +185,17 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
||||
return declaration.initializer || null;
|
||||
}
|
||||
|
||||
getDtsDeclaration(_: ts.Declaration): ts.Declaration|null { return null; }
|
||||
getDtsDeclaration(_: ts.Declaration): ts.Declaration|null {
|
||||
return null;
|
||||
}
|
||||
|
||||
getInternalNameOfClass(clazz: ClassDeclaration): ts.Identifier { return clazz.name; }
|
||||
getInternalNameOfClass(clazz: ClassDeclaration): ts.Identifier {
|
||||
return clazz.name;
|
||||
}
|
||||
|
||||
getAdjacentNameOfClass(clazz: ClassDeclaration): ts.Identifier { return clazz.name; }
|
||||
getAdjacentNameOfClass(clazz: ClassDeclaration): ts.Identifier {
|
||||
return clazz.name;
|
||||
}
|
||||
|
||||
protected getDirectImportOfIdentifier(id: ts.Identifier): Import|null {
|
||||
const symbol = this.checker.getSymbolAtLocation(id);
|
||||
@ -305,12 +313,14 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
||||
if (symbol.valueDeclaration !== undefined) {
|
||||
return {
|
||||
node: symbol.valueDeclaration,
|
||||
known: null, viaModule,
|
||||
known: null,
|
||||
viaModule,
|
||||
};
|
||||
} else if (symbol.declarations !== undefined && symbol.declarations.length > 0) {
|
||||
return {
|
||||
node: symbol.declarations[0],
|
||||
known: null, viaModule,
|
||||
known: null,
|
||||
viaModule,
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
@ -342,7 +352,9 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
||||
return {
|
||||
name: decoratorIdentifier.text,
|
||||
identifier: decoratorExpr,
|
||||
import: importDecl, node, args,
|
||||
import: importDecl,
|
||||
node,
|
||||
args,
|
||||
};
|
||||
}
|
||||
|
||||
@ -382,8 +394,14 @@ export class TypeScriptReflectionHost implements ReflectionHost {
|
||||
|
||||
return {
|
||||
node,
|
||||
implementation: node, kind,
|
||||
type: node.type || null, name, nameNode, decorators, value, isStatic,
|
||||
implementation: node,
|
||||
kind,
|
||||
type: node.type || null,
|
||||
name,
|
||||
nameNode,
|
||||
decorators,
|
||||
value,
|
||||
isStatic,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -405,7 +423,7 @@ export function reflectIdentifierOfDeclaration(decl: ts.Declaration): ts.Identif
|
||||
}
|
||||
|
||||
export function reflectTypeEntityToDeclaration(
|
||||
type: ts.EntityName, checker: ts.TypeChecker): {node: ts.Declaration, from: string | null} {
|
||||
type: ts.EntityName, checker: ts.TypeChecker): {node: ts.Declaration, from: string|null} {
|
||||
let realSymbol = checker.getSymbolAtLocation(type);
|
||||
if (realSymbol === undefined) {
|
||||
throw new Error(`Cannot resolve type entity ${type.getText()} to symbol`);
|
||||
@ -434,8 +452,8 @@ export function reflectTypeEntityToDeclaration(
|
||||
}
|
||||
const decl = symbol.declarations[0];
|
||||
if (ts.isNamespaceImport(decl)) {
|
||||
const clause = decl.parent !;
|
||||
const importDecl = clause.parent !;
|
||||
const clause = decl.parent!;
|
||||
const importDecl = clause.parent!;
|
||||
if (!ts.isStringLiteral(importDecl.moduleSpecifier)) {
|
||||
throw new Error(`Module specifier is not a string`);
|
||||
}
|
||||
@ -552,7 +570,7 @@ function getFarLeftIdentifier(propertyAccess: ts.PropertyAccessExpression): ts.I
|
||||
* `NamespaceImport`. If not return `null`.
|
||||
*/
|
||||
function getContainingImportDeclaration(node: ts.Node): ts.ImportDeclaration|null {
|
||||
return ts.isImportSpecifier(node) ? node.parent !.parent !.parent ! :
|
||||
return ts.isImportSpecifier(node) ? node.parent!.parent!.parent! :
|
||||
ts.isNamespaceImport(node) ? node.parent.parent : null;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ runInEachFileSystem(() => {
|
||||
const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration);
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const args = host.getConstructorParameters(clazz) !;
|
||||
const args = host.getConstructorParameters(clazz)!;
|
||||
expect(args.length).toBe(1);
|
||||
expectParameter(args[0], 'bar', 'Bar');
|
||||
});
|
||||
@ -63,7 +63,7 @@ runInEachFileSystem(() => {
|
||||
const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration);
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const args = host.getConstructorParameters(clazz) !;
|
||||
const args = host.getConstructorParameters(clazz)!;
|
||||
expect(args.length).toBe(1);
|
||||
expectParameter(args[0], 'bar', 'Bar', 'dec', './dec');
|
||||
});
|
||||
@ -92,7 +92,7 @@ runInEachFileSystem(() => {
|
||||
const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration);
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const args = host.getConstructorParameters(clazz) !;
|
||||
const args = host.getConstructorParameters(clazz)!;
|
||||
expect(args.length).toBe(1);
|
||||
expectParameter(args[0], 'bar', 'Bar', 'dec', './dec');
|
||||
});
|
||||
@ -120,7 +120,7 @@ runInEachFileSystem(() => {
|
||||
const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration);
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const args = host.getConstructorParameters(clazz) !;
|
||||
const args = host.getConstructorParameters(clazz)!;
|
||||
expect(args.length).toBe(2);
|
||||
expectParameter(args[0], 'bar', {moduleName: './bar', name: 'Bar'});
|
||||
expectParameter(args[1], 'otherBar', {moduleName: './bar', name: 'Bar'});
|
||||
@ -148,7 +148,7 @@ runInEachFileSystem(() => {
|
||||
const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration);
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const args = host.getConstructorParameters(clazz) !;
|
||||
const args = host.getConstructorParameters(clazz)!;
|
||||
expect(args.length).toBe(1);
|
||||
expectParameter(args[0], 'bar', {moduleName: './bar', name: 'Bar'});
|
||||
});
|
||||
@ -175,7 +175,7 @@ runInEachFileSystem(() => {
|
||||
const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration);
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const args = host.getConstructorParameters(clazz) !;
|
||||
const args = host.getConstructorParameters(clazz)!;
|
||||
expect(args.length).toBe(1);
|
||||
const param = args[0].typeValueReference;
|
||||
if (param === null || !param.local) {
|
||||
@ -207,7 +207,7 @@ runInEachFileSystem(() => {
|
||||
const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration);
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const args = host.getConstructorParameters(clazz) !;
|
||||
const args = host.getConstructorParameters(clazz)!;
|
||||
expect(args.length).toBe(1);
|
||||
expectParameter(args[0], 'bar', {moduleName: './bar', name: 'Bar'});
|
||||
});
|
||||
@ -228,12 +228,11 @@ runInEachFileSystem(() => {
|
||||
const clazz = getDeclaration(program, _('/entry.ts'), 'Foo', isNamedClassDeclaration);
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const args = host.getConstructorParameters(clazz) !;
|
||||
const args = host.getConstructorParameters(clazz)!;
|
||||
expect(args.length).toBe(2);
|
||||
expectParameter(args[0], 'bar', 'Bar');
|
||||
expectParameter(args[1], 'baz', 'Baz');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
@ -330,9 +329,9 @@ runInEachFileSystem(() => {
|
||||
} else if (directTargetDecl === null) {
|
||||
return fail('No declaration found for DirectTarget');
|
||||
}
|
||||
expect(targetDecl.node !.getSourceFile().fileName)
|
||||
expect(targetDecl.node!.getSourceFile().fileName)
|
||||
.toBe(_('/node_modules/absolute/index.ts'));
|
||||
expect(ts.isClassDeclaration(targetDecl.node !)).toBe(true);
|
||||
expect(ts.isClassDeclaration(targetDecl.node!)).toBe(true);
|
||||
expect(directTargetDecl.viaModule).toBe('absolute');
|
||||
expect(directTargetDecl.node).toBe(targetDecl.node);
|
||||
});
|
||||
@ -415,9 +414,9 @@ runInEachFileSystem(() => {
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const exportedDeclarations =
|
||||
host.getExportsOfModule(program.getSourceFile(_('/entry.ts')) !);
|
||||
expect(Array.from(exportedDeclarations !.keys())).toEqual(['foo', 'x', 'T', 'I', 'E']);
|
||||
expect(Array.from(exportedDeclarations !.values()).map(v => v.viaModule)).toEqual([
|
||||
host.getExportsOfModule(program.getSourceFile(_('/entry.ts'))!);
|
||||
expect(Array.from(exportedDeclarations!.keys())).toEqual(['foo', 'x', 'T', 'I', 'E']);
|
||||
expect(Array.from(exportedDeclarations!.values()).map(v => v.viaModule)).toEqual([
|
||||
null, null, null, null, null
|
||||
]);
|
||||
});
|
||||
@ -440,11 +439,11 @@ runInEachFileSystem(() => {
|
||||
const checker = program.getTypeChecker();
|
||||
const host = new TypeScriptReflectionHost(checker);
|
||||
const exportedDeclarations =
|
||||
host.getExportsOfModule(program.getSourceFile(_('/entry.ts')) !);
|
||||
expect(Array.from(exportedDeclarations !.keys())).toEqual([
|
||||
host.getExportsOfModule(program.getSourceFile(_('/entry.ts'))!);
|
||||
expect(Array.from(exportedDeclarations!.keys())).toEqual([
|
||||
'Target1', 'AliasTarget', 'AliasTarget2', 'Target'
|
||||
]);
|
||||
expect(Array.from(exportedDeclarations !.values()).map(v => v.viaModule)).toEqual([
|
||||
expect(Array.from(exportedDeclarations!.values()).map(v => v.viaModule)).toEqual([
|
||||
null, null, null, null
|
||||
]);
|
||||
});
|
||||
@ -452,9 +451,9 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
|
||||
function expectParameter(
|
||||
param: CtorParameter, name: string, type?: string | {name: string, moduleName: string},
|
||||
param: CtorParameter, name: string, type?: string|{name: string, moduleName: string},
|
||||
decorator?: string, decoratorFrom?: string): void {
|
||||
expect(param.name !).toEqual(name);
|
||||
expect(param.name!).toEqual(name);
|
||||
if (type === undefined) {
|
||||
expect(param.typeValueReference).toBeNull();
|
||||
} else {
|
||||
@ -467,21 +466,21 @@ runInEachFileSystem(() => {
|
||||
expect(param.typeValueReference.moduleName).toEqual(type.moduleName);
|
||||
expect(param.typeValueReference.name).toEqual(type.name);
|
||||
} else {
|
||||
return fail(
|
||||
`Mismatch between typeValueReference and expected type: ${param.name} / ${param.typeValueReference.local}`);
|
||||
return fail(`Mismatch between typeValueReference and expected type: ${param.name} / ${
|
||||
param.typeValueReference.local}`);
|
||||
}
|
||||
}
|
||||
if (decorator !== undefined) {
|
||||
expect(param.decorators).not.toBeNull();
|
||||
expect(param.decorators !.length).toBeGreaterThan(0);
|
||||
expect(param.decorators !.some(
|
||||
expect(param.decorators!.length).toBeGreaterThan(0);
|
||||
expect(param.decorators!.some(
|
||||
dec => dec.name === decorator && dec.import !== null &&
|
||||
dec.import.from === decoratorFrom))
|
||||
.toBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
function argExpressionToString(name: ts.Node | null): string {
|
||||
function argExpressionToString(name: ts.Node|null): string {
|
||||
if (name == null) {
|
||||
throw new Error('\'name\' argument can\'t be null');
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import * as ts from 'typescript';
|
||||
|
||||
import {ResourceLoader} from '../../annotations';
|
||||
import {ExtendedTsCompilerHost} from '../../core/api';
|
||||
import {AbsoluteFsPath, PathSegment, join} from '../../file_system';
|
||||
import {AbsoluteFsPath, join, PathSegment} from '../../file_system';
|
||||
import {getRootDirs} from '../../util/src/typescript';
|
||||
|
||||
const CSS_PREPROCESSOR_EXT = /(\.scss|\.sass|\.less|\.styl)$/;
|
||||
@ -101,7 +101,7 @@ export class HostResourceLoader implements ResourceLoader {
|
||||
*/
|
||||
load(resolvedUrl: string): string {
|
||||
if (this.cache.has(resolvedUrl)) {
|
||||
return this.cache.get(resolvedUrl) !;
|
||||
return this.cache.get(resolvedUrl)!;
|
||||
}
|
||||
|
||||
const result = this.host.readResource ? this.host.readResource(resolvedUrl) :
|
||||
@ -169,14 +169,15 @@ export class HostResourceLoader implements ResourceLoader {
|
||||
// but is marked @internal in TypeScript. See
|
||||
// https://github.com/Microsoft/TypeScript/issues/28770.
|
||||
type ResolvedModuleWithFailedLookupLocations =
|
||||
ts.ResolvedModuleWithFailedLookupLocations & {failedLookupLocations: ReadonlyArray<string>};
|
||||
ts.ResolvedModuleWithFailedLookupLocations&{failedLookupLocations: ReadonlyArray<string>};
|
||||
|
||||
// clang-format off
|
||||
const failedLookup = ts.resolveModuleName(url + '.$ngresource$', fromFile, this.options, this.host) as ResolvedModuleWithFailedLookupLocations;
|
||||
// clang-format on
|
||||
if (failedLookup.failedLookupLocations === undefined) {
|
||||
throw new Error(
|
||||
`Internal error: expected to find failedLookupLocations during resolution of resource '${url}' in context of ${fromFile}`);
|
||||
`Internal error: expected to find failedLookupLocations during resolution of resource '${
|
||||
url}' in context of ${fromFile}`);
|
||||
}
|
||||
|
||||
return failedLookup.failedLookupLocations
|
||||
|
@ -12,7 +12,7 @@ import {ModuleResolver} from '../../imports';
|
||||
import {PartialEvaluator} from '../../partial_evaluator';
|
||||
|
||||
import {scanForCandidateTransitiveModules, scanForRouteEntryPoints} from './lazy';
|
||||
import {RouterEntryPointManager, entryPointKeyFor} from './route';
|
||||
import {entryPointKeyFor, RouterEntryPointManager} from './route';
|
||||
|
||||
export interface NgModuleRawRouteData {
|
||||
sourceFile: ts.SourceFile;
|
||||
@ -42,10 +42,13 @@ export class NgModuleRouteAnalyzer {
|
||||
if (this.modules.has(key)) {
|
||||
throw new Error(`Double route analyzing for '${key}'.`);
|
||||
}
|
||||
this.modules.set(
|
||||
key, {
|
||||
sourceFile, moduleName, imports, exports, providers,
|
||||
});
|
||||
this.modules.set(key, {
|
||||
sourceFile,
|
||||
moduleName,
|
||||
imports,
|
||||
exports,
|
||||
providers,
|
||||
});
|
||||
}
|
||||
|
||||
listLazyRoutes(entryModuleKey?: string|undefined): LazyRoute[] {
|
||||
@ -63,7 +66,7 @@ export class NgModuleRouteAnalyzer {
|
||||
const scanRecursively = entryModuleKey !== undefined;
|
||||
|
||||
while (pendingModuleKeys.length > 0) {
|
||||
const key = pendingModuleKeys.pop() !;
|
||||
const key = pendingModuleKeys.pop()!;
|
||||
|
||||
if (scannedModuleKeys.has(key)) {
|
||||
continue;
|
||||
@ -71,7 +74,7 @@ export class NgModuleRouteAnalyzer {
|
||||
scannedModuleKeys.add(key);
|
||||
}
|
||||
|
||||
const data = this.modules.get(key) !;
|
||||
const data = this.modules.get(key)!;
|
||||
const entryPoints = scanForRouteEntryPoints(
|
||||
data.sourceFile, data.moduleName, data, this.entryPointManager, this.evaluator);
|
||||
|
||||
|
@ -12,7 +12,7 @@ import {Reference} from '../../imports';
|
||||
import {ForeignFunctionResolver, PartialEvaluator, ResolvedValue} from '../../partial_evaluator';
|
||||
|
||||
import {NgModuleRawRouteData} from './analyzer';
|
||||
import {RouterEntryPoint, RouterEntryPointManager, entryPointKeyFor} from './route';
|
||||
import {entryPointKeyFor, RouterEntryPoint, RouterEntryPointManager} from './route';
|
||||
|
||||
const ROUTES_MARKER = '__ngRoutesMarker__';
|
||||
|
||||
@ -23,7 +23,7 @@ export interface LazyRouteEntry {
|
||||
}
|
||||
|
||||
export function scanForCandidateTransitiveModules(
|
||||
expr: ts.Expression | null, evaluator: PartialEvaluator): string[] {
|
||||
expr: ts.Expression|null, evaluator: PartialEvaluator): string[] {
|
||||
if (expr === null) {
|
||||
return [];
|
||||
}
|
||||
@ -38,7 +38,7 @@ export function scanForCandidateTransitiveModules(
|
||||
}
|
||||
} else if (entry instanceof Map) {
|
||||
if (entry.has('ngModule')) {
|
||||
recursivelyAddModules(entry.get('ngModule') !);
|
||||
recursivelyAddModules(entry.get('ngModule')!);
|
||||
}
|
||||
} else if ((entry instanceof Reference) && hasIdentifier(entry.node)) {
|
||||
const filePath = entry.node.getSourceFile().fileName;
|
||||
@ -70,7 +70,9 @@ export function scanForRouteEntryPoints(
|
||||
const resolvedTo = entryPointManager.resolveLoadChildrenIdentifier(loadChildren, ngModule);
|
||||
if (resolvedTo !== null) {
|
||||
routes.push({
|
||||
loadChildren, from, resolvedTo,
|
||||
loadChildren,
|
||||
from,
|
||||
resolvedTo,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -159,29 +161,27 @@ function scanForLazyRoutes(routes: ResolvedValue[]): string[] {
|
||||
const routerModuleFFR: ForeignFunctionResolver =
|
||||
function routerModuleFFR(
|
||||
ref: Reference<ts.FunctionDeclaration|ts.MethodDeclaration|ts.FunctionExpression>,
|
||||
args: ReadonlyArray<ts.Expression>): ts.Expression |
|
||||
null {
|
||||
if (!isMethodNodeReference(ref) || !ts.isClassDeclaration(ref.node.parent)) {
|
||||
return null;
|
||||
} else if (
|
||||
ref.bestGuessOwningModule === null ||
|
||||
ref.bestGuessOwningModule.specifier !== '@angular/router') {
|
||||
return null;
|
||||
} else if (
|
||||
ref.node.parent.name === undefined || ref.node.parent.name.text !== 'RouterModule') {
|
||||
return null;
|
||||
} else if (
|
||||
!ts.isIdentifier(ref.node.name) ||
|
||||
(ref.node.name.text !== 'forRoot' && ref.node.name.text !== 'forChild')) {
|
||||
return null;
|
||||
}
|
||||
args: ReadonlyArray<ts.Expression>): ts.Expression|null {
|
||||
if (!isMethodNodeReference(ref) || !ts.isClassDeclaration(ref.node.parent)) {
|
||||
return null;
|
||||
} else if (
|
||||
ref.bestGuessOwningModule === null ||
|
||||
ref.bestGuessOwningModule.specifier !== '@angular/router') {
|
||||
return null;
|
||||
} else if (ref.node.parent.name === undefined || ref.node.parent.name.text !== 'RouterModule') {
|
||||
return null;
|
||||
} else if (
|
||||
!ts.isIdentifier(ref.node.name) ||
|
||||
(ref.node.name.text !== 'forRoot' && ref.node.name.text !== 'forChild')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const routes = args[0];
|
||||
return ts.createObjectLiteral([
|
||||
ts.createPropertyAssignment(ROUTES_MARKER, ts.createTrue()),
|
||||
ts.createPropertyAssignment('routes', routes),
|
||||
]);
|
||||
};
|
||||
const routes = args[0];
|
||||
return ts.createObjectLiteral([
|
||||
ts.createPropertyAssignment(ROUTES_MARKER, ts.createTrue()),
|
||||
ts.createPropertyAssignment('routes', routes),
|
||||
]);
|
||||
};
|
||||
|
||||
function hasIdentifier(node: ts.Node): node is ts.Node&{name: ts.Identifier} {
|
||||
const node_ = node as ts.NamedDeclaration;
|
||||
|
@ -22,10 +22,14 @@ export abstract class RouterEntryPoint {
|
||||
class RouterEntryPointImpl implements RouterEntryPoint {
|
||||
constructor(readonly filePath: string, readonly moduleName: string) {}
|
||||
|
||||
get name(): string { return this.moduleName; }
|
||||
get name(): string {
|
||||
return this.moduleName;
|
||||
}
|
||||
|
||||
// For debugging purposes.
|
||||
toString(): string { return `RouterEntryPoint(name: ${this.name}, filePath: ${this.filePath})`; }
|
||||
toString(): string {
|
||||
return `RouterEntryPoint(name: ${this.name}, filePath: ${this.filePath})`;
|
||||
}
|
||||
}
|
||||
|
||||
export class RouterEntryPointManager {
|
||||
@ -51,7 +55,7 @@ export class RouterEntryPointManager {
|
||||
if (!this.map.has(key)) {
|
||||
this.map.set(key, new RouterEntryPointImpl(sf.fileName, moduleName));
|
||||
}
|
||||
return this.map.get(key) !;
|
||||
return this.map.get(key)!;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,12 +47,12 @@ export class MetadataDtsModuleScopeResolver implements DtsModuleScopeResolver {
|
||||
const clazz = ref.node;
|
||||
const sourceFile = clazz.getSourceFile();
|
||||
if (!sourceFile.isDeclarationFile) {
|
||||
throw new Error(
|
||||
`Debug error: DtsModuleScopeResolver.read(${ref.debugName} from ${sourceFile.fileName}), but not a .d.ts file`);
|
||||
throw new Error(`Debug error: DtsModuleScopeResolver.read(${ref.debugName} from ${
|
||||
sourceFile.fileName}), but not a .d.ts file`);
|
||||
}
|
||||
|
||||
if (this.cache.has(clazz)) {
|
||||
return this.cache.get(clazz) !;
|
||||
return this.cache.get(clazz)!;
|
||||
}
|
||||
|
||||
// Build up the export scope - those directives and pipes made visible by this module.
|
||||
|
@ -143,7 +143,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
getScopeForComponent(clazz: ClassDeclaration): LocalModuleScope|null|'error' {
|
||||
const scope = !this.declarationToModule.has(clazz) ?
|
||||
null :
|
||||
this.getScopeOfModule(this.declarationToModule.get(clazz) !.ngModule);
|
||||
this.getScopeOfModule(this.declarationToModule.get(clazz)!.ngModule);
|
||||
return scope;
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
return null;
|
||||
}
|
||||
|
||||
return Array.from(this.duplicateDeclarations.get(node) !.values());
|
||||
return Array.from(this.duplicateDeclarations.get(node)!.values());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -172,7 +172,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
*/
|
||||
getScopeOfModule(clazz: ClassDeclaration): LocalModuleScope|'error'|null {
|
||||
const scope = this.moduleToRef.has(clazz) ?
|
||||
this.getScopeOfModuleReference(this.moduleToRef.get(clazz) !) :
|
||||
this.getScopeOfModuleReference(this.moduleToRef.get(clazz)!) :
|
||||
null;
|
||||
// If the NgModule class is marked as tainted, consider it an error.
|
||||
if (this.taintedModules.has(clazz)) {
|
||||
@ -193,7 +193,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
this.getScopeOfModule(clazz);
|
||||
|
||||
if (this.scopeErrors.has(clazz)) {
|
||||
return this.scopeErrors.get(clazz) !;
|
||||
return this.scopeErrors.get(clazz)!;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -218,21 +218,22 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
rawDeclarations: ts.Expression|null): void {
|
||||
const declData: DeclarationData = {
|
||||
ngModule,
|
||||
ref: decl, rawDeclarations,
|
||||
ref: decl,
|
||||
rawDeclarations,
|
||||
};
|
||||
|
||||
// First, check for duplicate declarations of the same directive/pipe.
|
||||
if (this.duplicateDeclarations.has(decl.node)) {
|
||||
// This directive/pipe has already been identified as being duplicated. Add this module to the
|
||||
// map of modules for which a duplicate declaration exists.
|
||||
this.duplicateDeclarations.get(decl.node) !.set(ngModule, declData);
|
||||
this.duplicateDeclarations.get(decl.node)!.set(ngModule, declData);
|
||||
} else if (
|
||||
this.declarationToModule.has(decl.node) &&
|
||||
this.declarationToModule.get(decl.node) !.ngModule !== ngModule) {
|
||||
this.declarationToModule.get(decl.node)!.ngModule !== ngModule) {
|
||||
// This directive/pipe is already registered as declared in another module. Mark it as a
|
||||
// duplicate instead.
|
||||
const duplicateDeclMap = new Map<ClassDeclaration, DeclarationData>();
|
||||
const firstDeclData = this.declarationToModule.get(decl.node) !;
|
||||
const firstDeclData = this.declarationToModule.get(decl.node)!;
|
||||
|
||||
// Mark both modules as tainted, since their declarations are missing a component.
|
||||
this.taintedModules.add(firstDeclData.ngModule);
|
||||
@ -341,11 +342,13 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
} else {
|
||||
this.taintedModules.add(ngModule.ref.node);
|
||||
|
||||
const errorNode = decl.getOriginForDiagnostics(ngModule.rawDeclarations !);
|
||||
const errorNode = decl.getOriginForDiagnostics(ngModule.rawDeclarations!);
|
||||
diagnostics.push(makeDiagnostic(
|
||||
ErrorCode.NGMODULE_INVALID_DECLARATION, errorNode,
|
||||
`The class '${decl.node.name.text}' is listed in the declarations ` +
|
||||
`of the NgModule '${ngModule.ref.node.name.text}', but is not a directive, a component, or a pipe. ` +
|
||||
`of the NgModule '${
|
||||
ngModule.ref.node.name
|
||||
.text}', but is not a directive, a component, or a pipe. ` +
|
||||
`Either remove it from the NgModule's declarations, or add an appropriate Angular decorator.`,
|
||||
[{node: decl.node.name, messageText: `'${decl.node.name.text}' is declared here.`}]));
|
||||
continue;
|
||||
@ -378,11 +381,11 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
}
|
||||
} else if (compilationDirectives.has(decl.node)) {
|
||||
// decl is a directive or component in the compilation scope of this NgModule.
|
||||
const directive = compilationDirectives.get(decl.node) !;
|
||||
const directive = compilationDirectives.get(decl.node)!;
|
||||
exportDirectives.set(decl.node, directive);
|
||||
} else if (compilationPipes.has(decl.node)) {
|
||||
// decl is a pipe in the compilation scope of this NgModule.
|
||||
const pipe = compilationPipes.get(decl.node) !;
|
||||
const pipe = compilationPipes.get(decl.node)!;
|
||||
exportPipes.set(decl.node, pipe);
|
||||
} else {
|
||||
// decl is an unknown export.
|
||||
@ -433,7 +436,9 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
/**
|
||||
* Check whether a component requires remote scoping.
|
||||
*/
|
||||
getRequiresRemoteScope(node: ClassDeclaration): boolean { return this.remoteScoping.has(node); }
|
||||
getRequiresRemoteScope(node: ClassDeclaration): boolean {
|
||||
return this.remoteScoping.has(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a component as requiring remote scoping.
|
||||
@ -466,7 +471,8 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
ErrorCode.NGMODULE_INVALID_EXPORT;
|
||||
diagnostics.push(makeDiagnostic(
|
||||
code, identifierOfNode(ref.node) || ref.node,
|
||||
`Appears in the NgModule.${type}s of ${nodeNameForError(ownerForErrors)}, but could not be resolved to an NgModule`));
|
||||
`Appears in the NgModule.${type}s of ${
|
||||
nodeNameForError(ownerForErrors)}, but could not be resolved to an NgModule`));
|
||||
return undefined;
|
||||
}
|
||||
return this.dependencyScopeReader.resolve(ref);
|
||||
@ -496,16 +502,16 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
return;
|
||||
}
|
||||
const isReExport = !declared.has(exportRef.node);
|
||||
const exportName = this.aliasingHost !.maybeAliasSymbolAs(
|
||||
const exportName = this.aliasingHost!.maybeAliasSymbolAs(
|
||||
exportRef, sourceFile, ngModule.ref.node.name.text, isReExport);
|
||||
if (exportName === null) {
|
||||
return;
|
||||
}
|
||||
if (!reexportMap.has(exportName)) {
|
||||
if (exportRef.alias && exportRef.alias instanceof ExternalExpr) {
|
||||
reexports !.push({
|
||||
fromModule: exportRef.alias.value.moduleName !,
|
||||
symbolName: exportRef.alias.value.name !,
|
||||
reexports!.push({
|
||||
fromModule: exportRef.alias.value.moduleName!,
|
||||
symbolName: exportRef.alias.value.name!,
|
||||
asAlias: exportName,
|
||||
});
|
||||
} else {
|
||||
@ -514,7 +520,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
expr.value.name === null) {
|
||||
throw new Error('Expected ExternalExpr');
|
||||
}
|
||||
reexports !.push({
|
||||
reexports!.push({
|
||||
fromModule: expr.value.moduleName,
|
||||
symbolName: expr.value.name,
|
||||
asAlias: exportName,
|
||||
@ -523,7 +529,7 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
reexportMap.set(exportName, exportRef);
|
||||
} else {
|
||||
// Another re-export already used this name. Produce a diagnostic.
|
||||
const prevRef = reexportMap.get(exportName) !;
|
||||
const prevRef = reexportMap.get(exportName)!;
|
||||
diagnostics.push(reexportCollision(ngModuleRef.node, prevRef, exportRef));
|
||||
}
|
||||
};
|
||||
@ -548,12 +554,13 @@ export class LocalModuleScopeRegistry implements MetadataRegistry, ComponentScop
|
||||
*/
|
||||
function invalidRef(
|
||||
clazz: ts.Declaration, decl: Reference<ts.Declaration>,
|
||||
type: 'import' | 'export'): ts.Diagnostic {
|
||||
type: 'import'|'export'): ts.Diagnostic {
|
||||
const code =
|
||||
type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT;
|
||||
const resolveTarget = type === 'import' ? 'NgModule' : 'NgModule, Component, Directive, or Pipe';
|
||||
let message =
|
||||
`Appears in the NgModule.${type}s of ${nodeNameForError(clazz)}, but could not be resolved to an ${resolveTarget} class.` +
|
||||
`Appears in the NgModule.${type}s of ${
|
||||
nodeNameForError(clazz)}, but could not be resolved to an ${resolveTarget} class.` +
|
||||
'\n\n';
|
||||
const library = decl.ownedByModuleGuess !== null ? ` (${decl.ownedByModuleGuess})` : '';
|
||||
const sf = decl.node.getSourceFile();
|
||||
@ -573,8 +580,8 @@ function invalidRef(
|
||||
} else {
|
||||
// This is a monorepo style local dependency. Unfortunately these are too different to really
|
||||
// offer much more advice than this.
|
||||
message +=
|
||||
`This likely means that the dependency${library} which declares ${decl.debugName} has not been processed correctly by ngcc.`;
|
||||
message += `This likely means that the dependency${library} which declares ${
|
||||
decl.debugName} has not been processed correctly by ngcc.`;
|
||||
}
|
||||
|
||||
return makeDiagnostic(code, identifierOfNode(decl.node) || decl.node, message);
|
||||
@ -585,7 +592,7 @@ function invalidRef(
|
||||
*/
|
||||
function invalidTransitiveNgModuleRef(
|
||||
clazz: ts.Declaration, decl: Reference<ts.Declaration>,
|
||||
type: 'import' | 'export'): ts.Diagnostic {
|
||||
type: 'import'|'export'): ts.Diagnostic {
|
||||
const code =
|
||||
type === 'import' ? ErrorCode.NGMODULE_INVALID_IMPORT : ErrorCode.NGMODULE_INVALID_EXPORT;
|
||||
return makeDiagnostic(
|
||||
@ -600,7 +607,8 @@ function invalidTransitiveNgModuleRef(
|
||||
function invalidReexport(clazz: ts.Declaration, decl: Reference<ts.Declaration>): ts.Diagnostic {
|
||||
return makeDiagnostic(
|
||||
ErrorCode.NGMODULE_INVALID_REEXPORT, identifierOfNode(decl.node) || decl.node,
|
||||
`Present in the NgModule.exports of ${nodeNameForError(clazz)} but neither declared nor imported`);
|
||||
`Present in the NgModule.exports of ${
|
||||
nodeNameForError(clazz)} but neither declared nor imported`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -609,11 +617,13 @@ function invalidReexport(clazz: ts.Declaration, decl: Reference<ts.Declaration>)
|
||||
function reexportCollision(
|
||||
module: ClassDeclaration, refA: Reference<ClassDeclaration>,
|
||||
refB: Reference<ClassDeclaration>): ts.Diagnostic {
|
||||
const childMessageText =
|
||||
`This directive/pipe is part of the exports of '${module.name.text}' and shares the same name as another exported directive/pipe.`;
|
||||
const childMessageText = `This directive/pipe is part of the exports of '${
|
||||
module.name.text}' and shares the same name as another exported directive/pipe.`;
|
||||
return makeDiagnostic(
|
||||
ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION, module.name, `
|
||||
There was a name collision between two classes named '${refA.node.name.text}', which are both part of the exports of '${module.name.text}'.
|
||||
ErrorCode.NGMODULE_REEXPORT_NAME_COLLISION, module.name,
|
||||
`
|
||||
There was a name collision between two classes named '${
|
||||
refA.node.name.text}', which are both part of the exports of '${module.name.text}'.
|
||||
|
||||
Angular generates re-exports of an NgModule's exported directives/pipes from the module's source file in certain cases, using the declared name of the class. If two classes of the same name are exported, this automatic naming does not work.
|
||||
|
||||
|
@ -22,7 +22,7 @@ const MODULE_FROM_NODE_MODULES_PATH = /.*node_modules\/(\w+)\/index\.d\.ts$/;
|
||||
|
||||
const testHost: UnifiedModulesHost = {
|
||||
fileNameToModuleName: function(imported: string): string {
|
||||
const res = MODULE_FROM_NODE_MODULES_PATH.exec(imported) !;
|
||||
const res = MODULE_FROM_NODE_MODULES_PATH.exec(imported)!;
|
||||
return 'root/' + res[1];
|
||||
}
|
||||
};
|
||||
@ -44,7 +44,7 @@ export declare type PipeMeta<A, B> = never;
|
||||
* destructured to retrieve references to specific declared classes.
|
||||
*/
|
||||
function makeTestEnv(
|
||||
modules: {[module: string]: string}, aliasGenerator: AliasingHost | null = null): {
|
||||
modules: {[module: string]: string}, aliasGenerator: AliasingHost|null = null): {
|
||||
refs: {[name: string]: Reference<ClassDeclaration>},
|
||||
resolver: MetadataDtsModuleScopeResolver,
|
||||
} {
|
||||
@ -64,11 +64,11 @@ function makeTestEnv(
|
||||
// Resolver for the refs object.
|
||||
const get = (target: {}, name: string): Reference<ts.ClassDeclaration> => {
|
||||
for (const sf of program.getSourceFiles()) {
|
||||
const symbol = checker.getSymbolAtLocation(sf) !;
|
||||
const exportedSymbol = symbol.exports !.get(name as ts.__String);
|
||||
const symbol = checker.getSymbolAtLocation(sf)!;
|
||||
const exportedSymbol = symbol.exports!.get(name as ts.__String);
|
||||
if (exportedSymbol !== undefined) {
|
||||
const decl = exportedSymbol.valueDeclaration as ts.ClassDeclaration;
|
||||
const specifier = MODULE_FROM_NODE_MODULES_PATH.exec(sf.fileName) ![1];
|
||||
const specifier = MODULE_FROM_NODE_MODULES_PATH.exec(sf.fileName)![1];
|
||||
return new Reference(decl, {specifier, resolutionContext: sf.fileName});
|
||||
}
|
||||
}
|
||||
@ -97,7 +97,7 @@ runInEachFileSystem(() => {
|
||||
`
|
||||
});
|
||||
const {Dir, Module} = refs;
|
||||
const scope = resolver.resolve(Module) !;
|
||||
const scope = resolver.resolve(Module)!;
|
||||
expect(scopeToRefs(scope)).toEqual([Dir]);
|
||||
});
|
||||
|
||||
@ -118,7 +118,7 @@ runInEachFileSystem(() => {
|
||||
`
|
||||
});
|
||||
const {Dir, ModuleB} = refs;
|
||||
const scope = resolver.resolve(ModuleB) !;
|
||||
const scope = resolver.resolve(ModuleB)!;
|
||||
expect(scopeToRefs(scope)).toEqual([Dir]);
|
||||
});
|
||||
|
||||
@ -142,7 +142,7 @@ runInEachFileSystem(() => {
|
||||
`
|
||||
});
|
||||
const {Dir, ModuleB} = refs;
|
||||
const scope = resolver.resolve(ModuleB) !;
|
||||
const scope = resolver.resolve(ModuleB)!;
|
||||
expect(scopeToRefs(scope)).toEqual([Dir]);
|
||||
|
||||
// Explicitly verify that the directive has the correct owning module.
|
||||
@ -186,7 +186,7 @@ runInEachFileSystem(() => {
|
||||
},
|
||||
new UnifiedModulesAliasingHost(testHost));
|
||||
const {ShallowModule} = refs;
|
||||
const scope = resolver.resolve(ShallowModule) !;
|
||||
const scope = resolver.resolve(ShallowModule)!;
|
||||
const [DeepDir, MiddleDir, ShallowDir] = scopeToRefs(scope);
|
||||
expect(getAlias(DeepDir)).toEqual({
|
||||
moduleName: 'root/shallow',
|
||||
@ -236,7 +236,7 @@ runInEachFileSystem(() => {
|
||||
},
|
||||
new UnifiedModulesAliasingHost(testHost));
|
||||
const {ShallowModule} = refs;
|
||||
const scope = resolver.resolve(ShallowModule) !;
|
||||
const scope = resolver.resolve(ShallowModule)!;
|
||||
const [DeepDir, MiddleDir, ShallowDir] = scopeToRefs(scope);
|
||||
expect(getAlias(DeepDir)).toEqual({
|
||||
moduleName: 'root/shallow',
|
||||
@ -269,7 +269,7 @@ runInEachFileSystem(() => {
|
||||
},
|
||||
new UnifiedModulesAliasingHost(testHost));
|
||||
const {DeepExportModule} = refs;
|
||||
const scope = resolver.resolve(DeepExportModule) !;
|
||||
const scope = resolver.resolve(DeepExportModule)!;
|
||||
const [DeepDir] = scopeToRefs(scope);
|
||||
expect(getAlias(DeepDir)).toBeNull();
|
||||
});
|
||||
@ -278,7 +278,7 @@ runInEachFileSystem(() => {
|
||||
function scopeToRefs(scope: ExportScope): Reference<ClassDeclaration>[] {
|
||||
const directives = scope.exported.directives.map(dir => dir.ref);
|
||||
const pipes = scope.exported.pipes.map(pipe => pipe.ref);
|
||||
return [...directives, ...pipes].sort((a, b) => a.debugName !.localeCompare(b.debugName !));
|
||||
return [...directives, ...pipes].sort((a, b) => a.debugName!.localeCompare(b.debugName!));
|
||||
}
|
||||
|
||||
function getAlias(ref: Reference<ClassDeclaration>): ExternalReference|null {
|
||||
|
@ -34,8 +34,8 @@ function registerFakeRefs(registry: MetadataRegistry):
|
||||
|
||||
describe('LocalModuleScopeRegistry', () => {
|
||||
const refEmitter = new ReferenceEmitter([]);
|
||||
let scopeRegistry !: LocalModuleScopeRegistry;
|
||||
let metaRegistry !: MetadataRegistry;
|
||||
let scopeRegistry!: LocalModuleScopeRegistry;
|
||||
let metaRegistry!: MetadataRegistry;
|
||||
|
||||
beforeEach(() => {
|
||||
const localRegistry = new LocalMetadataRegistry();
|
||||
@ -230,7 +230,7 @@ describe('LocalModuleScopeRegistry', () => {
|
||||
});
|
||||
|
||||
function fakeDirective(ref: Reference<ClassDeclaration>): DirectiveMeta {
|
||||
const name = ref.debugName !;
|
||||
const name = ref.debugName!;
|
||||
return {
|
||||
ref,
|
||||
name,
|
||||
@ -248,16 +248,18 @@ function fakeDirective(ref: Reference<ClassDeclaration>): DirectiveMeta {
|
||||
}
|
||||
|
||||
function fakePipe(ref: Reference<ClassDeclaration>): PipeMeta {
|
||||
const name = ref.debugName !;
|
||||
const name = ref.debugName!;
|
||||
return {ref, name};
|
||||
}
|
||||
|
||||
class MockDtsModuleScopeResolver implements DtsModuleScopeResolver {
|
||||
resolve(ref: Reference<ClassDeclaration>): null { return null; }
|
||||
resolve(ref: Reference<ClassDeclaration>): null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function scopeToRefs(scopeData: ScopeData): Reference<ClassDeclaration>[] {
|
||||
const directives = scopeData.directives.map(dir => dir.ref);
|
||||
const pipes = scopeData.pipes.map(pipe => pipe.ref);
|
||||
return [...directives, ...pipes].sort((a, b) => a.debugName !.localeCompare(b.debugName !));
|
||||
return [...directives, ...pipes].sort((a, b) => a.debugName!.localeCompare(b.debugName!));
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath, absoluteFrom, basename} from '../../file_system';
|
||||
import {absoluteFrom, AbsoluteFsPath, basename} from '../../file_system';
|
||||
import {ImportRewriter} from '../../imports';
|
||||
import {isNonDeclarationTsPath} from '../../util/src/typescript';
|
||||
|
||||
@ -24,15 +24,21 @@ const STRIP_NG_FACTORY = /(.*)NgFactory$/;
|
||||
export class FactoryGenerator implements ShimGenerator {
|
||||
private constructor(private map: Map<AbsoluteFsPath, AbsoluteFsPath>) {}
|
||||
|
||||
get factoryFileMap(): Map<AbsoluteFsPath, AbsoluteFsPath> { return this.map; }
|
||||
get factoryFileMap(): Map<AbsoluteFsPath, AbsoluteFsPath> {
|
||||
return this.map;
|
||||
}
|
||||
|
||||
get factoryFileNames(): AbsoluteFsPath[] { return Array.from(this.map.keys()); }
|
||||
get factoryFileNames(): AbsoluteFsPath[] {
|
||||
return Array.from(this.map.keys());
|
||||
}
|
||||
|
||||
recognize(fileName: AbsoluteFsPath): boolean { return this.map.has(fileName); }
|
||||
recognize(fileName: AbsoluteFsPath): boolean {
|
||||
return this.map.has(fileName);
|
||||
}
|
||||
|
||||
generate(genFilePath: AbsoluteFsPath, readFile: (fileName: string) => ts.SourceFile | null):
|
||||
ts.SourceFile|null {
|
||||
const originalPath = this.map.get(genFilePath) !;
|
||||
const originalPath = this.map.get(genFilePath)!;
|
||||
const original = readFile(originalPath);
|
||||
if (original === null) {
|
||||
return null;
|
||||
@ -53,7 +59,7 @@ export class FactoryGenerator implements ShimGenerator {
|
||||
decl => isExported(decl) && decl.decorators !== undefined &&
|
||||
decl.name !== undefined)
|
||||
// Grab the symbol name.
|
||||
.map(decl => decl.name !.text);
|
||||
.map(decl => decl.name!.text);
|
||||
|
||||
|
||||
let sourceText = '';
|
||||
@ -71,8 +77,8 @@ export class FactoryGenerator implements ShimGenerator {
|
||||
// This will encompass a lot of symbols which don't need factories, but that's okay
|
||||
// because it won't miss any that do.
|
||||
const varLines = symbolNames.map(
|
||||
name =>
|
||||
`export const ${name}NgFactory: i0.ɵNgModuleFactory<any> = new i0.ɵNgModuleFactory(${name});`);
|
||||
name => `export const ${
|
||||
name}NgFactory: i0.ɵNgModuleFactory<any> = new i0.ɵNgModuleFactory(${name});`);
|
||||
sourceText += [
|
||||
// This might be incorrect if the current package being compiled is Angular core, but it's
|
||||
// okay to leave in at type checking time. TypeScript can handle this reference via its path
|
||||
@ -136,7 +142,7 @@ function transformFactorySourceFile(
|
||||
return file;
|
||||
}
|
||||
|
||||
const {moduleSymbolNames, sourceFilePath} = factoryMap.get(file.fileName) !;
|
||||
const {moduleSymbolNames, sourceFilePath} = factoryMap.get(file.fileName)!;
|
||||
|
||||
file = ts.getMutableClone(file);
|
||||
|
||||
|
@ -32,7 +32,7 @@ export class FactoryTracker {
|
||||
|
||||
track(sf: ts.SourceFile, factorySymbolName: string): void {
|
||||
if (this.sourceToFactorySymbols.has(sf.fileName)) {
|
||||
this.sourceToFactorySymbols.get(sf.fileName) !.add(factorySymbolName);
|
||||
this.sourceToFactorySymbols.get(sf.fileName)!.add(factorySymbolName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath, absoluteFrom} from '../../file_system';
|
||||
import {absoluteFrom, AbsoluteFsPath} from '../../file_system';
|
||||
import {isNonDeclarationTsPath} from '../../util/src/typescript';
|
||||
|
||||
import {ShimGenerator} from './api';
|
||||
@ -17,13 +17,17 @@ import {generatedModuleName} from './util';
|
||||
export class SummaryGenerator implements ShimGenerator {
|
||||
private constructor(private map: Map<AbsoluteFsPath, AbsoluteFsPath>) {}
|
||||
|
||||
getSummaryFileNames(): AbsoluteFsPath[] { return Array.from(this.map.keys()); }
|
||||
getSummaryFileNames(): AbsoluteFsPath[] {
|
||||
return Array.from(this.map.keys());
|
||||
}
|
||||
|
||||
recognize(fileName: AbsoluteFsPath): boolean { return this.map.has(fileName); }
|
||||
recognize(fileName: AbsoluteFsPath): boolean {
|
||||
return this.map.has(fileName);
|
||||
}
|
||||
|
||||
generate(genFilePath: AbsoluteFsPath, readFile: (fileName: string) => ts.SourceFile | null):
|
||||
ts.SourceFile|null {
|
||||
const originalPath = this.map.get(genFilePath) !;
|
||||
const originalPath = this.map.get(genFilePath)!;
|
||||
const original = readFile(originalPath);
|
||||
if (original === null) {
|
||||
return null;
|
||||
|
@ -22,7 +22,9 @@ import {ShimGenerator} from './api';
|
||||
export class TypeCheckShimGenerator implements ShimGenerator {
|
||||
constructor(private typeCheckFile: AbsoluteFsPath) {}
|
||||
|
||||
recognize(fileName: AbsoluteFsPath): boolean { return fileName === this.typeCheckFile; }
|
||||
recognize(fileName: AbsoluteFsPath): boolean {
|
||||
return fileName === this.typeCheckFile;
|
||||
}
|
||||
|
||||
generate(genFileName: AbsoluteFsPath, readFile: (fileName: string) => ts.SourceFile | null):
|
||||
ts.SourceFile|null {
|
||||
|
@ -107,8 +107,8 @@ function flipIvySwitchesInVariableStatement(
|
||||
// reported as a thrown error and not a diagnostic as transformers cannot output diagnostics.
|
||||
let newIdentifier = findPostSwitchIdentifier(statements, postSwitchName);
|
||||
if (newIdentifier === null) {
|
||||
throw new Error(
|
||||
`Unable to find identifier ${postSwitchName} in ${stmt.getSourceFile().fileName} for the Ivy switch.`);
|
||||
throw new Error(`Unable to find identifier ${postSwitchName} in ${
|
||||
stmt.getSourceFile().fileName} for the Ivy switch.`);
|
||||
}
|
||||
|
||||
// Copy the identifier with updateIdentifier(). This copies the internal information which
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {AbsoluteFsPath, NgtscCompilerHost, dirname, getFileSystem, getSourceFileOrError} from '../../file_system';
|
||||
import {AbsoluteFsPath, dirname, getFileSystem, getSourceFileOrError, NgtscCompilerHost} from '../../file_system';
|
||||
|
||||
export function makeProgram(
|
||||
files: {name: AbsoluteFsPath, contents: string, isRoot?: boolean}[],
|
||||
@ -25,7 +25,8 @@ export function makeProgram(
|
||||
const compilerOptions = {
|
||||
noLib: true,
|
||||
experimentalDecorators: true,
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeJs, ...options
|
||||
moduleResolution: ts.ModuleResolutionKind.NodeJs,
|
||||
...options
|
||||
};
|
||||
const compilerHost = new NgtscCompilerHost(fs, compilerOptions);
|
||||
const rootNames = files.filter(file => file.isRoot !== false)
|
||||
@ -38,7 +39,7 @@ export function makeProgram(
|
||||
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
||||
if (diagnostic.file) {
|
||||
const {line, character} =
|
||||
diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start !);
|
||||
diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
|
||||
message = `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`;
|
||||
}
|
||||
return `Error: ${message}`;
|
||||
|
@ -17,7 +17,7 @@ export function aliasTransformFactory(exportStatements: Map<string, Map<string,
|
||||
}
|
||||
|
||||
const statements = [...file.statements];
|
||||
exportStatements.get(file.fileName) !.forEach(([moduleName, symbolName], aliasName) => {
|
||||
exportStatements.get(file.fileName)!.forEach(([moduleName, symbolName], aliasName) => {
|
||||
const stmt = ts.createExportDeclaration(
|
||||
/* decorators */ undefined,
|
||||
/* modifiers */ undefined,
|
||||
|
@ -94,9 +94,13 @@ export class TraitCompiler {
|
||||
}
|
||||
}
|
||||
|
||||
analyzeSync(sf: ts.SourceFile): void { this.analyze(sf, false); }
|
||||
analyzeSync(sf: ts.SourceFile): void {
|
||||
this.analyze(sf, false);
|
||||
}
|
||||
|
||||
analyzeAsync(sf: ts.SourceFile): Promise<void>|undefined { return this.analyze(sf, true); }
|
||||
analyzeAsync(sf: ts.SourceFile): Promise<void>|undefined {
|
||||
return this.analyze(sf, true);
|
||||
}
|
||||
|
||||
private analyze(sf: ts.SourceFile, preanalyze: false): void;
|
||||
private analyze(sf: ts.SourceFile, preanalyze: true): Promise<void>|undefined;
|
||||
@ -138,7 +142,7 @@ export class TraitCompiler {
|
||||
|
||||
recordFor(clazz: ClassDeclaration): ClassRecord|null {
|
||||
if (this.classes.has(clazz)) {
|
||||
return this.classes.get(clazz) !;
|
||||
return this.classes.get(clazz)!;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -149,8 +153,8 @@ export class TraitCompiler {
|
||||
return null;
|
||||
}
|
||||
const records: ClassRecord[] = [];
|
||||
for (const clazz of this.fileToClasses.get(sf) !) {
|
||||
records.push(this.classes.get(clazz) !);
|
||||
for (const clazz of this.fileToClasses.get(sf)!) {
|
||||
records.push(this.classes.get(clazz)!);
|
||||
}
|
||||
return records;
|
||||
}
|
||||
@ -173,7 +177,7 @@ export class TraitCompiler {
|
||||
};
|
||||
|
||||
for (const priorTrait of priorRecord.traits) {
|
||||
const handler = this.handlersByName.get(priorTrait.handler.name) !;
|
||||
const handler = this.handlersByName.get(priorTrait.handler.name)!;
|
||||
let trait: Trait<unknown, unknown, unknown> = Trait.pending(handler, priorTrait.detected);
|
||||
|
||||
if (priorTrait.state === TraitState.ANALYZED || priorTrait.state === TraitState.RESOLVED) {
|
||||
@ -195,7 +199,7 @@ export class TraitCompiler {
|
||||
if (!this.fileToClasses.has(sf)) {
|
||||
this.fileToClasses.set(sf, new Set<ClassDeclaration>());
|
||||
}
|
||||
this.fileToClasses.get(sf) !.add(record.node);
|
||||
this.fileToClasses.get(sf)!.add(record.node);
|
||||
}
|
||||
|
||||
private scanClassForTraits(clazz: ClassDeclaration):
|
||||
@ -242,7 +246,7 @@ export class TraitCompiler {
|
||||
if (!this.fileToClasses.has(sf)) {
|
||||
this.fileToClasses.set(sf, new Set<ClassDeclaration>());
|
||||
}
|
||||
this.fileToClasses.get(sf) !.add(clazz);
|
||||
this.fileToClasses.get(sf)!.add(clazz);
|
||||
} else {
|
||||
// This is at least the second handler to match this class. This is a slower path that some
|
||||
// classes will go through, which validates that the set of decorators applied to the class
|
||||
@ -317,7 +321,7 @@ export class TraitCompiler {
|
||||
}
|
||||
}
|
||||
if (preanalysis !== null) {
|
||||
preanalyzeQueue !.push(preanalysis.then(analyze));
|
||||
preanalyzeQueue!.push(preanalysis.then(analyze));
|
||||
} else {
|
||||
analyze();
|
||||
}
|
||||
@ -328,8 +332,8 @@ export class TraitCompiler {
|
||||
clazz: ClassDeclaration, trait: Trait<unknown, unknown, unknown>,
|
||||
flags?: HandlerFlags): void {
|
||||
if (trait.state !== TraitState.PENDING) {
|
||||
throw new Error(
|
||||
`Attempt to analyze trait of ${clazz.name.text} in state ${TraitState[trait.state]} (expected DETECTED)`);
|
||||
throw new Error(`Attempt to analyze trait of ${clazz.name.text} in state ${
|
||||
TraitState[trait.state]} (expected DETECTED)`);
|
||||
}
|
||||
|
||||
// Attempt analysis. This could fail with a `FatalDiagnosticError`; catch it if it does.
|
||||
@ -363,7 +367,7 @@ export class TraitCompiler {
|
||||
resolve(): void {
|
||||
const classes = Array.from(this.classes.keys());
|
||||
for (const clazz of classes) {
|
||||
const record = this.classes.get(clazz) !;
|
||||
const record = this.classes.get(clazz)!;
|
||||
for (let trait of record.traits) {
|
||||
const handler = trait.handler;
|
||||
switch (trait.state) {
|
||||
@ -371,8 +375,8 @@ export class TraitCompiler {
|
||||
case TraitState.ERRORED:
|
||||
continue;
|
||||
case TraitState.PENDING:
|
||||
throw new Error(
|
||||
`Resolving a trait that hasn't been analyzed: ${clazz.name.text} / ${Object.getPrototypeOf(trait.handler).constructor.name}`);
|
||||
throw new Error(`Resolving a trait that hasn't been analyzed: ${clazz.name.text} / ${
|
||||
Object.getPrototypeOf(trait.handler).constructor.name}`);
|
||||
case TraitState.RESOLVED:
|
||||
throw new Error(`Resolving an already resolved trait`);
|
||||
}
|
||||
@ -410,7 +414,7 @@ export class TraitCompiler {
|
||||
if (!this.reexportMap.has(fileName)) {
|
||||
this.reexportMap.set(fileName, new Map<string, [string, string]>());
|
||||
}
|
||||
const fileReexports = this.reexportMap.get(fileName) !;
|
||||
const fileReexports = this.reexportMap.get(fileName)!;
|
||||
for (const reexport of result.reexports) {
|
||||
fileReexports.set(reexport.asAlias, [reexport.fromModule, reexport.symbolName]);
|
||||
}
|
||||
@ -421,7 +425,7 @@ export class TraitCompiler {
|
||||
|
||||
typeCheck(ctx: TypeCheckContext): void {
|
||||
for (const clazz of this.classes.keys()) {
|
||||
const record = this.classes.get(clazz) !;
|
||||
const record = this.classes.get(clazz)!;
|
||||
for (const trait of record.traits) {
|
||||
if (trait.state !== TraitState.RESOLVED) {
|
||||
continue;
|
||||
@ -435,7 +439,7 @@ export class TraitCompiler {
|
||||
|
||||
index(ctx: IndexingContext): void {
|
||||
for (const clazz of this.classes.keys()) {
|
||||
const record = this.classes.get(clazz) !;
|
||||
const record = this.classes.get(clazz)!;
|
||||
for (const trait of record.traits) {
|
||||
if (trait.state !== TraitState.RESOLVED) {
|
||||
// Skip traits that haven't been resolved successfully.
|
||||
@ -457,7 +461,7 @@ export class TraitCompiler {
|
||||
return null;
|
||||
}
|
||||
|
||||
const record = this.classes.get(original) !;
|
||||
const record = this.classes.get(original)!;
|
||||
|
||||
let res: CompileResult[] = [];
|
||||
|
||||
@ -496,7 +500,7 @@ export class TraitCompiler {
|
||||
return [];
|
||||
}
|
||||
|
||||
const record = this.classes.get(original) !;
|
||||
const record = this.classes.get(original)!;
|
||||
const decorators: ts.Decorator[] = [];
|
||||
|
||||
for (const trait of record.traits) {
|
||||
@ -515,7 +519,7 @@ export class TraitCompiler {
|
||||
get diagnostics(): ReadonlyArray<ts.Diagnostic> {
|
||||
const diagnostics: ts.Diagnostic[] = [];
|
||||
for (const clazz of this.classes.keys()) {
|
||||
const record = this.classes.get(clazz) !;
|
||||
const record = this.classes.get(clazz)!;
|
||||
if (record.metaDiagnostics !== null) {
|
||||
diagnostics.push(...record.metaDiagnostics);
|
||||
}
|
||||
@ -528,5 +532,7 @@ export class TraitCompiler {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
get exportStatements(): Map<string, Map<string, [string, string]>> { return this.reexportMap; }
|
||||
get exportStatements(): Map<string, Map<string, [string, string]>> {
|
||||
return this.reexportMap;
|
||||
}
|
||||
}
|
||||
|
@ -28,14 +28,14 @@ export class DtsTransformRegistry {
|
||||
if (!this.ivyDeclarationTransforms.has(sf)) {
|
||||
this.ivyDeclarationTransforms.set(sf, new IvyDeclarationDtsTransform());
|
||||
}
|
||||
return this.ivyDeclarationTransforms.get(sf) !;
|
||||
return this.ivyDeclarationTransforms.get(sf)!;
|
||||
}
|
||||
|
||||
getReturnTypeTransform(sf: ts.SourceFile): ReturnTypeTransform {
|
||||
if (!this.returnTypeTransforms.has(sf)) {
|
||||
this.returnTypeTransforms.set(sf, new ReturnTypeTransform());
|
||||
}
|
||||
return this.returnTypeTransforms.get(sf) !;
|
||||
return this.returnTypeTransforms.get(sf)!;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,11 +55,11 @@ export class DtsTransformRegistry {
|
||||
let transforms: DtsTransform[]|null = null;
|
||||
if (this.ivyDeclarationTransforms.has(originalSf)) {
|
||||
transforms = [];
|
||||
transforms.push(this.ivyDeclarationTransforms.get(originalSf) !);
|
||||
transforms.push(this.ivyDeclarationTransforms.get(originalSf)!);
|
||||
}
|
||||
if (this.returnTypeTransforms.has(originalSf)) {
|
||||
transforms = transforms || [];
|
||||
transforms.push(this.returnTypeTransforms.get(originalSf) !);
|
||||
transforms.push(this.returnTypeTransforms.get(originalSf)!);
|
||||
}
|
||||
return transforms;
|
||||
}
|
||||
@ -200,7 +200,7 @@ export class IvyDeclarationDtsTransform implements DtsTransform {
|
||||
if (!this.declarationFields.has(original)) {
|
||||
return clazz;
|
||||
}
|
||||
const fields = this.declarationFields.get(original) !;
|
||||
const fields = this.declarationFields.get(original)!;
|
||||
|
||||
const newMembers = fields.map(decl => {
|
||||
const modifiers = [ts.createModifier(ts.SyntaxKind.StaticKeyword)];
|
||||
@ -247,7 +247,7 @@ export class ReturnTypeTransform implements DtsTransform {
|
||||
if (!this.typeReplacements.has(original)) {
|
||||
return element;
|
||||
}
|
||||
const returnType = this.typeReplacements.get(original) !;
|
||||
const returnType = this.typeReplacements.get(original)!;
|
||||
const tsReturnType = translateType(returnType, imports);
|
||||
|
||||
const methodSignature = ts.updateMethodSignature(
|
||||
@ -273,7 +273,7 @@ export class ReturnTypeTransform implements DtsTransform {
|
||||
if (!this.typeReplacements.has(original)) {
|
||||
return element;
|
||||
}
|
||||
const returnType = this.typeReplacements.get(original) !;
|
||||
const returnType = this.typeReplacements.get(original)!;
|
||||
const tsReturnType = translateType(returnType, imports);
|
||||
|
||||
return ts.updateFunctionDeclaration(
|
||||
|
@ -50,8 +50,8 @@ export enum TraitState {
|
||||
* This not only simplifies the implementation, but ensures traits are monomorphic objects as
|
||||
* they're all just "views" in the type system of the same object (which never changes shape).
|
||||
*/
|
||||
export type Trait<D, A, R> = PendingTrait<D, A, R>| SkippedTrait<D, A, R>| AnalyzedTrait<D, A, R>|
|
||||
ResolvedTrait<D, A, R>| ErroredTrait<D, A, R>;
|
||||
export type Trait<D, A, R> = PendingTrait<D, A, R>|SkippedTrait<D, A, R>|AnalyzedTrait<D, A, R>|
|
||||
ResolvedTrait<D, A, R>|ErroredTrait<D, A, R>;
|
||||
|
||||
/**
|
||||
* The value side of `Trait` exposes a helper to create a `Trait` in a pending state (by delegating
|
||||
@ -59,7 +59,7 @@ export type Trait<D, A, R> = PendingTrait<D, A, R>| SkippedTrait<D, A, R>| Analy
|
||||
*/
|
||||
export const Trait = {
|
||||
pending: <D, A, R>(handler: DecoratorHandler<D, A, R>, detected: DetectResult<D>):
|
||||
PendingTrait<D, A, R> => TraitImpl.pending(handler, detected),
|
||||
PendingTrait<D, A, R> => TraitImpl.pending(handler, detected),
|
||||
};
|
||||
|
||||
/**
|
||||
@ -137,7 +137,9 @@ export interface ErroredTrait<D, A, R> extends TraitBase<D, A, R> {
|
||||
*
|
||||
* This is a terminal state.
|
||||
*/
|
||||
export interface SkippedTrait<D, A, R> extends TraitBase<D, A, R> { state: TraitState.SKIPPED; }
|
||||
export interface SkippedTrait<D, A, R> extends TraitBase<D, A, R> {
|
||||
state: TraitState.SKIPPED;
|
||||
}
|
||||
|
||||
/**
|
||||
* The part of the `Trait` interface for any trait which has been successfully analyzed.
|
||||
@ -251,8 +253,8 @@ class TraitImpl<D, A, R> {
|
||||
*/
|
||||
private assertTransitionLegal(allowedState: TraitState, transitionTo: TraitState): void {
|
||||
if (!(this.state & allowedState)) {
|
||||
throw new Error(
|
||||
`Assertion failure: cannot transition from ${TraitState[this.state]} to ${TraitState[transitionTo]}.`);
|
||||
throw new Error(`Assertion failure: cannot transition from ${TraitState[this.state]} to ${
|
||||
TraitState[transitionTo]}.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import * as ts from 'typescript';
|
||||
import {DefaultImportRecorder, ImportRewriter} from '../../imports';
|
||||
import {Decorator, ReflectionHost} from '../../reflection';
|
||||
import {ImportManager, translateExpression, translateStatement} from '../../translator';
|
||||
import {VisitListEntryResult, Visitor, visit} from '../../util/src/visitor';
|
||||
import {visit, VisitListEntryResult, Visitor} from '../../util/src/visitor';
|
||||
|
||||
import {TraitCompiler} from './compilation';
|
||||
import {addImports} from './utils';
|
||||
@ -285,7 +285,7 @@ function setFileOverviewComment(sf: ts.SourceFile, fileoverview: FileOverviewMet
|
||||
}
|
||||
|
||||
function maybeFilterDecorator(
|
||||
decorators: ts.NodeArray<ts.Decorator>| undefined,
|
||||
decorators: ts.NodeArray<ts.Decorator>|undefined,
|
||||
toRemove: ts.Decorator[]): ts.NodeArray<ts.Decorator>|undefined {
|
||||
if (decorators === undefined) {
|
||||
return undefined;
|
||||
|
@ -24,11 +24,15 @@ runInEachFileSystem(() => {
|
||||
name = 'FakeDecoratorHandler';
|
||||
precedence = HandlerPrecedence.PRIMARY;
|
||||
|
||||
detect(): undefined { throw new Error('detect should not have been called'); }
|
||||
detect(): undefined {
|
||||
throw new Error('detect should not have been called');
|
||||
}
|
||||
analyze(): AnalysisOutput<unknown> {
|
||||
throw new Error('analyze should not have been called');
|
||||
}
|
||||
compile(): CompileResult { throw new Error('compile should not have been called'); }
|
||||
compile(): CompileResult {
|
||||
throw new Error('compile should not have been called');
|
||||
}
|
||||
}
|
||||
|
||||
const {program} = makeProgram([{
|
||||
@ -40,7 +44,7 @@ runInEachFileSystem(() => {
|
||||
const compiler = new TraitCompiler(
|
||||
[new FakeDecoratorHandler()], reflectionHost, NOOP_PERF_RECORDER, NOOP_INCREMENTAL_BUILD,
|
||||
true, new DtsTransformRegistry());
|
||||
const sourceFile = program.getSourceFile('lib.d.ts') !;
|
||||
const sourceFile = program.getSourceFile('lib.d.ts')!;
|
||||
const analysis = compiler.analyzeSync(sourceFile);
|
||||
|
||||
expect(sourceFile.isDeclarationFile).toBe(true);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user