Skip to content

Conversation

legendecas
Copy link
Contributor

@legendecas legendecas commented May 13, 2025

PR Checklist

Overview

This allows compilers based on require.extensions continue to work, like ts-node with Node.js built-in TypeScript support enabled.

Hopefully unblock nodejs/node#57298.

Fixes: #5314
Fixes: #5317

Copy link

linux-foundation-easycla bot commented May 13, 2025

CLA Signed

The committers listed above are authorized under a signed CLA.

  • ✅ login: legendecas / name: Chengzhong Wu (5cde7f5)

@legendecas legendecas marked this pull request as ready for review May 26, 2025 00:56
@marco-ippolito
Copy link

friendly ping @JoshuaKGoldberg

@JoshuaKGoldberg
Copy link
Member

JoshuaKGoldberg commented Jun 9, 2025

Hey thanks for the ping! I was out for a bit and am catching up on things now. I'm going to try to get to testing this thoroughly this week (hopefully tomorrow or so). But the more folks who can test this out, the better.

Note that we're pretty close to releasing Mocha 12 (#5357). I also have a vague memory of someone mentioning somewhere that once Mocha drops support for pre-require(esm) versions of Node.js, this whole area of code could be simplified. I don't have the time to spelunk for it right now, but will if nobody posts that soon.

IMO it might make sense to either...

  • ⚡ Faster: Merge this in as a feature in Mocha 11.x, then take on the drastic simplification in Mocha 12
  • 🦺 More careful: merge this in as a feature in Mocha 12.0, then file the simplification as a followup (either Mocha 12 or Mocha 13, depending on how breaking it is)

I'm personally leaning to ⚡ since this change is pretty targeted, and because of how broken things are at the moment for Node.js versions with type stripping. cc @mochajs/committers - WDYT?

@JoshuaKGoldberg
Copy link
Member

Oh and @legendecas in the meantime, could you please try to fix the lint error? It's failing in CI.

This allows compilers based on `require.extensions` continue to work.
@legendecas
Copy link
Contributor Author

Fixed lint issue.

"Merge this in as a feature in Mocha 11.x, then take on the drastic simplification in Mocha 12" sounds good to me!

@mark-wiemer mark-wiemer self-requested a review June 10, 2025 02:15
Copy link
Member

@JoshuaKGoldberg JoshuaKGoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯 Fantastic! Thanks for sending this in, it's really clean and I haven't been able to break it locally when testing on a bunch of real-world repos.

We should definitely get another review from @mochajs/committers if possible just to be safe. But no blockers from me on merging to main in v11.

@voxpelli voxpelli requested a review from Copilot June 12, 2025 07:44
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for loading ESM syntax via Node’s require(esm) feature while preserving require.extensions hooks, and provides integration tests for both .js and .ts compilers.

  • Introduce tryImportAndRequire and requireModule in esm-utils.js to choose between import() and require() based on process.features.require_module.
  • Add integration tests and fixtures under test/compiler-cjs and test/compiler-fixtures to validate require.extensions-based compilers.
  • Update ESLint config to include the new test file.

Reviewed Changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
lib/nodejs/esm-utils.js Replace single requireOrImport with two strategies and conditional override
test/integration/compiler-cjs.spec.js Add tests for .js and .ts extensions using fixtures
test/compiler-fixtures/js.fixture.js Fixture to override .js loader and compile files
test/compiler-fixtures/ts.fixture.js Fixture to override .ts loader and compile files
test/compiler-cjs/test.js ESM source file for .js test
test/compiler-cjs/test.js.compiled Compiled CJS output for .js test
test/compiler-cjs/test.ts ESM source file for .ts test
test/compiler-cjs/test.ts.compiled Compiled CJS output for .ts test
eslint.config.js Include the newly added test/compiler-cjs/test.js in linting
Comments suppressed due to low confidence (4)

lib/nodejs/esm-utils.js:86

  • It looks like the closing }; for the original exports.requireOrImport arrow function is left behind after replacing it with tryImportAndRequire. Consider removing this stray closing brace and semicolon to avoid a syntax error.
  };

