refactor(language-service): reformat using clang-format (#36426)

clang-format was recently updated and any PRs that touch files in the
language service will have to reformat all the files.

Instead of changing the formatting in those PRs, this PR formats all
files in language-service package once and for all.

PR Close #36426
This commit is contained in:
Keen Yee Liau 2020-04-03 20:57:39 -07:00 committed by Kara Erickson
parent 1b4df6484e
commit 1140bbc25c
35 changed files with 686 additions and 412 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AST, AbsoluteSourceSpan, AstPath, AttrAst, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, Element, ElementAst, EmptyExpr, ExpressionBinding, HtmlAstPath, NAMED_ENTITIES, Node as HtmlAst, NullTemplateVisitor, ParseSpan, ReferenceAst, TagContentType, TemplateBinding, Text, VariableBinding, getHtmlTagDefinition} from '@angular/compiler';
import {AbsoluteSourceSpan, AST, AstPath, AttrAst, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, Element, ElementAst, EmptyExpr, ExpressionBinding, getHtmlTagDefinition, HtmlAstPath, NAMED_ENTITIES, Node as HtmlAst, NullTemplateVisitor, ParseSpan, ReferenceAst, TagContentType, TemplateBinding, Text, VariableBinding} from '@angular/compiler';
import {$$, $_, isAsciiLetter, isDigit} from '@angular/compiler/src/chars';
import {AstResult} from './common';
@ -216,7 +216,8 @@ export function getTemplateCompletions(
const replacementSpan = getBoundedWordSpan(templateInfo, position);
return result.map(entry => {
return {
...entry, replacementSpan,
...entry,
replacementSpan,
};
});
}
@ -331,7 +332,7 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C
elemAst = parent;
}
} else if (templatePath.tail instanceof ElementAst) {
refAst = new ReferenceAst(htmlAttr.name, null !, htmlAttr.value, htmlAttr.valueSpan !);
refAst = new ReferenceAst(htmlAttr.name, null!, htmlAttr.value, htmlAttr.valueSpan!);
elemAst = templatePath.tail;
}
if (refAst && elemAst) {
@ -340,7 +341,7 @@ function attributeValueCompletions(info: AstResult, htmlPath: HtmlAstPath): ng.C
} else {
// HtmlAst contains the `Attribute` node, however the corresponding `AttrAst`
// node is missing from the TemplateAst.
const attrAst = new AttrAst(htmlAttr.name, htmlAttr.value, htmlAttr.valueSpan !);
const attrAst = new AttrAst(htmlAttr.name, htmlAttr.value, htmlAttr.valueSpan!);
attrAst.visit(visitor, null);
}
return visitor.results;
@ -434,7 +435,9 @@ class ExpressionVisitor extends NullTemplateVisitor {
super();
}
get results(): ng.CompletionEntry[] { return Array.from(this.completions.values()); }
get results(): ng.CompletionEntry[] {
return Array.from(this.completions.values());
}
visitDirectiveProperty(ast: BoundDirectivePropertyAst): void {
this.processExpressionCompletions(ast.value);
@ -444,7 +447,9 @@ class ExpressionVisitor extends NullTemplateVisitor {
this.processExpressionCompletions(ast.value);
}
visitEvent(ast: BoundEventAst): void { this.processExpressionCompletions(ast.handler); }
visitEvent(ast: BoundEventAst): void {
this.processExpressionCompletions(ast.handler);
}
visitElement(): void {
// no-op for now
@ -577,7 +582,7 @@ class ExpressionVisitor extends NullTemplateVisitor {
}
} else if (binding instanceof ExpressionBinding) {
if (inSpan(this.position, binding.value?.ast.sourceSpan)) {
this.processExpressionCompletions(binding.value !.ast);
this.processExpressionCompletions(binding.value!.ast);
return;
} else if (!binding.value && this.position > binding.key.span.end) {
// No expression is defined for the value of the key expression binding, but the cursor is
@ -637,7 +642,7 @@ function angularAttributes(info: AstResult, elementName: string): AngularAttribu
if (selector.element && selector.element !== elementName) {
continue;
}
const summary = selectorMap.get(selector) !;
const summary = selectorMap.get(selector)!;
const hasTemplateRef = isStructuralDirective(summary.type);
// attributes are listed in (attribute, value) pairs
for (let i = 0; i < selector.attrs.length; i += 2) {

View File

@ -7,7 +7,7 @@
*/
import * as path from 'path';
import * as ts from 'typescript'; // used as value and is provided at runtime
import * as ts from 'typescript'; // used as value and is provided at runtime
import {AstResult} from './common';
import {locateSymbols} from './locate_symbol';
import {getPropertyAssignmentFromValue, isClassDecoratorProperty} from './template';

View File

@ -14,15 +14,15 @@ export interface DiagnosticMessage {
kind: keyof typeof ts.DiagnosticCategory;
}
type DiagnosticName = 'directive_not_in_module' | 'missing_template_and_templateurl' |
'both_template_and_templateurl' | 'invalid_templateurl' | 'template_context_missing_member' |
'callable_expression_expected_method_call' | 'call_target_not_callable' |
'expression_might_be_null' | 'expected_a_number_type' | 'expected_a_string_or_number_type' |
'expected_operands_of_similar_type_or_any' | 'unrecognized_operator' |
'unrecognized_primitive' | 'no_pipe_found' | 'unable_to_resolve_compatible_call_signature' |
'unable_to_resolve_signature' | 'could_not_resolve_type' | 'identifier_not_callable' |
'identifier_possibly_undefined' | 'identifier_not_defined_in_app_context' |
'identifier_not_defined_on_receiver' | 'identifier_is_private';
type DiagnosticName = 'directive_not_in_module'|'missing_template_and_templateurl'|
'both_template_and_templateurl'|'invalid_templateurl'|'template_context_missing_member'|
'callable_expression_expected_method_call'|'call_target_not_callable'|
'expression_might_be_null'|'expected_a_number_type'|'expected_a_string_or_number_type'|
'expected_operands_of_similar_type_or_any'|'unrecognized_operator'|'unrecognized_primitive'|
'no_pipe_found'|'unable_to_resolve_compatible_call_signature'|'unable_to_resolve_signature'|
'could_not_resolve_type'|'identifier_not_callable'|'identifier_possibly_undefined'|
'identifier_not_defined_in_app_context'|'identifier_not_defined_on_receiver'|
'identifier_is_private';
export const Diagnostic: Record<DiagnosticName, DiagnosticMessage> = {
directive_not_in_module: {
@ -156,6 +156,7 @@ export function createDiagnostic(
dm.message.replace(/%(\d+)/g, (_, index: string) => formatArgs[+index - 1]);
return {
kind: ts.DiagnosticCategory[dm.kind],
message: formattedMessage, span,
message: formattedMessage,
span,
};
}

View File

@ -11,7 +11,7 @@ import * as path from 'path';
import * as ts from 'typescript';
import {AstResult} from './common';
import {Diagnostic, createDiagnostic} from './diagnostic_messages';
import {createDiagnostic, Diagnostic} from './diagnostic_messages';
import {getTemplateExpressionDiagnostics} from './expression_diagnostics';
import * as ng from './types';
import {TypeScriptServiceHost} from './typescript_host';
@ -193,7 +193,7 @@ function chainDiagnostics(chain: ng.DiagnosticMessageChain): ts.DiagnosticMessag
* @param file
*/
export function ngDiagnosticToTsDiagnostic(
d: ng.Diagnostic, file: ts.SourceFile | undefined): ts.Diagnostic {
d: ng.Diagnostic, file: ts.SourceFile|undefined): ts.Diagnostic {
return {
file,
start: d.span.start,

View File

@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AST, AstPath, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, CompileDirectiveSummary, CompileTypeMetadata, DirectiveAst, ElementAst, EmbeddedTemplateAst, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, ReferenceAst, TemplateAst, TemplateAstPath, VariableAst, identifierName, templateVisitAll, tokenReference} from '@angular/compiler';
import {AST, AstPath, Attribute, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, CompileDirectiveSummary, CompileTypeMetadata, DirectiveAst, ElementAst, EmbeddedTemplateAst, identifierName, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, ReferenceAst, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference, VariableAst} from '@angular/compiler';
import {Diagnostic, createDiagnostic} from './diagnostic_messages';
import {createDiagnostic, Diagnostic} from './diagnostic_messages';
import {AstType} from './expression_type';
import {BuiltinType, Definition, Span, Symbol, SymbolDeclaration, SymbolQuery, SymbolTable} from './symbols';
import * as ng from './types';
@ -44,7 +44,9 @@ function getReferences(info: DiagnosticTemplateInfo): SymbolDeclaration[] {
name: reference.name,
kind: 'reference',
type: type || info.query.getBuiltinType(BuiltinType.Any),
get definition() { return getDefinitionOf(info, reference); }
get definition() {
return getDefinitionOf(info, reference);
}
});
}
}
@ -109,7 +111,10 @@ function getVarDeclarations(
results.push({
name: variable.name,
kind: 'variable',
type: symbol, get definition() { return getDefinitionOf(info, variable); },
type: symbol,
get definition() {
return getDefinitionOf(info, variable);
},
});
}
}
@ -296,7 +301,7 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
visitVariable(ast: VariableAst): void {
const directive = this.directiveSummary;
if (directive && ast.value) {
const context = this.info.query.getTemplateContext(directive.type.reference) !;
const context = this.info.query.getTemplateContext(directive.type.reference)!;
if (context && !context.has(ast.value)) {
const missingMember =
ast.value === '$implicit' ? 'an implicit value' : `a member called '${ast.value}'`;
@ -322,7 +327,7 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
// Find directive that references this template
this.directiveSummary =
ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type)) !;
ast.directives.map(d => d.directive).find(d => hasTemplateReference(d.type))!;
// Process children
super.visitEmbeddedTemplate(ast, context);
@ -350,9 +355,13 @@ class ExpressionDiagnosticsVisitor extends RecursiveTemplateAstVisitor {
}
}
private push(ast: TemplateAst) { this.path.push(ast); }
private push(ast: TemplateAst) {
this.path.push(ast);
}
private pop() { this.path.pop(); }
private pop() {
this.path.pop();
}
private absSpan(span: Span, additionalOffset: number = 0): Span {
return {
@ -366,7 +375,7 @@ function hasTemplateReference(type: CompileTypeMetadata): boolean {
if (type.diDeps) {
for (let diDep of type.diDeps) {
if (diDep.token && diDep.token.identifier &&
identifierName(diDep.token !.identifier !) == 'TemplateRef')
identifierName(diDep.token!.identifier!) == 'TemplateRef')
return true;
}
}

View File

@ -8,11 +8,13 @@
import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '@angular/compiler';
import {Diagnostic, createDiagnostic} from './diagnostic_messages';
import {createDiagnostic, Diagnostic} from './diagnostic_messages';
import {BuiltinType, Signature, Symbol, SymbolQuery, SymbolTable} from './symbols';
import * as ng from './types';
export interface ExpressionDiagnosticsContext { inEvent?: boolean; }
export interface ExpressionDiagnosticsContext {
inEvent?: boolean;
}
// AstType calculatetype of the ast given AST element.
export class AstType implements AstVisitor {
@ -22,7 +24,9 @@ export class AstType implements AstVisitor {
private scope: SymbolTable, private query: SymbolQuery,
private context: ExpressionDiagnosticsContext, private source: string) {}
getType(ast: AST): Symbol { return ast.visit(this); }
getType(ast: AST): Symbol {
return ast.visit(this);
}
getDiagnostics(ast: AST): ng.Diagnostic[] {
const type: Symbol = ast.visit(this);
@ -204,10 +208,10 @@ export class AstType implements AstVisitor {
// support contextual typing of arguments so this is simpler than TypeScript's
// version.
const args = ast.args.map(arg => this.getType(arg));
const target = this.getType(ast.target !);
const target = this.getType(ast.target!);
if (!target || !target.callable) {
this.diagnostics.push(createDiagnostic(
ast.span, Diagnostic.call_target_not_callable, this.sourceOf(ast.target !), target.name));
ast.span, Diagnostic.call_target_not_callable, this.sourceOf(ast.target!), target.name));
return this.anyType;
}
const signature = target.selectSignature(args);
@ -237,11 +241,24 @@ export class AstType implements AstVisitor {
public: true,
definition: undefined,
documentation: [],
members(): SymbolTable{return _this.scope;},
signatures(): Signature[]{return [];},
selectSignature(types): Signature | undefined{return undefined;},
indexed(argument): Symbol | undefined{return undefined;},
typeArguments(): Symbol[] | undefined{return undefined;},
members(): SymbolTable {
return _this.scope;
},
signatures(): Signature[] {
return [];
},
selectSignature(types): Signature |
undefined {
return undefined;
},
indexed(argument): Symbol |
undefined {
return undefined;
},
typeArguments(): Symbol[] |
undefined {
return undefined;
},
};
}

View File

@ -6,7 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AST, ASTWithSource, AstPath as AstPathBase, RecursiveAstVisitor} from '@angular/compiler';
import {AST, AstPath as AstPathBase, ASTWithSource, RecursiveAstVisitor} from '@angular/compiler';
import {AstType} from './expression_type';
import {BuiltinType, Span, Symbol, SymbolTable, TemplateSource} from './types';
import {inSpan} from './utils';
@ -41,7 +42,7 @@ export function getExpressionCompletions(
undefined {
const path = findAstAt(ast, position);
if (path.empty) return undefined;
const tail = path.tail !;
const tail = path.tail!;
let result: SymbolTable|undefined = scope;
function getType(ast: AST): Symbol {
@ -57,7 +58,9 @@ export function getExpressionCompletions(
visitConditional(ast) {},
visitFunctionCall(ast) {},
visitImplicitReceiver(ast) {},
visitInterpolation(ast) { result = undefined; },
visitInterpolation(ast) {
result = undefined;
},
visitKeyedRead(ast) {},
visitKeyedWrite(ast) {},
visitLiteralArray(ast) {},
@ -111,7 +114,7 @@ export function getExpressionSymbol(
templateInfo: TemplateSource): {symbol: Symbol, span: Span}|undefined {
const path = findAstAt(ast, position, /* excludeEmpty */ true);
if (path.empty) return undefined;
const tail = path.tail !;
const tail = path.tail!;
function getType(ast: AST): Symbol {
return new AstType(scope, templateInfo.query, {}, templateInfo.source).getType(ast);

View File

@ -37,14 +37,15 @@ export function getTemplateHover(
// The container is either the symbol's container (for example, 'AppComponent'
// is the container of the symbol 'title' in its template) or the NgModule
// that the directive belongs to (the container of AppComponent is AppModule).
let containerName: string|undefined = symbol.container ?.name;
let containerName: string|undefined = symbol.container?.name;
if (!containerName && staticSymbol) {
// If there is a static symbol then the target is a directive.
const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol);
containerName = ngModule ?.type.reference.name;
containerName = ngModule?.type.reference.name;
}
return createQuickInfo(symbol.name, symbol.kind, span, containerName, symbol.type?.name, symbol.documentation);
return createQuickInfo(
symbol.name, symbol.kind, span, containerName, symbol.type?.name, symbol.documentation);
}
/**
@ -63,7 +64,7 @@ export function getTsHover(
const kind = metadata.isComponent ? 'component' : 'directive';
const textSpan = ts.createTextSpanFromBounds(declarationSpan.start, declarationSpan.end);
const ngModule = analyzedModules.ngModuleByPipeOrDirective.get(staticSymbol);
const moduleName = ngModule ?.type.reference.name;
const moduleName = ngModule?.type.reference.name;
return createQuickInfo(
directiveName, kind, textSpan, moduleName, ts.ScriptElementKind.classElement);
}

View File

@ -10,7 +10,7 @@
// This section defines the HTML elements and attribute surface of HTML 4
// which is derived from https://www.w3.org/TR/html4/strict.dtd
type attrType = string | string[];
type attrType = string|string[];
type hash<T> = {
[name: string]: T
};
@ -104,7 +104,9 @@ const groups: hash<number>[] = [
{class: 1, style: 1},
{hreflang: 2, rel: 1, rev: 1},
{ismap: 7},
{ defer: 25, event: 1, for : 1 }
{
defer: 25, event: 1, for: 1
}
];
const elements: {[name: string]: number[]} = {
@ -193,7 +195,7 @@ export function elementNames(): string[] {
return Object.keys(elements).sort().map(v => v.toLowerCase());
}
function compose(indexes: number[] | undefined): hash<attrType> {
function compose(indexes: number[]|undefined): hash<attrType> {
const result: hash<attrType> = {};
if (indexes) {
for (let index of indexes) {
@ -415,7 +417,9 @@ export class SchemaInformation {
});
}
allKnownElements(): string[] { return Object.keys(this.schema); }
allKnownElements(): string[] {
return Object.keys(this.schema);
}
eventsOf(elementName: string): string[] {
const elementType = this.schema[elementName.toLowerCase()] || {};

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AST, Attribute, BoundDirectivePropertyAst, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, RecursiveTemplateAstVisitor, SelectorMatcher, StaticSymbol, TemplateAst, TemplateAstPath, VariableBinding, templateVisitAll, tokenReference} from '@angular/compiler';
import {AST, Attribute, BoundDirectivePropertyAst, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, RecursiveTemplateAstVisitor, SelectorMatcher, StaticSymbol, TemplateAst, TemplateAstPath, templateVisitAll, tokenReference, VariableBinding} from '@angular/compiler';
import * as tss from 'typescript/lib/tsserverlibrary';
import {AstResult} from './common';
@ -77,7 +77,7 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
}
if (result) {
symbol = result.symbol;
span = offsetSpan(result.span, attribute.valueSpan !.start.offset);
span = offsetSpan(result.span, attribute.valueSpan!.start.offset);
}
return true;
}
@ -121,7 +121,9 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
span = spanOf(ast);
}
},
visitElementProperty(ast) { attributeValueSymbol(ast.value); },
visitElementProperty(ast) {
attributeValueSymbol(ast.value);
},
visitAttr(ast) {
const element = path.first(ElementAst);
if (!element) return;
@ -188,7 +190,8 @@ function locateSymbol(ast: TemplateAst, path: TemplateAstPath, info: AstResult):
const {start, end} = offsetSpan(span, info.template.span.start);
return {
symbol,
span: tss.createTextSpanFromBounds(start, end), staticSymbol,
span: tss.createTextSpanFromBounds(start, end),
staticSymbol,
};
}
}
@ -216,7 +219,7 @@ function getSymbolInMicrosyntax(info: AstResult, path: TemplateAstPath, attribut
if (inSpan(path.position, tb.value?.ast.sourceSpan)) {
const dinfo = diagnosticInfoFromTemplateInfo(info);
const scope = getExpressionScope(dinfo, path);
result = getExpressionSymbol(scope, tb.value !, path.position, info.template);
result = getExpressionSymbol(scope, tb.value!, path.position, info.template);
} else if (inSpan(path.position, tb.sourceSpan)) {
const template = path.first(EmbeddedTemplateAst);
if (template) {
@ -277,7 +280,9 @@ function findParentOfBinding(
}
visitDirective(ast: DirectiveAst) {
const result = this.visitChildren(ast, visit => { visit(ast.inputs); });
const result = this.visitChildren(ast, visit => {
visit(ast.inputs);
});
return result;
}
@ -309,33 +314,63 @@ function findInputBinding(info: AstResult, name: string, directiveAst: Directive
*/
class OverrideKindSymbol implements Symbol {
public readonly kind: DirectiveKind;
constructor(private sym: Symbol, kindOverride: DirectiveKind) { this.kind = kindOverride; }
constructor(private sym: Symbol, kindOverride: DirectiveKind) {
this.kind = kindOverride;
}
get name(): string { return this.sym.name; }
get name(): string {
return this.sym.name;
}
get language(): string { return this.sym.language; }
get language(): string {
return this.sym.language;
}
get type(): Symbol|undefined { return this.sym.type; }
get type(): Symbol|undefined {
return this.sym.type;
}
get container(): Symbol|undefined { return this.sym.container; }
get container(): Symbol|undefined {
return this.sym.container;
}
get public(): boolean { return this.sym.public; }
get public(): boolean {
return this.sym.public;
}
get callable(): boolean { return this.sym.callable; }
get callable(): boolean {
return this.sym.callable;
}
get nullable(): boolean { return this.sym.nullable; }
get nullable(): boolean {
return this.sym.nullable;
}
get definition(): Definition { return this.sym.definition; }
get definition(): Definition {
return this.sym.definition;
}
get documentation(): ts.SymbolDisplayPart[] { return this.sym.documentation; }
get documentation(): ts.SymbolDisplayPart[] {
return this.sym.documentation;
}
members() { return this.sym.members(); }
members() {
return this.sym.members();
}
signatures() { return this.sym.signatures(); }
signatures() {
return this.sym.signatures();
}
selectSignature(types: Symbol[]) { return this.sym.selectSignature(types); }
selectSignature(types: Symbol[]) {
return this.sym.selectSignature(types);
}
indexed(argument: Symbol) { return this.sym.indexed(argument); }
indexed(argument: Symbol) {
return this.sym.indexed(argument);
}
typeArguments(): Symbol[]|undefined { return this.sym.typeArguments(); }
typeArguments(): Symbol[]|undefined {
return this.sym.typeArguments();
}
}

View File

@ -7,7 +7,7 @@
*/
import {StaticSymbolResolverHost} from '@angular/compiler';
import {MetadataCollector, MetadataReaderHost, createMetadataReaderCache, readMetadata} from '@angular/compiler-cli/src/language_services';
import {createMetadataReaderCache, MetadataCollector, MetadataReaderHost, readMetadata} from '@angular/compiler-cli/src/language_services';
import * as path from 'path';
import * as ts from 'typescript';
@ -26,10 +26,10 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost, Me
private readonly tsLSHost: ts.LanguageServiceHost,
private readonly getProgram: () => ts.Program) {
if (tsLSHost.directoryExists) {
this.directoryExists = directoryName => tsLSHost.directoryExists !(directoryName);
this.directoryExists = directoryName => tsLSHost.directoryExists!(directoryName);
}
if (tsLSHost.realpath) {
this.realpath = path => tsLSHost.realpath !(path);
this.realpath = path => tsLSHost.realpath!(path);
}
}
@ -53,14 +53,14 @@ class ReflectorModuleModuleResolutionHost implements ts.ModuleResolutionHost, Me
// resolution, and it's used by Angular to read metadata.json during
// metadata resolution.
if (this.tsLSHost.readFile) {
return this.tsLSHost.readFile(fileName) !;
return this.tsLSHost.readFile(fileName)!;
}
// As a fallback, read the JSON files from the editor snapshot.
const snapshot = this.tsLSHost.getScriptSnapshot(fileName);
if (!snapshot) {
// MetadataReaderHost readFile() declaration should be
// `readFile(fileName: string): string | undefined`
return undefined !;
return undefined!;
}
return snapshot.getText(0, snapshot.getLength());
}
@ -120,5 +120,7 @@ export class ReflectorHost implements StaticSymbolResolverHost {
return resolved ? resolved.resolvedFileName : null;
}
getOutputName(filePath: string) { return filePath; }
getOutputName(filePath: string) {
return filePath;
}
}

View File

@ -40,7 +40,7 @@ export interface Location {
/**
* A defnition location(s).
*/
export type Definition = Location[] | undefined;
export type Definition = Location[]|undefined;
/**
* A symbol describing a language element that can be referenced by expressions
@ -235,8 +235,8 @@ export enum BuiltinType {
*
* @publicApi
*/
export type DeclarationKind = 'attribute' | 'html attribute' | 'component' | 'element' | 'entity' |
'key' | 'method' | 'pipe' | 'property' | 'type' | 'reference' | 'variable';
export type DeclarationKind = 'attribute'|'html attribute'|'component'|'element'|'entity'|'key'|
'method'|'pipe'|'property'|'type'|'reference'|'variable';
/**
* Describes a symbol to type binding used to build a symbol table.
@ -287,7 +287,7 @@ export interface PipeInfo {
*
* @publicApi
*/
export type Pipes = PipeInfo[] | undefined;
export type Pipes = PipeInfo[]|undefined;
/**
* Describes the language context in which an Angular expression is evaluated.

View File

@ -39,7 +39,9 @@ abstract class BaseTemplate implements ng.TemplateSource {
/**
* Return the Angular StaticSymbol for the class that contains this template.
*/
get type() { return this.classSymbol; }
get type() {
return this.classSymbol;
}
/**
* Return a Map-like data structure that allows users to retrieve some or all

View File

@ -27,8 +27,7 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService {
const ngLS = createLanguageService(ngLSHost);
function getCompletionsAtPosition(
fileName: string, position: number,
options: tss.GetCompletionsAtPositionOptions | undefined) {
fileName: string, position: number, options: tss.GetCompletionsAtPositionOptions|undefined) {
if (!angularOnly) {
const results = tsLS.getCompletionsAtPosition(fileName, position, options);
if (results && results.entries.length) {
@ -93,8 +92,11 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService {
{}, tsLS,
// Then override the methods supported by Angular language service
{
getCompletionsAtPosition, getQuickInfoAtPosition, getSemanticDiagnostics,
getDefinitionAtPosition, getDefinitionAndBoundSpan,
getCompletionsAtPosition,
getQuickInfoAtPosition,
getSemanticDiagnostics,
getDefinitionAtPosition,
getDefinitionAndBoundSpan,
});
return proxy;
}

View File

@ -74,7 +74,7 @@ export interface TemplateSource {
*
* @publicApi
*/
export type TemplateSources = TemplateSource[] | undefined;
export type TemplateSources = TemplateSource[]|undefined;
/**
* Error information found getting declaration information
@ -264,7 +264,7 @@ export enum CompletionKind {
VARIABLE = 'variable',
}
export type CompletionEntry = Omit<ts.CompletionEntry, 'kind'>& {
export type CompletionEntry = Omit<ts.CompletionEntry, 'kind'>&{
kind: CompletionKind,
};
@ -361,5 +361,6 @@ export interface Hover {
* @publicApi
*/
export type LanguageService = Pick<
ts.LanguageService, 'getCompletionsAtPosition'|'getDefinitionAndBoundSpan'|
'getQuickInfoAtPosition'|'getSemanticDiagnostics'>;
ts.LanguageService,
'getCompletionsAtPosition'|'getDefinitionAndBoundSpan'|'getQuickInfoAtPosition'|
'getSemanticDiagnostics'>;

View File

@ -6,14 +6,14 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AotSummaryResolver, CompileDirectiveSummary, CompileMetadataResolver, CompileNgModuleMetadata, CompilePipeSummary, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, FormattedError, FormattedMessageChain, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser, analyzeNgModules, createOfflineCompileUrlResolver, isFormattedError} from '@angular/compiler';
import {analyzeNgModules, AotSummaryResolver, CompileDirectiveSummary, CompileMetadataResolver, CompileNgModuleMetadata, CompilePipeSummary, CompilerConfig, createOfflineCompileUrlResolver, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, FormattedError, FormattedMessageChain, HtmlParser, I18NHtmlParser, isFormattedError, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, Parser, ParseTreeResult, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, TemplateParser} from '@angular/compiler';
import {SchemaMetadata, ViewEncapsulation, ɵConsole as Console} from '@angular/core';
import * as tss from 'typescript/lib/tsserverlibrary';
import {AstResult} from './common';
import {createLanguageService} from './language_service';
import {ReflectorHost} from './reflector_host';
import {ExternalTemplate, InlineTemplate, getClassDeclFromDecoratorProp, getPropertyAssignmentFromValue} from './template';
import {ExternalTemplate, getClassDeclFromDecoratorProp, getPropertyAssignmentFromValue, InlineTemplate} from './template';
import {Declaration, DeclarationError, DiagnosticMessageChain, LanguageService, LanguageServiceHost, Span, TemplateSource} from './types';
import {findTightestNode, getDirectiveClassLike} from './utils';
@ -35,14 +35,18 @@ export function createLanguageServiceFromTypescript(
* syntactically incorrect templates.
*/
export class DummyHtmlParser extends HtmlParser {
parse(): ParseTreeResult { return new ParseTreeResult([], []); }
parse(): ParseTreeResult {
return new ParseTreeResult([], []);
}
}
/**
* Avoid loading resources in the language servcie by using a dummy loader.
*/
export class DummyResourceLoader extends ResourceLoader {
get(url: string): Promise<string> { return Promise.resolve(''); }
get(url: string): Promise<string> {
return Promise.resolve('');
}
}
/**
@ -74,10 +78,18 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
readonly tsLsHost: tss.LanguageServiceHost, private readonly tsLS: tss.LanguageService) {
this.summaryResolver = new AotSummaryResolver(
{
loadSummary(filePath: string) { return null; },
isSourceFile(sourceFilePath: string) { return true; },
toSummaryFileName(sourceFilePath: string) { return sourceFilePath; },
fromSummaryFileName(filePath: string): string{return filePath;},
loadSummary(filePath: string) {
return null;
},
isSourceFile(sourceFilePath: string) {
return true;
},
toSummaryFileName(sourceFilePath: string) {
return sourceFilePath;
},
fromSummaryFileName(filePath: string): string {
return filePath;
},
},
this.staticSymbolCache);
this.reflectorHost = new ReflectorHost(() => this.program, tsLsHost);
@ -159,7 +171,11 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
this.collectedErrors.clear();
this.resolver.clearCache();
const analyzeHost = {isSourceFile(filePath: string) { return true; }};
const analyzeHost = {
isSourceFile(filePath: string) {
return true;
}
};
const programFiles = this.program.getSourceFiles().map(sf => sf.fileName);
this.analyzedModules =
analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver);
@ -168,7 +184,7 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
const urlResolver = createOfflineCompileUrlResolver();
for (const ngModule of this.analyzedModules.ngModules) {
for (const directive of ngModule.declaredDirectives) {
const {metadata} = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference) !;
const {metadata} = this.resolver.getNonNormalizedDirectiveMetadata(directive.reference)!;
if (metadata.isComponent && metadata.template && metadata.template.templateUrl) {
const templateName = urlResolver.resolve(
this.reflector.componentModuleUrl(directive.reference),
@ -494,9 +510,9 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
const parser = new TemplateParser(
new CompilerConfig(), this.reflector, expressionParser, new DomElementSchemaRegistry(),
htmlParser,
null !, // console
[] // tranforms
);
null!, // console
[] // tranforms
);
const htmlResult = htmlParser.parse(template.source, fileName, {
tokenizeExpansionForms: true,
preserveLineEndings: true, // do not convert CRLF to LF
@ -509,8 +525,12 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
return {
htmlAst: htmlResult.rootNodes,
templateAst: parseResult.templateAst,
directive: data.metadata, directives, pipes,
parseErrors: parseResult.errors, expressionParser, template,
directive: data.metadata,
directives,
pipes,
parseErrors: parseResult.errors,
expressionParser,
template,
};
}
@ -574,7 +594,7 @@ function spanOf(node: tss.Node): Span {
function spanAt(sourceFile: tss.SourceFile, line: number, column: number): Span|undefined {
if (line != null && column != null) {
const position = tss.getPositionOfLineAndCharacter(sourceFile, line, column);
const findChild = function findChild(node: tss.Node): tss.Node | undefined {
const findChild = function findChild(node: tss.Node): tss.Node|undefined {
if (node.kind > tss.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
const betterNode = tss.forEachChild(node, findChild);
return betterNode || node;

View File

@ -68,7 +68,7 @@ export function getClassFromStaticSymbol(
return classDeclaration;
}
}
}) as(ts.ClassDeclaration | undefined);
}) as (ts.ClassDeclaration | undefined);
}
return undefined;
@ -123,7 +123,9 @@ class TypeScriptSymbolQuery implements SymbolQuery {
return result || this.getBuiltinType(BuiltinType.Any);
}
getArrayType(type: Symbol): Symbol { return this.getBuiltinType(BuiltinType.Any); }
getArrayType(type: Symbol): Symbol {
return this.getBuiltinType(BuiltinType.Any);
}
getElementType(type: Symbol): Symbol|undefined {
if (type instanceof TypeWrapper) {
@ -193,13 +195,13 @@ class TypeScriptSymbolQuery implements SymbolQuery {
private getTemplateRefContextType(typeSymbol: ts.Symbol, context: TypeContext): Symbol|undefined {
const type = this.checker.getTypeOfSymbolAtLocation(typeSymbol, this.source);
const constructor = type.symbol && type.symbol.members &&
getFromSymbolTable(type.symbol.members !, '__constructor');
getFromSymbolTable(type.symbol.members!, '__constructor');
if (constructor) {
const constructorDeclaration = constructor.declarations ![0] as ts.ConstructorTypeNode;
const constructorDeclaration = constructor.declarations![0] as ts.ConstructorTypeNode;
for (const parameter of constructorDeclaration.parameters) {
const type = this.checker.getTypeAtLocation(parameter.type !);
if (type.symbol !.name == 'TemplateRef' && isReferenceType(type)) {
const type = this.checker.getTypeAtLocation(parameter.type!);
if (type.symbol!.name == 'TemplateRef' && isReferenceType(type)) {
const typeWrapper = new TypeWrapper(type, context);
const typeArguments = typeWrapper.typeArguments();
if (typeArguments && typeArguments.length === 1) {
@ -237,7 +239,9 @@ class TypeWrapper implements Symbol {
}
}
get name(): string { return this.context.checker.typeToString(this.tsType); }
get name(): string {
return this.context.checker.typeToString(this.tsType);
}
public readonly kind: DeclarationKind = 'type';
@ -249,7 +253,9 @@ class TypeWrapper implements Symbol {
public readonly public: boolean = true;
get callable(): boolean { return typeCallable(this.tsType); }
get callable(): boolean {
return typeCallable(this.tsType);
}
get nullable(): boolean {
return this.context.checker.getNonNullableType(this.tsType) != this.tsType;
@ -276,7 +282,9 @@ class TypeWrapper implements Symbol {
return new SymbolTableWrapper(this.tsType.getApparentProperties(), this.context, this.tsType);
}
signatures(): Signature[] { return signaturesOf(this.tsType, this.context); }
signatures(): Signature[] {
return signaturesOf(this.tsType, this.context);
}
selectSignature(types: Symbol[]): Signature|undefined {
return selectSignature(this.tsType, this.context, types);
@ -332,30 +340,44 @@ class SymbolWrapper implements Symbol {
symbol: ts.Symbol,
/** TypeScript type context of the symbol. */
private context: TypeContext,
/** Type of the TypeScript symbol, if known. If not provided, the type of the symbol
* will be determined dynamically; see `SymbolWrapper#tsType`. */
/**
* Type of the TypeScript symbol, if known. If not provided, the type of the symbol
* will be determined dynamically; see `SymbolWrapper#tsType`.
*/
private _tsType?: ts.Type) {
this.symbol = symbol && context && (symbol.flags & ts.SymbolFlags.Alias) ?
context.checker.getAliasedSymbol(symbol) :
symbol;
}
get name(): string { return this.symbol.name; }
get name(): string {
return this.symbol.name;
}
get kind(): DeclarationKind { return this.callable ? 'method' : 'property'; }
get kind(): DeclarationKind {
return this.callable ? 'method' : 'property';
}
get type(): TypeWrapper { return new TypeWrapper(this.tsType, this.context); }
get type(): TypeWrapper {
return new TypeWrapper(this.tsType, this.context);
}
get container(): Symbol|undefined { return getContainerOf(this.symbol, this.context); }
get container(): Symbol|undefined {
return getContainerOf(this.symbol, this.context);
}
get public(): boolean {
// Symbols that are not explicitly made private are public.
return !isSymbolPrivate(this.symbol);
}
get callable(): boolean { return typeCallable(this.tsType); }
get callable(): boolean {
return typeCallable(this.tsType);
}
get definition(): Definition { return definitionFromTsSymbol(this.symbol); }
get definition(): Definition {
return definitionFromTsSymbol(this.symbol);
}
get documentation(): ts.SymbolDisplayPart[] {
return this.symbol.getDocumentationComment(this.context.checker);
@ -368,21 +390,27 @@ class SymbolWrapper implements Symbol {
const typeWrapper = new TypeWrapper(declaredType, this.context);
this._members = typeWrapper.members();
} else {
this._members = new SymbolTableWrapper(this.symbol.members !, this.context, this.tsType);
this._members = new SymbolTableWrapper(this.symbol.members!, this.context, this.tsType);
}
}
return this._members;
}
signatures(): Signature[] { return signaturesOf(this.tsType, this.context); }
signatures(): Signature[] {
return signaturesOf(this.tsType, this.context);
}
selectSignature(types: Symbol[]): Signature|undefined {
return selectSignature(this.tsType, this.context, types);
}
indexed(argument: Symbol): Symbol|undefined { return undefined; }
indexed(argument: Symbol): Symbol|undefined {
return undefined;
}
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); }
typeArguments(): Symbol[]|undefined {
return this.type.typeArguments();
}
private get tsType(): ts.Type {
let type = this._tsType;
@ -403,29 +431,53 @@ class DeclaredSymbol implements Symbol {
constructor(private declaration: SymbolDeclaration) {}
get name() { return this.declaration.name; }
get name() {
return this.declaration.name;
}
get kind() { return this.declaration.kind; }
get kind() {
return this.declaration.kind;
}
get container(): Symbol|undefined { return undefined; }
get container(): Symbol|undefined {
return undefined;
}
get type(): Symbol { return this.declaration.type; }
get type(): Symbol {
return this.declaration.type;
}
get callable(): boolean { return this.type.callable; }
get callable(): boolean {
return this.type.callable;
}
get definition(): Definition { return this.declaration.definition; }
get definition(): Definition {
return this.declaration.definition;
}
get documentation(): ts.SymbolDisplayPart[] { return this.declaration.type.documentation; }
get documentation(): ts.SymbolDisplayPart[] {
return this.declaration.type.documentation;
}
members(): SymbolTable { return this.type.members(); }
members(): SymbolTable {
return this.type.members();
}
signatures(): Signature[] { return this.type.signatures(); }
signatures(): Signature[] {
return this.type.signatures();
}
selectSignature(types: Symbol[]): Signature|undefined { return this.type.selectSignature(types); }
selectSignature(types: Symbol[]): Signature|undefined {
return this.type.selectSignature(types);
}
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); }
typeArguments(): Symbol[]|undefined {
return this.type.typeArguments();
}
indexed(argument: Symbol): Symbol|undefined { return undefined; }
indexed(argument: Symbol): Symbol|undefined {
return undefined;
}
}
class SignatureWrapper implements Signature {
@ -435,15 +487,21 @@ class SignatureWrapper implements Signature {
return new SymbolTableWrapper(this.signature.getParameters(), this.context);
}
get result(): Symbol { return new TypeWrapper(this.signature.getReturnType(), this.context); }
get result(): Symbol {
return new TypeWrapper(this.signature.getReturnType(), this.context);
}
}
class SignatureResultOverride implements Signature {
constructor(private signature: Signature, private resultType: Symbol) {}
get arguments(): SymbolTable { return this.signature.arguments; }
get arguments(): SymbolTable {
return this.signature.arguments;
}
get result(): Symbol { return this.resultType; }
get result(): Symbol {
return this.resultType;
}
}
export function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable {
@ -456,7 +514,7 @@ export function toSymbolTableFactory(symbols: ts.Symbol[]): ts.SymbolTable {
return result as ts.SymbolTable;
}
function toSymbols(symbolTable: ts.SymbolTable | undefined): ts.Symbol[] {
function toSymbols(symbolTable: ts.SymbolTable|undefined): ts.Symbol[] {
if (!symbolTable) return [];
const table = symbolTable as any;
@ -507,7 +565,9 @@ class SymbolTableWrapper implements SymbolTable {
}
}
get size(): number { return this.symbols.length; }
get size(): number {
return this.symbols.length;
}
get(key: string): Symbol|undefined {
const symbol = getFromSymbolTable(this.symbolTable, key);
@ -535,20 +595,26 @@ class SymbolTableWrapper implements SymbolTable {
this.stringIndexType !== undefined;
}
values(): Symbol[] { return this.symbols.map(s => new SymbolWrapper(s, this.context)); }
values(): Symbol[] {
return this.symbols.map(s => new SymbolWrapper(s, this.context));
}
}
class MapSymbolTable implements SymbolTable {
private map = new Map<string, Symbol>();
private _values: Symbol[] = [];
get size(): number { return this.map.size; }
get size(): number {
return this.map.size;
}
get(key: string): Symbol|undefined { return this.map.get(key); }
get(key: string): Symbol|undefined {
return this.map.get(key);
}
add(symbol: Symbol) {
if (this.map.has(symbol.name)) {
const previous = this.map.get(symbol.name) !;
const previous = this.map.get(symbol.name)!;
this._values[this._values.indexOf(previous)] = symbol;
}
this.map.set(symbol.name, symbol);
@ -561,7 +627,9 @@ class MapSymbolTable implements SymbolTable {
}
}
has(key: string): boolean { return this.map.has(key); }
has(key: string): boolean {
return this.map.has(key);
}
values(): Symbol[] {
// Switch to this.map.values once iterables are supported by the target language.
@ -572,7 +640,9 @@ class MapSymbolTable implements SymbolTable {
class PipesTable implements SymbolTable {
constructor(private pipes: CompilePipeSummary[], private context: TypeContext) {}
get size() { return this.pipes.length; }
get size() {
return this.pipes.length;
}
get(key: string): Symbol|undefined {
const pipe = this.pipes.find(pipe => pipe.name == key);
@ -581,9 +651,13 @@ class PipesTable implements SymbolTable {
}
}
has(key: string): boolean { return this.pipes.find(pipe => pipe.name == key) != null; }
has(key: string): boolean {
return this.pipes.find(pipe => pipe.name == key) != null;
}
values(): Symbol[] { return this.pipes.map(pipe => new PipeSymbol(pipe, this.context)); }
values(): Symbol[] {
return this.pipes.map(pipe => new PipeSymbol(pipe, this.context));
}
}
// This matches .d.ts files that look like ".../<package-name>/<package-name>.d.ts",
@ -600,9 +674,13 @@ class PipeSymbol implements Symbol {
constructor(private pipe: CompilePipeSummary, private context: TypeContext) {}
get name(): string { return this.pipe.name; }
get name(): string {
return this.pipe.name;
}
get type(): TypeWrapper { return new TypeWrapper(this.tsType, this.context); }
get type(): TypeWrapper {
return new TypeWrapper(this.tsType, this.context);
}
get definition(): Definition|undefined {
const symbol = this.tsType.getSymbol();
@ -617,12 +695,16 @@ class PipeSymbol implements Symbol {
return symbol.getDocumentationComment(this.context.checker);
}
members(): SymbolTable { return EmptyTable.instance; }
members(): SymbolTable {
return EmptyTable.instance;
}
signatures(): Signature[] { return signaturesOf(this.tsType, this.context); }
signatures(): Signature[] {
return signaturesOf(this.tsType, this.context);
}
selectSignature(types: Symbol[]): Signature|undefined {
let signature = selectSignature(this.tsType, this.context, types) !;
let signature = selectSignature(this.tsType, this.context, types)!;
if (types.length > 0) {
const parameterType = types[0];
let resultType: Symbol|undefined = undefined;
@ -645,16 +727,20 @@ class PipeSymbol implements Symbol {
return signature;
}
indexed(argument: Symbol): Symbol|undefined { return undefined; }
indexed(argument: Symbol): Symbol|undefined {
return undefined;
}
typeArguments(): Symbol[]|undefined { return this.type.typeArguments(); }
typeArguments(): Symbol[]|undefined {
return this.type.typeArguments();
}
private get tsType(): ts.Type {
let type = this._tsType;
if (!type) {
const classSymbol = this.findClassSymbol(this.pipe.type.reference);
if (classSymbol) {
type = this._tsType = this.findTransformMethodType(classSymbol) !;
type = this._tsType = this.findTransformMethodType(classSymbol)!;
}
if (!type) {
type = this._tsType = getTsTypeFromBuiltinType(BuiltinType.Any, this.context);
@ -700,9 +786,15 @@ function findClassSymbolInContext(type: StaticSymbol, context: TypeContext): ts.
class EmptyTable implements SymbolTable {
public readonly size: number = 0;
get(key: string): Symbol|undefined { return undefined; }
has(key: string): boolean { return false; }
values(): Symbol[] { return []; }
get(key: string): Symbol|undefined {
return undefined;
}
has(key: string): boolean {
return false;
}
values(): Symbol[] {
return [];
}
static instance = new EmptyTable();
}
@ -743,7 +835,7 @@ function getTsTypeFromBuiltinType(builtinType: BuiltinType, ctx: TypeContext): t
function spanAt(sourceFile: ts.SourceFile, line: number, column: number): Span|undefined {
if (line != null && column != null) {
const position = ts.getPositionOfLineAndCharacter(sourceFile, line, column);
const findChild = function findChild(node: ts.Node): ts.Node | undefined {
const findChild = function findChild(node: ts.Node): ts.Node|undefined {
if (node.kind > ts.SyntaxKind.LastToken && node.pos <= position && node.end > position) {
const betterNode = ts.forEachChild(node, findChild);
return betterNode || node;
@ -779,7 +871,7 @@ function parentDeclarationOf(node: ts.Node): ts.Node|undefined {
case ts.SyntaxKind.SourceFile:
return undefined;
}
node = node.parent !;
node = node.parent!;
}
}
@ -797,7 +889,7 @@ function getContainerOf(symbol: ts.Symbol, context: TypeContext): Symbol|undefin
}
}
function typeKindOf(type: ts.Type | undefined): BuiltinType {
function typeKindOf(type: ts.Type|undefined): BuiltinType {
if (type) {
if (type.flags & ts.TypeFlags.Any) {
return BuiltinType.Any;

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AstPath, BoundEventAst, CompileDirectiveSummary, CompileTypeMetadata, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, HtmlAstPath, Identifiers, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, RecursiveVisitor, TemplateAst, TemplateAstPath, identifierName, templateVisitAll, visitAll} from '@angular/compiler';
import {AstPath, BoundEventAst, CompileDirectiveSummary, CompileTypeMetadata, CssSelector, DirectiveAst, ElementAst, EmbeddedTemplateAst, HtmlAstPath, identifierName, Identifiers, Node, ParseSourceSpan, RecursiveTemplateAstVisitor, RecursiveVisitor, TemplateAst, TemplateAstPath, templateVisitAll, visitAll} from '@angular/compiler';
import * as ts from 'typescript';
import {AstResult, SelectorInfo} from './common';
@ -25,8 +25,8 @@ export function isParseSourceSpan(value: any): value is ParseSourceSpan {
export function spanOf(span: SpanHolder): Span;
export function spanOf(span: ParseSourceSpan): Span;
export function spanOf(span: SpanHolder | ParseSourceSpan | undefined): Span|undefined;
export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined {
export function spanOf(span: SpanHolder|ParseSourceSpan|undefined): Span|undefined;
export function spanOf(span?: SpanHolder|ParseSourceSpan): Span|undefined {
if (!span) return undefined;
if (isParseSourceSpan(span)) {
return {start: span.start.offset, end: span.end.offset};
@ -36,7 +36,7 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined {
} else if (span.children && span.children.length) {
return {
start: span.sourceSpan.start.offset,
end: spanOf(span.children[span.children.length - 1]) !.end
end: spanOf(span.children[span.children.length - 1])!.end
};
}
return {start: span.sourceSpan.start.offset, end: span.sourceSpan.end.offset};
@ -44,8 +44,9 @@ export function spanOf(span?: SpanHolder | ParseSourceSpan): Span|undefined {
}
export function inSpan(position: number, span?: Span, exclusive?: boolean): boolean {
return span != null && (exclusive ? position >= span.start && position < span.end :
position >= span.start && position <= span.end);
return span != null &&
(exclusive ? position >= span.start && position < span.end :
position >= span.start && position <= span.end);
}
export function offsetSpan(span: Span, amount: number): Span {
@ -71,7 +72,7 @@ export function getSelectors(info: AstResult): SelectorInfo {
const map = new Map<CssSelector, CompileDirectiveSummary>();
const results: CssSelector[] = [];
for (const directive of info.directives) {
const selectors: CssSelector[] = CssSelector.parse(directive.selector !);
const selectors: CssSelector[] = CssSelector.parse(directive.selector!);
for (const selector of selectors) {
results.push(selector);
map.set(selector, directive);
@ -141,7 +142,9 @@ export function findTemplateAstAt(ast: TemplateAst[], position: number): Templat
visitDirective(ast: DirectiveAst, context: any): any {
// Ignore the host properties of a directive
const result = this.visitChildren(context, visit => { visit(ast.inputs); });
const result = this.visitChildren(context, visit => {
visit(ast.inputs);
});
// We never care about the diretive itself, just its inputs.
if (path[path.length - 1] === ast) {
path.pop();

View File

@ -25,7 +25,9 @@ describe('completions', () => {
const ngHost = new TypeScriptServiceHost(mockHost, tsLS);
const ngLS = createLanguageService(ngHost);
beforeEach(() => { mockHost.reset(); });
beforeEach(() => {
mockHost.reset();
});
it('should be able to get entity completions', () => {
const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'entity-amp');
@ -102,7 +104,7 @@ describe('completions', () => {
const marker = mockHost.getLocationMarkerFor(APP_COMPONENT, 'h2-hero');
const completions = ngLS.getCompletionsAtPosition(APP_COMPONENT, marker.start);
expect(completions).toBeDefined();
const internal = completions !.entries.find(e => e.name === 'internal');
const internal = completions!.entries.find(e => e.name === 'internal');
expect(internal).toBeUndefined();
});
@ -202,7 +204,7 @@ describe('completions', () => {
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
expect(completions).toBeDefined();
expect(completions !.entries).toContain(jasmine.objectContaining({
expect(completions!.entries).toContain(jasmine.objectContaining({
name: 'myClick',
kind: CompletionKind.METHOD as any,
insertText: 'myClick()',
@ -214,7 +216,7 @@ describe('completions', () => {
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'pipe-method');
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
expect(completions).toBeDefined();
expect(completions !.entries).toContain(jasmine.objectContaining({
expect(completions!.entries).toContain(jasmine.objectContaining({
name: 'lowercase',
kind: CompletionKind.PIPE as any,
insertText: 'lowercase',
@ -234,7 +236,7 @@ describe('completions', () => {
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, location);
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
expect(completions).toBeDefined();
const {entries} = completions !;
const {entries} = completions!;
expect(entries).not.toContain(jasmine.objectContaining({name: 'div'}));
expect(entries).not.toContain(jasmine.objectContaining({name: 'h1'}));
expect(entries).not.toContain(jasmine.objectContaining({name: 'h2'}));
@ -257,7 +259,7 @@ describe('completions', () => {
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'h1-after-space');
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start);
expect(completions).toBeDefined();
const {entries} = completions !;
const {entries} = completions!;
expect(entries).not.toContain(jasmine.objectContaining({name: 'class'}));
expect(entries).not.toContain(jasmine.objectContaining({name: 'id'}));
expect(entries).not.toContain(jasmine.objectContaining({name: 'onclick'}));
@ -536,9 +538,9 @@ describe('completions', () => {
}
`);
const location = mockHost.getLocationMarkerFor(fileName, 'key');
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
expect(completions).toBeDefined();
const completion = completions.entries.find(entry => entry.name === 'key') !;
const completion = completions.entries.find(entry => entry.name === 'key')!;
expect(completion).toBeDefined();
expect(completion.kind).toBe('property');
expect(completion.replacementSpan).toBeUndefined();
@ -553,9 +555,9 @@ describe('completions', () => {
export class FooComponent {}
`);
const location = mockHost.getLocationMarkerFor(fileName, 'start');
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
expect(completions).toBeDefined();
const completion = completions.entries.find(entry => entry.name === 'acronym') !;
const completion = completions.entries.find(entry => entry.name === 'acronym')!;
expect(completion).toBeDefined();
expect(completion.kind).toBe('html element');
expect(completion.replacementSpan).toEqual({start: location.start, length: 3});
@ -570,9 +572,9 @@ describe('completions', () => {
export class FooComponent {}
`);
const location = mockHost.getLocationMarkerFor(fileName, 'end');
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
expect(completions).toBeDefined();
const completion = completions.entries.find(entry => entry.name === 'acronym') !;
const completion = completions.entries.find(entry => entry.name === 'acronym')!;
expect(completion).toBeDefined();
expect(completion.kind).toBe('html element');
expect(completion.replacementSpan).toEqual({start: location.start - 4, length: 4});
@ -591,9 +593,9 @@ describe('completions', () => {
}
`);
const location = mockHost.getLocationMarkerFor(fileName, 'key');
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
expect(completions).toBeDefined();
const completion = completions.entries.find(entry => entry.name === 'key') !;
const completion = completions.entries.find(entry => entry.name === 'key')!;
expect(completion).toBeDefined();
expect(completion.kind).toBe('property');
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 5});
@ -612,9 +614,9 @@ describe('completions', () => {
}
`);
const location = mockHost.getLocationMarkerFor(fileName, 'field');
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
expect(completions).toBeDefined();
const completion = completions.entries.find(entry => entry.name === '$title_1') !;
const completion = completions.entries.find(entry => entry.name === '$title_1')!;
expect(completion).toBeDefined();
expect(completion.kind).toBe('property');
expect(completion.replacementSpan).toEqual({start: location.start, length: 8});
@ -631,9 +633,9 @@ describe('completions', () => {
export class FooComponent {}
`);
const location = mockHost.getLocationMarkerFor(fileName, 'click');
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
expect(completions).toBeDefined();
const completion = completions.entries.find(entry => entry.name === 'click') !;
const completion = completions.entries.find(entry => entry.name === 'click')!;
expect(completion).toBeDefined();
expect(completion.kind).toBe(CompletionKind.ATTRIBUTE);
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2});
@ -652,9 +654,9 @@ describe('completions', () => {
}
`);
const location = mockHost.getLocationMarkerFor(fileName, 'handleClick');
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
expect(completions).toBeDefined();
const completion = completions.entries.find(entry => entry.name === 'handleClick') !;
const completion = completions.entries.find(entry => entry.name === 'handleClick')!;
expect(completion).toBeDefined();
expect(completion.kind).toBe('method');
expect(completion.replacementSpan).toEqual({start: location.start - 3, length: 3});
@ -671,9 +673,9 @@ describe('completions', () => {
export class FooComponent {}
`);
const location = mockHost.getLocationMarkerFor(fileName, 'div');
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
expect(completions).toBeDefined();
const completion = completions.entries.find(entry => entry.name === 'div') !;
const completion = completions.entries.find(entry => entry.name === 'div')!;
expect(completion).toBeDefined();
expect(completion.kind).toBe('html element');
expect(completion.replacementSpan).toEqual({start: location.start - 2, length: 2});
@ -690,9 +692,9 @@ describe('completions', () => {
export class FooComponent {}
`);
const location = mockHost.getLocationMarkerFor(fileName, 'model');
const completions = ngLS.getCompletionsAtPosition(fileName, location.start) !;
const completions = ngLS.getCompletionsAtPosition(fileName, location.start)!;
expect(completions).toBeDefined();
const completion = completions.entries.find(entry => entry.name === 'ngModel') !;
const completion = completions.entries.find(entry => entry.name === 'ngModel')!;
expect(completion).toBeDefined();
expect(completion.kind).toBe(CompletionKind.ATTRIBUTE);
expect(completion.replacementSpan).toEqual({start: location.start - 5, length: 5});
@ -741,14 +743,14 @@ describe('completions', () => {
it('should be able to get the completions (ref- prefix)', () => {
mockHost.override(TEST_TEMPLATE, `<form ref-itemForm="ngF~{reference}"></form>`);
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'reference');
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start) !;
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start)!;
expectContain(completions, CompletionKind.REFERENCE, ['ngForm']);
});
it('should be able to get the completions (# prefix)', () => {
mockHost.override(TEST_TEMPLATE, `<form #itemForm="ngF~{reference}"></form>`);
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'reference');
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start) !;
const completions = ngLS.getCompletionsAtPosition(TEST_TEMPLATE, marker.start)!;
expectContain(completions, CompletionKind.REFERENCE, ['ngForm']);
});
});
@ -789,9 +791,9 @@ describe('completions', () => {
});
function expectContain(
completions: ts.CompletionInfo | undefined, kind: CompletionKind, names: string[]) {
completions: ts.CompletionInfo|undefined, kind: CompletionKind, names: string[]) {
expect(completions).toBeDefined();
for (const name of names) {
expect(completions !.entries).toContain(jasmine.objectContaining({ name, kind } as any));
expect(completions!.entries).toContain(jasmine.objectContaining({name, kind} as any));
}
}

View File

@ -22,7 +22,9 @@ describe('definitions', () => {
const ngHost = new TypeScriptServiceHost(mockHost, service);
const ngService = createLanguageService(ngHost);
beforeEach(() => { mockHost.reset(); });
beforeEach(() => {
mockHost.reset();
});
it('should be able to find field in an interpolation', () => {
const fileName = mockHost.addCode(`
@ -36,12 +38,12 @@ describe('definitions', () => {
const marker = mockHost.getReferenceMarkerFor(fileName, 'name');
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
expect(textSpan).toEqual(marker);
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
const def = definitions ![0];
expect(definitions!.length).toBe(1);
const def = definitions![0];
expect(def.fileName).toBe(fileName);
expect(def.name).toBe('name');
@ -55,20 +57,20 @@ describe('definitions', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
expect(textSpan).toEqual(marker);
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
const def = definitions ![0];
expect(definitions!.length).toBe(1);
const def = definitions![0];
expect(def.fileName).toBe(PARSING_CASES);
expect(def.name).toBe('title');
expect(def.kind).toBe('property');
const fileContent = mockHost.readFile(def.fileName);
expect(fileContent !.substring(def.textSpan.start, def.textSpan.start + def.textSpan.length))
expect(fileContent!.substring(def.textSpan.start, def.textSpan.start + def.textSpan.length))
.toEqual(`title = 'Some title';`);
});
@ -84,12 +86,12 @@ describe('definitions', () => {
const marker = mockHost.getReferenceMarkerFor(fileName, 'myClick');
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
expect(textSpan).toEqual(mockHost.getLocationMarkerFor(fileName, 'my'));
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
const def = definitions ![0];
expect(definitions!.length).toBe(1);
const def = definitions![0];
expect(def.fileName).toBe(fileName);
expect(def.name).toBe('myClick');
@ -109,12 +111,12 @@ describe('definitions', () => {
const marker = mockHost.getReferenceMarkerFor(fileName, 'include');
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
expect(textSpan).toEqual(marker);
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
const def = definitions ![0];
expect(definitions!.length).toBe(1);
const def = definitions![0];
expect(def.fileName).toBe(fileName);
expect(def.name).toBe('include');
@ -134,7 +136,7 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
// Get the marker for bounded text in the code added above.
const boundedText = mockHost.getLocationMarkerFor(fileName, 'my');
@ -142,14 +144,14 @@ describe('definitions', () => {
// There should be exactly 1 definition
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
const def = definitions ![0];
expect(definitions!.length).toBe(1);
const def = definitions![0];
const refFileName = '/app/parsing-cases.ts';
expect(def.fileName).toBe(refFileName);
expect(def.name).toBe('TestComponent');
expect(def.kind).toBe('component');
const content = mockHost.readFile(refFileName) !;
const content = mockHost.readFile(refFileName)!;
const begin = '/*BeginTestComponent*/ ';
const start = content.indexOf(begin) + begin.length;
const end = content.indexOf(' /*EndTestComponent*/');
@ -171,7 +173,7 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
// Get the marker for bounded text in the code added above
const boundedText = mockHost.getLocationMarkerFor(fileName, 'my');
@ -179,14 +181,14 @@ describe('definitions', () => {
// There should be exactly 1 definition
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
const def = definitions ![0];
expect(definitions!.length).toBe(1);
const def = definitions![0];
const refFileName = '/app/parsing-cases.ts';
expect(def.fileName).toBe(refFileName);
expect(def.name).toBe('testEvent');
expect(def.kind).toBe('event');
const content = mockHost.readFile(refFileName) !;
const content = mockHost.readFile(refFileName)!;
const ref = `@Output('test') testEvent = new EventEmitter();`;
expect(def.textSpan).toEqual({
start: content.indexOf(ref),
@ -208,7 +210,7 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
// Get the marker for bounded text in the code added above
const boundedText = mockHost.getLocationMarkerFor(fileName, 'my');
@ -216,14 +218,14 @@ describe('definitions', () => {
// There should be exactly 1 definition
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
const def = definitions ![0];
expect(definitions!.length).toBe(1);
const def = definitions![0];
const refFileName = '/app/parsing-cases.ts';
expect(def.fileName).toBe(refFileName);
expect(def.name).toBe('name');
expect(def.kind).toBe('property');
const content = mockHost.readFile(refFileName) !;
const content = mockHost.readFile(refFileName)!;
const ref = `@Input('tcName') name = 'test';`;
expect(def.textSpan).toEqual({
start: content.indexOf(ref),
@ -245,14 +247,14 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
expect(textSpan).toEqual(marker);
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(4);
expect(definitions!.length).toBe(4);
const refFileName = '/node_modules/@angular/common/common.d.ts';
for (const def of definitions !) {
for (const def of definitions!) {
expect(def.fileName).toBe(refFileName);
expect(def.name).toBe('async');
expect(def.kind).toBe('pipe');
@ -268,14 +270,14 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
expect(textSpan).toEqual(marker);
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
expect(definitions!.length).toBe(1);
const refFileName = '/node_modules/@angular/common/common.d.ts';
for (const def of definitions !) {
for (const def of definitions!) {
expect(def.fileName).toBe(refFileName);
expect(def.name).toBe('date');
expect(def.kind).toBe('pipe');
@ -292,17 +294,17 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
// Get the marker for bounded text in the code added above
const boundedText = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'my');
expect(textSpan).toEqual(boundedText);
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
expect(definitions!.length).toBe(1);
const refFileName = '/node_modules/@angular/common/common.d.ts';
const def = definitions ![0];
const def = definitions![0];
expect(def.fileName).toBe(refFileName);
expect(def.name).toBe('NgForOf');
expect(def.kind).toBe('directive');
@ -317,17 +319,17 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
// Get the marker for bounded text in the code added above
expect(textSpan).toEqual(marker);
expect(definitions).toBeDefined();
// The two definitions are setter and getter of 'ngForTrackBy'.
expect(definitions !.length).toBe(2);
expect(definitions!.length).toBe(2);
const refFileName = '/node_modules/@angular/common/common.d.ts';
definitions !.forEach(def => {
definitions!.forEach(def => {
expect(def.fileName).toBe(refFileName);
expect(def.name).toBe('ngForTrackBy');
expect(def.kind).toBe('method');
@ -343,19 +345,19 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
expect(textSpan).toEqual(marker);
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
expect(definitions!.length).toBe(1);
const refFileName = '/app/parsing-cases.ts';
const def = definitions ![0];
const def = definitions![0];
expect(def.fileName).toBe(refFileName);
expect(def.name).toBe('heroes');
expect(def.kind).toBe('property');
const content = mockHost.readFile(refFileName) !;
const content = mockHost.readFile(refFileName)!;
expect(content.substring(def.textSpan.start, def.textSpan.start + def.textSpan.length))
.toEqual(`heroes: Hero[] = [this.hero];`);
});
@ -370,28 +372,28 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(TEST_TEMPLATE, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
// Get the marker for bounded text in the code added above
const boundedText = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'my');
expect(textSpan).toEqual(boundedText);
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(2);
const [def1, def2] = definitions !;
expect(definitions!.length).toBe(2);
const [def1, def2] = definitions!;
const refFileName = '/app/parsing-cases.ts';
expect(def1.fileName).toBe(refFileName);
expect(def1.name).toBe('model');
expect(def1.kind).toBe('property');
let content = mockHost.readFile(refFileName) !;
let content = mockHost.readFile(refFileName)!;
expect(content.substring(def1.textSpan.start, def1.textSpan.start + def1.textSpan.length))
.toEqual(`@Input() model: string = 'model';`);
expect(def2.fileName).toBe(refFileName);
expect(def2.name).toBe('modelChange');
expect(def2.kind).toBe('event');
content = mockHost.readFile(refFileName) !;
content = mockHost.readFile(refFileName)!;
expect(content.substring(def2.textSpan.start, def2.textSpan.start + def2.textSpan.length))
.toEqual(`@Output() modelChange: EventEmitter<string> = new EventEmitter();`);
});
@ -407,13 +409,13 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
expect(textSpan).toEqual({start: marker.start - 2, length: 9});
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
const [def] = definitions !;
expect(definitions!.length).toBe(1);
const [def] = definitions!;
expect(def.fileName).toBe('/app/test.ng');
expect(def.textSpan).toEqual({start: 0, length: 0});
});
@ -430,13 +432,13 @@ describe('definitions', () => {
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
expect(textSpan).toEqual({start: marker.start - 2, length: 10});
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
const [def] = definitions !;
expect(definitions!.length).toBe(1);
const [def] = definitions!;
expect(def.fileName).toBe('/app/test.css');
expect(def.textSpan).toEqual({start: 0, length: 0});
});
@ -453,12 +455,12 @@ describe('definitions', () => {
const marker = mockHost.getReferenceMarkerFor(fileName, 'name');
const result = ngService.getDefinitionAndBoundSpan(fileName, marker.start);
expect(result).toBeDefined();
const {textSpan, definitions} = result !;
const {textSpan, definitions} = result!;
expect(textSpan).toEqual(marker);
expect(definitions).toBeDefined();
expect(definitions !.length).toBe(1);
const def = definitions ![0];
expect(definitions!.length).toBe(1);
const def = definitions![0];
expect(def.fileName).toBe(fileName);
expect(def.name).toBe('name');

View File

@ -7,7 +7,8 @@
*/
import * as ts from 'typescript';
import {DiagnosticMessage, createDiagnostic} from '../src/diagnostic_messages';
import {createDiagnostic, DiagnosticMessage} from '../src/diagnostic_messages';
describe('create diagnostic', () => {
it('should format and create diagnostics correctly', () => {

View File

@ -34,7 +34,9 @@ describe('diagnostics', () => {
const ngHost = new TypeScriptServiceHost(mockHost, tsLS);
const ngLS = createLanguageService(ngHost);
beforeEach(() => { mockHost.reset(); });
beforeEach(() => {
mockHost.reset();
});
it('should produce no diagnostics for test.ng', () => {
// there should not be any errors on existing external template
@ -300,7 +302,8 @@ describe('diagnostics', () => {
expect(messageText)
.toBe(
`The template context of 'CounterDirective' does not define an implicit value.\n` +
`If the context type is a base type or 'any', consider refining it to a more specific type.`, );
`If the context type is a base type or 'any', consider refining it to a more specific type.`,
);
const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb');
expect(start).toBe(span.start);
@ -337,7 +340,8 @@ describe('diagnostics', () => {
expect(category).toBe(ts.DiagnosticCategory.Error);
expect(messageText)
.toBe(
`Identifier 'missingField' is not defined. '{ implicitPerson: Person; }' does not contain such a member`, );
`Identifier 'missingField' is not defined. '{ implicitPerson: Person; }' does not contain such a member`,
);
const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb');
expect(start).toBe(span.start);
expect(length).toBe(span.length);
@ -355,7 +359,8 @@ describe('diagnostics', () => {
expect(category).toBe(ts.DiagnosticCategory.Error);
expect(messageText)
.toBe(
`Identifier 'missingField' is not defined. 'Person' does not contain such a member`, );
`Identifier 'missingField' is not defined. 'Person' does not contain such a member`,
);
const span = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'emb');
expect(start).toBe(span.start);
expect(length).toBe(span.length);
@ -396,7 +401,7 @@ describe('diagnostics', () => {
it('should reject it when not in an event binding', () => {
const content = mockHost.override(TEST_TEMPLATE, '<div [tabIndex]="$event"></div>');
const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE) !;
const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE)!;
expect(diagnostics.length).toBe(1);
const {messageText, start, length} = diagnostics[0];
expect(messageText)
@ -410,7 +415,7 @@ describe('diagnostics', () => {
it('should reject invalid properties on an event type', () => {
const content = mockHost.override(
TEST_TEMPLATE, '<div string-model (modelChange)="$event.notSubstring()"></div>');
const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE) !;
const diagnostics = ngLS.getSemanticDiagnostics(TEST_TEMPLATE)!;
expect(diagnostics.length).toBe(1);
const {messageText, start, length} = diagnostics[0];
expect(messageText)
@ -436,14 +441,14 @@ describe('diagnostics', () => {
template: '<div></div>'
})
export class MyComponent {}`);
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
const diagnostics = ngLS.getSemanticDiagnostics(fileName)!;
expect(diagnostics.length).toBe(1);
const {messageText, start, length} = diagnostics[0];
expect(messageText)
.toBe(
`Component 'MyComponent' is not included in a module and will not be available inside a template. Consider adding it to a NgModule declaration.`);
const content = mockHost.readFile(fileName) !;
expect(content.substring(start !, start ! + length !)).toBe('MyComponent');
const content = mockHost.readFile(fileName)!;
expect(content.substring(start!, start! + length!)).toBe('MyComponent');
});
@ -495,7 +500,7 @@ describe('diagnostics', () => {
}`);
const tsDiags = tsLS.getSemanticDiagnostics(APP_COMPONENT);
expect(tsDiags).toEqual([]);
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT) !;
const ngDiags = ngLS.getSemanticDiagnostics(APP_COMPONENT)!;
expect(ngDiags.length).toBe(1);
const {messageText, start, length} = ngDiags[0];
const keyword = `() => 'foo'`;
@ -504,9 +509,9 @@ describe('diagnostics', () => {
// messageText is a three-part chain
const firstPart = messageText as ts.DiagnosticMessageChain;
expect(firstPart.messageText).toBe(`Error during template compile of 'AppComponent'`);
const secondPart = firstPart.next !;
const secondPart = firstPart.next!;
expect(secondPart[0].messageText).toBe('Function expressions are not supported in decorators');
const thirdPart = secondPart[0].next !;
const thirdPart = secondPart[0].next!;
expect(thirdPart[0].messageText)
.toBe('Consider changing the function expression into an exported function');
expect(thirdPart[0].next).toBeFalsy();
@ -568,7 +573,7 @@ describe('diagnostics', () => {
export class AppComponent {
onClick() { }
}`);
const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT) !;
const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT)!;
const {messageText, start, length} = diagnostics[0];
expect(messageText).toBe('Unexpected callable expression. Expected a method call');
const keyword = `"onClick"`;
@ -682,7 +687,7 @@ describe('diagnostics', () => {
.toBe(
'Invalid providers for "AppComponent in /app/app.component.ts" - only instances of Provider and Type are allowed, got: [?null?]');
// TODO: Looks like this is the wrong span. Should point to 'null' instead.
expect(content.substring(start !, start ! + length !)).toBe('AppComponent');
expect(content.substring(start!, start! + length!)).toBe('AppComponent');
});
// Issue #15768
@ -815,12 +820,12 @@ describe('diagnostics', () => {
const marker = mockHost.getReferenceMarkerFor(fileName, 'notAFile');
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
const diagnostics = ngLS.getSemanticDiagnostics(fileName)!;
const urlDiagnostic =
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
expect(urlDiagnostic).toBeDefined();
const {start, length} = urlDiagnostic !;
const {start, length} = urlDiagnostic!;
expect(start).toBe(marker.start);
expect(length).toBe(marker.length);
});
@ -832,7 +837,7 @@ describe('diagnostics', () => {
})
export class MyComponent {}`);
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
const diagnostics = ngLS.getSemanticDiagnostics(fileName)!;
const urlDiagnostic =
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
expect(urlDiagnostic).toBeUndefined();
@ -849,9 +854,9 @@ describe('diagnostics', () => {
const diags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(diags.length).toBe(1);
const {file, messageText, start, length} = diags[0];
expect(file !.fileName).toBe(APP_COMPONENT);
expect(file!.fileName).toBe(APP_COMPONENT);
expect(messageText).toBe(`Component 'AppComponent' must have a template or templateUrl`);
expect(content.substring(start !, start ! + length !)).toBe('AppComponent');
expect(content.substring(start!, start! + length!)).toBe('AppComponent');
});
it('should report diagnostic for both template and templateUrl', () => {
@ -867,10 +872,10 @@ describe('diagnostics', () => {
const diags = ngLS.getSemanticDiagnostics(APP_COMPONENT);
expect(diags.length).toBe(1);
const {file, messageText, start, length} = diags[0];
expect(file !.fileName).toBe(APP_COMPONENT);
expect(file!.fileName).toBe(APP_COMPONENT);
expect(messageText)
.toBe(`Component 'AppComponent' must not have both template and templateUrl`);
expect(content.substring(start !, start ! + length !)).toBe('AppComponent');
expect(content.substring(start!, start! + length!)).toBe('AppComponent');
});
it('should report errors for invalid styleUrls', () => {
@ -882,12 +887,12 @@ describe('diagnostics', () => {
const marker = mockHost.getReferenceMarkerFor(fileName, 'notAFile');
const diagnostics = ngLS.getSemanticDiagnostics(fileName) !;
const diagnostics = ngLS.getSemanticDiagnostics(fileName)!;
const urlDiagnostic =
diagnostics.find(d => d.messageText === 'URL does not point to a valid file');
expect(urlDiagnostic).toBeDefined();
const {start, length} = urlDiagnostic !;
const {start, length} = urlDiagnostic!;
expect(start).toBe(marker.start);
expect(length).toBe(marker.length);
});
@ -902,7 +907,7 @@ describe('diagnostics', () => {
})
export class AppComponent {}`);
const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT) !;
const diagnostics = ngLS.getSemanticDiagnostics(APP_COMPONENT)!;
expect(diagnostics.length).toBe(0);
});
});
@ -926,14 +931,14 @@ describe('diagnostics', () => {
`element references do not contain such a member`);
// Assert that the span is actually highlight the bounded text. The span
// would be off if CRLF endings are not handled properly.
expect(content.substring(start !, start ! + length !)).toBe(`line${i}`);
expect(content.substring(start!, start! + length!)).toBe(`line${i}`);
}
});
it('should work correctly with CRLF endings in inline template', () => {
const fileName = mockHost.addCode(
'\n@Component({template:`\r\n\r\n{{line}}`})export class ComponentCRLF {}');
const content = mockHost.readFile(fileName) !;
const content = mockHost.readFile(fileName)!;
const ngDiags = ngLS.getSemanticDiagnostics(fileName);
expect(ngDiags.length).toBeGreaterThan(0);
const {messageText, start, length} = ngDiags[0];
@ -942,7 +947,7 @@ describe('diagnostics', () => {
`Identifier 'line' is not defined. ` +
`The component declaration, template variable declarations, and ` +
`element references do not contain such a member`);
expect(content.substring(start !, start ! + length !)).toBe('line');
expect(content.substring(start!, start! + length!)).toBe('line');
});
it('should not produce diagnostics for non-exported directives', () => {
@ -972,8 +977,7 @@ describe('diagnostics', () => {
`Consider using the safe navigation operator (optional?.toLowerCase) ` +
`or non-null assertion operator (optional!.toLowerCase).`);
expect(category).toBe(ts.DiagnosticCategory.Suggestion);
expect(content.substring(start !, start ! + length !)).toBe('optional.toLowerCase()');
expect(content.substring(start!, start! + length!)).toBe('optional.toLowerCase()');
});
it('should suggest ? or ! operator if property receiver is nullable', () => {
@ -987,7 +991,7 @@ describe('diagnostics', () => {
`Consider using the safe navigation operator (optional?.length) ` +
`or non-null assertion operator (optional!.length).`);
expect(category).toBe(ts.DiagnosticCategory.Suggestion);
expect(content.substring(start !, start ! + length !)).toBe('optional.length');
expect(content.substring(start!, start! + length!)).toBe('optional.length');
});
it('should report error if method is not found on non-nullable receiver', () => {
@ -1003,7 +1007,7 @@ describe('diagnostics', () => {
expect(messageText)
.toBe(`Identifier 'someMethod' is not defined. 'string' does not contain such a member`);
expect(category).toBe(ts.DiagnosticCategory.Error);
expect(content.substring(start !, start ! + length !)).toBe(expression);
expect(content.substring(start!, start! + length!)).toBe(expression);
}
});
@ -1020,7 +1024,7 @@ describe('diagnostics', () => {
expect(messageText)
.toBe(`Identifier 'someProp' is not defined. 'string' does not contain such a member`);
expect(category).toBe(ts.DiagnosticCategory.Error);
expect(content.substring(start !, start ! + length !)).toBe(expression);
expect(content.substring(start!, start! + length!)).toBe(expression);
}
});
@ -1030,12 +1034,12 @@ describe('diagnostics', () => {
<p>{{myClick()()}}</p>
`);
const content = mockHost.readFile(TEST_TEMPLATE) !;
const content = mockHost.readFile(TEST_TEMPLATE)!;
const diags = ngLS.getSemanticDiagnostics(TEST_TEMPLATE);
expect(diags.length).toBe(1);
const {messageText, start, length} = diags[0] !;
const {messageText, start, length} = diags[0]!;
expect(messageText).toBe(`Call target 'myClick()' has non-callable type 'void'.`);
expect(content.substring(start !, start ! + length !)).toBe('myClick()()');
expect(content.substring(start!, start! + length!)).toBe('myClick()()');
});
});
});

View File

@ -13,7 +13,7 @@ import * as ts from 'typescript';
import {getTemplateExpressionDiagnostics} from '../src/expression_diagnostics';
import {DiagnosticContext, MockLanguageServiceHost, getDiagnosticTemplateInfo} from './mocks';
import {DiagnosticContext, getDiagnosticTemplateInfo, MockLanguageServiceHost} from './mocks';
describe('expression diagnostics', () => {
let registry: ts.DocumentRegistry;
@ -27,15 +27,15 @@ describe('expression diagnostics', () => {
registry = ts.createDocumentRegistry(false, '/src');
host = new MockLanguageServiceHost(['app/app.component.ts'], FILES, '/src');
service = ts.createLanguageService(host, registry);
const program = service.getProgram() !;
const program = service.getProgram()!;
const checker = program.getTypeChecker();
const symbolResolverHost = new ReflectorHost(() => program !, host);
context = new DiagnosticContext(service, program !, checker, symbolResolverHost);
const symbolResolverHost = new ReflectorHost(() => program!, host);
context = new DiagnosticContext(service, program!, checker, symbolResolverHost);
type = context.getStaticSymbol('app/app.component.ts', 'AppComponent');
});
it('should have no diagnostics in default app', () => {
function messageToString(messageText: string | ts.DiagnosticMessageChain): string {
function messageToString(messageText: string|ts.DiagnosticMessageChain): string {
if (typeof messageText == 'string') {
return messageText;
} else {
@ -107,13 +107,14 @@ describe('expression diagnostics', () => {
{{p.name.first}} {{p.name.last}}
</div>
`));
it('should reject misspelled field in *ngFor', () => reject(
`
it('should reject misspelled field in *ngFor',
() => reject(
`
<div *ngFor="let p of people">
{{p.names.first}} {{p.name.last}}
</div>
`,
'Identifier \'names\' is not defined'));
'Identifier \'names\' is not defined'));
it('should accept an async expression',
() => accept('{{(promised_person | async)?.name.first || ""}}'));
it('should reject an async misspelled field',
@ -124,25 +125,27 @@ describe('expression diagnostics', () => {
{{p.name.first}} {{p.name.last}}
</div>
`));
it('should reject misspelled field an async *ngFor', () => reject(
`
it('should reject misspelled field an async *ngFor',
() => reject(
`
<div *ngFor="let p of promised_people | async">
{{p.name.first}} {{p.nume.last}}
</div>
`,
'Identifier \'nume\' is not defined'));
'Identifier \'nume\' is not defined'));
it('should accept an async *ngIf', () => accept(`
<div *ngIf="promised_person | async as p">
{{p.name.first}} {{p.name.last}}
</div>
`));
it('should reject misspelled field in async *ngIf', () => reject(
`
it('should reject misspelled field in async *ngIf',
() => reject(
`
<div *ngIf="promised_person | async as p">
{{p.name.first}} {{p.nume.last}}
</div>
`,
'Identifier \'nume\' is not defined'));
'Identifier \'nume\' is not defined'));
it('should reject access to potentially undefined field',
() => reject(
`<div>{{maybe_person.name.first}}`,

View File

@ -8,7 +8,7 @@
import * as ts from 'typescript/lib/tsserverlibrary';
import {EMPTY_SYMBOL_TABLE, createGlobalSymbolTable} from '../src/global_symbols';
import {createGlobalSymbolTable, EMPTY_SYMBOL_TABLE} from '../src/global_symbols';
import {getSymbolQuery} from '../src/typescript_symbols';
import {MockTypescriptHost} from './test_utils';
@ -18,7 +18,7 @@ describe('GlobalSymbolTable', () => {
const tsLS = ts.createLanguageService(mockHost);
it(`contains $any()`, () => {
const program = tsLS.getProgram() !;
const program = tsLS.getProgram()!;
const typeChecker = program.getTypeChecker();
const source = ts.createSourceFile('foo.ts', '', ts.ScriptTarget.ES2015);
const query = getSymbolQuery(program, typeChecker, source, () => EMPTY_SYMBOL_TABLE);

View File

@ -22,7 +22,9 @@ describe('hover', () => {
const ngLSHost = new TypeScriptServiceHost(mockHost, tsLS);
const ngLS = createLanguageService(ngLSHost);
beforeEach(() => { mockHost.reset(); });
beforeEach(() => {
mockHost.reset();
});
describe('location of hover', () => {
it('should find members in a text interpolation', () => {
@ -30,7 +32,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
});
@ -40,7 +42,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
});
@ -50,7 +52,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
});
@ -60,7 +62,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
});
@ -70,7 +72,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
});
@ -80,7 +82,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'anyValue');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) TemplateReference.anyValue: any');
});
@ -92,7 +94,7 @@ describe('hover', () => {
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeDefined();
const documentation = toText(quickInfo !.documentation);
const documentation = toText(quickInfo!.documentation);
expect(documentation).toBe('This is the title of the `TemplateReference` Component.');
});
@ -102,7 +104,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
});
@ -112,7 +114,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'hero');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(variable) hero: Hero');
});
@ -123,7 +125,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'hero');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(variable) hero: Readonly<Hero>');
});
@ -133,7 +135,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'name');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(variable) name: { readonly name: "name"; }');
});
@ -144,7 +146,7 @@ describe('hover', () => {
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'myClick');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(method) TemplateReference.myClick: (event: any) => void');
});
@ -154,7 +156,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'trackBy');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(method) NgForOf<T, U>.ngForTrackBy: TrackByFunction<T>');
});
@ -164,7 +166,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'heroes');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) TemplateReference.heroes: Hero[]');
});
@ -175,7 +177,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'date');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts))
.toBe(
@ -187,7 +189,7 @@ describe('hover', () => {
const position = content.indexOf('$any');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, position);
expect(quickInfo).toBeDefined();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual({
start: position,
length: '$any(title)'.length,
@ -202,7 +204,7 @@ describe('hover', () => {
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeDefined();
const documentation = toText(quickInfo !.documentation);
const documentation = toText(quickInfo!.documentation);
expect(documentation).toBe('This Component provides the `test-comp` selector.');
});
@ -211,7 +213,7 @@ describe('hover', () => {
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeDefined();
const {displayParts, documentation} = quickInfo !;
const {displayParts, documentation} = quickInfo!;
expect(toText(displayParts))
.toBe('(component) AppModule.TestComponent: typeof TestComponent');
expect(toText(documentation)).toBe('This Component provides the `test-comp` selector.');
@ -222,7 +224,7 @@ describe('hover', () => {
const marker = mockHost.getLocationMarkerFor(TEST_TEMPLATE, 'cursor');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeDefined();
const {displayParts, textSpan} = quickInfo !;
const {displayParts, textSpan} = quickInfo!;
expect(toText(displayParts)).toBe('(directive) AppModule.StringModel: typeof StringModel');
expect(content.substring(textSpan.start, textSpan.start + textSpan.length))
.toBe('string-model');
@ -233,7 +235,7 @@ describe('hover', () => {
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'test');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(event) TestComponent.testEvent: EventEmitter<any>');
});
@ -243,7 +245,7 @@ describe('hover', () => {
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'tcName');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) TestComponent.name: string');
});
@ -254,7 +256,7 @@ describe('hover', () => {
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'model');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) StringModel.model: string');
});
@ -264,7 +266,7 @@ describe('hover', () => {
const marker = mockHost.getDefinitionMarkerFor(TEST_TEMPLATE, 'ngFor');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(directive) NgForOf: typeof NgForOf');
});
@ -272,12 +274,12 @@ describe('hover', () => {
describe('hovering on TypeScript nodes', () => {
it('should work for component TypeScript declarations', () => {
const content = mockHost.readFile(PARSING_CASES) !;
const content = mockHost.readFile(PARSING_CASES)!;
const position = content.indexOf('TemplateReference');
expect(position).toBeGreaterThan(0);
const quickInfo = ngLS.getQuickInfoAtPosition(PARSING_CASES, position);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual({
start: position,
length: 'TemplateReference'.length,
@ -286,12 +288,12 @@ describe('hover', () => {
});
it('should work for directive TypeScript declarations', () => {
const content = mockHost.readFile(PARSING_CASES) !;
const content = mockHost.readFile(PARSING_CASES)!;
const position = content.indexOf('StringModel');
expect(position).toBeGreaterThan(0);
const quickInfo = ngLS.getQuickInfoAtPosition(PARSING_CASES, position);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual({
start: position,
length: 'StringModel'.length,
@ -313,7 +315,7 @@ describe('hover', () => {
const marker = mockHost.getReferenceMarkerFor(TEST_TEMPLATE, 'title');
const quickInfo = ngLS.getQuickInfoAtPosition(TEST_TEMPLATE, marker.start);
expect(quickInfo).toBeTruthy();
const {textSpan, displayParts} = quickInfo !;
const {textSpan, displayParts} = quickInfo!;
expect(textSpan).toEqual(marker);
expect(toText(displayParts)).toBe('(property) TemplateReference.title: string');
});

View File

@ -32,7 +32,6 @@ describe('html_info', () => {
}
}
});
});
function uniqueElements<T>(a: T[], b: T[]): T[] {

View File

@ -21,20 +21,25 @@ describe('service without angular', () => {
const fileName = '/app/test.ng';
const position = mockHost.getLocationMarkerFor(fileName, 'h1-content').start;
beforeEach(() => { mockHost.reset(); });
beforeEach(() => {
mockHost.reset();
});
it('should not crash a get diagnostics',
() => { expect(() => ngService.getSemanticDiagnostics(fileName)).not.toThrow(); });
it('should not crash a get diagnostics', () => {
expect(() => ngService.getSemanticDiagnostics(fileName)).not.toThrow();
});
it('should not crash a completion',
() => { expect(() => ngService.getCompletionsAtPosition(fileName, position)).not.toThrow(); });
it('should not crash a completion', () => {
expect(() => ngService.getCompletionsAtPosition(fileName, position)).not.toThrow();
});
it('should not crash a get definition', () => {
expect(() => ngService.getDefinitionAndBoundSpan(fileName, position)).not.toThrow();
});
it('should not crash a hover',
() => { expect(() => ngService.getQuickInfoAtPosition(fileName, position)).not.toThrow(); });
it('should not crash a hover', () => {
expect(() => ngService.getQuickInfoAtPosition(fileName, position)).not.toThrow();
});
it('should not crash with an incomplete class', () => {
mockHost.addCode('\nexport class');

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AotSummaryResolver, CompileMetadataResolver, CompilerConfig, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, ParseTreeResult, Parser, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, TemplateParser, analyzeNgModules, createOfflineCompileUrlResolver} from '@angular/compiler';
import {analyzeNgModules, AotSummaryResolver, CompileMetadataResolver, CompilerConfig, createOfflineCompileUrlResolver, DirectiveNormalizer, DirectiveResolver, DomElementSchemaRegistry, HtmlParser, I18NHtmlParser, JitSummaryResolver, Lexer, NgAnalyzedModules, NgModuleResolver, Parser, ParseTreeResult, PipeResolver, ResourceLoader, StaticReflector, StaticSymbol, StaticSymbolCache, StaticSymbolResolver, StaticSymbolResolverHost, TemplateParser} from '@angular/compiler';
import {Directory, MockAotContext} from '@angular/compiler-cli/test/mocks';
import {setup} from '@angular/compiler-cli/test/test_support';
import {ViewEncapsulation, ɵConsole as Console} from '@angular/core';
@ -45,11 +45,17 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost {
this.context = new MockAotContext(currentDirectory, files);
}
getCompilationSettings(): ts.CompilerOptions { return this.options; }
getCompilationSettings(): ts.CompilerOptions {
return this.options;
}
getScriptFileNames(): string[] { return this.scripts; }
getScriptFileNames(): string[] {
return this.scripts;
}
getScriptVersion(fileName: string): string { return '0'; }
getScriptVersion(fileName: string): string {
return '0';
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot|undefined {
const content = this.internalReadFile(fileName);
@ -58,15 +64,25 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost {
}
}
getCurrentDirectory(): string { return this.context.currentDirectory; }
getCurrentDirectory(): string {
return this.context.currentDirectory;
}
getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; }
getDefaultLibFileName(options: ts.CompilerOptions): string {
return 'lib.d.ts';
}
readFile(fileName: string): string { return this.internalReadFile(fileName) as string; }
readFile(fileName: string): string {
return this.internalReadFile(fileName) as string;
}
readResource(fileName: string): Promise<string> { return Promise.resolve(''); }
readResource(fileName: string): Promise<string> {
return Promise.resolve('');
}
assumeFileExists(fileName: string): void { this.assumedExist.add(fileName); }
assumeFileExists(fileName: string): void {
this.assumedExist.add(fileName);
}
fileExists(fileName: string): boolean {
return this.assumedExist.has(fileName) || this.internalReadFile(fileName) != null;
@ -99,10 +115,18 @@ export class MockLanguageServiceHost implements ts.LanguageServiceHost {
const staticSymbolCache = new StaticSymbolCache();
const summaryResolver = new AotSummaryResolver(
{
loadSummary(filePath: string) { return null; },
isSourceFile(sourceFilePath: string) { return true; },
toSummaryFileName(sourceFilePath: string) { return sourceFilePath; },
fromSummaryFileName(filePath: string): string{return filePath;},
loadSummary(filePath: string) {
return null;
},
isSourceFile(sourceFilePath: string) {
return true;
},
toSummaryFileName(sourceFilePath: string) {
return sourceFilePath;
},
fromSummaryFileName(filePath: string): string {
return filePath;
},
},
staticSymbolCache);
@ -117,7 +141,9 @@ export class DiagnosticContext {
public service: ts.LanguageService, public program: ts.Program,
public checker: ts.TypeChecker, public host: StaticSymbolResolverHost) {}
private collectError(e: any, path?: string) { this._errors.push({e, path}); }
private collectError(e: any, path?: string) {
this._errors.push({e, path});
}
private get staticSymbolResolver(): StaticSymbolResolver {
let result = this._staticSymbolResolver;
@ -133,7 +159,7 @@ export class DiagnosticContext {
if (!this._reflector) {
const ssr = this.staticSymbolResolver;
const result = this._reflector = new StaticReflector(
summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath !));
summaryResolver, ssr, [], [], (e, filePath) => this.collectError(e, filePath!));
this._reflector = result;
return result;
}
@ -148,11 +174,15 @@ export class DiagnosticContext {
const pipeResolver = new PipeResolver(this.reflector);
const elementSchemaRegistry = new DomElementSchemaRegistry();
const resourceLoader = new class extends ResourceLoader {
get(url: string): Promise<string> { return Promise.resolve(''); }
get(url: string): Promise<string> {
return Promise.resolve('');
}
};
const urlResolver = createOfflineCompileUrlResolver();
const htmlParser = new class extends HtmlParser {
parse(): ParseTreeResult { return new ParseTreeResult([], []); }
parse(): ParseTreeResult {
return new ParseTreeResult([], []);
}
};
// This tracks the CompileConfig in codegen.ts. Currently these options
@ -174,7 +204,11 @@ export class DiagnosticContext {
get analyzedModules(): NgAnalyzedModules {
let analyzedModules = this._analyzedModules;
if (!analyzedModules) {
const analyzeHost = {isSourceFile(filePath: string) { return true; }};
const analyzeHost = {
isSourceFile(filePath: string) {
return true;
}
};
const programFiles = this.program.getSourceFiles().map(sf => sf.fileName);
analyzedModules = this._analyzedModules =
analyzeNgModules(programFiles, analyzeHost, this.staticSymbolResolver, this.resolver);
@ -198,7 +232,7 @@ function compileTemplate(context: DiagnosticContext, type: StaticSymbol, templat
const config = new CompilerConfig();
const parser = new TemplateParser(
config, context.reflector, expressionParser, new DomElementSchemaRegistry(), htmlParser,
null !, []);
null!, []);
const htmlResult = htmlParser.parse(template, '', {tokenizeExpansionForms: true});
const analyzedModules = context.analyzedModules;
// let errors: Diagnostic[]|undefined = undefined;
@ -214,8 +248,11 @@ function compileTemplate(context: DiagnosticContext, type: StaticSymbol, templat
return {
htmlAst: htmlResult.rootNodes,
templateAst: parseResult.templateAst,
directive: metadata, directives, pipes,
parseErrors: parseResult.errors, expressionParser
directive: metadata,
directives,
pipes,
parseErrors: parseResult.errors,
expressionParser
};
}
}
@ -236,7 +273,9 @@ export function getDiagnosticTemplateInfo(
sourceFile, context.program, context.checker, compiledTemplate.pipes));
return {
fileName: templateFile,
offset: 0, query, members,
offset: 0,
query,
members,
htmlAst: compiledTemplate.htmlAst,
templateAst: compiledTemplate.templateAst,
source: sourceFile.text,
@ -246,6 +285,6 @@ export function getDiagnosticTemplateInfo(
}
}
function removeMissing<T>(values: (T | null | undefined)[]): T[] {
function removeMissing<T>(values: (T|null|undefined)[]): T[] {
return values.filter(e => !!e) as T[];
}

View File

@ -15,7 +15,6 @@ import {TypeScriptServiceHost} from '../src/typescript_host';
import {MockTypescriptHost} from './test_utils';
describe('reflector_host_spec', () => {
// Regression #21811
it('should be able to find angular under windows', () => {
const originalJoin = path.join;
@ -24,15 +23,16 @@ describe('reflector_host_spec', () => {
new MockTypescriptHost(['/app/main.ts', '/app/parsing-cases.ts'], 'node_modules', {
...path,
join: (...args: string[]) => originalJoin.apply(path, args),
posix:
{...path.posix, join: (...args: string[]) => originalPosixJoin.apply(path, args)}
posix: {...path.posix, join: (...args: string[]) => originalPosixJoin.apply(path, args)}
});
const reflectorHost = new ReflectorHost(() => undefined as any, mockHost);
if (process.platform !== 'win32') {
// If we call this in Windows it will cause a 'Maximum call stack size exceeded error'
// Because we are spying on the same function that we are call faking
spyOn(path, 'join').and.callFake((...args: string[]) => { return path.win32.join(...args); });
spyOn(path, 'join').and.callFake((...args: string[]) => {
return path.win32.join(...args);
});
}
const result = reflectorHost.moduleNameToFileName('@angular/core');

View File

@ -11,7 +11,6 @@ import {getClassDeclFromDecoratorProp} from '../src/template';
import {MockTypescriptHost} from './test_utils';
describe('getClassDeclFromTemplateNode', () => {
it('should find class declaration in syntax-only mode', () => {
const sourceFile = ts.createSourceFile(
'foo.ts', `
@ -28,24 +27,24 @@ describe('getClassDeclFromTemplateNode', () => {
}
const classDecl = sourceFile.forEachChild(visit);
expect(classDecl).toBeTruthy();
expect(classDecl !.kind).toBe(ts.SyntaxKind.ClassDeclaration);
expect((classDecl as ts.ClassDeclaration).name !.text).toBe('MyComponent');
expect(classDecl!.kind).toBe(ts.SyntaxKind.ClassDeclaration);
expect((classDecl as ts.ClassDeclaration).name!.text).toBe('MyComponent');
});
it('should return class declaration for AppComponent', () => {
const host = new MockTypescriptHost(['/app/app.component.ts']);
const tsLS = ts.createLanguageService(host);
const sourceFile = tsLS.getProgram() !.getSourceFile('/app/app.component.ts');
const sourceFile = tsLS.getProgram()!.getSourceFile('/app/app.component.ts');
expect(sourceFile).toBeTruthy();
const classDecl = sourceFile !.forEachChild(function visit(node): ts.Node | undefined {
const classDecl = sourceFile!.forEachChild(function visit(node): ts.Node|undefined {
if (ts.isPropertyAssignment(node)) {
return getClassDeclFromDecoratorProp(node);
}
return node.forEachChild(visit);
});
expect(classDecl).toBeTruthy();
expect(ts.isClassDeclaration(classDecl !)).toBe(true);
expect((classDecl as ts.ClassDeclaration).name !.text).toBe('AppComponent');
expect(ts.isClassDeclaration(classDecl!)).toBe(true);
expect((classDecl as ts.ClassDeclaration).name!.text).toBe('AppComponent');
});
});

View File

@ -57,11 +57,11 @@ function isFile(path: string) {
function loadTourOfHeroes(): ReadonlyMap<string, string> {
const {TEST_SRCDIR} = process.env;
const root =
path.join(TEST_SRCDIR !, 'angular', 'packages', 'language-service', 'test', 'project');
path.join(TEST_SRCDIR!, 'angular', 'packages', 'language-service', 'test', 'project');
const dirs = [root];
const files = new Map<string, string>();
while (dirs.length) {
const dirPath = dirs.pop() !;
const dirPath = dirs.pop()!;
for (const filePath of fs.readdirSync(dirPath)) {
const absPath = path.join(dirPath, filePath);
if (isFile(absPath)) {
@ -140,11 +140,17 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
this.projectVersion++;
}
getCompilationSettings(): ts.CompilerOptions { return {...this.options}; }
getCompilationSettings(): ts.CompilerOptions {
return {...this.options};
}
getProjectVersion(): string { return this.projectVersion.toString(); }
getProjectVersion(): string {
return this.projectVersion.toString();
}
getScriptFileNames(): string[] { return this.scriptNames; }
getScriptFileNames(): string[] {
return this.scriptNames;
}
getScriptVersion(fileName: string): string {
return (this.scriptVersion.get(fileName) || 0).toString();
@ -156,9 +162,13 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
return undefined;
}
getCurrentDirectory(): string { return '/'; }
getCurrentDirectory(): string {
return '/';
}
getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; }
getDefaultLibFileName(options: ts.CompilerOptions): string {
return 'lib.d.ts';
}
directoryExists(directoryName: string): boolean {
if (this.overrideDirectory.has(directoryName)) return true;
@ -172,7 +182,9 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
return this.pathExists(effectiveName);
}
fileExists(fileName: string): boolean { return this.getRawFileContent(fileName) != null; }
fileExists(fileName: string): boolean {
return this.getRawFileContent(fileName) != null;
}
readFile(fileName: string): string|undefined {
const content = this.getRawFileContent(fileName);
@ -242,7 +254,7 @@ export class MockTypescriptHost implements ts.LanguageServiceHost {
private pathExists(path: string): boolean {
if (this.existsCache.has(path)) {
return this.existsCache.get(path) !;
return this.existsCache.get(path)!;
}
const exists = fs.existsSync(path);
@ -411,8 +423,9 @@ function getReferenceMarkers(value: string): ReferenceResult {
let adjustment = 0;
const text = value.replace(
referenceMarker, (match: string, text: string, reference: string, _: string,
definition: string, definitionName: string, index: number): string => {
referenceMarker,
(match: string, text: string, reference: string, _: string, definition: string,
definitionName: string, index: number): string => {
const result = reference ? text : text.replace(/ᐱ/g, '');
const span: Span = {start: index - adjustment, end: index - adjustment + result.length};
const markers = reference ? references : definitions;

View File

@ -26,7 +26,7 @@ const mockProject = {
describe('plugin', () => {
const mockHost = new MockTypescriptHost(['/app/main.ts']);
const tsLS = ts.createLanguageService(mockHost);
const program = tsLS.getProgram() !;
const program = tsLS.getProgram()!;
const plugin = create({
languageService: tsLS,
languageServiceHost: mockHost,
@ -35,7 +35,9 @@ describe('plugin', () => {
config: {},
});
beforeEach(() => { mockHost.reset(); });
beforeEach(() => {
mockHost.reset();
});
it('should produce TypeScript diagnostics', () => {
const fileName = '/foo.ts';
@ -117,7 +119,7 @@ describe('plugin', () => {
const marker = mockHost.getLocationMarkerFor(MY_COMPONENT, 'tree');
const completions = plugin.getCompletionsAtPosition(MY_COMPONENT, marker.start, undefined);
expect(completions).toBeDefined();
expect(completions !.entries).toEqual([
expect(completions!.entries).toEqual([
{
name: 'children',
kind: CompletionKind.PROPERTY as any,

View File

@ -10,7 +10,7 @@ import * as ts from 'typescript';
import {TypeScriptServiceHost} from '../src/typescript_host';
import {MockTypescriptHost, findDirectiveMetadataByName} from './test_utils';
import {findDirectiveMetadataByName, MockTypescriptHost} from './test_utils';
describe('TypeScriptServiceHost', () => {
@ -62,7 +62,7 @@ describe('TypeScriptServiceHost', () => {
expect(oldModules.ngModules).toEqual([]);
// Now add a script, this would change the program
const fileName = '/app/main.ts';
const content = tsLSHost.readFile(fileName) !;
const content = tsLSHost.readFile(fileName)!;
tsLSHost.addScript(fileName, content);
// If the caches are not cleared, we would get back an empty array.
// But if the caches are cleared then the analyzed modules will be non-empty.
@ -121,8 +121,8 @@ describe('TypeScriptServiceHost', () => {
expect(oldModules.symbolsMissingModule).toEqual([]);
// Expect to find AppComponent in the old modules
const oldFile = oldModules.files.find(f => f.fileName === fileName);
expect(oldFile !.directives.length).toBe(1);
const appComp = oldFile !.directives[0];
expect(oldFile!.directives.length).toBe(1);
const appComp = oldFile!.directives[0];
expect(appComp.name).toBe('AppComponent');
expect(oldModules.ngModuleByPipeOrDirective.has(appComp)).toBe(true);
@ -154,8 +154,8 @@ describe('TypeScriptServiceHost', () => {
expect(newModules.symbolsMissingModule).toEqual([]);
// Expect to find HelloComponent in the new modules
const newFile = newModules.files.find(f => f.fileName === fileName);
expect(newFile !.directives.length).toBe(1);
const helloComp = newFile !.directives[0];
expect(newFile!.directives.length).toBe(1);
const helloComp = newFile!.directives[0];
expect(helloComp.name).toBe('HelloComponent');
expect(newModules.ngModuleByPipeOrDirective.has(helloComp)).toBe(true);
expect(newModules.ngModuleByPipeOrDirective.has(appComp)).toBe(false);

View File

@ -18,9 +18,15 @@ import {DiagnosticContext, MockLanguageServiceHost} from './mocks';
function emptyPipes(): SymbolTable {
return {
size: 0,
get(key: string) { return undefined; },
has(key: string) { return false; },
values(): Symbol[]{return [];}
get(key: string) {
return undefined;
},
has(key: string) {
return false;
},
values(): Symbol[] {
return [];
}
};
}
@ -35,9 +41,9 @@ describe('symbol query', () => {
const host = new MockLanguageServiceHost(
['/quickstart/app/app.component.ts'], QUICKSTART, '/quickstart');
const service = ts.createLanguageService(host, registry);
program = service.getProgram() !;
program = service.getProgram()!;
checker = program.getTypeChecker();
sourceFile = program.getSourceFile('/quickstart/app/app.component.ts') !;
sourceFile = program.getSourceFile('/quickstart/app/app.component.ts')!;
const symbolResolverHost = new ReflectorHost(() => program, host);
context = new DiagnosticContext(service, program, checker, symbolResolverHost);
query = getSymbolQuery(program, checker, sourceFile, emptyPipes);
@ -67,7 +73,7 @@ describe('symbol query', () => {
} else {
const symbol = query.getBuiltinType(builtinType);
const got: ts.TypeFlags = (symbol as any).tsType.flags;
expect(got).toBe(want !);
expect(got).toBe(want!);
}
}
});
@ -75,8 +81,8 @@ describe('symbol query', () => {
describe('toSymbolTableFactory(tsVersion)', () => {
it('should return a Map for versions of TypeScript >= 2.2', () => {
const a = { name: 'a' } as ts.Symbol;
const b = { name: 'b' } as ts.Symbol;
const a = {name: 'a'} as ts.Symbol;
const b = {name: 'b'} as ts.Symbol;
expect(toSymbolTableFactory([a, b]) instanceof Map).toEqual(true);
});
});

View File

@ -28,7 +28,7 @@ describe('getDirectiveClassLike', () => {
}
});
expect(result).toBeTruthy();
const {decoratorId, classId} = result !;
const {decoratorId, classId} = result!;
expect(decoratorId.kind).toBe(ts.SyntaxKind.Identifier);
expect(decoratorId.text).toBe('NgModule');
expect(classId.text).toBe('AppModule');