Skip to content

Support loading ESM test files #2382

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

Merged
merged 26 commits into from
Feb 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9e79ccf
fix: use dynamic import() to load esm modules
arlac77 Jan 26, 2020
c8c9b22
style: fix some xo styling issues
arlac77 Jan 26, 2020
6873e32
style: xo is happy now
arlac77 Jan 26, 2020
c886006
test: differentiate module loading asserts based on node version (13)
arlac77 Jan 26, 2020
bba6daf
test: use fully qualified imports (index.js) in esm fixtures
arlac77 Jan 26, 2020
bc7a7d6
style: add hints to make import '.../index.js' pass
arlac77 Jan 27, 2020
4103261
style: use optional catch binding
arlac77 Jan 27, 2020
d8fdba3
fix: force esm loading exceptions to be catched by load function
arlac77 Jan 28, 2020
4a5c3fb
debug: node@13/windows import error
arlac77 Jan 28, 2020
f327ca0
test: asume esm loading on node 13@win32 does not work for now
arlac77 Jan 28, 2020
db441ee
test: new wording for esm load tests
arlac77 Jan 29, 2020
b0fa2ba
Revert "fix: force esm loading exceptions to be catched by load funct…
arlac77 Feb 1, 2020
6946d74
fix: convert path to url when using import()
arlac77 Feb 1, 2020
531eb1e
style: make xo happy
Feb 1, 2020
f11ee1e
fix: wording on when node does not support import()
arlac77 Feb 2, 2020
d496118
fix: check for import() with well known module
arlac77 Feb 2, 2020
72fdd72
Merge branch 'master' of https://github.com/avajs/ava
arlac77 Feb 2, 2020
2579067
perf: move import() detection logic out of loop
Feb 3, 2020
08c14b6
fix: error message wording
arlac77 Feb 3, 2020
e7d33dd
Use .mjs extension for probe, update comments
novemberborn Feb 9, 2020
530a7fc
No need to await the import (my bad)
novemberborn Feb 9, 2020
dc743f6
Tweak error message
novemberborn Feb 9, 2020
3ad38d4
Rename userland esm package fixture
novemberborn Feb 9, 2020
71457dd
Rename type=module ESM fixture
novemberborn Feb 9, 2020
3cbc4e6
Lazily check for dynamic import support
novemberborn Feb 9, 2020
52e11cc
Update ES module recipe
novemberborn Feb 9, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions docs/recipes/es-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,35 @@

Translations: [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/docs/recipes/es-modules.md)

