Repository: denoland/deno
Pull request: fix(ext/web): support object in `DOMException` second argument for Node.js compat

<!--
Before submitting a PR, please read https://docs.deno.com/runtime/manual/references/contributing

1. Give the PR a descriptive title.

  Examples of good title:
    - fix(std/http): Fix race condition in server
    - docs(console): Update docstrings
    - feat(doc): Handle nested reexports

  Examples of bad title:
    - fix #7123
    - update docs
    - fix bugs

2. Ensure there is a related issue and it is referenced in the PR text.
3. Ensure there are tests that cover the changes.
4. Ensure `cargo test` passes.
5. Ensure `./tools/format.js` passes without changing files.
6. Ensure `./tools/lint.js` passes.
7. Open as a draft PR if your work is still in progress. The CI won't run
   all steps, but you can add '[ci]' to a commit message to force it to.
8. If you would like to run the benchmarks on the CI, add the 'ci-bench' label.
-->

fix #31915

Changed files:
filename: ext/web/01_dom_exception.js
patch: @@ -18,6 +18,7 @@ const {
   ObjectPrototypeIsPrototypeOf,
   ObjectSetPrototypeOf,
   ReflectConstruct,
+  ReflectHas,
   Symbol,
   SymbolFor,
 } = primordials;
@@ -95,21 +96,43 @@ class DOMException {
   [_code];
 
   // https://webidl.spec.whatwg.org/#dom-domexception-domexception
-  constructor(message = "", name = "Error") {
+  // Since Node.js allows the second argument to accept an options object, we need to support that
+  // https://github.com/nodejs/node/blob/9e201e61fd8e4b8bfb74409151cbcbbc7377ca67/lib/internal/per_context/domexception.js#L82-L96
+  constructor(message = "", options = "Error") {
     message = webidl.converters.DOMString(
       message,
       "Failed to construct 'DOMException'",
       "Argument 1",
     );
-    name = webidl.converters.DOMString(
-      name,
-      "Failed to construct 'DOMException'",
-      "Argument 2",
-    );
-    const code = nameToCodeMapping[name] ?? 0;
 
     // execute Error constructor to have stack property and [[ErrorData]] internal slot
     const error = ReflectConstruct(Error, [], new.target);
+
+    let name;
+    if (options !== null && typeof options === "object") {
+      name = webidl.converters.DOMString(
+        options.name,
+        "Failed to construct 'DOMException'",
+        "Argument 2",
+      );
+      if (ReflectHas(options, "cause")) {
+        ObjectDefineProperty(error, "cause", {
+          __proto__: null,
+          value: options.cause,
+          configurable: true,
+          writable: true,
+          enumerable: false,
+        });
+      }
+    } else {
+      name = webidl.converters.DOMString(
+        options,
+        "Failed to construct 'DOMException'",
+        "Argument 2",
+      );
+    }
+    const code = nameToCodeMapping[name] ?? 0;
+
     error[_message] = message;
     error[_name] = name;
     error[_code] = code;

filename: tests/node_compat/README.md
patch: @@ -5,8 +5,8 @@ Deno.
 
 - ./runner/suite/ - vendored Node.js test cases (git submodule at
   https://github.com/denoland/node_test)
-- ./config.toml - has the list of passing Node.js test cases
-- ./test.ts - The script entrypoint of node compat test.
+- ./config.jsonc - has the list of passing Node.js test cases
+- ./mod.rs - The script entrypoint of node compat test.
 
 If you run single node.js test case, use the command:
 
@@ -16,7 +16,7 @@ cargo test <name of test file>
 
 ## Configuration file
 
-The `config.toml` specifies which tests should pass in Deno and includes
+The `config.jsonc` specifies which tests should pass in Deno and includes
 platform-specific and behavioral settings for each test.
 
 ### Options
@@ -40,24 +40,31 @@ Each test entry can include the following optional configuration properties:
 
 ### Examples
 
-```toml
-# Should pass on all platforms
-"parallel/test-foo.js" = {}
-
-# Test marked as flaky
-"parallel/test-bar.js" = { flaky = true }
-
-# Test skipped on all platforms with explanation
-"parallel/test-baz.js" = { darwin = false, linux = false, windows = false, reason = "some reason" }
-
-# Test skipped only on Windows
-"parallel/test-qux.js" = { windows = false }
+```jsonc
+{
+  // Should pass on all platforms
+  "parallel/test-foo.js": {},
+
+  // Test marked as flaky
+  "parallel/test-bar.js": { "flaky": true },
+
+  // Test skipped on all platforms with explanation
+  "parallel/test-baz.js": {
+    "darwin": false,
+    "linux": false,
+    "windows": false,
+    "reason": "some reason"
+  },
+
+  // Test skipped only on Windows
+  "parallel/test-qux.js": { "windows": false }
+}
 ```
 
 ## Add test case entry to CI check
 
 If you fixed some Node.js compabitility and some test cases started passing,
-then add those cases to `config.toml`. The items listed in there are checked in
+then add those cases to `config.jsonc`. The items listed in there are checked in
 CI check.
 
 ## Daily test viewer

filename: tests/node_compat/config.jsonc
patch: @@ -418,6 +418,7 @@
     "parallel/test-domain-nested.js": {},
     "parallel/test-domain-stack.js": {},
     "parallel/test-domain-top-level-error-handler-clears-stack.js": {},
+    "parallel/test-domexception-cause.js": {},
     "parallel/test-dsa-fips-invalid-key.js": {},
     "parallel/test-env-newprotomethod-remove-unnecessary-prototypes.js": {},
     "parallel/test-error-format-list.js": {},