From 0b9b321b0c21cca7b9988490ecc80edf54591ac4 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Thu, 6 Aug 2020 09:34:46 -0700 Subject: [PATCH] Handle empty package.json files --- src/server/editorServices.ts | 15 +++++++-------- src/server/packageJsonCache.ts | 8 +++----- src/services/utilities.ts | 4 +--- .../unittests/tsserver/packageJsonInfo.ts | 17 +++++++++++++++++ 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index dc94d3b201436..a86e383d7b93a 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -3774,12 +3774,10 @@ namespace ts.server { /*@internal*/ getPackageJsonsVisibleToFile(fileName: string, rootDir?: string): readonly PackageJsonInfo[] { const packageJsonCache = this.packageJsonCache; - const watchPackageJsonFile = this.watchPackageJsonFile.bind(this); - const toPath = this.toPath.bind(this); - const rootPath = rootDir && toPath(rootDir); - const filePath = toPath(fileName); + const rootPath = rootDir && this.toPath(rootDir); + const filePath = this.toPath(fileName); const result: PackageJsonInfo[] = []; - forEachAncestorDirectory(getDirectoryPath(filePath), function processDirectory(directory): boolean | undefined { + const processDirectory = (directory: Path): boolean | undefined => { switch (packageJsonCache.directoryHasPackageJson(directory)) { // Sync and check same directory again case Ternary.Maybe: @@ -3788,15 +3786,16 @@ namespace ts.server { // Check package.json case Ternary.True: const packageJsonFileName = combinePaths(directory, "package.json"); - watchPackageJsonFile(packageJsonFileName); + this.watchPackageJsonFile(packageJsonFileName as Path); const info = packageJsonCache.getInDirectory(directory); if (info) result.push(info); } - if (rootPath && rootPath === toPath(directory)) { + if (rootPath && rootPath === this.toPath(directory)) { return true; } - }); + }; + forEachAncestorDirectory(getDirectoryPath(filePath), processDirectory); return result; } diff --git a/src/server/packageJsonCache.ts b/src/server/packageJsonCache.ts index 8c09ebda2c4d6..3aa6129430a53 100644 --- a/src/server/packageJsonCache.ts +++ b/src/server/packageJsonCache.ts @@ -42,11 +42,9 @@ namespace ts.server { }; function addOrUpdate(fileName: Path) { - const packageJsonInfo = createPackageJsonInfo(fileName, host.host); - if (packageJsonInfo !== undefined) { - packageJsons.set(fileName, packageJsonInfo); - directoriesWithoutPackageJson.delete(getDirectoryPath(fileName)); - } + const packageJsonInfo = Debug.checkDefined(createPackageJsonInfo(fileName, host.host)); + packageJsons.set(fileName, packageJsonInfo); + directoriesWithoutPackageJson.delete(getDirectoryPath(fileName)); } function directoryHasPackageJson(directory: Path) { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index c11c65d076dd6..baa70bf1df84f 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2735,9 +2735,7 @@ namespace ts { type PackageJsonRaw = Record | undefined>; const dependencyKeys = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"] as const; - const stringContent = host.readFile(fileName); - if (!stringContent) return undefined; - + const stringContent = host.readFile(fileName) || ""; const content = tryParseJson(stringContent) as PackageJsonRaw | undefined; const info: Pick = {}; if (content) { diff --git a/src/testRunner/unittests/tsserver/packageJsonInfo.ts b/src/testRunner/unittests/tsserver/packageJsonInfo.ts index efc977a335c92..b931d984c1275 100644 --- a/src/testRunner/unittests/tsserver/packageJsonInfo.ts +++ b/src/testRunner/unittests/tsserver/packageJsonInfo.ts @@ -82,6 +82,23 @@ namespace ts.projectSystem { assert.ok(packageJsonInfo2.peerDependencies); assert.ok(packageJsonInfo2.optionalDependencies); }); + + it("handles empty package.json", () => { + const packageJsonContent = ""; + const { projectService, host } = setup([tsConfig, { path: packageJson.path, content: packageJsonContent }]); + projectService.getPackageJsonsVisibleToFile("/src/whatever/blah.ts" as Path); + const packageJsonInfo = projectService.packageJsonCache.getInDirectory("/" as Path)!; + assert.isFalse(packageJsonInfo.parseable); + + host.writeFile(packageJson.path, packageJson.content); + projectService.getPackageJsonsVisibleToFile("/src/whatever/blah.ts" as Path); + const packageJsonInfo2 = projectService.packageJsonCache.getInDirectory("/" as Path)!; + assert.ok(packageJsonInfo2); + assert.ok(packageJsonInfo2.dependencies); + assert.ok(packageJsonInfo2.devDependencies); + assert.ok(packageJsonInfo2.peerDependencies); + assert.ok(packageJsonInfo2.optionalDependencies); + }); }); function setup(files: readonly File[] = [tsConfig, packageJson]) {