Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚀 Feature: Support ESM (ECMAScript Modules) in watch mode #4374

Open
4 tasks done
Swivelgames opened this issue Jul 14, 2020 · 17 comments
Open
4 tasks done

🚀 Feature: Support ESM (ECMAScript Modules) in watch mode #4374

Swivelgames opened this issue Jul 14, 2020 · 17 comments
Labels
status: needs upstream fix defect within Mocha's dependency tree type: feature enhancement proposal

Comments

@Swivelgames
Copy link

Swivelgames commented Jul 14, 2020

Prerequisites

  • Checked that your issue hasn't already been filed by cross-referencing issues with the faq label
  • Checked next-gen ES issues and syntax problems by using the same environment and/or transpiler configuration without Mocha to ensure it isn't just a feature that actually isn't supported in the environment in question or a bug in your code.
  • 'Smoke tested' the code to be tested by running it outside the real test suite to get a better sense of whether the problem is in the code under test, your usage of Mocha, or Mocha itself
  • Ensured that there is no discrepancy between the locally and globally installed versions of Mocha. You can find them with: node node_modules/.bin/mocha --version(Local) and mocha --version(Global). We recommend that you not install Mocha globally.

Description

When running --watch within a package that has "type": "module" set in its package.json, Mocha fails to properly import, instead attempting to require() the file.

$ npm run test-mocha:watch

> @ test-mocha:watch /home/jdalrymple/src/bugs/mochajs
> mocha --require @babel/register $(find ./src -type f -name '*.test.js') --watch

internal/modules/cjs/loader.js:1217
      throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
      ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /.../src/index.test.js
