Skip to content

Commit

Permalink
Cherry-pick PR #34906 into release-3.7 (#35006)
Browse files Browse the repository at this point in the history
Component commits:
dfa4bc0 Use empty object for invalid package json contents instead of undefined Fixes #34726

4d035ba Behave as if package json doesnt exist in case of invalid json in package json
  • Loading branch information
TypeScript Bot authored and sheetalkamat committed Nov 8, 2019
1 parent a22ad16 commit 4d5f30d
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 9 deletions.
6 changes: 3 additions & 3 deletions src/server/packageJsonCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace ts.server {
}

export function createPackageJsonCache(project: Project): PackageJsonCache {
const packageJsons = createMap<PackageJsonInfo>();
const packageJsons = createMap<PackageJsonInfo | false>();
const directoriesWithoutPackageJson = createMap<true>();
return {
addOrUpdate,
Expand All @@ -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 => {
Expand All @@ -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));
}
Expand Down
3 changes: 2 additions & 1 deletion src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1471,7 +1471,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;
Expand Down
9 changes: 4 additions & 5 deletions src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2210,19 +2210,18 @@ 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;
}

type PackageJsonRaw = Record<typeof dependencyKeys[number], Record<string, string> | undefined>;
const dependencyKeys = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"] as const;
const stringContent = host.readFile(fileName);
const content = stringContent && tryParseJson(stringContent) as PackageJsonRaw;
if (!content) {
return undefined;
}
if (!stringContent) return undefined;

const content = tryParseJson(stringContent) as PackageJsonRaw;
if (!content) return false;
const info: Pick<PackageJsonInfo, typeof dependencyKeys[number]> = {};
for (const key of dependencyKeys) {
const dependencies = content[key];
Expand Down
17 changes: 17 additions & 0 deletions src/testRunner/unittests/tsserver/packageJsonInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,23 @@ namespace ts.projectSystem {
assert.lengthOf(project.getPackageJsonsVisibleToFile("/a.ts" as Path), 1);
assert.lengthOf(project.getPackageJsonsVisibleToFile("/src/b.ts" as Path), 2);
});

it("handles errors in json parsing of package.json", () => {
const packageJsonContent = `{ "mod" }`;
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.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);
});
});

function setup(files: readonly File[] = [tsConfig, packageJson]) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/// <reference path="fourslash.ts" />

//@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
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/// <reference path="../fourslash.ts" />

// @Filename: /jsconfig.json
////{
//// "compilerOptions": {
//// "module": "commonjs",
//// },
////}

// @Filename: /node_modules/@types/node/index.d.ts
////declare module 'fs' {
//// export function readFile(): void;
////}
////declare module 'util' {
//// export function promisify(): void;
////}

// @Filename: /package.json
////{ "mod" }

// @Filename: /a.js
////
////readF/**/

goTo.marker("");
verify.completions({
includes: {
name: "readFile",
source: "fs",
hasAction: true,
sortText: completion.SortText.AutoImportSuggestions,
},
preferences: {
includeCompletionsForModuleExports: true,
includeInsertTextCompletions: true,
},
});

0 comments on commit 4d5f30d

Please sign in to comment.