@colrealpro/react-luau-doctor 0.17.0 → 0.17.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 CHANGED
@@ -11,8 +11,8 @@ var package_default = {
11
11
  publishConfig: {
12
12
  access: "public"
13
13
  },
14
- version: "0.17.0",
15
- description: "A syntax-aware React-Luau code doctor inspired by React Doctor.",
14
+ version: "0.17.1",
15
+ description: "Static analysis for React-Luau hooks, effects, rendering, and performance.",
16
16
  license: "MIT",
17
17
  type: "module",
18
18
  bin: {
@@ -24,8 +24,7 @@ var package_default = {
24
24
  "README.md",
25
25
  "LICENSE",
26
26
  "THIRD_PARTY_NOTICES.md",
27
- "docs",
28
- "action.yml"
27
+ "docs"
29
28
  ],
30
29
  scripts: {
31
30
  build: "bun run scripts/build.ts",
@@ -37,14 +36,14 @@ var package_default = {
37
36
  "verify:package": "bun run build && bun run scripts/verify-package.ts"
38
37
  },
39
38
  engines: {
40
- bun: ">=1.3.14"
39
+ bun: ">=1.4.0"
41
40
  },
42
- packageManager: "bun@1.3.14",
41
+ packageManager: "bun@1.4.0",
43
42
  dependencies: {
44
43
  "web-tree-sitter": "0.26.11"
45
44
  },
46
45
  devDependencies: {
47
- "@types/bun": "1.3.14",
46
+ "@types/bun": "1.4.0",
48
47
  typescript: "5.8.3"
49
48
  },
50
49
  keywords: [
@@ -67,21 +66,18 @@ var package_default = {
67
66
  // src/config.ts
68
67
  import fs from "fs";
69
68
  import path from "path";
70
- var CONFIG_NAMES = ["react-luau-doctor.config.json", "doctor.config.json"];
69
+ var CONFIG_NAME = "react-luau-doctor.config.json";
71
70
  var SEVERITIES = new Set(["off", "error", "warning", "suggestion"]);
72
71
  var SCOPES = new Set(["full", "files", "changed", "lines"]);
73
72
  var BLOCKING_LEVELS = new Set(["error", "warning", "none"]);
74
73
  var CATEGORIES = new Set(["Correctness", "Hooks", "Effects", "Performance", "Roblox", "Architecture"]);
75
74
  function loadConfigWithSource(root) {
76
- for (const filename of CONFIG_NAMES) {
77
- const candidate = path.join(root, filename);
78
- if (!fs.existsSync(candidate))
79
- continue;
80
- const parsed = JSON.parse(fs.readFileSync(candidate, "utf8"));
81
- validateConfig(parsed, candidate);
82
- return { config: parsed, filename: candidate };
83
- }
84
- return { config: {}, filename: null };
75
+ const candidate = path.join(root, CONFIG_NAME);
76
+ if (!fs.existsSync(candidate))
77
+ return { config: {}, filename: null };
78
+ const parsed = JSON.parse(fs.readFileSync(candidate, "utf8"));
79
+ validateConfig(parsed, candidate);
80
+ return { config: parsed, filename: candidate };
85
81
  }
86
82
  function loadConfig(root) {
87
83
  return loadConfigWithSource(root).config;
@@ -96,8 +92,6 @@ function validateConfig(config, filename) {
96
92
  validateStringArray(config.include, "include", filename);
97
93
  if (config.ignore !== undefined)
98
94
  validateStringArray(config.ignore, "ignore", filename);
99
- if (config.ignoredTags !== undefined)
100
- validateStringArray(config.ignoredTags, "ignoredTags", filename);
101
95
  if (config.projects !== undefined)
102
96
  validateStringArray(config.projects, "projects", filename);
103
97
  if (config.categories !== undefined) {
@@ -129,22 +123,8 @@ function validateConfig(config, filename) {
129
123
  if (config.blocking !== undefined && !BLOCKING_LEVELS.has(config.blocking)) {
130
124
  throw new Error(`${filename}: blocking must be error, warning, or none`);
131
125
  }
132
- if (config.failOn !== undefined && !BLOCKING_LEVELS.has(config.failOn)) {
133
- throw new Error(`${filename}: failOn must be error, warning, or none`);
134
- }
135
126
  }
136
- function ruleTags(rule) {
137
- return [...new Set([rule.category.toLowerCase(), ...(rule.tags ?? []).map((tag) => tag.toLowerCase())])];
138
- }
139
- function ruleFrameworks(rule) {
140
- if (rule.frameworks && rule.frameworks.length > 0)
141
- return rule.frameworks.map((framework) => framework.toLowerCase());
142
- return rule.category === "Roblox" ? ["react-luau", "roblox"] : ["react-luau", "global"];
143
- }
144
- function effectiveSeverity(defaultSeverity, ruleId, config, tags = []) {
145
- const ignoredTags = new Set((config.ignoredTags ?? []).map((tag) => tag.toLowerCase()));
146
- if (tags.some((tag) => ignoredTags.has(tag.toLowerCase())))
147
- return null;
127
+ function effectiveSeverity(defaultSeverity, ruleId, config) {
148
128
  const configured = config.rules?.[ruleId];
149
129
  if (!configured)
150
130
  return defaultSeverity;
@@ -152,12 +132,12 @@ function effectiveSeverity(defaultSeverity, ruleId, config, tags = []) {
152
132
  }
153
133
  function configPathForWrite(root) {
154
134
  const loaded = loadConfigWithSource(root);
155
- return loaded.filename ?? path.join(root, CONFIG_NAMES[0]);
135
+ return loaded.filename ?? path.join(root, CONFIG_NAME);
156
136
  }
157
137
  function writeConfig(root, update) {
158
138
  const loaded = loadConfigWithSource(root);
159
139
  const next = update(structuredClone(loaded.config));
160
- validateConfig(next, loaded.filename ?? CONFIG_NAMES[0]);
140
+ validateConfig(next, loaded.filename ?? CONFIG_NAME);
161
141
  const filename = loaded.filename ?? configPathForWrite(root);
162
142
  fs.mkdirSync(path.dirname(filename), { recursive: true });
163
143
  fs.writeFileSync(filename, `${JSON.stringify(next, null, 2)}
@@ -582,6 +562,7 @@ function createGitScopePlan(scanRootInput, options, relativeRootInput = scanRoot
582
562
  }
583
563
 
584
564
  // src/reporter.ts
565
+ var VERSION = package_default.version;
585
566
  var SYMBOLS = {
586
567
  error: "x",
587
568
  warning: "!",
@@ -744,7 +725,7 @@ function renderCounts(report, colorized) {
744
725
  ].join(", ");
745
726
  }
746
727
  function renderHeader(report, colorized) {
747
- const title = colorized ? paint("React-Luau Doctor", ANSI.bold, ANSI.magenta) : "React-Luau Doctor";
728
+ const title = colorized ? `${paint("React-Luau Doctor", ANSI.bold, ANSI.magenta)} ${paint(`v${VERSION}`, ANSI.dim)}` : `React-Luau Doctor v${VERSION}`;
748
729
  const scopeSuffix = report.scope && report.scope !== "full" ? ` (${report.scope} scope)` : "";
749
730
  const lines2 = [title, `Scanned ${report.scannedFiles} React-Luau files in ${report.durationMs.toFixed(2)}ms${scopeSuffix}`];
750
731
  if (report.partial) {
@@ -3237,7 +3218,7 @@ function directNodes(body) {
3237
3218
  function topLevelFunctionRecords(record) {
3238
3219
  const functions = [];
3239
3220
  let anonymousExportIndex = 0;
3240
- const add = (node, body, nameText, localName, memberName, method, exported) => {
3221
+ const add = (node, body, localName, memberName, method, exported) => {
3241
3222
  if (!body)
3242
3223
  return;
3243
3224
  const key = memberName ? `member:${memberName}` : localName ? `local:${localName}` : `export:${anonymousExportIndex++}`;
@@ -3262,9 +3243,9 @@ function topLevelFunctionRecords(record) {
3262
3243
  const text = nameNode?.text.replace(/\s+/g, "") ?? "";
3263
3244
  const methodMatch = text.match(/^([A-Za-z_][A-Za-z0-9_]*)[:.]([A-Za-z_][A-Za-z0-9_]*)$/);
3264
3245
  if (methodMatch && methodMatch[1] === record.exportName) {
3265
- add(node, body, text, null, methodMatch[2], nameNode?.type === "method_index_expression", false);
3246
+ add(node, body, null, methodMatch[2], nameNode?.type === "method_index_expression", false);
3266
3247
  } else if (nameNode?.type === "identifier") {
3267
- add(node, body, text, text, null, false, record.exportName === text);
3248
+ add(node, body, text, null, false, record.exportName === text);
3268
3249
  }
3269
3250
  continue;
3270
3251
  }
@@ -3274,7 +3255,7 @@ function topLevelFunctionRecords(record) {
3274
3255
  const expression = expressions[index] ?? expressions[0];
3275
3256
  if (expression?.type !== "function_definition")
3276
3257
  continue;
3277
- add(expression, child(expression, "block"), names[index], names[index], null, false, record.exportName === names[index]);
3258
+ add(expression, child(expression, "block"), names[index], null, false, record.exportName === names[index]);
3278
3259
  }
3279
3260
  continue;
3280
3261
  }
@@ -3287,7 +3268,7 @@ function topLevelFunctionRecords(record) {
3287
3268
  const text = left[index].text.replace(/\s+/g, "");
3288
3269
  const memberMatch = text.match(/^([A-Za-z_][A-Za-z0-9_]*)[.:]([A-Za-z_][A-Za-z0-9_]*)$/);
3289
3270
  if (memberMatch && memberMatch[1] === record.exportName) {
3290
- add(expression, child(expression, "block"), text, null, memberMatch[2], text.includes(":"), false);
3271
+ add(expression, child(expression, "block"), null, memberMatch[2], text.includes(":"), false);
3291
3272
  }
3292
3273
  }
3293
3274
  continue;
@@ -3296,7 +3277,7 @@ function topLevelFunctionRecords(record) {
3296
3277
  const expressions = child(node, "expression_list");
3297
3278
  const expression = expressions?.namedChildren[0];
3298
3279
  if (expression?.type === "function_definition")
3299
- add(expression, child(expression, "block"), null, null, null, false, true);
3280
+ add(expression, child(expression, "block"), null, null, false, true);
3300
3281
  }
3301
3282
  }
3302
3283
  return functions;
@@ -5161,7 +5142,7 @@ function declarationExpressions3(node) {
5161
5142
  const expressionList = assignment?.namedChildren.find((child2) => child2.type === "expression_list");
5162
5143
  return expressionList?.namedChildren ?? [];
5163
5144
  }
5164
- function directCallsInExpression(node, context) {
5145
+ function directCallsInExpression(node) {
5165
5146
  const calls = [];
5166
5147
  const pending = [node];
5167
5148
  while (pending.length > 0) {
@@ -5213,7 +5194,7 @@ function customHookDependencyHints(owner, context) {
5213
5194
  const expression = expressions[index] ?? expressions[0];
5214
5195
  if (!expression)
5215
5196
  continue;
5216
- const calls = directCallsInExpression(expression, context);
5197
+ const calls = directCallsInExpression(expression);
5217
5198
  if (calls.length === 0)
5218
5199
  continue;
5219
5200
  const directHook = calls.find((call) => isCustomHookCall(call, context));
@@ -6339,7 +6320,7 @@ function isIdentifierPropertyName2(node) {
6339
6320
  return sameNode(parent.childForFieldName("method"), node) || sameNode(parent.namedChildren.at(-1), node);
6340
6321
  return false;
6341
6322
  }
6342
- function isReadNode(node, valueName, owner, declaration, context) {
6323
+ function isReadNode(node, valueName, owner, declaration) {
6343
6324
  if (node.type !== "identifier" || node.text !== valueName)
6344
6325
  return false;
6345
6326
  if (isInside2(node, declaration))
@@ -6359,7 +6340,7 @@ function isReadNode(node, valueName, owner, declaration, context) {
6359
6340
  function hasAnyRead(valueName, owner, declaration, context) {
6360
6341
  if (!owner.body)
6361
6342
  return false;
6362
- return [...context.walk(owner.body)].some((node) => isReadNode(node, valueName, owner, declaration, context));
6343
+ return [...context.walk(owner.body)].some((node) => isReadNode(node, valueName, owner, declaration));
6363
6344
  }
6364
6345
  function isCreateElementCall2(call, context) {
6365
6346
  return call.type === "function_call" && context.resolveCallPath(context.getCallPath(call) ?? "") === "React.createElement";
@@ -6436,7 +6417,7 @@ function stateReadsAreBindingCompatible(valueName, owner, declaration, context,
6436
6417
  visited.add(visitKey);
6437
6418
  let reads = 0;
6438
6419
  for (const node of context.walk(owner.body)) {
6439
- if (!isReadNode(node, valueName, owner, declaration, context))
6420
+ if (!isReadNode(node, valueName, owner, declaration))
6440
6421
  continue;
6441
6422
  reads += 1;
6442
6423
  if (isTransparentUsage(node, context, compatibleComponentProps))
@@ -6721,7 +6702,7 @@ function bindingUsageSummary(valueName, owner, declaration, context, compatibleC
6721
6702
  if (!owner.body)
6722
6703
  return summary;
6723
6704
  for (const node of context.walk(owner.body)) {
6724
- if (!isReadNode(node, valueName, owner, declaration, context))
6705
+ if (!isReadNode(node, valueName, owner, declaration))
6725
6706
  continue;
6726
6707
  summary.reads += 1;
6727
6708
  const customComponentUsage = createElementPropUsage(node, context);
@@ -7519,7 +7500,7 @@ function stableShapeVariablesByFunction(context) {
7519
7500
  }
7520
7501
  return stable;
7521
7502
  }
7522
- function loopIterationIsProvablyStable(loop, context, imports, owner, stableShapes) {
7503
+ function loopIterationIsProvablyStable(loop, imports, owner, stableShapes) {
7523
7504
  const numeric = loop.namedChildren.find((child2) => child2.type === "for_numeric_clause");
7524
7505
  if (numeric) {
7525
7506
  const text = numeric.text;
@@ -7723,7 +7704,7 @@ end`,
7723
7704
  if (controlFlow) {
7724
7705
  if (currentModeSummary && currentModeSummary.knownCallSites > 0 && controlledParameterIndex(controlFlow, fn, currentModeSummary) !== null)
7725
7706
  continue;
7726
- if (controlFlow.type === "for_statement" && loopIterationIsProvablyStable(controlFlow, context, staticImports, fn, stableShapes))
7707
+ if (controlFlow.type === "for_statement" && loopIterationIsProvablyStable(controlFlow, staticImports, fn, stableShapes))
7727
7708
  continue;
7728
7709
  const kind = controlFlow.type.replaceAll("_", " ");
7729
7710
  const loopLike = controlFlow.type === "for_statement" || controlFlow.type === "while_statement" || controlFlow.type === "repeat_statement";
@@ -8319,8 +8300,7 @@ async function scanPath(target = ".", options = {}) {
8319
8300
  minSeverity,
8320
8301
  categories: options.categories ? [...options.categories].sort() : [],
8321
8302
  respectInlineDisables,
8322
- rules: config.rules ?? {},
8323
- ignoredTags: [...config.ignoredTags ?? []].sort()
8303
+ rules: config.rules ?? {}
8324
8304
  });
8325
8305
  const canCacheWholeReport = canPersistCache && targetIsProjectRoot && options.files === undefined && deadlineAt === undefined;
8326
8306
  const previousReport = canCacheWholeReport ? previousCachedReport(cacheSession, reportCacheKey) : null;
@@ -8442,7 +8422,7 @@ async function scanPath(target = ".", options = {}) {
8442
8422
  for (const rule of rules) {
8443
8423
  if (categorySet && !categorySet.has(rule.category))
8444
8424
  continue;
8445
- const severity = effectiveSeverity(rule.severity, rule.id, config, ruleTags(rule));
8425
+ const severity = effectiveSeverity(rule.severity, rule.id, config);
8446
8426
  if (!severity)
8447
8427
  continue;
8448
8428
  const findings = rule.run(context);
@@ -8648,12 +8628,11 @@ var DEFAULT_SETTINGS = {
8648
8628
  project: "*"
8649
8629
  };
8650
8630
  var GITHUB_WORKFLOW = path11.join(".github", "workflows", "react-luau-doctor.yml");
8651
- var GITHUB_LOCAL_ACTION = path11.join(".github", "actions", "react-luau-doctor", "action.yml");
8652
8631
  var GITLAB_WORKFLOW = ".gitlab-ci.yml";
8653
8632
  var SUMMARY_MARKER = "<!-- react-luau-doctor:summary -->";
8654
8633
  var REVIEW_MARKER = "<!-- react-luau-doctor:review -->";
8655
8634
  var MAX_REVIEW_COMMENTS = 20;
8656
- var PACKAGE_VERSION = package_default.version;
8635
+ var PACKAGE_SPEC = `${package_default.name}@${package_default.version}`;
8657
8636
  function yamlString(value) {
8658
8637
  return JSON.stringify(value);
8659
8638
  }
@@ -8682,92 +8661,25 @@ jobs:
8682
8661
  - uses: actions/checkout@v5
8683
8662
  with:
8684
8663
  fetch-depth: 0
8685
- - uses: ./.github/actions/react-luau-doctor
8664
+ - uses: oven-sh/setup-bun@v2
8686
8665
  with:
8687
- blocking: ${settings.blocking}
8688
- scope: ${settings.scope}
8689
- comment: ${settings.comment}
8690
- review-comments: ${settings.reviewComments}
8691
- commit-status: ${settings.commitStatus}
8692
- project: ${yamlString(settings.project)}
8693
- directory: ${yamlString(settings.directory)}
8694
- `;
8695
- }
8696
- function renderLocalGitHubAction() {
8697
- return renderPublishedGitHubAction();
8698
- }
8699
- function renderPublishedGitHubAction() {
8700
- return `name: React-Luau Doctor
8701
- description: "Review React-Luau pull requests and report introduced diagnostics."
8702
- inputs:
8703
- blocking:
8704
- description: "Severity that fails the workflow: error, warning, or none."
8705
- default: none
8706
- scope:
8707
- description: "Pull request scan scope: changed, files, lines, or full."
8708
- default: changed
8709
- comment:
8710
- description: "Create or update a sticky pull request summary comment."
8711
- default: "true"
8712
- review-comments:
8713
- description: "Post inline review comments on changed lines."
8714
- default: "true"
8715
- commit-status:
8716
- description: "Publish score and issue counts as a commit status."
8717
- default: "true"
8718
- project:
8719
- description: "Project directories to scan, comma-separated. Asterisk scans the whole directory."
8720
- default: "*"
8721
- directory:
8722
- description: "Project directory to scan."
8723
- default: "."
8724
- outputs:
8725
- score:
8726
- description: "Health score from 0 to 100."
8727
- value: \${{ steps.doctor.outputs.score }}
8728
- total-issues:
8729
- description: "Total diagnostics reported by this run."
8730
- value: \${{ steps.doctor.outputs.total-issues }}
8731
- fixed-issues:
8732
- description: "Number of diagnostics resolved by the pull request."
8733
- value: \${{ steps.doctor.outputs.fixed-issues }}
8734
- error-count:
8735
- description: "Error diagnostic count."
8736
- value: \${{ steps.doctor.outputs.error-count }}
8737
- warning-count:
8738
- description: "Warning diagnostic count."
8739
- value: \${{ steps.doctor.outputs.warning-count }}
8740
- affected-files:
8741
- description: "Number of files with diagnostics."
8742
- value: \${{ steps.doctor.outputs.affected-files }}
8743
- runs:
8744
- using: composite
8745
- steps:
8746
- - uses: oven-sh/setup-bun@v2
8747
- with:
8748
- bun-version: "1.3.14"
8749
- - name: Install parser runtime
8750
- shell: bash
8751
- run: |
8752
- cd "$GITHUB_ACTION_PATH"
8753
- bun install --production --frozen-lockfile
8754
- - id: doctor
8755
- shell: bash
8756
- env:
8757
- GITHUB_TOKEN: \${{ github.token }}
8758
- DOCTOR_DIRECTORY: \${{ inputs.directory }}
8759
- DOCTOR_PROJECT: \${{ inputs.project }}
8760
- DOCTOR_BLOCKING: \${{ inputs.blocking }}
8761
- DOCTOR_SCOPE: \${{ inputs.scope }}
8762
- run: >-
8763
- bun "$GITHUB_ACTION_PATH/dist/cli.js" ci run
8764
- --directory "$DOCTOR_DIRECTORY"
8765
- --project "$DOCTOR_PROJECT"
8766
- --blocking "$DOCTOR_BLOCKING"
8767
- --scope "$DOCTOR_SCOPE"
8768
- \${{ inputs.comment == 'true' && '--comment' || '--no-comment' }}
8769
- \${{ inputs.review-comments == 'true' && '--review-comments' || '--no-review-comments' }}
8770
- \${{ inputs.commit-status == 'true' && '--commit-status' || '--no-commit-status' }}
8666
+ bun-version: "1.4.0"
8667
+ - id: doctor
8668
+ env:
8669
+ GITHUB_TOKEN: \${{ github.token }}
8670
+ DOCTOR_DIRECTORY: ${yamlString(settings.directory)}
8671
+ DOCTOR_PROJECT: ${yamlString(settings.project)}
8672
+ DOCTOR_BLOCKING: ${settings.blocking}
8673
+ DOCTOR_SCOPE: ${settings.scope}
8674
+ run: >-
8675
+ bunx --bun ${PACKAGE_SPEC} ci run
8676
+ --directory "$DOCTOR_DIRECTORY"
8677
+ --project "$DOCTOR_PROJECT"
8678
+ --blocking "$DOCTOR_BLOCKING"
8679
+ --scope "$DOCTOR_SCOPE"
8680
+ ${settings.comment ? "--comment" : "--no-comment"}
8681
+ ${settings.reviewComments ? "--review-comments" : "--no-review-comments"}
8682
+ ${settings.commitStatus ? "--commit-status" : "--no-commit-status"}
8771
8683
  `;
8772
8684
  }
8773
8685
  function renderGitLabWorkflow(settings) {
@@ -8776,41 +8688,16 @@ function renderGitLabWorkflow(settings) {
8776
8688
 
8777
8689
  react-luau-doctor:
8778
8690
  stage: test
8779
- image: oven/bun:1.3.14
8691
+ image: oven/bun:1.4.0
8780
8692
  variables:
8781
8693
  GIT_DEPTH: "0"
8782
8694
  DOCTOR_DIRECTORY: ${yamlString(settings.directory)}
8783
8695
  DOCTOR_SCOPE: ${yamlString(settings.scope)}
8784
8696
  DOCTOR_BLOCKING: ${yamlString(settings.blocking)}
8785
8697
  script:
8786
- - '(cd .gitlab/react-luau-doctor && bun install --production --frozen-lockfile)'
8787
- - 'bun .gitlab/react-luau-doctor/dist/cli.js "$DOCTOR_DIRECTORY" --scope "$DOCTOR_SCOPE" --blocking "$DOCTOR_BLOCKING" --no-color'
8698
+ - 'bunx --bun ${PACKAGE_SPEC} "$DOCTOR_DIRECTORY" --scope "$DOCTOR_SCOPE" --blocking "$DOCTOR_BLOCKING" --no-color'
8788
8699
  `;
8789
8700
  }
8790
- function copyCiRuntime(destination) {
8791
- const root = path11.resolve(import.meta.dir, "..");
8792
- const entries = ["dist", "vendor", "package.json", "LICENSE", "THIRD_PARTY_NOTICES.md"];
8793
- for (const entry of entries) {
8794
- if (!fs9.existsSync(path11.join(root, entry)))
8795
- throw new Error(`Missing ${entry}; run bun run build before installing CI`);
8796
- }
8797
- const lockfile = fs9.existsSync(path11.join(root, "bun.lock")) ? path11.join(root, "bun.lock") : path11.join(root, "dist/runtime-lock.json");
8798
- if (!fs9.existsSync(lockfile))
8799
- throw new Error("Missing runtime lockfile; rebuild or reinstall Doctor");
8800
- fs9.mkdirSync(destination, { recursive: true });
8801
- const copiedLock = path11.join(destination, "bun.lock");
8802
- if (path11.resolve(lockfile) !== path11.resolve(copiedLock))
8803
- fs9.copyFileSync(lockfile, copiedLock);
8804
- const files = [copiedLock];
8805
- for (const entry of entries) {
8806
- const target = path11.join(destination, entry);
8807
- if (path11.resolve(root, entry) === path11.resolve(target))
8808
- continue;
8809
- fs9.cpSync(path11.join(root, entry), target, { recursive: true });
8810
- files.push(target);
8811
- }
8812
- return files;
8813
- }
8814
8701
  function parseBoolean(value, name) {
8815
8702
  if (value === "true")
8816
8703
  return true;
@@ -8887,24 +8774,22 @@ function parseManagedWorkflowSettings(filename) {
8887
8774
  if (!fs9.existsSync(filename))
8888
8775
  return {};
8889
8776
  const text = fs9.readFileSync(filename, "utf8");
8890
- if (filename.endsWith(".gitlab-ci.yml")) {
8891
- const scope2 = text.match(/--scope\s+(full|files|changed|lines)/)?.[1];
8892
- const blocking2 = text.match(/--blocking\s+(error|warning|none)/)?.[1];
8893
- return {
8894
- provider: "gitlab",
8895
- scope: scope2 ? parseScope(scope2) : DEFAULT_SETTINGS.scope,
8896
- blocking: blocking2 ? parseBlocking(blocking2) : "none"
8897
- };
8898
- }
8899
8777
  const match = (name) => text.match(new RegExp(`^\\s*${name}:\\s*(.+?)\\s*$`, "m"))?.[1]?.replace(/^['"]|['"]$/g, "");
8900
- const result = {};
8901
- const blocking = match("blocking");
8902
- const scope = match("scope");
8903
- const comment = match("comment");
8904
- const reviewComments = match("review-comments");
8905
- const commitStatus = match("commit-status");
8906
- const project = match("project");
8907
- const directory = match("directory");
8778
+ const result = { provider: filename.endsWith(".gitlab-ci.yml") ? "gitlab" : "github" };
8779
+ const blocking = match("DOCTOR_BLOCKING") ?? match("blocking");
8780
+ const scope = match("DOCTOR_SCOPE") ?? match("scope");
8781
+ const project = match("DOCTOR_PROJECT") ?? match("project");
8782
+ const directory = match("DOCTOR_DIRECTORY") ?? match("directory");
8783
+ const flag = (enabled, disabled) => {
8784
+ if (text.includes(disabled))
8785
+ return false;
8786
+ if (text.includes(enabled))
8787
+ return true;
8788
+ return;
8789
+ };
8790
+ const comment = match("comment") ?? flag("--comment", "--no-comment")?.toString();
8791
+ const reviewComments = match("review-comments") ?? flag("--review-comments", "--no-review-comments")?.toString();
8792
+ const commitStatus = match("commit-status") ?? flag("--commit-status", "--no-commit-status")?.toString();
8908
8793
  if (blocking)
8909
8794
  result.blocking = parseBlocking(blocking);
8910
8795
  if (scope)
@@ -8954,21 +8839,16 @@ async function completeSettings(options, current, action) {
8954
8839
  function writeManagedCi(cwd, settings) {
8955
8840
  if (settings.provider === "github") {
8956
8841
  const workflow2 = path11.join(cwd, GITHUB_WORKFLOW);
8957
- const action = path11.join(cwd, GITHUB_LOCAL_ACTION);
8958
- const runtimeFiles2 = copyCiRuntime(path11.dirname(action));
8959
8842
  fs9.mkdirSync(path11.dirname(workflow2), { recursive: true });
8960
- fs9.mkdirSync(path11.dirname(action), { recursive: true });
8961
8843
  fs9.writeFileSync(workflow2, renderGitHubWorkflow(settings));
8962
- fs9.writeFileSync(action, renderLocalGitHubAction());
8963
- return [workflow2, action, ...runtimeFiles2];
8844
+ return [workflow2];
8964
8845
  }
8965
8846
  const workflow = path11.join(cwd, GITLAB_WORKFLOW);
8966
8847
  if (fs9.existsSync(workflow) && !fs9.readFileSync(workflow, "utf8").includes("react-luau-doctor")) {
8967
8848
  throw new Error(".gitlab-ci.yml already exists and is not managed by React-Luau Doctor; add the generated job manually instead of overwriting it");
8968
8849
  }
8969
- const runtimeFiles = copyCiRuntime(path11.join(cwd, ".gitlab", "react-luau-doctor"));
8970
8850
  fs9.writeFileSync(workflow, renderGitLabWorkflow(settings));
8971
- return [workflow, ...runtimeFiles];
8851
+ return [workflow];
8972
8852
  }
8973
8853
  function runCommand(cwd, command, args, allowFailure = false) {
8974
8854
  const result = spawnSync2(command, args, { cwd, encoding: "utf8", windowsHide: true });
@@ -9293,7 +9173,7 @@ function parseCiRunOptions(argv) {
9293
9173
  }
9294
9174
  return settings;
9295
9175
  }
9296
- async function runCiAction(argv) {
9176
+ async function runCiJob(argv) {
9297
9177
  const settings = parseCiRunOptions(argv);
9298
9178
  const eventName = process.env.GITHUB_EVENT_NAME ?? "";
9299
9179
  const event = githubEvent();
@@ -9354,7 +9234,7 @@ async function runCiCommand(argv) {
9354
9234
  return;
9355
9235
  }
9356
9236
  if (action === "run") {
9357
- await runCiAction(argv.slice(1));
9237
+ await runCiJob(argv.slice(1));
9358
9238
  return;
9359
9239
  }
9360
9240
  throw new Error("ci requires install, config, or upgrade");
@@ -9830,7 +9710,7 @@ function createProgressRenderer(options = {}) {
9830
9710
  }
9831
9711
 
9832
9712
  // src/cli.ts
9833
- var VERSION = package_default.version;
9713
+ var VERSION2 = package_default.version;
9834
9714
  var SCOPES2 = new Set(["full", "files", "changed", "lines"]);
9835
9715
  var BLOCKING = new Set(["error", "warning", "none"]);
9836
9716
  var SEVERITIES2 = new Set(["suggestion", "warning", "error"]);
@@ -9840,7 +9720,7 @@ process.stdout.on("error", (error) => {
9840
9720
  throw error;
9841
9721
  });
9842
9722
  function usage() {
9843
- return `React-Luau Doctor ${VERSION}
9723
+ return `React-Luau Doctor ${VERSION2}
9844
9724
 
9845
9725
  Usage:
9846
9726
  react-luau-doctor [directory] [options]
@@ -9849,9 +9729,6 @@ Usage:
9849
9729
  react-luau-doctor rules <command>
9850
9730
 
9851
9731
  Scan options:
9852
- --lint / --no-lint Enable or skip React-Luau analysis
9853
- --dead-code / --no-dead-code React Doctor compatibility flag; no Luau dead-code pass yet
9854
- --supply-chain / --no-supply-chain React Doctor compatibility flag; no Roblox supply-chain pass yet
9855
9732
  --verbose Show every finding and per-file details
9856
9733
  --debug Print resolved local scan details to stderr
9857
9734
  --output-dir <dir> Write report.json, diagnostics.json, and summary.json
@@ -9859,35 +9736,26 @@ Scan options:
9859
9736
  --json Output one structured JSON report
9860
9737
  --json-compact Emit compact JSON instead of indented JSON
9861
9738
  --json-out <path> Write the JSON report to a file
9862
- -y, --yes Skip prompts; accepted for React Doctor CLI compatibility
9863
- --no-parallel Force serial scanning (the Luau analyzer is currently serial)
9864
9739
  --project <name> Select project directories, comma-separated
9865
9740
  --scope <value> full, files, changed, or lines
9866
- --full Legacy alias for --scope full
9867
9741
  --base <ref> Git base ref for files/changed/lines scope
9868
9742
  --include-untracked Include untracked files in git scopes
9869
9743
  --diff [base] Alias for changed scope; false forces full scope
9870
9744
  --changed-files-from <file> Read changed paths from a newline-delimited file
9871
9745
  --no-score Hide the local score
9872
9746
  --category <category> Only report a category; repeatable
9873
- --no-telemetry React Doctor-compatible alias for --no-score
9874
- --offline Legacy alias for --no-telemetry
9875
9747
  --staged Scan staged git-index content only
9876
9748
  --max-duration <seconds> Stop after a shared scan time budget and report partial results
9877
9749
  --blocking <level> Exit gate: error, warning, or none
9878
- --fail-on <level> Legacy alias for --blocking
9879
9750
  --respect-inline-disables Respect react-luau-doctor inline suppression comments
9880
9751
  --no-respect-inline-disables Ignore inline suppressions for audit scans
9881
9752
  --warnings / --no-warnings Show warnings (default) or errors only
9882
9753
  --annotations Emit GitHub Actions workflow annotations
9883
- --explain <file:line> Legacy inline form of the why command
9884
- --why <file:line> Alias for --explain
9885
9754
  --no-color Disable automatic ANSI colors
9886
9755
  --no-cache Disable the persistent OS-level analysis cache
9887
9756
 
9888
9757
  React-Luau Doctor options:
9889
9758
  --min-severity <level> suggestion, warning, or error
9890
- --rules List available rules
9891
9759
  -v, --version Print the version
9892
9760
  -h, --help Show this help
9893
9761
 
@@ -9898,17 +9766,15 @@ CI commands:
9898
9766
  Reporting toggles: --comment/--no-comment, --review-comments/--no-review-comments, --commit-status/--no-commit-status
9899
9767
 
9900
9768
  Rules commands:
9901
- rules list [--category <name>] [--tag <name>] [--framework <name>] [--configured] [--json]
9769
+ rules list [--category <name>] [--configured] [--json]
9902
9770
  rules explain <rule> [--json]
9903
9771
  rules set <rule> <severity>
9904
9772
  rules enable <rule> [--severity <level>]
9905
9773
  rules disable <rule>
9906
9774
  rules category <category> <severity>
9907
- rules ignore-tag <tag>
9908
- rules unignore-tag <tag>
9909
9775
 
9910
9776
  Config:
9911
- react-luau-doctor.config.json or doctor.config.json
9777
+ react-luau-doctor.config.json
9912
9778
  `;
9913
9779
  }
9914
9780
  function splitLongOption(arg) {
@@ -10309,17 +10175,12 @@ function parseArgs(argv) {
10309
10175
  scoreOnly: false,
10310
10176
  json: false,
10311
10177
  jsonCompact: false,
10312
- yes: false,
10313
- noParallel: false,
10314
- full: false,
10315
10178
  includeUntracked: false,
10316
10179
  categories: [],
10317
- noTelemetry: false,
10318
10180
  staged: false,
10319
10181
  annotations: false,
10320
10182
  noColor: false,
10321
10183
  noCache: false,
10322
- listRules: false,
10323
10184
  help: false,
10324
10185
  version: false
10325
10186
  };
@@ -10327,19 +10188,7 @@ function parseArgs(argv) {
10327
10188
  for (let index = 0;index < argv.length; index += 1) {
10328
10189
  const raw = argv[index];
10329
10190
  const { name: arg, inlineValue } = splitLongOption(raw);
10330
- if (arg === "--lint")
10331
- options.lint = true;
10332
- else if (arg === "--no-lint")
10333
- options.lint = false;
10334
- else if (arg === "--dead-code")
10335
- options.deadCode = true;
10336
- else if (arg === "--no-dead-code")
10337
- options.deadCode = false;
10338
- else if (arg === "--supply-chain")
10339
- options.supplyChain = true;
10340
- else if (arg === "--no-supply-chain")
10341
- options.supplyChain = false;
10342
- else if (arg === "--verbose")
10191
+ if (arg === "--verbose")
10343
10192
  options.verbose = true;
10344
10193
  else if (arg === "--debug")
10345
10194
  options.debug = true;
@@ -10349,20 +10198,11 @@ function parseArgs(argv) {
10349
10198
  options.json = true;
10350
10199
  else if (arg === "--json-compact")
10351
10200
  options.jsonCompact = true;
10352
- else if (arg === "-y" || arg === "--yes")
10353
- options.yes = true;
10354
- else if (arg === "--no-parallel")
10355
- options.noParallel = true;
10356
- else if (arg === "--full")
10357
- options.full = true;
10358
10201
  else if (arg === "--include-untracked")
10359
10202
  options.includeUntracked = true;
10360
10203
  else if (arg === "--no-score")
10361
10204
  options.showScore = false;
10362
- else if (arg === "--no-telemetry" || arg === "--offline") {
10363
- options.noTelemetry = true;
10364
- options.showScore = false;
10365
- } else if (arg === "--staged")
10205
+ else if (arg === "--staged")
10366
10206
  options.staged = true;
10367
10207
  else if (arg === "--no-respect-inline-disables")
10368
10208
  options.respectInlineDisables = false;
@@ -10378,8 +10218,6 @@ function parseArgs(argv) {
10378
10218
  options.noCache = true;
10379
10219
  else if (arg === "--annotations")
10380
10220
  options.annotations = true;
10381
- else if (arg === "--rules")
10382
- options.listRules = true;
10383
10221
  else if (arg === "--help" || arg === "-h")
10384
10222
  options.help = true;
10385
10223
  else if (arg === "--version" || arg === "-v")
@@ -10439,16 +10277,6 @@ function parseArgs(argv) {
10439
10277
  const parsed = requiredValue(argv, index, arg, inlineValue);
10440
10278
  options.blocking = parseBlocking2(parsed.value, arg);
10441
10279
  index = parsed.nextIndex;
10442
- } else if (arg === "--fail-on") {
10443
- const parsed = requiredValue(argv, index, arg, inlineValue);
10444
- options.failOn = parseBlocking2(parsed.value, arg);
10445
- index = parsed.nextIndex;
10446
- } else if (arg === "--explain" || arg === "--why") {
10447
- const parsed = requiredValue(argv, index, arg, inlineValue);
10448
- if (options.explain !== undefined)
10449
- throw new Error("Use --explain or --why once; they are aliases");
10450
- options.explain = parsed.value;
10451
- index = parsed.nextIndex;
10452
10280
  } else if (arg === "--min-severity") {
10453
10281
  const parsed = requiredValue(argv, index, arg, inlineValue);
10454
10282
  if (!SEVERITIES2.has(parsed.value))
@@ -10467,8 +10295,6 @@ function parseArgs(argv) {
10467
10295
  return options;
10468
10296
  }
10469
10297
  function resolveScope(options, config) {
10470
- if (options.full)
10471
- return { scope: "full", base: options.base ?? config.base };
10472
10298
  if (options.scope)
10473
10299
  return { scope: options.scope, base: options.base ?? config.base };
10474
10300
  if (options.diff !== undefined) {
@@ -10502,12 +10328,9 @@ function validateModeFlags(options, scope) {
10502
10328
  if (options.scoreOnly && options.json)
10503
10329
  throw new Error("Cannot combine --score and --json; pick one output mode");
10504
10330
  if (options.scoreOnly && options.showScore === false)
10505
- throw new Error("Cannot combine --score with --no-score, --no-telemetry, or --offline");
10331
+ throw new Error("Cannot combine --score with --no-score");
10506
10332
  if (options.annotations && (options.json || options.scoreOnly))
10507
10333
  throw new Error("--annotations cannot be combined with --json or --score");
10508
- if (options.explain && (options.json || options.scoreOnly || options.annotations || options.staged)) {
10509
- throw new Error("--explain cannot be combined with --json, --score, --annotations, or --staged");
10510
- }
10511
10334
  }
10512
10335
  function findProjectByName(root, name) {
10513
10336
  const direct = path12.resolve(root, name);
@@ -10599,7 +10422,7 @@ function parseCommandCwd(argv) {
10599
10422
  return { cwd, remaining };
10600
10423
  }
10601
10424
  function ruleSeverityMap(config) {
10602
- return new Map(rules.map((rule) => [rule.id, effectiveSeverity(rule.severity, rule.id, config, ruleTags(rule)) ?? "off"]));
10425
+ return new Map(rules.map((rule) => [rule.id, effectiveSeverity(rule.severity, rule.id, config) ?? "off"]));
10603
10426
  }
10604
10427
  function runRulesCommand(argv) {
10605
10428
  const noColor = argv.includes("--no-color");
@@ -10610,8 +10433,6 @@ function runRulesCommand(argv) {
10610
10433
  const config = loaded.config;
10611
10434
  if (action === "list") {
10612
10435
  let category = null;
10613
- let tag = null;
10614
- let framework = null;
10615
10436
  let configured = false;
10616
10437
  let json = false;
10617
10438
  for (let index = 0;index < remaining.length; index += 1) {
@@ -10627,14 +10448,6 @@ function runRulesCommand(argv) {
10627
10448
  category = normalizeCategory(value);
10628
10449
  if (!category)
10629
10450
  throw new Error(`Unknown category: ${value}`);
10630
- } else if (arg === "--tag") {
10631
- tag = remaining[++index]?.toLowerCase() ?? null;
10632
- if (!tag)
10633
- throw new Error("rules list --tag requires a name");
10634
- } else if (arg === "--framework") {
10635
- framework = remaining[++index]?.toLowerCase() ?? null;
10636
- if (!framework)
10637
- throw new Error("rules list --framework requires a name");
10638
10451
  } else
10639
10452
  throw new Error(`Unknown rules list option: ${arg}`);
10640
10453
  }
@@ -10642,13 +10455,8 @@ function runRulesCommand(argv) {
10642
10455
  const filtered = rules.filter((rule) => {
10643
10456
  if (category && rule.category !== category)
10644
10457
  return false;
10645
- if (tag && !ruleTags(rule).includes(tag))
10458
+ if (configured && config.rules?.[rule.id] === undefined)
10646
10459
  return false;
10647
- if (framework && !ruleFrameworks(rule).includes(framework))
10648
- return false;
10649
- if (configured && config.rules?.[rule.id] === undefined && !(config.ignoredTags ?? []).some((ignored) => ruleTags(rule).includes(ignored.toLowerCase()))) {
10650
- return false;
10651
- }
10652
10460
  return true;
10653
10461
  });
10654
10462
  if (json) {
@@ -10657,8 +10465,6 @@ function runRulesCommand(argv) {
10657
10465
  severity: severityMap.get(rule.id),
10658
10466
  defaultSeverity: rule.severity,
10659
10467
  category: rule.category,
10660
- tags: ruleTags(rule),
10661
- frameworks: ruleFrameworks(rule),
10662
10468
  description: rule.description
10663
10469
  })), null, 2)}
10664
10470
  `);
@@ -10676,14 +10482,12 @@ function runRulesCommand(argv) {
10676
10482
  if (extra.length > 0)
10677
10483
  throw new Error(`Unknown rules explain option: ${extra[0]}`);
10678
10484
  const rule = findRule(requested);
10679
- const currentSeverity = effectiveSeverity(rule.severity, rule.id, config, ruleTags(rule)) ?? "off";
10485
+ const currentSeverity = effectiveSeverity(rule.severity, rule.id, config) ?? "off";
10680
10486
  const result = {
10681
10487
  id: rule.id,
10682
10488
  severity: currentSeverity,
10683
10489
  defaultSeverity: rule.severity,
10684
10490
  category: rule.category,
10685
- tags: ruleTags(rule),
10686
- frameworks: ruleFrameworks(rule),
10687
10491
  description: rule.description,
10688
10492
  example: fixExampleForRule(rule.id)
10689
10493
  };
@@ -10773,22 +10577,6 @@ function runRulesCommand(argv) {
10773
10577
  ])
10774
10578
  }));
10775
10579
  process.stdout.write(`Set ${categoryRules.length} ${category} rules to ${severity} in ${path12.relative(cwd, filename)}
10776
- `);
10777
- return;
10778
- }
10779
- if (action === "ignore-tag" || action === "unignore-tag") {
10780
- const tag = remaining[0]?.toLowerCase();
10781
- if (!tag)
10782
- throw new Error(`rules ${action} requires a tag`);
10783
- const filename = writeConfig(cwd, (current) => {
10784
- const tags = new Set((current.ignoredTags ?? []).map((entry) => entry.toLowerCase()));
10785
- if (action === "ignore-tag")
10786
- tags.add(tag);
10787
- else
10788
- tags.delete(tag);
10789
- return { ...current, ignoredTags: [...tags].sort() };
10790
- });
10791
- process.stdout.write(`${action === "ignore-tag" ? "Ignored" : "Unignored"} tag ${tag} in ${path12.relative(cwd, filename)}
10792
10580
  `);
10793
10581
  return;
10794
10582
  }
@@ -11030,7 +10818,7 @@ function pathIsInside(parent, child2) {
11030
10818
  const relative = path12.relative(parent, child2);
11031
10819
  return relative === "" || !relative.startsWith("..") && !path12.isAbsolute(relative);
11032
10820
  }
11033
- async function runScan(options, onProgress, beforeOutput) {
10821
+ async function runScan(options, onProgress) {
11034
10822
  const commandRoot = process.cwd();
11035
10823
  const target = path12.resolve(commandRoot, options.target);
11036
10824
  if (!fs10.existsSync(target))
@@ -11042,21 +10830,6 @@ async function runScan(options, onProgress, beforeOutput) {
11042
10830
  const resolvedScope = resolveScope(options, config);
11043
10831
  const scope = options.staged && options.scope === undefined ? "files" : resolvedScope.scope;
11044
10832
  validateModeFlags(options, scope);
11045
- if (options.explain) {
11046
- await runWhy(options.explain, commandRoot, options.noColor, !options.noCache, onProgress, beforeOutput);
11047
- return {
11048
- schemaVersion: 1,
11049
- root: commandRoot,
11050
- scannedFiles: 0,
11051
- candidateFiles: 0,
11052
- durationMs: 0,
11053
- score: 100,
11054
- counts: { error: 0, warning: 0, suggestion: 0 },
11055
- diagnostics: [],
11056
- partial: false,
11057
- skippedFiles: []
11058
- };
11059
- }
11060
10833
  if (targetStat.isFile() && (scope !== "full" || options.staged || options.changedFilesFrom)) {
11061
10834
  throw new Error("Git scopes require a directory target; scan the containing project directory instead");
11062
10835
  }
@@ -11078,13 +10851,6 @@ async function runScan(options, onProgress, beforeOutput) {
11078
10851
  const categories = options.categories.length > 0 ? options.categories : config.categories;
11079
10852
  const respectInlineDisables = options.respectInlineDisables ?? config.respectInlineDisables ?? true;
11080
10853
  const deadlineAt = options.maxDurationSeconds !== undefined ? performance.now() + options.maxDurationSeconds * 1000 : undefined;
11081
- const notes = [];
11082
- if (options.deadCode === true)
11083
- notes.push("--dead-code is accepted for React Doctor CLI compatibility, but this port does not yet include a Luau dead-code pass.");
11084
- if (options.supplyChain === true)
11085
- notes.push("--supply-chain is accepted for React Doctor CLI compatibility, but this port does not run Socket.dev or Roblox dependency health checks.");
11086
- if (options.lint === false)
11087
- notes.push("React-Luau analysis was disabled by --no-lint.");
11088
10854
  const reports = [];
11089
10855
  for (const { projectRoot, targetRoot } of projectTargets) {
11090
10856
  let report2;
@@ -11094,15 +10860,7 @@ async function runScan(options, onProgress, beforeOutput) {
11094
10860
  phase: [projectName, progress.phase].filter(Boolean).join(":") || undefined,
11095
10861
  label: projectName && progress.label ? `${projectName}: ${progress.label}` : progress.label
11096
10862
  }) : undefined;
11097
- if (options.lint === false) {
11098
- report2 = await scanPath(targetRoot, {
11099
- projectRoot,
11100
- files: [],
11101
- config,
11102
- cache: !options.noCache
11103
- });
11104
- report2.scope = options.staged ? "staged" : scope;
11105
- } else if (targetStat.isFile()) {
10863
+ if (targetStat.isFile()) {
11106
10864
  report2 = await scanPath(targetRoot, {
11107
10865
  projectRoot,
11108
10866
  config,
@@ -11131,21 +10889,19 @@ async function runScan(options, onProgress, beforeOutput) {
11131
10889
  cache: !options.noCache
11132
10890
  });
11133
10891
  }
11134
- if (notes.length > 0)
11135
- report2.notes = [...report2.notes ?? [], ...notes];
11136
10892
  reports.push({ projectRoot, report: report2 });
11137
10893
  }
11138
10894
  const report = aggregateReports(displayRoot, reports);
11139
10895
  if (options.debug) {
11140
10896
  const debugLines = [
11141
- `[debug] version=${VERSION}`,
10897
+ `[debug] version=${VERSION2}`,
11142
10898
  `[debug] bun=${Bun.version} platform=${process.platform}/${process.arch}`,
11143
10899
  `[debug] target=${target}`,
11144
10900
  `[debug] config=${loaded.filename ?? "none"}`,
11145
10901
  `[debug] scope=${report.scope ?? scope} base=${report.base ?? resolvedScope.base ?? "auto"}`,
11146
10902
  `[debug] projects=${projectTargets.map(({ projectRoot }) => path12.relative(displayRoot, projectRoot) || ".").join(",")}`,
11147
10903
  `[debug] candidates=${report.candidateFiles ?? 0} scanned=${report.scannedFiles} partial=${Boolean(report.partial)}`,
11148
- `[debug] parallel=false${options.noParallel ? " (--no-parallel requested)" : ""}`
10904
+ `[debug] parallel=false`
11149
10905
  ];
11150
10906
  process.stderr.write(`${debugLines.join(`
11151
10907
  `)}
@@ -11187,7 +10943,7 @@ async function main() {
11187
10943
  return;
11188
10944
  }
11189
10945
  if (argv[0] === "version") {
11190
- process.stdout.write(`React-Luau Doctor ${VERSION}
10946
+ process.stdout.write(`React-Luau Doctor ${VERSION2}
11191
10947
  Bun ${Bun.version}
11192
10948
  ${process.platform} ${process.arch}
11193
10949
  ${os2.release()}
@@ -11200,12 +10956,7 @@ ${os2.release()}
11200
10956
  return;
11201
10957
  }
11202
10958
  if (options.version) {
11203
- process.stdout.write(`${VERSION}
11204
- `);
11205
- return;
11206
- }
11207
- if (options.listRules) {
11208
- process.stdout.write(`${renderRulesList(rules, process.stdout.columns ?? 120, new Map, shouldUseColor(options.noColor))}
10959
+ process.stdout.write(`${VERSION2}
11209
10960
  `);
11210
10961
  return;
11211
10962
  }
@@ -11217,12 +10968,10 @@ ${os2.release()}
11217
10968
  });
11218
10969
  let report;
11219
10970
  try {
11220
- report = await runScan(options, (scanProgress) => progress.update(scanProgress), () => progress.clear());
10971
+ report = await runScan(options, (scanProgress) => progress.update(scanProgress));
11221
10972
  } finally {
11222
10973
  progress.clear();
11223
10974
  }
11224
- if (options.explain)
11225
- return;
11226
10975
  const compactJson = options.jsonCompact;
11227
10976
  const json = `${serializeReport(report, compactJson)}
11228
10977
  `;
@@ -11249,7 +10998,7 @@ ${os2.release()}
11249
10998
  } else
11250
10999
  process.stdout.write(`${renderTextReport(report, showScore, colorized, verbose, process.stdout.columns ?? 120)}
11251
11000
  `);
11252
- const blocking = options.blocking ?? options.failOn ?? config.blocking ?? config.failOn ?? "error";
11001
+ const blocking = options.blocking ?? config.blocking ?? "error";
11253
11002
  if (shouldBlock2(report, blocking))
11254
11003
  process.exitCode = 1;
11255
11004
  } catch (error) {
@@ -11261,5 +11010,5 @@ ${os2.release()}
11261
11010
  }
11262
11011
  main();
11263
11012
 
11264
- //# debugId=DD15F49C505776FC64756E2164756E21
11013
+ //# debugId=269B394ED6CE708464756E2164756E21
11265
11014
  //# sourceMappingURL=cli.js.map