Skip to content

Commit b0e6135

Browse files
fix: fail with an informative error message on a file with a broken default import (#5413)
* Fixes #5411. Fail with an informative error message on a file with a broken default import. * Add debug logs to esm-utils * Always attempt import() after failed require(). On failure, only throw the require error in case the import error is ERR_INTERNAL_ASSERTION --------- Co-authored-by: Mark Wiemer <[email protected]>
1 parent 9d12fc2 commit b0e6135

File tree

4 files changed

+39
-9
lines changed

4 files changed

+39
-9
lines changed

lib/nodejs/esm-utils.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const path = require('node:path');
22
const url = require('node:url');
3+
const debug = require('debug')('mocha:esm-utils');
34

45
const forward = x => x;
56

@@ -95,14 +96,22 @@ const requireModule = async (file, esmDecorator) => {
9596
}
9697
try {
9798
return require(file);
98-
} catch (err) {
99-
// Import if require fails.
100-
return dealWithExports(await formattedImport(file, esmDecorator));
99+
} catch (requireErr) {
100+
debug('requireModule caught err: %O', requireErr.message);
101+
try {
102+
return dealWithExports(await formattedImport(file, esmDecorator));
103+
} catch (importErr) {
104+
if (importErr.code === 'ERR_INTERNAL_ASSERTION') {
105+
throw requireErr;
106+
}
107+
throw importErr;
108+
}
101109
}
102110
};
103111

104112
// We only assign this `requireOrImport` function once based on Node version
105113
// We check for file extensions in `requireModule` and `tryImportAndRequire`
114+
debug('assigning requireOrImport, require_module === %O', process.features.require_module);
106115
if (process.features.require_module) {
107116
exports.requireOrImport = requireModule;
108117
} else {

test/node-unit/esm-utils.spec.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,32 @@ const sinon = require('sinon');
55
const url = require('node:url');
66

77
describe('esm-utils', function () {
8-
beforeEach(function () {
9-
sinon.stub(esmUtils, 'doImport').resolves({});
10-
});
11-
12-
afterEach(function () {
13-
sinon.restore();
8+
describe('requireOrImport', function () {
9+
it('should show an informative error message for a broken default import', async function () {
10+
return expect(
11+
() =>
12+
esmUtils.requireOrImport(
13+
'../../test/node-unit/fixtures/broken-default-import.mjs'
14+
),
15+
'to be rejected with error satisfying',
16+
{
17+
name: 'SyntaxError',
18+
message:
19+
"The requested module './module-without-default-export.mjs' does not provide an export named 'default'"
20+
}
21+
);
22+
});
1423
});
1524

1625
describe('loadFilesAsync()', function () {
26+
beforeEach(function () {
27+
sinon.stub(esmUtils, 'doImport').resolves({});
28+
});
29+
30+
afterEach(function () {
31+
sinon.restore();
32+
});
33+
1734
it('should not decorate imported module if no decorator passed', async function () {
1835
await esmUtils.loadFilesAsync(
1936
['/foo/bar.mjs'],
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import moduleWithoutDefaultExport from './module-without-default-export.mjs';
2+
3+
moduleWithoutDefaultExport;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const value = 42;

0 commit comments

Comments
 (0)