Skip to content

Commit 0dba1cf

Browse files
committed
fix: handle named imports from CJS modules that use dynamic import
Fix #2294 Mark ambiguous (i.e. not unambiguously ESM) modules that contain dynamic import() so that they can be ignored like other ambiguous modules when analysing named imports. In this way, the named rule accepts named imports from CJS modules that contain dynamic imports. Also fix the case where the importing module uses a require() call.
1 parent ef980d4 commit 0dba1cf

File tree

5 files changed

+28
-3
lines changed

5 files changed

+28
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1010
- `importType`: avoid crashing on a non-string' ([#2305], thanks [@ljharb])
1111
- [`first`]: prevent crash when parsing angular templates ([#2210], thanks [@ljharb])
1212
- `importType`: properly resolve `@/*`-aliased imports as internal ([#2334], thanks [@ombene])
13+
- `named`: avoid false positives when importing from a CommonJS module that uses `import()` ([#2341], thanks [@ludofischer])
1314

1415
### Changed
1516
- [`no-default-import`]: report on the token "default" instead of the entire node ([#2299], thanks [@pmcelhaney])

src/ExportMap.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ export default class ExportMap {
4343
*/
4444
this.imports = new Map();
4545
this.errors = [];
46+
/**
47+
* true when we are still unsure if
48+
* the module is ESM, happens when the module
49+
* contains dynamic `import()` but no other
50+
* ESM import/export
51+
*/
52+
this.maybeNotEsm = false;
4653
}
4754

4855
get hasDefault() { return this.get('default') != null; } // stronger than this.has
@@ -406,7 +413,8 @@ ExportMap.parse = function (path, content, context) {
406413
},
407414
});
408415

409-
if (!unambiguous.isModule(ast) && !hasDynamicImports) return null;
416+
const maybeNotEsm = !unambiguous.isModule(ast);
417+
if (maybeNotEsm && !hasDynamicImports) return null;
410418

411419
const docstyle = (context.settings && context.settings['import/docstyle']) || ['jsdoc'];
412420
const docStyleParsers = {};
@@ -710,6 +718,7 @@ ExportMap.parse = function (path, content, context) {
710718
m.namespace.set('default', {}); // add default export
711719
}
712720

721+
m.maybeNotEsm = maybeNotEsm;
713722
return m;
714723
};
715724

src/rules/named.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ module.exports = {
4040
}
4141

4242
const imports = Exports.get(node.source.value, context);
43-
if (imports == null) {
43+
if (imports == null || imports.maybeNotEsm) {
4444
return;
4545
}
4646

@@ -97,6 +97,7 @@ module.exports = {
9797
// return if it's not a string source
9898
|| source.type !== 'Literal'
9999
|| variableExports == null
100+
|| variableExports.maybeNotEsm
100101
) {
101102
return;
102103
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
async function doSomething() {
2+
await import('./bar.js');
3+
}
4+
5+
exports.something = 'hello';

tests/src/rules/named.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,16 @@ ruleTester.run('named', rule, {
190190
sourceType: 'module',
191191
ecmaVersion: 2020,
192192
},
193-
})) || []),
193+
})),
194+
195+
testVersion('>=7.8.0', () =>({ code: 'const { something } = require("./dynamic-import-in-commonjs")',
196+
parserOptions: { ecmaVersion: 2021 },
197+
options: [{ commonjs: true }],
198+
})),
199+
200+
testVersion('>=7.8.0', () => ({ code: 'import { something } from "./dynamic-import-in-commonjs"',
201+
parserOptions: { ecmaVersion: 2021 } })),
202+
),
194203
],
195204

196205
invalid: [

0 commit comments

Comments
 (0)