Merge branch 'master' into fixFindReferencesOnExports

This commit is contained in:
Mohamed Hegazy 2016-01-07 13:31:22 -08:00
commit cefb741d9d
8 changed files with 373 additions and 325 deletions

View file

@ -53,13 +53,13 @@ namespace ts {
if (getRootLength(moduleName) !== 0 || nameStartsWithDotSlashOrDotDotSlash(moduleName)) {
const failedLookupLocations: string[] = [];
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
let resolvedFileName = loadNodeModuleFromFile(supportedExtensions, candidate, failedLookupLocations, host);
let resolvedFileName = loadNodeModuleFromFile(supportedExtensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, host);
if (resolvedFileName) {
return { resolvedModule: { resolvedFileName }, failedLookupLocations };
}
resolvedFileName = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, host);
resolvedFileName = loadNodeModuleFromDirectory(supportedExtensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, host);
return resolvedFileName
? { resolvedModule: { resolvedFileName }, failedLookupLocations }
: { resolvedModule: undefined, failedLookupLocations };
@ -69,12 +69,22 @@ namespace ts {
}
}
function loadNodeModuleFromFile(extensions: string[], candidate: string, failedLookupLocation: string[], host: ModuleResolutionHost): string {
/* @internal */
export function directoryProbablyExists(directoryName: string, host: { directoryExists?: (directoryName: string) => boolean } ): boolean {
// if host does not support 'directoryExists' assume that directory will exist
return !host.directoryExists || host.directoryExists(directoryName);
}
/**
* @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
*/
function loadNodeModuleFromFile(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, host: ModuleResolutionHost): string {
return forEach(extensions, tryLoad);
function tryLoad(ext: string): string {
const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext;
if (host.fileExists(fileName)) {
if (!onlyRecordFailures && host.fileExists(fileName)) {
return fileName;
}
else {
@ -84,9 +94,10 @@ namespace ts {
}
}
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], host: ModuleResolutionHost): string {
function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, host: ModuleResolutionHost): string {
const packageJsonPath = combinePaths(candidate, "package.json");
if (host.fileExists(packageJsonPath)) {
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, host);
if (directoryExists && host.fileExists(packageJsonPath)) {
let jsonContent: { typings?: string };
@ -100,7 +111,8 @@ namespace ts {
}
if (typeof jsonContent.typings === "string") {
const result = loadNodeModuleFromFile(extensions, normalizePath(combinePaths(candidate, jsonContent.typings)), failedLookupLocation, host);
const path = normalizePath(combinePaths(candidate, jsonContent.typings));
const result = loadNodeModuleFromFile(extensions, path, failedLookupLocation, !directoryProbablyExists(getDirectoryPath(path), host), host);
if (result) {
return result;
}
@ -111,7 +123,7 @@ namespace ts {
failedLookupLocation.push(packageJsonPath);
}
return loadNodeModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocation, host);
return loadNodeModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocation, !directoryExists, host);
}
function loadModuleFromNodeModules(moduleName: string, directory: string, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
@ -121,14 +133,15 @@ namespace ts {
const baseName = getBaseFileName(directory);
if (baseName !== "node_modules") {
const nodeModulesFolder = combinePaths(directory, "node_modules");
const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, host);
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
// Load only typescript files irrespective of allowJs option if loading from node modules
let result = loadNodeModuleFromFile(supportedTypeScriptExtensions, candidate, failedLookupLocations, host);
let result = loadNodeModuleFromFile(supportedTypeScriptExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, host);
if (result) {
return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations };
}
result = loadNodeModuleFromDirectory(supportedTypeScriptExtensions, candidate, failedLookupLocations, host);
result = loadNodeModuleFromDirectory(supportedTypeScriptExtensions, candidate, failedLookupLocations, !nodeModulesFolderExists, host);
if (result) {
return { resolvedModule: { resolvedFileName: result, isExternalLibraryImport: true }, failedLookupLocations };
}
@ -281,7 +294,8 @@ namespace ts {
getCanonicalFileName,
getNewLine: () => newLine,
fileExists: fileName => sys.fileExists(fileName),
readFile: fileName => sys.readFile(fileName)
readFile: fileName => sys.readFile(fileName),
directoryExists: directoryName => sys.directoryExists(directoryName)
};
}

View file

@ -2650,6 +2650,8 @@ namespace ts {
// readFile function is used to read arbitrary text files on disk, i.e. when resolution procedure needs the content of 'package.json'
// to determine location of bundled typings for node module
readFile(fileName: string): string;
directoryExists?(directoryName: string): boolean;
}
export interface ResolvedModule {

View file

@ -267,6 +267,10 @@ namespace Harness.LanguageService {
log(s: string): void { this.nativeHost.log(s); }
trace(s: string): void { this.nativeHost.trace(s); }
error(s: string): void { this.nativeHost.error(s); }
directoryExists(directoryName: string): boolean {
// for tests pessimistically assume that directory always exists
return true;
}
}
class ClassifierShimProxy implements ts.Classifier {

View file

@ -321,6 +321,7 @@ interface AudioContext extends EventTarget {
destination: AudioDestinationNode;
listener: AudioListener;
sampleRate: number;
state: string;
createAnalyser(): AnalyserNode;
createBiquadFilter(): BiquadFilterNode;
createBuffer(numberOfChannels: number, length: number, sampleRate: number): AudioBuffer;
@ -2774,6 +2775,7 @@ interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelec
tagName: string;
id: string;
className: string;
innerHTML: string;
getAttribute(name?: string): string;
getAttributeNS(namespaceURI: string, localName: string): string;
getAttributeNode(name: string): Attr;
@ -2969,7 +2971,7 @@ interface Element extends Node, GlobalEventHandlers, ElementTraversal, NodeSelec
removeAttributeNode(oldAttr: Attr): Attr;
requestFullscreen(): void;
requestPointerLock(): void;
setAttribute(name?: string, value?: string): void;
setAttribute(name: string, value: string): void;
setAttributeNS(namespaceURI: string, qualifiedName: string, value: string): void;
setAttributeNode(newAttr: Attr): Attr;
setAttributeNodeNS(newAttr: Attr): Attr;
@ -5512,7 +5514,7 @@ interface HTMLMediaElement extends HTMLElement {
* Gets or sets the current playback position, in seconds.
*/
preload: string;
readyState: any;
readyState: number;
/**
* Returns a TimeRanges object that represents the ranges of the current media resource that can be seeked.
*/
@ -6169,6 +6171,7 @@ interface HTMLSelectElement extends HTMLElement {
* Returns whether an element will successfully validate based on forms validation rules and constraints.
*/
willValidate: boolean;
selectedOptions: HTMLCollection;
/**
* Adds an element to the areas, controlRange, or options collection.
* @param element Variant of type Number that specifies the index position in the collection where the element is placed. If no value is given, the method places the element at the end of the collection.

View file

@ -100,7 +100,8 @@ namespace ts.server {
this.filenameToScript = createFileMap<ScriptInfo>();
this.moduleResolutionHost = {
fileExists: fileName => this.fileExists(fileName),
readFile: fileName => this.host.readFile(fileName)
readFile: fileName => this.host.readFile(fileName),
directoryExists: directoryName => this.host.directoryExists(directoryName)
};
}

View file

@ -1034,6 +1034,7 @@ namespace ts {
* host specific questions using 'getScriptSnapshot'.
*/
resolveModuleNames?(moduleNames: string[], containingFile: string): ResolvedModule[];
directoryExists?(directoryName: string): boolean;
}
//
@ -1911,7 +1912,8 @@ namespace ts {
getCurrentDirectory: () => "",
getNewLine: () => newLine,
fileExists: (fileName): boolean => fileName === inputFileName,
readFile: (fileName): string => ""
readFile: (fileName): string => "",
directoryExists: directoryExists => true
};
const program = createProgram([inputFileName], options, compilerHost);
@ -2768,6 +2770,10 @@ namespace ts {
// stub missing host functionality
const entry = hostCache.getOrCreateEntry(fileName);
return entry && entry.scriptSnapshot.getText(0, entry.scriptSnapshot.getLength());
},
directoryExists: directoryName => {
Debug.assert(!host.resolveModuleNames);
return directoryProbablyExists(directoryName, host);
}
};

View file

@ -60,8 +60,9 @@ namespace ts {
getNewLine?(): string;
getProjectVersion?(): string;
useCaseSensitiveFileNames?(): boolean;
getModuleResolutionsForFile?(fileName: string): string;
directoryExists(directoryName: string): boolean;
}
/** Public interface of the the of a config service shim instance.*/
@ -247,14 +248,14 @@ namespace ts {
}
public getChangeRange(oldSnapshot: IScriptSnapshot): TextChangeRange {
var oldSnapshotShim = <ScriptSnapshotShimAdapter>oldSnapshot;
var encoded = this.scriptSnapshotShim.getChangeRange(oldSnapshotShim.scriptSnapshotShim);
const oldSnapshotShim = <ScriptSnapshotShimAdapter>oldSnapshot;
const encoded = this.scriptSnapshotShim.getChangeRange(oldSnapshotShim.scriptSnapshotShim);
// TODO: should this be '==='?
if (encoded == null) {
return null;
}
var decoded: { span: { start: number; length: number; }; newLength: number; } = JSON.parse(encoded);
const decoded: { span: { start: number; length: number; }; newLength: number; } = JSON.parse(encoded);
return createTextChangeRange(
createTextSpan(decoded.span.start, decoded.span.length), decoded.newLength);
}
@ -272,21 +273,25 @@ namespace ts {
private files: string[];
private loggingEnabled = false;
private tracingEnabled = false;
public resolveModuleNames: (moduleName: string[], containingFile: string) => ResolvedModule[];
public directoryExists: (directoryName: string) => boolean;
constructor(private shimHost: LanguageServiceShimHost) {
// if shimHost is a COM object then property check will become method call with no arguments.
// 'in' does not have this effect.
if ("getModuleResolutionsForFile" in this.shimHost) {
this.resolveModuleNames = (moduleNames: string[], containingFile: string) => {
let resolutionsInFile = <Map<string>>JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile));
const resolutionsInFile = <Map<string>>JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile));
return map(moduleNames, name => {
const result = lookUp(resolutionsInFile, name);
return result ? { resolvedFileName: result } : undefined;
});
};
}
if ("directoryExists" in this.shimHost) {
this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
}
}
public log(s: string): void {
@ -319,7 +324,7 @@ namespace ts {
}
public getCompilationSettings(): CompilerOptions {
var settingsJson = this.shimHost.getCompilationSettings();
const settingsJson = this.shimHost.getCompilationSettings();
// TODO: should this be '==='?
if (settingsJson == null || settingsJson == "") {
throw Error("LanguageServiceShimHostAdapter.getCompilationSettings: empty compilationSettings");
@ -328,17 +333,12 @@ namespace ts {
}
public getScriptFileNames(): string[] {
var encoded = this.shimHost.getScriptFileNames();
const encoded = this.shimHost.getScriptFileNames();
return this.files = JSON.parse(encoded);
}
public getScriptSnapshot(fileName: string): IScriptSnapshot {
// Shim the API changes for 1.5 release. This should be removed once
// TypeScript 1.5 has shipped.
if (this.files && this.files.indexOf(fileName) < 0) {
return undefined;
}
var scriptSnapshot = this.shimHost.getScriptSnapshot(fileName);
const scriptSnapshot = this.shimHost.getScriptSnapshot(fileName);
return scriptSnapshot && new ScriptSnapshotShimAdapter(scriptSnapshot);
}
@ -347,7 +347,7 @@ namespace ts {
}
public getLocalizedDiagnosticMessages(): any {
var diagnosticMessagesJson = this.shimHost.getLocalizedDiagnosticMessages();
const diagnosticMessagesJson = this.shimHost.getLocalizedDiagnosticMessages();
if (diagnosticMessagesJson == null || diagnosticMessagesJson == "") {
return null;
}
@ -362,7 +362,7 @@ namespace ts {
}
public getCancellationToken(): HostCancellationToken {
var hostCancellationToken = this.shimHost.getCancellationToken();
const hostCancellationToken = this.shimHost.getCancellationToken();
return new ThrottledCancellationToken(hostCancellationToken);
}
@ -371,14 +371,7 @@ namespace ts {
}
public getDefaultLibFileName(options: CompilerOptions): string {
// Wrap the API changes for 1.5 release. This try/catch
// should be removed once TypeScript 1.5 has shipped.
try {
return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
}
catch (e) {
return "";
}
return this.shimHost.getDefaultLibFileName(JSON.stringify(options));
}
}
@ -393,8 +386,8 @@ namespace ts {
}
public isCancellationRequested(): boolean {
var time = Date.now();
var duration = Math.abs(time - this.lastCancellationCheckTime);
const time = Date.now();
const duration = Math.abs(time - this.lastCancellationCheckTime);
if (duration > 10) {
// Check no more than once every 10 ms.
this.lastCancellationCheckTime = time;
@ -405,52 +398,48 @@ namespace ts {
}
}
export class CoreServicesShimHostAdapter implements ParseConfigHost {
export class CoreServicesShimHostAdapter implements ParseConfigHost, ModuleResolutionHost {
public directoryExists: (directoryName: string) => boolean;
constructor(private shimHost: CoreServicesShimHost) {
if ("directoryExists" in this.shimHost) {
this.directoryExists = directoryName => this.shimHost.directoryExists(directoryName);
}
}
public readDirectory(rootDir: string, extension: string, exclude: string[]): string[] {
// Wrap the API changes for 1.5 release. This try/catch
// should be removed once TypeScript 1.5 has shipped.
// Also consider removing the optional designation for
// the exclude param at this time.
var encoded: string;
try {
encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude));
}
catch (e) {
encoded = this.shimHost.readDirectory(rootDir, extension);
}
const encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude));
return JSON.parse(encoded);
}
public fileExists(fileName: string): boolean {
return this.shimHost.fileExists(fileName);
}
public readFile(fileName: string): string {
return this.shimHost.readFile(fileName);
}
}
function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any, logPerformance: boolean): any {
let start: number;
if (logPerformance) {
logger.log(actionDescription);
var start = Date.now();
start = Date.now();
}
var result = action();
const result = action();
if (logPerformance) {
var end = Date.now();
logger.log(actionDescription + " completed in " + (end - start) + " msec");
if (typeof (result) === "string") {
var str = <string>result;
const end = Date.now();
logger.log(`${actionDescription} completed in ${end - start} msec`);
if (typeof result === "string") {
let str = result;
if (str.length > 128) {
str = str.substring(0, 128) + "...";
}
logger.log(" result.length=" + str.length + ", result='" + JSON.stringify(str) + "'");
logger.log(` result.length=${str.length}, result='${JSON.stringify(str)}'`);
}
}
@ -459,8 +448,8 @@ namespace ts {
function forwardJSONCall(logger: Logger, actionDescription: string, action: () => any, logPerformance: boolean): string {
try {
var result = simpleForwardCall(logger, actionDescription, action, logPerformance);
return JSON.stringify({ result: result });
const result = simpleForwardCall(logger, actionDescription, action, logPerformance);
return JSON.stringify({ result });
}
catch (err) {
if (err instanceof OperationCanceledException) {
@ -481,7 +470,7 @@ namespace ts {
}
}
export function realizeDiagnostics(diagnostics: Diagnostic[], newLine: string): { message: string; start: number; length: number; category: string; code: number; } []{
export function realizeDiagnostics(diagnostics: Diagnostic[], newLine: string): { message: string; start: number; length: number; category: string; code: number; }[] {
return diagnostics.map(d => realizeDiagnostic(d, newLine));
}
@ -540,10 +529,9 @@ namespace ts {
*/
public refresh(throwOnError: boolean): void {
this.forwardJSONCall(
"refresh(" + throwOnError + ")",
() => {
return <any>null;
});
`refresh(${throwOnError})`,
() => <any>null
);
}
public cleanupSemanticCache(): void {
@ -555,63 +543,57 @@ namespace ts {
});
}
private realizeDiagnostics(diagnostics: Diagnostic[]): { message: string; start: number; length: number; category: string; }[]{
var newLine = getNewLineOrDefaultFromHost(this.host);
private realizeDiagnostics(diagnostics: Diagnostic[]): { message: string; start: number; length: number; category: string; }[] {
const newLine = getNewLineOrDefaultFromHost(this.host);
return ts.realizeDiagnostics(diagnostics, newLine);
}
public getSyntacticClassifications(fileName: string, start: number, length: number): string {
return this.forwardJSONCall(
"getSyntacticClassifications('" + fileName + "', " + start + ", " + length + ")",
() => {
var classifications = this.languageService.getSyntacticClassifications(fileName, createTextSpan(start, length));
return classifications;
});
`getSyntacticClassifications('${fileName}', ${start}, ${length})`,
() => this.languageService.getSyntacticClassifications(fileName, createTextSpan(start, length))
);
}
public getSemanticClassifications(fileName: string, start: number, length: number): string {
return this.forwardJSONCall(
"getSemanticClassifications('" + fileName + "', " + start + ", " + length + ")",
() => {
var classifications = this.languageService.getSemanticClassifications(fileName, createTextSpan(start, length));
return classifications;
});
`getSemanticClassifications('${fileName}', ${start}, ${length})`,
() => this.languageService.getSemanticClassifications(fileName, createTextSpan(start, length))
);
}
public getEncodedSyntacticClassifications(fileName: string, start: number, length: number): string {
return this.forwardJSONCall(
"getEncodedSyntacticClassifications('" + fileName + "', " + start + ", " + length + ")",
() => {
// directly serialize the spans out to a string. This is much faster to decode
// on the managed side versus a full JSON array.
return convertClassifications(this.languageService.getEncodedSyntacticClassifications(fileName, createTextSpan(start, length)));
});
`getEncodedSyntacticClassifications('${fileName}', ${start}, ${length})`,
// directly serialize the spans out to a string. This is much faster to decode
// on the managed side versus a full JSON array.
() => convertClassifications(this.languageService.getEncodedSyntacticClassifications(fileName, createTextSpan(start, length)))
);
}
public getEncodedSemanticClassifications(fileName: string, start: number, length: number): string {
return this.forwardJSONCall(
"getEncodedSemanticClassifications('" + fileName + "', " + start + ", " + length + ")",
() => {
// directly serialize the spans out to a string. This is much faster to decode
// on the managed side versus a full JSON array.
return convertClassifications(this.languageService.getEncodedSemanticClassifications(fileName, createTextSpan(start, length)));
});
`getEncodedSemanticClassifications('${fileName}', ${start}, ${length})`,
// directly serialize the spans out to a string. This is much faster to decode
// on the managed side versus a full JSON array.
() => convertClassifications(this.languageService.getEncodedSemanticClassifications(fileName, createTextSpan(start, length)))
);
}
public getSyntacticDiagnostics(fileName: string): string {
return this.forwardJSONCall(
"getSyntacticDiagnostics('" + fileName + "')",
`getSyntacticDiagnostics('${fileName}')`,
() => {
var diagnostics = this.languageService.getSyntacticDiagnostics(fileName);
const diagnostics = this.languageService.getSyntacticDiagnostics(fileName);
return this.realizeDiagnostics(diagnostics);
});
}
public getSemanticDiagnostics(fileName: string): string {
return this.forwardJSONCall(
"getSemanticDiagnostics('" + fileName + "')",
`getSemanticDiagnostics('${fileName}')`,
() => {
var diagnostics = this.languageService.getSemanticDiagnostics(fileName);
const diagnostics = this.languageService.getSemanticDiagnostics(fileName);
return this.realizeDiagnostics(diagnostics);
});
}
@ -620,7 +602,7 @@ namespace ts {
return this.forwardJSONCall(
"getCompilerOptionsDiagnostics()",
() => {
var diagnostics = this.languageService.getCompilerOptionsDiagnostics();
const diagnostics = this.languageService.getCompilerOptionsDiagnostics();
return this.realizeDiagnostics(diagnostics);
});
}
@ -633,11 +615,9 @@ namespace ts {
*/
public getQuickInfoAtPosition(fileName: string, position: number): string {
return this.forwardJSONCall(
"getQuickInfoAtPosition('" + fileName + "', " + position + ")",
() => {
var quickInfo = this.languageService.getQuickInfoAtPosition(fileName, position);
return quickInfo;
});
`getQuickInfoAtPosition('${fileName}', ${position})`,
() => this.languageService.getQuickInfoAtPosition(fileName, position)
);
}
@ -649,11 +629,9 @@ namespace ts {
*/
public getNameOrDottedNameSpan(fileName: string, startPos: number, endPos: number): string {
return this.forwardJSONCall(
"getNameOrDottedNameSpan('" + fileName + "', " + startPos + ", " + endPos + ")",
() => {
var spanInfo = this.languageService.getNameOrDottedNameSpan(fileName, startPos, endPos);
return spanInfo;
});
`getNameOrDottedNameSpan('${fileName}', ${startPos}, ${endPos})`,
() => this.languageService.getNameOrDottedNameSpan(fileName, startPos, endPos)
);
}
/**
@ -662,22 +640,18 @@ namespace ts {
*/
public getBreakpointStatementAtPosition(fileName: string, position: number): string {
return this.forwardJSONCall(
"getBreakpointStatementAtPosition('" + fileName + "', " + position + ")",
() => {
var spanInfo = this.languageService.getBreakpointStatementAtPosition(fileName, position);
return spanInfo;
});
`getBreakpointStatementAtPosition('${fileName}', ${position})`,
() => this.languageService.getBreakpointStatementAtPosition(fileName, position)
);
}
/// SIGNATUREHELP
public getSignatureHelpItems(fileName: string, position: number): string {
return this.forwardJSONCall(
"getSignatureHelpItems('" + fileName + "', " + position + ")",
() => {
var signatureInfo = this.languageService.getSignatureHelpItems(fileName, position);
return signatureInfo;
});
`getSignatureHelpItems('${fileName}', ${position})`,
() => this.languageService.getSignatureHelpItems(fileName, position)
);
}
/// GOTO DEFINITION
@ -688,10 +662,9 @@ namespace ts {
*/
public getDefinitionAtPosition(fileName: string, position: number): string {
return this.forwardJSONCall(
"getDefinitionAtPosition('" + fileName + "', " + position + ")",
() => {
return this.languageService.getDefinitionAtPosition(fileName, position);
});
`getDefinitionAtPosition('${fileName}', ${position})`,
() => this.languageService.getDefinitionAtPosition(fileName, position)
);
}
/// GOTO Type
@ -702,44 +675,39 @@ namespace ts {
*/
public getTypeDefinitionAtPosition(fileName: string, position: number): string {
return this.forwardJSONCall(
"getTypeDefinitionAtPosition('" + fileName + "', " + position + ")",
() => {
return this.languageService.getTypeDefinitionAtPosition(fileName, position);
});
`getTypeDefinitionAtPosition('${fileName}', ${position})`,
() => this.languageService.getTypeDefinitionAtPosition(fileName, position)
);
}
public getRenameInfo(fileName: string, position: number): string {
return this.forwardJSONCall(
"getRenameInfo('" + fileName + "', " + position + ")",
() => {
return this.languageService.getRenameInfo(fileName, position);
});
`getRenameInfo('${fileName}', ${position})`,
() => this.languageService.getRenameInfo(fileName, position)
);
}
public findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean): string {
return this.forwardJSONCall(
"findRenameLocations('" + fileName + "', " + position + ", " + findInStrings + ", " + findInComments + ")",
() => {
return this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments);
});
`findRenameLocations('${fileName}', ${position}, ${findInStrings}, ${findInComments})`,
() => this.languageService.findRenameLocations(fileName, position, findInStrings, findInComments)
);
}
/// GET BRACE MATCHING
public getBraceMatchingAtPosition(fileName: string, position: number): string {
return this.forwardJSONCall(
"getBraceMatchingAtPosition('" + fileName + "', " + position + ")",
() => {
var textRanges = this.languageService.getBraceMatchingAtPosition(fileName, position);
return textRanges;
});
`getBraceMatchingAtPosition('${fileName}', ${position})`,
() => this.languageService.getBraceMatchingAtPosition(fileName, position)
);
}
/// GET SMART INDENT
public getIndentationAtPosition(fileName: string, position: number, options: string /*Services.EditorOptions*/): string {
return this.forwardJSONCall(
"getIndentationAtPosition('" + fileName + "', " + position + ")",
`getIndentationAtPosition('${fileName}', ${position})`,
() => {
var localOptions: EditorOptions = JSON.parse(options);
const localOptions: EditorOptions = JSON.parse(options);
return this.languageService.getIndentationAtPosition(fileName, position, localOptions);
});
}
@ -748,35 +716,32 @@ namespace ts {
public getReferencesAtPosition(fileName: string, position: number): string {
return this.forwardJSONCall(
"getReferencesAtPosition('" + fileName + "', " + position + ")",
() => {
return this.languageService.getReferencesAtPosition(fileName, position);
});
`getReferencesAtPosition('${fileName}', ${position})`,
() => this.languageService.getReferencesAtPosition(fileName, position)
);
}
public findReferences(fileName: string, position: number): string {
return this.forwardJSONCall(
"findReferences('" + fileName + "', " + position + ")",
() => {
return this.languageService.findReferences(fileName, position);
});
`findReferences('${fileName}', ${position})`,
() => this.languageService.findReferences(fileName, position)
);
}
public getOccurrencesAtPosition(fileName: string, position: number): string {
return this.forwardJSONCall(
"getOccurrencesAtPosition('" + fileName + "', " + position + ")",
() => {
return this.languageService.getOccurrencesAtPosition(fileName, position);
});
`getOccurrencesAtPosition('${fileName}', ${position})`,
() => this.languageService.getOccurrencesAtPosition(fileName, position)
);
}
public getDocumentHighlights(fileName: string, position: number, filesToSearch: string): string {
return this.forwardJSONCall(
"getDocumentHighlights('" + fileName + "', " + position + ")",
`getDocumentHighlights('${fileName}', ${position})`,
() => {
var results = this.languageService.getDocumentHighlights(fileName, position, JSON.parse(filesToSearch));
const results = this.languageService.getDocumentHighlights(fileName, position, JSON.parse(filesToSearch));
// workaround for VS document higlighting issue - keep only items from the initial file
let normalizedName = normalizeSlashes(fileName).toLowerCase();
const normalizedName = normalizeSlashes(fileName).toLowerCase();
return filter(results, r => normalizeSlashes(r.fileName).toLowerCase() === normalizedName);
});
}
@ -790,56 +755,49 @@ namespace ts {
*/
public getCompletionsAtPosition(fileName: string, position: number) {
return this.forwardJSONCall(
"getCompletionsAtPosition('" + fileName + "', " + position + ")",
() => {
var completion = this.languageService.getCompletionsAtPosition(fileName, position);
return completion;
});
`getCompletionsAtPosition('${fileName}', ${position})`,
() => this.languageService.getCompletionsAtPosition(fileName, position)
);
}
/** Get a string based representation of a completion list entry details */
public getCompletionEntryDetails(fileName: string, position: number, entryName: string) {
return this.forwardJSONCall(
"getCompletionEntryDetails('" + fileName + "', " + position + ", " + entryName + ")",
() => {
var details = this.languageService.getCompletionEntryDetails(fileName, position, entryName);
return details;
});
`getCompletionEntryDetails('${fileName}', ${position}, '${entryName}')`,
() => this.languageService.getCompletionEntryDetails(fileName, position, entryName)
);
}
public getFormattingEditsForRange(fileName: string, start: number, end: number, options: string/*Services.FormatCodeOptions*/): string {
return this.forwardJSONCall(
"getFormattingEditsForRange('" + fileName + "', " + start + ", " + end + ")",
`getFormattingEditsForRange('${fileName}', ${start}, ${end})`,
() => {
var localOptions: ts.FormatCodeOptions = JSON.parse(options);
var edits = this.languageService.getFormattingEditsForRange(fileName, start, end, localOptions);
return edits;
const localOptions: ts.FormatCodeOptions = JSON.parse(options);
return this.languageService.getFormattingEditsForRange(fileName, start, end, localOptions);
});
}
public getFormattingEditsForDocument(fileName: string, options: string/*Services.FormatCodeOptions*/): string {
return this.forwardJSONCall(
"getFormattingEditsForDocument('" + fileName + "')",
`getFormattingEditsForDocument('${fileName}')`,
() => {
var localOptions: ts.FormatCodeOptions = JSON.parse(options);
var edits = this.languageService.getFormattingEditsForDocument(fileName, localOptions);
return edits;
const localOptions: ts.FormatCodeOptions = JSON.parse(options);
return this.languageService.getFormattingEditsForDocument(fileName, localOptions);
});
}
public getFormattingEditsAfterKeystroke(fileName: string, position: number, key: string, options: string/*Services.FormatCodeOptions*/): string {
return this.forwardJSONCall(
"getFormattingEditsAfterKeystroke('" + fileName + "', " + position + ", '" + key + "')",
`getFormattingEditsAfterKeystroke('${fileName}', ${position}, '${key}')`,
() => {
var localOptions: ts.FormatCodeOptions = JSON.parse(options);
var edits = this.languageService.getFormattingEditsAfterKeystroke(fileName, position, key, localOptions);
return edits;
const localOptions: ts.FormatCodeOptions = JSON.parse(options);
return this.languageService.getFormattingEditsAfterKeystroke(fileName, position, key, localOptions);
});
}
public getDocCommentTemplateAtPosition(fileName: string, position: number): string {
return this.forwardJSONCall(
"getDocCommentTemplateAtPosition('" + fileName + "', " + position + ")",
`getDocCommentTemplateAtPosition('${fileName}', ${position})`,
() => this.languageService.getDocCommentTemplateAtPosition(fileName, position)
);
}
@ -849,51 +807,38 @@ namespace ts {
/** Return a list of symbols that are interesting to navigate to */
public getNavigateToItems(searchValue: string, maxResultCount?: number): string {
return this.forwardJSONCall(
"getNavigateToItems('" + searchValue + "', " + maxResultCount+ ")",
() => {
var items = this.languageService.getNavigateToItems(searchValue, maxResultCount);
return items;
});
`getNavigateToItems('${searchValue}', ${maxResultCount})`,
() => this.languageService.getNavigateToItems(searchValue, maxResultCount)
);
}
public getNavigationBarItems(fileName: string): string {
return this.forwardJSONCall(
"getNavigationBarItems('" + fileName + "')",
() => {
var items = this.languageService.getNavigationBarItems(fileName);
return items;
});
`getNavigationBarItems('${fileName}')`,
() => this.languageService.getNavigationBarItems(fileName)
);
}
public getOutliningSpans(fileName: string): string {
return this.forwardJSONCall(
"getOutliningSpans('" + fileName + "')",
() => {
var items = this.languageService.getOutliningSpans(fileName);
return items;
});
`getOutliningSpans('${fileName}')`,
() => this.languageService.getOutliningSpans(fileName)
);
}
public getTodoComments(fileName: string, descriptors: string): string {
return this.forwardJSONCall(
"getTodoComments('" + fileName + "')",
() => {
var items = this.languageService.getTodoComments(fileName, JSON.parse(descriptors));
return items;
});
`getTodoComments('${fileName}')`,
() => this.languageService.getTodoComments(fileName, JSON.parse(descriptors))
);
}
/// Emit
public getEmitOutput(fileName: string): string {
return this.forwardJSONCall(
"getEmitOutput('" + fileName + "')",
() => {
var output = this.languageService.getEmitOutput(fileName);
// Shim the API changes for 1.5 release. This should be removed once
// TypeScript 1.5 has shipped.
(<any>output).emitOutputStatus = output.emitSkipped ? 1 : 0;
return output;
});
`getEmitOutput('${fileName}')`,
() => this.languageService.getEmitOutput(fileName)
);
}
}
@ -918,12 +863,11 @@ namespace ts {
/// COLORIZATION
public getClassificationsForLine(text: string, lexState: EndOfLineState, classifyKeywordsInGenerics?: boolean): string {
var classification = this.classifier.getClassificationsForLine(text, lexState, classifyKeywordsInGenerics);
var items = classification.entries;
var result = "";
for (var i = 0; i < items.length; i++) {
result += items[i].length + "\n";
result += items[i].classification + "\n";
const classification = this.classifier.getClassificationsForLine(text, lexState, classifyKeywordsInGenerics);
let result = "";
for (const item of classification.entries) {
result += item.length + "\n";
result += item.classification + "\n";
}
result += classification.finalLexState;
return result;
@ -940,16 +884,16 @@ namespace ts {
private forwardJSONCall(actionDescription: string, action: () => any): any {
return forwardJSONCall(this.logger, actionDescription, action, this.logPerformance);
}
public resolveModuleName(fileName: string, moduleName: string, compilerOptionsJson: string): string {
return this.forwardJSONCall(`resolveModuleName('${fileName}')`, () => {
let compilerOptions = <CompilerOptions>JSON.parse(compilerOptionsJson);
const compilerOptions = <CompilerOptions>JSON.parse(compilerOptionsJson);
const result = resolveModuleName(moduleName, normalizeSlashes(fileName), compilerOptions, this.host);
return {
resolvedFileName: result.resolvedModule ? result.resolvedModule.resolvedFileName: undefined,
resolvedFileName: result.resolvedModule ? result.resolvedModule.resolvedFileName : undefined,
failedLookupLocations: result.failedLookupLocations
};
});
});
}
public getPreProcessedFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string {
@ -957,8 +901,8 @@ namespace ts {
"getPreProcessedFileInfo('" + fileName + "')",
() => {
// for now treat files as JavaScript
var result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true);
var convertResult = {
const result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true);
const convertResult = {
referencedFiles: <IFileReference[]>[],
importedFiles: <IFileReference[]>[],
ambientExternalModules: result.ambientExternalModules,
@ -986,11 +930,11 @@ namespace ts {
public getTSConfigFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string {
return this.forwardJSONCall(
"getTSConfigFileInfo('" + fileName + "')",
`getTSConfigFileInfo('${fileName}')`,
() => {
let text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength());
const text = sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength());
let result = parseConfigFileTextToJson(fileName, text);
const result = parseConfigFileTextToJson(fileName, text);
if (result.error) {
return {
@ -1000,7 +944,7 @@ namespace ts {
};
}
var configFile = parseJsonConfigFileContent(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName)));
const configFile = parseJsonConfigFileContent(result.config, this.host, getDirectoryPath(normalizeSlashes(fileName)));
return {
options: configFile.options,
@ -1013,9 +957,8 @@ namespace ts {
public getDefaultCompilationSettings(): string {
return this.forwardJSONCall(
"getDefaultCompilationSettings()",
() => {
return getDefaultCompilerOptions();
});
() => getDefaultCompilerOptions()
);
}
}
@ -1035,8 +978,8 @@ namespace ts {
if (this.documentRegistry === undefined) {
this.documentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory());
}
var hostAdapter = new LanguageServiceShimHostAdapter(host);
var languageService = createLanguageService(hostAdapter, this.documentRegistry);
const hostAdapter = new LanguageServiceShimHostAdapter(host);
const languageService = createLanguageService(hostAdapter, this.documentRegistry);
return new LanguageServiceShimObject(this, host, languageService);
}
catch (err) {
@ -1057,7 +1000,7 @@ namespace ts {
public createCoreServicesShim(host: CoreServicesShimHost): CoreServicesShim {
try {
var adapter = new CoreServicesShimHostAdapter(host);
const adapter = new CoreServicesShimHostAdapter(host);
return new CoreServicesShimObject(this, <Logger>host, adapter);
}
catch (err) {
@ -1077,7 +1020,7 @@ namespace ts {
}
public unregisterShim(shim: Shim): void {
for (var i = 0, n = this._shims.length; i < n; i++) {
for (let i = 0, n = this._shims.length; i < n; i++) {
if (this._shims[i] === shim) {
delete this._shims[i];
return;

View file

@ -26,15 +26,36 @@ module ts {
content?: string
}
function createModuleResolutionHost(...files: File[]): ModuleResolutionHost {
function createModuleResolutionHost(hasDirectoryExists: boolean, ...files: File[]): ModuleResolutionHost {
let map = arrayToMap(files, f => f.name);
return { fileExists, readFile };
function fileExists(path: string): boolean {
return hasProperty(map, path);
if (hasDirectoryExists) {
const directories: Map<string> = {};
for (const f of files) {
let name = getDirectoryPath(f.name);
while (true) {
directories[name] = name;
let baseName = getDirectoryPath(name);
if (baseName === name) {
break;
}
name = baseName;
}
}
return {
readFile,
directoryExists: path => {
return hasProperty(directories, path);
},
fileExists: path => {
assert.isTrue(hasProperty(directories, getDirectoryPath(path)), "'fileExists' request in non-existing directory");
return hasProperty(map, path);
}
}
}
else {
return { readFile, fileExists: path => hasProperty(map, path), };
}
function readFile(path: string): string {
return hasProperty(map, path) ? map[path].content : undefined;
}
@ -51,9 +72,14 @@ module ts {
function testLoadAsFile(containingFileName: string, moduleFileNameNoExt: string, moduleName: string): void {
for (let ext of supportedTypeScriptExtensions) {
test(ext, /*hasDirectoryExists*/ false);
test(ext, /*hasDirectoryExists*/ true);
}
function test(ext: string, hasDirectoryExists: boolean) {
let containingFile = { name: containingFileName }
let moduleFile = { name: moduleFileNameNoExt + ext }
let resolution = nodeModuleNameResolver(moduleName, containingFile.name, {}, createModuleResolutionHost(containingFile, moduleFile));
let resolution = nodeModuleNameResolver(moduleName, containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false);
@ -69,6 +95,7 @@ module ts {
}
assert.deepEqual(resolution.failedLookupLocations, failedLookupLocations);
}
}
@ -89,14 +116,19 @@ module ts {
});
function testLoadingFromPackageJson(containingFileName: string, packageJsonFileName: string, fieldRef: string, moduleFileName: string, moduleName: string): void {
let containingFile = { name: containingFileName };
let packageJson = { name: packageJsonFileName, content: JSON.stringify({ "typings": fieldRef }) };
let moduleFile = { name: moduleFileName };
let resolution = nodeModuleNameResolver(moduleName, containingFile.name, {}, createModuleResolutionHost(containingFile, packageJson, moduleFile));
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false);
// expect three failed lookup location - attempt to load module as file with all supported extensions
assert.equal(resolution.failedLookupLocations.length, supportedTypeScriptExtensions.length);
test(/*hasDirectoryExists*/ false);
test(/*hasDirectoryExists*/ true);
function test(hasDirectoryExists: boolean) {
let containingFile = { name: containingFileName };
let packageJson = { name: packageJsonFileName, content: JSON.stringify({ "typings": fieldRef }) };
let moduleFile = { name: moduleFileName };
let resolution = nodeModuleNameResolver(moduleName, containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, packageJson, moduleFile));
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false);
// expect three failed lookup location - attempt to load module as file with all supported extensions
assert.equal(resolution.failedLookupLocations.length, supportedTypeScriptExtensions.length);
}
}
it("module name as directory - load from 'typings'", () => {
@ -107,16 +139,21 @@ module ts {
});
function testTypingsIgnored(typings: any): void {
let containingFile = { name: "/a/b.ts" };
let packageJson = { name: "/node_modules/b/package.json", content: JSON.stringify({ "typings": typings }) };
let moduleFile = { name: "/a/b.d.ts" };
test(/*hasDirectoryExists*/ false);
test(/*hasDirectoryExists*/ true);
let indexPath = "/node_modules/b/index.d.ts";
let indexFile = { name: indexPath }
function test(hasDirectoryExists: boolean) {
let containingFile = { name: "/a/b.ts" };
let packageJson = { name: "/node_modules/b/package.json", content: JSON.stringify({ "typings": typings }) };
let moduleFile = { name: "/a/b.d.ts" };
let resolution = nodeModuleNameResolver("b", containingFile.name, {}, createModuleResolutionHost(containingFile, packageJson, moduleFile, indexFile));
let indexPath = "/node_modules/b/index.d.ts";
let indexFile = { name: indexPath }
assert.equal(resolution.resolvedModule.resolvedFileName, indexPath);
let resolution = nodeModuleNameResolver("b", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, packageJson, moduleFile, indexFile));
assert.equal(resolution.resolvedModule.resolvedFileName, indexPath);
}
}
it("module name as directory - handle invalid 'typings'", () => {
@ -128,89 +165,110 @@ module ts {
});
it("module name as directory - load index.d.ts", () => {
let containingFile = { name: "/a/b/c.ts" };
let packageJson = { name: "/a/b/foo/package.json", content: JSON.stringify({ main: "/c/d" }) };
let indexFile = { name: "/a/b/foo/index.d.ts" };
let resolution = nodeModuleNameResolver("./foo", containingFile.name, {}, createModuleResolutionHost(containingFile, packageJson, indexFile));
assert.equal(resolution.resolvedModule.resolvedFileName, indexFile.name);
assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false);
assert.deepEqual(resolution.failedLookupLocations, [
"/a/b/foo.ts",
"/a/b/foo.tsx",
"/a/b/foo.d.ts",
"/a/b/foo/index.ts",
"/a/b/foo/index.tsx",
]);
test(/*hasDirectoryExists*/ false);
test(/*hasDirectoryExists*/ true);
function test(hasDirectoryExists: boolean) {
let containingFile = { name: "/a/b/c.ts" };
let packageJson = { name: "/a/b/foo/package.json", content: JSON.stringify({ main: "/c/d" }) };
let indexFile = { name: "/a/b/foo/index.d.ts" };
let resolution = nodeModuleNameResolver("./foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, packageJson, indexFile));
assert.equal(resolution.resolvedModule.resolvedFileName, indexFile.name);
assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false);
assert.deepEqual(resolution.failedLookupLocations, [
"/a/b/foo.ts",
"/a/b/foo.tsx",
"/a/b/foo.d.ts",
"/a/b/foo/index.ts",
"/a/b/foo/index.tsx",
]);
}
});
});
describe("Node module resolution - non-relative paths", () => {
it("load module as file - ts files not loaded", () => {
let containingFile = { name: "/a/b/c/d/e.ts" };
let moduleFile = { name: "/a/b/node_modules/foo.ts" };
let resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(containingFile, moduleFile));
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
assert.deepEqual(resolution.failedLookupLocations, [
"/a/b/c/d/node_modules/foo.ts",
"/a/b/c/d/node_modules/foo.tsx",
"/a/b/c/d/node_modules/foo.d.ts",
"/a/b/c/d/node_modules/foo/package.json",
"/a/b/c/d/node_modules/foo/index.ts",
"/a/b/c/d/node_modules/foo/index.tsx",
"/a/b/c/d/node_modules/foo/index.d.ts",
"/a/b/c/node_modules/foo.ts",
"/a/b/c/node_modules/foo.tsx",
"/a/b/c/node_modules/foo.d.ts",
"/a/b/c/node_modules/foo/package.json",
"/a/b/c/node_modules/foo/index.ts",
"/a/b/c/node_modules/foo/index.tsx",
"/a/b/c/node_modules/foo/index.d.ts",
])
test(/*hasDirectoryExists*/ false);
test(/*hasDirectoryExists*/ true);
function test(hasDirectoryExists: boolean) {
let containingFile = { name: "/a/b/c/d/e.ts" };
let moduleFile = { name: "/a/b/node_modules/foo.ts" };
let resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
assert.deepEqual(resolution.failedLookupLocations, [
"/a/b/c/d/node_modules/foo.ts",
"/a/b/c/d/node_modules/foo.tsx",
"/a/b/c/d/node_modules/foo.d.ts",
"/a/b/c/d/node_modules/foo/package.json",
"/a/b/c/d/node_modules/foo/index.ts",
"/a/b/c/d/node_modules/foo/index.tsx",
"/a/b/c/d/node_modules/foo/index.d.ts",
"/a/b/c/node_modules/foo.ts",
"/a/b/c/node_modules/foo.tsx",
"/a/b/c/node_modules/foo.d.ts",
"/a/b/c/node_modules/foo/package.json",
"/a/b/c/node_modules/foo/index.ts",
"/a/b/c/node_modules/foo/index.tsx",
"/a/b/c/node_modules/foo/index.d.ts",
])
}
});
it("load module as file", () => {
let containingFile = { name: "/a/b/c/d/e.ts" };
let moduleFile = { name: "/a/b/node_modules/foo.d.ts" };
let resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(containingFile, moduleFile));
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
assert.equal(resolution.resolvedModule.isExternalLibraryImport, true);
test(/*hasDirectoryExists*/ false);
test(/*hasDirectoryExists*/ true);
function test(hasDirectoryExists: boolean) {
let containingFile = { name: "/a/b/c/d/e.ts" };
let moduleFile = { name: "/a/b/node_modules/foo.d.ts" };
let resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
assert.equal(resolution.resolvedModule.isExternalLibraryImport, true);
}
});
it("load module as directory", () => {
let containingFile = { name: "/a/node_modules/b/c/node_modules/d/e.ts" };
let moduleFile = { name: "/a/node_modules/foo/index.d.ts" };
let resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(containingFile, moduleFile));
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
assert.equal(resolution.resolvedModule.isExternalLibraryImport, true);
assert.deepEqual(resolution.failedLookupLocations, [
"/a/node_modules/b/c/node_modules/d/node_modules/foo.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/foo.tsx",
"/a/node_modules/b/c/node_modules/d/node_modules/foo.d.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/package.json",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.tsx",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.d.ts",
"/a/node_modules/b/c/node_modules/foo.ts",
"/a/node_modules/b/c/node_modules/foo.tsx",
"/a/node_modules/b/c/node_modules/foo.d.ts",
"/a/node_modules/b/c/node_modules/foo/package.json",
"/a/node_modules/b/c/node_modules/foo/index.ts",
"/a/node_modules/b/c/node_modules/foo/index.tsx",
"/a/node_modules/b/c/node_modules/foo/index.d.ts",
"/a/node_modules/b/node_modules/foo.ts",
"/a/node_modules/b/node_modules/foo.tsx",
"/a/node_modules/b/node_modules/foo.d.ts",
"/a/node_modules/b/node_modules/foo/package.json",
"/a/node_modules/b/node_modules/foo/index.ts",
"/a/node_modules/b/node_modules/foo/index.tsx",
"/a/node_modules/b/node_modules/foo/index.d.ts",
"/a/node_modules/foo.ts",
"/a/node_modules/foo.tsx",
"/a/node_modules/foo.d.ts",
"/a/node_modules/foo/package.json",
"/a/node_modules/foo/index.ts",
"/a/node_modules/foo/index.tsx"
]);
test(/*hasDirectoryExists*/ false);
test(/*hasDirectoryExists*/ true);
function test(hasDirectoryExists: boolean) {
let containingFile = { name: "/a/node_modules/b/c/node_modules/d/e.ts" };
let moduleFile = { name: "/a/node_modules/foo/index.d.ts" };
let resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
assert.equal(resolution.resolvedModule.isExternalLibraryImport, true);
assert.deepEqual(resolution.failedLookupLocations, [
"/a/node_modules/b/c/node_modules/d/node_modules/foo.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/foo.tsx",
"/a/node_modules/b/c/node_modules/d/node_modules/foo.d.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/package.json",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.ts",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.tsx",
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.d.ts",
"/a/node_modules/b/c/node_modules/foo.ts",
"/a/node_modules/b/c/node_modules/foo.tsx",
"/a/node_modules/b/c/node_modules/foo.d.ts",
"/a/node_modules/b/c/node_modules/foo/package.json",
"/a/node_modules/b/c/node_modules/foo/index.ts",
"/a/node_modules/b/c/node_modules/foo/index.tsx",
"/a/node_modules/b/c/node_modules/foo/index.d.ts",
"/a/node_modules/b/node_modules/foo.ts",
"/a/node_modules/b/node_modules/foo.tsx",
"/a/node_modules/b/node_modules/foo.d.ts",
"/a/node_modules/b/node_modules/foo/package.json",
"/a/node_modules/b/node_modules/foo/index.ts",
"/a/node_modules/b/node_modules/foo/index.tsx",
"/a/node_modules/b/node_modules/foo/index.d.ts",
"/a/node_modules/foo.ts",
"/a/node_modules/foo.tsx",
"/a/node_modules/foo.d.ts",
"/a/node_modules/foo/package.json",
"/a/node_modules/foo/index.ts",
"/a/node_modules/foo/index.tsx"
]);
}
});
});
@ -400,4 +458,21 @@ import b = require("./moduleB.ts");
test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /* useCaseSensitiveFileNames */ false, ["moduleD.ts"], []);
})
});
function notImplemented(name: string): () => any {
return () => assert(`${name} is not implemented and should not be called`);
}
describe("ModuleResolutionHost.directoryExists", () => {
it("No 'fileExists' calls if containing directory is missing", () => {
const host: ModuleResolutionHost = {
readFile: notImplemented("readFile"),
fileExists: notImplemented("fileExists"),
directoryExists: _ => false
};
const result = resolveModuleName("someName", "/a/b/c/d", { moduleResolution: ModuleResolutionKind.NodeJs }, host);
assert(!result.resolvedModule);
});
});
}