codeflash-internal/js/cf-api/github/pr-changes-utils.ts
Sarthak Agarwal 1cb2051b15
consistency in formatting across ide & js projs (#1499)
### **PR Type**
- Enhancement



___

### **Description**
- Add pre-commit hook integration and formatting commands

- Introduce lint-staged and simple-git-hooks into package scripts

- Update prettier configuration and ignore files for consistency

- Refresh dependency lock files with new tooling entries


___



### **Changes walkthrough** 📝
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Dependencies</strong></td><td><details><summary>2
files</summary><table>
<tr>
<td><strong>package-lock.json</strong><dd><code>Update dependency lock
with new tooling entries</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-54c17cef859f033fc84a59da2e977235ebc494943710c25d132e310ec500c5ef">+754/-2</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>package-lock.json</strong><dd><code>Refresh package lock
with lint and formatting tools</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-75446c74353509ca0232d6a1350aef075ced8f72bd568e9bafa09cf255683142">+743/-0</a>&nbsp;
</td>

</tr>
</table></details></td></tr><tr><td><strong>Configuration
changes</strong></td><td><details><summary>4 files</summary><table>
<tr>
<td><strong>package.json</strong><dd><code>Add formatting, lint and
pre-commit hook scripts</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-4edec169b0f8d3312edaf35b5cc8521fe1edfa163ce174f60eff51906896601f">+34/-17</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Introduce formatting commands
and pre-commit hooks</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-53ddfb1f8a02f1231d3d15a2e694ffe1407d2cc01d3e685de5653b67fec571c7">+18/-1</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>package.json</strong><dd><code>Integrate pre-commit hook and
formatting configurations</code>&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-b0d32af9c2caaba1377ec3e924eb553105cdc86e244018ffc6a866c530523599">+20/-3</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>settings.json</strong><dd><code>Enhance VSCode settings for
auto-format and lint fixes</code>&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-a5de3e5871ffcc383a2294845bd3df25d3eeff6c29ad46e3a396577c413bf357">+13/-1</a>&nbsp;
&nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Documentation</strong></td><td><details><summary>7
files</summary><table>
<tr>
<td><strong>.editorconfig</strong><dd><code>Add consistent editor
settings for file formatting</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-0947e2727d6bad8cd0ac4122f5314bb5b04e337393075bc4b5ef143b17fcbd5b">+32/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>.prettierrc</strong><dd><code>Update prettier config with
extended formatting rules</code>&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-ce5b7ae243151fb6eb3db1799b95d5c50ce2fe5080e8365c7834f81e8a44aade">+10/-4</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>.prettierrc</strong><dd><code>Update prettier settings for
consistent code style</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-e169a799a8a22863b844d1c816ebb5798c0bcf8151503b0329bf60a2b3050b03">+10/-4</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>.prettierrc</strong><dd><code>Add new prettier configuration
file</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-7058ba9d421d7fa280582bcc9a2053e64ec0b2bb700ae46cb7073f295d154713">+10/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>.prettierignore</strong><dd><code>Extend ignore rules with
node_modules and dist folders</code>&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-a33307d68affc99ba88b1b79308f622350c8306bdeac2368b70d99ce72a7c8fa">+3/-1</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>.prettierignore</strong><dd><code>Add ignore patterns for
node_modules and dist directories</code></dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-8f0741d174231baef1746c1fdb003dc727bb4416e16e99166edc020670861c1d">+2/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
<td><strong>.prettierignore</strong><dd><code>Update ignore file to
include node_modules and dist folders</code></dd></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-e84a66c182e9d121fc156f4b50d606f385b591ed493f8c284628451d58907875">+2/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Additional
files</strong></td><td><details><summary>1 files</summary><table>
<tr>
  <td><strong>package-lock.json</strong></td>
<td><a
href="https://github.com/codeflash-ai/codeflash-internal/pull/1499/files#diff-0214c85d1717ad8b736e0296bb8cbf50db2aed068f31316d3c39904824a14f8e">+1026/-52</a></td>

</tr>
</table></details></td></tr></tr></tbody></table>

___

> <details> <summary> Need help?</summary><li>Type <code>/help how to
...</code> in the comments thread for any questions about PR-Agent
usage.</li><li>Check out the <a
href="https://qodo-merge-docs.qodo.ai/usage-guide/">documentation</a>
for more information.</li></details>
2025-03-04 23:52:45 +00:00

192 lines
6.8 KiB
TypeScript

import fs from "fs"
import path from "path"
import { fileURLToPath } from "url"
import { dirname } from "path"
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const PR_HEADER_TEMPLATE = fs.readFileSync(
path.join(__dirname, "optimization_results_header.md"),
"utf8",
)
const PR_DETAILS_TEMPLATE = fs.readFileSync(
path.join(__dirname, "optimization_results_details.md"),
"utf8",
)
const PR_TEST_REPORT_TEMPLATE = fs.readFileSync(
path.join(__dirname, "optimization_results_test_report.md"),
"utf8",
)
const summaryLinesRegex = new RegExp(
/### 📄 ([\d,]+(\.\d+)?)% \(([\d,]+(\.\d+)?)x\) speedup for \*\*\*`(.+?)` in `(.+?)`\*\*\*/g,
)
type ReportTable = Record<
string,
{
passed: number
failed: number
}
>
type PrCommentFields = Record<string, any>
export function buildPrCommentBody(
prCommentFields: PrCommentFields,
existingTests: any,
generatedTests: any,
coverage_message: any,
newBranchName: string,
): string {
return (
`## ⚡️ Codeflash found optimizations for this PR\n` +
`${buildResultHeader(prCommentFields)}\n` +
`${buildResultDetails(prCommentFields)}\n` +
`${buildResultTestReport(prCommentFields, existingTests, generatedTests, coverage_message)}\n` +
`${buildMergeBranchMsg(newBranchName)}\n`
)
}
export function buildMergeBranchMsg(newBranchName: string): string {
if (newBranchName?.length > 0) {
return "To test or edit this optimization locally " + "`git merge " + newBranchName + "`\n\n"
}
return ""
}
export function buildResultHeader(fields: PrCommentFields): string {
return PR_HEADER_TEMPLATE.replace(/\{best_runtime}/g, fields.best_runtime)
.replace(/\{original_runtime}/g, fields.original_runtime)
.replace(/\{function_name}/g, fields.function_name)
.replace(/\{file_path}/g, fields.file_path)
.replace(/\{speedup_x}/g, fields.speedup_x)
.replace(/\{speedup_pct}/g, fields.speedup_pct)
.replace(/\{num_runs}/g, fields.loop_count)
}
export function buildResultDetails(fields: PrCommentFields): string {
return (
PR_DETAILS_TEMPLATE.replace(/\{optimization_explanation}/g, fields.optimization_explanation) +
"\n"
)
}
export function buildResultFooter(newBranchName: string): string {
return (
"To edit these changes " +
"`git checkout " +
newBranchName +
"` and push.\n\n" +
`[![Codeflash](https://img.shields.io/badge/Optimized%20with-Codeflash-yellow?style=flat&color=%23ffc428&logo=data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgwIiBoZWlnaHQ9ImF1dG8iIHZpZXdCb3g9IjAgMCA0ODAgMjgwIiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTI4Ni43IDAuMzc4NDE4SDIwMS43NTFMNTAuOTAxIDE0OC45MTFIMTM1Ljg1MUwwLjk2MDkzOCAyODEuOTk5SDk1LjQzNTJMMjgyLjMyNCA4OS45NjE2SDE5Ni4zNDVMMjg2LjcgMC4zNzg0MThaIiBmaWxsPSIjRkZDMDQzIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMzExLjYwNyAwLjM3ODkwNkwyNTguNTc4IDU0Ljk1MjZIMzc5LjU2N0w0MzIuMzM5IDAuMzc4OTA2SDMxMS42MDdaIiBmaWxsPSIjMEIwQTBBIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMzA5LjU0NyA4OS45NjAxTDI1Ni41MTggMTQ0LjI3NkgzNzcuNTA2TDQzMC4wMjEgODkuNzAyNkgzMDkuNTQ3Vjg5Ljk2MDFaIiBmaWxsPSIjMEIwQTBBIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMjQyLjg3MyAxNjQuNjZMMTg5Ljg0NCAyMTkuMjM0SDMxMC44MzNMMzYzLjM0NyAxNjQuNjZIMjQyLjg3M1oiIGZpbGw9IiMwQjBBMEEiLz4KPC9zdmc+Cg==)](https://codeflash.ai)`
)
}
export function buildResultTestReport(
fields: PrCommentFields,
existingTests: string,
generatedTests: string,
coverage_message: string,
): string {
const reportTableDict: ReportTable = fields.report_table
let reportTableMd: string = ""
// Initialize the markdown table
reportTableMd += "| Test | Status |\n"
reportTableMd += "| --------------------------- | ----------------- |\n"
// Loop through each test type and construct the table rows
for (const [testType, { passed = 0, failed = 0 }] of Object.entries(reportTableDict)) {
if (testType.includes("🎨")) {
continue
}
let status = ""
let detailsNote = ""
// Determine the status based on passed counts
if (passed > 0) {
status = `✅ **${passed} Passed**`
} else {
status = "🔘 **None Found**"
}
// If there are details, note that they are available below
if (passed > 0 && (testType.includes("Existing") || testType.includes("Generated"))) {
detailsNote = "See below"
} else {
detailsNote = ""
}
// Add the row to the markdown table
reportTableMd += `| ${testType} | ${status} |\n`
}
reportTableMd += `|📊 Tests Coverage | ${coverage_message} |\n`
// Add detailed test outputs below the table
for (const [testType, { passed = 0, failed = 0 }] of Object.entries(reportTableDict)) {
// Only include details for Existing and Generated tests with results
if (passed > 0 && (testType.includes("Existing") || testType.includes("Generated"))) {
// Add a heading for the test type details
reportTableMd += `<details>\n`
reportTableMd += `<summary>${testType} Details</summary>\n\n`
// Include the relevant test code
if (testType.includes("Existing")) {
reportTableMd += "```python\n"
reportTableMd += existingTests.trim()
reportTableMd += "\n```\n"
} else if (testType.includes("Generated")) {
reportTableMd += "```python\n"
reportTableMd += generatedTests.trim()
reportTableMd += "\n```\n"
} else {
reportTableMd += "_No additional details available._\n"
}
reportTableMd += `\n</details>\n\n`
}
}
// Add the final markdown content (e.g., the feedback section)
const finalMarkdown = `${reportTableMd}`
return PR_TEST_REPORT_TEMPLATE.replace(/\{report_table}/g, finalMarkdown)
}
export function parseAndCreateOptimizationsDict(
prBody: string,
prComments: { body: string }[],
): Record<string, Set<string>> {
const optimizations: Record<string, Set<string>> = {}
const textsToParse = [prBody, ...prComments.map(comment => comment.body)]
for (const text of textsToParse) {
let match
while ((match = summaryLinesRegex.exec(text)) !== null) {
const functionName = match[5]
const filePath = match[6]
if (!optimizations[filePath]) {
optimizations[filePath] = new Set()
}
optimizations[filePath].add(functionName)
}
}
return optimizations
}
export function buildDependentPrTitle(
functionName: string,
speedupPct: string,
pullNumber: number,
baseBranch: string,
): string {
return buildPrTitle(functionName, speedupPct) + ` in PR #${pullNumber} (\`${baseBranch}\`)`
}
export function buildPrTitle(functionName: string, speedupPct: string): string {
const type = functionName.includes(".") ? "method" : "function"
return `⚡️ Speed up ${type} \`${functionName}\` by ${speedupPct}`
}