diff --git a/.eslintrc.js b/.eslintrc.js index a6d2c177a278..3f3ff7adda01 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -35,6 +35,9 @@ module.exports = { // Flow handles these checks for us, so they aren't required 'no-undef': 'off', 'no-unreachable': 'off', + // Throwing from function or rejecting promises with non-error values could result in unclear error stack traces and lead to harder debugging + 'prefer-promise-reject-errors': 'error', + 'no-throw-literal': 'error', }, }, { diff --git a/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js b/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js index b6cf4e854e99..02d3a07fcee8 100644 --- a/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js +++ b/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js @@ -99,7 +99,7 @@ const AccessibilityInfo = { reject, ); } else { - reject(null); + reject(new Error('NativeAccessibilityManagerIOS is not available')); } }); } @@ -119,7 +119,11 @@ const AccessibilityInfo = { if (NativeAccessibilityInfoAndroid?.isGrayscaleEnabled != null) { NativeAccessibilityInfoAndroid.isGrayscaleEnabled(resolve); } else { - reject(null); + reject( + new Error( + 'NativeAccessibilityInfoAndroid.isGrayscaleEnabled is not available', + ), + ); } }); } else { @@ -130,7 +134,7 @@ const AccessibilityInfo = { reject, ); } else { - reject(null); + reject(new Error('AccessibilityInfo native module is not available')); } }); } @@ -150,7 +154,11 @@ const AccessibilityInfo = { if (NativeAccessibilityInfoAndroid?.isInvertColorsEnabled != null) { NativeAccessibilityInfoAndroid.isInvertColorsEnabled(resolve); } else { - reject(null); + reject( + new Error( + 'NativeAccessibilityInfoAndroid.isInvertColorsEnabled is not available', + ), + ); } }); } else { @@ -161,7 +169,7 @@ const AccessibilityInfo = { reject, ); } else { - reject(null); + reject(new Error('AccessibilityInfo native module is not available')); } }); } @@ -181,7 +189,7 @@ const AccessibilityInfo = { if (NativeAccessibilityInfoAndroid != null) { NativeAccessibilityInfoAndroid.isReduceMotionEnabled(resolve); } else { - reject(null); + reject(new Error('AccessibilityInfo native module is not available')); } } else { if (NativeAccessibilityManagerIOS != null) { @@ -190,7 +198,7 @@ const AccessibilityInfo = { reject, ); } else { - reject(null); + reject(new Error('NativeAccessibilityManagerIOS is not available')); } } }); @@ -208,7 +216,11 @@ const AccessibilityInfo = { if (NativeAccessibilityInfoAndroid?.isHighTextContrastEnabled != null) { NativeAccessibilityInfoAndroid.isHighTextContrastEnabled(resolve); } else { - reject(null); + reject( + new Error( + 'NativeAccessibilityInfoAndroid.isHighTextContrastEnabled is not available', + ), + ); } } else { return Promise.resolve(false); @@ -236,7 +248,11 @@ const AccessibilityInfo = { reject, ); } else { - reject(null); + reject( + new Error( + 'NativeAccessibilityManagerIOS.getCurrentDarkerSystemColorsState is not available', + ), + ); } } }); @@ -264,7 +280,11 @@ const AccessibilityInfo = { reject, ); } else { - reject(null); + reject( + new Error( + 'NativeAccessibilityManagerIOS.getCurrentPrefersCrossFadeTransitionsState is not available', + ), + ); } } }); @@ -289,7 +309,7 @@ const AccessibilityInfo = { reject, ); } else { - reject(null); + reject(new Error('NativeAccessibilityManagerIOS is not available')); } }); } @@ -309,7 +329,7 @@ const AccessibilityInfo = { if (NativeAccessibilityInfoAndroid != null) { NativeAccessibilityInfoAndroid.isTouchExplorationEnabled(resolve); } else { - reject(null); + reject(new Error('NativeAccessibilityInfoAndroid is not available')); } } else { if (NativeAccessibilityManagerIOS != null) { @@ -318,7 +338,7 @@ const AccessibilityInfo = { reject, ); } else { - reject(null); + reject(new Error('NativeAccessibilityManagerIOS is not available')); } } }); @@ -343,10 +363,18 @@ const AccessibilityInfo = { ) { NativeAccessibilityInfoAndroid.isAccessibilityServiceEnabled(resolve); } else { - reject(null); + reject( + new Error( + 'NativeAccessibilityInfoAndroid.isAccessibilityServiceEnabled is not available', + ), + ); } } else { - reject(null); + reject( + new Error( + 'isAccessibilityServiceEnabled is only available on Android', + ), + ); } }); }, diff --git a/packages/react-native/Libraries/Debugging/DebuggingOverlayRegistry.js b/packages/react-native/Libraries/Debugging/DebuggingOverlayRegistry.js index b8cba9961420..e81cb950463b 100644 --- a/packages/react-native/Libraries/Debugging/DebuggingOverlayRegistry.js +++ b/packages/react-native/Libraries/Debugging/DebuggingOverlayRegistry.js @@ -332,7 +332,9 @@ class DebuggingOverlayRegistry { instance.measure((x, y, width, height, left, top) => { // measure can execute callback without any values provided to signal error. if (left == null || top == null || width == null || height == null) { - reject('Unexpectedly failed to call measure on an instance.'); + reject( + new Error('Unexpectedly failed to call measure on an instance.'), + ); } resolve({ @@ -480,7 +482,11 @@ class DebuggingOverlayRegistry { width == null || height == null ) { - reject('Unexpectedly failed to call measure on an instance.'); + reject( + new Error( + 'Unexpectedly failed to call measure on an instance.', + ), + ); } resolve({x: left, y: top, width, height}); diff --git a/packages/react-native/Libraries/NativeComponent/NativeComponentRegistryUnstable.js b/packages/react-native/Libraries/NativeComponent/NativeComponentRegistryUnstable.js index cb6ac57f52ec..183076b80dde 100644 --- a/packages/react-native/Libraries/NativeComponent/NativeComponentRegistryUnstable.js +++ b/packages/react-native/Libraries/NativeComponent/NativeComponentRegistryUnstable.js @@ -23,7 +23,9 @@ export function unstable_hasComponent(name: string): boolean { hasNativeComponent = global.__nativeComponentRegistry__hasComponent(name); componentNameToExists.set(name, hasNativeComponent); } else { - throw `unstable_hasComponent('${name}'): Global function is not registered`; + throw new Error( + `unstable_hasComponent('${name}'): Global function is not registered`, + ); } } return hasNativeComponent; diff --git a/packages/react-native/scripts/codegen/codegen-utils.js b/packages/react-native/scripts/codegen/codegen-utils.js index bcd6729f150b..2fc48a1d0223 100644 --- a/packages/react-native/scripts/codegen/codegen-utils.js +++ b/packages/react-native/scripts/codegen/codegen-utils.js @@ -30,7 +30,7 @@ function getCodegen() /*: $FlowFixMe */ { RNCodegen = require('@react-native/codegen/lib/generators/RNCodegen.js'); } if (!RNCodegen) { - throw 'RNCodegen not found.'; + throw new Error('RNCodegen not found.'); } return RNCodegen; } @@ -45,7 +45,7 @@ function getCombineJSToSchema() /*: $FlowFixMe */ { combineJSToSchema = require('@react-native/codegen/lib/cli/combine/combine-js-to-schema.js'); } if (!combineJSToSchema) { - throw 'combine-js-to-schema not found.'; + throw new Error('combine-js-to-schema not found.'); } return combineJSToSchema; } diff --git a/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js b/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js index 97a4b89c540e..680f8a6d295d 100644 --- a/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js +++ b/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js @@ -38,7 +38,7 @@ const codegenLog = (text /*: string */, info /*: boolean */ = false) => { function readPkgJsonInDirectory(dir /*: string */) /*: $FlowFixMe */ { const pkgJsonPath = path.join(dir, 'package.json'); if (!fs.existsSync(pkgJsonPath)) { - throw `[Codegen] Error: ${pkgJsonPath} does not exist.`; + throw new Error(`[Codegen] Error: ${pkgJsonPath} does not exist.`); } return JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); } @@ -174,7 +174,7 @@ function findProjectRootLibraries( } if (typeof pkgJson.codegenConfig !== 'object') { - throw 'The "codegenConfig" field must be an Object.'; + throw new Error('The "codegenConfig" field must be an Object.'); } return extractLibrariesFromJSON(pkgJson, projectRoot); diff --git a/packages/rn-tester/NativeModuleExample/NativeScreenshotManager.js b/packages/rn-tester/NativeModuleExample/NativeScreenshotManager.js index a04cca85a831..0f6b0682777a 100644 --- a/packages/rn-tester/NativeModuleExample/NativeScreenshotManager.js +++ b/packages/rn-tester/NativeModuleExample/NativeScreenshotManager.js @@ -30,5 +30,5 @@ export function takeScreenshot( if (NativeModule != null) { return NativeModule.takeScreenshot(id, options); } - return Promise.reject(); + return Promise.reject(new Error('ScreenshotManager is not defined.')); } diff --git a/packages/rn-tester/js/examples/ContentURLAndroid/ContentURLAndroid.js b/packages/rn-tester/js/examples/ContentURLAndroid/ContentURLAndroid.js index 81f88a78bf16..69bce4cd0220 100644 --- a/packages/rn-tester/js/examples/ContentURLAndroid/ContentURLAndroid.js +++ b/packages/rn-tester/js/examples/ContentURLAndroid/ContentURLAndroid.js @@ -34,7 +34,7 @@ function blobToBase64(blob: Blob) { if (typeof result === 'string') { resolve(result); } else { - reject('error: incompatible types'); + reject(new Error('error: incompatible types')); } }; reader.readAsDataURL(blob);