diff --git a/doc/api/module.md b/doc/api/module.md index dc03b50961e680..956b10942ba36e 100644 --- a/doc/api/module.md +++ b/doc/api/module.md @@ -245,6 +245,7 @@ changes: * `deregister()` {Function} Remove the registered hooks so that they are no longer called. Hooks are otherwise retained for the lifetime of the running process. + * `[Symbol.dispose]` {Function} The same as `deregister`. Register [hooks][] that customize Node.js module resolution and loading behavior. See [Customization hooks][]. The returned object can be used to diff --git a/lib/internal/modules/customization_hooks.js b/lib/internal/modules/customization_hooks.js index c2579269ec6396..d709d3b285097d 100644 --- a/lib/internal/modules/customization_hooks.js +++ b/lib/internal/modules/customization_hooks.js @@ -9,6 +9,7 @@ const { StringPrototypeSlice, StringPrototypeStartsWith, Symbol, + SymbolDispose, } = primordials; const { isAnyArrayBuffer, @@ -83,10 +84,7 @@ class ModuleHooks { ObjectFreeze(this); } - // TODO(joyeecheung): we may want methods that allow disabling/enabling temporarily - // which just sets the item in the array to undefined temporarily. - // TODO(joyeecheung): this can be the [Symbol.dispose] implementation to pair with - // `using` when the explicit resource management proposal is shipped by V8. + /** * Deregister the hook instance. */ @@ -101,6 +99,13 @@ class ModuleHooks { ArrayPrototypeSplice(loadHooks, index, 1); } } + + /** + * Deregister the hook instance. + */ + [SymbolDispose]() { + this.deregister(); + } }; /** diff --git a/test/module-hooks/test-module-hooks-dispose.js b/test/module-hooks/test-module-hooks-dispose.js new file mode 100644 index 00000000000000..2a796d70a50fc2 --- /dev/null +++ b/test/module-hooks/test-module-hooks-dispose.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const assert = require('assert'); +const { registerHooks } = require('module'); + +// Test that using syntax works. +{ + // eslint-disable-next-line no-unused-vars + using hook = registerHooks({ + load: common.mustCall((url, context, nextLoad) => { + const result = nextLoad(url, context); + assert.strictEqual(result.source, ''); + return { + source: 'export const hello = "world"', + }; + }), + }); + + const mod = require('../fixtures/empty.js'); + assert.strictEqual(mod.hello, 'world'); +} + +delete require.cache[require.resolve('../fixtures/empty.js')]; +const mod = require('../fixtures/empty.js'); +assert.deepStrictEqual(mod, {});