diff --git a/src/server/packageJsonCache.ts b/src/server/packageJsonCache.ts index d47432341fbe3..290711a821721 100644 --- a/src/server/packageJsonCache.ts +++ b/src/server/packageJsonCache.ts @@ -9,7 +9,7 @@ namespace ts.server { } export function createPackageJsonCache(project: Project): PackageJsonCache { - const packageJsons = createMap(); + const packageJsons = createMap(); const directoriesWithoutPackageJson = createMap(); return { addOrUpdate, @@ -18,7 +18,7 @@ namespace ts.server { directoriesWithoutPackageJson.set(getDirectoryPath(fileName), true); }, getInDirectory: directory => { - return packageJsons.get(combinePaths(directory, "package.json")); + return packageJsons.get(combinePaths(directory, "package.json")) || undefined; }, directoryHasPackageJson, searchDirectoryAndAncestors: directory => { @@ -39,7 +39,7 @@ namespace ts.server { function addOrUpdate(fileName: Path) { const packageJsonInfo = createPackageJsonInfo(fileName, project); - if (packageJsonInfo) { + if (packageJsonInfo !== undefined) { packageJsons.set(fileName, packageJsonInfo); directoriesWithoutPackageJson.delete(getDirectoryPath(fileName)); } diff --git a/src/server/project.ts b/src/server/project.ts index 5121556f53b6d..20f4a070dc5fa 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1474,7 +1474,8 @@ namespace ts.server { case Ternary.True: const packageJsonFileName = combinePaths(directory, "package.json"); watchPackageJsonFile(packageJsonFileName); - result.push(Debug.assertDefined(packageJsonCache.getInDirectory(directory))); + const info = packageJsonCache.getInDirectory(directory); + if (info) result.push(info); } if (rootPath && rootPath === toPath(directory)) { return true; diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 6a4b85ba901cf..55dc95d3fc2a7 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2210,7 +2210,7 @@ namespace ts { return packageJsons; } - export function createPackageJsonInfo(fileName: string, host: LanguageServiceHost): PackageJsonInfo | undefined { + export function createPackageJsonInfo(fileName: string, host: LanguageServiceHost): PackageJsonInfo | false | undefined { if (!host.readFile) { return undefined; } @@ -2221,19 +2221,18 @@ namespace ts { if (!stringContent) return undefined; const content = tryParseJson(stringContent) as PackageJsonRaw; + if (!content) return false; const info: Pick = {}; - if (content) { - for (const key of dependencyKeys) { - const dependencies = content[key]; - if (!dependencies) { - continue; - } - const dependencyMap = createMap(); - for (const packageName in dependencies) { - dependencyMap.set(packageName, dependencies[packageName]); - } - info[key] = dependencyMap; + for (const key of dependencyKeys) { + const dependencies = content[key]; + if (!dependencies) { + continue; + } + const dependencyMap = createMap(); + for (const packageName in dependencies) { + dependencyMap.set(packageName, dependencies[packageName]); } + info[key] = dependencyMap; } const dependencyGroups = [ diff --git a/src/testRunner/unittests/tsserver/packageJsonInfo.ts b/src/testRunner/unittests/tsserver/packageJsonInfo.ts index 4d2e3dd26dc28..20be7ce56e3ab 100644 --- a/src/testRunner/unittests/tsserver/packageJsonInfo.ts +++ b/src/testRunner/unittests/tsserver/packageJsonInfo.ts @@ -74,14 +74,19 @@ namespace ts.projectSystem { it("handles errors in json parsing of package.json", () => { const packageJsonContent = `{ "mod" }`; - const { project } = setup([tsConfig, { path: packageJson.path, content: packageJsonContent }]); + const { project, host } = setup([tsConfig, { path: packageJson.path, content: packageJsonContent }]); project.getPackageJsonsVisibleToFile("/src/whatever/blah.ts" as Path); const packageJsonInfo = project.packageJsonCache.getInDirectory("/" as Path)!; - assert.isObject(packageJsonInfo); - assert.isUndefined(packageJsonInfo.dependencies); - assert.isUndefined(packageJsonInfo.devDependencies); - assert.isUndefined(packageJsonInfo.peerDependencies); - assert.isUndefined(packageJsonInfo.optionalDependencies); + assert.isUndefined(packageJsonInfo); + + host.writeFile(packageJson.path, packageJson.content); + project.getPackageJsonsVisibleToFile("/src/whatever/blah.ts" as Path); + const packageJsonInfo2 = project.packageJsonCache.getInDirectory("/" as Path)!; + assert.ok(packageJsonInfo2); + assert.ok(packageJsonInfo2.dependencies); + assert.ok(packageJsonInfo2.devDependencies); + assert.ok(packageJsonInfo2.peerDependencies); + assert.ok(packageJsonInfo2.optionalDependencies); }); }); diff --git a/tests/cases/fourslash/completionsImport_filteredByInvalidPackageJson_direct.ts b/tests/cases/fourslash/completionsImport_filteredByInvalidPackageJson_direct.ts new file mode 100644 index 0000000000000..5bd121ed37f7a --- /dev/null +++ b/tests/cases/fourslash/completionsImport_filteredByInvalidPackageJson_direct.ts @@ -0,0 +1,53 @@ +/// + +//@noEmit: true + +//@Filename: /package.json +////{ +//// "mod" +//// "dependencies": { +//// "react": "*" +//// } +////} + +//@Filename: /node_modules/react/index.d.ts +////export declare var React: any; + +//@Filename: /node_modules/react/package.json +////{ +//// "name": "react", +//// "types": "./index.d.ts" +////} + +//@Filename: /node_modules/fake-react/index.d.ts +////export declare var ReactFake: any; + +//@Filename: /node_modules/fake-react/package.json +////{ +//// "name": "fake-react", +//// "types": "./index.d.ts" +////} + +//@Filename: /src/index.ts +////const x = Re/**/ + +verify.completions({ + marker: test.marker(""), + isNewIdentifierLocation: true, + includes: [{ + name: "React", + hasAction: true, + source: "/node_modules/react/index", + sortText: completion.SortText.AutoImportSuggestions + }, + { + name: "ReactFake", + hasAction: true, + source: "/node_modules/fake-react/index", + sortText: completion.SortText.AutoImportSuggestions + } + ], + preferences: { + includeCompletionsForModuleExports: true + } +}); diff --git a/tests/cases/fourslash/server/importSuggestionsCache_invalidPackageJson.ts b/tests/cases/fourslash/server/importSuggestionsCache_invalidPackageJson.ts index d18ab19f5d2a0..ad1de44aea3af 100644 --- a/tests/cases/fourslash/server/importSuggestionsCache_invalidPackageJson.ts +++ b/tests/cases/fourslash/server/importSuggestionsCache_invalidPackageJson.ts @@ -22,36 +22,16 @@ //// ////readF/**/ -verifyExcludes("readFile"); -edit.replaceLine(0, "import { promisify } from 'util';"); -verifyIncludes("readFile"); -edit.deleteLine(0); -verifyExcludes("readFile"); - -function verifyIncludes(name: string) { - goTo.marker(""); - verify.completions({ - includes: { - name, - source: "fs", - hasAction: true, - sortText: completion.SortText.AutoImportSuggestions, - }, - preferences: { - includeCompletionsForModuleExports: true, - includeInsertTextCompletions: true, - }, - }); -} - -function verifyExcludes(name: string) { - goTo.marker(""); - verify.completions({ - excludes: name, - preferences: { - includeCompletionsForModuleExports: true, - includeInsertTextCompletions: true, - }, - }); -} - +goTo.marker(""); +verify.completions({ + includes: { + name: "readFile", + source: "fs", + hasAction: true, + sortText: completion.SortText.AutoImportSuggestions, + }, + preferences: { + includeCompletionsForModuleExports: true, + includeInsertTextCompletions: true, + }, +}); \ No newline at end of file