react-doctor 0.2.14-dev.75c1f99 → 0.2.14-dev.8921575

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.
Files changed (3) hide show
  1. package/dist/cli.js +139 -7
  2. package/dist/index.js +136 -4
  3. package/package.json +4 -4
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]="74dccde1-47a7-5718-88d7-ea8e5d7b463b")}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]="d012dbb2-27e9-5092-9eab-c7fa4573f0b2")}catch(e){}}();
3
3
  import { createRequire } from "node:module";
4
4
  import { execFileSync, spawn, spawnSync } from "node:child_process";
5
5
  import * as Path from "node:path";
@@ -8016,6 +8016,61 @@ const checkExpoPackageJsonConflicts = (context) => {
8016
8016
  }));
8017
8017
  return diagnostics;
8018
8018
  };
8019
+ const APP_CONFIG_JSON_FILES = ["app.config.json", "app.json"];
8020
+ const APP_CONFIG_DYNAMIC_FILES = [
8021
+ "app.config.ts",
8022
+ "app.config.js",
8023
+ "app.config.cjs",
8024
+ "app.config.mjs"
8025
+ ];
8026
+ const ExpoConfigSchema = Schema.Struct({
8027
+ newArchEnabled: Schema.optional(Schema.Boolean),
8028
+ updates: Schema.optional(Schema.Struct({ disableAntiBrickingMeasures: Schema.optional(Schema.Boolean) }))
8029
+ });
8030
+ const AppManifestSchema = Schema.Struct({ expo: Schema.optional(ExpoConfigSchema) });
8031
+ const NO_CONFIG = {
8032
+ config: null,
8033
+ configFile: null
8034
+ };
8035
+ const decodeExpoConfig = (filePath) => {
8036
+ let raw;
8037
+ try {
8038
+ raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
8039
+ } catch {
8040
+ return null;
8041
+ }
8042
+ return Option.getOrNull(Schema.decodeUnknownOption(AppManifestSchema)(raw))?.expo ?? null;
8043
+ };
8044
+ const readExpoAppConfig = (rootDirectory) => {
8045
+ if (APP_CONFIG_DYNAMIC_FILES.some((fileName) => isFile(path.join(rootDirectory, fileName)))) return NO_CONFIG;
8046
+ for (const fileName of APP_CONFIG_JSON_FILES) {
8047
+ const filePath = path.join(rootDirectory, fileName);
8048
+ if (!isFile(filePath)) continue;
8049
+ const config = decodeExpoConfig(filePath);
8050
+ if (config) return {
8051
+ config,
8052
+ configFile: fileName
8053
+ };
8054
+ }
8055
+ return NO_CONFIG;
8056
+ };
8057
+ const REANIMATED_PACKAGE = "react-native-reanimated";
8058
+ const WORKLETS_PACKAGE = "react-native-worklets";
8059
+ const FIRST_NEW_ARCH_ONLY_REANIMATED_MAJOR = 4;
8060
+ const checkExpoReanimatedNewArch = (context) => {
8061
+ const reanimatedSpec = context.packageJson.dependencies?.[REANIMATED_PACKAGE] ?? context.packageJson.devDependencies?.[REANIMATED_PACKAGE];
8062
+ const reanimatedMajor = reanimatedSpec === void 0 ? null : getLowestDependencyMajor(reanimatedSpec);
8063
+ if (!(reanimatedMajor !== null && reanimatedMajor >= FIRST_NEW_ARCH_ONLY_REANIMATED_MAJOR || context.directDependencyNames.has(WORKLETS_PACKAGE))) return [];
8064
+ const appConfig = readExpoAppConfig(context.rootDirectory);
8065
+ if (appConfig.config?.newArchEnabled !== false) return [];
8066
+ return [buildExpoDiagnostic({
8067
+ rule: "expo-reanimated-v4-requires-new-arch",
8068
+ severity: "error",
8069
+ filePath: appConfig.configFile ?? "app.json",
8070
+ message: "react-native-reanimated v4 supports only the New Architecture, but `newArchEnabled: false` is set in your app config, so the app will crash on first launch.",
8071
+ help: "Remove `newArchEnabled: false` from your app config (the New Architecture is the default on SDK 52+), or pin react-native-reanimated to v3 if you must stay on the legacy architecture."
8072
+ })];
8073
+ };
8019
8074
  const EXPO_ROUTER_REACT_NAVIGATION_MIN_SDK_MAJOR = 56;
