feat: allow catching all exceptions (#1593)

* feat: allow catching all exceptions

* Address review comments
diff --git a/doc/cmake-js.md b/doc/cmake-js.md
index 85bdf94..9b79a4a 100644
--- a/doc/cmake-js.md
+++ b/doc/cmake-js.md
@@ -45,6 +45,25 @@
 add_definitions(-DNAPI_EXPERIMENTAL)
 ```
 
+### Exception Handling
+
+To enable C++ exception handling (for more info see: [Setup](setup.md)), define
+the corresponding preprocessor directives depending on which exception handling
+behavior is desired.
+
+To enable C++ exception handling with `Napi::Error` objects only:
+
+```
+add_definitions(-DNAPI_EXPERIMENTAL)
+```
+
+To enable C++ exception handling for all exceptions thrown:
+
+```
+add_definitions(-DNODE_ADDON_API_CPP_EXCEPTIONS)
+add_definitions(-DNODE_ADDON_API_CPP_EXCEPTIONS_ALL)
+```
+
 ### node-addon-api
 
 If your Node-API native add-on uses the optional [**node-addon-api**](https://github.com/nodejs/node-addon-api#node-addon-api-module) C++ wrapper, the `CMakeLists.txt` file requires additional configuration information as described on the [CMake.js README file](https://github.com/cmake-js/cmake-js#node-api-and-node-addon-api).
diff --git a/doc/error_handling.md b/doc/error_handling.md
index 5bb7481..b4b4ca2 100644
--- a/doc/error_handling.md
+++ b/doc/error_handling.md
@@ -48,6 +48,18 @@
 If a C++ exception of type `Napi::Error` escapes from a Node-API C++ callback, then
 the Node-API wrapper automatically converts and throws it as a JavaScript exception.
 
+If other types of C++ exceptions are thrown, node-addon-api will either abort
+the process or wrap the exception in an `Napi::Error` in order to throw it as a
+JavaScript exception. This behavior is determined by which node-gyp dependency
+used:
+
+- When using the `node_addon_api_except` dependency, only `Napi::Error` objects
+  will be handled.
+- When using the `node_addon_api_except_all` dependency, all exceptions will be
+handled. For exceptions derived from `std::exception`, an `Napi::Error` will be
+created with the message of the exception's `what()` member function. For all
+other exceptions, an `Napi::Error` will be created with a generic error message.
+
 On return from a native method, node-addon-api will automatically convert a pending
 `Napi::Error` C++ exception to a JavaScript exception.
 
diff --git a/doc/setup.md b/doc/setup.md
index 86a1b0f..b3b7eff 100644
--- a/doc/setup.md
+++ b/doc/setup.md
@@ -38,7 +38,10 @@
        ],
      ```
 
-     To enable that capability, add an alternative dependency in `binding.gyp`:
+     To enable that capability, add an alternative dependency in `binding.gyp`
+     depending on if you want to integrate C++ exception handling for exceptions
+     derived from `Napi::Error` or all C++ exceptions. To catch only
+     `Napi::Error` exceptions, use:
 
      ```gyp
        'dependencies': [
@@ -46,6 +49,15 @@
        ],
      ```
 
+     Or, to allow catching all native C++ exceptions, use the
+     `node_addon_api_except_all` dependency:
+
+     ```gyp
+       'dependencies': [
+         "<!(node -p \"require('node-addon-api').targets\"):node_addon_api_except_all",
+       ],
+     ```
+
      If you decide to use node-addon-api without C++ exceptions enabled, please
      consider enabling node-addon-api safe API type guards to ensure the proper
      exception handling pattern:
diff --git a/napi-inl.h b/napi-inl.h
index b21eac2..e7141c0 100644
--- a/napi-inl.h
+++ b/napi-inl.h
@@ -79,19 +79,33 @@
 // For use in JS to C++ callback wrappers to catch any Napi::Error exceptions
 // and rethrow them as JavaScript exceptions before returning from the callback.
 template <typename Callable>
-inline napi_value WrapCallback(Callable callback) {
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS_ALL
+inline napi_value WrapCallback(napi_env env, Callable callback) {
+#else
+inline napi_value WrapCallback(napi_env, Callable callback) {
+#endif
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
   try {
     return callback();
   } catch (const Error& e) {
     e.ThrowAsJavaScriptException();
     return nullptr;
   }
-#else   // NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS_ALL
+  catch (const std::exception& e) {
+    Napi::Error::New(env, e.what()).ThrowAsJavaScriptException();
+    return nullptr;
+  } catch (...) {
+    Napi::Error::New(env, "A native exception was thrown")
+        .ThrowAsJavaScriptException();
+    return nullptr;
+  }
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS_ALL
+#else   // NODE_ADDON_API_CPP_EXCEPTIONS
   // When C++ exceptions are disabled, errors are immediately thrown as JS
   // exceptions, so there is no need to catch and rethrow them here.
   return callback();
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
 }
 
 // For use in JS to C++ void callback wrappers to catch any Napi::Error
@@ -99,7 +113,7 @@
 // the callback.
 template <typename Callable>
 inline void WrapVoidCallback(Callable callback) {
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
   try {
     callback();
   } catch (const Error& e) {
@@ -112,10 +126,41 @@
 #endif  // NAPI_CPP_EXCEPTIONS
 }
 
+// For use in JS to C++ void callback wrappers to catch _any_ thrown exception
+// and rethrow them as JavaScript exceptions before returning from the callback,
+// wrapping in an Napi::Error as needed.
+template <typename Callable>
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS_ALL
+inline void WrapVoidCallback(napi_env env, Callable callback) {
+#else
+inline void WrapVoidCallback(napi_env, Callable callback) {
+#endif
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
+  try {
+    callback();
+  } catch (const Error& e) {
+    e.ThrowAsJavaScriptException();
+  }
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS_ALL
+  catch (const std::exception& e) {
+    Napi::Error::New(env, e.what()).ThrowAsJavaScriptException();
+  } catch (...) {
+    Napi::Error::New(env, "A native exception was thrown")
+        .ThrowAsJavaScriptException();
+  }
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS_ALL
+#else
+  // When C++ exceptions are disabled, there is no need to catch and rethrow C++
+  // exceptions. JS errors should be thrown with
+  // `Error::ThrowAsJavaScriptException`.
+  callback();
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
+}
+
 template <typename Callable, typename Return>
 struct CallbackData {
   static inline napi_value Wrapper(napi_env env, napi_callback_info info) {
-    return details::WrapCallback([&] {
+    return details::WrapCallback(env, [&] {
       CallbackInfo callbackInfo(env, info);
       CallbackData* callbackData =
           static_cast<CallbackData*>(callbackInfo.Data());
@@ -131,7 +176,7 @@
 template <typename Callable>
 struct CallbackData<Callable, void> {
   static inline napi_value Wrapper(napi_env env, napi_callback_info info) {
-    return details::WrapCallback([&] {
+    return details::WrapCallback(env, [&] {
       CallbackInfo callbackInfo(env, info);
       CallbackData* callbackData =
           static_cast<CallbackData*>(callbackInfo.Data());
@@ -148,7 +193,7 @@
 template <void (*Callback)(const CallbackInfo& info)>
 napi_value TemplatedVoidCallback(napi_env env,
                                  napi_callback_info info) NAPI_NOEXCEPT {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo cbInfo(env, info);
     Callback(cbInfo);
     return nullptr;
@@ -158,7 +203,7 @@
 template <Napi::Value (*Callback)(const CallbackInfo& info)>
 napi_value TemplatedCallback(napi_env env,
                              napi_callback_info info) NAPI_NOEXCEPT {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo cbInfo(env, info);
     // MSVC requires to copy 'Callback' function pointer to a local variable
     // before invoking it.
@@ -171,7 +216,7 @@
           Napi::Value (T::*UnwrapCallback)(const CallbackInfo& info)>
 napi_value TemplatedInstanceCallback(napi_env env,
                                      napi_callback_info info) NAPI_NOEXCEPT {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo cbInfo(env, info);
     T* instance = T::Unwrap(cbInfo.This().As<Object>());
     return instance ? (instance->*UnwrapCallback)(cbInfo) : Napi::Value();
@@ -181,7 +226,7 @@
 template <typename T, void (T::*UnwrapCallback)(const CallbackInfo& info)>
 napi_value TemplatedInstanceVoidCallback(napi_env env, napi_callback_info info)
     NAPI_NOEXCEPT {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo cbInfo(env, info);
     T* instance = T::Unwrap(cbInfo.This().As<Object>());
     if (instance) (instance->*UnwrapCallback)(cbInfo);
@@ -264,7 +309,7 @@
   static inline void WrapperGCWithoutData(napi_env env,
                                           void* /*data*/,
                                           void* finalizeHint) NAPI_NOEXCEPT {
-    WrapVoidCallback([&] {
+    WrapVoidCallback(env, [&] {
       FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
       finalizeData->callback(env);
       delete finalizeData;
@@ -274,7 +319,7 @@
   static inline void WrapperGC(napi_env env,
                                void* data,
                                void* finalizeHint) NAPI_NOEXCEPT {
-    WrapVoidCallback([&] {
+    WrapVoidCallback(env, [&] {
       FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
       finalizeData->callback(env, static_cast<T*>(data));
       delete finalizeData;
@@ -284,7 +329,7 @@
   static inline void WrapperGCWithHint(napi_env env,
                                        void* data,
                                        void* finalizeHint) NAPI_NOEXCEPT {
-    WrapVoidCallback([&] {
+    WrapVoidCallback(env, [&] {
       FinalizeData* finalizeData = static_cast<FinalizeData*>(finalizeHint);
       finalizeData->callback(env, static_cast<T*>(data), finalizeData->hint);
       delete finalizeData;
@@ -351,7 +396,7 @@
 template <typename ContextType, typename DataType, typename CallJs, CallJs call>
 inline typename std::enable_if<call != static_cast<CallJs>(nullptr)>::type
 CallJsWrapper(napi_env env, napi_value jsCallback, void* context, void* data) {
-  details::WrapVoidCallback([&]() {
+  details::WrapVoidCallback(env, [&]() {
     call(env,
          Function(env, jsCallback),
          static_cast<ContextType*>(context),
@@ -365,7 +410,7 @@
               napi_value jsCallback,
               void* /*context*/,
               void* /*data*/) {
-  details::WrapVoidCallback([&]() {
+  details::WrapVoidCallback(env, [&]() {
     if (jsCallback != nullptr) {
       Function(env, jsCallback).Call(0, nullptr);
     }
@@ -399,7 +444,7 @@
 struct AccessorCallbackData {
   static inline napi_value GetterWrapper(napi_env env,
                                          napi_callback_info info) {
-    return details::WrapCallback([&] {
+    return details::WrapCallback(env, [&] {
       CallbackInfo callbackInfo(env, info);
       AccessorCallbackData* callbackData =
           static_cast<AccessorCallbackData*>(callbackInfo.Data());
@@ -410,7 +455,7 @@
 
   static inline napi_value SetterWrapper(napi_env env,
                                          napi_callback_info info) {
-    return details::WrapCallback([&] {
+    return details::WrapCallback(env, [&] {
       CallbackInfo callbackInfo(env, info);
       AccessorCallbackData* callbackData =
           static_cast<AccessorCallbackData*>(callbackInfo.Data());
@@ -501,7 +546,7 @@
 inline napi_value RegisterModule(napi_env env,
                                  napi_value exports,
                                  ModuleRegisterCallback registerCallback) {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     return napi_value(
         registerCallback(Napi::Env(env), Napi::Object(env, exports)));
   });
@@ -1808,7 +1853,7 @@
   }
 }
 
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
 inline Object::const_iterator::const_iterator(const Object* object,
                                               const Type type) {
   _object = object;
@@ -1883,7 +1928,7 @@
   PropertyLValue<Value> value = (*_object)[key];
   return {key, value};
 }
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
 
 #if NAPI_VERSION >= 8
 inline MaybeOrValue<bool> Object::Freeze() const {
@@ -3159,14 +3204,14 @@
 
 inline const std::string& Error::Message() const NAPI_NOEXCEPT {
   if (_message.size() == 0 && _env != nullptr) {
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
     try {
       _message = Get("message").As<String>();
     } catch (...) {
       // Catch all errors here, to include e.g. a std::bad_alloc from
       // the std::string::operator=, because this method may not throw.
     }
-#else  // NAPI_CPP_EXCEPTIONS
+#else  // NODE_ADDON_API_CPP_EXCEPTIONS
 #if defined(NODE_ADDON_API_ENABLE_MAYBE)
     Napi::Value message_val;
     if (Get("message").UnwrapTo(&message_val)) {
@@ -3175,7 +3220,7 @@
 #else
     _message = Get("message").As<String>();
 #endif
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
   }
   return _message;
 }
@@ -3222,24 +3267,24 @@
     napi_status status = napi_throw(_env, Value());
 #endif
 
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
     if (status != napi_ok) {
       throw Error::New(_env);
     }
-#else   // NAPI_CPP_EXCEPTIONS
+#else   // NODE_ADDON_API_CPP_EXCEPTIONS
     NAPI_FATAL_IF_FAILED(
         status, "Error::ThrowAsJavaScriptException", "napi_throw");
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
   }
 }
 
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
 
 inline const char* Error::what() const NAPI_NOEXCEPT {
   return Message().c_str();
 }
 
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
 
 inline const char* Error::ERROR_WRAP_VALUE() NAPI_NOEXCEPT {
   return "4bda9e7e-4913-4dbc-95de-891cbf66598e-errorVal";
@@ -4508,7 +4553,7 @@
 template <typename T>
 inline napi_value InstanceWrap<T>::InstanceVoidMethodCallbackWrapper(
     napi_env env, napi_callback_info info) {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo callbackInfo(env, info);
     InstanceVoidMethodCallbackData* callbackData =
         reinterpret_cast<InstanceVoidMethodCallbackData*>(callbackInfo.Data());
@@ -4523,7 +4568,7 @@
 template <typename T>
 inline napi_value InstanceWrap<T>::InstanceMethodCallbackWrapper(
     napi_env env, napi_callback_info info) {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo callbackInfo(env, info);
     InstanceMethodCallbackData* callbackData =
         reinterpret_cast<InstanceMethodCallbackData*>(callbackInfo.Data());
@@ -4537,7 +4582,7 @@
 template <typename T>
 inline napi_value InstanceWrap<T>::InstanceGetterCallbackWrapper(
     napi_env env, napi_callback_info info) {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo callbackInfo(env, info);
     InstanceAccessorCallbackData* callbackData =
         reinterpret_cast<InstanceAccessorCallbackData*>(callbackInfo.Data());
@@ -4551,7 +4596,7 @@
 template <typename T>
 inline napi_value InstanceWrap<T>::InstanceSetterCallbackWrapper(
     napi_env env, napi_callback_info info) {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo callbackInfo(env, info);
     InstanceAccessorCallbackData* callbackData =
         reinterpret_cast<InstanceAccessorCallbackData*>(callbackInfo.Data());
@@ -4567,7 +4612,7 @@
 template <typename InstanceWrap<T>::InstanceSetterCallback method>
 inline napi_value InstanceWrap<T>::WrappedMethod(
     napi_env env, napi_callback_info info) NAPI_NOEXCEPT {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     const CallbackInfo cbInfo(env, info);
     T* instance = T::Unwrap(cbInfo.This().As<Object>());
     if (instance) (instance->*method)(cbInfo, cbInfo[0]);
@@ -4962,13 +5007,13 @@
   bool isConstructCall = (new_target != nullptr);
   if (!isConstructCall) {
     return details::WrapCallback(
-        [&] { return T::OnCalledAsFunction(CallbackInfo(env, info)); });
+        env, [&] { return T::OnCalledAsFunction(CallbackInfo(env, info)); });
   }
 
-  napi_value wrapper = details::WrapCallback([&] {
+  napi_value wrapper = details::WrapCallback(env, [&] {
     CallbackInfo callbackInfo(env, info);
     T* instance = new T(callbackInfo);
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
     instance->_construction_failed = false;
 #else
     if (callbackInfo.Env().IsExceptionPending()) {
@@ -4979,7 +5024,7 @@
     } else {
       instance->_construction_failed = false;
     }
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
     return callbackInfo.This();
   });
 
@@ -4989,7 +5034,7 @@
 template <typename T>
 inline napi_value ObjectWrap<T>::StaticVoidMethodCallbackWrapper(
     napi_env env, napi_callback_info info) {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo callbackInfo(env, info);
     StaticVoidMethodCallbackData* callbackData =
         reinterpret_cast<StaticVoidMethodCallbackData*>(callbackInfo.Data());
@@ -5002,7 +5047,7 @@
 template <typename T>
 inline napi_value ObjectWrap<T>::StaticMethodCallbackWrapper(
     napi_env env, napi_callback_info info) {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo callbackInfo(env, info);
     StaticMethodCallbackData* callbackData =
         reinterpret_cast<StaticMethodCallbackData*>(callbackInfo.Data());
@@ -5014,7 +5059,7 @@
 template <typename T>
 inline napi_value ObjectWrap<T>::StaticGetterCallbackWrapper(
     napi_env env, napi_callback_info info) {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo callbackInfo(env, info);
     StaticAccessorCallbackData* callbackData =
         reinterpret_cast<StaticAccessorCallbackData*>(callbackInfo.Data());
@@ -5026,7 +5071,7 @@
 template <typename T>
 inline napi_value ObjectWrap<T>::StaticSetterCallbackWrapper(
     napi_env env, napi_callback_info info) {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     CallbackInfo callbackInfo(env, info);
     StaticAccessorCallbackData* callbackData =
         reinterpret_cast<StaticAccessorCallbackData*>(callbackInfo.Data());
@@ -5101,7 +5146,7 @@
 template <typename ObjectWrap<T>::StaticSetterCallback method>
 inline napi_value ObjectWrap<T>::WrappedMethod(
     napi_env env, napi_callback_info info) NAPI_NOEXCEPT {
-  return details::WrapCallback([&] {
+  return details::WrapCallback(env, [&] {
     const CallbackInfo cbInfo(env, info);
     // MSVC requires to copy 'method' function pointer to a local variable
     // before invoking it.
@@ -5402,15 +5447,15 @@
 // must not run any method that would cause JavaScript to run. In practice,
 // this means that almost any use of napi_env will be incorrect.
 inline void AsyncWorker::OnExecute(Napi::Env /*DO_NOT_USE*/) {
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
   try {
     Execute();
   } catch (const std::exception& e) {
     SetError(e.what());
   }
-#else   // NAPI_CPP_EXCEPTIONS
+#else   // NODE_ADDON_API_CPP_EXCEPTIONS
   Execute();
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
 }
 
 inline void AsyncWorker::OnAsyncWorkComplete(napi_env env,
@@ -5419,10 +5464,10 @@
   AsyncWorker* self = static_cast<AsyncWorker*>(asyncworker);
   self->OnWorkComplete(env, status);
 }
-inline void AsyncWorker::OnWorkComplete(Napi::Env /*env*/, napi_status status) {
+inline void AsyncWorker::OnWorkComplete(Napi::Env env, napi_status status) {
   if (status != napi_cancelled) {
     HandleScope scope(_env);
-    details::WrapCallback([&] {
+    details::WrapCallback(env, [&] {
       if (_error.size() == 0) {
         OnOK();
       } else {
@@ -6334,7 +6379,7 @@
     return;
   }
 
-  details::WrapVoidCallback([&]() {
+  details::WrapVoidCallback(env, [&]() {
     if (data != nullptr) {
       auto* callbackWrapper = static_cast<CallbackWrapper*>(data);
       (*callbackWrapper)(env, Function(env, jsCallback));
diff --git a/napi.h b/napi.h
index 99488f9..4838be6 100644
--- a/napi.h
+++ b/napi.h
@@ -37,22 +37,40 @@
 #define NAPI_WIDE_TEXT(x) u##x
 #endif
 
+// Backwards-compatibility to handle the rename of this macro definition, in
+// case they are used within userland code.
+#ifdef NAPI_CPP_EXCEPTIONS
+#define NODE_ADDON_API_CPP_EXCEPTIONS
+#endif
+#if defined(NODE_ADDON_API_CPP_EXCEPTIONS) && !defined(NAPI_CPP_EXCEPTIONS)
+#define NAPI_CPP_EXCEPTIONS
+#endif
+#ifdef NAPI_DISABLE_CPP_EXCEPTIONS
+#define NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS
+#endif
+#if defined(NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS) &&                          \
+    !defined(NAPI_DISABLE_CPP_EXCEPTIONS)
+#define NAPI_DISABLE_CPP_EXCEPTIONS
+#endif
+
 // If C++ exceptions are not explicitly enabled or disabled, enable them
 // if exceptions were enabled in the compiler settings.
-#if !defined(NAPI_CPP_EXCEPTIONS) && !defined(NAPI_DISABLE_CPP_EXCEPTIONS)
+#if !defined(NODE_ADDON_API_CPP_EXCEPTIONS) &&                                 \
+    !defined(NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS)
 #if defined(_CPPUNWIND) || defined(__EXCEPTIONS)
-#define NAPI_CPP_EXCEPTIONS
+#define NODE_ADDON_API_CPP_EXCEPTIONS
 #else
 #error Exception support not detected. \
-      Define either NAPI_CPP_EXCEPTIONS or NAPI_DISABLE_CPP_EXCEPTIONS.
+      Define either NODE_ADDON_API_CPP_EXCEPTIONS or NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS.
 #endif
 #endif
 
-// If C++ NAPI_CPP_EXCEPTIONS are enabled, NODE_ADDON_API_ENABLE_MAYBE should
-// not be set
-#if defined(NAPI_CPP_EXCEPTIONS) && defined(NODE_ADDON_API_ENABLE_MAYBE)
+// If C++ NODE_ADDON_API_CPP_EXCEPTIONS are enabled, NODE_ADDON_API_ENABLE_MAYBE
+// should not be set
+#if defined(NODE_ADDON_API_CPP_EXCEPTIONS) &&                                  \
+    defined(NODE_ADDON_API_ENABLE_MAYBE)
 #error NODE_ADDON_API_ENABLE_MAYBE should not be set when \
-    NAPI_CPP_EXCEPTIONS is defined.
+    NODE_ADDON_API_CPP_EXCEPTIONS is defined.
 #endif
 
 #ifdef _NOEXCEPT
@@ -61,7 +79,7 @@
 #define NAPI_NOEXCEPT noexcept
 #endif
 
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
 
 // When C++ exceptions are enabled, Errors are thrown directly. There is no need
 // to return anything after the throw statements. The variadic parameter is an
@@ -78,7 +96,7 @@
 #define NAPI_THROW_IF_FAILED_VOID(env, status)                                 \
   if ((status) != napi_ok) throw Napi::Error::New(env);
 
-#else  // NAPI_CPP_EXCEPTIONS
+#else  // NODE_ADDON_API_CPP_EXCEPTIONS
 
 // When C++ exceptions are disabled, Errors are thrown as JavaScript exceptions,
 // which are pending until the callback returns to JS.  The variadic parameter
@@ -110,7 +128,7 @@
     return;                                                                    \
   }
 
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
 
 #ifdef NODE_ADDON_API_ENABLE_MAYBE
 #define NAPI_MAYBE_THROW_IF_FAILED(env, status, type)                          \
@@ -1068,7 +1086,7 @@
                            T* data,
                            Hint* finalizeHint) const;
 
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
   class const_iterator;
 
   inline const_iterator begin() const;
@@ -1080,7 +1098,7 @@
   inline iterator begin();
 
   inline iterator end();
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
 
 #if NAPI_VERSION >= 8
   /// This operation can fail in case of Proxy.[[GetPrototypeOf]] calling into
@@ -1132,7 +1150,7 @@
   uint32_t Length() const;
 };
 
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
 class Object::const_iterator {
  private:
   enum class Type { BEGIN, END };
@@ -1179,7 +1197,7 @@
 
   friend class Object;
 };
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
 
 /// A JavaScript array buffer value.
 class ArrayBuffer : public Object {
@@ -1811,14 +1829,15 @@
 ///
 /// ### Handling Errors Without C++ Exceptions
 ///
-/// If C++ exceptions are disabled (by defining `NAPI_DISABLE_CPP_EXCEPTIONS`)
-/// then this class does not extend `std::exception`, and APIs in the `Napi`
-/// namespace do not throw C++ exceptions when they fail. Instead, they raise
-/// _pending_ JavaScript exceptions and return _empty_ `Value`s. Calling code
-/// should check `Value::IsEmpty()` before attempting to use a returned value,
-/// and may use methods on the `Env` class to check for, get, and clear a
-/// pending JavaScript exception. If the pending exception is not cleared, it
-/// will be thrown when the native callback returns to JavaScript.
+/// If C++ exceptions are disabled (by defining
+/// `NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS`) then this class does not extend
+/// `std::exception`, and APIs in the `Napi` namespace do not throw C++
+/// exceptions when they fail. Instead, they raise _pending_ JavaScript
+/// exceptions and return _empty_ `Value`s. Calling code should check
+/// `Value::IsEmpty()` before attempting to use a returned value, and may use
+/// methods on the `Env` class to check for, get, and clear a pending JavaScript
+/// exception. If the pending exception is not cleared, it will be thrown when
+/// the native callback returns to JavaScript.
 ///
 /// #### Example 1B - Throwing a JS exception
 ///
@@ -1853,10 +1872,10 @@
 /// Since the exception was cleared here, it will not be propagated as a
 /// JavaScript exception after the native callback returns.
 class Error : public ObjectReference
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
     ,
               public std::exception
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
 {
  public:
   static Error New(napi_env env);
@@ -1879,9 +1898,9 @@
 
   Object Value() const;
 
-#ifdef NAPI_CPP_EXCEPTIONS
+#ifdef NODE_ADDON_API_CPP_EXCEPTIONS
   const char* what() const NAPI_NOEXCEPT override;
-#endif  // NAPI_CPP_EXCEPTIONS
+#endif  // NODE_ADDON_API_CPP_EXCEPTIONS
 
  protected:
   /// !cond INTERNAL
diff --git a/node_addon_api.gyp b/node_addon_api.gyp
index 29905ed..8c09926 100644
--- a/node_addon_api.gyp
+++ b/node_addon_api.gyp
@@ -19,6 +19,16 @@
       }
     },
     {
+      'target_name': 'node_addon_api_except_all',
+      'type': 'none',
+      'sources': [ 'napi.h', 'napi-inl.h' ],
+      'direct_dependent_settings': {
+        'include_dirs': [ '.' ],
+        'includes': ['except.gypi'],
+        'defines': [ 'NODE_ADDON_API_CPP_EXCEPTIONS_ALL' ]
+      }
+    },
+    {
       'target_name': 'node_addon_api_maybe',
       'type': 'none',
       'sources': [ 'napi.h', 'napi-inl.h' ],
diff --git a/noexcept.gypi b/noexcept.gypi
index 404a05f..83df4dd 100644
--- a/noexcept.gypi
+++ b/noexcept.gypi
@@ -1,5 +1,5 @@
 {
-  'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ],
+  'defines': [ 'NODE_ADDON_API_DISABLE_CPP_EXCEPTIONS' ],
   'cflags': [ '-fno-exceptions' ],
   'cflags_cc': [ '-fno-exceptions' ],
   'conditions': [
diff --git a/test/binding.gyp b/test/binding.gyp
index 3dd861d..8ee391f 100644
--- a/test/binding.gyp
+++ b/test/binding.gyp
@@ -85,6 +85,9 @@
         'binding-swallowexcept.cc',
         'error.cc',
       ],
+      'build_sources_except_all': [
+        'except_all.cc',
+      ],
       'build_sources_type_check': [
         'value_type_cast.cc'
       ],
@@ -115,6 +118,11 @@
       'defines': ['NODE_ADDON_API_ENABLE_TYPE_CHECK_ON_AS']
     },
     {
+      'target_name': 'binding_except_all',
+      'dependencies': ['../node_addon_api.gyp:node_addon_api_except_all'],
+      'sources': [ '>@(build_sources_except_all)']
+    },
+    {
       'target_name': 'binding_noexcept',
       'dependencies': ['../node_addon_api.gyp:node_addon_api'],
       'sources': ['>@(build_sources)']
diff --git a/test/except_all.cc b/test/except_all.cc
new file mode 100644
index 0000000..ca91efd
--- /dev/null
+++ b/test/except_all.cc
@@ -0,0 +1,21 @@
+#include "napi.h"
+
+using namespace Napi;
+
+void ThrowStdException(const CallbackInfo& info) {
+  std::string message = info[0].As<String>().Utf8Value();
+  throw std::runtime_error(message);
+}
+
+void ThrowPrimitiveException(const CallbackInfo&) {
+  throw 0;
+}
+
+Object Init(Env env, Object exports) {
+  exports.Set("throwStdException", Napi::Function::New(env, ThrowStdException));
+  exports.Set("throwPrimitiveException",
+              Napi::Function::New(env, ThrowPrimitiveException));
+  return exports;
+}
+
+NODE_API_MODULE(addon, Init)
diff --git a/test/except_all.js b/test/except_all.js
new file mode 100644
index 0000000..d650ece
--- /dev/null
+++ b/test/except_all.js
@@ -0,0 +1,14 @@
+'use strict';
+
+const assert = require('assert');
+
+module.exports = require('./common').runTestWithBuildType(test);
+
+function test (buildType) {
+  const binding = require(`./build/${buildType}/binding_except_all.node`);
+
+  const message = 'error message';
+  assert.throws(binding.throwStdException.bind(undefined, message), { message });
+
+  assert.throws(binding.throwPrimitiveException.bind(undefined), { message: 'A native exception was thrown' });
+}