From abee78582123de81a3b1a77dadab7dafe4226055 Mon Sep 17 00:00:00 2001 From: Chuck Jazdzewski Date: Tue, 18 Jul 2017 12:52:48 -0700 Subject: [PATCH] refactor(tsc-wrapped): update tsc-wrapped to pass strictNullCheck (#18160) PR Close #18160 --- build.sh | 5 + packages/compiler-cli/src/codegen.ts | 4 +- packages/compiler/test/aot/test_util.ts | 2 +- packages/tsc-wrapped/src/bundler.ts | 61 +++++---- packages/tsc-wrapped/src/cli_options.ts | 22 +-- packages/tsc-wrapped/src/collector.ts | 69 ++++++---- packages/tsc-wrapped/src/compiler_host.ts | 13 +- packages/tsc-wrapped/src/evaluator.ts | 25 ++-- packages/tsc-wrapped/src/main.ts | 10 +- packages/tsc-wrapped/src/schema.ts | 8 +- packages/tsc-wrapped/src/symbols.ts | 33 +++-- packages/tsc-wrapped/src/tsc.ts | 15 +- packages/tsc-wrapped/test/bundler_spec.ts | 6 +- packages/tsc-wrapped/test/collector.spec.ts | 64 ++++----- packages/tsc-wrapped/test/evaluator.spec.ts | 129 +++++++++--------- packages/tsc-wrapped/test/symbols.spec.ts | 4 +- packages/tsc-wrapped/test/typescript.mocks.ts | 28 ++-- packages/tsc-wrapped/tsconfig.json | 1 + 18 files changed, 269 insertions(+), 230 deletions(-) diff --git a/build.sh b/build.sh index 099d7f05c8..71b476bb8d 100755 --- a/build.sh +++ b/build.sh @@ -419,6 +419,11 @@ if [[ ${BUILD_TOOLS} == true || ${BUILD_ALL} == true ]]; then $(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json cp ./packages/tsc-wrapped/package.json ./dist/packages-dist/tsc-wrapped cp ./packages/tsc-wrapped/README.md ./dist/packages-dist/tsc-wrapped + ( + cd dist/packages-dist/tsc-wrapped + echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-PLACEHOLDER/${VERSION}/g\" $""(grep -ril 0\.0\.0\-PLACEHOLDER .)" + perl -p -i -e "s/0\.0\.0\-PLACEHOLDER/${VERSION}/g" $(grep -ril 0\.0\.0\-PLACEHOLDER .) < /dev/null 2> /dev/null + ) fi for PACKAGE in ${PACKAGES[@]} diff --git a/packages/compiler-cli/src/codegen.ts b/packages/compiler-cli/src/codegen.ts index 8207c74e2e..cf8ea0a07e 100644 --- a/packages/compiler-cli/src/codegen.ts +++ b/packages/compiler-cli/src/codegen.ts @@ -100,8 +100,8 @@ export class CodeGenerator { } const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, { translations: transContent, - i18nFormat: cliOptions.i18nFormat, - locale: cliOptions.locale, missingTranslation, + i18nFormat: cliOptions.i18nFormat || undefined, + locale: cliOptions.locale || undefined, missingTranslation, enableLegacyTemplate: options.enableLegacyTemplate !== false, enableSummariesForJit: options.enableSummariesForJit !== false, }); diff --git a/packages/compiler/test/aot/test_util.ts b/packages/compiler/test/aot/test_util.ts index 7a0ffa4002..93b3c7b25b 100644 --- a/packages/compiler/test/aot/test_util.ts +++ b/packages/compiler/test/aot/test_util.ts @@ -405,7 +405,7 @@ export class MockMetadataBundlerHost implements MetadataBundlerHost { constructor(private host: ts.CompilerHost) {} - getMetadataFor(moduleName: string): ModuleMetadata { + getMetadataFor(moduleName: string): ModuleMetadata|undefined { const source = this.host.getSourceFile(moduleName + '.ts', ts.ScriptTarget.Latest); return this.collector.getMetadata(source); } diff --git a/packages/tsc-wrapped/src/bundler.ts b/packages/tsc-wrapped/src/bundler.ts index b599ddf560..92a48744e6 100644 --- a/packages/tsc-wrapped/src/bundler.ts +++ b/packages/tsc-wrapped/src/bundler.ts @@ -70,7 +70,9 @@ export interface BundledModule { privates: BundlePrivateEntry[]; } -export interface MetadataBundlerHost { getMetadataFor(moduleName: string): ModuleMetadata; } +export interface MetadataBundlerHost { + getMetadataFor(moduleName: string): ModuleMetadata|undefined; +} type StaticsMetadata = { [name: string]: MetadataValue | FunctionMetadata; @@ -78,7 +80,7 @@ type StaticsMetadata = { export class MetadataBundler { private symbolMap = new Map(); - private metadataCache = new Map(); + private metadataCache = new Map(); private exports = new Map(); private rootModule: string; private exported: Set; @@ -98,14 +100,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); @@ -114,7 +116,7 @@ export class MetadataBundler { __symbolic: 'module', version: VERSION, exports: exports.length ? exports : undefined, metadata, origins, - importAs: this.importAs + importAs: this.importAs ! }, privates }; @@ -124,7 +126,7 @@ export class MetadataBundler { return resolveModule(importName, from); } - private getMetadata(moduleName: string): ModuleMetadata { + private getMetadata(moduleName: string): ModuleMetadata|undefined { let result = this.metadataCache.get(moduleName); if (!result) { if (moduleName.startsWith('.')) { @@ -138,7 +140,7 @@ export class MetadataBundler { private exportAll(moduleName: string): Symbol[] { const module = this.getMetadata(moduleName); - let result: Symbol[] = this.exports.get(moduleName); + let result = this.exports.get(moduleName); if (result) { return result; @@ -148,7 +150,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; }; @@ -266,7 +268,7 @@ export class MetadataBundler { name = newPrivateName(); symbol.privateName = name; } - result[name] = symbol.value; + result[name] = symbol.value !; } }); @@ -279,9 +281,10 @@ export class MetadataBundler { const exportAlls = new Set(); for (const symbol of exportedSymbols) { if (symbol.reexport) { - const declaration = symbol.declaration; + // symbol.declaration is guarenteed to be defined during the phase this method is called. + const declaration = symbol.declaration !; const module = declaration.module; - if (declaration.name == '*') { + if (declaration !.name == '*') { // Reexport all the symbols. exportAlls.add(declaration.module); } else { @@ -304,11 +307,13 @@ export class MetadataBundler { } private convertSymbol(symbol: Symbol) { - const canonicalSymbol = symbol.canonicalSymbol; + // canonicalSymbol is ensured to be defined before this is called. + const canonicalSymbol = symbol.canonicalSymbol !; if (!canonicalSymbol.referenced) { canonicalSymbol.referenced = true; - const declaration = canonicalSymbol.declaration; + // declaration is ensured to be definded before this method is called. + const declaration = canonicalSymbol.declaration !; const module = this.getMetadata(declaration.module); if (module) { const value = module.metadata[declaration.name]; @@ -336,10 +341,10 @@ export class MetadataBundler { return { __symbolic: 'class', arity: value.arity, - extends: this.convertExpression(moduleName, value.extends), + extends: this.convertExpression(moduleName, value.extends) !, decorators: - value.decorators && value.decorators.map(d => this.convertExpression(moduleName, d)), - members: this.convertMembers(moduleName, value.members), + value.decorators && value.decorators.map(d => this.convertExpression(moduleName, d) !), + members: this.convertMembers(moduleName, value.members !), statics: value.statics && this.convertStatics(moduleName, value.statics) }; } @@ -356,11 +361,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 = @@ -397,7 +402,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)); @@ -413,8 +418,8 @@ export class MetadataBundler { } private convertExpression( - moduleName: string, value: MetadataSymbolicExpression|MetadataError| - undefined): MetadataSymbolicExpression|MetadataError|undefined { + moduleName: string, value: MetadataSymbolicExpression|MetadataError|null| + undefined): MetadataSymbolicExpression|MetadataError|undefined|null { if (value) { switch (value.__symbolic) { case 'error': @@ -439,9 +444,9 @@ export class MetadataBundler { } private convertReference(moduleName: string, value: MetadataSymbolicReferenceExpression): - MetadataSymbolicReferenceExpression|MetadataError { + 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. @@ -450,11 +455,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 { @@ -564,7 +569,7 @@ export class CompilerHostAdapter implements MetadataBundlerHost { constructor(private host: ts.CompilerHost) {} - getMetadataFor(fileName: string): ModuleMetadata { + getMetadataFor(fileName: string): ModuleMetadata|undefined { const sourceFile = this.host.getSourceFile(fileName + '.ts', ts.ScriptTarget.Latest); return this.collector.getMetadata(sourceFile); } diff --git a/packages/tsc-wrapped/src/cli_options.ts b/packages/tsc-wrapped/src/cli_options.ts index 566cf9cee1..5755f8665e 100644 --- a/packages/tsc-wrapped/src/cli_options.ts +++ b/packages/tsc-wrapped/src/cli_options.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ export class CliOptions { - public basePath: string; - constructor({basePath = null}: {basePath?: string}) { this.basePath = basePath; } + public basePath: string|null; + constructor({basePath = null}: {basePath?: string | null}) { this.basePath = basePath; } } export class I18nExtractionCliOptions extends CliOptions { @@ -28,18 +28,18 @@ export class I18nExtractionCliOptions extends CliOptions { } export class NgcCliOptions extends CliOptions { - public i18nFormat: string; - public i18nFile: string; - public locale: string; - public missingTranslation: string; + public i18nFormat: string|null; + public i18nFile: string|null; + public locale: string|null; + public missingTranslation: string|null; constructor({i18nFormat = null, i18nFile = null, locale = null, missingTranslation = null, basePath = null}: { - i18nFormat?: string, - i18nFile?: string, - locale?: string, - missingTranslation?: string, - basePath?: string + i18nFormat?: string | null, + i18nFile?: string|null, + locale?: string|null, + missingTranslation?: string|null, + basePath?: string|null }) { super({basePath: basePath}); this.i18nFormat = i18nFormat; diff --git a/packages/tsc-wrapped/src/collector.ts b/packages/tsc-wrapped/src/collector.ts index 7ec3183a99..f6d117d200 100644 --- a/packages/tsc-wrapped/src/collector.ts +++ b/packages/tsc-wrapped/src/collector.ts @@ -54,13 +54,13 @@ export class MetadataCollector { * Returns a JSON.stringify friendly form describing the decorators of the exported classes from * the source file that is expected to correspond to a module. */ - public getMetadata(sourceFile: ts.SourceFile, strict: boolean = false): ModuleMetadata { + public getMetadata(sourceFile: ts.SourceFile, strict: boolean = false): ModuleMetadata|undefined { const locals = new Symbols(sourceFile); const nodeMap = new Map(); const evaluator = new Evaluator(locals, nodeMap, this.options); let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined; - let exports: ModuleExportMetadata[]; + let exports: ModuleExportMetadata[]|undefined = undefined; function objFromDecorator(decoratorNode: ts.Decorator): MetadataSymbolicExpression { return evaluator.evaluateNode(decoratorNode.expression); @@ -79,7 +79,7 @@ export class MetadataCollector { function maybeGetSimpleFunction( functionDeclaration: ts.FunctionDeclaration | ts.MethodDeclaration): {func: FunctionMetadata, name: string}|undefined { - if (functionDeclaration.name.kind == ts.SyntaxKind.Identifier) { + if (functionDeclaration.name && functionDeclaration.name.kind == ts.SyntaxKind.Identifier) { const nameNode = functionDeclaration.name; const functionName = nameNode.text; const functionBody = functionDeclaration.body; @@ -107,7 +107,8 @@ export class MetadataCollector { function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata { const result: ClassMetadata = {__symbolic: 'class'}; - function getDecorators(decorators: ts.Decorator[]): MetadataSymbolicExpression[] { + function getDecorators(decorators: ts.Decorator[] | undefined): MetadataSymbolicExpression[]| + undefined { if (decorators && decorators.length) return decorators.map(decorator => objFromDecorator(decorator)); return undefined; @@ -145,7 +146,7 @@ export class MetadataCollector { } // member decorators - let members: MetadataMap = null; + let members: MetadataMap|null = null; function recordMember(name: string, metadata: MemberMetadata) { if (!members) members = {}; const data = members.hasOwnProperty(name) ? members[name] : []; @@ -154,7 +155,7 @@ export class MetadataCollector { } // static member - let statics: {[name: string]: MetadataValue | FunctionMetadata} = null; + let statics: {[name: string]: MetadataValue | FunctionMetadata}|null = null; function recordStaticMember(name: string, value: MetadataValue | FunctionMetadata) { if (!statics) statics = {}; statics[name] = value; @@ -176,7 +177,8 @@ export class MetadataCollector { } const methodDecorators = getDecorators(method.decorators); const parameters = method.parameters; - const parameterDecoratorData: (MetadataSymbolicExpression | MetadataError)[][] = []; + const parameterDecoratorData: + ((MetadataSymbolicExpression | MetadataError)[] | undefined)[] = []; const parametersData: (MetadataSymbolicReferenceExpression | MetadataError | MetadataSymbolicSelectExpression | null)[] = []; @@ -254,7 +256,8 @@ export class MetadataCollector { const {moduleSpecifier, exportClause} = exportDeclaration; if (!moduleSpecifier) { - exportClause.elements.forEach(spec => { + // If there is a module specifier there is also an exportClause + exportClause !.elements.forEach(spec => { const exportedAs = spec.name.text; const name = (spec.propertyName || spec.name).text; exportMap.set(name, exportedAs); @@ -263,12 +266,13 @@ export class MetadataCollector { } }); - const isExportedIdentifier = (identifier: ts.Identifier) => exportMap.has(identifier.text); + const isExportedIdentifier = (identifier?: ts.Identifier) => + identifier && exportMap.has(identifier.text); const isExported = (node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration | ts.EnumDeclaration) => isExport(node) || isExportedIdentifier(node.name); - const exportedIdentifierName = (identifier: ts.Identifier) => - exportMap.get(identifier.text) || identifier.text; + const exportedIdentifierName = (identifier?: ts.Identifier) => + identifier && (exportMap.get(identifier.text) || identifier.text); const exportedName = (node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration | ts.EnumDeclaration) => exportedIdentifierName(node.name); @@ -358,8 +362,11 @@ export class MetadataCollector { const classDeclaration = node; if (classDeclaration.name) { if (isExported(classDeclaration)) { - if (!metadata) metadata = {}; - metadata[exportedName(classDeclaration)] = classMetadataOf(classDeclaration); + const name = exportedName(classDeclaration); + if (name) { + if (!metadata) metadata = {}; + metadata[name] = classMetadataOf(classDeclaration); + } } } // Otherwise don't record metadata for the class. @@ -368,8 +375,11 @@ export class MetadataCollector { case ts.SyntaxKind.InterfaceDeclaration: const interfaceDeclaration = node; if (interfaceDeclaration.name && isExported(interfaceDeclaration)) { - if (!metadata) metadata = {}; - metadata[exportedName(interfaceDeclaration)] = {__symbolic: 'interface'}; + const name = exportedName(interfaceDeclaration); + if (name) { + if (!metadata) metadata = {}; + metadata[name] = {__symbolic: 'interface'}; + } } break; @@ -378,11 +388,13 @@ export class MetadataCollector { // names substitution will be performed by the StaticReflector. const functionDeclaration = node; if (isExported(functionDeclaration) && functionDeclaration.name) { - if (!metadata) metadata = {}; const name = exportedName(functionDeclaration); const maybeFunc = maybeGetSimpleFunction(functionDeclaration); - metadata[name] = - maybeFunc ? recordEntry(maybeFunc.func, node) : {__symbolic: 'function'}; + if (name) { + if (!metadata) metadata = {}; + metadata[name] = + maybeFunc ? recordEntry(maybeFunc.func, node) : {__symbolic: 'function'}; + } } break; @@ -400,7 +412,7 @@ export class MetadataCollector { } else { enumValue = evaluator.evaluateNode(member.initializer); } - let name: string = undefined; + let name: string|undefined = undefined; if (member.name.kind == ts.SyntaxKind.Identifier) { const identifier = member.name; name = identifier.text; @@ -424,8 +436,10 @@ export class MetadataCollector { } } if (writtenMembers) { - if (!metadata) metadata = {}; - metadata[enumName] = recordEntry(enumValueHolder, node); + if (enumName) { + if (!metadata) metadata = {}; + metadata[enumName] = recordEntry(enumValueHolder, node); + } } } break; @@ -444,8 +458,11 @@ export class MetadataCollector { let exported = false; if (isExport(variableStatement) || isExport(variableDeclaration) || isExportedIdentifier(nameNode)) { - if (!metadata) metadata = {}; - metadata[exportedIdentifierName(nameNode)] = recordEntry(varValue, node); + const name = exportedIdentifierName(nameNode); + if (name) { + if (!metadata) metadata = {}; + metadata[name] = recordEntry(varValue, node); + } exported = true; } if (typeof varValue == 'string' || typeof varValue == 'number' || @@ -601,11 +618,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 { @@ -628,7 +645,7 @@ function validateMetadata( } } - function shouldReportNode(node: ts.Node) { + function shouldReportNode(node: ts.Node | undefined) { if (node) { const nodeStart = node.getStart(); return !( diff --git a/packages/tsc-wrapped/src/compiler_host.ts b/packages/tsc-wrapped/src/compiler_host.ts index ae8939fad1..eaff682940 100644 --- a/packages/tsc-wrapped/src/compiler_host.ts +++ b/packages/tsc-wrapped/src/compiler_host.ts @@ -33,10 +33,10 @@ export abstract class DelegatingHost implements ts.CompilerHost { (fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) => this.delegate.getSourceFile(fileName, languageVersion, onError); - getCancellationToken = () => this.delegate.getCancellationToken(); + getCancellationToken = () => this.delegate.getCancellationToken !(); getDefaultLibFileName = (options: ts.CompilerOptions) => this.delegate.getDefaultLibFileName(options); - getDefaultLibLocation = () => this.delegate.getDefaultLibLocation(); + getDefaultLibLocation = () => this.delegate.getDefaultLibLocation !(); writeFile: ts.WriteFileCallback = this.delegate.writeFile; getCurrentDirectory = () => this.delegate.getCurrentDirectory(); getDirectories = (path: string): string[] => @@ -46,8 +46,8 @@ export abstract class DelegatingHost implements ts.CompilerHost { getNewLine = () => this.delegate.getNewLine(); fileExists = (fileName: string) => this.delegate.fileExists(fileName); readFile = (fileName: string) => this.delegate.readFile(fileName); - trace = (s: string) => this.delegate.trace(s); - directoryExists = (directoryName: string) => this.delegate.directoryExists(directoryName); + trace = (s: string) => this.delegate.trace !(s); + directoryExists = (directoryName: string) => this.delegate.directoryExists !(directoryName); } const IGNORED_FILES = /\.ngfactory\.js$|\.ngstyle\.js$/; @@ -79,7 +79,8 @@ export class MetadataWriterHost extends DelegatingHost { const metadata = this.metadataCollector.getMetadata(collectableFile, !!this.ngOptions.strictMetadataEmit); const metadata1 = this.metadataCollector1.getMetadata(collectableFile, false); - const metadatas: ModuleMetadata[] = [metadata, metadata1].filter(e => !!e); + const metadatas: ModuleMetadata[] = + [metadata, metadata1].filter(e => !!e) as ModuleMetadata[]; if (metadatas.length) { const metadataText = JSON.stringify(metadatas); writeFileSync(path, metadataText, {encoding: 'utf-8'}); @@ -163,7 +164,7 @@ export class SyntheticIndexHost extends DelegatingHost { normalize(sourceFiles[0].fileName) == this.normalSyntheticIndexName) { // If we are writing the synthetic index, write the metadata along side. const metadataName = fileName.replace(DTS, '.metadata.json'); - writeFileSync(metadataName, this.indexMetadata, 'utf8'); + writeFileSync(metadataName, this.indexMetadata, {encoding: 'utf8'}); } } } \ No newline at end of file diff --git a/packages/tsc-wrapped/src/evaluator.ts b/packages/tsc-wrapped/src/evaluator.ts index b7320ee79e..b77f55c027 100644 --- a/packages/tsc-wrapped/src/evaluator.ts +++ b/packages/tsc-wrapped/src/evaluator.ts @@ -68,7 +68,7 @@ export interface ImportMetadata { } -function getSourceFileOfNode(node: ts.Node): ts.SourceFile { +function getSourceFileOfNode(node: ts.Node | undefined): ts.SourceFile { while (node && node.kind != ts.SyntaxKind.SourceFile) { node = node.parent; } @@ -79,7 +79,7 @@ function getSourceFileOfNode(node: ts.Node): ts.SourceFile { export function errorSymbol( message: string, node?: ts.Node, context?: {[name: string]: string}, sourceFile?: ts.SourceFile): MetadataError { - let result: MetadataError; + let result: MetadataError|undefined = undefined; if (node) { sourceFile = sourceFile || getSourceFileOfNode(node); if (sourceFile) { @@ -106,15 +106,16 @@ export class Evaluator { private symbols: Symbols, private nodeMap: Map, private options: CollectorOptions = {}) {} - nameOf(node: ts.Node): string|MetadataError { - if (node.kind == ts.SyntaxKind.Identifier) { + nameOf(node: ts.Node|undefined): string|MetadataError { + if (node && node.kind == ts.SyntaxKind.Identifier) { return (node).text; } - const result = this.evaluateNode(node); + const result = node && this.evaluateNode(node); if (isMetadataError(result) || typeof result === 'string') { return result; } else { - return errorSymbol('Name expected', node, {received: node.getText()}); + return errorSymbol( + 'Name expected', node, {received: (node && node.getText()) || ''}); } } @@ -138,7 +139,7 @@ export class Evaluator { return this.isFoldableWorker(node, new Map()); } - private isFoldableWorker(node: ts.Node, folding: Map): boolean { + private isFoldableWorker(node: ts.Node|undefined, folding: Map): boolean { if (node) { switch (node.kind) { case ts.SyntaxKind.ObjectLiteralExpression: @@ -518,11 +519,11 @@ export class Evaluator { if (isDefined(operand) && isPrimitive(operand)) { switch (prefixUnaryExpression.operator) { case ts.SyntaxKind.PlusToken: - return +operand; + return +(operand as any); case ts.SyntaxKind.MinusToken: - return -operand; + return -(operand as any); case ts.SyntaxKind.TildeToken: - return ~operand; + return ~(operand as any); case ts.SyntaxKind.ExclamationToken: return !operand; } @@ -661,8 +662,8 @@ function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment { return node.kind == ts.SyntaxKind.PropertyAssignment; } -const empty = [] as ts.NodeArray; +const empty = ts.createNodeArray(); -function arrayOrEmpty(v: ts.NodeArray): ts.NodeArray { +function arrayOrEmpty(v: ts.NodeArray| undefined): ts.NodeArray { return v || empty; } \ No newline at end of file diff --git a/packages/tsc-wrapped/src/main.ts b/packages/tsc-wrapped/src/main.ts index e3b485b90e..10a5cf0fbb 100644 --- a/packages/tsc-wrapped/src/main.ts +++ b/packages/tsc-wrapped/src/main.ts @@ -41,9 +41,9 @@ export function createBundleIndexHost( return { host, errors: [{ - file: null, - start: null, - length: null, + file: null as any as ts.SourceFile, + start: null as any as number, + length: null as any as number, messageText: 'Angular compiler option "flatModuleIndex" requires one and only one .ts file in the "files" field.', category: ts.DiagnosticCategory.Error, @@ -58,7 +58,7 @@ export function createBundleIndexHost( const metadataBundle = bundler.getMetadataBundle(); const metadata = JSON.stringify(metadataBundle.metadata); 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); host = new SyntheticIndexHost(host, {name, content, metadata}); @@ -201,7 +201,7 @@ if (require.main === module) { const project = options.project || '.'; // TODO(alexeagle): command line should be TSC-compatible, remove "CliOptions" here const cliOptions = new CliOptions(require('minimist')(args)); - main(project, cliOptions, null, options) + main(project, cliOptions, undefined, options) .then((exitCode: any) => process.exit(exitCode)) .catch((e: any) => { console.error(e.stack); diff --git a/packages/tsc-wrapped/src/schema.ts b/packages/tsc-wrapped/src/schema.ts index 1e390394ab..20fcb9932b 100644 --- a/packages/tsc-wrapped/src/schema.ts +++ b/packages/tsc-wrapped/src/schema.ts @@ -73,7 +73,7 @@ export function isMemberMetadata(value: any): value is MemberMetadata { export interface MethodMetadata extends MemberMetadata { __symbolic: 'constructor'|'method'; - parameterDecorators?: (MetadataSymbolicExpression|MetadataError)[][]; + parameterDecorators?: ((MetadataSymbolicExpression | MetadataError)[]|undefined)[]; } export function isMethodMetadata(value: any): value is MethodMetadata { return value && (value.__symbolic === 'constructor' || value.__symbolic === 'method'); @@ -81,7 +81,7 @@ export function isMethodMetadata(value: any): value is MethodMetadata { export interface ConstructorMetadata extends MethodMetadata { __symbolic: 'constructor'; - parameters?: (MetadataSymbolicExpression|MetadataError|null)[]; + parameters?: (MetadataSymbolicExpression|MetadataError|null|undefined)[]; } export function isConstructorMetadata(value: any): value is ConstructorMetadata { return value && value.__symbolic === 'constructor'; @@ -97,8 +97,8 @@ export function isFunctionMetadata(value: any): value is FunctionMetadata { return value && value.__symbolic === 'function'; } -export type MetadataValue = string | number | boolean | MetadataObject | MetadataArray | - MetadataSymbolicExpression | MetadataError; +export type MetadataValue = string | number | boolean | undefined | null | MetadataObject | + MetadataArray | MetadataSymbolicExpression | MetadataError; export interface MetadataObject { [name: string]: MetadataValue; } diff --git a/packages/tsc-wrapped/src/symbols.ts b/packages/tsc-wrapped/src/symbols.ts index 44f0fe06bd..6e29dd267f 100644 --- a/packages/tsc-wrapped/src/symbols.ts +++ b/packages/tsc-wrapped/src/symbols.ts @@ -49,23 +49,26 @@ export class Symbols { ts.SyntaxKind.ExternalModuleReference) { const externalReference = importEqualsDeclaration.moduleReference; - // An `import = require(); - if (!externalReference.expression.parent) { - // The `parent` field of a node is set by the TypeScript binder (run as - // part of the type checker). Setting it here allows us to call `getText()` - // even if the `SourceFile` was not type checked (which looks for `SourceFile` - // in the parent chain). This doesn't damage the node as the binder unconditionally - // sets the parent. - externalReference.expression.parent = externalReference; - externalReference.parent = this.sourceFile as any; + if (externalReference.expression) { + // An `import = require(); + if (!externalReference.expression.parent) { + // The `parent` field of a node is set by the TypeScript binder (run as + // part of the type checker). Setting it here allows us to call `getText()` + // even if the `SourceFile` was not type checked (which looks for `SourceFile` + // in the parent chain). This doesn't damage the node as the binder unconditionally + // sets the parent. + externalReference.expression.parent = externalReference; + externalReference.parent = this.sourceFile as any; + } + const from = stripQuotes(externalReference.expression.getText()); + symbols.set( + importEqualsDeclaration.name.text, {__symbolic: 'reference', module: from}); + break; } - const from = stripQuotes(externalReference.expression.getText()); - symbols.set(importEqualsDeclaration.name.text, {__symbolic: 'reference', module: from}); - } else { - symbols.set( - importEqualsDeclaration.name.text, - {__symbolic: 'error', message: `Unsupported import syntax`}); } + symbols.set( + importEqualsDeclaration.name.text, + {__symbolic: 'error', message: `Unsupported import syntax`}); break; case ts.SyntaxKind.ImportDeclaration: const importDecl = node; diff --git a/packages/tsc-wrapped/src/tsc.ts b/packages/tsc-wrapped/src/tsc.ts index 15295dfe55..3fa20b1bc4 100644 --- a/packages/tsc-wrapped/src/tsc.ts +++ b/packages/tsc-wrapped/src/tsc.ts @@ -33,7 +33,7 @@ export class UserError extends Error { super(message); // Required for TS 2.1, see // https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work - Object.setPrototypeOf(this, UserError.prototype); + (Object as any).setPrototypeOf(this, UserError.prototype); const nativeError = new Error(message) as any as Error; this._nativeError = nativeError; @@ -76,13 +76,14 @@ export function formatDiagnostics(diags: ts.Diagnostic[]): string { .join('\n'); } -export function check(diags: ts.Diagnostic[]) { +export function check(diags: ts.Diagnostic[] | undefined) { if (diags && diags.length && diags[0]) { throw new UserError(formatDiagnostics(diags)); } } -export function validateAngularCompilerOptions(options: AngularCompilerOptions): ts.Diagnostic[] { +export function validateAngularCompilerOptions(options: AngularCompilerOptions): ts.Diagnostic[]| + undefined { if (options.annotationsAs) { switch (options.annotationsAs) { case 'decorators': @@ -90,9 +91,9 @@ export function validateAngularCompilerOptions(options: AngularCompilerOptions): break; default: return [{ - file: null, - start: null, - length: null, + file: null as any as ts.SourceFile, + start: null as any as number, + length: null as any as number, messageText: 'Angular compiler options "annotationsAs" only supports "static fields" and "decorators"', category: ts.DiagnosticCategory.Error, @@ -136,7 +137,7 @@ export class Tsc implements CompilerInterface { return ts.readConfigFile(project, this.readFile); } })(); - check([error]); + check([error !]); const parsed = ts.parseJsonConfigFileContent(config, this.parseConfigHost, basePath, existingOptions); diff --git a/packages/tsc-wrapped/test/bundler_spec.ts b/packages/tsc-wrapped/test/bundler_spec.ts index 54ba8dca60..347b3c58b2 100644 --- a/packages/tsc-wrapped/test/bundler_spec.ts +++ b/packages/tsc-wrapped/test/bundler_spec.ts @@ -29,7 +29,7 @@ describe('metadata bundler', () => { const originalTwo = './src/two/index'; expect(Object.keys(result.metadata.origins) .sort() - .map(name => ({name, value: result.metadata.origins[name]}))) + .map(name => ({name, value: result.metadata.origins ![name]}))) .toEqual([ {name: 'ONE_CLASSES', value: originalOne}, {name: 'One', value: originalOne}, {name: 'OneMore', value: originalOne}, {name: 'TWO_CLASSES', value: originalTwo}, @@ -190,7 +190,7 @@ describe('metadata bundler', () => { from: 'external_one' } ]); - expect(result.metadata.origins['E']).toBeUndefined(); + expect(result.metadata.origins !['E']).toBeUndefined(); }); }); @@ -199,7 +199,7 @@ export class MockStringBundlerHost implements MetadataBundlerHost { constructor(private dirName: string, private directory: Directory) {} - getMetadataFor(moduleName: string): ModuleMetadata { + getMetadataFor(moduleName: string): ModuleMetadata|undefined { const fileName = path.join(this.dirName, moduleName) + '.ts'; const text = open(this.directory, fileName); if (typeof text == 'string') { diff --git a/packages/tsc-wrapped/test/collector.spec.ts b/packages/tsc-wrapped/test/collector.spec.ts index a6ca662bf8..b22f3d1f5b 100644 --- a/packages/tsc-wrapped/test/collector.spec.ts +++ b/packages/tsc-wrapped/test/collector.spec.ts @@ -196,13 +196,13 @@ describe('Collector', () => { beforeEach(() => { casesFile = program.getSourceFile('/app/cases-data.ts'); - casesMetadata = collector.getMetadata(casesFile); + casesMetadata = collector.getMetadata(casesFile) !; }); it('should provide any reference for an any ctor parameter type', () => { const casesAny = casesMetadata.metadata['CaseAny']; expect(casesAny).toBeTruthy(); - const ctorData = casesAny.members['__ctor__']; + const ctorData = casesAny.members !['__ctor__']; expect(ctorData).toEqual( [{__symbolic: 'constructor', parameters: [{__symbolic: 'reference', name: 'any'}]}]); }); @@ -265,10 +265,10 @@ describe('Collector', () => { it('should report an error for references to unexpected types', () => { const unsupported1 = program.getSourceFile('/unsupported-2.ts'); - const metadata = collector.getMetadata(unsupported1); + const metadata = collector.getMetadata(unsupported1) !; const barClass = metadata.metadata['Bar']; - const ctor = barClass.members['__ctor__'][0]; - const parameter = ctor.parameters[0]; + const ctor = barClass.members !['__ctor__'][0]; + const parameter = ctor.parameters ![0]; expect(parameter).toEqual({ __symbolic: 'error', message: 'Reference to non-exported class', @@ -280,9 +280,9 @@ describe('Collector', () => { it('should be able to handle import star type references', () => { const importStar = program.getSourceFile('/import-star.ts'); - const metadata = collector.getMetadata(importStar); + const metadata = collector.getMetadata(importStar) !; const someClass = metadata.metadata['SomeClass']; - const ctor = someClass.members['__ctor__'][0]; + const ctor = someClass.members !['__ctor__'][0]; const parameters = ctor.parameters; expect(parameters).toEqual([ {__symbolic: 'reference', module: 'angular2/common', name: 'NgFor'} @@ -365,9 +365,9 @@ describe('Collector', () => { it('should be able to handle import star type references', () => { const importStar = program.getSourceFile('/import-star.ts'); - const metadata = collector.getMetadata(importStar); + const metadata = collector.getMetadata(importStar) !; const someClass = metadata.metadata['SomeClass']; - const ctor = someClass.members['__ctor__'][0]; + const ctor = someClass.members !['__ctor__'][0]; const parameters = ctor.parameters; expect(parameters).toEqual([ {__symbolic: 'reference', module: 'angular2/common', name: 'NgFor'} @@ -376,14 +376,14 @@ describe('Collector', () => { it('should be able to collect the value of an enum', () => { const enumSource = program.getSourceFile('/exported-enum.ts'); - const metadata = collector.getMetadata(enumSource); + const metadata = collector.getMetadata(enumSource) !; const someEnum: any = metadata.metadata['SomeEnum']; expect(someEnum).toEqual({A: 0, B: 1, C: 100, D: 101}); }); it('should ignore a non-export enum', () => { const enumSource = program.getSourceFile('/private-enum.ts'); - const metadata = collector.getMetadata(enumSource); + const metadata = collector.getMetadata(enumSource) !; const publicEnum: any = metadata.metadata['PublicEnum']; const privateEnum: any = metadata.metadata['PrivateEnum']; expect(publicEnum).toEqual({a: 0, b: 1, c: 2}); @@ -392,7 +392,7 @@ describe('Collector', () => { it('should be able to collect enums initialized from consts', () => { const enumSource = program.getSourceFile('/exported-enum.ts'); - const metadata = collector.getMetadata(enumSource); + const metadata = collector.getMetadata(enumSource) !; const complexEnum: any = metadata.metadata['ComplexEnum']; expect(complexEnum).toEqual({ A: 0, @@ -405,7 +405,7 @@ describe('Collector', () => { it('should be able to collect a simple static method', () => { const staticSource = program.getSourceFile('/static-method.ts'); - const metadata = collector.getMetadata(staticSource); + const metadata = collector.getMetadata(staticSource) !; expect(metadata).toBeDefined(); const classData = metadata.metadata['MyModule']; expect(classData).toBeDefined(); @@ -423,7 +423,7 @@ describe('Collector', () => { it('should be able to collect a call to a static method', () => { const staticSource = program.getSourceFile('/static-method-call.ts'); - const metadata = collector.getMetadata(staticSource); + const metadata = collector.getMetadata(staticSource) !; expect(metadata).toBeDefined(); const classData = metadata.metadata['Foo']; expect(classData).toBeDefined(); @@ -446,7 +446,7 @@ describe('Collector', () => { it('should be able to collect a static field', () => { const staticSource = program.getSourceFile('/static-field.ts'); - const metadata = collector.getMetadata(staticSource); + const metadata = collector.getMetadata(staticSource) !; expect(metadata).toBeDefined(); const classData = metadata.metadata['MyModule']; expect(classData).toBeDefined(); @@ -455,7 +455,7 @@ describe('Collector', () => { it('should be able to collect a reference to a static field', () => { const staticSource = program.getSourceFile('/static-field-reference.ts'); - const metadata = collector.getMetadata(staticSource); + const metadata = collector.getMetadata(staticSource) !; expect(metadata).toBeDefined(); const classData = metadata.metadata['Foo']; expect(classData).toBeDefined(); @@ -477,7 +477,7 @@ describe('Collector', () => { it('should be able to collect a method with a conditional expression', () => { const source = program.getSourceFile('/static-method-with-if.ts'); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect(metadata).toBeDefined(); const classData = metadata.metadata['MyModule']; expect(classData).toBeDefined(); @@ -502,7 +502,7 @@ describe('Collector', () => { it('should be able to collect a method with a default parameter', () => { const source = program.getSourceFile('/static-method-with-default.ts'); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect(metadata).toBeDefined(); const classData = metadata.metadata['MyModule']; expect(classData).toBeDefined(); @@ -531,7 +531,7 @@ describe('Collector', () => { it('should be able to collect re-exported symbols', () => { const source = program.getSourceFile('/re-exports.ts'); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect(metadata.exports).toEqual([ {from: './static-field', export: ['MyModule']}, {from: './static-field-reference', export: [{name: 'Foo', as: 'OtherModule'}]}, @@ -541,13 +541,13 @@ describe('Collector', () => { it('should be able to collect a export as symbol', () => { const source = program.getSourceFile('export-as.d.ts'); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect(metadata.metadata).toEqual({SomeFunction: {__symbolic: 'function'}}); }); it('should be able to collect exports with no module specifier', () => { const source = program.getSourceFile('/re-exports-2.ts'); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect(metadata.metadata).toEqual({ MyClass: Object({__symbolic: 'class'}), OtherModule: {__symbolic: 'reference', module: './static-field-reference', name: 'Foo'}, @@ -557,7 +557,7 @@ describe('Collector', () => { it('should collect an error symbol if collecting a reference to a non-exported symbol', () => { const source = program.getSourceFile('/local-symbol-ref.ts'); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect(metadata.metadata).toEqual({ REQUIRED_VALIDATOR: { __symbolic: 'error', @@ -579,7 +579,7 @@ describe('Collector', () => { it('should collect an error symbol if collecting a reference to a non-exported function', () => { const source = program.getSourceFile('/local-function-ref.ts'); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect(metadata.metadata).toEqual({ REQUIRED_VALIDATOR: { __symbolic: 'error', @@ -601,7 +601,7 @@ describe('Collector', () => { it('should collect an error for a simple function that references a local variable', () => { const source = program.getSourceFile('/local-symbol-ref-func.ts'); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect(metadata.metadata).toEqual({ foo: { __symbolic: 'function', @@ -619,7 +619,7 @@ describe('Collector', () => { it('should collect any for interface parameter reference', () => { const source = program.getSourceFile('/interface-reference.ts'); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect((metadata.metadata['SomeClass'] as ClassMetadata).members).toEqual({ __ctor__: [{ __symbolic: 'constructor', @@ -734,7 +734,7 @@ describe('Collector', () => { toString(): string { return \`InjectionToken ${this._desc}\`; } } as any;`, ts.ScriptTarget.Latest, true); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect(metadata.metadata).toEqual({InjectionToken: {__symbolic: 'class'}}); }); @@ -781,19 +781,19 @@ describe('Collector', () => { describe('inheritance', () => { it('should record `extends` clauses for declared classes', () => { - const metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts')); + const metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts')) !; expect(metadata.metadata['DeclaredChildClass']) .toEqual({__symbolic: 'class', extends: {__symbolic: 'reference', name: 'ParentClass'}}); }); it('should record `extends` clauses for classes in the same file', () => { - const metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts')); + const metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts')) !; expect(metadata.metadata['ChildClassSameFile']) .toEqual({__symbolic: 'class', extends: {__symbolic: 'reference', name: 'ParentClass'}}); }); it('should record `extends` clauses for classes in a different file', () => { - const metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts')); + const metadata = collector.getMetadata(program.getSourceFile('/class-inheritance.ts')) !; expect(metadata.metadata['ChildClassOtherFile']).toEqual({ __symbolic: 'class', extends: { @@ -811,7 +811,7 @@ describe('Collector', () => { } it('should collect the correct arity for a class', () => { - const metadata = collector.getMetadata(program.getSourceFile('/class-arity.ts')); + const metadata = collector.getMetadata(program.getSourceFile('/class-arity.ts')) !; const zero = metadata.metadata['Zero']; if (expectClass(zero)) expect(zero.arity).toBeUndefined(); @@ -883,7 +883,7 @@ describe('Collector', () => { }) export class MyComponent {} `); - const metadata = collector.getMetadata(source); + const metadata = collector.getMetadata(source) !; expect(metadata.metadata.MyComponent).toEqual({ __symbolic: 'class', decorators: [{ @@ -947,7 +947,7 @@ describe('Collector', () => { function collectSource(content: string): ModuleMetadata { const sourceFile = createSource(content); - return collector.getMetadata(sourceFile); + return collector.getMetadata(sourceFile) !; } }); diff --git a/packages/tsc-wrapped/test/evaluator.spec.ts b/packages/tsc-wrapped/test/evaluator.spec.ts index 0e5e05350a..2205a27120 100644 --- a/packages/tsc-wrapped/test/evaluator.spec.ts +++ b/packages/tsc-wrapped/test/evaluator.spec.ts @@ -12,7 +12,7 @@ import * as ts from 'typescript'; import {Evaluator} from '../src/evaluator'; import {Symbols} from '../src/symbols'; -import {Directory, Host, expectNoDiagnostics, findVar} from './typescript.mocks'; +import {Directory, Host, expectNoDiagnostics, findVar, findVarInitializer} from './typescript.mocks'; describe('Evaluator', () => { const documentRegistry = ts.createDocumentRegistry(); @@ -31,7 +31,7 @@ describe('Evaluator', () => { service = ts.createLanguageService(host, documentRegistry); program = service.getProgram(); typeChecker = program.getTypeChecker(); - symbols = new Symbols(null); + symbols = new Symbols(null as any as ts.SourceFile); evaluator = new Evaluator(symbols, new Map()); }); @@ -48,10 +48,10 @@ describe('Evaluator', () => { it('should be able to fold literal expressions', () => { const consts = program.getSourceFile('consts.ts'); - expect(evaluator.isFoldable(findVar(consts, 'someName').initializer)).toBeTruthy(); - expect(evaluator.isFoldable(findVar(consts, 'someBool').initializer)).toBeTruthy(); - expect(evaluator.isFoldable(findVar(consts, 'one').initializer)).toBeTruthy(); - expect(evaluator.isFoldable(findVar(consts, 'two').initializer)).toBeTruthy(); + expect(evaluator.isFoldable(findVarInitializer(consts, 'someName'))).toBeTruthy(); + expect(evaluator.isFoldable(findVarInitializer(consts, 'someBool'))).toBeTruthy(); + expect(evaluator.isFoldable(findVarInitializer(consts, 'one'))).toBeTruthy(); + expect(evaluator.isFoldable(findVarInitializer(consts, 'two'))).toBeTruthy(); }); it('should be able to fold expressions with foldable references', () => { @@ -60,20 +60,20 @@ describe('Evaluator', () => { symbols.define('someBool', true); symbols.define('one', 1); symbols.define('two', 2); - expect(evaluator.isFoldable(findVar(expressions, 'three').initializer)).toBeTruthy(); - expect(evaluator.isFoldable(findVar(expressions, 'four').initializer)).toBeTruthy(); + expect(evaluator.isFoldable(findVarInitializer(expressions, 'three'))).toBeTruthy(); + expect(evaluator.isFoldable(findVarInitializer(expressions, 'four'))).toBeTruthy(); symbols.define('three', 3); symbols.define('four', 4); - expect(evaluator.isFoldable(findVar(expressions, 'obj').initializer)).toBeTruthy(); - expect(evaluator.isFoldable(findVar(expressions, 'arr').initializer)).toBeTruthy(); + expect(evaluator.isFoldable(findVarInitializer(expressions, 'obj'))).toBeTruthy(); + expect(evaluator.isFoldable(findVarInitializer(expressions, 'arr'))).toBeTruthy(); }); it('should be able to evaluate literal expressions', () => { const consts = program.getSourceFile('consts.ts'); - expect(evaluator.evaluateNode(findVar(consts, 'someName').initializer)).toBe('some-name'); - expect(evaluator.evaluateNode(findVar(consts, 'someBool').initializer)).toBe(true); - expect(evaluator.evaluateNode(findVar(consts, 'one').initializer)).toBe(1); - expect(evaluator.evaluateNode(findVar(consts, 'two').initializer)).toBe(2); + expect(evaluator.evaluateNode(findVarInitializer(consts, 'someName'))).toBe('some-name'); + expect(evaluator.evaluateNode(findVarInitializer(consts, 'someBool'))).toBe(true); + expect(evaluator.evaluateNode(findVarInitializer(consts, 'one'))).toBe(1); + expect(evaluator.evaluateNode(findVarInitializer(consts, 'two'))).toBe(2); }); it('should be able to evaluate expressions', () => { @@ -82,78 +82,77 @@ describe('Evaluator', () => { symbols.define('someBool', true); symbols.define('one', 1); symbols.define('two', 2); - expect(evaluator.evaluateNode(findVar(expressions, 'three').initializer)).toBe(3); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'three'))).toBe(3); symbols.define('three', 3); - expect(evaluator.evaluateNode(findVar(expressions, 'four').initializer)).toBe(4); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'four'))).toBe(4); symbols.define('four', 4); - expect(evaluator.evaluateNode(findVar(expressions, 'obj').initializer)) + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'obj'))) .toEqual({one: 1, two: 2, three: 3, four: 4}); - expect(evaluator.evaluateNode(findVar(expressions, 'arr').initializer)).toEqual([1, 2, 3, 4]); - expect(evaluator.evaluateNode(findVar(expressions, 'bTrue').initializer)).toEqual(true); - expect(evaluator.evaluateNode(findVar(expressions, 'bFalse').initializer)).toEqual(false); - expect(evaluator.evaluateNode(findVar(expressions, 'bAnd').initializer)).toEqual(true); - expect(evaluator.evaluateNode(findVar(expressions, 'bOr').initializer)).toEqual(true); - expect(evaluator.evaluateNode(findVar(expressions, 'nDiv').initializer)).toEqual(2); - expect(evaluator.evaluateNode(findVar(expressions, 'nMod').initializer)).toEqual(1); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'arr'))).toEqual([1, 2, 3, 4]); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bTrue'))).toEqual(true); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bFalse'))).toEqual(false); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bAnd'))).toEqual(true); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bOr'))).toEqual(true); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'nDiv'))).toEqual(2); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'nMod'))).toEqual(1); - expect(evaluator.evaluateNode(findVar(expressions, 'bLOr').initializer)).toEqual(false || true); - expect(evaluator.evaluateNode(findVar(expressions, 'bLAnd').initializer)).toEqual(true && true); - expect(evaluator.evaluateNode(findVar(expressions, 'bBOr').initializer)).toEqual(0x11 | 0x22); - expect(evaluator.evaluateNode(findVar(expressions, 'bBAnd').initializer)).toEqual(0x11 & 0x03); - expect(evaluator.evaluateNode(findVar(expressions, 'bXor').initializer)).toEqual(0x11 ^ 0x21); - expect(evaluator.evaluateNode(findVar(expressions, 'bEqual').initializer)) + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLOr'))).toEqual(false || true); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLAnd'))).toEqual(true && true); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bBOr'))).toEqual(0x11 | 0x22); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bBAnd'))).toEqual(0x11 & 0x03); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bXor'))).toEqual(0x11 ^ 0x21); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bEqual'))) .toEqual(1 == '1'); - expect(evaluator.evaluateNode(findVar(expressions, 'bNotEqual').initializer)) + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bNotEqual'))) .toEqual(1 != '1'); - expect(evaluator.evaluateNode(findVar(expressions, 'bIdentical').initializer)) + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bIdentical'))) .toEqual(1 === '1'); - expect(evaluator.evaluateNode(findVar(expressions, 'bNotIdentical').initializer)) + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bNotIdentical'))) .toEqual(1 !== '1'); - expect(evaluator.evaluateNode(findVar(expressions, 'bLessThan').initializer)).toEqual(1 < 2); - expect(evaluator.evaluateNode(findVar(expressions, 'bGreaterThan').initializer)).toEqual(1 > 2); - expect(evaluator.evaluateNode(findVar(expressions, 'bLessThanEqual').initializer)) + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLessThan'))).toEqual(1 < 2); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bGreaterThan'))).toEqual(1 > 2); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLessThanEqual'))) .toEqual(1 <= 2); - expect(evaluator.evaluateNode(findVar(expressions, 'bGreaterThanEqual').initializer)) + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bGreaterThanEqual'))) .toEqual(1 >= 2); - expect(evaluator.evaluateNode(findVar(expressions, 'bShiftLeft').initializer)).toEqual(1 << 2); - expect(evaluator.evaluateNode(findVar(expressions, 'bShiftRight').initializer)) - .toEqual(-1 >> 2); - expect(evaluator.evaluateNode(findVar(expressions, 'bShiftRightU').initializer)) + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftLeft'))).toEqual(1 << 2); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftRight'))).toEqual(-1 >> 2); + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftRightU'))) .toEqual(-1 >>> 2); }); it('should report recursive references as symbolic', () => { const expressions = program.getSourceFile('expressions.ts'); - expect(evaluator.evaluateNode(findVar(expressions, 'recursiveA').initializer)) + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'recursiveA'))) .toEqual({__symbolic: 'reference', name: 'recursiveB'}); - expect(evaluator.evaluateNode(findVar(expressions, 'recursiveB').initializer)) + expect(evaluator.evaluateNode(findVarInitializer(expressions, 'recursiveB'))) .toEqual({__symbolic: 'reference', name: 'recursiveA'}); }); it('should correctly handle special cases for CONST_EXPR', () => { const const_expr = program.getSourceFile('const_expr.ts'); - expect(evaluator.evaluateNode(findVar(const_expr, 'bTrue').initializer)).toEqual(true); - expect(evaluator.evaluateNode(findVar(const_expr, 'bFalse').initializer)).toEqual(false); + expect(evaluator.evaluateNode(findVarInitializer(const_expr, 'bTrue'))).toEqual(true); + expect(evaluator.evaluateNode(findVarInitializer(const_expr, 'bFalse'))).toEqual(false); }); it('should resolve a forwardRef', () => { const forwardRef = program.getSourceFile('forwardRef.ts'); - expect(evaluator.evaluateNode(findVar(forwardRef, 'bTrue').initializer)).toEqual(true); - expect(evaluator.evaluateNode(findVar(forwardRef, 'bFalse').initializer)).toEqual(false); + expect(evaluator.evaluateNode(findVarInitializer(forwardRef, 'bTrue'))).toEqual(true); + expect(evaluator.evaluateNode(findVarInitializer(forwardRef, 'bFalse'))).toEqual(false); }); it('should return new expressions', () => { symbols.define('Value', {__symbolic: 'reference', module: './classes', name: 'Value'}); evaluator = new Evaluator(symbols, new Map()); const newExpression = program.getSourceFile('newExpression.ts'); - expect(evaluator.evaluateNode(findVar(newExpression, 'someValue').initializer)).toEqual({ + expect(evaluator.evaluateNode(findVarInitializer(newExpression, 'someValue'))).toEqual({ __symbolic: 'new', expression: {__symbolic: 'reference', name: 'Value', module: './classes'}, arguments: ['name', 12] }); - expect(evaluator.evaluateNode(findVar(newExpression, 'complex').initializer)).toEqual({ + expect(evaluator.evaluateNode(findVarInitializer(newExpression, 'complex'))).toEqual({ __symbolic: 'new', expression: {__symbolic: 'reference', name: 'Value', module: './classes'}, arguments: ['name', 12] @@ -162,8 +161,8 @@ describe('Evaluator', () => { it('should support referene to a declared module type', () => { const declared = program.getSourceFile('declared.ts'); - const aDecl = findVar(declared, 'a'); - expect(evaluator.evaluateNode(aDecl.type)).toEqual({ + const aDecl = findVar(declared, 'a') !; + expect(evaluator.evaluateNode(aDecl.type !)).toEqual({ __symbolic: 'select', expression: {__symbolic: 'reference', name: 'Foo'}, member: 'A' @@ -172,28 +171,28 @@ describe('Evaluator', () => { it('should return errors for unsupported expressions', () => { const errors = program.getSourceFile('errors.ts'); - const fDecl = findVar(errors, 'f'); - expect(evaluator.evaluateNode(fDecl.initializer)) + const fDecl = findVar(errors, 'f') !; + expect(evaluator.evaluateNode(fDecl.initializer !)) .toEqual( {__symbolic: 'error', message: 'Function call not supported', line: 1, character: 12}); - const eDecl = findVar(errors, 'e'); - expect(evaluator.evaluateNode(eDecl.type)).toEqual({ + const eDecl = findVar(errors, 'e') !; + expect(evaluator.evaluateNode(eDecl.type !)).toEqual({ __symbolic: 'error', message: 'Could not resolve type', line: 2, character: 11, context: {typeName: 'NotFound'} }); - const sDecl = findVar(errors, 's'); - expect(evaluator.evaluateNode(sDecl.initializer)).toEqual({ + const sDecl = findVar(errors, 's') !; + expect(evaluator.evaluateNode(sDecl.initializer !)).toEqual({ __symbolic: 'error', message: 'Name expected', line: 3, character: 14, context: {received: '1'} }); - const tDecl = findVar(errors, 't'); - expect(evaluator.evaluateNode(tDecl.initializer)).toEqual({ + const tDecl = findVar(errors, 't') !; + expect(evaluator.evaluateNode(tDecl.initializer !)).toEqual({ __symbolic: 'error', message: 'Expression form not supported', line: 4, @@ -204,14 +203,14 @@ describe('Evaluator', () => { it('should be able to fold an array spread', () => { const expressions = program.getSourceFile('expressions.ts'); symbols.define('arr', [1, 2, 3, 4]); - const arrSpread = findVar(expressions, 'arrSpread'); - expect(evaluator.evaluateNode(arrSpread.initializer)).toEqual([0, 1, 2, 3, 4, 5]); + const arrSpread = findVar(expressions, 'arrSpread') !; + expect(evaluator.evaluateNode(arrSpread.initializer !)).toEqual([0, 1, 2, 3, 4, 5]); }); it('should be able to produce a spread expression', () => { const expressions = program.getSourceFile('expressions.ts'); - const arrSpreadRef = findVar(expressions, 'arrSpreadRef'); - expect(evaluator.evaluateNode(arrSpreadRef.initializer)).toEqual([ + const arrSpreadRef = findVar(expressions, 'arrSpreadRef') !; + expect(evaluator.evaluateNode(arrSpreadRef.initializer !)).toEqual([ 0, {__symbolic: 'spread', expression: {__symbolic: 'reference', name: 'arrImport'}}, 5 ]); }); @@ -220,8 +219,8 @@ describe('Evaluator', () => { const source = sourceFileOf(` export var a = new f; `); - const expr = findVar(source, 'a'); - expect(evaluator.evaluateNode(expr.initializer)) + const expr = findVar(source, 'a') !; + expect(evaluator.evaluateNode(expr.initializer !)) .toEqual({__symbolic: 'new', expression: {__symbolic: 'reference', name: 'f'}}); }); }); diff --git a/packages/tsc-wrapped/test/symbols.spec.ts b/packages/tsc-wrapped/test/symbols.spec.ts index 5f2dc5b9fc..9b911ab4f2 100644 --- a/packages/tsc-wrapped/test/symbols.spec.ts +++ b/packages/tsc-wrapped/test/symbols.spec.ts @@ -17,7 +17,7 @@ describe('Symbols', () => { let symbols: Symbols; const someValue = 'some-value'; - beforeEach(() => symbols = new Symbols(null)); + beforeEach(() => symbols = new Symbols(null as any as ts.SourceFile)); it('should be able to add a symbol', () => symbols.define('someSymbol', someValue)); @@ -110,7 +110,7 @@ describe('Symbols', () => { } return false; }; - ts.forEachChild(core, visit); + ts.forEachChild(core !, visit); }); }); diff --git a/packages/tsc-wrapped/test/typescript.mocks.ts b/packages/tsc-wrapped/test/typescript.mocks.ts index ef1385294a..fbd35bdd5f 100644 --- a/packages/tsc-wrapped/test/typescript.mocks.ts +++ b/packages/tsc-wrapped/test/typescript.mocks.ts @@ -21,7 +21,7 @@ export class Host implements ts.LanguageServiceHost { getScriptVersion(fileName: string): string { return this.version.toString(); } - getScriptSnapshot(fileName: string): ts.IScriptSnapshot { + getScriptSnapshot(fileName: string): ts.IScriptSnapshot|undefined { const content = this.getFileContent(fileName); if (content) return ts.ScriptSnapshot.fromString(content); } @@ -40,7 +40,7 @@ export class Host implements ts.LanguageServiceHost { this.version++; } - private getFileContent(fileName: string): string { + private getFileContent(fileName: string): string|undefined { if (this.overrides.has(fileName)) { return this.overrides.get(fileName); } @@ -69,9 +69,9 @@ export class MockNode implements ts.Node { constructor( public kind: ts.SyntaxKind = ts.SyntaxKind.Identifier, public flags: ts.NodeFlags = 0, public pos: number = 0, public end: number = 0) {} - getSourceFile(): ts.SourceFile { return null; } + getSourceFile(): ts.SourceFile { return null as any as ts.SourceFile; } getChildCount(sourceFile?: ts.SourceFile): number { return 0 } - getChildAt(index: number, sourceFile?: ts.SourceFile): ts.Node { return null; } + getChildAt(index: number, sourceFile?: ts.SourceFile): ts.Node { return null as any as ts.Node; } getChildren(sourceFile?: ts.SourceFile): ts.Node[] { return []; } getStart(sourceFile?: ts.SourceFile): number { return 0; } getFullStart(): number { return 0; } @@ -81,10 +81,10 @@ export class MockNode implements ts.Node { getLeadingTriviaWidth(sourceFile?: ts.SourceFile): number { return 0; } getFullText(sourceFile?: ts.SourceFile): string { return ''; } getText(sourceFile?: ts.SourceFile): string { return ''; } - getFirstToken(sourceFile?: ts.SourceFile): ts.Node { return null; } - getLastToken(sourceFile?: ts.SourceFile): ts.Node { return null; } + getFirstToken(sourceFile?: ts.SourceFile): ts.Node { return null as any as ts.Node; } + getLastToken(sourceFile?: ts.SourceFile): ts.Node { return null as any as ts.Node; } forEachChild(cbNode: (node: ts.Node) => T, cbNodeArray?: (nodes: ts.Node[]) => T): T { - return null; + return null as any as T; } } @@ -162,22 +162,28 @@ export function allChildren(node: ts.Node, cb: (node: ts.Node) => T): T { }) } -export function findClass(sourceFile: ts.SourceFile, name: string): ts.ClassDeclaration { +export function findClass(sourceFile: ts.SourceFile, name: string): ts.ClassDeclaration|undefined { return ts.forEachChild( sourceFile, node => isClass(node) && isNamed(node.name, name) ? node : undefined); } -export function findVar(sourceFile: ts.SourceFile, name: string): ts.VariableDeclaration { +export function findVar(sourceFile: ts.SourceFile, name: string): ts.VariableDeclaration|undefined { return allChildren( sourceFile, node => isVar(node) && isNamed(node.name, name) ? node : undefined); } +export function findVarInitializer(sourceFile: ts.SourceFile, name: string): ts.Expression { + const v = findVar(sourceFile, name); + expect(v && v.initializer).toBeDefined(); + return v !.initializer !; +} + export function isClass(node: ts.Node): node is ts.ClassDeclaration { return node.kind === ts.SyntaxKind.ClassDeclaration; } -export function isNamed(node: ts.Node, name: string): node is ts.Identifier { - return node.kind === ts.SyntaxKind.Identifier && (node).text === name; +export function isNamed(node: ts.Node | undefined, name: string): node is ts.Identifier { + return !!node && node.kind === ts.SyntaxKind.Identifier && (node).text === name; } export function isVar(node: ts.Node): node is ts.VariableDeclaration { diff --git a/packages/tsc-wrapped/tsconfig.json b/packages/tsc-wrapped/tsconfig.json index c687e4971f..89b5d454cf 100644 --- a/packages/tsc-wrapped/tsconfig.json +++ b/packages/tsc-wrapped/tsconfig.json @@ -7,6 +7,7 @@ "module": "commonjs", "moduleResolution": "node", "outDir": "../../dist/all/@angular/tsc-wrapped", + "strictNullChecks": true, "noImplicitAny": true, "noFallthroughCasesInSwitch": true, "paths": {