Skip to content

Commit 25ec62f

Browse files
authored
Handle when output file would be in subFolder specified by outDir/declarationDir (#35366)
Fixes #35328
1 parent ea4e6b3 commit 25ec62f

File tree

3 files changed

+89
-45
lines changed

3 files changed

+89
-45
lines changed

src/server/project.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1765,7 +1765,11 @@ namespace ts.server {
17651765
const dirPathWithTrailingDirectorySeparator = `${dirPath}${directorySeparator}`;
17661766
return forEachKey(
17671767
this.mapOfDeclarationDirectories!,
1768-
declDirPath => dirPath === declDirPath || startsWith(declDirPath, dirPathWithTrailingDirectorySeparator)
1768+
declDirPath => dirPath === declDirPath ||
1769+
// Any parent directory of declaration dir
1770+
startsWith(declDirPath, dirPathWithTrailingDirectorySeparator) ||
1771+
// Any directory inside declaration dir
1772+
startsWith(dirPath, `${declDirPath}/`)
17691773
);
17701774
}
17711775

@@ -1802,6 +1806,16 @@ namespace ts.server {
18021806
return this.fileOrDirectoryExistsUsingSource(path, /*isFile*/ false);
18031807
}
18041808

1809+
/**
1810+
* Call super.getDirectories only if directory actually present on the host
1811+
* This is needed to ensure that we arent getting directories that we fake about presence for
1812+
*/
1813+
getDirectories(path: string): string[] {
1814+
return !this.useSourceOfProjectReferenceRedirect() || !this.projectReferenceCallbacks || super.directoryExists(path) ?
1815+
super.getDirectories(path) :
1816+
[];
1817+
}
1818+
18051819
private realpathIfSymlinkedProjectReferenceDts(s: string): string | undefined {
18061820
return this.symlinkedFiles && this.symlinkedFiles.get(this.toPath(s));
18071821
}
@@ -1820,7 +1834,7 @@ namespace ts.server {
18201834
const directoryPath = ensureTrailingDirectorySeparator(this.toPath(directory));
18211835
if (this.symlinkedDirectories.has(directoryPath)) return;
18221836

1823-
const real = this.projectService.host.realpath!(directory);
1837+
const real = normalizePath(this.projectService.host.realpath!(directory));
18241838
let realPath: Path;
18251839
if (real === directory ||
18261840
(realPath = ensureTrailingDirectorySeparator(this.toPath(real))) === directoryPath) {

src/testRunner/unittests/tsserver/projectReferences.ts

Lines changed: 68 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,78 +1375,70 @@ function foo() {
13751375
});
13761376

13771377
describe("when references are monorepo like with symlinks", () => {
1378-
function verifySession(alreadyBuilt: boolean, extraOptions: CompilerOptions) {
1379-
const bPackageJson: File = {
1380-
path: `${projectRoot}/packages/B/package.json`,
1381-
content: JSON.stringify({
1382-
main: "lib/index.js",
1383-
types: "lib/index.d.ts"
1384-
})
1385-
};
1378+
interface Packages {
1379+
bPackageJson: File;
1380+
aTest: File;
1381+
bFoo: File;
1382+
bBar: File;
1383+
}
1384+
function verifySymlinkScenario(packages: () => Packages) {
1385+
describe("when solution is not built", () => {
1386+
it("with preserveSymlinks turned off", () => {
1387+
verifySession(packages(), /*alreadyBuilt*/ false, {});
1388+
});
1389+
1390+
it("with preserveSymlinks turned on", () => {
1391+
verifySession(packages(), /*alreadyBuilt*/ false, { preserveSymlinks: true });
1392+
});
1393+
});
1394+
1395+
describe("when solution is already built", () => {
1396+
it("with preserveSymlinks turned off", () => {
1397+
verifySession(packages(), /*alreadyBuilt*/ true, {});
1398+
});
1399+
1400+
it("with preserveSymlinks turned on", () => {
1401+
verifySession(packages(), /*alreadyBuilt*/ true, { preserveSymlinks: true });
1402+
});
1403+
});
1404+
}
1405+
1406+
function verifySession({ bPackageJson, aTest, bFoo, bBar }: Packages, alreadyBuilt: boolean, extraOptions: CompilerOptions) {
13861407
const aConfig = config("A", extraOptions, ["../B"]);
13871408
const bConfig = config("B", extraOptions);
1388-
const aIndex = index("A", `import { foo } from 'b';
1389-
import { bar } from 'b/lib/bar';
1390-
foo();
1391-
bar();`);
1392-
const bIndex = index("B", `export function foo() { }`);
1393-
const bBar: File = {
1394-
path: `${projectRoot}/packages/B/src/bar.ts`,
1395-
content: `export function bar() { }`
1396-
};
13971409
const bSymlink: SymLink = {
13981410
path: `${projectRoot}/node_modules/b`,
13991411
symLink: `${projectRoot}/packages/B`
14001412
};
1401-
1402-
const files = [libFile, bPackageJson, aConfig, bConfig, aIndex, bIndex, bBar, bSymlink];
1413+
const files = [libFile, bPackageJson, aConfig, bConfig, aTest, bFoo, bBar, bSymlink];
14031414
const host = alreadyBuilt ?
14041415
createHost(files, [aConfig.path]) :
14051416
createServerHost(files);
14061417

14071418
// Create symlink in node module
14081419
const session = createSession(host, { canUseEvents: true });
1409-
openFilesForSession([aIndex], session);
1420+
openFilesForSession([aTest], session);
14101421
const service = session.getProjectService();
14111422
const project = service.configuredProjects.get(aConfig.path.toLowerCase())!;
14121423
assert.deepEqual(project.getAllProjectErrors(), []);
14131424
checkProjectActualFiles(
14141425
project,
1415-
[aConfig.path, aIndex.path, bIndex.path, bBar.path, libFile.path]
1426+
[aConfig.path, aTest.path, bFoo.path, bBar.path, libFile.path]
14161427
);
14171428
verifyGetErrRequest({
14181429
host,
14191430
session,
14201431
expected: [
1421-
{ file: aIndex, syntax: [], semantic: [], suggestion: [] }
1432+
{ file: aTest, syntax: [], semantic: [], suggestion: [] }
14221433
]
14231434
});
14241435
}
14251436

1426-
function verifySymlinkScenario(alreadyBuilt: boolean) {
1427-
it("with preserveSymlinks turned off", () => {
1428-
verifySession(alreadyBuilt, {});
1429-
});
1430-
1431-
it("with preserveSymlinks turned on", () => {
1432-
verifySession(alreadyBuilt, { preserveSymlinks: true });
1433-
});
1434-
}
1435-
1436-
describe("when solution is not built", () => {
1437-
verifySymlinkScenario(/*alreadyBuilt*/ false);
1438-
});
1439-
1440-
describe("when solution is already built", () => {
1441-
verifySymlinkScenario(/*alreadyBuilt*/ true);
1442-
});
1443-
14441437
function config(packageName: string, extraOptions: CompilerOptions, references?: string[]): File {
14451438
return {
14461439
path: `${projectRoot}/packages/${packageName}/tsconfig.json`,
14471440
content: JSON.stringify({
14481441
compilerOptions: {
1449-
baseUrl: ".",
14501442
outDir: "lib",
14511443
rootDir: "src",
14521444
composite: true,
@@ -1458,12 +1450,45 @@ bar();`);
14581450
};
14591451
}
14601452

1461-
function index(packageName: string, content: string): File {
1453+
function file(packageName: string, fileName: string, content: string): File {
14621454
return {
1463-
path: `${projectRoot}/packages/${packageName}/src/index.ts`,
1455+
path: `${projectRoot}/packages/${packageName}/src/${fileName}`,
14641456
content
14651457
};
14661458
}
1459+
1460+
describe("when packageJson has types field and has index.ts", () => {
1461+
verifySymlinkScenario(() => ({
1462+
bPackageJson: {
1463+
path: `${projectRoot}/packages/B/package.json`,
1464+
content: JSON.stringify({
1465+
main: "lib/index.js",
1466+
types: "lib/index.d.ts"
1467+
})
1468+
},
1469+
aTest: file("A", "index.ts", `import { foo } from 'b';
1470+
import { bar } from 'b/lib/bar';
1471+
foo();
1472+
bar();`),
1473+
bFoo: file("B", "index.ts", `export function foo() { }`),
1474+
bBar: file("B", "bar.ts", `export function bar() { }`)
1475+
}));
1476+
});
1477+
1478+
describe("when referencing file from subFolder", () => {
1479+
verifySymlinkScenario(() => ({
1480+
bPackageJson: {
1481+
path: `${projectRoot}/packages/B/package.json`,
1482+
content: "{}"
1483+
},
1484+
aTest: file("A", "test.ts", `import { foo } from 'b/lib/foo';
1485+
import { bar } from 'b/lib/bar/foo';
1486+
foo();
1487+
bar();`),
1488+
bFoo: file("B", "foo.ts", `export function foo() { }`),
1489+
bBar: file("B", "bar/foo.ts", `export function bar() { }`)
1490+
}));
1491+
});
14671492
});
14681493
});
14691494
}

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8707,6 +8707,11 @@ declare namespace ts.server {
87078707
* If it is it returns true irrespective of whether that directory exists on host
87088708
*/
87098709
directoryExists(path: string): boolean;
8710+
/**
8711+
* Call super.getDirectories only if directory actually present on the host
8712+
* This is needed to ensure that we arent getting directories that we fake about presence for
8713+
*/
8714+
getDirectories(path: string): string[];
87108715
private realpathIfSymlinkedProjectReferenceDts;
87118716
private getRealpath;
87128717
private handleDirectoryCouldBeSymlink;

0 commit comments

Comments
 (0)