diff --git a/.eslintrc.js b/.eslintrc.js index f5100fd3cc91b8..be332590ba7260 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -349,6 +349,7 @@ module.exports = { BigInt: 'readable', BigInt64Array: 'readable', BigUint64Array: 'readable', + DOMException: 'readable', Event: 'readable', EventTarget: 'readable', MessageChannel: 'readable', diff --git a/doc/api/globals.md b/doc/api/globals.md index 8ce4ff1942be27..2794009ffbd89a 100644 --- a/doc/api/globals.md +++ b/doc/api/globals.md @@ -380,6 +380,15 @@ added: v0.0.1 [`setTimeout`][] is described in the [timers][] section. +## `DOMException` +<!-- YAML +added: REPLACEME +--> + +<!-- type=global --> + +The WHATWG `DOMException` class. See [`DOMException`][] for more details. + ## `TextDecoder` <!-- YAML added: v11.0.0 @@ -430,6 +439,7 @@ The object that acts as the namespace for all W3C [Mozilla Developer Network][webassembly-mdn] for usage and compatibility. [`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController +[`DOMException`]: https://developer.mozilla.org/en-US/docs/Web/API/DOMException [`EventTarget` and `Event` API]: events.md#event-target-and-event-api [`MessageChannel`]: worker_threads.md#worker_threads_class_messagechannel [`MessageEvent`]: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/MessageEvent diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml index 38ca7683247b8c..49013e1f8ee8b5 100644 --- a/lib/.eslintrc.yaml +++ b/lib/.eslintrc.yaml @@ -37,6 +37,8 @@ rules: message: "Use `const { Atomics } = globalThis;` instead of the global." - name: Buffer message: "Use `const { Buffer } = require('buffer');` instead of the global." + - name: DOMException + message: "Use lazy function `const { lazyDOMException } = require('internal/util');` instead of the global." - name: Event message: "Use `const { Event } = require('internal/event_target');` instead of the global." - name: EventTarget diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 58f7396990dddb..ab63dd0604ea41 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -52,7 +52,7 @@ const { globalThis, } = primordials; const config = internalBinding('config'); -const { deprecate } = require('internal/util'); +const { deprecate, lazyDOMExceptionClass } = require('internal/util'); setupProcessObject(); @@ -201,6 +201,12 @@ if (!config.noBrowserGlobals) { exposeInterface(globalThis, 'URL', URL); // https://url.spec.whatwg.org/#urlsearchparams exposeInterface(globalThis, 'URLSearchParams', URLSearchParams); + exposeGetterAndSetter(globalThis, + 'DOMException', + lazyDOMExceptionClass, + (value) => { + exposeInterface(globalThis, 'DOMException', value); + }); const { TextEncoder, TextDecoder @@ -483,6 +489,15 @@ function exposeInterface(target, name, interfaceObject) { }); } +function exposeGetterAndSetter(target, name, getter, setter = undefined) { + ObjectDefineProperty(target, name, { + enumerable: false, + configurable: true, + get: getter, + set: setter, + }); +} + // https://heycam.github.io/webidl/#define-the-operations function defineOperation(target, name, method) { ObjectDefineProperty(target, name, { diff --git a/lib/internal/util.js b/lib/internal/util.js index 9158fc8e52431e..4fdf5171cb9976 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -442,11 +442,15 @@ function createDeferredPromise() { return { promise, resolve, reject }; } -let DOMException; +let _DOMException; +const lazyDOMExceptionClass = () => { + _DOMException ??= internalBinding('messaging').DOMException; + return _DOMException; +}; + const lazyDOMException = hideStackFrames((message, name) => { - if (DOMException === undefined) - DOMException = internalBinding('messaging').DOMException; - return new DOMException(message, name); + _DOMException ??= internalBinding('messaging').DOMException; + return new _DOMException(message, name); }); function structuredClone(value) { @@ -481,6 +485,7 @@ module.exports = { isInsideNodeModules, join, lazyDOMException, + lazyDOMExceptionClass, normalizeEncoding, once, promisify, diff --git a/test/parallel/test-global-domexception.js b/test/parallel/test-global-domexception.js new file mode 100644 index 00000000000000..d19b5a5e4f259d --- /dev/null +++ b/test/parallel/test-global-domexception.js @@ -0,0 +1,11 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); + +assert.strictEqual(typeof DOMException, 'function'); + +assert.throws(() => { + atob('我要抛错!'); +}, DOMException); diff --git a/test/wpt/test-atob.js b/test/wpt/test-atob.js index 51fa27e36d3f2f..8dc9c20303a7dc 100644 --- a/test/wpt/test-atob.js +++ b/test/wpt/test-atob.js @@ -12,8 +12,6 @@ runner.setFlags(['--expose-internals']); runner.setInitScript(` const { internalBinding } = require('internal/test/binding'); const { atob, btoa } = require('buffer'); - const { DOMException } = internalBinding('messaging'); - global.DOMException = DOMException; `); runner.runJsTests(); diff --git a/test/wpt/test-url.js b/test/wpt/test-url.js index 4652bfd880cd76..c1cb3e4f850c4e 100644 --- a/test/wpt/test-url.js +++ b/test/wpt/test-url.js @@ -5,14 +5,4 @@ const { WPTRunner } = require('../common/wpt'); const runner = new WPTRunner('url'); -// Needed to access to DOMException. -runner.setFlags(['--expose-internals']); - -// DOMException is needed by urlsearchparams-constructor.any.js -runner.setInitScript(` - const { internalBinding } = require('internal/test/binding'); - const { DOMException } = internalBinding('messaging'); - global.DOMException = DOMException; -`); - runner.runJsTests();