react-doctor 0.8.1-dev.7818922 → 0.8.1-dev.7eca0ee

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
@@ -34173,6 +34173,10 @@ const listWorkspacePackages = (rootDirectory) => {
34173
34173
  return packages;
34174
34174
  };
34175
34175
  const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
34176
+ const MOBX_REACT_PACKAGE_NAME = "mobx-react";
34177
+ const MOBX_REACT_LITE_PACKAGE_NAME = "mobx-react-lite";
34178
+ const MOBX_STATE_TREE_PACKAGE_NAME = "mobx-state-tree";
34179
+ const MOBX_REACT_OBSERVER_PACKAGE_NAME = "mobx-react-observer";
34176
34180
  const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
34177
34181
  const resolveWorkspaceDependencyVersion = ({ concreteVersion, packageName, rootDirectory, rootPackageJson, sections, workspaceDirectory, workspacePackageJson }) => {
34178
34182
  const dependencyDeclaration = getDependencyDeclaration({
@@ -34191,7 +34195,18 @@ const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
34191
34195
  if (nextMajor === null) return false;
34192
34196
  return nextMajor < currentMajor;
34193
34197
  };
34194
- const evaluateManifestFacts = (facts, packageJson, directory) => {
34198
+ const shouldReplaceSupportedDependencyFact = (currentFact, nextFact, latestSupportedMajor) => {
34199
+ if (currentFact.version === null) return true;
34200
+ if (nextFact.version === null) return false;
34201
+ const currentMajor = getLowestDependencyMajor(currentFact.version);
34202
+ const nextMajor = getLowestDependencyMajor(nextFact.version);
34203
+ if (currentMajor === null) return false;
34204
+ if (nextMajor === null) return true;
34205
+ if (currentMajor > latestSupportedMajor) return false;
34206
+ if (nextMajor > latestSupportedMajor) return true;
34207
+ return nextMajor < currentMajor;
34208
+ };
34209
+ const evaluateManifestFacts = (facts, packageJson, directory, rootDirectory, rootPackageJson) => {
34195
34210
  if (facts.expo.version === null) {
34196
34211
  const spec = getDependencySpec(packageJson, "expo");
34197
34212
  if (spec !== null) facts.expo = {
@@ -34213,13 +34228,65 @@ const evaluateManifestFacts = (facts, packageJson, directory) => {
34213
34228
  sourceDirectory: directory
34214
34229
  };
34215
34230
  }
34231
+ const mobxSpec = getDependencySpec(packageJson, "mobx");
34232
+ if (mobxSpec !== null) {
34233
+ const nextMobxFact = {
34234
+ version: resolveCatalogBackedDependencyVersion({
34235
+ rootDirectory: directory,
34236
+ rootPackageJson: packageJson,
34237
+ packageName: "mobx",
34238
+ version: mobxSpec
34239
+ }),
34240
+ sourceDirectory: directory
34241
+ };
34242
+ if (shouldReplaceSupportedDependencyFact(facts.mobx, nextMobxFact, 6)) facts.mobx = nextMobxFact;
34243
+ }
34244
+ facts.hasMobxReact = facts.hasMobxReact || getDependencySpec(packageJson, MOBX_REACT_PACKAGE_NAME) !== null;
34245
+ facts.hasMobxReactLite = facts.hasMobxReactLite || getDependencySpec(packageJson, MOBX_REACT_LITE_PACKAGE_NAME) !== null;
34246
+ facts.hasMobxStateTree = facts.hasMobxStateTree || getDependencySpec(packageJson, MOBX_STATE_TREE_PACKAGE_NAME) !== null;
34247
+ facts.hasMobxReactObserver = facts.hasMobxReactObserver || getDependencySpec(packageJson, MOBX_REACT_OBSERVER_PACKAGE_NAME) !== null;
34248
+ const zustandSpec = getDependencySpec(packageJson, "zustand");
34249
+ if (zustandSpec !== null) {
34250
+ const nextZustandFact = {
34251
+ version: resolveCatalogBackedDependencyVersion({
34252
+ rootDirectory: directory,
34253
+ rootPackageJson: packageJson,
34254
+ packageName: "zustand",
34255
+ version: zustandSpec
34256
+ }),
34257
+ sourceDirectory: directory
34258
+ };
34259
+ if (shouldReplaceSupportedDependencyFact(facts.zustand, nextZustandFact, 5)) facts.zustand = nextZustandFact;
34260
+ }
34216
34261
  if (facts.reanimatedVersion === null) {
34217
34262
  const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
34218
34263
  if (spec !== null) facts.reanimatedVersion = spec;
34219
34264
  }
34265
+ if (facts.valtioVersion === null) {
34266
+ const spec = getDependencySpec(packageJson, "valtio");
34267
+ if (spec !== null) facts.valtioVersion = spec;
34268
+ }
34220
34269
  facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
34221
34270
  facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
34222
34271
  facts.hasSsrDependency = facts.hasSsrDependency || isPackageJsonSsrAware(packageJson);
34272
+ const remotionSpec = getDependencySpec(packageJson, "remotion");
34273
+ if (remotionSpec !== null) {
34274
+ facts.hasRemotionDependency = true;
34275
+ const resolvedRemotionVersion = resolveCatalogBackedDependencyVersion({
34276
+ rootDirectory,
34277
+ rootPackageJson,
34278
+ packageName: "remotion",
34279
+ version: remotionSpec
34280
+ });
34281
+ const remotionMajorVersion = resolvedRemotionVersion === null ? null : getLowestDependencyMajor(resolvedRemotionVersion);
34282
+ if (remotionMajorVersion === null) {
34283
+ facts.hasUnknownRemotionVersion = true;
34284
+ facts.remotionVersion = null;
34285
+ } else if (!facts.hasUnknownRemotionVersion) {
34286
+ const currentRemotionMajorVersion = facts.remotionVersion === null ? null : getLowestDependencyMajor(facts.remotionVersion);
34287
+ if (currentRemotionMajorVersion === null || remotionMajorVersion < currentRemotionMajorVersion) facts.remotionVersion = resolvedRemotionVersion;
34288
+ }
34289
+ }
34223
34290
  };
34224
34291
  const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
34225
34292
  const facts = {
@@ -34239,12 +34306,28 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
34239
34306
  version: null,
34240
34307
  sourceDirectory: null
34241
34308
  },
34309
+ valtioVersion: null,
34310
+ mobx: {
34311
+ version: null,
34312
+ sourceDirectory: null
34313
+ },
34314
+ hasMobxReact: false,
34315
+ hasMobxReactLite: false,
34316
+ hasMobxStateTree: false,
34317
+ hasMobxReactObserver: false,
34318
+ zustand: {
34319
+ version: null,
34320
+ sourceDirectory: null
34321
+ },
34242
34322
  hasReactNativeAwarePackage: false,
34243
34323
  hasReanimatedAwarePackage: false,
34244
34324
  hasSsrDependency: false,
34325
+ hasRemotionDependency: false,
34326
+ hasUnknownRemotionVersion: false,
34327
+ remotionVersion: null,
34245
34328
  reanimatedVersion: null
34246
34329
  };
34247
- evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
34330
+ evaluateManifestFacts(facts, rootPackageJson, rootDirectory, rootDirectory, rootPackageJson);
34248
34331
  let isReactGroupSettled = !collectReactGroup;
34249
34332
  const visitedDirectories = /* @__PURE__ */ new Set();
34250
34333
  for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
@@ -34253,7 +34336,7 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
34253
34336
  if (visitedDirectories.has(workspaceDirectory)) continue;
34254
34337
  visitedDirectories.add(workspaceDirectory);
34255
34338
  const workspacePackageJson = readPackageJson(Path.join(workspaceDirectory, "package.json"));
34256
- evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
34339
+ evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory, rootDirectory, rootPackageJson);
34257
34340
  const info = extractDependencyInfo(workspacePackageJson);
34258
34341
  if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
34259
34342
  if (isReactGroupSettled) continue;
@@ -34477,11 +34560,24 @@ const discoverProjectWithoutPackageJson = (directory) => {
34477
34560
  tailwindVersion: null,
34478
34561
  zodVersion: null,
34479
34562
  zodMajorVersion: null,
34563
+ mobxVersion: null,
34564
+ mobxMajorVersion: null,
34565
+ hasMobxReact: false,
34566
+ hasMobxReactLite: false,
34567
+ hasMobxStateTree: false,
34568
+ hasMobxReactObserver: false,
34569
+ zustandVersion: null,
34570
+ zustandMajorVersion: null,
34480
34571
  framework: "unknown",
34481
34572
  hasTypeScript: hasOwnTsConfig,
34482
34573
  hasReactCompiler: false,
34483
34574
  hasReactCompilerLintPlugin: false,
34484
34575
  hasTanStackQuery: false,
34576
+ valtioVersion: null,
34577
+ valtioMajorVersion: null,
34578
+ hasRemotion: false,
34579
+ remotionVersion: null,
34580
+ remotionMajorVersion: null,
34485
34581
  hasSsrDependency: false,
34486
34582
  preactVersion: null,
34487
34583
  preactMajorVersion: null,
@@ -34580,6 +34676,12 @@ const discoverProject = (directory) => {
34580
34676
  packageName: SHOPIFY_FLASH_LIST_PACKAGE_NAME,
34581
34677
  version: workspaceFacts.shopifyFlashList.version
34582
34678
  }) : null;
34679
+ const zustandVersion = resolveCatalogBackedDependencyVersion({
34680
+ rootDirectory: directory,
34681
+ rootPackageJson: packageJson,
34682
+ packageName: "zustand",
34683
+ version: workspaceFacts.zustand.version
34684
+ });
34583
34685
  const hasReanimated = hasReactNativeWorkspace && workspaceFacts.hasReanimatedAwarePackage;
34584
34686
  const reanimatedVersion = hasReanimated ? workspaceFacts.reanimatedVersion : null;
34585
34687
  const nextjsVersion = framework === "nextjs" ? resolveCatalogBackedDependencyVersion({
@@ -34588,7 +34690,20 @@ const discoverProject = (directory) => {
34588
34690
  packageName: "next",
34589
34691
  version: workspaceFacts.next.version
34590
34692
  }) : null;
34693
+ const valtioVersion = resolveCatalogBackedDependencyVersion({
34694
+ rootDirectory: directory,
34695
+ rootPackageJson: packageJson,
34696
+ packageName: "valtio",
34697
+ version: workspaceFacts.valtioVersion
34698
+ });
34699
+ const mobxVersion = resolveCatalogBackedDependencyVersion({
34700
+ rootDirectory: directory,
34701
+ rootPackageJson: packageJson,
34702
+ packageName: "mobx",
34703
+ version: workspaceFacts.mobx.version
34704
+ });
34591
34705
  const preactVersion = getPreactVersion(packageJson);
34706
+ const remotionVersion = workspaceFacts.remotionVersion;
34592
34707
  const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
34593
34708
  const projectInfo = {
34594
34709
  rootDirectory: directory,
@@ -34598,11 +34713,24 @@ const discoverProject = (directory) => {
34598
34713
  tailwindVersion,
34599
34714
  zodVersion,
34600
34715
  zodMajorVersion: zodVersion === null ? null : getLowestDependencyMajor(zodVersion),
34716
+ mobxVersion,
34717
+ mobxMajorVersion: mobxVersion === null ? null : getLowestDependencyMajor(mobxVersion),
34718
+ hasMobxReact: workspaceFacts.hasMobxReact,
34719
+ hasMobxReactLite: workspaceFacts.hasMobxReactLite,
34720
+ hasMobxStateTree: workspaceFacts.hasMobxStateTree,
34721
+ hasMobxReactObserver: workspaceFacts.hasMobxReactObserver,
34722
+ zustandVersion,
34723
+ zustandMajorVersion: zustandVersion === null ? null : getLowestDependencyMajor(zustandVersion),
34601
34724
  framework,
34602
34725
  hasTypeScript,
34603
34726
  hasReactCompiler: detectReactCompiler(directory, packageJson),
34604
34727
  hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
34605
34728
  hasTanStackQuery: hasTanStackQuery(packageJson),
34729
+ valtioVersion,
34730
+ valtioMajorVersion: valtioVersion === null ? null : getLowestDependencyMajor(valtioVersion),
34731
+ hasRemotion: workspaceFacts.hasRemotionDependency,
34732
+ remotionVersion,
34733
+ remotionMajorVersion: remotionVersion === null ? null : getLowestDependencyMajor(remotionVersion),
34606
34734
  hasSsrDependency: workspaceFacts.hasSsrDependency,
34607
34735
  preactVersion,
34608
34736
  preactMajorVersion: parseReactMajor(preactVersion),
@@ -34836,7 +34964,6 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
34836
34964
  "react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
34837
34965
  "react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
34838
34966
  "react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
34839
- "react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
34840
34967
  "react/jsx-no-undef": "react-doctor/jsx-no-undef",
34841
34968
  "react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
34842
34969
  "react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
@@ -37049,9 +37176,25 @@ const BABEL_CONFIG_FILE_NAMES = [
37049
37176
  ".babelrc.json"
37050
37177
  ];
37051
37178
  const LEGACY_PRESET_SPEC = "module:metro-react-native-babel-preset";
37179
+ const LEGACY_PRESET_PACKAGE = "metro-react-native-babel-preset";
37180
+ const REACT_NATIVE_PACKAGE = "react-native";
37181
+ const PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION = "0.73.0";
37182
+ const LEGACY_PRESET_REFERENCE = new RegExp(`['"]${LEGACY_PRESET_SPEC}['"]`);
37052
37183
  const MODERN_PRESET_REFERENCE = new RegExp(`['"]module:@react-native/babel-preset['"]`);
37053
37184
  const ENABLE_BABEL_RUNTIME_VERSION = /enableBabelRuntime["']?\s*:\s*['"]/;
37185
+ const isReactNativeVersionAtLeastPresetRename = (rootDirectory, packageJson) => {
37186
+ const installedReactNativeManifestPath = Path.join(rootDirectory, "node_modules", REACT_NATIVE_PACKAGE, "package.json");
37187
+ const reactNativeVersionSpec = (isFile(installedReactNativeManifestPath) ? readPackageJson(installedReactNativeManifestPath).version : void 0) ?? getDependencySpec(packageJson, REACT_NATIVE_PACKAGE);
37188
+ if (reactNativeVersionSpec === null || reactNativeVersionSpec === void 0) return false;
37189
+ const normalizedVersionRange = import_semver.validRange(reactNativeVersionSpec);
37190
+ if (normalizedVersionRange === null) return false;
37191
+ const minimumVersion = import_semver.minVersion(normalizedVersionRange);
37192
+ return minimumVersion !== null && import_semver.gte(minimumVersion, PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION);
37193
+ };
37194
+ const canResolveLegacyPreset = (rootDirectory, packageJson) => getDependencySpec(packageJson, LEGACY_PRESET_PACKAGE) !== null || isFile(Path.join(rootDirectory, "node_modules", LEGACY_PRESET_PACKAGE, "package.json"));
37054
37195
  const checkReactNativeMetroBabelPreset = (rootDirectory) => {
37196
+ const packageJson = readPackageJson(Path.join(rootDirectory, "package.json"));
37197
+ const shouldReportLegacyPreset = isReactNativeVersionAtLeastPresetRename(rootDirectory, packageJson) && !canResolveLegacyPreset(rootDirectory, packageJson);
37055
37198
  for (const fileName of BABEL_CONFIG_FILE_NAMES) {
37056
37199
  const filePath = Path.join(rootDirectory, fileName);
37057
37200
  if (!isFile(filePath)) continue;
@@ -37061,7 +37204,7 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
37061
37204
  } catch {
37062
37205
  continue;
37063
37206
  }
37064
- if (contents.includes(LEGACY_PRESET_SPEC)) return [buildReactNativeDiagnostic({
37207
+ if (shouldReportLegacyPreset && LEGACY_PRESET_REFERENCE.test(contents)) return [buildReactNativeDiagnostic({
37065
37208
  filePath: fileName,
37066
37209
  rule: "rn-no-metro-babel-preset",
37067
37210
  severity: "error",
@@ -37084,12 +37227,6 @@ const checkReactNativeProject = (rootDirectory, project) => {
37084
37227
  return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
37085
37228
  };
37086
37229
  const RULE_KEY = "no-vulnerable-react-server-components";
37087
- const DEPENDENCY_SECTIONS = [
37088
- "dependencies",
37089
- "devDependencies",
37090
- "peerDependencies",
37091
- "optionalDependencies"
37092
- ];
37093
37230
  const REACT_RSC_ADVISORIES_BY_MINOR = {
37094
37231
  "19.0": {
37095
37232
  rceFixedVersion: "19.0.1",
@@ -37136,20 +37273,13 @@ const enumerateWorkspaceDirectories = (workspaceRoot) => {
37136
37273
  for (const pattern of patterns) for (const directory of resolveWorkspaceDirectories(workspaceRoot, pattern)) directories.add(directory);
37137
37274
  return [...directories];
37138
37275
  };
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
37276
  const resolveVersionInDirectory = (directory, packageName, declaredSpecOverride) => {
37147
37277
  const manifestPath = Path.join(directory, "node_modules", packageName, "package.json");
37148
37278
  if (isFile(manifestPath)) {
37149
37279
  const installedVersion = import_semver.valid(readPackageJson(manifestPath).version ?? null);
37150
37280
  if (installedVersion !== null) return installedVersion;
37151
37281
  }
37152
- const candidateSpecs = [readDeclaredSpec(readPackageJson(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
37282
+ const candidateSpecs = [getDependencySpec(readPackageJson(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
37153
37283
  for (const spec of candidateSpecs) {
37154
37284
  const pinnedVersion = spec === null ? null : import_semver.valid(spec);
37155
37285
  if (pinnedVersion !== null) return pinnedVersion;
@@ -37829,9 +37959,24 @@ const buildCapabilities = (project) => {
37829
37959
  })) capabilities.add("tailwind:3.4");
37830
37960
  if (project.zodVersion !== null) capabilities.add("zod");
37831
37961
  if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
37962
+ if (project.mobxVersion !== void 0 && project.mobxVersion !== null || project.hasMobxReact === true || project.hasMobxReactLite === true || project.hasMobxStateTree === true || project.hasMobxReactObserver === true) capabilities.add("mobx");
37963
+ if (project.hasMobxReact === true) capabilities.add("mobx-react");
37964
+ if (project.hasMobxReactLite === true) capabilities.add("mobx-react-lite");
37965
+ if (project.hasMobxReact === true || project.hasMobxReactLite === true) capabilities.add("mobx-react-binding");
37966
+ if (project.hasMobxStateTree === true) capabilities.add("mobx-state-tree");
37967
+ if (project.hasMobxReactObserver === true) capabilities.add("mobx-react-observer");
37968
+ if (project.mobxMajorVersion !== void 0 && project.mobxMajorVersion !== null && project.mobxMajorVersion >= 4 && project.mobxMajorVersion <= 6) addMajorLadder(capabilities, "mobx", project.mobxMajorVersion, 4, 6);
37969
+ if (project.zustandVersion !== void 0 && project.zustandVersion !== null) capabilities.add("zustand");
37970
+ if (project.zustandMajorVersion !== void 0 && project.zustandMajorVersion !== null && project.zustandMajorVersion >= 1 && project.zustandMajorVersion <= 5) addMajorLadder(capabilities, "zustand", project.zustandMajorVersion, 1, 5);
37832
37971
  if (project.isPreES2023Target) capabilities.add("pre-es2023");
37833
37972
  if (project.hasReactCompiler) capabilities.add("react-compiler");
37834
37973
  if (project.hasTanStackQuery) capabilities.add("tanstack-query");
37974
+ if (project.valtioVersion !== null) capabilities.add("valtio");
37975
+ addMajorLadder(capabilities, "valtio", project.valtioMajorVersion, 1, 10);
37976
+ if (project.hasRemotion) {
37977
+ capabilities.add("remotion");
37978
+ addMajorLadder(capabilities, "remotion", project.remotionMajorVersion ?? null, 4, 30);
37979
+ }
37835
37980
  if (project.hasTypeScript) capabilities.add("typescript");
37836
37981
  if (project.preactVersion !== null) capabilities.add("preact");
37837
37982
  addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
@@ -39068,6 +39213,35 @@ const parseGithubViewerPermission = (stdout) => {
39068
39213
  return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
39069
39214
  };
39070
39215
  const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
39216
+ const parseBaselineDiffPlan = (value) => {
39217
+ const entries = splitNullSeparated(value);
39218
+ const baseFiles = /* @__PURE__ */ new Set();
39219
+ const headFiles = /* @__PURE__ */ new Set();
39220
+ for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 2) {
39221
+ const status = entries[entryIndex];
39222
+ const filePath = entries[entryIndex + 1];
39223
+ if (status === void 0 || filePath === void 0 || status.length !== 1) return null;
39224
+ if (status === "A") {
39225
+ headFiles.add(filePath);
39226
+ continue;
39227
+ }
39228
+ if (status === "D") {
39229
+ baseFiles.add(filePath);
39230
+ continue;
39231
+ }
39232
+ if (status === "M" || status === "T") {
39233
+ baseFiles.add(filePath);
39234
+ headFiles.add(filePath);
39235
+ continue;
39236
+ }
39237
+ return null;
39238
+ }
39239
+ return {
39240
+ baseFiles: [...baseFiles],
39241
+ headFiles: [...headFiles],
39242
+ untrackedFiles: []
39243
+ };
39244
+ };
39071
39245
  const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
39072
39246
  /**
39073
39247
  * `Git` wraps every `git`-via-subprocess call react-doctor makes
@@ -39270,6 +39444,45 @@ var Git = class Git extends Service()("react-doctor/Git") {
39270
39444
  githubViewerPermission,
39271
39445
  branchExists,
39272
39446
  mergeBase,
39447
+ baselineDiffPlan: (input) => {
39448
+ if (!isSafeGitRevision(input.ref)) return succeed$2(null);
39449
+ return gen(function* () {
39450
+ const unmerged = yield* runGit(input.directory, [
39451
+ "diff",
39452
+ "--no-ext-diff",
39453
+ "-z",
39454
+ "--name-only",
39455
+ "--diff-filter=U",
39456
+ "--relative"
39457
+ ]);
39458
+ if (unmerged.status !== 0 || unmerged.stdout.length > 0) return null;
39459
+ const result = yield* runGit(input.directory, [
39460
+ "diff",
39461
+ "--no-ext-diff",
39462
+ "--no-textconv",
39463
+ "--no-renames",
39464
+ "-z",
39465
+ "--name-status",
39466
+ "--relative",
39467
+ input.ref
39468
+ ]);
39469
+ if (result.status !== 0) return null;
39470
+ const plan = parseBaselineDiffPlan(result.stdout);
39471
+ if (plan === null) return null;
39472
+ const untracked = yield* runGit(input.directory, [
39473
+ "ls-files",
39474
+ "--others",
39475
+ "--exclude-standard",
39476
+ "-z"
39477
+ ]);
39478
+ if (untracked.status !== 0) return null;
39479
+ return {
39480
+ baseFiles: plan.baseFiles,
39481
+ headFiles: plan.headFiles,
39482
+ untrackedFiles: splitNullSeparated(untracked.stdout)
39483
+ };
39484
+ }).pipe(catch_$1(() => succeed$2(null)), withSpan("Git.baselineDiffPlan"));
39485
+ },
39273
39486
  diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
39274
39487
  if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
39275
39488
  if (explicitBaseBranch !== void 0) {
@@ -39419,6 +39632,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
39419
39632
  githubViewerPermission: () => succeed$2(snapshot.githubViewerPermission ?? null),
39420
39633
  branchExists: (_directory, branch) => succeed$2(snapshot.branchExists?.get(branch) ?? false),
39421
39634
  mergeBase: ({ ref }) => succeed$2(snapshot.mergeBase?.get(ref) ?? null),
39635
+ baselineDiffPlan: () => succeed$2(snapshot.baselineDiffPlan ?? null),
39422
39636
  diffSelection: () => succeed$2(snapshot.diffSelection ?? null),
39423
39637
  stagedFilePaths: () => succeed$2(snapshot.stagedFiles ?? []),
39424
39638
  showStagedContent: (_directory, relativePath) => succeed$2(snapshot.stagedContent?.get(relativePath) ?? null),
@@ -40756,12 +40970,17 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
40756
40970
  const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
40757
40971
  const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
40758
40972
  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`.";
40973
+ 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`.";
40759
40974
  const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
40760
40975
  const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
40761
40976
  const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
40762
40977
  if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
40763
40978
  return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
40764
40979
  };
40980
+ const buildSetStateInEffectMessage = (reasonSummary) => {
40981
+ const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
40982
+ return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
40983
+ };
40765
40984
  const PLUGIN_CATEGORY_MAP = {
40766
40985
  react: "Bugs",
40767
40986
  "react-hooks": "Bugs",
@@ -40819,6 +41038,10 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
40819
41038
  message: REACT_ERROR_BOUNDARY_MESSAGE,
40820
41039
  help: reasonDetail || help
40821
41040
  };
41041
+ if (rule === "set-state-in-effect") return {
41042
+ message: buildSetStateInEffectMessage(reasonSummary.trim()),
41043
+ help: reasonDetail || help
41044
+ };
40822
41045
  return {
40823
41046
  message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
40824
41047
  help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
@@ -43144,12 +43367,19 @@ const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
43144
43367
  */
43145
43368
  const materializeSourceTree = (input) => gen(function* () {
43146
43369
  const materializedFiles = [];
43370
+ const unmaterializedFiles = [];
43147
43371
  const resolvedTempDirectory = path.resolve(input.tempDirectory);
43148
43372
  for (const relativePath of input.files) {
43149
43373
  const content = yield* input.readContent(relativePath).pipe(orElseSucceed(() => null));
43150
- if (content === null) continue;
43374
+ if (content === null) {
43375
+ unmaterializedFiles.push(relativePath);
43376
+ continue;
43377
+ }
43151
43378
  const candidateTargetPath = path.resolve(resolvedTempDirectory, relativePath);
43152
- if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory)) continue;
43379
+ if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory)) {
43380
+ unmaterializedFiles.push(relativePath);
43381
+ continue;
43382
+ }
43153
43383
  yield* sync(() => {
43154
43384
  fs.mkdirSync(path.dirname(candidateTargetPath), { recursive: true });
43155
43385
  fs.writeFileSync(candidateTargetPath, content);
@@ -43166,6 +43396,7 @@ const materializeSourceTree = (input) => gen(function* () {
43166
43396
  return {
43167
43397
  tempDirectory: input.tempDirectory,
43168
43398
  materializedFiles,
43399
+ unmaterializedFiles,
43169
43400
  cleanup: () => {
43170
43401
  try {
43171
43402
  fs.rmSync(input.tempDirectory, {
@@ -45371,5 +45602,5 @@ const startLanguageServer = () => {
45371
45602
  };
45372
45603
  //#endregion
45373
45604
  export { startLanguageServer };
45374
- !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]="0f56accc-b639-5b90-a1a3-5faa727907dc")}catch(e){}}();
45375
- //# debugId=0f56accc-b639-5b90-a1a3-5faa727907dc
45605
+ !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]="f2369b82-a22a-50e5-b71b-2ceb30eaee9f")}catch(e){}}();
45606
+ //# debugId=f2369b82-a22a-50e5-b71b-2ceb30eaee9f
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.8.1-dev.7818922",
3
+ "version": "0.8.1-dev.7eca0ee",
4
4
  "description": "Your agent writes bad React. This catches it",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -66,7 +66,7 @@
66
66
  "vscode-uri": "^3.1.0",
67
67
  "yaml": "^2.9.0",
68
68
  "deslop-js": "0.8.1",
69
- "oxlint-plugin-react-doctor": "0.8.1-dev.7818922"
69
+ "oxlint-plugin-react-doctor": "0.8.1-dev.7eca0ee"
70
70
  },
71
71
  "devDependencies": {
72
72
  "@types/babel__code-frame": "^7.27.0",