diff --git a/.azure-pipelines/typescript-pr-dt.yml b/.azure-pipelines/typescript-pr-dt.yml
index 0d9a21d14d..03ec8908d6 100644
--- a/.azure-pipelines/typescript-pr-dt.yml
+++ b/.azure-pipelines/typescript-pr-dt.yml
@@ -84,6 +84,7 @@ jobs:
- task: GoTool@0
displayName: Use Go 1.27
+ retryCountOnTaskFailure: 3
inputs:
version: "1.27"
goDownloadUrl: https://aka.ms/golang/release/latest
diff --git a/.changeset/neat-dt-results.md b/.changeset/neat-dt-results.md
new file mode 100644
index 0000000000..a845151cc8
--- /dev/null
+++ b/.changeset/neat-dt-results.md
@@ -0,0 +1,2 @@
+---
+---
diff --git a/packages/dtslint-runner/src/add-github-links.ts b/packages/dtslint-runner/src/add-github-links.ts
index 10219a5dd0..c80ee6eae6 100644
--- a/packages/dtslint-runner/src/add-github-links.ts
+++ b/packages/dtslint-runner/src/add-github-links.ts
@@ -59,14 +59,16 @@ export async function addGithubLinks(failures: Failure[], definitelyTypedPath: s
let stylishFile: string | undefined;
for (const match of failure.error.matchAll(/^.*$/gm)) {
const line = match[0].replace(/\r$/, "");
- const position = /^(.+?\.(?:[cm]?tsx?|json))(?::(\d+):(\d+)|\((\d+),(\d+)\))/.exec(line);
+ const prefixLength = line.startsWith("Error: ") ? "Error: ".length : 0;
+ const position = /^(.+?\.(?:[cm]?tsx?|json))(?::(\d+):(\d+)|\((\d+),(\d+)\))/.exec(line.slice(prefixLength));
const fileHeader = /^(.+\.(?:[cm]?tsx?|json))$/.exec(line);
const stylishPosition = /^\s+(\d+):(\d+)\s+(?:error|warning)\b/.exec(line);
if (position) {
stylishFile = undefined;
const file = resolve(packagePath, position[1]);
addLocation(file, Number(position[2] ?? position[4]), (url) => {
- links.push({ start: match.index, end: match.index + position[0].length, url });
+ const start = match.index + prefixLength;
+ links.push({ start, end: start + position[0].length, url });
});
} else if (fileHeader) {
stylishFile = resolve(packagePath, fileHeader[1]);
diff --git a/packages/dtslint-runner/src/post-results.ts b/packages/dtslint-runner/src/post-results.ts
index e899c68774..000dc00cf9 100644
--- a/packages/dtslint-runner/src/post-results.ts
+++ b/packages/dtslint-runner/src/post-results.ts
@@ -181,13 +181,14 @@ function getDiffReports(main: Errors, branch: Errors): DiffReport[] {
}
function formatReport(report: DiffReport, maxErrorLength = Infinity): string {
+ const packageName = escapeHtml((report.branch ?? report.main)!.path);
const errors = [
...(report.main ? [`Main error:\n${formatProjects(report.main)}${formatError(report.main, maxErrorLength)}`] : []),
...(report.branch
? [`Branch error:\n${formatProjects(report.branch)}${formatError(report.branch, maxErrorLength)}`]
: []),
];
- return `${report.title}
\n\n${formatPackage((report.branch ?? report.main)!)}\n${errors.join("\n")}\n${report.title}
\n\n${formatPackage((report.branch ?? report.main)!)}\n${errors.join("\n")}\n${packageName}example`);
expect(comment).toContain(
- `
${file}:12:3`,
+ `types/example/example-tests.ts:12:3`,
);
expect(comment).toContain("Type '<T>' is not assignable.");
+ expect(comment).toContain(
+ `types/example/index.d.ts:2:1`,
+ );
+ expect(comment).not.toContain(checkout);
});
test("links ESLint stylish headers and individual error lines, including CRLF", async () => {
@@ -99,6 +103,26 @@ test("links ESLint stylish headers and individual error lines, including CRLF",
["2:3", `${repoUrl}/blob/${commit}/types/example/index.d.ts#L2`],
["4:5", `${repoUrl}/blob/${commit}/types/example/index.d.ts#L4`],
]);
+ const comment = getDiffComment([], failures)!;
+ expect(comment).toContain(
+ `types/example/index.d.ts`,
+ );
+ expect(comment).toContain(`2:3`);
+});
+
+test("links the first diagnostic after the Error stack prefix", async () => {
+ const file = join(checkout, "types/example/index.d.ts");
+ const error = `Error: ${file}:2:6\nTypeScript@local compile error TS2300:\nDuplicate identifier.\n\n${file}:5:6\nAnother error.`;
+ const failures: Failure[] = [{ path: "example", error }];
+ await addGithubLinks(failures, checkout);
+ expect(failures[0].error).toBe(error);
+ expect(failures[0].errorLinks?.map(({ start, end, url }) => [error.slice(start, end), url])).toEqual([
+ [`${file}:2:6`, `${repoUrl}/blob/${commit}/types/example/index.d.ts#L2`],
+ [`${file}:5:6`, `${repoUrl}/blob/${commit}/types/example/index.d.ts#L5`],
+ ]);
+ expect(getDiffComment([], failures)).toContain(
+ `Error: types/example/index.d.ts:2:6`,
+ );
});
test("resolves versioned packages, workspace dependencies, and tsc locations", async () => {
@@ -110,6 +134,13 @@ test("resolves versioned packages, workspace dependencies, and tsc locations", a
expect(failures[0].packageUrl).toBe(`${repoUrl}/tree/${commit}/types/example/v1`);
expect(failures[0].errorLinks?.[0].url).toBe(`${repoUrl}/blob/${commit}/types/example/v1/index.d.ts#L3`);
expect(failures[1].errorLinks?.[0].url).toBe(`${repoUrl}/blob/${commit}/types/dependency/index.d.ts#L5`);
+ const comment = getDiffComment([], failures)!;
+ expect(comment).toContain(
+ `types/example/v1/index.d.ts(3,4)`,
+ );
+ expect(comment).toContain(
+ `types/dependency/index.d.ts:5:6`,
+ );
});
test("leaves untracked, missing, installed, and external files unlinked", async () => {
@@ -166,9 +197,9 @@ test("renders all three difference categories, retaining each side's links", ()
{ path: "changed", error: "new", errorLinks: [{ start: 0, end: 3, url: branchUrl }] },
],
)!;
- expect(comment).toContain("Branch only errors: ");
- expect(comment).toContain("Main only errors: ");
- expect(comment).toContain("Errors that changed between main and the branch: ");
+ expect(comment).toContain("Branch only errors: new ");
+ expect(comment).toContain("Main only errors: fixed ");
+ expect(comment).toContain("Errors that changed between main and the branch: changed ");
expect(comment).toContain(`old
`);
expect(comment).toContain(`new
`);
expect(comment).toContain("old failure
");
@@ -188,6 +219,7 @@ test("escapes diagnostic HTML and refuses non-DT links", () => {
},
],
)!;
+ expect(comment).toContain("Branch only errors: <example> ");
expect(comment).toContain("Package: <example>");
expect(comment).toContain("</pre><script>alert("oops")</script>\n```\n& <T>");
expect(comment).not.toContain("