Skip to content

Commit 3733d5e

Browse files
committed
Update the path in d.ts file if declaration dir/outDir is set in project with project references
Fixes #26036
1 parent 23e278e commit 3733d5e

34 files changed

+211
-19
lines changed

src/compiler/transformers/declarations.ts

+11
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,17 @@ namespace ts {
530530
if (symbol) {
531531
(exportedModulesFromDeclarationEmit || (exportedModulesFromDeclarationEmit = [])).push(symbol);
532532
}
533+
if ((options.outDir || options.declarationDir) && pathIsRelative(input.text) && currentSourceFile.redirectedReferences) {
534+
const normalizedTargetPath = getNormalizedAbsolutePath(input.text, getDirectoryPath(currentSourceFile.fileName));
535+
for (const ext of [Extension.Ts, Extension.Tsx]) {
536+
const probePath = normalizedTargetPath + ext;
537+
if (currentSourceFile.redirectedReferences.indexOf(probePath) >= 0) {
538+
const outputDir = getDirectoryPath(getOutputPathsFor(currentSourceFile, host, /*forceDtsPaths*/ true).declarationFilePath!);
539+
const relativePath = getRelativePathFromDirectory(outputDir, normalizedTargetPath, host.getCanonicalFileName);
540+
return createLiteral(relativePath);
541+
}
542+
}
543+
}
533544
}
534545
}
535546
return input;

src/testRunner/unittests/tsbuild.ts

+77-14
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ namespace ts {
33
export namespace Sample1 {
44
tick();
55
const projFs = loadProjectFromDisk("tests/projects/sample1");
6-
6+
const sample1ProjectOutput = loadOutputProjectFromDisk("sample1");
77
const allExpectedOutputs = ["/src/tests/index.js",
88
"/src/core/index.js", "/src/core/index.d.ts", "/src/core/index.d.ts.map",
99
"/src/logic/index.js", "/src/logic/index.js.map", "/src/logic/index.d.ts"];
@@ -17,11 +17,34 @@ namespace ts {
1717
host.clearDiagnostics();
1818
builder.buildAllProjects();
1919
host.assertDiagnosticMessages(/*empty*/);
20+
// Check for outputs
21+
verifyOutputsFs(fs, sample1ProjectOutput, "/src");
22+
});
2023

21-
// Check for outputs. Not an exhaustive list
22-
for (const output of allExpectedOutputs) {
23-
assert(fs.existsSync(output), `Expect file ${output} to exist`);
24-
}
24+
it("builds correctly when outDir is specified", () => {
25+
const fs = projFs.shadow();
26+
fs.writeFileSync("/src/logic/tsconfig.json", JSON.stringify({
27+
compilerOptions: { composite: true, declaration: true, outDir: "outDir" },
28+
references: [{ path: "../core" }]
29+
}));
30+
const host = new fakes.SolutionBuilderHost(fs);
31+
const builder = createSolutionBuilder(host, ["/src/tests"], { });
32+
builder.buildAllProjects();
33+
host.assertDiagnosticMessages(/*empty*/);
34+
verifyOutputs(fs, "sample1WithLogicOutDir");
35+
});
36+
37+
it("builds correctly when declarationDir is specified", () => {
38+
const fs = projFs.shadow();
39+
fs.writeFileSync("/src/logic/tsconfig.json", JSON.stringify({
40+
compilerOptions: { composite: true, declaration: true, declarationDir: "out/decls" },
41+
references: [{ path: "../core" }]
42+
}));
43+
const host = new fakes.SolutionBuilderHost(fs);
44+
const builder = createSolutionBuilder(host, ["/src/tests"], {});
45+
builder.buildAllProjects();
46+
host.assertDiagnosticMessages(/*empty*/);
47+
verifyOutputs(fs, "sample1WithLogicDeclarationDir");
2548
});
2649
});
2750

@@ -34,9 +57,7 @@ namespace ts {
3457
host.assertDiagnosticMessages(Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0, Diagnostics.A_non_dry_build_would_build_project_0);
3558

3659
// Check for outputs to not be written. Not an exhaustive list
37-
for (const output of allExpectedOutputs) {
38-
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
39-
}
60+
verifyOutputsNotExistOnFs(fs, sample1ProjectOutput, "/src");
4061
});
4162

