Skip to content
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
1 change: 1 addition & 0 deletions .azure-pipelines/typescript-pr-dt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions .changeset/neat-dt-results.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
6 changes: 4 additions & 2 deletions packages/dtslint-runner/src/add-github-links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
12 changes: 10 additions & 2 deletions packages/dtslint-runner/src/post-results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<details>\n<summary>${report.title}</summary>\n\n${formatPackage((report.branch ?? report.main)!)}\n${errors.join("\n")}\n</details>`;
return `<details>\n<summary>${report.title} <code>${packageName}</code></summary>\n\n${formatPackage((report.branch ?? report.main)!)}\n${errors.join("\n")}\n</details>`;
}

export function getResultComments(main: Errors, branch: Errors, userToTag: string, logUrl: string): string[] {
Expand Down Expand Up @@ -302,7 +303,14 @@ function formatError(error: Failure, maxLength = Infinity): string {
for (const location of error.errorLinks ?? []) {
if (location.end > end) break;
result += escapeHtml(error.error.slice(start, location.start));
result += link(error.error.slice(location.start, location.end), location.url);
const text = error.error.slice(location.start, location.end);
const githubPath = /^https:\/\/github\.com\/DefinitelyTyped\/DefinitelyTyped\/blob\/[^/]+\/([^#]+)(?:#L\d+)?$/.exec(
location.url,
);
const position = /(?::\d+:\d+|\(\d+,\d+\))$/.exec(text)?.[0] ?? "";
const label =
githubPath && /\.(?:[cm]?tsx?|json)(?::\d+:\d+|\(\d+,\d+\))?$/.test(text) ? `${githubPath[1]}${position}` : text;
result += link(label, location.url);
start = location.end;
}
result += escapeHtml(error.error.slice(start, end));
Expand Down
43 changes: 38 additions & 5 deletions packages/dtslint-runner/test/results.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,13 @@ test("adds pinned package and Corsa diagnostic links without changing the raw er
const comment = getDiffComment([], failures)!;
expect(comment).toContain(`<code><a href="${repoUrl}/tree/${commit}/types/example">example</a></code>`);
expect(comment).toContain(
`<pre><a href="${repoUrl}/blob/${commit}/types/example/example-tests.ts#L12">${file}:12:3</a>`,
`<pre><a href="${repoUrl}/blob/${commit}/types/example/example-tests.ts#L12">types/example/example-tests.ts:12:3</a>`,
);
expect(comment).toContain("Type '&lt;T&gt;' is not assignable.");
expect(comment).toContain(
`<a href="${repoUrl}/blob/${commit}/types/example/index.d.ts#L2">types/example/index.d.ts:2:1</a>`,
);
expect(comment).not.toContain(checkout);
});

test("links ESLint stylish headers and individual error lines, including CRLF", async () => {
Expand All @@ -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(
`<a href="${repoUrl}/blob/${commit}/types/example/index.d.ts">types/example/index.d.ts</a>`,
);
expect(comment).toContain(`<a href="${repoUrl}/blob/${commit}/types/example/index.d.ts#L2">2:3</a>`);
});

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(
`<pre>Error: <a href="${repoUrl}/blob/${commit}/types/example/index.d.ts#L2">types/example/index.d.ts:2:6</a>`,
);
});

test("resolves versioned packages, workspace dependencies, and tsc locations", async () => {
Expand All @@ -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(
`<a href="${repoUrl}/blob/${commit}/types/example/v1/index.d.ts#L3">types/example/v1/index.d.ts(3,4)</a>`,
);
expect(comment).toContain(
`<a href="${repoUrl}/blob/${commit}/types/dependency/index.d.ts#L5">types/dependency/index.d.ts:5:6</a>`,
);
});

test("leaves untracked, missing, installed, and external files unlinked", async () => {
Expand Down Expand Up @@ -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("<summary>Branch only errors:</summary>");
expect(comment).toContain("<summary>Main only errors:</summary>");
expect(comment).toContain("<summary>Errors that changed between main and the branch:</summary>");
expect(comment).toContain("<summary>Branch only errors: <code>new</code></summary>");
expect(comment).toContain("<summary>Main only errors: <code>fixed</code></summary>");
expect(comment).toContain("<summary>Errors that changed between main and the branch: <code>changed</code></summary>");
expect(comment).toContain(`<pre><a href="${mainUrl}">old</a></pre>`);
expect(comment).toContain(`<pre><a href="${branchUrl}">new</a></pre>`);
expect(comment).toContain("<pre>old failure</pre>");
Expand All @@ -188,6 +219,7 @@ test("escapes diagnostic HTML and refuses non-DT links", () => {
},
],
)!;
expect(comment).toContain("<summary>Branch only errors: <code>&lt;example&gt;</code></summary>");
expect(comment).toContain("Package: <code>&lt;example&gt;</code>");
expect(comment).toContain("&lt;/pre&gt;&lt;script&gt;alert(&quot;oops&quot;)&lt;/script&gt;\n```\n&amp; &lt;T&gt;");
expect(comment).not.toContain("<script>");
Expand All @@ -208,6 +240,7 @@ test("links project-level errors to the default and explicitly configured tsconf
{ path: "tsconfig.other.json", url: `${repoUrl}/blob/${commit}/types/example/v1/tsconfig.other.json` },
]);
const comment = getDiffComment([], failures)!;
expect(comment).toContain("<summary>Branch only errors: <code>example/v1</code></summary>");
expect(comment).toContain(`Project scope: <code><a href="${failures[0].projects![0].url}">tsconfig.json</a></code>`);
expect(comment).toContain(`href="${failures[1].projects![1].url}"`);
});
Expand Down Expand Up @@ -277,7 +310,7 @@ test("safely truncates oversized individual reports but retains full linked outp
const comments = getResultComments([], [failure, { path: "next", error: "still included" }], "tester", logUrl);
expect(comments).toHaveLength(2);
expectBalancedComments(comments);
expect(comments[0]).toContain(`<a href="${url}">index.d.ts:1:1</a>`);
expect(comments[0]).toContain(`<a href="${url}">types/example/index.d.ts:1:1</a>`);
expect(comments[0]).toContain("&lt;&amp;&quot;😀&gt;");
expect(comments[0]).toContain("[... truncated ...]");
expect(comments[0]).toContain("This package report was truncated");
Expand Down
Loading