react-doctor 0.9.1 → 0.9.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 CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="b226693e-22a9-5a5c-8c9f-2679545b582a")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c86de9ea-b858-5528-89a8-6ff4d377c873")}catch(e){}}();
3
3
  import { createRequire } from "node:module";
4
4
  import * as NodeChildProcess from "node:child_process";
5
5
  import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
@@ -11,6 +11,7 @@ import process$1 from "node:process";
11
11
  import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
12
12
  import * as Crypto from "node:crypto";
13
13
  import crypto, { createHash, randomUUID } from "node:crypto";
14
+ import { ResolverFactory } from "oxc-resolver";
14
15
  import ts from "typescript";
15
16
  import * as OS from "node:os";
16
17
  import os, { tmpdir } from "node:os";
@@ -64616,7 +64617,8 @@ var AmbiguousProjectError = class extends Error {
64616
64617
  }
64617
64618
  };
64618
64619
  const isProjectDiscoveryError = (value) => value instanceof ProjectNotFoundError || value instanceof NoReactDependencyError || value instanceof PackageJsonNotFoundError || value instanceof NotADirectoryError || value instanceof AmbiguousProjectError;
64619
- const SOURCE_FILE_PATTERN = /\.(tsx?|jsx?|mts|mjs)$/;
64620
+ const HTML_FILE_PATTERN = /\.html$/i;
64621
+ const SOURCE_FILE_PATTERN = /\.(?:tsx?|jsx?|mts|mjs|[hH][tT][mM][lL])$/;
64620
64622
  const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
64621
64623
  const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
64622
64624
  const MINIFIED_SNIFF_BYTES = 65536;
@@ -64994,7 +64996,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
64994
64996
  const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
64995
64997
  const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
64996
64998
  const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
64997
- const CORE_PACKAGE_VERSION = "0.9.1";
64999
+ const CORE_PACKAGE_VERSION = "0.9.2";
64998
65000
  const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
64999
65001
  const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
65000
65002
  const SUPPLY_CHAIN_PLUGIN = "socket";
