@colrealpro/react-luau-doctor 0.18.1 → 0.18.3

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 CHANGED
@@ -2,16 +2,16 @@
2
2
  // @bun
3
3
 
4
4
  // src/cli.ts
5
- import fs10 from "fs";
5
+ import fs11 from "fs";
6
6
  import os2 from "os";
7
- import path12 from "path";
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.1",
14
+ version: "0.18.3",
15
15
  description: "Static analysis for React-Luau hooks, effects, rendering, and performance.",
16
16
  license: "MIT",
17
17
  type: "module",
@@ -2141,6 +2141,71 @@ function applyMasks(source, ranges) {
2141
2141
  }
2142
2142
  return output;
2143
2143
  }
2144
+ function genericFunctionParameterRanges(source, mask) {
2145
+ const ranges = [];
2146
+ const functionPattern = /\bfunction\b/g;
2147
+ for (const match of source.matchAll(functionPattern)) {
2148
+ const start = match.index;
2149
+ if (!isCode(mask, start, start + match[0].length))
2150
+ continue;
2151
+ let cursor = start + match[0].length;
2152
+ while (cursor < source.length) {
2153
+ const char = source[cursor];
2154
+ if (/\s/.test(char)) {
2155
+ cursor += 1;
2156
+ continue;
2157
+ }
2158
+ if (char === "(")
2159
+ break;
2160
+ if (char === "<") {
2161
+ let depth = 0;
2162
+ const rangeStart = cursor;
2163
+ while (cursor < source.length) {
2164
+ if (isCode(mask, cursor)) {
2165
+ if (source[cursor] === "<")
2166
+ depth += 1;
2167
+ else if (source[cursor] === ">") {
2168
+ depth -= 1;
2169
+ if (depth === 0) {
2170
+ cursor += 1;
2171
+ ranges.push({ start: rangeStart, end: cursor });
2172
+ break;
2173
+ }
2174
+ }
2175
+ }
2176
+ cursor += 1;
2177
+ }
2178
+ break;
2179
+ }
2180
+ if (/[A-Za-z0-9_.:]/.test(char)) {
2181
+ cursor += 1;
2182
+ continue;
2183
+ }
2184
+ break;
2185
+ }
2186
+ }
2187
+ return ranges;
2188
+ }
2189
+ function normalizeGenericTypePackUses(source, mask) {
2190
+ const declarationRanges = genericFunctionParameterRanges(source, mask);
2191
+ const replacements = [];
2192
+ const packPattern = /\b[A-Za-z_][A-Za-z0-9_]*\.\.\./g;
2193
+ for (const match of source.matchAll(packPattern)) {
2194
+ const start = match.index;
2195
+ const end = start + match[0].length;
2196
+ if (!isCode(mask, start, end))
2197
+ continue;
2198
+ if (declarationRanges.some((range) => start >= range.start && end <= range.end))
2199
+ continue;
2200
+ const name = match[0].slice(0, -3);
2201
+ replacements.push({ start, end, value: `...${name}` });
2202
+ }
2203
+ let output = source;
2204
+ for (const replacement of replacements.sort((a, b) => b.start - a.start)) {
2205
+ output = `${output.slice(0, replacement.start)}${replacement.value}${output.slice(replacement.end)}`;
2206
+ }
2207
+ return output;
2208
+ }
2144
2209
  function normalizeIntegerLiteralSuffixes(source, mask) {
2145
2210
  const suffixes = [];
2146
2211
  const integerPattern = /\b(?:0[xX][0-9A-Fa-f_]+|0[bB][01_]+|[0-9][0-9_]*)i\b/g;
@@ -2180,13 +2245,14 @@ function parserCompatibleSource(source) {
2180
2245
  const mask = codeMask(source);
2181
2246
  const keywordCompatible = replaceContextualKeywords(source, mask);
2182
2247
  const literalCompatible = normalizeIntegerLiteralSuffixes(keywordCompatible, mask);
2248
+ const packCompatible = normalizeGenericTypePackUses(literalCompatible, mask);
2183
2249
  const ranges = [
2184
2250
  ...maskTypeAliases(source, mask),
2185
2251
  ...maskTypeLevelBlocks(source, mask),
2186
2252
  ...attributeRanges(source, mask),
2187
2253
  ...explicitTypeArgumentRanges(source, mask)
2188
2254
  ];
2189
- const masked = applyMasks(literalCompatible, ranges);
2255
+ const masked = applyMasks(packCompatible, ranges);
2190
2256
  return masked.replace(/^(\s*)type(?=\s*(?:\+=|-=|\*=|\/=|%=|\^=|\.\.=|=(?!=)))/gm, "$1_typ");
2191
2257
  }
2192
2258
 
@@ -10698,6 +10764,210 @@ async function runCiCommand(argv) {
10698
10764
  throw new Error("ci requires install, config, or upgrade");
10699
10765
  }
10700
10766
 
10767
+ // src/update-check.ts
10768
+ import { spawn } from "child_process";
10769
+ import fs10 from "fs";
10770
+ import path12 from "path";
10771
+ var UPDATE_CHECK_INTERVAL_MS = 2 * 60 * 60 * 1000;
10772
+ var UPDATE_REQUEST_TIMEOUT_MS = 5000;
10773
+ var UPDATE_CACHE_FILENAME = "update-check.json";
10774
+ function updateCacheFilename() {
10775
+ return path12.join(cacheBaseDirectory(), UPDATE_CACHE_FILENAME);
10776
+ }
10777
+ function readUpdateCache() {
10778
+ try {
10779
+ const parsed = JSON.parse(fs10.readFileSync(updateCacheFilename(), "utf8"));
10780
+ if (typeof parsed.checkedAt !== "number" || !Number.isFinite(parsed.checkedAt))
10781
+ return null;
10782
+ if (parsed.latest !== undefined && typeof parsed.latest !== "string")
10783
+ return null;
10784
+ return {
10785
+ checkedAt: parsed.checkedAt,
10786
+ latest: parsed.latest
10787
+ };
10788
+ } catch {
10789
+ return null;
10790
+ }
10791
+ }
10792
+ function writeUpdateCache(cache) {
10793
+ try {
10794
+ const filename = updateCacheFilename();
10795
+ fs10.mkdirSync(path12.dirname(filename), { recursive: true });
10796
+ fs10.writeFileSync(filename, `${JSON.stringify(cache)}
10797
+ `);
10798
+ } catch {}
10799
+ }
10800
+ function parseVersion(value) {
10801
+ const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(value.trim());
10802
+ if (!match)
10803
+ return null;
10804
+ return {
10805
+ major: Number(match[1]),
10806
+ minor: Number(match[2]),
10807
+ patch: Number(match[3]),
10808
+ prerelease: match[4]?.split(".") ?? []
10809
+ };
10810
+ }
10811
+ function comparePrerelease(left, right) {
10812
+ if (left.length === 0 || right.length === 0) {
10813
+ if (left.length === right.length)
10814
+ return 0;
10815
+ return left.length === 0 ? 1 : -1;
10816
+ }
10817
+ const count = Math.max(left.length, right.length);
10818
+ for (let index = 0;index < count; index += 1) {
10819
+ const leftPart = left[index];
10820
+ const rightPart = right[index];
10821
+ if (leftPart === undefined)
10822
+ return -1;
10823
+ if (rightPart === undefined)
10824
+ return 1;
10825
+ if (leftPart === rightPart)
10826
+ continue;
10827
+ const leftNumber = /^\d+$/.test(leftPart) ? Number(leftPart) : null;
10828
+ const rightNumber = /^\d+$/.test(rightPart) ? Number(rightPart) : null;
10829
+ if (leftNumber !== null && rightNumber !== null)
10830
+ return leftNumber < rightNumber ? -1 : 1;
10831
+ if (leftNumber !== null)
10832
+ return -1;
10833
+ if (rightNumber !== null)
10834
+ return 1;
10835
+ return leftPart < rightPart ? -1 : 1;
10836
+ }
10837
+ return 0;
10838
+ }
10839
+ function compareVersions(left, right) {
10840
+ const a = parseVersion(left);
10841
+ const b = parseVersion(right);
10842
+ if (!a || !b)
10843
+ return null;
10844
+ for (const key of ["major", "minor", "patch"]) {
10845
+ if (a[key] !== b[key])
10846
+ return a[key] < b[key] ? -1 : 1;
10847
+ }
10848
+ return comparePrerelease(a.prerelease, b.prerelease);
10849
+ }
10850
+ function updateRegistryUrl() {
10851
+ const registry = process.env.REACT_LUAU_DOCTOR_UPDATE_REGISTRY ?? process.env.npm_config_registry ?? process.env.NPM_CONFIG_REGISTRY ?? "https://registry.npmjs.org/";
10852
+ const base = registry.endsWith("/") ? registry : `${registry}/`;
10853
+ return new URL(`${encodeURIComponent(package_default.name)}/latest`, base).toString();
10854
+ }
10855
+ async function fetchLatestVersion() {
10856
+ const controller = new AbortController;
10857
+ const timer = setTimeout(() => controller.abort(), UPDATE_REQUEST_TIMEOUT_MS);
10858
+ timer.unref?.();
10859
+ try {
10860
+ const response = await fetch(updateRegistryUrl(), {
10861
+ headers: { accept: "application/json" },
10862
+ signal: controller.signal
10863
+ });
10864
+ if (!response.ok)
10865
+ throw new Error(`npm registry returned HTTP ${response.status}`);
10866
+ const body = await response.json();
10867
+ if (typeof body.version !== "string" || compareVersions(body.version, body.version) === null) {
10868
+ throw new Error("npm registry returned an invalid package version");
10869
+ }
10870
+ return body.version;
10871
+ } finally {
10872
+ clearTimeout(timer);
10873
+ }
10874
+ }
10875
+ function updateCacheIsStale(now = Date.now()) {
10876
+ const cache = readUpdateCache();
10877
+ return !cache || now - cache.checkedAt >= UPDATE_CHECK_INTERVAL_MS;
10878
+ }
10879
+ async function refreshUpdateCache(options = {}) {
10880
+ const now = options.now ?? Date.now();
10881
+ try {
10882
+ const latest = await fetchLatestVersion();
10883
+ writeUpdateCache({ checkedAt: now, latest });
10884
+ return latest;
10885
+ } catch (error) {
10886
+ const previous = readUpdateCache();
10887
+ writeUpdateCache({ checkedAt: now, latest: previous?.latest });
10888
+ if (options.silent)
10889
+ return null;
10890
+ throw error;
10891
+ }
10892
+ }
10893
+ function startBackgroundUpdateRefresh() {
10894
+ if (!updateCacheIsStale())
10895
+ return;
10896
+ const script = process.argv[1];
10897
+ if (!script)
10898
+ return;
10899
+ try {
10900
+ const child2 = spawn(process.execPath, [script, "__update-cache"], {
10901
+ detached: true,
10902
+ stdio: "ignore",
10903
+ windowsHide: true,
10904
+ env: process.env
10905
+ });
10906
+ child2.unref();
10907
+ } catch {}
10908
+ }
10909
+ function getCachedUpdateNotice(currentVersion) {
10910
+ const cache = readUpdateCache();
10911
+ if (!cache?.latest)
10912
+ return null;
10913
+ const comparison = compareVersions(cache.latest, currentVersion);
10914
+ if (comparison === null || comparison <= 0)
10915
+ return null;
10916
+ return { current: currentVersion, latest: cache.latest };
10917
+ }
10918
+ async function checkForUpdatesNow(currentVersion) {
10919
+ const latest = await refreshUpdateCache();
10920
+ if (!latest)
10921
+ throw new Error("Could not check npm for updates");
10922
+ const comparison = compareVersions(latest, currentVersion);
10923
+ if (comparison === null)
10924
+ throw new Error(`Could not compare installed version ${currentVersion} with ${latest}`);
10925
+ return { latest, updateAvailable: comparison > 0 };
10926
+ }
10927
+
10928
+ // src/update-install.ts
10929
+ import { spawnSync as spawnSync3 } from "child_process";
10930
+ import { fileURLToPath } from "url";
10931
+ function normalizedPath(filename) {
10932
+ return filename.replaceAll("\\", "/");
10933
+ }
10934
+ function updateInstallCommandForPath(filename) {
10935
+ const normalized = normalizedPath(filename);
10936
+ const spec = `${package_default.name}@latest`;
10937
+ if (normalized.includes("/.bun/install/global/node_modules/")) {
10938
+ return {
10939
+ manager: "bun",
10940
+ command: "bun",
10941
+ args: ["add", "-g", spec],
10942
+ display: `bun add -g ${spec}`
10943
+ };
10944
+ }
10945
+ if (normalized.includes("/.bun/install/cache/") || normalized.includes("/.npm/_npx/"))
10946
+ return null;
10947
+ if (normalized.includes("/node_modules/")) {
10948
+ return {
10949
+ manager: "npm",
10950
+ command: "npm",
10951
+ args: ["install", "-g", spec],
10952
+ display: `npm install -g ${spec}`
10953
+ };
10954
+ }
10955
+ return null;
10956
+ }
10957
+ function currentUpdateInstallCommand() {
10958
+ return updateInstallCommandForPath(fileURLToPath(import.meta.url));
10959
+ }
10960
+ function installLatestVersion(command) {
10961
+ const result = spawnSync3(command.command, command.args, {
10962
+ stdio: "inherit",
10963
+ windowsHide: true
10964
+ });
10965
+ if (result.error)
10966
+ throw result.error;
10967
+ if (result.status !== 0)
10968
+ throw new Error(`${command.display} exited with code ${result.status ?? "unknown"}`);
10969
+ }
10970
+
10701
10971
  // src/fix-examples.ts
10702
10972
  var examples = {
10703
10973
  "react-luau/parse-error": {
@@ -11184,6 +11454,7 @@ Usage:
11184
11454
  react-luau-doctor [directory] [options]
11185
11455
  react-luau-doctor ci <install|config|upgrade>
11186
11456
  react-luau-doctor why <file:line>
11457
+ react-luau-doctor update [--check]
11187
11458
  react-luau-doctor rules <command>
11188
11459
 
11189
11460
  Scan options:
@@ -11212,6 +11483,7 @@ Scan options:
11212
11483
  --no-color Disable automatic ANSI colors
11213
11484
  --no-cache Disable the persistent OS-level analysis cache
11214
11485
  --no-parallel Disable parallel file analysis
11486
+ --no-update-check Disable the automatic update notice
11215
11487
 
11216
11488
  React-Luau Doctor options:
11217
11489
  --min-severity <level> suggestion, warning, or error
@@ -11224,6 +11496,10 @@ CI commands:
11224
11496
  ci upgrade [--provider github|gitlab] [--pr] [-y] [--cwd <cwd>]
11225
11497
  Reporting toggles: --comment/--no-comment, --review-comments/--no-review-comments, --commit-status/--no-commit-status
11226
11498
 
11499
+ Update commands:
11500
+ update Update the global installation to the latest release
11501
+ update --check Check npm for a newer release without updating
11502
+
11227
11503
  Rules commands:
11228
11504
  rules list [--category <name>] [--configured] [--json]
11229
11505
  rules explain <rule> [--json]
@@ -11236,6 +11512,53 @@ Config:
11236
11512
  react-luau-doctor.config.json
11237
11513
  `;
11238
11514
  }
11515
+ function automaticUpdateNoticeEnabled(options, machineReadable) {
11516
+ 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;
11517
+ }
11518
+ function renderUpdateNotice(current, latest, colorized) {
11519
+ const label = whyPaint(colorized, "Update available:", WHY_ANSI.bold, WHY_ANSI.yellow);
11520
+ const oldVersion = whyPaint(colorized, `v${current}`, WHY_ANSI.dim);
11521
+ const newVersion = whyPaint(colorized, `v${latest}`, WHY_ANSI.bold);
11522
+ return `${label} ${oldVersion} \u2192 ${newVersion}
11523
+ Run \`react-luau-doctor update\` to update.`;
11524
+ }
11525
+ async function runUpdateCommand(argv) {
11526
+ if (argv.includes("--help") || argv.includes("-h")) {
11527
+ process.stdout.write(`Usage: react-luau-doctor update [--check]
11528
+ `);
11529
+ return;
11530
+ }
11531
+ if (argv.length > 1 || argv.length === 1 && argv[0] !== "--check") {
11532
+ throw new Error("Usage: react-luau-doctor update [--check]");
11533
+ }
11534
+ const result = await checkForUpdatesNow(VERSION2);
11535
+ if (!result.updateAvailable) {
11536
+ process.stdout.write(`React-Luau Doctor v${VERSION2} is up to date.
11537
+ `);
11538
+ return;
11539
+ }
11540
+ const colorized = shouldUseColor(false, false);
11541
+ if (argv[0] === "--check") {
11542
+ process.stdout.write(`${renderUpdateNotice(VERSION2, result.latest, colorized)}
11543
+ `);
11544
+ return;
11545
+ }
11546
+ const command = currentUpdateInstallCommand();
11547
+ if (!command) {
11548
+ throw new Error(`Could not determine the global package manager for this installation. Run \`npm install -g ${package_default.name}@latest\` manually.`);
11549
+ }
11550
+ const label = whyPaint(colorized, "Updating React-Luau Doctor:", WHY_ANSI.bold, WHY_ANSI.yellow);
11551
+ const oldVersion = whyPaint(colorized, `v${VERSION2}`, WHY_ANSI.dim);
11552
+ const newVersion = whyPaint(colorized, `v${result.latest}`, WHY_ANSI.bold);
11553
+ process.stdout.write(`${label} ${oldVersion} \u2192 ${newVersion}
11554
+ Using \`${command.display}\`
11555
+
11556
+ `);
11557
+ installLatestVersion(command);
11558
+ process.stdout.write(`
11559
+ Updated React-Luau Doctor to v${result.latest}.
11560
+ `);
11561
+ }
11239
11562
  function splitLongOption(arg) {
11240
11563
  if (!arg.startsWith("--"))
11241
11564
  return { name: arg };
@@ -11641,6 +11964,7 @@ function parseArgs(argv) {
11641
11964
  noColor: false,
11642
11965
  noCache: false,
11643
11966
  noParallel: false,
11967
+ noUpdateCheck: false,
11644
11968
  help: false,
11645
11969
  version: false
11646
11970
  };
@@ -11678,6 +12002,8 @@ function parseArgs(argv) {
11678
12002
  options.noCache = true;
11679
12003
  else if (arg === "--no-parallel")
11680
12004
  options.noParallel = true;
12005
+ else if (arg === "--no-update-check")
12006
+ options.noUpdateCheck = true;
11681
12007
  else if (arg === "--annotations")
11682
12008
  options.annotations = true;
11683
12009
  else if (arg === "--help" || arg === "-h")
@@ -11795,8 +12121,8 @@ function validateModeFlags(options, scope) {
11795
12121
  throw new Error("--annotations cannot be combined with --json or --score");
11796
12122
  }
11797
12123
  function findProjectByName(root, name) {
11798
- const direct = path12.resolve(root, name);
11799
- if (fs10.existsSync(direct) && fs10.statSync(direct).isDirectory())
12124
+ const direct = path13.resolve(root, name);
12125
+ if (fs11.existsSync(direct) && fs11.statSync(direct).isDirectory())
11800
12126
  return direct;
11801
12127
  const ignored = new Set([".git", "node_modules", "Packages", "DevPackages", "ServerPackages", "dist", "vendor"]);
11802
12128
  const queue = [{ directory: root, depth: 0 }];
@@ -11805,17 +12131,17 @@ function findProjectByName(root, name) {
11805
12131
  const current = queue.shift();
11806
12132
  if (current.depth >= 3)
11807
12133
  continue;
11808
- for (const entry of fs10.readdirSync(current.directory, { withFileTypes: true })) {
12134
+ for (const entry of fs11.readdirSync(current.directory, { withFileTypes: true })) {
11809
12135
  if (!entry.isDirectory() || ignored.has(entry.name))
11810
12136
  continue;
11811
- const absolute = path12.join(current.directory, entry.name);
12137
+ const absolute = path13.join(current.directory, entry.name);
11812
12138
  if (entry.name === name)
11813
12139
  matches.push(absolute);
11814
12140
  queue.push({ directory: absolute, depth: current.depth + 1 });
11815
12141
  }
11816
12142
  }
11817
12143
  if (matches.length > 1)
11818
- throw new Error(`Project selector "${name}" is ambiguous: ${matches.map((match) => path12.relative(root, match)).join(", ")}`);
12144
+ throw new Error(`Project selector "${name}" is ambiguous: ${matches.map((match) => path13.relative(root, match)).join(", ")}`);
11819
12145
  return matches[0] ?? null;
11820
12146
  }
11821
12147
  function resolveProjectRoots(root, projectFlag, config) {
@@ -11833,15 +12159,15 @@ function serializeReport(report, compact) {
11833
12159
  return compact ? JSON.stringify(report) : JSON.stringify(report, null, 2);
11834
12160
  }
11835
12161
  function writeJsonFile(filename, value, compact = false) {
11836
- fs10.mkdirSync(path12.dirname(filename), { recursive: true });
11837
- fs10.writeFileSync(filename, `${compact ? JSON.stringify(value) : JSON.stringify(value, null, 2)}
12162
+ fs11.mkdirSync(path13.dirname(filename), { recursive: true });
12163
+ fs11.writeFileSync(filename, `${compact ? JSON.stringify(value) : JSON.stringify(value, null, 2)}
11838
12164
  `);
11839
12165
  }
11840
12166
  function writeDiagnosticsDump(directory, report) {
11841
- fs10.mkdirSync(directory, { recursive: true });
11842
- writeJsonFile(path12.join(directory, "report.json"), report);
11843
- writeJsonFile(path12.join(directory, "diagnostics.json"), report.diagnostics);
11844
- writeJsonFile(path12.join(directory, "summary.json"), {
12167
+ fs11.mkdirSync(directory, { recursive: true });
12168
+ writeJsonFile(path13.join(directory, "report.json"), report);
12169
+ writeJsonFile(path13.join(directory, "diagnostics.json"), report.diagnostics);
12170
+ writeJsonFile(path13.join(directory, "summary.json"), {
11845
12171
  schemaVersion: report.schemaVersion,
11846
12172
  root: report.root,
11847
12173
  scope: report.scope ?? "full",
@@ -11877,7 +12203,7 @@ function parseCommandCwd(argv) {
11877
12203
  const value = argv[++index];
11878
12204
  if (!value)
11879
12205
  throw new Error(`${arg} requires a path`);
11880
- cwd = path12.resolve(value);
12206
+ cwd = path13.resolve(value);
11881
12207
  } else
11882
12208
  remaining.push(arg);
11883
12209
  }
@@ -11986,7 +12312,7 @@ function runRulesCommand(argv) {
11986
12312
  if (!severity)
11987
12313
  throw new Error("Rule severity must be off, suggestion, warning/warn, or error");
11988
12314
  const filename = writeConfig(cwd, (current) => ({ ...current, rules: { ...current.rules, [rule.id]: severity } }));
11989
- process.stdout.write(`Set ${rule.id} to ${severity} in ${path12.relative(cwd, filename)}
12315
+ process.stdout.write(`Set ${rule.id} to ${severity} in ${path13.relative(cwd, filename)}
11990
12316
  `);
11991
12317
  return;
11992
12318
  }
@@ -12005,7 +12331,7 @@ function runRulesCommand(argv) {
12005
12331
  severity = normalized;
12006
12332
  }
12007
12333
  const filename = writeConfig(cwd, (current) => ({ ...current, rules: { ...current.rules, [rule.id]: severity } }));
12008
- process.stdout.write(`Enabled ${rule.id} at ${severity} in ${path12.relative(cwd, filename)}
12334
+ process.stdout.write(`Enabled ${rule.id} at ${severity} in ${path13.relative(cwd, filename)}
12009
12335
  `);
12010
12336
  return;
12011
12337
  }
@@ -12015,7 +12341,7 @@ function runRulesCommand(argv) {
12015
12341
  throw new Error("rules disable requires a rule id");
12016
12342
  const rule = findRule(requested);
12017
12343
  const filename = writeConfig(cwd, (current) => ({ ...current, rules: { ...current.rules, [rule.id]: "off" } }));
12018
- process.stdout.write(`Disabled ${rule.id} in ${path12.relative(cwd, filename)}
12344
+ process.stdout.write(`Disabled ${rule.id} in ${path13.relative(cwd, filename)}
12019
12345
  `);
12020
12346
  return;
12021
12347
  }
@@ -12038,7 +12364,7 @@ function runRulesCommand(argv) {
12038
12364
  ...categoryRules.map((rule) => [rule.id, severity])
12039
12365
  ])
12040
12366
  }));
12041
- process.stdout.write(`Set ${categoryRules.length} ${category} rules to ${severity} in ${path12.relative(cwd, filename)}
12367
+ process.stdout.write(`Set ${categoryRules.length} ${category} rules to ${severity} in ${path13.relative(cwd, filename)}
12042
12368
  `);
12043
12369
  return;
12044
12370
  }
@@ -12122,7 +12448,7 @@ function whyCaretForLine(sourceLine, line, ranges) {
12122
12448
  return value;
12123
12449
  }
12124
12450
  function renderWhyCodeFrame(filename, diagnostic, colorized) {
12125
- const source = fs10.readFileSync(filename, "utf8").split(/\r?\n/);
12451
+ const source = fs11.readFileSync(filename, "utf8").split(/\r?\n/);
12126
12452
  const ranges = whyDiagnosticRanges(diagnostic);
12127
12453
  const intervals = whyFrameIntervals(ranges, source.length);
12128
12454
  const width = String(Math.max(...intervals.map((interval) => interval.end), 1)).length;
@@ -12230,13 +12556,13 @@ async function runWhy(location, cwd, noColor = false, cache = true, onProgress,
12230
12556
  const match = location.match(/^(.*):(\d+)(?::(\d+))?$/);
12231
12557
  if (!match)
12232
12558
  throw new Error("Location must be file:line or file:line:column");
12233
- const filename = path12.resolve(cwd, match[1]);
12559
+ const filename = path13.resolve(cwd, match[1]);
12234
12560
  const line = Number(match[2]);
12235
12561
  const column = match[3] === undefined ? undefined : Number(match[3]);
12236
- if (!fs10.existsSync(filename))
12562
+ if (!fs11.existsSync(filename))
12237
12563
  throw new Error(`File does not exist: ${match[1]}`);
12238
12564
  const colorized = shouldUseColor(noColor, false);
12239
- const source = fs10.readFileSync(filename, "utf8");
12565
+ const source = fs11.readFileSync(filename, "utf8");
12240
12566
  const isSuppressed = createInlineSuppressionChecker(source);
12241
12567
  const auditReport = await scanWhyFile(filename, cwd, cache, onProgress);
12242
12568
  beforeOutput?.();
@@ -12277,16 +12603,16 @@ async function runWhy(location, cwd, noColor = false, cache = true, onProgress,
12277
12603
  }
12278
12604
  }
12279
12605
  function pathIsInside(parent, child2) {
12280
- const relative = path12.relative(parent, child2);
12281
- return relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative);
12606
+ const relative = path13.relative(parent, child2);
12607
+ return relative === "" || !relative.startsWith("..") && !path13.isAbsolute(relative);
12282
12608
  }
12283
12609
  async function runScan(options, onProgress) {
12284
12610
  const commandRoot = process.cwd();
12285
- const target = path12.resolve(commandRoot, options.target);
12286
- if (!fs10.existsSync(target))
12611
+ const target = path13.resolve(commandRoot, options.target);
12612
+ if (!fs11.existsSync(target))
12287
12613
  throw new Error(`Scan path does not exist: ${options.target}`);
12288
- const targetStat = fs10.statSync(target);
12289
- const scanRoot = targetStat.isFile() ? path12.dirname(target) : target;
12614
+ const targetStat = fs11.statSync(target);
12615
+ const scanRoot = targetStat.isFile() ? path13.dirname(target) : target;
12290
12616
  const loaded = loadConfigWithSource(commandRoot);
12291
12617
  const config = loaded.config;
12292
12618
  const resolvedScope = resolveScope(options, config);
@@ -12316,7 +12642,7 @@ async function runScan(options, onProgress) {
12316
12642
  const reports = [];
12317
12643
  for (const { projectRoot, targetRoot } of projectTargets) {
12318
12644
  let report2;
12319
- const projectName = projectTargets.length > 1 ? path12.relative(displayRoot, projectRoot) || "." : undefined;
12645
+ const projectName = projectTargets.length > 1 ? path13.relative(displayRoot, projectRoot) || "." : undefined;
12320
12646
  const projectProgress = onProgress ? (progress) => onProgress({
12321
12647
  ...progress,
12322
12648
  phase: [projectName, progress.phase].filter(Boolean).join(":") || undefined,
@@ -12363,7 +12689,7 @@ async function runScan(options, onProgress) {
12363
12689
  `[debug] target=${target}`,
12364
12690
  `[debug] config=${loaded.filename ?? "none"}`,
12365
12691
  `[debug] scope=${report.scope ?? scope} base=${report.base ?? resolvedScope.base ?? "auto"}`,
12366
- `[debug] projects=${projectTargets.map(({ projectRoot }) => path12.relative(displayRoot, projectRoot) || ".").join(",")}`,
12692
+ `[debug] projects=${projectTargets.map(({ projectRoot }) => path13.relative(displayRoot, projectRoot) || ".").join(",")}`,
12367
12693
  `[debug] candidates=${report.candidateFiles ?? 0} scanned=${report.scannedFiles} partial=${Boolean(report.partial)}`,
12368
12694
  `[debug] parallel=${!options.noParallel}`
12369
12695
  ];
@@ -12376,6 +12702,14 @@ async function runScan(options, onProgress) {
12376
12702
  async function main() {
12377
12703
  try {
12378
12704
  const argv = process.argv.slice(2);
12705
+ if (argv[0] === "__update-cache") {
12706
+ await refreshUpdateCache({ silent: true });
12707
+ return;
12708
+ }
12709
+ if (argv[0] === "update") {
12710
+ await runUpdateCommand(argv.slice(1));
12711
+ return;
12712
+ }
12379
12713
  if (argv[0] === "ci") {
12380
12714
  await runCiCommand(argv.slice(1));
12381
12715
  return;
@@ -12426,6 +12760,9 @@ ${os2.release()}
12426
12760
  }
12427
12761
  const machineReadable = options.scoreOnly || options.json || options.annotations;
12428
12762
  const colorized = shouldUseColor(options.noColor, machineReadable);
12763
+ const updateNoticeEnabled = automaticUpdateNoticeEnabled(options, machineReadable);
12764
+ if (updateNoticeEnabled)
12765
+ startBackgroundUpdateRefresh();
12429
12766
  const progress = createProgressRenderer({
12430
12767
  enabled: Boolean(process.stdout.isTTY && !process.env.CI && !machineReadable),
12431
12768
  colorized
@@ -12440,12 +12777,12 @@ ${os2.release()}
12440
12777
  const json = `${serializeReport(report, compactJson)}
12441
12778
  `;
12442
12779
  if (options.jsonOut) {
12443
- const outputPath = path12.resolve(process.cwd(), options.jsonOut);
12444
- fs10.mkdirSync(path12.dirname(outputPath), { recursive: true });
12445
- fs10.writeFileSync(outputPath, json);
12780
+ const outputPath = path13.resolve(process.cwd(), options.jsonOut);
12781
+ fs11.mkdirSync(path13.dirname(outputPath), { recursive: true });
12782
+ fs11.writeFileSync(outputPath, json);
12446
12783
  }
12447
12784
  if (options.outputDir)
12448
- writeDiagnosticsDump(path12.resolve(process.cwd(), options.outputDir), report);
12785
+ writeDiagnosticsDump(path13.resolve(process.cwd(), options.outputDir), report);
12449
12786
  const config = loadConfigWithSource(process.cwd()).config;
12450
12787
  const showScore = options.showScore ?? true;
12451
12788
  const verbose = options.verbose ?? config.verbose ?? false;
@@ -12462,6 +12799,13 @@ ${os2.release()}
12462
12799
  } else
12463
12800
  process.stdout.write(`${renderTextReport(report, showScore, colorized, verbose, process.stdout.columns ?? 120)}
12464
12801
  `);
12802
+ if (updateNoticeEnabled) {
12803
+ const update = getCachedUpdateNotice(VERSION2);
12804
+ if (update)
12805
+ process.stdout.write(`
12806
+ ${renderUpdateNotice(update.current, update.latest, colorized)}
12807
+ `);
12808
+ }
12465
12809
  const blocking = options.blocking ?? config.blocking ?? "error";
12466
12810
  if (shouldBlock2(report, blocking))
12467
12811
  process.exitCode = 1;
@@ -12474,5 +12818,5 @@ ${os2.release()}
12474
12818
  }
12475
12819
  main();
12476
12820
 
12477
- //# debugId=93BE5E0B263571E264756E2164756E21
12821
+ //# debugId=32BB85E7FBDD457564756E2164756E21
12478
12822
  //# sourceMappingURL=cli.js.map