@colrealpro/react-luau-doctor 0.18.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +108 -113
- package/dist/cli.js.map +3 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ var package_default = {
|
|
|
11
11
|
publishConfig: {
|
|
12
12
|
access: "public"
|
|
13
13
|
},
|
|
14
|
-
version: "0.18.
|
|
14
|
+
version: "0.18.1",
|
|
15
15
|
description: "Static analysis for React-Luau hooks, effects, rendering, and performance.",
|
|
16
16
|
license: "MIT",
|
|
17
17
|
type: "module",
|
|
@@ -9904,7 +9904,6 @@ var GITLAB_WORKFLOW = ".gitlab-ci.yml";
|
|
|
9904
9904
|
var SUMMARY_MARKER = "<!-- react-luau-doctor:summary -->";
|
|
9905
9905
|
var REVIEW_MARKER = "<!-- react-luau-doctor:review -->";
|
|
9906
9906
|
var MAX_REVIEW_COMMENTS = 20;
|
|
9907
|
-
var MAX_RESOLVE_THREADS_PER_REQUEST = 100;
|
|
9908
9907
|
var MAX_GITHUB_RETRIES = 2;
|
|
9909
9908
|
var MAX_RATE_LIMIT_WAIT_MS = 60000;
|
|
9910
9909
|
var PACKAGE_SPEC = `${package_default.name}@${package_default.version}`;
|
|
@@ -9920,10 +9919,8 @@ on:
|
|
|
9920
9919
|
branches: [main]
|
|
9921
9920
|
|
|
9922
9921
|
permissions:
|
|
9923
|
-
|
|
9924
|
-
contents: ${settings.reviewComments ? "write" : "read"}
|
|
9922
|
+
contents: read
|
|
9925
9923
|
pull-requests: write
|
|
9926
|
-
issues: write
|
|
9927
9924
|
statuses: write
|
|
9928
9925
|
|
|
9929
9926
|
concurrency:
|
|
@@ -10332,23 +10329,6 @@ async function githubApi(repo, endpoint, options = {}) {
|
|
|
10332
10329
|
const apiBase = (process.env.GITHUB_API_URL ?? "https://api.github.com").replace(/\/$/, "");
|
|
10333
10330
|
return githubRequest(`${apiBase}/repos/${repo}${endpoint}`, options);
|
|
10334
10331
|
}
|
|
10335
|
-
function githubGraphQLUrl() {
|
|
10336
|
-
if (process.env.GITHUB_GRAPHQL_URL)
|
|
10337
|
-
return process.env.GITHUB_GRAPHQL_URL;
|
|
10338
|
-
const apiBase = (process.env.GITHUB_API_URL ?? "https://api.github.com").replace(/\/$/, "");
|
|
10339
|
-
return apiBase.endsWith("/api/v3") ? `${apiBase.slice(0, -"/v3".length)}/graphql` : `${apiBase}/graphql`;
|
|
10340
|
-
}
|
|
10341
|
-
async function githubGraphQL(query, variables) {
|
|
10342
|
-
const response = await githubRequest(githubGraphQLUrl(), {
|
|
10343
|
-
method: "POST",
|
|
10344
|
-
body: { query, variables }
|
|
10345
|
-
});
|
|
10346
|
-
if (response.errors?.length)
|
|
10347
|
-
throw new Error(`GitHub GraphQL: ${response.errors.map((error) => error.message ?? "unknown error").join("; ")}`);
|
|
10348
|
-
if (!response.data)
|
|
10349
|
-
throw new Error("GitHub GraphQL returned no data");
|
|
10350
|
-
return response.data;
|
|
10351
|
-
}
|
|
10352
10332
|
function repoRelativeDiagnosticPath(directory, diagnosticFile) {
|
|
10353
10333
|
const absoluteDirectory = path11.resolve(directory);
|
|
10354
10334
|
const repoRoot = findGitRoot(absoluteDirectory);
|
|
@@ -10458,54 +10438,32 @@ ${diagnostic.message}${diagnostic.help ? `
|
|
|
10458
10438
|
|
|
10459
10439
|
${diagnostic.help}` : ""}`;
|
|
10460
10440
|
}
|
|
10461
|
-
|
|
10462
|
-
|
|
10463
|
-
|
|
10464
|
-
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
|
|
10472
|
-
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
}
|
|
10486
|
-
}
|
|
10487
|
-
pageInfo { hasNextPage endCursor }
|
|
10488
|
-
}
|
|
10489
|
-
}
|
|
10490
|
-
}
|
|
10491
|
-
}
|
|
10492
|
-
`, { owner, name, pull: pullNumber, cursor });
|
|
10493
|
-
const connection = data.repository?.pullRequest?.reviewThreads;
|
|
10494
|
-
if (!connection)
|
|
10495
|
-
throw new Error(`Could not load review threads for pull request ${pullNumber}`);
|
|
10496
|
-
threads.push(...connection.nodes);
|
|
10497
|
-
if (!connection.pageInfo.hasNextPage)
|
|
10498
|
-
return threads;
|
|
10499
|
-
cursor = connection.pageInfo.endCursor;
|
|
10500
|
-
if (!cursor)
|
|
10501
|
-
throw new Error("GitHub review thread pagination returned no cursor");
|
|
10502
|
-
}
|
|
10503
|
-
}
|
|
10504
|
-
function doctorThreadFingerprint(thread) {
|
|
10505
|
-
const comment = thread.comments.nodes[0];
|
|
10506
|
-
if (comment?.author?.__typename !== "Bot" || !comment.body.startsWith(REVIEW_MARKER))
|
|
10441
|
+
var ARCHIVED_REVIEW_BODY = "\u2705 React-Luau Doctor: findings from this review are no longer current. Active findings, if any, are shown in newer reviews.";
|
|
10442
|
+
async function listReviewComments(repo, pullNumber) {
|
|
10443
|
+
const comments = [];
|
|
10444
|
+
for (let page = 1;; page += 1) {
|
|
10445
|
+
const batch = await githubApi(repo, `/pulls/${pullNumber}/comments?per_page=100&page=${page}`);
|
|
10446
|
+
comments.push(...batch);
|
|
10447
|
+
if (batch.length < 100)
|
|
10448
|
+
return comments;
|
|
10449
|
+
}
|
|
10450
|
+
}
|
|
10451
|
+
async function listReviews(repo, pullNumber) {
|
|
10452
|
+
const reviews = [];
|
|
10453
|
+
for (let page = 1;; page += 1) {
|
|
10454
|
+
const batch = await githubApi(repo, `/pulls/${pullNumber}/reviews?per_page=100&page=${page}`);
|
|
10455
|
+
reviews.push(...batch);
|
|
10456
|
+
if (batch.length < 100)
|
|
10457
|
+
return reviews;
|
|
10458
|
+
}
|
|
10459
|
+
}
|
|
10460
|
+
function isDoctorReview(review) {
|
|
10461
|
+
return review.user?.type === "Bot" && (review.body?.includes("React-Luau Doctor") ?? false);
|
|
10462
|
+
}
|
|
10463
|
+
function doctorReviewCommentFingerprint(comment) {
|
|
10464
|
+
if (comment.user?.type !== "Bot" || !comment.body.startsWith(REVIEW_MARKER))
|
|
10507
10465
|
return null;
|
|
10508
|
-
return fingerprintFromReviewBody(comment.body,
|
|
10466
|
+
return fingerprintFromReviewBody(comment.body, comment.path);
|
|
10509
10467
|
}
|
|
10510
10468
|
function takeCount(counts, key) {
|
|
10511
10469
|
const count = counts.get(key) ?? 0;
|
|
@@ -10514,58 +10472,64 @@ function takeCount(counts, key) {
|
|
|
10514
10472
|
counts.set(key, count - 1);
|
|
10515
10473
|
return true;
|
|
10516
10474
|
}
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
const batch = threadIds.slice(offset, offset + MAX_RESOLVE_THREADS_PER_REQUEST);
|
|
10520
|
-
const declarations = batch.map((_, index) => `$thread${index}: ID!`).join(", ");
|
|
10521
|
-
const mutations = batch.map((_, index) => `thread${index}: resolveReviewThread(input: { threadId: $thread${index} }) { thread { id isResolved } }`).join(`
|
|
10522
|
-
`);
|
|
10523
|
-
const variables = Object.fromEntries(batch.map((id, index) => [`thread${index}`, id]));
|
|
10524
|
-
await githubGraphQL(`mutation ResolveDoctorThreads(${declarations}) { ${mutations} }`, variables);
|
|
10525
|
-
if (offset + batch.length < threadIds.length)
|
|
10526
|
-
await wait(1000);
|
|
10527
|
-
}
|
|
10475
|
+
function incrementCount(counts, key) {
|
|
10476
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
10528
10477
|
}
|
|
10529
|
-
async function manageReviewComments(repo, pullNumber, currentReport, newDiagnostics, directory, newBase) {
|
|
10530
|
-
const
|
|
10478
|
+
async function manageReviewComments(repo, pullNumber, currentReport, newDiagnostics, directory, newBase, pullBase) {
|
|
10479
|
+
const [allReviewComments, reviews] = await Promise.all([
|
|
10480
|
+
listReviewComments(repo, pullNumber),
|
|
10481
|
+
listReviews(repo, pullNumber)
|
|
10482
|
+
]);
|
|
10483
|
+
const reviewComments = allReviewComments.map((comment) => ({ comment, fingerprint: doctorReviewCommentFingerprint(comment) })).filter((entry) => entry.fingerprint !== null);
|
|
10531
10484
|
const activeCounts = new Map;
|
|
10532
|
-
for (const diagnostic of currentReport.diagnostics)
|
|
10533
|
-
|
|
10534
|
-
activeCounts.set(fingerprint, (activeCounts.get(fingerprint) ?? 0) + 1);
|
|
10535
|
-
}
|
|
10485
|
+
for (const diagnostic of currentReport.diagnostics)
|
|
10486
|
+
incrementCount(activeCounts, diagnosticReviewFingerprint(directory, diagnostic));
|
|
10536
10487
|
const newCounts = new Map;
|
|
10537
|
-
for (const diagnostic of newDiagnostics)
|
|
10538
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
const
|
|
10542
|
-
for (const
|
|
10543
|
-
if (
|
|
10544
|
-
|
|
10545
|
-
const fingerprint = doctorThreadFingerprint(thread);
|
|
10546
|
-
if (!fingerprint)
|
|
10547
|
-
continue;
|
|
10548
|
-
if (thread.isOutdated && (newCounts.get(fingerprint) ?? 0) > 0) {
|
|
10549
|
-
resolvedThreadIds.push(thread.id);
|
|
10488
|
+
for (const diagnostic of newDiagnostics)
|
|
10489
|
+
incrementCount(newCounts, diagnosticReviewFingerprint(directory, diagnostic));
|
|
10490
|
+
const replacementCounts = new Map;
|
|
10491
|
+
const staleComments = [];
|
|
10492
|
+
const orderedComments = reviewComments.slice().sort((left, right) => Number(left.comment.position === null) - Number(right.comment.position === null));
|
|
10493
|
+
for (const { comment, fingerprint } of orderedComments) {
|
|
10494
|
+
if (comment.position !== null && takeCount(activeCounts, fingerprint)) {
|
|
10495
|
+
takeCount(newCounts, fingerprint);
|
|
10550
10496
|
continue;
|
|
10551
10497
|
}
|
|
10552
|
-
|
|
10498
|
+
const needsReplacement = takeCount(activeCounts, fingerprint);
|
|
10499
|
+
if (needsReplacement) {
|
|
10500
|
+
incrementCount(replacementCounts, fingerprint);
|
|
10553
10501
|
takeCount(newCounts, fingerprint);
|
|
10554
|
-
continue;
|
|
10555
10502
|
}
|
|
10556
|
-
|
|
10503
|
+
staleComments.push({ comment, fingerprint, needsReplacement });
|
|
10504
|
+
}
|
|
10505
|
+
const newLineMap = changedLineMap(directory, newBase);
|
|
10506
|
+
const pullLineMap = newBase === pullBase ? newLineMap : changedLineMap(directory, pullBase);
|
|
10507
|
+
const candidates = [];
|
|
10508
|
+
for (const diagnostic of newDiagnostics) {
|
|
10509
|
+
if (!touchesChangedLine(diagnostic, newLineMap.get(diagnostic.file) ?? []))
|
|
10510
|
+
continue;
|
|
10511
|
+
const fingerprint = diagnosticReviewFingerprint(directory, diagnostic);
|
|
10512
|
+
if (!takeCount(newCounts, fingerprint))
|
|
10513
|
+
continue;
|
|
10514
|
+
candidates.push({ diagnostic, replacement: false });
|
|
10557
10515
|
}
|
|
10558
|
-
const
|
|
10559
|
-
|
|
10516
|
+
for (const diagnostic of currentReport.diagnostics) {
|
|
10517
|
+
if (!touchesChangedLine(diagnostic, pullLineMap.get(diagnostic.file) ?? []))
|
|
10518
|
+
continue;
|
|
10560
10519
|
const fingerprint = diagnosticReviewFingerprint(directory, diagnostic);
|
|
10561
|
-
|
|
10562
|
-
|
|
10520
|
+
if (!takeCount(replacementCounts, fingerprint))
|
|
10521
|
+
continue;
|
|
10522
|
+
candidates.push({ diagnostic, replacement: true });
|
|
10523
|
+
}
|
|
10524
|
+
const selected = candidates.slice(0, MAX_REVIEW_COMMENTS);
|
|
10525
|
+
const comments = selected.map(({ diagnostic }) => ({
|
|
10563
10526
|
path: repoRelativeDiagnosticPath(directory, diagnostic.file),
|
|
10564
10527
|
line: diagnostic.location.line,
|
|
10565
10528
|
side: "RIGHT",
|
|
10566
10529
|
body: reviewCommentBody(directory, diagnostic)
|
|
10567
10530
|
}));
|
|
10568
10531
|
const failures = [];
|
|
10532
|
+
let postedNewComments = true;
|
|
10569
10533
|
if (comments.length > 0) {
|
|
10570
10534
|
try {
|
|
10571
10535
|
await githubApi(repo, `/pulls/${pullNumber}/reviews`, {
|
|
@@ -10573,14 +10537,45 @@ async function manageReviewComments(repo, pullNumber, currentReport, newDiagnost
|
|
|
10573
10537
|
body: { event: "COMMENT", body: "React-Luau Doctor found new issues", comments }
|
|
10574
10538
|
});
|
|
10575
10539
|
} catch (error) {
|
|
10540
|
+
postedNewComments = false;
|
|
10576
10541
|
failures.push(`could not create new review comments: ${errorMessage(error)}`);
|
|
10577
10542
|
}
|
|
10578
10543
|
}
|
|
10579
|
-
if (
|
|
10580
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10544
|
+
if (postedNewComments) {
|
|
10545
|
+
const postedReplacementCounts = new Map;
|
|
10546
|
+
for (const candidate of selected) {
|
|
10547
|
+
if (candidate.replacement)
|
|
10548
|
+
incrementCount(postedReplacementCounts, diagnosticReviewFingerprint(directory, candidate.diagnostic));
|
|
10549
|
+
}
|
|
10550
|
+
const remainingByReview = new Map;
|
|
10551
|
+
for (const { comment } of reviewComments) {
|
|
10552
|
+
remainingByReview.set(comment.pull_request_review_id, (remainingByReview.get(comment.pull_request_review_id) ?? 0) + 1);
|
|
10553
|
+
}
|
|
10554
|
+
for (const stale of staleComments) {
|
|
10555
|
+
if (stale.needsReplacement && !takeCount(postedReplacementCounts, stale.fingerprint))
|
|
10556
|
+
continue;
|
|
10557
|
+
try {
|
|
10558
|
+
await githubApi(repo, `/pulls/comments/${stale.comment.id}`, { method: "DELETE" });
|
|
10559
|
+
const reviewId = stale.comment.pull_request_review_id;
|
|
10560
|
+
const remaining = Math.max(0, (remainingByReview.get(reviewId) ?? 1) - 1);
|
|
10561
|
+
remainingByReview.set(reviewId, remaining);
|
|
10562
|
+
} catch (error) {
|
|
10563
|
+
failures.push(`could not delete stale review comment ${stale.comment.id}: ${errorMessage(error)}`);
|
|
10564
|
+
}
|
|
10565
|
+
}
|
|
10566
|
+
for (const review of reviews) {
|
|
10567
|
+
if (!isDoctorReview(review) || review.body === ARCHIVED_REVIEW_BODY)
|
|
10568
|
+
continue;
|
|
10569
|
+
if ((remainingByReview.get(review.id) ?? 0) > 0)
|
|
10570
|
+
continue;
|
|
10571
|
+
try {
|
|
10572
|
+
await githubApi(repo, `/pulls/${pullNumber}/reviews/${review.id}`, {
|
|
10573
|
+
method: "PUT",
|
|
10574
|
+
body: { body: ARCHIVED_REVIEW_BODY }
|
|
10575
|
+
});
|
|
10576
|
+
} catch (error) {
|
|
10577
|
+
failures.push(`could not archive empty review ${review.id}: ${errorMessage(error)}`);
|
|
10578
|
+
}
|
|
10584
10579
|
}
|
|
10585
10580
|
}
|
|
10586
10581
|
if (failures.length > 0)
|
|
@@ -10672,7 +10667,7 @@ async function runCiJob(argv) {
|
|
|
10672
10667
|
}
|
|
10673
10668
|
if (settings.reviewComments && base) {
|
|
10674
10669
|
try {
|
|
10675
|
-
await manageReviewComments(repo, pullNumber, report, reviewChanges.diagnostics, path11.resolve(settings.directory), reviewChanges.base ?? base);
|
|
10670
|
+
await manageReviewComments(repo, pullNumber, report, reviewChanges.diagnostics, path11.resolve(settings.directory), reviewChanges.base ?? base, base);
|
|
10676
10671
|
} catch (error) {
|
|
10677
10672
|
process.stderr.write(`react-luau-doctor: could not update inline review comments: ${errorMessage(error)}
|
|
10678
10673
|
`);
|
|
@@ -12479,5 +12474,5 @@ ${os2.release()}
|
|
|
12479
12474
|
}
|
|
12480
12475
|
main();
|
|
12481
12476
|
|
|
12482
|
-
//# debugId=
|
|
12477
|
+
//# debugId=93BE5E0B263571E264756E2164756E21
|
|
12483
12478
|
//# sourceMappingURL=cli.js.map
|