@@ -65238,8 +65240,13 @@ const findMonorepoRoot = (startDirectory) => {
65238
65240
  * byte-equivalent copy.
65239
65241
  */
65240
65242
  const isProjectBoundary = (directory) => NFS.existsSync(Path.join(directory, ".git")) || isMonorepoRoot(directory);
65243
+ const unwrapTypescriptExpression = (expression) => {
65244
+ let currentExpression = expression;
65245
+ while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
65246
+ return currentExpression;
65247
+ };
65241
65248
  const TSCONFIG_FILENAME = "tsconfig.json";
65242
- const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
65249
+ const isLocalModuleSpecifier = (moduleSpecifier) => moduleSpecifier === "." || moduleSpecifier === ".." || moduleSpecifier.startsWith("./") || moduleSpecifier.startsWith("../") || Path.isAbsolute(moduleSpecifier);
65243
65250
  const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
65244
65251
  const resolvePackageExtendsPath = (extendsValue, fromConfigDirectory) => {
65245
65252
  const requireFromConfig = createRequire(Path.join(fromConfigDirectory, "tsconfig.json"));
@@ -65256,7 +65263,12 @@ const resolvePackageExtendsPath = (extendsValue, fromConfigDirectory) => {
65256
65263
  return null;
65257
65264
  };
65258
65265
  const resolveExtendsPath = (extendsValue, fromConfigDirectory) => {
65259
- if (isRelativeExtendsValue(extendsValue)) return ensureJsonExtension(Path.resolve(fromConfigDirectory, extendsValue));
65266
+ if (isLocalModuleSpecifier(extendsValue)) {
65267
+ const resolvedPath = Path.resolve(fromConfigDirectory, extendsValue);
65268
+ if (isFile(resolvedPath)) return resolvedPath;
65269
+ const directoryConfigPath = Path.join(resolvedPath, TSCONFIG_FILENAME);
65270
+ return isFile(directoryConfigPath) ? directoryConfigPath : ensureJsonExtension(resolvedPath);
65271
+ }
65260
65272
  return resolvePackageExtendsPath(extendsValue, fromConfigDirectory);
65261
65273
  };
65262
65274
  const normalizeCompilerOptions = (compilerOptions) => {
@@ -65450,6 +65462,15 @@ const REACT_COMPILER_CONFIG_SOURCE_EXTENSIONS = [
65450
65462
  ".cts",
65451
65463
  ".json"
65452
65464
  ];
65465
+ const REACT_COMPILER_CONFIG_RESOLVER = new ResolverFactory({
65466
+ conditionNames: [
65467
+ "import",
65468
+ "require",
65469
+ "node",
65470
+ "default"
65471
+ ],
65472
+ extensions: REACT_COMPILER_CONFIG_SOURCE_EXTENSIONS
65473
+ });
65453
65474
  const STATIC_EXPORT_OUTPUT_PATTERN = /(?:^|[^.\w])["']?output["']?\s*:\s*["']export["']/m;
65454
65475
  const hasCompilerPackage = (packageJson, compilerPackages) => {
65455
65476
  const allDependencies = {
@@ -65473,6 +65494,18 @@ const hasCompilerPackageInAncestors = (directory, compilerPackages) => {
65473
65494
  return false;
65474
65495
  };
65475
65496
  const resolveImportedConfigFile = (fromFilePath, moduleSpecifier) => {
65497
+ if (!isLocalModuleSpecifier(moduleSpecifier)) {
65498
+ try {
65499
+ const resolvedPath = REACT_COMPILER_CONFIG_RESOLVER.resolveFileSync(fromFilePath, moduleSpecifier).path;
65500
+ if (resolvedPath && isFile(resolvedPath)) return resolvedPath;
65501
+ } catch {}
65502
+ try {
65503
+ const resolvedPath = createRequire(fromFilePath).resolve(moduleSpecifier);
65504
+ return isFile(resolvedPath) ? resolvedPath : null;
65505
+ } catch {
65506
+ return null;
65507
+ }
65508
+ }
65476
65509
  const unresolvedPath = Path.resolve(Path.dirname(fromFilePath), moduleSpecifier);
65477
65510
  const extension = Path.extname(unresolvedPath);
65478
65511
  return (extension ? [unresolvedPath, ...REACT_COMPILER_CONFIG_SOURCE_EXTENSIONS.map((sourceExtension) => `${unresolvedPath.slice(0, -extension.length)}${sourceExtension}`)] : [
@@ -65482,10 +65515,10 @@ const resolveImportedConfigFile = (fromFilePath, moduleSpecifier) => {
65482
65515
  ]).find(isFile) ?? null;
65483
65516
  };
65484
65517
  const parseConfigSourceFile = (filePath, content) => filePath.endsWith(".json") || Path.basename(filePath) === ".babelrc" ? ts.parseJsonText(filePath, content) : ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
65485
- const getStaticPropertyName$1 = (propertyName) => ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName) ? propertyName.text : ts.isComputedPropertyName(propertyName) && ts.isStringLiteralLike(propertyName.expression) ? propertyName.expression.text : null;
65518
+ const getStaticPropertyName$2 = (propertyName) => ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName) ? propertyName.text : ts.isComputedPropertyName(propertyName) && ts.isStringLiteralLike(propertyName.expression) ? propertyName.expression.text : null;
65486
65519
  const isCommonJsConfigExportAssignment = (node, sourceFile) => ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken && (node.left.getText(sourceFile) === "module.exports" || node.left.getText(sourceFile) === "exports.default");
65487
65520
  const hasExportModifier = (node) => ts.canHaveModifiers(node) && Boolean(ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword));
65488
- const getRequireModuleSpecifier = (expression) => {
65521
+ const getRequireModuleSpecifier$1 = (expression) => {
65489
65522
  if (!ts.isCallExpression(expression) || !ts.isIdentifier(expression.expression) || expression.expression.text !== "require" || expression.arguments.length !== 1) return null;
65490
65523
  const moduleSpecifier = expression.arguments[0];
65491
65524
  return moduleSpecifier && ts.isStringLiteralLike(moduleSpecifier) ? moduleSpecifier.text : null;
@@ -65498,7 +65531,7 @@ const hasTopLevelValueBinding = (sourceFile, bindingName) => sourceFile.statemen
65498
65531
  const namedBindings = importClause?.namedBindings;
65499
65532
  return namedBindings ? ts.isNamespaceImport(namedBindings) ? namedBindings.name.text === bindingName : namedBindings.elements.some((element) => element.name.text === bindingName) : false;
65500
65533
  }
65501
- if (ts.isVariableStatement(statement)) return statement.declarationList.declarations.some((declaration) => ts.isIdentifier(declaration.name) && declaration.name.text === bindingName);
65534
+ if (ts.isVariableStatement(statement)) return statement.declarationList.declarations.some((declaration) => bindingNameContainsIdentifier(declaration.name, bindingName));
65502
65535
  return (ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name?.text === bindingName;
65503
65536
  });
65504
65537
  const getImportBinding = (sourceFile, bindingName) => {
@@ -65529,24 +65562,24 @@ const getImportBinding = (sourceFile, bindingName) => {
65529
65562
  for (const declaration of statement.declarationList.declarations) {
65530
65563
  if (!declaration.initializer) continue;
65531
65564
  if (ts.isIdentifier(declaration.name) && declaration.name.text === bindingName) {
65532
- const directModuleSpecifier = getRequireModuleSpecifier(declaration.initializer);
65565
+ const directModuleSpecifier = getRequireModuleSpecifier$1(declaration.initializer);
65533
65566
  if (directModuleSpecifier) return {
65534
65567
  moduleSpecifier: directModuleSpecifier,
65535
65568
  exportName: "default",
65536
65569
  isNamespace: true
65537
65570
  };
65538
- if (ts.isPropertyAccessExpression(declaration.initializer) && getRequireModuleSpecifier(declaration.initializer.expression) !== null) return {
65539
- moduleSpecifier: getRequireModuleSpecifier(declaration.initializer.expression) ?? "",
65571
+ if (ts.isPropertyAccessExpression(declaration.initializer) && getRequireModuleSpecifier$1(declaration.initializer.expression) !== null) return {
65572
+ moduleSpecifier: getRequireModuleSpecifier$1(declaration.initializer.expression) ?? "",
65540
65573
  exportName: declaration.initializer.name.text,
65541
65574
  isNamespace: false
65542
65575
  };
65543
65576
  }
65544
65577
  if (!ts.isObjectBindingPattern(declaration.name)) continue;
65545
65578
  const bindingElement = declaration.name.elements.find((element) => ts.isIdentifier(element.name) && element.name.text === bindingName);
65546
- const moduleSpecifier = getRequireModuleSpecifier(declaration.initializer);
65579
+ const moduleSpecifier = getRequireModuleSpecifier$1(declaration.initializer);
65547
65580
  if (bindingElement && moduleSpecifier) return {
65548
65581
  moduleSpecifier,
65549
- exportName: bindingElement.propertyName ? getStaticPropertyName$1(bindingElement.propertyName) ?? bindingElement.propertyName.getText(sourceFile) : bindingElement.name.getText(sourceFile),
65582
+ exportName: bindingElement.propertyName ? getStaticPropertyName$2(bindingElement.propertyName) ?? bindingElement.propertyName.getText(sourceFile) : bindingElement.name.getText(sourceFile),
65550
65583
  isNamespace: false
65551
65584
  };
65552
65585
  }
@@ -65599,6 +65632,48 @@ const getScopedConfigBinding = (identifier) => {
65599
65632
  };
65600
65633
  };
65601
65634
  const isCompilerTransformModule = (moduleSpecifier, exportName) => moduleSpecifier === "babel-plugin-react-compiler" && exportName === "default" || moduleSpecifier === "@vitejs/plugin-react" && exportName === "reactCompilerPreset";
65635
+ const NODE_MODULE_SPECIFIERS = new Set(["module", "node:module"]);
65636
+ const isConstantVariableInitializer = (node) => ts.isExpression(node) && ts.isVariableDeclaration(node.parent) && ts.isVariableDeclarationList(node.parent.parent) && Boolean(node.parent.parent.flags & ts.NodeFlags.Const);
65637
+ const isNodeCreateRequireCall = (node, analysis) => {
65638
+ if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isTypeAssertionExpression(node) || ts.isSatisfiesExpression(node) || ts.isNonNullExpression(node) || ts.isAwaitExpression(node)) return isNodeCreateRequireCall(node.expression, analysis);
65639
+ if (!ts.isCallExpression(node)) return false;
65640
+ const target = node.expression;
65641
+ if (ts.isIdentifier(target)) {
65642
+ if (analysis.localBindings.has(target.text) || getScopedConfigBinding(target).wasFound) return false;
65643
+ const importBinding = getImportBinding(analysis.sourceFile, target.text);
65644
+ return Boolean(importBinding && NODE_MODULE_SPECIFIERS.has(importBinding.moduleSpecifier) && importBinding.exportName === "createRequire");
65645
+ }
65646
+ if (!ts.isPropertyAccessExpression(target) && !ts.isElementAccessExpression(target)) return false;
65647
+ if ((ts.isPropertyAccessExpression(target) ? target.name.text : target.argumentExpression && ts.isStringLiteralLike(target.argumentExpression) ? target.argumentExpression.text : null) !== "createRequire") return false;
65648
+ const createRequireReceiver = target.expression;
65649
+ if (ts.isCallExpression(createRequireReceiver)) {
65650
+ const requiredModuleSpecifier = getRequireModuleSpecifier$1(createRequireReceiver);
65651
+ if (requiredModuleSpecifier === null || !NODE_MODULE_SPECIFIERS.has(requiredModuleSpecifier) || !ts.isIdentifier(createRequireReceiver.expression)) return false;
65652
+ const requireIdentifier = createRequireReceiver.expression;
65653
+ return !analysis.localBindings.has(requireIdentifier.text) && !getScopedConfigBinding(requireIdentifier).wasFound && !hasTopLevelValueBinding(analysis.sourceFile, requireIdentifier.text);
65654
+ }
65655
+ if (!ts.isIdentifier(createRequireReceiver)) return false;
65656
+ if (analysis.localBindings.has(createRequireReceiver.text) || getScopedConfigBinding(createRequireReceiver).wasFound) return false;
65657
+ const importBinding = getImportBinding(analysis.sourceFile, createRequireReceiver.text);
65658
+ return Boolean(importBinding?.isNamespace && NODE_MODULE_SPECIFIERS.has(importBinding.moduleSpecifier));
65659
+ };
65660
+ const getNodeRequireResolveModuleSpecifier = (callExpression, analysis) => {
65661
+ const [moduleSpecifierNode] = callExpression.arguments;
65662
+ if (!moduleSpecifierNode || !ts.isStringLiteralLike(moduleSpecifierNode)) return null;
65663
+ const target = callExpression.expression;
65664
+ if (!ts.isPropertyAccessExpression(target) && !ts.isElementAccessExpression(target)) return null;
65665
+ if ((ts.isPropertyAccessExpression(target) ? target.name.text : target.argumentExpression && ts.isStringLiteralLike(target.argumentExpression) ? target.argumentExpression.text : null) !== "resolve") return null;
65666
+ if (isNodeCreateRequireCall(target.expression, analysis)) return moduleSpecifierNode.text;
65667
+ if (!ts.isIdentifier(target.expression)) return null;
65668
+ const resolverIdentifier = target.expression;
65669
+ if (analysis.localBindings.has(resolverIdentifier.text)) return null;
65670
+ const scopedBinding = getScopedConfigBinding(resolverIdentifier);
65671
+ const topLevelBinding = scopedBinding.wasFound ? null : getTopLevelBinding(analysis.sourceFile, resolverIdentifier.text);
65672
+ const resolverInitializer = scopedBinding.wasFound ? scopedBinding.initializer : topLevelBinding;
65673
+ if (resolverInitializer === null && !scopedBinding.wasFound && resolverIdentifier.text === "require" && !hasTopLevelValueBinding(analysis.sourceFile, resolverIdentifier.text) && getImportBinding(analysis.sourceFile, resolverIdentifier.text) === null) return moduleSpecifierNode.text;
65674
+ if (!resolverInitializer || !isConstantVariableInitializer(resolverInitializer) || !isNodeCreateRequireCall(resolverInitializer, analysis)) return null;
65675
+ return moduleSpecifierNode.text;
65676
+ };
65602
65677
  const getAssignedPropertyInitializer = (identifier, propertyName) => {
65603
65678
  let bindingScope = null;
65604
65679
  let currentNode = identifier;
@@ -65641,9 +65716,11 @@ const getAssignedPropertyInitializer = (identifier, propertyName) => {
65641
65716
  initializer: assignedInitializer
65642
65717
  };
65643
65718
  };
65644
- const getStaticConfigValueState = (expression, analysis) => {
65645
- let unwrappedExpression = expression;
65646
- while (ts.isParenthesizedExpression(unwrappedExpression) || ts.isAsExpression(unwrappedExpression) || ts.isTypeAssertionExpression(unwrappedExpression) || ts.isSatisfiesExpression(unwrappedExpression) || ts.isNonNullExpression(unwrappedExpression)) unwrappedExpression = unwrappedExpression.expression;
65719
+ const getStaticConfigValueState = (expression, analysis, visitedExpressions = /* @__PURE__ */ new Set()) => {
65720
+ const unwrappedExpression = unwrapTypescriptExpression(expression);
65721
+ if (visitedExpressions.has(unwrappedExpression)) return null;
65722
+ const nextVisitedExpressions = new Set(visitedExpressions);
65723
+ nextVisitedExpressions.add(unwrappedExpression);
65647
65724
  if (unwrappedExpression.kind === ts.SyntaxKind.NullKeyword || ts.isIdentifier(unwrappedExpression) && unwrappedExpression.text === "undefined") return {
65648
65725
  isNullish: true,
65649
65726
  isTruthy: false
@@ -65659,31 +65736,31 @@ const getStaticConfigValueState = (expression, analysis) => {
65659
65736
  if (ts.isIdentifier(unwrappedExpression)) {
65660
65737
  if (analysis.localBindings.has(unwrappedExpression.text)) {
65661
65738
  const localInitializer = analysis.localBindings.get(unwrappedExpression.text);
65662
- return localInitializer ? getStaticConfigValueState(localInitializer, analysis) : null;
65739
+ return localInitializer ? getStaticConfigValueState(localInitializer, analysis, nextVisitedExpressions) : null;
65663
65740
  }
65664
65741
  const topLevelBinding = getTopLevelBinding(analysis.sourceFile, unwrappedExpression.text);
65665
- return topLevelBinding && ts.isExpression(topLevelBinding) ? getStaticConfigValueState(topLevelBinding, analysis) : null;
65742
+ return topLevelBinding && ts.isExpression(topLevelBinding) ? getStaticConfigValueState(topLevelBinding, analysis, nextVisitedExpressions) : null;
65666
65743
  }
65667
65744
  if (ts.isConditionalExpression(unwrappedExpression)) {
65668
- const conditionState = getStaticConfigValueState(unwrappedExpression.condition, analysis);
65669
- if (conditionState !== null) return getStaticConfigValueState(conditionState.isTruthy ? unwrappedExpression.whenTrue : unwrappedExpression.whenFalse, analysis);
65670
- const whenTrueState = getStaticConfigValueState(unwrappedExpression.whenTrue, analysis);
65671
- const whenFalseState = getStaticConfigValueState(unwrappedExpression.whenFalse, analysis);
65745
+ const conditionState = getStaticConfigValueState(unwrappedExpression.condition, analysis, nextVisitedExpressions);
65746
+ if (conditionState !== null) return getStaticConfigValueState(conditionState.isTruthy ? unwrappedExpression.whenTrue : unwrappedExpression.whenFalse, analysis, nextVisitedExpressions);
65747
+ const whenTrueState = getStaticConfigValueState(unwrappedExpression.whenTrue, analysis, nextVisitedExpressions);
65748
+ const whenFalseState = getStaticConfigValueState(unwrappedExpression.whenFalse, analysis, nextVisitedExpressions);
65672
65749
  return whenTrueState !== null && whenFalseState !== null && whenTrueState.isNullish === whenFalseState.isNullish && whenTrueState.isTruthy === whenFalseState.isTruthy ? whenTrueState : null;
65673
65750
  }
65674
65751
  if (ts.isBinaryExpression(unwrappedExpression)) {
65675
- const leftState = getStaticConfigValueState(unwrappedExpression.left, analysis);
65752
+ const leftState = getStaticConfigValueState(unwrappedExpression.left, analysis, nextVisitedExpressions);
65676
65753
  if (unwrappedExpression.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken) {
65677
65754
  if (leftState === null) return null;
65678
- return leftState.isTruthy ? getStaticConfigValueState(unwrappedExpression.right, analysis) : leftState;
65755
+ return leftState.isTruthy ? getStaticConfigValueState(unwrappedExpression.right, analysis, nextVisitedExpressions) : leftState;
65679
65756
  }
65680
65757
  if (unwrappedExpression.operatorToken.kind === ts.SyntaxKind.BarBarToken) {
65681
65758
  if (leftState === null) return null;
65682
- return leftState.isTruthy ? leftState : getStaticConfigValueState(unwrappedExpression.right, analysis);
65759
+ return leftState.isTruthy ? leftState : getStaticConfigValueState(unwrappedExpression.right, analysis, nextVisitedExpressions);
65683
65760
  }
65684
65761
  if (unwrappedExpression.operatorToken.kind === ts.SyntaxKind.QuestionQuestionToken) {
65685
65762
  if (leftState === null) return null;
65686
- return leftState.isNullish ? getStaticConfigValueState(unwrappedExpression.right, analysis) : leftState;
65763
+ return leftState.isNullish ? getStaticConfigValueState(unwrappedExpression.right, analysis, nextVisitedExpressions) : leftState;
65687
65764
  }
65688
65765
  }
65689
65766
  return null;
@@ -65692,25 +65769,27 @@ const isStaticallyDisabledConfigExpression = (expression, analysis) => getStatic
65692
65769
  const isStaticallyTruthyConfigExpression = (expression, analysis) => getStaticConfigValueState(expression, analysis)?.isTruthy === true;
65693
65770
  const isStaticallyNullishConfigExpression = (expression, analysis) => getStaticConfigValueState(expression, analysis)?.isNullish === true;
65694
65771
  const isStaticallyNonNullishConfigExpression = (expression, analysis) => getStaticConfigValueState(expression, analysis)?.isNullish === false;
65695
- const getReactCompilerFlagState = (expression, analysis) => {
65696
- let unwrappedExpression = expression;
65697
- while (ts.isParenthesizedExpression(unwrappedExpression) || ts.isAsExpression(unwrappedExpression) || ts.isTypeAssertionExpression(unwrappedExpression) || ts.isSatisfiesExpression(unwrappedExpression) || ts.isNonNullExpression(unwrappedExpression)) unwrappedExpression = unwrappedExpression.expression;
65772
+ const getReactCompilerFlagState = (expression, analysis, visitedExpressions = /* @__PURE__ */ new Set()) => {
65773
+ const unwrappedExpression = unwrapTypescriptExpression(expression);
65774
+ if (visitedExpressions.has(unwrappedExpression)) return null;
65775
+ const nextVisitedExpressions = new Set(visitedExpressions);
65776
+ nextVisitedExpressions.add(unwrappedExpression);
65698
65777
  if (ts.isIdentifier(unwrappedExpression)) {
65699
65778
  const assignedBinding = getAssignedPropertyInitializer(unwrappedExpression, "reactCompiler");
65700
65779
  if (assignedBinding.wasFound && assignedBinding.initializer) return { isEnabled: !isStaticallyDisabledConfigExpression(assignedBinding.initializer, analysis) };
65701
65780
  if (analysis.localBindings.has(unwrappedExpression.text)) {
65702
65781
  const localInitializer = analysis.localBindings.get(unwrappedExpression.text);
65703
- return localInitializer ? getReactCompilerFlagState(localInitializer, analysis) : null;
65782
+ return localInitializer ? getReactCompilerFlagState(localInitializer, analysis, nextVisitedExpressions) : null;
65704
65783
  }
65705
65784
  const topLevelBinding = getTopLevelBinding(analysis.sourceFile, unwrappedExpression.text);
65706
- return topLevelBinding && ts.isExpression(topLevelBinding) ? getReactCompilerFlagState(topLevelBinding, analysis) : null;
65785
+ return topLevelBinding && ts.isExpression(topLevelBinding) ? getReactCompilerFlagState(topLevelBinding, analysis, nextVisitedExpressions) : null;
65707
65786
  }
65708
65787
  if (!ts.isObjectLiteralExpression(unwrappedExpression)) return null;
65709
65788
  for (const property of [...unwrappedExpression.properties].reverse()) {
65710
- if (ts.isPropertyAssignment(property) && getStaticPropertyName$1(property.name) === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.initializer, analysis) };
65789
+ if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.initializer, analysis) };
65711
65790
  if (ts.isShorthandPropertyAssignment(property) && property.name.text === "reactCompiler") return { isEnabled: !isStaticallyDisabledConfigExpression(property.name, analysis) };
65712
65791
  if (ts.isSpreadAssignment(property)) {
65713
- const spreadState = getReactCompilerFlagState(property.expression, analysis);
65792
+ const spreadState = getReactCompilerFlagState(property.expression, analysis, nextVisitedExpressions);
65714
65793
  if (spreadState) return spreadState;
65715
65794
  }
65716
65795
  }
@@ -65733,8 +65812,8 @@ const getSelectedObjectProperty = (expression, propertyName, analysis, visitedEx
65733
65812
  }
65734
65813
  if (!ts.isObjectLiteralExpression(resolvedExpression)) return null;
65735
65814
  for (const property of [...resolvedExpression.properties].reverse()) {
65736
- if (ts.isPropertyAssignment(property) && getStaticPropertyName$1(property.name) === propertyName) return property.initializer;
65737
- if (ts.isMethodDeclaration(property) && getStaticPropertyName$1(property.name) === propertyName) return property;
65815
+ if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === propertyName) return property.initializer;
65816
+ if (ts.isMethodDeclaration(property) && getStaticPropertyName$2(property.name) === propertyName) return property;
65738
65817
  if (ts.isShorthandPropertyAssignment(property) && property.name.text === propertyName) return property.name;
65739
65818
  if (ts.isSpreadAssignment(property)) {
65740
65819
  const spreadProperty = getSelectedObjectProperty(property.expression, propertyName, analysis, visitedExpressions);
@@ -65765,7 +65844,7 @@ const configExpressionMayDefineProperty = (expression, propertyName, analysis) =
65765
65844
  return resolvedExpression.properties.some((property) => {
65766
65845
  if (ts.isSpreadAssignment(property)) return configExpressionMayDefineProperty(property.expression, propertyName, analysis);
65767
65846
  if (ts.isPropertyAssignment(property) || ts.isShorthandPropertyAssignment(property) || ts.isMethodDeclaration(property) || ts.isGetAccessorDeclaration(property) || ts.isSetAccessorDeclaration(property)) {
65768
- const staticPropertyName = getStaticPropertyName$1(property.name);
65847
+ const staticPropertyName = getStaticPropertyName$2(property.name);
65769
65848
  return staticPropertyName === null || staticPropertyName === propertyName;
65770
65849
  }
65771
65850
  return false;
@@ -65784,7 +65863,7 @@ const getExportedConfigNodes = (sourceFile, exportName) => {
65784
65863
  continue;
65785
65864
  }
65786
65865
  if (ts.isExpressionStatement(statement) && isCommonJsConfigExportAssignment(statement.expression, sourceFile) && exportName !== "default" && ts.isObjectLiteralExpression(statement.expression.right)) for (const property of [...statement.expression.right.properties].reverse()) {
65787
- if (ts.isPropertyAssignment(property) && getStaticPropertyName$1(property.name) === exportName) {
65866
+ if (ts.isPropertyAssignment(property) && getStaticPropertyName$2(property.name) === exportName) {
65788
65867
  exportedNodes.push(property.initializer);
65789
65868
  break;
65790
65869
  }
@@ -65882,6 +65961,11 @@ const analyzeConfigFunction = (functionNode, analysis, allowCompilerTransform, a
65882
65961
  visitReturns(functionNode.body);
65883
65962
  return hasCompiler;
65884
65963
  };
65964
+ const analyzeImportedConfig = ({ analysis, moduleSpecifier, exportName, allowCompilerTransform, argumentsList }) => {
65965
+ const importedFilePath = resolveImportedConfigFile(analysis.filePath, moduleSpecifier);
65966
+ if (!importedFilePath) return null;
65967
+ return analyzeConfigModuleExport(importedFilePath, exportName, allowCompilerTransform, analysis.importDepth + 1, analysis.visitedModules, argumentsList);
65968
+ };
65885
65969
  const analyzeConfigIdentifier = (identifier, analysis, allowCompilerTransform, isCompilerTransformCollection, excludedPropertyNames) => {
65886
65970
  const assignedReactCompiler = getAssignedPropertyInitializer(identifier, "reactCompiler");
65887
65971
  if (assignedReactCompiler.initializer && !isStaticallyDisabledConfigExpression(assignedReactCompiler.initializer, analysis)) return true;
@@ -65902,30 +65986,62 @@ const analyzeConfigIdentifier = (identifier, analysis, allowCompilerTransform, i
65902
65986
  const importBinding = getImportBinding(analysis.sourceFile, identifier.text);
65903
65987
  if (importBinding) {
65904
65988
  if (allowCompilerTransform && isCompilerTransformModule(importBinding.moduleSpecifier, importBinding.exportName)) return true;
65905
- if (!importBinding.moduleSpecifier.startsWith(".")) return false;
65906
- const importedFilePath = resolveImportedConfigFile(analysis.filePath, importBinding.moduleSpecifier);
65907
- return Boolean(importedFilePath && analyzeConfigModuleExport(importedFilePath, importBinding.exportName, allowCompilerTransform, analysis.importDepth + 1, analysis.visitedModules));
65989
+ return Boolean(analyzeImportedConfig({
65990
+ analysis,
65991
+ moduleSpecifier: importBinding.moduleSpecifier,
65992
+ exportName: importBinding.exportName,
65993
+ allowCompilerTransform
65994
+ }));
65908
65995
  }
65909
65996
  const topLevelBinding = getTopLevelBinding(analysis.sourceFile, identifier.text);
65910
65997
  return Boolean(topLevelBinding && analyzeConfigNode(topLevelBinding, analysis, allowCompilerTransform, isCompilerTransformCollection, overriddenPropertyNames));
65911
65998
  };
65912
65999
  const analyzeConfigCallTarget = (callExpression, analysis, allowCompilerTransform) => {
65913
66000
  const target = callExpression.expression;
66001
+ const callableRequiredModuleSpecifier = getRequireModuleSpecifier$1(target);
66002
+ if (callableRequiredModuleSpecifier !== null) {
66003
+ if (ts.isCallExpression(target) && ts.isIdentifier(target.expression) && (analysis.localBindings.has(target.expression.text) || getScopedConfigBinding(target.expression).wasFound || hasTopLevelValueBinding(analysis.sourceFile, "require"))) return false;
66004
+ if (allowCompilerTransform && isCompilerTransformModule(callableRequiredModuleSpecifier, "default")) return true;
66005
+ const hasCompilerTransform = analyzeImportedConfig({
66006
+ analysis,
66007
+ moduleSpecifier: callableRequiredModuleSpecifier,
66008
+ exportName: "default",
66009
+ allowCompilerTransform,
66010
+ argumentsList: callExpression.arguments
66011
+ });
66012
+ if (isLocalModuleSpecifier(callableRequiredModuleSpecifier) || hasCompilerTransform !== null) return Boolean(hasCompilerTransform);
66013
+ return null;
66014
+ }
65914
66015
  if (!ts.isPropertyAccessExpression(target) && !ts.isElementAccessExpression(target)) return null;
65915
66016
  const propertyName = ts.isPropertyAccessExpression(target) ? target.name.text : target.argumentExpression && ts.isStringLiteralLike(target.argumentExpression) ? target.argumentExpression.text : null;
65916
66017
  if (propertyName === null) return null;
65917
- const requiredModuleSpecifier = getRequireModuleSpecifier(target.expression);
66018
+ const requiredModuleSpecifier = getRequireModuleSpecifier$1(target.expression);
65918
66019
  if (requiredModuleSpecifier !== null) {
65919
- if (ts.isCallExpression(target.expression) && ts.isIdentifier(target.expression.expression) && (getScopedConfigBinding(target.expression.expression).wasFound || hasTopLevelValueBinding(analysis.sourceFile, "require"))) return false;
65920
- if (!requiredModuleSpecifier.startsWith(".")) return null;
65921
- const importedFilePath = resolveImportedConfigFile(analysis.filePath, requiredModuleSpecifier);
65922
- return Boolean(importedFilePath && analyzeConfigModuleExport(importedFilePath, propertyName, allowCompilerTransform, analysis.importDepth + 1, analysis.visitedModules, callExpression.arguments));
66020
+ if (ts.isCallExpression(target.expression) && ts.isIdentifier(target.expression.expression) && (analysis.localBindings.has(target.expression.expression.text) || getScopedConfigBinding(target.expression.expression).wasFound || hasTopLevelValueBinding(analysis.sourceFile, "require"))) return false;
66021
+ if (allowCompilerTransform && isCompilerTransformModule(requiredModuleSpecifier, propertyName)) return true;
66022
+ const hasCompilerTransform = analyzeImportedConfig({
66023
+ analysis,
66024
+ moduleSpecifier: requiredModuleSpecifier,
66025
+ exportName: propertyName,
66026
+ allowCompilerTransform,
66027
+ argumentsList: callExpression.arguments
66028
+ });
66029
+ if (isLocalModuleSpecifier(requiredModuleSpecifier) || hasCompilerTransform !== null) return Boolean(hasCompilerTransform);
66030
+ return null;
65923
66031
  }
65924
66032
  if (!ts.isIdentifier(target.expression)) return null;
65925
- const importBinding = getImportBinding(analysis.sourceFile, target.expression.text);
65926
- if (importBinding?.isNamespace && importBinding.moduleSpecifier.startsWith(".")) {
65927
- const importedFilePath = resolveImportedConfigFile(analysis.filePath, importBinding.moduleSpecifier);
65928
- return Boolean(importedFilePath && analyzeConfigModuleExport(importedFilePath, propertyName, allowCompilerTransform, analysis.importDepth + 1, analysis.visitedModules, callExpression.arguments));
66033
+ const importBinding = analysis.localBindings.has(target.expression.text) || getScopedConfigBinding(target.expression).wasFound ? null : getImportBinding(analysis.sourceFile, target.expression.text);
66034
+ if (importBinding?.isNamespace) {
66035
+ if (allowCompilerTransform && isCompilerTransformModule(importBinding.moduleSpecifier, propertyName)) return true;
66036
+ const hasCompilerTransform = analyzeImportedConfig({
66037
+ analysis,
66038
+ moduleSpecifier: importBinding.moduleSpecifier,
66039
+ exportName: propertyName,
66040
+ allowCompilerTransform,
66041
+ argumentsList: callExpression.arguments
66042
+ });
66043
+ if (isLocalModuleSpecifier(importBinding.moduleSpecifier) || hasCompilerTransform !== null) return Boolean(hasCompilerTransform);
66044
+ return null;
65929
66045
  }
65930
66046
  const selectedProperty = getSelectedObjectProperty(target.expression, propertyName, analysis);
65931
66047
  if (selectedProperty === null) return null;
@@ -65943,25 +66059,29 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
65943
66059
  const reactCompilerFlagState = excludedPropertyNames?.has("reactCompiler") ? null : getReactCompilerFlagState(node, analysis);
65944
66060
  if (reactCompilerFlagState?.isEnabled) return true;
65945
66061
  for (const [propertyIndex, property] of node.properties.entries()) if (ts.isPropertyAssignment(property)) {
65946
- const propertyName = getStaticPropertyName$1(property.name);
66062
+ const propertyName = getStaticPropertyName$2(property.name);
65947
66063
  if (propertyName && excludedPropertyNames?.has(propertyName)) continue;
65948
66064
  if (propertyName === "reactCompiler") continue;
65949
- if ((propertyName === "plugins" || propertyName === "presets") && node.properties.slice(propertyIndex + 1).some((laterProperty) => ts.isSpreadAssignment(laterProperty) && configExpressionMayDefineProperty(laterProperty.expression, propertyName, analysis) || (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && getStaticPropertyName$1(laterProperty.name) === propertyName)) continue;
66065
+ if ((propertyName === "plugins" || propertyName === "presets") && node.properties.slice(propertyIndex + 1).some((laterProperty) => ts.isSpreadAssignment(laterProperty) && configExpressionMayDefineProperty(laterProperty.expression, propertyName, analysis) || (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && getStaticPropertyName$2(laterProperty.name) === propertyName)) continue;
65950
66066
  const propertyAllowsCompilerTransform = propertyName === "plugins" || propertyName === "presets";
65951
- if (propertyName === "extends" && ts.isStringLiteralLike(property.initializer) && property.initializer.text.startsWith(".")) {
65952
- const extendedFilePath = resolveImportedConfigFile(analysis.filePath, property.initializer.text);
65953
- if (extendedFilePath && analyzeConfigModuleExport(extendedFilePath, "default", false, analysis.importDepth + 1, analysis.visitedModules)) return true;
66067
+ if (propertyName === "extends" && ts.isStringLiteralLike(property.initializer)) {
66068
+ if (analyzeImportedConfig({
66069
+ analysis,
66070
+ moduleSpecifier: property.initializer.text,
66071
+ exportName: "default",
66072
+ allowCompilerTransform: false
66073
+ })) return true;
65954
66074
  }
65955
66075
  if (analyzeConfigNode(property.initializer, analysis, propertyAllowsCompilerTransform, propertyAllowsCompilerTransform)) return true;
65956
66076
  } else if (ts.isShorthandPropertyAssignment(property)) {
65957
66077
  if (excludedPropertyNames?.has(property.name.text)) continue;
65958
66078
  if (property.name.text === "reactCompiler") continue;
65959
- if ((property.name.text === "plugins" || property.name.text === "presets") && node.properties.slice(propertyIndex + 1).some((laterProperty) => ts.isSpreadAssignment(laterProperty) && configExpressionMayDefineProperty(laterProperty.expression, property.name.text, analysis) || (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && getStaticPropertyName$1(laterProperty.name) === property.name.text)) continue;
66079
+ if ((property.name.text === "plugins" || property.name.text === "presets") && node.properties.slice(propertyIndex + 1).some((laterProperty) => ts.isSpreadAssignment(laterProperty) && configExpressionMayDefineProperty(laterProperty.expression, property.name.text, analysis) || (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && getStaticPropertyName$2(laterProperty.name) === property.name.text)) continue;
65960
66080
  const propertyAllowsCompilerTransform = property.name.text === "plugins" || property.name.text === "presets";
65961
66081
  if (analyzeConfigNode(property.name, analysis, propertyAllowsCompilerTransform, propertyAllowsCompilerTransform)) return true;
65962
66082
  } else if (ts.isSpreadAssignment(property)) {
65963
66083
  if (reactCompilerFlagState && getReactCompilerFlagState(property.expression, analysis) !== null) continue;
65964
- if (node.properties.slice(propertyIndex + 1).some((laterProperty) => (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && (getStaticPropertyName$1(laterProperty.name) === "plugins" || getStaticPropertyName$1(laterProperty.name) === "presets"))) continue;
66084
+ if (node.properties.slice(propertyIndex + 1).some((laterProperty) => (ts.isPropertyAssignment(laterProperty) || ts.isShorthandPropertyAssignment(laterProperty)) && (getStaticPropertyName$2(laterProperty.name) === "plugins" || getStaticPropertyName$2(laterProperty.name) === "presets"))) continue;
65965
66085
  if (analyzeConfigNode(property.expression, analysis, allowCompilerTransform, false, excludedPropertyNames)) return true;
65966
66086
  }
65967
66087
  return false;
@@ -65974,23 +66094,36 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
65974
66094
  });
65975
66095
  }
65976
66096
  if (ts.isCallExpression(node)) {
66097
+ const resolvedModuleSpecifier = getNodeRequireResolveModuleSpecifier(node, analysis);
66098
+ if (resolvedModuleSpecifier !== null) return allowCompilerTransform && isCompilerTransformModule(resolvedModuleSpecifier, "default");
65977
66099
  const callTargetResult = analyzeConfigCallTarget(node, analysis, allowCompilerTransform);
65978
66100
  if (callTargetResult !== null) return callTargetResult;
65979
- const directModuleSpecifier = getRequireModuleSpecifier(node);
66101
+ const directModuleSpecifier = getRequireModuleSpecifier$1(node);
65980
66102
  const isRequireShadowed = analysis.localBindings.has("require") || ts.isIdentifier(node.expression) && getScopedConfigBinding(node.expression).wasFound || hasTopLevelValueBinding(analysis.sourceFile, "require");
65981
66103
  if (directModuleSpecifier && isRequireShadowed) return analyzeConfigNode(node.expression, analysis, allowCompilerTransform);
65982
66104
  if (directModuleSpecifier && !isRequireShadowed) {
65983
66105
  if (allowCompilerTransform && isCompilerTransformModule(directModuleSpecifier, "default")) return true;
65984
- if (directModuleSpecifier.startsWith(".")) {
65985
- const importedFilePath = resolveImportedConfigFile(analysis.filePath, directModuleSpecifier);
65986
- if (importedFilePath && analyzeConfigModuleExport(importedFilePath, "default", allowCompilerTransform, analysis.importDepth + 1, analysis.visitedModules)) return true;
65987
- }
66106
+ if (analyzeImportedConfig({
66107
+ analysis,
66108
+ moduleSpecifier: directModuleSpecifier,
66109
+ exportName: "default",
66110
+ allowCompilerTransform
66111
+ })) return true;
65988
66112
  }
65989
66113
  if (ts.isIdentifier(node.expression)) {
65990
- const importBinding = getImportBinding(analysis.sourceFile, node.expression.text);
65991
- if (importBinding?.moduleSpecifier.startsWith(".")) {
65992
- const importedFilePath = resolveImportedConfigFile(analysis.filePath, importBinding.moduleSpecifier);
65993
- return Boolean(importedFilePath && analyzeConfigModuleExport(importedFilePath, importBinding.exportName, allowCompilerTransform, analysis.importDepth + 1, analysis.visitedModules, node.arguments));
66114
+ if (!analysis.localBindings.has(node.expression.text) && !getScopedConfigBinding(node.expression).wasFound) {
66115
+ const importBinding = getImportBinding(analysis.sourceFile, node.expression.text);
66116
+ if (importBinding) {
66117
+ if (allowCompilerTransform && isCompilerTransformModule(importBinding.moduleSpecifier, importBinding.exportName)) return true;
66118
+ const hasCompilerTransform = analyzeImportedConfig({
66119
+ analysis,
66120
+ moduleSpecifier: importBinding.moduleSpecifier,
66121
+ exportName: importBinding.exportName,
66122
+ allowCompilerTransform,
66123
+ argumentsList: node.arguments
66124
+ });
66125
+ if (isLocalModuleSpecifier(importBinding.moduleSpecifier) || hasCompilerTransform !== null) return Boolean(hasCompilerTransform);
66126
+ }
65994
66127
  }
65995
66128
  const topLevelBinding = getTopLevelBinding(analysis.sourceFile, node.expression.text);
65996
66129
  if (topLevelBinding && ts.isFunctionLike(topLevelBinding) && analyzeConfigFunction(topLevelBinding, analysis, allowCompilerTransform, node.arguments)) return true;
@@ -65998,27 +66131,31 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
65998
66131
  return analyzeConfigNode(node.expression, analysis, allowCompilerTransform) || node.arguments.some((argument) => analyzeConfigNode(argument, analysis, false));
65999
66132
  }
66000
66133
  if (ts.isPropertyAccessExpression(node)) {
66001
- const requiredModuleSpecifier = getRequireModuleSpecifier(node.expression);
66134
+ const requiredModuleSpecifier = getRequireModuleSpecifier$1(node.expression);
66002
66135
  if (requiredModuleSpecifier) {
66003
- if (ts.isCallExpression(node.expression) && ts.isIdentifier(node.expression.expression) && (getScopedConfigBinding(node.expression.expression).wasFound || hasTopLevelValueBinding(analysis.sourceFile, "require"))) return false;
66004
- if (requiredModuleSpecifier.startsWith(".")) {
66005
- const importedFilePath = resolveImportedConfigFile(analysis.filePath, requiredModuleSpecifier);
66006
- return Boolean(importedFilePath && analyzeConfigModuleExport(importedFilePath, node.name.text, allowCompilerTransform, analysis.importDepth + 1, analysis.visitedModules));
66007
- }
66136
+ if (ts.isCallExpression(node.expression) && ts.isIdentifier(node.expression.expression) && (analysis.localBindings.has(node.expression.expression.text) || getScopedConfigBinding(node.expression.expression).wasFound || hasTopLevelValueBinding(analysis.sourceFile, "require"))) return false;
66137
+ if (analyzeImportedConfig({
66138
+ analysis,
66139
+ moduleSpecifier: requiredModuleSpecifier,
66140
+ exportName: node.name.text,
66141
+ allowCompilerTransform
66142
+ })) return true;
66008
66143
  return allowCompilerTransform && isCompilerTransformModule(requiredModuleSpecifier, node.name.text);
66009
66144
  }
66010
66145
  if (ts.isIdentifier(node.expression)) {
66011
- if (analysis.localBindings.has(node.expression.text)) {
66012
- const localInitializer = analysis.localBindings.get(node.expression.text);
66013
- return Boolean(localInitializer && analyzeConfigNode(localInitializer, analysis, allowCompilerTransform));
66146
+ if (analysis.localBindings.has(node.expression.text) || getScopedConfigBinding(node.expression).wasFound) {
66147
+ const selectedProperty = getSelectedObjectProperty(node.expression, node.name.text, analysis);
66148
+ return Boolean(selectedProperty && analyzeConfigNode(selectedProperty, analysis, allowCompilerTransform));
66014
66149
  }
66015
66150
  const importBinding = getImportBinding(analysis.sourceFile, node.expression.text);
66016
66151
  if (importBinding?.isNamespace) {
66017
66152
  if (allowCompilerTransform && isCompilerTransformModule(importBinding.moduleSpecifier, node.name.text)) return true;
66018
- if (importBinding.moduleSpecifier.startsWith(".")) {
66019
- const importedFilePath = resolveImportedConfigFile(analysis.filePath, importBinding.moduleSpecifier);
66020
- if (importedFilePath && analyzeConfigModuleExport(importedFilePath, node.name.text, allowCompilerTransform, analysis.importDepth + 1, analysis.visitedModules)) return true;
66021
- }
66153
+ if (analyzeImportedConfig({
66154
+ analysis,
66155
+ moduleSpecifier: importBinding.moduleSpecifier,
66156
+ exportName: node.name.text,
66157
+ allowCompilerTransform
66158
+ })) return true;
66022
66159
  return false;
66023
66160
  }
66024
66161
  const selectedProperty = getSelectedObjectProperty(node.expression, node.name.text, analysis);
@@ -66029,13 +66166,19 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
66029
66166
  }
66030
66167
  if (ts.isElementAccessExpression(node) && node.argumentExpression && ts.isStringLiteralLike(node.argumentExpression)) {
66031
66168
  if (ts.isIdentifier(node.expression)) {
66169
+ if (analysis.localBindings.has(node.expression.text) || getScopedConfigBinding(node.expression).wasFound) {
66170
+ const selectedProperty = getSelectedObjectProperty(node.expression, node.argumentExpression.text, analysis);
66171
+ return Boolean(selectedProperty && analyzeConfigNode(selectedProperty, analysis, allowCompilerTransform));
66172
+ }
66032
66173
  const importBinding = getImportBinding(analysis.sourceFile, node.expression.text);
66033
66174
  if (importBinding?.isNamespace) {
66034
66175
  if (allowCompilerTransform && isCompilerTransformModule(importBinding.moduleSpecifier, node.argumentExpression.text)) return true;
66035
- if (importBinding.moduleSpecifier.startsWith(".")) {
66036
- const importedFilePath = resolveImportedConfigFile(analysis.filePath, importBinding.moduleSpecifier);
66037
- return Boolean(importedFilePath && analyzeConfigModuleExport(importedFilePath, node.argumentExpression.text, allowCompilerTransform, analysis.importDepth + 1, analysis.visitedModules));
66038
- }
66176
+ return Boolean(analyzeImportedConfig({
66177
+ analysis,
66178
+ moduleSpecifier: importBinding.moduleSpecifier,
66179
+ exportName: node.argumentExpression.text,
66180
+ allowCompilerTransform
66181
+ }));
66039
66182
  }
66040
66183
  const selectedProperty = getSelectedObjectProperty(node.expression, node.argumentExpression.text, analysis);
66041
66184
  if (selectedProperty) return analyzeConfigNode(selectedProperty, analysis, allowCompilerTransform);
@@ -66073,14 +66216,19 @@ const analyzeConfigNode = (node, analysis, allowCompilerTransform, isCompilerTra
66073
66216
  };
66074
66217
  const hasCompilerInConfigFile = (filePath) => analyzeConfigModuleExport(filePath, "default", false, 0, /* @__PURE__ */ new Set());
66075
66218
  const hasCompilerInConfigFiles = (directory, filenames) => filenames.some((filename) => hasCompilerInConfigFile(Path.join(directory, filename)));
66076
- const hasCompilerInPackageJsonConfig = (packageJson) => isPlainObject(packageJson.babel) && analyzeConfigSourceFileExport(ts.parseJsonText("package.json", JSON.stringify(packageJson.babel)), "package.json#babel", "default", false, 0, /* @__PURE__ */ new Set());
66077
- const hasCompilerConfiguration = (directory, packageJson) => hasCompilerInPackageJsonConfig(packageJson) || hasCompilerInConfigFiles(directory, REACT_COMPILER_CONFIG_FILENAMES);
66219
+ const hasCompilerInPackageJsonConfig = (directory, packageJson) => {
66220
+ if (!isPlainObject(packageJson.babel)) return false;
66221
+ const packageJsonPath = Path.join(directory, "package.json");
66222
+ return analyzeConfigSourceFileExport(ts.parseJsonText(packageJsonPath, JSON.stringify(packageJson.babel)), packageJsonPath, "default", false, 0, /* @__PURE__ */ new Set());
66223
+ };
66224
+ const hasCompilerConfiguration = (directory, packageJson) => hasCompilerInPackageJsonConfig(directory, packageJson) || hasCompilerInConfigFiles(directory, REACT_COMPILER_CONFIG_FILENAMES);
66078
66225
  const hasCompilerConfigurationInAncestors = (directory) => {
66079
66226
  if (isProjectBoundary(directory)) return false;
66080
66227
  let ancestorDirectory = Path.dirname(directory);
66081
66228
  while (ancestorDirectory !== Path.dirname(ancestorDirectory)) {
66082
66229
  const ancestorPackagePath = Path.join(ancestorDirectory, "package.json");
66083
- if (hasCompilerInPackageJsonConfig(isFile(ancestorPackagePath) ? readPackageJson$1(ancestorPackagePath) : {}) || hasCompilerInConfigFiles(ancestorDirectory, BABEL_CONFIG_FILENAMES)) return true;
66230
+ const ancestorPackageJson = isFile(ancestorPackagePath) ? readPackageJson$1(ancestorPackagePath) : {};
66231
+ if (hasCompilerInPackageJsonConfig(ancestorDirectory, ancestorPackageJson) || hasCompilerInConfigFiles(ancestorDirectory, BABEL_CONFIG_FILENAMES)) return true;
66084
66232
  if (isProjectBoundary(ancestorDirectory)) return false;
66085
66233
  ancestorDirectory = Path.dirname(ancestorDirectory);
66086
66234
  }
@@ -69335,7 +69483,7 @@ const warn = (message) => {
69335
69483
  runSync(warn$1(message));
69336
69484
  };
69337
69485
  const CONFIG_BASENAME = "doctor.config";
69338
- const CONFIG_EXTENSIONS = [
69486
+ const CONFIG_EXTENSIONS$1 = [
69339
69487
  "ts",
69340
69488
  "mts",
69341
69489
  "cts",
@@ -69439,7 +69587,7 @@ const loadLegacyConfig = (directory) => {
69439
69587
  };
69440
69588
  const loadConfigFromDirectory = async (directory) => {
69441
69589
  let sawBrokenConfigFile = false;
69442
- for (const extension of CONFIG_EXTENSIONS) {
69590
+ for (const extension of CONFIG_EXTENSIONS$1) {
69443
69591
  const filePath = Path.join(directory, `${CONFIG_BASENAME}.${extension}`);
69444
69592
  if (!isFile(filePath)) continue;
69445
69593
  const isDataFile = DATA_CONFIG_EXTENSIONS.has(extension);
@@ -69498,7 +69646,7 @@ const loadConfigWithSource = (rootDirectory) => {
69498
69646
  return loadPromise;
69499
69647
  };
69500
69648
  const directoryHasCurrentConfig = (directory) => {
69501
- for (const extension of CONFIG_EXTENSIONS) if (isFile(Path.join(directory, `${CONFIG_BASENAME}.${extension}`))) return true;
69649
+ for (const extension of CONFIG_EXTENSIONS$1) if (isFile(Path.join(directory, `${CONFIG_BASENAME}.${extension}`))) return true;
69502
69650
  return readEmbeddedPackageJsonConfig(directory) !== null;
69503
69651
  };
69504
69652
  /**
@@ -70202,6 +70350,316 @@ const buildReactNativeDiagnostic = (input) => ({
70202
70350
  column: input.column ?? 0,
70203
70351
  category: input.category ?? "Correctness"
70204
70352
  });
70353
+ const ANDROID_APP_BUILD_FILE_NAMES = ["build.gradle", "build.gradle.kts"];
70354
+ const BUILD_TYPES_BLOCK_PATTERN = /\bbuildTypes\s*\{/g;
70355
+ const RELEASE_STRING_MARKER = "__RD_RELEASE__";
70356
+ const RELEASE_BLOCK_PATTERNS = [/\brelease\s*\{/g, /\b(?:getByName|named)\s*\(\s*(?:["']release["']|__RD_RELEASE__)\s*\)\s*\{/g];
70357
+ const MINIFY_ASSIGNMENT_PATTERN = /\b(?:isMinifyEnabled|minifyEnabled)\b\s*(?:\(\s*|=\s*|\s+)([^;}\r\n]+)/g;
70358
+ const SHRINK_RESOURCES_ASSIGNMENT_PATTERN = /\b(?:isShrinkResources|shrinkResources)\b\s*(?:\(\s*|=\s*|\s+)([^;}\r\n]+)/g;
70359
+ const stripGradleComments = (contents) => {
70360
+ let result = "";
70361
+ let index = 0;
70362
+ let quote = null;
70363
+ while (index < contents.length) {
70364
+ const character = contents[index];
70365
+ const nextCharacter = contents[index + 1];
70366
+ if (quote !== null) {
70367
+ result += character;
70368
+ if (character === "\\") {
70369
+ result += nextCharacter ?? "";
70370
+ index += 2;
70371
+ continue;
70372
+ }
70373
+ if (character === quote) quote = null;
70374
+ index += 1;
70375
+ continue;
70376
+ }
70377
+ if (character === "\"" || character === "'") {
70378
+ quote = character;
70379
+ result += character;
70380
+ index += 1;
70381
+ continue;
70382
+ }
70383
+ if (character === "/" && nextCharacter === "/") {
70384
+ while (index < contents.length && contents[index] !== "\n") {
70385
+ result += " ";
70386
+ index += 1;
70387
+ }
70388
+ continue;
70389
+ }
70390
+ if (character === "/" && nextCharacter === "*") {
70391
+ result += " ";
70392
+ index += 2;
70393
+ while (index < contents.length && !(contents[index] === "*" && contents[index + 1] === "/")) {
70394
+ result += contents[index] === "\n" ? "\n" : " ";
70395
+ index += 1;
70396
+ }
70397
+ if (index < contents.length) {
70398
+ result += " ";
70399
+ index += 2;
70400
+ }
70401
+ continue;
70402
+ }
70403
+ result += character;
70404
+ index += 1;
70405
+ }
70406
+ return result;
70407
+ };
70408
+ const maskGradleStrings = (contents) => {
70409
+ let result = "";
70410
+ let index = 0;
70411
+ let quote = null;
70412
+ while (index < contents.length) {
70413
+ const character = contents[index];
70414
+ if (quote === null) {
70415
+ if (character === "\"" || character === "'") {
70416
+ quote = character;
70417
+ result += " ";
70418
+ } else result += character;
70419
+ index += 1;
70420
+ continue;
70421
+ }
70422
+ if (character === "\\") {
70423
+ result += " ";
70424
+ index += 2;
70425
+ continue;
70426
+ }
70427
+ if (character === quote) quote = null;
70428
+ result += character === "\n" ? "\n" : " ";
70429
+ index += 1;
70430
+ }
70431
+ return result;
70432
+ };
70433
+ const findBlockContents = (contents, pattern) => {
70434
+ const blocks = [];
70435
+ pattern.lastIndex = 0;
70436
+ let match = pattern.exec(contents);
70437
+ while (match !== null) {
70438
+ const openingBraceIndex = pattern.lastIndex - 1;
70439
+ let depth = 1;
70440
+ let index = openingBraceIndex + 1;
70441
+ let quote = null;
70442
+ while (index < contents.length && depth > 0) {
70443
+ const character = contents[index];
70444
+ if (quote !== null) {
70445
+ if (character === "\\") {
70446
+ index += 2;
70447
+ continue;
70448
+ }
70449
+ if (character === quote) quote = null;
70450
+ } else if (character === "\"" || character === "'") quote = character;
70451
+ else if (character === "{") depth += 1;
70452
+ else if (character === "}") depth -= 1;
70453
+ index += 1;
70454
+ }
70455
+ if (depth === 0) blocks.push(contents.slice(openingBraceIndex + 1, index - 1));
70456
+ match = pattern.exec(contents);
70457
+ }
70458
+ return blocks;
70459
+ };
70460
+ const getFinalStaticBoolean = (contents, pattern) => {
70461
+ pattern.lastIndex = 0;
70462
+ let finalValue = null;
70463
+ let match = pattern.exec(contents);
70464
+ while (match !== null) {
70465
+ const staticValue = match[1]?.trim().replace(/\)$/, "").trim();
70466
+ if (staticValue === "true") finalValue = true;
70467
+ else if (staticValue === "false") finalValue = false;
70468
+ else finalValue = null;
70469
+ match = pattern.exec(contents);
70470
+ }
70471
+ return finalValue;
70472
+ };
70473
+ const getReleaseBlocks = (contents) => findBlockContents(contents, BUILD_TYPES_BLOCK_PATTERN).flatMap((buildTypesBlock) => RELEASE_BLOCK_PATTERNS.flatMap((pattern) => findBlockContents(buildTypesBlock, pattern)));
70474
+ const readAndroidReleaseShrinking = (rootDirectory) => {
70475
+ for (const buildFileName of ANDROID_APP_BUILD_FILE_NAMES) {
70476
+ const filePath = Path.posix.join("android", "app", buildFileName);
70477
+ const absoluteFilePath = Path.join(rootDirectory, filePath);
70478
+ if (!isFile(absoluteFilePath)) continue;
70479
+ let contents;
70480
+ try {
70481
+ contents = stripGradleComments(NFS.readFileSync(absoluteFilePath, "utf-8"));
70482
+ } catch {
70483
+ return null;
70484
+ }
70485
+ const releaseBlocks = getReleaseBlocks(maskGradleStrings(contents.replace(/(["'])release\1/g, RELEASE_STRING_MARKER)));
70486
+ const [releaseBlock] = releaseBlocks;
70487
+ if (releaseBlock === void 0 || releaseBlocks.length !== 1) return null;
70488
+ const hasDisabledMinification = getFinalStaticBoolean(releaseBlock, MINIFY_ASSIGNMENT_PATTERN) === false;
70489
+ const hasDisabledResourceShrinking = getFinalStaticBoolean(releaseBlock, SHRINK_RESOURCES_ASSIGNMENT_PATTERN) === false;
70490
+ if (!hasDisabledMinification && !hasDisabledResourceShrinking) return null;
70491
+ return {
70492
+ filePath,
70493
+ hasDisabledMinification,
70494
+ hasDisabledResourceShrinking
70495
+ };
70496
+ }
70497
+ return null;
70498
+ };
70499
+ const checkReactNativeAndroidReleaseShrinking = (rootDirectory) => {
70500
+ const releaseShrinking = readAndroidReleaseShrinking(rootDirectory);
70501
+ if (releaseShrinking === null) return [];
70502
+ const disabledFeatures = [releaseShrinking.hasDisabledMinification ? "code minification" : null, releaseShrinking.hasDisabledResourceShrinking ? "resource shrinking" : null].filter((feature) => feature !== null);
70503
+ return [buildReactNativeDiagnostic({
70504
+ filePath: releaseShrinking.filePath,
70505
+ rule: "rn-android-release-shrinking-disabled",
70506
+ category: "Performance",
70507
+ message: `The Android release build explicitly disables ${disabledFeatures.join(" and ")}, leaving unreachable code or resources in the shipped app.`,
70508
+ help: "Enable release code minification and resource shrinking, then test the optimized build and add keep rules for libraries that require them."
70509
+ })];
70510
+ };
70511
+ const getTypescriptScriptKind = (fileName) => {
70512
+ if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
70513
+ if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
70514
+ if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
70515
+ return ts.ScriptKind.JS;
70516
+ };
70517
+ const BABEL_CONFIG_FILE_NAMES$1 = [
70518
+ "babel.config.js",
70519
+ "babel.config.cjs",
70520
+ "babel.config.mjs",
70521
+ "babel.config.ts",
70522
+ "babel.config.cts",
70523
+ "babel.config.json",
70524
+ ".babelrc",
70525
+ ".babelrc.js",
70526
+ ".babelrc.cjs",
70527
+ ".babelrc.mjs",
70528
+ ".babelrc.cts",
70529
+ ".babelrc.json"
70530
+ ];
70531
+ const readUnknownPluginNames = (value) => {
70532
+ if (!Array.isArray(value)) return null;
70533
+ const pluginNames = [];
70534
+ for (const entry of value) {
70535
+ if (typeof entry === "string") {
70536
+ pluginNames.push(entry);
70537
+ continue;
70538
+ }
70539
+ if (Array.isArray(entry) && typeof entry[0] === "string") {
70540
+ pluginNames.push(entry[0]);
70541
+ continue;
70542
+ }
70543
+ return null;
70544
+ }
70545
+ return pluginNames;
70546
+ };
70547
+ const getStaticPropertyName$1 = (propertyName) => {
70548
+ if (ts.isIdentifier(propertyName) || ts.isStringLiteralLike(propertyName)) return propertyName.text;
70549
+ return null;
70550
+ };
70551
+ const getStaticPluginName = (expression) => {
70552
+ const unwrappedExpression = unwrapTypescriptExpression(expression);
70553
+ if (ts.isStringLiteralLike(unwrappedExpression)) return unwrappedExpression.text;
70554
+ if (!ts.isArrayLiteralExpression(unwrappedExpression)) return null;
70555
+ const pluginExpression = unwrappedExpression.elements[0];
70556
+ return pluginExpression && ts.isStringLiteralLike(pluginExpression) ? pluginExpression.text : null;
70557
+ };
70558
+ const getPluginNamesFromObject = (objectExpression) => {
70559
+ if (objectExpression.properties.some((property) => ts.isSpreadAssignment(property))) return null;
70560
+ const pluginProperties = objectExpression.properties.filter((property) => ts.isPropertyAssignment(property) && getStaticPropertyName$1(property.name) === "plugins");
70561
+ if (pluginProperties.length !== 1) return pluginProperties.length === 0 ? [] : null;
70562
+ const pluginListExpression = unwrapTypescriptExpression(pluginProperties[0].initializer);
70563
+ if (!ts.isArrayLiteralExpression(pluginListExpression)) return null;
70564
+ const pluginNames = [];
70565
+ for (const element of pluginListExpression.elements) {
70566
+ if (ts.isSpreadElement(element)) return null;
70567
+ const pluginName = getStaticPluginName(element);
70568
+ if (pluginName === null) return null;
70569
+ pluginNames.push(pluginName);
70570
+ }
70571
+ return pluginNames;
70572
+ };
70573
+ const isModuleExports = (expression) => ts.isPropertyAccessExpression(expression) && ts.isIdentifier(expression.expression) && expression.expression.text === "module" && expression.name.text === "exports" || ts.isElementAccessExpression(expression) && ts.isIdentifier(expression.expression) && expression.expression.text === "module" && expression.argumentExpression !== void 0 && ts.isStringLiteralLike(expression.argumentExpression) && expression.argumentExpression.text === "exports";
70574
+ const getDirectExportedObject = (sourceFile) => {
70575
+ const exportedExpressions = [];
70576
+ for (const statement of sourceFile.statements) {
70577
+ if (ts.isExportAssignment(statement)) {
70578
+ exportedExpressions.push(statement.expression);
70579
+ continue;
70580
+ }
70581
+ if (ts.isExpressionStatement(statement) && ts.isBinaryExpression(statement.expression) && statement.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken && isModuleExports(statement.expression.left)) exportedExpressions.push(statement.expression.right);
70582
+ }
70583
+ if (exportedExpressions.length !== 1) return null;
70584
+ const exportedExpression = unwrapTypescriptExpression(exportedExpressions[0]);
70585
+ return ts.isObjectLiteralExpression(exportedExpression) ? exportedExpression : null;
70586
+ };
70587
+ const parseConfigPluginNames = (filePath, contents) => {
70588
+ try {
70589
+ const parsedJson = JSON.parse(contents);
70590
+ return isPlainObject(parsedJson) ? readUnknownPluginNames(parsedJson.plugins) : null;
70591
+ } catch {
70592
+ if (ts.transpileModule(contents, {
70593
+ fileName: filePath,
70594
+ reportDiagnostics: true,
70595
+ compilerOptions: { allowJs: true }
70596
+ }).diagnostics?.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)) return null;
70597
+ const exportedObject = getDirectExportedObject(ts.createSourceFile(filePath, contents, ts.ScriptTarget.Latest, true, getTypescriptScriptKind(filePath)));
70598
+ return exportedObject === null ? null : getPluginNamesFromObject(exportedObject);
70599
+ }
70600
+ };
70601
+ const readStaticBabelPluginNames = (rootDirectory) => {
70602
+ for (const fileName of BABEL_CONFIG_FILE_NAMES$1) {
70603
+ const absoluteFilePath = Path.join(rootDirectory, fileName);
70604
+ if (!isFile(absoluteFilePath)) continue;
70605
+ try {
70606
+ return {
70607
+ filePath: fileName,
70608
+ pluginNames: parseConfigPluginNames(fileName, NFS.readFileSync(absoluteFilePath, "utf-8"))
70609
+ };
70610
+ } catch {
70611
+ return {
70612
+ filePath: fileName,
70613
+ pluginNames: null
70614
+ };
70615
+ }
70616
+ }
70617
+ const packageJson = readPackageJson$1(Path.join(rootDirectory, "package.json"));
70618
+ if (!isPlainObject(packageJson.babel)) return null;
70619
+ return {
70620
+ filePath: "package.json",
70621
+ pluginNames: readUnknownPluginNames(packageJson.babel.plugins)
70622
+ };
70623
+ };
70624
+ const REACT_COMPILER_PLUGIN_NAMES = new Set(["babel-plugin-react-compiler", "react-compiler"]);
70625
+ const REANIMATED_PLUGIN_NAME = "react-native-reanimated/plugin";
70626
+ const WORKLETS_PLUGIN_NAME = "react-native-worklets/plugin";
70627
+ const checkReactCompilerPluginOrder = (filePath, pluginNames) => {
70628
+ if (pluginNames.findIndex((pluginName) => REACT_COMPILER_PLUGIN_NAMES.has(pluginName)) <= 0) return [];
70629
+ return [buildReactNativeDiagnostic({
70630
+ filePath,
70631
+ rule: "rn-react-compiler-plugin-first",
70632
+ category: "Performance",
70633
+ message: "The React Compiler Babel plugin is not first, so an earlier transform can change component or hook code before the compiler analyzes it.",
70634
+ help: "Move `babel-plugin-react-compiler` to the first entry in the Babel `plugins` array."
70635
+ })];
70636
+ };
70637
+ const checkReanimatedPluginOrder = (project, filePath, pluginNames) => {
70638
+ const reanimatedMajorVersion = project.reanimatedVersion === null || project.reanimatedVersion === void 0 ? null : getLowestDependencyMajor(project.reanimatedVersion);
70639
+ if (reanimatedMajorVersion === null || reanimatedMajorVersion < 4) return [];
70640
+ const diagnostics = [];
70641
+ if (pluginNames.includes(REANIMATED_PLUGIN_NAME)) diagnostics.push(buildReactNativeDiagnostic({
70642
+ filePath,
70643
+ rule: "rn-reanimated-worklets-plugin-last",
70644
+ category: "Performance",
70645
+ message: "This Reanimated 4 project still uses `react-native-reanimated/plugin`, which was renamed to the Worklets Babel plugin.",
70646
+ help: "Replace it with `react-native-worklets/plugin` and keep that plugin last in the Babel `plugins` array."
70647
+ }));
70648
+ const workletsPluginIndex = pluginNames.lastIndexOf(WORKLETS_PLUGIN_NAME);
70649
+ if (workletsPluginIndex !== -1 && workletsPluginIndex !== pluginNames.length - 1) diagnostics.push(buildReactNativeDiagnostic({
70650
+ filePath,
70651
+ rule: "rn-reanimated-worklets-plugin-last",
70652
+ category: "Performance",
70653
+ message: "`react-native-worklets/plugin` is not last in the Babel `plugins` array, so later transforms can prevent worklets from being compiled correctly.",
70654
+ help: "Move `react-native-worklets/plugin` to the final entry in the Babel `plugins` array."
70655
+ }));
70656
+ return diagnostics;
70657
+ };
70658
+ const checkReactNativeBabelPluginOrder = (rootDirectory, project) => {
70659
+ const babelPlugins = readStaticBabelPluginNames(rootDirectory);
70660
+ if (babelPlugins === null || babelPlugins.pluginNames === null) return [];
70661
+ return [...checkReactCompilerPluginOrder(babelPlugins.filePath, babelPlugins.pluginNames), ...checkReanimatedPluginOrder(project, babelPlugins.filePath, babelPlugins.pluginNames)];
70662
+ };
70205
70663
  const BUILDER_BOB_PACKAGE = "react-native-builder-bob";
70206
70664
  const isBuilderBobLibrary = (packageJson) => {
70207
70665
  const bobConfig = packageJson[BUILDER_BOB_PACKAGE];
@@ -70278,7 +70736,12 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
70278
70736
  const isReactNativeProject = (project) => project.framework === "react-native" || project.framework === "expo" || project.hasReactNativeWorkspace || project.expoVersion !== null;
70279
70737
  const checkReactNativeProject = (rootDirectory, project) => {
70280
70738
  if (!isReactNativeProject(project)) return [];
70281
- return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
70739
+ return [
70740
+ ...checkReactNativeMetroBabelPreset(rootDirectory),
70741
+ ...checkReactNativeBabelPluginOrder(rootDirectory, project),
70742
+ ...checkReactNativeAndroidReleaseShrinking(rootDirectory),
70743
+ ...checkReactNativeLibraryDependencies(rootDirectory)
70744
+ ];
70282
70745
  };
70283
70746
  const RULE_KEY = "no-vulnerable-react-server-components";
70284
70747
  const REACT_RSC_ADVISORIES_BY_MINOR = {
@@ -70429,17 +70892,6 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
70429
70892
  }
70430
70893
  return diagnostics;
70431
70894
  };
70432
- const getTypescriptScriptKind = (fileName) => {
70433
- if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
70434
- if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
70435
- if (fileName.endsWith(".ts")) return ts.ScriptKind.TS;
70436
- return ts.ScriptKind.JS;
70437
- };
70438
- const unwrapTypescriptExpression = (expression) => {
70439
- let currentExpression = expression;
70440
- while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
70441
- return currentExpression;
70442
- };
70443
70895
  const EMPTY_MOTION_EXPRESSION_EVIDENCE = {
70444
70896
  isAnimationFunction: false,
70445
70897
  isMotionComponent: false,
@@ -70995,7 +71447,11 @@ const buildCapabilities = (project) => {
70995
71447
  capabilities.add(project.framework);
70996
71448
  if (project.reactVersion !== null || project.preactVersion !== null) capabilities.add("react");
70997
71449
  if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
70998
- if (project.expoVersion !== null) capabilities.add("expo");
71450
+ if (project.expoVersion !== null) {
71451
+ capabilities.add("expo");
71452
+ const expoSdkMajorVersion = getLowestDependencyMajor(project.expoVersion);
71453
+ if (expoSdkMajorVersion !== null && expoSdkMajorVersion >= 54) capabilities.add("expo:54");
71454
+ }
70999
71455
  if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
71000
71456
  if (SSR_FRAMEWORKS.has(project.framework) || project.hasSsrDependency) capabilities.add("ssr");
71001
71457
  if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
@@ -71059,6 +71515,11 @@ const buildCapabilities = (project) => {
71059
71515
  if (project.zustandMajorVersion !== void 0 && project.zustandMajorVersion !== null && project.zustandMajorVersion >= 1 && project.zustandMajorVersion <= 5) addVersionCapabilityLadder(capabilities, "zustand", project.zustandMajorVersion, 1, 5);
71060
71516
  if (project.isPreES2023Target) capabilities.add("pre-es2023");
71061
71517
  if (project.hasReactCompiler) capabilities.add("react-compiler");
71518
+ if (project.reanimatedVersion !== void 0 && project.reanimatedVersion !== null) {
71519
+ capabilities.add("reanimated");
71520
+ const reanimatedMajorVersion = getLowestDependencyMajor(project.reanimatedVersion);
71521
+ if (reanimatedMajorVersion !== null && reanimatedMajorVersion >= 4) capabilities.add("reanimated:4");
71522
+ }
71062
71523
  if (Boolean(project.hasTanStackQuery) || Boolean(project.tanstackQueryVersion)) capabilities.add("tanstack-query");
71063
71524
  if (project.styledComponentsVersion) {
71064
71525
  capabilities.add("styled-components");
@@ -72972,7 +73433,11 @@ const neutralizeDisableDirectives = async (rootDirectory, includePaths) => {
72972
73433
  const ROOT_DIRECTORY_PLACEHOLDER = "<root>";
72973
73434
  const normalizeConfigForHash = (config) => {
72974
73435
  const clone = JSON.parse(JSON.stringify(config));
72975
- if (clone?.settings?.["react-doctor"]) clone.settings["react-doctor"].rootDirectory = ROOT_DIRECTORY_PLACEHOLDER;
73436
+ if (clone?.settings?.["react-doctor"]) {
73437
+ const reactDoctorSettings = clone.settings["react-doctor"];
73438
+ reactDoctorSettings.rootDirectory = ROOT_DIRECTORY_PLACEHOLDER;
73439
+ if (Array.isArray(reactDoctorSettings.unpluginAutoImportRootDirectories)) reactDoctorSettings.unpluginAutoImportRootDirectories = [ROOT_DIRECTORY_PLACEHOLDER];
73440
+ }
72976
73441
  if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
72977
73442
  return clone;
72978
73443
  };
@@ -73131,13 +73596,14 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
73131
73596
  }
73132
73597
  return enabled;
73133
73598
  };
73134
- const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
73599
+ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, runtimeGlobals, unpluginAutoImportGlobalScopes, serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
73135
73600
  const hasIncludedTags = includedTags.size > 0;
73136
73601
  const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" || hasIncludedTags ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
73137
73602
  const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
73138
73603
  const jsPlugins = [];
73139
73604
  if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
73140
73605
  const capabilities = getCapabilities(project);
73606
+ const settingsRootDirectory = resolveSettingsRootDirectory(project.rootDirectory);
73141
73607
  const enabledReactDoctorRules = {};
73142
73608
  for (const registryEntry of REACT_DOCTOR_RULES) {
73143
73609
  const rule = reactDoctorPlugin.rules[registryEntry.id];
@@ -73183,8 +73649,16 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
73183
73649
  jsPlugins: [...jsPlugins, pluginPath],
73184
73650
  settings: { "react-doctor": {
73185
73651
  framework: project.framework,
73186
- rootDirectory: resolveSettingsRootDirectory(project.rootDirectory),
73652
+ rootDirectory: settingsRootDirectory,
73187
73653
  capabilities: [...capabilities].sort(),
73654
+ ...runtimeGlobals && runtimeGlobals.length > 0 ? { runtimeGlobals: [...runtimeGlobals] } : {},
73655
+ ...unpluginAutoImportGlobalScopes && unpluginAutoImportGlobalScopes.length > 0 ? {
73656
+ unpluginAutoImportRootDirectories: [...new Set([settingsRootDirectory, project.rootDirectory])],
73657
+ unpluginAutoImportGlobalScopes: unpluginAutoImportGlobalScopes.map((scope) => ({
73658
+ directory: scope.directory,
73659
+ names: [...scope.names]
73660
+ }))
73661
+ } : {},
73188
73662
  ...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
73189
73663
  ...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
73190
73664
  } },
@@ -73195,6 +73669,245 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
73195
73669
  }
73196
73670
  };
73197
73671
  };
73672
+ const AUTO_IMPORT_ADAPTER_PATTERN = /^unplugin-auto-import\/(?:astro|esbuild|rollup|rolldown|rspack|vite|webpack)$/;
73673
+ const CONFIG_BASENAMES = [
73674
+ "astro.config",
73675
+ "esbuild.config",
73676
+ "rollup.config",
73677
+ "rolldown.config",
73678
+ "rspack.config",
73679
+ "vite.config",
73680
+ "webpack.config"
73681
+ ];
73682
+ const CONFIG_EXTENSIONS = [
73683
+ ".cjs",
73684
+ ".cts",
73685
+ ".js",
73686
+ ".mjs",
73687
+ ".mts",
73688
+ ".ts"
73689
+ ];
73690
+ const CONFIG_FILENAMES = new Set(CONFIG_BASENAMES.flatMap((basename) => CONFIG_EXTENSIONS.map((extension) => `${basename}${extension}`)));
73691
+ const DEFAULT_ESLINT_GLOBALS_PATH = ".eslintrc-auto-import.json";
73692
+ const SUPPORTED_GLOBAL_VALUES = new Set([
73693
+ true,
73694
+ false,
73695
+ "readable",
73696
+ "readonly",
73697
+ "writable",
73698
+ "writeable"
73699
+ ]);
73700
+ const getStaticProperty = (objectExpression, propertyName) => objectExpression.properties.find((property) => {
73701
+ if (ts.isSpreadAssignment(property)) return false;
73702
+ const name = ts.isComputedPropertyName(property.name) ? unwrapTypescriptExpression(property.name.expression) : property.name;
73703
+ return (ts.isIdentifier(name) || ts.isStringLiteralLike(name)) && name.text === propertyName;
73704
+ }) ?? null;
73705
+ const hasNonLiteralComputedProperty = (objectExpression) => objectExpression.properties.some((property) => !ts.isSpreadAssignment(property) && ts.isComputedPropertyName(property.name) && !ts.isStringLiteralLike(unwrapTypescriptExpression(property.name.expression)));
73706
+ const getStaticPropertyAssignment = (objectExpression, propertyName) => {
73707
+ const property = getStaticProperty(objectExpression, propertyName);
73708
+ return property && ts.isPropertyAssignment(property) ? property : null;
73709
+ };
73710
+ const getRequireModuleSpecifier = (expression) => {
73711
+ const unwrappedExpression = unwrapTypescriptExpression(expression);
73712
+ if (ts.isPropertyAccessExpression(unwrappedExpression) && unwrappedExpression.name.text === "default") return getRequireModuleSpecifier(unwrappedExpression.expression);
73713
+ if (!ts.isCallExpression(unwrappedExpression) || !ts.isIdentifier(unwrappedExpression.expression) || unwrappedExpression.expression.text !== "require" || unwrappedExpression.arguments.length !== 1) return null;
73714
+ const moduleSpecifier = unwrappedExpression.arguments[0];
73715
+ return moduleSpecifier && ts.isStringLiteralLike(moduleSpecifier) ? moduleSpecifier.text : null;
73716
+ };
73717
+ const getRequiredPropertyModuleSpecifier = (expression, propertyName) => {
73718
+ const unwrappedExpression = unwrapTypescriptExpression(expression);
73719
+ return ts.isPropertyAccessExpression(unwrappedExpression) && unwrappedExpression.name.text === propertyName ? getRequireModuleSpecifier(unwrappedExpression.expression) : null;
73720
+ };
73721
+ const isCommonJsExportAssignment = (node, sourceFile) => ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.EqualsToken && (node.left.getText(sourceFile) === "module.exports" || node.left.getText(sourceFile) === "exports.default");
73722
+ const isTopLevelExpression = (node) => {
73723
+ let ancestor = node.parent;
73724
+ while (ancestor && !ts.isSourceFile(ancestor)) {
73725
+ if (ts.isExpressionStatement(ancestor)) return ts.isSourceFile(ancestor.parent);
73726
+ if (!ts.isParenthesizedExpression(ancestor) && !ts.isAsExpression(ancestor) && !ts.isSatisfiesExpression(ancestor) && !ts.isNonNullExpression(ancestor) && !ts.isTypeAssertionExpression(ancestor) && !ts.isAwaitExpression(ancestor)) return false;
73727
+ ancestor = ancestor.parent;
73728
+ }
73729
+ return false;
73730
+ };
73731
+ const isInsideActiveConfigProperty = (node, propertyName, sourceFile, esbuildBindings, esbuildNamespaceBindings) => {
73732
+ let ancestor = node.parent;
73733
+ let didFindConfigProperty = false;
73734
+ while (ancestor && !ts.isSourceFile(ancestor)) {
73735
+ if (!didFindConfigProperty && !ts.isArrayLiteralExpression(ancestor) && !ts.isParenthesizedExpression(ancestor) && !ts.isAsExpression(ancestor) && !ts.isSatisfiesExpression(ancestor) && !ts.isNonNullExpression(ancestor) && !ts.isTypeAssertionExpression(ancestor) && !ts.isPropertyAssignment(ancestor)) return false;
73736
+ if (ts.isPropertyAssignment(ancestor)) {
73737
+ const isConfigProperty = (ts.isIdentifier(ancestor.name) || ts.isStringLiteralLike(ancestor.name)) && ancestor.name.text === propertyName;
73738
+ if (didFindConfigProperty || !isConfigProperty) return false;
73739
+ if (!ts.isObjectLiteralExpression(ancestor.parent) || ancestor.parent.properties.some(ts.isSpreadAssignment)) return false;
73740
+ didFindConfigProperty = true;
73741
+ ancestor = ancestor.parent;
73742
+ continue;
73743
+ }
73744
+ if (didFindConfigProperty) {
73745
+ if (ts.isExportAssignment(ancestor)) return true;
73746
+ if (isCommonJsExportAssignment(ancestor, sourceFile)) return isTopLevelExpression(ancestor);
73747
+ if (propertyName === "plugins" && ts.isCallExpression(ancestor) && (ts.isIdentifier(ancestor.expression) ? esbuildBindings.has(ancestor.expression.text) : ts.isPropertyAccessExpression(ancestor.expression) && ancestor.expression.name.text === "build" && (ts.isIdentifier(ancestor.expression.expression) && esbuildNamespaceBindings.has(ancestor.expression.expression.text) || getRequireModuleSpecifier(ancestor.expression.expression) === "esbuild"))) return isTopLevelExpression(ancestor);
73748
+ if (!(ts.isObjectLiteralExpression(ancestor) || ts.isParenthesizedExpression(ancestor) || ts.isAsExpression(ancestor) || ts.isSatisfiesExpression(ancestor) || ts.isNonNullExpression(ancestor) || ts.isTypeAssertionExpression(ancestor) || ts.isCallExpression(ancestor) && ts.isIdentifier(ancestor.expression) && ancestor.expression.text === "defineConfig")) return false;
73749
+ }
73750
+ ancestor = ancestor.parent;
73751
+ }
73752
+ return false;
73753
+ };
73754
+ const getGlobalsPathFromActiveAutoImportCall = (callExpression, packageDirectory) => {
73755
+ const optionsExpression = callExpression.arguments[0];
73756
+ if (!optionsExpression) return null;
73757
+ const unwrappedOptions = unwrapTypescriptExpression(optionsExpression);
73758
+ if (!ts.isObjectLiteralExpression(unwrappedOptions)) return null;
73759
+ if (unwrappedOptions.properties.some(ts.isSpreadAssignment) || hasNonLiteralComputedProperty(unwrappedOptions)) return null;
73760
+ if (getStaticProperty(unwrappedOptions, "include") || getStaticProperty(unwrappedOptions, "exclude")) return null;
73761
+ const eslintrcProperty = getStaticPropertyAssignment(unwrappedOptions, "eslintrc");
73762
+ if (!eslintrcProperty) return null;
73763
+ const eslintrcExpression = unwrapTypescriptExpression(eslintrcProperty.initializer);
73764
+ if (!ts.isObjectLiteralExpression(eslintrcExpression)) return null;
73765
+ if (eslintrcExpression.properties.some(ts.isSpreadAssignment) || hasNonLiteralComputedProperty(eslintrcExpression)) return null;
73766
+ const enabledProperty = getStaticPropertyAssignment(eslintrcExpression, "enabled");
73767
+ if (!enabledProperty || unwrapTypescriptExpression(enabledProperty.initializer).kind !== ts.SyntaxKind.TrueKeyword) return null;
73768
+ const filepathProperty = getStaticProperty(eslintrcExpression, "filepath");
73769
+ if (!filepathProperty) return Path.join(packageDirectory, DEFAULT_ESLINT_GLOBALS_PATH);
73770
+ if (!ts.isPropertyAssignment(filepathProperty)) return null;
73771
+ const filepathExpression = unwrapTypescriptExpression(filepathProperty.initializer);
73772
+ return ts.isStringLiteralLike(filepathExpression) ? Path.resolve(packageDirectory, filepathExpression.text) : null;
73773
+ };
73774
+ const collectGlobalsPathsFromConfig = (configPath, packageDirectory) => {
73775
+ let sourceText;
73776
+ try {
73777
+ sourceText = NFS.readFileSync(configPath, "utf8");
73778
+ } catch {
73779
+ return {
73780
+ didFindActiveAutoImport: false,
73781
+ globalsPaths: []
73782
+ };
73783
+ }
73784
+ const sourceFile = ts.createSourceFile(configPath, sourceText, ts.ScriptTarget.Latest, true);
73785
+ const autoImportBindings = /* @__PURE__ */ new Map();
73786
+ const esbuildBindings = /* @__PURE__ */ new Set();
73787
+ const esbuildNamespaceBindings = /* @__PURE__ */ new Set();
73788
+ for (const statement of sourceFile.statements) {
73789
+ if (ts.isImportDeclaration(statement) && ts.isStringLiteralLike(statement.moduleSpecifier)) {
73790
+ const moduleSpecifier = statement.moduleSpecifier.text;
73791
+ if (AUTO_IMPORT_ADAPTER_PATTERN.test(moduleSpecifier)) {
73792
+ const defaultBinding = statement.importClause?.name;
73793
+ if (defaultBinding) autoImportBindings.set(defaultBinding.text, moduleSpecifier);
73794
+ }
73795
+ if (moduleSpecifier === "esbuild") {
73796
+ const defaultBinding = statement.importClause?.name;
73797
+ if (defaultBinding) esbuildNamespaceBindings.add(defaultBinding.text);
73798
+ const namedBindings = statement.importClause?.namedBindings;
73799
+ if (namedBindings && ts.isNamedImports(namedBindings)) {
73800
+ for (const importSpecifier of namedBindings.elements) if ((importSpecifier.propertyName ?? importSpecifier.name).text === "build") esbuildBindings.add(importSpecifier.name.text);
73801
+ }
73802
+ if (namedBindings && ts.isNamespaceImport(namedBindings)) esbuildNamespaceBindings.add(namedBindings.name.text);
73803
+ }
73804
+ }
73805
+ if (!ts.isVariableStatement(statement)) continue;
73806
+ for (const declaration of statement.declarationList.declarations) {
73807
+ if (!declaration.initializer) continue;
73808
+ const declarationName = ts.isIdentifier(declaration.name) ? declaration.name.text : null;
73809
+ const moduleSpecifier = getRequireModuleSpecifier(declaration.initializer);
73810
+ if (declarationName && moduleSpecifier && AUTO_IMPORT_ADAPTER_PATTERN.test(moduleSpecifier)) autoImportBindings.set(declarationName, moduleSpecifier);
73811
+ if (moduleSpecifier === "esbuild") {
73812
+ if (declarationName) esbuildNamespaceBindings.add(declarationName);
73813
+ if (ts.isObjectBindingPattern(declaration.name)) for (const bindingElement of declaration.name.elements) {
73814
+ const importedName = bindingElement.propertyName ?? bindingElement.name;
73815
+ if (ts.isIdentifier(importedName) && importedName.text === "build" && ts.isIdentifier(bindingElement.name)) esbuildBindings.add(bindingElement.name.text);
73816
+ }
73817
+ }
73818
+ if (declarationName && getRequiredPropertyModuleSpecifier(declaration.initializer, "build") === "esbuild") esbuildBindings.add(declarationName);
73819
+ }
73820
+ }
73821
+ const globalsPaths = /* @__PURE__ */ new Set();
73822
+ let didFindActiveAutoImport = false;
73823
+ let didFindUnsafeActiveAutoImport = false;
73824
+ const visit = (node) => {
73825
+ if (ts.isCallExpression(node)) {
73826
+ const moduleSpecifier = ts.isIdentifier(node.expression) ? autoImportBindings.get(node.expression.text) : getRequireModuleSpecifier(node.expression);
73827
+ if (moduleSpecifier && AUTO_IMPORT_ADAPTER_PATTERN.test(moduleSpecifier)) {
73828
+ if (isInsideActiveConfigProperty(node, moduleSpecifier.endsWith("/astro") ? "integrations" : "plugins", sourceFile, esbuildBindings, esbuildNamespaceBindings)) {
73829
+ didFindActiveAutoImport = true;
73830
+ const globalsPath = getGlobalsPathFromActiveAutoImportCall(node, packageDirectory);
73831
+ if (globalsPath) globalsPaths.add(globalsPath);
73832
+ else didFindUnsafeActiveAutoImport = true;
73833
+ }
73834
+ }
73835
+ }
73836
+ ts.forEachChild(node, visit);
73837
+ };
73838
+ visit(sourceFile);
73839
+ return {
73840
+ didFindActiveAutoImport,
73841
+ globalsPaths: didFindUnsafeActiveAutoImport ? [] : [...globalsPaths]
73842
+ };
73843
+ };
73844
+ const collectGeneratedGlobalNames = (globalsPath, rootDirectory) => {
73845
+ const relativePath = Path.relative(rootDirectory, globalsPath);
73846
+ if (relativePath.startsWith("..") || Path.isAbsolute(relativePath)) return [];
73847
+ const parsed = failOpenReadJson(globalsPath, null);
73848
+ if (!isPlainObject(parsed) || !isPlainObject(parsed.globals)) return [];
73849
+ return Object.entries(parsed.globals).filter((entry) => entry[0].length > 0 && (typeof entry[1] === "boolean" || typeof entry[1] === "string") && SUPPORTED_GLOBAL_VALUES.has(entry[1])).map(([name]) => name).sort();
73850
+ };
73851
+ const findOwningPackageDirectory = (absoluteFilePath, rootDirectory, cache) => {
73852
+ let currentDirectory = Path.dirname(absoluteFilePath);
73853
+ const visitedDirectories = [];
73854
+ while (true) {
73855
+ const cachedDirectory = cache.get(currentDirectory);
73856
+ if (cachedDirectory !== void 0) {
73857
+ for (const visitedDirectory of visitedDirectories) cache.set(visitedDirectory, cachedDirectory);
73858
+ return cachedDirectory;
73859
+ }
73860
+ const relativeDirectory = Path.relative(rootDirectory, currentDirectory);
73861
+ if (relativeDirectory.startsWith("..") || Path.isAbsolute(relativeDirectory)) {
73862
+ for (const visitedDirectory of visitedDirectories) cache.set(visitedDirectory, null);
73863
+ return null;
73864
+ }
73865
+ visitedDirectories.push(currentDirectory);
73866
+ if (NFS.existsSync(Path.join(currentDirectory, "package.json"))) {
73867
+ for (const visitedDirectory of visitedDirectories) cache.set(visitedDirectory, currentDirectory);
73868
+ return currentDirectory;
73869
+ }
73870
+ if (currentDirectory === rootDirectory) {
73871
+ for (const visitedDirectory of visitedDirectories) cache.set(visitedDirectory, null);
73872
+ return null;
73873
+ }
73874
+ currentDirectory = Path.dirname(currentDirectory);
73875
+ }
73876
+ };
73877
+ const collectPackageGlobalNames = (packageDirectory, rootDirectory) => {
73878
+ let directoryEntries;
73879
+ try {
73880
+ directoryEntries = NFS.readdirSync(packageDirectory, { withFileTypes: true });
73881
+ } catch {
73882
+ return [];
73883
+ }
73884
+ const configuredGlobalsPaths = [];
73885
+ for (const entry of directoryEntries) {
73886
+ if (!entry.isFile() && !entry.isSymbolicLink() || !CONFIG_FILENAMES.has(entry.name)) continue;
73887
+ const configGlobalsPaths = collectGlobalsPathsFromConfig(Path.join(packageDirectory, entry.name), packageDirectory);
73888
+ if (configGlobalsPaths.didFindActiveAutoImport) configuredGlobalsPaths.push(configGlobalsPaths.globalsPaths.toSorted());
73889
+ }
73890
+ if (configuredGlobalsPaths.length === 0) return [];
73891
+ const globalsPaths = configuredGlobalsPaths[0];
73892
+ if (configuredGlobalsPaths.some((candidatePaths) => candidatePaths.length !== globalsPaths.length || candidatePaths.some((candidatePath, index) => candidatePath !== globalsPaths[index]))) return [];
73893
+ const names = /* @__PURE__ */ new Set();
73894
+ for (const globalsPath of globalsPaths) for (const name of collectGeneratedGlobalNames(globalsPath, rootDirectory)) names.add(name);
73895
+ return [...names].sort();
73896
+ };
73897
+ const collectUnpluginAutoImportGlobalScopes = ({ rootDirectory, candidateFiles }) => {
73898
+ const absoluteRootDirectory = Path.resolve(rootDirectory);
73899
+ const packageDirectoryCache = /* @__PURE__ */ new Map();
73900
+ const packageDirectories = /* @__PURE__ */ new Set();
73901
+ for (const candidateFile of candidateFiles) {
73902
+ const packageDirectory = findOwningPackageDirectory(Path.resolve(absoluteRootDirectory, candidateFile), absoluteRootDirectory, packageDirectoryCache);
73903
+ if (packageDirectory) packageDirectories.add(packageDirectory);
73904
+ }
73905
+ const scopes = [...packageDirectories].map((packageDirectory) => ({
73906
+ directory: Path.relative(absoluteRootDirectory, packageDirectory).replaceAll("\\", "/"),
73907
+ names: collectPackageGlobalNames(packageDirectory, absoluteRootDirectory)
73908
+ })).sort((firstScope, secondScope) => firstScope.directory.localeCompare(secondScope.directory));
73909
+ return scopes.some((scope) => scope.names.length > 0) ? scopes : [];
73910
+ };
73198
73911
  const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
73199
73912
  const decodeFileDiagnostics = (raw) => {
73200
73913
  if (!Array.isArray(raw)) return null;
@@ -73517,6 +74230,12 @@ const appendReanimatedSharedValueHint = (help, rule, project) => {
73517
74230
  if (!help) return REANIMATED_SHARED_VALUE_HINT;
73518
74231
  return `${help}\n\n${REANIMATED_SHARED_VALUE_HINT}`;
73519
74232
  };
74233
+ const columnOfUtf8Offset = (sourceBuffer, utf8Offset) => {
74234
+ const boundedOffset = Math.min(Math.max(utf8Offset, 0), sourceBuffer.length);
74235
+ let lineStartOffset = boundedOffset;
74236
+ while (lineStartOffset > 0 && sourceBuffer[lineStartOffset - 1] !== 10) lineStartOffset--;
74237
+ return sourceBuffer.subarray(lineStartOffset, boundedOffset).toString("utf8").length + 1;
74238
+ };
73520
74239
  const REDACTED_PLACEHOLDER = "<redacted>";
73521
74240
  const KEEP_PREFIX = `$1${REDACTED_PLACEHOLDER}`;
73522
74241
  const KNOWN_SECRET_RULES = [
@@ -74232,7 +74951,7 @@ const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORI
74232
74951
  * with empty text still carry a useful jump target, so they're kept
74233
74952
  * with a neutral message.
74234
74953
  */
74235
- const buildRelatedLocations = (labels, filePath) => {
74954
+ const buildRelatedLocations = (labels, filePath, sourceBuffer) => {
74236
74955
  if (labels.length <= 1) return [];
74237
74956
  const related = [];
74238
74957
  for (let labelIndex = 1; labelIndex < labels.length; labelIndex++) {
@@ -74240,8 +74959,8 @@ const buildRelatedLocations = (labels, filePath) => {
74240
74959
  if (!label?.span) continue;
74241
74960
  related.push({
74242
74961
  filePath,
74243
- line: label.span.line ?? 0,
74244
- column: label.span.column ?? 0,
74962
+ line: sourceBuffer === null ? label.span.line ?? 0 : lineOfUtf8Offset(sourceBuffer, label.span.offset),
74963
+ column: sourceBuffer === null ? label.span.column ?? 0 : columnOfUtf8Offset(sourceBuffer, label.span.offset),
74245
74964
  offset: label.span.offset,
74246
74965
  length: label.span.length,
74247
74966
  message: label.label ?? ""
@@ -74261,7 +74980,7 @@ const isOxlintOutput = (value) => isRecord$1(value) && Array.isArray(value.diagn
74261
74980
  * to the first `{`. Raises `OxlintOutputUnparseable` when the
74262
74981
  * stdout isn't valid JSON or doesn't carry a `diagnostics` array.
74263
74982
  */
74264
- const parseOxlintOutput = (stdout, project, rootDirectory) => {
74983
+ const parseOxlintOutput = (stdout, project, rootDirectory, sourcePathByLintPath) => {
74265
74984
  if (!stdout) return [];
74266
74985
  const jsonStart = stdout.indexOf("{");
74267
74986
  const sanitizedStdout = jsonStart > 0 ? stdout.slice(jsonStart) : stdout;
@@ -74278,8 +74997,10 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
74278
74997
  const sourceBufferCache = /* @__PURE__ */ new Map();
74279
74998
  const sourceLinesCache = /* @__PURE__ */ new Map();
74280
74999
  const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
75000
+ const resolveMappedSourceFilename = (filename) => sourcePathByLintPath?.get(Path.normalize(resolveAbsolutePath(filename)));
75001
+ const resolveSourceFilename = (filename) => resolveMappedSourceFilename(filename) ?? filename;
74281
75002
  const readSourceBuffer = (filename) => {
74282
- const absolutePath = resolveAbsolutePath(filename);
75003
+ const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
74283
75004
  const cached = sourceBufferCache.get(absolutePath);
74284
75005
  if (cached !== void 0) return cached;
74285
75006
  let sourceBuffer;
@@ -74292,7 +75013,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
74292
75013
  return sourceBuffer;
74293
75014
  };
74294
75015
  const readSourceLines = (filename) => {
74295
- const absolutePath = resolveAbsolutePath(filename);
75016
+ const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
74296
75017
  const cached = sourceLinesCache.get(absolutePath);
74297
75018
  if (cached !== void 0) return cached;
74298
75019
  const sourceBuffer = readSourceBuffer(filename);
@@ -74301,27 +75022,30 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
74301
75022
  return sourceLines;
74302
75023
  };
74303
75024
  const isMinifiedDiagnosticFile = (filename) => {
74304
- const absolutePath = resolveAbsolutePath(filename);
75025
+ const absolutePath = resolveAbsolutePath(resolveSourceFilename(filename));
74305
75026
  const cached = minifiedFileCache.get(absolutePath);
74306
75027
  if (cached !== void 0) return cached;
74307
75028
  const minified = isMinifiedSource(absolutePath);
74308
75029
  minifiedFileCache.set(absolutePath, minified);
74309
75030
  return minified;
74310
75031
  };
74311
- return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(diagnostic.filename) && !(TYPESCRIPT_DECLARATION_FILE_PATTERN.test(diagnostic.filename) && OXLINT_IGNORED_DECLARATION_DIAGNOSTIC_CODES.has(diagnostic.code)) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
75032
+ return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => isMappableOxlintDiagnostic(diagnostic) && isLintableSourceFile(resolveSourceFilename(diagnostic.filename)) && !(TYPESCRIPT_DECLARATION_FILE_PATTERN.test(diagnostic.filename) && OXLINT_IGNORED_DECLARATION_DIAGNOSTIC_CODES.has(diagnostic.code)) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
74312
75033
  const { plugin, rule } = parseRuleCode(diagnostic.code);
74313
75034
  const primaryLabel = diagnostic.labels[0];
74314
75035
  const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
74315
- const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
75036
+ const mappedSourceFilename = resolveMappedSourceFilename(diagnostic.filename);
75037
+ const normalizedFilePath = (mappedSourceFilename ?? diagnostic.filename).replaceAll("\\", "/");
74316
75038
  const primarySpan = primaryLabel?.span;
74317
75039
  const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
74318
- const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
75040
+ const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath, mappedSourceFilename === void 0 ? null : sourceBuffer);
74319
75041
  const category = resolveDiagnosticCategory(plugin, rule);
74320
75042
  const matchByOccurrence = resolveMatchByOccurrence(rule, category);
74321
75043
  const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
74322
- const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
75044
+ const primaryLineNumber = primarySpan && mappedSourceFilename !== void 0 && sourceBuffer !== null ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset) : primarySpan?.line ?? 0;
75045
+ const primaryColumnNumber = primarySpan && mappedSourceFilename !== void 0 && sourceBuffer !== null ? columnOfUtf8Offset(sourceBuffer, primarySpan.offset) : primarySpan?.column ?? 0;
75046
+ const primaryLineIndex = primarySpan ? primaryLineNumber - 1 : -1;
74323
75047
  const primaryLine = sourceLines?.[primaryLineIndex];
74324
- const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
75048
+ const primaryColumnIndex = primarySpan ? primaryColumnNumber - 1 : -1;
74325
75049
  const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
74326
75050
  const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
74327
75051
  const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
@@ -74334,8 +75058,8 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
74334
75058
  message: cleaned.message,
74335
75059
  help: cleaned.help,
74336
75060
  url: diagnostic.url,
74337
- line: primarySpan?.line ?? 0,
74338
- column: primarySpan?.column ?? 0,
75061
+ line: primaryLineNumber,
75062
+ column: primaryColumnNumber,
74339
75063
  ...primarySpan ? {
74340
75064
  offset: primarySpan.offset,
74341
75065
  length: primarySpan.length,
@@ -74523,7 +75247,11 @@ const isParallelismRelatedSpawnError = (error) => {
74523
75247
  * loop with a slimmer config in that case.
74524
75248
  */
74525
75249
  const spawnLintBatches = async (input) => {
74526
- const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
75250
+ const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, sourcePathByLintPath, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
75251
+ const resolveSourcePath = (filePath) => {
75252
+ const absoluteFilePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory, filePath);
75253
+ return sourcePathByLintPath?.get(Path.normalize(absoluteFilePath)) ?? filePath;
75254
+ };
74527
75255
  const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
74528
75256
  let rescueDeadlineEpochMs;
74529
75257
  const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
@@ -74543,7 +75271,7 @@ const spawnLintBatches = async (input) => {
74543
75271
  }
74544
75272
  const batchArgs = [...baseArgs, ...batch];
74545
75273
  try {
74546
- return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
75274
+ return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory, sourcePathByLintPath);
74547
75275
  } catch (error) {
74548
75276
  if (!isSplittableReactDoctorError(error)) throw error;
74549
75277
  if (isPastDeadline()) {
@@ -74649,10 +75377,12 @@ const spawnLintBatches = async (input) => {
74649
75377
  onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
74650
75378
  };
74651
75379
  const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
74652
- reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
74653
- reportSkippedFiles(outcome.deadlineSkippedFiles, (fileListPreview) => `${outcome.deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
75380
+ const droppedSourceFiles = [...new Set(droppedFiles.map(resolveSourcePath))];
75381
+ const deadlineSkippedSourceFiles = [...new Set(outcome.deadlineSkippedFiles.map(resolveSourcePath))];
75382
+ reportSkippedFiles(droppedSourceFiles, (fileListPreview) => `${droppedSourceFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
75383
+ reportSkippedFiles(deadlineSkippedSourceFiles, (fileListPreview) => `${deadlineSkippedSourceFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
74654
75384
  const unavailableFiles = new Set([...droppedFiles, ...outcome.deadlineSkippedFiles]);
74655
- input.onAnalyzedFiles?.([...new Set(fileBatches.flat().filter((filePath) => !unavailableFiles.has(filePath)))].sort());
75385
+ input.onAnalyzedFiles?.([...new Set(fileBatches.flat().filter((filePath) => !unavailableFiles.has(filePath)).map(resolveSourcePath))].sort());
74656
75386
  return dedupeDiagnostics(diagnostics);
74657
75387
  };
74658
75388
  let didValidate = false;
@@ -74751,6 +75481,266 @@ const planLintBatches = ({ baseArgs, files, sizeByFile }) => {
74751
75481
  }
74752
75482
  return batches.filter((batch) => batch.fileIndexes.length > 0).map((batch) => batch.fileIndexes.sort((leftIndex, rightIndex) => leftIndex - rightIndex).map((fileIndex) => files[fileIndex]));
74753
75483
  };
75484
+ const THREE_MODULE_IMPORT_PATTERN = /\b(?:from\s*|import\s*(?:\(\s*)?|require\s*\(\s*)["']three(?:\/[^"']*)?["']/;
75485
+ const containsThreeModuleImport = (sourceText) => THREE_MODULE_IMPORT_PATTERN.test(sourceText);
75486
+ const COMMENT_OPEN = "<!--";
75487
+ const COMMENT_CLOSE = "-->";
75488
+ const FRONTMATTER_FENCE = "---";
75489
+ const SCRIPT_TAG_NAME = "script";
75490
+ const SCRIPT_OPEN = `<${SCRIPT_TAG_NAME}`;
75491
+ const SCRIPT_CLOSE = `</${SCRIPT_TAG_NAME}`;
75492
+ const TEMPLATE_TAG_NAME = "template";
75493
+ const TEMPLATE_CLOSE = `</${TEMPLATE_TAG_NAME}`;
75494
+ const JAVASCRIPT_MIME_TYPES = new Set([
75495
+ "application/ecmascript",
75496
+ "application/javascript",
75497
+ "application/x-ecmascript",
75498
+ "application/x-javascript",
75499
+ "text/ecmascript",
75500
+ "text/javascript",
75501
+ "text/javascript1.0",
75502
+ "text/javascript1.1",
75503
+ "text/javascript1.2",
75504
+ "text/javascript1.3",
75505
+ "text/javascript1.4",
75506
+ "text/javascript1.5",
75507
+ "text/jscript",
75508
+ "text/livescript",
75509
+ "text/x-ecmascript",
75510
+ "text/x-javascript"
75511
+ ]);
75512
+ const RAW_TEXT_TAG_NAMES = new Set([
75513
+ "iframe",
75514
+ "noembed",
75515
+ "noframes",
75516
+ "noscript",
75517
+ "style",
75518
+ "textarea",
75519
+ "title",
75520
+ "xmp"
75521
+ ]);
75522
+ const PLAINTEXT_TAG_NAME = "plaintext";
75523
+ const TAG_OPEN_UTF8_BYTE = "<".charCodeAt(0);
75524
+ const TAG_CLOSE_UTF8_BYTE = ">".charCodeAt(0);
75525
+ const SINGLE_QUOTE_UTF8_BYTE = "'".charCodeAt(0);
75526
+ const DOUBLE_QUOTE_UTF8_BYTE = "\"".charCodeAt(0);
75527
+ const ATTRIBUTE_ASSIGNMENT_UTF8_BYTE = "=".charCodeAt(0);
75528
+ const TAG_SELF_CLOSE_UTF8_BYTE = "/".charCodeAt(0);
75529
+ const TAB_UTF8_BYTE = " ".charCodeAt(0);
75530
+ const FORM_FEED_UTF8_BYTE = "\f".charCodeAt(0);
75531
+ const isHtmlWhitespace = (byte) => byte === TAB_UTF8_BYTE || byte === 10 || byte === FORM_FEED_UTF8_BYTE || byte === 13 || byte === 32;
75532
+ const matchesAsciiCaseInsensitive = (sourceBuffer, startByte, expected) => {
75533
+ if (startByte + expected.length > sourceBuffer.length) return false;
75534
+ for (let expectedIndex = 0; expectedIndex < expected.length; expectedIndex++) {
75535
+ const sourceByte = sourceBuffer[startByte + expectedIndex];
75536
+ if (sourceByte === void 0 || String.fromCharCode(sourceByte).toLowerCase() !== expected[expectedIndex]) return false;
75537
+ }
75538
+ return true;
75539
+ };
75540
+ const isTagBoundary = (byte) => byte === TAG_CLOSE_UTF8_BYTE || byte === TAG_SELF_CLOSE_UTF8_BYTE || isHtmlWhitespace(byte);
75541
+ const isPotentialTagStart = (byte) => {
75542
+ if (byte === void 0) return false;
75543
+ return /[A-Za-z!/?]/.test(String.fromCharCode(byte));
75544
+ };
75545
+ const findLineEnd = (sourceBuffer, lineStartByte) => {
75546
+ let byteIndex = lineStartByte;
75547
+ while (byteIndex < sourceBuffer.length && sourceBuffer[byteIndex] !== 10 && sourceBuffer[byteIndex] !== 13) byteIndex++;
75548
+ return byteIndex;
75549
+ };
75550
+ const findNextLineStart = (sourceBuffer, lineStartByte) => {
75551
+ const lineEndByte = findLineEnd(sourceBuffer, lineStartByte);
75552
+ if (sourceBuffer[lineEndByte] === 13 && sourceBuffer[lineEndByte + 1] === 10) return lineEndByte + 2;
75553
+ return Math.min(lineEndByte + 1, sourceBuffer.length);
75554
+ };
75555
+ const isFrontmatterFenceLine = (sourceBuffer, lineStartByte) => {
75556
+ if (!matchesAsciiCaseInsensitive(sourceBuffer, lineStartByte, FRONTMATTER_FENCE)) return false;
75557
+ const lineEndByte = findLineEnd(sourceBuffer, lineStartByte);
75558
+ for (let byteIndex = lineStartByte + 3; byteIndex < lineEndByte; byteIndex++) if (!isHtmlWhitespace(sourceBuffer[byteIndex])) return false;
75559
+ return true;
75560
+ };
75561
+ const findFrontmatterEnd = (sourceBuffer) => {
75562
+ if (!isFrontmatterFenceLine(sourceBuffer, 0)) return 0;
75563
+ const openingFenceEndByte = findLineEnd(sourceBuffer, 0);
75564
+ let lineStartByte = findNextLineStart(sourceBuffer, 0);
75565
+ while (lineStartByte < sourceBuffer.length) {
75566
+ if (isFrontmatterFenceLine(sourceBuffer, lineStartByte)) return findLineEnd(sourceBuffer, lineStartByte);
75567
+ lineStartByte = findNextLineStart(sourceBuffer, lineStartByte);
75568
+ }
75569
+ return openingFenceEndByte;
75570
+ };
75571
+ const maskByteRange = (maskedBuffer, startByte, endByte) => {
75572
+ for (let byteIndex = startByte; byteIndex < endByte; byteIndex++) {
75573
+ const byte = maskedBuffer[byteIndex];
75574
+ if (byte !== 10 && byte !== 13) maskedBuffer[byteIndex] = 32;
75575
+ }
75576
+ };
75577
+ const readStartTagName = (sourceBuffer, tagStartByte) => {
75578
+ const firstNameByte = sourceBuffer[tagStartByte + 1];
75579
+ if (firstNameByte === void 0 || !/[A-Za-z]/.test(String.fromCharCode(firstNameByte))) return null;
75580
+ let nameEndByte = tagStartByte + 2;
75581
+ while (nameEndByte < sourceBuffer.length && /[A-Za-z0-9:-]/.test(String.fromCharCode(sourceBuffer[nameEndByte] ?? 32))) nameEndByte++;
75582
+ return sourceBuffer.subarray(tagStartByte + 1, nameEndByte).toString("ascii").toLowerCase();
75583
+ };
75584
+ const findTagEnd = (sourceBuffer, startByte) => {
75585
+ let quoteByte = null;
75586
+ for (let byteIndex = startByte; byteIndex < sourceBuffer.length; byteIndex++) {
75587
+ const byte = sourceBuffer[byteIndex];
75588
+ if (quoteByte !== null) {
75589
+ if (byte === quoteByte) quoteByte = null;
75590
+ continue;
75591
+ }
75592
+ if (byte === SINGLE_QUOTE_UTF8_BYTE || byte === DOUBLE_QUOTE_UTF8_BYTE) {
75593
+ quoteByte = byte;
75594
+ continue;
75595
+ }
75596
+ if (byte === TAG_CLOSE_UTF8_BYTE) return byteIndex;
75597
+ }
75598
+ return sourceBuffer.length;
75599
+ };
75600
+ const parseScriptAttributes = (sourceBuffer, attributesStartByte, tagEndByte) => {
75601
+ let byteIndex = attributesStartByte;
75602
+ let hasSource = false;
75603
+ let language = null;
75604
+ let scriptType = null;
75605
+ while (byteIndex < tagEndByte) {
75606
+ while (byteIndex < tagEndByte && (isHtmlWhitespace(sourceBuffer[byteIndex]) || sourceBuffer[byteIndex] === TAG_SELF_CLOSE_UTF8_BYTE)) byteIndex++;
75607
+ const attributeNameStartByte = byteIndex;
75608
+ while (byteIndex < tagEndByte && !isHtmlWhitespace(sourceBuffer[byteIndex]) && sourceBuffer[byteIndex] !== ATTRIBUTE_ASSIGNMENT_UTF8_BYTE && sourceBuffer[byteIndex] !== TAG_SELF_CLOSE_UTF8_BYTE) byteIndex++;
75609
+ const attributeName = sourceBuffer.subarray(attributeNameStartByte, byteIndex).toString("ascii").toLowerCase();
75610
+ while (byteIndex < tagEndByte && isHtmlWhitespace(sourceBuffer[byteIndex])) byteIndex++;
75611
+ let attributeValue = "";
75612
+ if (sourceBuffer[byteIndex] === ATTRIBUTE_ASSIGNMENT_UTF8_BYTE) {
75613
+ byteIndex++;
75614
+ while (byteIndex < tagEndByte && isHtmlWhitespace(sourceBuffer[byteIndex])) byteIndex++;
75615
+ const quoteByte = sourceBuffer[byteIndex];
75616
+ if (quoteByte === SINGLE_QUOTE_UTF8_BYTE || quoteByte === DOUBLE_QUOTE_UTF8_BYTE) {
75617
+ const valueStartByte = byteIndex + 1;
75618
+ byteIndex = valueStartByte;
75619
+ while (byteIndex < tagEndByte && sourceBuffer[byteIndex] !== quoteByte) byteIndex++;
75620
+ attributeValue = sourceBuffer.subarray(valueStartByte, byteIndex).toString("utf8");
75621
+ if (byteIndex < tagEndByte) byteIndex++;
75622
+ } else {
75623
+ const valueStartByte = byteIndex;
75624
+ while (byteIndex < tagEndByte && !isHtmlWhitespace(sourceBuffer[byteIndex]) && sourceBuffer[byteIndex] !== TAG_CLOSE_UTF8_BYTE) byteIndex++;
75625
+ attributeValue = sourceBuffer.subarray(valueStartByte, byteIndex).toString("utf8");
75626
+ }
75627
+ }
75628
+ if (attributeName === "src") hasSource = true;
75629
+ else if (attributeName === "type" && scriptType === null) scriptType = attributeValue;
75630
+ else if (attributeName === "language" && language === null) language = attributeValue;
75631
+ }
75632
+ return {
75633
+ hasSource,
75634
+ language,
75635
+ type: scriptType
75636
+ };
75637
+ };
75638
+ const isExecutableScript = (attributes) => {
75639
+ if (attributes.hasSource) return false;
75640
+ const type = attributes.type?.trim().toLowerCase() ?? "";
75641
+ if (type !== "") {
75642
+ const mimeType = type.split(";")[0]?.trim() ?? "";
75643
+ return mimeType === "module" || JAVASCRIPT_MIME_TYPES.has(mimeType);
75644
+ }
75645
+ const language = attributes.language?.trim().toLowerCase() ?? "";
75646
+ return language === "" || JAVASCRIPT_MIME_TYPES.has(`text/${language}`);
75647
+ };
75648
+ const findElementClose = (sourceBuffer, startByte, tagName) => {
75649
+ const closeTagStart = `</${tagName}`;
75650
+ for (let byteIndex = startByte; byteIndex < sourceBuffer.length; byteIndex++) if (sourceBuffer[byteIndex] === TAG_OPEN_UTF8_BYTE && matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, closeTagStart) && isTagBoundary(sourceBuffer[byteIndex + closeTagStart.length])) return byteIndex;
75651
+ return sourceBuffer.length;
75652
+ };
75653
+ const prepareHtmlScriptSource = (sourceBuffer) => {
75654
+ const frontmatterEndByte = findFrontmatterEnd(sourceBuffer);
75655
+ let maskedBuffer = frontmatterEndByte > 0 ? Buffer.from(sourceBuffer) : null;
75656
+ if (maskedBuffer !== null) maskByteRange(maskedBuffer, 0, frontmatterEndByte);
75657
+ const executableScriptBodies = [];
75658
+ let byteIndex = frontmatterEndByte;
75659
+ let templateDepth = 0;
75660
+ while (byteIndex < sourceBuffer.length) {
75661
+ if (sourceBuffer[byteIndex] !== TAG_OPEN_UTF8_BYTE) {
75662
+ byteIndex++;
75663
+ continue;
75664
+ }
75665
+ if (matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, COMMENT_OPEN)) {
75666
+ const commentEndByte = sourceBuffer.indexOf(COMMENT_CLOSE, byteIndex + 4);
75667
+ byteIndex = commentEndByte === -1 ? sourceBuffer.length : commentEndByte + 3;
75668
+ continue;
75669
+ }
75670
+ if (matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, TEMPLATE_CLOSE) && isTagBoundary(sourceBuffer[byteIndex + TEMPLATE_CLOSE.length])) {
75671
+ const tagEndByte = findTagEnd(sourceBuffer, byteIndex + TEMPLATE_CLOSE.length);
75672
+ if (templateDepth > 0) templateDepth--;
75673
+ byteIndex = Math.min(tagEndByte + 1, sourceBuffer.length);
75674
+ continue;
75675
+ }
75676
+ if (!matchesAsciiCaseInsensitive(sourceBuffer, byteIndex, SCRIPT_OPEN) || !isTagBoundary(sourceBuffer[byteIndex + SCRIPT_OPEN.length])) {
75677
+ if (!isPotentialTagStart(sourceBuffer[byteIndex + 1])) {
75678
+ byteIndex++;
75679
+ continue;
75680
+ }
75681
+ const tagEndByte = findTagEnd(sourceBuffer, byteIndex + 1);
75682
+ const tagName = readStartTagName(sourceBuffer, byteIndex);
75683
+ if (tagName === PLAINTEXT_TAG_NAME) break;
75684
+ if (tagName === TEMPLATE_TAG_NAME) templateDepth++;
75685
+ if (tagName !== null && RAW_TEXT_TAG_NAMES.has(tagName)) {
75686
+ const closeTagStartByte = findElementClose(sourceBuffer, tagEndByte + 1, tagName);
75687
+ if (closeTagStartByte === sourceBuffer.length) break;
75688
+ const closeTagEndByte = findTagEnd(sourceBuffer, closeTagStartByte + tagName.length + 2);
75689
+ byteIndex = Math.min(closeTagEndByte + 1, sourceBuffer.length);
75690
+ continue;
75691
+ }
75692
+ byteIndex = Math.min(tagEndByte + 1, sourceBuffer.length);
75693
+ continue;
75694
+ }
75695
+ const tagEndByte = findTagEnd(sourceBuffer, byteIndex + SCRIPT_OPEN.length);
75696
+ if (tagEndByte === sourceBuffer.length) break;
75697
+ const bodyStartByte = tagEndByte + 1;
75698
+ const bodyEndByte = findElementClose(sourceBuffer, bodyStartByte, SCRIPT_TAG_NAME);
75699
+ const attributes = parseScriptAttributes(sourceBuffer, byteIndex + SCRIPT_OPEN.length, tagEndByte);
75700
+ if (attributes.hasSource || templateDepth > 0) {
75701
+ maskedBuffer ??= Buffer.from(sourceBuffer);
75702
+ maskByteRange(maskedBuffer, bodyStartByte, bodyEndByte);
75703
+ } else if (isExecutableScript(attributes)) executableScriptBodies.push(sourceBuffer.subarray(bodyStartByte, bodyEndByte));
75704
+ if (bodyEndByte === sourceBuffer.length) break;
75705
+ const closeTagEndByte = findTagEnd(sourceBuffer, bodyEndByte + SCRIPT_CLOSE.length);
75706
+ byteIndex = Math.min(closeTagEndByte + 1, sourceBuffer.length);
75707
+ }
75708
+ return {
75709
+ executableScriptBodies,
75710
+ lintBuffer: maskedBuffer ?? sourceBuffer
75711
+ };
75712
+ };
75713
+ const prepareHtmlLintSources = (rootDirectory, temporaryDirectory, candidateFiles) => {
75714
+ const lintFiles = [];
75715
+ const sourcePathByLintPath = /* @__PURE__ */ new Map();
75716
+ const sizeByLintPath = /* @__PURE__ */ new Map();
75717
+ const htmlSourcesDirectory = Path.join(temporaryDirectory, "html-sources");
75718
+ let htmlFileIndex = 0;
75719
+ let hasThreeModuleImport = false;
75720
+ for (const candidateFile of candidateFiles) {
75721
+ if (!HTML_FILE_PATTERN.test(candidateFile)) {
75722
+ lintFiles.push(candidateFile);
75723
+ continue;
75724
+ }
75725
+ const absoluteSourcePath = Path.isAbsolute(candidateFile) ? candidateFile : Path.resolve(rootDirectory, candidateFile);
75726
+ const sourceBuffer = NFS.readFileSync(absoluteSourcePath);
75727
+ const { executableScriptBodies, lintBuffer } = prepareHtmlScriptSource(sourceBuffer);
75728
+ if (htmlFileIndex === 0) NFS.mkdirSync(htmlSourcesDirectory, { recursive: true });
75729
+ const lintPath = Path.join(htmlSourcesDirectory, `${htmlFileIndex}.astro`);
75730
+ NFS.writeFileSync(lintPath, lintBuffer);
75731
+ lintFiles.push(lintPath);
75732
+ sourcePathByLintPath.set(Path.resolve(lintPath), candidateFile);
75733
+ sizeByLintPath.set(lintPath, sourceBuffer.length);
75734
+ hasThreeModuleImport ||= executableScriptBodies.some((scriptBody) => containsThreeModuleImport(scriptBody.toString("utf8")));
75735
+ htmlFileIndex++;
75736
+ }
75737
+ return {
75738
+ lintFiles,
75739
+ sourcePathByLintPath,
75740
+ sizeByLintPath,
75741
+ hasThreeModuleImport
75742
+ };
75743
+ };
74754
75744
  /**
74755
75745
  * Atomically (re)writes the generated oxlintrc.json. Used twice in
74756
75746
  * the runner: once for the primary scan, once for the
@@ -74911,6 +75901,8 @@ const reactHooksJsPluginDropNote = (error) => {
74911
75901
  const runOxlint = async (options) => {
74912
75902
  const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, userConfig, configSourceDirectory = rootDirectory, onPartialFailure, onFileCoverage, perFileLintCacheEnabled = false, sidecarLintCacheEnabled = false, onCacheStats, onSidecarStats, spawnTimeoutMs, outputMaxBytes, lintBatchOrdering = "cost" } = options;
74913
75903
  const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
75904
+ const runtimeGlobals = Array.isArray(userConfig?.runtimeGlobals) ? userConfig.runtimeGlobals.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
75905
+ let unpluginAutoImportGlobalScopes = [];
74914
75906
  const severityControls = buildRuleSeverityControls(userConfig);
74915
75907
  validateRuleRegistration();
74916
75908
  resetManifestCaches();
@@ -74924,21 +75916,6 @@ const runOxlint = async (options) => {
74924
75916
  const pluginPath = resolvePluginPath();
74925
75917
  const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
74926
75918
  const userPlugins = includedTags.size > 0 ? [] : resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
74927
- const buildConfig = (overrides) => createOxlintConfig({
74928
- pluginPath,
74929
- project,
74930
- customRulesOnly,
74931
- extendsPaths: overrides.extendsPaths,
74932
- ignoredTags,
74933
- includedTags,
74934
- includeTagDefaults,
74935
- serverAuthFunctionNames,
74936
- severityControls,
74937
- userPlugins,
74938
- disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
74939
- ruleSelection: overrides.ruleSelection,
74940
- sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
74941
- });
74942
75919
  const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
74943
75920
  const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
74944
75921
  const configPath = Path.join(configDirectory, "oxlintrc.json");
@@ -74974,7 +75951,36 @@ const runOxlint = async (options) => {
74974
75951
  await yieldToEventLoop();
74975
75952
  const sizedScanFiles = includePaths === void 0 ? listSourceFilesWithSize(rootDirectory) : null;
74976
75953
  const candidateFiles = includePaths !== void 0 ? includePaths : (sizedScanFiles ?? []).map((entry) => entry.path);
75954
+ unpluginAutoImportGlobalScopes = collectUnpluginAutoImportGlobalScopes({
75955
+ rootDirectory,
75956
+ candidateFiles
75957
+ });
75958
+ const preparedHtmlSources = prepareHtmlLintSources(rootDirectory, configDirectory, candidateFiles);
75959
+ const lintFiles = preparedHtmlSources.lintFiles;
75960
+ const hasHtmlFiles = preparedHtmlSources.sourcePathByLintPath.size > 0;
75961
+ const lintProject = !project.hasThree && preparedHtmlSources.hasThreeModuleImport ? {
75962
+ ...project,
75963
+ hasThree: true
75964
+ } : project;
75965
+ const buildConfig = (overrides) => createOxlintConfig({
75966
+ pluginPath,
75967
+ project: lintProject,
75968
+ customRulesOnly,
75969
+ extendsPaths: overrides.extendsPaths,
75970
+ ignoredTags,
75971
+ includedTags,
75972
+ includeTagDefaults,
75973
+ runtimeGlobals,
75974
+ unpluginAutoImportGlobalScopes,
75975
+ serverAuthFunctionNames,
75976
+ severityControls,
75977
+ userPlugins,
75978
+ disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin,
75979
+ ruleSelection: overrides.ruleSelection,
75980
+ sidecarRuleIdFilter: overrides.sidecarRuleIdFilter
75981
+ });
74977
75982
  const sizeByFile = sizedScanFiles !== null && lintBatchOrdering === "cost" ? new Map(sizedScanFiles.map((entry) => [entry.path, entry.sizeBytes])) : null;
75983
+ if (sizeByFile !== null) for (const [lintPath, sizeBytes] of preparedHtmlSources.sizeByLintPath) sizeByFile.set(lintPath, sizeBytes);
74978
75984
  const buildFileBatches = (passBaseArgs, passFiles) => sizeByFile !== null ? planLintBatches({
74979
75985
  baseArgs: passBaseArgs,
74980
75986
  files: passFiles,
@@ -75003,7 +76009,8 @@ const runOxlint = async (options) => {
75003
76009
  fileBatches: passFileBatches,
75004
76010
  rootDirectory,
75005
76011
  nodeBinaryPath,
75006
- project,
76012
+ project: lintProject,
76013
+ sourcePathByLintPath: preparedHtmlSources.sourcePathByLintPath,
75007
76014
  onPartialFailure: reportPartialFailure,
75008
76015
  onAnalyzedFiles: (filePaths) => {
75009
76016
  analyzedFiles = filePaths;
@@ -75038,7 +76045,7 @@ const runOxlint = async (options) => {
75038
76045
  };
75039
76046
  }
75040
76047
  };
75041
- if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0) {
76048
+ if (perFileLintCacheEnabled && !project.hasReactCompiler && project.hasReactCompilerLintPlugin !== true && extendsPaths.length === 0 && userPlugins.length === 0 && !hasHtmlFiles) {
75042
76049
  const rulesetHash = computeRulesetHash({
75043
76050
  config: buildConfig({
75044
76051
  extendsPaths: [],
@@ -75196,14 +76203,15 @@ const runOxlint = async (options) => {
75196
76203
  ]);
75197
76204
  }
75198
76205
  const baseArgs = makeBaseArgs(configPath);
75199
- const fileBatches = buildFileBatches(baseArgs, candidateFiles);
76206
+ const fileBatches = buildFileBatches(baseArgs, lintFiles);
75200
76207
  let analyzedFiles = [];
75201
76208
  const runBatches = () => spawnLintBatches({
75202
76209
  baseArgs,
75203
76210
  fileBatches,
75204
76211
  rootDirectory,
75205
76212
  nodeBinaryPath,
75206
- project,
76213
+ project: lintProject,
76214
+ sourcePathByLintPath: preparedHtmlSources.sourcePathByLintPath,
75207
76215
  onPartialFailure,
75208
76216
  onAnalyzedFiles: (filePaths) => {
75209
76217
  analyzedFiles = filePaths;
@@ -77410,7 +78418,7 @@ const makeNoopConsole = () => ({
77410
78418
  });
77411
78419
  //#endregion
77412
78420
  //#region src/cli/utils/version.ts
77413
- const VERSION = "0.9.1";
78421
+ const VERSION = "0.9.2";
77414
78422
  //#endregion
77415
78423
  //#region src/cli/utils/json-mode.ts
77416
78424
  let context = null;
@@ -77768,13 +78776,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
77768
78776
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
77769
78777
  * standard `SENTRY_RELEASE` override.
77770
78778
  */
77771
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.1`;
78779
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.2`;
77772
78780
  /**
77773
78781
  * Deployment environment shown in Sentry's environment filter. Defaults to
77774
78782
  * `production` for tagged releases and `development` for dev/unbuilt versions,
77775
78783
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
77776
78784
  */
77777
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.1") ? "development" : "production");
78785
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.2") ? "development" : "production");
77778
78786
  /**
77779
78787
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
77780
78788
  * (set to `0` to disable tracing) and falls back to
@@ -90439,6 +91447,7 @@ const buildScanAttributes = (input) => {
90439
91447
  rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
90440
91448
  fileCount: input.result?.scannedFileCount ?? null,
90441
91449
  nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null,
91450
+ htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length ?? null,
90442
91451
  multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length ?? null
90443
91452
  });
90444
91453
  };
@@ -96492,7 +97501,7 @@ ${highlighter.dim("Learn more:")}
96492
97501
  ${highlighter.info(CI_URL)}
96493
97502
  `;
96494
97503
  const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
96495
- const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
97504
+ const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS and inline HTML script source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
96496
97505
  program.action(inspectAction);
96497
97506
  program.command("design [directory]").description("Run only the focused UI design diagnostics").addHelpText("after", renderDesignHelpEpilog).action((directory, _options, command) => designAction(directory ?? ".", command.optsWithGlobals()));
96498
97507
  program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
@@ -96572,4 +97581,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
96572
97581
  export { toForwardSlashes as A, highlighter as B, buildRulePriorityMap as C, resolveBlockingLevel as D, formatLearnMoreLine as E, isCiEnvironment as F, sync as G, mergeReactDoctorConfigs as H, DIAGNOSTIC_CATEGORY_BUCKETS as I, void_ as K, Progress as L, isUnicodeSupported as M, METRIC as N, isReactDoctorWorkflowInstalled as O, TUI_REPORT_DETAIL_WIDTH_FRACTION as P, Reporter as R, hasLintHardFailure as S, formatFixRecipeLine as T, resolveScanTarget as U, mapWithConcurrency as V, runPromise as W, canAnimateOnboarding as _, resolveCliInspectOptions as a, buildCodeFrame as b, detectLaunchableAgents as c, launchCliAgent as d, isShareOptedOut as f, doctorFace as g, easeOutCubic as h, selectProjects as i, findNearestPackageDirectory as j, recordCount as k, CLI_AGENT_BINARIES as l, printFooter as m, shouldFailScanGate as n, countUniqueScannedFiles as o, inspect as p, succeed$3 as q, discoverWorkspacePackages as r, setUpGitHubActions as s, exitGracefully as t, copyToClipboard as u, computeProjectedScore as v, buildSortedRuleGroups as w, buildNoScoreMessage as x, formatElapsedTime as y, getCategoryImpact as z };
96573
97582
 
96574
97583
  //# sourceMappingURL=cli.js.map
96575
- //# debugId=b226693e-22a9-5a5c-8c9f-2679545b582a
97584
+ //# debugId=c86de9ea-b858-5528-89a8-6ff4d377c873