require() of ES modules is not supported.
require() of /.../src/index.test.js from /.../node_modules/mocha/lib/mocha.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.test.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /.../package.json.

    at Module._extensions..js (internal/modules/cjs/loader.js:1217:13)
    at Object.newLoader [as .js] (/home/jdalrymple/src/bugs/mochajs/node_modules/pirates/lib/index.js:104:7)
    at Module.load (internal/modules/cjs/loader.js:1050:32)
    at Function.Module._load (internal/modules/cjs/loader.js:938:14)
    at Module.require (internal/modules/cjs/loader.js:1090:19)
    at require (internal/modules/cjs/helpers.js:75:18)
    at /home/jdalrymple/src/bugs/mochajs/node_modules/mocha/lib/mocha.js:384:36
    at Array.forEach (<anonymous>)
    at Mocha.loadFiles (/home/jdalrymple/src/bugs/mochajs/node_modules/mocha/lib/mocha.js:381:14)
    at Mocha.run (/home/jdalrymple/src/bugs/mochajs/node_modules/mocha/lib/mocha.js:954:10)
    at Object.run (/home/jdalrymple/src/bugs/mochajs/node_modules/mocha/lib/cli/watch-run.js:223:20)
    at FSWatcher.<anonymous> (/home/jdalrymple/src/bugs/mochajs/node_modules/mocha/lib/cli/watch-run.js:169:14)
    at FSWatcher.emit (events.js:314:20)
    at /home/jdalrymple/src/bugs/mochajs/node_modules/mocha/node_modules/chokidar/index.js:364:35
    at processTicksAndRejections (internal/process/task_queues.js:75:11) {
  code: 'ERR_REQUIRE_ESM'
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @ test-mocha:watch: `mocha --require @babel/register $(find ./src -type f -name '*.test.js') --watch`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @ test-mocha:watch script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/jdalrymple/.npm/_logs/2020-07-14T00_20_36_063Z-debug.log

Steps to Reproduce

For convenience, the following respository was created that adequately reproduces the issue:

  1. Ensure your node version matches the one listed below
  2. Clone the repo: https://github.com/Swivelgames/issues-mochajs-4374
  3. Install its dependencies: npm i
  4. Test the repo: npm run test-mocha
  5. Start a watch: npm run test-mocha:watch

Expected Behavior:

  • Mocha would begin watching the files

Actual Behavior:

  • Error is thrown when attempting to watch files

Versions

Package Versions
  • node: v14.5.0
  • mocha: 8.0.1
  • babel: v7.10.4
Environment
  • OS: Linux, Arch, x86_64
  • Shell: tmux + zsh

Additional Information

It is my understanding that the following scenarios are applicable:

import should be used under the following conditions:

  • If the target file's associated package.json file contains "type": "module" and its file extension is .js
  • If the target file's extension is .mjs, regardless of what is inside its associated package.json file

require() should be used under the following conditions:

  • If the target file's associated package.json file does NOT contain "type": "module" and its file extension is .js
  • If the target file's extension is .cjs, regardless of what is inside its associated package.json file

Conclusion

Presently, it appears as though --watch is assuming the target file is a CommonJS module when its extension is .js, and failing to yield to its package.json file's "type": "module".

Considerations

It's worth considering the fact that the package.json file associated with the file being imported may be different than the package.json in the directory that mocha is being called, even in the event that the target file's path does not include node_modules.

Swivelgames added a commit to Swivelgames/issues-mochajs-4374 that referenced this issue Jul 14, 2020
@Swivelgames Swivelgames changed the title Mocha Insists on CJS, Ignoring "type": "module" in Project's package.json Mocha --watch insists on CJS, Ignores "type": "module" in project's package.json Jul 14, 2020
@juergba
Copy link
Contributor

juergba commented Jul 14, 2020

Please see our docs: current limitations.

We do not (yet?) support ESModules in watch mode. Node's ESM implementation is using its separate cache (not require.cache), which currently can't be cleared before re-running the tests in watch mode.

@boneskull boneskull added type: feature enhancement proposal status: needs upstream fix defect within Mocha's dependency tree and removed unconfirmed-bug labels Jul 30, 2020
@boneskull boneskull changed the title Mocha --watch insists on CJS, Ignores "type": "module" in project's package.json support ESM in watch mode Jul 30, 2020
@boneskull
Copy link
Contributor

@Swivelgames Have you tried --parallel combined with --watch? It might work, since we dump the workers after each run, but I don't think we are testing it anywhere yet.

naholyr added a commit to naholyr/formation-node-2020-12 that referenced this issue Dec 17, 2020
@hovissimo
Copy link

@boneskull I'm not @Swivelgames but your suggestion of adding --parallel works for me. Thanks!

@cspotcode
Copy link
Contributor

cspotcode commented Mar 7, 2021

To support ESM in watch mode, I think you will need to implement your own ESM loader hook. It can append query params to each imported module, effectively making them unique. By appending a different query param the second time, you can ensure a fresh copy of the module is imported. This may, however, be a memory leak, if node has no way to clear the previously-imported copies of those modules.

It is probably worth sharing this use-case with the node maintainers so they can add the necessary API surface to node.

https://github.com/nodejs/modules/issues

EDIT: I see this was already raised with the node team: nodejs/node#49442

@djfm

This comment was marked as off-topic.

@jasonrberk

This comment was marked as off-topic.

@lehni
Copy link

lehni commented Mar 20, 2022

Based on the comment of @cspotcode and the experiment by @vsnthdev in nodejs/node#49442, I've gone ahead and implemented this as a proof of concept for mocha --watch, and it does indeed seem to work:

lineto@99d5a14

To run mocha in this mode, you simply pass the --esm option along with --watch. Without --esm, nothing changes.

This may not be ready for primetime yet, but it works well for my use-case and it shows a viable path forward for ESM.

Perhaps it can be wrapped up for release?

@lehni
Copy link

lehni commented Mar 20, 2022

I've just made my branch work for NPM installs, so if you want to test the esm watch mode in your project, you can replace the mocha dependency:

yarn add -D mocha@https://github.com/lehni/mocha#99d5a14d3a1b11f81dbb08e1895d43061e7b0541
# Or:
npm install -D https://github.com/lehni/mocha#99d5a14d3a1b11f81dbb08e1895d43061e7b0541

And after that, simply run your test command with --watch --esm:

yarn test --watch --esm

Please let me know how this is working for you!

@lehni
Copy link

lehni commented Mar 21, 2022

I've added auto-detection of esm watch mode now, so the --esm argument is not required anymore:

lineto@64c5fcc

@inca
Copy link

inca commented May 30, 2022

Just fyi as of May 2022 this is still an open issue and quite a significant impediment.

As an interim workaround I ended up with nodemon in front of Mocha, which works flawlessly and doesn't have a memleak issue mentioned in the PR 👆.

As a reference, here's the nodemon.test.json that works for me (I'm using TypeScript, hence out dir):

