File
Blob: src/workerd/api/node/tests/module-require-mutable-exports-test.js
| 1 | // Copyright (c) 2017-2026 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | import { createRequire } from 'node:module'; |
| 5 | import { strictEqual, notStrictEqual, ok, doesNotThrow } from 'node:assert'; |
| 6 | |
| 7 | const require = createRequire('/'); |
| 8 | |
| 9 | export const testTimersPromisesMutable = { |
| 10 | async test() { |
| 11 | const timersPromises = require('node:timers/promises'); |
| 12 | const originalSetImmediate = timersPromises.setImmediate; |
| 13 | ok(typeof originalSetImmediate === 'function'); |
| 14 | |
| 15 | const patchedSetImmediate = async function patchedSetImmediate() { |
| 16 | return 'patched'; |
| 17 | }; |
| 18 | |
| 19 | doesNotThrow(() => { |
| 20 | timersPromises.setImmediate = patchedSetImmediate; |
| 21 | }); |
| 22 | |
| 23 | strictEqual(timersPromises.setImmediate, patchedSetImmediate); |
| 24 | timersPromises.setImmediate = originalSetImmediate; |
| 25 | strictEqual(timersPromises.setImmediate, originalSetImmediate); |
| 26 | |
| 27 | // require() should return the same object as import { default } |
| 28 | const { default: timersPromises2 } = await import('node:timers/promises'); |
| 29 | strictEqual(timersPromises, timersPromises2); |
| 30 | |
| 31 | // But import() returns the namespace object, not the default export |
| 32 | const timersPromisesNamespace = await import('node:timers/promises'); |
| 33 | notStrictEqual(timersPromises, timersPromisesNamespace); |
| 34 | strictEqual(timersPromisesNamespace.default, timersPromises); |
| 35 | }, |
| 36 | }; |
| 37 | |
| 38 | // Test that patching the require()'d object doesn't affect the original named exports |
| 39 | export const testPatchingDoesNotAffectNamedExports = { |
| 40 | async test() { |
| 41 | const timersPromises = require('node:timers/promises'); |
| 42 | const originalSetTimeout = timersPromises.setTimeout; |
| 43 | |
| 44 | // Patch the require()'d object |
| 45 | const patchedSetTimeout = async function patched() { |
| 46 | return 'patched'; |
| 47 | }; |
| 48 | timersPromises.setTimeout = patchedSetTimeout; |
| 49 | |
| 50 | // The named export from the module namespace should be unaffected |
| 51 | const { setTimeout: namedSetTimeout } = |
| 52 | await import('node:timers/promises'); |
| 53 | notStrictEqual(namedSetTimeout, patchedSetTimeout); |
| 54 | strictEqual(namedSetTimeout, originalSetTimeout); |
| 55 | |
| 56 | // Restore |
| 57 | timersPromises.setTimeout = originalSetTimeout; |
| 58 | }, |
| 59 | }; |
| 60 | |
| 61 | export const testTimersMutable = { |
| 62 | test() { |
| 63 | const timers = require('node:timers'); |
| 64 | const originalSetTimeout = timers.setTimeout; |
| 65 | ok(typeof originalSetTimeout === 'function'); |
| 66 | |
| 67 | const patchedSetTimeout = function patchedSetTimeout() { |
| 68 | return 'patched'; |
| 69 | }; |
| 70 | |
| 71 | doesNotThrow(() => { |
| 72 | timers.setTimeout = patchedSetTimeout; |
| 73 | }); |
| 74 | |
| 75 | strictEqual(timers.setTimeout, patchedSetTimeout); |
| 76 | timers.setTimeout = originalSetTimeout; |
| 77 | }, |
| 78 | }; |
| 79 | |
| 80 | export const testBufferMutable = { |
| 81 | test() { |
| 82 | const buffer = require('node:buffer'); |
| 83 | const originalBuffer = buffer.Buffer; |
| 84 | ok(typeof originalBuffer === 'function'); |
| 85 | |
| 86 | const patchedBuffer = function PatchedBuffer() { |
| 87 | return 'patched'; |
| 88 | }; |
| 89 | |
| 90 | doesNotThrow(() => { |
| 91 | buffer.Buffer = patchedBuffer; |
| 92 | }); |
| 93 | |
| 94 | strictEqual(buffer.Buffer, patchedBuffer); |
| 95 | buffer.Buffer = originalBuffer; |
| 96 | }, |
| 97 | }; |
| 98 | |
| 99 | export const testUtilMutable = { |
| 100 | test() { |
| 101 | const util = require('node:util'); |
| 102 | const originalPromisify = util.promisify; |
| 103 | ok(typeof originalPromisify === 'function'); |
| 104 | |
| 105 | const patchedPromisify = function patchedPromisify() { |
| 106 | return 'patched'; |
| 107 | }; |
| 108 | |
| 109 | doesNotThrow(() => { |
| 110 | util.promisify = patchedPromisify; |
| 111 | }); |
| 112 | |
| 113 | strictEqual(util.promisify, patchedPromisify); |
| 114 | util.promisify = originalPromisify; |
| 115 | }, |
| 116 | }; |
| 117 | |
| 118 | export const testRequireCachesMutableObject = { |
| 119 | test() { |
| 120 | const timersPromises1 = require('node:timers/promises'); |
| 121 | const timersPromises2 = require('node:timers/promises'); |
| 122 | |
| 123 | strictEqual(timersPromises1, timersPromises2); |
| 124 | |
| 125 | const patchedSetImmediate = async function patched() { |
| 126 | return 'patched'; |
| 127 | }; |
| 128 | const original = timersPromises1.setImmediate; |
| 129 | |
| 130 | timersPromises1.setImmediate = patchedSetImmediate; |
| 131 | strictEqual(timersPromises2.setImmediate, patchedSetImmediate); |
| 132 | timersPromises1.setImmediate = original; |
| 133 | }, |
| 134 | }; |
| 135 | |
| 136 | // When require_returns_default_export is enabled, require() should return the |
| 137 | // default export directly (which is the object with all the functions), |
| 138 | // not the namespace wrapper with both `default` and named exports. |
| 139 | export const testRequireReturnsDefaultExport = { |
| 140 | test() { |
| 141 | const timers = require('node:timers'); |
| 142 | // With require_returns_default_export enabled, timers should be the |
| 143 | // default export object directly, not the namespace wrapper. |
| 144 | // The default export IS the object with setTimeout, setInterval, etc. |
| 145 | ok(typeof timers.setTimeout === 'function'); |
| 146 | ok(typeof timers.setInterval === 'function'); |
| 147 | ok(typeof timers.clearTimeout === 'function'); |
| 148 | ok(typeof timers.clearInterval === 'function'); |
| 149 | // The namespace wrapper would have a 'default' property, but when |
| 150 | // we return the default export directly, there's no 'default' property |
| 151 | // on the returned object (unless the default export itself has one). |
| 152 | strictEqual(timers.default, undefined); |
| 153 | }, |
| 154 | }; |