8020
8075
  const EXPO_ROUTER_REACT_NAVIGATION_MAX_SDK_MAJOR_EXCLUSIVE = 57;
8021
8076
  const checkExpoRouterReactNavigation = (context) => {
@@ -8031,6 +8086,17 @@ const checkExpoRouterReactNavigation = (context) => {
8031
8086
  help: "Remove these `@react-navigation/*` packages and replace direct imports with their expo-router equivalents. See https://docs.expo.dev/router/migrate/sdk-55-to-56/"
8032
8087
  })];
8033
8088
  };
8089
+ const checkExpoUpdatesConfig = (context) => {
8090
+ const appConfig = readExpoAppConfig(context.rootDirectory);
8091
+ if (appConfig.config?.updates?.disableAntiBrickingMeasures !== true) return [];
8092
+ return [buildExpoDiagnostic({
8093
+ rule: "expo-updates-no-unsafe-production-config",
8094
+ severity: "error",
8095
+ filePath: appConfig.configFile ?? "app.json",
8096
+ message: "`updates.disableAntiBrickingMeasures: true` disables expo-updates' recovery safeguards and is liable to leave installed apps in a permanently bricked state, so it must not be used in production.",
8097
+ help: "Remove `disableAntiBrickingMeasures` from your app config's `updates` block. See https://docs.expo.dev/versions/latest/config/app/#updates"
8098
+ })];
8099
+ };
8034
8100
  const VECTOR_ICONS_MIN_SDK_MAJOR = 56;
8035
8101
  const SCOPED_VECTOR_ICONS_NAMESPACE = "@react-native-vector-icons/";
8036
8102
  const CONFLICTING_VECTOR_ICONS_PACKAGES = ["@expo/vector-icons", "react-native-vector-icons"];
@@ -8057,7 +8123,9 @@ const checkExpoProject = (rootDirectory, project) => {
8057
8123
  ...checkExpoLockfile(context),
8058
8124
  ...checkExpoGitignore(context),
8059
8125
  ...checkExpoEnvLocalFiles(context),
8060
- ...checkExpoMetroConfig(context)
8126
+ ...checkExpoMetroConfig(context),
8127
+ ...checkExpoReanimatedNewArch(context),
8128
+ ...checkExpoUpdatesConfig(context)
8061
8129
  ];
8062
8130
  };
8063
8131
  const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
@@ -8174,6 +8242,69 @@ const checkPnpmHardening = (rootDirectory) => {
8174
8242
  }));
8175
8243
  return diagnostics;
8176
8244
  };
