Skip to content

Use correct default project for file if that project is opened at later time #33647

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,14 @@ namespace ts.server {
return undefined;
}

/*@internal*/
findDefaultConfiguredProject(info: ScriptInfo) {
if (!info.isScriptOpen()) return undefined;
const configFileName = this.getConfigFileNameForFile(info);
return configFileName &&
this.findConfiguredProjectByProjectName(configFileName);
}

/**
* This function tries to search for a tsconfig.json for the given file.
* This is different from the method the compiler uses because
Expand Down
31 changes: 25 additions & 6 deletions src/server/scriptInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -491,21 +491,40 @@ namespace ts.server {
case 1:
return this.containingProjects[0];
default:
// if this file belongs to multiple projects, the first configured project should be
// the default project; if no configured projects, the first external project should
// be the default project; otherwise the first inferred project should be the default.
// If this file belongs to multiple projects, below is the order in which default project is used
// - for open script info, its default configured project during opening is default if info is part of it
// - first configured project of which script info is not a source of project reference redirect
// - first configured project
// - first external project
// - first inferred project
let firstExternalProject;
let firstConfiguredProject;
for (const project of this.containingProjects) {
let firstNonSourceOfProjectReferenceRedirect;
let defaultConfiguredProject: ConfiguredProject | false | undefined;
for (let index = 0; index < this.containingProjects.length; index++) {
const project = this.containingProjects[index];
if (project.projectKind === ProjectKind.Configured) {
if (!project.isSourceOfProjectReferenceRedirect(this.fileName)) return project;
if (!project.isSourceOfProjectReferenceRedirect(this.fileName)) {
// If we havent found default configuredProject and
// its not the last one, find it and use that one if there
if (defaultConfiguredProject === undefined &&
index !== this.containingProjects.length - 1) {
defaultConfiguredProject = project.projectService.findDefaultConfiguredProject(this) || false;
}
if (defaultConfiguredProject === project) return project;
if (!firstNonSourceOfProjectReferenceRedirect) firstNonSourceOfProjectReferenceRedirect = project;
}
if (!firstConfiguredProject) firstConfiguredProject = project;
}
else if (project.projectKind === ProjectKind.External && !firstExternalProject) {
firstExternalProject = project;
}
}
return firstConfiguredProject || firstExternalProject || this.containingProjects[0];
return defaultConfiguredProject ||
firstNonSourceOfProjectReferenceRedirect ||
firstConfiguredProject ||
firstExternalProject ||
this.containingProjects[0];
}
}

Expand Down
43 changes: 21 additions & 22 deletions src/testRunner/unittests/tscWatch/emitAndErrorUpdates.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
namespace ts.tscWatch {
describe("unittests:: tsc-watch:: Emit times and Error updates in builder after program changes", () => {
const currentDirectory = "/user/username/projects/myproject";
const config: File = {
path: `${currentDirectory}/tsconfig.json`,
path: `${projectRoot}/tsconfig.json`,
content: `{}`
};
function getOutputFileStampAndError(host: WatchedSystem, watch: Watch, file: File) {
Expand Down Expand Up @@ -83,7 +82,7 @@ namespace ts.tscWatch {
const nonLibFiles = [...filesWithNewEmit, ...filesWithOnlyErrorRefresh, ...filesNotTouched];
const files = [...nonLibFiles, configFile, libFile];
const compilerOptions = (JSON.parse(configFile.content).compilerOptions || {}) as CompilerOptions;
const host = createWatchedSystem(files, { currentDirectory });
const host = createWatchedSystem(files, { currentDirectory: projectRoot });
const watch = createWatchOfConfigFile("tsconfig.json", host);
checkProgramActualFiles(watch(), [...nonLibFiles.map(f => f.path), libFile.path]);
checkOutputErrorsInitial(host, getInitialErrors(watch));
Expand Down Expand Up @@ -167,7 +166,7 @@ namespace ts.tscWatch {

describe("deep import changes", () => {
const aFile: File = {
path: `${currentDirectory}/a.ts`,
path: `${projectRoot}/a.ts`,
content: `import {B} from './b';
declare var console: any;
let b = new B();
Expand Down Expand Up @@ -203,15 +202,15 @@ console.log(b.c.d);`

describe("updates errors when deep import file changes", () => {
const bFile: File = {
path: `${currentDirectory}/b.ts`,
path: `${projectRoot}/b.ts`,
content: `import {C} from './c';
export class B
{
c = new C();
}`
};
const cFile: File = {
path: `${currentDirectory}/c.ts`,
path: `${projectRoot}/c.ts`,
content: `export class C
{
d = 1;
Expand All @@ -222,15 +221,15 @@ export class B

describe("updates errors when deep import through declaration file changes", () => {
const bFile: File = {
path: `${currentDirectory}/b.d.ts`,
path: `${projectRoot}/b.d.ts`,
content: `import {C} from './c';
export class B
{
c: C;
}`
};
const cFile: File = {
path: `${currentDirectory}/c.d.ts`,
path: `${projectRoot}/c.d.ts`,
content: `export class C
{
d: number;
Expand All @@ -242,7 +241,7 @@ export class B

describe("updates errors in file not exporting a deep multilevel import that changes", () => {
const aFile: File = {
path: `${currentDirectory}/a.ts`,
path: `${projectRoot}/a.ts`,
content: `export interface Point {
name: string;
c: Coords;
Expand All @@ -253,13 +252,13 @@ export interface Coords {
}`
};
const bFile: File = {
path: `${currentDirectory}/b.ts`,
path: `${projectRoot}/b.ts`,
content: `import { Point } from "./a";
export interface PointWrapper extends Point {
}`
};
const cFile: File = {
path: `${currentDirectory}/c.ts`,
path: `${projectRoot}/c.ts`,
content: `import { PointWrapper } from "./b";
export function getPoint(): PointWrapper {
return {
Expand All @@ -272,12 +271,12 @@ export function getPoint(): PointWrapper {
};`
};
const dFile: File = {
path: `${currentDirectory}/d.ts`,
path: `${projectRoot}/d.ts`,
content: `import { getPoint } from "./c";
getPoint().c.x;`
};
const eFile: File = {
path: `${currentDirectory}/e.ts`,
path: `${projectRoot}/e.ts`,
content: `import "./d";`
};
verifyEmitAndErrorUpdates({
Expand All @@ -301,14 +300,14 @@ getPoint().c.x;`

describe("updates errors when file transitively exported file changes", () => {
const config: File = {
path: `${currentDirectory}/tsconfig.json`,
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({
files: ["app.ts"],
compilerOptions: { baseUrl: "." }
})
};
const app: File = {
path: `${currentDirectory}/app.ts`,
path: `${projectRoot}/app.ts`,
content: `import { Data } from "lib2/public";
export class App {
public constructor() {
Expand All @@ -317,11 +316,11 @@ export class App {
}`
};
const lib2Public: File = {
path: `${currentDirectory}/lib2/public.ts`,
path: `${projectRoot}/lib2/public.ts`,
content: `export * from "./data";`
};
const lib2Data: File = {
path: `${currentDirectory}/lib2/data.ts`,
path: `${projectRoot}/lib2/data.ts`,
content: `import { ITest } from "lib1/public";
export class Data {
public test() {
Expand All @@ -333,15 +332,15 @@ export class Data {
}`
};
const lib1Public: File = {
path: `${currentDirectory}/lib1/public.ts`,
path: `${projectRoot}/lib1/public.ts`,
content: `export * from "./tools/public";`
};
const lib1ToolsPublic: File = {
path: `${currentDirectory}/lib1/tools/public.ts`,
path: `${projectRoot}/lib1/tools/public.ts`,
content: `export * from "./tools.interface";`
};
const lib1ToolsInterface: File = {
path: `${currentDirectory}/lib1/tools/tools.interface.ts`,
path: `${projectRoot}/lib1/tools/tools.interface.ts`,
content: `export interface ITest {
title: string;
}`
Expand Down Expand Up @@ -372,7 +371,7 @@ export class Data {

describe("when there are circular import and exports", () => {
const lib2Data: File = {
path: `${currentDirectory}/lib2/data.ts`,
path: `${projectRoot}/lib2/data.ts`,
content: `import { ITest } from "lib1/public"; import { Data2 } from "./data2";
export class Data {
public dat?: Data2; public test() {
Expand All @@ -384,7 +383,7 @@ export class Data {
}`
};
const lib2Data2: File = {
path: `${currentDirectory}/lib2/data2.ts`,
path: `${projectRoot}/lib2/data2.ts`,
content: `import { Data } from "./data";
export class Data2 {
public dat?: Data;
Expand Down
4 changes: 4 additions & 0 deletions src/testRunner/unittests/tscWatch/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
namespace ts {
export const projects = `/user/username/projects`;
export const projectRoot = `${projects}/myproject`;
}
namespace ts.tscWatch {
export import WatchedSystem = TestFSWithWatch.TestServerHost;
export type File = TestFSWithWatch.File;
Expand Down
Loading