import-in-the-middle 2.0.4 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.0.5](https://github.com/nodejs/import-in-the-middle/compare/import-in-the-middle-v2.0.4...import-in-the-middle-v2.0.5) (2026-01-20)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * handle lazy initialization and circular dependencies ([#229](https://github.com/nodejs/import-in-the-middle/issues/229)) ([d1421dc](https://github.com/nodejs/import-in-the-middle/commit/d1421dc0ae65ce6da5de5cb58f41af99f9d87371))
9
+
3
10
  ## [2.0.4](https://github.com/nodejs/import-in-the-middle/compare/import-in-the-middle-v2.0.3...import-in-the-middle-v2.0.4) (2026-01-14)
4
11
 
5
12
 
package/create-hook.mjs CHANGED
@@ -24,7 +24,7 @@ const HANDLED_FORMATS = new Set(['builtin', 'module', 'commonjs'])
24
24
  const TRACE_WARNINGS = process.execArgv.includes('--trace-warnings')
25
25
 
26
26
  let getExports
27
- if (NODE_MAJOR >= 20 || (NODE_MAJOR === 18 && NODE_MINOR >= 19)) {
27
+ if (NODE_MAJOR > 16 || (NODE_MAJOR === 16 && NODE_MINOR >= 16)) {
28
28
  getExports = getExportsImpl
29
29
  } else {
30
30
  getExports = (url) => import(url).then(Object.keys)
@@ -295,13 +295,25 @@ async function processModule ({ srcUrl, context, parentGetSource, parentResolve,
295
295
 
296
296
  addSetter(n, `
297
297
  let ${variableName}
298
+ __overridden[${objectKey}] = false
299
+ let ${variableName}Defer = false
298
300
  try {
299
301
  ${variableName} = _[${objectKey}] = namespace[${objectKey}]
300
302
  } catch (err) {
301
303
  if (!(err instanceof ReferenceError)) throw err
304
+ ${variableName}Defer = true
305
+ }
306
+
307
+ if (${variableName}Defer || ${variableName} === undefined) {
308
+ __pending.push(__makeUpdater(
309
+ ${objectKey},
310
+ () => namespace[${objectKey}],
311
+ (v) => { ${variableName} = _[${objectKey}] = v }
312
+ ))
302
313
  }
303
314
  export { ${variableName} as ${reExportedName} }
304
315
  set[${objectKey}] = (v) => {
316
+ __overridden[${objectKey}] = true
305
317
  ${variableName} = v
306
318
  return true
307
319
  }
@@ -381,7 +393,7 @@ export function createHook (meta) {
381
393
  // "main" module (e.g. require.main === module). Wrapping changes how they
382
394
  // are evaluated, and can make them exit without doing anything.
383
395
  if (parentURL === '') {
384
- if (!EXTENSION_RE.test(result.url)) {
396
+ if (!EXTENSION_RE.test(result.url) && !hasIitm(result.url)) {
385
397
  entrypoint = result.url
386
398
  return { url: result.url, format: 'commonjs' }
387
399
  }
@@ -482,9 +494,65 @@ ${experimentalPatchInternals ? `import { setExperimentalPatchInternals } from '$
482
494
  const _ = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } })
483
495
  const set = {}
484
496
  const get = {}
497
+ const __overridden = Object.create(null)
498
+ let __pending = []
499
+
500
+ function __makeUpdater (key, read, assign) {
501
+ return () => {
502
+ if (__overridden[key] === true) return true
503
+ try {
504
+ const v = read()
505
+ if (v !== undefined) {
506
+ assign(v)
507
+ return true
508
+ }
509
+ return false
510
+ } catch (err) {
511
+ if (err instanceof ReferenceError) return false
512
+ throw err
513
+ }
514
+ }
515
+ }
516
+
517
+ function __flushPendingOnce () {
518
+ if (__pending.length === 0) return
519
+ const next = []
520
+ for (const fn of __pending) {
521
+ // If it still throws ReferenceError, keep it for the (single) next attempt.
522
+ if (fn() !== true) next.push(fn)
523
+ }
524
+ __pending = next
525
+ }
485
526
 
486
527
  ${Array.from(setters.values()).join('\n')}
487
528
 
529
+ if (__pending.length > 0) {
530
+ queueMicrotask(() => {
531
+ __flushPendingOnce()
532
+
533
+ if (__pending.length > 0) {
534
+ const __retryDelays = [0, 10, 50]
535
+ const __schedulePending = (i) => {
536
+ if (__pending.length === 0) return
537
+ if (i >= __retryDelays.length) {
538
+ // Give up: leave exports as-is to avoid unbounded retries.
539
+ __pending = []
540
+ return
541
+ }
542
+
543
+ const t = setTimeout(() => {
544
+ __flushPendingOnce()
545
+ __schedulePending(i + 1)
546
+ }, __retryDelays[i])
547
+ // Don't keep the process alive just for best-effort retries.
548
+ if (t && typeof t.unref === 'function') t.unref()
549
+ }
550
+
551
+ __schedulePending(0)
552
+ }
553
+ })
554
+ }
555
+
488
556
  register(${JSON.stringify(realUrl)}, _, set, get, ${JSON.stringify(originalSpecifier)})
489
557
  `
490
558
  }
@@ -518,12 +586,19 @@ register(${JSON.stringify(realUrl)}, _, set, get, ${JSON.stringify(originalSpeci
518
586
  // For Node.js 16.12.0 and higher.
519
587
  async function load (url, context, parentLoad) {
520
588
  if (hasIitm(url)) {
521
- const { source } = await getSource(url, context, parentLoad)
522
- return {
523
- source,
524
- shortCircuit: true,
525
- format: 'module'
589
+ const result = await getSource(url, context, parentLoad)
590
+ // If wrapping failed, `getSource()` may have fallen back to `parentLoad`,
591
+ // which can legally return `source: null` (e.g. for non-JS formats).
592
+ if (result && typeof result === 'object' && result.source != null) {
593
+ return {
594
+ source: result.source,
595
+ shortCircuit: true,
596
+ format: 'module'
597
+ }
526
598
  }
599
+
600
+ // Fall back to the parent loader with the original (non-iitm) URL.
601
+ return parentLoad(deleteIitm(url), context)
527
602
  }
528
603
 
529
604
  return parentLoad(url, context)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "import-in-the-middle",
3
- "version": "2.0.4",
3
+ "version": "2.0.5",
4
4
  "description": "Intercept imports in Node.js",
5
5
  "main": "index.js",
6
6
  "scripts": {