test/compiler-cjs/test.ts:3

  • [nitpick] The test title uses cts which can be confusing—consider renaming to ts written in esm for clarity and consistency with the file extension.
describe('cts written in esm', () => {

test/compiler-cjs/test.ts.compiled:3

  • [nitpick] The compiled test also uses cts in its description; update this to ts written in esm to match the source and avoid confusion.
describe('cts written in esm', () => {

test/integration/compiler-cjs.spec.js:58

  • [nitpick] This expectation references cts written in esm; after renaming the test titles to ts written in esm, update this expectation string accordingly.
          'cts written in esm should work',

@JoshuaKGoldberg
Copy link
Member

Just to be safe, I ran this PR's changes through another set of tests today on:

...across Node.js versions:

  • 18.18.0
  • 20.9.0
  • 21.1.0
  • 22.16.0
  • 23.11.1
  • 24.2.0

Confirmed that this fixes both of those issues and generally works. Great! I'll merge this now and publish a new release. Thanks a bunch @legendecas for the clean fix and patience with us on the PR review, this is great work! Additional shoutout to @marco-ippolito from the Node.js side and for helping push it forward. 🔥

@JoshuaKGoldberg JoshuaKGoldberg merged commit 41e24a2 into mochajs:main Jun 18, 2025
75 of 76 checks passed
@JoshuaKGoldberg
Copy link
Member

Released as 11.7.0:

🎉

@legendecas legendecas deleted the require-esm branch June 18, 2025 09:51
@AviVahl
Copy link
Contributor

AviVahl commented Jun 18, 2025

This PR has the unfortunate side-effect of removing support for setups that use asynchronous Node.js loaders/hooks.

Before this PR:
try import(). if that fails- try require().

After this PR:
try require(). if that fails and error is ERR_REQUIRE_ASYNC_MODULE- try import().
if require() fails for any other reason (e.g. needing the hooks being registered), it won't even try to import(), not making use of any asynchronous hooks.

@queengooborg
Copy link

queengooborg commented Jun 18, 2025

Looks like this change had broken testing on MDN's browser-compat-data: https://github.com/mdn/browser-compat-data/actions/runs/15731639024/job/44333918076?pr=27094

Exception during run: Error: Transform failed with 1 error:
/home/runner/work/browser-compat-data/browser-compat-data/index.ts:56:15: ERROR: Top-level await is currently not supported with the "cjs" output format

Also affecting the mdn-bcd-collector project in the same way: https://github.com/openwebdocs/mdn-bcd-collector/actions/runs/15732266958/job/44335981656?pr=2479

Not sure if it's an issue with tsx in particular, but something seems to be blocking us from upgrading to Mocha v11.7. Will try to dig in to see if it's something with our setup tomorrow!

@JoshuaKGoldberg
Copy link
Member

Lovely. Thanks @AviVahl and @queengooborg, that's good to know. Any investigation you can do would be greatly appreciated. Fixing this area is a high priority for Mocha right now.

@mshima
Copy link
Contributor

mshima commented Jun 20, 2025

@JoshuaKGoldberg it breaks mocha with custom ts loader. jhipster/generator-jhipster#29827
It's probably because the required module doesn't exist so it does not fallback to import due to condition.
Typescript loaders takes a non existing .js module and tries to load .ts alternative and I think there is no loader support for require.
I will do some test.

@MuTsunTsai
Copy link

It also breaks @hyperse/ts-node/register. It was working in Mocha 11.6.0.

@JoshuaKGoldberg
Copy link
Member

JoshuaKGoldberg commented Jun 23, 2025

We believe we have a fix:

  1. test: add esm loader test #5383: adds a test that exercises at least one of the kinds of cases now failing in 11.7.0
  2. fix: always fallback to import() if require() fails #5384 fixes at least all of the cases that have been reported:

If anybody is seeing a new error as of Mocha 11.7.0 that's not in any of those issues (and doesn't reproduce on Mocha 11.6.0) please do file a new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
8 participants