refactor(tsc-wrapped): update tsc-wrapped to pass strictNullCheck (#18160)

PR Close #18160
This commit is contained in:
Chuck Jazdzewski 2017-07-18 12:52:48 -07:00 committed by Miško Hevery
parent 619e625ee2
commit abee785821
18 changed files with 269 additions and 230 deletions

View File

@ -419,6 +419,11 @@ if [[ ${BUILD_TOOLS} == true || ${BUILD_ALL} == true ]]; then
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json $(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/package.json ./dist/packages-dist/tsc-wrapped
cp ./packages/tsc-wrapped/README.md ./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 fi
for PACKAGE in ${PACKAGES[@]} for PACKAGE in ${PACKAGES[@]}

View File

@ -100,8 +100,8 @@ export class CodeGenerator {
} }
const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, { const {compiler: aotCompiler} = compiler.createAotCompiler(ngCompilerHost, {
translations: transContent, translations: transContent,
i18nFormat: cliOptions.i18nFormat, i18nFormat: cliOptions.i18nFormat || undefined,
locale: cliOptions.locale, missingTranslation, locale: cliOptions.locale || undefined, missingTranslation,
enableLegacyTemplate: options.enableLegacyTemplate !== false, enableLegacyTemplate: options.enableLegacyTemplate !== false,
enableSummariesForJit: options.enableSummariesForJit !== false, enableSummariesForJit: options.enableSummariesForJit !== false,
}); });

View File

