ava
ava copied to clipboard
The ESM Plan
Here's what we're doing to support ESM in AVA. The work has already been broken up into individual issues that you can work on. See the ESM support project for details.
See https://nodejs.org/docs/latest/api/esm.html for the latest on what Node.js is implementing (this issue was opened when that documentation was for Node.js 13.1.0). I'm expecting this to become available without a flag at some point.
-
[x] Firstly, we're moving AVA's Babel support into a separate package that you can install alongside AVA. This will reduce confusion as to what module format AVA supports out of the box. It also allows us to evolve that package independently, which is important since loader hooks may still take some while to land.
-
[x] Secondly, we'll be detecting test files with
cjs
andmjs
extensions. Currently, by default, we only look for files withjs
extensions. -
[x] When loading test files, we'll always load files with
cjs
extensions usingrequire()
. Files withmjs
extensions will be loaded usingimport()
. https://github.com/avajs/ava/issues/2344 -
[x] Note that
import()
itself is currently behind an experimental flag. https://github.com/avajs/ava/pull/2272 will let you configure that flag. We'll print a useful error message ifimport()
is not available and AVA is trying to load anmjs
test file. -
[x] When it comes to
js
files we'll follow thepackage.json
"type"
field. So by default we'll treat them as CJS. -
[x] We'll also let you configure how
js
files (and other files, but notcjs
ormjs
files) should be loaded. This way you could override the"type"
field, or load test files as ESM even if you're publishing CJS. https://github.com/avajs/ava/issues/2345 -
[x] The Babel provider will detect when you've configured files to be loaded as ESM even though it cannot load them as such, and print an error.
-
[x] We'll support
ava.config.cjs
andava.config.mjs
configuration files. However deciding what to do withava.config.js
files is more difficult. We shouldn't change how it's loaded based on"type"
fields, nor can we use the built-inimport()
since it needs to be enabled. And finally, right now we need to be able to load it synchronously for use with our ESLint plugin. https://github.com/avajs/ava/issues/2346 -
[x] I'm proposing we remove support for
import
statements andrequire()
calls fromava.config.js
files. In other words, they must only containexport default {}
orexport default () => ({})
style configurations. This is a breaking change, as at the moment we loadava.config.js
using theesm
package. However it gives us the best upgrade path. -
[x] We do need to consider what syntax we use in our documentation. I think we should use CJS, until ESM is available without a flag. It's not as hip, but at least it'll work out of the box.
-
[x] When loading
require
modules, if they are loaded from the project itself we can use the above logic to load the files based on their extension. If they're packages andimport()
is available then that should work. Else, for now, we'll fall back to usingrequire()
. https://github.com/avajs/ava/issues/2347 -
[ ] Dependency tracking for our watch mode also needs work. https://github.com/avajs/ava/issues/2388
We shouldn't change how it's loaded based on "type" fields
Why not?
We shouldn't change how it's loaded based on "type" fields
Why not?
First of, Node.js defaults to CJS for js
files. Following this default breaks all existing configuration files. With my suggestion to only support export default
most simple files will still work.
Changing the "type"
field would have the consequence of breaking your AVA configuration, which I'd find surprising.
Finally, if we say you can override how js
files are loaded, through configuration, it's confusing that you can't change how the ava.config.js
file itself is loaded.
I've also just realized our dependency tracking (for the watcher) won't work for ESM. Perhaps we can use the loader plugins, or else we should try and do static analysis. The latter is complicated by new JavaScript & TypeScript syntaxes.
IMO, why don't we support Node experimental modules under a flag. For example:
ava --experimental-modules
So that it won't break the current behavior and we can switch to the new modules system when the interoperability and the loader hooks are stable enough.
IMO, why don't we support Node experimental modules under a flag. For example:
ava --experimental-modules
So that it won't break the current behavior and we can switch to the new modules system when the interoperability and the loader hooks are stable enough.
#2272 will add a nicer way of defining the Node.js flags applied to the worker processes than what is currently possible. However, ESM is available already without a flag in Node.js 13. We just need to make a bunch of changes to make effective use of it. And yes it would be opt-in, but that's largely due to how the feature is designed in Node.js itself.
It appears that AVA can't be used on Node 13 with type: module
in package.json.
Any suggestions? index.js
has an export default
in it so it's definitely ESM.
Should I open a new issue?
❯ node -v
v13.2.0
Without any ava
config
❯ ava
⠦ (node:64001) Warning: require() of ES modules is not supported.
require() of ./index.js from ./test/index.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.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from ./package.json.
✖ No tests found in test/index.js
1 uncaught exception
Uncaught exception in test/index.js
./test/index.js:4
3: import {compressToEncodedURIComponent as compress} from 'lz-string';
4: import OptionsSync from '..';
5:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: ./index.js
With require: esm
This configuration worked up to v12
"ava": {
"require": [
"esm"
]
}
❯ ava
⠧ (node:63214) Warning: require() of ES modules is not supported.
require() of ./index.js from ./test/index.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.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from ./package.json.
✖ No tests found in test/index.js
1 uncaught exception
Uncaught exception in test/index.js
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: ./index.js
@fregante,
It appears that AVA can't be used on Node 13 with
type: module
in package.json.
Yup. This issue sets out our approach to making it work.
I have made all the breaking changes needed for this as part of our v3 release. However we haven't yet added any actual support for ESM files. Please see https://github.com/orgs/avajs/projects/2 for the issues where you can help out.
Have you considered using the esm package ? Native ESM module with node is fine, but when you start mixing with old dependencies or use an older version of node, you will quickly bump into problems.
I have been using esm
with my test runner for some time now and I am very happy.
You don't even need to ship it as a dependency, if ava allows require hooks like node does you can pass it as a command argument.
ava -r esm some/test.js
Yes that’s been supported for a long time. You can configure the required modules in AVA’s configuration and well then use it to load all subsequent files.
It’s only a stop gap solution though.
esm
is no longer enough in Node 13, at least until this is fixed https://github.com/standard-things/esm/issues/855
All the necessary breaking changes have already gone out in AVA 3.0. The remaining work has been broken up into individual issues that can be worked on. See the ESM support project for details.
Any current recipes? My entire codebase uses import
statements etc., but I cannot seem to quickly figure out how it's suppose to work (running Node 14.6). My package.json
has "type": "module"
, but module resolution doesn't seem to work inside tests.
@haywirez I think that should work fine. Perhaps you could open a new issue and include additional details, like your AVA version, and a reproduction?
@novemberborn would you consider adding support for a --loader option to the new ava?
In order to use this esmock package, for example, one uses something like this inside package.json
"scripts" : {
"unit-test": "ava --node-arguments=\"--loader=esmock\""
}
If a --loader option were supported something like this might work
"scripts" : {
"unit-test": "ava --loader=esmock"
}
@iambumblehead as far as I know, loaders are still experimental: https://nodejs.org/api/esm.html#esm_loaders
When that changes we can consider how best to expose it in AVA.
Using type: module or .mjs prevents agents like newrelic from instrumenting code or sinon from mocking and jest doesn't support modules fully either, see their ecmascript-modules page. The esm library does not yet support optional chaining (elvis operator). The esm-wallaby library which is a fork of esm does. It's disappointing that module support hasn't addressed these issues and we can't move to type:module, .mjs or ava 4.x until these are resolved. For that reason I'm not updating libraries I maintain to type: module or .mjs until these issues are resolved. Actually I did update them to type:module and .mjs, but backed all the changes out after discovering the issues and no workarounds. Consumer of my libs can use type:module, .mjs, or use esm, esm-wallaby, or some other shim (lots of options). They aren't blocked from updating like ava 4.x currently.
@tcollinsworth AVA 4 works just fine with CJS. Being a Node.js test runner, for ESM support we rely on Node.js' standard behavior.
@tcollinsworth stop using non-native esm and stop using tools that don't support esm. The only tool you listed that doesn't have a native-esm replacement is newrelic https://github.com/newrelic/node-newrelic/issues/553
If you have control of tools you are using, I would recommending dropping newrelic from your setup because using native esm simplifies so many other things within a package -- smaller download/install size, better stack traces, less configuration and dot files etc. Using native esm makes everything easier.
@iambumblehead any suggestions for an esm alternative to newrelic?
@drmrbrewer I'm not a newrelic user and don't know of any suggestions (sorry)
@drmrbrewer check out https://github.com/getsentry
With https://github.com/avajs/ava/pull/3218 watch mode now does dependency tracking of ES modules.