react-doctor 0.8.1-dev.c9f2c4b → 0.8.1-dev.cd9ca68

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/lsp.js CHANGED
@@ -33427,10 +33427,10 @@ const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"]
33427
33427
  const FRAMEWORK_PACKAGES = {
33428
33428
  next: "nextjs",
33429
33429
  "@tanstack/react-start": "tanstack-start",
33430
- vite: "vite",
33431
- "react-scripts": "cra",
33432
33430
  "@remix-run/react": "remix",
33433
33431
  gatsby: "gatsby",
33432
+ vite: "vite",
33433
+ "react-scripts": "cra",
33434
33434
  expo: "expo",
33435
33435
  "react-native": "react-native"
33436
33436
  };
@@ -33957,19 +33957,6 @@ const getPreactVersion = (packageJson) => {
33957
33957
  ...packageJson.devDependencies
33958
33958
  }.preact ?? null;
33959
33959
  };
33960
- const TANSTACK_QUERY_PACKAGES = new Set([
33961
- "@tanstack/react-query",
33962
- "@tanstack/query-core",
33963
- "react-query"
33964
- ]);
33965
- const hasTanStackQuery = (packageJson) => {
33966
- const allDependencies = {
33967
- ...packageJson.peerDependencies,
33968
- ...packageJson.dependencies,
33969
- ...packageJson.devDependencies
33970
- };
33971
- return Object.keys(allDependencies).some((packageName) => TANSTACK_QUERY_PACKAGES.has(packageName));
33972
- };
33973
33960
  const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
33974
33961
  if (version === null || !isCatalogReference(version)) return version;
33975
33962
  const catalogName = extractCatalogName(version);
@@ -34172,7 +34159,60 @@ const listWorkspacePackages = (rootDirectory) => {
34172
34159
  }
34173
34160
  return packages;
34174
34161
  };
34162
+ const getPreferredDependencyVersion = ({ packageJson, packageNames }) => {
34163
+ const allDependencies = {
34164
+ ...packageJson.devDependencies,
34165
+ ...packageJson.peerDependencies,
34166
+ ...packageJson.dependencies
34167
+ };
34168
+ for (const packageName of packageNames) {
34169
+ const version = allDependencies[packageName];
34170
+ if (version !== void 0) return version;
34171
+ }
34172
+ return null;
34173
+ };
34174
+ const TANSTACK_REACT_QUERY_PACKAGES = ["@tanstack/react-query", "react-query"];
34175
+ const getTanStackQueryVersion = (packageJson) => {
34176
+ return getPreferredDependencyVersion({
34177
+ packageJson,
34178
+ packageNames: TANSTACK_REACT_QUERY_PACKAGES
34179
+ });
34180
+ };
34181
+ const STYLED_COMPONENTS_PACKAGES = ["styled-components"];
34182
+ const getStyledComponentsVersion = (packageJson) => {
34183
+ return getPreferredDependencyVersion({
34184
+ packageJson,
34185
+ packageNames: STYLED_COMPONENTS_PACKAGES
34186
+ });
34187
+ };
34188
+ const I18N_PACKAGES = [
34189
+ "i18next",
34190
+ "react-i18next",
34191
+ "next-i18next",
34192
+ "react-intl",
34193
+ "next-intl",
34194
+ "use-intl",
34195
+ "@formatjs/intl",
34196
+ "@lingui/core",
34197
+ "@lingui/react",
34198
+ "typesafe-i18n",
34199
+ "@tolgee/react",
34200
+ "react-intl-universal"
34201
+ ];
34202
+ const hasI18nDependency = (packageJson) => {
34203
+ const allDependencies = {
34204
+ ...packageJson.peerDependencies,
34205
+ ...packageJson.dependencies,
34206
+ ...packageJson.devDependencies,
34207
+ ...packageJson.optionalDependencies
34208
+ };
34209
+ return I18N_PACKAGES.some((packageName) => allDependencies[packageName] !== void 0);
34210
+ };
34175
34211
  const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