As of Node.js 13, [ECMAScript modules](https://nodejs.org/docs/latest/api/esm.html#esm_introduction) are natively supported in Node.js itself. AVA does not quite support them *yet*, but we're close.
As of Node.js 13, [ECMAScript Modules](https://nodejs.org/docs/latest/api/esm.html#esm_introduction) are natively supported in Node.js itself. AVA 3.3 supports ESM test files, however support is incomplete. The [ESM support project](https://github.com/orgs/avajs/projects/2) tracks our progress.

For the time being, AVA *does* select test files with the `.mjs` extension, however it refuses to load them. Similarly the `package.json` `"type": "module"` field is recognized, but if set AVA will refuse to load test files with the `.js` extension.
ESM support in Node.js is experimental, though enabled by default in Node.js 13. *You will see messages like `ExperimentalWarning: The ESM module loader is experimental` in AVA's output. These are emitted by Node.js, not AVA.*

For now, your best bet is to use the [`esm`](https://github.com/standard-things/esm) package. Make sure to use the `.js` extension and *do not* set `"type": "module"` in `package.json`.
## Enabling experimental ESM support in Node.js 12

In Node.js 12 you need to enable ESM support. You can do so via AVA by configuring `nodeArguments` in your `package.json` or `ava.config.*` file:

**`package.json`:**

```json
{
"ava": {
"nodeArguments": [
"--experimental-modules"
]
}
}
```

Or on the command line:

```console
npx ava --node-arguments '--experimental-modules' test.mjs
```

## Using the `esm` package

If you want to use the ESM syntax, without relying on Node.js' implementation, your best bet is to use the [`esm`](https://github.com/standard-things/esm) package. Make sure to use the `.js` extension and *do not* set `"type": "module"` in `package.json`.

Here's how you get it working with AVA.

Expand Down
2 changes: 2 additions & 0 deletions lib/esm-probe.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Keep this file in place.
// It is there to check that ESM dynamic import actually works in the current Node.js version.
19 changes: 18 additions & 1 deletion lib/worker/subprocess.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict';
const {pathToFileURL} = require('url');
const currentlyUnhandled = require('currently-unhandled')();

require('./ensure-forked'); // eslint-disable-line import/no-unassigned-import
Expand Down Expand Up @@ -133,11 +134,27 @@ ipc.options.then(async options => {
return null;
}).filter(provider => provider !== null);

// Lazily determine support since this prints an experimental warning.
let supportsESM = async () => {
try {
await import('../esm-probe.mjs');
supportsESM = async () => true;
} catch {
supportsESM = async () => false;
}

return supportsESM();
};

let requireFn = require;
const load = async ref => {
for (const extension of extensionsToLoadAsModules) {
if (ref.endsWith(`.${extension}`)) {
ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, new Error('AVA cannot yet load ESM files.'))});
if (await supportsESM()) { // eslint-disable-line no-await-in-loop
Copy link
Contributor

Choose a reason for hiding this comment

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

I think

if (cachedSupportsESMResult || await supportsESM())

can make this loop faster.

return import(pathToFileURL(ref));
}

ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, new Error('ECMAScript Modules are not supported in this Node.js version.'))});
exit(1);
return;
}
Expand Down
2 changes: 1 addition & 1 deletion test/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ test('`esm` package support', t => {
require: [require.resolve('esm')]
});

return api.run({files: [path.join(__dirname, 'fixture/esm-pkg/test.js')]})
return api.run({files: [path.join(__dirname, 'fixture/userland-esm-package/test.js')]})
.then(runStatus => {
t.is(runStatus.stats.passedTests, 1);
});
Expand Down
5 changes: 0 additions & 5 deletions test/fixture/esm/test.js

This file was deleted.

2 changes: 1 addition & 1 deletion test/fixture/mjs.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import test from '../..';
import test from '../../index.js'; // eslint-disable-line import/no-useless-path-segments, unicorn/import-index, import/extensions

test('pass', t => {
t.pass();
Expand Down
5 changes: 5 additions & 0 deletions test/fixture/pkg-type-module/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import test from '../../../index.js'; // eslint-disable-line import/no-useless-path-segments, unicorn/import-index, import/extensions

test('pass', t => {
t.pass();
});
File renamed without changes.
30 changes: 21 additions & 9 deletions test/integration/assorted.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,18 +164,30 @@ test('selects .cjs test files', t => {
});
});

test('refuses to load .mjs test files', t => {
test('load .mjs test files (when node supports it)', t => {
execCli('mjs.mjs', (err, stdout) => {
t.ok(err);
t.match(stdout, /AVA cannot yet load ESM files/);
t.end();
if (Number.parseFloat(process.version.slice(1)) >= 13) {
t.ifError(err);
t.match(stdout, /1 test passed/);
t.end();
} else {
t.ok(err);
t.match(stdout, /ECMAScript Modules are not supported in this Node.js version./);
t.end();
}
});
});

test('refuses to load .js test files as ESM modules', t => {
execCli('test.js', {dirname: 'fixture/esm'}, (err, stdout) => {
t.ok(err);
t.match(stdout, /AVA cannot yet load ESM files/);
t.end();
test('load .js test files as ESM modules (when node supports it)', t => {
execCli('test.js', {dirname: 'fixture/pkg-type-module'}, (err, stdout) => {
if (Number.parseFloat(process.version.slice(1)) >= 13) {
t.ifError(err);
t.match(stdout, /1 test passed/);
t.end();
} else {
t.ok(err);
t.match(stdout, /ECMAScript Modules are not supported in this Node.js version./);
t.end();
}
});
});