nodejs / import-in-the-middle

Like `require-in-the-middle`, but for ESM import
https://www.npmjs.com/package/import-in-the-middle
Apache License 2.0
60 stars 22 forks source link

tests not running when using `import-in-the-middle` with `mocha` #34

Open pichlermarc opened 10 months ago

pichlermarc commented 10 months ago

It seems that when using import-in-the-middle with mocha, mocha exits without running tests as require.main === module evaluates to false when it's trying to determine if it is run directly from Node.js.

Expected Behavior

mocha runs tests even if IITM is used

Actual Behavior

mocha exists with 0, not running any tests

Steps to Reproduce the Problem

I've created a reproducer to illustrate that contains a more detailed description: https://github.com/pichlermarc/esm-test

It contains two scripts, both attempt to run a single test that always fails, one with the IITM loader, and one without.

  1. git clone https://github.com/pichlermarc/esm-test
  2. npm install
  3. npm run test
    • this does not use the IITM hook, tests do run, and mocha exits with 1 (as expected)
  4. npm run test:iitm
    • this uses the IITM hook, tests do not run, and mocha exits with 0 (1 is expected)

Specifications

bengl commented 9 months ago

We wrap all modules in the loader with a wrapper module that allows for modification of exports. We could exclude CJS entrypoints, but that would create an edge case in which they would not be hook-able. In practice, that shouldn't break much, since entrypoints are rarely intentionally hooked, but that would constitute a breaking change if not behind a flag.

For now, you can work around this by wrapping the IITM hook in your own that would look something like this:

// iitm hook that preserves require.main === module

import { load, resolve as iitmResolve } from 'import-in-the-middle/hook.mjs'

async function resolve (specifier, context, parentResolve) {
  if (!context.parentURL) {
    const resolved = await parentResolve(specifier, context)
    if (resolved.format === 'commonjs') {
      return resolved
    }
  }
  return iitmResolve(specifier, context, parentResolve)
}

export { load, resolve }

In a future release, we may exclude CJS entrypoints from wrapping. It might be behind a flag, in order to not break the aforementioned edge case.