diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 1b2aa481b26..66ebdcfaa14 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -1192,6 +1192,24 @@ namespace ts.projectSystem { checkNumberOfInferredProjects(projectService, 0); }); + it("external project for dynamic file", () => { + const externalProjectName = "^ScriptDocument1 file1.ts"; + const externalFiles = toExternalFiles(["^ScriptDocument1 file1.ts"]); + const host = createServerHost([]); + const projectService = createProjectService(host); + projectService.openExternalProject({ + rootFiles: externalFiles, + options: {}, + projectFileName: externalProjectName + }); + + checkNumberOfExternalProjects(projectService, 1); + checkNumberOfInferredProjects(projectService, 0); + + externalFiles[0].content = "let x =1;"; + projectService.applyChangesInOpenFiles(externalFiles, [], []); + }); + it("external project that included config files", () => { const file1 = { path: "/a/b/f1.ts", @@ -6390,4 +6408,76 @@ namespace ts.projectSystem { verifyWatchedDirectories(/*useProjectAtRoot*/ false); }); }); + + describe("tsserverProjectSystem typingsInstaller on inferred Project", () => { + it("when projectRootPath is provided", () => { + const projects = "/users/username/projects"; + const projectRootPath = `${projects}/san2`; + const file: FileOrFolder = { + path: `${projectRootPath}/x.js`, + content: "const aaaaaaav = 1;" + }; + + const currentDirectory = `${projects}/anotherProject`; + const packageJsonInCurrentDirectory: FileOrFolder = { + path: `${currentDirectory}/package.json`, + content: JSON.stringify({ + devDependencies: { + pkgcurrentdirectory: "" + }, + }) + }; + const packageJsonOfPkgcurrentdirectory: FileOrFolder = { + path: `${currentDirectory}/node_modules/pkgcurrentdirectory/package.json`, + content: JSON.stringify({ + name: "pkgcurrentdirectory", + main: "index.js", + typings: "index.d.ts" + }) + }; + const indexOfPkgcurrentdirectory: FileOrFolder = { + path: `${currentDirectory}/node_modules/pkgcurrentdirectory/index.d.ts`, + content: "export function foo() { }" + }; + + const typingsCache = `/users/username/Library/Caches/typescript/2.7`; + const typingsCachePackageJson: FileOrFolder = { + path: `${typingsCache}/package.json`, + content: JSON.stringify({ + devDependencies: { + }, + }) + }; + + const files = [file, packageJsonInCurrentDirectory, packageJsonOfPkgcurrentdirectory, indexOfPkgcurrentdirectory, typingsCachePackageJson]; + const host = createServerHost(files, { currentDirectory }); + + const typesRegistry = createMap(); + typesRegistry.set("pkgcurrentdirectory", void 0); + const typingsInstaller = new TestTypingsInstaller(typingsCache, /*throttleLimit*/ 5, host, typesRegistry); + + const projectService = createProjectService(host, { typingsInstaller }); + + projectService.setCompilerOptionsForInferredProjects({ + module: ModuleKind.CommonJS, + target: ScriptTarget.ES2016, + jsx: JsxEmit.Preserve, + experimentalDecorators: true, + allowJs: true, + allowSyntheticDefaultImports: true, + allowNonTsExtensions: true + }); + + projectService.openClientFile(file.path, file.content, ScriptKind.JS, projectRootPath); + + const project = projectService.inferredProjects[0]; + assert.isDefined(project); + + // Ensure that we use result from types cache when getting ls + assert.isDefined(project.getLanguageService()); + + // Verify that the pkgcurrentdirectory from the current directory isnt picked up + checkProjectActualFiles(project, [file.path]); + }); + }); } diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 158e460780c..4db75c48386 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -539,7 +539,7 @@ namespace ts.TestFSWithWatch { } readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { - return ts.matchFiles(this.toNormalizedAbsolutePath(path), extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => { + return ts.matchFiles(path, extensions, exclude, include, this.useCaseSensitiveFileNames, this.getCurrentDirectory(), depth, (dir) => { const directories: string[] = []; const files: string[] = []; const dirEntry = this.fs.get(this.toPath(dir)); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index b10ca156de1..e57becc76b9 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1748,9 +1748,10 @@ namespace ts.server { const path = normalizedPathToPath(fileName, currentDirectory, this.toCanonicalFileName); let info = this.getScriptInfoForPath(path); if (!info) { - Debug.assert(isRootedDiskPath(fileName) || openedByClient, "Script info with relative file name can only be open script info"); - Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "Open script files with non rooted disk path opened with current directory context cannot have same canonical names"); const isDynamic = isDynamicFileName(fileName); + Debug.assert(isRootedDiskPath(fileName) || isDynamic || openedByClient, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nScript info with non-dynamic relative file name can only be open script info`); + Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nOpen script files with non rooted disk path opened with current directory context cannot have same canonical names`); + Debug.assert(!isDynamic || this.currentDirectory === currentDirectory, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nDynamic files must always have current directory context since containing external project name will always match the script info name.`); // If the file is not opened by client and the file doesnot exist on the disk, return if (!openedByClient && !isDynamic && !(hostToQueryFileExistsOn || this.host).fileExists(fileName)) { return; diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 69399b672b3..55490fc112d 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -33,19 +33,6 @@ namespace ts.server { export type Types = Err | Info | Perf; } - function getProjectRootPath(project: Project): Path { - switch (project.projectKind) { - case ProjectKind.Configured: - return getDirectoryPath(project.getProjectName()); - case ProjectKind.Inferred: - // TODO: fixme - return ""; - case ProjectKind.External: - const projectName = normalizeSlashes(project.getProjectName()); - return project.projectService.host.fileExists(projectName) ? getDirectoryPath(projectName) : projectName; - } - } - export function createInstallTypingsRequest(project: Project, typeAcquisition: TypeAcquisition, unresolvedImports: SortedReadonlyArray, cachePath?: string): DiscoverTypings { return { projectName: project.getProjectName(), @@ -53,7 +40,7 @@ namespace ts.server { compilerOptions: project.getCompilationSettings(), typeAcquisition, unresolvedImports, - projectRootPath: getProjectRootPath(project), + projectRootPath: project.getCurrentDirectory() as Path, cachePath, kind: "discover" };