8245
+ const BUILDER_BOB_PACKAGE = "react-native-builder-bob";
8246
+ const isBuilderBobLibrary = (packageJson) => {
8247
+ const bobConfig = packageJson[BUILDER_BOB_PACKAGE];
8248
+ return typeof bobConfig === "object" && bobConfig !== null;
8249
+ };
8250
+ const checkReactNativeLibraryDependencies = (rootDirectory) => {
8251
+ const packageJson = readPackageJson$1(path.join(rootDirectory, "package.json"));
8252
+ if (!isBuilderBobLibrary(packageJson)) return [];
8253
+ const misplaced = ["react", "react-native"].filter((name) => packageJson.dependencies?.[name] !== void 0);
8254
+ if (misplaced.length === 0) return [];
8255
+ const quoted = misplaced.map((name) => `"${name}"`).join(" and ");
8256
+ return [{
8257
+ filePath: "package.json",
8258
+ plugin: "react-doctor",
8259
+ rule: "rn-library-react-in-dependencies",
8260
+ severity: "warning",
8261
+ message: `This react-native-builder-bob library lists ${quoted} in \`dependencies\` — that ships a second copy into consumer apps, causing "Invalid hook call" (duplicate React) and duplicate-native-module crashes.`,
8262
+ help: `Move ${quoted} to \`peerDependencies\` (keep ${misplaced.length === 1 ? "it" : "them"} in \`devDependencies\` for local development).`,
8263
+ line: 0,
8264
+ column: 0,
8265
+ category: "Correctness"
8266
+ }];
8267
+ };
8268
+ const BABEL_CONFIG_FILE_NAMES = [
8269
+ "babel.config.js",
8270
+ "babel.config.cjs",
8271
+ "babel.config.mjs",
8272
+ "babel.config.json",
8273
+ ".babelrc",
8274
+ ".babelrc.js",
8275
+ ".babelrc.json"
8276
+ ];
8277
+ const LEGACY_PRESET_SPEC = "module:metro-react-native-babel-preset";
8278
+ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
8279
+ for (const fileName of BABEL_CONFIG_FILE_NAMES) {
8280
+ const filePath = path.join(rootDirectory, fileName);
8281
+ if (!isFile(filePath)) continue;
8282
+ let contents;
8283
+ try {
8284
+ contents = fs.readFileSync(filePath, "utf-8");
8285
+ } catch {
8286
+ continue;
8287
+ }
8288
+ if (!contents.includes(LEGACY_PRESET_SPEC)) continue;
8289
+ return [{
8290
+ filePath: fileName,
8291
+ plugin: "react-doctor",
8292
+ rule: "rn-no-metro-babel-preset",
8293
+ severity: "error",
8294
+ message: "`module:metro-react-native-babel-preset` was renamed to `@react-native/babel-preset` and is no longer installed by React Native 0.73+ — this preset reference fails to resolve and breaks the Metro/Babel transform.",
8295
+ help: "Replace the preset with `module:@react-native/babel-preset` (or `babel-preset-expo` on Expo) and remove the old `metro-react-native-babel-preset` dependency.",
8296
+ line: 0,
8297
+ column: 0,
8298
+ category: "Correctness"
8299
+ }];
8300
+ }
8301
+ return [];
8302
+ };
8303
+ const isReactNativeProject = (project) => project.framework === "react-native" || project.framework === "expo" || project.hasReactNativeWorkspace || project.expoVersion !== null;
8304
+ const checkReactNativeProject = (rootDirectory, project) => {
8305
+ if (!isReactNativeProject(project)) return [];
8306
+ return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
8307
+ };
8177
8308
  const REDUCED_MOTION_GREP_PATTERN = "prefers-reduced-motion|useReducedMotion|MotionConfig|reducedMotion";