4263
it("indicates that it would skip builds during a dry build", () => {
@@ -62,14 +83,10 @@ namespace ts {
6283
const builder = createSolutionBuilder(host, ["/src/tests"], { dry: false, force: false, verbose: false });
6384
builder.buildAllProjects();
6485
// Verify they exist
65-
for (const output of allExpectedOutputs) {
66-
assert(fs.existsSync(output), `Expect file ${output} to exist`);
67-
}
86+
verifyOutputsFs(fs, sample1ProjectOutput, "/src");
6887
builder.cleanAllProjects();
6988
// Verify they are gone
70-
for (const output of allExpectedOutputs) {
71-
assert(!fs.existsSync(output), `Expect file ${output} to not exist`);
72-
}
89+
verifyOutputsNotExistOnFs(fs, sample1ProjectOutput, "/src");
7390
// Subsequent clean shouldn't throw / etc
7491
builder.cleanAllProjects();
7592
});
@@ -529,4 +546,50 @@ export class cNew {}`);
529546
fs.makeReadonly();
530547
return fs;
531548
}
549+
550+
function loadOutputProjectFromDisk(project: string): vfs.FileSystem {
551+
const resolver = vfs.createResolver(Harness.IO);
552+
const fs = new vfs.FileSystem(/*ignoreCase*/ true, {
553+
files: {
554+
["/src"]: new vfs.Mount(vpath.resolve(Harness.IO.getWorkspaceRoot(), `tests/baselines/reference/projects/${project}`), resolver)
555+
},
556+
cwd: "/",
557+
time,
558+
});
559+
fs.makeReadonly();
560+
return fs;
561+
}
562+
563+
function verifyOutputs(buildFs: vfs.FileSystem, expectedOutputProject: string) {
564+
const fs = loadOutputProjectFromDisk(expectedOutputProject);
565+
verifyOutputsFs(buildFs, fs, "/src");
566+
}
567+
568+
function verifyOutputsFs(buildFs: vfs.FileSystem, expectedOutputFs: vfs.FileSystem, directory: string) {
569+
withOutputFs(expectedOutputFs, directory, outputFile => {
570+
const actual = buildFs.readFileSync(outputFile, "utf8");
571+
const expected = expectedOutputFs.readFileSync(outputFile, "utf8");
572+
assert.equal(actual, expected, `File contents of ${outputFile} expected to be:
573+
actual: ${actual}
574+
expected: ${expected}`);
575+
});
576+
}
577+
578+
function verifyOutputsNotExistOnFs(buildFs: vfs.FileSystem, expectedOutputFs: vfs.FileSystem, directory: string) {
579+
withOutputFs(expectedOutputFs, directory, outputFile =>
580+
assert(!buildFs.existsSync(outputFile), `Expect file ${outputFile} to not exist`));
581+
}
582+
583+
function withOutputFs(expectedOutputFs: vfs.FileSystem, directory: string, actionOnFile: (outputFile: string) => void) {
584+
const children = expectedOutputFs.readdirSync(directory);
585+
for (const child of children) {
586+
const fullName = `${directory}/${child}`;
587+
if (expectedOutputFs.statSync(fullName).isFile()) {
588+
actionOnFile(fullName);
589+
}
590+
else {
591+
withOutputFs(expectedOutputFs, fullName, actionOnFile);
592+
}
593+
}
594+
}
532595
}

src/testRunner/unittests/tsbuildWatchMode.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -486,11 +486,7 @@ export function gfoo() {
486486
solutionBuilder.buildInvalidatedProject();
487487

488488
host.checkTimeoutQueueLengthAndRun(1);
489-
checkOutputErrorsIncremental(host, [
490-
// TODO: #26036
491-
// The error is reported in d.ts file because it isnt resolved from ts file path, but is resolved from .d.ts file
492-
"sample1/logic/decls/index.d.ts(2,22): error TS2307: Cannot find module '../core/anotherModule'.\n"
493-
]);
489+
checkOutputErrorsIncremental(host, emptyArray);
494490
checkProgramActualFiles(watch().getProgram(), [tests[1].path, libFile.path, coreIndexDts, coreAnotherModuleDts, projectFilePath(SubProject.logic, "decls/index.d.ts")]);
495491
});
496492
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export declare const World = "hello";
2+
//# sourceMappingURL=anotherModule.d.ts.map

tests/baselines/reference/projects/sample1/core/anotherModule.d.ts.map

+1
Original file line numberDiff line numberDiff line change
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
exports.World = "hello";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export declare const someString: string;
2+
export declare function leftPad(s: string, n: number): string;
3+
export declare function multiply(a: number, b: number): number;
4+
//# sourceMappingURL=index.d.ts.map

tests/baselines/reference/projects/sample1/core/index.d.ts.map

+1
Original file line numberDiff line numberDiff line change
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
exports.someString = "HELLO WORLD";
4+
function leftPad(s, n) { return s + n; }
5+
exports.leftPad = leftPad;
6+
function multiply(a, b) { return a * b; }
7+
exports.multiply = multiply;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export declare function getSecondsInDay(): number;
2+
import * as mod from '../core/anotherModule';
3+
export declare const m: typeof mod;

tests/baselines/reference/projects/sample1/logic/index.js

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/baselines/reference/projects/sample1/logic/index.js.map

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import * as mod from '../core/anotherModule';
2+
export declare const m: typeof mod;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
var c = require("../core/index");
4+
var logic = require("../logic/index");
5+
c.leftPad("", 10);
6+
logic.getSecondsInDay();
7+
var mod = require("../core/anotherModule");
8+
exports.m = mod;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export declare const World = "hello";
2+
//# sourceMappingURL=anotherModule.d.ts.map

tests/baselines/reference/projects/sample1WithLogicDeclarationDir/core/anotherModule.d.ts.map

+1
Original file line numberDiff line numberDiff line change
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
exports.World = "hello";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export declare const someString: string;
2+
export declare function leftPad(s: string, n: number): string;
3+
export declare function multiply(a: number, b: number): number;
4+
//# sourceMappingURL=index.d.ts.map

tests/baselines/reference/projects/sample1WithLogicDeclarationDir/core/index.d.ts.map

+1
Original file line numberDiff line numberDiff line change
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
exports.someString = "HELLO WORLD";
4+
function leftPad(s, n) { return s + n; }
5+
exports.leftPad = leftPad;
6+
function multiply(a, b) { return a * b; }
7+
exports.multiply = multiply;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
var c = require("../core/index");
4+
function getSecondsInDay() {
5+
return c.multiply(10, 15);
6+
}
7+
exports.getSecondsInDay = getSecondsInDay;
8+
var mod = require("../core/anotherModule");
9+
exports.m = mod;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export declare function getSecondsInDay(): number;
2+
import * as mod from "../../../core/anotherModule";
3+
export declare const m: typeof mod;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import * as mod from '../core/anotherModule';
2+
export declare const m: typeof mod;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
var c = require("../core/index");
4+
var logic = require("../logic/index");
5+
c.leftPad("", 10);
6+
logic.getSecondsInDay();
7+
var mod = require("../core/anotherModule");
8+
exports.m = mod;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export declare const World = "hello";
2+
//# sourceMappingURL=anotherModule.d.ts.map

tests/baselines/reference/projects/sample1WithLogicOutDir/core/anotherModule.d.ts.map

+1
Original file line numberDiff line numberDiff line change
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
exports.World = "hello";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export declare const someString: string;
2+
export declare function leftPad(s: string, n: number): string;
3+
export declare function multiply(a: number, b: number): number;
4+
//# sourceMappingURL=index.d.ts.map

tests/baselines/reference/projects/sample1WithLogicOutDir/core/index.d.ts.map

+1
Original file line numberDiff line numberDiff line change
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
exports.someString = "HELLO WORLD";
4+
function leftPad(s, n) { return s + n; }
5+
exports.leftPad = leftPad;
6+
function multiply(a, b) { return a * b; }
7+
exports.multiply = multiply;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export declare function getSecondsInDay(): number;
2+
import * as mod from "../../core/anotherModule";
3+
export declare const m: typeof mod;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
var c = require("../core/index");
4+
function getSecondsInDay() {
5+
return c.multiply(10, 15);
6+
}
7+
exports.getSecondsInDay = getSecondsInDay;
8+
var mod = require("../core/anotherModule");
9+
exports.m = mod;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import * as mod from '../core/anotherModule';
2+
export declare const m: typeof mod;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"use strict";
2+
exports.__esModule = true;
3+
var c = require("../core/index");
4+
var logic = require("../logic/index");
5+
c.leftPad("", 10);
6+
logic.getSecondsInDay();
7+
var mod = require("../core/anotherModule");
8+
exports.m = mod;

0 commit comments

Comments
 (0)