34212
+ const MOBX_REACT_PACKAGE_NAME = "mobx-react";
34213
+ const MOBX_REACT_LITE_PACKAGE_NAME = "mobx-react-lite";
34214
+ const MOBX_STATE_TREE_PACKAGE_NAME = "mobx-state-tree";
34215
+ const MOBX_REACT_OBSERVER_PACKAGE_NAME = "mobx-react-observer";
34176
34216
  const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
34177
34217
  const resolveWorkspaceDependencyVersion = ({ concreteVersion, packageName, rootDirectory, rootPackageJson, sections, workspaceDirectory, workspacePackageJson }) => {
34178
34218
  const dependencyDeclaration = getDependencyDeclaration({
@@ -34191,7 +34231,26 @@ const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
34191
34231
  if (nextMajor === null) return false;
34192
34232
  return nextMajor < currentMajor;
34193
34233
  };
34194
- const evaluateManifestFacts = (facts, packageJson, directory) => {
34234
+ const shouldReplaceSupportedDependencyFact = (currentFact, nextFact, latestSupportedMajor) => {
34235
+ if (currentFact.version === null) return true;
34236
+ if (nextFact.version === null) return false;
34237
+ const currentMajor = getLowestDependencyMajor(currentFact.version);
34238
+ const nextMajor = getLowestDependencyMajor(nextFact.version);
34239
+ if (currentMajor === null) return false;
34240
+ if (nextMajor === null) return true;
34241
+ if (currentMajor > latestSupportedMajor) return false;
34242
+ if (nextMajor > latestSupportedMajor) return true;
34243
+ return nextMajor < currentMajor;
34244
+ };
34245
+ const shouldReplaceStyledComponentsVersion = (currentVersion, nextVersion) => {
34246
+ if (!currentVersion) return true;
34247
+ const currentMajor = getLowestDependencyMajor(currentVersion);
34248
+ const nextMajor = getLowestDependencyMajor(nextVersion);
34249
+ if (currentMajor === null) return nextMajor !== null && nextMajor < 6;
34250
+ if (nextMajor === null) return currentMajor >= 6;
34251
+ return nextMajor < currentMajor;
34252
+ };
34253
+ const evaluateManifestFacts = (facts, packageJson, directory, rootDirectory, rootPackageJson) => {
34195
34254
  if (facts.expo.version === null) {
34196
34255
  const spec = getDependencySpec(packageJson, "expo");
34197
34256
  if (spec !== null) facts.expo = {
@@ -34213,13 +34272,74 @@ const evaluateManifestFacts = (facts, packageJson, directory) => {
34213
34272
  sourceDirectory: directory
34214
34273
  };
34215
34274
  }
34275
+ const mobxSpec = getDependencySpec(packageJson, "mobx");
34276
+ if (mobxSpec !== null) {
34277
+ const nextMobxFact = {
34278
+ version: resolveCatalogBackedDependencyVersion({
34279
+ rootDirectory: directory,
34280
+ rootPackageJson: packageJson,
34281
+ packageName: "mobx",
34282
+ version: mobxSpec
34283
+ }),
34284
+ sourceDirectory: directory
34285
+ };
34286
+ if (shouldReplaceSupportedDependencyFact(facts.mobx, nextMobxFact, 6)) facts.mobx = nextMobxFact;
34287
+ }
34288
+ facts.hasMobxReact = facts.hasMobxReact || getDependencySpec(packageJson, MOBX_REACT_PACKAGE_NAME) !== null;
34289
+ facts.hasMobxReactLite = facts.hasMobxReactLite || getDependencySpec(packageJson, MOBX_REACT_LITE_PACKAGE_NAME) !== null;
34290
+ facts.hasMobxStateTree = facts.hasMobxStateTree || getDependencySpec(packageJson, MOBX_STATE_TREE_PACKAGE_NAME) !== null;
34291
+ facts.hasMobxReactObserver = facts.hasMobxReactObserver || getDependencySpec(packageJson, MOBX_REACT_OBSERVER_PACKAGE_NAME) !== null;
34292
+ const zustandSpec = getDependencySpec(packageJson, "zustand");
34293
+ if (zustandSpec !== null) {
34294
+ const nextZustandFact = {
34295
+ version: resolveCatalogBackedDependencyVersion({
34296
+ rootDirectory: directory,
34297
+ rootPackageJson: packageJson,
34298
+ packageName: "zustand",
34299
+ version: zustandSpec
34300
+ }),
34301
+ sourceDirectory: directory
34302
+ };
34303
+ if (shouldReplaceSupportedDependencyFact(facts.zustand, nextZustandFact, 5)) facts.zustand = nextZustandFact;
34304
+ }
34216
34305
  if (facts.reanimatedVersion === null) {
34217
34306
  const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
34218
34307
  if (spec !== null) facts.reanimatedVersion = spec;
34219
34308
  }
34309
+ if (facts.valtioVersion === null) {
34310
+ const spec = getDependencySpec(packageJson, "valtio");
34311
+ if (spec !== null) facts.valtioVersion = spec;
34312
+ }
34313
+ facts.tanstackQueryVersion ??= getTanStackQueryVersion(packageJson);
34314
+ const styledComponentsVersion = resolveCatalogBackedDependencyVersion({
34315
+ rootDirectory: directory,
34316
+ rootPackageJson: packageJson,
34317
+ packageName: "styled-components",
34318
+ version: getStyledComponentsVersion(packageJson)
34319
+ });
34320
+ if (styledComponentsVersion && shouldReplaceStyledComponentsVersion(facts.styledComponentsVersion, styledComponentsVersion)) facts.styledComponentsVersion = styledComponentsVersion;
34321
+ facts.hasI18nLibrary = facts.hasI18nLibrary || hasI18nDependency(packageJson);
34220
34322
  facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
34221
34323
  facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
34222
34324
  facts.hasSsrDependency = facts.hasSsrDependency || isPackageJsonSsrAware(packageJson);
34325
+ const remotionSpec = getDependencySpec(packageJson, "remotion");
34326
+ if (remotionSpec !== null) {
34327
+ facts.hasRemotionDependency = true;
34328
+ const resolvedRemotionVersion = resolveCatalogBackedDependencyVersion({
34329
+ rootDirectory,
34330
+ rootPackageJson,
34331
+ packageName: "remotion",
34332
+ version: remotionSpec
34333
+ });
34334
+ const remotionMajorVersion = resolvedRemotionVersion === null ? null : getLowestDependencyMajor(resolvedRemotionVersion);
34335
+ if (remotionMajorVersion === null) {
34336
+ facts.hasUnknownRemotionVersion = true;
34337
+ facts.remotionVersion = null;
34338
+ } else if (!facts.hasUnknownRemotionVersion) {
34339
+ const currentRemotionMajorVersion = facts.remotionVersion === null ? null : getLowestDependencyMajor(facts.remotionVersion);
34340
+ if (currentRemotionMajorVersion === null || remotionMajorVersion < currentRemotionMajorVersion) facts.remotionVersion = resolvedRemotionVersion;
34341
+ }
34342
+ }
34223
34343
  };
34224
34344
  const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
34225
34345
  const facts = {
@@ -34239,12 +34359,31 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
34239
34359
  version: null,
34240
34360
  sourceDirectory: null
34241
34361
  },
34362
+ valtioVersion: null,
34363
+ mobx: {
34364
+ version: null,
34365
+ sourceDirectory: null
34366
+ },
34367
+ hasMobxReact: false,
34368
+ hasMobxReactLite: false,
34369
+ hasMobxStateTree: false,
34370
+ hasMobxReactObserver: false,
34371
+ zustand: {
34372
+ version: null,
34373
+ sourceDirectory: null
34374
+ },
34375
+ tanstackQueryVersion: null,
34376
+ styledComponentsVersion: null,
34377
+ hasI18nLibrary: false,
34242
34378
  hasReactNativeAwarePackage: false,
34243
34379
  hasReanimatedAwarePackage: false,
34244
34380
  hasSsrDependency: false,
34381
+ hasRemotionDependency: false,
34382
+ hasUnknownRemotionVersion: false,
34383
+ remotionVersion: null,
34245
34384
  reanimatedVersion: null
34246
34385
  };
34247
- evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
34386
+ evaluateManifestFacts(facts, rootPackageJson, rootDirectory, rootDirectory, rootPackageJson);
34248
34387
  let isReactGroupSettled = !collectReactGroup;
34249
34388
  const visitedDirectories = /* @__PURE__ */ new Set();
34250
34389
  for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
@@ -34253,7 +34392,7 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
34253
34392
  if (visitedDirectories.has(workspaceDirectory)) continue;
34254
34393
  visitedDirectories.add(workspaceDirectory);
34255
34394
  const workspacePackageJson = readPackageJson(Path.join(workspaceDirectory, "package.json"));
34256
- evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
34395
+ evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory, rootDirectory, rootPackageJson);
34257
34396
  const info = extractDependencyInfo(workspacePackageJson);
34258
34397
  if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
34259
34398
  if (isReactGroupSettled) continue;
@@ -34477,11 +34616,27 @@ const discoverProjectWithoutPackageJson = (directory) => {
34477
34616
  tailwindVersion: null,
34478
34617
  zodVersion: null,
34479
34618
  zodMajorVersion: null,
34619
+ mobxVersion: null,
34620
+ mobxMajorVersion: null,
34621
+ hasMobxReact: false,
34622
+ hasMobxReactLite: false,
34623
+ hasMobxStateTree: false,
34624
+ hasMobxReactObserver: false,
34625
+ zustandVersion: null,
34626
+ zustandMajorVersion: null,
34480
34627
  framework: "unknown",
34481
34628
  hasTypeScript: hasOwnTsConfig,
34482
34629
  hasReactCompiler: false,
34483
34630
  hasReactCompilerLintPlugin: false,
34484
34631
  hasTanStackQuery: false,
34632
+ valtioVersion: null,
34633
+ valtioMajorVersion: null,
34634
+ hasRemotion: false,
34635
+ remotionVersion: null,
34636
+ remotionMajorVersion: null,
34637
+ hasI18nLibrary: false,
34638
+ tanstackQueryVersion: null,
34639
+ styledComponentsVersion: null,
34485
34640
  hasSsrDependency: false,
34486
34641
  preactVersion: null,
34487
34642
  preactMajorVersion: null,
@@ -34580,6 +34735,12 @@ const discoverProject = (directory) => {
34580
34735
  packageName: SHOPIFY_FLASH_LIST_PACKAGE_NAME,
34581
34736
  version: workspaceFacts.shopifyFlashList.version
34582
34737
  }) : null;
34738
+ const zustandVersion = resolveCatalogBackedDependencyVersion({
34739
+ rootDirectory: directory,
34740
+ rootPackageJson: packageJson,
34741
+ packageName: "zustand",
34742
+ version: workspaceFacts.zustand.version
34743
+ });
34583
34744
  const hasReanimated = hasReactNativeWorkspace && workspaceFacts.hasReanimatedAwarePackage;
34584
34745
  const reanimatedVersion = hasReanimated ? workspaceFacts.reanimatedVersion : null;
34585
34746
  const nextjsVersion = framework === "nextjs" ? resolveCatalogBackedDependencyVersion({
@@ -34588,7 +34749,21 @@ const discoverProject = (directory) => {
34588
34749
  packageName: "next",
34589
34750
  version: workspaceFacts.next.version
34590
34751
  }) : null;
34752
+ const valtioVersion = resolveCatalogBackedDependencyVersion({
34753
+ rootDirectory: directory,
34754
+ rootPackageJson: packageJson,
34755
+ packageName: "valtio",
34756
+ version: workspaceFacts.valtioVersion
34757
+ });
34758
+ const mobxVersion = resolveCatalogBackedDependencyVersion({
34759
+ rootDirectory: directory,
34760
+ rootPackageJson: packageJson,
34761
+ packageName: "mobx",
34762
+ version: workspaceFacts.mobx.version
34763
+ });
34591
34764
  const preactVersion = getPreactVersion(packageJson);
34765
+ const remotionVersion = workspaceFacts.remotionVersion;
34766
+ const tanstackQueryVersion = getTanStackQueryVersion(packageJson) ?? workspaceFacts.tanstackQueryVersion;
34592
34767
  const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
34593
34768
  const projectInfo = {
34594
34769
  rootDirectory: directory,
@@ -34598,11 +34773,27 @@ const discoverProject = (directory) => {
34598
34773
  tailwindVersion,
34599
34774
  zodVersion,
34600
34775
  zodMajorVersion: zodVersion === null ? null : getLowestDependencyMajor(zodVersion),
34776
+ mobxVersion,
34777
+ mobxMajorVersion: mobxVersion === null ? null : getLowestDependencyMajor(mobxVersion),
34778
+ hasMobxReact: workspaceFacts.hasMobxReact,
34779
+ hasMobxReactLite: workspaceFacts.hasMobxReactLite,
34780
+ hasMobxStateTree: workspaceFacts.hasMobxStateTree,
34781
+ hasMobxReactObserver: workspaceFacts.hasMobxReactObserver,
34782
+ zustandVersion,
34783
+ zustandMajorVersion: zustandVersion === null ? null : getLowestDependencyMajor(zustandVersion),
34601
34784
  framework,
34602
34785
  hasTypeScript,
34603
34786
  hasReactCompiler: detectReactCompiler(directory, packageJson),
34604
34787
  hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
34605
- hasTanStackQuery: hasTanStackQuery(packageJson),
34788
+ hasTanStackQuery: tanstackQueryVersion !== null,
34789
+ hasI18nLibrary: workspaceFacts.hasI18nLibrary,
34790
+ tanstackQueryVersion,
34791
+ styledComponentsVersion: workspaceFacts.styledComponentsVersion,
34792
+ valtioVersion,
34793
+ valtioMajorVersion: valtioVersion === null ? null : getLowestDependencyMajor(valtioVersion),
34794
+ hasRemotion: workspaceFacts.hasRemotionDependency,
34795
+ remotionVersion,
34796
+ remotionMajorVersion: remotionVersion === null ? null : getLowestDependencyMajor(remotionVersion),
34606
34797
  hasSsrDependency: workspaceFacts.hasSsrDependency,
34607
34798
  preactVersion,
34608
34799
  preactMajorVersion: parseReactMajor(preactVersion),
@@ -34836,7 +35027,6 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
34836
35027
  "react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
34837
35028
  "react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
34838
35029
  "react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
34839
- "react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
34840
35030
  "react/jsx-no-undef": "react-doctor/jsx-no-undef",
34841
35031
  "react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
34842
35032
  "react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
@@ -37049,9 +37239,25 @@ const BABEL_CONFIG_FILE_NAMES = [
37049
37239
  ".babelrc.json"
37050
37240
  ];
37051
37241
  const LEGACY_PRESET_SPEC = "module:metro-react-native-babel-preset";
37242
+ const LEGACY_PRESET_PACKAGE = "metro-react-native-babel-preset";
37243
+ const REACT_NATIVE_PACKAGE = "react-native";
37244
+ const PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION = "0.73.0";
37245
+ const LEGACY_PRESET_REFERENCE = new RegExp(`['"]${LEGACY_PRESET_SPEC}['"]`);
37052
37246
  const MODERN_PRESET_REFERENCE = new RegExp(`['"]module:@react-native/babel-preset['"]`);
37053
37247
  const ENABLE_BABEL_RUNTIME_VERSION = /enableBabelRuntime["']?\s*:\s*['"]/;
37248
+ const isReactNativeVersionAtLeastPresetRename = (rootDirectory, packageJson) => {
37249
+ const installedReactNativeManifestPath = Path.join(rootDirectory, "node_modules", REACT_NATIVE_PACKAGE, "package.json");
37250
+ const reactNativeVersionSpec = (isFile(installedReactNativeManifestPath) ? readPackageJson(installedReactNativeManifestPath).version : void 0) ?? getDependencySpec(packageJson, REACT_NATIVE_PACKAGE);
37251
+ if (reactNativeVersionSpec === null || reactNativeVersionSpec === void 0) return false;
37252
+ const normalizedVersionRange = import_semver.validRange(reactNativeVersionSpec);
37253
+ if (normalizedVersionRange === null) return false;
37254
+ const minimumVersion = import_semver.minVersion(normalizedVersionRange);
37255
+ return minimumVersion !== null && import_semver.gte(minimumVersion, PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION);
37256
+ };
37257
+ const canResolveLegacyPreset = (rootDirectory, packageJson) => getDependencySpec(packageJson, LEGACY_PRESET_PACKAGE) !== null || isFile(Path.join(rootDirectory, "node_modules", LEGACY_PRESET_PACKAGE, "package.json"));
37054
37258
  const checkReactNativeMetroBabelPreset = (rootDirectory) => {
37259
+ const packageJson = readPackageJson(Path.join(rootDirectory, "package.json"));
37260
+ const shouldReportLegacyPreset = isReactNativeVersionAtLeastPresetRename(rootDirectory, packageJson) && !canResolveLegacyPreset(rootDirectory, packageJson);
37055
37261
  for (const fileName of BABEL_CONFIG_FILE_NAMES) {
37056
37262
  const filePath = Path.join(rootDirectory, fileName);
37057
37263
  if (!isFile(filePath)) continue;
@@ -37061,7 +37267,7 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
37061
37267
  } catch {
37062
37268
  continue;
37063
37269
  }
37064
- if (contents.includes(LEGACY_PRESET_SPEC)) return [buildReactNativeDiagnostic({
37270
+ if (shouldReportLegacyPreset && LEGACY_PRESET_REFERENCE.test(contents)) return [buildReactNativeDiagnostic({
37065
37271
  filePath: fileName,
37066
37272
  rule: "rn-no-metro-babel-preset",
37067
37273
  severity: "error",
@@ -37084,12 +37290,6 @@ const checkReactNativeProject = (rootDirectory, project) => {
37084
37290
  return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
37085
37291
  };
37086
37292
  const RULE_KEY = "no-vulnerable-react-server-components";
37087
- const DEPENDENCY_SECTIONS = [
37088
- "dependencies",
37089
- "devDependencies",
37090
- "peerDependencies",
37091
- "optionalDependencies"
37092
- ];
37093
37293
  const REACT_RSC_ADVISORIES_BY_MINOR = {
37094
37294
  "19.0": {
37095
37295
  rceFixedVersion: "19.0.1",
@@ -37136,20 +37336,13 @@ const enumerateWorkspaceDirectories = (workspaceRoot) => {
37136
37336
  for (const pattern of patterns) for (const directory of resolveWorkspaceDirectories(workspaceRoot, pattern)) directories.add(directory);
37137
37337
  return [...directories];
37138
37338
  };
37139
- const readDeclaredSpec = (packageJson, packageName) => {
37140
- for (const section of DEPENDENCY_SECTIONS) {
37141
- const spec = packageJson[section]?.[packageName];
37142
- if (typeof spec === "string") return spec;
37143
- }
37144
- return null;
37145
- };
37146
37339
  const resolveVersionInDirectory = (directory, packageName, declaredSpecOverride) => {
37147
37340
  const manifestPath = Path.join(directory, "node_modules", packageName, "package.json");
37148
37341
  if (isFile(manifestPath)) {
37149
37342
  const installedVersion = import_semver.valid(readPackageJson(manifestPath).version ?? null);
37150
37343
  if (installedVersion !== null) return installedVersion;
37151
37344
  }
37152
- const candidateSpecs = [readDeclaredSpec(readPackageJson(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
37345
+ const candidateSpecs = [getDependencySpec(readPackageJson(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
37153
37346
  for (const spec of candidateSpecs) {
37154
37347
  const pinnedVersion = spec === null ? null : import_semver.valid(spec);
37155
37348
  if (pinnedVersion !== null) return pinnedVersion;
@@ -37817,6 +38010,7 @@ const buildCapabilities = (project) => {
37817
38010
  if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
37818
38011
  if (project.isStaticExport) capabilities.add("nextjs:static-export");
37819
38012
  if (project.nextjsMajorVersion !== null && project.nextjsMajorVersion >= 15) capabilities.add("nextjs:15");
38013
+ if (project.nextjsMajorVersion !== null && project.nextjsMajorVersion >= 16) capabilities.add("nextjs:16");
37820
38014
  addMajorLadder(capabilities, "react", project.reactMajorVersion, 17, 30);
37821
38015
  if (project.reactMajorVersion !== null && project.reactMajorVersion >= 19 && isMajorMinorAtLeast(parseReactMajorMinor(project.reactVersion), {
37822
38016
  major: 19,
@@ -37829,9 +38023,30 @@ const buildCapabilities = (project) => {
37829
38023
  })) capabilities.add("tailwind:3.4");
37830
38024
  if (project.zodVersion !== null) capabilities.add("zod");
37831
38025
  if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
38026
+ if (project.mobxVersion !== void 0 && project.mobxVersion !== null || project.hasMobxReact === true || project.hasMobxReactLite === true || project.hasMobxStateTree === true || project.hasMobxReactObserver === true) capabilities.add("mobx");
38027
+ if (project.hasMobxReact === true) capabilities.add("mobx-react");
38028
+ if (project.hasMobxReactLite === true) capabilities.add("mobx-react-lite");
38029
+ if (project.hasMobxReact === true || project.hasMobxReactLite === true) capabilities.add("mobx-react-binding");
38030
+ if (project.hasMobxStateTree === true) capabilities.add("mobx-state-tree");
38031
+ if (project.hasMobxReactObserver === true) capabilities.add("mobx-react-observer");
38032
+ if (project.mobxMajorVersion !== void 0 && project.mobxMajorVersion !== null && project.mobxMajorVersion >= 4 && project.mobxMajorVersion <= 6) addMajorLadder(capabilities, "mobx", project.mobxMajorVersion, 4, 6);
38033
+ if (project.zustandVersion !== void 0 && project.zustandVersion !== null) capabilities.add("zustand");
38034
+ if (project.zustandMajorVersion !== void 0 && project.zustandMajorVersion !== null && project.zustandMajorVersion >= 1 && project.zustandMajorVersion <= 5) addMajorLadder(capabilities, "zustand", project.zustandMajorVersion, 1, 5);
37832
38035
  if (project.isPreES2023Target) capabilities.add("pre-es2023");
37833
38036
  if (project.hasReactCompiler) capabilities.add("react-compiler");
37834
- if (project.hasTanStackQuery) capabilities.add("tanstack-query");
38037
+ if (Boolean(project.hasTanStackQuery) || Boolean(project.tanstackQueryVersion)) capabilities.add("tanstack-query");
38038
+ if (project.styledComponentsVersion) {
38039
+ capabilities.add("styled-components");
38040
+ const styledComponentsMajor = getLowestDependencyMajor(project.styledComponentsVersion);
38041
+ if (styledComponentsMajor !== null && styledComponentsMajor >= 6) capabilities.add("styled-components:6");
38042
+ }
38043
+ if (project.hasI18nLibrary) capabilities.add("i18n");
38044
+ if (project.valtioVersion !== null) capabilities.add("valtio");
38045
+ addMajorLadder(capabilities, "valtio", project.valtioMajorVersion, 1, 10);
38046
+ if (project.hasRemotion) {
38047
+ capabilities.add("remotion");
38048
+ addMajorLadder(capabilities, "remotion", project.remotionMajorVersion ?? null, 4, 30);
38049
+ }
37835
38050
  if (project.hasTypeScript) capabilities.add("typescript");
37836
38051
  if (project.preactVersion !== null) capabilities.add("preact");
37837
38052
  addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
@@ -40100,6 +40315,19 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
40100
40315
  }
40101
40316
  };
40102
40317
  };
40318
+ const fingerprintCacheByFilePath = /* @__PURE__ */ new Map();
40319
+ const fingerprintFileContents = (filePath, fingerprintLength) => {
40320
+ const fileStats = NFS.statSync(filePath, { bigint: true });
40321
+ const cachedFingerprint = fingerprintCacheByFilePath.get(filePath);
40322
+ if (cachedFingerprint?.modifiedTimeNanoseconds === fileStats.mtimeNs && cachedFingerprint.sizeBytes === fileStats.size) return cachedFingerprint.fingerprint;
40323
+ const fingerprint = crypto.createHash("sha256").update(NFS.readFileSync(filePath)).digest("hex").slice(0, fingerprintLength);
40324
+ fingerprintCacheByFilePath.set(filePath, {
40325
+ fingerprint,
40326
+ modifiedTimeNanoseconds: fileStats.mtimeNs,
40327
+ sizeBytes: fileStats.size
40328
+ });
40329
+ return fingerprint;
40330
+ };
40103
40331
  const bundledRequire = createRequire(import.meta.url);
40104
40332
  const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
40105
40333
  const resolveChildNodeVersion = (nodeBinaryPath) => {
@@ -40123,6 +40351,13 @@ const TOOLCHAIN_PACKAGE_SPECIFIERS = [
40123
40351
  "oxlint-plugin-react-doctor/package.json",
40124
40352
  "eslint-plugin-react-hooks/package.json"
40125
40353
  ];
40354
+ const resolvePluginFingerprint = () => {
40355
+ try {
40356
+ return fingerprintFileContents(bundledRequire.resolve("oxlint-plugin-react-doctor"), 16);
40357
+ } catch {
40358
+ return "unresolved";
40359
+ }
40360
+ };
40126
40361
  const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
40127
40362
  const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
40128
40363
  for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
@@ -40132,6 +40367,7 @@ const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
40132
40367
  } catch {
40133
40368
  versions.push(`${specifier}=missing`);
40134
40369
  }
40370
+ versions.push(`oxlint-plugin-react-doctor#fingerprint=${resolvePluginFingerprint()}`);
40135
40371
  return versions;
40136
40372
  };
40137
40373
  const esmRequire = createRequire(import.meta.url);
@@ -40825,12 +41061,17 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
40825
41061
  const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
40826
41062
  const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
40827
41063
  const REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION = "It's how the library works, not a bug in your code. Memoize values you pass from it into other memoized components, or suppress it with `// react-doctor-disable-next-line react-hooks-js/incompatible-library`.";
41064
+ const SET_STATE_IN_EFFECT_ACTION = "Prefer deriving or initializing the value before render. If the effect must read a browser API after mount, treat this as advisory or suppress it with `// react-doctor-disable-next-line react-hooks-js/set-state-in-effect`.";
40828
41065
  const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
40829
41066
  const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
40830
41067
  const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
40831
41068
  if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
40832
41069
  return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
40833
41070
  };
41071
+ const buildSetStateInEffectMessage = (reasonSummary) => {
41072
+ const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
41073
+ return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
41074
+ };
40834
41075
  const PLUGIN_CATEGORY_MAP = {
40835
41076
  react: "Bugs",
40836
41077
  "react-hooks": "Bugs",
@@ -40888,6 +41129,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
40888
41129
  message: REACT_ERROR_BOUNDARY_MESSAGE,
40889
41130
  help: reasonDetail || help
40890
41131
  };
41132
+ if (rule === "set-state-in-effect") return {
41133
+ message: buildSetStateInEffectMessage(reasonSummary.trim()),
41134
+ help: reasonDetail || help
41135
+ };
40891
41136
  return {
40892
41137
  message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
40893
41138
  help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
@@ -45448,5 +45693,5 @@ const startLanguageServer = () => {
45448
45693
  };
45449
45694
  //#endregion
45450
45695
  export { startLanguageServer };
45451
- !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]="4fa679df-c187-5989-bc8a-a96791a6f02c")}catch(e){}}();
45452
- //# debugId=4fa679df-c187-5989-bc8a-a96791a6f02c
45696
+ !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]="9a23503e-e7cd-5df6-a9fe-39a2baf287bc")}catch(e){}}();
45697
+ //# debugId=9a23503e-e7cd-5df6-a9fe-39a2baf287bc
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.8.1-dev.c9f2c4b",
3
+ "version": "0.8.1-dev.cd9ca68",
4
4
  "description": "Your agent writes bad React. This catches it",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -65,8 +65,8 @@
65
65
  "vscode-languageserver-textdocument": "^1.0.12",
66
66
  "vscode-uri": "^3.1.0",
67
67
  "yaml": "^2.9.0",
68
- "oxlint-plugin-react-doctor": "0.8.1-dev.c9f2c4b",
69
- "deslop-js": "0.8.1"
68
+ "deslop-js": "0.8.1",
69
+ "oxlint-plugin-react-doctor": "0.8.1-dev.cd9ca68"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/babel__code-frame": "^7.27.0",