Adding test for #16456 to verify watched directories in case-sensitive and non sensitive file system and fixing caching

This commit is contained in:
Sheetal Nandi 2017-08-18 17:28:38 -07:00
parent 7173da2134
commit e500be28cd
9 changed files with 259 additions and 83 deletions

View file

@ -2076,8 +2076,8 @@ namespace ts {
}
export interface FileSystemEntries {
files: ReadonlyArray<string>;
directories: ReadonlyArray<string>;
readonly files: ReadonlyArray<string>;
readonly directories: ReadonlyArray<string>;
}
export interface FileMatcherPatterns {
@ -2644,8 +2644,13 @@ namespace ts {
export interface CachedPartialSystem extends PartialSystem, CachedHost {
}
interface MutableFileSystemEntries {
readonly files: string[];
readonly directories: string[];
}
export function createCachedPartialSystem(host: HostForCaching): CachedPartialSystem {
const cachedReadDirectoryResult = createMap<FileSystemEntries>();
const cachedReadDirectoryResult = createMap<MutableFileSystemEntries>();
const getCurrentDirectory = memoize(() => host.getCurrentDirectory());
const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames);
return {
@ -2665,11 +2670,11 @@ namespace ts {
return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName);
}
function getCachedFileSystemEntries(rootDirPath: Path): FileSystemEntries | undefined {
function getCachedFileSystemEntries(rootDirPath: Path): MutableFileSystemEntries | undefined {
return cachedReadDirectoryResult.get(rootDirPath);
}
function getCachedFileSystemEntriesForBaseDir(path: Path): FileSystemEntries | undefined {
function getCachedFileSystemEntriesForBaseDir(path: Path): MutableFileSystemEntries | undefined {
return getCachedFileSystemEntries(getDirectoryPath(path));
}
@ -2678,8 +2683,8 @@ namespace ts {
}
function createCachedFileSystemEntries(rootDir: string, rootDirPath: Path) {
const resultFromHost: FileSystemEntries = {
files: host.readDirectory(rootDir, /*extensions*/ undefined, /*exclude*/ undefined, /*include*/["*.*"]) || [],
const resultFromHost: MutableFileSystemEntries = {
files: map(host.readDirectory(rootDir, /*extensions*/ undefined, /*exclude*/ undefined, /*include*/["*.*"]), getBaseNameOfFileName) || [],
directories: host.getDirectories(rootDir) || []
};
@ -2692,7 +2697,7 @@ namespace ts {
* Otherwise gets result from host and caches it.
* The host request is done under try catch block to avoid caching incorrect result
*/
function tryReadDirectory(rootDir: string, rootDirPath: Path): FileSystemEntries | undefined {
function tryReadDirectory(rootDir: string, rootDirPath: Path): MutableFileSystemEntries | undefined {
const cachedResult = getCachedFileSystemEntries(rootDirPath);
if (cachedResult) {
return cachedResult;
@ -2716,16 +2721,15 @@ namespace ts {
return some(entries, file => fileNameEqual(file, name));
}
function updateFileSystemEntry(entries: ReadonlyArray<string>, baseName: string, isValid: boolean) {
function updateFileSystemEntry(entries: string[], baseName: string, isValid: boolean) {
if (hasEntry(entries, baseName)) {
if (!isValid) {
return filter(entries, entry => !fileNameEqual(entry, baseName));
return filterMutate(entries, entry => !fileNameEqual(entry, baseName));
}
}
else if (isValid) {
return entries.concat(baseName);
return entries.push(baseName);
}
return entries;
}
function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void {
@ -2754,7 +2758,7 @@ namespace ts {
const result = getCachedFileSystemEntriesForBaseDir(path);
const baseFileName = getBaseNameOfFileName(dirPath);
if (result) {
result.directories = updateFileSystemEntry(result.directories, baseFileName, /*isValid*/ true);
updateFileSystemEntry(result.directories, baseFileName, /*isValid*/ true);
}
host.createDirectory(dirPath);
}
@ -2801,7 +2805,7 @@ namespace ts {
const baseName = getBaseNameOfFileName(fileOrFolder);
if (parentResult) {
updateFilesOfFileSystemEntry(parentResult, baseName, host.fileExists(fileOrFolderPath));
parentResult.directories = updateFileSystemEntry(parentResult.directories, baseName, host.directoryExists(fileOrFolderPath));
updateFileSystemEntry(parentResult.directories, baseName, host.directoryExists(fileOrFolderPath));
}
}
}
@ -2818,8 +2822,8 @@ namespace ts {
}
}
function updateFilesOfFileSystemEntry(parentResult: FileSystemEntries, baseName: string, fileExists: boolean) {
parentResult.files = updateFileSystemEntry(parentResult.files, baseName, fileExists);
function updateFilesOfFileSystemEntry(parentResult: MutableFileSystemEntries, baseName: string, fileExists: boolean) {
updateFileSystemEntry(parentResult.files, baseName, fileExists);
}
function clearCache() {

View file

@ -14,7 +14,7 @@ namespace ts {
resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[];
invalidateResolutionOfFile(filePath: Path): void;
invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocation: string): void;
invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocationPath: Path): void;
createHasInvalidatedResolution(): HasInvalidatedResolution;
@ -300,12 +300,12 @@ namespace ts {
}
function invalidateResolutionCacheOfChangedFailedLookupLocation<T extends NameResolutionWithFailedLookupLocations>(
failedLookupLocation: string,
failedLookupLocationPath: Path,
cache: Map<Map<T>>) {
cache.forEach((value, containingFile) => {
if (value) {
value.forEach(resolution => {
if (resolution && !resolution.isInvalidated && some(resolution.failedLookupLocations, location => toPath(location) === failedLookupLocation)) {
if (resolution && !resolution.isInvalidated && some(resolution.failedLookupLocations, location => toPath(location) === failedLookupLocationPath)) {
// Mark the file as needing re-evaluation of module resolution instead of using it blindly.
resolution.isInvalidated = true;
(filesWithInvalidatedResolutions || (filesWithInvalidatedResolutions = createMap<true>())).set(containingFile, true);
@ -320,9 +320,9 @@ namespace ts {
invalidateResolutionCacheOfDeletedFile(filePath, resolvedTypeReferenceDirectives, m => m.resolvedTypeReferenceDirective, r => r.resolvedFileName);
}
function invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocation: string) {
invalidateResolutionCacheOfChangedFailedLookupLocation(failedLookupLocation, resolvedModuleNames);
invalidateResolutionCacheOfChangedFailedLookupLocation(failedLookupLocation, resolvedTypeReferenceDirectives);
function invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocationPath: Path) {
invalidateResolutionCacheOfChangedFailedLookupLocation(failedLookupLocationPath, resolvedModuleNames);
invalidateResolutionCacheOfChangedFailedLookupLocation(failedLookupLocationPath, resolvedTypeReferenceDirectives);
}
}
}

View file

@ -564,7 +564,7 @@ namespace ts {
function onFailedLookupLocationChange(fileName: string, eventKind: FileWatcherEventKind, failedLookupLocation: string, failedLookupLocationPath: Path, containingFile: string, name: string) {
writeLog(`Failed lookup location : ${failedLookupLocation} changed: ${FileWatcherEventKind[eventKind]}, fileName: ${fileName} containingFile: ${containingFile}, name: ${name}`);
updateCachedSystemWithFile(fileName, failedLookupLocationPath, eventKind);
resolutionCache.invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocation);
resolutionCache.invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocationPath);
scheduleProgramUpdate();
}
@ -633,7 +633,7 @@ namespace ts {
// Reload is pending, do the reload
if (!needsReload) {
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, host);
if (!configFileSpecs.filesSpecs) {
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
}
rootFileNames = result.fileNames;

View file

@ -61,7 +61,7 @@ namespace ts {
typingsInstaller: undefined
};
const projectService = new server.ProjectService(svcOpts);
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */ true, /*containingProject*/ undefined);
const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */ true, serverHost);
const project = projectService.assignOrphanScriptInfoToInferredProject(rootScriptInfo);
project.setCompilerOptions({ module: ts.ModuleKind.AMD, noLib: true } );

View file

@ -130,7 +130,7 @@ namespace ts.tscWatch {
const host = createWatchedSystem([f1, config], { useCaseSensitiveFileNames: false });
const upperCaseConfigFilePath = combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path));
const watch = createWatchModeWithConfigFile(upperCaseConfigFilePath, host);
checkProgramActualFiles(watch(), [f1.path]);
checkProgramActualFiles(watch(), [combinePaths(getDirectoryPath(upperCaseConfigFilePath), getBaseFileName(f1.path))]);
});
it("create configured project without file list", () => {
@ -722,12 +722,13 @@ namespace ts.tscWatch {
const host = createWatchedSystem([moduleFile, file1, configFile, libFile]);
createWatchModeWithConfigFile(configFile.path, host);
const error = `error TS6053: File '${moduleFile.path}' not found.${host.newLine}`;
const error = "a/b/file1.ts(1,20): error TS2307: Cannot find module \'./moduleFile\'.\n";
checkOutputDoesNotContain(host, [error]);
const moduleFileOldPath = moduleFile.path;
const moduleFileNewPath = "/a/b/moduleFile1.ts";
moduleFile.path = moduleFileNewPath;
host.clearOutput();
host.reloadFS([moduleFile, file1, configFile, libFile]);
host.runQueuedTimeoutCallbacks();
checkOutputContains(host, [error]);

View file

@ -3998,33 +3998,206 @@ namespace ts.projectSystem {
});
describe("CachingFileSystemInformation", () => {
function getFunctionWithCalledMapForSingleArgumentCb<T>(cb: (f: string) => T) {
const calledMap = createMultiMap<true>();
type CalledMaps = {
fileExists: MultiMap<true>;
directoryExists: MultiMap<true>;
getDirectories: MultiMap<true>;
readFile: MultiMap<true>;
readDirectory: MultiMap<[ReadonlyArray<string>, ReadonlyArray<string>, ReadonlyArray<string>, number]>;
};
function createCallsTrackingHost(host: TestServerHost) {
const keys: Array<keyof CalledMaps> = ["fileExists", "directoryExists", "getDirectories", "readFile", "readDirectory"];
const calledMaps = getCallsTrackingMap();
return {
cb: (f: string) => {
calledMap.add(f, /*value*/ true);
return cb(f);
},
calledMap
verifyNoCall,
verifyCalledOnEachEntryOnce,
verifyCalledOnEachEntry,
verifyNoHostCalls,
verifyNoHostCallsExceptFileExistsOnce,
clear
};
function getCallsTrackingMap() {
const calledMaps: { [s: string]: Map<any> } = {};
for (let i = 0; i < keys.length - 1; i++) {
setCallsTrackingWithSingleArgFn(keys[i]);
}
setCallsTrackingWithFiveArgFn(keys[keys.length - 1]);
return calledMaps as CalledMaps;
function setCallsTrackingWithSingleArgFn(prop: keyof CalledMaps) {
const calledMap = createMultiMap<true>();
const cb = (<any>host)[prop].bind(host);
(<any>host)[prop] = (f: string) => {
calledMap.add(f, /*value*/ true);
return cb(f);
};
calledMaps[prop] = calledMap;
}
function setCallsTrackingWithFiveArgFn<U, V, W, X>(prop: keyof CalledMaps) {
const calledMap = createMultiMap<[U, V, W, X]>();
const cb = (<any>host)[prop].bind(host);
(<any>host)[prop] = (f: string, arg1?: U, arg2?: V, arg3?: W, arg4?: X) => {
calledMap.add(f, [arg1, arg2, arg3, arg4]);
return cb(f, arg1, arg2, arg3, arg4);
};
calledMaps[prop] = calledMap;
}
}
function verifyNoCall(callback: keyof CalledMaps) {
const calledMap = calledMaps[callback];
assert.equal(calledMap.size, 0, `${callback} shouldnt be called: ${arrayFrom(calledMap.keys())}`);
}
function verifyCalledOnEachEntry(callback: keyof CalledMaps, expectedKeys: Map<number>) {
const calledMap = calledMaps[callback];
assert.equal(calledMap.size, expectedKeys.size, `${callback}: incorrect size of map: Actual keys: ${arrayFrom(calledMap.keys())} Expected: ${arrayFrom(expectedKeys.keys())}`);
expectedKeys.forEach((called, name) => {
assert.isTrue(calledMap.has(name), `${callback} is expected to contain ${name}, actual keys: ${arrayFrom(calledMap.keys())}`);
assert.equal(calledMap.get(name).length, called, `${callback} is expected to be called ${called} times with ${name}. Actual entry: ${calledMap.get(name)}`);
});
}
function verifyCalledOnEachEntryOnce(callback: keyof CalledMaps, expectedKeys: string[]) {
return verifyCalledOnEachEntry(callback, zipToMap(expectedKeys, expectedKeys.map(() => 1)));
}
function verifyNoHostCalls() {
for (const key of keys) {
verifyNoCall(key);
}
}
function verifyNoHostCallsExceptFileExistsOnce(expectedKeys: string[]) {
verifyCalledOnEachEntryOnce("fileExists", expectedKeys);
verifyNoCall("directoryExists");
verifyNoCall("getDirectories");
verifyNoCall("readFile");
verifyNoCall("readDirectory");
}
function clear() {
for (const key of keys) {
calledMaps[key].clear();
}
}
}
function getFunctionWithCalledMapForFiveArgumentCb<T, U, V, W, X>(cb: (f: string, arg1?: U, arg2?: V, arg3?: W, arg4?: X) => T) {
const calledMap = createMultiMap<[U, V, W, X]>();
return {
cb: (f: string, arg1?: U, arg2?: V, arg3?: W, arg4?: X) => {
calledMap.add(f, [arg1, arg2, arg3, arg4]);
return cb(f, arg1, arg2, arg3, arg4);
},
calledMap
function verifyWatchDirectoriesCaseSensitivity(useCaseSensitiveFileNames: boolean) {
const frontendDir = "/Users/someuser/work/applications/frontend";
const canonicalFrontendDir = useCaseSensitiveFileNames ? frontendDir : frontendDir.toLowerCase();
const file1: FileOrFolder = {
path: `${frontendDir}/src/app/utils/Analytic.ts`,
content: "export class SomeClass { };"
};
}
const file2: FileOrFolder = {
path: `${frontendDir}/src/app/redux/configureStore.ts`,
content: "export class configureStore { }"
};
const file3: FileOrFolder = {
path: `${frontendDir}/src/app/utils/Cookie.ts`,
content: "export class Cookie { }"
};
const es2016LibFile: FileOrFolder = {
path: "/a/lib/lib.es2016.full.d.ts",
content: libFile.content
};
const tsconfigFile: FileOrFolder = {
path: `${frontendDir}/tsconfig.json`,
content: JSON.stringify({
"compilerOptions": {
"strict": true,
"strictNullChecks": true,
"target": "es2016",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"noEmitOnError": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"types": [
"node",
"jest"
],
"noUnusedLocals": true,
"outDir": "./compiled",
"typeRoots": [
"types",
"node_modules/@types"
],
"baseUrl": ".",
"paths": {
"*": [
"types/*"
]
}
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"compiled"
]
})
};
const projectFiles = [file1, file2, es2016LibFile, tsconfigFile];
const host = createServerHost(projectFiles, { useCaseSensitiveFileNames });
const projectService = createProjectService(host);
const canonicalConfigPath = useCaseSensitiveFileNames ? tsconfigFile.path : tsconfigFile.path.toLowerCase();
const { configFileName } = projectService.openClientFile(file1.path);
assert.equal(configFileName, tsconfigFile.path, `should find config`);
checkNumberOfConfiguredProjects(projectService, 1);
function checkMultiMapKeysForSingleEntry<T>(caption: string, multiMap: MultiMap<T>, expectedKeys: string[]) {
assert.equal(multiMap.size, expectedKeys.length, `${caption}: incorrect size of map: Actual keys: ${arrayFrom(multiMap.keys())} Expected: ${expectedKeys}`);
for (const name of expectedKeys) {
assert.isTrue(multiMap.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(multiMap.keys())}`);
assert.equal(multiMap.get(name).length, 1, `${caption} is expected to have just one entry for key ${name}, actual entry: ${multiMap.get(name)}`);
const project = projectService.configuredProjects.get(canonicalConfigPath);
verifyProjectAndWatchedDirectories();
const callsTrackingHost = createCallsTrackingHost(host);
// Create file cookie.ts
projectFiles.push(file3);
host.reloadFS(projectFiles);
host.runQueuedTimeoutCallbacks();
const canonicalFile3Path = useCaseSensitiveFileNames ? file3.path : file3.path.toLocaleLowerCase();
callsTrackingHost.verifyCalledOnEachEntryOnce("fileExists", [canonicalFile3Path]);
// Called for type root resolution
const directoryExistsCalled = createMap<number>();
for (let dir = frontendDir; dir !== "/"; dir = getDirectoryPath(dir)) {
directoryExistsCalled.set(`${dir}/node_modules`, 2);
}
directoryExistsCalled.set(`/node_modules`, 2);
directoryExistsCalled.set(`${frontendDir}/types`, 2);
directoryExistsCalled.set(`${frontendDir}/node_modules/@types`, 2);
directoryExistsCalled.set(canonicalFile3Path, 1);
callsTrackingHost.verifyCalledOnEachEntry("directoryExists", directoryExistsCalled);
callsTrackingHost.verifyNoCall("getDirectories");
callsTrackingHost.verifyCalledOnEachEntryOnce("readFile", [file3.path]);
callsTrackingHost.verifyNoCall("readDirectory");
checkNumberOfConfiguredProjects(projectService, 1);
assert.strictEqual(projectService.configuredProjects.get(canonicalConfigPath), project);
verifyProjectAndWatchedDirectories();
callsTrackingHost.clear();
const { configFileName: configFile2 } = projectService.openClientFile(file3.path);
assert.equal(configFile2, configFileName);
checkNumberOfConfiguredProjects(projectService, 1);
assert.strictEqual(projectService.configuredProjects.get(canonicalConfigPath), project);
verifyProjectAndWatchedDirectories();
callsTrackingHost.verifyNoHostCalls();
function verifyProjectAndWatchedDirectories() {
checkProjectActualFiles(project, map(projectFiles, f => f.path));
checkWatchedDirectories(host, [`${canonicalFrontendDir}/src`], /*recursive*/ true);
checkWatchedDirectories(host, [`${canonicalFrontendDir}/types`, `${canonicalFrontendDir}/node_modules/@types`], /*recursive*/ false);
}
}
@ -4069,7 +4242,6 @@ namespace ts.projectSystem {
]
})
};
const projectFiles = [clientFile, anotherModuleFile, moduleFile, tsconfigFile];
const host = createServerHost(projectFiles);
const session = createSession(host);
@ -4082,17 +4254,7 @@ namespace ts.projectSystem {
const project = projectService.configuredProjects.get(tsconfigFile.path);
checkProjectActualFiles(project, map(projectFiles, f => f.path));
const fileExistsCalledOn = getFunctionWithCalledMapForSingleArgumentCb<boolean>(host.fileExists.bind(host));
host.fileExists = fileExistsCalledOn.cb;
const directoryExistsCalledOn = getFunctionWithCalledMapForSingleArgumentCb<boolean>(host.directoryExists.bind(host));
host.directoryExists = directoryExistsCalledOn.cb;
const getDirectoriesCalledOn = getFunctionWithCalledMapForSingleArgumentCb<string[]>(host.getDirectories.bind(host));
host.getDirectories = getDirectoriesCalledOn.cb;
const readFileCalledOn = getFunctionWithCalledMapForSingleArgumentCb<string>(host.readFile.bind(host));
host.readFile = readFileCalledOn.cb;
const readDirectoryCalledOn = getFunctionWithCalledMapForFiveArgumentCb<string[], ReadonlyArray<string>, ReadonlyArray<string>, ReadonlyArray<string>, number>(host.readDirectory.bind(host));
host.readDirectory = readDirectoryCalledOn.cb;
const callsTrackingHost = createCallsTrackingHost(host);
// Get definitions shouldnt make host requests
const getDefinitionRequest = makeSessionRequest<protocol.FileLocationRequestArgs>(protocol.CommandTypes.Definition, {
@ -4103,23 +4265,23 @@ namespace ts.projectSystem {
});
const { response } = session.executeCommand(getDefinitionRequest);
assert.equal(response[0].file, moduleFile.path, "Should go to definition of vessel: response: " + JSON.stringify(response));
assert.equal(fileExistsCalledOn.calledMap.size, 0, `fileExists shouldnt be called`);
assert.equal(directoryExistsCalledOn.calledMap.size, 0, `directoryExists shouldnt be called`);
assert.equal(getDirectoriesCalledOn.calledMap.size, 0, `getDirectories shouldnt be called`);
assert.equal(readFileCalledOn.calledMap.size, 0, `readFile shouldnt be called`);
assert.equal(readDirectoryCalledOn.calledMap.size, 0, `readDirectory shouldnt be called`);
callsTrackingHost.verifyNoHostCalls();
// Open the file should call only file exists on module directory and use cached value for parental directory
const { configFileName: config2 } = projectService.openClientFile(moduleFile.path);
assert.equal(config2, configFileName);
checkMultiMapKeysForSingleEntry("fileExists", fileExistsCalledOn.calledMap, ["/a/b/models/tsconfig.json", "/a/b/models/jsconfig.json"]);
assert.equal(directoryExistsCalledOn.calledMap.size, 0, `directoryExists shouldnt be called`);
assert.equal(getDirectoriesCalledOn.calledMap.size, 0, `getDirectories shouldnt be called`);
assert.equal(readFileCalledOn.calledMap.size, 0, `readFile shouldnt be called`);
assert.equal(readDirectoryCalledOn.calledMap.size, 0, `readDirectory shouldnt be called`);
callsTrackingHost.verifyNoHostCallsExceptFileExistsOnce(["/a/b/models/tsconfig.json", "/a/b/models/jsconfig.json"]);
checkNumberOfConfiguredProjects(projectService, 1);
assert.strictEqual(projectService.configuredProjects.get(tsconfigFile.path), project);
});
it("WatchDirectories for config file With non case sensitive file system", () => {
verifyWatchDirectoriesCaseSensitivity(/*useCaseSensitiveFileNames*/ false);
});
it("WatchDirectories for config file With case sensitive file system", () => {
verifyWatchDirectoriesCaseSensitivity(/*useCaseSensitiveFileNames*/ true);
});
});
}

View file

@ -197,9 +197,12 @@ namespace ts.TestFSWithWatch {
this.reloadFS(fileOrFolderList);
}
toNormalizedAbsolutePath(s: string) {
return getNormalizedAbsolutePath(s, this.currentDirectory);
}
toFullPath(s: string) {
const fullPath = getNormalizedAbsolutePath(s, this.currentDirectory);
return this.toPath(fullPath);
return this.toPath(this.toNormalizedAbsolutePath(s));
}
reloadFS(fileOrFolderList: FileOrFolder[]) {
@ -413,7 +416,7 @@ namespace ts.TestFSWithWatch {
}
readDirectory(path: string, extensions?: ReadonlyArray<string>, exclude?: ReadonlyArray<string>, include?: ReadonlyArray<string>, depth?: number): string[] {
return ts.matchFiles(this.toFullPath(path), extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => {
return ts.matchFiles(this.toNormalizedAbsolutePath(path), extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => {
const directories: string[] = [];
const files: string[] = [];
const dirEntry = this.fs.get(this.toPath(dir));

View file

@ -1429,7 +1429,7 @@ namespace ts.server {
else {
const scriptKind = propertyReader.getScriptKind(f);
const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions);
scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ clientFileName === newRootFile, /*fileContent*/ undefined, scriptKind, hasMixedContent);
scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ clientFileName === newRootFile, /*fileContent*/ undefined, scriptKind, hasMixedContent, project.lsHost.host);
path = scriptInfo.path;
// If this script info is not already a root add it
if (!project.isRoot(scriptInfo)) {
@ -1583,9 +1583,12 @@ namespace ts.server {
* @param uncheckedFileName is absolute pathname
* @param fileContent is a known version of the file content that is more up to date than the one on disk
*/
getOrCreateScriptInfo(uncheckedFileName: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind) {
return this.getOrCreateScriptInfoForNormalizedPath(toNormalizedPath(uncheckedFileName), openedByClient, fileContent, scriptKind);
/*@internal*/
getOrCreateScriptInfo(uncheckedFileName: string, openedByClient: boolean, hostToQueryFileExistsOn: ServerHost) {
return this.getOrCreateScriptInfoForNormalizedPath(
toNormalizedPath(uncheckedFileName), openedByClient, /*fileContent*/ undefined,
/*scriptKind*/ undefined, /*hasMixedContent*/ undefined, hostToQueryFileExistsOn
);
}
getScriptInfo(uncheckedFileName: string) {
@ -1610,11 +1613,11 @@ namespace ts.server {
}
}
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean) {
getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: ServerHost) {
const path = normalizedPathToPath(fileName, this.currentDirectory, this.toCanonicalFileName);
let info = this.getScriptInfoForPath(path);
if (!info) {
if (openedByClient || this.host.fileExists(fileName)) {
if (openedByClient || (hostToQueryFileExistsOn || this.host).fileExists(fileName)) {
info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, path);
this.filenameToScriptInfo.set(info.path, info);

View file

@ -240,7 +240,7 @@ namespace ts.server {
if (this.projectKind === ProjectKind.Configured) {
(this.lsHost.host as CachedServerHost).addOrDeleteFile(fileName, failedLookupLocationPath, eventKind);
}
this.resolutionCache.invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocation);
this.resolutionCache.invalidateResolutionOfChangedFailedLookupLocation(failedLookupLocationPath);
this.markAsDirty();
this.projectService.delayUpdateProjectGraphAndInferredProjectsRefresh(this);
});
@ -701,7 +701,7 @@ namespace ts.server {
// by the LSHost for files in the program when the program is retrieved above but
// the program doesn't contain external files so this must be done explicitly.
inserted => {
const scriptInfo = this.projectService.getOrCreateScriptInfo(inserted, /*openedByClient*/ false);
const scriptInfo = this.projectService.getOrCreateScriptInfo(inserted, /*openedByClient*/ false, this.lsHost.host);
scriptInfo.attachToProject(this);
},
removed => {
@ -744,7 +744,7 @@ namespace ts.server {
}
getScriptInfoLSHost(fileName: string) {
const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false);
const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false, this.lsHost.host);
if (scriptInfo) {
const existingValue = this.rootFilesMap.get(scriptInfo.path);
if (existingValue !== undefined && existingValue !== scriptInfo) {
@ -758,7 +758,10 @@ namespace ts.server {
}
getScriptInfoForNormalizedPath(fileName: NormalizedPath) {
const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ false);
const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath(
fileName, /*openedByClient*/ false, /*fileContent*/ undefined,
/*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.lsHost.host
);
if (scriptInfo && !scriptInfo.isAttached(this)) {
return Errors.ThrowProjectDoesNotContainDocument(fileName, this);
}