@ -405,7 +405,7 @@ export class MockMetadataBundlerHost implements MetadataBundlerHost {
constructor(private host: ts.CompilerHost) {} constructor(private host: ts.CompilerHost) {}
getMetadataFor(moduleName: string): ModuleMetadata { getMetadataFor(moduleName: string): ModuleMetadata|undefined {
const source = this.host.getSourceFile(moduleName + '.ts', ts.ScriptTarget.Latest); const source = this.host.getSourceFile(moduleName + '.ts', ts.ScriptTarget.Latest);
return this.collector.getMetadata(source); return this.collector.getMetadata(source);
} }

View File

@ -70,7 +70,9 @@ export interface BundledModule {
privates: BundlePrivateEntry[]; privates: BundlePrivateEntry[];
} }
export interface MetadataBundlerHost { getMetadataFor(moduleName: string): ModuleMetadata; } export interface MetadataBundlerHost {
getMetadataFor(moduleName: string): ModuleMetadata|undefined;
}
type StaticsMetadata = { type StaticsMetadata = {
[name: string]: MetadataValue | FunctionMetadata; [name: string]: MetadataValue | FunctionMetadata;
@ -78,7 +80,7 @@ type StaticsMetadata = {
export class MetadataBundler { export class MetadataBundler {
private symbolMap = new Map<string, Symbol>(); private symbolMap = new Map<string, Symbol>();
private metadataCache = new Map<string, ModuleMetadata>(); private metadataCache = new Map<string, ModuleMetadata|undefined>();
private exports = new Map<string, Symbol[]>(); private exports = new Map<string, Symbol[]>();
private rootModule: string; private rootModule: string;
private exported: Set<Symbol>; private exported: Set<Symbol>;
@ -98,14 +100,14 @@ export class MetadataBundler {
const privates = Array.from(this.symbolMap.values()) const privates = Array.from(this.symbolMap.values())
.filter(s => s.referenced && s.isPrivate) .filter(s => s.referenced && s.isPrivate)
.map(s => ({ .map(s => ({
privateName: s.privateName, privateName: s.privateName !,
name: s.declaration.name, name: s.declaration !.name,
module: s.declaration.module module: s.declaration !.module
})); }));
const origins = Array.from(this.symbolMap.values()) const origins = Array.from(this.symbolMap.values())
.filter(s => s.referenced && !s.reexport) .filter(s => s.referenced && !s.reexport)
.reduce<{[name: string]: string}>((p, s) => { .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; return p;
}, {}); }, {});
const exports = this.getReExports(exportedSymbols); const exports = this.getReExports(exportedSymbols);
@ -114,7 +116,7 @@ export class MetadataBundler {
__symbolic: 'module', __symbolic: 'module',
version: VERSION, version: VERSION,
exports: exports.length ? exports : undefined, metadata, origins, exports: exports.length ? exports : undefined, metadata, origins,
importAs: this.importAs importAs: this.importAs !
}, },
privates privates
}; };
@ -124,7 +126,7 @@ export class MetadataBundler {
return resolveModule(importName, from); return resolveModule(importName, from);
} }
private getMetadata(moduleName: string): ModuleMetadata { private getMetadata(moduleName: string): ModuleMetadata|undefined {
let result = this.metadataCache.get(moduleName); let result = this.metadataCache.get(moduleName);
if (!result) { if (!result) {
if (moduleName.startsWith('.')) { if (moduleName.startsWith('.')) {
@ -138,7 +140,7 @@ export class MetadataBundler {
private exportAll(moduleName: string): Symbol[] { private exportAll(moduleName: string): Symbol[] {
const module = this.getMetadata(moduleName); const module = this.getMetadata(moduleName);
let result: Symbol[] = this.exports.get(moduleName); let result = this.exports.get(moduleName);
if (result) { if (result) {
return result; return result;
@ -148,7 +150,7 @@ export class MetadataBundler {
const exportSymbol = (exportedSymbol: Symbol, exportAs: string) => { const exportSymbol = (exportedSymbol: Symbol, exportAs: string) => {
const symbol = this.symbolOf(moduleName, exportAs); const symbol = this.symbolOf(moduleName, exportAs);
result.push(symbol); result !.push(symbol);
exportedSymbol.reexportedAs = symbol; exportedSymbol.reexportedAs = symbol;
symbol.exports = exportedSymbol; symbol.exports = exportedSymbol;
}; };
@ -266,7 +268,7 @@ export class MetadataBundler {
name = newPrivateName(); name = newPrivateName();
symbol.privateName = name; symbol.privateName = name;
} }
result[name] = symbol.value; result[name] = symbol.value !;
} }
}); });
@ -279,9 +281,10 @@ export class MetadataBundler {
const exportAlls = new Set<string>(); const exportAlls = new Set<string>();
for (const symbol of exportedSymbols) { for (const symbol of exportedSymbols) {
if (symbol.reexport) { 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; const module = declaration.module;
if (declaration.name == '*') { if (declaration !.name == '*') {
// Reexport all the symbols. // Reexport all the symbols.
exportAlls.add(declaration.module); exportAlls.add(declaration.module);
} else { } else {
@ -304,11 +307,13 @@ export class MetadataBundler {
} }
private convertSymbol(symbol: Symbol) { 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) { if (!canonicalSymbol.referenced) {
canonicalSymbol.referenced = true; 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); const module = this.getMetadata(declaration.module);
if (module) { if (module) {
const value = module.metadata[declaration.name]; const value = module.metadata[declaration.name];
@ -336,10 +341,10 @@ export class MetadataBundler {
return { return {
__symbolic: 'class', __symbolic: 'class',
arity: value.arity, arity: value.arity,
extends: this.convertExpression(moduleName, value.extends), extends: this.convertExpression(moduleName, value.extends) !,
decorators: decorators:
value.decorators && value.decorators.map(d => this.convertExpression(moduleName, d)), value.decorators && value.decorators.map(d => this.convertExpression(moduleName, d) !),
members: this.convertMembers(moduleName, value.members), members: this.convertMembers(moduleName, value.members !),
statics: value.statics && this.convertStatics(moduleName, value.statics) statics: value.statics && this.convertStatics(moduleName, value.statics)
}; };
} }
@ -356,11 +361,11 @@ export class MetadataBundler {
private convertMember(moduleName: string, member: MemberMetadata) { private convertMember(moduleName: string, member: MemberMetadata) {
const result: MemberMetadata = {__symbolic: member.__symbolic}; const result: MemberMetadata = {__symbolic: member.__symbolic};
result.decorators = 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)) { if (isMethodMetadata(member)) {
(result as MethodMetadata).parameterDecorators = member.parameterDecorators && (result as MethodMetadata).parameterDecorators = member.parameterDecorators &&
member.parameterDecorators.map( 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 (isConstructorMetadata(member)) {
if (member.parameters) { if (member.parameters) {
(result as ConstructorMetadata).parameters = (result as ConstructorMetadata).parameters =
@ -397,7 +402,7 @@ export class MetadataBundler {
return this.convertError(moduleName, value); return this.convertError(moduleName, value);
} }
if (isMetadataSymbolicExpression(value)) { if (isMetadataSymbolicExpression(value)) {
return this.convertExpression(moduleName, value); return this.convertExpression(moduleName, value) !;
} }
if (Array.isArray(value)) { if (Array.isArray(value)) {
return value.map(v => this.convertValue(moduleName, v)); return value.map(v => this.convertValue(moduleName, v));
@ -413,8 +418,8 @@ export class MetadataBundler {
} }
private convertExpression( private convertExpression(
moduleName: string, value: MetadataSymbolicExpression|MetadataError| moduleName: string, value: MetadataSymbolicExpression|MetadataError|null|
undefined): MetadataSymbolicExpression|MetadataError|undefined { undefined): MetadataSymbolicExpression|MetadataError|undefined|null {
if (value) { if (value) {
switch (value.__symbolic) { switch (value.__symbolic) {
case 'error': case 'error':
@ -439,9 +444,9 @@ export class MetadataBundler {
} }
private convertReference(moduleName: string, value: MetadataSymbolicReferenceExpression): private convertReference(moduleName: string, value: MetadataSymbolicReferenceExpression):
MetadataSymbolicReferenceExpression|MetadataError { MetadataSymbolicReferenceExpression|MetadataError|undefined {
const createReference = (symbol: Symbol): MetadataSymbolicReferenceExpression => { const createReference = (symbol: Symbol): MetadataSymbolicReferenceExpression => {
const declaration = symbol.declaration; const declaration = symbol.declaration !;
if (declaration.module.startsWith('.')) { if (declaration.module.startsWith('.')) {
// Reference to a symbol defined in the module. Ensure it is converted then return a // Reference to a symbol defined in the module. Ensure it is converted then return a
// references to the final symbol. // references to the final symbol.
@ -450,11 +455,11 @@ export class MetadataBundler {
__symbolic: 'reference', __symbolic: 'reference',
get name() { get name() {
// Resolved lazily because private names are assigned late. // Resolved lazily because private names are assigned late.
const canonicalSymbol = symbol.canonicalSymbol; const canonicalSymbol = symbol.canonicalSymbol !;
if (canonicalSymbol.isPrivate == null) { if (canonicalSymbol.isPrivate == null) {
throw Error('Invalid state: isPrivate was not initialized'); throw Error('Invalid state: isPrivate was not initialized');
} }
return canonicalSymbol.isPrivate ? canonicalSymbol.privateName : canonicalSymbol.name; return canonicalSymbol.isPrivate ? canonicalSymbol.privateName ! : canonicalSymbol.name;
} }
}; };
} else { } else {
@ -564,7 +569,7 @@ export class CompilerHostAdapter implements MetadataBundlerHost {
constructor(private host: ts.CompilerHost) {} constructor(private host: ts.CompilerHost) {}
getMetadataFor(fileName: string): ModuleMetadata { getMetadataFor(fileName: string): ModuleMetadata|undefined {
const sourceFile = this.host.getSourceFile(fileName + '.ts', ts.ScriptTarget.Latest); const sourceFile = this.host.getSourceFile(fileName + '.ts', ts.ScriptTarget.Latest);
return this.collector.getMetadata(sourceFile); return this.collector.getMetadata(sourceFile);
} }

View File

@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
export class CliOptions { export class CliOptions {
public basePath: string; public basePath: string|null;
constructor({basePath = null}: {basePath?: string}) { this.basePath = basePath; } constructor({basePath = null}: {basePath?: string | null}) { this.basePath = basePath; }
} }
export class I18nExtractionCliOptions extends CliOptions { export class I18nExtractionCliOptions extends CliOptions {
@ -28,18 +28,18 @@ export class I18nExtractionCliOptions extends CliOptions {
} }
export class NgcCliOptions extends CliOptions { export class NgcCliOptions extends CliOptions {
public i18nFormat: string; public i18nFormat: string|null;
public i18nFile: string; public i18nFile: string|null;
public locale: string; public locale: string|null;
public missingTranslation: string; public missingTranslation: string|null;
constructor({i18nFormat = null, i18nFile = null, locale = null, missingTranslation = null, constructor({i18nFormat = null, i18nFile = null, locale = null, missingTranslation = null,
basePath = null}: { basePath = null}: {
i18nFormat?: string, i18nFormat?: string | null,
i18nFile?: string, i18nFile?: string|null,
locale?: string, locale?: string|null,
missingTranslation?: string, missingTranslation?: string|null,
basePath?: string basePath?: string|null
}) { }) {
super({basePath: basePath}); super({basePath: basePath});
this.i18nFormat = i18nFormat; this.i18nFormat = i18nFormat;

View File

@ -54,13 +54,13 @@ export class MetadataCollector {
* Returns a JSON.stringify friendly form describing the decorators of the exported classes from * 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. * 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 locals = new Symbols(sourceFile);
const nodeMap = const nodeMap =
new Map<MetadataValue|ClassMetadata|InterfaceMetadata|FunctionMetadata, ts.Node>(); new Map<MetadataValue|ClassMetadata|InterfaceMetadata|FunctionMetadata, ts.Node>();
const evaluator = new Evaluator(locals, nodeMap, this.options); const evaluator = new Evaluator(locals, nodeMap, this.options);
let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined; let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined;
let exports: ModuleExportMetadata[]; let exports: ModuleExportMetadata[]|undefined = undefined;
function objFromDecorator(decoratorNode: ts.Decorator): MetadataSymbolicExpression { function objFromDecorator(decoratorNode: ts.Decorator): MetadataSymbolicExpression {
return <MetadataSymbolicExpression>evaluator.evaluateNode(decoratorNode.expression); return <MetadataSymbolicExpression>evaluator.evaluateNode(decoratorNode.expression);
@ -79,7 +79,7 @@ export class MetadataCollector {
function maybeGetSimpleFunction( function maybeGetSimpleFunction(
functionDeclaration: ts.FunctionDeclaration | functionDeclaration: ts.FunctionDeclaration |
ts.MethodDeclaration): {func: FunctionMetadata, name: string}|undefined { 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 = <ts.Identifier>functionDeclaration.name; const nameNode = <ts.Identifier>functionDeclaration.name;
const functionName = nameNode.text; const functionName = nameNode.text;
const functionBody = functionDeclaration.body; const functionBody = functionDeclaration.body;
@ -107,7 +107,8 @@ export class MetadataCollector {
function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata { function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata {
const result: ClassMetadata = {__symbolic: 'class'}; const result: ClassMetadata = {__symbolic: 'class'};
function getDecorators(decorators: ts.Decorator[]): MetadataSymbolicExpression[] { function getDecorators(decorators: ts.Decorator[] | undefined): MetadataSymbolicExpression[]|
undefined {
if (decorators && decorators.length) if (decorators && decorators.length)
return decorators.map(decorator => objFromDecorator(decorator)); return decorators.map(decorator => objFromDecorator(decorator));
return undefined; return undefined;
@ -145,7 +146,7 @@ export class MetadataCollector {
} }
// member decorators // member decorators
let members: MetadataMap = null; let members: MetadataMap|null = null;
function recordMember(name: string, metadata: MemberMetadata) { function recordMember(name: string, metadata: MemberMetadata) {
if (!members) members = {}; if (!members) members = {};
const data = members.hasOwnProperty(name) ? members[name] : []; const data = members.hasOwnProperty(name) ? members[name] : [];
@ -154,7 +155,7 @@ export class MetadataCollector {
} }
// static member // static member
let statics: {[name: string]: MetadataValue | FunctionMetadata} = null; let statics: {[name: string]: MetadataValue | FunctionMetadata}|null = null;
function recordStaticMember(name: string, value: MetadataValue | FunctionMetadata) { function recordStaticMember(name: string, value: MetadataValue | FunctionMetadata) {
if (!statics) statics = {}; if (!statics) statics = {};
statics[name] = value; statics[name] = value;
@ -176,7 +177,8 @@ export class MetadataCollector {
} }
const methodDecorators = getDecorators(method.decorators); const methodDecorators = getDecorators(method.decorators);
const parameters = method.parameters; const parameters = method.parameters;
const parameterDecoratorData: (MetadataSymbolicExpression | MetadataError)[][] = []; const parameterDecoratorData:
((MetadataSymbolicExpression | MetadataError)[] | undefined)[] = [];
const parametersData: const parametersData:
(MetadataSymbolicReferenceExpression | MetadataError | (MetadataSymbolicReferenceExpression | MetadataError |
MetadataSymbolicSelectExpression | null)[] = []; MetadataSymbolicSelectExpression | null)[] = [];
@ -254,7 +256,8 @@ export class MetadataCollector {
const {moduleSpecifier, exportClause} = exportDeclaration; const {moduleSpecifier, exportClause} = exportDeclaration;
if (!moduleSpecifier) { 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 exportedAs = spec.name.text;
const name = (spec.propertyName || spec.name).text; const name = (spec.propertyName || spec.name).text;
exportMap.set(name, exportedAs); 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 = const isExported =
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration | (node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration |
ts.EnumDeclaration) => isExport(node) || isExportedIdentifier(node.name); ts.EnumDeclaration) => isExport(node) || isExportedIdentifier(node.name);
const exportedIdentifierName = (identifier: ts.Identifier) => const exportedIdentifierName = (identifier?: ts.Identifier) =>
exportMap.get(identifier.text) || identifier.text; identifier && (exportMap.get(identifier.text) || identifier.text);
const exportedName = const exportedName =
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration | (node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration |
ts.EnumDeclaration) => exportedIdentifierName(node.name); ts.EnumDeclaration) => exportedIdentifierName(node.name);
@ -358,8 +362,11 @@ export class MetadataCollector {
const classDeclaration = <ts.ClassDeclaration>node; const classDeclaration = <ts.ClassDeclaration>node;
if (classDeclaration.name) { if (classDeclaration.name) {
if (isExported(classDeclaration)) { if (isExported(classDeclaration)) {
if (!metadata) metadata = {}; const name = exportedName(classDeclaration);
metadata[exportedName(classDeclaration)] = classMetadataOf(classDeclaration); if (name) {
if (!metadata) metadata = {};
metadata[name] = classMetadataOf(classDeclaration);
}
} }
} }
// Otherwise don't record metadata for the class. // Otherwise don't record metadata for the class.
@ -368,8 +375,11 @@ export class MetadataCollector {
case ts.SyntaxKind.InterfaceDeclaration: case ts.SyntaxKind.InterfaceDeclaration:
const interfaceDeclaration = <ts.InterfaceDeclaration>node; const interfaceDeclaration = <ts.InterfaceDeclaration>node;
if (interfaceDeclaration.name && isExported(interfaceDeclaration)) { if (interfaceDeclaration.name && isExported(interfaceDeclaration)) {
if (!metadata) metadata = {}; const name = exportedName(interfaceDeclaration);
metadata[exportedName(interfaceDeclaration)] = {__symbolic: 'interface'}; if (name) {
if (!metadata) metadata = {};
metadata[name] = {__symbolic: 'interface'};
}
} }
break; break;
@ -378,11 +388,13 @@ export class MetadataCollector {
// names substitution will be performed by the StaticReflector. // names substitution will be performed by the StaticReflector.
const functionDeclaration = <ts.FunctionDeclaration>node; const functionDeclaration = <ts.FunctionDeclaration>node;
if (isExported(functionDeclaration) && functionDeclaration.name) { if (isExported(functionDeclaration) && functionDeclaration.name) {
if (!metadata) metadata = {};
const name = exportedName(functionDeclaration); const name = exportedName(functionDeclaration);
const maybeFunc = maybeGetSimpleFunction(functionDeclaration); const maybeFunc = maybeGetSimpleFunction(functionDeclaration);
metadata[name] = if (name) {
maybeFunc ? recordEntry(maybeFunc.func, node) : {__symbolic: 'function'}; if (!metadata) metadata = {};
metadata[name] =
maybeFunc ? recordEntry(maybeFunc.func, node) : {__symbolic: 'function'};
}
} }
break; break;
@ -400,7 +412,7 @@ export class MetadataCollector {
} else { } else {
enumValue = evaluator.evaluateNode(member.initializer); enumValue = evaluator.evaluateNode(member.initializer);
} }
let name: string = undefined; let name: string|undefined = undefined;
if (member.name.kind == ts.SyntaxKind.Identifier) { if (member.name.kind == ts.SyntaxKind.Identifier) {
const identifier = <ts.Identifier>member.name; const identifier = <ts.Identifier>member.name;
name = identifier.text; name = identifier.text;
@ -424,8 +436,10 @@ export class MetadataCollector {
} }
} }
if (writtenMembers) { if (writtenMembers) {
if (!metadata) metadata = {}; if (enumName) {
metadata[enumName] = recordEntry(enumValueHolder, node); if (!metadata) metadata = {};
metadata[enumName] = recordEntry(enumValueHolder, node);
}
} }
} }
break; break;
@ -444,8 +458,11 @@ export class MetadataCollector {
let exported = false; let exported = false;
if (isExport(variableStatement) || isExport(variableDeclaration) || if (isExport(variableStatement) || isExport(variableDeclaration) ||
isExportedIdentifier(nameNode)) { isExportedIdentifier(nameNode)) {
if (!metadata) metadata = {}; const name = exportedIdentifierName(nameNode);
metadata[exportedIdentifierName(nameNode)] = recordEntry(varValue, node); if (name) {
if (!metadata) metadata = {};
metadata[name] = recordEntry(varValue, node);
}
exported = true; exported = true;
} }
if (typeof varValue == 'string' || typeof varValue == 'number' || if (typeof varValue == 'string' || typeof varValue == 'number' ||
@ -601,11 +618,11 @@ function validateMetadata(
} }
if (classData.members) { if (classData.members) {
Object.getOwnPropertyNames(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) { if (classData.statics) {
Object.getOwnPropertyNames(classData.statics).forEach(name => { Object.getOwnPropertyNames(classData.statics).forEach(name => {
const staticMember = classData.statics[name]; const staticMember = classData.statics ![name];
if (isFunctionMetadata(staticMember)) { if (isFunctionMetadata(staticMember)) {
validateExpression(staticMember.value); validateExpression(staticMember.value);
} else { } else {
@ -628,7 +645,7 @@ function validateMetadata(
} }
} }
function shouldReportNode(node: ts.Node) { function shouldReportNode(node: ts.Node | undefined) {
if (node) { if (node) {
const nodeStart = node.getStart(); const nodeStart = node.getStart();
return !( return !(

View File

@ -33,10 +33,10 @@ export abstract class DelegatingHost implements ts.CompilerHost {
(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) => (fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) =>
this.delegate.getSourceFile(fileName, languageVersion, onError); this.delegate.getSourceFile(fileName, languageVersion, onError);
getCancellationToken = () => this.delegate.getCancellationToken(); getCancellationToken = () => this.delegate.getCancellationToken !();
getDefaultLibFileName = (options: ts.CompilerOptions) => getDefaultLibFileName = (options: ts.CompilerOptions) =>
this.delegate.getDefaultLibFileName(options); this.delegate.getDefaultLibFileName(options);
getDefaultLibLocation = () => this.delegate.getDefaultLibLocation(); getDefaultLibLocation = () => this.delegate.getDefaultLibLocation !();
writeFile: ts.WriteFileCallback = this.delegate.writeFile; writeFile: ts.WriteFileCallback = this.delegate.writeFile;
getCurrentDirectory = () => this.delegate.getCurrentDirectory(); getCurrentDirectory = () => this.delegate.getCurrentDirectory();
getDirectories = (path: string): string[] => getDirectories = (path: string): string[] =>
@ -46,8 +46,8 @@ export abstract class DelegatingHost implements ts.CompilerHost {
getNewLine = () => this.delegate.getNewLine(); getNewLine = () => this.delegate.getNewLine();
fileExists = (fileName: string) => this.delegate.fileExists(fileName); fileExists = (fileName: string) => this.delegate.fileExists(fileName);
readFile = (fileName: string) => this.delegate.readFile(fileName); readFile = (fileName: string) => this.delegate.readFile(fileName);
trace = (s: string) => this.delegate.trace(s); trace = (s: string) => this.delegate.trace !(s);
directoryExists = (directoryName: string) => this.delegate.directoryExists(directoryName); directoryExists = (directoryName: string) => this.delegate.directoryExists !(directoryName);
} }
const IGNORED_FILES = /\.ngfactory\.js$|\.ngstyle\.js$/; const IGNORED_FILES = /\.ngfactory\.js$|\.ngstyle\.js$/;
@ -79,7 +79,8 @@ export class MetadataWriterHost extends DelegatingHost {
const metadata = const metadata =
this.metadataCollector.getMetadata(collectableFile, !!this.ngOptions.strictMetadataEmit); this.metadataCollector.getMetadata(collectableFile, !!this.ngOptions.strictMetadataEmit);
const metadata1 = this.metadataCollector1.getMetadata(collectableFile, false); 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) { if (metadatas.length) {
const metadataText = JSON.stringify(metadatas); const metadataText = JSON.stringify(metadatas);
writeFileSync(path, metadataText, {encoding: 'utf-8'}); writeFileSync(path, metadataText, {encoding: 'utf-8'});
@ -163,7 +164,7 @@ export class SyntheticIndexHost extends DelegatingHost {
normalize(sourceFiles[0].fileName) == this.normalSyntheticIndexName) { normalize(sourceFiles[0].fileName) == this.normalSyntheticIndexName) {
// If we are writing the synthetic index, write the metadata along side. // If we are writing the synthetic index, write the metadata along side.
const metadataName = fileName.replace(DTS, '.metadata.json'); const metadataName = fileName.replace(DTS, '.metadata.json');
writeFileSync(metadataName, this.indexMetadata, 'utf8'); writeFileSync(metadataName, this.indexMetadata, {encoding: 'utf8'});
} }
} }
} }

View File

@ -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) { while (node && node.kind != ts.SyntaxKind.SourceFile) {
node = node.parent; node = node.parent;
} }
@ -79,7 +79,7 @@ function getSourceFileOfNode(node: ts.Node): ts.SourceFile {
export function errorSymbol( export function errorSymbol(
message: string, node?: ts.Node, context?: {[name: string]: string}, message: string, node?: ts.Node, context?: {[name: string]: string},
sourceFile?: ts.SourceFile): MetadataError { sourceFile?: ts.SourceFile): MetadataError {
let result: MetadataError; let result: MetadataError|undefined = undefined;
if (node) { if (node) {
sourceFile = sourceFile || getSourceFileOfNode(node); sourceFile = sourceFile || getSourceFileOfNode(node);
if (sourceFile) { if (sourceFile) {
@ -106,15 +106,16 @@ export class Evaluator {
private symbols: Symbols, private nodeMap: Map<MetadataEntry, ts.Node>, private symbols: Symbols, private nodeMap: Map<MetadataEntry, ts.Node>,
private options: CollectorOptions = {}) {} private options: CollectorOptions = {}) {}
nameOf(node: ts.Node): string|MetadataError { nameOf(node: ts.Node|undefined): string|MetadataError {
if (node.kind == ts.SyntaxKind.Identifier) { if (node && node.kind == ts.SyntaxKind.Identifier) {
return (<ts.Identifier>node).text; return (<ts.Identifier>node).text;
} }
const result = this.evaluateNode(node); const result = node && this.evaluateNode(node);
if (isMetadataError(result) || typeof result === 'string') { if (isMetadataError(result) || typeof result === 'string') {
return result; return result;
} else { } else {
return errorSymbol('Name expected', node, {received: node.getText()}); return errorSymbol(
'Name expected', node, {received: (node && node.getText()) || '<missing>'});
} }
} }
@ -138,7 +139,7 @@ export class Evaluator {
return this.isFoldableWorker(node, new Map<ts.Node, boolean>()); return this.isFoldableWorker(node, new Map<ts.Node, boolean>());
} }
private isFoldableWorker(node: ts.Node, folding: Map<ts.Node, boolean>): boolean { private isFoldableWorker(node: ts.Node|undefined, folding: Map<ts.Node, boolean>): boolean {
if (node) { if (node) {
switch (node.kind) { switch (node.kind) {
case ts.SyntaxKind.ObjectLiteralExpression: case ts.SyntaxKind.ObjectLiteralExpression:
@ -518,11 +519,11 @@ export class Evaluator {
if (isDefined(operand) && isPrimitive(operand)) { if (isDefined(operand) && isPrimitive(operand)) {
switch (prefixUnaryExpression.operator) { switch (prefixUnaryExpression.operator) {
case ts.SyntaxKind.PlusToken: case ts.SyntaxKind.PlusToken:
return +operand; return +(operand as any);
case ts.SyntaxKind.MinusToken: case ts.SyntaxKind.MinusToken:
return -operand; return -(operand as any);
case ts.SyntaxKind.TildeToken: case ts.SyntaxKind.TildeToken:
return ~operand; return ~(operand as any);
case ts.SyntaxKind.ExclamationToken: case ts.SyntaxKind.ExclamationToken:
return !operand; return !operand;
} }
@ -661,8 +662,8 @@ function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment {
return node.kind == ts.SyntaxKind.PropertyAssignment; return node.kind == ts.SyntaxKind.PropertyAssignment;
} }
const empty = [] as ts.NodeArray<any>; const empty = ts.createNodeArray<any>();
function arrayOrEmpty<T extends ts.Node>(v: ts.NodeArray<T>): ts.NodeArray<T> { function arrayOrEmpty<T extends ts.Node>(v: ts.NodeArray<T>| undefined): ts.NodeArray<T> {
return v || empty; return v || empty;
} }

View File

@ -41,9 +41,9 @@ export function createBundleIndexHost(
return { return {
host, host,
errors: [{ errors: [{
file: null, file: null as any as ts.SourceFile,
start: null, start: null as any as number,
length: null, length: null as any as number,
messageText: messageText:
'Angular compiler option "flatModuleIndex" requires one and only one .ts file in the "files" field.', 'Angular compiler option "flatModuleIndex" requires one and only one .ts file in the "files" field.',
category: ts.DiagnosticCategory.Error, category: ts.DiagnosticCategory.Error,
@ -58,7 +58,7 @@ export function createBundleIndexHost(
const metadataBundle = bundler.getMetadataBundle(); const metadataBundle = bundler.getMetadataBundle();
const metadata = JSON.stringify(metadataBundle.metadata); const metadata = JSON.stringify(metadataBundle.metadata);
const name = 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 libraryIndex = `./${path.basename(indexModule)}`;
const content = privateEntriesToIndex(libraryIndex, metadataBundle.privates); const content = privateEntriesToIndex(libraryIndex, metadataBundle.privates);
host = new SyntheticIndexHost(host, {name, content, metadata}); host = new SyntheticIndexHost(host, {name, content, metadata});
@ -201,7 +201,7 @@ if (require.main === module) {
const project = options.project || '.'; const project = options.project || '.';
// TODO(alexeagle): command line should be TSC-compatible, remove "CliOptions" here // TODO(alexeagle): command line should be TSC-compatible, remove "CliOptions" here
const cliOptions = new CliOptions(require('minimist')(args)); const cliOptions = new CliOptions(require('minimist')(args));
main(project, cliOptions, null, options) main(project, cliOptions, undefined, options)
.then((exitCode: any) => process.exit(exitCode)) .then((exitCode: any) => process.exit(exitCode))
.catch((e: any) => { .catch((e: any) => {
console.error(e.stack); console.error(e.stack);

View File

@ -73,7 +73,7 @@ export function isMemberMetadata(value: any): value is MemberMetadata {
export interface MethodMetadata extends MemberMetadata { export interface MethodMetadata extends MemberMetadata {
__symbolic: 'constructor'|'method'; __symbolic: 'constructor'|'method';
parameterDecorators?: (MetadataSymbolicExpression|MetadataError)[][]; parameterDecorators?: ((MetadataSymbolicExpression | MetadataError)[]|undefined)[];
} }
export function isMethodMetadata(value: any): value is MethodMetadata { export function isMethodMetadata(value: any): value is MethodMetadata {
return value && (value.__symbolic === 'constructor' || value.__symbolic === 'method'); 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 { export interface ConstructorMetadata extends MethodMetadata {
__symbolic: 'constructor'; __symbolic: 'constructor';
parameters?: (MetadataSymbolicExpression|MetadataError|null)[]; parameters?: (MetadataSymbolicExpression|MetadataError|null|undefined)[];
} }
export function isConstructorMetadata(value: any): value is ConstructorMetadata { export function isConstructorMetadata(value: any): value is ConstructorMetadata {
return value && value.__symbolic === 'constructor'; return value && value.__symbolic === 'constructor';
@ -97,8 +97,8 @@ export function isFunctionMetadata(value: any): value is FunctionMetadata {
return value && value.__symbolic === 'function'; return value && value.__symbolic === 'function';
} }
export type MetadataValue = string | number | boolean | MetadataObject | MetadataArray | export type MetadataValue = string | number | boolean | undefined | null | MetadataObject |
MetadataSymbolicExpression | MetadataError; MetadataArray | MetadataSymbolicExpression | MetadataError;
export interface MetadataObject { [name: string]: MetadataValue; } export interface MetadataObject { [name: string]: MetadataValue; }

View File

@ -49,23 +49,26 @@ export class Symbols {
ts.SyntaxKind.ExternalModuleReference) { ts.SyntaxKind.ExternalModuleReference) {
const externalReference = const externalReference =
<ts.ExternalModuleReference>importEqualsDeclaration.moduleReference; <ts.ExternalModuleReference>importEqualsDeclaration.moduleReference;
// An `import <identifier> = require(<module-specifier>); if (externalReference.expression) {
if (!externalReference.expression.parent) { // An `import <identifier> = require(<module-specifier>);
// The `parent` field of a node is set by the TypeScript binder (run as if (!externalReference.expression.parent) {
// part of the type checker). Setting it here allows us to call `getText()` // The `parent` field of a node is set by the TypeScript binder (run as
// even if the `SourceFile` was not type checked (which looks for `SourceFile` // part of the type checker). Setting it here allows us to call `getText()`
// in the parent chain). This doesn't damage the node as the binder unconditionally // even if the `SourceFile` was not type checked (which looks for `SourceFile`
// sets the parent. // in the parent chain). This doesn't damage the node as the binder unconditionally
externalReference.expression.parent = externalReference; // sets the parent.
externalReference.parent = this.sourceFile as any; 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; break;
case ts.SyntaxKind.ImportDeclaration: case ts.SyntaxKind.ImportDeclaration:
const importDecl = <ts.ImportDeclaration>node; const importDecl = <ts.ImportDeclaration>node;

View File

@ -33,7 +33,7 @@ export class UserError extends Error {
super(message); super(message);
// Required for TS 2.1, see // 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 // 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; const nativeError = new Error(message) as any as Error;
this._nativeError = nativeError; this._nativeError = nativeError;
@ -76,13 +76,14 @@ export function formatDiagnostics(diags: ts.Diagnostic[]): string {
.join('\n'); .join('\n');
} }
export function check(diags: ts.Diagnostic[]) { export function check(diags: ts.Diagnostic[] | undefined) {
if (diags && diags.length && diags[0]) { if (diags && diags.length && diags[0]) {
throw new UserError(formatDiagnostics(diags)); throw new UserError(formatDiagnostics(diags));
} }
} }
export function validateAngularCompilerOptions(options: AngularCompilerOptions): ts.Diagnostic[] { export function validateAngularCompilerOptions(options: AngularCompilerOptions): ts.Diagnostic[]|
undefined {
if (options.annotationsAs) { if (options.annotationsAs) {
switch (options.annotationsAs) { switch (options.annotationsAs) {
case 'decorators': case 'decorators':
@ -90,9 +91,9 @@ export function validateAngularCompilerOptions(options: AngularCompilerOptions):
break; break;
default: default:
return [{ return [{
file: null, file: null as any as ts.SourceFile,
start: null, start: null as any as number,
length: null, length: null as any as number,
messageText: messageText:
'Angular compiler options "annotationsAs" only supports "static fields" and "decorators"', 'Angular compiler options "annotationsAs" only supports "static fields" and "decorators"',
category: ts.DiagnosticCategory.Error, category: ts.DiagnosticCategory.Error,
@ -136,7 +137,7 @@ export class Tsc implements CompilerInterface {
return ts.readConfigFile(project, this.readFile); return ts.readConfigFile(project, this.readFile);
} }
})(); })();
check([error]); check([error !]);
const parsed = const parsed =
ts.parseJsonConfigFileContent(config, this.parseConfigHost, basePath, existingOptions); ts.parseJsonConfigFileContent(config, this.parseConfigHost, basePath, existingOptions);

View File

@ -29,7 +29,7 @@ describe('metadata bundler', () => {
const originalTwo = './src/two/index'; const originalTwo = './src/two/index';
expect(Object.keys(result.metadata.origins) expect(Object.keys(result.metadata.origins)
.sort() .sort()
.map(name => ({name, value: result.metadata.origins[name]}))) .map(name => ({name, value: result.metadata.origins ![name]})))
.toEqual([ .toEqual([
{name: 'ONE_CLASSES', value: originalOne}, {name: 'One', value: originalOne}, {name: 'ONE_CLASSES', value: originalOne}, {name: 'One', value: originalOne},
{name: 'OneMore', value: originalOne}, {name: 'TWO_CLASSES', value: originalTwo}, {name: 'OneMore', value: originalOne}, {name: 'TWO_CLASSES', value: originalTwo},
@ -190,7 +190,7 @@ describe('metadata bundler', () => {
from: 'external_one' 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) {} 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 fileName = path.join(this.dirName, moduleName) + '.ts';
const text = open(this.directory, fileName); const text = open(this.directory, fileName);
if (typeof text == 'string') { if (typeof text == 'string') {

View File

@ -196,13 +196,13 @@ describe('Collector', () => {
beforeEach(() => { beforeEach(() => {
casesFile = program.getSourceFile('/app/cases-data.ts'); 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', () => { it('should provide any reference for an any ctor parameter type', () => {
const casesAny = <ClassMetadata>casesMetadata.metadata['CaseAny']; const casesAny = <ClassMetadata>casesMetadata.metadata['CaseAny'];
expect(casesAny).toBeTruthy(); expect(casesAny).toBeTruthy();
const ctorData = casesAny.members['__ctor__']; const ctorData = casesAny.members !['__ctor__'];
expect(ctorData).toEqual( expect(ctorData).toEqual(
[{__symbolic: 'constructor', parameters: [{__symbolic: 'reference', name: 'any'}]}]); [{__symbolic: 'constructor', parameters: [{__symbolic: 'reference', name: 'any'}]}]);
}); });
@ -265,10 +265,10 @@ describe('Collector', () => {
it('should report an error for references to unexpected types', () => { it('should report an error for references to unexpected types', () => {
const unsupported1 = program.getSourceFile('/unsupported-2.ts'); const unsupported1 = program.getSourceFile('/unsupported-2.ts');
const metadata = collector.getMetadata(unsupported1); const metadata = collector.getMetadata(unsupported1) !;
const barClass = <ClassMetadata>metadata.metadata['Bar']; const barClass = <ClassMetadata>metadata.metadata['Bar'];
const ctor = <ConstructorMetadata>barClass.members['__ctor__'][0]; const ctor = <ConstructorMetadata>barClass.members !['__ctor__'][0];
const parameter = ctor.parameters[0]; const parameter = ctor.parameters ![0];
expect(parameter).toEqual({ expect(parameter).toEqual({
__symbolic: 'error', __symbolic: 'error',
message: 'Reference to non-exported class', message: 'Reference to non-exported class',
@ -280,9 +280,9 @@ describe('Collector', () => {
it('should be able to handle import star type references', () => { it('should be able to handle import star type references', () => {
const importStar = program.getSourceFile('/import-star.ts'); const importStar = program.getSourceFile('/import-star.ts');
const metadata = collector.getMetadata(importStar); const metadata = collector.getMetadata(importStar) !;
const someClass = <ClassMetadata>metadata.metadata['SomeClass']; const someClass = <ClassMetadata>metadata.metadata['SomeClass'];
const ctor = <ConstructorMetadata>someClass.members['__ctor__'][0]; const ctor = <ConstructorMetadata>someClass.members !['__ctor__'][0];
const parameters = ctor.parameters; const parameters = ctor.parameters;
expect(parameters).toEqual([ expect(parameters).toEqual([
{__symbolic: 'reference', module: 'angular2/common', name: 'NgFor'} {__symbolic: 'reference', module: 'angular2/common', name: 'NgFor'}
@ -365,9 +365,9 @@ describe('Collector', () => {
it('should be able to handle import star type references', () => { it('should be able to handle import star type references', () => {
const importStar = program.getSourceFile('/import-star.ts'); const importStar = program.getSourceFile('/import-star.ts');
const metadata = collector.getMetadata(importStar); const metadata = collector.getMetadata(importStar) !;
const someClass = <ClassMetadata>metadata.metadata['SomeClass']; const someClass = <ClassMetadata>metadata.metadata['SomeClass'];
const ctor = <ConstructorMetadata>someClass.members['__ctor__'][0]; const ctor = <ConstructorMetadata>someClass.members !['__ctor__'][0];
const parameters = ctor.parameters; const parameters = ctor.parameters;
expect(parameters).toEqual([ expect(parameters).toEqual([
{__symbolic: 'reference', module: 'angular2/common', name: 'NgFor'} {__symbolic: 'reference', module: 'angular2/common', name: 'NgFor'}
@ -376,14 +376,14 @@ describe('Collector', () => {
it('should be able to collect the value of an enum', () => { it('should be able to collect the value of an enum', () => {
const enumSource = program.getSourceFile('/exported-enum.ts'); const enumSource = program.getSourceFile('/exported-enum.ts');
const metadata = collector.getMetadata(enumSource); const metadata = collector.getMetadata(enumSource) !;
const someEnum: any = metadata.metadata['SomeEnum']; const someEnum: any = metadata.metadata['SomeEnum'];
expect(someEnum).toEqual({A: 0, B: 1, C: 100, D: 101}); expect(someEnum).toEqual({A: 0, B: 1, C: 100, D: 101});
}); });
it('should ignore a non-export enum', () => { it('should ignore a non-export enum', () => {
const enumSource = program.getSourceFile('/private-enum.ts'); const enumSource = program.getSourceFile('/private-enum.ts');
const metadata = collector.getMetadata(enumSource); const metadata = collector.getMetadata(enumSource) !;
const publicEnum: any = metadata.metadata['PublicEnum']; const publicEnum: any = metadata.metadata['PublicEnum'];
const privateEnum: any = metadata.metadata['PrivateEnum']; const privateEnum: any = metadata.metadata['PrivateEnum'];
expect(publicEnum).toEqual({a: 0, b: 1, c: 2}); 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', () => { it('should be able to collect enums initialized from consts', () => {
const enumSource = program.getSourceFile('/exported-enum.ts'); const enumSource = program.getSourceFile('/exported-enum.ts');
const metadata = collector.getMetadata(enumSource); const metadata = collector.getMetadata(enumSource) !;
const complexEnum: any = metadata.metadata['ComplexEnum']; const complexEnum: any = metadata.metadata['ComplexEnum'];
expect(complexEnum).toEqual({ expect(complexEnum).toEqual({
A: 0, A: 0,
@ -405,7 +405,7 @@ describe('Collector', () => {
it('should be able to collect a simple static method', () => { it('should be able to collect a simple static method', () => {
const staticSource = program.getSourceFile('/static-method.ts'); const staticSource = program.getSourceFile('/static-method.ts');
const metadata = collector.getMetadata(staticSource); const metadata = collector.getMetadata(staticSource) !;
expect(metadata).toBeDefined(); expect(metadata).toBeDefined();
const classData = <ClassMetadata>metadata.metadata['MyModule']; const classData = <ClassMetadata>metadata.metadata['MyModule'];
expect(classData).toBeDefined(); expect(classData).toBeDefined();
@ -423,7 +423,7 @@ describe('Collector', () => {
it('should be able to collect a call to a static method', () => { it('should be able to collect a call to a static method', () => {
const staticSource = program.getSourceFile('/static-method-call.ts'); const staticSource = program.getSourceFile('/static-method-call.ts');
const metadata = collector.getMetadata(staticSource); const metadata = collector.getMetadata(staticSource) !;
expect(metadata).toBeDefined(); expect(metadata).toBeDefined();
const classData = <ClassMetadata>metadata.metadata['Foo']; const classData = <ClassMetadata>metadata.metadata['Foo'];
expect(classData).toBeDefined(); expect(classData).toBeDefined();
@ -446,7 +446,7 @@ describe('Collector', () => {
it('should be able to collect a static field', () => { it('should be able to collect a static field', () => {
const staticSource = program.getSourceFile('/static-field.ts'); const staticSource = program.getSourceFile('/static-field.ts');
const metadata = collector.getMetadata(staticSource); const metadata = collector.getMetadata(staticSource) !;
expect(metadata).toBeDefined(); expect(metadata).toBeDefined();
const classData = <ClassMetadata>metadata.metadata['MyModule']; const classData = <ClassMetadata>metadata.metadata['MyModule'];
expect(classData).toBeDefined(); expect(classData).toBeDefined();
@ -455,7 +455,7 @@ describe('Collector', () => {
it('should be able to collect a reference to a static field', () => { it('should be able to collect a reference to a static field', () => {
const staticSource = program.getSourceFile('/static-field-reference.ts'); const staticSource = program.getSourceFile('/static-field-reference.ts');
const metadata = collector.getMetadata(staticSource); const metadata = collector.getMetadata(staticSource) !;
expect(metadata).toBeDefined(); expect(metadata).toBeDefined();
const classData = <ClassMetadata>metadata.metadata['Foo']; const classData = <ClassMetadata>metadata.metadata['Foo'];
expect(classData).toBeDefined(); expect(classData).toBeDefined();
@ -477,7 +477,7 @@ describe('Collector', () => {
it('should be able to collect a method with a conditional expression', () => { it('should be able to collect a method with a conditional expression', () => {
const source = program.getSourceFile('/static-method-with-if.ts'); const source = program.getSourceFile('/static-method-with-if.ts');
const metadata = collector.getMetadata(source); const metadata = collector.getMetadata(source) !;
expect(metadata).toBeDefined(); expect(metadata).toBeDefined();
const classData = <ClassMetadata>metadata.metadata['MyModule']; const classData = <ClassMetadata>metadata.metadata['MyModule'];
expect(classData).toBeDefined(); expect(classData).toBeDefined();
@ -502,7 +502,7 @@ describe('Collector', () => {
it('should be able to collect a method with a default parameter', () => { it('should be able to collect a method with a default parameter', () => {
const source = program.getSourceFile('/static-method-with-default.ts'); const source = program.getSourceFile('/static-method-with-default.ts');
const metadata = collector.getMetadata(source); const metadata = collector.getMetadata(source) !;
expect(metadata).toBeDefined(); expect(metadata).toBeDefined();
const classData = <ClassMetadata>metadata.metadata['MyModule']; const classData = <ClassMetadata>metadata.metadata['MyModule'];
expect(classData).toBeDefined(); expect(classData).toBeDefined();
@ -531,7 +531,7 @@ describe('Collector', () => {
it('should be able to collect re-exported symbols', () => { it('should be able to collect re-exported symbols', () => {
const source = program.getSourceFile('/re-exports.ts'); const source = program.getSourceFile('/re-exports.ts');
const metadata = collector.getMetadata(source); const metadata = collector.getMetadata(source) !;
expect(metadata.exports).toEqual([ expect(metadata.exports).toEqual([
{from: './static-field', export: ['MyModule']}, {from: './static-field', export: ['MyModule']},
{from: './static-field-reference', export: [{name: 'Foo', as: 'OtherModule'}]}, {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', () => { it('should be able to collect a export as symbol', () => {
const source = program.getSourceFile('export-as.d.ts'); 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'}}); expect(metadata.metadata).toEqual({SomeFunction: {__symbolic: 'function'}});
}); });
it('should be able to collect exports with no module specifier', () => { it('should be able to collect exports with no module specifier', () => {
const source = program.getSourceFile('/re-exports-2.ts'); const source = program.getSourceFile('/re-exports-2.ts');
const metadata = collector.getMetadata(source); const metadata = collector.getMetadata(source) !;
expect(metadata.metadata).toEqual({ expect(metadata.metadata).toEqual({
MyClass: Object({__symbolic: 'class'}), MyClass: Object({__symbolic: 'class'}),
OtherModule: {__symbolic: 'reference', module: './static-field-reference', name: 'Foo'}, 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', () => { it('should collect an error symbol if collecting a reference to a non-exported symbol', () => {
const source = program.getSourceFile('/local-symbol-ref.ts'); const source = program.getSourceFile('/local-symbol-ref.ts');
const metadata = collector.getMetadata(source); const metadata = collector.getMetadata(source) !;
expect(metadata.metadata).toEqual({ expect(metadata.metadata).toEqual({
REQUIRED_VALIDATOR: { REQUIRED_VALIDATOR: {
__symbolic: 'error', __symbolic: 'error',
@ -579,7 +579,7 @@ describe('Collector', () => {
it('should collect an error symbol if collecting a reference to a non-exported function', () => { it('should collect an error symbol if collecting a reference to a non-exported function', () => {
const source = program.getSourceFile('/local-function-ref.ts'); const source = program.getSourceFile('/local-function-ref.ts');
const metadata = collector.getMetadata(source); const metadata = collector.getMetadata(source) !;
expect(metadata.metadata).toEqual({ expect(metadata.metadata).toEqual({
REQUIRED_VALIDATOR: { REQUIRED_VALIDATOR: {
__symbolic: 'error', __symbolic: 'error',
@ -601,7 +601,7 @@ describe('Collector', () => {
it('should collect an error for a simple function that references a local variable', () => { it('should collect an error for a simple function that references a local variable', () => {
const source = program.getSourceFile('/local-symbol-ref-func.ts'); const source = program.getSourceFile('/local-symbol-ref-func.ts');
const metadata = collector.getMetadata(source); const metadata = collector.getMetadata(source) !;
expect(metadata.metadata).toEqual({ expect(metadata.metadata).toEqual({
foo: { foo: {
__symbolic: 'function', __symbolic: 'function',
@ -619,7 +619,7 @@ describe('Collector', () => {
it('should collect any for interface parameter reference', () => { it('should collect any for interface parameter reference', () => {
const source = program.getSourceFile('/interface-reference.ts'); 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({ expect((metadata.metadata['SomeClass'] as ClassMetadata).members).toEqual({
__ctor__: [{ __ctor__: [{
__symbolic: 'constructor', __symbolic: 'constructor',
@ -734,7 +734,7 @@ describe('Collector', () => {
toString(): string { return \`InjectionToken ${this._desc}\`; } toString(): string { return \`InjectionToken ${this._desc}\`; }
} as any;`, } as any;`,
ts.ScriptTarget.Latest, true); ts.ScriptTarget.Latest, true);
const metadata = collector.getMetadata(source); const metadata = collector.getMetadata(source) !;
expect(metadata.metadata).toEqual({InjectionToken: {__symbolic: 'class'}}); expect(metadata.metadata).toEqual({InjectionToken: {__symbolic: 'class'}});
}); });
@ -781,19 +781,19 @@ describe('Collector', () => {
describe('inheritance', () => { describe('inheritance', () => {
it('should record `extends` clauses for declared classes', () => { 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']) expect(metadata.metadata['DeclaredChildClass'])
.toEqual({__symbolic: 'class', extends: {__symbolic: 'reference', name: 'ParentClass'}}); .toEqual({__symbolic: 'class', extends: {__symbolic: 'reference', name: 'ParentClass'}});
}); });
it('should record `extends` clauses for classes in the same file', () => { 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']) expect(metadata.metadata['ChildClassSameFile'])
.toEqual({__symbolic: 'class', extends: {__symbolic: 'reference', name: 'ParentClass'}}); .toEqual({__symbolic: 'class', extends: {__symbolic: 'reference', name: 'ParentClass'}});
}); });
it('should record `extends` clauses for classes in a different file', () => { 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({ expect(metadata.metadata['ChildClassOtherFile']).toEqual({
__symbolic: 'class', __symbolic: 'class',
extends: { extends: {
@ -811,7 +811,7 @@ describe('Collector', () => {
} }
it('should collect the correct arity for a class', () => { 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']; const zero = metadata.metadata['Zero'];
if (expectClass(zero)) expect(zero.arity).toBeUndefined(); if (expectClass(zero)) expect(zero.arity).toBeUndefined();
@ -883,7 +883,7 @@ describe('Collector', () => {
}) })
export class MyComponent {} export class MyComponent {}
`); `);
const metadata = collector.getMetadata(source); const metadata = collector.getMetadata(source) !;
expect(metadata.metadata.MyComponent).toEqual({ expect(metadata.metadata.MyComponent).toEqual({
__symbolic: 'class', __symbolic: 'class',
decorators: [{ decorators: [{
@ -947,7 +947,7 @@ describe('Collector', () => {
function collectSource(content: string): ModuleMetadata { function collectSource(content: string): ModuleMetadata {
const sourceFile = createSource(content); const sourceFile = createSource(content);
return collector.getMetadata(sourceFile); return collector.getMetadata(sourceFile) !;
} }
}); });

View File

@ -12,7 +12,7 @@ import * as ts from 'typescript';
import {Evaluator} from '../src/evaluator'; import {Evaluator} from '../src/evaluator';
import {Symbols} from '../src/symbols'; import {Symbols} from '../src/symbols';
import {Directory, Host, expectNoDiagnostics, findVar} from './typescript.mocks'; import {Directory, Host, expectNoDiagnostics, findVar, findVarInitializer} from './typescript.mocks';
describe('Evaluator', () => { describe('Evaluator', () => {
const documentRegistry = ts.createDocumentRegistry(); const documentRegistry = ts.createDocumentRegistry();
@ -31,7 +31,7 @@ describe('Evaluator', () => {
service = ts.createLanguageService(host, documentRegistry); service = ts.createLanguageService(host, documentRegistry);
program = service.getProgram(); program = service.getProgram();
typeChecker = program.getTypeChecker(); typeChecker = program.getTypeChecker();
symbols = new Symbols(null); symbols = new Symbols(null as any as ts.SourceFile);
evaluator = new Evaluator(symbols, new Map()); evaluator = new Evaluator(symbols, new Map());
}); });
@ -48,10 +48,10 @@ describe('Evaluator', () => {
it('should be able to fold literal expressions', () => { it('should be able to fold literal expressions', () => {
const consts = program.getSourceFile('consts.ts'); const consts = program.getSourceFile('consts.ts');
expect(evaluator.isFoldable(findVar(consts, 'someName').initializer)).toBeTruthy(); expect(evaluator.isFoldable(findVarInitializer(consts, 'someName'))).toBeTruthy();
expect(evaluator.isFoldable(findVar(consts, 'someBool').initializer)).toBeTruthy(); expect(evaluator.isFoldable(findVarInitializer(consts, 'someBool'))).toBeTruthy();
expect(evaluator.isFoldable(findVar(consts, 'one').initializer)).toBeTruthy(); expect(evaluator.isFoldable(findVarInitializer(consts, 'one'))).toBeTruthy();
expect(evaluator.isFoldable(findVar(consts, 'two').initializer)).toBeTruthy(); expect(evaluator.isFoldable(findVarInitializer(consts, 'two'))).toBeTruthy();
}); });
it('should be able to fold expressions with foldable references', () => { it('should be able to fold expressions with foldable references', () => {
@ -60,20 +60,20 @@ describe('Evaluator', () => {
symbols.define('someBool', true); symbols.define('someBool', true);
symbols.define('one', 1); symbols.define('one', 1);
symbols.define('two', 2); symbols.define('two', 2);
expect(evaluator.isFoldable(findVar(expressions, 'three').initializer)).toBeTruthy(); expect(evaluator.isFoldable(findVarInitializer(expressions, 'three'))).toBeTruthy();
expect(evaluator.isFoldable(findVar(expressions, 'four').initializer)).toBeTruthy(); expect(evaluator.isFoldable(findVarInitializer(expressions, 'four'))).toBeTruthy();
symbols.define('three', 3); symbols.define('three', 3);
symbols.define('four', 4); symbols.define('four', 4);
expect(evaluator.isFoldable(findVar(expressions, 'obj').initializer)).toBeTruthy(); expect(evaluator.isFoldable(findVarInitializer(expressions, 'obj'))).toBeTruthy();
expect(evaluator.isFoldable(findVar(expressions, 'arr').initializer)).toBeTruthy(); expect(evaluator.isFoldable(findVarInitializer(expressions, 'arr'))).toBeTruthy();
}); });
it('should be able to evaluate literal expressions', () => { it('should be able to evaluate literal expressions', () => {
const consts = program.getSourceFile('consts.ts'); const consts = program.getSourceFile('consts.ts');
expect(evaluator.evaluateNode(findVar(consts, 'someName').initializer)).toBe('some-name'); expect(evaluator.evaluateNode(findVarInitializer(consts, 'someName'))).toBe('some-name');
expect(evaluator.evaluateNode(findVar(consts, 'someBool').initializer)).toBe(true); expect(evaluator.evaluateNode(findVarInitializer(consts, 'someBool'))).toBe(true);
expect(evaluator.evaluateNode(findVar(consts, 'one').initializer)).toBe(1); expect(evaluator.evaluateNode(findVarInitializer(consts, 'one'))).toBe(1);
expect(evaluator.evaluateNode(findVar(consts, 'two').initializer)).toBe(2); expect(evaluator.evaluateNode(findVarInitializer(consts, 'two'))).toBe(2);
}); });
it('should be able to evaluate expressions', () => { it('should be able to evaluate expressions', () => {
@ -82,78 +82,77 @@ describe('Evaluator', () => {
symbols.define('someBool', true); symbols.define('someBool', true);
symbols.define('one', 1); symbols.define('one', 1);
symbols.define('two', 2); 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); 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); 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}); .toEqual({one: 1, two: 2, three: 3, four: 4});
expect(evaluator.evaluateNode(findVar(expressions, 'arr').initializer)).toEqual([1, 2, 3, 4]); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'arr'))).toEqual([1, 2, 3, 4]);
expect(evaluator.evaluateNode(findVar(expressions, 'bTrue').initializer)).toEqual(true); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bTrue'))).toEqual(true);
expect(evaluator.evaluateNode(findVar(expressions, 'bFalse').initializer)).toEqual(false); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bFalse'))).toEqual(false);
expect(evaluator.evaluateNode(findVar(expressions, 'bAnd').initializer)).toEqual(true); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bAnd'))).toEqual(true);
expect(evaluator.evaluateNode(findVar(expressions, 'bOr').initializer)).toEqual(true); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bOr'))).toEqual(true);
expect(evaluator.evaluateNode(findVar(expressions, 'nDiv').initializer)).toEqual(2); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'nDiv'))).toEqual(2);
expect(evaluator.evaluateNode(findVar(expressions, 'nMod').initializer)).toEqual(1); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'nMod'))).toEqual(1);
expect(evaluator.evaluateNode(findVar(expressions, 'bLOr').initializer)).toEqual(false || true); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLOr'))).toEqual(false || true);
expect(evaluator.evaluateNode(findVar(expressions, 'bLAnd').initializer)).toEqual(true && true); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLAnd'))).toEqual(true && true);
expect(evaluator.evaluateNode(findVar(expressions, 'bBOr').initializer)).toEqual(0x11 | 0x22); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bBOr'))).toEqual(0x11 | 0x22);
expect(evaluator.evaluateNode(findVar(expressions, 'bBAnd').initializer)).toEqual(0x11 & 0x03); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bBAnd'))).toEqual(0x11 & 0x03);
expect(evaluator.evaluateNode(findVar(expressions, 'bXor').initializer)).toEqual(0x11 ^ 0x21); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bXor'))).toEqual(0x11 ^ 0x21);
expect(evaluator.evaluateNode(findVar(expressions, 'bEqual').initializer)) expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bEqual')))
.toEqual(1 == <any>'1'); .toEqual(1 == <any>'1');
expect(evaluator.evaluateNode(findVar(expressions, 'bNotEqual').initializer)) expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bNotEqual')))
.toEqual(1 != <any>'1'); .toEqual(1 != <any>'1');
expect(evaluator.evaluateNode(findVar(expressions, 'bIdentical').initializer)) expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bIdentical')))
.toEqual(1 === <any>'1'); .toEqual(1 === <any>'1');
expect(evaluator.evaluateNode(findVar(expressions, 'bNotIdentical').initializer)) expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bNotIdentical')))
.toEqual(1 !== <any>'1'); .toEqual(1 !== <any>'1');
expect(evaluator.evaluateNode(findVar(expressions, 'bLessThan').initializer)).toEqual(1 < 2); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLessThan'))).toEqual(1 < 2);
expect(evaluator.evaluateNode(findVar(expressions, 'bGreaterThan').initializer)).toEqual(1 > 2); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bGreaterThan'))).toEqual(1 > 2);
expect(evaluator.evaluateNode(findVar(expressions, 'bLessThanEqual').initializer)) expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLessThanEqual')))
.toEqual(1 <= 2); .toEqual(1 <= 2);
expect(evaluator.evaluateNode(findVar(expressions, 'bGreaterThanEqual').initializer)) expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bGreaterThanEqual')))
.toEqual(1 >= 2); .toEqual(1 >= 2);
expect(evaluator.evaluateNode(findVar(expressions, 'bShiftLeft').initializer)).toEqual(1 << 2); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftLeft'))).toEqual(1 << 2);
expect(evaluator.evaluateNode(findVar(expressions, 'bShiftRight').initializer)) expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftRight'))).toEqual(-1 >> 2);
.toEqual(-1 >> 2); expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftRightU')))
expect(evaluator.evaluateNode(findVar(expressions, 'bShiftRightU').initializer))
.toEqual(-1 >>> 2); .toEqual(-1 >>> 2);
}); });
it('should report recursive references as symbolic', () => { it('should report recursive references as symbolic', () => {
const expressions = program.getSourceFile('expressions.ts'); const expressions = program.getSourceFile('expressions.ts');
expect(evaluator.evaluateNode(findVar(expressions, 'recursiveA').initializer)) expect(evaluator.evaluateNode(findVarInitializer(expressions, 'recursiveA')))
.toEqual({__symbolic: 'reference', name: 'recursiveB'}); .toEqual({__symbolic: 'reference', name: 'recursiveB'});
expect(evaluator.evaluateNode(findVar(expressions, 'recursiveB').initializer)) expect(evaluator.evaluateNode(findVarInitializer(expressions, 'recursiveB')))
.toEqual({__symbolic: 'reference', name: 'recursiveA'}); .toEqual({__symbolic: 'reference', name: 'recursiveA'});
}); });
it('should correctly handle special cases for CONST_EXPR', () => { it('should correctly handle special cases for CONST_EXPR', () => {
const const_expr = program.getSourceFile('const_expr.ts'); const const_expr = program.getSourceFile('const_expr.ts');
expect(evaluator.evaluateNode(findVar(const_expr, 'bTrue').initializer)).toEqual(true); expect(evaluator.evaluateNode(findVarInitializer(const_expr, 'bTrue'))).toEqual(true);
expect(evaluator.evaluateNode(findVar(const_expr, 'bFalse').initializer)).toEqual(false); expect(evaluator.evaluateNode(findVarInitializer(const_expr, 'bFalse'))).toEqual(false);
}); });
it('should resolve a forwardRef', () => { it('should resolve a forwardRef', () => {
const forwardRef = program.getSourceFile('forwardRef.ts'); const forwardRef = program.getSourceFile('forwardRef.ts');
expect(evaluator.evaluateNode(findVar(forwardRef, 'bTrue').initializer)).toEqual(true); expect(evaluator.evaluateNode(findVarInitializer(forwardRef, 'bTrue'))).toEqual(true);
expect(evaluator.evaluateNode(findVar(forwardRef, 'bFalse').initializer)).toEqual(false); expect(evaluator.evaluateNode(findVarInitializer(forwardRef, 'bFalse'))).toEqual(false);
}); });
it('should return new expressions', () => { it('should return new expressions', () => {
symbols.define('Value', {__symbolic: 'reference', module: './classes', name: 'Value'}); symbols.define('Value', {__symbolic: 'reference', module: './classes', name: 'Value'});
evaluator = new Evaluator(symbols, new Map()); evaluator = new Evaluator(symbols, new Map());
const newExpression = program.getSourceFile('newExpression.ts'); const newExpression = program.getSourceFile('newExpression.ts');
expect(evaluator.evaluateNode(findVar(newExpression, 'someValue').initializer)).toEqual({ expect(evaluator.evaluateNode(findVarInitializer(newExpression, 'someValue'))).toEqual({
__symbolic: 'new', __symbolic: 'new',
expression: {__symbolic: 'reference', name: 'Value', module: './classes'}, expression: {__symbolic: 'reference', name: 'Value', module: './classes'},
arguments: ['name', 12] arguments: ['name', 12]
}); });
expect(evaluator.evaluateNode(findVar(newExpression, 'complex').initializer)).toEqual({ expect(evaluator.evaluateNode(findVarInitializer(newExpression, 'complex'))).toEqual({
__symbolic: 'new', __symbolic: 'new',
expression: {__symbolic: 'reference', name: 'Value', module: './classes'}, expression: {__symbolic: 'reference', name: 'Value', module: './classes'},
arguments: ['name', 12] arguments: ['name', 12]
@ -162,8 +161,8 @@ describe('Evaluator', () => {
it('should support referene to a declared module type', () => { it('should support referene to a declared module type', () => {
const declared = program.getSourceFile('declared.ts'); const declared = program.getSourceFile('declared.ts');
const aDecl = findVar(declared, 'a'); const aDecl = findVar(declared, 'a') !;
expect(evaluator.evaluateNode(aDecl.type)).toEqual({ expect(evaluator.evaluateNode(aDecl.type !)).toEqual({
__symbolic: 'select', __symbolic: 'select',
expression: {__symbolic: 'reference', name: 'Foo'}, expression: {__symbolic: 'reference', name: 'Foo'},
member: 'A' member: 'A'
@ -172,28 +171,28 @@ describe('Evaluator', () => {
it('should return errors for unsupported expressions', () => { it('should return errors for unsupported expressions', () => {
const errors = program.getSourceFile('errors.ts'); const errors = program.getSourceFile('errors.ts');
const fDecl = findVar(errors, 'f'); const fDecl = findVar(errors, 'f') !;
expect(evaluator.evaluateNode(fDecl.initializer)) expect(evaluator.evaluateNode(fDecl.initializer !))
.toEqual( .toEqual(
{__symbolic: 'error', message: 'Function call not supported', line: 1, character: 12}); {__symbolic: 'error', message: 'Function call not supported', line: 1, character: 12});
const eDecl = findVar(errors, 'e'); const eDecl = findVar(errors, 'e') !;
expect(evaluator.evaluateNode(eDecl.type)).toEqual({ expect(evaluator.evaluateNode(eDecl.type !)).toEqual({
__symbolic: 'error', __symbolic: 'error',
message: 'Could not resolve type', message: 'Could not resolve type',
line: 2, line: 2,
character: 11, character: 11,
context: {typeName: 'NotFound'} context: {typeName: 'NotFound'}
}); });
const sDecl = findVar(errors, 's'); const sDecl = findVar(errors, 's') !;
expect(evaluator.evaluateNode(sDecl.initializer)).toEqual({ expect(evaluator.evaluateNode(sDecl.initializer !)).toEqual({
__symbolic: 'error', __symbolic: 'error',
message: 'Name expected', message: 'Name expected',
line: 3, line: 3,
character: 14, character: 14,
context: {received: '1'} context: {received: '1'}
}); });
const tDecl = findVar(errors, 't'); const tDecl = findVar(errors, 't') !;
expect(evaluator.evaluateNode(tDecl.initializer)).toEqual({ expect(evaluator.evaluateNode(tDecl.initializer !)).toEqual({
__symbolic: 'error', __symbolic: 'error',
message: 'Expression form not supported', message: 'Expression form not supported',
line: 4, line: 4,
@ -204,14 +203,14 @@ describe('Evaluator', () => {
it('should be able to fold an array spread', () => { it('should be able to fold an array spread', () => {
const expressions = program.getSourceFile('expressions.ts'); const expressions = program.getSourceFile('expressions.ts');
symbols.define('arr', [1, 2, 3, 4]); symbols.define('arr', [1, 2, 3, 4]);
const arrSpread = findVar(expressions, 'arrSpread'); const arrSpread = findVar(expressions, 'arrSpread') !;
expect(evaluator.evaluateNode(arrSpread.initializer)).toEqual([0, 1, 2, 3, 4, 5]); expect(evaluator.evaluateNode(arrSpread.initializer !)).toEqual([0, 1, 2, 3, 4, 5]);
}); });
it('should be able to produce a spread expression', () => { it('should be able to produce a spread expression', () => {
const expressions = program.getSourceFile('expressions.ts'); const expressions = program.getSourceFile('expressions.ts');
const arrSpreadRef = findVar(expressions, 'arrSpreadRef'); const arrSpreadRef = findVar(expressions, 'arrSpreadRef') !;
expect(evaluator.evaluateNode(arrSpreadRef.initializer)).toEqual([ expect(evaluator.evaluateNode(arrSpreadRef.initializer !)).toEqual([
0, {__symbolic: 'spread', expression: {__symbolic: 'reference', name: 'arrImport'}}, 5 0, {__symbolic: 'spread', expression: {__symbolic: 'reference', name: 'arrImport'}}, 5
]); ]);
}); });
@ -220,8 +219,8 @@ describe('Evaluator', () => {
const source = sourceFileOf(` const source = sourceFileOf(`
export var a = new f; export var a = new f;
`); `);
const expr = findVar(source, 'a'); const expr = findVar(source, 'a') !;
expect(evaluator.evaluateNode(expr.initializer)) expect(evaluator.evaluateNode(expr.initializer !))
.toEqual({__symbolic: 'new', expression: {__symbolic: 'reference', name: 'f'}}); .toEqual({__symbolic: 'new', expression: {__symbolic: 'reference', name: 'f'}});
}); });
}); });

View File

@ -17,7 +17,7 @@ describe('Symbols', () => {
let symbols: Symbols; let symbols: Symbols;
const someValue = 'some-value'; 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)); it('should be able to add a symbol', () => symbols.define('someSymbol', someValue));
@ -110,7 +110,7 @@ describe('Symbols', () => {
} }
return false; return false;
}; };
ts.forEachChild(core, visit); ts.forEachChild(core !, visit);
}); });
}); });

View File

@ -21,7 +21,7 @@ export class Host implements ts.LanguageServiceHost {
getScriptVersion(fileName: string): string { return this.version.toString(); } getScriptVersion(fileName: string): string { return this.version.toString(); }
getScriptSnapshot(fileName: string): ts.IScriptSnapshot { getScriptSnapshot(fileName: string): ts.IScriptSnapshot|undefined {
const content = this.getFileContent(fileName); const content = this.getFileContent(fileName);
if (content) return ts.ScriptSnapshot.fromString(content); if (content) return ts.ScriptSnapshot.fromString(content);
} }
@ -40,7 +40,7 @@ export class Host implements ts.LanguageServiceHost {
this.version++; this.version++;
} }
private getFileContent(fileName: string): string { private getFileContent(fileName: string): string|undefined {
if (this.overrides.has(fileName)) { if (this.overrides.has(fileName)) {
return this.overrides.get(fileName); return this.overrides.get(fileName);
} }
@ -69,9 +69,9 @@ export class MockNode implements ts.Node {
constructor( constructor(
public kind: ts.SyntaxKind = ts.SyntaxKind.Identifier, public flags: ts.NodeFlags = 0, public kind: ts.SyntaxKind = ts.SyntaxKind.Identifier, public flags: ts.NodeFlags = 0,
public pos: number = 0, public end: number = 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 } 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 []; } getChildren(sourceFile?: ts.SourceFile): ts.Node[] { return []; }
getStart(sourceFile?: ts.SourceFile): number { return 0; } getStart(sourceFile?: ts.SourceFile): number { return 0; }
getFullStart(): number { return 0; } getFullStart(): number { return 0; }
@ -81,10 +81,10 @@ export class MockNode implements ts.Node {
getLeadingTriviaWidth(sourceFile?: ts.SourceFile): number { return 0; } getLeadingTriviaWidth(sourceFile?: ts.SourceFile): number { return 0; }
getFullText(sourceFile?: ts.SourceFile): string { return ''; } getFullText(sourceFile?: ts.SourceFile): string { return ''; }
getText(sourceFile?: ts.SourceFile): string { return ''; } getText(sourceFile?: ts.SourceFile): string { return ''; }
getFirstToken(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; } getLastToken(sourceFile?: ts.SourceFile): ts.Node { return null as any as ts.Node; }
forEachChild<T>(cbNode: (node: ts.Node) => T, cbNodeArray?: (nodes: ts.Node[]) => T): T { forEachChild<T>(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<T>(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( return ts.forEachChild(
sourceFile, node => isClass(node) && isNamed(node.name, name) ? node : undefined); 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( return allChildren(
sourceFile, node => isVar(node) && isNamed(node.name, name) ? node : undefined); 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 { export function isClass(node: ts.Node): node is ts.ClassDeclaration {
return node.kind === ts.SyntaxKind.ClassDeclaration; return node.kind === ts.SyntaxKind.ClassDeclaration;
} }
export function isNamed(node: ts.Node, name: string): node is ts.Identifier { export function isNamed(node: ts.Node | undefined, name: string): node is ts.Identifier {
return node.kind === ts.SyntaxKind.Identifier && (<ts.Identifier>node).text === name; return !!node && node.kind === ts.SyntaxKind.Identifier && (<ts.Identifier>node).text === name;
} }
export function isVar(node: ts.Node): node is ts.VariableDeclaration { export function isVar(node: ts.Node): node is ts.VariableDeclaration {

View File

@ -7,6 +7,7 @@
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"outDir": "../../dist/all/@angular/tsc-wrapped", "outDir": "../../dist/all/@angular/tsc-wrapped",
"strictNullChecks": true,
"noImplicitAny": true, "noImplicitAny": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"paths": { "paths": {