diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d1b0b4ac9..52777b296a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Added - [`no-named-default`, `no-default-export`, `prefer-default-export`, `no-named-export`, `export`, `named`, `namespace`, `no-unused-modules`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) +- [`no-dynamic-require`]: support dynamic import with espree ([#2371], thanks [@sosukesuzuki]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) @@ -963,6 +964,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2371]: https://github.com/import-js/eslint-plugin-import/pull/2371 [#2367]: https://github.com/import-js/eslint-plugin-import/pull/2367 [#2332]: https://github.com/import-js/eslint-plugin-import/pull/2332 [#2358]: https://github.com/import-js/eslint-plugin-import/pull/2358 diff --git a/src/rules/no-dynamic-require.js b/src/rules/no-dynamic-require.js index 8267fd26e9..27e9a957a7 100644 --- a/src/rules/no-dynamic-require.js +++ b/src/rules/no-dynamic-require.js @@ -19,6 +19,8 @@ function isStaticValue(arg) { (arg.type === 'TemplateLiteral' && arg.expressions.length === 0); } +const dynamicImportErrorMessage = 'Calls to import() should use string literals'; + module.exports = { meta: { type: 'suggestion', @@ -55,10 +57,19 @@ module.exports = { if (options.esmodule && isDynamicImport(node)) { return context.report({ node, - message: 'Calls to import() should use string literals', + message: dynamicImportErrorMessage, }); } }, + ImportExpression(node) { + if (!options.esmodule || isStaticValue(node.source)) { + return; + } + return context.report({ + node, + message: dynamicImportErrorMessage, + }); + }, }; }, }; diff --git a/tests/src/rules/no-dynamic-require.js b/tests/src/rules/no-dynamic-require.js index 368ec11935..0b141ccd76 100644 --- a/tests/src/rules/no-dynamic-require.js +++ b/tests/src/rules/no-dynamic-require.js @@ -1,6 +1,7 @@ -import { parsers, test } from '../utils'; +import { parsers, test, testVersion } from '../utils'; import { RuleTester } from 'eslint'; +import flatMap from 'array.prototype.flatmap'; const ruleTester = new RuleTester(); const rule = require('rules/no-dynamic-require'); @@ -28,56 +29,93 @@ ruleTester.run('no-dynamic-require', rule, { test({ code: 'var foo = require("@scope/foo")' }), //dynamic import - test({ - code: 'import("foo")', - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'import(`foo`)', - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'import("./foo")', - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'import("@scope/foo")', - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'var foo = import("foo")', - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'var foo = import(`foo`)', - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'var foo = import("./foo")', - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'var foo = import("@scope/foo")', - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'import("../" + name)', - errors: [dynamicImportError], - parser: parsers.BABEL_OLD, - options: [{ esmodule: false }], - }), - test({ - code: 'import(`../${name}`)', - errors: [dynamicImportError], - parser: parsers.BABEL_OLD, + ...flatMap([parsers.ESPREE, parsers.BABEL_OLD], (parser) => { + const _test = + parser === parsers.ESPREE + ? (testObj) => testVersion('>= 6.2.0', () => testObj) + : (testObj) => test(testObj); + return [].concat( + _test({ + code: 'import("foo")', + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'import(`foo`)', + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'import("./foo")', + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'import("@scope/foo")', + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'var foo = import("foo")', + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'var foo = import(`foo`)', + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'var foo = import("./foo")', + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'var foo = import("@scope/foo")', + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'import("../" + name)', + errors: [dynamicImportError], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'import(`../${name}`)', + errors: [dynamicImportError], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + ); }), ], invalid: [ @@ -104,29 +142,49 @@ ruleTester.run('no-dynamic-require', rule, { }), // dynamic import - test({ - code: 'import("../" + name)', - errors: [dynamicImportError], - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'import(`../${name}`)', - errors: [dynamicImportError], - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'import(name)', - errors: [dynamicImportError], - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], - }), - test({ - code: 'import(name())', - errors: [dynamicImportError], - parser: parsers.BABEL_OLD, - options: [{ esmodule: true }], + ...flatMap([parsers.ESPREE, parsers.BABEL_OLD], (parser) => { + const _test = + parser === parsers.ESPREE + ? (testObj) => testVersion('>= 6.2.0', () => testObj) + : (testObj) => test(testObj); + return [].concat( + _test({ + code: 'import("../" + name)', + errors: [dynamicImportError], + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'import(`../${name}`)', + errors: [dynamicImportError], + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'import(name)', + errors: [dynamicImportError], + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + _test({ + code: 'import(name())', + errors: [dynamicImportError], + options: [{ esmodule: true }], + parser, + parserOptions: { + ecmaVersion: 2020, + }, + }), + ); }), test({ code: 'require(`foo${x}`)', diff --git a/tests/src/utils.js b/tests/src/utils.js index 49ba6b4702..b66ecf9c66 100644 --- a/tests/src/utils.js +++ b/tests/src/utils.js @@ -6,6 +6,7 @@ import semver from 'semver'; import 'babel-eslint'; export const parsers = { + ESPREE: require.resolve('espree'), TS_OLD: semver.satisfies(eslintPkg.version, '>=4.0.0 <6.0.0') && require.resolve('typescript-eslint-parser'), TS_NEW: semver.satisfies(eslintPkg.version, '>5.0.0') && require.resolve('@typescript-eslint/parser'), BABEL_OLD: require.resolve('babel-eslint'),