8178
8309
  const REDUCED_MOTION_FILE_GLOBS = [
8179
8310
  "*.ts",
@@ -10931,7 +11062,8 @@ const runInspect = (input, hooks = {}) => Effect.gen(function* () {
10931
11062
  const environmentDiagnostics = isDiffMode ? [] : [
10932
11063
  ...checkReducedMotion(scanDirectory),
10933
11064
  ...checkPnpmHardening(scanDirectory),
10934
- ...checkExpoProject(scanDirectory, project)
11065
+ ...checkExpoProject(scanDirectory, project),
11066
+ ...checkReactNativeProject(scanDirectory, project)
10935
11067
  ];
10936
11068
  const envCollected = yield* Stream.runCollect(applyPerElementPipeline(Stream.fromIterable(environmentDiagnostics)));
10937
11069
  const lintFailure = yield* Ref.make({
@@ -11669,7 +11801,7 @@ const makeNoopConsole = () => ({
11669
11801
  });
11670
11802
  //#endregion
11671
11803
  //#region src/cli/utils/version.ts
11672
- const VERSION = "0.2.14-dev.75c1f99";
11804
+ const VERSION = "0.2.14-dev.8921575";
11673
11805
  //#endregion
11674
11806
  //#region src/cli/utils/json-mode.ts
11675
11807
  let context = null;
@@ -11985,13 +12117,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
11985
12117
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
11986
12118
  * standard `SENTRY_RELEASE` override.
11987
12119
  */
11988
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.2.14-dev.75c1f99`;
12120
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.2.14-dev.8921575`;
11989
12121
  /**
11990
12122
  * Deployment environment shown in Sentry's environment filter. Defaults to
11991
12123
  * `production` for tagged releases and `development` for dev/unbuilt versions,
11992
12124
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
11993
12125
  */
11994
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.2.14-dev.75c1f99") ? "development" : "production");
12126
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.2.14-dev.8921575") ? "development" : "production");
11995
12127
  /**
11996
12128
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
11997
12129
  * (set to `0` to disable tracing) and falls back to
@@ -19525,4 +19657,4 @@ program.parseAsync(argv).then(() => flushSentry()).catch(async (error) => {
19525
19657
  export {};
19526
19658
 
19527
19659
  //# sourceMappingURL=cli.js.map
19528
- //# debugId=74dccde1-47a7-5718-88d7-ea8e5d7b463b
19660
+ //# debugId=d012dbb2-27e9-5092-9eab-c7fa4573f0b2
package/dist/index.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]="fce73b02-d297-5132-af08-817f37e1467c")}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]="df20b0d3-9e4e-52e0-8991-ce8a6349a04a")}catch(e){}}();
3
3
  import { createRequire } from "node:module";
4
4
  import * as Schema from "effect/Schema";
5
5
  import * as fs$1 from "node:fs";
@@ -4966,6 +4966,61 @@ const checkExpoPackageJsonConflicts = (context) => {
4966
4966
  }));
4967
4967
  return diagnostics;
4968
4968
  };
4969
+ const APP_CONFIG_JSON_FILES = ["app.config.json", "app.json"];
4970
+ const APP_CONFIG_DYNAMIC_FILES = [
4971
+ "app.config.ts",
4972
+ "app.config.js",
4973
+ "app.config.cjs",
4974
+ "app.config.mjs"
4975
+ ];
4976
+ const ExpoConfigSchema = Schema.Struct({
4977
+ newArchEnabled: Schema.optional(Schema.Boolean),
4978
+ updates: Schema.optional(Schema.Struct({ disableAntiBrickingMeasures: Schema.optional(Schema.Boolean) }))
4979
+ });
4980
+ const AppManifestSchema = Schema.Struct({ expo: Schema.optional(ExpoConfigSchema) });
4981
+ const NO_CONFIG = {
4982
+ config: null,
4983
+ configFile: null
4984
+ };
4985
+ const decodeExpoConfig = (filePath) => {
4986
+ let raw;
4987
+ try {
4988
+ raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
4989
+ } catch {
4990
+ return null;
4991
+ }
4992
+ return Option.getOrNull(Schema.decodeUnknownOption(AppManifestSchema)(raw))?.expo ?? null;
4993
+ };
4994
+ const readExpoAppConfig = (rootDirectory) => {
4995
+ if (APP_CONFIG_DYNAMIC_FILES.some((fileName) => isFile(path.join(rootDirectory, fileName)))) return NO_CONFIG;
4996
+ for (const fileName of APP_CONFIG_JSON_FILES) {
4997
+ const filePath = path.join(rootDirectory, fileName);
4998
+ if (!isFile(filePath)) continue;
4999
+ const config = decodeExpoConfig(filePath);
5000
+ if (config) return {
5001
+ config,
5002
+ configFile: fileName
5003
+ };
5004
+ }
5005
+ return NO_CONFIG;
5006
+ };
5007
+ const REANIMATED_PACKAGE = "react-native-reanimated";
5008
+ const WORKLETS_PACKAGE = "react-native-worklets";
5009
+ const FIRST_NEW_ARCH_ONLY_REANIMATED_MAJOR = 4;
5010
+ const checkExpoReanimatedNewArch = (context) => {
5011
+ const reanimatedSpec = context.packageJson.dependencies?.[REANIMATED_PACKAGE] ?? context.packageJson.devDependencies?.[REANIMATED_PACKAGE];
5012
+ const reanimatedMajor = reanimatedSpec === void 0 ? null : getLowestDependencyMajor(reanimatedSpec);
5013
+ if (!(reanimatedMajor !== null && reanimatedMajor >= FIRST_NEW_ARCH_ONLY_REANIMATED_MAJOR || context.directDependencyNames.has(WORKLETS_PACKAGE))) return [];
5014
+ const appConfig = readExpoAppConfig(context.rootDirectory);
5015
+ if (appConfig.config?.newArchEnabled !== false) return [];
5016
+ return [buildExpoDiagnostic({
5017
+ rule: "expo-reanimated-v4-requires-new-arch",
5018
+ severity: "error",
5019
+ filePath: appConfig.configFile ?? "app.json",
5020
+ message: "react-native-reanimated v4 supports only the New Architecture, but `newArchEnabled: false` is set in your app config, so the app will crash on first launch.",
5021
+ help: "Remove `newArchEnabled: false` from your app config (the New Architecture is the default on SDK 52+), or pin react-native-reanimated to v3 if you must stay on the legacy architecture."
5022
+ })];
5023
+ };
4969
5024
  const EXPO_ROUTER_REACT_NAVIGATION_MIN_SDK_MAJOR = 56;
4970
5025
  const EXPO_ROUTER_REACT_NAVIGATION_MAX_SDK_MAJOR_EXCLUSIVE = 57;
4971
5026
  const checkExpoRouterReactNavigation = (context) => {
@@ -4981,6 +5036,17 @@ const checkExpoRouterReactNavigation = (context) => {
4981
5036
  help: "Remove these `@react-navigation/*` packages and replace direct imports with their expo-router equivalents. See https://docs.expo.dev/router/migrate/sdk-55-to-56/"
4982
5037
  })];
4983
5038
  };
5039
+ const checkExpoUpdatesConfig = (context) => {
5040
+ const appConfig = readExpoAppConfig(context.rootDirectory);
5041
+ if (appConfig.config?.updates?.disableAntiBrickingMeasures !== true) return [];
5042
+ return [buildExpoDiagnostic({
5043
+ rule: "expo-updates-no-unsafe-production-config",
5044
+ severity: "error",
5045
+ filePath: appConfig.configFile ?? "app.json",
5046
+ message: "`updates.disableAntiBrickingMeasures: true` disables expo-updates' recovery safeguards and is liable to leave installed apps in a permanently bricked state, so it must not be used in production.",
5047
+ help: "Remove `disableAntiBrickingMeasures` from your app config's `updates` block. See https://docs.expo.dev/versions/latest/config/app/#updates"
5048
+ })];
5049
+ };
4984
5050
  const VECTOR_ICONS_MIN_SDK_MAJOR = 56;
4985
5051
  const SCOPED_VECTOR_ICONS_NAMESPACE = "@react-native-vector-icons/";
4986
5052
  const CONFLICTING_VECTOR_ICONS_PACKAGES = ["@expo/vector-icons", "react-native-vector-icons"];
@@ -5007,7 +5073,9 @@ const checkExpoProject = (rootDirectory, project) => {
5007
5073
  ...checkExpoLockfile(context),
5008
5074
  ...checkExpoGitignore(context),
5009
5075
  ...checkExpoEnvLocalFiles(context),
5010
- ...checkExpoMetroConfig(context)
5076
+ ...checkExpoMetroConfig(context),
5077
+ ...checkExpoReanimatedNewArch(context),
5078
+ ...checkExpoUpdatesConfig(context)
5011
5079
  ];
5012
5080
  };
5013
5081
  const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
@@ -5124,6 +5192,69 @@ const checkPnpmHardening = (rootDirectory) => {
5124
5192
  }));
5125
5193
  return diagnostics;
5126
5194
  };
5195
+ const BUILDER_BOB_PACKAGE = "react-native-builder-bob";
5196
+ const isBuilderBobLibrary = (packageJson) => {
5197
+ const bobConfig = packageJson[BUILDER_BOB_PACKAGE];
5198
+ return typeof bobConfig === "object" && bobConfig !== null;
5199
+ };
5200
+ const checkReactNativeLibraryDependencies = (rootDirectory) => {
5201
+ const packageJson = readPackageJson(path.join(rootDirectory, "package.json"));
5202
+ if (!isBuilderBobLibrary(packageJson)) return [];
5203
+ const misplaced = ["react", "react-native"].filter((name) => packageJson.dependencies?.[name] !== void 0);
5204
+ if (misplaced.length === 0) return [];
5205
+ const quoted = misplaced.map((name) => `"${name}"`).join(" and ");
5206
+ return [{
5207
+ filePath: "package.json",
5208
+ plugin: "react-doctor",
5209
+ rule: "rn-library-react-in-dependencies",
5210
+ severity: "warning",
5211
+ message: `This react-native-builder-bob library lists ${quoted} in \`dependencies\` — that ships a second copy into consumer apps, causing "Invalid hook call" (duplicate React) and duplicate-native-module crashes.`,
5212
+ help: `Move ${quoted} to \`peerDependencies\` (keep ${misplaced.length === 1 ? "it" : "them"} in \`devDependencies\` for local development).`,
5213
+ line: 0,
5214
+ column: 0,
5215
+ category: "Correctness"
5216
+ }];
5217
+ };
5218
+ const BABEL_CONFIG_FILE_NAMES = [
5219
+ "babel.config.js",
5220
+ "babel.config.cjs",
5221
+ "babel.config.mjs",
5222
+ "babel.config.json",
5223
+ ".babelrc",
5224
+ ".babelrc.js",
5225
+ ".babelrc.json"
5226
+ ];
5227
+ const LEGACY_PRESET_SPEC = "module:metro-react-native-babel-preset";
5228
+ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
5229
+ for (const fileName of BABEL_CONFIG_FILE_NAMES) {
5230
+ const filePath = path.join(rootDirectory, fileName);
5231
+ if (!isFile(filePath)) continue;
5232
+ let contents;
5233
+ try {
5234
+ contents = fs.readFileSync(filePath, "utf-8");
5235
+ } catch {
5236
+ continue;
5237
+ }
5238
+ if (!contents.includes(LEGACY_PRESET_SPEC)) continue;
5239
+ return [{
5240
+ filePath: fileName,
5241
+ plugin: "react-doctor",
5242
+ rule: "rn-no-metro-babel-preset",
5243
+ severity: "error",
5244
+ message: "`module:metro-react-native-babel-preset` was renamed to `@react-native/babel-preset` and is no longer installed by React Native 0.73+ — this preset reference fails to resolve and breaks the Metro/Babel transform.",
5245
+ help: "Replace the preset with `module:@react-native/babel-preset` (or `babel-preset-expo` on Expo) and remove the old `metro-react-native-babel-preset` dependency.",
5246
+ line: 0,
5247
+ column: 0,
5248
+ category: "Correctness"
5249
+ }];
5250
+ }
5251
+ return [];
5252
+ };
5253
+ const isReactNativeProject = (project) => project.framework === "react-native" || project.framework === "expo" || project.hasReactNativeWorkspace || project.expoVersion !== null;
5254
+ const checkReactNativeProject = (rootDirectory, project) => {
5255
+ if (!isReactNativeProject(project)) return [];
5256
+ return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
5257
+ };
5127
5258
  const REDUCED_MOTION_GREP_PATTERN = "prefers-reduced-motion|useReducedMotion|MotionConfig|reducedMotion";
5128
5259
  const REDUCED_MOTION_FILE_GLOBS = [
5129
5260
  "*.ts",
@@ -7884,7 +8015,8 @@ const runInspect = (input, hooks = {}) => Effect.gen(function* () {
7884
8015
  const environmentDiagnostics = isDiffMode ? [] : [
7885
8016
  ...checkReducedMotion(scanDirectory),
7886
8017
  ...checkPnpmHardening(scanDirectory),
7887
- ...checkExpoProject(scanDirectory, project)
8018
+ ...checkExpoProject(scanDirectory, project),
8019
+ ...checkReactNativeProject(scanDirectory, project)
7888
8020
  ];
7889
8021
  const envCollected = yield* Stream.runCollect(applyPerElementPipeline(Stream.fromIterable(environmentDiagnostics)));
7890
8022
  const lintFailure = yield* Ref.make({
@@ -8488,4 +8620,4 @@ const toJsonReport = (result, options) => buildJsonReport({
8488
8620
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
8489
8621
 
8490
8622
  //# sourceMappingURL=index.js.map
8491
- //# debugId=fce73b02-d297-5132-af08-817f37e1467c
8623
+ //# debugId=df20b0d3-9e4e-52e0-8991-ce8a6349a04a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.2.14-dev.75c1f99",
3
+ "version": "0.2.14-dev.8921575",
4
4
  "description": "Diagnose and fix React codebases for security, performance, correctness, accessibility, bundle-size, and architecture issues",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -63,7 +63,7 @@
63
63
  "oxlint": "^1.66.0",
64
64
  "prompts": "^2.4.2",
65
65
  "typescript": ">=5.0.4 <7",
66
- "oxlint-plugin-react-doctor": "0.2.14-dev.75c1f99"
66
+ "oxlint-plugin-react-doctor": "0.2.14-dev.8921575"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@types/babel__code-frame": "^7.27.0",
@@ -71,8 +71,8 @@
71
71
  "@xterm/headless": "^6.0.0",
72
72
  "commander": "^14.0.3",
73
73
  "ora": "^9.4.0",
74
- "@react-doctor/api": "0.2.14",
75
- "@react-doctor/core": "0.2.14"
74
+ "@react-doctor/core": "0.2.14",
75
+ "@react-doctor/api": "0.2.14"
76
76
  },
77
77
  "engines": {
78
78
  "node": "^20.19.0 || >=22.12.0"