{
    "exec": "npx mocha --node-option=experimental-network-imports",
    "watch": [
        "./out/**/*"
    ],
    "delay": 100,
    "env": {
        "NODE_ENV": "test"
    }
}

@Daghall
Copy link

Daghall commented Jul 18, 2022

Possible workaround, using fswatch:

fswatch lib/ test/ --event Updated | xargs -I _ mocha test/file.js -b

@tomchiverton
Copy link

Having to use

    "test-watch":"nodemon --exec mocha spec",

in package.json scripts is a workaround, but it'd be better if it Just Worked

@jackdbd
Copy link

jackdbd commented Dec 28, 2022

Another simple workaround is to use entr to watch your source files:

{
  "scripts": {
    "test:watch": "ls lib/*.js | entr -r mocha"
  }
}

falsefalse added a commit to falsefalse/yaf-extension that referenced this issue Apr 9, 2023
mocha + sinon + chai.

ESM + typescript is hard :-/
Editor TS, build TS and spec TS don't want to agree on module imports.
On top of that — Jakefile can not be ESM yet.
So src/ and /spec are modules, and project is not.
--watch won't work unless ran with --parallel

mochajs/mocha#4374
mochajs/mocha-examples#47
https://gist.github.com/jordansexton/2a0c3c360aa700cc9528e89620e82c3d
falsefalse added a commit to falsefalse/yaf-extension that referenced this issue Apr 9, 2023
mocha + sinon + chai.

ESM + typescript is hard :-/
Editor TS, build TS and spec TS don't want to agree on module imports.
On top of that — Jakefile can not be ESM yet.
So src/ and /spec are modules, and project is not.
--watch won't work unless ran with --parallel

mochajs/mocha#4374
mochajs/mocha-examples#47
https://gist.github.com/jordansexton/2a0c3c360aa700cc9528e89620e82c3d
@kantbtrue
Copy link

I've tried @boneskull solution and combined the --parallel with --watch flag. It worked for me.
Here is the part of my package.json scripts section
"test:watch": "mocha --watch --parallel --slow 100"

@JoshuaKGoldberg JoshuaKGoldberg changed the title support ESM in watch mode 🚀 Feature: Support ESM (ECMAScript Modules) in watch mode Dec 27, 2023
@gvozdenkov
Copy link

gvozdenkov commented Jan 17, 2024

I found one pretty simple solution for typescript and esm. I use tsx in mocha config file. Post here. Now mocha works with simple script "test:watch": "mocha --watch"

@icetbr
Copy link

icetbr commented Jun 4, 2024

This is now possible using a new flag from node 22. This should work:

mocha --watch --experimental-require-module

Here is the person who found the "fix"
https://joyeecheung.github.io/blog/2024/03/18/require-esm-in-node-js/

The docs
https://nodejs.org/api/modules.html#loading-ecmascript-modules-using-require

Here is where I first saw the merge
nodejs/node#51977 (comment)

Kehrlann added a commit to Kehrlann/spring-security-webauthn that referenced this issue Jul 8, 2024
- We can't use `mocha --watch` with ESM modules. However, it seems
  possible with --parallel, see this comment:
  mochajs/mocha#4374 (comment)
@lo1tuma
Copy link

lo1tuma commented Aug 23, 2024

Not sure if this has been mentioned before, but mocha --node-option=watch works as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: needs upstream fix defect within Mocha's dependency tree type: feature enhancement proposal
Projects
None yet
Development

No branches or pull requests