@colrealpro/react-luau-doctor 0.18.0 → 0.18.2
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 +420 -147
- package/dist/cli.js.map +7 -5
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
|
-
import
|
|
5
|
+
import fs11 from "fs";
|
|
6
6
|
import os2 from "os";
|
|
7
|
-
import
|
|
7
|
+
import path13 from "path";
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "@colrealpro/react-luau-doctor",
|
|
11
11
|
publishConfig: {
|
|
12
12
|
access: "public"
|
|
13
13
|
},
|
|
14
|
-
version: "0.18.
|
|
14
|
+
version: "0.18.2",
|
|
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
|
`);
|
|
@@ -10703,6 +10698,210 @@ async function runCiCommand(argv) {
|
|
|
10703
10698
|
throw new Error("ci requires install, config, or upgrade");
|
|
10704
10699
|
}
|
|
10705
10700
|
|
|
10701
|
+
// src/update-check.ts
|
|
10702
|
+
import { spawn } from "child_process";
|
|
10703
|
+
import fs10 from "fs";
|
|
10704
|
+
import path12 from "path";
|
|
10705
|
+
var UPDATE_CHECK_INTERVAL_MS = 2 * 60 * 60 * 1000;
|
|
10706
|
+
var UPDATE_REQUEST_TIMEOUT_MS = 5000;
|
|
10707
|
+
var UPDATE_CACHE_FILENAME = "update-check.json";
|
|
10708
|
+
function updateCacheFilename() {
|
|
10709
|
+
return path12.join(cacheBaseDirectory(), UPDATE_CACHE_FILENAME);
|
|
10710
|
+
}
|
|
10711
|
+
function readUpdateCache() {
|
|
10712
|
+
try {
|
|
10713
|
+
const parsed = JSON.parse(fs10.readFileSync(updateCacheFilename(), "utf8"));
|
|
10714
|
+
if (typeof parsed.checkedAt !== "number" || !Number.isFinite(parsed.checkedAt))
|
|
10715
|
+
return null;
|
|
10716
|
+
if (parsed.latest !== undefined && typeof parsed.latest !== "string")
|
|
10717
|
+
return null;
|
|
10718
|
+
return {
|
|
10719
|
+
checkedAt: parsed.checkedAt,
|
|
10720
|
+
latest: parsed.latest
|
|
10721
|
+
};
|
|
10722
|
+
} catch {
|
|
10723
|
+
return null;
|
|
10724
|
+
}
|
|
10725
|
+
}
|
|
10726
|
+
function writeUpdateCache(cache) {
|
|
10727
|
+
try {
|
|
10728
|
+
const filename = updateCacheFilename();
|
|
10729
|
+
fs10.mkdirSync(path12.dirname(filename), { recursive: true });
|
|
10730
|
+
fs10.writeFileSync(filename, `${JSON.stringify(cache)}
|
|
10731
|
+
`);
|
|
10732
|
+
} catch {}
|
|
10733
|
+
}
|
|
10734
|
+
function parseVersion(value) {
|
|
10735
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value.trim());
|
|
10736
|
+
if (!match)
|
|
10737
|
+
return null;
|
|
10738
|
+
return {
|
|
10739
|
+
major: Number(match[1]),
|
|
10740
|
+
minor: Number(match[2]),
|
|
10741
|
+
patch: Number(match[3]),
|
|
10742
|
+
prerelease: match[4]?.split(".") ?? []
|
|
10743
|
+
};
|
|
10744
|
+
}
|
|
10745
|
+
function comparePrerelease(left, right) {
|
|
10746
|
+
if (left.length === 0 || right.length === 0) {
|
|
10747
|
+
if (left.length === right.length)
|
|
10748
|
+
return 0;
|
|
10749
|
+
return left.length === 0 ? 1 : -1;
|
|
10750
|
+
}
|
|
10751
|
+
const count = Math.max(left.length, right.length);
|
|
10752
|
+
for (let index = 0;index < count; index += 1) {
|
|
10753
|
+
const leftPart = left[index];
|
|
10754
|
+
const rightPart = right[index];
|
|
10755
|
+
if (leftPart === undefined)
|
|
10756
|
+
return -1;
|
|
10757
|
+
if (rightPart === undefined)
|
|
10758
|
+
return 1;
|
|
10759
|
+
if (leftPart === rightPart)
|
|
10760
|
+
continue;
|
|
10761
|
+
const leftNumber = /^\d+$/.test(leftPart) ? Number(leftPart) : null;
|
|
10762
|
+
const rightNumber = /^\d+$/.test(rightPart) ? Number(rightPart) : null;
|
|
10763
|
+
if (leftNumber !== null && rightNumber !== null)
|
|
10764
|
+
return leftNumber < rightNumber ? -1 : 1;
|
|
10765
|
+
if (leftNumber !== null)
|
|
10766
|
+
return -1;
|
|
10767
|
+
if (rightNumber !== null)
|
|
10768
|
+
return 1;
|
|
10769
|
+
return leftPart < rightPart ? -1 : 1;
|
|
10770
|
+
}
|
|
10771
|
+
return 0;
|
|
10772
|
+
}
|
|
10773
|
+
function compareVersions(left, right) {
|
|
10774
|
+
const a = parseVersion(left);
|
|
10775
|
+
const b = parseVersion(right);
|
|
10776
|
+
if (!a || !b)
|
|
10777
|
+
return null;
|
|
10778
|
+
for (const key of ["major", "minor", "patch"]) {
|
|
10779
|
+
if (a[key] !== b[key])
|
|
10780
|
+
return a[key] < b[key] ? -1 : 1;
|
|
10781
|
+
}
|
|
10782
|
+
return comparePrerelease(a.prerelease, b.prerelease);
|
|
10783
|
+
}
|
|
10784
|
+
function updateRegistryUrl() {
|
|
10785
|
+
const registry = process.env.REACT_LUAU_DOCTOR_UPDATE_REGISTRY ?? process.env.npm_config_registry ?? process.env.NPM_CONFIG_REGISTRY ?? "https://registry.npmjs.org/";
|
|
10786
|
+
const base = registry.endsWith("/") ? registry : `${registry}/`;
|
|
10787
|
+
return new URL(`${encodeURIComponent(package_default.name)}/latest`, base).toString();
|
|
10788
|
+
}
|
|
10789
|
+
async function fetchLatestVersion() {
|
|
10790
|
+
const controller = new AbortController;
|
|
10791
|
+
const timer = setTimeout(() => controller.abort(), UPDATE_REQUEST_TIMEOUT_MS);
|
|
10792
|
+
timer.unref?.();
|
|
10793
|
+
try {
|
|
10794
|
+
const response = await fetch(updateRegistryUrl(), {
|
|
10795
|
+
headers: { accept: "application/json" },
|
|
10796
|
+
signal: controller.signal
|
|
10797
|
+
});
|
|
10798
|
+
if (!response.ok)
|
|
10799
|
+
throw new Error(`npm registry returned HTTP ${response.status}`);
|
|
10800
|
+
const body = await response.json();
|
|
10801
|
+
if (typeof body.version !== "string" || compareVersions(body.version, body.version) === null) {
|
|
10802
|
+
throw new Error("npm registry returned an invalid package version");
|
|
10803
|
+
}
|
|
10804
|
+
return body.version;
|
|
10805
|
+
} finally {
|
|
10806
|
+
clearTimeout(timer);
|
|
10807
|
+
}
|
|
10808
|
+
}
|
|
10809
|
+
function updateCacheIsStale(now = Date.now()) {
|
|
10810
|
+
const cache = readUpdateCache();
|
|
10811
|
+
return !cache || now - cache.checkedAt >= UPDATE_CHECK_INTERVAL_MS;
|
|
10812
|
+
}
|
|
10813
|
+
async function refreshUpdateCache(options = {}) {
|
|
10814
|
+
const now = options.now ?? Date.now();
|
|
10815
|
+
try {
|
|
10816
|
+
const latest = await fetchLatestVersion();
|
|
10817
|
+
writeUpdateCache({ checkedAt: now, latest });
|
|
10818
|
+
return latest;
|
|
10819
|
+
} catch (error) {
|
|
10820
|
+
const previous = readUpdateCache();
|
|
10821
|
+
writeUpdateCache({ checkedAt: now, latest: previous?.latest });
|
|
10822
|
+
if (options.silent)
|
|
10823
|
+
return null;
|
|
10824
|
+
throw error;
|
|
10825
|
+
}
|
|
10826
|
+
}
|
|
10827
|
+
function startBackgroundUpdateRefresh() {
|
|
10828
|
+
if (!updateCacheIsStale())
|
|
10829
|
+
return;
|
|
10830
|
+
const script = process.argv[1];
|
|
10831
|
+
if (!script)
|
|
10832
|
+
return;
|
|
10833
|
+
try {
|
|
10834
|
+
const child2 = spawn(process.execPath, [script, "__update-cache"], {
|
|
10835
|
+
detached: true,
|
|
10836
|
+
stdio: "ignore",
|
|
10837
|
+
windowsHide: true,
|
|
10838
|
+
env: process.env
|
|
10839
|
+
});
|
|
10840
|
+
child2.unref();
|
|
10841
|
+
} catch {}
|
|
10842
|
+
}
|
|
10843
|
+
function getCachedUpdateNotice(currentVersion) {
|
|
10844
|
+
const cache = readUpdateCache();
|
|
10845
|
+
if (!cache?.latest)
|
|
10846
|
+
return null;
|
|
10847
|
+
const comparison = compareVersions(cache.latest, currentVersion);
|
|
10848
|
+
if (comparison === null || comparison <= 0)
|
|
10849
|
+
return null;
|
|
10850
|
+
return { current: currentVersion, latest: cache.latest };
|
|
10851
|
+
}
|
|
10852
|
+
async function checkForUpdatesNow(currentVersion) {
|
|
10853
|
+
const latest = await refreshUpdateCache();
|
|
10854
|
+
if (!latest)
|
|
10855
|
+
throw new Error("Could not check npm for updates");
|
|
10856
|
+
const comparison = compareVersions(latest, currentVersion);
|
|
10857
|
+
if (comparison === null)
|
|
10858
|
+
throw new Error(`Could not compare installed version ${currentVersion} with ${latest}`);
|
|
10859
|
+
return { latest, updateAvailable: comparison > 0 };
|
|
10860
|
+
}
|
|
10861
|
+
|
|
10862
|
+
// src/update-install.ts
|
|
10863
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
10864
|
+
import { fileURLToPath } from "url";
|
|
10865
|
+
function normalizedPath(filename) {
|
|
10866
|
+
return filename.replaceAll("\\", "/");
|
|
10867
|
+
}
|
|
10868
|
+
function updateInstallCommandForPath(filename) {
|
|
10869
|
+
const normalized = normalizedPath(filename);
|
|
10870
|
+
const spec = `${package_default.name}@latest`;
|
|
10871
|
+
if (normalized.includes("/.bun/install/global/node_modules/")) {
|
|
10872
|
+
return {
|
|
10873
|
+
manager: "bun",
|
|
10874
|
+
command: "bun",
|
|
10875
|
+
args: ["add", "-g", spec],
|
|
10876
|
+
display: `bun add -g ${spec}`
|
|
10877
|
+
};
|
|
10878
|
+
}
|
|
10879
|
+
if (normalized.includes("/.bun/install/cache/") || normalized.includes("/.npm/_npx/"))
|
|
10880
|
+
return null;
|
|
10881
|
+
if (normalized.includes("/node_modules/")) {
|
|
10882
|
+
return {
|
|
10883
|
+
manager: "npm",
|
|
10884
|
+
command: "npm",
|
|
10885
|
+
args: ["install", "-g", spec],
|
|
10886
|
+
display: `npm install -g ${spec}`
|
|
10887
|
+
};
|
|
10888
|
+
}
|
|
10889
|
+
return null;
|
|
10890
|
+
}
|
|
10891
|
+
function currentUpdateInstallCommand() {
|
|
10892
|
+
return updateInstallCommandForPath(fileURLToPath(import.meta.url));
|
|
10893
|
+
}
|
|
10894
|
+
function installLatestVersion(command) {
|
|
10895
|
+
const result = spawnSync3(command.command, command.args, {
|
|
10896
|
+
stdio: "inherit",
|
|
10897
|
+
windowsHide: true
|
|
10898
|
+
});
|
|
10899
|
+
if (result.error)
|
|
10900
|
+
throw result.error;
|
|
10901
|
+
if (result.status !== 0)
|
|
10902
|
+
throw new Error(`${command.display} exited with code ${result.status ?? "unknown"}`);
|
|
10903
|
+
}
|
|
10904
|
+
|
|
10706
10905
|
// src/fix-examples.ts
|
|
10707
10906
|
var examples = {
|
|
10708
10907
|
"react-luau/parse-error": {
|
|
@@ -11189,6 +11388,7 @@ Usage:
|
|
|
11189
11388
|
react-luau-doctor [directory] [options]
|
|
11190
11389
|
react-luau-doctor ci <install|config|upgrade>
|
|
11191
11390
|
react-luau-doctor why <file:line>
|
|
11391
|
+
react-luau-doctor update [--check]
|
|
11192
11392
|
react-luau-doctor rules <command>
|
|
11193
11393
|
|
|
11194
11394
|
Scan options:
|
|
@@ -11217,6 +11417,7 @@ Scan options:
|
|
|
11217
11417
|
--no-color Disable automatic ANSI colors
|
|
11218
11418
|
--no-cache Disable the persistent OS-level analysis cache
|
|
11219
11419
|
--no-parallel Disable parallel file analysis
|
|
11420
|
+
--no-update-check Disable the automatic update notice
|
|
11220
11421
|
|
|
11221
11422
|
React-Luau Doctor options:
|
|
11222
11423
|
--min-severity <level> suggestion, warning, or error
|
|
@@ -11229,6 +11430,10 @@ CI commands:
|
|
|
11229
11430
|
ci upgrade [--provider github|gitlab] [--pr] [-y] [--cwd <cwd>]
|
|
11230
11431
|
Reporting toggles: --comment/--no-comment, --review-comments/--no-review-comments, --commit-status/--no-commit-status
|
|
11231
11432
|
|
|
11433
|
+
Update commands:
|
|
11434
|
+
update Update the global installation to the latest release
|
|
11435
|
+
update --check Check npm for a newer release without updating
|
|
11436
|
+
|
|
11232
11437
|
Rules commands:
|
|
11233
11438
|
rules list [--category <name>] [--configured] [--json]
|
|
11234
11439
|
rules explain <rule> [--json]
|
|
@@ -11241,6 +11446,53 @@ Config:
|
|
|
11241
11446
|
react-luau-doctor.config.json
|
|
11242
11447
|
`;
|
|
11243
11448
|
}
|
|
11449
|
+
function automaticUpdateNoticeEnabled(options, machineReadable) {
|
|
11450
|
+
return !options.noUpdateCheck && !machineReadable && Boolean(process.stdout.isTTY) && !process.env.CI && process.env.NO_UPDATE_NOTIFIER === undefined && process.env.REACT_LUAU_DOCTOR_NO_UPDATE_CHECK === undefined;
|
|
11451
|
+
}
|
|
11452
|
+
function renderUpdateNotice(current, latest, colorized) {
|
|
11453
|
+
const label = whyPaint(colorized, "Update available:", WHY_ANSI.bold, WHY_ANSI.yellow);
|
|
11454
|
+
const oldVersion = whyPaint(colorized, `v${current}`, WHY_ANSI.dim);
|
|
11455
|
+
const newVersion = whyPaint(colorized, `v${latest}`, WHY_ANSI.bold);
|
|
11456
|
+
return `${label} ${oldVersion} \u2192 ${newVersion}
|
|
11457
|
+
Run \`react-luau-doctor update\` to update.`;
|
|
11458
|
+
}
|
|
11459
|
+
async function runUpdateCommand(argv) {
|
|
11460
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
11461
|
+
process.stdout.write(`Usage: react-luau-doctor update [--check]
|
|
11462
|
+
`);
|
|
11463
|
+
return;
|
|
11464
|
+
}
|
|
11465
|
+
if (argv.length > 1 || argv.length === 1 && argv[0] !== "--check") {
|
|
11466
|
+
throw new Error("Usage: react-luau-doctor update [--check]");
|
|
11467
|
+
}
|
|
11468
|
+
const result = await checkForUpdatesNow(VERSION2);
|
|
11469
|
+
if (!result.updateAvailable) {
|
|
11470
|
+
process.stdout.write(`React-Luau Doctor v${VERSION2} is up to date.
|
|
11471
|
+
`);
|
|
11472
|
+
return;
|
|
11473
|
+
}
|
|
11474
|
+
const colorized = shouldUseColor(false, false);
|
|
11475
|
+
if (argv[0] === "--check") {
|
|
11476
|
+
process.stdout.write(`${renderUpdateNotice(VERSION2, result.latest, colorized)}
|
|
11477
|
+
`);
|
|
11478
|
+
return;
|
|
11479
|
+
}
|
|
11480
|
+
const command = currentUpdateInstallCommand();
|
|
11481
|
+
if (!command) {
|
|
11482
|
+
throw new Error(`Could not determine the global package manager for this installation. Run \`npm install -g ${package_default.name}@latest\` manually.`);
|
|
11483
|
+
}
|
|
11484
|
+
const label = whyPaint(colorized, "Updating React-Luau Doctor:", WHY_ANSI.bold, WHY_ANSI.yellow);
|
|
11485
|
+
const oldVersion = whyPaint(colorized, `v${VERSION2}`, WHY_ANSI.dim);
|
|
11486
|
+
const newVersion = whyPaint(colorized, `v${result.latest}`, WHY_ANSI.bold);
|
|
11487
|
+
process.stdout.write(`${label} ${oldVersion} \u2192 ${newVersion}
|
|
11488
|
+
Using \`${command.display}\`
|
|
11489
|
+
|
|
11490
|
+
`);
|
|
11491
|
+
installLatestVersion(command);
|
|
11492
|
+
process.stdout.write(`
|
|
11493
|
+
Updated React-Luau Doctor to v${result.latest}.
|
|
11494
|
+
`);
|
|
11495
|
+
}
|
|
11244
11496
|
function splitLongOption(arg) {
|
|
11245
11497
|
if (!arg.startsWith("--"))
|
|
11246
11498
|
return { name: arg };
|
|
@@ -11646,6 +11898,7 @@ function parseArgs(argv) {
|
|
|
11646
11898
|
noColor: false,
|
|
11647
11899
|
noCache: false,
|
|
11648
11900
|
noParallel: false,
|
|
11901
|
+
noUpdateCheck: false,
|
|
11649
11902
|
help: false,
|
|
11650
11903
|
version: false
|
|
11651
11904
|
};
|
|
@@ -11683,6 +11936,8 @@ function parseArgs(argv) {
|
|
|
11683
11936
|
options.noCache = true;
|
|
11684
11937
|
else if (arg === "--no-parallel")
|
|
11685
11938
|
options.noParallel = true;
|
|
11939
|
+
else if (arg === "--no-update-check")
|
|
11940
|
+
options.noUpdateCheck = true;
|
|
11686
11941
|
else if (arg === "--annotations")
|
|
11687
11942
|
options.annotations = true;
|
|
11688
11943
|
else if (arg === "--help" || arg === "-h")
|
|
@@ -11800,8 +12055,8 @@ function validateModeFlags(options, scope) {
|
|
|
11800
12055
|
throw new Error("--annotations cannot be combined with --json or --score");
|
|
11801
12056
|
}
|
|
11802
12057
|
function findProjectByName(root, name) {
|
|
11803
|
-
const direct =
|
|
11804
|
-
if (
|
|
12058
|
+
const direct = path13.resolve(root, name);
|
|
12059
|
+
if (fs11.existsSync(direct) && fs11.statSync(direct).isDirectory())
|
|
11805
12060
|
return direct;
|
|
11806
12061
|
const ignored = new Set([".git", "node_modules", "Packages", "DevPackages", "ServerPackages", "dist", "vendor"]);
|
|
11807
12062
|
const queue = [{ directory: root, depth: 0 }];
|
|
@@ -11810,17 +12065,17 @@ function findProjectByName(root, name) {
|
|
|
11810
12065
|
const current = queue.shift();
|
|
11811
12066
|
if (current.depth >= 3)
|
|
11812
12067
|
continue;
|
|
11813
|
-
for (const entry of
|
|
12068
|
+
for (const entry of fs11.readdirSync(current.directory, { withFileTypes: true })) {
|
|
11814
12069
|
if (!entry.isDirectory() || ignored.has(entry.name))
|
|
11815
12070
|
continue;
|
|
11816
|
-
const absolute =
|
|
12071
|
+
const absolute = path13.join(current.directory, entry.name);
|
|
11817
12072
|
if (entry.name === name)
|
|
11818
12073
|
matches.push(absolute);
|
|
11819
12074
|
queue.push({ directory: absolute, depth: current.depth + 1 });
|
|
11820
12075
|
}
|
|
11821
12076
|
}
|
|
11822
12077
|
if (matches.length > 1)
|
|
11823
|
-
throw new Error(`Project selector "${name}" is ambiguous: ${matches.map((match) =>
|
|
12078
|
+
throw new Error(`Project selector "${name}" is ambiguous: ${matches.map((match) => path13.relative(root, match)).join(", ")}`);
|
|
11824
12079
|
return matches[0] ?? null;
|
|
11825
12080
|
}
|
|
11826
12081
|
function resolveProjectRoots(root, projectFlag, config) {
|
|
@@ -11838,15 +12093,15 @@ function serializeReport(report, compact) {
|
|
|
11838
12093
|
return compact ? JSON.stringify(report) : JSON.stringify(report, null, 2);
|
|
11839
12094
|
}
|
|
11840
12095
|
function writeJsonFile(filename, value, compact = false) {
|
|
11841
|
-
|
|
11842
|
-
|
|
12096
|
+
fs11.mkdirSync(path13.dirname(filename), { recursive: true });
|
|
12097
|
+
fs11.writeFileSync(filename, `${compact ? JSON.stringify(value) : JSON.stringify(value, null, 2)}
|
|
11843
12098
|
`);
|
|
11844
12099
|
}
|
|
11845
12100
|
function writeDiagnosticsDump(directory, report) {
|
|
11846
|
-
|
|
11847
|
-
writeJsonFile(
|
|
11848
|
-
writeJsonFile(
|
|
11849
|
-
writeJsonFile(
|
|
12101
|
+
fs11.mkdirSync(directory, { recursive: true });
|
|
12102
|
+
writeJsonFile(path13.join(directory, "report.json"), report);
|
|
12103
|
+
writeJsonFile(path13.join(directory, "diagnostics.json"), report.diagnostics);
|
|
12104
|
+
writeJsonFile(path13.join(directory, "summary.json"), {
|
|
11850
12105
|
schemaVersion: report.schemaVersion,
|
|
11851
12106
|
root: report.root,
|
|
11852
12107
|
scope: report.scope ?? "full",
|
|
@@ -11882,7 +12137,7 @@ function parseCommandCwd(argv) {
|
|
|
11882
12137
|
const value = argv[++index];
|
|
11883
12138
|
if (!value)
|
|
11884
12139
|
throw new Error(`${arg} requires a path`);
|
|
11885
|
-
cwd =
|
|
12140
|
+
cwd = path13.resolve(value);
|
|
11886
12141
|
} else
|
|
11887
12142
|
remaining.push(arg);
|
|
11888
12143
|
}
|
|
@@ -11991,7 +12246,7 @@ function runRulesCommand(argv) {
|
|
|
11991
12246
|
if (!severity)
|
|
11992
12247
|
throw new Error("Rule severity must be off, suggestion, warning/warn, or error");
|
|
11993
12248
|
const filename = writeConfig(cwd, (current) => ({ ...current, rules: { ...current.rules, [rule.id]: severity } }));
|
|
11994
|
-
process.stdout.write(`Set ${rule.id} to ${severity} in ${
|
|
12249
|
+
process.stdout.write(`Set ${rule.id} to ${severity} in ${path13.relative(cwd, filename)}
|
|
11995
12250
|
`);
|
|
11996
12251
|
return;
|
|
11997
12252
|
}
|
|
@@ -12010,7 +12265,7 @@ function runRulesCommand(argv) {
|
|
|
12010
12265
|
severity = normalized;
|
|
12011
12266
|
}
|
|
12012
12267
|
const filename = writeConfig(cwd, (current) => ({ ...current, rules: { ...current.rules, [rule.id]: severity } }));
|
|
12013
|
-
process.stdout.write(`Enabled ${rule.id} at ${severity} in ${
|
|
12268
|
+
process.stdout.write(`Enabled ${rule.id} at ${severity} in ${path13.relative(cwd, filename)}
|
|
12014
12269
|
`);
|
|
12015
12270
|
return;
|
|
12016
12271
|
}
|
|
@@ -12020,7 +12275,7 @@ function runRulesCommand(argv) {
|
|
|
12020
12275
|
throw new Error("rules disable requires a rule id");
|
|
12021
12276
|
const rule = findRule(requested);
|
|
12022
12277
|
const filename = writeConfig(cwd, (current) => ({ ...current, rules: { ...current.rules, [rule.id]: "off" } }));
|
|
12023
|
-
process.stdout.write(`Disabled ${rule.id} in ${
|
|
12278
|
+
process.stdout.write(`Disabled ${rule.id} in ${path13.relative(cwd, filename)}
|
|
12024
12279
|
`);
|
|
12025
12280
|
return;
|
|
12026
12281
|
}
|
|
@@ -12043,7 +12298,7 @@ function runRulesCommand(argv) {
|
|
|
12043
12298
|
...categoryRules.map((rule) => [rule.id, severity])
|
|
12044
12299
|
])
|
|
12045
12300
|
}));
|
|
12046
|
-
process.stdout.write(`Set ${categoryRules.length} ${category} rules to ${severity} in ${
|
|
12301
|
+
process.stdout.write(`Set ${categoryRules.length} ${category} rules to ${severity} in ${path13.relative(cwd, filename)}
|
|
12047
12302
|
`);
|
|
12048
12303
|
return;
|
|
12049
12304
|
}
|
|
@@ -12127,7 +12382,7 @@ function whyCaretForLine(sourceLine, line, ranges) {
|
|
|
12127
12382
|
return value;
|
|
12128
12383
|
}
|
|
12129
12384
|
function renderWhyCodeFrame(filename, diagnostic, colorized) {
|
|
12130
|
-
const source =
|
|
12385
|
+
const source = fs11.readFileSync(filename, "utf8").split(/\r?\n/);
|
|
12131
12386
|
const ranges = whyDiagnosticRanges(diagnostic);
|
|
12132
12387
|
const intervals = whyFrameIntervals(ranges, source.length);
|
|
12133
12388
|
const width = String(Math.max(...intervals.map((interval) => interval.end), 1)).length;
|
|
@@ -12235,13 +12490,13 @@ async function runWhy(location, cwd, noColor = false, cache = true, onProgress,
|
|
|
12235
12490
|
const match = location.match(/^(.*):(\d+)(?::(\d+))?$/);
|
|
12236
12491
|
if (!match)
|
|
12237
12492
|
throw new Error("Location must be file:line or file:line:column");
|
|
12238
|
-
const filename =
|
|
12493
|
+
const filename = path13.resolve(cwd, match[1]);
|
|
12239
12494
|
const line = Number(match[2]);
|
|
12240
12495
|
const column = match[3] === undefined ? undefined : Number(match[3]);
|
|
12241
|
-
if (!
|
|
12496
|
+
if (!fs11.existsSync(filename))
|
|
12242
12497
|
throw new Error(`File does not exist: ${match[1]}`);
|
|
12243
12498
|
const colorized = shouldUseColor(noColor, false);
|
|
12244
|
-
const source =
|
|
12499
|
+
const source = fs11.readFileSync(filename, "utf8");
|
|
12245
12500
|
const isSuppressed = createInlineSuppressionChecker(source);
|
|
12246
12501
|
const auditReport = await scanWhyFile(filename, cwd, cache, onProgress);
|
|
12247
12502
|
beforeOutput?.();
|
|
@@ -12282,16 +12537,16 @@ async function runWhy(location, cwd, noColor = false, cache = true, onProgress,
|
|
|
12282
12537
|
}
|
|
12283
12538
|
}
|
|
12284
12539
|
function pathIsInside(parent, child2) {
|
|
12285
|
-
const relative =
|
|
12286
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
12540
|
+
const relative = path13.relative(parent, child2);
|
|
12541
|
+
return relative === "" || !relative.startsWith("..") && !path13.isAbsolute(relative);
|
|
12287
12542
|
}
|
|
12288
12543
|
async function runScan(options, onProgress) {
|
|
12289
12544
|
const commandRoot = process.cwd();
|
|
12290
|
-
const target =
|
|
12291
|
-
if (!
|
|
12545
|
+
const target = path13.resolve(commandRoot, options.target);
|
|
12546
|
+
if (!fs11.existsSync(target))
|
|
12292
12547
|
throw new Error(`Scan path does not exist: ${options.target}`);
|
|
12293
|
-
const targetStat =
|
|
12294
|
-
const scanRoot = targetStat.isFile() ?
|
|
12548
|
+
const targetStat = fs11.statSync(target);
|
|
12549
|
+
const scanRoot = targetStat.isFile() ? path13.dirname(target) : target;
|
|
12295
12550
|
const loaded = loadConfigWithSource(commandRoot);
|
|
12296
12551
|
const config = loaded.config;
|
|
12297
12552
|
const resolvedScope = resolveScope(options, config);
|
|
@@ -12321,7 +12576,7 @@ async function runScan(options, onProgress) {
|
|
|
12321
12576
|
const reports = [];
|
|
12322
12577
|
for (const { projectRoot, targetRoot } of projectTargets) {
|
|
12323
12578
|
let report2;
|
|
12324
|
-
const projectName = projectTargets.length > 1 ?
|
|
12579
|
+
const projectName = projectTargets.length > 1 ? path13.relative(displayRoot, projectRoot) || "." : undefined;
|
|
12325
12580
|
const projectProgress = onProgress ? (progress) => onProgress({
|
|
12326
12581
|
...progress,
|
|
12327
12582
|
phase: [projectName, progress.phase].filter(Boolean).join(":") || undefined,
|
|
@@ -12368,7 +12623,7 @@ async function runScan(options, onProgress) {
|
|
|
12368
12623
|
`[debug] target=${target}`,
|
|
12369
12624
|
`[debug] config=${loaded.filename ?? "none"}`,
|
|
12370
12625
|
`[debug] scope=${report.scope ?? scope} base=${report.base ?? resolvedScope.base ?? "auto"}`,
|
|
12371
|
-
`[debug] projects=${projectTargets.map(({ projectRoot }) =>
|
|
12626
|
+
`[debug] projects=${projectTargets.map(({ projectRoot }) => path13.relative(displayRoot, projectRoot) || ".").join(",")}`,
|
|
12372
12627
|
`[debug] candidates=${report.candidateFiles ?? 0} scanned=${report.scannedFiles} partial=${Boolean(report.partial)}`,
|
|
12373
12628
|
`[debug] parallel=${!options.noParallel}`
|
|
12374
12629
|
];
|
|
@@ -12381,6 +12636,14 @@ async function runScan(options, onProgress) {
|
|
|
12381
12636
|
async function main() {
|
|
12382
12637
|
try {
|
|
12383
12638
|
const argv = process.argv.slice(2);
|
|
12639
|
+
if (argv[0] === "__update-cache") {
|
|
12640
|
+
await refreshUpdateCache({ silent: true });
|
|
12641
|
+
return;
|
|
12642
|
+
}
|
|
12643
|
+
if (argv[0] === "update") {
|
|
12644
|
+
await runUpdateCommand(argv.slice(1));
|
|
12645
|
+
return;
|
|
12646
|
+
}
|
|
12384
12647
|
if (argv[0] === "ci") {
|
|
12385
12648
|
await runCiCommand(argv.slice(1));
|
|
12386
12649
|
return;
|
|
@@ -12431,6 +12694,9 @@ ${os2.release()}
|
|
|
12431
12694
|
}
|
|
12432
12695
|
const machineReadable = options.scoreOnly || options.json || options.annotations;
|
|
12433
12696
|
const colorized = shouldUseColor(options.noColor, machineReadable);
|
|
12697
|
+
const updateNoticeEnabled = automaticUpdateNoticeEnabled(options, machineReadable);
|
|
12698
|
+
if (updateNoticeEnabled)
|
|
12699
|
+
startBackgroundUpdateRefresh();
|
|
12434
12700
|
const progress = createProgressRenderer({
|
|
12435
12701
|
enabled: Boolean(process.stdout.isTTY && !process.env.CI && !machineReadable),
|
|
12436
12702
|
colorized
|
|
@@ -12445,12 +12711,12 @@ ${os2.release()}
|
|
|
12445
12711
|
const json = `${serializeReport(report, compactJson)}
|
|
12446
12712
|
`;
|
|
12447
12713
|
if (options.jsonOut) {
|
|
12448
|
-
const outputPath =
|
|
12449
|
-
|
|
12450
|
-
|
|
12714
|
+
const outputPath = path13.resolve(process.cwd(), options.jsonOut);
|
|
12715
|
+
fs11.mkdirSync(path13.dirname(outputPath), { recursive: true });
|
|
12716
|
+
fs11.writeFileSync(outputPath, json);
|
|
12451
12717
|
}
|
|
12452
12718
|
if (options.outputDir)
|
|
12453
|
-
writeDiagnosticsDump(
|
|
12719
|
+
writeDiagnosticsDump(path13.resolve(process.cwd(), options.outputDir), report);
|
|
12454
12720
|
const config = loadConfigWithSource(process.cwd()).config;
|
|
12455
12721
|
const showScore = options.showScore ?? true;
|
|
12456
12722
|
const verbose = options.verbose ?? config.verbose ?? false;
|
|
@@ -12467,6 +12733,13 @@ ${os2.release()}
|
|
|
12467
12733
|
} else
|
|
12468
12734
|
process.stdout.write(`${renderTextReport(report, showScore, colorized, verbose, process.stdout.columns ?? 120)}
|
|
12469
12735
|
`);
|
|
12736
|
+
if (updateNoticeEnabled) {
|
|
12737
|
+
const update = getCachedUpdateNotice(VERSION2);
|
|
12738
|
+
if (update)
|
|
12739
|
+
process.stdout.write(`
|
|
12740
|
+
${renderUpdateNotice(update.current, update.latest, colorized)}
|
|
12741
|
+
`);
|
|
12742
|
+
}
|
|
12470
12743
|
const blocking = options.blocking ?? config.blocking ?? "error";
|
|
12471
12744
|
if (shouldBlock2(report, blocking))
|
|
12472
12745
|
process.exitCode = 1;
|
|
@@ -12479,5 +12752,5 @@ ${os2.release()}
|
|
|
12479
12752
|
}
|
|
12480
12753
|
main();
|
|
12481
12754
|
|
|
12482
|
-
//# debugId=
|
|
12755
|
+
//# debugId=53584D6CB0EB00E064756E2164756E21
|
|
12483
12756
|
//# sourceMappingURL=cli.js.map
|