Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createPromiseWithResolvers } from '../../shared/lib/promise-with-resolv
import {
DANGEROUSLY_runPendingImmediatesAfterCurrentTask,
expectNoPendingImmediates,
unpatchedSetImmediate,
} from './fast-set-immediate.external'
import { createAtomicTimerGroup } from '../app-render/app-render-scheduling'

Expand Down Expand Up @@ -1443,3 +1444,22 @@ describe('error recovery', () => {
})
})
})

describe('module re-evaluation', () => {
it('keeps unpatchedSetImmediate bound to the base original', () => {
const baseUnpatchedSetImmediate = unpatchedSetImmediate

for (let i = 0; i < 3; i++) {
const previousPatchedSetImmediate = globalThis.setImmediate
jest.resetModules()

const reloaded =
require('./fast-set-immediate.external') as typeof import('./fast-set-immediate.external')

expect(reloaded.unpatchedSetImmediate).toBe(baseUnpatchedSetImmediate)
expect(reloaded.unpatchedSetImmediate).not.toBe(
previousPatchedSetImmediate
)
}
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,32 @@ enum ExecutionState {
Abandoned = 4,
}

type FastSetImmediateOriginals = {
setImmediate: typeof globalThis.setImmediate
clearImmediate: typeof globalThis.clearImmediate
nextTick: typeof process.nextTick
}

const FAST_SET_IMMEDIATE_ORIGINALS_KEY = Symbol.for(
'next.fast-set-immediate.originals'
)

// Re-evaluations in dev should always use the same base/original functions.
const originals = ((globalThis as any)[FAST_SET_IMMEDIATE_ORIGINALS_KEY] ??
((globalThis as any)[FAST_SET_IMMEDIATE_ORIGINALS_KEY] = {
setImmediate: globalThis.setImmediate,
clearImmediate: globalThis.clearImmediate,
nextTick: process.nextTick,
})) as FastSetImmediateOriginals

let wasEnabledAtLeastOnce = false

let pendingNextTicks = 0
let currentExecution: Execution | null = null

const originalSetImmediate = globalThis.setImmediate
const originalClearImmediate = globalThis.clearImmediate
const originalNextTick = process.nextTick
const originalSetImmediate = originals.setImmediate
const originalClearImmediate = originals.clearImmediate
const originalNextTick = originals.nextTick
const originalSetImmediatePromisify: (typeof setImmediate)['__promisify__'] =
typeof originalSetImmediate === 'function'
? // @ts-expect-error: the types for `promisify.custom` are strange
Expand All @@ -30,8 +48,6 @@ const originalSetImmediatePromisify: (typeof setImmediate)['__promisify__'] =
// and won't ever enable the patch, so this can be a dummy value
undefined!

export { originalSetImmediate as unpatchedSetImmediate }

function install() {
if (process.env.NEXT_RUNTIME === 'edge') {
// Nothing to patch. The exported functions all error if used in the edge runtime,
Expand Down Expand Up @@ -171,6 +187,8 @@ export function expectNoPendingImmediates() {
}
}

export { originalSetImmediate as unpatchedSetImmediate }

/**
* Wait until all nextTicks and microtasks spawned from the current task are done,
* then execute any immediates that they queued.
Expand Down
Loading